diff options
author | Guennadi Liakhovetski <g.liakhovetski@gmx.de> | 2010-11-02 07:27:16 -0400 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2010-11-10 03:23:54 -0500 |
commit | c36940e678fc30779c99246c034deca1fed61ae4 (patch) | |
tree | 88158e04b5b516a35965dad1f58686d0be735844 /drivers/video/sh_mobile_hdmi.c | |
parent | 5fd284e6cd39f731db86dfd2440553365d5fad4d (diff) |
fbdev: sh_mobile_hdmi: add support for more precise HDMI clock configuration
The HDMI clock has to be reconfigured for different video modes. However, the
precision of the supplying SoC clock on SH-Mobile systems is often
insufficient. This patch allows to additionally reconfigure the parent clock
to achieve the optimal HDMI clock frequency, in case this is supported by the
platform.
Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'drivers/video/sh_mobile_hdmi.c')
-rw-r--r-- | drivers/video/sh_mobile_hdmi.c | 112 |
1 files changed, 68 insertions, 44 deletions
diff --git a/drivers/video/sh_mobile_hdmi.c b/drivers/video/sh_mobile_hdmi.c index d7df10315d8d..ef41c215abae 100644 --- a/drivers/video/sh_mobile_hdmi.c +++ b/drivers/video/sh_mobile_hdmi.c | |||
@@ -685,11 +685,21 @@ static void sh_hdmi_configure(struct sh_hdmi *hdmi) | |||
685 | } | 685 | } |
686 | 686 | ||
687 | static unsigned long sh_hdmi_rate_error(struct sh_hdmi *hdmi, | 687 | static unsigned long sh_hdmi_rate_error(struct sh_hdmi *hdmi, |
688 | const struct fb_videomode *mode) | 688 | const struct fb_videomode *mode, |
689 | unsigned long *hdmi_rate, unsigned long *parent_rate) | ||
689 | { | 690 | { |
690 | long target = PICOS2KHZ(mode->pixclock) * 1000, | 691 | unsigned long target = PICOS2KHZ(mode->pixclock) * 1000, rate_error; |
691 | rate = clk_round_rate(hdmi->hdmi_clk, target); | 692 | struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; |
692 | unsigned long rate_error = rate > 0 ? abs(rate - target) : ULONG_MAX; | 693 | |
694 | *hdmi_rate = clk_round_rate(hdmi->hdmi_clk, target); | ||
695 | if ((long)*hdmi_rate < 0) | ||
696 | *hdmi_rate = clk_get_rate(hdmi->hdmi_clk); | ||
697 | |||
698 | rate_error = (long)*hdmi_rate > 0 ? abs(*hdmi_rate - target) : ULONG_MAX; | ||
699 | if (rate_error && pdata->clk_optimize_parent) | ||
700 | rate_error = pdata->clk_optimize_parent(target, hdmi_rate, parent_rate); | ||
701 | else if (clk_get_parent(hdmi->hdmi_clk)) | ||
702 | *parent_rate = clk_get_rate(clk_get_parent(hdmi->hdmi_clk)); | ||
693 | 703 | ||
694 | dev_dbg(hdmi->dev, "%u-%u-%u-%u x %u-%u-%u-%u\n", | 704 | dev_dbg(hdmi->dev, "%u-%u-%u-%u x %u-%u-%u-%u\n", |
695 | mode->left_margin, mode->xres, | 705 | mode->left_margin, mode->xres, |
@@ -697,14 +707,15 @@ static unsigned long sh_hdmi_rate_error(struct sh_hdmi *hdmi, | |||
697 | mode->upper_margin, mode->yres, | 707 | mode->upper_margin, mode->yres, |
698 | mode->lower_margin, mode->vsync_len); | 708 | mode->lower_margin, mode->vsync_len); |
699 | 709 | ||
700 | dev_dbg(hdmi->dev, "\t@%lu(+/-%lu)Hz, e=%lu / 1000, r=%uHz\n", target, | 710 | dev_dbg(hdmi->dev, "\t@%lu(+/-%lu)Hz, e=%lu / 1000, r=%uHz, p=%luHz\n", target, |
701 | rate_error, rate_error ? 10000 / (10 * target / rate_error) : 0, | 711 | rate_error, rate_error ? 10000 / (10 * target / rate_error) : 0, |
702 | mode->refresh); | 712 | mode->refresh, *parent_rate); |
703 | 713 | ||
704 | return rate_error; | 714 | return rate_error; |
705 | } | 715 | } |
706 | 716 | ||
707 | static int sh_hdmi_read_edid(struct sh_hdmi *hdmi) | 717 | static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate, |
718 | unsigned long *parent_rate) | ||
708 | { | 719 | { |
709 | struct fb_var_screeninfo tmpvar; | 720 | struct fb_var_screeninfo tmpvar; |
710 | struct fb_var_screeninfo *var = &tmpvar; | 721 | struct fb_var_screeninfo *var = &tmpvar; |
@@ -754,11 +765,14 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi) | |||
754 | for (i = 0, mode = hdmi->monspec.modedb; | 765 | for (i = 0, mode = hdmi->monspec.modedb; |
755 | f_width && f_height && i < hdmi->monspec.modedb_len && !exact_match; | 766 | f_width && f_height && i < hdmi->monspec.modedb_len && !exact_match; |
756 | i++, mode++) { | 767 | i++, mode++) { |
757 | unsigned long rate_error = sh_hdmi_rate_error(hdmi, mode); | 768 | unsigned long rate_error; |
758 | 769 | ||
759 | /* No interest in unmatching modes */ | 770 | /* No interest in unmatching modes */ |
760 | if (f_width != mode->xres || f_height != mode->yres) | 771 | if (f_width != mode->xres || f_height != mode->yres) |
761 | continue; | 772 | continue; |
773 | |||
774 | rate_error = sh_hdmi_rate_error(hdmi, mode, hdmi_rate, parent_rate); | ||
775 | |||
762 | if (f_refresh == mode->refresh || (!f_refresh && !rate_error)) | 776 | if (f_refresh == mode->refresh || (!f_refresh && !rate_error)) |
763 | /* | 777 | /* |
764 | * Exact match if either the refresh rate matches or it | 778 | * Exact match if either the refresh rate matches or it |
@@ -802,7 +816,7 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi) | |||
802 | 816 | ||
803 | if (modelist) { | 817 | if (modelist) { |
804 | found = &modelist->mode; | 818 | found = &modelist->mode; |
805 | found_rate_error = sh_hdmi_rate_error(hdmi, found); | 819 | found_rate_error = sh_hdmi_rate_error(hdmi, found, hdmi_rate, parent_rate); |
806 | } | 820 | } |
807 | } | 821 | } |
808 | 822 | ||
@@ -810,10 +824,6 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi) | |||
810 | if (!found) | 824 | if (!found) |
811 | return -ENXIO; | 825 | return -ENXIO; |
812 | 826 | ||
813 | dev_info(hdmi->dev, "Using %s mode %ux%u@%uHz (%luHz), clock error %luHz\n", | ||
814 | modelist ? "default" : "EDID", found->xres, found->yres, | ||
815 | found->refresh, PICOS2KHZ(found->pixclock) * 1000, found_rate_error); | ||
816 | |||
817 | if ((found->xres == 720 && found->yres == 480) || | 827 | if ((found->xres == 720 && found->yres == 480) || |
818 | (found->xres == 1280 && found->yres == 720) || | 828 | (found->xres == 1280 && found->yres == 720) || |
819 | (found->xres == 1920 && found->yres == 1080)) | 829 | (found->xres == 1920 && found->yres == 1080)) |
@@ -821,6 +831,11 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi) | |||
821 | else | 831 | else |
822 | hdmi->preprogrammed_mode = false; | 832 | hdmi->preprogrammed_mode = false; |
823 | 833 | ||
834 | dev_dbg(hdmi->dev, "Using %s %s mode %ux%u@%uHz (%luHz), clock error %luHz\n", | ||
835 | modelist ? "default" : "EDID", hdmi->preprogrammed_mode ? "VIC" : "external", | ||
836 | found->xres, found->yres, found->refresh, | ||
837 | PICOS2KHZ(found->pixclock) * 1000, found_rate_error); | ||
838 | |||
824 | fb_videomode_to_var(&hdmi->var, found); | 839 | fb_videomode_to_var(&hdmi->var, found); |
825 | sh_hdmi_external_video_param(hdmi); | 840 | sh_hdmi_external_video_param(hdmi); |
826 | 841 | ||
@@ -972,39 +987,38 @@ static bool sh_hdmi_must_reconfigure(struct sh_hdmi *hdmi) | |||
972 | 987 | ||
973 | /** | 988 | /** |
974 | * sh_hdmi_clk_configure() - set HDMI clock frequency and enable the clock | 989 | * sh_hdmi_clk_configure() - set HDMI clock frequency and enable the clock |
975 | * @hdmi: driver context | 990 | * @hdmi: driver context |
976 | * @pixclock: pixel clock period in picoseconds | 991 | * @hdmi_rate: HDMI clock frequency in Hz |
977 | * return: configured positive rate if successful | 992 | * @parent_rate: if != 0 - set parent clock rate for optimal precision |
978 | * 0 if couldn't set the rate, but managed to enable the clock | 993 | * return: configured positive rate if successful |
979 | * negative error, if couldn't enable the clock | 994 | * 0 if couldn't set the rate, but managed to enable the |
995 | * clock, negative error, if couldn't enable the clock | ||
980 | */ | 996 | */ |
981 | static long sh_hdmi_clk_configure(struct sh_hdmi *hdmi, unsigned long pixclock) | 997 | static long sh_hdmi_clk_configure(struct sh_hdmi *hdmi, unsigned long hdmi_rate, |
998 | unsigned long parent_rate) | ||
982 | { | 999 | { |
983 | long rate; | 1000 | struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; |
984 | int ret; | 1001 | int ret; |
985 | 1002 | ||
986 | rate = PICOS2KHZ(pixclock) * 1000; | 1003 | if (parent_rate && clk_get_parent(hdmi->hdmi_clk)) { |
987 | rate = clk_round_rate(hdmi->hdmi_clk, rate); | 1004 | ret = clk_set_rate(clk_get_parent(hdmi->hdmi_clk), parent_rate); |
988 | if (rate > 0) { | ||
989 | ret = clk_set_rate(hdmi->hdmi_clk, rate); | ||
990 | if (ret < 0) { | 1005 | if (ret < 0) { |
991 | dev_warn(hdmi->dev, "Cannot set rate %ld: %d\n", rate, ret); | 1006 | dev_warn(hdmi->dev, "Cannot set parent rate %ld: %d\n", parent_rate, ret); |
992 | rate = 0; | 1007 | hdmi_rate = clk_round_rate(hdmi->hdmi_clk, hdmi_rate); |
993 | } else { | 1008 | } else { |
994 | dev_dbg(hdmi->dev, "HDMI set frequency %lu\n", rate); | 1009 | dev_dbg(hdmi->dev, "HDMI set parent frequency %lu\n", parent_rate); |
995 | } | 1010 | } |
996 | } else { | ||
997 | rate = 0; | ||
998 | dev_warn(hdmi->dev, "Cannot get suitable rate: %ld\n", rate); | ||
999 | } | 1011 | } |
1000 | 1012 | ||
1001 | ret = clk_enable(hdmi->hdmi_clk); | 1013 | ret = clk_set_rate(hdmi->hdmi_clk, hdmi_rate); |
1002 | if (ret < 0) { | 1014 | if (ret < 0) { |
1003 | dev_err(hdmi->dev, "Cannot enable clock: %d\n", ret); | 1015 | dev_warn(hdmi->dev, "Cannot set rate %ld: %d\n", hdmi_rate, ret); |
1004 | return ret; | 1016 | hdmi_rate = 0; |
1017 | } else { | ||
1018 | dev_dbg(hdmi->dev, "HDMI set frequency %lu\n", hdmi_rate); | ||
1005 | } | 1019 | } |
1006 | 1020 | ||
1007 | return rate; | 1021 | return hdmi_rate; |
1008 | } | 1022 | } |
1009 | 1023 | ||
1010 | /* Hotplug interrupt occurred, read EDID */ | 1024 | /* Hotplug interrupt occurred, read EDID */ |
@@ -1024,16 +1038,17 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work) | |||
1024 | mutex_lock(&hdmi->mutex); | 1038 | mutex_lock(&hdmi->mutex); |
1025 | 1039 | ||
1026 | if (hdmi->hp_state == HDMI_HOTPLUG_EDID_DONE) { | 1040 | if (hdmi->hp_state == HDMI_HOTPLUG_EDID_DONE) { |
1041 | unsigned long parent_rate = 0, hdmi_rate; | ||
1042 | |||
1027 | /* A device has been plugged in */ | 1043 | /* A device has been plugged in */ |
1028 | pm_runtime_get_sync(hdmi->dev); | 1044 | pm_runtime_get_sync(hdmi->dev); |
1029 | 1045 | ||
1030 | ret = sh_hdmi_read_edid(hdmi); | 1046 | ret = sh_hdmi_read_edid(hdmi, &hdmi_rate, &parent_rate); |
1031 | if (ret < 0) | 1047 | if (ret < 0) |
1032 | goto out; | 1048 | goto out; |
1033 | 1049 | ||
1034 | /* Reconfigure the clock */ | 1050 | /* Reconfigure the clock */ |
1035 | clk_disable(hdmi->hdmi_clk); | 1051 | ret = sh_hdmi_clk_configure(hdmi, hdmi_rate, parent_rate); |
1036 | ret = sh_hdmi_clk_configure(hdmi, hdmi->var.pixclock); | ||
1037 | if (ret < 0) | 1052 | if (ret < 0) |
1038 | goto out; | 1053 | goto out; |
1039 | 1054 | ||
@@ -1166,13 +1181,22 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) | |||
1166 | goto egetclk; | 1181 | goto egetclk; |
1167 | } | 1182 | } |
1168 | 1183 | ||
1169 | /* Some arbitrary relaxed pixclock just to get things started */ | 1184 | /* An arbitrary relaxed pixclock just to get things started: from standard 480p */ |
1170 | rate = sh_hdmi_clk_configure(hdmi, 37037); | 1185 | rate = clk_round_rate(hdmi->hdmi_clk, PICOS2KHZ(37037)); |
1186 | if (rate > 0) | ||
1187 | rate = sh_hdmi_clk_configure(hdmi, rate, 0); | ||
1188 | |||
1171 | if (rate < 0) { | 1189 | if (rate < 0) { |
1172 | ret = rate; | 1190 | ret = rate; |
1173 | goto erate; | 1191 | goto erate; |
1174 | } | 1192 | } |
1175 | 1193 | ||
1194 | ret = clk_enable(hdmi->hdmi_clk); | ||
1195 | if (ret < 0) { | ||
1196 | dev_err(hdmi->dev, "Cannot enable clock: %d\n", ret); | ||
1197 | goto erate; | ||
1198 | } | ||
1199 | |||
1176 | dev_dbg(&pdev->dev, "Enabled HDMI clock at %luHz\n", rate); | 1200 | dev_dbg(&pdev->dev, "Enabled HDMI clock at %luHz\n", rate); |
1177 | 1201 | ||
1178 | if (!request_mem_region(res->start, resource_size(res), dev_name(&pdev->dev))) { | 1202 | if (!request_mem_region(res->start, resource_size(res), dev_name(&pdev->dev))) { |
@@ -1190,10 +1214,6 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) | |||
1190 | 1214 | ||
1191 | platform_set_drvdata(pdev, hdmi); | 1215 | platform_set_drvdata(pdev, hdmi); |
1192 | 1216 | ||
1193 | /* Product and revision IDs are 0 in sh-mobile version */ | ||
1194 | dev_info(&pdev->dev, "Detected HDMI controller 0x%x:0x%x\n", | ||
1195 | hdmi_read(hdmi, HDMI_PRODUCT_ID), hdmi_read(hdmi, HDMI_REVISION_ID)); | ||
1196 | |||
1197 | /* Set up LCDC callbacks */ | 1217 | /* Set up LCDC callbacks */ |
1198 | board_cfg = &pdata->lcd_chan->board_cfg; | 1218 | board_cfg = &pdata->lcd_chan->board_cfg; |
1199 | board_cfg->owner = THIS_MODULE; | 1219 | board_cfg->owner = THIS_MODULE; |
@@ -1206,6 +1226,10 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) | |||
1206 | pm_runtime_enable(&pdev->dev); | 1226 | pm_runtime_enable(&pdev->dev); |
1207 | pm_runtime_resume(&pdev->dev); | 1227 | pm_runtime_resume(&pdev->dev); |
1208 | 1228 | ||
1229 | /* Product and revision IDs are 0 in sh-mobile version */ | ||
1230 | dev_info(&pdev->dev, "Detected HDMI controller 0x%x:0x%x\n", | ||
1231 | hdmi_read(hdmi, HDMI_PRODUCT_ID), hdmi_read(hdmi, HDMI_REVISION_ID)); | ||
1232 | |||
1209 | ret = request_irq(irq, sh_hdmi_hotplug, 0, | 1233 | ret = request_irq(irq, sh_hdmi_hotplug, 0, |
1210 | dev_name(&pdev->dev), hdmi); | 1234 | dev_name(&pdev->dev), hdmi); |
1211 | if (ret < 0) { | 1235 | if (ret < 0) { |