diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /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.c | 1873 |
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", ®s, ¶m); + + 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; |