|
Framebuffer Driver for the epson S1D15G00 controller based 12 bit color LCD with mmap support.
Code :
/* *File epson12bpp.c * *Driver for the epson S1D15G00 controller based 12-bit color LCD * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * * For all queries about this code, please contact the current author, * PrasadTVR <vishnu.tvrprasad@gmail.com> . * * */
#include <linux/kernel.h> #include <linux/init.h> #include <linux/clk.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/delay.h> #include <linux/dma-mapping.h> #include <linux/err.h> #include <linux/interrupt.h> #include <linux/spi/spi.h> #include <linux/delay.h> #include <linux/errno.h> #include <linux/fb.h> #include <linux/mm.h> #include <asm/io.h> #include <asm/uaccess.h> #include <linux/slab.h> #include <asm/io.h> #include <asm/arch/board.h> #include <asm/arch/gpio.h> #include <asm/arch/cpu.h> #include "lcd.h" #include <linux/vmalloc.h>
#define DEBUG 1 #define X_RES 132 #define Y_RES 132 #define B_PP 12 #define MEM_LEN X_RES*Y_RES*B_PP/8 #define EPSON_REFRESH_JIFFIES (HZ)/5 #define EPSON_NAME "EPSONREFD" #define BLACK 0x000
static void *videomemory; static u_long videomemorysize = MEM_LEN; module_param(videomemorysize, ulong, 0);
static int __devinit mylcd_probe(struct spi_device *spi); static int __devexit mylcd_remove(struct spi_device *spi); static void *rvmalloc(unsigned long size); static void rvfree(void *mem, unsigned long size); void LCDClearScreen(struct spi_device *spi) ; static void WriteSpiCommand(struct spi_device *spi, unsigned int c); static void WriteSpiData(struct spi_device *spi, unsigned int d);
/******************************************************************************************/ struct EpsonLCD { struct fb_info *info; struct spi_device *spi; u16 *scr; u8 *shadow; u8 quitting; int RefThrPid; struct semaphore thread_sem; };
static struct fb_fix_screeninfo Epson_fix __devinitdata = { .id = "EpsonLCD", .type = FB_TYPE_PACKED_PIXELS, .visual = FB_VISUAL_DIRECTCOLOR, .xpanstep = 0, .ypanstep = 0, .ywrapstep = 0, .line_length = X_RES*B_PP/8, .smem_len = MEM_LEN, .accel = FB_ACCEL_NONE, };
static struct fb_var_screeninfo Epson_var __devinitdata = { .xres = X_RES, .yres = Y_RES, .xres_virtual = X_RES, .yres_virtual = Y_RES, .bits_per_pixel = B_PP, .red = {8, 4, 0}, .green = {4, 4, 0}, .blue = {0, 4, 0}, .transp = {0, 0, 0}, .nonstd = 0, }; static struct spi_driver mylcd_driver = { .driver = { .name = "mylcd", .bus = &spi_bus_type, .owner = THIS_MODULE, },
.probe = mylcd_probe, .remove = __devexit_p(mylcd_remove),
/* FIXME: investigate suspend and resume... */ }; /***********************************************************************************************/ static void *rvmalloc(unsigned long size) { void *mem; unsigned long adr;
size = PAGE_ALIGN(size); mem = vmalloc_32(size); if (!mem) return NULL;
memset(mem, 0, size); /* Clear the ram out, no junk to the user */ adr = (unsigned long) mem; while (size > 0) { SetPageReserved(vmalloc_to_page((void *)adr)); adr += PAGE_SIZE; size -= PAGE_SIZE; }
return mem; } /***********************************************************************************************/ static void rvfree(void *mem, unsigned long size) { unsigned long adr;
if (!mem) return;
adr = (unsigned long) mem; while ((long) size > 0) { ClearPageReserved(vmalloc_to_page((void *)adr)); adr += PAGE_SIZE; size -= PAGE_SIZE; } vfree(mem); } /*********************************************************************************************/ static void WriteSpiCommand(struct spi_device *spi, unsigned int c) { unsigned int w = c & ~0x0100;
spi_write(spi, (u8 *)&w, 2); } /*-------------------------------------------------------------------------------------------*/ static void WriteSpiData(struct spi_device *spi, unsigned int d) { unsigned int w = d | 0x0100;
spi_write(spi, (u8 *)&w, 2); } /*------------------------------------------------------------------------------------------*/ void Backlight(unsigned char state) {
if(state == BKLGHT_LCD_ON) at91_set_gpio_value(AT91_PIN_PB9,1); else at91_set_gpio_value(AT91_PIN_PB9,0);
} /*------------------------------------------------------------------------------------------*/ static void epson_gram_update(struct EpsonLCD *par) { struct spi_device *spi = par->spi; unsigned int x=0, y=0, i=0; u16 *scr = par->scr; u8 *videomemory = par->info->screen_base; for(x = 0; x <MEM_LEN; x++) { scr[i] = videomemory[x] | 0x100; i++; }
WriteSpiCommand(spi, CASET); // column start/end ram (x) WriteSpiData(spi, 0); WriteSpiData(spi, 131); WriteSpiCommand(spi, PASET); // page start/end ram (y) WriteSpiData(spi, 0); WriteSpiData(spi, 131);
WriteSpiCommand(spi, RAMWR); // write some stuff spi_write(spi, (u8 *)scr, i*2);
} /*------------------------------------------------------------------------------------------*/ static void epson_fillrect(struct fb_info *info, const struct fb_fillrect *rect) { struct EpsonLCD *par = info->par;
cfb_fillrect(info, rect);
/* update the physical lcd */ epson_gram_update(par); } /*------------------------------------------------------------------------------------------*/ static void epson_copyarea(struct fb_info *info, const struct fb_copyarea *area) { struct EpsonLCD *par = info->par;
cfb_copyarea(info, area);
/* update the physical lcd */ epson_gram_update(par); } /*------------------------------------------------------------------------------------------*/ static void epson_imageblit(struct fb_info *info, const struct fb_image *image) { struct EpsonLCD *par = info->par;
cfb_imageblit(info, image);
/* update the physical lcd */ epson_gram_update(par); } /*------------------------------------------------------------------------------------------*/ static int epson_mmap(struct fb_info *info,struct vm_area_struct *vma) { unsigned long start = vma->vm_start; unsigned long size = vma->vm_end - vma->vm_start; unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; unsigned long page, pos; if (offset + size > info->fix.smem_len) { return -EINVAL; }
pos = (unsigned long)info->fix.smem_start + offset;
while (size > 0) { page = vmalloc_to_pfn((void *)pos); if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) { return -EAGAIN; } start += PAGE_SIZE; pos += PAGE_SIZE; if (size > PAGE_SIZE) size -= PAGE_SIZE; else size = 0; }
vma->vm_flags |=VM_IO | VM_RESERVED; return 0; } /*-------------------------------------------------------------------------------------------*/ static int epson_thread(void *arg) { struct EpsonLCD *info = (struct EpsonLCD*)arg; /* Do some setup to look like a real daemon */ daemonize(EPSON_NAME); //set_user_nice(current, -5); while (!info->quitting) { struct spi_device *spi = info->spi; if (memcmp(info->shadow, info->info->screen_base,MEM_LEN)) { memcpy(info->shadow, info->info->screen_base,MEM_LEN); epson_gram_update(info); } set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(EPSON_REFRESH_JIFFIES); } /* Tell module we're done */ up(&info->thread_sem); return 0; } /*------------------------------------------------------------------------------------------*/ void resetLCD (void) { gpio_direction_output(AT91_PIN_PB8,0); mdelay(100); gpio_direction_output(AT91_PIN_PB8,1); mdelay(100); } /*------------------------------------------------------------------------------------------*/ void LCDInit(struct spi_device *spi) {
resetLCD();
WriteSpiCommand(spi,DISCTL);
WriteSpiData(spi,0x0c); // default WriteSpiData(spi,0x20); // (32 + 1) * 4 = 132 lines (of which 130 are visible) WriteSpiData(spi,0x00); // default WriteSpiData(spi,0x01); // default
WriteSpiCommand(spi,COMSCN); WriteSpiData(spi,0x01); // Scan 1-80 WriteSpiCommand(spi,OSCON);
WriteSpiCommand(spi,SLPOUT); WriteSpiCommand(spi,PWRCTR); WriteSpiData(spi,0x0f); // referance voltage regulator on, circuit voltage follower on, BOOST ON
WriteSpiCommand(spi,DISINV); WriteSpiCommand(spi,DATCTL); WriteSpiData(spi,0x01); // all inversions off, column direction WriteSpiData(spi,0x00); // RGB sequence WriteSpiData(spi,0x02); // Grayscale -> 16 WriteSpiCommand(spi,VOLCTR); // electronic volume, this is the contrast/brightness(EPSON) WriteSpiData(spi,0x24); // volume (contrast) setting - fine tuning, original WriteSpiData(spi,0x03); // internal resistor ratio - coarse adjustmen
WriteSpiCommand(spi,NOP);
WriteSpiCommand(spi,DISON);
} /*----------------------------------------------------------------------------------------------------------*/ static struct fb_ops epson_ops = { .owner = THIS_MODULE, .fb_read = epson_read, .fb_mmap = epson_mmap, .fb_fillrect = epson_fillrect, .fb_copyarea = epson_copyarea, .fb_imageblit = epson_imageblit, }; /*------------------------------------------------------------------------------------------------------------*/ void LCDClearScreen(struct spi_device *spi) { long i; // loop counter // Row address set (command 0x2B) WriteSpiCommand(spi,PASET); WriteSpiData(spi,0); WriteSpiData(spi,131); // Column address set (command 0x2A) WriteSpiCommand(spi,CASET); WriteSpiData(spi,0); WriteSpiData(spi,131); // set the display memory to BLACK WriteSpiCommand(spi,RAMWR); for(i = 0; i < ((131 * 131) / 2); i++) { WriteSpiData(spi,(BLACK >> 4) & 0xFF); WriteSpiData(spi,((BLACK & 0xF) << 4) | ((BLACK >> & 0xF)); WriteSpiData(spi,BLACK & 0xFF); } } /*------------------------------------------------------------------------------------------------------------*/ static int __devinit mylcd_probe(struct spi_device *spi) { int retval; struct fb_info *info; struct EpsonLCD *priv; spi->master->bus_num = 1; spi->chip_select = 0; spi->max_speed_hz = 6 * 1000 * 1000; // ?? spi->mode = SPI_MODE_0; spi->bits_per_word = 9; retval = spi_setup(spi); #ifdef DEBUG printk("SPI setup for LCD successful \n"); #endif if (retval < 0) return retval; LCDInit(spi); #ifdef DEBUG printk("LCD Initialisation successful \n"); #endif LCDClearScreen(spi); retval = -ENOMEM; videomemorysize = MEM_LEN; if (!(videomemory = rvmalloc(videomemorysize))) return retval; memset(videomemory, 0, videomemorysize); #ifdef DEBUG printk("videomemory Allocation successful \n"); #endif
info = framebuffer_alloc(sizeof(struct EpsonLCD), &spi->dev); if (!info) goto err; #ifdef DEBUG printk("Framebuffer alloc successful \n"); #endif
info->screen_base = (char __iomem *)videomemory; info->fbops = &epson_ops; info->var = Epson_var; Epson_fix.smem_start=(unsigned long) videomemory; Epson_fix.smem_len = PAGE_ALIGN(videomemorysize); info->fix = Epson_fix; info->flags = FBINFO_FLAG_DEFAULT;
priv = info->par; priv->info = info; priv->spi = spi; if(!(priv->scr = kzalloc(MEM_LEN*2, GFP_KERNEL))) goto err;
if(!(priv->shadow = kzalloc(MEM_LEN, GFP_KERNEL))) goto err1; priv->quitting=0; retval = register_framebuffer(info); if (retval < 0) goto err2; #ifdef DEBUG printk("Register framebuffer successful \n"); #endif
dev_set_drvdata(&spi->dev, info); init_MUTEX_LOCKED(&priv->thread_sem); if ((priv->RefThrPid = kernel_thread(epson_thread,priv, CLONE_FS | CLONE_FILES | CLONE_SIGHAND)) < 0) { printk("can't create refresh thread\n"); goto err3; }
printk(KERN_INFO "fb%d: %s frame buffer device, %dK of video memory\n", info->node, info->fix.id, videomemorysize >> 10);
return 0;
err3:
unregister_framebuffer(info); framebuffer_release(info); err2: kfree(priv->shadow); err1: kfree(priv->scr); err: rvfree(videomemory,videomemorysize); return retval;
return 0; } /*-----------------------------------------------------------------------------------------------------------------*/ static int __devexit mylcd_remove(struct spi_device *spi) { struct fb_info *info = dev_get_drvdata(&spi->dev);
if (info) { struct EpsonLCD *priv = info->par; priv->quitting=1; down(&priv->thread_sem); unregister_framebuffer(info); rvfree((void __force *)info->screen_base,MEM_LEN); kfree((void __force *)priv->scr); kfree((void __force *)priv->shadow); framebuffer_release(info); } return 0; } /*------------------------------------------------------------------------------------------------------------------*/ static int __init mylcd_spi_init(void) { int ret=0;
at91_set_gpio_output(AT91_PIN_PB8,1); at91_set_gpio_output(AT91_PIN_PB9,1); spi_register_driver(&mylcd_driver); return ret; }
module_init(mylcd_spi_init); /*----------------------------------------------------------------------------------------------------------------*/ static void __exit mylcd_spi_exit(void) { spi_unregister_driver(&mylcd_driver); } module_exit(mylcd_spi_exit); /*---------------------------------------------------------------------------------------------------------------*/ MODULE_DESCRIPTION("Nokia 6610 LCD driver"); MODULE_AUTHOR("vishnu.tvrprasad@gmail.com"); MODULE_LICENSE("GPL");
|