aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/video/sh_mobile_hdmi.c
diff options
context:
space:
mode:
authorGuennadi Liakhovetski <g.liakhovetski@gmx.de>2010-11-02 07:27:16 -0400
committerPaul Mundt <lethal@linux-sh.org>2010-11-10 03:23:54 -0500
commitc36940e678fc30779c99246c034deca1fed61ae4 (patch)
tree88158e04b5b516a35965dad1f58686d0be735844 /drivers/video/sh_mobile_hdmi.c
parent5fd284e6cd39f731db86dfd2440553365d5fad4d (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.c112
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
687static unsigned long sh_hdmi_rate_error(struct sh_hdmi *hdmi, 687static 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
707static int sh_hdmi_read_edid(struct sh_hdmi *hdmi) 717static 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 */
981static long sh_hdmi_clk_configure(struct sh_hdmi *hdmi, unsigned long pixclock) 997static 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) {