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 46 struct 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 58 static 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 65 static 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 89 static int 90 dw_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 102 static const struct 103 drm_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 108 static 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 115 static 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 123 static enum drm_connector_status 124 dw_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 135 static 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 146 static 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 153 static 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 168 static 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 185 static 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 207 static 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 }; 212 MODULE_DEVICE_TABLE(of, dw_hdmi_qp_rockchip_dt_ids); 213 214 static 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 359 static 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 370 static 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 375 static int dw_hdmi_qp_rockchip_probe(struct platform_device *pdev) 376 { 377 return component_add(&pdev->dev, &dw_hdmi_qp_rockchip_ops); 378 } 379 380 static void dw_hdmi_qp_rockchip_remove(struct platform_device *pdev) 381 { 382 component_del(&pdev->dev, &dw_hdmi_qp_rockchip_ops); 383 } 384 385 static 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 412 static const struct dev_pm_ops dw_hdmi_qp_rockchip_pm = { 413 SET_SYSTEM_SLEEP_PM_OPS(NULL, dw_hdmi_qp_rockchip_resume) 414 }; 415 416 struct 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 }; 425