1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * HDMI PLL 4 * 5 * Copyright (C) 2013 Texas Instruments Incorporated 6 */ 7 8 #define DSS_SUBSYS_NAME "HDMIPLL" 9 10 #include <linux/kernel.h> 11 #include <linux/module.h> 12 #include <linux/err.h> 13 #include <linux/io.h> 14 #include <linux/platform_device.h> 15 #include <linux/clk.h> 16 #include <linux/seq_file.h> 17 18 #include <video/omapfb_dss.h> 19 20 #include "dss.h" 21 #include "hdmi.h" 22 23 void hdmi_pll_dump(struct hdmi_pll_data *pll, struct seq_file *s) 24 { 25 #define DUMPPLL(r) seq_printf(s, "%-35s %08x\n", #r,\ 26 hdmi_read_reg(pll->base, r)) 27 28 DUMPPLL(PLLCTRL_PLL_CONTROL); 29 DUMPPLL(PLLCTRL_PLL_STATUS); 30 DUMPPLL(PLLCTRL_PLL_GO); 31 DUMPPLL(PLLCTRL_CFG1); 32 DUMPPLL(PLLCTRL_CFG2); 33 DUMPPLL(PLLCTRL_CFG3); 34 DUMPPLL(PLLCTRL_SSC_CFG1); 35 DUMPPLL(PLLCTRL_SSC_CFG2); 36 DUMPPLL(PLLCTRL_CFG4); 37 } 38 39 void hdmi_pll_compute(struct hdmi_pll_data *pll, 40 unsigned long target_tmds, struct dss_pll_clock_info *pi) 41 { 42 unsigned long fint, clkdco, clkout; 43 unsigned long target_bitclk, target_clkdco; 44 unsigned long min_dco; 45 unsigned n, m, mf, m2, sd; 46 unsigned long clkin; 47 const struct dss_pll_hw *hw = pll->pll.hw; 48 49 clkin = clk_get_rate(pll->pll.clkin); 50 51 DSSDBG("clkin %lu, target tmds %lu\n", clkin, target_tmds); 52 53 target_bitclk = target_tmds * 10; 54 55 /* Fint */ 56 n = DIV_ROUND_UP(clkin, hw->fint_max); 57 fint = clkin / n; 58 59 /* adjust m2 so that the clkdco will be high enough */ 60 min_dco = roundup(hw->clkdco_min, fint); 61 m2 = DIV_ROUND_UP(min_dco, target_bitclk); 62 if (m2 == 0) 63 m2 = 1; 64 65 target_clkdco = target_bitclk * m2; 66 m = target_clkdco / fint; 67 68 clkdco = fint * m; 69 70 /* adjust clkdco with fractional mf */ 71 if (WARN_ON(target_clkdco - clkdco > fint)) 72 mf = 0; 73 else 74 mf = (u32)div_u64(262144ull * (target_clkdco - clkdco), fint); 75 76 if (mf > 0) 77 clkdco += (u32)div_u64((u64)mf * fint, 262144); 78 79 clkout = clkdco / m2; 80 81 /* sigma-delta */ 82 sd = DIV_ROUND_UP(fint * m, 250000000); 83 84 DSSDBG("N = %u, M = %u, M.f = %u, M2 = %u, SD = %u\n", 85 n, m, mf, m2, sd); 86 DSSDBG("Fint %lu, clkdco %lu, clkout %lu\n", fint, clkdco, clkout); 87 88 pi->n = n; 89 pi->m = m; 90 pi->mf = mf; 91 pi->mX[0] = m2; 92 pi->sd = sd; 93 94 pi->fint = fint; 95 pi->clkdco = clkdco; 96 pi->clkout[0] = clkout; 97 } 98 99 static int hdmi_pll_enable(struct dss_pll *dsspll) 100 { 101 struct hdmi_pll_data *pll = container_of(dsspll, struct hdmi_pll_data, pll); 102 struct hdmi_wp_data *wp = pll->wp; 103 u16 r = 0; 104 105 dss_ctrl_pll_enable(DSS_PLL_HDMI, true); 106 107 r = hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_BOTHON_ALLCLKS); 108 if (r) 109 return r; 110 111 return 0; 112 } 113 114 static void hdmi_pll_disable(struct dss_pll *dsspll) 115 { 116 struct hdmi_pll_data *pll = container_of(dsspll, struct hdmi_pll_data, pll); 117 struct hdmi_wp_data *wp = pll->wp; 118 119 hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_ALLOFF); 120 121 dss_ctrl_pll_enable(DSS_PLL_HDMI, false); 122 } 123 124 static const struct dss_pll_ops dsi_pll_ops = { 125 .enable = hdmi_pll_enable, 126 .disable = hdmi_pll_disable, 127 .set_config = dss_pll_write_config_type_b, 128 }; 129 130 static const struct dss_pll_hw dss_omap4_hdmi_pll_hw = { 131 .n_max = 255, 132 .m_min = 20, 133 .m_max = 4095, 134 .mX_max = 127, 135 .fint_min = 500000, 136 .fint_max = 2500000, 137 138 .clkdco_min = 500000000, 139 .clkdco_low = 1000000000, 140 .clkdco_max = 2000000000, 141 142 .n_msb = 8, 143 .n_lsb = 1, 144 .m_msb = 20, 145 .m_lsb = 9, 146 147 .mX_msb[0] = 24, 148 .mX_lsb[0] = 18, 149 150 .has_selfreqdco = true, 151 }; 152 153 static const struct dss_pll_hw dss_omap5_hdmi_pll_hw = { 154 .n_max = 255, 155 .m_min = 20, 156 .m_max = 2045, 157 .mX_max = 127, 158 .fint_min = 620000, 159 .fint_max = 2500000, 160 161 .clkdco_min = 750000000, 162 .clkdco_low = 1500000000, 163 .clkdco_max = 2500000000UL, 164 165 .n_msb = 8, 166 .n_lsb = 1, 167 .m_msb = 20, 168 .m_lsb = 9, 169 170 .mX_msb[0] = 24, 171 .mX_lsb[0] = 18, 172 173 .has_selfreqdco = true, 174 .has_refsel = true, 175 }; 176 177 static int dsi_init_pll_data(struct platform_device *pdev, struct hdmi_pll_data *hpll) 178 { 179 struct dss_pll *pll = &hpll->pll; 180 struct clk *clk; 181 int r; 182 183 clk = devm_clk_get(&pdev->dev, "sys_clk"); 184 if (IS_ERR(clk)) { 185 DSSERR("can't get sys_clk\n"); 186 return PTR_ERR(clk); 187 } 188 189 pll->name = "hdmi"; 190 pll->id = DSS_PLL_HDMI; 191 pll->base = hpll->base; 192 pll->clkin = clk; 193 194 switch (omapdss_get_version()) { 195 case OMAPDSS_VER_OMAP4430_ES1: 196 case OMAPDSS_VER_OMAP4430_ES2: 197 case OMAPDSS_VER_OMAP4: 198 pll->hw = &dss_omap4_hdmi_pll_hw; 199 break; 200 201 case OMAPDSS_VER_OMAP5: 202 case OMAPDSS_VER_DRA7xx: 203 pll->hw = &dss_omap5_hdmi_pll_hw; 204 break; 205 206 default: 207 return -ENODEV; 208 } 209 210 pll->ops = &dsi_pll_ops; 211 212 r = dss_pll_register(pll); 213 if (r) 214 return r; 215 216 return 0; 217 } 218 219 int hdmi_pll_init(struct platform_device *pdev, struct hdmi_pll_data *pll, 220 struct hdmi_wp_data *wp) 221 { 222 int r; 223 struct resource *res; 224 225 pll->wp = wp; 226 227 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pll"); 228 if (!res) { 229 DSSERR("can't get PLL mem resource\n"); 230 return -EINVAL; 231 } 232 233 pll->base = devm_ioremap_resource(&pdev->dev, res); 234 if (IS_ERR(pll->base)) { 235 DSSERR("can't ioremap PLLCTRL\n"); 236 return PTR_ERR(pll->base); 237 } 238 239 r = dsi_init_pll_data(pdev, pll); 240 if (r) { 241 DSSERR("failed to init HDMI PLL\n"); 242 return r; 243 } 244 245 return 0; 246 } 247 248 void hdmi_pll_uninit(struct hdmi_pll_data *hpll) 249 { 250 struct dss_pll *pll = &hpll->pll; 251 252 dss_pll_unregister(pll); 253 } 254