From 8fa15164798bb612d9fd2be4f92305b36e3bc3fd Mon Sep 17 00:00:00 2001 From: Algea Cao Date: Thu, 17 Sep 2020 10:53:13 +0800 Subject: [PATCH] drm/bridge: synopsys: dw-hdmi: Support dw-hdmi does not serve as a connector If dw-hdmi is not used as the final output port, it is only used as a bridge but not a connector. Change-Id: Ie730f47d6075db74c0c54374849fd938c13f5ba8 Signed-off-by: Algea Cao --- drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 109 +++++++++++++++----- drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c | 11 +- 2 files changed, 89 insertions(+), 31 deletions(-) diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c index 9a08b48e83e5..d6f28431021f 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c @@ -260,6 +260,7 @@ struct dw_hdmi_phy_data { struct dw_hdmi { struct drm_connector connector; struct drm_bridge bridge; + struct drm_bridge *next_bridge; struct platform_device *hdcp_dev; unsigned int version; @@ -1749,14 +1750,16 @@ void dw_hdmi_phy_setup_hpd(struct dw_hdmi *hdmi, void *data) hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE, HDMI_IH_PHY_STAT0); - /* Enable cable hot plug irq. */ - hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0); + if (!hdmi->next_bridge) { + /* Enable cable hot plug irq. */ + hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0); - /* Clear and unmute interrupts. */ - hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE, - HDMI_IH_PHY_STAT0); - hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE), - HDMI_IH_MUTE_PHY_STAT0); + /* Clear and unmute interrupts. */ + hdmi_writeb(hdmi, HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE, + HDMI_IH_PHY_STAT0); + hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE), + HDMI_IH_MUTE_PHY_STAT0); + } } EXPORT_SYMBOL_GPL(dw_hdmi_phy_setup_hpd); @@ -2964,18 +2967,32 @@ static int dw_hdmi_bridge_attach(struct drm_bridge *bridge) struct dw_hdmi *hdmi = bridge->driver_private; struct drm_encoder *encoder = bridge->encoder; struct drm_connector *connector = &hdmi->connector; + int ret; - connector->interlace_allowed = 1; - connector->polled = DRM_CONNECTOR_POLL_HPD; + if (!hdmi->next_bridge) { + connector->interlace_allowed = 1; + connector->polled = DRM_CONNECTOR_POLL_HPD; - drm_connector_helper_add(connector, &dw_hdmi_connector_helper_funcs); + drm_connector_helper_add(connector, &dw_hdmi_connector_helper_funcs); - drm_connector_init(bridge->dev, connector, &dw_hdmi_connector_funcs, - DRM_MODE_CONNECTOR_HDMIA); + drm_connector_init(bridge->dev, connector, &dw_hdmi_connector_funcs, + DRM_MODE_CONNECTOR_HDMIA); - drm_connector_attach_encoder(connector, encoder); + drm_connector_attach_encoder(connector, encoder); - dw_hdmi_attach_properties(hdmi); + dw_hdmi_attach_properties(hdmi); + + return 0; + } + + hdmi->next_bridge->encoder = bridge->encoder; + ret = drm_bridge_attach(bridge->encoder, hdmi->next_bridge, bridge); + if (ret) { + DRM_ERROR("Failed to attach bridge with dw-hdmi\n"); + return ret; + } + + bridge->next = hdmi->next_bridge; return 0; } @@ -2988,6 +3005,9 @@ dw_hdmi_bridge_mode_valid(struct drm_bridge *bridge, struct drm_connector *connector = &hdmi->connector; enum drm_mode_status mode_status = MODE_OK; + if (hdmi->next_bridge) + return MODE_OK; + if (hdmi->plat_data->mode_valid) mode_status = hdmi->plat_data->mode_valid(connector, mode); @@ -3154,8 +3174,10 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id) check_hdmi_irq(hdmi, intr_stat, phy_int_pol); hdmi_writeb(hdmi, intr_stat, HDMI_IH_PHY_STAT0); - hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | HDMI_IH_PHY_STAT0_RX_SENSE), - HDMI_IH_MUTE_PHY_STAT0); + if (!hdmi->next_bridge) + hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | + HDMI_IH_PHY_STAT0_RX_SENSE), + HDMI_IH_MUTE_PHY_STAT0); hdcp_stat = hdmi_readb(hdmi, HDMI_A_APIINTSTAT); if (hdcp_stat) { @@ -3608,6 +3630,7 @@ __dw_hdmi_probe(struct platform_device *pdev, { struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; + struct device_node *endpoint; struct platform_device_info pdevinfo; struct device_node *ddc_node; struct dw_hdmi_cec_data cec; @@ -3819,6 +3842,30 @@ __dw_hdmi_probe(struct platform_device *pdev, hdmi->bridge.of_node = pdev->dev.of_node; #endif + endpoint = of_graph_get_endpoint_by_regs(hdmi->dev->of_node, 1, -1); + if (endpoint && of_device_is_available(endpoint)) { + struct device_node *remote; + + remote = of_graph_get_remote_port_parent(endpoint); + of_node_put(endpoint); + if (!remote || !of_device_is_available(remote)) { + of_node_put(remote); + ret = -ENODEV; + goto err_iahb; + } + + hdmi->next_bridge = of_drm_find_bridge(remote); + of_node_put(remote); + if (!hdmi->next_bridge) { + dev_err(hdmi->dev, "can't find next bridge\n"); + ret = -EPROBE_DEFER; + goto err_iahb; + } + + hdmi->sink_is_hdmi = true; + hdmi->sink_has_audio = true; + } + dw_hdmi_setup_i2c(hdmi); if (hdmi->phy.ops->setup_hpd) hdmi->phy.ops->setup_hpd(hdmi, hdmi->phy.data); @@ -3960,9 +4007,13 @@ static void __dw_hdmi_remove(struct dw_hdmi *hdmi) /* Disable all interrupts */ hdmi_writeb(hdmi, ~0, HDMI_IH_MUTE_PHY_STAT0); - dw_hdmi_destroy_properties(hdmi); - hdmi->connector.funcs->destroy(&hdmi->connector); - hdmi->bridge.encoder->funcs->destroy(hdmi->bridge.encoder); + if (!hdmi->next_bridge) { + dw_hdmi_destroy_properties(hdmi); + hdmi->connector.funcs->destroy(&hdmi->connector); + } + + if (hdmi->bridge.encoder) + hdmi->bridge.encoder->funcs->destroy(hdmi->bridge.encoder); if (hdmi->cec_notifier) cec_notifier_put(hdmi->cec_notifier); @@ -4020,11 +4071,13 @@ struct dw_hdmi *dw_hdmi_bind(struct platform_device *pdev, ret = drm_bridge_attach(encoder, &hdmi->bridge, NULL); if (ret) { - dw_hdmi_remove(hdmi); + __dw_hdmi_remove(hdmi); DRM_ERROR("Failed to initialize bridge with drm\n"); return ERR_PTR(ret); } - plat_data->connector = &hdmi->connector; + + if (!hdmi->next_bridge) + plat_data->connector = &hdmi->connector; return hdmi; } @@ -4047,12 +4100,14 @@ static void dw_hdmi_reg_initial(struct dw_hdmi *hdmi) HDMI_PHY_I2CM_CTLINT_ADDR_ARBITRATION_POL, HDMI_PHY_I2CM_CTLINT_ADDR); - hdmi_writeb(hdmi, HDMI_PHY_HPD | HDMI_PHY_RX_SENSE, - HDMI_PHY_POL0); - hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0); - hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | - HDMI_IH_PHY_STAT0_RX_SENSE), - HDMI_IH_MUTE_PHY_STAT0); + if (!hdmi->next_bridge) { + hdmi_writeb(hdmi, HDMI_PHY_HPD | HDMI_PHY_RX_SENSE, + HDMI_PHY_POL0); + hdmi_writeb(hdmi, hdmi->phy_mask, HDMI_PHY_MASK0); + hdmi_writeb(hdmi, ~(HDMI_IH_PHY_STAT0_HPD | + HDMI_IH_PHY_STAT0_RX_SENSE), + HDMI_IH_MUTE_PHY_STAT0); + } } } diff --git a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c index 7fa35d26243b..9dd145e06202 100644 --- a/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c +++ b/drivers/gpu/drm/rockchip/dw_hdmi-rockchip.c @@ -1534,9 +1534,11 @@ static int dw_hdmi_rockchip_bind(struct device *dev, struct device *master, clk_disable_unprepare(hdmi->hclk_vop); } - hdmi->sub_dev.connector = plat_data->connector; - hdmi->sub_dev.of_node = dev->of_node; - rockchip_drm_register_sub_dev(&hdmi->sub_dev); + if (plat_data->connector) { + hdmi->sub_dev.connector = plat_data->connector; + hdmi->sub_dev.of_node = dev->of_node; + rockchip_drm_register_sub_dev(&hdmi->sub_dev); + } return ret; } @@ -1546,7 +1548,8 @@ static void dw_hdmi_rockchip_unbind(struct device *dev, struct device *master, { struct rockchip_hdmi *hdmi = dev_get_drvdata(dev); - rockchip_drm_unregister_sub_dev(&hdmi->sub_dev); + if (hdmi->sub_dev.connector) + rockchip_drm_unregister_sub_dev(&hdmi->sub_dev); dw_hdmi_unbind(hdmi->hdmi); clk_disable_unprepare(hdmi->phyref_clk); clk_disable_unprepare(hdmi->hclk_vop);