summaryrefslogtreecommitdiff
path: root/drivers/video/fbdev/mxsfb.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/video/fbdev/mxsfb.c')
-rw-r--r--drivers/video/fbdev/mxsfb.c139
1 files changed, 103 insertions, 36 deletions
diff --git a/drivers/video/fbdev/mxsfb.c b/drivers/video/fbdev/mxsfb.c
index b826c17702ed..29b851dfafec 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)
@@ -477,7 +478,7 @@ static const struct fb_bitfield def_rgb444[] = {
.length = 0,
}
};
-#endif
+#endif /* CONFIG_FB_MXC_OVERLAY */
static const struct fb_bitfield def_rgb666[] = {
[RED] = {
@@ -685,7 +686,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) {
@@ -1302,6 +1302,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;
@@ -1309,12 +1344,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");
@@ -1377,34 +1414,67 @@ 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;
- }
-
- for (i = 0; i < of_get_child_count(timings_np); i++) {
- struct videomode vm;
- struct fb_videomode fb_vm;
+ INIT_LIST_HEAD(&fb_info->modelist);
+ for (i = 0; i < timings->num_timings; i++) {
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) {
+ native_mode = fb_vm;
+ 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;
+
+ switch (retval) {
+ case 0:
+ dev_err(dev, "fb_find_mode can't find a "
+ "suitable timing\n");
+ break;
+ case 1:
+ dev_info(dev, "Using timings from kernel parameters\n");
+ break;
+ case 2:
+ dev_info(dev, "Using timings from kernel parameters "
+ "and ignoring refresh rate\n");
+ break;
+ case 3:
+ dev_info(dev, "Using timings from devicetree\n");
+ break;
+ case 4:
+ dev_warn(dev, "Falling back to any valid timing\n");
+ default:
+ break;
+ }
+
put_display_node:
if (timings)
kfree(timings);
@@ -1417,7 +1487,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;
@@ -1427,6 +1496,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;
@@ -1436,15 +1509,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;
@@ -2177,11 +2241,14 @@ static void mxsfb_overlay_suspend(struct mxsfb_info *fbi)
clk_disable_pix(fbi);
}
}
-#endif
+#endif /* CONFIG_PM_SLEEP */
-#else
+#else /* CONFIG_FB_MXC_OVERLAY */
static void mxsfb_overlay_init(struct mxsfb_info *fbi) {}
static void mxsfb_overlay_exit(struct mxsfb_info *fbi) {}
+#endif /* CONFIG_FB_MXC_OVERLAY */
+
+#if !(defined(CONFIG_FB_MXC_OVERLAY) && defined(CONFIG_PM_SLEEP))
static void mxsfb_overlay_resume(struct mxsfb_info *fbi) {}
static void mxsfb_overlay_suspend(struct mxsfb_info *fbi) {}
#endif
@@ -2291,8 +2358,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);
@@ -2438,7 +2503,7 @@ static int mxsfb_runtime_resume(struct device *dev)
return 0;
}
-#endif
+#endif /* CONFIG_PM */
#ifdef CONFIG_PM_SLEEP
static int mxsfb_suspend(struct device *pdev)
@@ -2455,6 +2520,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;
@@ -2466,6 +2532,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_overlay_resume(host);
@@ -2475,7 +2542,7 @@ static int mxsfb_resume(struct device *pdev)
return 0;
}
-#endif
+#endif /* CONFIG_PM_SLEEP */
static const struct dev_pm_ops mxsfb_pm_ops = {
SET_RUNTIME_PM_OPS(mxsfb_runtime_suspend, mxsfb_runtime_resume, NULL)