xref: /linux/drivers/gpu/drm/msm/hdmi/hdmi_pll_8960.c (revision f14aa5ea415b8add245e976bfab96a12986c6843)
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 	writel(data, pll->mmio + reg);
240 }
241 
242 static inline u32 pll_read(struct hdmi_pll_8960 *pll, u32 reg)
243 {
244 	return 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(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, 0x8d);
263 	pll_write(pll, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0, 0x10);
264 	pll_write(pll, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1, 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, 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, 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, val);
289 	hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG2,  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, 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, val);
302 	hdmi_phy_write(phy, REG_HDMI_8960_PHY_REG2, 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, 0x8d);
322 		udelay(10);
323 		pll_write(pll, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, 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, 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, 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, pll_rate->conf[i].reg, 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, sizeof(*pll), GFP_KERNEL);
433 	if (!pll)
434 		return -ENOMEM;
435 
436 	pll->mmio = msm_ioremap(pdev, "hdmi_pll");
437 	if (IS_ERR(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, &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, of_clk_hw_simple_get, &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