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