blob: 30c47f167608e3c0b42e220d7e63ca97464616f6 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * drivers/video/cirrusfb.c - driver for Cirrus Logic chipsets
3 *
4 * Copyright 1999-2001 Jeff Garzik <jgarzik@pobox.com>
5 *
6 * Contributors (thanks, all!)
7 *
8 * David Eger:
9 * Overhaul for Linux 2.6
10 *
11 * Jeff Rugen:
12 * Major contributions; Motorola PowerStack (PPC and PCI) support,
13 * GD54xx, 1280x1024 mode support, change MCLK based on VCLK.
14 *
15 * Geert Uytterhoeven:
16 * Excellent code review.
17 *
18 * Lars Hecking:
19 * Amiga updates and testing.
20 *
21 * Original cirrusfb author: Frank Neumann
22 *
23 * Based on retz3fb.c and cirrusfb.c:
24 * Copyright (C) 1997 Jes Sorensen
25 * Copyright (C) 1996 Frank Neumann
26 *
27 ***************************************************************
28 *
29 * Format this code with GNU indent '-kr -i8 -pcs' options.
30 *
31 * This file is subject to the terms and conditions of the GNU General Public
32 * License. See the file COPYING in the main directory of this archive
33 * for more details.
34 *
35 */
36
37#define CIRRUSFB_VERSION "2.0-pre2"
38
Linus Torvalds1da177e2005-04-16 15:20:36 -070039#include <linux/module.h>
40#include <linux/kernel.h>
41#include <linux/errno.h>
42#include <linux/string.h>
43#include <linux/mm.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070044#include <linux/slab.h>
45#include <linux/delay.h>
46#include <linux/fb.h>
47#include <linux/init.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070048#include <asm/pgtable.h>
49
50#ifdef CONFIG_ZORRO
51#include <linux/zorro.h>
52#endif
53#ifdef CONFIG_PCI
54#include <linux/pci.h>
55#endif
56#ifdef CONFIG_AMIGA
57#include <asm/amigahw.h>
58#endif
59#ifdef CONFIG_PPC_PREP
Benjamin Herrenschmidte8222502006-03-28 23:15:54 +110060#include <asm/machdep.h>
Krzysztof Helt8503df62007-10-16 01:29:08 -070061#define isPReP machine_is(prep)
Linus Torvalds1da177e2005-04-16 15:20:36 -070062#else
63#define isPReP 0
64#endif
65
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -070066#include <video/vga.h>
67#include <video/cirrus.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070068
Linus Torvalds1da177e2005-04-16 15:20:36 -070069/*****************************************************************
70 *
71 * debugging and utility macros
72 *
73 */
74
75/* enable debug output? */
76/* #define CIRRUSFB_DEBUG 1 */
77
78/* disable runtime assertions? */
79/* #define CIRRUSFB_NDEBUG */
80
81/* debug output */
82#ifdef CIRRUSFB_DEBUG
Krzysztof Helt8503df62007-10-16 01:29:08 -070083#define DPRINTK(fmt, args...) \
Harvey Harrison5ae12172008-04-28 02:15:47 -070084 printk(KERN_DEBUG "%s: " fmt, __func__ , ## args)
Linus Torvalds1da177e2005-04-16 15:20:36 -070085#else
86#define DPRINTK(fmt, args...)
87#endif
88
89/* debugging assertions */
90#ifndef CIRRUSFB_NDEBUG
91#define assert(expr) \
Krzysztof Helt8503df62007-10-16 01:29:08 -070092 if (!(expr)) { \
93 printk("Assertion failed! %s,%s,%s,line=%d\n", \
Harvey Harrison5ae12172008-04-28 02:15:47 -070094 #expr, __FILE__, __func__, __LINE__); \
Krzysztof Helt8503df62007-10-16 01:29:08 -070095 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070096#else
97#define assert(expr)
98#endif
99
Krzysztof Helt8503df62007-10-16 01:29:08 -0700100#define MB_ (1024 * 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700101
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102/*****************************************************************
103 *
104 * chipset information
105 *
106 */
107
108/* board types */
Krzysztof Helt7345de32007-10-16 01:29:11 -0700109enum cirrus_board {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110 BT_NONE = 0,
111 BT_SD64,
112 BT_PICCOLO,
113 BT_PICASSO,
114 BT_SPECTRUM,
115 BT_PICASSO4, /* GD5446 */
116 BT_ALPINE, /* GD543x/4x */
117 BT_GD5480,
118 BT_LAGUNA, /* GD546x */
Krzysztof Helt7345de32007-10-16 01:29:11 -0700119};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121/*
122 * per-board-type information, used for enumerating and abstracting
123 * chip-specific information
Krzysztof Helt7345de32007-10-16 01:29:11 -0700124 * NOTE: MUST be in the same order as enum cirrus_board in order to
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125 * use direct indexing on this array
126 * NOTE: '__initdata' cannot be used as some of this info
127 * is required at runtime. Maybe separate into an init-only and
128 * a run-time table?
129 */
130static const struct cirrusfb_board_info_rec {
131 char *name; /* ASCII name of chipset */
132 long maxclock[5]; /* maximum video clock */
133 /* for 1/4bpp, 8bpp 15/16bpp, 24bpp, 32bpp - numbers from xorg code */
Richard Knutssonc930faa2007-05-08 00:38:29 -0700134 bool init_sr07 : 1; /* init SR07 during init_vgachip() */
135 bool init_sr1f : 1; /* write SR1F during init_vgachip() */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700136 /* construct bit 19 of screen start address */
137 bool scrn_start_bit19 : 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138
139 /* initial SR07 value, then for each mode */
140 unsigned char sr07;
141 unsigned char sr07_1bpp;
142 unsigned char sr07_1bpp_mux;
143 unsigned char sr07_8bpp;
144 unsigned char sr07_8bpp_mux;
145
146 unsigned char sr1f; /* SR1F VGA initial register value */
147} cirrusfb_board_info[] = {
148 [BT_SD64] = {
149 .name = "CL SD64",
150 .maxclock = {
151 /* guess */
152 /* the SD64/P4 have a higher max. videoclock */
153 140000, 140000, 140000, 140000, 140000,
154 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700155 .init_sr07 = true,
156 .init_sr1f = true,
157 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158 .sr07 = 0xF0,
159 .sr07_1bpp = 0xF0,
160 .sr07_8bpp = 0xF1,
161 .sr1f = 0x20
162 },
163 [BT_PICCOLO] = {
164 .name = "CL Piccolo",
165 .maxclock = {
166 /* guess */
167 90000, 90000, 90000, 90000, 90000
168 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700169 .init_sr07 = true,
170 .init_sr1f = true,
171 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172 .sr07 = 0x80,
173 .sr07_1bpp = 0x80,
174 .sr07_8bpp = 0x81,
175 .sr1f = 0x22
176 },
177 [BT_PICASSO] = {
178 .name = "CL Picasso",
179 .maxclock = {
180 /* guess */
181 90000, 90000, 90000, 90000, 90000
182 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700183 .init_sr07 = true,
184 .init_sr1f = true,
185 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186 .sr07 = 0x20,
187 .sr07_1bpp = 0x20,
188 .sr07_8bpp = 0x21,
189 .sr1f = 0x22
190 },
191 [BT_SPECTRUM] = {
192 .name = "CL Spectrum",
193 .maxclock = {
194 /* guess */
195 90000, 90000, 90000, 90000, 90000
196 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700197 .init_sr07 = true,
198 .init_sr1f = true,
199 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200 .sr07 = 0x80,
201 .sr07_1bpp = 0x80,
202 .sr07_8bpp = 0x81,
203 .sr1f = 0x22
204 },
205 [BT_PICASSO4] = {
206 .name = "CL Picasso4",
207 .maxclock = {
208 135100, 135100, 85500, 85500, 0
209 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700210 .init_sr07 = true,
211 .init_sr1f = false,
212 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 .sr07 = 0x20,
214 .sr07_1bpp = 0x20,
215 .sr07_8bpp = 0x21,
216 .sr1f = 0
217 },
218 [BT_ALPINE] = {
219 .name = "CL Alpine",
220 .maxclock = {
221 /* for the GD5430. GD5446 can do more... */
222 85500, 85500, 50000, 28500, 0
223 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700224 .init_sr07 = true,
225 .init_sr1f = true,
226 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227 .sr07 = 0xA0,
228 .sr07_1bpp = 0xA1,
229 .sr07_1bpp_mux = 0xA7,
230 .sr07_8bpp = 0xA1,
231 .sr07_8bpp_mux = 0xA7,
232 .sr1f = 0x1C
233 },
234 [BT_GD5480] = {
235 .name = "CL GD5480",
236 .maxclock = {
237 135100, 200000, 200000, 135100, 135100
238 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700239 .init_sr07 = true,
240 .init_sr1f = true,
241 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242 .sr07 = 0x10,
243 .sr07_1bpp = 0x11,
244 .sr07_8bpp = 0x11,
245 .sr1f = 0x1C
246 },
247 [BT_LAGUNA] = {
248 .name = "CL Laguna",
249 .maxclock = {
250 /* guess */
251 135100, 135100, 135100, 135100, 135100,
252 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700253 .init_sr07 = false,
254 .init_sr1f = false,
255 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256 }
257};
258
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259#ifdef CONFIG_PCI
260#define CHIP(id, btype) \
Grant Coady41538122005-09-29 10:40:52 +1000261 { PCI_VENDOR_ID_CIRRUS, id, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (btype) }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262
263static struct pci_device_id cirrusfb_pci_table[] = {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700264 CHIP(PCI_DEVICE_ID_CIRRUS_5436, BT_ALPINE),
265 CHIP(PCI_DEVICE_ID_CIRRUS_5434_8, BT_ALPINE),
266 CHIP(PCI_DEVICE_ID_CIRRUS_5434_4, BT_ALPINE),
267 CHIP(PCI_DEVICE_ID_CIRRUS_5430, BT_ALPINE), /* GD-5440 is same id */
268 CHIP(PCI_DEVICE_ID_CIRRUS_7543, BT_ALPINE),
269 CHIP(PCI_DEVICE_ID_CIRRUS_7548, BT_ALPINE),
270 CHIP(PCI_DEVICE_ID_CIRRUS_5480, BT_GD5480), /* MacPicasso likely */
271 CHIP(PCI_DEVICE_ID_CIRRUS_5446, BT_PICASSO4), /* Picasso 4 is 5446 */
272 CHIP(PCI_DEVICE_ID_CIRRUS_5462, BT_LAGUNA), /* CL Laguna */
273 CHIP(PCI_DEVICE_ID_CIRRUS_5464, BT_LAGUNA), /* CL Laguna 3D */
274 CHIP(PCI_DEVICE_ID_CIRRUS_5465, BT_LAGUNA), /* CL Laguna 3DA*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 { 0, }
276};
277MODULE_DEVICE_TABLE(pci, cirrusfb_pci_table);
278#undef CHIP
279#endif /* CONFIG_PCI */
280
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281#ifdef CONFIG_ZORRO
282static const struct zorro_device_id cirrusfb_zorro_table[] = {
283 {
284 .id = ZORRO_PROD_HELFRICH_SD64_RAM,
285 .driver_data = BT_SD64,
286 }, {
287 .id = ZORRO_PROD_HELFRICH_PICCOLO_RAM,
288 .driver_data = BT_PICCOLO,
289 }, {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700290 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_RAM,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 .driver_data = BT_PICASSO,
292 }, {
293 .id = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_RAM,
294 .driver_data = BT_SPECTRUM,
295 }, {
296 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z3,
297 .driver_data = BT_PICASSO4,
298 },
299 { 0 }
300};
301
302static const struct {
303 zorro_id id2;
304 unsigned long size;
305} cirrusfb_zorro_table2[] = {
306 [BT_SD64] = {
307 .id2 = ZORRO_PROD_HELFRICH_SD64_REG,
308 .size = 0x400000
309 },
310 [BT_PICCOLO] = {
311 .id2 = ZORRO_PROD_HELFRICH_PICCOLO_REG,
312 .size = 0x200000
313 },
314 [BT_PICASSO] = {
315 .id2 = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_REG,
316 .size = 0x200000
317 },
318 [BT_SPECTRUM] = {
319 .id2 = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_REG,
320 .size = 0x200000
321 },
322 [BT_PICASSO4] = {
323 .id2 = 0,
324 .size = 0x400000
325 }
326};
327#endif /* CONFIG_ZORRO */
328
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329struct cirrusfb_regs {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330 long multiplexing;
331 long mclk;
332 long divMCLK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333};
334
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335#ifdef CIRRUSFB_DEBUG
Krzysztof Helt7345de32007-10-16 01:29:11 -0700336enum cirrusfb_dbg_reg_class {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700337 CRT,
338 SEQ
Krzysztof Helt7345de32007-10-16 01:29:11 -0700339};
Krzysztof Helt8503df62007-10-16 01:29:08 -0700340#endif /* CIRRUSFB_DEBUG */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341
342/* info about board */
343struct cirrusfb_info {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 u8 __iomem *regbase;
Krzysztof Helt7345de32007-10-16 01:29:11 -0700345 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346 unsigned char SFR; /* Shadow of special function register */
347
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348 struct cirrusfb_regs currentmode;
349 int blank_mode;
Krzysztof Helt64beab12008-10-15 22:03:38 -0700350 u32 pseudo_palette[16];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700352 void (*unmap)(struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353};
354
Krzysztof Helt8503df62007-10-16 01:29:08 -0700355static int noaccel;
Krzysztof Helta1d35a72008-10-15 22:03:38 -0700356static char *mode_option __devinitdata = "640x480@60";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357
358/****************************************************************************/
359/**** BEGIN PROTOTYPES ******************************************************/
360
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361/*--- Interface used by the world ------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700362static int cirrusfb_init(void);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363#ifndef MODULE
Krzysztof Helt8503df62007-10-16 01:29:08 -0700364static int cirrusfb_setup(char *options);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365#endif
366
Krzysztof Helt8503df62007-10-16 01:29:08 -0700367static int cirrusfb_open(struct fb_info *info, int user);
368static int cirrusfb_release(struct fb_info *info, int user);
369static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
370 unsigned blue, unsigned transp,
371 struct fb_info *info);
372static int cirrusfb_check_var(struct fb_var_screeninfo *var,
373 struct fb_info *info);
374static int cirrusfb_set_par(struct fb_info *info);
375static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
376 struct fb_info *info);
377static int cirrusfb_blank(int blank_mode, struct fb_info *info);
378static void cirrusfb_fillrect(struct fb_info *info,
379 const struct fb_fillrect *region);
380static void cirrusfb_copyarea(struct fb_info *info,
381 const struct fb_copyarea *area);
382static void cirrusfb_imageblit(struct fb_info *info,
383 const struct fb_image *image);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384
385/* function table of the above functions */
386static struct fb_ops cirrusfb_ops = {
387 .owner = THIS_MODULE,
388 .fb_open = cirrusfb_open,
389 .fb_release = cirrusfb_release,
390 .fb_setcolreg = cirrusfb_setcolreg,
391 .fb_check_var = cirrusfb_check_var,
392 .fb_set_par = cirrusfb_set_par,
393 .fb_pan_display = cirrusfb_pan_display,
394 .fb_blank = cirrusfb_blank,
395 .fb_fillrect = cirrusfb_fillrect,
396 .fb_copyarea = cirrusfb_copyarea,
397 .fb_imageblit = cirrusfb_imageblit,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398};
399
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400/*--- Internal routines ----------------------------------------------------*/
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700401static void init_vgachip(struct fb_info *info);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700402static void switch_monitor(struct cirrusfb_info *cinfo, int on);
403static void WGen(const struct cirrusfb_info *cinfo,
404 int regnum, unsigned char val);
405static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum);
406static void AttrOn(const struct cirrusfb_info *cinfo);
407static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val);
408static void WSFR(struct cirrusfb_info *cinfo, unsigned char val);
409static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val);
410static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum,
411 unsigned char red, unsigned char green, unsigned char blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412#if 0
Krzysztof Helt8503df62007-10-16 01:29:08 -0700413static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum,
414 unsigned char *red, unsigned char *green,
415 unsigned char *blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -0700417static void cirrusfb_WaitBLT(u8 __iomem *regbase);
418static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
419 u_short curx, u_short cury,
420 u_short destx, u_short desty,
421 u_short width, u_short height,
422 u_short line_length);
423static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
424 u_short x, u_short y,
425 u_short width, u_short height,
426 u_char color, u_short line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700427
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700428static void bestclock(long freq, int *nom, int *den, int *div);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429
430#ifdef CIRRUSFB_DEBUG
Krzysztof Helt8503df62007-10-16 01:29:08 -0700431static void cirrusfb_dump(void);
432static void cirrusfb_dbg_reg_dump(caddr_t regbase);
433static void cirrusfb_dbg_print_regs(caddr_t regbase,
Krzysztof Helt7345de32007-10-16 01:29:11 -0700434 enum cirrusfb_dbg_reg_class reg_class, ...);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700435static void cirrusfb_dbg_print_byte(const char *name, unsigned char val);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436#endif /* CIRRUSFB_DEBUG */
437
438/*** END PROTOTYPES ********************************************************/
439/*****************************************************************************/
440/*** BEGIN Interface Used by the World ***************************************/
441
Krzysztof Helt8503df62007-10-16 01:29:08 -0700442static int opencount;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443
444/*--- Open /dev/fbx ---------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700445static int cirrusfb_open(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446{
447 if (opencount++ == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700448 switch_monitor(info->par, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449 return 0;
450}
451
452/*--- Close /dev/fbx --------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700453static int cirrusfb_release(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454{
455 if (--opencount == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700456 switch_monitor(info->par, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457 return 0;
458}
459
460/**** END Interface used by the World *************************************/
461/****************************************************************************/
462/**** BEGIN Hardware specific Routines **************************************/
463
464/* Get a good MCLK value */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700465static long cirrusfb_get_mclk(long freq, int bpp, long *div)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466{
467 long mclk;
468
Krzysztof Helt8503df62007-10-16 01:29:08 -0700469 assert(div != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470
471 /* Calculate MCLK, in case VCLK is high enough to require > 50MHz.
472 * Assume a 64-bit data path for now. The formula is:
473 * ((B * PCLK * 2)/W) * 1.2
474 * B = bytes per pixel, PCLK = pixclock, W = data width in bytes */
475 mclk = ((bpp / 8) * freq * 2) / 4;
476 mclk = (mclk * 12) / 10;
477 if (mclk < 50000)
478 mclk = 50000;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700479 DPRINTK("Use MCLK of %ld kHz\n", mclk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480
481 /* Calculate value for SR1F. Multiply by 2 so we can round up. */
482 mclk = ((mclk * 16) / 14318);
483 mclk = (mclk + 1) / 2;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700484 DPRINTK("Set SR1F[5:0] to 0x%lx\n", mclk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485
486 /* Determine if we should use MCLK instead of VCLK, and if so, what we
487 * should divide it by to get VCLK */
488 switch (freq) {
489 case 24751 ... 25249:
490 *div = 2;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700491 DPRINTK("Using VCLK = MCLK/2\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492 break;
493 case 49501 ... 50499:
494 *div = 1;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700495 DPRINTK("Using VCLK = MCLK\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496 break;
497 default:
498 *div = 0;
499 break;
500 }
501
502 return mclk;
503}
504
505static int cirrusfb_check_var(struct fb_var_screeninfo *var,
506 struct fb_info *info)
507{
Krzysztof Helt09a29102008-09-02 14:35:51 -0700508 int yres;
509 /* memory size in pixels */
510 unsigned pixels = info->screen_size * 8 / var->bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511
512 switch (var->bits_per_pixel) {
Krzysztof Helt060b6002007-10-16 01:29:13 -0700513 case 1:
Krzysztof Helt09a29102008-09-02 14:35:51 -0700514 pixels /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515 break; /* 8 pixel per byte, only 1/4th of mem usable */
Krzysztof Helt060b6002007-10-16 01:29:13 -0700516 case 8:
517 case 16:
Krzysztof Helt060b6002007-10-16 01:29:13 -0700518 case 32:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519 break; /* 1 pixel == 1 byte */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700521 printk(KERN_ERR "cirrusfb: mode %dx%dx%d rejected..."
522 "color depth not supported.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700524 DPRINTK("EXIT - EINVAL error\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525 return -EINVAL;
526 }
527
Krzysztof Helt09a29102008-09-02 14:35:51 -0700528 if (var->xres_virtual < var->xres)
529 var->xres_virtual = var->xres;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530 /* use highest possible virtual resolution */
Krzysztof Helt09a29102008-09-02 14:35:51 -0700531 if (var->yres_virtual == -1) {
532 var->yres_virtual = pixels / var->xres_virtual;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533
Krzysztof Helt8503df62007-10-16 01:29:08 -0700534 printk(KERN_INFO "cirrusfb: virtual resolution set to "
535 "maximum of %dx%d\n", var->xres_virtual,
536 var->yres_virtual);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 if (var->yres_virtual < var->yres)
539 var->yres_virtual = var->yres;
540
Krzysztof Helt09a29102008-09-02 14:35:51 -0700541 if (var->xres_virtual * var->yres_virtual > pixels) {
542 printk(KERN_ERR "cirrusfb: mode %dx%dx%d rejected... "
543 "virtual resolution too high to fit into video memory!\n",
544 var->xres_virtual, var->yres_virtual,
545 var->bits_per_pixel);
546 DPRINTK("EXIT - EINVAL error\n");
547 return -EINVAL;
548 }
549
550
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551 if (var->xoffset < 0)
552 var->xoffset = 0;
553 if (var->yoffset < 0)
554 var->yoffset = 0;
555
556 /* truncate xoffset and yoffset to maximum if too high */
557 if (var->xoffset > var->xres_virtual - var->xres)
558 var->xoffset = var->xres_virtual - var->xres - 1;
559 if (var->yoffset > var->yres_virtual - var->yres)
560 var->yoffset = var->yres_virtual - var->yres - 1;
561
562 switch (var->bits_per_pixel) {
563 case 1:
564 var->red.offset = 0;
565 var->red.length = 1;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700566 var->green = var->red;
567 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568 break;
569
570 case 8:
571 var->red.offset = 0;
572 var->red.length = 6;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700573 var->green = var->red;
574 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 break;
576
577 case 16:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700578 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 var->red.offset = 2;
580 var->green.offset = -3;
581 var->blue.offset = 8;
582 } else {
583 var->red.offset = 10;
584 var->green.offset = 5;
585 var->blue.offset = 0;
586 }
587 var->red.length = 5;
588 var->green.length = 5;
589 var->blue.length = 5;
590 break;
591
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592 case 32:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700593 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594 var->red.offset = 8;
595 var->green.offset = 16;
596 var->blue.offset = 24;
597 } else {
598 var->red.offset = 16;
599 var->green.offset = 8;
600 var->blue.offset = 0;
601 }
602 var->red.length = 8;
603 var->green.length = 8;
604 var->blue.length = 8;
605 break;
606
607 default:
608 DPRINTK("Unsupported bpp size: %d\n", var->bits_per_pixel);
Richard Knutssonc930faa2007-05-08 00:38:29 -0700609 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610 /* should never occur */
611 break;
612 }
613
614 var->red.msb_right =
615 var->green.msb_right =
616 var->blue.msb_right =
617 var->transp.offset =
618 var->transp.length =
619 var->transp.msb_right = 0;
620
621 yres = var->yres;
622 if (var->vmode & FB_VMODE_DOUBLE)
623 yres *= 2;
624 else if (var->vmode & FB_VMODE_INTERLACED)
625 yres = (yres + 1) / 2;
626
627 if (yres >= 1280) {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700628 printk(KERN_ERR "cirrusfb: ERROR: VerticalTotal >= 1280; "
629 "special treatment required! (TODO)\n");
630 DPRINTK("EXIT - EINVAL error\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631 return -EINVAL;
632 }
633
634 return 0;
635}
636
Krzysztof Helt8503df62007-10-16 01:29:08 -0700637static int cirrusfb_decode_var(const struct fb_var_screeninfo *var,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638 struct cirrusfb_regs *regs,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700639 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640{
641 long freq;
642 long maxclock;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700643 int maxclockidx = var->bits_per_pixel >> 3;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645
Krzysztof Helt8503df62007-10-16 01:29:08 -0700646 switch (var->bits_per_pixel) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700647 case 1:
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700648 info->fix.line_length = var->xres_virtual / 8;
649 info->fix.visual = FB_VISUAL_MONO10;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650 break;
651
652 case 8:
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700653 info->fix.line_length = var->xres_virtual;
654 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655 break;
656
657 case 16:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 case 32:
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700659 info->fix.line_length = var->xres_virtual * maxclockidx;
660 info->fix.visual = FB_VISUAL_DIRECTCOLOR;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661 break;
662
663 default:
664 DPRINTK("Unsupported bpp size: %d\n", var->bits_per_pixel);
Richard Knutssonc930faa2007-05-08 00:38:29 -0700665 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700666 /* should never occur */
667 break;
668 }
669
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -0700670 info->fix.type = FB_TYPE_PACKED_PIXELS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671
672 /* convert from ps to kHz */
Krzysztof Helt060b6002007-10-16 01:29:13 -0700673 freq = PICOS2KHZ(var->pixclock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674
Krzysztof Helt8503df62007-10-16 01:29:08 -0700675 DPRINTK("desired pixclock: %ld kHz\n", freq);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676
677 maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx];
678 regs->multiplexing = 0;
679
680 /* If the frequency is greater than we can support, we might be able
681 * to use multiplexing for the video mode */
682 if (freq > maxclock) {
683 switch (cinfo->btype) {
684 case BT_ALPINE:
685 case BT_GD5480:
686 regs->multiplexing = 1;
687 break;
688
689 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700690 printk(KERN_ERR "cirrusfb: Frequency greater "
691 "than maxclock (%ld kHz)\n", maxclock);
692 DPRINTK("EXIT - return -EINVAL\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693 return -EINVAL;
694 }
695 }
696#if 0
697 /* TODO: If we have a 1MB 5434, we need to put ourselves in a mode where
698 * the VCLK is double the pixel clock. */
699 switch (var->bits_per_pixel) {
700 case 16:
701 case 32:
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700702 if (var->xres <= 800)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700703 /* Xbh has this type of clock for 32-bit */
704 freq /= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705 break;
706 }
707#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -0700708 regs->mclk = cirrusfb_get_mclk(freq, var->bits_per_pixel,
709 &regs->divMCLK);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710
Linus Torvalds1da177e2005-04-16 15:20:36 -0700711 return 0;
712}
713
Krzysztof Helt8503df62007-10-16 01:29:08 -0700714static void cirrusfb_set_mclk(const struct cirrusfb_info *cinfo, int val,
715 int div)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716{
Krzysztof Helt8503df62007-10-16 01:29:08 -0700717 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718
719 if (div == 2) {
720 /* VCLK = MCLK/2 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700721 unsigned char old = vga_rseq(cinfo->regbase, CL_SEQR1E);
722 vga_wseq(cinfo->regbase, CL_SEQR1E, old | 0x1);
723 vga_wseq(cinfo->regbase, CL_SEQR1F, 0x40 | (val & 0x3f));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 } else if (div == 1) {
725 /* VCLK = MCLK */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700726 unsigned char old = vga_rseq(cinfo->regbase, CL_SEQR1E);
727 vga_wseq(cinfo->regbase, CL_SEQR1E, old & ~0x1);
728 vga_wseq(cinfo->regbase, CL_SEQR1F, 0x40 | (val & 0x3f));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700730 vga_wseq(cinfo->regbase, CL_SEQR1F, val & 0x3f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700731 }
732}
733
734/*************************************************************************
735 cirrusfb_set_par_foo()
736
737 actually writes the values for a new video mode into the hardware,
738**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700739static int cirrusfb_set_par_foo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740{
741 struct cirrusfb_info *cinfo = info->par;
742 struct fb_var_screeninfo *var = &info->var;
743 struct cirrusfb_regs regs;
744 u8 __iomem *regbase = cinfo->regbase;
745 unsigned char tmp;
746 int offset = 0, err;
747 const struct cirrusfb_board_info_rec *bi;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700748 int hdispend, hsyncstart, hsyncend, htotal;
749 int yres, vdispend, vsyncstart, vsyncend, vtotal;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700750 long freq;
751 int nom, den, div;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752
Krzysztof Helt8503df62007-10-16 01:29:08 -0700753 DPRINTK("ENTER\n");
754 DPRINTK("Requested mode: %dx%dx%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700756 DPRINTK("pixclock: %d\n", var->pixclock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700758 init_vgachip(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759
760 err = cirrusfb_decode_var(var, &regs, info);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700761 if (err) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762 /* should never happen */
763 DPRINTK("mode change aborted. invalid var.\n");
764 return -EINVAL;
765 }
766
767 bi = &cirrusfb_board_info[cinfo->btype];
768
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700769 hsyncstart = var->xres + var->right_margin;
770 hsyncend = hsyncstart + var->hsync_len;
771 htotal = (hsyncend + var->left_margin) / 8 - 5;
772 hdispend = var->xres / 8 - 1;
773 hsyncstart = hsyncstart / 8 + 1;
774 hsyncend = hsyncend / 8 + 1;
775
776 yres = var->yres;
777 vsyncstart = yres + var->lower_margin;
778 vsyncend = vsyncstart + var->vsync_len;
779 vtotal = vsyncend + var->upper_margin;
780 vdispend = yres - 1;
781
782 if (var->vmode & FB_VMODE_DOUBLE) {
783 yres *= 2;
784 vsyncstart *= 2;
785 vsyncend *= 2;
786 vtotal *= 2;
787 } else if (var->vmode & FB_VMODE_INTERLACED) {
788 yres = (yres + 1) / 2;
789 vsyncstart = (vsyncstart + 1) / 2;
790 vsyncend = (vsyncend + 1) / 2;
791 vtotal = (vtotal + 1) / 2;
792 }
793
794 vtotal -= 2;
795 vsyncstart -= 1;
796 vsyncend -= 1;
797
798 if (yres >= 1024) {
799 vtotal /= 2;
800 vsyncstart /= 2;
801 vsyncend /= 2;
802 vdispend /= 2;
803 }
804 if (regs.multiplexing) {
805 htotal /= 2;
806 hsyncstart /= 2;
807 hsyncend /= 2;
808 hdispend /= 2;
809 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810 /* unlock register VGA_CRTC_H_TOTAL..CRT7 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700811 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20); /* previously: 0x00) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812
813 /* if debugging is enabled, all parameters get output before writing */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700814 DPRINTK("CRT0: %d\n", htotal);
815 vga_wcrt(regbase, VGA_CRTC_H_TOTAL, htotal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700817 DPRINTK("CRT1: %d\n", hdispend);
818 vga_wcrt(regbase, VGA_CRTC_H_DISP, hdispend);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700820 DPRINTK("CRT2: %d\n", var->xres / 8);
821 vga_wcrt(regbase, VGA_CRTC_H_BLANK_START, var->xres / 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822
Krzysztof Helt8503df62007-10-16 01:29:08 -0700823 /* + 128: Compatible read */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700824 DPRINTK("CRT3: 128+%d\n", (htotal + 5) % 32);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700825 vga_wcrt(regbase, VGA_CRTC_H_BLANK_END,
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700826 128 + ((htotal + 5) % 32));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700828 DPRINTK("CRT4: %d\n", hsyncstart);
829 vga_wcrt(regbase, VGA_CRTC_H_SYNC_START, hsyncstart);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700831 tmp = hsyncend % 32;
832 if ((htotal + 5) & 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833 tmp += 128;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700834 DPRINTK("CRT5: %d\n", tmp);
835 vga_wcrt(regbase, VGA_CRTC_H_SYNC_END, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700836
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700837 DPRINTK("CRT6: %d\n", vtotal & 0xff);
838 vga_wcrt(regbase, VGA_CRTC_V_TOTAL, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839
840 tmp = 16; /* LineCompare bit #9 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700841 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700843 if (vdispend & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844 tmp |= 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700845 if (vsyncstart & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700846 tmp |= 4;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700847 if ((vdispend + 1) & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848 tmp |= 8;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700849 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700850 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700851 if (vdispend & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700853 if (vsyncstart & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854 tmp |= 128;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700855 DPRINTK("CRT7: %d\n", tmp);
856 vga_wcrt(regbase, VGA_CRTC_OVERFLOW, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857
858 tmp = 0x40; /* LineCompare bit #8 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700859 if ((vdispend + 1) & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860 tmp |= 0x20;
861 if (var->vmode & FB_VMODE_DOUBLE)
862 tmp |= 0x80;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700863 DPRINTK("CRT9: %d\n", tmp);
864 vga_wcrt(regbase, VGA_CRTC_MAX_SCAN, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700865
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700866 DPRINTK("CRT10: %d\n", vsyncstart & 0xff);
867 vga_wcrt(regbase, VGA_CRTC_V_SYNC_START, vsyncstart & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700869 DPRINTK("CRT11: 64+32+%d\n", vsyncend % 16);
870 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, vsyncend % 16 + 64 + 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700871
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700872 DPRINTK("CRT12: %d\n", vdispend & 0xff);
873 vga_wcrt(regbase, VGA_CRTC_V_DISP_END, vdispend & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700875 DPRINTK("CRT15: %d\n", (vdispend + 1) & 0xff);
876 vga_wcrt(regbase, VGA_CRTC_V_BLANK_START, (vdispend + 1) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700878 DPRINTK("CRT16: %d\n", vtotal & 0xff);
879 vga_wcrt(regbase, VGA_CRTC_V_BLANK_END, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880
Krzysztof Helt8503df62007-10-16 01:29:08 -0700881 DPRINTK("CRT18: 0xff\n");
882 vga_wcrt(regbase, VGA_CRTC_LINE_COMPARE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883
884 tmp = 0;
885 if (var->vmode & FB_VMODE_INTERLACED)
886 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700887 if ((htotal + 5) & 64)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888 tmp |= 16;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700889 if ((htotal + 5) & 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700890 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700891 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700893 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894 tmp |= 128;
895
Krzysztof Helt8503df62007-10-16 01:29:08 -0700896 DPRINTK("CRT1a: %d\n", tmp);
897 vga_wcrt(regbase, CL_CRT1A, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700899 freq = PICOS2KHZ(var->pixclock);
900 bestclock(freq, &nom, &den, &div);
901
Linus Torvalds1da177e2005-04-16 15:20:36 -0700902 /* set VCLK0 */
903 /* hardware RefClock: 14.31818 MHz */
904 /* formula: VClk = (OSC * N) / (D * (1+P)) */
905 /* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */
906
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700907 vga_wseq(regbase, CL_SEQRB, nom);
908 tmp = den << 1;
909 if (div != 0)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910 tmp |= 1;
911
Krzysztof Helt8503df62007-10-16 01:29:08 -0700912 /* 6 bit denom; ONLY 5434!!! (bugged me 10 days) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913 if ((cinfo->btype == BT_SD64) ||
914 (cinfo->btype == BT_ALPINE) ||
915 (cinfo->btype == BT_GD5480))
Krzysztof Helt8503df62007-10-16 01:29:08 -0700916 tmp |= 0x80;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917
Krzysztof Helt8503df62007-10-16 01:29:08 -0700918 DPRINTK("CL_SEQR1B: %ld\n", (long) tmp);
919 vga_wseq(regbase, CL_SEQR1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700921 if (yres >= 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922 /* 1280x1024 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700923 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924 else
925 /* mode control: VGA_CRTC_START_HI enable, ROTATE(?), 16bit
926 * address wrap, no compat. */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700927 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700928
Krzysztof Helt8503df62007-10-16 01:29:08 -0700929/* HAEH? vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20);
930 * previously: 0x00 unlock VGA_CRTC_H_TOTAL..CRT7 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931
932 /* don't know if it would hurt to also program this if no interlaced */
933 /* mode is used, but I feel better this way.. :-) */
934 if (var->vmode & FB_VMODE_INTERLACED)
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700935 vga_wcrt(regbase, VGA_CRTC_REGS, htotal / 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936 else
Krzysztof Helt8503df62007-10-16 01:29:08 -0700937 vga_wcrt(regbase, VGA_CRTC_REGS, 0x00); /* interlace control */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938
Krzysztof Helt8503df62007-10-16 01:29:08 -0700939 vga_wseq(regbase, VGA_SEQ_CHARACTER_MAP, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940
941 /* adjust horizontal/vertical sync type (low/high) */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700942 /* enable display memory & CRTC I/O address for color mode */
943 tmp = 0x03;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
945 tmp |= 0x40;
946 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
947 tmp |= 0x80;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700948 WGen(cinfo, VGA_MIS_W, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949
Krzysztof Helt8503df62007-10-16 01:29:08 -0700950 /* Screen A Preset Row-Scan register */
951 vga_wcrt(regbase, VGA_CRTC_PRESET_ROW, 0);
952 /* text cursor on and start line */
953 vga_wcrt(regbase, VGA_CRTC_CURSOR_START, 0);
954 /* text cursor end line */
955 vga_wcrt(regbase, VGA_CRTC_CURSOR_END, 31);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700956
957 /******************************************************
958 *
959 * 1 bpp
960 *
961 */
962
963 /* programming for different color depths */
964 if (var->bits_per_pixel == 1) {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700965 DPRINTK("cirrusfb: preparing for 1 bit deep display\n");
966 vga_wgfx(regbase, VGA_GFX_MODE, 0); /* mode register */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967
968 /* SR07 */
969 switch (cinfo->btype) {
970 case BT_SD64:
971 case BT_PICCOLO:
972 case BT_PICASSO:
973 case BT_SPECTRUM:
974 case BT_PICASSO4:
975 case BT_ALPINE:
976 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700977 DPRINTK(" (for GD54xx)\n");
978 vga_wseq(regbase, CL_SEQR7,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979 regs.multiplexing ?
980 bi->sr07_1bpp_mux : bi->sr07_1bpp);
981 break;
982
983 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700984 DPRINTK(" (for GD546x)\n");
985 vga_wseq(regbase, CL_SEQR7,
986 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987 break;
988
989 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700990 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991 break;
992 }
993
994 /* Extended Sequencer Mode */
995 switch (cinfo->btype) {
996 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700997 /* setting the SEQRF on SD64 is not necessary
998 * (only during init)
999 */
1000 DPRINTK("(for SD64)\n");
1001 /* MCLK select */
1002 vga_wseq(regbase, CL_SEQR1F, 0x1a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001003 break;
1004
1005 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001006 case BT_SPECTRUM:
1007 DPRINTK("(for Piccolo/Spectrum)\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -07001008 /* ### ueberall 0x22? */
1009 /* ##vorher 1c MCLK select */
1010 vga_wseq(regbase, CL_SEQR1F, 0x22);
1011 /* evtl d0 bei 1 bit? avoid FIFO underruns..? */
1012 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001013 break;
1014
1015 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001016 DPRINTK("(for Picasso)\n");
1017 /* ##vorher 22 MCLK select */
1018 vga_wseq(regbase, CL_SEQR1F, 0x22);
1019 /* ## vorher d0 avoid FIFO underruns..? */
1020 vga_wseq(regbase, CL_SEQRF, 0xd0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001021 break;
1022
Linus Torvalds1da177e2005-04-16 15:20:36 -07001023 case BT_PICASSO4:
1024 case BT_ALPINE:
1025 case BT_GD5480:
1026 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001027 DPRINTK(" (for GD54xx)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028 /* do nothing */
1029 break;
1030
1031 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001032 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033 break;
1034 }
1035
Krzysztof Helt8503df62007-10-16 01:29:08 -07001036 /* pixel mask: pass-through for first plane */
1037 WGen(cinfo, VGA_PEL_MSK, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001038 if (regs.multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001039 /* hidden dac reg: 1280x1024 */
1040 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001041 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001042 /* hidden dac: nothing */
1043 WHDR(cinfo, 0);
1044 /* memory mode: odd/even, ext. memory */
1045 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x06);
1046 /* plane mask: only write to first plane */
1047 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048 offset = var->xres_virtual / 16;
1049 }
1050
1051 /******************************************************
1052 *
1053 * 8 bpp
1054 *
1055 */
1056
1057 else if (var->bits_per_pixel == 8) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001058 DPRINTK("cirrusfb: preparing for 8 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059 switch (cinfo->btype) {
1060 case BT_SD64:
1061 case BT_PICCOLO:
1062 case BT_PICASSO:
1063 case BT_SPECTRUM:
1064 case BT_PICASSO4:
1065 case BT_ALPINE:
1066 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001067 DPRINTK(" (for GD54xx)\n");
1068 vga_wseq(regbase, CL_SEQR7,
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 regs.multiplexing ?
1070 bi->sr07_8bpp_mux : bi->sr07_8bpp);
1071 break;
1072
1073 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001074 DPRINTK(" (for GD546x)\n");
1075 vga_wseq(regbase, CL_SEQR7,
1076 vga_rseq(regbase, CL_SEQR7) | 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001077 break;
1078
1079 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001080 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081 break;
1082 }
1083
1084 switch (cinfo->btype) {
1085 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001086 /* MCLK select */
1087 vga_wseq(regbase, CL_SEQR1F, 0x1d);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088 break;
1089
1090 case BT_PICCOLO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091 case BT_PICASSO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001093 /* ### vorher 1c MCLK select */
1094 vga_wseq(regbase, CL_SEQR1F, 0x22);
1095 /* Fast Page-Mode writes */
1096 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001097 break;
1098
1099 case BT_PICASSO4:
1100#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001101 /* ### INCOMPLETE!! */
1102 vga_wseq(regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001103#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07001104/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105 break;
1106
1107 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001108 DPRINTK(" (for GD543x)\n");
1109 cirrusfb_set_mclk(cinfo, regs.mclk, regs.divMCLK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001110 /* We already set SRF and SR1F */
1111 break;
1112
1113 case BT_GD5480:
1114 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001115 DPRINTK(" (for GD54xx)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001116 /* do nothing */
1117 break;
1118
1119 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001120 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001121 break;
1122 }
1123
Krzysztof Helt8503df62007-10-16 01:29:08 -07001124 /* mode register: 256 color mode */
1125 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1126 /* pixel mask: pass-through all planes */
1127 WGen(cinfo, VGA_PEL_MSK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128 if (regs.multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001129 /* hidden dac reg: 1280x1024 */
1130 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001132 /* hidden dac: nothing */
1133 WHDR(cinfo, 0);
1134 /* memory mode: chain4, ext. memory */
1135 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1136 /* plane mask: enable writing to all 4 planes */
1137 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138 offset = var->xres_virtual / 8;
1139 }
1140
1141 /******************************************************
1142 *
1143 * 16 bpp
1144 *
1145 */
1146
1147 else if (var->bits_per_pixel == 16) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001148 DPRINTK("cirrusfb: preparing for 16 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001149 switch (cinfo->btype) {
1150 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001151 /* Extended Sequencer Mode: 256c col. mode */
1152 vga_wseq(regbase, CL_SEQR7, 0xf7);
1153 /* MCLK select */
1154 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001155 break;
1156
1157 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001158 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001159 vga_wseq(regbase, CL_SEQR7, 0x87);
1160 /* Fast Page-Mode writes */
1161 vga_wseq(regbase, CL_SEQRF, 0xb0);
1162 /* MCLK select */
1163 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001164 break;
1165
1166 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001167 vga_wseq(regbase, CL_SEQR7, 0x27);
1168 /* Fast Page-Mode writes */
1169 vga_wseq(regbase, CL_SEQRF, 0xb0);
1170 /* MCLK select */
1171 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001172 break;
1173
Linus Torvalds1da177e2005-04-16 15:20:36 -07001174 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001175 vga_wseq(regbase, CL_SEQR7, 0x27);
1176/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177 break;
1178
1179 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001180 DPRINTK(" (for GD543x)\n");
Krzysztof Helt9a85cf52008-10-15 22:03:39 -07001181 if (var->xres >= 1024)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001182 vga_wseq(regbase, CL_SEQR7, 0xa7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001183 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001184 vga_wseq(regbase, CL_SEQR7, 0xa3);
1185 cirrusfb_set_mclk(cinfo, regs.mclk, regs.divMCLK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001186 break;
1187
1188 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001189 DPRINTK(" (for GD5480)\n");
1190 vga_wseq(regbase, CL_SEQR7, 0x17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001191 /* We already set SRF and SR1F */
1192 break;
1193
1194 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001195 DPRINTK(" (for GD546x)\n");
1196 vga_wseq(regbase, CL_SEQR7,
1197 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198 break;
1199
1200 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001201 printk(KERN_WARNING "CIRRUSFB: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001202 break;
1203 }
1204
Krzysztof Helt8503df62007-10-16 01:29:08 -07001205 /* mode register: 256 color mode */
1206 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1207 /* pixel mask: pass-through all planes */
1208 WGen(cinfo, VGA_PEL_MSK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001210 WHDR(cinfo, 0xc0); /* Copy Xbh */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001211#elif defined(CONFIG_ZORRO)
1212 /* FIXME: CONFIG_PCI and CONFIG_ZORRO may be defined both */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001213 WHDR(cinfo, 0xa0); /* hidden dac reg: nothing special */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001214#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07001215 /* memory mode: chain4, ext. memory */
1216 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1217 /* plane mask: enable writing to all 4 planes */
1218 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001219 offset = var->xres_virtual / 4;
1220 }
1221
1222 /******************************************************
1223 *
1224 * 32 bpp
1225 *
1226 */
1227
1228 else if (var->bits_per_pixel == 32) {
Krzysztof Helt1cea9a92008-10-15 22:03:37 -07001229 DPRINTK("cirrusfb: preparing for 32 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001230 switch (cinfo->btype) {
1231 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001232 /* Extended Sequencer Mode: 256c col. mode */
1233 vga_wseq(regbase, CL_SEQR7, 0xf9);
1234 /* MCLK select */
1235 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001236 break;
1237
1238 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001239 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001240 vga_wseq(regbase, CL_SEQR7, 0x85);
1241 /* Fast Page-Mode writes */
1242 vga_wseq(regbase, CL_SEQRF, 0xb0);
1243 /* MCLK select */
1244 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001245 break;
1246
1247 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001248 vga_wseq(regbase, CL_SEQR7, 0x25);
1249 /* Fast Page-Mode writes */
1250 vga_wseq(regbase, CL_SEQRF, 0xb0);
1251 /* MCLK select */
1252 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001253 break;
1254
Linus Torvalds1da177e2005-04-16 15:20:36 -07001255 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001256 vga_wseq(regbase, CL_SEQR7, 0x25);
1257/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001258 break;
1259
1260 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001261 DPRINTK(" (for GD543x)\n");
1262 vga_wseq(regbase, CL_SEQR7, 0xa9);
1263 cirrusfb_set_mclk(cinfo, regs.mclk, regs.divMCLK);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001264 break;
1265
1266 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001267 DPRINTK(" (for GD5480)\n");
1268 vga_wseq(regbase, CL_SEQR7, 0x19);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001269 /* We already set SRF and SR1F */
1270 break;
1271
1272 case BT_LAGUNA:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001273 DPRINTK(" (for GD546x)\n");
1274 vga_wseq(regbase, CL_SEQR7,
1275 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001276 break;
1277
1278 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001279 printk(KERN_WARNING "cirrusfb: unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280 break;
1281 }
1282
Krzysztof Helt8503df62007-10-16 01:29:08 -07001283 /* mode register: 256 color mode */
1284 vga_wgfx(regbase, VGA_GFX_MODE, 64);
1285 /* pixel mask: pass-through all planes */
1286 WGen(cinfo, VGA_PEL_MSK, 0xff);
1287 /* hidden dac reg: 8-8-8 mode (24 or 32) */
1288 WHDR(cinfo, 0xc5);
1289 /* memory mode: chain4, ext. memory */
1290 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
1291 /* plane mask: enable writing to all 4 planes */
1292 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001293 offset = var->xres_virtual / 4;
1294 }
1295
1296 /******************************************************
1297 *
1298 * unknown/unsupported bpp
1299 *
1300 */
1301
Krzysztof Helt8503df62007-10-16 01:29:08 -07001302 else
1303 printk(KERN_ERR "cirrusfb: What's this?? "
1304 " requested color depth == %d.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001305 var->bits_per_pixel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001306
Krzysztof Helt8503df62007-10-16 01:29:08 -07001307 vga_wcrt(regbase, VGA_CRTC_OFFSET, offset & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001308 tmp = 0x22;
1309 if (offset & 0x100)
1310 tmp |= 0x10; /* offset overflow bit */
1311
Krzysztof Helt8503df62007-10-16 01:29:08 -07001312 /* screen start addr #16-18, fastpagemode cycles */
1313 vga_wcrt(regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001314
1315 if (cinfo->btype == BT_SD64 ||
1316 cinfo->btype == BT_PICASSO4 ||
1317 cinfo->btype == BT_ALPINE ||
1318 cinfo->btype == BT_GD5480)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001319 /* screen start address bit 19 */
1320 vga_wcrt(regbase, CL_CRT1D, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001321
Krzysztof Helt8503df62007-10-16 01:29:08 -07001322 /* text cursor location high */
1323 vga_wcrt(regbase, VGA_CRTC_CURSOR_HI, 0);
1324 /* text cursor location low */
1325 vga_wcrt(regbase, VGA_CRTC_CURSOR_LO, 0);
1326 /* underline row scanline = at very bottom */
1327 vga_wcrt(regbase, VGA_CRTC_UNDERLINE, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001328
Krzysztof Helt8503df62007-10-16 01:29:08 -07001329 /* controller mode */
1330 vga_wattr(regbase, VGA_ATC_MODE, 1);
1331 /* overscan (border) color */
1332 vga_wattr(regbase, VGA_ATC_OVERSCAN, 0);
1333 /* color plane enable */
1334 vga_wattr(regbase, VGA_ATC_PLANE_ENABLE, 15);
1335 /* pixel panning */
1336 vga_wattr(regbase, CL_AR33, 0);
1337 /* color select */
1338 vga_wattr(regbase, VGA_ATC_COLOR_PAGE, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339
1340 /* [ EGS: SetOffset(); ] */
1341 /* From SetOffset(): Turn on VideoEnable bit in Attribute controller */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001342 AttrOn(cinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001343
Krzysztof Helt8503df62007-10-16 01:29:08 -07001344 /* set/reset register */
1345 vga_wgfx(regbase, VGA_GFX_SR_VALUE, 0);
1346 /* set/reset enable */
1347 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, 0);
1348 /* color compare */
1349 vga_wgfx(regbase, VGA_GFX_COMPARE_VALUE, 0);
1350 /* data rotate */
1351 vga_wgfx(regbase, VGA_GFX_DATA_ROTATE, 0);
1352 /* read map select */
1353 vga_wgfx(regbase, VGA_GFX_PLANE_READ, 0);
1354 /* miscellaneous register */
1355 vga_wgfx(regbase, VGA_GFX_MISC, 1);
1356 /* color don't care */
1357 vga_wgfx(regbase, VGA_GFX_COMPARE_MASK, 15);
1358 /* bit mask */
1359 vga_wgfx(regbase, VGA_GFX_BIT_MASK, 255);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001360
Krzysztof Helt8503df62007-10-16 01:29:08 -07001361 /* graphics cursor attributes: nothing special */
1362 vga_wseq(regbase, CL_SEQR12, 0x0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363
1364 /* finally, turn on everything - turn off "FullBandwidth" bit */
1365 /* also, set "DotClock%2" bit where requested */
1366 tmp = 0x01;
1367
1368/*** FB_VMODE_CLOCK_HALVE in linux/fb.h not defined anymore ?
1369 if (var->vmode & FB_VMODE_CLOCK_HALVE)
1370 tmp |= 0x08;
1371*/
1372
Krzysztof Helt8503df62007-10-16 01:29:08 -07001373 vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, tmp);
1374 DPRINTK("CL_SEQR1: %d\n", tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001375
1376 cinfo->currentmode = regs;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001377
1378 /* pan to requested offset */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001379 cirrusfb_pan_display(var, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001380
1381#ifdef CIRRUSFB_DEBUG
Krzysztof Helt8503df62007-10-16 01:29:08 -07001382 cirrusfb_dump();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001383#endif
1384
Krzysztof Helt8503df62007-10-16 01:29:08 -07001385 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001386 return 0;
1387}
1388
1389/* for some reason incomprehensible to me, cirrusfb requires that you write
1390 * the registers twice for the settings to take..grr. -dte */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001391static int cirrusfb_set_par(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001392{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001393 cirrusfb_set_par_foo(info);
1394 return cirrusfb_set_par_foo(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001395}
1396
Krzysztof Helt8503df62007-10-16 01:29:08 -07001397static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
1398 unsigned blue, unsigned transp,
1399 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001400{
1401 struct cirrusfb_info *cinfo = info->par;
1402
1403 if (regno > 255)
1404 return -EINVAL;
1405
1406 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
1407 u32 v;
1408 red >>= (16 - info->var.red.length);
1409 green >>= (16 - info->var.green.length);
1410 blue >>= (16 - info->var.blue.length);
1411
Krzysztof Helt8503df62007-10-16 01:29:08 -07001412 if (regno >= 16)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001413 return 1;
1414 v = (red << info->var.red.offset) |
1415 (green << info->var.green.offset) |
1416 (blue << info->var.blue.offset);
1417
Krzysztof Helt060b6002007-10-16 01:29:13 -07001418 cinfo->pseudo_palette[regno] = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001419 return 0;
1420 }
1421
Krzysztof Helt8503df62007-10-16 01:29:08 -07001422 if (info->var.bits_per_pixel == 8)
1423 WClut(cinfo, regno, red >> 10, green >> 10, blue >> 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001424
1425 return 0;
1426
1427}
1428
1429/*************************************************************************
1430 cirrusfb_pan_display()
1431
1432 performs display panning - provided hardware permits this
1433**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07001434static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
1435 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001436{
1437 int xoffset = 0;
1438 int yoffset = 0;
1439 unsigned long base;
1440 unsigned char tmp = 0, tmp2 = 0, xpix;
1441 struct cirrusfb_info *cinfo = info->par;
1442
Krzysztof Helt8503df62007-10-16 01:29:08 -07001443 DPRINTK("ENTER\n");
1444 DPRINTK("virtual offset: (%d,%d)\n", var->xoffset, var->yoffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001445
1446 /* no range checks for xoffset and yoffset, */
1447 /* as fb_pan_display has already done this */
1448 if (var->vmode & FB_VMODE_YWRAP)
1449 return -EINVAL;
1450
1451 info->var.xoffset = var->xoffset;
1452 info->var.yoffset = var->yoffset;
1453
1454 xoffset = var->xoffset * info->var.bits_per_pixel / 8;
1455 yoffset = var->yoffset;
1456
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001457 base = yoffset * info->fix.line_length + xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001458
1459 if (info->var.bits_per_pixel == 1) {
1460 /* base is already correct */
1461 xpix = (unsigned char) (var->xoffset % 8);
1462 } else {
1463 base /= 4;
1464 xpix = (unsigned char) ((xoffset % 4) * 2);
1465 }
1466
Krzysztof Helt8503df62007-10-16 01:29:08 -07001467 cirrusfb_WaitBLT(cinfo->regbase); /* make sure all the BLT's are done */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001468
1469 /* lower 8 + 8 bits of screen start address */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001470 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO,
1471 (unsigned char) (base & 0xff));
1472 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI,
1473 (unsigned char) (base >> 8));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001474
1475 /* construct bits 16, 17 and 18 of screen start address */
1476 if (base & 0x10000)
1477 tmp |= 0x01;
1478 if (base & 0x20000)
1479 tmp |= 0x04;
1480 if (base & 0x40000)
1481 tmp |= 0x08;
1482
Krzysztof Helt8503df62007-10-16 01:29:08 -07001483 /* 0xf2 is %11110010, exclude tmp bits */
1484 tmp2 = (vga_rcrt(cinfo->regbase, CL_CRT1B) & 0xf2) | tmp;
1485 vga_wcrt(cinfo->regbase, CL_CRT1B, tmp2);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001486
1487 /* construct bit 19 of screen start address */
Krzysztof Helt060b6002007-10-16 01:29:13 -07001488 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19)
1489 vga_wcrt(cinfo->regbase, CL_CRT1D, (base >> 12) & 0x80);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001490
Krzysztof Helt8503df62007-10-16 01:29:08 -07001491 /* write pixel panning value to AR33; this does not quite work in 8bpp
1492 *
1493 * ### Piccolo..? Will this work?
1494 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001495 if (info->var.bits_per_pixel == 1)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001496 vga_wattr(cinfo->regbase, CL_AR33, xpix);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001497
Krzysztof Helt8503df62007-10-16 01:29:08 -07001498 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001499
Krzysztof Helt8503df62007-10-16 01:29:08 -07001500 DPRINTK("EXIT\n");
1501 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001502}
1503
Krzysztof Helt8503df62007-10-16 01:29:08 -07001504static int cirrusfb_blank(int blank_mode, struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001505{
1506 /*
Krzysztof Helt8503df62007-10-16 01:29:08 -07001507 * Blank the screen if blank_mode != 0, else unblank. If blank == NULL
1508 * then the caller blanks by setting the CLUT (Color Look Up Table)
1509 * to all black. Return 0 if blanking succeeded, != 0 if un-/blanking
1510 * failed due to e.g. a video mode which doesn't support it.
1511 * Implements VESA suspend and powerdown modes on hardware that
1512 * supports disabling hsync/vsync:
1513 * blank_mode == 2: suspend vsync
1514 * blank_mode == 3: suspend hsync
1515 * blank_mode == 4: powerdown
Linus Torvalds1da177e2005-04-16 15:20:36 -07001516 */
1517 unsigned char val;
1518 struct cirrusfb_info *cinfo = info->par;
1519 int current_mode = cinfo->blank_mode;
1520
Krzysztof Helt8503df62007-10-16 01:29:08 -07001521 DPRINTK("ENTER, blank mode = %d\n", blank_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522
1523 if (info->state != FBINFO_STATE_RUNNING ||
1524 current_mode == blank_mode) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001525 DPRINTK("EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001526 return 0;
1527 }
1528
1529 /* Undo current */
1530 if (current_mode == FB_BLANK_NORMAL ||
1531 current_mode == FB_BLANK_UNBLANK) {
1532 /* unblank the screen */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001533 val = vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE);
1534 /* clear "FullBandwidth" bit */
1535 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val & 0xdf);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001536 /* and undo VESA suspend trickery */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001537 vga_wgfx(cinfo->regbase, CL_GRE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001538 }
1539
1540 /* set new */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001541 if (blank_mode > FB_BLANK_NORMAL) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001542 /* blank the screen */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001543 val = vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE);
1544 /* set "FullBandwidth" bit */
1545 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val | 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001546 }
1547
1548 switch (blank_mode) {
1549 case FB_BLANK_UNBLANK:
1550 case FB_BLANK_NORMAL:
1551 break;
1552 case FB_BLANK_VSYNC_SUSPEND:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001553 vga_wgfx(cinfo->regbase, CL_GRE, 0x04);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001554 break;
1555 case FB_BLANK_HSYNC_SUSPEND:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001556 vga_wgfx(cinfo->regbase, CL_GRE, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001557 break;
1558 case FB_BLANK_POWERDOWN:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001559 vga_wgfx(cinfo->regbase, CL_GRE, 0x06);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001560 break;
1561 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001562 DPRINTK("EXIT, returning 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001563 return 1;
1564 }
1565
1566 cinfo->blank_mode = blank_mode;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001567 DPRINTK("EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001568
1569 /* Let fbcon do a soft blank for us */
1570 return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
1571}
1572/**** END Hardware specific Routines **************************************/
1573/****************************************************************************/
1574/**** BEGIN Internal Routines ***********************************************/
1575
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001576static void init_vgachip(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001577{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001578 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001579 const struct cirrusfb_board_info_rec *bi;
1580
Krzysztof Helt8503df62007-10-16 01:29:08 -07001581 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001582
Krzysztof Helt8503df62007-10-16 01:29:08 -07001583 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001584
1585 bi = &cirrusfb_board_info[cinfo->btype];
1586
1587 /* reset board globally */
1588 switch (cinfo->btype) {
1589 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001590 WSFR(cinfo, 0x01);
1591 udelay(500);
1592 WSFR(cinfo, 0x51);
1593 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001594 break;
1595 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001596 WSFR2(cinfo, 0xff);
1597 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001598 break;
1599 case BT_SD64:
1600 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001601 WSFR(cinfo, 0x1f);
1602 udelay(500);
1603 WSFR(cinfo, 0x4f);
1604 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001605 break;
1606 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001607 /* disable flickerfixer */
1608 vga_wcrt(cinfo->regbase, CL_CRT51, 0x00);
1609 mdelay(100);
1610 /* from Klaus' NetBSD driver: */
1611 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
1612 /* put blitter into 542x compat */
1613 vga_wgfx(cinfo->regbase, CL_GR33, 0x00);
1614 /* mode */
1615 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001616 break;
1617
1618 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001619 /* from Klaus' NetBSD driver: */
1620 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001621 break;
1622
1623 case BT_ALPINE:
1624 /* Nothing to do to reset the board. */
1625 break;
1626
1627 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001628 printk(KERN_ERR "cirrusfb: Warning: Unknown board type\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001629 break;
1630 }
1631
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001632 /* make sure RAM size set by this point */
1633 assert(info->screen_size > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001634
1635 /* the P4 is not fully initialized here; I rely on it having been */
1636 /* inited under AmigaOS already, which seems to work just fine */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001637 /* (Klaus advised to do it this way) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001638
1639 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001640 WGen(cinfo, CL_VSSM, 0x10); /* EGS: 0x16 */
1641 WGen(cinfo, CL_POS102, 0x01);
1642 WGen(cinfo, CL_VSSM, 0x08); /* EGS: 0x0e */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001643
1644 if (cinfo->btype != BT_SD64)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001645 WGen(cinfo, CL_VSSM2, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001646
Krzysztof Helt8503df62007-10-16 01:29:08 -07001647 /* reset sequencer logic */
1648 vga_wseq(cinfo->regbase, CL_SEQR0, 0x03);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001649
Krzysztof Helt8503df62007-10-16 01:29:08 -07001650 /* FullBandwidth (video off) and 8/9 dot clock */
1651 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, 0x21);
1652 /* polarity (-/-), disable access to display memory,
1653 * VGA_CRTC_START_HI base address: color
1654 */
1655 WGen(cinfo, VGA_MIS_W, 0xc1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001656
Krzysztof Helt8503df62007-10-16 01:29:08 -07001657 /* "magic cookie" - doesn't make any sense to me.. */
1658/* vga_wgfx(cinfo->regbase, CL_GRA, 0xce); */
1659 /* unlock all extension registers */
1660 vga_wseq(cinfo->regbase, CL_SEQR6, 0x12);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001661
Krzysztof Helt8503df62007-10-16 01:29:08 -07001662 /* reset blitter */
1663 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001664
1665 switch (cinfo->btype) {
1666 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001667 vga_wseq(cinfo->regbase, CL_SEQRF, 0x98);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001668 break;
1669 case BT_ALPINE:
1670 break;
1671 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001672 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001673 break;
1674 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001675 vga_wseq(cinfo->regbase, CL_SEQR16, 0x0f);
1676 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001677 break;
1678 }
1679 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001680 /* plane mask: nothing */
1681 vga_wseq(cinfo->regbase, VGA_SEQ_PLANE_WRITE, 0xff);
1682 /* character map select: doesn't even matter in gx mode */
1683 vga_wseq(cinfo->regbase, VGA_SEQ_CHARACTER_MAP, 0x00);
1684 /* memory mode: chain-4, no odd/even, ext. memory */
1685 vga_wseq(cinfo->regbase, VGA_SEQ_MEMORY_MODE, 0x0e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001686
1687 /* controller-internal base address of video memory */
1688 if (bi->init_sr07)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001689 vga_wseq(cinfo->regbase, CL_SEQR7, bi->sr07);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001690
Krzysztof Helt8503df62007-10-16 01:29:08 -07001691 /* vga_wseq(cinfo->regbase, CL_SEQR8, 0x00); */
1692 /* EEPROM control: shouldn't be necessary to write to this at all.. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001693
Krzysztof Helt8503df62007-10-16 01:29:08 -07001694 /* graphics cursor X position (incomplete; position gives rem. 3 bits */
1695 vga_wseq(cinfo->regbase, CL_SEQR10, 0x00);
1696 /* graphics cursor Y position (..."... ) */
1697 vga_wseq(cinfo->regbase, CL_SEQR11, 0x00);
1698 /* graphics cursor attributes */
1699 vga_wseq(cinfo->regbase, CL_SEQR12, 0x00);
1700 /* graphics cursor pattern address */
1701 vga_wseq(cinfo->regbase, CL_SEQR13, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001702
1703 /* writing these on a P4 might give problems.. */
1704 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001705 /* configuration readback and ext. color */
1706 vga_wseq(cinfo->regbase, CL_SEQR17, 0x00);
1707 /* signature generator */
1708 vga_wseq(cinfo->regbase, CL_SEQR18, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001709 }
1710
1711 /* MCLK select etc. */
1712 if (bi->init_sr1f)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001713 vga_wseq(cinfo->regbase, CL_SEQR1F, bi->sr1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001714
Krzysztof Helt8503df62007-10-16 01:29:08 -07001715 /* Screen A preset row scan: none */
1716 vga_wcrt(cinfo->regbase, VGA_CRTC_PRESET_ROW, 0x00);
1717 /* Text cursor start: disable text cursor */
1718 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_START, 0x20);
1719 /* Text cursor end: - */
1720 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_END, 0x00);
1721 /* Screen start address high: 0 */
1722 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, 0x00);
1723 /* Screen start address low: 0 */
1724 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, 0x00);
1725 /* text cursor location high: 0 */
1726 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_HI, 0x00);
1727 /* text cursor location low: 0 */
1728 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_LO, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001729
Krzysztof Helt8503df62007-10-16 01:29:08 -07001730 /* Underline Row scanline: - */
1731 vga_wcrt(cinfo->regbase, VGA_CRTC_UNDERLINE, 0x00);
1732 /* mode control: timing enable, byte mode, no compat modes */
1733 vga_wcrt(cinfo->regbase, VGA_CRTC_MODE, 0xc3);
1734 /* Line Compare: not needed */
1735 vga_wcrt(cinfo->regbase, VGA_CRTC_LINE_COMPARE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001736 /* ### add 0x40 for text modes with > 30 MHz pixclock */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001737 /* ext. display controls: ext.adr. wrap */
1738 vga_wcrt(cinfo->regbase, CL_CRT1B, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001739
Krzysztof Helt8503df62007-10-16 01:29:08 -07001740 /* Set/Reset registes: - */
1741 vga_wgfx(cinfo->regbase, VGA_GFX_SR_VALUE, 0x00);
1742 /* Set/Reset enable: - */
1743 vga_wgfx(cinfo->regbase, VGA_GFX_SR_ENABLE, 0x00);
1744 /* Color Compare: - */
1745 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_VALUE, 0x00);
1746 /* Data Rotate: - */
1747 vga_wgfx(cinfo->regbase, VGA_GFX_DATA_ROTATE, 0x00);
1748 /* Read Map Select: - */
1749 vga_wgfx(cinfo->regbase, VGA_GFX_PLANE_READ, 0x00);
1750 /* Mode: conf. for 16/4/2 color mode, no odd/even, read/write mode 0 */
1751 vga_wgfx(cinfo->regbase, VGA_GFX_MODE, 0x00);
1752 /* Miscellaneous: memory map base address, graphics mode */
1753 vga_wgfx(cinfo->regbase, VGA_GFX_MISC, 0x01);
1754 /* Color Don't care: involve all planes */
1755 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_MASK, 0x0f);
1756 /* Bit Mask: no mask at all */
1757 vga_wgfx(cinfo->regbase, VGA_GFX_BIT_MASK, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001758 if (cinfo->btype == BT_ALPINE)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001759 /* (5434 can't have bit 3 set for bitblt) */
1760 vga_wgfx(cinfo->regbase, CL_GRB, 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001761 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001762 /* Graphics controller mode extensions: finer granularity,
1763 * 8byte data latches
1764 */
1765 vga_wgfx(cinfo->regbase, CL_GRB, 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001766
Krzysztof Helt8503df62007-10-16 01:29:08 -07001767 vga_wgfx(cinfo->regbase, CL_GRC, 0xff); /* Color Key compare: - */
1768 vga_wgfx(cinfo->regbase, CL_GRD, 0x00); /* Color Key compare mask: - */
1769 vga_wgfx(cinfo->regbase, CL_GRE, 0x00); /* Miscellaneous control: - */
1770 /* Background color byte 1: - */
1771 /* vga_wgfx (cinfo->regbase, CL_GR10, 0x00); */
1772 /* vga_wgfx (cinfo->regbase, CL_GR11, 0x00); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001773
Krzysztof Helt8503df62007-10-16 01:29:08 -07001774 /* Attribute Controller palette registers: "identity mapping" */
1775 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE0, 0x00);
1776 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE1, 0x01);
1777 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE2, 0x02);
1778 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE3, 0x03);
1779 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE4, 0x04);
1780 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE5, 0x05);
1781 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE6, 0x06);
1782 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE7, 0x07);
1783 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE8, 0x08);
1784 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE9, 0x09);
1785 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEA, 0x0a);
1786 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEB, 0x0b);
1787 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEC, 0x0c);
1788 vga_wattr(cinfo->regbase, VGA_ATC_PALETTED, 0x0d);
1789 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEE, 0x0e);
1790 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEF, 0x0f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001791
Krzysztof Helt8503df62007-10-16 01:29:08 -07001792 /* Attribute Controller mode: graphics mode */
1793 vga_wattr(cinfo->regbase, VGA_ATC_MODE, 0x01);
1794 /* Overscan color reg.: reg. 0 */
1795 vga_wattr(cinfo->regbase, VGA_ATC_OVERSCAN, 0x00);
1796 /* Color Plane enable: Enable all 4 planes */
1797 vga_wattr(cinfo->regbase, VGA_ATC_PLANE_ENABLE, 0x0f);
1798/* ### vga_wattr(cinfo->regbase, CL_AR33, 0x00); * Pixel Panning: - */
1799 /* Color Select: - */
1800 vga_wattr(cinfo->regbase, VGA_ATC_COLOR_PAGE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001801
Krzysztof Helt8503df62007-10-16 01:29:08 -07001802 WGen(cinfo, VGA_PEL_MSK, 0xff); /* Pixel mask: no mask */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001803
1804 if (cinfo->btype != BT_ALPINE && cinfo->btype != BT_GD5480)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001805 /* polarity (-/-), enable display mem,
1806 * VGA_CRTC_START_HI i/o base = color
1807 */
1808 WGen(cinfo, VGA_MIS_W, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001809
Krzysztof Helt8503df62007-10-16 01:29:08 -07001810 /* BLT Start/status: Blitter reset */
1811 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
1812 /* - " - : "end-of-reset" */
1813 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001814
1815 /* misc... */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001816 WHDR(cinfo, 0); /* Hidden DAC register: - */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001817
Krzysztof Helt8503df62007-10-16 01:29:08 -07001818 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001819 return;
1820}
1821
Krzysztof Helt8503df62007-10-16 01:29:08 -07001822static void switch_monitor(struct cirrusfb_info *cinfo, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001823{
1824#ifdef CONFIG_ZORRO /* only works on Zorro boards */
1825 static int IsOn = 0; /* XXX not ok for multiple boards */
1826
Krzysztof Helt8503df62007-10-16 01:29:08 -07001827 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001828
1829 if (cinfo->btype == BT_PICASSO4)
1830 return; /* nothing to switch */
1831 if (cinfo->btype == BT_ALPINE)
1832 return; /* nothing to switch */
1833 if (cinfo->btype == BT_GD5480)
1834 return; /* nothing to switch */
1835 if (cinfo->btype == BT_PICASSO) {
1836 if ((on && !IsOn) || (!on && IsOn))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001837 WSFR(cinfo, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001838
Krzysztof Helt8503df62007-10-16 01:29:08 -07001839 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001840 return;
1841 }
1842 if (on) {
1843 switch (cinfo->btype) {
1844 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001845 WSFR(cinfo, cinfo->SFR | 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001846 break;
1847 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001848 WSFR(cinfo, cinfo->SFR | 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001849 break;
1850 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001851 WSFR(cinfo, 0x6f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001852 break;
1853 default: /* do nothing */ break;
1854 }
1855 } else {
1856 switch (cinfo->btype) {
1857 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001858 WSFR(cinfo, cinfo->SFR & 0xde);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001859 break;
1860 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001861 WSFR(cinfo, cinfo->SFR & 0xd7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001862 break;
1863 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001864 WSFR(cinfo, 0x4f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001865 break;
1866 default: /* do nothing */ break;
1867 }
1868 }
1869
Krzysztof Helt8503df62007-10-16 01:29:08 -07001870 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001871#endif /* CONFIG_ZORRO */
1872}
1873
Linus Torvalds1da177e2005-04-16 15:20:36 -07001874/******************************************/
1875/* Linux 2.6-style accelerated functions */
1876/******************************************/
1877
Krzysztof Helt8503df62007-10-16 01:29:08 -07001878static void cirrusfb_fillrect(struct fb_info *info,
1879 const struct fb_fillrect *region)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001880{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001881 struct fb_fillrect modded;
1882 int vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001883 struct cirrusfb_info *cinfo = info->par;
1884 int m = info->var.bits_per_pixel;
1885 u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ?
1886 cinfo->pseudo_palette[region->color] : region->color;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001887
1888 if (info->state != FBINFO_STATE_RUNNING)
1889 return;
1890 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1891 cfb_fillrect(info, region);
1892 return;
1893 }
1894
1895 vxres = info->var.xres_virtual;
1896 vyres = info->var.yres_virtual;
1897
1898 memcpy(&modded, region, sizeof(struct fb_fillrect));
1899
Krzysztof Helt8503df62007-10-16 01:29:08 -07001900 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001901 modded.dx >= vxres || modded.dy >= vyres)
1902 return;
1903
Krzysztof Helt8503df62007-10-16 01:29:08 -07001904 if (modded.dx + modded.width > vxres)
1905 modded.width = vxres - modded.dx;
1906 if (modded.dy + modded.height > vyres)
1907 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001908
Krzysztof Helt060b6002007-10-16 01:29:13 -07001909 cirrusfb_RectFill(cinfo->regbase,
1910 info->var.bits_per_pixel,
1911 (region->dx * m) / 8, region->dy,
1912 (region->width * m) / 8, region->height,
1913 color,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001914 info->fix.line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001915}
1916
Krzysztof Helt8503df62007-10-16 01:29:08 -07001917static void cirrusfb_copyarea(struct fb_info *info,
1918 const struct fb_copyarea *area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001919{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001920 struct fb_copyarea modded;
1921 u32 vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001922 struct cirrusfb_info *cinfo = info->par;
1923 int m = info->var.bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001924
1925 if (info->state != FBINFO_STATE_RUNNING)
1926 return;
1927 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1928 cfb_copyarea(info, area);
1929 return;
1930 }
1931
1932 vxres = info->var.xres_virtual;
1933 vyres = info->var.yres_virtual;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001934 memcpy(&modded, area, sizeof(struct fb_copyarea));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001935
Krzysztof Helt8503df62007-10-16 01:29:08 -07001936 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001937 modded.sx >= vxres || modded.sy >= vyres ||
1938 modded.dx >= vxres || modded.dy >= vyres)
1939 return;
1940
Krzysztof Helt8503df62007-10-16 01:29:08 -07001941 if (modded.sx + modded.width > vxres)
1942 modded.width = vxres - modded.sx;
1943 if (modded.dx + modded.width > vxres)
1944 modded.width = vxres - modded.dx;
1945 if (modded.sy + modded.height > vyres)
1946 modded.height = vyres - modded.sy;
1947 if (modded.dy + modded.height > vyres)
1948 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001949
Krzysztof Helt060b6002007-10-16 01:29:13 -07001950 cirrusfb_BitBLT(cinfo->regbase, info->var.bits_per_pixel,
1951 (area->sx * m) / 8, area->sy,
1952 (area->dx * m) / 8, area->dy,
1953 (area->width * m) / 8, area->height,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001954 info->fix.line_length);
Krzysztof Helt060b6002007-10-16 01:29:13 -07001955
Linus Torvalds1da177e2005-04-16 15:20:36 -07001956}
1957
Krzysztof Helt8503df62007-10-16 01:29:08 -07001958static void cirrusfb_imageblit(struct fb_info *info,
1959 const struct fb_image *image)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001960{
1961 struct cirrusfb_info *cinfo = info->par;
1962
Krzysztof Helt8503df62007-10-16 01:29:08 -07001963 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001964 cfb_imageblit(info, image);
1965}
1966
Linus Torvalds1da177e2005-04-16 15:20:36 -07001967#ifdef CONFIG_PPC_PREP
1968#define PREP_VIDEO_BASE ((volatile unsigned long) 0xC0000000)
1969#define PREP_IO_BASE ((volatile unsigned char *) 0x80000000)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001970static void get_prep_addrs(unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001971{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001972 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001973
1974 *display = PREP_VIDEO_BASE;
1975 *registers = (unsigned long) PREP_IO_BASE;
1976
Krzysztof Helt8503df62007-10-16 01:29:08 -07001977 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001978}
1979
1980#endif /* CONFIG_PPC_PREP */
1981
Linus Torvalds1da177e2005-04-16 15:20:36 -07001982#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001983static int release_io_ports;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001984
1985/* Pulled the logic from XFree86 Cirrus driver to get the memory size,
1986 * based on the DRAM bandwidth bit and DRAM bank switching bit. This
1987 * works with 1MB, 2MB and 4MB configurations (which the Motorola boards
1988 * seem to have. */
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07001989static unsigned int __devinit cirrusfb_get_memsize(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001990{
1991 unsigned long mem;
1992 unsigned char SRF;
1993
Krzysztof Helt8503df62007-10-16 01:29:08 -07001994 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001995
Krzysztof Helt8503df62007-10-16 01:29:08 -07001996 SRF = vga_rseq(regbase, CL_SEQRF);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001997 switch ((SRF & 0x18)) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001998 case 0x08:
1999 mem = 512 * 1024;
2000 break;
2001 case 0x10:
2002 mem = 1024 * 1024;
2003 break;
2004 /* 64-bit DRAM data bus width; assume 2MB. Also indicates 2MB memory
2005 * on the 5430.
2006 */
2007 case 0x18:
2008 mem = 2048 * 1024;
2009 break;
2010 default:
2011 printk(KERN_WARNING "CLgenfb: Unknown memory size!\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002012 mem = 1024 * 1024;
2013 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07002014 if (SRF & 0x80)
2015 /* If DRAM bank switching is enabled, there must be twice as much
2016 * memory installed. (4MB on the 5434)
2017 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002018 mem *= 2;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002019
Linus Torvalds1da177e2005-04-16 15:20:36 -07002020 /* TODO: Handling of GD5446/5480 (see XF86 sources ...) */
2021
Krzysztof Helt8503df62007-10-16 01:29:08 -07002022 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002023 return mem;
2024}
2025
Krzysztof Helt8503df62007-10-16 01:29:08 -07002026static void get_pci_addrs(const struct pci_dev *pdev,
2027 unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002028{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002029 assert(pdev != NULL);
2030 assert(display != NULL);
2031 assert(registers != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002032
Krzysztof Helt8503df62007-10-16 01:29:08 -07002033 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002034
2035 *display = 0;
2036 *registers = 0;
2037
2038 /* This is a best-guess for now */
2039
2040 if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
2041 *display = pci_resource_start(pdev, 1);
2042 *registers = pci_resource_start(pdev, 0);
2043 } else {
2044 *display = pci_resource_start(pdev, 0);
2045 *registers = pci_resource_start(pdev, 1);
2046 }
2047
Krzysztof Helt8503df62007-10-16 01:29:08 -07002048 assert(*display != 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002049
Krzysztof Helt8503df62007-10-16 01:29:08 -07002050 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002051}
2052
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002053static void cirrusfb_pci_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002054{
Krzysztof Helt64beab12008-10-15 22:03:38 -07002055 struct pci_dev *pdev = to_pci_dev(info->device);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002056
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002057 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002058#if 0 /* if system didn't claim this region, we would... */
2059 release_mem_region(0xA0000, 65535);
2060#endif
2061 if (release_io_ports)
2062 release_region(0x3C0, 32);
2063 pci_release_regions(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002064}
2065#endif /* CONFIG_PCI */
2066
Linus Torvalds1da177e2005-04-16 15:20:36 -07002067#ifdef CONFIG_ZORRO
Al Virod91f5bb2007-10-17 00:27:18 +01002068static void __devexit cirrusfb_zorro_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002069{
Al Virod91f5bb2007-10-17 00:27:18 +01002070 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt64beab12008-10-15 22:03:38 -07002071 struct zorro_dev *zdev = to_zorro_dev(info->device);
2072
2073 zorro_release_device(zdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002074
2075 if (cinfo->btype == BT_PICASSO4) {
2076 cinfo->regbase -= 0x600000;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002077 iounmap((void *)cinfo->regbase);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002078 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002079 } else {
Krzysztof Helt64beab12008-10-15 22:03:38 -07002080 if (zorro_resource_start(zdev) > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002081 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002082 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002083}
2084#endif /* CONFIG_ZORRO */
2085
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002086static int __devinit cirrusfb_set_fbinfo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002087{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002088 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002089 struct fb_var_screeninfo *var = &info->var;
2090
Linus Torvalds1da177e2005-04-16 15:20:36 -07002091 info->pseudo_palette = cinfo->pseudo_palette;
2092 info->flags = FBINFO_DEFAULT
2093 | FBINFO_HWACCEL_XPAN
2094 | FBINFO_HWACCEL_YPAN
2095 | FBINFO_HWACCEL_FILLRECT
2096 | FBINFO_HWACCEL_COPYAREA;
2097 if (noaccel)
2098 info->flags |= FBINFO_HWACCEL_DISABLED;
2099 info->fbops = &cirrusfb_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002100 if (cinfo->btype == BT_GD5480) {
2101 if (var->bits_per_pixel == 16)
2102 info->screen_base += 1 * MB_;
Krzysztof Helt1cea9a92008-10-15 22:03:37 -07002103 if (var->bits_per_pixel == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002104 info->screen_base += 2 * MB_;
2105 }
2106
2107 /* Fill fix common fields */
2108 strlcpy(info->fix.id, cirrusfb_board_info[cinfo->btype].name,
2109 sizeof(info->fix.id));
2110
2111 /* monochrome: only 1 memory plane */
2112 /* 8 bit and above: Use whole memory area */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002113 info->fix.smem_len = info->screen_size;
2114 if (var->bits_per_pixel == 1)
2115 info->fix.smem_len /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002116 info->fix.type_aux = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002117 info->fix.xpanstep = 1;
2118 info->fix.ypanstep = 1;
2119 info->fix.ywrapstep = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002120
2121 /* FIXME: map region at 0xB8000 if available, fill in here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002122 info->fix.mmio_len = 0;
2123 info->fix.accel = FB_ACCEL_NONE;
2124
2125 fb_alloc_cmap(&info->cmap, 256, 0);
2126
2127 return 0;
2128}
2129
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002130static int __devinit cirrusfb_register(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002131{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002132 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002133 int err;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002134 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002135
Krzysztof Helt8503df62007-10-16 01:29:08 -07002136 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002137
Krzysztof Helt8503df62007-10-16 01:29:08 -07002138 printk(KERN_INFO "cirrusfb: Driver for Cirrus Logic based "
2139 "graphic boards, v" CIRRUSFB_VERSION "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002140
Linus Torvalds1da177e2005-04-16 15:20:36 -07002141 btype = cinfo->btype;
2142
2143 /* sanity checks */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002144 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002145
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002146 /* set all the vital stuff */
2147 cirrusfb_set_fbinfo(info);
2148
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002149 DPRINTK("cirrusfb: (RAM start set to: 0x%p)\n", info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002150
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002151 err = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8);
2152 if (!err) {
2153 DPRINTK("wrong initial video mode\n");
2154 err = -EINVAL;
2155 goto err_dealloc_cmap;
2156 }
2157
Linus Torvalds1da177e2005-04-16 15:20:36 -07002158 info->var.activate = FB_ACTIVATE_NOW;
2159
2160 err = cirrusfb_decode_var(&info->var, &cinfo->currentmode, info);
2161 if (err < 0) {
2162 /* should never happen */
2163 DPRINTK("choking on default var... umm, no good.\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002164 goto err_dealloc_cmap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002165 }
2166
Linus Torvalds1da177e2005-04-16 15:20:36 -07002167 err = register_framebuffer(info);
2168 if (err < 0) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002169 printk(KERN_ERR "cirrusfb: could not register "
2170 "fb device; err = %d!\n", err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002171 goto err_dealloc_cmap;
2172 }
2173
Krzysztof Helt8503df62007-10-16 01:29:08 -07002174 DPRINTK("EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002175 return 0;
2176
2177err_dealloc_cmap:
2178 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002179 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002180 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002181 return err;
2182}
2183
Krzysztof Helt8503df62007-10-16 01:29:08 -07002184static void __devexit cirrusfb_cleanup(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002185{
2186 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002187 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002188
Krzysztof Helt8503df62007-10-16 01:29:08 -07002189 switch_monitor(cinfo, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002190
Krzysztof Helt8503df62007-10-16 01:29:08 -07002191 unregister_framebuffer(info);
2192 fb_dealloc_cmap(&info->cmap);
2193 printk("Framebuffer unregistered\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002194 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002195 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002196
Krzysztof Helt8503df62007-10-16 01:29:08 -07002197 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002198}
2199
Linus Torvalds1da177e2005-04-16 15:20:36 -07002200#ifdef CONFIG_PCI
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002201static int __devinit cirrusfb_pci_register(struct pci_dev *pdev,
2202 const struct pci_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002203{
2204 struct cirrusfb_info *cinfo;
2205 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002206 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002207 unsigned long board_addr, board_size;
2208 int ret;
2209
2210 ret = pci_enable_device(pdev);
2211 if (ret < 0) {
2212 printk(KERN_ERR "cirrusfb: Cannot enable PCI device\n");
2213 goto err_out;
2214 }
2215
2216 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &pdev->dev);
2217 if (!info) {
2218 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
2219 ret = -ENOMEM;
2220 goto err_disable;
2221 }
2222
2223 cinfo = info->par;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002224 cinfo->btype = btype = (enum cirrus_board) ent->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002225
Krzysztof Helt7345de32007-10-16 01:29:11 -07002226 DPRINTK(" Found PCI device, base address 0 is 0x%x, btype set to %d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002227 pdev->resource[0].start, btype);
Krzysztof Helt7345de32007-10-16 01:29:11 -07002228 DPRINTK(" base address 1 is 0x%x\n", pdev->resource[1].start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002229
Krzysztof Helt8503df62007-10-16 01:29:08 -07002230 if (isPReP) {
2231 pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, 0x00000000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002232#ifdef CONFIG_PPC_PREP
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002233 get_prep_addrs(&board_addr, &info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002234#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07002235 /* PReP dies if we ioremap the IO registers, but it works w/out... */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002236 cinfo->regbase = (char __iomem *) info->fix.mmio_start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002237 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002238 DPRINTK("Attempt to get PCI info for Cirrus Graphics Card\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002239 get_pci_addrs(pdev, &board_addr, &info->fix.mmio_start);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002240 /* FIXME: this forces VGA. alternatives? */
2241 cinfo->regbase = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002242 }
2243
Krzysztof Helt8503df62007-10-16 01:29:08 -07002244 DPRINTK("Board address: 0x%lx, register address: 0x%lx\n",
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002245 board_addr, info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002246
2247 board_size = (btype == BT_GD5480) ?
Krzysztof Helt8503df62007-10-16 01:29:08 -07002248 32 * MB_ : cirrusfb_get_memsize(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002249
2250 ret = pci_request_regions(pdev, "cirrusfb");
Krzysztof Helt8503df62007-10-16 01:29:08 -07002251 if (ret < 0) {
2252 printk(KERN_ERR "cirrusfb: cannot reserve region 0x%lx, "
2253 "abort\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002254 board_addr);
2255 goto err_release_fb;
2256 }
2257#if 0 /* if the system didn't claim this region, we would... */
2258 if (!request_mem_region(0xA0000, 65535, "cirrusfb")) {
2259 printk(KERN_ERR "cirrusfb: cannot reserve region 0x%lx, abort\n"
2260,
2261 0xA0000L);
2262 ret = -EBUSY;
2263 goto err_release_regions;
2264 }
2265#endif
2266 if (request_region(0x3C0, 32, "cirrusfb"))
2267 release_io_ports = 1;
2268
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002269 info->screen_base = ioremap(board_addr, board_size);
2270 if (!info->screen_base) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002271 ret = -EIO;
2272 goto err_release_legacy;
2273 }
2274
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002275 info->fix.smem_start = board_addr;
2276 info->screen_size = board_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002277 cinfo->unmap = cirrusfb_pci_unmap;
2278
Philippe De Muytere59b6a52008-06-12 15:21:45 -07002279 printk(KERN_INFO "RAM (%lu kB) at 0x%lx, Cirrus "
2280 "Logic chipset on PCI bus\n",
2281 info->screen_size >> 10, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002282 pci_set_drvdata(pdev, info);
2283
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002284 ret = cirrusfb_register(info);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002285 if (ret)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002286 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002287 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002288
2289err_release_legacy:
2290 if (release_io_ports)
2291 release_region(0x3C0, 32);
2292#if 0
2293 release_mem_region(0xA0000, 65535);
2294err_release_regions:
2295#endif
2296 pci_release_regions(pdev);
2297err_release_fb:
2298 framebuffer_release(info);
2299err_disable:
Linus Torvalds1da177e2005-04-16 15:20:36 -07002300err_out:
2301 return ret;
2302}
2303
Krzysztof Helt8503df62007-10-16 01:29:08 -07002304static void __devexit cirrusfb_pci_unregister(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002305{
2306 struct fb_info *info = pci_get_drvdata(pdev);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002307 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002308
Krzysztof Helt8503df62007-10-16 01:29:08 -07002309 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002310
Krzysztof Helt8503df62007-10-16 01:29:08 -07002311 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002312}
2313
2314static struct pci_driver cirrusfb_pci_driver = {
2315 .name = "cirrusfb",
2316 .id_table = cirrusfb_pci_table,
2317 .probe = cirrusfb_pci_register,
2318 .remove = __devexit_p(cirrusfb_pci_unregister),
2319#ifdef CONFIG_PM
2320#if 0
2321 .suspend = cirrusfb_pci_suspend,
2322 .resume = cirrusfb_pci_resume,
2323#endif
2324#endif
2325};
2326#endif /* CONFIG_PCI */
2327
Linus Torvalds1da177e2005-04-16 15:20:36 -07002328#ifdef CONFIG_ZORRO
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002329static int __devinit cirrusfb_zorro_register(struct zorro_dev *z,
2330 const struct zorro_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002331{
2332 struct cirrusfb_info *cinfo;
2333 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002334 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002335 struct zorro_dev *z2 = NULL;
2336 unsigned long board_addr, board_size, size;
2337 int ret;
2338
2339 btype = ent->driver_data;
2340 if (cirrusfb_zorro_table2[btype].id2)
2341 z2 = zorro_find_device(cirrusfb_zorro_table2[btype].id2, NULL);
2342 size = cirrusfb_zorro_table2[btype].size;
2343 printk(KERN_INFO "cirrusfb: %s board detected; ",
2344 cirrusfb_board_info[btype].name);
2345
2346 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &z->dev);
2347 if (!info) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002348 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002349 ret = -ENOMEM;
2350 goto err_out;
2351 }
2352
2353 cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002354 cinfo->btype = btype;
2355
Al Viro36ea96a2007-10-27 19:46:58 +01002356 assert(z);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002357 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002358
Linus Torvalds1da177e2005-04-16 15:20:36 -07002359 board_addr = zorro_resource_start(z);
2360 board_size = zorro_resource_len(z);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002361 info->screen_size = size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002362
2363 if (!zorro_request_device(z, "cirrusfb")) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002364 printk(KERN_ERR "cirrusfb: cannot reserve region 0x%lx, "
2365 "abort\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07002366 board_addr);
2367 ret = -EBUSY;
2368 goto err_release_fb;
2369 }
2370
Krzysztof Helt8503df62007-10-16 01:29:08 -07002371 printk(" RAM (%lu MB) at $%lx, ", board_size / MB_, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002372
2373 ret = -EIO;
2374
2375 if (btype == BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002376 printk(KERN_INFO " REG at $%lx\n", board_addr + 0x600000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002377
2378 /* To be precise, for the P4 this is not the */
2379 /* begin of the board, but the begin of RAM. */
2380 /* for P4, map in its address space in 2 chunks (### TEST! ) */
2381 /* (note the ugly hardcoded 16M number) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002382 cinfo->regbase = ioremap(board_addr, 16777216);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002383 if (!cinfo->regbase)
2384 goto err_release_region;
2385
Krzysztof Helt8503df62007-10-16 01:29:08 -07002386 DPRINTK("cirrusfb: Virtual address for board set to: $%p\n",
2387 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002388 cinfo->regbase += 0x600000;
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002389 info->fix.mmio_start = board_addr + 0x600000;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002390
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002391 info->fix.smem_start = board_addr + 16777216;
2392 info->screen_base = ioremap(info->fix.smem_start, 16777216);
2393 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002394 goto err_unmap_regbase;
2395 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002396 printk(KERN_INFO " REG at $%lx\n",
2397 (unsigned long) z2->resource.start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002398
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002399 info->fix.smem_start = board_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002400 if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002401 info->screen_base = ioremap(board_addr, board_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002402 else
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002403 info->screen_base = (caddr_t) ZTWO_VADDR(board_addr);
2404 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002405 goto err_release_region;
2406
2407 /* set address for REG area of board */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002408 cinfo->regbase = (caddr_t) ZTWO_VADDR(z2->resource.start);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002409 info->fix.mmio_start = z2->resource.start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002410
Krzysztof Helt8503df62007-10-16 01:29:08 -07002411 DPRINTK("cirrusfb: Virtual address for board set to: $%p\n",
2412 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002413 }
2414 cinfo->unmap = cirrusfb_zorro_unmap;
2415
Krzysztof Helt8503df62007-10-16 01:29:08 -07002416 printk(KERN_INFO "Cirrus Logic chipset on Zorro bus\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002417 zorro_set_drvdata(z, info);
2418
Al Virod91f5bb2007-10-17 00:27:18 +01002419 ret = cirrusfb_register(info);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002420 if (ret) {
2421 if (btype == BT_PICASSO4) {
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002422 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002423 iounmap(cinfo->regbase - 0x600000);
2424 } else if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002425 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002426 }
2427 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002428
2429err_unmap_regbase:
2430 /* Parental advisory: explicit hack */
2431 iounmap(cinfo->regbase - 0x600000);
2432err_release_region:
2433 release_region(board_addr, board_size);
2434err_release_fb:
2435 framebuffer_release(info);
2436err_out:
2437 return ret;
2438}
2439
2440void __devexit cirrusfb_zorro_unregister(struct zorro_dev *z)
2441{
2442 struct fb_info *info = zorro_get_drvdata(z);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002443 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002444
Krzysztof Helt8503df62007-10-16 01:29:08 -07002445 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002446
Krzysztof Helt8503df62007-10-16 01:29:08 -07002447 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002448}
2449
2450static struct zorro_driver cirrusfb_zorro_driver = {
2451 .name = "cirrusfb",
2452 .id_table = cirrusfb_zorro_table,
2453 .probe = cirrusfb_zorro_register,
2454 .remove = __devexit_p(cirrusfb_zorro_unregister),
2455};
2456#endif /* CONFIG_ZORRO */
2457
2458static int __init cirrusfb_init(void)
2459{
2460 int error = 0;
2461
2462#ifndef MODULE
2463 char *option = NULL;
2464
2465 if (fb_get_options("cirrusfb", &option))
2466 return -ENODEV;
2467 cirrusfb_setup(option);
2468#endif
2469
2470#ifdef CONFIG_ZORRO
Bjorn Helgaas33d86752006-03-25 03:07:20 -08002471 error |= zorro_register_driver(&cirrusfb_zorro_driver);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002472#endif
2473#ifdef CONFIG_PCI
2474 error |= pci_register_driver(&cirrusfb_pci_driver);
2475#endif
2476 return error;
2477}
2478
Linus Torvalds1da177e2005-04-16 15:20:36 -07002479#ifndef MODULE
2480static int __init cirrusfb_setup(char *options) {
2481 char *this_opt, s[32];
2482 int i;
2483
Krzysztof Helt8503df62007-10-16 01:29:08 -07002484 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002485
2486 if (!options || !*options)
2487 return 0;
2488
Krzysztof Helt8503df62007-10-16 01:29:08 -07002489 while ((this_opt = strsep(&options, ",")) != NULL) {
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002490 if (!*this_opt)
2491 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002492
2493 DPRINTK("cirrusfb_setup: option '%s'\n", this_opt);
2494
Linus Torvalds1da177e2005-04-16 15:20:36 -07002495 if (!strcmp(this_opt, "noaccel"))
2496 noaccel = 1;
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002497 else if (!strncmp(this_opt, "mode:", 5))
2498 mode_option = this_opt + 5;
2499 else
2500 mode_option = this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002501 }
2502 return 0;
2503}
2504#endif
2505
Linus Torvalds1da177e2005-04-16 15:20:36 -07002506 /*
2507 * Modularization
2508 */
2509
2510MODULE_AUTHOR("Copyright 1999,2000 Jeff Garzik <jgarzik@pobox.com>");
2511MODULE_DESCRIPTION("Accelerated FBDev driver for Cirrus Logic chips");
2512MODULE_LICENSE("GPL");
2513
Krzysztof Helt8503df62007-10-16 01:29:08 -07002514static void __exit cirrusfb_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002515{
2516#ifdef CONFIG_PCI
2517 pci_unregister_driver(&cirrusfb_pci_driver);
2518#endif
2519#ifdef CONFIG_ZORRO
2520 zorro_unregister_driver(&cirrusfb_zorro_driver);
2521#endif
2522}
2523
2524module_init(cirrusfb_init);
2525
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002526module_param(mode_option, charp, 0);
2527MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");
2528
Linus Torvalds1da177e2005-04-16 15:20:36 -07002529#ifdef MODULE
2530module_exit(cirrusfb_exit);
2531#endif
2532
Linus Torvalds1da177e2005-04-16 15:20:36 -07002533/**********************************************************************/
2534/* about the following functions - I have used the same names for the */
2535/* functions as Markus Wild did in his Retina driver for NetBSD as */
2536/* they just made sense for this purpose. Apart from that, I wrote */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002537/* these functions myself. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002538/**********************************************************************/
2539
2540/*** WGen() - write into one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002541static void WGen(const struct cirrusfb_info *cinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002542 int regnum, unsigned char val)
2543{
2544 unsigned long regofs = 0;
2545
2546 if (cinfo->btype == BT_PICASSO) {
2547 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002548/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2549 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002550 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2551 regofs = 0xfff;
2552 }
2553
Krzysztof Helt8503df62007-10-16 01:29:08 -07002554 vga_w(cinfo->regbase, regofs + regnum, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002555}
2556
2557/*** RGen() - read out one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002558static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002559{
2560 unsigned long regofs = 0;
2561
2562 if (cinfo->btype == BT_PICASSO) {
2563 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002564/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2565 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002566 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2567 regofs = 0xfff;
2568 }
2569
Krzysztof Helt8503df62007-10-16 01:29:08 -07002570 return vga_r(cinfo->regbase, regofs + regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002571}
2572
2573/*** AttrOn() - turn on VideoEnable for Attribute controller ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002574static void AttrOn(const struct cirrusfb_info *cinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002575{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002576 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002577
Krzysztof Helt8503df62007-10-16 01:29:08 -07002578 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002579
Krzysztof Helt8503df62007-10-16 01:29:08 -07002580 if (vga_rcrt(cinfo->regbase, CL_CRT24) & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002581 /* if we're just in "write value" mode, write back the */
2582 /* same value as before to not modify anything */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002583 vga_w(cinfo->regbase, VGA_ATT_IW,
2584 vga_r(cinfo->regbase, VGA_ATT_R));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002585 }
2586 /* turn on video bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002587/* vga_w(cinfo->regbase, VGA_ATT_IW, 0x20); */
2588 vga_w(cinfo->regbase, VGA_ATT_IW, 0x33);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002589
2590 /* dummy write on Reg0 to be on "write index" mode next time */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002591 vga_w(cinfo->regbase, VGA_ATT_IW, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002592
Krzysztof Helt8503df62007-10-16 01:29:08 -07002593 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002594}
2595
2596/*** WHDR() - write into the Hidden DAC register ***/
2597/* as the HDR is the only extension register that requires special treatment
2598 * (the other extension registers are accessible just like the "ordinary"
2599 * registers of their functional group) here is a specialized routine for
2600 * accessing the HDR
2601 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002602static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002603{
2604 unsigned char dummy;
2605
2606 if (cinfo->btype == BT_PICASSO) {
2607 /* Klaus' hint for correct access to HDR on some boards */
2608 /* first write 0 to pixel mask (3c6) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002609 WGen(cinfo, VGA_PEL_MSK, 0x00);
2610 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002611 /* next read dummy from pixel address (3c8) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002612 dummy = RGen(cinfo, VGA_PEL_IW);
2613 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002614 }
2615 /* now do the usual stuff to access the HDR */
2616
Krzysztof Helt8503df62007-10-16 01:29:08 -07002617 dummy = RGen(cinfo, VGA_PEL_MSK);
2618 udelay(200);
2619 dummy = RGen(cinfo, VGA_PEL_MSK);
2620 udelay(200);
2621 dummy = RGen(cinfo, VGA_PEL_MSK);
2622 udelay(200);
2623 dummy = RGen(cinfo, VGA_PEL_MSK);
2624 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002625
Krzysztof Helt8503df62007-10-16 01:29:08 -07002626 WGen(cinfo, VGA_PEL_MSK, val);
2627 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002628
2629 if (cinfo->btype == BT_PICASSO) {
2630 /* now first reset HDR access counter */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002631 dummy = RGen(cinfo, VGA_PEL_IW);
2632 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002633
2634 /* and at the end, restore the mask value */
2635 /* ## is this mask always 0xff? */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002636 WGen(cinfo, VGA_PEL_MSK, 0xff);
2637 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002638 }
2639}
2640
Linus Torvalds1da177e2005-04-16 15:20:36 -07002641/*** WSFR() - write to the "special function register" (SFR) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002642static void WSFR(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002643{
2644#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07002645 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002646 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002647 z_writeb(val, cinfo->regbase + 0x8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002648#endif
2649}
2650
2651/* The Picasso has a second register for switching the monitor bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002652static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002653{
2654#ifdef CONFIG_ZORRO
2655 /* writing an arbitrary value to this one causes the monitor switcher */
2656 /* to flip to Amiga display */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002657 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002658 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002659 z_writeb(val, cinfo->regbase + 0x9000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002660#endif
2661}
2662
Linus Torvalds1da177e2005-04-16 15:20:36 -07002663/*** WClut - set CLUT entry (range: 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002664static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002665 unsigned char green, unsigned char blue)
2666{
2667 unsigned int data = VGA_PEL_D;
2668
2669 /* address write mode register is not translated.. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002670 vga_w(cinfo->regbase, VGA_PEL_IW, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002671
2672 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2673 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2674 /* but DAC data register IS, at least for Picasso II */
2675 if (cinfo->btype == BT_PICASSO)
2676 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002677 vga_w(cinfo->regbase, data, red);
2678 vga_w(cinfo->regbase, data, green);
2679 vga_w(cinfo->regbase, data, blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002680 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002681 vga_w(cinfo->regbase, data, blue);
2682 vga_w(cinfo->regbase, data, green);
2683 vga_w(cinfo->regbase, data, red);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002684 }
2685}
2686
Linus Torvalds1da177e2005-04-16 15:20:36 -07002687#if 0
2688/*** RClut - read CLUT entry (range 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002689static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char *red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002690 unsigned char *green, unsigned char *blue)
2691{
2692 unsigned int data = VGA_PEL_D;
2693
Krzysztof Helt8503df62007-10-16 01:29:08 -07002694 vga_w(cinfo->regbase, VGA_PEL_IR, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002695
2696 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2697 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2698 if (cinfo->btype == BT_PICASSO)
2699 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002700 *red = vga_r(cinfo->regbase, data);
2701 *green = vga_r(cinfo->regbase, data);
2702 *blue = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002703 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002704 *blue = vga_r(cinfo->regbase, data);
2705 *green = vga_r(cinfo->regbase, data);
2706 *red = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002707 }
2708}
2709#endif
2710
Linus Torvalds1da177e2005-04-16 15:20:36 -07002711/*******************************************************************
2712 cirrusfb_WaitBLT()
2713
2714 Wait for the BitBLT engine to complete a possible earlier job
2715*********************************************************************/
2716
2717/* FIXME: use interrupts instead */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002718static void cirrusfb_WaitBLT(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002719{
2720 /* now busy-wait until we're done */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002721 while (vga_rgfx(regbase, CL_GR31) & 0x08)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002722 /* do nothing */ ;
2723}
2724
2725/*******************************************************************
2726 cirrusfb_BitBLT()
2727
2728 perform accelerated "scrolling"
2729********************************************************************/
2730
Krzysztof Helt8503df62007-10-16 01:29:08 -07002731static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
2732 u_short curx, u_short cury,
2733 u_short destx, u_short desty,
2734 u_short width, u_short height,
2735 u_short line_length)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002736{
2737 u_short nwidth, nheight;
2738 u_long nsrc, ndest;
2739 u_char bltmode;
2740
Krzysztof Helt8503df62007-10-16 01:29:08 -07002741 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002742
2743 nwidth = width - 1;
2744 nheight = height - 1;
2745
2746 bltmode = 0x00;
2747 /* if source adr < dest addr, do the Blt backwards */
2748 if (cury <= desty) {
2749 if (cury == desty) {
2750 /* if src and dest are on the same line, check x */
2751 if (curx < destx)
2752 bltmode |= 0x01;
2753 } else
2754 bltmode |= 0x01;
2755 }
2756 if (!bltmode) {
2757 /* standard case: forward blitting */
2758 nsrc = (cury * line_length) + curx;
2759 ndest = (desty * line_length) + destx;
2760 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002761 /* this means start addresses are at the end,
2762 * counting backwards
2763 */
2764 nsrc = cury * line_length + curx +
2765 nheight * line_length + nwidth;
2766 ndest = desty * line_length + destx +
2767 nheight * line_length + nwidth;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002768 }
2769
2770 /*
2771 run-down of registers to be programmed:
2772 destination pitch
2773 source pitch
2774 BLT width/height
2775 source start
2776 destination start
2777 BLT mode
2778 BLT ROP
2779 VGA_GFX_SR_VALUE / VGA_GFX_SR_ENABLE: "fill color"
2780 start/stop
2781 */
2782
Krzysztof Helt8503df62007-10-16 01:29:08 -07002783 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002784
2785 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002786 /* dest pitch low */
2787 vga_wgfx(regbase, CL_GR24, line_length & 0xff);
2788 /* dest pitch hi */
2789 vga_wgfx(regbase, CL_GR25, line_length >> 8);
2790 /* source pitch low */
2791 vga_wgfx(regbase, CL_GR26, line_length & 0xff);
2792 /* source pitch hi */
2793 vga_wgfx(regbase, CL_GR27, line_length >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002794
2795 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002796 /* BLT width low */
2797 vga_wgfx(regbase, CL_GR20, nwidth & 0xff);
2798 /* BLT width hi */
2799 vga_wgfx(regbase, CL_GR21, nwidth >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002800
2801 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002802 /* BLT height low */
2803 vga_wgfx(regbase, CL_GR22, nheight & 0xff);
2804 /* BLT width hi */
2805 vga_wgfx(regbase, CL_GR23, nheight >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002806
2807 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002808 /* BLT dest low */
2809 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2810 /* BLT dest mid */
2811 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2812 /* BLT dest hi */
2813 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002814
2815 /* BLT source */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002816 /* BLT src low */
2817 vga_wgfx(regbase, CL_GR2C, (u_char) (nsrc & 0xff));
2818 /* BLT src mid */
2819 vga_wgfx(regbase, CL_GR2D, (u_char) (nsrc >> 8));
2820 /* BLT src hi */
2821 vga_wgfx(regbase, CL_GR2E, (u_char) (nsrc >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002822
2823 /* BLT mode */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002824 vga_wgfx(regbase, CL_GR30, bltmode); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002825
2826 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002827 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002828
2829 /* and finally: GO! */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002830 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002831
Krzysztof Helt8503df62007-10-16 01:29:08 -07002832 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002833}
2834
Linus Torvalds1da177e2005-04-16 15:20:36 -07002835/*******************************************************************
2836 cirrusfb_RectFill()
2837
2838 perform accelerated rectangle fill
2839********************************************************************/
2840
Krzysztof Helt8503df62007-10-16 01:29:08 -07002841static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002842 u_short x, u_short y, u_short width, u_short height,
2843 u_char color, u_short line_length)
2844{
2845 u_short nwidth, nheight;
2846 u_long ndest;
2847 u_char op;
2848
Krzysztof Helt8503df62007-10-16 01:29:08 -07002849 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002850
2851 nwidth = width - 1;
2852 nheight = height - 1;
2853
2854 ndest = (y * line_length) + x;
2855
Krzysztof Helt8503df62007-10-16 01:29:08 -07002856 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002857
2858 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002859 vga_wgfx(regbase, CL_GR24, line_length & 0xff); /* dest pitch low */
2860 vga_wgfx(regbase, CL_GR25, line_length >> 8); /* dest pitch hi */
2861 vga_wgfx(regbase, CL_GR26, line_length & 0xff); /* source pitch low */
2862 vga_wgfx(regbase, CL_GR27, line_length >> 8); /* source pitch hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002863
2864 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002865 vga_wgfx(regbase, CL_GR20, nwidth & 0xff); /* BLT width low */
2866 vga_wgfx(regbase, CL_GR21, nwidth >> 8); /* BLT width hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002867
2868 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002869 vga_wgfx(regbase, CL_GR22, nheight & 0xff); /* BLT height low */
2870 vga_wgfx(regbase, CL_GR23, nheight >> 8); /* BLT width hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002871
2872 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002873 /* BLT dest low */
2874 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2875 /* BLT dest mid */
2876 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2877 /* BLT dest hi */
2878 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002879
2880 /* BLT source: set to 0 (is a dummy here anyway) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002881 vga_wgfx(regbase, CL_GR2C, 0x00); /* BLT src low */
2882 vga_wgfx(regbase, CL_GR2D, 0x00); /* BLT src mid */
2883 vga_wgfx(regbase, CL_GR2E, 0x00); /* BLT src hi */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002884
2885 /* This is a ColorExpand Blt, using the */
2886 /* same color for foreground and background */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002887 vga_wgfx(regbase, VGA_GFX_SR_VALUE, color); /* foreground color */
2888 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, color); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002889
2890 op = 0xc0;
2891 if (bits_per_pixel == 16) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002892 vga_wgfx(regbase, CL_GR10, color); /* foreground color */
2893 vga_wgfx(regbase, CL_GR11, color); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002894 op = 0x50;
2895 op = 0xd0;
2896 } else if (bits_per_pixel == 32) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002897 vga_wgfx(regbase, CL_GR10, color); /* foreground color */
2898 vga_wgfx(regbase, CL_GR11, color); /* background color */
2899 vga_wgfx(regbase, CL_GR12, color); /* foreground color */
2900 vga_wgfx(regbase, CL_GR13, color); /* background color */
2901 vga_wgfx(regbase, CL_GR14, 0); /* foreground color */
2902 vga_wgfx(regbase, CL_GR15, 0); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002903 op = 0x50;
2904 op = 0xf0;
2905 }
2906 /* BLT mode: color expand, Enable 8x8 copy (faster?) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002907 vga_wgfx(regbase, CL_GR30, op); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002908
2909 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002910 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002911
2912 /* and finally: GO! */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002913 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002914
Krzysztof Helt8503df62007-10-16 01:29:08 -07002915 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002916}
2917
Linus Torvalds1da177e2005-04-16 15:20:36 -07002918/**************************************************************************
2919 * bestclock() - determine closest possible clock lower(?) than the
2920 * desired pixel clock
2921 **************************************************************************/
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002922static void bestclock(long freq, int *nom, int *den, int *div)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002923{
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002924 int n, d;
2925 long h, diff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002926
Krzysztof Helt8503df62007-10-16 01:29:08 -07002927 assert(nom != NULL);
2928 assert(den != NULL);
2929 assert(div != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002930
2931 *nom = 0;
2932 *den = 0;
2933 *div = 0;
2934
Krzysztof Helt8503df62007-10-16 01:29:08 -07002935 DPRINTK("ENTER\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002936
2937 if (freq < 8000)
2938 freq = 8000;
2939
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002940 diff = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002941
2942 for (n = 32; n < 128; n++) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002943 int s = 0;
2944
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002945 d = (14318 * n) / freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002946 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002947 int temp = d;
2948
2949 if (temp > 31) {
2950 s = 1;
2951 temp >>= 1;
2952 }
2953 h = ((14318 * n) / temp) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002954 h = h > freq ? h - freq : freq - h;
2955 if (h < diff) {
2956 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002957 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002958 *den = temp;
2959 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002960 }
2961 }
Krzysztof Helt7528f542008-10-15 22:03:36 -07002962 d++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002963 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002964 if (d > 31) {
2965 s = 1;
2966 d >>= 1;
2967 }
2968 h = ((14318 * n) / d) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002969 h = h > freq ? h - freq : freq - h;
2970 if (h < diff) {
2971 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002972 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002973 *den = d;
2974 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002975 }
2976 }
2977 }
2978
Krzysztof Helt8503df62007-10-16 01:29:08 -07002979 DPRINTK("Best possible values for given frequency:\n");
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002980 DPRINTK(" freq: %ld kHz nom: %d den: %d div: %d\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002981 freq, *nom, *den, *div);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002982
Krzysztof Helt8503df62007-10-16 01:29:08 -07002983 DPRINTK("EXIT\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002984}
2985
Linus Torvalds1da177e2005-04-16 15:20:36 -07002986/* -------------------------------------------------------------------------
2987 *
2988 * debugging functions
2989 *
2990 * -------------------------------------------------------------------------
2991 */
2992
2993#ifdef CIRRUSFB_DEBUG
2994
2995/**
2996 * cirrusfb_dbg_print_byte
2997 * @name: name associated with byte value to be displayed
2998 * @val: byte value to be displayed
2999 *
3000 * DESCRIPTION:
3001 * Display an indented string, along with a hexidecimal byte value, and
3002 * its decoded bits. Bits 7 through 0 are listed in left-to-right
3003 * order.
3004 */
3005
3006static
Krzysztof Helt8503df62007-10-16 01:29:08 -07003007void cirrusfb_dbg_print_byte(const char *name, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003008{
Krzysztof Helt8503df62007-10-16 01:29:08 -07003009 DPRINTK("%8s = 0x%02X (bits 7-0: %c%c%c%c%c%c%c%c)\n",
3010 name, val,
3011 val & 0x80 ? '1' : '0',
3012 val & 0x40 ? '1' : '0',
3013 val & 0x20 ? '1' : '0',
3014 val & 0x10 ? '1' : '0',
3015 val & 0x08 ? '1' : '0',
3016 val & 0x04 ? '1' : '0',
3017 val & 0x02 ? '1' : '0',
3018 val & 0x01 ? '1' : '0');
Linus Torvalds1da177e2005-04-16 15:20:36 -07003019}
3020
Linus Torvalds1da177e2005-04-16 15:20:36 -07003021/**
3022 * cirrusfb_dbg_print_regs
3023 * @base: If using newmmio, the newmmio base address, otherwise %NULL
3024 * @reg_class: type of registers to read: %CRT, or %SEQ
3025 *
3026 * DESCRIPTION:
3027 * Dumps the given list of VGA CRTC registers. If @base is %NULL,
3028 * old-style I/O ports are queried for information, otherwise MMIO is
3029 * used at the given @base address to query the information.
3030 */
3031
3032static
Krzysztof Helt8503df62007-10-16 01:29:08 -07003033void cirrusfb_dbg_print_regs(caddr_t regbase,
Krzysztof Helt7345de32007-10-16 01:29:11 -07003034 enum cirrusfb_dbg_reg_class reg_class, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003035{
3036 va_list list;
3037 unsigned char val = 0;
3038 unsigned reg;
3039 char *name;
3040
Krzysztof Helt8503df62007-10-16 01:29:08 -07003041 va_start(list, reg_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003042
Krzysztof Helt8503df62007-10-16 01:29:08 -07003043 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003044 while (name != NULL) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07003045 reg = va_arg(list, int);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003046
3047 switch (reg_class) {
3048 case CRT:
Krzysztof Helt8503df62007-10-16 01:29:08 -07003049 val = vga_rcrt(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003050 break;
3051 case SEQ:
Krzysztof Helt8503df62007-10-16 01:29:08 -07003052 val = vga_rseq(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003053 break;
3054 default:
3055 /* should never occur */
Richard Knutssonc930faa2007-05-08 00:38:29 -07003056 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003057 break;
3058 }
3059
Krzysztof Helt8503df62007-10-16 01:29:08 -07003060 cirrusfb_dbg_print_byte(name, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003061
Krzysztof Helt8503df62007-10-16 01:29:08 -07003062 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003063 }
3064
Krzysztof Helt8503df62007-10-16 01:29:08 -07003065 va_end(list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003066}
3067
Linus Torvalds1da177e2005-04-16 15:20:36 -07003068/**
3069 * cirrusfb_dump
3070 * @cirrusfbinfo:
3071 *
3072 * DESCRIPTION:
3073 */
3074
Krzysztof Helt8503df62007-10-16 01:29:08 -07003075static void cirrusfb_dump(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003076{
Krzysztof Helt8503df62007-10-16 01:29:08 -07003077 cirrusfb_dbg_reg_dump(NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003078}
3079
Linus Torvalds1da177e2005-04-16 15:20:36 -07003080/**
3081 * cirrusfb_dbg_reg_dump
3082 * @base: If using newmmio, the newmmio base address, otherwise %NULL
3083 *
3084 * DESCRIPTION:
3085 * Dumps a list of interesting VGA and CIRRUSFB registers. If @base is %NULL,
3086 * old-style I/O ports are queried for information, otherwise MMIO is
3087 * used at the given @base address to query the information.
3088 */
3089
3090static
Krzysztof Helt8503df62007-10-16 01:29:08 -07003091void cirrusfb_dbg_reg_dump(caddr_t regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003092{
Krzysztof Helt8503df62007-10-16 01:29:08 -07003093 DPRINTK("CIRRUSFB VGA CRTC register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003094
Krzysztof Helt8503df62007-10-16 01:29:08 -07003095 cirrusfb_dbg_print_regs(regbase, CRT,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003096 "CR00", 0x00,
3097 "CR01", 0x01,
3098 "CR02", 0x02,
3099 "CR03", 0x03,
3100 "CR04", 0x04,
3101 "CR05", 0x05,
3102 "CR06", 0x06,
3103 "CR07", 0x07,
3104 "CR08", 0x08,
3105 "CR09", 0x09,
3106 "CR0A", 0x0A,
3107 "CR0B", 0x0B,
3108 "CR0C", 0x0C,
3109 "CR0D", 0x0D,
3110 "CR0E", 0x0E,
3111 "CR0F", 0x0F,
3112 "CR10", 0x10,
3113 "CR11", 0x11,
3114 "CR12", 0x12,
3115 "CR13", 0x13,
3116 "CR14", 0x14,
3117 "CR15", 0x15,
3118 "CR16", 0x16,
3119 "CR17", 0x17,
3120 "CR18", 0x18,
3121 "CR22", 0x22,
3122 "CR24", 0x24,
3123 "CR26", 0x26,
3124 "CR2D", 0x2D,
3125 "CR2E", 0x2E,
3126 "CR2F", 0x2F,
3127 "CR30", 0x30,
3128 "CR31", 0x31,
3129 "CR32", 0x32,
3130 "CR33", 0x33,
3131 "CR34", 0x34,
3132 "CR35", 0x35,
3133 "CR36", 0x36,
3134 "CR37", 0x37,
3135 "CR38", 0x38,
3136 "CR39", 0x39,
3137 "CR3A", 0x3A,
3138 "CR3B", 0x3B,
3139 "CR3C", 0x3C,
3140 "CR3D", 0x3D,
3141 "CR3E", 0x3E,
3142 "CR3F", 0x3F,
3143 NULL);
3144
Krzysztof Helt8503df62007-10-16 01:29:08 -07003145 DPRINTK("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003146
Krzysztof Helt8503df62007-10-16 01:29:08 -07003147 DPRINTK("CIRRUSFB VGA SEQ register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003148
Krzysztof Helt8503df62007-10-16 01:29:08 -07003149 cirrusfb_dbg_print_regs(regbase, SEQ,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003150 "SR00", 0x00,
3151 "SR01", 0x01,
3152 "SR02", 0x02,
3153 "SR03", 0x03,
3154 "SR04", 0x04,
3155 "SR08", 0x08,
3156 "SR09", 0x09,
3157 "SR0A", 0x0A,
3158 "SR0B", 0x0B,
3159 "SR0D", 0x0D,
3160 "SR10", 0x10,
3161 "SR11", 0x11,
3162 "SR12", 0x12,
3163 "SR13", 0x13,
3164 "SR14", 0x14,
3165 "SR15", 0x15,
3166 "SR16", 0x16,
3167 "SR17", 0x17,
3168 "SR18", 0x18,
3169 "SR19", 0x19,
3170 "SR1A", 0x1A,
3171 "SR1B", 0x1B,
3172 "SR1C", 0x1C,
3173 "SR1D", 0x1D,
3174 "SR1E", 0x1E,
3175 "SR1F", 0x1F,
3176 NULL);
3177
Krzysztof Helt8503df62007-10-16 01:29:08 -07003178 DPRINTK("\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003179}
3180
3181#endif /* CIRRUSFB_DEBUG */
3182