Code:
#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/slab.h>
#include <linux/interrupt.h>
#include <linux/kthread.h>
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <asm/io.h>
#include<linux/delay.h>
#include <linux/uaccess.h>
#include <linux/screen_info.h>
#include <linux/gfp.h>
#include "lcd.h"
//static int lcd_fb_thread(void *param);
struct lcd_fb_par {
struct task_struct *lcd_fb_thread_task;
struct platform_device *pdev;
u8 *buffer;
u8 *shadow;
};
static struct fb_fix_screeninfo lcd_fb_fix __devinitdata = {
.id = "lcd_fb",
.type = FB_TYPE_PACKED_PIXELS,
.visual = FB_VISUAL_TRUECOLOR,
.xpanstep = 0,
.ypanstep = 0,
.ywrapstep = 0,
.line_length = X_RES * B_PP / 8,
.accel = FB_ACCEL_NONE,
};
static struct fb_var_screeninfo lcd_fb_var __devinitdata = {
.xres = X_RES,
.yres = Y_RES,
.xres_virtual = X_RES,
.yres_virtual = Y_RES,
.height = -1,
.width = -1,
.vmode = FB_VMODE_NONINTERLACED,
.bits_per_pixel = B_PP,
.red = { 11, 5, 0 },
.green = { 5, 6, 0 },
.blue = { 0, 5, 0 },
.nonstd = 0,
};
//**********************************************************************************************************************************************
static void write_command(unsigned short command)
{
unsigned short cmd=0;
CLR_CS;
CLR_CD;
SET_RD;
SET_WR;
//cmd=command & 8;
__raw_writel( command, PORTD + PIO_ODSR);//PORT INIT
CLR_WR;
SET_WR;
SET_CS;
}
static void write_data(unsigned short data)
{
unsigned short dd=0;
CLR_CS;
SET_CD;
SET_RD;
SET_WR;
//dd=data & 0xFF00;
__raw_writel( data, PORTD + PIO_ODSR);//PORT INIT
CLR_WR;
SET_WR;
SET_CS;
// keeps the display from going wonky -- don't know why this is.
__raw_writel( 0xFFFF, PORTD + PIO_ODSR);//PORT INIT
}
static void lcd_cls(unsigned short data)
{
unsigned short i,j;
write_command(0x0200); // GRAM Address Set (Horizontal Address) (R200h)
write_data(0x0000); // Page 30 of SPFD5420A Datasheet
write_command(0x0201); // GRAM Address Set (Vertical Address) (R201h)
write_data(0x0000); // Page 30 of SPFD5420A Datasheet
write_command(0x0202); // Write Data to GRAM (R202h)
for(i=0;i<400;i++)
{
for(j=0;j<240;j++)
{
write_data(data);
}
}
}
static void spfd5420Command(unsigned short cmmd,unsigned short data)
{
//Command
CLR_CS;//Chip select on
CLR_CD;
SET_RD;
SET_WR;
__raw_writel( cmmd, PORTD + PIO_ODSR);//PORT INIT
CLR_WR;
SET_WR;
//SET_CS;
//*********************
//Data
//CLR_CS;
SET_CD;
//SET_RD;
//SET_WR;
//dd=data & 0xFF00;
__raw_writel( data, PORTD + PIO_ODSR);//PORT INIT
CLR_WR;
SET_WR;
SET_CS;//Chip select off
//write_command(cmmd);
//write_data(data);
}
static void lcd_test(void)
{
unsigned int i,j;
write_command(0x0200); // GRAM Address Set (Horizontal Address) (R200h)
write_data(0x0000); // Page 30 of SPFD5420A Datasheet
write_command(0x0201); // GRAM Address Set (Vertical Address) (R201h)
write_data(0x0000); // Page 30 of SPFD5420A Datasheet
write_command(0x0202); // Write Data to GRAM (R202h)
for(i=0;i<240;i++)
for(j=0;j<400;j++)
{
if(i>209)write_data(1);
else if(i>179)write_data(BLUE);
else if(i>149)write_data(RED);
else if(i>119)write_data(GREEN);
else if(i>89)write_data(CYAN);
else if(i>59)write_data(MAGENTA);
else if(i>29)write_data(YELLOW);
else write_data(WHITE);
}
}
//******************************************************************************************************************
//******************************************************************************************************************
/*
//--------------- Normal set ---------------//
//--------------- Panel interface control 1~6 ---------------//
//--------------- Partial display ---------------//
*/
static void init1_lcd(void)
{
__raw_writel( LCD_CMMD_MASK, PORTC + PIO_PER);//PORT INIT
__raw_writel( LCD_CMMD_MASK, PORTC + PIO_OER);//PORT OUT INIT
__raw_writel( LCD_DATA_MASK, PORTD + PIO_PER);//PORT INIT LCD_DATA_MASK
__raw_writel( LCD_DATA_MASK, PORTD + PIO_OER);//PORT OUT INIT
__raw_writel( LCD_DATA_MASK, PORTD + PIO_OWER);//PORT OUT INIT
//***************************************************
__raw_writel( LCD_CMMD_MASK, PORTC + PIO_SODR);//PORT OUT INIT
__raw_writel( 0xFFFF, PORTD + PIO_ODSR);
CLR_RESET;
mdelay(100);
SET_RESET;
mdelay(250);
mdelay(250);
mdelay(250);
mdelay(250);
spfd5420Command(0x0606, 0x0000); // Pin Control (R606h)
ndelay(500);
spfd5420Command(0x0007, 0x0001); // Display Control 1 (R007h)
ndelay(500);
spfd5420Command(0x0110, 0x0001); // Power Control 6(R110h)
ndelay(500);
spfd5420Command(0x0100, 0x17B0); // Power Control 1 (R100h)
spfd5420Command(0x0101, 0x0147); // Power Control 2 (R101h)
spfd5420Command(0x0102, 0x019D); // Power Control 3 (R102h)
spfd5420Command(0x0103, 0x3600); // Power Control 4 (R103h)
spfd5420Command(0x0281, 0x0010); // NVM read data 2 (R281h)
ndelay(500);
spfd5420Command(0x0102, 0x01BD); // Power Control 3 (R102h)
ndelay(500);
//--------------- Power control 1~6 ---------------//
spfd5420Command(0x0100, 0x16B0); // Power Control 1 (R100h)
spfd5420Command(0x0101, 0x0147); // Power Control 2 (R101h)
spfd5420Command(0x0102, 0x01BD); // Power Control 3 (R102h)
spfd5420Command(0x0103, 0x2d00); // Power Control 4 (R103h)
spfd5420Command(0x0107, 0x0000); // Power Control 5 (R107h)
spfd5420Command(0x0110, 0x0001); // Power Control 6(R110h)
spfd5420Command(0x0280, 0x0000); // NVM read data 1 (R280h)
spfd5420Command(0x0281, 0x0006); // NVM read data 2 (R281h)
spfd5420Command(0x0282, 0x0000); // NVM read data 3 (R282h)
//------- Gamma 2.2 control (R300h to R30Fh) ------//
spfd5420Command(0x0300, 0X0101);
spfd5420Command(0x0301, 0x0b27);
spfd5420Command(0x0302, 0x132A);
spfd5420Command(0x0303, 0x2A13);
spfd5420Command(0x0304, 0x270B);
spfd5420Command(0x0305, 0x0101);
spfd5420Command(0x0306, 0x1205);
spfd5420Command(0x0307, 0x0512);
spfd5420Command(0x0308, 0X0005);
spfd5420Command(0x0309, 0x0003);
spfd5420Command(0x030A, 0X0F04);
spfd5420Command(0x030B, 0x0F00);
spfd5420Command(0x030C, 0x000F);
spfd5420Command(0x030D, 0x040F);
spfd5420Command(0x030E, 0x0300);
spfd5420Command(0x030F, 0x0500);
spfd5420Command(0x0400, 0x3100); // Base Image Number of Line (R400h)0x3500 0x3100->400 dots
spfd5420Command(0x0401, 0x0001); // Base Image Display Control (R401h) 0x0001
spfd5420Command(0x0404, 0x0000); // Based Image Vertical Scroll Control (R404h)
//--------------- Normal set ---------------//
spfd5420Command(0x0000, 0x0000); // ID Read Register (R000h)
spfd5420Command(0x0001, 0x0100); // Driver Output Control Register (R001h) 0x0100
spfd5420Command(0x0002, 0x0100); // LCD Driving Waveform Control (R002h)
spfd5420Command(0x0003, 0x1018); // Entry Mode (R003h)//0x1030
spfd5420Command(0x0006, 0x0000); // Display Control 1 (R007h)
spfd5420Command(0x0008, 0x0808); // Display Control 2 (R008h)
spfd5420Command(0x0009, 0x0001); // Display Control 3 (R009h)
spfd5420Command(0x000B, 0x0010); // Low Power Control (R00Bh)
spfd5420Command(0x000C, 0x0000); // External Display Interface Control 1 (R00Ch)
spfd5420Command(0x000F, 0x0000); // External Display Interface Control 2 (R00Fh)
spfd5420Command(0x0007, 0x0001); // Display Control 1 (R007h)
//--------------- Panel interface control 1~6 ---------------//
spfd5420Command(0x0010, 0x0012); // Panel Interface Control 1 (R010h) //0x0012
spfd5420Command(0x0011, 0x0202); // Panel Interface Control 2 (R011h)
spfd5420Command(0x0012, 0x0300); // Panel Interface control 3 (R012h)
spfd5420Command(0x0020, 0x021E); // Panel Interface control 4 (R020h)
spfd5420Command(0x0021, 0x0202); // Panel Interface Control 5 (021Rh)
spfd5420Command(0x0022, 0x0100); // Panel Interface Control 6 (R022h)
spfd5420Command(0x0090, 0x8000); // Frame Marker Control (R090h) //0x8000
//--------------- Partial display ---------------//
spfd5420Command(0x0210, 0x0000); // Window Horizontal RAM Address Start (R210h)
spfd5420Command(0x0211, 0x00EF); // Window Horziontal RAM Address End (R211h) 0x00EF
spfd5420Command(0x0212, 0x0000); // Window Vertical RAM Address Start (R212h)
spfd5420Command(0x0213, 0x018F); // Window Vertical RAM Address End (R213h) 0x018F
spfd5420Command(0x0500, 0X0000); // Display Position - Partial Display 1 (R500h)
spfd5420Command(0x0501, 0X0000); // RAM Address Start - Partial Display 1 (R501h)
spfd5420Command(0x0502, 0X0000); // RAM Address End - Partail Display 1 (R502h)
spfd5420Command(0x0503, 0x0000); // Display Position - Partial Display 2 (R503h)
spfd5420Command(0x0504, 0x0000); // RAM Address Start – Partial Display 2 (R504h)
spfd5420Command(0x0505, 0x0000); // RAM Address End – Partial Display 2 (R505h)
spfd5420Command(0x0606, 0x0000); // Pin Control (R606h)
spfd5420Command(0x06F0, 0X0000); // NVM Access Control (R6F0h)
//--------------- Display on --------------------//
spfd5420Command(0x0007, 0x0173); // Display Control 1 (R007h)
ndelay(500);
spfd5420Command(0x0007, 0x0171); // Display Control 1 (R007h)
ndelay(500);
spfd5420Command(0x0007, 0x0173); // Display Control 1 (R007h)
}
//**************************************************************************************************************************************************
static int lcd_fb_blank(int blank_mode, struct fb_info *info)
{
// struct lcd_fb_par *par = info->par;
switch (blank_mode) {
case FB_BLANK_UNBLANK:
// spi_command(par->spi, DISPON);
// break;
case FB_BLANK_NORMAL:
case FB_BLANK_VSYNC_SUSPEND:
case FB_BLANK_HSYNC_SUSPEND:
case FB_BLANK_POWERDOWN:
// spi_command(par->spi, DISPOFF);
// break;
default:
return -EINVAL;
}
return 0;
}
static int lcd_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
{
unsigned long size, offset;
size = vma->vm_end - vma->vm_start;
offset = vma->vm_pgoff << PAGE_SHIFT;
offset += info->fix.smem_start;
if (remap_pfn_range(vma, vma->vm_start, offset >> PAGE_SHIFT,
size, vma->vm_page_prot))
return -EAGAIN;
printk(KERN_INFO "lcd_fb: mmap framebuffer P(%lx)->V(%lx), size %lx\n",
offset, vma->vm_start, size);
return 0;
}
static int lcd_fb_fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
u_int transp, struct fb_info *info)
{
if (regno >= 256)
return -EINVAL;
if (info->var.grayscale) {
/* grayscale = 0.30*R + 0.59*G + 0.11*B */
red = green = blue =
(red * 77 + green * 151 + blue * 28) >> 8;
}
if (info->fix.visual == FB_VISUAL_TRUECOLOR) {
u32 value;
/* Place color in the pseudopalette */
if (regno > 16)
return -EINVAL;
red >>= (16 - info->var.red.length);
green >>= (16 - info->var.green.length);
blue >>= (16 - info->var.blue.length);
value = (red << info->var.red.offset) |
(green << info->var.green.offset)|
(blue << info->var.blue.offset);
value &= 0xFFFF;
((u32 *) (info->pseudo_palette))[regno] = value;
}
return 0;
}
static struct fb_ops lcd_fb_ops = {
.owner = THIS_MODULE,
//.fb_read = fb_sys_read,
//.fb_write = fb_sys_write,
//.fb_fillrect = sys_fillrect,
//.fb_copyarea = sys_copyarea,
//.fb_imageblit = sys_imageblit,
.fb_blank = lcd_fb_blank,
.fb_mmap = lcd_fb_mmap,
.fb_setcolreg = lcd_fb_fb_setcolreg,
};
static int lcd_fb_thread(void *param)
{
struct lcd_fb_par *par = (struct lcd_fb_par *)param;
int i,ii;
unsigned short data;
set_user_nice(current, 5);
while(!kthread_should_stop()) {
/* Optimize refresh: transfer image only when it has changed */
if(memcmp(par->shadow, par->buffer, MEM_LEN))
{
memcpy(par->shadow, par->buffer, MEM_LEN);
unsigned short *fb=(unsigned short*)par->buffer;
spfd5420Command(0x0200,0x0000);
spfd5420Command(0x0201,0x0000);
CLR_CS;
//Command ************************
CLR_CD;
SET_RD;
SET_WR;
__raw_writel( 0x0202, PORTD + PIO_ODSR);//PORT INIT
CLR_WR;//Pulse
SET_WR;
//********************************
//Data
SET_CD;
//SET_RD;
for(i = 0; i < MEM_LEN/2; i++) // += 2
{
__raw_writel( *fb++, PORTD + PIO_ODSR);//PORT INIT
CLR_WR;
SET_WR;
}
SET_WR;
SET_CS;
}
schedule_timeout_interruptible(REFR_TIME);
}
return 0;
}
//static int __devinit lcd_probe(struct platform_device *pdev)
static int __init lcd_probe(struct platform_device *pdev)
{
int retval=-ENOMEM;
//struct device *dev = &pdev->dev;
struct fb_info *info;
struct lcd_fb_par *par;
init1_lcd();
printk(KERN_WARNING "framebuffer_alloc\n");
info = framebuffer_alloc(sizeof(struct lcd_fb_par),&pdev->dev);//dev &spi->dev
if(!info)
return retval;
printk(KERN_WARNING "info->screen_base alloc_pages_exact\n");
info->screen_base = alloc_pages_exact(MEM_LEN, GFP_DMA | __GFP_ZERO);
if(!info->screen_base)
goto err;
printk(KERN_INFO "info->screen_base 0x%x\n",info->screen_base);
info->fbops = &lcd_fb_ops;
info->var = lcd_fb_var;
info->fix = lcd_fb_fix;
info->fix.smem_len = MEM_LEN;
info->fix.smem_start = (unsigned long)virt_to_phys(info->screen_base);
info->screen_size = info->fix.smem_len;
info->flags = FBINFO_FLAG_DEFAULT;
//pseudo palette
printk(KERN_INFO "pseudo_palette alloc_pages_exact\n");
info->pseudo_palette = alloc_pages_exact(MEM_LEN, GFP_DMA | __GFP_ZERO);
if(!info->pseudo_palette)
goto err1;
printk(KERN_INFO "info->pseudo_palette 0x%x\n",info->pseudo_palette);
//cmap
printk(KERN_INFO "fb_alloc_cmap\n");
if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) {
goto err2;
}
par = info->par;
par->pdev = pdev;
par->buffer = info->screen_base;
printk(KERN_INFO "kmalloc par->shadow\n");
par->shadow = kmalloc(MEM_LEN, GFP_KERNEL | GFP_DMA);
if(!par->shadow)
goto err2;
printk(KERN_INFO "par->shadow 0x%x\n",par->shadow);
printk(KERN_INFO "register_framebuffer\n");
retval = register_framebuffer(info);
if (retval < 0)
goto err3;
printk(KERN_INFO "retval=%i\n",retval);
printk(KERN_INFO "dev_set_drvdata\n");
dev_set_drvdata(&pdev->dev, info);
printk(KERN_INFO "kthread_run\n");
par->lcd_fb_thread_task = kthread_run(lcd_fb_thread, par, "lcd");
retval = -EIO;
if(IS_ERR(par->lcd_fb_thread_task))
goto err3;
printk(KERN_INFO "fb%d: %s frame buffer device, %lx base address %dK of video memory\n",
info->node, info->fix.id, info->fix.smem_start, info->fix.smem_len >> 10);
printk(KERN_INFO "Alles OK\n");
mdelay(1000);
return 0;
err3:
printk(KERN_INFO "err3\n");
kfree(par->shadow);
err2:
printk(KERN_INFO "err2\n");
free_pages_exact(info->pseudo_palette, MEM_LEN);
err1:
printk(KERN_INFO "err1\n");
free_pages_exact(par->buffer, MEM_LEN);
err:
printk(KERN_INFO "err\n");
framebuffer_release(info);
return retval;
return 0;
}
//static int __devexit lcd_remove(struct platform_device *pdev)
static int __exit lcd_remove(struct platform_device *pdev)
{
struct fb_info *info = dev_get_drvdata(&pdev->dev);
if (info) {
struct lcd_fb_par *par = info->par;
unregister_framebuffer(info);
free_pages_exact(par->buffer, MEM_LEN);
kfree(par->shadow);
framebuffer_release(info);
kthread_stop(par->lcd_fb_thread_task);
}
return 0;
}
static struct platform_driver lcd_driver =
{
.probe = lcd_probe,
.remove = __exit_p(lcd_remove),
.driver.name = "lcd_fb",
.driver.owner = THIS_MODULE,
};
static struct platform_device lcd_device = {
.name = "lcd_fb",
};
static int lcd_init(void)
{
int ret;
ret = platform_driver_register(&lcd_driver);
printk(KERN_INFO "driver_register :%i\n",ret);
if (!ret) {
ret = platform_device_register(&lcd_device);
printk(KERN_INFO "device_register :%i\n",ret);
if (ret)
{
printk(KERN_INFO "driver_unregister\n");
platform_driver_unregister(&lcd_driver);
}
}
return ret;
}
static void lcd_exit(void)
{
printk(KERN_INFO "lcd_exit\n");
platform_device_unregister(&lcd_device);
platform_driver_unregister(&lcd_driver);
}
module_init(lcd_init);
module_exit(lcd_exit);
MODULE_AUTHOR("Heinrich");
MODULE_LICENSE("GPL");
Looking for additional LCD resources? Check out our LCD blog for the latest developments in LCD technology.