blob: 65a1831ddd0d980d5b531cab03a77a079ad467db [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * drivers/video/cirrusfb.c - driver for Cirrus Logic chipsets
3 *
4 * Copyright 1999-2001 Jeff Garzik <jgarzik@pobox.com>
5 *
6 * Contributors (thanks, all!)
7 *
8 * David Eger:
9 * Overhaul for Linux 2.6
10 *
11 * Jeff Rugen:
12 * Major contributions; Motorola PowerStack (PPC and PCI) support,
13 * GD54xx, 1280x1024 mode support, change MCLK based on VCLK.
14 *
15 * Geert Uytterhoeven:
16 * Excellent code review.
17 *
18 * Lars Hecking:
19 * Amiga updates and testing.
20 *
21 * Original cirrusfb author: Frank Neumann
22 *
23 * Based on retz3fb.c and cirrusfb.c:
24 * Copyright (C) 1997 Jes Sorensen
25 * Copyright (C) 1996 Frank Neumann
26 *
27 ***************************************************************
28 *
29 * Format this code with GNU indent '-kr -i8 -pcs' options.
30 *
31 * This file is subject to the terms and conditions of the GNU General Public
32 * License. See the file COPYING in the main directory of this archive
33 * for more details.
34 *
35 */
36
Linus Torvalds1da177e2005-04-16 15:20:36 -070037#include <linux/module.h>
38#include <linux/kernel.h>
39#include <linux/errno.h>
40#include <linux/string.h>
41#include <linux/mm.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070042#include <linux/slab.h>
43#include <linux/delay.h>
44#include <linux/fb.h>
45#include <linux/init.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070046#include <asm/pgtable.h>
47
48#ifdef CONFIG_ZORRO
49#include <linux/zorro.h>
50#endif
51#ifdef CONFIG_PCI
52#include <linux/pci.h>
53#endif
54#ifdef CONFIG_AMIGA
55#include <asm/amigahw.h>
56#endif
57#ifdef CONFIG_PPC_PREP
Benjamin Herrenschmidte8222502006-03-28 23:15:54 +110058#include <asm/machdep.h>
Krzysztof Helt8503df62007-10-16 01:29:08 -070059#define isPReP machine_is(prep)
Linus Torvalds1da177e2005-04-16 15:20:36 -070060#else
61#define isPReP 0
62#endif
63
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -070064#include <video/vga.h>
65#include <video/cirrus.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070066
Linus Torvalds1da177e2005-04-16 15:20:36 -070067/*****************************************************************
68 *
69 * debugging and utility macros
70 *
71 */
72
Linus Torvalds1da177e2005-04-16 15:20:36 -070073/* disable runtime assertions? */
74/* #define CIRRUSFB_NDEBUG */
75
Linus Torvalds1da177e2005-04-16 15:20:36 -070076/* debugging assertions */
77#ifndef CIRRUSFB_NDEBUG
78#define assert(expr) \
Krzysztof Helt8503df62007-10-16 01:29:08 -070079 if (!(expr)) { \
80 printk("Assertion failed! %s,%s,%s,line=%d\n", \
Harvey Harrison5ae12172008-04-28 02:15:47 -070081 #expr, __FILE__, __func__, __LINE__); \
Krzysztof Helt8503df62007-10-16 01:29:08 -070082 }
Linus Torvalds1da177e2005-04-16 15:20:36 -070083#else
84#define assert(expr)
85#endif
86
Krzysztof Helt8503df62007-10-16 01:29:08 -070087#define MB_ (1024 * 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -070088
Linus Torvalds1da177e2005-04-16 15:20:36 -070089/*****************************************************************
90 *
91 * chipset information
92 *
93 */
94
95/* board types */
Krzysztof Helt7345de32007-10-16 01:29:11 -070096enum cirrus_board {
Linus Torvalds1da177e2005-04-16 15:20:36 -070097 BT_NONE = 0,
Krzysztof Helt7cade312009-03-31 15:25:13 -070098 BT_SD64, /* GD5434 */
99 BT_PICCOLO, /* GD5426 */
100 BT_PICASSO, /* GD5426 or GD5428 */
101 BT_SPECTRUM, /* GD5426 or GD5428 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700102 BT_PICASSO4, /* GD5446 */
103 BT_ALPINE, /* GD543x/4x */
104 BT_GD5480,
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700105 BT_LAGUNA, /* GD5462/64 */
106 BT_LAGUNAB, /* GD5465 */
Krzysztof Helt7345de32007-10-16 01:29:11 -0700107};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109/*
110 * per-board-type information, used for enumerating and abstracting
111 * chip-specific information
Krzysztof Helt7345de32007-10-16 01:29:11 -0700112 * NOTE: MUST be in the same order as enum cirrus_board in order to
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113 * use direct indexing on this array
114 * NOTE: '__initdata' cannot be used as some of this info
115 * is required at runtime. Maybe separate into an init-only and
116 * a run-time table?
117 */
118static const struct cirrusfb_board_info_rec {
119 char *name; /* ASCII name of chipset */
120 long maxclock[5]; /* maximum video clock */
121 /* for 1/4bpp, 8bpp 15/16bpp, 24bpp, 32bpp - numbers from xorg code */
Richard Knutssonc930faa2007-05-08 00:38:29 -0700122 bool init_sr07 : 1; /* init SR07 during init_vgachip() */
123 bool init_sr1f : 1; /* write SR1F during init_vgachip() */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700124 /* construct bit 19 of screen start address */
125 bool scrn_start_bit19 : 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126
127 /* initial SR07 value, then for each mode */
128 unsigned char sr07;
129 unsigned char sr07_1bpp;
130 unsigned char sr07_1bpp_mux;
131 unsigned char sr07_8bpp;
132 unsigned char sr07_8bpp_mux;
133
134 unsigned char sr1f; /* SR1F VGA initial register value */
135} cirrusfb_board_info[] = {
136 [BT_SD64] = {
137 .name = "CL SD64",
138 .maxclock = {
139 /* guess */
140 /* the SD64/P4 have a higher max. videoclock */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700141 135100, 135100, 85500, 85500, 0
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700143 .init_sr07 = true,
144 .init_sr1f = true,
145 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 .sr07 = 0xF0,
147 .sr07_1bpp = 0xF0,
148 .sr07_8bpp = 0xF1,
149 .sr1f = 0x20
150 },
151 [BT_PICCOLO] = {
152 .name = "CL Piccolo",
153 .maxclock = {
154 /* guess */
155 90000, 90000, 90000, 90000, 90000
156 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700157 .init_sr07 = true,
158 .init_sr1f = true,
159 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160 .sr07 = 0x80,
161 .sr07_1bpp = 0x80,
162 .sr07_8bpp = 0x81,
163 .sr1f = 0x22
164 },
165 [BT_PICASSO] = {
166 .name = "CL Picasso",
167 .maxclock = {
168 /* guess */
169 90000, 90000, 90000, 90000, 90000
170 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700171 .init_sr07 = true,
172 .init_sr1f = true,
173 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 .sr07 = 0x20,
175 .sr07_1bpp = 0x20,
176 .sr07_8bpp = 0x21,
177 .sr1f = 0x22
178 },
179 [BT_SPECTRUM] = {
180 .name = "CL Spectrum",
181 .maxclock = {
182 /* guess */
183 90000, 90000, 90000, 90000, 90000
184 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700185 .init_sr07 = true,
186 .init_sr1f = true,
187 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188 .sr07 = 0x80,
189 .sr07_1bpp = 0x80,
190 .sr07_8bpp = 0x81,
191 .sr1f = 0x22
192 },
193 [BT_PICASSO4] = {
194 .name = "CL Picasso4",
195 .maxclock = {
196 135100, 135100, 85500, 85500, 0
197 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700198 .init_sr07 = true,
199 .init_sr1f = false,
200 .scrn_start_bit19 = true,
Krzysztof Helt527410f2009-03-31 15:25:13 -0700201 .sr07 = 0xA0,
202 .sr07_1bpp = 0xA0,
203 .sr07_1bpp_mux = 0xA6,
204 .sr07_8bpp = 0xA1,
205 .sr07_8bpp_mux = 0xA7,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206 .sr1f = 0
207 },
208 [BT_ALPINE] = {
209 .name = "CL Alpine",
210 .maxclock = {
211 /* for the GD5430. GD5446 can do more... */
212 85500, 85500, 50000, 28500, 0
213 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700214 .init_sr07 = true,
215 .init_sr1f = true,
216 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217 .sr07 = 0xA0,
Krzysztof Helt527410f2009-03-31 15:25:13 -0700218 .sr07_1bpp = 0xA0,
219 .sr07_1bpp_mux = 0xA6,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220 .sr07_8bpp = 0xA1,
221 .sr07_8bpp_mux = 0xA7,
222 .sr1f = 0x1C
223 },
224 [BT_GD5480] = {
225 .name = "CL GD5480",
226 .maxclock = {
227 135100, 200000, 200000, 135100, 135100
228 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700229 .init_sr07 = true,
230 .init_sr1f = true,
231 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232 .sr07 = 0x10,
233 .sr07_1bpp = 0x11,
234 .sr07_8bpp = 0x11,
235 .sr1f = 0x1C
236 },
237 [BT_LAGUNA] = {
238 .name = "CL Laguna",
239 .maxclock = {
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700240 /* taken from X11 code */
241 170000, 170000, 170000, 170000, 135100,
242 },
243 .init_sr07 = false,
244 .init_sr1f = false,
245 .scrn_start_bit19 = true,
246 },
247 [BT_LAGUNAB] = {
248 .name = "CL Laguna AGP",
249 .maxclock = {
250 /* taken from X11 code */
251 170000, 250000, 170000, 170000, 135100,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700253 .init_sr07 = false,
254 .init_sr1f = false,
255 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256 }
257};
258
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259#ifdef CONFIG_PCI
260#define CHIP(id, btype) \
Grant Coady41538122005-09-29 10:40:52 +1000261 { PCI_VENDOR_ID_CIRRUS, id, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (btype) }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262
263static struct pci_device_id cirrusfb_pci_table[] = {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700264 CHIP(PCI_DEVICE_ID_CIRRUS_5436, BT_ALPINE),
265 CHIP(PCI_DEVICE_ID_CIRRUS_5434_8, BT_ALPINE),
266 CHIP(PCI_DEVICE_ID_CIRRUS_5434_4, BT_ALPINE),
267 CHIP(PCI_DEVICE_ID_CIRRUS_5430, BT_ALPINE), /* GD-5440 is same id */
268 CHIP(PCI_DEVICE_ID_CIRRUS_7543, BT_ALPINE),
269 CHIP(PCI_DEVICE_ID_CIRRUS_7548, BT_ALPINE),
270 CHIP(PCI_DEVICE_ID_CIRRUS_5480, BT_GD5480), /* MacPicasso likely */
271 CHIP(PCI_DEVICE_ID_CIRRUS_5446, BT_PICASSO4), /* Picasso 4 is 5446 */
272 CHIP(PCI_DEVICE_ID_CIRRUS_5462, BT_LAGUNA), /* CL Laguna */
273 CHIP(PCI_DEVICE_ID_CIRRUS_5464, BT_LAGUNA), /* CL Laguna 3D */
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700274 CHIP(PCI_DEVICE_ID_CIRRUS_5465, BT_LAGUNAB), /* CL Laguna 3DA*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275 { 0, }
276};
277MODULE_DEVICE_TABLE(pci, cirrusfb_pci_table);
278#undef CHIP
279#endif /* CONFIG_PCI */
280
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281#ifdef CONFIG_ZORRO
282static const struct zorro_device_id cirrusfb_zorro_table[] = {
283 {
284 .id = ZORRO_PROD_HELFRICH_SD64_RAM,
285 .driver_data = BT_SD64,
286 }, {
287 .id = ZORRO_PROD_HELFRICH_PICCOLO_RAM,
288 .driver_data = BT_PICCOLO,
289 }, {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700290 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_RAM,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 .driver_data = BT_PICASSO,
292 }, {
293 .id = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_RAM,
294 .driver_data = BT_SPECTRUM,
295 }, {
296 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z3,
297 .driver_data = BT_PICASSO4,
298 },
299 { 0 }
300};
301
302static const struct {
303 zorro_id id2;
304 unsigned long size;
305} cirrusfb_zorro_table2[] = {
306 [BT_SD64] = {
307 .id2 = ZORRO_PROD_HELFRICH_SD64_REG,
308 .size = 0x400000
309 },
310 [BT_PICCOLO] = {
311 .id2 = ZORRO_PROD_HELFRICH_PICCOLO_REG,
312 .size = 0x200000
313 },
314 [BT_PICASSO] = {
315 .id2 = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_REG,
316 .size = 0x200000
317 },
318 [BT_SPECTRUM] = {
319 .id2 = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_REG,
320 .size = 0x200000
321 },
322 [BT_PICASSO4] = {
323 .id2 = 0,
324 .size = 0x400000
325 }
326};
327#endif /* CONFIG_ZORRO */
328
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329#ifdef CIRRUSFB_DEBUG
Krzysztof Helt7345de32007-10-16 01:29:11 -0700330enum cirrusfb_dbg_reg_class {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700331 CRT,
332 SEQ
Krzysztof Helt7345de32007-10-16 01:29:11 -0700333};
Krzysztof Helt8503df62007-10-16 01:29:08 -0700334#endif /* CIRRUSFB_DEBUG */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335
336/* info about board */
337struct cirrusfb_info {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 u8 __iomem *regbase;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700339 u8 __iomem *laguna_mmio;
Krzysztof Helt7345de32007-10-16 01:29:11 -0700340 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341 unsigned char SFR; /* Shadow of special function register */
342
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700343 int multiplexing;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 int blank_mode;
Krzysztof Helt64beab12008-10-15 22:03:38 -0700345 u32 pseudo_palette[16];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700347 void (*unmap)(struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348};
349
Krzysztof Helt55a0dd82008-10-15 22:03:41 -0700350static int noaccel __devinitdata;
Krzysztof Helta1d35a72008-10-15 22:03:38 -0700351static char *mode_option __devinitdata = "640x480@60";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352
353/****************************************************************************/
354/**** BEGIN PROTOTYPES ******************************************************/
355
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356/*--- Interface used by the world ------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700357static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
358 struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360/*--- Internal routines ----------------------------------------------------*/
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700361static void init_vgachip(struct fb_info *info);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700362static void switch_monitor(struct cirrusfb_info *cinfo, int on);
363static void WGen(const struct cirrusfb_info *cinfo,
364 int regnum, unsigned char val);
365static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum);
366static void AttrOn(const struct cirrusfb_info *cinfo);
367static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val);
368static void WSFR(struct cirrusfb_info *cinfo, unsigned char val);
369static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val);
370static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum,
371 unsigned char red, unsigned char green, unsigned char blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372#if 0
Krzysztof Helt8503df62007-10-16 01:29:08 -0700373static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum,
374 unsigned char *red, unsigned char *green,
375 unsigned char *blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -0700377static void cirrusfb_WaitBLT(u8 __iomem *regbase);
378static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
379 u_short curx, u_short cury,
380 u_short destx, u_short desty,
381 u_short width, u_short height,
382 u_short line_length);
383static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
384 u_short x, u_short y,
385 u_short width, u_short height,
Krzysztof Helt9e848062009-03-31 15:25:11 -0700386 u32 fg_color, u32 bg_color,
387 u_short line_length, u_char blitmode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700389static void bestclock(long freq, int *nom, int *den, int *div);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390
391#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700392static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase);
393static void cirrusfb_dbg_print_regs(struct fb_info *info,
394 caddr_t regbase,
Krzysztof Helt7345de32007-10-16 01:29:11 -0700395 enum cirrusfb_dbg_reg_class reg_class, ...);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700396#endif /* CIRRUSFB_DEBUG */
397
398/*** END PROTOTYPES ********************************************************/
399/*****************************************************************************/
400/*** BEGIN Interface Used by the World ***************************************/
401
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700402static inline int is_laguna(const struct cirrusfb_info *cinfo)
403{
404 return cinfo->btype == BT_LAGUNA || cinfo->btype == BT_LAGUNAB;
405}
406
Krzysztof Helt8503df62007-10-16 01:29:08 -0700407static int opencount;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408
409/*--- Open /dev/fbx ---------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700410static int cirrusfb_open(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411{
412 if (opencount++ == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700413 switch_monitor(info->par, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414 return 0;
415}
416
417/*--- Close /dev/fbx --------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700418static int cirrusfb_release(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419{
420 if (--opencount == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700421 switch_monitor(info->par, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422 return 0;
423}
424
425/**** END Interface used by the World *************************************/
426/****************************************************************************/
427/**** BEGIN Hardware specific Routines **************************************/
428
Krzysztof Helt486ff382008-10-15 22:03:42 -0700429/* Check if the MCLK is not a better clock source */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700430static int cirrusfb_check_mclk(struct fb_info *info, long freq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700432 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700433 long mclk = vga_rseq(cinfo->regbase, CL_SEQR1F) & 0x3f;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434
Krzysztof Helt486ff382008-10-15 22:03:42 -0700435 /* Read MCLK value */
436 mclk = (14318 * mclk) >> 3;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700437 dev_dbg(info->device, "Read MCLK of %ld kHz\n", mclk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700438
439 /* Determine if we should use MCLK instead of VCLK, and if so, what we
Krzysztof Helt486ff382008-10-15 22:03:42 -0700440 * should divide it by to get VCLK
441 */
442
443 if (abs(freq - mclk) < 250) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700444 dev_dbg(info->device, "Using VCLK = MCLK\n");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700445 return 1;
446 } else if (abs(freq - (mclk / 2)) < 250) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700447 dev_dbg(info->device, "Using VCLK = MCLK/2\n");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700448 return 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449 }
450
Krzysztof Helt486ff382008-10-15 22:03:42 -0700451 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452}
453
Krzysztof Helt99a45842009-03-31 15:25:09 -0700454static int cirrusfb_check_pixclock(const struct fb_var_screeninfo *var,
455 struct fb_info *info)
456{
457 long freq;
458 long maxclock;
459 struct cirrusfb_info *cinfo = info->par;
460 unsigned maxclockidx = var->bits_per_pixel >> 3;
461
462 /* convert from ps to kHz */
463 freq = PICOS2KHZ(var->pixclock);
464
465 dev_dbg(info->device, "desired pixclock: %ld kHz\n", freq);
466
467 maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx];
468 cinfo->multiplexing = 0;
469
470 /* If the frequency is greater than we can support, we might be able
471 * to use multiplexing for the video mode */
472 if (freq > maxclock) {
473 switch (cinfo->btype) {
474 case BT_ALPINE:
475 case BT_GD5480:
476 cinfo->multiplexing = 1;
477 break;
478
479 default:
480 dev_err(info->device,
481 "Frequency greater than maxclock (%ld kHz)\n",
482 maxclock);
483 return -EINVAL;
484 }
485 }
486#if 0
487 /* TODO: If we have a 1MB 5434, we need to put ourselves in a mode where
488 * the VCLK is double the pixel clock. */
489 switch (var->bits_per_pixel) {
490 case 16:
Krzysztof Helt7cade312009-03-31 15:25:13 -0700491 case 24:
Krzysztof Helt99a45842009-03-31 15:25:09 -0700492 if (var->xres <= 800)
493 /* Xbh has this type of clock for 32-bit */
494 freq /= 2;
495 break;
496 }
497#endif
498 return 0;
499}
500
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501static int cirrusfb_check_var(struct fb_var_screeninfo *var,
502 struct fb_info *info)
503{
Krzysztof Helt09a29102008-09-02 14:35:51 -0700504 int yres;
505 /* memory size in pixels */
506 unsigned pixels = info->screen_size * 8 / var->bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507
508 switch (var->bits_per_pixel) {
Krzysztof Helt060b6002007-10-16 01:29:13 -0700509 case 1:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510 var->red.offset = 0;
511 var->red.length = 1;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700512 var->green = var->red;
513 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514 break;
515
516 case 8:
517 var->red.offset = 0;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700518 var->red.length = 8;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700519 var->green = var->red;
520 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521 break;
522
523 case 16:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700524 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525 var->red.offset = 2;
526 var->green.offset = -3;
527 var->blue.offset = 8;
528 } else {
Krzysztof Heltc4dec392009-03-31 15:25:07 -0700529 var->red.offset = 11;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530 var->green.offset = 5;
531 var->blue.offset = 0;
532 }
533 var->red.length = 5;
Krzysztof Heltc4dec392009-03-31 15:25:07 -0700534 var->green.length = 6;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 var->blue.length = 5;
536 break;
537
Krzysztof Helt7cade312009-03-31 15:25:13 -0700538 case 24:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700539 if (isPReP) {
Krzysztof Helt7cade312009-03-31 15:25:13 -0700540 var->red.offset = 0;
541 var->green.offset = 8;
542 var->blue.offset = 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 } else {
544 var->red.offset = 16;
545 var->green.offset = 8;
546 var->blue.offset = 0;
547 }
548 var->red.length = 8;
549 var->green.length = 8;
550 var->blue.length = 8;
551 break;
552
553 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700554 dev_dbg(info->device,
555 "Unsupported bpp size: %d\n", var->bits_per_pixel);
Richard Knutssonc930faa2007-05-08 00:38:29 -0700556 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557 /* should never occur */
558 break;
559 }
560
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700561 if (var->xres_virtual < var->xres)
562 var->xres_virtual = var->xres;
563 /* use highest possible virtual resolution */
564 if (var->yres_virtual == -1) {
565 var->yres_virtual = pixels / var->xres_virtual;
566
567 dev_info(info->device,
568 "virtual resolution set to maximum of %dx%d\n",
569 var->xres_virtual, var->yres_virtual);
570 }
571 if (var->yres_virtual < var->yres)
572 var->yres_virtual = var->yres;
573
574 if (var->xres_virtual * var->yres_virtual > pixels) {
575 dev_err(info->device, "mode %dx%dx%d rejected... "
576 "virtual resolution too high to fit into video memory!\n",
577 var->xres_virtual, var->yres_virtual,
578 var->bits_per_pixel);
579 return -EINVAL;
580 }
581
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700582 if (var->xoffset < 0)
583 var->xoffset = 0;
584 if (var->yoffset < 0)
585 var->yoffset = 0;
586
587 /* truncate xoffset and yoffset to maximum if too high */
588 if (var->xoffset > var->xres_virtual - var->xres)
589 var->xoffset = var->xres_virtual - var->xres - 1;
590 if (var->yoffset > var->yres_virtual - var->yres)
591 var->yoffset = var->yres_virtual - var->yres - 1;
592
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593 var->red.msb_right =
594 var->green.msb_right =
595 var->blue.msb_right =
596 var->transp.offset =
597 var->transp.length =
598 var->transp.msb_right = 0;
599
600 yres = var->yres;
601 if (var->vmode & FB_VMODE_DOUBLE)
602 yres *= 2;
603 else if (var->vmode & FB_VMODE_INTERLACED)
604 yres = (yres + 1) / 2;
605
606 if (yres >= 1280) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700607 dev_err(info->device, "ERROR: VerticalTotal >= 1280; "
Krzysztof Helt8503df62007-10-16 01:29:08 -0700608 "special treatment required! (TODO)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609 return -EINVAL;
610 }
611
Krzysztof Helt99a45842009-03-31 15:25:09 -0700612 if (cirrusfb_check_pixclock(var, info))
613 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615 return 0;
616}
617
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700618static void cirrusfb_set_mclk_as_source(const struct fb_info *info, int div)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700620 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700621 unsigned char old1f, old1e;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700622
Krzysztof Helt8503df62007-10-16 01:29:08 -0700623 assert(cinfo != NULL);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700624 old1f = vga_rseq(cinfo->regbase, CL_SEQR1F) & ~0x40;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625
Krzysztof Helt486ff382008-10-15 22:03:42 -0700626 if (div) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700627 dev_dbg(info->device, "Set %s as pixclock source.\n",
628 (div == 2) ? "MCLK/2" : "MCLK");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700629 old1f |= 0x40;
630 old1e = vga_rseq(cinfo->regbase, CL_SEQR1E) & ~0x1;
631 if (div == 2)
632 old1e |= 1;
633
634 vga_wseq(cinfo->regbase, CL_SEQR1E, old1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700636 vga_wseq(cinfo->regbase, CL_SEQR1F, old1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700637}
638
639/*************************************************************************
640 cirrusfb_set_par_foo()
641
642 actually writes the values for a new video mode into the hardware,
643**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700644static int cirrusfb_set_par_foo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645{
646 struct cirrusfb_info *cinfo = info->par;
647 struct fb_var_screeninfo *var = &info->var;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648 u8 __iomem *regbase = cinfo->regbase;
649 unsigned char tmp;
Krzysztof Helt6683e012009-03-31 15:25:06 -0700650 int pitch;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651 const struct cirrusfb_board_info_rec *bi;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700652 int hdispend, hsyncstart, hsyncend, htotal;
653 int yres, vdispend, vsyncstart, vsyncend, vtotal;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700654 long freq;
655 int nom, den, div;
Krzysztof Helt1b48cb52009-03-31 15:25:08 -0700656 unsigned int control = 0, format = 0, threshold = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700658 dev_dbg(info->device, "Requested mode: %dx%dx%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt99a45842009-03-31 15:25:09 -0700660
661 switch (var->bits_per_pixel) {
662 case 1:
663 info->fix.line_length = var->xres_virtual / 8;
664 info->fix.visual = FB_VISUAL_MONO10;
665 break;
666
667 case 8:
668 info->fix.line_length = var->xres_virtual;
669 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
670 break;
671
672 case 16:
Krzysztof Helt7cade312009-03-31 15:25:13 -0700673 case 24:
Krzysztof Helt99a45842009-03-31 15:25:09 -0700674 info->fix.line_length = var->xres_virtual *
675 var->bits_per_pixel >> 3;
676 info->fix.visual = FB_VISUAL_TRUECOLOR;
677 break;
678 }
679 info->fix.type = FB_TYPE_PACKED_PIXELS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700681 init_vgachip(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683 bi = &cirrusfb_board_info[cinfo->btype];
684
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700685 hsyncstart = var->xres + var->right_margin;
686 hsyncend = hsyncstart + var->hsync_len;
687 htotal = (hsyncend + var->left_margin) / 8 - 5;
688 hdispend = var->xres / 8 - 1;
689 hsyncstart = hsyncstart / 8 + 1;
690 hsyncend = hsyncend / 8 + 1;
691
692 yres = var->yres;
693 vsyncstart = yres + var->lower_margin;
694 vsyncend = vsyncstart + var->vsync_len;
695 vtotal = vsyncend + var->upper_margin;
696 vdispend = yres - 1;
697
698 if (var->vmode & FB_VMODE_DOUBLE) {
699 yres *= 2;
700 vsyncstart *= 2;
701 vsyncend *= 2;
702 vtotal *= 2;
703 } else if (var->vmode & FB_VMODE_INTERLACED) {
704 yres = (yres + 1) / 2;
705 vsyncstart = (vsyncstart + 1) / 2;
706 vsyncend = (vsyncend + 1) / 2;
707 vtotal = (vtotal + 1) / 2;
708 }
709
710 vtotal -= 2;
711 vsyncstart -= 1;
712 vsyncend -= 1;
713
714 if (yres >= 1024) {
715 vtotal /= 2;
716 vsyncstart /= 2;
717 vsyncend /= 2;
718 vdispend /= 2;
719 }
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700720 if (cinfo->multiplexing) {
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700721 htotal /= 2;
722 hsyncstart /= 2;
723 hsyncend /= 2;
724 hdispend /= 2;
725 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726 /* unlock register VGA_CRTC_H_TOTAL..CRT7 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700727 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20); /* previously: 0x00) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728
729 /* if debugging is enabled, all parameters get output before writing */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700730 dev_dbg(info->device, "CRT0: %d\n", htotal);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700731 vga_wcrt(regbase, VGA_CRTC_H_TOTAL, htotal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700733 dev_dbg(info->device, "CRT1: %d\n", hdispend);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700734 vga_wcrt(regbase, VGA_CRTC_H_DISP, hdispend);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700736 dev_dbg(info->device, "CRT2: %d\n", var->xres / 8);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700737 vga_wcrt(regbase, VGA_CRTC_H_BLANK_START, var->xres / 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738
Krzysztof Helt8503df62007-10-16 01:29:08 -0700739 /* + 128: Compatible read */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700740 dev_dbg(info->device, "CRT3: 128+%d\n", (htotal + 5) % 32);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700741 vga_wcrt(regbase, VGA_CRTC_H_BLANK_END,
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700742 128 + ((htotal + 5) % 32));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700744 dev_dbg(info->device, "CRT4: %d\n", hsyncstart);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700745 vga_wcrt(regbase, VGA_CRTC_H_SYNC_START, hsyncstart);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700746
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700747 tmp = hsyncend % 32;
748 if ((htotal + 5) & 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749 tmp += 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700750 dev_dbg(info->device, "CRT5: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700751 vga_wcrt(regbase, VGA_CRTC_H_SYNC_END, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700753 dev_dbg(info->device, "CRT6: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700754 vga_wcrt(regbase, VGA_CRTC_V_TOTAL, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755
756 tmp = 16; /* LineCompare bit #9 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700757 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700759 if (vdispend & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760 tmp |= 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700761 if (vsyncstart & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762 tmp |= 4;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700763 if ((vdispend + 1) & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764 tmp |= 8;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700765 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700767 if (vdispend & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700769 if (vsyncstart & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770 tmp |= 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700771 dev_dbg(info->device, "CRT7: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700772 vga_wcrt(regbase, VGA_CRTC_OVERFLOW, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773
774 tmp = 0x40; /* LineCompare bit #8 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700775 if ((vdispend + 1) & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776 tmp |= 0x20;
777 if (var->vmode & FB_VMODE_DOUBLE)
778 tmp |= 0x80;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700779 dev_dbg(info->device, "CRT9: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700780 vga_wcrt(regbase, VGA_CRTC_MAX_SCAN, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700782 dev_dbg(info->device, "CRT10: %d\n", vsyncstart & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700783 vga_wcrt(regbase, VGA_CRTC_V_SYNC_START, vsyncstart & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700785 dev_dbg(info->device, "CRT11: 64+32+%d\n", vsyncend % 16);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700786 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, vsyncend % 16 + 64 + 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700788 dev_dbg(info->device, "CRT12: %d\n", vdispend & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700789 vga_wcrt(regbase, VGA_CRTC_V_DISP_END, vdispend & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700791 dev_dbg(info->device, "CRT15: %d\n", (vdispend + 1) & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700792 vga_wcrt(regbase, VGA_CRTC_V_BLANK_START, (vdispend + 1) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700794 dev_dbg(info->device, "CRT16: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700795 vga_wcrt(regbase, VGA_CRTC_V_BLANK_END, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700797 dev_dbg(info->device, "CRT18: 0xff\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700798 vga_wcrt(regbase, VGA_CRTC_LINE_COMPARE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799
800 tmp = 0;
801 if (var->vmode & FB_VMODE_INTERLACED)
802 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700803 if ((htotal + 5) & 64)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804 tmp |= 16;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700805 if ((htotal + 5) & 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700807 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700809 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810 tmp |= 128;
811
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700812 dev_dbg(info->device, "CRT1a: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700813 vga_wcrt(regbase, CL_CRT1A, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700815 freq = PICOS2KHZ(var->pixclock);
Krzysztof Helt7cade312009-03-31 15:25:13 -0700816 if (cinfo->btype == BT_ALPINE && var->bits_per_pixel == 24)
817 freq *= 3;
818
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700819 bestclock(freq, &nom, &den, &div);
820
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700821 dev_dbg(info->device, "VCLK freq: %ld kHz nom: %d den: %d div: %d\n",
822 freq, nom, den, div);
823
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824 /* set VCLK0 */
825 /* hardware RefClock: 14.31818 MHz */
826 /* formula: VClk = (OSC * N) / (D * (1+P)) */
827 /* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */
828
Krzysztof Helt527410f2009-03-31 15:25:13 -0700829 if (cinfo->btype == BT_ALPINE || cinfo->btype == BT_PICASSO4) {
Krzysztof Helt486ff382008-10-15 22:03:42 -0700830 /* if freq is close to mclk or mclk/2 select mclk
831 * as clock source
832 */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700833 int divMCLK = cirrusfb_check_mclk(info, freq);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700834 if (divMCLK) {
835 nom = 0;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700836 cirrusfb_set_mclk_as_source(info, divMCLK);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700837 }
838 }
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700839 if (is_laguna(cinfo)) {
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700840 long pcifc = fb_readl(cinfo->laguna_mmio + 0x3fc);
841 unsigned char tile = fb_readb(cinfo->laguna_mmio + 0x407);
842 unsigned short tile_control;
843
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700844 if (cinfo->btype == BT_LAGUNAB) {
845 tile_control = fb_readw(cinfo->laguna_mmio + 0x2c4);
846 tile_control &= ~0x80;
847 fb_writew(tile_control, cinfo->laguna_mmio + 0x2c4);
848 }
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700849
850 fb_writel(pcifc | 0x10000000l, cinfo->laguna_mmio + 0x3fc);
851 fb_writeb(tile & 0x3f, cinfo->laguna_mmio + 0x407);
852 control = fb_readw(cinfo->laguna_mmio + 0x402);
853 threshold = fb_readw(cinfo->laguna_mmio + 0xea);
854 control &= ~0x6800;
855 format = 0;
Krzysztof Helt1b48cb52009-03-31 15:25:08 -0700856 threshold &= 0xffe0 & 0x3fbf;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700857 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700858 if (nom) {
Krzysztof Helt486ff382008-10-15 22:03:42 -0700859 tmp = den << 1;
860 if (div != 0)
861 tmp |= 1;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700862 /* 6 bit denom; ONLY 5434!!! (bugged me 10 days) */
863 if ((cinfo->btype == BT_SD64) ||
864 (cinfo->btype == BT_ALPINE) ||
865 (cinfo->btype == BT_GD5480))
866 tmp |= 0x80;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700867
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700868 dev_dbg(info->device, "CL_SEQR1B: %d\n", (int) tmp);
869 /* Laguna chipset has reversed clock registers */
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700870 if (is_laguna(cinfo)) {
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700871 vga_wseq(regbase, CL_SEQRE, tmp);
872 vga_wseq(regbase, CL_SEQR1E, nom);
873 } else {
874 vga_wseq(regbase, CL_SEQRB, nom);
875 vga_wseq(regbase, CL_SEQR1B, tmp);
876 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700877 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700878
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700879 if (yres >= 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880 /* 1280x1024 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700881 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700882 else
883 /* mode control: VGA_CRTC_START_HI enable, ROTATE(?), 16bit
884 * address wrap, no compat. */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700885 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700886
Linus Torvalds1da177e2005-04-16 15:20:36 -0700887 /* don't know if it would hurt to also program this if no interlaced */
888 /* mode is used, but I feel better this way.. :-) */
889 if (var->vmode & FB_VMODE_INTERLACED)
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700890 vga_wcrt(regbase, VGA_CRTC_REGS, htotal / 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891 else
Krzysztof Helt8503df62007-10-16 01:29:08 -0700892 vga_wcrt(regbase, VGA_CRTC_REGS, 0x00); /* interlace control */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700893
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894 /* adjust horizontal/vertical sync type (low/high) */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700895 /* enable display memory & CRTC I/O address for color mode */
896 tmp = 0x03;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700897 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
898 tmp |= 0x40;
899 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
900 tmp |= 0x80;
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700901 if (is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -0700902 tmp |= 0xc;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700903 WGen(cinfo, VGA_MIS_W, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904
Krzysztof Helt8503df62007-10-16 01:29:08 -0700905 /* text cursor on and start line */
906 vga_wcrt(regbase, VGA_CRTC_CURSOR_START, 0);
907 /* text cursor end line */
908 vga_wcrt(regbase, VGA_CRTC_CURSOR_END, 31);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700909
910 /******************************************************
911 *
912 * 1 bpp
913 *
914 */
915
916 /* programming for different color depths */
917 if (var->bits_per_pixel == 1) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700918 dev_dbg(info->device, "preparing for 1 bit deep display\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700919 vga_wgfx(regbase, VGA_GFX_MODE, 0); /* mode register */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920
921 /* SR07 */
922 switch (cinfo->btype) {
923 case BT_SD64:
924 case BT_PICCOLO:
925 case BT_PICASSO:
926 case BT_SPECTRUM:
927 case BT_PICASSO4:
928 case BT_ALPINE:
929 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700930 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700931 cinfo->multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700932 bi->sr07_1bpp_mux : bi->sr07_1bpp);
933 break;
934
935 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700936 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700937 vga_wseq(regbase, CL_SEQR7,
938 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700939 break;
940
941 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700942 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700943 break;
944 }
945
946 /* Extended Sequencer Mode */
947 switch (cinfo->btype) {
948 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700949 /* setting the SEQRF on SD64 is not necessary
950 * (only during init)
951 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700952 /* MCLK select */
953 vga_wseq(regbase, CL_SEQR1F, 0x1a);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954 break;
955
956 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -0700957 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700958 /* ### ueberall 0x22? */
959 /* ##vorher 1c MCLK select */
960 vga_wseq(regbase, CL_SEQR1F, 0x22);
961 /* evtl d0 bei 1 bit? avoid FIFO underruns..? */
962 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700963 break;
964
965 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700966 /* ##vorher 22 MCLK select */
967 vga_wseq(regbase, CL_SEQR1F, 0x22);
968 /* ## vorher d0 avoid FIFO underruns..? */
969 vga_wseq(regbase, CL_SEQRF, 0xd0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700970 break;
971
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972 case BT_PICASSO4:
973 case BT_ALPINE:
974 case BT_GD5480:
975 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700976 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977 /* do nothing */
978 break;
979
980 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700981 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700982 break;
983 }
984
Krzysztof Helt8503df62007-10-16 01:29:08 -0700985 /* pixel mask: pass-through for first plane */
986 WGen(cinfo, VGA_PEL_MSK, 0x01);
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700987 if (cinfo->multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700988 /* hidden dac reg: 1280x1024 */
989 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700990 else
Krzysztof Helt8503df62007-10-16 01:29:08 -0700991 /* hidden dac: nothing */
992 WHDR(cinfo, 0);
993 /* memory mode: odd/even, ext. memory */
994 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x06);
995 /* plane mask: only write to first plane */
996 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997 }
998
999 /******************************************************
1000 *
1001 * 8 bpp
1002 *
1003 */
1004
1005 else if (var->bits_per_pixel == 8) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001006 dev_dbg(info->device, "preparing for 8 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007 switch (cinfo->btype) {
1008 case BT_SD64:
1009 case BT_PICCOLO:
1010 case BT_PICASSO:
1011 case BT_SPECTRUM:
1012 case BT_PICASSO4:
1013 case BT_ALPINE:
1014 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001015 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001016 cinfo->multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017 bi->sr07_8bpp_mux : bi->sr07_8bpp);
1018 break;
1019
1020 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001021 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001022 vga_wseq(regbase, CL_SEQR7,
1023 vga_rseq(regbase, CL_SEQR7) | 0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001024 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001025 break;
1026
1027 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001028 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001029 break;
1030 }
1031
1032 switch (cinfo->btype) {
1033 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001034 /* MCLK select */
1035 vga_wseq(regbase, CL_SEQR1F, 0x1d);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036 break;
1037
1038 case BT_PICCOLO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001039 case BT_PICASSO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001040 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001041 /* ### vorher 1c MCLK select */
1042 vga_wseq(regbase, CL_SEQR1F, 0x22);
1043 /* Fast Page-Mode writes */
1044 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045 break;
1046
1047 case BT_PICASSO4:
1048#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001049 /* ### INCOMPLETE!! */
1050 vga_wseq(regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052 case BT_ALPINE:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053 /* We already set SRF and SR1F */
1054 break;
1055
1056 case BT_GD5480:
1057 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001058 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059 /* do nothing */
1060 break;
1061
1062 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001063 dev_warn(info->device, "unknown board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064 break;
1065 }
1066
Krzysztof Helt8503df62007-10-16 01:29:08 -07001067 /* mode register: 256 color mode */
1068 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001069 if (cinfo->multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001070 /* hidden dac reg: 1280x1024 */
1071 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001073 /* hidden dac: nothing */
1074 WHDR(cinfo, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001075 }
1076
1077 /******************************************************
1078 *
1079 * 16 bpp
1080 *
1081 */
1082
1083 else if (var->bits_per_pixel == 16) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001084 dev_dbg(info->device, "preparing for 16 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085 switch (cinfo->btype) {
1086 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001087 /* Extended Sequencer Mode: 256c col. mode */
1088 vga_wseq(regbase, CL_SEQR7, 0xf7);
1089 /* MCLK select */
1090 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091 break;
1092
1093 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001094 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001095 vga_wseq(regbase, CL_SEQR7, 0x87);
1096 /* Fast Page-Mode writes */
1097 vga_wseq(regbase, CL_SEQRF, 0xb0);
1098 /* MCLK select */
1099 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001100 break;
1101
1102 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001103 vga_wseq(regbase, CL_SEQR7, 0x27);
1104 /* Fast Page-Mode writes */
1105 vga_wseq(regbase, CL_SEQRF, 0xb0);
1106 /* MCLK select */
1107 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108 break;
1109
Linus Torvalds1da177e2005-04-16 15:20:36 -07001110 case BT_PICASSO4:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001111 case BT_ALPINE:
Krzysztof Helt3b921832008-10-15 22:03:41 -07001112 vga_wseq(regbase, CL_SEQR7, 0xa7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001113 break;
1114
1115 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001116 vga_wseq(regbase, CL_SEQR7, 0x17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001117 /* We already set SRF and SR1F */
1118 break;
1119
1120 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001121 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001122 vga_wseq(regbase, CL_SEQR7,
1123 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001124 control |= 0x2000;
1125 format |= 0x1400;
1126 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001127 break;
1128
1129 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001130 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131 break;
1132 }
1133
Krzysztof Helt8503df62007-10-16 01:29:08 -07001134 /* mode register: 256 color mode */
1135 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136#ifdef CONFIG_PCI
Krzysztof Heltc4dec392009-03-31 15:25:07 -07001137 WHDR(cinfo, 0xc1); /* Copy Xbh */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138#elif defined(CONFIG_ZORRO)
1139 /* FIXME: CONFIG_PCI and CONFIG_ZORRO may be defined both */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001140 WHDR(cinfo, 0xa0); /* hidden dac reg: nothing special */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001141#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142 }
1143
1144 /******************************************************
1145 *
Krzysztof Helt7cade312009-03-31 15:25:13 -07001146 * 24 bpp
Linus Torvalds1da177e2005-04-16 15:20:36 -07001147 *
1148 */
1149
Krzysztof Helt7cade312009-03-31 15:25:13 -07001150 else if (var->bits_per_pixel == 24) {
1151 dev_dbg(info->device, "preparing for 24 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001152 switch (cinfo->btype) {
1153 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001154 /* Extended Sequencer Mode: 256c col. mode */
Krzysztof Helt7cade312009-03-31 15:25:13 -07001155 vga_wseq(regbase, CL_SEQR7, 0xf5);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001156 /* MCLK select */
1157 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001158 break;
1159
1160 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001161 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001162 vga_wseq(regbase, CL_SEQR7, 0x85);
1163 /* Fast Page-Mode writes */
1164 vga_wseq(regbase, CL_SEQRF, 0xb0);
1165 /* MCLK select */
1166 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001167 break;
1168
1169 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001170 vga_wseq(regbase, CL_SEQR7, 0x25);
1171 /* Fast Page-Mode writes */
1172 vga_wseq(regbase, CL_SEQRF, 0xb0);
1173 /* MCLK select */
1174 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001175 break;
1176
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177 case BT_PICASSO4:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001178 case BT_ALPINE:
Krzysztof Helt7cade312009-03-31 15:25:13 -07001179 vga_wseq(regbase, CL_SEQR7, 0xa5);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001180 break;
1181
1182 case BT_GD5480:
Krzysztof Helt7cade312009-03-31 15:25:13 -07001183 vga_wseq(regbase, CL_SEQR7, 0x15);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001184 /* We already set SRF and SR1F */
1185 break;
1186
1187 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001188 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001189 vga_wseq(regbase, CL_SEQR7,
1190 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt7cade312009-03-31 15:25:13 -07001191 control |= 0x4000;
1192 format |= 0x2400;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001193 threshold |= 0x20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001194 break;
1195
1196 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001197 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001198 break;
1199 }
1200
Krzysztof Helt8503df62007-10-16 01:29:08 -07001201 /* mode register: 256 color mode */
1202 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001203 /* hidden dac reg: 8-8-8 mode (24 or 32) */
1204 WHDR(cinfo, 0xc5);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001205 }
1206
1207 /******************************************************
1208 *
1209 * unknown/unsupported bpp
1210 *
1211 */
1212
Krzysztof Helt8503df62007-10-16 01:29:08 -07001213 else
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001214 dev_err(info->device,
1215 "What's this? requested color depth == %d.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001216 var->bits_per_pixel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001217
Krzysztof Helt6683e012009-03-31 15:25:06 -07001218 pitch = info->fix.line_length >> 3;
1219 vga_wcrt(regbase, VGA_CRTC_OFFSET, pitch & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001220 tmp = 0x22;
Krzysztof Helt6683e012009-03-31 15:25:06 -07001221 if (pitch & 0x100)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001222 tmp |= 0x10; /* offset overflow bit */
1223
Krzysztof Helt8503df62007-10-16 01:29:08 -07001224 /* screen start addr #16-18, fastpagemode cycles */
1225 vga_wcrt(regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001226
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001227 /* screen start address bit 19 */
1228 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19)
Krzysztof Helt6683e012009-03-31 15:25:06 -07001229 vga_wcrt(regbase, CL_CRT1D, (pitch >> 9) & 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001230
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001231 if (is_laguna(cinfo)) {
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001232 tmp = 0;
1233 if ((htotal + 5) & 256)
1234 tmp |= 128;
1235 if (hdispend & 256)
1236 tmp |= 64;
1237 if (hsyncstart & 256)
1238 tmp |= 48;
1239 if (vtotal & 1024)
1240 tmp |= 8;
1241 if (vdispend & 1024)
1242 tmp |= 4;
1243 if (vsyncstart & 1024)
1244 tmp |= 3;
1245
1246 vga_wcrt(regbase, CL_CRT1E, tmp);
1247 dev_dbg(info->device, "CRT1e: %d\n", tmp);
1248 }
1249
Krzysztof Helt8503df62007-10-16 01:29:08 -07001250 /* pixel panning */
1251 vga_wattr(regbase, CL_AR33, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001252
1253 /* [ EGS: SetOffset(); ] */
1254 /* From SetOffset(): Turn on VideoEnable bit in Attribute controller */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001255 AttrOn(cinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001256
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001257 if (is_laguna(cinfo)) {
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001258 /* no tiles */
1259 fb_writew(control | 0x1000, cinfo->laguna_mmio + 0x402);
1260 fb_writew(format, cinfo->laguna_mmio + 0xc0);
1261 fb_writew(threshold, cinfo->laguna_mmio + 0xea);
1262 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263 /* finally, turn on everything - turn off "FullBandwidth" bit */
1264 /* also, set "DotClock%2" bit where requested */
1265 tmp = 0x01;
1266
1267/*** FB_VMODE_CLOCK_HALVE in linux/fb.h not defined anymore ?
1268 if (var->vmode & FB_VMODE_CLOCK_HALVE)
1269 tmp |= 0x08;
1270*/
1271
Krzysztof Helt8503df62007-10-16 01:29:08 -07001272 vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, tmp);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001273 dev_dbg(info->device, "CL_SEQR1: %d\n", tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001274
Linus Torvalds1da177e2005-04-16 15:20:36 -07001275#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001276 cirrusfb_dbg_reg_dump(info, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001277#endif
1278
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279 return 0;
1280}
1281
1282/* for some reason incomprehensible to me, cirrusfb requires that you write
1283 * the registers twice for the settings to take..grr. -dte */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001284static int cirrusfb_set_par(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001285{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001286 cirrusfb_set_par_foo(info);
1287 return cirrusfb_set_par_foo(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001288}
1289
Krzysztof Helt8503df62007-10-16 01:29:08 -07001290static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
1291 unsigned blue, unsigned transp,
1292 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001293{
1294 struct cirrusfb_info *cinfo = info->par;
1295
1296 if (regno > 255)
1297 return -EINVAL;
1298
1299 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
1300 u32 v;
1301 red >>= (16 - info->var.red.length);
1302 green >>= (16 - info->var.green.length);
1303 blue >>= (16 - info->var.blue.length);
1304
Krzysztof Helt8503df62007-10-16 01:29:08 -07001305 if (regno >= 16)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001306 return 1;
1307 v = (red << info->var.red.offset) |
1308 (green << info->var.green.offset) |
1309 (blue << info->var.blue.offset);
1310
Krzysztof Helt060b6002007-10-16 01:29:13 -07001311 cinfo->pseudo_palette[regno] = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001312 return 0;
1313 }
1314
Krzysztof Helt8503df62007-10-16 01:29:08 -07001315 if (info->var.bits_per_pixel == 8)
1316 WClut(cinfo, regno, red >> 10, green >> 10, blue >> 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317
1318 return 0;
1319
1320}
1321
1322/*************************************************************************
1323 cirrusfb_pan_display()
1324
1325 performs display panning - provided hardware permits this
1326**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07001327static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
1328 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329{
Krzysztof Helt99a45842009-03-31 15:25:09 -07001330 int xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001331 unsigned long base;
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001332 unsigned char tmp, xpix;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001333 struct cirrusfb_info *cinfo = info->par;
1334
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001335 dev_dbg(info->device,
1336 "virtual offset: (%d,%d)\n", var->xoffset, var->yoffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337
1338 /* no range checks for xoffset and yoffset, */
1339 /* as fb_pan_display has already done this */
1340 if (var->vmode & FB_VMODE_YWRAP)
1341 return -EINVAL;
1342
Linus Torvalds1da177e2005-04-16 15:20:36 -07001343 xoffset = var->xoffset * info->var.bits_per_pixel / 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001344
Krzysztof Helt99a45842009-03-31 15:25:09 -07001345 base = var->yoffset * info->fix.line_length + xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001346
1347 if (info->var.bits_per_pixel == 1) {
1348 /* base is already correct */
1349 xpix = (unsigned char) (var->xoffset % 8);
1350 } else {
1351 base /= 4;
1352 xpix = (unsigned char) ((xoffset % 4) * 2);
1353 }
1354
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001355 if (!is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001356 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001357
1358 /* lower 8 + 8 bits of screen start address */
Krzysztof Helt99a45842009-03-31 15:25:09 -07001359 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, base & 0xff);
1360 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, (base >> 8) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001361
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001362 /* 0xf2 is %11110010, exclude tmp bits */
1363 tmp = vga_rcrt(cinfo->regbase, CL_CRT1B) & 0xf2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001364 /* construct bits 16, 17 and 18 of screen start address */
1365 if (base & 0x10000)
1366 tmp |= 0x01;
1367 if (base & 0x20000)
1368 tmp |= 0x04;
1369 if (base & 0x40000)
1370 tmp |= 0x08;
1371
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001372 vga_wcrt(cinfo->regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001373
1374 /* construct bit 19 of screen start address */
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001375 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19) {
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001376 tmp = vga_rcrt(cinfo->regbase, CL_CRT1D);
1377 if (is_laguna(cinfo))
1378 tmp = (tmp & ~0x18) | ((base >> 16) & 0x18);
1379 else
1380 tmp = (tmp & ~0x80) | ((base >> 12) & 0x80);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001381 vga_wcrt(cinfo->regbase, CL_CRT1D, tmp);
1382 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001383
Krzysztof Helt8503df62007-10-16 01:29:08 -07001384 /* write pixel panning value to AR33; this does not quite work in 8bpp
1385 *
1386 * ### Piccolo..? Will this work?
1387 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001388 if (info->var.bits_per_pixel == 1)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001389 vga_wattr(cinfo->regbase, CL_AR33, xpix);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001390
Krzysztof Helt8503df62007-10-16 01:29:08 -07001391 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001392}
1393
Krzysztof Helt8503df62007-10-16 01:29:08 -07001394static int cirrusfb_blank(int blank_mode, struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001395{
1396 /*
Krzysztof Helt8503df62007-10-16 01:29:08 -07001397 * Blank the screen if blank_mode != 0, else unblank. If blank == NULL
1398 * then the caller blanks by setting the CLUT (Color Look Up Table)
1399 * to all black. Return 0 if blanking succeeded, != 0 if un-/blanking
1400 * failed due to e.g. a video mode which doesn't support it.
1401 * Implements VESA suspend and powerdown modes on hardware that
1402 * supports disabling hsync/vsync:
1403 * blank_mode == 2: suspend vsync
1404 * blank_mode == 3: suspend hsync
1405 * blank_mode == 4: powerdown
Linus Torvalds1da177e2005-04-16 15:20:36 -07001406 */
1407 unsigned char val;
1408 struct cirrusfb_info *cinfo = info->par;
1409 int current_mode = cinfo->blank_mode;
1410
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001411 dev_dbg(info->device, "ENTER, blank mode = %d\n", blank_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001412
1413 if (info->state != FBINFO_STATE_RUNNING ||
1414 current_mode == blank_mode) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001415 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001416 return 0;
1417 }
1418
1419 /* Undo current */
1420 if (current_mode == FB_BLANK_NORMAL ||
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001421 current_mode == FB_BLANK_UNBLANK)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001422 /* clear "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001423 val = 0;
1424 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001425 /* set "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001426 val = 0x20;
1427
1428 val |= vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE) & 0xdf;
1429 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001430
1431 switch (blank_mode) {
1432 case FB_BLANK_UNBLANK:
1433 case FB_BLANK_NORMAL:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001434 val = 0x00;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001435 break;
1436 case FB_BLANK_VSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001437 val = 0x04;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001438 break;
1439 case FB_BLANK_HSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001440 val = 0x02;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001441 break;
1442 case FB_BLANK_POWERDOWN:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001443 val = 0x06;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001444 break;
1445 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001446 dev_dbg(info->device, "EXIT, returning 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447 return 1;
1448 }
1449
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001450 vga_wgfx(cinfo->regbase, CL_GRE, val);
1451
Linus Torvalds1da177e2005-04-16 15:20:36 -07001452 cinfo->blank_mode = blank_mode;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001453 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001454
1455 /* Let fbcon do a soft blank for us */
1456 return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
1457}
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001458
Linus Torvalds1da177e2005-04-16 15:20:36 -07001459/**** END Hardware specific Routines **************************************/
1460/****************************************************************************/
1461/**** BEGIN Internal Routines ***********************************************/
1462
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001463static void init_vgachip(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001464{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001465 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001466 const struct cirrusfb_board_info_rec *bi;
1467
Krzysztof Helt8503df62007-10-16 01:29:08 -07001468 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001469
1470 bi = &cirrusfb_board_info[cinfo->btype];
1471
1472 /* reset board globally */
1473 switch (cinfo->btype) {
1474 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001475 WSFR(cinfo, 0x01);
1476 udelay(500);
1477 WSFR(cinfo, 0x51);
1478 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001479 break;
1480 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001481 WSFR2(cinfo, 0xff);
1482 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001483 break;
1484 case BT_SD64:
1485 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001486 WSFR(cinfo, 0x1f);
1487 udelay(500);
1488 WSFR(cinfo, 0x4f);
1489 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001490 break;
1491 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001492 /* disable flickerfixer */
1493 vga_wcrt(cinfo->regbase, CL_CRT51, 0x00);
1494 mdelay(100);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001495 /* mode */
1496 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Krzysztof Helt7cade312009-03-31 15:25:13 -07001497 case BT_GD5480: /* fall through */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001498 /* from Klaus' NetBSD driver: */
1499 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
Krzysztof Helt7cade312009-03-31 15:25:13 -07001500 case BT_ALPINE: /* fall through */
1501 /* put blitter into 542x compat */
1502 vga_wgfx(cinfo->regbase, CL_GR33, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001503 break;
1504
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001505 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001506 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001507 /* Nothing to do to reset the board. */
1508 break;
1509
1510 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001511 dev_err(info->device, "Warning: Unknown board type\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001512 break;
1513 }
1514
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001515 /* make sure RAM size set by this point */
1516 assert(info->screen_size > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001517
1518 /* the P4 is not fully initialized here; I rely on it having been */
1519 /* inited under AmigaOS already, which seems to work just fine */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001520 /* (Klaus advised to do it this way) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001521
1522 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001523 WGen(cinfo, CL_VSSM, 0x10); /* EGS: 0x16 */
1524 WGen(cinfo, CL_POS102, 0x01);
1525 WGen(cinfo, CL_VSSM, 0x08); /* EGS: 0x0e */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001526
1527 if (cinfo->btype != BT_SD64)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001528 WGen(cinfo, CL_VSSM2, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001529
Krzysztof Helt8503df62007-10-16 01:29:08 -07001530 /* reset sequencer logic */
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001531 vga_wseq(cinfo->regbase, VGA_SEQ_RESET, 0x03);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001532
Krzysztof Helt8503df62007-10-16 01:29:08 -07001533 /* FullBandwidth (video off) and 8/9 dot clock */
1534 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001535
Krzysztof Helt8503df62007-10-16 01:29:08 -07001536 /* "magic cookie" - doesn't make any sense to me.. */
1537/* vga_wgfx(cinfo->regbase, CL_GRA, 0xce); */
1538 /* unlock all extension registers */
1539 vga_wseq(cinfo->regbase, CL_SEQR6, 0x12);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001540
Linus Torvalds1da177e2005-04-16 15:20:36 -07001541 switch (cinfo->btype) {
1542 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001543 vga_wseq(cinfo->regbase, CL_SEQRF, 0x98);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544 break;
1545 case BT_ALPINE:
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001546 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001547 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001548 break;
1549 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001550 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001551 break;
1552 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001553 vga_wseq(cinfo->regbase, CL_SEQR16, 0x0f);
1554 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001555 break;
1556 }
1557 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001558 /* plane mask: nothing */
1559 vga_wseq(cinfo->regbase, VGA_SEQ_PLANE_WRITE, 0xff);
1560 /* character map select: doesn't even matter in gx mode */
1561 vga_wseq(cinfo->regbase, VGA_SEQ_CHARACTER_MAP, 0x00);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001562 /* memory mode: chain4, ext. memory */
1563 vga_wseq(cinfo->regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001564
1565 /* controller-internal base address of video memory */
1566 if (bi->init_sr07)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001567 vga_wseq(cinfo->regbase, CL_SEQR7, bi->sr07);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001568
Krzysztof Helt8503df62007-10-16 01:29:08 -07001569 /* vga_wseq(cinfo->regbase, CL_SEQR8, 0x00); */
1570 /* EEPROM control: shouldn't be necessary to write to this at all.. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001571
Krzysztof Helt8503df62007-10-16 01:29:08 -07001572 /* graphics cursor X position (incomplete; position gives rem. 3 bits */
1573 vga_wseq(cinfo->regbase, CL_SEQR10, 0x00);
1574 /* graphics cursor Y position (..."... ) */
1575 vga_wseq(cinfo->regbase, CL_SEQR11, 0x00);
1576 /* graphics cursor attributes */
1577 vga_wseq(cinfo->regbase, CL_SEQR12, 0x00);
1578 /* graphics cursor pattern address */
1579 vga_wseq(cinfo->regbase, CL_SEQR13, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001580
1581 /* writing these on a P4 might give problems.. */
1582 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001583 /* configuration readback and ext. color */
1584 vga_wseq(cinfo->regbase, CL_SEQR17, 0x00);
1585 /* signature generator */
1586 vga_wseq(cinfo->regbase, CL_SEQR18, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001587 }
1588
1589 /* MCLK select etc. */
1590 if (bi->init_sr1f)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001591 vga_wseq(cinfo->regbase, CL_SEQR1F, bi->sr1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001592
Krzysztof Helt8503df62007-10-16 01:29:08 -07001593 /* Screen A preset row scan: none */
1594 vga_wcrt(cinfo->regbase, VGA_CRTC_PRESET_ROW, 0x00);
1595 /* Text cursor start: disable text cursor */
1596 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_START, 0x20);
1597 /* Text cursor end: - */
1598 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_END, 0x00);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001599 /* text cursor location high: 0 */
1600 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_HI, 0x00);
1601 /* text cursor location low: 0 */
1602 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_LO, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001603
Krzysztof Helt8503df62007-10-16 01:29:08 -07001604 /* Underline Row scanline: - */
1605 vga_wcrt(cinfo->regbase, VGA_CRTC_UNDERLINE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001606 /* ### add 0x40 for text modes with > 30 MHz pixclock */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001607 /* ext. display controls: ext.adr. wrap */
1608 vga_wcrt(cinfo->regbase, CL_CRT1B, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001609
Krzysztof Helt8503df62007-10-16 01:29:08 -07001610 /* Set/Reset registes: - */
1611 vga_wgfx(cinfo->regbase, VGA_GFX_SR_VALUE, 0x00);
1612 /* Set/Reset enable: - */
1613 vga_wgfx(cinfo->regbase, VGA_GFX_SR_ENABLE, 0x00);
1614 /* Color Compare: - */
1615 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_VALUE, 0x00);
1616 /* Data Rotate: - */
1617 vga_wgfx(cinfo->regbase, VGA_GFX_DATA_ROTATE, 0x00);
1618 /* Read Map Select: - */
1619 vga_wgfx(cinfo->regbase, VGA_GFX_PLANE_READ, 0x00);
1620 /* Mode: conf. for 16/4/2 color mode, no odd/even, read/write mode 0 */
1621 vga_wgfx(cinfo->regbase, VGA_GFX_MODE, 0x00);
1622 /* Miscellaneous: memory map base address, graphics mode */
1623 vga_wgfx(cinfo->regbase, VGA_GFX_MISC, 0x01);
1624 /* Color Don't care: involve all planes */
1625 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_MASK, 0x0f);
1626 /* Bit Mask: no mask at all */
1627 vga_wgfx(cinfo->regbase, VGA_GFX_BIT_MASK, 0xff);
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001628
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001629 if (cinfo->btype == BT_ALPINE || is_laguna(cinfo))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001630 /* (5434 can't have bit 3 set for bitblt) */
1631 vga_wgfx(cinfo->regbase, CL_GRB, 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001632 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001633 /* Graphics controller mode extensions: finer granularity,
1634 * 8byte data latches
1635 */
1636 vga_wgfx(cinfo->regbase, CL_GRB, 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001637
Krzysztof Helt8503df62007-10-16 01:29:08 -07001638 vga_wgfx(cinfo->regbase, CL_GRC, 0xff); /* Color Key compare: - */
1639 vga_wgfx(cinfo->regbase, CL_GRD, 0x00); /* Color Key compare mask: - */
1640 vga_wgfx(cinfo->regbase, CL_GRE, 0x00); /* Miscellaneous control: - */
1641 /* Background color byte 1: - */
1642 /* vga_wgfx (cinfo->regbase, CL_GR10, 0x00); */
1643 /* vga_wgfx (cinfo->regbase, CL_GR11, 0x00); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001644
Krzysztof Helt8503df62007-10-16 01:29:08 -07001645 /* Attribute Controller palette registers: "identity mapping" */
1646 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE0, 0x00);
1647 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE1, 0x01);
1648 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE2, 0x02);
1649 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE3, 0x03);
1650 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE4, 0x04);
1651 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE5, 0x05);
1652 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE6, 0x06);
1653 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE7, 0x07);
1654 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE8, 0x08);
1655 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE9, 0x09);
1656 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEA, 0x0a);
1657 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEB, 0x0b);
1658 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEC, 0x0c);
1659 vga_wattr(cinfo->regbase, VGA_ATC_PALETTED, 0x0d);
1660 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEE, 0x0e);
1661 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEF, 0x0f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001662
Krzysztof Helt8503df62007-10-16 01:29:08 -07001663 /* Attribute Controller mode: graphics mode */
1664 vga_wattr(cinfo->regbase, VGA_ATC_MODE, 0x01);
1665 /* Overscan color reg.: reg. 0 */
1666 vga_wattr(cinfo->regbase, VGA_ATC_OVERSCAN, 0x00);
1667 /* Color Plane enable: Enable all 4 planes */
1668 vga_wattr(cinfo->regbase, VGA_ATC_PLANE_ENABLE, 0x0f);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001669 /* Color Select: - */
1670 vga_wattr(cinfo->regbase, VGA_ATC_COLOR_PAGE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001671
Krzysztof Helt8503df62007-10-16 01:29:08 -07001672 WGen(cinfo, VGA_PEL_MSK, 0xff); /* Pixel mask: no mask */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001673
Krzysztof Helt8503df62007-10-16 01:29:08 -07001674 /* BLT Start/status: Blitter reset */
1675 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
1676 /* - " - : "end-of-reset" */
1677 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001678
1679 /* misc... */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001680 WHDR(cinfo, 0); /* Hidden DAC register: - */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001681 return;
1682}
1683
Krzysztof Helt8503df62007-10-16 01:29:08 -07001684static void switch_monitor(struct cirrusfb_info *cinfo, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001685{
1686#ifdef CONFIG_ZORRO /* only works on Zorro boards */
1687 static int IsOn = 0; /* XXX not ok for multiple boards */
1688
Linus Torvalds1da177e2005-04-16 15:20:36 -07001689 if (cinfo->btype == BT_PICASSO4)
1690 return; /* nothing to switch */
1691 if (cinfo->btype == BT_ALPINE)
1692 return; /* nothing to switch */
1693 if (cinfo->btype == BT_GD5480)
1694 return; /* nothing to switch */
1695 if (cinfo->btype == BT_PICASSO) {
1696 if ((on && !IsOn) || (!on && IsOn))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001697 WSFR(cinfo, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001698 return;
1699 }
1700 if (on) {
1701 switch (cinfo->btype) {
1702 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001703 WSFR(cinfo, cinfo->SFR | 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001704 break;
1705 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001706 WSFR(cinfo, cinfo->SFR | 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001707 break;
1708 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001709 WSFR(cinfo, 0x6f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001710 break;
1711 default: /* do nothing */ break;
1712 }
1713 } else {
1714 switch (cinfo->btype) {
1715 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001716 WSFR(cinfo, cinfo->SFR & 0xde);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001717 break;
1718 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001719 WSFR(cinfo, cinfo->SFR & 0xd7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001720 break;
1721 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001722 WSFR(cinfo, 0x4f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001723 break;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001724 default: /* do nothing */
1725 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001726 }
1727 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001728#endif /* CONFIG_ZORRO */
1729}
1730
Linus Torvalds1da177e2005-04-16 15:20:36 -07001731/******************************************/
1732/* Linux 2.6-style accelerated functions */
1733/******************************************/
1734
Krzysztof Helt8343c892009-03-31 15:25:11 -07001735static int cirrusfb_sync(struct fb_info *info)
1736{
1737 struct cirrusfb_info *cinfo = info->par;
1738
1739 if (!is_laguna(cinfo)) {
1740 while (vga_rgfx(cinfo->regbase, CL_GR31) & 0x03)
1741 cpu_relax();
1742 }
1743 return 0;
1744}
1745
Krzysztof Helt8503df62007-10-16 01:29:08 -07001746static void cirrusfb_fillrect(struct fb_info *info,
1747 const struct fb_fillrect *region)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001748{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001749 struct fb_fillrect modded;
1750 int vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001751 struct cirrusfb_info *cinfo = info->par;
1752 int m = info->var.bits_per_pixel;
1753 u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ?
1754 cinfo->pseudo_palette[region->color] : region->color;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001755
1756 if (info->state != FBINFO_STATE_RUNNING)
1757 return;
1758 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1759 cfb_fillrect(info, region);
1760 return;
1761 }
1762
1763 vxres = info->var.xres_virtual;
1764 vyres = info->var.yres_virtual;
1765
1766 memcpy(&modded, region, sizeof(struct fb_fillrect));
1767
Krzysztof Helt8503df62007-10-16 01:29:08 -07001768 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001769 modded.dx >= vxres || modded.dy >= vyres)
1770 return;
1771
Krzysztof Helt8503df62007-10-16 01:29:08 -07001772 if (modded.dx + modded.width > vxres)
1773 modded.width = vxres - modded.dx;
1774 if (modded.dy + modded.height > vyres)
1775 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001776
Krzysztof Helt060b6002007-10-16 01:29:13 -07001777 cirrusfb_RectFill(cinfo->regbase,
1778 info->var.bits_per_pixel,
1779 (region->dx * m) / 8, region->dy,
1780 (region->width * m) / 8, region->height,
Krzysztof Helt9e848062009-03-31 15:25:11 -07001781 color, color,
1782 info->fix.line_length, 0x40);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001783}
1784
Krzysztof Helt8503df62007-10-16 01:29:08 -07001785static void cirrusfb_copyarea(struct fb_info *info,
1786 const struct fb_copyarea *area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001787{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001788 struct fb_copyarea modded;
1789 u32 vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001790 struct cirrusfb_info *cinfo = info->par;
1791 int m = info->var.bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001792
1793 if (info->state != FBINFO_STATE_RUNNING)
1794 return;
1795 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1796 cfb_copyarea(info, area);
1797 return;
1798 }
1799
1800 vxres = info->var.xres_virtual;
1801 vyres = info->var.yres_virtual;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001802 memcpy(&modded, area, sizeof(struct fb_copyarea));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001803
Krzysztof Helt8503df62007-10-16 01:29:08 -07001804 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001805 modded.sx >= vxres || modded.sy >= vyres ||
1806 modded.dx >= vxres || modded.dy >= vyres)
1807 return;
1808
Krzysztof Helt8503df62007-10-16 01:29:08 -07001809 if (modded.sx + modded.width > vxres)
1810 modded.width = vxres - modded.sx;
1811 if (modded.dx + modded.width > vxres)
1812 modded.width = vxres - modded.dx;
1813 if (modded.sy + modded.height > vyres)
1814 modded.height = vyres - modded.sy;
1815 if (modded.dy + modded.height > vyres)
1816 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001817
Krzysztof Helt060b6002007-10-16 01:29:13 -07001818 cirrusfb_BitBLT(cinfo->regbase, info->var.bits_per_pixel,
1819 (area->sx * m) / 8, area->sy,
1820 (area->dx * m) / 8, area->dy,
1821 (area->width * m) / 8, area->height,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001822 info->fix.line_length);
Krzysztof Helt060b6002007-10-16 01:29:13 -07001823
Linus Torvalds1da177e2005-04-16 15:20:36 -07001824}
1825
Krzysztof Helt8503df62007-10-16 01:29:08 -07001826static void cirrusfb_imageblit(struct fb_info *info,
1827 const struct fb_image *image)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001828{
1829 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt7cade312009-03-31 15:25:13 -07001830 unsigned char op = (info->var.bits_per_pixel == 24) ? 0xc : 0x4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001831
Krzysztof Helt9e848062009-03-31 15:25:11 -07001832 if (info->state != FBINFO_STATE_RUNNING)
1833 return;
Krzysztof Helt7cade312009-03-31 15:25:13 -07001834 /* Alpine acceleration does not work at 24bpp ?!? */
1835 if (info->flags & FBINFO_HWACCEL_DISABLED || image->depth != 1 ||
1836 (cinfo->btype == BT_ALPINE && op == 0xc))
Krzysztof Helt9e848062009-03-31 15:25:11 -07001837 cfb_imageblit(info, image);
1838 else {
1839 unsigned size = ((image->width + 7) >> 3) * image->height;
1840 int m = info->var.bits_per_pixel;
1841 u32 fg, bg;
1842
1843 if (info->var.bits_per_pixel == 8) {
1844 fg = image->fg_color;
1845 bg = image->bg_color;
1846 } else {
1847 fg = ((u32 *)(info->pseudo_palette))[image->fg_color];
1848 bg = ((u32 *)(info->pseudo_palette))[image->bg_color];
1849 }
Krzysztof Helt7cade312009-03-31 15:25:13 -07001850 if (info->var.bits_per_pixel == 24) {
1851 /* clear background first */
1852 cirrusfb_RectFill(cinfo->regbase,
1853 info->var.bits_per_pixel,
1854 (image->dx * m) / 8, image->dy,
1855 (image->width * m) / 8,
1856 image->height,
1857 bg, bg,
1858 info->fix.line_length, 0x40);
1859 }
Krzysztof Helt9e848062009-03-31 15:25:11 -07001860 cirrusfb_RectFill(cinfo->regbase,
1861 info->var.bits_per_pixel,
1862 (image->dx * m) / 8, image->dy,
1863 (image->width * m) / 8, image->height,
1864 fg, bg,
Krzysztof Helt7cade312009-03-31 15:25:13 -07001865 info->fix.line_length, op);
Krzysztof Helt9e848062009-03-31 15:25:11 -07001866 memcpy(info->screen_base, image->data, size);
1867 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001868}
1869
Linus Torvalds1da177e2005-04-16 15:20:36 -07001870#ifdef CONFIG_PPC_PREP
1871#define PREP_VIDEO_BASE ((volatile unsigned long) 0xC0000000)
1872#define PREP_IO_BASE ((volatile unsigned char *) 0x80000000)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001873static void get_prep_addrs(unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001874{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001875 *display = PREP_VIDEO_BASE;
1876 *registers = (unsigned long) PREP_IO_BASE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001877}
1878
1879#endif /* CONFIG_PPC_PREP */
1880
Linus Torvalds1da177e2005-04-16 15:20:36 -07001881#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001882static int release_io_ports;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001883
1884/* Pulled the logic from XFree86 Cirrus driver to get the memory size,
1885 * based on the DRAM bandwidth bit and DRAM bank switching bit. This
1886 * works with 1MB, 2MB and 4MB configurations (which the Motorola boards
1887 * seem to have. */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001888static unsigned int __devinit cirrusfb_get_memsize(struct fb_info *info,
1889 u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001890{
1891 unsigned long mem;
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001892 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001893
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001894 if (is_laguna(cinfo)) {
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001895 unsigned char SR14 = vga_rseq(regbase, CL_SEQR14);
1896
1897 mem = ((SR14 & 7) + 1) << 20;
1898 } else {
1899 unsigned char SRF = vga_rseq(regbase, CL_SEQRF);
1900 switch ((SRF & 0x18)) {
1901 case 0x08:
1902 mem = 512 * 1024;
1903 break;
1904 case 0x10:
1905 mem = 1024 * 1024;
1906 break;
1907 /* 64-bit DRAM data bus width; assume 2MB.
1908 * Also indicates 2MB memory on the 5430.
1909 */
1910 case 0x18:
1911 mem = 2048 * 1024;
1912 break;
1913 default:
1914 dev_warn(info->device, "Unknown memory size!\n");
1915 mem = 1024 * 1024;
1916 }
1917 /* If DRAM bank switching is enabled, there must be
1918 * twice as much memory installed. (4MB on the 5434)
1919 */
1920 if (SRF & 0x80)
1921 mem *= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001922 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001923
Linus Torvalds1da177e2005-04-16 15:20:36 -07001924 /* TODO: Handling of GD5446/5480 (see XF86 sources ...) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001925 return mem;
1926}
1927
Krzysztof Helt8503df62007-10-16 01:29:08 -07001928static void get_pci_addrs(const struct pci_dev *pdev,
1929 unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001930{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001931 assert(pdev != NULL);
1932 assert(display != NULL);
1933 assert(registers != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001934
Linus Torvalds1da177e2005-04-16 15:20:36 -07001935 *display = 0;
1936 *registers = 0;
1937
1938 /* This is a best-guess for now */
1939
1940 if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
1941 *display = pci_resource_start(pdev, 1);
1942 *registers = pci_resource_start(pdev, 0);
1943 } else {
1944 *display = pci_resource_start(pdev, 0);
1945 *registers = pci_resource_start(pdev, 1);
1946 }
1947
Krzysztof Helt8503df62007-10-16 01:29:08 -07001948 assert(*display != 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001949}
1950
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001951static void cirrusfb_pci_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001952{
Krzysztof Helt64beab12008-10-15 22:03:38 -07001953 struct pci_dev *pdev = to_pci_dev(info->device);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001954 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001955
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001956 if (cinfo->laguna_mmio == NULL)
1957 iounmap(cinfo->laguna_mmio);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001958 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001959#if 0 /* if system didn't claim this region, we would... */
1960 release_mem_region(0xA0000, 65535);
1961#endif
1962 if (release_io_ports)
1963 release_region(0x3C0, 32);
1964 pci_release_regions(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001965}
1966#endif /* CONFIG_PCI */
1967
Linus Torvalds1da177e2005-04-16 15:20:36 -07001968#ifdef CONFIG_ZORRO
Al Virof5ee0512008-11-01 18:20:39 +00001969static void cirrusfb_zorro_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001970{
Al Virod91f5bb2007-10-17 00:27:18 +01001971 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt64beab12008-10-15 22:03:38 -07001972 struct zorro_dev *zdev = to_zorro_dev(info->device);
1973
1974 zorro_release_device(zdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001975
1976 if (cinfo->btype == BT_PICASSO4) {
1977 cinfo->regbase -= 0x600000;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001978 iounmap((void *)cinfo->regbase);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001979 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001980 } else {
Krzysztof Helt64beab12008-10-15 22:03:38 -07001981 if (zorro_resource_start(zdev) > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001982 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001983 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001984}
1985#endif /* CONFIG_ZORRO */
1986
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001987/* function table of the above functions */
1988static struct fb_ops cirrusfb_ops = {
1989 .owner = THIS_MODULE,
1990 .fb_open = cirrusfb_open,
1991 .fb_release = cirrusfb_release,
1992 .fb_setcolreg = cirrusfb_setcolreg,
1993 .fb_check_var = cirrusfb_check_var,
1994 .fb_set_par = cirrusfb_set_par,
1995 .fb_pan_display = cirrusfb_pan_display,
1996 .fb_blank = cirrusfb_blank,
1997 .fb_fillrect = cirrusfb_fillrect,
1998 .fb_copyarea = cirrusfb_copyarea,
Krzysztof Helt8343c892009-03-31 15:25:11 -07001999 .fb_sync = cirrusfb_sync,
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002000 .fb_imageblit = cirrusfb_imageblit,
2001};
2002
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002003static int __devinit cirrusfb_set_fbinfo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002004{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002005 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002006 struct fb_var_screeninfo *var = &info->var;
2007
Linus Torvalds1da177e2005-04-16 15:20:36 -07002008 info->pseudo_palette = cinfo->pseudo_palette;
2009 info->flags = FBINFO_DEFAULT
2010 | FBINFO_HWACCEL_XPAN
2011 | FBINFO_HWACCEL_YPAN
2012 | FBINFO_HWACCEL_FILLRECT
Krzysztof Helt9e848062009-03-31 15:25:11 -07002013 | FBINFO_HWACCEL_IMAGEBLIT
Linus Torvalds1da177e2005-04-16 15:20:36 -07002014 | FBINFO_HWACCEL_COPYAREA;
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002015 if (noaccel || is_laguna(cinfo))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002016 info->flags |= FBINFO_HWACCEL_DISABLED;
2017 info->fbops = &cirrusfb_ops;
Krzysztof Helt9e848062009-03-31 15:25:11 -07002018
Linus Torvalds1da177e2005-04-16 15:20:36 -07002019 if (cinfo->btype == BT_GD5480) {
2020 if (var->bits_per_pixel == 16)
2021 info->screen_base += 1 * MB_;
Krzysztof Helt1cea9a92008-10-15 22:03:37 -07002022 if (var->bits_per_pixel == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002023 info->screen_base += 2 * MB_;
2024 }
2025
2026 /* Fill fix common fields */
2027 strlcpy(info->fix.id, cirrusfb_board_info[cinfo->btype].name,
2028 sizeof(info->fix.id));
2029
2030 /* monochrome: only 1 memory plane */
2031 /* 8 bit and above: Use whole memory area */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002032 info->fix.smem_len = info->screen_size;
2033 if (var->bits_per_pixel == 1)
2034 info->fix.smem_len /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002035 info->fix.type_aux = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002036 info->fix.xpanstep = 1;
2037 info->fix.ypanstep = 1;
2038 info->fix.ywrapstep = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002039
2040 /* FIXME: map region at 0xB8000 if available, fill in here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002041 info->fix.mmio_len = 0;
2042 info->fix.accel = FB_ACCEL_NONE;
2043
2044 fb_alloc_cmap(&info->cmap, 256, 0);
2045
2046 return 0;
2047}
2048
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002049static int __devinit cirrusfb_register(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002050{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002051 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002052 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002053
2054 /* sanity checks */
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002055 assert(cinfo->btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002056
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002057 /* set all the vital stuff */
2058 cirrusfb_set_fbinfo(info);
2059
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002060 dev_dbg(info->device, "(RAM start set to: 0x%p)\n", info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002061
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002062 err = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8);
2063 if (!err) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002064 dev_dbg(info->device, "wrong initial video mode\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002065 err = -EINVAL;
2066 goto err_dealloc_cmap;
2067 }
2068
Linus Torvalds1da177e2005-04-16 15:20:36 -07002069 info->var.activate = FB_ACTIVATE_NOW;
2070
Krzysztof Helt99a45842009-03-31 15:25:09 -07002071 err = cirrusfb_check_var(&info->var, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002072 if (err < 0) {
2073 /* should never happen */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002074 dev_dbg(info->device,
2075 "choking on default var... umm, no good.\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002076 goto err_dealloc_cmap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002077 }
2078
Linus Torvalds1da177e2005-04-16 15:20:36 -07002079 err = register_framebuffer(info);
2080 if (err < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002081 dev_err(info->device,
2082 "could not register fb device; err = %d!\n", err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002083 goto err_dealloc_cmap;
2084 }
2085
Linus Torvalds1da177e2005-04-16 15:20:36 -07002086 return 0;
2087
2088err_dealloc_cmap:
2089 fb_dealloc_cmap(&info->cmap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002090 return err;
2091}
2092
Krzysztof Helt8503df62007-10-16 01:29:08 -07002093static void __devexit cirrusfb_cleanup(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002094{
2095 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002096
Krzysztof Helt8503df62007-10-16 01:29:08 -07002097 switch_monitor(cinfo, 0);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002098 unregister_framebuffer(info);
2099 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002100 dev_dbg(info->device, "Framebuffer unregistered\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002101 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002102 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002103}
2104
Linus Torvalds1da177e2005-04-16 15:20:36 -07002105#ifdef CONFIG_PCI
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002106static int __devinit cirrusfb_pci_register(struct pci_dev *pdev,
2107 const struct pci_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002108{
2109 struct cirrusfb_info *cinfo;
2110 struct fb_info *info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002111 unsigned long board_addr, board_size;
2112 int ret;
2113
2114 ret = pci_enable_device(pdev);
2115 if (ret < 0) {
2116 printk(KERN_ERR "cirrusfb: Cannot enable PCI device\n");
2117 goto err_out;
2118 }
2119
2120 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &pdev->dev);
2121 if (!info) {
2122 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
2123 ret = -ENOMEM;
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002124 goto err_out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002125 }
2126
2127 cinfo = info->par;
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002128 cinfo->btype = (enum cirrus_board) ent->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002129
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002130 dev_dbg(info->device,
2131 " Found PCI device, base address 0 is 0x%Lx, btype set to %d\n",
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002132 (unsigned long long)pdev->resource[0].start, cinfo->btype);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002133 dev_dbg(info->device, " base address 1 is 0x%Lx\n",
2134 (unsigned long long)pdev->resource[1].start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002135
Krzysztof Helt8503df62007-10-16 01:29:08 -07002136 if (isPReP) {
2137 pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, 0x00000000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002138#ifdef CONFIG_PPC_PREP
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002139 get_prep_addrs(&board_addr, &info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002140#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07002141 /* PReP dies if we ioremap the IO registers, but it works w/out... */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002142 cinfo->regbase = (char __iomem *) info->fix.mmio_start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002143 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002144 dev_dbg(info->device,
2145 "Attempt to get PCI info for Cirrus Graphics Card\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002146 get_pci_addrs(pdev, &board_addr, &info->fix.mmio_start);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002147 /* FIXME: this forces VGA. alternatives? */
2148 cinfo->regbase = NULL;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002149 cinfo->laguna_mmio = ioremap(info->fix.mmio_start, 0x1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002150 }
2151
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002152 dev_dbg(info->device, "Board address: 0x%lx, register address: 0x%lx\n",
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002153 board_addr, info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002154
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002155 board_size = (cinfo->btype == BT_GD5480) ?
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002156 32 * MB_ : cirrusfb_get_memsize(info, cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002157
2158 ret = pci_request_regions(pdev, "cirrusfb");
Krzysztof Helt8503df62007-10-16 01:29:08 -07002159 if (ret < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002160 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2161 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002162 goto err_release_fb;
2163 }
2164#if 0 /* if the system didn't claim this region, we would... */
2165 if (!request_mem_region(0xA0000, 65535, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002166 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2167 0xA0000L);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002168 ret = -EBUSY;
2169 goto err_release_regions;
2170 }
2171#endif
2172 if (request_region(0x3C0, 32, "cirrusfb"))
2173 release_io_ports = 1;
2174
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002175 info->screen_base = ioremap(board_addr, board_size);
2176 if (!info->screen_base) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002177 ret = -EIO;
2178 goto err_release_legacy;
2179 }
2180
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002181 info->fix.smem_start = board_addr;
2182 info->screen_size = board_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002183 cinfo->unmap = cirrusfb_pci_unmap;
2184
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002185 dev_info(info->device,
2186 "Cirrus Logic chipset on PCI bus, RAM (%lu kB) at 0x%lx\n",
2187 info->screen_size >> 10, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002188 pci_set_drvdata(pdev, info);
2189
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002190 ret = cirrusfb_register(info);
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002191 if (!ret)
2192 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002193
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002194 pci_set_drvdata(pdev, NULL);
2195 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002196err_release_legacy:
2197 if (release_io_ports)
2198 release_region(0x3C0, 32);
2199#if 0
2200 release_mem_region(0xA0000, 65535);
2201err_release_regions:
2202#endif
2203 pci_release_regions(pdev);
2204err_release_fb:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002205 if (cinfo->laguna_mmio != NULL)
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002206 iounmap(cinfo->laguna_mmio);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002207 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002208err_out:
2209 return ret;
2210}
2211
Krzysztof Helt8503df62007-10-16 01:29:08 -07002212static void __devexit cirrusfb_pci_unregister(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002213{
2214 struct fb_info *info = pci_get_drvdata(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002215
Krzysztof Helt8503df62007-10-16 01:29:08 -07002216 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002217}
2218
2219static struct pci_driver cirrusfb_pci_driver = {
2220 .name = "cirrusfb",
2221 .id_table = cirrusfb_pci_table,
2222 .probe = cirrusfb_pci_register,
2223 .remove = __devexit_p(cirrusfb_pci_unregister),
2224#ifdef CONFIG_PM
2225#if 0
2226 .suspend = cirrusfb_pci_suspend,
2227 .resume = cirrusfb_pci_resume,
2228#endif
2229#endif
2230};
2231#endif /* CONFIG_PCI */
2232
Linus Torvalds1da177e2005-04-16 15:20:36 -07002233#ifdef CONFIG_ZORRO
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002234static int __devinit cirrusfb_zorro_register(struct zorro_dev *z,
2235 const struct zorro_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002236{
2237 struct cirrusfb_info *cinfo;
2238 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002239 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002240 struct zorro_dev *z2 = NULL;
2241 unsigned long board_addr, board_size, size;
2242 int ret;
2243
2244 btype = ent->driver_data;
2245 if (cirrusfb_zorro_table2[btype].id2)
2246 z2 = zorro_find_device(cirrusfb_zorro_table2[btype].id2, NULL);
2247 size = cirrusfb_zorro_table2[btype].size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002248
2249 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &z->dev);
2250 if (!info) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002251 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002252 ret = -ENOMEM;
2253 goto err_out;
2254 }
2255
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002256 dev_info(info->device, "%s board detected\n",
2257 cirrusfb_board_info[btype].name);
2258
Linus Torvalds1da177e2005-04-16 15:20:36 -07002259 cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002260 cinfo->btype = btype;
2261
Al Viro36ea96a2007-10-27 19:46:58 +01002262 assert(z);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002263 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002264
Linus Torvalds1da177e2005-04-16 15:20:36 -07002265 board_addr = zorro_resource_start(z);
2266 board_size = zorro_resource_len(z);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002267 info->screen_size = size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002268
2269 if (!zorro_request_device(z, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002270 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2271 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002272 ret = -EBUSY;
2273 goto err_release_fb;
2274 }
2275
Linus Torvalds1da177e2005-04-16 15:20:36 -07002276 ret = -EIO;
2277
2278 if (btype == BT_PICASSO4) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002279 dev_info(info->device, " REG at $%lx\n", board_addr + 0x600000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002280
2281 /* To be precise, for the P4 this is not the */
2282 /* begin of the board, but the begin of RAM. */
2283 /* for P4, map in its address space in 2 chunks (### TEST! ) */
2284 /* (note the ugly hardcoded 16M number) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002285 cinfo->regbase = ioremap(board_addr, 16777216);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002286 if (!cinfo->regbase)
2287 goto err_release_region;
2288
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002289 dev_dbg(info->device, "Virtual address for board set to: $%p\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002290 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002291 cinfo->regbase += 0x600000;
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002292 info->fix.mmio_start = board_addr + 0x600000;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002293
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002294 info->fix.smem_start = board_addr + 16777216;
2295 info->screen_base = ioremap(info->fix.smem_start, 16777216);
2296 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002297 goto err_unmap_regbase;
2298 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002299 dev_info(info->device, " REG at $%lx\n",
2300 (unsigned long) z2->resource.start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002301
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002302 info->fix.smem_start = board_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002303 if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002304 info->screen_base = ioremap(board_addr, board_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002305 else
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002306 info->screen_base = (caddr_t) ZTWO_VADDR(board_addr);
2307 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002308 goto err_release_region;
2309
2310 /* set address for REG area of board */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002311 cinfo->regbase = (caddr_t) ZTWO_VADDR(z2->resource.start);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002312 info->fix.mmio_start = z2->resource.start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002313
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002314 dev_dbg(info->device, "Virtual address for board set to: $%p\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002315 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002316 }
2317 cinfo->unmap = cirrusfb_zorro_unmap;
2318
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002319 dev_info(info->device,
2320 "Cirrus Logic chipset on Zorro bus, RAM (%lu MB) at $%lx\n",
2321 board_size / MB_, board_addr);
2322
Linus Torvalds1da177e2005-04-16 15:20:36 -07002323 zorro_set_drvdata(z, info);
2324
Al Virod91f5bb2007-10-17 00:27:18 +01002325 ret = cirrusfb_register(info);
Krzysztof Heltbc5d8ac2009-03-31 15:25:12 -07002326 if (!ret)
2327 return 0;
2328
2329 if (btype == BT_PICASSO4 || board_addr > 0x01000000)
2330 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002331
2332err_unmap_regbase:
Krzysztof Heltbc5d8ac2009-03-31 15:25:12 -07002333 if (btype == BT_PICASSO4)
2334 iounmap(cinfo->regbase - 0x600000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002335err_release_region:
2336 release_region(board_addr, board_size);
2337err_release_fb:
2338 framebuffer_release(info);
2339err_out:
2340 return ret;
2341}
2342
2343void __devexit cirrusfb_zorro_unregister(struct zorro_dev *z)
2344{
2345 struct fb_info *info = zorro_get_drvdata(z);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002346
Krzysztof Helt8503df62007-10-16 01:29:08 -07002347 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002348}
2349
2350static struct zorro_driver cirrusfb_zorro_driver = {
2351 .name = "cirrusfb",
2352 .id_table = cirrusfb_zorro_table,
2353 .probe = cirrusfb_zorro_register,
2354 .remove = __devexit_p(cirrusfb_zorro_unregister),
2355};
2356#endif /* CONFIG_ZORRO */
2357
Linus Torvalds1da177e2005-04-16 15:20:36 -07002358#ifndef MODULE
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002359static int __init cirrusfb_setup(char *options)
2360{
Vlada Pericee119402008-11-19 15:36:45 -08002361 char *this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002362
Linus Torvalds1da177e2005-04-16 15:20:36 -07002363 if (!options || !*options)
2364 return 0;
2365
Krzysztof Helt8503df62007-10-16 01:29:08 -07002366 while ((this_opt = strsep(&options, ",")) != NULL) {
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002367 if (!*this_opt)
2368 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002369
Linus Torvalds1da177e2005-04-16 15:20:36 -07002370 if (!strcmp(this_opt, "noaccel"))
2371 noaccel = 1;
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002372 else if (!strncmp(this_opt, "mode:", 5))
2373 mode_option = this_opt + 5;
2374 else
2375 mode_option = this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002376 }
2377 return 0;
2378}
2379#endif
2380
Linus Torvalds1da177e2005-04-16 15:20:36 -07002381 /*
2382 * Modularization
2383 */
2384
2385MODULE_AUTHOR("Copyright 1999,2000 Jeff Garzik <jgarzik@pobox.com>");
2386MODULE_DESCRIPTION("Accelerated FBDev driver for Cirrus Logic chips");
2387MODULE_LICENSE("GPL");
2388
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002389static int __init cirrusfb_init(void)
2390{
2391 int error = 0;
2392
2393#ifndef MODULE
2394 char *option = NULL;
2395
2396 if (fb_get_options("cirrusfb", &option))
2397 return -ENODEV;
2398 cirrusfb_setup(option);
2399#endif
2400
2401#ifdef CONFIG_ZORRO
2402 error |= zorro_register_driver(&cirrusfb_zorro_driver);
2403#endif
2404#ifdef CONFIG_PCI
2405 error |= pci_register_driver(&cirrusfb_pci_driver);
2406#endif
2407 return error;
2408}
2409
Krzysztof Helt8503df62007-10-16 01:29:08 -07002410static void __exit cirrusfb_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002411{
2412#ifdef CONFIG_PCI
2413 pci_unregister_driver(&cirrusfb_pci_driver);
2414#endif
2415#ifdef CONFIG_ZORRO
2416 zorro_unregister_driver(&cirrusfb_zorro_driver);
2417#endif
2418}
2419
2420module_init(cirrusfb_init);
2421
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002422module_param(mode_option, charp, 0);
2423MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");
Krzysztof Helt55a0dd82008-10-15 22:03:41 -07002424module_param(noaccel, bool, 0);
2425MODULE_PARM_DESC(noaccel, "Disable acceleration");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002426
Linus Torvalds1da177e2005-04-16 15:20:36 -07002427#ifdef MODULE
2428module_exit(cirrusfb_exit);
2429#endif
2430
Linus Torvalds1da177e2005-04-16 15:20:36 -07002431/**********************************************************************/
2432/* about the following functions - I have used the same names for the */
2433/* functions as Markus Wild did in his Retina driver for NetBSD as */
2434/* they just made sense for this purpose. Apart from that, I wrote */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002435/* these functions myself. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002436/**********************************************************************/
2437
2438/*** WGen() - write into one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002439static void WGen(const struct cirrusfb_info *cinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002440 int regnum, unsigned char val)
2441{
2442 unsigned long regofs = 0;
2443
2444 if (cinfo->btype == BT_PICASSO) {
2445 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002446/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2447 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002448 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2449 regofs = 0xfff;
2450 }
2451
Krzysztof Helt8503df62007-10-16 01:29:08 -07002452 vga_w(cinfo->regbase, regofs + regnum, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002453}
2454
2455/*** RGen() - read out one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002456static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002457{
2458 unsigned long regofs = 0;
2459
2460 if (cinfo->btype == BT_PICASSO) {
2461 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002462/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2463 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002464 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2465 regofs = 0xfff;
2466 }
2467
Krzysztof Helt8503df62007-10-16 01:29:08 -07002468 return vga_r(cinfo->regbase, regofs + regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002469}
2470
2471/*** AttrOn() - turn on VideoEnable for Attribute controller ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002472static void AttrOn(const struct cirrusfb_info *cinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002473{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002474 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002475
Krzysztof Helt8503df62007-10-16 01:29:08 -07002476 if (vga_rcrt(cinfo->regbase, CL_CRT24) & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002477 /* if we're just in "write value" mode, write back the */
2478 /* same value as before to not modify anything */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002479 vga_w(cinfo->regbase, VGA_ATT_IW,
2480 vga_r(cinfo->regbase, VGA_ATT_R));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002481 }
2482 /* turn on video bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002483/* vga_w(cinfo->regbase, VGA_ATT_IW, 0x20); */
2484 vga_w(cinfo->regbase, VGA_ATT_IW, 0x33);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002485
2486 /* dummy write on Reg0 to be on "write index" mode next time */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002487 vga_w(cinfo->regbase, VGA_ATT_IW, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002488}
2489
2490/*** WHDR() - write into the Hidden DAC register ***/
2491/* as the HDR is the only extension register that requires special treatment
2492 * (the other extension registers are accessible just like the "ordinary"
2493 * registers of their functional group) here is a specialized routine for
2494 * accessing the HDR
2495 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002496static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002497{
2498 unsigned char dummy;
2499
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002500 if (is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07002501 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002502 if (cinfo->btype == BT_PICASSO) {
2503 /* Klaus' hint for correct access to HDR on some boards */
2504 /* first write 0 to pixel mask (3c6) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002505 WGen(cinfo, VGA_PEL_MSK, 0x00);
2506 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002507 /* next read dummy from pixel address (3c8) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002508 dummy = RGen(cinfo, VGA_PEL_IW);
2509 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002510 }
2511 /* now do the usual stuff to access the HDR */
2512
Krzysztof Helt8503df62007-10-16 01:29:08 -07002513 dummy = RGen(cinfo, VGA_PEL_MSK);
2514 udelay(200);
2515 dummy = RGen(cinfo, VGA_PEL_MSK);
2516 udelay(200);
2517 dummy = RGen(cinfo, VGA_PEL_MSK);
2518 udelay(200);
2519 dummy = RGen(cinfo, VGA_PEL_MSK);
2520 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002521
Krzysztof Helt8503df62007-10-16 01:29:08 -07002522 WGen(cinfo, VGA_PEL_MSK, val);
2523 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002524
2525 if (cinfo->btype == BT_PICASSO) {
2526 /* now first reset HDR access counter */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002527 dummy = RGen(cinfo, VGA_PEL_IW);
2528 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002529
2530 /* and at the end, restore the mask value */
2531 /* ## is this mask always 0xff? */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002532 WGen(cinfo, VGA_PEL_MSK, 0xff);
2533 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002534 }
2535}
2536
Linus Torvalds1da177e2005-04-16 15:20:36 -07002537/*** WSFR() - write to the "special function register" (SFR) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002538static void WSFR(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002539{
2540#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07002541 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002542 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002543 z_writeb(val, cinfo->regbase + 0x8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002544#endif
2545}
2546
2547/* The Picasso has a second register for switching the monitor bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002548static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002549{
2550#ifdef CONFIG_ZORRO
2551 /* writing an arbitrary value to this one causes the monitor switcher */
2552 /* to flip to Amiga display */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002553 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002554 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002555 z_writeb(val, cinfo->regbase + 0x9000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002556#endif
2557}
2558
Linus Torvalds1da177e2005-04-16 15:20:36 -07002559/*** WClut - set CLUT entry (range: 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002560static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002561 unsigned char green, unsigned char blue)
2562{
2563 unsigned int data = VGA_PEL_D;
2564
2565 /* address write mode register is not translated.. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002566 vga_w(cinfo->regbase, VGA_PEL_IW, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002567
2568 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07002569 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480 ||
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002570 is_laguna(cinfo)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002571 /* but DAC data register IS, at least for Picasso II */
2572 if (cinfo->btype == BT_PICASSO)
2573 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002574 vga_w(cinfo->regbase, data, red);
2575 vga_w(cinfo->regbase, data, green);
2576 vga_w(cinfo->regbase, data, blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002577 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002578 vga_w(cinfo->regbase, data, blue);
2579 vga_w(cinfo->regbase, data, green);
2580 vga_w(cinfo->regbase, data, red);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002581 }
2582}
2583
Linus Torvalds1da177e2005-04-16 15:20:36 -07002584#if 0
2585/*** RClut - read CLUT entry (range 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002586static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char *red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002587 unsigned char *green, unsigned char *blue)
2588{
2589 unsigned int data = VGA_PEL_D;
2590
Krzysztof Helt8503df62007-10-16 01:29:08 -07002591 vga_w(cinfo->regbase, VGA_PEL_IR, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002592
2593 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2594 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2595 if (cinfo->btype == BT_PICASSO)
2596 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002597 *red = vga_r(cinfo->regbase, data);
2598 *green = vga_r(cinfo->regbase, data);
2599 *blue = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002600 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002601 *blue = vga_r(cinfo->regbase, data);
2602 *green = vga_r(cinfo->regbase, data);
2603 *red = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002604 }
2605}
2606#endif
2607
Linus Torvalds1da177e2005-04-16 15:20:36 -07002608/*******************************************************************
2609 cirrusfb_WaitBLT()
2610
2611 Wait for the BitBLT engine to complete a possible earlier job
2612*********************************************************************/
2613
2614/* FIXME: use interrupts instead */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002615static void cirrusfb_WaitBLT(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002616{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002617 while (vga_rgfx(regbase, CL_GR31) & 0x08)
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002618 cpu_relax();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002619}
2620
2621/*******************************************************************
2622 cirrusfb_BitBLT()
2623
2624 perform accelerated "scrolling"
2625********************************************************************/
2626
Krzysztof Helt8343c892009-03-31 15:25:11 -07002627static void cirrusfb_set_blitter(u8 __iomem *regbase,
2628 u_short nwidth, u_short nheight,
2629 u_long nsrc, u_long ndest,
2630 u_short bltmode, u_short line_length)
2631
Linus Torvalds1da177e2005-04-16 15:20:36 -07002632{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002633 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002634 /* dest pitch low */
2635 vga_wgfx(regbase, CL_GR24, line_length & 0xff);
2636 /* dest pitch hi */
2637 vga_wgfx(regbase, CL_GR25, line_length >> 8);
2638 /* source pitch low */
2639 vga_wgfx(regbase, CL_GR26, line_length & 0xff);
2640 /* source pitch hi */
2641 vga_wgfx(regbase, CL_GR27, line_length >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002642
2643 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002644 /* BLT width low */
2645 vga_wgfx(regbase, CL_GR20, nwidth & 0xff);
2646 /* BLT width hi */
2647 vga_wgfx(regbase, CL_GR21, nwidth >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002648
2649 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002650 /* BLT height low */
2651 vga_wgfx(regbase, CL_GR22, nheight & 0xff);
2652 /* BLT width hi */
2653 vga_wgfx(regbase, CL_GR23, nheight >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002654
2655 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002656 /* BLT dest low */
2657 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2658 /* BLT dest mid */
2659 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2660 /* BLT dest hi */
2661 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002662
2663 /* BLT source */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002664 /* BLT src low */
2665 vga_wgfx(regbase, CL_GR2C, (u_char) (nsrc & 0xff));
2666 /* BLT src mid */
2667 vga_wgfx(regbase, CL_GR2D, (u_char) (nsrc >> 8));
2668 /* BLT src hi */
2669 vga_wgfx(regbase, CL_GR2E, (u_char) (nsrc >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002670
2671 /* BLT mode */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002672 vga_wgfx(regbase, CL_GR30, bltmode); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002673
2674 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002675 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002676
2677 /* and finally: GO! */
Krzysztof Helt527410f2009-03-31 15:25:13 -07002678 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Krzysztof Helt8343c892009-03-31 15:25:11 -07002679}
2680
2681/*******************************************************************
2682 cirrusfb_BitBLT()
2683
2684 perform accelerated "scrolling"
2685********************************************************************/
2686
2687static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
2688 u_short curx, u_short cury,
2689 u_short destx, u_short desty,
2690 u_short width, u_short height,
2691 u_short line_length)
2692{
2693 u_short nwidth = width - 1;
2694 u_short nheight = height - 1;
2695 u_long nsrc, ndest;
2696 u_char bltmode;
2697
2698 bltmode = 0x00;
2699 /* if source adr < dest addr, do the Blt backwards */
2700 if (cury <= desty) {
2701 if (cury == desty) {
2702 /* if src and dest are on the same line, check x */
2703 if (curx < destx)
2704 bltmode |= 0x01;
2705 } else
2706 bltmode |= 0x01;
2707 }
2708 /* standard case: forward blitting */
2709 nsrc = (cury * line_length) + curx;
2710 ndest = (desty * line_length) + destx;
2711 if (bltmode) {
2712 /* this means start addresses are at the end,
2713 * counting backwards
2714 */
2715 nsrc += nheight * line_length + nwidth;
2716 ndest += nheight * line_length + nwidth;
2717 }
2718
2719 cirrusfb_WaitBLT(regbase);
2720
2721 cirrusfb_set_blitter(regbase, nwidth, nheight,
2722 nsrc, ndest, bltmode, line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002723}
2724
Linus Torvalds1da177e2005-04-16 15:20:36 -07002725/*******************************************************************
2726 cirrusfb_RectFill()
2727
2728 perform accelerated rectangle fill
2729********************************************************************/
2730
Krzysztof Helt8503df62007-10-16 01:29:08 -07002731static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002732 u_short x, u_short y, u_short width, u_short height,
Krzysztof Helt9e848062009-03-31 15:25:11 -07002733 u32 fg_color, u32 bg_color, u_short line_length,
2734 u_char blitmode)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002735{
Krzysztof Helt8343c892009-03-31 15:25:11 -07002736 u_long ndest = (y * line_length) + x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002737 u_char op;
2738
Krzysztof Helt8503df62007-10-16 01:29:08 -07002739 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002740
Linus Torvalds1da177e2005-04-16 15:20:36 -07002741 /* This is a ColorExpand Blt, using the */
2742 /* same color for foreground and background */
Krzysztof Helt9e848062009-03-31 15:25:11 -07002743 vga_wgfx(regbase, VGA_GFX_SR_VALUE, bg_color);
2744 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, fg_color);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002745
Krzysztof Helt9e848062009-03-31 15:25:11 -07002746 op = 0x80;
Krzysztof Helt8343c892009-03-31 15:25:11 -07002747 if (bits_per_pixel >= 16) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002748 vga_wgfx(regbase, CL_GR10, bg_color >> 8);
2749 vga_wgfx(regbase, CL_GR11, fg_color >> 8);
2750 op = 0x90;
Krzysztof Helt8343c892009-03-31 15:25:11 -07002751 }
Krzysztof Helt7cade312009-03-31 15:25:13 -07002752 if (bits_per_pixel >= 24) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002753 vga_wgfx(regbase, CL_GR12, bg_color >> 16);
2754 vga_wgfx(regbase, CL_GR13, fg_color >> 16);
Krzysztof Helt7cade312009-03-31 15:25:13 -07002755 op = 0xa0;
2756 }
2757 if (bits_per_pixel == 32) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002758 vga_wgfx(regbase, CL_GR14, bg_color >> 24);
2759 vga_wgfx(regbase, CL_GR15, fg_color >> 24);
2760 op = 0xb0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002761 }
Krzysztof Helt8343c892009-03-31 15:25:11 -07002762 cirrusfb_set_blitter(regbase, width - 1, height - 1,
Krzysztof Helt9e848062009-03-31 15:25:11 -07002763 0, ndest, op | blitmode, line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002764}
2765
Linus Torvalds1da177e2005-04-16 15:20:36 -07002766/**************************************************************************
2767 * bestclock() - determine closest possible clock lower(?) than the
2768 * desired pixel clock
2769 **************************************************************************/
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002770static void bestclock(long freq, int *nom, int *den, int *div)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002771{
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002772 int n, d;
2773 long h, diff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002774
Krzysztof Helt8503df62007-10-16 01:29:08 -07002775 assert(nom != NULL);
2776 assert(den != NULL);
2777 assert(div != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002778
2779 *nom = 0;
2780 *den = 0;
2781 *div = 0;
2782
Linus Torvalds1da177e2005-04-16 15:20:36 -07002783 if (freq < 8000)
2784 freq = 8000;
2785
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002786 diff = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002787
2788 for (n = 32; n < 128; n++) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002789 int s = 0;
2790
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002791 d = (14318 * n) / freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002792 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002793 int temp = d;
2794
2795 if (temp > 31) {
2796 s = 1;
2797 temp >>= 1;
2798 }
2799 h = ((14318 * n) / temp) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002800 h = h > freq ? h - freq : freq - h;
2801 if (h < diff) {
2802 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002803 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002804 *den = temp;
2805 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002806 }
2807 }
Krzysztof Helt7528f542008-10-15 22:03:36 -07002808 d++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002809 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002810 if (d > 31) {
2811 s = 1;
2812 d >>= 1;
2813 }
2814 h = ((14318 * n) / d) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002815 h = h > freq ? h - freq : freq - h;
2816 if (h < diff) {
2817 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002818 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002819 *den = d;
2820 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002821 }
2822 }
2823 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002824}
2825
Linus Torvalds1da177e2005-04-16 15:20:36 -07002826/* -------------------------------------------------------------------------
2827 *
2828 * debugging functions
2829 *
2830 * -------------------------------------------------------------------------
2831 */
2832
2833#ifdef CIRRUSFB_DEBUG
2834
2835/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002836 * cirrusfb_dbg_print_regs
2837 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2838 * @reg_class: type of registers to read: %CRT, or %SEQ
2839 *
2840 * DESCRIPTION:
2841 * Dumps the given list of VGA CRTC registers. If @base is %NULL,
2842 * old-style I/O ports are queried for information, otherwise MMIO is
2843 * used at the given @base address to query the information.
2844 */
2845
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002846static void cirrusfb_dbg_print_regs(struct fb_info *info,
2847 caddr_t regbase,
2848 enum cirrusfb_dbg_reg_class reg_class, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002849{
2850 va_list list;
2851 unsigned char val = 0;
2852 unsigned reg;
2853 char *name;
2854
Krzysztof Helt8503df62007-10-16 01:29:08 -07002855 va_start(list, reg_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002856
Krzysztof Helt8503df62007-10-16 01:29:08 -07002857 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002858 while (name != NULL) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002859 reg = va_arg(list, int);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002860
2861 switch (reg_class) {
2862 case CRT:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002863 val = vga_rcrt(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002864 break;
2865 case SEQ:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002866 val = vga_rseq(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002867 break;
2868 default:
2869 /* should never occur */
Richard Knutssonc930faa2007-05-08 00:38:29 -07002870 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002871 break;
2872 }
2873
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002874 dev_dbg(info->device, "%8s = 0x%02X\n", name, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002875
Krzysztof Helt8503df62007-10-16 01:29:08 -07002876 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002877 }
2878
Krzysztof Helt8503df62007-10-16 01:29:08 -07002879 va_end(list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002880}
2881
Linus Torvalds1da177e2005-04-16 15:20:36 -07002882/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002883 * cirrusfb_dbg_reg_dump
2884 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2885 *
2886 * DESCRIPTION:
2887 * Dumps a list of interesting VGA and CIRRUSFB registers. If @base is %NULL,
2888 * old-style I/O ports are queried for information, otherwise MMIO is
2889 * used at the given @base address to query the information.
2890 */
2891
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002892static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002893{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002894 dev_dbg(info->device, "VGA CRTC register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002895
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002896 cirrusfb_dbg_print_regs(info, regbase, CRT,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002897 "CR00", 0x00,
2898 "CR01", 0x01,
2899 "CR02", 0x02,
2900 "CR03", 0x03,
2901 "CR04", 0x04,
2902 "CR05", 0x05,
2903 "CR06", 0x06,
2904 "CR07", 0x07,
2905 "CR08", 0x08,
2906 "CR09", 0x09,
2907 "CR0A", 0x0A,
2908 "CR0B", 0x0B,
2909 "CR0C", 0x0C,
2910 "CR0D", 0x0D,
2911 "CR0E", 0x0E,
2912 "CR0F", 0x0F,
2913 "CR10", 0x10,
2914 "CR11", 0x11,
2915 "CR12", 0x12,
2916 "CR13", 0x13,
2917 "CR14", 0x14,
2918 "CR15", 0x15,
2919 "CR16", 0x16,
2920 "CR17", 0x17,
2921 "CR18", 0x18,
2922 "CR22", 0x22,
2923 "CR24", 0x24,
2924 "CR26", 0x26,
2925 "CR2D", 0x2D,
2926 "CR2E", 0x2E,
2927 "CR2F", 0x2F,
2928 "CR30", 0x30,
2929 "CR31", 0x31,
2930 "CR32", 0x32,
2931 "CR33", 0x33,
2932 "CR34", 0x34,
2933 "CR35", 0x35,
2934 "CR36", 0x36,
2935 "CR37", 0x37,
2936 "CR38", 0x38,
2937 "CR39", 0x39,
2938 "CR3A", 0x3A,
2939 "CR3B", 0x3B,
2940 "CR3C", 0x3C,
2941 "CR3D", 0x3D,
2942 "CR3E", 0x3E,
2943 "CR3F", 0x3F,
2944 NULL);
2945
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002946 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002947
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002948 dev_dbg(info->device, "VGA SEQ register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002949
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002950 cirrusfb_dbg_print_regs(info, regbase, SEQ,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002951 "SR00", 0x00,
2952 "SR01", 0x01,
2953 "SR02", 0x02,
2954 "SR03", 0x03,
2955 "SR04", 0x04,
2956 "SR08", 0x08,
2957 "SR09", 0x09,
2958 "SR0A", 0x0A,
2959 "SR0B", 0x0B,
2960 "SR0D", 0x0D,
2961 "SR10", 0x10,
2962 "SR11", 0x11,
2963 "SR12", 0x12,
2964 "SR13", 0x13,
2965 "SR14", 0x14,
2966 "SR15", 0x15,
2967 "SR16", 0x16,
2968 "SR17", 0x17,
2969 "SR18", 0x18,
2970 "SR19", 0x19,
2971 "SR1A", 0x1A,
2972 "SR1B", 0x1B,
2973 "SR1C", 0x1C,
2974 "SR1D", 0x1D,
2975 "SR1E", 0x1E,
2976 "SR1F", 0x1F,
2977 NULL);
2978
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002979 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002980}
2981
2982#endif /* CIRRUSFB_DEBUG */
2983