diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2009-03-19 03:25:41 -0400 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2009-08-07 06:10:56 -0400 |
commit | 343684ffb793a3c371579b7bbc16724713ee5ac7 (patch) | |
tree | 148ad5f2ba60bf529732645624998798c072da90 /drivers/video/imxfb.c | |
parent | d6b515028863a912d051d371b6d71e09f2a9ff19 (diff) |
imxfb: Add support for multiple displays
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'drivers/video/imxfb.c')
-rw-r--r-- | drivers/video/imxfb.c | 191 |
1 files changed, 120 insertions, 71 deletions
diff --git a/drivers/video/imxfb.c b/drivers/video/imxfb.c index 330857a8b318..30ae3022f633 100644 --- a/drivers/video/imxfb.c +++ b/drivers/video/imxfb.c | |||
@@ -130,6 +130,10 @@ | |||
130 | #define LCDISR_EOF (1<<1) | 130 | #define LCDISR_EOF (1<<1) |
131 | #define LCDISR_BOF (1<<0) | 131 | #define LCDISR_BOF (1<<0) |
132 | 132 | ||
133 | /* Used fb-mode. Can be set on kernel command line, therefore file-static. */ | ||
134 | static const char *fb_mode; | ||
135 | |||
136 | |||
133 | /* | 137 | /* |
134 | * These are the bitfields for each | 138 | * These are the bitfields for each |
135 | * display depth that we support. | 139 | * display depth that we support. |
@@ -146,10 +150,6 @@ struct imxfb_info { | |||
146 | void __iomem *regs; | 150 | void __iomem *regs; |
147 | struct clk *clk; | 151 | struct clk *clk; |
148 | 152 | ||
149 | u_int max_bpp; | ||
150 | u_int max_xres; | ||
151 | u_int max_yres; | ||
152 | |||
153 | /* | 153 | /* |
154 | * These are the addresses we mapped | 154 | * These are the addresses we mapped |
155 | * the framebuffer memory region to. | 155 | * the framebuffer memory region to. |
@@ -173,6 +173,9 @@ struct imxfb_info { | |||
173 | cmap_static:1, | 173 | cmap_static:1, |
174 | unused:30; | 174 | unused:30; |
175 | 175 | ||
176 | struct imx_fb_videomode *mode; | ||
177 | int num_modes; | ||
178 | |||
176 | void (*lcd_power)(int); | 179 | void (*lcd_power)(int); |
177 | void (*backlight_power)(int); | 180 | void (*backlight_power)(int); |
178 | }; | 181 | }; |
@@ -299,6 +302,18 @@ static int imxfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, | |||
299 | return ret; | 302 | return ret; |
300 | } | 303 | } |
301 | 304 | ||
305 | static const struct imx_fb_videomode *imxfb_find_mode(struct imxfb_info *fbi) | ||
306 | { | ||
307 | struct imx_fb_videomode *m; | ||
308 | int i; | ||
309 | |||
310 | for (i = 0, m = &fbi->mode[0]; i < fbi->num_modes; i++, m++) { | ||
311 | if (!strcmp(m->mode.name, fb_mode)) | ||
312 | return m; | ||
313 | } | ||
314 | return NULL; | ||
315 | } | ||
316 | |||
302 | /* | 317 | /* |
303 | * imxfb_check_var(): | 318 | * imxfb_check_var(): |
304 | * Round up in the following order: bits_per_pixel, xres, | 319 | * 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) | |||
309 | { | 324 | { |
310 | struct imxfb_info *fbi = info->par; | 325 | struct imxfb_info *fbi = info->par; |
311 | struct imxfb_rgb *rgb; | 326 | struct imxfb_rgb *rgb; |
327 | const struct imx_fb_videomode *imxfb_mode; | ||
328 | unsigned long lcd_clk; | ||
329 | unsigned long long tmp; | ||
330 | u32 pcr = 0; | ||
312 | 331 | ||
313 | if (var->xres < MIN_XRES) | 332 | if (var->xres < MIN_XRES) |
314 | var->xres = MIN_XRES; | 333 | var->xres = MIN_XRES; |
315 | if (var->yres < MIN_YRES) | 334 | if (var->yres < MIN_YRES) |
316 | var->yres = MIN_YRES; | 335 | var->yres = MIN_YRES; |
317 | if (var->xres > fbi->max_xres) | 336 | |
318 | var->xres = fbi->max_xres; | 337 | imxfb_mode = imxfb_find_mode(fbi); |
319 | if (var->yres > fbi->max_yres) | 338 | if (!imxfb_mode) |
320 | var->yres = fbi->max_yres; | 339 | return -EINVAL; |
321 | var->xres_virtual = max(var->xres_virtual, var->xres); | 340 | |
322 | var->yres_virtual = max(var->yres_virtual, var->yres); | 341 | var->xres = imxfb_mode->mode.xres; |
342 | var->yres = imxfb_mode->mode.yres; | ||
343 | var->bits_per_pixel = imxfb_mode->bpp; | ||
344 | var->pixclock = imxfb_mode->mode.pixclock; | ||
345 | var->hsync_len = imxfb_mode->mode.hsync_len; | ||
346 | var->left_margin = imxfb_mode->mode.left_margin; | ||
347 | var->right_margin = imxfb_mode->mode.right_margin; | ||
348 | var->vsync_len = imxfb_mode->mode.vsync_len; | ||
349 | var->upper_margin = imxfb_mode->mode.upper_margin; | ||
350 | var->lower_margin = imxfb_mode->mode.lower_margin; | ||
351 | var->sync = imxfb_mode->mode.sync; | ||
352 | var->xres_virtual = max(var->xres_virtual, var->xres); | ||
353 | var->yres_virtual = max(var->yres_virtual, var->yres); | ||
323 | 354 | ||
324 | pr_debug("var->bits_per_pixel=%d\n", var->bits_per_pixel); | 355 | pr_debug("var->bits_per_pixel=%d\n", var->bits_per_pixel); |
356 | |||
357 | lcd_clk = clk_get_rate(fbi->clk); | ||
358 | |||
359 | tmp = var->pixclock * (unsigned long long)lcd_clk; | ||
360 | |||
361 | do_div(tmp, 1000000); | ||
362 | |||
363 | if (do_div(tmp, 1000000) > 500000) | ||
364 | tmp++; | ||
365 | |||
366 | pcr = (unsigned int)tmp; | ||
367 | |||
368 | if (--pcr > 0x3F) { | ||
369 | pcr = 0x3F; | ||
370 | printk(KERN_WARNING "Must limit pixel clock to %luHz\n", | ||
371 | lcd_clk / pcr); | ||
372 | } | ||
373 | |||
325 | switch (var->bits_per_pixel) { | 374 | switch (var->bits_per_pixel) { |
326 | case 32: | 375 | case 32: |
376 | pcr |= PCR_BPIX_18; | ||
327 | rgb = &def_rgb_18; | 377 | rgb = &def_rgb_18; |
328 | break; | 378 | break; |
329 | case 16: | 379 | case 16: |
330 | default: | 380 | default: |
331 | if (fbi->pcr & PCR_TFT) | 381 | if (cpu_is_mx1()) |
382 | pcr |= PCR_BPIX_12; | ||
383 | else | ||
384 | pcr |= PCR_BPIX_16; | ||
385 | |||
386 | if (imxfb_mode->pcr & PCR_TFT) | ||
332 | rgb = &def_rgb_16_tft; | 387 | rgb = &def_rgb_16_tft; |
333 | else | 388 | else |
334 | rgb = &def_rgb_16_stn; | 389 | rgb = &def_rgb_16_stn; |
335 | break; | 390 | break; |
336 | case 8: | 391 | case 8: |
392 | pcr |= PCR_BPIX_8; | ||
337 | rgb = &def_rgb_8; | 393 | rgb = &def_rgb_8; |
338 | break; | 394 | break; |
339 | } | 395 | } |
340 | 396 | ||
397 | /* add sync polarities */ | ||
398 | pcr |= imxfb_mode->pcr & ~(0x3f | (7 << 25)); | ||
399 | |||
400 | fbi->pcr = pcr; | ||
401 | |||
341 | /* | 402 | /* |
342 | * Copy the RGB parameters for this display | 403 | * Copy the RGB parameters for this display |
343 | * from the machine specific parameters. | 404 | * from the machine specific parameters. |
@@ -394,10 +455,6 @@ static void imxfb_enable_controller(struct imxfb_info *fbi) | |||
394 | 455 | ||
395 | writel(fbi->screen_dma, fbi->regs + LCDC_SSA); | 456 | writel(fbi->screen_dma, fbi->regs + LCDC_SSA); |
396 | 457 | ||
397 | /* physical screen start address */ | ||
398 | writel(VPW_VPW(fbi->max_xres * fbi->max_bpp / 8 / 4), | ||
399 | fbi->regs + LCDC_VPW); | ||
400 | |||
401 | /* panning offset 0 (0 pixel offset) */ | 458 | /* panning offset 0 (0 pixel offset) */ |
402 | writel(0x00000000, fbi->regs + LCDC_POS); | 459 | writel(0x00000000, fbi->regs + LCDC_POS); |
403 | 460 | ||
@@ -469,8 +526,6 @@ static struct fb_ops imxfb_ops = { | |||
469 | static int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *info) | 526 | static int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *info) |
470 | { | 527 | { |
471 | struct imxfb_info *fbi = info->par; | 528 | struct imxfb_info *fbi = info->par; |
472 | unsigned int pcr, lcd_clk; | ||
473 | unsigned long long tmp; | ||
474 | 529 | ||
475 | pr_debug("var: xres=%d hslen=%d lm=%d rm=%d\n", | 530 | pr_debug("var: xres=%d hslen=%d lm=%d rm=%d\n", |
476 | var->xres, var->hsync_len, | 531 | var->xres, var->hsync_len, |
@@ -506,6 +561,10 @@ static int imxfb_activate_var(struct fb_var_screeninfo *var, struct fb_info *inf | |||
506 | info->fix.id, var->lower_margin); | 561 | info->fix.id, var->lower_margin); |
507 | #endif | 562 | #endif |
508 | 563 | ||
564 | /* physical screen start address */ | ||
565 | writel(VPW_VPW(var->xres * var->bits_per_pixel / 8 / 4), | ||
566 | fbi->regs + LCDC_VPW); | ||
567 | |||
509 | writel(HCR_H_WIDTH(var->hsync_len - 1) | | 568 | writel(HCR_H_WIDTH(var->hsync_len - 1) | |
510 | HCR_H_WAIT_1(var->right_margin - 1) | | 569 | HCR_H_WAIT_1(var->right_margin - 1) | |
511 | HCR_H_WAIT_2(var->left_margin - 3), | 570 | 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 | |||
519 | writel(SIZE_XMAX(var->xres) | SIZE_YMAX(var->yres), | 578 | writel(SIZE_XMAX(var->xres) | SIZE_YMAX(var->yres), |
520 | fbi->regs + LCDC_SIZE); | 579 | fbi->regs + LCDC_SIZE); |
521 | 580 | ||
522 | lcd_clk = clk_get_rate(fbi->clk); | 581 | writel(fbi->pcr, fbi->regs + LCDC_PCR); |
523 | tmp = var->pixclock * (unsigned long long)lcd_clk; | ||
524 | do_div(tmp, 1000000); | ||
525 | if (do_div(tmp, 1000000) > 500000) | ||
526 | tmp++; | ||
527 | pcr = (unsigned int)tmp; | ||
528 | if (--pcr > 0x3F) { | ||
529 | pcr = 0x3F; | ||
530 | printk(KERN_WARNING "Must limit pixel clock to %uHz\n", | ||
531 | lcd_clk / pcr); | ||
532 | } | ||
533 | |||
534 | switch (var->bits_per_pixel) { | ||
535 | case 32: | ||
536 | pcr |= PCR_BPIX_18; | ||
537 | break; | ||
538 | case 16: | ||
539 | default: | ||
540 | if (cpu_is_mx1()) | ||
541 | pcr |= PCR_BPIX_12; | ||
542 | else | ||
543 | pcr |= PCR_BPIX_16; | ||
544 | break; | ||
545 | case 8: | ||
546 | pcr |= PCR_BPIX_8; | ||
547 | break; | ||
548 | } | ||
549 | |||
550 | /* add sync polarities */ | ||
551 | pcr |= fbi->pcr & ~(0x3f | (7 << 25)); | ||
552 | |||
553 | writel(pcr, fbi->regs + LCDC_PCR); | ||
554 | writel(fbi->pwmr, fbi->regs + LCDC_PWMR); | 582 | writel(fbi->pwmr, fbi->regs + LCDC_PWMR); |
555 | writel(fbi->lscr1, fbi->regs + LCDC_LSCR1); | 583 | writel(fbi->lscr1, fbi->regs + LCDC_LSCR1); |
556 | writel(fbi->dmacr, fbi->regs + LCDC_DMACR); | 584 | writel(fbi->dmacr, fbi->regs + LCDC_DMACR); |
@@ -592,6 +620,8 @@ static int __init imxfb_init_fbinfo(struct platform_device *pdev) | |||
592 | struct imx_fb_platform_data *pdata = pdev->dev.platform_data; | 620 | struct imx_fb_platform_data *pdata = pdev->dev.platform_data; |
593 | struct fb_info *info = dev_get_drvdata(&pdev->dev); | 621 | struct fb_info *info = dev_get_drvdata(&pdev->dev); |
594 | struct imxfb_info *fbi = info->par; | 622 | struct imxfb_info *fbi = info->par; |
623 | struct imx_fb_videomode *m; | ||
624 | int i; | ||
595 | 625 | ||
596 | pr_debug("%s\n",__func__); | 626 | pr_debug("%s\n",__func__); |
597 | 627 | ||
@@ -620,35 +650,18 @@ static int __init imxfb_init_fbinfo(struct platform_device *pdev) | |||
620 | info->fbops = &imxfb_ops; | 650 | info->fbops = &imxfb_ops; |
621 | info->flags = FBINFO_FLAG_DEFAULT | | 651 | info->flags = FBINFO_FLAG_DEFAULT | |
622 | FBINFO_READS_FAST; | 652 | FBINFO_READS_FAST; |
623 | |||
624 | fbi->max_xres = pdata->xres; | ||
625 | info->var.xres = pdata->xres; | ||
626 | info->var.xres_virtual = pdata->xres; | ||
627 | fbi->max_yres = pdata->yres; | ||
628 | info->var.yres = pdata->yres; | ||
629 | info->var.yres_virtual = pdata->yres; | ||
630 | fbi->max_bpp = pdata->bpp; | ||
631 | info->var.bits_per_pixel = pdata->bpp; | ||
632 | info->var.nonstd = pdata->nonstd; | ||
633 | info->var.pixclock = pdata->pixclock; | ||
634 | info->var.hsync_len = pdata->hsync_len; | ||
635 | info->var.left_margin = pdata->left_margin; | ||
636 | info->var.right_margin = pdata->right_margin; | ||
637 | info->var.vsync_len = pdata->vsync_len; | ||
638 | info->var.upper_margin = pdata->upper_margin; | ||
639 | info->var.lower_margin = pdata->lower_margin; | ||
640 | info->var.sync = pdata->sync; | ||
641 | info->var.grayscale = pdata->cmap_greyscale; | 653 | info->var.grayscale = pdata->cmap_greyscale; |
642 | fbi->cmap_inverse = pdata->cmap_inverse; | 654 | fbi->cmap_inverse = pdata->cmap_inverse; |
643 | fbi->cmap_static = pdata->cmap_static; | 655 | fbi->cmap_static = pdata->cmap_static; |
644 | fbi->pcr = pdata->pcr; | ||
645 | fbi->lscr1 = pdata->lscr1; | 656 | fbi->lscr1 = pdata->lscr1; |
646 | fbi->dmacr = pdata->dmacr; | 657 | fbi->dmacr = pdata->dmacr; |
647 | fbi->pwmr = pdata->pwmr; | 658 | fbi->pwmr = pdata->pwmr; |
648 | fbi->lcd_power = pdata->lcd_power; | 659 | fbi->lcd_power = pdata->lcd_power; |
649 | fbi->backlight_power = pdata->backlight_power; | 660 | fbi->backlight_power = pdata->backlight_power; |
650 | info->fix.smem_len = fbi->max_xres * fbi->max_yres * | 661 | |
651 | fbi->max_bpp / 8; | 662 | for (i = 0, m = &pdata->mode[0]; i < pdata->num_modes; i++, m++) |
663 | info->fix.smem_len = max_t(size_t, info->fix.smem_len, | ||
664 | m->mode.xres * m->mode.yres * m->bpp / 8); | ||
652 | 665 | ||
653 | return 0; | 666 | return 0; |
654 | } | 667 | } |
@@ -659,7 +672,7 @@ static int __init imxfb_probe(struct platform_device *pdev) | |||
659 | struct fb_info *info; | 672 | struct fb_info *info; |
660 | struct imx_fb_platform_data *pdata; | 673 | struct imx_fb_platform_data *pdata; |
661 | struct resource *res; | 674 | struct resource *res; |
662 | int ret; | 675 | int ret, i; |
663 | 676 | ||
664 | dev_info(&pdev->dev, "i.MX Framebuffer driver\n"); | 677 | dev_info(&pdev->dev, "i.MX Framebuffer driver\n"); |
665 | 678 | ||
@@ -679,6 +692,9 @@ static int __init imxfb_probe(struct platform_device *pdev) | |||
679 | 692 | ||
680 | fbi = info->par; | 693 | fbi = info->par; |
681 | 694 | ||
695 | if (!fb_mode) | ||
696 | fb_mode = pdata->mode[0].mode.name; | ||
697 | |||
682 | platform_set_drvdata(pdev, info); | 698 | platform_set_drvdata(pdev, info); |
683 | 699 | ||
684 | ret = imxfb_init_fbinfo(pdev); | 700 | ret = imxfb_init_fbinfo(pdev); |
@@ -736,6 +752,13 @@ static int __init imxfb_probe(struct platform_device *pdev) | |||
736 | goto failed_platform_init; | 752 | goto failed_platform_init; |
737 | } | 753 | } |
738 | 754 | ||
755 | fbi->mode = pdata->mode; | ||
756 | fbi->num_modes = pdata->num_modes; | ||
757 | |||
758 | INIT_LIST_HEAD(&info->modelist); | ||
759 | for (i = 0; i < pdata->num_modes; i++) | ||
760 | fb_add_videomode(&pdata->mode[i].mode, &info->modelist); | ||
761 | |||
739 | /* | 762 | /* |
740 | * This makes sure that our colour bitfield | 763 | * This makes sure that our colour bitfield |
741 | * descriptors are correctly initialised. | 764 | * descriptors are correctly initialised. |
@@ -828,8 +851,34 @@ static struct platform_driver imxfb_driver = { | |||
828 | }, | 851 | }, |
829 | }; | 852 | }; |
830 | 853 | ||
854 | static int imxfb_setup(void) | ||
855 | { | ||
856 | #ifndef MODULE | ||
857 | char *opt, *options = NULL; | ||
858 | |||
859 | if (fb_get_options("imxfb", &options)) | ||
860 | return -ENODEV; | ||
861 | |||
862 | if (!options || !*options) | ||
863 | return 0; | ||
864 | |||
865 | while ((opt = strsep(&options, ",")) != NULL) { | ||
866 | if (!*opt) | ||
867 | continue; | ||
868 | else | ||
869 | fb_mode = opt; | ||
870 | } | ||
871 | #endif | ||
872 | return 0; | ||
873 | } | ||
874 | |||
831 | int __init imxfb_init(void) | 875 | int __init imxfb_init(void) |
832 | { | 876 | { |
877 | int ret = imxfb_setup(); | ||
878 | |||
879 | if (ret < 0) | ||
880 | return ret; | ||
881 | |||
833 | return platform_driver_probe(&imxfb_driver, imxfb_probe); | 882 | return platform_driver_probe(&imxfb_driver, imxfb_probe); |
834 | } | 883 | } |
835 | 884 | ||