blob: f23cae094f1b75e2781987705ef1ad115b540bb9 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * linux/drivers/video/amifb.c -- Amiga builtin chipset frame buffer device
3 *
4 * Copyright (C) 1995-2003 Geert Uytterhoeven
5 *
6 * with work by Roman Zippel
7 *
8 *
9 * This file is based on the Atari frame buffer device (atafb.c):
10 *
11 * Copyright (C) 1994 Martin Schaller
12 * Roman Hodek
13 *
14 * with work by Andreas Schwab
15 * Guenther Kelleter
16 *
17 * and on the original Amiga console driver (amicon.c):
18 *
19 * Copyright (C) 1993 Hamish Macdonald
20 * Greg Harp
21 * Copyright (C) 1994 David Carter [carter@compsci.bristol.ac.uk]
22 *
23 * with work by William Rucklidge (wjr@cs.cornell.edu)
24 * Geert Uytterhoeven
25 * Jes Sorensen (jds@kom.auc.dk)
26 *
27 *
28 * History:
29 *
30 * - 24 Jul 96: Copper generates now vblank interrupt and
31 * VESA Power Saving Protocol is fully implemented
32 * - 14 Jul 96: Rework and hopefully last ECS bugs fixed
33 * - 7 Mar 96: Hardware sprite support by Roman Zippel
34 * - 18 Feb 96: OCS and ECS support by Roman Zippel
35 * Hardware functions completely rewritten
36 * - 2 Dec 95: AGA version by Geert Uytterhoeven
37 *
38 * This file is subject to the terms and conditions of the GNU General Public
39 * License. See the file COPYING in the main directory of this archive
40 * for more details.
41 */
42
43#include <linux/module.h>
44#include <linux/kernel.h>
45#include <linux/errno.h>
46#include <linux/string.h>
47#include <linux/mm.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070048#include <linux/delay.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070049#include <linux/interrupt.h>
50#include <linux/fb.h>
51#include <linux/init.h>
52#include <linux/ioport.h>
Geert Uytterhoevenfa6688e2009-04-05 12:45:56 +020053#include <linux/platform_device.h>
Krzysztof Helt84902b72007-10-16 01:29:04 -070054#include <linux/uaccess.h>
Geert Uytterhoevenfa6688e2009-04-05 12:45:56 +020055
Linus Torvalds1da177e2005-04-16 15:20:36 -070056#include <asm/system.h>
57#include <asm/irq.h>
58#include <asm/amigahw.h>
59#include <asm/amigaints.h>
60#include <asm/setup.h>
61
62#include "c2p.h"
63
64
65#define DEBUG
66
67#if !defined(CONFIG_FB_AMIGA_OCS) && !defined(CONFIG_FB_AMIGA_ECS) && !defined(CONFIG_FB_AMIGA_AGA)
68#define CONFIG_FB_AMIGA_OCS /* define at least one fb driver, this will change later */
69#endif
70
71#if !defined(CONFIG_FB_AMIGA_OCS)
72# define IS_OCS (0)
73#elif defined(CONFIG_FB_AMIGA_ECS) || defined(CONFIG_FB_AMIGA_AGA)
74# define IS_OCS (chipset == TAG_OCS)
75#else
76# define CONFIG_FB_AMIGA_OCS_ONLY
77# define IS_OCS (1)
78#endif
79
80#if !defined(CONFIG_FB_AMIGA_ECS)
81# define IS_ECS (0)
82#elif defined(CONFIG_FB_AMIGA_OCS) || defined(CONFIG_FB_AMIGA_AGA)
83# define IS_ECS (chipset == TAG_ECS)
84#else
85# define CONFIG_FB_AMIGA_ECS_ONLY
86# define IS_ECS (1)
87#endif
88
89#if !defined(CONFIG_FB_AMIGA_AGA)
90# define IS_AGA (0)
91#elif defined(CONFIG_FB_AMIGA_OCS) || defined(CONFIG_FB_AMIGA_ECS)
92# define IS_AGA (chipset == TAG_AGA)
93#else
94# define CONFIG_FB_AMIGA_AGA_ONLY
95# define IS_AGA (1)
96#endif
97
98#ifdef DEBUG
Harvey Harrison5ae12172008-04-28 02:15:47 -070099# define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __func__ , ## args)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100#else
101# define DPRINTK(fmt, args...)
102#endif
103
104/*******************************************************************************
105
106
107 Generic video timings
108 ---------------------
109
110 Timings used by the frame buffer interface:
111
112 +----------+---------------------------------------------+----------+-------+
113 | | ^ | | |
114 | | |upper_margin | | |
Jan Engelhardt96de0e22007-10-19 23:21:04 +0200115 | | v | | |
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116 +----------###############################################----------+-------+
117 | # ^ # | |
118 | # | # | |
119 | # | # | |
120 | # | # | |
121 | left # | # right | hsync |
122 | margin # | xres # margin | len |
123 |<-------->#<---------------+--------------------------->#<-------->|<----->|
124 | # | # | |
125 | # | # | |
126 | # | # | |
127 | # |yres # | |
128 | # | # | |
129 | # | # | |
130 | # | # | |
131 | # | # | |
132 | # | # | |
133 | # | # | |
134 | # | # | |
135 | # | # | |
Jan Engelhardt96de0e22007-10-19 23:21:04 +0200136 | # v # | |
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137 +----------###############################################----------+-------+
138 | | ^ | | |
139 | | |lower_margin | | |
Jan Engelhardt96de0e22007-10-19 23:21:04 +0200140 | | v | | |
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141 +----------+---------------------------------------------+----------+-------+
142 | | ^ | | |
143 | | |vsync_len | | |
Jan Engelhardt96de0e22007-10-19 23:21:04 +0200144 | | v | | |
Linus Torvalds1da177e2005-04-16 15:20:36 -0700145 +----------+---------------------------------------------+----------+-------+
146
147
148 Amiga video timings
149 -------------------
150
151 The Amiga native chipsets uses another timing scheme:
152
153 - hsstrt: Start of horizontal synchronization pulse
154 - hsstop: End of horizontal synchronization pulse
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100155 - htotal: Last value on the line (i.e. line length = htotal + 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156 - vsstrt: Start of vertical synchronization pulse
157 - vsstop: End of vertical synchronization pulse
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100158 - vtotal: Last line value (i.e. number of lines = vtotal + 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159 - hcenter: Start of vertical retrace for interlace
160
161 You can specify the blanking timings independently. Currently I just set
162 them equal to the respective synchronization values:
163
164 - hbstrt: Start of horizontal blank
165 - hbstop: End of horizontal blank
166 - vbstrt: Start of vertical blank
167 - vbstop: End of vertical blank
168
169 Horizontal values are in color clock cycles (280 ns), vertical values are in
170 scanlines.
171
172 (0, 0) is somewhere in the upper-left corner :-)
173
174
175 Amiga visible window definitions
176 --------------------------------
177
178 Currently I only have values for AGA, SHRES (28 MHz dotclock). Feel free to
179 make corrections and/or additions.
180
181 Within the above synchronization specifications, the visible window is
182 defined by the following parameters (actual register resolutions may be
183 different; all horizontal values are normalized with respect to the pixel
184 clock):
185
186 - diwstrt_h: Horizontal start of the visible window
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100187 - diwstop_h: Horizontal stop + 1(*) of the visible window
Linus Torvalds1da177e2005-04-16 15:20:36 -0700188 - diwstrt_v: Vertical start of the visible window
189 - diwstop_v: Vertical stop of the visible window
190 - ddfstrt: Horizontal start of display DMA
191 - ddfstop: Horizontal stop of display DMA
192 - hscroll: Horizontal display output delay
193
194 Sprite positioning:
195
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100196 - sprstrt_h: Horizontal start - 4 of sprite
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197 - sprstrt_v: Vertical start of sprite
198
199 (*) Even Commodore did it wrong in the AGA monitor drivers by not adding 1.
200
201 Horizontal values are in dotclock cycles (35 ns), vertical values are in
202 scanlines.
203
204 (0, 0) is somewhere in the upper-left corner :-)
205
206
207 Dependencies (AGA, SHRES (35 ns dotclock))
208 -------------------------------------------
209
210 Since there are much more parameters for the Amiga display than for the
211 frame buffer interface, there must be some dependencies among the Amiga
212 display parameters. Here's what I found out:
213
214 - ddfstrt and ddfstop are best aligned to 64 pixels.
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100215 - the chipset needs 64 + 4 horizontal pixels after the DMA start before
216 the first pixel is output, so diwstrt_h = ddfstrt + 64 + 4 if you want
217 to display the first pixel on the line too. Increase diwstrt_h for
218 virtual screen panning.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219 - the display DMA always fetches 64 pixels at a time (fmode = 3).
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100220 - ddfstop is ddfstrt+#pixels - 64.
221 - diwstop_h = diwstrt_h + xres + 1. Because of the additional 1 this can
222 be 1 more than htotal.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 - hscroll simply adds a delay to the display output. Smooth horizontal
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100224 panning needs an extra 64 pixels on the left to prefetch the pixels that
225 `fall off' on the left.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226 - if ddfstrt < 192, the sprite DMA cycles are all stolen by the bitplane
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100227 DMA, so it's best to make the DMA start as late as possible.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228 - you really don't want to make ddfstrt < 128, since this will steal DMA
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100229 cycles from the other DMA channels (audio, floppy and Chip RAM refresh).
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230 - I make diwstop_h and diwstop_v as large as possible.
231
232 General dependencies
233 --------------------
234
235 - all values are SHRES pixel (35ns)
236
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100237 table 1:fetchstart table 2:prefetch table 3:fetchsize
238 ------------------ ---------------- -----------------
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239 Pixclock # SHRES|HIRES|LORES # SHRES|HIRES|LORES # SHRES|HIRES|LORES
240 -------------#------+-----+------#------+-----+------#------+-----+------
241 Bus width 1x # 16 | 32 | 64 # 16 | 32 | 64 # 64 | 64 | 64
242 Bus width 2x # 32 | 64 | 128 # 32 | 64 | 64 # 64 | 64 | 128
243 Bus width 4x # 64 | 128 | 256 # 64 | 64 | 64 # 64 | 128 | 256
244
245 - chipset needs 4 pixels before the first pixel is output
246 - ddfstrt must be aligned to fetchstart (table 1)
247 - chipset needs also prefetch (table 2) to get first pixel data, so
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100248 ddfstrt = ((diwstrt_h - 4) & -fetchstart) - prefetch
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249 - for horizontal panning decrease diwstrt_h
250 - the length of a fetchline must be aligned to fetchsize (table 3)
251 - if fetchstart is smaller than fetchsize, then ddfstrt can a little bit
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100252 moved to optimize use of dma (useful for OCS/ECS overscan displays)
253 - ddfstop is ddfstrt + ddfsize - fetchsize
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254 - If C= didn't change anything for AGA, then at following positions the
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100255 dma bus is already used:
256 ddfstrt < 48 -> memory refresh
257 < 96 -> disk dma
258 < 160 -> audio dma
259 < 192 -> sprite 0 dma
260 < 416 -> sprite dma (32 per sprite)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261 - in accordance with the hardware reference manual a hardware stop is at
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100262 192, but AGA (ECS?) can go below this.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263
264 DMA priorities
265 --------------
266
267 Since there are limits on the earliest start value for display DMA and the
268 display of sprites, I use the following policy on horizontal panning and
269 the hardware cursor:
270
271 - if you want to start display DMA too early, you lose the ability to
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100272 do smooth horizontal panning (xpanstep 1 -> 64).
Linus Torvalds1da177e2005-04-16 15:20:36 -0700273 - if you want to go even further, you lose the hardware cursor too.
274
275 IMHO a hardware cursor is more important for X than horizontal scrolling,
276 so that's my motivation.
277
278
279 Implementation
280 --------------
281
282 ami_decode_var() converts the frame buffer values to the Amiga values. It's
283 just a `straightforward' implementation of the above rules.
284
285
286 Standard VGA timings
287 --------------------
288
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100289 xres yres left right upper lower hsync vsync
290 ---- ---- ---- ----- ----- ----- ----- -----
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 80x25 720 400 27 45 35 12 108 2
292 80x30 720 480 27 45 30 9 108 2
293
294 These were taken from a XFree86 configuration file, recalculated for a 28 MHz
295 dotclock (Amigas don't have a 25 MHz dotclock) and converted to frame buffer
296 generic timings.
297
298 As a comparison, graphics/monitor.h suggests the following:
299
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100300 xres yres left right upper lower hsync vsync
301 ---- ---- ---- ----- ----- ----- ----- -----
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302
303 VGA 640 480 52 112 24 19 112 - 2 +
304 VGA70 640 400 52 112 27 21 112 - 2 -
305
306
307 Sync polarities
308 ---------------
309
310 VSYNC HSYNC Vertical size Vertical total
311 ----- ----- ------------- --------------
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100312 + + Reserved Reserved
313 + - 400 414
314 - + 350 362
315 - - 480 496
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316
317 Source: CL-GD542X Technical Reference Manual, Cirrus Logic, Oct 1992
318
319
320 Broadcast video timings
321 -----------------------
322
323 According to the CCIR and RETMA specifications, we have the following values:
324
325 CCIR -> PAL
326 -----------
327
Jan Engelhardt96de0e22007-10-19 23:21:04 +0200328 - a scanline is 64 µs long, of which 52.48 µs are visible. This is about
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100329 736 visible 70 ns pixels per line.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330 - we have 625 scanlines, of which 575 are visible (interlaced); after
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100331 rounding this becomes 576.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332
333 RETMA -> NTSC
334 -------------
335
Jan Engelhardt96de0e22007-10-19 23:21:04 +0200336 - a scanline is 63.5 µs long, of which 53.5 µs are visible. This is about
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100337 736 visible 70 ns pixels per line.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 - we have 525 scanlines, of which 485 are visible (interlaced); after
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100339 rounding this becomes 484.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340
341 Thus if you want a PAL compatible display, you have to do the following:
342
343 - set the FB_SYNC_BROADCAST flag to indicate that standard broadcast
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100344 timings are to be used.
345 - make sure upper_margin + yres + lower_margin + vsync_len = 625 for an
346 interlaced, 312 for a non-interlaced and 156 for a doublescanned
347 display.
348 - make sure left_margin + xres + right_margin + hsync_len = 1816 for a
349 SHRES, 908 for a HIRES and 454 for a LORES display.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350 - the left visible part begins at 360 (SHRES; HIRES:180, LORES:90),
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100351 left_margin + 2 * hsync_len must be greater or equal.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352 - the upper visible part begins at 48 (interlaced; non-interlaced:24,
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100353 doublescanned:12), upper_margin + 2 * vsync_len must be greater or
354 equal.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355 - ami_encode_var() calculates margins with a hsync of 5320 ns and a vsync
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100356 of 4 scanlines
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357
358 The settings for a NTSC compatible display are straightforward.
359
360 Note that in a strict sense the PAL and NTSC standards only define the
361 encoding of the color part (chrominance) of the video signal and don't say
362 anything about horizontal/vertical synchronization nor refresh rates.
363
364
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100365 -- Geert --
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366
367*******************************************************************************/
368
369
370 /*
371 * Custom Chipset Definitions
372 */
373
374#define CUSTOM_OFS(fld) ((long)&((struct CUSTOM*)0)->fld)
375
376 /*
377 * BPLCON0 -- Bitplane Control Register 0
378 */
379
380#define BPC0_HIRES (0x8000)
381#define BPC0_BPU2 (0x4000) /* Bit plane used count */
382#define BPC0_BPU1 (0x2000)
383#define BPC0_BPU0 (0x1000)
384#define BPC0_HAM (0x0800) /* HAM mode */
385#define BPC0_DPF (0x0400) /* Double playfield */
386#define BPC0_COLOR (0x0200) /* Enable colorburst */
387#define BPC0_GAUD (0x0100) /* Genlock audio enable */
388#define BPC0_UHRES (0x0080) /* Ultrahi res enable */
389#define BPC0_SHRES (0x0040) /* Super hi res mode */
390#define BPC0_BYPASS (0x0020) /* Bypass LUT - AGA */
391#define BPC0_BPU3 (0x0010) /* AGA */
392#define BPC0_LPEN (0x0008) /* Light pen enable */
393#define BPC0_LACE (0x0004) /* Interlace */
394#define BPC0_ERSY (0x0002) /* External resync */
395#define BPC0_ECSENA (0x0001) /* ECS enable */
396
397 /*
398 * BPLCON2 -- Bitplane Control Register 2
399 */
400
401#define BPC2_ZDBPSEL2 (0x4000) /* Bitplane to be used for ZD - AGA */
402#define BPC2_ZDBPSEL1 (0x2000)
403#define BPC2_ZDBPSEL0 (0x1000)
404#define BPC2_ZDBPEN (0x0800) /* Enable ZD with ZDBPSELx - AGA */
405#define BPC2_ZDCTEN (0x0400) /* Enable ZD with palette bit #31 - AGA */
406#define BPC2_KILLEHB (0x0200) /* Kill EHB mode - AGA */
407#define BPC2_RDRAM (0x0100) /* Color table accesses read, not write - AGA */
408#define BPC2_SOGEN (0x0080) /* SOG output pin high - AGA */
409#define BPC2_PF2PRI (0x0040) /* PF2 priority over PF1 */
410#define BPC2_PF2P2 (0x0020) /* PF2 priority wrt sprites */
411#define BPC2_PF2P1 (0x0010)
412#define BPC2_PF2P0 (0x0008)
413#define BPC2_PF1P2 (0x0004) /* ditto PF1 */
414#define BPC2_PF1P1 (0x0002)
415#define BPC2_PF1P0 (0x0001)
416
417 /*
418 * BPLCON3 -- Bitplane Control Register 3 (AGA)
419 */
420
421#define BPC3_BANK2 (0x8000) /* Bits to select color register bank */
422#define BPC3_BANK1 (0x4000)
423#define BPC3_BANK0 (0x2000)
424#define BPC3_PF2OF2 (0x1000) /* Bits for color table offset when PF2 */
425#define BPC3_PF2OF1 (0x0800)
426#define BPC3_PF2OF0 (0x0400)
427#define BPC3_LOCT (0x0200) /* Color register writes go to low bits */
428#define BPC3_SPRES1 (0x0080) /* Sprite resolution bits */
429#define BPC3_SPRES0 (0x0040)
430#define BPC3_BRDRBLNK (0x0020) /* Border blanked? */
431#define BPC3_BRDRTRAN (0x0010) /* Border transparent? */
432#define BPC3_ZDCLKEN (0x0004) /* ZD pin is 14 MHz (HIRES) clock output */
433#define BPC3_BRDRSPRT (0x0002) /* Sprites in border? */
434#define BPC3_EXTBLKEN (0x0001) /* BLANK programmable */
435
436 /*
437 * BPLCON4 -- Bitplane Control Register 4 (AGA)
438 */
439
440#define BPC4_BPLAM7 (0x8000) /* bitplane color XOR field */
441#define BPC4_BPLAM6 (0x4000)
442#define BPC4_BPLAM5 (0x2000)
443#define BPC4_BPLAM4 (0x1000)
444#define BPC4_BPLAM3 (0x0800)
445#define BPC4_BPLAM2 (0x0400)
446#define BPC4_BPLAM1 (0x0200)
447#define BPC4_BPLAM0 (0x0100)
448#define BPC4_ESPRM7 (0x0080) /* 4 high bits for even sprite colors */
449#define BPC4_ESPRM6 (0x0040)
450#define BPC4_ESPRM5 (0x0020)
451#define BPC4_ESPRM4 (0x0010)
452#define BPC4_OSPRM7 (0x0008) /* 4 high bits for odd sprite colors */
453#define BPC4_OSPRM6 (0x0004)
454#define BPC4_OSPRM5 (0x0002)
455#define BPC4_OSPRM4 (0x0001)
456
457 /*
458 * BEAMCON0 -- Beam Control Register
459 */
460
461#define BMC0_HARDDIS (0x4000) /* Disable hardware limits */
462#define BMC0_LPENDIS (0x2000) /* Disable light pen latch */
463#define BMC0_VARVBEN (0x1000) /* Enable variable vertical blank */
464#define BMC0_LOLDIS (0x0800) /* Disable long/short line toggle */
465#define BMC0_CSCBEN (0x0400) /* Composite sync/blank */
466#define BMC0_VARVSYEN (0x0200) /* Enable variable vertical sync */
467#define BMC0_VARHSYEN (0x0100) /* Enable variable horizontal sync */
468#define BMC0_VARBEAMEN (0x0080) /* Enable variable beam counters */
469#define BMC0_DUAL (0x0040) /* Enable alternate horizontal beam counter */
470#define BMC0_PAL (0x0020) /* Set decodes for PAL */
471#define BMC0_VARCSYEN (0x0010) /* Enable variable composite sync */
472#define BMC0_BLANKEN (0x0008) /* Blank enable (no longer used on AGA) */
473#define BMC0_CSYTRUE (0x0004) /* CSY polarity */
474#define BMC0_VSYTRUE (0x0002) /* VSY polarity */
475#define BMC0_HSYTRUE (0x0001) /* HSY polarity */
476
477
478 /*
479 * FMODE -- Fetch Mode Control Register (AGA)
480 */
481
482#define FMODE_SSCAN2 (0x8000) /* Sprite scan-doubling */
483#define FMODE_BSCAN2 (0x4000) /* Use PF2 modulus every other line */
484#define FMODE_SPAGEM (0x0008) /* Sprite page mode */
485#define FMODE_SPR32 (0x0004) /* Sprite 32 bit fetch */
486#define FMODE_BPAGEM (0x0002) /* Bitplane page mode */
487#define FMODE_BPL32 (0x0001) /* Bitplane 32 bit fetch */
488
489 /*
490 * Tags used to indicate a specific Pixel Clock
491 *
492 * clk_shift is the shift value to get the timings in 35 ns units
493 */
494
495enum { TAG_SHRES, TAG_HIRES, TAG_LORES };
496
497 /*
498 * Tags used to indicate the specific chipset
499 */
500
501enum { TAG_OCS, TAG_ECS, TAG_AGA };
502
503 /*
504 * Tags used to indicate the memory bandwidth
505 */
506
507enum { TAG_FMODE_1, TAG_FMODE_2, TAG_FMODE_4 };
508
509
510 /*
511 * Clock Definitions, Maximum Display Depth
512 *
513 * These depend on the E-Clock or the Chipset, so they are filled in
514 * dynamically
515 */
516
517static u_long pixclock[3]; /* SHRES/HIRES/LORES: index = clk_shift */
518static u_short maxdepth[3]; /* SHRES/HIRES/LORES: index = clk_shift */
519static u_short maxfmode, chipset;
520
521
522 /*
523 * Broadcast Video Timings
524 *
525 * Horizontal values are in 35 ns (SHRES) units
526 * Vertical values are in interlaced scanlines
527 */
528
529#define PAL_DIWSTRT_H (360) /* PAL Window Limits */
530#define PAL_DIWSTRT_V (48)
531#define PAL_HTOTAL (1816)
532#define PAL_VTOTAL (625)
533
534#define NTSC_DIWSTRT_H (360) /* NTSC Window Limits */
535#define NTSC_DIWSTRT_V (40)
536#define NTSC_HTOTAL (1816)
537#define NTSC_VTOTAL (525)
538
539
540 /*
541 * Various macros
542 */
543
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100544#define up2(v) (((v) + 1) & -2)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545#define down2(v) ((v) & -2)
546#define div2(v) ((v)>>1)
547#define mod2(v) ((v) & 1)
548
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100549#define up4(v) (((v) + 3) & -4)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550#define down4(v) ((v) & -4)
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100551#define mul4(v) ((v) << 2)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552#define div4(v) ((v)>>2)
553#define mod4(v) ((v) & 3)
554
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100555#define up8(v) (((v) + 7) & -8)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556#define down8(v) ((v) & -8)
557#define div8(v) ((v)>>3)
558#define mod8(v) ((v) & 7)
559
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100560#define up16(v) (((v) + 15) & -16)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561#define down16(v) ((v) & -16)
562#define div16(v) ((v)>>4)
563#define mod16(v) ((v) & 15)
564
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100565#define up32(v) (((v) + 31) & -32)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700566#define down32(v) ((v) & -32)
567#define div32(v) ((v)>>5)
568#define mod32(v) ((v) & 31)
569
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100570#define up64(v) (((v) + 63) & -64)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571#define down64(v) ((v) & -64)
572#define div64(v) ((v)>>6)
573#define mod64(v) ((v) & 63)
574
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100575#define upx(x, v) (((v) + (x) - 1) & -(x))
576#define downx(x, v) ((v) & -(x))
577#define modx(x, v) ((v) & ((x) - 1))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578
579/* if x1 is not a constant, this macro won't make real sense :-) */
580#ifdef __mc68000__
581#define DIVUL(x1, x2) ({int res; asm("divul %1,%2,%3": "=d" (res): \
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100582 "d" (x2), "d" ((long)((x1) / 0x100000000ULL)), "0" ((long)(x1))); res;})
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583#else
584/* We know a bit about the numbers, so we can do it this way */
585#define DIVUL(x1, x2) ((((long)((unsigned long long)x1 >> 8) / x2) << 8) + \
586 ((((long)((unsigned long long)x1 >> 8) % x2) << 8) / x2))
587#endif
588
589#define highw(x) ((u_long)(x)>>16 & 0xffff)
590#define loww(x) ((u_long)(x) & 0xffff)
591
Al Virob4290a22006-01-12 01:06:12 -0800592#define custom amiga_custom
593
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594#define VBlankOn() custom.intena = IF_SETCLR|IF_COPER
595#define VBlankOff() custom.intena = IF_COPER
596
597
598 /*
599 * Chip RAM we reserve for the Frame Buffer
600 *
601 * This defines the Maximum Virtual Screen Size
602 * (Setable per kernel options?)
603 */
604
605#define VIDEOMEMSIZE_AGA_2M (1310720) /* AGA (2MB) : max 1280*1024*256 */
606#define VIDEOMEMSIZE_AGA_1M (786432) /* AGA (1MB) : max 1024*768*256 */
607#define VIDEOMEMSIZE_ECS_2M (655360) /* ECS (2MB) : max 1280*1024*16 */
608#define VIDEOMEMSIZE_ECS_1M (393216) /* ECS (1MB) : max 1024*768*16 */
609#define VIDEOMEMSIZE_OCS (262144) /* OCS : max ca. 800*600*16 */
610
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100611#define SPRITEMEMSIZE (64 * 64 / 4) /* max 64*64*4 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612#define DUMMYSPRITEMEMSIZE (8)
613static u_long spritememory;
614
615#define CHIPRAM_SAFETY_LIMIT (16384)
616
617static u_long videomemory;
618
619 /*
620 * This is the earliest allowed start of fetching display data.
621 * Only if you really want no hardware cursor and audio,
622 * set this to 128, but let it better at 192
623 */
624
625static u_long min_fstrt = 192;
626
627#define assignchunk(name, type, ptr, size) \
628{ \
629 (name) = (type)(ptr); \
630 ptr += size; \
631}
632
633
634 /*
635 * Copper Instructions
636 */
637
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100638#define CMOVE(val, reg) (CUSTOM_OFS(reg) << 16 | (val))
639#define CMOVE2(val, reg) ((CUSTOM_OFS(reg) + 2) << 16 | (val))
640#define CWAIT(x, y) (((y) & 0x1fe) << 23 | ((x) & 0x7f0) << 13 | 0x0001fffe)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641#define CEND (0xfffffffe)
642
643
644typedef union {
645 u_long l;
646 u_short w[2];
647} copins;
648
649static struct copdisplay {
650 copins *init;
651 copins *wait;
652 copins *list[2][2];
653 copins *rebuild[2];
654} copdisplay;
655
656static u_short currentcop = 0;
657
658 /*
659 * Hardware Cursor API Definitions
660 * These used to be in linux/fb.h, but were preliminary and used by
661 * amifb only anyway
662 */
663
664#define FBIOGET_FCURSORINFO 0x4607
665#define FBIOGET_VCURSORINFO 0x4608
666#define FBIOPUT_VCURSORINFO 0x4609
667#define FBIOGET_CURSORSTATE 0x460A
668#define FBIOPUT_CURSORSTATE 0x460B
669
670
671struct fb_fix_cursorinfo {
672 __u16 crsr_width; /* width and height of the cursor in */
673 __u16 crsr_height; /* pixels (zero if no cursor) */
674 __u16 crsr_xsize; /* cursor size in display pixels */
675 __u16 crsr_ysize;
676 __u16 crsr_color1; /* colormap entry for cursor color1 */
677 __u16 crsr_color2; /* colormap entry for cursor color2 */
678};
679
680struct fb_var_cursorinfo {
681 __u16 width;
682 __u16 height;
683 __u16 xspot;
684 __u16 yspot;
685 __u8 data[1]; /* field with [height][width] */
686};
687
688struct fb_cursorstate {
689 __s16 xoffset;
690 __s16 yoffset;
691 __u16 mode;
692};
693
694#define FB_CURSOR_OFF 0
695#define FB_CURSOR_ON 1
696#define FB_CURSOR_FLASH 2
697
698
699 /*
700 * Hardware Cursor
701 */
702
703static int cursorrate = 20; /* Number of frames/flash toggle */
704static u_short cursorstate = -1;
705static u_short cursormode = FB_CURSOR_OFF;
706
707static u_short *lofsprite, *shfsprite, *dummysprite;
708
709 /*
710 * Current Video Mode
711 */
712
Geert Uytterhoeven61640c22011-11-21 21:53:59 +0100713struct amifb_par {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714
715 /* General Values */
716
717 int xres; /* vmode */
718 int yres; /* vmode */
719 int vxres; /* vmode */
720 int vyres; /* vmode */
721 int xoffset; /* vmode */
722 int yoffset; /* vmode */
723 u_short bpp; /* vmode */
724 u_short clk_shift; /* vmode */
725 u_short line_shift; /* vmode */
726 int vmode; /* vmode */
727 u_short diwstrt_h; /* vmode */
728 u_short diwstop_h; /* vmode */
729 u_short diwstrt_v; /* vmode */
730 u_short diwstop_v; /* vmode */
731 u_long next_line; /* modulo for next line */
732 u_long next_plane; /* modulo for next plane */
733
734 /* Cursor Values */
735
736 struct {
737 short crsr_x; /* movecursor */
738 short crsr_y; /* movecursor */
739 short spot_x;
740 short spot_y;
741 u_short height;
742 u_short width;
743 u_short fmode;
744 } crsr;
745
746 /* OCS Hardware Registers */
747
748 u_long bplpt0; /* vmode, pan (Note: physical address) */
749 u_long bplpt0wrap; /* vmode, pan (Note: physical address) */
750 u_short ddfstrt;
751 u_short ddfstop;
752 u_short bpl1mod;
753 u_short bpl2mod;
754 u_short bplcon0; /* vmode */
755 u_short bplcon1; /* vmode */
756 u_short htotal; /* vmode */
757 u_short vtotal; /* vmode */
758
759 /* Additional ECS Hardware Registers */
760
761 u_short bplcon3; /* vmode */
762 u_short beamcon0; /* vmode */
763 u_short hsstrt; /* vmode */
764 u_short hsstop; /* vmode */
765 u_short hbstrt; /* vmode */
766 u_short hbstop; /* vmode */
767 u_short vsstrt; /* vmode */
768 u_short vsstop; /* vmode */
769 u_short vbstrt; /* vmode */
770 u_short vbstop; /* vmode */
771 u_short hcenter; /* vmode */
772
773 /* Additional AGA Hardware Registers */
774
775 u_short fmode; /* vmode */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776};
777
778
779 /*
780 * Saved color entry 0 so we can restore it when unblanking
781 */
782
783static u_char red0, green0, blue0;
784
785
786#if defined(CONFIG_FB_AMIGA_ECS)
787static u_short ecs_palette[32];
788#endif
789
790
791 /*
792 * Latches for Display Changes during VBlank
793 */
794
795static u_short do_vmode_full = 0; /* Change the Video Mode */
796static u_short do_vmode_pan = 0; /* Update the Video Mode */
Jan Engelhardt96de0e22007-10-19 23:21:04 +0200797static short do_blank = 0; /* (Un)Blank the Screen (±1) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798static u_short do_cursor = 0; /* Move the Cursor */
799
800
801 /*
802 * Various Flags
803 */
804
805static u_short is_blanked = 0; /* Screen is Blanked */
806static u_short is_lace = 0; /* Screen is laced */
807
808 /*
809 * Predefined Video Modes
810 *
811 */
812
813static struct fb_videomode ami_modedb[] __initdata = {
814
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100815 /*
816 * AmigaOS Video Modes
817 *
818 * If you change these, make sure to update DEFMODE_* as well!
819 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100821 {
822 /* 640x200, 15 kHz, 60 Hz (NTSC) */
823 "ntsc", 60, 640, 200, TAG_HIRES, 106, 86, 44, 16, 76, 2,
824 FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
825 }, {
826 /* 640x400, 15 kHz, 60 Hz interlaced (NTSC) */
827 "ntsc-lace", 60, 640, 400, TAG_HIRES, 106, 86, 88, 33, 76, 4,
828 FB_SYNC_BROADCAST, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
829 }, {
830 /* 640x256, 15 kHz, 50 Hz (PAL) */
831 "pal", 50, 640, 256, TAG_HIRES, 106, 86, 40, 14, 76, 2,
832 FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
833 }, {
834 /* 640x512, 15 kHz, 50 Hz interlaced (PAL) */
835 "pal-lace", 50, 640, 512, TAG_HIRES, 106, 86, 80, 29, 76, 4,
836 FB_SYNC_BROADCAST, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
837 }, {
838 /* 640x480, 29 kHz, 57 Hz */
839 "multiscan", 57, 640, 480, TAG_SHRES, 96, 112, 29, 8, 72, 8,
840 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
841 }, {
842 /* 640x960, 29 kHz, 57 Hz interlaced */
843 "multiscan-lace", 57, 640, 960, TAG_SHRES, 96, 112, 58, 16, 72,
844 16,
845 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
846 }, {
847 /* 640x200, 15 kHz, 72 Hz */
848 "euro36", 72, 640, 200, TAG_HIRES, 92, 124, 6, 6, 52, 5,
849 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
850 }, {
851 /* 640x400, 15 kHz, 72 Hz interlaced */
852 "euro36-lace", 72, 640, 400, TAG_HIRES, 92, 124, 12, 12, 52,
853 10,
854 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
855 }, {
856 /* 640x400, 29 kHz, 68 Hz */
857 "euro72", 68, 640, 400, TAG_SHRES, 164, 92, 9, 9, 80, 8,
858 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
859 }, {
860 /* 640x800, 29 kHz, 68 Hz interlaced */
861 "euro72-lace", 68, 640, 800, TAG_SHRES, 164, 92, 18, 18, 80,
862 16,
863 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
864 }, {
865 /* 800x300, 23 kHz, 70 Hz */
866 "super72", 70, 800, 300, TAG_SHRES, 212, 140, 10, 11, 80, 7,
867 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
868 }, {
869 /* 800x600, 23 kHz, 70 Hz interlaced */
870 "super72-lace", 70, 800, 600, TAG_SHRES, 212, 140, 20, 22, 80,
871 14,
872 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
873 }, {
874 /* 640x200, 27 kHz, 57 Hz doublescan */
875 "dblntsc", 57, 640, 200, TAG_SHRES, 196, 124, 18, 17, 80, 4,
876 0, FB_VMODE_DOUBLE | FB_VMODE_YWRAP
877 }, {
878 /* 640x400, 27 kHz, 57 Hz */
879 "dblntsc-ff", 57, 640, 400, TAG_SHRES, 196, 124, 36, 35, 80, 7,
880 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
881 }, {
882 /* 640x800, 27 kHz, 57 Hz interlaced */
883 "dblntsc-lace", 57, 640, 800, TAG_SHRES, 196, 124, 72, 70, 80,
884 14,
885 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
886 }, {
887 /* 640x256, 27 kHz, 47 Hz doublescan */
888 "dblpal", 47, 640, 256, TAG_SHRES, 196, 124, 14, 13, 80, 4,
889 0, FB_VMODE_DOUBLE | FB_VMODE_YWRAP
890 }, {
891 /* 640x512, 27 kHz, 47 Hz */
892 "dblpal-ff", 47, 640, 512, TAG_SHRES, 196, 124, 28, 27, 80, 7,
893 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
894 }, {
895 /* 640x1024, 27 kHz, 47 Hz interlaced */
896 "dblpal-lace", 47, 640, 1024, TAG_SHRES, 196, 124, 56, 54, 80,
897 14,
898 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
899 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100901 /*
902 * VGA Video Modes
903 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700904
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100905 {
906 /* 640x480, 31 kHz, 60 Hz (VGA) */
907 "vga", 60, 640, 480, TAG_SHRES, 64, 96, 30, 9, 112, 2,
908 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
909 }, {
910 /* 640x400, 31 kHz, 70 Hz (VGA) */
911 "vga70", 70, 640, 400, TAG_SHRES, 64, 96, 35, 12, 112, 2,
912 FB_SYNC_VERT_HIGH_ACT | FB_SYNC_COMP_HIGH_ACT,
913 FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
914 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915
916#if 0
917
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100918 /*
919 * A2024 video modes
920 * These modes don't work yet because there's no A2024 driver.
921 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100923 {
924 /* 1024x800, 10 Hz */
925 "a2024-10", 10, 1024, 800, TAG_HIRES, 0, 0, 0, 0, 0, 0,
926 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
927 }, {
928 /* 1024x800, 15 Hz */
929 "a2024-15", 15, 1024, 800, TAG_HIRES, 0, 0, 0, 0, 0, 0,
930 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
931 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700932#endif
933};
934
935#define NUM_TOTAL_MODES ARRAY_SIZE(ami_modedb)
936
937static char *mode_option __initdata = NULL;
938static int round_down_bpp = 1; /* for mode probing */
939
940 /*
941 * Some default modes
942 */
943
944
945#define DEFMODE_PAL 2 /* "pal" for PAL OCS/ECS */
946#define DEFMODE_NTSC 0 /* "ntsc" for NTSC OCS/ECS */
947#define DEFMODE_AMBER_PAL 3 /* "pal-lace" for flicker fixed PAL (A3000) */
948#define DEFMODE_AMBER_NTSC 1 /* "ntsc-lace" for flicker fixed NTSC (A3000) */
949#define DEFMODE_AGA 19 /* "vga70" for AGA */
950
951
952static int amifb_ilbm = 0; /* interleaved or normal bitplanes */
953static int amifb_inverse = 0;
954
Geert Uytterhoeven03c740a2011-11-21 21:53:57 +0100955static u32 amifb_hfmin __initdata; /* monitor hfreq lower limit (Hz) */
956static u32 amifb_hfmax __initdata; /* monitor hfreq upper limit (Hz) */
957static u16 amifb_vfmin __initdata; /* monitor vfreq lower limit (Hz) */
958static u16 amifb_vfmax __initdata; /* monitor vfreq upper limit (Hz) */
959
Linus Torvalds1da177e2005-04-16 15:20:36 -0700960
961 /*
962 * Macros for the conversion from real world values to hardware register
963 * values
964 *
965 * This helps us to keep our attention on the real stuff...
966 *
967 * Hardware limits for AGA:
968 *
969 * parameter min max step
970 * --------- --- ---- ----
971 * diwstrt_h 0 2047 1
972 * diwstrt_v 0 2047 1
973 * diwstop_h 0 4095 1
974 * diwstop_v 0 4095 1
975 *
976 * ddfstrt 0 2032 16
977 * ddfstop 0 2032 16
978 *
979 * htotal 8 2048 8
980 * hsstrt 0 2040 8
981 * hsstop 0 2040 8
982 * vtotal 1 4096 1
983 * vsstrt 0 4095 1
984 * vsstop 0 4095 1
985 * hcenter 0 2040 8
986 *
987 * hbstrt 0 2047 1
988 * hbstop 0 2047 1
989 * vbstrt 0 4095 1
990 * vbstop 0 4095 1
991 *
992 * Horizontal values are in 35 ns (SHRES) pixels
993 * Vertical values are in half scanlines
994 */
995
996/* bplcon1 (smooth scrolling) */
997
998#define hscroll2hw(hscroll) \
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100999 (((hscroll) << 12 & 0x3000) | ((hscroll) << 8 & 0xc300) | \
1000 ((hscroll) << 4 & 0x0c00) | ((hscroll) << 2 & 0x00f0) | \
1001 ((hscroll)>>2 & 0x000f))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001002
1003/* diwstrt/diwstop/diwhigh (visible display window) */
1004
1005#define diwstrt2hw(diwstrt_h, diwstrt_v) \
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001006 (((diwstrt_v) << 7 & 0xff00) | ((diwstrt_h)>>2 & 0x00ff))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007#define diwstop2hw(diwstop_h, diwstop_v) \
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001008 (((diwstop_v) << 7 & 0xff00) | ((diwstop_h)>>2 & 0x00ff))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001009#define diwhigh2hw(diwstrt_h, diwstrt_v, diwstop_h, diwstop_v) \
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001010 (((diwstop_h) << 3 & 0x2000) | ((diwstop_h) << 11 & 0x1800) | \
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011 ((diwstop_v)>>1 & 0x0700) | ((diwstrt_h)>>5 & 0x0020) | \
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001012 ((diwstrt_h) << 3 & 0x0018) | ((diwstrt_v)>>9 & 0x0007))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001013
1014/* ddfstrt/ddfstop (display DMA) */
1015
1016#define ddfstrt2hw(ddfstrt) div8(ddfstrt)
1017#define ddfstop2hw(ddfstop) div8(ddfstop)
1018
1019/* hsstrt/hsstop/htotal/vsstrt/vsstop/vtotal/hcenter (sync timings) */
1020
1021#define hsstrt2hw(hsstrt) (div8(hsstrt))
1022#define hsstop2hw(hsstop) (div8(hsstop))
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001023#define htotal2hw(htotal) (div8(htotal) - 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001024#define vsstrt2hw(vsstrt) (div2(vsstrt))
1025#define vsstop2hw(vsstop) (div2(vsstop))
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001026#define vtotal2hw(vtotal) (div2(vtotal) - 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001027#define hcenter2hw(htotal) (div8(htotal))
1028
1029/* hbstrt/hbstop/vbstrt/vbstop (blanking timings) */
1030
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001031#define hbstrt2hw(hbstrt) (((hbstrt) << 8 & 0x0700) | ((hbstrt)>>3 & 0x00ff))
1032#define hbstop2hw(hbstop) (((hbstop) << 8 & 0x0700) | ((hbstop)>>3 & 0x00ff))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033#define vbstrt2hw(vbstrt) (div2(vbstrt))
1034#define vbstop2hw(vbstop) (div2(vbstop))
1035
1036/* colour */
1037
1038#define rgb2hw8_high(red, green, blue) \
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001039 (((red & 0xf0) << 4) | (green & 0xf0) | ((blue & 0xf0)>>4))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001040#define rgb2hw8_low(red, green, blue) \
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001041 (((red & 0x0f) << 8) | ((green & 0x0f) << 4) | (blue & 0x0f))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042#define rgb2hw4(red, green, blue) \
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001043 (((red & 0xf0) << 4) | (green & 0xf0) | ((blue & 0xf0)>>4))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044#define rgb2hw2(red, green, blue) \
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001045 (((red & 0xc0) << 4) | (green & 0xc0) | ((blue & 0xc0)>>4))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001046
1047/* sprpos/sprctl (sprite positioning) */
1048
1049#define spr2hw_pos(start_v, start_h) \
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001050 (((start_v) << 7 & 0xff00) | ((start_h)>>3 & 0x00ff))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051#define spr2hw_ctl(start_v, start_h, stop_v) \
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001052 (((stop_v) << 7 & 0xff00) | ((start_v)>>4 & 0x0040) | \
1053 ((stop_v)>>5 & 0x0020) | ((start_h) << 3 & 0x0018) | \
1054 ((start_v)>>7 & 0x0004) | ((stop_v)>>8 & 0x0002) | \
1055 ((start_h)>>2 & 0x0001))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001056
1057/* get current vertical position of beam */
1058#define get_vbpos() ((u_short)((*(u_long volatile *)&custom.vposr >> 7) & 0xffe))
1059
1060 /*
1061 * Copper Initialisation List
1062 */
1063
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001064#define COPINITSIZE (sizeof(copins) * 40)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065
1066enum {
1067 cip_bplcon0
1068};
1069
1070 /*
1071 * Long Frame/Short Frame Copper List
1072 * Don't change the order, build_copper()/rebuild_copper() rely on this
1073 */
1074
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001075#define COPLISTSIZE (sizeof(copins) * 64)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076
1077enum {
1078 cop_wait, cop_bplcon0,
1079 cop_spr0ptrh, cop_spr0ptrl,
1080 cop_diwstrt, cop_diwstop,
1081 cop_diwhigh,
1082};
1083
1084 /*
1085 * Pixel modes for Bitplanes and Sprites
1086 */
1087
1088static u_short bplpixmode[3] = {
1089 BPC0_SHRES, /* 35 ns */
1090 BPC0_HIRES, /* 70 ns */
1091 0 /* 140 ns */
1092};
1093
1094static u_short sprpixmode[3] = {
1095 BPC3_SPRES1 | BPC3_SPRES0, /* 35 ns */
1096 BPC3_SPRES1, /* 70 ns */
1097 BPC3_SPRES0 /* 140 ns */
1098};
1099
1100 /*
1101 * Fetch modes for Bitplanes and Sprites
1102 */
1103
1104static u_short bplfetchmode[3] = {
1105 0, /* 1x */
1106 FMODE_BPL32, /* 2x */
1107 FMODE_BPAGEM | FMODE_BPL32 /* 4x */
1108};
1109
1110static u_short sprfetchmode[3] = {
1111 0, /* 1x */
1112 FMODE_SPR32, /* 2x */
1113 FMODE_SPAGEM | FMODE_SPR32 /* 4x */
1114};
1115
1116
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01001117/* --------------------------- Hardware routines --------------------------- */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001118
1119 /*
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01001120 * Get the video params out of `var'. If a value doesn't fit, round
1121 * it up, if it's too big, return -EINVAL.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001122 */
1123
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01001124static int ami_decode_var(struct fb_var_screeninfo *var, struct amifb_par *par,
1125 const struct fb_info *info)
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01001126{
1127 u_short clk_shift, line_shift;
1128 u_long maxfetchstop, fstrt, fsize, fconst, xres_n, yres_n;
1129 u_int htotal, vtotal;
1130
1131 /*
1132 * Find a matching Pixel Clock
1133 */
1134
1135 for (clk_shift = TAG_SHRES; clk_shift <= TAG_LORES; clk_shift++)
1136 if (var->pixclock <= pixclock[clk_shift])
1137 break;
1138 if (clk_shift > TAG_LORES) {
1139 DPRINTK("pixclock too high\n");
1140 return -EINVAL;
1141 }
1142 par->clk_shift = clk_shift;
1143
1144 /*
1145 * Check the Geometry Values
1146 */
1147
1148 if ((par->xres = var->xres) < 64)
1149 par->xres = 64;
1150 if ((par->yres = var->yres) < 64)
1151 par->yres = 64;
1152 if ((par->vxres = var->xres_virtual) < par->xres)
1153 par->vxres = par->xres;
1154 if ((par->vyres = var->yres_virtual) < par->yres)
1155 par->vyres = par->yres;
1156
1157 par->bpp = var->bits_per_pixel;
1158 if (!var->nonstd) {
1159 if (par->bpp < 1)
1160 par->bpp = 1;
1161 if (par->bpp > maxdepth[clk_shift]) {
1162 if (round_down_bpp && maxdepth[clk_shift])
1163 par->bpp = maxdepth[clk_shift];
1164 else {
1165 DPRINTK("invalid bpp\n");
1166 return -EINVAL;
1167 }
1168 }
1169 } else if (var->nonstd == FB_NONSTD_HAM) {
1170 if (par->bpp < 6)
1171 par->bpp = 6;
1172 if (par->bpp != 6) {
1173 if (par->bpp < 8)
1174 par->bpp = 8;
1175 if (par->bpp != 8 || !IS_AGA) {
1176 DPRINTK("invalid bpp for ham mode\n");
1177 return -EINVAL;
1178 }
1179 }
1180 } else {
1181 DPRINTK("unknown nonstd mode\n");
1182 return -EINVAL;
1183 }
1184
1185 /*
1186 * FB_VMODE_SMOOTH_XPAN will be cleared, if one of the folloing
1187 * checks failed and smooth scrolling is not possible
1188 */
1189
1190 par->vmode = var->vmode | FB_VMODE_SMOOTH_XPAN;
1191 switch (par->vmode & FB_VMODE_MASK) {
1192 case FB_VMODE_INTERLACED:
1193 line_shift = 0;
1194 break;
1195 case FB_VMODE_NONINTERLACED:
1196 line_shift = 1;
1197 break;
1198 case FB_VMODE_DOUBLE:
1199 if (!IS_AGA) {
1200 DPRINTK("double mode only possible with aga\n");
1201 return -EINVAL;
1202 }
1203 line_shift = 2;
1204 break;
1205 default:
1206 DPRINTK("unknown video mode\n");
1207 return -EINVAL;
1208 break;
1209 }
1210 par->line_shift = line_shift;
1211
1212 /*
1213 * Vertical and Horizontal Timings
1214 */
1215
1216 xres_n = par->xres << clk_shift;
1217 yres_n = par->yres << line_shift;
1218 par->htotal = down8((var->left_margin + par->xres + var->right_margin +
1219 var->hsync_len) << clk_shift);
1220 par->vtotal =
1221 down2(((var->upper_margin + par->yres + var->lower_margin +
1222 var->vsync_len) << line_shift) + 1);
1223
1224 if (IS_AGA)
1225 par->bplcon3 = sprpixmode[clk_shift];
1226 else
1227 par->bplcon3 = 0;
1228 if (var->sync & FB_SYNC_BROADCAST) {
1229 par->diwstop_h = par->htotal -
1230 ((var->right_margin - var->hsync_len) << clk_shift);
1231 if (IS_AGA)
1232 par->diwstop_h += mod4(var->hsync_len);
1233 else
1234 par->diwstop_h = down4(par->diwstop_h);
1235
1236 par->diwstrt_h = par->diwstop_h - xres_n;
1237 par->diwstop_v = par->vtotal -
1238 ((var->lower_margin - var->vsync_len) << line_shift);
1239 par->diwstrt_v = par->diwstop_v - yres_n;
1240 if (par->diwstop_h >= par->htotal + 8) {
1241 DPRINTK("invalid diwstop_h\n");
1242 return -EINVAL;
1243 }
1244 if (par->diwstop_v > par->vtotal) {
1245 DPRINTK("invalid diwstop_v\n");
1246 return -EINVAL;
1247 }
1248
1249 if (!IS_OCS) {
1250 /* Initialize sync with some reasonable values for pwrsave */
1251 par->hsstrt = 160;
1252 par->hsstop = 320;
1253 par->vsstrt = 30;
1254 par->vsstop = 34;
1255 } else {
1256 par->hsstrt = 0;
1257 par->hsstop = 0;
1258 par->vsstrt = 0;
1259 par->vsstop = 0;
1260 }
1261 if (par->vtotal > (PAL_VTOTAL + NTSC_VTOTAL) / 2) {
1262 /* PAL video mode */
1263 if (par->htotal != PAL_HTOTAL) {
1264 DPRINTK("htotal invalid for pal\n");
1265 return -EINVAL;
1266 }
1267 if (par->diwstrt_h < PAL_DIWSTRT_H) {
1268 DPRINTK("diwstrt_h too low for pal\n");
1269 return -EINVAL;
1270 }
1271 if (par->diwstrt_v < PAL_DIWSTRT_V) {
1272 DPRINTK("diwstrt_v too low for pal\n");
1273 return -EINVAL;
1274 }
1275 htotal = PAL_HTOTAL>>clk_shift;
1276 vtotal = PAL_VTOTAL>>1;
1277 if (!IS_OCS) {
1278 par->beamcon0 = BMC0_PAL;
1279 par->bplcon3 |= BPC3_BRDRBLNK;
1280 } else if (AMIGAHW_PRESENT(AGNUS_HR_PAL) ||
1281 AMIGAHW_PRESENT(AGNUS_HR_NTSC)) {
1282 par->beamcon0 = BMC0_PAL;
1283 par->hsstop = 1;
1284 } else if (amiga_vblank != 50) {
1285 DPRINTK("pal not supported by this chipset\n");
1286 return -EINVAL;
1287 }
1288 } else {
1289 /* NTSC video mode
1290 * In the AGA chipset seems to be hardware bug with BPC3_BRDRBLNK
1291 * and NTSC activated, so than better let diwstop_h <= 1812
1292 */
1293 if (par->htotal != NTSC_HTOTAL) {
1294 DPRINTK("htotal invalid for ntsc\n");
1295 return -EINVAL;
1296 }
1297 if (par->diwstrt_h < NTSC_DIWSTRT_H) {
1298 DPRINTK("diwstrt_h too low for ntsc\n");
1299 return -EINVAL;
1300 }
1301 if (par->diwstrt_v < NTSC_DIWSTRT_V) {
1302 DPRINTK("diwstrt_v too low for ntsc\n");
1303 return -EINVAL;
1304 }
1305 htotal = NTSC_HTOTAL>>clk_shift;
1306 vtotal = NTSC_VTOTAL>>1;
1307 if (!IS_OCS) {
1308 par->beamcon0 = 0;
1309 par->bplcon3 |= BPC3_BRDRBLNK;
1310 } else if (AMIGAHW_PRESENT(AGNUS_HR_PAL) ||
1311 AMIGAHW_PRESENT(AGNUS_HR_NTSC)) {
1312 par->beamcon0 = 0;
1313 par->hsstop = 1;
1314 } else if (amiga_vblank != 60) {
1315 DPRINTK("ntsc not supported by this chipset\n");
1316 return -EINVAL;
1317 }
1318 }
1319 if (IS_OCS) {
1320 if (par->diwstrt_h >= 1024 || par->diwstop_h < 1024 ||
1321 par->diwstrt_v >= 512 || par->diwstop_v < 256) {
1322 DPRINTK("invalid position for display on ocs\n");
1323 return -EINVAL;
1324 }
1325 }
1326 } else if (!IS_OCS) {
1327 /* Programmable video mode */
1328 par->hsstrt = var->right_margin << clk_shift;
1329 par->hsstop = (var->right_margin + var->hsync_len) << clk_shift;
1330 par->diwstop_h = par->htotal - mod8(par->hsstrt) + 8 - (1 << clk_shift);
1331 if (!IS_AGA)
1332 par->diwstop_h = down4(par->diwstop_h) - 16;
1333 par->diwstrt_h = par->diwstop_h - xres_n;
1334 par->hbstop = par->diwstrt_h + 4;
1335 par->hbstrt = par->diwstop_h + 4;
1336 if (par->hbstrt >= par->htotal + 8)
1337 par->hbstrt -= par->htotal;
1338 par->hcenter = par->hsstrt + (par->htotal >> 1);
1339 par->vsstrt = var->lower_margin << line_shift;
1340 par->vsstop = (var->lower_margin + var->vsync_len) << line_shift;
1341 par->diwstop_v = par->vtotal;
1342 if ((par->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED)
1343 par->diwstop_v -= 2;
1344 par->diwstrt_v = par->diwstop_v - yres_n;
1345 par->vbstop = par->diwstrt_v - 2;
1346 par->vbstrt = par->diwstop_v - 2;
1347 if (par->vtotal > 2048) {
1348 DPRINTK("vtotal too high\n");
1349 return -EINVAL;
1350 }
1351 if (par->htotal > 2048) {
1352 DPRINTK("htotal too high\n");
1353 return -EINVAL;
1354 }
1355 par->bplcon3 |= BPC3_EXTBLKEN;
1356 par->beamcon0 = BMC0_HARDDIS | BMC0_VARVBEN | BMC0_LOLDIS |
1357 BMC0_VARVSYEN | BMC0_VARHSYEN | BMC0_VARBEAMEN |
1358 BMC0_PAL | BMC0_VARCSYEN;
1359 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
1360 par->beamcon0 |= BMC0_HSYTRUE;
1361 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
1362 par->beamcon0 |= BMC0_VSYTRUE;
1363 if (var->sync & FB_SYNC_COMP_HIGH_ACT)
1364 par->beamcon0 |= BMC0_CSYTRUE;
1365 htotal = par->htotal>>clk_shift;
1366 vtotal = par->vtotal>>1;
1367 } else {
1368 DPRINTK("only broadcast modes possible for ocs\n");
1369 return -EINVAL;
1370 }
1371
1372 /*
1373 * Checking the DMA timing
1374 */
1375
1376 fconst = 16 << maxfmode << clk_shift;
1377
1378 /*
1379 * smallest window start value without turn off other dma cycles
1380 * than sprite1-7, unless you change min_fstrt
1381 */
1382
1383
1384 fsize = ((maxfmode + clk_shift <= 1) ? fconst : 64);
1385 fstrt = downx(fconst, par->diwstrt_h - 4) - fsize;
1386 if (fstrt < min_fstrt) {
1387 DPRINTK("fetch start too low\n");
1388 return -EINVAL;
1389 }
1390
1391 /*
1392 * smallest window start value where smooth scrolling is possible
1393 */
1394
1395 fstrt = downx(fconst, par->diwstrt_h - fconst + (1 << clk_shift) - 4) -
1396 fsize;
1397 if (fstrt < min_fstrt)
1398 par->vmode &= ~FB_VMODE_SMOOTH_XPAN;
1399
1400 maxfetchstop = down16(par->htotal - 80);
1401
1402 fstrt = downx(fconst, par->diwstrt_h - 4) - 64 - fconst;
1403 fsize = upx(fconst, xres_n +
1404 modx(fconst, downx(1 << clk_shift, par->diwstrt_h - 4)));
1405 if (fstrt + fsize > maxfetchstop)
1406 par->vmode &= ~FB_VMODE_SMOOTH_XPAN;
1407
1408 fsize = upx(fconst, xres_n);
1409 if (fstrt + fsize > maxfetchstop) {
1410 DPRINTK("fetch stop too high\n");
1411 return -EINVAL;
1412 }
1413
1414 if (maxfmode + clk_shift <= 1) {
1415 fsize = up64(xres_n + fconst - 1);
1416 if (min_fstrt + fsize - 64 > maxfetchstop)
1417 par->vmode &= ~FB_VMODE_SMOOTH_XPAN;
1418
1419 fsize = up64(xres_n);
1420 if (min_fstrt + fsize - 64 > maxfetchstop) {
1421 DPRINTK("fetch size too high\n");
1422 return -EINVAL;
1423 }
1424
1425 fsize -= 64;
1426 } else
1427 fsize -= fconst;
1428
1429 /*
1430 * Check if there is enough time to update the bitplane pointers for ywrap
1431 */
1432
1433 if (par->htotal - fsize - 64 < par->bpp * 64)
1434 par->vmode &= ~FB_VMODE_YWRAP;
1435
1436 /*
1437 * Bitplane calculations and check the Memory Requirements
1438 */
1439
1440 if (amifb_ilbm) {
1441 par->next_plane = div8(upx(16 << maxfmode, par->vxres));
1442 par->next_line = par->bpp * par->next_plane;
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01001443 if (par->next_line * par->vyres > info->fix.smem_len) {
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01001444 DPRINTK("too few video mem\n");
1445 return -EINVAL;
1446 }
1447 } else {
1448 par->next_line = div8(upx(16 << maxfmode, par->vxres));
1449 par->next_plane = par->vyres * par->next_line;
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01001450 if (par->next_plane * par->bpp > info->fix.smem_len) {
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01001451 DPRINTK("too few video mem\n");
1452 return -EINVAL;
1453 }
1454 }
1455
1456 /*
1457 * Hardware Register Values
1458 */
1459
1460 par->bplcon0 = BPC0_COLOR | bplpixmode[clk_shift];
1461 if (!IS_OCS)
1462 par->bplcon0 |= BPC0_ECSENA;
1463 if (par->bpp == 8)
1464 par->bplcon0 |= BPC0_BPU3;
1465 else
1466 par->bplcon0 |= par->bpp << 12;
1467 if (var->nonstd == FB_NONSTD_HAM)
1468 par->bplcon0 |= BPC0_HAM;
1469 if (var->sync & FB_SYNC_EXT)
1470 par->bplcon0 |= BPC0_ERSY;
1471
1472 if (IS_AGA)
1473 par->fmode = bplfetchmode[maxfmode];
1474
1475 switch (par->vmode & FB_VMODE_MASK) {
1476 case FB_VMODE_INTERLACED:
1477 par->bplcon0 |= BPC0_LACE;
1478 break;
1479 case FB_VMODE_DOUBLE:
1480 if (IS_AGA)
1481 par->fmode |= FMODE_SSCAN2 | FMODE_BSCAN2;
1482 break;
1483 }
1484
1485 if (!((par->vmode ^ var->vmode) & FB_VMODE_YWRAP)) {
1486 par->xoffset = var->xoffset;
1487 par->yoffset = var->yoffset;
1488 if (par->vmode & FB_VMODE_YWRAP) {
1489 if (par->xoffset || par->yoffset < 0 ||
1490 par->yoffset >= par->vyres)
1491 par->xoffset = par->yoffset = 0;
1492 } else {
1493 if (par->xoffset < 0 ||
1494 par->xoffset > upx(16 << maxfmode, par->vxres - par->xres) ||
1495 par->yoffset < 0 || par->yoffset > par->vyres - par->yres)
1496 par->xoffset = par->yoffset = 0;
1497 }
1498 } else
1499 par->xoffset = par->yoffset = 0;
1500
1501 par->crsr.crsr_x = par->crsr.crsr_y = 0;
1502 par->crsr.spot_x = par->crsr.spot_y = 0;
1503 par->crsr.height = par->crsr.width = 0;
1504
1505 return 0;
1506}
1507
1508 /*
1509 * Fill the `var' structure based on the values in `par' and maybe
1510 * other values read out of the hardware.
1511 */
1512
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01001513static void ami_encode_var(struct fb_var_screeninfo *var,
1514 struct amifb_par *par)
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01001515{
1516 u_short clk_shift, line_shift;
1517
1518 memset(var, 0, sizeof(struct fb_var_screeninfo));
1519
1520 clk_shift = par->clk_shift;
1521 line_shift = par->line_shift;
1522
1523 var->xres = par->xres;
1524 var->yres = par->yres;
1525 var->xres_virtual = par->vxres;
1526 var->yres_virtual = par->vyres;
1527 var->xoffset = par->xoffset;
1528 var->yoffset = par->yoffset;
1529
1530 var->bits_per_pixel = par->bpp;
1531 var->grayscale = 0;
1532
1533 var->red.offset = 0;
1534 var->red.msb_right = 0;
1535 var->red.length = par->bpp;
1536 if (par->bplcon0 & BPC0_HAM)
1537 var->red.length -= 2;
1538 var->blue = var->green = var->red;
1539 var->transp.offset = 0;
1540 var->transp.length = 0;
1541 var->transp.msb_right = 0;
1542
1543 if (par->bplcon0 & BPC0_HAM)
1544 var->nonstd = FB_NONSTD_HAM;
1545 else
1546 var->nonstd = 0;
1547 var->activate = 0;
1548
1549 var->height = -1;
1550 var->width = -1;
1551
1552 var->pixclock = pixclock[clk_shift];
1553
1554 if (IS_AGA && par->fmode & FMODE_BSCAN2)
1555 var->vmode = FB_VMODE_DOUBLE;
1556 else if (par->bplcon0 & BPC0_LACE)
1557 var->vmode = FB_VMODE_INTERLACED;
1558 else
1559 var->vmode = FB_VMODE_NONINTERLACED;
1560
1561 if (!IS_OCS && par->beamcon0 & BMC0_VARBEAMEN) {
1562 var->hsync_len = (par->hsstop - par->hsstrt)>>clk_shift;
1563 var->right_margin = par->hsstrt>>clk_shift;
1564 var->left_margin = (par->htotal>>clk_shift) - var->xres - var->right_margin - var->hsync_len;
1565 var->vsync_len = (par->vsstop - par->vsstrt)>>line_shift;
1566 var->lower_margin = par->vsstrt>>line_shift;
1567 var->upper_margin = (par->vtotal>>line_shift) - var->yres - var->lower_margin - var->vsync_len;
1568 var->sync = 0;
1569 if (par->beamcon0 & BMC0_HSYTRUE)
1570 var->sync |= FB_SYNC_HOR_HIGH_ACT;
1571 if (par->beamcon0 & BMC0_VSYTRUE)
1572 var->sync |= FB_SYNC_VERT_HIGH_ACT;
1573 if (par->beamcon0 & BMC0_CSYTRUE)
1574 var->sync |= FB_SYNC_COMP_HIGH_ACT;
1575 } else {
1576 var->sync = FB_SYNC_BROADCAST;
1577 var->hsync_len = (152>>clk_shift) + mod4(par->diwstop_h);
1578 var->right_margin = ((par->htotal - down4(par->diwstop_h))>>clk_shift) + var->hsync_len;
1579 var->left_margin = (par->htotal>>clk_shift) - var->xres - var->right_margin - var->hsync_len;
1580 var->vsync_len = 4>>line_shift;
1581 var->lower_margin = ((par->vtotal - par->diwstop_v)>>line_shift) + var->vsync_len;
1582 var->upper_margin = (((par->vtotal - 2)>>line_shift) + 1) - var->yres -
1583 var->lower_margin - var->vsync_len;
1584 }
1585
1586 if (par->bplcon0 & BPC0_ERSY)
1587 var->sync |= FB_SYNC_EXT;
1588 if (par->vmode & FB_VMODE_YWRAP)
1589 var->vmode |= FB_VMODE_YWRAP;
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01001590}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001591
1592
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01001593 /*
1594 * Update hardware
1595 */
1596
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01001597static void ami_update_par(struct fb_info *info)
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01001598{
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01001599 struct amifb_par *par = info->par;
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01001600 short clk_shift, vshift, fstrt, fsize, fstop, fconst, shift, move, mod;
1601
1602 clk_shift = par->clk_shift;
1603
1604 if (!(par->vmode & FB_VMODE_SMOOTH_XPAN))
1605 par->xoffset = upx(16 << maxfmode, par->xoffset);
1606
1607 fconst = 16 << maxfmode << clk_shift;
1608 vshift = modx(16 << maxfmode, par->xoffset);
1609 fstrt = par->diwstrt_h - (vshift << clk_shift) - 4;
1610 fsize = (par->xres + vshift) << clk_shift;
1611 shift = modx(fconst, fstrt);
1612 move = downx(2 << maxfmode, div8(par->xoffset));
1613 if (maxfmode + clk_shift > 1) {
1614 fstrt = downx(fconst, fstrt) - 64;
1615 fsize = upx(fconst, fsize);
1616 fstop = fstrt + fsize - fconst;
1617 } else {
1618 mod = fstrt = downx(fconst, fstrt) - fconst;
1619 fstop = fstrt + upx(fconst, fsize) - 64;
1620 fsize = up64(fsize);
1621 fstrt = fstop - fsize + 64;
1622 if (fstrt < min_fstrt) {
1623 fstop += min_fstrt - fstrt;
1624 fstrt = min_fstrt;
1625 }
1626 move = move - div8((mod - fstrt)>>clk_shift);
1627 }
1628 mod = par->next_line - div8(fsize>>clk_shift);
1629 par->ddfstrt = fstrt;
1630 par->ddfstop = fstop;
1631 par->bplcon1 = hscroll2hw(shift);
1632 par->bpl2mod = mod;
1633 if (par->bplcon0 & BPC0_LACE)
1634 par->bpl2mod += par->next_line;
1635 if (IS_AGA && (par->fmode & FMODE_BSCAN2))
1636 par->bpl1mod = -div8(fsize>>clk_shift);
1637 else
1638 par->bpl1mod = par->bpl2mod;
1639
1640 if (par->yoffset) {
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01001641 par->bplpt0 = info->fix.smem_start +
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01001642 par->next_line * par->yoffset + move;
1643 if (par->vmode & FB_VMODE_YWRAP) {
1644 if (par->yoffset > par->vyres - par->yres) {
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01001645 par->bplpt0wrap = info->fix.smem_start + move;
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01001646 if (par->bplcon0 & BPC0_LACE &&
1647 mod2(par->diwstrt_v + par->vyres -
1648 par->yoffset))
1649 par->bplpt0wrap += par->next_line;
1650 }
1651 }
1652 } else
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01001653 par->bplpt0 = info->fix.smem_start + move;
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01001654
1655 if (par->bplcon0 & BPC0_LACE && mod2(par->diwstrt_v))
1656 par->bplpt0 += par->next_line;
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01001657}
1658
1659
1660 /*
1661 * Pan or Wrap the Display
1662 *
1663 * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
1664 * in `var'.
1665 */
1666
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01001667static void ami_pan_var(struct fb_var_screeninfo *var, struct fb_info *info)
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01001668{
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01001669 struct amifb_par *par = info->par;
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01001670
1671 par->xoffset = var->xoffset;
1672 par->yoffset = var->yoffset;
1673 if (var->vmode & FB_VMODE_YWRAP)
1674 par->vmode |= FB_VMODE_YWRAP;
1675 else
1676 par->vmode &= ~FB_VMODE_YWRAP;
1677
1678 do_vmode_pan = 0;
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01001679 ami_update_par(info);
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01001680 do_vmode_pan = 1;
1681}
1682
1683
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01001684static void ami_update_display(const struct amifb_par *par)
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01001685{
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01001686 custom.bplcon1 = par->bplcon1;
1687 custom.bpl1mod = par->bpl1mod;
1688 custom.bpl2mod = par->bpl2mod;
1689 custom.ddfstrt = ddfstrt2hw(par->ddfstrt);
1690 custom.ddfstop = ddfstop2hw(par->ddfstop);
1691}
1692
1693 /*
1694 * Change the video mode (called by VBlank interrupt)
1695 */
1696
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01001697static void ami_init_display(const struct amifb_par *par)
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01001698{
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01001699 int i;
1700
1701 custom.bplcon0 = par->bplcon0 & ~BPC0_LACE;
1702 custom.bplcon2 = (IS_OCS ? 0 : BPC2_KILLEHB) | BPC2_PF2P2 | BPC2_PF1P2;
1703 if (!IS_OCS) {
1704 custom.bplcon3 = par->bplcon3;
1705 if (IS_AGA)
1706 custom.bplcon4 = BPC4_ESPRM4 | BPC4_OSPRM4;
1707 if (par->beamcon0 & BMC0_VARBEAMEN) {
1708 custom.htotal = htotal2hw(par->htotal);
1709 custom.hbstrt = hbstrt2hw(par->hbstrt);
1710 custom.hbstop = hbstop2hw(par->hbstop);
1711 custom.hsstrt = hsstrt2hw(par->hsstrt);
1712 custom.hsstop = hsstop2hw(par->hsstop);
1713 custom.hcenter = hcenter2hw(par->hcenter);
1714 custom.vtotal = vtotal2hw(par->vtotal);
1715 custom.vbstrt = vbstrt2hw(par->vbstrt);
1716 custom.vbstop = vbstop2hw(par->vbstop);
1717 custom.vsstrt = vsstrt2hw(par->vsstrt);
1718 custom.vsstop = vsstop2hw(par->vsstop);
1719 }
1720 }
1721 if (!IS_OCS || par->hsstop)
1722 custom.beamcon0 = par->beamcon0;
1723 if (IS_AGA)
1724 custom.fmode = par->fmode;
1725
1726 /*
1727 * The minimum period for audio depends on htotal
1728 */
1729
1730 amiga_audio_min_period = div16(par->htotal);
1731
1732 is_lace = par->bplcon0 & BPC0_LACE ? 1 : 0;
1733#if 1
1734 if (is_lace) {
1735 i = custom.vposr >> 15;
1736 } else {
1737 custom.vposw = custom.vposr | 0x8000;
1738 i = 1;
1739 }
1740#else
1741 i = 1;
1742 custom.vposw = custom.vposr | 0x8000;
1743#endif
1744 custom.cop2lc = (u_short *)ZTWO_PADDR(copdisplay.list[currentcop][i]);
1745}
1746
1747 /*
1748 * (Un)Blank the screen (called by VBlank interrupt)
1749 */
1750
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01001751static void ami_do_blank(const struct amifb_par *par)
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01001752{
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01001753#if defined(CONFIG_FB_AMIGA_AGA)
1754 u_short bplcon3 = par->bplcon3;
1755#endif
1756 u_char red, green, blue;
1757
1758 if (do_blank > 0) {
1759 custom.dmacon = DMAF_RASTER | DMAF_SPRITE;
1760 red = green = blue = 0;
1761 if (!IS_OCS && do_blank > 1) {
1762 switch (do_blank) {
1763 case FB_BLANK_VSYNC_SUSPEND:
1764 custom.hsstrt = hsstrt2hw(par->hsstrt);
1765 custom.hsstop = hsstop2hw(par->hsstop);
1766 custom.vsstrt = vsstrt2hw(par->vtotal + 4);
1767 custom.vsstop = vsstop2hw(par->vtotal + 4);
1768 break;
1769 case FB_BLANK_HSYNC_SUSPEND:
1770 custom.hsstrt = hsstrt2hw(par->htotal + 16);
1771 custom.hsstop = hsstop2hw(par->htotal + 16);
1772 custom.vsstrt = vsstrt2hw(par->vsstrt);
1773 custom.vsstop = vsstrt2hw(par->vsstop);
1774 break;
1775 case FB_BLANK_POWERDOWN:
1776 custom.hsstrt = hsstrt2hw(par->htotal + 16);
1777 custom.hsstop = hsstop2hw(par->htotal + 16);
1778 custom.vsstrt = vsstrt2hw(par->vtotal + 4);
1779 custom.vsstop = vsstop2hw(par->vtotal + 4);
1780 break;
1781 }
1782 if (!(par->beamcon0 & BMC0_VARBEAMEN)) {
1783 custom.htotal = htotal2hw(par->htotal);
1784 custom.vtotal = vtotal2hw(par->vtotal);
1785 custom.beamcon0 = BMC0_HARDDIS | BMC0_VARBEAMEN |
1786 BMC0_VARVSYEN | BMC0_VARHSYEN | BMC0_VARCSYEN;
1787 }
1788 }
1789 } else {
1790 custom.dmacon = DMAF_SETCLR | DMAF_RASTER | DMAF_SPRITE;
1791 red = red0;
1792 green = green0;
1793 blue = blue0;
1794 if (!IS_OCS) {
1795 custom.hsstrt = hsstrt2hw(par->hsstrt);
1796 custom.hsstop = hsstop2hw(par->hsstop);
1797 custom.vsstrt = vsstrt2hw(par->vsstrt);
1798 custom.vsstop = vsstop2hw(par->vsstop);
1799 custom.beamcon0 = par->beamcon0;
1800 }
1801 }
1802#if defined(CONFIG_FB_AMIGA_AGA)
1803 if (IS_AGA) {
1804 custom.bplcon3 = bplcon3;
1805 custom.color[0] = rgb2hw8_high(red, green, blue);
1806 custom.bplcon3 = bplcon3 | BPC3_LOCT;
1807 custom.color[0] = rgb2hw8_low(red, green, blue);
1808 custom.bplcon3 = bplcon3;
1809 } else
1810#endif
1811#if defined(CONFIG_FB_AMIGA_ECS)
1812 if (par->bplcon0 & BPC0_SHRES) {
1813 u_short color, mask;
1814 int i;
1815
1816 mask = 0x3333;
1817 color = rgb2hw2(red, green, blue);
1818 for (i = 12; i >= 0; i -= 4)
1819 custom.color[i] = ecs_palette[i] = (ecs_palette[i] & mask) | color;
1820 mask <<= 2; color >>= 2;
1821 for (i = 3; i >= 0; i--)
1822 custom.color[i] = ecs_palette[i] = (ecs_palette[i] & mask) | color;
1823 } else
1824#endif
1825 custom.color[0] = rgb2hw4(red, green, blue);
1826 is_blanked = do_blank > 0 ? do_blank : 0;
1827}
1828
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01001829static int ami_get_fix_cursorinfo(struct fb_fix_cursorinfo *fix,
1830 const struct amifb_par *par)
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01001831{
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01001832 fix->crsr_width = fix->crsr_xsize = par->crsr.width;
1833 fix->crsr_height = fix->crsr_ysize = par->crsr.height;
1834 fix->crsr_color1 = 17;
1835 fix->crsr_color2 = 18;
1836 return 0;
1837}
1838
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01001839static int ami_get_var_cursorinfo(struct fb_var_cursorinfo *var,
1840 u_char __user *data,
1841 const struct amifb_par *par)
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01001842{
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01001843 register u_short *lspr, *sspr;
1844#ifdef __mc68000__
1845 register u_long datawords asm ("d2");
1846#else
1847 register u_long datawords;
1848#endif
1849 register short delta;
1850 register u_char color;
1851 short height, width, bits, words;
1852 int size, alloc;
1853
1854 size = par->crsr.height * par->crsr.width;
1855 alloc = var->height * var->width;
1856 var->height = par->crsr.height;
1857 var->width = par->crsr.width;
1858 var->xspot = par->crsr.spot_x;
1859 var->yspot = par->crsr.spot_y;
1860 if (size > var->height * var->width)
1861 return -ENAMETOOLONG;
1862 if (!access_ok(VERIFY_WRITE, data, size))
1863 return -EFAULT;
1864 delta = 1 << par->crsr.fmode;
1865 lspr = lofsprite + (delta << 1);
1866 if (par->bplcon0 & BPC0_LACE)
1867 sspr = shfsprite + (delta << 1);
1868 else
1869 sspr = NULL;
1870 for (height = (short)var->height - 1; height >= 0; height--) {
1871 bits = 0; words = delta; datawords = 0;
1872 for (width = (short)var->width - 1; width >= 0; width--) {
1873 if (bits == 0) {
1874 bits = 16; --words;
1875#ifdef __mc68000__
1876 asm volatile ("movew %1@(%3:w:2),%0 ; swap %0 ; movew %1@+,%0"
1877 : "=d" (datawords), "=a" (lspr) : "1" (lspr), "d" (delta));
1878#else
1879 datawords = (*(lspr + delta) << 16) | (*lspr++);
1880#endif
1881 }
1882 --bits;
1883#ifdef __mc68000__
1884 asm volatile (
1885 "clrb %0 ; swap %1 ; lslw #1,%1 ; roxlb #1,%0 ; "
1886 "swap %1 ; lslw #1,%1 ; roxlb #1,%0"
1887 : "=d" (color), "=d" (datawords) : "1" (datawords));
1888#else
1889 color = (((datawords >> 30) & 2)
1890 | ((datawords >> 15) & 1));
1891 datawords <<= 1;
1892#endif
1893 put_user(color, data++);
1894 }
1895 if (bits > 0) {
1896 --words; ++lspr;
1897 }
1898 while (--words >= 0)
1899 ++lspr;
1900#ifdef __mc68000__
1901 asm volatile ("lea %0@(%4:w:2),%0 ; tstl %1 ; jeq 1f ; exg %0,%1\n1:"
1902 : "=a" (lspr), "=a" (sspr) : "0" (lspr), "1" (sspr), "d" (delta));
1903#else
1904 lspr += delta;
1905 if (sspr) {
1906 u_short *tmp = lspr;
1907 lspr = sspr;
1908 sspr = tmp;
1909 }
1910#endif
1911 }
1912 return 0;
1913}
1914
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01001915static int ami_set_var_cursorinfo(struct fb_var_cursorinfo *var,
1916 u_char __user *data, struct amifb_par *par)
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01001917{
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01001918 register u_short *lspr, *sspr;
1919#ifdef __mc68000__
1920 register u_long datawords asm ("d2");
1921#else
1922 register u_long datawords;
1923#endif
1924 register short delta;
1925 u_short fmode;
1926 short height, width, bits, words;
1927
1928 if (!var->width)
1929 return -EINVAL;
1930 else if (var->width <= 16)
1931 fmode = TAG_FMODE_1;
1932 else if (var->width <= 32)
1933 fmode = TAG_FMODE_2;
1934 else if (var->width <= 64)
1935 fmode = TAG_FMODE_4;
1936 else
1937 return -EINVAL;
1938 if (fmode > maxfmode)
1939 return -EINVAL;
1940 if (!var->height)
1941 return -EINVAL;
1942 if (!access_ok(VERIFY_READ, data, var->width * var->height))
1943 return -EFAULT;
1944 delta = 1 << fmode;
1945 lofsprite = shfsprite = (u_short *)spritememory;
1946 lspr = lofsprite + (delta << 1);
1947 if (par->bplcon0 & BPC0_LACE) {
1948 if (((var->height + 4) << fmode << 2) > SPRITEMEMSIZE)
1949 return -EINVAL;
1950 memset(lspr, 0, (var->height + 4) << fmode << 2);
1951 shfsprite += ((var->height + 5)&-2) << fmode;
1952 sspr = shfsprite + (delta << 1);
1953 } else {
1954 if (((var->height + 2) << fmode << 2) > SPRITEMEMSIZE)
1955 return -EINVAL;
1956 memset(lspr, 0, (var->height + 2) << fmode << 2);
1957 sspr = NULL;
1958 }
1959 for (height = (short)var->height - 1; height >= 0; height--) {
1960 bits = 16; words = delta; datawords = 0;
1961 for (width = (short)var->width - 1; width >= 0; width--) {
1962 unsigned long tdata = 0;
1963 get_user(tdata, data);
1964 data++;
1965#ifdef __mc68000__
1966 asm volatile (
1967 "lsrb #1,%2 ; roxlw #1,%0 ; swap %0 ; "
1968 "lsrb #1,%2 ; roxlw #1,%0 ; swap %0"
1969 : "=d" (datawords)
1970 : "0" (datawords), "d" (tdata));
1971#else
1972 datawords = ((datawords << 1) & 0xfffefffe);
1973 datawords |= tdata & 1;
1974 datawords |= (tdata & 2) << (16 - 1);
1975#endif
1976 if (--bits == 0) {
1977 bits = 16; --words;
1978#ifdef __mc68000__
1979 asm volatile ("swap %2 ; movew %2,%0@(%3:w:2) ; swap %2 ; movew %2,%0@+"
1980 : "=a" (lspr) : "0" (lspr), "d" (datawords), "d" (delta));
1981#else
1982 *(lspr + delta) = (u_short) (datawords >> 16);
1983 *lspr++ = (u_short) (datawords & 0xffff);
1984#endif
1985 }
1986 }
1987 if (bits < 16) {
1988 --words;
1989#ifdef __mc68000__
1990 asm volatile (
1991 "swap %2 ; lslw %4,%2 ; movew %2,%0@(%3:w:2) ; "
1992 "swap %2 ; lslw %4,%2 ; movew %2,%0@+"
1993 : "=a" (lspr) : "0" (lspr), "d" (datawords), "d" (delta), "d" (bits));
1994#else
1995 *(lspr + delta) = (u_short) (datawords >> (16 + bits));
1996 *lspr++ = (u_short) ((datawords & 0x0000ffff) >> bits);
1997#endif
1998 }
1999 while (--words >= 0) {
2000#ifdef __mc68000__
2001 asm volatile ("moveql #0,%%d0 ; movew %%d0,%0@(%2:w:2) ; movew %%d0,%0@+"
2002 : "=a" (lspr) : "0" (lspr), "d" (delta) : "d0");
2003#else
2004 *(lspr + delta) = 0;
2005 *lspr++ = 0;
2006#endif
2007 }
2008#ifdef __mc68000__
2009 asm volatile ("lea %0@(%4:w:2),%0 ; tstl %1 ; jeq 1f ; exg %0,%1\n1:"
2010 : "=a" (lspr), "=a" (sspr) : "0" (lspr), "1" (sspr), "d" (delta));
2011#else
2012 lspr += delta;
2013 if (sspr) {
2014 u_short *tmp = lspr;
2015 lspr = sspr;
2016 sspr = tmp;
2017 }
2018#endif
2019 }
2020 par->crsr.height = var->height;
2021 par->crsr.width = var->width;
2022 par->crsr.spot_x = var->xspot;
2023 par->crsr.spot_y = var->yspot;
2024 par->crsr.fmode = fmode;
2025 if (IS_AGA) {
2026 par->fmode &= ~(FMODE_SPAGEM | FMODE_SPR32);
2027 par->fmode |= sprfetchmode[fmode];
2028 custom.fmode = par->fmode;
2029 }
2030 return 0;
2031}
2032
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01002033static int ami_get_cursorstate(struct fb_cursorstate *state,
2034 const struct amifb_par *par)
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01002035{
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01002036 state->xoffset = par->crsr.crsr_x;
2037 state->yoffset = par->crsr.crsr_y;
2038 state->mode = cursormode;
2039 return 0;
2040}
2041
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01002042static int ami_set_cursorstate(struct fb_cursorstate *state,
2043 struct amifb_par *par)
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01002044{
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01002045 par->crsr.crsr_x = state->xoffset;
2046 par->crsr.crsr_y = state->yoffset;
2047 if ((cursormode = state->mode) == FB_CURSOR_OFF)
2048 cursorstate = -1;
2049 do_cursor = 1;
2050 return 0;
2051}
2052
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01002053static void ami_set_sprite(const struct amifb_par *par)
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01002054{
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01002055 copins *copl, *cops;
2056 u_short hs, vs, ve;
2057 u_long pl, ps, pt;
2058 short mx, my;
2059
2060 cops = copdisplay.list[currentcop][0];
2061 copl = copdisplay.list[currentcop][1];
2062 ps = pl = ZTWO_PADDR(dummysprite);
2063 mx = par->crsr.crsr_x - par->crsr.spot_x;
2064 my = par->crsr.crsr_y - par->crsr.spot_y;
2065 if (!(par->vmode & FB_VMODE_YWRAP)) {
2066 mx -= par->xoffset;
2067 my -= par->yoffset;
2068 }
2069 if (!is_blanked && cursorstate > 0 && par->crsr.height > 0 &&
2070 mx > -(short)par->crsr.width && mx < par->xres &&
2071 my > -(short)par->crsr.height && my < par->yres) {
2072 pl = ZTWO_PADDR(lofsprite);
2073 hs = par->diwstrt_h + (mx << par->clk_shift) - 4;
2074 vs = par->diwstrt_v + (my << par->line_shift);
2075 ve = vs + (par->crsr.height << par->line_shift);
2076 if (par->bplcon0 & BPC0_LACE) {
2077 ps = ZTWO_PADDR(shfsprite);
2078 lofsprite[0] = spr2hw_pos(vs, hs);
2079 shfsprite[0] = spr2hw_pos(vs + 1, hs);
2080 if (mod2(vs)) {
2081 lofsprite[1 << par->crsr.fmode] = spr2hw_ctl(vs, hs, ve);
2082 shfsprite[1 << par->crsr.fmode] = spr2hw_ctl(vs + 1, hs, ve + 1);
2083 pt = pl; pl = ps; ps = pt;
2084 } else {
2085 lofsprite[1 << par->crsr.fmode] = spr2hw_ctl(vs, hs, ve + 1);
2086 shfsprite[1 << par->crsr.fmode] = spr2hw_ctl(vs + 1, hs, ve);
2087 }
2088 } else {
2089 lofsprite[0] = spr2hw_pos(vs, hs) | (IS_AGA && (par->fmode & FMODE_BSCAN2) ? 0x80 : 0);
2090 lofsprite[1 << par->crsr.fmode] = spr2hw_ctl(vs, hs, ve);
2091 }
2092 }
2093 copl[cop_spr0ptrh].w[1] = highw(pl);
2094 copl[cop_spr0ptrl].w[1] = loww(pl);
2095 if (par->bplcon0 & BPC0_LACE) {
2096 cops[cop_spr0ptrh].w[1] = highw(ps);
2097 cops[cop_spr0ptrl].w[1] = loww(ps);
2098 }
2099}
2100
2101
2102 /*
2103 * Initialise the Copper Initialisation List
2104 */
2105
2106static void __init ami_init_copper(void)
2107{
2108 copins *cop = copdisplay.init;
2109 u_long p;
2110 int i;
2111
2112 if (!IS_OCS) {
2113 (cop++)->l = CMOVE(BPC0_COLOR | BPC0_SHRES | BPC0_ECSENA, bplcon0);
2114 (cop++)->l = CMOVE(0x0181, diwstrt);
2115 (cop++)->l = CMOVE(0x0281, diwstop);
2116 (cop++)->l = CMOVE(0x0000, diwhigh);
2117 } else
2118 (cop++)->l = CMOVE(BPC0_COLOR, bplcon0);
2119 p = ZTWO_PADDR(dummysprite);
2120 for (i = 0; i < 8; i++) {
2121 (cop++)->l = CMOVE(0, spr[i].pos);
2122 (cop++)->l = CMOVE(highw(p), sprpt[i]);
2123 (cop++)->l = CMOVE2(loww(p), sprpt[i]);
2124 }
2125
2126 (cop++)->l = CMOVE(IF_SETCLR | IF_COPER, intreq);
2127 copdisplay.wait = cop;
2128 (cop++)->l = CEND;
2129 (cop++)->l = CMOVE(0, copjmp2);
2130 cop->l = CEND;
2131
2132 custom.cop1lc = (u_short *)ZTWO_PADDR(copdisplay.init);
2133 custom.copjmp1 = 0;
2134}
2135
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01002136static void ami_reinit_copper(const struct amifb_par *par)
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01002137{
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01002138 copdisplay.init[cip_bplcon0].w[1] = ~(BPC0_BPU3 | BPC0_BPU2 | BPC0_BPU1 | BPC0_BPU0) & par->bplcon0;
2139 copdisplay.wait->l = CWAIT(32, par->diwstrt_v - 4);
2140}
2141
2142
2143 /*
2144 * Rebuild the Copper List
2145 *
2146 * We only change the things that are not static
2147 */
2148
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01002149static void ami_rebuild_copper(const struct amifb_par *par)
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01002150{
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01002151 copins *copl, *cops;
2152 u_short line, h_end1, h_end2;
2153 short i;
2154 u_long p;
2155
2156 if (IS_AGA && maxfmode + par->clk_shift == 0)
2157 h_end1 = par->diwstrt_h - 64;
2158 else
2159 h_end1 = par->htotal - 32;
2160 h_end2 = par->ddfstop + 64;
2161
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01002162 ami_set_sprite(par);
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01002163
2164 copl = copdisplay.rebuild[1];
2165 p = par->bplpt0;
2166 if (par->vmode & FB_VMODE_YWRAP) {
2167 if ((par->vyres - par->yoffset) != 1 || !mod2(par->diwstrt_v)) {
2168 if (par->yoffset > par->vyres - par->yres) {
2169 for (i = 0; i < (short)par->bpp; i++, p += par->next_plane) {
2170 (copl++)->l = CMOVE(highw(p), bplpt[i]);
2171 (copl++)->l = CMOVE2(loww(p), bplpt[i]);
2172 }
2173 line = par->diwstrt_v + ((par->vyres - par->yoffset) << par->line_shift) - 1;
2174 while (line >= 512) {
2175 (copl++)->l = CWAIT(h_end1, 510);
2176 line -= 512;
2177 }
2178 if (line >= 510 && IS_AGA && maxfmode + par->clk_shift == 0)
2179 (copl++)->l = CWAIT(h_end1, line);
2180 else
2181 (copl++)->l = CWAIT(h_end2, line);
2182 p = par->bplpt0wrap;
2183 }
2184 } else
2185 p = par->bplpt0wrap;
2186 }
2187 for (i = 0; i < (short)par->bpp; i++, p += par->next_plane) {
2188 (copl++)->l = CMOVE(highw(p), bplpt[i]);
2189 (copl++)->l = CMOVE2(loww(p), bplpt[i]);
2190 }
2191 copl->l = CEND;
2192
2193 if (par->bplcon0 & BPC0_LACE) {
2194 cops = copdisplay.rebuild[0];
2195 p = par->bplpt0;
2196 if (mod2(par->diwstrt_v))
2197 p -= par->next_line;
2198 else
2199 p += par->next_line;
2200 if (par->vmode & FB_VMODE_YWRAP) {
2201 if ((par->vyres - par->yoffset) != 1 || mod2(par->diwstrt_v)) {
2202 if (par->yoffset > par->vyres - par->yres + 1) {
2203 for (i = 0; i < (short)par->bpp; i++, p += par->next_plane) {
2204 (cops++)->l = CMOVE(highw(p), bplpt[i]);
2205 (cops++)->l = CMOVE2(loww(p), bplpt[i]);
2206 }
2207 line = par->diwstrt_v + ((par->vyres - par->yoffset) << par->line_shift) - 2;
2208 while (line >= 512) {
2209 (cops++)->l = CWAIT(h_end1, 510);
2210 line -= 512;
2211 }
2212 if (line > 510 && IS_AGA && maxfmode + par->clk_shift == 0)
2213 (cops++)->l = CWAIT(h_end1, line);
2214 else
2215 (cops++)->l = CWAIT(h_end2, line);
2216 p = par->bplpt0wrap;
2217 if (mod2(par->diwstrt_v + par->vyres -
2218 par->yoffset))
2219 p -= par->next_line;
2220 else
2221 p += par->next_line;
2222 }
2223 } else
2224 p = par->bplpt0wrap - par->next_line;
2225 }
2226 for (i = 0; i < (short)par->bpp; i++, p += par->next_plane) {
2227 (cops++)->l = CMOVE(highw(p), bplpt[i]);
2228 (cops++)->l = CMOVE2(loww(p), bplpt[i]);
2229 }
2230 cops->l = CEND;
2231 }
2232}
2233
2234
2235 /*
2236 * Build the Copper List
2237 */
2238
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01002239static void ami_build_copper(struct fb_info *info)
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01002240{
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01002241 struct amifb_par *par = info->par;
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01002242 copins *copl, *cops;
2243 u_long p;
2244
2245 currentcop = 1 - currentcop;
2246
2247 copl = copdisplay.list[currentcop][1];
2248
2249 (copl++)->l = CWAIT(0, 10);
2250 (copl++)->l = CMOVE(par->bplcon0, bplcon0);
2251 (copl++)->l = CMOVE(0, sprpt[0]);
2252 (copl++)->l = CMOVE2(0, sprpt[0]);
2253
2254 if (par->bplcon0 & BPC0_LACE) {
2255 cops = copdisplay.list[currentcop][0];
2256
2257 (cops++)->l = CWAIT(0, 10);
2258 (cops++)->l = CMOVE(par->bplcon0, bplcon0);
2259 (cops++)->l = CMOVE(0, sprpt[0]);
2260 (cops++)->l = CMOVE2(0, sprpt[0]);
2261
2262 (copl++)->l = CMOVE(diwstrt2hw(par->diwstrt_h, par->diwstrt_v + 1), diwstrt);
2263 (copl++)->l = CMOVE(diwstop2hw(par->diwstop_h, par->diwstop_v + 1), diwstop);
2264 (cops++)->l = CMOVE(diwstrt2hw(par->diwstrt_h, par->diwstrt_v), diwstrt);
2265 (cops++)->l = CMOVE(diwstop2hw(par->diwstop_h, par->diwstop_v), diwstop);
2266 if (!IS_OCS) {
2267 (copl++)->l = CMOVE(diwhigh2hw(par->diwstrt_h, par->diwstrt_v + 1,
2268 par->diwstop_h, par->diwstop_v + 1), diwhigh);
2269 (cops++)->l = CMOVE(diwhigh2hw(par->diwstrt_h, par->diwstrt_v,
2270 par->diwstop_h, par->diwstop_v), diwhigh);
2271#if 0
2272 if (par->beamcon0 & BMC0_VARBEAMEN) {
2273 (copl++)->l = CMOVE(vtotal2hw(par->vtotal), vtotal);
2274 (copl++)->l = CMOVE(vbstrt2hw(par->vbstrt + 1), vbstrt);
2275 (copl++)->l = CMOVE(vbstop2hw(par->vbstop + 1), vbstop);
2276 (cops++)->l = CMOVE(vtotal2hw(par->vtotal), vtotal);
2277 (cops++)->l = CMOVE(vbstrt2hw(par->vbstrt), vbstrt);
2278 (cops++)->l = CMOVE(vbstop2hw(par->vbstop), vbstop);
2279 }
2280#endif
2281 }
2282 p = ZTWO_PADDR(copdisplay.list[currentcop][0]);
2283 (copl++)->l = CMOVE(highw(p), cop2lc);
2284 (copl++)->l = CMOVE2(loww(p), cop2lc);
2285 p = ZTWO_PADDR(copdisplay.list[currentcop][1]);
2286 (cops++)->l = CMOVE(highw(p), cop2lc);
2287 (cops++)->l = CMOVE2(loww(p), cop2lc);
2288 copdisplay.rebuild[0] = cops;
2289 } else {
2290 (copl++)->l = CMOVE(diwstrt2hw(par->diwstrt_h, par->diwstrt_v), diwstrt);
2291 (copl++)->l = CMOVE(diwstop2hw(par->diwstop_h, par->diwstop_v), diwstop);
2292 if (!IS_OCS) {
2293 (copl++)->l = CMOVE(diwhigh2hw(par->diwstrt_h, par->diwstrt_v,
2294 par->diwstop_h, par->diwstop_v), diwhigh);
2295#if 0
2296 if (par->beamcon0 & BMC0_VARBEAMEN) {
2297 (copl++)->l = CMOVE(vtotal2hw(par->vtotal), vtotal);
2298 (copl++)->l = CMOVE(vbstrt2hw(par->vbstrt), vbstrt);
2299 (copl++)->l = CMOVE(vbstop2hw(par->vbstop), vbstop);
2300 }
2301#endif
2302 }
2303 }
2304 copdisplay.rebuild[1] = copl;
2305
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01002306 ami_update_par(info);
2307 ami_rebuild_copper(info->par);
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01002308}
2309
Linus Torvalds1da177e2005-04-16 15:20:36 -07002310
2311static void __init amifb_setup_mcap(char *spec)
2312{
2313 char *p;
2314 int vmin, vmax, hmin, hmax;
2315
2316 /* Format for monitor capabilities is: <Vmin>;<Vmax>;<Hmin>;<Hmax>
2317 * <V*> vertical freq. in Hz
2318 * <H*> horizontal freq. in kHz
2319 */
2320
2321 if (!(p = strsep(&spec, ";")) || !*p)
2322 return;
2323 vmin = simple_strtoul(p, NULL, 10);
2324 if (vmin <= 0)
2325 return;
2326 if (!(p = strsep(&spec, ";")) || !*p)
2327 return;
2328 vmax = simple_strtoul(p, NULL, 10);
2329 if (vmax <= 0 || vmax <= vmin)
2330 return;
2331 if (!(p = strsep(&spec, ";")) || !*p)
2332 return;
2333 hmin = 1000 * simple_strtoul(p, NULL, 10);
2334 if (hmin <= 0)
2335 return;
2336 if (!(p = strsep(&spec, "")) || !*p)
2337 return;
2338 hmax = 1000 * simple_strtoul(p, NULL, 10);
2339 if (hmax <= 0 || hmax <= hmin)
2340 return;
2341
Geert Uytterhoeven03c740a2011-11-21 21:53:57 +01002342 amifb_hfmin = hmin;
2343 amifb_hfmax = hmax;
2344 amifb_vfmin = vmin;
2345 amifb_vfmax = vmax;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002346}
2347
Geert Uytterhoeven78ffd702011-11-21 21:53:55 +01002348static int __init amifb_setup(char *options)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002349{
2350 char *this_opt;
2351
2352 if (!options || !*options)
2353 return 0;
2354
2355 while ((this_opt = strsep(&options, ",")) != NULL) {
2356 if (!*this_opt)
2357 continue;
2358 if (!strcmp(this_opt, "inverse")) {
2359 amifb_inverse = 1;
2360 fb_invert_cmaps();
2361 } else if (!strcmp(this_opt, "ilbm"))
2362 amifb_ilbm = 1;
2363 else if (!strncmp(this_opt, "monitorcap:", 11))
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002364 amifb_setup_mcap(this_opt + 11);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002365 else if (!strncmp(this_opt, "fstart:", 7))
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002366 min_fstrt = simple_strtoul(this_opt + 7, NULL, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002367 else
2368 mode_option = this_opt;
2369 }
2370
2371 if (min_fstrt < 48)
2372 min_fstrt = 48;
2373
2374 return 0;
2375}
2376
2377
2378static int amifb_check_var(struct fb_var_screeninfo *var,
2379 struct fb_info *info)
2380{
2381 int err;
2382 struct amifb_par par;
2383
2384 /* Validate wanted screen parameters */
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01002385 err = ami_decode_var(var, &par, info);
2386 if (err)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002387 return err;
2388
2389 /* Encode (possibly rounded) screen parameters */
2390 ami_encode_var(var, &par);
2391 return 0;
2392}
2393
2394
2395static int amifb_set_par(struct fb_info *info)
2396{
Geert Uytterhoeven423a5302011-11-21 21:53:56 +01002397 struct amifb_par *par = info->par;
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01002398 int error;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002399
2400 do_vmode_pan = 0;
2401 do_vmode_full = 0;
2402
2403 /* Decode wanted screen parameters */
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01002404 error = ami_decode_var(&info->var, par, info);
2405 if (error)
2406 return error;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002407
2408 /* Set new videomode */
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01002409 ami_build_copper(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002410
2411 /* Set VBlank trigger */
2412 do_vmode_full = 1;
2413
2414 /* Update fix for new screen parameters */
2415 if (par->bpp == 1) {
2416 info->fix.type = FB_TYPE_PACKED_PIXELS;
2417 info->fix.type_aux = 0;
2418 } else if (amifb_ilbm) {
2419 info->fix.type = FB_TYPE_INTERLEAVED_PLANES;
2420 info->fix.type_aux = par->next_line;
2421 } else {
2422 info->fix.type = FB_TYPE_PLANES;
2423 info->fix.type_aux = 0;
2424 }
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002425 info->fix.line_length = div8(upx(16 << maxfmode, par->vxres));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002426
2427 if (par->vmode & FB_VMODE_YWRAP) {
2428 info->fix.ywrapstep = 1;
2429 info->fix.xpanstep = 0;
2430 info->fix.ypanstep = 0;
2431 info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YWRAP |
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002432 FBINFO_READS_FAST; /* override SCROLL_REDRAW */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002433 } else {
2434 info->fix.ywrapstep = 0;
2435 if (par->vmode & FB_VMODE_SMOOTH_XPAN)
2436 info->fix.xpanstep = 1;
2437 else
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002438 info->fix.xpanstep = 16 << maxfmode;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002439 info->fix.ypanstep = 1;
2440 info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN;
2441 }
2442 return 0;
2443}
2444
2445
2446 /*
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01002447 * Set a single color register. The values supplied are already
2448 * rounded down to the hardware's capabilities (according to the
2449 * entries in the var structure). Return != 0 for invalid regno.
2450 */
2451
2452static int amifb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
2453 u_int transp, struct fb_info *info)
2454{
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01002455 const struct amifb_par *par = info->par;
2456
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01002457 if (IS_AGA) {
2458 if (regno > 255)
2459 return 1;
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01002460 } else if (par->bplcon0 & BPC0_SHRES) {
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01002461 if (regno > 3)
2462 return 1;
2463 } else {
2464 if (regno > 31)
2465 return 1;
2466 }
2467 red >>= 8;
2468 green >>= 8;
2469 blue >>= 8;
2470 if (!regno) {
2471 red0 = red;
2472 green0 = green;
2473 blue0 = blue;
2474 }
2475
2476 /*
2477 * Update the corresponding Hardware Color Register, unless it's Color
2478 * Register 0 and the screen is blanked.
2479 *
2480 * VBlank is switched off to protect bplcon3 or ecs_palette[] from
2481 * being changed by ami_do_blank() during the VBlank.
2482 */
2483
2484 if (regno || !is_blanked) {
2485#if defined(CONFIG_FB_AMIGA_AGA)
2486 if (IS_AGA) {
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01002487 u_short bplcon3 = par->bplcon3;
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01002488 VBlankOff();
2489 custom.bplcon3 = bplcon3 | (regno << 8 & 0xe000);
2490 custom.color[regno & 31] = rgb2hw8_high(red, green,
2491 blue);
2492 custom.bplcon3 = bplcon3 | (regno << 8 & 0xe000) |
2493 BPC3_LOCT;
2494 custom.color[regno & 31] = rgb2hw8_low(red, green,
2495 blue);
2496 custom.bplcon3 = bplcon3;
2497 VBlankOn();
2498 } else
2499#endif
2500#if defined(CONFIG_FB_AMIGA_ECS)
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01002501 if (par->bplcon0 & BPC0_SHRES) {
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01002502 u_short color, mask;
2503 int i;
2504
2505 mask = 0x3333;
2506 color = rgb2hw2(red, green, blue);
2507 VBlankOff();
2508 for (i = regno + 12; i >= (int)regno; i -= 4)
2509 custom.color[i] = ecs_palette[i] = (ecs_palette[i] & mask) | color;
2510 mask <<= 2; color >>= 2;
2511 regno = down16(regno) + mul4(mod4(regno));
2512 for (i = regno + 3; i >= (int)regno; i--)
2513 custom.color[i] = ecs_palette[i] = (ecs_palette[i] & mask) | color;
2514 VBlankOn();
2515 } else
2516#endif
2517 custom.color[regno] = rgb2hw4(red, green, blue);
2518 }
2519 return 0;
2520}
2521
2522
2523 /*
2524 * Blank the display.
2525 */
2526
2527static int amifb_blank(int blank, struct fb_info *info)
2528{
2529 do_blank = blank ? blank : -1;
2530
2531 return 0;
2532}
2533
2534
2535 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -07002536 * Pan or Wrap the Display
2537 *
2538 * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
2539 */
2540
2541static int amifb_pan_display(struct fb_var_screeninfo *var,
2542 struct fb_info *info)
2543{
2544 if (var->vmode & FB_VMODE_YWRAP) {
2545 if (var->yoffset < 0 ||
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002546 var->yoffset >= info->var.yres_virtual || var->xoffset)
2547 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002548 } else {
2549 /*
2550 * TODO: There will be problems when xpan!=1, so some columns
2551 * on the right side will never be seen
2552 */
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002553 if (var->xoffset + info->var.xres >
2554 upx(16 << maxfmode, info->var.xres_virtual) ||
2555 var->yoffset + info->var.yres > info->var.yres_virtual)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002556 return -EINVAL;
2557 }
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01002558 ami_pan_var(var, info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002559 info->var.xoffset = var->xoffset;
2560 info->var.yoffset = var->yoffset;
2561 if (var->vmode & FB_VMODE_YWRAP)
2562 info->var.vmode |= FB_VMODE_YWRAP;
2563 else
2564 info->var.vmode &= ~FB_VMODE_YWRAP;
2565 return 0;
2566}
2567
2568
2569#if BITS_PER_LONG == 32
2570#define BYTES_PER_LONG 4
2571#define SHIFT_PER_LONG 5
2572#elif BITS_PER_LONG == 64
2573#define BYTES_PER_LONG 8
2574#define SHIFT_PER_LONG 6
2575#else
2576#define Please update me
2577#endif
2578
2579
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002580 /*
2581 * Compose two values, using a bitmask as decision value
2582 * This is equivalent to (a & mask) | (b & ~mask)
2583 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002584
2585static inline unsigned long comp(unsigned long a, unsigned long b,
2586 unsigned long mask)
2587{
2588 return ((a ^ b) & mask) ^ b;
2589}
2590
2591
2592static inline unsigned long xor(unsigned long a, unsigned long b,
2593 unsigned long mask)
2594{
2595 return (a & mask) ^ b;
2596}
2597
2598
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002599 /*
2600 * Unaligned forward bit copy using 32-bit or 64-bit memory accesses
2601 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002602
2603static void bitcpy(unsigned long *dst, int dst_idx, const unsigned long *src,
2604 int src_idx, u32 n)
2605{
2606 unsigned long first, last;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002607 int shift = dst_idx - src_idx, left, right;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002608 unsigned long d0, d1;
2609 int m;
2610
2611 if (!n)
2612 return;
2613
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002614 shift = dst_idx - src_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002615 first = ~0UL >> dst_idx;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002616 last = ~(~0UL >> ((dst_idx + n) % BITS_PER_LONG));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002617
2618 if (!shift) {
2619 // Same alignment for source and dest
2620
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002621 if (dst_idx + n <= BITS_PER_LONG) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002622 // Single word
2623 if (last)
2624 first &= last;
2625 *dst = comp(*src, *dst, first);
2626 } else {
2627 // Multiple destination words
2628 // Leading bits
2629 if (first) {
2630 *dst = comp(*src, *dst, first);
2631 dst++;
2632 src++;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002633 n -= BITS_PER_LONG - dst_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002634 }
2635
2636 // Main chunk
2637 n /= BITS_PER_LONG;
2638 while (n >= 8) {
2639 *dst++ = *src++;
2640 *dst++ = *src++;
2641 *dst++ = *src++;
2642 *dst++ = *src++;
2643 *dst++ = *src++;
2644 *dst++ = *src++;
2645 *dst++ = *src++;
2646 *dst++ = *src++;
2647 n -= 8;
2648 }
2649 while (n--)
2650 *dst++ = *src++;
2651
2652 // Trailing bits
2653 if (last)
2654 *dst = comp(*src, *dst, last);
2655 }
2656 } else {
2657 // Different alignment for source and dest
2658
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002659 right = shift & (BITS_PER_LONG - 1);
2660 left = -shift & (BITS_PER_LONG - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002661
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002662 if (dst_idx + n <= BITS_PER_LONG) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002663 // Single destination word
2664 if (last)
2665 first &= last;
2666 if (shift > 0) {
2667 // Single source word
2668 *dst = comp(*src >> right, *dst, first);
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002669 } else if (src_idx + n <= BITS_PER_LONG) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002670 // Single source word
2671 *dst = comp(*src << left, *dst, first);
2672 } else {
2673 // 2 source words
2674 d0 = *src++;
2675 d1 = *src;
2676 *dst = comp(d0 << left | d1 >> right, *dst,
2677 first);
2678 }
2679 } else {
2680 // Multiple destination words
2681 d0 = *src++;
2682 // Leading bits
2683 if (shift > 0) {
2684 // Single source word
2685 *dst = comp(d0 >> right, *dst, first);
2686 dst++;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002687 n -= BITS_PER_LONG - dst_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002688 } else {
2689 // 2 source words
2690 d1 = *src++;
2691 *dst = comp(d0 << left | d1 >> right, *dst,
2692 first);
2693 d0 = d1;
2694 dst++;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002695 n -= BITS_PER_LONG - dst_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002696 }
2697
2698 // Main chunk
2699 m = n % BITS_PER_LONG;
2700 n /= BITS_PER_LONG;
2701 while (n >= 4) {
2702 d1 = *src++;
2703 *dst++ = d0 << left | d1 >> right;
2704 d0 = d1;
2705 d1 = *src++;
2706 *dst++ = d0 << left | d1 >> right;
2707 d0 = d1;
2708 d1 = *src++;
2709 *dst++ = d0 << left | d1 >> right;
2710 d0 = d1;
2711 d1 = *src++;
2712 *dst++ = d0 << left | d1 >> right;
2713 d0 = d1;
2714 n -= 4;
2715 }
2716 while (n--) {
2717 d1 = *src++;
2718 *dst++ = d0 << left | d1 >> right;
2719 d0 = d1;
2720 }
2721
2722 // Trailing bits
2723 if (last) {
2724 if (m <= right) {
2725 // Single source word
2726 *dst = comp(d0 << left, *dst, last);
2727 } else {
2728 // 2 source words
2729 d1 = *src;
2730 *dst = comp(d0 << left | d1 >> right,
2731 *dst, last);
2732 }
2733 }
2734 }
2735 }
2736}
2737
2738
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002739 /*
2740 * Unaligned reverse bit copy using 32-bit or 64-bit memory accesses
2741 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002742
2743static void bitcpy_rev(unsigned long *dst, int dst_idx,
2744 const unsigned long *src, int src_idx, u32 n)
2745{
2746 unsigned long first, last;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002747 int shift = dst_idx - src_idx, left, right;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002748 unsigned long d0, d1;
2749 int m;
2750
2751 if (!n)
2752 return;
2753
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002754 dst += (n - 1) / BITS_PER_LONG;
2755 src += (n - 1) / BITS_PER_LONG;
2756 if ((n - 1) % BITS_PER_LONG) {
2757 dst_idx += (n - 1) % BITS_PER_LONG;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002758 dst += dst_idx >> SHIFT_PER_LONG;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002759 dst_idx &= BITS_PER_LONG - 1;
2760 src_idx += (n - 1) % BITS_PER_LONG;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002761 src += src_idx >> SHIFT_PER_LONG;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002762 src_idx &= BITS_PER_LONG - 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002763 }
2764
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002765 shift = dst_idx - src_idx;
2766 first = ~0UL << (BITS_PER_LONG - 1 - dst_idx);
2767 last = ~(~0UL << (BITS_PER_LONG - 1 - ((dst_idx - n) % BITS_PER_LONG)));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002768
2769 if (!shift) {
2770 // Same alignment for source and dest
2771
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002772 if ((unsigned long)dst_idx + 1 >= n) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002773 // Single word
2774 if (last)
2775 first &= last;
2776 *dst = comp(*src, *dst, first);
2777 } else {
2778 // Multiple destination words
2779 // Leading bits
2780 if (first) {
2781 *dst = comp(*src, *dst, first);
2782 dst--;
2783 src--;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002784 n -= dst_idx + 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002785 }
2786
2787 // Main chunk
2788 n /= BITS_PER_LONG;
2789 while (n >= 8) {
2790 *dst-- = *src--;
2791 *dst-- = *src--;
2792 *dst-- = *src--;
2793 *dst-- = *src--;
2794 *dst-- = *src--;
2795 *dst-- = *src--;
2796 *dst-- = *src--;
2797 *dst-- = *src--;
2798 n -= 8;
2799 }
2800 while (n--)
2801 *dst-- = *src--;
2802
2803 // Trailing bits
2804 if (last)
2805 *dst = comp(*src, *dst, last);
2806 }
2807 } else {
2808 // Different alignment for source and dest
2809
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002810 right = shift & (BITS_PER_LONG - 1);
2811 left = -shift & (BITS_PER_LONG - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002812
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002813 if ((unsigned long)dst_idx + 1 >= n) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002814 // Single destination word
2815 if (last)
2816 first &= last;
2817 if (shift < 0) {
2818 // Single source word
2819 *dst = comp(*src << left, *dst, first);
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002820 } else if (1 + (unsigned long)src_idx >= n) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002821 // Single source word
2822 *dst = comp(*src >> right, *dst, first);
2823 } else {
2824 // 2 source words
2825 d0 = *src--;
2826 d1 = *src;
2827 *dst = comp(d0 >> right | d1 << left, *dst,
2828 first);
2829 }
2830 } else {
2831 // Multiple destination words
2832 d0 = *src--;
2833 // Leading bits
2834 if (shift < 0) {
2835 // Single source word
2836 *dst = comp(d0 << left, *dst, first);
2837 dst--;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002838 n -= dst_idx + 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002839 } else {
2840 // 2 source words
2841 d1 = *src--;
2842 *dst = comp(d0 >> right | d1 << left, *dst,
2843 first);
2844 d0 = d1;
2845 dst--;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002846 n -= dst_idx + 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002847 }
2848
2849 // Main chunk
2850 m = n % BITS_PER_LONG;
2851 n /= BITS_PER_LONG;
2852 while (n >= 4) {
2853 d1 = *src--;
2854 *dst-- = d0 >> right | d1 << left;
2855 d0 = d1;
2856 d1 = *src--;
2857 *dst-- = d0 >> right | d1 << left;
2858 d0 = d1;
2859 d1 = *src--;
2860 *dst-- = d0 >> right | d1 << left;
2861 d0 = d1;
2862 d1 = *src--;
2863 *dst-- = d0 >> right | d1 << left;
2864 d0 = d1;
2865 n -= 4;
2866 }
2867 while (n--) {
2868 d1 = *src--;
2869 *dst-- = d0 >> right | d1 << left;
2870 d0 = d1;
2871 }
2872
2873 // Trailing bits
2874 if (last) {
2875 if (m <= left) {
2876 // Single source word
2877 *dst = comp(d0 >> right, *dst, last);
2878 } else {
2879 // 2 source words
2880 d1 = *src;
2881 *dst = comp(d0 >> right | d1 << left,
2882 *dst, last);
2883 }
2884 }
2885 }
2886 }
2887}
2888
2889
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002890 /*
2891 * Unaligned forward inverting bit copy using 32-bit or 64-bit memory
2892 * accesses
2893 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002894
2895static void bitcpy_not(unsigned long *dst, int dst_idx,
2896 const unsigned long *src, int src_idx, u32 n)
2897{
2898 unsigned long first, last;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002899 int shift = dst_idx - src_idx, left, right;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002900 unsigned long d0, d1;
2901 int m;
2902
2903 if (!n)
2904 return;
2905
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002906 shift = dst_idx - src_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002907 first = ~0UL >> dst_idx;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002908 last = ~(~0UL >> ((dst_idx + n) % BITS_PER_LONG));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002909
2910 if (!shift) {
2911 // Same alignment for source and dest
2912
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002913 if (dst_idx + n <= BITS_PER_LONG) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002914 // Single word
2915 if (last)
2916 first &= last;
2917 *dst = comp(~*src, *dst, first);
2918 } else {
2919 // Multiple destination words
2920 // Leading bits
2921 if (first) {
2922 *dst = comp(~*src, *dst, first);
2923 dst++;
2924 src++;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002925 n -= BITS_PER_LONG - dst_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002926 }
2927
2928 // Main chunk
2929 n /= BITS_PER_LONG;
2930 while (n >= 8) {
2931 *dst++ = ~*src++;
2932 *dst++ = ~*src++;
2933 *dst++ = ~*src++;
2934 *dst++ = ~*src++;
2935 *dst++ = ~*src++;
2936 *dst++ = ~*src++;
2937 *dst++ = ~*src++;
2938 *dst++ = ~*src++;
2939 n -= 8;
2940 }
2941 while (n--)
2942 *dst++ = ~*src++;
2943
2944 // Trailing bits
2945 if (last)
2946 *dst = comp(~*src, *dst, last);
2947 }
2948 } else {
2949 // Different alignment for source and dest
2950
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002951 right = shift & (BITS_PER_LONG - 1);
2952 left = -shift & (BITS_PER_LONG - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002953
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002954 if (dst_idx + n <= BITS_PER_LONG) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002955 // Single destination word
2956 if (last)
2957 first &= last;
2958 if (shift > 0) {
2959 // Single source word
2960 *dst = comp(~*src >> right, *dst, first);
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002961 } else if (src_idx + n <= BITS_PER_LONG) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002962 // Single source word
2963 *dst = comp(~*src << left, *dst, first);
2964 } else {
2965 // 2 source words
2966 d0 = ~*src++;
2967 d1 = ~*src;
2968 *dst = comp(d0 << left | d1 >> right, *dst,
2969 first);
2970 }
2971 } else {
2972 // Multiple destination words
2973 d0 = ~*src++;
2974 // Leading bits
2975 if (shift > 0) {
2976 // Single source word
2977 *dst = comp(d0 >> right, *dst, first);
2978 dst++;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002979 n -= BITS_PER_LONG - dst_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002980 } else {
2981 // 2 source words
2982 d1 = ~*src++;
2983 *dst = comp(d0 << left | d1 >> right, *dst,
2984 first);
2985 d0 = d1;
2986 dst++;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002987 n -= BITS_PER_LONG - dst_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002988 }
2989
2990 // Main chunk
2991 m = n % BITS_PER_LONG;
2992 n /= BITS_PER_LONG;
2993 while (n >= 4) {
2994 d1 = ~*src++;
2995 *dst++ = d0 << left | d1 >> right;
2996 d0 = d1;
2997 d1 = ~*src++;
2998 *dst++ = d0 << left | d1 >> right;
2999 d0 = d1;
3000 d1 = ~*src++;
3001 *dst++ = d0 << left | d1 >> right;
3002 d0 = d1;
3003 d1 = ~*src++;
3004 *dst++ = d0 << left | d1 >> right;
3005 d0 = d1;
3006 n -= 4;
3007 }
3008 while (n--) {
3009 d1 = ~*src++;
3010 *dst++ = d0 << left | d1 >> right;
3011 d0 = d1;
3012 }
3013
3014 // Trailing bits
3015 if (last) {
3016 if (m <= right) {
3017 // Single source word
3018 *dst = comp(d0 << left, *dst, last);
3019 } else {
3020 // 2 source words
3021 d1 = ~*src;
3022 *dst = comp(d0 << left | d1 >> right,
3023 *dst, last);
3024 }
3025 }
3026 }
3027 }
3028}
3029
3030
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003031 /*
3032 * Unaligned 32-bit pattern fill using 32/64-bit memory accesses
3033 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003034
3035static void bitfill32(unsigned long *dst, int dst_idx, u32 pat, u32 n)
3036{
3037 unsigned long val = pat;
3038 unsigned long first, last;
3039
3040 if (!n)
3041 return;
3042
3043#if BITS_PER_LONG == 64
3044 val |= val << 32;
3045#endif
3046
3047 first = ~0UL >> dst_idx;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003048 last = ~(~0UL >> ((dst_idx + n) % BITS_PER_LONG));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003049
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003050 if (dst_idx + n <= BITS_PER_LONG) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003051 // Single word
3052 if (last)
3053 first &= last;
3054 *dst = comp(val, *dst, first);
3055 } else {
3056 // Multiple destination words
3057 // Leading bits
3058 if (first) {
3059 *dst = comp(val, *dst, first);
3060 dst++;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003061 n -= BITS_PER_LONG - dst_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003062 }
3063
3064 // Main chunk
3065 n /= BITS_PER_LONG;
3066 while (n >= 8) {
3067 *dst++ = val;
3068 *dst++ = val;
3069 *dst++ = val;
3070 *dst++ = val;
3071 *dst++ = val;
3072 *dst++ = val;
3073 *dst++ = val;
3074 *dst++ = val;
3075 n -= 8;
3076 }
3077 while (n--)
3078 *dst++ = val;
3079
3080 // Trailing bits
3081 if (last)
3082 *dst = comp(val, *dst, last);
3083 }
3084}
3085
3086
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003087 /*
3088 * Unaligned 32-bit pattern xor using 32/64-bit memory accesses
3089 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003090
3091static void bitxor32(unsigned long *dst, int dst_idx, u32 pat, u32 n)
3092{
3093 unsigned long val = pat;
3094 unsigned long first, last;
3095
3096 if (!n)
3097 return;
3098
3099#if BITS_PER_LONG == 64
3100 val |= val << 32;
3101#endif
3102
3103 first = ~0UL >> dst_idx;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003104 last = ~(~0UL >> ((dst_idx + n) % BITS_PER_LONG));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003105
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003106 if (dst_idx + n <= BITS_PER_LONG) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003107 // Single word
3108 if (last)
3109 first &= last;
3110 *dst = xor(val, *dst, first);
3111 } else {
3112 // Multiple destination words
3113 // Leading bits
3114 if (first) {
3115 *dst = xor(val, *dst, first);
3116 dst++;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003117 n -= BITS_PER_LONG - dst_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003118 }
3119
3120 // Main chunk
3121 n /= BITS_PER_LONG;
3122 while (n >= 4) {
3123 *dst++ ^= val;
3124 *dst++ ^= val;
3125 *dst++ ^= val;
3126 *dst++ ^= val;
3127 n -= 4;
3128 }
3129 while (n--)
3130 *dst++ ^= val;
3131
3132 // Trailing bits
3133 if (last)
3134 *dst = xor(val, *dst, last);
3135 }
3136}
3137
3138static inline void fill_one_line(int bpp, unsigned long next_plane,
3139 unsigned long *dst, int dst_idx, u32 n,
3140 u32 color)
3141{
3142 while (1) {
3143 dst += dst_idx >> SHIFT_PER_LONG;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003144 dst_idx &= (BITS_PER_LONG - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003145 bitfill32(dst, dst_idx, color & 1 ? ~0 : 0, n);
3146 if (!--bpp)
3147 break;
3148 color >>= 1;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003149 dst_idx += next_plane * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003150 }
3151}
3152
3153static inline void xor_one_line(int bpp, unsigned long next_plane,
3154 unsigned long *dst, int dst_idx, u32 n,
3155 u32 color)
3156{
3157 while (color) {
3158 dst += dst_idx >> SHIFT_PER_LONG;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003159 dst_idx &= (BITS_PER_LONG - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003160 bitxor32(dst, dst_idx, color & 1 ? ~0 : 0, n);
3161 if (!--bpp)
3162 break;
3163 color >>= 1;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003164 dst_idx += next_plane * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003165 }
3166}
3167
3168
3169static void amifb_fillrect(struct fb_info *info,
3170 const struct fb_fillrect *rect)
3171{
Geert Uytterhoeven423a5302011-11-21 21:53:56 +01003172 struct amifb_par *par = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003173 int dst_idx, x2, y2;
3174 unsigned long *dst;
3175 u32 width, height;
3176
3177 if (!rect->width || !rect->height)
3178 return;
3179
3180 /*
3181 * We could use hardware clipping but on many cards you get around
3182 * hardware clipping by writing to framebuffer directly.
3183 * */
3184 x2 = rect->dx + rect->width;
3185 y2 = rect->dy + rect->height;
3186 x2 = x2 < info->var.xres_virtual ? x2 : info->var.xres_virtual;
3187 y2 = y2 < info->var.yres_virtual ? y2 : info->var.yres_virtual;
3188 width = x2 - rect->dx;
3189 height = y2 - rect->dy;
3190
3191 dst = (unsigned long *)
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003192 ((unsigned long)info->screen_base & ~(BYTES_PER_LONG - 1));
3193 dst_idx = ((unsigned long)info->screen_base & (BYTES_PER_LONG - 1)) * 8;
3194 dst_idx += rect->dy * par->next_line * 8 + rect->dx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003195 while (height--) {
3196 switch (rect->rop) {
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003197 case ROP_COPY:
Linus Torvalds1da177e2005-04-16 15:20:36 -07003198 fill_one_line(info->var.bits_per_pixel,
3199 par->next_plane, dst, dst_idx, width,
3200 rect->color);
3201 break;
3202
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003203 case ROP_XOR:
Linus Torvalds1da177e2005-04-16 15:20:36 -07003204 xor_one_line(info->var.bits_per_pixel, par->next_plane,
3205 dst, dst_idx, width, rect->color);
3206 break;
3207 }
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003208 dst_idx += par->next_line * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003209 }
3210}
3211
3212static inline void copy_one_line(int bpp, unsigned long next_plane,
3213 unsigned long *dst, int dst_idx,
3214 unsigned long *src, int src_idx, u32 n)
3215{
3216 while (1) {
3217 dst += dst_idx >> SHIFT_PER_LONG;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003218 dst_idx &= (BITS_PER_LONG - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003219 src += src_idx >> SHIFT_PER_LONG;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003220 src_idx &= (BITS_PER_LONG - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003221 bitcpy(dst, dst_idx, src, src_idx, n);
3222 if (!--bpp)
3223 break;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003224 dst_idx += next_plane * 8;
3225 src_idx += next_plane * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003226 }
3227}
3228
3229static inline void copy_one_line_rev(int bpp, unsigned long next_plane,
3230 unsigned long *dst, int dst_idx,
3231 unsigned long *src, int src_idx, u32 n)
3232{
3233 while (1) {
3234 dst += dst_idx >> SHIFT_PER_LONG;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003235 dst_idx &= (BITS_PER_LONG - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003236 src += src_idx >> SHIFT_PER_LONG;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003237 src_idx &= (BITS_PER_LONG - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003238 bitcpy_rev(dst, dst_idx, src, src_idx, n);
3239 if (!--bpp)
3240 break;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003241 dst_idx += next_plane * 8;
3242 src_idx += next_plane * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003243 }
3244}
3245
3246
3247static void amifb_copyarea(struct fb_info *info,
3248 const struct fb_copyarea *area)
3249{
Geert Uytterhoeven423a5302011-11-21 21:53:56 +01003250 struct amifb_par *par = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003251 int x2, y2;
3252 u32 dx, dy, sx, sy, width, height;
3253 unsigned long *dst, *src;
3254 int dst_idx, src_idx;
3255 int rev_copy = 0;
3256
3257 /* clip the destination */
3258 x2 = area->dx + area->width;
3259 y2 = area->dy + area->height;
3260 dx = area->dx > 0 ? area->dx : 0;
3261 dy = area->dy > 0 ? area->dy : 0;
3262 x2 = x2 < info->var.xres_virtual ? x2 : info->var.xres_virtual;
3263 y2 = y2 < info->var.yres_virtual ? y2 : info->var.yres_virtual;
3264 width = x2 - dx;
3265 height = y2 - dy;
3266
Roel Kluin091c82c2008-07-23 21:31:18 -07003267 if (area->sx + dx < area->dx || area->sy + dy < area->dy)
3268 return;
3269
Linus Torvalds1da177e2005-04-16 15:20:36 -07003270 /* update sx,sy */
3271 sx = area->sx + (dx - area->dx);
3272 sy = area->sy + (dy - area->dy);
3273
3274 /* the source must be completely inside the virtual screen */
Roel Kluin091c82c2008-07-23 21:31:18 -07003275 if (sx + width > info->var.xres_virtual ||
3276 sy + height > info->var.yres_virtual)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003277 return;
3278
3279 if (dy > sy || (dy == sy && dx > sx)) {
3280 dy += height;
3281 sy += height;
3282 rev_copy = 1;
3283 }
3284 dst = (unsigned long *)
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003285 ((unsigned long)info->screen_base & ~(BYTES_PER_LONG - 1));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003286 src = dst;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003287 dst_idx = ((unsigned long)info->screen_base & (BYTES_PER_LONG - 1)) * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003288 src_idx = dst_idx;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003289 dst_idx += dy * par->next_line * 8 + dx;
3290 src_idx += sy * par->next_line * 8 + sx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003291 if (rev_copy) {
3292 while (height--) {
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003293 dst_idx -= par->next_line * 8;
3294 src_idx -= par->next_line * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003295 copy_one_line_rev(info->var.bits_per_pixel,
3296 par->next_plane, dst, dst_idx, src,
3297 src_idx, width);
3298 }
3299 } else {
3300 while (height--) {
3301 copy_one_line(info->var.bits_per_pixel,
3302 par->next_plane, dst, dst_idx, src,
3303 src_idx, width);
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003304 dst_idx += par->next_line * 8;
3305 src_idx += par->next_line * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003306 }
3307 }
3308}
3309
3310
3311static inline void expand_one_line(int bpp, unsigned long next_plane,
3312 unsigned long *dst, int dst_idx, u32 n,
3313 const u8 *data, u32 bgcolor, u32 fgcolor)
3314{
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003315 const unsigned long *src;
3316 int src_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003317
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003318 while (1) {
3319 dst += dst_idx >> SHIFT_PER_LONG;
3320 dst_idx &= (BITS_PER_LONG - 1);
3321 if ((bgcolor ^ fgcolor) & 1) {
3322 src = (unsigned long *)
3323 ((unsigned long)data & ~(BYTES_PER_LONG - 1));
3324 src_idx = ((unsigned long)data & (BYTES_PER_LONG - 1)) * 8;
3325 if (fgcolor & 1)
3326 bitcpy(dst, dst_idx, src, src_idx, n);
3327 else
3328 bitcpy_not(dst, dst_idx, src, src_idx, n);
3329 /* set or clear */
3330 } else
3331 bitfill32(dst, dst_idx, fgcolor & 1 ? ~0 : 0, n);
3332 if (!--bpp)
3333 break;
3334 bgcolor >>= 1;
3335 fgcolor >>= 1;
3336 dst_idx += next_plane * 8;
3337 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003338}
3339
3340
3341static void amifb_imageblit(struct fb_info *info, const struct fb_image *image)
3342{
Geert Uytterhoeven423a5302011-11-21 21:53:56 +01003343 struct amifb_par *par = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003344 int x2, y2;
3345 unsigned long *dst;
3346 int dst_idx;
3347 const char *src;
3348 u32 dx, dy, width, height, pitch;
3349
3350 /*
3351 * We could use hardware clipping but on many cards you get around
3352 * hardware clipping by writing to framebuffer directly like we are
3353 * doing here.
3354 */
3355 x2 = image->dx + image->width;
3356 y2 = image->dy + image->height;
3357 dx = image->dx;
3358 dy = image->dy;
3359 x2 = x2 < info->var.xres_virtual ? x2 : info->var.xres_virtual;
3360 y2 = y2 < info->var.yres_virtual ? y2 : info->var.yres_virtual;
3361 width = x2 - dx;
3362 height = y2 - dy;
3363
3364 if (image->depth == 1) {
3365 dst = (unsigned long *)
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003366 ((unsigned long)info->screen_base & ~(BYTES_PER_LONG - 1));
3367 dst_idx = ((unsigned long)info->screen_base & (BYTES_PER_LONG - 1)) * 8;
3368 dst_idx += dy * par->next_line * 8 + dx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003369 src = image->data;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003370 pitch = (image->width + 7) / 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003371 while (height--) {
3372 expand_one_line(info->var.bits_per_pixel,
3373 par->next_plane, dst, dst_idx, width,
3374 src, image->bg_color,
3375 image->fg_color);
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003376 dst_idx += par->next_line * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003377 src += pitch;
3378 }
3379 } else {
Geert Uytterhoeven2eab7ff2008-12-21 15:48:13 +01003380 c2p_planar(info->screen_base, image->data, dx, dy, width,
3381 height, par->next_line, par->next_plane,
3382 image->width, info->var.bits_per_pixel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003383 }
3384}
3385
3386
3387 /*
3388 * Amiga Frame Buffer Specific ioctls
3389 */
3390
Christoph Hellwig67a66802006-01-14 13:21:25 -08003391static int amifb_ioctl(struct fb_info *info,
3392 unsigned int cmd, unsigned long arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003393{
3394 union {
3395 struct fb_fix_cursorinfo fix;
3396 struct fb_var_cursorinfo var;
3397 struct fb_cursorstate state;
3398 } crsr;
Al Viro3728d252006-01-12 01:06:31 -08003399 void __user *argp = (void __user *)arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003400 int i;
3401
3402 switch (cmd) {
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003403 case FBIOGET_FCURSORINFO:
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003404 i = ami_get_fix_cursorinfo(&crsr.fix, info->par);
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003405 if (i)
3406 return i;
3407 return copy_to_user(argp, &crsr.fix,
3408 sizeof(crsr.fix)) ? -EFAULT : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003409
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003410 case FBIOGET_VCURSORINFO:
3411 i = ami_get_var_cursorinfo(&crsr.var,
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003412 ((struct fb_var_cursorinfo __user *)arg)->data,
3413 info->par);
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003414 if (i)
3415 return i;
3416 return copy_to_user(argp, &crsr.var,
3417 sizeof(crsr.var)) ? -EFAULT : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003418
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003419 case FBIOPUT_VCURSORINFO:
3420 if (copy_from_user(&crsr.var, argp, sizeof(crsr.var)))
3421 return -EFAULT;
3422 return ami_set_var_cursorinfo(&crsr.var,
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003423 ((struct fb_var_cursorinfo __user *)arg)->data,
3424 info->par);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003425
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003426 case FBIOGET_CURSORSTATE:
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003427 i = ami_get_cursorstate(&crsr.state, info->par);
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003428 if (i)
3429 return i;
3430 return copy_to_user(argp, &crsr.state,
3431 sizeof(crsr.state)) ? -EFAULT : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003432
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003433 case FBIOPUT_CURSORSTATE:
3434 if (copy_from_user(&crsr.state, argp, sizeof(crsr.state)))
3435 return -EFAULT;
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003436 return ami_set_cursorstate(&crsr.state, info->par);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003437 }
3438 return -EINVAL;
3439}
3440
3441
3442 /*
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01003443 * Flash the cursor (called by VBlank interrupt)
3444 */
3445
3446static int flash_cursor(void)
3447{
3448 static int cursorcount = 1;
3449
3450 if (cursormode == FB_CURSOR_FLASH) {
3451 if (!--cursorcount) {
3452 cursorstate = -cursorstate;
3453 cursorcount = cursorrate;
3454 if (!is_blanked)
3455 return 1;
3456 }
3457 }
3458 return 0;
3459}
3460
3461 /*
3462 * VBlank Display Interrupt
3463 */
3464
3465static irqreturn_t amifb_interrupt(int irq, void *dev_id)
3466{
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003467 struct amifb_par *par = dev_id;
3468
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01003469 if (do_vmode_pan || do_vmode_full)
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003470 ami_update_display(par);
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01003471
3472 if (do_vmode_full)
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003473 ami_init_display(par);
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01003474
3475 if (do_vmode_pan) {
3476 flash_cursor();
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003477 ami_rebuild_copper(par);
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01003478 do_cursor = do_vmode_pan = 0;
3479 } else if (do_cursor) {
3480 flash_cursor();
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003481 ami_set_sprite(par);
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01003482 do_cursor = 0;
3483 } else {
3484 if (flash_cursor())
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003485 ami_set_sprite(par);
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01003486 }
3487
3488 if (do_blank) {
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003489 ami_do_blank(par);
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01003490 do_blank = 0;
3491 }
3492
3493 if (do_vmode_full) {
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003494 ami_reinit_copper(par);
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01003495 do_vmode_full = 0;
3496 }
3497 return IRQ_HANDLED;
3498}
3499
3500
3501static struct fb_ops amifb_ops = {
3502 .owner = THIS_MODULE,
3503 .fb_check_var = amifb_check_var,
3504 .fb_set_par = amifb_set_par,
3505 .fb_setcolreg = amifb_setcolreg,
3506 .fb_blank = amifb_blank,
3507 .fb_pan_display = amifb_pan_display,
3508 .fb_fillrect = amifb_fillrect,
3509 .fb_copyarea = amifb_copyarea,
3510 .fb_imageblit = amifb_imageblit,
3511 .fb_ioctl = amifb_ioctl,
3512};
3513
3514
3515 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -07003516 * Allocate, Clear and Align a Block of Chip Memory
3517 */
3518
Geert Uytterhoeven8f25c012011-05-21 19:42:56 +00003519static void *aligned_chipptr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003520
3521static inline u_long __init chipalloc(u_long size)
3522{
Geert Uytterhoeven8f25c012011-05-21 19:42:56 +00003523 aligned_chipptr = amiga_chip_alloc(size, "amifb [RAM]");
3524 if (!aligned_chipptr) {
Geert Uytterhoevena7076422011-05-21 19:42:55 +00003525 pr_err("amifb: No Chip RAM for frame buffer");
3526 return 0;
3527 }
Geert Uytterhoeven8f25c012011-05-21 19:42:56 +00003528 memset(aligned_chipptr, 0, size);
3529 return (u_long)aligned_chipptr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003530}
3531
3532static inline void chipfree(void)
3533{
Geert Uytterhoeven8f25c012011-05-21 19:42:56 +00003534 if (aligned_chipptr)
3535 amiga_chip_free(aligned_chipptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003536}
3537
3538
3539 /*
3540 * Initialisation
3541 */
3542
Geert Uytterhoevenfa6688e2009-04-05 12:45:56 +02003543static int __init amifb_probe(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003544{
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003545 struct fb_info *info;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003546 int tag, i, err = 0;
3547 u_long chipptr;
3548 u_int defmode;
3549
3550#ifndef MODULE
3551 char *option = NULL;
3552
3553 if (fb_get_options("amifb", &option)) {
3554 amifb_video_off();
3555 return -ENODEV;
3556 }
3557 amifb_setup(option);
3558#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07003559 custom.dmacon = DMAF_ALL | DMAF_MASTER;
3560
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003561 info = framebuffer_alloc(sizeof(struct amifb_par), &pdev->dev);
3562 if (!info) {
3563 dev_err(&pdev->dev, "framebuffer_alloc failed\n");
3564 return -ENOMEM;
3565 }
3566
3567 strcpy(info->fix.id, "Amiga ");
3568 info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
3569 info->fix.accel = FB_ACCEL_AMIGABLITT;
3570
Linus Torvalds1da177e2005-04-16 15:20:36 -07003571 switch (amiga_chipset) {
3572#ifdef CONFIG_FB_AMIGA_OCS
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003573 case CS_OCS:
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003574 strcat(info->fix.id, "OCS");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003575default_chipset:
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003576 chipset = TAG_OCS;
3577 maxdepth[TAG_SHRES] = 0; /* OCS means no SHRES */
3578 maxdepth[TAG_HIRES] = 4;
3579 maxdepth[TAG_LORES] = 6;
3580 maxfmode = TAG_FMODE_1;
3581 defmode = amiga_vblank == 50 ? DEFMODE_PAL : DEFMODE_NTSC;
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003582 info->fix.smem_len = VIDEOMEMSIZE_OCS;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003583 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003584#endif /* CONFIG_FB_AMIGA_OCS */
3585
3586#ifdef CONFIG_FB_AMIGA_ECS
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003587 case CS_ECS:
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003588 strcat(info->fix.id, "ECS");
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003589 chipset = TAG_ECS;
3590 maxdepth[TAG_SHRES] = 2;
3591 maxdepth[TAG_HIRES] = 4;
3592 maxdepth[TAG_LORES] = 6;
3593 maxfmode = TAG_FMODE_1;
3594 if (AMIGAHW_PRESENT(AMBER_FF))
3595 defmode = amiga_vblank == 50 ? DEFMODE_AMBER_PAL
3596 : DEFMODE_AMBER_NTSC;
3597 else
3598 defmode = amiga_vblank == 50 ? DEFMODE_PAL
3599 : DEFMODE_NTSC;
3600 if (amiga_chip_avail() - CHIPRAM_SAFETY_LIMIT >
3601 VIDEOMEMSIZE_ECS_2M)
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003602 info->fix.smem_len = VIDEOMEMSIZE_ECS_2M;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003603 else
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003604 info->fix.smem_len = VIDEOMEMSIZE_ECS_1M;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003605 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003606#endif /* CONFIG_FB_AMIGA_ECS */
3607
3608#ifdef CONFIG_FB_AMIGA_AGA
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003609 case CS_AGA:
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003610 strcat(info->fix.id, "AGA");
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003611 chipset = TAG_AGA;
3612 maxdepth[TAG_SHRES] = 8;
3613 maxdepth[TAG_HIRES] = 8;
3614 maxdepth[TAG_LORES] = 8;
3615 maxfmode = TAG_FMODE_4;
3616 defmode = DEFMODE_AGA;
3617 if (amiga_chip_avail() - CHIPRAM_SAFETY_LIMIT >
3618 VIDEOMEMSIZE_AGA_2M)
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003619 info->fix.smem_len = VIDEOMEMSIZE_AGA_2M;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003620 else
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003621 info->fix.smem_len = VIDEOMEMSIZE_AGA_1M;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003622 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003623#endif /* CONFIG_FB_AMIGA_AGA */
3624
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003625 default:
Linus Torvalds1da177e2005-04-16 15:20:36 -07003626#ifdef CONFIG_FB_AMIGA_OCS
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003627 printk("Unknown graphics chipset, defaulting to OCS\n");
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003628 strcat(info->fix.id, "Unknown");
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003629 goto default_chipset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003630#else /* CONFIG_FB_AMIGA_OCS */
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003631 err = -ENODEV;
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003632 goto release;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003633#endif /* CONFIG_FB_AMIGA_OCS */
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003634 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003635 }
3636
3637 /*
3638 * Calculate the Pixel Clock Values for this Machine
3639 */
3640
3641 {
3642 u_long tmp = DIVUL(200000000000ULL, amiga_eclock);
3643
3644 pixclock[TAG_SHRES] = (tmp + 4) / 8; /* SHRES: 35 ns / 28 MHz */
3645 pixclock[TAG_HIRES] = (tmp + 2) / 4; /* HIRES: 70 ns / 14 MHz */
3646 pixclock[TAG_LORES] = (tmp + 1) / 2; /* LORES: 140 ns / 7 MHz */
3647 }
3648
3649 /*
3650 * Replace the Tag Values with the Real Pixel Clock Values
3651 */
3652
3653 for (i = 0; i < NUM_TOTAL_MODES; i++) {
3654 struct fb_videomode *mode = &ami_modedb[i];
3655 tag = mode->pixclock;
3656 if (tag == TAG_SHRES || tag == TAG_HIRES || tag == TAG_LORES) {
3657 mode->pixclock = pixclock[tag];
3658 }
3659 }
3660
Geert Uytterhoeven03c740a2011-11-21 21:53:57 +01003661 if (amifb_hfmin) {
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003662 info->monspecs.hfmin = amifb_hfmin;
3663 info->monspecs.hfmax = amifb_hfmax;
3664 info->monspecs.vfmin = amifb_vfmin;
3665 info->monspecs.vfmax = amifb_vfmax;
Geert Uytterhoeven03c740a2011-11-21 21:53:57 +01003666 } else {
3667 /*
3668 * These are for a typical Amiga monitor (e.g. A1960)
3669 */
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003670 info->monspecs.hfmin = 15000;
3671 info->monspecs.hfmax = 38000;
3672 info->monspecs.vfmin = 49;
3673 info->monspecs.vfmax = 90;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003674 }
3675
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003676 info->fbops = &amifb_ops;
3677 info->flags = FBINFO_DEFAULT;
3678 info->device = &pdev->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003679
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003680 if (!fb_find_mode(&info->var, info, mode_option, ami_modedb,
Linus Torvalds1da177e2005-04-16 15:20:36 -07003681 NUM_TOTAL_MODES, &ami_modedb[defmode], 4)) {
3682 err = -EINVAL;
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003683 goto release;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003684 }
3685
Geert Uytterhoevendb3e5282008-07-17 21:16:18 +02003686 fb_videomode_to_modelist(ami_modedb, NUM_TOTAL_MODES,
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003687 &info->modelist);
Geert Uytterhoevendb3e5282008-07-17 21:16:18 +02003688
Linus Torvalds1da177e2005-04-16 15:20:36 -07003689 round_down_bpp = 0;
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003690 chipptr = chipalloc(info->fix.smem_len + SPRITEMEMSIZE +
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003691 DUMMYSPRITEMEMSIZE + COPINITSIZE +
3692 4 * COPLISTSIZE);
Geert Uytterhoevena7076422011-05-21 19:42:55 +00003693 if (!chipptr) {
3694 err = -ENOMEM;
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003695 goto release;
Geert Uytterhoevena7076422011-05-21 19:42:55 +00003696 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003697
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003698 assignchunk(videomemory, u_long, chipptr, info->fix.smem_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003699 assignchunk(spritememory, u_long, chipptr, SPRITEMEMSIZE);
3700 assignchunk(dummysprite, u_short *, chipptr, DUMMYSPRITEMEMSIZE);
3701 assignchunk(copdisplay.init, copins *, chipptr, COPINITSIZE);
3702 assignchunk(copdisplay.list[0][0], copins *, chipptr, COPLISTSIZE);
3703 assignchunk(copdisplay.list[0][1], copins *, chipptr, COPLISTSIZE);
3704 assignchunk(copdisplay.list[1][0], copins *, chipptr, COPLISTSIZE);
3705 assignchunk(copdisplay.list[1][1], copins *, chipptr, COPLISTSIZE);
3706
3707 /*
3708 * access the videomem with writethrough cache
3709 */
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003710 info->fix.smem_start = (u_long)ZTWO_PADDR(videomemory);
3711 videomemory = (u_long)ioremap_writethrough(info->fix.smem_start,
3712 info->fix.smem_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003713 if (!videomemory) {
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003714 dev_warn(&pdev->dev,
3715 "Unable to map videomem cached writethrough\n");
3716 info->screen_base = (char *)ZTWO_VADDR(info->fix.smem_start);
Amol Lad57354c42006-12-08 02:40:16 -08003717 } else
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003718 info->screen_base = (char *)videomemory;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003719
Linus Torvalds1da177e2005-04-16 15:20:36 -07003720 memset(dummysprite, 0, DUMMYSPRITEMEMSIZE);
3721
3722 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -07003723 * Make sure the Copper has something to do
3724 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003725 ami_init_copper();
3726
Geert Uytterhoevenaf5761d2011-11-21 21:53:58 +01003727 /*
3728 * Enable Display DMA
3729 */
3730 custom.dmacon = DMAF_SETCLR | DMAF_MASTER | DMAF_RASTER | DMAF_COPPER |
3731 DMAF_BLITTER | DMAF_SPRITE;
3732
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003733 err = request_irq(IRQ_AMIGA_COPPER, amifb_interrupt, 0,
3734 "fb vertb handler", info->par);
Andres Salomoneb8972b2009-03-31 15:25:30 -07003735 if (err)
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003736 goto disable_dma;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003737
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003738 err = fb_alloc_cmap(&info->cmap, 1 << info->var.bits_per_pixel, 0);
3739 if (err)
3740 goto free_irq;
3741
3742 dev_set_drvdata(&pdev->dev, info);
3743
3744 err = register_framebuffer(info);
3745 if (err)
3746 goto unset_drvdata;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003747
3748 printk("fb%d: %s frame buffer device, using %dK of video memory\n",
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003749 info->node, info->fix.id, info->fix.smem_len>>10);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003750
3751 return 0;
3752
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003753unset_drvdata:
3754 dev_set_drvdata(&pdev->dev, NULL);
3755 fb_dealloc_cmap(&info->cmap);
3756free_irq:
3757 free_irq(IRQ_AMIGA_COPPER, info->par);
3758disable_dma:
3759 custom.dmacon = DMAF_ALL | DMAF_MASTER;
3760 if (videomemory)
3761 iounmap((void *)videomemory);
3762 chipfree();
3763release:
3764 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003765 return err;
3766}
3767
Linus Torvalds1da177e2005-04-16 15:20:36 -07003768
Geert Uytterhoevenfa6688e2009-04-05 12:45:56 +02003769static int __exit amifb_remove(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003770{
Geert Uytterhoeven61640c22011-11-21 21:53:59 +01003771 struct fb_info *info = dev_get_drvdata(&pdev->dev);
3772
3773 unregister_framebuffer(info);
3774 dev_set_drvdata(&pdev->dev, NULL);
3775 fb_dealloc_cmap(&info->cmap);
3776 free_irq(IRQ_AMIGA_COPPER, info->par);
3777 custom.dmacon = DMAF_ALL | DMAF_MASTER;
3778 if (videomemory)
3779 iounmap((void *)videomemory);
3780 chipfree();
3781 framebuffer_release(info);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003782 amifb_video_off();
Geert Uytterhoevenfa6688e2009-04-05 12:45:56 +02003783 return 0;
3784}
3785
3786static struct platform_driver amifb_driver = {
3787 .remove = __exit_p(amifb_remove),
3788 .driver = {
3789 .name = "amiga-video",
3790 .owner = THIS_MODULE,
3791 },
3792};
3793
3794static int __init amifb_init(void)
3795{
3796 return platform_driver_probe(&amifb_driver, amifb_probe);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003797}
Adrian Bunk57987122008-07-23 21:31:43 -07003798
3799module_init(amifb_init);
Geert Uytterhoevenfa6688e2009-04-05 12:45:56 +02003800
3801static void __exit amifb_exit(void)
3802{
3803 platform_driver_unregister(&amifb_driver);
3804}
3805
Adrian Bunk57987122008-07-23 21:31:43 -07003806module_exit(amifb_exit);
3807
3808MODULE_LICENSE("GPL");
Geert Uytterhoevenfa6688e2009-04-05 12:45:56 +02003809MODULE_ALIAS("platform:amiga-video");