blob: 048b139f0e50ce67eaa85d8d228f74fd40a72038 [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 {
Krzysztof Helt486ff382008-10-15 22:03:42 -0700330 int multiplexing;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331};
332
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333#ifdef CIRRUSFB_DEBUG
Krzysztof Helt7345de32007-10-16 01:29:11 -0700334enum cirrusfb_dbg_reg_class {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700335 CRT,
336 SEQ
Krzysztof Helt7345de32007-10-16 01:29:11 -0700337};
Krzysztof Helt8503df62007-10-16 01:29:08 -0700338#endif /* CIRRUSFB_DEBUG */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339
340/* info about board */
341struct cirrusfb_info {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342 u8 __iomem *regbase;
Krzysztof Helt7345de32007-10-16 01:29:11 -0700343 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 unsigned char SFR; /* Shadow of special function register */
345
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346 struct cirrusfb_regs currentmode;
347 int blank_mode;
Krzysztof Helt64beab12008-10-15 22:03:38 -0700348 u32 pseudo_palette[16];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700350 void (*unmap)(struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351};
352
Krzysztof Helt55a0dd82008-10-15 22:03:41 -0700353static int noaccel __devinitdata;
Krzysztof Helta1d35a72008-10-15 22:03:38 -0700354static char *mode_option __devinitdata = "640x480@60";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355
356/****************************************************************************/
357/**** BEGIN PROTOTYPES ******************************************************/
358
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359/*--- Interface used by the world ------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700360static int cirrusfb_init(void);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361#ifndef MODULE
Krzysztof Helt8503df62007-10-16 01:29:08 -0700362static int cirrusfb_setup(char *options);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363#endif
364
Krzysztof Helt8503df62007-10-16 01:29:08 -0700365static int cirrusfb_open(struct fb_info *info, int user);
366static int cirrusfb_release(struct fb_info *info, int user);
367static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
368 unsigned blue, unsigned transp,
369 struct fb_info *info);
370static int cirrusfb_check_var(struct fb_var_screeninfo *var,
371 struct fb_info *info);
372static int cirrusfb_set_par(struct fb_info *info);
373static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
374 struct fb_info *info);
375static int cirrusfb_blank(int blank_mode, struct fb_info *info);
376static void cirrusfb_fillrect(struct fb_info *info,
377 const struct fb_fillrect *region);
378static void cirrusfb_copyarea(struct fb_info *info,
379 const struct fb_copyarea *area);
380static void cirrusfb_imageblit(struct fb_info *info,
381 const struct fb_image *image);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382
383/* function table of the above functions */
384static struct fb_ops cirrusfb_ops = {
385 .owner = THIS_MODULE,
386 .fb_open = cirrusfb_open,
387 .fb_release = cirrusfb_release,
388 .fb_setcolreg = cirrusfb_setcolreg,
389 .fb_check_var = cirrusfb_check_var,
390 .fb_set_par = cirrusfb_set_par,
391 .fb_pan_display = cirrusfb_pan_display,
392 .fb_blank = cirrusfb_blank,
393 .fb_fillrect = cirrusfb_fillrect,
394 .fb_copyarea = cirrusfb_copyarea,
395 .fb_imageblit = cirrusfb_imageblit,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396};
397
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398/*--- Internal routines ----------------------------------------------------*/
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700399static void init_vgachip(struct fb_info *info);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700400static void switch_monitor(struct cirrusfb_info *cinfo, int on);
401static void WGen(const struct cirrusfb_info *cinfo,
402 int regnum, unsigned char val);
403static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum);
404static void AttrOn(const struct cirrusfb_info *cinfo);
405static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val);
406static void WSFR(struct cirrusfb_info *cinfo, unsigned char val);
407static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val);
408static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum,
409 unsigned char red, unsigned char green, unsigned char blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410#if 0
Krzysztof Helt8503df62007-10-16 01:29:08 -0700411static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum,
412 unsigned char *red, unsigned char *green,
413 unsigned char *blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -0700415static void cirrusfb_WaitBLT(u8 __iomem *regbase);
416static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
417 u_short curx, u_short cury,
418 u_short destx, u_short desty,
419 u_short width, u_short height,
420 u_short line_length);
421static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
422 u_short x, u_short y,
423 u_short width, u_short height,
424 u_char color, u_short line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700426static void bestclock(long freq, int *nom, int *den, int *div);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427
428#ifdef CIRRUSFB_DEBUG
Krzysztof Helt8503df62007-10-16 01:29:08 -0700429static void cirrusfb_dump(void);
430static void cirrusfb_dbg_reg_dump(caddr_t regbase);
431static void cirrusfb_dbg_print_regs(caddr_t regbase,
Krzysztof Helt7345de32007-10-16 01:29:11 -0700432 enum cirrusfb_dbg_reg_class reg_class, ...);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700433static void cirrusfb_dbg_print_byte(const char *name, unsigned char val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434#endif /* CIRRUSFB_DEBUG */
435
436/*** END PROTOTYPES ********************************************************/
437/*****************************************************************************/
438/*** BEGIN Interface Used by the World ***************************************/
439
Krzysztof Helt8503df62007-10-16 01:29:08 -0700440static int opencount;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441
442/*--- Open /dev/fbx ---------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700443static int cirrusfb_open(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700444{
445 if (opencount++ == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700446 switch_monitor(info->par, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447 return 0;
448}
449
450/*--- Close /dev/fbx --------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700451static int cirrusfb_release(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452{
453 if (--opencount == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700454 switch_monitor(info->par, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 return 0;
456}
457
458/**** END Interface used by the World *************************************/
459/****************************************************************************/
460/**** BEGIN Hardware specific Routines **************************************/
461
Krzysztof Helt486ff382008-10-15 22:03:42 -0700462/* Check if the MCLK is not a better clock source */
463static int cirrusfb_check_mclk(struct cirrusfb_info *cinfo, long freq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464{
Krzysztof Helt486ff382008-10-15 22:03:42 -0700465 long mclk = vga_rseq(cinfo->regbase, CL_SEQR1F) & 0x3f;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466
Krzysztof Helt486ff382008-10-15 22:03:42 -0700467 /* Read MCLK value */
468 mclk = (14318 * mclk) >> 3;
469 DPRINTK("Read MCLK of %ld kHz\n", mclk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470
471 /* Determine if we should use MCLK instead of VCLK, and if so, what we
Krzysztof Helt486ff382008-10-15 22:03:42 -0700472 * should divide it by to get VCLK
473 */
474
475 if (abs(freq - mclk) < 250) {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700476 DPRINTK("Using VCLK = MCLK\n");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700477 return 1;
478 } else if (abs(freq - (mclk / 2)) < 250) {
479 DPRINTK("Using VCLK = MCLK/2\n");
480 return 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481 }
482
Krzysztof Helt486ff382008-10-15 22:03:42 -0700483 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484}
485
486static int cirrusfb_check_var(struct fb_var_screeninfo *var,
487 struct fb_info *info)
488{
Krzysztof Helt09a29102008-09-02 14:35:51 -0700489 int yres;
490 /* memory size in pixels */
491 unsigned pixels = info->screen_size * 8 / var->bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492
493 switch (var->bits_per_pixel) {
Krzysztof Helt060b6002007-10-16 01:29:13 -0700494 case 1:
Krzysztof Helt09a29102008-09-02 14:35:51 -0700495 pixels /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496 break; /* 8 pixel per byte, only 1/4th of mem usable */
Krzysztof Helt060b6002007-10-16 01:29:13 -0700497 case 8:
498 case 16:
Krzysztof Helt060b6002007-10-16 01:29:13 -0700499 case 32:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 break; /* 1 pixel == 1 byte */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700502 printk(KERN_ERR "cirrusfb: mode %dx%dx%d rejected..."
503 "color depth not supported.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700505 DPRINTK("EXIT - EINVAL error\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506 return -EINVAL;
507 }
508
Krzysztof Helt09a29102008-09-02 14:35:51 -0700509 if (var->xres_virtual < var->xres)
510 var->xres_virtual = var->xres;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 /* use highest possible virtual resolution */
Krzysztof Helt09a29102008-09-02 14:35:51 -0700512 if (var->yres_virtual == -1) {
513 var->yres_virtual = pixels / var->xres_virtual;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514
Krzysztof Helt8503df62007-10-16 01:29:08 -0700515 printk(KERN_INFO "cirrusfb: virtual resolution set to "
516 "maximum of %dx%d\n", var->xres_virtual,
517 var->yres_virtual);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519 if (var->yres_virtual < var->yres)
520 var->yres_virtual = var->yres;
521
Krzysztof Helt09a29102008-09-02 14:35:51 -0700522 if (var->xres_virtual * var->yres_virtual > pixels) {
523 printk(KERN_ERR "cirrusfb: mode %dx%dx%d rejected... "
524 "virtual resolution too high to fit into video memory!\n",
525 var->xres_virtual, var->yres_virtual,
526 var->bits_per_pixel);
527 DPRINTK("EXIT - EINVAL error\n");
528 return -EINVAL;
529 }
530
531
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532 if (var->xoffset < 0)
533 var->xoffset = 0;
534 if (var->yoffset < 0)
535 var->yoffset = 0;
536
537 /* truncate xoffset and yoffset to maximum if too high */
538 if (var->xoffset > var->xres_virtual - var->xres)
539 var->xoffset = var->xres_virtual - var->xres - 1;
540 if (var->yoffset > var->yres_virtual - var->yres)
541 var->yoffset = var->yres_virtual - var->yres - 1;
542
543 switch (var->bits_per_pixel) {
544 case 1:
545 var->red.offset = 0;
546 var->red.length = 1;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700547 var->green = var->red;
548 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 break;
550
551 case 8:
552 var->red.offset = 0;
553 var->red.length = 6;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700554 var->green = var->red;
555 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556 break;
557
558 case 16:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700559 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560 var->red.offset = 2;
561 var->green.offset = -3;
562 var->blue.offset = 8;
563 } else {
564 var->red.offset = 10;
565 var->green.offset = 5;
566 var->blue.offset = 0;
567 }
568 var->red.length = 5;
569 var->green.length = 5;
570 var->blue.length = 5;
571 break;
572
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573 case 32:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700574 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 var->red.offset = 8;
576 var->green.offset = 16;
577 var->blue.offset = 24;
578 } else {
579 var->red.offset = 16;
580 var->green.offset = 8;
581 var->blue.offset = 0;
582 }
583 var->red.length = 8;
584 var->green.length = 8;
585 var->blue.length = 8;
586 break;
587
588 default:
589 DPRINTK("Unsupported bpp size: %d\n", var->bits_per_pixel);
Richard Knutssonc930faa2007-05-08 00:38:29 -0700590 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591 /* should never occur */
592 break;
593 }
594
595 var->red.msb_right =
596 var->green.msb_right =
597 var->blue.msb_right =
598 var->transp.offset =
599 var->transp.length =
600 var->transp.msb_right = 0;
601
602 yres = var->yres;
603 if (var->vmode & FB_VMODE_DOUBLE)
604 yres *= 2;
605 else if (var->vmode & FB_VMODE_INTERLACED)
606 yres = (yres + 1) / 2;
607
608 if (yres >= 1280) {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700609 printk(KERN_ERR "cirrusfb: ERROR: VerticalTotal >= 1280; "
610 "special treatment required! (TODO)\n");
611 DPRINTK("EXIT - EINVAL error\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612 return -EINVAL;
613 }
614
615 return 0;
616}
617
Krzysztof Helt8503df62007-10-16 01:29:08 -0700618static int cirrusfb_decode_var(const struct fb_var_screeninfo *var,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619 struct cirrusfb_regs *regs,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700620 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621{
622 long freq;
623 long maxclock;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700624 int maxclockidx = var->bits_per_pixel >> 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626
Krzysztof Helt8503df62007-10-16 01:29:08 -0700627 switch (var->bits_per_pixel) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628 case 1:
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700629 info->fix.line_length = var->xres_virtual / 8;
630 info->fix.visual = FB_VISUAL_MONO10;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631 break;
632
633 case 8:
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700634 info->fix.line_length = var->xres_virtual;
635 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636 break;
637
638 case 16:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639 case 32:
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700640 info->fix.line_length = var->xres_virtual * maxclockidx;
Krzysztof Helt3b921832008-10-15 22:03:41 -0700641 info->fix.visual = FB_VISUAL_TRUECOLOR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642 break;
643
644 default:
645 DPRINTK("Unsupported bpp size: %d\n", var->bits_per_pixel);
Richard Knutssonc930faa2007-05-08 00:38:29 -0700646 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647 /* should never occur */
648 break;
649 }
650
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700651 info->fix.type = FB_TYPE_PACKED_PIXELS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652
653 /* convert from ps to kHz */
Krzysztof Helt060b6002007-10-16 01:29:13 -0700654 freq = PICOS2KHZ(var->pixclock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655
Krzysztof Helt8503df62007-10-16 01:29:08 -0700656 DPRINTK("desired pixclock: %ld kHz\n", freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657
658 maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx];
659 regs->multiplexing = 0;
660
661 /* If the frequency is greater than we can support, we might be able
662 * to use multiplexing for the video mode */
663 if (freq > maxclock) {
664 switch (cinfo->btype) {
665 case BT_ALPINE:
666 case BT_GD5480:
667 regs->multiplexing = 1;
668 break;
669
670 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700671 printk(KERN_ERR "cirrusfb: Frequency greater "
672 "than maxclock (%ld kHz)\n", maxclock);
673 DPRINTK("EXIT - return -EINVAL\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674 return -EINVAL;
675 }
676 }
677#if 0
678 /* TODO: If we have a 1MB 5434, we need to put ourselves in a mode where
679 * the VCLK is double the pixel clock. */
680 switch (var->bits_per_pixel) {
681 case 16:
682 case 32:
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700683 if (var->xres <= 800)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700684 /* Xbh has this type of clock for 32-bit */
685 freq /= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686 break;
687 }
688#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689 return 0;
690}
691
Krzysztof Helt486ff382008-10-15 22:03:42 -0700692static void cirrusfb_set_mclk_as_source(const struct cirrusfb_info *cinfo,
693 int div)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694{
Krzysztof Helt486ff382008-10-15 22:03:42 -0700695 unsigned char old1f, old1e;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700696 assert(cinfo != NULL);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700697 old1f = vga_rseq(cinfo->regbase, CL_SEQR1F) & ~0x40;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698
Krzysztof Helt486ff382008-10-15 22:03:42 -0700699 if (div) {
700 DPRINTK("Set %s as pixclock source.\n",
701 (div == 2) ? "MCLK/2" : "MCLK");
702 old1f |= 0x40;
703 old1e = vga_rseq(cinfo->regbase, CL_SEQR1E) & ~0x1;
704 if (div == 2)
705 old1e |= 1;
706
707 vga_wseq(cinfo->regbase, CL_SEQR1E, old1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700709 vga_wseq(cinfo->regbase, CL_SEQR1F, old1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710}
711
712/*************************************************************************
713 cirrusfb_set_par_foo()
714
715 actually writes the values for a new video mode into the hardware,
716**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700717static int cirrusfb_set_par_foo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718{
719 struct cirrusfb_info *cinfo = info->par;
720 struct fb_var_screeninfo *var = &info->var;
721 struct cirrusfb_regs regs;
722 u8 __iomem *regbase = cinfo->regbase;
723 unsigned char tmp;
724 int offset = 0, err;
725 const struct cirrusfb_board_info_rec *bi;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700726 int hdispend, hsyncstart, hsyncend, htotal;
727 int yres, vdispend, vsyncstart, vsyncend, vtotal;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700728 long freq;
729 int nom, den, div;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730
Krzysztof Helt8503df62007-10-16 01:29:08 -0700731 DPRINTK("ENTER\n");
732 DPRINTK("Requested mode: %dx%dx%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700734 DPRINTK("pixclock: %d\n", var->pixclock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700736 init_vgachip(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737
738 err = cirrusfb_decode_var(var, &regs, info);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700739 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740 /* should never happen */
741 DPRINTK("mode change aborted. invalid var.\n");
742 return -EINVAL;
743 }
744
745 bi = &cirrusfb_board_info[cinfo->btype];
746
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700747 hsyncstart = var->xres + var->right_margin;
748 hsyncend = hsyncstart + var->hsync_len;
749 htotal = (hsyncend + var->left_margin) / 8 - 5;
750 hdispend = var->xres / 8 - 1;
751 hsyncstart = hsyncstart / 8 + 1;
752 hsyncend = hsyncend / 8 + 1;
753
754 yres = var->yres;
755 vsyncstart = yres + var->lower_margin;
756 vsyncend = vsyncstart + var->vsync_len;
757 vtotal = vsyncend + var->upper_margin;
758 vdispend = yres - 1;
759
760 if (var->vmode & FB_VMODE_DOUBLE) {
761 yres *= 2;
762 vsyncstart *= 2;
763 vsyncend *= 2;
764 vtotal *= 2;
765 } else if (var->vmode & FB_VMODE_INTERLACED) {
766 yres = (yres + 1) / 2;
767 vsyncstart = (vsyncstart + 1) / 2;
768 vsyncend = (vsyncend + 1) / 2;
769 vtotal = (vtotal + 1) / 2;
770 }
771
772 vtotal -= 2;
773 vsyncstart -= 1;
774 vsyncend -= 1;
775
776 if (yres >= 1024) {
777 vtotal /= 2;
778 vsyncstart /= 2;
779 vsyncend /= 2;
780 vdispend /= 2;
781 }
782 if (regs.multiplexing) {
783 htotal /= 2;
784 hsyncstart /= 2;
785 hsyncend /= 2;
786 hdispend /= 2;
787 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788 /* unlock register VGA_CRTC_H_TOTAL..CRT7 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700789 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20); /* previously: 0x00) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790
791 /* if debugging is enabled, all parameters get output before writing */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700792 DPRINTK("CRT0: %d\n", htotal);
793 vga_wcrt(regbase, VGA_CRTC_H_TOTAL, htotal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700795 DPRINTK("CRT1: %d\n", hdispend);
796 vga_wcrt(regbase, VGA_CRTC_H_DISP, hdispend);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700798 DPRINTK("CRT2: %d\n", var->xres / 8);
799 vga_wcrt(regbase, VGA_CRTC_H_BLANK_START, var->xres / 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800
Krzysztof Helt8503df62007-10-16 01:29:08 -0700801 /* + 128: Compatible read */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700802 DPRINTK("CRT3: 128+%d\n", (htotal + 5) % 32);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700803 vga_wcrt(regbase, VGA_CRTC_H_BLANK_END,
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700804 128 + ((htotal + 5) % 32));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700806 DPRINTK("CRT4: %d\n", hsyncstart);
807 vga_wcrt(regbase, VGA_CRTC_H_SYNC_START, hsyncstart);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700809 tmp = hsyncend % 32;
810 if ((htotal + 5) & 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811 tmp += 128;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700812 DPRINTK("CRT5: %d\n", tmp);
813 vga_wcrt(regbase, VGA_CRTC_H_SYNC_END, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700815 DPRINTK("CRT6: %d\n", vtotal & 0xff);
816 vga_wcrt(regbase, VGA_CRTC_V_TOTAL, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817
818 tmp = 16; /* LineCompare bit #9 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700819 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700821 if (vdispend & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822 tmp |= 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700823 if (vsyncstart & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824 tmp |= 4;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700825 if ((vdispend + 1) & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826 tmp |= 8;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700827 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700829 if (vdispend & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700831 if (vsyncstart & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832 tmp |= 128;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700833 DPRINTK("CRT7: %d\n", tmp);
834 vga_wcrt(regbase, VGA_CRTC_OVERFLOW, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835
836 tmp = 0x40; /* LineCompare bit #8 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700837 if ((vdispend + 1) & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838 tmp |= 0x20;
839 if (var->vmode & FB_VMODE_DOUBLE)
840 tmp |= 0x80;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700841 DPRINTK("CRT9: %d\n", tmp);
842 vga_wcrt(regbase, VGA_CRTC_MAX_SCAN, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700844 DPRINTK("CRT10: %d\n", vsyncstart & 0xff);
845 vga_wcrt(regbase, VGA_CRTC_V_SYNC_START, vsyncstart & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700847 DPRINTK("CRT11: 64+32+%d\n", vsyncend % 16);
848 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, vsyncend % 16 + 64 + 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700850 DPRINTK("CRT12: %d\n", vdispend & 0xff);
851 vga_wcrt(regbase, VGA_CRTC_V_DISP_END, vdispend & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700853 DPRINTK("CRT15: %d\n", (vdispend + 1) & 0xff);
854 vga_wcrt(regbase, VGA_CRTC_V_BLANK_START, (vdispend + 1) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700855
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700856 DPRINTK("CRT16: %d\n", vtotal & 0xff);
857 vga_wcrt(regbase, VGA_CRTC_V_BLANK_END, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858
Krzysztof Helt8503df62007-10-16 01:29:08 -0700859 DPRINTK("CRT18: 0xff\n");
860 vga_wcrt(regbase, VGA_CRTC_LINE_COMPARE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861
862 tmp = 0;
863 if (var->vmode & FB_VMODE_INTERLACED)
864 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700865 if ((htotal + 5) & 64)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866 tmp |= 16;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700867 if ((htotal + 5) & 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700869 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700870 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700871 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872 tmp |= 128;
873
Krzysztof Helt8503df62007-10-16 01:29:08 -0700874 DPRINTK("CRT1a: %d\n", tmp);
875 vga_wcrt(regbase, CL_CRT1A, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700877 freq = PICOS2KHZ(var->pixclock);
878 bestclock(freq, &nom, &den, &div);
879
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880 /* set VCLK0 */
881 /* hardware RefClock: 14.31818 MHz */
882 /* formula: VClk = (OSC * N) / (D * (1+P)) */
883 /* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */
884
Krzysztof Helt486ff382008-10-15 22:03:42 -0700885 if (cinfo->btype == BT_ALPINE) {
886 /* if freq is close to mclk or mclk/2 select mclk
887 * as clock source
888 */
889 int divMCLK = cirrusfb_check_mclk(cinfo, freq);
890 if (divMCLK) {
891 nom = 0;
892 cirrusfb_set_mclk_as_source(cinfo, divMCLK);
893 }
894 }
895 if (nom) {
896 vga_wseq(regbase, CL_SEQRB, nom);
897 tmp = den << 1;
898 if (div != 0)
899 tmp |= 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900
Krzysztof Helt486ff382008-10-15 22:03:42 -0700901 /* 6 bit denom; ONLY 5434!!! (bugged me 10 days) */
902 if ((cinfo->btype == BT_SD64) ||
903 (cinfo->btype == BT_ALPINE) ||
904 (cinfo->btype == BT_GD5480))
905 tmp |= 0x80;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906
Krzysztof Helt486ff382008-10-15 22:03:42 -0700907 DPRINTK("CL_SEQR1B: %ld\n", (long) tmp);
908 vga_wseq(regbase, CL_SEQR1B, tmp);
909 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700911 if (yres >= 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700912 /* 1280x1024 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700913 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914 else
915 /* mode control: VGA_CRTC_START_HI enable, ROTATE(?), 16bit
916 * address wrap, no compat. */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700917 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700918
Krzysztof Helt8503df62007-10-16 01:29:08 -0700919/* HAEH? vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20);
920 * previously: 0x00 unlock VGA_CRTC_H_TOTAL..CRT7 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921
922 /* don't know if it would hurt to also program this if no interlaced */
923 /* mode is used, but I feel better this way.. :-) */
924 if (var->vmode & FB_VMODE_INTERLACED)
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700925 vga_wcrt(regbase, VGA_CRTC_REGS, htotal / 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926 else
Krzysztof Helt8503df62007-10-16 01:29:08 -0700927 vga_wcrt(regbase, VGA_CRTC_REGS, 0x00); /* interlace control */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700928
Krzysztof Helt8503df62007-10-16 01:29:08 -0700929 vga_wseq(regbase, VGA_SEQ_CHARACTER_MAP, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930
931 /* adjust horizontal/vertical sync type (low/high) */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700932 /* enable display memory & CRTC I/O address for color mode */
933 tmp = 0x03;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
935 tmp |= 0x40;
936 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
937 tmp |= 0x80;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700938 WGen(cinfo, VGA_MIS_W, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700939
Krzysztof Helt8503df62007-10-16 01:29:08 -0700940 /* Screen A Preset Row-Scan register */
941 vga_wcrt(regbase, VGA_CRTC_PRESET_ROW, 0);
942 /* text cursor on and start line */
943 vga_wcrt(regbase, VGA_CRTC_CURSOR_START, 0);
944 /* text cursor end line */
945 vga_wcrt(regbase, VGA_CRTC_CURSOR_END, 31);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946
947 /******************************************************
948 *
949 * 1 bpp
950 *
951 */
952
953 /* programming for different color depths */
954 if (var->bits_per_pixel == 1) {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700955 DPRINTK("cirrusfb: preparing for 1 bit deep display\n");
956 vga_wgfx(regbase, VGA_GFX_MODE, 0); /* mode register */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957
958 /* SR07 */
959 switch (cinfo->btype) {
960 case BT_SD64:
961 case BT_PICCOLO:
962 case BT_PICASSO:
963 case BT_SPECTRUM:
964 case BT_PICASSO4:
965 case BT_ALPINE:
966 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700967 DPRINTK(" (for GD54xx)\n");
968 vga_wseq(regbase, CL_SEQR7,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700969 regs.multiplexing ?
970 bi->sr07_1bpp_mux : bi->sr07_1bpp);
971 break;
972
973 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700974 DPRINTK(" (for GD546x)\n");
975 vga_wseq(regbase, CL_SEQR7,
976 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977 break;
978
979 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700980 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981 break;
982 }
983
984 /* Extended Sequencer Mode */
985 switch (cinfo->btype) {
986 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700987 /* setting the SEQRF on SD64 is not necessary
988 * (only during init)
989 */
990 DPRINTK("(for SD64)\n");
991 /* MCLK select */
992 vga_wseq(regbase, CL_SEQR1F, 0x1a);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993 break;
994
995 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -0700996 case BT_SPECTRUM:
997 DPRINTK("(for Piccolo/Spectrum)\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700998 /* ### ueberall 0x22? */
999 /* ##vorher 1c MCLK select */
1000 vga_wseq(regbase, CL_SEQR1F, 0x22);
1001 /* evtl d0 bei 1 bit? avoid FIFO underruns..? */
1002 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001003 break;
1004
1005 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001006 DPRINTK("(for Picasso)\n");
1007 /* ##vorher 22 MCLK select */
1008 vga_wseq(regbase, CL_SEQR1F, 0x22);
1009 /* ## vorher d0 avoid FIFO underruns..? */
1010 vga_wseq(regbase, CL_SEQRF, 0xd0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011 break;
1012
Linus Torvalds1da177e2005-04-16 15:20:36 -07001013 case BT_PICASSO4:
1014 case BT_ALPINE:
1015 case BT_GD5480:
1016 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001017 DPRINTK(" (for GD54xx)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001018 /* do nothing */
1019 break;
1020
1021 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001022 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001023 break;
1024 }
1025
Krzysztof Helt8503df62007-10-16 01:29:08 -07001026 /* pixel mask: pass-through for first plane */
1027 WGen(cinfo, VGA_PEL_MSK, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028 if (regs.multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001029 /* hidden dac reg: 1280x1024 */
1030 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001032 /* hidden dac: nothing */
1033 WHDR(cinfo, 0);
1034 /* memory mode: odd/even, ext. memory */
1035 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x06);
1036 /* plane mask: only write to first plane */
1037 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038 offset = var->xres_virtual / 16;
1039 }
1040
1041 /******************************************************
1042 *
1043 * 8 bpp
1044 *
1045 */
1046
1047 else if (var->bits_per_pixel == 8) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001048 DPRINTK("cirrusfb: preparing for 8 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049 switch (cinfo->btype) {
1050 case BT_SD64:
1051 case BT_PICCOLO:
1052 case BT_PICASSO:
1053 case BT_SPECTRUM:
1054 case BT_PICASSO4:
1055 case BT_ALPINE:
1056 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001057 DPRINTK(" (for GD54xx)\n");
1058 vga_wseq(regbase, CL_SEQR7,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059 regs.multiplexing ?
1060 bi->sr07_8bpp_mux : bi->sr07_8bpp);
1061 break;
1062
1063 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001064 DPRINTK(" (for GD546x)\n");
1065 vga_wseq(regbase, CL_SEQR7,
1066 vga_rseq(regbase, CL_SEQR7) | 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001067 break;
1068
1069 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001070 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071 break;
1072 }
1073
1074 switch (cinfo->btype) {
1075 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001076 /* MCLK select */
1077 vga_wseq(regbase, CL_SEQR1F, 0x1d);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078 break;
1079
1080 case BT_PICCOLO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081 case BT_PICASSO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001083 /* ### vorher 1c MCLK select */
1084 vga_wseq(regbase, CL_SEQR1F, 0x22);
1085 /* Fast Page-Mode writes */
1086 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001087 break;
1088
1089 case BT_PICASSO4:
1090#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001091 /* ### INCOMPLETE!! */
1092 vga_wseq(regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001093#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07001094/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001095 break;
1096
1097 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001098 DPRINTK(" (for GD543x)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001099 /* We already set SRF and SR1F */
1100 break;
1101
1102 case BT_GD5480:
1103 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001104 DPRINTK(" (for GD54xx)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105 /* do nothing */
1106 break;
1107
1108 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001109 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001110 break;
1111 }
1112
Krzysztof Helt8503df62007-10-16 01:29:08 -07001113 /* mode register: 256 color mode */
1114 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1115 /* pixel mask: pass-through all planes */
1116 WGen(cinfo, VGA_PEL_MSK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117 if (regs.multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001118 /* hidden dac reg: 1280x1024 */
1119 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001120 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001121 /* hidden dac: nothing */
1122 WHDR(cinfo, 0);
1123 /* memory mode: chain4, ext. memory */
1124 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1125 /* plane mask: enable writing to all 4 planes */
1126 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001127 offset = var->xres_virtual / 8;
1128 }
1129
1130 /******************************************************
1131 *
1132 * 16 bpp
1133 *
1134 */
1135
1136 else if (var->bits_per_pixel == 16) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001137 DPRINTK("cirrusfb: preparing for 16 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138 switch (cinfo->btype) {
1139 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001140 /* Extended Sequencer Mode: 256c col. mode */
1141 vga_wseq(regbase, CL_SEQR7, 0xf7);
1142 /* MCLK select */
1143 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144 break;
1145
1146 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001147 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001148 vga_wseq(regbase, CL_SEQR7, 0x87);
1149 /* Fast Page-Mode writes */
1150 vga_wseq(regbase, CL_SEQRF, 0xb0);
1151 /* MCLK select */
1152 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001153 break;
1154
1155 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001156 vga_wseq(regbase, CL_SEQR7, 0x27);
1157 /* Fast Page-Mode writes */
1158 vga_wseq(regbase, CL_SEQRF, 0xb0);
1159 /* MCLK select */
1160 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001161 break;
1162
Linus Torvalds1da177e2005-04-16 15:20:36 -07001163 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001164 vga_wseq(regbase, CL_SEQR7, 0x27);
1165/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166 break;
1167
1168 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001169 DPRINTK(" (for GD543x)\n");
Krzysztof Helt3b921832008-10-15 22:03:41 -07001170 vga_wseq(regbase, CL_SEQR7, 0xa7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001171 break;
1172
1173 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001174 DPRINTK(" (for GD5480)\n");
1175 vga_wseq(regbase, CL_SEQR7, 0x17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001176 /* We already set SRF and SR1F */
1177 break;
1178
1179 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001180 DPRINTK(" (for GD546x)\n");
1181 vga_wseq(regbase, CL_SEQR7,
1182 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001183 break;
1184
1185 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001186 printk(KERN_WARNING "CIRRUSFB: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001187 break;
1188 }
1189
Krzysztof Helt8503df62007-10-16 01:29:08 -07001190 /* mode register: 256 color mode */
1191 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1192 /* pixel mask: pass-through all planes */
1193 WGen(cinfo, VGA_PEL_MSK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001194#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001195 WHDR(cinfo, 0xc0); /* Copy Xbh */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001196#elif defined(CONFIG_ZORRO)
1197 /* FIXME: CONFIG_PCI and CONFIG_ZORRO may be defined both */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001198 WHDR(cinfo, 0xa0); /* hidden dac reg: nothing special */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07001200 /* memory mode: chain4, ext. memory */
1201 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1202 /* plane mask: enable writing to all 4 planes */
1203 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204 offset = var->xres_virtual / 4;
1205 }
1206
1207 /******************************************************
1208 *
1209 * 32 bpp
1210 *
1211 */
1212
1213 else if (var->bits_per_pixel == 32) {
Krzysztof Helt1cea9a92008-10-15 22:03:37 -07001214 DPRINTK("cirrusfb: preparing for 32 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001215 switch (cinfo->btype) {
1216 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001217 /* Extended Sequencer Mode: 256c col. mode */
1218 vga_wseq(regbase, CL_SEQR7, 0xf9);
1219 /* MCLK select */
1220 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001221 break;
1222
1223 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001224 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001225 vga_wseq(regbase, CL_SEQR7, 0x85);
1226 /* Fast Page-Mode writes */
1227 vga_wseq(regbase, CL_SEQRF, 0xb0);
1228 /* MCLK select */
1229 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001230 break;
1231
1232 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001233 vga_wseq(regbase, CL_SEQR7, 0x25);
1234 /* Fast Page-Mode writes */
1235 vga_wseq(regbase, CL_SEQRF, 0xb0);
1236 /* MCLK select */
1237 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001238 break;
1239
Linus Torvalds1da177e2005-04-16 15:20:36 -07001240 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001241 vga_wseq(regbase, CL_SEQR7, 0x25);
1242/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001243 break;
1244
1245 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001246 DPRINTK(" (for GD543x)\n");
1247 vga_wseq(regbase, CL_SEQR7, 0xa9);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001248 break;
1249
1250 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001251 DPRINTK(" (for GD5480)\n");
1252 vga_wseq(regbase, CL_SEQR7, 0x19);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001253 /* We already set SRF and SR1F */
1254 break;
1255
1256 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001257 DPRINTK(" (for GD546x)\n");
1258 vga_wseq(regbase, CL_SEQR7,
1259 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001260 break;
1261
1262 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001263 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001264 break;
1265 }
1266
Krzysztof Helt8503df62007-10-16 01:29:08 -07001267 /* mode register: 256 color mode */
1268 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1269 /* pixel mask: pass-through all planes */
1270 WGen(cinfo, VGA_PEL_MSK, 0xff);
1271 /* hidden dac reg: 8-8-8 mode (24 or 32) */
1272 WHDR(cinfo, 0xc5);
1273 /* memory mode: chain4, ext. memory */
1274 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1275 /* plane mask: enable writing to all 4 planes */
1276 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001277 offset = var->xres_virtual / 4;
1278 }
1279
1280 /******************************************************
1281 *
1282 * unknown/unsupported bpp
1283 *
1284 */
1285
Krzysztof Helt8503df62007-10-16 01:29:08 -07001286 else
1287 printk(KERN_ERR "cirrusfb: What's this?? "
1288 " requested color depth == %d.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001289 var->bits_per_pixel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290
Krzysztof Helt8503df62007-10-16 01:29:08 -07001291 vga_wcrt(regbase, VGA_CRTC_OFFSET, offset & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001292 tmp = 0x22;
1293 if (offset & 0x100)
1294 tmp |= 0x10; /* offset overflow bit */
1295
Krzysztof Helt8503df62007-10-16 01:29:08 -07001296 /* screen start addr #16-18, fastpagemode cycles */
1297 vga_wcrt(regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001298
1299 if (cinfo->btype == BT_SD64 ||
1300 cinfo->btype == BT_PICASSO4 ||
1301 cinfo->btype == BT_ALPINE ||
1302 cinfo->btype == BT_GD5480)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001303 /* screen start address bit 19 */
1304 vga_wcrt(regbase, CL_CRT1D, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001305
Krzysztof Helt8503df62007-10-16 01:29:08 -07001306 /* text cursor location high */
1307 vga_wcrt(regbase, VGA_CRTC_CURSOR_HI, 0);
1308 /* text cursor location low */
1309 vga_wcrt(regbase, VGA_CRTC_CURSOR_LO, 0);
1310 /* underline row scanline = at very bottom */
1311 vga_wcrt(regbase, VGA_CRTC_UNDERLINE, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001312
Krzysztof Helt8503df62007-10-16 01:29:08 -07001313 /* controller mode */
1314 vga_wattr(regbase, VGA_ATC_MODE, 1);
1315 /* overscan (border) color */
1316 vga_wattr(regbase, VGA_ATC_OVERSCAN, 0);
1317 /* color plane enable */
1318 vga_wattr(regbase, VGA_ATC_PLANE_ENABLE, 15);
1319 /* pixel panning */
1320 vga_wattr(regbase, CL_AR33, 0);
1321 /* color select */
1322 vga_wattr(regbase, VGA_ATC_COLOR_PAGE, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001323
1324 /* [ EGS: SetOffset(); ] */
1325 /* From SetOffset(): Turn on VideoEnable bit in Attribute controller */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001326 AttrOn(cinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001327
Krzysztof Helt8503df62007-10-16 01:29:08 -07001328 /* set/reset register */
1329 vga_wgfx(regbase, VGA_GFX_SR_VALUE, 0);
1330 /* set/reset enable */
1331 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, 0);
1332 /* color compare */
1333 vga_wgfx(regbase, VGA_GFX_COMPARE_VALUE, 0);
1334 /* data rotate */
1335 vga_wgfx(regbase, VGA_GFX_DATA_ROTATE, 0);
1336 /* read map select */
1337 vga_wgfx(regbase, VGA_GFX_PLANE_READ, 0);
1338 /* miscellaneous register */
1339 vga_wgfx(regbase, VGA_GFX_MISC, 1);
1340 /* color don't care */
1341 vga_wgfx(regbase, VGA_GFX_COMPARE_MASK, 15);
1342 /* bit mask */
1343 vga_wgfx(regbase, VGA_GFX_BIT_MASK, 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344
Krzysztof Helt8503df62007-10-16 01:29:08 -07001345 /* graphics cursor attributes: nothing special */
1346 vga_wseq(regbase, CL_SEQR12, 0x0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001347
1348 /* finally, turn on everything - turn off "FullBandwidth" bit */
1349 /* also, set "DotClock%2" bit where requested */
1350 tmp = 0x01;
1351
1352/*** FB_VMODE_CLOCK_HALVE in linux/fb.h not defined anymore ?
1353 if (var->vmode & FB_VMODE_CLOCK_HALVE)
1354 tmp |= 0x08;
1355*/
1356
Krzysztof Helt8503df62007-10-16 01:29:08 -07001357 vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, tmp);
1358 DPRINTK("CL_SEQR1: %d\n", tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359
1360 cinfo->currentmode = regs;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361
1362 /* pan to requested offset */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001363 cirrusfb_pan_display(var, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001364
1365#ifdef CIRRUSFB_DEBUG
Krzysztof Helt8503df62007-10-16 01:29:08 -07001366 cirrusfb_dump();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367#endif
1368
Krzysztof Helt8503df62007-10-16 01:29:08 -07001369 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370 return 0;
1371}
1372
1373/* for some reason incomprehensible to me, cirrusfb requires that you write
1374 * the registers twice for the settings to take..grr. -dte */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001375static int cirrusfb_set_par(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001376{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001377 cirrusfb_set_par_foo(info);
1378 return cirrusfb_set_par_foo(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379}
1380
Krzysztof Helt8503df62007-10-16 01:29:08 -07001381static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
1382 unsigned blue, unsigned transp,
1383 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001384{
1385 struct cirrusfb_info *cinfo = info->par;
1386
1387 if (regno > 255)
1388 return -EINVAL;
1389
1390 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
1391 u32 v;
1392 red >>= (16 - info->var.red.length);
1393 green >>= (16 - info->var.green.length);
1394 blue >>= (16 - info->var.blue.length);
1395
Krzysztof Helt8503df62007-10-16 01:29:08 -07001396 if (regno >= 16)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001397 return 1;
1398 v = (red << info->var.red.offset) |
1399 (green << info->var.green.offset) |
1400 (blue << info->var.blue.offset);
1401
Krzysztof Helt060b6002007-10-16 01:29:13 -07001402 cinfo->pseudo_palette[regno] = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001403 return 0;
1404 }
1405
Krzysztof Helt8503df62007-10-16 01:29:08 -07001406 if (info->var.bits_per_pixel == 8)
1407 WClut(cinfo, regno, red >> 10, green >> 10, blue >> 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001408
1409 return 0;
1410
1411}
1412
1413/*************************************************************************
1414 cirrusfb_pan_display()
1415
1416 performs display panning - provided hardware permits this
1417**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07001418static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
1419 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001420{
1421 int xoffset = 0;
1422 int yoffset = 0;
1423 unsigned long base;
1424 unsigned char tmp = 0, tmp2 = 0, xpix;
1425 struct cirrusfb_info *cinfo = info->par;
1426
Krzysztof Helt8503df62007-10-16 01:29:08 -07001427 DPRINTK("ENTER\n");
1428 DPRINTK("virtual offset: (%d,%d)\n", var->xoffset, var->yoffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001429
1430 /* no range checks for xoffset and yoffset, */
1431 /* as fb_pan_display has already done this */
1432 if (var->vmode & FB_VMODE_YWRAP)
1433 return -EINVAL;
1434
1435 info->var.xoffset = var->xoffset;
1436 info->var.yoffset = var->yoffset;
1437
1438 xoffset = var->xoffset * info->var.bits_per_pixel / 8;
1439 yoffset = var->yoffset;
1440
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001441 base = yoffset * info->fix.line_length + xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001442
1443 if (info->var.bits_per_pixel == 1) {
1444 /* base is already correct */
1445 xpix = (unsigned char) (var->xoffset % 8);
1446 } else {
1447 base /= 4;
1448 xpix = (unsigned char) ((xoffset % 4) * 2);
1449 }
1450
Krzysztof Helt8503df62007-10-16 01:29:08 -07001451 cirrusfb_WaitBLT(cinfo->regbase); /* make sure all the BLT's are done */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001452
1453 /* lower 8 + 8 bits of screen start address */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001454 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO,
1455 (unsigned char) (base & 0xff));
1456 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI,
1457 (unsigned char) (base >> 8));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001458
1459 /* construct bits 16, 17 and 18 of screen start address */
1460 if (base & 0x10000)
1461 tmp |= 0x01;
1462 if (base & 0x20000)
1463 tmp |= 0x04;
1464 if (base & 0x40000)
1465 tmp |= 0x08;
1466
Krzysztof Helt8503df62007-10-16 01:29:08 -07001467 /* 0xf2 is %11110010, exclude tmp bits */
1468 tmp2 = (vga_rcrt(cinfo->regbase, CL_CRT1B) & 0xf2) | tmp;
1469 vga_wcrt(cinfo->regbase, CL_CRT1B, tmp2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001470
1471 /* construct bit 19 of screen start address */
Krzysztof Helt060b6002007-10-16 01:29:13 -07001472 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19)
1473 vga_wcrt(cinfo->regbase, CL_CRT1D, (base >> 12) & 0x80);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001474
Krzysztof Helt8503df62007-10-16 01:29:08 -07001475 /* write pixel panning value to AR33; this does not quite work in 8bpp
1476 *
1477 * ### Piccolo..? Will this work?
1478 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001479 if (info->var.bits_per_pixel == 1)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001480 vga_wattr(cinfo->regbase, CL_AR33, xpix);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001481
Krzysztof Helt8503df62007-10-16 01:29:08 -07001482 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001483
Krzysztof Helt8503df62007-10-16 01:29:08 -07001484 DPRINTK("EXIT\n");
1485 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001486}
1487
Krzysztof Helt8503df62007-10-16 01:29:08 -07001488static int cirrusfb_blank(int blank_mode, struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001489{
1490 /*
Krzysztof Helt8503df62007-10-16 01:29:08 -07001491 * Blank the screen if blank_mode != 0, else unblank. If blank == NULL
1492 * then the caller blanks by setting the CLUT (Color Look Up Table)
1493 * to all black. Return 0 if blanking succeeded, != 0 if un-/blanking
1494 * failed due to e.g. a video mode which doesn't support it.
1495 * Implements VESA suspend and powerdown modes on hardware that
1496 * supports disabling hsync/vsync:
1497 * blank_mode == 2: suspend vsync
1498 * blank_mode == 3: suspend hsync
1499 * blank_mode == 4: powerdown
Linus Torvalds1da177e2005-04-16 15:20:36 -07001500 */
1501 unsigned char val;
1502 struct cirrusfb_info *cinfo = info->par;
1503 int current_mode = cinfo->blank_mode;
1504
Krzysztof Helt8503df62007-10-16 01:29:08 -07001505 DPRINTK("ENTER, blank mode = %d\n", blank_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001506
1507 if (info->state != FBINFO_STATE_RUNNING ||
1508 current_mode == blank_mode) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001509 DPRINTK("EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001510 return 0;
1511 }
1512
1513 /* Undo current */
1514 if (current_mode == FB_BLANK_NORMAL ||
1515 current_mode == FB_BLANK_UNBLANK) {
1516 /* unblank the screen */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001517 val = vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE);
1518 /* clear "FullBandwidth" bit */
1519 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val & 0xdf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001520 /* and undo VESA suspend trickery */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001521 vga_wgfx(cinfo->regbase, CL_GRE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522 }
1523
1524 /* set new */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001525 if (blank_mode > FB_BLANK_NORMAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001526 /* blank the screen */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001527 val = vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE);
1528 /* set "FullBandwidth" bit */
1529 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val | 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001530 }
1531
1532 switch (blank_mode) {
1533 case FB_BLANK_UNBLANK:
1534 case FB_BLANK_NORMAL:
1535 break;
1536 case FB_BLANK_VSYNC_SUSPEND:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001537 vga_wgfx(cinfo->regbase, CL_GRE, 0x04);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001538 break;
1539 case FB_BLANK_HSYNC_SUSPEND:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001540 vga_wgfx(cinfo->regbase, CL_GRE, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001541 break;
1542 case FB_BLANK_POWERDOWN:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001543 vga_wgfx(cinfo->regbase, CL_GRE, 0x06);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544 break;
1545 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001546 DPRINTK("EXIT, returning 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001547 return 1;
1548 }
1549
1550 cinfo->blank_mode = blank_mode;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001551 DPRINTK("EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001552
1553 /* Let fbcon do a soft blank for us */
1554 return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
1555}
1556/**** END Hardware specific Routines **************************************/
1557/****************************************************************************/
1558/**** BEGIN Internal Routines ***********************************************/
1559
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001560static void init_vgachip(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001561{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001562 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001563 const struct cirrusfb_board_info_rec *bi;
1564
Krzysztof Helt8503df62007-10-16 01:29:08 -07001565 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001566
Krzysztof Helt8503df62007-10-16 01:29:08 -07001567 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001568
1569 bi = &cirrusfb_board_info[cinfo->btype];
1570
1571 /* reset board globally */
1572 switch (cinfo->btype) {
1573 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001574 WSFR(cinfo, 0x01);
1575 udelay(500);
1576 WSFR(cinfo, 0x51);
1577 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001578 break;
1579 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001580 WSFR2(cinfo, 0xff);
1581 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001582 break;
1583 case BT_SD64:
1584 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001585 WSFR(cinfo, 0x1f);
1586 udelay(500);
1587 WSFR(cinfo, 0x4f);
1588 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001589 break;
1590 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001591 /* disable flickerfixer */
1592 vga_wcrt(cinfo->regbase, CL_CRT51, 0x00);
1593 mdelay(100);
1594 /* from Klaus' NetBSD driver: */
1595 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
1596 /* put blitter into 542x compat */
1597 vga_wgfx(cinfo->regbase, CL_GR33, 0x00);
1598 /* mode */
1599 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001600 break;
1601
1602 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001603 /* from Klaus' NetBSD driver: */
1604 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001605 break;
1606
1607 case BT_ALPINE:
1608 /* Nothing to do to reset the board. */
1609 break;
1610
1611 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001612 printk(KERN_ERR "cirrusfb: Warning: Unknown board type\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001613 break;
1614 }
1615
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001616 /* make sure RAM size set by this point */
1617 assert(info->screen_size > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001618
1619 /* the P4 is not fully initialized here; I rely on it having been */
1620 /* inited under AmigaOS already, which seems to work just fine */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001621 /* (Klaus advised to do it this way) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001622
1623 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001624 WGen(cinfo, CL_VSSM, 0x10); /* EGS: 0x16 */
1625 WGen(cinfo, CL_POS102, 0x01);
1626 WGen(cinfo, CL_VSSM, 0x08); /* EGS: 0x0e */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001627
1628 if (cinfo->btype != BT_SD64)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001629 WGen(cinfo, CL_VSSM2, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001630
Krzysztof Helt8503df62007-10-16 01:29:08 -07001631 /* reset sequencer logic */
1632 vga_wseq(cinfo->regbase, CL_SEQR0, 0x03);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001633
Krzysztof Helt8503df62007-10-16 01:29:08 -07001634 /* FullBandwidth (video off) and 8/9 dot clock */
1635 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, 0x21);
1636 /* polarity (-/-), disable access to display memory,
1637 * VGA_CRTC_START_HI base address: color
1638 */
1639 WGen(cinfo, VGA_MIS_W, 0xc1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001640
Krzysztof Helt8503df62007-10-16 01:29:08 -07001641 /* "magic cookie" - doesn't make any sense to me.. */
1642/* vga_wgfx(cinfo->regbase, CL_GRA, 0xce); */
1643 /* unlock all extension registers */
1644 vga_wseq(cinfo->regbase, CL_SEQR6, 0x12);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001645
Krzysztof Helt8503df62007-10-16 01:29:08 -07001646 /* reset blitter */
1647 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001648
1649 switch (cinfo->btype) {
1650 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001651 vga_wseq(cinfo->regbase, CL_SEQRF, 0x98);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001652 break;
1653 case BT_ALPINE:
1654 break;
1655 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001656 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001657 break;
1658 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001659 vga_wseq(cinfo->regbase, CL_SEQR16, 0x0f);
1660 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001661 break;
1662 }
1663 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001664 /* plane mask: nothing */
1665 vga_wseq(cinfo->regbase, VGA_SEQ_PLANE_WRITE, 0xff);
1666 /* character map select: doesn't even matter in gx mode */
1667 vga_wseq(cinfo->regbase, VGA_SEQ_CHARACTER_MAP, 0x00);
1668 /* memory mode: chain-4, no odd/even, ext. memory */
1669 vga_wseq(cinfo->regbase, VGA_SEQ_MEMORY_MODE, 0x0e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001670
1671 /* controller-internal base address of video memory */
1672 if (bi->init_sr07)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001673 vga_wseq(cinfo->regbase, CL_SEQR7, bi->sr07);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001674
Krzysztof Helt8503df62007-10-16 01:29:08 -07001675 /* vga_wseq(cinfo->regbase, CL_SEQR8, 0x00); */
1676 /* EEPROM control: shouldn't be necessary to write to this at all.. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001677
Krzysztof Helt8503df62007-10-16 01:29:08 -07001678 /* graphics cursor X position (incomplete; position gives rem. 3 bits */
1679 vga_wseq(cinfo->regbase, CL_SEQR10, 0x00);
1680 /* graphics cursor Y position (..."... ) */
1681 vga_wseq(cinfo->regbase, CL_SEQR11, 0x00);
1682 /* graphics cursor attributes */
1683 vga_wseq(cinfo->regbase, CL_SEQR12, 0x00);
1684 /* graphics cursor pattern address */
1685 vga_wseq(cinfo->regbase, CL_SEQR13, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001686
1687 /* writing these on a P4 might give problems.. */
1688 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001689 /* configuration readback and ext. color */
1690 vga_wseq(cinfo->regbase, CL_SEQR17, 0x00);
1691 /* signature generator */
1692 vga_wseq(cinfo->regbase, CL_SEQR18, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001693 }
1694
1695 /* MCLK select etc. */
1696 if (bi->init_sr1f)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001697 vga_wseq(cinfo->regbase, CL_SEQR1F, bi->sr1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001698
Krzysztof Helt8503df62007-10-16 01:29:08 -07001699 /* Screen A preset row scan: none */
1700 vga_wcrt(cinfo->regbase, VGA_CRTC_PRESET_ROW, 0x00);
1701 /* Text cursor start: disable text cursor */
1702 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_START, 0x20);
1703 /* Text cursor end: - */
1704 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_END, 0x00);
1705 /* Screen start address high: 0 */
1706 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, 0x00);
1707 /* Screen start address low: 0 */
1708 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, 0x00);
1709 /* text cursor location high: 0 */
1710 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_HI, 0x00);
1711 /* text cursor location low: 0 */
1712 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_LO, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001713
Krzysztof Helt8503df62007-10-16 01:29:08 -07001714 /* Underline Row scanline: - */
1715 vga_wcrt(cinfo->regbase, VGA_CRTC_UNDERLINE, 0x00);
1716 /* mode control: timing enable, byte mode, no compat modes */
1717 vga_wcrt(cinfo->regbase, VGA_CRTC_MODE, 0xc3);
1718 /* Line Compare: not needed */
1719 vga_wcrt(cinfo->regbase, VGA_CRTC_LINE_COMPARE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001720 /* ### add 0x40 for text modes with > 30 MHz pixclock */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001721 /* ext. display controls: ext.adr. wrap */
1722 vga_wcrt(cinfo->regbase, CL_CRT1B, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001723
Krzysztof Helt8503df62007-10-16 01:29:08 -07001724 /* Set/Reset registes: - */
1725 vga_wgfx(cinfo->regbase, VGA_GFX_SR_VALUE, 0x00);
1726 /* Set/Reset enable: - */
1727 vga_wgfx(cinfo->regbase, VGA_GFX_SR_ENABLE, 0x00);
1728 /* Color Compare: - */
1729 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_VALUE, 0x00);
1730 /* Data Rotate: - */
1731 vga_wgfx(cinfo->regbase, VGA_GFX_DATA_ROTATE, 0x00);
1732 /* Read Map Select: - */
1733 vga_wgfx(cinfo->regbase, VGA_GFX_PLANE_READ, 0x00);
1734 /* Mode: conf. for 16/4/2 color mode, no odd/even, read/write mode 0 */
1735 vga_wgfx(cinfo->regbase, VGA_GFX_MODE, 0x00);
1736 /* Miscellaneous: memory map base address, graphics mode */
1737 vga_wgfx(cinfo->regbase, VGA_GFX_MISC, 0x01);
1738 /* Color Don't care: involve all planes */
1739 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_MASK, 0x0f);
1740 /* Bit Mask: no mask at all */
1741 vga_wgfx(cinfo->regbase, VGA_GFX_BIT_MASK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001742 if (cinfo->btype == BT_ALPINE)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001743 /* (5434 can't have bit 3 set for bitblt) */
1744 vga_wgfx(cinfo->regbase, CL_GRB, 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001745 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001746 /* Graphics controller mode extensions: finer granularity,
1747 * 8byte data latches
1748 */
1749 vga_wgfx(cinfo->regbase, CL_GRB, 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001750
Krzysztof Helt8503df62007-10-16 01:29:08 -07001751 vga_wgfx(cinfo->regbase, CL_GRC, 0xff); /* Color Key compare: - */
1752 vga_wgfx(cinfo->regbase, CL_GRD, 0x00); /* Color Key compare mask: - */
1753 vga_wgfx(cinfo->regbase, CL_GRE, 0x00); /* Miscellaneous control: - */
1754 /* Background color byte 1: - */
1755 /* vga_wgfx (cinfo->regbase, CL_GR10, 0x00); */
1756 /* vga_wgfx (cinfo->regbase, CL_GR11, 0x00); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001757
Krzysztof Helt8503df62007-10-16 01:29:08 -07001758 /* Attribute Controller palette registers: "identity mapping" */
1759 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE0, 0x00);
1760 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE1, 0x01);
1761 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE2, 0x02);
1762 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE3, 0x03);
1763 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE4, 0x04);
1764 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE5, 0x05);
1765 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE6, 0x06);
1766 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE7, 0x07);
1767 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE8, 0x08);
1768 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE9, 0x09);
1769 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEA, 0x0a);
1770 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEB, 0x0b);
1771 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEC, 0x0c);
1772 vga_wattr(cinfo->regbase, VGA_ATC_PALETTED, 0x0d);
1773 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEE, 0x0e);
1774 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEF, 0x0f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001775
Krzysztof Helt8503df62007-10-16 01:29:08 -07001776 /* Attribute Controller mode: graphics mode */
1777 vga_wattr(cinfo->regbase, VGA_ATC_MODE, 0x01);
1778 /* Overscan color reg.: reg. 0 */
1779 vga_wattr(cinfo->regbase, VGA_ATC_OVERSCAN, 0x00);
1780 /* Color Plane enable: Enable all 4 planes */
1781 vga_wattr(cinfo->regbase, VGA_ATC_PLANE_ENABLE, 0x0f);
1782/* ### vga_wattr(cinfo->regbase, CL_AR33, 0x00); * Pixel Panning: - */
1783 /* Color Select: - */
1784 vga_wattr(cinfo->regbase, VGA_ATC_COLOR_PAGE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001785
Krzysztof Helt8503df62007-10-16 01:29:08 -07001786 WGen(cinfo, VGA_PEL_MSK, 0xff); /* Pixel mask: no mask */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001787
1788 if (cinfo->btype != BT_ALPINE && cinfo->btype != BT_GD5480)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001789 /* polarity (-/-), enable display mem,
1790 * VGA_CRTC_START_HI i/o base = color
1791 */
1792 WGen(cinfo, VGA_MIS_W, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001793
Krzysztof Helt8503df62007-10-16 01:29:08 -07001794 /* BLT Start/status: Blitter reset */
1795 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
1796 /* - " - : "end-of-reset" */
1797 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001798
1799 /* misc... */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001800 WHDR(cinfo, 0); /* Hidden DAC register: - */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001801
Krzysztof Helt8503df62007-10-16 01:29:08 -07001802 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001803 return;
1804}
1805
Krzysztof Helt8503df62007-10-16 01:29:08 -07001806static void switch_monitor(struct cirrusfb_info *cinfo, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001807{
1808#ifdef CONFIG_ZORRO /* only works on Zorro boards */
1809 static int IsOn = 0; /* XXX not ok for multiple boards */
1810
Krzysztof Helt8503df62007-10-16 01:29:08 -07001811 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001812
1813 if (cinfo->btype == BT_PICASSO4)
1814 return; /* nothing to switch */
1815 if (cinfo->btype == BT_ALPINE)
1816 return; /* nothing to switch */
1817 if (cinfo->btype == BT_GD5480)
1818 return; /* nothing to switch */
1819 if (cinfo->btype == BT_PICASSO) {
1820 if ((on && !IsOn) || (!on && IsOn))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001821 WSFR(cinfo, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001822
Krzysztof Helt8503df62007-10-16 01:29:08 -07001823 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001824 return;
1825 }
1826 if (on) {
1827 switch (cinfo->btype) {
1828 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001829 WSFR(cinfo, cinfo->SFR | 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001830 break;
1831 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001832 WSFR(cinfo, cinfo->SFR | 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001833 break;
1834 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001835 WSFR(cinfo, 0x6f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001836 break;
1837 default: /* do nothing */ break;
1838 }
1839 } else {
1840 switch (cinfo->btype) {
1841 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001842 WSFR(cinfo, cinfo->SFR & 0xde);
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 & 0xd7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001846 break;
1847 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001848 WSFR(cinfo, 0x4f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001849 break;
1850 default: /* do nothing */ break;
1851 }
1852 }
1853
Krzysztof Helt8503df62007-10-16 01:29:08 -07001854 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001855#endif /* CONFIG_ZORRO */
1856}
1857
Linus Torvalds1da177e2005-04-16 15:20:36 -07001858/******************************************/
1859/* Linux 2.6-style accelerated functions */
1860/******************************************/
1861
Krzysztof Helt8503df62007-10-16 01:29:08 -07001862static void cirrusfb_fillrect(struct fb_info *info,
1863 const struct fb_fillrect *region)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001864{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001865 struct fb_fillrect modded;
1866 int vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001867 struct cirrusfb_info *cinfo = info->par;
1868 int m = info->var.bits_per_pixel;
1869 u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ?
1870 cinfo->pseudo_palette[region->color] : region->color;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001871
1872 if (info->state != FBINFO_STATE_RUNNING)
1873 return;
1874 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1875 cfb_fillrect(info, region);
1876 return;
1877 }
1878
1879 vxres = info->var.xres_virtual;
1880 vyres = info->var.yres_virtual;
1881
1882 memcpy(&modded, region, sizeof(struct fb_fillrect));
1883
Krzysztof Helt8503df62007-10-16 01:29:08 -07001884 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001885 modded.dx >= vxres || modded.dy >= vyres)
1886 return;
1887
Krzysztof Helt8503df62007-10-16 01:29:08 -07001888 if (modded.dx + modded.width > vxres)
1889 modded.width = vxres - modded.dx;
1890 if (modded.dy + modded.height > vyres)
1891 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001892
Krzysztof Helt060b6002007-10-16 01:29:13 -07001893 cirrusfb_RectFill(cinfo->regbase,
1894 info->var.bits_per_pixel,
1895 (region->dx * m) / 8, region->dy,
1896 (region->width * m) / 8, region->height,
1897 color,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001898 info->fix.line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001899}
1900
Krzysztof Helt8503df62007-10-16 01:29:08 -07001901static void cirrusfb_copyarea(struct fb_info *info,
1902 const struct fb_copyarea *area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001903{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001904 struct fb_copyarea modded;
1905 u32 vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001906 struct cirrusfb_info *cinfo = info->par;
1907 int m = info->var.bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001908
1909 if (info->state != FBINFO_STATE_RUNNING)
1910 return;
1911 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1912 cfb_copyarea(info, area);
1913 return;
1914 }
1915
1916 vxres = info->var.xres_virtual;
1917 vyres = info->var.yres_virtual;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001918 memcpy(&modded, area, sizeof(struct fb_copyarea));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001919
Krzysztof Helt8503df62007-10-16 01:29:08 -07001920 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001921 modded.sx >= vxres || modded.sy >= vyres ||
1922 modded.dx >= vxres || modded.dy >= vyres)
1923 return;
1924
Krzysztof Helt8503df62007-10-16 01:29:08 -07001925 if (modded.sx + modded.width > vxres)
1926 modded.width = vxres - modded.sx;
1927 if (modded.dx + modded.width > vxres)
1928 modded.width = vxres - modded.dx;
1929 if (modded.sy + modded.height > vyres)
1930 modded.height = vyres - modded.sy;
1931 if (modded.dy + modded.height > vyres)
1932 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001933
Krzysztof Helt060b6002007-10-16 01:29:13 -07001934 cirrusfb_BitBLT(cinfo->regbase, info->var.bits_per_pixel,
1935 (area->sx * m) / 8, area->sy,
1936 (area->dx * m) / 8, area->dy,
1937 (area->width * m) / 8, area->height,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001938 info->fix.line_length);
Krzysztof Helt060b6002007-10-16 01:29:13 -07001939
Linus Torvalds1da177e2005-04-16 15:20:36 -07001940}
1941
Krzysztof Helt8503df62007-10-16 01:29:08 -07001942static void cirrusfb_imageblit(struct fb_info *info,
1943 const struct fb_image *image)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001944{
1945 struct cirrusfb_info *cinfo = info->par;
1946
Krzysztof Helt8503df62007-10-16 01:29:08 -07001947 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001948 cfb_imageblit(info, image);
1949}
1950
Linus Torvalds1da177e2005-04-16 15:20:36 -07001951#ifdef CONFIG_PPC_PREP
1952#define PREP_VIDEO_BASE ((volatile unsigned long) 0xC0000000)
1953#define PREP_IO_BASE ((volatile unsigned char *) 0x80000000)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001954static void get_prep_addrs(unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001955{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001956 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001957
1958 *display = PREP_VIDEO_BASE;
1959 *registers = (unsigned long) PREP_IO_BASE;
1960
Krzysztof Helt8503df62007-10-16 01:29:08 -07001961 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001962}
1963
1964#endif /* CONFIG_PPC_PREP */
1965
Linus Torvalds1da177e2005-04-16 15:20:36 -07001966#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001967static int release_io_ports;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001968
1969/* Pulled the logic from XFree86 Cirrus driver to get the memory size,
1970 * based on the DRAM bandwidth bit and DRAM bank switching bit. This
1971 * works with 1MB, 2MB and 4MB configurations (which the Motorola boards
1972 * seem to have. */
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07001973static unsigned int __devinit cirrusfb_get_memsize(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001974{
1975 unsigned long mem;
1976 unsigned char SRF;
1977
Krzysztof Helt8503df62007-10-16 01:29:08 -07001978 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001979
Krzysztof Helt8503df62007-10-16 01:29:08 -07001980 SRF = vga_rseq(regbase, CL_SEQRF);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001981 switch ((SRF & 0x18)) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001982 case 0x08:
1983 mem = 512 * 1024;
1984 break;
1985 case 0x10:
1986 mem = 1024 * 1024;
1987 break;
1988 /* 64-bit DRAM data bus width; assume 2MB. Also indicates 2MB memory
1989 * on the 5430.
1990 */
1991 case 0x18:
1992 mem = 2048 * 1024;
1993 break;
1994 default:
1995 printk(KERN_WARNING "CLgenfb: Unknown memory size!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001996 mem = 1024 * 1024;
1997 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001998 if (SRF & 0x80)
1999 /* If DRAM bank switching is enabled, there must be twice as much
2000 * memory installed. (4MB on the 5434)
2001 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002002 mem *= 2;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002003
Linus Torvalds1da177e2005-04-16 15:20:36 -07002004 /* TODO: Handling of GD5446/5480 (see XF86 sources ...) */
2005
Krzysztof Helt8503df62007-10-16 01:29:08 -07002006 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002007 return mem;
2008}
2009
Krzysztof Helt8503df62007-10-16 01:29:08 -07002010static void get_pci_addrs(const struct pci_dev *pdev,
2011 unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002012{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002013 assert(pdev != NULL);
2014 assert(display != NULL);
2015 assert(registers != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002016
Krzysztof Helt8503df62007-10-16 01:29:08 -07002017 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002018
2019 *display = 0;
2020 *registers = 0;
2021
2022 /* This is a best-guess for now */
2023
2024 if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
2025 *display = pci_resource_start(pdev, 1);
2026 *registers = pci_resource_start(pdev, 0);
2027 } else {
2028 *display = pci_resource_start(pdev, 0);
2029 *registers = pci_resource_start(pdev, 1);
2030 }
2031
Krzysztof Helt8503df62007-10-16 01:29:08 -07002032 assert(*display != 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002033
Krzysztof Helt8503df62007-10-16 01:29:08 -07002034 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002035}
2036
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002037static void cirrusfb_pci_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002038{
Krzysztof Helt64beab12008-10-15 22:03:38 -07002039 struct pci_dev *pdev = to_pci_dev(info->device);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002040
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002041 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002042#if 0 /* if system didn't claim this region, we would... */
2043 release_mem_region(0xA0000, 65535);
2044#endif
2045 if (release_io_ports)
2046 release_region(0x3C0, 32);
2047 pci_release_regions(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002048}
2049#endif /* CONFIG_PCI */
2050
Linus Torvalds1da177e2005-04-16 15:20:36 -07002051#ifdef CONFIG_ZORRO
Al Virod91f5bb2007-10-17 00:27:18 +01002052static void __devexit cirrusfb_zorro_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002053{
Al Virod91f5bb2007-10-17 00:27:18 +01002054 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt64beab12008-10-15 22:03:38 -07002055 struct zorro_dev *zdev = to_zorro_dev(info->device);
2056
2057 zorro_release_device(zdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002058
2059 if (cinfo->btype == BT_PICASSO4) {
2060 cinfo->regbase -= 0x600000;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002061 iounmap((void *)cinfo->regbase);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002062 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002063 } else {
Krzysztof Helt64beab12008-10-15 22:03:38 -07002064 if (zorro_resource_start(zdev) > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002065 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002066 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002067}
2068#endif /* CONFIG_ZORRO */
2069
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002070static int __devinit cirrusfb_set_fbinfo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002071{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002072 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002073 struct fb_var_screeninfo *var = &info->var;
2074
Linus Torvalds1da177e2005-04-16 15:20:36 -07002075 info->pseudo_palette = cinfo->pseudo_palette;
2076 info->flags = FBINFO_DEFAULT
2077 | FBINFO_HWACCEL_XPAN
2078 | FBINFO_HWACCEL_YPAN
2079 | FBINFO_HWACCEL_FILLRECT
2080 | FBINFO_HWACCEL_COPYAREA;
2081 if (noaccel)
2082 info->flags |= FBINFO_HWACCEL_DISABLED;
2083 info->fbops = &cirrusfb_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002084 if (cinfo->btype == BT_GD5480) {
2085 if (var->bits_per_pixel == 16)
2086 info->screen_base += 1 * MB_;
Krzysztof Helt1cea9a92008-10-15 22:03:37 -07002087 if (var->bits_per_pixel == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002088 info->screen_base += 2 * MB_;
2089 }
2090
2091 /* Fill fix common fields */
2092 strlcpy(info->fix.id, cirrusfb_board_info[cinfo->btype].name,
2093 sizeof(info->fix.id));
2094
2095 /* monochrome: only 1 memory plane */
2096 /* 8 bit and above: Use whole memory area */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002097 info->fix.smem_len = info->screen_size;
2098 if (var->bits_per_pixel == 1)
2099 info->fix.smem_len /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002100 info->fix.type_aux = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002101 info->fix.xpanstep = 1;
2102 info->fix.ypanstep = 1;
2103 info->fix.ywrapstep = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002104
2105 /* FIXME: map region at 0xB8000 if available, fill in here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002106 info->fix.mmio_len = 0;
2107 info->fix.accel = FB_ACCEL_NONE;
2108
2109 fb_alloc_cmap(&info->cmap, 256, 0);
2110
2111 return 0;
2112}
2113
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002114static int __devinit cirrusfb_register(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002115{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002116 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002117 int err;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002118 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002119
Krzysztof Helt8503df62007-10-16 01:29:08 -07002120 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002121
Krzysztof Helt8503df62007-10-16 01:29:08 -07002122 printk(KERN_INFO "cirrusfb: Driver for Cirrus Logic based "
2123 "graphic boards, v" CIRRUSFB_VERSION "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002124
Linus Torvalds1da177e2005-04-16 15:20:36 -07002125 btype = cinfo->btype;
2126
2127 /* sanity checks */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002128 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002129
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002130 /* set all the vital stuff */
2131 cirrusfb_set_fbinfo(info);
2132
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002133 DPRINTK("cirrusfb: (RAM start set to: 0x%p)\n", info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002134
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002135 err = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8);
2136 if (!err) {
2137 DPRINTK("wrong initial video mode\n");
2138 err = -EINVAL;
2139 goto err_dealloc_cmap;
2140 }
2141
Linus Torvalds1da177e2005-04-16 15:20:36 -07002142 info->var.activate = FB_ACTIVATE_NOW;
2143
2144 err = cirrusfb_decode_var(&info->var, &cinfo->currentmode, info);
2145 if (err < 0) {
2146 /* should never happen */
2147 DPRINTK("choking on default var... umm, no good.\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002148 goto err_dealloc_cmap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002149 }
2150
Linus Torvalds1da177e2005-04-16 15:20:36 -07002151 err = register_framebuffer(info);
2152 if (err < 0) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002153 printk(KERN_ERR "cirrusfb: could not register "
2154 "fb device; err = %d!\n", err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002155 goto err_dealloc_cmap;
2156 }
2157
Krzysztof Helt8503df62007-10-16 01:29:08 -07002158 DPRINTK("EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002159 return 0;
2160
2161err_dealloc_cmap:
2162 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002163 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002164 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002165 return err;
2166}
2167
Krzysztof Helt8503df62007-10-16 01:29:08 -07002168static void __devexit cirrusfb_cleanup(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002169{
2170 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002171 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002172
Krzysztof Helt8503df62007-10-16 01:29:08 -07002173 switch_monitor(cinfo, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002174
Krzysztof Helt8503df62007-10-16 01:29:08 -07002175 unregister_framebuffer(info);
2176 fb_dealloc_cmap(&info->cmap);
2177 printk("Framebuffer unregistered\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002178 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002179 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002180
Krzysztof Helt8503df62007-10-16 01:29:08 -07002181 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002182}
2183
Linus Torvalds1da177e2005-04-16 15:20:36 -07002184#ifdef CONFIG_PCI
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002185static int __devinit cirrusfb_pci_register(struct pci_dev *pdev,
2186 const struct pci_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002187{
2188 struct cirrusfb_info *cinfo;
2189 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002190 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002191 unsigned long board_addr, board_size;
2192 int ret;
2193
2194 ret = pci_enable_device(pdev);
2195 if (ret < 0) {
2196 printk(KERN_ERR "cirrusfb: Cannot enable PCI device\n");
2197 goto err_out;
2198 }
2199
2200 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &pdev->dev);
2201 if (!info) {
2202 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
2203 ret = -ENOMEM;
2204 goto err_disable;
2205 }
2206
2207 cinfo = info->par;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002208 cinfo->btype = btype = (enum cirrus_board) ent->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002209
Krzysztof Helt7345de32007-10-16 01:29:11 -07002210 DPRINTK(" Found PCI device, base address 0 is 0x%x, btype set to %d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002211 pdev->resource[0].start, btype);
Krzysztof Helt7345de32007-10-16 01:29:11 -07002212 DPRINTK(" base address 1 is 0x%x\n", pdev->resource[1].start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002213
Krzysztof Helt8503df62007-10-16 01:29:08 -07002214 if (isPReP) {
2215 pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, 0x00000000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002216#ifdef CONFIG_PPC_PREP
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002217 get_prep_addrs(&board_addr, &info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002218#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07002219 /* PReP dies if we ioremap the IO registers, but it works w/out... */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002220 cinfo->regbase = (char __iomem *) info->fix.mmio_start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002221 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002222 DPRINTK("Attempt to get PCI info for Cirrus Graphics Card\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002223 get_pci_addrs(pdev, &board_addr, &info->fix.mmio_start);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002224 /* FIXME: this forces VGA. alternatives? */
2225 cinfo->regbase = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002226 }
2227
Krzysztof Helt8503df62007-10-16 01:29:08 -07002228 DPRINTK("Board address: 0x%lx, register address: 0x%lx\n",
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002229 board_addr, info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002230
2231 board_size = (btype == BT_GD5480) ?
Krzysztof Helt8503df62007-10-16 01:29:08 -07002232 32 * MB_ : cirrusfb_get_memsize(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002233
2234 ret = pci_request_regions(pdev, "cirrusfb");
Krzysztof Helt8503df62007-10-16 01:29:08 -07002235 if (ret < 0) {
2236 printk(KERN_ERR "cirrusfb: cannot reserve region 0x%lx, "
2237 "abort\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002238 board_addr);
2239 goto err_release_fb;
2240 }
2241#if 0 /* if the system didn't claim this region, we would... */
2242 if (!request_mem_region(0xA0000, 65535, "cirrusfb")) {
2243 printk(KERN_ERR "cirrusfb: cannot reserve region 0x%lx, abort\n"
2244,
2245 0xA0000L);
2246 ret = -EBUSY;
2247 goto err_release_regions;
2248 }
2249#endif
2250 if (request_region(0x3C0, 32, "cirrusfb"))
2251 release_io_ports = 1;
2252
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002253 info->screen_base = ioremap(board_addr, board_size);
2254 if (!info->screen_base) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002255 ret = -EIO;
2256 goto err_release_legacy;
2257 }
2258
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002259 info->fix.smem_start = board_addr;
2260 info->screen_size = board_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002261 cinfo->unmap = cirrusfb_pci_unmap;
2262
Philippe De Muytere59b6a52008-06-12 15:21:45 -07002263 printk(KERN_INFO "RAM (%lu kB) at 0x%lx, Cirrus "
2264 "Logic chipset on PCI bus\n",
2265 info->screen_size >> 10, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002266 pci_set_drvdata(pdev, info);
2267
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002268 ret = cirrusfb_register(info);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002269 if (ret)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002270 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002271 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002272
2273err_release_legacy:
2274 if (release_io_ports)
2275 release_region(0x3C0, 32);
2276#if 0
2277 release_mem_region(0xA0000, 65535);
2278err_release_regions:
2279#endif
2280 pci_release_regions(pdev);
2281err_release_fb:
2282 framebuffer_release(info);
2283err_disable:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002284err_out:
2285 return ret;
2286}
2287
Krzysztof Helt8503df62007-10-16 01:29:08 -07002288static void __devexit cirrusfb_pci_unregister(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002289{
2290 struct fb_info *info = pci_get_drvdata(pdev);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002291 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002292
Krzysztof Helt8503df62007-10-16 01:29:08 -07002293 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002294
Krzysztof Helt8503df62007-10-16 01:29:08 -07002295 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002296}
2297
2298static struct pci_driver cirrusfb_pci_driver = {
2299 .name = "cirrusfb",
2300 .id_table = cirrusfb_pci_table,
2301 .probe = cirrusfb_pci_register,
2302 .remove = __devexit_p(cirrusfb_pci_unregister),
2303#ifdef CONFIG_PM
2304#if 0
2305 .suspend = cirrusfb_pci_suspend,
2306 .resume = cirrusfb_pci_resume,
2307#endif
2308#endif
2309};
2310#endif /* CONFIG_PCI */
2311
Linus Torvalds1da177e2005-04-16 15:20:36 -07002312#ifdef CONFIG_ZORRO
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002313static int __devinit cirrusfb_zorro_register(struct zorro_dev *z,
2314 const struct zorro_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002315{
2316 struct cirrusfb_info *cinfo;
2317 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002318 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002319 struct zorro_dev *z2 = NULL;
2320 unsigned long board_addr, board_size, size;
2321 int ret;
2322
2323 btype = ent->driver_data;
2324 if (cirrusfb_zorro_table2[btype].id2)
2325 z2 = zorro_find_device(cirrusfb_zorro_table2[btype].id2, NULL);
2326 size = cirrusfb_zorro_table2[btype].size;
2327 printk(KERN_INFO "cirrusfb: %s board detected; ",
2328 cirrusfb_board_info[btype].name);
2329
2330 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &z->dev);
2331 if (!info) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002332 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002333 ret = -ENOMEM;
2334 goto err_out;
2335 }
2336
2337 cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002338 cinfo->btype = btype;
2339
Al Viro36ea96a2007-10-27 19:46:58 +01002340 assert(z);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002341 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002342
Linus Torvalds1da177e2005-04-16 15:20:36 -07002343 board_addr = zorro_resource_start(z);
2344 board_size = zorro_resource_len(z);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002345 info->screen_size = size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002346
2347 if (!zorro_request_device(z, "cirrusfb")) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002348 printk(KERN_ERR "cirrusfb: cannot reserve region 0x%lx, "
2349 "abort\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002350 board_addr);
2351 ret = -EBUSY;
2352 goto err_release_fb;
2353 }
2354
Krzysztof Helt8503df62007-10-16 01:29:08 -07002355 printk(" RAM (%lu MB) at $%lx, ", board_size / MB_, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002356
2357 ret = -EIO;
2358
2359 if (btype == BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002360 printk(KERN_INFO " REG at $%lx\n", board_addr + 0x600000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002361
2362 /* To be precise, for the P4 this is not the */
2363 /* begin of the board, but the begin of RAM. */
2364 /* for P4, map in its address space in 2 chunks (### TEST! ) */
2365 /* (note the ugly hardcoded 16M number) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002366 cinfo->regbase = ioremap(board_addr, 16777216);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002367 if (!cinfo->regbase)
2368 goto err_release_region;
2369
Krzysztof Helt8503df62007-10-16 01:29:08 -07002370 DPRINTK("cirrusfb: Virtual address for board set to: $%p\n",
2371 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002372 cinfo->regbase += 0x600000;
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002373 info->fix.mmio_start = board_addr + 0x600000;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002374
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002375 info->fix.smem_start = board_addr + 16777216;
2376 info->screen_base = ioremap(info->fix.smem_start, 16777216);
2377 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002378 goto err_unmap_regbase;
2379 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002380 printk(KERN_INFO " REG at $%lx\n",
2381 (unsigned long) z2->resource.start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002382
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002383 info->fix.smem_start = board_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002384 if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002385 info->screen_base = ioremap(board_addr, board_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002386 else
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002387 info->screen_base = (caddr_t) ZTWO_VADDR(board_addr);
2388 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002389 goto err_release_region;
2390
2391 /* set address for REG area of board */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002392 cinfo->regbase = (caddr_t) ZTWO_VADDR(z2->resource.start);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002393 info->fix.mmio_start = z2->resource.start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002394
Krzysztof Helt8503df62007-10-16 01:29:08 -07002395 DPRINTK("cirrusfb: Virtual address for board set to: $%p\n",
2396 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002397 }
2398 cinfo->unmap = cirrusfb_zorro_unmap;
2399
Krzysztof Helt8503df62007-10-16 01:29:08 -07002400 printk(KERN_INFO "Cirrus Logic chipset on Zorro bus\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002401 zorro_set_drvdata(z, info);
2402
Al Virod91f5bb2007-10-17 00:27:18 +01002403 ret = cirrusfb_register(info);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002404 if (ret) {
2405 if (btype == BT_PICASSO4) {
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002406 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002407 iounmap(cinfo->regbase - 0x600000);
2408 } else if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002409 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002410 }
2411 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002412
2413err_unmap_regbase:
2414 /* Parental advisory: explicit hack */
2415 iounmap(cinfo->regbase - 0x600000);
2416err_release_region:
2417 release_region(board_addr, board_size);
2418err_release_fb:
2419 framebuffer_release(info);
2420err_out:
2421 return ret;
2422}
2423
2424void __devexit cirrusfb_zorro_unregister(struct zorro_dev *z)
2425{
2426 struct fb_info *info = zorro_get_drvdata(z);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002427 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002428
Krzysztof Helt8503df62007-10-16 01:29:08 -07002429 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002430
Krzysztof Helt8503df62007-10-16 01:29:08 -07002431 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002432}
2433
2434static struct zorro_driver cirrusfb_zorro_driver = {
2435 .name = "cirrusfb",
2436 .id_table = cirrusfb_zorro_table,
2437 .probe = cirrusfb_zorro_register,
2438 .remove = __devexit_p(cirrusfb_zorro_unregister),
2439};
2440#endif /* CONFIG_ZORRO */
2441
2442static int __init cirrusfb_init(void)
2443{
2444 int error = 0;
2445
2446#ifndef MODULE
2447 char *option = NULL;
2448
2449 if (fb_get_options("cirrusfb", &option))
2450 return -ENODEV;
2451 cirrusfb_setup(option);
2452#endif
2453
2454#ifdef CONFIG_ZORRO
Bjorn Helgaas33d86752006-03-25 03:07:20 -08002455 error |= zorro_register_driver(&cirrusfb_zorro_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002456#endif
2457#ifdef CONFIG_PCI
2458 error |= pci_register_driver(&cirrusfb_pci_driver);
2459#endif
2460 return error;
2461}
2462
Linus Torvalds1da177e2005-04-16 15:20:36 -07002463#ifndef MODULE
2464static int __init cirrusfb_setup(char *options) {
2465 char *this_opt, s[32];
2466 int i;
2467
Krzysztof Helt8503df62007-10-16 01:29:08 -07002468 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002469
2470 if (!options || !*options)
2471 return 0;
2472
Krzysztof Helt8503df62007-10-16 01:29:08 -07002473 while ((this_opt = strsep(&options, ",")) != NULL) {
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002474 if (!*this_opt)
2475 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002476
2477 DPRINTK("cirrusfb_setup: option '%s'\n", this_opt);
2478
Linus Torvalds1da177e2005-04-16 15:20:36 -07002479 if (!strcmp(this_opt, "noaccel"))
2480 noaccel = 1;
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002481 else if (!strncmp(this_opt, "mode:", 5))
2482 mode_option = this_opt + 5;
2483 else
2484 mode_option = this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002485 }
2486 return 0;
2487}
2488#endif
2489
Linus Torvalds1da177e2005-04-16 15:20:36 -07002490 /*
2491 * Modularization
2492 */
2493
2494MODULE_AUTHOR("Copyright 1999,2000 Jeff Garzik <jgarzik@pobox.com>");
2495MODULE_DESCRIPTION("Accelerated FBDev driver for Cirrus Logic chips");
2496MODULE_LICENSE("GPL");
2497
Krzysztof Helt8503df62007-10-16 01:29:08 -07002498static void __exit cirrusfb_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002499{
2500#ifdef CONFIG_PCI
2501 pci_unregister_driver(&cirrusfb_pci_driver);
2502#endif
2503#ifdef CONFIG_ZORRO
2504 zorro_unregister_driver(&cirrusfb_zorro_driver);
2505#endif
2506}
2507
2508module_init(cirrusfb_init);
2509
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002510module_param(mode_option, charp, 0);
2511MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");
Krzysztof Helt55a0dd82008-10-15 22:03:41 -07002512module_param(noaccel, bool, 0);
2513MODULE_PARM_DESC(noaccel, "Disable acceleration");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002514
Linus Torvalds1da177e2005-04-16 15:20:36 -07002515#ifdef MODULE
2516module_exit(cirrusfb_exit);
2517#endif
2518
Linus Torvalds1da177e2005-04-16 15:20:36 -07002519/**********************************************************************/
2520/* about the following functions - I have used the same names for the */
2521/* functions as Markus Wild did in his Retina driver for NetBSD as */
2522/* they just made sense for this purpose. Apart from that, I wrote */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002523/* these functions myself. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002524/**********************************************************************/
2525
2526/*** WGen() - write into one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002527static void WGen(const struct cirrusfb_info *cinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002528 int regnum, unsigned char val)
2529{
2530 unsigned long regofs = 0;
2531
2532 if (cinfo->btype == BT_PICASSO) {
2533 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002534/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2535 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002536 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2537 regofs = 0xfff;
2538 }
2539
Krzysztof Helt8503df62007-10-16 01:29:08 -07002540 vga_w(cinfo->regbase, regofs + regnum, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002541}
2542
2543/*** RGen() - read out one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002544static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002545{
2546 unsigned long regofs = 0;
2547
2548 if (cinfo->btype == BT_PICASSO) {
2549 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002550/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2551 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002552 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2553 regofs = 0xfff;
2554 }
2555
Krzysztof Helt8503df62007-10-16 01:29:08 -07002556 return vga_r(cinfo->regbase, regofs + regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002557}
2558
2559/*** AttrOn() - turn on VideoEnable for Attribute controller ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002560static void AttrOn(const struct cirrusfb_info *cinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002561{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002562 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002563
Krzysztof Helt8503df62007-10-16 01:29:08 -07002564 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002565
Krzysztof Helt8503df62007-10-16 01:29:08 -07002566 if (vga_rcrt(cinfo->regbase, CL_CRT24) & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002567 /* if we're just in "write value" mode, write back the */
2568 /* same value as before to not modify anything */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002569 vga_w(cinfo->regbase, VGA_ATT_IW,
2570 vga_r(cinfo->regbase, VGA_ATT_R));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002571 }
2572 /* turn on video bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002573/* vga_w(cinfo->regbase, VGA_ATT_IW, 0x20); */
2574 vga_w(cinfo->regbase, VGA_ATT_IW, 0x33);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002575
2576 /* dummy write on Reg0 to be on "write index" mode next time */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002577 vga_w(cinfo->regbase, VGA_ATT_IW, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002578
Krzysztof Helt8503df62007-10-16 01:29:08 -07002579 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002580}
2581
2582/*** WHDR() - write into the Hidden DAC register ***/
2583/* as the HDR is the only extension register that requires special treatment
2584 * (the other extension registers are accessible just like the "ordinary"
2585 * registers of their functional group) here is a specialized routine for
2586 * accessing the HDR
2587 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002588static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002589{
2590 unsigned char dummy;
2591
2592 if (cinfo->btype == BT_PICASSO) {
2593 /* Klaus' hint for correct access to HDR on some boards */
2594 /* first write 0 to pixel mask (3c6) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002595 WGen(cinfo, VGA_PEL_MSK, 0x00);
2596 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002597 /* next read dummy from pixel address (3c8) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002598 dummy = RGen(cinfo, VGA_PEL_IW);
2599 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002600 }
2601 /* now do the usual stuff to access the HDR */
2602
Krzysztof Helt8503df62007-10-16 01:29:08 -07002603 dummy = RGen(cinfo, VGA_PEL_MSK);
2604 udelay(200);
2605 dummy = RGen(cinfo, VGA_PEL_MSK);
2606 udelay(200);
2607 dummy = RGen(cinfo, VGA_PEL_MSK);
2608 udelay(200);
2609 dummy = RGen(cinfo, VGA_PEL_MSK);
2610 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002611
Krzysztof Helt8503df62007-10-16 01:29:08 -07002612 WGen(cinfo, VGA_PEL_MSK, val);
2613 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002614
2615 if (cinfo->btype == BT_PICASSO) {
2616 /* now first reset HDR access counter */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002617 dummy = RGen(cinfo, VGA_PEL_IW);
2618 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002619
2620 /* and at the end, restore the mask value */
2621 /* ## is this mask always 0xff? */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002622 WGen(cinfo, VGA_PEL_MSK, 0xff);
2623 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002624 }
2625}
2626
Linus Torvalds1da177e2005-04-16 15:20:36 -07002627/*** WSFR() - write to the "special function register" (SFR) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002628static void WSFR(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002629{
2630#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07002631 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002632 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002633 z_writeb(val, cinfo->regbase + 0x8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002634#endif
2635}
2636
2637/* The Picasso has a second register for switching the monitor bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002638static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002639{
2640#ifdef CONFIG_ZORRO
2641 /* writing an arbitrary value to this one causes the monitor switcher */
2642 /* to flip to Amiga display */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002643 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002644 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002645 z_writeb(val, cinfo->regbase + 0x9000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002646#endif
2647}
2648
Linus Torvalds1da177e2005-04-16 15:20:36 -07002649/*** WClut - set CLUT entry (range: 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002650static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002651 unsigned char green, unsigned char blue)
2652{
2653 unsigned int data = VGA_PEL_D;
2654
2655 /* address write mode register is not translated.. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002656 vga_w(cinfo->regbase, VGA_PEL_IW, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002657
2658 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2659 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2660 /* but DAC data register IS, at least for Picasso II */
2661 if (cinfo->btype == BT_PICASSO)
2662 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002663 vga_w(cinfo->regbase, data, red);
2664 vga_w(cinfo->regbase, data, green);
2665 vga_w(cinfo->regbase, data, blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002666 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002667 vga_w(cinfo->regbase, data, blue);
2668 vga_w(cinfo->regbase, data, green);
2669 vga_w(cinfo->regbase, data, red);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002670 }
2671}
2672
Linus Torvalds1da177e2005-04-16 15:20:36 -07002673#if 0
2674/*** RClut - read CLUT entry (range 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002675static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char *red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002676 unsigned char *green, unsigned char *blue)
2677{
2678 unsigned int data = VGA_PEL_D;
2679
Krzysztof Helt8503df62007-10-16 01:29:08 -07002680 vga_w(cinfo->regbase, VGA_PEL_IR, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002681
2682 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2683 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2684 if (cinfo->btype == BT_PICASSO)
2685 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002686 *red = vga_r(cinfo->regbase, data);
2687 *green = vga_r(cinfo->regbase, data);
2688 *blue = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002689 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002690 *blue = vga_r(cinfo->regbase, data);
2691 *green = vga_r(cinfo->regbase, data);
2692 *red = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002693 }
2694}
2695#endif
2696
Linus Torvalds1da177e2005-04-16 15:20:36 -07002697/*******************************************************************
2698 cirrusfb_WaitBLT()
2699
2700 Wait for the BitBLT engine to complete a possible earlier job
2701*********************************************************************/
2702
2703/* FIXME: use interrupts instead */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002704static void cirrusfb_WaitBLT(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002705{
2706 /* now busy-wait until we're done */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002707 while (vga_rgfx(regbase, CL_GR31) & 0x08)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002708 /* do nothing */ ;
2709}
2710
2711/*******************************************************************
2712 cirrusfb_BitBLT()
2713
2714 perform accelerated "scrolling"
2715********************************************************************/
2716
Krzysztof Helt8503df62007-10-16 01:29:08 -07002717static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
2718 u_short curx, u_short cury,
2719 u_short destx, u_short desty,
2720 u_short width, u_short height,
2721 u_short line_length)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002722{
2723 u_short nwidth, nheight;
2724 u_long nsrc, ndest;
2725 u_char bltmode;
2726
Krzysztof Helt8503df62007-10-16 01:29:08 -07002727 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002728
2729 nwidth = width - 1;
2730 nheight = height - 1;
2731
2732 bltmode = 0x00;
2733 /* if source adr < dest addr, do the Blt backwards */
2734 if (cury <= desty) {
2735 if (cury == desty) {
2736 /* if src and dest are on the same line, check x */
2737 if (curx < destx)
2738 bltmode |= 0x01;
2739 } else
2740 bltmode |= 0x01;
2741 }
2742 if (!bltmode) {
2743 /* standard case: forward blitting */
2744 nsrc = (cury * line_length) + curx;
2745 ndest = (desty * line_length) + destx;
2746 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002747 /* this means start addresses are at the end,
2748 * counting backwards
2749 */
2750 nsrc = cury * line_length + curx +
2751 nheight * line_length + nwidth;
2752 ndest = desty * line_length + destx +
2753 nheight * line_length + nwidth;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002754 }
2755
2756 /*
2757 run-down of registers to be programmed:
2758 destination pitch
2759 source pitch
2760 BLT width/height
2761 source start
2762 destination start
2763 BLT mode
2764 BLT ROP
2765 VGA_GFX_SR_VALUE / VGA_GFX_SR_ENABLE: "fill color"
2766 start/stop
2767 */
2768
Krzysztof Helt8503df62007-10-16 01:29:08 -07002769 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002770
2771 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002772 /* dest pitch low */
2773 vga_wgfx(regbase, CL_GR24, line_length & 0xff);
2774 /* dest pitch hi */
2775 vga_wgfx(regbase, CL_GR25, line_length >> 8);
2776 /* source pitch low */
2777 vga_wgfx(regbase, CL_GR26, line_length & 0xff);
2778 /* source pitch hi */
2779 vga_wgfx(regbase, CL_GR27, line_length >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002780
2781 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002782 /* BLT width low */
2783 vga_wgfx(regbase, CL_GR20, nwidth & 0xff);
2784 /* BLT width hi */
2785 vga_wgfx(regbase, CL_GR21, nwidth >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002786
2787 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002788 /* BLT height low */
2789 vga_wgfx(regbase, CL_GR22, nheight & 0xff);
2790 /* BLT width hi */
2791 vga_wgfx(regbase, CL_GR23, nheight >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002792
2793 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002794 /* BLT dest low */
2795 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2796 /* BLT dest mid */
2797 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2798 /* BLT dest hi */
2799 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002800
2801 /* BLT source */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002802 /* BLT src low */
2803 vga_wgfx(regbase, CL_GR2C, (u_char) (nsrc & 0xff));
2804 /* BLT src mid */
2805 vga_wgfx(regbase, CL_GR2D, (u_char) (nsrc >> 8));
2806 /* BLT src hi */
2807 vga_wgfx(regbase, CL_GR2E, (u_char) (nsrc >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002808
2809 /* BLT mode */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002810 vga_wgfx(regbase, CL_GR30, bltmode); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002811
2812 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002813 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002814
2815 /* and finally: GO! */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002816 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002817
Krzysztof Helt8503df62007-10-16 01:29:08 -07002818 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002819}
2820
Linus Torvalds1da177e2005-04-16 15:20:36 -07002821/*******************************************************************
2822 cirrusfb_RectFill()
2823
2824 perform accelerated rectangle fill
2825********************************************************************/
2826
Krzysztof Helt8503df62007-10-16 01:29:08 -07002827static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002828 u_short x, u_short y, u_short width, u_short height,
2829 u_char color, u_short line_length)
2830{
2831 u_short nwidth, nheight;
2832 u_long ndest;
2833 u_char op;
2834
Krzysztof Helt8503df62007-10-16 01:29:08 -07002835 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002836
2837 nwidth = width - 1;
2838 nheight = height - 1;
2839
2840 ndest = (y * line_length) + x;
2841
Krzysztof Helt8503df62007-10-16 01:29:08 -07002842 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002843
2844 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002845 vga_wgfx(regbase, CL_GR24, line_length & 0xff); /* dest pitch low */
2846 vga_wgfx(regbase, CL_GR25, line_length >> 8); /* dest pitch hi */
2847 vga_wgfx(regbase, CL_GR26, line_length & 0xff); /* source pitch low */
2848 vga_wgfx(regbase, CL_GR27, line_length >> 8); /* source pitch hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002849
2850 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002851 vga_wgfx(regbase, CL_GR20, nwidth & 0xff); /* BLT width low */
2852 vga_wgfx(regbase, CL_GR21, nwidth >> 8); /* BLT width hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002853
2854 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002855 vga_wgfx(regbase, CL_GR22, nheight & 0xff); /* BLT height low */
2856 vga_wgfx(regbase, CL_GR23, nheight >> 8); /* BLT width hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002857
2858 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002859 /* BLT dest low */
2860 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2861 /* BLT dest mid */
2862 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2863 /* BLT dest hi */
2864 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002865
2866 /* BLT source: set to 0 (is a dummy here anyway) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002867 vga_wgfx(regbase, CL_GR2C, 0x00); /* BLT src low */
2868 vga_wgfx(regbase, CL_GR2D, 0x00); /* BLT src mid */
2869 vga_wgfx(regbase, CL_GR2E, 0x00); /* BLT src hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002870
2871 /* This is a ColorExpand Blt, using the */
2872 /* same color for foreground and background */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002873 vga_wgfx(regbase, VGA_GFX_SR_VALUE, color); /* foreground color */
2874 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, color); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002875
2876 op = 0xc0;
2877 if (bits_per_pixel == 16) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002878 vga_wgfx(regbase, CL_GR10, color); /* foreground color */
2879 vga_wgfx(regbase, CL_GR11, color); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002880 op = 0x50;
2881 op = 0xd0;
2882 } else if (bits_per_pixel == 32) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002883 vga_wgfx(regbase, CL_GR10, color); /* foreground color */
2884 vga_wgfx(regbase, CL_GR11, color); /* background color */
2885 vga_wgfx(regbase, CL_GR12, color); /* foreground color */
2886 vga_wgfx(regbase, CL_GR13, color); /* background color */
2887 vga_wgfx(regbase, CL_GR14, 0); /* foreground color */
2888 vga_wgfx(regbase, CL_GR15, 0); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002889 op = 0x50;
2890 op = 0xf0;
2891 }
2892 /* BLT mode: color expand, Enable 8x8 copy (faster?) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002893 vga_wgfx(regbase, CL_GR30, op); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002894
2895 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002896 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002897
2898 /* and finally: GO! */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002899 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002900
Krzysztof Helt8503df62007-10-16 01:29:08 -07002901 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002902}
2903
Linus Torvalds1da177e2005-04-16 15:20:36 -07002904/**************************************************************************
2905 * bestclock() - determine closest possible clock lower(?) than the
2906 * desired pixel clock
2907 **************************************************************************/
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002908static void bestclock(long freq, int *nom, int *den, int *div)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002909{
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002910 int n, d;
2911 long h, diff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002912
Krzysztof Helt8503df62007-10-16 01:29:08 -07002913 assert(nom != NULL);
2914 assert(den != NULL);
2915 assert(div != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002916
2917 *nom = 0;
2918 *den = 0;
2919 *div = 0;
2920
Krzysztof Helt8503df62007-10-16 01:29:08 -07002921 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002922
2923 if (freq < 8000)
2924 freq = 8000;
2925
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002926 diff = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002927
2928 for (n = 32; n < 128; n++) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002929 int s = 0;
2930
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002931 d = (14318 * n) / freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002932 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002933 int temp = d;
2934
2935 if (temp > 31) {
2936 s = 1;
2937 temp >>= 1;
2938 }
2939 h = ((14318 * n) / temp) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002940 h = h > freq ? h - freq : freq - h;
2941 if (h < diff) {
2942 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002943 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002944 *den = temp;
2945 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002946 }
2947 }
Krzysztof Helt7528f542008-10-15 22:03:36 -07002948 d++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002949 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002950 if (d > 31) {
2951 s = 1;
2952 d >>= 1;
2953 }
2954 h = ((14318 * n) / d) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002955 h = h > freq ? h - freq : freq - h;
2956 if (h < diff) {
2957 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002958 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002959 *den = d;
2960 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002961 }
2962 }
2963 }
2964
Krzysztof Helt8503df62007-10-16 01:29:08 -07002965 DPRINTK("Best possible values for given frequency:\n");
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002966 DPRINTK(" freq: %ld kHz nom: %d den: %d div: %d\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002967 freq, *nom, *den, *div);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002968
Krzysztof Helt8503df62007-10-16 01:29:08 -07002969 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002970}
2971
Linus Torvalds1da177e2005-04-16 15:20:36 -07002972/* -------------------------------------------------------------------------
2973 *
2974 * debugging functions
2975 *
2976 * -------------------------------------------------------------------------
2977 */
2978
2979#ifdef CIRRUSFB_DEBUG
2980
2981/**
2982 * cirrusfb_dbg_print_byte
2983 * @name: name associated with byte value to be displayed
2984 * @val: byte value to be displayed
2985 *
2986 * DESCRIPTION:
2987 * Display an indented string, along with a hexidecimal byte value, and
2988 * its decoded bits. Bits 7 through 0 are listed in left-to-right
2989 * order.
2990 */
2991
2992static
Krzysztof Helt8503df62007-10-16 01:29:08 -07002993void cirrusfb_dbg_print_byte(const char *name, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002994{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002995 DPRINTK("%8s = 0x%02X (bits 7-0: %c%c%c%c%c%c%c%c)\n",
2996 name, val,
2997 val & 0x80 ? '1' : '0',
2998 val & 0x40 ? '1' : '0',
2999 val & 0x20 ? '1' : '0',
3000 val & 0x10 ? '1' : '0',
3001 val & 0x08 ? '1' : '0',
3002 val & 0x04 ? '1' : '0',
3003 val & 0x02 ? '1' : '0',
3004 val & 0x01 ? '1' : '0');
Linus Torvalds1da177e2005-04-16 15:20:36 -07003005}
3006
Linus Torvalds1da177e2005-04-16 15:20:36 -07003007/**
3008 * cirrusfb_dbg_print_regs
3009 * @base: If using newmmio, the newmmio base address, otherwise %NULL
3010 * @reg_class: type of registers to read: %CRT, or %SEQ
3011 *
3012 * DESCRIPTION:
3013 * Dumps the given list of VGA CRTC registers. If @base is %NULL,
3014 * old-style I/O ports are queried for information, otherwise MMIO is
3015 * used at the given @base address to query the information.
3016 */
3017
3018static
Krzysztof Helt8503df62007-10-16 01:29:08 -07003019void cirrusfb_dbg_print_regs(caddr_t regbase,
Krzysztof Helt7345de32007-10-16 01:29:11 -07003020 enum cirrusfb_dbg_reg_class reg_class, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003021{
3022 va_list list;
3023 unsigned char val = 0;
3024 unsigned reg;
3025 char *name;
3026
Krzysztof Helt8503df62007-10-16 01:29:08 -07003027 va_start(list, reg_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003028
Krzysztof Helt8503df62007-10-16 01:29:08 -07003029 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003030 while (name != NULL) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07003031 reg = va_arg(list, int);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003032
3033 switch (reg_class) {
3034 case CRT:
Krzysztof Helt8503df62007-10-16 01:29:08 -07003035 val = vga_rcrt(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003036 break;
3037 case SEQ:
Krzysztof Helt8503df62007-10-16 01:29:08 -07003038 val = vga_rseq(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003039 break;
3040 default:
3041 /* should never occur */
Richard Knutssonc930faa2007-05-08 00:38:29 -07003042 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003043 break;
3044 }
3045
Krzysztof Helt8503df62007-10-16 01:29:08 -07003046 cirrusfb_dbg_print_byte(name, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003047
Krzysztof Helt8503df62007-10-16 01:29:08 -07003048 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003049 }
3050
Krzysztof Helt8503df62007-10-16 01:29:08 -07003051 va_end(list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003052}
3053
Linus Torvalds1da177e2005-04-16 15:20:36 -07003054/**
3055 * cirrusfb_dump
3056 * @cirrusfbinfo:
3057 *
3058 * DESCRIPTION:
3059 */
3060
Krzysztof Helt8503df62007-10-16 01:29:08 -07003061static void cirrusfb_dump(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003062{
Krzysztof Helt8503df62007-10-16 01:29:08 -07003063 cirrusfb_dbg_reg_dump(NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003064}
3065
Linus Torvalds1da177e2005-04-16 15:20:36 -07003066/**
3067 * cirrusfb_dbg_reg_dump
3068 * @base: If using newmmio, the newmmio base address, otherwise %NULL
3069 *
3070 * DESCRIPTION:
3071 * Dumps a list of interesting VGA and CIRRUSFB registers. If @base is %NULL,
3072 * old-style I/O ports are queried for information, otherwise MMIO is
3073 * used at the given @base address to query the information.
3074 */
3075
3076static
Krzysztof Helt8503df62007-10-16 01:29:08 -07003077void cirrusfb_dbg_reg_dump(caddr_t regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003078{
Krzysztof Helt8503df62007-10-16 01:29:08 -07003079 DPRINTK("CIRRUSFB VGA CRTC register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003080
Krzysztof Helt8503df62007-10-16 01:29:08 -07003081 cirrusfb_dbg_print_regs(regbase, CRT,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003082 "CR00", 0x00,
3083 "CR01", 0x01,
3084 "CR02", 0x02,
3085 "CR03", 0x03,
3086 "CR04", 0x04,
3087 "CR05", 0x05,
3088 "CR06", 0x06,
3089 "CR07", 0x07,
3090 "CR08", 0x08,
3091 "CR09", 0x09,
3092 "CR0A", 0x0A,
3093 "CR0B", 0x0B,
3094 "CR0C", 0x0C,
3095 "CR0D", 0x0D,
3096 "CR0E", 0x0E,
3097 "CR0F", 0x0F,
3098 "CR10", 0x10,
3099 "CR11", 0x11,
3100 "CR12", 0x12,
3101 "CR13", 0x13,
3102 "CR14", 0x14,
3103 "CR15", 0x15,
3104 "CR16", 0x16,
3105 "CR17", 0x17,
3106 "CR18", 0x18,
3107 "CR22", 0x22,
3108 "CR24", 0x24,
3109 "CR26", 0x26,
3110 "CR2D", 0x2D,
3111 "CR2E", 0x2E,
3112 "CR2F", 0x2F,
3113 "CR30", 0x30,
3114 "CR31", 0x31,
3115 "CR32", 0x32,
3116 "CR33", 0x33,
3117 "CR34", 0x34,
3118 "CR35", 0x35,
3119 "CR36", 0x36,
3120 "CR37", 0x37,
3121 "CR38", 0x38,
3122 "CR39", 0x39,
3123 "CR3A", 0x3A,
3124 "CR3B", 0x3B,
3125 "CR3C", 0x3C,
3126 "CR3D", 0x3D,
3127 "CR3E", 0x3E,
3128 "CR3F", 0x3F,
3129 NULL);
3130
Krzysztof Helt8503df62007-10-16 01:29:08 -07003131 DPRINTK("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003132
Krzysztof Helt8503df62007-10-16 01:29:08 -07003133 DPRINTK("CIRRUSFB VGA SEQ register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003134
Krzysztof Helt8503df62007-10-16 01:29:08 -07003135 cirrusfb_dbg_print_regs(regbase, SEQ,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003136 "SR00", 0x00,
3137 "SR01", 0x01,
3138 "SR02", 0x02,
3139 "SR03", 0x03,
3140 "SR04", 0x04,
3141 "SR08", 0x08,
3142 "SR09", 0x09,
3143 "SR0A", 0x0A,
3144 "SR0B", 0x0B,
3145 "SR0D", 0x0D,
3146 "SR10", 0x10,
3147 "SR11", 0x11,
3148 "SR12", 0x12,
3149 "SR13", 0x13,
3150 "SR14", 0x14,
3151 "SR15", 0x15,
3152 "SR16", 0x16,
3153 "SR17", 0x17,
3154 "SR18", 0x18,
3155 "SR19", 0x19,
3156 "SR1A", 0x1A,
3157 "SR1B", 0x1B,
3158 "SR1C", 0x1C,
3159 "SR1D", 0x1D,
3160 "SR1E", 0x1E,
3161 "SR1F", 0x1F,
3162 NULL);
3163
Krzysztof Helt8503df62007-10-16 01:29:08 -07003164 DPRINTK("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003165}
3166
3167#endif /* CIRRUSFB_DEBUG */
3168