Root/
1 | /* |
2 | * linux/drivers/video/68328fb.c -- Low level implementation of the |
3 | * mc68x328 LCD frame buffer device |
4 | * |
5 | * Copyright (C) 2003 Georges Menie |
6 | * |
7 | * This driver assumes an already configured controller (e.g. from config.c) |
8 | * Keep the code clean of board specific initialization. |
9 | * |
10 | * This code has not been tested with colors, colormap management functions |
11 | * are minimal (no colormap data written to the 68328 registers...) |
12 | * |
13 | * initial version of this driver: |
14 | * Copyright (C) 1998,1999 Kenneth Albanowski <kjahds@kjahds.com>, |
15 | * The Silver Hammer Group, Ltd. |
16 | * |
17 | * this version is based on : |
18 | * |
19 | * linux/drivers/video/vfb.c -- Virtual frame buffer device |
20 | * |
21 | * Copyright (C) 2002 James Simmons |
22 | * |
23 | * Copyright (C) 1997 Geert Uytterhoeven |
24 | * |
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 |
27 | * more details. |
28 | */ |
29 | |
30 | #include <linux/module.h> |
31 | #include <linux/kernel.h> |
32 | #include <linux/errno.h> |
33 | #include <linux/string.h> |
34 | #include <linux/mm.h> |
35 | #include <linux/slab.h> |
36 | #include <linux/vmalloc.h> |
37 | #include <linux/delay.h> |
38 | #include <linux/interrupt.h> |
39 | #include <asm/uaccess.h> |
40 | #include <linux/fb.h> |
41 | #include <linux/init.h> |
42 | |
43 | #if defined(CONFIG_M68VZ328) |
44 | #include <asm/MC68VZ328.h> |
45 | #elif defined(CONFIG_M68EZ328) |
46 | #include <asm/MC68EZ328.h> |
47 | #elif defined(CONFIG_M68328) |
48 | #include <asm/MC68328.h> |
49 | #else |
50 | #error wrong architecture for the MC68x328 frame buffer device |
51 | #endif |
52 | |
53 | #if defined(CONFIG_FB_68328_INVERT) |
54 | #define MC68X328FB_MONO_VISUAL FB_VISUAL_MONO01 |
55 | #else |
56 | #define MC68X328FB_MONO_VISUAL FB_VISUAL_MONO10 |
57 | #endif |
58 | |
59 | static u_long videomemory; |
60 | static u_long videomemorysize; |
61 | |
62 | static struct fb_info fb_info; |
63 | static u32 mc68x328fb_pseudo_palette[16]; |
64 | |
65 | static struct fb_var_screeninfo mc68x328fb_default __initdata = { |
66 | .red = { 0, 8, 0 }, |
67 | .green = { 0, 8, 0 }, |
68 | .blue = { 0, 8, 0 }, |
69 | .activate = FB_ACTIVATE_TEST, |
70 | .height = -1, |
71 | .width = -1, |
72 | .pixclock = 20000, |
73 | .left_margin = 64, |
74 | .right_margin = 64, |
75 | .upper_margin = 32, |
76 | .lower_margin = 32, |
77 | .hsync_len = 64, |
78 | .vsync_len = 2, |
79 | .vmode = FB_VMODE_NONINTERLACED, |
80 | }; |
81 | |
82 | static struct fb_fix_screeninfo mc68x328fb_fix __initdata = { |
83 | .id = "68328fb", |
84 | .type = FB_TYPE_PACKED_PIXELS, |
85 | .xpanstep = 1, |
86 | .ypanstep = 1, |
87 | .ywrapstep = 1, |
88 | .accel = FB_ACCEL_NONE, |
89 | }; |
90 | |
91 | /* |
92 | * Interface used by the world |
93 | */ |
94 | int mc68x328fb_init(void); |
95 | int mc68x328fb_setup(char *); |
96 | |
97 | static int mc68x328fb_check_var(struct fb_var_screeninfo *var, |
98 | struct fb_info *info); |
99 | static int mc68x328fb_set_par(struct fb_info *info); |
100 | static int mc68x328fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, |
101 | u_int transp, struct fb_info *info); |
102 | static int mc68x328fb_pan_display(struct fb_var_screeninfo *var, |
103 | struct fb_info *info); |
104 | static int mc68x328fb_mmap(struct fb_info *info, struct vm_area_struct *vma); |
105 | |
106 | static struct fb_ops mc68x328fb_ops = { |
107 | .fb_check_var = mc68x328fb_check_var, |
108 | .fb_set_par = mc68x328fb_set_par, |
109 | .fb_setcolreg = mc68x328fb_setcolreg, |
110 | .fb_pan_display = mc68x328fb_pan_display, |
111 | .fb_fillrect = cfb_fillrect, |
112 | .fb_copyarea = cfb_copyarea, |
113 | .fb_imageblit = cfb_imageblit, |
114 | .fb_mmap = mc68x328fb_mmap, |
115 | }; |
116 | |
117 | /* |
118 | * Internal routines |
119 | */ |
120 | |
121 | static u_long get_line_length(int xres_virtual, int bpp) |
122 | { |
123 | u_long length; |
124 | |
125 | length = xres_virtual * bpp; |
126 | length = (length + 31) & ~31; |
127 | length >>= 3; |
128 | return (length); |
129 | } |
130 | |
131 | /* |
132 | * Setting the video mode has been split into two parts. |
133 | * First part, xxxfb_check_var, must not write anything |
134 | * to hardware, it should only verify and adjust var. |
135 | * This means it doesn't alter par but it does use hardware |
136 | * data from it to check this var. |
137 | */ |
138 | |
139 | static int mc68x328fb_check_var(struct fb_var_screeninfo *var, |
140 | struct fb_info *info) |
141 | { |
142 | u_long line_length; |
143 | |
144 | /* |
145 | * FB_VMODE_CONUPDATE and FB_VMODE_SMOOTH_XPAN are equal! |
146 | * as FB_VMODE_SMOOTH_XPAN is only used internally |
147 | */ |
148 | |
149 | if (var->vmode & FB_VMODE_CONUPDATE) { |
150 | var->vmode |= FB_VMODE_YWRAP; |
151 | var->xoffset = info->var.xoffset; |
152 | var->yoffset = info->var.yoffset; |
153 | } |
154 | |
155 | /* |
156 | * Some very basic checks |
157 | */ |
158 | if (!var->xres) |
159 | var->xres = 1; |
160 | if (!var->yres) |
161 | var->yres = 1; |
162 | if (var->xres > var->xres_virtual) |
163 | var->xres_virtual = var->xres; |
164 | if (var->yres > var->yres_virtual) |
165 | var->yres_virtual = var->yres; |
166 | if (var->bits_per_pixel <= 1) |
167 | var->bits_per_pixel = 1; |
168 | else if (var->bits_per_pixel <= 8) |
169 | var->bits_per_pixel = 8; |
170 | else if (var->bits_per_pixel <= 16) |
171 | var->bits_per_pixel = 16; |
172 | else if (var->bits_per_pixel <= 24) |
173 | var->bits_per_pixel = 24; |
174 | else if (var->bits_per_pixel <= 32) |
175 | var->bits_per_pixel = 32; |
176 | else |
177 | return -EINVAL; |
178 | |
179 | if (var->xres_virtual < var->xoffset + var->xres) |
180 | var->xres_virtual = var->xoffset + var->xres; |
181 | if (var->yres_virtual < var->yoffset + var->yres) |
182 | var->yres_virtual = var->yoffset + var->yres; |
183 | |
184 | /* |
185 | * Memory limit |
186 | */ |
187 | line_length = |
188 | get_line_length(var->xres_virtual, var->bits_per_pixel); |
189 | if (line_length * var->yres_virtual > videomemorysize) |
190 | return -ENOMEM; |
191 | |
192 | /* |
193 | * Now that we checked it we alter var. The reason being is that the video |
194 | * mode passed in might not work but slight changes to it might make it |
195 | * work. This way we let the user know what is acceptable. |
196 | */ |
197 | switch (var->bits_per_pixel) { |
198 | case 1: |
199 | var->red.offset = 0; |
200 | var->red.length = 1; |
201 | var->green.offset = 0; |
202 | var->green.length = 1; |
203 | var->blue.offset = 0; |
204 | var->blue.length = 1; |
205 | var->transp.offset = 0; |
206 | var->transp.length = 0; |
207 | break; |
208 | case 8: |
209 | var->red.offset = 0; |
210 | var->red.length = 8; |
211 | var->green.offset = 0; |
212 | var->green.length = 8; |
213 | var->blue.offset = 0; |
214 | var->blue.length = 8; |
215 | var->transp.offset = 0; |
216 | var->transp.length = 0; |
217 | break; |
218 | case 16: /* RGBA 5551 */ |
219 | if (var->transp.length) { |
220 | var->red.offset = 0; |
221 | var->red.length = 5; |
222 | var->green.offset = 5; |
223 | var->green.length = 5; |
224 | var->blue.offset = 10; |
225 | var->blue.length = 5; |
226 | var->transp.offset = 15; |
227 | var->transp.length = 1; |
228 | } else { /* RGB 565 */ |
229 | var->red.offset = 0; |
230 | var->red.length = 5; |
231 | var->green.offset = 5; |
232 | var->green.length = 6; |
233 | var->blue.offset = 11; |
234 | var->blue.length = 5; |
235 | var->transp.offset = 0; |
236 | var->transp.length = 0; |
237 | } |
238 | break; |
239 | case 24: /* RGB 888 */ |
240 | var->red.offset = 0; |
241 | var->red.length = 8; |
242 | var->green.offset = 8; |
243 | var->green.length = 8; |
244 | var->blue.offset = 16; |
245 | var->blue.length = 8; |
246 | var->transp.offset = 0; |
247 | var->transp.length = 0; |
248 | break; |
249 | case 32: /* RGBA 8888 */ |
250 | var->red.offset = 0; |
251 | var->red.length = 8; |
252 | var->green.offset = 8; |
253 | var->green.length = 8; |
254 | var->blue.offset = 16; |
255 | var->blue.length = 8; |
256 | var->transp.offset = 24; |
257 | var->transp.length = 8; |
258 | break; |
259 | } |
260 | var->red.msb_right = 0; |
261 | var->green.msb_right = 0; |
262 | var->blue.msb_right = 0; |
263 | var->transp.msb_right = 0; |
264 | |
265 | return 0; |
266 | } |
267 | |
268 | /* This routine actually sets the video mode. It's in here where we |
269 | * the hardware state info->par and fix which can be affected by the |
270 | * change in par. For this driver it doesn't do much. |
271 | */ |
272 | static int mc68x328fb_set_par(struct fb_info *info) |
273 | { |
274 | info->fix.line_length = get_line_length(info->var.xres_virtual, |
275 | info->var.bits_per_pixel); |
276 | return 0; |
277 | } |
278 | |
279 | /* |
280 | * Set a single color register. The values supplied are already |
281 | * rounded down to the hardware's capabilities (according to the |
282 | * entries in the var structure). Return != 0 for invalid regno. |
283 | */ |
284 | |
285 | static int mc68x328fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, |
286 | u_int transp, struct fb_info *info) |
287 | { |
288 | if (regno >= 256) /* no. of hw registers */ |
289 | return 1; |
290 | /* |
291 | * Program hardware... do anything you want with transp |
292 | */ |
293 | |
294 | /* grayscale works only partially under directcolor */ |
295 | if (info->var.grayscale) { |
296 | /* grayscale = 0.30*R + 0.59*G + 0.11*B */ |
297 | red = green = blue = |
298 | (red * 77 + green * 151 + blue * 28) >> 8; |
299 | } |
300 | |
301 | /* Directcolor: |
302 | * var->{color}.offset contains start of bitfield |
303 | * var->{color}.length contains length of bitfield |
304 | * {hardwarespecific} contains width of RAMDAC |
305 | * cmap[X] is programmed to (X << red.offset) | (X << green.offset) | (X << blue.offset) |
306 | * RAMDAC[X] is programmed to (red, green, blue) |
307 | * |
308 | * Pseudocolor: |
309 | * uses offset = 0 && length = RAMDAC register width. |
310 | * var->{color}.offset is 0 |
311 | * var->{color}.length contains widht of DAC |
312 | * cmap is not used |
313 | * RAMDAC[X] is programmed to (red, green, blue) |
314 | * Truecolor: |
315 | * does not use DAC. Usually 3 are present. |
316 | * var->{color}.offset contains start of bitfield |
317 | * var->{color}.length contains length of bitfield |
318 | * cmap is programmed to (red << red.offset) | (green << green.offset) | |
319 | * (blue << blue.offset) | (transp << transp.offset) |
320 | * RAMDAC does not exist |
321 | */ |
322 | #define CNVT_TOHW(val,width) ((((val)<<(width))+0x7FFF-(val))>>16) |
323 | switch (info->fix.visual) { |
324 | case FB_VISUAL_TRUECOLOR: |
325 | case FB_VISUAL_PSEUDOCOLOR: |
326 | red = CNVT_TOHW(red, info->var.red.length); |
327 | green = CNVT_TOHW(green, info->var.green.length); |
328 | blue = CNVT_TOHW(blue, info->var.blue.length); |
329 | transp = CNVT_TOHW(transp, info->var.transp.length); |
330 | break; |
331 | case FB_VISUAL_DIRECTCOLOR: |
332 | red = CNVT_TOHW(red, 8); /* expect 8 bit DAC */ |
333 | green = CNVT_TOHW(green, 8); |
334 | blue = CNVT_TOHW(blue, 8); |
335 | /* hey, there is bug in transp handling... */ |
336 | transp = CNVT_TOHW(transp, 8); |
337 | break; |
338 | } |
339 | #undef CNVT_TOHW |
340 | /* Truecolor has hardware independent palette */ |
341 | if (info->fix.visual == FB_VISUAL_TRUECOLOR) { |
342 | u32 v; |
343 | |
344 | if (regno >= 16) |
345 | return 1; |
346 | |
347 | v = (red << info->var.red.offset) | |
348 | (green << info->var.green.offset) | |
349 | (blue << info->var.blue.offset) | |
350 | (transp << info->var.transp.offset); |
351 | switch (info->var.bits_per_pixel) { |
352 | case 8: |
353 | break; |
354 | case 16: |
355 | ((u32 *) (info->pseudo_palette))[regno] = v; |
356 | break; |
357 | case 24: |
358 | case 32: |
359 | ((u32 *) (info->pseudo_palette))[regno] = v; |
360 | break; |
361 | } |
362 | return 0; |
363 | } |
364 | return 0; |
365 | } |
366 | |
367 | /* |
368 | * Pan or Wrap the Display |
369 | * |
370 | * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag |
371 | */ |
372 | |
373 | static int mc68x328fb_pan_display(struct fb_var_screeninfo *var, |
374 | struct fb_info *info) |
375 | { |
376 | if (var->vmode & FB_VMODE_YWRAP) { |
377 | if (var->yoffset < 0 |
378 | || var->yoffset >= info->var.yres_virtual |
379 | || var->xoffset) |
380 | return -EINVAL; |
381 | } else { |
382 | if (var->xoffset + var->xres > info->var.xres_virtual || |
383 | var->yoffset + var->yres > info->var.yres_virtual) |
384 | return -EINVAL; |
385 | } |
386 | info->var.xoffset = var->xoffset; |
387 | info->var.yoffset = var->yoffset; |
388 | if (var->vmode & FB_VMODE_YWRAP) |
389 | info->var.vmode |= FB_VMODE_YWRAP; |
390 | else |
391 | info->var.vmode &= ~FB_VMODE_YWRAP; |
392 | return 0; |
393 | } |
394 | |
395 | /* |
396 | * Most drivers don't need their own mmap function |
397 | */ |
398 | |
399 | static int mc68x328fb_mmap(struct fb_info *info, struct vm_area_struct *vma) |
400 | { |
401 | #ifndef MMU |
402 | /* this is uClinux (no MMU) specific code */ |
403 | |
404 | vma->vm_flags |= VM_RESERVED; |
405 | vma->vm_start = videomemory; |
406 | |
407 | return 0; |
408 | #else |
409 | return -EINVAL; |
410 | #endif |
411 | } |
412 | |
413 | int __init mc68x328fb_setup(char *options) |
414 | { |
415 | #if 0 |
416 | char *this_opt; |
417 | #endif |
418 | |
419 | if (!options || !*options) |
420 | return 1; |
421 | #if 0 |
422 | while ((this_opt = strsep(&options, ",")) != NULL) { |
423 | if (!*this_opt) |
424 | continue; |
425 | if (!strncmp(this_opt, "disable", 7)) |
426 | mc68x328fb_enable = 0; |
427 | } |
428 | #endif |
429 | return 1; |
430 | } |
431 | |
432 | /* |
433 | * Initialisation |
434 | */ |
435 | |
436 | int __init mc68x328fb_init(void) |
437 | { |
438 | #ifndef MODULE |
439 | char *option = NULL; |
440 | |
441 | if (fb_get_options("68328fb", &option)) |
442 | return -ENODEV; |
443 | mc68x328fb_setup(option); |
444 | #endif |
445 | /* |
446 | * initialize the default mode from the LCD controller registers |
447 | */ |
448 | mc68x328fb_default.xres = LXMAX; |
449 | mc68x328fb_default.yres = LYMAX+1; |
450 | mc68x328fb_default.xres_virtual = mc68x328fb_default.xres; |
451 | mc68x328fb_default.yres_virtual = mc68x328fb_default.yres; |
452 | mc68x328fb_default.bits_per_pixel = 1 + (LPICF & 0x01); |
453 | videomemory = LSSA; |
454 | videomemorysize = (mc68x328fb_default.xres_virtual+7) / 8 * |
455 | mc68x328fb_default.yres_virtual * mc68x328fb_default.bits_per_pixel; |
456 | |
457 | fb_info.screen_base = (void *)videomemory; |
458 | fb_info.fbops = &mc68x328fb_ops; |
459 | fb_info.var = mc68x328fb_default; |
460 | fb_info.fix = mc68x328fb_fix; |
461 | fb_info.fix.smem_start = videomemory; |
462 | fb_info.fix.smem_len = videomemorysize; |
463 | fb_info.fix.line_length = |
464 | get_line_length(mc68x328fb_default.xres_virtual, mc68x328fb_default.bits_per_pixel); |
465 | fb_info.fix.visual = (mc68x328fb_default.bits_per_pixel) == 1 ? |
466 | MC68X328FB_MONO_VISUAL : FB_VISUAL_PSEUDOCOLOR; |
467 | if (fb_info.var.bits_per_pixel == 1) { |
468 | fb_info.var.red.length = fb_info.var.green.length = fb_info.var.blue.length = 1; |
469 | fb_info.var.red.offset = fb_info.var.green.offset = fb_info.var.blue.offset = 0; |
470 | } |
471 | fb_info.pseudo_palette = &mc68x328fb_pseudo_palette; |
472 | fb_info.flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN; |
473 | |
474 | if (fb_alloc_cmap(&fb_info.cmap, 256, 0)) |
475 | return -ENOMEM; |
476 | |
477 | if (register_framebuffer(&fb_info) < 0) { |
478 | fb_dealloc_cmap(&fb_info.cmap); |
479 | return -EINVAL; |
480 | } |
481 | |
482 | printk(KERN_INFO |
483 | "fb%d: %s frame buffer device\n", fb_info.node, fb_info.fix.id); |
484 | printk(KERN_INFO |
485 | "fb%d: %dx%dx%d at 0x%08lx\n", fb_info.node, |
486 | mc68x328fb_default.xres_virtual, mc68x328fb_default.yres_virtual, |
487 | 1 << mc68x328fb_default.bits_per_pixel, videomemory); |
488 | |
489 | return 0; |
490 | } |
491 | |
492 | module_init(mc68x328fb_init); |
493 | |
494 | #ifdef MODULE |
495 | |
496 | static void __exit mc68x328fb_cleanup(void) |
497 | { |
498 | unregister_framebuffer(&fb_info); |
499 | fb_dealloc_cmap(&fb_info.cmap); |
500 | } |
501 | |
502 | module_exit(mc68x328fb_cleanup); |
503 | |
504 | MODULE_LICENSE("GPL"); |
505 | #endif /* MODULE */ |
506 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9