From: Andy Yan <andy.yan@rock-chips.com>
Add driver extension for Synopsys DesignWare DPTX IP used
on Rockchip RK3588 SoC.
Signed-off-by: Andy Yan <andy.yan@rock-chips.com>
Acked-by: Dmitry Baryshkov <lumag@kernel.org>
---
(no changes since v2)
Changes in v2:
- no include uapi path
- switch to drmm_encoder_init
drivers/gpu/drm/rockchip/Kconfig | 9 ++
drivers/gpu/drm/rockchip/Makefile | 1 +
drivers/gpu/drm/rockchip/dw_dp-rockchip.c | 154 ++++++++++++++++++++
drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 1 +
drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 1 +
5 files changed, 166 insertions(+)
create mode 100644 drivers/gpu/drm/rockchip/dw_dp-rockchip.c
diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
index 26c4410b2407c..00315cc6be5a8 100644
--- a/drivers/gpu/drm/rockchip/Kconfig
+++ b/drivers/gpu/drm/rockchip/Kconfig
@@ -8,6 +8,7 @@ config DRM_ROCKCHIP
select DRM_PANEL
select VIDEOMODE_HELPERS
select DRM_ANALOGIX_DP if ROCKCHIP_ANALOGIX_DP
+ select DRM_DW_DP if ROCKCHIP_DW_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
@@ -58,6 +59,14 @@ config ROCKCHIP_CDN_DP
RK3399 based SoC, you should select this
option.
+config ROCKCHIP_DW_DP
+ bool "Rockchip specific extensions for Synopsys DW DP"
+ help
+ This selects support for Rockchip SoC specific extensions
+ to enable Synopsys DesignWare Cores based DisplayPort transmit
+ controller support on Rockchip SoC, If you want to enable DP on
+ rk3588 based SoC, you should select this option.
+
config ROCKCHIP_DW_HDMI
bool "Rockchip specific extensions for Synopsys DW HDMI"
help
diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
index 2b867cebbc121..097f062399c7a 100644
--- a/drivers/gpu/drm/rockchip/Makefile
+++ b/drivers/gpu/drm/rockchip/Makefile
@@ -14,6 +14,7 @@ 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_DW_MIPI_DSI2) += dw-mipi-dsi2-rockchip.o
+rockchipdrm-$(CONFIG_ROCKCHIP_DW_DP) += dw_dp-rockchip.o
rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
rockchipdrm-$(CONFIG_ROCKCHIP_LVDS) += rockchip_lvds.o
rockchipdrm-$(CONFIG_ROCKCHIP_RGB) += rockchip_rgb.o
diff --git a/drivers/gpu/drm/rockchip/dw_dp-rockchip.c b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
new file mode 100644
index 0000000000000..5ff8a6a54997e
--- /dev/null
+++ b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
@@ -0,0 +1,154 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020 Rockchip Electronics Co., Ltd.
+ *
+ * Author: Zhang Yubing <yubing.zhang@rock-chips.com>
+ * Author: Andy Yan <andy.yan@rock-chips.com>
+ */
+
+#include <linux/component.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <drm/bridge/dw_dp.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_bridge.h>
+#include <drm/drm_bridge_connector.h>
+#include <drm/drm_of.h>
+#include <drm/drm_print.h>
+#include <drm/drm_probe_helper.h>
+#include <drm/drm_simple_kms_helper.h>
+
+#include <linux/media-bus-format.h>
+#include <linux/videodev2.h>
+
+#include "rockchip_drm_drv.h"
+#include "rockchip_drm_vop.h"
+
+struct rockchip_dw_dp {
+ struct dw_dp *base;
+ struct device *dev;
+ struct rockchip_encoder encoder;
+};
+
+static inline struct rockchip_dw_dp *encoder_to_dp(struct drm_encoder *encoder)
+{
+ struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
+
+ return container_of(rkencoder, struct rockchip_dw_dp, encoder);
+}
+
+static int dw_dp_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);
+ struct drm_atomic_state *state = conn_state->state;
+ struct drm_display_info *di = &conn_state->connector->display_info;
+ struct drm_bridge *bridge = drm_bridge_chain_get_first_bridge(encoder);
+ struct drm_bridge_state *bridge_state = drm_atomic_get_new_bridge_state(state, bridge);
+ u32 bus_format = bridge_state->input_bus_cfg.format;
+
+ switch (bus_format) {
+ case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
+ case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
+ s->output_mode = ROCKCHIP_OUT_MODE_YUV420;
+ break;
+ case MEDIA_BUS_FMT_YUYV10_1X20:
+ case MEDIA_BUS_FMT_YUYV8_1X16:
+ s->output_mode = ROCKCHIP_OUT_MODE_S888_DUMMY;
+ break;
+ case MEDIA_BUS_FMT_RGB101010_1X30:
+ case MEDIA_BUS_FMT_RGB888_1X24:
+ case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
+ case MEDIA_BUS_FMT_YUV10_1X30:
+ case MEDIA_BUS_FMT_YUV8_1X24:
+ default:
+ s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
+ break;
+ }
+
+ s->output_type = DRM_MODE_CONNECTOR_DisplayPort;
+ s->bus_format = bus_format;
+ s->bus_flags = di->bus_flags;
+ s->color_space = V4L2_COLORSPACE_DEFAULT;
+
+ return 0;
+}
+
+static const struct drm_encoder_helper_funcs dw_dp_encoder_helper_funcs = {
+ .atomic_check = dw_dp_encoder_atomic_check,
+};
+
+static int dw_dp_rockchip_bind(struct device *dev, struct device *master, void *data)
+{
+ struct dw_dp_plat_data plat_data;
+ struct drm_device *drm_dev = data;
+ struct rockchip_dw_dp *dp;
+ struct drm_encoder *encoder;
+ struct drm_connector *connector;
+ int ret;
+
+ dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL);
+ if (!dp)
+ return -ENOMEM;
+
+ dp->dev = dev;
+ plat_data.max_link_rate = 810000;
+ encoder = &dp->encoder.encoder;
+ encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev, dev->of_node);
+ rockchip_drm_encoder_set_crtc_endpoint_id(&dp->encoder, dev->of_node, 0, 0);
+
+ ret = drmm_encoder_init(drm_dev, encoder, NULL, DRM_MODE_ENCODER_TMDS, NULL);
+ if (ret)
+ return ret;
+ drm_encoder_helper_add(encoder, &dw_dp_encoder_helper_funcs);
+
+ dp->base = dw_dp_bind(dev, encoder, &plat_data);
+ if (IS_ERR(dp->base)) {
+ ret = PTR_ERR(dp->base);
+ return ret;
+ }
+
+ connector = drm_bridge_connector_init(drm_dev, encoder);
+ if (IS_ERR(connector)) {
+ ret = PTR_ERR(connector);
+ return dev_err_probe(dev, ret, "Failed to init bridge connector");
+ }
+
+ drm_connector_attach_encoder(connector, encoder);
+
+ return 0;
+}
+
+static const struct component_ops dw_dp_rockchip_component_ops = {
+ .bind = dw_dp_rockchip_bind,
+};
+
+static int dw_dp_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+
+ return component_add(dev, &dw_dp_rockchip_component_ops);
+}
+
+static void dw_dp_remove(struct platform_device *pdev)
+{
+ struct rockchip_dw_dp *dp = platform_get_drvdata(pdev);
+
+ component_del(dp->dev, &dw_dp_rockchip_component_ops);
+}
+
+static const struct of_device_id dw_dp_of_match[] = {
+ { .compatible = "rockchip,rk3588-dp", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, dw_dp_of_match);
+
+struct platform_driver dw_dp_driver = {
+ .probe = dw_dp_probe,
+ .remove = dw_dp_remove,
+ .driver = {
+ .name = "dw-dp",
+ .of_match_table = dw_dp_of_match,
+ },
+};
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
index ed88788e04dd2..687bb7b252e8e 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
@@ -531,6 +531,7 @@ static int __init rockchip_drm_init(void)
ADD_ROCKCHIP_SUB_DRIVER(rockchip_dp_driver,
CONFIG_ROCKCHIP_ANALOGIX_DP);
ADD_ROCKCHIP_SUB_DRIVER(cdn_dp_driver, CONFIG_ROCKCHIP_CDN_DP);
+ ADD_ROCKCHIP_SUB_DRIVER(dw_dp_driver, CONFIG_ROCKCHIP_DW_DP);
ADD_ROCKCHIP_SUB_DRIVER(dw_hdmi_rockchip_pltfm_driver,
CONFIG_ROCKCHIP_DW_HDMI);
ADD_ROCKCHIP_SUB_DRIVER(dw_hdmi_qp_rockchip_pltfm_driver,
diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
index c183e82a42a51..2e86ad00979c4 100644
--- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
+++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
@@ -87,6 +87,7 @@ int rockchip_drm_encoder_set_crtc_endpoint_id(struct rockchip_encoder *rencoder,
struct device_node *np, int port, int reg);
int rockchip_drm_endpoint_is_subdriver(struct device_node *ep);
extern struct platform_driver cdn_dp_driver;
+extern struct platform_driver dw_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;
--
2.43.0
Hi Andy,
thank you for the driver. I'll leave some review comments inline. I don't
have specific knowledge on the DRM subsystem so my comments will be of more
general nature.
On Thursday, 3 April 2025 05:37:31 Central European Summer Time Andy Yan wrote:
> From: Andy Yan <andy.yan@rock-chips.com>
>
> Add driver extension for Synopsys DesignWare DPTX IP used
> on Rockchip RK3588 SoC.
>
> Signed-off-by: Andy Yan <andy.yan@rock-chips.com>
> Acked-by: Dmitry Baryshkov <lumag@kernel.org>
>
> ---
>
> (no changes since v2)
>
> Changes in v2:
> - no include uapi path
> - switch to drmm_encoder_init
>
> drivers/gpu/drm/rockchip/Kconfig | 9 ++
> drivers/gpu/drm/rockchip/Makefile | 1 +
> drivers/gpu/drm/rockchip/dw_dp-rockchip.c | 154 ++++++++++++++++++++
> drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 1 +
> drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 1 +
> 5 files changed, 166 insertions(+)
> create mode 100644 drivers/gpu/drm/rockchip/dw_dp-rockchip.c
>
> diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
> index 26c4410b2407c..00315cc6be5a8 100644
> --- a/drivers/gpu/drm/rockchip/Kconfig
> +++ b/drivers/gpu/drm/rockchip/Kconfig
> @@ -8,6 +8,7 @@ config DRM_ROCKCHIP
> select DRM_PANEL
> select VIDEOMODE_HELPERS
> select DRM_ANALOGIX_DP if ROCKCHIP_ANALOGIX_DP
> + select DRM_DW_DP if ROCKCHIP_DW_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
> @@ -58,6 +59,14 @@ config ROCKCHIP_CDN_DP
> RK3399 based SoC, you should select this
> option.
>
> +config ROCKCHIP_DW_DP
> + bool "Rockchip specific extensions for Synopsys DW DP"
> + help
> + This selects support for Rockchip SoC specific extensions
> + to enable Synopsys DesignWare Cores based DisplayPort transmit
> + controller support on Rockchip SoC, If you want to enable DP on
> + rk3588 based SoC, you should select this option.
> +
> config ROCKCHIP_DW_HDMI
> bool "Rockchip specific extensions for Synopsys DW HDMI"
> help
> diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
> index 2b867cebbc121..097f062399c7a 100644
> --- a/drivers/gpu/drm/rockchip/Makefile
> +++ b/drivers/gpu/drm/rockchip/Makefile
> @@ -14,6 +14,7 @@ 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_DW_MIPI_DSI2) += dw-mipi-dsi2-rockchip.o
> +rockchipdrm-$(CONFIG_ROCKCHIP_DW_DP) += dw_dp-rockchip.o
> rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
> rockchipdrm-$(CONFIG_ROCKCHIP_LVDS) += rockchip_lvds.o
> rockchipdrm-$(CONFIG_ROCKCHIP_RGB) += rockchip_rgb.o
> diff --git a/drivers/gpu/drm/rockchip/dw_dp-rockchip.c b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
> new file mode 100644
> index 0000000000000..5ff8a6a54997e
> --- /dev/null
> +++ b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
> @@ -0,0 +1,154 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) 2020 Rockchip Electronics Co., Ltd.
> + *
> + * Author: Zhang Yubing <yubing.zhang@rock-chips.com>
> + * Author: Andy Yan <andy.yan@rock-chips.com>
> + */
> +
> +#include <linux/component.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <drm/bridge/dw_dp.h>
> +#include <drm/drm_atomic_helper.h>
> +#include <drm/drm_bridge.h>
> +#include <drm/drm_bridge_connector.h>
I think there's a missing #include <drm/display/drm_dp_helper.h> here. It
gets pulled in implicitly in most configurations, but I think this is what
the s390 build failure from the kernel test robot report is about.
> +#include <drm/drm_of.h>
> +#include <drm/drm_print.h>
> +#include <drm/drm_probe_helper.h>
> +#include <drm/drm_simple_kms_helper.h>
> +
> +#include <linux/media-bus-format.h>
> +#include <linux/videodev2.h>
> +
> +#include "rockchip_drm_drv.h"
> +#include "rockchip_drm_vop.h"
> +
> +struct rockchip_dw_dp {
> + struct dw_dp *base;
> + struct device *dev;
> + struct rockchip_encoder encoder;
> +};
> +
> +static inline struct rockchip_dw_dp *encoder_to_dp(struct drm_encoder *encoder)
> +{
> + struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
> +
> + return container_of(rkencoder, struct rockchip_dw_dp, encoder);
> +}
This function appears to be unused, and will generate a warning:
linux/drivers/gpu/drm/rockchip/dw_dp-rockchip.c:33:38: warning: unused
function 'encoder_to_dp' [-Wunused-function]
I assume it may be used in a follow-up series. I think it's fine to add it
there when it's needed and avoid the warning for now by removing it.
> +
> +static int dw_dp_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);
> + struct drm_atomic_state *state = conn_state->state;
> + struct drm_display_info *di = &conn_state->connector->display_info;
> + struct drm_bridge *bridge = drm_bridge_chain_get_first_bridge(encoder);
> + struct drm_bridge_state *bridge_state = drm_atomic_get_new_bridge_state(state, bridge);
> + u32 bus_format = bridge_state->input_bus_cfg.format;
> +
> + switch (bus_format) {
> + case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
> + case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
> + s->output_mode = ROCKCHIP_OUT_MODE_YUV420;
> + break;
> + case MEDIA_BUS_FMT_YUYV10_1X20:
> + case MEDIA_BUS_FMT_YUYV8_1X16:
> + s->output_mode = ROCKCHIP_OUT_MODE_S888_DUMMY;
> + break;
> + case MEDIA_BUS_FMT_RGB101010_1X30:
> + case MEDIA_BUS_FMT_RGB888_1X24:
> + case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
> + case MEDIA_BUS_FMT_YUV10_1X30:
> + case MEDIA_BUS_FMT_YUV8_1X24:
> + default:
> + s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
> + break;
> + }
> +
> + s->output_type = DRM_MODE_CONNECTOR_DisplayPort;
> + s->bus_format = bus_format;
> + s->bus_flags = di->bus_flags;
> + s->color_space = V4L2_COLORSPACE_DEFAULT;
Reading the VOP2 code s->color_space gets read by, it seems this results
in the output always using BT.709 as the colour space in YUV output mode.
Is my understanding of the code correct?
I don't know if DP 1.4 is limited with regards to HDR or if this is just
left for later to implement, but BT.709 in the case of an HDR RGB VOP
input seems wrong, unless VOP2 sets the output color_space to something like
BT.2020 or similar in the case of output_mode == ROCKCHIP_OUT_MODE_AAAA and
I'm not seeing it.
> +
> + return 0;
> +}
> +
> +static const struct drm_encoder_helper_funcs dw_dp_encoder_helper_funcs = {
> + .atomic_check = dw_dp_encoder_atomic_check,
> +};
> +
> +static int dw_dp_rockchip_bind(struct device *dev, struct device *master, void *data)
> +{
> + struct dw_dp_plat_data plat_data;
> + struct drm_device *drm_dev = data;
> + struct rockchip_dw_dp *dp;
> + struct drm_encoder *encoder;
> + struct drm_connector *connector;
> + int ret;
> +
> + dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL);
> + if (!dp)
> + return -ENOMEM;
> +
> + dp->dev = dev;
> + plat_data.max_link_rate = 810000;
> + encoder = &dp->encoder.encoder;
> + encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev, dev->of_node);
> + rockchip_drm_encoder_set_crtc_endpoint_id(&dp->encoder, dev->of_node, 0, 0);
> +
> + ret = drmm_encoder_init(drm_dev, encoder, NULL, DRM_MODE_ENCODER_TMDS, NULL);
> + if (ret)
> + return ret;
> + drm_encoder_helper_add(encoder, &dw_dp_encoder_helper_funcs);
> +
> + dp->base = dw_dp_bind(dev, encoder, &plat_data);
> + if (IS_ERR(dp->base)) {
> + ret = PTR_ERR(dp->base);
> + return ret;
> + }
> +
> + connector = drm_bridge_connector_init(drm_dev, encoder);
> + if (IS_ERR(connector)) {
> + ret = PTR_ERR(connector);
> + return dev_err_probe(dev, ret, "Failed to init bridge connector");
> + }
> +
> + drm_connector_attach_encoder(connector, encoder);
> +
> + return 0;
> +}
> +
> +static const struct component_ops dw_dp_rockchip_component_ops = {
> + .bind = dw_dp_rockchip_bind,
> +};
> +
> +static int dw_dp_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> +
> + return component_add(dev, &dw_dp_rockchip_component_ops);
> +}
> +
> +static void dw_dp_remove(struct platform_device *pdev)
> +{
> + struct rockchip_dw_dp *dp = platform_get_drvdata(pdev);
Does one of the helper functions or something else set drvdata? Otherwise
I don't see how this is ever non-null.
> +
> + component_del(dp->dev, &dw_dp_rockchip_component_ops);
> +}
> +
> +static const struct of_device_id dw_dp_of_match[] = {
> + { .compatible = "rockchip,rk3588-dp", },
> + {}
> +};
> +MODULE_DEVICE_TABLE(of, dw_dp_of_match);
> +
> +struct platform_driver dw_dp_driver = {
> + .probe = dw_dp_probe,
> + .remove = dw_dp_remove,
> + .driver = {
> + .name = "dw-dp",
> + .of_match_table = dw_dp_of_match,
> + },
> +};
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> index ed88788e04dd2..687bb7b252e8e 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
> @@ -531,6 +531,7 @@ static int __init rockchip_drm_init(void)
> ADD_ROCKCHIP_SUB_DRIVER(rockchip_dp_driver,
> CONFIG_ROCKCHIP_ANALOGIX_DP);
> ADD_ROCKCHIP_SUB_DRIVER(cdn_dp_driver, CONFIG_ROCKCHIP_CDN_DP);
> + ADD_ROCKCHIP_SUB_DRIVER(dw_dp_driver, CONFIG_ROCKCHIP_DW_DP);
> ADD_ROCKCHIP_SUB_DRIVER(dw_hdmi_rockchip_pltfm_driver,
> CONFIG_ROCKCHIP_DW_HDMI);
> ADD_ROCKCHIP_SUB_DRIVER(dw_hdmi_qp_rockchip_pltfm_driver,
> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> index c183e82a42a51..2e86ad00979c4 100644
> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
> @@ -87,6 +87,7 @@ int rockchip_drm_encoder_set_crtc_endpoint_id(struct rockchip_encoder *rencoder,
> struct device_node *np, int port, int reg);
> int rockchip_drm_endpoint_is_subdriver(struct device_node *ep);
> extern struct platform_driver cdn_dp_driver;
> +extern struct platform_driver dw_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;
>
Other than that, the driver looks great, thank you! I've tested it on my
ROCK 5T over DP altmode, where it correctly interfaces with a DP monitor
I have through an alt-mode adapter. So feel free to add a
Tested-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
Kind regards,
Nicolas Frattaroli
Hi Nicolas,
At 2025-05-28 00:12:43, "Nicolas Frattaroli" <nicolas.frattaroli@collabora.com> wrote:
>Hi Andy,
>
>thank you for the driver. I'll leave some review comments inline. I don't
>have specific knowledge on the DRM subsystem so my comments will be of more
>general nature.
>
Thank you very much for your review.
>On Thursday, 3 April 2025 05:37:31 Central European Summer Time Andy Yan wrote:
>> From: Andy Yan <andy.yan@rock-chips.com>
>>
>> Add driver extension for Synopsys DesignWare DPTX IP used
>> on Rockchip RK3588 SoC.
>>
>> Signed-off-by: Andy Yan <andy.yan@rock-chips.com>
>> Acked-by: Dmitry Baryshkov <lumag@kernel.org>
>>
>> ---
>>
>> (no changes since v2)
>>
>> Changes in v2:
>> - no include uapi path
>> - switch to drmm_encoder_init
>>
>> drivers/gpu/drm/rockchip/Kconfig | 9 ++
>> drivers/gpu/drm/rockchip/Makefile | 1 +
>> drivers/gpu/drm/rockchip/dw_dp-rockchip.c | 154 ++++++++++++++++++++
>> drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 1 +
>> drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 1 +
>> 5 files changed, 166 insertions(+)
>> create mode 100644 drivers/gpu/drm/rockchip/dw_dp-rockchip.c
>>
>> diff --git a/drivers/gpu/drm/rockchip/Kconfig b/drivers/gpu/drm/rockchip/Kconfig
>> index 26c4410b2407c..00315cc6be5a8 100644
>> --- a/drivers/gpu/drm/rockchip/Kconfig
>> +++ b/drivers/gpu/drm/rockchip/Kconfig
>> @@ -8,6 +8,7 @@ config DRM_ROCKCHIP
>> select DRM_PANEL
>> select VIDEOMODE_HELPERS
>> select DRM_ANALOGIX_DP if ROCKCHIP_ANALOGIX_DP
>> + select DRM_DW_DP if ROCKCHIP_DW_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
>> @@ -58,6 +59,14 @@ config ROCKCHIP_CDN_DP
>> RK3399 based SoC, you should select this
>> option.
>>
>> +config ROCKCHIP_DW_DP
>> + bool "Rockchip specific extensions for Synopsys DW DP"
>> + help
>> + This selects support for Rockchip SoC specific extensions
>> + to enable Synopsys DesignWare Cores based DisplayPort transmit
>> + controller support on Rockchip SoC, If you want to enable DP on
>> + rk3588 based SoC, you should select this option.
>> +
>> config ROCKCHIP_DW_HDMI
>> bool "Rockchip specific extensions for Synopsys DW HDMI"
>> help
>> diff --git a/drivers/gpu/drm/rockchip/Makefile b/drivers/gpu/drm/rockchip/Makefile
>> index 2b867cebbc121..097f062399c7a 100644
>> --- a/drivers/gpu/drm/rockchip/Makefile
>> +++ b/drivers/gpu/drm/rockchip/Makefile
>> @@ -14,6 +14,7 @@ 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_DW_MIPI_DSI2) += dw-mipi-dsi2-rockchip.o
>> +rockchipdrm-$(CONFIG_ROCKCHIP_DW_DP) += dw_dp-rockchip.o
>> rockchipdrm-$(CONFIG_ROCKCHIP_INNO_HDMI) += inno_hdmi.o
>> rockchipdrm-$(CONFIG_ROCKCHIP_LVDS) += rockchip_lvds.o
>> rockchipdrm-$(CONFIG_ROCKCHIP_RGB) += rockchip_rgb.o
>> diff --git a/drivers/gpu/drm/rockchip/dw_dp-rockchip.c b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
>> new file mode 100644
>> index 0000000000000..5ff8a6a54997e
>> --- /dev/null
>> +++ b/drivers/gpu/drm/rockchip/dw_dp-rockchip.c
>> @@ -0,0 +1,154 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Copyright (c) 2020 Rockchip Electronics Co., Ltd.
>> + *
>> + * Author: Zhang Yubing <yubing.zhang@rock-chips.com>
>> + * Author: Andy Yan <andy.yan@rock-chips.com>
>> + */
>> +
>> +#include <linux/component.h>
>> +#include <linux/of_device.h>
>> +#include <linux/platform_device.h>
>> +#include <drm/bridge/dw_dp.h>
>> +#include <drm/drm_atomic_helper.h>
>> +#include <drm/drm_bridge.h>
>> +#include <drm/drm_bridge_connector.h>
>
>I think there's a missing #include <drm/display/drm_dp_helper.h> here. It
>gets pulled in implicitly in most configurations, but I think this is what
>the s390 build failure from the kernel test robot report is about.
I have included this header file in the file dw-dp.c.
I think the error occurred during the robot test because the following patch[0] had
not been merged yet. Now that this submission has been merged, and dw_dp-rockchip.c
does not call functions from that header file, so it should no longer report any errors now.
[0]https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/commit/?id=09cdda7a60f45784cebddf1fa2109d6279f9890b
>
>> +#include <drm/drm_of.h>
>> +#include <drm/drm_print.h>
>> +#include <drm/drm_probe_helper.h>
>> +#include <drm/drm_simple_kms_helper.h>
>> +
>> +#include <linux/media-bus-format.h>
>> +#include <linux/videodev2.h>
>> +
>> +#include "rockchip_drm_drv.h"
>> +#include "rockchip_drm_vop.h"
>> +
>> +struct rockchip_dw_dp {
>> + struct dw_dp *base;
>> + struct device *dev;
>> + struct rockchip_encoder encoder;
>> +};
>> +
>> +static inline struct rockchip_dw_dp *encoder_to_dp(struct drm_encoder *encoder)
>> +{
>> + struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
>> +
>> + return container_of(rkencoder, struct rockchip_dw_dp, encoder);
>> +}
>
>This function appears to be unused, and will generate a warning:
>
> linux/drivers/gpu/drm/rockchip/dw_dp-rockchip.c:33:38: warning: unused
> function 'encoder_to_dp' [-Wunused-function]
>
>I assume it may be used in a follow-up series. I think it's fine to add it
>there when it's needed and avoid the warning for now by removing it.
Thank you for catching it, i will remove it in V4.
>
>> +
>> +static int dw_dp_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);
>> + struct drm_atomic_state *state = conn_state->state;
>> + struct drm_display_info *di = &conn_state->connector->display_info;
>> + struct drm_bridge *bridge = drm_bridge_chain_get_first_bridge(encoder);
>> + struct drm_bridge_state *bridge_state = drm_atomic_get_new_bridge_state(state, bridge);
>> + u32 bus_format = bridge_state->input_bus_cfg.format;
>> +
>> + switch (bus_format) {
>> + case MEDIA_BUS_FMT_UYYVYY10_0_5X30:
>> + case MEDIA_BUS_FMT_UYYVYY8_0_5X24:
>> + s->output_mode = ROCKCHIP_OUT_MODE_YUV420;
>> + break;
>> + case MEDIA_BUS_FMT_YUYV10_1X20:
>> + case MEDIA_BUS_FMT_YUYV8_1X16:
>> + s->output_mode = ROCKCHIP_OUT_MODE_S888_DUMMY;
>> + break;
>> + case MEDIA_BUS_FMT_RGB101010_1X30:
>> + case MEDIA_BUS_FMT_RGB888_1X24:
>> + case MEDIA_BUS_FMT_RGB666_1X24_CPADHI:
>> + case MEDIA_BUS_FMT_YUV10_1X30:
>> + case MEDIA_BUS_FMT_YUV8_1X24:
>> + default:
>> + s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
>> + break;
>> + }
>> +
>> + s->output_type = DRM_MODE_CONNECTOR_DisplayPort;
>> + s->bus_format = bus_format;
>> + s->bus_flags = di->bus_flags;
>> + s->color_space = V4L2_COLORSPACE_DEFAULT;
>
>Reading the VOP2 code s->color_space gets read by, it seems this results
>in the output always using BT.709 as the colour space in YUV output mode.
>Is my understanding of the code correct?
>
>I don't know if DP 1.4 is limited with regards to HDR or if this is just
>left for later to implement, but BT.709 in the case of an HDR RGB VOP
>input seems wrong, unless VOP2 sets the output color_space to something like
>BT.2020 or similar in the case of output_mode == ROCKCHIP_OUT_MODE_AAAA and
>I'm not seeing it.
In our downstream code, when DP is in HDR output mode, the color_space will be set to V4L2_COLORSPACE_BT2020.
Therefore, when we later support HDR output on the mainline, we can make the same adjustment.
>
>> +
>> + return 0;
>> +}
>> +
>> +static const struct drm_encoder_helper_funcs dw_dp_encoder_helper_funcs = {
>> + .atomic_check = dw_dp_encoder_atomic_check,
>> +};
>> +
>> +static int dw_dp_rockchip_bind(struct device *dev, struct device *master, void *data)
>> +{
>> + struct dw_dp_plat_data plat_data;
>> + struct drm_device *drm_dev = data;
>> + struct rockchip_dw_dp *dp;
>> + struct drm_encoder *encoder;
>> + struct drm_connector *connector;
>> + int ret;
>> +
>> + dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL);
>> + if (!dp)
>> + return -ENOMEM;
>> +
>> + dp->dev = dev;
>> + plat_data.max_link_rate = 810000;
>> + encoder = &dp->encoder.encoder;
>> + encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev, dev->of_node);
>> + rockchip_drm_encoder_set_crtc_endpoint_id(&dp->encoder, dev->of_node, 0, 0);
>> +
>> + ret = drmm_encoder_init(drm_dev, encoder, NULL, DRM_MODE_ENCODER_TMDS, NULL);
>> + if (ret)
>> + return ret;
>> + drm_encoder_helper_add(encoder, &dw_dp_encoder_helper_funcs);
>> +
>> + dp->base = dw_dp_bind(dev, encoder, &plat_data);
>> + if (IS_ERR(dp->base)) {
>> + ret = PTR_ERR(dp->base);
>> + return ret;
>> + }
>> +
>> + connector = drm_bridge_connector_init(drm_dev, encoder);
>> + if (IS_ERR(connector)) {
>> + ret = PTR_ERR(connector);
>> + return dev_err_probe(dev, ret, "Failed to init bridge connector");
>> + }
>> +
>> + drm_connector_attach_encoder(connector, encoder);
>> +
>> + return 0;
>> +}
>> +
>> +static const struct component_ops dw_dp_rockchip_component_ops = {
>> + .bind = dw_dp_rockchip_bind,
>> +};
>> +
>> +static int dw_dp_probe(struct platform_device *pdev)
>> +{
>> + struct device *dev = &pdev->dev;
>> +
>> + return component_add(dev, &dw_dp_rockchip_component_ops);
>> +}
>> +
>> +static void dw_dp_remove(struct platform_device *pdev)
>> +{
>> + struct rockchip_dw_dp *dp = platform_get_drvdata(pdev);
>
>Does one of the helper functions or something else set drvdata? Otherwise
>I don't see how this is ever non-null.
Thanks for catching it, will be fixed in V4.
>
>> +
>> + component_del(dp->dev, &dw_dp_rockchip_component_ops);
>> +}
>> +
>> +static const struct of_device_id dw_dp_of_match[] = {
>> + { .compatible = "rockchip,rk3588-dp", },
>> + {}
>> +};
>> +MODULE_DEVICE_TABLE(of, dw_dp_of_match);
>> +
>> +struct platform_driver dw_dp_driver = {
>> + .probe = dw_dp_probe,
>> + .remove = dw_dp_remove,
>> + .driver = {
>> + .name = "dw-dp",
>> + .of_match_table = dw_dp_of_match,
>> + },
>> +};
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
>> index ed88788e04dd2..687bb7b252e8e 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.c
>> @@ -531,6 +531,7 @@ static int __init rockchip_drm_init(void)
>> ADD_ROCKCHIP_SUB_DRIVER(rockchip_dp_driver,
>> CONFIG_ROCKCHIP_ANALOGIX_DP);
>> ADD_ROCKCHIP_SUB_DRIVER(cdn_dp_driver, CONFIG_ROCKCHIP_CDN_DP);
>> + ADD_ROCKCHIP_SUB_DRIVER(dw_dp_driver, CONFIG_ROCKCHIP_DW_DP);
>> ADD_ROCKCHIP_SUB_DRIVER(dw_hdmi_rockchip_pltfm_driver,
>> CONFIG_ROCKCHIP_DW_HDMI);
>> ADD_ROCKCHIP_SUB_DRIVER(dw_hdmi_qp_rockchip_pltfm_driver,
>> diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>> index c183e82a42a51..2e86ad00979c4 100644
>> --- a/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>> +++ b/drivers/gpu/drm/rockchip/rockchip_drm_drv.h
>> @@ -87,6 +87,7 @@ int rockchip_drm_encoder_set_crtc_endpoint_id(struct rockchip_encoder *rencoder,
>> struct device_node *np, int port, int reg);
>> int rockchip_drm_endpoint_is_subdriver(struct device_node *ep);
>> extern struct platform_driver cdn_dp_driver;
>> +extern struct platform_driver dw_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;
>>
>
>Other than that, the driver looks great, thank you! I've tested it on my
>ROCK 5T over DP altmode, where it correctly interfaces with a DP monitor
>I have through an alt-mode adapter. So feel free to add a
>
>Tested-by: Nicolas Frattaroli <nicolas.frattaroli@collabora.com>
Thanks again.
>
>Kind regards,
>Nicolas Frattaroli
>
>
>
Hi Andy,
kernel test robot noticed the following build errors:
[auto build test ERROR on rockchip/for-next]
[also build test ERROR on robh/for-next drm-exynos/exynos-drm-next linus/master v6.14 next-20250404]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Andy-Yan/dt-bindings-display-rockchip-Add-schema-for-RK3588-DPTX-Controller/20250403-114203
base: https://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip.git for-next
patch link: https://lore.kernel.org/r/20250403033748.245007-4-andyshrk%40163.com
patch subject: [PATCH v3 3/9] drm/rockchip: Add RK3588 DPTX output support
config: s390-allyesconfig (https://download.01.org/0day-ci/archive/20250404/202504041920.g5XTp8Xp-lkp@intel.com/config)
compiler: s390-linux-gcc (GCC) 14.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250404/202504041920.g5XTp8Xp-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202504041920.g5XTp8Xp-lkp@intel.com/
All errors (new ones prefixed by >>):
drivers/gpu/drm/bridge/synopsys/dw-dp.c: In function 'dw_dp_link_disable':
>> drivers/gpu/drm/bridge/synopsys/dw-dp.c:1599:17: error: implicit declaration of function 'drm_dp_link_power_down' [-Wimplicit-function-declaration]
1599 | drm_dp_link_power_down(&dp->aux, dp->link.revision);
| ^~~~~~~~~~~~~~~~~~~~~~
drivers/gpu/drm/bridge/synopsys/dw-dp.c: In function 'dw_dp_link_enable':
>> drivers/gpu/drm/bridge/synopsys/dw-dp.c:1617:15: error: implicit declaration of function 'drm_dp_link_power_up' [-Wimplicit-function-declaration]
1617 | ret = drm_dp_link_power_up(&dp->aux, dp->link.revision);
| ^~~~~~~~~~~~~~~~~~~~
drivers/gpu/drm/bridge/synopsys/dw-dp.c: At top level:
>> drivers/gpu/drm/bridge/synopsys/dw-dp.c:1790:26: error: initialization of 'void (*)(struct drm_bridge *, struct drm_bridge_state *)' from incompatible pointer type 'void (*)(struct drm_bridge *, struct drm_atomic_state *)' [-Wincompatible-pointer-types]
1790 | .atomic_enable = dw_dp_bridge_atomic_enable,
| ^~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/gpu/drm/bridge/synopsys/dw-dp.c:1790:26: note: (near initialization for 'dw_dp_bridge_funcs.atomic_enable')
drivers/gpu/drm/bridge/synopsys/dw-dp.c:1791:27: error: initialization of 'void (*)(struct drm_bridge *, struct drm_bridge_state *)' from incompatible pointer type 'void (*)(struct drm_bridge *, struct drm_atomic_state *)' [-Wincompatible-pointer-types]
1791 | .atomic_disable = dw_dp_bridge_atomic_disable,
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/gpu/drm/bridge/synopsys/dw-dp.c:1791:27: note: (near initialization for 'dw_dp_bridge_funcs.atomic_disable')
vim +1790 drivers/gpu/drm/bridge/synopsys/dw-dp.c
d366451bed980ac Andy Yan 2025-04-03 1593
d366451bed980ac Andy Yan 2025-04-03 1594 static void dw_dp_link_disable(struct dw_dp *dp)
d366451bed980ac Andy Yan 2025-04-03 1595 {
d366451bed980ac Andy Yan 2025-04-03 1596 struct dw_dp_link *link = &dp->link;
d366451bed980ac Andy Yan 2025-04-03 1597
d366451bed980ac Andy Yan 2025-04-03 1598 if (dw_dp_hpd_detect(dp))
d366451bed980ac Andy Yan 2025-04-03 @1599 drm_dp_link_power_down(&dp->aux, dp->link.revision);
d366451bed980ac Andy Yan 2025-04-03 1600
d366451bed980ac Andy Yan 2025-04-03 1601 dw_dp_phy_xmit_enable(dp, 0);
d366451bed980ac Andy Yan 2025-04-03 1602
d366451bed980ac Andy Yan 2025-04-03 1603 phy_power_off(dp->phy);
d366451bed980ac Andy Yan 2025-04-03 1604
d366451bed980ac Andy Yan 2025-04-03 1605 link->train.clock_recovered = false;
d366451bed980ac Andy Yan 2025-04-03 1606 link->train.channel_equalized = false;
d366451bed980ac Andy Yan 2025-04-03 1607 }
d366451bed980ac Andy Yan 2025-04-03 1608
d366451bed980ac Andy Yan 2025-04-03 1609 static int dw_dp_link_enable(struct dw_dp *dp)
d366451bed980ac Andy Yan 2025-04-03 1610 {
d366451bed980ac Andy Yan 2025-04-03 1611 int ret;
d366451bed980ac Andy Yan 2025-04-03 1612
d366451bed980ac Andy Yan 2025-04-03 1613 ret = phy_power_on(dp->phy);
d366451bed980ac Andy Yan 2025-04-03 1614 if (ret)
d366451bed980ac Andy Yan 2025-04-03 1615 return ret;
d366451bed980ac Andy Yan 2025-04-03 1616
d366451bed980ac Andy Yan 2025-04-03 @1617 ret = drm_dp_link_power_up(&dp->aux, dp->link.revision);
d366451bed980ac Andy Yan 2025-04-03 1618 if (ret < 0)
d366451bed980ac Andy Yan 2025-04-03 1619 return ret;
d366451bed980ac Andy Yan 2025-04-03 1620
d366451bed980ac Andy Yan 2025-04-03 1621 ret = dw_dp_link_train(dp);
d366451bed980ac Andy Yan 2025-04-03 1622
d366451bed980ac Andy Yan 2025-04-03 1623 return ret;
d366451bed980ac Andy Yan 2025-04-03 1624 }
d366451bed980ac Andy Yan 2025-04-03 1625
d366451bed980ac Andy Yan 2025-04-03 1626 static void dw_dp_bridge_atomic_enable(struct drm_bridge *bridge,
d366451bed980ac Andy Yan 2025-04-03 1627 struct drm_atomic_state *state)
d366451bed980ac Andy Yan 2025-04-03 1628 {
d366451bed980ac Andy Yan 2025-04-03 1629 struct dw_dp *dp = bridge_to_dp(bridge);
d366451bed980ac Andy Yan 2025-04-03 1630 struct drm_connector *connector;
d366451bed980ac Andy Yan 2025-04-03 1631 struct drm_connector_state *conn_state;
d366451bed980ac Andy Yan 2025-04-03 1632 int ret;
d366451bed980ac Andy Yan 2025-04-03 1633
d366451bed980ac Andy Yan 2025-04-03 1634 connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
d366451bed980ac Andy Yan 2025-04-03 1635 if (!connector) {
d366451bed980ac Andy Yan 2025-04-03 1636 dev_err(dp->dev, "failed to get connector\n");
d366451bed980ac Andy Yan 2025-04-03 1637 return;
d366451bed980ac Andy Yan 2025-04-03 1638 }
d366451bed980ac Andy Yan 2025-04-03 1639
d366451bed980ac Andy Yan 2025-04-03 1640 conn_state = drm_atomic_get_new_connector_state(state, connector);
d366451bed980ac Andy Yan 2025-04-03 1641 if (!conn_state) {
d366451bed980ac Andy Yan 2025-04-03 1642 dev_err(dp->dev, "failed to get connector state\n");
d366451bed980ac Andy Yan 2025-04-03 1643 return;
d366451bed980ac Andy Yan 2025-04-03 1644 }
d366451bed980ac Andy Yan 2025-04-03 1645
d366451bed980ac Andy Yan 2025-04-03 1646 set_bit(0, dp->sdp_reg_bank);
d366451bed980ac Andy Yan 2025-04-03 1647
d366451bed980ac Andy Yan 2025-04-03 1648 ret = dw_dp_link_enable(dp);
d366451bed980ac Andy Yan 2025-04-03 1649 if (ret < 0) {
d366451bed980ac Andy Yan 2025-04-03 1650 dev_err(dp->dev, "failed to enable link: %d\n", ret);
d366451bed980ac Andy Yan 2025-04-03 1651 return;
d366451bed980ac Andy Yan 2025-04-03 1652 }
d366451bed980ac Andy Yan 2025-04-03 1653
d366451bed980ac Andy Yan 2025-04-03 1654 ret = dw_dp_video_enable(dp);
d366451bed980ac Andy Yan 2025-04-03 1655 if (ret < 0) {
d366451bed980ac Andy Yan 2025-04-03 1656 dev_err(dp->dev, "failed to enable video: %d\n", ret);
d366451bed980ac Andy Yan 2025-04-03 1657 return;
d366451bed980ac Andy Yan 2025-04-03 1658 }
d366451bed980ac Andy Yan 2025-04-03 1659 }
d366451bed980ac Andy Yan 2025-04-03 1660
d366451bed980ac Andy Yan 2025-04-03 1661 static void dw_dp_reset(struct dw_dp *dp)
d366451bed980ac Andy Yan 2025-04-03 1662 {
d366451bed980ac Andy Yan 2025-04-03 1663 int val;
d366451bed980ac Andy Yan 2025-04-03 1664
d366451bed980ac Andy Yan 2025-04-03 1665 disable_irq(dp->irq);
d366451bed980ac Andy Yan 2025-04-03 1666 regmap_update_bits(dp->regmap, DW_DP_SOFT_RESET_CTRL, CONTROLLER_RESET,
d366451bed980ac Andy Yan 2025-04-03 1667 FIELD_PREP(CONTROLLER_RESET, 1));
d366451bed980ac Andy Yan 2025-04-03 1668 udelay(10);
d366451bed980ac Andy Yan 2025-04-03 1669 regmap_update_bits(dp->regmap, DW_DP_SOFT_RESET_CTRL, CONTROLLER_RESET,
d366451bed980ac Andy Yan 2025-04-03 1670 FIELD_PREP(CONTROLLER_RESET, 0));
d366451bed980ac Andy Yan 2025-04-03 1671
d366451bed980ac Andy Yan 2025-04-03 1672 dw_dp_init_hw(dp);
d366451bed980ac Andy Yan 2025-04-03 1673 regmap_read_poll_timeout(dp->regmap, DW_DP_HPD_STATUS, val,
d366451bed980ac Andy Yan 2025-04-03 1674 FIELD_GET(HPD_HOT_PLUG, val), 200, 200000);
d366451bed980ac Andy Yan 2025-04-03 1675 regmap_write(dp->regmap, DW_DP_HPD_STATUS, HPD_HOT_PLUG);
d366451bed980ac Andy Yan 2025-04-03 1676 enable_irq(dp->irq);
d366451bed980ac Andy Yan 2025-04-03 1677 }
d366451bed980ac Andy Yan 2025-04-03 1678
d366451bed980ac Andy Yan 2025-04-03 1679 static void dw_dp_bridge_atomic_disable(struct drm_bridge *bridge,
d366451bed980ac Andy Yan 2025-04-03 1680 struct drm_atomic_state *state)
d366451bed980ac Andy Yan 2025-04-03 1681 {
d366451bed980ac Andy Yan 2025-04-03 1682 struct dw_dp *dp = bridge_to_dp(bridge);
d366451bed980ac Andy Yan 2025-04-03 1683
d366451bed980ac Andy Yan 2025-04-03 1684 dw_dp_video_disable(dp);
d366451bed980ac Andy Yan 2025-04-03 1685 dw_dp_link_disable(dp);
d366451bed980ac Andy Yan 2025-04-03 1686 bitmap_zero(dp->sdp_reg_bank, SDP_REG_BANK_SIZE);
d366451bed980ac Andy Yan 2025-04-03 1687 dw_dp_reset(dp);
d366451bed980ac Andy Yan 2025-04-03 1688 }
d366451bed980ac Andy Yan 2025-04-03 1689
d366451bed980ac Andy Yan 2025-04-03 1690 static bool dw_dp_hpd_detect_link(struct dw_dp *dp)
d366451bed980ac Andy Yan 2025-04-03 1691 {
d366451bed980ac Andy Yan 2025-04-03 1692 int ret;
d366451bed980ac Andy Yan 2025-04-03 1693
d366451bed980ac Andy Yan 2025-04-03 1694 ret = phy_power_on(dp->phy);
d366451bed980ac Andy Yan 2025-04-03 1695 if (ret < 0)
d366451bed980ac Andy Yan 2025-04-03 1696 return false;
d366451bed980ac Andy Yan 2025-04-03 1697 ret = dw_dp_link_parse(dp);
d366451bed980ac Andy Yan 2025-04-03 1698 phy_power_off(dp->phy);
d366451bed980ac Andy Yan 2025-04-03 1699
d366451bed980ac Andy Yan 2025-04-03 1700 return !ret;
d366451bed980ac Andy Yan 2025-04-03 1701 }
d366451bed980ac Andy Yan 2025-04-03 1702
d366451bed980ac Andy Yan 2025-04-03 1703 static enum drm_connector_status dw_dp_bridge_detect(struct drm_bridge *bridge)
d366451bed980ac Andy Yan 2025-04-03 1704 {
d366451bed980ac Andy Yan 2025-04-03 1705 struct dw_dp *dp = bridge_to_dp(bridge);
d366451bed980ac Andy Yan 2025-04-03 1706
d366451bed980ac Andy Yan 2025-04-03 1707 if (!dw_dp_hpd_detect(dp))
d366451bed980ac Andy Yan 2025-04-03 1708 return connector_status_disconnected;
d366451bed980ac Andy Yan 2025-04-03 1709
d366451bed980ac Andy Yan 2025-04-03 1710 if (!dw_dp_hpd_detect_link(dp))
d366451bed980ac Andy Yan 2025-04-03 1711 return connector_status_disconnected;
d366451bed980ac Andy Yan 2025-04-03 1712
d366451bed980ac Andy Yan 2025-04-03 1713 return connector_status_connected;
d366451bed980ac Andy Yan 2025-04-03 1714 }
d366451bed980ac Andy Yan 2025-04-03 1715
d366451bed980ac Andy Yan 2025-04-03 1716 static const struct drm_edid *dw_dp_bridge_edid_read(struct drm_bridge *bridge,
d366451bed980ac Andy Yan 2025-04-03 1717 struct drm_connector *connector)
d366451bed980ac Andy Yan 2025-04-03 1718 {
d366451bed980ac Andy Yan 2025-04-03 1719 struct dw_dp *dp = bridge_to_dp(bridge);
d366451bed980ac Andy Yan 2025-04-03 1720 const struct drm_edid *edid;
d366451bed980ac Andy Yan 2025-04-03 1721 int ret;
d366451bed980ac Andy Yan 2025-04-03 1722
d366451bed980ac Andy Yan 2025-04-03 1723 ret = phy_power_on(dp->phy);
d366451bed980ac Andy Yan 2025-04-03 1724 if (ret)
d366451bed980ac Andy Yan 2025-04-03 1725 return NULL;
d366451bed980ac Andy Yan 2025-04-03 1726
d366451bed980ac Andy Yan 2025-04-03 1727 edid = drm_edid_read_ddc(connector, &dp->aux.ddc);
d366451bed980ac Andy Yan 2025-04-03 1728
d366451bed980ac Andy Yan 2025-04-03 1729 phy_power_off(dp->phy);
d366451bed980ac Andy Yan 2025-04-03 1730
d366451bed980ac Andy Yan 2025-04-03 1731 return edid;
d366451bed980ac Andy Yan 2025-04-03 1732 }
d366451bed980ac Andy Yan 2025-04-03 1733
d366451bed980ac Andy Yan 2025-04-03 1734 static u32 *dw_dp_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
d366451bed980ac Andy Yan 2025-04-03 1735 struct drm_bridge_state *bridge_state,
d366451bed980ac Andy Yan 2025-04-03 1736 struct drm_crtc_state *crtc_state,
d366451bed980ac Andy Yan 2025-04-03 1737 struct drm_connector_state *conn_state,
d366451bed980ac Andy Yan 2025-04-03 1738 unsigned int *num_output_fmts)
d366451bed980ac Andy Yan 2025-04-03 1739 {
d366451bed980ac Andy Yan 2025-04-03 1740 struct dw_dp *dp = bridge_to_dp(bridge);
d366451bed980ac Andy Yan 2025-04-03 1741 struct dw_dp_link *link = &dp->link;
d366451bed980ac Andy Yan 2025-04-03 1742 struct drm_display_info *di = &conn_state->connector->display_info;
d366451bed980ac Andy Yan 2025-04-03 1743 struct drm_display_mode mode = crtc_state->mode;
d366451bed980ac Andy Yan 2025-04-03 1744 const struct dw_dp_output_format *fmt;
d366451bed980ac Andy Yan 2025-04-03 1745 u32 i, j = 0;
d366451bed980ac Andy Yan 2025-04-03 1746 u32 *output_fmts;
d366451bed980ac Andy Yan 2025-04-03 1747
d366451bed980ac Andy Yan 2025-04-03 1748 *num_output_fmts = 0;
d366451bed980ac Andy Yan 2025-04-03 1749
d366451bed980ac Andy Yan 2025-04-03 1750 output_fmts = kcalloc(ARRAY_SIZE(dw_dp_output_formats), sizeof(*output_fmts), GFP_KERNEL);
d366451bed980ac Andy Yan 2025-04-03 1751 if (!output_fmts)
d366451bed980ac Andy Yan 2025-04-03 1752 return NULL;
d366451bed980ac Andy Yan 2025-04-03 1753
d366451bed980ac Andy Yan 2025-04-03 1754 for (i = 0; i < ARRAY_SIZE(dw_dp_output_formats); i++) {
d366451bed980ac Andy Yan 2025-04-03 1755 fmt = &dw_dp_output_formats[i];
d366451bed980ac Andy Yan 2025-04-03 1756
d366451bed980ac Andy Yan 2025-04-03 1757 if (fmt->bpc > conn_state->max_bpc)
d366451bed980ac Andy Yan 2025-04-03 1758 continue;
d366451bed980ac Andy Yan 2025-04-03 1759
d366451bed980ac Andy Yan 2025-04-03 1760 if (!(fmt->color_format & di->color_formats))
d366451bed980ac Andy Yan 2025-04-03 1761 continue;
d366451bed980ac Andy Yan 2025-04-03 1762
d366451bed980ac Andy Yan 2025-04-03 1763 if (fmt->color_format == DRM_COLOR_FORMAT_YCBCR420 &&
d366451bed980ac Andy Yan 2025-04-03 1764 !link->vsc_sdp_supported)
d366451bed980ac Andy Yan 2025-04-03 1765 continue;
d366451bed980ac Andy Yan 2025-04-03 1766
d366451bed980ac Andy Yan 2025-04-03 1767 if (fmt->color_format != DRM_COLOR_FORMAT_YCBCR420 &&
d366451bed980ac Andy Yan 2025-04-03 1768 drm_mode_is_420_only(di, &mode))
d366451bed980ac Andy Yan 2025-04-03 1769 continue;
d366451bed980ac Andy Yan 2025-04-03 1770
d366451bed980ac Andy Yan 2025-04-03 1771 if (!dw_dp_bandwidth_ok(dp, &mode, fmt->bpp, link->lanes, link->rate))
d366451bed980ac Andy Yan 2025-04-03 1772 continue;
d366451bed980ac Andy Yan 2025-04-03 1773
d366451bed980ac Andy Yan 2025-04-03 1774 output_fmts[j++] = fmt->bus_format;
d366451bed980ac Andy Yan 2025-04-03 1775 }
d366451bed980ac Andy Yan 2025-04-03 1776
d366451bed980ac Andy Yan 2025-04-03 1777 *num_output_fmts = j;
d366451bed980ac Andy Yan 2025-04-03 1778
d366451bed980ac Andy Yan 2025-04-03 1779 return output_fmts;
d366451bed980ac Andy Yan 2025-04-03 1780 }
d366451bed980ac Andy Yan 2025-04-03 1781
d366451bed980ac Andy Yan 2025-04-03 1782 static const struct drm_bridge_funcs dw_dp_bridge_funcs = {
d366451bed980ac Andy Yan 2025-04-03 1783 .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
d366451bed980ac Andy Yan 2025-04-03 1784 .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
d366451bed980ac Andy Yan 2025-04-03 1785 .atomic_reset = drm_atomic_helper_bridge_reset,
d366451bed980ac Andy Yan 2025-04-03 1786 .atomic_get_input_bus_fmts = drm_atomic_helper_bridge_propagate_bus_fmt,
d366451bed980ac Andy Yan 2025-04-03 1787 .atomic_get_output_bus_fmts = dw_dp_bridge_atomic_get_output_bus_fmts,
d366451bed980ac Andy Yan 2025-04-03 1788 .atomic_check = dw_dp_bridge_atomic_check,
d366451bed980ac Andy Yan 2025-04-03 1789 .mode_valid = dw_dp_bridge_mode_valid,
d366451bed980ac Andy Yan 2025-04-03 @1790 .atomic_enable = dw_dp_bridge_atomic_enable,
d366451bed980ac Andy Yan 2025-04-03 1791 .atomic_disable = dw_dp_bridge_atomic_disable,
d366451bed980ac Andy Yan 2025-04-03 1792 .detect = dw_dp_bridge_detect,
d366451bed980ac Andy Yan 2025-04-03 1793 .edid_read = dw_dp_bridge_edid_read,
d366451bed980ac Andy Yan 2025-04-03 1794 };
d366451bed980ac Andy Yan 2025-04-03 1795
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
Hi Andy,
kernel test robot noticed the following build errors:
[auto build test ERROR on rockchip/for-next]
[also build test ERROR on robh/for-next drm-exynos/exynos-drm-next linus/master v6.14 next-20250404]
[cannot apply to drm/drm-next drm-intel/for-linux-next drm-intel/for-linux-next-fixes drm-misc/drm-misc-next drm-tip/drm-tip]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Andy-Yan/dt-bindings-display-rockchip-Add-schema-for-RK3588-DPTX-Controller/20250403-114203
base: https://git.kernel.org/pub/scm/linux/kernel/git/mmind/linux-rockchip.git for-next
patch link: https://lore.kernel.org/r/20250403033748.245007-4-andyshrk%40163.com
patch subject: [PATCH v3 3/9] drm/rockchip: Add RK3588 DPTX output support
config: s390-allmodconfig (https://download.01.org/0day-ci/archive/20250404/202504041655.rA3EV7B8-lkp@intel.com/config)
compiler: clang version 18.1.8 (https://github.com/llvm/llvm-project 3b5b5c1ec4a3095ab096dd780e84d7ab81f3d7ff)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250404/202504041655.rA3EV7B8-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202504041655.rA3EV7B8-lkp@intel.com/
All errors (new ones prefixed by >>):
>> drivers/gpu/drm/bridge/synopsys/dw-dp.c:1599:3: error: call to undeclared function 'drm_dp_link_power_down'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
1599 | drm_dp_link_power_down(&dp->aux, dp->link.revision);
| ^
>> drivers/gpu/drm/bridge/synopsys/dw-dp.c:1617:8: error: call to undeclared function 'drm_dp_link_power_up'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
1617 | ret = drm_dp_link_power_up(&dp->aux, dp->link.revision);
| ^
>> drivers/gpu/drm/bridge/synopsys/dw-dp.c:1790:19: error: incompatible function pointer types initializing 'void (*)(struct drm_bridge *, struct drm_bridge_state *)' with an expression of type 'void (struct drm_bridge *, struct drm_atomic_state *)' [-Wincompatible-function-pointer-types]
1790 | .atomic_enable = dw_dp_bridge_atomic_enable,
| ^~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/gpu/drm/bridge/synopsys/dw-dp.c:1791:20: error: incompatible function pointer types initializing 'void (*)(struct drm_bridge *, struct drm_bridge_state *)' with an expression of type 'void (struct drm_bridge *, struct drm_atomic_state *)' [-Wincompatible-function-pointer-types]
1791 | .atomic_disable = dw_dp_bridge_atomic_disable,
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~
4 errors generated.
vim +1790 drivers/gpu/drm/bridge/synopsys/dw-dp.c
d366451bed980ac Andy Yan 2025-04-03 1593
d366451bed980ac Andy Yan 2025-04-03 1594 static void dw_dp_link_disable(struct dw_dp *dp)
d366451bed980ac Andy Yan 2025-04-03 1595 {
d366451bed980ac Andy Yan 2025-04-03 1596 struct dw_dp_link *link = &dp->link;
d366451bed980ac Andy Yan 2025-04-03 1597
d366451bed980ac Andy Yan 2025-04-03 1598 if (dw_dp_hpd_detect(dp))
d366451bed980ac Andy Yan 2025-04-03 @1599 drm_dp_link_power_down(&dp->aux, dp->link.revision);
d366451bed980ac Andy Yan 2025-04-03 1600
d366451bed980ac Andy Yan 2025-04-03 1601 dw_dp_phy_xmit_enable(dp, 0);
d366451bed980ac Andy Yan 2025-04-03 1602
d366451bed980ac Andy Yan 2025-04-03 1603 phy_power_off(dp->phy);
d366451bed980ac Andy Yan 2025-04-03 1604
d366451bed980ac Andy Yan 2025-04-03 1605 link->train.clock_recovered = false;
d366451bed980ac Andy Yan 2025-04-03 1606 link->train.channel_equalized = false;
d366451bed980ac Andy Yan 2025-04-03 1607 }
d366451bed980ac Andy Yan 2025-04-03 1608
d366451bed980ac Andy Yan 2025-04-03 1609 static int dw_dp_link_enable(struct dw_dp *dp)
d366451bed980ac Andy Yan 2025-04-03 1610 {
d366451bed980ac Andy Yan 2025-04-03 1611 int ret;
d366451bed980ac Andy Yan 2025-04-03 1612
d366451bed980ac Andy Yan 2025-04-03 1613 ret = phy_power_on(dp->phy);
d366451bed980ac Andy Yan 2025-04-03 1614 if (ret)
d366451bed980ac Andy Yan 2025-04-03 1615 return ret;
d366451bed980ac Andy Yan 2025-04-03 1616
d366451bed980ac Andy Yan 2025-04-03 @1617 ret = drm_dp_link_power_up(&dp->aux, dp->link.revision);
d366451bed980ac Andy Yan 2025-04-03 1618 if (ret < 0)
d366451bed980ac Andy Yan 2025-04-03 1619 return ret;
d366451bed980ac Andy Yan 2025-04-03 1620
d366451bed980ac Andy Yan 2025-04-03 1621 ret = dw_dp_link_train(dp);
d366451bed980ac Andy Yan 2025-04-03 1622
d366451bed980ac Andy Yan 2025-04-03 1623 return ret;
d366451bed980ac Andy Yan 2025-04-03 1624 }
d366451bed980ac Andy Yan 2025-04-03 1625
d366451bed980ac Andy Yan 2025-04-03 1626 static void dw_dp_bridge_atomic_enable(struct drm_bridge *bridge,
d366451bed980ac Andy Yan 2025-04-03 1627 struct drm_atomic_state *state)
d366451bed980ac Andy Yan 2025-04-03 1628 {
d366451bed980ac Andy Yan 2025-04-03 1629 struct dw_dp *dp = bridge_to_dp(bridge);
d366451bed980ac Andy Yan 2025-04-03 1630 struct drm_connector *connector;
d366451bed980ac Andy Yan 2025-04-03 1631 struct drm_connector_state *conn_state;
d366451bed980ac Andy Yan 2025-04-03 1632 int ret;
d366451bed980ac Andy Yan 2025-04-03 1633
d366451bed980ac Andy Yan 2025-04-03 1634 connector = drm_atomic_get_new_connector_for_encoder(state, bridge->encoder);
d366451bed980ac Andy Yan 2025-04-03 1635 if (!connector) {
d366451bed980ac Andy Yan 2025-04-03 1636 dev_err(dp->dev, "failed to get connector\n");
d366451bed980ac Andy Yan 2025-04-03 1637 return;
d366451bed980ac Andy Yan 2025-04-03 1638 }
d366451bed980ac Andy Yan 2025-04-03 1639
d366451bed980ac Andy Yan 2025-04-03 1640 conn_state = drm_atomic_get_new_connector_state(state, connector);
d366451bed980ac Andy Yan 2025-04-03 1641 if (!conn_state) {
d366451bed980ac Andy Yan 2025-04-03 1642 dev_err(dp->dev, "failed to get connector state\n");
d366451bed980ac Andy Yan 2025-04-03 1643 return;
d366451bed980ac Andy Yan 2025-04-03 1644 }
d366451bed980ac Andy Yan 2025-04-03 1645
d366451bed980ac Andy Yan 2025-04-03 1646 set_bit(0, dp->sdp_reg_bank);
d366451bed980ac Andy Yan 2025-04-03 1647
d366451bed980ac Andy Yan 2025-04-03 1648 ret = dw_dp_link_enable(dp);
d366451bed980ac Andy Yan 2025-04-03 1649 if (ret < 0) {
d366451bed980ac Andy Yan 2025-04-03 1650 dev_err(dp->dev, "failed to enable link: %d\n", ret);
d366451bed980ac Andy Yan 2025-04-03 1651 return;
d366451bed980ac Andy Yan 2025-04-03 1652 }
d366451bed980ac Andy Yan 2025-04-03 1653
d366451bed980ac Andy Yan 2025-04-03 1654 ret = dw_dp_video_enable(dp);
d366451bed980ac Andy Yan 2025-04-03 1655 if (ret < 0) {
d366451bed980ac Andy Yan 2025-04-03 1656 dev_err(dp->dev, "failed to enable video: %d\n", ret);
d366451bed980ac Andy Yan 2025-04-03 1657 return;
d366451bed980ac Andy Yan 2025-04-03 1658 }
d366451bed980ac Andy Yan 2025-04-03 1659 }
d366451bed980ac Andy Yan 2025-04-03 1660
d366451bed980ac Andy Yan 2025-04-03 1661 static void dw_dp_reset(struct dw_dp *dp)
d366451bed980ac Andy Yan 2025-04-03 1662 {
d366451bed980ac Andy Yan 2025-04-03 1663 int val;
d366451bed980ac Andy Yan 2025-04-03 1664
d366451bed980ac Andy Yan 2025-04-03 1665 disable_irq(dp->irq);
d366451bed980ac Andy Yan 2025-04-03 1666 regmap_update_bits(dp->regmap, DW_DP_SOFT_RESET_CTRL, CONTROLLER_RESET,
d366451bed980ac Andy Yan 2025-04-03 1667 FIELD_PREP(CONTROLLER_RESET, 1));
d366451bed980ac Andy Yan 2025-04-03 1668 udelay(10);
d366451bed980ac Andy Yan 2025-04-03 1669 regmap_update_bits(dp->regmap, DW_DP_SOFT_RESET_CTRL, CONTROLLER_RESET,
d366451bed980ac Andy Yan 2025-04-03 1670 FIELD_PREP(CONTROLLER_RESET, 0));
d366451bed980ac Andy Yan 2025-04-03 1671
d366451bed980ac Andy Yan 2025-04-03 1672 dw_dp_init_hw(dp);
d366451bed980ac Andy Yan 2025-04-03 1673 regmap_read_poll_timeout(dp->regmap, DW_DP_HPD_STATUS, val,
d366451bed980ac Andy Yan 2025-04-03 1674 FIELD_GET(HPD_HOT_PLUG, val), 200, 200000);
d366451bed980ac Andy Yan 2025-04-03 1675 regmap_write(dp->regmap, DW_DP_HPD_STATUS, HPD_HOT_PLUG);
d366451bed980ac Andy Yan 2025-04-03 1676 enable_irq(dp->irq);
d366451bed980ac Andy Yan 2025-04-03 1677 }
d366451bed980ac Andy Yan 2025-04-03 1678
d366451bed980ac Andy Yan 2025-04-03 1679 static void dw_dp_bridge_atomic_disable(struct drm_bridge *bridge,
d366451bed980ac Andy Yan 2025-04-03 1680 struct drm_atomic_state *state)
d366451bed980ac Andy Yan 2025-04-03 1681 {
d366451bed980ac Andy Yan 2025-04-03 1682 struct dw_dp *dp = bridge_to_dp(bridge);
d366451bed980ac Andy Yan 2025-04-03 1683
d366451bed980ac Andy Yan 2025-04-03 1684 dw_dp_video_disable(dp);
d366451bed980ac Andy Yan 2025-04-03 1685 dw_dp_link_disable(dp);
d366451bed980ac Andy Yan 2025-04-03 1686 bitmap_zero(dp->sdp_reg_bank, SDP_REG_BANK_SIZE);
d366451bed980ac Andy Yan 2025-04-03 1687 dw_dp_reset(dp);
d366451bed980ac Andy Yan 2025-04-03 1688 }
d366451bed980ac Andy Yan 2025-04-03 1689
d366451bed980ac Andy Yan 2025-04-03 1690 static bool dw_dp_hpd_detect_link(struct dw_dp *dp)
d366451bed980ac Andy Yan 2025-04-03 1691 {
d366451bed980ac Andy Yan 2025-04-03 1692 int ret;
d366451bed980ac Andy Yan 2025-04-03 1693
d366451bed980ac Andy Yan 2025-04-03 1694 ret = phy_power_on(dp->phy);
d366451bed980ac Andy Yan 2025-04-03 1695 if (ret < 0)
d366451bed980ac Andy Yan 2025-04-03 1696 return false;
d366451bed980ac Andy Yan 2025-04-03 1697 ret = dw_dp_link_parse(dp);
d366451bed980ac Andy Yan 2025-04-03 1698 phy_power_off(dp->phy);
d366451bed980ac Andy Yan 2025-04-03 1699
d366451bed980ac Andy Yan 2025-04-03 1700 return !ret;
d366451bed980ac Andy Yan 2025-04-03 1701 }
d366451bed980ac Andy Yan 2025-04-03 1702
d366451bed980ac Andy Yan 2025-04-03 1703 static enum drm_connector_status dw_dp_bridge_detect(struct drm_bridge *bridge)
d366451bed980ac Andy Yan 2025-04-03 1704 {
d366451bed980ac Andy Yan 2025-04-03 1705 struct dw_dp *dp = bridge_to_dp(bridge);
d366451bed980ac Andy Yan 2025-04-03 1706
d366451bed980ac Andy Yan 2025-04-03 1707 if (!dw_dp_hpd_detect(dp))
d366451bed980ac Andy Yan 2025-04-03 1708 return connector_status_disconnected;
d366451bed980ac Andy Yan 2025-04-03 1709
d366451bed980ac Andy Yan 2025-04-03 1710 if (!dw_dp_hpd_detect_link(dp))
d366451bed980ac Andy Yan 2025-04-03 1711 return connector_status_disconnected;
d366451bed980ac Andy Yan 2025-04-03 1712
d366451bed980ac Andy Yan 2025-04-03 1713 return connector_status_connected;
d366451bed980ac Andy Yan 2025-04-03 1714 }
d366451bed980ac Andy Yan 2025-04-03 1715
d366451bed980ac Andy Yan 2025-04-03 1716 static const struct drm_edid *dw_dp_bridge_edid_read(struct drm_bridge *bridge,
d366451bed980ac Andy Yan 2025-04-03 1717 struct drm_connector *connector)
d366451bed980ac Andy Yan 2025-04-03 1718 {
d366451bed980ac Andy Yan 2025-04-03 1719 struct dw_dp *dp = bridge_to_dp(bridge);
d366451bed980ac Andy Yan 2025-04-03 1720 const struct drm_edid *edid;
d366451bed980ac Andy Yan 2025-04-03 1721 int ret;
d366451bed980ac Andy Yan 2025-04-03 1722
d366451bed980ac Andy Yan 2025-04-03 1723 ret = phy_power_on(dp->phy);
d366451bed980ac Andy Yan 2025-04-03 1724 if (ret)
d366451bed980ac Andy Yan 2025-04-03 1725 return NULL;
d366451bed980ac Andy Yan 2025-04-03 1726
d366451bed980ac Andy Yan 2025-04-03 1727 edid = drm_edid_read_ddc(connector, &dp->aux.ddc);
d366451bed980ac Andy Yan 2025-04-03 1728
d366451bed980ac Andy Yan 2025-04-03 1729 phy_power_off(dp->phy);
d366451bed980ac Andy Yan 2025-04-03 1730
d366451bed980ac Andy Yan 2025-04-03 1731 return edid;
d366451bed980ac Andy Yan 2025-04-03 1732 }
d366451bed980ac Andy Yan 2025-04-03 1733
d366451bed980ac Andy Yan 2025-04-03 1734 static u32 *dw_dp_bridge_atomic_get_output_bus_fmts(struct drm_bridge *bridge,
d366451bed980ac Andy Yan 2025-04-03 1735 struct drm_bridge_state *bridge_state,
d366451bed980ac Andy Yan 2025-04-03 1736 struct drm_crtc_state *crtc_state,
d366451bed980ac Andy Yan 2025-04-03 1737 struct drm_connector_state *conn_state,
d366451bed980ac Andy Yan 2025-04-03 1738 unsigned int *num_output_fmts)
d366451bed980ac Andy Yan 2025-04-03 1739 {
d366451bed980ac Andy Yan 2025-04-03 1740 struct dw_dp *dp = bridge_to_dp(bridge);
d366451bed980ac Andy Yan 2025-04-03 1741 struct dw_dp_link *link = &dp->link;
d366451bed980ac Andy Yan 2025-04-03 1742 struct drm_display_info *di = &conn_state->connector->display_info;
d366451bed980ac Andy Yan 2025-04-03 1743 struct drm_display_mode mode = crtc_state->mode;
d366451bed980ac Andy Yan 2025-04-03 1744 const struct dw_dp_output_format *fmt;
d366451bed980ac Andy Yan 2025-04-03 1745 u32 i, j = 0;
d366451bed980ac Andy Yan 2025-04-03 1746 u32 *output_fmts;
d366451bed980ac Andy Yan 2025-04-03 1747
d366451bed980ac Andy Yan 2025-04-03 1748 *num_output_fmts = 0;
d366451bed980ac Andy Yan 2025-04-03 1749
d366451bed980ac Andy Yan 2025-04-03 1750 output_fmts = kcalloc(ARRAY_SIZE(dw_dp_output_formats), sizeof(*output_fmts), GFP_KERNEL);
d366451bed980ac Andy Yan 2025-04-03 1751 if (!output_fmts)
d366451bed980ac Andy Yan 2025-04-03 1752 return NULL;
d366451bed980ac Andy Yan 2025-04-03 1753
d366451bed980ac Andy Yan 2025-04-03 1754 for (i = 0; i < ARRAY_SIZE(dw_dp_output_formats); i++) {
d366451bed980ac Andy Yan 2025-04-03 1755 fmt = &dw_dp_output_formats[i];
d366451bed980ac Andy Yan 2025-04-03 1756
d366451bed980ac Andy Yan 2025-04-03 1757 if (fmt->bpc > conn_state->max_bpc)
d366451bed980ac Andy Yan 2025-04-03 1758 continue;
d366451bed980ac Andy Yan 2025-04-03 1759
d366451bed980ac Andy Yan 2025-04-03 1760 if (!(fmt->color_format & di->color_formats))
d366451bed980ac Andy Yan 2025-04-03 1761 continue;
d366451bed980ac Andy Yan 2025-04-03 1762
d366451bed980ac Andy Yan 2025-04-03 1763 if (fmt->color_format == DRM_COLOR_FORMAT_YCBCR420 &&
d366451bed980ac Andy Yan 2025-04-03 1764 !link->vsc_sdp_supported)
d366451bed980ac Andy Yan 2025-04-03 1765 continue;
d366451bed980ac Andy Yan 2025-04-03 1766
d366451bed980ac Andy Yan 2025-04-03 1767 if (fmt->color_format != DRM_COLOR_FORMAT_YCBCR420 &&
d366451bed980ac Andy Yan 2025-04-03 1768 drm_mode_is_420_only(di, &mode))
d366451bed980ac Andy Yan 2025-04-03 1769 continue;
d366451bed980ac Andy Yan 2025-04-03 1770
d366451bed980ac Andy Yan 2025-04-03 1771 if (!dw_dp_bandwidth_ok(dp, &mode, fmt->bpp, link->lanes, link->rate))
d366451bed980ac Andy Yan 2025-04-03 1772 continue;
d366451bed980ac Andy Yan 2025-04-03 1773
d366451bed980ac Andy Yan 2025-04-03 1774 output_fmts[j++] = fmt->bus_format;
d366451bed980ac Andy Yan 2025-04-03 1775 }
d366451bed980ac Andy Yan 2025-04-03 1776
d366451bed980ac Andy Yan 2025-04-03 1777 *num_output_fmts = j;
d366451bed980ac Andy Yan 2025-04-03 1778
d366451bed980ac Andy Yan 2025-04-03 1779 return output_fmts;
d366451bed980ac Andy Yan 2025-04-03 1780 }
d366451bed980ac Andy Yan 2025-04-03 1781
d366451bed980ac Andy Yan 2025-04-03 1782 static const struct drm_bridge_funcs dw_dp_bridge_funcs = {
d366451bed980ac Andy Yan 2025-04-03 1783 .atomic_duplicate_state = drm_atomic_helper_bridge_duplicate_state,
d366451bed980ac Andy Yan 2025-04-03 1784 .atomic_destroy_state = drm_atomic_helper_bridge_destroy_state,
d366451bed980ac Andy Yan 2025-04-03 1785 .atomic_reset = drm_atomic_helper_bridge_reset,
d366451bed980ac Andy Yan 2025-04-03 1786 .atomic_get_input_bus_fmts = drm_atomic_helper_bridge_propagate_bus_fmt,
d366451bed980ac Andy Yan 2025-04-03 1787 .atomic_get_output_bus_fmts = dw_dp_bridge_atomic_get_output_bus_fmts,
d366451bed980ac Andy Yan 2025-04-03 1788 .atomic_check = dw_dp_bridge_atomic_check,
d366451bed980ac Andy Yan 2025-04-03 1789 .mode_valid = dw_dp_bridge_mode_valid,
d366451bed980ac Andy Yan 2025-04-03 @1790 .atomic_enable = dw_dp_bridge_atomic_enable,
d366451bed980ac Andy Yan 2025-04-03 1791 .atomic_disable = dw_dp_bridge_atomic_disable,
d366451bed980ac Andy Yan 2025-04-03 1792 .detect = dw_dp_bridge_detect,
d366451bed980ac Andy Yan 2025-04-03 1793 .edid_read = dw_dp_bridge_edid_read,
d366451bed980ac Andy Yan 2025-04-03 1794 };
d366451bed980ac Andy Yan 2025-04-03 1795
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
© 2016 - 2026 Red Hat, Inc.