rk3588: edge: Add new v10 patch for basic HDMI out support

Source: https://lore.kernel.org/all/20241016-b4-rk3588-bridge-upstream-v10-0-87ef92a6d14e@collabora.com/^

-----------------------------

**Quote from source:**

Subject: [PATCH v10 0/3] Add initial support for the Rockchip RK3588 HDMI TX Controller
Date: Wed, 16 Oct 2024 23:06:50 +0300

The Rockchip RK3588 SoC family integrates the Synopsys DesignWare HDMI
2.1 Quad-Pixel (QP) TX controller, which is a new IP block, quite
different from those used in the previous generations of Rockchip SoCs.

The controller supports the following features, among others:

* Fixed Rate Link (FRL)
* Display Stream Compression (DSC)
* 4K@120Hz and 8K@60Hz video modes
* Variable Refresh Rate (VRR) including Quick Media Switching (QMS)
* Fast Vactive (FVA)
* SCDC I2C DDC access
* Multi-stream audio
* Enhanced Audio Return Channel (EARC)

This is the last component that needs to be supported in order to enable
the HDMI output functionality on the RK3588 based SBCs, such as the
RADXA Rock 5B.  The other components are the Video Output Processor
(VOP2) and the Samsung IP based HDMI/eDP TX Combo PHY, for which basic
support has been already made available via [1] and [2], respectively.

Please note this is a reworked version of the original series, which
relied on a commonized dw-hdmi approach.  Since the general consensus
was to handle it as an entirely new IP, I dropped all patches related to
the old dw-hdmi and Rockchip glue code - a few of them might still make
sense as general improvements and will be submitted separately.

It's worth mentioning the HDMI output support is currently limited to
RGB output up to 4K@60Hz, without audio, CEC or any of the HDMI 2.1
specific features.  Moreover, the VOP2 driver is not able to properly
handle all display modes supported by the connected screens, e.g. it
doesn't cope with non-integer refresh rates.

A possible workaround consists of enabling the display controller to
make use of the clock provided by the HDMI PHY PLL.  This is still work
in progress and will be submitted later, as well as the required DTS
updates.

To facilitate testing and experimentation, all HDMI output related
patches, including those part of this series, are available at [3].

So far I could only verify this on the RADXA Rock 5B board.

Thanks,
Cristian

[1]: 5a028e8f062f ("drm/rockchip: vop2: Add support for rk3588")
[2]: 553be2830c5f ("phy: rockchip: Add Samsung HDMI/eDP Combo PHY driver")
[3]: https://gitlab.collabora.com/hardware-enablement/rockchip-3588/linux/-/commits/rk3588-hdmi-bridge-v6.12-rc2
[4]: https://lore.kernel.org/lkml/20240801-dw-hdmi-qp-tx-v1-0-148f542de5fd@collabora.com/

Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
This commit is contained in:
ColorfulRhino 2024-10-18 14:01:35 +02:00 committed by Igor
parent 870eacd2fa
commit 43629768b7
3 changed files with 2338 additions and 0 deletions

View File

