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 <algea.cao@rock-chips.com>
Signed-off-by: Guochun Huang <hero.huang@rock-chips.com>
This commit is contained in:
Algea Cao
2020-09-17 10:53:13 +08:00
committed by 黄国椿
parent 4cf554ae0f
commit 7b772c40d3
2 changed files with 79 additions and 24 deletions

View File

@@ -247,6 +247,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;
@@ -1930,14 +1931,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);
@@ -3593,10 +3596,22 @@ static int dw_hdmi_bridge_attach(struct drm_bridge *bridge,
enum drm_bridge_attach_flags flags)
{
struct dw_hdmi *hdmi = bridge->driver_private;
int ret;
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
return 0;
if (hdmi->next_bridge) {
hdmi->next_bridge->encoder = bridge->encoder;
ret = drm_bridge_attach(bridge->encoder, hdmi->next_bridge, bridge, flags);
if (ret) {
DRM_ERROR("Failed to attach bridge with dw-hdmi\n");
return ret;
}
return 0;
}
return dw_hdmi_connector_create(hdmi);
}
@@ -3620,10 +3635,12 @@ dw_hdmi_bridge_mode_valid(struct drm_bridge *bridge,
const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
enum drm_mode_status mode_status = MODE_OK;
if (hdmi->next_bridge)
return MODE_OK;
if (pdata->mode_valid)
mode_status = pdata->mode_valid(connector, pdata->priv_data,
info, mode);
return mode_status;
}
@@ -3832,8 +3849,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) {
@@ -4340,6 +4359,7 @@ struct dw_hdmi *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;
@@ -4572,6 +4592,30 @@ struct dw_hdmi *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;
}
memset(&pdevinfo, 0, sizeof(pdevinfo));
pdevinfo.parent = dev;
pdevinfo.id = PLATFORM_DEVID_AUTO;
@@ -4697,9 +4741,13 @@ 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);
clk_disable_unprepare(hdmi->iahb_clk);
clk_disable_unprepare(hdmi->isfr_clk);
@@ -4733,7 +4781,9 @@ struct dw_hdmi *dw_hdmi_bind(struct platform_device *pdev,
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;
}
@@ -4759,12 +4809,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);
}
}
}

View File

@@ -1686,9 +1686,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;
}
@@ -1698,7 +1700,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);