File: /usr/src/linux/drivers/video/sa1100fb.c

1     /*
2      *  linux/drivers/video/sa1100fb.c
3      *
4      *  Copyright (C) 1999 Eric A. Thomas
5      *   Based on acornfb.c Copyright (C) Russell King.
6      *
7      * This file is subject to the terms and conditions of the GNU General Public
8      * License.  See the file COPYING in the main directory of this archive for
9      * more details.
10      *
11      *	        StrongARM 1100 LCD Controller Frame Buffer Driver
12      *
13      * Please direct your questions and comments on this driver to the following
14      * email address:
15      *
16      *	linux-arm-kernel@lists.arm.linux.org.uk
17      *
18      * Clean patches should be sent to the ARM Linux Patch System.  Please see the
19      * following web page for more information:
20      *
21      *	http://www.arm.linux.org.uk/developer/patches/info.shtml
22      *
23      * Thank you.
24      *
25      * Known problems:
26      *  - With the Neponset plugged into an Assabet, LCD powerdown
27      *    doesn't work (LCD stays powered up).  Therefore we shouldn't
28      *    blank the screen.
29      *  - We don't limit the CPU clock rate nor the mode selection
30      *    according to the available SDRAM bandwidth.
31      *
32      *
33      * Code Status:
34      * 1999/04/01:
35      *	- Driver appears to be working for Brutus 320x200x8bpp mode.  Other
36      *	  resolutions are working, but only the 8bpp mode is supported.
37      *	  Changes need to be made to the palette encode and decode routines
38      *	  to support 4 and 16 bpp modes.  
39      *	  Driver is not designed to be a module.  The FrameBuffer is statically
40      *	  allocated since dynamic allocation of a 300k buffer cannot be 
41      *	  guaranteed. 
42      *
43      * 1999/06/17:
44      *	- FrameBuffer memory is now allocated at run-time when the
45      *	  driver is initialized.    
46      *
47      * 2000/04/10: Nicolas Pitre <nico@cam.org>
48      *	- Big cleanup for dynamic selection of machine type at run time.
49      *
50      * 2000/07/19: Jamey Hicks <jamey@crl.dec.com>
51      *	- Support for Bitsy aka Compaq iPAQ H3600 added.
52      *
53      * 2000/08/07: Tak-Shing Chan <tchan.rd@idthk.com>
54      *	       Jeff Sutherland <jsutherland@accelent.com>
55      *	- Resolved an issue caused by a change made to the Assabet's PLD 
56      *	  earlier this year which broke the framebuffer driver for newer 
57      *	  Phase 4 Assabets.  Some other parameters were changed to optimize
58      *	  for the Sharp display.
59      *
60      * 2000/08/09: Kunihiko IMAI <imai@vasara.co.jp>
61      *	- XP860 support added
62      *
63      * 2000/08/19: Mark Huang <mhuang@livetoy.com>
64      *	- Allows standard options to be passed on the kernel command line
65      *	  for most common passive displays.
66      *
67      * 2000/08/29:
68      *	- s/save_flags_cli/local_irq_save/
69      *	- remove unneeded extra save_flags_cli in sa1100fb_enable_lcd_controller
70      *
71      * 2000/10/10: Erik Mouw <J.A.K.Mouw@its.tudelft.nl>
72      *	- Updated LART stuff. Fixed some minor bugs.
73      *
74      * 2000/10/30: Murphy Chen <murphy@mail.dialogue.com.tw>
75      *	- Pangolin support added
76      *
77      * 2000/10/31: Roman Jordan <jor@hoeft-wessel.de>
78      *	- Huw Webpanel support added
79      *
80      * 2000/11/23: Eric Peng <ericpeng@coventive.com>
81      *	- Freebird add
82      *
83      * 2001/02/07: Jamey Hicks <jamey.hicks@compaq.com> 
84      *	       Cliff Brake <cbrake@accelent.com>
85      *	- Added PM callback
86      *
87      * 2001/05/26: <rmk@arm.linux.org.uk>
88      *	- Fix 16bpp so that (a) we use the right colours rather than some
89      *	  totally random colour depending on what was in page 0, and (b)
90      *	  we don't de-reference a NULL pointer.
91      *	- remove duplicated implementation of consistent_alloc()
92      *	- convert dma address types to dma_addr_t
93      *	- remove unused 'montype' stuff
94      *	- remove redundant zero inits of init_var after the initial
95      *	  memzero.
96      *	- remove allow_modeset (acornfb idea does not belong here)
97      *
98      * 2001/05/28: <rmk@arm.linux.org.uk>
99      *	- massive cleanup - move machine dependent data into structures
100      *	- I've left various #warnings in - if you see one, and know
101      *	  the hardware concerned, please get in contact with me.
102      *
103      * 2001/05/31: <rmk@arm.linux.org.uk>
104      *	- Fix LCCR1 HSW value, fix all machine type specifications to
105      *	  keep values in line.  (Please check your machine type specs)
106      *
107      * 2001/06/10: <rmk@arm.linux.org.uk>
108      *	- Fiddle with the LCD controller from task context only; mainly
109      *	  so that we can run with interrupts on, and sleep.
110      *	- Convert #warnings into #errors.  No pain, no gain. ;)
111      *
112      * 2001/06/14: <rmk@arm.linux.org.uk>
113      *	- Make the palette BPS value for 12bpp come out correctly.
114      *	- Take notice of "greyscale" on any colour depth.
115      *	- Make truecolor visuals use the RGB channel encoding information.
116      *
117      * 2001/07/02: <rmk@arm.linux.org.uk>
118      *	- Fix colourmap problems.
119      *
120      * 2001/07/13: <abraham@2d3d.co.za>
121      *	- Added support for the ICP LCD-Kit01 on LART. This LCD is
122      *	  manufactured by Prime View, model no V16C6448AB
123      *
124      * 2001/07/23: <rmk@arm.linux.org.uk>
125      *	- Hand merge version from handhelds.org CVS tree.  See patch
126      *	  notes for 595/1 for more information.
127      *	- Drop 12bpp (it's 16bpp with different colour register mappings).
128      *	- This hardware can not do direct colour.  Therefore we don't
129      *	  support it.
130      *
131      * 2001/07/27: <rmk@arm.linux.org.uk>
132      *	- Halve YRES on dual scan LCDs.
133      */
134     
135     #include <linux/config.h>
136     #include <linux/module.h>
137     #include <linux/kernel.h>
138     #include <linux/sched.h>
139     #include <linux/errno.h>
140     #include <linux/string.h>
141     #include <linux/interrupt.h>
142     #include <linux/slab.h>
143     #include <linux/fb.h>
144     #include <linux/delay.h>
145     #include <linux/pm.h>
146     #include <linux/init.h>
147     #include <linux/cpufreq.h>
148     
149     #include <asm/hardware.h>
150     #include <asm/io.h>
151     #include <asm/irq.h>
152     #include <asm/mach-types.h>
153     #include <asm/uaccess.h>
154     
155     #include <video/fbcon.h>
156     #include <video/fbcon-mfb.h>
157     #include <video/fbcon-cfb4.h>
158     #include <video/fbcon-cfb8.h>
159     #include <video/fbcon-cfb16.h>
160     
161     /*
162      * enable this if your panel appears to have broken
163      */
164     #undef CHECK_COMPAT
165     
166     /*
167      * debugging?
168      */
169     #define DEBUG 0
170     /*
171      * Complain if VAR is out of range.
172      */
173     #define DEBUG_VAR 1
174     
175     #undef ASSABET_PAL_VIDEO
176     
177     #include "sa1100fb.h"
178     
179     void (*sa1100fb_blank_helper)(int blank);
180     EXPORT_SYMBOL(sa1100fb_blank_helper);
181     
182     
183     #ifdef CHECK_COMPAT
184     static void
185     sa1100fb_check_shadow(struct sa1100fb_lcd_reg *new_regs,
186     			   struct fb_var_screeninfo *var, u_int pcd)
187     {
188     	struct sa1100fb_lcd_reg shadow;
189     	int different = 0;
190     
191     	/*
192     	 * These machines are good machines!
193     	 */
194     	if (machine_is_assabet() || machine_is_bitsy())
195     		return;
196     
197     	/*
198     	 * The following ones are bad, bad, bad.
199     	 * Please make yours good!
200     	 */
201     	if (machine_is_pangolin()) {
202     		DPRINTK("Configuring Pangolin LCD\n");
203     		shadow.lccr0 =
204     		    LCCR0_LEN + LCCR0_Color + LCCR0_LDM +
205     		    LCCR0_BAM + LCCR0_ERM + LCCR0_Act +
206     		    LCCR0_LtlEnd + LCCR0_DMADel(0);
207     		shadow.lccr1 =
208     		    LCCR1_DisWdth(var->xres) + LCCR1_HorSnchWdth(64) +
209     		    LCCR1_BegLnDel(160) + LCCR1_EndLnDel(24);
210     		shadow.lccr2 =
211     		    LCCR2_DisHght(var->yres) + LCCR2_VrtSnchWdth(7) +
212     		    LCCR2_BegFrmDel(7) + LCCR2_EndFrmDel(1);
213     		shadow.lccr3 =
214     		    LCCR3_PixClkDiv(pcd) + LCCR3_HorSnchH +
215     		    LCCR3_VrtSnchH + LCCR3_PixFlEdg + LCCR3_OutEnH;
216     
217     		DPRINTK("pcd = %x, PixCldDiv(pcd)=%x\n",
218     			pcd, LCCR3_PixClkDiv(pcd));
219     	}
220     	if (machine_is_freebird()) {
221     		DPRINTK("Configuring  Freebird LCD\n");
222     #if 1
223     		shadow.lccr0 = 0x00000038;
224     		shadow.lccr1 = 0x010108e0;
225     		shadow.lccr2 = 0x0000053f;
226     		shadow.lccr3 = 0x00000c20;
227     #else
228     		shadow.lccr0 =
229     		    LCCR0_LEN + LCCR0_Color + LCCR0_Sngl +
230     		    LCCR0_LDM + LCCR0_BAM + LCCR0_ERM + LCCR0_Pas +
231     		    LCCR0_LtlEnd + LCCR0_DMADel(0);
232     		/* Check ,Chester */
233     		shadow.lccr1 =
234     		    LCCR1_DisWdth(var->xres) + LCCR1_HorSnchWdth(5) +
235     		    LCCR1_BegLnDel(61) + LCCR1_EndLnDel(9);
236     		/* Check ,Chester */
237     		shadow.lccr2 =
238     		    LCCR2_DisHght(var->yres) + LCCR2_VrtSnchWdth(1) +
239     		    LCCR2_BegFrmDel(3) + LCCR2_EndFrmDel(0);
240     		/* Check ,Chester */
241     		shadow.lccr3 =
242     		    LCCR3_OutEnH + LCCR3_PixFlEdg + LCCR3_VrtSnchH +
243     		    LCCR3_HorSnchH + LCCR3_ACBsCntOff +
244     		    LCCR3_ACBsDiv(2) + LCCR3_PixClkDiv(pcd);
245     #endif
246     	}
247     	if (machine_is_brutus()) {
248     		DPRINTK("Configuring  Brutus LCD\n");
249     		shadow.lccr0 =
250     		    LCCR0_LEN + LCCR0_Color + LCCR0_Sngl + LCCR0_Pas +
251     		    LCCR0_LtlEnd + LCCR0_LDM + LCCR0_BAM + LCCR0_ERM +
252     		    LCCR0_DMADel(0);
253     		shadow.lccr1 =
254     		    LCCR1_DisWdth(var->xres) + LCCR1_HorSnchWdth(3) +
255     		    LCCR1_BegLnDel(41) + LCCR1_EndLnDel(101);
256     		shadow.lccr2 =
257     		    LCCR2_DisHght(var->yres) + LCCR2_VrtSnchWdth(1) +
258     		    LCCR2_BegFrmDel(0) + LCCR2_EndFrmDel(0);
259     		shadow.lccr3 =
260     		    LCCR3_OutEnH + LCCR3_PixRsEdg + LCCR3_VrtSnchH +
261     		    LCCR3_HorSnchH + LCCR3_ACBsCntOff +
262     		    LCCR3_ACBsDiv(2) + LCCR3_PixClkDiv(44);
263     	}
264     	if (machine_is_huw_webpanel()) {
265     		DPRINTK("Configuring  HuW LCD\n");
266     		shadow.lccr0 = LCCR0_LEN + LCCR0_Dual + LCCR0_LDM;
267     		shadow.lccr1 = LCCR1_DisWdth(var->xres) +
268     		    LCCR1_HorSnchWdth(3) +
269     		    LCCR1_BegLnDel(41) + LCCR1_EndLnDel(101);
270     		shadow.lccr2 = 239 + LCCR2_VrtSnchWdth(1);
271     		shadow.lccr3 = 8 + LCCR3_OutEnH +
272     		    LCCR3_PixRsEdg + LCCR3_VrtSnchH +
273     		    LCCR3_HorSnchH + LCCR3_ACBsCntOff + LCCR3_ACBsDiv(2);
274     	}
275     #ifdef CONFIG_SA1100_CERF
276     	if (machine_is_cerf()) {
277     		DPRINTK("Configuring Cerf LCD\n");
278     #if defined (CONFIG_CERF_LCD_72_A)
279     		shadow.lccr0 =
280     		    LCCR0_LEN + LCCR0_Color + LCCR0_Dual +
281     		    LCCR0_LDM + LCCR0_BAM + LCCR0_ERM + LCCR0_Pas +
282     		    LCCR0_LtlEnd + LCCR0_DMADel(0);
283     		shadow.lccr1 =
284     		    LCCR1_DisWdth(var->xres) + LCCR1_HorSnchWdth(5) +
285     		    LCCR1_BegLnDel(61) + LCCR1_EndLnDel(9);
286     		shadow.lccr2 =
287     		    LCCR2_DisHght(var->yres / 2) + LCCR2_VrtSnchWdth(1) +
288     		    LCCR2_BegFrmDel(3) + LCCR2_EndFrmDel(0);
289     		shadow.lccr3 =
290     		    LCCR3_OutEnH + LCCR3_PixRsEdg + LCCR3_VrtSnchH +
291     		    LCCR3_HorSnchH + LCCR3_ACBsCntOff +
292     		    LCCR3_ACBsDiv(2) + LCCR3_PixClkDiv(38);
293     #elif defined (CONFIG_CERF_LCD_57_A)
294     		shadow.lccr0 =
295     		    LCCR0_LEN + LCCR0_Color + LCCR0_Sngl +
296     		    LCCR0_LDM + LCCR0_BAM + LCCR0_ERM + LCCR0_Pas +
297     		    LCCR0_LtlEnd + LCCR0_DMADel(0);
298     		shadow.lccr1 =
299     		    LCCR1_DisWdth(var->xres) + LCCR1_HorSnchWdth(5) +
300     		    LCCR1_BegLnDel(61) + LCCR1_EndLnDel(9);
301     		shadow.lccr2 =
302     		    LCCR2_DisHght(var->yres) + LCCR2_VrtSnchWdth(1) +
303     		    LCCR2_BegFrmDel(3) + LCCR2_EndFrmDel(0);
304     		shadow.lccr3 =
305     		    LCCR3_OutEnH + LCCR3_PixRsEdg + LCCR3_VrtSnchH +
306     		    LCCR3_HorSnchH + LCCR3_ACBsCntOff +
307     		    LCCR3_ACBsDiv(2) + LCCR3_PixClkDiv(38);
308     #elif defined (CONFIG_CERF_LCD_38_A)
309     		shadow.lccr0 =
310     		    LCCR0_LEN + LCCR0_Color + LCCR0_Sngl +
311     		    LCCR0_LDM + LCCR0_BAM + LCCR0_ERM + LCCR0_Pas +
312     		    LCCR0_LtlEnd + LCCR0_DMADel(0);
313     		shadow.lccr1 =
314     		    LCCR1_DisWdth(var->xres) + LCCR1_HorSnchWdth(5) +
315     		    LCCR1_BegLnDel(61) + LCCR1_EndLnDel(9);
316     		shadow.lccr2 =
317     		    LCCR2_DisHght(var->yres) + LCCR2_VrtSnchWdth(1) +
318     		    LCCR2_BegFrmDel(3) + LCCR2_EndFrmDel(0);
319     		shadow.lccr3 =
320     		    LCCR3_OutEnH + LCCR3_PixRsEdg + LCCR3_VrtSnchH +
321     		    LCCR3_HorSnchH + LCCR3_ACBsCntOff +
322     		    LCCR3_ACBsDiv(2) + LCCR3_PixClkDiv(38);
323     #else
324     #error "Must have a CerfBoard LCD form factor selected"
325     #endif
326     	}
327     #endif
328     	if (machine_is_lart()) {
329     		DPRINTK("Configuring LART LCD\n");
330     #if defined LART_GREY_LCD
331     		shadow.lccr0 =
332     		    LCCR0_LEN + LCCR0_Mono + LCCR0_Sngl + LCCR0_Pas +
333     		    LCCR0_LtlEnd + LCCR0_LDM + LCCR0_BAM + LCCR0_ERM +
334     		    LCCR0_DMADel(0);
335     		shadow.lccr1 =
336     		    LCCR1_DisWdth(var->xres) + LCCR1_HorSnchWdth(1) +
337     		    LCCR1_BegLnDel(4) + LCCR1_EndLnDel(2);
338     		shadow.lccr2 =
339     		    LCCR2_DisHght(var->yres) + LCCR2_VrtSnchWdth(1) +
340     		    LCCR2_BegFrmDel(0) + LCCR2_EndFrmDel(0);
341     		shadow.lccr3 =
342     		    LCCR3_PixClkDiv(34) + LCCR3_ACBsDiv(512) +
343     		    LCCR3_ACBsCntOff + LCCR3_HorSnchH + LCCR3_VrtSnchH;
344     #endif
345     #if defined LART_COLOR_LCD
346     		shadow.lccr0 =
347     		    LCCR0_LEN + LCCR0_Color + LCCR0_Sngl + LCCR0_Act +
348     		    LCCR0_LtlEnd + LCCR0_LDM + LCCR0_BAM + LCCR0_ERM +
349     		    LCCR0_DMADel(0);
350     		shadow.lccr1 =
351     		    LCCR1_DisWdth(var->xres) + LCCR1_HorSnchWdth(2) +
352     		    LCCR1_BegLnDel(69) + LCCR1_EndLnDel(8);
353     		shadow.lccr2 =
354     		    LCCR2_DisHght(var->yres) + LCCR2_VrtSnchWdth(3) +
355     		    LCCR2_BegFrmDel(14) + LCCR2_EndFrmDel(4);
356     		shadow.lccr3 =
357     		    LCCR3_PixClkDiv(34) + LCCR3_ACBsDiv(512) +
358     		    LCCR3_ACBsCntOff + LCCR3_HorSnchL + LCCR3_VrtSnchL +
359     		    LCCR3_PixFlEdg;
360     #endif
361     #if defined LART_VIDEO_OUT
362     		shadow.lccr0 =
363     		    LCCR0_LEN + LCCR0_Color + LCCR0_Sngl + LCCR0_Act +
364     		    LCCR0_LtlEnd + LCCR0_LDM + LCCR0_BAM + LCCR0_ERM +
365     		    LCCR0_DMADel(0);
366     		shadow.lccr1 =
367     		    LCCR1_DisWdth(640) + LCCR1_HorSnchWdth(95) +
368     		    LCCR1_BegLnDel(40) + LCCR1_EndLnDel(24);
369     		shadow.lccr2 =
370     		    LCCR2_DisHght(480) + LCCR2_VrtSnchWdth(2) +
371     		    LCCR2_BegFrmDel(32) + LCCR2_EndFrmDel(11);
372     		shadow.lccr3 =
373     		    LCCR3_PixClkDiv(8) + LCCR3_ACBsDiv(512) +
374     		    LCCR3_ACBsCntOff + LCCR3_HorSnchH + LCCR3_VrtSnchH +
375     		    LCCR3_PixFlEdg + LCCR3_OutEnL;
376     #endif
377     	}
378     	if (machine_is_graphicsclient()) {
379     		DPRINTK("Configuring GraphicsClient LCD\n");
380     		shadow.lccr0 =
381     		    LCCR0_LEN + LCCR0_Color + LCCR0_Sngl + LCCR0_Act;
382     		shadow.lccr1 =
383     		    LCCR1_DisWdth(var->xres) + LCCR1_HorSnchWdth(9) +
384     		    LCCR1_EndLnDel(54) + LCCR1_BegLnDel(54);
385     		shadow.lccr2 =
386     		    LCCR2_DisHght(var->yres) + LCCR2_VrtSnchWdth(9) +
387     		    LCCR2_EndFrmDel(32) + LCCR2_BegFrmDel(24);
388     		shadow.lccr3 =
389     		    LCCR3_PixClkDiv(10) + LCCR3_ACBsDiv(2) +
390     		    LCCR3_ACBsCntOff + LCCR3_HorSnchL + LCCR3_VrtSnchL;
391     	}
392     	if (machine_is_omnimeter()) {
393     		DPRINTK("Configuring  OMNI LCD\n");
394     		shadow.lccr0 = LCCR0_LEN | LCCR0_CMS | LCCR0_DPD;
395     		shadow.lccr1 =
396     		    LCCR1_BegLnDel(10) + LCCR1_EndLnDel(10) +
397     		    LCCR1_HorSnchWdth(1) + LCCR1_DisWdth(var->xres);
398     		shadow.lccr2 = LCCR2_DisHght(var->yres);
399     		shadow.lccr3 =
400     		    LCCR3_ACBsDiv(0xFF) + LCCR3_PixClkDiv(44);
401     //jca (GetPCD(25) << LCD3_V_PCD);
402     	}
403     	if (machine_is_xp860()) {
404     		DPRINTK("Configuring XP860 LCD\n");
405     		shadow.lccr0 =
406     		    LCCR0_LEN + LCCR0_Color + LCCR0_Sngl + LCCR0_Act +
407     		    LCCR0_LtlEnd + LCCR0_LDM + LCCR0_ERM + LCCR0_DMADel(0);
408     		shadow.lccr1 =
409     		    LCCR1_DisWdth(var->xres) +
410     		    LCCR1_HorSnchWdth(var->hsync_len) +
411     		    LCCR1_BegLnDel(var->left_margin) +
412     		    LCCR1_EndLnDel(var->right_margin);
413     		shadow.lccr2 =
414     		    LCCR2_DisHght(var->yres) +
415     		    LCCR2_VrtSnchWdth(var->vsync_len) +
416     		    LCCR2_BegFrmDel(var->upper_margin) +
417     		    LCCR2_EndFrmDel(var->lower_margin);
418     		shadow.lccr3 =
419     		    LCCR3_PixClkDiv(6) + LCCR3_HorSnchL + LCCR3_VrtSnchL;
420     	}
421     
422     	/*
423     	 * Ok, since we're calculating these values, we want to know
424     	 * if the calculation is correct.  If you see any of these
425     	 * messages _PLEASE_ report the incident to me for diagnosis,
426     	 * including details about what was happening when the
427     	 * messages appeared. --rmk, 30 March 2001
428     	 */
429     	if (shadow.lccr0 != new_regs->lccr0) {
430     		printk(KERN_ERR "LCCR1 mismatch: 0x%08x != 0x%08x\n",
431     			shadow.lccr1, new_regs->lccr1);
432     		different = 1;
433     	}
434     	if (shadow.lccr1 != new_regs->lccr1) {
435     		printk(KERN_ERR "LCCR1 mismatch: 0x%08x != 0x%08x\n",
436     			shadow.lccr1, new_regs->lccr1);
437     		different = 1;
438     	}
439     	if (shadow.lccr2 != new_regs->lccr2) {
440     		printk(KERN_ERR "LCCR2 mismatch: 0x%08x != 0x%08x\n",
441     			shadow.lccr2, new_regs->lccr2);
442     		different = 1;
443     	}
444     	if (shadow.lccr3 != new_regs->lccr3) {
445     		printk(KERN_ERR "LCCR3 mismatch: 0x%08x != 0x%08x\n",
446     			shadow.lccr3, new_regs->lccr3);
447     		different = 1;
448     	}
449     	if (different) {
450     		printk(KERN_ERR "var: xres=%d hslen=%d lm=%d rm=%d\n",
451     			var->xres, var->hsync_len,
452     			var->left_margin, var->right_margin);
453     		printk(KERN_ERR "var: yres=%d vslen=%d um=%d bm=%d\n",
454     			var->yres, var->vsync_len,
455     			var->upper_margin, var->lower_margin);
456     
457     		printk(KERN_ERR "Please report this to Russell King "
458     			"<rmk@arm.linux.org.uk>\n");
459     	}
460     
461     	DPRINTK("olccr0 = 0x%08x\n", shadow.lccr0);
462     	DPRINTK("olccr1 = 0x%08x\n", shadow.lccr1);
463     	DPRINTK("olccr2 = 0x%08x\n", shadow.lccr2);
464     	DPRINTK("olccr3 = 0x%08x\n", shadow.lccr3);
465     }
466     #else
467     #define sa1100fb_check_shadow(regs,var,pcd)
468     #endif
469     
470     
471     
472     /*
473      * IMHO this looks wrong.  In 8BPP, length should be 8.
474      */
475     static struct sa1100fb_rgb rgb_8 = {
476     	red:	{ offset: 0,  length: 4, },
477     	green:	{ offset: 0,  length: 4, },
478     	blue:	{ offset: 0,  length: 4, },
479     	transp:	{ offset: 0,  length: 0, },
480     };
481     
482     static struct sa1100fb_rgb def_rgb_16 = {
483     	red:	{ offset: 11, length: 5, },
484     	green:	{ offset: 5,  length: 6, },
485     	blue:	{ offset: 0,  length: 5, },
486     	transp:	{ offset: 0,  length: 0, },
487     };
488     
489     #ifdef CONFIG_SA1100_ASSABET
490     static struct sa1100fb_mach_info assabet_info __initdata = {
491     #ifdef ASSABET_PAL_VIDEO
492     	pixclock:	67797,		bpp:		16,
493     	xres:		640,		yres:		512,
494     
495     	hsync_len:	64,		vsync_len:	6,
496     	left_margin:	125,		upper_margin:	70,
497     	right_margin:	115,		lower_margin:	36,
498     
499     	sync:		0,
500     
501     	lccr0:		LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
502     	lccr3:		LCCR3_OutEnH | LCCR3_PixRsEdg |	LCCR3_ACBsDiv(512),
503     #else
504     	pixclock:	171521,		bpp:		16,
505     	xres:		320,		yres:		240,
506     
507     	hsync_len:	5,		vsync_len:	1,
508     	left_margin:	61,		upper_margin:	3,
509     	right_margin:	9,		lower_margin:	0,
510     
511     	sync:		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
512     
513     	lccr0:		LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
514     	lccr3:		LCCR3_OutEnH | LCCR3_PixRsEdg |	LCCR3_ACBsDiv(2),
515     #endif
516     };
517     #endif
518     
519     #ifdef CONFIG_SA1100_BITSY
520     static struct sa1100fb_mach_info bitsy_info __initdata = {
521     #ifdef CONFIG_IPAQ_H3100
522     	pixclock:	0,		bpp:		4,
523     	xres:		320,		yres:		240,
524     
525     	hsync_len:	26,		vsync_len:	41,
526     	left_margin:	4,		upper_margin:	0,
527     	right_margin:	4,		lower_margin:	0,
528     
529     	sync:		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
530     	cmap_greyscale:	1,		cmap_static:	1,
531     	cmap_inverse:	1,
532     
533     	lccr0:		LCCR0_Mono | LCCR0_4PixMono | LCCR0_Sngl | LCCR0_Pas,
534     	lccr3:		LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2),
535     #error FIXME
536     	/*
537     	 * Sorry, this should have read:
538     	 * FIXME: please get rid of the PCD and PixClkDiv definitions
539     	 * in favour of pixclock. --rmk
540     	 */
541     #else
542     	pixclock:	5722222,	bpp:		16,
543     	xres:		320,		yres:		240,
544     
545     	hsync_len:	3,		vsync_len:	3,
546     	left_margin:	12,		upper_margin:	10,
547     	right_margin:	17,		lower_margin:	1,
548     
549     	sync:		0,
550     
551     	lccr0:		LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
552     	lccr3:		LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2),
553     #endif
554     };
555     
556     static struct sa1100fb_rgb bitsy_rgb_16 = {
557     	red:	{ offset: 12, length: 4, },
558     	green:	{ offset: 7,  length: 4, },
559     	blue:	{ offset: 1,  length: 4, },
560     	transp:	{ offset: 0,  length: 0, },
561     };
562     #endif
563     
564     #ifdef CONFIG_SA1100_BRUTUS
565     static struct sa1100fb_mach_info brutus_info __initdata = {
566     	pixclock:	0,		bpp:		8,
567     	xres:		320,		yres:		240,
568     
569     	hsync_len:	3,		vsync_len:	1,
570     	left_margin:	41,		upper_margin:	0,
571     	right_margin:	101,		lower_margin:	0,
572     
573     	sync:		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
574     
575     	lccr0:		LCCR0_Color | LCCR0_Sngl | LCCR0_Pas,
576     	lccr3:		LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2) |
577     			LCCR3_PixClkDiv(44),
578     };
579     #endif
580     
581     #ifdef CONFIG_SA1100_CERF
582     static struct sa1100fb_mach_info cerf_info __initdata = {
583     	pixclock:	171521,		bpp:		8,
584     #if defined(CONFIG_CERF_LCD_72_A)
585     	xres:		640,		yres:		480,
586     	lccr0:		LCCR0_Color | LCCR0_Dual | LCCR0_Pas,
587     	lccr3:		LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2) |
588     			LCCR3_PixClkDiv(38),
589     #elif defined(CONFIG_CERF_LCD_57_A)
590     	xres:		320,		yres:		240,
591     	lccr0:		LCCR0_Color | LCCR0_Sngl | LCCR0_Pas,
592     	lccr3:		LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2) |
593     			LCCR3_PixClkDiv(38),
594     #elif defined(CONFIG_CERF_LCD_38_A)
595     	xres:		240,		yres:		320,
596     	lccr0:		LCCR0_Color | LCCR0_Sngl | LCCR0_Pas,
597     	lccr3:		LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2) |
598     			LCCR3_PixClkDiv(38),
599     #else
600     #error "Must have a CerfBoard LCD form factor selected"
601     #endif
602     
603     	hsync_len:	5,		vsync_len:	1,
604     	left_margin:	61,		upper_margin:	3,
605     	right_margin:	9,		lower_margin:	0,
606     
607     	sync:		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
608     
609     };
610     #endif
611     
612     #ifdef CONFIG_SA1100_FREEBIRD
613     #warning Please check this carefully
614     static struct sa1100fb_mach_info freebird_info __initdata = {
615     	pixclock:	171521,		bpp:		16,
616     	xres:		240,		yres:		320,
617     
618     	hsync_len:	3,		vsync_len:	2,
619     	left_margin:	2,		upper_margin:	0,
620     	right_margin:	2,		lower_margin:	0,
621     
622     	sync:		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
623     
624     	lccr0:		LCCR0_Color | LCCR0_Sngl | LCCR0_Pas,
625     	lccr3:		LCCR3_OutEnH | LCCR3_PixFlEdg | LCCR3_ACBsDiv(2),
626     };
627     
628     static struct sa1100fb_rgb freebird_rgb_16 = {
629     	red:	{ offset: 8,  length: 4, },
630     	green:	{ offset: 4,  length: 4, },
631     	blue:	{ offset: 0,  length: 4, },
632     	transp:	{ offset: 12, length: 4, },
633     };
634     #endif
635     
636     #ifdef CONFIG_SA1100_GRAPHICSCLIENT
637     static struct sa1100fb_mach_info graphicsclient_info __initdata = {
638     	pixclock:	0,		bpp:		8,
639     	xres:		640,		yres:		480,
640     
641     	hsync_len:	9,		vsync_len:	9,
642     	left_margin:	54,		upper_margin:	24,
643     	right_margin:	54,		lower_margin:	32,
644     
645     	sync:		0,
646     
647     	lccr0:		LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
648     	lccr3:		LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2) |
649     			LCCR3_PixClkDiv(10),
650     };
651     #endif
652     
653     #ifdef CONFIG_SA1100_HUW_WEBPANEL
654     static struct sa1100fb_mach_info huw_webpanel_info __initdata = {
655     	pixclock:	0,		bpp:		8,
656     	xres:		640,		yres:		480,
657     
658     	hsync_len:	3,		vsync_len:	1,
659     	left_margin:	41,		upper_margin:	0,
660     	right_margin:	101,		lower_margin:	0,
661     
662     	sync:		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
663     
664     	lccr0:		LCCR0_Color | LCCR0_Dual | LCCR0_Pas,
665     	lccr3:		LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(2) | 8,
666     #error FIXME
667     	/*
668     	 * FIXME: please get rid of the '| 8' in preference to an
669     	 * LCCR3_PixClkDiv() version. --rmk
670     	 */
671     };
672     #endif
673     
674     #ifdef LART_GREY_LCD
675     static struct sa1100fb_mach_info lart_grey_info __initdata = {
676     	pixclock:	150000,		bpp:		4,
677     	xres:		320,		yres:		240,
678     
679     	hsync_len:	1,		vsync_len:	1,
680     	left_margin:	4,		upper_margin:	0,
681     	right_margin:	2,		lower_margin:	0,
682     
683     	cmap_greyscale:	1,
684     	sync:		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
685     
686     	lccr0:		LCCR0_Mono | LCCR0_Sngl | LCCR0_Pas | LCCR0_4PixMono,
687     	lccr3:		LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(512),
688     };
689     #endif
690     #ifdef LART_COLOR_LCD
691     static struct sa1100fb_mach_info lart_color_info __initdata = {
692     	pixclock:	150000,		bpp:		16,
693     	xres:		320,		yres:		240,
694     
695     	hsync_len:	2,		vsync_len:	3,
696     	left_margin:	69,		upper_margin:	14,
697     	right_margin:	8,		lower_margin:	4,
698     
699     	sync:		0,
700     
701     	lccr0:		LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
702     	lccr3:		LCCR3_OutEnH | LCCR3_PixFlEdg | LCCR3_ACBsDiv(512),
703     };
704     #endif
705     #ifdef LART_VIDEO_OUT
706     static struct sa1100fb_mach_info lart_video_info __initdata = {
707     	pixclock:	39721,		bpp:		16,
708     	xres:		640,		yres:		480,
709     
710     	hsync_len:	95,		vsync_len:	2,
711     	left_margin:	40,		upper_margin:	32,
712     	right_margin:	24,		lower_margin:	11,
713     
714     	sync:		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
715     
716     	lccr0:		LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
717     	lccr3:		LCCR3_OutEnL | LCCR3_PixFlEdg | LCCR3_ACBsDiv(512),
718     };
719     #endif
720     
721     #ifdef LART_KIT01_LCD
722     static struct sa1100fb_mach_info lart_kit01_info __initdata =
723     {
724     	pixclock:	63291,		bpp:		16,
725     	xres:		640,		yres:		480,
726     
727     	hsync_len:	64,		vsync_len:	3,
728     	left_margin:	122,		upper_margin:	45,
729     	right_margin:	10,		lower_margin:	10,
730     
731     	sync:		0,
732     
733     	lccr0:		LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
734     	lccr3:		LCCR3_OutEnH | LCCR3_PixFlEdg
735     };
736     #endif
737     
738     #ifdef CONFIG_SA1100_OMNIMETER
739     static struct sa1100fb_mach_info omnimeter_info __initdata = {
740     	pixclock:	0,		bpp:		4,
741     	xres:		480,		yres:		320,
742     
743     	hsync_len:	1,		vsync_len:	1,
744     	left_margin:	10,		upper_margin:	0,
745     	right_margin:	10,		lower_margin:	0,
746     
747     	cmap_greyscale:	1,
748     	sync:		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
749     
750     	lccr0:		LCCR0_Mono | LCCR0_Sngl | LCCR0_Pas | LCCR0_8PixMono,
751     	lccr3:		LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_ACBsDiv(255) |
752     			LCCR3_PixClkDiv(44),
753     #error FIXME: fix pixclock, ACBsDiv
754     	/*
755     	 * FIXME: I think ACBsDiv is wrong above - should it be 512 (disabled)?
756     	 *   - rmk
757     	 */
758     };
759     #endif
760     
761     #ifdef CONFIG_SA1100_PANGOLIN
762     static struct sa1100fb_mach_info pangolin_info __initdata = {
763     	pixclock:	341521,		bpp:		16,
764     	xres:		800,		yres:		600,
765     
766     	hsync_len:	64,		vsync_len:	7,
767     	left_margin:	160,		upper_margin:	7,
768     	right_margin:	24,		lower_margin:	1,
769     
770     	sync:		FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
771     
772     	lccr0:		LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
773     	lccr3:		LCCR3_OutEnH | LCCR3_PixFlEdg,
774     };
775     #endif
776     
777     #ifdef CONFIG_SA1100_XP860
778     static struct sa1100fb_mach_info xp860_info __initdata = {
779     	pixclock:	0,		bpp:		8,
780     	xres:		1024,		yres:		768,
781     
782     	hsync_len:	3,		vsync_len:	3,
783     	left_margin:	3,		upper_margin:	2,
784     	right_margin:	2,		lower_margin:	1,
785     
786     	sync:		0,
787     
788     	lccr0:		LCCR0_Color | LCCR0_Sngl | LCCR0_Act,
789     	lccr3:		LCCR3_OutEnH | LCCR3_PixRsEdg | LCCR3_PixClkDiv(6),
790     };
791     #endif
792     
793     static struct sa1100fb_mach_info * __init
794     sa1100fb_get_machine_info(struct sa1100fb_info *fbi)
795     {
796     	struct sa1100fb_mach_info *inf = NULL;
797     
798     	/*
799     	 *            R        G       B       T
800     	 * default  {11,5}, { 5,6}, { 0,5}, { 0,0}
801     	 * bitsy    {12,4}, { 7,4}, { 1,4}, { 0,0}
802     	 * freebird { 8,4}, { 4,4}, { 0,4}, {12,4}
803     	 */
804     #ifdef CONFIG_SA1100_ASSABET
805     	if (machine_is_assabet()) {
806     		inf = &assabet_info;
807     	}
808     #endif
809     #ifdef CONFIG_SA1100_BITSY
810     	if (machine_is_bitsy()) {
811     		inf = &bitsy_info;
812     		fbi->rgb[RGB_16] = &bitsy_rgb_16;
813     	}
814     #endif
815     #ifdef CONFIG_SA1100_BRUTUS
816     	if (machine_is_brutus()) {
817     		inf = &brutus_info;
818     	}
819     #endif
820     #ifdef CONFIG_SA1100_CERF
821     	if (machine_is_cerf()) {
822     		inf = &cerf_info;
823     	}
824     #endif
825     #ifdef CONFIG_SA1100_FREEBIRD
826     	if (machine_is_freebird()) {
827     		inf = &freebird_info;
828     		fbi->rgb[RGB_16] = &freebird_rgb16;
829     	}
830     #endif
831     #ifdef CONFIG_SA1100_GRAPHICSCLIENT
832     	if (machine_is_graphicsclient()) {
833     		inf = &graphicsclient_info;
834     	}
835     #endif
836     #ifdef CONFIG_SA1100_HUW_WEBPANEL
837     	if (machine_is_huw_webpanel()) {
838     		inf = &huw_webpanel_info;
839     	}
840     #endif
841     #ifdef CONFIG_SA1100_LART
842     	if (machine_is_lart()) {
843     #ifdef LART_GREY_LCD
844     		inf = &lart_grey_info;
845     #endif
846     #ifdef LART_COLOR_LCD
847     		inf = &lart_color_info;
848     #endif
849     #ifdef LART_VIDEO_OUT
850     		inf = &lart_video_info;
851     #endif
852     #ifdef LART_KIT01_LCD
853     		inf = &lart_kit01_info;
854     #endif
855     	}
856     #endif
857     #ifdef CONFIG_SA1100_OMNIMETER
858     	if (machine_is_omnimeter()) {
859     		inf = &omnimeter_info;
860     	}
861     #endif
862     #ifdef CONFIG_SA1100_PANGOLIN
863     	if (machine_is_pangolin()) {
864     		inf = &pangolin_info;
865     	}
866     #endif
867     #ifdef CONFIG_SA1100_XP860
868     	if (machine_is_xp860()) {
869     		inf = &xp860_info;
870     	}
871     #endif
872     	return inf;
873     }
874     
875     static int sa1100fb_activate_var(struct fb_var_screeninfo *var, struct sa1100fb_info *);
876     static void set_ctrlr_state(struct sa1100fb_info *fbi, u_int state);
877     
878     static inline void sa1100fb_schedule_task(struct sa1100fb_info *fbi, u_int state)
879     {
880     	unsigned long flags;
881     
882     	local_irq_save(flags);
883     	/*
884     	 * We need to handle two requests being made at the same time.
885     	 * There are two important cases:
886     	 *  1. When we are changing VT (C_REENABLE) while unblanking (C_ENABLE)
887     	 *     We must perform the unblanking, which will do our REENABLE for us.
888     	 *  2. When we are blanking, but immediately unblank before we have
889     	 *     blanked.  We do the "REENABLE" thing here as well, just to be sure.
890     	 */
891     	if (fbi->task_state == C_ENABLE && state == C_REENABLE)
892     		state = (u_int) -1;
893     	if (fbi->task_state == C_DISABLE && state == C_ENABLE)
894     		state = C_REENABLE;
895     
896     	if (state != (u_int)-1) {
897     		fbi->task_state = state;
898     		schedule_task(&fbi->task);
899     	}
900     	local_irq_restore(flags);
901     }
902     
903     /*
904      * Get the VAR structure pointer for the specified console
905      */
906     static inline struct fb_var_screeninfo *get_con_var(struct fb_info *info, int con)
907     {
908     	struct sa1100fb_info *fbi = (struct sa1100fb_info *)info;
909     	return (con == fbi->currcon || con == -1) ? &fbi->fb.var : &fb_display[con].var;
910     }
911     
912     /*
913      * Get the DISPLAY structure pointer for the specified console
914      */
915     static inline struct display *get_con_display(struct fb_info *info, int con)
916     {
917     	struct sa1100fb_info *fbi = (struct sa1100fb_info *)info;
918     	return (con < 0) ? fbi->fb.disp : &fb_display[con];
919     }
920     
921     /*
922      * Get the CMAP pointer for the specified console
923      */
924     static inline struct fb_cmap *get_con_cmap(struct fb_info *info, int con)
925     {
926     	struct sa1100fb_info *fbi = (struct sa1100fb_info *)info;
927     	return (con == fbi->currcon || con == -1) ? &fbi->fb.cmap : &fb_display[con].cmap;
928     }
929     
930     static inline u_int
931     chan_to_field(u_int chan, struct fb_bitfield *bf)
932     {
933     	chan &= 0xffff;
934     	chan >>= 16 - bf->length;
935     	return chan << bf->offset;
936     }
937     
938     /*
939      * Convert bits-per-pixel to a hardware palette PBS value.
940      */
941     static inline u_int
942     palette_pbs(struct fb_var_screeninfo *var)
943     {
944     	int ret = 0;
945     	switch (var->bits_per_pixel) {
946     #ifdef FBCON_HAS_CFB4
947     	case 4:  ret = 0 << 12;	break;
948     #endif
949     #ifdef FBCON_HAS_CFB8
950     	case 8:  ret = 1 << 12; break;
951     #endif
952     #ifdef FBCON_HAS_CFB16
953     	case 16: ret = 2 << 12; break;
954     #endif
955     	}
956     	return ret;
957     }
958     
959     static int
960     sa1100fb_setpalettereg(u_int regno, u_int red, u_int green, u_int blue,
961     		       u_int trans, struct fb_info *info)
962     {
963     	struct sa1100fb_info *fbi = (struct sa1100fb_info *)info;
964     	u_int val, ret = 1;
965     
966     	if (regno < fbi->palette_size) {
967     		val = ((red >> 4) & 0xf00);
968     		val |= ((green >> 8) & 0x0f0);
969     		val |= ((blue >> 12) & 0x00f);
970     
971     		if (regno == 0)
972     			val |= palette_pbs(&fbi->fb.var);
973     
974     		fbi->palette_cpu[regno] = val;
975     		ret = 0;
976     	}
977     	return ret;
978     }
979     
980     static int
981     sa1100fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
982     		   u_int trans, struct fb_info *info)
983     {
984     	struct sa1100fb_info *fbi = (struct sa1100fb_info *)info;
985     	struct display *disp = get_con_display(info, fbi->currcon);
986     	u_int val;
987     	int ret = 1;
988     
989     	/*
990     	 * If inverse mode was selected, invert all the colours
991     	 * rather than the register number.  The register number
992     	 * is what you poke into the framebuffer to produce the
993     	 * colour you requested.
994     	 */
995     	if (disp->inverse) {
996     		red   = 0xffff - red;
997     		green = 0xffff - green;
998     		blue  = 0xffff - blue;
999     	}
1000     
1001     	/*
1002     	 * If greyscale is true, then we convert the RGB value
1003     	 * to greyscale no mater what visual we are using.
1004     	 */
1005     	if (fbi->fb.var.grayscale)
1006     		red = green = blue = (19595 * red + 38470 * green +
1007     					7471 * blue) >> 16;
1008     
1009     	switch (fbi->fb.disp->visual) {
1010     	case FB_VISUAL_TRUECOLOR:
1011     		/*
1012     		 * 12 or 16-bit True Colour.  We encode the RGB value
1013     		 * according to the RGB bitfield information.
1014     		 */
1015     		if (regno < 16) {
1016     			u16 *pal = fbi->fb.pseudo_palette;
1017     
1018     			val  = chan_to_field(red, &fbi->fb.var.red);
1019     			val |= chan_to_field(green, &fbi->fb.var.green);
1020     			val |= chan_to_field(blue, &fbi->fb.var.blue);
1021     
1022     			pal[regno] = val;
1023     			ret = 0;
1024     		}
1025     		break;
1026     
1027     	case FB_VISUAL_STATIC_PSEUDOCOLOR:
1028     	case FB_VISUAL_PSEUDOCOLOR:
1029     		ret = sa1100fb_setpalettereg(regno, red, green, blue, trans, info);
1030     		break;
1031     	}
1032     
1033     	return ret;
1034     }
1035     
1036     /*
1037      *  sa1100fb_display_dma_period()
1038      *    Calculate the minimum period (in picoseconds) between two DMA
1039      *    requests for the LCD controller.
1040      */
1041     static unsigned int
1042     sa1100fb_display_dma_period(struct fb_var_screeninfo *var)
1043     {
1044     	unsigned int mem_bits_per_pixel;
1045     
1046     	mem_bits_per_pixel = var->bits_per_pixel;
1047     	if (mem_bits_per_pixel == 12)
1048     		mem_bits_per_pixel = 16;
1049     
1050     	/*
1051     	 * Period = pixclock * bits_per_byte * bytes_per_transfer
1052     	 *		/ memory_bits_per_pixel;
1053     	 */
1054     	return var->pixclock * 8 * 16 / mem_bits_per_pixel;
1055     }
1056     
1057     /*
1058      *  sa1100fb_decode_var():
1059      *    Get the video params out of 'var'. If a value doesn't fit, round it up,
1060      *    if it's too big, return -EINVAL.
1061      *
1062      *    Suggestion: Round up in the following order: bits_per_pixel, xres,
1063      *    yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale,
1064      *    bitfields, horizontal timing, vertical timing.
1065      */
1066     static int
1067     sa1100fb_validate_var(struct fb_var_screeninfo *var,
1068     		      struct sa1100fb_info *fbi)
1069     {
1070     	int ret = -EINVAL;
1071     
1072     	if (var->xres < MIN_XRES)
1073     		var->xres = MIN_XRES;
1074     	if (var->yres < MIN_YRES)
1075     		var->yres = MIN_YRES;
1076     	if (var->xres > fbi->max_xres)
1077     		var->xres = fbi->max_xres;
1078     	if (var->yres > fbi->max_yres)
1079     		var->yres = fbi->max_yres;
1080     	var->xres_virtual =
1081     	    var->xres_virtual < var->xres ? var->xres : var->xres_virtual;
1082     	var->yres_virtual =
1083     	    var->yres_virtual < var->yres ? var->yres : var->yres_virtual;
1084     
1085     	DPRINTK("var->bits_per_pixel=%d\n", var->bits_per_pixel);
1086     	switch (var->bits_per_pixel) {
1087     #ifdef FBCON_HAS_CFB4
1088     	case 4:		ret = 0; break;
1089     #endif
1090     #ifdef FBCON_HAS_CFB8
1091     	case 8:		ret = 0; break;
1092     #endif
1093     #ifdef FBCON_HAS_CFB16
1094     	case 16:	ret = 0; break;
1095     #endif
1096     	default:
1097     		break;
1098     	}
1099     
1100     	printk(KERN_DEBUG "dma period = %d ps, clock = %d kHz",
1101     		sa1100fb_display_dma_period(var),
1102     		cpufreq_get(smp_processor_id()));
1103     
1104     	return ret;
1105     }
1106     
1107     static inline void sa1100fb_set_truecolor(u_int is_true_color)
1108     {
1109     	DPRINTK("true_color = %d\n", is_true_color);
1110     #ifdef CONFIG_SA1100_ASSABET
1111     	if (machine_is_assabet()) {
1112     #if 1
1113     		// phase 4 or newer Assabet's
1114     		if (is_true_color)
1115     			BCR_set(BCR_LCD_12RGB);
1116     		else
1117     			BCR_clear(BCR_LCD_12RGB);
1118     #else
1119     		// older Assabet's
1120     		if (is_true_color)
1121     			BCR_clear(BCR_LCD_12RGB);
1122     		else
1123     			BCR_set(BCR_LCD_12RGB);
1124     #endif
1125     	}
1126     #endif
1127     }
1128     
1129     static void
1130     sa1100fb_hw_set_var(struct fb_var_screeninfo *var, struct sa1100fb_info *fbi)
1131     {
1132     	u_long palette_mem_size;
1133     
1134     	fbi->palette_size = var->bits_per_pixel == 8 ? 256 : 16;
1135     
1136     	palette_mem_size = fbi->palette_size * sizeof(u16);
1137     
1138     	DPRINTK("palette_mem_size = 0x%08lx\n", (u_long) palette_mem_size);
1139     
1140     	fbi->palette_cpu = (u16 *)(fbi->map_cpu + PAGE_SIZE - palette_mem_size);
1141     	fbi->palette_dma = fbi->map_dma + PAGE_SIZE - palette_mem_size;
1142     
1143     	fb_set_cmap(&fbi->fb.cmap, 1, sa1100fb_setcolreg, &fbi->fb);
1144     
1145     	/* Set board control register to handle new color depth */
1146     	sa1100fb_set_truecolor(var->bits_per_pixel >= 16);
1147     
1148     #ifdef CONFIG_SA1100_OMNIMETER
1149     #error Do we have to do this here?   We already do it at init time.
1150     	if (machine_is_omnimeter())
1151     		SetLCDContrast(DefaultLCDContrast);
1152     #endif
1153     
1154     	sa1100fb_activate_var(var, fbi);
1155     
1156     	fbi->palette_cpu[0] = (fbi->palette_cpu[0] &
1157     					 0xcfff) | palette_pbs(var);
1158     
1159     }
1160     
1161     /*
1162      * sa1100fb_set_var():
1163      *	Set the user defined part of the display for the specified console
1164      */
1165     static int
1166     sa1100fb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
1167     {
1168     	struct sa1100fb_info *fbi = (struct sa1100fb_info *)info;
1169     	struct fb_var_screeninfo *dvar = get_con_var(&fbi->fb, con);
1170     	struct display *display = get_con_display(&fbi->fb, con);
1171     	int err, chgvar = 0, rgbidx;
1172     
1173     	DPRINTK("set_var\n");
1174     
1175     	/*
1176     	 * Decode var contents into a par structure, adjusting any
1177     	 * out of range values.
1178     	 */
1179     	err = sa1100fb_validate_var(var, fbi);
1180     	if (err)
1181     		return err;
1182     
1183     	if (var->activate & FB_ACTIVATE_TEST)
1184     		return 0;
1185     
1186     	if ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW)
1187     		return -EINVAL;
1188     
1189     	if (dvar->xres != var->xres)
1190     		chgvar = 1;
1191     	if (dvar->yres != var->yres)
1192     		chgvar = 1;
1193     	if (dvar->xres_virtual != var->xres_virtual)
1194     		chgvar = 1;
1195     	if (dvar->yres_virtual != var->yres_virtual)
1196     		chgvar = 1;
1197     	if (dvar->bits_per_pixel != var->bits_per_pixel)
1198     		chgvar = 1;
1199     	if (con < 0)
1200     		chgvar = 0;
1201     
1202     	switch (var->bits_per_pixel) {
1203     #ifdef FBCON_HAS_CFB4
1204     	case 4:
1205     		if (fbi->cmap_static)
1206     			display->visual	= FB_VISUAL_STATIC_PSEUDOCOLOR;
1207     		else
1208     			display->visual	= FB_VISUAL_PSEUDOCOLOR;
1209     		display->line_length	= var->xres / 2;
1210     		display->dispsw		= &fbcon_cfb4;
1211     		rgbidx			= RGB_8;
1212     		break;
1213     #endif
1214     #ifdef FBCON_HAS_CFB8
1215     	case 8:
1216     		if (fbi->cmap_static)
1217     			display->visual	= FB_VISUAL_STATIC_PSEUDOCOLOR;
1218     		else
1219     			display->visual	= FB_VISUAL_PSEUDOCOLOR;
1220     		display->line_length	= var->xres;
1221     		display->dispsw		= &fbcon_cfb8;
1222     		rgbidx			= RGB_8;
1223     		break;
1224     #endif
1225     #ifdef FBCON_HAS_CFB16
1226     	case 16:
1227     		display->visual		= FB_VISUAL_TRUECOLOR;
1228     		display->line_length	= var->xres * 2;
1229     		display->dispsw		= &fbcon_cfb16;
1230     		display->dispsw_data	= fbi->fb.pseudo_palette;
1231     		rgbidx			= RGB_16;
1232     		break;
1233     #endif
1234     	default:
1235     		rgbidx = 0;
1236     		display->dispsw = &fbcon_dummy;
1237     		break;
1238     	}
1239     
1240     	display->screen_base	= fbi->screen_cpu;
1241     	display->next_line	= display->line_length;
1242     	display->type		= fbi->fb.fix.type;
1243     	display->type_aux	= fbi->fb.fix.type_aux;
1244     	display->ypanstep	= fbi->fb.fix.ypanstep;
1245     	display->ywrapstep	= fbi->fb.fix.ywrapstep;
1246     	display->can_soft_blank	= 1;
1247     	display->inverse	= fbi->cmap_inverse;
1248     
1249     	*dvar			= *var;
1250     	dvar->activate		&= ~FB_ACTIVATE_ALL;
1251     
1252     	/*
1253     	 * Copy the RGB parameters for this display
1254     	 * from the machine specific parameters.
1255     	 */
1256     	dvar->red		= fbi->rgb[rgbidx]->red;
1257     	dvar->green		= fbi->rgb[rgbidx]->green;
1258     	dvar->blue		= fbi->rgb[rgbidx]->blue;
1259     	dvar->transp		= fbi->rgb[rgbidx]->transp;
1260     
1261     	DPRINTK("RGBT length = %d:%d:%d:%d\n",
1262     		dvar->red.length, dvar->green.length, dvar->blue.length,
1263     		dvar->transp.length);
1264     
1265     	DPRINTK("RGBT offset = %d:%d:%d:%d\n",
1266     		dvar->red.offset, dvar->green.offset, dvar->blue.offset,
1267     		dvar->transp.offset);
1268     
1269     	/*
1270     	 * Update the old var.  The fbcon drivers still use this.
1271     	 * Once they are using fbi->fb.var, this can be dropped.
1272     	 */
1273     	display->var = *dvar;
1274     
1275     	/*
1276     	 * If we are setting all the virtual consoles, also set the
1277     	 * defaults used to create new consoles.
1278     	 */
1279     	if (var->activate & FB_ACTIVATE_ALL)
1280     		fbi->fb.disp->var = *dvar;
1281     
1282     	/*
1283     	 * If the console has changed and the console has defined
1284     	 * a changevar function, call that function.
1285     	 */
1286     	if (chgvar && info && fbi->fb.changevar)
1287     		fbi->fb.changevar(con);
1288     
1289     	/* If the current console is selected, activate the new var. */
1290     	if (con != fbi->currcon)
1291     		return 0;
1292     
1293     	sa1100fb_hw_set_var(dvar, fbi);
1294     
1295     	return 0;
1296     }
1297     
1298     static int
1299     __do_set_cmap(struct fb_cmap *cmap, int kspc, int con,
1300     	      struct fb_info *info)
1301     {
1302     	struct sa1100fb_info *fbi = (struct sa1100fb_info *)info;
1303     	struct fb_cmap *dcmap = get_con_cmap(info, con);
1304     	int err = 0;
1305     
1306     	if (con == -1)
1307     		con = fbi->currcon;
1308     
1309     	/* no colormap allocated? (we always have "this" colour map allocated) */
1310     	if (con >= 0)
1311     		err = fb_alloc_cmap(&fb_display[con].cmap, fbi->palette_size, 0);
1312     
1313     	if (!err && con == fbi->currcon)
1314     		err = fb_set_cmap(cmap, kspc, sa1100fb_setcolreg, info);
1315     
1316     	if (!err)
1317     		fb_copy_cmap(cmap, dcmap, kspc ? 0 : 1);
1318     
1319     	return err;
1320     }
1321     
1322     static int
1323     sa1100fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
1324     		  struct fb_info *info)
1325     {
1326     	struct display *disp = get_con_display(info, con);
1327     
1328     	if (disp->visual == FB_VISUAL_TRUECOLOR ||
1329     	    disp->visual == FB_VISUAL_STATIC_PSEUDOCOLOR)
1330     		return -EINVAL;
1331     
1332     	return __do_set_cmap(cmap, kspc, con, info);
1333     }
1334     
1335     static int
1336     sa1100fb_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info)
1337     {
1338     	struct display *display = get_con_display(info, con);
1339     
1340     	*fix = info->fix;
1341     
1342     	fix->line_length = display->line_length;
1343     	fix->visual	 = display->visual;
1344     	return 0;
1345     }
1346     
1347     static int
1348     sa1100fb_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
1349     {
1350     	*var = *get_con_var(info, con);
1351     	return 0;
1352     }
1353     
1354     static int
1355     sa1100fb_get_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info)
1356     {
1357     	struct fb_cmap *dcmap = get_con_cmap(info, con);
1358     	fb_copy_cmap(dcmap, cmap, kspc ? 0 : 2);
1359     	return 0;
1360     }
1361     
1362     static struct fb_ops sa1100fb_ops = {
1363     	owner:		THIS_MODULE,
1364     	fb_get_fix:	sa1100fb_get_fix,
1365     	fb_get_var:	sa1100fb_get_var,
1366     	fb_set_var:	sa1100fb_set_var,
1367     	fb_get_cmap:	sa1100fb_get_cmap,
1368     	fb_set_cmap:	sa1100fb_set_cmap,
1369     };
1370     
1371     /*
1372      *  sa1100fb_switch():       
1373      *	Change to the specified console.  Palette and video mode
1374      *      are changed to the console's stored parameters.
1375      *
1376      *	Uh oh, this can be called from a tasklet (IRQ)
1377      */
1378     static int sa1100fb_switch(int con, struct fb_info *info)
1379     {
1380     	struct sa1100fb_info *fbi = (struct sa1100fb_info *)info;
1381     	struct display *disp;
1382     	struct fb_cmap *cmap;
1383     
1384     	DPRINTK("con=%d info->modename=%s\n", con, fbi->fb.modename);
1385     
1386     	if (con == fbi->currcon)
1387     		return 0;
1388     
1389     	if (fbi->currcon >= 0) {
1390     		disp = fb_display + fbi->currcon;
1391     
1392     		/*
1393     		 * Save the old colormap and video mode.
1394     		 */
1395     		disp->var = fbi->fb.var;
1396     
1397     		if (disp->cmap.len)
1398     			fb_copy_cmap(&fbi->fb.cmap, &disp->cmap, 0);
1399     	}
1400     
1401     	fbi->currcon = con;
1402     	disp = fb_display + con;
1403     
1404     	/*
1405     	 * Make sure that our colourmap contains 256 entries.
1406     	 */
1407     	fb_alloc_cmap(&fbi->fb.cmap, 256, 0);
1408     
1409     	if (disp->cmap.len)
1410     		cmap = &disp->cmap;
1411     	else
1412     		cmap = fb_default_cmap(1 << disp->var.bits_per_pixel);
1413     
1414     	fb_copy_cmap(cmap, &fbi->fb.cmap, 0);
1415     
1416     	fbi->fb.var = disp->var;
1417     	fbi->fb.var.activate = FB_ACTIVATE_NOW;
1418     
1419     	sa1100fb_set_var(&fbi->fb.var, con, info);
1420     	return 0;
1421     }
1422     
1423     /*
1424      * Formal definition of the VESA spec:
1425      *  On
1426      *  	This refers to the state of the display when it is in full operation
1427      *  Stand-By
1428      *  	This defines an optional operating state of minimal power reduction with
1429      *  	the shortest recovery time
1430      *  Suspend
1431      *  	This refers to a level of power management in which substantial power
1432      *  	reduction is achieved by the display.  The display can have a longer 
1433      *  	recovery time from this state than from the Stand-by state
1434      *  Off
1435      *  	This indicates that the display is consuming the lowest level of power
1436      *  	and is non-operational. Recovery from this state may optionally require
1437      *  	the user to manually power on the monitor
1438      *
1439      *  Now, the fbdev driver adds an additional state, (blank), where they
1440      *  turn off the video (maybe by colormap tricks), but don't mess with the
1441      *  video itself: think of it semantically between on and Stand-By.
1442      *
1443      *  So here's what we should do in our fbdev blank routine:
1444      *
1445      *  	VESA_NO_BLANKING (mode 0)	Video on,  front/back light on
1446      *  	VESA_VSYNC_SUSPEND (mode 1)  	Video on,  front/back light off
1447      *  	VESA_HSYNC_SUSPEND (mode 2)  	Video on,  front/back light off
1448      *  	VESA_POWERDOWN (mode 3)		Video off, front/back light off
1449      *
1450      *  This will match the matrox implementation.
1451      */
1452     /*
1453      * sa1100fb_blank():
1454      *	Blank the display by setting all palette values to zero.  Note, the 
1455      * 	12 and 16 bpp modes don't really use the palette, so this will not
1456      *      blank the display in all modes.  
1457      */
1458     static void sa1100fb_blank(int blank, struct fb_info *info)
1459     {
1460     	struct sa1100fb_info *fbi = (struct sa1100fb_info *)info;
1461     	int i;
1462     
1463     	DPRINTK("sa1100fb_blank: blank=%d info->modename=%s\n", blank,
1464     		fbi->fb.modename);
1465     
1466     	switch (blank) {
1467     	case VESA_POWERDOWN:
1468     	case VESA_VSYNC_SUSPEND:
1469     	case VESA_HSYNC_SUSPEND:
1470     		if (fbi->fb.disp->visual == FB_VISUAL_PSEUDOCOLOR ||
1471     		    fbi->fb.disp->visual == FB_VISUAL_STATIC_PSEUDOCOLOR)
1472     			for (i = 0; i < fbi->palette_size; i++)
1473     				sa1100fb_setpalettereg(i, 0, 0, 0, 0, info);
1474     		sa1100fb_schedule_task(fbi, C_DISABLE);
1475     		if (sa1100fb_blank_helper)
1476     			sa1100fb_blank_helper(blank);
1477     		break;
1478     
1479     	case VESA_NO_BLANKING:
1480     		if (sa1100fb_blank_helper)
1481     			sa1100fb_blank_helper(blank);
1482     		if (fbi->fb.disp->visual == FB_VISUAL_PSEUDOCOLOR ||
1483     		    fbi->fb.disp->visual == FB_VISUAL_STATIC_PSEUDOCOLOR)
1484     			fb_set_cmap(&fbi->fb.cmap, 1, sa1100fb_setcolreg, info);
1485     		sa1100fb_schedule_task(fbi, C_ENABLE);
1486     	}
1487     }
1488     
1489     static int sa1100fb_updatevar(int con, struct fb_info *info)
1490     {
1491     	DPRINTK("entered\n");
1492     	return 0;
1493     }
1494     
1495     /*
1496      * Calculate the PCD value from the clock rate (in picoseconds).
1497      * We take account of the PPCR clock setting.
1498      */
1499     static inline int get_pcd(unsigned int pixclock)
1500     {
1501     	unsigned int pcd;
1502     
1503     	if (pixclock) {
1504     		pcd = get_cclk_frequency() * pixclock;
1505     		pcd /= 10000000;
1506     		pcd += 1;	/* make up for integer math truncations */
1507     	} else {
1508     		/*
1509     		 * People seem to be missing this message.  Make it big.
1510     		 * Make it stand out.  Make sure people see it.
1511     		 */
1512     		printk(KERN_WARNING "******************************************************\n");
1513     		printk(KERN_WARNING "**            ZERO PIXEL CLOCK DETECTED             **\n");
1514     		printk(KERN_WARNING "** You are using a zero pixclock.  This means that  **\n");
1515     		printk(KERN_WARNING "** clock scaling will not be able to adjust your    **\n");
1516     		printk(KERN_WARNING "** your timing parameters appropriately, and the    **\n");
1517     		printk(KERN_WARNING "** bandwidth calculations will fail to work.  This  **\n");
1518     		printk(KERN_WARNING "** will shortly become an error condition, which    **\n");
1519     		printk(KERN_WARNING "** will prevent your LCD display working.  Please   **\n");
1520     		printk(KERN_WARNING "** send your patches in as soon as possible to shut **\n");
1521     		printk(KERN_WARNING "** this message up.                                 **\n");
1522     		printk(KERN_WARNING "******************************************************\n");
1523     		pcd = 0;
1524     	}
1525     	return pcd;
1526     }
1527     
1528     /*
1529      * sa1100fb_activate_var():
1530      *	Configures LCD Controller based on entries in var parameter.  Settings are      
1531      *      only written to the controller if changes were made.  
1532      */
1533     static int sa1100fb_activate_var(struct fb_var_screeninfo *var, struct sa1100fb_info *fbi)
1534     {
1535     	struct sa1100fb_lcd_reg new_regs;
1536     	u_int half_screen_size, yres, pcd = get_pcd(var->pixclock);
1537     	u_long flags;
1538     
1539     	DPRINTK("Configuring SA1100 LCD\n");
1540     
1541     	DPRINTK("var: xres=%d hslen=%d lm=%d rm=%d\n",
1542     		var->xres, var->hsync_len,
1543     		var->left_margin, var->right_margin);
1544     	DPRINTK("var: yres=%d vslen=%d um=%d bm=%d\n",
1545     		var->yres, var->vsync_len,
1546     		var->upper_margin, var->lower_margin);
1547     
1548     #if DEBUG_VAR
1549     	if (var->xres < 16        || var->xres > 1024)
1550     		printk(KERN_ERR "%s: invalid xres %d\n",
1551     			fbi->fb.fix.id, var->xres);
1552     	if (var->hsync_len < 1    || var->hsync_len > 64)
1553     		printk(KERN_ERR "%s: invalid hsync_len %d\n",
1554     			fbi->fb.fix.id, var->hsync_len);
1555     	if (var->left_margin < 1  || var->left_margin > 255)
1556     		printk(KERN_ERR "%s: invalid left_margin %d\n",
1557     			fbi->fb.fix.id, var->left_margin);
1558     	if (var->right_margin < 1 || var->right_margin > 255)
1559     		printk(KERN_ERR "%s: invalid right_margin %d\n",
1560     			fbi->fb.fix.id, var->right_margin);
1561     	if (var->yres < 1         || var->yres > 1024)
1562     		printk(KERN_ERR "%s: invalid yres %d\n",
1563     			fbi->fb.fix.id, var->yres);
1564     	if (var->vsync_len < 1    || var->vsync_len > 64)
1565     		printk(KERN_ERR "%s: invalid vsync_len %d\n",
1566     			fbi->fb.fix.id, var->vsync_len);
1567     	if (var->upper_margin < 0 || var->upper_margin > 255)
1568     		printk(KERN_ERR "%s: invalid upper_margin %d\n",
1569     			fbi->fb.fix.id, var->upper_margin);
1570     	if (var->lower_margin < 0 || var->lower_margin > 255)
1571     		printk(KERN_ERR "%s: invalid lower_margin %d\n",
1572     			fbi->fb.fix.id, var->lower_margin);
1573     #endif
1574     
1575     	new_regs.lccr0 = fbi->lccr0 |
1576     		LCCR0_LEN | LCCR0_LDM | LCCR0_BAM |
1577     		LCCR0_ERM | LCCR0_LtlEnd | LCCR0_DMADel(0);
1578     
1579     	new_regs.lccr1 =
1580     		LCCR1_DisWdth(var->xres) +
1581     		LCCR1_HorSnchWdth(var->hsync_len) +
1582     		LCCR1_BegLnDel(var->left_margin) +
1583     		LCCR1_EndLnDel(var->right_margin);
1584     
1585     	/*
1586     	 * If we have a dual scan LCD, then we need to halve
1587     	 * the YRES parameter.
1588     	 */
1589     	yres = var->yres;
1590     	if (fbi->lccr0 & LCCR0_Dual)
1591     		yres /= 2;
1592     
1593     	new_regs.lccr2 =
1594     		LCCR2_DisHght(yres) +
1595     		LCCR2_VrtSnchWdth(var->vsync_len) +
1596     		LCCR2_BegFrmDel(var->upper_margin) +
1597     		LCCR2_EndFrmDel(var->lower_margin);
1598     
1599     	new_regs.lccr3 = fbi->lccr3 |
1600     		(var->sync & FB_SYNC_HOR_HIGH_ACT ? LCCR3_HorSnchH : LCCR3_HorSnchL) |
1601     		(var->sync & FB_SYNC_VERT_HIGH_ACT ? LCCR3_VrtSnchH : LCCR3_VrtSnchL) |
1602     		LCCR3_ACBsCntOff;
1603     
1604     	if (pcd)
1605     		new_regs.lccr3 |= LCCR3_PixClkDiv(pcd);
1606     
1607     	sa1100fb_check_shadow(&new_regs, var, pcd);
1608     
1609     	DPRINTK("nlccr0 = 0x%08x\n", new_regs.lccr0);
1610     	DPRINTK("nlccr1 = 0x%08x\n", new_regs.lccr1);
1611     	DPRINTK("nlccr2 = 0x%08x\n", new_regs.lccr2);
1612     	DPRINTK("nlccr3 = 0x%08x\n", new_regs.lccr3);
1613     
1614     	half_screen_size = var->bits_per_pixel;
1615     	half_screen_size = half_screen_size * var->xres * var->yres / 16;
1616     
1617     	/* Update shadow copy atomically */
1618     	local_irq_save(flags);
1619     	fbi->dbar1 = fbi->palette_dma;
1620     	fbi->dbar2 = fbi->screen_dma + half_screen_size;
1621     
1622     	fbi->reg_lccr0 = new_regs.lccr0;
1623     	fbi->reg_lccr1 = new_regs.lccr1;
1624     	fbi->reg_lccr2 = new_regs.lccr2;
1625     	fbi->reg_lccr3 = new_regs.lccr3;
1626     	local_irq_restore(flags);
1627     
1628     	/*
1629     	 * Only update the registers if the controller is enabled
1630     	 * and something has changed.
1631     	 */
1632     	if ((LCCR0 != fbi->reg_lccr0)       || (LCCR1 != fbi->reg_lccr1) ||
1633     	    (LCCR2 != fbi->reg_lccr2)       || (LCCR3 != fbi->reg_lccr3) ||
1634     	    (DBAR1 != (Address) fbi->dbar1) || (DBAR2 != (Address) fbi->dbar2))
1635     		sa1100fb_schedule_task(fbi, C_REENABLE);
1636     
1637     	return 0;
1638     }
1639     
1640     /*
1641      * NOTE!  The following functions are purely helpers for set_ctrlr_state.
1642      * Do not call them directly; set_ctrlr_state does the correct serialisation
1643      * to ensure that things happen in the right way 100% of time time.
1644      *	-- rmk
1645      */
1646     
1647     /*
1648      * FIXME: move LCD power stuff into sa1100fb_power_up_lcd()
1649      * Also, I'm expecting that the backlight stuff should
1650      * be handled differently.
1651      */
1652     static void sa1100fb_backlight_on(struct sa1100fb_info *fbi)
1653     {
1654     	DPRINTK("backlight on\n");
1655     
1656     #ifdef CONFIG_SA1100_FREEBIRD
1657     #error FIXME
1658     	if (machine_is_freebird()) {
1659     		BCR_set(BCR_FREEBIRD_LCD_PWR | BCR_FREEBIRD_LCD_DISP);
1660     	}
1661     #endif
1662     #ifdef CONFIG_SA1100_FREEBIRD
1663     	if (machine_is_freebird()) {
1664     		/* Turn on backlight ,Chester */
1665     		BCR_set(BCR_FREEBIRD_LCD_BACKLIGHT);
1666     	}
1667     #endif
1668     #ifdef CONFIG_SA1100_HUW_WEBPANEL
1669     #error FIXME
1670     	if (machine_is_huw_webpanel()) {
1671     		BCR_set(BCR_CCFL_POW + BCR_PWM_BACKLIGHT);
1672     		set_current_state(TASK_UNINTERRUPTIBLE);
1673     		schedule_task(200 * HZ / 1000);
1674     		BCR_set(BCR_TFT_ENA);
1675     	}
1676     #endif
1677     #ifdef CONFIG_SA1100_OMNIMETER
1678     	if (machine_is_omnimeter())
1679     		LEDBacklightOn();
1680     #endif
1681     #ifdef CONFIG_SA1100_BITSY
1682     	/* what rmk said --dneuer */
1683     #endif
1684     }
1685     
1686     /*
1687      * FIXME: move LCD power stuf into sa1100fb_power_down_lcd()
1688      * Also, I'm expecting that the backlight stuff should
1689      * be handled differently.
1690      */
1691     static void sa1100fb_backlight_off(struct sa1100fb_info *fbi)
1692     {
1693     	DPRINTK("backlight off\n");
1694     
1695     #ifdef CONFIG_SA1100_FREEBIRD
1696     #error FIXME
1697     	if (machine_is_freebird()) {
1698     		BCR_clear(BCR_FREEBIRD_LCD_PWR | BCR_FREEBIRD_LCD_DISP
1699     			  /*| BCR_FREEBIRD_LCD_BACKLIGHT */ );
1700     	}
1701     #endif
1702     #ifdef CONFIG_SA1100_OMNIMETER
1703     	if (machine_is_omnimeter())
1704     		LEDBacklightOff();
1705     #endif
1706     #ifdef CONFIG_SA1100_BITSY
1707     	/* what rmk said --dneuer */
1708     #endif
1709     }
1710     
1711     static void sa1100fb_power_up_lcd(struct sa1100fb_info *fbi)
1712     {
1713     	DPRINTK("LCD power on\n");
1714     
1715     #if defined(CONFIG_SA1100_ASSABET) && !defined(ASSABET_PAL_VIDEO)
1716     	if (machine_is_assabet())
1717     		BCR_set(BCR_LCD_ON);
1718     #endif
1719     #ifdef CONFIG_SA1100_HUW_WEBPANEL
1720     	if (machine_is_huw_webpanel())
1721     		BCR_clear(BCR_TFT_NPWR);
1722     #endif
1723     #ifdef CONFIG_SA1100_OMNIMETER
1724     	if (machine_is_omnimeter())
1725     		LCDPowerOn();
1726     #endif
1727     #ifdef CONFIG_SA1100_BITSY
1728     	if (machine_is_bitsy()) {
1729     		set_bitsy_egpio(EGPIO_BITSY_LCD_ON |
1730     				EGPIO_BITSY_LCD_PCI |
1731     				EGPIO_BITSY_LCD_5V_ON |
1732     				EGPIO_BITSY_LVDD_ON);
1733     	}
1734     #endif
1735     }
1736     
1737     static void sa1100fb_power_down_lcd(struct sa1100fb_info *fbi)
1738     {
1739     	DPRINTK("LCD power off\n");
1740     
1741     #if defined(CONFIG_SA1100_ASSABET) && !defined(ASSABET_PAL_VIDEO)
1742     	if (machine_is_assabet())
1743     		BCR_clear(BCR_LCD_ON);
1744     #endif
1745     #ifdef CONFIG_SA1100_HUW_WEBPANEL
1746     	// dont forget to set the control lines to zero (?)
1747     	if (machine_is_huw_webpanel())
1748     		BCR_set(BCR_TFT_NPWR);
1749     #endif
1750     #ifdef CONFIG_SA1100_BITSY
1751     	if (machine_is_bitsy()) {
1752     		clr_bitsy_egpio(EGPIO_BITSY_LCD_ON |
1753     				EGPIO_BITSY_LCD_PCI |
1754     				EGPIO_BITSY_LCD_5V_ON |
1755     				EGPIO_BITSY_LVDD_ON);
1756     	}
1757     #endif
1758     }
1759     
1760     static void sa1100fb_setup_gpio(struct sa1100fb_info *fbi)
1761     {
1762     	u_int mask = 0;
1763     
1764     	/*
1765     	 * Enable GPIO<9:2> for LCD use if:
1766     	 *  1. Active display, or
1767     	 *  2. Color Dual Passive display
1768     	 *
1769     	 * see table 11.8 on page 11-27 in the SA1100 manual
1770     	 *   -- Erik.
1771     	 *
1772     	 * SA1110 spec update nr. 25 says we can and should
1773     	 * clear LDD15 to 12 for 4 or 8bpp modes with active
1774     	 * panels.  
1775     	 */
1776     	if ((fbi->reg_lccr0 & LCCR0_CMS) == LCCR0_Color &&
1777     	    (fbi->reg_lccr0 & (LCCR0_Dual|LCCR0_Act)) != 0) {
1778     		mask = GPIO_LDD11 | GPIO_LDD10 | GPIO_LDD9  | GPIO_LDD8;
1779     
1780     		if (fbi->fb.var.bits_per_pixel > 8 ||
1781     		    (fbi->reg_lccr0 & (LCCR0_Dual|LCCR0_Act)) == LCCR0_Dual)
1782     			mask |= GPIO_LDD15 | GPIO_LDD14 | GPIO_LDD13 | GPIO_LDD12;
1783     
1784     	}
1785     
1786     #ifdef CONFIG_SA1100_FREEBIRD
1787     #error Please contact <rmk@arm.linux.org.uk> about this
1788     	if (machine_is_freebird()) {
1789     		/* Color single passive */
1790     		mask |= GPIO_LDD15 | GPIO_LDD14 | GPIO_LDD13 | GPIO_LDD12 |
1791     			GPIO_LDD11 | GPIO_LDD10 | GPIO_LDD9  | GPIO_LDD8;
1792     	}
1793     #endif
1794     #ifdef CONFIG_SA1100_CERF
1795     #error Please contact <rmk@arm.linux.org.uk> about this
1796     	if (machine_is_cerf()) {
1797     		/* GPIO15 is used as a bypass for 3.8" displays */
1798     		mask |= GPIO_GPIO15;
1799     
1800     		/* FIXME: why is this? The Cerf's display doesn't seem
1801     		 * to be dual scan or active. I just leave it here,
1802     		 * but in my opinion this is definitively wrong.
1803     		 *  -- Erik <J.A.K.Mouw@its.tudelft.nl>
1804     		 */
1805     
1806     		/* REPLY: Umm.. Well to be honest, the 5.7" LCD which
1807     		 * this was used for does not use these pins, but
1808     		 * apparently all hell breaks loose if they are not
1809     		 * set on the Cerf, so we decided to leave them in ;)
1810     		 *  -- Daniel Chemko <dchemko@intrinsyc.com>
1811     		 */
1812     		/* color {dual/single} passive */
1813     		mask |= GPIO_LDD15 | GPIO_LDD14 | GPIO_LDD13 | GPIO_LDD12 |
1814     			GPIO_LDD11 | GPIO_LDD10 | GPIO_LDD9  | GPIO_LDD8;
1815     	}
1816     #endif
1817     
1818     	if (mask) {
1819     		GPDR |= mask;
1820     		GAFR |= mask;
1821     	}
1822     }
1823     
1824     static void sa1100fb_enable_controller(struct sa1100fb_info *fbi)
1825     {
1826     	DPRINTK("Enabling LCD controller\n");
1827     
1828     	/*
1829     	 * Make sure the mode bits are present in the first palette entry
1830     	 */
1831     	fbi->palette_cpu[0] &= 0xcfff;
1832     	fbi->palette_cpu[0] |= palette_pbs(&fbi->fb.var);
1833     
1834     	/* Sequence from 11.7.10 */
1835     	LCCR3 = fbi->reg_lccr3;
1836     	LCCR2 = fbi->reg_lccr2;
1837     	LCCR1 = fbi->reg_lccr1;
1838     	LCCR0 = fbi->reg_lccr0 & ~LCCR0_LEN;
1839     	DBAR1 = (Address) fbi->dbar1;
1840     	DBAR2 = (Address) fbi->dbar2;
1841     	LCCR0 |= LCCR0_LEN;
1842     
1843     #ifdef CONFIG_SA1100_GRAPHICSCLIENT
1844     #error Where is GPIO24 set as an output?  Can we fit this in somewhere else?
1845     	if (machine_is_graphicsclient()) {
1846     		// From ADS doc again...same as disable
1847     		set_current_state(TASK_UNINTERRUPTIBLE);
1848     		schedule_timeout(20 * HZ / 1000);
1849     		GPSR |= GPIO_GPIO24;
1850     	}
1851     #endif
1852     
1853     	DPRINTK("DBAR1 = %p\n", DBAR1);
1854     	DPRINTK("DBAR2 = %p\n", DBAR2);
1855     	DPRINTK("LCCR0 = 0x%08x\n", LCCR0);
1856     	DPRINTK("LCCR1 = 0x%08x\n", LCCR1);
1857     	DPRINTK("LCCR2 = 0x%08x\n", LCCR2);
1858     	DPRINTK("LCCR3 = 0x%08x\n", LCCR3);
1859     }
1860     
1861     static void sa1100fb_disable_controller(struct sa1100fb_info *fbi)
1862     {
1863     	DECLARE_WAITQUEUE(wait, current);
1864     
1865     	DPRINTK("Disabling LCD controller\n");
1866     
1867     #ifdef CONFIG_SA1100_GRAPHICSCLIENT
1868     #error Where is GPIO24 set as an output?  Can we fit this in somewhere else?
1869     	if (machine_is_graphicsclient()) {
1870     		/*
1871     		 * From ADS internal document:
1872     		 *  GPIO24 should be LOW at least 10msec prior to disabling
1873     		 *  the LCD interface.
1874     		 *
1875     		 * We'll wait 20msec.
1876     		 */
1877     		GPCR |= GPIO_GPIO24;
1878     		set_current_state(TASK_UNINTERRUPTIBLE);
1879     		schedule_timeout(20 * HZ / 1000);
1880     	}
1881     #endif
1882     #ifdef CONFIG_SA1100_HUW_WEBPANEL
1883     #error Move me into sa1100fb_power_up_lcd and/or sa1100fb_backlight_on
1884     	if (machine_is_huw_webpanel()) {
1885     		// dont forget to set the control lines to zero (?)
1886     		DPRINTK("ShutDown HuW LCD controller\n");
1887     		BCR_clear(BCR_TFT_ENA + BCR_CCFL_POW + BCR_PWM_BACKLIGHT);
1888     	}
1889     #endif
1890     
1891     	add_wait_queue(&fbi->ctrlr_wait, &wait);
1892     	set_current_state(TASK_UNINTERRUPTIBLE);
1893     
1894     	LCSR = 0xffffffff;	/* Clear LCD Status Register */
1895     	LCCR0 &= ~LCCR0_LDM;	/* Enable LCD Disable Done Interrupt */
1896     	enable_irq(IRQ_LCD);	/* Enable LCD IRQ */
1897     	LCCR0 &= ~LCCR0_LEN;	/* Disable LCD Controller */
1898     
1899     	schedule_timeout(20 * HZ / 1000);
1900     	current->state = TASK_RUNNING;
1901     	remove_wait_queue(&fbi->ctrlr_wait, &wait);
1902     }
1903     
1904     /*
1905      *  sa1100fb_handle_irq: Handle 'LCD DONE' interrupts.
1906      */
1907     static void sa1100fb_handle_irq(int irq, void *dev_id, struct pt_regs *regs)
1908     {
1909     	struct sa1100fb_info *fbi = dev_id;
1910     	unsigned int lcsr = LCSR;
1911     
1912     	if (lcsr & LCSR_LDD) {
1913     		LCCR0 |= LCCR0_LDM;
1914     		wake_up(&fbi->ctrlr_wait);
1915     	}
1916     
1917     	LCSR = lcsr;
1918     }
1919     
1920     /*
1921      * This function must be called from task context only, since it will
1922      * sleep when disabling the LCD controller, or if we get two contending
1923      * processes trying to alter state.
1924      */
1925     static void set_ctrlr_state(struct sa1100fb_info *fbi, u_int state)
1926     {
1927     	u_int old_state;
1928     
1929     	down(&fbi->ctrlr_sem);
1930     
1931     	old_state = fbi->state;
1932     
1933     	switch (state) {
1934     	case C_DISABLE_CLKCHANGE:
1935     		/*
1936     		 * Disable controller for clock change.  If the
1937     		 * controller is already disabled, then do nothing.
1938     		 */
1939     		if (old_state != C_DISABLE) {
1940     			fbi->state = state;
1941     			sa1100fb_disable_controller(fbi);
1942     		}
1943     		break;
1944     
1945     	case C_DISABLE:
1946     		/*
1947     		 * Disable controller
1948     		 */
1949     		if (old_state != C_DISABLE) {
1950     			fbi->state = state;
1951     
1952     			sa1100fb_backlight_off(fbi);
1953     			if (old_state != C_DISABLE_CLKCHANGE)
1954     				sa1100fb_disable_controller(fbi);
1955     			sa1100fb_power_down_lcd(fbi);
1956     		}
1957     		break;
1958     
1959     	case C_ENABLE_CLKCHANGE:
1960     		/*
1961     		 * Enable the controller after clock change.  Only
1962     		 * do this if we were disabled for the clock change.
1963     		 */
1964     		if (old_state == C_DISABLE_CLKCHANGE) {
1965     			fbi->state = C_ENABLE;
1966     			sa1100fb_enable_controller(fbi);
1967     		}
1968     		break;
1969     
1970     	case C_REENABLE:
1971     		/*
1972     		 * Re-enable the controller only if it was already
1973     		 * enabled.  This is so we reprogram the control
1974     		 * registers.
1975     		 */
1976     		if (old_state == C_ENABLE) {
1977     			sa1100fb_disable_controller(fbi);
1978     			sa1100fb_setup_gpio(fbi);
1979     			sa1100fb_enable_controller(fbi);
1980     		}
1981     		break;
1982     
1983     	case C_ENABLE:
1984     		/*
1985     		 * Power up the LCD screen, enable controller, and
1986     		 * turn on the backlight.
1987     		 */
1988     		if (old_state != C_ENABLE) {
1989     			fbi->state = C_ENABLE;
1990     			sa1100fb_setup_gpio(fbi);
1991     			sa1100fb_power_up_lcd(fbi);
1992     			sa1100fb_enable_controller(fbi);
1993     			sa1100fb_backlight_on(fbi);
1994     		}
1995     		break;
1996     	}
1997     	up(&fbi->ctrlr_sem);
1998     }
1999     
2000     /*
2001      * Our LCD controller task (which is called when we blank or unblank)
2002      * via keventd.
2003      */
2004     static void sa1100fb_task(void *dummy)
2005     {
2006     	struct sa1100fb_info *fbi = dummy;
2007     	u_int state = xchg(&fbi->task_state, -1);
2008     
2009     	set_ctrlr_state(fbi, state);
2010     }
2011     
2012     #ifdef CONFIG_CPU_FREQ
2013     /*
2014      * Calculate the minimum DMA period over all displays that we own.
2015      * This, together with the SDRAM bandwidth defines the slowest CPU
2016      * frequency that can be selected.
2017      */
2018     static unsigned int sa1100fb_min_dma_period(struct sa1100fb_info *fbi)
2019     {
2020     	unsigned int min_period = (unsigned int)-1;
2021     	int i;
2022     
2023     	for (i = 0; i < MAX_NR_CONSOLES; i++) {
2024     		unsigned int period;
2025     
2026     		/*
2027     		 * Do we own this display?
2028     		 */
2029     		if (fb_display[i].fb_info != &fbi->fb)
2030     			continue;
2031     
2032     		/*
2033     		 * Ok, calculate its DMA period
2034     		 */
2035     		period = sa1100fb_display_dma_period(get_con_var(fbi, i));
2036     		if (period < min_period)
2037     			min_period = period;
2038     	}
2039     
2040     	return min_period;
2041     }
2042     
2043     /*
2044      * CPU clock speed change handler.  We need to adjust the LCD timing
2045      * parameters when the CPU clock is adjusted by the power management
2046      * subsystem.
2047      */
2048     static int
2049     sa1100fb_clkchg_notifier(struct notifier_block *nb, unsigned long val,
2050     			 void *data)
2051     {
2052     	struct sa1100fb_info *fbi = TO_INF(nb, clockchg);
2053     	struct cpufreq_minmax *mm = data;
2054     	u_int pcd;
2055     
2056     	switch (val) {
2057     	case CPUFREQ_MINMAX:
2058     		printk(KERN_DEBUG "min dma period: %d ps, old clock %d kHz, "
2059     			"new clock %d kHz\n", sa1100fb_min_dma_period(fbi),
2060     			mm->cur_freq, mm->new_freq);
2061     		/* todo: fill in min/max values */
2062     		break;
2063     
2064     	case CPUFREQ_PRECHANGE:
2065     		set_ctrlr_state(fbi, C_DISABLE_CLKCHANGE);
2066     		break;
2067     
2068     	case CPUFREQ_POSTCHANGE:
2069     		pcd = get_pcd(fbi->fb.var.pixclock);
2070     		fbi->reg_lccr3 = (fbi->reg_lccr3 & ~0xff) | LCCR3_PixClkDiv(pcd);
2071     		set_ctrlr_state(fbi, C_ENABLE_CLKCHANGE);
2072     		break;
2073     	}
2074     	return 0;
2075     }
2076     #endif
2077     
2078     #ifdef CONFIG_PM
2079     /*
2080      * Power management hook.  Note that we won't be called from IRQ context,
2081      * unlike the blank functions above, so we may sleep.
2082      */
2083     static int
2084     sa1100fb_pm_callback(struct pm_dev *pm_dev, pm_request_t req, void *data)
2085     {
2086     	struct sa1100fb_info *fbi = pm_dev->data;
2087     
2088     	DPRINTK("pm_callback: %d\n", req);
2089     
2090     	if (req == PM_SUSPEND || req == PM_RESUME) {
2091     		int state = (int)data;
2092     
2093     		if (state == 0) {
2094     			/* Enter D0. */
2095     			set_ctrlr_state(fbi, C_ENABLE);
2096     		} else {
2097     			/* Enter D1-D3.  Disable the LCD controller.  */
2098     			set_ctrlr_state(fbi, C_DISABLE);
2099     		}
2100     	}
2101     	DPRINTK("done\n");
2102     	return 0;
2103     }
2104     #endif
2105     
2106     /*
2107      * sa1100fb_map_video_memory():
2108      *      Allocates the DRAM memory for the frame buffer.  This buffer is  
2109      *	remapped into a non-cached, non-buffered, memory region to  
2110      *      allow palette and pixel writes to occur without flushing the 
2111      *      cache.  Once this area is remapped, all virtual memory
2112      *      access to the video memory should occur at the new region.
2113      */
2114     static int __init sa1100fb_map_video_memory(struct sa1100fb_info *fbi)
2115     {
2116     	/*
2117     	 * We reserve one page for the palette, plus the size
2118     	 * of the framebuffer.
2119     	 */
2120     	fbi->map_size = PAGE_ALIGN(fbi->fb.fix.smem_len + PAGE_SIZE);
2121     	fbi->map_cpu = consistent_alloc(GFP_KERNEL, fbi->map_size,
2122     					&fbi->map_dma);
2123     
2124     	if (fbi->map_cpu) {
2125     		fbi->screen_cpu = fbi->map_cpu + PAGE_SIZE;
2126     		fbi->screen_dma = fbi->map_dma + PAGE_SIZE;
2127     		fbi->fb.fix.smem_start = fbi->screen_dma;
2128     	}
2129     
2130     	return fbi->map_cpu ? 0 : -ENOMEM;
2131     }
2132     
2133     /* Fake monspecs to fill in fbinfo structure */
2134     static struct fb_monspecs monspecs __initdata = {
2135     	30000, 70000, 50, 65, 0	/* Generic */
2136     };
2137     
2138     
2139     static struct sa1100fb_info * __init sa1100fb_init_fbinfo(void)
2140     {
2141     	struct sa1100fb_mach_info *inf;
2142     	struct sa1100fb_info *fbi;
2143     
2144     	fbi = kmalloc(sizeof(struct sa1100fb_info) + sizeof(struct display) +
2145     		      sizeof(u16) * 16, GFP_KERNEL);
2146     	if (!fbi)
2147     		return NULL;
2148     
2149     	memset(fbi, 0, sizeof(struct sa1100fb_info) + sizeof(struct display));
2150     
2151     	fbi->currcon		= -1;
2152     
2153     	strcpy(fbi->fb.fix.id, SA1100_NAME);
2154     
2155     	fbi->fb.fix.type	= FB_TYPE_PACKED_PIXELS;
2156     	fbi->fb.fix.type_aux	= 0;
2157     	fbi->fb.fix.xpanstep	= 0;
2158     	fbi->fb.fix.ypanstep	= 0;
2159     	fbi->fb.fix.ywrapstep	= 0;
2160     	fbi->fb.fix.accel	= FB_ACCEL_NONE;
2161     
2162     	fbi->fb.var.nonstd	= 0;
2163     	fbi->fb.var.activate	= FB_ACTIVATE_NOW;
2164     	fbi->fb.var.height	= -1;
2165     	fbi->fb.var.width	= -1;
2166     	fbi->fb.var.accel_flags	= 0;
2167     	fbi->fb.var.vmode	= FB_VMODE_NONINTERLACED;
2168     
2169     	strcpy(fbi->fb.modename, SA1100_NAME);
2170     	strcpy(fbi->fb.fontname, "Acorn8x8");
2171     
2172     	fbi->fb.fbops		= &sa1100fb_ops;
2173     	fbi->fb.changevar	= NULL;
2174     	fbi->fb.switch_con	= sa1100fb_switch;
2175     	fbi->fb.updatevar	= sa1100fb_updatevar;
2176     	fbi->fb.blank		= sa1100fb_blank;
2177     	fbi->fb.flags		= FBINFO_FLAG_DEFAULT;
2178     	fbi->fb.node		= -1;
2179     	fbi->fb.monspecs	= monspecs;
2180     	fbi->fb.disp		= (struct display *)(fbi + 1);
2181     	fbi->fb.pseudo_palette	= (void *)(fbi->fb.disp + 1);
2182     
2183     	fbi->rgb[RGB_8]		= &rgb_8;
2184     	fbi->rgb[RGB_16]	= &def_rgb_16;
2185     
2186     	inf = sa1100fb_get_machine_info(fbi);
2187     
2188     	fbi->max_xres			= inf->xres;
2189     	fbi->fb.var.xres		= inf->xres;
2190     	fbi->fb.var.xres_virtual	= inf->xres;
2191     	fbi->max_yres			= inf->yres;
2192     	fbi->fb.var.yres		= inf->yres;
2193     	fbi->fb.var.yres_virtual	= inf->yres;
2194     	fbi->max_bpp			= inf->bpp;
2195     	fbi->fb.var.bits_per_pixel	= inf->bpp;
2196     	fbi->fb.var.pixclock		= inf->pixclock;
2197     	fbi->fb.var.hsync_len		= inf->hsync_len;
2198     	fbi->fb.var.left_margin		= inf->left_margin;
2199     	fbi->fb.var.right_margin	= inf->right_margin;
2200     	fbi->fb.var.vsync_len		= inf->vsync_len;
2201     	fbi->fb.var.upper_margin	= inf->upper_margin;
2202     	fbi->fb.var.lower_margin	= inf->lower_margin;
2203     	fbi->fb.var.sync		= inf->sync;
2204     	fbi->fb.var.grayscale		= inf->cmap_greyscale;
2205     	fbi->cmap_inverse		= inf->cmap_inverse;
2206     	fbi->cmap_static		= inf->cmap_static;
2207     	fbi->lccr0			= inf->lccr0;
2208     	fbi->lccr3			= inf->lccr3;
2209     	fbi->state			= C_DISABLE;
2210     	fbi->task_state			= (u_char)-1;
2211     	fbi->fb.fix.smem_len		= fbi->max_xres * fbi->max_yres *
2212     					  fbi->max_bpp / 8;
2213     
2214     	init_waitqueue_head(&fbi->ctrlr_wait);
2215     	INIT_TQUEUE(&fbi->task, sa1100fb_task, fbi);
2216     	init_MUTEX(&fbi->ctrlr_sem);
2217     
2218     	return fbi;
2219     }
2220     
2221     int __init sa1100fb_init(void)
2222     {
2223     	struct sa1100fb_info *fbi;
2224     	int ret;
2225     
2226     	fbi = sa1100fb_init_fbinfo();
2227     	ret = -ENOMEM;
2228     	if (!fbi)
2229     		goto failed;
2230     
2231     	/* Initialize video memory */
2232     	ret = sa1100fb_map_video_memory(fbi);
2233     	if (ret)
2234     		goto failed;
2235     
2236     	ret = request_irq(IRQ_LCD, sa1100fb_handle_irq, SA_INTERRUPT,
2237     			  fbi->fb.fix.id, fbi);
2238     	if (ret) {
2239     		printk(KERN_ERR "sa1100fb: failed in request_irq: %d\n", ret);
2240     		goto failed;
2241     	}
2242     
2243     #if defined(CONFIG_SA1100_ASSABET) && defined(ASSABET_PAL_VIDEO)
2244     	if (machine_is_assabet())
2245     		BCR_clear(BCR_LCD_ON);
2246     #endif
2247     
2248     #ifdef CONFIG_SA1100_FREEBIRD
2249     #error Please move this into sa1100fb_power_up_lcd
2250     	if (machine_is_freebird()) {
2251     		BCR_set(BCR_FREEBIRD_LCD_DISP);
2252     		mdelay(20);
2253     		BCR_set(BCR_FREEBIRD_LCD_PWR);
2254     		mdelay(20);
2255     	}
2256     #endif
2257     
2258     	sa1100fb_set_var(&fbi->fb.var, -1, &fbi->fb);
2259     
2260     	ret = register_framebuffer(&fbi->fb);
2261     	if (ret < 0)
2262     		goto failed;
2263     
2264     #ifdef CONFIG_PM
2265     	/*
2266     	 * Note that the console registers this as well, but we want to
2267     	 * power down the display prior to sleeping.
2268     	 */
2269     	fbi->pm = pm_register(PM_SYS_DEV, PM_SYS_VGA, sa1100fb_pm_callback);
2270     	if (fbi->pm)
2271     		fbi->pm->data = fbi;
2272     #endif
2273     #ifdef CONFIG_CPU_FREQ
2274     	fbi->clockchg.notifier_call = sa1100fb_clkchg_notifier;
2275     	cpufreq_register_notifier(&fbi->clockchg);
2276     #endif
2277     
2278     	/*
2279     	 * Ok, now enable the LCD controller
2280     	 */
2281     	set_ctrlr_state(fbi, C_ENABLE);
2282     
2283     	/* This driver cannot be unloaded at the moment */
2284     	MOD_INC_USE_COUNT;
2285     
2286     	return 0;
2287     
2288     failed:
2289     	if (fbi)
2290     		kfree(fbi);
2291     	return ret;
2292     }
2293     
2294     int __init sa1100fb_setup(char *options)
2295     {
2296     #if 0
2297     	char *this_opt;
2298     
2299     	if (!options || !*options)
2300     		return 0;
2301     
2302     	for (this_opt = strtok(options, ","); this_opt;
2303     	     this_opt = strtok(NULL, ",")) {
2304     
2305     		if (!strncmp(this_opt, "bpp:", 4))
2306     			current_par.max_bpp =
2307     			    simple_strtoul(this_opt + 4, NULL, 0);
2308     
2309     		if (!strncmp(this_opt, "lccr0:", 6))
2310     			lcd_shadow.lccr0 =
2311     			    simple_strtoul(this_opt + 6, NULL, 0);
2312     		if (!strncmp(this_opt, "lccr1:", 6)) {
2313     			lcd_shadow.lccr1 =
2314     			    simple_strtoul(this_opt + 6, NULL, 0);
2315     			current_par.max_xres =
2316     			    (lcd_shadow.lccr1 & 0x3ff) + 16;
2317     		}
2318     		if (!strncmp(this_opt, "lccr2:", 6)) {
2319     			lcd_shadow.lccr2 =
2320     			    simple_strtoul(this_opt + 6, NULL, 0);
2321     			current_par.max_yres =
2322     			    (lcd_shadow.
2323     			     lccr0 & LCCR0_SDS) ? ((lcd_shadow.
2324     						    lccr2 & 0x3ff) +
2325     						   1) *
2326     			    2 : ((lcd_shadow.lccr2 & 0x3ff) + 1);
2327     		}
2328     		if (!strncmp(this_opt, "lccr3:", 6))
2329     			lcd_shadow.lccr3 =
2330     			    simple_strtoul(this_opt + 6, NULL, 0);
2331     	}
2332     #endif
2333     	return 0;
2334     }
2335     
2336     MODULE_LICENSE("GPL");
2337