Commit | Line | Data |
---|---|---|
2874c5fd | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
12b9f204 AY |
2 | /* |
3 | * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd | |
12b9f204 AY |
4 | */ |
5 | ||
5e3bc6d1 MY |
6 | #include <linux/clk.h> |
7 | #include <linux/mfd/syscon.h> | |
12b9f204 AY |
8 | #include <linux/module.h> |
9 | #include <linux/platform_device.h> | |
bd130230 | 10 | #include <linux/phy/phy.h> |
12b9f204 | 11 | #include <linux/regmap.h> |
ca80c4eb | 12 | #include <linux/regulator/consumer.h> |
5e3bc6d1 | 13 | |
c2156ccd | 14 | #include <drm/bridge/dw_hdmi.h> |
12b9f204 | 15 | #include <drm/drm_edid.h> |
c2156ccd | 16 | #include <drm/drm_of.h> |
fcd70cd3 | 17 | #include <drm/drm_probe_helper.h> |
0dbd7354 | 18 | #include <drm/drm_simple_kms_helper.h> |
12b9f204 AY |
19 | |
20 | #include "rockchip_drm_drv.h" | |
21 | #include "rockchip_drm_vop.h" | |
22 | ||
53ffa1ee JS |
23 | #define RK3228_GRF_SOC_CON2 0x0408 |
24 | #define RK3228_HDMI_SDAIN_MSK BIT(14) | |
25 | #define RK3228_HDMI_SCLIN_MSK BIT(13) | |
26 | #define RK3228_GRF_SOC_CON6 0x0418 | |
27 | #define RK3228_HDMI_HPD_VSEL BIT(6) | |
28 | #define RK3228_HDMI_SDA_VSEL BIT(5) | |
29 | #define RK3228_HDMI_SCL_VSEL BIT(4) | |
30 | ||
6445e394 MY |
31 | #define RK3288_GRF_SOC_CON6 0x025C |
32 | #define RK3288_HDMI_LCDC_SEL BIT(4) | |
1c53ba8f HS |
33 | #define RK3328_GRF_SOC_CON2 0x0408 |
34 | ||
35 | #define RK3328_HDMI_SDAIN_MSK BIT(11) | |
36 | #define RK3328_HDMI_SCLIN_MSK BIT(10) | |
37 | #define RK3328_HDMI_HPD_IOE BIT(2) | |
38 | #define RK3328_GRF_SOC_CON3 0x040c | |
39 | /* need to be unset if hdmi or i2c should control voltage */ | |
40 | #define RK3328_HDMI_SDA5V_GRF BIT(15) | |
41 | #define RK3328_HDMI_SCL5V_GRF BIT(14) | |
42 | #define RK3328_HDMI_HPD5V_GRF BIT(13) | |
43 | #define RK3328_HDMI_CEC5V_GRF BIT(12) | |
44 | #define RK3328_GRF_SOC_CON4 0x0410 | |
45 | #define RK3328_HDMI_HPD_SARADC BIT(13) | |
46 | #define RK3328_HDMI_CEC_5V BIT(11) | |
47 | #define RK3328_HDMI_SDA_5V BIT(10) | |
48 | #define RK3328_HDMI_SCL_5V BIT(9) | |
49 | #define RK3328_HDMI_HPD_5V BIT(8) | |
50 | ||
6445e394 MY |
51 | #define RK3399_GRF_SOC_CON20 0x6250 |
52 | #define RK3399_HDMI_LCDC_SEL BIT(6) | |
53 | ||
28bbb5ff SH |
54 | #define RK3568_GRF_VO_CON1 0x0364 |
55 | #define RK3568_HDMI_SDAIN_MSK BIT(15) | |
56 | #define RK3568_HDMI_SCLIN_MSK BIT(14) | |
57 | ||
6445e394 MY |
58 | #define HIWORD_UPDATE(val, mask) (val | (mask) << 16) |
59 | ||
60 | /** | |
61 | * struct rockchip_hdmi_chip_data - splite the grf setting of kind of chips | |
62 | * @lcdsel_grf_reg: grf register offset of lcdc select | |
63 | * @lcdsel_big: reg value of selecting vop big for HDMI | |
64 | * @lcdsel_lit: reg value of selecting vop little for HDMI | |
65 | */ | |
66 | struct rockchip_hdmi_chip_data { | |
96c4704f | 67 | int lcdsel_grf_reg; |
6445e394 MY |
68 | u32 lcdsel_big; |
69 | u32 lcdsel_lit; | |
70 | }; | |
12b9f204 AY |
71 | |
72 | struct rockchip_hdmi { | |
73 | struct device *dev; | |
74 | struct regmap *regmap; | |
540b8f27 | 75 | struct rockchip_encoder encoder; |
6445e394 | 76 | const struct rockchip_hdmi_chip_data *chip_data; |
de13db32 | 77 | const struct dw_hdmi_plat_data *plat_data; |
a9d37e68 | 78 | struct clk *ref_clk; |
8814b40b | 79 | struct clk *grf_clk; |
eea034af | 80 | struct dw_hdmi *hdmi; |
ca80c4eb SH |
81 | struct regulator *avdd_0v9; |
82 | struct regulator *avdd_1v8; | |
bd130230 | 83 | struct phy *phy; |
12b9f204 AY |
84 | }; |
85 | ||
540b8f27 SH |
86 | static struct rockchip_hdmi *to_rockchip_hdmi(struct drm_encoder *encoder) |
87 | { | |
88 | struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder); | |
89 | ||
90 | return container_of(rkencoder, struct rockchip_hdmi, encoder); | |
91 | } | |
12b9f204 AY |
92 | |
93 | static const struct dw_hdmi_mpll_config rockchip_mpll_cfg[] = { | |
94 | { | |
95 | 27000000, { | |
96 | { 0x00b3, 0x0000}, | |
97 | { 0x2153, 0x0000}, | |
98 | { 0x40f3, 0x0000} | |
99 | }, | |
100 | }, { | |
101 | 36000000, { | |
102 | { 0x00b3, 0x0000}, | |
103 | { 0x2153, 0x0000}, | |
104 | { 0x40f3, 0x0000} | |
105 | }, | |
106 | }, { | |
107 | 40000000, { | |
108 | { 0x00b3, 0x0000}, | |
109 | { 0x2153, 0x0000}, | |
110 | { 0x40f3, 0x0000} | |
111 | }, | |
112 | }, { | |
113 | 54000000, { | |
114 | { 0x0072, 0x0001}, | |
115 | { 0x2142, 0x0001}, | |
116 | { 0x40a2, 0x0001}, | |
117 | }, | |
118 | }, { | |
119 | 65000000, { | |
120 | { 0x0072, 0x0001}, | |
121 | { 0x2142, 0x0001}, | |
122 | { 0x40a2, 0x0001}, | |
123 | }, | |
124 | }, { | |
125 | 66000000, { | |
126 | { 0x013e, 0x0003}, | |
127 | { 0x217e, 0x0002}, | |
128 | { 0x4061, 0x0002} | |
129 | }, | |
130 | }, { | |
131 | 74250000, { | |
132 | { 0x0072, 0x0001}, | |
133 | { 0x2145, 0x0002}, | |
134 | { 0x4061, 0x0002} | |
135 | }, | |
136 | }, { | |
137 | 83500000, { | |
138 | { 0x0072, 0x0001}, | |
139 | }, | |
140 | }, { | |
141 | 108000000, { | |
142 | { 0x0051, 0x0002}, | |
143 | { 0x2145, 0x0002}, | |
144 | { 0x4061, 0x0002} | |
145 | }, | |
146 | }, { | |
147 | 106500000, { | |
148 | { 0x0051, 0x0002}, | |
149 | { 0x2145, 0x0002}, | |
150 | { 0x4061, 0x0002} | |
151 | }, | |
152 | }, { | |
153 | 146250000, { | |
154 | { 0x0051, 0x0002}, | |
155 | { 0x2145, 0x0002}, | |
156 | { 0x4061, 0x0002} | |
157 | }, | |
158 | }, { | |
159 | 148500000, { | |
160 | { 0x0051, 0x0003}, | |
161 | { 0x214c, 0x0003}, | |
162 | { 0x4064, 0x0003} | |
163 | }, | |
83b61f81 SH |
164 | }, { |
165 | 340000000, { | |
166 | { 0x0040, 0x0003 }, | |
167 | { 0x3b4c, 0x0003 }, | |
168 | { 0x5a64, 0x0003 }, | |
169 | }, | |
12b9f204 AY |
170 | }, { |
171 | ~0UL, { | |
172 | { 0x00a0, 0x000a }, | |
173 | { 0x2001, 0x000f }, | |
174 | { 0x4002, 0x000f }, | |
175 | }, | |
176 | } | |
177 | }; | |
178 | ||
179 | static const struct dw_hdmi_curr_ctrl rockchip_cur_ctr[] = { | |
180 | /* pixelclk bpp8 bpp10 bpp12 */ | |
181 | { | |
182 | 40000000, { 0x0018, 0x0018, 0x0018 }, | |
183 | }, { | |
184 | 65000000, { 0x0028, 0x0028, 0x0028 }, | |
185 | }, { | |
186 | 66000000, { 0x0038, 0x0038, 0x0038 }, | |
187 | }, { | |
188 | 74250000, { 0x0028, 0x0038, 0x0038 }, | |
189 | }, { | |
190 | 83500000, { 0x0028, 0x0038, 0x0038 }, | |
191 | }, { | |
192 | 146250000, { 0x0038, 0x0038, 0x0038 }, | |
193 | }, { | |
194 | 148500000, { 0x0000, 0x0038, 0x0038 }, | |
83b61f81 SH |
195 | }, { |
196 | 600000000, { 0x0000, 0x0000, 0x0000 }, | |
12b9f204 AY |
197 | }, { |
198 | ~0UL, { 0x0000, 0x0000, 0x0000}, | |
199 | } | |
200 | }; | |
201 | ||
034705a4 YY |
202 | static const struct dw_hdmi_phy_config rockchip_phy_config[] = { |
203 | /*pixelclk symbol term vlev*/ | |
1dbee1a3 YY |
204 | { 74250000, 0x8009, 0x0004, 0x0272}, |
205 | { 148500000, 0x802b, 0x0004, 0x028d}, | |
206 | { 297000000, 0x8039, 0x0005, 0x028d}, | |
034705a4 | 207 | { ~0UL, 0x0000, 0x0000, 0x0000} |
12b9f204 AY |
208 | }; |
209 | ||
210 | static int rockchip_hdmi_parse_dt(struct rockchip_hdmi *hdmi) | |
211 | { | |
212 | struct device_node *np = hdmi->dev->of_node; | |
213 | ||
214 | hdmi->regmap = syscon_regmap_lookup_by_phandle(np, "rockchip,grf"); | |
215 | if (IS_ERR(hdmi->regmap)) { | |
d8dd6804 | 216 | DRM_DEV_ERROR(hdmi->dev, "Unable to get rockchip,grf\n"); |
12b9f204 AY |
217 | return PTR_ERR(hdmi->regmap); |
218 | } | |
219 | ||
a9d37e68 SH |
220 | hdmi->ref_clk = devm_clk_get_optional(hdmi->dev, "ref"); |
221 | if (!hdmi->ref_clk) | |
222 | hdmi->ref_clk = devm_clk_get_optional(hdmi->dev, "vpll"); | |
223 | ||
224 | if (PTR_ERR(hdmi->ref_clk) == -EPROBE_DEFER) { | |
5e3bc6d1 | 225 | return -EPROBE_DEFER; |
a9d37e68 SH |
226 | } else if (IS_ERR(hdmi->ref_clk)) { |
227 | DRM_DEV_ERROR(hdmi->dev, "failed to get reference clock\n"); | |
228 | return PTR_ERR(hdmi->ref_clk); | |
5e3bc6d1 MY |
229 | } |
230 | ||
8814b40b MY |
231 | hdmi->grf_clk = devm_clk_get(hdmi->dev, "grf"); |
232 | if (PTR_ERR(hdmi->grf_clk) == -ENOENT) { | |
233 | hdmi->grf_clk = NULL; | |
234 | } else if (PTR_ERR(hdmi->grf_clk) == -EPROBE_DEFER) { | |
235 | return -EPROBE_DEFER; | |
236 | } else if (IS_ERR(hdmi->grf_clk)) { | |
d8dd6804 | 237 | DRM_DEV_ERROR(hdmi->dev, "failed to get grf clock\n"); |
8814b40b MY |
238 | return PTR_ERR(hdmi->grf_clk); |
239 | } | |
240 | ||
ca80c4eb SH |
241 | hdmi->avdd_0v9 = devm_regulator_get(hdmi->dev, "avdd-0v9"); |
242 | if (IS_ERR(hdmi->avdd_0v9)) | |
243 | return PTR_ERR(hdmi->avdd_0v9); | |
244 | ||
245 | hdmi->avdd_1v8 = devm_regulator_get(hdmi->dev, "avdd-1v8"); | |
246 | if (IS_ERR(hdmi->avdd_1v8)) | |
247 | return PTR_ERR(hdmi->avdd_1v8); | |
248 | ||
12b9f204 AY |
249 | return 0; |
250 | } | |
251 | ||
252 | static enum drm_mode_status | |
de13db32 | 253 | dw_hdmi_rockchip_mode_valid(struct dw_hdmi *dw_hdmi, void *data, |
af05bba0 | 254 | const struct drm_display_info *info, |
b0febde7 | 255 | const struct drm_display_mode *mode) |
12b9f204 | 256 | { |
de13db32 | 257 | struct rockchip_hdmi *hdmi = data; |
12b9f204 AY |
258 | const struct dw_hdmi_mpll_config *mpll_cfg = rockchip_mpll_cfg; |
259 | int pclk = mode->clock * 1000; | |
de13db32 | 260 | bool exact_match = hdmi->plat_data->phy_force_vendor; |
12b9f204 AY |
261 | int i; |
262 | ||
d13b10ec SH |
263 | if (hdmi->ref_clk) { |
264 | int rpclk = clk_round_rate(hdmi->ref_clk, pclk); | |
265 | ||
266 | if (abs(rpclk - pclk) > pclk / 1000) | |
267 | return MODE_NOCLOCK; | |
268 | } | |
269 | ||
12b9f204 | 270 | for (i = 0; mpll_cfg[i].mpixelclock != (~0UL); i++) { |
de13db32 SH |
271 | /* |
272 | * For vendor specific phys force an exact match of the pixelclock | |
273 | * to preserve the original behaviour of the driver. | |
274 | */ | |
275 | if (exact_match && pclk == mpll_cfg[i].mpixelclock) | |
276 | return MODE_OK; | |
277 | /* | |
278 | * The Synopsys phy can work with pixelclocks up to the value given | |
279 | * in the corresponding mpll_cfg entry. | |
280 | */ | |
281 | if (!exact_match && pclk <= mpll_cfg[i].mpixelclock) | |
282 | return MODE_OK; | |
12b9f204 AY |
283 | } |
284 | ||
de13db32 | 285 | return MODE_BAD; |
12b9f204 AY |
286 | } |
287 | ||
12b9f204 AY |
288 | static void dw_hdmi_rockchip_encoder_disable(struct drm_encoder *encoder) |
289 | { | |
290 | } | |
291 | ||
292 | static bool | |
293 | dw_hdmi_rockchip_encoder_mode_fixup(struct drm_encoder *encoder, | |
294 | const struct drm_display_mode *mode, | |
295 | struct drm_display_mode *adj_mode) | |
296 | { | |
297 | return true; | |
298 | } | |
299 | ||
300 | static void dw_hdmi_rockchip_encoder_mode_set(struct drm_encoder *encoder, | |
301 | struct drm_display_mode *mode, | |
302 | struct drm_display_mode *adj_mode) | |
303 | { | |
5e3bc6d1 MY |
304 | struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder); |
305 | ||
a9d37e68 | 306 | clk_set_rate(hdmi->ref_clk, adj_mode->clock * 1000); |
12b9f204 AY |
307 | } |
308 | ||
a8eef71d | 309 | static void dw_hdmi_rockchip_encoder_enable(struct drm_encoder *encoder) |
12b9f204 AY |
310 | { |
311 | struct rockchip_hdmi *hdmi = to_rockchip_hdmi(encoder); | |
312 | u32 val; | |
6445e394 | 313 | int ret; |
12b9f204 | 314 | |
96c4704f HS |
315 | if (hdmi->chip_data->lcdsel_grf_reg < 0) |
316 | return; | |
317 | ||
6445e394 MY |
318 | ret = drm_of_encoder_active_endpoint_id(hdmi->dev->of_node, encoder); |
319 | if (ret) | |
320 | val = hdmi->chip_data->lcdsel_lit; | |
12b9f204 | 321 | else |
6445e394 MY |
322 | val = hdmi->chip_data->lcdsel_big; |
323 | ||
8814b40b MY |
324 | ret = clk_prepare_enable(hdmi->grf_clk); |
325 | if (ret < 0) { | |
d8dd6804 | 326 | DRM_DEV_ERROR(hdmi->dev, "failed to enable grfclk %d\n", ret); |
8814b40b MY |
327 | return; |
328 | } | |
329 | ||
6445e394 MY |
330 | ret = regmap_write(hdmi->regmap, hdmi->chip_data->lcdsel_grf_reg, val); |
331 | if (ret != 0) | |
d8dd6804 | 332 | DRM_DEV_ERROR(hdmi->dev, "Could not write to GRF: %d\n", ret); |
12b9f204 | 333 | |
8814b40b | 334 | clk_disable_unprepare(hdmi->grf_clk); |
d8dd6804 HM |
335 | DRM_DEV_DEBUG(hdmi->dev, "vop %s output to hdmi\n", |
336 | ret ? "LIT" : "BIG"); | |
12b9f204 AY |
337 | } |
338 | ||
4e257d9e MY |
339 | static int |
340 | dw_hdmi_rockchip_encoder_atomic_check(struct drm_encoder *encoder, | |
341 | struct drm_crtc_state *crtc_state, | |
342 | struct drm_connector_state *conn_state) | |
343 | { | |
344 | struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state); | |
345 | ||
346 | s->output_mode = ROCKCHIP_OUT_MODE_AAAA; | |
347 | s->output_type = DRM_MODE_CONNECTOR_HDMIA; | |
348 | ||
349 | return 0; | |
350 | } | |
351 | ||
28c508ec | 352 | static const struct drm_encoder_helper_funcs dw_hdmi_rockchip_encoder_helper_funcs = { |
12b9f204 AY |
353 | .mode_fixup = dw_hdmi_rockchip_encoder_mode_fixup, |
354 | .mode_set = dw_hdmi_rockchip_encoder_mode_set, | |
a8eef71d | 355 | .enable = dw_hdmi_rockchip_encoder_enable, |
12b9f204 | 356 | .disable = dw_hdmi_rockchip_encoder_disable, |
4e257d9e | 357 | .atomic_check = dw_hdmi_rockchip_encoder_atomic_check, |
12b9f204 AY |
358 | }; |
359 | ||
1c53ba8f | 360 | static int dw_hdmi_rockchip_genphy_init(struct dw_hdmi *dw_hdmi, void *data, |
7be390d4 | 361 | const struct drm_display_info *display, |
35a395f1 | 362 | const struct drm_display_mode *mode) |
1c53ba8f HS |
363 | { |
364 | struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; | |
365 | ||
366 | return phy_power_on(hdmi->phy); | |
367 | } | |
368 | ||
369 | static void dw_hdmi_rockchip_genphy_disable(struct dw_hdmi *dw_hdmi, void *data) | |
370 | { | |
371 | struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; | |
372 | ||
373 | phy_power_off(hdmi->phy); | |
374 | } | |
375 | ||
53ffa1ee JS |
376 | static void dw_hdmi_rk3228_setup_hpd(struct dw_hdmi *dw_hdmi, void *data) |
377 | { | |
378 | struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; | |
379 | ||
380 | dw_hdmi_phy_setup_hpd(dw_hdmi, data); | |
381 | ||
382 | regmap_write(hdmi->regmap, | |
383 | RK3228_GRF_SOC_CON6, | |
384 | HIWORD_UPDATE(RK3228_HDMI_HPD_VSEL | RK3228_HDMI_SDA_VSEL | | |
385 | RK3228_HDMI_SCL_VSEL, | |
386 | RK3228_HDMI_HPD_VSEL | RK3228_HDMI_SDA_VSEL | | |
387 | RK3228_HDMI_SCL_VSEL)); | |
388 | ||
389 | regmap_write(hdmi->regmap, | |
390 | RK3228_GRF_SOC_CON2, | |
391 | HIWORD_UPDATE(RK3228_HDMI_SDAIN_MSK | RK3228_HDMI_SCLIN_MSK, | |
392 | RK3228_HDMI_SDAIN_MSK | RK3228_HDMI_SCLIN_MSK)); | |
393 | } | |
394 | ||
1c53ba8f HS |
395 | static enum drm_connector_status |
396 | dw_hdmi_rk3328_read_hpd(struct dw_hdmi *dw_hdmi, void *data) | |
397 | { | |
398 | struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; | |
399 | enum drm_connector_status status; | |
400 | ||
401 | status = dw_hdmi_phy_read_hpd(dw_hdmi, data); | |
402 | ||
403 | if (status == connector_status_connected) | |
404 | regmap_write(hdmi->regmap, | |
405 | RK3328_GRF_SOC_CON4, | |
406 | HIWORD_UPDATE(RK3328_HDMI_SDA_5V | RK3328_HDMI_SCL_5V, | |
407 | RK3328_HDMI_SDA_5V | RK3328_HDMI_SCL_5V)); | |
408 | else | |
409 | regmap_write(hdmi->regmap, | |
410 | RK3328_GRF_SOC_CON4, | |
411 | HIWORD_UPDATE(0, RK3328_HDMI_SDA_5V | | |
412 | RK3328_HDMI_SCL_5V)); | |
413 | return status; | |
414 | } | |
415 | ||
416 | static void dw_hdmi_rk3328_setup_hpd(struct dw_hdmi *dw_hdmi, void *data) | |
417 | { | |
418 | struct rockchip_hdmi *hdmi = (struct rockchip_hdmi *)data; | |
419 | ||
420 | dw_hdmi_phy_setup_hpd(dw_hdmi, data); | |
421 | ||
422 | /* Enable and map pins to 3V grf-controlled io-voltage */ | |
423 | regmap_write(hdmi->regmap, | |
424 | RK3328_GRF_SOC_CON4, | |
425 | HIWORD_UPDATE(0, RK3328_HDMI_HPD_SARADC | RK3328_HDMI_CEC_5V | | |
426 | RK3328_HDMI_SDA_5V | RK3328_HDMI_SCL_5V | | |
427 | RK3328_HDMI_HPD_5V)); | |
428 | regmap_write(hdmi->regmap, | |
429 | RK3328_GRF_SOC_CON3, | |
430 | HIWORD_UPDATE(0, RK3328_HDMI_SDA5V_GRF | RK3328_HDMI_SCL5V_GRF | | |
431 | RK3328_HDMI_HPD5V_GRF | | |
432 | RK3328_HDMI_CEC5V_GRF)); | |
433 | regmap_write(hdmi->regmap, | |
434 | RK3328_GRF_SOC_CON2, | |
435 | HIWORD_UPDATE(RK3328_HDMI_SDAIN_MSK | RK3328_HDMI_SCLIN_MSK, | |
436 | RK3328_HDMI_SDAIN_MSK | RK3328_HDMI_SCLIN_MSK | | |
437 | RK3328_HDMI_HPD_IOE)); | |
438 | } | |
439 | ||
53ffa1ee JS |
440 | static const struct dw_hdmi_phy_ops rk3228_hdmi_phy_ops = { |
441 | .init = dw_hdmi_rockchip_genphy_init, | |
442 | .disable = dw_hdmi_rockchip_genphy_disable, | |
443 | .read_hpd = dw_hdmi_phy_read_hpd, | |
444 | .update_hpd = dw_hdmi_phy_update_hpd, | |
445 | .setup_hpd = dw_hdmi_rk3228_setup_hpd, | |
446 | }; | |
447 | ||
448 | static struct rockchip_hdmi_chip_data rk3228_chip_data = { | |
449 | .lcdsel_grf_reg = -1, | |
450 | }; | |
451 | ||
452 | static const struct dw_hdmi_plat_data rk3228_hdmi_drv_data = { | |
453 | .mode_valid = dw_hdmi_rockchip_mode_valid, | |
454 | .mpll_cfg = rockchip_mpll_cfg, | |
455 | .cur_ctr = rockchip_cur_ctr, | |
456 | .phy_config = rockchip_phy_config, | |
457 | .phy_data = &rk3228_chip_data, | |
458 | .phy_ops = &rk3228_hdmi_phy_ops, | |
459 | .phy_name = "inno_dw_hdmi_phy2", | |
460 | .phy_force_vendor = true, | |
461 | }; | |
462 | ||
6445e394 MY |
463 | static struct rockchip_hdmi_chip_data rk3288_chip_data = { |
464 | .lcdsel_grf_reg = RK3288_GRF_SOC_CON6, | |
465 | .lcdsel_big = HIWORD_UPDATE(0, RK3288_HDMI_LCDC_SEL), | |
466 | .lcdsel_lit = HIWORD_UPDATE(RK3288_HDMI_LCDC_SEL, RK3288_HDMI_LCDC_SEL), | |
467 | }; | |
468 | ||
469 | static const struct dw_hdmi_plat_data rk3288_hdmi_drv_data = { | |
12b9f204 AY |
470 | .mode_valid = dw_hdmi_rockchip_mode_valid, |
471 | .mpll_cfg = rockchip_mpll_cfg, | |
472 | .cur_ctr = rockchip_cur_ctr, | |
034705a4 | 473 | .phy_config = rockchip_phy_config, |
6445e394 MY |
474 | .phy_data = &rk3288_chip_data, |
475 | }; | |
476 | ||
1c53ba8f HS |
477 | static const struct dw_hdmi_phy_ops rk3328_hdmi_phy_ops = { |
478 | .init = dw_hdmi_rockchip_genphy_init, | |
479 | .disable = dw_hdmi_rockchip_genphy_disable, | |
480 | .read_hpd = dw_hdmi_rk3328_read_hpd, | |
481 | .update_hpd = dw_hdmi_phy_update_hpd, | |
482 | .setup_hpd = dw_hdmi_rk3328_setup_hpd, | |
483 | }; | |
484 | ||
485 | static struct rockchip_hdmi_chip_data rk3328_chip_data = { | |
486 | .lcdsel_grf_reg = -1, | |
487 | }; | |
488 | ||
489 | static const struct dw_hdmi_plat_data rk3328_hdmi_drv_data = { | |
490 | .mode_valid = dw_hdmi_rockchip_mode_valid, | |
491 | .mpll_cfg = rockchip_mpll_cfg, | |
492 | .cur_ctr = rockchip_cur_ctr, | |
493 | .phy_config = rockchip_phy_config, | |
494 | .phy_data = &rk3328_chip_data, | |
495 | .phy_ops = &rk3328_hdmi_phy_ops, | |
496 | .phy_name = "inno_dw_hdmi_phy2", | |
497 | .phy_force_vendor = true, | |
1bf95915 | 498 | .use_drm_infoframe = true, |
1c53ba8f HS |
499 | }; |
500 | ||
6445e394 MY |
501 | static struct rockchip_hdmi_chip_data rk3399_chip_data = { |
502 | .lcdsel_grf_reg = RK3399_GRF_SOC_CON20, | |
503 | .lcdsel_big = HIWORD_UPDATE(0, RK3399_HDMI_LCDC_SEL), | |
504 | .lcdsel_lit = HIWORD_UPDATE(RK3399_HDMI_LCDC_SEL, RK3399_HDMI_LCDC_SEL), | |
505 | }; | |
506 | ||
507 | static const struct dw_hdmi_plat_data rk3399_hdmi_drv_data = { | |
508 | .mode_valid = dw_hdmi_rockchip_mode_valid, | |
509 | .mpll_cfg = rockchip_mpll_cfg, | |
510 | .cur_ctr = rockchip_cur_ctr, | |
511 | .phy_config = rockchip_phy_config, | |
512 | .phy_data = &rk3399_chip_data, | |
1bf95915 | 513 | .use_drm_infoframe = true, |
12b9f204 AY |
514 | }; |
515 | ||
28bbb5ff SH |
516 | static struct rockchip_hdmi_chip_data rk3568_chip_data = { |
517 | .lcdsel_grf_reg = -1, | |
518 | }; | |
519 | ||
520 | static const struct dw_hdmi_plat_data rk3568_hdmi_drv_data = { | |
521 | .mode_valid = dw_hdmi_rockchip_mode_valid, | |
522 | .mpll_cfg = rockchip_mpll_cfg, | |
523 | .cur_ctr = rockchip_cur_ctr, | |
524 | .phy_config = rockchip_phy_config, | |
525 | .phy_data = &rk3568_chip_data, | |
526 | .use_drm_infoframe = true, | |
527 | }; | |
528 | ||
12b9f204 | 529 | static const struct of_device_id dw_hdmi_rockchip_dt_ids[] = { |
53ffa1ee JS |
530 | { .compatible = "rockchip,rk3228-dw-hdmi", |
531 | .data = &rk3228_hdmi_drv_data | |
532 | }, | |
12b9f204 | 533 | { .compatible = "rockchip,rk3288-dw-hdmi", |
6445e394 MY |
534 | .data = &rk3288_hdmi_drv_data |
535 | }, | |
1c53ba8f HS |
536 | { .compatible = "rockchip,rk3328-dw-hdmi", |
537 | .data = &rk3328_hdmi_drv_data | |
538 | }, | |
6445e394 MY |
539 | { .compatible = "rockchip,rk3399-dw-hdmi", |
540 | .data = &rk3399_hdmi_drv_data | |
12b9f204 | 541 | }, |
28bbb5ff SH |
542 | { .compatible = "rockchip,rk3568-dw-hdmi", |
543 | .data = &rk3568_hdmi_drv_data | |
544 | }, | |
12b9f204 AY |
545 | {}, |
546 | }; | |
547 | MODULE_DEVICE_TABLE(of, dw_hdmi_rockchip_dt_ids); | |
548 | ||
549 | static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, | |
550 | void *data) | |
551 | { | |
552 | struct platform_device *pdev = to_platform_device(dev); | |
5c3f3d22 | 553 | struct dw_hdmi_plat_data *plat_data; |
12b9f204 AY |
554 | const struct of_device_id *match; |
555 | struct drm_device *drm = data; | |
556 | struct drm_encoder *encoder; | |
557 | struct rockchip_hdmi *hdmi; | |
12b9f204 AY |
558 | int ret; |
559 | ||
560 | if (!pdev->dev.of_node) | |
561 | return -ENODEV; | |
562 | ||
563 | hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL); | |
564 | if (!hdmi) | |
565 | return -ENOMEM; | |
566 | ||
567 | match = of_match_node(dw_hdmi_rockchip_dt_ids, pdev->dev.of_node); | |
5c3f3d22 HS |
568 | plat_data = devm_kmemdup(&pdev->dev, match->data, |
569 | sizeof(*plat_data), GFP_KERNEL); | |
570 | if (!plat_data) | |
571 | return -ENOMEM; | |
572 | ||
12b9f204 | 573 | hdmi->dev = &pdev->dev; |
de13db32 | 574 | hdmi->plat_data = plat_data; |
6445e394 | 575 | hdmi->chip_data = plat_data->phy_data; |
5c3f3d22 | 576 | plat_data->phy_data = hdmi; |
de13db32 | 577 | plat_data->priv_data = hdmi; |
540b8f27 | 578 | encoder = &hdmi->encoder.encoder; |
12b9f204 | 579 | |
12b9f204 | 580 | encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node); |
28bbb5ff SH |
581 | rockchip_drm_encoder_set_crtc_endpoint_id(&hdmi->encoder, |
582 | dev->of_node, 0, 0); | |
583 | ||
12b9f204 AY |
584 | /* |
585 | * If we failed to find the CRTC(s) which this encoder is | |
586 | * supposed to be connected to, it's because the CRTC has | |
587 | * not been registered yet. Defer probing, and hope that | |
588 | * the required CRTC is added later. | |
589 | */ | |
590 | if (encoder->possible_crtcs == 0) | |
591 | return -EPROBE_DEFER; | |
592 | ||
593 | ret = rockchip_hdmi_parse_dt(hdmi); | |
594 | if (ret) { | |
bfab00b9 AJ |
595 | if (ret != -EPROBE_DEFER) |
596 | DRM_DEV_ERROR(hdmi->dev, "Unable to parse OF data\n"); | |
12b9f204 AY |
597 | return ret; |
598 | } | |
599 | ||
bd130230 HS |
600 | hdmi->phy = devm_phy_optional_get(dev, "hdmi"); |
601 | if (IS_ERR(hdmi->phy)) { | |
602 | ret = PTR_ERR(hdmi->phy); | |
603 | if (ret != -EPROBE_DEFER) | |
604 | DRM_DEV_ERROR(hdmi->dev, "failed to get phy\n"); | |
605 | return ret; | |
606 | } | |
607 | ||
ca80c4eb SH |
608 | ret = regulator_enable(hdmi->avdd_0v9); |
609 | if (ret) { | |
610 | DRM_DEV_ERROR(hdmi->dev, "failed to enable avdd0v9: %d\n", ret); | |
611 | goto err_avdd_0v9; | |
612 | } | |
613 | ||
614 | ret = regulator_enable(hdmi->avdd_1v8); | |
615 | if (ret) { | |
616 | DRM_DEV_ERROR(hdmi->dev, "failed to enable avdd1v8: %d\n", ret); | |
617 | goto err_avdd_1v8; | |
618 | } | |
619 | ||
a9d37e68 | 620 | ret = clk_prepare_enable(hdmi->ref_clk); |
c0cfbb12 | 621 | if (ret) { |
a9d37e68 | 622 | DRM_DEV_ERROR(hdmi->dev, "Failed to enable HDMI reference clock: %d\n", |
c0cfbb12 | 623 | ret); |
ca80c4eb | 624 | goto err_clk; |
c0cfbb12 SH |
625 | } |
626 | ||
28bbb5ff SH |
627 | if (hdmi->chip_data == &rk3568_chip_data) { |
628 | regmap_write(hdmi->regmap, RK3568_GRF_VO_CON1, | |
629 | HIWORD_UPDATE(RK3568_HDMI_SDAIN_MSK | | |
630 | RK3568_HDMI_SCLIN_MSK, | |
631 | RK3568_HDMI_SDAIN_MSK | | |
632 | RK3568_HDMI_SCLIN_MSK)); | |
633 | } | |
634 | ||
12b9f204 | 635 | drm_encoder_helper_add(encoder, &dw_hdmi_rockchip_encoder_helper_funcs); |
0dbd7354 | 636 | drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS); |
12b9f204 | 637 | |
eea034af JS |
638 | platform_set_drvdata(pdev, hdmi); |
639 | ||
640 | hdmi->hdmi = dw_hdmi_bind(pdev, encoder, plat_data); | |
948cf427 DA |
641 | |
642 | /* | |
643 | * If dw_hdmi_bind() fails we'll never call dw_hdmi_unbind(), | |
644 | * which would have called the encoder cleanup. Do it manually. | |
645 | */ | |
eea034af JS |
646 | if (IS_ERR(hdmi->hdmi)) { |
647 | ret = PTR_ERR(hdmi->hdmi); | |
ca80c4eb | 648 | goto err_bind; |
eea034af | 649 | } |
948cf427 | 650 | |
ca80c4eb SH |
651 | return 0; |
652 | ||
653 | err_bind: | |
654 | drm_encoder_cleanup(encoder); | |
655 | clk_disable_unprepare(hdmi->ref_clk); | |
656 | err_clk: | |
657 | regulator_disable(hdmi->avdd_1v8); | |
658 | err_avdd_1v8: | |
659 | regulator_disable(hdmi->avdd_0v9); | |
660 | err_avdd_0v9: | |
948cf427 | 661 | return ret; |
12b9f204 AY |
662 | } |
663 | ||
664 | static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master, | |
665 | void *data) | |
666 | { | |
eea034af JS |
667 | struct rockchip_hdmi *hdmi = dev_get_drvdata(dev); |
668 | ||
669 | dw_hdmi_unbind(hdmi->hdmi); | |
b5af48ee | 670 | drm_encoder_cleanup(&hdmi->encoder.encoder); |
a9d37e68 | 671 | clk_disable_unprepare(hdmi->ref_clk); |
ca80c4eb SH |
672 | |
673 | regulator_disable(hdmi->avdd_1v8); | |
674 | regulator_disable(hdmi->avdd_0v9); | |
12b9f204 AY |
675 | } |
676 | ||
677 | static const struct component_ops dw_hdmi_rockchip_ops = { | |
678 | .bind = dw_hdmi_rockchip_bind, | |
679 | .unbind = dw_hdmi_rockchip_unbind, | |
680 | }; | |
681 | ||
682 | static int dw_hdmi_rockchip_probe(struct platform_device *pdev) | |
683 | { | |
684 | return component_add(&pdev->dev, &dw_hdmi_rockchip_ops); | |
685 | } | |
686 | ||
3c855610 | 687 | static void dw_hdmi_rockchip_remove(struct platform_device *pdev) |
12b9f204 AY |
688 | { |
689 | component_del(&pdev->dev, &dw_hdmi_rockchip_ops); | |
12b9f204 AY |
690 | } |
691 | ||
27c9130b DA |
692 | static int __maybe_unused dw_hdmi_rockchip_resume(struct device *dev) |
693 | { | |
694 | struct rockchip_hdmi *hdmi = dev_get_drvdata(dev); | |
695 | ||
696 | dw_hdmi_resume(hdmi->hdmi); | |
697 | ||
698 | return 0; | |
699 | } | |
700 | ||
701 | static const struct dev_pm_ops dw_hdmi_rockchip_pm = { | |
702 | SET_SYSTEM_SLEEP_PM_OPS(NULL, dw_hdmi_rockchip_resume) | |
703 | }; | |
704 | ||
8820b68b | 705 | struct platform_driver dw_hdmi_rockchip_pltfm_driver = { |
12b9f204 | 706 | .probe = dw_hdmi_rockchip_probe, |
3c855610 | 707 | .remove_new = dw_hdmi_rockchip_remove, |
12b9f204 AY |
708 | .driver = { |
709 | .name = "dwhdmi-rockchip", | |
27c9130b | 710 | .pm = &dw_hdmi_rockchip_pm, |
12b9f204 AY |
711 | .of_match_table = dw_hdmi_rockchip_dt_ids, |
712 | }, | |
713 | }; |