1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) 2021-2022 Rockchip Electronics Co., Ltd.
4 * Copyright (c) 2024 Collabora Ltd.
5 *
6 * Author: Algea Cao <algea.cao@rock-chips.com>
7 * Author: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
8 */
9
10#include <linux/clk.h>
11#include <linux/gpio/consumer.h>
12#include <linux/hw_bitfield.h>
13#include <linux/mfd/syscon.h>
14#include <linux/module.h>
15#include <linux/platform_device.h>
16#include <linux/phy/phy.h>
17#include <linux/phy/phy-hdmi.h>
18#include <linux/regmap.h>
19#include <linux/workqueue.h>
20
21#include <drm/bridge/dw_hdmi_qp.h>
22#include <drm/display/drm_hdmi_helper.h>
23#include <drm/drm_bridge_connector.h>
24#include <drm/drm_of.h>
25#include <drm/drm_probe_helper.h>
26#include <drm/drm_simple_kms_helper.h>
27
28#include "rockchip_drm_drv.h"
29
30#define RK3576_IOC_MISC_CON0 0xa400
31#define RK3576_HDMI_HPD_INT_MSK BIT(2)
32#define RK3576_HDMI_HPD_INT_CLR BIT(1)
33
34#define RK3576_IOC_HDMI_HPD_STATUS 0xa440
35#define RK3576_HDMI_LEVEL_INT BIT(3)
36
37#define RK3576_VO0_GRF_SOC_CON1 0x0004
38#define RK3576_HDMI_FRL_MOD BIT(0)
39#define RK3576_HDMI_HDCP14_MEM_EN BIT(15)
40
41#define RK3576_VO0_GRF_SOC_CON8 0x0020
42#define RK3576_COLOR_DEPTH_MASK GENMASK(11, 8)
43#define RK3576_8BPC 0x0
44#define RK3576_10BPC 0x6
45#define RK3576_COLOR_FORMAT_MASK GENMASK(7, 4)
46#define RK3576_RGB 0x9
47#define RK3576_YUV422 0x1
48#define RK3576_YUV444 0x2
49#define RK3576_YUV420 0x3
50#define RK3576_CECIN_MASK BIT(3)
51
52#define RK3576_VO0_GRF_SOC_CON14 0x0038
53#define RK3576_I2S_SEL_MASK BIT(0)
54#define RK3576_SPDIF_SEL_MASK BIT(1)
55#define HDCP0_P1_GPIO_IN BIT(2)
56#define RK3576_SCLIN_MASK BIT(4)
57#define RK3576_SDAIN_MASK BIT(5)
58#define RK3576_HDMI_GRANT_SEL BIT(6)
59
60#define RK3588_GRF_SOC_CON2 0x0308
61#define RK3588_HDMI0_HPD_INT_MSK BIT(13)
62#define RK3588_HDMI0_HPD_INT_CLR BIT(12)
63#define RK3588_HDMI1_HPD_INT_MSK BIT(15)
64#define RK3588_HDMI1_HPD_INT_CLR BIT(14)
65#define RK3588_GRF_SOC_CON7 0x031c
66#define RK3588_HPD_HDMI0_IO_EN_MASK BIT(12)
67#define RK3588_HPD_HDMI1_IO_EN_MASK BIT(13)
68#define RK3588_GRF_SOC_STATUS1 0x0384
69#define RK3588_HDMI0_LEVEL_INT BIT(16)
70#define RK3588_HDMI1_LEVEL_INT BIT(24)
71#define RK3588_GRF_VO1_CON3 0x000c
72#define RK3588_GRF_VO1_CON6 0x0018
73#define RK3588_COLOR_DEPTH_MASK GENMASK(7, 4)
74#define RK3588_8BPC 0x0
75#define RK3588_10BPC 0x6
76#define RK3588_COLOR_FORMAT_MASK GENMASK(3, 0)
77#define RK3588_RGB 0x0
78#define RK3588_YUV420 0x3
79#define RK3588_SCLIN_MASK BIT(9)
80#define RK3588_SDAIN_MASK BIT(10)
81#define RK3588_MODE_MASK BIT(11)
82#define RK3588_I2S_SEL_MASK BIT(13)
83#define RK3588_GRF_VO1_CON9 0x0024
84#define RK3588_HDMI0_GRANT_SEL BIT(10)
85#define RK3588_HDMI1_GRANT_SEL BIT(12)
86
87#define HOTPLUG_DEBOUNCE_MS 150
88#define MAX_HDMI_PORT_NUM 2
89
90struct rockchip_hdmi_qp {
91 struct device *dev;
92 struct regmap *regmap;
93 struct regmap *vo_regmap;
94 struct rockchip_encoder encoder;
95 struct dw_hdmi_qp *hdmi;
96 struct phy *phy;
97 struct gpio_desc *frl_enable_gpio;
98 struct delayed_work hpd_work;
99 int port_id;
100 const struct rockchip_hdmi_qp_ctrl_ops *ctrl_ops;
101 unsigned long long tmds_char_rate;
102};
103
104struct rockchip_hdmi_qp_ctrl_ops {
105 void (*io_init)(struct rockchip_hdmi_qp *hdmi);
106 void (*enc_init)(struct rockchip_hdmi_qp *hdmi, struct rockchip_crtc_state *state);
107 irqreturn_t (*irq_callback)(int irq, void *dev_id);
108 irqreturn_t (*hardirq_callback)(int irq, void *dev_id);
109};
110
111static struct rockchip_hdmi_qp *to_rockchip_hdmi_qp(struct drm_encoder *encoder)
112{
113 struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
114
115 return container_of(rkencoder, struct rockchip_hdmi_qp, encoder);
116}
117
118static void dw_hdmi_qp_rockchip_encoder_enable(struct drm_encoder *encoder)
119{
120 struct rockchip_hdmi_qp *hdmi = to_rockchip_hdmi_qp(encoder);
121 struct drm_crtc *crtc = encoder->crtc;
122
123 /* Unconditionally switch to TMDS as FRL is not yet supported */
124 gpiod_set_value_cansleep(desc: hdmi->frl_enable_gpio, value: 0);
125
126 if (!crtc || !crtc->state)
127 return;
128
129 if (hdmi->ctrl_ops->enc_init)
130 hdmi->ctrl_ops->enc_init(hdmi, to_rockchip_crtc_state(crtc->state));
131}
132
133static int
134dw_hdmi_qp_rockchip_encoder_atomic_check(struct drm_encoder *encoder,
135 struct drm_crtc_state *crtc_state,
136 struct drm_connector_state *conn_state)
137{
138 struct rockchip_hdmi_qp *hdmi = to_rockchip_hdmi_qp(encoder);
139 struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
140 union phy_configure_opts phy_cfg = {};
141 int ret;
142
143 if (hdmi->tmds_char_rate == conn_state->hdmi.tmds_char_rate &&
144 s->output_bpc == conn_state->hdmi.output_bpc)
145 return 0;
146
147 phy_cfg.hdmi.tmds_char_rate = conn_state->hdmi.tmds_char_rate;
148 phy_cfg.hdmi.bpc = conn_state->hdmi.output_bpc;
149
150 ret = phy_configure(phy: hdmi->phy, opts: &phy_cfg);
151 if (!ret) {
152 hdmi->tmds_char_rate = conn_state->hdmi.tmds_char_rate;
153 s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
154 s->output_type = DRM_MODE_CONNECTOR_HDMIA;
155 s->output_bpc = conn_state->hdmi.output_bpc;
156 } else {
157 dev_err(hdmi->dev, "Failed to configure phy: %d\n", ret);
158 }
159
160 return ret;
161}
162
163static const struct
164drm_encoder_helper_funcs dw_hdmi_qp_rockchip_encoder_helper_funcs = {
165 .enable = dw_hdmi_qp_rockchip_encoder_enable,
166 .atomic_check = dw_hdmi_qp_rockchip_encoder_atomic_check,
167};
168
169static int dw_hdmi_qp_rk3588_phy_init(struct dw_hdmi_qp *dw_hdmi, void *data)
170{
171 struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data;
172
173 return phy_power_on(phy: hdmi->phy);
174}
175
176static void dw_hdmi_qp_rk3588_phy_disable(struct dw_hdmi_qp *dw_hdmi,
177 void *data)
178{
179 struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data;
180
181 phy_power_off(phy: hdmi->phy);
182}
183
184static enum drm_connector_status
185dw_hdmi_qp_rk3588_read_hpd(struct dw_hdmi_qp *dw_hdmi, void *data)
186{
187 struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data;
188 u32 val;
189
190 regmap_read(map: hdmi->regmap, RK3588_GRF_SOC_STATUS1, val: &val);
191 val &= hdmi->port_id ? RK3588_HDMI1_LEVEL_INT : RK3588_HDMI0_LEVEL_INT;
192
193 return val ? connector_status_connected : connector_status_disconnected;
194}
195
196static void dw_hdmi_qp_rk3588_setup_hpd(struct dw_hdmi_qp *dw_hdmi, void *data)
197{
198 struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data;
199 u32 val;
200
201 if (hdmi->port_id)
202 val = (FIELD_PREP_WM16(RK3588_HDMI1_HPD_INT_CLR, 1) |
203 FIELD_PREP_WM16(RK3588_HDMI1_HPD_INT_MSK, 0));
204 else
205 val = (FIELD_PREP_WM16(RK3588_HDMI0_HPD_INT_CLR, 1) |
206 FIELD_PREP_WM16(RK3588_HDMI0_HPD_INT_MSK, 0));
207
208 regmap_write(map: hdmi->regmap, RK3588_GRF_SOC_CON2, val);
209}
210
211static const struct dw_hdmi_qp_phy_ops rk3588_hdmi_phy_ops = {
212 .init = dw_hdmi_qp_rk3588_phy_init,
213 .disable = dw_hdmi_qp_rk3588_phy_disable,
214 .read_hpd = dw_hdmi_qp_rk3588_read_hpd,
215 .setup_hpd = dw_hdmi_qp_rk3588_setup_hpd,
216};
217
218static enum drm_connector_status
219dw_hdmi_qp_rk3576_read_hpd(struct dw_hdmi_qp *dw_hdmi, void *data)
220{
221 struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data;
222 u32 val;
223
224 regmap_read(map: hdmi->regmap, RK3576_IOC_HDMI_HPD_STATUS, val: &val);
225
226 return val & RK3576_HDMI_LEVEL_INT ?
227 connector_status_connected : connector_status_disconnected;
228}
229
230static void dw_hdmi_qp_rk3576_setup_hpd(struct dw_hdmi_qp *dw_hdmi, void *data)
231{
232 struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data;
233 u32 val;
234
235 val = (FIELD_PREP_WM16(RK3576_HDMI_HPD_INT_CLR, 1) |
236 FIELD_PREP_WM16(RK3576_HDMI_HPD_INT_MSK, 0));
237
238 regmap_write(map: hdmi->regmap, RK3576_IOC_MISC_CON0, val);
239 regmap_write(map: hdmi->regmap, reg: 0xa404, val: 0xffff0102);
240}
241
242static const struct dw_hdmi_qp_phy_ops rk3576_hdmi_phy_ops = {
243 .init = dw_hdmi_qp_rk3588_phy_init,
244 .disable = dw_hdmi_qp_rk3588_phy_disable,
245 .read_hpd = dw_hdmi_qp_rk3576_read_hpd,
246 .setup_hpd = dw_hdmi_qp_rk3576_setup_hpd,
247};
248
249static void dw_hdmi_qp_rk3588_hpd_work(struct work_struct *work)
250{
251 struct rockchip_hdmi_qp *hdmi = container_of(work,
252 struct rockchip_hdmi_qp,
253 hpd_work.work);
254 struct drm_device *drm = hdmi->encoder.encoder.dev;
255 bool changed;
256
257 if (drm) {
258 changed = drm_helper_hpd_irq_event(dev: drm);
259 if (changed)
260 dev_dbg(hdmi->dev, "connector status changed\n");
261 }
262}
263
264static irqreturn_t dw_hdmi_qp_rk3576_hardirq(int irq, void *dev_id)
265{
266 struct rockchip_hdmi_qp *hdmi = dev_id;
267 u32 intr_stat, val;
268
269 regmap_read(map: hdmi->regmap, RK3576_IOC_HDMI_HPD_STATUS, val: &intr_stat);
270 if (intr_stat) {
271 val = FIELD_PREP_WM16(RK3576_HDMI_HPD_INT_MSK, 1);
272
273 regmap_write(map: hdmi->regmap, RK3576_IOC_MISC_CON0, val);
274 return IRQ_WAKE_THREAD;
275 }
276
277 return IRQ_NONE;
278}
279
280static irqreturn_t dw_hdmi_qp_rk3576_irq(int irq, void *dev_id)
281{
282 struct rockchip_hdmi_qp *hdmi = dev_id;
283 u32 intr_stat, val;
284
285 regmap_read(map: hdmi->regmap, RK3576_IOC_HDMI_HPD_STATUS, val: &intr_stat);
286
287 if (!intr_stat)
288 return IRQ_NONE;
289
290 val = FIELD_PREP_WM16(RK3576_HDMI_HPD_INT_CLR, 1);
291 regmap_write(map: hdmi->regmap, RK3576_IOC_MISC_CON0, val);
292 mod_delayed_work(wq: system_wq, dwork: &hdmi->hpd_work,
293 delay: msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS));
294
295 val = FIELD_PREP_WM16(RK3576_HDMI_HPD_INT_MSK, 0);
296 regmap_write(map: hdmi->regmap, RK3576_IOC_MISC_CON0, val);
297
298 return IRQ_HANDLED;
299}
300
301static irqreturn_t dw_hdmi_qp_rk3588_hardirq(int irq, void *dev_id)
302{
303 struct rockchip_hdmi_qp *hdmi = dev_id;
304 u32 intr_stat, val;
305
306 regmap_read(map: hdmi->regmap, RK3588_GRF_SOC_STATUS1, val: &intr_stat);
307
308 if (intr_stat) {
309 if (hdmi->port_id)
310 val = FIELD_PREP_WM16(RK3588_HDMI1_HPD_INT_MSK, 1);
311 else
312 val = FIELD_PREP_WM16(RK3588_HDMI0_HPD_INT_MSK, 1);
313 regmap_write(map: hdmi->regmap, RK3588_GRF_SOC_CON2, val);
314 return IRQ_WAKE_THREAD;
315 }
316
317 return IRQ_NONE;
318}
319
320static irqreturn_t dw_hdmi_qp_rk3588_irq(int irq, void *dev_id)
321{
322 struct rockchip_hdmi_qp *hdmi = dev_id;
323 u32 intr_stat, val;
324
325 regmap_read(map: hdmi->regmap, RK3588_GRF_SOC_STATUS1, val: &intr_stat);
326 if (!intr_stat)
327 return IRQ_NONE;
328
329 if (hdmi->port_id)
330 val = FIELD_PREP_WM16(RK3588_HDMI1_HPD_INT_CLR, 1);
331 else
332 val = FIELD_PREP_WM16(RK3588_HDMI0_HPD_INT_CLR, 1);
333 regmap_write(map: hdmi->regmap, RK3588_GRF_SOC_CON2, val);
334
335 mod_delayed_work(wq: system_wq, dwork: &hdmi->hpd_work,
336 delay: msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS));
337
338 if (hdmi->port_id)
339 val |= FIELD_PREP_WM16(RK3588_HDMI1_HPD_INT_MSK, 0);
340 else
341 val |= FIELD_PREP_WM16(RK3588_HDMI0_HPD_INT_MSK, 0);
342 regmap_write(map: hdmi->regmap, RK3588_GRF_SOC_CON2, val);
343
344 return IRQ_HANDLED;
345}
346
347static void dw_hdmi_qp_rk3576_io_init(struct rockchip_hdmi_qp *hdmi)
348{
349 u32 val;
350
351 val = FIELD_PREP_WM16(RK3576_SCLIN_MASK, 1) |
352 FIELD_PREP_WM16(RK3576_SDAIN_MASK, 1) |
353 FIELD_PREP_WM16(RK3576_HDMI_GRANT_SEL, 1) |
354 FIELD_PREP_WM16(RK3576_I2S_SEL_MASK, 1);
355
356 regmap_write(map: hdmi->vo_regmap, RK3576_VO0_GRF_SOC_CON14, val);
357
358 val = FIELD_PREP_WM16(RK3576_HDMI_HPD_INT_MSK, 0);
359 regmap_write(map: hdmi->regmap, RK3576_IOC_MISC_CON0, val);
360}
361
362static void dw_hdmi_qp_rk3588_io_init(struct rockchip_hdmi_qp *hdmi)
363{
364 u32 val;
365
366 val = FIELD_PREP_WM16(RK3588_SCLIN_MASK, 1) |
367 FIELD_PREP_WM16(RK3588_SDAIN_MASK, 1) |
368 FIELD_PREP_WM16(RK3588_MODE_MASK, 1) |
369 FIELD_PREP_WM16(RK3588_I2S_SEL_MASK, 1);
370 regmap_write(map: hdmi->vo_regmap,
371 reg: hdmi->port_id ? RK3588_GRF_VO1_CON6 : RK3588_GRF_VO1_CON3,
372 val);
373
374 val = FIELD_PREP_WM16(RK3588_HPD_HDMI0_IO_EN_MASK, 1) |
375 FIELD_PREP_WM16(RK3588_HPD_HDMI1_IO_EN_MASK, 1);
376 regmap_write(map: hdmi->regmap, RK3588_GRF_SOC_CON7, val);
377
378 if (hdmi->port_id)
379 val = FIELD_PREP_WM16(RK3588_HDMI1_GRANT_SEL, 1);
380 else
381 val = FIELD_PREP_WM16(RK3588_HDMI0_GRANT_SEL, 1);
382 regmap_write(map: hdmi->vo_regmap, RK3588_GRF_VO1_CON9, val);
383
384 if (hdmi->port_id)
385 val = FIELD_PREP_WM16(RK3588_HDMI1_HPD_INT_MSK, 1);
386 else
387 val = FIELD_PREP_WM16(RK3588_HDMI0_HPD_INT_MSK, 1);
388 regmap_write(map: hdmi->regmap, RK3588_GRF_SOC_CON2, val);
389}
390
391static void dw_hdmi_qp_rk3576_enc_init(struct rockchip_hdmi_qp *hdmi,
392 struct rockchip_crtc_state *state)
393{
394 u32 val;
395
396 if (state->output_bpc == 10)
397 val = FIELD_PREP_WM16(RK3576_COLOR_DEPTH_MASK, RK3576_10BPC);
398 else
399 val = FIELD_PREP_WM16(RK3576_COLOR_DEPTH_MASK, RK3576_8BPC);
400
401 regmap_write(map: hdmi->vo_regmap, RK3576_VO0_GRF_SOC_CON8, val);
402}
403
404static void dw_hdmi_qp_rk3588_enc_init(struct rockchip_hdmi_qp *hdmi,
405 struct rockchip_crtc_state *state)
406{
407 u32 val;
408
409 if (state->output_bpc == 10)
410 val = FIELD_PREP_WM16(RK3588_COLOR_DEPTH_MASK, RK3588_10BPC);
411 else
412 val = FIELD_PREP_WM16(RK3588_COLOR_DEPTH_MASK, RK3588_8BPC);
413
414 regmap_write(map: hdmi->vo_regmap,
415 reg: hdmi->port_id ? RK3588_GRF_VO1_CON6 : RK3588_GRF_VO1_CON3,
416 val);
417}
418
419static const struct rockchip_hdmi_qp_ctrl_ops rk3576_hdmi_ctrl_ops = {
420 .io_init = dw_hdmi_qp_rk3576_io_init,
421 .enc_init = dw_hdmi_qp_rk3576_enc_init,
422 .irq_callback = dw_hdmi_qp_rk3576_irq,
423 .hardirq_callback = dw_hdmi_qp_rk3576_hardirq,
424};
425
426static const struct rockchip_hdmi_qp_ctrl_ops rk3588_hdmi_ctrl_ops = {
427 .io_init = dw_hdmi_qp_rk3588_io_init,
428 .enc_init = dw_hdmi_qp_rk3588_enc_init,
429 .irq_callback = dw_hdmi_qp_rk3588_irq,
430 .hardirq_callback = dw_hdmi_qp_rk3588_hardirq,
431};
432
433struct rockchip_hdmi_qp_cfg {
434 unsigned int num_ports;
435 unsigned int port_ids[MAX_HDMI_PORT_NUM];
436 const struct rockchip_hdmi_qp_ctrl_ops *ctrl_ops;
437 const struct dw_hdmi_qp_phy_ops *phy_ops;
438};
439
440static const struct rockchip_hdmi_qp_cfg rk3576_hdmi_cfg = {
441 .num_ports = 1,
442 .port_ids = {
443 0x27da0000,
444 },
445 .ctrl_ops = &rk3576_hdmi_ctrl_ops,
446 .phy_ops = &rk3576_hdmi_phy_ops,
447};
448
449static const struct rockchip_hdmi_qp_cfg rk3588_hdmi_cfg = {
450 .num_ports = 2,
451 .port_ids = {
452 0xfde80000,
453 0xfdea0000,
454 },
455 .ctrl_ops = &rk3588_hdmi_ctrl_ops,
456 .phy_ops = &rk3588_hdmi_phy_ops,
457};
458
459static const struct of_device_id dw_hdmi_qp_rockchip_dt_ids[] = {
460 {
461 .compatible = "rockchip,rk3576-dw-hdmi-qp",
462 .data = &rk3576_hdmi_cfg
463 }, {
464 .compatible = "rockchip,rk3588-dw-hdmi-qp",
465 .data = &rk3588_hdmi_cfg
466 },
467 {},
468};
469MODULE_DEVICE_TABLE(of, dw_hdmi_qp_rockchip_dt_ids);
470
471static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master,
472 void *data)
473{
474 struct platform_device *pdev = to_platform_device(dev);
475 struct dw_hdmi_qp_plat_data plat_data = {};
476 const struct rockchip_hdmi_qp_cfg *cfg;
477 struct drm_device *drm = data;
478 struct drm_connector *connector;
479 struct drm_encoder *encoder;
480 struct rockchip_hdmi_qp *hdmi;
481 struct resource *res;
482 struct clk_bulk_data *clks;
483 struct clk *ref_clk;
484 int ret, irq, i;
485
486 if (!pdev->dev.of_node)
487 return -ENODEV;
488
489 hdmi = devm_kzalloc(dev: &pdev->dev, size: sizeof(*hdmi), GFP_KERNEL);
490 if (!hdmi)
491 return -ENOMEM;
492
493 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
494 if (!res)
495 return -ENODEV;
496
497 cfg = of_device_get_match_data(dev);
498 if (!cfg)
499 return -ENODEV;
500
501 if (!cfg->ctrl_ops || !cfg->ctrl_ops->io_init ||
502 !cfg->ctrl_ops->irq_callback || !cfg->ctrl_ops->hardirq_callback)
503 return dev_err_probe(dev, err: -ENODEV, fmt: "Missing platform ctrl ops\n");
504
505 hdmi->ctrl_ops = cfg->ctrl_ops;
506 hdmi->dev = &pdev->dev;
507 hdmi->port_id = -ENODEV;
508
509 /* Identify port ID by matching base IO address */
510 for (i = 0; i < cfg->num_ports; i++) {
511 if (res->start == cfg->port_ids[i]) {
512 hdmi->port_id = i;
513 break;
514 }
515 }
516 if (hdmi->port_id < 0)
517 return dev_err_probe(dev: hdmi->dev, err: hdmi->port_id,
518 fmt: "Failed to match HDMI port ID\n");
519
520 plat_data.phy_ops = cfg->phy_ops;
521 plat_data.phy_data = hdmi;
522 plat_data.max_bpc = 10;
523
524 encoder = &hdmi->encoder.encoder;
525 encoder->possible_crtcs = drm_of_find_possible_crtcs(dev: drm, port: dev->of_node);
526
527 rockchip_drm_encoder_set_crtc_endpoint_id(rencoder: &hdmi->encoder,
528 np: dev->of_node, port: 0, reg: 0);
529 /*
530 * If we failed to find the CRTC(s) which this encoder is
531 * supposed to be connected to, it's because the CRTC has
532 * not been registered yet. Defer probing, and hope that
533 * the required CRTC is added later.
534 */
535 if (encoder->possible_crtcs == 0)
536 return -EPROBE_DEFER;
537
538 hdmi->regmap = syscon_regmap_lookup_by_phandle(np: dev->of_node,
539 property: "rockchip,grf");
540 if (IS_ERR(ptr: hdmi->regmap))
541 return dev_err_probe(dev: hdmi->dev, err: PTR_ERR(ptr: hdmi->regmap),
542 fmt: "Unable to get rockchip,grf\n");
543
544 hdmi->vo_regmap = syscon_regmap_lookup_by_phandle(np: dev->of_node,
545 property: "rockchip,vo-grf");
546 if (IS_ERR(ptr: hdmi->vo_regmap))
547 return dev_err_probe(dev: hdmi->dev, err: PTR_ERR(ptr: hdmi->vo_regmap),
548 fmt: "Unable to get rockchip,vo-grf\n");
549
550 ret = devm_clk_bulk_get_all_enabled(dev: hdmi->dev, clks: &clks);
551 if (ret < 0)
552 return dev_err_probe(dev: hdmi->dev, err: ret, fmt: "Failed to get clocks\n");
553
554 ref_clk = clk_get(dev: hdmi->dev, id: "ref");
555 if (IS_ERR(ptr: ref_clk))
556 return dev_err_probe(dev: hdmi->dev, err: PTR_ERR(ptr: ref_clk),
557 fmt: "Failed to get ref clock\n");
558
559 plat_data.ref_clk_rate = clk_get_rate(clk: ref_clk);
560 clk_put(clk: ref_clk);
561
562 hdmi->frl_enable_gpio = devm_gpiod_get_optional(dev: hdmi->dev, con_id: "frl-enable",
563 flags: GPIOD_OUT_LOW);
564 if (IS_ERR(ptr: hdmi->frl_enable_gpio))
565 return dev_err_probe(dev: hdmi->dev, err: PTR_ERR(ptr: hdmi->frl_enable_gpio),
566 fmt: "Failed to request FRL enable GPIO\n");
567
568 hdmi->phy = devm_of_phy_get_by_index(dev, np: dev->of_node, index: 0);
569 if (IS_ERR(ptr: hdmi->phy))
570 return dev_err_probe(dev: hdmi->dev, err: PTR_ERR(ptr: hdmi->phy),
571 fmt: "Failed to get phy\n");
572
573 cfg->ctrl_ops->io_init(hdmi);
574
575 INIT_DELAYED_WORK(&hdmi->hpd_work, dw_hdmi_qp_rk3588_hpd_work);
576
577 plat_data.main_irq = platform_get_irq_byname(pdev, "main");
578 if (plat_data.main_irq < 0)
579 return plat_data.main_irq;
580
581 plat_data.cec_irq = platform_get_irq_byname(pdev, "cec");
582 if (plat_data.cec_irq < 0)
583 return plat_data.cec_irq;
584
585 irq = platform_get_irq_byname(pdev, "hpd");
586 if (irq < 0)
587 return irq;
588
589 ret = devm_request_threaded_irq(dev: hdmi->dev, irq,
590 handler: cfg->ctrl_ops->hardirq_callback,
591 thread_fn: cfg->ctrl_ops->irq_callback,
592 IRQF_SHARED, devname: "dw-hdmi-qp-hpd",
593 dev_id: hdmi);
594 if (ret)
595 return ret;
596
597 drm_encoder_helper_add(encoder, funcs: &dw_hdmi_qp_rockchip_encoder_helper_funcs);
598 drm_simple_encoder_init(dev: drm, encoder, DRM_MODE_ENCODER_TMDS);
599
600 platform_set_drvdata(pdev, data: hdmi);
601
602 hdmi->hdmi = dw_hdmi_qp_bind(pdev, encoder, plat_data: &plat_data);
603 if (IS_ERR(ptr: hdmi->hdmi)) {
604 drm_encoder_cleanup(encoder);
605 return dev_err_probe(dev: hdmi->dev, err: PTR_ERR(ptr: hdmi->hdmi),
606 fmt: "Failed to bind dw-hdmi-qp");
607 }
608
609 connector = drm_bridge_connector_init(drm, encoder);
610 if (IS_ERR(ptr: connector))
611 return dev_err_probe(dev: hdmi->dev, err: PTR_ERR(ptr: connector),
612 fmt: "Failed to init bridge connector\n");
613
614 return drm_connector_attach_encoder(connector, encoder);
615}
616
617static void dw_hdmi_qp_rockchip_unbind(struct device *dev,
618 struct device *master,
619 void *data)
620{
621 struct rockchip_hdmi_qp *hdmi = dev_get_drvdata(dev);
622
623 cancel_delayed_work_sync(dwork: &hdmi->hpd_work);
624
625 drm_encoder_cleanup(encoder: &hdmi->encoder.encoder);
626}
627
628static const struct component_ops dw_hdmi_qp_rockchip_ops = {
629 .bind = dw_hdmi_qp_rockchip_bind,
630 .unbind = dw_hdmi_qp_rockchip_unbind,
631};
632
633static int dw_hdmi_qp_rockchip_probe(struct platform_device *pdev)
634{
635 return component_add(&pdev->dev, &dw_hdmi_qp_rockchip_ops);
636}
637
638static void dw_hdmi_qp_rockchip_remove(struct platform_device *pdev)
639{
640 component_del(&pdev->dev, &dw_hdmi_qp_rockchip_ops);
641}
642
643static int __maybe_unused dw_hdmi_qp_rockchip_suspend(struct device *dev)
644{
645 struct rockchip_hdmi_qp *hdmi = dev_get_drvdata(dev);
646
647 dw_hdmi_qp_suspend(dev, hdmi: hdmi->hdmi);
648
649 return 0;
650}
651
652static int __maybe_unused dw_hdmi_qp_rockchip_resume(struct device *dev)
653{
654 struct rockchip_hdmi_qp *hdmi = dev_get_drvdata(dev);
655
656 hdmi->ctrl_ops->io_init(hdmi);
657
658 dw_hdmi_qp_resume(dev, hdmi: hdmi->hdmi);
659
660 if (hdmi->encoder.encoder.dev)
661 drm_helper_hpd_irq_event(dev: hdmi->encoder.encoder.dev);
662
663 return 0;
664}
665
666static const struct dev_pm_ops dw_hdmi_qp_rockchip_pm = {
667 SET_SYSTEM_SLEEP_PM_OPS(dw_hdmi_qp_rockchip_suspend,
668 dw_hdmi_qp_rockchip_resume)
669};
670
671struct platform_driver dw_hdmi_qp_rockchip_pltfm_driver = {
672 .probe = dw_hdmi_qp_rockchip_probe,
673 .remove = dw_hdmi_qp_rockchip_remove,
674 .driver = {
675 .name = "dwhdmiqp-rockchip",
676 .pm = &dw_hdmi_qp_rockchip_pm,
677 .of_match_table = dw_hdmi_qp_rockchip_dt_ids,
678 },
679};
680

source code of linux/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c