blob: f04d435c4c0f06e343326282e17727d2dfd5b582 [file] [log] [blame]
Archit Tanejac1577c12013-10-08 12:55:26 +05301/*
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 Valkeinenac9f2422013-11-14 13:46:32 +020011#define DSS_SUBSYS_NAME "HDMIPLL"
12
Archit Tanejac1577c12013-10-08 12:55:26 +053013#include <linux/kernel.h>
14#include <linux/module.h>
Archit Tanejac1577c12013-10-08 12:55:26 +053015#include <linux/err.h>
16#include <linux/io.h>
17#include <linux/platform_device.h>
18#include <video/omapdss.h>
19
20#include "dss.h"
Archit Tanejaef269582013-09-12 17:45:57 +053021#include "hdmi.h"
Archit Tanejac1577c12013-10-08 12:55:26 +053022
Archit Taneja2d64b1b2013-09-23 15:12:34 +053023struct hdmi_pll_features {
Tomi Valkeinen8530c412014-09-15 15:48:39 +030024 bool has_refsel;
Archit Taneja2d64b1b2013-09-23 15:12:34 +053025 bool sys_reset;
Archit Taneja2d64b1b2013-09-23 15:12:34 +053026 unsigned long fint_min, fint_max;
27 u16 regm_max;
28 unsigned long dcofreq_low_min, dcofreq_low_max;
29 unsigned long dcofreq_high_min, dcofreq_high_max;
30};
31
32static const struct hdmi_pll_features *pll_feat;
33
Archit Tanejac1577c12013-10-08 12:55:26 +053034void hdmi_pll_dump(struct hdmi_pll_data *pll, struct seq_file *s)
35{
36#define DUMPPLL(r) seq_printf(s, "%-35s %08x\n", #r,\
37 hdmi_read_reg(pll->base, r))
38
39 DUMPPLL(PLLCTRL_PLL_CONTROL);
40 DUMPPLL(PLLCTRL_PLL_STATUS);
41 DUMPPLL(PLLCTRL_PLL_GO);
42 DUMPPLL(PLLCTRL_CFG1);
43 DUMPPLL(PLLCTRL_CFG2);
44 DUMPPLL(PLLCTRL_CFG3);
45 DUMPPLL(PLLCTRL_SSC_CFG1);
46 DUMPPLL(PLLCTRL_SSC_CFG2);
47 DUMPPLL(PLLCTRL_CFG4);
48}
49
Tomi Valkeinen33f13122014-09-15 15:40:47 +030050void hdmi_pll_compute(struct hdmi_pll_data *pll, unsigned long clkin,
51 unsigned long target_tmds)
Archit Tanejac1577c12013-10-08 12:55:26 +053052{
53 struct hdmi_pll_info *pi = &pll->info;
Tomi Valkeinen33f13122014-09-15 15:40:47 +030054 unsigned long fint, clkdco, clkout;
55 unsigned long target_bitclk, target_clkdco;
56 unsigned long min_dco;
57 unsigned n, m, mf, m2, sd;
Archit Tanejac1577c12013-10-08 12:55:26 +053058
Tomi Valkeinen33f13122014-09-15 15:40:47 +030059 DSSDBG("clkin %lu, target tmds %lu\n", clkin, target_tmds);
Archit Tanejac1577c12013-10-08 12:55:26 +053060
Tomi Valkeinen33f13122014-09-15 15:40:47 +030061 target_bitclk = target_tmds * 10;
Archit Tanejac1577c12013-10-08 12:55:26 +053062
Tomi Valkeinen33f13122014-09-15 15:40:47 +030063 /* Fint */
64 n = DIV_ROUND_UP(clkin, pll_feat->fint_max);
65 fint = clkin / n;
Archit Tanejac1577c12013-10-08 12:55:26 +053066
Tomi Valkeinen33f13122014-09-15 15:40:47 +030067 /* adjust m2 so that the clkdco will be high enough */
68 min_dco = roundup(pll_feat->dcofreq_low_min, fint);
69 m2 = DIV_ROUND_UP(min_dco, target_bitclk);
70 if (m2 == 0)
71 m2 = 1;
Archit Tanejac1577c12013-10-08 12:55:26 +053072
Tomi Valkeinen33f13122014-09-15 15:40:47 +030073 target_clkdco = target_bitclk * m2;
74 m = target_clkdco / fint;
75
76 clkdco = fint * m;
77
78 /* adjust clkdco with fractional mf */
79 if (WARN_ON(target_clkdco - clkdco > fint))
80 mf = 0;
Archit Taneja2d64b1b2013-09-23 15:12:34 +053081 else
Tomi Valkeinen33f13122014-09-15 15:40:47 +030082 mf = (u32)div_u64(262144ull * (target_clkdco - clkdco), fint);
Archit Tanejac1577c12013-10-08 12:55:26 +053083
Tomi Valkeinen33f13122014-09-15 15:40:47 +030084 if (mf > 0)
85 clkdco += (u32)div_u64((u64)mf * fint, 262144);
Archit Tanejac1577c12013-10-08 12:55:26 +053086
Tomi Valkeinen33f13122014-09-15 15:40:47 +030087 clkout = clkdco / m2;
Archit Tanejac1577c12013-10-08 12:55:26 +053088
Tomi Valkeinen33f13122014-09-15 15:40:47 +030089 /* sigma-delta */
90 sd = DIV_ROUND_UP(fint * m, 250000000);
Archit Tanejac1577c12013-10-08 12:55:26 +053091
Tomi Valkeinen33f13122014-09-15 15:40:47 +030092 DSSDBG("N = %u, M = %u, M.f = %u, M2 = %u, SD = %u\n",
93 n, m, mf, m2, sd);
94 DSSDBG("Fint %lu, clkdco %lu, clkout %lu\n", fint, clkdco, clkout);
95
96 pi->regn = n;
97 pi->regm = m;
98 pi->regmf = mf;
99 pi->regm2 = m2;
100 pi->regsd = sd;
101
102 pi->clkdco = clkdco;
103 pi->clkout = clkout;
Archit Tanejac1577c12013-10-08 12:55:26 +0530104}
105
Archit Tanejac1577c12013-10-08 12:55:26 +0530106static int hdmi_pll_config(struct hdmi_pll_data *pll)
107{
108 u32 r;
109 struct hdmi_pll_info *fmt = &pll->info;
110
111 /* PLL start always use manual mode */
112 REG_FLD_MOD(pll->base, PLLCTRL_PLL_CONTROL, 0x0, 0, 0);
113
114 r = hdmi_read_reg(pll->base, PLLCTRL_CFG1);
115 r = FLD_MOD(r, fmt->regm, 20, 9); /* CFG1_PLL_REGM */
116 r = FLD_MOD(r, fmt->regn - 1, 8, 1); /* CFG1_PLL_REGN */
117 hdmi_write_reg(pll->base, PLLCTRL_CFG1, r);
118
119 r = hdmi_read_reg(pll->base, PLLCTRL_CFG2);
120
121 r = FLD_MOD(r, 0x0, 12, 12); /* PLL_HIGHFREQ divide by 2 */
122 r = FLD_MOD(r, 0x1, 13, 13); /* PLL_REFEN */
123 r = FLD_MOD(r, 0x0, 14, 14); /* PHY_CLKINEN de-assert during locking */
Tomi Valkeinen8530c412014-09-15 15:48:39 +0300124 if (pll_feat->has_refsel)
125 r = FLD_MOD(r, 0x3, 22, 21); /* REFSEL = SYSCLK */
Archit Tanejac1577c12013-10-08 12:55:26 +0530126
Tomi Valkeinen33f13122014-09-15 15:40:47 +0300127 if (fmt->clkdco > pll_feat->dcofreq_low_max)
Archit Tanejac1577c12013-10-08 12:55:26 +0530128 r = FLD_MOD(r, 0x4, 3, 1); /* 1000MHz and 2000MHz */
Tomi Valkeinen0856eba2014-09-15 15:50:01 +0300129 else
Archit Tanejac1577c12013-10-08 12:55:26 +0530130 r = FLD_MOD(r, 0x2, 3, 1); /* 500MHz and 1000MHz */
Archit Tanejac1577c12013-10-08 12:55:26 +0530131
132 hdmi_write_reg(pll->base, PLLCTRL_CFG2, r);
133
Tomi Valkeinen0856eba2014-09-15 15:50:01 +0300134 REG_FLD_MOD(pll->base, PLLCTRL_CFG3, fmt->regsd, 17, 10);
135
Archit Tanejac1577c12013-10-08 12:55:26 +0530136 r = hdmi_read_reg(pll->base, PLLCTRL_CFG4);
137 r = FLD_MOD(r, fmt->regm2, 24, 18);
138 r = FLD_MOD(r, fmt->regmf, 17, 0);
139 hdmi_write_reg(pll->base, PLLCTRL_CFG4, r);
140
141 /* go now */
142 REG_FLD_MOD(pll->base, PLLCTRL_PLL_GO, 0x1, 0, 0);
143
144 /* wait for bit change */
145 if (hdmi_wait_for_bit_change(pll->base, PLLCTRL_PLL_GO,
Tomi Valkeinen88e3c762014-10-16 16:52:16 +0300146 0, 0, 0) != 0) {
147 DSSERR("PLL GO bit not clearing\n");
Archit Tanejac1577c12013-10-08 12:55:26 +0530148 return -ETIMEDOUT;
149 }
150
151 /* Wait till the lock bit is set in PLL status */
152 if (hdmi_wait_for_bit_change(pll->base,
153 PLLCTRL_PLL_STATUS, 1, 1, 1) != 1) {
Tomi Valkeinenac9f2422013-11-14 13:46:32 +0200154 DSSERR("cannot lock PLL\n");
155 DSSERR("CFG1 0x%x\n",
Archit Tanejac1577c12013-10-08 12:55:26 +0530156 hdmi_read_reg(pll->base, PLLCTRL_CFG1));
Tomi Valkeinenac9f2422013-11-14 13:46:32 +0200157 DSSERR("CFG2 0x%x\n",
Archit Tanejac1577c12013-10-08 12:55:26 +0530158 hdmi_read_reg(pll->base, PLLCTRL_CFG2));
Tomi Valkeinenac9f2422013-11-14 13:46:32 +0200159 DSSERR("CFG4 0x%x\n",
Archit Tanejac1577c12013-10-08 12:55:26 +0530160 hdmi_read_reg(pll->base, PLLCTRL_CFG4));
161 return -ETIMEDOUT;
162 }
163
Tomi Valkeinenac9f2422013-11-14 13:46:32 +0200164 DSSDBG("PLL locked!\n");
Archit Tanejac1577c12013-10-08 12:55:26 +0530165
166 return 0;
167}
168
169static int hdmi_pll_reset(struct hdmi_pll_data *pll)
170{
171 /* SYSRESET controlled by power FSM */
Archit Taneja2d64b1b2013-09-23 15:12:34 +0530172 REG_FLD_MOD(pll->base, PLLCTRL_PLL_CONTROL, pll_feat->sys_reset, 3, 3);
Archit Tanejac1577c12013-10-08 12:55:26 +0530173
174 /* READ 0x0 reset is in progress */
175 if (hdmi_wait_for_bit_change(pll->base, PLLCTRL_PLL_STATUS, 0, 0, 1)
176 != 1) {
Tomi Valkeinenac9f2422013-11-14 13:46:32 +0200177 DSSERR("Failed to sysreset PLL\n");
Archit Tanejac1577c12013-10-08 12:55:26 +0530178 return -ETIMEDOUT;
179 }
180
181 return 0;
182}
183
184int hdmi_pll_enable(struct hdmi_pll_data *pll, struct hdmi_wp_data *wp)
185{
186 u16 r = 0;
187
188 r = hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_ALLOFF);
189 if (r)
190 return r;
191
192 r = hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_BOTHON_ALLCLKS);
193 if (r)
194 return r;
195
196 r = hdmi_pll_reset(pll);
197 if (r)
198 return r;
199
200 r = hdmi_pll_config(pll);
201 if (r)
202 return r;
203
204 return 0;
205}
206
207void hdmi_pll_disable(struct hdmi_pll_data *pll, struct hdmi_wp_data *wp)
208{
209 hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_ALLOFF);
210}
211
Archit Taneja2d64b1b2013-09-23 15:12:34 +0530212static const struct hdmi_pll_features omap44xx_pll_feats = {
213 .sys_reset = false,
Archit Taneja2d64b1b2013-09-23 15:12:34 +0530214 .fint_min = 500000,
215 .fint_max = 2500000,
216 .regm_max = 4095,
217 .dcofreq_low_min = 500000000,
218 .dcofreq_low_max = 1000000000,
219 .dcofreq_high_min = 1000000000,
220 .dcofreq_high_max = 2000000000,
221};
222
223static const struct hdmi_pll_features omap54xx_pll_feats = {
Tomi Valkeinen8530c412014-09-15 15:48:39 +0300224 .has_refsel = true,
Archit Taneja2d64b1b2013-09-23 15:12:34 +0530225 .sys_reset = true,
Archit Taneja2d64b1b2013-09-23 15:12:34 +0530226 .fint_min = 620000,
227 .fint_max = 2500000,
228 .regm_max = 2046,
229 .dcofreq_low_min = 750000000,
230 .dcofreq_low_max = 1500000000,
231 .dcofreq_high_min = 1250000000,
232 .dcofreq_high_max = 2500000000UL,
233};
234
235static int hdmi_pll_init_features(struct platform_device *pdev)
236{
237 struct hdmi_pll_features *dst;
238 const struct hdmi_pll_features *src;
239
240 dst = devm_kzalloc(&pdev->dev, sizeof(*dst), GFP_KERNEL);
241 if (!dst) {
242 dev_err(&pdev->dev, "Failed to allocate HDMI PHY Features\n");
243 return -ENOMEM;
244 }
245
246 switch (omapdss_get_version()) {
247 case OMAPDSS_VER_OMAP4430_ES1:
248 case OMAPDSS_VER_OMAP4430_ES2:
249 case OMAPDSS_VER_OMAP4:
250 src = &omap44xx_pll_feats;
251 break;
252
253 case OMAPDSS_VER_OMAP5:
254 src = &omap54xx_pll_feats;
255 break;
256
257 default:
258 return -ENODEV;
259 }
260
261 memcpy(dst, src, sizeof(*dst));
262 pll_feat = dst;
263
264 return 0;
265}
266
Archit Tanejac1577c12013-10-08 12:55:26 +0530267int hdmi_pll_init(struct platform_device *pdev, struct hdmi_pll_data *pll)
268{
Archit Taneja2d64b1b2013-09-23 15:12:34 +0530269 int r;
Archit Tanejac1577c12013-10-08 12:55:26 +0530270 struct resource *res;
Archit Tanejac1577c12013-10-08 12:55:26 +0530271
Archit Taneja2d64b1b2013-09-23 15:12:34 +0530272 r = hdmi_pll_init_features(pdev);
273 if (r)
274 return r;
275
Tomi Valkeinen77601502013-12-17 14:41:14 +0200276 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pll");
Archit Tanejac1577c12013-10-08 12:55:26 +0530277 if (!res) {
Tomi Valkeinen59b3d382014-04-28 16:11:01 +0300278 DSSERR("can't get PLL mem resource\n");
279 return -EINVAL;
Archit Tanejac1577c12013-10-08 12:55:26 +0530280 }
281
Tomi Valkeinen59b3d382014-04-28 16:11:01 +0300282 pll->base = devm_ioremap_resource(&pdev->dev, res);
Tomi Valkeinen2b22df82014-05-23 14:50:09 +0300283 if (IS_ERR(pll->base)) {
Archit Tanejac1577c12013-10-08 12:55:26 +0530284 DSSERR("can't ioremap PLLCTRL\n");
Tomi Valkeinen2b22df82014-05-23 14:50:09 +0300285 return PTR_ERR(pll->base);
Archit Tanejac1577c12013-10-08 12:55:26 +0530286 }
287
288 return 0;
289}