2 * linux/drivers/video/sa1100fb.c -- StrongARM 1100 LCD Controller Frame Buffer Device
4 * Copyright (C) 1999 Eric A. Thomas
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
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>
34 #include <linux/tty.h>
35 #include <linux/malloc.h>
36 #include <linux/init.h>
38 #include <linux/delay.h>
40 #include <asm/hardware.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>
58 # define DPRINTK(fmt, args...) printk("%s: " fmt, __FUNCTION__ , ## args)
60 # define DPRINTK(fmt, args...)
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 */
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"
90 sa1100fb_assabet_set_truecolor(u_int is_true_color
)
92 #ifdef CONFIG_SA1100_ASSABET
94 // phase 4 or newer Assabet's
96 BCR_set(BCR_LCD_12RGB
);
98 BCR_clear(BCR_LCD_12RGB
);
102 BCR_clear(BCR_LCD_12RGB
);
104 BCR_set(BCR_LCD_12RGB
);
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
;
126 unsigned int xres_virtual
;
127 unsigned int yres_virtual
;
128 unsigned int max_bpp
;
129 unsigned int bits_per_pixel
;
131 unsigned int currcon
;
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
{
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
= {
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
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
)
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
)
210 pal
= ((red
>> 4) & 0xf00);
211 pal
|= ((green
>> 8) & 0x0f0);
212 pal
|= ((blue
>> 12) & 0x00f);
218 static inline u_short
219 sa1100fb_palette_read(u_int regno
)
221 return (current_par
.v_palette_base
[regno
] & 0x0FFF);
226 sa1100fb_palette_decode(u_int regno
, u_int
*red
, u_int
*green
, u_int
*blue
, u_int
*trans
)
230 pal
= sa1100fb_palette_read(regno
);
232 if( current_par
.bits_per_pixel
== 4){
233 if( current_par
.inv_4bpp
)
238 *blue
= *green
= *red
= pal
;
241 *blue
= (pal
& 0x000f) << 12;
242 *green
= (pal
& 0x00f0) << 8;
243 *red
= (pal
& 0x0f00) << 4;
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
)
254 sa1100fb_palette_decode(regno
, red
, green
, blue
, trans
);
261 sa1100fb_setcolreg(u_int regno
, u_int red
, u_int green
, u_int blue
, u_int trans
, struct fb_info
*info
)
265 if (regno
>= current_par
.palette_size
)
268 pal
= sa1100fb_palette_encode(regno
, red
, green
, blue
, trans
);
270 sa1100fb_palette_write(regno
, pal
);
276 sa1100fb_get_cmap(struct fb_cmap
*cmap
, int kspc
, int con
,
277 struct fb_info
*info
)
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);
287 fb_copy_cmap(fb_default_cmap(current_par
.palette_size
),
293 sa1100fb_set_cmap(struct fb_cmap
*cmap
, int kspc
, int con
,
294 struct fb_info
*info
)
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);
303 if (con
== current_par
.currcon
)
304 err
= fb_set_cmap(cmap
, kspc
, sa1100fb_setcolreg
,
306 fb_copy_cmap(cmap
, &fb_display
[con
].cmap
, kspc
? 0 : 1);
312 sa1100fb_get_par(struct sa1100fb_par
*par
)
319 * sa1100fb_encode_var():
320 * Modify var structure using values in par
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
) {
343 var
->green
= var
->red
;
344 var
->blue
= var
->red
;
345 var
->transp
.length
= 0;
347 case 12: // This case should differ for Active/Passive mode
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;
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.
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
;
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
;
389 var
->xres_virtual
< par
->xres
? par
->xres
: var
->xres_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
398 par
->visual
= FB_VISUAL_PSEUDOCOLOR
;
399 par
->palette_size
= 16;
402 #ifdef FBCON_HAS_CFB8
404 par
->visual
= FB_VISUAL_PSEUDOCOLOR
;
405 par
->palette_size
= 256;
408 #ifdef FBCON_HAS_CFB16
409 case 16: /* RGB 565 */
410 par
->visual
= FB_VISUAL_TRUECOLOR
;
411 par
->palette_size
= 0;
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
);
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
);
441 sa1100fb_get_par(&par
);
442 sa1100fb_encode_var(var
, &par
);
444 *var
= fb_display
[con
].var
;
450 * sa1100fb_set_var():
451 * Set the user defined part of the display for the specified console
454 sa1100fb_set_var(struct fb_var_screeninfo
*var
, int con
, struct fb_info
*info
)
456 struct display
*display
;
458 struct sa1100fb_par par
;
461 display
= &fb_display
[con
]; /* Display settings for console */
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
)))
471 // Store adjusted par values into var structure
472 sa1100fb_encode_var(var
, &par
);
474 if ((var
->activate
& FB_ACTIVATE_MASK
) == FB_ACTIVATE_TEST
)
476 else if (((var
->activate
& FB_ACTIVATE_MASK
) != FB_ACTIVATE_NOW
) &&
477 ((var
->activate
& FB_ACTIVATE_MASK
) != FB_ACTIVATE_NXTOPEN
))
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
))))
492 DPRINTK("chgvar=%d\n", chgvar
);
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
512 display
->dispsw
= &fbcon_cfb4
;
515 #ifdef FBCON_HAS_CFB8
517 display
->dispsw
= &fbcon_cfb8
;
520 #ifdef FBCON_HAS_CFB16
522 display
->dispsw
= &fbcon_cfb16
;
526 display
->dispsw
= &fbcon_dummy
;
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,
538 if ((con
== current_par
.currcon
) &&
539 (current_par
.visual
!= FB_VISUAL_TRUECOLOR
)) {
540 struct fb_cmap
*cmap
;
543 if (display
->cmap
.len
)
544 cmap
= &display
->cmap
;
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
);
560 sa1100fb_updatevar(int con
, struct fb_info
*info
)
562 DPRINTK("entered\n");
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
);
576 DPRINTK("Using console specific display for con=%d\n",con
);
577 display
= &fb_display
[con
]; /* Display settings for console */
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
;
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
;
598 __init
sa1100fb_init_fbinfo(void)
600 strcpy(fb_info
.modename
, SA1100_NAME
);
601 strcpy(fb_info
.fontname
, "Acorn8x8");
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;
620 init_var
.activate
= FB_ACTIVATE_NOW
;
621 init_var
.xoffset
= 0;
622 init_var
.yoffset
= 0;
623 init_var
.height
= -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;
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
;
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;
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
;
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
;
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
;
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.
728 __init
sa1100fb_map_video_memory(void)
730 u_int required_pages
;
734 char *allocated_region
;
736 if (VideoMemRegion
!= NULL
)
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
)
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
,
771 memset(VideoMemRegion
, 0xAA, ALLOCATED_FB_MEM_SIZE
);
772 return (VideoMemRegion
== NULL
? -EINVAL
: 0);
775 static const int frequency
[16] = {
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 */
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.
816 sa1100fb_activate_var(struct fb_var_screeninfo
*var
)
819 int pcd
= get_pcd(var
->pixclock
);
821 DPRINTK("Configuring SA1100 LCD\n");
823 if (current_par
.p_palette_base
== NULL
)
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");
839 LCCR0_LEN
+ LCCR0_Color
+ LCCR0_Sngl
+
840 LCCR0_LDM
+ LCCR0_BAM
+ LCCR0_ERM
+ LCCR0_Act
+
841 LCCR0_LtlEnd
+ LCCR0_DMADel(0);
843 LCCR1_DisWdth(var
->xres
) + LCCR1_HorSnchWdth(6) +
844 LCCR1_BegLnDel(61) + LCCR1_EndLnDel(9);
846 LCCR2_DisHght(var
->yres
) + LCCR2_VrtSnchWdth(2) +
847 LCCR2_BegFrmDel(3) + LCCR2_EndFrmDel(0);
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
+
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");
872 LCCR0_LEN
+ LCCR0_Color
+ LCCR0_Sngl
+ LCCR0_Pas
+
873 LCCR0_LtlEnd
+ LCCR0_LDM
+ LCCR0_BAM
+ LCCR0_ERM
+
876 LCCR1_DisWdth(var
->xres
) + LCCR1_HorSnchWdth(4) +
877 LCCR1_BegLnDel(41) + LCCR1_EndLnDel(101);
879 LCCR2_DisHght(var
->yres
) + LCCR2_VrtSnchWdth(1) +
880 LCCR2_BegFrmDel(0) + LCCR2_EndFrmDel(0);
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");
888 LCCR0_LEN
+ LCCR0_Mono
+ LCCR0_Sngl
+ LCCR0_Pas
+
889 LCCR0_LtlEnd
+ LCCR0_LDM
+ LCCR0_BAM
+ LCCR0_ERM
+
892 LCCR1_DisWdth(var
->xres
) + LCCR1_HorSnchWdth(2) +
893 LCCR1_BegLnDel(4) + LCCR1_EndLnDel(2);
895 LCCR2_DisHght(var
->yres
) + LCCR2_VrtSnchWdth(1) +
896 LCCR2_BegFrmDel(0) + LCCR2_EndFrmDel(0);
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");
903 LCCR0_LEN
+ LCCR0_Color
+ LCCR0_Sngl
+ LCCR0_Act
+
904 LCCR0_LtlEnd
+ LCCR0_LDM
+ LCCR0_BAM
+ LCCR0_ERM
+
907 LCCR1_DisWdth(var
->xres
) + LCCR1_HorSnchWdth(65) +
908 LCCR1_EndLnDel(43) + LCCR1_BegLnDel(43);
910 LCCR2_DisHght(var
->yres
) + LCCR2_VrtSnchWdth(35) +
911 LCCR2_EndFrmDel(0) + LCCR2_BegFrmDel(0);
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");
919 LCCR0_LEN
+ LCCR0_Color
+ LCCR0_Sngl
+ LCCR0_Act
;
921 LCCR1_DisWdth(var
->xres
) + LCCR1_HorSnchWdth(10) +
922 LCCR1_EndLnDel(81) + LCCR1_BegLnDel(81);
924 LCCR2_DisHght(var
->yres
) + LCCR2_VrtSnchWdth(9) +
925 LCCR2_EndFrmDel (20) + LCCR2_BegFrmDel(20);
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");
932 LCCR0_LEN
+ LCCR0_Mono
+ LCCR0_Sngl
+ LCCR0_Pas
+
933 LCCR0_BigEnd
+ LCCR0_LDM
+ LCCR0_BAM
+ LCCR0_ERM
+
934 LCCR0_8PixMono
+ LCCR0_DMADel(0);
936 LCCR1_DisWdth(var
->xres
) +
937 LCCR1_HorSnchWdth(var
->hsync_len
) +
938 LCCR1_BegLnDel(var
->left_margin
) +
939 LCCR1_EndLnDel(var
->right_margin
);
941 LCCR2_DisHght(var
->yres
) +
942 LCCR2_VrtSnchWdth(var
->vsync_len
) +
943 LCCR2_BegFrmDel(var
->upper_margin
) +
944 LCCR2_EndFrmDel(var
->lower_margin
);
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();
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");
1006 if (machine_is_assabet()) {
1007 #ifdef CONFIG_SA1100_ASSABET
1008 BCR_clear(BCR_LCD_ON
);
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
);
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 */
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)
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();
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
);
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
);
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 */
1080 } else if (machine_is_tifon()) {
1081 GPCR
= GPIO_GPIO(24); /* cycle on/off-switch */
1083 GPSR
= GPIO_GPIO(24); /* turn on display */
1086 current_par
.controller_state
= LCD_MODE_ENABLED
;
1089 /* Restore interrupt status */
1090 restore_flags(flags
);
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.
1100 sa1100fb_blank(int blank
, struct fb_info
*info
)
1104 DPRINTK("blank=%d info->modename=%s\n", blank
, info
->modename
);
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();
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.
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
;
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
);
1152 int __init
sa1100fb_init(void)
1156 sa1100fb_init_fbinfo();
1158 /* Initialize video memory */
1159 if ((ret
= sa1100fb_map_video_memory()) != 0)
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");
1169 DPRINTK("sa1100fb: request_irq succeeded\n");
1170 disable_irq(IRQ_LCD
);
1172 if (machine_is_assabet()) {
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 */
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
, ¤t_par
);
1193 register_framebuffer(&fb_info
);
1195 /* This driver cannot be unloaded at the moment */
1201 int __init
sa1100fb_setup(char *options
)