aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGuennadi Liakhovetski <g.liakhovetski@gmx.de>2010-10-15 03:54:04 -0400
committerPaul Mundt <lethal@linux-sh.org>2010-10-15 06:00:57 -0400
commitf1198d1ea1c1fe7a0d619c5cf7049efe6205e527 (patch)
treecff5525cc1b1323b0d29fedb1711acaf96283e54
parent6031f347389c84066a3ecb55e98782a63e2d7589 (diff)
fbdev: sh_mobile_hdmi: add support for the "video=" kernel command line option
Add support for specifying video modes on the kernel command line. Mode selection priorities are also changed such, that only exact matches of specified modes with monitor modes from EDID are accepted, at least in width and height. If none found - fall back to framebuffer default setting, if available. Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de> Signed-off-by: Paul Mundt <lethal@linux-sh.org>
-rw-r--r--drivers/video/sh_mobile_hdmi.c138
1 files changed, 88 insertions, 50 deletions
diff --git a/drivers/video/sh_mobile_hdmi.c b/drivers/video/sh_mobile_hdmi.c
index 94a94fde2c67..f0ff2848700c 100644
--- a/drivers/video/sh_mobile_hdmi.c
+++ b/drivers/video/sh_mobile_hdmi.c
@@ -611,14 +611,39 @@ static void sh_hdmi_configure(struct sh_hdmi *hdmi)
611 hdmi_write(hdmi, 0x40, HDMI_SYSTEM_CTRL); 611 hdmi_write(hdmi, 0x40, HDMI_SYSTEM_CTRL);
612} 612}
613 613
614static unsigned long sh_hdmi_rate_error(struct sh_hdmi *hdmi,
615 const struct fb_videomode *mode)
616{
617 long target = PICOS2KHZ(mode->pixclock) * 1000,
618 rate = clk_round_rate(hdmi->hdmi_clk, target);
619 unsigned long rate_error = rate > 0 ? abs(rate - target) : ULONG_MAX;
620
621 dev_dbg(hdmi->dev, "%u-%u-%u-%u x %u-%u-%u-%u\n",
622 mode->left_margin, mode->xres,
623 mode->right_margin, mode->hsync_len,
624 mode->upper_margin, mode->yres,
625 mode->lower_margin, mode->vsync_len);
626
627 dev_dbg(hdmi->dev, "\t@%lu(+/-%lu)Hz, e=%lu / 1000, r=%uHz\n", target,
628 rate_error, rate_error ? 10000 / (10 * target / rate_error) : 0,
629 mode->refresh);
630
631 return rate_error;
632}
633
614static int sh_hdmi_read_edid(struct sh_hdmi *hdmi) 634static int sh_hdmi_read_edid(struct sh_hdmi *hdmi)
615{ 635{
616 struct fb_var_screeninfo tmpvar; 636 struct fb_var_screeninfo tmpvar;
617 /* TODO: When we are ready to use EDID, use this to fill &hdmi->var */
618 struct fb_var_screeninfo *var = &tmpvar; 637 struct fb_var_screeninfo *var = &tmpvar;
619 const struct fb_videomode *mode, *found = NULL; 638 const struct fb_videomode *mode, *found = NULL;
620 int i; 639 struct fb_info *info = hdmi->info;
640 struct fb_modelist *modelist = NULL;
641 unsigned int f_width = 0, f_height = 0, f_refresh = 0;
642 unsigned long found_rate_error = ULONG_MAX; /* silly compiler... */
643 bool exact_match = false;
621 u8 edid[128]; 644 u8 edid[128];
645 char *forced;
646 int i;
622 647
623 /* Read EDID */ 648 /* Read EDID */
624 dev_dbg(hdmi->dev, "Read back EDID code:"); 649 dev_dbg(hdmi->dev, "Read back EDID code:");
@@ -639,69 +664,82 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi)
639 664
640 fb_edid_to_monspecs(edid, &hdmi->monspec); 665 fb_edid_to_monspecs(edid, &hdmi->monspec);
641 666
642 /* First look for an exact match */ 667 fb_get_options("sh_mobile_lcdc", &forced);
643 for (i = 0, mode = hdmi->monspec.modedb; i < hdmi->monspec.modedb_len; 668 if (forced && *forced) {
669 /* Only primitive parsing so far */
670 i = sscanf(forced, "%ux%u@%u",
671 &f_width, &f_height, &f_refresh);
672 if (i < 2) {
673 f_width = 0;
674 f_height = 0;
675 }
676 dev_dbg(hdmi->dev, "Forced mode %ux%u@%uHz\n",
677 f_width, f_height, f_refresh);
678 }
679
680 /* Walk monitor modes to find the best or the exact match */
681 for (i = 0, mode = hdmi->monspec.modedb;
682 f_width && f_height && i < hdmi->monspec.modedb_len && !exact_match;
644 i++, mode++) { 683 i++, mode++) {
645 dev_dbg(hdmi->dev, "%u-%u-%u-%u x %u-%u-%u-%u @ %lu kHz monitor detected\n", 684 unsigned long rate_error = sh_hdmi_rate_error(hdmi, mode);
646 mode->left_margin, mode->xres, 685
647 mode->right_margin, mode->hsync_len, 686 /* No interest in unmatching modes */
648 mode->upper_margin, mode->yres, 687 if (f_width != mode->xres || f_height != mode->yres)
649 mode->lower_margin, mode->vsync_len, 688 continue;
650 PICOS2KHZ(mode->pixclock)); 689 if (f_refresh == mode->refresh || (!f_refresh && !rate_error))
651 if (!found && hdmi->info) { 690 /*
652 fb_videomode_to_var(var, mode); 691 * Exact match if either the refresh rate matches or it
653 found = fb_match_mode(var, &hdmi->info->modelist); 692 * hasn't been specified and we've found a mode, for
693 * which we can configure the clock precisely
694 */
695 exact_match = true;
696 else if (found && found_rate_error <= rate_error)
654 /* 697 /*
655 * If an exact match found, we're good to bail out, but 698 * We otherwise search for the closest matching clock
656 * continue to print out all modes 699 * rate - either if no refresh rate has been specified
700 * or we cannot find an exactly matching one
657 */ 701 */
702 continue;
703
704 /* Check if supported: sufficient fb memory, supported clock-rate */
705 fb_videomode_to_var(var, mode);
706
707 if (info && info->fbops->fb_check_var &&
708 info->fbops->fb_check_var(var, info)) {
709 exact_match = false;
710 continue;
658 } 711 }
712
713 found = mode;
714 found_rate_error = rate_error;
659 } 715 }
660 716
661 /* 717 /*
662 * The monitor might also work with a mode, that is smaller, than one of 718 * TODO 1: if no ->info is present, postpone running the config until
663 * its modes, use the first (default) one for this 719 * after ->info first gets registered.
720 * TODO 2: consider registering the HDMI platform device from the LCDC
721 * driver, and passing ->info with HDMI platform data.
664 */ 722 */
665 if (!found && hdmi->info && hdmi->monspec.modedb_len) { 723 if (info && !found) {
666 struct fb_modelist *modelist; 724 modelist = hdmi->info->modelist.next &&
667 unsigned int min_err = UINT_MAX, err; 725 !list_empty(&hdmi->info->modelist) ?
668 const struct fb_videomode *mon_mode = hdmi->monspec.modedb; 726 list_entry(hdmi->info->modelist.next,
669 727 struct fb_modelist, list) :
670 list_for_each_entry(modelist, &hdmi->info->modelist, list) { 728 NULL;
671 mode = &modelist->mode; 729
672 dev_dbg(hdmi->dev, "matching %ux%u to %ux%u\n", mode->xres, mode->yres, 730 if (modelist) {
673 mon_mode->xres, mon_mode->yres); 731 found = &modelist->mode;
674 if (mode->xres <= mon_mode->xres && mode->yres <= mon_mode->yres) { 732 found_rate_error = sh_hdmi_rate_error(hdmi, found);
675 err = mon_mode->xres - mode->xres + mon_mode->yres - mode->yres;
676 if (!err) {
677 found = mode;
678 break;
679 }
680 if (err < min_err) {
681 found = mode;
682 min_err = err;
683 }
684 }
685 } 733 }
686 } 734 }
687 735
688 /* Nothing suitable specified by the platform: use monitor's first mode */
689 if (!found && hdmi->monspec.modedb_len)
690 found = hdmi->monspec.modedb;
691
692 /* No valid timing info in EDID - last resort: use platform default mode */
693 if (!found && hdmi->info) {
694 struct fb_modelist *modelist = list_entry(hdmi->info->modelist.next,
695 struct fb_modelist, list);
696 found = &modelist->mode;
697 }
698
699 /* No cookie today */ 736 /* No cookie today */
700 if (!found) 737 if (!found)
701 return -ENXIO; 738 return -ENXIO;
702 739
703 dev_dbg(hdmi->dev, "best \"%s\" %ux%u@%ups\n", found->name, 740 dev_info(hdmi->dev, "Using %s mode %ux%u@%uHz (%luHz), clock error %luHz\n",
704 found->xres, found->yres, found->pixclock); 741 modelist ? "default" : "EDID", found->xres, found->yres,
742 found->refresh, PICOS2KHZ(found->pixclock) * 1000, found_rate_error);
705 743
706 if ((found->xres == 720 && found->yres == 480) || 744 if ((found->xres == 720 && found->yres == 480) ||
707 (found->xres == 1280 && found->yres == 720) || 745 (found->xres == 1280 && found->yres == 720) ||