2 * linux/drivers/video/68328fb.c -- Low level implementation of the
3 * mc68x328 LCD frame buffer device
5 * Copyright (C) 2003 Georges Menie
7 * This driver assumes an already configured controller (e.g. from config.c)
8 * Keep the code clean of board specific initialization.
10 * This code has not been tested with colors, colormap management functions
11 * are minimal (no colormap data written to the 68328 registers...)
13 * initial version of this driver:
14 * Copyright (C) 1998,1999 Kenneth Albanowski <kjahds@kjahds.com>,
15 * The Silver Hammer Group, Ltd.
17 * this version is based on :
19 * linux/drivers/video/vfb.c -- Virtual frame buffer device
21 * Copyright (C) 2002 James Simmons
23 * Copyright (C) 1997 Geert Uytterhoeven
25 * This file is subject to the terms and conditions of the GNU General Public
26 * License. See the file COPYING in the main directory of this archive for
30 #include <linux/module.h>
31 #include <linux/kernel.h>
32 #include <linux/errno.h>
33 #include <linux/string.h>
35 #include <linux/tty.h>
36 #include <linux/slab.h>
37 #include <linux/vmalloc.h>
38 #include <linux/delay.h>
39 #include <linux/interrupt.h>
40 #include <asm/uaccess.h>
42 #include <linux/init.h>
44 #if defined(CONFIG_M68VZ328)
45 #include <asm/MC68VZ328.h>
46 #elif defined(CONFIG_M68EZ328)
47 #include <asm/MC68EZ328.h>
48 #elif defined(CONFIG_M68328)
49 #include <asm/MC68328.h>
51 #error wrong architecture for the MC68x328 frame buffer device
54 #if defined(CONFIG_FB_68328_INVERT)
55 #define MC68X328FB_MONO_VISUAL FB_VISUAL_MONO01
57 #define MC68X328FB_MONO_VISUAL FB_VISUAL_MONO10
60 static u_long videomemory
;
61 static u_long videomemorysize
;
63 static struct fb_info fb_info
;
64 static u32 mc68x328fb_pseudo_palette
[17];
66 static struct fb_var_screeninfo mc68x328fb_default __initdata
= {
70 .activate
= FB_ACTIVATE_TEST
,
80 .vmode
= FB_VMODE_NONINTERLACED
,
83 static struct fb_fix_screeninfo mc68x328fb_fix __initdata
= {
85 .type
= FB_TYPE_PACKED_PIXELS
,
89 .accel
= FB_ACCEL_NONE
,
93 * Interface used by the world
95 int mc68x328fb_init(void);
96 int mc68x328fb_setup(char *);
98 static int mc68x328fb_check_var(struct fb_var_screeninfo
*var
,
99 struct fb_info
*info
);
100 static int mc68x328fb_set_par(struct fb_info
*info
);
101 static int mc68x328fb_setcolreg(u_int regno
, u_int red
, u_int green
, u_int blue
,
102 u_int transp
, struct fb_info
*info
);
103 static int mc68x328fb_pan_display(struct fb_var_screeninfo
*var
,
104 struct fb_info
*info
);
105 static int mc68x328fb_mmap(struct fb_info
*info
, struct file
*file
,
106 struct vm_area_struct
*vma
);
108 static struct fb_ops mc68x328fb_ops
= {
109 .fb_check_var
= mc68x328fb_check_var
,
110 .fb_set_par
= mc68x328fb_set_par
,
111 .fb_setcolreg
= mc68x328fb_setcolreg
,
112 .fb_pan_display
= mc68x328fb_pan_display
,
113 .fb_fillrect
= cfb_fillrect
,
114 .fb_copyarea
= cfb_copyarea
,
115 .fb_imageblit
= cfb_imageblit
,
116 .fb_cursor
= soft_cursor
,
117 .fb_mmap
= mc68x328fb_mmap
,
124 static u_long
get_line_length(int xres_virtual
, int bpp
)
128 length
= xres_virtual
* bpp
;
129 length
= (length
+ 31) & ~31;
135 * Setting the video mode has been split into two parts.
136 * First part, xxxfb_check_var, must not write anything
137 * to hardware, it should only verify and adjust var.
138 * This means it doesn't alter par but it does use hardware
139 * data from it to check this var.
142 static int mc68x328fb_check_var(struct fb_var_screeninfo
*var
,
143 struct fb_info
*info
)
148 * FB_VMODE_CONUPDATE and FB_VMODE_SMOOTH_XPAN are equal!
149 * as FB_VMODE_SMOOTH_XPAN is only used internally
152 if (var
->vmode
& FB_VMODE_CONUPDATE
) {
153 var
->vmode
|= FB_VMODE_YWRAP
;
154 var
->xoffset
= info
->var
.xoffset
;
155 var
->yoffset
= info
->var
.yoffset
;
159 * Some very basic checks
165 if (var
->xres
> var
->xres_virtual
)
166 var
->xres_virtual
= var
->xres
;
167 if (var
->yres
> var
->yres_virtual
)
168 var
->yres_virtual
= var
->yres
;
169 if (var
->bits_per_pixel
<= 1)
170 var
->bits_per_pixel
= 1;
171 else if (var
->bits_per_pixel
<= 8)
172 var
->bits_per_pixel
= 8;
173 else if (var
->bits_per_pixel
<= 16)
174 var
->bits_per_pixel
= 16;
175 else if (var
->bits_per_pixel
<= 24)
176 var
->bits_per_pixel
= 24;
177 else if (var
->bits_per_pixel
<= 32)
178 var
->bits_per_pixel
= 32;
182 if (var
->xres_virtual
< var
->xoffset
+ var
->xres
)
183 var
->xres_virtual
= var
->xoffset
+ var
->xres
;
184 if (var
->yres_virtual
< var
->yoffset
+ var
->yres
)
185 var
->yres_virtual
= var
->yoffset
+ var
->yres
;
191 get_line_length(var
->xres_virtual
, var
->bits_per_pixel
);
192 if (line_length
* var
->yres_virtual
> videomemorysize
)
196 * Now that we checked it we alter var. The reason being is that the video
197 * mode passed in might not work but slight changes to it might make it
198 * work. This way we let the user know what is acceptable.
200 switch (var
->bits_per_pixel
) {
204 var
->green
.offset
= 0;
205 var
->green
.length
= 1;
206 var
->blue
.offset
= 0;
207 var
->blue
.length
= 1;
208 var
->transp
.offset
= 0;
209 var
->transp
.length
= 0;
214 var
->green
.offset
= 0;
215 var
->green
.length
= 8;
216 var
->blue
.offset
= 0;
217 var
->blue
.length
= 8;
218 var
->transp
.offset
= 0;
219 var
->transp
.length
= 0;
221 case 16: /* RGBA 5551 */
222 if (var
->transp
.length
) {
225 var
->green
.offset
= 5;
226 var
->green
.length
= 5;
227 var
->blue
.offset
= 10;
228 var
->blue
.length
= 5;
229 var
->transp
.offset
= 15;
230 var
->transp
.length
= 1;
231 } else { /* RGB 565 */
234 var
->green
.offset
= 5;
235 var
->green
.length
= 6;
236 var
->blue
.offset
= 11;
237 var
->blue
.length
= 5;
238 var
->transp
.offset
= 0;
239 var
->transp
.length
= 0;
242 case 24: /* RGB 888 */
245 var
->green
.offset
= 8;
246 var
->green
.length
= 8;
247 var
->blue
.offset
= 16;
248 var
->blue
.length
= 8;
249 var
->transp
.offset
= 0;
250 var
->transp
.length
= 0;
252 case 32: /* RGBA 8888 */
255 var
->green
.offset
= 8;
256 var
->green
.length
= 8;
257 var
->blue
.offset
= 16;
258 var
->blue
.length
= 8;
259 var
->transp
.offset
= 24;
260 var
->transp
.length
= 8;
263 var
->red
.msb_right
= 0;
264 var
->green
.msb_right
= 0;
265 var
->blue
.msb_right
= 0;
266 var
->transp
.msb_right
= 0;
271 /* This routine actually sets the video mode. It's in here where we
272 * the hardware state info->par and fix which can be affected by the
273 * change in par. For this driver it doesn't do much.
275 static int mc68x328fb_set_par(struct fb_info
*info
)
277 info
->fix
.line_length
= get_line_length(info
->var
.xres_virtual
,
278 info
->var
.bits_per_pixel
);
283 * Set a single color register. The values supplied are already
284 * rounded down to the hardware's capabilities (according to the
285 * entries in the var structure). Return != 0 for invalid regno.
288 static int mc68x328fb_setcolreg(u_int regno
, u_int red
, u_int green
, u_int blue
,
289 u_int transp
, struct fb_info
*info
)
291 if (regno
>= 256) /* no. of hw registers */
294 * Program hardware... do anything you want with transp
297 /* grayscale works only partially under directcolor */
298 if (info
->var
.grayscale
) {
299 /* grayscale = 0.30*R + 0.59*G + 0.11*B */
301 (red
* 77 + green
* 151 + blue
* 28) >> 8;
305 * var->{color}.offset contains start of bitfield
306 * var->{color}.length contains length of bitfield
307 * {hardwarespecific} contains width of RAMDAC
308 * cmap[X] is programmed to (X << red.offset) | (X << green.offset) | (X << blue.offset)
309 * RAMDAC[X] is programmed to (red, green, blue)
312 * uses offset = 0 && length = RAMDAC register width.
313 * var->{color}.offset is 0
314 * var->{color}.length contains widht of DAC
316 * RAMDAC[X] is programmed to (red, green, blue)
318 * does not use DAC. Usually 3 are present.
319 * var->{color}.offset contains start of bitfield
320 * var->{color}.length contains length of bitfield
321 * cmap is programmed to (red << red.offset) | (green << green.offset) |
322 * (blue << blue.offset) | (transp << transp.offset)
323 * RAMDAC does not exist
325 #define CNVT_TOHW(val,width) ((((val)<<(width))+0x7FFF-(val))>>16)
326 switch (info
->fix
.visual
) {
327 case FB_VISUAL_TRUECOLOR
:
328 case FB_VISUAL_PSEUDOCOLOR
:
329 red
= CNVT_TOHW(red
, info
->var
.red
.length
);
330 green
= CNVT_TOHW(green
, info
->var
.green
.length
);
331 blue
= CNVT_TOHW(blue
, info
->var
.blue
.length
);
332 transp
= CNVT_TOHW(transp
, info
->var
.transp
.length
);
334 case FB_VISUAL_DIRECTCOLOR
:
335 red
= CNVT_TOHW(red
, 8); /* expect 8 bit DAC */
336 green
= CNVT_TOHW(green
, 8);
337 blue
= CNVT_TOHW(blue
, 8);
338 /* hey, there is bug in transp handling... */
339 transp
= CNVT_TOHW(transp
, 8);
343 /* Truecolor has hardware independent palette */
344 if (info
->fix
.visual
== FB_VISUAL_TRUECOLOR
) {
350 v
= (red
<< info
->var
.red
.offset
) |
351 (green
<< info
->var
.green
.offset
) |
352 (blue
<< info
->var
.blue
.offset
) |
353 (transp
<< info
->var
.transp
.offset
);
354 switch (info
->var
.bits_per_pixel
) {
358 ((u32
*) (info
->pseudo_palette
))[regno
] = v
;
362 ((u32
*) (info
->pseudo_palette
))[regno
] = v
;
371 * Pan or Wrap the Display
373 * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
376 static int mc68x328fb_pan_display(struct fb_var_screeninfo
*var
,
377 struct fb_info
*info
)
379 if (var
->vmode
& FB_VMODE_YWRAP
) {
381 || var
->yoffset
>= info
->var
.yres_virtual
385 if (var
->xoffset
+ var
->xres
> info
->var
.xres_virtual
||
386 var
->yoffset
+ var
->yres
> info
->var
.yres_virtual
)
389 info
->var
.xoffset
= var
->xoffset
;
390 info
->var
.yoffset
= var
->yoffset
;
391 if (var
->vmode
& FB_VMODE_YWRAP
)
392 info
->var
.vmode
|= FB_VMODE_YWRAP
;
394 info
->var
.vmode
&= ~FB_VMODE_YWRAP
;
399 * Most drivers don't need their own mmap function
402 static int mc68x328fb_mmap(struct fb_info
*info
, struct file
*file
,
403 struct vm_area_struct
*vma
)
406 /* this is uClinux (no MMU) specific code */
408 vma
->vm_flags
|= VM_RESERVED
;
409 vma
->vm_start
= videomemory
;
417 int __init
mc68x328fb_setup(char *options
)
423 if (!options
|| !*options
)
426 while ((this_opt
= strsep(&options
, ",")) != NULL
) {
429 if (!strncmp(this_opt
, "disable", 7))
430 mc68x328fb_enable
= 0;
440 int __init
mc68x328fb_init(void)
445 if (fb_get_options("68328fb", &option
))
447 mc68x328fb_setup(option
);
450 * initialize the default mode from the LCD controller registers
452 mc68x328fb_default
.xres
= LXMAX
;
453 mc68x328fb_default
.yres
= LYMAX
+1;
454 mc68x328fb_default
.xres_virtual
= mc68x328fb_default
.xres
;
455 mc68x328fb_default
.yres_virtual
= mc68x328fb_default
.yres
;
456 mc68x328fb_default
.bits_per_pixel
= 1 + (LPICF
& 0x01);
458 videomemorysize
= (mc68x328fb_default
.xres_virtual
+7) / 8 *
459 mc68x328fb_default
.yres_virtual
* mc68x328fb_default
.bits_per_pixel
;
461 fb_info
.screen_base
= (void *)videomemory
;
462 fb_info
.fbops
= &mc68x328fb_ops
;
463 fb_info
.var
= mc68x328fb_default
;
464 fb_info
.fix
= mc68x328fb_fix
;
465 fb_info
.fix
.smem_start
= videomemory
;
466 fb_info
.fix
.smem_len
= videomemorysize
;
467 fb_info
.fix
.line_length
=
468 get_line_length(mc68x328fb_default
.xres_virtual
, mc68x328fb_default
.bits_per_pixel
);
469 fb_info
.fix
.visual
= (mc68x328fb_default
.bits_per_pixel
) == 1 ?
470 MC68X328FB_MONO_VISUAL
: FB_VISUAL_PSEUDOCOLOR
;
471 if (fb_info
.var
.bits_per_pixel
== 1) {
472 fb_info
.var
.red
.length
= fb_info
.var
.green
.length
= fb_info
.var
.blue
.length
= 1;
473 fb_info
.var
.red
.offset
= fb_info
.var
.green
.offset
= fb_info
.var
.blue
.offset
= 0;
475 fb_info
.pseudo_palette
= &mc68x328fb_pseudo_palette
;
476 fb_info
.flags
= FBINFO_DEFAULT
| FBINFO_HWACCEL_YPAN
;
478 fb_alloc_cmap(&fb_info
.cmap
, 256, 0);
480 if (register_framebuffer(&fb_info
) < 0) {
485 "fb%d: %s frame buffer device\n", fb_info
.node
, fb_info
.fix
.id
);
487 "fb%d: %dx%dx%d at 0x%08lx\n", fb_info
.node
,
488 mc68x328fb_default
.xres_virtual
, mc68x328fb_default
.yres_virtual
,
489 1 << mc68x328fb_default
.bits_per_pixel
, videomemory
);
494 module_init(mc68x328fb_init
);
498 static void __exit
mc68x328fb_cleanup(void)
500 unregister_framebuffer(&fb_info
);
503 module_exit(mc68x328fb_cleanup
);
505 MODULE_LICENSE("GPL");