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