diff options
-rw-r--r-- | drivers/video/sh_mobile_hdmi.c | 138 |
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 | ||
614 | static 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 | |||
614 | static int sh_hdmi_read_edid(struct sh_hdmi *hdmi) | 634 | static 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) || |