Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Guennadi Liakhovetski and committed by
Paul Mundt
c36940e6 5fd284e6

+71 -44
+68 -44
drivers/video/sh_mobile_hdmi.c
··· 685 685 } 686 686 687 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 - rate = clk_round_rate(hdmi->hdmi_clk, target); 692 - unsigned long rate_error = rate > 0 ? abs(rate - target) : ULONG_MAX; 691 + unsigned long target = PICOS2KHZ(mode->pixclock) * 1000, rate_error; 692 + struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; 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 704 dev_dbg(hdmi->dev, "%u-%u-%u-%u x %u-%u-%u-%u\n", 695 705 mode->left_margin, mode->xres, ··· 707 697 mode->upper_margin, mode->yres, 708 698 mode->lower_margin, mode->vsync_len); 709 699 710 - dev_dbg(hdmi->dev, "\t@%lu(+/-%lu)Hz, e=%lu / 1000, r=%uHz\n", target, 711 - rate_error, rate_error ? 10000 / (10 * target / rate_error) : 0, 712 - mode->refresh); 700 + 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, 702 + mode->refresh, *parent_rate); 713 703 714 704 return rate_error; 715 705 } 716 706 717 - static int sh_hdmi_read_edid(struct sh_hdmi *hdmi) 707 + static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate, 708 + unsigned long *parent_rate) 718 709 { 719 710 struct fb_var_screeninfo tmpvar; 720 711 struct fb_var_screeninfo *var = &tmpvar; ··· 765 754 for (i = 0, mode = hdmi->monspec.modedb; 766 755 f_width && f_height && i < hdmi->monspec.modedb_len && !exact_match; 767 756 i++, mode++) { 768 - unsigned long rate_error = sh_hdmi_rate_error(hdmi, mode); 757 + unsigned long rate_error; 769 758 770 759 /* No interest in unmatching modes */ 771 760 if (f_width != mode->xres || f_height != mode->yres) 772 761 continue; 762 + 763 + rate_error = sh_hdmi_rate_error(hdmi, mode, hdmi_rate, parent_rate); 764 + 773 765 if (f_refresh == mode->refresh || (!f_refresh && !rate_error)) 774 766 /* 775 767 * Exact match if either the refresh rate matches or it ··· 816 802 817 803 if (modelist) { 818 804 found = &modelist->mode; 819 - found_rate_error = sh_hdmi_rate_error(hdmi, found); 805 + found_rate_error = sh_hdmi_rate_error(hdmi, found, hdmi_rate, parent_rate); 820 806 } 821 807 } 822 808 ··· 824 810 if (!found) 825 811 return -ENXIO; 826 812 827 - dev_info(hdmi->dev, "Using %s mode %ux%u@%uHz (%luHz), clock error %luHz\n", 828 - modelist ? "default" : "EDID", found->xres, found->yres, 829 - found->refresh, PICOS2KHZ(found->pixclock) * 1000, found_rate_error); 830 - 831 813 if ((found->xres == 720 && found->yres == 480) || 832 814 (found->xres == 1280 && found->yres == 720) || 833 815 (found->xres == 1920 && found->yres == 1080)) 834 816 hdmi->preprogrammed_mode = true; 835 817 else 836 818 hdmi->preprogrammed_mode = false; 819 + 820 + dev_dbg(hdmi->dev, "Using %s %s mode %ux%u@%uHz (%luHz), clock error %luHz\n", 821 + modelist ? "default" : "EDID", hdmi->preprogrammed_mode ? "VIC" : "external", 822 + found->xres, found->yres, found->refresh, 823 + PICOS2KHZ(found->pixclock) * 1000, found_rate_error); 837 824 838 825 fb_videomode_to_var(&hdmi->var, found); 839 826 sh_hdmi_external_video_param(hdmi); ··· 987 972 988 973 /** 989 974 * sh_hdmi_clk_configure() - set HDMI clock frequency and enable the clock 990 - * @hdmi: driver context 991 - * @pixclock: pixel clock period in picoseconds 992 - * return: configured positive rate if successful 993 - * 0 if couldn't set the rate, but managed to enable the clock 994 - * negative error, if couldn't enable the clock 975 + * @hdmi: driver context 976 + * @hdmi_rate: HDMI clock frequency in Hz 977 + * @parent_rate: if != 0 - set parent clock rate for optimal precision 978 + * return: configured positive rate if successful 979 + * 0 if couldn't set the rate, but managed to enable the 980 + * clock, negative error, if couldn't enable the clock 995 981 */ 996 - static long sh_hdmi_clk_configure(struct sh_hdmi *hdmi, unsigned long pixclock) 982 + static long sh_hdmi_clk_configure(struct sh_hdmi *hdmi, unsigned long hdmi_rate, 983 + unsigned long parent_rate) 997 984 { 998 - long rate; 985 + struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; 999 986 int ret; 1000 987 1001 - rate = PICOS2KHZ(pixclock) * 1000; 1002 - rate = clk_round_rate(hdmi->hdmi_clk, rate); 1003 - if (rate > 0) { 1004 - ret = clk_set_rate(hdmi->hdmi_clk, rate); 988 + if (parent_rate && clk_get_parent(hdmi->hdmi_clk)) { 989 + ret = clk_set_rate(clk_get_parent(hdmi->hdmi_clk), parent_rate); 1005 990 if (ret < 0) { 1006 - dev_warn(hdmi->dev, "Cannot set rate %ld: %d\n", rate, ret); 1007 - rate = 0; 991 + dev_warn(hdmi->dev, "Cannot set parent rate %ld: %d\n", parent_rate, ret); 992 + hdmi_rate = clk_round_rate(hdmi->hdmi_clk, hdmi_rate); 1008 993 } else { 1009 - dev_dbg(hdmi->dev, "HDMI set frequency %lu\n", rate); 994 + dev_dbg(hdmi->dev, "HDMI set parent frequency %lu\n", parent_rate); 1010 995 } 1011 - } else { 1012 - rate = 0; 1013 - dev_warn(hdmi->dev, "Cannot get suitable rate: %ld\n", rate); 1014 996 } 1015 997 1016 - ret = clk_enable(hdmi->hdmi_clk); 998 + ret = clk_set_rate(hdmi->hdmi_clk, hdmi_rate); 1017 999 if (ret < 0) { 1018 - dev_err(hdmi->dev, "Cannot enable clock: %d\n", ret); 1019 - return ret; 1000 + dev_warn(hdmi->dev, "Cannot set rate %ld: %d\n", hdmi_rate, ret); 1001 + hdmi_rate = 0; 1002 + } else { 1003 + dev_dbg(hdmi->dev, "HDMI set frequency %lu\n", hdmi_rate); 1020 1004 } 1021 1005 1022 - return rate; 1006 + return hdmi_rate; 1023 1007 } 1024 1008 1025 1009 /* Hotplug interrupt occurred, read EDID */ ··· 1038 1024 mutex_lock(&hdmi->mutex); 1039 1025 1040 1026 if (hdmi->hp_state == HDMI_HOTPLUG_EDID_DONE) { 1027 + unsigned long parent_rate = 0, hdmi_rate; 1028 + 1041 1029 /* A device has been plugged in */ 1042 1030 pm_runtime_get_sync(hdmi->dev); 1043 1031 1044 - ret = sh_hdmi_read_edid(hdmi); 1032 + ret = sh_hdmi_read_edid(hdmi, &hdmi_rate, &parent_rate); 1045 1033 if (ret < 0) 1046 1034 goto out; 1047 1035 1048 1036 /* Reconfigure the clock */ 1049 - clk_disable(hdmi->hdmi_clk); 1050 - ret = sh_hdmi_clk_configure(hdmi, hdmi->var.pixclock); 1037 + ret = sh_hdmi_clk_configure(hdmi, hdmi_rate, parent_rate); 1051 1038 if (ret < 0) 1052 1039 goto out; 1053 1040 ··· 1181 1166 goto egetclk; 1182 1167 } 1183 1168 1184 - /* Some arbitrary relaxed pixclock just to get things started */ 1185 - rate = sh_hdmi_clk_configure(hdmi, 37037); 1169 + /* An arbitrary relaxed pixclock just to get things started: from standard 480p */ 1170 + rate = clk_round_rate(hdmi->hdmi_clk, PICOS2KHZ(37037)); 1171 + if (rate > 0) 1172 + rate = sh_hdmi_clk_configure(hdmi, rate, 0); 1173 + 1186 1174 if (rate < 0) { 1187 1175 ret = rate; 1176 + goto erate; 1177 + } 1178 + 1179 + ret = clk_enable(hdmi->hdmi_clk); 1180 + if (ret < 0) { 1181 + dev_err(hdmi->dev, "Cannot enable clock: %d\n", ret); 1188 1182 goto erate; 1189 1183 } 1190 1184 ··· 1214 1190 1215 1191 platform_set_drvdata(pdev, hdmi); 1216 1192 1217 - /* Product and revision IDs are 0 in sh-mobile version */ 1218 - dev_info(&pdev->dev, "Detected HDMI controller 0x%x:0x%x\n", 1219 - hdmi_read(hdmi, HDMI_PRODUCT_ID), hdmi_read(hdmi, HDMI_REVISION_ID)); 1220 - 1221 1193 /* Set up LCDC callbacks */ 1222 1194 board_cfg = &pdata->lcd_chan->board_cfg; 1223 1195 board_cfg->owner = THIS_MODULE; ··· 1225 1205 1226 1206 pm_runtime_enable(&pdev->dev); 1227 1207 pm_runtime_resume(&pdev->dev); 1208 + 1209 + /* Product and revision IDs are 0 in sh-mobile version */ 1210 + dev_info(&pdev->dev, "Detected HDMI controller 0x%x:0x%x\n", 1211 + hdmi_read(hdmi, HDMI_PRODUCT_ID), hdmi_read(hdmi, HDMI_REVISION_ID)); 1228 1212 1229 1213 ret = request_irq(irq, sh_hdmi_hotplug, 0, 1230 1214 dev_name(&pdev->dev), hdmi);
+3
include/video/sh_mobile_hdmi.h
··· 13 13 14 14 struct sh_mobile_lcdc_chan_cfg; 15 15 struct device; 16 + struct clk; 16 17 17 18 /* 18 19 * flags format ··· 34 33 struct sh_mobile_lcdc_chan_cfg *lcd_chan; 35 34 struct device *lcd_dev; 36 35 unsigned int flags; 36 + long (*clk_optimize_parent)(unsigned long target, unsigned long *best_freq, 37 + unsigned long *parent_freq); 37 38 }; 38 39 39 40 #endif