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