@ -0,0 +1,215 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
Date: Wed, 16 Oct 2024 23:06:52 +0300
Subject: dt-bindings: display: rockchip: Add schema for RK3588 HDMI TX
Controller
Rockchip RK3588 SoC integrates the Synopsys DesignWare HDMI 2.1
Quad-Pixel (QP) TX controller IP.
Since this is a new IP block, quite different from those used in the
previous generations of Rockchip SoCs, add a dedicated binding file.
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
Documentation/devicetree/bindings/display/rockchip/rockchip,rk3588-dw-hdmi-qp.yaml | 188 ++++++++++
1 file changed, 188 insertions(+)
diff --git a/Documentation/devicetree/bindings/display/rockchip/rockchip,rk3588-dw-hdmi-qp.yaml b/Documentation/devicetree/bindings/display/rockchip/rockchip,rk3588-dw-hdmi-qp.yaml
new file mode 100644
index 000000000000..111111111111
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/rockchip/rockchip,rk3588-dw-hdmi-qp.yaml
@@ -0,0 +1,188 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/display/rockchip/rockchip,rk3588-dw-hdmi-qp.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Rockchip DW HDMI QP TX Encoder
+
+maintainers:
+ - Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
+
+description: |
+ Rockchip RK3588 SoC integrates the Synopsys DesignWare HDMI QP TX controller
+ IP and a HDMI/eDP TX Combo PHY based on a Samsung IP block, providing the
+ following features, among others:
+
+ * Fixed Rate Link (FRL)
+ * Display Stream Compression (DSC)
+ * 4K@120Hz and 8K@60Hz video modes
+ * Variable Refresh Rate (VRR) including Quick Media Switching (QMS)
+ * Fast Vactive (FVA)
+ * SCDC I2C DDC access
+ * Multi-stream audio
+ * Enhanced Audio Return Channel (EARC)
+
+allOf:
+ - $ref: /schemas/sound/dai-common.yaml#
+
+properties:
+ compatible:
+ enum:
+ - rockchip,rk3588-dw-hdmi-qp
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: Peripheral/APB bus clock
+ - description: EARC RX biphase clock
+ - description: Reference clock
+ - description: Audio interface clock
+ - description: TMDS/FRL link clock
+ - description: Video datapath clock
+
+ clock-names:
+ items:
+ - const: pclk
+ - const: earc
+ - const: ref
+ - const: aud
+ - const: hdp
+ - const: hclk_vo1
+
+ interrupts:
+ items:
+ - description: AVP Unit interrupt
+ - description: CEC interrupt
+ - description: eARC RX interrupt
+ - description: Main Unit interrupt
+ - description: HPD interrupt
+
+ interrupt-names:
+ items:
+ - const: avp
+ - const: cec
+ - const: earc
+ - const: main
+ - const: hpd
+
+ phys:
+ maxItems: 1
+ description: The HDMI/eDP PHY
+
+ ports:
+ $ref: /schemas/graph.yaml#/properties/ports
+
+ properties:
+ port@0:
+ $ref: /schemas/graph.yaml#/properties/port
+ description: Video port for RGB/YUV input.
+
+ port@1:
+ $ref: /schemas/graph.yaml#/properties/port
+ description: Video port for HDMI/eDP output.
+
+ required:
+ - port@0
+ - port@1
+
+ power-domains:
+ maxItems: 1
+
+ resets:
+ maxItems: 2
+
+ reset-names:
+ items:
+ - const: ref
+ - const: hdp
+
+ "#sound-dai-cells":
+ const: 0
+
+ rockchip,grf:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description:
+ Some HDMI QP related data is accessed through SYS GRF regs.
+
+ rockchip,vo-grf:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description:
+ Additional HDMI QP related data is accessed through VO GRF regs.
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+ - interrupts
+ - interrupt-names
+ - phys
+ - ports
+ - resets
+ - reset-names
+ - rockchip,grf
+ - rockchip,vo-grf
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/clock/rockchip,rk3588-cru.h>
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+ #include <dt-bindings/power/rk3588-power.h>
+ #include <dt-bindings/reset/rockchip,rk3588-cru.h>
+
+ soc {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ hdmi@fde80000 {
+ compatible = "rockchip,rk3588-dw-hdmi-qp";
+ reg = <0x0 0xfde80000 0x0 0x20000>;
+ clocks = <&cru PCLK_HDMITX0>,
+ <&cru CLK_HDMITX0_EARC>,
+ <&cru CLK_HDMITX0_REF>,
+ <&cru MCLK_I2S5_8CH_TX>,
+ <&cru CLK_HDMIHDP0>,
+ <&cru HCLK_VO1>;
+ clock-names = "pclk", "earc", "ref", "aud", "hdp", "hclk_vo1";
+ interrupts = <GIC_SPI 169 IRQ_TYPE_LEVEL_HIGH 0>,
+ <GIC_SPI 170 IRQ_TYPE_LEVEL_HIGH 0>,
+ <GIC_SPI 171 IRQ_TYPE_LEVEL_HIGH 0>,
+ <GIC_SPI 172 IRQ_TYPE_LEVEL_HIGH 0>,
+ <GIC_SPI 360 IRQ_TYPE_LEVEL_HIGH 0>;
+ interrupt-names = "avp", "cec", "earc", "main", "hpd";
+ phys = <&hdptxphy_hdmi0>;
+ power-domains = <&power RK3588_PD_VO1>;
+ resets = <&cru SRST_HDMITX0_REF>, <&cru SRST_HDMIHDP0>;
+ reset-names = "ref", "hdp";
+ rockchip,grf = <&sys_grf>;
+ rockchip,vo-grf = <&vo1_grf>;
+ #sound-dai-cells = <0>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+
+ hdmi0_in_vp0: endpoint {
+ remote-endpoint = <&vp0_out_hdmi0>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+
+ hdmi0_out_con0: endpoint {
+ remote-endpoint = <&hdmi_con0_in>;
+ };
+ };
+ };
+ };
+ };
--
Armbian

