Archit Taneja | c1577c1 | 2013-10-08 12:55:26 +0530 | [diff] [blame] | 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 | |
Tomi Valkeinen | ac9f242 | 2013-11-14 13:46:32 +0200 | [diff] [blame] | 11 | #define DSS_SUBSYS_NAME "HDMIPLL" |
| 12 | |
Archit Taneja | c1577c1 | 2013-10-08 12:55:26 +0530 | [diff] [blame] | 13 | #include <linux/kernel.h> |
| 14 | #include <linux/module.h> |
Archit Taneja | c1577c1 | 2013-10-08 12:55:26 +0530 | [diff] [blame] | 15 | #include <linux/err.h> |
| 16 | #include <linux/io.h> |
| 17 | #include <linux/platform_device.h> |
Tomi Valkeinen | c84c3a5 | 2014-10-22 15:02:17 +0300 | [diff] [blame] | 18 | #include <linux/clk.h> |
Tomi Valkeinen | 86c9305 | 2016-05-17 17:07:46 +0300 | [diff] [blame^] | 19 | #include <linux/pm_runtime.h> |
Tomi Valkeinen | c84c3a5 | 2014-10-22 15:02:17 +0300 | [diff] [blame] | 20 | |
Archit Taneja | c1577c1 | 2013-10-08 12:55:26 +0530 | [diff] [blame] | 21 | #include <video/omapdss.h> |
| 22 | |
| 23 | #include "dss.h" |
Archit Taneja | ef26958 | 2013-09-12 17:45:57 +0530 | [diff] [blame] | 24 | #include "hdmi.h" |
Archit Taneja | c1577c1 | 2013-10-08 12:55:26 +0530 | [diff] [blame] | 25 | |
Archit Taneja | c1577c1 | 2013-10-08 12:55:26 +0530 | [diff] [blame] | 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 | |
Tomi Valkeinen | c84c3a5 | 2014-10-22 15:02:17 +0300 | [diff] [blame] | 42 | void hdmi_pll_compute(struct hdmi_pll_data *pll, |
| 43 | unsigned long target_tmds, struct dss_pll_clock_info *pi) |
Archit Taneja | c1577c1 | 2013-10-08 12:55:26 +0530 | [diff] [blame] | 44 | { |
Tomi Valkeinen | 33f1312 | 2014-09-15 15:40:47 +0300 | [diff] [blame] | 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; |
Tomi Valkeinen | c84c3a5 | 2014-10-22 15:02:17 +0300 | [diff] [blame] | 49 | unsigned long clkin; |
| 50 | const struct dss_pll_hw *hw = pll->pll.hw; |
| 51 | |
| 52 | clkin = clk_get_rate(pll->pll.clkin); |
Archit Taneja | c1577c1 | 2013-10-08 12:55:26 +0530 | [diff] [blame] | 53 | |
Tomi Valkeinen | 33f1312 | 2014-09-15 15:40:47 +0300 | [diff] [blame] | 54 | DSSDBG("clkin %lu, target tmds %lu\n", clkin, target_tmds); |
Archit Taneja | c1577c1 | 2013-10-08 12:55:26 +0530 | [diff] [blame] | 55 | |
Tomi Valkeinen | 33f1312 | 2014-09-15 15:40:47 +0300 | [diff] [blame] | 56 | target_bitclk = target_tmds * 10; |
Archit Taneja | c1577c1 | 2013-10-08 12:55:26 +0530 | [diff] [blame] | 57 | |
Tomi Valkeinen | 33f1312 | 2014-09-15 15:40:47 +0300 | [diff] [blame] | 58 | /* Fint */ |
Tomi Valkeinen | c84c3a5 | 2014-10-22 15:02:17 +0300 | [diff] [blame] | 59 | n = DIV_ROUND_UP(clkin, hw->fint_max); |
Tomi Valkeinen | 33f1312 | 2014-09-15 15:40:47 +0300 | [diff] [blame] | 60 | fint = clkin / n; |
Archit Taneja | c1577c1 | 2013-10-08 12:55:26 +0530 | [diff] [blame] | 61 | |
Tomi Valkeinen | 33f1312 | 2014-09-15 15:40:47 +0300 | [diff] [blame] | 62 | /* adjust m2 so that the clkdco will be high enough */ |
Tomi Valkeinen | c84c3a5 | 2014-10-22 15:02:17 +0300 | [diff] [blame] | 63 | min_dco = roundup(hw->clkdco_min, fint); |
Tomi Valkeinen | 33f1312 | 2014-09-15 15:40:47 +0300 | [diff] [blame] | 64 | m2 = DIV_ROUND_UP(min_dco, target_bitclk); |
| 65 | if (m2 == 0) |
| 66 | m2 = 1; |
Archit Taneja | c1577c1 | 2013-10-08 12:55:26 +0530 | [diff] [blame] | 67 | |
Tomi Valkeinen | 33f1312 | 2014-09-15 15:40:47 +0300 | [diff] [blame] | 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; |
Archit Taneja | 2d64b1b | 2013-09-23 15:12:34 +0530 | [diff] [blame] | 76 | else |
Tomi Valkeinen | 33f1312 | 2014-09-15 15:40:47 +0300 | [diff] [blame] | 77 | mf = (u32)div_u64(262144ull * (target_clkdco - clkdco), fint); |
Archit Taneja | c1577c1 | 2013-10-08 12:55:26 +0530 | [diff] [blame] | 78 | |
Tomi Valkeinen | 33f1312 | 2014-09-15 15:40:47 +0300 | [diff] [blame] | 79 | if (mf > 0) |
| 80 | clkdco += (u32)div_u64((u64)mf * fint, 262144); |
Archit Taneja | c1577c1 | 2013-10-08 12:55:26 +0530 | [diff] [blame] | 81 | |
Tomi Valkeinen | 33f1312 | 2014-09-15 15:40:47 +0300 | [diff] [blame] | 82 | clkout = clkdco / m2; |
Archit Taneja | c1577c1 | 2013-10-08 12:55:26 +0530 | [diff] [blame] | 83 | |
Tomi Valkeinen | 33f1312 | 2014-09-15 15:40:47 +0300 | [diff] [blame] | 84 | /* sigma-delta */ |
| 85 | sd = DIV_ROUND_UP(fint * m, 250000000); |
Archit Taneja | c1577c1 | 2013-10-08 12:55:26 +0530 | [diff] [blame] | 86 | |
Tomi Valkeinen | 33f1312 | 2014-09-15 15:40:47 +0300 | [diff] [blame] | 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 | |
Tomi Valkeinen | c84c3a5 | 2014-10-22 15:02:17 +0300 | [diff] [blame] | 91 | pi->n = n; |
| 92 | pi->m = m; |
| 93 | pi->mf = mf; |
| 94 | pi->mX[0] = m2; |
| 95 | pi->sd = sd; |
Tomi Valkeinen | 33f1312 | 2014-09-15 15:40:47 +0300 | [diff] [blame] | 96 | |
Tomi Valkeinen | c84c3a5 | 2014-10-22 15:02:17 +0300 | [diff] [blame] | 97 | pi->fint = fint; |
Tomi Valkeinen | 33f1312 | 2014-09-15 15:40:47 +0300 | [diff] [blame] | 98 | pi->clkdco = clkdco; |
Tomi Valkeinen | c84c3a5 | 2014-10-22 15:02:17 +0300 | [diff] [blame] | 99 | pi->clkout[0] = clkout; |
Archit Taneja | c1577c1 | 2013-10-08 12:55:26 +0530 | [diff] [blame] | 100 | } |
| 101 | |
Tomi Valkeinen | c84c3a5 | 2014-10-22 15:02:17 +0300 | [diff] [blame] | 102 | static int hdmi_pll_enable(struct dss_pll *dsspll) |
Archit Taneja | c1577c1 | 2013-10-08 12:55:26 +0530 | [diff] [blame] | 103 | { |
Tomi Valkeinen | c84c3a5 | 2014-10-22 15:02:17 +0300 | [diff] [blame] | 104 | struct hdmi_pll_data *pll = container_of(dsspll, struct hdmi_pll_data, pll); |
Tomi Valkeinen | 03aafa2 | 2014-10-16 15:31:38 +0300 | [diff] [blame] | 105 | struct hdmi_wp_data *wp = pll->wp; |
Tomi Valkeinen | f7dd8f5 | 2016-05-17 17:00:52 +0300 | [diff] [blame] | 106 | int r; |
Archit Taneja | c1577c1 | 2013-10-08 12:55:26 +0530 | [diff] [blame] | 107 | |
Tomi Valkeinen | 86c9305 | 2016-05-17 17:07:46 +0300 | [diff] [blame^] | 108 | r = pm_runtime_get_sync(&pll->pdev->dev); |
| 109 | WARN_ON(r < 0); |
| 110 | |
Tomi Valkeinen | adb5ff8 | 2014-12-31 11:26:18 +0200 | [diff] [blame] | 111 | dss_ctrl_pll_enable(DSS_PLL_HDMI, true); |
| 112 | |
Archit Taneja | c1577c1 | 2013-10-08 12:55:26 +0530 | [diff] [blame] | 113 | r = hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_BOTHON_ALLCLKS); |
| 114 | if (r) |
| 115 | return r; |
| 116 | |
Archit Taneja | c1577c1 | 2013-10-08 12:55:26 +0530 | [diff] [blame] | 117 | return 0; |
| 118 | } |
| 119 | |
Tomi Valkeinen | c84c3a5 | 2014-10-22 15:02:17 +0300 | [diff] [blame] | 120 | static void hdmi_pll_disable(struct dss_pll *dsspll) |
Archit Taneja | c1577c1 | 2013-10-08 12:55:26 +0530 | [diff] [blame] | 121 | { |
Tomi Valkeinen | c84c3a5 | 2014-10-22 15:02:17 +0300 | [diff] [blame] | 122 | struct hdmi_pll_data *pll = container_of(dsspll, struct hdmi_pll_data, pll); |
Tomi Valkeinen | 03aafa2 | 2014-10-16 15:31:38 +0300 | [diff] [blame] | 123 | struct hdmi_wp_data *wp = pll->wp; |
Tomi Valkeinen | 86c9305 | 2016-05-17 17:07:46 +0300 | [diff] [blame^] | 124 | int r; |
Tomi Valkeinen | 03aafa2 | 2014-10-16 15:31:38 +0300 | [diff] [blame] | 125 | |
Archit Taneja | c1577c1 | 2013-10-08 12:55:26 +0530 | [diff] [blame] | 126 | hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_ALLOFF); |
Tomi Valkeinen | adb5ff8 | 2014-12-31 11:26:18 +0200 | [diff] [blame] | 127 | |
| 128 | dss_ctrl_pll_enable(DSS_PLL_HDMI, false); |
Tomi Valkeinen | 86c9305 | 2016-05-17 17:07:46 +0300 | [diff] [blame^] | 129 | |
| 130 | r = pm_runtime_put_sync(&pll->pdev->dev); |
| 131 | WARN_ON(r < 0 && r != -ENOSYS); |
Archit Taneja | c1577c1 | 2013-10-08 12:55:26 +0530 | [diff] [blame] | 132 | } |
| 133 | |
Tomi Valkeinen | c84c3a5 | 2014-10-22 15:02:17 +0300 | [diff] [blame] | 134 | static const struct dss_pll_ops dsi_pll_ops = { |
| 135 | .enable = hdmi_pll_enable, |
| 136 | .disable = hdmi_pll_disable, |
| 137 | .set_config = dss_pll_write_config_type_b, |
Archit Taneja | 2d64b1b | 2013-09-23 15:12:34 +0530 | [diff] [blame] | 138 | }; |
| 139 | |
Tomi Valkeinen | c84c3a5 | 2014-10-22 15:02:17 +0300 | [diff] [blame] | 140 | static const struct dss_pll_hw dss_omap4_hdmi_pll_hw = { |
Tomi Valkeinen | 06ede3d | 2016-05-18 10:48:44 +0300 | [diff] [blame] | 141 | .type = DSS_PLL_TYPE_B, |
| 142 | |
Tomi Valkeinen | c84c3a5 | 2014-10-22 15:02:17 +0300 | [diff] [blame] | 143 | .n_max = 255, |
| 144 | .m_min = 20, |
| 145 | .m_max = 4095, |
| 146 | .mX_max = 127, |
| 147 | .fint_min = 500000, |
| 148 | .fint_max = 2500000, |
Tomi Valkeinen | c84c3a5 | 2014-10-22 15:02:17 +0300 | [diff] [blame] | 149 | |
| 150 | .clkdco_min = 500000000, |
| 151 | .clkdco_low = 1000000000, |
| 152 | .clkdco_max = 2000000000, |
| 153 | |
| 154 | .n_msb = 8, |
| 155 | .n_lsb = 1, |
| 156 | .m_msb = 20, |
| 157 | .m_lsb = 9, |
| 158 | |
| 159 | .mX_msb[0] = 24, |
| 160 | .mX_lsb[0] = 18, |
| 161 | |
| 162 | .has_selfreqdco = true, |
Archit Taneja | 2d64b1b | 2013-09-23 15:12:34 +0530 | [diff] [blame] | 163 | }; |
| 164 | |
Tomi Valkeinen | c84c3a5 | 2014-10-22 15:02:17 +0300 | [diff] [blame] | 165 | static const struct dss_pll_hw dss_omap5_hdmi_pll_hw = { |
Tomi Valkeinen | 06ede3d | 2016-05-18 10:48:44 +0300 | [diff] [blame] | 166 | .type = DSS_PLL_TYPE_B, |
| 167 | |
Tomi Valkeinen | c84c3a5 | 2014-10-22 15:02:17 +0300 | [diff] [blame] | 168 | .n_max = 255, |
| 169 | .m_min = 20, |
| 170 | .m_max = 2045, |
| 171 | .mX_max = 127, |
| 172 | .fint_min = 620000, |
| 173 | .fint_max = 2500000, |
Tomi Valkeinen | c84c3a5 | 2014-10-22 15:02:17 +0300 | [diff] [blame] | 174 | |
| 175 | .clkdco_min = 750000000, |
| 176 | .clkdco_low = 1500000000, |
| 177 | .clkdco_max = 2500000000UL, |
| 178 | |
| 179 | .n_msb = 8, |
| 180 | .n_lsb = 1, |
| 181 | .m_msb = 20, |
| 182 | .m_lsb = 9, |
| 183 | |
| 184 | .mX_msb[0] = 24, |
| 185 | .mX_lsb[0] = 18, |
| 186 | |
| 187 | .has_selfreqdco = true, |
| 188 | .has_refsel = true, |
| 189 | }; |
| 190 | |
| 191 | static int dsi_init_pll_data(struct platform_device *pdev, struct hdmi_pll_data *hpll) |
Archit Taneja | 2d64b1b | 2013-09-23 15:12:34 +0530 | [diff] [blame] | 192 | { |
Tomi Valkeinen | c84c3a5 | 2014-10-22 15:02:17 +0300 | [diff] [blame] | 193 | struct dss_pll *pll = &hpll->pll; |
| 194 | struct clk *clk; |
| 195 | int r; |
Archit Taneja | 2d64b1b | 2013-09-23 15:12:34 +0530 | [diff] [blame] | 196 | |
Tomi Valkeinen | c84c3a5 | 2014-10-22 15:02:17 +0300 | [diff] [blame] | 197 | clk = devm_clk_get(&pdev->dev, "sys_clk"); |
| 198 | if (IS_ERR(clk)) { |
| 199 | DSSERR("can't get sys_clk\n"); |
| 200 | return PTR_ERR(clk); |
Archit Taneja | 2d64b1b | 2013-09-23 15:12:34 +0530 | [diff] [blame] | 201 | } |
| 202 | |
Tomi Valkeinen | c84c3a5 | 2014-10-22 15:02:17 +0300 | [diff] [blame] | 203 | pll->name = "hdmi"; |
Tomi Valkeinen | 64e22ff | 2015-01-02 10:05:33 +0200 | [diff] [blame] | 204 | pll->id = DSS_PLL_HDMI; |
Tomi Valkeinen | c84c3a5 | 2014-10-22 15:02:17 +0300 | [diff] [blame] | 205 | pll->base = hpll->base; |
| 206 | pll->clkin = clk; |
| 207 | |
Archit Taneja | 2d64b1b | 2013-09-23 15:12:34 +0530 | [diff] [blame] | 208 | switch (omapdss_get_version()) { |
| 209 | case OMAPDSS_VER_OMAP4430_ES1: |
| 210 | case OMAPDSS_VER_OMAP4430_ES2: |
| 211 | case OMAPDSS_VER_OMAP4: |
Tomi Valkeinen | c84c3a5 | 2014-10-22 15:02:17 +0300 | [diff] [blame] | 212 | pll->hw = &dss_omap4_hdmi_pll_hw; |
Archit Taneja | 2d64b1b | 2013-09-23 15:12:34 +0530 | [diff] [blame] | 213 | break; |
| 214 | |
| 215 | case OMAPDSS_VER_OMAP5: |
Tomi Valkeinen | adb5ff8 | 2014-12-31 11:26:18 +0200 | [diff] [blame] | 216 | case OMAPDSS_VER_DRA7xx: |
Tomi Valkeinen | c84c3a5 | 2014-10-22 15:02:17 +0300 | [diff] [blame] | 217 | pll->hw = &dss_omap5_hdmi_pll_hw; |
Archit Taneja | 2d64b1b | 2013-09-23 15:12:34 +0530 | [diff] [blame] | 218 | break; |
| 219 | |
| 220 | default: |
| 221 | return -ENODEV; |
| 222 | } |
| 223 | |
Tomi Valkeinen | c84c3a5 | 2014-10-22 15:02:17 +0300 | [diff] [blame] | 224 | pll->ops = &dsi_pll_ops; |
| 225 | |
| 226 | r = dss_pll_register(pll); |
| 227 | if (r) |
| 228 | return r; |
Archit Taneja | 2d64b1b | 2013-09-23 15:12:34 +0530 | [diff] [blame] | 229 | |
| 230 | return 0; |
| 231 | } |
| 232 | |
Tomi Valkeinen | 03aafa2 | 2014-10-16 15:31:38 +0300 | [diff] [blame] | 233 | int hdmi_pll_init(struct platform_device *pdev, struct hdmi_pll_data *pll, |
| 234 | struct hdmi_wp_data *wp) |
Archit Taneja | c1577c1 | 2013-10-08 12:55:26 +0530 | [diff] [blame] | 235 | { |
Archit Taneja | 2d64b1b | 2013-09-23 15:12:34 +0530 | [diff] [blame] | 236 | int r; |
Archit Taneja | c1577c1 | 2013-10-08 12:55:26 +0530 | [diff] [blame] | 237 | struct resource *res; |
Archit Taneja | c1577c1 | 2013-10-08 12:55:26 +0530 | [diff] [blame] | 238 | |
Tomi Valkeinen | 86c9305 | 2016-05-17 17:07:46 +0300 | [diff] [blame^] | 239 | pll->pdev = pdev; |
Tomi Valkeinen | 03aafa2 | 2014-10-16 15:31:38 +0300 | [diff] [blame] | 240 | pll->wp = wp; |
| 241 | |
Tomi Valkeinen | 7760150 | 2013-12-17 14:41:14 +0200 | [diff] [blame] | 242 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pll"); |
Archit Taneja | c1577c1 | 2013-10-08 12:55:26 +0530 | [diff] [blame] | 243 | if (!res) { |
Tomi Valkeinen | 59b3d38 | 2014-04-28 16:11:01 +0300 | [diff] [blame] | 244 | DSSERR("can't get PLL mem resource\n"); |
| 245 | return -EINVAL; |
Archit Taneja | c1577c1 | 2013-10-08 12:55:26 +0530 | [diff] [blame] | 246 | } |
| 247 | |
Tomi Valkeinen | 59b3d38 | 2014-04-28 16:11:01 +0300 | [diff] [blame] | 248 | pll->base = devm_ioremap_resource(&pdev->dev, res); |
Tomi Valkeinen | 2b22df8 | 2014-05-23 14:50:09 +0300 | [diff] [blame] | 249 | if (IS_ERR(pll->base)) { |
Archit Taneja | c1577c1 | 2013-10-08 12:55:26 +0530 | [diff] [blame] | 250 | DSSERR("can't ioremap PLLCTRL\n"); |
Tomi Valkeinen | 2b22df8 | 2014-05-23 14:50:09 +0300 | [diff] [blame] | 251 | return PTR_ERR(pll->base); |
Archit Taneja | c1577c1 | 2013-10-08 12:55:26 +0530 | [diff] [blame] | 252 | } |
| 253 | |
Tomi Valkeinen | c84c3a5 | 2014-10-22 15:02:17 +0300 | [diff] [blame] | 254 | r = dsi_init_pll_data(pdev, pll); |
| 255 | if (r) { |
| 256 | DSSERR("failed to init HDMI PLL\n"); |
| 257 | return r; |
| 258 | } |
| 259 | |
Archit Taneja | c1577c1 | 2013-10-08 12:55:26 +0530 | [diff] [blame] | 260 | return 0; |
| 261 | } |
Tomi Valkeinen | c84c3a5 | 2014-10-22 15:02:17 +0300 | [diff] [blame] | 262 | |
| 263 | void hdmi_pll_uninit(struct hdmi_pll_data *hpll) |
| 264 | { |
| 265 | struct dss_pll *pll = &hpll->pll; |
| 266 | |
| 267 | dss_pll_unregister(pll); |
| 268 | } |