aboutsummaryrefslogtreecommitdiff
path: root/drivers/video/w100fb.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/video/w100fb.c
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'drivers/video/w100fb.c')
-rw-r--r--drivers/video/w100fb.c1873
1 files changed, 1873 insertions, 0 deletions
diff --git a/drivers/video/w100fb.c b/drivers/video/w100fb.c
new file mode 100644
index 00000000000..057e154c885
--- /dev/null
+++ b/drivers/video/w100fb.c
@@ -0,0 +1,1873 @@
+/*
+ * linux/drivers/video/w100fb.c
+ *
+ * Frame Buffer Device for ATI Imageon w100 (Wallaby)
+ *
+ * Copyright (C) 2002, ATI Corp.
+ * Copyright (C) 2004-2005 Richard Purdie
+ *
+ * Rewritten for 2.6 by Richard Purdie <rpurdie@rpsys.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/device.h>
+#include <linux/string.h>
+#include <linux/proc_fs.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <video/w100fb.h>
+#include "w100fb.h"
+
+/*
+ * Prototypes
+ */
+static void w100fb_save_buffer(void);
+static void w100fb_clear_buffer(void);
+static void w100fb_restore_buffer(void);
+static void w100fb_clear_screen(u32 mode, long int offset);
+static void w100_resume(void);
+static void w100_suspend(u32 mode);
+static void w100_init_qvga_rotation(u16 deg);
+static void w100_init_vga_rotation(u16 deg);
+static void w100_vsync(void);
+static void w100_init_sharp_lcd(u32 mode);
+static void w100_pwm_setup(void);
+static void w100_InitExtMem(u32 mode);
+static void w100_hw_init(void);
+static u16 w100_set_fastsysclk(u16 Freq);
+
+static void lcdtg_hw_init(u32 mode);
+static void lcdtg_lcd_change(u32 mode);
+static void lcdtg_resume(void);
+static void lcdtg_suspend(void);
+
+
+/* Register offsets & lengths */
+#define REMAPPED_FB_LEN 0x15ffff
+
+#define BITS_PER_PIXEL 16
+
+/* Pseudo palette size */
+#define MAX_PALETTES 16
+
+/* for resolution change */
+#define LCD_MODE_INIT (-1)
+#define LCD_MODE_480 0
+#define LCD_MODE_320 1
+#define LCD_MODE_240 2
+#define LCD_MODE_640 3
+
+#define LCD_SHARP_QVGA 0
+#define LCD_SHARP_VGA 1
+
+#define LCD_MODE_PORTRAIT 0
+#define LCD_MODE_LANDSCAPE 1
+
+#define W100_SUSPEND_EXTMEM 0
+#define W100_SUSPEND_ALL 1
+
+/* General frame buffer data structures */
+struct w100fb_par {
+ u32 xres;
+ u32 yres;
+ int fastsysclk_mode;
+ int lcdMode;
+ int rotation_flag;
+ int blanking_flag;
+ int comadj;
+ int phadadj;
+};
+
+static struct w100fb_par *current_par;
+
+/* Remapped addresses for base cfg, memmapped regs and the frame buffer itself */
+static void *remapped_base;
+static void *remapped_regs;
+static void *remapped_fbuf;
+
+/* External Function */
+static void(*w100fb_ssp_send)(u8 adrs, u8 data);
+
+/*
+ * Sysfs functions
+ */
+
+static ssize_t rotation_show(struct device *dev, char *buf)
+{
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct w100fb_par *par=info->par;
+
+ return sprintf(buf, "%d\n",par->rotation_flag);
+}
+
+static ssize_t rotation_store(struct device *dev, const char *buf, size_t count)
+{
+ unsigned int rotate;
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct w100fb_par *par=info->par;
+
+ rotate = simple_strtoul(buf, NULL, 10);
+
+ if (rotate > 0) par->rotation_flag = 1;
+ else par->rotation_flag = 0;
+
+ if (par->lcdMode == LCD_MODE_320)
+ w100_init_qvga_rotation(par->rotation_flag ? 270 : 90);
+ else if (par->lcdMode == LCD_MODE_240)
+ w100_init_qvga_rotation(par->rotation_flag ? 180 : 0);
+ else if (par->lcdMode == LCD_MODE_640)
+ w100_init_vga_rotation(par->rotation_flag ? 270 : 90);
+ else if (par->lcdMode == LCD_MODE_480)
+ w100_init_vga_rotation(par->rotation_flag ? 180 : 0);
+
+ return count;
+}
+
+static DEVICE_ATTR(rotation, 0644, rotation_show, rotation_store);
+
+static ssize_t w100fb_reg_read(struct device *dev, const char *buf, size_t count)
+{
+ unsigned long param;
+ unsigned long regs;
+ regs = simple_strtoul(buf, NULL, 16);
+ param = readl(remapped_regs + regs);
+ printk("Read Register 0x%08lX: 0x%08lX\n", regs, param);
+ return count;
+}
+
+static DEVICE_ATTR(reg_read, 0200, NULL, w100fb_reg_read);
+
+static ssize_t w100fb_reg_write(struct device *dev, const char *buf, size_t count)
+{
+ unsigned long regs;
+ unsigned long param;
+ sscanf(buf, "%lx %lx", &regs, &param);
+
+ if (regs <= 0x2000) {
+ printk("Write Register 0x%08lX: 0x%08lX\n", regs, param);
+ writel(param, remapped_regs + regs);
+ }
+
+ return count;
+}
+
+static DEVICE_ATTR(reg_write, 0200, NULL, w100fb_reg_write);
+
+
+static ssize_t fastsysclk_show(struct device *dev, char *buf)
+{
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct w100fb_par *par=info->par;
+
+ return sprintf(buf, "%d\n",par->fastsysclk_mode);
+}
+
+static ssize_t fastsysclk_store(struct device *dev, const char *buf, size_t count)
+{
+ int param;
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct w100fb_par *par=info->par;
+
+ param = simple_strtoul(buf, NULL, 10);
+
+ if (param == 75) {
+ printk("Set fastsysclk %d\n", param);
+ par->fastsysclk_mode = param;
+ w100_set_fastsysclk(par->fastsysclk_mode);
+ } else if (param == 100) {
+ printk("Set fastsysclk %d\n", param);
+ par->fastsysclk_mode = param;
+ w100_set_fastsysclk(par->fastsysclk_mode);
+ }
+ return count;
+}
+
+static DEVICE_ATTR(fastsysclk, 0644, fastsysclk_show, fastsysclk_store);
+
+/*
+ * The touchscreen on this device needs certain information
+ * from the video driver to function correctly. We export it here.
+ */
+int w100fb_get_xres(void) {
+ return current_par->xres;
+}
+
+int w100fb_get_blanking(void) {
+ return current_par->blanking_flag;
+}
+
+int w100fb_get_fastsysclk(void) {
+ return current_par->fastsysclk_mode;
+}
+EXPORT_SYMBOL(w100fb_get_xres);
+EXPORT_SYMBOL(w100fb_get_blanking);
+EXPORT_SYMBOL(w100fb_get_fastsysclk);
+
+
+/*
+ * Set a palette value from rgb components
+ */
+static int w100fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+ u_int trans, struct fb_info *info)
+{
+ unsigned int val;
+ int ret = 1;
+
+ /*
+ * If greyscale is true, then we convert the RGB value
+ * to greyscale no matter what visual we are using.
+ */
+ if (info->var.grayscale)
+ red = green = blue = (19595 * red + 38470 * green + 7471 * blue) >> 16;
+
+ /*
+ * 16-bit True Colour. We encode the RGB value
+ * according to the RGB bitfield information.
+ */
+ if (regno < MAX_PALETTES) {
+
+ u32 *pal = info->pseudo_palette;
+
+ val = (red & 0xf800) | ((green & 0xfc00) >> 5) | ((blue & 0xf800) >> 11);
+ pal[regno] = val;
+ ret = 0;
+ }
+ return ret;
+}
+
+
+/*
+ * Blank the display based on value in blank_mode
+ */
+static int w100fb_blank(int blank_mode, struct fb_info *info)
+{
+ struct w100fb_par *par;
+ par=info->par;
+
+ switch(blank_mode) {
+
+ case FB_BLANK_NORMAL: /* Normal blanking */
+ case FB_BLANK_VSYNC_SUSPEND: /* VESA blank (vsync off) */
+ case FB_BLANK_HSYNC_SUSPEND: /* VESA blank (hsync off) */
+ case FB_BLANK_POWERDOWN: /* Poweroff */
+ if (par->blanking_flag == 0) {
+ w100fb_save_buffer();
+ lcdtg_suspend();
+ par->blanking_flag = 1;
+ }
+ break;
+
+ case FB_BLANK_UNBLANK: /* Unblanking */
+ if (par->blanking_flag != 0) {
+ w100fb_restore_buffer();
+ lcdtg_resume();
+ par->blanking_flag = 0;
+ }
+ break;
+ }
+ return 0;
+}
+
+/*
+ * Change the resolution by calling the appropriate hardware functions
+ */
+static void w100fb_changeres(int rotate_mode, u32 mode)
+{
+ u16 rotation=0;
+
+ switch(rotate_mode) {
+ case LCD_MODE_LANDSCAPE:
+ rotation=(current_par->rotation_flag ? 270 : 90);
+ break;
+ case LCD_MODE_PORTRAIT:
+ rotation=(current_par->rotation_flag ? 180 : 0);
+ break;
+ }
+
+ w100_pwm_setup();
+ switch(mode) {
+ case LCD_SHARP_QVGA:
+ w100_vsync();
+ w100_suspend(W100_SUSPEND_EXTMEM);
+ w100_init_sharp_lcd(LCD_SHARP_QVGA);
+ w100_init_qvga_rotation(rotation);
+ w100_InitExtMem(LCD_SHARP_QVGA);
+ w100fb_clear_screen(LCD_SHARP_QVGA, 0);
+ lcdtg_lcd_change(LCD_SHARP_QVGA);
+ break;
+ case LCD_SHARP_VGA:
+ w100fb_clear_screen(LCD_SHARP_QVGA, 0);
+ writel(0xBFFFA000, remapped_regs + mmMC_EXT_MEM_LOCATION);
+ w100_InitExtMem(LCD_SHARP_VGA);
+ w100fb_clear_screen(LCD_SHARP_VGA, 0x200000);
+ w100_vsync();
+ w100_init_sharp_lcd(LCD_SHARP_VGA);
+ if (rotation != 0)
+ w100_init_vga_rotation(rotation);
+ lcdtg_lcd_change(LCD_SHARP_VGA);
+ break;
+ }
+}
+
+/*
+ * Set up the display for the fb subsystem
+ */
+static void w100fb_activate_var(struct fb_info *info)
+{
+ u32 temp32;
+ struct w100fb_par *par=info->par;
+ struct fb_var_screeninfo *var = &info->var;
+
+ /* Set the hardware to 565 */
+ temp32 = readl(remapped_regs + mmDISP_DEBUG2);
+ temp32 &= 0xff7fffff;
+ temp32 |= 0x00800000;
+ writel(temp32, remapped_regs + mmDISP_DEBUG2);
+
+ if (par->lcdMode == LCD_MODE_INIT) {
+ w100_init_sharp_lcd(LCD_SHARP_VGA);
+ w100_init_vga_rotation(par->rotation_flag ? 270 : 90);
+ par->lcdMode = LCD_MODE_640;
+ lcdtg_hw_init(LCD_SHARP_VGA);
+ } else if (var->xres == 320 && var->yres == 240) {
+ if (par->lcdMode != LCD_MODE_320) {
+ w100fb_changeres(LCD_MODE_LANDSCAPE, LCD_SHARP_QVGA);
+ par->lcdMode = LCD_MODE_320;
+ }
+ } else if (var->xres == 240 && var->yres == 320) {
+ if (par->lcdMode != LCD_MODE_240) {
+ w100fb_changeres(LCD_MODE_PORTRAIT, LCD_SHARP_QVGA);
+ par->lcdMode = LCD_MODE_240;
+ }
+ } else if (var->xres == 640 && var->yres == 480) {
+ if (par->lcdMode != LCD_MODE_640) {
+ w100fb_changeres(LCD_MODE_LANDSCAPE, LCD_SHARP_VGA);
+ par->lcdMode = LCD_MODE_640;
+ }
+ } else if (var->xres == 480 && var->yres == 640) {
+ if (par->lcdMode != LCD_MODE_480) {
+ w100fb_changeres(LCD_MODE_PORTRAIT, LCD_SHARP_VGA);
+ par->lcdMode = LCD_MODE_480;
+ }
+ } else printk(KERN_ERR "W100FB: Resolution error!\n");
+}
+
+
+/*
+ * w100fb_check_var():
+ * Get the video params out of 'var'. If a value doesn't fit, round it up,
+ * if it's too big, return -EINVAL.
+ *
+ */
+static int w100fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ if (var->xres < var->yres) { /* Portrait mode */
+ if ((var->xres > 480) || (var->yres > 640)) {
+ return -EINVAL;
+ } else if ((var->xres > 240) || (var->yres > 320)) {
+ var->xres = 480;
+ var->yres = 640;
+ } else {
+ var->xres = 240;
+ var->yres = 320;
+ }
+ } else { /* Landscape mode */
+ if ((var->xres > 640) || (var->yres > 480)) {
+ return -EINVAL;
+ } else if ((var->xres > 320) || (var->yres > 240)) {
+ var->xres = 640;
+ var->yres = 480;
+ } else {
+ var->xres = 320;
+ var->yres = 240;
+ }
+ }
+
+ var->xres_virtual = max(var->xres_virtual, var->xres);
+ var->yres_virtual = max(var->yres_virtual, var->yres);
+
+ if (var->bits_per_pixel > BITS_PER_PIXEL)
+ return -EINVAL;
+ else
+ var->bits_per_pixel = BITS_PER_PIXEL;
+
+ var->red.offset = 11;
+ var->red.length = 5;
+ var->green.offset = 5;
+ var->green.length = 6;
+ var->blue.offset = 0;
+ var->blue.length = 5;
+ var->transp.offset = var->transp.length = 0;
+
+ var->nonstd = 0;
+
+ var->height = -1;
+ var->width = -1;
+ var->vmode = FB_VMODE_NONINTERLACED;
+
+ var->sync = 0;
+ var->pixclock = 0x04; /* 171521; */
+
+ return 0;
+}
+
+
+/*
+ * w100fb_set_par():
+ * Set the user defined part of the display for the specified console
+ * by looking at the values in info.var
+ */
+static int w100fb_set_par(struct fb_info *info)
+{
+ struct w100fb_par *par=info->par;
+
+ par->xres = info->var.xres;
+ par->yres = info->var.yres;
+
+ info->fix.visual = FB_VISUAL_TRUECOLOR;
+
+ info->fix.ypanstep = 0;
+ info->fix.ywrapstep = 0;
+
+ if (par->blanking_flag)
+ w100fb_clear_buffer();
+
+ w100fb_activate_var(info);
+
+ if (par->lcdMode == LCD_MODE_480) {
+ info->fix.line_length = (480 * BITS_PER_PIXEL) / 8;
+ info->fix.smem_len = 0x200000;
+ } else if (par->lcdMode == LCD_MODE_320) {
+ info->fix.line_length = (320 * BITS_PER_PIXEL) / 8;
+ info->fix.smem_len = 0x60000;
+ } else if (par->lcdMode == LCD_MODE_240) {
+ info->fix.line_length = (240 * BITS_PER_PIXEL) / 8;
+ info->fix.smem_len = 0x60000;
+ } else if (par->lcdMode == LCD_MODE_INIT || par->lcdMode == LCD_MODE_640) {
+ info->fix.line_length = (640 * BITS_PER_PIXEL) / 8;
+ info->fix.smem_len = 0x200000;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Frame buffer operations
+ */
+static struct fb_ops w100fb_ops = {
+ .owner = THIS_MODULE,
+ .fb_check_var = w100fb_check_var,
+ .fb_set_par = w100fb_set_par,
+ .fb_setcolreg = w100fb_setcolreg,
+ .fb_blank = w100fb_blank,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+ .fb_cursor = soft_cursor,
+};
+
+
+static void w100fb_clear_screen(u32 mode, long int offset)
+{
+ int i, numPix = 0;
+
+ if (mode == LCD_SHARP_VGA)
+ numPix = 640 * 480;
+ else if (mode == LCD_SHARP_QVGA)
+ numPix = 320 * 240;
+
+ for (i = 0; i < numPix; i++)
+ writew(0xffff, remapped_fbuf + offset + (2*i));
+}
+
+
+/* Need to split up the buffers to stay within the limits of kmalloc */
+#define W100_BUF_NUM 6
+static uint32_t *gSaveImagePtr[W100_BUF_NUM] = { NULL };
+
+static void w100fb_save_buffer(void)
+{
+ int i, j, bufsize;
+
+ bufsize=(current_par->xres * current_par->yres * BITS_PER_PIXEL / 8) / W100_BUF_NUM;
+ for (i = 0; i < W100_BUF_NUM; i++) {
+ if (gSaveImagePtr[i] == NULL)
+ gSaveImagePtr[i] = kmalloc(bufsize, GFP_KERNEL);
+ if (gSaveImagePtr[i] == NULL) {
+ w100fb_clear_buffer();
+ printk(KERN_WARNING "can't alloc pre-off image buffer %d\n", i);
+ break;
+ }
+ for (j = 0; j < bufsize/4; j++)
+ *(gSaveImagePtr[i] + j) = readl(remapped_fbuf + (bufsize*i) + j*4);
+ }
+}
+
+
+static void w100fb_restore_buffer(void)
+{
+ int i, j, bufsize;
+
+ bufsize=(current_par->xres * current_par->yres * BITS_PER_PIXEL / 8) / W100_BUF_NUM;
+ for (i = 0; i < W100_BUF_NUM; i++) {
+ if (gSaveImagePtr[i] == NULL) {
+ printk(KERN_WARNING "can't find pre-off image buffer %d\n", i);
+ w100fb_clear_buffer();
+ break;
+ }
+ for (j = 0; j < (bufsize/4); j++)
+ writel(*(gSaveImagePtr[i] + j),remapped_fbuf + (bufsize*i) + (j*4));
+ kfree(gSaveImagePtr[i]);
+ gSaveImagePtr[i] = NULL;
+ }
+}
+
+
+static void w100fb_clear_buffer(void)
+{
+ int i;
+ for (i = 0; i < W100_BUF_NUM; i++) {
+ kfree(gSaveImagePtr[i]);
+ gSaveImagePtr[i] = NULL;
+ }
+}
+
+
+#ifdef CONFIG_PM
+static int w100fb_suspend(struct device *dev, u32 state, u32 level)
+{
+ if (level == SUSPEND_POWER_DOWN) {
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct w100fb_par *par=info->par;
+
+ w100fb_save_buffer();
+ lcdtg_suspend();
+ w100_suspend(W100_SUSPEND_ALL);
+ par->blanking_flag = 1;
+ }
+ return 0;
+}
+
+static int w100fb_resume(struct device *dev, u32 level)
+{
+ if (level == RESUME_POWER_ON) {
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct w100fb_par *par=info->par;
+
+ w100_resume();
+ w100fb_restore_buffer();
+ lcdtg_resume();
+ par->blanking_flag = 0;
+ }
+ return 0;
+}
+#else
+#define w100fb_suspend NULL
+#define w100fb_resume NULL
+#endif
+
+
+int __init w100fb_probe(struct device *dev)
+{
+ struct w100fb_mach_info *inf;
+ struct fb_info *info;
+ struct w100fb_par *par;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct resource *mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if (!mem)
+ return -EINVAL;
+
+ /* remap the areas we're going to use */
+ remapped_base = ioremap_nocache(mem->start+W100_CFG_BASE, W100_CFG_LEN);
+ if (remapped_base == NULL)
+ return -EIO;
+
+ remapped_regs = ioremap_nocache(mem->start+W100_REG_BASE, W100_REG_LEN);
+ if (remapped_regs == NULL) {
+ iounmap(remapped_base);
+ return -EIO;
+ }
+
+ remapped_fbuf = ioremap_nocache(mem->start+MEM_EXT_BASE_VALUE, REMAPPED_FB_LEN);
+ if (remapped_fbuf == NULL) {
+ iounmap(remapped_base);
+ iounmap(remapped_regs);
+ return -EIO;
+ }
+
+ info=framebuffer_alloc(sizeof(struct w100fb_par), dev);
+ if (!info) {
+ iounmap(remapped_base);
+ iounmap(remapped_regs);
+ iounmap(remapped_fbuf);
+ return -ENOMEM;
+ }
+
+ info->device=dev;
+ par = info->par;
+ current_par=info->par;
+ dev_set_drvdata(dev, info);
+
+ inf = dev->platform_data;
+ par->phadadj = inf->phadadj;
+ par->comadj = inf->comadj;
+ par->fastsysclk_mode = 75;
+ par->lcdMode = LCD_MODE_INIT;
+ par->rotation_flag=0;
+ par->blanking_flag=0;
+ w100fb_ssp_send = inf->w100fb_ssp_send;
+
+ w100_hw_init();
+ w100_pwm_setup();
+
+ info->pseudo_palette = kmalloc(sizeof (u32) * MAX_PALETTES, GFP_KERNEL);
+ if (!info->pseudo_palette) {
+ iounmap(remapped_base);
+ iounmap(remapped_regs);
+ iounmap(remapped_fbuf);
+ return -ENOMEM;
+ }
+
+ info->fbops = &w100fb_ops;
+ info->flags = FBINFO_DEFAULT;
+ info->node = -1;
+ info->screen_base = remapped_fbuf;
+ info->screen_size = REMAPPED_FB_LEN;
+
+ info->var.xres = 640;
+ info->var.xres_virtual = info->var.xres;
+ info->var.yres = 480;
+ info->var.yres_virtual = info->var.yres;
+ info->var.pixclock = 0x04; /* 171521; */
+ info->var.sync = 0;
+ info->var.grayscale = 0;
+ info->var.xoffset = info->var.yoffset = 0;
+ info->var.accel_flags = 0;
+ info->var.activate = FB_ACTIVATE_NOW;
+
+ strcpy(info->fix.id, "w100fb");
+ info->fix.type = FB_TYPE_PACKED_PIXELS;
+ info->fix.type_aux = 0;
+ info->fix.accel = FB_ACCEL_NONE;
+ info->fix.smem_start = mem->start+MEM_EXT_BASE_VALUE;
+ info->fix.mmio_start = mem->start+W100_REG_BASE;
+ info->fix.mmio_len = W100_REG_LEN;
+
+ w100fb_check_var(&info->var, info);
+ w100fb_set_par(info);
+
+ if (register_framebuffer(info) < 0) {
+ kfree(info->pseudo_palette);
+ iounmap(remapped_base);
+ iounmap(remapped_regs);
+ iounmap(remapped_fbuf);
+ return -EINVAL;
+ }
+
+ device_create_file(dev, &dev_attr_fastsysclk);
+ device_create_file(dev, &dev_attr_reg_read);
+ device_create_file(dev, &dev_attr_reg_write);
+ device_create_file(dev, &dev_attr_rotation);
+
+ printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node, info->fix.id);
+ return 0;
+}
+
+
+static int w100fb_remove(struct device *dev)
+{
+ struct fb_info *info = dev_get_drvdata(dev);
+
+ device_remove_file(dev, &dev_attr_fastsysclk);
+ device_remove_file(dev, &dev_attr_reg_read);
+ device_remove_file(dev, &dev_attr_reg_write);
+ device_remove_file(dev, &dev_attr_rotation);
+
+ unregister_framebuffer(info);
+
+ w100fb_clear_buffer();
+ kfree(info->pseudo_palette);
+
+ iounmap(remapped_base);
+ iounmap(remapped_regs);
+ iounmap(remapped_fbuf);
+
+ framebuffer_release(info);
+
+ return 0;
+}
+
+
+/* ------------------- chipset specific functions -------------------------- */
+
+
+static void w100_soft_reset(void)
+{
+ u16 val = readw((u16 *) remapped_base + cfgSTATUS);
+ writew(val | 0x08, (u16 *) remapped_base + cfgSTATUS);
+ udelay(100);
+ writew(0x00, (u16 *) remapped_base + cfgSTATUS);
+ udelay(100);
+}
+
+/*
+ * Initialization of critical w100 hardware
+ */
+static void w100_hw_init(void)
+{
+ u32 temp32;
+ union cif_cntl_u cif_cntl;
+ union intf_cntl_u intf_cntl;
+ union cfgreg_base_u cfgreg_base;
+ union wrap_top_dir_u wrap_top_dir;
+ union cif_read_dbg_u cif_read_dbg;
+ union cpu_defaults_u cpu_default;
+ union cif_write_dbg_u cif_write_dbg;
+ union wrap_start_dir_u wrap_start_dir;
+ union mc_ext_mem_location_u mc_ext_mem_loc;
+ union cif_io_u cif_io;
+
+ w100_soft_reset();
+
+ /* This is what the fpga_init code does on reset. May be wrong
+ but there is little info available */
+ writel(0x31, remapped_regs + mmSCRATCH_UMSK);
+ for (temp32 = 0; temp32 < 10000; temp32++)
+ readl(remapped_regs + mmSCRATCH_UMSK);
+ writel(0x30, remapped_regs + mmSCRATCH_UMSK);
+
+ /* Set up CIF */
+ cif_io.val = defCIF_IO;
+ writel((u32)(cif_io.val), remapped_regs + mmCIF_IO);
+
+ cif_write_dbg.val = readl(remapped_regs + mmCIF_WRITE_DBG);
+ cif_write_dbg.f.dis_packer_ful_during_rbbm_timeout = 0;
+ cif_write_dbg.f.en_dword_split_to_rbbm = 1;
+ cif_write_dbg.f.dis_timeout_during_rbbm = 1;
+ writel((u32) (cif_write_dbg.val), remapped_regs + mmCIF_WRITE_DBG);
+
+ cif_read_dbg.val = readl(remapped_regs + mmCIF_READ_DBG);
+ cif_read_dbg.f.dis_rd_same_byte_to_trig_fetch = 1;
+ writel((u32) (cif_read_dbg.val), remapped_regs + mmCIF_READ_DBG);
+
+ cif_cntl.val = readl(remapped_regs + mmCIF_CNTL);
+ cif_cntl.f.dis_system_bits = 1;
+ cif_cntl.f.dis_mr = 1;
+ cif_cntl.f.en_wait_to_compensate_dq_prop_dly = 0;
+ cif_cntl.f.intb_oe = 1;
+ cif_cntl.f.interrupt_active_high = 1;
+ writel((u32) (cif_cntl.val), remapped_regs + mmCIF_CNTL);
+
+ /* Setup cfgINTF_CNTL and cfgCPU defaults */
+ intf_cntl.val = defINTF_CNTL;
+ intf_cntl.f.ad_inc_a = 1;
+ intf_cntl.f.ad_inc_b = 1;
+ intf_cntl.f.rd_data_rdy_a = 0;
+ intf_cntl.f.rd_data_rdy_b = 0;
+ writeb((u8) (intf_cntl.val), remapped_base + cfgINTF_CNTL);
+
+ cpu_default.val = defCPU_DEFAULTS;
+ cpu_default.f.access_ind_addr_a = 1;
+ cpu_default.f.access_ind_addr_b = 1;
+ cpu_default.f.access_scratch_reg = 1;
+ cpu_default.f.transition_size = 0;
+ writeb((u8) (cpu_default.val), remapped_base + cfgCPU_DEFAULTS);
+
+ /* set up the apertures */
+ writeb((u8) (W100_REG_BASE >> 16), remapped_base + cfgREG_BASE);
+
+ cfgreg_base.val = defCFGREG_BASE;
+ cfgreg_base.f.cfgreg_base = W100_CFG_BASE;
+ writel((u32) (cfgreg_base.val), remapped_regs + mmCFGREG_BASE);
+
+ /* This location is relative to internal w100 addresses */
+ writel(0x15FF1000, remapped_regs + mmMC_FB_LOCATION);
+
+ mc_ext_mem_loc.val = defMC_EXT_MEM_LOCATION;
+ mc_ext_mem_loc.f.mc_ext_mem_start = MEM_EXT_BASE_VALUE >> 8;
+ mc_ext_mem_loc.f.mc_ext_mem_top = MEM_EXT_TOP_VALUE >> 8;
+ writel((u32) (mc_ext_mem_loc.val), remapped_regs + mmMC_EXT_MEM_LOCATION);
+
+ if ((current_par->lcdMode == LCD_MODE_240) || (current_par->lcdMode == LCD_MODE_320))
+ w100_InitExtMem(LCD_SHARP_QVGA);
+ else
+ w100_InitExtMem(LCD_SHARP_VGA);
+
+ wrap_start_dir.val = defWRAP_START_DIR;
+ wrap_start_dir.f.start_addr = WRAP_BUF_BASE_VALUE >> 1;
+ writel((u32) (wrap_start_dir.val), remapped_regs + mmWRAP_START_DIR);
+
+ wrap_top_dir.val = defWRAP_TOP_DIR;
+ wrap_top_dir.f.top_addr = WRAP_BUF_TOP_VALUE >> 1;
+ writel((u32) (wrap_top_dir.val), remapped_regs + mmWRAP_TOP_DIR);
+
+ writel((u32) 0x2440, remapped_regs + mmRBBM_CNTL);
+}
+
+
+/*
+ * Types
+ */
+
+struct pll_parm {
+ u16 freq; /* desired Fout for PLL */
+ u8 M;
+ u8 N_int;
+ u8 N_fac;
+ u8 tfgoal;
+ u8 lock_time;
+};
+
+struct power_state {
+ union clk_pin_cntl_u clk_pin_cntl;
+ union pll_ref_fb_div_u pll_ref_fb_div;
+ union pll_cntl_u pll_cntl;
+ union sclk_cntl_u sclk_cntl;
+ union pclk_cntl_u pclk_cntl;
+ union clk_test_cntl_u clk_test_cntl;
+ union pwrmgt_cntl_u pwrmgt_cntl;
+ u32 freq; /* Fout for PLL calibration */
+ u8 tf100; /* for pll calibration */
+ u8 tf80; /* for pll calibration */
+ u8 tf20; /* for pll calibration */
+ u8 M; /* for pll calibration */
+ u8 N_int; /* for pll calibration */
+ u8 N_fac; /* for pll calibration */
+ u8 lock_time; /* for pll calibration */
+ u8 tfgoal; /* for pll calibration */
+ u8 auto_mode; /* hardware auto switch? */
+ u8 pwm_mode; /* 0 fast, 1 normal/slow */
+ u16 fast_sclk; /* fast clk freq */
+ u16 norm_sclk; /* slow clk freq */
+};
+
+
+/*
+ * Global state variables
+ */
+
+static struct power_state w100_pwr_state;
+
+/* This table is specific for 12.5MHz ref crystal. */
+static struct pll_parm gPLLTable[] = {
+ /*freq M N_int N_fac tfgoal lock_time */
+ { 50, 0, 1, 0, 0xE0, 56}, /* 50.00 MHz */
+ { 75, 0, 5, 0, 0xDE, 37}, /* 75.00 MHz */
+ {100, 0, 7, 0, 0xE0, 28}, /* 100.00 MHz */
+ {125, 0, 9, 0, 0xE0, 22}, /* 125.00 MHz */
+ {150, 0, 11, 0, 0xE0, 17}, /* 150.00 MHz */
+ { 0, 0, 0, 0, 0, 0} /* Terminator */
+};
+
+
+static u8 w100_pll_get_testcount(u8 testclk_sel)
+{
+ udelay(5);
+
+ w100_pwr_state.clk_test_cntl.f.start_check_freq = 0x0;
+ w100_pwr_state.clk_test_cntl.f.testclk_sel = testclk_sel;
+ w100_pwr_state.clk_test_cntl.f.tstcount_rst = 0x1; /*reset test count */
+ writel((u32) (w100_pwr_state.clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);
+ w100_pwr_state.clk_test_cntl.f.tstcount_rst = 0x0;
+ writel((u32) (w100_pwr_state.clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);
+
+ w100_pwr_state.clk_test_cntl.f.start_check_freq = 0x1;
+ writel((u32) (w100_pwr_state.clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);
+
+ udelay(20);
+
+ w100_pwr_state.clk_test_cntl.val = readl(remapped_regs + mmCLK_TEST_CNTL);
+ w100_pwr_state.clk_test_cntl.f.start_check_freq = 0x0;
+ writel((u32) (w100_pwr_state.clk_test_cntl.val), remapped_regs + mmCLK_TEST_CNTL);
+
+ return w100_pwr_state.clk_test_cntl.f.test_count;
+}
+
+
+static u8 w100_pll_adjust(void)
+{
+ do {
+ /* Wai Ming 80 percent of VDD 1.3V gives 1.04V, minimum operating voltage is 1.08V
+ * therefore, commented out the following lines
+ * tf80 meant tf100
+ * set VCO input = 0.8 * VDD
+ */
+ w100_pwr_state.pll_cntl.f.pll_dactal = 0xd;
+ writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
+
+ w100_pwr_state.tf80 = w100_pll_get_testcount(0x1); /* PLLCLK */
+ if (w100_pwr_state.tf80 >= (w100_pwr_state.tfgoal)) {
+ /* set VCO input = 0.2 * VDD */
+ w100_pwr_state.pll_cntl.f.pll_dactal = 0x7;
+ writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
+
+ w100_pwr_state.tf20 = w100_pll_get_testcount(0x1); /* PLLCLK */
+ if (w100_pwr_state.tf20 <= (w100_pwr_state.tfgoal))
+ return 1; // Success
+
+ if ((w100_pwr_state.pll_cntl.f.pll_vcofr == 0x0) &&
+ ((w100_pwr_state.pll_cntl.f.pll_pvg == 0x7) ||
+ (w100_pwr_state.pll_cntl.f.pll_ioffset == 0x0))) {
+ /* slow VCO config */
+ w100_pwr_state.pll_cntl.f.pll_vcofr = 0x1;
+ w100_pwr_state.pll_cntl.f.pll_pvg = 0x0;
+ w100_pwr_state.pll_cntl.f.pll_ioffset = 0x0;
+ writel((u32) (w100_pwr_state.pll_cntl.val),
+ remapped_regs + mmPLL_CNTL);
+ continue;
+ }
+ }
+ if ((w100_pwr_state.pll_cntl.f.pll_ioffset) < 0x3) {
+ w100_pwr_state.pll_cntl.f.pll_ioffset += 0x1;
+ writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
+ continue;
+ }
+ if ((w100_pwr_state.pll_cntl.f.pll_pvg) < 0x7) {
+ w100_pwr_state.pll_cntl.f.pll_ioffset = 0x0;
+ w100_pwr_state.pll_cntl.f.pll_pvg += 0x1;
+ writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
+ continue;
+ }
+ return 0; // error
+ } while(1);
+}
+
+
+/*
+ * w100_pll_calibration
+ * freq = target frequency of the PLL
+ * (note: crystal = 14.3MHz)
+ */
+static u8 w100_pll_calibration(u32 freq)
+{
+ u8 status;
+
+ /* initial setting */
+ w100_pwr_state.pll_cntl.f.pll_pwdn = 0x0; /* power down */
+ w100_pwr_state.pll_cntl.f.pll_reset = 0x0; /* not reset */
+ w100_pwr_state.pll_cntl.f.pll_tcpoff = 0x1; /* Hi-Z */
+ w100_pwr_state.pll_cntl.f.pll_pvg = 0x0; /* VCO gain = 0 */
+ w100_pwr_state.pll_cntl.f.pll_vcofr = 0x0; /* VCO frequency range control = off */
+ w100_pwr_state.pll_cntl.f.pll_ioffset = 0x0; /* current offset inside VCO = 0 */
+ w100_pwr_state.pll_cntl.f.pll_ring_off = 0x0;
+ writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
+
+ /* check for (tf80 >= tfgoal) && (tf20 =< tfgoal) */
+ if ((w100_pwr_state.tf80 < w100_pwr_state.tfgoal) || (w100_pwr_state.tf20 > w100_pwr_state.tfgoal)) {
+ status=w100_pll_adjust();
+ }
+ /* PLL Reset And Lock */
+
+ /* set VCO input = 0.5 * VDD */
+ w100_pwr_state.pll_cntl.f.pll_dactal = 0xa;
+ writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
+
+ /* reset time */
+ udelay(1);
+
+ /* enable charge pump */
+ w100_pwr_state.pll_cntl.f.pll_tcpoff = 0x0; /* normal */
+ writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
+
+ /* set VCO input = Hi-Z */
+ /* disable DAC */
+ w100_pwr_state.pll_cntl.f.pll_dactal = 0x0;
+ writel((u32) (w100_pwr_state.pll_cntl.val), remapped_regs + mmPLL_CNTL);
+
+ /* lock time */
+ udelay(400); /* delay 400 us */
+
+ /* PLL locked */
+
+ w100_pwr_state.sclk_cntl.f.sclk_src_sel = 0x1; /* PLL clock */
+ writel((u32) (w100_pwr_state.sclk_cntl.val), remapped_regs + mmSCLK_CNTL);
+
+ w100_pwr_state.tf100 = w100_pll_get_testcount(0x1); /* PLLCLK */
+
+ return status;
+}
+
+
+static u8 w100_pll_set_clk(void)
+{
+ u8 status;
+
+ if (w100_pwr_state.auto_mode == 1) /* auto mode */
+ {
+ w100_pwr_state.pwrmgt_cntl.f.pwm_fast_noml_hw_en = 0x0; /* disable fast to normal */
+ w100_pwr_state.pwrmgt_cntl.f.pwm_noml_fast_hw_en = 0x0; /* disable normal to fast */
+ writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);
+ }
+
+ w100_pwr_state.sclk_cntl.f.sclk_src_sel = 0x0; /* crystal clock */
+ writel((u32) (w100_pwr_state.sclk_cntl.val), remapped_regs + mmSCLK_CNTL);
+
+ w100_pwr_state.pll_ref_fb_div.f.pll_ref_div = w100_pwr_state.M;
+ w100_pwr_state.pll_ref_fb_div.f.pll_fb_div_int = w100_pwr_state.N_int;
+ w100_pwr_state.pll_ref_fb_div.f.pll_fb_div_frac = w100_pwr_state.N_fac;
+ w100_pwr_state.pll_ref_fb_div.f.pll_lock_time = w100_pwr_state.lock_time;
+ writel((u32) (w100_pwr_state.pll_ref_fb_div.val), remapped_regs + mmPLL_REF_FB_DIV);
+
+ w100_pwr_state.pwrmgt_cntl.f.pwm_mode_req = 0;
+ writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);
+
+ status = w100_pll_calibration (w100_pwr_state.freq);
+
+ if (w100_pwr_state.auto_mode == 1) /* auto mode */
+ {
+ w100_pwr_state.pwrmgt_cntl.f.pwm_fast_noml_hw_en = 0x1; /* reenable fast to normal */
+ w100_pwr_state.pwrmgt_cntl.f.pwm_noml_fast_hw_en = 0x1; /* reenable normal to fast */
+ writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);
+ }
+ return status;
+}
+
+
+/* assume reference crystal clk is 12.5MHz,
+ * and that doubling is not enabled.
+ *
+ * Freq = 12 == 12.5MHz.
+ */
+static u16 w100_set_slowsysclk(u16 freq)
+{
+ if (w100_pwr_state.norm_sclk == freq)
+ return freq;
+
+ if (w100_pwr_state.auto_mode == 1) /* auto mode */
+ return 0;
+
+ if (freq == 12) {
+ w100_pwr_state.norm_sclk = freq;
+ w100_pwr_state.sclk_cntl.f.sclk_post_div_slow = 0x0; /* Pslow = 1 */
+ w100_pwr_state.sclk_cntl.f.sclk_src_sel = 0x0; /* crystal src */
+
+ writel((u32) (w100_pwr_state.sclk_cntl.val), remapped_regs + mmSCLK_CNTL);
+
+ w100_pwr_state.clk_pin_cntl.f.xtalin_pm_en = 0x1;
+ writel((u32) (w100_pwr_state.clk_pin_cntl.val), remapped_regs + mmCLK_PIN_CNTL);
+
+ w100_pwr_state.pwrmgt_cntl.f.pwm_enable = 0x1;
+ w100_pwr_state.pwrmgt_cntl.f.pwm_mode_req = 0x1;
+ writel((u32) (w100_pwr_state.pwrmgt_cntl.val), remapped_regs + mmPWRMGT_CNTL);
+ w100_pwr_state.pwm_mode = 1; /* normal mode */
+ return freq;
+ } else
+ return 0;
+}
+
+
+static u16 w100_set_fastsysclk(u16 freq)
+{
+ u16 pll_freq;
+ int i;
+
+ while(1) {
+ pll_freq = (u16) (freq * (w100_pwr_state.sclk_cntl.f.sclk_post_div_fast + 1));
+ i = 0;
+ do {
+ if (pll_freq == gPLLTable[i].freq) {
+ w100_pwr_state.freq = gPLLTable[i].freq * 1000000;
+ w100_pwr_state.M = gPLLTable[i].M;
+ w100_pwr_state.N_int = gPLLTable[i].N_int;
+ w100_pwr_state.N_fac = gPLLTable[i].N_fac;
+ w100_pwr_state.tfgoal = gPLLTable[i].tfgoal;
+ w100_pwr_state.lock_time = gPLLTable[i].lock_time;
+ w100_pwr_state.tf20 = 0xff; /* set highest */
+ w100_pwr_state.tf80 = 0x00; /* set lowest */
+
+ w100_pll_set_clk();
+ w100_pwr_state.pwm_mode = 0; /* fast mode */
+ w100_pwr_state.fast_sclk = freq;
+ return freq;
+ }
+ i++;
+ } while(gPLLTable[i].freq);
+
+ if (w100_pwr_state.auto_mode == 1)
+ break;
+
+ if (w100_pwr_state.sclk_cntl.f.sclk_post_div_fast == 0)
+ break;
+
+ w100_pwr_state.sclk_cntl.f.sclk_post_div_fast -= 1;
+ writel((u32) (w100_pwr_state.sclk_cntl.val), remapped_regs + mmSCLK_CNTL);
+ }
+ return 0;
+}
+
+
+/* Set up an initial state. Some values/fields set
+ here will be overwritten. */
+static void w100_pwm_setup(void)
+{
+ w100_pwr_state.clk_pin_cntl.f.osc_en = 0x1;
+ w100_pwr_state.clk_pin_cntl.f.osc_gain = 0x1f;
+ w100_pwr_state.clk_pin_cntl.f.dont_use_xtalin = 0x0;
+ w100_pwr_state.clk_pin_cntl.f.xtalin_pm_en = 0x0;
+ w100_pwr_state.clk_pin_cntl.f.xtalin_dbl_en = 0x0; /* no freq doubling */
+ w100_pwr_state.clk_pin_cntl.f.cg_debug = 0x0;
+ writel((u32) (w100_pwr_state.clk_pin_cntl.val), remapped_regs + mmCLK_PIN_CNTL);
+
+ w100_pwr_state.sclk_cntl.f.sclk_src_sel = 0x0; /* Crystal Clk */
+ w100_pwr_state.sclk_cntl.f.sclk_post_div_fast = 0x0; /* Pfast = 1 */
+ w100_pwr_state.sclk_cntl.f.sclk_clkon_hys = 0x3;
+ w100_pwr_state.sclk_cntl.f.sclk_post_div_slow = 0x0; /* Pslow = 1 */
+ w100_pwr_state.sclk_cntl.f.disp_cg_ok2switch_en = 0x0;
+ w100_pwr_state.sclk_cntl.f.sclk_force_reg = 0x0; /* Dynamic */
+ w100_pwr_state.sclk_cntl.f.sclk_force_disp = 0x0; /* Dynamic */
+ w100_pwr_state.sclk_cntl.f.sclk_force_mc = 0x0; /* Dynamic */
+ w100_pwr_state.sclk_cntl.f.sclk_force_extmc = 0x0; /* Dynamic */
+ w100_pwr_state.sclk_cntl.f.sclk_force_cp = 0x0; /* Dynamic */
+ w100_pwr_state.sclk_cntl.f.sclk_force_e2 = 0x0; /* Dynamic */
+ w100_pwr_state.sclk_cntl.f.sclk_force_e3 = 0x0;