Commit 343684ff authored by Sascha Hauer's avatar Sascha Hauer
Browse files

imxfb: Add support for multiple displays

parent d6b51502
Loading
Loading
Loading
Loading
+22 −14
Original line number Diff line number Diff line
@@ -183,20 +183,29 @@ void lcd_power(int on)
		__raw_writew(PBC_BCTRL1_LCDON, PBC_BCTRL1_CLEAR_REG);
}

static struct imx_fb_platform_data mx27ads_fb_data = {
	.pixclock	= 188679,
static struct imx_fb_videomode mx27ads_modes[] = {
	{
		.mode = {
			.name		= "Sharp-LQ035Q7",
			.refresh	= 60,
			.xres		= 240,
			.yres		= 320,

	.bpp		= 16,
			.pixclock	= 188679, /* in ps (5.3MHz) */
			.hsync_len	= 1,
			.left_margin	= 9,
			.right_margin	= 16,

			.vsync_len	= 1,
			.upper_margin	= 7,
			.lower_margin	= 9,
	.fixed_screen_cpu = 0,
		},
		.bpp		= 16,
		.pcr		= 0xFB008BC0,
	},
};

static struct imx_fb_platform_data mx27ads_fb_data = {
	.mode = mx27ads_modes,
	.num_modes = ARRAY_SIZE(mx27ads_modes),

	/*
	 * - HSYNC active high
@@ -207,7 +216,6 @@ static struct imx_fb_platform_data mx27ads_fb_data = {
	 * - data enable low active
	 * - enable sharp mode
	 */
	.pcr		= 0xFB008BC0,
	.pwmr		= 0x00A903FF,
	.lscr1		= 0x00120300,
	.dmacr		= 0x00020010,
+56 −27
Original line number Diff line number Diff line
@@ -125,25 +125,21 @@ static struct imxmmc_platform_data sdhc_pdata = {
	.exit = pcm970_sdhc2_exit,
};

/*
 * Connected is a portrait Sharp-QVGA display
 * of type: LQ035Q7DH06
 */
static struct imx_fb_platform_data pcm038_fb_data = {
	.pixclock	= 188679, /* in ps (5.3MHz) */
static struct imx_fb_videomode pcm970_modes[] = {
	{
		.mode = {
			.name		= "Sharp-LQ035Q7",
			.refresh	= 60,
			.xres		= 240,
			.yres		= 320,

	.bpp		= 16,
			.pixclock	= 188679, /* in ps (5.3MHz) */
			.hsync_len	= 7,
			.left_margin	= 5,
			.right_margin	= 16,

			.vsync_len	= 1,
			.upper_margin	= 7,
			.lower_margin	= 9,
	.fixed_screen_cpu = 0,

		},
		/*
		 * - HSYNC active high
		 * - VSYNC active high
@@ -153,7 +149,40 @@ static struct imx_fb_platform_data pcm038_fb_data = {
		 * - data enable low active
		 * - enable sharp mode
		 */
	.pcr		= 0xFA0080C0,
		.pcr		= 0xF00080C0,
		.bpp		= 16,
	}, {
		.mode = {
			.name		= "TX090",
			.refresh	= 60,
			.xres		= 240,
			.yres		= 320,
			.pixclock	= 38255,
			.left_margin	= 144,
			.right_margin	= 0,
			.upper_margin	= 7,
			.lower_margin	= 40,
			.hsync_len	= 96,
			.vsync_len	= 1,
		},
		/*
		 * - HSYNC active low (1 << 22)
		 * - VSYNC active low (1 << 23)
		 * - clk notenabled while idle
		 * - clock not inverted
		 * - data not inverted
		 * - data enable low active
		 * - enable sharp mode
		 */
		.pcr = 0xF0008080 | (1<<22) | (1<<23) | (1<<19),
		.bpp = 32,
	},
};

static struct imx_fb_platform_data pcm038_fb_data = {
	.mode = pcm970_modes,
	.num_modes = ARRAY_SIZE(pcm970_modes),

	.pwmr		= 0x00A903FF,
	.lscr1		= 0x00120300,
	.dmacr		= 0x00020010,
+10 −16
Original line number Diff line number Diff line
@@ -2,6 +2,8 @@
 * This structure describes the machine which we are running on.
 */

#include <linux/fb.h>

#define PCR_TFT		(1 << 31)
#define PCR_COLOR	(1 << 30)
#define PCR_PBSIZ_1	(0 << 28)
@@ -47,29 +49,21 @@
#define DMACR_HM(x)	(((x) & 0xf) << 16)
#define DMACR_TM(x)	((x) & 0xf)

struct imx_fb_platform_data {
	u_long		pixclock;

	u_short		xres;
	u_short		yres;

	u_int		nonstd;
	u_char		bpp;
	u_char		hsync_len;
	u_char		left_margin;
	u_char		right_margin;
struct imx_fb_videomode {
	struct fb_videomode mode;
	u32 pcr;
	unsigned char	bpp;
};

	u_char		vsync_len;
	u_char		upper_margin;
	u_char		lower_margin;
	u_char		sync;
struct imx_fb_platform_data {
	struct imx_fb_videomode *mode;
	int		num_modes;

	u_int		cmap_greyscale:1,
			cmap_inverse:1,
			cmap_static:1,
			unused:29;

	u_int		pcr;
	u_int		pwmr;
	u_int		lscr1;
	u_int		dmacr;
+120 −71
Original line number Diff line number Diff line
@@ -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 @@ struct imxfb_info {
	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 @@ struct imxfb_info {
				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 @@ static int imxfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
	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 @@ static int imxfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
	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;

	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 @@ static void imxfb_enable_controller(struct imxfb_info *fbi)

	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 struct fb_ops imxfb_ops = {
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 @@ static int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *inf
			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 @@ static int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *inf
	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 @@ static int __init imxfb_init_fbinfo(struct platform_device *pdev)
	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 @@ static int __init imxfb_init_fbinfo(struct platform_device *pdev)
	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 @@ static int __init imxfb_probe(struct platform_device *pdev)
	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 @@ static int __init imxfb_probe(struct platform_device *pdev)

	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 @@ static int __init imxfb_probe(struct platform_device *pdev)
			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 struct platform_driver imxfb_driver = {
	},
};

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);
}