blob: a2a09b39c59a14597d7d79ff65a98ee731dc9bb4 [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 Helt9e848062009-03-31 15:25:11 -0700384 u32 fg_color, u32 bg_color,
385 u_short line_length, u_char blitmode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700387static void bestclock(long freq, int *nom, int *den, int *div);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388
389#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700390static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase);
391static void cirrusfb_dbg_print_regs(struct fb_info *info,
392 caddr_t regbase,
Krzysztof Helt7345de32007-10-16 01:29:11 -0700393 enum cirrusfb_dbg_reg_class reg_class, ...);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394#endif /* CIRRUSFB_DEBUG */
395
396/*** END PROTOTYPES ********************************************************/
397/*****************************************************************************/
398/*** BEGIN Interface Used by the World ***************************************/
399
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700400static inline int is_laguna(const struct cirrusfb_info *cinfo)
401{
402 return cinfo->btype == BT_LAGUNA || cinfo->btype == BT_LAGUNAB;
403}
404
Krzysztof Helt8503df62007-10-16 01:29:08 -0700405static int opencount;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406
407/*--- Open /dev/fbx ---------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700408static int cirrusfb_open(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409{
410 if (opencount++ == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700411 switch_monitor(info->par, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412 return 0;
413}
414
415/*--- Close /dev/fbx --------------------------------------------------------*/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700416static int cirrusfb_release(struct fb_info *info, int user)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700417{
418 if (--opencount == 0)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700419 switch_monitor(info->par, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420 return 0;
421}
422
423/**** END Interface used by the World *************************************/
424/****************************************************************************/
425/**** BEGIN Hardware specific Routines **************************************/
426
Krzysztof Helt486ff382008-10-15 22:03:42 -0700427/* Check if the MCLK is not a better clock source */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700428static int cirrusfb_check_mclk(struct fb_info *info, long freq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700430 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700431 long mclk = vga_rseq(cinfo->regbase, CL_SEQR1F) & 0x3f;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432
Krzysztof Helt486ff382008-10-15 22:03:42 -0700433 /* Read MCLK value */
434 mclk = (14318 * mclk) >> 3;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700435 dev_dbg(info->device, "Read MCLK of %ld kHz\n", mclk);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436
437 /* Determine if we should use MCLK instead of VCLK, and if so, what we
Krzysztof Helt486ff382008-10-15 22:03:42 -0700438 * should divide it by to get VCLK
439 */
440
441 if (abs(freq - mclk) < 250) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700442 dev_dbg(info->device, "Using VCLK = MCLK\n");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700443 return 1;
444 } else if (abs(freq - (mclk / 2)) < 250) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700445 dev_dbg(info->device, "Using VCLK = MCLK/2\n");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700446 return 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700447 }
448
Krzysztof Helt486ff382008-10-15 22:03:42 -0700449 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450}
451
Krzysztof Helt99a45842009-03-31 15:25:09 -0700452static int cirrusfb_check_pixclock(const struct fb_var_screeninfo *var,
453 struct fb_info *info)
454{
455 long freq;
456 long maxclock;
457 struct cirrusfb_info *cinfo = info->par;
458 unsigned maxclockidx = var->bits_per_pixel >> 3;
459
460 /* convert from ps to kHz */
461 freq = PICOS2KHZ(var->pixclock);
462
463 dev_dbg(info->device, "desired pixclock: %ld kHz\n", freq);
464
465 maxclock = cirrusfb_board_info[cinfo->btype].maxclock[maxclockidx];
466 cinfo->multiplexing = 0;
467
468 /* If the frequency is greater than we can support, we might be able
469 * to use multiplexing for the video mode */
470 if (freq > maxclock) {
471 switch (cinfo->btype) {
472 case BT_ALPINE:
473 case BT_GD5480:
474 cinfo->multiplexing = 1;
475 break;
476
477 default:
478 dev_err(info->device,
479 "Frequency greater than maxclock (%ld kHz)\n",
480 maxclock);
481 return -EINVAL;
482 }
483 }
484#if 0
485 /* TODO: If we have a 1MB 5434, we need to put ourselves in a mode where
486 * the VCLK is double the pixel clock. */
487 switch (var->bits_per_pixel) {
488 case 16:
489 case 32:
490 if (var->xres <= 800)
491 /* Xbh has this type of clock for 32-bit */
492 freq /= 2;
493 break;
494 }
495#endif
496 return 0;
497}
498
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499static int cirrusfb_check_var(struct fb_var_screeninfo *var,
500 struct fb_info *info)
501{
Krzysztof Helt09a29102008-09-02 14:35:51 -0700502 int yres;
503 /* memory size in pixels */
504 unsigned pixels = info->screen_size * 8 / var->bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505
506 switch (var->bits_per_pixel) {
Krzysztof Helt060b6002007-10-16 01:29:13 -0700507 case 1:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508 var->red.offset = 0;
509 var->red.length = 1;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700510 var->green = var->red;
511 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512 break;
513
514 case 8:
515 var->red.offset = 0;
Krzysztof Helt99a45842009-03-31 15:25:09 -0700516 var->red.length = 8;
Krzysztof Helt060b6002007-10-16 01:29:13 -0700517 var->green = var->red;
518 var->blue = var->red;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519 break;
520
521 case 16:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700522 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523 var->red.offset = 2;
524 var->green.offset = -3;
525 var->blue.offset = 8;
526 } else {
Krzysztof Heltc4dec392009-03-31 15:25:07 -0700527 var->red.offset = 11;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528 var->green.offset = 5;
529 var->blue.offset = 0;
530 }
531 var->red.length = 5;
Krzysztof Heltc4dec392009-03-31 15:25:07 -0700532 var->green.length = 6;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533 var->blue.length = 5;
534 break;
535
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536 case 32:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700537 if (isPReP) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 var->red.offset = 8;
539 var->green.offset = 16;
540 var->blue.offset = 24;
541 } else {
542 var->red.offset = 16;
543 var->green.offset = 8;
544 var->blue.offset = 0;
545 }
546 var->red.length = 8;
547 var->green.length = 8;
548 var->blue.length = 8;
549 break;
550
551 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700552 dev_dbg(info->device,
553 "Unsupported bpp size: %d\n", var->bits_per_pixel);
Richard Knutssonc930faa2007-05-08 00:38:29 -0700554 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555 /* should never occur */
556 break;
557 }
558
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700559 if (var->xres_virtual < var->xres)
560 var->xres_virtual = var->xres;
561 /* use highest possible virtual resolution */
562 if (var->yres_virtual == -1) {
563 var->yres_virtual = pixels / var->xres_virtual;
564
565 dev_info(info->device,
566 "virtual resolution set to maximum of %dx%d\n",
567 var->xres_virtual, var->yres_virtual);
568 }
569 if (var->yres_virtual < var->yres)
570 var->yres_virtual = var->yres;
571
572 if (var->xres_virtual * var->yres_virtual > pixels) {
573 dev_err(info->device, "mode %dx%dx%d rejected... "
574 "virtual resolution too high to fit into video memory!\n",
575 var->xres_virtual, var->yres_virtual,
576 var->bits_per_pixel);
577 return -EINVAL;
578 }
579
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700580 if (var->xoffset < 0)
581 var->xoffset = 0;
582 if (var->yoffset < 0)
583 var->yoffset = 0;
584
585 /* truncate xoffset and yoffset to maximum if too high */
586 if (var->xoffset > var->xres_virtual - var->xres)
587 var->xoffset = var->xres_virtual - var->xres - 1;
588 if (var->yoffset > var->yres_virtual - var->yres)
589 var->yoffset = var->yres_virtual - var->yres - 1;
590
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591 var->red.msb_right =
592 var->green.msb_right =
593 var->blue.msb_right =
594 var->transp.offset =
595 var->transp.length =
596 var->transp.msb_right = 0;
597
598 yres = var->yres;
599 if (var->vmode & FB_VMODE_DOUBLE)
600 yres *= 2;
601 else if (var->vmode & FB_VMODE_INTERLACED)
602 yres = (yres + 1) / 2;
603
604 if (yres >= 1280) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700605 dev_err(info->device, "ERROR: VerticalTotal >= 1280; "
Krzysztof Helt8503df62007-10-16 01:29:08 -0700606 "special treatment required! (TODO)\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700607 return -EINVAL;
608 }
609
Krzysztof Helt99a45842009-03-31 15:25:09 -0700610 if (cirrusfb_check_pixclock(var, info))
611 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613 return 0;
614}
615
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700616static void cirrusfb_set_mclk_as_source(const struct fb_info *info, int div)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700618 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700619 unsigned char old1f, old1e;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700620
Krzysztof Helt8503df62007-10-16 01:29:08 -0700621 assert(cinfo != NULL);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700622 old1f = vga_rseq(cinfo->regbase, CL_SEQR1F) & ~0x40;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623
Krzysztof Helt486ff382008-10-15 22:03:42 -0700624 if (div) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700625 dev_dbg(info->device, "Set %s as pixclock source.\n",
626 (div == 2) ? "MCLK/2" : "MCLK");
Krzysztof Helt486ff382008-10-15 22:03:42 -0700627 old1f |= 0x40;
628 old1e = vga_rseq(cinfo->regbase, CL_SEQR1E) & ~0x1;
629 if (div == 2)
630 old1e |= 1;
631
632 vga_wseq(cinfo->regbase, CL_SEQR1E, old1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700634 vga_wseq(cinfo->regbase, CL_SEQR1F, old1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635}
636
637/*************************************************************************
638 cirrusfb_set_par_foo()
639
640 actually writes the values for a new video mode into the hardware,
641**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -0700642static int cirrusfb_set_par_foo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700643{
644 struct cirrusfb_info *cinfo = info->par;
645 struct fb_var_screeninfo *var = &info->var;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646 u8 __iomem *regbase = cinfo->regbase;
647 unsigned char tmp;
Krzysztof Helt6683e012009-03-31 15:25:06 -0700648 int pitch;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649 const struct cirrusfb_board_info_rec *bi;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700650 int hdispend, hsyncstart, hsyncend, htotal;
651 int yres, vdispend, vsyncstart, vsyncend, vtotal;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700652 long freq;
653 int nom, den, div;
Krzysztof Helt1b48cb52009-03-31 15:25:08 -0700654 unsigned int control = 0, format = 0, threshold = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700656 dev_dbg(info->device, "Requested mode: %dx%dx%d\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657 var->xres, var->yres, var->bits_per_pixel);
Krzysztof Helt99a45842009-03-31 15:25:09 -0700658
659 switch (var->bits_per_pixel) {
660 case 1:
661 info->fix.line_length = var->xres_virtual / 8;
662 info->fix.visual = FB_VISUAL_MONO10;
663 break;
664
665 case 8:
666 info->fix.line_length = var->xres_virtual;
667 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
668 break;
669
670 case 16:
671 case 32:
672 info->fix.line_length = var->xres_virtual *
673 var->bits_per_pixel >> 3;
674 info->fix.visual = FB_VISUAL_TRUECOLOR;
675 break;
676 }
677 info->fix.type = FB_TYPE_PACKED_PIXELS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678
Krzysztof Helt9199ec52007-10-16 01:29:12 -0700679 init_vgachip(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681 bi = &cirrusfb_board_info[cinfo->btype];
682
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700683 hsyncstart = var->xres + var->right_margin;
684 hsyncend = hsyncstart + var->hsync_len;
685 htotal = (hsyncend + var->left_margin) / 8 - 5;
686 hdispend = var->xres / 8 - 1;
687 hsyncstart = hsyncstart / 8 + 1;
688 hsyncend = hsyncend / 8 + 1;
689
690 yres = var->yres;
691 vsyncstart = yres + var->lower_margin;
692 vsyncend = vsyncstart + var->vsync_len;
693 vtotal = vsyncend + var->upper_margin;
694 vdispend = yres - 1;
695
696 if (var->vmode & FB_VMODE_DOUBLE) {
697 yres *= 2;
698 vsyncstart *= 2;
699 vsyncend *= 2;
700 vtotal *= 2;
701 } else if (var->vmode & FB_VMODE_INTERLACED) {
702 yres = (yres + 1) / 2;
703 vsyncstart = (vsyncstart + 1) / 2;
704 vsyncend = (vsyncend + 1) / 2;
705 vtotal = (vtotal + 1) / 2;
706 }
707
708 vtotal -= 2;
709 vsyncstart -= 1;
710 vsyncend -= 1;
711
712 if (yres >= 1024) {
713 vtotal /= 2;
714 vsyncstart /= 2;
715 vsyncend /= 2;
716 vdispend /= 2;
717 }
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700718 if (cinfo->multiplexing) {
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700719 htotal /= 2;
720 hsyncstart /= 2;
721 hsyncend /= 2;
722 hdispend /= 2;
723 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724 /* unlock register VGA_CRTC_H_TOTAL..CRT7 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700725 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, 0x20); /* previously: 0x00) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726
727 /* if debugging is enabled, all parameters get output before writing */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700728 dev_dbg(info->device, "CRT0: %d\n", htotal);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700729 vga_wcrt(regbase, VGA_CRTC_H_TOTAL, htotal);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700730
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700731 dev_dbg(info->device, "CRT1: %d\n", hdispend);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700732 vga_wcrt(regbase, VGA_CRTC_H_DISP, hdispend);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700734 dev_dbg(info->device, "CRT2: %d\n", var->xres / 8);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700735 vga_wcrt(regbase, VGA_CRTC_H_BLANK_START, var->xres / 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736
Krzysztof Helt8503df62007-10-16 01:29:08 -0700737 /* + 128: Compatible read */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700738 dev_dbg(info->device, "CRT3: 128+%d\n", (htotal + 5) % 32);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700739 vga_wcrt(regbase, VGA_CRTC_H_BLANK_END,
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700740 128 + ((htotal + 5) % 32));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700742 dev_dbg(info->device, "CRT4: %d\n", hsyncstart);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700743 vga_wcrt(regbase, VGA_CRTC_H_SYNC_START, hsyncstart);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700745 tmp = hsyncend % 32;
746 if ((htotal + 5) & 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747 tmp += 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700748 dev_dbg(info->device, "CRT5: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700749 vga_wcrt(regbase, VGA_CRTC_H_SYNC_END, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700751 dev_dbg(info->device, "CRT6: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700752 vga_wcrt(regbase, VGA_CRTC_V_TOTAL, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753
754 tmp = 16; /* LineCompare bit #9 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700755 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700757 if (vdispend & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758 tmp |= 2;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700759 if (vsyncstart & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760 tmp |= 4;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700761 if ((vdispend + 1) & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762 tmp |= 8;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700763 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700765 if (vdispend & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700767 if (vsyncstart & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700768 tmp |= 128;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700769 dev_dbg(info->device, "CRT7: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700770 vga_wcrt(regbase, VGA_CRTC_OVERFLOW, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771
772 tmp = 0x40; /* LineCompare bit #8 */
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700773 if ((vdispend + 1) & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774 tmp |= 0x20;
775 if (var->vmode & FB_VMODE_DOUBLE)
776 tmp |= 0x80;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700777 dev_dbg(info->device, "CRT9: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700778 vga_wcrt(regbase, VGA_CRTC_MAX_SCAN, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700779
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700780 dev_dbg(info->device, "CRT10: %d\n", vsyncstart & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700781 vga_wcrt(regbase, VGA_CRTC_V_SYNC_START, vsyncstart & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700783 dev_dbg(info->device, "CRT11: 64+32+%d\n", vsyncend % 16);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700784 vga_wcrt(regbase, VGA_CRTC_V_SYNC_END, vsyncend % 16 + 64 + 32);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700786 dev_dbg(info->device, "CRT12: %d\n", vdispend & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700787 vga_wcrt(regbase, VGA_CRTC_V_DISP_END, vdispend & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700789 dev_dbg(info->device, "CRT15: %d\n", (vdispend + 1) & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700790 vga_wcrt(regbase, VGA_CRTC_V_BLANK_START, (vdispend + 1) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700792 dev_dbg(info->device, "CRT16: %d\n", vtotal & 0xff);
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700793 vga_wcrt(regbase, VGA_CRTC_V_BLANK_END, vtotal & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700795 dev_dbg(info->device, "CRT18: 0xff\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700796 vga_wcrt(regbase, VGA_CRTC_LINE_COMPARE, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797
798 tmp = 0;
799 if (var->vmode & FB_VMODE_INTERLACED)
800 tmp |= 1;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700801 if ((htotal + 5) & 64)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802 tmp |= 16;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700803 if ((htotal + 5) & 128)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804 tmp |= 32;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700805 if (vtotal & 256)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806 tmp |= 64;
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700807 if (vtotal & 512)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700808 tmp |= 128;
809
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700810 dev_dbg(info->device, "CRT1a: %d\n", tmp);
Krzysztof Helt8503df62007-10-16 01:29:08 -0700811 vga_wcrt(regbase, CL_CRT1A, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812
Krzysztof Heltdafa32c2008-10-15 22:03:40 -0700813 freq = PICOS2KHZ(var->pixclock);
814 bestclock(freq, &nom, &den, &div);
815
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700816 dev_dbg(info->device, "VCLK freq: %ld kHz nom: %d den: %d div: %d\n",
817 freq, nom, den, div);
818
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819 /* set VCLK0 */
820 /* hardware RefClock: 14.31818 MHz */
821 /* formula: VClk = (OSC * N) / (D * (1+P)) */
822 /* Example: VClk = (14.31818 * 91) / (23 * (1+1)) = 28.325 MHz */
823
Krzysztof Helt486ff382008-10-15 22:03:42 -0700824 if (cinfo->btype == BT_ALPINE) {
825 /* if freq is close to mclk or mclk/2 select mclk
826 * as clock source
827 */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700828 int divMCLK = cirrusfb_check_mclk(info, freq);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700829 if (divMCLK) {
830 nom = 0;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700831 cirrusfb_set_mclk_as_source(info, divMCLK);
Krzysztof Helt486ff382008-10-15 22:03:42 -0700832 }
833 }
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700834 if (is_laguna(cinfo)) {
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700835 long pcifc = fb_readl(cinfo->laguna_mmio + 0x3fc);
836 unsigned char tile = fb_readb(cinfo->laguna_mmio + 0x407);
837 unsigned short tile_control;
838
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700839 if (cinfo->btype == BT_LAGUNAB) {
840 tile_control = fb_readw(cinfo->laguna_mmio + 0x2c4);
841 tile_control &= ~0x80;
842 fb_writew(tile_control, cinfo->laguna_mmio + 0x2c4);
843 }
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700844
845 fb_writel(pcifc | 0x10000000l, cinfo->laguna_mmio + 0x3fc);
846 fb_writeb(tile & 0x3f, cinfo->laguna_mmio + 0x407);
847 control = fb_readw(cinfo->laguna_mmio + 0x402);
848 threshold = fb_readw(cinfo->laguna_mmio + 0xea);
849 control &= ~0x6800;
850 format = 0;
Krzysztof Helt1b48cb52009-03-31 15:25:08 -0700851 threshold &= 0xffe0 & 0x3fbf;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -0700852 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700853 if (nom) {
Krzysztof Helt486ff382008-10-15 22:03:42 -0700854 tmp = den << 1;
855 if (div != 0)
856 tmp |= 1;
Krzysztof Helt486ff382008-10-15 22:03:42 -0700857 /* 6 bit denom; ONLY 5434!!! (bugged me 10 days) */
858 if ((cinfo->btype == BT_SD64) ||
859 (cinfo->btype == BT_ALPINE) ||
860 (cinfo->btype == BT_GD5480))
861 tmp |= 0x80;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700863 dev_dbg(info->device, "CL_SEQR1B: %d\n", (int) tmp);
864 /* Laguna chipset has reversed clock registers */
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700865 if (is_laguna(cinfo)) {
Krzysztof Helt55a4ea62009-03-31 15:25:04 -0700866 vga_wseq(regbase, CL_SEQRE, tmp);
867 vga_wseq(regbase, CL_SEQR1E, nom);
868 } else {
869 vga_wseq(regbase, CL_SEQRB, nom);
870 vga_wseq(regbase, CL_SEQR1B, tmp);
871 }
Krzysztof Helt486ff382008-10-15 22:03:42 -0700872 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700874 if (yres >= 1024)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700875 /* 1280x1024 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700876 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc7);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877 else
878 /* mode control: VGA_CRTC_START_HI enable, ROTATE(?), 16bit
879 * address wrap, no compat. */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700880 vga_wcrt(regbase, VGA_CRTC_MODE, 0xc3);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700881
Linus Torvalds1da177e2005-04-16 15:20:36 -0700882 /* don't know if it would hurt to also program this if no interlaced */
883 /* mode is used, but I feel better this way.. :-) */
884 if (var->vmode & FB_VMODE_INTERLACED)
Krzysztof Helt9a85cf52008-10-15 22:03:39 -0700885 vga_wcrt(regbase, VGA_CRTC_REGS, htotal / 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700886 else
Krzysztof Helt8503df62007-10-16 01:29:08 -0700887 vga_wcrt(regbase, VGA_CRTC_REGS, 0x00); /* interlace control */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888
Linus Torvalds1da177e2005-04-16 15:20:36 -0700889 /* adjust horizontal/vertical sync type (low/high) */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700890 /* enable display memory & CRTC I/O address for color mode */
891 tmp = 0x03;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
893 tmp |= 0x40;
894 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
895 tmp |= 0x80;
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700896 if (is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -0700897 tmp |= 0xc;
Krzysztof Helt8503df62007-10-16 01:29:08 -0700898 WGen(cinfo, VGA_MIS_W, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700899
Krzysztof Helt8503df62007-10-16 01:29:08 -0700900 /* text cursor on and start line */
901 vga_wcrt(regbase, VGA_CRTC_CURSOR_START, 0);
902 /* text cursor end line */
903 vga_wcrt(regbase, VGA_CRTC_CURSOR_END, 31);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904
905 /******************************************************
906 *
907 * 1 bpp
908 *
909 */
910
911 /* programming for different color depths */
912 if (var->bits_per_pixel == 1) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700913 dev_dbg(info->device, "preparing for 1 bit deep display\n");
Krzysztof Helt8503df62007-10-16 01:29:08 -0700914 vga_wgfx(regbase, VGA_GFX_MODE, 0); /* mode register */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915
916 /* SR07 */
917 switch (cinfo->btype) {
918 case BT_SD64:
919 case BT_PICCOLO:
920 case BT_PICASSO:
921 case BT_SPECTRUM:
922 case BT_PICASSO4:
923 case BT_ALPINE:
924 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700925 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700926 cinfo->multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927 bi->sr07_1bpp_mux : bi->sr07_1bpp);
928 break;
929
930 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700931 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700932 vga_wseq(regbase, CL_SEQR7,
933 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934 break;
935
936 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700937 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700938 break;
939 }
940
941 /* Extended Sequencer Mode */
942 switch (cinfo->btype) {
943 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700944 /* setting the SEQRF on SD64 is not necessary
945 * (only during init)
946 */
Krzysztof Helt8503df62007-10-16 01:29:08 -0700947 /* MCLK select */
948 vga_wseq(regbase, CL_SEQR1F, 0x1a);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949 break;
950
951 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -0700952 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700953 /* ### ueberall 0x22? */
954 /* ##vorher 1c MCLK select */
955 vga_wseq(regbase, CL_SEQR1F, 0x22);
956 /* evtl d0 bei 1 bit? avoid FIFO underruns..? */
957 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700958 break;
959
960 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -0700961 /* ##vorher 22 MCLK select */
962 vga_wseq(regbase, CL_SEQR1F, 0x22);
963 /* ## vorher d0 avoid FIFO underruns..? */
964 vga_wseq(regbase, CL_SEQRF, 0xd0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965 break;
966
Linus Torvalds1da177e2005-04-16 15:20:36 -0700967 case BT_PICASSO4:
968 case BT_ALPINE:
969 case BT_GD5480:
970 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -0700971 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700972 /* do nothing */
973 break;
974
975 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -0700976 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700977 break;
978 }
979
Krzysztof Helt8503df62007-10-16 01:29:08 -0700980 /* pixel mask: pass-through for first plane */
981 WGen(cinfo, VGA_PEL_MSK, 0x01);
Krzysztof Helt48c329e2009-03-31 15:25:08 -0700982 if (cinfo->multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -0700983 /* hidden dac reg: 1280x1024 */
984 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700985 else
Krzysztof Helt8503df62007-10-16 01:29:08 -0700986 /* hidden dac: nothing */
987 WHDR(cinfo, 0);
988 /* memory mode: odd/even, ext. memory */
989 vga_wseq(regbase, VGA_SEQ_MEMORY_MODE, 0x06);
990 /* plane mask: only write to first plane */
991 vga_wseq(regbase, VGA_SEQ_PLANE_WRITE, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700992 }
993
994 /******************************************************
995 *
996 * 8 bpp
997 *
998 */
999
1000 else if (var->bits_per_pixel == 8) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001001 dev_dbg(info->device, "preparing for 8 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002 switch (cinfo->btype) {
1003 case BT_SD64:
1004 case BT_PICCOLO:
1005 case BT_PICASSO:
1006 case BT_SPECTRUM:
1007 case BT_PICASSO4:
1008 case BT_ALPINE:
1009 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001010 vga_wseq(regbase, CL_SEQR7,
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001011 cinfo->multiplexing ?
Linus Torvalds1da177e2005-04-16 15:20:36 -07001012 bi->sr07_8bpp_mux : bi->sr07_8bpp);
1013 break;
1014
1015 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001016 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001017 vga_wseq(regbase, CL_SEQR7,
1018 vga_rseq(regbase, CL_SEQR7) | 0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001019 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001020 break;
1021
1022 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001023 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001024 break;
1025 }
1026
1027 switch (cinfo->btype) {
1028 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001029 /* MCLK select */
1030 vga_wseq(regbase, CL_SEQR1F, 0x1d);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031 break;
1032
1033 case BT_PICCOLO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001034 case BT_PICASSO:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001035 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001036 /* ### vorher 1c MCLK select */
1037 vga_wseq(regbase, CL_SEQR1F, 0x22);
1038 /* Fast Page-Mode writes */
1039 vga_wseq(regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001040 break;
1041
1042 case BT_PICASSO4:
1043#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07001044 /* ### INCOMPLETE!! */
1045 vga_wseq(regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07001047/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001048 break;
1049
1050 case BT_ALPINE:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051 /* We already set SRF and SR1F */
1052 break;
1053
1054 case BT_GD5480:
1055 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001056 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001057 /* do nothing */
1058 break;
1059
1060 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001061 dev_warn(info->device, "unknown board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062 break;
1063 }
1064
Krzysztof Helt8503df62007-10-16 01:29:08 -07001065 /* mode register: 256 color mode */
1066 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001067 if (cinfo->multiplexing)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001068 /* hidden dac reg: 1280x1024 */
1069 WHDR(cinfo, 0x4a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001070 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001071 /* hidden dac: nothing */
1072 WHDR(cinfo, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073 }
1074
1075 /******************************************************
1076 *
1077 * 16 bpp
1078 *
1079 */
1080
1081 else if (var->bits_per_pixel == 16) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001082 dev_dbg(info->device, "preparing for 16 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083 switch (cinfo->btype) {
1084 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001085 /* Extended Sequencer Mode: 256c col. mode */
1086 vga_wseq(regbase, CL_SEQR7, 0xf7);
1087 /* MCLK select */
1088 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001089 break;
1090
1091 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001092 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001093 vga_wseq(regbase, CL_SEQR7, 0x87);
1094 /* Fast Page-Mode writes */
1095 vga_wseq(regbase, CL_SEQRF, 0xb0);
1096 /* MCLK select */
1097 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001098 break;
1099
1100 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001101 vga_wseq(regbase, CL_SEQR7, 0x27);
1102 /* Fast Page-Mode writes */
1103 vga_wseq(regbase, CL_SEQRF, 0xb0);
1104 /* MCLK select */
1105 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001106 break;
1107
Linus Torvalds1da177e2005-04-16 15:20:36 -07001108 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001109 vga_wseq(regbase, CL_SEQR7, 0x27);
1110/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001111 break;
1112
1113 case BT_ALPINE:
Krzysztof Helt3b921832008-10-15 22:03:41 -07001114 vga_wseq(regbase, CL_SEQR7, 0xa7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001115 break;
1116
1117 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001118 vga_wseq(regbase, CL_SEQR7, 0x17);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001119 /* We already set SRF and SR1F */
1120 break;
1121
1122 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001123 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001124 vga_wseq(regbase, CL_SEQR7,
1125 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001126 control |= 0x2000;
1127 format |= 0x1400;
1128 threshold |= 0x10;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001129 break;
1130
1131 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001132 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001133 break;
1134 }
1135
Krzysztof Helt8503df62007-10-16 01:29:08 -07001136 /* mode register: 256 color mode */
1137 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001138#ifdef CONFIG_PCI
Krzysztof Heltc4dec392009-03-31 15:25:07 -07001139 WHDR(cinfo, 0xc1); /* Copy Xbh */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001140#elif defined(CONFIG_ZORRO)
1141 /* FIXME: CONFIG_PCI and CONFIG_ZORRO may be defined both */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001142 WHDR(cinfo, 0xa0); /* hidden dac reg: nothing special */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001143#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07001144 }
1145
1146 /******************************************************
1147 *
1148 * 32 bpp
1149 *
1150 */
1151
1152 else if (var->bits_per_pixel == 32) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001153 dev_dbg(info->device, "preparing for 32 bit deep display\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001154 switch (cinfo->btype) {
1155 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001156 /* Extended Sequencer Mode: 256c col. mode */
1157 vga_wseq(regbase, CL_SEQR7, 0xf9);
1158 /* MCLK select */
1159 vga_wseq(regbase, CL_SEQR1F, 0x1e);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001160 break;
1161
1162 case BT_PICCOLO:
Krzysztof Helt060b6002007-10-16 01:29:13 -07001163 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001164 vga_wseq(regbase, CL_SEQR7, 0x85);
1165 /* Fast Page-Mode writes */
1166 vga_wseq(regbase, CL_SEQRF, 0xb0);
1167 /* MCLK select */
1168 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001169 break;
1170
1171 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001172 vga_wseq(regbase, CL_SEQR7, 0x25);
1173 /* Fast Page-Mode writes */
1174 vga_wseq(regbase, CL_SEQRF, 0xb0);
1175 /* MCLK select */
1176 vga_wseq(regbase, CL_SEQR1F, 0x22);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001177 break;
1178
Linus Torvalds1da177e2005-04-16 15:20:36 -07001179 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001180 vga_wseq(regbase, CL_SEQR7, 0x25);
1181/* vga_wseq(regbase, CL_SEQR1F, 0x1c); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001182 break;
1183
1184 case BT_ALPINE:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001185 vga_wseq(regbase, CL_SEQR7, 0xa9);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001186 break;
1187
1188 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001189 vga_wseq(regbase, CL_SEQR7, 0x19);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001190 /* We already set SRF and SR1F */
1191 break;
1192
1193 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001194 case BT_LAGUNAB:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001195 vga_wseq(regbase, CL_SEQR7,
1196 vga_rseq(regbase, CL_SEQR7) & ~0x01);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001197 control |= 0x6000;
1198 format |= 0x3400;
1199 threshold |= 0x20;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001200 break;
1201
1202 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001203 dev_warn(info->device, "unknown Board\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001204 break;
1205 }
1206
Krzysztof Helt8503df62007-10-16 01:29:08 -07001207 /* mode register: 256 color mode */
1208 vga_wgfx(regbase, VGA_GFX_MODE, 64);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001209 /* hidden dac reg: 8-8-8 mode (24 or 32) */
1210 WHDR(cinfo, 0xc5);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001211 }
1212
1213 /******************************************************
1214 *
1215 * unknown/unsupported bpp
1216 *
1217 */
1218
Krzysztof Helt8503df62007-10-16 01:29:08 -07001219 else
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001220 dev_err(info->device,
1221 "What's this? requested color depth == %d.\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -07001222 var->bits_per_pixel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001223
Krzysztof Helt6683e012009-03-31 15:25:06 -07001224 pitch = info->fix.line_length >> 3;
1225 vga_wcrt(regbase, VGA_CRTC_OFFSET, pitch & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001226 tmp = 0x22;
Krzysztof Helt6683e012009-03-31 15:25:06 -07001227 if (pitch & 0x100)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001228 tmp |= 0x10; /* offset overflow bit */
1229
Krzysztof Helt8503df62007-10-16 01:29:08 -07001230 /* screen start addr #16-18, fastpagemode cycles */
1231 vga_wcrt(regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001232
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001233 /* screen start address bit 19 */
1234 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19)
Krzysztof Helt6683e012009-03-31 15:25:06 -07001235 vga_wcrt(regbase, CL_CRT1D, (pitch >> 9) & 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001236
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001237 if (is_laguna(cinfo)) {
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001238 tmp = 0;
1239 if ((htotal + 5) & 256)
1240 tmp |= 128;
1241 if (hdispend & 256)
1242 tmp |= 64;
1243 if (hsyncstart & 256)
1244 tmp |= 48;
1245 if (vtotal & 1024)
1246 tmp |= 8;
1247 if (vdispend & 1024)
1248 tmp |= 4;
1249 if (vsyncstart & 1024)
1250 tmp |= 3;
1251
1252 vga_wcrt(regbase, CL_CRT1E, tmp);
1253 dev_dbg(info->device, "CRT1e: %d\n", tmp);
1254 }
1255
Krzysztof Helt8503df62007-10-16 01:29:08 -07001256 /* pixel panning */
1257 vga_wattr(regbase, CL_AR33, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001258
1259 /* [ EGS: SetOffset(); ] */
1260 /* From SetOffset(): Turn on VideoEnable bit in Attribute controller */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001261 AttrOn(cinfo);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001262
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001263 if (is_laguna(cinfo)) {
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001264 /* no tiles */
1265 fb_writew(control | 0x1000, cinfo->laguna_mmio + 0x402);
1266 fb_writew(format, cinfo->laguna_mmio + 0xc0);
1267 fb_writew(threshold, cinfo->laguna_mmio + 0xea);
1268 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001269 /* finally, turn on everything - turn off "FullBandwidth" bit */
1270 /* also, set "DotClock%2" bit where requested */
1271 tmp = 0x01;
1272
1273/*** FB_VMODE_CLOCK_HALVE in linux/fb.h not defined anymore ?
1274 if (var->vmode & FB_VMODE_CLOCK_HALVE)
1275 tmp |= 0x08;
1276*/
1277
Krzysztof Helt8503df62007-10-16 01:29:08 -07001278 vga_wseq(regbase, VGA_SEQ_CLOCK_MODE, tmp);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001279 dev_dbg(info->device, "CL_SEQR1: %d\n", tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001280
Linus Torvalds1da177e2005-04-16 15:20:36 -07001281#ifdef CIRRUSFB_DEBUG
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001282 cirrusfb_dbg_reg_dump(info, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001283#endif
1284
Linus Torvalds1da177e2005-04-16 15:20:36 -07001285 return 0;
1286}
1287
1288/* for some reason incomprehensible to me, cirrusfb requires that you write
1289 * the registers twice for the settings to take..grr. -dte */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001290static int cirrusfb_set_par(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001291{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001292 cirrusfb_set_par_foo(info);
1293 return cirrusfb_set_par_foo(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001294}
1295
Krzysztof Helt8503df62007-10-16 01:29:08 -07001296static int cirrusfb_setcolreg(unsigned regno, unsigned red, unsigned green,
1297 unsigned blue, unsigned transp,
1298 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001299{
1300 struct cirrusfb_info *cinfo = info->par;
1301
1302 if (regno > 255)
1303 return -EINVAL;
1304
1305 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
1306 u32 v;
1307 red >>= (16 - info->var.red.length);
1308 green >>= (16 - info->var.green.length);
1309 blue >>= (16 - info->var.blue.length);
1310
Krzysztof Helt8503df62007-10-16 01:29:08 -07001311 if (regno >= 16)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001312 return 1;
1313 v = (red << info->var.red.offset) |
1314 (green << info->var.green.offset) |
1315 (blue << info->var.blue.offset);
1316
Krzysztof Helt060b6002007-10-16 01:29:13 -07001317 cinfo->pseudo_palette[regno] = v;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001318 return 0;
1319 }
1320
Krzysztof Helt8503df62007-10-16 01:29:08 -07001321 if (info->var.bits_per_pixel == 8)
1322 WClut(cinfo, regno, red >> 10, green >> 10, blue >> 10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001323
1324 return 0;
1325
1326}
1327
1328/*************************************************************************
1329 cirrusfb_pan_display()
1330
1331 performs display panning - provided hardware permits this
1332**************************************************************************/
Krzysztof Helt8503df62007-10-16 01:29:08 -07001333static int cirrusfb_pan_display(struct fb_var_screeninfo *var,
1334 struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001335{
Krzysztof Helt99a45842009-03-31 15:25:09 -07001336 int xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001337 unsigned long base;
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001338 unsigned char tmp, xpix;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001339 struct cirrusfb_info *cinfo = info->par;
1340
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001341 dev_dbg(info->device,
1342 "virtual offset: (%d,%d)\n", var->xoffset, var->yoffset);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001343
1344 /* no range checks for xoffset and yoffset, */
1345 /* as fb_pan_display has already done this */
1346 if (var->vmode & FB_VMODE_YWRAP)
1347 return -EINVAL;
1348
Linus Torvalds1da177e2005-04-16 15:20:36 -07001349 xoffset = var->xoffset * info->var.bits_per_pixel / 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001350
Krzysztof Helt99a45842009-03-31 15:25:09 -07001351 base = var->yoffset * info->fix.line_length + xoffset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001352
1353 if (info->var.bits_per_pixel == 1) {
1354 /* base is already correct */
1355 xpix = (unsigned char) (var->xoffset % 8);
1356 } else {
1357 base /= 4;
1358 xpix = (unsigned char) ((xoffset % 4) * 2);
1359 }
1360
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001361 if (!is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001362 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001363
1364 /* lower 8 + 8 bits of screen start address */
Krzysztof Helt99a45842009-03-31 15:25:09 -07001365 vga_wcrt(cinfo->regbase, VGA_CRTC_START_LO, base & 0xff);
1366 vga_wcrt(cinfo->regbase, VGA_CRTC_START_HI, (base >> 8) & 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001367
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001368 /* 0xf2 is %11110010, exclude tmp bits */
1369 tmp = vga_rcrt(cinfo->regbase, CL_CRT1B) & 0xf2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001370 /* construct bits 16, 17 and 18 of screen start address */
1371 if (base & 0x10000)
1372 tmp |= 0x01;
1373 if (base & 0x20000)
1374 tmp |= 0x04;
1375 if (base & 0x40000)
1376 tmp |= 0x08;
1377
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001378 vga_wcrt(cinfo->regbase, CL_CRT1B, tmp);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001379
1380 /* construct bit 19 of screen start address */
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001381 if (cirrusfb_board_info[cinfo->btype].scrn_start_bit19) {
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001382 tmp = vga_rcrt(cinfo->regbase, CL_CRT1D);
1383 if (is_laguna(cinfo))
1384 tmp = (tmp & ~0x18) | ((base >> 16) & 0x18);
1385 else
1386 tmp = (tmp & ~0x80) | ((base >> 12) & 0x80);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001387 vga_wcrt(cinfo->regbase, CL_CRT1D, tmp);
1388 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001389
Krzysztof Helt8503df62007-10-16 01:29:08 -07001390 /* write pixel panning value to AR33; this does not quite work in 8bpp
1391 *
1392 * ### Piccolo..? Will this work?
1393 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001394 if (info->var.bits_per_pixel == 1)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001395 vga_wattr(cinfo->regbase, CL_AR33, xpix);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001396
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001397 if (!is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001398 cirrusfb_WaitBLT(cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001399
Krzysztof Helt8503df62007-10-16 01:29:08 -07001400 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001401}
1402
Krzysztof Helt8503df62007-10-16 01:29:08 -07001403static int cirrusfb_blank(int blank_mode, struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001404{
1405 /*
Krzysztof Helt8503df62007-10-16 01:29:08 -07001406 * Blank the screen if blank_mode != 0, else unblank. If blank == NULL
1407 * then the caller blanks by setting the CLUT (Color Look Up Table)
1408 * to all black. Return 0 if blanking succeeded, != 0 if un-/blanking
1409 * failed due to e.g. a video mode which doesn't support it.
1410 * Implements VESA suspend and powerdown modes on hardware that
1411 * supports disabling hsync/vsync:
1412 * blank_mode == 2: suspend vsync
1413 * blank_mode == 3: suspend hsync
1414 * blank_mode == 4: powerdown
Linus Torvalds1da177e2005-04-16 15:20:36 -07001415 */
1416 unsigned char val;
1417 struct cirrusfb_info *cinfo = info->par;
1418 int current_mode = cinfo->blank_mode;
1419
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001420 dev_dbg(info->device, "ENTER, blank mode = %d\n", blank_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001421
1422 if (info->state != FBINFO_STATE_RUNNING ||
1423 current_mode == blank_mode) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001424 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001425 return 0;
1426 }
1427
1428 /* Undo current */
1429 if (current_mode == FB_BLANK_NORMAL ||
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001430 current_mode == FB_BLANK_UNBLANK)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001431 /* clear "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001432 val = 0;
1433 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001434 /* set "FullBandwidth" bit */
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001435 val = 0x20;
1436
1437 val |= vga_rseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE) & 0xdf;
1438 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001439
1440 switch (blank_mode) {
1441 case FB_BLANK_UNBLANK:
1442 case FB_BLANK_NORMAL:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001443 val = 0x00;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001444 break;
1445 case FB_BLANK_VSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001446 val = 0x04;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001447 break;
1448 case FB_BLANK_HSYNC_SUSPEND:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001449 val = 0x02;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001450 break;
1451 case FB_BLANK_POWERDOWN:
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001452 val = 0x06;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001453 break;
1454 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001455 dev_dbg(info->device, "EXIT, returning 1\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001456 return 1;
1457 }
1458
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001459 vga_wgfx(cinfo->regbase, CL_GRE, val);
1460
Linus Torvalds1da177e2005-04-16 15:20:36 -07001461 cinfo->blank_mode = blank_mode;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001462 dev_dbg(info->device, "EXIT, returning 0\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001463
1464 /* Let fbcon do a soft blank for us */
1465 return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
1466}
Krzysztof Helt213d4bd2009-03-31 15:25:04 -07001467
Linus Torvalds1da177e2005-04-16 15:20:36 -07001468/**** END Hardware specific Routines **************************************/
1469/****************************************************************************/
1470/**** BEGIN Internal Routines ***********************************************/
1471
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001472static void init_vgachip(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001473{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001474 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001475 const struct cirrusfb_board_info_rec *bi;
1476
Krzysztof Helt8503df62007-10-16 01:29:08 -07001477 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001478
1479 bi = &cirrusfb_board_info[cinfo->btype];
1480
1481 /* reset board globally */
1482 switch (cinfo->btype) {
1483 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001484 WSFR(cinfo, 0x01);
1485 udelay(500);
1486 WSFR(cinfo, 0x51);
1487 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001488 break;
1489 case BT_PICASSO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001490 WSFR2(cinfo, 0xff);
1491 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001492 break;
1493 case BT_SD64:
1494 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001495 WSFR(cinfo, 0x1f);
1496 udelay(500);
1497 WSFR(cinfo, 0x4f);
1498 udelay(500);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001499 break;
1500 case BT_PICASSO4:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001501 /* disable flickerfixer */
1502 vga_wcrt(cinfo->regbase, CL_CRT51, 0x00);
1503 mdelay(100);
1504 /* from Klaus' NetBSD driver: */
1505 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
1506 /* put blitter into 542x compat */
1507 vga_wgfx(cinfo->regbase, CL_GR33, 0x00);
1508 /* mode */
1509 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001510 break;
1511
1512 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001513 /* from Klaus' NetBSD driver: */
1514 vga_wgfx(cinfo->regbase, CL_GR2F, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001515 break;
1516
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001517 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001518 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001519 case BT_ALPINE:
1520 /* Nothing to do to reset the board. */
1521 break;
1522
1523 default:
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001524 dev_err(info->device, "Warning: Unknown board type\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07001525 break;
1526 }
1527
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001528 /* make sure RAM size set by this point */
1529 assert(info->screen_size > 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001530
1531 /* the P4 is not fully initialized here; I rely on it having been */
1532 /* inited under AmigaOS already, which seems to work just fine */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001533 /* (Klaus advised to do it this way) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001534
1535 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001536 WGen(cinfo, CL_VSSM, 0x10); /* EGS: 0x16 */
1537 WGen(cinfo, CL_POS102, 0x01);
1538 WGen(cinfo, CL_VSSM, 0x08); /* EGS: 0x0e */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001539
1540 if (cinfo->btype != BT_SD64)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001541 WGen(cinfo, CL_VSSM2, 0x01);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001542
Krzysztof Helt8503df62007-10-16 01:29:08 -07001543 /* reset sequencer logic */
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001544 vga_wseq(cinfo->regbase, VGA_SEQ_RESET, 0x03);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001545
Krzysztof Helt8503df62007-10-16 01:29:08 -07001546 /* FullBandwidth (video off) and 8/9 dot clock */
1547 vga_wseq(cinfo->regbase, VGA_SEQ_CLOCK_MODE, 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001548
Krzysztof Helt8503df62007-10-16 01:29:08 -07001549 /* "magic cookie" - doesn't make any sense to me.. */
1550/* vga_wgfx(cinfo->regbase, CL_GRA, 0xce); */
1551 /* unlock all extension registers */
1552 vga_wseq(cinfo->regbase, CL_SEQR6, 0x12);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001553
Linus Torvalds1da177e2005-04-16 15:20:36 -07001554 switch (cinfo->btype) {
1555 case BT_GD5480:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001556 vga_wseq(cinfo->regbase, CL_SEQRF, 0x98);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001557 break;
1558 case BT_ALPINE:
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001559 case BT_LAGUNA:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001560 case BT_LAGUNAB:
Linus Torvalds1da177e2005-04-16 15:20:36 -07001561 break;
1562 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001563 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001564 break;
1565 default:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001566 vga_wseq(cinfo->regbase, CL_SEQR16, 0x0f);
1567 vga_wseq(cinfo->regbase, CL_SEQRF, 0xb0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001568 break;
1569 }
1570 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001571 /* plane mask: nothing */
1572 vga_wseq(cinfo->regbase, VGA_SEQ_PLANE_WRITE, 0xff);
1573 /* character map select: doesn't even matter in gx mode */
1574 vga_wseq(cinfo->regbase, VGA_SEQ_CHARACTER_MAP, 0x00);
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001575 /* memory mode: chain4, ext. memory */
1576 vga_wseq(cinfo->regbase, VGA_SEQ_MEMORY_MODE, 0x0a);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001577
1578 /* controller-internal base address of video memory */
1579 if (bi->init_sr07)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001580 vga_wseq(cinfo->regbase, CL_SEQR7, bi->sr07);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001581
Krzysztof Helt8503df62007-10-16 01:29:08 -07001582 /* vga_wseq(cinfo->regbase, CL_SEQR8, 0x00); */
1583 /* EEPROM control: shouldn't be necessary to write to this at all.. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001584
Krzysztof Helt8503df62007-10-16 01:29:08 -07001585 /* graphics cursor X position (incomplete; position gives rem. 3 bits */
1586 vga_wseq(cinfo->regbase, CL_SEQR10, 0x00);
1587 /* graphics cursor Y position (..."... ) */
1588 vga_wseq(cinfo->regbase, CL_SEQR11, 0x00);
1589 /* graphics cursor attributes */
1590 vga_wseq(cinfo->regbase, CL_SEQR12, 0x00);
1591 /* graphics cursor pattern address */
1592 vga_wseq(cinfo->regbase, CL_SEQR13, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001593
1594 /* writing these on a P4 might give problems.. */
1595 if (cinfo->btype != BT_PICASSO4) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07001596 /* configuration readback and ext. color */
1597 vga_wseq(cinfo->regbase, CL_SEQR17, 0x00);
1598 /* signature generator */
1599 vga_wseq(cinfo->regbase, CL_SEQR18, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001600 }
1601
1602 /* MCLK select etc. */
1603 if (bi->init_sr1f)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001604 vga_wseq(cinfo->regbase, CL_SEQR1F, bi->sr1f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001605
Krzysztof Helt8503df62007-10-16 01:29:08 -07001606 /* Screen A preset row scan: none */
1607 vga_wcrt(cinfo->regbase, VGA_CRTC_PRESET_ROW, 0x00);
1608 /* Text cursor start: disable text cursor */
1609 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_START, 0x20);
1610 /* Text cursor end: - */
1611 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_END, 0x00);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001612 /* text cursor location high: 0 */
1613 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_HI, 0x00);
1614 /* text cursor location low: 0 */
1615 vga_wcrt(cinfo->regbase, VGA_CRTC_CURSOR_LO, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001616
Krzysztof Helt8503df62007-10-16 01:29:08 -07001617 /* Underline Row scanline: - */
1618 vga_wcrt(cinfo->regbase, VGA_CRTC_UNDERLINE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001619 /* ### add 0x40 for text modes with > 30 MHz pixclock */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001620 /* ext. display controls: ext.adr. wrap */
1621 vga_wcrt(cinfo->regbase, CL_CRT1B, 0x02);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001622
Krzysztof Helt8503df62007-10-16 01:29:08 -07001623 /* Set/Reset registes: - */
1624 vga_wgfx(cinfo->regbase, VGA_GFX_SR_VALUE, 0x00);
1625 /* Set/Reset enable: - */
1626 vga_wgfx(cinfo->regbase, VGA_GFX_SR_ENABLE, 0x00);
1627 /* Color Compare: - */
1628 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_VALUE, 0x00);
1629 /* Data Rotate: - */
1630 vga_wgfx(cinfo->regbase, VGA_GFX_DATA_ROTATE, 0x00);
1631 /* Read Map Select: - */
1632 vga_wgfx(cinfo->regbase, VGA_GFX_PLANE_READ, 0x00);
1633 /* Mode: conf. for 16/4/2 color mode, no odd/even, read/write mode 0 */
1634 vga_wgfx(cinfo->regbase, VGA_GFX_MODE, 0x00);
1635 /* Miscellaneous: memory map base address, graphics mode */
1636 vga_wgfx(cinfo->regbase, VGA_GFX_MISC, 0x01);
1637 /* Color Don't care: involve all planes */
1638 vga_wgfx(cinfo->regbase, VGA_GFX_COMPARE_MASK, 0x0f);
1639 /* Bit Mask: no mask at all */
1640 vga_wgfx(cinfo->regbase, VGA_GFX_BIT_MASK, 0xff);
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001641
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001642 if (cinfo->btype == BT_ALPINE || is_laguna(cinfo))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001643 /* (5434 can't have bit 3 set for bitblt) */
1644 vga_wgfx(cinfo->regbase, CL_GRB, 0x20);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001645 else
Krzysztof Helt8503df62007-10-16 01:29:08 -07001646 /* Graphics controller mode extensions: finer granularity,
1647 * 8byte data latches
1648 */
1649 vga_wgfx(cinfo->regbase, CL_GRB, 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001650
Krzysztof Helt8503df62007-10-16 01:29:08 -07001651 vga_wgfx(cinfo->regbase, CL_GRC, 0xff); /* Color Key compare: - */
1652 vga_wgfx(cinfo->regbase, CL_GRD, 0x00); /* Color Key compare mask: - */
1653 vga_wgfx(cinfo->regbase, CL_GRE, 0x00); /* Miscellaneous control: - */
1654 /* Background color byte 1: - */
1655 /* vga_wgfx (cinfo->regbase, CL_GR10, 0x00); */
1656 /* vga_wgfx (cinfo->regbase, CL_GR11, 0x00); */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001657
Krzysztof Helt8503df62007-10-16 01:29:08 -07001658 /* Attribute Controller palette registers: "identity mapping" */
1659 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE0, 0x00);
1660 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE1, 0x01);
1661 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE2, 0x02);
1662 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE3, 0x03);
1663 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE4, 0x04);
1664 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE5, 0x05);
1665 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE6, 0x06);
1666 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE7, 0x07);
1667 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE8, 0x08);
1668 vga_wattr(cinfo->regbase, VGA_ATC_PALETTE9, 0x09);
1669 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEA, 0x0a);
1670 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEB, 0x0b);
1671 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEC, 0x0c);
1672 vga_wattr(cinfo->regbase, VGA_ATC_PALETTED, 0x0d);
1673 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEE, 0x0e);
1674 vga_wattr(cinfo->regbase, VGA_ATC_PALETTEF, 0x0f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001675
Krzysztof Helt8503df62007-10-16 01:29:08 -07001676 /* Attribute Controller mode: graphics mode */
1677 vga_wattr(cinfo->regbase, VGA_ATC_MODE, 0x01);
1678 /* Overscan color reg.: reg. 0 */
1679 vga_wattr(cinfo->regbase, VGA_ATC_OVERSCAN, 0x00);
1680 /* Color Plane enable: Enable all 4 planes */
1681 vga_wattr(cinfo->regbase, VGA_ATC_PLANE_ENABLE, 0x0f);
Krzysztof Helt8503df62007-10-16 01:29:08 -07001682 /* Color Select: - */
1683 vga_wattr(cinfo->regbase, VGA_ATC_COLOR_PAGE, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001684
Krzysztof Helt8503df62007-10-16 01:29:08 -07001685 WGen(cinfo, VGA_PEL_MSK, 0xff); /* Pixel mask: no mask */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001686
Krzysztof Helt8503df62007-10-16 01:29:08 -07001687 /* BLT Start/status: Blitter reset */
1688 vga_wgfx(cinfo->regbase, CL_GR31, 0x04);
1689 /* - " - : "end-of-reset" */
1690 vga_wgfx(cinfo->regbase, CL_GR31, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001691
1692 /* misc... */
Krzysztof Helt8503df62007-10-16 01:29:08 -07001693 WHDR(cinfo, 0); /* Hidden DAC register: - */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001694 return;
1695}
1696
Krzysztof Helt8503df62007-10-16 01:29:08 -07001697static void switch_monitor(struct cirrusfb_info *cinfo, int on)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001698{
1699#ifdef CONFIG_ZORRO /* only works on Zorro boards */
1700 static int IsOn = 0; /* XXX not ok for multiple boards */
1701
Linus Torvalds1da177e2005-04-16 15:20:36 -07001702 if (cinfo->btype == BT_PICASSO4)
1703 return; /* nothing to switch */
1704 if (cinfo->btype == BT_ALPINE)
1705 return; /* nothing to switch */
1706 if (cinfo->btype == BT_GD5480)
1707 return; /* nothing to switch */
1708 if (cinfo->btype == BT_PICASSO) {
1709 if ((on && !IsOn) || (!on && IsOn))
Krzysztof Helt8503df62007-10-16 01:29:08 -07001710 WSFR(cinfo, 0xff);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001711 return;
1712 }
1713 if (on) {
1714 switch (cinfo->btype) {
1715 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001716 WSFR(cinfo, cinfo->SFR | 0x21);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001717 break;
1718 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001719 WSFR(cinfo, cinfo->SFR | 0x28);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001720 break;
1721 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001722 WSFR(cinfo, 0x6f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001723 break;
1724 default: /* do nothing */ break;
1725 }
1726 } else {
1727 switch (cinfo->btype) {
1728 case BT_SD64:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001729 WSFR(cinfo, cinfo->SFR & 0xde);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001730 break;
1731 case BT_PICCOLO:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001732 WSFR(cinfo, cinfo->SFR & 0xd7);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001733 break;
1734 case BT_SPECTRUM:
Krzysztof Helt8503df62007-10-16 01:29:08 -07001735 WSFR(cinfo, 0x4f);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001736 break;
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001737 default: /* do nothing */
1738 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001739 }
1740 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001741#endif /* CONFIG_ZORRO */
1742}
1743
Linus Torvalds1da177e2005-04-16 15:20:36 -07001744/******************************************/
1745/* Linux 2.6-style accelerated functions */
1746/******************************************/
1747
Krzysztof Helt8343c892009-03-31 15:25:11 -07001748static int cirrusfb_sync(struct fb_info *info)
1749{
1750 struct cirrusfb_info *cinfo = info->par;
1751
1752 if (!is_laguna(cinfo)) {
1753 while (vga_rgfx(cinfo->regbase, CL_GR31) & 0x03)
1754 cpu_relax();
1755 }
1756 return 0;
1757}
1758
Krzysztof Helt8503df62007-10-16 01:29:08 -07001759static void cirrusfb_fillrect(struct fb_info *info,
1760 const struct fb_fillrect *region)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001761{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001762 struct fb_fillrect modded;
1763 int vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001764 struct cirrusfb_info *cinfo = info->par;
1765 int m = info->var.bits_per_pixel;
1766 u32 color = (info->fix.visual == FB_VISUAL_TRUECOLOR) ?
1767 cinfo->pseudo_palette[region->color] : region->color;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001768
1769 if (info->state != FBINFO_STATE_RUNNING)
1770 return;
1771 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1772 cfb_fillrect(info, region);
1773 return;
1774 }
1775
1776 vxres = info->var.xres_virtual;
1777 vyres = info->var.yres_virtual;
1778
1779 memcpy(&modded, region, sizeof(struct fb_fillrect));
1780
Krzysztof Helt8503df62007-10-16 01:29:08 -07001781 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001782 modded.dx >= vxres || modded.dy >= vyres)
1783 return;
1784
Krzysztof Helt8503df62007-10-16 01:29:08 -07001785 if (modded.dx + modded.width > vxres)
1786 modded.width = vxres - modded.dx;
1787 if (modded.dy + modded.height > vyres)
1788 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001789
Krzysztof Helt060b6002007-10-16 01:29:13 -07001790 cirrusfb_RectFill(cinfo->regbase,
1791 info->var.bits_per_pixel,
1792 (region->dx * m) / 8, region->dy,
1793 (region->width * m) / 8, region->height,
Krzysztof Helt9e848062009-03-31 15:25:11 -07001794 color, color,
1795 info->fix.line_length, 0x40);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001796}
1797
Krzysztof Helt8503df62007-10-16 01:29:08 -07001798static void cirrusfb_copyarea(struct fb_info *info,
1799 const struct fb_copyarea *area)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001800{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001801 struct fb_copyarea modded;
1802 u32 vxres, vyres;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001803 struct cirrusfb_info *cinfo = info->par;
1804 int m = info->var.bits_per_pixel;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001805
1806 if (info->state != FBINFO_STATE_RUNNING)
1807 return;
1808 if (info->flags & FBINFO_HWACCEL_DISABLED) {
1809 cfb_copyarea(info, area);
1810 return;
1811 }
1812
1813 vxres = info->var.xres_virtual;
1814 vyres = info->var.yres_virtual;
Krzysztof Helt060b6002007-10-16 01:29:13 -07001815 memcpy(&modded, area, sizeof(struct fb_copyarea));
Linus Torvalds1da177e2005-04-16 15:20:36 -07001816
Krzysztof Helt8503df62007-10-16 01:29:08 -07001817 if (!modded.width || !modded.height ||
Linus Torvalds1da177e2005-04-16 15:20:36 -07001818 modded.sx >= vxres || modded.sy >= vyres ||
1819 modded.dx >= vxres || modded.dy >= vyres)
1820 return;
1821
Krzysztof Helt8503df62007-10-16 01:29:08 -07001822 if (modded.sx + modded.width > vxres)
1823 modded.width = vxres - modded.sx;
1824 if (modded.dx + modded.width > vxres)
1825 modded.width = vxres - modded.dx;
1826 if (modded.sy + modded.height > vyres)
1827 modded.height = vyres - modded.sy;
1828 if (modded.dy + modded.height > vyres)
1829 modded.height = vyres - modded.dy;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001830
Krzysztof Helt060b6002007-10-16 01:29:13 -07001831 cirrusfb_BitBLT(cinfo->regbase, info->var.bits_per_pixel,
1832 (area->sx * m) / 8, area->sy,
1833 (area->dx * m) / 8, area->dy,
1834 (area->width * m) / 8, area->height,
Krzysztof Helt0ff1ede2007-10-16 01:29:13 -07001835 info->fix.line_length);
Krzysztof Helt060b6002007-10-16 01:29:13 -07001836
Linus Torvalds1da177e2005-04-16 15:20:36 -07001837}
1838
Krzysztof Helt8503df62007-10-16 01:29:08 -07001839static void cirrusfb_imageblit(struct fb_info *info,
1840 const struct fb_image *image)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001841{
1842 struct cirrusfb_info *cinfo = info->par;
1843
Krzysztof Helt9e848062009-03-31 15:25:11 -07001844 if (info->state != FBINFO_STATE_RUNNING)
1845 return;
1846 if (info->flags & FBINFO_HWACCEL_DISABLED)
1847 cfb_imageblit(info, image);
1848 else {
1849 unsigned size = ((image->width + 7) >> 3) * image->height;
1850 int m = info->var.bits_per_pixel;
1851 u32 fg, bg;
1852
1853 if (info->var.bits_per_pixel == 8) {
1854 fg = image->fg_color;
1855 bg = image->bg_color;
1856 } else {
1857 fg = ((u32 *)(info->pseudo_palette))[image->fg_color];
1858 bg = ((u32 *)(info->pseudo_palette))[image->bg_color];
1859 }
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07001860 cirrusfb_WaitBLT(cinfo->regbase);
Krzysztof Helt9e848062009-03-31 15:25:11 -07001861 /* byte rounded scanlines */
1862 vga_wgfx(cinfo->regbase, CL_GR33, 0x00);
1863 cirrusfb_RectFill(cinfo->regbase,
1864 info->var.bits_per_pixel,
1865 (image->dx * m) / 8, image->dy,
1866 (image->width * m) / 8, image->height,
1867 fg, bg,
1868 info->fix.line_length, 0x04);
1869 memcpy(info->screen_base, image->data, size);
1870 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001871}
1872
Linus Torvalds1da177e2005-04-16 15:20:36 -07001873#ifdef CONFIG_PPC_PREP
1874#define PREP_VIDEO_BASE ((volatile unsigned long) 0xC0000000)
1875#define PREP_IO_BASE ((volatile unsigned char *) 0x80000000)
Krzysztof Helt8503df62007-10-16 01:29:08 -07001876static void get_prep_addrs(unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001877{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001878 *display = PREP_VIDEO_BASE;
1879 *registers = (unsigned long) PREP_IO_BASE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001880}
1881
1882#endif /* CONFIG_PPC_PREP */
1883
Linus Torvalds1da177e2005-04-16 15:20:36 -07001884#ifdef CONFIG_PCI
Krzysztof Helt8503df62007-10-16 01:29:08 -07001885static int release_io_ports;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001886
1887/* Pulled the logic from XFree86 Cirrus driver to get the memory size,
1888 * based on the DRAM bandwidth bit and DRAM bank switching bit. This
1889 * works with 1MB, 2MB and 4MB configurations (which the Motorola boards
1890 * seem to have. */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07001891static unsigned int __devinit cirrusfb_get_memsize(struct fb_info *info,
1892 u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001893{
1894 unsigned long mem;
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001895 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001896
Krzysztof Helt78d780e2009-03-31 15:25:10 -07001897 if (is_laguna(cinfo)) {
Krzysztof Helt55a4ea62009-03-31 15:25:04 -07001898 unsigned char SR14 = vga_rseq(regbase, CL_SEQR14);
1899
1900 mem = ((SR14 & 7) + 1) << 20;
1901 } else {
1902 unsigned char SRF = vga_rseq(regbase, CL_SEQRF);
1903 switch ((SRF & 0x18)) {
1904 case 0x08:
1905 mem = 512 * 1024;
1906 break;
1907 case 0x10:
1908 mem = 1024 * 1024;
1909 break;
1910 /* 64-bit DRAM data bus width; assume 2MB.
1911 * Also indicates 2MB memory on the 5430.
1912 */
1913 case 0x18:
1914 mem = 2048 * 1024;
1915 break;
1916 default:
1917 dev_warn(info->device, "Unknown memory size!\n");
1918 mem = 1024 * 1024;
1919 }
1920 /* If DRAM bank switching is enabled, there must be
1921 * twice as much memory installed. (4MB on the 5434)
1922 */
1923 if (SRF & 0x80)
1924 mem *= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001925 }
Krzysztof Helt8503df62007-10-16 01:29:08 -07001926
Linus Torvalds1da177e2005-04-16 15:20:36 -07001927 /* TODO: Handling of GD5446/5480 (see XF86 sources ...) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001928 return mem;
1929}
1930
Krzysztof Helt8503df62007-10-16 01:29:08 -07001931static void get_pci_addrs(const struct pci_dev *pdev,
1932 unsigned long *display, unsigned long *registers)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001933{
Krzysztof Helt8503df62007-10-16 01:29:08 -07001934 assert(pdev != NULL);
1935 assert(display != NULL);
1936 assert(registers != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001937
Linus Torvalds1da177e2005-04-16 15:20:36 -07001938 *display = 0;
1939 *registers = 0;
1940
1941 /* This is a best-guess for now */
1942
1943 if (pci_resource_flags(pdev, 0) & IORESOURCE_IO) {
1944 *display = pci_resource_start(pdev, 1);
1945 *registers = pci_resource_start(pdev, 0);
1946 } else {
1947 *display = pci_resource_start(pdev, 0);
1948 *registers = pci_resource_start(pdev, 1);
1949 }
1950
Krzysztof Helt8503df62007-10-16 01:29:08 -07001951 assert(*display != 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001952}
1953
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001954static void cirrusfb_pci_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001955{
Krzysztof Helt64beab12008-10-15 22:03:38 -07001956 struct pci_dev *pdev = to_pci_dev(info->device);
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001957 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001958
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07001959 if (cinfo->laguna_mmio == NULL)
1960 iounmap(cinfo->laguna_mmio);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001961 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001962#if 0 /* if system didn't claim this region, we would... */
1963 release_mem_region(0xA0000, 65535);
1964#endif
1965 if (release_io_ports)
1966 release_region(0x3C0, 32);
1967 pci_release_regions(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001968}
1969#endif /* CONFIG_PCI */
1970
Linus Torvalds1da177e2005-04-16 15:20:36 -07001971#ifdef CONFIG_ZORRO
Al Virof5ee0512008-11-01 18:20:39 +00001972static void cirrusfb_zorro_unmap(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001973{
Al Virod91f5bb2007-10-17 00:27:18 +01001974 struct cirrusfb_info *cinfo = info->par;
Krzysztof Helt64beab12008-10-15 22:03:38 -07001975 struct zorro_dev *zdev = to_zorro_dev(info->device);
1976
1977 zorro_release_device(zdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001978
1979 if (cinfo->btype == BT_PICASSO4) {
1980 cinfo->regbase -= 0x600000;
Krzysztof Helt8503df62007-10-16 01:29:08 -07001981 iounmap((void *)cinfo->regbase);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001982 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001983 } else {
Krzysztof Helt64beab12008-10-15 22:03:38 -07001984 if (zorro_resource_start(zdev) > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07001985 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001986 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07001987}
1988#endif /* CONFIG_ZORRO */
1989
Krzysztof Helt48c329e2009-03-31 15:25:08 -07001990/* function table of the above functions */
1991static struct fb_ops cirrusfb_ops = {
1992 .owner = THIS_MODULE,
1993 .fb_open = cirrusfb_open,
1994 .fb_release = cirrusfb_release,
1995 .fb_setcolreg = cirrusfb_setcolreg,
1996 .fb_check_var = cirrusfb_check_var,
1997 .fb_set_par = cirrusfb_set_par,
1998 .fb_pan_display = cirrusfb_pan_display,
1999 .fb_blank = cirrusfb_blank,
2000 .fb_fillrect = cirrusfb_fillrect,
2001 .fb_copyarea = cirrusfb_copyarea,
Krzysztof Helt8343c892009-03-31 15:25:11 -07002002 .fb_sync = cirrusfb_sync,
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002003 .fb_imageblit = cirrusfb_imageblit,
2004};
2005
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002006static int __devinit cirrusfb_set_fbinfo(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002007{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002008 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002009 struct fb_var_screeninfo *var = &info->var;
2010
Linus Torvalds1da177e2005-04-16 15:20:36 -07002011 info->pseudo_palette = cinfo->pseudo_palette;
2012 info->flags = FBINFO_DEFAULT
2013 | FBINFO_HWACCEL_XPAN
2014 | FBINFO_HWACCEL_YPAN
2015 | FBINFO_HWACCEL_FILLRECT
Krzysztof Helt9e848062009-03-31 15:25:11 -07002016 | FBINFO_HWACCEL_IMAGEBLIT
Linus Torvalds1da177e2005-04-16 15:20:36 -07002017 | FBINFO_HWACCEL_COPYAREA;
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002018 if (noaccel || is_laguna(cinfo))
Linus Torvalds1da177e2005-04-16 15:20:36 -07002019 info->flags |= FBINFO_HWACCEL_DISABLED;
2020 info->fbops = &cirrusfb_ops;
Krzysztof Helt9e848062009-03-31 15:25:11 -07002021
Linus Torvalds1da177e2005-04-16 15:20:36 -07002022 if (cinfo->btype == BT_GD5480) {
2023 if (var->bits_per_pixel == 16)
2024 info->screen_base += 1 * MB_;
Krzysztof Helt1cea9a92008-10-15 22:03:37 -07002025 if (var->bits_per_pixel == 32)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002026 info->screen_base += 2 * MB_;
2027 }
2028
2029 /* Fill fix common fields */
2030 strlcpy(info->fix.id, cirrusfb_board_info[cinfo->btype].name,
2031 sizeof(info->fix.id));
2032
2033 /* monochrome: only 1 memory plane */
2034 /* 8 bit and above: Use whole memory area */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002035 info->fix.smem_len = info->screen_size;
2036 if (var->bits_per_pixel == 1)
2037 info->fix.smem_len /= 4;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002038 info->fix.type_aux = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002039 info->fix.xpanstep = 1;
2040 info->fix.ypanstep = 1;
2041 info->fix.ywrapstep = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002042
2043 /* FIXME: map region at 0xB8000 if available, fill in here */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002044 info->fix.mmio_len = 0;
2045 info->fix.accel = FB_ACCEL_NONE;
2046
2047 fb_alloc_cmap(&info->cmap, 256, 0);
2048
2049 return 0;
2050}
2051
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002052static int __devinit cirrusfb_register(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002053{
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002054 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002055 int err;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002056
2057 /* sanity checks */
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002058 assert(cinfo->btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002059
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002060 /* set all the vital stuff */
2061 cirrusfb_set_fbinfo(info);
2062
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002063 dev_dbg(info->device, "(RAM start set to: 0x%p)\n", info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002064
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002065 err = fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 8);
2066 if (!err) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002067 dev_dbg(info->device, "wrong initial video mode\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002068 err = -EINVAL;
2069 goto err_dealloc_cmap;
2070 }
2071
Linus Torvalds1da177e2005-04-16 15:20:36 -07002072 info->var.activate = FB_ACTIVATE_NOW;
2073
Krzysztof Helt99a45842009-03-31 15:25:09 -07002074 err = cirrusfb_check_var(&info->var, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002075 if (err < 0) {
2076 /* should never happen */
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002077 dev_dbg(info->device,
2078 "choking on default var... umm, no good.\n");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002079 goto err_dealloc_cmap;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002080 }
2081
Linus Torvalds1da177e2005-04-16 15:20:36 -07002082 err = register_framebuffer(info);
2083 if (err < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002084 dev_err(info->device,
2085 "could not register fb device; err = %d!\n", err);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002086 goto err_dealloc_cmap;
2087 }
2088
Linus Torvalds1da177e2005-04-16 15:20:36 -07002089 return 0;
2090
2091err_dealloc_cmap:
2092 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002093 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002094 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002095 return err;
2096}
2097
Krzysztof Helt8503df62007-10-16 01:29:08 -07002098static void __devexit cirrusfb_cleanup(struct fb_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002099{
2100 struct cirrusfb_info *cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002101
Krzysztof Helt8503df62007-10-16 01:29:08 -07002102 switch_monitor(cinfo, 0);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002103 unregister_framebuffer(info);
2104 fb_dealloc_cmap(&info->cmap);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002105 dev_dbg(info->device, "Framebuffer unregistered\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002106 cinfo->unmap(info);
Krzysztof Helt060b6002007-10-16 01:29:13 -07002107 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002108}
2109
Linus Torvalds1da177e2005-04-16 15:20:36 -07002110#ifdef CONFIG_PCI
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002111static int __devinit cirrusfb_pci_register(struct pci_dev *pdev,
2112 const struct pci_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002113{
2114 struct cirrusfb_info *cinfo;
2115 struct fb_info *info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002116 unsigned long board_addr, board_size;
2117 int ret;
2118
2119 ret = pci_enable_device(pdev);
2120 if (ret < 0) {
2121 printk(KERN_ERR "cirrusfb: Cannot enable PCI device\n");
2122 goto err_out;
2123 }
2124
2125 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &pdev->dev);
2126 if (!info) {
2127 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
2128 ret = -ENOMEM;
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002129 goto err_out;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002130 }
2131
2132 cinfo = info->par;
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002133 cinfo->btype = (enum cirrus_board) ent->driver_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002134
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002135 dev_dbg(info->device,
2136 " Found PCI device, base address 0 is 0x%Lx, btype set to %d\n",
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002137 (unsigned long long)pdev->resource[0].start, cinfo->btype);
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002138 dev_dbg(info->device, " base address 1 is 0x%Lx\n",
2139 (unsigned long long)pdev->resource[1].start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002140
Krzysztof Helt8503df62007-10-16 01:29:08 -07002141 if (isPReP) {
2142 pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, 0x00000000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002143#ifdef CONFIG_PPC_PREP
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002144 get_prep_addrs(&board_addr, &info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002145#endif
Krzysztof Helt8503df62007-10-16 01:29:08 -07002146 /* PReP dies if we ioremap the IO registers, but it works w/out... */
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002147 cinfo->regbase = (char __iomem *) info->fix.mmio_start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002148 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002149 dev_dbg(info->device,
2150 "Attempt to get PCI info for Cirrus Graphics Card\n");
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002151 get_pci_addrs(pdev, &board_addr, &info->fix.mmio_start);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002152 /* FIXME: this forces VGA. alternatives? */
2153 cinfo->regbase = NULL;
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002154 cinfo->laguna_mmio = ioremap(info->fix.mmio_start, 0x1000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002155 }
2156
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002157 dev_dbg(info->device, "Board address: 0x%lx, register address: 0x%lx\n",
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002158 board_addr, info->fix.mmio_start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002159
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002160 board_size = (cinfo->btype == BT_GD5480) ?
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002161 32 * MB_ : cirrusfb_get_memsize(info, cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002162
2163 ret = pci_request_regions(pdev, "cirrusfb");
Krzysztof Helt8503df62007-10-16 01:29:08 -07002164 if (ret < 0) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002165 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2166 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002167 goto err_release_fb;
2168 }
2169#if 0 /* if the system didn't claim this region, we would... */
2170 if (!request_mem_region(0xA0000, 65535, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002171 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2172 0xA0000L);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002173 ret = -EBUSY;
2174 goto err_release_regions;
2175 }
2176#endif
2177 if (request_region(0x3C0, 32, "cirrusfb"))
2178 release_io_ports = 1;
2179
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002180 info->screen_base = ioremap(board_addr, board_size);
2181 if (!info->screen_base) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002182 ret = -EIO;
2183 goto err_release_legacy;
2184 }
2185
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002186 info->fix.smem_start = board_addr;
2187 info->screen_size = board_size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002188 cinfo->unmap = cirrusfb_pci_unmap;
2189
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002190 dev_info(info->device,
2191 "Cirrus Logic chipset on PCI bus, RAM (%lu kB) at 0x%lx\n",
2192 info->screen_size >> 10, board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002193 pci_set_drvdata(pdev, info);
2194
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002195 ret = cirrusfb_register(info);
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002196 if (!ret)
2197 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002198
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002199 pci_set_drvdata(pdev, NULL);
2200 iounmap(info->screen_base);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002201err_release_legacy:
2202 if (release_io_ports)
2203 release_region(0x3C0, 32);
2204#if 0
2205 release_mem_region(0xA0000, 65535);
2206err_release_regions:
2207#endif
2208 pci_release_regions(pdev);
2209err_release_fb:
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002210 if (cinfo->laguna_mmio != NULL)
Krzysztof Helt6e30fc02009-03-31 15:25:05 -07002211 iounmap(cinfo->laguna_mmio);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002212 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002213err_out:
2214 return ret;
2215}
2216
Krzysztof Helt8503df62007-10-16 01:29:08 -07002217static void __devexit cirrusfb_pci_unregister(struct pci_dev *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002218{
2219 struct fb_info *info = pci_get_drvdata(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002220
Krzysztof Helt8503df62007-10-16 01:29:08 -07002221 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002222}
2223
2224static struct pci_driver cirrusfb_pci_driver = {
2225 .name = "cirrusfb",
2226 .id_table = cirrusfb_pci_table,
2227 .probe = cirrusfb_pci_register,
2228 .remove = __devexit_p(cirrusfb_pci_unregister),
2229#ifdef CONFIG_PM
2230#if 0
2231 .suspend = cirrusfb_pci_suspend,
2232 .resume = cirrusfb_pci_resume,
2233#endif
2234#endif
2235};
2236#endif /* CONFIG_PCI */
2237
Linus Torvalds1da177e2005-04-16 15:20:36 -07002238#ifdef CONFIG_ZORRO
Krzysztof Heltc395d3e2008-10-15 22:03:39 -07002239static int __devinit cirrusfb_zorro_register(struct zorro_dev *z,
2240 const struct zorro_device_id *ent)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002241{
2242 struct cirrusfb_info *cinfo;
2243 struct fb_info *info;
Krzysztof Helt7345de32007-10-16 01:29:11 -07002244 enum cirrus_board btype;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002245 struct zorro_dev *z2 = NULL;
2246 unsigned long board_addr, board_size, size;
2247 int ret;
2248
2249 btype = ent->driver_data;
2250 if (cirrusfb_zorro_table2[btype].id2)
2251 z2 = zorro_find_device(cirrusfb_zorro_table2[btype].id2, NULL);
2252 size = cirrusfb_zorro_table2[btype].size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002253
2254 info = framebuffer_alloc(sizeof(struct cirrusfb_info), &z->dev);
2255 if (!info) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002256 printk(KERN_ERR "cirrusfb: could not allocate memory\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002257 ret = -ENOMEM;
2258 goto err_out;
2259 }
2260
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002261 dev_info(info->device, "%s board detected\n",
2262 cirrusfb_board_info[btype].name);
2263
Linus Torvalds1da177e2005-04-16 15:20:36 -07002264 cinfo = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002265 cinfo->btype = btype;
2266
Al Viro36ea96a2007-10-27 19:46:58 +01002267 assert(z);
Krzysztof Helt8503df62007-10-16 01:29:08 -07002268 assert(btype != BT_NONE);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002269
Linus Torvalds1da177e2005-04-16 15:20:36 -07002270 board_addr = zorro_resource_start(z);
2271 board_size = zorro_resource_len(z);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002272 info->screen_size = size;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002273
2274 if (!zorro_request_device(z, "cirrusfb")) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002275 dev_err(info->device, "cannot reserve region 0x%lx, abort\n",
2276 board_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002277 ret = -EBUSY;
2278 goto err_release_fb;
2279 }
2280
Linus Torvalds1da177e2005-04-16 15:20:36 -07002281 ret = -EIO;
2282
2283 if (btype == BT_PICASSO4) {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002284 dev_info(info->device, " REG at $%lx\n", board_addr + 0x600000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002285
2286 /* To be precise, for the P4 this is not the */
2287 /* begin of the board, but the begin of RAM. */
2288 /* for P4, map in its address space in 2 chunks (### TEST! ) */
2289 /* (note the ugly hardcoded 16M number) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002290 cinfo->regbase = ioremap(board_addr, 16777216);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002291 if (!cinfo->regbase)
2292 goto err_release_region;
2293
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002294 dev_dbg(info->device, "Virtual address for board set to: $%p\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002295 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002296 cinfo->regbase += 0x600000;
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002297 info->fix.mmio_start = board_addr + 0x600000;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002298
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002299 info->fix.smem_start = board_addr + 16777216;
2300 info->screen_base = ioremap(info->fix.smem_start, 16777216);
2301 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002302 goto err_unmap_regbase;
2303 } else {
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002304 dev_info(info->device, " REG at $%lx\n",
2305 (unsigned long) z2->resource.start);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002306
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002307 info->fix.smem_start = board_addr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002308 if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002309 info->screen_base = ioremap(board_addr, board_size);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002310 else
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002311 info->screen_base = (caddr_t) ZTWO_VADDR(board_addr);
2312 if (!info->screen_base)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002313 goto err_release_region;
2314
2315 /* set address for REG area of board */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002316 cinfo->regbase = (caddr_t) ZTWO_VADDR(z2->resource.start);
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002317 info->fix.mmio_start = z2->resource.start;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002318
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002319 dev_dbg(info->device, "Virtual address for board set to: $%p\n",
Krzysztof Helt8503df62007-10-16 01:29:08 -07002320 cinfo->regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002321 }
2322 cinfo->unmap = cirrusfb_zorro_unmap;
2323
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002324 dev_info(info->device,
2325 "Cirrus Logic chipset on Zorro bus, RAM (%lu MB) at $%lx\n",
2326 board_size / MB_, board_addr);
2327
Linus Torvalds1da177e2005-04-16 15:20:36 -07002328 zorro_set_drvdata(z, info);
2329
Al Virod91f5bb2007-10-17 00:27:18 +01002330 ret = cirrusfb_register(info);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002331 if (ret) {
2332 if (btype == BT_PICASSO4) {
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002333 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002334 iounmap(cinfo->regbase - 0x600000);
2335 } else if (board_addr > 0x01000000)
Krzysztof Helt9199ec52007-10-16 01:29:12 -07002336 iounmap(info->screen_base);
Amol Ladd8b8c0a2006-12-08 02:40:13 -08002337 }
2338 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002339
2340err_unmap_regbase:
2341 /* Parental advisory: explicit hack */
2342 iounmap(cinfo->regbase - 0x600000);
2343err_release_region:
2344 release_region(board_addr, board_size);
2345err_release_fb:
2346 framebuffer_release(info);
2347err_out:
2348 return ret;
2349}
2350
2351void __devexit cirrusfb_zorro_unregister(struct zorro_dev *z)
2352{
2353 struct fb_info *info = zorro_get_drvdata(z);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002354
Krzysztof Helt8503df62007-10-16 01:29:08 -07002355 cirrusfb_cleanup(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002356}
2357
2358static struct zorro_driver cirrusfb_zorro_driver = {
2359 .name = "cirrusfb",
2360 .id_table = cirrusfb_zorro_table,
2361 .probe = cirrusfb_zorro_register,
2362 .remove = __devexit_p(cirrusfb_zorro_unregister),
2363};
2364#endif /* CONFIG_ZORRO */
2365
Linus Torvalds1da177e2005-04-16 15:20:36 -07002366#ifndef MODULE
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002367static int __init cirrusfb_setup(char *options)
2368{
Vlada Pericee119402008-11-19 15:36:45 -08002369 char *this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002370
Linus Torvalds1da177e2005-04-16 15:20:36 -07002371 if (!options || !*options)
2372 return 0;
2373
Krzysztof Helt8503df62007-10-16 01:29:08 -07002374 while ((this_opt = strsep(&options, ",")) != NULL) {
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002375 if (!*this_opt)
2376 continue;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002377
Linus Torvalds1da177e2005-04-16 15:20:36 -07002378 if (!strcmp(this_opt, "noaccel"))
2379 noaccel = 1;
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002380 else if (!strncmp(this_opt, "mode:", 5))
2381 mode_option = this_opt + 5;
2382 else
2383 mode_option = this_opt;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002384 }
2385 return 0;
2386}
2387#endif
2388
Linus Torvalds1da177e2005-04-16 15:20:36 -07002389 /*
2390 * Modularization
2391 */
2392
2393MODULE_AUTHOR("Copyright 1999,2000 Jeff Garzik <jgarzik@pobox.com>");
2394MODULE_DESCRIPTION("Accelerated FBDev driver for Cirrus Logic chips");
2395MODULE_LICENSE("GPL");
2396
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002397static int __init cirrusfb_init(void)
2398{
2399 int error = 0;
2400
2401#ifndef MODULE
2402 char *option = NULL;
2403
2404 if (fb_get_options("cirrusfb", &option))
2405 return -ENODEV;
2406 cirrusfb_setup(option);
2407#endif
2408
2409#ifdef CONFIG_ZORRO
2410 error |= zorro_register_driver(&cirrusfb_zorro_driver);
2411#endif
2412#ifdef CONFIG_PCI
2413 error |= pci_register_driver(&cirrusfb_pci_driver);
2414#endif
2415 return error;
2416}
2417
Krzysztof Helt8503df62007-10-16 01:29:08 -07002418static void __exit cirrusfb_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002419{
2420#ifdef CONFIG_PCI
2421 pci_unregister_driver(&cirrusfb_pci_driver);
2422#endif
2423#ifdef CONFIG_ZORRO
2424 zorro_unregister_driver(&cirrusfb_zorro_driver);
2425#endif
2426}
2427
2428module_init(cirrusfb_init);
2429
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002430module_param(mode_option, charp, 0);
2431MODULE_PARM_DESC(mode_option, "Initial video mode e.g. '648x480-8@60'");
Krzysztof Helt55a0dd82008-10-15 22:03:41 -07002432module_param(noaccel, bool, 0);
2433MODULE_PARM_DESC(noaccel, "Disable acceleration");
Krzysztof Helta1d35a72008-10-15 22:03:38 -07002434
Linus Torvalds1da177e2005-04-16 15:20:36 -07002435#ifdef MODULE
2436module_exit(cirrusfb_exit);
2437#endif
2438
Linus Torvalds1da177e2005-04-16 15:20:36 -07002439/**********************************************************************/
2440/* about the following functions - I have used the same names for the */
2441/* functions as Markus Wild did in his Retina driver for NetBSD as */
2442/* they just made sense for this purpose. Apart from that, I wrote */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002443/* these functions myself. */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002444/**********************************************************************/
2445
2446/*** WGen() - write into one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002447static void WGen(const struct cirrusfb_info *cinfo,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002448 int regnum, unsigned char val)
2449{
2450 unsigned long regofs = 0;
2451
2452 if (cinfo->btype == BT_PICASSO) {
2453 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002454/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2455 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002456 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2457 regofs = 0xfff;
2458 }
2459
Krzysztof Helt8503df62007-10-16 01:29:08 -07002460 vga_w(cinfo->regbase, regofs + regnum, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002461}
2462
2463/*** RGen() - read out one of the external/general registers ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002464static unsigned char RGen(const struct cirrusfb_info *cinfo, int regnum)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002465{
2466 unsigned long regofs = 0;
2467
2468 if (cinfo->btype == BT_PICASSO) {
2469 /* Picasso II specific hack */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002470/* if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D ||
2471 regnum == CL_VSSM2) */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002472 if (regnum == VGA_PEL_IR || regnum == VGA_PEL_D)
2473 regofs = 0xfff;
2474 }
2475
Krzysztof Helt8503df62007-10-16 01:29:08 -07002476 return vga_r(cinfo->regbase, regofs + regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002477}
2478
2479/*** AttrOn() - turn on VideoEnable for Attribute controller ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002480static void AttrOn(const struct cirrusfb_info *cinfo)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002481{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002482 assert(cinfo != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002483
Krzysztof Helt8503df62007-10-16 01:29:08 -07002484 if (vga_rcrt(cinfo->regbase, CL_CRT24) & 0x80) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002485 /* if we're just in "write value" mode, write back the */
2486 /* same value as before to not modify anything */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002487 vga_w(cinfo->regbase, VGA_ATT_IW,
2488 vga_r(cinfo->regbase, VGA_ATT_R));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002489 }
2490 /* turn on video bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002491/* vga_w(cinfo->regbase, VGA_ATT_IW, 0x20); */
2492 vga_w(cinfo->regbase, VGA_ATT_IW, 0x33);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002493
2494 /* dummy write on Reg0 to be on "write index" mode next time */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002495 vga_w(cinfo->regbase, VGA_ATT_IW, 0x00);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002496}
2497
2498/*** WHDR() - write into the Hidden DAC register ***/
2499/* as the HDR is the only extension register that requires special treatment
2500 * (the other extension registers are accessible just like the "ordinary"
2501 * registers of their functional group) here is a specialized routine for
2502 * accessing the HDR
2503 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002504static void WHDR(const struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002505{
2506 unsigned char dummy;
2507
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002508 if (is_laguna(cinfo))
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07002509 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002510 if (cinfo->btype == BT_PICASSO) {
2511 /* Klaus' hint for correct access to HDR on some boards */
2512 /* first write 0 to pixel mask (3c6) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002513 WGen(cinfo, VGA_PEL_MSK, 0x00);
2514 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002515 /* next read dummy from pixel address (3c8) */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002516 dummy = RGen(cinfo, VGA_PEL_IW);
2517 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002518 }
2519 /* now do the usual stuff to access the HDR */
2520
Krzysztof Helt8503df62007-10-16 01:29:08 -07002521 dummy = RGen(cinfo, VGA_PEL_MSK);
2522 udelay(200);
2523 dummy = RGen(cinfo, VGA_PEL_MSK);
2524 udelay(200);
2525 dummy = RGen(cinfo, VGA_PEL_MSK);
2526 udelay(200);
2527 dummy = RGen(cinfo, VGA_PEL_MSK);
2528 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002529
Krzysztof Helt8503df62007-10-16 01:29:08 -07002530 WGen(cinfo, VGA_PEL_MSK, val);
2531 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002532
2533 if (cinfo->btype == BT_PICASSO) {
2534 /* now first reset HDR access counter */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002535 dummy = RGen(cinfo, VGA_PEL_IW);
2536 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002537
2538 /* and at the end, restore the mask value */
2539 /* ## is this mask always 0xff? */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002540 WGen(cinfo, VGA_PEL_MSK, 0xff);
2541 udelay(200);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002542 }
2543}
2544
Linus Torvalds1da177e2005-04-16 15:20:36 -07002545/*** WSFR() - write to the "special function register" (SFR) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002546static void WSFR(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002547{
2548#ifdef CONFIG_ZORRO
Krzysztof Helt8503df62007-10-16 01:29:08 -07002549 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002550 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002551 z_writeb(val, cinfo->regbase + 0x8000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002552#endif
2553}
2554
2555/* The Picasso has a second register for switching the monitor bit */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002556static void WSFR2(struct cirrusfb_info *cinfo, unsigned char val)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002557{
2558#ifdef CONFIG_ZORRO
2559 /* writing an arbitrary value to this one causes the monitor switcher */
2560 /* to flip to Amiga display */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002561 assert(cinfo->regbase != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002562 cinfo->SFR = val;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002563 z_writeb(val, cinfo->regbase + 0x9000);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002564#endif
2565}
2566
Linus Torvalds1da177e2005-04-16 15:20:36 -07002567/*** WClut - set CLUT entry (range: 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002568static void WClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002569 unsigned char green, unsigned char blue)
2570{
2571 unsigned int data = VGA_PEL_D;
2572
2573 /* address write mode register is not translated.. */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002574 vga_w(cinfo->regbase, VGA_PEL_IW, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002575
2576 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
Krzysztof Helt1b48cb52009-03-31 15:25:08 -07002577 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480 ||
Krzysztof Helt78d780e2009-03-31 15:25:10 -07002578 is_laguna(cinfo)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002579 /* but DAC data register IS, at least for Picasso II */
2580 if (cinfo->btype == BT_PICASSO)
2581 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002582 vga_w(cinfo->regbase, data, red);
2583 vga_w(cinfo->regbase, data, green);
2584 vga_w(cinfo->regbase, data, blue);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002585 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002586 vga_w(cinfo->regbase, data, blue);
2587 vga_w(cinfo->regbase, data, green);
2588 vga_w(cinfo->regbase, data, red);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002589 }
2590}
2591
Linus Torvalds1da177e2005-04-16 15:20:36 -07002592#if 0
2593/*** RClut - read CLUT entry (range 0..63) ***/
Krzysztof Helt8503df62007-10-16 01:29:08 -07002594static void RClut(struct cirrusfb_info *cinfo, unsigned char regnum, unsigned char *red,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002595 unsigned char *green, unsigned char *blue)
2596{
2597 unsigned int data = VGA_PEL_D;
2598
Krzysztof Helt8503df62007-10-16 01:29:08 -07002599 vga_w(cinfo->regbase, VGA_PEL_IR, regnum);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002600
2601 if (cinfo->btype == BT_PICASSO || cinfo->btype == BT_PICASSO4 ||
2602 cinfo->btype == BT_ALPINE || cinfo->btype == BT_GD5480) {
2603 if (cinfo->btype == BT_PICASSO)
2604 data += 0xfff;
Krzysztof Helt8503df62007-10-16 01:29:08 -07002605 *red = vga_r(cinfo->regbase, data);
2606 *green = vga_r(cinfo->regbase, data);
2607 *blue = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002608 } else {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002609 *blue = vga_r(cinfo->regbase, data);
2610 *green = vga_r(cinfo->regbase, data);
2611 *red = vga_r(cinfo->regbase, data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002612 }
2613}
2614#endif
2615
Linus Torvalds1da177e2005-04-16 15:20:36 -07002616/*******************************************************************
2617 cirrusfb_WaitBLT()
2618
2619 Wait for the BitBLT engine to complete a possible earlier job
2620*********************************************************************/
2621
2622/* FIXME: use interrupts instead */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002623static void cirrusfb_WaitBLT(u8 __iomem *regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002624{
Krzysztof Helt8503df62007-10-16 01:29:08 -07002625 while (vga_rgfx(regbase, CL_GR31) & 0x08)
Krzysztof Helt48c329e2009-03-31 15:25:08 -07002626 cpu_relax();
Linus Torvalds1da177e2005-04-16 15:20:36 -07002627}
2628
2629/*******************************************************************
2630 cirrusfb_BitBLT()
2631
2632 perform accelerated "scrolling"
2633********************************************************************/
2634
Krzysztof Helt8343c892009-03-31 15:25:11 -07002635static void cirrusfb_set_blitter(u8 __iomem *regbase,
2636 u_short nwidth, u_short nheight,
2637 u_long nsrc, u_long ndest,
2638 u_short bltmode, u_short line_length)
2639
Linus Torvalds1da177e2005-04-16 15:20:36 -07002640{
Linus Torvalds1da177e2005-04-16 15:20:36 -07002641 /* pitch: set to line_length */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002642 /* dest pitch low */
2643 vga_wgfx(regbase, CL_GR24, line_length & 0xff);
2644 /* dest pitch hi */
2645 vga_wgfx(regbase, CL_GR25, line_length >> 8);
2646 /* source pitch low */
2647 vga_wgfx(regbase, CL_GR26, line_length & 0xff);
2648 /* source pitch hi */
2649 vga_wgfx(regbase, CL_GR27, line_length >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002650
2651 /* BLT width: actual number of pixels - 1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002652 /* BLT width low */
2653 vga_wgfx(regbase, CL_GR20, nwidth & 0xff);
2654 /* BLT width hi */
2655 vga_wgfx(regbase, CL_GR21, nwidth >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002656
2657 /* BLT height: actual number of lines -1 */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002658 /* BLT height low */
2659 vga_wgfx(regbase, CL_GR22, nheight & 0xff);
2660 /* BLT width hi */
2661 vga_wgfx(regbase, CL_GR23, nheight >> 8);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002662
2663 /* BLT destination */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002664 /* BLT dest low */
2665 vga_wgfx(regbase, CL_GR28, (u_char) (ndest & 0xff));
2666 /* BLT dest mid */
2667 vga_wgfx(regbase, CL_GR29, (u_char) (ndest >> 8));
2668 /* BLT dest hi */
2669 vga_wgfx(regbase, CL_GR2A, (u_char) (ndest >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002670
2671 /* BLT source */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002672 /* BLT src low */
2673 vga_wgfx(regbase, CL_GR2C, (u_char) (nsrc & 0xff));
2674 /* BLT src mid */
2675 vga_wgfx(regbase, CL_GR2D, (u_char) (nsrc >> 8));
2676 /* BLT src hi */
2677 vga_wgfx(regbase, CL_GR2E, (u_char) (nsrc >> 16));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002678
2679 /* BLT mode */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002680 vga_wgfx(regbase, CL_GR30, bltmode); /* BLT mode */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002681
2682 /* BLT ROP: SrcCopy */
Krzysztof Helt8503df62007-10-16 01:29:08 -07002683 vga_wgfx(regbase, CL_GR32, 0x0d); /* BLT ROP */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002684
2685 /* and finally: GO! */
Krzysztof Helt8343c892009-03-31 15:25:11 -07002686 vga_wgfx(regbase, CL_GR31, 0x82); /* BLT Start/status */
2687}
2688
2689/*******************************************************************
2690 cirrusfb_BitBLT()
2691
2692 perform accelerated "scrolling"
2693********************************************************************/
2694
2695static void cirrusfb_BitBLT(u8 __iomem *regbase, int bits_per_pixel,
2696 u_short curx, u_short cury,
2697 u_short destx, u_short desty,
2698 u_short width, u_short height,
2699 u_short line_length)
2700{
2701 u_short nwidth = width - 1;
2702 u_short nheight = height - 1;
2703 u_long nsrc, ndest;
2704 u_char bltmode;
2705
2706 bltmode = 0x00;
2707 /* if source adr < dest addr, do the Blt backwards */
2708 if (cury <= desty) {
2709 if (cury == desty) {
2710 /* if src and dest are on the same line, check x */
2711 if (curx < destx)
2712 bltmode |= 0x01;
2713 } else
2714 bltmode |= 0x01;
2715 }
2716 /* standard case: forward blitting */
2717 nsrc = (cury * line_length) + curx;
2718 ndest = (desty * line_length) + destx;
2719 if (bltmode) {
2720 /* this means start addresses are at the end,
2721 * counting backwards
2722 */
2723 nsrc += nheight * line_length + nwidth;
2724 ndest += nheight * line_length + nwidth;
2725 }
2726
2727 cirrusfb_WaitBLT(regbase);
2728
2729 cirrusfb_set_blitter(regbase, nwidth, nheight,
2730 nsrc, ndest, bltmode, line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002731}
2732
Linus Torvalds1da177e2005-04-16 15:20:36 -07002733/*******************************************************************
2734 cirrusfb_RectFill()
2735
2736 perform accelerated rectangle fill
2737********************************************************************/
2738
Krzysztof Helt8503df62007-10-16 01:29:08 -07002739static void cirrusfb_RectFill(u8 __iomem *regbase, int bits_per_pixel,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002740 u_short x, u_short y, u_short width, u_short height,
Krzysztof Helt9e848062009-03-31 15:25:11 -07002741 u32 fg_color, u32 bg_color, u_short line_length,
2742 u_char blitmode)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002743{
Krzysztof Helt8343c892009-03-31 15:25:11 -07002744 u_long ndest = (y * line_length) + x;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002745 u_char op;
2746
Krzysztof Helt8503df62007-10-16 01:29:08 -07002747 cirrusfb_WaitBLT(regbase);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002748
Linus Torvalds1da177e2005-04-16 15:20:36 -07002749 /* This is a ColorExpand Blt, using the */
2750 /* same color for foreground and background */
Krzysztof Helt9e848062009-03-31 15:25:11 -07002751 vga_wgfx(regbase, VGA_GFX_SR_VALUE, bg_color);
2752 vga_wgfx(regbase, VGA_GFX_SR_ENABLE, fg_color);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002753
Krzysztof Helt9e848062009-03-31 15:25:11 -07002754 op = 0x80;
Krzysztof Helt8343c892009-03-31 15:25:11 -07002755 if (bits_per_pixel >= 16) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002756 vga_wgfx(regbase, CL_GR10, bg_color >> 8);
2757 vga_wgfx(regbase, CL_GR11, fg_color >> 8);
2758 op = 0x90;
Krzysztof Helt8343c892009-03-31 15:25:11 -07002759 }
2760 if (bits_per_pixel == 32) {
Krzysztof Helt9e848062009-03-31 15:25:11 -07002761 vga_wgfx(regbase, CL_GR12, bg_color >> 16);
2762 vga_wgfx(regbase, CL_GR13, fg_color >> 16);
2763 vga_wgfx(regbase, CL_GR14, bg_color >> 24);
2764 vga_wgfx(regbase, CL_GR15, fg_color >> 24);
2765 op = 0xb0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002766 }
Krzysztof Helt8343c892009-03-31 15:25:11 -07002767 cirrusfb_set_blitter(regbase, width - 1, height - 1,
Krzysztof Helt9e848062009-03-31 15:25:11 -07002768 0, ndest, op | blitmode, line_length);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002769}
2770
Linus Torvalds1da177e2005-04-16 15:20:36 -07002771/**************************************************************************
2772 * bestclock() - determine closest possible clock lower(?) than the
2773 * desired pixel clock
2774 **************************************************************************/
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002775static void bestclock(long freq, int *nom, int *den, int *div)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002776{
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002777 int n, d;
2778 long h, diff;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002779
Krzysztof Helt8503df62007-10-16 01:29:08 -07002780 assert(nom != NULL);
2781 assert(den != NULL);
2782 assert(div != NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002783
2784 *nom = 0;
2785 *den = 0;
2786 *div = 0;
2787
Linus Torvalds1da177e2005-04-16 15:20:36 -07002788 if (freq < 8000)
2789 freq = 8000;
2790
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002791 diff = freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002792
2793 for (n = 32; n < 128; n++) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002794 int s = 0;
2795
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002796 d = (14318 * n) / freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002797 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002798 int temp = d;
2799
2800 if (temp > 31) {
2801 s = 1;
2802 temp >>= 1;
2803 }
2804 h = ((14318 * n) / temp) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002805 h = h > freq ? h - freq : freq - h;
2806 if (h < diff) {
2807 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002808 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002809 *den = temp;
2810 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002811 }
2812 }
Krzysztof Helt7528f542008-10-15 22:03:36 -07002813 d++;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002814 if ((d >= 7) && (d <= 63)) {
Krzysztof Helt7528f542008-10-15 22:03:36 -07002815 if (d > 31) {
2816 s = 1;
2817 d >>= 1;
2818 }
2819 h = ((14318 * n) / d) >> s;
Krzysztof Heltdafa32c2008-10-15 22:03:40 -07002820 h = h > freq ? h - freq : freq - h;
2821 if (h < diff) {
2822 diff = h;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002823 *nom = n;
Krzysztof Helt7528f542008-10-15 22:03:36 -07002824 *den = d;
2825 *div = s;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002826 }
2827 }
2828 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07002829}
2830
Linus Torvalds1da177e2005-04-16 15:20:36 -07002831/* -------------------------------------------------------------------------
2832 *
2833 * debugging functions
2834 *
2835 * -------------------------------------------------------------------------
2836 */
2837
2838#ifdef CIRRUSFB_DEBUG
2839
2840/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002841 * cirrusfb_dbg_print_regs
2842 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2843 * @reg_class: type of registers to read: %CRT, or %SEQ
2844 *
2845 * DESCRIPTION:
2846 * Dumps the given list of VGA CRTC registers. If @base is %NULL,
2847 * old-style I/O ports are queried for information, otherwise MMIO is
2848 * used at the given @base address to query the information.
2849 */
2850
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002851static void cirrusfb_dbg_print_regs(struct fb_info *info,
2852 caddr_t regbase,
2853 enum cirrusfb_dbg_reg_class reg_class, ...)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002854{
2855 va_list list;
2856 unsigned char val = 0;
2857 unsigned reg;
2858 char *name;
2859
Krzysztof Helt8503df62007-10-16 01:29:08 -07002860 va_start(list, reg_class);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002861
Krzysztof Helt8503df62007-10-16 01:29:08 -07002862 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002863 while (name != NULL) {
Krzysztof Helt8503df62007-10-16 01:29:08 -07002864 reg = va_arg(list, int);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002865
2866 switch (reg_class) {
2867 case CRT:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002868 val = vga_rcrt(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002869 break;
2870 case SEQ:
Krzysztof Helt8503df62007-10-16 01:29:08 -07002871 val = vga_rseq(regbase, (unsigned char) reg);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002872 break;
2873 default:
2874 /* should never occur */
Richard Knutssonc930faa2007-05-08 00:38:29 -07002875 assert(false);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002876 break;
2877 }
2878
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002879 dev_dbg(info->device, "%8s = 0x%02X\n", name, val);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002880
Krzysztof Helt8503df62007-10-16 01:29:08 -07002881 name = va_arg(list, char *);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002882 }
2883
Krzysztof Helt8503df62007-10-16 01:29:08 -07002884 va_end(list);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002885}
2886
Linus Torvalds1da177e2005-04-16 15:20:36 -07002887/**
Linus Torvalds1da177e2005-04-16 15:20:36 -07002888 * cirrusfb_dbg_reg_dump
2889 * @base: If using newmmio, the newmmio base address, otherwise %NULL
2890 *
2891 * DESCRIPTION:
2892 * Dumps a list of interesting VGA and CIRRUSFB registers. If @base is %NULL,
2893 * old-style I/O ports are queried for information, otherwise MMIO is
2894 * used at the given @base address to query the information.
2895 */
2896
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002897static void cirrusfb_dbg_reg_dump(struct fb_info *info, caddr_t regbase)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002898{
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002899 dev_dbg(info->device, "VGA CRTC register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002900
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002901 cirrusfb_dbg_print_regs(info, regbase, CRT,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002902 "CR00", 0x00,
2903 "CR01", 0x01,
2904 "CR02", 0x02,
2905 "CR03", 0x03,
2906 "CR04", 0x04,
2907 "CR05", 0x05,
2908 "CR06", 0x06,
2909 "CR07", 0x07,
2910 "CR08", 0x08,
2911 "CR09", 0x09,
2912 "CR0A", 0x0A,
2913 "CR0B", 0x0B,
2914 "CR0C", 0x0C,
2915 "CR0D", 0x0D,
2916 "CR0E", 0x0E,
2917 "CR0F", 0x0F,
2918 "CR10", 0x10,
2919 "CR11", 0x11,
2920 "CR12", 0x12,
2921 "CR13", 0x13,
2922 "CR14", 0x14,
2923 "CR15", 0x15,
2924 "CR16", 0x16,
2925 "CR17", 0x17,
2926 "CR18", 0x18,
2927 "CR22", 0x22,
2928 "CR24", 0x24,
2929 "CR26", 0x26,
2930 "CR2D", 0x2D,
2931 "CR2E", 0x2E,
2932 "CR2F", 0x2F,
2933 "CR30", 0x30,
2934 "CR31", 0x31,
2935 "CR32", 0x32,
2936 "CR33", 0x33,
2937 "CR34", 0x34,
2938 "CR35", 0x35,
2939 "CR36", 0x36,
2940 "CR37", 0x37,
2941 "CR38", 0x38,
2942 "CR39", 0x39,
2943 "CR3A", 0x3A,
2944 "CR3B", 0x3B,
2945 "CR3C", 0x3C,
2946 "CR3D", 0x3D,
2947 "CR3E", 0x3E,
2948 "CR3F", 0x3F,
2949 NULL);
2950
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002951 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002952
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002953 dev_dbg(info->device, "VGA SEQ register dump:\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002954
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002955 cirrusfb_dbg_print_regs(info, regbase, SEQ,
Linus Torvalds1da177e2005-04-16 15:20:36 -07002956 "SR00", 0x00,
2957 "SR01", 0x01,
2958 "SR02", 0x02,
2959 "SR03", 0x03,
2960 "SR04", 0x04,
2961 "SR08", 0x08,
2962 "SR09", 0x09,
2963 "SR0A", 0x0A,
2964 "SR0B", 0x0B,
2965 "SR0D", 0x0D,
2966 "SR10", 0x10,
2967 "SR11", 0x11,
2968 "SR12", 0x12,
2969 "SR13", 0x13,
2970 "SR14", 0x14,
2971 "SR15", 0x15,
2972 "SR16", 0x16,
2973 "SR17", 0x17,
2974 "SR18", 0x18,
2975 "SR19", 0x19,
2976 "SR1A", 0x1A,
2977 "SR1B", 0x1B,
2978 "SR1C", 0x1C,
2979 "SR1D", 0x1D,
2980 "SR1E", 0x1E,
2981 "SR1F", 0x1F,
2982 NULL);
2983
Krzysztof Helt75ed3a12009-03-31 15:25:03 -07002984 dev_dbg(info->device, "\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -07002985}
2986
2987#endif /* CIRRUSFB_DEBUG */
2988