Merge with Linux 2.4.0-test5-pre3.
[linux-2.6/linux-mips.git] / drivers / video / sa1100fb.c
blobeb98ba112e0f5ba90c89044d0be269c1a9c1475c
1 /*
2 * linux/drivers/video/sa1100fb.c -- StrongARM 1100 LCD Controller Frame Buffer Device
4 * Copyright (C) 1999 Eric A. Thomas
5 *
6 * This file is subject to the terms and conditions of the GNU General Public
7 * License. See the file COPYING in the main directory of this archive
8 * for more details.
14 * Code Status:
15 * 4/1/99 - Driver appears to be working for Brutus 320x200x8bpp mode. Other
16 * resolutions are working, but only the 8bpp mode is supported.
17 * Changes need to be made to the palette encode and decode routines
18 * to support 4 and 16 bpp modes.
19 * Driver is not designed to be a module. The FrameBuffer is statically
20 * allocated since dynamic allocation of a 300k buffer cannot be guaranteed.
22 * 6/17/99 - FrameBuffer memory is now allocated at run-time when the
23 * driver is initialized.
26 #include <linux/config.h>
27 #include <linux/module.h>
28 #include <linux/kernel.h>
29 #include <linux/sched.h>
30 #include <linux/errno.h>
31 #include <linux/string.h>
32 #include <linux/ctype.h>
33 #include <linux/mm.h>
34 #include <linux/tty.h>
35 #include <linux/malloc.h>
36 #include <linux/init.h>
37 #include <linux/fb.h>
38 #include <linux/delay.h>
40 #include <asm/hardware.h>
41 #include <asm/io.h>
42 #include <asm/irq.h>
43 #include <asm/uaccess.h>
44 #include <asm/proc/pgtable.h>
46 #include <video/fbcon.h>
47 #include <video/fbcon-mfb.h>
48 #include <video/fbcon-cfb4.h>
49 #include <video/fbcon-cfb8.h>
50 #include <video/fbcon-cfb16.h>
54 * Debug macros
56 //#define DEBUG
57 #ifdef DEBUG
58 # define DPRINTK(fmt, args...) printk("%s: " fmt, __FUNCTION__ , ## args)
59 #else
60 # define DPRINTK(fmt, args...)
61 #endif
64 /* Memory size macros for determining required FrameBuffer size */
65 #define MAX_PALETTE_NUM_ENTRIES 256
66 #define MAX_PALETTE_MEM_SIZE (MAX_PALETTE_NUM_ENTRIES * 2)
67 #define MAX_PIXEL_MEM_SIZE \
68 ((current_par.max_xres * current_par.max_yres * current_par.max_bpp)/8)
69 #define MAX_FRAMEBUFFER_MEM_SIZE \
70 (MAX_PIXEL_MEM_SIZE + MAX_PALETTE_MEM_SIZE + 32)
71 #define ALLOCATED_FB_MEM_SIZE \
72 (PAGE_ALIGN(MAX_FRAMEBUFFER_MEM_SIZE + PAGE_SIZE * 2))
74 #define SA1100_PALETTE_MEM_SIZE(bpp) (((bpp)==8?256:16)*2)
75 #define SA1100_PALETTE_MODE_VAL(bpp) (((bpp) & 0x018) << 9)
77 /* Minimum X and Y resolutions */
78 #define MIN_XRES 64
79 #define MIN_YRES 64
81 /* Possible controller_state modes */
82 #define LCD_MODE_DISABLED 0 // Controller is disabled and Disable Done received
83 #define LCD_MODE_DISABLE_BEFORE_ENABLE 1 // Re-enable after Disable Done IRQ is received
84 #define LCD_MODE_ENABLED 2 // Controller is enabled
86 #define SA1100_NAME "SA1100"
87 #define NR_MONTYPES 1
89 static inline void
90 sa1100fb_assabet_set_truecolor(u_int is_true_color)
92 #ifdef CONFIG_SA1100_ASSABET
93 #if 1
94 // phase 4 or newer Assabet's
95 if (is_true_color)
96 BCR_set(BCR_LCD_12RGB);
97 else
98 BCR_clear(BCR_LCD_12RGB);
99 #else
100 // older Assabet's
101 if (is_true_color)
102 BCR_clear(BCR_LCD_12RGB);
103 else
104 BCR_set(BCR_LCD_12RGB);
105 #endif
106 #endif
109 static u_char *VideoMemRegion;
110 static u_char *VideoMemRegion_phys;
112 /* Local LCD controller parameters */
113 /* These can be reduced by making better use of fb_var_screeninfo parameters. */
114 /* Several duplicates exist in the two structures. */
115 struct sa1100fb_par {
116 u_char *p_screen_base;
117 u_char *v_screen_base;
118 u_short *p_palette_base;
119 u_short *v_palette_base;
120 unsigned long screen_size;
121 unsigned int palette_size;
122 unsigned int max_xres;
123 unsigned int max_yres;
124 unsigned int xres;
125 unsigned int yres;
126 unsigned int xres_virtual;
127 unsigned int yres_virtual;
128 unsigned int max_bpp;
129 unsigned int bits_per_pixel;
130 signed int montype;
131 unsigned int currcon;
132 unsigned int visual;
133 unsigned int allow_modeset : 1;
134 unsigned int active_lcd : 1;
135 unsigned int inv_4bpp : 1;
136 volatile u_char controller_state;
139 /* Shadows for LCD controller registers */
140 struct sa1100fb_lcd_reg {
141 Address dbar1;
142 Word lccr0;
143 Word lccr1;
144 Word lccr2;
145 Word lccr3;
148 /* Fake monspecs to fill in fbinfo structure */
149 static struct fb_monspecs monspecs __initdata = {
150 30000, 70000, 50, 65, 0 /* Generic */
153 static struct display global_disp; /* Initial (default) Display Settings */
154 static struct fb_info fb_info;
155 static struct sa1100fb_par current_par;
156 static struct fb_var_screeninfo __initdata init_var = {};
157 static struct sa1100fb_lcd_reg lcd_shadow;
160 static int sa1100fb_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info);
161 static int sa1100fb_ioctl(struct inode *ino, struct file *file, unsigned int cmd,
162 unsigned long arg, int con, struct fb_info *info);
163 static int sa1100fb_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info);
164 static int sa1100fb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info);
165 static int sa1100fb_get_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info);
166 static int sa1100fb_set_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info);
167 static int sa1100fb_pan_display(struct fb_var_screeninfo *var, int con, struct fb_info *info);
169 static int sa1100fb_switch(int con, struct fb_info *info);
170 static void sa1100fb_blank(int blank, struct fb_info *info);
171 static int sa1100fb_map_video_memory(void);
172 static int sa1100fb_activate_var(struct fb_var_screeninfo *var);
173 static void sa1100fb_enable_lcd_controller(void);
174 static void sa1100fb_disable_lcd_controller(void);
176 static struct fb_ops sa1100fb_ops = {
177 owner: THIS_MODULE,
178 fb_get_fix: sa1100fb_get_fix,
179 fb_get_var: sa1100fb_get_var,
180 fb_set_var: sa1100fb_set_var,
181 fb_get_cmap: sa1100fb_get_cmap,
182 fb_set_cmap: sa1100fb_set_cmap,
183 fb_pan_display: sa1100fb_pan_display,
184 fb_ioctl: sa1100fb_ioctl,
189 * sa1100fb_palette_write:
190 * Write palette data to the LCD frame buffer's palette area
192 static inline void
193 sa1100fb_palette_write(u_int regno, u_short pal)
195 current_par.v_palette_base[regno] = (regno ? pal : pal |
196 SA1100_PALETTE_MODE_VAL(current_par.bits_per_pixel));
200 static inline u_short
201 sa1100fb_palette_encode(u_int regno, u_int red, u_int green, u_int blue, u_int trans)
203 u_int pal;
205 if(current_par.bits_per_pixel == 4){
207 * RGB -> luminance is defined to be
208 * Y = 0.299 * R + 0.587 * G + 0.114 * B
210 pal = (19595 * red + 38470 * green + 7471 * blue) >> 28;
211 if( current_par.inv_4bpp )
212 pal = 15 - pal;
214 else{
215 pal = ((red >> 4) & 0xf00);
216 pal |= ((green >> 8) & 0x0f0);
217 pal |= ((blue >> 12) & 0x00f);
220 return pal;
223 static inline u_short
224 sa1100fb_palette_read(u_int regno)
226 return (current_par.v_palette_base[regno] & 0x0FFF);
230 static void
231 sa1100fb_palette_decode(u_int regno, u_int *red, u_int *green, u_int *blue, u_int *trans)
233 u_short pal;
235 pal = sa1100fb_palette_read(regno);
237 if( current_par.bits_per_pixel == 4){
238 if( current_par.inv_4bpp )
239 pal = 15 - pal;
240 pal &= 0x000f;
241 pal |= pal << 4;
242 pal |= pal << 8;
243 *blue = *green = *red = pal;
245 else{
246 *blue = (pal & 0x000f) << 12;
247 *green = (pal & 0x00f0) << 8;
248 *red = (pal & 0x0f00) << 4;
250 *trans = 0;
253 static int
254 sa1100fb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue, u_int *trans, struct fb_info *info)
256 if (regno >= current_par.palette_size)
257 return 1;
259 sa1100fb_palette_decode(regno, red, green, blue, trans);
261 return 0;
265 static int
266 sa1100fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, u_int trans, struct fb_info *info)
268 u_short pal;
270 if (regno >= current_par.palette_size)
271 return 1;
273 pal = sa1100fb_palette_encode(regno, red, green, blue, trans);
275 sa1100fb_palette_write(regno, pal);
277 return 0;
280 static int
281 sa1100fb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
282 struct fb_info *info)
284 int err = 0;
286 DPRINTK("current_par.visual=%d\n", current_par.visual);
287 if (con == current_par.currcon)
288 err = fb_get_cmap(cmap, kspc, sa1100fb_getcolreg, info);
289 else if (fb_display[con].cmap.len)
290 fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
291 else
292 fb_copy_cmap(fb_default_cmap(current_par.palette_size),
293 cmap, kspc ? 0 : 2);
294 return err;
297 static int
298 sa1100fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
299 struct fb_info *info)
301 int err = 0;
303 DPRINTK("current_par.visual=%d\n", current_par.visual);
304 if (!fb_display[con].cmap.len)
305 err = fb_alloc_cmap(&fb_display[con].cmap,
306 current_par.palette_size, 0);
307 if (!err) {
308 if (con == current_par.currcon)
309 err = fb_set_cmap(cmap, kspc, sa1100fb_setcolreg,
310 info);
311 fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
313 return err;
316 static void inline
317 sa1100fb_get_par(struct sa1100fb_par *par)
319 *par = current_par;
324 * sa1100fb_encode_var():
325 * Modify var structure using values in par
327 static int
328 sa1100fb_encode_var(struct fb_var_screeninfo *var,
329 struct sa1100fb_par *par)
331 // Don't know if really want to var on entry.
332 // Look at set_var to see. If so, may need to add extra params to par
333 // memset(var, 0, sizeof(struct fb_var_screeninfo));
335 var->xres = par->xres;
336 var->yres = par->yres;
337 var->xres_virtual = par->xres_virtual;
338 var->yres_virtual = par->yres_virtual;
340 var->bits_per_pixel = par->bits_per_pixel;
342 DPRINTK("var->bits_per_pixel=%d\n", var->bits_per_pixel);
343 switch(var->bits_per_pixel) {
344 case 2:
345 case 4:
346 case 8:
347 var->red.length = 4;
348 var->green = var->red;
349 var->blue = var->red;
350 var->transp.length = 0;
351 break;
352 case 12: // This case should differ for Active/Passive mode
353 case 16:
354 var->red.length = 5;
355 var->green.length = 6;
356 var->blue.length = 5;
357 var->transp.length = 0;
358 var->red.offset = 11;
359 var->green.offset = 5;
360 var->blue.offset = 0;
361 var->transp.offset = 0;
362 break;
364 return 0;
368 * sa1100fb_decode_var():
369 * Get the video params out of 'var'. If a value doesn't fit, round it up,
370 * if it's too big, return -EINVAL.
372 * Suggestion: Round up in the following order: bits_per_pixel, xres,
373 * yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale,
374 * bitfields, horizontal timing, vertical timing.
376 static int
377 sa1100fb_decode_var(struct fb_var_screeninfo *var,
378 struct sa1100fb_par *par)
380 u_long palette_mem_phys;
381 u_long palette_mem_size;
383 *par = current_par;
385 if ((par->xres = var->xres) < MIN_XRES)
386 par->xres = MIN_XRES;
387 if ((par->yres = var->yres) < MIN_YRES)
388 par->yres = MIN_YRES;
389 if (par->xres > current_par.max_xres)
390 par->xres = current_par.max_xres;
391 if (par->yres > current_par.max_yres)
392 par->yres = current_par.max_yres;
393 par->xres_virtual =
394 var->xres_virtual < par->xres ? par->xres : var->xres_virtual;
395 par->yres_virtual =
396 var->yres_virtual < par->yres ? par->yres : var->yres_virtual;
397 par->bits_per_pixel = var->bits_per_pixel;
399 DPRINTK("par->bits_per_pixel=%d\n", par->bits_per_pixel);
400 switch (par->bits_per_pixel) {
401 #ifdef FBCON_HAS_CFB4
402 case 4:
403 par->visual = FB_VISUAL_PSEUDOCOLOR;
404 par->palette_size = 16;
405 break;
406 #endif
407 #ifdef FBCON_HAS_CFB8
408 case 8:
409 par->visual = FB_VISUAL_PSEUDOCOLOR;
410 par->palette_size = 256;
411 break;
412 #endif
413 #ifdef FBCON_HAS_CFB16
414 case 16: /* RGB 565 */
415 par->visual = FB_VISUAL_TRUECOLOR;
416 par->palette_size = 0;
417 break;
418 #endif
419 default:
420 return -EINVAL;
423 palette_mem_size = SA1100_PALETTE_MEM_SIZE(par->bits_per_pixel);
424 palette_mem_phys = (u_long)VideoMemRegion_phys + PAGE_SIZE - palette_mem_size;
425 par->p_palette_base = (u_short *)palette_mem_phys;
426 par->v_palette_base = (u_short *)((u_long)VideoMemRegion + PAGE_SIZE - palette_mem_size);
427 par->p_screen_base = (u_char *)((u_long)VideoMemRegion_phys + PAGE_SIZE);
428 par->v_screen_base = (u_char *)((u_long)VideoMemRegion + PAGE_SIZE);
430 DPRINTK("p_palette_base = 0x%08lx\n",(u_long)par->p_palette_base);
431 DPRINTK("v_palette_base = 0x%08lx\n",(u_long)par->v_palette_base);
432 DPRINTK("p_screen_base = 0x%08lx\n",(u_long)par->p_screen_base);
433 DPRINTK("v_screen_base = 0x%08lx\n",(u_long)par->v_screen_base);
434 DPRINTK("VideoMemRegion = 0x%08lx\n",(u_long)VideoMemRegion);
435 DPRINTK("VideoMemRegion_phys = 0x%08lx\n",(u_long)VideoMemRegion_phys);
436 return 0;
439 static int
440 sa1100fb_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
442 struct sa1100fb_par par;
444 DPRINTK("con=%d\n", con);
445 if (con == -1) {
446 sa1100fb_get_par(&par);
447 sa1100fb_encode_var(var, &par);
448 } else
449 *var = fb_display[con].var;
451 return 0;
455 * sa1100fb_set_var():
456 * Set the user defined part of the display for the specified console
458 static int
459 sa1100fb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
461 struct display *display;
462 int err, chgvar = 0;
463 struct sa1100fb_par par;
465 if (con >= 0)
466 display = &fb_display[con]; /* Display settings for console */
467 else
468 display = &global_disp; /* Default display settings */
471 DPRINTK("xres = %d, yres = %d\n",var->xres, var->yres);
472 /* Decode var contents into a par structure, adjusting any */
473 /* out of range values. */
474 if ((err = sa1100fb_decode_var(var, &par)))
475 return err;
476 // Store adjusted par values into var structure
477 sa1100fb_encode_var(var, &par);
479 if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_TEST)
480 return 0;
481 else if (((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW) &&
482 ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NXTOPEN))
483 return -EINVAL;
485 if (con >= 0) {
486 if ((display->var.xres != var->xres) ||
487 (display->var.yres != var->yres) ||
488 (display->var.xres_virtual != var->xres_virtual) ||
489 (display->var.yres_virtual != var->yres_virtual) ||
490 (display->var.sync != var->sync) ||
491 (display->var.bits_per_pixel != var->bits_per_pixel) ||
492 (memcmp(&display->var.red, &var->red, sizeof(var->red))) ||
493 (memcmp(&display->var.green, &var->green, sizeof(var->green))) ||
494 (memcmp(&display->var.blue, &var->blue, sizeof(var->blue))))
495 chgvar = 1;
497 DPRINTK("chgvar=%d\n", chgvar);
499 display->var = *var;
500 display->screen_base = par.v_screen_base;
501 display->visual = par.visual;
502 display->type = FB_TYPE_PACKED_PIXELS;
503 display->type_aux = 0;
504 display->ypanstep = 0;
505 display->ywrapstep = 0;
506 display->line_length =
507 display->next_line = (var->xres * var->bits_per_pixel) / 8;
509 display->can_soft_blank = 1;
510 display->inverse = 0;
512 DPRINTK("display->var.bits_per_pixel=%d xres=%d yres=%d display->dispsw=%p\n",
513 display->var.bits_per_pixel, var->xres, var->yres, display->dispsw);
514 switch (display->var.bits_per_pixel) {
515 #ifdef FBCON_HAS_CFB4
516 case 4:
517 display->dispsw = &fbcon_cfb4;
518 break;
519 #endif
520 #ifdef FBCON_HAS_CFB8
521 case 8:
522 display->dispsw = &fbcon_cfb8;
523 break;
524 #endif
525 #ifdef FBCON_HAS_CFB16
526 case 16:
527 display->dispsw = &fbcon_cfb16;
528 break;
529 #endif
530 default:
531 display->dispsw = &fbcon_dummy;
532 break;
535 /* If the console has changed and the console has defined */
536 /* a changevar function, call that function. */
537 if (chgvar && info && info->changevar)
538 info->changevar(con);
540 /* If the current console is selected and it's not truecolor,
541 * update the palette
543 if ((con == current_par.currcon) &&
544 (current_par.visual != FB_VISUAL_TRUECOLOR)) {
545 struct fb_cmap *cmap;
547 current_par = par;
548 if (display->cmap.len)
549 cmap = &display->cmap;
550 else
551 cmap = fb_default_cmap(current_par.palette_size);
552 DPRINTK("visual=%d palette_size=%d cmap=%p\n", current_par.visual, current_par.palette_size, cmap);
554 fb_set_cmap(cmap, 1, sa1100fb_setcolreg, info);
557 /* If the current console is selected, activate the new var. */
558 if (con == current_par.currcon)
559 sa1100fb_activate_var(var);
561 return 0;
564 static int
565 sa1100fb_updatevar(int con, struct fb_info *info)
567 DPRINTK("entered\n");
568 return 0;
571 static int
572 sa1100fb_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info)
574 struct display *display;
576 memset(fix, 0, sizeof(struct fb_fix_screeninfo));
577 strcpy(fix->id, SA1100_NAME);
579 if (con >= 0)
581 DPRINTK("Using console specific display for con=%d\n",con);
582 display = &fb_display[con]; /* Display settings for console */
584 else
585 display = &global_disp; /* Default display settings */
587 fix->smem_start = (unsigned long)current_par.p_screen_base;
588 fix->smem_len = current_par.screen_size;
589 fix->type = display->type;
590 fix->type_aux = display->type_aux;
591 fix->xpanstep = 0;
592 fix->ypanstep = display->ypanstep;
593 fix->ywrapstep = display->ywrapstep;
594 fix->visual = display->visual;
595 fix->line_length = display->line_length;
596 fix->accel = FB_ACCEL_NONE;
598 return 0;
602 static void
603 __init sa1100fb_init_fbinfo(void)
605 strcpy(fb_info.modename, SA1100_NAME);
606 strcpy(fb_info.fontname, "Acorn8x8");
608 fb_info.node = -1;
609 fb_info.flags = FBINFO_FLAG_DEFAULT;
610 fb_info.fbops = &sa1100fb_ops;
611 fb_info.monspecs = monspecs;
612 fb_info.disp = &global_disp;
613 fb_info.changevar = NULL;
614 fb_info.switch_con = sa1100fb_switch;
615 fb_info.updatevar = sa1100fb_updatevar;
616 fb_info.blank = sa1100fb_blank;
619 * setup initial parameters
621 memset(&init_var, 0, sizeof(init_var));
623 init_var.transp.length = 0;
624 init_var.nonstd = 0;
625 init_var.activate = FB_ACTIVATE_NOW;
626 init_var.xoffset = 0;
627 init_var.yoffset = 0;
628 init_var.height = -1;
629 init_var.width = -1;
630 init_var.vmode = FB_VMODE_NONINTERLACED;
632 if (machine_is_assabet()) {
633 current_par.max_xres = 320;
634 current_par.max_yres = 240;
635 current_par.max_bpp = 16;
636 init_var.red.length = 5;
637 init_var.green.length = 6;
638 init_var.blue.length = 5;
639 init_var.grayscale = 0;
640 init_var.sync = 0;
641 } else if (machine_is_bitsy()) {
642 current_par.max_xres = 320;
643 current_par.max_yres = 240;
644 current_par.max_bpp = 16;
645 init_var.red.length = 5;
646 init_var.green.length = 6;
647 init_var.blue.length = 5;
648 init_var.grayscale = 0;
649 } else if (machine_is_brutus()) {
650 current_par.max_xres = 320;
651 current_par.max_yres = 240;
652 current_par.max_bpp = 8;
653 init_var.red.length = 4;
654 init_var.green = init_var.red;
655 init_var.blue = init_var.red;
656 init_var.sync = 0;
657 } else if (machine_is_lart()) {
658 current_par.max_xres = 320;
659 current_par.max_yres = 240;
660 current_par.max_bpp = 4;
661 init_var.red.length = 4;
662 init_var.green = init_var.red;
663 init_var.blue = init_var.red;
664 init_var.grayscale = 1;
665 init_var.pixclock = 150000;
666 init_var.sync = 0;
667 } else if (machine_is_penny()) {
668 current_par.max_xres = 640;
669 current_par.max_yres = 480;
670 current_par.max_bpp = 8;
671 init_var.red.length = 4;
672 init_var.green = init_var.red;
673 init_var.blue = init_var.red;
674 init_var.sync = 0;
675 } else if (machine_is_thinclient() || machine_is_graphicsclient()) {
676 current_par.max_xres = 640;
677 current_par.max_yres = 480;
678 current_par.max_bpp = 8;
679 init_var.red.length = 4;
680 init_var.green = init_var.red;
681 init_var.blue = init_var.red;
682 init_var.sync = 0;
683 } else if (machine_is_tifon()) {
684 current_par.max_xres = 640;
685 current_par.max_yres = 200;
686 current_par.max_bpp = 4;
687 current_par.inv_4bpp = 1;
688 init_var.red.length = 4;
689 init_var.green = init_var.red;
690 init_var.blue = init_var.red;
691 init_var.grayscale = 1;
692 init_var.pixclock = 150000;
693 init_var.left_margin = 20;
694 init_var.right_margin = 255;
695 init_var.upper_margin = 20;
696 init_var.lower_margin = 0;
697 init_var.hsync_len = 2;
698 init_var.vsync_len = 1;
699 init_var.sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT;
700 init_var.vmode = 0;
703 current_par.p_palette_base = NULL;
704 current_par.v_palette_base = NULL;
705 current_par.p_screen_base = NULL;
706 current_par.v_screen_base = NULL;
707 current_par.palette_size = MAX_PALETTE_NUM_ENTRIES;
708 current_par.screen_size = MAX_PIXEL_MEM_SIZE;
709 current_par.montype = -1;
710 current_par.currcon = -1;
711 current_par.allow_modeset = 1;
712 current_par.controller_state = LCD_MODE_DISABLED;
714 init_var.xres = current_par.max_xres;
715 init_var.yres = current_par.max_yres;
716 init_var.xres_virtual = init_var.xres;
717 init_var.yres_virtual = init_var.yres;
718 init_var.bits_per_pixel = current_par.max_bpp;
725 * sa1100fb_map_video_memory():
726 * Allocates the DRAM memory for the frame buffer. This buffer is
727 * remapped into a non-cached, non-buffered, memory region to
728 * allow palette and pixel writes to occur without flushing the
729 * cache. Once this area is remapped, all virtual memory
730 * access to the video memory should occur at the new region.
732 static int
733 __init sa1100fb_map_video_memory(void)
735 u_int required_pages;
736 u_int extra_pages;
737 u_int order;
738 u_int i;
739 char *allocated_region;
741 if (VideoMemRegion != NULL)
742 return -EINVAL;
744 DPRINTK("-1-");
746 /* Find order required to allocate enough memory for framebuffer */
747 required_pages = ALLOCATED_FB_MEM_SIZE >> PAGE_SHIFT;
748 for (order = 0 ; required_pages >> order ; order++) {;}
749 extra_pages = (1 << order) - required_pages;
751 if ((allocated_region =
752 (char *)__get_free_pages(GFP_KERNEL | GFP_DMA, order)) == NULL)
753 return -ENOMEM;
755 VideoMemRegion = (u_char *)allocated_region + (extra_pages << PAGE_SHIFT);
756 VideoMemRegion_phys = (u_char *)__virt_to_phys((u_long)VideoMemRegion);
758 /* Free all pages that we don't need but were given to us because */
759 /* __get_free_pages() works on powers of 2. */
760 for (;extra_pages;extra_pages--)
761 free_page((u_int)allocated_region + ((extra_pages-1) << PAGE_SHIFT));
763 /* Set reserved flag for fb memory to allow it to be remapped into */
764 /* user space by the common fbmem driver using remap_page_range(). */
765 for(i = MAP_NR(VideoMemRegion);
766 i < MAP_NR(VideoMemRegion + ALLOCATED_FB_MEM_SIZE); i++)
767 set_bit(PG_reserved, &mem_map[i].flags);
769 /* Remap the fb memory to a non-buffered, non-cached region */
770 VideoMemRegion = (u_char *)__ioremap((u_long)VideoMemRegion_phys,
771 ALLOCATED_FB_MEM_SIZE,
772 L_PTE_PRESENT |
773 L_PTE_YOUNG |
774 L_PTE_DIRTY |
775 L_PTE_WRITE);
776 memset(VideoMemRegion, 0xAA, ALLOCATED_FB_MEM_SIZE);
777 return (VideoMemRegion == NULL ? -EINVAL : 0);
780 static const int frequency[16] = {
781 59000000,
782 73700000,
783 88500000,
784 103200000,
785 118000000,
786 132700000,
787 147500000,
788 162200000,
789 176900000,
790 191700000,
791 206400000,
792 230000000,
793 245000000,
794 260000000,
795 275000000,
796 290000000
800 static inline int get_pcd(unsigned int pixclock)
802 unsigned int pcd = 0;
804 if (machine_is_tifon()) {
805 pcd = frequency[PPCR &0xf] / 1000;
806 pcd *= pixclock/1000;
807 pcd = pcd / 10000000 * 12;
808 /* the last multiplication by 1.2 is to handle */
809 /* sync problems */
811 return pcd;
816 * sa1100fb_activate_var():
817 * Configures LCD Controller based on entries in var parameter. Settings are
818 * only written to the controller if changes were made.
820 static int
821 sa1100fb_activate_var(struct fb_var_screeninfo *var)
823 u_long flags;
824 int pcd = get_pcd(var->pixclock);
826 DPRINTK("Configuring SA1100 LCD\n");
828 if (current_par.p_palette_base == NULL)
829 return -EINVAL;
831 DPRINTK("activating\n");
833 /* Disable interrupts and save status */
834 save_flags_cli(flags); // disable the interrupts and save flags
836 /* Reset the LCD Controller's DMA address if it has changed */
837 lcd_shadow.dbar1 = (Address)current_par.p_palette_base;
839 DPRINTK("Configuring xres = %d, yres = %d\n",var->xres, var->yres);
841 if (machine_is_assabet()) {
842 DPRINTK("Configuring Assabet LCD\n");
843 lcd_shadow.lccr0 =
844 LCCR0_LEN + LCCR0_Color + LCCR0_Sngl +
845 LCCR0_LDM + LCCR0_BAM + LCCR0_ERM + LCCR0_Act +
846 LCCR0_LtlEnd + LCCR0_DMADel(0);
847 lcd_shadow.lccr1 =
848 LCCR1_DisWdth(var->xres) + LCCR1_HorSnchWdth(6) +
849 LCCR1_BegLnDel(61) + LCCR1_EndLnDel(9);
850 lcd_shadow.lccr2 =
851 LCCR2_DisHght(var->yres) + LCCR2_VrtSnchWdth(2) +
852 LCCR2_BegFrmDel(3) + LCCR2_EndFrmDel(0);
853 lcd_shadow.lccr3 =
854 LCCR3_OutEnH + LCCR3_PixFlEdg + LCCR3_VrtSnchH +
855 LCCR3_HorSnchH + LCCR3_ACBsCntOff +
856 LCCR3_ACBsDiv(2) + LCCR3_PixClkDiv(38);
858 /* Set board control register to handle new color depth */
859 sa1100fb_assabet_set_truecolor(var->bits_per_pixel >= 16);
860 } else if (machine_is_bitsy()) {
861 DPRINTK("Configuring Bitsy LCD\n");
862 lcd_shadow.lccr0 = LCCR0_LEN + LCCR0_Color + LCCR0_Sngl + LCCR0_Act +
863 LCCR0_LtlEnd + LCCR0_LDM + LCCR0_BAM + LCCR0_ERM +
864 LCCR0_DMADel(0);
865 lcd_shadow.lccr1 = LCCR1_DisWdth( var->xres ) +
866 LCCR1_HorSnchWdth( 4 ) +
867 LCCR1_BegLnDel( 0x1f ) +
868 LCCR1_EndLnDel( 0x1f );
869 lcd_shadow.lccr2 = LCCR2_DisHght( var->yres ) +
870 LCCR2_VrtSnchWdth( 1 )+
871 LCCR2_BegFrmDel( 0 ) +
872 LCCR2_EndFrmDel( 0 );
873 lcd_shadow.lccr3 = 15;
874 } else if (machine_is_brutus()) {
875 DPRINTK("Configuring Brutus LCD\n");
876 lcd_shadow.lccr0 =
877 LCCR0_LEN + LCCR0_Color + LCCR0_Sngl + LCCR0_Pas +
878 LCCR0_LtlEnd + LCCR0_LDM + LCCR0_BAM + LCCR0_ERM +
879 LCCR0_DMADel(0);
880 lcd_shadow.lccr1 =
881 LCCR1_DisWdth(var->xres) + LCCR1_HorSnchWdth(4) +
882 LCCR1_BegLnDel(41) + LCCR1_EndLnDel(101);
883 lcd_shadow.lccr2 =
884 LCCR2_DisHght(var->yres) + LCCR2_VrtSnchWdth(1) +
885 LCCR2_BegFrmDel(0) + LCCR2_EndFrmDel(0);
886 lcd_shadow.lccr3 =
887 LCCR3_OutEnH + LCCR3_PixFlEdg + LCCR3_VrtSnchH +
888 LCCR3_HorSnchH + LCCR3_ACBsCntOff +
889 LCCR3_ACBsDiv(2) + LCCR3_PixClkDiv(44);
890 } else if (machine_is_lart()) {
891 DPRINTK("Configuring LART LCD\n");
892 lcd_shadow.lccr0 =
893 LCCR0_LEN + LCCR0_Mono + LCCR0_Sngl + LCCR0_Pas +
894 LCCR0_LtlEnd + LCCR0_LDM + LCCR0_BAM + LCCR0_ERM +
895 LCCR0_DMADel(0);
896 lcd_shadow.lccr1 =
897 LCCR1_DisWdth(var->xres) + LCCR1_HorSnchWdth(2) +
898 LCCR1_BegLnDel(4) + LCCR1_EndLnDel(2);
899 lcd_shadow.lccr2 =
900 LCCR2_DisHght(var->yres) + LCCR2_VrtSnchWdth(1) +
901 LCCR2_BegFrmDel(0) + LCCR2_EndFrmDel(0);
902 lcd_shadow.lccr3 =
903 LCCR3_PixClkDiv(34) + LCCR3_ACBsDiv(512) +
904 LCCR3_ACBsCntOff + LCCR3_HorSnchH + LCCR3_VrtSnchH;
905 } else if (machine_is_penny()) {
906 DPRINTK("Configuring Penny LCD\n");
907 lcd_shadow.lccr0 =
908 LCCR0_LEN + LCCR0_Color + LCCR0_Sngl + LCCR0_Act +
909 LCCR0_LtlEnd + LCCR0_LDM + LCCR0_BAM + LCCR0_ERM +
910 LCCR0_DMADel(0);
911 lcd_shadow.lccr1 =
912 LCCR1_DisWdth(var->xres) + LCCR1_HorSnchWdth(65) +
913 LCCR1_EndLnDel(43) + LCCR1_BegLnDel(43);
914 lcd_shadow.lccr2 =
915 LCCR2_DisHght(var->yres) + LCCR2_VrtSnchWdth(35) +
916 LCCR2_EndFrmDel(0) + LCCR2_BegFrmDel(0);
917 lcd_shadow.lccr3 =
918 LCCR3_PixClkDiv(16) + LCCR3_ACBsDiv (2) + LCCR3_ACBsCntOff +
919 ((var->sync & FB_SYNC_HOR_HIGH_ACT) ? LCCR3_HorSnchH : LCCR3_HorSnchL) +
920 ((var->sync & FB_SYNC_VERT_HIGH_ACT) ? LCCR3_VrtSnchH : LCCR3_VrtSnchL);
921 } else if (machine_is_thinclient() || machine_is_graphicsclient()) {
922 DPRINTK("Configuring ThinClient LCD\n");
923 lcd_shadow.lccr0 =
924 LCCR0_LEN + LCCR0_Color + LCCR0_Sngl + LCCR0_Act;
925 lcd_shadow.lccr1 =
926 LCCR1_DisWdth(var->xres) + LCCR1_HorSnchWdth(10) +
927 LCCR1_EndLnDel(81) + LCCR1_BegLnDel(81);
928 lcd_shadow.lccr2 =
929 LCCR2_DisHght(var->yres) + LCCR2_VrtSnchWdth(9) +
930 LCCR2_EndFrmDel (20) + LCCR2_BegFrmDel(20);
931 lcd_shadow.lccr3 =
932 LCCR3_PixClkDiv(6) + LCCR3_ACBsDiv(2) +
933 LCCR3_ACBsCntOff + LCCR3_HorSnchL + LCCR3_VrtSnchL;
934 } else if (machine_is_tifon()) {
935 DPRINTK("Configuring TIFON LCD\n");
936 lcd_shadow.lccr0 =
937 LCCR0_LEN + LCCR0_Mono + LCCR0_Sngl + LCCR0_Pas +
938 LCCR0_BigEnd + LCCR0_LDM + LCCR0_BAM + LCCR0_ERM +
939 LCCR0_8PixMono + LCCR0_DMADel(0);
940 lcd_shadow.lccr1 =
941 LCCR1_DisWdth(var->xres) +
942 LCCR1_HorSnchWdth(var->hsync_len) +
943 LCCR1_BegLnDel(var->left_margin) +
944 LCCR1_EndLnDel(var->right_margin);
945 lcd_shadow.lccr2 =
946 LCCR2_DisHght(var->yres) +
947 LCCR2_VrtSnchWdth(var->vsync_len) +
948 LCCR2_BegFrmDel(var->upper_margin) +
949 LCCR2_EndFrmDel(var->lower_margin);
950 lcd_shadow.lccr3 =
951 LCCR3_PixClkDiv(pcd) + LCCR3_ACBsDiv(512) +
952 LCCR3_ACBsCnt(0) + LCCR3_HorSnchH + LCCR3_VrtSnchH;
954 ((current_var.sync & FB_SYNC_HOR_HIGH_ACT) ? LCCR3_HorSnchH : LCCR3_HorSnchL) +
955 ((current_var.sync & FB_SYNC_VERT_HIGH_ACT) ? LCCR3_VrtSnchH : LCCR3_VrtSnchL);
959 /* Restore interrupt status */
960 restore_flags(flags);
962 if (( LCCR0 != lcd_shadow.lccr0 ) ||
963 ( LCCR1 != lcd_shadow.lccr1 ) ||
964 ( LCCR2 != lcd_shadow.lccr2 ) ||
965 ( LCCR3 != lcd_shadow.lccr3 ) ||
966 ( DBAR1 != lcd_shadow.dbar1 ))
968 sa1100fb_enable_lcd_controller();
971 return 0;
976 * sa1100fb_inter_handler():
977 * Interrupt handler for LCD controller. Processes disable done interrupt (LDD)
978 * to reenable controller if controller was disabled to change register values.
980 static void sa1100fb_inter_handler(int irq, void *dev_id, struct pt_regs *regs)
982 if (LCSR & LCSR_LDD) {
983 /* Disable Done Flag is set */
984 LCCR0 |= LCCR0_LDM; /* Mask LCD Disable Done Interrupt */
985 current_par.controller_state = LCD_MODE_DISABLED;
986 if (current_par.controller_state == LCD_MODE_DISABLE_BEFORE_ENABLE) {
987 sa1100fb_enable_lcd_controller();
990 LCSR = 0; /* Clear LCD Status Register */
995 * sa1100fb_disable_lcd_controller():
996 * Disables LCD controller by and enables LDD interrupt. The controller_state
997 * is not changed until the LDD interrupt is received to indicate the current
998 * frame has completed. Platform specific hardware disabling is also included.
1000 static void sa1100fb_disable_lcd_controller(void)
1002 DPRINTK("Disabling LCD controller\n");
1004 /* Exit if already LCD disabled, or LDD IRQ unmasked */
1005 if ((current_par.controller_state == LCD_MODE_DISABLED) ||
1006 (!(LCCR0 & LCCR0_LDM))) {
1007 DPRINTK("LCD already disabled\n");
1008 return;
1011 if (machine_is_assabet()) {
1012 #ifdef CONFIG_SA1100_ASSABET
1013 BCR_clear(BCR_LCD_ON);
1014 #endif
1015 } else if (machine_is_bitsy()) {
1016 #ifdef CONFIG_SA1100_BITSY
1017 clr_bitsy_egpio(EGPIO_BITSY_LCD_ON | EGPIO_BITSY_LCD_PCI | EGPIO_BITSY_LCD_5V_ON | EGPIO_BITSY_LVDD_ON);
1018 #endif
1019 } else if (machine_is_penny()) {
1020 #ifdef CONFIG_SA1100_PENNY
1021 FpgaLcdCS1 = 0x000; /* LCD Backlight to 0% */
1022 FpgaPortI &= ~LCD_ON; /* Turn off LCD Backlight */
1023 #endif
1024 } else if (machine_is_tifon()) {
1025 GPCR = GPIO_GPIO(24); /* turn off display */
1028 LCSR = 0; /* Clear LCD Status Register */
1029 LCCR0 &= ~(LCCR0_LDM); /* Enable LCD Disable Done Interrupt */
1030 enable_irq(IRQ_LCD); /* Enable LCD IRQ */
1031 LCCR0 &= ~(LCCR0_LEN); /* Disable LCD Controller */
1036 * sa1100fb_enable_lcd_controller():
1037 * Enables LCD controller. If the controller is already enabled, it is first disabled.
1038 * This forces all changes to the LCD controller registers to be done when the
1039 * controller is disabled. Platform specific hardware enabling is also included.
1041 static void sa1100fb_enable_lcd_controller(void)
1043 u_long flags;
1045 save_flags_cli(flags);
1047 /* Disable controller before changing parameters */
1048 if (current_par.controller_state == LCD_MODE_ENABLED) {
1049 current_par.controller_state = LCD_MODE_DISABLE_BEFORE_ENABLE;
1050 sa1100fb_disable_lcd_controller();
1051 } else {
1052 DPRINTK("Enabling LCD controller\n");
1054 /* Make sure the mode bits are present in the first palette entry */
1055 current_par.v_palette_base[0] &= 0x0FFF;
1056 current_par.v_palette_base[0] |= SA1100_PALETTE_MODE_VAL(current_par.bits_per_pixel);
1058 /* disable the interrupts and save flags */
1059 save_flags_cli(flags);
1061 DBAR1 = lcd_shadow.dbar1;
1062 LCCR3 = lcd_shadow.lccr3;
1063 LCCR2 = lcd_shadow.lccr2;
1064 LCCR1 = lcd_shadow.lccr1;
1065 LCCR0 = lcd_shadow.lccr0;
1067 if (machine_is_assabet()) {
1068 #ifdef CONFIG_SA1100_ASSABET
1069 BCR_set(BCR_LCD_ON);
1070 #endif
1071 } else if (machine_is_bitsy()) {
1072 #ifdef CONFIG_SA1100_BITSY
1073 set_bitsy_egpio(EGPIO_BITSY_LCD_ON | EGPIO_BITSY_LCD_PCI | EGPIO_BITSY_LCD_5V_ON | EGPIO_BITSY_LVDD_ON)
1074 DPRINTK("DBAR1=%p\n", DBAR1);
1075 DPRINTK("LCCR0=%x\n", LCCR0);
1076 DPRINTK("LCCR1=%x\n", LCCR1);
1077 DPRINTK("LCCR2=%x\n", LCCR2);
1078 DPRINTK("LCCR3=%x\n", LCCR3);
1079 #endif
1080 } else if (machine_is_penny()) {
1081 #ifdef CONFIG_SA1100_PENNY
1082 FpgaLcdCS1 = 0x0FF; /* LCD Backlight to 100% */
1083 FpgaPortI |= LCD_ON; /* Turn on LCD Backlight */
1084 #endif
1085 } else if (machine_is_tifon()) {
1086 GPCR = GPIO_GPIO(24); /* cycle on/off-switch */
1087 udelay(150);
1088 GPSR = GPIO_GPIO(24); /* turn on display */
1091 current_par.controller_state = LCD_MODE_ENABLED;
1094 /* Restore interrupt status */
1095 restore_flags(flags);
1098 static int
1099 sa1100fb_pan_display(struct fb_var_screeninfo *var, int con,
1100 struct fb_info *info)
1102 DPRINTK("entered\n");
1103 return -EINVAL;
1108 * sa1100fb_blank():
1109 * Blank the display by setting all palette values to zero. Note, the
1110 * 12 and 16 bpp modes don't really use the palette, so this will not
1111 * blank the display in all modes.
1113 static void
1114 sa1100fb_blank(int blank, struct fb_info *info)
1116 int i;
1118 DPRINTK("blank=%d info->modename=%s\n", blank, info->modename);
1119 if (blank) {
1120 if (current_par.visual != FB_VISUAL_TRUECOLOR)
1121 for (i = 0; i < current_par.palette_size; i++)
1122 sa1100fb_palette_write(i, sa1100fb_palette_encode(i, 0, 0, 0, 0));
1123 sa1100fb_disable_lcd_controller();
1125 else {
1126 if (current_par.visual != FB_VISUAL_TRUECOLOR)
1127 sa1100fb_set_cmap(&fb_display[current_par.currcon].cmap, 1,
1128 current_par.currcon, info);
1129 sa1100fb_enable_lcd_controller();
1135 * sa1100fb_switch():
1136 * Change to the specified console. Palette and video mode
1137 * are changed to the console's stored parameters.
1139 static int
1140 sa1100fb_switch(int con, struct fb_info *info)
1143 DPRINTK("con=%d info->modename=%s\n", con, info->modename);
1144 if (current_par.visual != FB_VISUAL_TRUECOLOR) {
1145 struct fb_cmap *cmap;
1146 if (current_par.currcon >= 0) {
1147 // Get the colormap for the selected console
1148 cmap = &fb_display[current_par.currcon].cmap;
1150 if (cmap->len)
1151 fb_get_cmap(cmap, 1, sa1100fb_getcolreg, info);
1155 current_par.currcon = con;
1156 fb_display[con].var.activate = FB_ACTIVATE_NOW;
1157 DPRINTK("fb_display[%d].var.activate=%x\n", con, fb_display[con].var.activate);
1158 sa1100fb_set_var(&fb_display[con].var, con, info);
1159 current_par.v_palette_base[0] = (current_par.v_palette_base[0] &
1160 0xcfff) | SA1100_PALETTE_MODE_VAL(current_par.bits_per_pixel);
1162 return 0;
1166 int __init sa1100fb_init(void)
1168 int ret;
1170 sa1100fb_init_fbinfo();
1172 /* Initialize video memory */
1173 if ((ret = sa1100fb_map_video_memory()) != 0)
1174 return ret;
1176 if (current_par.montype < 0 || current_par.montype > NR_MONTYPES)
1177 current_par.montype = 1;
1179 if (request_irq(IRQ_LCD, sa1100fb_inter_handler, SA_INTERRUPT, "SA1100 LCD", NULL) != 0) {
1180 printk("sa1100fb: failed in request_irq\n");
1181 return -EBUSY;
1183 DPRINTK("sa1100fb: request_irq succeeded\n");
1184 disable_irq(IRQ_LCD);
1186 if (machine_is_assabet()) {
1187 GPDR |= 0x3fc;
1188 GAFR |= 0x3fc;
1189 sa1100fb_assabet_set_truecolor(current_par.visual ==
1190 FB_VISUAL_TRUECOLOR);
1191 } else if (machine_is_bitsy()) {
1192 GPDR = (GPIO_LDD15 | GPIO_LDD14 | GPIO_LDD13 | GPIO_LDD12 | GPIO_LDD11 | GPIO_LDD10 | GPIO_LDD9 | GPIO_LDD8);
1193 GAFR |= (GPIO_LDD15 | GPIO_LDD14 | GPIO_LDD13 | GPIO_LDD12 | GPIO_LDD11 | GPIO_LDD10 | GPIO_LDD9 | GPIO_LDD8);
1194 } else if (machine_is_penny()) {
1195 #ifdef CONFIG_SA1100_PENNY
1196 GPDR |= GPIO_GPDR_GFX; /* GPIO Data Direction register for LCD data bits 8-11 */
1197 GAFR |= GPIO_GAFR_GFX; /* GPIO Alternate Function register for LCD data bits 8-11 */
1198 #endif
1199 } else if (machine_is_tifon()) {
1200 GPDR |= GPIO_GPIO(24); /* set GPIO24 to output */
1203 if (sa1100fb_set_var(&init_var, -1, &fb_info))
1204 current_par.allow_modeset = 0;
1205 sa1100fb_decode_var(&init_var, &current_par);
1207 register_framebuffer(&fb_info);
1209 /* This driver cannot be unloaded at the moment */
1210 MOD_INC_USE_COUNT;
1212 return 0;
1215 int __init sa1100fb_setup(char *options)
1217 return 0;
1222 static int
1223 sa1100fb_ioctl(struct inode *ino, struct file *file, unsigned int cmd,
1224 unsigned long arg, int con, struct fb_info *info)
1226 return -ENOIOCTLCMD;