blob: 9e0a1c58fde4425b6c8650b66d1b8ed1c0c846c0 [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
37#define CIRRUSFB_VERSION "2.0-pre2"
38
Linus Torvalds1da177e2005-04-16 15:20:36 -070039#include <linux/module.h>
40#include <linux/kernel.h>
41#include <linux/errno.h>
42#include <linux/string.h>
43#include <linux/mm.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070044#include <linux/slab.h>
45#include <linux/delay.h>
46#include <linux/fb.h>
47#include <linux/init.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070048#include <asm/pgtable.h>
49
50#ifdef CONFIG_ZORRO
51#include <linux/zorro.h>
52#endif
53#ifdef CONFIG_PCI
54#include <linux/pci.h>
55#endif
56#ifdef CONFIG_AMIGA
57#include <asm/amigahw.h>
58#endif
59#ifdef CONFIG_PPC_PREP
Benjamin Herrenschmidte8222502006-03-28 23:15:54 +110060#include <asm/machdep.h>
Krzysztof Helt8503df62007-10-16 01:29:08 -070061#define isPReP machine_is(prep)
Linus Torvalds1da177e2005-04-16 15:20:36 -070062#else
63#define isPReP 0
64#endif
65
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -070066#include <video/vga.h>
67#include <video/cirrus.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070068
Linus Torvalds1da177e2005-04-16 15:20:36 -070069/*****************************************************************
70 *
71 * debugging and utility macros
72 *
73 */
74
75/* enable debug output? */
76/* #define CIRRUSFB_DEBUG 1 */
77
78/* disable runtime assertions? */
79/* #define CIRRUSFB_NDEBUG */
80
81/* debug output */
82#ifdef CIRRUSFB_DEBUG
Krzysztof Helt8503df62007-10-16 01:29:08 -070083#define DPRINTK(fmt, args...) \
Harvey Harrison5ae12172008-04-28 02:15:47 -070084 printk(KERN_DEBUG "%s: " fmt, __func__ , ## args)
Linus Torvalds1da177e2005-04-16 15:20:36 -070085#else
86#define DPRINTK(fmt, args...)
87#endif
88
89/* debugging assertions */
90#ifndef CIRRUSFB_NDEBUG
91#define assert(expr) \
Krzysztof Helt8503df62007-10-16 01:29:08 -070092 if (!(expr)) { \
93 printk("Assertion failed! %s,%s,%s,line=%d\n", \
Harvey Harrison5ae12172008-04-28 02:15:47 -070094 #expr, __FILE__, __func__, __LINE__); \
Krzysztof Helt8503df62007-10-16 01:29:08 -070095 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070096#else
97#define assert(expr)
98#endif
99
Krzysztof Helt8503df62007-10-16 01:29:08 -0700100#define MB_ (1024 * 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102/*****************************************************************
103 *
104 * chipset information
105 *
106 */
107
108/* board types */
Krzysztof Helt7345de32007-10-16 01:29:11 -0700109enum cirrus_board {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110 BT_NONE = 0,
111 BT_SD64,
112 BT_PICCOLO,
113 BT_PICASSO,
114 BT_SPECTRUM,
115 BT_PICASSO4, /* GD5446 */
116 BT_ALPINE, /* GD543x/4x */
117 BT_GD5480,
118 BT_LAGUNA, /* GD546x */
Krzysztof Helt7345de32007-10-16 01:29:11 -0700119};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121/*
122 * per-board-type information, used for enumerating and abstracting
123 * chip-specific information
Krzysztof Helt7345de32007-10-16 01:29:11 -0700124 * NOTE: MUST be in the same order as enum cirrus_board in order to
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125 * use direct indexing on this array
126 * NOTE: '__initdata' cannot be used as some of this info
127 * is required at runtime. Maybe separate into an init-only and
128 * a run-time table?
129 */
130static const struct cirrusfb_board_info_rec {
131 char *name; /* ASCII name of chipset */
132 long maxclock[5]; /* maximum video clock */
133 /* for 1/4bpp, 8bpp 15/16bpp, 24bpp, 32bpp - numbers from xorg code */
Richard Knutssonc930faa2007-05-08 00:38:29 -0700134 bool init_sr07 : 1; /* init SR07 during init_vgachip() */
135 bool init_sr1f : 1; /* write SR1F during init_vgachip() */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700136 /* construct bit 19 of screen start address */
137 bool scrn_start_bit19 : 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138
139 /* initial SR07 value, then for each mode */
140 unsigned char sr07;
141 unsigned char sr07_1bpp;
142 unsigned char sr07_1bpp_mux;
143 unsigned char sr07_8bpp;
144 unsigned char sr07_8bpp_mux;
145
146 unsigned char sr1f; /* SR1F VGA initial register value */
147} cirrusfb_board_info[] = {
148 [BT_SD64] = {
149 .name = "CL SD64",
150 .maxclock = {
151 /* guess */
152 /* the SD64/P4 have a higher max. videoclock */
153 140000, 140000, 140000, 140000, 140000,
154 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700155 .init_sr07 = true,
156 .init_sr1f = true,
157 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158 .sr07 = 0xF0,
159 .sr07_1bpp = 0xF0,
160 .sr07_8bpp = 0xF1,
161 .sr1f = 0x20
162 },
163 [BT_PICCOLO] = {
164 .name = "CL Piccolo",
165 .maxclock = {
166 /* guess */
167 90000, 90000, 90000, 90000, 90000
168 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700169 .init_sr07 = true,
170 .init_sr1f = true,
171 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172 .sr07 = 0x80,
173 .sr07_1bpp = 0x80,
174 .sr07_8bpp = 0x81,
175 .sr1f = 0x22
176 },
177 [BT_PICASSO] = {
178 .name = "CL Picasso",
179 .maxclock = {
180 /* guess */
181 90000, 90000, 90000, 90000, 90000
182 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700183 .init_sr07 = true,
184 .init_sr1f = true,
185 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186 .sr07 = 0x20,
187 .sr07_1bpp = 0x20,
188 .sr07_8bpp = 0x21,
189 .sr1f = 0x22
190 },
191 [BT_SPECTRUM] = {
192 .name = "CL Spectrum",
193 .maxclock = {
194 /* guess */
195 90000, 90000, 90000, 90000, 90000
196 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700197 .init_sr07 = true,
198 .init_sr1f = true,
199 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200 .sr07 = 0x80,
201 .sr07_1bpp = 0x80,
202 .sr07_8bpp = 0x81,
203 .sr1f = 0x22
204 },
205 [BT_PICASSO4] = {
206 .name = "CL Picasso4",
207 .maxclock = {
208 135100, 135100, 85500, 85500, 0
209 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700210 .init_sr07 = true,
211 .init_sr1f = false,
212 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 .sr07 = 0x20,
214 .sr07_1bpp = 0x20,
215 .sr07_8bpp = 0x21,
216 .sr1f = 0
217 },
218 [BT_ALPINE] = {
219 .name = "CL Alpine",
220 .maxclock = {
221 /* for the GD5430. GD5446 can do more... */
222 85500, 85500, 50000, 28500, 0
223 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700224 .init_sr07 = true,
225 .init_sr1f = true,
226 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227 .sr07 = 0xA0,
228 .sr07_1bpp = 0xA1,
229 .sr07_1bpp_mux = 0xA7,
230 .sr07_8bpp = 0xA1,
231 .sr07_8bpp_mux = 0xA7,
232 .sr1f = 0x1C
233 },
234 [BT_GD5480] = {
235 .name = "CL GD5480",
236 .maxclock = {
237 135100, 200000, 200000, 135100, 135100
238 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700239 .init_sr07 = true,
240 .init_sr1f = true,
241 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242 .sr07 = 0x10,
243 .sr07_1bpp = 0x11,
244 .sr07_8bpp = 0x11,
245 .sr1f = 0x1C
246 },
247 [BT_LAGUNA] = {
248 .name = "CL Laguna",
249 .maxclock = {
250 /* guess */
251 135100, 135100, 135100, 135100, 135100,
252 },
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 */
274 CHIP(PCI_DEVICE_ID_CIRRUS_5465, BT_LAGUNA), /* 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 -0700329struct cirrusfb_regs {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330 long multiplexing;
331 long mclk;
332 long divMCLK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333};
334
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335#ifdef CIRRUSFB_DEBUG
Krzysztof Helt7345de32007-10-16 01:29:11 -0700336enum cirrusfb_dbg_reg_class {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700337 CRT,
338 SEQ
Krzysztof Helt7345de32007-10-16 01:29:11 -0700339};
Krzysztof Helt8503df62007-10-16 01:29:08 -0700340#endif /* CIRRUSFB_DEBUG */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341
342/* info about board */
343struct cirrusfb_info {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 u8 __iomem *regbase;
Krzysztof Helt7345de32007-10-16 01:29:11 -0700345 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346 unsigned char SFR; /* Shadow of special function register */
347
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348 struct cirrusfb_regs currentmode;
349 int blank_mode;
Krzysztof Helt64beab12008-10-15 22:03:38 -0700350 u32 pseudo_palette[16];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700352 void (*unmap)(struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353};
354
Krzysztof Helt55a0dd82008-10-15 22:03:41 -0700355static int noaccel __devinitdata;
Krzysztof Helta1d35a72008-10-15 22:03:38 -0700356static char *mode_option __devinitdata = "640x480@60";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357
358/****************************************************************************/
359/**** BEGIN PROTOTYPES ******************************************************/
360
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361/*--- Interface used by the world ------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700362static int cirrusfb_init(void);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363#ifndef MODULE
Krzysztof Helt8503df62007-10-16 01:29:08 -0700364static int cirrusfb_setup(char *options);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365#endif
366
Krzysztof Helt8503df62007-10-16 01:29:08 -0700367static int cirrusfb_open(struct fb_info *info, int user);
368static int cirrusfb_release(struct fb_info *info, int user);
369static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
370 unsigned blue, unsigned transp,
371 struct fb_info *info);
372static int cirrusfb_check_var(struct fb_var_screeninfo *var,
373 struct fb_info *info);
374static int cirrusfb_set_par(struct fb_info *info);
375static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
376 struct fb_info *info);
377static int cirrusfb_blank(int blank_mode, struct fb_info *info);
378static void cirrusfb_fillrect(struct fb_info *info,
379 const struct fb_fillrect *region);
380static void cirrusfb_copyarea(struct fb_info *info,
381 const struct fb_copyarea *area);
382static void cirrusfb_imageblit(struct fb_info *info,
383 const struct fb_image *image);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384
385/* function table of the above functions */
386static struct fb_ops cirrusfb_ops = {
387 .owner = THIS_MODULE,
388 .fb_open = cirrusfb_open,
389 .fb_release = cirrusfb_release,
390 .fb_setcolreg = cirrusfb_setcolreg,
391 .fb_check_var = cirrusfb_check_var,
392 .fb_set_par = cirrusfb_set_par,
393 .fb_pan_display = cirrusfb_pan_display,
394 .fb_blank = cirrusfb_blank,
395 .fb_fillrect = cirrusfb_fillrect,
396 .fb_copyarea = cirrusfb_copyarea,
397 .fb_imageblit = cirrusfb_imageblit,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398};
399
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400/*--- Internal routines ----------------------------------------------------*/
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700401static void init_vgachip(struct fb_info *info);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700402static void switch_monitor(struct cirrusfb_info *cinfo, int on);
403static void WGen(const struct cirrusfb_info *cinfo,
404 int regnum, unsigned char val);
405static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum);
406static void AttrOn(const struct cirrusfb_info *cinfo);
407static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val);
408static void WSFR(struct cirrusfb_info *cinfo, unsigned char val);
409static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val);
410static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum,
411 unsigned char red, unsigned char green, unsigned char blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412#if 0
Krzysztof Helt8503df62007-10-16 01:29:08 -0700413static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum,
414 unsigned char *red, unsigned char *green,
415 unsigned char *blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -0700417static void cirrusfb_WaitBLT(u8 __iomem *regbase);
418static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
419 u_short curx, u_short cury,
420 u_short destx, u_short desty,
421 u_short width, u_short height,
422 u_short line_length);
423static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
424 u_short x, u_short y,
425 u_short width, u_short height,
426 u_char color, u_short line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700428static void bestclock(long freq, int *nom, int *den, int *div);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429
430#ifdef CIRRUSFB_DEBUG
Krzysztof Helt8503df62007-10-16 01:29:08 -0700431static void cirrusfb_dump(void);
432static void cirrusfb_dbg_reg_dump(caddr_t regbase);
433static void cirrusfb_dbg_print_regs(caddr_t regbase,
Krzysztof Helt7345de32007-10-16 01:29:11 -0700434 enum cirrusfb_dbg_reg_class reg_class, ...);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700435static void cirrusfb_dbg_print_byte(const char *name, unsigned char val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436#endif /* CIRRUSFB_DEBUG */
437
438/*** END PROTOTYPES ********************************************************/
439/*****************************************************************************/
440/*** BEGIN Interface Used by the World ***************************************/
441
Krzysztof Helt8503df62007-10-16 01:29:08 -0700442static int opencount;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443
444/*--- Open /dev/fbx ---------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700445static int cirrusfb_open(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446{
447 if (opencount++ == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700448 switch_monitor(info->par, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449 return 0;
450}
451
452/*--- Close /dev/fbx --------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700453static int cirrusfb_release(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454{
455 if (--opencount == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700456 switch_monitor(info->par, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457 return 0;
458}
459
460/**** END Interface used by the World *************************************/
461/****************************************************************************/
462/**** BEGIN Hardware specific Routines **************************************/
463
464/* Get a good MCLK value */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700465static long cirrusfb_get_mclk(long freq, int bpp, long *div)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466{
467 long mclk;
468
Krzysztof Helt8503df62007-10-16 01:29:08 -0700469 assert(div != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470
471 /* Calculate MCLK, in case VCLK is high enough to require > 50MHz.
472 * Assume a 64-bit data path for now. The formula is:
473 * ((B * PCLK * 2)/W) * 1.2
474 * B = bytes per pixel, PCLK = pixclock, W = data width in bytes */
475 mclk = ((bpp / 8) * freq * 2) / 4;
476 mclk = (mclk * 12) / 10;
477 if (mclk < 50000)
478 mclk = 50000;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700479 DPRINTK("Use MCLK of %ld kHz\n", mclk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480
481 /* Calculate value for SR1F. Multiply by 2 so we can round up. */
482 mclk = ((mclk * 16) / 14318);
483 mclk = (mclk + 1) / 2;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700484 DPRINTK("Set SR1F[5:0] to 0x%lx\n", mclk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485
486 /* Determine if we should use MCLK instead of VCLK, and if so, what we
487 * should divide it by to get VCLK */
488 switch (freq) {
489 case 24751 ... 25249:
490 *div = 2;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700491 DPRINTK("Using VCLK = MCLK/2\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492 break;
493 case 49501 ... 50499:
494 *div = 1;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700495 DPRINTK("Using VCLK = MCLK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496 break;
497 default:
498 *div = 0;
499 break;
500 }
501
502 return mclk;
503}
504
505static int cirrusfb_check_var(struct fb_var_screeninfo *var,
506 struct fb_info *info)
507{
Krzysztof Helt09a29102008-09-02 14:35:51 -0700508 int yres;
509 /* memory size in pixels */
510 unsigned pixels = info->screen_size * 8 / var->bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511
512 switch (var->bits_per_pixel) {
Krzysztof Helt060b6002007-10-16 01:29:13 -0700513 case 1:
Krzysztof Helt09a29102008-09-02 14:35:51 -0700514 pixels /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515 break; /* 8 pixel per byte, only 1/4th of mem usable */
Krzysztof Helt060b6002007-10-16 01:29:13 -0700516 case 8:
517 case 16:
Krzysztof Helt060b6002007-10-16 01:29:13 -0700518 case 32:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519 break; /* 1 pixel == 1 byte */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700521 printk(KERN_ERR "cirrusfb: mode %dx%dx%d rejected..."
522 "color depth not supported.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700524 DPRINTK("EXIT - EINVAL error\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525 return -EINVAL;
526 }
527
Krzysztof Helt09a29102008-09-02 14:35:51 -0700528 if (var->xres_virtual < var->xres)
529 var->xres_virtual = var->xres;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530 /* use highest possible virtual resolution */
Krzysztof Helt09a29102008-09-02 14:35:51 -0700531 if (var->yres_virtual == -1) {
532 var->yres_virtual = pixels / var->xres_virtual;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533
Krzysztof Helt8503df62007-10-16 01:29:08 -0700534 printk(KERN_INFO "cirrusfb: virtual resolution set to "
535 "maximum of %dx%d\n", var->xres_virtual,
536 var->yres_virtual);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 if (var->yres_virtual < var->yres)
539 var->yres_virtual = var->yres;
540
Krzysztof Helt09a29102008-09-02 14:35:51 -0700541 if (var->xres_virtual * var->yres_virtual > pixels) {
542 printk(KERN_ERR "cirrusfb: mode %dx%dx%d rejected... "
543 "virtual resolution too high to fit into video memory!\n",
544 var->xres_virtual, var->yres_virtual,
545 var->bits_per_pixel);
546 DPRINTK("EXIT - EINVAL error\n");
547 return -EINVAL;
548 }
549
550
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551 if (var->xoffset < 0)
552 var->xoffset = 0;
553 if (var->yoffset < 0)
554 var->yoffset = 0;
555
556 /* truncate xoffset and yoffset to maximum if too high */
557 if (var->xoffset > var->xres_virtual - var->xres)
558 var->xoffset = var->xres_virtual - var->xres - 1;
559 if (var->yoffset > var->yres_virtual - var->yres)
560 var->yoffset = var->yres_virtual - var->yres - 1;
561
562 switch (var->bits_per_pixel) {
563 case 1:
564 var->red.offset = 0;
565 var->red.length = 1;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700566 var->green = var->red;
567 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568 break;
569
570 case 8:
571 var->red.offset = 0;
572 var->red.length = 6;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700573 var->green = var->red;
574 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 break;
576
577 case 16:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700578 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 var->red.offset = 2;
580 var->green.offset = -3;
581 var->blue.offset = 8;
582 } else {
583 var->red.offset = 10;
584 var->green.offset = 5;
585 var->blue.offset = 0;
586 }
587 var->red.length = 5;
588 var->green.length = 5;
589 var->blue.length = 5;
590 break;
591
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592 case 32:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700593 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594 var->red.offset = 8;
595 var->green.offset = 16;
596 var->blue.offset = 24;
597 } else {
598 var->red.offset = 16;
599 var->green.offset = 8;
600 var->blue.offset = 0;
601 }
602 var->red.length = 8;
603 var->green.length = 8;
604 var->blue.length = 8;
605 break;
606
607 default:
608 DPRINTK("Unsupported bpp size: %d\n", var->bits_per_pixel);
Richard Knutssonc930faa2007-05-08 00:38:29 -0700609 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610 /* should never occur */
611 break;
612 }
613
614 var->red.msb_right =
615 var->green.msb_right =
616 var->blue.msb_right =
617 var->transp.offset =
618 var->transp.length =
619 var->transp.msb_right = 0;
620
621 yres = var->yres;
622 if (var->vmode & FB_VMODE_DOUBLE)
623 yres *= 2;
624 else if (var->vmode & FB_VMODE_INTERLACED)
625 yres = (yres + 1) / 2;
626
627 if (yres >= 1280) {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700628 printk(KERN_ERR "cirrusfb: ERROR: VerticalTotal >= 1280; "
629 "special treatment required! (TODO)\n");
630 DPRINTK("EXIT - EINVAL error\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631 return -EINVAL;
632 }
633
634 return 0;
635}
636
Krzysztof Helt8503df62007-10-16 01:29:08 -0700637static int cirrusfb_decode_var(const struct fb_var_screeninfo *var,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638 struct cirrusfb_regs *regs,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700639 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640{
641 long freq;
642 long maxclock;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700643 int maxclockidx = var->bits_per_pixel >> 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645
Krzysztof Helt8503df62007-10-16 01:29:08 -0700646 switch (var->bits_per_pixel) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647 case 1:
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700648 info->fix.line_length = var->xres_virtual / 8;
649 info->fix.visual = FB_VISUAL_MONO10;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650 break;
651
652 case 8:
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700653 info->fix.line_length = var->xres_virtual;
654 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655 break;
656
657 case 16:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 case 32:
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700659 info->fix.line_length = var->xres_virtual * maxclockidx;
Krzysztof Helt3b921832008-10-15 22:03:41 -0700660 info->fix.visual = FB_VISUAL_TRUECOLOR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661 break;
662
663 default:
664 DPRINTK("Unsupported bpp size: %d\n", var->bits_per_pixel);
Richard Knutssonc930faa2007-05-08 00:38:29 -0700665 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666 /* should never occur */
667 break;
668 }
669
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700670 info->fix.type = FB_TYPE_PACKED_PIXELS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671
672 /* convert from ps to kHz */
Krzysztof Helt060b6002007-10-16 01:29:13 -0700673 freq = PICOS2KHZ(var->pixclock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674
Krzysztof Helt8503df62007-10-16 01:29:08 -0700675 DPRINTK("desired pixclock: %ld kHz\n", freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676
677 maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx];
678 regs->multiplexing = 0;
679
680 /* If the frequency is greater than we can support, we might be able
681 * to use multiplexing for the video mode */
682 if (freq > maxclock) {
683 switch (cinfo->btype) {
684 case BT_ALPINE:
685 case BT_GD5480:
686 regs->multiplexing = 1;
687 break;
688
689 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700690 printk(KERN_ERR "cirrusfb: Frequency greater "
691 "than maxclock (%ld kHz)\n", maxclock);
692 DPRINTK("EXIT - return -EINVAL\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693 return -EINVAL;
694 }
695 }
696#if 0
697 /* TODO: If we have a 1MB 5434, we need to put ourselves in a mode where
698 * the VCLK is double the pixel clock. */
699 switch (var->bits_per_pixel) {
700 case 16:
701 case 32:
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700702 if (var->xres <= 800)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700703 /* Xbh has this type of clock for 32-bit */
704 freq /= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705 break;
706 }
707#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -0700708 regs->mclk = cirrusfb_get_mclk(freq, var->bits_per_pixel,
709 &regs->divMCLK);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711 return 0;
712}
713
Krzysztof Helt8503df62007-10-16 01:29:08 -0700714static void cirrusfb_set_mclk(const struct cirrusfb_info *cinfo, int val,
715 int div)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716{
Krzysztof Helt8503df62007-10-16 01:29:08 -0700717 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718
719 if (div == 2) {
720 /* VCLK = MCLK/2 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700721 unsigned char old = vga_rseq(cinfo->regbase, CL_SEQR1E);
722 vga_wseq(cinfo->regbase, CL_SEQR1E, old | 0x1);
723 vga_wseq(cinfo->regbase, CL_SEQR1F, 0x40 | (val & 0x3f));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 } else if (div == 1) {
725 /* VCLK = MCLK */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700726 unsigned char old = vga_rseq(cinfo->regbase, CL_SEQR1E);
727 vga_wseq(cinfo->regbase, CL_SEQR1E, old & ~0x1);
728 vga_wseq(cinfo->regbase, CL_SEQR1F, 0x40 | (val & 0x3f));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700730 vga_wseq(cinfo->regbase, CL_SEQR1F, val & 0x3f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700731 }
732}
733
734/*************************************************************************
735 cirrusfb_set_par_foo()
736
737 actually writes the values for a new video mode into the hardware,
738**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700739static int cirrusfb_set_par_foo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740{
741 struct cirrusfb_info *cinfo = info->par;
742 struct fb_var_screeninfo *var = &info->var;
743 struct cirrusfb_regs regs;
744 u8 __iomem *regbase = cinfo->regbase;
745 unsigned char tmp;
746 int offset = 0, err;
747 const struct cirrusfb_board_info_rec *bi;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700748 int hdispend, hsyncstart, hsyncend, htotal;
749 int yres, vdispend, vsyncstart, vsyncend, vtotal;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700750 long freq;
751 int nom, den, div;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752
Krzysztof Helt8503df62007-10-16 01:29:08 -0700753 DPRINTK("ENTER\n");
754 DPRINTK("Requested mode: %dx%dx%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700756 DPRINTK("pixclock: %d\n", var->pixclock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700758 init_vgachip(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759
760 err = cirrusfb_decode_var(var, &regs, info);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700761 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762 /* should never happen */
763 DPRINTK("mode change aborted. invalid var.\n");
764 return -EINVAL;
765 }
766
767 bi = &cirrusfb_board_info[cinfo->btype];
768
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700769 hsyncstart = var->xres + var->right_margin;
770 hsyncend = hsyncstart + var->hsync_len;
771 htotal = (hsyncend + var->left_margin) / 8 - 5;
772 hdispend = var->xres / 8 - 1;
773 hsyncstart = hsyncstart / 8 + 1;
774 hsyncend = hsyncend / 8 + 1;
775
776 yres = var->yres;
777 vsyncstart = yres + var->lower_margin;
778 vsyncend = vsyncstart + var->vsync_len;
779 vtotal = vsyncend + var->upper_margin;
780 vdispend = yres - 1;
781
782 if (var->vmode & FB_VMODE_DOUBLE) {
783 yres *= 2;
784 vsyncstart *= 2;
785 vsyncend *= 2;
786 vtotal *= 2;
787 } else if (var->vmode & FB_VMODE_INTERLACED) {
788 yres = (yres + 1) / 2;
789 vsyncstart = (vsyncstart + 1) / 2;
790 vsyncend = (vsyncend + 1) / 2;
791 vtotal = (vtotal + 1) / 2;
792 }
793
794 vtotal -= 2;
795 vsyncstart -= 1;
796 vsyncend -= 1;
797
798 if (yres >= 1024) {
799 vtotal /= 2;
800 vsyncstart /= 2;
801 vsyncend /= 2;
802 vdispend /= 2;
803 }
804 if (regs.multiplexing) {
805 htotal /= 2;
806 hsyncstart /= 2;
807 hsyncend /= 2;
808 hdispend /= 2;
809 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810 /* unlock register VGA_CRTC_H_TOTAL..CRT7 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700811 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20); /* previously: 0x00) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812
813 /* if debugging is enabled, all parameters get output before writing */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700814 DPRINTK("CRT0: %d\n", htotal);
815 vga_wcrt(regbase, VGA_CRTC_H_TOTAL, htotal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700817 DPRINTK("CRT1: %d\n", hdispend);
818 vga_wcrt(regbase, VGA_CRTC_H_DISP, hdispend);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700820 DPRINTK("CRT2: %d\n", var->xres / 8);
821 vga_wcrt(regbase, VGA_CRTC_H_BLANK_START, var->xres / 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822
Krzysztof Helt8503df62007-10-16 01:29:08 -0700823 /* + 128: Compatible read */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700824 DPRINTK("CRT3: 128+%d\n", (htotal + 5) % 32);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700825 vga_wcrt(regbase, VGA_CRTC_H_BLANK_END,
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700826 128 + ((htotal + 5) % 32));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700828 DPRINTK("CRT4: %d\n", hsyncstart);
829 vga_wcrt(regbase, VGA_CRTC_H_SYNC_START, hsyncstart);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700831 tmp = hsyncend % 32;
832 if ((htotal + 5) & 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833 tmp += 128;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700834 DPRINTK("CRT5: %d\n", tmp);
835 vga_wcrt(regbase, VGA_CRTC_H_SYNC_END, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700837 DPRINTK("CRT6: %d\n", vtotal & 0xff);
838 vga_wcrt(regbase, VGA_CRTC_V_TOTAL, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839
840 tmp = 16; /* LineCompare bit #9 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700841 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700843 if (vdispend & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844 tmp |= 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700845 if (vsyncstart & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846 tmp |= 4;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700847 if ((vdispend + 1) & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 tmp |= 8;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700849 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700851 if (vdispend & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700853 if (vsyncstart & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854 tmp |= 128;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700855 DPRINTK("CRT7: %d\n", tmp);
856 vga_wcrt(regbase, VGA_CRTC_OVERFLOW, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857
858 tmp = 0x40; /* LineCompare bit #8 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700859 if ((vdispend + 1) & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860 tmp |= 0x20;
861 if (var->vmode & FB_VMODE_DOUBLE)
862 tmp |= 0x80;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700863 DPRINTK("CRT9: %d\n", tmp);
864 vga_wcrt(regbase, VGA_CRTC_MAX_SCAN, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700866 DPRINTK("CRT10: %d\n", vsyncstart & 0xff);
867 vga_wcrt(regbase, VGA_CRTC_V_SYNC_START, vsyncstart & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700869 DPRINTK("CRT11: 64+32+%d\n", vsyncend % 16);
870 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, vsyncend % 16 + 64 + 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700871
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700872 DPRINTK("CRT12: %d\n", vdispend & 0xff);
873 vga_wcrt(regbase, VGA_CRTC_V_DISP_END, vdispend & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700875 DPRINTK("CRT15: %d\n", (vdispend + 1) & 0xff);
876 vga_wcrt(regbase, VGA_CRTC_V_BLANK_START, (vdispend + 1) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700878 DPRINTK("CRT16: %d\n", vtotal & 0xff);
879 vga_wcrt(regbase, VGA_CRTC_V_BLANK_END, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880
Krzysztof Helt8503df62007-10-16 01:29:08 -0700881 DPRINTK("CRT18: 0xff\n");
882 vga_wcrt(regbase, VGA_CRTC_LINE_COMPARE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883
884 tmp = 0;
885 if (var->vmode & FB_VMODE_INTERLACED)
886 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700887 if ((htotal + 5) & 64)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888 tmp |= 16;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700889 if ((htotal + 5) & 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700890 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700891 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700893 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894 tmp |= 128;
895
Krzysztof Helt8503df62007-10-16 01:29:08 -0700896 DPRINTK("CRT1a: %d\n", tmp);
897 vga_wcrt(regbase, CL_CRT1A, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700899 freq = PICOS2KHZ(var->pixclock);
900 bestclock(freq, &nom, &den, &div);
901
Linus Torvalds1da177e2005-04-16 15:20:36 -0700902 /* set VCLK0 */
903 /* hardware RefClock: 14.31818 MHz */
904 /* formula: VClk = (OSC * N) / (D * (1+P)) */
905 /* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */
906
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700907 vga_wseq(regbase, CL_SEQRB, nom);
908 tmp = den << 1;
909 if (div != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910 tmp |= 1;
911
Krzysztof Helt8503df62007-10-16 01:29:08 -0700912 /* 6 bit denom; ONLY 5434!!! (bugged me 10 days) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913 if ((cinfo->btype == BT_SD64) ||
914 (cinfo->btype == BT_ALPINE) ||
915 (cinfo->btype == BT_GD5480))
Krzysztof Helt8503df62007-10-16 01:29:08 -0700916 tmp |= 0x80;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917
Krzysztof Helt8503df62007-10-16 01:29:08 -0700918 DPRINTK("CL_SEQR1B: %ld\n", (long) tmp);
919 vga_wseq(regbase, CL_SEQR1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700921 if (yres >= 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922 /* 1280x1024 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700923 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924 else
925 /* mode control: VGA_CRTC_START_HI enable, ROTATE(?), 16bit
926 * address wrap, no compat. */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700927 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700928
Krzysztof Helt8503df62007-10-16 01:29:08 -0700929/* HAEH? vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20);
930 * previously: 0x00 unlock VGA_CRTC_H_TOTAL..CRT7 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931
932 /* don't know if it would hurt to also program this if no interlaced */
933 /* mode is used, but I feel better this way.. :-) */
934 if (var->vmode & FB_VMODE_INTERLACED)
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700935 vga_wcrt(regbase, VGA_CRTC_REGS, htotal / 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936 else
Krzysztof Helt8503df62007-10-16 01:29:08 -0700937 vga_wcrt(regbase, VGA_CRTC_REGS, 0x00); /* interlace control */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938
Krzysztof Helt8503df62007-10-16 01:29:08 -0700939 vga_wseq(regbase, VGA_SEQ_CHARACTER_MAP, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940
941 /* adjust horizontal/vertical sync type (low/high) */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700942 /* enable display memory & CRTC I/O address for color mode */
943 tmp = 0x03;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
945 tmp |= 0x40;
946 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
947 tmp |= 0x80;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700948 WGen(cinfo, VGA_MIS_W, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949
Krzysztof Helt8503df62007-10-16 01:29:08 -0700950 /* Screen A Preset Row-Scan register */
951 vga_wcrt(regbase, VGA_CRTC_PRESET_ROW, 0);
952 /* text cursor on and start line */
953 vga_wcrt(regbase, VGA_CRTC_CURSOR_START, 0);
954 /* text cursor end line */
955 vga_wcrt(regbase, VGA_CRTC_CURSOR_END, 31);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700956
957 /******************************************************
958 *
959 * 1 bpp
960 *
961 */
962
963 /* programming for different color depths */
964 if (var->bits_per_pixel == 1) {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700965 DPRINTK("cirrusfb: preparing for 1 bit deep display\n");
966 vga_wgfx(regbase, VGA_GFX_MODE, 0); /* mode register */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967
968 /* SR07 */
969 switch (cinfo->btype) {
970 case BT_SD64:
971 case BT_PICCOLO:
972 case BT_PICASSO:
973 case BT_SPECTRUM:
974 case BT_PICASSO4:
975 case BT_ALPINE:
976 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700977 DPRINTK(" (for GD54xx)\n");
978 vga_wseq(regbase, CL_SEQR7,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979 regs.multiplexing ?
980 bi->sr07_1bpp_mux : bi->sr07_1bpp);
981 break;
982
983 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700984 DPRINTK(" (for GD546x)\n");
985 vga_wseq(regbase, CL_SEQR7,
986 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987 break;
988
989 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700990 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991 break;
992 }
993
994 /* Extended Sequencer Mode */
995 switch (cinfo->btype) {
996 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700997 /* setting the SEQRF on SD64 is not necessary
998 * (only during init)
999 */
1000 DPRINTK("(for SD64)\n");
1001 /* MCLK select */
1002 vga_wseq(regbase, CL_SEQR1F, 0x1a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001003 break;
1004
1005 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001006 case BT_SPECTRUM:
1007 DPRINTK("(for Piccolo/Spectrum)\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -07001008 /* ### ueberall 0x22? */
1009 /* ##vorher 1c MCLK select */
1010 vga_wseq(regbase, CL_SEQR1F, 0x22);
1011 /* evtl d0 bei 1 bit? avoid FIFO underruns..? */
1012 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001013 break;
1014
1015 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001016 DPRINTK("(for Picasso)\n");
1017 /* ##vorher 22 MCLK select */
1018 vga_wseq(regbase, CL_SEQR1F, 0x22);
1019 /* ## vorher d0 avoid FIFO underruns..? */
1020 vga_wseq(regbase, CL_SEQRF, 0xd0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021 break;
1022
Linus Torvalds1da177e2005-04-16 15:20:36 -07001023 case BT_PICASSO4:
1024 case BT_ALPINE:
1025 case BT_GD5480:
1026 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001027 DPRINTK(" (for GD54xx)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028 /* do nothing */
1029 break;
1030
1031 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001032 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033 break;
1034 }
1035
Krzysztof Helt8503df62007-10-16 01:29:08 -07001036 /* pixel mask: pass-through for first plane */
1037 WGen(cinfo, VGA_PEL_MSK, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038 if (regs.multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001039 /* hidden dac reg: 1280x1024 */
1040 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001041 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001042 /* hidden dac: nothing */
1043 WHDR(cinfo, 0);
1044 /* memory mode: odd/even, ext. memory */
1045 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x06);
1046 /* plane mask: only write to first plane */
1047 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048 offset = var->xres_virtual / 16;
1049 }
1050
1051 /******************************************************
1052 *
1053 * 8 bpp
1054 *
1055 */
1056
1057 else if (var->bits_per_pixel == 8) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001058 DPRINTK("cirrusfb: preparing for 8 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059 switch (cinfo->btype) {
1060 case BT_SD64:
1061 case BT_PICCOLO:
1062 case BT_PICASSO:
1063 case BT_SPECTRUM:
1064 case BT_PICASSO4:
1065 case BT_ALPINE:
1066 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001067 DPRINTK(" (for GD54xx)\n");
1068 vga_wseq(regbase, CL_SEQR7,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 regs.multiplexing ?
1070 bi->sr07_8bpp_mux : bi->sr07_8bpp);
1071 break;
1072
1073 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001074 DPRINTK(" (for GD546x)\n");
1075 vga_wseq(regbase, CL_SEQR7,
1076 vga_rseq(regbase, CL_SEQR7) | 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077 break;
1078
1079 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001080 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081 break;
1082 }
1083
1084 switch (cinfo->btype) {
1085 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001086 /* MCLK select */
1087 vga_wseq(regbase, CL_SEQR1F, 0x1d);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088 break;
1089
1090 case BT_PICCOLO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091 case BT_PICASSO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001093 /* ### vorher 1c MCLK select */
1094 vga_wseq(regbase, CL_SEQR1F, 0x22);
1095 /* Fast Page-Mode writes */
1096 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001097 break;
1098
1099 case BT_PICASSO4:
1100#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001101 /* ### INCOMPLETE!! */
1102 vga_wseq(regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001103#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07001104/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105 break;
1106
1107 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001108 DPRINTK(" (for GD543x)\n");
1109 cirrusfb_set_mclk(cinfo, regs.mclk, regs.divMCLK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001110 /* We already set SRF and SR1F */
1111 break;
1112
1113 case BT_GD5480:
1114 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001115 DPRINTK(" (for GD54xx)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001116 /* do nothing */
1117 break;
1118
1119 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001120 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001121 break;
1122 }
1123
Krzysztof Helt8503df62007-10-16 01:29:08 -07001124 /* mode register: 256 color mode */
1125 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1126 /* pixel mask: pass-through all planes */
1127 WGen(cinfo, VGA_PEL_MSK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128 if (regs.multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001129 /* hidden dac reg: 1280x1024 */
1130 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001132 /* hidden dac: nothing */
1133 WHDR(cinfo, 0);
1134 /* memory mode: chain4, ext. memory */
1135 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1136 /* plane mask: enable writing to all 4 planes */
1137 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138 offset = var->xres_virtual / 8;
1139 }
1140
1141 /******************************************************
1142 *
1143 * 16 bpp
1144 *
1145 */
1146
1147 else if (var->bits_per_pixel == 16) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001148 DPRINTK("cirrusfb: preparing for 16 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, 0xf7);
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, 0x87);
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, 0x27);
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:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001175 vga_wseq(regbase, CL_SEQR7, 0x27);
1176/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177 break;
1178
1179 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001180 DPRINTK(" (for GD543x)\n");
Krzysztof Helt3b921832008-10-15 22:03:41 -07001181 vga_wseq(regbase, CL_SEQR7, 0xa7);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001182 cirrusfb_set_mclk(cinfo, regs.mclk, regs.divMCLK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001183 break;
1184
1185 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001186 DPRINTK(" (for GD5480)\n");
1187 vga_wseq(regbase, CL_SEQR7, 0x17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001188 /* We already set SRF and SR1F */
1189 break;
1190
1191 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001192 DPRINTK(" (for GD546x)\n");
1193 vga_wseq(regbase, CL_SEQR7,
1194 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001195 break;
1196
1197 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001198 printk(KERN_WARNING "CIRRUSFB: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199 break;
1200 }
1201
Krzysztof Helt8503df62007-10-16 01:29:08 -07001202 /* mode register: 256 color mode */
1203 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1204 /* pixel mask: pass-through all planes */
1205 WGen(cinfo, VGA_PEL_MSK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001206#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001207 WHDR(cinfo, 0xc0); /* Copy Xbh */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001208#elif defined(CONFIG_ZORRO)
1209 /* FIXME: CONFIG_PCI and CONFIG_ZORRO may be defined both */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001210 WHDR(cinfo, 0xa0); /* hidden dac reg: nothing special */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001211#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07001212 /* memory mode: chain4, ext. memory */
1213 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1214 /* plane mask: enable writing to all 4 planes */
1215 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001216 offset = var->xres_virtual / 4;
1217 }
1218
1219 /******************************************************
1220 *
1221 * 32 bpp
1222 *
1223 */
1224
1225 else if (var->bits_per_pixel == 32) {
Krzysztof Helt1cea9a92008-10-15 22:03:37 -07001226 DPRINTK("cirrusfb: preparing for 32 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001227 switch (cinfo->btype) {
1228 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001229 /* Extended Sequencer Mode: 256c col. mode */
1230 vga_wseq(regbase, CL_SEQR7, 0xf9);
1231 /* MCLK select */
1232 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001233 break;
1234
1235 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001236 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001237 vga_wseq(regbase, CL_SEQR7, 0x85);
1238 /* Fast Page-Mode writes */
1239 vga_wseq(regbase, CL_SEQRF, 0xb0);
1240 /* MCLK select */
1241 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001242 break;
1243
1244 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001245 vga_wseq(regbase, CL_SEQR7, 0x25);
1246 /* Fast Page-Mode writes */
1247 vga_wseq(regbase, CL_SEQRF, 0xb0);
1248 /* MCLK select */
1249 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001250 break;
1251
Linus Torvalds1da177e2005-04-16 15:20:36 -07001252 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001253 vga_wseq(regbase, CL_SEQR7, 0x25);
1254/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255 break;
1256
1257 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001258 DPRINTK(" (for GD543x)\n");
1259 vga_wseq(regbase, CL_SEQR7, 0xa9);
1260 cirrusfb_set_mclk(cinfo, regs.mclk, regs.divMCLK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001261 break;
1262
1263 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001264 DPRINTK(" (for GD5480)\n");
1265 vga_wseq(regbase, CL_SEQR7, 0x19);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001266 /* We already set SRF and SR1F */
1267 break;
1268
1269 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001270 DPRINTK(" (for GD546x)\n");
1271 vga_wseq(regbase, CL_SEQR7,
1272 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001273 break;
1274
1275 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001276 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001277 break;
1278 }
1279
Krzysztof Helt8503df62007-10-16 01:29:08 -07001280 /* mode register: 256 color mode */
1281 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1282 /* pixel mask: pass-through all planes */
1283 WGen(cinfo, VGA_PEL_MSK, 0xff);
1284 /* hidden dac reg: 8-8-8 mode (24 or 32) */
1285 WHDR(cinfo, 0xc5);
1286 /* memory mode: chain4, ext. memory */
1287 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1288 /* plane mask: enable writing to all 4 planes */
1289 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290 offset = var->xres_virtual / 4;
1291 }
1292
1293 /******************************************************
1294 *
1295 * unknown/unsupported bpp
1296 *
1297 */
1298
Krzysztof Helt8503df62007-10-16 01:29:08 -07001299 else
1300 printk(KERN_ERR "cirrusfb: What's this?? "
1301 " requested color depth == %d.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001302 var->bits_per_pixel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001303
Krzysztof Helt8503df62007-10-16 01:29:08 -07001304 vga_wcrt(regbase, VGA_CRTC_OFFSET, offset & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001305 tmp = 0x22;
1306 if (offset & 0x100)
1307 tmp |= 0x10; /* offset overflow bit */
1308
Krzysztof Helt8503df62007-10-16 01:29:08 -07001309 /* screen start addr #16-18, fastpagemode cycles */
1310 vga_wcrt(regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001311
1312 if (cinfo->btype == BT_SD64 ||
1313 cinfo->btype == BT_PICASSO4 ||
1314 cinfo->btype == BT_ALPINE ||
1315 cinfo->btype == BT_GD5480)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001316 /* screen start address bit 19 */
1317 vga_wcrt(regbase, CL_CRT1D, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001318
Krzysztof Helt8503df62007-10-16 01:29:08 -07001319 /* text cursor location high */
1320 vga_wcrt(regbase, VGA_CRTC_CURSOR_HI, 0);
1321 /* text cursor location low */
1322 vga_wcrt(regbase, VGA_CRTC_CURSOR_LO, 0);
1323 /* underline row scanline = at very bottom */
1324 vga_wcrt(regbase, VGA_CRTC_UNDERLINE, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001325
Krzysztof Helt8503df62007-10-16 01:29:08 -07001326 /* controller mode */
1327 vga_wattr(regbase, VGA_ATC_MODE, 1);
1328 /* overscan (border) color */
1329 vga_wattr(regbase, VGA_ATC_OVERSCAN, 0);
1330 /* color plane enable */
1331 vga_wattr(regbase, VGA_ATC_PLANE_ENABLE, 15);
1332 /* pixel panning */
1333 vga_wattr(regbase, CL_AR33, 0);
1334 /* color select */
1335 vga_wattr(regbase, VGA_ATC_COLOR_PAGE, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001336
1337 /* [ EGS: SetOffset(); ] */
1338 /* From SetOffset(): Turn on VideoEnable bit in Attribute controller */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001339 AttrOn(cinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001340
Krzysztof Helt8503df62007-10-16 01:29:08 -07001341 /* set/reset register */
1342 vga_wgfx(regbase, VGA_GFX_SR_VALUE, 0);
1343 /* set/reset enable */
1344 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, 0);
1345 /* color compare */
1346 vga_wgfx(regbase, VGA_GFX_COMPARE_VALUE, 0);
1347 /* data rotate */
1348 vga_wgfx(regbase, VGA_GFX_DATA_ROTATE, 0);
1349 /* read map select */
1350 vga_wgfx(regbase, VGA_GFX_PLANE_READ, 0);
1351 /* miscellaneous register */
1352 vga_wgfx(regbase, VGA_GFX_MISC, 1);
1353 /* color don't care */
1354 vga_wgfx(regbase, VGA_GFX_COMPARE_MASK, 15);
1355 /* bit mask */
1356 vga_wgfx(regbase, VGA_GFX_BIT_MASK, 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357
Krzysztof Helt8503df62007-10-16 01:29:08 -07001358 /* graphics cursor attributes: nothing special */
1359 vga_wseq(regbase, CL_SEQR12, 0x0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001360
1361 /* finally, turn on everything - turn off "FullBandwidth" bit */
1362 /* also, set "DotClock%2" bit where requested */
1363 tmp = 0x01;
1364
1365/*** FB_VMODE_CLOCK_HALVE in linux/fb.h not defined anymore ?
1366 if (var->vmode & FB_VMODE_CLOCK_HALVE)
1367 tmp |= 0x08;
1368*/
1369
Krzysztof Helt8503df62007-10-16 01:29:08 -07001370 vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, tmp);
1371 DPRINTK("CL_SEQR1: %d\n", tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001372
1373 cinfo->currentmode = regs;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001374
1375 /* pan to requested offset */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001376 cirrusfb_pan_display(var, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377
1378#ifdef CIRRUSFB_DEBUG
Krzysztof Helt8503df62007-10-16 01:29:08 -07001379 cirrusfb_dump();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001380#endif
1381
Krzysztof Helt8503df62007-10-16 01:29:08 -07001382 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001383 return 0;
1384}
1385
1386/* for some reason incomprehensible to me, cirrusfb requires that you write
1387 * the registers twice for the settings to take..grr. -dte */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001388static int cirrusfb_set_par(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001390 cirrusfb_set_par_foo(info);
1391 return cirrusfb_set_par_foo(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001392}
1393
Krzysztof Helt8503df62007-10-16 01:29:08 -07001394static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
1395 unsigned blue, unsigned transp,
1396 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001397{
1398 struct cirrusfb_info *cinfo = info->par;
1399
1400 if (regno > 255)
1401 return -EINVAL;
1402
1403 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
1404 u32 v;
1405 red >>= (16 - info->var.red.length);
1406 green >>= (16 - info->var.green.length);
1407 blue >>= (16 - info->var.blue.length);
1408
Krzysztof Helt8503df62007-10-16 01:29:08 -07001409 if (regno >= 16)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001410 return 1;
1411 v = (red << info->var.red.offset) |
1412 (green << info->var.green.offset) |
1413 (blue << info->var.blue.offset);
1414
Krzysztof Helt060b6002007-10-16 01:29:13 -07001415 cinfo->pseudo_palette[regno] = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001416 return 0;
1417 }
1418
Krzysztof Helt8503df62007-10-16 01:29:08 -07001419 if (info->var.bits_per_pixel == 8)
1420 WClut(cinfo, regno, red >> 10, green >> 10, blue >> 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001421
1422 return 0;
1423
1424}
1425
1426/*************************************************************************
1427 cirrusfb_pan_display()
1428
1429 performs display panning - provided hardware permits this
1430**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07001431static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
1432 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001433{
1434 int xoffset = 0;
1435 int yoffset = 0;
1436 unsigned long base;
1437 unsigned char tmp = 0, tmp2 = 0, xpix;
1438 struct cirrusfb_info *cinfo = info->par;
1439
Krzysztof Helt8503df62007-10-16 01:29:08 -07001440 DPRINTK("ENTER\n");
1441 DPRINTK("virtual offset: (%d,%d)\n", var->xoffset, var->yoffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001442
1443 /* no range checks for xoffset and yoffset, */
1444 /* as fb_pan_display has already done this */
1445 if (var->vmode & FB_VMODE_YWRAP)
1446 return -EINVAL;
1447
1448 info->var.xoffset = var->xoffset;
1449 info->var.yoffset = var->yoffset;
1450
1451 xoffset = var->xoffset * info->var.bits_per_pixel / 8;
1452 yoffset = var->yoffset;
1453
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001454 base = yoffset * info->fix.line_length + xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001455
1456 if (info->var.bits_per_pixel == 1) {
1457 /* base is already correct */
1458 xpix = (unsigned char) (var->xoffset % 8);
1459 } else {
1460 base /= 4;
1461 xpix = (unsigned char) ((xoffset % 4) * 2);
1462 }
1463
Krzysztof Helt8503df62007-10-16 01:29:08 -07001464 cirrusfb_WaitBLT(cinfo->regbase); /* make sure all the BLT's are done */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001465
1466 /* lower 8 + 8 bits of screen start address */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001467 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO,
1468 (unsigned char) (base & 0xff));
1469 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI,
1470 (unsigned char) (base >> 8));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001471
1472 /* construct bits 16, 17 and 18 of screen start address */
1473 if (base & 0x10000)
1474 tmp |= 0x01;
1475 if (base & 0x20000)
1476 tmp |= 0x04;
1477 if (base & 0x40000)
1478 tmp |= 0x08;
1479
Krzysztof Helt8503df62007-10-16 01:29:08 -07001480 /* 0xf2 is %11110010, exclude tmp bits */
1481 tmp2 = (vga_rcrt(cinfo->regbase, CL_CRT1B) & 0xf2) | tmp;
1482 vga_wcrt(cinfo->regbase, CL_CRT1B, tmp2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001483
1484 /* construct bit 19 of screen start address */
Krzysztof Helt060b6002007-10-16 01:29:13 -07001485 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19)
1486 vga_wcrt(cinfo->regbase, CL_CRT1D, (base >> 12) & 0x80);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001487
Krzysztof Helt8503df62007-10-16 01:29:08 -07001488 /* write pixel panning value to AR33; this does not quite work in 8bpp
1489 *
1490 * ### Piccolo..? Will this work?
1491 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001492 if (info->var.bits_per_pixel == 1)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001493 vga_wattr(cinfo->regbase, CL_AR33, xpix);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001494
Krzysztof Helt8503df62007-10-16 01:29:08 -07001495 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001496
Krzysztof Helt8503df62007-10-16 01:29:08 -07001497 DPRINTK("EXIT\n");
1498 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001499}
1500
Krzysztof Helt8503df62007-10-16 01:29:08 -07001501static int cirrusfb_blank(int blank_mode, struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001502{
1503 /*
Krzysztof Helt8503df62007-10-16 01:29:08 -07001504 * Blank the screen if blank_mode != 0, else unblank. If blank == NULL
1505 * then the caller blanks by setting the CLUT (Color Look Up Table)
1506 * to all black. Return 0 if blanking succeeded, != 0 if un-/blanking
1507 * failed due to e.g. a video mode which doesn't support it.
1508 * Implements VESA suspend and powerdown modes on hardware that
1509 * supports disabling hsync/vsync:
1510 * blank_mode == 2: suspend vsync
1511 * blank_mode == 3: suspend hsync
1512 * blank_mode == 4: powerdown
Linus Torvalds1da177e2005-04-16 15:20:36 -07001513 */
1514 unsigned char val;
1515 struct cirrusfb_info *cinfo = info->par;
1516 int current_mode = cinfo->blank_mode;
1517
Krzysztof Helt8503df62007-10-16 01:29:08 -07001518 DPRINTK("ENTER, blank mode = %d\n", blank_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001519
1520 if (info->state != FBINFO_STATE_RUNNING ||
1521 current_mode == blank_mode) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001522 DPRINTK("EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001523 return 0;
1524 }
1525
1526 /* Undo current */
1527 if (current_mode == FB_BLANK_NORMAL ||
1528 current_mode == FB_BLANK_UNBLANK) {
1529 /* unblank the screen */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001530 val = vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE);
1531 /* clear "FullBandwidth" bit */
1532 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val & 0xdf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533 /* and undo VESA suspend trickery */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001534 vga_wgfx(cinfo->regbase, CL_GRE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001535 }
1536
1537 /* set new */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001538 if (blank_mode > FB_BLANK_NORMAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001539 /* blank the screen */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001540 val = vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE);
1541 /* set "FullBandwidth" bit */
1542 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val | 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001543 }
1544
1545 switch (blank_mode) {
1546 case FB_BLANK_UNBLANK:
1547 case FB_BLANK_NORMAL:
1548 break;
1549 case FB_BLANK_VSYNC_SUSPEND:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001550 vga_wgfx(cinfo->regbase, CL_GRE, 0x04);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001551 break;
1552 case FB_BLANK_HSYNC_SUSPEND:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001553 vga_wgfx(cinfo->regbase, CL_GRE, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001554 break;
1555 case FB_BLANK_POWERDOWN:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001556 vga_wgfx(cinfo->regbase, CL_GRE, 0x06);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001557 break;
1558 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001559 DPRINTK("EXIT, returning 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001560 return 1;
1561 }
1562
1563 cinfo->blank_mode = blank_mode;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001564 DPRINTK("EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001565
1566 /* Let fbcon do a soft blank for us */
1567 return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
1568}
1569/**** END Hardware specific Routines **************************************/
1570/****************************************************************************/
1571/**** BEGIN Internal Routines ***********************************************/
1572
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001573static void init_vgachip(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001574{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001575 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001576 const struct cirrusfb_board_info_rec *bi;
1577
Krzysztof Helt8503df62007-10-16 01:29:08 -07001578 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001579
Krzysztof Helt8503df62007-10-16 01:29:08 -07001580 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001581
1582 bi = &cirrusfb_board_info[cinfo->btype];
1583
1584 /* reset board globally */
1585 switch (cinfo->btype) {
1586 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001587 WSFR(cinfo, 0x01);
1588 udelay(500);
1589 WSFR(cinfo, 0x51);
1590 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001591 break;
1592 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001593 WSFR2(cinfo, 0xff);
1594 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001595 break;
1596 case BT_SD64:
1597 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001598 WSFR(cinfo, 0x1f);
1599 udelay(500);
1600 WSFR(cinfo, 0x4f);
1601 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001602 break;
1603 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001604 /* disable flickerfixer */
1605 vga_wcrt(cinfo->regbase, CL_CRT51, 0x00);
1606 mdelay(100);
1607 /* from Klaus' NetBSD driver: */
1608 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
1609 /* put blitter into 542x compat */
1610 vga_wgfx(cinfo->regbase, CL_GR33, 0x00);
1611 /* mode */
1612 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001613 break;
1614
1615 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001616 /* from Klaus' NetBSD driver: */
1617 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001618 break;
1619
1620 case BT_ALPINE:
1621 /* Nothing to do to reset the board. */
1622 break;
1623
1624 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001625 printk(KERN_ERR "cirrusfb: Warning: Unknown board type\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001626 break;
1627 }
1628
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001629 /* make sure RAM size set by this point */
1630 assert(info->screen_size > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001631
1632 /* the P4 is not fully initialized here; I rely on it having been */
1633 /* inited under AmigaOS already, which seems to work just fine */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001634 /* (Klaus advised to do it this way) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001635
1636 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001637 WGen(cinfo, CL_VSSM, 0x10); /* EGS: 0x16 */
1638 WGen(cinfo, CL_POS102, 0x01);
1639 WGen(cinfo, CL_VSSM, 0x08); /* EGS: 0x0e */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001640
1641 if (cinfo->btype != BT_SD64)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001642 WGen(cinfo, CL_VSSM2, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001643
Krzysztof Helt8503df62007-10-16 01:29:08 -07001644 /* reset sequencer logic */
1645 vga_wseq(cinfo->regbase, CL_SEQR0, 0x03);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001646
Krzysztof Helt8503df62007-10-16 01:29:08 -07001647 /* FullBandwidth (video off) and 8/9 dot clock */
1648 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, 0x21);
1649 /* polarity (-/-), disable access to display memory,
1650 * VGA_CRTC_START_HI base address: color
1651 */
1652 WGen(cinfo, VGA_MIS_W, 0xc1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001653
Krzysztof Helt8503df62007-10-16 01:29:08 -07001654 /* "magic cookie" - doesn't make any sense to me.. */
1655/* vga_wgfx(cinfo->regbase, CL_GRA, 0xce); */
1656 /* unlock all extension registers */
1657 vga_wseq(cinfo->regbase, CL_SEQR6, 0x12);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001658
Krzysztof Helt8503df62007-10-16 01:29:08 -07001659 /* reset blitter */
1660 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001661
1662 switch (cinfo->btype) {
1663 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001664 vga_wseq(cinfo->regbase, CL_SEQRF, 0x98);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001665 break;
1666 case BT_ALPINE:
1667 break;
1668 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001669 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001670 break;
1671 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001672 vga_wseq(cinfo->regbase, CL_SEQR16, 0x0f);
1673 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001674 break;
1675 }
1676 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001677 /* plane mask: nothing */
1678 vga_wseq(cinfo->regbase, VGA_SEQ_PLANE_WRITE, 0xff);
1679 /* character map select: doesn't even matter in gx mode */
1680 vga_wseq(cinfo->regbase, VGA_SEQ_CHARACTER_MAP, 0x00);
1681 /* memory mode: chain-4, no odd/even, ext. memory */
1682 vga_wseq(cinfo->regbase, VGA_SEQ_MEMORY_MODE, 0x0e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001683
1684 /* controller-internal base address of video memory */
1685 if (bi->init_sr07)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001686 vga_wseq(cinfo->regbase, CL_SEQR7, bi->sr07);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001687
Krzysztof Helt8503df62007-10-16 01:29:08 -07001688 /* vga_wseq(cinfo->regbase, CL_SEQR8, 0x00); */
1689 /* EEPROM control: shouldn't be necessary to write to this at all.. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001690
Krzysztof Helt8503df62007-10-16 01:29:08 -07001691 /* graphics cursor X position (incomplete; position gives rem. 3 bits */
1692 vga_wseq(cinfo->regbase, CL_SEQR10, 0x00);
1693 /* graphics cursor Y position (..."... ) */
1694 vga_wseq(cinfo->regbase, CL_SEQR11, 0x00);
1695 /* graphics cursor attributes */
1696 vga_wseq(cinfo->regbase, CL_SEQR12, 0x00);
1697 /* graphics cursor pattern address */
1698 vga_wseq(cinfo->regbase, CL_SEQR13, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001699
1700 /* writing these on a P4 might give problems.. */
1701 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001702 /* configuration readback and ext. color */
1703 vga_wseq(cinfo->regbase, CL_SEQR17, 0x00);
1704 /* signature generator */
1705 vga_wseq(cinfo->regbase, CL_SEQR18, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001706 }
1707
1708 /* MCLK select etc. */
1709 if (bi->init_sr1f)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001710 vga_wseq(cinfo->regbase, CL_SEQR1F, bi->sr1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001711
Krzysztof Helt8503df62007-10-16 01:29:08 -07001712 /* Screen A preset row scan: none */
1713 vga_wcrt(cinfo->regbase, VGA_CRTC_PRESET_ROW, 0x00);
1714 /* Text cursor start: disable text cursor */
1715 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_START, 0x20);
1716 /* Text cursor end: - */
1717 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_END, 0x00);
1718 /* Screen start address high: 0 */
1719 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, 0x00);
1720 /* Screen start address low: 0 */
1721 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, 0x00);
1722 /* text cursor location high: 0 */
1723 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_HI, 0x00);
1724 /* text cursor location low: 0 */
1725 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_LO, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001726
Krzysztof Helt8503df62007-10-16 01:29:08 -07001727 /* Underline Row scanline: - */
1728 vga_wcrt(cinfo->regbase, VGA_CRTC_UNDERLINE, 0x00);
1729 /* mode control: timing enable, byte mode, no compat modes */
1730 vga_wcrt(cinfo->regbase, VGA_CRTC_MODE, 0xc3);
1731 /* Line Compare: not needed */
1732 vga_wcrt(cinfo->regbase, VGA_CRTC_LINE_COMPARE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001733 /* ### add 0x40 for text modes with > 30 MHz pixclock */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001734 /* ext. display controls: ext.adr. wrap */
1735 vga_wcrt(cinfo->regbase, CL_CRT1B, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001736
Krzysztof Helt8503df62007-10-16 01:29:08 -07001737 /* Set/Reset registes: - */
1738 vga_wgfx(cinfo->regbase, VGA_GFX_SR_VALUE, 0x00);
1739 /* Set/Reset enable: - */
1740 vga_wgfx(cinfo->regbase, VGA_GFX_SR_ENABLE, 0x00);
1741 /* Color Compare: - */
1742 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_VALUE, 0x00);
1743 /* Data Rotate: - */
1744 vga_wgfx(cinfo->regbase, VGA_GFX_DATA_ROTATE, 0x00);
1745 /* Read Map Select: - */
1746 vga_wgfx(cinfo->regbase, VGA_GFX_PLANE_READ, 0x00);
1747 /* Mode: conf. for 16/4/2 color mode, no odd/even, read/write mode 0 */
1748 vga_wgfx(cinfo->regbase, VGA_GFX_MODE, 0x00);
1749 /* Miscellaneous: memory map base address, graphics mode */
1750 vga_wgfx(cinfo->regbase, VGA_GFX_MISC, 0x01);
1751 /* Color Don't care: involve all planes */
1752 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_MASK, 0x0f);
1753 /* Bit Mask: no mask at all */
1754 vga_wgfx(cinfo->regbase, VGA_GFX_BIT_MASK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001755 if (cinfo->btype == BT_ALPINE)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001756 /* (5434 can't have bit 3 set for bitblt) */
1757 vga_wgfx(cinfo->regbase, CL_GRB, 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001758 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001759 /* Graphics controller mode extensions: finer granularity,
1760 * 8byte data latches
1761 */
1762 vga_wgfx(cinfo->regbase, CL_GRB, 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001763
Krzysztof Helt8503df62007-10-16 01:29:08 -07001764 vga_wgfx(cinfo->regbase, CL_GRC, 0xff); /* Color Key compare: - */
1765 vga_wgfx(cinfo->regbase, CL_GRD, 0x00); /* Color Key compare mask: - */
1766 vga_wgfx(cinfo->regbase, CL_GRE, 0x00); /* Miscellaneous control: - */
1767 /* Background color byte 1: - */
1768 /* vga_wgfx (cinfo->regbase, CL_GR10, 0x00); */
1769 /* vga_wgfx (cinfo->regbase, CL_GR11, 0x00); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001770
Krzysztof Helt8503df62007-10-16 01:29:08 -07001771 /* Attribute Controller palette registers: "identity mapping" */
1772 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE0, 0x00);
1773 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE1, 0x01);
1774 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE2, 0x02);
1775 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE3, 0x03);
1776 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE4, 0x04);
1777 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE5, 0x05);
1778 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE6, 0x06);
1779 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE7, 0x07);
1780 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE8, 0x08);
1781 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE9, 0x09);
1782 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEA, 0x0a);
1783 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEB, 0x0b);
1784 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEC, 0x0c);
1785 vga_wattr(cinfo->regbase, VGA_ATC_PALETTED, 0x0d);
1786 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEE, 0x0e);
1787 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEF, 0x0f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001788
Krzysztof Helt8503df62007-10-16 01:29:08 -07001789 /* Attribute Controller mode: graphics mode */
1790 vga_wattr(cinfo->regbase, VGA_ATC_MODE, 0x01);
1791 /* Overscan color reg.: reg. 0 */
1792 vga_wattr(cinfo->regbase, VGA_ATC_OVERSCAN, 0x00);
1793 /* Color Plane enable: Enable all 4 planes */
1794 vga_wattr(cinfo->regbase, VGA_ATC_PLANE_ENABLE, 0x0f);
1795/* ### vga_wattr(cinfo->regbase, CL_AR33, 0x00); * Pixel Panning: - */
1796 /* Color Select: - */
1797 vga_wattr(cinfo->regbase, VGA_ATC_COLOR_PAGE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001798
Krzysztof Helt8503df62007-10-16 01:29:08 -07001799 WGen(cinfo, VGA_PEL_MSK, 0xff); /* Pixel mask: no mask */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001800
1801 if (cinfo->btype != BT_ALPINE && cinfo->btype != BT_GD5480)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001802 /* polarity (-/-), enable display mem,
1803 * VGA_CRTC_START_HI i/o base = color
1804 */
1805 WGen(cinfo, VGA_MIS_W, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001806
Krzysztof Helt8503df62007-10-16 01:29:08 -07001807 /* BLT Start/status: Blitter reset */
1808 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
1809 /* - " - : "end-of-reset" */
1810 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001811
1812 /* misc... */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001813 WHDR(cinfo, 0); /* Hidden DAC register: - */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001814
Krzysztof Helt8503df62007-10-16 01:29:08 -07001815 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001816 return;
1817}
1818
Krzysztof Helt8503df62007-10-16 01:29:08 -07001819static void switch_monitor(struct cirrusfb_info *cinfo, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001820{
1821#ifdef CONFIG_ZORRO /* only works on Zorro boards */
1822 static int IsOn = 0; /* XXX not ok for multiple boards */
1823
Krzysztof Helt8503df62007-10-16 01:29:08 -07001824 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001825
1826 if (cinfo->btype == BT_PICASSO4)
1827 return; /* nothing to switch */
1828 if (cinfo->btype == BT_ALPINE)
1829 return; /* nothing to switch */
1830 if (cinfo->btype == BT_GD5480)
1831 return; /* nothing to switch */
1832 if (cinfo->btype == BT_PICASSO) {
1833 if ((on && !IsOn) || (!on && IsOn))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001834 WSFR(cinfo, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001835
Krzysztof Helt8503df62007-10-16 01:29:08 -07001836 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001837 return;
1838 }
1839 if (on) {
1840 switch (cinfo->btype) {
1841 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001842 WSFR(cinfo, cinfo->SFR | 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001843 break;
1844 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001845 WSFR(cinfo, cinfo->SFR | 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001846 break;
1847 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001848 WSFR(cinfo, 0x6f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001849 break;
1850 default: /* do nothing */ break;
1851 }
1852 } else {
1853 switch (cinfo->btype) {
1854 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001855 WSFR(cinfo, cinfo->SFR & 0xde);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001856 break;
1857 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001858 WSFR(cinfo, cinfo->SFR & 0xd7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001859 break;
1860 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001861 WSFR(cinfo, 0x4f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001862 break;
1863 default: /* do nothing */ break;
1864 }
1865 }
1866
Krzysztof Helt8503df62007-10-16 01:29:08 -07001867 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001868#endif /* CONFIG_ZORRO */
1869}
1870
Linus Torvalds1da177e2005-04-16 15:20:36 -07001871/******************************************/
1872/* Linux 2.6-style accelerated functions */
1873/******************************************/
1874
Krzysztof Helt8503df62007-10-16 01:29:08 -07001875static void cirrusfb_fillrect(struct fb_info *info,
1876 const struct fb_fillrect *region)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001877{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001878 struct fb_fillrect modded;
1879 int vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001880 struct cirrusfb_info *cinfo = info->par;
1881 int m = info->var.bits_per_pixel;
1882 u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ?
1883 cinfo->pseudo_palette[region->color] : region->color;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001884
1885 if (info->state != FBINFO_STATE_RUNNING)
1886 return;
1887 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1888 cfb_fillrect(info, region);
1889 return;
1890 }
1891
1892 vxres = info->var.xres_virtual;
1893 vyres = info->var.yres_virtual;
1894
1895 memcpy(&modded, region, sizeof(struct fb_fillrect));
1896
Krzysztof Helt8503df62007-10-16 01:29:08 -07001897 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001898 modded.dx >= vxres || modded.dy >= vyres)
1899 return;
1900
Krzysztof Helt8503df62007-10-16 01:29:08 -07001901 if (modded.dx + modded.width > vxres)
1902 modded.width = vxres - modded.dx;
1903 if (modded.dy + modded.height > vyres)
1904 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001905
Krzysztof Helt060b6002007-10-16 01:29:13 -07001906 cirrusfb_RectFill(cinfo->regbase,
1907 info->var.bits_per_pixel,
1908 (region->dx * m) / 8, region->dy,
1909 (region->width * m) / 8, region->height,
1910 color,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001911 info->fix.line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001912}
1913
Krzysztof Helt8503df62007-10-16 01:29:08 -07001914static void cirrusfb_copyarea(struct fb_info *info,
1915 const struct fb_copyarea *area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001916{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001917 struct fb_copyarea modded;
1918 u32 vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001919 struct cirrusfb_info *cinfo = info->par;
1920 int m = info->var.bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001921
1922 if (info->state != FBINFO_STATE_RUNNING)
1923 return;
1924 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1925 cfb_copyarea(info, area);
1926 return;
1927 }
1928
1929 vxres = info->var.xres_virtual;
1930 vyres = info->var.yres_virtual;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001931 memcpy(&modded, area, sizeof(struct fb_copyarea));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001932
Krzysztof Helt8503df62007-10-16 01:29:08 -07001933 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001934 modded.sx >= vxres || modded.sy >= vyres ||
1935 modded.dx >= vxres || modded.dy >= vyres)
1936 return;
1937
Krzysztof Helt8503df62007-10-16 01:29:08 -07001938 if (modded.sx + modded.width > vxres)
1939 modded.width = vxres - modded.sx;
1940 if (modded.dx + modded.width > vxres)
1941 modded.width = vxres - modded.dx;
1942 if (modded.sy + modded.height > vyres)
1943 modded.height = vyres - modded.sy;
1944 if (modded.dy + modded.height > vyres)
1945 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001946
Krzysztof Helt060b6002007-10-16 01:29:13 -07001947 cirrusfb_BitBLT(cinfo->regbase, info->var.bits_per_pixel,
1948 (area->sx * m) / 8, area->sy,
1949 (area->dx * m) / 8, area->dy,
1950 (area->width * m) / 8, area->height,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001951 info->fix.line_length);
Krzysztof Helt060b6002007-10-16 01:29:13 -07001952
Linus Torvalds1da177e2005-04-16 15:20:36 -07001953}
1954
Krzysztof Helt8503df62007-10-16 01:29:08 -07001955static void cirrusfb_imageblit(struct fb_info *info,
1956 const struct fb_image *image)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001957{
1958 struct cirrusfb_info *cinfo = info->par;
1959
Krzysztof Helt8503df62007-10-16 01:29:08 -07001960 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001961 cfb_imageblit(info, image);
1962}
1963
Linus Torvalds1da177e2005-04-16 15:20:36 -07001964#ifdef CONFIG_PPC_PREP
1965#define PREP_VIDEO_BASE ((volatile unsigned long) 0xC0000000)
1966#define PREP_IO_BASE ((volatile unsigned char *) 0x80000000)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001967static void get_prep_addrs(unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001968{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001969 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001970
1971 *display = PREP_VIDEO_BASE;
1972 *registers = (unsigned long) PREP_IO_BASE;
1973
Krzysztof Helt8503df62007-10-16 01:29:08 -07001974 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001975}
1976
1977#endif /* CONFIG_PPC_PREP */
1978
Linus Torvalds1da177e2005-04-16 15:20:36 -07001979#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001980static int release_io_ports;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001981
1982/* Pulled the logic from XFree86 Cirrus driver to get the memory size,
1983 * based on the DRAM bandwidth bit and DRAM bank switching bit. This
1984 * works with 1MB, 2MB and 4MB configurations (which the Motorola boards
1985 * seem to have. */
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07001986static unsigned int __devinit cirrusfb_get_memsize(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001987{
1988 unsigned long mem;
1989 unsigned char SRF;
1990
Krzysztof Helt8503df62007-10-16 01:29:08 -07001991 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001992
Krzysztof Helt8503df62007-10-16 01:29:08 -07001993 SRF = vga_rseq(regbase, CL_SEQRF);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001994 switch ((SRF & 0x18)) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001995 case 0x08:
1996 mem = 512 * 1024;
1997 break;
1998 case 0x10:
1999 mem = 1024 * 1024;
2000 break;
2001 /* 64-bit DRAM data bus width; assume 2MB. Also indicates 2MB memory
2002 * on the 5430.
2003 */
2004 case 0x18:
2005 mem = 2048 * 1024;
2006 break;
2007 default:
2008 printk(KERN_WARNING "CLgenfb: Unknown memory size!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002009 mem = 1024 * 1024;
2010 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07002011 if (SRF & 0x80)
2012 /* If DRAM bank switching is enabled, there must be twice as much
2013 * memory installed. (4MB on the 5434)
2014 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002015 mem *= 2;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002016
Linus Torvalds1da177e2005-04-16 15:20:36 -07002017 /* TODO: Handling of GD5446/5480 (see XF86 sources ...) */
2018
Krzysztof Helt8503df62007-10-16 01:29:08 -07002019 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002020 return mem;
2021}
2022
Krzysztof Helt8503df62007-10-16 01:29:08 -07002023static void get_pci_addrs(const struct pci_dev *pdev,
2024 unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002025{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002026 assert(pdev != NULL);
2027 assert(display != NULL);
2028 assert(registers != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002029
Krzysztof Helt8503df62007-10-16 01:29:08 -07002030 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002031
2032 *display = 0;
2033 *registers = 0;
2034
2035 /* This is a best-guess for now */
2036
2037 if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
2038 *display = pci_resource_start(pdev, 1);
2039 *registers = pci_resource_start(pdev, 0);
2040 } else {
2041 *display = pci_resource_start(pdev, 0);
2042 *registers = pci_resource_start(pdev, 1);
2043 }
2044
Krzysztof Helt8503df62007-10-16 01:29:08 -07002045 assert(*display != 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002046
Krzysztof Helt8503df62007-10-16 01:29:08 -07002047 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002048}
2049
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002050static void cirrusfb_pci_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002051{
Krzysztof Helt64beab12008-10-15 22:03:38 -07002052 struct pci_dev *pdev = to_pci_dev(info->device);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002053
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002054 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002055#if 0 /* if system didn't claim this region, we would... */
2056 release_mem_region(0xA0000, 65535);
2057#endif
2058 if (release_io_ports)
2059 release_region(0x3C0, 32);
2060 pci_release_regions(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002061}
2062#endif /* CONFIG_PCI */
2063
Linus Torvalds1da177e2005-04-16 15:20:36 -07002064#ifdef CONFIG_ZORRO
Al Virod91f5bb2007-10-17 00:27:18 +01002065static void __devexit cirrusfb_zorro_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002066{
Al Virod91f5bb2007-10-17 00:27:18 +01002067 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt64beab12008-10-15 22:03:38 -07002068 struct zorro_dev *zdev = to_zorro_dev(info->device);
2069
2070 zorro_release_device(zdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002071
2072 if (cinfo->btype == BT_PICASSO4) {
2073 cinfo->regbase -= 0x600000;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002074 iounmap((void *)cinfo->regbase);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002075 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002076 } else {
Krzysztof Helt64beab12008-10-15 22:03:38 -07002077 if (zorro_resource_start(zdev) > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002078 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002079 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002080}
2081#endif /* CONFIG_ZORRO */
2082
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002083static int __devinit cirrusfb_set_fbinfo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002084{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002085 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002086 struct fb_var_screeninfo *var = &info->var;
2087
Linus Torvalds1da177e2005-04-16 15:20:36 -07002088 info->pseudo_palette = cinfo->pseudo_palette;
2089 info->flags = FBINFO_DEFAULT
2090 | FBINFO_HWACCEL_XPAN
2091 | FBINFO_HWACCEL_YPAN
2092 | FBINFO_HWACCEL_FILLRECT
2093 | FBINFO_HWACCEL_COPYAREA;
2094 if (noaccel)
2095 info->flags |= FBINFO_HWACCEL_DISABLED;
2096 info->fbops = &cirrusfb_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002097 if (cinfo->btype == BT_GD5480) {
2098 if (var->bits_per_pixel == 16)
2099 info->screen_base += 1 * MB_;
Krzysztof Helt1cea9a92008-10-15 22:03:37 -07002100 if (var->bits_per_pixel == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002101 info->screen_base += 2 * MB_;
2102 }
2103
2104 /* Fill fix common fields */
2105 strlcpy(info->fix.id, cirrusfb_board_info[cinfo->btype].name,
2106 sizeof(info->fix.id));
2107
2108 /* monochrome: only 1 memory plane */
2109 /* 8 bit and above: Use whole memory area */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002110 info->fix.smem_len = info->screen_size;
2111 if (var->bits_per_pixel == 1)
2112 info->fix.smem_len /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002113 info->fix.type_aux = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002114 info->fix.xpanstep = 1;
2115 info->fix.ypanstep = 1;
2116 info->fix.ywrapstep = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002117
2118 /* FIXME: map region at 0xB8000 if available, fill in here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002119 info->fix.mmio_len = 0;
2120 info->fix.accel = FB_ACCEL_NONE;
2121
2122 fb_alloc_cmap(&info->cmap, 256, 0);
2123
2124 return 0;
2125}
2126
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002127static int __devinit cirrusfb_register(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002128{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002129 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002130 int err;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002131 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002132
Krzysztof Helt8503df62007-10-16 01:29:08 -07002133 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002134
Krzysztof Helt8503df62007-10-16 01:29:08 -07002135 printk(KERN_INFO "cirrusfb: Driver for Cirrus Logic based "
2136 "graphic boards, v" CIRRUSFB_VERSION "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002137
Linus Torvalds1da177e2005-04-16 15:20:36 -07002138 btype = cinfo->btype;
2139
2140 /* sanity checks */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002141 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002142
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002143 /* set all the vital stuff */
2144 cirrusfb_set_fbinfo(info);
2145
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002146 DPRINTK("cirrusfb: (RAM start set to: 0x%p)\n", info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002147
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002148 err = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8);
2149 if (!err) {
2150 DPRINTK("wrong initial video mode\n");
2151 err = -EINVAL;
2152 goto err_dealloc_cmap;
2153 }
2154
Linus Torvalds1da177e2005-04-16 15:20:36 -07002155 info->var.activate = FB_ACTIVATE_NOW;
2156
2157 err = cirrusfb_decode_var(&info->var, &cinfo->currentmode, info);
2158 if (err < 0) {
2159 /* should never happen */
2160 DPRINTK("choking on default var... umm, no good.\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002161 goto err_dealloc_cmap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002162 }
2163
Linus Torvalds1da177e2005-04-16 15:20:36 -07002164 err = register_framebuffer(info);
2165 if (err < 0) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002166 printk(KERN_ERR "cirrusfb: could not register "
2167 "fb device; err = %d!\n", err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002168 goto err_dealloc_cmap;
2169 }
2170
Krzysztof Helt8503df62007-10-16 01:29:08 -07002171 DPRINTK("EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002172 return 0;
2173
2174err_dealloc_cmap:
2175 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002176 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002177 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002178 return err;
2179}
2180
Krzysztof Helt8503df62007-10-16 01:29:08 -07002181static void __devexit cirrusfb_cleanup(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002182{
2183 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002184 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002185
Krzysztof Helt8503df62007-10-16 01:29:08 -07002186 switch_monitor(cinfo, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002187
Krzysztof Helt8503df62007-10-16 01:29:08 -07002188 unregister_framebuffer(info);
2189 fb_dealloc_cmap(&info->cmap);
2190 printk("Framebuffer unregistered\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002191 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002192 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002193
Krzysztof Helt8503df62007-10-16 01:29:08 -07002194 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002195}
2196
Linus Torvalds1da177e2005-04-16 15:20:36 -07002197#ifdef CONFIG_PCI
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002198static int __devinit cirrusfb_pci_register(struct pci_dev *pdev,
2199 const struct pci_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002200{
2201 struct cirrusfb_info *cinfo;
2202 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002203 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002204 unsigned long board_addr, board_size;
2205 int ret;
2206
2207 ret = pci_enable_device(pdev);
2208 if (ret < 0) {
2209 printk(KERN_ERR "cirrusfb: Cannot enable PCI device\n");
2210 goto err_out;
2211 }
2212
2213 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &pdev->dev);
2214 if (!info) {
2215 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
2216 ret = -ENOMEM;
2217 goto err_disable;
2218 }
2219
2220 cinfo = info->par;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002221 cinfo->btype = btype = (enum cirrus_board) ent->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002222
Krzysztof Helt7345de32007-10-16 01:29:11 -07002223 DPRINTK(" Found PCI device, base address 0 is 0x%x, btype set to %d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002224 pdev->resource[0].start, btype);
Krzysztof Helt7345de32007-10-16 01:29:11 -07002225 DPRINTK(" base address 1 is 0x%x\n", pdev->resource[1].start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002226
Krzysztof Helt8503df62007-10-16 01:29:08 -07002227 if (isPReP) {
2228 pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, 0x00000000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002229#ifdef CONFIG_PPC_PREP
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002230 get_prep_addrs(&board_addr, &info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002231#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07002232 /* PReP dies if we ioremap the IO registers, but it works w/out... */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002233 cinfo->regbase = (char __iomem *) info->fix.mmio_start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002234 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002235 DPRINTK("Attempt to get PCI info for Cirrus Graphics Card\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002236 get_pci_addrs(pdev, &board_addr, &info->fix.mmio_start);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002237 /* FIXME: this forces VGA. alternatives? */
2238 cinfo->regbase = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002239 }
2240
Krzysztof Helt8503df62007-10-16 01:29:08 -07002241 DPRINTK("Board address: 0x%lx, register address: 0x%lx\n",
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002242 board_addr, info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002243
2244 board_size = (btype == BT_GD5480) ?
Krzysztof Helt8503df62007-10-16 01:29:08 -07002245 32 * MB_ : cirrusfb_get_memsize(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002246
2247 ret = pci_request_regions(pdev, "cirrusfb");
Krzysztof Helt8503df62007-10-16 01:29:08 -07002248 if (ret < 0) {
2249 printk(KERN_ERR "cirrusfb: cannot reserve region 0x%lx, "
2250 "abort\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002251 board_addr);
2252 goto err_release_fb;
2253 }
2254#if 0 /* if the system didn't claim this region, we would... */
2255 if (!request_mem_region(0xA0000, 65535, "cirrusfb")) {
2256 printk(KERN_ERR "cirrusfb: cannot reserve region 0x%lx, abort\n"
2257,
2258 0xA0000L);
2259 ret = -EBUSY;
2260 goto err_release_regions;
2261 }
2262#endif
2263 if (request_region(0x3C0, 32, "cirrusfb"))
2264 release_io_ports = 1;
2265
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002266 info->screen_base = ioremap(board_addr, board_size);
2267 if (!info->screen_base) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002268 ret = -EIO;
2269 goto err_release_legacy;
2270 }
2271
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002272 info->fix.smem_start = board_addr;
2273 info->screen_size = board_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002274 cinfo->unmap = cirrusfb_pci_unmap;
2275
Philippe De Muytere59b6a52008-06-12 15:21:45 -07002276 printk(KERN_INFO "RAM (%lu kB) at 0x%lx, Cirrus "
2277 "Logic chipset on PCI bus\n",
2278 info->screen_size >> 10, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002279 pci_set_drvdata(pdev, info);
2280
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002281 ret = cirrusfb_register(info);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002282 if (ret)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002283 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002284 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002285
2286err_release_legacy:
2287 if (release_io_ports)
2288 release_region(0x3C0, 32);
2289#if 0
2290 release_mem_region(0xA0000, 65535);
2291err_release_regions:
2292#endif
2293 pci_release_regions(pdev);
2294err_release_fb:
2295 framebuffer_release(info);
2296err_disable:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002297err_out:
2298 return ret;
2299}
2300
Krzysztof Helt8503df62007-10-16 01:29:08 -07002301static void __devexit cirrusfb_pci_unregister(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002302{
2303 struct fb_info *info = pci_get_drvdata(pdev);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002304 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002305
Krzysztof Helt8503df62007-10-16 01:29:08 -07002306 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002307
Krzysztof Helt8503df62007-10-16 01:29:08 -07002308 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002309}
2310
2311static struct pci_driver cirrusfb_pci_driver = {
2312 .name = "cirrusfb",
2313 .id_table = cirrusfb_pci_table,
2314 .probe = cirrusfb_pci_register,
2315 .remove = __devexit_p(cirrusfb_pci_unregister),
2316#ifdef CONFIG_PM
2317#if 0
2318 .suspend = cirrusfb_pci_suspend,
2319 .resume = cirrusfb_pci_resume,
2320#endif
2321#endif
2322};
2323#endif /* CONFIG_PCI */
2324
Linus Torvalds1da177e2005-04-16 15:20:36 -07002325#ifdef CONFIG_ZORRO
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002326static int __devinit cirrusfb_zorro_register(struct zorro_dev *z,
2327 const struct zorro_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002328{
2329 struct cirrusfb_info *cinfo;
2330 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002331 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002332 struct zorro_dev *z2 = NULL;
2333 unsigned long board_addr, board_size, size;
2334 int ret;
2335
2336 btype = ent->driver_data;
2337 if (cirrusfb_zorro_table2[btype].id2)
2338 z2 = zorro_find_device(cirrusfb_zorro_table2[btype].id2, NULL);
2339 size = cirrusfb_zorro_table2[btype].size;
2340 printk(KERN_INFO "cirrusfb: %s board detected; ",
2341 cirrusfb_board_info[btype].name);
2342
2343 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &z->dev);
2344 if (!info) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002345 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002346 ret = -ENOMEM;
2347 goto err_out;
2348 }
2349
2350 cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002351 cinfo->btype = btype;
2352
Al Viro36ea96a2007-10-27 19:46:58 +01002353 assert(z);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002354 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002355
Linus Torvalds1da177e2005-04-16 15:20:36 -07002356 board_addr = zorro_resource_start(z);
2357 board_size = zorro_resource_len(z);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002358 info->screen_size = size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002359
2360 if (!zorro_request_device(z, "cirrusfb")) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002361 printk(KERN_ERR "cirrusfb: cannot reserve region 0x%lx, "
2362 "abort\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002363 board_addr);
2364 ret = -EBUSY;
2365 goto err_release_fb;
2366 }
2367
Krzysztof Helt8503df62007-10-16 01:29:08 -07002368 printk(" RAM (%lu MB) at $%lx, ", board_size / MB_, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002369
2370 ret = -EIO;
2371
2372 if (btype == BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002373 printk(KERN_INFO " REG at $%lx\n", board_addr + 0x600000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002374
2375 /* To be precise, for the P4 this is not the */
2376 /* begin of the board, but the begin of RAM. */
2377 /* for P4, map in its address space in 2 chunks (### TEST! ) */
2378 /* (note the ugly hardcoded 16M number) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002379 cinfo->regbase = ioremap(board_addr, 16777216);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002380 if (!cinfo->regbase)
2381 goto err_release_region;
2382
Krzysztof Helt8503df62007-10-16 01:29:08 -07002383 DPRINTK("cirrusfb: Virtual address for board set to: $%p\n",
2384 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002385 cinfo->regbase += 0x600000;
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002386 info->fix.mmio_start = board_addr + 0x600000;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002387
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002388 info->fix.smem_start = board_addr + 16777216;
2389 info->screen_base = ioremap(info->fix.smem_start, 16777216);
2390 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002391 goto err_unmap_regbase;
2392 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002393 printk(KERN_INFO " REG at $%lx\n",
2394 (unsigned long) z2->resource.start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002395
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002396 info->fix.smem_start = board_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002397 if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002398 info->screen_base = ioremap(board_addr, board_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002399 else
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002400 info->screen_base = (caddr_t) ZTWO_VADDR(board_addr);
2401 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002402 goto err_release_region;
2403
2404 /* set address for REG area of board */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002405 cinfo->regbase = (caddr_t) ZTWO_VADDR(z2->resource.start);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002406 info->fix.mmio_start = z2->resource.start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002407
Krzysztof Helt8503df62007-10-16 01:29:08 -07002408 DPRINTK("cirrusfb: Virtual address for board set to: $%p\n",
2409 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002410 }
2411 cinfo->unmap = cirrusfb_zorro_unmap;
2412
Krzysztof Helt8503df62007-10-16 01:29:08 -07002413 printk(KERN_INFO "Cirrus Logic chipset on Zorro bus\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002414 zorro_set_drvdata(z, info);
2415
Al Virod91f5bb2007-10-17 00:27:18 +01002416 ret = cirrusfb_register(info);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002417 if (ret) {
2418 if (btype == BT_PICASSO4) {
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002419 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002420 iounmap(cinfo->regbase - 0x600000);
2421 } else if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002422 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002423 }
2424 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002425
2426err_unmap_regbase:
2427 /* Parental advisory: explicit hack */
2428 iounmap(cinfo->regbase - 0x600000);
2429err_release_region:
2430 release_region(board_addr, board_size);
2431err_release_fb:
2432 framebuffer_release(info);
2433err_out:
2434 return ret;
2435}
2436
2437void __devexit cirrusfb_zorro_unregister(struct zorro_dev *z)
2438{
2439 struct fb_info *info = zorro_get_drvdata(z);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002440 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002441
Krzysztof Helt8503df62007-10-16 01:29:08 -07002442 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002443
Krzysztof Helt8503df62007-10-16 01:29:08 -07002444 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002445}
2446
2447static struct zorro_driver cirrusfb_zorro_driver = {
2448 .name = "cirrusfb",
2449 .id_table = cirrusfb_zorro_table,
2450 .probe = cirrusfb_zorro_register,
2451 .remove = __devexit_p(cirrusfb_zorro_unregister),
2452};
2453#endif /* CONFIG_ZORRO */
2454
2455static int __init cirrusfb_init(void)
2456{
2457 int error = 0;
2458
2459#ifndef MODULE
2460 char *option = NULL;
2461
2462 if (fb_get_options("cirrusfb", &option))
2463 return -ENODEV;
2464 cirrusfb_setup(option);
2465#endif
2466
2467#ifdef CONFIG_ZORRO
Bjorn Helgaas33d86752006-03-25 03:07:20 -08002468 error |= zorro_register_driver(&cirrusfb_zorro_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002469#endif
2470#ifdef CONFIG_PCI
2471 error |= pci_register_driver(&cirrusfb_pci_driver);
2472#endif
2473 return error;
2474}
2475
Linus Torvalds1da177e2005-04-16 15:20:36 -07002476#ifndef MODULE
2477static int __init cirrusfb_setup(char *options) {
2478 char *this_opt, s[32];
2479 int i;
2480
Krzysztof Helt8503df62007-10-16 01:29:08 -07002481 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002482
2483 if (!options || !*options)
2484 return 0;
2485
Krzysztof Helt8503df62007-10-16 01:29:08 -07002486 while ((this_opt = strsep(&options, ",")) != NULL) {
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002487 if (!*this_opt)
2488 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002489
2490 DPRINTK("cirrusfb_setup: option '%s'\n", this_opt);
2491
Linus Torvalds1da177e2005-04-16 15:20:36 -07002492 if (!strcmp(this_opt, "noaccel"))
2493 noaccel = 1;
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002494 else if (!strncmp(this_opt, "mode:", 5))
2495 mode_option = this_opt + 5;
2496 else
2497 mode_option = this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002498 }
2499 return 0;
2500}
2501#endif
2502
Linus Torvalds1da177e2005-04-16 15:20:36 -07002503 /*
2504 * Modularization
2505 */
2506
2507MODULE_AUTHOR("Copyright 1999,2000 Jeff Garzik <jgarzik@pobox.com>");
2508MODULE_DESCRIPTION("Accelerated FBDev driver for Cirrus Logic chips");
2509MODULE_LICENSE("GPL");
2510
Krzysztof Helt8503df62007-10-16 01:29:08 -07002511static void __exit cirrusfb_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002512{
2513#ifdef CONFIG_PCI
2514 pci_unregister_driver(&cirrusfb_pci_driver);
2515#endif
2516#ifdef CONFIG_ZORRO
2517 zorro_unregister_driver(&cirrusfb_zorro_driver);
2518#endif
2519}
2520
2521module_init(cirrusfb_init);
2522
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002523module_param(mode_option, charp, 0);
2524MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");
Krzysztof Helt55a0dd82008-10-15 22:03:41 -07002525module_param(noaccel, bool, 0);
2526MODULE_PARM_DESC(noaccel, "Disable acceleration");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002527
Linus Torvalds1da177e2005-04-16 15:20:36 -07002528#ifdef MODULE
2529module_exit(cirrusfb_exit);
2530#endif
2531
Linus Torvalds1da177e2005-04-16 15:20:36 -07002532/**********************************************************************/
2533/* about the following functions - I have used the same names for the */
2534/* functions as Markus Wild did in his Retina driver for NetBSD as */
2535/* they just made sense for this purpose. Apart from that, I wrote */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002536/* these functions myself. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002537/**********************************************************************/
2538
2539/*** WGen() - write into one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002540static void WGen(const struct cirrusfb_info *cinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002541 int regnum, unsigned char val)
2542{
2543 unsigned long regofs = 0;
2544
2545 if (cinfo->btype == BT_PICASSO) {
2546 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002547/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2548 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002549 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2550 regofs = 0xfff;
2551 }
2552
Krzysztof Helt8503df62007-10-16 01:29:08 -07002553 vga_w(cinfo->regbase, regofs + regnum, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002554}
2555
2556/*** RGen() - read out one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002557static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002558{
2559 unsigned long regofs = 0;
2560
2561 if (cinfo->btype == BT_PICASSO) {
2562 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002563/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2564 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002565 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2566 regofs = 0xfff;
2567 }
2568
Krzysztof Helt8503df62007-10-16 01:29:08 -07002569 return vga_r(cinfo->regbase, regofs + regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002570}
2571
2572/*** AttrOn() - turn on VideoEnable for Attribute controller ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002573static void AttrOn(const struct cirrusfb_info *cinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002574{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002575 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002576
Krzysztof Helt8503df62007-10-16 01:29:08 -07002577 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002578
Krzysztof Helt8503df62007-10-16 01:29:08 -07002579 if (vga_rcrt(cinfo->regbase, CL_CRT24) & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002580 /* if we're just in "write value" mode, write back the */
2581 /* same value as before to not modify anything */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002582 vga_w(cinfo->regbase, VGA_ATT_IW,
2583 vga_r(cinfo->regbase, VGA_ATT_R));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002584 }
2585 /* turn on video bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002586/* vga_w(cinfo->regbase, VGA_ATT_IW, 0x20); */
2587 vga_w(cinfo->regbase, VGA_ATT_IW, 0x33);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002588
2589 /* dummy write on Reg0 to be on "write index" mode next time */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002590 vga_w(cinfo->regbase, VGA_ATT_IW, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002591
Krzysztof Helt8503df62007-10-16 01:29:08 -07002592 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002593}
2594
2595/*** WHDR() - write into the Hidden DAC register ***/
2596/* as the HDR is the only extension register that requires special treatment
2597 * (the other extension registers are accessible just like the "ordinary"
2598 * registers of their functional group) here is a specialized routine for
2599 * accessing the HDR
2600 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002601static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002602{
2603 unsigned char dummy;
2604
2605 if (cinfo->btype == BT_PICASSO) {
2606 /* Klaus' hint for correct access to HDR on some boards */
2607 /* first write 0 to pixel mask (3c6) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002608 WGen(cinfo, VGA_PEL_MSK, 0x00);
2609 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002610 /* next read dummy from pixel address (3c8) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002611 dummy = RGen(cinfo, VGA_PEL_IW);
2612 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002613 }
2614 /* now do the usual stuff to access the HDR */
2615
Krzysztof Helt8503df62007-10-16 01:29:08 -07002616 dummy = RGen(cinfo, VGA_PEL_MSK);
2617 udelay(200);
2618 dummy = RGen(cinfo, VGA_PEL_MSK);
2619 udelay(200);
2620 dummy = RGen(cinfo, VGA_PEL_MSK);
2621 udelay(200);
2622 dummy = RGen(cinfo, VGA_PEL_MSK);
2623 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002624
Krzysztof Helt8503df62007-10-16 01:29:08 -07002625 WGen(cinfo, VGA_PEL_MSK, val);
2626 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002627
2628 if (cinfo->btype == BT_PICASSO) {
2629 /* now first reset HDR access counter */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002630 dummy = RGen(cinfo, VGA_PEL_IW);
2631 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002632
2633 /* and at the end, restore the mask value */
2634 /* ## is this mask always 0xff? */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002635 WGen(cinfo, VGA_PEL_MSK, 0xff);
2636 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002637 }
2638}
2639
Linus Torvalds1da177e2005-04-16 15:20:36 -07002640/*** WSFR() - write to the "special function register" (SFR) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002641static void WSFR(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002642{
2643#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07002644 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002645 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002646 z_writeb(val, cinfo->regbase + 0x8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002647#endif
2648}
2649
2650/* The Picasso has a second register for switching the monitor bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002651static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002652{
2653#ifdef CONFIG_ZORRO
2654 /* writing an arbitrary value to this one causes the monitor switcher */
2655 /* to flip to Amiga display */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002656 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002657 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002658 z_writeb(val, cinfo->regbase + 0x9000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002659#endif
2660}
2661
Linus Torvalds1da177e2005-04-16 15:20:36 -07002662/*** WClut - set CLUT entry (range: 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002663static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002664 unsigned char green, unsigned char blue)
2665{
2666 unsigned int data = VGA_PEL_D;
2667
2668 /* address write mode register is not translated.. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002669 vga_w(cinfo->regbase, VGA_PEL_IW, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002670
2671 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2672 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2673 /* but DAC data register IS, at least for Picasso II */
2674 if (cinfo->btype == BT_PICASSO)
2675 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002676 vga_w(cinfo->regbase, data, red);
2677 vga_w(cinfo->regbase, data, green);
2678 vga_w(cinfo->regbase, data, blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002679 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002680 vga_w(cinfo->regbase, data, blue);
2681 vga_w(cinfo->regbase, data, green);
2682 vga_w(cinfo->regbase, data, red);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002683 }
2684}
2685
Linus Torvalds1da177e2005-04-16 15:20:36 -07002686#if 0
2687/*** RClut - read CLUT entry (range 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002688static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char *red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002689 unsigned char *green, unsigned char *blue)
2690{
2691 unsigned int data = VGA_PEL_D;
2692
Krzysztof Helt8503df62007-10-16 01:29:08 -07002693 vga_w(cinfo->regbase, VGA_PEL_IR, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002694
2695 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2696 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2697 if (cinfo->btype == BT_PICASSO)
2698 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002699 *red = vga_r(cinfo->regbase, data);
2700 *green = vga_r(cinfo->regbase, data);
2701 *blue = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002702 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002703 *blue = vga_r(cinfo->regbase, data);
2704 *green = vga_r(cinfo->regbase, data);
2705 *red = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002706 }
2707}
2708#endif
2709
Linus Torvalds1da177e2005-04-16 15:20:36 -07002710/*******************************************************************
2711 cirrusfb_WaitBLT()
2712
2713 Wait for the BitBLT engine to complete a possible earlier job
2714*********************************************************************/
2715
2716/* FIXME: use interrupts instead */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002717static void cirrusfb_WaitBLT(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002718{
2719 /* now busy-wait until we're done */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002720 while (vga_rgfx(regbase, CL_GR31) & 0x08)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002721 /* do nothing */ ;
2722}
2723
2724/*******************************************************************
2725 cirrusfb_BitBLT()
2726
2727 perform accelerated "scrolling"
2728********************************************************************/
2729
Krzysztof Helt8503df62007-10-16 01:29:08 -07002730static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
2731 u_short curx, u_short cury,
2732 u_short destx, u_short desty,
2733 u_short width, u_short height,
2734 u_short line_length)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002735{
2736 u_short nwidth, nheight;
2737 u_long nsrc, ndest;
2738 u_char bltmode;
2739
Krzysztof Helt8503df62007-10-16 01:29:08 -07002740 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002741
2742 nwidth = width - 1;
2743 nheight = height - 1;
2744
2745 bltmode = 0x00;
2746 /* if source adr < dest addr, do the Blt backwards */
2747 if (cury <= desty) {
2748 if (cury == desty) {
2749 /* if src and dest are on the same line, check x */
2750 if (curx < destx)
2751 bltmode |= 0x01;
2752 } else
2753 bltmode |= 0x01;
2754 }
2755 if (!bltmode) {
2756 /* standard case: forward blitting */
2757 nsrc = (cury * line_length) + curx;
2758 ndest = (desty * line_length) + destx;
2759 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002760 /* this means start addresses are at the end,
2761 * counting backwards
2762 */
2763 nsrc = cury * line_length + curx +
2764 nheight * line_length + nwidth;
2765 ndest = desty * line_length + destx +
2766 nheight * line_length + nwidth;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002767 }
2768
2769 /*
2770 run-down of registers to be programmed:
2771 destination pitch
2772 source pitch
2773 BLT width/height
2774 source start
2775 destination start
2776 BLT mode
2777 BLT ROP
2778 VGA_GFX_SR_VALUE / VGA_GFX_SR_ENABLE: "fill color"
2779 start/stop
2780 */
2781
Krzysztof Helt8503df62007-10-16 01:29:08 -07002782 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002783
2784 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002785 /* dest pitch low */
2786 vga_wgfx(regbase, CL_GR24, line_length & 0xff);
2787 /* dest pitch hi */
2788 vga_wgfx(regbase, CL_GR25, line_length >> 8);
2789 /* source pitch low */
2790 vga_wgfx(regbase, CL_GR26, line_length & 0xff);
2791 /* source pitch hi */
2792 vga_wgfx(regbase, CL_GR27, line_length >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002793
2794 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002795 /* BLT width low */
2796 vga_wgfx(regbase, CL_GR20, nwidth & 0xff);
2797 /* BLT width hi */
2798 vga_wgfx(regbase, CL_GR21, nwidth >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002799
2800 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002801 /* BLT height low */
2802 vga_wgfx(regbase, CL_GR22, nheight & 0xff);
2803 /* BLT width hi */
2804 vga_wgfx(regbase, CL_GR23, nheight >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002805
2806 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002807 /* BLT dest low */
2808 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2809 /* BLT dest mid */
2810 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2811 /* BLT dest hi */
2812 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002813
2814 /* BLT source */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002815 /* BLT src low */
2816 vga_wgfx(regbase, CL_GR2C, (u_char) (nsrc & 0xff));
2817 /* BLT src mid */
2818 vga_wgfx(regbase, CL_GR2D, (u_char) (nsrc >> 8));
2819 /* BLT src hi */
2820 vga_wgfx(regbase, CL_GR2E, (u_char) (nsrc >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002821
2822 /* BLT mode */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002823 vga_wgfx(regbase, CL_GR30, bltmode); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002824
2825 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002826 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002827
2828 /* and finally: GO! */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002829 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002830
Krzysztof Helt8503df62007-10-16 01:29:08 -07002831 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002832}
2833
Linus Torvalds1da177e2005-04-16 15:20:36 -07002834/*******************************************************************
2835 cirrusfb_RectFill()
2836
2837 perform accelerated rectangle fill
2838********************************************************************/
2839
Krzysztof Helt8503df62007-10-16 01:29:08 -07002840static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002841 u_short x, u_short y, u_short width, u_short height,
2842 u_char color, u_short line_length)
2843{
2844 u_short nwidth, nheight;
2845 u_long ndest;
2846 u_char op;
2847
Krzysztof Helt8503df62007-10-16 01:29:08 -07002848 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002849
2850 nwidth = width - 1;
2851 nheight = height - 1;
2852
2853 ndest = (y * line_length) + x;
2854
Krzysztof Helt8503df62007-10-16 01:29:08 -07002855 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002856
2857 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002858 vga_wgfx(regbase, CL_GR24, line_length & 0xff); /* dest pitch low */
2859 vga_wgfx(regbase, CL_GR25, line_length >> 8); /* dest pitch hi */
2860 vga_wgfx(regbase, CL_GR26, line_length & 0xff); /* source pitch low */
2861 vga_wgfx(regbase, CL_GR27, line_length >> 8); /* source pitch hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002862
2863 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002864 vga_wgfx(regbase, CL_GR20, nwidth & 0xff); /* BLT width low */
2865 vga_wgfx(regbase, CL_GR21, nwidth >> 8); /* BLT width hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002866
2867 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002868 vga_wgfx(regbase, CL_GR22, nheight & 0xff); /* BLT height low */
2869 vga_wgfx(regbase, CL_GR23, nheight >> 8); /* BLT width hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002870
2871 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002872 /* BLT dest low */
2873 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2874 /* BLT dest mid */
2875 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2876 /* BLT dest hi */
2877 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002878
2879 /* BLT source: set to 0 (is a dummy here anyway) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002880 vga_wgfx(regbase, CL_GR2C, 0x00); /* BLT src low */
2881 vga_wgfx(regbase, CL_GR2D, 0x00); /* BLT src mid */
2882 vga_wgfx(regbase, CL_GR2E, 0x00); /* BLT src hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002883
2884 /* This is a ColorExpand Blt, using the */
2885 /* same color for foreground and background */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002886 vga_wgfx(regbase, VGA_GFX_SR_VALUE, color); /* foreground color */
2887 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, color); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002888
2889 op = 0xc0;
2890 if (bits_per_pixel == 16) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002891 vga_wgfx(regbase, CL_GR10, color); /* foreground color */
2892 vga_wgfx(regbase, CL_GR11, color); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002893 op = 0x50;
2894 op = 0xd0;
2895 } else if (bits_per_pixel == 32) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002896 vga_wgfx(regbase, CL_GR10, color); /* foreground color */
2897 vga_wgfx(regbase, CL_GR11, color); /* background color */
2898 vga_wgfx(regbase, CL_GR12, color); /* foreground color */
2899 vga_wgfx(regbase, CL_GR13, color); /* background color */
2900 vga_wgfx(regbase, CL_GR14, 0); /* foreground color */
2901 vga_wgfx(regbase, CL_GR15, 0); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002902 op = 0x50;
2903 op = 0xf0;
2904 }
2905 /* BLT mode: color expand, Enable 8x8 copy (faster?) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002906 vga_wgfx(regbase, CL_GR30, op); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002907
2908 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002909 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002910
2911 /* and finally: GO! */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002912 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002913
Krzysztof Helt8503df62007-10-16 01:29:08 -07002914 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002915}
2916
Linus Torvalds1da177e2005-04-16 15:20:36 -07002917/**************************************************************************
2918 * bestclock() - determine closest possible clock lower(?) than the
2919 * desired pixel clock
2920 **************************************************************************/
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002921static void bestclock(long freq, int *nom, int *den, int *div)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002922{
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002923 int n, d;
2924 long h, diff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002925
Krzysztof Helt8503df62007-10-16 01:29:08 -07002926 assert(nom != NULL);
2927 assert(den != NULL);
2928 assert(div != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002929
2930 *nom = 0;
2931 *den = 0;
2932 *div = 0;
2933
Krzysztof Helt8503df62007-10-16 01:29:08 -07002934 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002935
2936 if (freq < 8000)
2937 freq = 8000;
2938
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002939 diff = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002940
2941 for (n = 32; n < 128; n++) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002942 int s = 0;
2943
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002944 d = (14318 * n) / freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002945 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002946 int temp = d;
2947
2948 if (temp > 31) {
2949 s = 1;
2950 temp >>= 1;
2951 }
2952 h = ((14318 * n) / temp) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002953 h = h > freq ? h - freq : freq - h;
2954 if (h < diff) {
2955 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002956 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002957 *den = temp;
2958 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002959 }
2960 }
Krzysztof Helt7528f542008-10-15 22:03:36 -07002961 d++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002962 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002963 if (d > 31) {
2964 s = 1;
2965 d >>= 1;
2966 }
2967 h = ((14318 * n) / d) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002968 h = h > freq ? h - freq : freq - h;
2969 if (h < diff) {
2970 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002971 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002972 *den = d;
2973 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002974 }
2975 }
2976 }
2977
Krzysztof Helt8503df62007-10-16 01:29:08 -07002978 DPRINTK("Best possible values for given frequency:\n");
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002979 DPRINTK(" freq: %ld kHz nom: %d den: %d div: %d\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002980 freq, *nom, *den, *div);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002981
Krzysztof Helt8503df62007-10-16 01:29:08 -07002982 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002983}
2984
Linus Torvalds1da177e2005-04-16 15:20:36 -07002985/* -------------------------------------------------------------------------
2986 *
2987 * debugging functions
2988 *
2989 * -------------------------------------------------------------------------
2990 */
2991
2992#ifdef CIRRUSFB_DEBUG
2993
2994/**
2995 * cirrusfb_dbg_print_byte
2996 * @name: name associated with byte value to be displayed
2997 * @val: byte value to be displayed
2998 *
2999 * DESCRIPTION:
3000 * Display an indented string, along with a hexidecimal byte value, and
3001 * its decoded bits. Bits 7 through 0 are listed in left-to-right
3002 * order.
3003 */
3004
3005static
Krzysztof Helt8503df62007-10-16 01:29:08 -07003006void cirrusfb_dbg_print_byte(const char *name, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003007{
Krzysztof Helt8503df62007-10-16 01:29:08 -07003008 DPRINTK("%8s = 0x%02X (bits 7-0: %c%c%c%c%c%c%c%c)\n",
3009 name, val,
3010 val & 0x80 ? '1' : '0',
3011 val & 0x40 ? '1' : '0',
3012 val & 0x20 ? '1' : '0',
3013 val & 0x10 ? '1' : '0',
3014 val & 0x08 ? '1' : '0',
3015 val & 0x04 ? '1' : '0',
3016 val & 0x02 ? '1' : '0',
3017 val & 0x01 ? '1' : '0');
Linus Torvalds1da177e2005-04-16 15:20:36 -07003018}
3019
Linus Torvalds1da177e2005-04-16 15:20:36 -07003020/**
3021 * cirrusfb_dbg_print_regs
3022 * @base: If using newmmio, the newmmio base address, otherwise %NULL
3023 * @reg_class: type of registers to read: %CRT, or %SEQ
3024 *
3025 * DESCRIPTION:
3026 * Dumps the given list of VGA CRTC registers. If @base is %NULL,
3027 * old-style I/O ports are queried for information, otherwise MMIO is
3028 * used at the given @base address to query the information.
3029 */
3030
3031static
Krzysztof Helt8503df62007-10-16 01:29:08 -07003032void cirrusfb_dbg_print_regs(caddr_t regbase,
Krzysztof Helt7345de32007-10-16 01:29:11 -07003033 enum cirrusfb_dbg_reg_class reg_class, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003034{
3035 va_list list;
3036 unsigned char val = 0;
3037 unsigned reg;
3038 char *name;
3039
Krzysztof Helt8503df62007-10-16 01:29:08 -07003040 va_start(list, reg_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003041
Krzysztof Helt8503df62007-10-16 01:29:08 -07003042 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003043 while (name != NULL) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07003044 reg = va_arg(list, int);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003045
3046 switch (reg_class) {
3047 case CRT:
Krzysztof Helt8503df62007-10-16 01:29:08 -07003048 val = vga_rcrt(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003049 break;
3050 case SEQ:
Krzysztof Helt8503df62007-10-16 01:29:08 -07003051 val = vga_rseq(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003052 break;
3053 default:
3054 /* should never occur */
Richard Knutssonc930faa2007-05-08 00:38:29 -07003055 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003056 break;
3057 }
3058
Krzysztof Helt8503df62007-10-16 01:29:08 -07003059 cirrusfb_dbg_print_byte(name, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003060
Krzysztof Helt8503df62007-10-16 01:29:08 -07003061 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003062 }
3063
Krzysztof Helt8503df62007-10-16 01:29:08 -07003064 va_end(list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003065}
3066
Linus Torvalds1da177e2005-04-16 15:20:36 -07003067/**
3068 * cirrusfb_dump
3069 * @cirrusfbinfo:
3070 *
3071 * DESCRIPTION:
3072 */
3073
Krzysztof Helt8503df62007-10-16 01:29:08 -07003074static void cirrusfb_dump(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003075{
Krzysztof Helt8503df62007-10-16 01:29:08 -07003076 cirrusfb_dbg_reg_dump(NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003077}
3078
Linus Torvalds1da177e2005-04-16 15:20:36 -07003079/**
3080 * cirrusfb_dbg_reg_dump
3081 * @base: If using newmmio, the newmmio base address, otherwise %NULL
3082 *
3083 * DESCRIPTION:
3084 * Dumps a list of interesting VGA and CIRRUSFB registers. If @base is %NULL,
3085 * old-style I/O ports are queried for information, otherwise MMIO is
3086 * used at the given @base address to query the information.
3087 */
3088
3089static
Krzysztof Helt8503df62007-10-16 01:29:08 -07003090void cirrusfb_dbg_reg_dump(caddr_t regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003091{
Krzysztof Helt8503df62007-10-16 01:29:08 -07003092 DPRINTK("CIRRUSFB VGA CRTC register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003093
Krzysztof Helt8503df62007-10-16 01:29:08 -07003094 cirrusfb_dbg_print_regs(regbase, CRT,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003095 "CR00", 0x00,
3096 "CR01", 0x01,
3097 "CR02", 0x02,
3098 "CR03", 0x03,
3099 "CR04", 0x04,
3100 "CR05", 0x05,
3101 "CR06", 0x06,
3102 "CR07", 0x07,
3103 "CR08", 0x08,
3104 "CR09", 0x09,
3105 "CR0A", 0x0A,
3106 "CR0B", 0x0B,
3107 "CR0C", 0x0C,
3108 "CR0D", 0x0D,
3109 "CR0E", 0x0E,
3110 "CR0F", 0x0F,
3111 "CR10", 0x10,
3112 "CR11", 0x11,
3113 "CR12", 0x12,
3114 "CR13", 0x13,
3115 "CR14", 0x14,
3116 "CR15", 0x15,
3117 "CR16", 0x16,
3118 "CR17", 0x17,
3119 "CR18", 0x18,
3120 "CR22", 0x22,
3121 "CR24", 0x24,
3122 "CR26", 0x26,
3123 "CR2D", 0x2D,
3124 "CR2E", 0x2E,
3125 "CR2F", 0x2F,
3126 "CR30", 0x30,
3127 "CR31", 0x31,
3128 "CR32", 0x32,
3129 "CR33", 0x33,
3130 "CR34", 0x34,
3131 "CR35", 0x35,
3132 "CR36", 0x36,
3133 "CR37", 0x37,
3134 "CR38", 0x38,
3135 "CR39", 0x39,
3136 "CR3A", 0x3A,
3137 "CR3B", 0x3B,
3138 "CR3C", 0x3C,
3139 "CR3D", 0x3D,
3140 "CR3E", 0x3E,
3141 "CR3F", 0x3F,
3142 NULL);
3143
Krzysztof Helt8503df62007-10-16 01:29:08 -07003144 DPRINTK("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003145
Krzysztof Helt8503df62007-10-16 01:29:08 -07003146 DPRINTK("CIRRUSFB VGA SEQ register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003147
Krzysztof Helt8503df62007-10-16 01:29:08 -07003148 cirrusfb_dbg_print_regs(regbase, SEQ,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003149 "SR00", 0x00,
3150 "SR01", 0x01,
3151 "SR02", 0x02,
3152 "SR03", 0x03,
3153 "SR04", 0x04,
3154 "SR08", 0x08,
3155 "SR09", 0x09,
3156 "SR0A", 0x0A,
3157 "SR0B", 0x0B,
3158 "SR0D", 0x0D,
3159 "SR10", 0x10,
3160 "SR11", 0x11,
3161 "SR12", 0x12,
3162 "SR13", 0x13,
3163 "SR14", 0x14,
3164 "SR15", 0x15,
3165 "SR16", 0x16,
3166 "SR17", 0x17,
3167 "SR18", 0x18,
3168 "SR19", 0x19,
3169 "SR1A", 0x1A,
3170 "SR1B", 0x1B,
3171 "SR1C", 0x1C,
3172 "SR1D", 0x1D,
3173 "SR1E", 0x1E,
3174 "SR1F", 0x1F,
3175 NULL);
3176
Krzysztof Helt8503df62007-10-16 01:29:08 -07003177 DPRINTK("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003178}
3179
3180#endif /* CIRRUSFB_DEBUG */
3181