1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2016, The Linux Foundation. All rights reserved. |
4 | * Copyright (C) 2013 Red Hat |
5 | * Author: Rob Clark <robdclark@gmail.com> |
6 | */ |
7 | |
8 | #include <linux/clk-provider.h> |
9 | #include <linux/delay.h> |
10 | |
11 | #include "hdmi.h" |
12 | |
13 | struct hdmi_pll_8960 { |
14 | struct platform_device *pdev; |
15 | struct clk_hw clk_hw; |
16 | void __iomem *mmio; |
17 | |
18 | unsigned long pixclk; |
19 | }; |
20 | |
21 | #define hw_clk_to_pll(x) container_of(x, struct hdmi_pll_8960, clk_hw) |
22 | |
23 | /* |
24 | * HDMI PLL: |
25 | * |
26 | * To get the parent clock setup properly, we need to plug in hdmi pll |
27 | * configuration into common-clock-framework. |
28 | */ |
29 | |
30 | struct pll_rate { |
31 | unsigned long rate; |
32 | int num_reg; |
33 | struct { |
34 | u32 val; |
35 | u32 reg; |
36 | } conf[32]; |
37 | }; |
38 | |
39 | /* NOTE: keep sorted highest freq to lowest: */ |
40 | static const struct pll_rate freqtbl[] = { |
41 | { 154000000, 14, { |
42 | { 0x08, REG_HDMI_8960_PHY_PLL_REFCLK_CFG }, |
43 | { 0x20, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 }, |
44 | { 0xf9, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 }, |
45 | { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 }, |
46 | { 0x03, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 }, |
47 | { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 }, |
48 | { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3 }, |
49 | { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 }, |
50 | { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 }, |
51 | { 0x0d, REG_HDMI_8960_PHY_PLL_SDM_CFG0 }, |
52 | { 0x4d, REG_HDMI_8960_PHY_PLL_SDM_CFG1 }, |
53 | { 0x5e, REG_HDMI_8960_PHY_PLL_SDM_CFG2 }, |
54 | { 0x42, REG_HDMI_8960_PHY_PLL_SDM_CFG3 }, |
55 | { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4 }, |
56 | } |
57 | }, |
58 | /* 1080p60/1080p50 case */ |
59 | { 148500000, 27, { |
60 | { 0x02, REG_HDMI_8960_PHY_PLL_REFCLK_CFG }, |
61 | { 0x02, REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG }, |
62 | { 0x01, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 }, |
63 | { 0x33, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 }, |
64 | { 0x2c, REG_HDMI_8960_PHY_PLL_IDAC_ADJ_CFG }, |
65 | { 0x06, REG_HDMI_8960_PHY_PLL_I_VI_KVCO_CFG }, |
66 | { 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B }, |
67 | { 0x76, REG_HDMI_8960_PHY_PLL_SDM_CFG0 }, |
68 | { 0x01, REG_HDMI_8960_PHY_PLL_SDM_CFG1 }, |
69 | { 0x4c, REG_HDMI_8960_PHY_PLL_SDM_CFG2 }, |
70 | { 0xc0, REG_HDMI_8960_PHY_PLL_SDM_CFG3 }, |
71 | { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4 }, |
72 | { 0x9a, REG_HDMI_8960_PHY_PLL_SSC_CFG0 }, |
73 | { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG1 }, |
74 | { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG2 }, |
75 | { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG3 }, |
76 | { 0x10, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0 }, |
77 | { 0x1a, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1 }, |
78 | { 0x0d, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2 }, |
79 | { 0xe6, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 }, |
80 | { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 }, |
81 | { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 }, |
82 | { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3 }, |
83 | { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 }, |
84 | { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 }, |
85 | { 0x33, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6 }, |
86 | { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7 }, |
87 | } |
88 | }, |
89 | { 108000000, 13, { |
90 | { 0x08, REG_HDMI_8960_PHY_PLL_REFCLK_CFG }, |
91 | { 0x21, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 }, |
92 | { 0xf9, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 }, |
93 | { 0x1c, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 }, |
94 | { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 }, |
95 | { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 }, |
96 | { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 }, |
97 | { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 }, |
98 | { 0x49, REG_HDMI_8960_PHY_PLL_SDM_CFG0 }, |
99 | { 0x49, REG_HDMI_8960_PHY_PLL_SDM_CFG1 }, |
100 | { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG2 }, |
101 | { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG3 }, |
102 | { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4 }, |
103 | } |
104 | }, |
105 | /* 720p60/720p50/1080i60/1080i50/1080p24/1080p30/1080p25 */ |
106 | { 74250000, 8, { |
107 | { 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B }, |
108 | { 0x12, REG_HDMI_8960_PHY_PLL_REFCLK_CFG }, |
109 | { 0x01, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 }, |
110 | { 0x33, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 }, |
111 | { 0x76, REG_HDMI_8960_PHY_PLL_SDM_CFG0 }, |
112 | { 0xe6, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 }, |
113 | { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 }, |
114 | { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 }, |
115 | } |
116 | }, |
117 | { 74176000, 14, { |
118 | { 0x18, REG_HDMI_8960_PHY_PLL_REFCLK_CFG }, |
119 | { 0x20, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 }, |
120 | { 0xf9, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 }, |
121 | { 0xe5, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 }, |
122 | { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 }, |
123 | { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 }, |
124 | { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3 }, |
125 | { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 }, |
126 | { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 }, |
127 | { 0x0c, REG_HDMI_8960_PHY_PLL_SDM_CFG0 }, |
128 | { 0x4c, REG_HDMI_8960_PHY_PLL_SDM_CFG1 }, |
129 | { 0x7d, REG_HDMI_8960_PHY_PLL_SDM_CFG2 }, |
130 | { 0xbc, REG_HDMI_8960_PHY_PLL_SDM_CFG3 }, |
131 | { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4 }, |
132 | } |
133 | }, |
134 | { 65000000, 14, { |
135 | { 0x18, REG_HDMI_8960_PHY_PLL_REFCLK_CFG }, |
136 | { 0x20, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 }, |
137 | { 0xf9, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 }, |
138 | { 0x8a, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 }, |
139 | { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 }, |
140 | { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 }, |
141 | { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3 }, |
142 | { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 }, |
143 | { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 }, |
144 | { 0x0b, REG_HDMI_8960_PHY_PLL_SDM_CFG0 }, |
145 | { 0x4b, REG_HDMI_8960_PHY_PLL_SDM_CFG1 }, |
146 | { 0x7b, REG_HDMI_8960_PHY_PLL_SDM_CFG2 }, |
147 | { 0x09, REG_HDMI_8960_PHY_PLL_SDM_CFG3 }, |
148 | { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4 }, |
149 | } |
150 | }, |
151 | /* 480p60/480i60 */ |
152 | { 27030000, 18, { |
153 | { 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B }, |
154 | { 0x38, REG_HDMI_8960_PHY_PLL_REFCLK_CFG }, |
155 | { 0x02, REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG }, |
156 | { 0x20, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 }, |
157 | { 0xff, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 }, |
158 | { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG0 }, |
159 | { 0x4e, REG_HDMI_8960_PHY_PLL_SDM_CFG1 }, |
160 | { 0xd7, REG_HDMI_8960_PHY_PLL_SDM_CFG2 }, |
161 | { 0x03, REG_HDMI_8960_PHY_PLL_SDM_CFG3 }, |
162 | { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4 }, |
163 | { 0x2a, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 }, |
164 | { 0x03, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 }, |
165 | { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 }, |
166 | { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3 }, |
167 | { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 }, |
168 | { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 }, |
169 | { 0x33, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6 }, |
170 | { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7 }, |
171 | } |
172 | }, |
173 | /* 576p50/576i50 */ |
174 | { 27000000, 27, { |
175 | { 0x32, REG_HDMI_8960_PHY_PLL_REFCLK_CFG }, |
176 | { 0x02, REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG }, |
177 | { 0x01, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 }, |
178 | { 0x33, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 }, |
179 | { 0x2c, REG_HDMI_8960_PHY_PLL_IDAC_ADJ_CFG }, |
180 | { 0x06, REG_HDMI_8960_PHY_PLL_I_VI_KVCO_CFG }, |
181 | { 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B }, |
182 | { 0x7b, REG_HDMI_8960_PHY_PLL_SDM_CFG0 }, |
183 | { 0x01, REG_HDMI_8960_PHY_PLL_SDM_CFG1 }, |
184 | { 0x4c, REG_HDMI_8960_PHY_PLL_SDM_CFG2 }, |
185 | { 0xc0, REG_HDMI_8960_PHY_PLL_SDM_CFG3 }, |
186 | { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4 }, |
187 | { 0x9a, REG_HDMI_8960_PHY_PLL_SSC_CFG0 }, |
188 | { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG1 }, |
189 | { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG2 }, |
190 | { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG3 }, |
191 | { 0x10, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0 }, |
192 | { 0x1a, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1 }, |
193 | { 0x0d, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2 }, |
194 | { 0x2a, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 }, |
195 | { 0x03, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 }, |
196 | { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 }, |
197 | { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3 }, |
198 | { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 }, |
199 | { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 }, |
200 | { 0x33, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6 }, |
201 | { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7 }, |
202 | } |
203 | }, |
204 | /* 640x480p60 */ |
205 | { 25200000, 27, { |
206 | { 0x32, REG_HDMI_8960_PHY_PLL_REFCLK_CFG }, |
207 | { 0x02, REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG }, |
208 | { 0x01, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 }, |
209 | { 0x33, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 }, |
210 | { 0x2c, REG_HDMI_8960_PHY_PLL_IDAC_ADJ_CFG }, |
211 | { 0x06, REG_HDMI_8960_PHY_PLL_I_VI_KVCO_CFG }, |
212 | { 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B }, |
213 | { 0x77, REG_HDMI_8960_PHY_PLL_SDM_CFG0 }, |
214 | { 0x4c, REG_HDMI_8960_PHY_PLL_SDM_CFG1 }, |
215 | { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG2 }, |
216 | { 0xc0, REG_HDMI_8960_PHY_PLL_SDM_CFG3 }, |
217 | { 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4 }, |
218 | { 0x9a, REG_HDMI_8960_PHY_PLL_SSC_CFG0 }, |
219 | { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG1 }, |
220 | { 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG2 }, |
221 | { 0x20, REG_HDMI_8960_PHY_PLL_SSC_CFG3 }, |
222 | { 0x10, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0 }, |
223 | { 0x1a, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1 }, |
224 | { 0x0d, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2 }, |
225 | { 0xf4, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0 }, |
226 | { 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1 }, |
227 | { 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2 }, |
228 | { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3 }, |
229 | { 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4 }, |
230 | { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5 }, |
231 | { 0x33, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6 }, |
232 | { 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7 }, |
233 | } |
234 | }, |
235 | }; |
236 | |
237 | static inline void pll_write(struct hdmi_pll_8960 *pll, u32 reg, u32 data) |
238 | { |
239 | msm_writel(data, pll->mmio + reg); |
240 | } |
241 | |
242 | static inline u32 pll_read(struct hdmi_pll_8960 *pll, u32 reg) |
243 | { |
244 | return msm_readl(pll->mmio + reg); |
245 | } |
246 | |
247 | static inline struct hdmi_phy *pll_get_phy(struct hdmi_pll_8960 *pll) |
248 | { |
249 | return platform_get_drvdata(pdev: pll->pdev); |
250 | } |
251 | |
252 | static int hdmi_pll_enable(struct clk_hw *hw) |
253 | { |
254 | struct hdmi_pll_8960 *pll = hw_clk_to_pll(hw); |
255 | struct hdmi_phy *phy = pll_get_phy(pll); |
256 | int timeout_count, pll_lock_retry = 10; |
257 | unsigned int val; |
258 | |
259 | DBG("" ); |
260 | |
261 | /* Assert PLL S/W reset */ |
262 | pll_write(pll, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, data: 0x8d); |
263 | pll_write(pll, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0, data: 0x10); |
264 | pll_write(pll, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1, data: 0x1a); |
265 | |
266 | /* Wait for a short time before de-asserting |
267 | * to allow the hardware to complete its job. |
268 | * This much of delay should be fine for hardware |
269 | * to assert and de-assert. |
270 | */ |
271 | udelay(10); |
272 | |
273 | /* De-assert PLL S/W reset */ |
274 | pll_write(pll, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, data: 0x0d); |
275 | |
276 | val = hdmi_phy_read(phy, REG_HDMI_8960_PHY_REG12); |
277 | val |= HDMI_8960_PHY_REG12_SW_RESET; |
278 | /* Assert PHY S/W reset */ |
279 | hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG12, data: val); |
280 | val &= ~HDMI_8960_PHY_REG12_SW_RESET; |
281 | /* |
282 | * Wait for a short time before de-asserting to allow the hardware to |
283 | * complete its job. This much of delay should be fine for hardware to |
284 | * assert and de-assert. |
285 | */ |
286 | udelay(10); |
287 | /* De-assert PHY S/W reset */ |
288 | hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG12, data: val); |
289 | hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG2, data: 0x3f); |
290 | |
291 | val = hdmi_phy_read(phy, REG_HDMI_8960_PHY_REG12); |
292 | val |= HDMI_8960_PHY_REG12_PWRDN_B; |
293 | hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG12, data: val); |
294 | /* Wait 10 us for enabling global power for PHY */ |
295 | mb(); |
296 | udelay(10); |
297 | |
298 | val = pll_read(pll, REG_HDMI_8960_PHY_PLL_PWRDN_B); |
299 | val |= HDMI_8960_PHY_PLL_PWRDN_B_PLL_PWRDN_B; |
300 | val &= ~HDMI_8960_PHY_PLL_PWRDN_B_PD_PLL; |
301 | pll_write(pll, REG_HDMI_8960_PHY_PLL_PWRDN_B, data: val); |
302 | hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG2, data: 0x80); |
303 | |
304 | timeout_count = 1000; |
305 | while (--pll_lock_retry > 0) { |
306 | /* are we there yet? */ |
307 | val = pll_read(pll, REG_HDMI_8960_PHY_PLL_STATUS0); |
308 | if (val & HDMI_8960_PHY_PLL_STATUS0_PLL_LOCK) |
309 | break; |
310 | |
311 | udelay(1); |
312 | |
313 | if (--timeout_count > 0) |
314 | continue; |
315 | |
316 | /* |
317 | * PLL has still not locked. |
318 | * Do a software reset and try again |
319 | * Assert PLL S/W reset first |
320 | */ |
321 | pll_write(pll, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, data: 0x8d); |
322 | udelay(10); |
323 | pll_write(pll, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, data: 0x0d); |
324 | |
325 | /* |
326 | * Wait for a short duration for the PLL calibration |
327 | * before checking if the PLL gets locked |
328 | */ |
329 | udelay(350); |
330 | |
331 | timeout_count = 1000; |
332 | } |
333 | |
334 | return 0; |
335 | } |
336 | |
337 | static void hdmi_pll_disable(struct clk_hw *hw) |
338 | { |
339 | struct hdmi_pll_8960 *pll = hw_clk_to_pll(hw); |
340 | struct hdmi_phy *phy = pll_get_phy(pll); |
341 | unsigned int val; |
342 | |
343 | DBG("" ); |
344 | |
345 | val = hdmi_phy_read(phy, REG_HDMI_8960_PHY_REG12); |
346 | val &= ~HDMI_8960_PHY_REG12_PWRDN_B; |
347 | hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG12, data: val); |
348 | |
349 | val = pll_read(pll, REG_HDMI_8960_PHY_PLL_PWRDN_B); |
350 | val |= HDMI_8960_PHY_REG12_SW_RESET; |
351 | val &= ~HDMI_8960_PHY_REG12_PWRDN_B; |
352 | pll_write(pll, REG_HDMI_8960_PHY_PLL_PWRDN_B, data: val); |
353 | /* Make sure HDMI PHY/PLL are powered down */ |
354 | mb(); |
355 | } |
356 | |
357 | static const struct pll_rate *find_rate(unsigned long rate) |
358 | { |
359 | int i; |
360 | |
361 | for (i = 1; i < ARRAY_SIZE(freqtbl); i++) |
362 | if (rate > freqtbl[i].rate) |
363 | return &freqtbl[i - 1]; |
364 | |
365 | return &freqtbl[i - 1]; |
366 | } |
367 | |
368 | static unsigned long hdmi_pll_recalc_rate(struct clk_hw *hw, |
369 | unsigned long parent_rate) |
370 | { |
371 | struct hdmi_pll_8960 *pll = hw_clk_to_pll(hw); |
372 | |
373 | return pll->pixclk; |
374 | } |
375 | |
376 | static long hdmi_pll_round_rate(struct clk_hw *hw, unsigned long rate, |
377 | unsigned long *parent_rate) |
378 | { |
379 | const struct pll_rate *pll_rate = find_rate(rate); |
380 | |
381 | return pll_rate->rate; |
382 | } |
383 | |
384 | static int hdmi_pll_set_rate(struct clk_hw *hw, unsigned long rate, |
385 | unsigned long parent_rate) |
386 | { |
387 | struct hdmi_pll_8960 *pll = hw_clk_to_pll(hw); |
388 | const struct pll_rate *pll_rate = find_rate(rate); |
389 | int i; |
390 | |
391 | DBG("rate=%lu" , rate); |
392 | |
393 | for (i = 0; i < pll_rate->num_reg; i++) |
394 | pll_write(pll, reg: pll_rate->conf[i].reg, data: pll_rate->conf[i].val); |
395 | |
396 | pll->pixclk = rate; |
397 | |
398 | return 0; |
399 | } |
400 | |
401 | static const struct clk_ops hdmi_pll_ops = { |
402 | .enable = hdmi_pll_enable, |
403 | .disable = hdmi_pll_disable, |
404 | .recalc_rate = hdmi_pll_recalc_rate, |
405 | .round_rate = hdmi_pll_round_rate, |
406 | .set_rate = hdmi_pll_set_rate, |
407 | }; |
408 | |
409 | static const struct clk_parent_data hdmi_pll_parents[] = { |
410 | { .fw_name = "pxo" , .name = "pxo_board" }, |
411 | }; |
412 | |
413 | static struct clk_init_data pll_init = { |
414 | .name = "hdmi_pll" , |
415 | .ops = &hdmi_pll_ops, |
416 | .parent_data = hdmi_pll_parents, |
417 | .num_parents = ARRAY_SIZE(hdmi_pll_parents), |
418 | .flags = CLK_IGNORE_UNUSED, |
419 | }; |
420 | |
421 | int msm_hdmi_pll_8960_init(struct platform_device *pdev) |
422 | { |
423 | struct device *dev = &pdev->dev; |
424 | struct hdmi_pll_8960 *pll; |
425 | int i, ret; |
426 | |
427 | /* sanity check: */ |
428 | for (i = 0; i < (ARRAY_SIZE(freqtbl) - 1); i++) |
429 | if (WARN_ON(freqtbl[i].rate < freqtbl[i + 1].rate)) |
430 | return -EINVAL; |
431 | |
432 | pll = devm_kzalloc(dev, size: sizeof(*pll), GFP_KERNEL); |
433 | if (!pll) |
434 | return -ENOMEM; |
435 | |
436 | pll->mmio = msm_ioremap(pdev, "hdmi_pll" ); |
437 | if (IS_ERR(ptr: pll->mmio)) { |
438 | DRM_DEV_ERROR(dev, "failed to map pll base\n" ); |
439 | return -ENOMEM; |
440 | } |
441 | |
442 | pll->pdev = pdev; |
443 | pll->clk_hw.init = &pll_init; |
444 | |
445 | ret = devm_clk_hw_register(dev, hw: &pll->clk_hw); |
446 | if (ret < 0) { |
447 | DRM_DEV_ERROR(dev, "failed to register pll clock\n" ); |
448 | return ret; |
449 | } |
450 | |
451 | ret = devm_of_clk_add_hw_provider(dev, get: of_clk_hw_simple_get, data: &pll->clk_hw); |
452 | if (ret) { |
453 | DRM_DEV_ERROR(dev, "%s: failed to register clk provider: %d\n" , __func__, ret); |
454 | return ret; |
455 | } |
456 | |
457 | return 0; |
458 | } |
459 | |