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