View File

@ -0,0 +1,522 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
Date: Wed, 16 Oct 2024 23:06:53 +0300
Subject: drm/rockchip: Add basic RK3588 HDMI output support
The RK3588 SoC family integrates the newer Synopsys DesignWare HDMI 2.1
Quad-Pixel (QP) TX controller IP and a HDMI/eDP TX Combo PHY based on a
Samsung IP block.
Add just the basic support for now, i.e. RGB output up to 4K@60Hz,
without audio, CEC or any of the HDMI 2.1 specific features.
Co-developed-by: Algea Cao <algea.cao@rock-chips.com>
Signed-off-by: Algea Cao <algea.cao@rock-chips.com>
Tested-by: Heiko Stuebner <heiko@sntech.de>
Reviewed-by: Maxime Ripard <mripard@kernel.org>
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/rockchip/Kconfig | 9 +
drivers/gpu/drm/rockchip/Makefile | 1 +
drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c | 424 ++++++++++
drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 2 +
drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 1 +
5 files changed, 437 insertions(+)
diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index 111111111111..222222222222 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -8,6 +8,7 @@ config DRM_ROCKCHIP
select VIDEOMODE_HELPERS
select DRM_ANALOGIX_DP if ROCKCHIP_ANALOGIX_DP
select DRM_DW_HDMI if ROCKCHIP_DW_HDMI
+ select DRM_DW_HDMI_QP if ROCKCHIP_DW_HDMI_QP
select DRM_DW_MIPI_DSI if ROCKCHIP_DW_MIPI_DSI
select GENERIC_PHY if ROCKCHIP_DW_MIPI_DSI
select GENERIC_PHY_MIPI_DPHY if ROCKCHIP_DW_MIPI_DSI
@@ -63,6 +64,14 @@ config ROCKCHIP_DW_HDMI
enable HDMI on RK3288 or RK3399 based SoC, you should select
this option.
+config ROCKCHIP_DW_HDMI_QP
+ bool "Rockchip specific extensions for Synopsys DW HDMI QP"
+ select DRM_BRIDGE_CONNECTOR
+ help
+ This selects support for Rockchip SoC specific extensions
+ for the Synopsys DesignWare HDMI QP driver. If you want to
+ enable HDMI on RK3588 based SoC, you should select this option.
+
config ROCKCHIP_DW_MIPI_DSI
bool "Rockchip specific extensions for Synopsys DW MIPI DSI"
select GENERIC_PHY_MIPI_DPHY
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index 111111111111..222222222222 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -11,6 +11,7 @@ rockchipdrm-$(CONFIG_ROCKCHIP_VOP) += rockchip_drm_vop.o rockchip_vop_reg.o
rockchipdrm-$(CONFIG_ROCKCHIP_ANALOGIX_DP) += analogix_dp-rockchip.o
rockchipdrm-$(CONFIG_ROCKCHIP_CDN_DP) += cdn-dp-core.o cdn-dp-reg.o
rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI) += dw_hdmi-rockchip.o
+rockchipdrm-$(CONFIG_ROCKCHIP_DW_HDMI_QP) += dw_hdmi_qp-rockchip.o
rockchipdrm-$(CONFIG_ROCKCHIP_DW_MIPI_DSI) += dw-mipi-dsi-rockchip.o
rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
rockchipdrm-$(CONFIG_ROCKCHIP_LVDS) += rockchip_lvds.o
diff --git a/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c
new file mode 100644
index 000000000000..111111111111
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/dw_hdmi_qp-rockchip.c
@@ -0,0 +1,424 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2021-2022 Rockchip Electronics Co., Ltd.
+ * Copyright (c) 2024 Collabora Ltd.
+ *
+ * Author: Algea Cao <algea.cao@rock-chips.com>
+ * Author: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/gpio/consumer.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+#include <linux/workqueue.h>
+
+#include <drm/bridge/dw_hdmi_qp.h>
+#include <drm/display/drm_hdmi_helper.h>
+#include <drm/drm_bridge_connector.h>
+#include <drm/drm_of.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_simple_kms_helper.h>
+
+#include "rockchip_drm_drv.h"
+
+#define RK3588_GRF_SOC_CON2 0x0308
+#define RK3588_HDMI0_HPD_INT_MSK BIT(13)
+#define RK3588_HDMI0_HPD_INT_CLR BIT(12)
+#define RK3588_GRF_SOC_CON7 0x031c
+#define RK3588_SET_HPD_PATH_MASK GENMASK(13, 12)
+#define RK3588_GRF_SOC_STATUS1 0x0384
+#define RK3588_HDMI0_LEVEL_INT BIT(16)
+#define RK3588_GRF_VO1_CON3 0x000c
+#define RK3588_SCLIN_MASK BIT(9)
+#define RK3588_SDAIN_MASK BIT(10)
+#define RK3588_MODE_MASK BIT(11)
+#define RK3588_I2S_SEL_MASK BIT(13)
+#define RK3588_GRF_VO1_CON9 0x0024
+#define RK3588_HDMI0_GRANT_SEL BIT(10)
+
+#define HIWORD_UPDATE(val, mask) ((val) | (mask) << 16)
+#define HOTPLUG_DEBOUNCE_MS 150
+
+struct rockchip_hdmi_qp {
+ struct device *dev;
+ struct regmap *regmap;
+ struct regmap *vo_regmap;
+ struct rockchip_encoder encoder;
+ struct clk *ref_clk;
+ struct dw_hdmi_qp *hdmi;
+ struct phy *phy;
+ struct gpio_desc *enable_gpio;
+ struct delayed_work hpd_work;
+};
+
+static struct rockchip_hdmi_qp *to_rockchip_hdmi_qp(struct drm_encoder *encoder)
+{
+ struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
+
+ return container_of(rkencoder, struct rockchip_hdmi_qp, encoder);
+}
+
+static void dw_hdmi_qp_rockchip_encoder_enable(struct drm_encoder *encoder)
+{
+ struct rockchip_hdmi_qp *hdmi = to_rockchip_hdmi_qp(encoder);
+ struct drm_crtc *crtc = encoder->crtc;
+ unsigned long long rate;
+
+ /* Unconditionally switch to TMDS as FRL is not yet supported */
+ gpiod_set_value(hdmi->enable_gpio, 1);
+
+ if (crtc && crtc->state) {
+ rate = drm_hdmi_compute_mode_clock(&crtc->state->adjusted_mode,
+ 8, HDMI_COLORSPACE_RGB);
+ clk_set_rate(hdmi->ref_clk, rate);
+ /*
+ * FIXME: Temporary workaround to pass pixel clock rate
+ * to the PHY driver until phy_configure_opts_hdmi
+ * becomes available in the PHY API. See also the related
+ * comment in rk_hdptx_phy_power_on() from
+ * drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
+ */
+ phy_set_bus_width(hdmi->phy, rate / 100);
+ }
+}
+
+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)
+{
+ struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
+
+ s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
+ s->output_type = DRM_MODE_CONNECTOR_HDMIA;
+
+ return 0;
+}
+
+static const struct
+drm_encoder_helper_funcs dw_hdmi_qp_rockchip_encoder_helper_funcs = {
+ .enable = dw_hdmi_qp_rockchip_encoder_enable,
+ .atomic_check = dw_hdmi_qp_rockchip_encoder_atomic_check,
+};
+
+static int dw_hdmi_qp_rk3588_phy_init(struct dw_hdmi_qp *dw_hdmi, void *data)
+{
+ struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data;
+
+ return phy_power_on(hdmi->phy);
+}
+
+static void dw_hdmi_qp_rk3588_phy_disable(struct dw_hdmi_qp *dw_hdmi,
+ void *data)
+{
+ struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data;
+
+ phy_power_off(hdmi->phy);
+}
+
+static enum drm_connector_status
+dw_hdmi_qp_rk3588_read_hpd(struct dw_hdmi_qp *dw_hdmi, void *data)
+{
+ struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data;
+ u32 val;
+
+ regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &val);
+
+ return val & RK3588_HDMI0_LEVEL_INT ?
+ connector_status_connected : connector_status_disconnected;
+}
+
+static void dw_hdmi_qp_rk3588_setup_hpd(struct dw_hdmi_qp *dw_hdmi, void *data)
+{
+ struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data;
+
+ regmap_write(hdmi->regmap,
+ RK3588_GRF_SOC_CON2,
+ HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR,
+ RK3588_HDMI0_HPD_INT_CLR |
+ RK3588_HDMI0_HPD_INT_MSK));
+}
+
+static const struct dw_hdmi_qp_phy_ops rk3588_hdmi_phy_ops = {
+ .init = dw_hdmi_qp_rk3588_phy_init,
+ .disable = dw_hdmi_qp_rk3588_phy_disable,
+ .read_hpd = dw_hdmi_qp_rk3588_read_hpd,
+ .setup_hpd = dw_hdmi_qp_rk3588_setup_hpd,
+};
+
+static void dw_hdmi_qp_rk3588_hpd_work(struct work_struct *work)
+{
+ struct rockchip_hdmi_qp *hdmi = container_of(work,
+ struct rockchip_hdmi_qp,
+ hpd_work.work);
+ struct drm_device *drm = hdmi->encoder.encoder.dev;
+ bool changed;
+
+ if (drm) {
+ changed = drm_helper_hpd_irq_event(drm);
+ if (changed)
+ drm_dbg(hdmi, "connector status changed\n");
+ }
+}
+
+static irqreturn_t dw_hdmi_qp_rk3588_hardirq(int irq, void *dev_id)
+{
+ struct rockchip_hdmi_qp *hdmi = dev_id;
+ u32 intr_stat, val;
+
+ regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &intr_stat);
+
+ if (intr_stat) {
+ val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_MSK,
+ RK3588_HDMI0_HPD_INT_MSK);
+ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
+ return IRQ_WAKE_THREAD;
+ }
+
+ return IRQ_NONE;
+}
+
+static irqreturn_t dw_hdmi_qp_rk3588_irq(int irq, void *dev_id)
+{
+ struct rockchip_hdmi_qp *hdmi = dev_id;
+ u32 intr_stat, val;
+
+ regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &intr_stat);
+ if (!intr_stat)
+ return IRQ_NONE;
+
+ val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR,
+ RK3588_HDMI0_HPD_INT_CLR);
+ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
+
+ mod_delayed_work(system_wq, &hdmi->hpd_work,
+ msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS));
+
+ val |= HIWORD_UPDATE(0, RK3588_HDMI0_HPD_INT_MSK);
+ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
+
+ return IRQ_HANDLED;
+}
+
+static const struct of_device_id dw_hdmi_qp_rockchip_dt_ids[] = {
+ { .compatible = "rockchip,rk3588-dw-hdmi-qp",
+ .data = &rk3588_hdmi_phy_ops },
+ {},
+};
+MODULE_DEVICE_TABLE(of, dw_hdmi_qp_rockchip_dt_ids);
+
+static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master,
+ void *data)
+{
+ static const char * const clk_names[] = {
+ "pclk", "earc", "aud", "hdp", "hclk_vo1",
+ "ref" /* keep "ref" last */
+ };
+ struct platform_device *pdev = to_platform_device(dev);
+ struct dw_hdmi_qp_plat_data plat_data;
+ struct drm_device *drm = data;
+ struct drm_connector *connector;
+ struct drm_encoder *encoder;
+ struct rockchip_hdmi_qp *hdmi;
+ struct clk *clk;
+ int ret, irq, i;
+ u32 val;
+
+ if (!pdev->dev.of_node)
+ return -ENODEV;
+
+ hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
+ if (!hdmi)
+ return -ENOMEM;
+
+ plat_data.phy_ops = of_device_get_match_data(dev);
+ if (!plat_data.phy_ops)
+ return -ENODEV;
+
+ plat_data.phy_data = hdmi;
+ hdmi->dev = &pdev->dev;
+
+ encoder = &hdmi->encoder.encoder;
+ encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
+
+ rockchip_drm_encoder_set_crtc_endpoint_id(&hdmi->encoder,
+ dev->of_node, 0, 0);
+ /*
+ * If we failed to find the CRTC(s) which this encoder is
+ * supposed to be connected to, it's because the CRTC has
+ * not been registered yet. Defer probing, and hope that
+ * the required CRTC is added later.
+ */
+ if (encoder->possible_crtcs == 0)
+ return -EPROBE_DEFER;
+
+ hdmi->regmap = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "rockchip,grf");
+ if (IS_ERR(hdmi->regmap)) {
+ drm_err(hdmi, "Unable to get rockchip,grf\n");
+ return PTR_ERR(hdmi->regmap);
+ }
+
+ hdmi->vo_regmap = syscon_regmap_lookup_by_phandle(dev->of_node,
+ "rockchip,vo-grf");
+ if (IS_ERR(hdmi->vo_regmap)) {
+ drm_err(hdmi, "Unable to get rockchip,vo-grf\n");
+ return PTR_ERR(hdmi->vo_regmap);
+ }
+
+ for (i = 0; i < ARRAY_SIZE(clk_names); i++) {
+ clk = devm_clk_get_enabled(hdmi->dev, clk_names[i]);
+
+ if (IS_ERR(clk)) {
+ ret = PTR_ERR(clk);
+ if (ret != -EPROBE_DEFER)
+ drm_err(hdmi, "Failed to get %s clock: %d\n",
+ clk_names[i], ret);
+ return ret;
+ }
+ }
+ hdmi->ref_clk = clk;
+
+ hdmi->enable_gpio = devm_gpiod_get_optional(hdmi->dev, "enable",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(hdmi->enable_gpio)) {
+ ret = PTR_ERR(hdmi->enable_gpio);
+ drm_err(hdmi, "Failed to request enable GPIO: %d\n", ret);
+ return ret;
+ }
+
+ hdmi->phy = devm_of_phy_get_by_index(dev, dev->of_node, 0);
+ if (IS_ERR(hdmi->phy)) {
+ ret = PTR_ERR(hdmi->phy);
+ if (ret != -EPROBE_DEFER)
+ drm_err(hdmi, "failed to get phy: %d\n", ret);
+ return ret;
+ }
+
+ val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) |
+ HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) |
+ HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) |
+ HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK);
+ regmap_write(hdmi->vo_regmap, RK3588_GRF_VO1_CON3, val);
+
+ val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK,
+ RK3588_SET_HPD_PATH_MASK);
+ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val);
+
+ val = HIWORD_UPDATE(RK3588_HDMI0_GRANT_SEL,
+ RK3588_HDMI0_GRANT_SEL);
+ regmap_write(hdmi->vo_regmap, RK3588_GRF_VO1_CON9, val);
+
+ val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_MSK, RK3588_HDMI0_HPD_INT_MSK);
+ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
+
+ INIT_DELAYED_WORK(&hdmi->hpd_work, dw_hdmi_qp_rk3588_hpd_work);
+
+ plat_data.main_irq = platform_get_irq_byname(pdev, "main");
+ if (plat_data.main_irq < 0)
+ return plat_data.main_irq;
+
+ irq = platform_get_irq_byname(pdev, "hpd");
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_threaded_irq(hdmi->dev, irq,
+ dw_hdmi_qp_rk3588_hardirq,
+ dw_hdmi_qp_rk3588_irq,
+ IRQF_SHARED, "dw-hdmi-qp-hpd",
+ hdmi);
+ if (ret)
+ return ret;
+
+ drm_encoder_helper_add(encoder, &dw_hdmi_qp_rockchip_encoder_helper_funcs);
+ drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
+
+ platform_set_drvdata(pdev, hdmi);
+
+ hdmi->hdmi = dw_hdmi_qp_bind(pdev, encoder, &plat_data);
+ if (IS_ERR(hdmi->hdmi)) {
+ ret = PTR_ERR(hdmi->hdmi);
+ drm_encoder_cleanup(encoder);
+ return ret;
+ }
+
+ connector = drm_bridge_connector_init(drm, encoder);
+ if (IS_ERR(connector)) {
+ ret = PTR_ERR(connector);
+ drm_err(hdmi, "failed to init bridge connector: %d\n", ret);
+ return ret;
+ }
+
+ return drm_connector_attach_encoder(connector, encoder);
+}
+
+static void dw_hdmi_qp_rockchip_unbind(struct device *dev,
+ struct device *master,
+ void *data)
+{
+ struct rockchip_hdmi_qp *hdmi = dev_get_drvdata(dev);
+
+ cancel_delayed_work_sync(&hdmi->hpd_work);
+
+ drm_encoder_cleanup(&hdmi->encoder.encoder);
+}
+
+static const struct component_ops dw_hdmi_qp_rockchip_ops = {
+ .bind = dw_hdmi_qp_rockchip_bind,
+ .unbind = dw_hdmi_qp_rockchip_unbind,
+};
+
+static int dw_hdmi_qp_rockchip_probe(struct platform_device *pdev)
+{
+ return component_add(&pdev->dev, &dw_hdmi_qp_rockchip_ops);
+}
+
+static void dw_hdmi_qp_rockchip_remove(struct platform_device *pdev)
+{
+ component_del(&pdev->dev, &dw_hdmi_qp_rockchip_ops);
+}
+
+static int __maybe_unused dw_hdmi_qp_rockchip_resume(struct device *dev)
+{
+ struct rockchip_hdmi_qp *hdmi = dev_get_drvdata(dev);
+ u32 val;
+
+ val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) |
+ HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) |
+ HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) |
+ HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK);
+ regmap_write(hdmi->vo_regmap, RK3588_GRF_VO1_CON3, val);
+
+ val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK,
+ RK3588_SET_HPD_PATH_MASK);
+ regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val);
+
+ val = HIWORD_UPDATE(RK3588_HDMI0_GRANT_SEL,
+ RK3588_HDMI0_GRANT_SEL);
+ regmap_write(hdmi->vo_regmap, RK3588_GRF_VO1_CON9, val);
+
+ dw_hdmi_qp_resume(dev, hdmi->hdmi);
+
+ if (hdmi->encoder.encoder.dev)
+ drm_helper_hpd_irq_event(hdmi->encoder.encoder.dev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops dw_hdmi_qp_rockchip_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(NULL, dw_hdmi_qp_rockchip_resume)
+};
+
+struct platform_driver dw_hdmi_qp_rockchip_pltfm_driver = {
+ .probe = dw_hdmi_qp_rockchip_probe,
+ .remove = dw_hdmi_qp_rockchip_remove,
+ .driver = {
+ .name = "dwhdmiqp-rockchip",
+ .pm = &dw_hdmi_qp_rockchip_pm,
+ .of_match_table = dw_hdmi_qp_rockchip_dt_ids,
+ },
+};
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
index 111111111111..222222222222 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -507,6 +507,8 @@ static int __init rockchip_drm_init(void)
ADD_ROCKCHIP_SUB_DRIVER(cdn_dp_driver, CONFIG_ROCKCHIP_CDN_DP);
ADD_ROCKCHIP_SUB_DRIVER(dw_hdmi_rockchip_pltfm_driver,
CONFIG_ROCKCHIP_DW_HDMI);
+ ADD_ROCKCHIP_SUB_DRIVER(dw_hdmi_qp_rockchip_pltfm_driver,
+ CONFIG_ROCKCHIP_DW_HDMI_QP);
ADD_ROCKCHIP_SUB_DRIVER(dw_mipi_dsi_rockchip_driver,
CONFIG_ROCKCHIP_DW_MIPI_DSI);
ADD_ROCKCHIP_SUB_DRIVER(inno_hdmi_driver, CONFIG_ROCKCHIP_INNO_HDMI);
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index 111111111111..222222222222 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -88,6 +88,7 @@ int rockchip_drm_encoder_set_crtc_endpoint_id(struct rockchip_encoder *rencoder,
int rockchip_drm_endpoint_is_subdriver(struct device_node *ep);
extern struct platform_driver cdn_dp_driver;
extern struct platform_driver dw_hdmi_rockchip_pltfm_driver;
+extern struct platform_driver dw_hdmi_qp_rockchip_pltfm_driver;
extern struct platform_driver dw_mipi_dsi_rockchip_driver;
extern struct platform_driver inno_hdmi_driver;
extern struct platform_driver rockchip_dp_driver;
--
Armbian