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