Now that the fbops member of struct fb_info is const, we can start making the ops const as well. This does not cover all drivers; some actually modify the fbops struct, for example to adjust for different configurations, and others do more involved things that I'd rather not touch in practically obsolete drivers. Mostly this is the low hanging fruit where we can add "const" and be done with it. v3: - un-constify atyfb, mb862xx, nvidia and uvesabf (0day) v2: - fix typo (Christophe de Dinechin) - use "static const" instead of "const static" in mx3fb.c - also constify smscufx.c Cc: linux-fbdev@vger.kernel.org Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch> Signed-off-by: Jani Nikula <jani.nikula@intel.com> Link: https://patchwork.freedesktop.org/patch/msgid/ce67f14435f3af498f2e8bf35ce4be11f7504132.1575390740.git.jani.nikula@intel.com
498 lines
13 KiB
C
498 lines
13 KiB
C
/*
|
|
* linux/drivers/video/68328fb.c -- Low level implementation of the
|
|
* mc68x328 LCD frame buffer device
|
|
*
|
|
* Copyright (C) 2003 Georges Menie
|
|
*
|
|
* This driver assumes an already configured controller (e.g. from config.c)
|
|
* Keep the code clean of board specific initialization.
|
|
*
|
|
* This code has not been tested with colors, colormap management functions
|
|
* are minimal (no colormap data written to the 68328 registers...)
|
|
*
|
|
* initial version of this driver:
|
|
* Copyright (C) 1998,1999 Kenneth Albanowski <kjahds@kjahds.com>,
|
|
* The Silver Hammer Group, Ltd.
|
|
*
|
|
* this version is based on :
|
|
*
|
|
* linux/drivers/video/vfb.c -- Virtual frame buffer device
|
|
*
|
|
* Copyright (C) 2002 James Simmons
|
|
*
|
|
* Copyright (C) 1997 Geert Uytterhoeven
|
|
*
|
|
* This file is subject to the terms and conditions of the GNU General Public
|
|
* License. See the file COPYING in the main directory of this archive for
|
|
* more details.
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/string.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/fb.h>
|
|
#include <linux/init.h>
|
|
|
|
#if defined(CONFIG_M68VZ328)
|
|
#include <asm/MC68VZ328.h>
|
|
#elif defined(CONFIG_M68EZ328)
|
|
#include <asm/MC68EZ328.h>
|
|
#elif defined(CONFIG_M68328)
|
|
#include <asm/MC68328.h>
|
|
#else
|
|
#error wrong architecture for the MC68x328 frame buffer device
|
|
#endif
|
|
|
|
static u_long videomemory;
|
|
static u_long videomemorysize;
|
|
|
|
static struct fb_info fb_info;
|
|
static u32 mc68x328fb_pseudo_palette[16];
|
|
|
|
static struct fb_var_screeninfo mc68x328fb_default __initdata = {
|
|
.red = { 0, 8, 0 },
|
|
.green = { 0, 8, 0 },
|
|
.blue = { 0, 8, 0 },
|
|
.activate = FB_ACTIVATE_TEST,
|
|
.height = -1,
|
|
.width = -1,
|
|
.pixclock = 20000,
|
|
.left_margin = 64,
|
|
.right_margin = 64,
|
|
.upper_margin = 32,
|
|
.lower_margin = 32,
|
|
.hsync_len = 64,
|
|
.vsync_len = 2,
|
|
.vmode = FB_VMODE_NONINTERLACED,
|
|
};
|
|
|
|
static const struct fb_fix_screeninfo mc68x328fb_fix __initconst = {
|
|
.id = "68328fb",
|
|
.type = FB_TYPE_PACKED_PIXELS,
|
|
.xpanstep = 1,
|
|
.ypanstep = 1,
|
|
.ywrapstep = 1,
|
|
.accel = FB_ACCEL_NONE,
|
|
};
|
|
|
|
/*
|
|
* Interface used by the world
|
|
*/
|
|
int mc68x328fb_init(void);
|
|
int mc68x328fb_setup(char *);
|
|
|
|
static int mc68x328fb_check_var(struct fb_var_screeninfo *var,
|
|
struct fb_info *info);
|
|
static int mc68x328fb_set_par(struct fb_info *info);
|
|
static int mc68x328fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
|
|
u_int transp, struct fb_info *info);
|
|
static int mc68x328fb_pan_display(struct fb_var_screeninfo *var,
|
|
struct fb_info *info);
|
|
static int mc68x328fb_mmap(struct fb_info *info, struct vm_area_struct *vma);
|
|
|
|
static const struct fb_ops mc68x328fb_ops = {
|
|
.fb_check_var = mc68x328fb_check_var,
|
|
.fb_set_par = mc68x328fb_set_par,
|
|
.fb_setcolreg = mc68x328fb_setcolreg,
|
|
.fb_pan_display = mc68x328fb_pan_display,
|
|
.fb_fillrect = cfb_fillrect,
|
|
.fb_copyarea = cfb_copyarea,
|
|
.fb_imageblit = cfb_imageblit,
|
|
.fb_mmap = mc68x328fb_mmap,
|
|
};
|
|
|
|
/*
|
|
* Internal routines
|
|
*/
|
|
|
|
static u_long get_line_length(int xres_virtual, int bpp)
|
|
{
|
|
u_long length;
|
|
|
|
length = xres_virtual * bpp;
|
|
length = (length + 31) & ~31;
|
|
length >>= 3;
|
|
return (length);
|
|
}
|
|
|
|
/*
|
|
* Setting the video mode has been split into two parts.
|
|
* First part, xxxfb_check_var, must not write anything
|
|
* to hardware, it should only verify and adjust var.
|
|
* This means it doesn't alter par but it does use hardware
|
|
* data from it to check this var.
|
|
*/
|
|
|
|
static int mc68x328fb_check_var(struct fb_var_screeninfo *var,
|
|
struct fb_info *info)
|
|
{
|
|
u_long line_length;
|
|
|
|
/*
|
|
* FB_VMODE_CONUPDATE and FB_VMODE_SMOOTH_XPAN are equal!
|
|
* as FB_VMODE_SMOOTH_XPAN is only used internally
|
|
*/
|
|
|
|
if (var->vmode & FB_VMODE_CONUPDATE) {
|
|
var->vmode |= FB_VMODE_YWRAP;
|
|
var->xoffset = info->var.xoffset;
|
|
var->yoffset = info->var.yoffset;
|
|
}
|
|
|
|
/*
|
|
* Some very basic checks
|
|
*/
|
|
if (!var->xres)
|
|
var->xres = 1;
|
|
if (!var->yres)
|
|
var->yres = 1;
|
|
if (var->xres > var->xres_virtual)
|
|
var->xres_virtual = var->xres;
|
|
if (var->yres > var->yres_virtual)
|
|
var->yres_virtual = var->yres;
|
|
if (var->bits_per_pixel <= 1)
|
|
var->bits_per_pixel = 1;
|
|
else if (var->bits_per_pixel <= 8)
|
|
var->bits_per_pixel = 8;
|
|
else if (var->bits_per_pixel <= 16)
|
|
var->bits_per_pixel = 16;
|
|
else if (var->bits_per_pixel <= 24)
|
|
var->bits_per_pixel = 24;
|
|
else if (var->bits_per_pixel <= 32)
|
|
var->bits_per_pixel = 32;
|
|
else
|
|
return -EINVAL;
|
|
|
|
if (var->xres_virtual < var->xoffset + var->xres)
|
|
var->xres_virtual = var->xoffset + var->xres;
|
|
if (var->yres_virtual < var->yoffset + var->yres)
|
|
var->yres_virtual = var->yoffset + var->yres;
|
|
|
|
/*
|
|
* Memory limit
|
|
*/
|
|
line_length =
|
|
get_line_length(var->xres_virtual, var->bits_per_pixel);
|
|
if (line_length * var->yres_virtual > videomemorysize)
|
|
return -ENOMEM;
|
|
|
|
/*
|
|
* Now that we checked it we alter var. The reason being is that the video
|
|
* mode passed in might not work but slight changes to it might make it
|
|
* work. This way we let the user know what is acceptable.
|
|
*/
|
|
switch (var->bits_per_pixel) {
|
|
case 1:
|
|
var->red.offset = 0;
|
|
var->red.length = 1;
|
|
var->green.offset = 0;
|
|
var->green.length = 1;
|
|
var->blue.offset = 0;
|
|
var->blue.length = 1;
|
|
var->transp.offset = 0;
|
|
var->transp.length = 0;
|
|
break;
|
|
case 8:
|
|
var->red.offset = 0;
|
|
var->red.length = 8;
|
|
var->green.offset = 0;
|
|
var->green.length = 8;
|
|
var->blue.offset = 0;
|
|
var->blue.length = 8;
|
|
var->transp.offset = 0;
|
|
var->transp.length = 0;
|
|
break;
|
|
case 16: /* RGBA 5551 */
|
|
if (var->transp.length) {
|
|
var->red.offset = 0;
|
|
var->red.length = 5;
|
|
var->green.offset = 5;
|
|
var->green.length = 5;
|
|
var->blue.offset = 10;
|
|
var->blue.length = 5;
|
|
var->transp.offset = 15;
|
|
var->transp.length = 1;
|
|
} else { /* RGB 565 */
|
|
var->red.offset = 0;
|
|
var->red.length = 5;
|
|
var->green.offset = 5;
|
|
var->green.length = 6;
|
|
var->blue.offset = 11;
|
|
var->blue.length = 5;
|
|
var->transp.offset = 0;
|
|
var->transp.length = 0;
|
|
}
|
|
break;
|
|
case 24: /* RGB 888 */
|
|
var->red.offset = 0;
|
|
var->red.length = 8;
|
|
var->green.offset = 8;
|
|
var->green.length = 8;
|
|
var->blue.offset = 16;
|
|
var->blue.length = 8;
|
|
var->transp.offset = 0;
|
|
var->transp.length = 0;
|
|
break;
|
|
case 32: /* RGBA 8888 */
|
|
var->red.offset = 0;
|
|
var->red.length = 8;
|
|
var->green.offset = 8;
|
|
var->green.length = 8;
|
|
var->blue.offset = 16;
|
|
var->blue.length = 8;
|
|
var->transp.offset = 24;
|
|
var->transp.length = 8;
|
|
break;
|
|
}
|
|
var->red.msb_right = 0;
|
|
var->green.msb_right = 0;
|
|
var->blue.msb_right = 0;
|
|
var->transp.msb_right = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* This routine actually sets the video mode. It's in here where we
|
|
* the hardware state info->par and fix which can be affected by the
|
|
* change in par. For this driver it doesn't do much.
|
|
*/
|
|
static int mc68x328fb_set_par(struct fb_info *info)
|
|
{
|
|
info->fix.line_length = get_line_length(info->var.xres_virtual,
|
|
info->var.bits_per_pixel);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Set a single color register. The values supplied are already
|
|
* rounded down to the hardware's capabilities (according to the
|
|
* entries in the var structure). Return != 0 for invalid regno.
|
|
*/
|
|
|
|
static int mc68x328fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
|
|
u_int transp, struct fb_info *info)
|
|
{
|
|
if (regno >= 256) /* no. of hw registers */
|
|
return 1;
|
|
/*
|
|
* Program hardware... do anything you want with transp
|
|
*/
|
|
|
|
/* grayscale works only partially under directcolor */
|
|
if (info->var.grayscale) {
|
|
/* grayscale = 0.30*R + 0.59*G + 0.11*B */
|
|
red = green = blue =
|
|
(red * 77 + green * 151 + blue * 28) >> 8;
|
|
}
|
|
|
|
/* Directcolor:
|
|
* var->{color}.offset contains start of bitfield
|
|
* var->{color}.length contains length of bitfield
|
|
* {hardwarespecific} contains width of RAMDAC
|
|
* cmap[X] is programmed to (X << red.offset) | (X << green.offset) | (X << blue.offset)
|
|
* RAMDAC[X] is programmed to (red, green, blue)
|
|
*
|
|
* Pseudocolor:
|
|
* uses offset = 0 && length = RAMDAC register width.
|
|
* var->{color}.offset is 0
|
|
* var->{color}.length contains width of DAC
|
|
* cmap is not used
|
|
* RAMDAC[X] is programmed to (red, green, blue)
|
|
* Truecolor:
|
|
* does not use DAC. Usually 3 are present.
|
|
* var->{color}.offset contains start of bitfield
|
|
* var->{color}.length contains length of bitfield
|
|
* cmap is programmed to (red << red.offset) | (green << green.offset) |
|
|
* (blue << blue.offset) | (transp << transp.offset)
|
|
* RAMDAC does not exist
|
|
*/
|
|
#define CNVT_TOHW(val,width) ((((val)<<(width))+0x7FFF-(val))>>16)
|
|
switch (info->fix.visual) {
|
|
case FB_VISUAL_TRUECOLOR:
|
|
case FB_VISUAL_PSEUDOCOLOR:
|
|
red = CNVT_TOHW(red, info->var.red.length);
|
|
green = CNVT_TOHW(green, info->var.green.length);
|
|
blue = CNVT_TOHW(blue, info->var.blue.length);
|
|
transp = CNVT_TOHW(transp, info->var.transp.length);
|
|
break;
|
|
case FB_VISUAL_DIRECTCOLOR:
|
|
red = CNVT_TOHW(red, 8); /* expect 8 bit DAC */
|
|
green = CNVT_TOHW(green, 8);
|
|
blue = CNVT_TOHW(blue, 8);
|
|
/* hey, there is bug in transp handling... */
|
|
transp = CNVT_TOHW(transp, 8);
|
|
break;
|
|
}
|
|
#undef CNVT_TOHW
|
|
/* Truecolor has hardware independent palette */
|
|
if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
|
|
u32 v;
|
|
|
|
if (regno >= 16)
|
|
return 1;
|
|
|
|
v = (red << info->var.red.offset) |
|
|
(green << info->var.green.offset) |
|
|
(blue << info->var.blue.offset) |
|
|
(transp << info->var.transp.offset);
|
|
switch (info->var.bits_per_pixel) {
|
|
case 8:
|
|
break;
|
|
case 16:
|
|
((u32 *) (info->pseudo_palette))[regno] = v;
|
|
break;
|
|
case 24:
|
|
case 32:
|
|
((u32 *) (info->pseudo_palette))[regno] = v;
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Pan or Wrap the Display
|
|
*
|
|
* This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
|
|
*/
|
|
|
|
static int mc68x328fb_pan_display(struct fb_var_screeninfo *var,
|
|
struct fb_info *info)
|
|
{
|
|
if (var->vmode & FB_VMODE_YWRAP) {
|
|
if (var->yoffset < 0
|
|
|| var->yoffset >= info->var.yres_virtual
|
|
|| var->xoffset)
|
|
return -EINVAL;
|
|
} else {
|
|
if (var->xoffset + info->var.xres > info->var.xres_virtual ||
|
|
var->yoffset + info->var.yres > info->var.yres_virtual)
|
|
return -EINVAL;
|
|
}
|
|
info->var.xoffset = var->xoffset;
|
|
info->var.yoffset = var->yoffset;
|
|
if (var->vmode & FB_VMODE_YWRAP)
|
|
info->var.vmode |= FB_VMODE_YWRAP;
|
|
else
|
|
info->var.vmode &= ~FB_VMODE_YWRAP;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Most drivers don't need their own mmap function
|
|
*/
|
|
|
|
static int mc68x328fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
|
|
{
|
|
#ifndef MMU
|
|
/* this is uClinux (no MMU) specific code */
|
|
|
|
vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
|
|
vma->vm_start = videomemory;
|
|
|
|
return 0;
|
|
#else
|
|
return -EINVAL;
|
|
#endif
|
|
}
|
|
|
|
int __init mc68x328fb_setup(char *options)
|
|
{
|
|
#if 0
|
|
char *this_opt;
|
|
#endif
|
|
|
|
if (!options || !*options)
|
|
return 1;
|
|
#if 0
|
|
while ((this_opt = strsep(&options, ",")) != NULL) {
|
|
if (!*this_opt)
|
|
continue;
|
|
if (!strncmp(this_opt, "disable", 7))
|
|
mc68x328fb_enable = 0;
|
|
}
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Initialisation
|
|
*/
|
|
|
|
int __init mc68x328fb_init(void)
|
|
{
|
|
#ifndef MODULE
|
|
char *option = NULL;
|
|
|
|
if (fb_get_options("68328fb", &option))
|
|
return -ENODEV;
|
|
mc68x328fb_setup(option);
|
|
#endif
|
|
/*
|
|
* initialize the default mode from the LCD controller registers
|
|
*/
|
|
mc68x328fb_default.xres = LXMAX;
|
|
mc68x328fb_default.yres = LYMAX+1;
|
|
mc68x328fb_default.xres_virtual = mc68x328fb_default.xres;
|
|
mc68x328fb_default.yres_virtual = mc68x328fb_default.yres;
|
|
mc68x328fb_default.bits_per_pixel = 1 + (LPICF & 0x01);
|
|
videomemory = LSSA;
|
|
videomemorysize = (mc68x328fb_default.xres_virtual+7) / 8 *
|
|
mc68x328fb_default.yres_virtual * mc68x328fb_default.bits_per_pixel;
|
|
|
|
fb_info.screen_base = (void *)videomemory;
|
|
fb_info.fbops = &mc68x328fb_ops;
|
|
fb_info.var = mc68x328fb_default;
|
|
fb_info.fix = mc68x328fb_fix;
|
|
fb_info.fix.smem_start = videomemory;
|
|
fb_info.fix.smem_len = videomemorysize;
|
|
fb_info.fix.line_length =
|
|
get_line_length(mc68x328fb_default.xres_virtual, mc68x328fb_default.bits_per_pixel);
|
|
fb_info.fix.visual = (mc68x328fb_default.bits_per_pixel) == 1 ?
|
|
FB_VISUAL_MONO10 : FB_VISUAL_PSEUDOCOLOR;
|
|
if (fb_info.var.bits_per_pixel == 1) {
|
|
fb_info.var.red.length = fb_info.var.green.length = fb_info.var.blue.length = 1;
|
|
fb_info.var.red.offset = fb_info.var.green.offset = fb_info.var.blue.offset = 0;
|
|
}
|
|
fb_info.pseudo_palette = &mc68x328fb_pseudo_palette;
|
|
fb_info.flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN;
|
|
|
|
if (fb_alloc_cmap(&fb_info.cmap, 256, 0))
|
|
return -ENOMEM;
|
|
|
|
if (register_framebuffer(&fb_info) < 0) {
|
|
fb_dealloc_cmap(&fb_info.cmap);
|
|
return -EINVAL;
|
|
}
|
|
|
|
fb_info(&fb_info, "%s frame buffer device\n", fb_info.fix.id);
|
|
fb_info(&fb_info, "%dx%dx%d at 0x%08lx\n",
|
|
mc68x328fb_default.xres_virtual,
|
|
mc68x328fb_default.yres_virtual,
|
|
1 << mc68x328fb_default.bits_per_pixel, videomemory);
|
|
|
|
return 0;
|
|
}
|
|
|
|
module_init(mc68x328fb_init);
|
|
|
|
#ifdef MODULE
|
|
|
|
static void __exit mc68x328fb_cleanup(void)
|
|
{
|
|
unregister_framebuffer(&fb_info);
|
|
fb_dealloc_cmap(&fb_info.cmap);
|
|
}
|
|
|
|
module_exit(mc68x328fb_cleanup);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
#endif /* MODULE */
|