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 <[email protected]>
7  * Author: Cristian Ciocaltea <[email protected]>
8  */
9 
10 #include <linux/clk.h>
11 #include <linux/gpio/consumer.h>
12 #include <linux/mfd/syscon.h>
13 #include <linux/module.h>
14 #include <linux/platform_device.h>
15 #include <linux/phy/phy.h>
16 #include <linux/regmap.h>
17 #include <linux/workqueue.h>
18 
19 #include <drm/bridge/dw_hdmi_qp.h>
20 #include <drm/display/drm_hdmi_helper.h>
21 #include <drm/drm_bridge_connector.h>
22 #include <drm/drm_of.h>
23 #include <drm/drm_probe_helper.h>
24 #include <drm/drm_simple_kms_helper.h>
25 
26 #include "rockchip_drm_drv.h"
27 
28 #define RK3588_GRF_SOC_CON2		0x0308
29 #define RK3588_HDMI0_HPD_INT_MSK	BIT(13)
30 #define RK3588_HDMI0_HPD_INT_CLR	BIT(12)
31 #define RK3588_HDMI1_HPD_INT_MSK	BIT(15)
32 #define RK3588_HDMI1_HPD_INT_CLR	BIT(14)
33 #define RK3588_GRF_SOC_CON7		0x031c
34 #define RK3588_SET_HPD_PATH_MASK	GENMASK(13, 12)
35 #define RK3588_GRF_SOC_STATUS1		0x0384
36 #define RK3588_HDMI0_LEVEL_INT		BIT(16)
37 #define RK3588_HDMI1_LEVEL_INT		BIT(24)
38 #define RK3588_GRF_VO1_CON3		0x000c
39 #define RK3588_GRF_VO1_CON6		0x0018
40 #define RK3588_SCLIN_MASK		BIT(9)
41 #define RK3588_SDAIN_MASK		BIT(10)
42 #define RK3588_MODE_MASK		BIT(11)
43 #define RK3588_I2S_SEL_MASK		BIT(13)
44 #define RK3588_GRF_VO1_CON9		0x0024
45 #define RK3588_HDMI0_GRANT_SEL		BIT(10)
46 #define RK3588_HDMI1_GRANT_SEL		BIT(12)
47 
48 #define HIWORD_UPDATE(val, mask)	((val) | (mask) << 16)
49 #define HOTPLUG_DEBOUNCE_MS		150
50 #define MAX_HDMI_PORT_NUM		2
51 
52 struct rockchip_hdmi_qp {
53 	struct device *dev;
54 	struct regmap *regmap;
55 	struct regmap *vo_regmap;
56 	struct rockchip_encoder encoder;
57 	struct dw_hdmi_qp *hdmi;
58 	struct phy *phy;
59 	struct gpio_desc *enable_gpio;
60 	struct delayed_work hpd_work;
61 	int port_id;
62 };
63 
to_rockchip_hdmi_qp(struct drm_encoder * encoder)64 static struct rockchip_hdmi_qp *to_rockchip_hdmi_qp(struct drm_encoder *encoder)
65 {
66 	struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
67 
68 	return container_of(rkencoder, struct rockchip_hdmi_qp, encoder);
69 }
70 
dw_hdmi_qp_rockchip_encoder_enable(struct drm_encoder * encoder)71 static void dw_hdmi_qp_rockchip_encoder_enable(struct drm_encoder *encoder)
72 {
73 	struct rockchip_hdmi_qp *hdmi = to_rockchip_hdmi_qp(encoder);
74 	struct drm_crtc *crtc = encoder->crtc;
75 	unsigned long long rate;
76 
77 	/* Unconditionally switch to TMDS as FRL is not yet supported */
78 	gpiod_set_value(hdmi->enable_gpio, 1);
79 
80 	if (crtc && crtc->state) {
81 		rate = drm_hdmi_compute_mode_clock(&crtc->state->adjusted_mode,
82 						   8, HDMI_COLORSPACE_RGB);
83 		/*
84 		 * FIXME: Temporary workaround to pass pixel clock rate
85 		 * to the PHY driver until phy_configure_opts_hdmi
86 		 * becomes available in the PHY API. See also the related
87 		 * comment in rk_hdptx_phy_power_on() from
88 		 * drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
89 		 */
90 		phy_set_bus_width(hdmi->phy, div_u64(rate, 100));
91 	}
92 }
93 
94 static int
dw_hdmi_qp_rockchip_encoder_atomic_check(struct drm_encoder * encoder,struct drm_crtc_state * crtc_state,struct drm_connector_state * conn_state)95 dw_hdmi_qp_rockchip_encoder_atomic_check(struct drm_encoder *encoder,
96 					 struct drm_crtc_state *crtc_state,
97 					 struct drm_connector_state *conn_state)
98 {
99 	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
100 
101 	s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
102 	s->output_type = DRM_MODE_CONNECTOR_HDMIA;
103 
104 	return 0;
105 }
106 
107 static const struct
108 drm_encoder_helper_funcs dw_hdmi_qp_rockchip_encoder_helper_funcs = {
109 	.enable		= dw_hdmi_qp_rockchip_encoder_enable,
110 	.atomic_check	= dw_hdmi_qp_rockchip_encoder_atomic_check,
111 };
112 
dw_hdmi_qp_rk3588_phy_init(struct dw_hdmi_qp * dw_hdmi,void * data)113 static int dw_hdmi_qp_rk3588_phy_init(struct dw_hdmi_qp *dw_hdmi, void *data)
114 {
115 	struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data;
116 
117 	return phy_power_on(hdmi->phy);
118 }
119 
dw_hdmi_qp_rk3588_phy_disable(struct dw_hdmi_qp * dw_hdmi,void * data)120 static void dw_hdmi_qp_rk3588_phy_disable(struct dw_hdmi_qp *dw_hdmi,
121 					  void *data)
122 {
123 	struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data;
124 
125 	phy_power_off(hdmi->phy);
126 }
127 
128 static enum drm_connector_status
dw_hdmi_qp_rk3588_read_hpd(struct dw_hdmi_qp * dw_hdmi,void * data)129 dw_hdmi_qp_rk3588_read_hpd(struct dw_hdmi_qp *dw_hdmi, void *data)
130 {
131 	struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data;
132 	u32 val;
133 
134 	regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &val);
135 	val &= hdmi->port_id ? RK3588_HDMI1_LEVEL_INT : RK3588_HDMI0_LEVEL_INT;
136 
137 	return val ? connector_status_connected : connector_status_disconnected;
138 }
139 
dw_hdmi_qp_rk3588_setup_hpd(struct dw_hdmi_qp * dw_hdmi,void * data)140 static void dw_hdmi_qp_rk3588_setup_hpd(struct dw_hdmi_qp *dw_hdmi, void *data)
141 {
142 	struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data;
143 	u32 val;
144 
145 	if (hdmi->port_id)
146 		val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_CLR,
147 				    RK3588_HDMI1_HPD_INT_CLR | RK3588_HDMI1_HPD_INT_MSK);
148 	else
149 		val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR,
150 				    RK3588_HDMI0_HPD_INT_CLR | RK3588_HDMI0_HPD_INT_MSK);
151 
152 	regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
153 }
154 
155 static const struct dw_hdmi_qp_phy_ops rk3588_hdmi_phy_ops = {
156 	.init		= dw_hdmi_qp_rk3588_phy_init,
157 	.disable	= dw_hdmi_qp_rk3588_phy_disable,
158 	.read_hpd	= dw_hdmi_qp_rk3588_read_hpd,
159 	.setup_hpd	= dw_hdmi_qp_rk3588_setup_hpd,
160 };
161 
dw_hdmi_qp_rk3588_hpd_work(struct work_struct * work)162 static void dw_hdmi_qp_rk3588_hpd_work(struct work_struct *work)
163 {
164 	struct rockchip_hdmi_qp *hdmi = container_of(work,
165 						     struct rockchip_hdmi_qp,
166 						     hpd_work.work);
167 	struct drm_device *drm = hdmi->encoder.encoder.dev;
168 	bool changed;
169 
170 	if (drm) {
171 		changed = drm_helper_hpd_irq_event(drm);
172 		if (changed)
173 			dev_dbg(hdmi->dev, "connector status changed\n");
174 	}
175 }
176 
dw_hdmi_qp_rk3588_hardirq(int irq,void * dev_id)177 static irqreturn_t dw_hdmi_qp_rk3588_hardirq(int irq, void *dev_id)
178 {
179 	struct rockchip_hdmi_qp *hdmi = dev_id;
180 	u32 intr_stat, val;
181 
182 	regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &intr_stat);
183 
184 	if (intr_stat) {
185 		if (hdmi->port_id)
186 			val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_MSK,
187 					    RK3588_HDMI1_HPD_INT_MSK);
188 		else
189 			val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_MSK,
190 					    RK3588_HDMI0_HPD_INT_MSK);
191 		regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
192 		return IRQ_WAKE_THREAD;
193 	}
194 
195 	return IRQ_NONE;
196 }
197 
dw_hdmi_qp_rk3588_irq(int irq,void * dev_id)198 static irqreturn_t dw_hdmi_qp_rk3588_irq(int irq, void *dev_id)
199 {
200 	struct rockchip_hdmi_qp *hdmi = dev_id;
201 	u32 intr_stat, val;
202 
203 	regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &intr_stat);
204 	if (!intr_stat)
205 		return IRQ_NONE;
206 
207 	if (hdmi->port_id)
208 		val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_CLR,
209 				    RK3588_HDMI1_HPD_INT_CLR);
210 	else
211 		val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR,
212 				    RK3588_HDMI0_HPD_INT_CLR);
213 	regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
214 
215 	mod_delayed_work(system_wq, &hdmi->hpd_work,
216 			 msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS));
217 
218 	if (hdmi->port_id)
219 		val |= HIWORD_UPDATE(0, RK3588_HDMI1_HPD_INT_MSK);
220 	else
221 		val |= HIWORD_UPDATE(0, RK3588_HDMI0_HPD_INT_MSK);
222 	regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
223 
224 	return IRQ_HANDLED;
225 }
226 
227 struct rockchip_hdmi_qp_cfg {
228 	unsigned int num_ports;
229 	unsigned int port_ids[MAX_HDMI_PORT_NUM];
230 	const struct dw_hdmi_qp_phy_ops *phy_ops;
231 };
232 
233 static const struct rockchip_hdmi_qp_cfg rk3588_hdmi_cfg = {
234 	.num_ports = 2,
235 	.port_ids = {
236 		0xfde80000,
237 		0xfdea0000,
238 	},
239 	.phy_ops = &rk3588_hdmi_phy_ops,
240 };
241 
242 static const struct of_device_id dw_hdmi_qp_rockchip_dt_ids[] = {
243 	{ .compatible = "rockchip,rk3588-dw-hdmi-qp",
244 	  .data = &rk3588_hdmi_cfg },
245 	{},
246 };
247 MODULE_DEVICE_TABLE(of, dw_hdmi_qp_rockchip_dt_ids);
248 
dw_hdmi_qp_rockchip_bind(struct device * dev,struct device * master,void * data)249 static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master,
250 				    void *data)
251 {
252 	struct platform_device *pdev = to_platform_device(dev);
253 	const struct rockchip_hdmi_qp_cfg *cfg;
254 	struct dw_hdmi_qp_plat_data plat_data;
255 	struct drm_device *drm = data;
256 	struct drm_connector *connector;
257 	struct drm_encoder *encoder;
258 	struct rockchip_hdmi_qp *hdmi;
259 	struct resource *res;
260 	struct clk_bulk_data *clks;
261 	int ret, irq, i;
262 	u32 val;
263 
264 	if (!pdev->dev.of_node)
265 		return -ENODEV;
266 
267 	hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
268 	if (!hdmi)
269 		return -ENOMEM;
270 
271 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
272 	if (!res)
273 		return -ENODEV;
274 
275 	cfg = of_device_get_match_data(dev);
276 	if (!cfg)
277 		return -ENODEV;
278 
279 	hdmi->dev = &pdev->dev;
280 	hdmi->port_id = -ENODEV;
281 
282 	/* Identify port ID by matching base IO address */
283 	for (i = 0; i < cfg->num_ports; i++) {
284 		if (res->start == cfg->port_ids[i]) {
285 			hdmi->port_id = i;
286 			break;
287 		}
288 	}
289 	if (hdmi->port_id < 0) {
290 		dev_err(hdmi->dev, "Failed to match HDMI port ID\n");
291 		return hdmi->port_id;
292 	}
293 
294 	plat_data.phy_ops = cfg->phy_ops;
295 	plat_data.phy_data = hdmi;
296 
297 	encoder = &hdmi->encoder.encoder;
298 	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
299 
300 	rockchip_drm_encoder_set_crtc_endpoint_id(&hdmi->encoder,
301 						  dev->of_node, 0, 0);
302 	/*
303 	 * If we failed to find the CRTC(s) which this encoder is
304 	 * supposed to be connected to, it's because the CRTC has
305 	 * not been registered yet.  Defer probing, and hope that
306 	 * the required CRTC is added later.
307 	 */
308 	if (encoder->possible_crtcs == 0)
309 		return -EPROBE_DEFER;
310 
311 	hdmi->regmap = syscon_regmap_lookup_by_phandle(dev->of_node,
312 						       "rockchip,grf");
313 	if (IS_ERR(hdmi->regmap)) {
314 		dev_err(hdmi->dev, "Unable to get rockchip,grf\n");
315 		return PTR_ERR(hdmi->regmap);
316 	}
317 
318 	hdmi->vo_regmap = syscon_regmap_lookup_by_phandle(dev->of_node,
319 							  "rockchip,vo-grf");
320 	if (IS_ERR(hdmi->vo_regmap)) {
321 		dev_err(hdmi->dev, "Unable to get rockchip,vo-grf\n");
322 		return PTR_ERR(hdmi->vo_regmap);
323 	}
324 
325 	ret = devm_clk_bulk_get_all_enabled(hdmi->dev, &clks);
326 	if (ret < 0) {
327 		dev_err(hdmi->dev, "Failed to get clocks: %d\n", ret);
328 		return ret;
329 	}
330 
331 	hdmi->enable_gpio = devm_gpiod_get_optional(hdmi->dev, "enable",
332 						    GPIOD_OUT_HIGH);
333 	if (IS_ERR(hdmi->enable_gpio)) {
334 		ret = PTR_ERR(hdmi->enable_gpio);
335 		dev_err(hdmi->dev, "Failed to request enable GPIO: %d\n", ret);
336 		return ret;
337 	}
338 
339 	hdmi->phy = devm_of_phy_get_by_index(dev, dev->of_node, 0);
340 	if (IS_ERR(hdmi->phy)) {
341 		ret = PTR_ERR(hdmi->phy);
342 		if (ret != -EPROBE_DEFER)
343 			dev_err(hdmi->dev, "failed to get phy: %d\n", ret);
344 		return ret;
345 	}
346 
347 	val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) |
348 	      HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) |
349 	      HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) |
350 	      HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK);
351 	regmap_write(hdmi->vo_regmap,
352 		     hdmi->port_id ? RK3588_GRF_VO1_CON6 : RK3588_GRF_VO1_CON3,
353 		     val);
354 
355 	val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK,
356 			    RK3588_SET_HPD_PATH_MASK);
357 	regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val);
358 
359 	if (hdmi->port_id)
360 		val = HIWORD_UPDATE(RK3588_HDMI1_GRANT_SEL,
361 				    RK3588_HDMI1_GRANT_SEL);
362 	else
363 		val = HIWORD_UPDATE(RK3588_HDMI0_GRANT_SEL,
364 				    RK3588_HDMI0_GRANT_SEL);
365 	regmap_write(hdmi->vo_regmap, RK3588_GRF_VO1_CON9, val);
366 
367 	if (hdmi->port_id)
368 		val = HIWORD_UPDATE(RK3588_HDMI1_HPD_INT_MSK, RK3588_HDMI1_HPD_INT_MSK);
369 	else
370 		val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_MSK, RK3588_HDMI0_HPD_INT_MSK);
371 	regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
372 
373 	INIT_DELAYED_WORK(&hdmi->hpd_work, dw_hdmi_qp_rk3588_hpd_work);
374 
375 	plat_data.main_irq = platform_get_irq_byname(pdev, "main");
376 	if (plat_data.main_irq < 0)
377 		return plat_data.main_irq;
378 
379 	irq = platform_get_irq_byname(pdev, "hpd");
380 	if (irq < 0)
381 		return irq;
382 
383 	ret = devm_request_threaded_irq(hdmi->dev, irq,
384 					dw_hdmi_qp_rk3588_hardirq,
385 					dw_hdmi_qp_rk3588_irq,
386 					IRQF_SHARED, "dw-hdmi-qp-hpd",
387 					hdmi);
388 	if (ret)
389 		return ret;
390 
391 	drm_encoder_helper_add(encoder, &dw_hdmi_qp_rockchip_encoder_helper_funcs);
392 	drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
393 
394 	platform_set_drvdata(pdev, hdmi);
395 
396 	hdmi->hdmi = dw_hdmi_qp_bind(pdev, encoder, &plat_data);
397 	if (IS_ERR(hdmi->hdmi)) {
398 		ret = PTR_ERR(hdmi->hdmi);
399 		drm_encoder_cleanup(encoder);
400 		return ret;
401 	}
402 
403 	connector = drm_bridge_connector_init(drm, encoder);
404 	if (IS_ERR(connector)) {
405 		ret = PTR_ERR(connector);
406 		dev_err(hdmi->dev, "failed to init bridge connector: %d\n", ret);
407 		return ret;
408 	}
409 
410 	return drm_connector_attach_encoder(connector, encoder);
411 }
412 
dw_hdmi_qp_rockchip_unbind(struct device * dev,struct device * master,void * data)413 static void dw_hdmi_qp_rockchip_unbind(struct device *dev,
414 				       struct device *master,
415 				       void *data)
416 {
417 	struct rockchip_hdmi_qp *hdmi = dev_get_drvdata(dev);
418 
419 	cancel_delayed_work_sync(&hdmi->hpd_work);
420 
421 	drm_encoder_cleanup(&hdmi->encoder.encoder);
422 }
423 
424 static const struct component_ops dw_hdmi_qp_rockchip_ops = {
425 	.bind	= dw_hdmi_qp_rockchip_bind,
426 	.unbind	= dw_hdmi_qp_rockchip_unbind,
427 };
428 
dw_hdmi_qp_rockchip_probe(struct platform_device * pdev)429 static int dw_hdmi_qp_rockchip_probe(struct platform_device *pdev)
430 {
431 	return component_add(&pdev->dev, &dw_hdmi_qp_rockchip_ops);
432 }
433 
dw_hdmi_qp_rockchip_remove(struct platform_device * pdev)434 static void dw_hdmi_qp_rockchip_remove(struct platform_device *pdev)
435 {
436 	component_del(&pdev->dev, &dw_hdmi_qp_rockchip_ops);
437 }
438 
dw_hdmi_qp_rockchip_resume(struct device * dev)439 static int __maybe_unused dw_hdmi_qp_rockchip_resume(struct device *dev)
440 {
441 	struct rockchip_hdmi_qp *hdmi = dev_get_drvdata(dev);
442 	u32 val;
443 
444 	val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) |
445 	      HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) |
446 	      HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) |
447 	      HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK);
448 	regmap_write(hdmi->vo_regmap,
449 		     hdmi->port_id ? RK3588_GRF_VO1_CON6 : RK3588_GRF_VO1_CON3,
450 		     val);
451 
452 	val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK,
453 			    RK3588_SET_HPD_PATH_MASK);
454 	regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val);
455 
456 	if (hdmi->port_id)
457 		val = HIWORD_UPDATE(RK3588_HDMI1_GRANT_SEL,
458 				    RK3588_HDMI1_GRANT_SEL);
459 	else
460 		val = HIWORD_UPDATE(RK3588_HDMI0_GRANT_SEL,
461 				    RK3588_HDMI0_GRANT_SEL);
462 	regmap_write(hdmi->vo_regmap, RK3588_GRF_VO1_CON9, val);
463 
464 	dw_hdmi_qp_resume(dev, hdmi->hdmi);
465 
466 	if (hdmi->encoder.encoder.dev)
467 		drm_helper_hpd_irq_event(hdmi->encoder.encoder.dev);
468 
469 	return 0;
470 }
471 
472 static const struct dev_pm_ops dw_hdmi_qp_rockchip_pm = {
473 	SET_SYSTEM_SLEEP_PM_OPS(NULL, dw_hdmi_qp_rockchip_resume)
474 };
475 
476 struct platform_driver dw_hdmi_qp_rockchip_pltfm_driver = {
477 	.probe = dw_hdmi_qp_rockchip_probe,
478 	.remove = dw_hdmi_qp_rockchip_remove,
479 	.driver = {
480 		.name = "dwhdmiqp-rockchip",
481 		.pm = &dw_hdmi_qp_rockchip_pm,
482 		.of_match_table = dw_hdmi_qp_rockchip_dt_ids,
483 	},
484 };
485