blob: e4ac2f6d4d973960ded4c1e17b67736d172e6b34 [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,
98 BT_SD64,
99 BT_PICCOLO,
100 BT_PICASSO,
101 BT_SPECTRUM,
102 BT_PICASSO4, /* GD5446 */
103 BT_ALPINE, /* GD543x/4x */
104 BT_GD5480,
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700105 BT_LAGUNA, /* GD5462/64 */
106 BT_LAGUNAB, /* GD5465 */
Krzysztof Helt7345de32007-10-16 01:29:11 -0700107};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109/*
110 * per-board-type information, used for enumerating and abstracting
111 * chip-specific information
Krzysztof Helt7345de32007-10-16 01:29:11 -0700112 * NOTE: MUST be in the same order as enum cirrus_board in order to
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113 * use direct indexing on this array
114 * NOTE: '__initdata' cannot be used as some of this info
115 * is required at runtime. Maybe separate into an init-only and
116 * a run-time table?
117 */
118static const struct cirrusfb_board_info_rec {
119 char *name; /* ASCII name of chipset */
120 long maxclock[5]; /* maximum video clock */
121 /* for 1/4bpp, 8bpp 15/16bpp, 24bpp, 32bpp - numbers from xorg code */
Richard Knutssonc930faa2007-05-08 00:38:29 -0700122 bool init_sr07 : 1; /* init SR07 during init_vgachip() */
123 bool init_sr1f : 1; /* write SR1F during init_vgachip() */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700124 /* construct bit 19 of screen start address */
125 bool scrn_start_bit19 : 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126
127 /* initial SR07 value, then for each mode */
128 unsigned char sr07;
129 unsigned char sr07_1bpp;
130 unsigned char sr07_1bpp_mux;
131 unsigned char sr07_8bpp;
132 unsigned char sr07_8bpp_mux;
133
134 unsigned char sr1f; /* SR1F VGA initial register value */
135} cirrusfb_board_info[] = {
136 [BT_SD64] = {
137 .name = "CL SD64",
138 .maxclock = {
139 /* guess */
140 /* the SD64/P4 have a higher max. videoclock */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700141 135100, 135100, 85500, 85500, 0
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700143 .init_sr07 = true,
144 .init_sr1f = true,
145 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 .sr07 = 0xF0,
147 .sr07_1bpp = 0xF0,
148 .sr07_8bpp = 0xF1,
149 .sr1f = 0x20
150 },
151 [BT_PICCOLO] = {
152 .name = "CL Piccolo",
153 .maxclock = {
154 /* guess */
155 90000, 90000, 90000, 90000, 90000
156 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700157 .init_sr07 = true,
158 .init_sr1f = true,
159 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160 .sr07 = 0x80,
161 .sr07_1bpp = 0x80,
162 .sr07_8bpp = 0x81,
163 .sr1f = 0x22
164 },
165 [BT_PICASSO] = {
166 .name = "CL Picasso",
167 .maxclock = {
168 /* guess */
169 90000, 90000, 90000, 90000, 90000
170 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700171 .init_sr07 = true,
172 .init_sr1f = true,
173 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 .sr07 = 0x20,
175 .sr07_1bpp = 0x20,
176 .sr07_8bpp = 0x21,
177 .sr1f = 0x22
178 },
179 [BT_SPECTRUM] = {
180 .name = "CL Spectrum",
181 .maxclock = {
182 /* guess */
183 90000, 90000, 90000, 90000, 90000
184 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700185 .init_sr07 = true,
186 .init_sr1f = true,
187 .scrn_start_bit19 = false,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188 .sr07 = 0x80,
189 .sr07_1bpp = 0x80,
190 .sr07_8bpp = 0x81,
191 .sr1f = 0x22
192 },
193 [BT_PICASSO4] = {
194 .name = "CL Picasso4",
195 .maxclock = {
196 135100, 135100, 85500, 85500, 0
197 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700198 .init_sr07 = true,
199 .init_sr1f = false,
200 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201 .sr07 = 0x20,
202 .sr07_1bpp = 0x20,
203 .sr07_8bpp = 0x21,
204 .sr1f = 0
205 },
206 [BT_ALPINE] = {
207 .name = "CL Alpine",
208 .maxclock = {
209 /* for the GD5430. GD5446 can do more... */
210 85500, 85500, 50000, 28500, 0
211 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700212 .init_sr07 = true,
213 .init_sr1f = true,
214 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215 .sr07 = 0xA0,
216 .sr07_1bpp = 0xA1,
217 .sr07_1bpp_mux = 0xA7,
218 .sr07_8bpp = 0xA1,
219 .sr07_8bpp_mux = 0xA7,
220 .sr1f = 0x1C
221 },
222 [BT_GD5480] = {
223 .name = "CL GD5480",
224 .maxclock = {
225 135100, 200000, 200000, 135100, 135100
226 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700227 .init_sr07 = true,
228 .init_sr1f = true,
229 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230 .sr07 = 0x10,
231 .sr07_1bpp = 0x11,
232 .sr07_8bpp = 0x11,
233 .sr1f = 0x1C
234 },
235 [BT_LAGUNA] = {
236 .name = "CL Laguna",
237 .maxclock = {
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700238 /* taken from X11 code */
239 170000, 170000, 170000, 170000, 135100,
240 },
241 .init_sr07 = false,
242 .init_sr1f = false,
243 .scrn_start_bit19 = true,
244 },
245 [BT_LAGUNAB] = {
246 .name = "CL Laguna AGP",
247 .maxclock = {
248 /* taken from X11 code */
249 170000, 250000, 170000, 170000, 135100,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250 },
Richard Knutssonc930faa2007-05-08 00:38:29 -0700251 .init_sr07 = false,
252 .init_sr1f = false,
253 .scrn_start_bit19 = true,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254 }
255};
256
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257#ifdef CONFIG_PCI
258#define CHIP(id, btype) \
Grant Coady41538122005-09-29 10:40:52 +1000259 { PCI_VENDOR_ID_CIRRUS, id, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (btype) }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260
261static struct pci_device_id cirrusfb_pci_table[] = {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700262 CHIP(PCI_DEVICE_ID_CIRRUS_5436, BT_ALPINE),
263 CHIP(PCI_DEVICE_ID_CIRRUS_5434_8, BT_ALPINE),
264 CHIP(PCI_DEVICE_ID_CIRRUS_5434_4, BT_ALPINE),
265 CHIP(PCI_DEVICE_ID_CIRRUS_5430, BT_ALPINE), /* GD-5440 is same id */
266 CHIP(PCI_DEVICE_ID_CIRRUS_7543, BT_ALPINE),
267 CHIP(PCI_DEVICE_ID_CIRRUS_7548, BT_ALPINE),
268 CHIP(PCI_DEVICE_ID_CIRRUS_5480, BT_GD5480), /* MacPicasso likely */
269 CHIP(PCI_DEVICE_ID_CIRRUS_5446, BT_PICASSO4), /* Picasso 4 is 5446 */
270 CHIP(PCI_DEVICE_ID_CIRRUS_5462, BT_LAGUNA), /* CL Laguna */
271 CHIP(PCI_DEVICE_ID_CIRRUS_5464, BT_LAGUNA), /* CL Laguna 3D */
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700272 CHIP(PCI_DEVICE_ID_CIRRUS_5465, BT_LAGUNAB), /* CL Laguna 3DA*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273 { 0, }
274};
275MODULE_DEVICE_TABLE(pci, cirrusfb_pci_table);
276#undef CHIP
277#endif /* CONFIG_PCI */
278
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279#ifdef CONFIG_ZORRO
280static const struct zorro_device_id cirrusfb_zorro_table[] = {
281 {
282 .id = ZORRO_PROD_HELFRICH_SD64_RAM,
283 .driver_data = BT_SD64,
284 }, {
285 .id = ZORRO_PROD_HELFRICH_PICCOLO_RAM,
286 .driver_data = BT_PICCOLO,
287 }, {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700288 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_RAM,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289 .driver_data = BT_PICASSO,
290 }, {
291 .id = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_RAM,
292 .driver_data = BT_SPECTRUM,
293 }, {
294 .id = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_IV_Z3,
295 .driver_data = BT_PICASSO4,
296 },
297 { 0 }
298};
299
300static const struct {
301 zorro_id id2;
302 unsigned long size;
303} cirrusfb_zorro_table2[] = {
304 [BT_SD64] = {
305 .id2 = ZORRO_PROD_HELFRICH_SD64_REG,
306 .size = 0x400000
307 },
308 [BT_PICCOLO] = {
309 .id2 = ZORRO_PROD_HELFRICH_PICCOLO_REG,
310 .size = 0x200000
311 },
312 [BT_PICASSO] = {
313 .id2 = ZORRO_PROD_VILLAGE_TRONIC_PICASSO_II_II_PLUS_REG,
314 .size = 0x200000
315 },
316 [BT_SPECTRUM] = {
317 .id2 = ZORRO_PROD_GVP_EGS_28_24_SPECTRUM_REG,
318 .size = 0x200000
319 },
320 [BT_PICASSO4] = {
321 .id2 = 0,
322 .size = 0x400000
323 }
324};
325#endif /* CONFIG_ZORRO */
326
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327#ifdef CIRRUSFB_DEBUG
Krzysztof Helt7345de32007-10-16 01:29:11 -0700328enum cirrusfb_dbg_reg_class {
Krzysztof Helt8503df62007-10-16 01:29:08 -0700329 CRT,
330 SEQ
Krzysztof Helt7345de32007-10-16 01:29:11 -0700331};
Krzysztof Helt8503df62007-10-16 01:29:08 -0700332#endif /* CIRRUSFB_DEBUG */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333
334/* info about board */
335struct cirrusfb_info {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 u8 __iomem *regbase;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700337 u8 __iomem *laguna_mmio;
Krzysztof Helt7345de32007-10-16 01:29:11 -0700338 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339 unsigned char SFR; /* Shadow of special function register */
340
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700341 int multiplexing;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342 int blank_mode;
Krzysztof Helt64beab12008-10-15 22:03:38 -0700343 u32 pseudo_palette[16];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700345 void (*unmap)(struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346};
347
Krzysztof Helt55a0dd82008-10-15 22:03:41 -0700348static int noaccel __devinitdata;
Krzysztof Helta1d35a72008-10-15 22:03:38 -0700349static char *mode_option __devinitdata = "640x480@60";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350
351/****************************************************************************/
352/**** BEGIN PROTOTYPES ******************************************************/
353
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354/*--- Interface used by the world ------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700355static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
356 struct fb_info *info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358/*--- Internal routines ----------------------------------------------------*/
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700359static void init_vgachip(struct fb_info *info);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700360static void switch_monitor(struct cirrusfb_info *cinfo, int on);
361static void WGen(const struct cirrusfb_info *cinfo,
362 int regnum, unsigned char val);
363static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum);
364static void AttrOn(const struct cirrusfb_info *cinfo);
365static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val);
366static void WSFR(struct cirrusfb_info *cinfo, unsigned char val);
367static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val);
368static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum,
369 unsigned char red, unsigned char green, unsigned char blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370#if 0
Krzysztof Helt8503df62007-10-16 01:29:08 -0700371static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum,
372 unsigned char *red, unsigned char *green,
373 unsigned char *blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -0700375static void cirrusfb_WaitBLT(u8 __iomem *regbase);
376static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
377 u_short curx, u_short cury,
378 u_short destx, u_short desty,
379 u_short width, u_short height,
380 u_short line_length);
381static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
382 u_short x, u_short y,
383 u_short width, u_short height,
Krzysztof Helt8343c892009-03-31 15:25:11 -0700384 u32 color, u_short line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700386static void bestclock(long freq, int *nom, int *den, int *div);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387
388#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700389static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase);
390static void cirrusfb_dbg_print_regs(struct fb_info *info,
391 caddr_t regbase,
Krzysztof Helt7345de32007-10-16 01:29:11 -0700392 enum cirrusfb_dbg_reg_class reg_class, ...);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393#endif /* CIRRUSFB_DEBUG */
394
395/*** END PROTOTYPES ********************************************************/
396/*****************************************************************************/
397/*** BEGIN Interface Used by the World ***************************************/
398
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700399static inline int is_laguna(const struct cirrusfb_info *cinfo)
400{
401 return cinfo->btype == BT_LAGUNA || cinfo->btype == BT_LAGUNAB;
402}
403
Krzysztof Helt8503df62007-10-16 01:29:08 -0700404static int opencount;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405
406/*--- Open /dev/fbx ---------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700407static int cirrusfb_open(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408{
409 if (opencount++ == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700410 switch_monitor(info->par, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411 return 0;
412}
413
414/*--- Close /dev/fbx --------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700415static int cirrusfb_release(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700416{
417 if (--opencount == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700418 switch_monitor(info->par, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419 return 0;
420}
421
422/**** END Interface used by the World *************************************/
423/****************************************************************************/
424/**** BEGIN Hardware specific Routines **************************************/
425
Krzysztof Helt486ff382008-10-15 22:03:42 -0700426/* Check if the MCLK is not a better clock source */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700427static int cirrusfb_check_mclk(struct fb_info *info, long freq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700429 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700430 long mclk = vga_rseq(cinfo->regbase, CL_SEQR1F) & 0x3f;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431
Krzysztof Helt486ff382008-10-15 22:03:42 -0700432 /* Read MCLK value */
433 mclk = (14318 * mclk) >> 3;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700434 dev_dbg(info->device, "Read MCLK of %ld kHz\n", mclk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435
436 /* Determine if we should use MCLK instead of VCLK, and if so, what we
Krzysztof Helt486ff382008-10-15 22:03:42 -0700437 * should divide it by to get VCLK
438 */
439
440 if (abs(freq - mclk) < 250) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700441 dev_dbg(info->device, "Using VCLK = MCLK\n");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700442 return 1;
443 } else if (abs(freq - (mclk / 2)) < 250) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700444 dev_dbg(info->device, "Using VCLK = MCLK/2\n");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700445 return 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446 }
447
Krzysztof Helt486ff382008-10-15 22:03:42 -0700448 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449}
450
Krzysztof Helt99a45842009-03-31 15:25:09 -0700451static int cirrusfb_check_pixclock(const struct fb_var_screeninfo *var,
452 struct fb_info *info)
453{
454 long freq;
455 long maxclock;
456 struct cirrusfb_info *cinfo = info->par;
457 unsigned maxclockidx = var->bits_per_pixel >> 3;
458
459 /* convert from ps to kHz */
460 freq = PICOS2KHZ(var->pixclock);
461
462 dev_dbg(info->device, "desired pixclock: %ld kHz\n", freq);
463
464 maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx];
465 cinfo->multiplexing = 0;
466
467 /* If the frequency is greater than we can support, we might be able
468 * to use multiplexing for the video mode */
469 if (freq > maxclock) {
470 switch (cinfo->btype) {
471 case BT_ALPINE:
472 case BT_GD5480:
473 cinfo->multiplexing = 1;
474 break;
475
476 default:
477 dev_err(info->device,
478 "Frequency greater than maxclock (%ld kHz)\n",
479 maxclock);
480 return -EINVAL;
481 }
482 }
483#if 0
484 /* TODO: If we have a 1MB 5434, we need to put ourselves in a mode where
485 * the VCLK is double the pixel clock. */
486 switch (var->bits_per_pixel) {
487 case 16:
488 case 32:
489 if (var->xres <= 800)
490 /* Xbh has this type of clock for 32-bit */
491 freq /= 2;
492 break;
493 }
494#endif
495 return 0;
496}
497
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498static int cirrusfb_check_var(struct fb_var_screeninfo *var,
499 struct fb_info *info)
500{
Krzysztof Helt09a29102008-09-02 14:35:51 -0700501 int yres;
502 /* memory size in pixels */
503 unsigned pixels = info->screen_size * 8 / var->bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504
505 switch (var->bits_per_pixel) {
Krzysztof Helt060b6002007-10-16 01:29:13 -0700506 case 1:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507 var->red.offset = 0;
508 var->red.length = 1;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700509 var->green = var->red;
510 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 break;
512
513 case 8:
514 var->red.offset = 0;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700515 var->red.length = 8;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700516 var->green = var->red;
517 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 break;
519
520 case 16:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700521 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522 var->red.offset = 2;
523 var->green.offset = -3;
524 var->blue.offset = 8;
525 } else {
Krzysztof Heltc4dec392009-03-31 15:25:07 -0700526 var->red.offset = 11;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527 var->green.offset = 5;
528 var->blue.offset = 0;
529 }
530 var->red.length = 5;
Krzysztof Heltc4dec392009-03-31 15:25:07 -0700531 var->green.length = 6;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532 var->blue.length = 5;
533 break;
534
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 case 32:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700536 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537 var->red.offset = 8;
538 var->green.offset = 16;
539 var->blue.offset = 24;
540 } else {
541 var->red.offset = 16;
542 var->green.offset = 8;
543 var->blue.offset = 0;
544 }
545 var->red.length = 8;
546 var->green.length = 8;
547 var->blue.length = 8;
548 break;
549
550 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700551 dev_dbg(info->device,
552 "Unsupported bpp size: %d\n", var->bits_per_pixel);
Richard Knutssonc930faa2007-05-08 00:38:29 -0700553 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554 /* should never occur */
555 break;
556 }
557
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700558 if (var->xres_virtual < var->xres)
559 var->xres_virtual = var->xres;
560 /* use highest possible virtual resolution */
561 if (var->yres_virtual == -1) {
562 var->yres_virtual = pixels / var->xres_virtual;
563
564 dev_info(info->device,
565 "virtual resolution set to maximum of %dx%d\n",
566 var->xres_virtual, var->yres_virtual);
567 }
568 if (var->yres_virtual < var->yres)
569 var->yres_virtual = var->yres;
570
571 if (var->xres_virtual * var->yres_virtual > pixels) {
572 dev_err(info->device, "mode %dx%dx%d rejected... "
573 "virtual resolution too high to fit into video memory!\n",
574 var->xres_virtual, var->yres_virtual,
575 var->bits_per_pixel);
576 return -EINVAL;
577 }
578
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700579 if (var->xoffset < 0)
580 var->xoffset = 0;
581 if (var->yoffset < 0)
582 var->yoffset = 0;
583
584 /* truncate xoffset and yoffset to maximum if too high */
585 if (var->xoffset > var->xres_virtual - var->xres)
586 var->xoffset = var->xres_virtual - var->xres - 1;
587 if (var->yoffset > var->yres_virtual - var->yres)
588 var->yoffset = var->yres_virtual - var->yres - 1;
589
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590 var->red.msb_right =
591 var->green.msb_right =
592 var->blue.msb_right =
593 var->transp.offset =
594 var->transp.length =
595 var->transp.msb_right = 0;
596
597 yres = var->yres;
598 if (var->vmode & FB_VMODE_DOUBLE)
599 yres *= 2;
600 else if (var->vmode & FB_VMODE_INTERLACED)
601 yres = (yres + 1) / 2;
602
603 if (yres >= 1280) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700604 dev_err(info->device, "ERROR: VerticalTotal >= 1280; "
Krzysztof Helt8503df62007-10-16 01:29:08 -0700605 "special treatment required! (TODO)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606 return -EINVAL;
607 }
608
Krzysztof Helt99a45842009-03-31 15:25:09 -0700609 if (cirrusfb_check_pixclock(var, info))
610 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612 return 0;
613}
614
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700615static void cirrusfb_set_mclk_as_source(const struct fb_info *info, int div)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700617 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700618 unsigned char old1f, old1e;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700619
Krzysztof Helt8503df62007-10-16 01:29:08 -0700620 assert(cinfo != NULL);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700621 old1f = vga_rseq(cinfo->regbase, CL_SEQR1F) & ~0x40;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622
Krzysztof Helt486ff382008-10-15 22:03:42 -0700623 if (div) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700624 dev_dbg(info->device, "Set %s as pixclock source.\n",
625 (div == 2) ? "MCLK/2" : "MCLK");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700626 old1f |= 0x40;
627 old1e = vga_rseq(cinfo->regbase, CL_SEQR1E) & ~0x1;
628 if (div == 2)
629 old1e |= 1;
630
631 vga_wseq(cinfo->regbase, CL_SEQR1E, old1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700632 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700633 vga_wseq(cinfo->regbase, CL_SEQR1F, old1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634}
635
636/*************************************************************************
637 cirrusfb_set_par_foo()
638
639 actually writes the values for a new video mode into the hardware,
640**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700641static int cirrusfb_set_par_foo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642{
643 struct cirrusfb_info *cinfo = info->par;
644 struct fb_var_screeninfo *var = &info->var;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645 u8 __iomem *regbase = cinfo->regbase;
646 unsigned char tmp;
Krzysztof Helt6683e012009-03-31 15:25:06 -0700647 int pitch;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648 const struct cirrusfb_board_info_rec *bi;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700649 int hdispend, hsyncstart, hsyncend, htotal;
650 int yres, vdispend, vsyncstart, vsyncend, vtotal;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700651 long freq;
652 int nom, den, div;
Krzysztof Helt1b48cb52009-03-31 15:25:08 -0700653 unsigned int control = 0, format = 0, threshold = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700655 dev_dbg(info->device, "Requested mode: %dx%dx%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt99a45842009-03-31 15:25:09 -0700657
658 switch (var->bits_per_pixel) {
659 case 1:
660 info->fix.line_length = var->xres_virtual / 8;
661 info->fix.visual = FB_VISUAL_MONO10;
662 break;
663
664 case 8:
665 info->fix.line_length = var->xres_virtual;
666 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
667 break;
668
669 case 16:
670 case 32:
671 info->fix.line_length = var->xres_virtual *
672 var->bits_per_pixel >> 3;
673 info->fix.visual = FB_VISUAL_TRUECOLOR;
674 break;
675 }
676 info->fix.type = FB_TYPE_PACKED_PIXELS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700678 init_vgachip(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680 bi = &cirrusfb_board_info[cinfo->btype];
681
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700682 hsyncstart = var->xres + var->right_margin;
683 hsyncend = hsyncstart + var->hsync_len;
684 htotal = (hsyncend + var->left_margin) / 8 - 5;
685 hdispend = var->xres / 8 - 1;
686 hsyncstart = hsyncstart / 8 + 1;
687 hsyncend = hsyncend / 8 + 1;
688
689 yres = var->yres;
690 vsyncstart = yres + var->lower_margin;
691 vsyncend = vsyncstart + var->vsync_len;
692 vtotal = vsyncend + var->upper_margin;
693 vdispend = yres - 1;
694
695 if (var->vmode & FB_VMODE_DOUBLE) {
696 yres *= 2;
697 vsyncstart *= 2;
698 vsyncend *= 2;
699 vtotal *= 2;
700 } else if (var->vmode & FB_VMODE_INTERLACED) {
701 yres = (yres + 1) / 2;
702 vsyncstart = (vsyncstart + 1) / 2;
703 vsyncend = (vsyncend + 1) / 2;
704 vtotal = (vtotal + 1) / 2;
705 }
706
707 vtotal -= 2;
708 vsyncstart -= 1;
709 vsyncend -= 1;
710
711 if (yres >= 1024) {
712 vtotal /= 2;
713 vsyncstart /= 2;
714 vsyncend /= 2;
715 vdispend /= 2;
716 }
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700717 if (cinfo->multiplexing) {
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700718 htotal /= 2;
719 hsyncstart /= 2;
720 hsyncend /= 2;
721 hdispend /= 2;
722 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 /* unlock register VGA_CRTC_H_TOTAL..CRT7 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700724 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20); /* previously: 0x00) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725
726 /* if debugging is enabled, all parameters get output before writing */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700727 dev_dbg(info->device, "CRT0: %d\n", htotal);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700728 vga_wcrt(regbase, VGA_CRTC_H_TOTAL, htotal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700730 dev_dbg(info->device, "CRT1: %d\n", hdispend);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700731 vga_wcrt(regbase, VGA_CRTC_H_DISP, hdispend);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700733 dev_dbg(info->device, "CRT2: %d\n", var->xres / 8);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700734 vga_wcrt(regbase, VGA_CRTC_H_BLANK_START, var->xres / 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735
Krzysztof Helt8503df62007-10-16 01:29:08 -0700736 /* + 128: Compatible read */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700737 dev_dbg(info->device, "CRT3: 128+%d\n", (htotal + 5) % 32);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700738 vga_wcrt(regbase, VGA_CRTC_H_BLANK_END,
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700739 128 + ((htotal + 5) % 32));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700741 dev_dbg(info->device, "CRT4: %d\n", hsyncstart);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700742 vga_wcrt(regbase, VGA_CRTC_H_SYNC_START, hsyncstart);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700744 tmp = hsyncend % 32;
745 if ((htotal + 5) & 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700746 tmp += 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700747 dev_dbg(info->device, "CRT5: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700748 vga_wcrt(regbase, VGA_CRTC_H_SYNC_END, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700750 dev_dbg(info->device, "CRT6: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700751 vga_wcrt(regbase, VGA_CRTC_V_TOTAL, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752
753 tmp = 16; /* LineCompare bit #9 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700754 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700756 if (vdispend & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757 tmp |= 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700758 if (vsyncstart & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759 tmp |= 4;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700760 if ((vdispend + 1) & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761 tmp |= 8;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700762 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700764 if (vdispend & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700766 if (vsyncstart & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767 tmp |= 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700768 dev_dbg(info->device, "CRT7: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700769 vga_wcrt(regbase, VGA_CRTC_OVERFLOW, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770
771 tmp = 0x40; /* LineCompare bit #8 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700772 if ((vdispend + 1) & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773 tmp |= 0x20;
774 if (var->vmode & FB_VMODE_DOUBLE)
775 tmp |= 0x80;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700776 dev_dbg(info->device, "CRT9: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700777 vga_wcrt(regbase, VGA_CRTC_MAX_SCAN, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700779 dev_dbg(info->device, "CRT10: %d\n", vsyncstart & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700780 vga_wcrt(regbase, VGA_CRTC_V_SYNC_START, vsyncstart & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700782 dev_dbg(info->device, "CRT11: 64+32+%d\n", vsyncend % 16);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700783 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, vsyncend % 16 + 64 + 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700785 dev_dbg(info->device, "CRT12: %d\n", vdispend & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700786 vga_wcrt(regbase, VGA_CRTC_V_DISP_END, vdispend & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700788 dev_dbg(info->device, "CRT15: %d\n", (vdispend + 1) & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700789 vga_wcrt(regbase, VGA_CRTC_V_BLANK_START, (vdispend + 1) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700791 dev_dbg(info->device, "CRT16: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700792 vga_wcrt(regbase, VGA_CRTC_V_BLANK_END, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700794 dev_dbg(info->device, "CRT18: 0xff\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700795 vga_wcrt(regbase, VGA_CRTC_LINE_COMPARE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700796
797 tmp = 0;
798 if (var->vmode & FB_VMODE_INTERLACED)
799 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700800 if ((htotal + 5) & 64)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801 tmp |= 16;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700802 if ((htotal + 5) & 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700803 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700804 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700806 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807 tmp |= 128;
808
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700809 dev_dbg(info->device, "CRT1a: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700810 vga_wcrt(regbase, CL_CRT1A, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700812 freq = PICOS2KHZ(var->pixclock);
813 bestclock(freq, &nom, &den, &div);
814
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700815 dev_dbg(info->device, "VCLK freq: %ld kHz nom: %d den: %d div: %d\n",
816 freq, nom, den, div);
817
Linus Torvalds1da177e2005-04-16 15:20:36 -0700818 /* set VCLK0 */
819 /* hardware RefClock: 14.31818 MHz */
820 /* formula: VClk = (OSC * N) / (D * (1+P)) */
821 /* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */
822
Krzysztof Helt486ff382008-10-15 22:03:42 -0700823 if (cinfo->btype == BT_ALPINE) {
824 /* if freq is close to mclk or mclk/2 select mclk
825 * as clock source
826 */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700827 int divMCLK = cirrusfb_check_mclk(info, freq);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700828 if (divMCLK) {
829 nom = 0;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700830 cirrusfb_set_mclk_as_source(info, divMCLK);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700831 }
832 }
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700833 if (is_laguna(cinfo)) {
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700834 long pcifc = fb_readl(cinfo->laguna_mmio + 0x3fc);
835 unsigned char tile = fb_readb(cinfo->laguna_mmio + 0x407);
836 unsigned short tile_control;
837
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700838 if (cinfo->btype == BT_LAGUNAB) {
839 tile_control = fb_readw(cinfo->laguna_mmio + 0x2c4);
840 tile_control &= ~0x80;
841 fb_writew(tile_control, cinfo->laguna_mmio + 0x2c4);
842 }
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700843
844 fb_writel(pcifc | 0x10000000l, cinfo->laguna_mmio + 0x3fc);
845 fb_writeb(tile & 0x3f, cinfo->laguna_mmio + 0x407);
846 control = fb_readw(cinfo->laguna_mmio + 0x402);
847 threshold = fb_readw(cinfo->laguna_mmio + 0xea);
848 control &= ~0x6800;
849 format = 0;
Krzysztof Helt1b48cb52009-03-31 15:25:08 -0700850 threshold &= 0xffe0 & 0x3fbf;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700851 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700852 if (nom) {
Krzysztof Helt486ff382008-10-15 22:03:42 -0700853 tmp = den << 1;
854 if (div != 0)
855 tmp |= 1;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700856 /* 6 bit denom; ONLY 5434!!! (bugged me 10 days) */
857 if ((cinfo->btype == BT_SD64) ||
858 (cinfo->btype == BT_ALPINE) ||
859 (cinfo->btype == BT_GD5480))
860 tmp |= 0x80;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700862 dev_dbg(info->device, "CL_SEQR1B: %d\n", (int) tmp);
863 /* Laguna chipset has reversed clock registers */
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700864 if (is_laguna(cinfo)) {
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700865 vga_wseq(regbase, CL_SEQRE, tmp);
866 vga_wseq(regbase, CL_SEQR1E, nom);
867 } else {
868 vga_wseq(regbase, CL_SEQRB, nom);
869 vga_wseq(regbase, CL_SEQR1B, tmp);
870 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700871 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700873 if (yres >= 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874 /* 1280x1024 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700875 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876 else
877 /* mode control: VGA_CRTC_START_HI enable, ROTATE(?), 16bit
878 * address wrap, no compat. */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700879 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880
Linus Torvalds1da177e2005-04-16 15:20:36 -0700881 /* don't know if it would hurt to also program this if no interlaced */
882 /* mode is used, but I feel better this way.. :-) */
883 if (var->vmode & FB_VMODE_INTERLACED)
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700884 vga_wcrt(regbase, VGA_CRTC_REGS, htotal / 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700885 else
Krzysztof Helt8503df62007-10-16 01:29:08 -0700886 vga_wcrt(regbase, VGA_CRTC_REGS, 0x00); /* interlace control */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700887
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888 /* adjust horizontal/vertical sync type (low/high) */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700889 /* enable display memory & CRTC I/O address for color mode */
890 tmp = 0x03;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700891 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
892 tmp |= 0x40;
893 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
894 tmp |= 0x80;
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700895 if (is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -0700896 tmp |= 0xc;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700897 WGen(cinfo, VGA_MIS_W, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700898
Krzysztof Helt8503df62007-10-16 01:29:08 -0700899 /* text cursor on and start line */
900 vga_wcrt(regbase, VGA_CRTC_CURSOR_START, 0);
901 /* text cursor end line */
902 vga_wcrt(regbase, VGA_CRTC_CURSOR_END, 31);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700903
904 /******************************************************
905 *
906 * 1 bpp
907 *
908 */
909
910 /* programming for different color depths */
911 if (var->bits_per_pixel == 1) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700912 dev_dbg(info->device, "preparing for 1 bit deep display\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700913 vga_wgfx(regbase, VGA_GFX_MODE, 0); /* mode register */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700914
915 /* SR07 */
916 switch (cinfo->btype) {
917 case BT_SD64:
918 case BT_PICCOLO:
919 case BT_PICASSO:
920 case BT_SPECTRUM:
921 case BT_PICASSO4:
922 case BT_ALPINE:
923 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700924 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700925 cinfo->multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926 bi->sr07_1bpp_mux : bi->sr07_1bpp);
927 break;
928
929 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700930 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700931 vga_wseq(regbase, CL_SEQR7,
932 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700933 break;
934
935 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700936 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937 break;
938 }
939
940 /* Extended Sequencer Mode */
941 switch (cinfo->btype) {
942 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700943 /* setting the SEQRF on SD64 is not necessary
944 * (only during init)
945 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700946 /* MCLK select */
947 vga_wseq(regbase, CL_SEQR1F, 0x1a);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948 break;
949
950 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -0700951 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700952 /* ### ueberall 0x22? */
953 /* ##vorher 1c MCLK select */
954 vga_wseq(regbase, CL_SEQR1F, 0x22);
955 /* evtl d0 bei 1 bit? avoid FIFO underruns..? */
956 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957 break;
958
959 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700960 /* ##vorher 22 MCLK select */
961 vga_wseq(regbase, CL_SEQR1F, 0x22);
962 /* ## vorher d0 avoid FIFO underruns..? */
963 vga_wseq(regbase, CL_SEQRF, 0xd0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964 break;
965
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966 case BT_PICASSO4:
967 case BT_ALPINE:
968 case BT_GD5480:
969 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700970 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700971 /* do nothing */
972 break;
973
974 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700975 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700976 break;
977 }
978
Krzysztof Helt8503df62007-10-16 01:29:08 -0700979 /* pixel mask: pass-through for first plane */
980 WGen(cinfo, VGA_PEL_MSK, 0x01);
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700981 if (cinfo->multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700982 /* hidden dac reg: 1280x1024 */
983 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984 else
Krzysztof Helt8503df62007-10-16 01:29:08 -0700985 /* hidden dac: nothing */
986 WHDR(cinfo, 0);
987 /* memory mode: odd/even, ext. memory */
988 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x06);
989 /* plane mask: only write to first plane */
990 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991 }
992
993 /******************************************************
994 *
995 * 8 bpp
996 *
997 */
998
999 else if (var->bits_per_pixel == 8) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001000 dev_dbg(info->device, "preparing for 8 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001 switch (cinfo->btype) {
1002 case BT_SD64:
1003 case BT_PICCOLO:
1004 case BT_PICASSO:
1005 case BT_SPECTRUM:
1006 case BT_PICASSO4:
1007 case BT_ALPINE:
1008 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001009 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001010 cinfo->multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011 bi->sr07_8bpp_mux : bi->sr07_8bpp);
1012 break;
1013
1014 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001015 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001016 vga_wseq(regbase, CL_SEQR7,
1017 vga_rseq(regbase, CL_SEQR7) | 0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001018 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001019 break;
1020
1021 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001022 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001023 break;
1024 }
1025
1026 switch (cinfo->btype) {
1027 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001028 /* MCLK select */
1029 vga_wseq(regbase, CL_SEQR1F, 0x1d);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001030 break;
1031
1032 case BT_PICCOLO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033 case BT_PICASSO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001034 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001035 /* ### vorher 1c MCLK select */
1036 vga_wseq(regbase, CL_SEQR1F, 0x22);
1037 /* Fast Page-Mode writes */
1038 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001039 break;
1040
1041 case BT_PICASSO4:
1042#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001043 /* ### INCOMPLETE!! */
1044 vga_wseq(regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001045#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07001046/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001047 break;
1048
1049 case BT_ALPINE:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001050 /* We already set SRF and SR1F */
1051 break;
1052
1053 case BT_GD5480:
1054 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001055 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056 /* do nothing */
1057 break;
1058
1059 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001060 dev_warn(info->device, "unknown board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061 break;
1062 }
1063
Krzysztof Helt8503df62007-10-16 01:29:08 -07001064 /* mode register: 256 color mode */
1065 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001066 if (cinfo->multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001067 /* hidden dac reg: 1280x1024 */
1068 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001070 /* hidden dac: nothing */
1071 WHDR(cinfo, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072 }
1073
1074 /******************************************************
1075 *
1076 * 16 bpp
1077 *
1078 */
1079
1080 else if (var->bits_per_pixel == 16) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001081 dev_dbg(info->device, "preparing for 16 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082 switch (cinfo->btype) {
1083 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001084 /* Extended Sequencer Mode: 256c col. mode */
1085 vga_wseq(regbase, CL_SEQR7, 0xf7);
1086 /* MCLK select */
1087 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088 break;
1089
1090 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001091 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001092 vga_wseq(regbase, CL_SEQR7, 0x87);
1093 /* Fast Page-Mode writes */
1094 vga_wseq(regbase, CL_SEQRF, 0xb0);
1095 /* MCLK select */
1096 vga_wseq(regbase, CL_SEQR1F, 0x22);
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);
1103 /* MCLK select */
1104 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001105 break;
1106
Linus Torvalds1da177e2005-04-16 15:20:36 -07001107 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001108 vga_wseq(regbase, CL_SEQR7, 0x27);
1109/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001110 break;
1111
1112 case BT_ALPINE:
Krzysztof Helt3b921832008-10-15 22:03:41 -07001113 vga_wseq(regbase, CL_SEQR7, 0xa7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001114 break;
1115
1116 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001117 vga_wseq(regbase, CL_SEQR7, 0x17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001118 /* We already set SRF and SR1F */
1119 break;
1120
1121 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001122 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001123 vga_wseq(regbase, CL_SEQR7,
1124 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001125 control |= 0x2000;
1126 format |= 0x1400;
1127 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001128 break;
1129
1130 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001131 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001132 break;
1133 }
1134
Krzysztof Helt8503df62007-10-16 01:29:08 -07001135 /* mode register: 256 color mode */
1136 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001137#ifdef CONFIG_PCI
Krzysztof Heltc4dec392009-03-31 15:25:07 -07001138 WHDR(cinfo, 0xc1); /* Copy Xbh */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001139#elif defined(CONFIG_ZORRO)
1140 /* FIXME: CONFIG_PCI and CONFIG_ZORRO may be defined both */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001141 WHDR(cinfo, 0xa0); /* hidden dac reg: nothing special */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001142#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001143 }
1144
1145 /******************************************************
1146 *
1147 * 32 bpp
1148 *
1149 */
1150
1151 else if (var->bits_per_pixel == 32) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001152 dev_dbg(info->device, "preparing for 32 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001153 switch (cinfo->btype) {
1154 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001155 /* Extended Sequencer Mode: 256c col. mode */
1156 vga_wseq(regbase, CL_SEQR7, 0xf9);
1157 /* MCLK select */
1158 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001159 break;
1160
1161 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001162 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001163 vga_wseq(regbase, CL_SEQR7, 0x85);
1164 /* Fast Page-Mode writes */
1165 vga_wseq(regbase, CL_SEQRF, 0xb0);
1166 /* MCLK select */
1167 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001168 break;
1169
1170 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001171 vga_wseq(regbase, CL_SEQR7, 0x25);
1172 /* Fast Page-Mode writes */
1173 vga_wseq(regbase, CL_SEQRF, 0xb0);
1174 /* MCLK select */
1175 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001176 break;
1177
Linus Torvalds1da177e2005-04-16 15:20:36 -07001178 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001179 vga_wseq(regbase, CL_SEQR7, 0x25);
1180/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001181 break;
1182
1183 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001184 vga_wseq(regbase, CL_SEQR7, 0xa9);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001185 break;
1186
1187 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001188 vga_wseq(regbase, CL_SEQR7, 0x19);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001189 /* We already set SRF and SR1F */
1190 break;
1191
1192 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001193 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001194 vga_wseq(regbase, CL_SEQR7,
1195 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001196 control |= 0x6000;
1197 format |= 0x3400;
1198 threshold |= 0x20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001199 break;
1200
1201 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001202 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001203 break;
1204 }
1205
Krzysztof Helt8503df62007-10-16 01:29:08 -07001206 /* mode register: 256 color mode */
1207 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001208 /* hidden dac reg: 8-8-8 mode (24 or 32) */
1209 WHDR(cinfo, 0xc5);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001210 }
1211
1212 /******************************************************
1213 *
1214 * unknown/unsupported bpp
1215 *
1216 */
1217
Krzysztof Helt8503df62007-10-16 01:29:08 -07001218 else
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001219 dev_err(info->device,
1220 "What's this? requested color depth == %d.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001221 var->bits_per_pixel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001222
Krzysztof Helt6683e012009-03-31 15:25:06 -07001223 pitch = info->fix.line_length >> 3;
1224 vga_wcrt(regbase, VGA_CRTC_OFFSET, pitch & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001225 tmp = 0x22;
Krzysztof Helt6683e012009-03-31 15:25:06 -07001226 if (pitch & 0x100)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001227 tmp |= 0x10; /* offset overflow bit */
1228
Krzysztof Helt8503df62007-10-16 01:29:08 -07001229 /* screen start addr #16-18, fastpagemode cycles */
1230 vga_wcrt(regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001231
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001232 /* screen start address bit 19 */
1233 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19)
Krzysztof Helt6683e012009-03-31 15:25:06 -07001234 vga_wcrt(regbase, CL_CRT1D, (pitch >> 9) & 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001235
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001236 if (is_laguna(cinfo)) {
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001237 tmp = 0;
1238 if ((htotal + 5) & 256)
1239 tmp |= 128;
1240 if (hdispend & 256)
1241 tmp |= 64;
1242 if (hsyncstart & 256)
1243 tmp |= 48;
1244 if (vtotal & 1024)
1245 tmp |= 8;
1246 if (vdispend & 1024)
1247 tmp |= 4;
1248 if (vsyncstart & 1024)
1249 tmp |= 3;
1250
1251 vga_wcrt(regbase, CL_CRT1E, tmp);
1252 dev_dbg(info->device, "CRT1e: %d\n", tmp);
1253 }
1254
Krzysztof Helt8503df62007-10-16 01:29:08 -07001255 /* pixel panning */
1256 vga_wattr(regbase, CL_AR33, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001257
1258 /* [ EGS: SetOffset(); ] */
1259 /* From SetOffset(): Turn on VideoEnable bit in Attribute controller */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001260 AttrOn(cinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001261
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001262 if (is_laguna(cinfo)) {
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001263 /* no tiles */
1264 fb_writew(control | 0x1000, cinfo->laguna_mmio + 0x402);
1265 fb_writew(format, cinfo->laguna_mmio + 0xc0);
1266 fb_writew(threshold, cinfo->laguna_mmio + 0xea);
1267 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001268 /* finally, turn on everything - turn off "FullBandwidth" bit */
1269 /* also, set "DotClock%2" bit where requested */
1270 tmp = 0x01;
1271
1272/*** FB_VMODE_CLOCK_HALVE in linux/fb.h not defined anymore ?
1273 if (var->vmode & FB_VMODE_CLOCK_HALVE)
1274 tmp |= 0x08;
1275*/
1276
Krzysztof Helt8503df62007-10-16 01:29:08 -07001277 vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, tmp);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001278 dev_dbg(info->device, "CL_SEQR1: %d\n", tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001279
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001281 cirrusfb_dbg_reg_dump(info, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001282#endif
1283
Linus Torvalds1da177e2005-04-16 15:20:36 -07001284 return 0;
1285}
1286
1287/* for some reason incomprehensible to me, cirrusfb requires that you write
1288 * the registers twice for the settings to take..grr. -dte */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001289static int cirrusfb_set_par(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001290{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001291 cirrusfb_set_par_foo(info);
1292 return cirrusfb_set_par_foo(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001293}
1294
Krzysztof Helt8503df62007-10-16 01:29:08 -07001295static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
1296 unsigned blue, unsigned transp,
1297 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001298{
1299 struct cirrusfb_info *cinfo = info->par;
1300
1301 if (regno > 255)
1302 return -EINVAL;
1303
1304 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
1305 u32 v;
1306 red >>= (16 - info->var.red.length);
1307 green >>= (16 - info->var.green.length);
1308 blue >>= (16 - info->var.blue.length);
1309
Krzysztof Helt8503df62007-10-16 01:29:08 -07001310 if (regno >= 16)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001311 return 1;
1312 v = (red << info->var.red.offset) |
1313 (green << info->var.green.offset) |
1314 (blue << info->var.blue.offset);
1315
Krzysztof Helt060b6002007-10-16 01:29:13 -07001316 cinfo->pseudo_palette[regno] = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001317 return 0;
1318 }
1319
Krzysztof Helt8503df62007-10-16 01:29:08 -07001320 if (info->var.bits_per_pixel == 8)
1321 WClut(cinfo, regno, red >> 10, green >> 10, blue >> 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001322
1323 return 0;
1324
1325}
1326
1327/*************************************************************************
1328 cirrusfb_pan_display()
1329
1330 performs display panning - provided hardware permits this
1331**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07001332static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
1333 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001334{
Krzysztof Helt99a45842009-03-31 15:25:09 -07001335 int xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001336 unsigned long base;
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001337 unsigned char tmp, xpix;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001338 struct cirrusfb_info *cinfo = info->par;
1339
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001340 dev_dbg(info->device,
1341 "virtual offset: (%d,%d)\n", var->xoffset, var->yoffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001342
1343 /* no range checks for xoffset and yoffset, */
1344 /* as fb_pan_display has already done this */
1345 if (var->vmode & FB_VMODE_YWRAP)
1346 return -EINVAL;
1347
Linus Torvalds1da177e2005-04-16 15:20:36 -07001348 xoffset = var->xoffset * info->var.bits_per_pixel / 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349
Krzysztof Helt99a45842009-03-31 15:25:09 -07001350 base = var->yoffset * info->fix.line_length + xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001351
1352 if (info->var.bits_per_pixel == 1) {
1353 /* base is already correct */
1354 xpix = (unsigned char) (var->xoffset % 8);
1355 } else {
1356 base /= 4;
1357 xpix = (unsigned char) ((xoffset % 4) * 2);
1358 }
1359
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001360 if (!is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001361 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001362
1363 /* lower 8 + 8 bits of screen start address */
Krzysztof Helt99a45842009-03-31 15:25:09 -07001364 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, base & 0xff);
1365 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, (base >> 8) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001366
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001367 /* 0xf2 is %11110010, exclude tmp bits */
1368 tmp = vga_rcrt(cinfo->regbase, CL_CRT1B) & 0xf2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001369 /* construct bits 16, 17 and 18 of screen start address */
1370 if (base & 0x10000)
1371 tmp |= 0x01;
1372 if (base & 0x20000)
1373 tmp |= 0x04;
1374 if (base & 0x40000)
1375 tmp |= 0x08;
1376
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001377 vga_wcrt(cinfo->regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001378
1379 /* construct bit 19 of screen start address */
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001380 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19) {
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001381 tmp = vga_rcrt(cinfo->regbase, CL_CRT1D);
1382 if (is_laguna(cinfo))
1383 tmp = (tmp & ~0x18) | ((base >> 16) & 0x18);
1384 else
1385 tmp = (tmp & ~0x80) | ((base >> 12) & 0x80);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001386 vga_wcrt(cinfo->regbase, CL_CRT1D, tmp);
1387 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001388
Krzysztof Helt8503df62007-10-16 01:29:08 -07001389 /* write pixel panning value to AR33; this does not quite work in 8bpp
1390 *
1391 * ### Piccolo..? Will this work?
1392 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001393 if (info->var.bits_per_pixel == 1)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001394 vga_wattr(cinfo->regbase, CL_AR33, xpix);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001395
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001396 if (!is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001397 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001398
Krzysztof Helt8503df62007-10-16 01:29:08 -07001399 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001400}
1401
Krzysztof Helt8503df62007-10-16 01:29:08 -07001402static int cirrusfb_blank(int blank_mode, struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001403{
1404 /*
Krzysztof Helt8503df62007-10-16 01:29:08 -07001405 * Blank the screen if blank_mode != 0, else unblank. If blank == NULL
1406 * then the caller blanks by setting the CLUT (Color Look Up Table)
1407 * to all black. Return 0 if blanking succeeded, != 0 if un-/blanking
1408 * failed due to e.g. a video mode which doesn't support it.
1409 * Implements VESA suspend and powerdown modes on hardware that
1410 * supports disabling hsync/vsync:
1411 * blank_mode == 2: suspend vsync
1412 * blank_mode == 3: suspend hsync
1413 * blank_mode == 4: powerdown
Linus Torvalds1da177e2005-04-16 15:20:36 -07001414 */
1415 unsigned char val;
1416 struct cirrusfb_info *cinfo = info->par;
1417 int current_mode = cinfo->blank_mode;
1418
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001419 dev_dbg(info->device, "ENTER, blank mode = %d\n", blank_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001420
1421 if (info->state != FBINFO_STATE_RUNNING ||
1422 current_mode == blank_mode) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001423 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001424 return 0;
1425 }
1426
1427 /* Undo current */
1428 if (current_mode == FB_BLANK_NORMAL ||
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001429 current_mode == FB_BLANK_UNBLANK)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001430 /* clear "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001431 val = 0;
1432 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001433 /* set "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001434 val = 0x20;
1435
1436 val |= vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE) & 0xdf;
1437 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001438
1439 switch (blank_mode) {
1440 case FB_BLANK_UNBLANK:
1441 case FB_BLANK_NORMAL:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001442 val = 0x00;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001443 break;
1444 case FB_BLANK_VSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001445 val = 0x04;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001446 break;
1447 case FB_BLANK_HSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001448 val = 0x02;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001449 break;
1450 case FB_BLANK_POWERDOWN:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001451 val = 0x06;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001452 break;
1453 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001454 dev_dbg(info->device, "EXIT, returning 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001455 return 1;
1456 }
1457
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001458 vga_wgfx(cinfo->regbase, CL_GRE, val);
1459
Linus Torvalds1da177e2005-04-16 15:20:36 -07001460 cinfo->blank_mode = blank_mode;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001461 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001462
1463 /* Let fbcon do a soft blank for us */
1464 return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
1465}
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001466
Linus Torvalds1da177e2005-04-16 15:20:36 -07001467/**** END Hardware specific Routines **************************************/
1468/****************************************************************************/
1469/**** BEGIN Internal Routines ***********************************************/
1470
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001471static void init_vgachip(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001472{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001473 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001474 const struct cirrusfb_board_info_rec *bi;
1475
Krzysztof Helt8503df62007-10-16 01:29:08 -07001476 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001477
1478 bi = &cirrusfb_board_info[cinfo->btype];
1479
1480 /* reset board globally */
1481 switch (cinfo->btype) {
1482 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001483 WSFR(cinfo, 0x01);
1484 udelay(500);
1485 WSFR(cinfo, 0x51);
1486 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001487 break;
1488 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001489 WSFR2(cinfo, 0xff);
1490 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001491 break;
1492 case BT_SD64:
1493 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001494 WSFR(cinfo, 0x1f);
1495 udelay(500);
1496 WSFR(cinfo, 0x4f);
1497 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001498 break;
1499 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001500 /* disable flickerfixer */
1501 vga_wcrt(cinfo->regbase, CL_CRT51, 0x00);
1502 mdelay(100);
1503 /* from Klaus' NetBSD driver: */
1504 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
1505 /* put blitter into 542x compat */
1506 vga_wgfx(cinfo->regbase, CL_GR33, 0x00);
1507 /* mode */
1508 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001509 break;
1510
1511 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001512 /* from Klaus' NetBSD driver: */
1513 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001514 break;
1515
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001516 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001517 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001518 case BT_ALPINE:
1519 /* Nothing to do to reset the board. */
1520 break;
1521
1522 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001523 dev_err(info->device, "Warning: Unknown board type\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001524 break;
1525 }
1526
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001527 /* make sure RAM size set by this point */
1528 assert(info->screen_size > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001529
1530 /* the P4 is not fully initialized here; I rely on it having been */
1531 /* inited under AmigaOS already, which seems to work just fine */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001532 /* (Klaus advised to do it this way) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001533
1534 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001535 WGen(cinfo, CL_VSSM, 0x10); /* EGS: 0x16 */
1536 WGen(cinfo, CL_POS102, 0x01);
1537 WGen(cinfo, CL_VSSM, 0x08); /* EGS: 0x0e */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001538
1539 if (cinfo->btype != BT_SD64)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001540 WGen(cinfo, CL_VSSM2, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001541
Krzysztof Helt8503df62007-10-16 01:29:08 -07001542 /* reset sequencer logic */
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001543 vga_wseq(cinfo->regbase, VGA_SEQ_RESET, 0x03);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001544
Krzysztof Helt8503df62007-10-16 01:29:08 -07001545 /* FullBandwidth (video off) and 8/9 dot clock */
1546 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001547
Krzysztof Helt8503df62007-10-16 01:29:08 -07001548 /* "magic cookie" - doesn't make any sense to me.. */
1549/* vga_wgfx(cinfo->regbase, CL_GRA, 0xce); */
1550 /* unlock all extension registers */
1551 vga_wseq(cinfo->regbase, CL_SEQR6, 0x12);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001552
Linus Torvalds1da177e2005-04-16 15:20:36 -07001553 switch (cinfo->btype) {
1554 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001555 vga_wseq(cinfo->regbase, CL_SEQRF, 0x98);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001556 break;
1557 case BT_ALPINE:
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001558 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001559 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001560 break;
1561 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001562 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001563 break;
1564 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001565 vga_wseq(cinfo->regbase, CL_SEQR16, 0x0f);
1566 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001567 break;
1568 }
1569 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001570 /* plane mask: nothing */
1571 vga_wseq(cinfo->regbase, VGA_SEQ_PLANE_WRITE, 0xff);
1572 /* character map select: doesn't even matter in gx mode */
1573 vga_wseq(cinfo->regbase, VGA_SEQ_CHARACTER_MAP, 0x00);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001574 /* memory mode: chain4, ext. memory */
1575 vga_wseq(cinfo->regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001576
1577 /* controller-internal base address of video memory */
1578 if (bi->init_sr07)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001579 vga_wseq(cinfo->regbase, CL_SEQR7, bi->sr07);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001580
Krzysztof Helt8503df62007-10-16 01:29:08 -07001581 /* vga_wseq(cinfo->regbase, CL_SEQR8, 0x00); */
1582 /* EEPROM control: shouldn't be necessary to write to this at all.. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001583
Krzysztof Helt8503df62007-10-16 01:29:08 -07001584 /* graphics cursor X position (incomplete; position gives rem. 3 bits */
1585 vga_wseq(cinfo->regbase, CL_SEQR10, 0x00);
1586 /* graphics cursor Y position (..."... ) */
1587 vga_wseq(cinfo->regbase, CL_SEQR11, 0x00);
1588 /* graphics cursor attributes */
1589 vga_wseq(cinfo->regbase, CL_SEQR12, 0x00);
1590 /* graphics cursor pattern address */
1591 vga_wseq(cinfo->regbase, CL_SEQR13, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001592
1593 /* writing these on a P4 might give problems.. */
1594 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001595 /* configuration readback and ext. color */
1596 vga_wseq(cinfo->regbase, CL_SEQR17, 0x00);
1597 /* signature generator */
1598 vga_wseq(cinfo->regbase, CL_SEQR18, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001599 }
1600
1601 /* MCLK select etc. */
1602 if (bi->init_sr1f)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001603 vga_wseq(cinfo->regbase, CL_SEQR1F, bi->sr1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001604
Krzysztof Helt8503df62007-10-16 01:29:08 -07001605 /* Screen A preset row scan: none */
1606 vga_wcrt(cinfo->regbase, VGA_CRTC_PRESET_ROW, 0x00);
1607 /* Text cursor start: disable text cursor */
1608 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_START, 0x20);
1609 /* Text cursor end: - */
1610 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_END, 0x00);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001611 /* text cursor location high: 0 */
1612 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_HI, 0x00);
1613 /* text cursor location low: 0 */
1614 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_LO, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001615
Krzysztof Helt8503df62007-10-16 01:29:08 -07001616 /* Underline Row scanline: - */
1617 vga_wcrt(cinfo->regbase, VGA_CRTC_UNDERLINE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001618 /* ### add 0x40 for text modes with > 30 MHz pixclock */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001619 /* ext. display controls: ext.adr. wrap */
1620 vga_wcrt(cinfo->regbase, CL_CRT1B, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001621
Krzysztof Helt8503df62007-10-16 01:29:08 -07001622 /* Set/Reset registes: - */
1623 vga_wgfx(cinfo->regbase, VGA_GFX_SR_VALUE, 0x00);
1624 /* Set/Reset enable: - */
1625 vga_wgfx(cinfo->regbase, VGA_GFX_SR_ENABLE, 0x00);
1626 /* Color Compare: - */
1627 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_VALUE, 0x00);
1628 /* Data Rotate: - */
1629 vga_wgfx(cinfo->regbase, VGA_GFX_DATA_ROTATE, 0x00);
1630 /* Read Map Select: - */
1631 vga_wgfx(cinfo->regbase, VGA_GFX_PLANE_READ, 0x00);
1632 /* Mode: conf. for 16/4/2 color mode, no odd/even, read/write mode 0 */
1633 vga_wgfx(cinfo->regbase, VGA_GFX_MODE, 0x00);
1634 /* Miscellaneous: memory map base address, graphics mode */
1635 vga_wgfx(cinfo->regbase, VGA_GFX_MISC, 0x01);
1636 /* Color Don't care: involve all planes */
1637 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_MASK, 0x0f);
1638 /* Bit Mask: no mask at all */
1639 vga_wgfx(cinfo->regbase, VGA_GFX_BIT_MASK, 0xff);
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001640
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001641 if (cinfo->btype == BT_ALPINE || is_laguna(cinfo))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001642 /* (5434 can't have bit 3 set for bitblt) */
1643 vga_wgfx(cinfo->regbase, CL_GRB, 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001644 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001645 /* Graphics controller mode extensions: finer granularity,
1646 * 8byte data latches
1647 */
1648 vga_wgfx(cinfo->regbase, CL_GRB, 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001649
Krzysztof Helt8503df62007-10-16 01:29:08 -07001650 vga_wgfx(cinfo->regbase, CL_GRC, 0xff); /* Color Key compare: - */
1651 vga_wgfx(cinfo->regbase, CL_GRD, 0x00); /* Color Key compare mask: - */
1652 vga_wgfx(cinfo->regbase, CL_GRE, 0x00); /* Miscellaneous control: - */
1653 /* Background color byte 1: - */
1654 /* vga_wgfx (cinfo->regbase, CL_GR10, 0x00); */
1655 /* vga_wgfx (cinfo->regbase, CL_GR11, 0x00); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001656
Krzysztof Helt8503df62007-10-16 01:29:08 -07001657 /* Attribute Controller palette registers: "identity mapping" */
1658 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE0, 0x00);
1659 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE1, 0x01);
1660 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE2, 0x02);
1661 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE3, 0x03);
1662 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE4, 0x04);
1663 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE5, 0x05);
1664 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE6, 0x06);
1665 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE7, 0x07);
1666 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE8, 0x08);
1667 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE9, 0x09);
1668 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEA, 0x0a);
1669 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEB, 0x0b);
1670 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEC, 0x0c);
1671 vga_wattr(cinfo->regbase, VGA_ATC_PALETTED, 0x0d);
1672 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEE, 0x0e);
1673 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEF, 0x0f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001674
Krzysztof Helt8503df62007-10-16 01:29:08 -07001675 /* Attribute Controller mode: graphics mode */
1676 vga_wattr(cinfo->regbase, VGA_ATC_MODE, 0x01);
1677 /* Overscan color reg.: reg. 0 */
1678 vga_wattr(cinfo->regbase, VGA_ATC_OVERSCAN, 0x00);
1679 /* Color Plane enable: Enable all 4 planes */
1680 vga_wattr(cinfo->regbase, VGA_ATC_PLANE_ENABLE, 0x0f);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001681 /* Color Select: - */
1682 vga_wattr(cinfo->regbase, VGA_ATC_COLOR_PAGE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001683
Krzysztof Helt8503df62007-10-16 01:29:08 -07001684 WGen(cinfo, VGA_PEL_MSK, 0xff); /* Pixel mask: no mask */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001685
Krzysztof Helt8503df62007-10-16 01:29:08 -07001686 /* BLT Start/status: Blitter reset */
1687 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
1688 /* - " - : "end-of-reset" */
1689 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001690
1691 /* misc... */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001692 WHDR(cinfo, 0); /* Hidden DAC register: - */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001693 return;
1694}
1695
Krzysztof Helt8503df62007-10-16 01:29:08 -07001696static void switch_monitor(struct cirrusfb_info *cinfo, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001697{
1698#ifdef CONFIG_ZORRO /* only works on Zorro boards */
1699 static int IsOn = 0; /* XXX not ok for multiple boards */
1700
Linus Torvalds1da177e2005-04-16 15:20:36 -07001701 if (cinfo->btype == BT_PICASSO4)
1702 return; /* nothing to switch */
1703 if (cinfo->btype == BT_ALPINE)
1704 return; /* nothing to switch */
1705 if (cinfo->btype == BT_GD5480)
1706 return; /* nothing to switch */
1707 if (cinfo->btype == BT_PICASSO) {
1708 if ((on && !IsOn) || (!on && IsOn))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001709 WSFR(cinfo, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001710 return;
1711 }
1712 if (on) {
1713 switch (cinfo->btype) {
1714 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001715 WSFR(cinfo, cinfo->SFR | 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001716 break;
1717 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001718 WSFR(cinfo, cinfo->SFR | 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001719 break;
1720 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001721 WSFR(cinfo, 0x6f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001722 break;
1723 default: /* do nothing */ break;
1724 }
1725 } else {
1726 switch (cinfo->btype) {
1727 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001728 WSFR(cinfo, cinfo->SFR & 0xde);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001729 break;
1730 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001731 WSFR(cinfo, cinfo->SFR & 0xd7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001732 break;
1733 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001734 WSFR(cinfo, 0x4f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001735 break;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001736 default: /* do nothing */
1737 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001738 }
1739 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001740#endif /* CONFIG_ZORRO */
1741}
1742
Linus Torvalds1da177e2005-04-16 15:20:36 -07001743/******************************************/
1744/* Linux 2.6-style accelerated functions */
1745/******************************************/
1746
Krzysztof Helt8343c892009-03-31 15:25:11 -07001747static int cirrusfb_sync(struct fb_info *info)
1748{
1749 struct cirrusfb_info *cinfo = info->par;
1750
1751 if (!is_laguna(cinfo)) {
1752 while (vga_rgfx(cinfo->regbase, CL_GR31) & 0x03)
1753 cpu_relax();
1754 }
1755 return 0;
1756}
1757
Krzysztof Helt8503df62007-10-16 01:29:08 -07001758static void cirrusfb_fillrect(struct fb_info *info,
1759 const struct fb_fillrect *region)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001760{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001761 struct fb_fillrect modded;
1762 int vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001763 struct cirrusfb_info *cinfo = info->par;
1764 int m = info->var.bits_per_pixel;
1765 u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ?
1766 cinfo->pseudo_palette[region->color] : region->color;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001767
1768 if (info->state != FBINFO_STATE_RUNNING)
1769 return;
1770 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1771 cfb_fillrect(info, region);
1772 return;
1773 }
1774
1775 vxres = info->var.xres_virtual;
1776 vyres = info->var.yres_virtual;
1777
1778 memcpy(&modded, region, sizeof(struct fb_fillrect));
1779
Krzysztof Helt8503df62007-10-16 01:29:08 -07001780 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001781 modded.dx >= vxres || modded.dy >= vyres)
1782 return;
1783
Krzysztof Helt8503df62007-10-16 01:29:08 -07001784 if (modded.dx + modded.width > vxres)
1785 modded.width = vxres - modded.dx;
1786 if (modded.dy + modded.height > vyres)
1787 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001788
Krzysztof Helt060b6002007-10-16 01:29:13 -07001789 cirrusfb_RectFill(cinfo->regbase,
1790 info->var.bits_per_pixel,
1791 (region->dx * m) / 8, region->dy,
1792 (region->width * m) / 8, region->height,
1793 color,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001794 info->fix.line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001795}
1796
Krzysztof Helt8503df62007-10-16 01:29:08 -07001797static void cirrusfb_copyarea(struct fb_info *info,
1798 const struct fb_copyarea *area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001799{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001800 struct fb_copyarea modded;
1801 u32 vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001802 struct cirrusfb_info *cinfo = info->par;
1803 int m = info->var.bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001804
1805 if (info->state != FBINFO_STATE_RUNNING)
1806 return;
1807 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1808 cfb_copyarea(info, area);
1809 return;
1810 }
1811
1812 vxres = info->var.xres_virtual;
1813 vyres = info->var.yres_virtual;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001814 memcpy(&modded, area, sizeof(struct fb_copyarea));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001815
Krzysztof Helt8503df62007-10-16 01:29:08 -07001816 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001817 modded.sx >= vxres || modded.sy >= vyres ||
1818 modded.dx >= vxres || modded.dy >= vyres)
1819 return;
1820
Krzysztof Helt8503df62007-10-16 01:29:08 -07001821 if (modded.sx + modded.width > vxres)
1822 modded.width = vxres - modded.sx;
1823 if (modded.dx + modded.width > vxres)
1824 modded.width = vxres - modded.dx;
1825 if (modded.sy + modded.height > vyres)
1826 modded.height = vyres - modded.sy;
1827 if (modded.dy + modded.height > vyres)
1828 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001829
Krzysztof Helt060b6002007-10-16 01:29:13 -07001830 cirrusfb_BitBLT(cinfo->regbase, info->var.bits_per_pixel,
1831 (area->sx * m) / 8, area->sy,
1832 (area->dx * m) / 8, area->dy,
1833 (area->width * m) / 8, area->height,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001834 info->fix.line_length);
Krzysztof Helt060b6002007-10-16 01:29:13 -07001835
Linus Torvalds1da177e2005-04-16 15:20:36 -07001836}
1837
Krzysztof Helt8503df62007-10-16 01:29:08 -07001838static void cirrusfb_imageblit(struct fb_info *info,
1839 const struct fb_image *image)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001840{
1841 struct cirrusfb_info *cinfo = info->par;
1842
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001843 if (!is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001844 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001845 cfb_imageblit(info, image);
1846}
1847
Linus Torvalds1da177e2005-04-16 15:20:36 -07001848#ifdef CONFIG_PPC_PREP
1849#define PREP_VIDEO_BASE ((volatile unsigned long) 0xC0000000)
1850#define PREP_IO_BASE ((volatile unsigned char *) 0x80000000)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001851static void get_prep_addrs(unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001852{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001853 *display = PREP_VIDEO_BASE;
1854 *registers = (unsigned long) PREP_IO_BASE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001855}
1856
1857#endif /* CONFIG_PPC_PREP */
1858
Linus Torvalds1da177e2005-04-16 15:20:36 -07001859#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001860static int release_io_ports;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001861
1862/* Pulled the logic from XFree86 Cirrus driver to get the memory size,
1863 * based on the DRAM bandwidth bit and DRAM bank switching bit. This
1864 * works with 1MB, 2MB and 4MB configurations (which the Motorola boards
1865 * seem to have. */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001866static unsigned int __devinit cirrusfb_get_memsize(struct fb_info *info,
1867 u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001868{
1869 unsigned long mem;
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001870 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001871
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001872 if (is_laguna(cinfo)) {
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001873 unsigned char SR14 = vga_rseq(regbase, CL_SEQR14);
1874
1875 mem = ((SR14 & 7) + 1) << 20;
1876 } else {
1877 unsigned char SRF = vga_rseq(regbase, CL_SEQRF);
1878 switch ((SRF & 0x18)) {
1879 case 0x08:
1880 mem = 512 * 1024;
1881 break;
1882 case 0x10:
1883 mem = 1024 * 1024;
1884 break;
1885 /* 64-bit DRAM data bus width; assume 2MB.
1886 * Also indicates 2MB memory on the 5430.
1887 */
1888 case 0x18:
1889 mem = 2048 * 1024;
1890 break;
1891 default:
1892 dev_warn(info->device, "Unknown memory size!\n");
1893 mem = 1024 * 1024;
1894 }
1895 /* If DRAM bank switching is enabled, there must be
1896 * twice as much memory installed. (4MB on the 5434)
1897 */
1898 if (SRF & 0x80)
1899 mem *= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001900 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001901
Linus Torvalds1da177e2005-04-16 15:20:36 -07001902 /* TODO: Handling of GD5446/5480 (see XF86 sources ...) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001903 return mem;
1904}
1905
Krzysztof Helt8503df62007-10-16 01:29:08 -07001906static void get_pci_addrs(const struct pci_dev *pdev,
1907 unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001908{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001909 assert(pdev != NULL);
1910 assert(display != NULL);
1911 assert(registers != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001912
Linus Torvalds1da177e2005-04-16 15:20:36 -07001913 *display = 0;
1914 *registers = 0;
1915
1916 /* This is a best-guess for now */
1917
1918 if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
1919 *display = pci_resource_start(pdev, 1);
1920 *registers = pci_resource_start(pdev, 0);
1921 } else {
1922 *display = pci_resource_start(pdev, 0);
1923 *registers = pci_resource_start(pdev, 1);
1924 }
1925
Krzysztof Helt8503df62007-10-16 01:29:08 -07001926 assert(*display != 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001927}
1928
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001929static void cirrusfb_pci_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001930{
Krzysztof Helt64beab12008-10-15 22:03:38 -07001931 struct pci_dev *pdev = to_pci_dev(info->device);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001932 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001933
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001934 if (cinfo->laguna_mmio == NULL)
1935 iounmap(cinfo->laguna_mmio);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001936 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001937#if 0 /* if system didn't claim this region, we would... */
1938 release_mem_region(0xA0000, 65535);
1939#endif
1940 if (release_io_ports)
1941 release_region(0x3C0, 32);
1942 pci_release_regions(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001943}
1944#endif /* CONFIG_PCI */
1945
Linus Torvalds1da177e2005-04-16 15:20:36 -07001946#ifdef CONFIG_ZORRO
Al Virof5ee0512008-11-01 18:20:39 +00001947static void cirrusfb_zorro_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001948{
Al Virod91f5bb2007-10-17 00:27:18 +01001949 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt64beab12008-10-15 22:03:38 -07001950 struct zorro_dev *zdev = to_zorro_dev(info->device);
1951
1952 zorro_release_device(zdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001953
1954 if (cinfo->btype == BT_PICASSO4) {
1955 cinfo->regbase -= 0x600000;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001956 iounmap((void *)cinfo->regbase);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001957 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001958 } else {
Krzysztof Helt64beab12008-10-15 22:03:38 -07001959 if (zorro_resource_start(zdev) > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001960 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001961 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001962}
1963#endif /* CONFIG_ZORRO */
1964
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001965/* function table of the above functions */
1966static struct fb_ops cirrusfb_ops = {
1967 .owner = THIS_MODULE,
1968 .fb_open = cirrusfb_open,
1969 .fb_release = cirrusfb_release,
1970 .fb_setcolreg = cirrusfb_setcolreg,
1971 .fb_check_var = cirrusfb_check_var,
1972 .fb_set_par = cirrusfb_set_par,
1973 .fb_pan_display = cirrusfb_pan_display,
1974 .fb_blank = cirrusfb_blank,
1975 .fb_fillrect = cirrusfb_fillrect,
1976 .fb_copyarea = cirrusfb_copyarea,
Krzysztof Helt8343c892009-03-31 15:25:11 -07001977 .fb_sync = cirrusfb_sync,
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001978 .fb_imageblit = cirrusfb_imageblit,
1979};
1980
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07001981static int __devinit cirrusfb_set_fbinfo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001982{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001983 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001984 struct fb_var_screeninfo *var = &info->var;
1985
Linus Torvalds1da177e2005-04-16 15:20:36 -07001986 info->pseudo_palette = cinfo->pseudo_palette;
1987 info->flags = FBINFO_DEFAULT
1988 | FBINFO_HWACCEL_XPAN
1989 | FBINFO_HWACCEL_YPAN
1990 | FBINFO_HWACCEL_FILLRECT
1991 | FBINFO_HWACCEL_COPYAREA;
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001992 if (noaccel || is_laguna(cinfo))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001993 info->flags |= FBINFO_HWACCEL_DISABLED;
1994 info->fbops = &cirrusfb_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001995 if (cinfo->btype == BT_GD5480) {
1996 if (var->bits_per_pixel == 16)
1997 info->screen_base += 1 * MB_;
Krzysztof Helt1cea9a92008-10-15 22:03:37 -07001998 if (var->bits_per_pixel == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001999 info->screen_base += 2 * MB_;
2000 }
2001
2002 /* Fill fix common fields */
2003 strlcpy(info->fix.id, cirrusfb_board_info[cinfo->btype].name,
2004 sizeof(info->fix.id));
2005
2006 /* monochrome: only 1 memory plane */
2007 /* 8 bit and above: Use whole memory area */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002008 info->fix.smem_len = info->screen_size;
2009 if (var->bits_per_pixel == 1)
2010 info->fix.smem_len /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002011 info->fix.type_aux = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002012 info->fix.xpanstep = 1;
2013 info->fix.ypanstep = 1;
2014 info->fix.ywrapstep = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002015
2016 /* FIXME: map region at 0xB8000 if available, fill in here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002017 info->fix.mmio_len = 0;
2018 info->fix.accel = FB_ACCEL_NONE;
2019
2020 fb_alloc_cmap(&info->cmap, 256, 0);
2021
2022 return 0;
2023}
2024
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002025static int __devinit cirrusfb_register(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002026{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002027 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002028 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002029
2030 /* sanity checks */
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002031 assert(cinfo->btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002032
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002033 /* set all the vital stuff */
2034 cirrusfb_set_fbinfo(info);
2035
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002036 dev_dbg(info->device, "(RAM start set to: 0x%p)\n", info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002037
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002038 err = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8);
2039 if (!err) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002040 dev_dbg(info->device, "wrong initial video mode\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002041 err = -EINVAL;
2042 goto err_dealloc_cmap;
2043 }
2044
Linus Torvalds1da177e2005-04-16 15:20:36 -07002045 info->var.activate = FB_ACTIVATE_NOW;
2046
Krzysztof Helt99a45842009-03-31 15:25:09 -07002047 err = cirrusfb_check_var(&info->var, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002048 if (err < 0) {
2049 /* should never happen */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002050 dev_dbg(info->device,
2051 "choking on default var... umm, no good.\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002052 goto err_dealloc_cmap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002053 }
2054
Linus Torvalds1da177e2005-04-16 15:20:36 -07002055 err = register_framebuffer(info);
2056 if (err < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002057 dev_err(info->device,
2058 "could not register fb device; err = %d!\n", err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002059 goto err_dealloc_cmap;
2060 }
2061
Linus Torvalds1da177e2005-04-16 15:20:36 -07002062 return 0;
2063
2064err_dealloc_cmap:
2065 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002066 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002067 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002068 return err;
2069}
2070
Krzysztof Helt8503df62007-10-16 01:29:08 -07002071static void __devexit cirrusfb_cleanup(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002072{
2073 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002074
Krzysztof Helt8503df62007-10-16 01:29:08 -07002075 switch_monitor(cinfo, 0);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002076 unregister_framebuffer(info);
2077 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002078 dev_dbg(info->device, "Framebuffer unregistered\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002079 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002080 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002081}
2082
Linus Torvalds1da177e2005-04-16 15:20:36 -07002083#ifdef CONFIG_PCI
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002084static int __devinit cirrusfb_pci_register(struct pci_dev *pdev,
2085 const struct pci_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002086{
2087 struct cirrusfb_info *cinfo;
2088 struct fb_info *info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002089 unsigned long board_addr, board_size;
2090 int ret;
2091
2092 ret = pci_enable_device(pdev);
2093 if (ret < 0) {
2094 printk(KERN_ERR "cirrusfb: Cannot enable PCI device\n");
2095 goto err_out;
2096 }
2097
2098 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &pdev->dev);
2099 if (!info) {
2100 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
2101 ret = -ENOMEM;
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002102 goto err_out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002103 }
2104
2105 cinfo = info->par;
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002106 cinfo->btype = (enum cirrus_board) ent->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002107
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002108 dev_dbg(info->device,
2109 " Found PCI device, base address 0 is 0x%Lx, btype set to %d\n",
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002110 (unsigned long long)pdev->resource[0].start, cinfo->btype);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002111 dev_dbg(info->device, " base address 1 is 0x%Lx\n",
2112 (unsigned long long)pdev->resource[1].start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002113
Krzysztof Helt8503df62007-10-16 01:29:08 -07002114 if (isPReP) {
2115 pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, 0x00000000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002116#ifdef CONFIG_PPC_PREP
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002117 get_prep_addrs(&board_addr, &info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002118#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07002119 /* PReP dies if we ioremap the IO registers, but it works w/out... */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002120 cinfo->regbase = (char __iomem *) info->fix.mmio_start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002121 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002122 dev_dbg(info->device,
2123 "Attempt to get PCI info for Cirrus Graphics Card\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002124 get_pci_addrs(pdev, &board_addr, &info->fix.mmio_start);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002125 /* FIXME: this forces VGA. alternatives? */
2126 cinfo->regbase = NULL;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002127 cinfo->laguna_mmio = ioremap(info->fix.mmio_start, 0x1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002128 }
2129
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002130 dev_dbg(info->device, "Board address: 0x%lx, register address: 0x%lx\n",
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002131 board_addr, info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002132
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002133 board_size = (cinfo->btype == BT_GD5480) ?
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002134 32 * MB_ : cirrusfb_get_memsize(info, cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002135
2136 ret = pci_request_regions(pdev, "cirrusfb");
Krzysztof Helt8503df62007-10-16 01:29:08 -07002137 if (ret < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002138 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2139 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002140 goto err_release_fb;
2141 }
2142#if 0 /* if the system didn't claim this region, we would... */
2143 if (!request_mem_region(0xA0000, 65535, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002144 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2145 0xA0000L);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002146 ret = -EBUSY;
2147 goto err_release_regions;
2148 }
2149#endif
2150 if (request_region(0x3C0, 32, "cirrusfb"))
2151 release_io_ports = 1;
2152
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002153 info->screen_base = ioremap(board_addr, board_size);
2154 if (!info->screen_base) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002155 ret = -EIO;
2156 goto err_release_legacy;
2157 }
2158
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002159 info->fix.smem_start = board_addr;
2160 info->screen_size = board_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002161 cinfo->unmap = cirrusfb_pci_unmap;
2162
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002163 dev_info(info->device,
2164 "Cirrus Logic chipset on PCI bus, RAM (%lu kB) at 0x%lx\n",
2165 info->screen_size >> 10, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002166 pci_set_drvdata(pdev, info);
2167
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002168 ret = cirrusfb_register(info);
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002169 if (!ret)
2170 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002171
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002172 pci_set_drvdata(pdev, NULL);
2173 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002174err_release_legacy:
2175 if (release_io_ports)
2176 release_region(0x3C0, 32);
2177#if 0
2178 release_mem_region(0xA0000, 65535);
2179err_release_regions:
2180#endif
2181 pci_release_regions(pdev);
2182err_release_fb:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002183 if (cinfo->laguna_mmio != NULL)
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002184 iounmap(cinfo->laguna_mmio);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002185 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002186err_out:
2187 return ret;
2188}
2189
Krzysztof Helt8503df62007-10-16 01:29:08 -07002190static void __devexit cirrusfb_pci_unregister(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002191{
2192 struct fb_info *info = pci_get_drvdata(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002193
Krzysztof Helt8503df62007-10-16 01:29:08 -07002194 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002195}
2196
2197static struct pci_driver cirrusfb_pci_driver = {
2198 .name = "cirrusfb",
2199 .id_table = cirrusfb_pci_table,
2200 .probe = cirrusfb_pci_register,
2201 .remove = __devexit_p(cirrusfb_pci_unregister),
2202#ifdef CONFIG_PM
2203#if 0
2204 .suspend = cirrusfb_pci_suspend,
2205 .resume = cirrusfb_pci_resume,
2206#endif
2207#endif
2208};
2209#endif /* CONFIG_PCI */
2210
Linus Torvalds1da177e2005-04-16 15:20:36 -07002211#ifdef CONFIG_ZORRO
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002212static int __devinit cirrusfb_zorro_register(struct zorro_dev *z,
2213 const struct zorro_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002214{
2215 struct cirrusfb_info *cinfo;
2216 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002217 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002218 struct zorro_dev *z2 = NULL;
2219 unsigned long board_addr, board_size, size;
2220 int ret;
2221
2222 btype = ent->driver_data;
2223 if (cirrusfb_zorro_table2[btype].id2)
2224 z2 = zorro_find_device(cirrusfb_zorro_table2[btype].id2, NULL);
2225 size = cirrusfb_zorro_table2[btype].size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002226
2227 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &z->dev);
2228 if (!info) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002229 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002230 ret = -ENOMEM;
2231 goto err_out;
2232 }
2233
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002234 dev_info(info->device, "%s board detected\n",
2235 cirrusfb_board_info[btype].name);
2236
Linus Torvalds1da177e2005-04-16 15:20:36 -07002237 cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002238 cinfo->btype = btype;
2239
Al Viro36ea96a2007-10-27 19:46:58 +01002240 assert(z);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002241 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002242
Linus Torvalds1da177e2005-04-16 15:20:36 -07002243 board_addr = zorro_resource_start(z);
2244 board_size = zorro_resource_len(z);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002245 info->screen_size = size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002246
2247 if (!zorro_request_device(z, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002248 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2249 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002250 ret = -EBUSY;
2251 goto err_release_fb;
2252 }
2253
Linus Torvalds1da177e2005-04-16 15:20:36 -07002254 ret = -EIO;
2255
2256 if (btype == BT_PICASSO4) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002257 dev_info(info->device, " REG at $%lx\n", board_addr + 0x600000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002258
2259 /* To be precise, for the P4 this is not the */
2260 /* begin of the board, but the begin of RAM. */
2261 /* for P4, map in its address space in 2 chunks (### TEST! ) */
2262 /* (note the ugly hardcoded 16M number) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002263 cinfo->regbase = ioremap(board_addr, 16777216);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002264 if (!cinfo->regbase)
2265 goto err_release_region;
2266
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002267 dev_dbg(info->device, "Virtual address for board set to: $%p\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002268 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002269 cinfo->regbase += 0x600000;
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002270 info->fix.mmio_start = board_addr + 0x600000;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002271
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002272 info->fix.smem_start = board_addr + 16777216;
2273 info->screen_base = ioremap(info->fix.smem_start, 16777216);
2274 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002275 goto err_unmap_regbase;
2276 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002277 dev_info(info->device, " REG at $%lx\n",
2278 (unsigned long) z2->resource.start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002279
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002280 info->fix.smem_start = board_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002281 if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002282 info->screen_base = ioremap(board_addr, board_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002283 else
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002284 info->screen_base = (caddr_t) ZTWO_VADDR(board_addr);
2285 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002286 goto err_release_region;
2287
2288 /* set address for REG area of board */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002289 cinfo->regbase = (caddr_t) ZTWO_VADDR(z2->resource.start);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002290 info->fix.mmio_start = z2->resource.start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002291
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002292 dev_dbg(info->device, "Virtual address for board set to: $%p\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002293 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002294 }
2295 cinfo->unmap = cirrusfb_zorro_unmap;
2296
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002297 dev_info(info->device,
2298 "Cirrus Logic chipset on Zorro bus, RAM (%lu MB) at $%lx\n",
2299 board_size / MB_, board_addr);
2300
Linus Torvalds1da177e2005-04-16 15:20:36 -07002301 zorro_set_drvdata(z, info);
2302
Al Virod91f5bb2007-10-17 00:27:18 +01002303 ret = cirrusfb_register(info);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002304 if (ret) {
2305 if (btype == BT_PICASSO4) {
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002306 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002307 iounmap(cinfo->regbase - 0x600000);
2308 } else if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002309 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002310 }
2311 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002312
2313err_unmap_regbase:
2314 /* Parental advisory: explicit hack */
2315 iounmap(cinfo->regbase - 0x600000);
2316err_release_region:
2317 release_region(board_addr, board_size);
2318err_release_fb:
2319 framebuffer_release(info);
2320err_out:
2321 return ret;
2322}
2323
2324void __devexit cirrusfb_zorro_unregister(struct zorro_dev *z)
2325{
2326 struct fb_info *info = zorro_get_drvdata(z);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002327
Krzysztof Helt8503df62007-10-16 01:29:08 -07002328 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002329}
2330
2331static struct zorro_driver cirrusfb_zorro_driver = {
2332 .name = "cirrusfb",
2333 .id_table = cirrusfb_zorro_table,
2334 .probe = cirrusfb_zorro_register,
2335 .remove = __devexit_p(cirrusfb_zorro_unregister),
2336};
2337#endif /* CONFIG_ZORRO */
2338
Linus Torvalds1da177e2005-04-16 15:20:36 -07002339#ifndef MODULE
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002340static int __init cirrusfb_setup(char *options)
2341{
Vlada Pericee119402008-11-19 15:36:45 -08002342 char *this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002343
Linus Torvalds1da177e2005-04-16 15:20:36 -07002344 if (!options || !*options)
2345 return 0;
2346
Krzysztof Helt8503df62007-10-16 01:29:08 -07002347 while ((this_opt = strsep(&options, ",")) != NULL) {
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002348 if (!*this_opt)
2349 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002350
Linus Torvalds1da177e2005-04-16 15:20:36 -07002351 if (!strcmp(this_opt, "noaccel"))
2352 noaccel = 1;
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002353 else if (!strncmp(this_opt, "mode:", 5))
2354 mode_option = this_opt + 5;
2355 else
2356 mode_option = this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002357 }
2358 return 0;
2359}
2360#endif
2361
Linus Torvalds1da177e2005-04-16 15:20:36 -07002362 /*
2363 * Modularization
2364 */
2365
2366MODULE_AUTHOR("Copyright 1999,2000 Jeff Garzik <jgarzik@pobox.com>");
2367MODULE_DESCRIPTION("Accelerated FBDev driver for Cirrus Logic chips");
2368MODULE_LICENSE("GPL");
2369
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002370static int __init cirrusfb_init(void)
2371{
2372 int error = 0;
2373
2374#ifndef MODULE
2375 char *option = NULL;
2376
2377 if (fb_get_options("cirrusfb", &option))
2378 return -ENODEV;
2379 cirrusfb_setup(option);
2380#endif
2381
2382#ifdef CONFIG_ZORRO
2383 error |= zorro_register_driver(&cirrusfb_zorro_driver);
2384#endif
2385#ifdef CONFIG_PCI
2386 error |= pci_register_driver(&cirrusfb_pci_driver);
2387#endif
2388 return error;
2389}
2390
Krzysztof Helt8503df62007-10-16 01:29:08 -07002391static void __exit cirrusfb_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002392{
2393#ifdef CONFIG_PCI
2394 pci_unregister_driver(&cirrusfb_pci_driver);
2395#endif
2396#ifdef CONFIG_ZORRO
2397 zorro_unregister_driver(&cirrusfb_zorro_driver);
2398#endif
2399}
2400
2401module_init(cirrusfb_init);
2402
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002403module_param(mode_option, charp, 0);
2404MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");
Krzysztof Helt55a0dd82008-10-15 22:03:41 -07002405module_param(noaccel, bool, 0);
2406MODULE_PARM_DESC(noaccel, "Disable acceleration");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002407
Linus Torvalds1da177e2005-04-16 15:20:36 -07002408#ifdef MODULE
2409module_exit(cirrusfb_exit);
2410#endif
2411
Linus Torvalds1da177e2005-04-16 15:20:36 -07002412/**********************************************************************/
2413/* about the following functions - I have used the same names for the */
2414/* functions as Markus Wild did in his Retina driver for NetBSD as */
2415/* they just made sense for this purpose. Apart from that, I wrote */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002416/* these functions myself. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002417/**********************************************************************/
2418
2419/*** WGen() - write into one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002420static void WGen(const struct cirrusfb_info *cinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002421 int regnum, unsigned char val)
2422{
2423 unsigned long regofs = 0;
2424
2425 if (cinfo->btype == BT_PICASSO) {
2426 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002427/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2428 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002429 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2430 regofs = 0xfff;
2431 }
2432
Krzysztof Helt8503df62007-10-16 01:29:08 -07002433 vga_w(cinfo->regbase, regofs + regnum, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002434}
2435
2436/*** RGen() - read out one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002437static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002438{
2439 unsigned long regofs = 0;
2440
2441 if (cinfo->btype == BT_PICASSO) {
2442 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002443/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2444 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002445 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2446 regofs = 0xfff;
2447 }
2448
Krzysztof Helt8503df62007-10-16 01:29:08 -07002449 return vga_r(cinfo->regbase, regofs + regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002450}
2451
2452/*** AttrOn() - turn on VideoEnable for Attribute controller ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002453static void AttrOn(const struct cirrusfb_info *cinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002454{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002455 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002456
Krzysztof Helt8503df62007-10-16 01:29:08 -07002457 if (vga_rcrt(cinfo->regbase, CL_CRT24) & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002458 /* if we're just in "write value" mode, write back the */
2459 /* same value as before to not modify anything */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002460 vga_w(cinfo->regbase, VGA_ATT_IW,
2461 vga_r(cinfo->regbase, VGA_ATT_R));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002462 }
2463 /* turn on video bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002464/* vga_w(cinfo->regbase, VGA_ATT_IW, 0x20); */
2465 vga_w(cinfo->regbase, VGA_ATT_IW, 0x33);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002466
2467 /* dummy write on Reg0 to be on "write index" mode next time */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002468 vga_w(cinfo->regbase, VGA_ATT_IW, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002469}
2470
2471/*** WHDR() - write into the Hidden DAC register ***/
2472/* as the HDR is the only extension register that requires special treatment
2473 * (the other extension registers are accessible just like the "ordinary"
2474 * registers of their functional group) here is a specialized routine for
2475 * accessing the HDR
2476 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002477static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002478{
2479 unsigned char dummy;
2480
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002481 if (is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07002482 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002483 if (cinfo->btype == BT_PICASSO) {
2484 /* Klaus' hint for correct access to HDR on some boards */
2485 /* first write 0 to pixel mask (3c6) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002486 WGen(cinfo, VGA_PEL_MSK, 0x00);
2487 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002488 /* next read dummy from pixel address (3c8) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002489 dummy = RGen(cinfo, VGA_PEL_IW);
2490 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002491 }
2492 /* now do the usual stuff to access the HDR */
2493
Krzysztof Helt8503df62007-10-16 01:29:08 -07002494 dummy = RGen(cinfo, VGA_PEL_MSK);
2495 udelay(200);
2496 dummy = RGen(cinfo, VGA_PEL_MSK);
2497 udelay(200);
2498 dummy = RGen(cinfo, VGA_PEL_MSK);
2499 udelay(200);
2500 dummy = RGen(cinfo, VGA_PEL_MSK);
2501 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002502
Krzysztof Helt8503df62007-10-16 01:29:08 -07002503 WGen(cinfo, VGA_PEL_MSK, val);
2504 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002505
2506 if (cinfo->btype == BT_PICASSO) {
2507 /* now first reset HDR access counter */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002508 dummy = RGen(cinfo, VGA_PEL_IW);
2509 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002510
2511 /* and at the end, restore the mask value */
2512 /* ## is this mask always 0xff? */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002513 WGen(cinfo, VGA_PEL_MSK, 0xff);
2514 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002515 }
2516}
2517
Linus Torvalds1da177e2005-04-16 15:20:36 -07002518/*** WSFR() - write to the "special function register" (SFR) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002519static void WSFR(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002520{
2521#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07002522 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002523 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002524 z_writeb(val, cinfo->regbase + 0x8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002525#endif
2526}
2527
2528/* The Picasso has a second register for switching the monitor bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002529static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002530{
2531#ifdef CONFIG_ZORRO
2532 /* writing an arbitrary value to this one causes the monitor switcher */
2533 /* to flip to Amiga display */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002534 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002535 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002536 z_writeb(val, cinfo->regbase + 0x9000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002537#endif
2538}
2539
Linus Torvalds1da177e2005-04-16 15:20:36 -07002540/*** WClut - set CLUT entry (range: 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002541static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002542 unsigned char green, unsigned char blue)
2543{
2544 unsigned int data = VGA_PEL_D;
2545
2546 /* address write mode register is not translated.. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002547 vga_w(cinfo->regbase, VGA_PEL_IW, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002548
2549 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07002550 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480 ||
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002551 is_laguna(cinfo)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002552 /* but DAC data register IS, at least for Picasso II */
2553 if (cinfo->btype == BT_PICASSO)
2554 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002555 vga_w(cinfo->regbase, data, red);
2556 vga_w(cinfo->regbase, data, green);
2557 vga_w(cinfo->regbase, data, blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002558 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002559 vga_w(cinfo->regbase, data, blue);
2560 vga_w(cinfo->regbase, data, green);
2561 vga_w(cinfo->regbase, data, red);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002562 }
2563}
2564
Linus Torvalds1da177e2005-04-16 15:20:36 -07002565#if 0
2566/*** RClut - read CLUT entry (range 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002567static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char *red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002568 unsigned char *green, unsigned char *blue)
2569{
2570 unsigned int data = VGA_PEL_D;
2571
Krzysztof Helt8503df62007-10-16 01:29:08 -07002572 vga_w(cinfo->regbase, VGA_PEL_IR, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002573
2574 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2575 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2576 if (cinfo->btype == BT_PICASSO)
2577 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002578 *red = vga_r(cinfo->regbase, data);
2579 *green = vga_r(cinfo->regbase, data);
2580 *blue = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002581 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002582 *blue = vga_r(cinfo->regbase, data);
2583 *green = vga_r(cinfo->regbase, data);
2584 *red = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002585 }
2586}
2587#endif
2588
Linus Torvalds1da177e2005-04-16 15:20:36 -07002589/*******************************************************************
2590 cirrusfb_WaitBLT()
2591
2592 Wait for the BitBLT engine to complete a possible earlier job
2593*********************************************************************/
2594
2595/* FIXME: use interrupts instead */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002596static void cirrusfb_WaitBLT(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002597{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002598 while (vga_rgfx(regbase, CL_GR31) & 0x08)
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002599 cpu_relax();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002600}
2601
2602/*******************************************************************
2603 cirrusfb_BitBLT()
2604
2605 perform accelerated "scrolling"
2606********************************************************************/
2607
Krzysztof Helt8343c892009-03-31 15:25:11 -07002608static void cirrusfb_set_blitter(u8 __iomem *regbase,
2609 u_short nwidth, u_short nheight,
2610 u_long nsrc, u_long ndest,
2611 u_short bltmode, u_short line_length)
2612
Linus Torvalds1da177e2005-04-16 15:20:36 -07002613{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002614 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002615 /* dest pitch low */
2616 vga_wgfx(regbase, CL_GR24, line_length & 0xff);
2617 /* dest pitch hi */
2618 vga_wgfx(regbase, CL_GR25, line_length >> 8);
2619 /* source pitch low */
2620 vga_wgfx(regbase, CL_GR26, line_length & 0xff);
2621 /* source pitch hi */
2622 vga_wgfx(regbase, CL_GR27, line_length >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002623
2624 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002625 /* BLT width low */
2626 vga_wgfx(regbase, CL_GR20, nwidth & 0xff);
2627 /* BLT width hi */
2628 vga_wgfx(regbase, CL_GR21, nwidth >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002629
2630 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002631 /* BLT height low */
2632 vga_wgfx(regbase, CL_GR22, nheight & 0xff);
2633 /* BLT width hi */
2634 vga_wgfx(regbase, CL_GR23, nheight >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002635
2636 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002637 /* BLT dest low */
2638 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2639 /* BLT dest mid */
2640 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2641 /* BLT dest hi */
2642 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002643
2644 /* BLT source */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002645 /* BLT src low */
2646 vga_wgfx(regbase, CL_GR2C, (u_char) (nsrc & 0xff));
2647 /* BLT src mid */
2648 vga_wgfx(regbase, CL_GR2D, (u_char) (nsrc >> 8));
2649 /* BLT src hi */
2650 vga_wgfx(regbase, CL_GR2E, (u_char) (nsrc >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002651
2652 /* BLT mode */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002653 vga_wgfx(regbase, CL_GR30, bltmode); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002654
2655 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002656 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002657
2658 /* and finally: GO! */
Krzysztof Helt8343c892009-03-31 15:25:11 -07002659 vga_wgfx(regbase, CL_GR31, 0x82); /* BLT Start/status */
2660}
2661
2662/*******************************************************************
2663 cirrusfb_BitBLT()
2664
2665 perform accelerated "scrolling"
2666********************************************************************/
2667
2668static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
2669 u_short curx, u_short cury,
2670 u_short destx, u_short desty,
2671 u_short width, u_short height,
2672 u_short line_length)
2673{
2674 u_short nwidth = width - 1;
2675 u_short nheight = height - 1;
2676 u_long nsrc, ndest;
2677 u_char bltmode;
2678
2679 bltmode = 0x00;
2680 /* if source adr < dest addr, do the Blt backwards */
2681 if (cury <= desty) {
2682 if (cury == desty) {
2683 /* if src and dest are on the same line, check x */
2684 if (curx < destx)
2685 bltmode |= 0x01;
2686 } else
2687 bltmode |= 0x01;
2688 }
2689 /* standard case: forward blitting */
2690 nsrc = (cury * line_length) + curx;
2691 ndest = (desty * line_length) + destx;
2692 if (bltmode) {
2693 /* this means start addresses are at the end,
2694 * counting backwards
2695 */
2696 nsrc += nheight * line_length + nwidth;
2697 ndest += nheight * line_length + nwidth;
2698 }
2699
2700 cirrusfb_WaitBLT(regbase);
2701
2702 cirrusfb_set_blitter(regbase, nwidth, nheight,
2703 nsrc, ndest, bltmode, line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002704}
2705
Linus Torvalds1da177e2005-04-16 15:20:36 -07002706/*******************************************************************
2707 cirrusfb_RectFill()
2708
2709 perform accelerated rectangle fill
2710********************************************************************/
2711
Krzysztof Helt8503df62007-10-16 01:29:08 -07002712static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002713 u_short x, u_short y, u_short width, u_short height,
Krzysztof Helt8343c892009-03-31 15:25:11 -07002714 u32 color, u_short line_length)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002715{
Krzysztof Helt8343c892009-03-31 15:25:11 -07002716 u_long ndest = (y * line_length) + x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002717 u_char op;
2718
Krzysztof Helt8503df62007-10-16 01:29:08 -07002719 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002720
Linus Torvalds1da177e2005-04-16 15:20:36 -07002721 /* This is a ColorExpand Blt, using the */
2722 /* same color for foreground and background */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002723 vga_wgfx(regbase, VGA_GFX_SR_VALUE, color); /* foreground color */
2724 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, color); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002725
2726 op = 0xc0;
Krzysztof Helt8343c892009-03-31 15:25:11 -07002727 if (bits_per_pixel >= 16) {
2728 vga_wgfx(regbase, CL_GR10, color >> 8); /* foreground color */
2729 vga_wgfx(regbase, CL_GR11, color >> 8); /* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002730 op = 0xd0;
Krzysztof Helt8343c892009-03-31 15:25:11 -07002731 }
2732 if (bits_per_pixel == 32) {
2733 vga_wgfx(regbase, CL_GR12, color >> 16);/* foreground color */
2734 vga_wgfx(regbase, CL_GR13, color >> 16);/* background color */
2735 vga_wgfx(regbase, CL_GR14, color >> 24);/* foreground color */
2736 vga_wgfx(regbase, CL_GR15, color >> 24);/* background color */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002737 op = 0xf0;
2738 }
Krzysztof Helt8343c892009-03-31 15:25:11 -07002739 cirrusfb_set_blitter(regbase, width - 1, height - 1,
2740 0, ndest, op, line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002741}
2742
Linus Torvalds1da177e2005-04-16 15:20:36 -07002743/**************************************************************************
2744 * bestclock() - determine closest possible clock lower(?) than the
2745 * desired pixel clock
2746 **************************************************************************/
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002747static void bestclock(long freq, int *nom, int *den, int *div)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002748{
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002749 int n, d;
2750 long h, diff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002751
Krzysztof Helt8503df62007-10-16 01:29:08 -07002752 assert(nom != NULL);
2753 assert(den != NULL);
2754 assert(div != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002755
2756 *nom = 0;
2757 *den = 0;
2758 *div = 0;
2759
Linus Torvalds1da177e2005-04-16 15:20:36 -07002760 if (freq < 8000)
2761 freq = 8000;
2762
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002763 diff = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002764
2765 for (n = 32; n < 128; n++) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002766 int s = 0;
2767
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002768 d = (14318 * n) / freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002769 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002770 int temp = d;
2771
2772 if (temp > 31) {
2773 s = 1;
2774 temp >>= 1;
2775 }
2776 h = ((14318 * n) / temp) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002777 h = h > freq ? h - freq : freq - h;
2778 if (h < diff) {
2779 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002780 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002781 *den = temp;
2782 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002783 }
2784 }
Krzysztof Helt7528f542008-10-15 22:03:36 -07002785 d++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002786 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002787 if (d > 31) {
2788 s = 1;
2789 d >>= 1;
2790 }
2791 h = ((14318 * n) / d) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002792 h = h > freq ? h - freq : freq - h;
2793 if (h < diff) {
2794 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002795 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002796 *den = d;
2797 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002798 }
2799 }
2800 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002801}
2802
Linus Torvalds1da177e2005-04-16 15:20:36 -07002803/* -------------------------------------------------------------------------
2804 *
2805 * debugging functions
2806 *
2807 * -------------------------------------------------------------------------
2808 */
2809
2810#ifdef CIRRUSFB_DEBUG
2811
2812/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002813 * cirrusfb_dbg_print_regs
2814 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2815 * @reg_class: type of registers to read: %CRT, or %SEQ
2816 *
2817 * DESCRIPTION:
2818 * Dumps the given list of VGA CRTC registers. If @base is %NULL,
2819 * old-style I/O ports are queried for information, otherwise MMIO is
2820 * used at the given @base address to query the information.
2821 */
2822
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002823static void cirrusfb_dbg_print_regs(struct fb_info *info,
2824 caddr_t regbase,
2825 enum cirrusfb_dbg_reg_class reg_class, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002826{
2827 va_list list;
2828 unsigned char val = 0;
2829 unsigned reg;
2830 char *name;
2831
Krzysztof Helt8503df62007-10-16 01:29:08 -07002832 va_start(list, reg_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002833
Krzysztof Helt8503df62007-10-16 01:29:08 -07002834 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002835 while (name != NULL) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002836 reg = va_arg(list, int);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002837
2838 switch (reg_class) {
2839 case CRT:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002840 val = vga_rcrt(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002841 break;
2842 case SEQ:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002843 val = vga_rseq(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002844 break;
2845 default:
2846 /* should never occur */
Richard Knutssonc930faa2007-05-08 00:38:29 -07002847 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002848 break;
2849 }
2850
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002851 dev_dbg(info->device, "%8s = 0x%02X\n", name, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002852
Krzysztof Helt8503df62007-10-16 01:29:08 -07002853 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002854 }
2855
Krzysztof Helt8503df62007-10-16 01:29:08 -07002856 va_end(list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002857}
2858
Linus Torvalds1da177e2005-04-16 15:20:36 -07002859/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002860 * cirrusfb_dbg_reg_dump
2861 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2862 *
2863 * DESCRIPTION:
2864 * Dumps a list of interesting VGA and CIRRUSFB registers. If @base is %NULL,
2865 * old-style I/O ports are queried for information, otherwise MMIO is
2866 * used at the given @base address to query the information.
2867 */
2868
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002869static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002870{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002871 dev_dbg(info->device, "VGA CRTC register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002872
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002873 cirrusfb_dbg_print_regs(info, regbase, CRT,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002874 "CR00", 0x00,
2875 "CR01", 0x01,
2876 "CR02", 0x02,
2877 "CR03", 0x03,
2878 "CR04", 0x04,
2879 "CR05", 0x05,
2880 "CR06", 0x06,
2881 "CR07", 0x07,
2882 "CR08", 0x08,
2883 "CR09", 0x09,
2884 "CR0A", 0x0A,
2885 "CR0B", 0x0B,
2886 "CR0C", 0x0C,
2887 "CR0D", 0x0D,
2888 "CR0E", 0x0E,
2889 "CR0F", 0x0F,
2890 "CR10", 0x10,
2891 "CR11", 0x11,
2892 "CR12", 0x12,
2893 "CR13", 0x13,
2894 "CR14", 0x14,
2895 "CR15", 0x15,
2896 "CR16", 0x16,
2897 "CR17", 0x17,
2898 "CR18", 0x18,
2899 "CR22", 0x22,
2900 "CR24", 0x24,
2901 "CR26", 0x26,
2902 "CR2D", 0x2D,
2903 "CR2E", 0x2E,
2904 "CR2F", 0x2F,
2905 "CR30", 0x30,
2906 "CR31", 0x31,
2907 "CR32", 0x32,
2908 "CR33", 0x33,
2909 "CR34", 0x34,
2910 "CR35", 0x35,
2911 "CR36", 0x36,
2912 "CR37", 0x37,
2913 "CR38", 0x38,
2914 "CR39", 0x39,
2915 "CR3A", 0x3A,
2916 "CR3B", 0x3B,
2917 "CR3C", 0x3C,
2918 "CR3D", 0x3D,
2919 "CR3E", 0x3E,
2920 "CR3F", 0x3F,
2921 NULL);
2922
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002923 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002924
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002925 dev_dbg(info->device, "VGA SEQ register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002926
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002927 cirrusfb_dbg_print_regs(info, regbase, SEQ,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002928 "SR00", 0x00,
2929 "SR01", 0x01,
2930 "SR02", 0x02,
2931 "SR03", 0x03,
2932 "SR04", 0x04,
2933 "SR08", 0x08,
2934 "SR09", 0x09,
2935 "SR0A", 0x0A,
2936 "SR0B", 0x0B,
2937 "SR0D", 0x0D,
2938 "SR10", 0x10,
2939 "SR11", 0x11,
2940 "SR12", 0x12,
2941 "SR13", 0x13,
2942 "SR14", 0x14,
2943 "SR15", 0x15,
2944 "SR16", 0x16,
2945 "SR17", 0x17,
2946 "SR18", 0x18,
2947 "SR19", 0x19,
2948 "SR1A", 0x1A,
2949 "SR1B", 0x1B,
2950 "SR1C", 0x1C,
2951 "SR1D", 0x1D,
2952 "SR1E", 0x1E,
2953 "SR1F", 0x1F,
2954 NULL);
2955
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002956 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002957}
2958
2959#endif /* CIRRUSFB_DEBUG */
2960