blob: 13e7a179f8d472e3837261d3d6bf5aed8dfbef29 [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
713static struct amifb_par {
714
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 */
776} currentpar;
777
778
779static struct fb_info fb_info = {
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100780 .fix = {
781 .id = "Amiga ",
782 .visual = FB_VISUAL_PSEUDOCOLOR,
783 .accel = FB_ACCEL_AMIGABLITT
784 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785};
786
787
788 /*
789 * Saved color entry 0 so we can restore it when unblanking
790 */
791
792static u_char red0, green0, blue0;
793
794
795#if defined(CONFIG_FB_AMIGA_ECS)
796static u_short ecs_palette[32];
797#endif
798
799
800 /*
801 * Latches for Display Changes during VBlank
802 */
803
804static u_short do_vmode_full = 0; /* Change the Video Mode */
805static u_short do_vmode_pan = 0; /* Update the Video Mode */
Jan Engelhardt96de0e22007-10-19 23:21:04 +0200806static short do_blank = 0; /* (Un)Blank the Screen (±1) */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807static u_short do_cursor = 0; /* Move the Cursor */
808
809
810 /*
811 * Various Flags
812 */
813
814static u_short is_blanked = 0; /* Screen is Blanked */
815static u_short is_lace = 0; /* Screen is laced */
816
817 /*
818 * Predefined Video Modes
819 *
820 */
821
822static struct fb_videomode ami_modedb[] __initdata = {
823
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100824 /*
825 * AmigaOS Video Modes
826 *
827 * If you change these, make sure to update DEFMODE_* as well!
828 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100830 {
831 /* 640x200, 15 kHz, 60 Hz (NTSC) */
832 "ntsc", 60, 640, 200, TAG_HIRES, 106, 86, 44, 16, 76, 2,
833 FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
834 }, {
835 /* 640x400, 15 kHz, 60 Hz interlaced (NTSC) */
836 "ntsc-lace", 60, 640, 400, TAG_HIRES, 106, 86, 88, 33, 76, 4,
837 FB_SYNC_BROADCAST, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
838 }, {
839 /* 640x256, 15 kHz, 50 Hz (PAL) */
840 "pal", 50, 640, 256, TAG_HIRES, 106, 86, 40, 14, 76, 2,
841 FB_SYNC_BROADCAST, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
842 }, {
843 /* 640x512, 15 kHz, 50 Hz interlaced (PAL) */
844 "pal-lace", 50, 640, 512, TAG_HIRES, 106, 86, 80, 29, 76, 4,
845 FB_SYNC_BROADCAST, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
846 }, {
847 /* 640x480, 29 kHz, 57 Hz */
848 "multiscan", 57, 640, 480, TAG_SHRES, 96, 112, 29, 8, 72, 8,
849 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
850 }, {
851 /* 640x960, 29 kHz, 57 Hz interlaced */
852 "multiscan-lace", 57, 640, 960, TAG_SHRES, 96, 112, 58, 16, 72,
853 16,
854 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
855 }, {
856 /* 640x200, 15 kHz, 72 Hz */
857 "euro36", 72, 640, 200, TAG_HIRES, 92, 124, 6, 6, 52, 5,
858 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
859 }, {
860 /* 640x400, 15 kHz, 72 Hz interlaced */
861 "euro36-lace", 72, 640, 400, TAG_HIRES, 92, 124, 12, 12, 52,
862 10,
863 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
864 }, {
865 /* 640x400, 29 kHz, 68 Hz */
866 "euro72", 68, 640, 400, TAG_SHRES, 164, 92, 9, 9, 80, 8,
867 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
868 }, {
869 /* 640x800, 29 kHz, 68 Hz interlaced */
870 "euro72-lace", 68, 640, 800, TAG_SHRES, 164, 92, 18, 18, 80,
871 16,
872 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
873 }, {
874 /* 800x300, 23 kHz, 70 Hz */
875 "super72", 70, 800, 300, TAG_SHRES, 212, 140, 10, 11, 80, 7,
876 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
877 }, {
878 /* 800x600, 23 kHz, 70 Hz interlaced */
879 "super72-lace", 70, 800, 600, TAG_SHRES, 212, 140, 20, 22, 80,
880 14,
881 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
882 }, {
883 /* 640x200, 27 kHz, 57 Hz doublescan */
884 "dblntsc", 57, 640, 200, TAG_SHRES, 196, 124, 18, 17, 80, 4,
885 0, FB_VMODE_DOUBLE | FB_VMODE_YWRAP
886 }, {
887 /* 640x400, 27 kHz, 57 Hz */
888 "dblntsc-ff", 57, 640, 400, TAG_SHRES, 196, 124, 36, 35, 80, 7,
889 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
890 }, {
891 /* 640x800, 27 kHz, 57 Hz interlaced */
892 "dblntsc-lace", 57, 640, 800, TAG_SHRES, 196, 124, 72, 70, 80,
893 14,
894 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
895 }, {
896 /* 640x256, 27 kHz, 47 Hz doublescan */
897 "dblpal", 47, 640, 256, TAG_SHRES, 196, 124, 14, 13, 80, 4,
898 0, FB_VMODE_DOUBLE | FB_VMODE_YWRAP
899 }, {
900 /* 640x512, 27 kHz, 47 Hz */
901 "dblpal-ff", 47, 640, 512, TAG_SHRES, 196, 124, 28, 27, 80, 7,
902 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
903 }, {
904 /* 640x1024, 27 kHz, 47 Hz interlaced */
905 "dblpal-lace", 47, 640, 1024, TAG_SHRES, 196, 124, 56, 54, 80,
906 14,
907 0, FB_VMODE_INTERLACED | FB_VMODE_YWRAP
908 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700909
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100910 /*
911 * VGA Video Modes
912 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100914 {
915 /* 640x480, 31 kHz, 60 Hz (VGA) */
916 "vga", 60, 640, 480, TAG_SHRES, 64, 96, 30, 9, 112, 2,
917 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
918 }, {
919 /* 640x400, 31 kHz, 70 Hz (VGA) */
920 "vga70", 70, 640, 400, TAG_SHRES, 64, 96, 35, 12, 112, 2,
921 FB_SYNC_VERT_HIGH_ACT | FB_SYNC_COMP_HIGH_ACT,
922 FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
923 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924
925#if 0
926
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100927 /*
928 * A2024 video modes
929 * These modes don't work yet because there's no A2024 driver.
930 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700931
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +0100932 {
933 /* 1024x800, 10 Hz */
934 "a2024-10", 10, 1024, 800, TAG_HIRES, 0, 0, 0, 0, 0, 0,
935 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
936 }, {
937 /* 1024x800, 15 Hz */
938 "a2024-15", 15, 1024, 800, TAG_HIRES, 0, 0, 0, 0, 0, 0,
939 0, FB_VMODE_NONINTERLACED | FB_VMODE_YWRAP
940 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700941#endif
942};
943
944#define NUM_TOTAL_MODES ARRAY_SIZE(ami_modedb)
945
946static char *mode_option __initdata = NULL;
947static int round_down_bpp = 1; /* for mode probing */
948
949 /*
950 * Some default modes
951 */
952
953
954#define DEFMODE_PAL 2 /* "pal" for PAL OCS/ECS */
955#define DEFMODE_NTSC 0 /* "ntsc" for NTSC OCS/ECS */
956#define DEFMODE_AMBER_PAL 3 /* "pal-lace" for flicker fixed PAL (A3000) */
957#define DEFMODE_AMBER_NTSC 1 /* "ntsc-lace" for flicker fixed NTSC (A3000) */
958#define DEFMODE_AGA 19 /* "vga70" for AGA */
959
960
961static int amifb_ilbm = 0; /* interleaved or normal bitplanes */
962static int amifb_inverse = 0;
963
Geert Uytterhoeven03c740a2011-11-21 21:53:57 +0100964static u32 amifb_hfmin __initdata; /* monitor hfreq lower limit (Hz) */
965static u32 amifb_hfmax __initdata; /* monitor hfreq upper limit (Hz) */
966static u16 amifb_vfmin __initdata; /* monitor vfreq lower limit (Hz) */
967static u16 amifb_vfmax __initdata; /* monitor vfreq upper limit (Hz) */
968
Linus Torvalds1da177e2005-04-16 15:20:36 -0700969
970 /*
971 * Macros for the conversion from real world values to hardware register
972 * values
973 *
974 * This helps us to keep our attention on the real stuff...
975 *
976 * Hardware limits for AGA:
977 *
978 * parameter min max step
979 * --------- --- ---- ----
980 * diwstrt_h 0 2047 1
981 * diwstrt_v 0 2047 1
982 * diwstop_h 0 4095 1
983 * diwstop_v 0 4095 1
984 *
985 * ddfstrt 0 2032 16
986 * ddfstop 0 2032 16
987 *
988 * htotal 8 2048 8
989 * hsstrt 0 2040 8
990 * hsstop 0 2040 8
991 * vtotal 1 4096 1
992 * vsstrt 0 4095 1
993 * vsstop 0 4095 1
994 * hcenter 0 2040 8
995 *
996 * hbstrt 0 2047 1
997 * hbstop 0 2047 1
998 * vbstrt 0 4095 1
999 * vbstop 0 4095 1
1000 *
1001 * Horizontal values are in 35 ns (SHRES) pixels
1002 * Vertical values are in half scanlines
1003 */
1004
1005/* bplcon1 (smooth scrolling) */
1006
1007#define hscroll2hw(hscroll) \
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001008 (((hscroll) << 12 & 0x3000) | ((hscroll) << 8 & 0xc300) | \
1009 ((hscroll) << 4 & 0x0c00) | ((hscroll) << 2 & 0x00f0) | \
1010 ((hscroll)>>2 & 0x000f))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011
1012/* diwstrt/diwstop/diwhigh (visible display window) */
1013
1014#define diwstrt2hw(diwstrt_h, diwstrt_v) \
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001015 (((diwstrt_v) << 7 & 0xff00) | ((diwstrt_h)>>2 & 0x00ff))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001016#define diwstop2hw(diwstop_h, diwstop_v) \
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001017 (((diwstop_v) << 7 & 0xff00) | ((diwstop_h)>>2 & 0x00ff))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001018#define diwhigh2hw(diwstrt_h, diwstrt_v, diwstop_h, diwstop_v) \
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001019 (((diwstop_h) << 3 & 0x2000) | ((diwstop_h) << 11 & 0x1800) | \
Linus Torvalds1da177e2005-04-16 15:20:36 -07001020 ((diwstop_v)>>1 & 0x0700) | ((diwstrt_h)>>5 & 0x0020) | \
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001021 ((diwstrt_h) << 3 & 0x0018) | ((diwstrt_v)>>9 & 0x0007))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022
1023/* ddfstrt/ddfstop (display DMA) */
1024
1025#define ddfstrt2hw(ddfstrt) div8(ddfstrt)
1026#define ddfstop2hw(ddfstop) div8(ddfstop)
1027
1028/* hsstrt/hsstop/htotal/vsstrt/vsstop/vtotal/hcenter (sync timings) */
1029
1030#define hsstrt2hw(hsstrt) (div8(hsstrt))
1031#define hsstop2hw(hsstop) (div8(hsstop))
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001032#define htotal2hw(htotal) (div8(htotal) - 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001033#define vsstrt2hw(vsstrt) (div2(vsstrt))
1034#define vsstop2hw(vsstop) (div2(vsstop))
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001035#define vtotal2hw(vtotal) (div2(vtotal) - 1)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001036#define hcenter2hw(htotal) (div8(htotal))
1037
1038/* hbstrt/hbstop/vbstrt/vbstop (blanking timings) */
1039
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001040#define hbstrt2hw(hbstrt) (((hbstrt) << 8 & 0x0700) | ((hbstrt)>>3 & 0x00ff))
1041#define hbstop2hw(hbstop) (((hbstop) << 8 & 0x0700) | ((hbstop)>>3 & 0x00ff))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001042#define vbstrt2hw(vbstrt) (div2(vbstrt))
1043#define vbstop2hw(vbstop) (div2(vbstop))
1044
1045/* colour */
1046
1047#define rgb2hw8_high(red, green, blue) \
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001048 (((red & 0xf0) << 4) | (green & 0xf0) | ((blue & 0xf0)>>4))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001049#define rgb2hw8_low(red, green, blue) \
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001050 (((red & 0x0f) << 8) | ((green & 0x0f) << 4) | (blue & 0x0f))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001051#define rgb2hw4(red, green, blue) \
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001052 (((red & 0xf0) << 4) | (green & 0xf0) | ((blue & 0xf0)>>4))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001053#define rgb2hw2(red, green, blue) \
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001054 (((red & 0xc0) << 4) | (green & 0xc0) | ((blue & 0xc0)>>4))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001055
1056/* sprpos/sprctl (sprite positioning) */
1057
1058#define spr2hw_pos(start_v, start_h) \
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001059 (((start_v) << 7 & 0xff00) | ((start_h)>>3 & 0x00ff))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001060#define spr2hw_ctl(start_v, start_h, stop_v) \
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001061 (((stop_v) << 7 & 0xff00) | ((start_v)>>4 & 0x0040) | \
1062 ((stop_v)>>5 & 0x0020) | ((start_h) << 3 & 0x0018) | \
1063 ((start_v)>>7 & 0x0004) | ((stop_v)>>8 & 0x0002) | \
1064 ((start_h)>>2 & 0x0001))
Linus Torvalds1da177e2005-04-16 15:20:36 -07001065
1066/* get current vertical position of beam */
1067#define get_vbpos() ((u_short)((*(u_long volatile *)&custom.vposr >> 7) & 0xffe))
1068
1069 /*
1070 * Copper Initialisation List
1071 */
1072
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001073#define COPINITSIZE (sizeof(copins) * 40)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001074
1075enum {
1076 cip_bplcon0
1077};
1078
1079 /*
1080 * Long Frame/Short Frame Copper List
1081 * Don't change the order, build_copper()/rebuild_copper() rely on this
1082 */
1083
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01001084#define COPLISTSIZE (sizeof(copins) * 64)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001085
1086enum {
1087 cop_wait, cop_bplcon0,
1088 cop_spr0ptrh, cop_spr0ptrl,
1089 cop_diwstrt, cop_diwstop,
1090 cop_diwhigh,
1091};
1092
1093 /*
1094 * Pixel modes for Bitplanes and Sprites
1095 */
1096
1097static u_short bplpixmode[3] = {
1098 BPC0_SHRES, /* 35 ns */
1099 BPC0_HIRES, /* 70 ns */
1100 0 /* 140 ns */
1101};
1102
1103static u_short sprpixmode[3] = {
1104 BPC3_SPRES1 | BPC3_SPRES0, /* 35 ns */
1105 BPC3_SPRES1, /* 70 ns */
1106 BPC3_SPRES0 /* 140 ns */
1107};
1108
1109 /*
1110 * Fetch modes for Bitplanes and Sprites
1111 */
1112
1113static u_short bplfetchmode[3] = {
1114 0, /* 1x */
1115 FMODE_BPL32, /* 2x */
1116 FMODE_BPAGEM | FMODE_BPL32 /* 4x */
1117};
1118
1119static u_short sprfetchmode[3] = {
1120 0, /* 1x */
1121 FMODE_SPR32, /* 2x */
1122 FMODE_SPAGEM | FMODE_SPR32 /* 4x */
1123};
1124
1125
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01001126/* --------------------------- Hardware routines --------------------------- */
Linus Torvalds1da177e2005-04-16 15:20:36 -07001127
1128 /*
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01001129 * Get the video params out of `var'. If a value doesn't fit, round
1130 * it up, if it's too big, return -EINVAL.
Linus Torvalds1da177e2005-04-16 15:20:36 -07001131 */
1132
1133static int ami_decode_var(struct fb_var_screeninfo *var,
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01001134 struct amifb_par *par)
1135{
1136 u_short clk_shift, line_shift;
1137 u_long maxfetchstop, fstrt, fsize, fconst, xres_n, yres_n;
1138 u_int htotal, vtotal;
1139
1140 /*
1141 * Find a matching Pixel Clock
1142 */
1143
1144 for (clk_shift = TAG_SHRES; clk_shift <= TAG_LORES; clk_shift++)
1145 if (var->pixclock <= pixclock[clk_shift])
1146 break;
1147 if (clk_shift > TAG_LORES) {
1148 DPRINTK("pixclock too high\n");
1149 return -EINVAL;
1150 }
1151 par->clk_shift = clk_shift;
1152
1153 /*
1154 * Check the Geometry Values
1155 */
1156
1157 if ((par->xres = var->xres) < 64)
1158 par->xres = 64;
1159 if ((par->yres = var->yres) < 64)
1160 par->yres = 64;
1161 if ((par->vxres = var->xres_virtual) < par->xres)
1162 par->vxres = par->xres;
1163 if ((par->vyres = var->yres_virtual) < par->yres)
1164 par->vyres = par->yres;
1165
1166 par->bpp = var->bits_per_pixel;
1167 if (!var->nonstd) {
1168 if (par->bpp < 1)
1169 par->bpp = 1;
1170 if (par->bpp > maxdepth[clk_shift]) {
1171 if (round_down_bpp && maxdepth[clk_shift])
1172 par->bpp = maxdepth[clk_shift];
1173 else {
1174 DPRINTK("invalid bpp\n");
1175 return -EINVAL;
1176 }
1177 }
1178 } else if (var->nonstd == FB_NONSTD_HAM) {
1179 if (par->bpp < 6)
1180 par->bpp = 6;
1181 if (par->bpp != 6) {
1182 if (par->bpp < 8)
1183 par->bpp = 8;
1184 if (par->bpp != 8 || !IS_AGA) {
1185 DPRINTK("invalid bpp for ham mode\n");
1186 return -EINVAL;
1187 }
1188 }
1189 } else {
1190 DPRINTK("unknown nonstd mode\n");
1191 return -EINVAL;
1192 }
1193
1194 /*
1195 * FB_VMODE_SMOOTH_XPAN will be cleared, if one of the folloing
1196 * checks failed and smooth scrolling is not possible
1197 */
1198
1199 par->vmode = var->vmode | FB_VMODE_SMOOTH_XPAN;
1200 switch (par->vmode & FB_VMODE_MASK) {
1201 case FB_VMODE_INTERLACED:
1202 line_shift = 0;
1203 break;
1204 case FB_VMODE_NONINTERLACED:
1205 line_shift = 1;
1206 break;
1207 case FB_VMODE_DOUBLE:
1208 if (!IS_AGA) {
1209 DPRINTK("double mode only possible with aga\n");
1210 return -EINVAL;
1211 }
1212 line_shift = 2;
1213 break;
1214 default:
1215 DPRINTK("unknown video mode\n");
1216 return -EINVAL;
1217 break;
1218 }
1219 par->line_shift = line_shift;
1220
1221 /*
1222 * Vertical and Horizontal Timings
1223 */
1224
1225 xres_n = par->xres << clk_shift;
1226 yres_n = par->yres << line_shift;
1227 par->htotal = down8((var->left_margin + par->xres + var->right_margin +
1228 var->hsync_len) << clk_shift);
1229 par->vtotal =
1230 down2(((var->upper_margin + par->yres + var->lower_margin +
1231 var->vsync_len) << line_shift) + 1);
1232
1233 if (IS_AGA)
1234 par->bplcon3 = sprpixmode[clk_shift];
1235 else
1236 par->bplcon3 = 0;
1237 if (var->sync & FB_SYNC_BROADCAST) {
1238 par->diwstop_h = par->htotal -
1239 ((var->right_margin - var->hsync_len) << clk_shift);
1240 if (IS_AGA)
1241 par->diwstop_h += mod4(var->hsync_len);
1242 else
1243 par->diwstop_h = down4(par->diwstop_h);
1244
1245 par->diwstrt_h = par->diwstop_h - xres_n;
1246 par->diwstop_v = par->vtotal -
1247 ((var->lower_margin - var->vsync_len) << line_shift);
1248 par->diwstrt_v = par->diwstop_v - yres_n;
1249 if (par->diwstop_h >= par->htotal + 8) {
1250 DPRINTK("invalid diwstop_h\n");
1251 return -EINVAL;
1252 }
1253 if (par->diwstop_v > par->vtotal) {
1254 DPRINTK("invalid diwstop_v\n");
1255 return -EINVAL;
1256 }
1257
1258 if (!IS_OCS) {
1259 /* Initialize sync with some reasonable values for pwrsave */
1260 par->hsstrt = 160;
1261 par->hsstop = 320;
1262 par->vsstrt = 30;
1263 par->vsstop = 34;
1264 } else {
1265 par->hsstrt = 0;
1266 par->hsstop = 0;
1267 par->vsstrt = 0;
1268 par->vsstop = 0;
1269 }
1270 if (par->vtotal > (PAL_VTOTAL + NTSC_VTOTAL) / 2) {
1271 /* PAL video mode */
1272 if (par->htotal != PAL_HTOTAL) {
1273 DPRINTK("htotal invalid for pal\n");
1274 return -EINVAL;
1275 }
1276 if (par->diwstrt_h < PAL_DIWSTRT_H) {
1277 DPRINTK("diwstrt_h too low for pal\n");
1278 return -EINVAL;
1279 }
1280 if (par->diwstrt_v < PAL_DIWSTRT_V) {
1281 DPRINTK("diwstrt_v too low for pal\n");
1282 return -EINVAL;
1283 }
1284 htotal = PAL_HTOTAL>>clk_shift;
1285 vtotal = PAL_VTOTAL>>1;
1286 if (!IS_OCS) {
1287 par->beamcon0 = BMC0_PAL;
1288 par->bplcon3 |= BPC3_BRDRBLNK;
1289 } else if (AMIGAHW_PRESENT(AGNUS_HR_PAL) ||
1290 AMIGAHW_PRESENT(AGNUS_HR_NTSC)) {
1291 par->beamcon0 = BMC0_PAL;
1292 par->hsstop = 1;
1293 } else if (amiga_vblank != 50) {
1294 DPRINTK("pal not supported by this chipset\n");
1295 return -EINVAL;
1296 }
1297 } else {
1298 /* NTSC video mode
1299 * In the AGA chipset seems to be hardware bug with BPC3_BRDRBLNK
1300 * and NTSC activated, so than better let diwstop_h <= 1812
1301 */
1302 if (par->htotal != NTSC_HTOTAL) {
1303 DPRINTK("htotal invalid for ntsc\n");
1304 return -EINVAL;
1305 }
1306 if (par->diwstrt_h < NTSC_DIWSTRT_H) {
1307 DPRINTK("diwstrt_h too low for ntsc\n");
1308 return -EINVAL;
1309 }
1310 if (par->diwstrt_v < NTSC_DIWSTRT_V) {
1311 DPRINTK("diwstrt_v too low for ntsc\n");
1312 return -EINVAL;
1313 }
1314 htotal = NTSC_HTOTAL>>clk_shift;
1315 vtotal = NTSC_VTOTAL>>1;
1316 if (!IS_OCS) {
1317 par->beamcon0 = 0;
1318 par->bplcon3 |= BPC3_BRDRBLNK;
1319 } else if (AMIGAHW_PRESENT(AGNUS_HR_PAL) ||
1320 AMIGAHW_PRESENT(AGNUS_HR_NTSC)) {
1321 par->beamcon0 = 0;
1322 par->hsstop = 1;
1323 } else if (amiga_vblank != 60) {
1324 DPRINTK("ntsc not supported by this chipset\n");
1325 return -EINVAL;
1326 }
1327 }
1328 if (IS_OCS) {
1329 if (par->diwstrt_h >= 1024 || par->diwstop_h < 1024 ||
1330 par->diwstrt_v >= 512 || par->diwstop_v < 256) {
1331 DPRINTK("invalid position for display on ocs\n");
1332 return -EINVAL;
1333 }
1334 }
1335 } else if (!IS_OCS) {
1336 /* Programmable video mode */
1337 par->hsstrt = var->right_margin << clk_shift;
1338 par->hsstop = (var->right_margin + var->hsync_len) << clk_shift;
1339 par->diwstop_h = par->htotal - mod8(par->hsstrt) + 8 - (1 << clk_shift);
1340 if (!IS_AGA)
1341 par->diwstop_h = down4(par->diwstop_h) - 16;
1342 par->diwstrt_h = par->diwstop_h - xres_n;
1343 par->hbstop = par->diwstrt_h + 4;
1344 par->hbstrt = par->diwstop_h + 4;
1345 if (par->hbstrt >= par->htotal + 8)
1346 par->hbstrt -= par->htotal;
1347 par->hcenter = par->hsstrt + (par->htotal >> 1);
1348 par->vsstrt = var->lower_margin << line_shift;
1349 par->vsstop = (var->lower_margin + var->vsync_len) << line_shift;
1350 par->diwstop_v = par->vtotal;
1351 if ((par->vmode & FB_VMODE_MASK) == FB_VMODE_INTERLACED)
1352 par->diwstop_v -= 2;
1353 par->diwstrt_v = par->diwstop_v - yres_n;
1354 par->vbstop = par->diwstrt_v - 2;
1355 par->vbstrt = par->diwstop_v - 2;
1356 if (par->vtotal > 2048) {
1357 DPRINTK("vtotal too high\n");
1358 return -EINVAL;
1359 }
1360 if (par->htotal > 2048) {
1361 DPRINTK("htotal too high\n");
1362 return -EINVAL;
1363 }
1364 par->bplcon3 |= BPC3_EXTBLKEN;
1365 par->beamcon0 = BMC0_HARDDIS | BMC0_VARVBEN | BMC0_LOLDIS |
1366 BMC0_VARVSYEN | BMC0_VARHSYEN | BMC0_VARBEAMEN |
1367 BMC0_PAL | BMC0_VARCSYEN;
1368 if (var->sync & FB_SYNC_HOR_HIGH_ACT)
1369 par->beamcon0 |= BMC0_HSYTRUE;
1370 if (var->sync & FB_SYNC_VERT_HIGH_ACT)
1371 par->beamcon0 |= BMC0_VSYTRUE;
1372 if (var->sync & FB_SYNC_COMP_HIGH_ACT)
1373 par->beamcon0 |= BMC0_CSYTRUE;
1374 htotal = par->htotal>>clk_shift;
1375 vtotal = par->vtotal>>1;
1376 } else {
1377 DPRINTK("only broadcast modes possible for ocs\n");
1378 return -EINVAL;
1379 }
1380
1381 /*
1382 * Checking the DMA timing
1383 */
1384
1385 fconst = 16 << maxfmode << clk_shift;
1386
1387 /*
1388 * smallest window start value without turn off other dma cycles
1389 * than sprite1-7, unless you change min_fstrt
1390 */
1391
1392
1393 fsize = ((maxfmode + clk_shift <= 1) ? fconst : 64);
1394 fstrt = downx(fconst, par->diwstrt_h - 4) - fsize;
1395 if (fstrt < min_fstrt) {
1396 DPRINTK("fetch start too low\n");
1397 return -EINVAL;
1398 }
1399
1400 /*
1401 * smallest window start value where smooth scrolling is possible
1402 */
1403
1404 fstrt = downx(fconst, par->diwstrt_h - fconst + (1 << clk_shift) - 4) -
1405 fsize;
1406 if (fstrt < min_fstrt)
1407 par->vmode &= ~FB_VMODE_SMOOTH_XPAN;
1408
1409 maxfetchstop = down16(par->htotal - 80);
1410
1411 fstrt = downx(fconst, par->diwstrt_h - 4) - 64 - fconst;
1412 fsize = upx(fconst, xres_n +
1413 modx(fconst, downx(1 << clk_shift, par->diwstrt_h - 4)));
1414 if (fstrt + fsize > maxfetchstop)
1415 par->vmode &= ~FB_VMODE_SMOOTH_XPAN;
1416
1417 fsize = upx(fconst, xres_n);
1418 if (fstrt + fsize > maxfetchstop) {
1419 DPRINTK("fetch stop too high\n");
1420 return -EINVAL;
1421 }
1422
1423 if (maxfmode + clk_shift <= 1) {
1424 fsize = up64(xres_n + fconst - 1);
1425 if (min_fstrt + fsize - 64 > maxfetchstop)
1426 par->vmode &= ~FB_VMODE_SMOOTH_XPAN;
1427
1428 fsize = up64(xres_n);
1429 if (min_fstrt + fsize - 64 > maxfetchstop) {
1430 DPRINTK("fetch size too high\n");
1431 return -EINVAL;
1432 }
1433
1434 fsize -= 64;
1435 } else
1436 fsize -= fconst;
1437
1438 /*
1439 * Check if there is enough time to update the bitplane pointers for ywrap
1440 */
1441
1442 if (par->htotal - fsize - 64 < par->bpp * 64)
1443 par->vmode &= ~FB_VMODE_YWRAP;
1444
1445 /*
1446 * Bitplane calculations and check the Memory Requirements
1447 */
1448
1449 if (amifb_ilbm) {
1450 par->next_plane = div8(upx(16 << maxfmode, par->vxres));
1451 par->next_line = par->bpp * par->next_plane;
1452 if (par->next_line * par->vyres > fb_info.fix.smem_len) {
1453 DPRINTK("too few video mem\n");
1454 return -EINVAL;
1455 }
1456 } else {
1457 par->next_line = div8(upx(16 << maxfmode, par->vxres));
1458 par->next_plane = par->vyres * par->next_line;
1459 if (par->next_plane * par->bpp > fb_info.fix.smem_len) {
1460 DPRINTK("too few video mem\n");
1461 return -EINVAL;
1462 }
1463 }
1464
1465 /*
1466 * Hardware Register Values
1467 */
1468
1469 par->bplcon0 = BPC0_COLOR | bplpixmode[clk_shift];
1470 if (!IS_OCS)
1471 par->bplcon0 |= BPC0_ECSENA;
1472 if (par->bpp == 8)
1473 par->bplcon0 |= BPC0_BPU3;
1474 else
1475 par->bplcon0 |= par->bpp << 12;
1476 if (var->nonstd == FB_NONSTD_HAM)
1477 par->bplcon0 |= BPC0_HAM;
1478 if (var->sync & FB_SYNC_EXT)
1479 par->bplcon0 |= BPC0_ERSY;
1480
1481 if (IS_AGA)
1482 par->fmode = bplfetchmode[maxfmode];
1483
1484 switch (par->vmode & FB_VMODE_MASK) {
1485 case FB_VMODE_INTERLACED:
1486 par->bplcon0 |= BPC0_LACE;
1487 break;
1488 case FB_VMODE_DOUBLE:
1489 if (IS_AGA)
1490 par->fmode |= FMODE_SSCAN2 | FMODE_BSCAN2;
1491 break;
1492 }
1493
1494 if (!((par->vmode ^ var->vmode) & FB_VMODE_YWRAP)) {
1495 par->xoffset = var->xoffset;
1496 par->yoffset = var->yoffset;
1497 if (par->vmode & FB_VMODE_YWRAP) {
1498 if (par->xoffset || par->yoffset < 0 ||
1499 par->yoffset >= par->vyres)
1500 par->xoffset = par->yoffset = 0;
1501 } else {
1502 if (par->xoffset < 0 ||
1503 par->xoffset > upx(16 << maxfmode, par->vxres - par->xres) ||
1504 par->yoffset < 0 || par->yoffset > par->vyres - par->yres)
1505 par->xoffset = par->yoffset = 0;
1506 }
1507 } else
1508 par->xoffset = par->yoffset = 0;
1509
1510 par->crsr.crsr_x = par->crsr.crsr_y = 0;
1511 par->crsr.spot_x = par->crsr.spot_y = 0;
1512 par->crsr.height = par->crsr.width = 0;
1513
1514 return 0;
1515}
1516
1517 /*
1518 * Fill the `var' structure based on the values in `par' and maybe
1519 * other values read out of the hardware.
1520 */
1521
Linus Torvalds1da177e2005-04-16 15:20:36 -07001522static int ami_encode_var(struct fb_var_screeninfo *var,
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01001523 struct amifb_par *par)
1524{
1525 u_short clk_shift, line_shift;
1526
1527 memset(var, 0, sizeof(struct fb_var_screeninfo));
1528
1529 clk_shift = par->clk_shift;
1530 line_shift = par->line_shift;
1531
1532 var->xres = par->xres;
1533 var->yres = par->yres;
1534 var->xres_virtual = par->vxres;
1535 var->yres_virtual = par->vyres;
1536 var->xoffset = par->xoffset;
1537 var->yoffset = par->yoffset;
1538
1539 var->bits_per_pixel = par->bpp;
1540 var->grayscale = 0;
1541
1542 var->red.offset = 0;
1543 var->red.msb_right = 0;
1544 var->red.length = par->bpp;
1545 if (par->bplcon0 & BPC0_HAM)
1546 var->red.length -= 2;
1547 var->blue = var->green = var->red;
1548 var->transp.offset = 0;
1549 var->transp.length = 0;
1550 var->transp.msb_right = 0;
1551
1552 if (par->bplcon0 & BPC0_HAM)
1553 var->nonstd = FB_NONSTD_HAM;
1554 else
1555 var->nonstd = 0;
1556 var->activate = 0;
1557
1558 var->height = -1;
1559 var->width = -1;
1560
1561 var->pixclock = pixclock[clk_shift];
1562
1563 if (IS_AGA && par->fmode & FMODE_BSCAN2)
1564 var->vmode = FB_VMODE_DOUBLE;
1565 else if (par->bplcon0 & BPC0_LACE)
1566 var->vmode = FB_VMODE_INTERLACED;
1567 else
1568 var->vmode = FB_VMODE_NONINTERLACED;
1569
1570 if (!IS_OCS && par->beamcon0 & BMC0_VARBEAMEN) {
1571 var->hsync_len = (par->hsstop - par->hsstrt)>>clk_shift;
1572 var->right_margin = par->hsstrt>>clk_shift;
1573 var->left_margin = (par->htotal>>clk_shift) - var->xres - var->right_margin - var->hsync_len;
1574 var->vsync_len = (par->vsstop - par->vsstrt)>>line_shift;
1575 var->lower_margin = par->vsstrt>>line_shift;
1576 var->upper_margin = (par->vtotal>>line_shift) - var->yres - var->lower_margin - var->vsync_len;
1577 var->sync = 0;
1578 if (par->beamcon0 & BMC0_HSYTRUE)
1579 var->sync |= FB_SYNC_HOR_HIGH_ACT;
1580 if (par->beamcon0 & BMC0_VSYTRUE)
1581 var->sync |= FB_SYNC_VERT_HIGH_ACT;
1582 if (par->beamcon0 & BMC0_CSYTRUE)
1583 var->sync |= FB_SYNC_COMP_HIGH_ACT;
1584 } else {
1585 var->sync = FB_SYNC_BROADCAST;
1586 var->hsync_len = (152>>clk_shift) + mod4(par->diwstop_h);
1587 var->right_margin = ((par->htotal - down4(par->diwstop_h))>>clk_shift) + var->hsync_len;
1588 var->left_margin = (par->htotal>>clk_shift) - var->xres - var->right_margin - var->hsync_len;
1589 var->vsync_len = 4>>line_shift;
1590 var->lower_margin = ((par->vtotal - par->diwstop_v)>>line_shift) + var->vsync_len;
1591 var->upper_margin = (((par->vtotal - 2)>>line_shift) + 1) - var->yres -
1592 var->lower_margin - var->vsync_len;
1593 }
1594
1595 if (par->bplcon0 & BPC0_ERSY)
1596 var->sync |= FB_SYNC_EXT;
1597 if (par->vmode & FB_VMODE_YWRAP)
1598 var->vmode |= FB_VMODE_YWRAP;
1599
1600 return 0;
1601}
Linus Torvalds1da177e2005-04-16 15:20:36 -07001602
1603
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01001604 /*
1605 * Update hardware
1606 */
1607
1608static int ami_update_par(void)
1609{
1610 struct amifb_par *par = &currentpar;
1611 short clk_shift, vshift, fstrt, fsize, fstop, fconst, shift, move, mod;
1612
1613 clk_shift = par->clk_shift;
1614
1615 if (!(par->vmode & FB_VMODE_SMOOTH_XPAN))
1616 par->xoffset = upx(16 << maxfmode, par->xoffset);
1617
1618 fconst = 16 << maxfmode << clk_shift;
1619 vshift = modx(16 << maxfmode, par->xoffset);
1620 fstrt = par->diwstrt_h - (vshift << clk_shift) - 4;
1621 fsize = (par->xres + vshift) << clk_shift;
1622 shift = modx(fconst, fstrt);
1623 move = downx(2 << maxfmode, div8(par->xoffset));
1624 if (maxfmode + clk_shift > 1) {
1625 fstrt = downx(fconst, fstrt) - 64;
1626 fsize = upx(fconst, fsize);
1627 fstop = fstrt + fsize - fconst;
1628 } else {
1629 mod = fstrt = downx(fconst, fstrt) - fconst;
1630 fstop = fstrt + upx(fconst, fsize) - 64;
1631 fsize = up64(fsize);
1632 fstrt = fstop - fsize + 64;
1633 if (fstrt < min_fstrt) {
1634 fstop += min_fstrt - fstrt;
1635 fstrt = min_fstrt;
1636 }
1637 move = move - div8((mod - fstrt)>>clk_shift);
1638 }
1639 mod = par->next_line - div8(fsize>>clk_shift);
1640 par->ddfstrt = fstrt;
1641 par->ddfstop = fstop;
1642 par->bplcon1 = hscroll2hw(shift);
1643 par->bpl2mod = mod;
1644 if (par->bplcon0 & BPC0_LACE)
1645 par->bpl2mod += par->next_line;
1646 if (IS_AGA && (par->fmode & FMODE_BSCAN2))
1647 par->bpl1mod = -div8(fsize>>clk_shift);
1648 else
1649 par->bpl1mod = par->bpl2mod;
1650
1651 if (par->yoffset) {
1652 par->bplpt0 = fb_info.fix.smem_start +
1653 par->next_line * par->yoffset + move;
1654 if (par->vmode & FB_VMODE_YWRAP) {
1655 if (par->yoffset > par->vyres - par->yres) {
1656 par->bplpt0wrap = fb_info.fix.smem_start + move;
1657 if (par->bplcon0 & BPC0_LACE &&
1658 mod2(par->diwstrt_v + par->vyres -
1659 par->yoffset))
1660 par->bplpt0wrap += par->next_line;
1661 }
1662 }
1663 } else
1664 par->bplpt0 = fb_info.fix.smem_start + move;
1665
1666 if (par->bplcon0 & BPC0_LACE && mod2(par->diwstrt_v))
1667 par->bplpt0 += par->next_line;
1668
1669 return 0;
1670}
1671
1672
1673 /*
1674 * Pan or Wrap the Display
1675 *
1676 * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
1677 * in `var'.
1678 */
1679
1680static void ami_pan_var(struct fb_var_screeninfo *var)
1681{
1682 struct amifb_par *par = &currentpar;
1683
1684 par->xoffset = var->xoffset;
1685 par->yoffset = var->yoffset;
1686 if (var->vmode & FB_VMODE_YWRAP)
1687 par->vmode |= FB_VMODE_YWRAP;
1688 else
1689 par->vmode &= ~FB_VMODE_YWRAP;
1690
1691 do_vmode_pan = 0;
1692 ami_update_par();
1693 do_vmode_pan = 1;
1694}
1695
1696
1697static void ami_update_display(void)
1698{
1699 struct amifb_par *par = &currentpar;
1700
1701 custom.bplcon1 = par->bplcon1;
1702 custom.bpl1mod = par->bpl1mod;
1703 custom.bpl2mod = par->bpl2mod;
1704 custom.ddfstrt = ddfstrt2hw(par->ddfstrt);
1705 custom.ddfstop = ddfstop2hw(par->ddfstop);
1706}
1707
1708 /*
1709 * Change the video mode (called by VBlank interrupt)
1710 */
1711
1712static void ami_init_display(void)
1713{
1714 struct amifb_par *par = &currentpar;
1715 int i;
1716
1717 custom.bplcon0 = par->bplcon0 & ~BPC0_LACE;
1718 custom.bplcon2 = (IS_OCS ? 0 : BPC2_KILLEHB) | BPC2_PF2P2 | BPC2_PF1P2;
1719 if (!IS_OCS) {
1720 custom.bplcon3 = par->bplcon3;
1721 if (IS_AGA)
1722 custom.bplcon4 = BPC4_ESPRM4 | BPC4_OSPRM4;
1723 if (par->beamcon0 & BMC0_VARBEAMEN) {
1724 custom.htotal = htotal2hw(par->htotal);
1725 custom.hbstrt = hbstrt2hw(par->hbstrt);
1726 custom.hbstop = hbstop2hw(par->hbstop);
1727 custom.hsstrt = hsstrt2hw(par->hsstrt);
1728 custom.hsstop = hsstop2hw(par->hsstop);
1729 custom.hcenter = hcenter2hw(par->hcenter);
1730 custom.vtotal = vtotal2hw(par->vtotal);
1731 custom.vbstrt = vbstrt2hw(par->vbstrt);
1732 custom.vbstop = vbstop2hw(par->vbstop);
1733 custom.vsstrt = vsstrt2hw(par->vsstrt);
1734 custom.vsstop = vsstop2hw(par->vsstop);
1735 }
1736 }
1737 if (!IS_OCS || par->hsstop)
1738 custom.beamcon0 = par->beamcon0;
1739 if (IS_AGA)
1740 custom.fmode = par->fmode;
1741
1742 /*
1743 * The minimum period for audio depends on htotal
1744 */
1745
1746 amiga_audio_min_period = div16(par->htotal);
1747
1748 is_lace = par->bplcon0 & BPC0_LACE ? 1 : 0;
1749#if 1
1750 if (is_lace) {
1751 i = custom.vposr >> 15;
1752 } else {
1753 custom.vposw = custom.vposr | 0x8000;
1754 i = 1;
1755 }
1756#else
1757 i = 1;
1758 custom.vposw = custom.vposr | 0x8000;
1759#endif
1760 custom.cop2lc = (u_short *)ZTWO_PADDR(copdisplay.list[currentcop][i]);
1761}
1762
1763 /*
1764 * (Un)Blank the screen (called by VBlank interrupt)
1765 */
1766
1767static void ami_do_blank(void)
1768{
1769 struct amifb_par *par = &currentpar;
1770#if defined(CONFIG_FB_AMIGA_AGA)
1771 u_short bplcon3 = par->bplcon3;
1772#endif
1773 u_char red, green, blue;
1774
1775 if (do_blank > 0) {
1776 custom.dmacon = DMAF_RASTER | DMAF_SPRITE;
1777 red = green = blue = 0;
1778 if (!IS_OCS && do_blank > 1) {
1779 switch (do_blank) {
1780 case FB_BLANK_VSYNC_SUSPEND:
1781 custom.hsstrt = hsstrt2hw(par->hsstrt);
1782 custom.hsstop = hsstop2hw(par->hsstop);
1783 custom.vsstrt = vsstrt2hw(par->vtotal + 4);
1784 custom.vsstop = vsstop2hw(par->vtotal + 4);
1785 break;
1786 case FB_BLANK_HSYNC_SUSPEND:
1787 custom.hsstrt = hsstrt2hw(par->htotal + 16);
1788 custom.hsstop = hsstop2hw(par->htotal + 16);
1789 custom.vsstrt = vsstrt2hw(par->vsstrt);
1790 custom.vsstop = vsstrt2hw(par->vsstop);
1791 break;
1792 case FB_BLANK_POWERDOWN:
1793 custom.hsstrt = hsstrt2hw(par->htotal + 16);
1794 custom.hsstop = hsstop2hw(par->htotal + 16);
1795 custom.vsstrt = vsstrt2hw(par->vtotal + 4);
1796 custom.vsstop = vsstop2hw(par->vtotal + 4);
1797 break;
1798 }
1799 if (!(par->beamcon0 & BMC0_VARBEAMEN)) {
1800 custom.htotal = htotal2hw(par->htotal);
1801 custom.vtotal = vtotal2hw(par->vtotal);
1802 custom.beamcon0 = BMC0_HARDDIS | BMC0_VARBEAMEN |
1803 BMC0_VARVSYEN | BMC0_VARHSYEN | BMC0_VARCSYEN;
1804 }
1805 }
1806 } else {
1807 custom.dmacon = DMAF_SETCLR | DMAF_RASTER | DMAF_SPRITE;
1808 red = red0;
1809 green = green0;
1810 blue = blue0;
1811 if (!IS_OCS) {
1812 custom.hsstrt = hsstrt2hw(par->hsstrt);
1813 custom.hsstop = hsstop2hw(par->hsstop);
1814 custom.vsstrt = vsstrt2hw(par->vsstrt);
1815 custom.vsstop = vsstop2hw(par->vsstop);
1816 custom.beamcon0 = par->beamcon0;
1817 }
1818 }
1819#if defined(CONFIG_FB_AMIGA_AGA)
1820 if (IS_AGA) {
1821 custom.bplcon3 = bplcon3;
1822 custom.color[0] = rgb2hw8_high(red, green, blue);
1823 custom.bplcon3 = bplcon3 | BPC3_LOCT;
1824 custom.color[0] = rgb2hw8_low(red, green, blue);
1825 custom.bplcon3 = bplcon3;
1826 } else
1827#endif
1828#if defined(CONFIG_FB_AMIGA_ECS)
1829 if (par->bplcon0 & BPC0_SHRES) {
1830 u_short color, mask;
1831 int i;
1832
1833 mask = 0x3333;
1834 color = rgb2hw2(red, green, blue);
1835 for (i = 12; i >= 0; i -= 4)
1836 custom.color[i] = ecs_palette[i] = (ecs_palette[i] & mask) | color;
1837 mask <<= 2; color >>= 2;
1838 for (i = 3; i >= 0; i--)
1839 custom.color[i] = ecs_palette[i] = (ecs_palette[i] & mask) | color;
1840 } else
1841#endif
1842 custom.color[0] = rgb2hw4(red, green, blue);
1843 is_blanked = do_blank > 0 ? do_blank : 0;
1844}
1845
1846static int ami_get_fix_cursorinfo(struct fb_fix_cursorinfo *fix)
1847{
1848 struct amifb_par *par = &currentpar;
1849
1850 fix->crsr_width = fix->crsr_xsize = par->crsr.width;
1851 fix->crsr_height = fix->crsr_ysize = par->crsr.height;
1852 fix->crsr_color1 = 17;
1853 fix->crsr_color2 = 18;
1854 return 0;
1855}
1856
1857static int ami_get_var_cursorinfo(struct fb_var_cursorinfo *var, u_char __user *data)
1858{
1859 struct amifb_par *par = &currentpar;
1860 register u_short *lspr, *sspr;
1861#ifdef __mc68000__
1862 register u_long datawords asm ("d2");
1863#else
1864 register u_long datawords;
1865#endif
1866 register short delta;
1867 register u_char color;
1868 short height, width, bits, words;
1869 int size, alloc;
1870
1871 size = par->crsr.height * par->crsr.width;
1872 alloc = var->height * var->width;
1873 var->height = par->crsr.height;
1874 var->width = par->crsr.width;
1875 var->xspot = par->crsr.spot_x;
1876 var->yspot = par->crsr.spot_y;
1877 if (size > var->height * var->width)
1878 return -ENAMETOOLONG;
1879 if (!access_ok(VERIFY_WRITE, data, size))
1880 return -EFAULT;
1881 delta = 1 << par->crsr.fmode;
1882 lspr = lofsprite + (delta << 1);
1883 if (par->bplcon0 & BPC0_LACE)
1884 sspr = shfsprite + (delta << 1);
1885 else
1886 sspr = NULL;
1887 for (height = (short)var->height - 1; height >= 0; height--) {
1888 bits = 0; words = delta; datawords = 0;
1889 for (width = (short)var->width - 1; width >= 0; width--) {
1890 if (bits == 0) {
1891 bits = 16; --words;
1892#ifdef __mc68000__
1893 asm volatile ("movew %1@(%3:w:2),%0 ; swap %0 ; movew %1@+,%0"
1894 : "=d" (datawords), "=a" (lspr) : "1" (lspr), "d" (delta));
1895#else
1896 datawords = (*(lspr + delta) << 16) | (*lspr++);
1897#endif
1898 }
1899 --bits;
1900#ifdef __mc68000__
1901 asm volatile (
1902 "clrb %0 ; swap %1 ; lslw #1,%1 ; roxlb #1,%0 ; "
1903 "swap %1 ; lslw #1,%1 ; roxlb #1,%0"
1904 : "=d" (color), "=d" (datawords) : "1" (datawords));
1905#else
1906 color = (((datawords >> 30) & 2)
1907 | ((datawords >> 15) & 1));
1908 datawords <<= 1;
1909#endif
1910 put_user(color, data++);
1911 }
1912 if (bits > 0) {
1913 --words; ++lspr;
1914 }
1915 while (--words >= 0)
1916 ++lspr;
1917#ifdef __mc68000__
1918 asm volatile ("lea %0@(%4:w:2),%0 ; tstl %1 ; jeq 1f ; exg %0,%1\n1:"
1919 : "=a" (lspr), "=a" (sspr) : "0" (lspr), "1" (sspr), "d" (delta));
1920#else
1921 lspr += delta;
1922 if (sspr) {
1923 u_short *tmp = lspr;
1924 lspr = sspr;
1925 sspr = tmp;
1926 }
1927#endif
1928 }
1929 return 0;
1930}
1931
1932static int ami_set_var_cursorinfo(struct fb_var_cursorinfo *var, u_char __user *data)
1933{
1934 struct amifb_par *par = &currentpar;
1935 register u_short *lspr, *sspr;
1936#ifdef __mc68000__
1937 register u_long datawords asm ("d2");
1938#else
1939 register u_long datawords;
1940#endif
1941 register short delta;
1942 u_short fmode;
1943 short height, width, bits, words;
1944
1945 if (!var->width)
1946 return -EINVAL;
1947 else if (var->width <= 16)
1948 fmode = TAG_FMODE_1;
1949 else if (var->width <= 32)
1950 fmode = TAG_FMODE_2;
1951 else if (var->width <= 64)
1952 fmode = TAG_FMODE_4;
1953 else
1954 return -EINVAL;
1955 if (fmode > maxfmode)
1956 return -EINVAL;
1957 if (!var->height)
1958 return -EINVAL;
1959 if (!access_ok(VERIFY_READ, data, var->width * var->height))
1960 return -EFAULT;
1961 delta = 1 << fmode;
1962 lofsprite = shfsprite = (u_short *)spritememory;
1963 lspr = lofsprite + (delta << 1);
1964 if (par->bplcon0 & BPC0_LACE) {
1965 if (((var->height + 4) << fmode << 2) > SPRITEMEMSIZE)
1966 return -EINVAL;
1967 memset(lspr, 0, (var->height + 4) << fmode << 2);
1968 shfsprite += ((var->height + 5)&-2) << fmode;
1969 sspr = shfsprite + (delta << 1);
1970 } else {
1971 if (((var->height + 2) << fmode << 2) > SPRITEMEMSIZE)
1972 return -EINVAL;
1973 memset(lspr, 0, (var->height + 2) << fmode << 2);
1974 sspr = NULL;
1975 }
1976 for (height = (short)var->height - 1; height >= 0; height--) {
1977 bits = 16; words = delta; datawords = 0;
1978 for (width = (short)var->width - 1; width >= 0; width--) {
1979 unsigned long tdata = 0;
1980 get_user(tdata, data);
1981 data++;
1982#ifdef __mc68000__
1983 asm volatile (
1984 "lsrb #1,%2 ; roxlw #1,%0 ; swap %0 ; "
1985 "lsrb #1,%2 ; roxlw #1,%0 ; swap %0"
1986 : "=d" (datawords)
1987 : "0" (datawords), "d" (tdata));
1988#else
1989 datawords = ((datawords << 1) & 0xfffefffe);
1990 datawords |= tdata & 1;
1991 datawords |= (tdata & 2) << (16 - 1);
1992#endif
1993 if (--bits == 0) {
1994 bits = 16; --words;
1995#ifdef __mc68000__
1996 asm volatile ("swap %2 ; movew %2,%0@(%3:w:2) ; swap %2 ; movew %2,%0@+"
1997 : "=a" (lspr) : "0" (lspr), "d" (datawords), "d" (delta));
1998#else
1999 *(lspr + delta) = (u_short) (datawords >> 16);
2000 *lspr++ = (u_short) (datawords & 0xffff);
2001#endif
2002 }
2003 }
2004 if (bits < 16) {
2005 --words;
2006#ifdef __mc68000__
2007 asm volatile (
2008 "swap %2 ; lslw %4,%2 ; movew %2,%0@(%3:w:2) ; "
2009 "swap %2 ; lslw %4,%2 ; movew %2,%0@+"
2010 : "=a" (lspr) : "0" (lspr), "d" (datawords), "d" (delta), "d" (bits));
2011#else
2012 *(lspr + delta) = (u_short) (datawords >> (16 + bits));
2013 *lspr++ = (u_short) ((datawords & 0x0000ffff) >> bits);
2014#endif
2015 }
2016 while (--words >= 0) {
2017#ifdef __mc68000__
2018 asm volatile ("moveql #0,%%d0 ; movew %%d0,%0@(%2:w:2) ; movew %%d0,%0@+"
2019 : "=a" (lspr) : "0" (lspr), "d" (delta) : "d0");
2020#else
2021 *(lspr + delta) = 0;
2022 *lspr++ = 0;
2023#endif
2024 }
2025#ifdef __mc68000__
2026 asm volatile ("lea %0@(%4:w:2),%0 ; tstl %1 ; jeq 1f ; exg %0,%1\n1:"
2027 : "=a" (lspr), "=a" (sspr) : "0" (lspr), "1" (sspr), "d" (delta));
2028#else
2029 lspr += delta;
2030 if (sspr) {
2031 u_short *tmp = lspr;
2032 lspr = sspr;
2033 sspr = tmp;
2034 }
2035#endif
2036 }
2037 par->crsr.height = var->height;
2038 par->crsr.width = var->width;
2039 par->crsr.spot_x = var->xspot;
2040 par->crsr.spot_y = var->yspot;
2041 par->crsr.fmode = fmode;
2042 if (IS_AGA) {
2043 par->fmode &= ~(FMODE_SPAGEM | FMODE_SPR32);
2044 par->fmode |= sprfetchmode[fmode];
2045 custom.fmode = par->fmode;
2046 }
2047 return 0;
2048}
2049
2050static int ami_get_cursorstate(struct fb_cursorstate *state)
2051{
2052 struct amifb_par *par = &currentpar;
2053
2054 state->xoffset = par->crsr.crsr_x;
2055 state->yoffset = par->crsr.crsr_y;
2056 state->mode = cursormode;
2057 return 0;
2058}
2059
2060static int ami_set_cursorstate(struct fb_cursorstate *state)
2061{
2062 struct amifb_par *par = &currentpar;
2063
2064 par->crsr.crsr_x = state->xoffset;
2065 par->crsr.crsr_y = state->yoffset;
2066 if ((cursormode = state->mode) == FB_CURSOR_OFF)
2067 cursorstate = -1;
2068 do_cursor = 1;
2069 return 0;
2070}
2071
2072static void ami_set_sprite(void)
2073{
2074 struct amifb_par *par = &currentpar;
2075 copins *copl, *cops;
2076 u_short hs, vs, ve;
2077 u_long pl, ps, pt;
2078 short mx, my;
2079
2080 cops = copdisplay.list[currentcop][0];
2081 copl = copdisplay.list[currentcop][1];
2082 ps = pl = ZTWO_PADDR(dummysprite);
2083 mx = par->crsr.crsr_x - par->crsr.spot_x;
2084 my = par->crsr.crsr_y - par->crsr.spot_y;
2085 if (!(par->vmode & FB_VMODE_YWRAP)) {
2086 mx -= par->xoffset;
2087 my -= par->yoffset;
2088 }
2089 if (!is_blanked && cursorstate > 0 && par->crsr.height > 0 &&
2090 mx > -(short)par->crsr.width && mx < par->xres &&
2091 my > -(short)par->crsr.height && my < par->yres) {
2092 pl = ZTWO_PADDR(lofsprite);
2093 hs = par->diwstrt_h + (mx << par->clk_shift) - 4;
2094 vs = par->diwstrt_v + (my << par->line_shift);
2095 ve = vs + (par->crsr.height << par->line_shift);
2096 if (par->bplcon0 & BPC0_LACE) {
2097 ps = ZTWO_PADDR(shfsprite);
2098 lofsprite[0] = spr2hw_pos(vs, hs);
2099 shfsprite[0] = spr2hw_pos(vs + 1, hs);
2100 if (mod2(vs)) {
2101 lofsprite[1 << par->crsr.fmode] = spr2hw_ctl(vs, hs, ve);
2102 shfsprite[1 << par->crsr.fmode] = spr2hw_ctl(vs + 1, hs, ve + 1);
2103 pt = pl; pl = ps; ps = pt;
2104 } else {
2105 lofsprite[1 << par->crsr.fmode] = spr2hw_ctl(vs, hs, ve + 1);
2106 shfsprite[1 << par->crsr.fmode] = spr2hw_ctl(vs + 1, hs, ve);
2107 }
2108 } else {
2109 lofsprite[0] = spr2hw_pos(vs, hs) | (IS_AGA && (par->fmode & FMODE_BSCAN2) ? 0x80 : 0);
2110 lofsprite[1 << par->crsr.fmode] = spr2hw_ctl(vs, hs, ve);
2111 }
2112 }
2113 copl[cop_spr0ptrh].w[1] = highw(pl);
2114 copl[cop_spr0ptrl].w[1] = loww(pl);
2115 if (par->bplcon0 & BPC0_LACE) {
2116 cops[cop_spr0ptrh].w[1] = highw(ps);
2117 cops[cop_spr0ptrl].w[1] = loww(ps);
2118 }
2119}
2120
2121
2122 /*
2123 * Initialise the Copper Initialisation List
2124 */
2125
2126static void __init ami_init_copper(void)
2127{
2128 copins *cop = copdisplay.init;
2129 u_long p;
2130 int i;
2131
2132 if (!IS_OCS) {
2133 (cop++)->l = CMOVE(BPC0_COLOR | BPC0_SHRES | BPC0_ECSENA, bplcon0);
2134 (cop++)->l = CMOVE(0x0181, diwstrt);
2135 (cop++)->l = CMOVE(0x0281, diwstop);
2136 (cop++)->l = CMOVE(0x0000, diwhigh);
2137 } else
2138 (cop++)->l = CMOVE(BPC0_COLOR, bplcon0);
2139 p = ZTWO_PADDR(dummysprite);
2140 for (i = 0; i < 8; i++) {
2141 (cop++)->l = CMOVE(0, spr[i].pos);
2142 (cop++)->l = CMOVE(highw(p), sprpt[i]);
2143 (cop++)->l = CMOVE2(loww(p), sprpt[i]);
2144 }
2145
2146 (cop++)->l = CMOVE(IF_SETCLR | IF_COPER, intreq);
2147 copdisplay.wait = cop;
2148 (cop++)->l = CEND;
2149 (cop++)->l = CMOVE(0, copjmp2);
2150 cop->l = CEND;
2151
2152 custom.cop1lc = (u_short *)ZTWO_PADDR(copdisplay.init);
2153 custom.copjmp1 = 0;
2154}
2155
2156static void ami_reinit_copper(void)
2157{
2158 struct amifb_par *par = &currentpar;
2159
2160 copdisplay.init[cip_bplcon0].w[1] = ~(BPC0_BPU3 | BPC0_BPU2 | BPC0_BPU1 | BPC0_BPU0) & par->bplcon0;
2161 copdisplay.wait->l = CWAIT(32, par->diwstrt_v - 4);
2162}
2163
2164
2165 /*
2166 * Rebuild the Copper List
2167 *
2168 * We only change the things that are not static
2169 */
2170
2171static void ami_rebuild_copper(void)
2172{
2173 struct amifb_par *par = &currentpar;
2174 copins *copl, *cops;
2175 u_short line, h_end1, h_end2;
2176 short i;
2177 u_long p;
2178
2179 if (IS_AGA && maxfmode + par->clk_shift == 0)
2180 h_end1 = par->diwstrt_h - 64;
2181 else
2182 h_end1 = par->htotal - 32;
2183 h_end2 = par->ddfstop + 64;
2184
2185 ami_set_sprite();
2186
2187 copl = copdisplay.rebuild[1];
2188 p = par->bplpt0;
2189 if (par->vmode & FB_VMODE_YWRAP) {
2190 if ((par->vyres - par->yoffset) != 1 || !mod2(par->diwstrt_v)) {
2191 if (par->yoffset > par->vyres - par->yres) {
2192 for (i = 0; i < (short)par->bpp; i++, p += par->next_plane) {
2193 (copl++)->l = CMOVE(highw(p), bplpt[i]);
2194 (copl++)->l = CMOVE2(loww(p), bplpt[i]);
2195 }
2196 line = par->diwstrt_v + ((par->vyres - par->yoffset) << par->line_shift) - 1;
2197 while (line >= 512) {
2198 (copl++)->l = CWAIT(h_end1, 510);
2199 line -= 512;
2200 }
2201 if (line >= 510 && IS_AGA && maxfmode + par->clk_shift == 0)
2202 (copl++)->l = CWAIT(h_end1, line);
2203 else
2204 (copl++)->l = CWAIT(h_end2, line);
2205 p = par->bplpt0wrap;
2206 }
2207 } else
2208 p = par->bplpt0wrap;
2209 }
2210 for (i = 0; i < (short)par->bpp; i++, p += par->next_plane) {
2211 (copl++)->l = CMOVE(highw(p), bplpt[i]);
2212 (copl++)->l = CMOVE2(loww(p), bplpt[i]);
2213 }
2214 copl->l = CEND;
2215
2216 if (par->bplcon0 & BPC0_LACE) {
2217 cops = copdisplay.rebuild[0];
2218 p = par->bplpt0;
2219 if (mod2(par->diwstrt_v))
2220 p -= par->next_line;
2221 else
2222 p += par->next_line;
2223 if (par->vmode & FB_VMODE_YWRAP) {
2224 if ((par->vyres - par->yoffset) != 1 || mod2(par->diwstrt_v)) {
2225 if (par->yoffset > par->vyres - par->yres + 1) {
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 line = par->diwstrt_v + ((par->vyres - par->yoffset) << par->line_shift) - 2;
2231 while (line >= 512) {
2232 (cops++)->l = CWAIT(h_end1, 510);
2233 line -= 512;
2234 }
2235 if (line > 510 && IS_AGA && maxfmode + par->clk_shift == 0)
2236 (cops++)->l = CWAIT(h_end1, line);
2237 else
2238 (cops++)->l = CWAIT(h_end2, line);
2239 p = par->bplpt0wrap;
2240 if (mod2(par->diwstrt_v + par->vyres -
2241 par->yoffset))
2242 p -= par->next_line;
2243 else
2244 p += par->next_line;
2245 }
2246 } else
2247 p = par->bplpt0wrap - par->next_line;
2248 }
2249 for (i = 0; i < (short)par->bpp; i++, p += par->next_plane) {
2250 (cops++)->l = CMOVE(highw(p), bplpt[i]);
2251 (cops++)->l = CMOVE2(loww(p), bplpt[i]);
2252 }
2253 cops->l = CEND;
2254 }
2255}
2256
2257
2258 /*
2259 * Build the Copper List
2260 */
2261
2262static void ami_build_copper(void)
2263{
2264 struct amifb_par *par = &currentpar;
2265 copins *copl, *cops;
2266 u_long p;
2267
2268 currentcop = 1 - currentcop;
2269
2270 copl = copdisplay.list[currentcop][1];
2271
2272 (copl++)->l = CWAIT(0, 10);
2273 (copl++)->l = CMOVE(par->bplcon0, bplcon0);
2274 (copl++)->l = CMOVE(0, sprpt[0]);
2275 (copl++)->l = CMOVE2(0, sprpt[0]);
2276
2277 if (par->bplcon0 & BPC0_LACE) {
2278 cops = copdisplay.list[currentcop][0];
2279
2280 (cops++)->l = CWAIT(0, 10);
2281 (cops++)->l = CMOVE(par->bplcon0, bplcon0);
2282 (cops++)->l = CMOVE(0, sprpt[0]);
2283 (cops++)->l = CMOVE2(0, sprpt[0]);
2284
2285 (copl++)->l = CMOVE(diwstrt2hw(par->diwstrt_h, par->diwstrt_v + 1), diwstrt);
2286 (copl++)->l = CMOVE(diwstop2hw(par->diwstop_h, par->diwstop_v + 1), diwstop);
2287 (cops++)->l = CMOVE(diwstrt2hw(par->diwstrt_h, par->diwstrt_v), diwstrt);
2288 (cops++)->l = CMOVE(diwstop2hw(par->diwstop_h, par->diwstop_v), diwstop);
2289 if (!IS_OCS) {
2290 (copl++)->l = CMOVE(diwhigh2hw(par->diwstrt_h, par->diwstrt_v + 1,
2291 par->diwstop_h, par->diwstop_v + 1), diwhigh);
2292 (cops++)->l = CMOVE(diwhigh2hw(par->diwstrt_h, par->diwstrt_v,
2293 par->diwstop_h, par->diwstop_v), diwhigh);
2294#if 0
2295 if (par->beamcon0 & BMC0_VARBEAMEN) {
2296 (copl++)->l = CMOVE(vtotal2hw(par->vtotal), vtotal);
2297 (copl++)->l = CMOVE(vbstrt2hw(par->vbstrt + 1), vbstrt);
2298 (copl++)->l = CMOVE(vbstop2hw(par->vbstop + 1), vbstop);
2299 (cops++)->l = CMOVE(vtotal2hw(par->vtotal), vtotal);
2300 (cops++)->l = CMOVE(vbstrt2hw(par->vbstrt), vbstrt);
2301 (cops++)->l = CMOVE(vbstop2hw(par->vbstop), vbstop);
2302 }
2303#endif
2304 }
2305 p = ZTWO_PADDR(copdisplay.list[currentcop][0]);
2306 (copl++)->l = CMOVE(highw(p), cop2lc);
2307 (copl++)->l = CMOVE2(loww(p), cop2lc);
2308 p = ZTWO_PADDR(copdisplay.list[currentcop][1]);
2309 (cops++)->l = CMOVE(highw(p), cop2lc);
2310 (cops++)->l = CMOVE2(loww(p), cop2lc);
2311 copdisplay.rebuild[0] = cops;
2312 } else {
2313 (copl++)->l = CMOVE(diwstrt2hw(par->diwstrt_h, par->diwstrt_v), diwstrt);
2314 (copl++)->l = CMOVE(diwstop2hw(par->diwstop_h, par->diwstop_v), diwstop);
2315 if (!IS_OCS) {
2316 (copl++)->l = CMOVE(diwhigh2hw(par->diwstrt_h, par->diwstrt_v,
2317 par->diwstop_h, par->diwstop_v), diwhigh);
2318#if 0
2319 if (par->beamcon0 & BMC0_VARBEAMEN) {
2320 (copl++)->l = CMOVE(vtotal2hw(par->vtotal), vtotal);
2321 (copl++)->l = CMOVE(vbstrt2hw(par->vbstrt), vbstrt);
2322 (copl++)->l = CMOVE(vbstop2hw(par->vbstop), vbstop);
2323 }
2324#endif
2325 }
2326 }
2327 copdisplay.rebuild[1] = copl;
2328
2329 ami_update_par();
2330 ami_rebuild_copper();
2331}
2332
Linus Torvalds1da177e2005-04-16 15:20:36 -07002333
2334static void __init amifb_setup_mcap(char *spec)
2335{
2336 char *p;
2337 int vmin, vmax, hmin, hmax;
2338
2339 /* Format for monitor capabilities is: <Vmin>;<Vmax>;<Hmin>;<Hmax>
2340 * <V*> vertical freq. in Hz
2341 * <H*> horizontal freq. in kHz
2342 */
2343
2344 if (!(p = strsep(&spec, ";")) || !*p)
2345 return;
2346 vmin = simple_strtoul(p, NULL, 10);
2347 if (vmin <= 0)
2348 return;
2349 if (!(p = strsep(&spec, ";")) || !*p)
2350 return;
2351 vmax = simple_strtoul(p, NULL, 10);
2352 if (vmax <= 0 || vmax <= vmin)
2353 return;
2354 if (!(p = strsep(&spec, ";")) || !*p)
2355 return;
2356 hmin = 1000 * simple_strtoul(p, NULL, 10);
2357 if (hmin <= 0)
2358 return;
2359 if (!(p = strsep(&spec, "")) || !*p)
2360 return;
2361 hmax = 1000 * simple_strtoul(p, NULL, 10);
2362 if (hmax <= 0 || hmax <= hmin)
2363 return;
2364
Geert Uytterhoeven03c740a2011-11-21 21:53:57 +01002365 amifb_hfmin = hmin;
2366 amifb_hfmax = hmax;
2367 amifb_vfmin = vmin;
2368 amifb_vfmax = vmax;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002369}
2370
Geert Uytterhoeven78ffd702011-11-21 21:53:55 +01002371static int __init amifb_setup(char *options)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002372{
2373 char *this_opt;
2374
2375 if (!options || !*options)
2376 return 0;
2377
2378 while ((this_opt = strsep(&options, ",")) != NULL) {
2379 if (!*this_opt)
2380 continue;
2381 if (!strcmp(this_opt, "inverse")) {
2382 amifb_inverse = 1;
2383 fb_invert_cmaps();
2384 } else if (!strcmp(this_opt, "ilbm"))
2385 amifb_ilbm = 1;
2386 else if (!strncmp(this_opt, "monitorcap:", 11))
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002387 amifb_setup_mcap(this_opt + 11);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002388 else if (!strncmp(this_opt, "fstart:", 7))
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002389 min_fstrt = simple_strtoul(this_opt + 7, NULL, 0);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002390 else
2391 mode_option = this_opt;
2392 }
2393
2394 if (min_fstrt < 48)
2395 min_fstrt = 48;
2396
2397 return 0;
2398}
2399
2400
2401static int amifb_check_var(struct fb_var_screeninfo *var,
2402 struct fb_info *info)
2403{
2404 int err;
2405 struct amifb_par par;
2406
2407 /* Validate wanted screen parameters */
2408 if ((err = ami_decode_var(var, &par)))
2409 return err;
2410
2411 /* Encode (possibly rounded) screen parameters */
2412 ami_encode_var(var, &par);
2413 return 0;
2414}
2415
2416
2417static int amifb_set_par(struct fb_info *info)
2418{
Geert Uytterhoeven423a5302011-11-21 21:53:56 +01002419 struct amifb_par *par = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002420
2421 do_vmode_pan = 0;
2422 do_vmode_full = 0;
2423
2424 /* Decode wanted screen parameters */
2425 ami_decode_var(&info->var, par);
2426
2427 /* Set new videomode */
2428 ami_build_copper();
2429
2430 /* Set VBlank trigger */
2431 do_vmode_full = 1;
2432
2433 /* Update fix for new screen parameters */
2434 if (par->bpp == 1) {
2435 info->fix.type = FB_TYPE_PACKED_PIXELS;
2436 info->fix.type_aux = 0;
2437 } else if (amifb_ilbm) {
2438 info->fix.type = FB_TYPE_INTERLEAVED_PLANES;
2439 info->fix.type_aux = par->next_line;
2440 } else {
2441 info->fix.type = FB_TYPE_PLANES;
2442 info->fix.type_aux = 0;
2443 }
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002444 info->fix.line_length = div8(upx(16 << maxfmode, par->vxres));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002445
2446 if (par->vmode & FB_VMODE_YWRAP) {
2447 info->fix.ywrapstep = 1;
2448 info->fix.xpanstep = 0;
2449 info->fix.ypanstep = 0;
2450 info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YWRAP |
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002451 FBINFO_READS_FAST; /* override SCROLL_REDRAW */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002452 } else {
2453 info->fix.ywrapstep = 0;
2454 if (par->vmode & FB_VMODE_SMOOTH_XPAN)
2455 info->fix.xpanstep = 1;
2456 else
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002457 info->fix.xpanstep = 16 << maxfmode;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002458 info->fix.ypanstep = 1;
2459 info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN;
2460 }
2461 return 0;
2462}
2463
2464
2465 /*
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01002466 * Set a single color register. The values supplied are already
2467 * rounded down to the hardware's capabilities (according to the
2468 * entries in the var structure). Return != 0 for invalid regno.
2469 */
2470
2471static int amifb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
2472 u_int transp, struct fb_info *info)
2473{
2474 if (IS_AGA) {
2475 if (regno > 255)
2476 return 1;
2477 } else if (currentpar.bplcon0 & BPC0_SHRES) {
2478 if (regno > 3)
2479 return 1;
2480 } else {
2481 if (regno > 31)
2482 return 1;
2483 }
2484 red >>= 8;
2485 green >>= 8;
2486 blue >>= 8;
2487 if (!regno) {
2488 red0 = red;
2489 green0 = green;
2490 blue0 = blue;
2491 }
2492
2493 /*
2494 * Update the corresponding Hardware Color Register, unless it's Color
2495 * Register 0 and the screen is blanked.
2496 *
2497 * VBlank is switched off to protect bplcon3 or ecs_palette[] from
2498 * being changed by ami_do_blank() during the VBlank.
2499 */
2500
2501 if (regno || !is_blanked) {
2502#if defined(CONFIG_FB_AMIGA_AGA)
2503 if (IS_AGA) {
2504 u_short bplcon3 = currentpar.bplcon3;
2505 VBlankOff();
2506 custom.bplcon3 = bplcon3 | (regno << 8 & 0xe000);
2507 custom.color[regno & 31] = rgb2hw8_high(red, green,
2508 blue);
2509 custom.bplcon3 = bplcon3 | (regno << 8 & 0xe000) |
2510 BPC3_LOCT;
2511 custom.color[regno & 31] = rgb2hw8_low(red, green,
2512 blue);
2513 custom.bplcon3 = bplcon3;
2514 VBlankOn();
2515 } else
2516#endif
2517#if defined(CONFIG_FB_AMIGA_ECS)
2518 if (currentpar.bplcon0 & BPC0_SHRES) {
2519 u_short color, mask;
2520 int i;
2521
2522 mask = 0x3333;
2523 color = rgb2hw2(red, green, blue);
2524 VBlankOff();
2525 for (i = regno + 12; i >= (int)regno; i -= 4)
2526 custom.color[i] = ecs_palette[i] = (ecs_palette[i] & mask) | color;
2527 mask <<= 2; color >>= 2;
2528 regno = down16(regno) + mul4(mod4(regno));
2529 for (i = regno + 3; i >= (int)regno; i--)
2530 custom.color[i] = ecs_palette[i] = (ecs_palette[i] & mask) | color;
2531 VBlankOn();
2532 } else
2533#endif
2534 custom.color[regno] = rgb2hw4(red, green, blue);
2535 }
2536 return 0;
2537}
2538
2539
2540 /*
2541 * Blank the display.
2542 */
2543
2544static int amifb_blank(int blank, struct fb_info *info)
2545{
2546 do_blank = blank ? blank : -1;
2547
2548 return 0;
2549}
2550
2551
2552 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -07002553 * Pan or Wrap the Display
2554 *
2555 * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
2556 */
2557
2558static int amifb_pan_display(struct fb_var_screeninfo *var,
2559 struct fb_info *info)
2560{
2561 if (var->vmode & FB_VMODE_YWRAP) {
2562 if (var->yoffset < 0 ||
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002563 var->yoffset >= info->var.yres_virtual || var->xoffset)
2564 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002565 } else {
2566 /*
2567 * TODO: There will be problems when xpan!=1, so some columns
2568 * on the right side will never be seen
2569 */
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002570 if (var->xoffset + info->var.xres >
2571 upx(16 << maxfmode, info->var.xres_virtual) ||
2572 var->yoffset + info->var.yres > info->var.yres_virtual)
Linus Torvalds1da177e2005-04-16 15:20:36 -07002573 return -EINVAL;
2574 }
2575 ami_pan_var(var);
2576 info->var.xoffset = var->xoffset;
2577 info->var.yoffset = var->yoffset;
2578 if (var->vmode & FB_VMODE_YWRAP)
2579 info->var.vmode |= FB_VMODE_YWRAP;
2580 else
2581 info->var.vmode &= ~FB_VMODE_YWRAP;
2582 return 0;
2583}
2584
2585
2586#if BITS_PER_LONG == 32
2587#define BYTES_PER_LONG 4
2588#define SHIFT_PER_LONG 5
2589#elif BITS_PER_LONG == 64
2590#define BYTES_PER_LONG 8
2591#define SHIFT_PER_LONG 6
2592#else
2593#define Please update me
2594#endif
2595
2596
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002597 /*
2598 * Compose two values, using a bitmask as decision value
2599 * This is equivalent to (a & mask) | (b & ~mask)
2600 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002601
2602static inline unsigned long comp(unsigned long a, unsigned long b,
2603 unsigned long mask)
2604{
2605 return ((a ^ b) & mask) ^ b;
2606}
2607
2608
2609static inline unsigned long xor(unsigned long a, unsigned long b,
2610 unsigned long mask)
2611{
2612 return (a & mask) ^ b;
2613}
2614
2615
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002616 /*
2617 * Unaligned forward bit copy using 32-bit or 64-bit memory accesses
2618 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002619
2620static void bitcpy(unsigned long *dst, int dst_idx, const unsigned long *src,
2621 int src_idx, u32 n)
2622{
2623 unsigned long first, last;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002624 int shift = dst_idx - src_idx, left, right;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002625 unsigned long d0, d1;
2626 int m;
2627
2628 if (!n)
2629 return;
2630
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002631 shift = dst_idx - src_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002632 first = ~0UL >> dst_idx;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002633 last = ~(~0UL >> ((dst_idx + n) % BITS_PER_LONG));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002634
2635 if (!shift) {
2636 // Same alignment for source and dest
2637
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002638 if (dst_idx + n <= BITS_PER_LONG) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002639 // Single word
2640 if (last)
2641 first &= last;
2642 *dst = comp(*src, *dst, first);
2643 } else {
2644 // Multiple destination words
2645 // Leading bits
2646 if (first) {
2647 *dst = comp(*src, *dst, first);
2648 dst++;
2649 src++;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002650 n -= BITS_PER_LONG - dst_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002651 }
2652
2653 // Main chunk
2654 n /= BITS_PER_LONG;
2655 while (n >= 8) {
2656 *dst++ = *src++;
2657 *dst++ = *src++;
2658 *dst++ = *src++;
2659 *dst++ = *src++;
2660 *dst++ = *src++;
2661 *dst++ = *src++;
2662 *dst++ = *src++;
2663 *dst++ = *src++;
2664 n -= 8;
2665 }
2666 while (n--)
2667 *dst++ = *src++;
2668
2669 // Trailing bits
2670 if (last)
2671 *dst = comp(*src, *dst, last);
2672 }
2673 } else {
2674 // Different alignment for source and dest
2675
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002676 right = shift & (BITS_PER_LONG - 1);
2677 left = -shift & (BITS_PER_LONG - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002678
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002679 if (dst_idx + n <= BITS_PER_LONG) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002680 // Single destination word
2681 if (last)
2682 first &= last;
2683 if (shift > 0) {
2684 // Single source word
2685 *dst = comp(*src >> right, *dst, first);
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002686 } else if (src_idx + n <= BITS_PER_LONG) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002687 // Single source word
2688 *dst = comp(*src << left, *dst, first);
2689 } else {
2690 // 2 source words
2691 d0 = *src++;
2692 d1 = *src;
2693 *dst = comp(d0 << left | d1 >> right, *dst,
2694 first);
2695 }
2696 } else {
2697 // Multiple destination words
2698 d0 = *src++;
2699 // Leading bits
2700 if (shift > 0) {
2701 // Single source word
2702 *dst = comp(d0 >> right, *dst, first);
2703 dst++;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002704 n -= BITS_PER_LONG - dst_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002705 } else {
2706 // 2 source words
2707 d1 = *src++;
2708 *dst = comp(d0 << left | d1 >> right, *dst,
2709 first);
2710 d0 = d1;
2711 dst++;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002712 n -= BITS_PER_LONG - dst_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002713 }
2714
2715 // Main chunk
2716 m = n % BITS_PER_LONG;
2717 n /= BITS_PER_LONG;
2718 while (n >= 4) {
2719 d1 = *src++;
2720 *dst++ = d0 << left | d1 >> right;
2721 d0 = d1;
2722 d1 = *src++;
2723 *dst++ = d0 << left | d1 >> right;
2724 d0 = d1;
2725 d1 = *src++;
2726 *dst++ = d0 << left | d1 >> right;
2727 d0 = d1;
2728 d1 = *src++;
2729 *dst++ = d0 << left | d1 >> right;
2730 d0 = d1;
2731 n -= 4;
2732 }
2733 while (n--) {
2734 d1 = *src++;
2735 *dst++ = d0 << left | d1 >> right;
2736 d0 = d1;
2737 }
2738
2739 // Trailing bits
2740 if (last) {
2741 if (m <= right) {
2742 // Single source word
2743 *dst = comp(d0 << left, *dst, last);
2744 } else {
2745 // 2 source words
2746 d1 = *src;
2747 *dst = comp(d0 << left | d1 >> right,
2748 *dst, last);
2749 }
2750 }
2751 }
2752 }
2753}
2754
2755
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002756 /*
2757 * Unaligned reverse bit copy using 32-bit or 64-bit memory accesses
2758 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002759
2760static void bitcpy_rev(unsigned long *dst, int dst_idx,
2761 const unsigned long *src, int src_idx, u32 n)
2762{
2763 unsigned long first, last;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002764 int shift = dst_idx - src_idx, left, right;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002765 unsigned long d0, d1;
2766 int m;
2767
2768 if (!n)
2769 return;
2770
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002771 dst += (n - 1) / BITS_PER_LONG;
2772 src += (n - 1) / BITS_PER_LONG;
2773 if ((n - 1) % BITS_PER_LONG) {
2774 dst_idx += (n - 1) % BITS_PER_LONG;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002775 dst += dst_idx >> SHIFT_PER_LONG;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002776 dst_idx &= BITS_PER_LONG - 1;
2777 src_idx += (n - 1) % BITS_PER_LONG;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002778 src += src_idx >> SHIFT_PER_LONG;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002779 src_idx &= BITS_PER_LONG - 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002780 }
2781
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002782 shift = dst_idx - src_idx;
2783 first = ~0UL << (BITS_PER_LONG - 1 - dst_idx);
2784 last = ~(~0UL << (BITS_PER_LONG - 1 - ((dst_idx - n) % BITS_PER_LONG)));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002785
2786 if (!shift) {
2787 // Same alignment for source and dest
2788
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002789 if ((unsigned long)dst_idx + 1 >= n) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002790 // Single word
2791 if (last)
2792 first &= last;
2793 *dst = comp(*src, *dst, first);
2794 } else {
2795 // Multiple destination words
2796 // Leading bits
2797 if (first) {
2798 *dst = comp(*src, *dst, first);
2799 dst--;
2800 src--;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002801 n -= dst_idx + 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002802 }
2803
2804 // Main chunk
2805 n /= BITS_PER_LONG;
2806 while (n >= 8) {
2807 *dst-- = *src--;
2808 *dst-- = *src--;
2809 *dst-- = *src--;
2810 *dst-- = *src--;
2811 *dst-- = *src--;
2812 *dst-- = *src--;
2813 *dst-- = *src--;
2814 *dst-- = *src--;
2815 n -= 8;
2816 }
2817 while (n--)
2818 *dst-- = *src--;
2819
2820 // Trailing bits
2821 if (last)
2822 *dst = comp(*src, *dst, last);
2823 }
2824 } else {
2825 // Different alignment for source and dest
2826
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002827 right = shift & (BITS_PER_LONG - 1);
2828 left = -shift & (BITS_PER_LONG - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002829
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002830 if ((unsigned long)dst_idx + 1 >= n) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002831 // Single destination word
2832 if (last)
2833 first &= last;
2834 if (shift < 0) {
2835 // Single source word
2836 *dst = comp(*src << left, *dst, first);
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002837 } else if (1 + (unsigned long)src_idx >= n) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002838 // Single source word
2839 *dst = comp(*src >> right, *dst, first);
2840 } else {
2841 // 2 source words
2842 d0 = *src--;
2843 d1 = *src;
2844 *dst = comp(d0 >> right | d1 << left, *dst,
2845 first);
2846 }
2847 } else {
2848 // Multiple destination words
2849 d0 = *src--;
2850 // Leading bits
2851 if (shift < 0) {
2852 // Single source word
2853 *dst = comp(d0 << left, *dst, first);
2854 dst--;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002855 n -= dst_idx + 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002856 } else {
2857 // 2 source words
2858 d1 = *src--;
2859 *dst = comp(d0 >> right | d1 << left, *dst,
2860 first);
2861 d0 = d1;
2862 dst--;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002863 n -= dst_idx + 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002864 }
2865
2866 // Main chunk
2867 m = n % BITS_PER_LONG;
2868 n /= BITS_PER_LONG;
2869 while (n >= 4) {
2870 d1 = *src--;
2871 *dst-- = d0 >> right | d1 << left;
2872 d0 = d1;
2873 d1 = *src--;
2874 *dst-- = d0 >> right | d1 << left;
2875 d0 = d1;
2876 d1 = *src--;
2877 *dst-- = d0 >> right | d1 << left;
2878 d0 = d1;
2879 d1 = *src--;
2880 *dst-- = d0 >> right | d1 << left;
2881 d0 = d1;
2882 n -= 4;
2883 }
2884 while (n--) {
2885 d1 = *src--;
2886 *dst-- = d0 >> right | d1 << left;
2887 d0 = d1;
2888 }
2889
2890 // Trailing bits
2891 if (last) {
2892 if (m <= left) {
2893 // Single source word
2894 *dst = comp(d0 >> right, *dst, last);
2895 } else {
2896 // 2 source words
2897 d1 = *src;
2898 *dst = comp(d0 >> right | d1 << left,
2899 *dst, last);
2900 }
2901 }
2902 }
2903 }
2904}
2905
2906
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002907 /*
2908 * Unaligned forward inverting bit copy using 32-bit or 64-bit memory
2909 * accesses
2910 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07002911
2912static void bitcpy_not(unsigned long *dst, int dst_idx,
2913 const unsigned long *src, int src_idx, u32 n)
2914{
2915 unsigned long first, last;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002916 int shift = dst_idx - src_idx, left, right;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002917 unsigned long d0, d1;
2918 int m;
2919
2920 if (!n)
2921 return;
2922
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002923 shift = dst_idx - src_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002924 first = ~0UL >> dst_idx;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002925 last = ~(~0UL >> ((dst_idx + n) % BITS_PER_LONG));
Linus Torvalds1da177e2005-04-16 15:20:36 -07002926
2927 if (!shift) {
2928 // Same alignment for source and dest
2929
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002930 if (dst_idx + n <= BITS_PER_LONG) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002931 // Single word
2932 if (last)
2933 first &= last;
2934 *dst = comp(~*src, *dst, first);
2935 } else {
2936 // Multiple destination words
2937 // Leading bits
2938 if (first) {
2939 *dst = comp(~*src, *dst, first);
2940 dst++;
2941 src++;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002942 n -= BITS_PER_LONG - dst_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002943 }
2944
2945 // Main chunk
2946 n /= BITS_PER_LONG;
2947 while (n >= 8) {
2948 *dst++ = ~*src++;
2949 *dst++ = ~*src++;
2950 *dst++ = ~*src++;
2951 *dst++ = ~*src++;
2952 *dst++ = ~*src++;
2953 *dst++ = ~*src++;
2954 *dst++ = ~*src++;
2955 *dst++ = ~*src++;
2956 n -= 8;
2957 }
2958 while (n--)
2959 *dst++ = ~*src++;
2960
2961 // Trailing bits
2962 if (last)
2963 *dst = comp(~*src, *dst, last);
2964 }
2965 } else {
2966 // Different alignment for source and dest
2967
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002968 right = shift & (BITS_PER_LONG - 1);
2969 left = -shift & (BITS_PER_LONG - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07002970
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002971 if (dst_idx + n <= BITS_PER_LONG) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002972 // Single destination word
2973 if (last)
2974 first &= last;
2975 if (shift > 0) {
2976 // Single source word
2977 *dst = comp(~*src >> right, *dst, first);
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002978 } else if (src_idx + n <= BITS_PER_LONG) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07002979 // Single source word
2980 *dst = comp(~*src << left, *dst, first);
2981 } else {
2982 // 2 source words
2983 d0 = ~*src++;
2984 d1 = ~*src;
2985 *dst = comp(d0 << left | d1 >> right, *dst,
2986 first);
2987 }
2988 } else {
2989 // Multiple destination words
2990 d0 = ~*src++;
2991 // Leading bits
2992 if (shift > 0) {
2993 // Single source word
2994 *dst = comp(d0 >> right, *dst, first);
2995 dst++;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01002996 n -= BITS_PER_LONG - dst_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07002997 } else {
2998 // 2 source words
2999 d1 = ~*src++;
3000 *dst = comp(d0 << left | d1 >> right, *dst,
3001 first);
3002 d0 = d1;
3003 dst++;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003004 n -= BITS_PER_LONG - dst_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003005 }
3006
3007 // Main chunk
3008 m = n % BITS_PER_LONG;
3009 n /= BITS_PER_LONG;
3010 while (n >= 4) {
3011 d1 = ~*src++;
3012 *dst++ = d0 << left | d1 >> right;
3013 d0 = d1;
3014 d1 = ~*src++;
3015 *dst++ = d0 << left | d1 >> right;
3016 d0 = d1;
3017 d1 = ~*src++;
3018 *dst++ = d0 << left | d1 >> right;
3019 d0 = d1;
3020 d1 = ~*src++;
3021 *dst++ = d0 << left | d1 >> right;
3022 d0 = d1;
3023 n -= 4;
3024 }
3025 while (n--) {
3026 d1 = ~*src++;
3027 *dst++ = d0 << left | d1 >> right;
3028 d0 = d1;
3029 }
3030
3031 // Trailing bits
3032 if (last) {
3033 if (m <= right) {
3034 // Single source word
3035 *dst = comp(d0 << left, *dst, last);
3036 } else {
3037 // 2 source words
3038 d1 = ~*src;
3039 *dst = comp(d0 << left | d1 >> right,
3040 *dst, last);
3041 }
3042 }
3043 }
3044 }
3045}
3046
3047
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003048 /*
3049 * Unaligned 32-bit pattern fill using 32/64-bit memory accesses
3050 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003051
3052static void bitfill32(unsigned long *dst, int dst_idx, u32 pat, u32 n)
3053{
3054 unsigned long val = pat;
3055 unsigned long first, last;
3056
3057 if (!n)
3058 return;
3059
3060#if BITS_PER_LONG == 64
3061 val |= val << 32;
3062#endif
3063
3064 first = ~0UL >> dst_idx;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003065 last = ~(~0UL >> ((dst_idx + n) % BITS_PER_LONG));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003066
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003067 if (dst_idx + n <= BITS_PER_LONG) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003068 // Single word
3069 if (last)
3070 first &= last;
3071 *dst = comp(val, *dst, first);
3072 } else {
3073 // Multiple destination words
3074 // Leading bits
3075 if (first) {
3076 *dst = comp(val, *dst, first);
3077 dst++;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003078 n -= BITS_PER_LONG - dst_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003079 }
3080
3081 // Main chunk
3082 n /= BITS_PER_LONG;
3083 while (n >= 8) {
3084 *dst++ = val;
3085 *dst++ = val;
3086 *dst++ = val;
3087 *dst++ = val;
3088 *dst++ = val;
3089 *dst++ = val;
3090 *dst++ = val;
3091 *dst++ = val;
3092 n -= 8;
3093 }
3094 while (n--)
3095 *dst++ = val;
3096
3097 // Trailing bits
3098 if (last)
3099 *dst = comp(val, *dst, last);
3100 }
3101}
3102
3103
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003104 /*
3105 * Unaligned 32-bit pattern xor using 32/64-bit memory accesses
3106 */
Linus Torvalds1da177e2005-04-16 15:20:36 -07003107
3108static void bitxor32(unsigned long *dst, int dst_idx, u32 pat, u32 n)
3109{
3110 unsigned long val = pat;
3111 unsigned long first, last;
3112
3113 if (!n)
3114 return;
3115
3116#if BITS_PER_LONG == 64
3117 val |= val << 32;
3118#endif
3119
3120 first = ~0UL >> dst_idx;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003121 last = ~(~0UL >> ((dst_idx + n) % BITS_PER_LONG));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003122
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003123 if (dst_idx + n <= BITS_PER_LONG) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003124 // Single word
3125 if (last)
3126 first &= last;
3127 *dst = xor(val, *dst, first);
3128 } else {
3129 // Multiple destination words
3130 // Leading bits
3131 if (first) {
3132 *dst = xor(val, *dst, first);
3133 dst++;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003134 n -= BITS_PER_LONG - dst_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003135 }
3136
3137 // Main chunk
3138 n /= BITS_PER_LONG;
3139 while (n >= 4) {
3140 *dst++ ^= val;
3141 *dst++ ^= val;
3142 *dst++ ^= val;
3143 *dst++ ^= val;
3144 n -= 4;
3145 }
3146 while (n--)
3147 *dst++ ^= val;
3148
3149 // Trailing bits
3150 if (last)
3151 *dst = xor(val, *dst, last);
3152 }
3153}
3154
3155static inline void fill_one_line(int bpp, unsigned long next_plane,
3156 unsigned long *dst, int dst_idx, u32 n,
3157 u32 color)
3158{
3159 while (1) {
3160 dst += dst_idx >> SHIFT_PER_LONG;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003161 dst_idx &= (BITS_PER_LONG - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003162 bitfill32(dst, dst_idx, color & 1 ? ~0 : 0, n);
3163 if (!--bpp)
3164 break;
3165 color >>= 1;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003166 dst_idx += next_plane * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003167 }
3168}
3169
3170static inline void xor_one_line(int bpp, unsigned long next_plane,
3171 unsigned long *dst, int dst_idx, u32 n,
3172 u32 color)
3173{
3174 while (color) {
3175 dst += dst_idx >> SHIFT_PER_LONG;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003176 dst_idx &= (BITS_PER_LONG - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003177 bitxor32(dst, dst_idx, color & 1 ? ~0 : 0, n);
3178 if (!--bpp)
3179 break;
3180 color >>= 1;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003181 dst_idx += next_plane * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003182 }
3183}
3184
3185
3186static void amifb_fillrect(struct fb_info *info,
3187 const struct fb_fillrect *rect)
3188{
Geert Uytterhoeven423a5302011-11-21 21:53:56 +01003189 struct amifb_par *par = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003190 int dst_idx, x2, y2;
3191 unsigned long *dst;
3192 u32 width, height;
3193
3194 if (!rect->width || !rect->height)
3195 return;
3196
3197 /*
3198 * We could use hardware clipping but on many cards you get around
3199 * hardware clipping by writing to framebuffer directly.
3200 * */
3201 x2 = rect->dx + rect->width;
3202 y2 = rect->dy + rect->height;
3203 x2 = x2 < info->var.xres_virtual ? x2 : info->var.xres_virtual;
3204 y2 = y2 < info->var.yres_virtual ? y2 : info->var.yres_virtual;
3205 width = x2 - rect->dx;
3206 height = y2 - rect->dy;
3207
3208 dst = (unsigned long *)
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003209 ((unsigned long)info->screen_base & ~(BYTES_PER_LONG - 1));
3210 dst_idx = ((unsigned long)info->screen_base & (BYTES_PER_LONG - 1)) * 8;
3211 dst_idx += rect->dy * par->next_line * 8 + rect->dx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003212 while (height--) {
3213 switch (rect->rop) {
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003214 case ROP_COPY:
Linus Torvalds1da177e2005-04-16 15:20:36 -07003215 fill_one_line(info->var.bits_per_pixel,
3216 par->next_plane, dst, dst_idx, width,
3217 rect->color);
3218 break;
3219
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003220 case ROP_XOR:
Linus Torvalds1da177e2005-04-16 15:20:36 -07003221 xor_one_line(info->var.bits_per_pixel, par->next_plane,
3222 dst, dst_idx, width, rect->color);
3223 break;
3224 }
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003225 dst_idx += par->next_line * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003226 }
3227}
3228
3229static inline void copy_one_line(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(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
3246static inline void copy_one_line_rev(int bpp, unsigned long next_plane,
3247 unsigned long *dst, int dst_idx,
3248 unsigned long *src, int src_idx, u32 n)
3249{
3250 while (1) {
3251 dst += dst_idx >> SHIFT_PER_LONG;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003252 dst_idx &= (BITS_PER_LONG - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003253 src += src_idx >> SHIFT_PER_LONG;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003254 src_idx &= (BITS_PER_LONG - 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003255 bitcpy_rev(dst, dst_idx, src, src_idx, n);
3256 if (!--bpp)
3257 break;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003258 dst_idx += next_plane * 8;
3259 src_idx += next_plane * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003260 }
3261}
3262
3263
3264static void amifb_copyarea(struct fb_info *info,
3265 const struct fb_copyarea *area)
3266{
Geert Uytterhoeven423a5302011-11-21 21:53:56 +01003267 struct amifb_par *par = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003268 int x2, y2;
3269 u32 dx, dy, sx, sy, width, height;
3270 unsigned long *dst, *src;
3271 int dst_idx, src_idx;
3272 int rev_copy = 0;
3273
3274 /* clip the destination */
3275 x2 = area->dx + area->width;
3276 y2 = area->dy + area->height;
3277 dx = area->dx > 0 ? area->dx : 0;
3278 dy = area->dy > 0 ? area->dy : 0;
3279 x2 = x2 < info->var.xres_virtual ? x2 : info->var.xres_virtual;
3280 y2 = y2 < info->var.yres_virtual ? y2 : info->var.yres_virtual;
3281 width = x2 - dx;
3282 height = y2 - dy;
3283
Roel Kluin091c82c2008-07-23 21:31:18 -07003284 if (area->sx + dx < area->dx || area->sy + dy < area->dy)
3285 return;
3286
Linus Torvalds1da177e2005-04-16 15:20:36 -07003287 /* update sx,sy */
3288 sx = area->sx + (dx - area->dx);
3289 sy = area->sy + (dy - area->dy);
3290
3291 /* the source must be completely inside the virtual screen */
Roel Kluin091c82c2008-07-23 21:31:18 -07003292 if (sx + width > info->var.xres_virtual ||
3293 sy + height > info->var.yres_virtual)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003294 return;
3295
3296 if (dy > sy || (dy == sy && dx > sx)) {
3297 dy += height;
3298 sy += height;
3299 rev_copy = 1;
3300 }
3301 dst = (unsigned long *)
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003302 ((unsigned long)info->screen_base & ~(BYTES_PER_LONG - 1));
Linus Torvalds1da177e2005-04-16 15:20:36 -07003303 src = dst;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003304 dst_idx = ((unsigned long)info->screen_base & (BYTES_PER_LONG - 1)) * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003305 src_idx = dst_idx;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003306 dst_idx += dy * par->next_line * 8 + dx;
3307 src_idx += sy * par->next_line * 8 + sx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003308 if (rev_copy) {
3309 while (height--) {
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003310 dst_idx -= par->next_line * 8;
3311 src_idx -= par->next_line * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003312 copy_one_line_rev(info->var.bits_per_pixel,
3313 par->next_plane, dst, dst_idx, src,
3314 src_idx, width);
3315 }
3316 } else {
3317 while (height--) {
3318 copy_one_line(info->var.bits_per_pixel,
3319 par->next_plane, dst, dst_idx, src,
3320 src_idx, width);
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003321 dst_idx += par->next_line * 8;
3322 src_idx += par->next_line * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003323 }
3324 }
3325}
3326
3327
3328static inline void expand_one_line(int bpp, unsigned long next_plane,
3329 unsigned long *dst, int dst_idx, u32 n,
3330 const u8 *data, u32 bgcolor, u32 fgcolor)
3331{
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003332 const unsigned long *src;
3333 int src_idx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003334
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003335 while (1) {
3336 dst += dst_idx >> SHIFT_PER_LONG;
3337 dst_idx &= (BITS_PER_LONG - 1);
3338 if ((bgcolor ^ fgcolor) & 1) {
3339 src = (unsigned long *)
3340 ((unsigned long)data & ~(BYTES_PER_LONG - 1));
3341 src_idx = ((unsigned long)data & (BYTES_PER_LONG - 1)) * 8;
3342 if (fgcolor & 1)
3343 bitcpy(dst, dst_idx, src, src_idx, n);
3344 else
3345 bitcpy_not(dst, dst_idx, src, src_idx, n);
3346 /* set or clear */
3347 } else
3348 bitfill32(dst, dst_idx, fgcolor & 1 ? ~0 : 0, n);
3349 if (!--bpp)
3350 break;
3351 bgcolor >>= 1;
3352 fgcolor >>= 1;
3353 dst_idx += next_plane * 8;
3354 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003355}
3356
3357
3358static void amifb_imageblit(struct fb_info *info, const struct fb_image *image)
3359{
Geert Uytterhoeven423a5302011-11-21 21:53:56 +01003360 struct amifb_par *par = info->par;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003361 int x2, y2;
3362 unsigned long *dst;
3363 int dst_idx;
3364 const char *src;
3365 u32 dx, dy, width, height, pitch;
3366
3367 /*
3368 * We could use hardware clipping but on many cards you get around
3369 * hardware clipping by writing to framebuffer directly like we are
3370 * doing here.
3371 */
3372 x2 = image->dx + image->width;
3373 y2 = image->dy + image->height;
3374 dx = image->dx;
3375 dy = image->dy;
3376 x2 = x2 < info->var.xres_virtual ? x2 : info->var.xres_virtual;
3377 y2 = y2 < info->var.yres_virtual ? y2 : info->var.yres_virtual;
3378 width = x2 - dx;
3379 height = y2 - dy;
3380
3381 if (image->depth == 1) {
3382 dst = (unsigned long *)
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003383 ((unsigned long)info->screen_base & ~(BYTES_PER_LONG - 1));
3384 dst_idx = ((unsigned long)info->screen_base & (BYTES_PER_LONG - 1)) * 8;
3385 dst_idx += dy * par->next_line * 8 + dx;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003386 src = image->data;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003387 pitch = (image->width + 7) / 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003388 while (height--) {
3389 expand_one_line(info->var.bits_per_pixel,
3390 par->next_plane, dst, dst_idx, width,
3391 src, image->bg_color,
3392 image->fg_color);
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003393 dst_idx += par->next_line * 8;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003394 src += pitch;
3395 }
3396 } else {
Geert Uytterhoeven2eab7ff2008-12-21 15:48:13 +01003397 c2p_planar(info->screen_base, image->data, dx, dy, width,
3398 height, par->next_line, par->next_plane,
3399 image->width, info->var.bits_per_pixel);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003400 }
3401}
3402
3403
3404 /*
3405 * Amiga Frame Buffer Specific ioctls
3406 */
3407
Christoph Hellwig67a66802006-01-14 13:21:25 -08003408static int amifb_ioctl(struct fb_info *info,
3409 unsigned int cmd, unsigned long arg)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003410{
3411 union {
3412 struct fb_fix_cursorinfo fix;
3413 struct fb_var_cursorinfo var;
3414 struct fb_cursorstate state;
3415 } crsr;
Al Viro3728d252006-01-12 01:06:31 -08003416 void __user *argp = (void __user *)arg;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003417 int i;
3418
3419 switch (cmd) {
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003420 case FBIOGET_FCURSORINFO:
3421 i = ami_get_fix_cursorinfo(&crsr.fix);
3422 if (i)
3423 return i;
3424 return copy_to_user(argp, &crsr.fix,
3425 sizeof(crsr.fix)) ? -EFAULT : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003426
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003427 case FBIOGET_VCURSORINFO:
3428 i = ami_get_var_cursorinfo(&crsr.var,
3429 ((struct fb_var_cursorinfo __user *)arg)->data);
3430 if (i)
3431 return i;
3432 return copy_to_user(argp, &crsr.var,
3433 sizeof(crsr.var)) ? -EFAULT : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003434
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003435 case FBIOPUT_VCURSORINFO:
3436 if (copy_from_user(&crsr.var, argp, sizeof(crsr.var)))
3437 return -EFAULT;
3438 return ami_set_var_cursorinfo(&crsr.var,
3439 ((struct fb_var_cursorinfo __user *)arg)->data);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003440
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003441 case FBIOGET_CURSORSTATE:
3442 i = ami_get_cursorstate(&crsr.state);
3443 if (i)
3444 return i;
3445 return copy_to_user(argp, &crsr.state,
3446 sizeof(crsr.state)) ? -EFAULT : 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003447
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003448 case FBIOPUT_CURSORSTATE:
3449 if (copy_from_user(&crsr.state, argp, sizeof(crsr.state)))
3450 return -EFAULT;
3451 return ami_set_cursorstate(&crsr.state);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003452 }
3453 return -EINVAL;
3454}
3455
3456
3457 /*
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01003458 * Flash the cursor (called by VBlank interrupt)
3459 */
3460
3461static int flash_cursor(void)
3462{
3463 static int cursorcount = 1;
3464
3465 if (cursormode == FB_CURSOR_FLASH) {
3466 if (!--cursorcount) {
3467 cursorstate = -cursorstate;
3468 cursorcount = cursorrate;
3469 if (!is_blanked)
3470 return 1;
3471 }
3472 }
3473 return 0;
3474}
3475
3476 /*
3477 * VBlank Display Interrupt
3478 */
3479
3480static irqreturn_t amifb_interrupt(int irq, void *dev_id)
3481{
3482 if (do_vmode_pan || do_vmode_full)
3483 ami_update_display();
3484
3485 if (do_vmode_full)
3486 ami_init_display();
3487
3488 if (do_vmode_pan) {
3489 flash_cursor();
3490 ami_rebuild_copper();
3491 do_cursor = do_vmode_pan = 0;
3492 } else if (do_cursor) {
3493 flash_cursor();
3494 ami_set_sprite();
3495 do_cursor = 0;
3496 } else {
3497 if (flash_cursor())
3498 ami_set_sprite();
3499 }
3500
3501 if (do_blank) {
3502 ami_do_blank();
3503 do_blank = 0;
3504 }
3505
3506 if (do_vmode_full) {
3507 ami_reinit_copper();
3508 do_vmode_full = 0;
3509 }
3510 return IRQ_HANDLED;
3511}
3512
3513
3514static struct fb_ops amifb_ops = {
3515 .owner = THIS_MODULE,
3516 .fb_check_var = amifb_check_var,
3517 .fb_set_par = amifb_set_par,
3518 .fb_setcolreg = amifb_setcolreg,
3519 .fb_blank = amifb_blank,
3520 .fb_pan_display = amifb_pan_display,
3521 .fb_fillrect = amifb_fillrect,
3522 .fb_copyarea = amifb_copyarea,
3523 .fb_imageblit = amifb_imageblit,
3524 .fb_ioctl = amifb_ioctl,
3525};
3526
3527
3528 /*
Linus Torvalds1da177e2005-04-16 15:20:36 -07003529 * Allocate, Clear and Align a Block of Chip Memory
3530 */
3531
Geert Uytterhoeven8f25c012011-05-21 19:42:56 +00003532static void *aligned_chipptr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003533
3534static inline u_long __init chipalloc(u_long size)
3535{
Geert Uytterhoeven8f25c012011-05-21 19:42:56 +00003536 aligned_chipptr = amiga_chip_alloc(size, "amifb [RAM]");
3537 if (!aligned_chipptr) {
Geert Uytterhoevena7076422011-05-21 19:42:55 +00003538 pr_err("amifb: No Chip RAM for frame buffer");
3539 return 0;
3540 }
Geert Uytterhoeven8f25c012011-05-21 19:42:56 +00003541 memset(aligned_chipptr, 0, size);
3542 return (u_long)aligned_chipptr;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003543}
3544
3545static inline void chipfree(void)
3546{
Geert Uytterhoeven8f25c012011-05-21 19:42:56 +00003547 if (aligned_chipptr)
3548 amiga_chip_free(aligned_chipptr);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003549}
3550
3551
Geert Uytterhoevenf1cbb172011-11-21 21:53:54 +01003552static void amifb_deinit(struct platform_device *pdev)
3553{
3554 if (fb_info.cmap.len)
3555 fb_dealloc_cmap(&fb_info.cmap);
3556 chipfree();
3557 if (videomemory)
3558 iounmap((void *)videomemory);
3559 custom.dmacon = DMAF_ALL | DMAF_MASTER;
3560}
3561
3562
Linus Torvalds1da177e2005-04-16 15:20:36 -07003563 /*
3564 * Initialisation
3565 */
3566
Geert Uytterhoevenfa6688e2009-04-05 12:45:56 +02003567static int __init amifb_probe(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003568{
3569 int tag, i, err = 0;
3570 u_long chipptr;
3571 u_int defmode;
3572
3573#ifndef MODULE
3574 char *option = NULL;
3575
3576 if (fb_get_options("amifb", &option)) {
3577 amifb_video_off();
3578 return -ENODEV;
3579 }
3580 amifb_setup(option);
3581#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -07003582 custom.dmacon = DMAF_ALL | DMAF_MASTER;
3583
3584 switch (amiga_chipset) {
3585#ifdef CONFIG_FB_AMIGA_OCS
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003586 case CS_OCS:
3587 strcat(fb_info.fix.id, "OCS");
Linus Torvalds1da177e2005-04-16 15:20:36 -07003588default_chipset:
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003589 chipset = TAG_OCS;
3590 maxdepth[TAG_SHRES] = 0; /* OCS means no SHRES */
3591 maxdepth[TAG_HIRES] = 4;
3592 maxdepth[TAG_LORES] = 6;
3593 maxfmode = TAG_FMODE_1;
3594 defmode = amiga_vblank == 50 ? DEFMODE_PAL : DEFMODE_NTSC;
3595 fb_info.fix.smem_len = VIDEOMEMSIZE_OCS;
3596 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003597#endif /* CONFIG_FB_AMIGA_OCS */
3598
3599#ifdef CONFIG_FB_AMIGA_ECS
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003600 case CS_ECS:
3601 strcat(fb_info.fix.id, "ECS");
3602 chipset = TAG_ECS;
3603 maxdepth[TAG_SHRES] = 2;
3604 maxdepth[TAG_HIRES] = 4;
3605 maxdepth[TAG_LORES] = 6;
3606 maxfmode = TAG_FMODE_1;
3607 if (AMIGAHW_PRESENT(AMBER_FF))
3608 defmode = amiga_vblank == 50 ? DEFMODE_AMBER_PAL
3609 : DEFMODE_AMBER_NTSC;
3610 else
3611 defmode = amiga_vblank == 50 ? DEFMODE_PAL
3612 : DEFMODE_NTSC;
3613 if (amiga_chip_avail() - CHIPRAM_SAFETY_LIMIT >
3614 VIDEOMEMSIZE_ECS_2M)
3615 fb_info.fix.smem_len = VIDEOMEMSIZE_ECS_2M;
3616 else
3617 fb_info.fix.smem_len = VIDEOMEMSIZE_ECS_1M;
3618 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003619#endif /* CONFIG_FB_AMIGA_ECS */
3620
3621#ifdef CONFIG_FB_AMIGA_AGA
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003622 case CS_AGA:
3623 strcat(fb_info.fix.id, "AGA");
3624 chipset = TAG_AGA;
3625 maxdepth[TAG_SHRES] = 8;
3626 maxdepth[TAG_HIRES] = 8;
3627 maxdepth[TAG_LORES] = 8;
3628 maxfmode = TAG_FMODE_4;
3629 defmode = DEFMODE_AGA;
3630 if (amiga_chip_avail() - CHIPRAM_SAFETY_LIMIT >
3631 VIDEOMEMSIZE_AGA_2M)
3632 fb_info.fix.smem_len = VIDEOMEMSIZE_AGA_2M;
3633 else
3634 fb_info.fix.smem_len = VIDEOMEMSIZE_AGA_1M;
3635 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003636#endif /* CONFIG_FB_AMIGA_AGA */
3637
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003638 default:
Linus Torvalds1da177e2005-04-16 15:20:36 -07003639#ifdef CONFIG_FB_AMIGA_OCS
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003640 printk("Unknown graphics chipset, defaulting to OCS\n");
3641 strcat(fb_info.fix.id, "Unknown");
3642 goto default_chipset;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003643#else /* CONFIG_FB_AMIGA_OCS */
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003644 err = -ENODEV;
3645 goto amifb_error;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003646#endif /* CONFIG_FB_AMIGA_OCS */
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003647 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003648 }
3649
3650 /*
3651 * Calculate the Pixel Clock Values for this Machine
3652 */
3653
3654 {
3655 u_long tmp = DIVUL(200000000000ULL, amiga_eclock);
3656
3657 pixclock[TAG_SHRES] = (tmp + 4) / 8; /* SHRES: 35 ns / 28 MHz */
3658 pixclock[TAG_HIRES] = (tmp + 2) / 4; /* HIRES: 70 ns / 14 MHz */
3659 pixclock[TAG_LORES] = (tmp + 1) / 2; /* LORES: 140 ns / 7 MHz */
3660 }
3661
3662 /*
3663 * Replace the Tag Values with the Real Pixel Clock Values
3664 */
3665
3666 for (i = 0; i < NUM_TOTAL_MODES; i++) {
3667 struct fb_videomode *mode = &ami_modedb[i];
3668 tag = mode->pixclock;
3669 if (tag == TAG_SHRES || tag == TAG_HIRES || tag == TAG_LORES) {
3670 mode->pixclock = pixclock[tag];
3671 }
3672 }
3673
Geert Uytterhoeven03c740a2011-11-21 21:53:57 +01003674 if (amifb_hfmin) {
3675 fb_info.monspecs.hfmin = amifb_hfmin;
3676 fb_info.monspecs.hfmax = amifb_hfmax;
3677 fb_info.monspecs.vfmin = amifb_vfmin;
3678 fb_info.monspecs.vfmax = amifb_vfmax;
3679 } else {
3680 /*
3681 * These are for a typical Amiga monitor (e.g. A1960)
3682 */
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003683 fb_info.monspecs.hfmin = 15000;
3684 fb_info.monspecs.hfmax = 38000;
3685 fb_info.monspecs.vfmin = 49;
3686 fb_info.monspecs.vfmax = 90;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003687 }
3688
3689 fb_info.fbops = &amifb_ops;
3690 fb_info.par = &currentpar;
3691 fb_info.flags = FBINFO_DEFAULT;
Geert Uytterhoevenfa6688e2009-04-05 12:45:56 +02003692 fb_info.device = &pdev->dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003693
3694 if (!fb_find_mode(&fb_info.var, &fb_info, mode_option, ami_modedb,
3695 NUM_TOTAL_MODES, &ami_modedb[defmode], 4)) {
3696 err = -EINVAL;
3697 goto amifb_error;
3698 }
3699
Geert Uytterhoevendb3e5282008-07-17 21:16:18 +02003700 fb_videomode_to_modelist(ami_modedb, NUM_TOTAL_MODES,
3701 &fb_info.modelist);
3702
Linus Torvalds1da177e2005-04-16 15:20:36 -07003703 round_down_bpp = 0;
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003704 chipptr = chipalloc(fb_info.fix.smem_len + SPRITEMEMSIZE +
3705 DUMMYSPRITEMEMSIZE + COPINITSIZE +
3706 4 * COPLISTSIZE);
Geert Uytterhoevena7076422011-05-21 19:42:55 +00003707 if (!chipptr) {
3708 err = -ENOMEM;
3709 goto amifb_error;
3710 }
Linus Torvalds1da177e2005-04-16 15:20:36 -07003711
3712 assignchunk(videomemory, u_long, chipptr, fb_info.fix.smem_len);
3713 assignchunk(spritememory, u_long, chipptr, SPRITEMEMSIZE);
3714 assignchunk(dummysprite, u_short *, chipptr, DUMMYSPRITEMEMSIZE);
3715 assignchunk(copdisplay.init, copins *, chipptr, COPINITSIZE);
3716 assignchunk(copdisplay.list[0][0], copins *, chipptr, COPLISTSIZE);
3717 assignchunk(copdisplay.list[0][1], copins *, chipptr, COPLISTSIZE);
3718 assignchunk(copdisplay.list[1][0], copins *, chipptr, COPLISTSIZE);
3719 assignchunk(copdisplay.list[1][1], copins *, chipptr, COPLISTSIZE);
3720
3721 /*
3722 * access the videomem with writethrough cache
3723 */
3724 fb_info.fix.smem_start = (u_long)ZTWO_PADDR(videomemory);
3725 videomemory = (u_long)ioremap_writethrough(fb_info.fix.smem_start,
3726 fb_info.fix.smem_len);
3727 if (!videomemory) {
3728 printk("amifb: WARNING! unable to map videomem cached writethrough\n");
Amol Lad57354c42006-12-08 02:40:16 -08003729 fb_info.screen_base = (char *)ZTWO_VADDR(fb_info.fix.smem_start);
3730 } else
3731 fb_info.screen_base = (char *)videomemory;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003732
Linus Torvalds1da177e2005-04-16 15:20:36 -07003733 memset(dummysprite, 0, DUMMYSPRITEMEMSIZE);
3734
3735 /*
3736 * Enable Display DMA
3737 */
3738
3739 custom.dmacon = DMAF_SETCLR | DMAF_MASTER | DMAF_RASTER | DMAF_COPPER |
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003740 DMAF_BLITTER | DMAF_SPRITE;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003741
3742 /*
3743 * Make sure the Copper has something to do
3744 */
3745
3746 ami_init_copper();
3747
3748 if (request_irq(IRQ_AMIGA_COPPER, amifb_interrupt, 0,
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003749 "fb vertb handler", &currentpar)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -07003750 err = -EBUSY;
3751 goto amifb_error;
3752 }
3753
Geert Uytterhoevenf0058b42011-11-21 21:53:52 +01003754 err = fb_alloc_cmap(&fb_info.cmap, 1 << fb_info.var.bits_per_pixel, 0);
Andres Salomoneb8972b2009-03-31 15:25:30 -07003755 if (err)
3756 goto amifb_error;
Linus Torvalds1da177e2005-04-16 15:20:36 -07003757
3758 if (register_framebuffer(&fb_info) < 0) {
3759 err = -EINVAL;
3760 goto amifb_error;
3761 }
3762
3763 printk("fb%d: %s frame buffer device, using %dK of video memory\n",
3764 fb_info.node, fb_info.fix.id, fb_info.fix.smem_len>>10);
3765
3766 return 0;
3767
3768amifb_error:
Geert Uytterhoevenfa6688e2009-04-05 12:45:56 +02003769 amifb_deinit(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003770 return err;
3771}
3772
Linus Torvalds1da177e2005-04-16 15:20:36 -07003773
Geert Uytterhoevenfa6688e2009-04-05 12:45:56 +02003774static int __exit amifb_remove(struct platform_device *pdev)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003775{
3776 unregister_framebuffer(&fb_info);
Geert Uytterhoevenfa6688e2009-04-05 12:45:56 +02003777 amifb_deinit(pdev);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003778 amifb_video_off();
Geert Uytterhoevenfa6688e2009-04-05 12:45:56 +02003779 return 0;
3780}
3781
3782static struct platform_driver amifb_driver = {
3783 .remove = __exit_p(amifb_remove),
3784 .driver = {
3785 .name = "amiga-video",
3786 .owner = THIS_MODULE,
3787 },
3788};
3789
3790static int __init amifb_init(void)
3791{
3792 return platform_driver_probe(&amifb_driver, amifb_probe);
Linus Torvalds1da177e2005-04-16 15:20:36 -07003793}
Adrian Bunk57987122008-07-23 21:31:43 -07003794
3795module_init(amifb_init);
Geert Uytterhoevenfa6688e2009-04-05 12:45:56 +02003796
3797static void __exit amifb_exit(void)
3798{
3799 platform_driver_unregister(&amifb_driver);
3800}
3801
Adrian Bunk57987122008-07-23 21:31:43 -07003802module_exit(amifb_exit);
3803
3804MODULE_LICENSE("GPL");
Geert Uytterhoevenfa6688e2009-04-05 12:45:56 +02003805MODULE_ALIAS("platform:amiga-video");