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