diff options
Diffstat (limited to 'drivers/video/fbdev/omap2/dss/hdmi_pll.c')
-rw-r--r-- | drivers/video/fbdev/omap2/dss/hdmi_pll.c | 313 |
1 files changed, 137 insertions, 176 deletions
diff --git a/drivers/video/fbdev/omap2/dss/hdmi_pll.c b/drivers/video/fbdev/omap2/dss/hdmi_pll.c index 6d92bb32fe51..87accdb59c81 100644 --- a/drivers/video/fbdev/omap2/dss/hdmi_pll.c +++ b/drivers/video/fbdev/omap2/dss/hdmi_pll.c | |||
@@ -15,26 +15,13 @@ | |||
15 | #include <linux/err.h> | 15 | #include <linux/err.h> |
16 | #include <linux/io.h> | 16 | #include <linux/io.h> |
17 | #include <linux/platform_device.h> | 17 | #include <linux/platform_device.h> |
18 | #include <linux/clk.h> | ||
19 | |||
18 | #include <video/omapdss.h> | 20 | #include <video/omapdss.h> |
19 | 21 | ||
20 | #include "dss.h" | 22 | #include "dss.h" |
21 | #include "hdmi.h" | 23 | #include "hdmi.h" |
22 | 24 | ||
23 | #define HDMI_DEFAULT_REGN 16 | ||
24 | #define HDMI_DEFAULT_REGM2 1 | ||
25 | |||
26 | struct hdmi_pll_features { | ||
27 | bool sys_reset; | ||
28 | /* this is a hack, need to replace it with a better computation of M2 */ | ||
29 | bool bound_dcofreq; | ||
30 | unsigned long fint_min, fint_max; | ||
31 | u16 regm_max; | ||
32 | unsigned long dcofreq_low_min, dcofreq_low_max; | ||
33 | unsigned long dcofreq_high_min, dcofreq_high_max; | ||
34 | }; | ||
35 | |||
36 | static const struct hdmi_pll_features *pll_feat; | ||
37 | |||
38 | void hdmi_pll_dump(struct hdmi_pll_data *pll, struct seq_file *s) | 25 | void hdmi_pll_dump(struct hdmi_pll_data *pll, struct seq_file *s) |
39 | { | 26 | { |
40 | #define DUMPPLL(r) seq_printf(s, "%-35s %08x\n", #r,\ | 27 | #define DUMPPLL(r) seq_printf(s, "%-35s %08x\n", #r,\ |
@@ -51,228 +38,189 @@ void hdmi_pll_dump(struct hdmi_pll_data *pll, struct seq_file *s) | |||
51 | DUMPPLL(PLLCTRL_CFG4); | 38 | DUMPPLL(PLLCTRL_CFG4); |
52 | } | 39 | } |
53 | 40 | ||
54 | void hdmi_pll_compute(struct hdmi_pll_data *pll, unsigned long clkin, int phy) | 41 | void hdmi_pll_compute(struct hdmi_pll_data *pll, |
42 | unsigned long target_tmds, struct dss_pll_clock_info *pi) | ||
55 | { | 43 | { |
56 | struct hdmi_pll_info *pi = &pll->info; | 44 | unsigned long fint, clkdco, clkout; |
57 | unsigned long refclk; | 45 | unsigned long target_bitclk, target_clkdco; |
58 | u32 mf; | 46 | unsigned long min_dco; |
47 | unsigned n, m, mf, m2, sd; | ||
48 | unsigned long clkin; | ||
49 | const struct dss_pll_hw *hw = pll->pll.hw; | ||
59 | 50 | ||
60 | /* use our funky units */ | 51 | clkin = clk_get_rate(pll->pll.clkin); |
61 | clkin /= 10000; | ||
62 | 52 | ||
63 | /* | 53 | DSSDBG("clkin %lu, target tmds %lu\n", clkin, target_tmds); |
64 | * Input clock is predivided by N + 1 | ||
65 | * out put of which is reference clk | ||
66 | */ | ||
67 | 54 | ||
68 | pi->regn = HDMI_DEFAULT_REGN; | 55 | target_bitclk = target_tmds * 10; |
69 | 56 | ||
70 | refclk = clkin / pi->regn; | 57 | /* Fint */ |
58 | n = DIV_ROUND_UP(clkin, hw->fint_max); | ||
59 | fint = clkin / n; | ||
71 | 60 | ||
72 | /* temorary hack to make sure DCO freq isn't calculated too low */ | 61 | /* adjust m2 so that the clkdco will be high enough */ |
73 | if (pll_feat->bound_dcofreq && phy <= 65000) | 62 | min_dco = roundup(hw->clkdco_min, fint); |
74 | pi->regm2 = 3; | 63 | m2 = DIV_ROUND_UP(min_dco, target_bitclk); |
75 | else | 64 | if (m2 == 0) |
76 | pi->regm2 = HDMI_DEFAULT_REGM2; | 65 | m2 = 1; |
77 | |||
78 | /* | ||
79 | * multiplier is pixel_clk/ref_clk | ||
80 | * Multiplying by 100 to avoid fractional part removal | ||
81 | */ | ||
82 | pi->regm = phy * pi->regm2 / refclk; | ||
83 | |||
84 | /* | ||
85 | * fractional multiplier is remainder of the difference between | ||
86 | * multiplier and actual phy(required pixel clock thus should be | ||
87 | * multiplied by 2^18(262144) divided by the reference clock | ||
88 | */ | ||
89 | mf = (phy - pi->regm / pi->regm2 * refclk) * 262144; | ||
90 | pi->regmf = pi->regm2 * mf / refclk; | ||
91 | |||
92 | /* | ||
93 | * Dcofreq should be set to 1 if required pixel clock | ||
94 | * is greater than 1000MHz | ||
95 | */ | ||
96 | pi->dcofreq = phy > 1000 * 100; | ||
97 | pi->regsd = ((pi->regm * clkin / 10) / (pi->regn * 250) + 5) / 10; | ||
98 | |||
99 | /* Set the reference clock to sysclk reference */ | ||
100 | pi->refsel = HDMI_REFSEL_SYSCLK; | ||
101 | |||
102 | DSSDBG("M = %d Mf = %d\n", pi->regm, pi->regmf); | ||
103 | DSSDBG("range = %d sd = %d\n", pi->dcofreq, pi->regsd); | ||
104 | } | ||
105 | 66 | ||
67 | target_clkdco = target_bitclk * m2; | ||
68 | m = target_clkdco / fint; | ||
106 | 69 | ||
107 | static int hdmi_pll_config(struct hdmi_pll_data *pll) | 70 | clkdco = fint * m; |
108 | { | ||
109 | u32 r; | ||
110 | struct hdmi_pll_info *fmt = &pll->info; | ||
111 | 71 | ||
112 | /* PLL start always use manual mode */ | 72 | /* adjust clkdco with fractional mf */ |
113 | REG_FLD_MOD(pll->base, PLLCTRL_PLL_CONTROL, 0x0, 0, 0); | 73 | if (WARN_ON(target_clkdco - clkdco > fint)) |
114 | 74 | mf = 0; | |
115 | r = hdmi_read_reg(pll->base, PLLCTRL_CFG1); | ||
116 | r = FLD_MOD(r, fmt->regm, 20, 9); /* CFG1_PLL_REGM */ | ||
117 | r = FLD_MOD(r, fmt->regn - 1, 8, 1); /* CFG1_PLL_REGN */ | ||
118 | hdmi_write_reg(pll->base, PLLCTRL_CFG1, r); | ||
119 | |||
120 | r = hdmi_read_reg(pll->base, PLLCTRL_CFG2); | ||
121 | |||
122 | r = FLD_MOD(r, 0x0, 12, 12); /* PLL_HIGHFREQ divide by 2 */ | ||
123 | r = FLD_MOD(r, 0x1, 13, 13); /* PLL_REFEN */ | ||
124 | r = FLD_MOD(r, 0x0, 14, 14); /* PHY_CLKINEN de-assert during locking */ | ||
125 | r = FLD_MOD(r, fmt->refsel, 22, 21); /* REFSEL */ | ||
126 | |||
127 | if (fmt->dcofreq) | ||
128 | r = FLD_MOD(r, 0x4, 3, 1); /* 1000MHz and 2000MHz */ | ||
129 | else | 75 | else |
130 | r = FLD_MOD(r, 0x2, 3, 1); /* 500MHz and 1000MHz */ | 76 | mf = (u32)div_u64(262144ull * (target_clkdco - clkdco), fint); |
131 | |||
132 | hdmi_write_reg(pll->base, PLLCTRL_CFG2, r); | ||
133 | |||
134 | REG_FLD_MOD(pll->base, PLLCTRL_CFG3, fmt->regsd, 17, 10); | ||
135 | 77 | ||
136 | r = hdmi_read_reg(pll->base, PLLCTRL_CFG4); | 78 | if (mf > 0) |
137 | r = FLD_MOD(r, fmt->regm2, 24, 18); | 79 | clkdco += (u32)div_u64((u64)mf * fint, 262144); |
138 | r = FLD_MOD(r, fmt->regmf, 17, 0); | ||
139 | hdmi_write_reg(pll->base, PLLCTRL_CFG4, r); | ||
140 | 80 | ||
141 | /* go now */ | 81 | clkout = clkdco / m2; |
142 | REG_FLD_MOD(pll->base, PLLCTRL_PLL_GO, 0x1, 0, 0); | ||
143 | 82 | ||
144 | /* wait for bit change */ | 83 | /* sigma-delta */ |
145 | if (hdmi_wait_for_bit_change(pll->base, PLLCTRL_PLL_GO, | 84 | sd = DIV_ROUND_UP(fint * m, 250000000); |
146 | 0, 0, 0) != 0) { | ||
147 | DSSERR("PLL GO bit not clearing\n"); | ||
148 | return -ETIMEDOUT; | ||
149 | } | ||
150 | 85 | ||
151 | /* Wait till the lock bit is set in PLL status */ | 86 | DSSDBG("N = %u, M = %u, M.f = %u, M2 = %u, SD = %u\n", |
152 | if (hdmi_wait_for_bit_change(pll->base, | 87 | n, m, mf, m2, sd); |
153 | PLLCTRL_PLL_STATUS, 1, 1, 1) != 1) { | 88 | DSSDBG("Fint %lu, clkdco %lu, clkout %lu\n", fint, clkdco, clkout); |
154 | DSSERR("cannot lock PLL\n"); | ||
155 | DSSERR("CFG1 0x%x\n", | ||
156 | hdmi_read_reg(pll->base, PLLCTRL_CFG1)); | ||
157 | DSSERR("CFG2 0x%x\n", | ||
158 | hdmi_read_reg(pll->base, PLLCTRL_CFG2)); | ||
159 | DSSERR("CFG4 0x%x\n", | ||
160 | hdmi_read_reg(pll->base, PLLCTRL_CFG4)); | ||
161 | return -ETIMEDOUT; | ||
162 | } | ||
163 | 89 | ||
164 | DSSDBG("PLL locked!\n"); | 90 | pi->n = n; |
91 | pi->m = m; | ||
92 | pi->mf = mf; | ||
93 | pi->mX[0] = m2; | ||
94 | pi->sd = sd; | ||
165 | 95 | ||
166 | return 0; | 96 | pi->fint = fint; |
97 | pi->clkdco = clkdco; | ||
98 | pi->clkout[0] = clkout; | ||
167 | } | 99 | } |
168 | 100 | ||
169 | static int hdmi_pll_reset(struct hdmi_pll_data *pll) | 101 | static int hdmi_pll_enable(struct dss_pll *dsspll) |
170 | { | ||
171 | /* SYSRESET controlled by power FSM */ | ||
172 | REG_FLD_MOD(pll->base, PLLCTRL_PLL_CONTROL, pll_feat->sys_reset, 3, 3); | ||
173 | |||
174 | /* READ 0x0 reset is in progress */ | ||
175 | if (hdmi_wait_for_bit_change(pll->base, PLLCTRL_PLL_STATUS, 0, 0, 1) | ||
176 | != 1) { | ||
177 | DSSERR("Failed to sysreset PLL\n"); | ||
178 | return -ETIMEDOUT; | ||
179 | } | ||
180 | |||
181 | return 0; | ||
182 | } | ||
183 | |||
184 | int hdmi_pll_enable(struct hdmi_pll_data *pll, struct hdmi_wp_data *wp) | ||
185 | { | 102 | { |
103 | struct hdmi_pll_data *pll = container_of(dsspll, struct hdmi_pll_data, pll); | ||
104 | struct hdmi_wp_data *wp = pll->wp; | ||
186 | u16 r = 0; | 105 | u16 r = 0; |
187 | 106 | ||
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); | 107 | r = hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_BOTHON_ALLCLKS); |
193 | if (r) | 108 | if (r) |
194 | return r; | 109 | return r; |
195 | 110 | ||
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; | 111 | return 0; |
205 | } | 112 | } |
206 | 113 | ||
207 | void hdmi_pll_disable(struct hdmi_pll_data *pll, struct hdmi_wp_data *wp) | 114 | static void hdmi_pll_disable(struct dss_pll *dsspll) |
208 | { | 115 | { |
116 | struct hdmi_pll_data *pll = container_of(dsspll, struct hdmi_pll_data, pll); | ||
117 | struct hdmi_wp_data *wp = pll->wp; | ||
118 | |||
209 | hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_ALLOFF); | 119 | hdmi_wp_set_pll_pwr(wp, HDMI_PLLPWRCMD_ALLOFF); |
210 | } | 120 | } |
211 | 121 | ||
212 | static const struct hdmi_pll_features omap44xx_pll_feats = { | 122 | static const struct dss_pll_ops dsi_pll_ops = { |
213 | .sys_reset = false, | 123 | .enable = hdmi_pll_enable, |
214 | .bound_dcofreq = false, | 124 | .disable = hdmi_pll_disable, |
215 | .fint_min = 500000, | 125 | .set_config = dss_pll_write_config_type_b, |
216 | .fint_max = 2500000, | ||
217 | .regm_max = 4095, | ||
218 | .dcofreq_low_min = 500000000, | ||
219 | .dcofreq_low_max = 1000000000, | ||
220 | .dcofreq_high_min = 1000000000, | ||
221 | .dcofreq_high_max = 2000000000, | ||
222 | }; | 126 | }; |
223 | 127 | ||
224 | static const struct hdmi_pll_features omap54xx_pll_feats = { | 128 | static const struct dss_pll_hw dss_omap4_hdmi_pll_hw = { |
225 | .sys_reset = true, | 129 | .n_max = 255, |
226 | .bound_dcofreq = true, | 130 | .m_min = 20, |
227 | .fint_min = 620000, | 131 | .m_max = 4095, |
228 | .fint_max = 2500000, | 132 | .mX_max = 127, |
229 | .regm_max = 2046, | 133 | .fint_min = 500000, |
230 | .dcofreq_low_min = 750000000, | 134 | .fint_max = 2500000, |
231 | .dcofreq_low_max = 1500000000, | 135 | .clkdco_max = 1800000000, |
232 | .dcofreq_high_min = 1250000000, | 136 | |
233 | .dcofreq_high_max = 2500000000UL, | 137 | .clkdco_min = 500000000, |
138 | .clkdco_low = 1000000000, | ||
139 | .clkdco_max = 2000000000, | ||
140 | |||
141 | .n_msb = 8, | ||
142 | .n_lsb = 1, | ||
143 | .m_msb = 20, | ||
144 | .m_lsb = 9, | ||
145 | |||
146 | .mX_msb[0] = 24, | ||
147 | .mX_lsb[0] = 18, | ||
148 | |||
149 | .has_selfreqdco = true, | ||
234 | }; | 150 | }; |
235 | 151 | ||
236 | static int hdmi_pll_init_features(struct platform_device *pdev) | 152 | static const struct dss_pll_hw dss_omap5_hdmi_pll_hw = { |
153 | .n_max = 255, | ||
154 | .m_min = 20, | ||
155 | .m_max = 2045, | ||
156 | .mX_max = 127, | ||
157 | .fint_min = 620000, | ||
158 | .fint_max = 2500000, | ||
159 | .clkdco_max = 1800000000, | ||
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) | ||
237 | { | 178 | { |
238 | struct hdmi_pll_features *dst; | 179 | struct dss_pll *pll = &hpll->pll; |
239 | const struct hdmi_pll_features *src; | 180 | struct clk *clk; |
181 | int r; | ||
240 | 182 | ||
241 | dst = devm_kzalloc(&pdev->dev, sizeof(*dst), GFP_KERNEL); | 183 | clk = devm_clk_get(&pdev->dev, "sys_clk"); |
242 | if (!dst) { | 184 | if (IS_ERR(clk)) { |
243 | dev_err(&pdev->dev, "Failed to allocate HDMI PHY Features\n"); | 185 | DSSERR("can't get sys_clk\n"); |
244 | return -ENOMEM; | 186 | return PTR_ERR(clk); |
245 | } | 187 | } |
246 | 188 | ||
189 | pll->name = "hdmi"; | ||
190 | pll->base = hpll->base; | ||
191 | pll->clkin = clk; | ||
192 | |||
247 | switch (omapdss_get_version()) { | 193 | switch (omapdss_get_version()) { |
248 | case OMAPDSS_VER_OMAP4430_ES1: | 194 | case OMAPDSS_VER_OMAP4430_ES1: |
249 | case OMAPDSS_VER_OMAP4430_ES2: | 195 | case OMAPDSS_VER_OMAP4430_ES2: |
250 | case OMAPDSS_VER_OMAP4: | 196 | case OMAPDSS_VER_OMAP4: |
251 | src = &omap44xx_pll_feats; | 197 | pll->hw = &dss_omap4_hdmi_pll_hw; |
252 | break; | 198 | break; |
253 | 199 | ||
254 | case OMAPDSS_VER_OMAP5: | 200 | case OMAPDSS_VER_OMAP5: |
255 | src = &omap54xx_pll_feats; | 201 | pll->hw = &dss_omap5_hdmi_pll_hw; |
256 | break; | 202 | break; |
257 | 203 | ||
258 | default: | 204 | default: |
259 | return -ENODEV; | 205 | return -ENODEV; |
260 | } | 206 | } |
261 | 207 | ||
262 | memcpy(dst, src, sizeof(*dst)); | 208 | pll->ops = &dsi_pll_ops; |
263 | pll_feat = dst; | 209 | |
210 | r = dss_pll_register(pll); | ||
211 | if (r) | ||
212 | return r; | ||
264 | 213 | ||
265 | return 0; | 214 | return 0; |
266 | } | 215 | } |
267 | 216 | ||
268 | int hdmi_pll_init(struct platform_device *pdev, struct hdmi_pll_data *pll) | 217 | int hdmi_pll_init(struct platform_device *pdev, struct hdmi_pll_data *pll, |
218 | struct hdmi_wp_data *wp) | ||
269 | { | 219 | { |
270 | int r; | 220 | int r; |
271 | struct resource *res; | 221 | struct resource *res; |
272 | 222 | ||
273 | r = hdmi_pll_init_features(pdev); | 223 | pll->wp = wp; |
274 | if (r) | ||
275 | return r; | ||
276 | 224 | ||
277 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pll"); | 225 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pll"); |
278 | if (!res) { | 226 | if (!res) { |
@@ -286,5 +234,18 @@ int hdmi_pll_init(struct platform_device *pdev, struct hdmi_pll_data *pll) | |||
286 | return PTR_ERR(pll->base); | 234 | return PTR_ERR(pll->base); |
287 | } | 235 | } |
288 | 236 | ||
237 | r = dsi_init_pll_data(pdev, pll); | ||
238 | if (r) { | ||
239 | DSSERR("failed to init HDMI PLL\n"); | ||
240 | return r; | ||
241 | } | ||
242 | |||
289 | return 0; | 243 | return 0; |
290 | } | 244 | } |
245 | |||
246 | void hdmi_pll_uninit(struct hdmi_pll_data *hpll) | ||
247 | { | ||
248 | struct dss_pll *pll = &hpll->pll; | ||
249 | |||
250 | dss_pll_unregister(pll); | ||
251 | } | ||