xref: /linux/drivers/gpu/drm/msm/hdmi/hdmi_phy_8960.c (revision b85d45947951d23cb22d90caecf4c1eb81342c96)
1 /*
2  * Copyright (C) 2013 Red Hat
3  * Author: Rob Clark <robdclark@gmail.com>
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 as published by
7  * the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
12  * more details.
13  *
14  * You should have received a copy of the GNU General Public License along with
15  * this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #ifdef CONFIG_COMMON_CLK
19 #include <linux/clk.h>
20 #include <linux/clk-provider.h>
21 #endif
22 
23 #include "hdmi.h"
24 
25 struct hdmi_phy_8960 {
26 	struct hdmi_phy base;
27 	struct hdmi *hdmi;
28 #ifdef CONFIG_COMMON_CLK
29 	struct clk_hw pll_hw;
30 	struct clk *pll;
31 	unsigned long pixclk;
32 #endif
33 };
34 #define to_hdmi_phy_8960(x) container_of(x, struct hdmi_phy_8960, base)
35 
36 #ifdef CONFIG_COMMON_CLK
37 #define clk_to_phy(x) container_of(x, struct hdmi_phy_8960, pll_hw)
38 
39 /*
40  * HDMI PLL:
41  *
42  * To get the parent clock setup properly, we need to plug in hdmi pll
43  * configuration into common-clock-framework.
44  */
45 
46 struct pll_rate {
47 	unsigned long rate;
48 	struct {
49 		uint32_t val;
50 		uint32_t reg;
51 	} conf[32];
52 };
53 
54 /* NOTE: keep sorted highest freq to lowest: */
55 static const struct pll_rate freqtbl[] = {
56 	{ 154000000, {
57 		{ 0x08, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
58 		{ 0x20, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
59 		{ 0xf9, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
60 		{ 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
61 		{ 0x03, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
62 		{ 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
63 		{ 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3   },
64 		{ 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4   },
65 		{ 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5   },
66 		{ 0x0d, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
67 		{ 0x4d, REG_HDMI_8960_PHY_PLL_SDM_CFG1      },
68 		{ 0x5e, REG_HDMI_8960_PHY_PLL_SDM_CFG2      },
69 		{ 0x42, REG_HDMI_8960_PHY_PLL_SDM_CFG3      },
70 		{ 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4      },
71 		{ 0, 0 } }
72 	},
73 	/* 1080p60/1080p50 case */
74 	{ 148500000, {
75 		{ 0x02, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
76 		{ 0x02, REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG },
77 		{ 0x01, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
78 		{ 0x33, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
79 		{ 0x2c, REG_HDMI_8960_PHY_PLL_IDAC_ADJ_CFG  },
80 		{ 0x06, REG_HDMI_8960_PHY_PLL_I_VI_KVCO_CFG },
81 		{ 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B       },
82 		{ 0x76, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
83 		{ 0x01, REG_HDMI_8960_PHY_PLL_SDM_CFG1      },
84 		{ 0x4c, REG_HDMI_8960_PHY_PLL_SDM_CFG2      },
85 		{ 0xc0, REG_HDMI_8960_PHY_PLL_SDM_CFG3      },
86 		{ 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4      },
87 		{ 0x9a, REG_HDMI_8960_PHY_PLL_SSC_CFG0      },
88 		{ 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG1      },
89 		{ 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG2      },
90 		{ 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG3      },
91 		{ 0x10, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0  },
92 		{ 0x1a, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1  },
93 		{ 0x0d, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2  },
94 		{ 0xe6, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
95 		{ 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
96 		{ 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
97 		{ 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3   },
98 		{ 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4   },
99 		{ 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5   },
100 		{ 0x33, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6   },
101 		{ 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7   },
102 		{ 0, 0 } }
103 	},
104 	{ 108000000, {
105 		{ 0x08, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
106 		{ 0x21, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
107 		{ 0xf9, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
108 		{ 0x1c, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
109 		{ 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
110 		{ 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
111 		{ 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4   },
112 		{ 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5   },
113 		{ 0x49, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
114 		{ 0x49, REG_HDMI_8960_PHY_PLL_SDM_CFG1      },
115 		{ 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG2      },
116 		{ 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG3      },
117 		{ 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4      },
118 		{ 0, 0 } }
119 	},
120 	/* 720p60/720p50/1080i60/1080i50/1080p24/1080p30/1080p25 */
121 	{ 74250000, {
122 		{ 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B       },
123 		{ 0x12, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
124 		{ 0x01, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
125 		{ 0x33, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
126 		{ 0x76, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
127 		{ 0xe6, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
128 		{ 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
129 		{ 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
130 		{ 0, 0 } }
131 	},
132 	{ 74176000, {
133 		{ 0x18, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
134 		{ 0x20, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
135 		{ 0xf9, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
136 		{ 0xe5, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
137 		{ 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
138 		{ 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
139 		{ 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3   },
140 		{ 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4   },
141 		{ 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5   },
142 		{ 0x0c, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
143 		{ 0x4c, REG_HDMI_8960_PHY_PLL_SDM_CFG1      },
144 		{ 0x7d, REG_HDMI_8960_PHY_PLL_SDM_CFG2      },
145 		{ 0xbc, REG_HDMI_8960_PHY_PLL_SDM_CFG3      },
146 		{ 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4      },
147 		{ 0, 0 } }
148 	},
149 	{ 65000000, {
150 		{ 0x18, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
151 		{ 0x20, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
152 		{ 0xf9, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
153 		{ 0x8a, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
154 		{ 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
155 		{ 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
156 		{ 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3   },
157 		{ 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4   },
158 		{ 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5   },
159 		{ 0x0b, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
160 		{ 0x4b, REG_HDMI_8960_PHY_PLL_SDM_CFG1      },
161 		{ 0x7b, REG_HDMI_8960_PHY_PLL_SDM_CFG2      },
162 		{ 0x09, REG_HDMI_8960_PHY_PLL_SDM_CFG3      },
163 		{ 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4      },
164 		{ 0, 0 } }
165 	},
166 	/* 480p60/480i60 */
167 	{ 27030000, {
168 		{ 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B       },
169 		{ 0x38, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
170 		{ 0x02, REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG },
171 		{ 0x20, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
172 		{ 0xff, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
173 		{ 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
174 		{ 0x4e, REG_HDMI_8960_PHY_PLL_SDM_CFG1      },
175 		{ 0xd7, REG_HDMI_8960_PHY_PLL_SDM_CFG2      },
176 		{ 0x03, REG_HDMI_8960_PHY_PLL_SDM_CFG3      },
177 		{ 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4      },
178 		{ 0x2a, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
179 		{ 0x03, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
180 		{ 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
181 		{ 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3   },
182 		{ 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4   },
183 		{ 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5   },
184 		{ 0x33, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6   },
185 		{ 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7   },
186 		{ 0, 0 } }
187 	},
188 	/* 576p50/576i50 */
189 	{ 27000000, {
190 		{ 0x32, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
191 		{ 0x02, REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG },
192 		{ 0x01, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
193 		{ 0x33, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
194 		{ 0x2c, REG_HDMI_8960_PHY_PLL_IDAC_ADJ_CFG  },
195 		{ 0x06, REG_HDMI_8960_PHY_PLL_I_VI_KVCO_CFG },
196 		{ 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B       },
197 		{ 0x7b, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
198 		{ 0x01, REG_HDMI_8960_PHY_PLL_SDM_CFG1      },
199 		{ 0x4c, REG_HDMI_8960_PHY_PLL_SDM_CFG2      },
200 		{ 0xc0, REG_HDMI_8960_PHY_PLL_SDM_CFG3      },
201 		{ 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4      },
202 		{ 0x9a, REG_HDMI_8960_PHY_PLL_SSC_CFG0      },
203 		{ 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG1      },
204 		{ 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG2      },
205 		{ 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG3      },
206 		{ 0x10, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0  },
207 		{ 0x1a, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1  },
208 		{ 0x0d, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2  },
209 		{ 0x2a, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
210 		{ 0x03, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
211 		{ 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
212 		{ 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3   },
213 		{ 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4   },
214 		{ 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5   },
215 		{ 0x33, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6   },
216 		{ 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7   },
217 		{ 0, 0 } }
218 	},
219 	/* 640x480p60 */
220 	{ 25200000, {
221 		{ 0x32, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
222 		{ 0x02, REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG },
223 		{ 0x01, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
224 		{ 0x33, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
225 		{ 0x2c, REG_HDMI_8960_PHY_PLL_IDAC_ADJ_CFG  },
226 		{ 0x06, REG_HDMI_8960_PHY_PLL_I_VI_KVCO_CFG },
227 		{ 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B       },
228 		{ 0x77, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
229 		{ 0x4c, REG_HDMI_8960_PHY_PLL_SDM_CFG1      },
230 		{ 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG2      },
231 		{ 0xc0, REG_HDMI_8960_PHY_PLL_SDM_CFG3      },
232 		{ 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4      },
233 		{ 0x9a, REG_HDMI_8960_PHY_PLL_SSC_CFG0      },
234 		{ 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG1      },
235 		{ 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG2      },
236 		{ 0x20, REG_HDMI_8960_PHY_PLL_SSC_CFG3      },
237 		{ 0x10, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0  },
238 		{ 0x1a, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1  },
239 		{ 0x0d, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2  },
240 		{ 0xf4, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
241 		{ 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
242 		{ 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
243 		{ 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3   },
244 		{ 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4   },
245 		{ 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5   },
246 		{ 0x33, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6   },
247 		{ 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7   },
248 		{ 0, 0 } }
249 	},
250 };
251 
252 static int hdmi_pll_enable(struct clk_hw *hw)
253 {
254 	struct hdmi_phy_8960 *phy_8960 = clk_to_phy(hw);
255 	struct hdmi *hdmi = phy_8960->hdmi;
256 	int timeout_count, pll_lock_retry = 10;
257 	unsigned int val;
258 
259 	DBG("");
260 
261 	/* Assert PLL S/W reset */
262 	hdmi_write(hdmi, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, 0x8d);
263 	hdmi_write(hdmi, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0, 0x10);
264 	hdmi_write(hdmi, 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 	hdmi_write(hdmi, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, 0x0d);
275 
276 	val = hdmi_read(hdmi, REG_HDMI_8960_PHY_REG12);
277 	val |= HDMI_8960_PHY_REG12_SW_RESET;
278 	/* Assert PHY S/W reset */
279 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG12, val);
280 	val &= ~HDMI_8960_PHY_REG12_SW_RESET;
281 	/* Wait for a short time before de-asserting
282 	   to allow the hardware to complete its job.
283 	   This much of delay should be fine for hardware
284 	   to assert and de-assert. */
285 	udelay(10);
286 	/* De-assert PHY S/W reset */
287 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG12, val);
288 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG2,  0x3f);
289 
290 	val = hdmi_read(hdmi, REG_HDMI_8960_PHY_REG12);
291 	val |= HDMI_8960_PHY_REG12_PWRDN_B;
292 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG12, val);
293 	/* Wait 10 us for enabling global power for PHY */
294 	mb();
295 	udelay(10);
296 
297 	val = hdmi_read(hdmi, REG_HDMI_8960_PHY_PLL_PWRDN_B);
298 	val |= HDMI_8960_PHY_PLL_PWRDN_B_PLL_PWRDN_B;
299 	val &= ~HDMI_8960_PHY_PLL_PWRDN_B_PD_PLL;
300 	hdmi_write(hdmi, REG_HDMI_8960_PHY_PLL_PWRDN_B, val);
301 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG2, 0x80);
302 
303 	timeout_count = 1000;
304 	while (--pll_lock_retry > 0) {
305 
306 		/* are we there yet? */
307 		val = hdmi_read(hdmi, 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 		hdmi_write(hdmi, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, 0x8d);
322 		udelay(10);
323 		hdmi_write(hdmi, 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_phy_8960 *phy_8960 = clk_to_phy(hw);
340 	struct hdmi *hdmi = phy_8960->hdmi;
341 	unsigned int val;
342 
343 	DBG("");
344 
345 	val = hdmi_read(hdmi, REG_HDMI_8960_PHY_REG12);
346 	val &= ~HDMI_8960_PHY_REG12_PWRDN_B;
347 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG12, val);
348 
349 	val = hdmi_read(hdmi, REG_HDMI_8960_PHY_PLL_PWRDN_B);
350 	val |= HDMI_8960_PHY_REG12_SW_RESET;
351 	val &= ~HDMI_8960_PHY_REG12_PWRDN_B;
352 	hdmi_write(hdmi, 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 	for (i = 1; i < ARRAY_SIZE(freqtbl); i++)
361 		if (rate > freqtbl[i].rate)
362 			return &freqtbl[i-1];
363 	return &freqtbl[i-1];
364 }
365 
366 static unsigned long hdmi_pll_recalc_rate(struct clk_hw *hw,
367 				unsigned long parent_rate)
368 {
369 	struct hdmi_phy_8960 *phy_8960 = clk_to_phy(hw);
370 	return phy_8960->pixclk;
371 }
372 
373 static long hdmi_pll_round_rate(struct clk_hw *hw, unsigned long rate,
374 		unsigned long *parent_rate)
375 {
376 	const struct pll_rate *pll_rate = find_rate(rate);
377 	return pll_rate->rate;
378 }
379 
380 static int hdmi_pll_set_rate(struct clk_hw *hw, unsigned long rate,
381 		unsigned long parent_rate)
382 {
383 	struct hdmi_phy_8960 *phy_8960 = clk_to_phy(hw);
384 	struct hdmi *hdmi = phy_8960->hdmi;
385 	const struct pll_rate *pll_rate = find_rate(rate);
386 	int i;
387 
388 	DBG("rate=%lu", rate);
389 
390 	for (i = 0; pll_rate->conf[i].reg; i++)
391 		hdmi_write(hdmi, pll_rate->conf[i].reg, pll_rate->conf[i].val);
392 
393 	phy_8960->pixclk = rate;
394 
395 	return 0;
396 }
397 
398 
399 static const struct clk_ops hdmi_pll_ops = {
400 	.enable = hdmi_pll_enable,
401 	.disable = hdmi_pll_disable,
402 	.recalc_rate = hdmi_pll_recalc_rate,
403 	.round_rate = hdmi_pll_round_rate,
404 	.set_rate = hdmi_pll_set_rate,
405 };
406 
407 static const char *hdmi_pll_parents[] = {
408 	"pxo",
409 };
410 
411 static struct clk_init_data pll_init = {
412 	.name = "hdmi_pll",
413 	.ops = &hdmi_pll_ops,
414 	.parent_names = hdmi_pll_parents,
415 	.num_parents = ARRAY_SIZE(hdmi_pll_parents),
416 };
417 #endif
418 
419 /*
420  * HDMI Phy:
421  */
422 
423 static void hdmi_phy_8960_destroy(struct hdmi_phy *phy)
424 {
425 	struct hdmi_phy_8960 *phy_8960 = to_hdmi_phy_8960(phy);
426 	kfree(phy_8960);
427 }
428 
429 static void hdmi_phy_8960_powerup(struct hdmi_phy *phy,
430 		unsigned long int pixclock)
431 {
432 	struct hdmi_phy_8960 *phy_8960 = to_hdmi_phy_8960(phy);
433 	struct hdmi *hdmi = phy_8960->hdmi;
434 
435 	DBG("pixclock: %lu", pixclock);
436 
437 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG2, 0x00);
438 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG0, 0x1b);
439 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG1, 0xf2);
440 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG4, 0x00);
441 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG5, 0x00);
442 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG6, 0x00);
443 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG7, 0x00);
444 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG8, 0x00);
445 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG9, 0x00);
446 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG10, 0x00);
447 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG11, 0x00);
448 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG3, 0x20);
449 }
450 
451 static void hdmi_phy_8960_powerdown(struct hdmi_phy *phy)
452 {
453 	struct hdmi_phy_8960 *phy_8960 = to_hdmi_phy_8960(phy);
454 	struct hdmi *hdmi = phy_8960->hdmi;
455 
456 	DBG("");
457 
458 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG2, 0x7f);
459 }
460 
461 static const struct hdmi_phy_funcs hdmi_phy_8960_funcs = {
462 		.destroy = hdmi_phy_8960_destroy,
463 		.powerup = hdmi_phy_8960_powerup,
464 		.powerdown = hdmi_phy_8960_powerdown,
465 };
466 
467 struct hdmi_phy *hdmi_phy_8960_init(struct hdmi *hdmi)
468 {
469 	struct hdmi_phy_8960 *phy_8960;
470 	struct hdmi_phy *phy = NULL;
471 	int ret;
472 #ifdef CONFIG_COMMON_CLK
473 	int i;
474 
475 	/* sanity check: */
476 	for (i = 0; i < (ARRAY_SIZE(freqtbl) - 1); i++)
477 		if (WARN_ON(freqtbl[i].rate < freqtbl[i+1].rate))
478 			return ERR_PTR(-EINVAL);
479 #endif
480 
481 	phy_8960 = kzalloc(sizeof(*phy_8960), GFP_KERNEL);
482 	if (!phy_8960) {
483 		ret = -ENOMEM;
484 		goto fail;
485 	}
486 
487 	phy = &phy_8960->base;
488 
489 	phy->funcs = &hdmi_phy_8960_funcs;
490 
491 	phy_8960->hdmi = hdmi;
492 
493 #ifdef CONFIG_COMMON_CLK
494 	phy_8960->pll_hw.init = &pll_init;
495 	phy_8960->pll = devm_clk_register(&hdmi->pdev->dev, &phy_8960->pll_hw);
496 	if (IS_ERR(phy_8960->pll)) {
497 		ret = PTR_ERR(phy_8960->pll);
498 		phy_8960->pll = NULL;
499 		goto fail;
500 	}
501 #endif
502 
503 	return phy;
504 
505 fail:
506 	if (phy)
507 		hdmi_phy_8960_destroy(phy);
508 	return ERR_PTR(ret);
509 }
510