diff options
Diffstat (limited to 'drivers/video/fbdev/mxsfb.c')
-rw-r--r-- | drivers/video/fbdev/mxsfb.c | 107 |
1 files changed, 77 insertions, 30 deletions
diff --git a/drivers/video/fbdev/mxsfb.c b/drivers/video/fbdev/mxsfb.c index 5659ec2d18c4..b756c969c36c 100644 --- a/drivers/video/fbdev/mxsfb.c +++ b/drivers/video/fbdev/mxsfb.c @@ -268,6 +268,7 @@ struct mxsfb_info { #ifdef CONFIG_FB_MXC_OVERLAY struct mxsfb_layer overlay; #endif + char *fb_mode_str; }; #define mxsfb_is_v3(host) (host->devdata->ipversion == 3) @@ -687,7 +688,6 @@ static void mxsfb_enable_controller(struct fb_info *fb_info) "dispdrv:%s\n", host->dispdrv->drv->name); return; } - host->sync = fb_info->var.sync; } if (host->reg_lcd) { @@ -1301,6 +1301,41 @@ static int mxsfb_restore_mode(struct mxsfb_info *host) return 0; } +/* + * Parse user specified options (`video=') + * e.g.: + * video=mxsfb:800x480M-16@60,pixclockpol=1,outputen=1 + */ +static int mxsfb_option_setup(struct mxsfb_info *host, struct fb_info *fb_info) +{ + char *options, *opt; + struct platform_device *pdev = host->pdev; + + if (fb_get_options(DRIVER_NAME, &options)) { + dev_err(&pdev->dev, "Can't get fb option for %s!\n", DRIVER_NAME); + return -ENODEV; + } + + if (!options || !*options) + return 0; + + while ((opt = strsep(&options, ",")) != NULL) { + if (!*opt) { + continue; + } else if (!strncmp(opt, "pixclockpol=", 12)) { + if(simple_strtoul(opt + 12, NULL, 0)) + host->sync |= FB_SYNC_CLK_LAT_FALL; + } else if (!strncmp(opt, "outputen=", 9)) { + if(simple_strtoul(opt + 9, NULL, 0)) + host->sync |= FB_SYNC_OE_LOW_ACT; + } else { + host->fb_mode_str = opt; + } + } + + return 0; +} + static int mxsfb_init_fbinfo_dt(struct mxsfb_info *host) { struct fb_info *fb_info = host->fb_info; @@ -1308,12 +1343,14 @@ static int mxsfb_init_fbinfo_dt(struct mxsfb_info *host) struct device *dev = &host->pdev->dev; struct device_node *np = host->pdev->dev.of_node; struct device_node *display_np; - struct device_node *timings_np; struct display_timings *timings = NULL; const char *disp_dev, *disp_videomode; + struct videomode vm; + struct fb_videomode fb_vm; + struct fb_videomode native_mode; u32 width; int i; - int ret = 0; + int ret = 0, retval = 0; host->id = of_alias_get_id(np, "lcdif"); @@ -1376,34 +1413,50 @@ static int mxsfb_init_fbinfo_dt(struct mxsfb_info *host) goto put_display_node; } - timings_np = of_find_node_by_name(display_np, - "display-timings"); - if (!timings_np) { - dev_err(dev, "failed to find display-timings node\n"); - ret = -ENOENT; - goto put_display_node; - } + INIT_LIST_HEAD(&fb_info->modelist); - for (i = 0; i < of_get_child_count(timings_np); i++) { - struct videomode vm; - struct fb_videomode fb_vm; + for (i = 0; i < timings->num_timings; i++) { + /* Only consider native mode */ + if (i != timings->native_mode) + continue; ret = videomode_from_timings(timings, &vm, i); if (ret < 0) - goto put_timings_node; + goto put_display_node; + ret = fb_videomode_from_videomode(&vm, &fb_vm); if (ret < 0) - goto put_timings_node; + goto put_display_node; if (!(vm.flags & DISPLAY_FLAGS_DE_HIGH)) fb_vm.sync |= FB_SYNC_OE_LOW_ACT; - if (vm.flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE) + + /* + * The PIXDATA flags of the display_flags enum are controller + * centric, e.g. NEGEDGE means drive data on negative edge. + * However, the drivers flag is display centric: Sample the + * data on negative (falling) edge. Therefore, check for the + * POSEDGE flag: + * drive on positive edge => sample on negative edge + */ + if (vm.flags & DISPLAY_FLAGS_PIXDATA_POSEDGE) fb_vm.sync |= FB_SYNC_CLK_LAT_FALL; + + if (i == timings->native_mode) { + fb_videomode_from_videomode(&vm, &native_mode); + fb_videomode_to_var(&fb_info->var, &fb_vm); + } + fb_add_videomode(&fb_vm, &fb_info->modelist); } -put_timings_node: - of_node_put(timings_np); + retval = fb_find_mode(&fb_info->var, fb_info, host->fb_mode_str, &fb_vm, + timings->num_timings, &native_mode, + fb_info->var.bits_per_pixel); + if (retval != 1) + /* save the sync value getting from dtb */ + host->sync = fb_info->var.sync; + put_display_node: if (timings) kfree(timings); @@ -1416,7 +1469,6 @@ static int mxsfb_init_fbinfo(struct mxsfb_info *host) int ret; struct fb_info *fb_info = host->fb_info; struct fb_var_screeninfo *var = &fb_info->var; - struct fb_modelist *modelist; fb_info->fbops = &mxsfb_ops; fb_info->flags = FBINFO_FLAG_DEFAULT | FBINFO_READS_FAST; @@ -1426,6 +1478,10 @@ static int mxsfb_init_fbinfo(struct mxsfb_info *host) fb_info->fix.visual = FB_VISUAL_TRUECOLOR, fb_info->fix.accel = FB_ACCEL_NONE; + ret = mxsfb_option_setup(host, fb_info); + if (ret) + return ret; + ret = mxsfb_init_fbinfo_dt(host); if (ret) return ret; @@ -1435,15 +1491,6 @@ static int mxsfb_init_fbinfo(struct mxsfb_info *host) else sprintf(fb_info->fix.id, "mxs-lcdif%d", host->id); - if (!list_empty(&fb_info->modelist)) { - /* first video mode in the modelist as default video mode */ - modelist = list_first_entry(&fb_info->modelist, - struct fb_modelist, list); - fb_videomode_to_var(var, &modelist->mode); - } - /* save the sync value getting from dtb */ - host->sync = fb_info->var.sync; - var->nonstd = 0; var->activate = FB_ACTIVATE_NOW; var->accel_flags = 0; @@ -2285,8 +2332,6 @@ static int mxsfb_probe(struct platform_device *pdev) goto fb_release; } - INIT_LIST_HEAD(&fb_info->modelist); - pm_runtime_enable(&host->pdev->dev); ret = mxsfb_init_fbinfo(host); @@ -2447,6 +2492,7 @@ static int mxsfb_suspend(struct device *pdev) host->restore_blank = saved_blank; console_unlock(); + pm_runtime_force_suspend(&host->pdev->dev); pinctrl_pm_select_sleep_state(pdev); return 0; @@ -2458,6 +2504,7 @@ static int mxsfb_resume(struct device *pdev) struct fb_info *fb_info = host->fb_info; pinctrl_pm_select_default_state(pdev); + pm_runtime_force_resume(&host->pdev->dev); console_lock(); mxsfb_blank(host->restore_blank, fb_info); |