aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/s1d13mp900fb.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video/s1d13mp900fb.c')
-rw-r--r--drivers/video/s1d13mp900fb.c282
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");