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