summaryrefslogtreecommitdiff
path: root/drivers/video/fbdev/fsl-dcu-fb.c
diff options
context:
space:
mode:
authorStefan Agner <stefan.agner@toradex.com>2014-10-01 18:50:49 +0200
committerStefan Agner <stefan.agner@toradex.com>2014-11-19 11:29:46 +0100
commit45200ae12944196bd56310bb6dd980e1bf83b3de (patch)
tree494d5ba5aad2e7e6646d63967d55d8156e96f0db /drivers/video/fbdev/fsl-dcu-fb.c
parentbd4dbda18234af959568a24c0e09a9ee62a5834e (diff)
video: fsl-dcu-fb: add video= cmdline support
Add video= cmd support through converting the display timings into a modedb and using the fb_find_mode helper. Because we need to pass the fb_info struct, we do this for every layer. This is not a clean solution but proved to work reliable.
Diffstat (limited to 'drivers/video/fbdev/fsl-dcu-fb.c')
-rw-r--r--drivers/video/fbdev/fsl-dcu-fb.c96
1 files changed, 86 insertions, 10 deletions
diff --git a/drivers/video/fbdev/fsl-dcu-fb.c b/drivers/video/fbdev/fsl-dcu-fb.c
index 2ceb44c4a5c7..7593b7f84bf4 100644
--- a/drivers/video/fbdev/fsl-dcu-fb.c
+++ b/drivers/video/fbdev/fsl-dcu-fb.c
@@ -140,7 +140,8 @@ struct dcu_fb_data {
void __iomem *reg_base;
unsigned int irq;
struct clk *clk;
- struct list_head modelist;
+ struct fb_videomode *mode_db;
+ int modecnt;
struct fb_videomode native_mode;
u32 bits_per_pixel;
bool clk_pol_negedge;
@@ -427,7 +428,6 @@ static int fsl_dcu_calc_div(struct fb_info *info)
struct mfb_info *mfbi = info->par;
struct dcu_fb_data *dcufb = mfbi->parent;
unsigned long long div;
-
div = (unsigned long long)(clk_get_rate(dcufb->clk) / 1000);
div *= info->var.pixclock;
do_div(div, 1000000000);
@@ -464,9 +464,11 @@ static void update_controller(struct fb_info *info)
if (dcufb->clk_pol_negedge)
pol |= DCU_SYN_POL_INV_PXCK_FALL;
+ /* hsync:0 => active low => HS_LOW */
if (!(var->sync & FB_SYNC_HOR_HIGH_ACT))
pol |= DCU_SYN_POL_INV_HS_LOW;
+ /* vsync:0 => active low => VS_LOW */
if (!(var->sync & FB_SYNC_VERT_HIGH_ACT))
pol |= DCU_SYN_POL_INV_VS_LOW;
@@ -801,17 +803,23 @@ static int fsl_dcu_init_modelist(struct dcu_fb_data *dcufb)
goto put_display_node;
}
+ dcufb->mode_db = devm_kzalloc(dcufb->dev, sizeof(struct fb_videomode) *
+ timings->num_timings, GFP_KERNEL);
+ if (!dcufb->mode_db) {
+ ret = -ENOMEM;
+ goto put_display_node;
+ }
+
for (i = 0; i < timings->num_timings; i++) {
struct videomode vm;
- struct fb_videomode fb_vm;
ret = videomode_from_timings(timings, &vm, i);
if (ret < 0)
- goto put_display_node;
+ goto free_dcu_mode_db;
- ret = fb_videomode_from_videomode(&vm, &fb_vm);
+ ret = fb_videomode_from_videomode(&vm, &dcufb->mode_db[i]);
if (ret < 0)
- goto put_display_node;
+ goto free_dcu_mode_db;
if (i == timings->native_mode) {
fb_videomode_from_videomode(&vm, &dcufb->native_mode);
@@ -819,15 +827,63 @@ static int fsl_dcu_init_modelist(struct dcu_fb_data *dcufb)
DISPLAY_FLAGS_PIXDATA_NEGEDGE;
}
- fb_add_videomode(&fb_vm, &dcufb->modelist);
+ dcufb->modecnt++;
}
+ of_node_put(display_np);
+ return 0;
+
+free_dcu_mode_db:
+ kfree(dcufb->mode_db);
+ dcufb->mode_db = NULL;
put_display_node:
of_node_put(display_np);
return ret;
}
-static int install_framebuffer(struct fb_info *info)
+static int parse_opt(struct dcu_fb_data *dcufb, char *this_opt, u32 *sync)
+{
+ if (!strncmp(this_opt, "hsync:", 6)) {
+ if (simple_strtoul(this_opt+6, NULL, 0))
+ *sync |= FB_SYNC_HOR_HIGH_ACT;
+ } else if (!strncmp(this_opt, "vsync:", 6)) {
+ if (simple_strtoul(this_opt+6, NULL, 0))
+ *sync |= FB_SYNC_VERT_HIGH_ACT;
+ } else if (!strncmp(this_opt, "pixclockpol:", 12))
+ dcufb->clk_pol_negedge =
+ !!simple_strtoul(this_opt+12, NULL, 0);
+ else
+ return -EINVAL;
+
+ return 0;
+}
+
+static int fsl_dcu_parse_options(struct dcu_fb_data *dcufb,
+ struct fb_info *info, char *option)
+{
+ char *this_opt;
+ int ret = 0;
+ u32 sync = 0;
+
+ while ((this_opt = strsep(&option, ",")) != NULL) {
+ /* Parse driver specific arguments */
+ if (parse_opt(dcufb, this_opt, &sync) == 0)
+ continue;
+
+ /* No valid driver specific argument, has to be mode */
+ ret = fb_find_mode(&info->var, info, this_opt, dcufb->mode_db,
+ dcufb->modecnt, &dcufb->native_mode,
+ dcufb->bits_per_pixel);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Overwrite from command line */
+ info->var.sync = sync;
+ return 0;
+}
+
+static int install_framebuffer(struct fb_info *info, char *option)
{
struct mfb_info *mfbi = info->par;
struct dcu_fb_data *dcufb = mfbi->parent;
@@ -846,6 +902,11 @@ static int install_framebuffer(struct fb_info *info)
fb_videomode_to_var(&info->var, mode);
info->var.bits_per_pixel = dcufb->bits_per_pixel;
+ /* Parse DCU option for every layer... */
+ ret = fsl_dcu_parse_options(dcufb, info, option);
+ if (ret < 0)
+ return ret;
+
fsl_dcu_check_var(&info->var, info);
ret = register_framebuffer(info);
if (ret < 0) {
@@ -947,6 +1008,15 @@ static int fsl_dcu_probe(struct platform_device *pdev)
struct resource *res;
int ret = 0;
int i;
+ char *option = NULL;
+
+ fb_get_options("dcufb", &option);
+
+ if (option != NULL) {
+ dev_info(&pdev->dev, "using cmd options: %s\n", option);
+ if (!strcmp(option, "off"))
+ return -ENODEV;
+ }
dcufb = devm_kzalloc(&pdev->dev,
sizeof(struct dcu_fb_data), GFP_KERNEL);
@@ -1000,7 +1070,6 @@ static int fsl_dcu_probe(struct platform_device *pdev)
pm_runtime_enable(dcufb->dev);
pm_runtime_get_sync(dcufb->dev);
- INIT_LIST_HEAD(&dcufb->modelist);
ret = fsl_dcu_init_modelist(dcufb);
if (ret)
goto failed_alloc_framebuffer;
@@ -1019,7 +1088,7 @@ static int fsl_dcu_probe(struct platform_device *pdev)
memcpy(mfbi, &mfb_template[i], sizeof(struct mfb_info));
mfbi->parent = dcufb;
- ret = install_framebuffer(dcufb->fsl_dcu_info[i]);
+ ret = install_framebuffer(dcufb->fsl_dcu_info[i], option);
if (ret) {
dev_err(&pdev->dev,
"could not register framebuffer %d\n", i);
@@ -1027,6 +1096,13 @@ static int fsl_dcu_probe(struct platform_device *pdev)
}
}
+ if (option != NULL) {
+ ret = fsl_dcu_parse_options(dcufb, dcufb->fsl_dcu_info[0],
+ option);
+ if (ret < 0)
+ goto failed_alloc_framebuffer;
+ }
+
reset_total_layers(mfbi->parent);
return 0;