mirror of
https://github.com/Fishwaldo/build.git
synced 2025-03-27 01:02:19 +00:00
251 lines
8.6 KiB
Diff
251 lines
8.6 KiB
Diff
From 3fdd6b3e658373e45ecbe36093403a30bd309df5 Mon Sep 17 00:00:00 2001
|
|
From: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
|
|
Date: Fri, 3 Mar 2017 19:20:04 +0200
|
|
Subject: [PATCH 37/93] drm: bridge: dw-hdmi: Add support for custom PHY
|
|
configuration
|
|
|
|
The DWC HDMI TX controller interfaces with a companion PHY. While
|
|
Synopsys provides multiple standard PHYs, SoC vendors can also integrate
|
|
a custom PHY.
|
|
|
|
Modularize PHY configuration to support vendor PHYs through platform
|
|
data. The existing PHY configuration code was originally written to
|
|
support the DWC HDMI 3D TX PHY, and seems to be compatible with the DWC
|
|
MLP PHY. The HDMI 2.0 PHY will require a separate configuration
|
|
function.
|
|
|
|
Signed-off-by: Kieran Bingham <kieran.bingham+renesas@ideasonboard.com>
|
|
Signed-off-by: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
|
|
Tested-by: Neil Armstrong <narmstrong@baylibre.com>
|
|
Reviewed-by: Jose Abreu <Jose.Abreu@synopsys.com>
|
|
Signed-off-by: Archit Taneja <architt@codeaurora.org>
|
|
Link: http://patchwork.freedesktop.org/patch/msgid/20170303172007.26541-8-laurent.pinchart+renesas@ideasonboard.com
|
|
---
|
|
drivers/gpu/drm/bridge/dw-hdmi.c | 109 ++++++++++++++++++++++++++-------------
|
|
include/drm/bridge/dw_hdmi.h | 7 +++
|
|
2 files changed, 81 insertions(+), 35 deletions(-)
|
|
|
|
diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c
|
|
index cb27038..b835d81 100644
|
|
--- a/drivers/gpu/drm/bridge/dw-hdmi.c
|
|
+++ b/drivers/gpu/drm/bridge/dw-hdmi.c
|
|
@@ -118,6 +118,9 @@ struct dw_hdmi_phy_data {
|
|
const char *name;
|
|
unsigned int gen;
|
|
bool has_svsret;
|
|
+ int (*configure)(struct dw_hdmi *hdmi,
|
|
+ const struct dw_hdmi_plat_data *pdata,
|
|
+ unsigned long mpixelclock);
|
|
};
|
|
|
|
struct dw_hdmi {
|
|
@@ -860,8 +863,8 @@ static bool hdmi_phy_wait_i2c_done(struct dw_hdmi *hdmi, int msec)
|
|
return true;
|
|
}
|
|
|
|
-static void hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data,
|
|
- unsigned char addr)
|
|
+void dw_hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data,
|
|
+ unsigned char addr)
|
|
{
|
|
hdmi_writeb(hdmi, 0xFF, HDMI_IH_I2CMPHY_STAT0);
|
|
hdmi_writeb(hdmi, addr, HDMI_PHY_I2CM_ADDRESS_ADDR);
|
|
@@ -873,6 +876,7 @@ static void hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data,
|
|
HDMI_PHY_I2CM_OPERATION_ADDR);
|
|
hdmi_phy_wait_i2c_done(hdmi, 1000);
|
|
}
|
|
+EXPORT_SYMBOL_GPL(dw_hdmi_phy_i2c_write);
|
|
|
|
static void dw_hdmi_phy_enable_powerdown(struct dw_hdmi *hdmi, bool enable)
|
|
{
|
|
@@ -993,37 +997,67 @@ static int dw_hdmi_phy_power_on(struct dw_hdmi *hdmi)
|
|
return 0;
|
|
}
|
|
|
|
-static int hdmi_phy_configure(struct dw_hdmi *hdmi)
|
|
+/*
|
|
+ * PHY configuration function for the DWC HDMI 3D TX PHY. Based on the available
|
|
+ * information the DWC MHL PHY has the same register layout and is thus also
|
|
+ * supported by this function.
|
|
+ */
|
|
+static int hdmi_phy_configure_dwc_hdmi_3d_tx(struct dw_hdmi *hdmi,
|
|
+ const struct dw_hdmi_plat_data *pdata,
|
|
+ unsigned long mpixelclock)
|
|
{
|
|
- const struct dw_hdmi_phy_data *phy = hdmi->phy.data;
|
|
- const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
|
|
const struct dw_hdmi_mpll_config *mpll_config = pdata->mpll_cfg;
|
|
const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr;
|
|
const struct dw_hdmi_phy_config *phy_config = pdata->phy_config;
|
|
|
|
/* PLL/MPLL Cfg - always match on final entry */
|
|
for (; mpll_config->mpixelclock != ~0UL; mpll_config++)
|
|
- if (hdmi->hdmi_data.video_mode.mpixelclock <=
|
|
- mpll_config->mpixelclock)
|
|
+ if (mpixelclock <= mpll_config->mpixelclock)
|
|
break;
|
|
|
|
for (; curr_ctrl->mpixelclock != ~0UL; curr_ctrl++)
|
|
- if (hdmi->hdmi_data.video_mode.mpixelclock <=
|
|
- curr_ctrl->mpixelclock)
|
|
+ if (mpixelclock <= curr_ctrl->mpixelclock)
|
|
break;
|
|
|
|
for (; phy_config->mpixelclock != ~0UL; phy_config++)
|
|
- if (hdmi->hdmi_data.video_mode.mpixelclock <=
|
|
- phy_config->mpixelclock)
|
|
+ if (mpixelclock <= phy_config->mpixelclock)
|
|
break;
|
|
|
|
if (mpll_config->mpixelclock == ~0UL ||
|
|
curr_ctrl->mpixelclock == ~0UL ||
|
|
- phy_config->mpixelclock == ~0UL) {
|
|
- dev_err(hdmi->dev, "Pixel clock %d - unsupported by HDMI\n",
|
|
- hdmi->hdmi_data.video_mode.mpixelclock);
|
|
+ phy_config->mpixelclock == ~0UL)
|
|
return -EINVAL;
|
|
- }
|
|
+
|
|
+ dw_hdmi_phy_i2c_write(hdmi, mpll_config->res[0].cpce,
|
|
+ HDMI_3D_TX_PHY_CPCE_CTRL);
|
|
+ dw_hdmi_phy_i2c_write(hdmi, mpll_config->res[0].gmp,
|
|
+ HDMI_3D_TX_PHY_GMPCTRL);
|
|
+ dw_hdmi_phy_i2c_write(hdmi, curr_ctrl->curr[0],
|
|
+ HDMI_3D_TX_PHY_CURRCTRL);
|
|
+
|
|
+ dw_hdmi_phy_i2c_write(hdmi, 0, HDMI_3D_TX_PHY_PLLPHBYCTRL);
|
|
+ dw_hdmi_phy_i2c_write(hdmi, HDMI_3D_TX_PHY_MSM_CTRL_CKO_SEL_FB_CLK,
|
|
+ HDMI_3D_TX_PHY_MSM_CTRL);
|
|
+
|
|
+ dw_hdmi_phy_i2c_write(hdmi, phy_config->term, HDMI_3D_TX_PHY_TXTERM);
|
|
+ dw_hdmi_phy_i2c_write(hdmi, phy_config->sym_ctr,
|
|
+ HDMI_3D_TX_PHY_CKSYMTXCTRL);
|
|
+ dw_hdmi_phy_i2c_write(hdmi, phy_config->vlev_ctr,
|
|
+ HDMI_3D_TX_PHY_VLEVCTRL);
|
|
+
|
|
+ /* Override and disable clock termination. */
|
|
+ dw_hdmi_phy_i2c_write(hdmi, HDMI_3D_TX_PHY_CKCALCTRL_OVERRIDE,
|
|
+ HDMI_3D_TX_PHY_CKCALCTRL);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int hdmi_phy_configure(struct dw_hdmi *hdmi)
|
|
+{
|
|
+ const struct dw_hdmi_phy_data *phy = hdmi->phy.data;
|
|
+ const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
|
|
+ unsigned long mpixelclock = hdmi->hdmi_data.video_mode.mpixelclock;
|
|
+ int ret;
|
|
|
|
dw_hdmi_phy_power_off(hdmi);
|
|
|
|
@@ -1042,26 +1076,16 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi)
|
|
HDMI_PHY_I2CM_SLAVE_ADDR);
|
|
hdmi_phy_test_clear(hdmi, 0);
|
|
|
|
- hdmi_phy_i2c_write(hdmi, mpll_config->res[0].cpce,
|
|
- HDMI_3D_TX_PHY_CPCE_CTRL);
|
|
- hdmi_phy_i2c_write(hdmi, mpll_config->res[0].gmp,
|
|
- HDMI_3D_TX_PHY_GMPCTRL);
|
|
- hdmi_phy_i2c_write(hdmi, curr_ctrl->curr[0],
|
|
- HDMI_3D_TX_PHY_CURRCTRL);
|
|
-
|
|
- hdmi_phy_i2c_write(hdmi, 0, HDMI_3D_TX_PHY_PLLPHBYCTRL);
|
|
- hdmi_phy_i2c_write(hdmi, HDMI_3D_TX_PHY_MSM_CTRL_CKO_SEL_FB_CLK,
|
|
- HDMI_3D_TX_PHY_MSM_CTRL);
|
|
-
|
|
- hdmi_phy_i2c_write(hdmi, phy_config->term, HDMI_3D_TX_PHY_TXTERM);
|
|
- hdmi_phy_i2c_write(hdmi, phy_config->sym_ctr,
|
|
- HDMI_3D_TX_PHY_CKSYMTXCTRL);
|
|
- hdmi_phy_i2c_write(hdmi, phy_config->vlev_ctr,
|
|
- HDMI_3D_TX_PHY_VLEVCTRL);
|
|
-
|
|
- /* Override and disable clock termination. */
|
|
- hdmi_phy_i2c_write(hdmi, HDMI_3D_TX_PHY_CKCALCTRL_OVERRIDE,
|
|
- HDMI_3D_TX_PHY_CKCALCTRL);
|
|
+ /* Write to the PHY as configured by the platform */
|
|
+ if (pdata->configure_phy)
|
|
+ ret = pdata->configure_phy(hdmi, pdata, mpixelclock);
|
|
+ else
|
|
+ ret = phy->configure(hdmi, pdata, mpixelclock);
|
|
+ if (ret) {
|
|
+ dev_err(hdmi->dev, "PHY configuration failed (clock %lu)\n",
|
|
+ mpixelclock);
|
|
+ return ret;
|
|
+ }
|
|
|
|
return dw_hdmi_phy_power_on(hdmi);
|
|
}
|
|
@@ -1895,24 +1919,31 @@ static irqreturn_t dw_hdmi_irq(int irq, void *dev_id)
|
|
.name = "DWC MHL PHY + HEAC PHY",
|
|
.gen = 2,
|
|
.has_svsret = true,
|
|
+ .configure = hdmi_phy_configure_dwc_hdmi_3d_tx,
|
|
}, {
|
|
.type = DW_HDMI_PHY_DWC_MHL_PHY,
|
|
.name = "DWC MHL PHY",
|
|
.gen = 2,
|
|
.has_svsret = true,
|
|
+ .configure = hdmi_phy_configure_dwc_hdmi_3d_tx,
|
|
}, {
|
|
.type = DW_HDMI_PHY_DWC_HDMI_3D_TX_PHY_HEAC,
|
|
.name = "DWC HDMI 3D TX PHY + HEAC PHY",
|
|
.gen = 2,
|
|
+ .configure = hdmi_phy_configure_dwc_hdmi_3d_tx,
|
|
}, {
|
|
.type = DW_HDMI_PHY_DWC_HDMI_3D_TX_PHY,
|
|
.name = "DWC HDMI 3D TX PHY",
|
|
.gen = 2,
|
|
+ .configure = hdmi_phy_configure_dwc_hdmi_3d_tx,
|
|
}, {
|
|
.type = DW_HDMI_PHY_DWC_HDMI20_TX_PHY,
|
|
.name = "DWC HDMI 2.0 TX PHY",
|
|
.gen = 2,
|
|
.has_svsret = true,
|
|
+ }, {
|
|
+ .type = DW_HDMI_PHY_VENDOR_PHY,
|
|
+ .name = "Vendor PHY",
|
|
}
|
|
};
|
|
|
|
@@ -1943,6 +1974,14 @@ static int dw_hdmi_detect_phy(struct dw_hdmi *hdmi)
|
|
hdmi->phy.ops = &dw_hdmi_synopsys_phy_ops;
|
|
hdmi->phy.name = dw_hdmi_phys[i].name;
|
|
hdmi->phy.data = (void *)&dw_hdmi_phys[i];
|
|
+
|
|
+ if (!dw_hdmi_phys[i].configure &&
|
|
+ !hdmi->plat_data->configure_phy) {
|
|
+ dev_err(hdmi->dev, "%s requires platform support\n",
|
|
+ hdmi->phy.name);
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
return 0;
|
|
}
|
|
}
|
|
diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h
|
|
index 0f583ca..dd33025 100644
|
|
--- a/include/drm/bridge/dw_hdmi.h
|
|
+++ b/include/drm/bridge/dw_hdmi.h
|
|
@@ -78,6 +78,9 @@ struct dw_hdmi_plat_data {
|
|
const struct dw_hdmi_mpll_config *mpll_cfg;
|
|
const struct dw_hdmi_curr_ctrl *cur_ctr;
|
|
const struct dw_hdmi_phy_config *phy_config;
|
|
+ int (*configure_phy)(struct dw_hdmi *hdmi,
|
|
+ const struct dw_hdmi_plat_data *pdata,
|
|
+ unsigned long mpixelclock);
|
|
};
|
|
|
|
int dw_hdmi_probe(struct platform_device *pdev,
|
|
@@ -91,4 +94,8 @@ int dw_hdmi_bind(struct platform_device *pdev, struct drm_encoder *encoder,
|
|
void dw_hdmi_audio_enable(struct dw_hdmi *hdmi);
|
|
void dw_hdmi_audio_disable(struct dw_hdmi *hdmi);
|
|
|
|
+/* PHY configuration */
|
|
+void dw_hdmi_phy_i2c_write(struct dw_hdmi *hdmi, unsigned short data,
|
|
+ unsigned char addr);
|
|
+
|
|
#endif /* __IMX_HDMI_H__ */
|
|
--
|
|
1.9.1
|
|
|