#include <linux/fb.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
/* Address map of LCD controller registers */
#define LCD_CONTROLLER_BASE 0x01000D00
#define SIZE_REG (*(volatile u32*)(LCD_CONTROLLER_BASE))
#define HSYNC_REG (*(volatile u32*)(LCD_CONTROLLER_BASE + 4))
#define VSYNC_REG (*(volatile u32*)(LCD_CONTROLLER_BASE + 8))
#define CONF_REG (*(volatile u32*)(LCD_CONTROLLER_BASE + 12))
#define CTRL_REG (*(volatile u32*)(LCD_CONTROLLER_BASE + 16))
#define DMA_REG (*(volatile u32*)(LCD_CONTROLLER_BASE + 20))
#define STATUS_REG (*(volatile u32 *)(LCD_CONTROLLER_BASE + 24))
#define CONTRAST_REG (*(volatile u32 *)(LCD_CONTROLLER_BASE + 28))
#define LCD_CONTROLLER_SIZE 32
/* Resources for the LCD controller platform device */
static struct resource myfb_resources[] = {
[0] = {
.start = LCD_CONTROLLER_BASE,
.end = LCD_CONTROLLER_SIZE,
.flags = IORESOURCE_MEM,
},
};
/* Platform device definition */
static struct platform_device myfb_device = {
.name = "myfb",
.id = 0,
.dev = {
.coherent_dma_mask = 0xffffffff,
},
.num_resources = ARRAY_SIZE(myfb_resources),
.resource = myfb_resources,
};
/* Set LCD controller parameters */
static int
myfb_set_par(struct fb_info *info)
{
unsigned long adjusted_fb_start;
struct fb_var_screeninfo *var = &info->var;
struct fb_fix_screeninfo *fix = &info->fix;
/* Top 16 bits of HSYNC_REG hold HSYNC duration, next 8 contain
the left margin, while the bottom 8 house the right margin */
HSYNC_REG = (var->hsync_len << 16) |
(var->left_margin << 8)|
(var->right_margin);
/* Top 16 bits of VSYNC_REG hold VSYNC duration, next 8 contain
the upper margin, while the bottom 8 house the lower margin */
VSYNC_REG = (var->vsync_len << 16) |
(var->upper_margin << 8)|
(var->lower_margin);
/* Top 16 bits of SIZE_REG hold xres, bottom 16 hold yres */
SIZE_REG = (var->xres << 16) | (var->yres);
/* Set bits per pixel, pixel polarity, clock dividers for
the pixclock, and color/monochrome mode in CONF_REG */
/* ... */
/* Fill DMA_REG with the start address of the frame buffer
coherently allocated from myfb_probe(). Adjust this address
to account for any offset to the start of screen area */
adjusted_fb_start = fix->smem_start +
(var->yoffset * var->xres_virtual + var->xoffset) *
(var->bits_per_pixel) / 8;
__raw_writel(adjusted_fb_start, (unsigned long *)DMA_REG);
/* Set the DMA burst length and watermark sizes in DMA_REG */
/* ... */
/* Set fixed information */
fix->accel = FB_ACCEL_NONE; /* No hardware acceleration */
fix->visual = FB_VISUAL_TRUECOLOR; /* True color mode */
fix->line_length = var->xres_virtual * var->bits_per_pixel/8;
return 0;
}
/* Enable LCD controller */
static void
myfb_enable_controller(struct fb_info *info)
{
/* Enable LCD controller, start DMA, enable clocks and power
by writing to CTRL_REG */
/* ... */
}
/* Disable LCD controller */
static void
myfb_disable_controller(struct fb_info *info)
{
/* Disable LCD controller, stop DMA, disable clocks and power
by writing to CTRL_REG */
/* ... */
}
/* Sanity check and adjustment of variables */
static int
myfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
/* Round up to the minimum resolution supported by
the LCD controller */
if (var->xres < 64) var->xres = 64;
if (var->yres < 64) var->yres = 64;
/* ... */
/* This hardware supports the RGB565 color format.
See the section "Color Modes" for more details */
if (var->bits_per_pixel == 16) {
/* Encoding Red */
var->red.length = 5;
var->red.offset = 11;
/* Encoding Green */
var->green.length = 6;
var->green.offset = 5;
/* Encoding Blue */
var->blue.length = 5;
var->blue.offset = 0;
/* No hardware support for alpha blending */
var->transp.length = 0;
var->transp.offset = 0;
}
return 0;
}
/* Blank/unblank screen */
static int
myfb_blank(int blank_mode, struct fb_info *info)
{
switch (blank_mode) {
case FB_BLANK_POWERDOWN:
case FB_BLANK_VSYNC_SUSPEND:
case FB_BLANK_HSYNC_SUSPEND:
case FB_BLANK_NORMAL:
myfb_disable_controller(info);
break;
case FB_BLANK_UNBLANK:
myfb_enable_controller(info);
break;
}
return 0;
}
/* Configure pseudo color palette map */
static int
myfb_setcolreg(u_int color_index, u_int red, u_int green,
u_int blue, u_int transp, struct fb_info *info)
{
if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
/* Do any required translations to convert red, blue, green and
transp, to values that can be directly fed to the hardware */
/* ... */
((u32 *)(info->pseudo_palette))[color_index] =
(red << info->var.red.offset) |
(green << info->var.green.offset) |
(blue << info->var.blue.offset) |
(transp << info->var.transp.offset);
}
return 0;
}
/* Device-specific ioctl definition */
#define MYFB_SET_BRIGHTNESS _IOW('M', 3, int8_t)
/* Device-specific ioctl */
static int
myfb_ioctl(struct fb_info *info, unsigned int cmd,
unsigned long arg)
{
u32 blevel ;
switch (cmd) {
case MYFB_SET_BRIGHTNESS :
copy_from_user((void *)&blevel, (void *)arg,
sizeof(blevel)) ;
/* Write blevel to CONTRAST_REG */
/* ... */
break;
default:
return -EINVAL;
}
return 0;
}
/* The fb_ops structure */
static struct fb_ops myfb_ops = {
.owner = THIS_MODULE,
.fb_check_var = myfb_check_var,/* Sanity check */
.fb_set_par = myfb_set_par, /* Program controller registers */
.fb_setcolreg = myfb_setcolreg,/* Set color map */
.fb_blank = myfb_blank, /* Blank/unblank display */
.fb_fillrect = cfb_fillrect, /* Generic function to fill
rectangle */
.fb_copyarea = cfb_copyarea, /* Generic function to copy area */
.fb_imageblit = cfb_imageblit, /* Generic function to draw */
.fb_ioctl = myfb_ioctl, /* Device-specific ioctl */
};
/* Platform driver's probe() routine */
static int __init
myfb_probe(struct platform_device *pdev)
{
struct fb_info *info;
struct resource *res;
info = framebuffer_alloc(0, &pdev->dev);
/* ... */
/* Obtain the associated resource defined while registering the
corresponding platform_device */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
/* Get the kernel's sanction for using the I/O memory chunk
starting from LCD_CONTROLLER_BASE and having a size of
LCD_CONTROLLER_SIZE bytes */
res = request_mem_region(res->start, res->end - res->start + 1,
pdev->name);
/* Fill the fb_info structure with fixed (info->fix) and variable
(info->var) values such as frame buffer length, xres, yres,
bits_per_pixel, fbops, cmap, etc */
initialize_fb_info(info, pdev); /* Not expanded */
info->fbops = &myfb_ops;
fb_alloc_cmap(&info->cmap, 16, 0);
/* DMA-map the frame buffer memory coherently. info->screen_base
holds the CPU address of the mapped buffer,
info->fix.smem_start carries the associated hardware address */
info->screen_base = dma_alloc_coherent(0, info->fix.smem_len,
(dma_addr_t *)&info->fix.smem_start,
GFP_DMA | GFP_KERNEL);
/* Set the information in info->var to the appropriate
LCD controller registers */
myfb_set_par(info);
/* Register with the frame buffer core */
register_framebuffer(info);
return 0;
}
/* Platform driver's remove() routine */
static int
myfb_remove(struct platform_device *pdev)
{
struct fb_info *info = platform_get_drvdata(pdev);
struct resource *res;
/* Disable screen refresh, turn off DMA,.. */
myfb_disable_controller(info);
/* Unregister frame buffer driver */
unregister_framebuffer(info);
/* Deallocate color map */
fb_dealloc_cmap(&info->cmap);
kfree(info->pseudo_palette);
/* Reverse of framebuffer_alloc() */
framebuffer_release(info);
/* Release memory region */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(res->start, res->end - res->start + 1);
platform_set_drvdata(pdev, NULL);
return 0;
}
/* The platform driver structure */
static struct platform_driver myfb_driver = {
.probe = myfb_probe,
.remove = myfb_remove,
.driver = {
.name = "myfb",
},
};
/* Module Initialization */
int __init
myfb_init(void)
{
platform_device_add(&myfb_device);
return platform_driver_register(&myfb_driver);
}
/* Module Exit */
void __exit
myfb_exit(void)
{
platform_driver_unregister(&myfb_driver);
platform_device_unregister(&myfb_device);
}
module_init(myfb_init);
module_exit(myfb_exit);
syntax highlighted by Code2HTML, v. 0.9.1