imxfb: Add support for multiple displays

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
diff --git a/drivers/video/imxfb.c b/drivers/video/imxfb.c
index 330857a..30ae302 100644
--- a/drivers/video/imxfb.c
+++ b/drivers/video/imxfb.c
@@ -130,6 +130,10 @@
 #define LCDISR_EOF	(1<<1)
 #define LCDISR_BOF	(1<<0)
 
+/* Used fb-mode. Can be set on kernel command line, therefore file-static. */
+static const char *fb_mode;
+
+
 /*
  * These are the bitfields for each
  * display depth that we support.
@@ -146,10 +150,6 @@
 	void __iomem		*regs;
 	struct clk		*clk;
 
-	u_int			max_bpp;
-	u_int			max_xres;
-	u_int			max_yres;
-
 	/*
 	 * These are the addresses we mapped
 	 * the framebuffer memory region to.
@@ -173,6 +173,9 @@
 				cmap_static:1,
 				unused:30;
 
+	struct imx_fb_videomode *mode;
+	int			num_modes;
+
 	void (*lcd_power)(int);
 	void (*backlight_power)(int);
 };
@@ -299,6 +302,18 @@
 	return ret;
 }
 
+static const struct imx_fb_videomode *imxfb_find_mode(struct imxfb_info *fbi)
+{
+	struct imx_fb_videomode *m;
+	int i;
+
+	for (i = 0, m = &fbi->mode[0]; i < fbi->num_modes; i++, m++) {
+		if (!strcmp(m->mode.name, fb_mode))
+			return m;
+	}
+	return NULL;
+}
+
 /*
  *  imxfb_check_var():
  *    Round up in the following order: bits_per_pixel, xres,
@@ -309,35 +324,81 @@
 {
 	struct imxfb_info *fbi = info->par;
 	struct imxfb_rgb *rgb;
+	const struct imx_fb_videomode *imxfb_mode;
+	unsigned long lcd_clk;
+	unsigned long long tmp;
+	u32 pcr = 0;
 
 	if (var->xres < MIN_XRES)
 		var->xres = MIN_XRES;
 	if (var->yres < MIN_YRES)
 		var->yres = MIN_YRES;
-	if (var->xres > fbi->max_xres)
-		var->xres = fbi->max_xres;
-	if (var->yres > fbi->max_yres)
-		var->yres = fbi->max_yres;
-	var->xres_virtual = max(var->xres_virtual, var->xres);
-	var->yres_virtual = max(var->yres_virtual, var->yres);
+
+	imxfb_mode = imxfb_find_mode(fbi);
+	if (!imxfb_mode)
+		return -EINVAL;
+
+	var->xres		= imxfb_mode->mode.xres;
+	var->yres		= imxfb_mode->mode.yres;
+	var->bits_per_pixel	= imxfb_mode->bpp;
+	var->pixclock		= imxfb_mode->mode.pixclock;
+	var->hsync_len		= imxfb_mode->mode.hsync_len;
+	var->left_margin	= imxfb_mode->mode.left_margin;
+	var->right_margin	= imxfb_mode->mode.right_margin;
+	var->vsync_len		= imxfb_mode->mode.vsync_len;
+	var->upper_margin	= imxfb_mode->mode.upper_margin;
+	var->lower_margin	= imxfb_mode->mode.lower_margin;
+	var->sync		= imxfb_mode->mode.sync;
+	var->xres_virtual	= max(var->xres_virtual, var->xres);
+	var->yres_virtual	= max(var->yres_virtual, var->yres);
 
 	pr_debug("var->bits_per_pixel=%d\n", var->bits_per_pixel);
+
+	lcd_clk = clk_get_rate(fbi->clk);
+
+	tmp = var->pixclock * (unsigned long long)lcd_clk;
+
+	do_div(tmp, 1000000);
+
+	if (do_div(tmp, 1000000) > 500000)
+		tmp++;
+
+	pcr = (unsigned int)tmp;
+
+	if (--pcr > 0x3F) {
+		pcr = 0x3F;
+		printk(KERN_WARNING "Must limit pixel clock to %luHz\n",
+				lcd_clk / pcr);
+	}
+
 	switch (var->bits_per_pixel) {
 	case 32:
+		pcr |= PCR_BPIX_18;
 		rgb = &def_rgb_18;
 		break;
 	case 16:
 	default:
-		if (fbi->pcr & PCR_TFT)
+		if (cpu_is_mx1())
+			pcr |= PCR_BPIX_12;
+		else
+			pcr |= PCR_BPIX_16;
+
+		if (imxfb_mode->pcr & PCR_TFT)
 			rgb = &def_rgb_16_tft;
 		else
 			rgb = &def_rgb_16_stn;
 		break;
 	case 8:
+		pcr |= PCR_BPIX_8;
 		rgb = &def_rgb_8;
 		break;
 	}
 
+	/* add sync polarities */
+	pcr |= imxfb_mode->pcr & ~(0x3f | (7 << 25));
+
+	fbi->pcr = pcr;
+
 	/*
 	 * Copy the RGB parameters for this display
 	 * from the machine specific parameters.
@@ -394,10 +455,6 @@
 
 	writel(fbi->screen_dma, fbi->regs + LCDC_SSA);
 
-	/* physical screen start address	    */
-	writel(VPW_VPW(fbi->max_xres * fbi->max_bpp / 8 / 4),
-		fbi->regs + LCDC_VPW);
-
 	/* panning offset 0 (0 pixel offset)        */
 	writel(0x00000000, fbi->regs + LCDC_POS);
 
