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_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
= {
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
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
)
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
)
215 pal
= ((red
>> 4) & 0xf00);
216 pal
|= ((green
>> 8) & 0x0f0);
217 pal
|= ((blue
>> 12) & 0x00f);
223 static inline u_short
224 sa1100fb_palette_read(u_int regno
)
226 return (current_par
.v_palette_base
[regno
] & 0x0FFF);
231 sa1100fb_palette_decode(u_int regno
, u_int
*red
, u_int
*green
, u_int
*blue
, u_int
*trans
)
235 pal
= sa1100fb_palette_read(regno
);
237 if( current_par
.bits_per_pixel
== 4){
238 if( current_par
.inv_4bpp
)
243 *blue
= *green
= *red
= pal
;
246 *blue
= (pal
& 0x000f) << 12;
247 *green
= (pal
& 0x00f0) << 8;
248 *red
= (pal
& 0x0f00) << 4;
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
)
259 sa1100fb_palette_decode(regno
, red
, green
, blue
, trans
);
266 sa1100fb_setcolreg(u_int regno
, u_int red
, u_int green
, u_int blue
, u_int trans
, struct fb_info
*info
)
270 if (regno
>= current_par
.palette_size
)
273 pal
= sa1100fb_palette_encode(regno
, red
, green
, blue
, trans
);
275 sa1100fb_palette_write(regno
, pal
);
281 sa1100fb_get_cmap(struct fb_cmap
*cmap
, int kspc
, int con
,
282 struct fb_info
*info
)
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);
292 fb_copy_cmap(fb_default_cmap(current_par
.palette_size
),
298 sa1100fb_set_cmap(struct fb_cmap
*cmap
, int kspc
, int con
,
299 struct fb_info
*info
)
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);
308 if (con
== current_par
.currcon
)
309 err
= fb_set_cmap(cmap
, kspc
, sa1100fb_setcolreg
,
311 fb_copy_cmap(cmap
, &fb_display
[con
].cmap
, kspc
? 0 : 1);
317 sa1100fb_get_par(struct sa1100fb_par
*par
)
324 * sa1100fb_encode_var():
325 * Modify var structure using values in par
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
) {
348 var
->green
= var
->red
;
349 var
->blue
= var
->red
;
350 var
->transp
.length
= 0;
352 case 12: // This case should differ for Active/Passive mode
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;
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.
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
;
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
;
394 var
->xres_virtual
< par
->xres
? par
->xres
: var
->xres_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
403 par
->visual
= FB_VISUAL_PSEUDOCOLOR
;
404 par
->palette_size
= 16;
407 #ifdef FBCON_HAS_CFB8
409 par
->visual
= FB_VISUAL_PSEUDOCOLOR
;
410 par
->palette_size
= 256;
413 #ifdef FBCON_HAS_CFB16
414 case 16: /* RGB 565 */
415 par
->visual
= FB_VISUAL_TRUECOLOR
;
416 par
->palette_size
= 0;
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
);
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
);
446 sa1100fb_get_par(&par
);
447 sa1100fb_encode_var(var
, &par
);
449 *var
= fb_display
[con
].var
;
455 * sa1100fb_set_var():
456 * Set the user defined part of the display for the specified console
459 sa1100fb_set_var(struct fb_var_screeninfo
*var
, int con
, struct fb_info
*info
)
461 struct display
*display
;
463 struct sa1100fb_par par
;
466 display
= &fb_display
[con
]; /* Display settings for console */
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
)))
476 // Store adjusted par values into var structure
477 sa1100fb_encode_var(var
, &par
);
479 if ((var
->activate
& FB_ACTIVATE_MASK
) == FB_ACTIVATE_TEST
)
481 else if (((var
->activate
& FB_ACTIVATE_MASK
) != FB_ACTIVATE_NOW
) &&
482 ((var
->activate
& FB_ACTIVATE_MASK
) != FB_ACTIVATE_NXTOPEN
))
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
))))
497 DPRINTK("chgvar=%d\n", chgvar
);
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
517 display
->dispsw
= &fbcon_cfb4
;
520 #ifdef FBCON_HAS_CFB8
522 display
->dispsw
= &fbcon_cfb8
;
525 #ifdef FBCON_HAS_CFB16
527 display
->dispsw
= &fbcon_cfb16
;
531 display
->dispsw
= &fbcon_dummy
;
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,
543 if ((con
== current_par
.currcon
) &&
544 (current_par
.visual
!= FB_VISUAL_TRUECOLOR
)) {
545 struct fb_cmap
*cmap
;
548 if (display
->cmap
.len
)
549 cmap
= &display
->cmap
;
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
);
565 sa1100fb_updatevar(int con
, struct fb_info
*info
)
567 DPRINTK("entered\n");
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
);
581 DPRINTK("Using console specific display for con=%d\n",con
);
582 display
= &fb_display
[con
]; /* Display settings for console */
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
;
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
;
603 __init
sa1100fb_init_fbinfo(void)
605 strcpy(fb_info
.modename
, SA1100_NAME
);
606 strcpy(fb_info
.fontname
, "Acorn8x8");
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;
625 init_var
.activate
= FB_ACTIVATE_NOW
;
626 init_var
.xoffset
= 0;
627 init_var
.yoffset
= 0;
628 init_var
.height
= -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;
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
;
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;
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
;
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
;
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
;
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.
733 __init
sa1100fb_map_video_memory(void)
735 u_int required_pages
;
739 char *allocated_region
;
741 if (VideoMemRegion
!= NULL
)
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
)
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
,
776 memset(VideoMemRegion
, 0xAA, ALLOCATED_FB_MEM_SIZE
);
777 return (VideoMemRegion
== NULL
? -EINVAL
: 0);
780 static const int frequency
[16] = {
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 */
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.
821 sa1100fb_activate_var(struct fb_var_screeninfo
*var
)
824 int pcd
= get_pcd(var
->pixclock
);
826 DPRINTK("Configuring SA1100 LCD\n");
828 if (current_par
.p_palette_base
== NULL
)
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");
844 LCCR0_LEN
+ LCCR0_Color
+ LCCR0_Sngl
+
845 LCCR0_LDM
+ LCCR0_BAM
+ LCCR0_ERM
+ LCCR0_Act
+
846 LCCR0_LtlEnd
+ LCCR0_DMADel(0);
848 LCCR1_DisWdth(var
->xres
) + LCCR1_HorSnchWdth(6) +
849 LCCR1_BegLnDel(61) + LCCR1_EndLnDel(9);
851 LCCR2_DisHght(var
->yres
) + LCCR2_VrtSnchWdth(2) +
852 LCCR2_BegFrmDel(3) + LCCR2_EndFrmDel(0);
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
+
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");
877 LCCR0_LEN
+ LCCR0_Color
+ LCCR0_Sngl
+ LCCR0_Pas
+
878 LCCR0_LtlEnd
+ LCCR0_LDM
+ LCCR0_BAM
+ LCCR0_ERM
+
881 LCCR1_DisWdth(var
->xres
) + LCCR1_HorSnchWdth(4) +
882 LCCR1_BegLnDel(41) + LCCR1_EndLnDel(101);
884 LCCR2_DisHght(var
->yres
) + LCCR2_VrtSnchWdth(1) +
885 LCCR2_BegFrmDel(0) + LCCR2_EndFrmDel(0);
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");
893 LCCR0_LEN
+ LCCR0_Mono
+ LCCR0_Sngl
+ LCCR0_Pas
+
894 LCCR0_LtlEnd
+ LCCR0_LDM
+ LCCR0_BAM
+ LCCR0_ERM
+
897 LCCR1_DisWdth(var
->xres
) + LCCR1_HorSnchWdth(2) +
898 LCCR1_BegLnDel(4) + LCCR1_EndLnDel(2);
900 LCCR2_DisHght(var
->yres
) + LCCR2_VrtSnchWdth(1) +
901 LCCR2_BegFrmDel(0) + LCCR2_EndFrmDel(0);
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");
908 LCCR0_LEN
+ LCCR0_Color
+ LCCR0_Sngl
+ LCCR0_Act
+
909 LCCR0_LtlEnd
+ LCCR0_LDM
+ LCCR0_BAM
+ LCCR0_ERM
+
912 LCCR1_DisWdth(var
->xres
) + LCCR1_HorSnchWdth(65) +
913 LCCR1_EndLnDel(43) + LCCR1_BegLnDel(43);
915 LCCR2_DisHght(var
->yres
) + LCCR2_VrtSnchWdth(35) +
916 LCCR2_EndFrmDel(0) + LCCR2_BegFrmDel(0);
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");
924 LCCR0_LEN
+ LCCR0_Color
+ LCCR0_Sngl
+ LCCR0_Act
;
926 LCCR1_DisWdth(var
->xres
) + LCCR1_HorSnchWdth(10) +
927 LCCR1_EndLnDel(81) + LCCR1_BegLnDel(81);
929 LCCR2_DisHght(var
->yres
) + LCCR2_VrtSnchWdth(9) +
930 LCCR2_EndFrmDel (20) + LCCR2_BegFrmDel(20);
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");
937 LCCR0_LEN
+ LCCR0_Mono
+ LCCR0_Sngl
+ LCCR0_Pas
+
938 LCCR0_BigEnd
+ LCCR0_LDM
+ LCCR0_BAM
+ LCCR0_ERM
+
939 LCCR0_8PixMono
+ LCCR0_DMADel(0);
941 LCCR1_DisWdth(var
->xres
) +
942 LCCR1_HorSnchWdth(var
->hsync_len
) +
943 LCCR1_BegLnDel(var
->left_margin
) +
944 LCCR1_EndLnDel(var
->right_margin
);
946 LCCR2_DisHght(var
->yres
) +
947 LCCR2_VrtSnchWdth(var
->vsync_len
) +
948 LCCR2_BegFrmDel(var
->upper_margin
) +
949 LCCR2_EndFrmDel(var
->lower_margin
);
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();
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");
1011 if (machine_is_assabet()) {
1012 #ifdef CONFIG_SA1100_ASSABET
1013 BCR_clear(BCR_LCD_ON
);
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
);
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 */
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)
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();
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
);
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
);
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 */
1085 } else if (machine_is_tifon()) {
1086 GPCR
= GPIO_GPIO(24); /* cycle on/off-switch */
1088 GPSR
= GPIO_GPIO(24); /* turn on display */
1091 current_par
.controller_state
= LCD_MODE_ENABLED
;
1094 /* Restore interrupt status */
1095 restore_flags(flags
);
1099 sa1100fb_pan_display(struct fb_var_screeninfo
*var
, int con
,
1100 struct fb_info
*info
)
1102 DPRINTK("entered\n");
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.
1114 sa1100fb_blank(int blank
, struct fb_info
*info
)
1118 DPRINTK("blank=%d info->modename=%s\n", blank
, info
->modename
);
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();
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.
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
;
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
);
1166 int __init
sa1100fb_init(void)
1170 sa1100fb_init_fbinfo();
1172 /* Initialize video memory */
1173 if ((ret
= sa1100fb_map_video_memory()) != 0)
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");
1183 DPRINTK("sa1100fb: request_irq succeeded\n");
1184 disable_irq(IRQ_LCD
);
1186 if (machine_is_assabet()) {
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 */
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
, ¤t_par
);
1207 register_framebuffer(&fb_info
);
1209 /* This driver cannot be unloaded at the moment */
1215 int __init
sa1100fb_setup(char *options
)
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
;