diff options
Diffstat (limited to 'drivers/video/s1d13mp900fb.c')
-rw-r--r-- | drivers/video/s1d13mp900fb.c | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/drivers/video/s1d13mp900fb.c b/drivers/video/s1d13mp900fb.c new file mode 100644 index 00000000000000..3248004d41fa3f --- /dev/null +++ b/drivers/video/s1d13mp900fb.c @@ -0,0 +1,282 @@ +/* drivers/video/s1d13mp900fb.c + * + * IT IS NOT INTENDED THAT THIS FILE BE SUBMITTED TO VANILLA + * + * for now this is a standalone driver for testing the + * specifics of the MobilePro->S1D13806 interface + * + * the existing s1d13xxxfb.c driver should work with the Mobilepro900c + * if it is told where in memory to find the chip + * physical addresses: + * base/registers 0x0c00_0000 + * framebuffer 0x0c20_0000 + * and initial register settings + * TODO establish default register values + * perhaps all that belongs in + * arch/arm/mach-pxa/mp900.c + * + * blanking/backlight specific code should go in + * drivers/video/backlight/mp900_bl.c or so + * + * Michael Petchkovsky mkpetch@internode.on.net May 2007 + */ + +/* TODO + * clear hardware cursor + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/errno.h> +#include <linux/string.h> +#include <linux/interrupt.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/fb.h> +#include <linux/init.h> + +#include <linux/ioport.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> + +#include <asm/io.h> +#include <asm/uaccess.h> +#include <asm/hardware.h> + +#include "console/fbcon.h" + +#define PFX "s1d13mp900fb: " +#define S1D13MP900_FB_PHYS 0x0C200000 +#define S1D13MP900_REG_PHYS 0x0C000000 +#define S1D13MP900_FB_SIZE 0x00140000 + +u32 pseudo_pal[16]; +static void *remapped_regs; +static void *remapped_fb; +struct fb_info fb_info; + +static int s1d13mp900fb_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, + struct fb_info *fb_info) +{ + int bpp, m = 0; + + bpp = fb_info->var.bits_per_pixel; + m = (bpp <= 8) ? (1 << bpp) : 256; + if (regno >= m) { + printk("regno %d out of range (max %d)\n", regno, m); + return -EINVAL; + } + switch (bpp) { + case 8: + break; + case 16: + pseudo_pal[regno] = ((red & 0xF800) | + ((green & 0xFC00) >> 5) | + ((blue & 0xF800) >> 11)); + break; + } + + return 0; +} + +static int s1d13mp900fb_blank(int blank, struct fb_info *info) +{ + u32 rval; + switch (blank) { + case FB_BLANK_POWERDOWN: + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_NORMAL: + /* we want to switch off the backlight via + * the s1d13xxx gpio pins and put the chip + * to sleep + * + * gpio pins are controlled through register + * 0x08/0x09, we clear pins 4,1,2 and set pin 0 + * + * this could be done by + * writel(0x0001, remapped_regs + 0x8) + * but safer to read initial values and set pins + * one-by-one, delays could be introduced between + * steps if required... + */ + rval = readl(remapped_regs + 0x8); + rval &= 0xffef; + writel(rval, remapped_regs + 0x8); + rval &= 0xfffd; + writel(rval, remapped_regs + 0x8); + rval &= 0xfffd; + writel(rval, remapped_regs + 0x8); + rval |= 1; + writel(rval, remapped_regs + 0x8); + /* power save config register is at 0x1f0 + * set it to 0x11 for zzz and 0x10 to wake + */ + writel(0x11, remapped_regs + 0x1f0); + /* after this it would be safe to shutdown + * pixel and memory clocks, read 0x1f1 to + * confirm sleep-mode entered + * + * perhaps PWM0 clock can be disabled with + * backlight off to save a little power + */ + break; + + case FB_BLANK_UNBLANK: + /* we reverse the blanking sequence */ + writel(0x10, remapped_regs + 0x1f0); + rval = readl(remapped_regs + 0x8); + rval &= 0xfffe; + writel(rval, remapped_regs + 0x8); + rval |= 4; + writel(rval, remapped_regs + 0x8); + rval |= 2; + writel(rval, remapped_regs + 0x8); + /* want a delay here? */ + rval |= 0x10; + writel(rval, remapped_regs + 0x8); + } + + return 0; +} + +struct s1d13mp900fb_par { + void __iomem *regs; + unsigned char display; +}; + +static struct fb_fix_screeninfo s1d13mp900fb_fix __initdata = { + .id = "S1DMP_FBID", +// .smem_len = (640 * 240 * 16) / 8, //TODO check this + .smem_len = S1D13MP900_FB_SIZE, + .smem_start = S1D13MP900_FB_PHYS, + .type = FB_TYPE_PACKED_PIXELS, + .visual = FB_VISUAL_TRUECOLOR, + .line_length = (640 * 16) / 8, + .type_aux = 0, + .xpanstep = 0, + .ypanstep = 1, + .ywrapstep = 0, + .accel = FB_ACCEL_NONE, +}; + +static struct fb_var_screeninfo s1d13mp900fb_screeninfo = { + .xres = 640, + .yres = 240, + .xres_virtual = 640, + .yres_virtual = 240, + .bits_per_pixel = 16, + .red.length = 5, + .green.length = 6, + .blue.length = 5, + .transp.length = 0, + .red.offset = 11, + .green.offset = 5, + .blue.offset = 0, + .transp.offset = 0, + .activate = FB_ACTIVATE_NOW, + .height = -1, + .width = -1, + .vmode = FB_VMODE_NONINTERLACED, + .accel_flags = 0, + .nonstd = 0, +}; + +static struct fb_ops s1d13mp900fb_ops = { + .owner = THIS_MODULE, + .fb_setcolreg = s1d13mp900fb_setcolreg, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, +// .fb_cursor = soft_cursor, + .fb_blank = s1d13mp900fb_blank, +}; + +/* colour lookup tables? l8r if we need them + */ + +unsigned char LUT8[256*3]; + +//static char lut_base[90]; + +void s1d13mp900fb_init_hardware (void) +{ +// unsigned char *pLUT = LUT8; +// unsigned char *pseed = lut_base; +// unsigned char plast[3]; +// int i, j, rgb; + int rval; + + /* OK let's assume chip has been set up by bootloader for now + * this would be a good chance to take a peek at the regs ;) + * TODO let's not assume... + */ + + rval = readb(remapped_regs); + printk (KERN_INFO PFX "reg[0x000] revision code is 0x%X\n", rval); +} + +int __init s1d13mp900fb_init(void) +{ + if (fb_get_options("s1d13mp900fb", NULL)) + return -ENODEV; + + printk (KERN_INFO PFX "initing now...\n"); + + /* remap framebuffer and registers */ + + /* do we need to request_mem_region ? */ + if (!request_mem_region(S1D13MP900_FB_PHYS, + S1D13MP900_FB_SIZE, "s1d13806_fb")) { + printk (KERN_ERR PFX "unable to reserve framebuffer\n"); + } else { + remapped_fb = ioremap_nocache(S1D13MP900_FB_PHYS, + S1D13MP900_FB_SIZE); + if (!remapped_fb) + printk (KERN_INFO PFX "unable to map framebuffer\n"); + } + + if (!request_mem_region(S1D13MP900_REG_PHYS, 512, "s1d13806_regs")) { + printk (KERN_ERR PFX "unable to reserve registers\n"); + } else { + remapped_regs = ioremap_nocache(S1D13MP900_REG_PHYS, 512); + if (!remapped_regs) + printk(KERN_ERR PFX "unable to map registers\n"); + } + + fb_info.screen_base = remapped_fb; + fb_info.screen_size = S1D13MP900_FB_SIZE; //TODO correct?? + memset(&fb_info.var, 0, sizeof(fb_info.var)); + + s1d13mp900fb_init_hardware(); + /* you could zero out the display here with memset */ + + fb_info.fbops = &s1d13mp900fb_ops; + fb_info.var = s1d13mp900fb_screeninfo; + fb_info.fix = s1d13mp900fb_fix; + fb_info.flags = FBINFO_DEFAULT; + fb_info.pseudo_palette= &pseudo_pal; + + if (register_framebuffer(&fb_info) < 0) + return 1; + + return 0; +} + +static void __exit s1d13mp900fb_exit(void) +{ + printk (KERN_INFO PFX "unregistering framebuffer device\n"); + iounmap(remapped_regs); + iounmap(remapped_fb); + release_mem_region(S1D13MP900_REG_PHYS, 512); + release_mem_region(S1D13MP900_FB_PHYS, S1D13MP900_FB_SIZE); + unregister_framebuffer(&fb_info); +} + +module_init(s1d13mp900fb_init); +module_exit(s1d13mp900fb_exit); +MODULE_AUTHOR("Michael Petchkovsky"); +MODULE_DESCRIPTION("Epson S1D13806 fb interface for NEC MobilePro900/c"); +MODULE_LICENSE("GPL"); |