Commit | Line | Data |
---|---|---|
8f83f268 JQ |
1 | /* |
2 | * Copyright (c) 2014 MediaTek Inc. | |
3 | * Author: Jie Qiu <jie.qiu@mediatek.com> | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License version 2 as | |
7 | * published by the Free Software Foundation. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
13 | */ | |
14 | ||
15 | #include <linux/clk.h> | |
16 | #include <linux/clk-provider.h> | |
17 | #include <linux/delay.h> | |
18 | #include <linux/io.h> | |
19 | #include <linux/mfd/syscon.h> | |
20 | #include <linux/module.h> | |
21 | #include <linux/phy/phy.h> | |
22 | #include <linux/platform_device.h> | |
23 | #include <linux/types.h> | |
24 | ||
25 | #define HDMI_CON0 0x00 | |
26 | #define RG_HDMITX_PLL_EN BIT(31) | |
27 | #define RG_HDMITX_PLL_FBKDIV (0x7f << 24) | |
28 | #define PLL_FBKDIV_SHIFT 24 | |
29 | #define RG_HDMITX_PLL_FBKSEL (0x3 << 22) | |
30 | #define PLL_FBKSEL_SHIFT 22 | |
31 | #define RG_HDMITX_PLL_PREDIV (0x3 << 20) | |
32 | #define PREDIV_SHIFT 20 | |
33 | #define RG_HDMITX_PLL_POSDIV (0x3 << 18) | |
34 | #define POSDIV_SHIFT 18 | |
35 | #define RG_HDMITX_PLL_RST_DLY (0x3 << 16) | |
36 | #define RG_HDMITX_PLL_IR (0xf << 12) | |
37 | #define PLL_IR_SHIFT 12 | |
38 | #define RG_HDMITX_PLL_IC (0xf << 8) | |
39 | #define PLL_IC_SHIFT 8 | |
40 | #define RG_HDMITX_PLL_BP (0xf << 4) | |
41 | #define PLL_BP_SHIFT 4 | |
42 | #define RG_HDMITX_PLL_BR (0x3 << 2) | |
43 | #define PLL_BR_SHIFT 2 | |
44 | #define RG_HDMITX_PLL_BC (0x3 << 0) | |
45 | #define PLL_BC_SHIFT 0 | |
46 | #define HDMI_CON1 0x04 | |
47 | #define RG_HDMITX_PLL_DIVEN (0x7 << 29) | |
48 | #define PLL_DIVEN_SHIFT 29 | |
49 | #define RG_HDMITX_PLL_AUTOK_EN BIT(28) | |
50 | #define RG_HDMITX_PLL_AUTOK_KF (0x3 << 26) | |
51 | #define RG_HDMITX_PLL_AUTOK_KS (0x3 << 24) | |
52 | #define RG_HDMITX_PLL_AUTOK_LOAD BIT(23) | |
53 | #define RG_HDMITX_PLL_BAND (0x3f << 16) | |
54 | #define RG_HDMITX_PLL_REF_SEL BIT(15) | |
55 | #define RG_HDMITX_PLL_BIAS_EN BIT(14) | |
56 | #define RG_HDMITX_PLL_BIAS_LPF_EN BIT(13) | |
57 | #define RG_HDMITX_PLL_TXDIV_EN BIT(12) | |
58 | #define RG_HDMITX_PLL_TXDIV (0x3 << 10) | |
59 | #define PLL_TXDIV_SHIFT 10 | |
60 | #define RG_HDMITX_PLL_LVROD_EN BIT(9) | |
61 | #define RG_HDMITX_PLL_MONVC_EN BIT(8) | |
62 | #define RG_HDMITX_PLL_MONCK_EN BIT(7) | |
63 | #define RG_HDMITX_PLL_MONREF_EN BIT(6) | |
64 | #define RG_HDMITX_PLL_TST_EN BIT(5) | |
65 | #define RG_HDMITX_PLL_TST_CK_EN BIT(4) | |
66 | #define RG_HDMITX_PLL_TST_SEL (0xf << 0) | |
67 | #define HDMI_CON2 0x08 | |
68 | #define RGS_HDMITX_PLL_AUTOK_BAND (0x7f << 8) | |
69 | #define RGS_HDMITX_PLL_AUTOK_FAIL BIT(1) | |
70 | #define RG_HDMITX_EN_TX_CKLDO BIT(0) | |
71 | #define HDMI_CON3 0x0c | |
72 | #define RG_HDMITX_SER_EN (0xf << 28) | |
73 | #define RG_HDMITX_PRD_EN (0xf << 24) | |
74 | #define RG_HDMITX_PRD_IMP_EN (0xf << 20) | |
75 | #define RG_HDMITX_DRV_EN (0xf << 16) | |
76 | #define RG_HDMITX_DRV_IMP_EN (0xf << 12) | |
77 | #define DRV_IMP_EN_SHIFT 12 | |
78 | #define RG_HDMITX_MHLCK_FORCE BIT(10) | |
79 | #define RG_HDMITX_MHLCK_PPIX_EN BIT(9) | |
80 | #define RG_HDMITX_MHLCK_EN BIT(8) | |
81 | #define RG_HDMITX_SER_DIN_SEL (0xf << 4) | |
82 | #define RG_HDMITX_SER_5T1_BIST_EN BIT(3) | |
83 | #define RG_HDMITX_SER_BIST_TOG BIT(2) | |
84 | #define RG_HDMITX_SER_DIN_TOG BIT(1) | |
85 | #define RG_HDMITX_SER_CLKDIG_INV BIT(0) | |
86 | #define HDMI_CON4 0x10 | |
87 | #define RG_HDMITX_PRD_IBIAS_CLK (0xf << 24) | |
88 | #define RG_HDMITX_PRD_IBIAS_D2 (0xf << 16) | |
89 | #define RG_HDMITX_PRD_IBIAS_D1 (0xf << 8) | |
90 | #define RG_HDMITX_PRD_IBIAS_D0 (0xf << 0) | |
91 | #define PRD_IBIAS_CLK_SHIFT 24 | |
92 | #define PRD_IBIAS_D2_SHIFT 16 | |
93 | #define PRD_IBIAS_D1_SHIFT 8 | |
94 | #define PRD_IBIAS_D0_SHIFT 0 | |
95 | #define HDMI_CON5 0x14 | |
96 | #define RG_HDMITX_DRV_IBIAS_CLK (0x3f << 24) | |
97 | #define RG_HDMITX_DRV_IBIAS_D2 (0x3f << 16) | |
98 | #define RG_HDMITX_DRV_IBIAS_D1 (0x3f << 8) | |
99 | #define RG_HDMITX_DRV_IBIAS_D0 (0x3f << 0) | |
100 | #define DRV_IBIAS_CLK_SHIFT 24 | |
101 | #define DRV_IBIAS_D2_SHIFT 16 | |
102 | #define DRV_IBIAS_D1_SHIFT 8 | |
103 | #define DRV_IBIAS_D0_SHIFT 0 | |
104 | #define HDMI_CON6 0x18 | |
105 | #define RG_HDMITX_DRV_IMP_CLK (0x3f << 24) | |
106 | #define RG_HDMITX_DRV_IMP_D2 (0x3f << 16) | |
107 | #define RG_HDMITX_DRV_IMP_D1 (0x3f << 8) | |
108 | #define RG_HDMITX_DRV_IMP_D0 (0x3f << 0) | |
109 | #define DRV_IMP_CLK_SHIFT 24 | |
110 | #define DRV_IMP_D2_SHIFT 16 | |
111 | #define DRV_IMP_D1_SHIFT 8 | |
112 | #define DRV_IMP_D0_SHIFT 0 | |
113 | #define HDMI_CON7 0x1c | |
114 | #define RG_HDMITX_MHLCK_DRV_IBIAS (0x1f << 27) | |
115 | #define RG_HDMITX_SER_DIN (0x3ff << 16) | |
116 | #define RG_HDMITX_CHLDC_TST (0xf << 12) | |
117 | #define RG_HDMITX_CHLCK_TST (0xf << 8) | |
118 | #define RG_HDMITX_RESERVE (0xff << 0) | |
119 | #define HDMI_CON8 0x20 | |
120 | #define RGS_HDMITX_2T1_LEV (0xf << 16) | |
121 | #define RGS_HDMITX_2T1_EDG (0xf << 12) | |
122 | #define RGS_HDMITX_5T1_LEV (0xf << 8) | |
123 | #define RGS_HDMITX_5T1_EDG (0xf << 4) | |
124 | #define RGS_HDMITX_PLUG_TST BIT(0) | |
125 | ||
126 | struct mtk_hdmi_phy { | |
127 | void __iomem *regs; | |
128 | struct device *dev; | |
129 | struct clk *pll; | |
130 | struct clk_hw pll_hw; | |
131 | unsigned long pll_rate; | |
132 | u8 drv_imp_clk; | |
133 | u8 drv_imp_d2; | |
134 | u8 drv_imp_d1; | |
135 | u8 drv_imp_d0; | |
136 | u32 ibias; | |
137 | u32 ibias_up; | |
138 | }; | |
139 | ||
140 | static const u8 PREDIV[3][4] = { | |
141 | {0x0, 0x0, 0x0, 0x0}, /* 27Mhz */ | |
142 | {0x1, 0x1, 0x1, 0x1}, /* 74Mhz */ | |
143 | {0x1, 0x1, 0x1, 0x1} /* 148Mhz */ | |
144 | }; | |
145 | ||
146 | static const u8 TXDIV[3][4] = { | |
147 | {0x3, 0x3, 0x3, 0x2}, /* 27Mhz */ | |
148 | {0x2, 0x1, 0x1, 0x1}, /* 74Mhz */ | |
149 | {0x1, 0x0, 0x0, 0x0} /* 148Mhz */ | |
150 | }; | |
151 | ||
152 | static const u8 FBKSEL[3][4] = { | |
153 | {0x1, 0x1, 0x1, 0x1}, /* 27Mhz */ | |
154 | {0x1, 0x0, 0x1, 0x1}, /* 74Mhz */ | |
155 | {0x1, 0x0, 0x1, 0x1} /* 148Mhz */ | |
156 | }; | |
157 | ||
158 | static const u8 FBKDIV[3][4] = { | |
159 | {19, 24, 29, 19}, /* 27Mhz */ | |
160 | {19, 24, 14, 19}, /* 74Mhz */ | |
161 | {19, 24, 14, 19} /* 148Mhz */ | |
162 | }; | |
163 | ||
164 | static const u8 DIVEN[3][4] = { | |
165 | {0x2, 0x1, 0x1, 0x2}, /* 27Mhz */ | |
166 | {0x2, 0x2, 0x2, 0x2}, /* 74Mhz */ | |
167 | {0x2, 0x2, 0x2, 0x2} /* 148Mhz */ | |
168 | }; | |
169 | ||
170 | static const u8 HTPLLBP[3][4] = { | |
171 | {0xc, 0xc, 0x8, 0xc}, /* 27Mhz */ | |
172 | {0xc, 0xf, 0xf, 0xc}, /* 74Mhz */ | |
173 | {0xc, 0xf, 0xf, 0xc} /* 148Mhz */ | |
174 | }; | |
175 | ||
176 | static const u8 HTPLLBC[3][4] = { | |
177 | {0x2, 0x3, 0x3, 0x2}, /* 27Mhz */ | |
178 | {0x2, 0x3, 0x3, 0x2}, /* 74Mhz */ | |
179 | {0x2, 0x3, 0x3, 0x2} /* 148Mhz */ | |
180 | }; | |
181 | ||
182 | static const u8 HTPLLBR[3][4] = { | |
183 | {0x1, 0x1, 0x0, 0x1}, /* 27Mhz */ | |
184 | {0x1, 0x2, 0x2, 0x1}, /* 74Mhz */ | |
185 | {0x1, 0x2, 0x2, 0x1} /* 148Mhz */ | |
186 | }; | |
187 | ||
188 | static void mtk_hdmi_phy_clear_bits(struct mtk_hdmi_phy *hdmi_phy, u32 offset, | |
189 | u32 bits) | |
190 | { | |
191 | void __iomem *reg = hdmi_phy->regs + offset; | |
192 | u32 tmp; | |
193 | ||
194 | tmp = readl(reg); | |
195 | tmp &= ~bits; | |
196 | writel(tmp, reg); | |
197 | } | |
198 | ||
199 | static void mtk_hdmi_phy_set_bits(struct mtk_hdmi_phy *hdmi_phy, u32 offset, | |
200 | u32 bits) | |
201 | { | |
202 | void __iomem *reg = hdmi_phy->regs + offset; | |
203 | u32 tmp; | |
204 | ||
205 | tmp = readl(reg); | |
206 | tmp |= bits; | |
207 | writel(tmp, reg); | |
208 | } | |
209 | ||
210 | static void mtk_hdmi_phy_mask(struct mtk_hdmi_phy *hdmi_phy, u32 offset, | |
211 | u32 val, u32 mask) | |
212 | { | |
213 | void __iomem *reg = hdmi_phy->regs + offset; | |
214 | u32 tmp; | |
215 | ||
216 | tmp = readl(reg); | |
217 | tmp = (tmp & ~mask) | (val & mask); | |
218 | writel(tmp, reg); | |
219 | } | |
220 | ||
221 | static inline struct mtk_hdmi_phy *to_mtk_hdmi_phy(struct clk_hw *hw) | |
222 | { | |
223 | return container_of(hw, struct mtk_hdmi_phy, pll_hw); | |
224 | } | |
225 | ||
226 | static int mtk_hdmi_pll_prepare(struct clk_hw *hw) | |
227 | { | |
228 | struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw); | |
229 | ||
230 | dev_dbg(hdmi_phy->dev, "%s\n", __func__); | |
231 | ||
232 | mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON1, RG_HDMITX_PLL_AUTOK_EN); | |
233 | mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON0, RG_HDMITX_PLL_POSDIV); | |
234 | mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON3, RG_HDMITX_MHLCK_EN); | |
235 | mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON1, RG_HDMITX_PLL_BIAS_EN); | |
236 | usleep_range(100, 150); | |
237 | mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON0, RG_HDMITX_PLL_EN); | |
238 | usleep_range(100, 150); | |
239 | mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON1, RG_HDMITX_PLL_BIAS_LPF_EN); | |
240 | mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON1, RG_HDMITX_PLL_TXDIV_EN); | |
241 | ||
242 | return 0; | |
243 | } | |
244 | ||
245 | static void mtk_hdmi_pll_unprepare(struct clk_hw *hw) | |
246 | { | |
247 | struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw); | |
248 | ||
249 | dev_dbg(hdmi_phy->dev, "%s\n", __func__); | |
250 | ||
251 | mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON1, RG_HDMITX_PLL_TXDIV_EN); | |
252 | mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON1, RG_HDMITX_PLL_BIAS_LPF_EN); | |
253 | usleep_range(100, 150); | |
254 | mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON0, RG_HDMITX_PLL_EN); | |
255 | usleep_range(100, 150); | |
256 | mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON1, RG_HDMITX_PLL_BIAS_EN); | |
257 | mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON0, RG_HDMITX_PLL_POSDIV); | |
258 | mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON1, RG_HDMITX_PLL_AUTOK_EN); | |
259 | usleep_range(100, 150); | |
260 | } | |
261 | ||
262 | static int mtk_hdmi_pll_set_rate(struct clk_hw *hw, unsigned long rate, | |
263 | unsigned long parent_rate) | |
264 | { | |
265 | struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw); | |
266 | unsigned int pre_div; | |
267 | unsigned int div; | |
268 | ||
269 | dev_dbg(hdmi_phy->dev, "%s: %lu Hz, parent: %lu Hz\n", __func__, | |
270 | rate, parent_rate); | |
271 | ||
272 | if (rate <= 27000000) { | |
273 | pre_div = 0; | |
274 | div = 3; | |
275 | } else if (rate <= 74250000) { | |
276 | pre_div = 1; | |
277 | div = 2; | |
278 | } else { | |
279 | pre_div = 1; | |
280 | div = 1; | |
281 | } | |
282 | ||
283 | mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON0, | |
284 | (pre_div << PREDIV_SHIFT), RG_HDMITX_PLL_PREDIV); | |
285 | mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON0, RG_HDMITX_PLL_POSDIV); | |
286 | mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON0, | |
287 | (0x1 << PLL_IC_SHIFT) | (0x1 << PLL_IR_SHIFT), | |
288 | RG_HDMITX_PLL_IC | RG_HDMITX_PLL_IR); | |
289 | mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON1, | |
290 | (div << PLL_TXDIV_SHIFT), RG_HDMITX_PLL_TXDIV); | |
291 | mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON0, | |
292 | (0x1 << PLL_FBKSEL_SHIFT) | (19 << PLL_FBKDIV_SHIFT), | |
293 | RG_HDMITX_PLL_FBKSEL | RG_HDMITX_PLL_FBKDIV); | |
294 | mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON1, | |
295 | (0x2 << PLL_DIVEN_SHIFT), RG_HDMITX_PLL_DIVEN); | |
296 | mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON0, | |
297 | (0xc << PLL_BP_SHIFT) | (0x2 << PLL_BC_SHIFT) | | |
298 | (0x1 << PLL_BR_SHIFT), | |
299 | RG_HDMITX_PLL_BP | RG_HDMITX_PLL_BC | | |
300 | RG_HDMITX_PLL_BR); | |
301 | mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON3, RG_HDMITX_PRD_IMP_EN); | |
302 | mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON4, | |
303 | (0x3 << PRD_IBIAS_CLK_SHIFT) | | |
304 | (0x3 << PRD_IBIAS_D2_SHIFT) | | |
305 | (0x3 << PRD_IBIAS_D1_SHIFT) | | |
306 | (0x3 << PRD_IBIAS_D0_SHIFT), | |
307 | RG_HDMITX_PRD_IBIAS_CLK | | |
308 | RG_HDMITX_PRD_IBIAS_D2 | | |
309 | RG_HDMITX_PRD_IBIAS_D1 | | |
310 | RG_HDMITX_PRD_IBIAS_D0); | |
311 | mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON3, | |
312 | (0x0 << DRV_IMP_EN_SHIFT), RG_HDMITX_DRV_IMP_EN); | |
313 | mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON6, | |
314 | (hdmi_phy->drv_imp_clk << DRV_IMP_CLK_SHIFT) | | |
315 | (hdmi_phy->drv_imp_d2 << DRV_IMP_D2_SHIFT) | | |
316 | (hdmi_phy->drv_imp_d1 << DRV_IMP_D1_SHIFT) | | |
317 | (hdmi_phy->drv_imp_d0 << DRV_IMP_D0_SHIFT), | |
318 | RG_HDMITX_DRV_IMP_CLK | RG_HDMITX_DRV_IMP_D2 | | |
319 | RG_HDMITX_DRV_IMP_D1 | RG_HDMITX_DRV_IMP_D0); | |
320 | mtk_hdmi_phy_mask(hdmi_phy, HDMI_CON5, | |
321 | (hdmi_phy->ibias << DRV_IBIAS_CLK_SHIFT) | | |
322 | (hdmi_phy->ibias << DRV_IBIAS_D2_SHIFT) | | |
323 | (hdmi_phy->ibias << DRV_IBIAS_D1_SHIFT) | | |
324 | (hdmi_phy->ibias << DRV_IBIAS_D0_SHIFT), | |
325 | RG_HDMITX_DRV_IBIAS_CLK | RG_HDMITX_DRV_IBIAS_D2 | | |
326 | RG_HDMITX_DRV_IBIAS_D1 | RG_HDMITX_DRV_IBIAS_D0); | |
327 | return 0; | |
328 | } | |
329 | ||
330 | static long mtk_hdmi_pll_round_rate(struct clk_hw *hw, unsigned long rate, | |
331 | unsigned long *parent_rate) | |
332 | { | |
333 | struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw); | |
334 | ||
335 | hdmi_phy->pll_rate = rate; | |
336 | if (rate <= 74250000) | |
337 | *parent_rate = rate; | |
338 | else | |
339 | *parent_rate = rate / 2; | |
340 | ||
341 | return rate; | |
342 | } | |
343 | ||
344 | static unsigned long mtk_hdmi_pll_recalc_rate(struct clk_hw *hw, | |
345 | unsigned long parent_rate) | |
346 | { | |
347 | struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw); | |
348 | ||
349 | return hdmi_phy->pll_rate; | |
350 | } | |
351 | ||
352 | static const struct clk_ops mtk_hdmi_pll_ops = { | |
353 | .prepare = mtk_hdmi_pll_prepare, | |
354 | .unprepare = mtk_hdmi_pll_unprepare, | |
355 | .set_rate = mtk_hdmi_pll_set_rate, | |
356 | .round_rate = mtk_hdmi_pll_round_rate, | |
357 | .recalc_rate = mtk_hdmi_pll_recalc_rate, | |
358 | }; | |
359 | ||
360 | static void mtk_hdmi_phy_enable_tmds(struct mtk_hdmi_phy *hdmi_phy) | |
361 | { | |
362 | mtk_hdmi_phy_set_bits(hdmi_phy, HDMI_CON3, | |
363 | RG_HDMITX_SER_EN | RG_HDMITX_PRD_EN | | |
364 | RG_HDMITX_DRV_EN); | |
365 | usleep_range(100, 150); | |
366 | } | |
367 | ||
368 | static void mtk_hdmi_phy_disable_tmds(struct mtk_hdmi_phy *hdmi_phy) | |
369 | { | |
370 | mtk_hdmi_phy_clear_bits(hdmi_phy, HDMI_CON3, | |
371 | RG_HDMITX_DRV_EN | RG_HDMITX_PRD_EN | | |
372 | RG_HDMITX_SER_EN); | |
373 | } | |
374 | ||
375 | static int mtk_hdmi_phy_power_on(struct phy *phy) | |
376 | { | |
377 | struct mtk_hdmi_phy *hdmi_phy = phy_get_drvdata(phy); | |
378 | int ret; | |
379 | ||
380 | ret = clk_prepare_enable(hdmi_phy->pll); | |
381 | if (ret < 0) | |
382 | return ret; | |
383 | ||
384 | mtk_hdmi_phy_enable_tmds(hdmi_phy); | |
385 | ||
386 | return 0; | |
387 | } | |
388 | ||
389 | static int mtk_hdmi_phy_power_off(struct phy *phy) | |
390 | { | |
391 | struct mtk_hdmi_phy *hdmi_phy = phy_get_drvdata(phy); | |
392 | ||
393 | mtk_hdmi_phy_disable_tmds(hdmi_phy); | |
394 | clk_disable_unprepare(hdmi_phy->pll); | |
395 | ||
396 | return 0; | |
397 | } | |
398 | ||
399 | static const struct phy_ops mtk_hdmi_phy_ops = { | |
400 | .power_on = mtk_hdmi_phy_power_on, | |
401 | .power_off = mtk_hdmi_phy_power_off, | |
402 | .owner = THIS_MODULE, | |
403 | }; | |
404 | ||
405 | static int mtk_hdmi_phy_probe(struct platform_device *pdev) | |
406 | { | |
407 | struct device *dev = &pdev->dev; | |
408 | struct mtk_hdmi_phy *hdmi_phy; | |
409 | struct resource *mem; | |
410 | struct clk *ref_clk; | |
411 | const char *ref_clk_name; | |
412 | struct clk_init_data clk_init = { | |
413 | .ops = &mtk_hdmi_pll_ops, | |
414 | .num_parents = 1, | |
415 | .parent_names = (const char * const *)&ref_clk_name, | |
416 | .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, | |
417 | }; | |
418 | struct phy *phy; | |
419 | struct phy_provider *phy_provider; | |
420 | int ret; | |
421 | ||
422 | hdmi_phy = devm_kzalloc(dev, sizeof(*hdmi_phy), GFP_KERNEL); | |
423 | if (!hdmi_phy) | |
424 | return -ENOMEM; | |
425 | ||
426 | mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
427 | hdmi_phy->regs = devm_ioremap_resource(dev, mem); | |
428 | if (IS_ERR(hdmi_phy->regs)) { | |
429 | ret = PTR_ERR(hdmi_phy->regs); | |
430 | dev_err(dev, "Failed to get memory resource: %d\n", ret); | |
431 | return ret; | |
432 | } | |
433 | ||
434 | ref_clk = devm_clk_get(dev, "pll_ref"); | |
435 | if (IS_ERR(ref_clk)) { | |
436 | ret = PTR_ERR(ref_clk); | |
437 | dev_err(&pdev->dev, "Failed to get PLL reference clock: %d\n", | |
438 | ret); | |
439 | return ret; | |
440 | } | |
441 | ref_clk_name = __clk_get_name(ref_clk); | |
442 | ||
443 | ret = of_property_read_string(dev->of_node, "clock-output-names", | |
444 | &clk_init.name); | |
445 | if (ret < 0) { | |
446 | dev_err(dev, "Failed to read clock-output-names: %d\n", ret); | |
447 | return ret; | |
448 | } | |
449 | ||
450 | hdmi_phy->pll_hw.init = &clk_init; | |
451 | hdmi_phy->pll = devm_clk_register(dev, &hdmi_phy->pll_hw); | |
452 | if (IS_ERR(hdmi_phy->pll)) { | |
453 | ret = PTR_ERR(hdmi_phy->pll); | |
454 | dev_err(dev, "Failed to register PLL: %d\n", ret); | |
455 | return ret; | |
456 | } | |
457 | ||
458 | ret = of_property_read_u32(dev->of_node, "mediatek,ibias", | |
459 | &hdmi_phy->ibias); | |
460 | if (ret < 0) { | |
461 | dev_err(&pdev->dev, "Failed to get ibias: %d\n", ret); | |
462 | return ret; | |
463 | } | |
464 | ||
465 | ret = of_property_read_u32(dev->of_node, "mediatek,ibias_up", | |
466 | &hdmi_phy->ibias_up); | |
467 | if (ret < 0) { | |
468 | dev_err(&pdev->dev, "Failed to get ibias up: %d\n", ret); | |
469 | return ret; | |
470 | } | |
471 | ||
472 | dev_info(dev, "Using default TX DRV impedance: 4.2k/36\n"); | |
473 | hdmi_phy->drv_imp_clk = 0x30; | |
474 | hdmi_phy->drv_imp_d2 = 0x30; | |
475 | hdmi_phy->drv_imp_d1 = 0x30; | |
476 | hdmi_phy->drv_imp_d0 = 0x30; | |
477 | ||
478 | phy = devm_phy_create(dev, NULL, &mtk_hdmi_phy_ops); | |
479 | if (IS_ERR(phy)) { | |
480 | dev_err(dev, "Failed to create HDMI PHY\n"); | |
481 | return PTR_ERR(phy); | |
482 | } | |
483 | phy_set_drvdata(phy, hdmi_phy); | |
484 | ||
485 | phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); | |
486 | if (IS_ERR(phy_provider)) | |
487 | return PTR_ERR(phy_provider); | |
488 | ||
489 | hdmi_phy->dev = dev; | |
490 | return of_clk_add_provider(dev->of_node, of_clk_src_simple_get, | |
491 | hdmi_phy->pll); | |
492 | } | |
493 | ||
494 | static int mtk_hdmi_phy_remove(struct platform_device *pdev) | |
495 | { | |
496 | return 0; | |
497 | } | |
498 | ||
499 | static const struct of_device_id mtk_hdmi_phy_match[] = { | |
500 | { .compatible = "mediatek,mt8173-hdmi-phy", }, | |
501 | {}, | |
502 | }; | |
503 | ||
504 | struct platform_driver mtk_hdmi_phy_driver = { | |
505 | .probe = mtk_hdmi_phy_probe, | |
506 | .remove = mtk_hdmi_phy_remove, | |
507 | .driver = { | |
508 | .name = "mediatek-hdmi-phy", | |
509 | .of_match_table = mtk_hdmi_phy_match, | |
510 | }, | |
511 | }; | |
512 | ||
513 | MODULE_AUTHOR("Jie Qiu <jie.qiu@mediatek.com>"); | |
514 | MODULE_DESCRIPTION("MediaTek MT8173 HDMI PHY Driver"); | |
515 | MODULE_LICENSE("GPL v2"); |