483 lines · c
1/*2 * linux/drivers/video/68328fb.c -- Low level implementation of the3 * mc68x328 LCD frame buffer device4 *5 * Copyright (C) 2003 Georges Menie6 *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 functions11 * 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 device20 *21 * Copyright (C) 2002 James Simmons22 *23 * Copyright (C) 1997 Geert Uytterhoeven24 *25 * This file is subject to the terms and conditions of the GNU General Public26 * License. See the file COPYING in the main directory of this archive for27 * 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 <linux/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#else49#error wrong architecture for the MC68x328 frame buffer device50#endif51 52static u_long videomemory;53static u_long videomemorysize;54 55static struct fb_info fb_info;56static u32 mc68x328fb_pseudo_palette[16];57 58static struct fb_var_screeninfo mc68x328fb_default __initdata = {59 .red = { 0, 8, 0 },60 .green = { 0, 8, 0 },61 .blue = { 0, 8, 0 },62 .activate = FB_ACTIVATE_TEST,63 .height = -1,64 .width = -1,65 .pixclock = 20000,66 .left_margin = 64,67 .right_margin = 64,68 .upper_margin = 32,69 .lower_margin = 32,70 .hsync_len = 64,71 .vsync_len = 2,72 .vmode = FB_VMODE_NONINTERLACED,73};74 75static const struct fb_fix_screeninfo mc68x328fb_fix __initconst = {76 .id = "68328fb",77 .type = FB_TYPE_PACKED_PIXELS,78 .xpanstep = 1,79 .ypanstep = 1,80 .ywrapstep = 1,81 .accel = FB_ACCEL_NONE,82};83 84 /*85 * Interface used by the world86 */87static int mc68x328fb_check_var(struct fb_var_screeninfo *var,88 struct fb_info *info);89static int mc68x328fb_set_par(struct fb_info *info);90static int mc68x328fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,91 u_int transp, struct fb_info *info);92static int mc68x328fb_pan_display(struct fb_var_screeninfo *var,93 struct fb_info *info);94static int mc68x328fb_mmap(struct fb_info *info, struct vm_area_struct *vma);95 96static const struct fb_ops mc68x328fb_ops = {97 .owner = THIS_MODULE,98 __FB_DEFAULT_IOMEM_OPS_RDWR,99 .fb_check_var = mc68x328fb_check_var,100 .fb_set_par = mc68x328fb_set_par,101 .fb_setcolreg = mc68x328fb_setcolreg,102 .fb_pan_display = mc68x328fb_pan_display,103 __FB_DEFAULT_IOMEM_OPS_DRAW,104 .fb_mmap = mc68x328fb_mmap,105};106 107 /*108 * Internal routines109 */110 111static u_long get_line_length(int xres_virtual, int bpp)112{113 u_long length;114 115 length = xres_virtual * bpp;116 length = (length + 31) & ~31;117 length >>= 3;118 return (length);119}120 121 /*122 * Setting the video mode has been split into two parts.123 * First part, xxxfb_check_var, must not write anything124 * to hardware, it should only verify and adjust var.125 * This means it doesn't alter par but it does use hardware126 * data from it to check this var.127 */128 129static int mc68x328fb_check_var(struct fb_var_screeninfo *var,130 struct fb_info *info)131{132 u_long line_length;133 134 /*135 * FB_VMODE_CONUPDATE and FB_VMODE_SMOOTH_XPAN are equal!136 * as FB_VMODE_SMOOTH_XPAN is only used internally137 */138 139 if (var->vmode & FB_VMODE_CONUPDATE) {140 var->vmode |= FB_VMODE_YWRAP;141 var->xoffset = info->var.xoffset;142 var->yoffset = info->var.yoffset;143 }144 145 /*146 * Some very basic checks147 */148 if (!var->xres)149 var->xres = 1;150 if (!var->yres)151 var->yres = 1;152 if (var->xres > var->xres_virtual)153 var->xres_virtual = var->xres;154 if (var->yres > var->yres_virtual)155 var->yres_virtual = var->yres;156 if (var->bits_per_pixel <= 1)157 var->bits_per_pixel = 1;158 else if (var->bits_per_pixel <= 8)159 var->bits_per_pixel = 8;160 else if (var->bits_per_pixel <= 16)161 var->bits_per_pixel = 16;162 else if (var->bits_per_pixel <= 24)163 var->bits_per_pixel = 24;164 else if (var->bits_per_pixel <= 32)165 var->bits_per_pixel = 32;166 else167 return -EINVAL;168 169 if (var->xres_virtual < var->xoffset + var->xres)170 var->xres_virtual = var->xoffset + var->xres;171 if (var->yres_virtual < var->yoffset + var->yres)172 var->yres_virtual = var->yoffset + var->yres;173 174 /*175 * Memory limit176 */177 line_length =178 get_line_length(var->xres_virtual, var->bits_per_pixel);179 if (line_length * var->yres_virtual > videomemorysize)180 return -ENOMEM;181 182 /*183 * Now that we checked it we alter var. The reason being is that the video184 * mode passed in might not work but slight changes to it might make it185 * work. This way we let the user know what is acceptable.186 */187 switch (var->bits_per_pixel) {188 case 1:189 var->red.offset = 0;190 var->red.length = 1;191 var->green.offset = 0;192 var->green.length = 1;193 var->blue.offset = 0;194 var->blue.length = 1;195 var->transp.offset = 0;196 var->transp.length = 0;197 break;198 case 8:199 var->red.offset = 0;200 var->red.length = 8;201 var->green.offset = 0;202 var->green.length = 8;203 var->blue.offset = 0;204 var->blue.length = 8;205 var->transp.offset = 0;206 var->transp.length = 0;207 break;208 case 16: /* RGBA 5551 */209 if (var->transp.length) {210 var->red.offset = 0;211 var->red.length = 5;212 var->green.offset = 5;213 var->green.length = 5;214 var->blue.offset = 10;215 var->blue.length = 5;216 var->transp.offset = 15;217 var->transp.length = 1;218 } else { /* RGB 565 */219 var->red.offset = 0;220 var->red.length = 5;221 var->green.offset = 5;222 var->green.length = 6;223 var->blue.offset = 11;224 var->blue.length = 5;225 var->transp.offset = 0;226 var->transp.length = 0;227 }228 break;229 case 24: /* RGB 888 */230 var->red.offset = 0;231 var->red.length = 8;232 var->green.offset = 8;233 var->green.length = 8;234 var->blue.offset = 16;235 var->blue.length = 8;236 var->transp.offset = 0;237 var->transp.length = 0;238 break;239 case 32: /* RGBA 8888 */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 = 24;247 var->transp.length = 8;248 break;249 }250 var->red.msb_right = 0;251 var->green.msb_right = 0;252 var->blue.msb_right = 0;253 var->transp.msb_right = 0;254 255 return 0;256}257 258/* This routine actually sets the video mode. It's in here where we259 * the hardware state info->par and fix which can be affected by the260 * change in par. For this driver it doesn't do much.261 */262static int mc68x328fb_set_par(struct fb_info *info)263{264 info->fix.line_length = get_line_length(info->var.xres_virtual,265 info->var.bits_per_pixel);266 return 0;267}268 269 /*270 * Set a single color register. The values supplied are already271 * rounded down to the hardware's capabilities (according to the272 * entries in the var structure). Return != 0 for invalid regno.273 */274 275static int mc68x328fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,276 u_int transp, struct fb_info *info)277{278 if (regno >= 256) /* no. of hw registers */279 return 1;280 /*281 * Program hardware... do anything you want with transp282 */283 284 /* grayscale works only partially under directcolor */285 if (info->var.grayscale) {286 /* grayscale = 0.30*R + 0.59*G + 0.11*B */287 red = green = blue =288 (red * 77 + green * 151 + blue * 28) >> 8;289 }290 291 /* Directcolor:292 * var->{color}.offset contains start of bitfield293 * var->{color}.length contains length of bitfield294 * {hardwarespecific} contains width of RAMDAC295 * cmap[X] is programmed to (X << red.offset) | (X << green.offset) | (X << blue.offset)296 * RAMDAC[X] is programmed to (red, green, blue)297 *298 * Pseudocolor:299 * uses offset = 0 && length = RAMDAC register width.300 * var->{color}.offset is 0301 * var->{color}.length contains width of DAC302 * cmap is not used303 * RAMDAC[X] is programmed to (red, green, blue)304 * Truecolor:305 * does not use DAC. Usually 3 are present.306 * var->{color}.offset contains start of bitfield307 * var->{color}.length contains length of bitfield308 * cmap is programmed to (red << red.offset) | (green << green.offset) |309 * (blue << blue.offset) | (transp << transp.offset)310 * RAMDAC does not exist311 */312#define CNVT_TOHW(val,width) ((((val)<<(width))+0x7FFF-(val))>>16)313 switch (info->fix.visual) {314 case FB_VISUAL_TRUECOLOR:315 case FB_VISUAL_PSEUDOCOLOR:316 red = CNVT_TOHW(red, info->var.red.length);317 green = CNVT_TOHW(green, info->var.green.length);318 blue = CNVT_TOHW(blue, info->var.blue.length);319 transp = CNVT_TOHW(transp, info->var.transp.length);320 break;321 case FB_VISUAL_DIRECTCOLOR:322 red = CNVT_TOHW(red, 8); /* expect 8 bit DAC */323 green = CNVT_TOHW(green, 8);324 blue = CNVT_TOHW(blue, 8);325 /* hey, there is bug in transp handling... */326 transp = CNVT_TOHW(transp, 8);327 break;328 }329#undef CNVT_TOHW330 /* Truecolor has hardware independent palette */331 if (info->fix.visual == FB_VISUAL_TRUECOLOR) {332 u32 v;333 334 if (regno >= 16)335 return 1;336 337 v = (red << info->var.red.offset) |338 (green << info->var.green.offset) |339 (blue << info->var.blue.offset) |340 (transp << info->var.transp.offset);341 switch (info->var.bits_per_pixel) {342 case 8:343 break;344 case 16:345 ((u32 *) (info->pseudo_palette))[regno] = v;346 break;347 case 24:348 case 32:349 ((u32 *) (info->pseudo_palette))[regno] = v;350 break;351 }352 return 0;353 }354 return 0;355}356 357 /*358 * Pan or Wrap the Display359 *360 * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag361 */362 363static int mc68x328fb_pan_display(struct fb_var_screeninfo *var,364 struct fb_info *info)365{366 if (var->vmode & FB_VMODE_YWRAP) {367 if (var->yoffset < 0368 || var->yoffset >= info->var.yres_virtual369 || var->xoffset)370 return -EINVAL;371 } else {372 if (var->xoffset + info->var.xres > info->var.xres_virtual ||373 var->yoffset + info->var.yres > info->var.yres_virtual)374 return -EINVAL;375 }376 info->var.xoffset = var->xoffset;377 info->var.yoffset = var->yoffset;378 if (var->vmode & FB_VMODE_YWRAP)379 info->var.vmode |= FB_VMODE_YWRAP;380 else381 info->var.vmode &= ~FB_VMODE_YWRAP;382 return 0;383}384 385 /*386 * Most drivers don't need their own mmap function387 */388 389static int mc68x328fb_mmap(struct fb_info *info, struct vm_area_struct *vma)390{391#ifndef MMU392 /* this is uClinux (no MMU) specific code */393 394 vm_flags_set(vma, VM_DONTEXPAND | VM_DONTDUMP);395 vma->vm_start = videomemory;396 397 return 0;398#else399 return -EINVAL;400#endif401}402 403static int __init mc68x328fb_setup(char *options)404{405 if (!options || !*options)406 return 1;407 return 1;408}409 410 /*411 * Initialisation412 */413 414static int __init mc68x328fb_init(void)415{416#ifndef MODULE417 char *option = NULL;418 419 if (fb_get_options("68328fb", &option))420 return -ENODEV;421 mc68x328fb_setup(option);422#endif423 /*424 * initialize the default mode from the LCD controller registers425 */426 mc68x328fb_default.xres = LXMAX;427 mc68x328fb_default.yres = LYMAX+1;428 mc68x328fb_default.xres_virtual = mc68x328fb_default.xres;429 mc68x328fb_default.yres_virtual = mc68x328fb_default.yres;430 mc68x328fb_default.bits_per_pixel = 1 + (LPICF & 0x01);431 videomemory = LSSA;432 videomemorysize = (mc68x328fb_default.xres_virtual+7) / 8 *433 mc68x328fb_default.yres_virtual * mc68x328fb_default.bits_per_pixel;434 435 fb_info.screen_base = (void *)videomemory;436 fb_info.fbops = &mc68x328fb_ops;437 fb_info.var = mc68x328fb_default;438 fb_info.fix = mc68x328fb_fix;439 fb_info.fix.smem_start = videomemory;440 fb_info.fix.smem_len = videomemorysize;441 fb_info.fix.line_length =442 get_line_length(mc68x328fb_default.xres_virtual, mc68x328fb_default.bits_per_pixel);443 fb_info.fix.visual = (mc68x328fb_default.bits_per_pixel) == 1 ?444 FB_VISUAL_MONO10 : FB_VISUAL_PSEUDOCOLOR;445 if (fb_info.var.bits_per_pixel == 1) {446 fb_info.var.red.length = fb_info.var.green.length = fb_info.var.blue.length = 1;447 fb_info.var.red.offset = fb_info.var.green.offset = fb_info.var.blue.offset = 0;448 }449 fb_info.pseudo_palette = &mc68x328fb_pseudo_palette;450 fb_info.flags = FBINFO_HWACCEL_YPAN;451 452 if (fb_alloc_cmap(&fb_info.cmap, 256, 0))453 return -ENOMEM;454 455 if (register_framebuffer(&fb_info) < 0) {456 fb_dealloc_cmap(&fb_info.cmap);457 return -EINVAL;458 }459 460 fb_info(&fb_info, "%s frame buffer device\n", fb_info.fix.id);461 fb_info(&fb_info, "%dx%dx%d at 0x%08lx\n",462 mc68x328fb_default.xres_virtual,463 mc68x328fb_default.yres_virtual,464 1 << mc68x328fb_default.bits_per_pixel, videomemory);465 466 return 0;467}468 469module_init(mc68x328fb_init);470 471#ifdef MODULE472 473static void __exit mc68x328fb_cleanup(void)474{475 unregister_framebuffer(&fb_info);476 fb_dealloc_cmap(&fb_info.cmap);477}478 479module_exit(mc68x328fb_cleanup);480 481MODULE_LICENSE("GPL");482#endif /* MODULE */483