- include/linux/glamofb.h (struct glamofb_platform_data): add fields for timing data - drivers/video/glamo/glamo-fb.c: move modeline setting to a function of itself. Call this function on initialization and set_par. - drivers/video/glamo/glamo-fb.c (glamofb_cmd_mode): glamo fails to return to display mode if we switch to and back from cmd mode too quickly. Add a certain amount of delay after entering cmd mode. - drivers/video/glamo/glamo-core.c (glamo_engine_reset, glamo_pll_rate, glamo_engine_reclock), drivers/video/glamo/glamo-core.h (glamo_engine_reclock), drivers/video/glamo/glamo-fb.c (glamofb_activate_var): adjust DCLK ratio to match the desired pixclock. Signed-off-by: Chia-I Wu Acked-by: Werner Almesberger Index: linux-2.6.22.5/include/linux/glamofb.h =================================================================== --- linux-2.6.22.5.orig/include/linux/glamofb.h +++ linux-2.6.22.5/include/linux/glamofb.h @@ -13,6 +13,11 @@ struct glamofb_platform_data { int width, height; + int pixclock; + int left_margin, right_margin; + int upper_margin, lower_margin; + int hsync_len, vsync_len; + int fb_mem_size; struct glamofb_val xres; struct glamofb_val yres; Index: linux-2.6.22.5/drivers/video/glamo/glamo-fb.c =================================================================== --- linux-2.6.22.5.orig/drivers/video/glamo/glamo-fb.c +++ linux-2.6.22.5/drivers/video/glamo/glamo-fb.c @@ -52,9 +52,18 @@ #include "glamo-regs.h" #include "glamo-core.h" -#define RESSIZE(ressource) (((ressource)->end - (ressource)->start)+1) +#ifdef DEBUG +#define GLAMO_LOG(...) +#else +#define GLAMO_LOG(...) \ +do { \ + printk(KERN_DEBUG "in %s:%s:%d", __FILE__, __func__, __LINE__); \ + printk(KERN_DEBUG __VA_ARGS__); \ +} while (0); +#endif -#define GLAMO_FB_ALLOC (640*480*2) + +#define RESSIZE(ressource) (((ressource)->end - (ressource)->start)+1) struct glamofb_handle { struct fb_info *fb; @@ -99,25 +108,12 @@ { GLAMO_REG_LCD_MODE3, 0x0b40 }, /* src data rgb565, res, 18bit rgb666 * 000 01 011 0100 0000 */ - { GLAMO_REG_LCD_WIDTH, 480 }, - { GLAMO_REG_LCD_HEIGHT, 640 }, { GLAMO_REG_LCD_POLARITY, 0x440c }, /* DE high active, no cpu/lcd if, cs0 force low, a0 low active, * np cpu if, 9bit serial data, sclk rising edge latch data * 01 00 0 100 0 000 01 0 0 */ { GLAMO_REG_LCD_A_BASE1, 0x0000 }, /* display A base address 15:0 */ { GLAMO_REG_LCD_A_BASE2, 0x0000 }, /* display A base address 22:16 */ - { GLAMO_REG_LCD_PITCH, 480*2 }, - { GLAMO_REG_LCD_HORIZ_TOTAL, 480 + 8 + 8 + 104 }, /* 600 */ - { GLAMO_REG_LCD_HORIZ_RETR_START, 0 }, - { GLAMO_REG_LCD_HORIZ_RETR_END, 8 }, - { GLAMO_REG_LCD_HORIZ_DISP_START, 8 + 104 }, - { GLAMO_REG_LCD_HORIZ_DISP_END, 8 + 104 + 480 }, - { GLAMO_REG_LCD_VERT_TOTAL, 640 + 2 + 2 + 16 }, /* 660 */ - { GLAMO_REG_LCD_VERT_RETR_START, 0 }, - { GLAMO_REG_LCD_VERT_RETR_END, 2 }, - { GLAMO_REG_LCD_VERT_DISP_START, 2 + 2 }, - { GLAMO_REG_LCD_VERT_DISP_END, 2 + 2 + 640 }, }; static int glamofb_run_script(struct glamofb_handle *glamo, @@ -207,24 +203,230 @@ return 0; } +static void reg_set_bit_mask(struct glamofb_handle *glamo, + u_int16_t reg, u_int16_t mask, + u_int16_t val) +{ + u_int16_t tmp; + + val &= mask; + + tmp = reg_read(glamo, reg); + tmp &= ~mask; + tmp |= val; + reg_write(glamo, reg, tmp); +} + +#define GLAMO_LCD_WIDTH_MASK 0x03FF +#define GLAMO_LCD_HEIGHT_MASK 0x03FF +#define GLAMO_LCD_PITCH_MASK 0x07FE +#define GLAMO_LCD_HV_TOTAL_MASK 0x03FF +#define GLAMO_LCD_HV_RETR_START_MASK 0x03FF +#define GLAMO_LCD_HV_RETR_END_MASK 0x03FF +#define GLAMO_LCD_HV_RETR_DISP_START_MASK 0x03FF +#define GLAMO_LCD_HV_RETR_DISP_END_MASK 0x03FF + +enum orientation { + ORIENTATION_PORTRAIT, + ORIENTATION_LANDSCAPE +}; + + +static void rotate_lcd(struct glamofb_handle *glamo, + __u32 rotation) +{ + int glamo_rot; + + switch (rotation) { + case FB_ROTATE_UR: + glamo_rot = GLAMO_LCD_ROT_MODE_0; + break; + case FB_ROTATE_CW: + glamo_rot = GLAMO_LCD_ROT_MODE_90; + break; + case FB_ROTATE_UD: + glamo_rot = GLAMO_LCD_ROT_MODE_180; + break; + case FB_ROTATE_CCW: + glamo_rot = GLAMO_LCD_ROT_MODE_270; + break; + default: + glamo_rot = GLAMO_LCD_ROT_MODE_0; + break; + } + glamofb_cmd_mode(glamo, 1); + reg_set_bit_mask(glamo, + GLAMO_REG_LCD_WIDTH, + GLAMO_LCD_ROT_MODE_MASK, + glamo_rot); + reg_set_bit_mask(glamo, + GLAMO_REG_LCD_MODE1, + GLAMO_LCD_MODE1_ROTATE_EN, + (glamo_rot != GLAMO_LCD_ROT_MODE_0)? + GLAMO_LCD_MODE1_ROTATE_EN : 0); + glamofb_cmd_mode(glamo, 0); +} + +static enum orientation get_orientation(struct fb_var_screeninfo *var) +{ + GLAMO_LOG("mark\n") + if (var->xres <= var->yres) { + GLAMO_LOG("portrait\n") + return ORIENTATION_PORTRAIT; + } + GLAMO_LOG("landscape\n") + return ORIENTATION_LANDSCAPE; +} + +static int will_orientation_change(struct fb_var_screeninfo *var) +{ + enum orientation orient = get_orientation(var); + switch (orient) { + case ORIENTATION_LANDSCAPE: + if (var->rotate == FB_ROTATE_UR || var->rotate == FB_ROTATE_UD) + return 1; + break; + case ORIENTATION_PORTRAIT: + if (var->rotate == FB_ROTATE_CW || var->rotate == FB_ROTATE_CCW) + return 1; + break; + } + return 0; +} + +static void glamofb_update_lcd_controller(struct glamofb_handle *glamo, + struct fb_var_screeninfo *var) +{ + int sync, bp, disp, fp, total, xres, yres, pitch, orientation_changing; + + GLAMO_LOG("enter: glamo:%#x, var:%#x\n", (unsigned)glamo, (unsigned)var); + if (!glamo || !var) + return; + + glamofb_cmd_mode(glamo, 1); + + if (var->pixclock) + glamo_engine_reclock(glamo->mach_info->glamo, + GLAMO_ENGINE_LCD, + var->pixclock); + + xres = var->xres; + yres = var->yres; + GLAMO_LOG("xres:%d, yres:%d, rotate:%d\n", xres, yres, var->rotate); + + /* + * figure out if orientation is going to change + */ + orientation_changing = will_orientation_change(var); + GLAMO_LOG("orientation_changing:%d\n", orientation_changing); + + /* + * adjust the pitch according to new orientation to come + */ + if (orientation_changing) { + pitch = var->yres * var->bits_per_pixel / 8; + } else { + pitch = var->xres * var->bits_per_pixel / 8; + } + GLAMO_LOG("pitch:%d\n", pitch); + + /* + * set the awaiten LCD geometry + */ + reg_set_bit_mask(glamo, + GLAMO_REG_LCD_WIDTH, + GLAMO_LCD_WIDTH_MASK, + xres); + reg_set_bit_mask(glamo, + GLAMO_REG_LCD_HEIGHT, + GLAMO_LCD_HEIGHT_MASK, + yres); + reg_set_bit_mask(glamo, + GLAMO_REG_LCD_PITCH, + GLAMO_LCD_PITCH_MASK, + pitch); + + GLAMO_LOG("mark:\n"); + /* + * honour the rotation request + */ + rotate_lcd(glamo, var->rotate); + + /* + * update the reported geometry + * of the framebuffer. + */ + if (orientation_changing) { + var->xres_virtual = var->xres = yres; + var->yres_virtual = var->yres = xres; + } else { + var->xres_virtual = var->xres = xres; + var->yres_virtual = var->yres = yres; + } + + GLAMO_LOG("reported res:(%d,%d)\n", var->xres, var->yres); + /* + * update scannout timings + */ + sync = 0; + bp = sync + var->hsync_len; + disp = bp + var->left_margin; + fp = disp + xres; + total = fp + var->right_margin; + + reg_set_bit_mask(glamo, GLAMO_REG_LCD_HORIZ_TOTAL, + GLAMO_LCD_HV_TOTAL_MASK, total); + reg_set_bit_mask(glamo, GLAMO_REG_LCD_HORIZ_RETR_START, + GLAMO_LCD_HV_RETR_START_MASK, sync); + reg_set_bit_mask(glamo, GLAMO_REG_LCD_HORIZ_RETR_END, + GLAMO_LCD_HV_RETR_END_MASK, bp); + reg_set_bit_mask(glamo, GLAMO_REG_LCD_HORIZ_DISP_START, + GLAMO_LCD_HV_RETR_DISP_START_MASK, disp); + reg_set_bit_mask(glamo, GLAMO_REG_LCD_HORIZ_DISP_END, + GLAMO_LCD_HV_RETR_DISP_END_MASK, fp); + + GLAMO_LOG("mark:\n"); + + sync = 0; + bp = sync + var->vsync_len; + disp = bp + var->upper_margin; + fp = disp + yres; + total = fp + var->lower_margin; + + reg_set_bit_mask(glamo, GLAMO_REG_LCD_VERT_TOTAL, + GLAMO_LCD_HV_TOTAL_MASK, total); + reg_set_bit_mask(glamo, GLAMO_REG_LCD_VERT_RETR_START, + GLAMO_LCD_HV_RETR_START_MASK, sync); + reg_set_bit_mask(glamo, GLAMO_REG_LCD_VERT_RETR_END, + GLAMO_LCD_HV_RETR_END_MASK, bp); + reg_set_bit_mask(glamo, GLAMO_REG_LCD_VERT_DISP_START, + GLAMO_LCD_HV_RETR_DISP_START_MASK, disp); + reg_set_bit_mask(glamo, GLAMO_REG_LCD_VERT_DISP_END, + GLAMO_LCD_HV_RETR_DISP_END_MASK, fp); + + GLAMO_LOG("mark:\n"); + glamofb_cmd_mode(glamo, 0); + + GLAMO_LOG("leave:\n"); +} + static int glamofb_set_par(struct fb_info *info) { struct glamofb_handle *glamo = info->par; struct fb_var_screeninfo *var = &info->var; - /* FIXME */ - switch (var->bits_per_pixel) { case 16: - glamo->fb->fix.visual = FB_VISUAL_TRUECOLOR; + info->fix.visual = FB_VISUAL_TRUECOLOR; break; default: - printk("Smedia driver doens't support != 16bpp\n"); + printk("Smedia driver doesn't support != 16bpp\n"); return -EINVAL; } - glamo->fb->fix.line_length = (var->width * var->bits_per_pixel) / 8; - glamo->fb->fix.smem_len = info->fix.line_length * var->yres_virtual; + info->fix.line_length = (var->xres * var->bits_per_pixel) / 8; + + glamofb_update_lcd_controller(glamo, var); return 0; } @@ -368,6 +570,8 @@ while (!reg_read(gfb, GLAMO_REG_LCD_STATUS2) & (1 << 12)) yield(); dev_dbg(gfb->dev, "idle!\n"); + + msleep(90); } else { /* RGB interface needs vsync/hsync */ if (reg_read(gfb, GLAMO_REG_LCD_MODE3) & GLAMO_LCD_MODE3_RGB) @@ -384,7 +588,6 @@ int glamofb_cmd_write(struct glamofb_handle *gfb, u_int16_t val) { - dev_dbg(gfb->dev, "%s: waiting for cmdq empty\n", __FUNCTION__); while (!glamofb_cmdq_empty(gfb)) @@ -409,6 +612,18 @@ .fb_imageblit = cfb_imageblit, }; +static int glamofb_init_regs(struct glamofb_handle *glamo) +{ + struct fb_info *info = glamo->fb; + struct fb_var_screeninfo *var = &info->var; + + glamofb_check_var(&info->var, info); + glamofb_run_script(glamo, glamo_regs, ARRAY_SIZE(glamo_regs)); + glamofb_set_par(info); + + return 0; +} + static int __init glamofb_probe(struct platform_device *pdev) { int rc = -EIO; @@ -453,7 +668,8 @@ } glamofb->fb_res = request_mem_region(glamofb->fb_res->start, - GLAMO_FB_ALLOC, pdev->name); + mach_info->fb_mem_size, + pdev->name); if (!glamofb->fb_res) { dev_err(&pdev->dev, "failed to request vram region\n"); goto out_release_reg; @@ -504,34 +720,21 @@ fbinfo->var.yres_virtual = mach_info->yres.defval; fbinfo->var.bits_per_pixel = mach_info->bpp.defval; -#if 0 - fbinfo->var.upper_margin = - fbinfo->var.lower_margin = - fbinfo->var.vsync_len = 2; - - fbinfo->var.left_margin = - fbinfo->var.right_margin = - fbinfo->var.hsync_len = 8; -#endif + fbinfo->var.pixclock = mach_info->pixclock; + fbinfo->var.left_margin = mach_info->left_margin; + fbinfo->var.right_margin = mach_info->right_margin; + fbinfo->var.upper_margin = mach_info->upper_margin; + fbinfo->var.lower_margin = mach_info->lower_margin; + fbinfo->var.hsync_len = mach_info->hsync_len; + fbinfo->var.vsync_len = mach_info->vsync_len; - fbinfo->var.red.offset = 11; - fbinfo->var.green.offset = 5; - fbinfo->var.blue.offset = 0; - fbinfo->var.transp.offset = 0; - fbinfo->var.red.length = 5; - fbinfo->var.green.length = 6; - fbinfo->var.blue.length = 5; - fbinfo->var.transp.length = 0; - fbinfo->fix.smem_len = mach_info->xres.max * - mach_info->yres.max * - mach_info->bpp.max / 8; + fbinfo->fix.smem_len = mach_info->fb_mem_size; memset(fbinfo->screen_base, 0, fbinfo->fix.smem_len); glamo_engine_enable(mach_info->glamo, GLAMO_ENGINE_LCD); glamo_engine_reset(mach_info->glamo, GLAMO_ENGINE_LCD); - glamofb_run_script(glamofb, glamo_regs, ARRAY_SIZE(glamo_regs)); - glamofb_cmd_mode(glamofb, 0); + glamofb_init_regs(glamofb); rc = register_framebuffer(fbinfo); if (rc < 0) { Index: linux-2.6.22.5/drivers/video/glamo/glamo-core.c =================================================================== --- linux-2.6.22.5.orig/drivers/video/glamo/glamo-core.c +++ linux-2.6.22.5/drivers/video/glamo/glamo-core.c @@ -488,9 +488,85 @@ spin_lock(&glamo->lock); __reg_clear_bit(glamo, rst->reg, rst->val); spin_unlock(&glamo->lock); + + msleep(1); } EXPORT_SYMBOL_GPL(glamo_engine_reset); +enum glamo_pll { + GLAMO_PLL1, + GLAMO_PLL2, +}; + +static int glamo_pll_rate(struct glamo_core *glamo, + enum glamo_pll pll) +{ + u_int16_t reg; + unsigned int div = 512; + /* FIXME: move osci into platform_data */ + unsigned int osci = 32768; + + if (osci == 32768) + div = 1; + + switch (pll) { + case GLAMO_PLL1: + reg = __reg_read(glamo, GLAMO_REG_PLL_GEN1); + break; + case GLAMO_PLL2: + reg = __reg_read(glamo, GLAMO_REG_PLL_GEN3); + break; + default: + return -EINVAL; + } + return (osci/div)*reg; +} + +int glamo_engine_reclock(struct glamo_core *glamo, + enum glamo_engine engine, + int ps) +{ + int pll, khz; + u_int16_t reg, mask, val = 0; + + if (!ps) + return 0; + + switch (engine) { + case GLAMO_ENGINE_LCD: + pll = GLAMO_PLL1; + reg = GLAMO_REG_CLOCK_GEN7; + mask = 0xff; + break; + default: + dev_warn(&glamo->pdev->dev, + "reclock of engine 0x%x not supported\n", engine); + return -EINVAL; + break; + } + + pll = glamo_pll_rate(glamo, pll); + khz = 1000000000UL / ps; + + if (khz) + val = (pll / khz) / 1000; + + dev_dbg(&glamo->pdev->dev, + "PLL %d, kHZ %d, div %d\n", pll, khz, val); + + if (val) { + val--; + + reg_set_bit_mask(glamo, reg, mask, val); + msleep(5); /* wait some time to stabilize */ + + return 0; + } else { + return -EINVAL; + } +} +EXPORT_SYMBOL_GPL(glamo_engine_reclock); + /*********************************************************************** * script support ***********************************************************************/ @@ -643,35 +719,6 @@ }; #endif -enum glamo_pll { - GLAMO_PLL1, - GLAMO_PLL2, -}; - -static int glamo_pll_rate(struct glamo_core *glamo, - enum glamo_pll pll) -{ - u_int16_t reg; - unsigned int div = 512; - /* FIXME: move osci into platform_data */ - unsigned int osci = 32768; - - if (osci == 32768) - div = 1; - - switch (pll) { - case GLAMO_PLL1: - reg = __reg_read(glamo, GLAMO_REG_PLL_GEN1); - break; - case GLAMO_PLL2: - reg = __reg_read(glamo, GLAMO_REG_PLL_GEN3); - break; - default: - return -EINVAL; - } - return (osci/div)*reg; -} - enum glamo_power { GLAMO_POWER_ON, GLAMO_POWER_STANDBY, Index: linux-2.6.22.5/drivers/video/glamo/glamo-regs.h =================================================================== --- linux-2.6.22.5.orig/drivers/video/glamo/glamo-regs.h +++ linux-2.6.22.5/drivers/video/glamo/glamo-regs.h @@ -431,6 +431,16 @@ GLAMO_LCD_MODE3_18BITS = 0x0040, }; +enum glamo_lcd_rot_mode { + GLAMO_LCD_ROT_MODE_0 = 0x0000, + GLAMO_LCD_ROT_MODE_180 = 0x2000, + GLAMO_LCD_ROT_MODE_MIRROR = 0x4000, + GLAMO_LCD_ROT_MODE_FLIP = 0x6000, + GLAMO_LCD_ROT_MODE_90 = 0x8000, + GLAMO_LCD_ROT_MODE_270 = 0xa000, +}; +#define GLAMO_LCD_ROT_MODE_MASK 0xe000 + enum glamo_lcd_cmd_type { GLAMO_LCD_CMD_TYPE_DISP = 0x0000, GLAMO_LCD_CMD_TYPE_PARALLEL = 0x4000,