blob: d42e385f091c7658418d844f7cc77db306bd0a93 [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,
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700148 .sr07_1bpp_mux = 0xF6,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149 .sr07_8bpp = 0xF1,
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700150 .sr07_8bpp_mux = 0xF7,
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700151 .sr1f = 0x1E
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152 },
153 [BT_PICCOLO] = {
154 .name = "CL Piccolo",
155 .maxclock = {
156 /* guess */
157 90000, 90000, 90000, 90000, 90000
158 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700159 .init_sr07 = true,
160 .init_sr1f = true,
161 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700162 .sr07 = 0x80,
163 .sr07_1bpp = 0x80,
164 .sr07_8bpp = 0x81,
165 .sr1f = 0x22
166 },
167 [BT_PICASSO] = {
168 .name = "CL Picasso",
169 .maxclock = {
170 /* guess */
171 90000, 90000, 90000, 90000, 90000
172 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700173 .init_sr07 = true,
174 .init_sr1f = true,
175 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176 .sr07 = 0x20,
177 .sr07_1bpp = 0x20,
178 .sr07_8bpp = 0x21,
179 .sr1f = 0x22
180 },
181 [BT_SPECTRUM] = {
182 .name = "CL Spectrum",
183 .maxclock = {
184 /* guess */
185 90000, 90000, 90000, 90000, 90000
186 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700187 .init_sr07 = true,
188 .init_sr1f = true,
189 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190 .sr07 = 0x80,
191 .sr07_1bpp = 0x80,
192 .sr07_8bpp = 0x81,
193 .sr1f = 0x22
194 },
195 [BT_PICASSO4] = {
196 .name = "CL Picasso4",
197 .maxclock = {
198 135100, 135100, 85500, 85500, 0
199 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700200 .init_sr07 = true,
201 .init_sr1f = false,
202 .scrn_start_bit19 = true,
Krzysztof Helt527410f2009-03-31 15:25:13 -0700203 .sr07 = 0xA0,
204 .sr07_1bpp = 0xA0,
205 .sr07_1bpp_mux = 0xA6,
206 .sr07_8bpp = 0xA1,
207 .sr07_8bpp_mux = 0xA7,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208 .sr1f = 0
209 },
210 [BT_ALPINE] = {
211 .name = "CL Alpine",
212 .maxclock = {
213 /* for the GD5430. GD5446 can do more... */
214 85500, 85500, 50000, 28500, 0
215 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700216 .init_sr07 = true,
217 .init_sr1f = true,
218 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219 .sr07 = 0xA0,
Krzysztof Helt527410f2009-03-31 15:25:13 -0700220 .sr07_1bpp = 0xA0,
221 .sr07_1bpp_mux = 0xA6,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222 .sr07_8bpp = 0xA1,
223 .sr07_8bpp_mux = 0xA7,
224 .sr1f = 0x1C
225 },
226 [BT_GD5480] = {
227 .name = "CL GD5480",
228 .maxclock = {
229 135100, 200000, 200000, 135100, 135100
230 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700231 .init_sr07 = true,
232 .init_sr1f = true,
233 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700234 .sr07 = 0x10,
235 .sr07_1bpp = 0x11,
236 .sr07_8bpp = 0x11,
237 .sr1f = 0x1C
238 },
239 [BT_LAGUNA] = {
240 .name = "CL Laguna",
241 .maxclock = {
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700242 /* taken from X11 code */
243 170000, 170000, 170000, 170000, 135100,
244 },
245 .init_sr07 = false,
246 .init_sr1f = false,
247 .scrn_start_bit19 = true,
248 },
249 [BT_LAGUNAB] = {
250 .name = "CL Laguna AGP",
251 .maxclock = {
252 /* taken from X11 code */
253 170000, 250000, 170000, 170000, 135100,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700255 .init_sr07 = false,
256 .init_sr1f = false,
257 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258 }
259};
260
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261#ifdef CONFIG_PCI
262#define CHIP(id, btype) \
Grant Coady41538122005-09-29 10:40:52 +1000263 { PCI_VENDOR_ID_CIRRUS, id, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (btype) }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264
265static struct pci_device_id cirrusfb_pci_table[] = {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700266 CHIP(PCI_DEVICE_ID_CIRRUS_5436, BT_ALPINE),
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700267 CHIP(PCI_DEVICE_ID_CIRRUS_5434_8, BT_SD64),
268 CHIP(PCI_DEVICE_ID_CIRRUS_5434_4, BT_SD64),
Krzysztof Helt8503df62007-10-16 01:29:08 -0700269 CHIP(PCI_DEVICE_ID_CIRRUS_5430, BT_ALPINE), /* GD-5440 is same id */
270 CHIP(PCI_DEVICE_ID_CIRRUS_7543, BT_ALPINE),
271 CHIP(PCI_DEVICE_ID_CIRRUS_7548, BT_ALPINE),
272 CHIP(PCI_DEVICE_ID_CIRRUS_5480, BT_GD5480), /* MacPicasso likely */
273 CHIP(PCI_DEVICE_ID_CIRRUS_5446, BT_PICASSO4), /* Picasso 4 is 5446 */
274 CHIP(PCI_DEVICE_ID_CIRRUS_5462, BT_LAGUNA), /* CL Laguna */
275 CHIP(PCI_DEVICE_ID_CIRRUS_5464, BT_LAGUNA), /* CL Laguna 3D */
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700276 CHIP(PCI_DEVICE_ID_CIRRUS_5465, BT_LAGUNAB), /* CL Laguna 3DA*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277 { 0, }
278};
279MODULE_DEVICE_TABLE(pci, cirrusfb_pci_table);
280#undef CHIP
281#endif /* CONFIG_PCI */
282
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283#ifdef CONFIG_ZORRO
284static const struct zorro_device_id cirrusfb_zorro_table[] = {
285 {
286 .id = ZORRO_PROD_HELFRICH_SD64_RAM,
287 .driver_data = BT_SD64,
288 }, {
289 .id = ZORRO_PROD_HELFRICH_PICCOLO_RAM,
290 .driver_data = BT_PICCOLO,
291 }, {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700292 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_RAM,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293 .driver_data = BT_PICASSO,
294 }, {
295 .id = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_RAM,
296 .driver_data = BT_SPECTRUM,
297 }, {
298 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z3,
299 .driver_data = BT_PICASSO4,
300 },
301 { 0 }
302};
303
304static const struct {
305 zorro_id id2;
306 unsigned long size;
307} cirrusfb_zorro_table2[] = {
308 [BT_SD64] = {
309 .id2 = ZORRO_PROD_HELFRICH_SD64_REG,
310 .size = 0x400000
311 },
312 [BT_PICCOLO] = {
313 .id2 = ZORRO_PROD_HELFRICH_PICCOLO_REG,
314 .size = 0x200000
315 },
316 [BT_PICASSO] = {
317 .id2 = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_REG,
318 .size = 0x200000
319 },
320 [BT_SPECTRUM] = {
321 .id2 = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_REG,
322 .size = 0x200000
323 },
324 [BT_PICASSO4] = {
325 .id2 = 0,
326 .size = 0x400000
327 }
328};
329#endif /* CONFIG_ZORRO */
330
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331#ifdef CIRRUSFB_DEBUG
Krzysztof Helt7345de32007-10-16 01:29:11 -0700332enum cirrusfb_dbg_reg_class {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700333 CRT,
334 SEQ
Krzysztof Helt7345de32007-10-16 01:29:11 -0700335};
Krzysztof Helt8503df62007-10-16 01:29:08 -0700336#endif /* CIRRUSFB_DEBUG */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337
338/* info about board */
339struct cirrusfb_info {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 u8 __iomem *regbase;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700341 u8 __iomem *laguna_mmio;
Krzysztof Helt7345de32007-10-16 01:29:11 -0700342 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700343 unsigned char SFR; /* Shadow of special function register */
344
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700345 int multiplexing;
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700346 int doubleVCLK;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347 int blank_mode;
Krzysztof Helt64beab12008-10-15 22:03:38 -0700348 u32 pseudo_palette[16];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700350 void (*unmap)(struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351};
352
Krzysztof Helt55a0dd82008-10-15 22:03:41 -0700353static int noaccel __devinitdata;
Krzysztof Helta1d35a72008-10-15 22:03:38 -0700354static char *mode_option __devinitdata = "640x480@60";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355
356/****************************************************************************/
357/**** BEGIN PROTOTYPES ******************************************************/
358
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359/*--- Interface used by the world ------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700360static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
361 struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363/*--- Internal routines ----------------------------------------------------*/
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700364static void init_vgachip(struct fb_info *info);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700365static void switch_monitor(struct cirrusfb_info *cinfo, int on);
366static void WGen(const struct cirrusfb_info *cinfo,
367 int regnum, unsigned char val);
368static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum);
369static void AttrOn(const struct cirrusfb_info *cinfo);
370static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val);
371static void WSFR(struct cirrusfb_info *cinfo, unsigned char val);
372static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val);
373static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum,
374 unsigned char red, unsigned char green, unsigned char blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375#if 0
Krzysztof Helt8503df62007-10-16 01:29:08 -0700376static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum,
377 unsigned char *red, unsigned char *green,
378 unsigned char *blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700379#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -0700380static void cirrusfb_WaitBLT(u8 __iomem *regbase);
381static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
382 u_short curx, u_short cury,
383 u_short destx, u_short desty,
384 u_short width, u_short height,
385 u_short line_length);
386static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
387 u_short x, u_short y,
388 u_short width, u_short height,
Krzysztof Helt9e848062009-03-31 15:25:11 -0700389 u32 fg_color, u32 bg_color,
390 u_short line_length, u_char blitmode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700392static void bestclock(long freq, int *nom, int *den, int *div);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393
394#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700395static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase);
396static void cirrusfb_dbg_print_regs(struct fb_info *info,
397 caddr_t regbase,
Krzysztof Helt7345de32007-10-16 01:29:11 -0700398 enum cirrusfb_dbg_reg_class reg_class, ...);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700399#endif /* CIRRUSFB_DEBUG */
400
401/*** END PROTOTYPES ********************************************************/
402/*****************************************************************************/
403/*** BEGIN Interface Used by the World ***************************************/
404
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700405static inline int is_laguna(const struct cirrusfb_info *cinfo)
406{
407 return cinfo->btype == BT_LAGUNA || cinfo->btype == BT_LAGUNAB;
408}
409
Krzysztof Helt8503df62007-10-16 01:29:08 -0700410static int opencount;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411
412/*--- Open /dev/fbx ---------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700413static int cirrusfb_open(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414{
415 if (opencount++ == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700416 switch_monitor(info->par, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417 return 0;
418}
419
420/*--- Close /dev/fbx --------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700421static int cirrusfb_release(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422{
423 if (--opencount == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700424 switch_monitor(info->par, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700425 return 0;
426}
427
428/**** END Interface used by the World *************************************/
429/****************************************************************************/
430/**** BEGIN Hardware specific Routines **************************************/
431
Krzysztof Helt486ff382008-10-15 22:03:42 -0700432/* Check if the MCLK is not a better clock source */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700433static int cirrusfb_check_mclk(struct fb_info *info, long freq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700435 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700436 long mclk = vga_rseq(cinfo->regbase, CL_SEQR1F) & 0x3f;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437
Krzysztof Helt486ff382008-10-15 22:03:42 -0700438 /* Read MCLK value */
439 mclk = (14318 * mclk) >> 3;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700440 dev_dbg(info->device, "Read MCLK of %ld kHz\n", mclk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700441
442 /* Determine if we should use MCLK instead of VCLK, and if so, what we
Krzysztof Helt486ff382008-10-15 22:03:42 -0700443 * should divide it by to get VCLK
444 */
445
446 if (abs(freq - mclk) < 250) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700447 dev_dbg(info->device, "Using VCLK = MCLK\n");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700448 return 1;
449 } else if (abs(freq - (mclk / 2)) < 250) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700450 dev_dbg(info->device, "Using VCLK = MCLK/2\n");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700451 return 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452 }
453
Krzysztof Helt486ff382008-10-15 22:03:42 -0700454 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455}
456
Krzysztof Helt99a45842009-03-31 15:25:09 -0700457static int cirrusfb_check_pixclock(const struct fb_var_screeninfo *var,
458 struct fb_info *info)
459{
460 long freq;
461 long maxclock;
462 struct cirrusfb_info *cinfo = info->par;
463 unsigned maxclockidx = var->bits_per_pixel >> 3;
464
465 /* convert from ps to kHz */
466 freq = PICOS2KHZ(var->pixclock);
467
468 dev_dbg(info->device, "desired pixclock: %ld kHz\n", freq);
469
470 maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx];
471 cinfo->multiplexing = 0;
472
473 /* If the frequency is greater than we can support, we might be able
474 * to use multiplexing for the video mode */
475 if (freq > maxclock) {
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700476 dev_err(info->device,
477 "Frequency greater than maxclock (%ld kHz)\n",
478 maxclock);
479 return -EINVAL;
480 }
481 /*
482 * Additional constraint: 8bpp uses DAC clock doubling to allow maximum
483 * pixel clock
484 */
485 if (var->bits_per_pixel == 8) {
Krzysztof Helt99a45842009-03-31 15:25:09 -0700486 switch (cinfo->btype) {
487 case BT_ALPINE:
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700488 case BT_SD64:
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700489 case BT_PICASSO4:
490 if (freq > 85500)
491 cinfo->multiplexing = 1;
492 break;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700493 case BT_GD5480:
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700494 if (freq > 135100)
495 cinfo->multiplexing = 1;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700496 break;
497
498 default:
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700499 break;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700500 }
501 }
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700502
503 /* If we have a 1MB 5434, we need to put ourselves in a mode where
Krzysztof Helt99a45842009-03-31 15:25:09 -0700504 * the VCLK is double the pixel clock. */
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700505 cinfo->doubleVCLK = 0;
506 if (cinfo->btype == BT_SD64 && info->fix.smem_len <= MB_ &&
507 var->bits_per_pixel == 16) {
508 cinfo->doubleVCLK = 1;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700509 }
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700510
Krzysztof Helt99a45842009-03-31 15:25:09 -0700511 return 0;
512}
513
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514static int cirrusfb_check_var(struct fb_var_screeninfo *var,
515 struct fb_info *info)
516{
Krzysztof Helt09a29102008-09-02 14:35:51 -0700517 int yres;
518 /* memory size in pixels */
519 unsigned pixels = info->screen_size * 8 / var->bits_per_pixel;
Krzysztof Helt614c0dc2009-03-31 15:25:15 -0700520 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521
522 switch (var->bits_per_pixel) {
Krzysztof Helt060b6002007-10-16 01:29:13 -0700523 case 1:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524 var->red.offset = 0;
525 var->red.length = 1;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700526 var->green = var->red;
527 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528 break;
529
530 case 8:
531 var->red.offset = 0;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700532 var->red.length = 8;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700533 var->green = var->red;
534 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 break;
536
537 case 16:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700538 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539 var->red.offset = 2;
540 var->green.offset = -3;
541 var->blue.offset = 8;
542 } else {
Krzysztof Heltc4dec392009-03-31 15:25:07 -0700543 var->red.offset = 11;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 var->green.offset = 5;
545 var->blue.offset = 0;
546 }
547 var->red.length = 5;
Krzysztof Heltc4dec392009-03-31 15:25:07 -0700548 var->green.length = 6;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 var->blue.length = 5;
550 break;
551
Krzysztof Helt7cade312009-03-31 15:25:13 -0700552 case 24:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700553 if (isPReP) {
Krzysztof Helt7cade312009-03-31 15:25:13 -0700554 var->red.offset = 0;
555 var->green.offset = 8;
556 var->blue.offset = 16;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700557 } else {
558 var->red.offset = 16;
559 var->green.offset = 8;
560 var->blue.offset = 0;
561 }
562 var->red.length = 8;
563 var->green.length = 8;
564 var->blue.length = 8;
565 break;
566
567 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700568 dev_dbg(info->device,
569 "Unsupported bpp size: %d\n", var->bits_per_pixel);
Richard Knutssonc930faa2007-05-08 00:38:29 -0700570 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571 /* should never occur */
572 break;
573 }
574
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700575 if (var->xres_virtual < var->xres)
576 var->xres_virtual = var->xres;
577 /* use highest possible virtual resolution */
578 if (var->yres_virtual == -1) {
579 var->yres_virtual = pixels / var->xres_virtual;
580
581 dev_info(info->device,
582 "virtual resolution set to maximum of %dx%d\n",
583 var->xres_virtual, var->yres_virtual);
584 }
585 if (var->yres_virtual < var->yres)
586 var->yres_virtual = var->yres;
587
588 if (var->xres_virtual * var->yres_virtual > pixels) {
589 dev_err(info->device, "mode %dx%dx%d rejected... "
590 "virtual resolution too high to fit into video memory!\n",
591 var->xres_virtual, var->yres_virtual,
592 var->bits_per_pixel);
593 return -EINVAL;
594 }
595
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700596 if (var->xoffset < 0)
597 var->xoffset = 0;
598 if (var->yoffset < 0)
599 var->yoffset = 0;
600
601 /* truncate xoffset and yoffset to maximum if too high */
602 if (var->xoffset > var->xres_virtual - var->xres)
603 var->xoffset = var->xres_virtual - var->xres - 1;
604 if (var->yoffset > var->yres_virtual - var->yres)
605 var->yoffset = var->yres_virtual - var->yres - 1;
606
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607 var->red.msb_right =
608 var->green.msb_right =
609 var->blue.msb_right =
610 var->transp.offset =
611 var->transp.length =
612 var->transp.msb_right = 0;
613
614 yres = var->yres;
615 if (var->vmode & FB_VMODE_DOUBLE)
616 yres *= 2;
617 else if (var->vmode & FB_VMODE_INTERLACED)
618 yres = (yres + 1) / 2;
619
620 if (yres >= 1280) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700621 dev_err(info->device, "ERROR: VerticalTotal >= 1280; "
Krzysztof Helt8503df62007-10-16 01:29:08 -0700622 "special treatment required! (TODO)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623 return -EINVAL;
624 }
625
Krzysztof Helt99a45842009-03-31 15:25:09 -0700626 if (cirrusfb_check_pixclock(var, info))
627 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628
Krzysztof Helt614c0dc2009-03-31 15:25:15 -0700629 if (!is_laguna(cinfo))
630 var->accel_flags = FB_ACCELF_TEXT;
631
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632 return 0;
633}
634
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700635static void cirrusfb_set_mclk_as_source(const struct fb_info *info, int div)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700637 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700638 unsigned char old1f, old1e;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700639
Krzysztof Helt8503df62007-10-16 01:29:08 -0700640 assert(cinfo != NULL);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700641 old1f = vga_rseq(cinfo->regbase, CL_SEQR1F) & ~0x40;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642
Krzysztof Helt486ff382008-10-15 22:03:42 -0700643 if (div) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700644 dev_dbg(info->device, "Set %s as pixclock source.\n",
645 (div == 2) ? "MCLK/2" : "MCLK");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700646 old1f |= 0x40;
647 old1e = vga_rseq(cinfo->regbase, CL_SEQR1E) & ~0x1;
648 if (div == 2)
649 old1e |= 1;
650
651 vga_wseq(cinfo->regbase, CL_SEQR1E, old1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700653 vga_wseq(cinfo->regbase, CL_SEQR1F, old1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654}
655
656/*************************************************************************
657 cirrusfb_set_par_foo()
658
659 actually writes the values for a new video mode into the hardware,
660**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700661static int cirrusfb_set_par_foo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662{
663 struct cirrusfb_info *cinfo = info->par;
664 struct fb_var_screeninfo *var = &info->var;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 u8 __iomem *regbase = cinfo->regbase;
666 unsigned char tmp;
Krzysztof Helt6683e012009-03-31 15:25:06 -0700667 int pitch;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668 const struct cirrusfb_board_info_rec *bi;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700669 int hdispend, hsyncstart, hsyncend, htotal;
670 int yres, vdispend, vsyncstart, vsyncend, vtotal;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700671 long freq;
672 int nom, den, div;
Krzysztof Helt1b48cb52009-03-31 15:25:08 -0700673 unsigned int control = 0, format = 0, threshold = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700675 dev_dbg(info->device, "Requested mode: %dx%dx%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt99a45842009-03-31 15:25:09 -0700677
678 switch (var->bits_per_pixel) {
679 case 1:
680 info->fix.line_length = var->xres_virtual / 8;
681 info->fix.visual = FB_VISUAL_MONO10;
682 break;
683
684 case 8:
685 info->fix.line_length = var->xres_virtual;
686 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
687 break;
688
689 case 16:
Krzysztof Helt7cade312009-03-31 15:25:13 -0700690 case 24:
Krzysztof Helt99a45842009-03-31 15:25:09 -0700691 info->fix.line_length = var->xres_virtual *
692 var->bits_per_pixel >> 3;
693 info->fix.visual = FB_VISUAL_TRUECOLOR;
694 break;
695 }
696 info->fix.type = FB_TYPE_PACKED_PIXELS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700698 init_vgachip(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700 bi = &cirrusfb_board_info[cinfo->btype];
701
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700702 hsyncstart = var->xres + var->right_margin;
703 hsyncend = hsyncstart + var->hsync_len;
Krzysztof Helt8636a922009-03-31 15:25:17 -0700704 htotal = (hsyncend + var->left_margin) / 8;
705 hdispend = var->xres / 8;
706 hsyncstart = hsyncstart / 8;
707 hsyncend = hsyncend / 8;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700708
Krzysztof Helt8636a922009-03-31 15:25:17 -0700709 vdispend = var->yres;
710 vsyncstart = vdispend + var->lower_margin;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700711 vsyncend = vsyncstart + var->vsync_len;
712 vtotal = vsyncend + var->upper_margin;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700713
714 if (var->vmode & FB_VMODE_DOUBLE) {
Krzysztof Helt8636a922009-03-31 15:25:17 -0700715 vdispend *= 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700716 vsyncstart *= 2;
717 vsyncend *= 2;
718 vtotal *= 2;
719 } else if (var->vmode & FB_VMODE_INTERLACED) {
Krzysztof Helt8636a922009-03-31 15:25:17 -0700720 vdispend = (vdispend + 1) / 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700721 vsyncstart = (vsyncstart + 1) / 2;
722 vsyncend = (vsyncend + 1) / 2;
723 vtotal = (vtotal + 1) / 2;
724 }
Krzysztof Helt8636a922009-03-31 15:25:17 -0700725 yres = vdispend;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700726 if (yres >= 1024) {
727 vtotal /= 2;
728 vsyncstart /= 2;
729 vsyncend /= 2;
730 vdispend /= 2;
731 }
Krzysztof Helt8636a922009-03-31 15:25:17 -0700732
733 vdispend -= 1;
734 vsyncstart -= 1;
735 vsyncend -= 1;
736 vtotal -= 2;
737
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700738 if (cinfo->multiplexing) {
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700739 htotal /= 2;
740 hsyncstart /= 2;
741 hsyncend /= 2;
742 hdispend /= 2;
743 }
Krzysztof Helt8636a922009-03-31 15:25:17 -0700744
745 htotal -= 5;
746 hdispend -= 1;
747 hsyncstart += 1;
748 hsyncend += 1;
749
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750 /* unlock register VGA_CRTC_H_TOTAL..CRT7 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700751 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20); /* previously: 0x00) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752
753 /* if debugging is enabled, all parameters get output before writing */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700754 dev_dbg(info->device, "CRT0: %d\n", htotal);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700755 vga_wcrt(regbase, VGA_CRTC_H_TOTAL, htotal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700757 dev_dbg(info->device, "CRT1: %d\n", hdispend);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700758 vga_wcrt(regbase, VGA_CRTC_H_DISP, hdispend);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700760 dev_dbg(info->device, "CRT2: %d\n", var->xres / 8);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700761 vga_wcrt(regbase, VGA_CRTC_H_BLANK_START, var->xres / 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762
Krzysztof Helt8503df62007-10-16 01:29:08 -0700763 /* + 128: Compatible read */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700764 dev_dbg(info->device, "CRT3: 128+%d\n", (htotal + 5) % 32);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700765 vga_wcrt(regbase, VGA_CRTC_H_BLANK_END,
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700766 128 + ((htotal + 5) % 32));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700768 dev_dbg(info->device, "CRT4: %d\n", hsyncstart);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700769 vga_wcrt(regbase, VGA_CRTC_H_SYNC_START, hsyncstart);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700771 tmp = hsyncend % 32;
772 if ((htotal + 5) & 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773 tmp += 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700774 dev_dbg(info->device, "CRT5: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700775 vga_wcrt(regbase, VGA_CRTC_H_SYNC_END, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700777 dev_dbg(info->device, "CRT6: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700778 vga_wcrt(regbase, VGA_CRTC_V_TOTAL, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779
780 tmp = 16; /* LineCompare bit #9 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700781 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700783 if (vdispend & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784 tmp |= 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700785 if (vsyncstart & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786 tmp |= 4;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700787 if ((vdispend + 1) & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788 tmp |= 8;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700789 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700791 if (vdispend & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700792 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700793 if (vsyncstart & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 tmp |= 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700795 dev_dbg(info->device, "CRT7: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700796 vga_wcrt(regbase, VGA_CRTC_OVERFLOW, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797
798 tmp = 0x40; /* LineCompare bit #8 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700799 if ((vdispend + 1) & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800 tmp |= 0x20;
801 if (var->vmode & FB_VMODE_DOUBLE)
802 tmp |= 0x80;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700803 dev_dbg(info->device, "CRT9: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700804 vga_wcrt(regbase, VGA_CRTC_MAX_SCAN, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700806 dev_dbg(info->device, "CRT10: %d\n", vsyncstart & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700807 vga_wcrt(regbase, VGA_CRTC_V_SYNC_START, vsyncstart & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700809 dev_dbg(info->device, "CRT11: 64+32+%d\n", vsyncend % 16);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700810 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, vsyncend % 16 + 64 + 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700812 dev_dbg(info->device, "CRT12: %d\n", vdispend & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700813 vga_wcrt(regbase, VGA_CRTC_V_DISP_END, vdispend & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700815 dev_dbg(info->device, "CRT15: %d\n", (vdispend + 1) & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700816 vga_wcrt(regbase, VGA_CRTC_V_BLANK_START, (vdispend + 1) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700818 dev_dbg(info->device, "CRT16: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700819 vga_wcrt(regbase, VGA_CRTC_V_BLANK_END, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700821 dev_dbg(info->device, "CRT18: 0xff\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700822 vga_wcrt(regbase, VGA_CRTC_LINE_COMPARE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823
824 tmp = 0;
825 if (var->vmode & FB_VMODE_INTERLACED)
826 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700827 if ((htotal + 5) & 64)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828 tmp |= 16;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700829 if ((htotal + 5) & 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700831 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700833 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700834 tmp |= 128;
835
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700836 dev_dbg(info->device, "CRT1a: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700837 vga_wcrt(regbase, CL_CRT1A, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700838
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700839 freq = PICOS2KHZ(var->pixclock);
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700840 if (var->bits_per_pixel == 24)
841 if (cinfo->btype == BT_ALPINE || cinfo->btype == BT_SD64)
842 freq *= 3;
Krzysztof Heltdd14f712009-03-31 15:25:14 -0700843 if (cinfo->multiplexing)
844 freq /= 2;
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700845 if (cinfo->doubleVCLK)
846 freq *= 2;
Krzysztof Helt7cade312009-03-31 15:25:13 -0700847
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700848 bestclock(freq, &nom, &den, &div);
849
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700850 dev_dbg(info->device, "VCLK freq: %ld kHz nom: %d den: %d div: %d\n",
851 freq, nom, den, div);
852
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853 /* set VCLK0 */
854 /* hardware RefClock: 14.31818 MHz */
855 /* formula: VClk = (OSC * N) / (D * (1+P)) */
856 /* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */
857
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700858 if (cinfo->btype == BT_ALPINE || cinfo->btype == BT_PICASSO4 ||
859 cinfo->btype == BT_SD64) {
Krzysztof Helt486ff382008-10-15 22:03:42 -0700860 /* if freq is close to mclk or mclk/2 select mclk
861 * as clock source
862 */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700863 int divMCLK = cirrusfb_check_mclk(info, freq);
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700864 if (divMCLK)
Krzysztof Helt486ff382008-10-15 22:03:42 -0700865 nom = 0;
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700866 cirrusfb_set_mclk_as_source(info, divMCLK);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700867 }
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700868 if (is_laguna(cinfo)) {
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700869 long pcifc = fb_readl(cinfo->laguna_mmio + 0x3fc);
870 unsigned char tile = fb_readb(cinfo->laguna_mmio + 0x407);
871 unsigned short tile_control;
872
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700873 if (cinfo->btype == BT_LAGUNAB) {
874 tile_control = fb_readw(cinfo->laguna_mmio + 0x2c4);
875 tile_control &= ~0x80;
876 fb_writew(tile_control, cinfo->laguna_mmio + 0x2c4);
877 }
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700878
879 fb_writel(pcifc | 0x10000000l, cinfo->laguna_mmio + 0x3fc);
880 fb_writeb(tile & 0x3f, cinfo->laguna_mmio + 0x407);
881 control = fb_readw(cinfo->laguna_mmio + 0x402);
882 threshold = fb_readw(cinfo->laguna_mmio + 0xea);
883 control &= ~0x6800;
884 format = 0;
Krzysztof Helt4242a232009-03-31 15:25:17 -0700885 threshold &= 0xffc0 & 0x3fbf;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700886 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700887 if (nom) {
Krzysztof Helt486ff382008-10-15 22:03:42 -0700888 tmp = den << 1;
889 if (div != 0)
890 tmp |= 1;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700891 /* 6 bit denom; ONLY 5434!!! (bugged me 10 days) */
892 if ((cinfo->btype == BT_SD64) ||
893 (cinfo->btype == BT_ALPINE) ||
894 (cinfo->btype == BT_GD5480))
895 tmp |= 0x80;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700897 /* Laguna chipset has reversed clock registers */
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700898 if (is_laguna(cinfo)) {
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700899 vga_wseq(regbase, CL_SEQRE, tmp);
900 vga_wseq(regbase, CL_SEQR1E, nom);
901 } else {
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700902 vga_wseq(regbase, CL_SEQRE, nom);
903 vga_wseq(regbase, CL_SEQR1E, tmp);
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700904 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700905 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700907 if (yres >= 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700908 /* 1280x1024 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700909 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910 else
911 /* mode control: VGA_CRTC_START_HI enable, ROTATE(?), 16bit
912 * address wrap, no compat. */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700913 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915 /* don't know if it would hurt to also program this if no interlaced */
916 /* mode is used, but I feel better this way.. :-) */
917 if (var->vmode & FB_VMODE_INTERLACED)
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700918 vga_wcrt(regbase, VGA_CRTC_REGS, htotal / 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700919 else
Krzysztof Helt8503df62007-10-16 01:29:08 -0700920 vga_wcrt(regbase, VGA_CRTC_REGS, 0x00); /* interlace control */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700922 /* adjust horizontal/vertical sync type (low/high), use VCLK3 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700923 /* enable display memory & CRTC I/O address for color mode */
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -0700924 tmp = 0x03 | 0xc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
926 tmp |= 0x40;
927 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
928 tmp |= 0x80;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700929 WGen(cinfo, VGA_MIS_W, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930
Krzysztof Helt8503df62007-10-16 01:29:08 -0700931 /* text cursor on and start line */
932 vga_wcrt(regbase, VGA_CRTC_CURSOR_START, 0);
933 /* text cursor end line */
934 vga_wcrt(regbase, VGA_CRTC_CURSOR_END, 31);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935
936 /******************************************************
937 *
938 * 1 bpp
939 *
940 */
941
942 /* programming for different color depths */
943 if (var->bits_per_pixel == 1) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700944 dev_dbg(info->device, "preparing for 1 bit deep display\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700945 vga_wgfx(regbase, VGA_GFX_MODE, 0); /* mode register */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946
947 /* SR07 */
948 switch (cinfo->btype) {
949 case BT_SD64:
950 case BT_PICCOLO:
951 case BT_PICASSO:
952 case BT_SPECTRUM:
953 case BT_PICASSO4:
954 case BT_ALPINE:
955 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700956 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700957 cinfo->multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700958 bi->sr07_1bpp_mux : bi->sr07_1bpp);
959 break;
960
961 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700962 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700963 vga_wseq(regbase, CL_SEQR7,
964 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965 break;
966
967 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700968 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700969 break;
970 }
971
972 /* Extended Sequencer Mode */
973 switch (cinfo->btype) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974
975 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -0700976 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700977 /* evtl d0 bei 1 bit? avoid FIFO underruns..? */
978 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979 break;
980
981 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700982 /* ## vorher d0 avoid FIFO underruns..? */
983 vga_wseq(regbase, CL_SEQRF, 0xd0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984 break;
985
Krzysztof Helt8f19e152009-03-31 15:25:15 -0700986 case BT_SD64:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700987 case BT_PICASSO4:
988 case BT_ALPINE:
989 case BT_GD5480:
990 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700991 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700992 /* do nothing */
993 break;
994
995 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700996 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700997 break;
998 }
999
Krzysztof Helt8503df62007-10-16 01:29:08 -07001000 /* pixel mask: pass-through for first plane */
1001 WGen(cinfo, VGA_PEL_MSK, 0x01);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001002 if (cinfo->multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001003 /* hidden dac reg: 1280x1024 */
1004 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001005 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001006 /* hidden dac: nothing */
1007 WHDR(cinfo, 0);
1008 /* memory mode: odd/even, ext. memory */
1009 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x06);
1010 /* plane mask: only write to first plane */
1011 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001012 }
1013
1014 /******************************************************
1015 *
1016 * 8 bpp
1017 *
1018 */
1019
1020 else if (var->bits_per_pixel == 8) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001021 dev_dbg(info->device, "preparing for 8 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022 switch (cinfo->btype) {
1023 case BT_SD64:
1024 case BT_PICCOLO:
1025 case BT_PICASSO:
1026 case BT_SPECTRUM:
1027 case BT_PICASSO4:
1028 case BT_ALPINE:
1029 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001030 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001031 cinfo->multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -07001032 bi->sr07_8bpp_mux : bi->sr07_8bpp);
1033 break;
1034
1035 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001036 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001037 vga_wseq(regbase, CL_SEQR7,
1038 vga_rseq(regbase, CL_SEQR7) | 0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001039 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001040 break;
1041
1042 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001043 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044 break;
1045 }
1046
1047 switch (cinfo->btype) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048 case BT_PICCOLO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049 case BT_PICASSO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001051 /* Fast Page-Mode writes */
1052 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053 break;
1054
1055 case BT_PICASSO4:
1056#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001057 /* ### INCOMPLETE!! */
1058 vga_wseq(regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060 case BT_ALPINE:
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001061 case BT_SD64:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062 case BT_GD5480:
1063 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001064 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065 /* do nothing */
1066 break;
1067
1068 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001069 dev_warn(info->device, "unknown board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070 break;
1071 }
1072
Krzysztof Helt8503df62007-10-16 01:29:08 -07001073 /* mode register: 256 color mode */
1074 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001075 if (cinfo->multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001076 /* hidden dac reg: 1280x1024 */
1077 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001079 /* hidden dac: nothing */
1080 WHDR(cinfo, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001081 }
1082
1083 /******************************************************
1084 *
1085 * 16 bpp
1086 *
1087 */
1088
1089 else if (var->bits_per_pixel == 16) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001090 dev_dbg(info->device, "preparing for 16 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001091 switch (cinfo->btype) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001092 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001093 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001094 vga_wseq(regbase, CL_SEQR7, 0x87);
1095 /* Fast Page-Mode writes */
1096 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001097 break;
1098
1099 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001100 vga_wseq(regbase, CL_SEQR7, 0x27);
1101 /* Fast Page-Mode writes */
1102 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001103 break;
1104
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001105 case BT_SD64:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106 case BT_PICASSO4:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001107 case BT_ALPINE:
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001108 /* Extended Sequencer Mode: 256c col. mode */
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001109 vga_wseq(regbase, CL_SEQR7,
1110 cinfo->doubleVCLK ? 0xa3 : 0xa7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001111 break;
1112
1113 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001114 vga_wseq(regbase, CL_SEQR7, 0x17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001115 /* We already set SRF and SR1F */
1116 break;
1117
1118 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001119 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001120 vga_wseq(regbase, CL_SEQR7,
1121 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001122 control |= 0x2000;
1123 format |= 0x1400;
1124 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001125 break;
1126
1127 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001128 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129 break;
1130 }
1131
Krzysztof Helt8503df62007-10-16 01:29:08 -07001132 /* mode register: 256 color mode */
1133 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001134#ifdef CONFIG_PCI
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001135 WHDR(cinfo, cinfo->doubleVCLK ? 0xe1 : 0xc1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001136#elif defined(CONFIG_ZORRO)
1137 /* FIXME: CONFIG_PCI and CONFIG_ZORRO may be defined both */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001138 WHDR(cinfo, 0xa0); /* hidden dac reg: nothing special */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001140 }
1141
1142 /******************************************************
1143 *
Krzysztof Helt7cade312009-03-31 15:25:13 -07001144 * 24 bpp
Linus Torvalds1da177e2005-04-16 15:20:36 -07001145 *
1146 */
1147
Krzysztof Helt7cade312009-03-31 15:25:13 -07001148 else if (var->bits_per_pixel == 24) {
1149 dev_dbg(info->device, "preparing for 24 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001150 switch (cinfo->btype) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07001151 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001152 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001153 vga_wseq(regbase, CL_SEQR7, 0x85);
1154 /* Fast Page-Mode writes */
1155 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001156 break;
1157
1158 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001159 vga_wseq(regbase, CL_SEQR7, 0x25);
1160 /* Fast Page-Mode writes */
1161 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001162 break;
1163
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001164 case BT_SD64:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001165 case BT_PICASSO4:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001166 case BT_ALPINE:
Krzysztof Helt8f19e152009-03-31 15:25:15 -07001167 /* Extended Sequencer Mode: 256c col. mode */
Krzysztof Helt7cade312009-03-31 15:25:13 -07001168 vga_wseq(regbase, CL_SEQR7, 0xa5);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169 break;
1170
1171 case BT_GD5480:
Krzysztof Helt7cade312009-03-31 15:25:13 -07001172 vga_wseq(regbase, CL_SEQR7, 0x15);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001173 /* We already set SRF and SR1F */
1174 break;
1175
1176 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001177 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001178 vga_wseq(regbase, CL_SEQR7,
1179 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt7cade312009-03-31 15:25:13 -07001180 control |= 0x4000;
1181 format |= 0x2400;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001182 threshold |= 0x20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001183 break;
1184
1185 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001186 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001187 break;
1188 }
1189
Krzysztof Helt8503df62007-10-16 01:29:08 -07001190 /* mode register: 256 color mode */
1191 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001192 /* hidden dac reg: 8-8-8 mode (24 or 32) */
1193 WHDR(cinfo, 0xc5);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001194 }
1195
1196 /******************************************************
1197 *
1198 * unknown/unsupported bpp
1199 *
1200 */
1201
Krzysztof Helt8503df62007-10-16 01:29:08 -07001202 else
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001203 dev_err(info->device,
1204 "What's this? requested color depth == %d.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001205 var->bits_per_pixel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001206
Krzysztof Helt6683e012009-03-31 15:25:06 -07001207 pitch = info->fix.line_length >> 3;
1208 vga_wcrt(regbase, VGA_CRTC_OFFSET, pitch & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001209 tmp = 0x22;
Krzysztof Helt6683e012009-03-31 15:25:06 -07001210 if (pitch & 0x100)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001211 tmp |= 0x10; /* offset overflow bit */
1212
Krzysztof Helt8503df62007-10-16 01:29:08 -07001213 /* screen start addr #16-18, fastpagemode cycles */
1214 vga_wcrt(regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001215
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001216 /* screen start address bit 19 */
1217 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19)
Krzysztof Helt6683e012009-03-31 15:25:06 -07001218 vga_wcrt(regbase, CL_CRT1D, (pitch >> 9) & 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001219
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001220 if (is_laguna(cinfo)) {
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001221 tmp = 0;
1222 if ((htotal + 5) & 256)
1223 tmp |= 128;
1224 if (hdispend & 256)
1225 tmp |= 64;
1226 if (hsyncstart & 256)
1227 tmp |= 48;
1228 if (vtotal & 1024)
1229 tmp |= 8;
1230 if (vdispend & 1024)
1231 tmp |= 4;
1232 if (vsyncstart & 1024)
1233 tmp |= 3;
1234
1235 vga_wcrt(regbase, CL_CRT1E, tmp);
1236 dev_dbg(info->device, "CRT1e: %d\n", tmp);
1237 }
1238
Krzysztof Helt8503df62007-10-16 01:29:08 -07001239 /* pixel panning */
1240 vga_wattr(regbase, CL_AR33, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001241
1242 /* [ EGS: SetOffset(); ] */
1243 /* From SetOffset(): Turn on VideoEnable bit in Attribute controller */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001244 AttrOn(cinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001245
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001246 if (is_laguna(cinfo)) {
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001247 /* no tiles */
1248 fb_writew(control | 0x1000, cinfo->laguna_mmio + 0x402);
1249 fb_writew(format, cinfo->laguna_mmio + 0xc0);
1250 fb_writew(threshold, cinfo->laguna_mmio + 0xea);
1251 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001252 /* finally, turn on everything - turn off "FullBandwidth" bit */
1253 /* also, set "DotClock%2" bit where requested */
1254 tmp = 0x01;
1255
1256/*** FB_VMODE_CLOCK_HALVE in linux/fb.h not defined anymore ?
1257 if (var->vmode & FB_VMODE_CLOCK_HALVE)
1258 tmp |= 0x08;
1259*/
1260
Krzysztof Helt8503df62007-10-16 01:29:08 -07001261 vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, tmp);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001262 dev_dbg(info->device, "CL_SEQR1: %d\n", tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001263
Linus Torvalds1da177e2005-04-16 15:20:36 -07001264#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001265 cirrusfb_dbg_reg_dump(info, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001266#endif
1267
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268 return 0;
1269}
1270
1271/* for some reason incomprehensible to me, cirrusfb requires that you write
1272 * the registers twice for the settings to take..grr. -dte */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001273static int cirrusfb_set_par(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001274{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001275 cirrusfb_set_par_foo(info);
1276 return cirrusfb_set_par_foo(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001277}
1278
Krzysztof Helt8503df62007-10-16 01:29:08 -07001279static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
1280 unsigned blue, unsigned transp,
1281 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001282{
1283 struct cirrusfb_info *cinfo = info->par;
1284
1285 if (regno > 255)
1286 return -EINVAL;
1287
1288 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
1289 u32 v;
1290 red >>= (16 - info->var.red.length);
1291 green >>= (16 - info->var.green.length);
1292 blue >>= (16 - info->var.blue.length);
1293
Krzysztof Helt8503df62007-10-16 01:29:08 -07001294 if (regno >= 16)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001295 return 1;
1296 v = (red << info->var.red.offset) |
1297 (green << info->var.green.offset) |
1298 (blue << info->var.blue.offset);
1299
Krzysztof Helt060b6002007-10-16 01:29:13 -07001300 cinfo->pseudo_palette[regno] = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001301 return 0;
1302 }
1303
Krzysztof Helt8503df62007-10-16 01:29:08 -07001304 if (info->var.bits_per_pixel == 8)
1305 WClut(cinfo, regno, red >> 10, green >> 10, blue >> 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001306
1307 return 0;
1308
1309}
1310
1311/*************************************************************************
1312 cirrusfb_pan_display()
1313
1314 performs display panning - provided hardware permits this
1315**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07001316static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
1317 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001318{
Krzysztof Helt99a45842009-03-31 15:25:09 -07001319 int xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001320 unsigned long base;
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001321 unsigned char tmp, xpix;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322 struct cirrusfb_info *cinfo = info->par;
1323
Linus Torvalds1da177e2005-04-16 15:20:36 -07001324 /* no range checks for xoffset and yoffset, */
1325 /* as fb_pan_display has already done this */
1326 if (var->vmode & FB_VMODE_YWRAP)
1327 return -EINVAL;
1328
Linus Torvalds1da177e2005-04-16 15:20:36 -07001329 xoffset = var->xoffset * info->var.bits_per_pixel / 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001330
Krzysztof Helt99a45842009-03-31 15:25:09 -07001331 base = var->yoffset * info->fix.line_length + xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001332
1333 if (info->var.bits_per_pixel == 1) {
1334 /* base is already correct */
1335 xpix = (unsigned char) (var->xoffset % 8);
1336 } else {
1337 base /= 4;
1338 xpix = (unsigned char) ((xoffset % 4) * 2);
1339 }
1340
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001341 if (!is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001342 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001343
1344 /* lower 8 + 8 bits of screen start address */
Krzysztof Helt99a45842009-03-31 15:25:09 -07001345 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, base & 0xff);
1346 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, (base >> 8) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001347
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001348 /* 0xf2 is %11110010, exclude tmp bits */
1349 tmp = vga_rcrt(cinfo->regbase, CL_CRT1B) & 0xf2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350 /* construct bits 16, 17 and 18 of screen start address */
1351 if (base & 0x10000)
1352 tmp |= 0x01;
1353 if (base & 0x20000)
1354 tmp |= 0x04;
1355 if (base & 0x40000)
1356 tmp |= 0x08;
1357
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001358 vga_wcrt(cinfo->regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001359
1360 /* construct bit 19 of screen start address */
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001361 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19) {
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001362 tmp = vga_rcrt(cinfo->regbase, CL_CRT1D);
1363 if (is_laguna(cinfo))
1364 tmp = (tmp & ~0x18) | ((base >> 16) & 0x18);
1365 else
1366 tmp = (tmp & ~0x80) | ((base >> 12) & 0x80);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001367 vga_wcrt(cinfo->regbase, CL_CRT1D, tmp);
1368 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001369
Krzysztof Helt8503df62007-10-16 01:29:08 -07001370 /* write pixel panning value to AR33; this does not quite work in 8bpp
1371 *
1372 * ### Piccolo..? Will this work?
1373 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001374 if (info->var.bits_per_pixel == 1)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001375 vga_wattr(cinfo->regbase, CL_AR33, xpix);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001376
Krzysztof Helt8503df62007-10-16 01:29:08 -07001377 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001378}
1379
Krzysztof Helt8503df62007-10-16 01:29:08 -07001380static int cirrusfb_blank(int blank_mode, struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001381{
1382 /*
Krzysztof Helt8503df62007-10-16 01:29:08 -07001383 * Blank the screen if blank_mode != 0, else unblank. If blank == NULL
1384 * then the caller blanks by setting the CLUT (Color Look Up Table)
1385 * to all black. Return 0 if blanking succeeded, != 0 if un-/blanking
1386 * failed due to e.g. a video mode which doesn't support it.
1387 * Implements VESA suspend and powerdown modes on hardware that
1388 * supports disabling hsync/vsync:
1389 * blank_mode == 2: suspend vsync
1390 * blank_mode == 3: suspend hsync
1391 * blank_mode == 4: powerdown
Linus Torvalds1da177e2005-04-16 15:20:36 -07001392 */
1393 unsigned char val;
1394 struct cirrusfb_info *cinfo = info->par;
1395 int current_mode = cinfo->blank_mode;
1396
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001397 dev_dbg(info->device, "ENTER, blank mode = %d\n", blank_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398
1399 if (info->state != FBINFO_STATE_RUNNING ||
1400 current_mode == blank_mode) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001401 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001402 return 0;
1403 }
1404
1405 /* Undo current */
1406 if (current_mode == FB_BLANK_NORMAL ||
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001407 current_mode == FB_BLANK_UNBLANK)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001408 /* clear "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001409 val = 0;
1410 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001411 /* set "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001412 val = 0x20;
1413
1414 val |= vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE) & 0xdf;
1415 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001416
1417 switch (blank_mode) {
1418 case FB_BLANK_UNBLANK:
1419 case FB_BLANK_NORMAL:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001420 val = 0x00;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001421 break;
1422 case FB_BLANK_VSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001423 val = 0x04;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001424 break;
1425 case FB_BLANK_HSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001426 val = 0x02;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001427 break;
1428 case FB_BLANK_POWERDOWN:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001429 val = 0x06;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001430 break;
1431 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001432 dev_dbg(info->device, "EXIT, returning 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001433 return 1;
1434 }
1435
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001436 vga_wgfx(cinfo->regbase, CL_GRE, val);
1437
Linus Torvalds1da177e2005-04-16 15:20:36 -07001438 cinfo->blank_mode = blank_mode;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001439 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001440
1441 /* Let fbcon do a soft blank for us */
1442 return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
1443}
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001444
Linus Torvalds1da177e2005-04-16 15:20:36 -07001445/**** END Hardware specific Routines **************************************/
1446/****************************************************************************/
1447/**** BEGIN Internal Routines ***********************************************/
1448
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001449static void init_vgachip(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001450{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001451 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001452 const struct cirrusfb_board_info_rec *bi;
1453
Krzysztof Helt8503df62007-10-16 01:29:08 -07001454 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001455
1456 bi = &cirrusfb_board_info[cinfo->btype];
1457
1458 /* reset board globally */
1459 switch (cinfo->btype) {
1460 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001461 WSFR(cinfo, 0x01);
1462 udelay(500);
1463 WSFR(cinfo, 0x51);
1464 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001465 break;
1466 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001467 WSFR2(cinfo, 0xff);
1468 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001469 break;
1470 case BT_SD64:
1471 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001472 WSFR(cinfo, 0x1f);
1473 udelay(500);
1474 WSFR(cinfo, 0x4f);
1475 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001476 break;
1477 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001478 /* disable flickerfixer */
1479 vga_wcrt(cinfo->regbase, CL_CRT51, 0x00);
1480 mdelay(100);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001481 /* mode */
1482 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Krzysztof Helt7cade312009-03-31 15:25:13 -07001483 case BT_GD5480: /* fall through */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001484 /* from Klaus' NetBSD driver: */
1485 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
Krzysztof Helt7cade312009-03-31 15:25:13 -07001486 case BT_ALPINE: /* fall through */
1487 /* put blitter into 542x compat */
1488 vga_wgfx(cinfo->regbase, CL_GR33, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001489 break;
1490
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001491 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001492 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001493 /* Nothing to do to reset the board. */
1494 break;
1495
1496 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001497 dev_err(info->device, "Warning: Unknown board type\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001498 break;
1499 }
1500
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001501 /* make sure RAM size set by this point */
1502 assert(info->screen_size > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001503
1504 /* the P4 is not fully initialized here; I rely on it having been */
1505 /* inited under AmigaOS already, which seems to work just fine */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001506 /* (Klaus advised to do it this way) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001507
1508 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001509 WGen(cinfo, CL_VSSM, 0x10); /* EGS: 0x16 */
1510 WGen(cinfo, CL_POS102, 0x01);
1511 WGen(cinfo, CL_VSSM, 0x08); /* EGS: 0x0e */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001512
1513 if (cinfo->btype != BT_SD64)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001514 WGen(cinfo, CL_VSSM2, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001515
Krzysztof Helt8503df62007-10-16 01:29:08 -07001516 /* reset sequencer logic */
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001517 vga_wseq(cinfo->regbase, VGA_SEQ_RESET, 0x03);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001518
Krzysztof Helt8503df62007-10-16 01:29:08 -07001519 /* FullBandwidth (video off) and 8/9 dot clock */
1520 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001521
Krzysztof Helt8503df62007-10-16 01:29:08 -07001522 /* "magic cookie" - doesn't make any sense to me.. */
1523/* vga_wgfx(cinfo->regbase, CL_GRA, 0xce); */
1524 /* unlock all extension registers */
1525 vga_wseq(cinfo->regbase, CL_SEQR6, 0x12);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001526
Linus Torvalds1da177e2005-04-16 15:20:36 -07001527 switch (cinfo->btype) {
1528 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001529 vga_wseq(cinfo->regbase, CL_SEQRF, 0x98);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001530 break;
1531 case BT_ALPINE:
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001532 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001533 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001534 break;
1535 case BT_SD64:
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001536#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001537 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb8);
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001538#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001539 break;
1540 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001541 vga_wseq(cinfo->regbase, CL_SEQR16, 0x0f);
1542 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001543 break;
1544 }
1545 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001546 /* plane mask: nothing */
1547 vga_wseq(cinfo->regbase, VGA_SEQ_PLANE_WRITE, 0xff);
1548 /* character map select: doesn't even matter in gx mode */
1549 vga_wseq(cinfo->regbase, VGA_SEQ_CHARACTER_MAP, 0x00);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001550 /* memory mode: chain4, ext. memory */
1551 vga_wseq(cinfo->regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001552
1553 /* controller-internal base address of video memory */
1554 if (bi->init_sr07)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001555 vga_wseq(cinfo->regbase, CL_SEQR7, bi->sr07);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001556
Krzysztof Helt8503df62007-10-16 01:29:08 -07001557 /* vga_wseq(cinfo->regbase, CL_SEQR8, 0x00); */
1558 /* EEPROM control: shouldn't be necessary to write to this at all.. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001559
Krzysztof Helt8503df62007-10-16 01:29:08 -07001560 /* graphics cursor X position (incomplete; position gives rem. 3 bits */
1561 vga_wseq(cinfo->regbase, CL_SEQR10, 0x00);
1562 /* graphics cursor Y position (..."... ) */
1563 vga_wseq(cinfo->regbase, CL_SEQR11, 0x00);
1564 /* graphics cursor attributes */
1565 vga_wseq(cinfo->regbase, CL_SEQR12, 0x00);
1566 /* graphics cursor pattern address */
1567 vga_wseq(cinfo->regbase, CL_SEQR13, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001568
1569 /* writing these on a P4 might give problems.. */
1570 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001571 /* configuration readback and ext. color */
1572 vga_wseq(cinfo->regbase, CL_SEQR17, 0x00);
1573 /* signature generator */
1574 vga_wseq(cinfo->regbase, CL_SEQR18, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001575 }
1576
Krzysztof Helt8503df62007-10-16 01:29:08 -07001577 /* Screen A preset row scan: none */
1578 vga_wcrt(cinfo->regbase, VGA_CRTC_PRESET_ROW, 0x00);
1579 /* Text cursor start: disable text cursor */
1580 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_START, 0x20);
1581 /* Text cursor end: - */
1582 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_END, 0x00);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001583 /* text cursor location high: 0 */
1584 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_HI, 0x00);
1585 /* text cursor location low: 0 */
1586 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_LO, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001587
Krzysztof Helt8503df62007-10-16 01:29:08 -07001588 /* Underline Row scanline: - */
1589 vga_wcrt(cinfo->regbase, VGA_CRTC_UNDERLINE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001590 /* ### add 0x40 for text modes with > 30 MHz pixclock */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001591 /* ext. display controls: ext.adr. wrap */
1592 vga_wcrt(cinfo->regbase, CL_CRT1B, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001593
Krzysztof Helt8503df62007-10-16 01:29:08 -07001594 /* Set/Reset registes: - */
1595 vga_wgfx(cinfo->regbase, VGA_GFX_SR_VALUE, 0x00);
1596 /* Set/Reset enable: - */
1597 vga_wgfx(cinfo->regbase, VGA_GFX_SR_ENABLE, 0x00);
1598 /* Color Compare: - */
1599 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_VALUE, 0x00);
1600 /* Data Rotate: - */
1601 vga_wgfx(cinfo->regbase, VGA_GFX_DATA_ROTATE, 0x00);
1602 /* Read Map Select: - */
1603 vga_wgfx(cinfo->regbase, VGA_GFX_PLANE_READ, 0x00);
1604 /* Mode: conf. for 16/4/2 color mode, no odd/even, read/write mode 0 */
1605 vga_wgfx(cinfo->regbase, VGA_GFX_MODE, 0x00);
1606 /* Miscellaneous: memory map base address, graphics mode */
1607 vga_wgfx(cinfo->regbase, VGA_GFX_MISC, 0x01);
1608 /* Color Don't care: involve all planes */
1609 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_MASK, 0x0f);
1610 /* Bit Mask: no mask at all */
1611 vga_wgfx(cinfo->regbase, VGA_GFX_BIT_MASK, 0xff);
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001612
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001613 if (cinfo->btype == BT_ALPINE || cinfo->btype == BT_SD64 ||
1614 is_laguna(cinfo))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001615 /* (5434 can't have bit 3 set for bitblt) */
1616 vga_wgfx(cinfo->regbase, CL_GRB, 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001617 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001618 /* Graphics controller mode extensions: finer granularity,
1619 * 8byte data latches
1620 */
1621 vga_wgfx(cinfo->regbase, CL_GRB, 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001622
Krzysztof Helt8503df62007-10-16 01:29:08 -07001623 vga_wgfx(cinfo->regbase, CL_GRC, 0xff); /* Color Key compare: - */
1624 vga_wgfx(cinfo->regbase, CL_GRD, 0x00); /* Color Key compare mask: - */
1625 vga_wgfx(cinfo->regbase, CL_GRE, 0x00); /* Miscellaneous control: - */
1626 /* Background color byte 1: - */
1627 /* vga_wgfx (cinfo->regbase, CL_GR10, 0x00); */
1628 /* vga_wgfx (cinfo->regbase, CL_GR11, 0x00); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001629
Krzysztof Helt8503df62007-10-16 01:29:08 -07001630 /* Attribute Controller palette registers: "identity mapping" */
1631 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE0, 0x00);
1632 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE1, 0x01);
1633 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE2, 0x02);
1634 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE3, 0x03);
1635 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE4, 0x04);
1636 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE5, 0x05);
1637 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE6, 0x06);
1638 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE7, 0x07);
1639 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE8, 0x08);
1640 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE9, 0x09);
1641 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEA, 0x0a);
1642 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEB, 0x0b);
1643 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEC, 0x0c);
1644 vga_wattr(cinfo->regbase, VGA_ATC_PALETTED, 0x0d);
1645 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEE, 0x0e);
1646 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEF, 0x0f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001647
Krzysztof Helt8503df62007-10-16 01:29:08 -07001648 /* Attribute Controller mode: graphics mode */
1649 vga_wattr(cinfo->regbase, VGA_ATC_MODE, 0x01);
1650 /* Overscan color reg.: reg. 0 */
1651 vga_wattr(cinfo->regbase, VGA_ATC_OVERSCAN, 0x00);
1652 /* Color Plane enable: Enable all 4 planes */
1653 vga_wattr(cinfo->regbase, VGA_ATC_PLANE_ENABLE, 0x0f);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001654 /* Color Select: - */
1655 vga_wattr(cinfo->regbase, VGA_ATC_COLOR_PAGE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001656
Krzysztof Helt8503df62007-10-16 01:29:08 -07001657 WGen(cinfo, VGA_PEL_MSK, 0xff); /* Pixel mask: no mask */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001658
Krzysztof Helt8503df62007-10-16 01:29:08 -07001659 /* BLT Start/status: Blitter reset */
1660 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
1661 /* - " - : "end-of-reset" */
1662 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001663
1664 /* misc... */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001665 WHDR(cinfo, 0); /* Hidden DAC register: - */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001666 return;
1667}
1668
Krzysztof Helt8503df62007-10-16 01:29:08 -07001669static void switch_monitor(struct cirrusfb_info *cinfo, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001670{
1671#ifdef CONFIG_ZORRO /* only works on Zorro boards */
1672 static int IsOn = 0; /* XXX not ok for multiple boards */
1673
Linus Torvalds1da177e2005-04-16 15:20:36 -07001674 if (cinfo->btype == BT_PICASSO4)
1675 return; /* nothing to switch */
1676 if (cinfo->btype == BT_ALPINE)
1677 return; /* nothing to switch */
1678 if (cinfo->btype == BT_GD5480)
1679 return; /* nothing to switch */
1680 if (cinfo->btype == BT_PICASSO) {
1681 if ((on && !IsOn) || (!on && IsOn))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001682 WSFR(cinfo, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001683 return;
1684 }
1685 if (on) {
1686 switch (cinfo->btype) {
1687 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001688 WSFR(cinfo, cinfo->SFR | 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001689 break;
1690 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001691 WSFR(cinfo, cinfo->SFR | 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001692 break;
1693 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001694 WSFR(cinfo, 0x6f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001695 break;
1696 default: /* do nothing */ break;
1697 }
1698 } else {
1699 switch (cinfo->btype) {
1700 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001701 WSFR(cinfo, cinfo->SFR & 0xde);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001702 break;
1703 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001704 WSFR(cinfo, cinfo->SFR & 0xd7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001705 break;
1706 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001707 WSFR(cinfo, 0x4f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001708 break;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001709 default: /* do nothing */
1710 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001711 }
1712 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001713#endif /* CONFIG_ZORRO */
1714}
1715
Linus Torvalds1da177e2005-04-16 15:20:36 -07001716/******************************************/
1717/* Linux 2.6-style accelerated functions */
1718/******************************************/
1719
Krzysztof Helt8343c892009-03-31 15:25:11 -07001720static int cirrusfb_sync(struct fb_info *info)
1721{
1722 struct cirrusfb_info *cinfo = info->par;
1723
1724 if (!is_laguna(cinfo)) {
1725 while (vga_rgfx(cinfo->regbase, CL_GR31) & 0x03)
1726 cpu_relax();
1727 }
1728 return 0;
1729}
1730
Krzysztof Helt8503df62007-10-16 01:29:08 -07001731static void cirrusfb_fillrect(struct fb_info *info,
1732 const struct fb_fillrect *region)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001733{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001734 struct fb_fillrect modded;
1735 int vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001736 struct cirrusfb_info *cinfo = info->par;
1737 int m = info->var.bits_per_pixel;
1738 u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ?
1739 cinfo->pseudo_palette[region->color] : region->color;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001740
1741 if (info->state != FBINFO_STATE_RUNNING)
1742 return;
1743 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1744 cfb_fillrect(info, region);
1745 return;
1746 }
1747
1748 vxres = info->var.xres_virtual;
1749 vyres = info->var.yres_virtual;
1750
1751 memcpy(&modded, region, sizeof(struct fb_fillrect));
1752
Krzysztof Helt8503df62007-10-16 01:29:08 -07001753 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001754 modded.dx >= vxres || modded.dy >= vyres)
1755 return;
1756
Krzysztof Helt8503df62007-10-16 01:29:08 -07001757 if (modded.dx + modded.width > vxres)
1758 modded.width = vxres - modded.dx;
1759 if (modded.dy + modded.height > vyres)
1760 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001761
Krzysztof Helt060b6002007-10-16 01:29:13 -07001762 cirrusfb_RectFill(cinfo->regbase,
1763 info->var.bits_per_pixel,
1764 (region->dx * m) / 8, region->dy,
1765 (region->width * m) / 8, region->height,
Krzysztof Helt9e848062009-03-31 15:25:11 -07001766 color, color,
1767 info->fix.line_length, 0x40);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001768}
1769
Krzysztof Helt8503df62007-10-16 01:29:08 -07001770static void cirrusfb_copyarea(struct fb_info *info,
1771 const struct fb_copyarea *area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001772{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001773 struct fb_copyarea modded;
1774 u32 vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001775 struct cirrusfb_info *cinfo = info->par;
1776 int m = info->var.bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001777
1778 if (info->state != FBINFO_STATE_RUNNING)
1779 return;
1780 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1781 cfb_copyarea(info, area);
1782 return;
1783 }
1784
1785 vxres = info->var.xres_virtual;
1786 vyres = info->var.yres_virtual;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001787 memcpy(&modded, area, sizeof(struct fb_copyarea));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001788
Krzysztof Helt8503df62007-10-16 01:29:08 -07001789 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001790 modded.sx >= vxres || modded.sy >= vyres ||
1791 modded.dx >= vxres || modded.dy >= vyres)
1792 return;
1793
Krzysztof Helt8503df62007-10-16 01:29:08 -07001794 if (modded.sx + modded.width > vxres)
1795 modded.width = vxres - modded.sx;
1796 if (modded.dx + modded.width > vxres)
1797 modded.width = vxres - modded.dx;
1798 if (modded.sy + modded.height > vyres)
1799 modded.height = vyres - modded.sy;
1800 if (modded.dy + modded.height > vyres)
1801 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001802
Krzysztof Helt060b6002007-10-16 01:29:13 -07001803 cirrusfb_BitBLT(cinfo->regbase, info->var.bits_per_pixel,
1804 (area->sx * m) / 8, area->sy,
1805 (area->dx * m) / 8, area->dy,
1806 (area->width * m) / 8, area->height,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001807 info->fix.line_length);
Krzysztof Helt060b6002007-10-16 01:29:13 -07001808
Linus Torvalds1da177e2005-04-16 15:20:36 -07001809}
1810
Krzysztof Helt8503df62007-10-16 01:29:08 -07001811static void cirrusfb_imageblit(struct fb_info *info,
1812 const struct fb_image *image)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001813{
1814 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt7cade312009-03-31 15:25:13 -07001815 unsigned char op = (info->var.bits_per_pixel == 24) ? 0xc : 0x4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001816
Krzysztof Helt9e848062009-03-31 15:25:11 -07001817 if (info->state != FBINFO_STATE_RUNNING)
1818 return;
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001819 /* Alpine/SD64 does not work at 24bpp ??? */
1820 if (info->flags & FBINFO_HWACCEL_DISABLED || image->depth != 1)
1821 cfb_imageblit(info, image);
1822 else if ((cinfo->btype == BT_ALPINE || cinfo->btype == BT_SD64) &&
1823 op == 0xc)
Krzysztof Helt9e848062009-03-31 15:25:11 -07001824 cfb_imageblit(info, image);
1825 else {
1826 unsigned size = ((image->width + 7) >> 3) * image->height;
1827 int m = info->var.bits_per_pixel;
1828 u32 fg, bg;
1829
1830 if (info->var.bits_per_pixel == 8) {
1831 fg = image->fg_color;
1832 bg = image->bg_color;
1833 } else {
1834 fg = ((u32 *)(info->pseudo_palette))[image->fg_color];
1835 bg = ((u32 *)(info->pseudo_palette))[image->bg_color];
1836 }
Krzysztof Helt7cade312009-03-31 15:25:13 -07001837 if (info->var.bits_per_pixel == 24) {
1838 /* clear background first */
1839 cirrusfb_RectFill(cinfo->regbase,
1840 info->var.bits_per_pixel,
1841 (image->dx * m) / 8, image->dy,
1842 (image->width * m) / 8,
1843 image->height,
1844 bg, bg,
1845 info->fix.line_length, 0x40);
1846 }
Krzysztof Helt9e848062009-03-31 15:25:11 -07001847 cirrusfb_RectFill(cinfo->regbase,
1848 info->var.bits_per_pixel,
1849 (image->dx * m) / 8, image->dy,
1850 (image->width * m) / 8, image->height,
1851 fg, bg,
Krzysztof Helt7cade312009-03-31 15:25:13 -07001852 info->fix.line_length, op);
Krzysztof Helt9e848062009-03-31 15:25:11 -07001853 memcpy(info->screen_base, image->data, size);
1854 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001855}
1856
Linus Torvalds1da177e2005-04-16 15:20:36 -07001857#ifdef CONFIG_PPC_PREP
1858#define PREP_VIDEO_BASE ((volatile unsigned long) 0xC0000000)
1859#define PREP_IO_BASE ((volatile unsigned char *) 0x80000000)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001860static void get_prep_addrs(unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001861{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001862 *display = PREP_VIDEO_BASE;
1863 *registers = (unsigned long) PREP_IO_BASE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001864}
1865
1866#endif /* CONFIG_PPC_PREP */
1867
Linus Torvalds1da177e2005-04-16 15:20:36 -07001868#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001869static int release_io_ports;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001870
1871/* Pulled the logic from XFree86 Cirrus driver to get the memory size,
1872 * based on the DRAM bandwidth bit and DRAM bank switching bit. This
1873 * works with 1MB, 2MB and 4MB configurations (which the Motorola boards
1874 * seem to have. */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001875static unsigned int __devinit cirrusfb_get_memsize(struct fb_info *info,
1876 u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001877{
1878 unsigned long mem;
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001879 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001880
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001881 if (is_laguna(cinfo)) {
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001882 unsigned char SR14 = vga_rseq(regbase, CL_SEQR14);
1883
1884 mem = ((SR14 & 7) + 1) << 20;
1885 } else {
1886 unsigned char SRF = vga_rseq(regbase, CL_SEQRF);
1887 switch ((SRF & 0x18)) {
1888 case 0x08:
1889 mem = 512 * 1024;
1890 break;
1891 case 0x10:
1892 mem = 1024 * 1024;
1893 break;
1894 /* 64-bit DRAM data bus width; assume 2MB.
1895 * Also indicates 2MB memory on the 5430.
1896 */
1897 case 0x18:
1898 mem = 2048 * 1024;
1899 break;
1900 default:
1901 dev_warn(info->device, "Unknown memory size!\n");
1902 mem = 1024 * 1024;
1903 }
1904 /* If DRAM bank switching is enabled, there must be
1905 * twice as much memory installed. (4MB on the 5434)
1906 */
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07001907 if (cinfo->btype != BT_ALPINE && (SRF & 0x80) != 0)
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001908 mem *= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001909 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001910
Linus Torvalds1da177e2005-04-16 15:20:36 -07001911 /* TODO: Handling of GD5446/5480 (see XF86 sources ...) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001912 return mem;
1913}
1914
Krzysztof Helt8503df62007-10-16 01:29:08 -07001915static void get_pci_addrs(const struct pci_dev *pdev,
1916 unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001917{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001918 assert(pdev != NULL);
1919 assert(display != NULL);
1920 assert(registers != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001921
Linus Torvalds1da177e2005-04-16 15:20:36 -07001922 *display = 0;
1923 *registers = 0;
1924
1925 /* This is a best-guess for now */
1926
1927 if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
1928 *display = pci_resource_start(pdev, 1);
1929 *registers = pci_resource_start(pdev, 0);
1930 } else {
1931 *display = pci_resource_start(pdev, 0);
1932 *registers = pci_resource_start(pdev, 1);
1933 }
1934
Krzysztof Helt8503df62007-10-16 01:29:08 -07001935 assert(*display != 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001936}
1937
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001938static void cirrusfb_pci_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001939{
Krzysztof Helt64beab12008-10-15 22:03:38 -07001940 struct pci_dev *pdev = to_pci_dev(info->device);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001941 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001942
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001943 if (cinfo->laguna_mmio == NULL)
1944 iounmap(cinfo->laguna_mmio);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001945 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001946#if 0 /* if system didn't claim this region, we would... */
1947 release_mem_region(0xA0000, 65535);
1948#endif
1949 if (release_io_ports)
1950 release_region(0x3C0, 32);
1951 pci_release_regions(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001952}
1953#endif /* CONFIG_PCI */
1954
Linus Torvalds1da177e2005-04-16 15:20:36 -07001955#ifdef CONFIG_ZORRO
Al Virof5ee0512008-11-01 18:20:39 +00001956static void cirrusfb_zorro_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001957{
Al Virod91f5bb2007-10-17 00:27:18 +01001958 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt64beab12008-10-15 22:03:38 -07001959 struct zorro_dev *zdev = to_zorro_dev(info->device);
1960
1961 zorro_release_device(zdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001962
1963 if (cinfo->btype == BT_PICASSO4) {
1964 cinfo->regbase -= 0x600000;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001965 iounmap((void *)cinfo->regbase);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001966 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001967 } else {
Krzysztof Helt64beab12008-10-15 22:03:38 -07001968 if (zorro_resource_start(zdev) > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001969 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001970 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001971}
1972#endif /* CONFIG_ZORRO */
1973
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001974/* function table of the above functions */
1975static struct fb_ops cirrusfb_ops = {
1976 .owner = THIS_MODULE,
1977 .fb_open = cirrusfb_open,
1978 .fb_release = cirrusfb_release,
1979 .fb_setcolreg = cirrusfb_setcolreg,
1980 .fb_check_var = cirrusfb_check_var,
1981 .fb_set_par = cirrusfb_set_par,
1982 .fb_pan_display = cirrusfb_pan_display,
1983 .fb_blank = cirrusfb_blank,
1984 .fb_fillrect = cirrusfb_fillrect,
1985 .fb_copyarea = cirrusfb_copyarea,
Krzysztof Helt8343c892009-03-31 15:25:11 -07001986 .fb_sync = cirrusfb_sync,
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001987 .fb_imageblit = cirrusfb_imageblit,
1988};
1989
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07001990static int __devinit cirrusfb_set_fbinfo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001991{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001992 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001993 struct fb_var_screeninfo *var = &info->var;
1994
Linus Torvalds1da177e2005-04-16 15:20:36 -07001995 info->pseudo_palette = cinfo->pseudo_palette;
1996 info->flags = FBINFO_DEFAULT
1997 | FBINFO_HWACCEL_XPAN
1998 | FBINFO_HWACCEL_YPAN
1999 | FBINFO_HWACCEL_FILLRECT
Krzysztof Helt9e848062009-03-31 15:25:11 -07002000 | FBINFO_HWACCEL_IMAGEBLIT
Linus Torvalds1da177e2005-04-16 15:20:36 -07002001 | FBINFO_HWACCEL_COPYAREA;
Krzysztof Helt614c0dc2009-03-31 15:25:15 -07002002 if (noaccel || is_laguna(cinfo)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002003 info->flags |= FBINFO_HWACCEL_DISABLED;
Krzysztof Helt614c0dc2009-03-31 15:25:15 -07002004 info->fix.accel = FB_ACCEL_NONE;
2005 } else
2006 info->fix.accel = FB_ACCEL_CIRRUS_ALPINE;
2007
Linus Torvalds1da177e2005-04-16 15:20:36 -07002008 info->fbops = &cirrusfb_ops;
Krzysztof Helt9e848062009-03-31 15:25:11 -07002009
Linus Torvalds1da177e2005-04-16 15:20:36 -07002010 if (cinfo->btype == BT_GD5480) {
2011 if (var->bits_per_pixel == 16)
2012 info->screen_base += 1 * MB_;
Krzysztof Helt1cea9a92008-10-15 22:03:37 -07002013 if (var->bits_per_pixel == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002014 info->screen_base += 2 * MB_;
2015 }
2016
2017 /* Fill fix common fields */
2018 strlcpy(info->fix.id, cirrusfb_board_info[cinfo->btype].name,
2019 sizeof(info->fix.id));
2020
2021 /* monochrome: only 1 memory plane */
2022 /* 8 bit and above: Use whole memory area */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002023 info->fix.smem_len = info->screen_size;
2024 if (var->bits_per_pixel == 1)
2025 info->fix.smem_len /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002026 info->fix.type_aux = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002027 info->fix.xpanstep = 1;
2028 info->fix.ypanstep = 1;
2029 info->fix.ywrapstep = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002030
2031 /* FIXME: map region at 0xB8000 if available, fill in here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002032 info->fix.mmio_len = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002033
2034 fb_alloc_cmap(&info->cmap, 256, 0);
2035
2036 return 0;
2037}
2038
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002039static int __devinit cirrusfb_register(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002040{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002041 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002042 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002043
2044 /* sanity checks */
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002045 assert(cinfo->btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002046
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002047 /* set all the vital stuff */
2048 cirrusfb_set_fbinfo(info);
2049
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002050 dev_dbg(info->device, "(RAM start set to: 0x%p)\n", info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002051
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002052 err = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8);
2053 if (!err) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002054 dev_dbg(info->device, "wrong initial video mode\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002055 err = -EINVAL;
2056 goto err_dealloc_cmap;
2057 }
2058
Linus Torvalds1da177e2005-04-16 15:20:36 -07002059 info->var.activate = FB_ACTIVATE_NOW;
2060
Krzysztof Helt99a45842009-03-31 15:25:09 -07002061 err = cirrusfb_check_var(&info->var, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002062 if (err < 0) {
2063 /* should never happen */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002064 dev_dbg(info->device,
2065 "choking on default var... umm, no good.\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002066 goto err_dealloc_cmap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002067 }
2068
Linus Torvalds1da177e2005-04-16 15:20:36 -07002069 err = register_framebuffer(info);
2070 if (err < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002071 dev_err(info->device,
2072 "could not register fb device; err = %d!\n", err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002073 goto err_dealloc_cmap;
2074 }
2075
Linus Torvalds1da177e2005-04-16 15:20:36 -07002076 return 0;
2077
2078err_dealloc_cmap:
2079 fb_dealloc_cmap(&info->cmap);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002080 return err;
2081}
2082
Krzysztof Helt8503df62007-10-16 01:29:08 -07002083static void __devexit cirrusfb_cleanup(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002084{
2085 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002086
Krzysztof Helt8503df62007-10-16 01:29:08 -07002087 switch_monitor(cinfo, 0);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002088 unregister_framebuffer(info);
2089 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002090 dev_dbg(info->device, "Framebuffer unregistered\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002091 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002092 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002093}
2094
Linus Torvalds1da177e2005-04-16 15:20:36 -07002095#ifdef CONFIG_PCI
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002096static int __devinit cirrusfb_pci_register(struct pci_dev *pdev,
2097 const struct pci_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002098{
2099 struct cirrusfb_info *cinfo;
2100 struct fb_info *info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002101 unsigned long board_addr, board_size;
2102 int ret;
2103
2104 ret = pci_enable_device(pdev);
2105 if (ret < 0) {
2106 printk(KERN_ERR "cirrusfb: Cannot enable PCI device\n");
2107 goto err_out;
2108 }
2109
2110 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &pdev->dev);
2111 if (!info) {
2112 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
2113 ret = -ENOMEM;
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002114 goto err_out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002115 }
2116
2117 cinfo = info->par;
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002118 cinfo->btype = (enum cirrus_board) ent->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002119
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002120 dev_dbg(info->device,
2121 " Found PCI device, base address 0 is 0x%Lx, btype set to %d\n",
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002122 (unsigned long long)pdev->resource[0].start, cinfo->btype);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002123 dev_dbg(info->device, " base address 1 is 0x%Lx\n",
2124 (unsigned long long)pdev->resource[1].start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002125
Krzysztof Helt8503df62007-10-16 01:29:08 -07002126 if (isPReP) {
2127 pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, 0x00000000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002128#ifdef CONFIG_PPC_PREP
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002129 get_prep_addrs(&board_addr, &info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002130#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07002131 /* PReP dies if we ioremap the IO registers, but it works w/out... */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002132 cinfo->regbase = (char __iomem *) info->fix.mmio_start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002133 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002134 dev_dbg(info->device,
2135 "Attempt to get PCI info for Cirrus Graphics Card\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002136 get_pci_addrs(pdev, &board_addr, &info->fix.mmio_start);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002137 /* FIXME: this forces VGA. alternatives? */
2138 cinfo->regbase = NULL;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002139 cinfo->laguna_mmio = ioremap(info->fix.mmio_start, 0x1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002140 }
2141
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002142 dev_dbg(info->device, "Board address: 0x%lx, register address: 0x%lx\n",
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002143 board_addr, info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002144
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002145 board_size = (cinfo->btype == BT_GD5480) ?
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002146 32 * MB_ : cirrusfb_get_memsize(info, cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002147
2148 ret = pci_request_regions(pdev, "cirrusfb");
Krzysztof Helt8503df62007-10-16 01:29:08 -07002149 if (ret < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002150 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2151 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002152 goto err_release_fb;
2153 }
2154#if 0 /* if the system didn't claim this region, we would... */
2155 if (!request_mem_region(0xA0000, 65535, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002156 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2157 0xA0000L);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002158 ret = -EBUSY;
2159 goto err_release_regions;
2160 }
2161#endif
2162 if (request_region(0x3C0, 32, "cirrusfb"))
2163 release_io_ports = 1;
2164
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002165 info->screen_base = ioremap(board_addr, board_size);
2166 if (!info->screen_base) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002167 ret = -EIO;
2168 goto err_release_legacy;
2169 }
2170
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002171 info->fix.smem_start = board_addr;
2172 info->screen_size = board_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002173 cinfo->unmap = cirrusfb_pci_unmap;
2174
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002175 dev_info(info->device,
2176 "Cirrus Logic chipset on PCI bus, RAM (%lu kB) at 0x%lx\n",
2177 info->screen_size >> 10, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002178 pci_set_drvdata(pdev, info);
2179
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002180 ret = cirrusfb_register(info);
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002181 if (!ret)
2182 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002183
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002184 pci_set_drvdata(pdev, NULL);
2185 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002186err_release_legacy:
2187 if (release_io_ports)
2188 release_region(0x3C0, 32);
2189#if 0
2190 release_mem_region(0xA0000, 65535);
2191err_release_regions:
2192#endif
2193 pci_release_regions(pdev);
2194err_release_fb:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002195 if (cinfo->laguna_mmio != NULL)
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002196 iounmap(cinfo->laguna_mmio);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002197 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002198err_out:
2199 return ret;
2200}
2201
Krzysztof Helt8503df62007-10-16 01:29:08 -07002202static void __devexit cirrusfb_pci_unregister(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002203{
2204 struct fb_info *info = pci_get_drvdata(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002205
Krzysztof Helt8503df62007-10-16 01:29:08 -07002206 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002207}
2208
2209static struct pci_driver cirrusfb_pci_driver = {
2210 .name = "cirrusfb",
2211 .id_table = cirrusfb_pci_table,
2212 .probe = cirrusfb_pci_register,
2213 .remove = __devexit_p(cirrusfb_pci_unregister),
2214#ifdef CONFIG_PM
2215#if 0
2216 .suspend = cirrusfb_pci_suspend,
2217 .resume = cirrusfb_pci_resume,
2218#endif
2219#endif
2220};
2221#endif /* CONFIG_PCI */
2222
Linus Torvalds1da177e2005-04-16 15:20:36 -07002223#ifdef CONFIG_ZORRO
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002224static int __devinit cirrusfb_zorro_register(struct zorro_dev *z,
2225 const struct zorro_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002226{
2227 struct cirrusfb_info *cinfo;
2228 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002229 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002230 struct zorro_dev *z2 = NULL;
2231 unsigned long board_addr, board_size, size;
2232 int ret;
2233
2234 btype = ent->driver_data;
2235 if (cirrusfb_zorro_table2[btype].id2)
2236 z2 = zorro_find_device(cirrusfb_zorro_table2[btype].id2, NULL);
2237 size = cirrusfb_zorro_table2[btype].size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002238
2239 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &z->dev);
2240 if (!info) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002241 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002242 ret = -ENOMEM;
2243 goto err_out;
2244 }
2245
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002246 dev_info(info->device, "%s board detected\n",
2247 cirrusfb_board_info[btype].name);
2248
Linus Torvalds1da177e2005-04-16 15:20:36 -07002249 cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002250 cinfo->btype = btype;
2251
Al Viro36ea96a2007-10-27 19:46:58 +01002252 assert(z);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002253 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002254
Linus Torvalds1da177e2005-04-16 15:20:36 -07002255 board_addr = zorro_resource_start(z);
2256 board_size = zorro_resource_len(z);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002257 info->screen_size = size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002258
2259 if (!zorro_request_device(z, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002260 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2261 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002262 ret = -EBUSY;
2263 goto err_release_fb;
2264 }
2265
Linus Torvalds1da177e2005-04-16 15:20:36 -07002266 ret = -EIO;
2267
2268 if (btype == BT_PICASSO4) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002269 dev_info(info->device, " REG at $%lx\n", board_addr + 0x600000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002270
2271 /* To be precise, for the P4 this is not the */
2272 /* begin of the board, but the begin of RAM. */
2273 /* for P4, map in its address space in 2 chunks (### TEST! ) */
2274 /* (note the ugly hardcoded 16M number) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002275 cinfo->regbase = ioremap(board_addr, 16777216);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002276 if (!cinfo->regbase)
2277 goto err_release_region;
2278
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002279 dev_dbg(info->device, "Virtual address for board set to: $%p\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002280 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002281 cinfo->regbase += 0x600000;
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002282 info->fix.mmio_start = board_addr + 0x600000;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002283
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002284 info->fix.smem_start = board_addr + 16777216;
2285 info->screen_base = ioremap(info->fix.smem_start, 16777216);
2286 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002287 goto err_unmap_regbase;
2288 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002289 dev_info(info->device, " REG at $%lx\n",
2290 (unsigned long) z2->resource.start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002291
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002292 info->fix.smem_start = board_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002293 if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002294 info->screen_base = ioremap(board_addr, board_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002295 else
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002296 info->screen_base = (caddr_t) ZTWO_VADDR(board_addr);
2297 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002298 goto err_release_region;
2299
2300 /* set address for REG area of board */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002301 cinfo->regbase = (caddr_t) ZTWO_VADDR(z2->resource.start);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002302 info->fix.mmio_start = z2->resource.start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002303
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002304 dev_dbg(info->device, "Virtual address for board set to: $%p\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002305 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002306 }
2307 cinfo->unmap = cirrusfb_zorro_unmap;
2308
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002309 dev_info(info->device,
2310 "Cirrus Logic chipset on Zorro bus, RAM (%lu MB) at $%lx\n",
2311 board_size / MB_, board_addr);
2312
Linus Torvalds1da177e2005-04-16 15:20:36 -07002313 zorro_set_drvdata(z, info);
2314
Krzysztof Helt8f19e152009-03-31 15:25:15 -07002315 /* MCLK select etc. */
2316 if (cirrusfb_board_info[btype].init_sr1f)
2317 vga_wseq(cinfo->regbase, CL_SEQR1F,
2318 cirrusfb_board_info[btype].sr1f);
2319
Al Virod91f5bb2007-10-17 00:27:18 +01002320 ret = cirrusfb_register(info);
Krzysztof Heltbc5d8ac2009-03-31 15:25:12 -07002321 if (!ret)
2322 return 0;
2323
2324 if (btype == BT_PICASSO4 || board_addr > 0x01000000)
2325 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002326
2327err_unmap_regbase:
Krzysztof Heltbc5d8ac2009-03-31 15:25:12 -07002328 if (btype == BT_PICASSO4)
2329 iounmap(cinfo->regbase - 0x600000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002330err_release_region:
2331 release_region(board_addr, board_size);
2332err_release_fb:
2333 framebuffer_release(info);
2334err_out:
2335 return ret;
2336}
2337
2338void __devexit cirrusfb_zorro_unregister(struct zorro_dev *z)
2339{
2340 struct fb_info *info = zorro_get_drvdata(z);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002341
Krzysztof Helt8503df62007-10-16 01:29:08 -07002342 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002343}
2344
2345static struct zorro_driver cirrusfb_zorro_driver = {
2346 .name = "cirrusfb",
2347 .id_table = cirrusfb_zorro_table,
2348 .probe = cirrusfb_zorro_register,
2349 .remove = __devexit_p(cirrusfb_zorro_unregister),
2350};
2351#endif /* CONFIG_ZORRO */
2352
Linus Torvalds1da177e2005-04-16 15:20:36 -07002353#ifndef MODULE
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002354static int __init cirrusfb_setup(char *options)
2355{
Vlada Pericee119402008-11-19 15:36:45 -08002356 char *this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002357
Linus Torvalds1da177e2005-04-16 15:20:36 -07002358 if (!options || !*options)
2359 return 0;
2360
Krzysztof Helt8503df62007-10-16 01:29:08 -07002361 while ((this_opt = strsep(&options, ",")) != NULL) {
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002362 if (!*this_opt)
2363 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002364
Linus Torvalds1da177e2005-04-16 15:20:36 -07002365 if (!strcmp(this_opt, "noaccel"))
2366 noaccel = 1;
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002367 else if (!strncmp(this_opt, "mode:", 5))
2368 mode_option = this_opt + 5;
2369 else
2370 mode_option = this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002371 }
2372 return 0;
2373}
2374#endif
2375
Linus Torvalds1da177e2005-04-16 15:20:36 -07002376 /*
2377 * Modularization
2378 */
2379
2380MODULE_AUTHOR("Copyright 1999,2000 Jeff Garzik <jgarzik@pobox.com>");
2381MODULE_DESCRIPTION("Accelerated FBDev driver for Cirrus Logic chips");
2382MODULE_LICENSE("GPL");
2383
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002384static int __init cirrusfb_init(void)
2385{
2386 int error = 0;
2387
2388#ifndef MODULE
2389 char *option = NULL;
2390
2391 if (fb_get_options("cirrusfb", &option))
2392 return -ENODEV;
2393 cirrusfb_setup(option);
2394#endif
2395
2396#ifdef CONFIG_ZORRO
2397 error |= zorro_register_driver(&cirrusfb_zorro_driver);
2398#endif
2399#ifdef CONFIG_PCI
2400 error |= pci_register_driver(&cirrusfb_pci_driver);
2401#endif
2402 return error;
2403}
2404
Krzysztof Helt8503df62007-10-16 01:29:08 -07002405static void __exit cirrusfb_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002406{
2407#ifdef CONFIG_PCI
2408 pci_unregister_driver(&cirrusfb_pci_driver);
2409#endif
2410#ifdef CONFIG_ZORRO
2411 zorro_unregister_driver(&cirrusfb_zorro_driver);
2412#endif
2413}
2414
2415module_init(cirrusfb_init);
2416
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002417module_param(mode_option, charp, 0);
2418MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");
Krzysztof Helt55a0dd82008-10-15 22:03:41 -07002419module_param(noaccel, bool, 0);
2420MODULE_PARM_DESC(noaccel, "Disable acceleration");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002421
Linus Torvalds1da177e2005-04-16 15:20:36 -07002422#ifdef MODULE
2423module_exit(cirrusfb_exit);
2424#endif
2425
Linus Torvalds1da177e2005-04-16 15:20:36 -07002426/**********************************************************************/
2427/* about the following functions - I have used the same names for the */
2428/* functions as Markus Wild did in his Retina driver for NetBSD as */
2429/* they just made sense for this purpose. Apart from that, I wrote */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002430/* these functions myself. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002431/**********************************************************************/
2432
2433/*** WGen() - write into one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002434static void WGen(const struct cirrusfb_info *cinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002435 int regnum, unsigned char val)
2436{
2437 unsigned long regofs = 0;
2438
2439 if (cinfo->btype == BT_PICASSO) {
2440 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002441/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2442 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002443 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2444 regofs = 0xfff;
2445 }
2446
Krzysztof Helt8503df62007-10-16 01:29:08 -07002447 vga_w(cinfo->regbase, regofs + regnum, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002448}
2449
2450/*** RGen() - read out one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002451static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002452{
2453 unsigned long regofs = 0;
2454
2455 if (cinfo->btype == BT_PICASSO) {
2456 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002457/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2458 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002459 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2460 regofs = 0xfff;
2461 }
2462
Krzysztof Helt8503df62007-10-16 01:29:08 -07002463 return vga_r(cinfo->regbase, regofs + regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002464}
2465
2466/*** AttrOn() - turn on VideoEnable for Attribute controller ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002467static void AttrOn(const struct cirrusfb_info *cinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002468{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002469 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002470
Krzysztof Helt8503df62007-10-16 01:29:08 -07002471 if (vga_rcrt(cinfo->regbase, CL_CRT24) & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002472 /* if we're just in "write value" mode, write back the */
2473 /* same value as before to not modify anything */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002474 vga_w(cinfo->regbase, VGA_ATT_IW,
2475 vga_r(cinfo->regbase, VGA_ATT_R));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002476 }
2477 /* turn on video bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002478/* vga_w(cinfo->regbase, VGA_ATT_IW, 0x20); */
2479 vga_w(cinfo->regbase, VGA_ATT_IW, 0x33);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002480
2481 /* dummy write on Reg0 to be on "write index" mode next time */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002482 vga_w(cinfo->regbase, VGA_ATT_IW, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002483}
2484
2485/*** WHDR() - write into the Hidden DAC register ***/
2486/* as the HDR is the only extension register that requires special treatment
2487 * (the other extension registers are accessible just like the "ordinary"
2488 * registers of their functional group) here is a specialized routine for
2489 * accessing the HDR
2490 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002491static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002492{
2493 unsigned char dummy;
2494
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002495 if (is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07002496 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002497 if (cinfo->btype == BT_PICASSO) {
2498 /* Klaus' hint for correct access to HDR on some boards */
2499 /* first write 0 to pixel mask (3c6) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002500 WGen(cinfo, VGA_PEL_MSK, 0x00);
2501 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002502 /* next read dummy from pixel address (3c8) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002503 dummy = RGen(cinfo, VGA_PEL_IW);
2504 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002505 }
2506 /* now do the usual stuff to access the HDR */
2507
Krzysztof Helt8503df62007-10-16 01:29:08 -07002508 dummy = RGen(cinfo, VGA_PEL_MSK);
2509 udelay(200);
2510 dummy = RGen(cinfo, VGA_PEL_MSK);
2511 udelay(200);
2512 dummy = RGen(cinfo, VGA_PEL_MSK);
2513 udelay(200);
2514 dummy = RGen(cinfo, VGA_PEL_MSK);
2515 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002516
Krzysztof Helt8503df62007-10-16 01:29:08 -07002517 WGen(cinfo, VGA_PEL_MSK, val);
2518 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002519
2520 if (cinfo->btype == BT_PICASSO) {
2521 /* now first reset HDR access counter */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002522 dummy = RGen(cinfo, VGA_PEL_IW);
2523 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002524
2525 /* and at the end, restore the mask value */
2526 /* ## is this mask always 0xff? */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002527 WGen(cinfo, VGA_PEL_MSK, 0xff);
2528 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002529 }
2530}
2531
Linus Torvalds1da177e2005-04-16 15:20:36 -07002532/*** WSFR() - write to the "special function register" (SFR) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002533static void WSFR(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002534{
2535#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07002536 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002537 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002538 z_writeb(val, cinfo->regbase + 0x8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002539#endif
2540}
2541
2542/* The Picasso has a second register for switching the monitor bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002543static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002544{
2545#ifdef CONFIG_ZORRO
2546 /* writing an arbitrary value to this one causes the monitor switcher */
2547 /* to flip to Amiga display */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002548 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002549 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002550 z_writeb(val, cinfo->regbase + 0x9000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002551#endif
2552}
2553
Linus Torvalds1da177e2005-04-16 15:20:36 -07002554/*** WClut - set CLUT entry (range: 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002555static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002556 unsigned char green, unsigned char blue)
2557{
2558 unsigned int data = VGA_PEL_D;
2559
2560 /* address write mode register is not translated.. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002561 vga_w(cinfo->regbase, VGA_PEL_IW, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002562
2563 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07002564 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480 ||
Krzysztof Heltdf3aafd2009-03-31 15:25:16 -07002565 cinfo->btype == BT_SD64 || is_laguna(cinfo)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002566 /* but DAC data register IS, at least for Picasso II */
2567 if (cinfo->btype == BT_PICASSO)
2568 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002569 vga_w(cinfo->regbase, data, red);
2570 vga_w(cinfo->regbase, data, green);
2571 vga_w(cinfo->regbase, data, blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002572 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002573 vga_w(cinfo->regbase, data, blue);
2574 vga_w(cinfo->regbase, data, green);
2575 vga_w(cinfo->regbase, data, red);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002576 }
2577}
2578
Linus Torvalds1da177e2005-04-16 15:20:36 -07002579#if 0
2580/*** RClut - read CLUT entry (range 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002581static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char *red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002582 unsigned char *green, unsigned char *blue)
2583{
2584 unsigned int data = VGA_PEL_D;
2585
Krzysztof Helt8503df62007-10-16 01:29:08 -07002586 vga_w(cinfo->regbase, VGA_PEL_IR, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002587
2588 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2589 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2590 if (cinfo->btype == BT_PICASSO)
2591 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002592 *red = vga_r(cinfo->regbase, data);
2593 *green = vga_r(cinfo->regbase, data);
2594 *blue = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002595 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002596 *blue = vga_r(cinfo->regbase, data);
2597 *green = vga_r(cinfo->regbase, data);
2598 *red = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002599 }
2600}
2601#endif
2602
Linus Torvalds1da177e2005-04-16 15:20:36 -07002603/*******************************************************************
2604 cirrusfb_WaitBLT()
2605
2606 Wait for the BitBLT engine to complete a possible earlier job
2607*********************************************************************/
2608
2609/* FIXME: use interrupts instead */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002610static void cirrusfb_WaitBLT(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002611{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002612 while (vga_rgfx(regbase, CL_GR31) & 0x08)
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002613 cpu_relax();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002614}
2615
2616/*******************************************************************
2617 cirrusfb_BitBLT()
2618
2619 perform accelerated "scrolling"
2620********************************************************************/
2621
Krzysztof Helt8343c892009-03-31 15:25:11 -07002622static void cirrusfb_set_blitter(u8 __iomem *regbase,
2623 u_short nwidth, u_short nheight,
2624 u_long nsrc, u_long ndest,
2625 u_short bltmode, u_short line_length)
2626
Linus Torvalds1da177e2005-04-16 15:20:36 -07002627{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002628 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002629 /* dest pitch low */
2630 vga_wgfx(regbase, CL_GR24, line_length & 0xff);
2631 /* dest pitch hi */
2632 vga_wgfx(regbase, CL_GR25, line_length >> 8);
2633 /* source pitch low */
2634 vga_wgfx(regbase, CL_GR26, line_length & 0xff);
2635 /* source pitch hi */
2636 vga_wgfx(regbase, CL_GR27, line_length >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002637
2638 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002639 /* BLT width low */
2640 vga_wgfx(regbase, CL_GR20, nwidth & 0xff);
2641 /* BLT width hi */
2642 vga_wgfx(regbase, CL_GR21, nwidth >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002643
2644 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002645 /* BLT height low */
2646 vga_wgfx(regbase, CL_GR22, nheight & 0xff);
2647 /* BLT width hi */
2648 vga_wgfx(regbase, CL_GR23, nheight >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002649
2650 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002651 /* BLT dest low */
2652 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2653 /* BLT dest mid */
2654 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2655 /* BLT dest hi */
2656 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002657
2658 /* BLT source */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002659 /* BLT src low */
2660 vga_wgfx(regbase, CL_GR2C, (u_char) (nsrc & 0xff));
2661 /* BLT src mid */
2662 vga_wgfx(regbase, CL_GR2D, (u_char) (nsrc >> 8));
2663 /* BLT src hi */
2664 vga_wgfx(regbase, CL_GR2E, (u_char) (nsrc >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002665
2666 /* BLT mode */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002667 vga_wgfx(regbase, CL_GR30, bltmode); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002668
2669 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002670 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002671
2672 /* and finally: GO! */
Krzysztof Helt527410f2009-03-31 15:25:13 -07002673 vga_wgfx(regbase, CL_GR31, 0x02); /* BLT Start/status */
Krzysztof Helt8343c892009-03-31 15:25:11 -07002674}
2675
2676/*******************************************************************
2677 cirrusfb_BitBLT()
2678
2679 perform accelerated "scrolling"
2680********************************************************************/
2681
2682static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
2683 u_short curx, u_short cury,
2684 u_short destx, u_short desty,
2685 u_short width, u_short height,
2686 u_short line_length)
2687{
2688 u_short nwidth = width - 1;
2689 u_short nheight = height - 1;
2690 u_long nsrc, ndest;
2691 u_char bltmode;
2692
2693 bltmode = 0x00;
2694 /* if source adr < dest addr, do the Blt backwards */
2695 if (cury <= desty) {
2696 if (cury == desty) {
2697 /* if src and dest are on the same line, check x */
2698 if (curx < destx)
2699 bltmode |= 0x01;
2700 } else
2701 bltmode |= 0x01;
2702 }
2703 /* standard case: forward blitting */
2704 nsrc = (cury * line_length) + curx;
2705 ndest = (desty * line_length) + destx;
2706 if (bltmode) {
2707 /* this means start addresses are at the end,
2708 * counting backwards
2709 */
2710 nsrc += nheight * line_length + nwidth;
2711 ndest += nheight * line_length + nwidth;
2712 }
2713
2714 cirrusfb_WaitBLT(regbase);
2715
2716 cirrusfb_set_blitter(regbase, nwidth, nheight,
2717 nsrc, ndest, bltmode, line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002718}
2719
Linus Torvalds1da177e2005-04-16 15:20:36 -07002720/*******************************************************************
2721 cirrusfb_RectFill()
2722
2723 perform accelerated rectangle fill
2724********************************************************************/
2725
Krzysztof Helt8503df62007-10-16 01:29:08 -07002726static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002727 u_short x, u_short y, u_short width, u_short height,
Krzysztof Helt9e848062009-03-31 15:25:11 -07002728 u32 fg_color, u32 bg_color, u_short line_length,
2729 u_char blitmode)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002730{
Krzysztof Helt8343c892009-03-31 15:25:11 -07002731 u_long ndest = (y * line_length) + x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002732 u_char op;
2733
Krzysztof Helt8503df62007-10-16 01:29:08 -07002734 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002735
Linus Torvalds1da177e2005-04-16 15:20:36 -07002736 /* This is a ColorExpand Blt, using the */
2737 /* same color for foreground and background */
Krzysztof Helt9e848062009-03-31 15:25:11 -07002738 vga_wgfx(regbase, VGA_GFX_SR_VALUE, bg_color);
2739 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, fg_color);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002740
Krzysztof Helt9e848062009-03-31 15:25:11 -07002741 op = 0x80;
Krzysztof Helt8343c892009-03-31 15:25:11 -07002742 if (bits_per_pixel >= 16) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002743 vga_wgfx(regbase, CL_GR10, bg_color >> 8);
2744 vga_wgfx(regbase, CL_GR11, fg_color >> 8);
2745 op = 0x90;
Krzysztof Helt8343c892009-03-31 15:25:11 -07002746 }
Krzysztof Helt7cade312009-03-31 15:25:13 -07002747 if (bits_per_pixel >= 24) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002748 vga_wgfx(regbase, CL_GR12, bg_color >> 16);
2749 vga_wgfx(regbase, CL_GR13, fg_color >> 16);
Krzysztof Helt7cade312009-03-31 15:25:13 -07002750 op = 0xa0;
2751 }
2752 if (bits_per_pixel == 32) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002753 vga_wgfx(regbase, CL_GR14, bg_color >> 24);
2754 vga_wgfx(regbase, CL_GR15, fg_color >> 24);
2755 op = 0xb0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002756 }
Krzysztof Helt8343c892009-03-31 15:25:11 -07002757 cirrusfb_set_blitter(regbase, width - 1, height - 1,
Krzysztof Helt9e848062009-03-31 15:25:11 -07002758 0, ndest, op | blitmode, line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002759}
2760
Linus Torvalds1da177e2005-04-16 15:20:36 -07002761/**************************************************************************
2762 * bestclock() - determine closest possible clock lower(?) than the
2763 * desired pixel clock
2764 **************************************************************************/
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002765static void bestclock(long freq, int *nom, int *den, int *div)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002766{
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002767 int n, d;
2768 long h, diff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002769
Krzysztof Helt8503df62007-10-16 01:29:08 -07002770 assert(nom != NULL);
2771 assert(den != NULL);
2772 assert(div != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002773
2774 *nom = 0;
2775 *den = 0;
2776 *div = 0;
2777
Linus Torvalds1da177e2005-04-16 15:20:36 -07002778 if (freq < 8000)
2779 freq = 8000;
2780
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002781 diff = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002782
2783 for (n = 32; n < 128; n++) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002784 int s = 0;
2785
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002786 d = (14318 * n) / freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002787 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002788 int temp = d;
2789
2790 if (temp > 31) {
2791 s = 1;
2792 temp >>= 1;
2793 }
2794 h = ((14318 * n) / temp) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002795 h = h > freq ? h - freq : freq - h;
2796 if (h < diff) {
2797 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002798 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002799 *den = temp;
2800 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002801 }
2802 }
Krzysztof Helt7528f542008-10-15 22:03:36 -07002803 d++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002804 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002805 if (d > 31) {
2806 s = 1;
2807 d >>= 1;
2808 }
2809 h = ((14318 * n) / d) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002810 h = h > freq ? h - freq : freq - h;
2811 if (h < diff) {
2812 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002813 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002814 *den = d;
2815 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002816 }
2817 }
2818 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002819}
2820
Linus Torvalds1da177e2005-04-16 15:20:36 -07002821/* -------------------------------------------------------------------------
2822 *
2823 * debugging functions
2824 *
2825 * -------------------------------------------------------------------------
2826 */
2827
2828#ifdef CIRRUSFB_DEBUG
2829
2830/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002831 * cirrusfb_dbg_print_regs
2832 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2833 * @reg_class: type of registers to read: %CRT, or %SEQ
2834 *
2835 * DESCRIPTION:
2836 * Dumps the given list of VGA CRTC registers. If @base is %NULL,
2837 * old-style I/O ports are queried for information, otherwise MMIO is
2838 * used at the given @base address to query the information.
2839 */
2840
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002841static void cirrusfb_dbg_print_regs(struct fb_info *info,
2842 caddr_t regbase,
2843 enum cirrusfb_dbg_reg_class reg_class, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002844{
2845 va_list list;
2846 unsigned char val = 0;
2847 unsigned reg;
2848 char *name;
2849
Krzysztof Helt8503df62007-10-16 01:29:08 -07002850 va_start(list, reg_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002851
Krzysztof Helt8503df62007-10-16 01:29:08 -07002852 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002853 while (name != NULL) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002854 reg = va_arg(list, int);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002855
2856 switch (reg_class) {
2857 case CRT:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002858 val = vga_rcrt(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002859 break;
2860 case SEQ:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002861 val = vga_rseq(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002862 break;
2863 default:
2864 /* should never occur */
Richard Knutssonc930faa2007-05-08 00:38:29 -07002865 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002866 break;
2867 }
2868
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002869 dev_dbg(info->device, "%8s = 0x%02X\n", name, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002870
Krzysztof Helt8503df62007-10-16 01:29:08 -07002871 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002872 }
2873
Krzysztof Helt8503df62007-10-16 01:29:08 -07002874 va_end(list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002875}
2876
Linus Torvalds1da177e2005-04-16 15:20:36 -07002877/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002878 * cirrusfb_dbg_reg_dump
2879 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2880 *
2881 * DESCRIPTION:
2882 * Dumps a list of interesting VGA and CIRRUSFB registers. If @base is %NULL,
2883 * old-style I/O ports are queried for information, otherwise MMIO is
2884 * used at the given @base address to query the information.
2885 */
2886
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002887static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002888{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002889 dev_dbg(info->device, "VGA CRTC register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002890
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002891 cirrusfb_dbg_print_regs(info, regbase, CRT,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002892 "CR00", 0x00,
2893 "CR01", 0x01,
2894 "CR02", 0x02,
2895 "CR03", 0x03,
2896 "CR04", 0x04,
2897 "CR05", 0x05,
2898 "CR06", 0x06,
2899 "CR07", 0x07,
2900 "CR08", 0x08,
2901 "CR09", 0x09,
2902 "CR0A", 0x0A,
2903 "CR0B", 0x0B,
2904 "CR0C", 0x0C,
2905 "CR0D", 0x0D,
2906 "CR0E", 0x0E,
2907 "CR0F", 0x0F,
2908 "CR10", 0x10,
2909 "CR11", 0x11,
2910 "CR12", 0x12,
2911 "CR13", 0x13,
2912 "CR14", 0x14,
2913 "CR15", 0x15,
2914 "CR16", 0x16,
2915 "CR17", 0x17,
2916 "CR18", 0x18,
2917 "CR22", 0x22,
2918 "CR24", 0x24,
2919 "CR26", 0x26,
2920 "CR2D", 0x2D,
2921 "CR2E", 0x2E,
2922 "CR2F", 0x2F,
2923 "CR30", 0x30,
2924 "CR31", 0x31,
2925 "CR32", 0x32,
2926 "CR33", 0x33,
2927 "CR34", 0x34,
2928 "CR35", 0x35,
2929 "CR36", 0x36,
2930 "CR37", 0x37,
2931 "CR38", 0x38,
2932 "CR39", 0x39,
2933 "CR3A", 0x3A,
2934 "CR3B", 0x3B,
2935 "CR3C", 0x3C,
2936 "CR3D", 0x3D,
2937 "CR3E", 0x3E,
2938 "CR3F", 0x3F,
2939 NULL);
2940
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002941 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002942
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002943 dev_dbg(info->device, "VGA SEQ register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002944
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002945 cirrusfb_dbg_print_regs(info, regbase, SEQ,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002946 "SR00", 0x00,
2947 "SR01", 0x01,
2948 "SR02", 0x02,
2949 "SR03", 0x03,
2950 "SR04", 0x04,
2951 "SR08", 0x08,
2952 "SR09", 0x09,
2953 "SR0A", 0x0A,
2954 "SR0B", 0x0B,
2955 "SR0D", 0x0D,
2956 "SR10", 0x10,
2957 "SR11", 0x11,
2958 "SR12", 0x12,
2959 "SR13", 0x13,
2960 "SR14", 0x14,
2961 "SR15", 0x15,
2962 "SR16", 0x16,
2963 "SR17", 0x17,
2964 "SR18", 0x18,
2965 "SR19", 0x19,
2966 "SR1A", 0x1A,
2967 "SR1B", 0x1B,
2968 "SR1C", 0x1C,
2969 "SR1D", 0x1D,
2970 "SR1E", 0x1E,
2971 "SR1F", 0x1F,
2972 NULL);
2973
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002974 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002975}
2976
2977#endif /* CIRRUSFB_DEBUG */
2978