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 #include <linux/clk.h>
19 #include <linux/clk-provider.h>
20 
21 #include "hdmi.h"
22 
23 struct hdmi_phy_8960 {
24 	struct hdmi_phy base;
25 	struct hdmi *hdmi;
26 	struct clk_hw pll_hw;
27 	struct clk *pll;
28 	unsigned long pixclk;
29 };
30 #define to_hdmi_phy_8960(x) container_of(x, struct hdmi_phy_8960, base)
31 #define clk_to_phy(x) container_of(x, struct hdmi_phy_8960, pll_hw)
32 
33 /*
34  * HDMI PLL:
35  *
36  * To get the parent clock setup properly, we need to plug in hdmi pll
37  * configuration into common-clock-framework.
38  */
39 
40 struct pll_rate {
41 	unsigned long rate;
42 	struct {
43 		uint32_t val;
44 		uint32_t reg;
45 	} conf[32];
46 };
47 
48 /* NOTE: keep sorted highest freq to lowest: */
49 static const struct pll_rate freqtbl[] = {
50 	/* 1080p60/1080p50 case */
51 	{ 148500000, {
52 		{ 0x02, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
53 		{ 0x02, REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG },
54 		{ 0x01, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
55 		{ 0x33, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
56 		{ 0x2c, REG_HDMI_8960_PHY_PLL_IDAC_ADJ_CFG  },
57 		{ 0x06, REG_HDMI_8960_PHY_PLL_I_VI_KVCO_CFG },
58 		{ 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B       },
59 		{ 0x76, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
60 		{ 0x01, REG_HDMI_8960_PHY_PLL_SDM_CFG1      },
61 		{ 0x4c, REG_HDMI_8960_PHY_PLL_SDM_CFG2      },
62 		{ 0xc0, REG_HDMI_8960_PHY_PLL_SDM_CFG3      },
63 		{ 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4      },
64 		{ 0x9a, REG_HDMI_8960_PHY_PLL_SSC_CFG0      },
65 		{ 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG1      },
66 		{ 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG2      },
67 		{ 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG3      },
68 		{ 0x10, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0  },
69 		{ 0x1a, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1  },
70 		{ 0x0d, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2  },
71 		{ 0xe6, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
72 		{ 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
73 		{ 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
74 		{ 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3   },
75 		{ 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4   },
76 		{ 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5   },
77 		{ 0x33, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6   },
78 		{ 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7   },
79 		{ 0, 0 } }
80 	},
81 	{ 108000000, {
82 		{ 0x08, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
83 		{ 0x21, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
84 		{ 0xf9, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
85 		{ 0x1c, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
86 		{ 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
87 		{ 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
88 		{ 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4   },
89 		{ 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5   },
90 		{ 0x49, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
91 		{ 0x49, REG_HDMI_8960_PHY_PLL_SDM_CFG1      },
92 		{ 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG2      },
93 		{ 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG3      },
94 		{ 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4      },
95 		{ 0, 0 } }
96 	},
97 	/* 720p60/720p50/1080i60/1080i50/1080p24/1080p30/1080p25 */
98 	{ 74250000, {
99 		{ 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B       },
100 		{ 0x12, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
101 		{ 0x01, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
102 		{ 0x33, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
103 		{ 0x76, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
104 		{ 0xe6, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
105 		{ 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
106 		{ 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
107 		{ 0, 0 } }
108 	},
109 	{ 65000000, {
110 		{ 0x18, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
111 		{ 0x20, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
112 		{ 0xf9, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
113 		{ 0x8a, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
114 		{ 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
115 		{ 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
116 		{ 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3   },
117 		{ 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4   },
118 		{ 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5   },
119 		{ 0x0b, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
120 		{ 0x4b, REG_HDMI_8960_PHY_PLL_SDM_CFG1      },
121 		{ 0x7b, REG_HDMI_8960_PHY_PLL_SDM_CFG2      },
122 		{ 0x09, REG_HDMI_8960_PHY_PLL_SDM_CFG3      },
123 		{ 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4      },
124 		{ 0, 0 } }
125 	},
126 	/* 480p60/480i60 */
127 	{ 27030000, {
128 		{ 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B       },
129 		{ 0x38, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
130 		{ 0x02, REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG },
131 		{ 0x20, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
132 		{ 0xff, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
133 		{ 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
134 		{ 0x4e, REG_HDMI_8960_PHY_PLL_SDM_CFG1      },
135 		{ 0xd7, REG_HDMI_8960_PHY_PLL_SDM_CFG2      },
136 		{ 0x03, REG_HDMI_8960_PHY_PLL_SDM_CFG3      },
137 		{ 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4      },
138 		{ 0x2a, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
139 		{ 0x03, 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 		{ 0x33, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6   },
145 		{ 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7   },
146 		{ 0, 0 } }
147 	},
148 	/* 576p50/576i50 */
149 	{ 27000000, {
150 		{ 0x32, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
151 		{ 0x02, REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG },
152 		{ 0x01, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
153 		{ 0x33, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
154 		{ 0x2c, REG_HDMI_8960_PHY_PLL_IDAC_ADJ_CFG  },
155 		{ 0x06, REG_HDMI_8960_PHY_PLL_I_VI_KVCO_CFG },
156 		{ 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B       },
157 		{ 0x7b, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
158 		{ 0x01, REG_HDMI_8960_PHY_PLL_SDM_CFG1      },
159 		{ 0x4c, REG_HDMI_8960_PHY_PLL_SDM_CFG2      },
160 		{ 0xc0, REG_HDMI_8960_PHY_PLL_SDM_CFG3      },
161 		{ 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4      },
162 		{ 0x9a, REG_HDMI_8960_PHY_PLL_SSC_CFG0      },
163 		{ 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG1      },
164 		{ 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG2      },
165 		{ 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG3      },
166 		{ 0x10, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0  },
167 		{ 0x1a, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1  },
168 		{ 0x0d, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2  },
169 		{ 0x2a, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
170 		{ 0x03, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
171 		{ 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
172 		{ 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3   },
173 		{ 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4   },
174 		{ 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5   },
175 		{ 0x33, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6   },
176 		{ 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7   },
177 		{ 0, 0 } }
178 	},
179 	/* 640x480p60 */
180 	{ 25200000, {
181 		{ 0x32, REG_HDMI_8960_PHY_PLL_REFCLK_CFG    },
182 		{ 0x02, REG_HDMI_8960_PHY_PLL_CHRG_PUMP_CFG },
183 		{ 0x01, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG0 },
184 		{ 0x33, REG_HDMI_8960_PHY_PLL_LOOP_FLT_CFG1 },
185 		{ 0x2c, REG_HDMI_8960_PHY_PLL_IDAC_ADJ_CFG  },
186 		{ 0x06, REG_HDMI_8960_PHY_PLL_I_VI_KVCO_CFG },
187 		{ 0x0a, REG_HDMI_8960_PHY_PLL_PWRDN_B       },
188 		{ 0x77, REG_HDMI_8960_PHY_PLL_SDM_CFG0      },
189 		{ 0x4c, REG_HDMI_8960_PHY_PLL_SDM_CFG1      },
190 		{ 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG2      },
191 		{ 0xc0, REG_HDMI_8960_PHY_PLL_SDM_CFG3      },
192 		{ 0x00, REG_HDMI_8960_PHY_PLL_SDM_CFG4      },
193 		{ 0x9a, REG_HDMI_8960_PHY_PLL_SSC_CFG0      },
194 		{ 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG1      },
195 		{ 0x00, REG_HDMI_8960_PHY_PLL_SSC_CFG2      },
196 		{ 0x20, REG_HDMI_8960_PHY_PLL_SSC_CFG3      },
197 		{ 0x10, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0  },
198 		{ 0x1a, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1  },
199 		{ 0x0d, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2  },
200 		{ 0xf4, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG0   },
201 		{ 0x02, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG1   },
202 		{ 0x3b, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG2   },
203 		{ 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG3   },
204 		{ 0x86, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG4   },
205 		{ 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG5   },
206 		{ 0x33, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG6   },
207 		{ 0x00, REG_HDMI_8960_PHY_PLL_VCOCAL_CFG7   },
208 		{ 0, 0 } }
209 	},
210 };
211 
212 static int hdmi_pll_enable(struct clk_hw *hw)
213 {
214 	struct hdmi_phy_8960 *phy_8960 = clk_to_phy(hw);
215 	struct hdmi *hdmi = phy_8960->hdmi;
216 	int timeout_count, pll_lock_retry = 10;
217 	unsigned int val;
218 
219 	DBG("");
220 
221 	/* Assert PLL S/W reset */
222 	hdmi_write(hdmi, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, 0x8d);
223 	hdmi_write(hdmi, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG0, 0x10);
224 	hdmi_write(hdmi, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG1, 0x1a);
225 
226 	/* Wait for a short time before de-asserting
227 	 * to allow the hardware to complete its job.
228 	 * This much of delay should be fine for hardware
229 	 * to assert and de-assert.
230 	 */
231 	udelay(10);
232 
233 	/* De-assert PLL S/W reset */
234 	hdmi_write(hdmi, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, 0x0d);
235 
236 	val = hdmi_read(hdmi, REG_HDMI_8960_PHY_REG12);
237 	val |= HDMI_8960_PHY_REG12_SW_RESET;
238 	/* Assert PHY S/W reset */
239 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG12, val);
240 	val &= ~HDMI_8960_PHY_REG12_SW_RESET;
241 	/* Wait for a short time before de-asserting
242 	   to allow the hardware to complete its job.
243 	   This much of delay should be fine for hardware
244 	   to assert and de-assert. */
245 	udelay(10);
246 	/* De-assert PHY S/W reset */
247 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG12, val);
248 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG2,  0x3f);
249 
250 	val = hdmi_read(hdmi, REG_HDMI_8960_PHY_REG12);
251 	val |= HDMI_8960_PHY_REG12_PWRDN_B;
252 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG12, val);
253 	/* Wait 10 us for enabling global power for PHY */
254 	mb();
255 	udelay(10);
256 
257 	val = hdmi_read(hdmi, REG_HDMI_8960_PHY_PLL_PWRDN_B);
258 	val |= HDMI_8960_PHY_PLL_PWRDN_B_PLL_PWRDN_B;
259 	val &= ~HDMI_8960_PHY_PLL_PWRDN_B_PD_PLL;
260 	hdmi_write(hdmi, REG_HDMI_8960_PHY_PLL_PWRDN_B, val);
261 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG2, 0x80);
262 
263 	timeout_count = 1000;
264 	while (--pll_lock_retry > 0) {
265 
266 		/* are we there yet? */
267 		val = hdmi_read(hdmi, REG_HDMI_8960_PHY_PLL_STATUS0);
268 		if (val & HDMI_8960_PHY_PLL_STATUS0_PLL_LOCK)
269 			break;
270 
271 		udelay(1);
272 
273 		if (--timeout_count > 0)
274 			continue;
275 
276 		/*
277 		 * PLL has still not locked.
278 		 * Do a software reset and try again
279 		 * Assert PLL S/W reset first
280 		 */
281 		hdmi_write(hdmi, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, 0x8d);
282 		udelay(10);
283 		hdmi_write(hdmi, REG_HDMI_8960_PHY_PLL_LOCKDET_CFG2, 0x0d);
284 
285 		/*
286 		 * Wait for a short duration for the PLL calibration
287 		 * before checking if the PLL gets locked
288 		 */
289 		udelay(350);
290 
291 		timeout_count = 1000;
292 	}
293 
294 	return 0;
295 }
296 
297 static void hdmi_pll_disable(struct clk_hw *hw)
298 {
299 	struct hdmi_phy_8960 *phy_8960 = clk_to_phy(hw);
300 	struct hdmi *hdmi = phy_8960->hdmi;
301 	unsigned int val;
302 
303 	DBG("");
304 
305 	val = hdmi_read(hdmi, REG_HDMI_8960_PHY_REG12);
306 	val &= ~HDMI_8960_PHY_REG12_PWRDN_B;
307 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG12, val);
308 
309 	val = hdmi_read(hdmi, REG_HDMI_8960_PHY_PLL_PWRDN_B);
310 	val |= HDMI_8960_PHY_REG12_SW_RESET;
311 	val &= ~HDMI_8960_PHY_REG12_PWRDN_B;
312 	hdmi_write(hdmi, REG_HDMI_8960_PHY_PLL_PWRDN_B, val);
313 	/* Make sure HDMI PHY/PLL are powered down */
314 	mb();
315 }
316 
317 static const struct pll_rate *find_rate(unsigned long rate)
318 {
319 	int i;
320 	for (i = 1; i < ARRAY_SIZE(freqtbl); i++)
321 		if (rate > freqtbl[i].rate)
322 			return &freqtbl[i-1];
323 	return &freqtbl[i-1];
324 }
325 
326 static unsigned long hdmi_pll_recalc_rate(struct clk_hw *hw,
327 				unsigned long parent_rate)
328 {
329 	struct hdmi_phy_8960 *phy_8960 = clk_to_phy(hw);
330 	return phy_8960->pixclk;
331 }
332 
333 static long hdmi_pll_round_rate(struct clk_hw *hw, unsigned long rate,
334 		unsigned long *parent_rate)
335 {
336 	const struct pll_rate *pll_rate = find_rate(rate);
337 	return pll_rate->rate;
338 }
339 
340 static int hdmi_pll_set_rate(struct clk_hw *hw, unsigned long rate,
341 		unsigned long parent_rate)
342 {
343 	struct hdmi_phy_8960 *phy_8960 = clk_to_phy(hw);
344 	struct hdmi *hdmi = phy_8960->hdmi;
345 	const struct pll_rate *pll_rate = find_rate(rate);
346 	int i;
347 
348 	DBG("rate=%lu", rate);
349 
350 	for (i = 0; pll_rate->conf[i].reg; i++)
351 		hdmi_write(hdmi, pll_rate->conf[i].reg, pll_rate->conf[i].val);
352 
353 	phy_8960->pixclk = rate;
354 
355 	return 0;
356 }
357 
358 
359 static const struct clk_ops hdmi_pll_ops = {
360 	.enable = hdmi_pll_enable,
361 	.disable = hdmi_pll_disable,
362 	.recalc_rate = hdmi_pll_recalc_rate,
363 	.round_rate = hdmi_pll_round_rate,
364 	.set_rate = hdmi_pll_set_rate,
365 };
366 
367 static const char *hdmi_pll_parents[] = {
368 	"pxo",
369 };
370 
371 static struct clk_init_data pll_init = {
372 	.name = "hdmi_pll",
373 	.ops = &hdmi_pll_ops,
374 	.parent_names = hdmi_pll_parents,
375 	.num_parents = ARRAY_SIZE(hdmi_pll_parents),
376 };
377 
378 
379 /*
380  * HDMI Phy:
381  */
382 
383 static void hdmi_phy_8960_destroy(struct hdmi_phy *phy)
384 {
385 	struct hdmi_phy_8960 *phy_8960 = to_hdmi_phy_8960(phy);
386 	kfree(phy_8960);
387 }
388 
389 static void hdmi_phy_8960_reset(struct hdmi_phy *phy)
390 {
391 	struct hdmi_phy_8960 *phy_8960 = to_hdmi_phy_8960(phy);
392 	struct hdmi *hdmi = phy_8960->hdmi;
393 	unsigned int val;
394 
395 	val = hdmi_read(hdmi, REG_HDMI_PHY_CTRL);
396 
397 	if (val & HDMI_PHY_CTRL_SW_RESET_LOW) {
398 		/* pull low */
399 		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
400 				val & ~HDMI_PHY_CTRL_SW_RESET);
401 	} else {
402 		/* pull high */
403 		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
404 				val | HDMI_PHY_CTRL_SW_RESET);
405 	}
406 
407 	if (val & HDMI_PHY_CTRL_SW_RESET_PLL_LOW) {
408 		/* pull low */
409 		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
410 				val & ~HDMI_PHY_CTRL_SW_RESET_PLL);
411 	} else {
412 		/* pull high */
413 		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
414 				val | HDMI_PHY_CTRL_SW_RESET_PLL);
415 	}
416 
417 	msleep(100);
418 
419 	if (val & HDMI_PHY_CTRL_SW_RESET_LOW) {
420 		/* pull high */
421 		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
422 				val | HDMI_PHY_CTRL_SW_RESET);
423 	} else {
424 		/* pull low */
425 		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
426 				val & ~HDMI_PHY_CTRL_SW_RESET);
427 	}
428 
429 	if (val & HDMI_PHY_CTRL_SW_RESET_PLL_LOW) {
430 		/* pull high */
431 		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
432 				val | HDMI_PHY_CTRL_SW_RESET_PLL);
433 	} else {
434 		/* pull low */
435 		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
436 				val & ~HDMI_PHY_CTRL_SW_RESET_PLL);
437 	}
438 }
439 
440 static void hdmi_phy_8960_powerup(struct hdmi_phy *phy,
441 		unsigned long int pixclock)
442 {
443 	struct hdmi_phy_8960 *phy_8960 = to_hdmi_phy_8960(phy);
444 	struct hdmi *hdmi = phy_8960->hdmi;
445 
446 	DBG("pixclock: %lu", pixclock);
447 
448 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG2, 0x00);
449 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG0, 0x1b);
450 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG1, 0xf2);
451 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG4, 0x00);
452 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG5, 0x00);
453 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG6, 0x00);
454 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG7, 0x00);
455 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG8, 0x00);
456 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG9, 0x00);
457 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG10, 0x00);
458 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG11, 0x00);
459 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG3, 0x20);
460 }
461 
462 static void hdmi_phy_8960_powerdown(struct hdmi_phy *phy)
463 {
464 	struct hdmi_phy_8960 *phy_8960 = to_hdmi_phy_8960(phy);
465 	struct hdmi *hdmi = phy_8960->hdmi;
466 
467 	DBG("");
468 
469 	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG2, 0x7f);
470 }
471 
472 static const struct hdmi_phy_funcs hdmi_phy_8960_funcs = {
473 		.destroy = hdmi_phy_8960_destroy,
474 		.reset = hdmi_phy_8960_reset,
475 		.powerup = hdmi_phy_8960_powerup,
476 		.powerdown = hdmi_phy_8960_powerdown,
477 };
478 
479 struct hdmi_phy *hdmi_phy_8960_init(struct hdmi *hdmi)
480 {
481 	struct hdmi_phy_8960 *phy_8960;
482 	struct hdmi_phy *phy = NULL;
483 	int ret, i;
484 
485 	/* sanity check: */
486 	for (i = 0; i < (ARRAY_SIZE(freqtbl) - 1); i++)
487 		if (WARN_ON(freqtbl[i].rate < freqtbl[i+1].rate))
488 			return ERR_PTR(-EINVAL);
489 
490 	phy_8960 = kzalloc(sizeof(*phy_8960), GFP_KERNEL);
491 	if (!phy_8960) {
492 		ret = -ENOMEM;
493 		goto fail;
494 	}
495 
496 	phy = &phy_8960->base;
497 
498 	phy->funcs = &hdmi_phy_8960_funcs;
499 
500 	phy_8960->hdmi = hdmi;
501 
502 	phy_8960->pll_hw.init = &pll_init;
503 	phy_8960->pll = devm_clk_register(hdmi->dev->dev, &phy_8960->pll_hw);
504 	if (IS_ERR(phy_8960->pll)) {
505 		ret = PTR_ERR(phy_8960->pll);
506 		phy_8960->pll = NULL;
507 		goto fail;
508 	}
509 
510 	return phy;
511 
512 fail:
513 	if (phy)
514 		hdmi_phy_8960_destroy(phy);
515 	return ERR_PTR(ret);
516 }
517