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	{ 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
252static 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
337static 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
357static 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
366static 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
373static 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
380static 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
399static 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
407static const char *hdmi_pll_parents[] = {
408	"pxo",
409};
410
411static 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
423static 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
429static void hdmi_phy_8960_reset(struct hdmi_phy *phy)
430{
431	struct hdmi_phy_8960 *phy_8960 = to_hdmi_phy_8960(phy);
432	struct hdmi *hdmi = phy_8960->hdmi;
433	unsigned int val;
434
435	val = hdmi_read(hdmi, REG_HDMI_PHY_CTRL);
436
437	if (val & HDMI_PHY_CTRL_SW_RESET_LOW) {
438		/* pull low */
439		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
440				val & ~HDMI_PHY_CTRL_SW_RESET);
441	} else {
442		/* pull high */
443		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
444				val | HDMI_PHY_CTRL_SW_RESET);
445	}
446
447	if (val & HDMI_PHY_CTRL_SW_RESET_PLL_LOW) {
448		/* pull low */
449		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
450				val & ~HDMI_PHY_CTRL_SW_RESET_PLL);
451	} else {
452		/* pull high */
453		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
454				val | HDMI_PHY_CTRL_SW_RESET_PLL);
455	}
456
457	msleep(100);
458
459	if (val & HDMI_PHY_CTRL_SW_RESET_LOW) {
460		/* pull high */
461		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
462				val | HDMI_PHY_CTRL_SW_RESET);
463	} else {
464		/* pull low */
465		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
466				val & ~HDMI_PHY_CTRL_SW_RESET);
467	}
468
469	if (val & HDMI_PHY_CTRL_SW_RESET_PLL_LOW) {
470		/* pull high */
471		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
472				val | HDMI_PHY_CTRL_SW_RESET_PLL);
473	} else {
474		/* pull low */
475		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
476				val & ~HDMI_PHY_CTRL_SW_RESET_PLL);
477	}
478}
479
480static void hdmi_phy_8960_powerup(struct hdmi_phy *phy,
481		unsigned long int pixclock)
482{
483	struct hdmi_phy_8960 *phy_8960 = to_hdmi_phy_8960(phy);
484	struct hdmi *hdmi = phy_8960->hdmi;
485
486	DBG("pixclock: %lu", pixclock);
487
488	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG2, 0x00);
489	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG0, 0x1b);
490	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG1, 0xf2);
491	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG4, 0x00);
492	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG5, 0x00);
493	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG6, 0x00);
494	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG7, 0x00);
495	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG8, 0x00);
496	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG9, 0x00);
497	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG10, 0x00);
498	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG11, 0x00);
499	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG3, 0x20);
500}
501
502static void hdmi_phy_8960_powerdown(struct hdmi_phy *phy)
503{
504	struct hdmi_phy_8960 *phy_8960 = to_hdmi_phy_8960(phy);
505	struct hdmi *hdmi = phy_8960->hdmi;
506
507	DBG("");
508
509	hdmi_write(hdmi, REG_HDMI_8960_PHY_REG2, 0x7f);
510}
511
512static const struct hdmi_phy_funcs hdmi_phy_8960_funcs = {
513		.destroy = hdmi_phy_8960_destroy,
514		.reset = hdmi_phy_8960_reset,
515		.powerup = hdmi_phy_8960_powerup,
516		.powerdown = hdmi_phy_8960_powerdown,
517};
518
519struct hdmi_phy *hdmi_phy_8960_init(struct hdmi *hdmi)
520{
521	struct hdmi_phy_8960 *phy_8960;
522	struct hdmi_phy *phy = NULL;
523	int ret;
524#ifdef CONFIG_COMMON_CLK
525	int i;
526
527	/* sanity check: */
528	for (i = 0; i < (ARRAY_SIZE(freqtbl) - 1); i++)
529		if (WARN_ON(freqtbl[i].rate < freqtbl[i+1].rate))
530			return ERR_PTR(-EINVAL);
531#endif
532
533	phy_8960 = kzalloc(sizeof(*phy_8960), GFP_KERNEL);
534	if (!phy_8960) {
535		ret = -ENOMEM;
536		goto fail;
537	}
538
539	phy = &phy_8960->base;
540
541	phy->funcs = &hdmi_phy_8960_funcs;
542
543	phy_8960->hdmi = hdmi;
544
545#ifdef CONFIG_COMMON_CLK
546	phy_8960->pll_hw.init = &pll_init;
547	phy_8960->pll = devm_clk_register(&hdmi->pdev->dev, &phy_8960->pll_hw);
548	if (IS_ERR(phy_8960->pll)) {
549		ret = PTR_ERR(phy_8960->pll);
550		phy_8960->pll = NULL;
551		goto fail;
552	}
553#endif
554
555	return phy;
556
557fail:
558	if (phy)
559		hdmi_phy_8960_destroy(phy);
560	return ERR_PTR(ret);
561}
562