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