Import 2.4.0-test5pre6
[davej-history.git] / drivers / video / sa1100fb.c
blobdf2d81b667eb901a7729158b4604bb7d46e6ccdc
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_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info);
162 static int sa1100fb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info);
163 static int sa1100fb_get_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info);
164 static int sa1100fb_set_cmap(struct fb_cmap *cmap, int kspc, int con, struct fb_info *info);
166 static int sa1100fb_switch(int con, struct fb_info *info);
167 static void sa1100fb_blank(int blank, struct fb_info *info);
168 static int sa1100fb_map_video_memory(void);
169 static int sa1100fb_activate_var(struct fb_var_screeninfo *var);
170 static void sa1100fb_enable_lcd_controller(void);
171 static void sa1100fb_disable_lcd_controller(void);
173 static struct fb_ops sa1100fb_ops = {
174 owner: THIS_MODULE,
175 fb_get_fix: sa1100fb_get_fix,
176 fb_get_var: sa1100fb_get_var,
177 fb_set_var: sa1100fb_set_var,
178 fb_get_cmap: sa1100fb_get_cmap,
179 fb_set_cmap: sa1100fb_set_cmap,
184 * sa1100fb_palette_write:
185 * Write palette data to the LCD frame buffer's palette area
187 static inline void
188 sa1100fb_palette_write(u_int regno, u_short pal)
190 current_par.v_palette_base[regno] = (regno ? pal : pal |
191 SA1100_PALETTE_MODE_VAL(current_par.bits_per_pixel));
195 static inline u_short
196 sa1100fb_palette_encode(u_int regno, u_int red, u_int green, u_int blue, u_int trans)
198 u_int pal;
200 if(current_par.bits_per_pixel == 4){
202 * RGB -> luminance is defined to be
203 * Y = 0.299 * R + 0.587 * G + 0.114 * B
205 pal = (19595 * red + 38470 * green + 7471 * blue) >> 28;
206 if( current_par.inv_4bpp )
207 pal = 15 - pal;
209 else{
210 pal = ((red >> 4) & 0xf00);
211 pal |= ((green >> 8) & 0x0f0);
212 pal |= ((blue >> 12) & 0x00f);
215 return pal;
218 static inline u_short
219 sa1100fb_palette_read(u_int regno)
221 return (current_par.v_palette_base[regno] & 0x0FFF);
225 static void
226 sa1100fb_palette_decode(u_int regno, u_int *red, u_int *green, u_int *blue, u_int *trans)
228 u_short pal;
230 pal = sa1100fb_palette_read(regno);
232 if( current_par.bits_per_pixel == 4){
233 if( current_par.inv_4bpp )
234 pal = 15 - pal;
235 pal &= 0x000f;
236 pal |= pal << 4;
237 pal |= pal << 8;
238 *blue = *green = *red = pal;
240 else{
241 *blue = (pal & 0x000f) << 12;
242 *green = (pal & 0x00f0) << 8;
243 *red = (pal & 0x0f00) << 4;
245 *trans = 0;
248 static int
249 sa1100fb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue, u_int *trans, struct fb_info *info)
251 if (regno >= current_par.palette_size)
252 return 1;
254 sa1100fb_palette_decode(regno, red, green, blue, trans);
256 return 0;
260 static int
261 sa1100fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, u_int trans, struct fb_info *info)
263 u_short pal;
265 if (regno >= current_par.palette_size)
266 return 1;
268 pal = sa1100fb_palette_encode(regno, red, green, blue, trans);
270 sa1100fb_palette_write(regno, pal);
272 return 0;
275 static int
276 sa1100fb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
277 struct fb_info *info)
279 int err = 0;
281 DPRINTK("current_par.visual=%d\n", current_par.visual);
282 if (con == current_par.currcon)
283 err = fb_get_cmap(cmap, kspc, sa1100fb_getcolreg, info);
284 else if (fb_display[con].cmap.len)
285 fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
286 else
287 fb_copy_cmap(fb_default_cmap(current_par.palette_size),
288 cmap, kspc ? 0 : 2);
289 return err;
292 static int
293 sa1100fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
294 struct fb_info *info)
296 int err = 0;
298 DPRINTK("current_par.visual=%d\n", current_par.visual);
299 if (!fb_display[con].cmap.len)
300 err = fb_alloc_cmap(&fb_display[con].cmap,
301 current_par.palette_size, 0);
302 if (!err) {
303 if (con == current_par.currcon)
304 err = fb_set_cmap(cmap, kspc, sa1100fb_setcolreg,
305 info);
306 fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
308 return err;
311 static void inline
312 sa1100fb_get_par(struct sa1100fb_par *par)
314 *par = current_par;
319 * sa1100fb_encode_var():
320 * Modify var structure using values in par
322 static int
323 sa1100fb_encode_var(struct fb_var_screeninfo *var,
324 struct sa1100fb_par *par)
326 // Don't know if really want to var on entry.
327 // Look at set_var to see. If so, may need to add extra params to par
328 // memset(var, 0, sizeof(struct fb_var_screeninfo));
330 var->xres = par->xres;
331 var->yres = par->yres;
332 var->xres_virtual = par->xres_virtual;
333 var->yres_virtual = par->yres_virtual;
335 var->bits_per_pixel = par->bits_per_pixel;
337 DPRINTK("var->bits_per_pixel=%d\n", var->bits_per_pixel);
338 switch(var->bits_per_pixel) {
339 case 2:
340 case 4:
341 case 8:
342 var->red.length = 4;
343 var->green = var->red;
344 var->blue = var->red;
345 var->transp.length = 0;
346 break;
347 case 12: // This case should differ for Active/Passive mode
348 case 16:
349 var->red.length = 5;
350 var->green.length = 6;
351 var->blue.length = 5;
352 var->transp.length = 0;
353 var->red.offset = 11;
354 var->green.offset = 5;
355 var->blue.offset = 0;
356 var->transp.offset = 0;
357 break;
359 return 0;
363 * sa1100fb_decode_var():
364 * Get the video params out of 'var'. If a value doesn't fit, round it up,
365 * if it's too big, return -EINVAL.
367 * Suggestion: Round up in the following order: bits_per_pixel, xres,
368 * yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale,
369 * bitfields, horizontal timing, vertical timing.
371 static int
372 sa1100fb_decode_var(struct fb_var_screeninfo *var,
373 struct sa1100fb_par *par)
375 u_long palette_mem_phys;
376 u_long palette_mem_size;
378 *par = current_par;
380 if ((par->xres = var->xres) < MIN_XRES)
381 par->xres = MIN_XRES;
382 if ((par->yres = var->yres) < MIN_YRES)
383 par->yres = MIN_YRES;
384 if (par->xres > current_par.max_xres)
385 par->xres = current_par.max_xres;
386 if (par->yres > current_par.max_yres)
387 par->yres = current_par.max_yres;
388 par->xres_virtual =
389 var->xres_virtual < par->xres ? par->xres : var->xres_virtual;
390 par->yres_virtual =
391 var->yres_virtual < par->yres ? par->yres : var->yres_virtual;
392 par->bits_per_pixel = var->bits_per_pixel;
394 DPRINTK("par->bits_per_pixel=%d\n", par->bits_per_pixel);
395 switch (par->bits_per_pixel) {
396 #ifdef FBCON_HAS_CFB4
397 case 4:
398 par->visual = FB_VISUAL_PSEUDOCOLOR;
399 par->palette_size = 16;
400 break;
401 #endif
402 #ifdef FBCON_HAS_CFB8
403 case 8:
404 par->visual = FB_VISUAL_PSEUDOCOLOR;
405 par->palette_size = 256;
406 break;
407 #endif
408 #ifdef FBCON_HAS_CFB16
409 case 16: /* RGB 565 */
410 par->visual = FB_VISUAL_TRUECOLOR;
411 par->palette_size = 0;
412 break;
413 #endif
414 default:
415 return -EINVAL;
418 palette_mem_size = SA1100_PALETTE_MEM_SIZE(par->bits_per_pixel);
419 palette_mem_phys = (u_long)VideoMemRegion_phys + PAGE_SIZE - palette_mem_size;
420 par->p_palette_base = (u_short *)palette_mem_phys;
421 par->v_palette_base = (u_short *)((u_long)VideoMemRegion + PAGE_SIZE - palette_mem_size);
422 par->p_screen_base = (u_char *)((u_long)VideoMemRegion_phys + PAGE_SIZE);
423 par->v_screen_base = (u_char *)((u_long)VideoMemRegion + PAGE_SIZE);
425 DPRINTK("p_palette_base = 0x%08lx\n",(u_long)par->p_palette_base);
426 DPRINTK("v_palette_base = 0x%08lx\n",(u_long)par->v_palette_base);
427 DPRINTK("p_screen_base = 0x%08lx\n",(u_long)par->p_screen_base);
428 DPRINTK("v_screen_base = 0x%08lx\n",(u_long)par->v_screen_base);
429 DPRINTK("VideoMemRegion = 0x%08lx\n",(u_long)VideoMemRegion);
430 DPRINTK("VideoMemRegion_phys = 0x%08lx\n",(u_long)VideoMemRegion_phys);
431 return 0;
434 static int
435 sa1100fb_get_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
437 struct sa1100fb_par par;
439 DPRINTK("con=%d\n", con);
440 if (con == -1) {
441 sa1100fb_get_par(&par);
442 sa1100fb_encode_var(var, &par);
443 } else
444 *var = fb_display[con].var;
446 return 0;
450 * sa1100fb_set_var():
451 * Set the user defined part of the display for the specified console
453 static int
454 sa1100fb_set_var(struct fb_var_screeninfo *var, int con, struct fb_info *info)
456 struct display *display;
457 int err, chgvar = 0;
458 struct sa1100fb_par par;
460 if (con >= 0)
461 display = &fb_display[con]; /* Display settings for console */
462 else
463 display = &global_disp; /* Default display settings */
466 DPRINTK("xres = %d, yres = %d\n",var->xres, var->yres);
467 /* Decode var contents into a par structure, adjusting any */
468 /* out of range values. */
469 if ((err = sa1100fb_decode_var(var, &par)))
470 return err;
471 // Store adjusted par values into var structure
472 sa1100fb_encode_var(var, &par);
474 if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_TEST)
475 return 0;
476 else if (((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NOW) &&
477 ((var->activate & FB_ACTIVATE_MASK) != FB_ACTIVATE_NXTOPEN))
478 return -EINVAL;
480 if (con >= 0) {
481 if ((display->var.xres != var->xres) ||
482 (display->var.yres != var->yres) ||
483 (display->var.xres_virtual != var->xres_virtual) ||
484 (display->var.yres_virtual != var->yres_virtual) ||
485 (display->var.sync != var->sync) ||
486 (display->var.bits_per_pixel != var->bits_per_pixel) ||
487 (memcmp(&display->var.red, &var->red, sizeof(var->red))) ||
488 (memcmp(&display->var.green, &var->green, sizeof(var->green))) ||
489 (memcmp(&display->var.blue, &var->blue, sizeof(var->blue))))
490 chgvar = 1;
492 DPRINTK("chgvar=%d\n", chgvar);
494 display->var = *var;
495 display->screen_base = par.v_screen_base;
496 display->visual = par.visual;
497 display->type = FB_TYPE_PACKED_PIXELS;
498 display->type_aux = 0;
499 display->ypanstep = 0;
500 display->ywrapstep = 0;
501 display->line_length =
502 display->next_line = (var->xres * var->bits_per_pixel) / 8;
504 display->can_soft_blank = 1;
505 display->inverse = 0;
507 DPRINTK("display->var.bits_per_pixel=%d xres=%d yres=%d display->dispsw=%p\n",
508 display->var.bits_per_pixel, var->xres, var->yres, display->dispsw);
509 switch (display->var.bits_per_pixel) {
510 #ifdef FBCON_HAS_CFB4
511 case 4:
512 display->dispsw = &fbcon_cfb4;
513 break;
514 #endif
515 #ifdef FBCON_HAS_CFB8
516 case 8:
517 display->dispsw = &fbcon_cfb8;
518 break;
519 #endif
520 #ifdef FBCON_HAS_CFB16
521 case 16:
522 display->dispsw = &fbcon_cfb16;
523 break;
524 #endif
525 default:
526 display->dispsw = &fbcon_dummy;
527 break;
530 /* If the console has changed and the console has defined */
531 /* a changevar function, call that function. */
532 if (chgvar && info && info->changevar)
533 info->changevar(con);
535 /* If the current console is selected and it's not truecolor,
536 * update the palette
538 if ((con == current_par.currcon) &&
539 (current_par.visual != FB_VISUAL_TRUECOLOR)) {
540 struct fb_cmap *cmap;
542 current_par = par;
543 if (display->cmap.len)
544 cmap = &display->cmap;
545 else
546 cmap = fb_default_cmap(current_par.palette_size);
547 DPRINTK("visual=%d palette_size=%d cmap=%p\n", current_par.visual, current_par.palette_size, cmap);
549 fb_set_cmap(cmap, 1, sa1100fb_setcolreg, info);
552 /* If the current console is selected, activate the new var. */
553 if (con == current_par.currcon)
554 sa1100fb_activate_var(var);
556 return 0;
559 static int
560 sa1100fb_updatevar(int con, struct fb_info *info)
562 DPRINTK("entered\n");
563 return 0;
566 static int
567 sa1100fb_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info)
569 struct display *display;
571 memset(fix, 0, sizeof(struct fb_fix_screeninfo));
572 strcpy(fix->id, SA1100_NAME);
574 if (con >= 0)
576 DPRINTK("Using console specific display for con=%d\n",con);
577 display = &fb_display[con]; /* Display settings for console */
579 else
580 display = &global_disp; /* Default display settings */
582 fix->smem_start = (unsigned long)current_par.p_screen_base;
583 fix->smem_len = current_par.screen_size;
584 fix->type = display->type;
585 fix->type_aux = display->type_aux;
586 fix->xpanstep = 0;
587 fix->ypanstep = display->ypanstep;
588 fix->ywrapstep = display->ywrapstep;
589 fix->visual = display->visual;
590 fix->line_length = display->line_length;
591 fix->accel = FB_ACCEL_NONE;
593 return 0;
597 static void
598 __init sa1100fb_init_fbinfo(void)
600 strcpy(fb_info.modename, SA1100_NAME);
601 strcpy(fb_info.fontname, "Acorn8x8");
603 fb_info.node = -1;
604 fb_info.flags = FBINFO_FLAG_DEFAULT;
605 fb_info.fbops = &sa1100fb_ops;
606 fb_info.monspecs = monspecs;
607 fb_info.disp = &global_disp;
608 fb_info.changevar = NULL;
609 fb_info.switch_con = sa1100fb_switch;
610 fb_info.updatevar = sa1100fb_updatevar;
611 fb_info.blank = sa1100fb_blank;
614 * setup initial parameters
616 memset(&init_var, 0, sizeof(init_var));
618 init_var.transp.length = 0;
619 init_var.nonstd = 0;
620 init_var.activate = FB_ACTIVATE_NOW;
621 init_var.xoffset = 0;
622 init_var.yoffset = 0;
623 init_var.height = -1;
624 init_var.width = -1;
625 init_var.vmode = FB_VMODE_NONINTERLACED;
627 if (machine_is_assabet()) {
628 current_par.max_xres = 320;
629 current_par.max_yres = 240;
630 current_par.max_bpp = 16;
631 init_var.red.length = 5;
632 init_var.green.length = 6;
633 init_var.blue.length = 5;
634 init_var.grayscale = 0;
635 init_var.sync = 0;
636 } else if (machine_is_bitsy()) {
637 current_par.max_xres = 320;
638 current_par.max_yres = 240;
639 current_par.max_bpp = 16;
640 init_var.red.length = 5;
641 init_var.green.length = 6;
642 init_var.blue.length = 5;
643 init_var.grayscale = 0;
644 } else if (machine_is_brutus()) {
645 current_par.max_xres = 320;
646 current_par.max_yres = 240;
647 current_par.max_bpp = 8;
648 init_var.red.length = 4;
649 init_var.green = init_var.red;
650 init_var.blue = init_var.red;
651 init_var.sync = 0;
652 } else if (machine_is_lart()) {
653 current_par.max_xres = 320;
654 current_par.max_yres = 240;
655 current_par.max_bpp = 4;
656 init_var.red.length = 4;
657 init_var.green = init_var.red;
658 init_var.blue = init_var.red;
659 init_var.grayscale = 1;
660 init_var.pixclock = 150000;
661 init_var.sync = 0;
662 } else if (machine_is_penny()) {
663 current_par.max_xres = 640;
664 current_par.max_yres = 480;
665 current_par.max_bpp = 8;
666 init_var.red.length = 4;
667 init_var.green = init_var.red;
668 init_var.blue = init_var.red;
669 init_var.sync = 0;
670 } else if (machine_is_thinclient() || machine_is_graphicsclient()) {
671 current_par.max_xres = 640;
672 current_par.max_yres = 480;
673 current_par.max_bpp = 8;
674 init_var.red.length = 4;
675 init_var.green = init_var.red;
676 init_var.blue = init_var.red;
677 init_var.sync = 0;
678 } else if (machine_is_tifon()) {
679 current_par.max_xres = 640;
680 current_par.max_yres = 200;
681 current_par.max_bpp = 4;
682 current_par.inv_4bpp = 1;
683 init_var.red.length = 4;
684 init_var.green = init_var.red;
685 init_var.blue = init_var.red;
686 init_var.grayscale = 1;
687 init_var.pixclock = 150000;
688 init_var.left_margin = 20;
689 init_var.right_margin = 255;
690 init_var.upper_margin = 20;
691 init_var.lower_margin = 0;
692 init_var.hsync_len = 2;
693 init_var.vsync_len = 1;
694 init_var.sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT;
695 init_var.vmode = 0;
698 current_par.p_palette_base = NULL;
699 current_par.v_palette_base = NULL;
700 current_par.p_screen_base = NULL;
701 current_par.v_screen_base = NULL;
702 current_par.palette_size = MAX_PALETTE_NUM_ENTRIES;
703 current_par.screen_size = MAX_PIXEL_MEM_SIZE;
704 current_par.montype = -1;
705 current_par.currcon = -1;
706 current_par.allow_modeset = 1;
707 current_par.controller_state = LCD_MODE_DISABLED;
709 init_var.xres = current_par.max_xres;
710 init_var.yres = current_par.max_yres;
711 init_var.xres_virtual = init_var.xres;
712 init_var.yres_virtual = init_var.yres;
713 init_var.bits_per_pixel = current_par.max_bpp;
720 * sa1100fb_map_video_memory():
721 * Allocates the DRAM memory for the frame buffer. This buffer is
722 * remapped into a non-cached, non-buffered, memory region to
723 * allow palette and pixel writes to occur without flushing the
724 * cache. Once this area is remapped, all virtual memory
725 * access to the video memory should occur at the new region.
727 static int
728 __init sa1100fb_map_video_memory(void)
730 u_int required_pages;
731 u_int extra_pages;
732 u_int order;
733 u_int i;
734 char *allocated_region;
736 if (VideoMemRegion != NULL)
737 return -EINVAL;
739 DPRINTK("-1-");
741 /* Find order required to allocate enough memory for framebuffer */
742 required_pages = ALLOCATED_FB_MEM_SIZE >> PAGE_SHIFT;
743 for (order = 0 ; required_pages >> order ; order++) {;}
744 extra_pages = (1 << order) - required_pages;
746 if ((allocated_region =
747 (char *)__get_free_pages(GFP_KERNEL | GFP_DMA, order)) == NULL)
748 return -ENOMEM;
750 VideoMemRegion = (u_char *)allocated_region + (extra_pages << PAGE_SHIFT);
751 VideoMemRegion_phys = (u_char *)__virt_to_phys((u_long)VideoMemRegion);
753 /* Free all pages that we don't need but were given to us because */
754 /* __get_free_pages() works on powers of 2. */
755 for (;extra_pages;extra_pages--)
756 free_page((u_int)allocated_region + ((extra_pages-1) << PAGE_SHIFT));
758 /* Set reserved flag for fb memory to allow it to be remapped into */
759 /* user space by the common fbmem driver using remap_page_range(). */
760 for(i = MAP_NR(VideoMemRegion);
761 i < MAP_NR(VideoMemRegion + ALLOCATED_FB_MEM_SIZE); i++)
762 set_bit(PG_reserved, &mem_map[i].flags);
764 /* Remap the fb memory to a non-buffered, non-cached region */
765 VideoMemRegion = (u_char *)__ioremap((u_long)VideoMemRegion_phys,
766 ALLOCATED_FB_MEM_SIZE,
767 L_PTE_PRESENT |
768 L_PTE_YOUNG |
769 L_PTE_DIRTY |
770 L_PTE_WRITE);
771 memset(VideoMemRegion, 0xAA, ALLOCATED_FB_MEM_SIZE);
772 return (VideoMemRegion == NULL ? -EINVAL : 0);
775 static const int frequency[16] = {
776 59000000,
777 73700000,
778 88500000,
779 103200000,
780 118000000,
781 132700000,
782 147500000,
783 162200000,
784 176900000,
785 191700000,
786 206400000,
787 230000000,
788 245000000,
789 260000000,
790 275000000,
791 290000000
795 static inline int get_pcd(unsigned int pixclock)
797 unsigned int pcd = 0;
799 if (machine_is_tifon()) {
800 pcd = frequency[PPCR &0xf] / 1000;
801 pcd *= pixclock/1000;
802 pcd = pcd / 10000000 * 12;
803 /* the last multiplication by 1.2 is to handle */
804 /* sync problems */
806 return pcd;
811 * sa1100fb_activate_var():
812 * Configures LCD Controller based on entries in var parameter. Settings are
813 * only written to the controller if changes were made.
815 static int
816 sa1100fb_activate_var(struct fb_var_screeninfo *var)
818 u_long flags;
819 int pcd = get_pcd(var->pixclock);
821 DPRINTK("Configuring SA1100 LCD\n");
823 if (current_par.p_palette_base == NULL)
824 return -EINVAL;
826 DPRINTK("activating\n");
828 /* Disable interrupts and save status */
829 save_flags_cli(flags); // disable the interrupts and save flags
831 /* Reset the LCD Controller's DMA address if it has changed */
832 lcd_shadow.dbar1 = (Address)current_par.p_palette_base;
834 DPRINTK("Configuring xres = %d, yres = %d\n",var->xres, var->yres);
836 if (machine_is_assabet()) {
837 DPRINTK("Configuring Assabet LCD\n");
838 lcd_shadow.lccr0 =
839 LCCR0_LEN + LCCR0_Color + LCCR0_Sngl +
840 LCCR0_LDM + LCCR0_BAM + LCCR0_ERM + LCCR0_Act +
841 LCCR0_LtlEnd + LCCR0_DMADel(0);
842 lcd_shadow.lccr1 =
843 LCCR1_DisWdth(var->xres) + LCCR1_HorSnchWdth(6) +
844 LCCR1_BegLnDel(61) + LCCR1_EndLnDel(9);
845 lcd_shadow.lccr2 =
846 LCCR2_DisHght(var->yres) + LCCR2_VrtSnchWdth(2) +
847 LCCR2_BegFrmDel(3) + LCCR2_EndFrmDel(0);
848 lcd_shadow.lccr3 =
849 LCCR3_OutEnH + LCCR3_PixFlEdg + LCCR3_VrtSnchH +
850 LCCR3_HorSnchH + LCCR3_ACBsCntOff +
851 LCCR3_ACBsDiv(2) + LCCR3_PixClkDiv(38);
853 /* Set board control register to handle new color depth */
854 sa1100fb_assabet_set_truecolor(var->bits_per_pixel >= 16);
855 } else if (machine_is_bitsy()) {
856 DPRINTK("Configuring Bitsy LCD\n");
857 lcd_shadow.lccr0 = LCCR0_LEN + LCCR0_Color + LCCR0_Sngl + LCCR0_Act +
858 LCCR0_LtlEnd + LCCR0_LDM + LCCR0_BAM + LCCR0_ERM +
859 LCCR0_DMADel(0);
860 lcd_shadow.lccr1 = LCCR1_DisWdth( var->xres ) +
861 LCCR1_HorSnchWdth( 4 ) +
862 LCCR1_BegLnDel( 0x1f ) +
863 LCCR1_EndLnDel( 0x1f );
864 lcd_shadow.lccr2 = LCCR2_DisHght( var->yres ) +
865 LCCR2_VrtSnchWdth( 1 )+
866 LCCR2_BegFrmDel( 0 ) +
867 LCCR2_EndFrmDel( 0 );
868 lcd_shadow.lccr3 = 15;
869 } else if (machine_is_brutus()) {
870 DPRINTK("Configuring Brutus LCD\n");
871 lcd_shadow.lccr0 =
872 LCCR0_LEN + LCCR0_Color + LCCR0_Sngl + LCCR0_Pas +
873 LCCR0_LtlEnd + LCCR0_LDM + LCCR0_BAM + LCCR0_ERM +
874 LCCR0_DMADel(0);
875 lcd_shadow.lccr1 =
876 LCCR1_DisWdth(var->xres) + LCCR1_HorSnchWdth(4) +
877 LCCR1_BegLnDel(41) + LCCR1_EndLnDel(101);
878 lcd_shadow.lccr2 =
879 LCCR2_DisHght(var->yres) + LCCR2_VrtSnchWdth(1) +
880 LCCR2_BegFrmDel(0) + LCCR2_EndFrmDel(0);
881 lcd_shadow.lccr3 =
882 LCCR3_OutEnH + LCCR3_PixFlEdg + LCCR3_VrtSnchH +
883 LCCR3_HorSnchH + LCCR3_ACBsCntOff +
884 LCCR3_ACBsDiv(2) + LCCR3_PixClkDiv(44);
885 } else if (machine_is_lart()) {
886 DPRINTK("Configuring LART LCD\n");
887 lcd_shadow.lccr0 =
888 LCCR0_LEN + LCCR0_Mono + LCCR0_Sngl + LCCR0_Pas +
889 LCCR0_LtlEnd + LCCR0_LDM + LCCR0_BAM + LCCR0_ERM +
890 LCCR0_DMADel(0);
891 lcd_shadow.lccr1 =
892 LCCR1_DisWdth(var->xres) + LCCR1_HorSnchWdth(2) +
893 LCCR1_BegLnDel(4) + LCCR1_EndLnDel(2);
894 lcd_shadow.lccr2 =
895 LCCR2_DisHght(var->yres) + LCCR2_VrtSnchWdth(1) +
896 LCCR2_BegFrmDel(0) + LCCR2_EndFrmDel(0);
897 lcd_shadow.lccr3 =
898 LCCR3_PixClkDiv(34) + LCCR3_ACBsDiv(512) +
899 LCCR3_ACBsCntOff + LCCR3_HorSnchH + LCCR3_VrtSnchH;
900 } else if (machine_is_penny()) {
901 DPRINTK("Configuring Penny LCD\n");
902 lcd_shadow.lccr0 =
903 LCCR0_LEN + LCCR0_Color + LCCR0_Sngl + LCCR0_Act +
904 LCCR0_LtlEnd + LCCR0_LDM + LCCR0_BAM + LCCR0_ERM +
905 LCCR0_DMADel(0);
906 lcd_shadow.lccr1 =
907 LCCR1_DisWdth(var->xres) + LCCR1_HorSnchWdth(65) +
908 LCCR1_EndLnDel(43) + LCCR1_BegLnDel(43);
909 lcd_shadow.lccr2 =
910 LCCR2_DisHght(var->yres) + LCCR2_VrtSnchWdth(35) +
911 LCCR2_EndFrmDel(0) + LCCR2_BegFrmDel(0);
912 lcd_shadow.lccr3 =
913 LCCR3_PixClkDiv(16) + LCCR3_ACBsDiv (2) + LCCR3_ACBsCntOff +
914 ((var->sync & FB_SYNC_HOR_HIGH_ACT) ? LCCR3_HorSnchH : LCCR3_HorSnchL) +
915 ((var->sync & FB_SYNC_VERT_HIGH_ACT) ? LCCR3_VrtSnchH : LCCR3_VrtSnchL);
916 } else if (machine_is_thinclient() || machine_is_graphicsclient()) {
917 DPRINTK("Configuring ThinClient LCD\n");
918 lcd_shadow.lccr0 =
919 LCCR0_LEN + LCCR0_Color + LCCR0_Sngl + LCCR0_Act;
920 lcd_shadow.lccr1 =
921 LCCR1_DisWdth(var->xres) + LCCR1_HorSnchWdth(10) +
922 LCCR1_EndLnDel(81) + LCCR1_BegLnDel(81);
923 lcd_shadow.lccr2 =
924 LCCR2_DisHght(var->yres) + LCCR2_VrtSnchWdth(9) +
925 LCCR2_EndFrmDel (20) + LCCR2_BegFrmDel(20);
926 lcd_shadow.lccr3 =
927 LCCR3_PixClkDiv(6) + LCCR3_ACBsDiv(2) +
928 LCCR3_ACBsCntOff + LCCR3_HorSnchL + LCCR3_VrtSnchL;
929 } else if (machine_is_tifon()) {
930 DPRINTK("Configuring TIFON LCD\n");
931 lcd_shadow.lccr0 =
932 LCCR0_LEN + LCCR0_Mono + LCCR0_Sngl + LCCR0_Pas +
933 LCCR0_BigEnd + LCCR0_LDM + LCCR0_BAM + LCCR0_ERM +
934 LCCR0_8PixMono + LCCR0_DMADel(0);
935 lcd_shadow.lccr1 =
936 LCCR1_DisWdth(var->xres) +
937 LCCR1_HorSnchWdth(var->hsync_len) +
938 LCCR1_BegLnDel(var->left_margin) +
939 LCCR1_EndLnDel(var->right_margin);
940 lcd_shadow.lccr2 =
941 LCCR2_DisHght(var->yres) +
942 LCCR2_VrtSnchWdth(var->vsync_len) +
943 LCCR2_BegFrmDel(var->upper_margin) +
944 LCCR2_EndFrmDel(var->lower_margin);
945 lcd_shadow.lccr3 =
946 LCCR3_PixClkDiv(pcd) + LCCR3_ACBsDiv(512) +
947 LCCR3_ACBsCnt(0) + LCCR3_HorSnchH + LCCR3_VrtSnchH;
949 ((current_var.sync & FB_SYNC_HOR_HIGH_ACT) ? LCCR3_HorSnchH : LCCR3_HorSnchL) +
950 ((current_var.sync & FB_SYNC_VERT_HIGH_ACT) ? LCCR3_VrtSnchH : LCCR3_VrtSnchL);
954 /* Restore interrupt status */
955 restore_flags(flags);
957 if (( LCCR0 != lcd_shadow.lccr0 ) ||
958 ( LCCR1 != lcd_shadow.lccr1 ) ||
959 ( LCCR2 != lcd_shadow.lccr2 ) ||
960 ( LCCR3 != lcd_shadow.lccr3 ) ||
961 ( DBAR1 != lcd_shadow.dbar1 ))
963 sa1100fb_enable_lcd_controller();
966 return 0;
971 * sa1100fb_inter_handler():
972 * Interrupt handler for LCD controller. Processes disable done interrupt (LDD)
973 * to reenable controller if controller was disabled to change register values.
975 static void sa1100fb_inter_handler(int irq, void *dev_id, struct pt_regs *regs)
977 if (LCSR & LCSR_LDD) {
978 /* Disable Done Flag is set */
979 LCCR0 |= LCCR0_LDM; /* Mask LCD Disable Done Interrupt */
980 current_par.controller_state = LCD_MODE_DISABLED;
981 if (current_par.controller_state == LCD_MODE_DISABLE_BEFORE_ENABLE) {
982 sa1100fb_enable_lcd_controller();
985 LCSR = 0; /* Clear LCD Status Register */
990 * sa1100fb_disable_lcd_controller():
991 * Disables LCD controller by and enables LDD interrupt. The controller_state
992 * is not changed until the LDD interrupt is received to indicate the current
993 * frame has completed. Platform specific hardware disabling is also included.
995 static void sa1100fb_disable_lcd_controller(void)
997 DPRINTK("Disabling LCD controller\n");
999 /* Exit if already LCD disabled, or LDD IRQ unmasked */
1000 if ((current_par.controller_state == LCD_MODE_DISABLED) ||
1001 (!(LCCR0 & LCCR0_LDM))) {
1002 DPRINTK("LCD already disabled\n");
1003 return;
1006 if (machine_is_assabet()) {
1007 #ifdef CONFIG_SA1100_ASSABET
1008 BCR_clear(BCR_LCD_ON);
1009 #endif
1010 } else if (machine_is_bitsy()) {
1011 #ifdef CONFIG_SA1100_BITSY
1012 clr_bitsy_egpio(EGPIO_BITSY_LCD_ON | EGPIO_BITSY_LCD_PCI | EGPIO_BITSY_LCD_5V_ON | EGPIO_BITSY_LVDD_ON);
1013 #endif
1014 } else if (machine_is_penny()) {
1015 #ifdef CONFIG_SA1100_PENNY
1016 FpgaLcdCS1 = 0x000; /* LCD Backlight to 0% */
1017 FpgaPortI &= ~LCD_ON; /* Turn off LCD Backlight */
1018 #endif
1019 } else if (machine_is_tifon()) {
1020 GPCR = GPIO_GPIO(24); /* turn off display */
1023 LCSR = 0; /* Clear LCD Status Register */
1024 LCCR0 &= ~(LCCR0_LDM); /* Enable LCD Disable Done Interrupt */
1025 enable_irq(IRQ_LCD); /* Enable LCD IRQ */
1026 LCCR0 &= ~(LCCR0_LEN); /* Disable LCD Controller */
1031 * sa1100fb_enable_lcd_controller():
1032 * Enables LCD controller. If the controller is already enabled, it is first disabled.
1033 * This forces all changes to the LCD controller registers to be done when the
1034 * controller is disabled. Platform specific hardware enabling is also included.
1036 static void sa1100fb_enable_lcd_controller(void)
1038 u_long flags;
1040 save_flags_cli(flags);
1042 /* Disable controller before changing parameters */
1043 if (current_par.controller_state == LCD_MODE_ENABLED) {
1044 current_par.controller_state = LCD_MODE_DISABLE_BEFORE_ENABLE;
1045 sa1100fb_disable_lcd_controller();
1046 } else {
1047 DPRINTK("Enabling LCD controller\n");
1049 /* Make sure the mode bits are present in the first palette entry */
1050 current_par.v_palette_base[0] &= 0x0FFF;
1051 current_par.v_palette_base[0] |= SA1100_PALETTE_MODE_VAL(current_par.bits_per_pixel);
1053 /* disable the interrupts and save flags */
1054 save_flags_cli(flags);
1056 DBAR1 = lcd_shadow.dbar1;
1057 LCCR3 = lcd_shadow.lccr3;
1058 LCCR2 = lcd_shadow.lccr2;
1059 LCCR1 = lcd_shadow.lccr1;
1060 LCCR0 = lcd_shadow.lccr0;
1062 if (machine_is_assabet()) {
1063 #ifdef CONFIG_SA1100_ASSABET
1064 BCR_set(BCR_LCD_ON);
1065 #endif
1066 } else if (machine_is_bitsy()) {
1067 #ifdef CONFIG_SA1100_BITSY
1068 set_bitsy_egpio(EGPIO_BITSY_LCD_ON | EGPIO_BITSY_LCD_PCI | EGPIO_BITSY_LCD_5V_ON | EGPIO_BITSY_LVDD_ON)
1069 DPRINTK("DBAR1=%p\n", DBAR1);
1070 DPRINTK("LCCR0=%x\n", LCCR0);
1071 DPRINTK("LCCR1=%x\n", LCCR1);
1072 DPRINTK("LCCR2=%x\n", LCCR2);
1073 DPRINTK("LCCR3=%x\n", LCCR3);
1074 #endif
1075 } else if (machine_is_penny()) {
1076 #ifdef CONFIG_SA1100_PENNY
1077 FpgaLcdCS1 = 0x0FF; /* LCD Backlight to 100% */
1078 FpgaPortI |= LCD_ON; /* Turn on LCD Backlight */
1079 #endif
1080 } else if (machine_is_tifon()) {
1081 GPCR = GPIO_GPIO(24); /* cycle on/off-switch */
1082 udelay(150);
1083 GPSR = GPIO_GPIO(24); /* turn on display */
1086 current_par.controller_state = LCD_MODE_ENABLED;
1089 /* Restore interrupt status */
1090 restore_flags(flags);
1094 * sa1100fb_blank():
1095 * Blank the display by setting all palette values to zero. Note, the
1096 * 12 and 16 bpp modes don't really use the palette, so this will not
1097 * blank the display in all modes.
1099 static void
1100 sa1100fb_blank(int blank, struct fb_info *info)
1102 int i;
1104 DPRINTK("blank=%d info->modename=%s\n", blank, info->modename);
1105 if (blank) {
1106 if (current_par.visual != FB_VISUAL_TRUECOLOR)
1107 for (i = 0; i < current_par.palette_size; i++)
1108 sa1100fb_palette_write(i, sa1100fb_palette_encode(i, 0, 0, 0, 0));
1109 sa1100fb_disable_lcd_controller();
1111 else {
1112 if (current_par.visual != FB_VISUAL_TRUECOLOR)
1113 sa1100fb_set_cmap(&fb_display[current_par.currcon].cmap, 1,
1114 current_par.currcon, info);
1115 sa1100fb_enable_lcd_controller();
1121 * sa1100fb_switch():
1122 * Change to the specified console. Palette and video mode
1123 * are changed to the console's stored parameters.
1125 static int
1126 sa1100fb_switch(int con, struct fb_info *info)
1129 DPRINTK("con=%d info->modename=%s\n", con, info->modename);
1130 if (current_par.visual != FB_VISUAL_TRUECOLOR) {
1131 struct fb_cmap *cmap;
1132 if (current_par.currcon >= 0) {
1133 // Get the colormap for the selected console
1134 cmap = &fb_display[current_par.currcon].cmap;
1136 if (cmap->len)
1137 fb_get_cmap(cmap, 1, sa1100fb_getcolreg, info);
1141 current_par.currcon = con;
1142 fb_display[con].var.activate = FB_ACTIVATE_NOW;
1143 DPRINTK("fb_display[%d].var.activate=%x\n", con, fb_display[con].var.activate);
1144 sa1100fb_set_var(&fb_display[con].var, con, info);
1145 current_par.v_palette_base[0] = (current_par.v_palette_base[0] &
1146 0xcfff) | SA1100_PALETTE_MODE_VAL(current_par.bits_per_pixel);
1148 return 0;
1152 int __init sa1100fb_init(void)
1154 int ret;
1156 sa1100fb_init_fbinfo();
1158 /* Initialize video memory */
1159 if ((ret = sa1100fb_map_video_memory()) != 0)
1160 return ret;
1162 if (current_par.montype < 0 || current_par.montype > NR_MONTYPES)
1163 current_par.montype = 1;
1165 if (request_irq(IRQ_LCD, sa1100fb_inter_handler, SA_INTERRUPT, "SA1100 LCD", NULL) != 0) {
1166 printk("sa1100fb: failed in request_irq\n");
1167 return -EBUSY;
1169 DPRINTK("sa1100fb: request_irq succeeded\n");
1170 disable_irq(IRQ_LCD);
1172 if (machine_is_assabet()) {
1173 GPDR |= 0x3fc;
1174 GAFR |= 0x3fc;
1175 sa1100fb_assabet_set_truecolor(current_par.visual ==
1176 FB_VISUAL_TRUECOLOR);
1177 } else if (machine_is_bitsy()) {
1178 GPDR = (GPIO_LDD15 | GPIO_LDD14 | GPIO_LDD13 | GPIO_LDD12 | GPIO_LDD11 | GPIO_LDD10 | GPIO_LDD9 | GPIO_LDD8);
1179 GAFR |= (GPIO_LDD15 | GPIO_LDD14 | GPIO_LDD13 | GPIO_LDD12 | GPIO_LDD11 | GPIO_LDD10 | GPIO_LDD9 | GPIO_LDD8);
1180 } else if (machine_is_penny()) {
1181 #ifdef CONFIG_SA1100_PENNY
1182 GPDR |= GPIO_GPDR_GFX; /* GPIO Data Direction register for LCD data bits 8-11 */
1183 GAFR |= GPIO_GAFR_GFX; /* GPIO Alternate Function register for LCD data bits 8-11 */
1184 #endif
1185 } else if (machine_is_tifon()) {
1186 GPDR |= GPIO_GPIO(24); /* set GPIO24 to output */
1189 if (sa1100fb_set_var(&init_var, -1, &fb_info))
1190 current_par.allow_modeset = 0;
1191 sa1100fb_decode_var(&init_var, &current_par);
1193 register_framebuffer(&fb_info);
1195 /* This driver cannot be unloaded at the moment */
1196 MOD_INC_USE_COUNT;
1198 return 0;
1201 int __init sa1100fb_setup(char *options)
1203 return 0;