X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=drivers%2Fvideo%2Fsm501fb.c;h=dcd98793d5681e14e7521e1f9efcf92806fe4f8b;hb=8afb1cebf5e7fde4a1bddacb559bda8526e64144;hp=02b290ca01e125700116eb804e4c44138f9a7a3e;hpb=5fc404e47bdf2d34ffc2edc16070cda410838291;p=safe%2Fjmp%2Flinux-2.6 diff --git a/drivers/video/sm501fb.c b/drivers/video/sm501fb.c index 02b290c..dcd9879 100644 --- a/drivers/video/sm501fb.c +++ b/drivers/video/sm501fb.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -47,10 +48,15 @@ enum sm501_controller { HEAD_PANEL = 1, }; -/* SM501 memory adress */ +/* SM501 memory address. + * + * This structure is used to track memory usage within the SM501 framebuffer + * allocation. The sm_addr field is stored as an offset as it is often used + * against both the physical and mapped addresses. + */ struct sm501_mem { unsigned long size; - unsigned long sm_addr; + unsigned long sm_addr; /* offset from base of sm501 fb. */ void __iomem *k_addr; }; @@ -62,6 +68,8 @@ struct sm501fb_info { struct resource *regs_res; /* registers resource */ struct sm501_platdata_fb *pdata; /* our platform data */ + unsigned long pm_crt_ctrl; /* pm: crt ctrl save */ + int irq; int swap_endian; /* set to swap rgb=>bgr */ void __iomem *regs; /* remapped registers */ @@ -136,34 +144,71 @@ static inline void sm501fb_sync_regs(struct sm501fb_info *info) #define SM501_MEMF_CRT (4) #define SM501_MEMF_ACCEL (8) -int sm501_alloc_mem(struct sm501fb_info *inf, struct sm501_mem *mem, - unsigned int why, size_t size) +static int sm501_alloc_mem(struct sm501fb_info *inf, struct sm501_mem *mem, + unsigned int why, size_t size) { - unsigned int ptr = 0; + struct sm501fb_par *par; + struct fb_info *fbi; + unsigned int ptr; + unsigned int end; switch (why) { case SM501_MEMF_CURSOR: ptr = inf->fbmem_len - size; - inf->fbmem_len = ptr; + inf->fbmem_len = ptr; /* adjust available memory. */ break; case SM501_MEMF_PANEL: + if (size > inf->fbmem_len) + return -ENOMEM; + ptr = inf->fbmem_len - size; - if (ptr < inf->fb[0]->fix.smem_len) + fbi = inf->fb[HEAD_CRT]; + + /* round down, some programs such as directfb do not draw + * 0,0 correctly unless the start is aligned to a page start. + */ + + if (ptr > 0) + ptr &= ~(PAGE_SIZE - 1); + + if (fbi && ptr < fbi->fix.smem_len) return -ENOMEM; break; case SM501_MEMF_CRT: ptr = 0; + + /* check to see if we have panel memory allocated + * which would put an limit on available memory. */ + + fbi = inf->fb[HEAD_PANEL]; + if (fbi) { + par = fbi->par; + end = par->screen.k_addr ? par->screen.sm_addr : inf->fbmem_len; + } else + end = inf->fbmem_len; + + if ((ptr + size) > end) + return -ENOMEM; + break; case SM501_MEMF_ACCEL: - ptr = inf->fb[0]->fix.smem_len; + fbi = inf->fb[HEAD_CRT]; + ptr = fbi ? fbi->fix.smem_len : 0; - if ((ptr + size) > - (inf->fb[1]->fix.smem_start - inf->fbmem_res->start)) + fbi = inf->fb[HEAD_PANEL]; + if (fbi) { + par = fbi->par; + end = par->screen.sm_addr; + } else + end = inf->fbmem_len; + + if ((ptr + size) > end) return -ENOMEM; + break; default: @@ -234,12 +279,14 @@ static int sm501fb_check_var(struct fb_var_screeninfo *var, /* check we can fit these values into the registers */ - if (var->hsync_len > 255 || var->vsync_len > 255) + if (var->hsync_len > 255 || var->vsync_len > 63) return -EINVAL; - if ((var->xres + var->right_margin) >= 4096) + /* hdisplay end and hsync start */ + if ((var->xres + var->right_margin) > 4096) return -EINVAL; + /* vdisplay end and vsync start */ if ((var->yres + var->lower_margin) > 2048) return -EINVAL; @@ -278,19 +325,21 @@ static int sm501fb_check_var(struct fb_var_screeninfo *var, var->blue.length = var->bits_per_pixel; var->blue.offset = 0; var->transp.length = 0; + var->transp.offset = 0; break; case 16: if (sm->pdata->flags & SM501_FBPD_SWAP_FB_ENDIAN) { - var->red.offset = 11; - var->green.offset = 5; - var->blue.offset = 0; - } else { var->blue.offset = 11; var->green.offset = 5; var->red.offset = 0; + } else { + var->red.offset = 11; + var->green.offset = 5; + var->blue.offset = 0; } + var->transp.offset = 0; var->red.length = 5; var->green.length = 6; @@ -394,7 +443,7 @@ static int sm501fb_set_par_common(struct fb_info *info, break; case 16: - info->fix.visual = FB_VISUAL_DIRECTCOLOR; + info->fix.visual = FB_VISUAL_TRUECOLOR; break; case 32: @@ -610,6 +659,7 @@ static int sm501fb_set_par_crt(struct fb_info *info) case 16: control |= SM501_DC_CRT_CONTROL_16BPP; + sm501fb_setup_gamma(fbi, SM501_DC_CRT_PALETTE); break; case 32: @@ -638,6 +688,7 @@ static void sm501fb_panel_power(struct sm501fb_info *fbi, int to) { unsigned long control; void __iomem *ctrl_reg = fbi->regs + SM501_DC_PANEL_CONTROL; + struct sm501_platdata_fbsub *pd = fbi->pdata->fb_pnl; control = readl(ctrl_reg); @@ -654,26 +705,52 @@ static void sm501fb_panel_power(struct sm501fb_info *fbi, int to) sm501fb_sync_regs(fbi); mdelay(10); - control |= SM501_DC_PANEL_CONTROL_BIAS; /* VBIASEN */ - writel(control, ctrl_reg); - sm501fb_sync_regs(fbi); - mdelay(10); + /* VBIASEN */ - control |= SM501_DC_PANEL_CONTROL_FPEN; - writel(control, ctrl_reg); + if (!(pd->flags & SM501FB_FLAG_PANEL_NO_VBIASEN)) { + if (pd->flags & SM501FB_FLAG_PANEL_INV_VBIASEN) + control &= ~SM501_DC_PANEL_CONTROL_BIAS; + else + control |= SM501_DC_PANEL_CONTROL_BIAS; + writel(control, ctrl_reg); + sm501fb_sync_regs(fbi); + mdelay(10); + } + + if (!(pd->flags & SM501FB_FLAG_PANEL_NO_FPEN)) { + if (pd->flags & SM501FB_FLAG_PANEL_INV_FPEN) + control &= ~SM501_DC_PANEL_CONTROL_FPEN; + else + control |= SM501_DC_PANEL_CONTROL_FPEN; + + writel(control, ctrl_reg); + sm501fb_sync_regs(fbi); + mdelay(10); + } } else if (!to && (control & SM501_DC_PANEL_CONTROL_VDD) != 0) { /* disable panel power */ + if (!(pd->flags & SM501FB_FLAG_PANEL_NO_FPEN)) { + if (pd->flags & SM501FB_FLAG_PANEL_INV_FPEN) + control |= SM501_DC_PANEL_CONTROL_FPEN; + else + control &= ~SM501_DC_PANEL_CONTROL_FPEN; + + writel(control, ctrl_reg); + sm501fb_sync_regs(fbi); + mdelay(10); + } - control &= ~SM501_DC_PANEL_CONTROL_FPEN; - writel(control, ctrl_reg); - sm501fb_sync_regs(fbi); - mdelay(10); + if (!(pd->flags & SM501FB_FLAG_PANEL_NO_VBIASEN)) { + if (pd->flags & SM501FB_FLAG_PANEL_INV_VBIASEN) + control |= SM501_DC_PANEL_CONTROL_BIAS; + else + control &= ~SM501_DC_PANEL_CONTROL_BIAS; - control &= ~SM501_DC_PANEL_CONTROL_BIAS; - writel(control, ctrl_reg); - sm501fb_sync_regs(fbi); - mdelay(10); + writel(control, ctrl_reg); + sm501fb_sync_regs(fbi); + mdelay(10); + } control &= ~SM501_DC_PANEL_CONTROL_DATA; writel(control, ctrl_reg); @@ -738,6 +815,7 @@ static int sm501fb_set_par_pnl(struct fb_info *info) case 16: control |= SM501_DC_PANEL_CONTROL_16BPP; + sm501fb_setup_gamma(fbi, SM501_DC_PANEL_PALETTE); break; case 32: @@ -774,6 +852,11 @@ static int sm501fb_set_par_pnl(struct fb_info *info) writel(control, fbi->regs + SM501_DC_PANEL_CONTROL); sm501fb_sync_regs(fbi); + /* ensure the panel interface is not tristated at this point */ + + sm501_modify_reg(fbi->dev->parent, SM501_SYSTEM_CONTROL, + 0, SM501_SYSCTRL_PANEL_TRISTATE); + /* power the panel up */ sm501fb_panel_power(fbi, 1); return 0; @@ -926,7 +1009,7 @@ static int sm501fb_blank_crt(int blank_mode, struct fb_info *info) * set or change the hardware cursor parameters */ -int sm501fb_cursor(struct fb_info *info, struct fb_cursor *cursor) +static int sm501fb_cursor(struct fb_info *info, struct fb_cursor *cursor) { struct sm501fb_par *par = info->par; struct sm501fb_info *fbi = par->info; @@ -989,7 +1072,7 @@ int sm501fb_cursor(struct fb_info *info, struct fb_cursor *cursor) ((info->cmap.green[fg_col] & 0xFC) << 3) | ((info->cmap.blue[fg_col] & 0xF8) >> 3); - dev_dbg(fbi->dev, "fgcol %08x, bgcol %08x\n", fg, bg); + dev_dbg(fbi->dev, "fgcol %08lx, bgcol %08lx\n", fg, bg); writel(bg, base + SM501_OFF_HWC_COLOR_1_2); writel(fg, base + SM501_OFF_HWC_COLOR_3); @@ -1074,9 +1157,9 @@ static ssize_t sm501fb_crtsrc_store(struct device *dev, if (len < 1) return -EINVAL; - if (strnicmp(buf, "crt", sizeof("crt")) == 0) + if (strnicmp(buf, "crt", 3) == 0) head = HEAD_CRT; - else if (strnicmp(buf, "panel", sizeof("panel")) == 0) + else if (strnicmp(buf, "panel", 5) == 0) head = HEAD_PANEL; else return -EINVAL; @@ -1098,7 +1181,7 @@ static ssize_t sm501fb_crtsrc_store(struct device *dev, writel(ctrl, info->regs + SM501_DC_CRT_CONTROL); sm501fb_sync_regs(info); - return (head == HEAD_CRT) ? 3 : 5; + return len; } /* Prepare the device_attr for registration with sysfs later */ @@ -1187,50 +1270,23 @@ static struct fb_ops sm501fb_ops_pnl = { .fb_imageblit = cfb_imageblit, }; -/* sm501fb_info_alloc - * - * creates and initialises an sm501fb_info structure -*/ - -static struct sm501fb_info *sm501fb_info_alloc(struct fb_info *fbinfo_crt, - struct fb_info *fbinfo_pnl) -{ - struct sm501fb_info *info; - struct sm501fb_par *par; - - info = kzalloc(sizeof(struct sm501fb_info), GFP_KERNEL); - if (info) { - /* set the references back */ - - par = fbinfo_crt->par; - par->info = info; - par->head = HEAD_CRT; - fbinfo_crt->pseudo_palette = &par->pseudo_palette; - - par = fbinfo_pnl->par; - par->info = info; - par->head = HEAD_PANEL; - fbinfo_pnl->pseudo_palette = &par->pseudo_palette; - - /* store the two fbs into our info */ - info->fb[HEAD_CRT] = fbinfo_crt; - info->fb[HEAD_PANEL] = fbinfo_pnl; - } - - return info; -} - /* sm501_init_cursor * * initialise hw cursor parameters */ -int sm501_init_cursor(struct fb_info *fbi, unsigned int reg_base) +static int sm501_init_cursor(struct fb_info *fbi, unsigned int reg_base) { - struct sm501fb_par *par = fbi->par; - struct sm501fb_info *info = par->info; + struct sm501fb_par *par; + struct sm501fb_info *info; int ret; + if (fbi == NULL) + return 0; + + par = fbi->par; + info = par->info; + par->cursor_regs = info->regs + reg_base; ret = sm501_alloc_mem(info, &par->cursor, SM501_MEMF_CURSOR, 1024); @@ -1258,12 +1314,10 @@ static int sm501fb_start(struct sm501fb_info *info, struct platform_device *pdev) { struct resource *res; - struct device *dev; + struct device *dev = &pdev->dev; + int k; int ret; - info->dev = dev = &pdev->dev; - platform_set_drvdata(pdev, info); - info->irq = ret = platform_get_irq(pdev, 0); if (ret < 0) { /* we currently do not use the IRQ */ @@ -1320,6 +1374,13 @@ static int sm501fb_start(struct sm501fb_info *info, info->fbmem_len = (res->end - res->start)+1; + /* clear framebuffer memory - avoids garbage data on unused fb */ + memset(info->fbmem, 0, info->fbmem_len); + + /* clear palette ram - undefined at power on */ + for (k = 0; k < (256 * 3); k++) + writel(0, info->regs + SM501_DC_PANEL_PALETTE + (k * 4)); + /* enable display controller */ sm501_unit_power(dev->parent, SM501_GATE_DISPLAY, 1); @@ -1359,11 +1420,6 @@ static void sm501fb_stop(struct sm501fb_info *info) kfree(info->regs_res); } -static void sm501fb_info_release(struct sm501fb_info *info) -{ - kfree(info); -} - static int sm501fb_init_fb(struct fb_info *fb, enum sm501_controller head, const char *fbname) @@ -1508,36 +1564,93 @@ static struct sm501_platdata_fb sm501fb_def_pdata = { static char driver_name_crt[] = "sm501fb-crt"; static char driver_name_pnl[] = "sm501fb-panel"; -static int __init sm501fb_probe(struct platform_device *pdev) +static int __devinit sm501fb_probe_one(struct sm501fb_info *info, + enum sm501_controller head) { - struct sm501fb_info *info; - struct device *dev = &pdev->dev; - struct fb_info *fbinfo_crt; - struct fb_info *fbinfo_pnl; - int ret; + unsigned char *name = (head == HEAD_CRT) ? "crt" : "panel"; + struct sm501_platdata_fbsub *pd; + struct sm501fb_par *par; + struct fb_info *fbi; - /* allocate our framebuffers */ + pd = (head == HEAD_CRT) ? info->pdata->fb_crt : info->pdata->fb_pnl; - fbinfo_crt = framebuffer_alloc(sizeof(struct sm501fb_par), dev); - if (fbinfo_crt == NULL) { - dev_err(dev, "cannot allocate crt framebuffer\n"); + /* Do not initialise if we've not been given any platform data */ + if (pd == NULL) { + dev_info(info->dev, "no data for fb %s (disabled)\n", name); + return 0; + } + + fbi = framebuffer_alloc(sizeof(struct sm501fb_par), info->dev); + if (fbi == NULL) { + dev_err(info->dev, "cannot allocate %s framebuffer\n", name); return -ENOMEM; } - fbinfo_pnl = framebuffer_alloc(sizeof(struct sm501fb_par), dev); - if (fbinfo_pnl == NULL) { - dev_err(dev, "cannot allocate panel framebuffer\n"); - ret = -ENOMEM; - goto fbinfo_crt_alloc_fail; + par = fbi->par; + par->info = info; + par->head = head; + fbi->pseudo_palette = &par->pseudo_palette; + + info->fb[head] = fbi; + + return 0; +} + +/* Free up anything allocated by sm501fb_init_fb */ + +static void sm501_free_init_fb(struct sm501fb_info *info, + enum sm501_controller head) +{ + struct fb_info *fbi = info->fb[head]; + + fb_dealloc_cmap(&fbi->cmap); +} + +static int __devinit sm501fb_start_one(struct sm501fb_info *info, + enum sm501_controller head, + const char *drvname) +{ + struct fb_info *fbi = info->fb[head]; + int ret; + + if (!fbi) + return 0; + + ret = sm501fb_init_fb(info->fb[head], head, drvname); + if (ret) { + dev_err(info->dev, "cannot initialise fb %s\n", drvname); + return ret; } - info = sm501fb_info_alloc(fbinfo_crt, fbinfo_pnl); - if (info == NULL) { - dev_err(dev, "cannot allocate par\n"); - ret = -ENOMEM; - goto sm501fb_alloc_fail; + ret = register_framebuffer(info->fb[head]); + if (ret) { + dev_err(info->dev, "failed to register fb %s\n", drvname); + sm501_free_init_fb(info, head); + return ret; } + dev_info(info->dev, "fb%d: %s frame buffer\n", fbi->node, fbi->fix.id); + + return 0; +} + +static int __devinit sm501fb_probe(struct platform_device *pdev) +{ + struct sm501fb_info *info; + struct device *dev = &pdev->dev; + int ret; + + /* allocate our framebuffers */ + + info = kzalloc(sizeof(struct sm501fb_info), GFP_KERNEL); + if (!info) { + dev_err(dev, "failed to allocate state\n"); + return -ENOMEM; + } + + info->dev = dev = &pdev->dev; + platform_set_drvdata(pdev, info); + if (dev->parent->platform_data) { struct sm501_platdata *pd = dev->parent->platform_data; info->pdata = pd->fb; @@ -1548,90 +1661,88 @@ static int __init sm501fb_probe(struct platform_device *pdev) info->pdata = &sm501fb_def_pdata; } - /* start the framebuffers */ + /* probe for the presence of each panel */ - ret = sm501fb_start(info, pdev); - if (ret) { - dev_err(dev, "cannot initialise SM501\n"); - goto sm501fb_start_fail; + ret = sm501fb_probe_one(info, HEAD_CRT); + if (ret < 0) { + dev_err(dev, "failed to probe CRT\n"); + goto err_alloc; } - /* CRT framebuffer setup */ + ret = sm501fb_probe_one(info, HEAD_PANEL); + if (ret < 0) { + dev_err(dev, "failed to probe PANEL\n"); + goto err_probed_crt; + } - ret = sm501fb_init_fb(fbinfo_crt, HEAD_CRT, driver_name_crt); - if (ret) { - dev_err(dev, "cannot initialise CRT fb\n"); - goto sm501fb_start_fail; + if (info->fb[HEAD_PANEL] == NULL && + info->fb[HEAD_CRT] == NULL) { + dev_err(dev, "no framebuffers found\n"); + goto err_alloc; } - /* Panel framebuffer setup */ + /* get the resources for both of the framebuffers */ - ret = sm501fb_init_fb(fbinfo_pnl, HEAD_PANEL, driver_name_pnl); + ret = sm501fb_start(info, pdev); if (ret) { - dev_err(dev, "cannot initialise Panel fb\n"); - goto sm501fb_start_fail; + dev_err(dev, "cannot initialise SM501\n"); + goto err_probed_panel; } - /* register framebuffers */ - - ret = register_framebuffer(fbinfo_crt); - if (ret < 0) { - dev_err(dev, "failed to register CRT fb (%d)\n", ret); - goto register_crt_fail; + ret = sm501fb_start_one(info, HEAD_CRT, driver_name_crt); + if (ret) { + dev_err(dev, "failed to start CRT\n"); + goto err_started; } - ret = register_framebuffer(fbinfo_pnl); - if (ret < 0) { - dev_err(dev, "failed to register panel fb (%d)\n", ret); - goto register_pnl_fail; + ret = sm501fb_start_one(info, HEAD_PANEL, driver_name_pnl); + if (ret) { + dev_err(dev, "failed to start Panel\n"); + goto err_started_crt; } - dev_info(dev, "fb%d: %s frame buffer device\n", - fbinfo_crt->node, fbinfo_crt->fix.id); - - dev_info(dev, "fb%d: %s frame buffer device\n", - fbinfo_pnl->node, fbinfo_pnl->fix.id); - /* create device files */ ret = device_create_file(dev, &dev_attr_crt_src); if (ret) - goto crtsrc_fail; + goto err_started_panel; ret = device_create_file(dev, &dev_attr_fbregs_pnl); if (ret) - goto fbregs_pnl_fail; + goto err_attached_crtsrc_file; ret = device_create_file(dev, &dev_attr_fbregs_crt); if (ret) - goto fbregs_crt_fail; + goto err_attached_pnlregs_file; /* we registered, return ok */ return 0; - fbregs_crt_fail: +err_attached_pnlregs_file: device_remove_file(dev, &dev_attr_fbregs_pnl); - fbregs_pnl_fail: +err_attached_crtsrc_file: device_remove_file(dev, &dev_attr_crt_src); - crtsrc_fail: - unregister_framebuffer(fbinfo_pnl); +err_started_panel: + unregister_framebuffer(info->fb[HEAD_PANEL]); + sm501_free_init_fb(info, HEAD_PANEL); - register_pnl_fail: - unregister_framebuffer(fbinfo_crt); +err_started_crt: + unregister_framebuffer(info->fb[HEAD_CRT]); + sm501_free_init_fb(info, HEAD_CRT); - register_crt_fail: +err_started: sm501fb_stop(info); - sm501fb_start_fail: - sm501fb_info_release(info); +err_probed_panel: + framebuffer_release(info->fb[HEAD_PANEL]); - sm501fb_alloc_fail: - framebuffer_release(fbinfo_pnl); +err_probed_crt: + framebuffer_release(info->fb[HEAD_CRT]); - fbinfo_crt_alloc_fail: - framebuffer_release(fbinfo_crt); +err_alloc: + kfree(info); return ret; } @@ -1650,11 +1761,14 @@ static int sm501fb_remove(struct platform_device *pdev) device_remove_file(&pdev->dev, &dev_attr_fbregs_pnl); device_remove_file(&pdev->dev, &dev_attr_crt_src); + sm501_free_init_fb(info, HEAD_CRT); + sm501_free_init_fb(info, HEAD_PANEL); + unregister_framebuffer(fbinfo_crt); unregister_framebuffer(fbinfo_pnl); sm501fb_stop(info); - sm501fb_info_release(info); + kfree(info); framebuffer_release(fbinfo_pnl); framebuffer_release(fbinfo_crt); @@ -1673,6 +1787,15 @@ static int sm501fb_suspend_fb(struct sm501fb_info *info, if (par->screen.size == 0) return 0; + /* blank the relevant interface to ensure unit power minimised */ + (par->ops.fb_blank)(FB_BLANK_POWERDOWN, fbi); + + /* tell console/fb driver we are suspending */ + + acquire_console_sem(); + fb_set_suspend(fbi, 1); + release_console_sem(); + /* backup copies in case chip is powered down over suspend */ par->store_fb = vmalloc(par->screen.size); @@ -1687,19 +1810,19 @@ static int sm501fb_suspend_fb(struct sm501fb_info *info, goto err_nocursor; } + dev_dbg(info->dev, "suspending screen to %p\n", par->store_fb); + dev_dbg(info->dev, "suspending cursor to %p\n", par->store_cursor); + memcpy_fromio(par->store_fb, par->screen.k_addr, par->screen.size); memcpy_fromio(par->store_cursor, par->cursor.k_addr, par->cursor.size); - /* blank the relevant interface to ensure unit power minimised */ - (par->ops.fb_blank)(FB_BLANK_POWERDOWN, fbi); - return 0; err_nocursor: vfree(par->store_fb); + par->store_fb = NULL; return -ENOMEM; - } static void sm501fb_resume_fb(struct sm501fb_info *info, @@ -1717,8 +1840,20 @@ static void sm501fb_resume_fb(struct sm501fb_info *info, /* restore the data */ - memcpy_toio(par->screen.k_addr, par->store_fb, par->screen.size); - memcpy_toio(par->cursor.k_addr, par->store_cursor, par->cursor.size); + dev_dbg(info->dev, "restoring screen from %p\n", par->store_fb); + dev_dbg(info->dev, "restoring cursor from %p\n", par->store_cursor); + + if (par->store_fb) + memcpy_toio(par->screen.k_addr, par->store_fb, + par->screen.size); + + if (par->store_cursor) + memcpy_toio(par->cursor.k_addr, par->store_cursor, + par->cursor.size); + + acquire_console_sem(); + fb_set_suspend(fbi, 0); + release_console_sem(); vfree(par->store_fb); vfree(par->store_cursor); @@ -1731,6 +1866,9 @@ static int sm501fb_suspend(struct platform_device *pdev, pm_message_t state) { struct sm501fb_info *info = platform_get_drvdata(pdev); + /* store crt control to resume with */ + info->pm_crt_ctrl = readl(info->regs + SM501_DC_CRT_CONTROL); + sm501fb_suspend_fb(info, HEAD_CRT); sm501fb_suspend_fb(info, HEAD_PANEL); @@ -1740,12 +1878,24 @@ static int sm501fb_suspend(struct platform_device *pdev, pm_message_t state) return 0; } +#define SM501_CRT_CTRL_SAVE (SM501_DC_CRT_CONTROL_TVP | \ + SM501_DC_CRT_CONTROL_SEL) + + static int sm501fb_resume(struct platform_device *pdev) { struct sm501fb_info *info = platform_get_drvdata(pdev); + unsigned long crt_ctrl; sm501_unit_power(info->dev->parent, SM501_GATE_DISPLAY, 1); + /* restore the items we want to be saved for crt control */ + + crt_ctrl = readl(info->regs + SM501_DC_CRT_CONTROL); + crt_ctrl &= ~SM501_CRT_CTRL_SAVE; + crt_ctrl |= info->pm_crt_ctrl & SM501_CRT_CTRL_SAVE; + writel(crt_ctrl, info->regs + SM501_DC_CRT_CONTROL); + sm501fb_resume_fb(info, HEAD_CRT); sm501fb_resume_fb(info, HEAD_PANEL); @@ -1768,7 +1918,7 @@ static struct platform_driver sm501fb_driver = { }, }; -int __devinit sm501fb_init(void) +static int __devinit sm501fb_init(void) { return platform_driver_register(&sm501fb_driver); }