@@ -469,8 +526,6 @@
 static int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *info)
 {
 	struct imxfb_info *fbi = info->par;
-	unsigned int pcr, lcd_clk;
-	unsigned long long tmp;
 
 	pr_debug("var: xres=%d hslen=%d lm=%d rm=%d\n",
 		var->xres, var->hsync_len,
@@ -506,6 +561,10 @@
 			info->fix.id, var->lower_margin);
 #endif
 
+	/* physical screen start address	    */
+	writel(VPW_VPW(var->xres * var->bits_per_pixel / 8 / 4),
+		fbi->regs + LCDC_VPW);
+
 	writel(HCR_H_WIDTH(var->hsync_len - 1) |
 		HCR_H_WAIT_1(var->right_margin - 1) |
 		HCR_H_WAIT_2(var->left_margin - 3),
@@ -519,38 +578,7 @@
 	writel(SIZE_XMAX(var->xres) | SIZE_YMAX(var->yres),
 			fbi->regs + LCDC_SIZE);
 
-	lcd_clk = clk_get_rate(fbi->clk);
-	tmp = var->pixclock * (unsigned long long)lcd_clk;
-	do_div(tmp, 1000000);
-	if (do_div(tmp, 1000000) > 500000)
-		tmp++;
-	pcr = (unsigned int)tmp;
-	if (--pcr > 0x3F) {
-		pcr = 0x3F;
-		printk(KERN_WARNING "Must limit pixel clock to %uHz\n",
-				lcd_clk / pcr);
-	}
-
-	switch (var->bits_per_pixel) {
-	case 32:
-		pcr |= PCR_BPIX_18;
-		break;
-	case 16:
-	default:
-		if (cpu_is_mx1())
-			pcr |= PCR_BPIX_12;
-		else
-			pcr |= PCR_BPIX_16;
-		break;
-	case 8:
-		pcr |= PCR_BPIX_8;
-		break;
-	}
-
-	/* add sync polarities */
-	pcr |= fbi->pcr & ~(0x3f | (7 << 25));
-
-	writel(pcr, fbi->regs + LCDC_PCR);
+	writel(fbi->pcr, fbi->regs + LCDC_PCR);
 	writel(fbi->pwmr, fbi->regs + LCDC_PWMR);
 	writel(fbi->lscr1, fbi->regs + LCDC_LSCR1);
 	writel(fbi->dmacr, fbi->regs + LCDC_DMACR);
@@ -592,6 +620,8 @@
 	struct imx_fb_platform_data *pdata = pdev->dev.platform_data;
 	struct fb_info *info = dev_get_drvdata(&pdev->dev);
 	struct imxfb_info *fbi = info->par;
+	struct imx_fb_videomode *m;
+	int i;
 
 	pr_debug("%s\n",__func__);
 
@@ -620,35 +650,18 @@
 	info->fbops			= &imxfb_ops;
 	info->flags			= FBINFO_FLAG_DEFAULT |
 					  FBINFO_READS_FAST;
-
-	fbi->max_xres			= pdata->xres;
-	info->var.xres			= pdata->xres;
-	info->var.xres_virtual		= pdata->xres;
-	fbi->max_yres			= pdata->yres;
-	info->var.yres			= pdata->yres;
-	info->var.yres_virtual		= pdata->yres;
-	fbi->max_bpp			= pdata->bpp;
-	info->var.bits_per_pixel	= pdata->bpp;
-	info->var.nonstd		= pdata->nonstd;
-	info->var.pixclock		= pdata->pixclock;
-	info->var.hsync_len		= pdata->hsync_len;
-	info->var.left_margin		= pdata->left_margin;
-	info->var.right_margin		= pdata->right_margin;
-	info->var.vsync_len		= pdata->vsync_len;
-	info->var.upper_margin		= pdata->upper_margin;
-	info->var.lower_margin		= pdata->lower_margin;
-	info->var.sync			= pdata->sync;
 	info->var.grayscale		= pdata->cmap_greyscale;
 	fbi->cmap_inverse		= pdata->cmap_inverse;
 	fbi->cmap_static		= pdata->cmap_static;
-	fbi->pcr			= pdata->pcr;
 	fbi->lscr1			= pdata->lscr1;
 	fbi->dmacr			= pdata->dmacr;
 	fbi->pwmr			= pdata->pwmr;
 	fbi->lcd_power			= pdata->lcd_power;
 	fbi->backlight_power		= pdata->backlight_power;
-	info->fix.smem_len		= fbi->max_xres * fbi->max_yres *
-					  fbi->max_bpp / 8;
+
+	for (i = 0, m = &pdata->mode[0]; i < pdata->num_modes; i++, m++)
+		info->fix.smem_len = max_t(size_t, info->fix.smem_len,
+				m->mode.xres * m->mode.yres * m->bpp / 8);
 
 	return 0;
 }
@@ -659,7 +672,7 @@
 	struct fb_info *info;
 	struct imx_fb_platform_data *pdata;
 	struct resource *res;
-	int ret;
+	int ret, i;
 
 	dev_info(&pdev->dev, "i.MX Framebuffer driver\n");
 
@@ -679,6 +692,9 @@
 
 	fbi = info->par;
 
+	if (!fb_mode)
+		fb_mode = pdata->mode[0].mode.name;
+
 	platform_set_drvdata(pdev, info);
 
 	ret = imxfb_init_fbinfo(pdev);
@@ -736,6 +752,13 @@
 			goto failed_platform_init;
 	}
 
+	fbi->mode = pdata->mode;
+	fbi->num_modes = pdata->num_modes;
+
+	INIT_LIST_HEAD(&info->modelist);
+	for (i = 0; i < pdata->num_modes; i++)
+		fb_add_videomode(&pdata->mode[i].mode, &info->modelist);
+
 	/*
 	 * This makes sure that our colour bitfield
 	 * descriptors are correctly initialised.
@@ -828,8 +851,34 @@
 	},
 };
 
+static int imxfb_setup(void)
+{
+#ifndef MODULE
+	char *opt, *options = NULL;
+
+	if (fb_get_options("imxfb", &options))
+		return -ENODEV;
+
+	if (!options || !*options)
+		return 0;
+
+	while ((opt = strsep(&options, ",")) != NULL) {
+		if (!*opt)
+			continue;
+		else
+			fb_mode = opt;
+	}
+#endif
+	return 0;
+}
+
 int __init imxfb_init(void)
 {
+	int ret = imxfb_setup();
+
+	if (ret < 0)
+		return ret;
+
 	return platform_driver_probe(&imxfb_driver, imxfb_probe);
 }