@@ -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 {
@@ -878,8 +881,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);
@@ -891,6 +894,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)
{
@@ -966,37 +970,62 @@ static void dw_hdmi_phy_power_on(struct dw_hdmi *hdmi)
}
}
-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)
{
- u8 val, msec;
- 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, 0x06);
+ dw_hdmi_phy_i2c_write(hdmi, mpll_config->res[0].gmp, 0x15);
+
+ /* CURRCTRL */
+ dw_hdmi_phy_i2c_write(hdmi, curr_ctrl->curr[0], 0x10);
+
+ dw_hdmi_phy_i2c_write(hdmi, 0x0000, 0x13); /* PLLPHBYCTRL */
+ dw_hdmi_phy_i2c_write(hdmi, 0x0006, 0x17);
+
+ dw_hdmi_phy_i2c_write(hdmi, phy_config->term, 0x19); /* TXTERM */
+ dw_hdmi_phy_i2c_write(hdmi, phy_config->sym_ctr, 0x09); /* CKSYMTXCTRL */
+ dw_hdmi_phy_i2c_write(hdmi, phy_config->vlev_ctr, 0x0E); /* VLEVCTRL */
+
+ /* REMOVE CLK TERM */
+ dw_hdmi_phy_i2c_write(hdmi, 0x8000, 0x05); /* CKCALCTRL */
+
+ return 0;
+}
+
+static int hdmi_phy_configure(struct dw_hdmi *hdmi)
+{
+ const struct dw_hdmi_plat_data *pdata = hdmi->plat_data;
+ unsigned long mpixelclock = hdmi->hdmi_data.video_mode.mpixelclock;
+ u8 val, msec;
+ int ret;
dw_hdmi_phy_power_off(hdmi);
@@ -1011,21 +1040,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, 0x06);
- hdmi_phy_i2c_write(hdmi, mpll_config->res[0].gmp, 0x15);
-
- /* CURRCTRL */
- hdmi_phy_i2c_write(hdmi, curr_ctrl->curr[0], 0x10);
-
- hdmi_phy_i2c_write(hdmi, 0x0000, 0x13); /* PLLPHBYCTRL */
- hdmi_phy_i2c_write(hdmi, 0x0006, 0x17);
-
- hdmi_phy_i2c_write(hdmi, phy_config->term, 0x19); /* TXTERM */
- hdmi_phy_i2c_write(hdmi, phy_config->sym_ctr, 0x09); /* CKSYMTXCTRL */
- hdmi_phy_i2c_write(hdmi, phy_config->vlev_ctr, 0x0E); /* VLEVCTRL */
-
- /* REMOVE CLK TERM */
- hdmi_phy_i2c_write(hdmi, 0x8000, 0x05); /* CKCALCTRL */
+ /* Write to the PHY as configured by the platform */
+ if (pdata->configure_phy)
+ ret = pdata->configure_phy(hdmi, pdata, mpixelclock);
+ else
+ ret = hdmi->phy->configure(hdmi, pdata, mpixelclock);
+ if (ret) {
+ dev_err(hdmi->dev, "PHY configuration failed (clock %lu)\n",
+ mpixelclock);
+ return ret;
+ }
dw_hdmi_phy_power_on(hdmi);
@@ -1864,24 +1888,31 @@ static const struct dw_hdmi_phy_data dw_hdmi_phys[] = {
.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",
}
};
@@ -1895,17 +1926,23 @@ static int dw_hdmi_detect_phy(struct dw_hdmi *hdmi)
for (i = 0; i < ARRAY_SIZE(dw_hdmi_phys); ++i) {
if (dw_hdmi_phys[i].type == phy_type) {
hdmi->phy = &dw_hdmi_phys[i];
- return 0;
+ break;
}
}
- if (phy_type == DW_HDMI_PHY_VENDOR_PHY)
- dev_err(hdmi->dev, "Unsupported vendor HDMI PHY\n");
- else
+ if (!hdmi->phy) {
dev_err(hdmi->dev, "Unsupported HDMI PHY type (%02x)\n",
phy_type);
+ return -ENODEV;
+ }
+
+ if (!hdmi->phy->configure && !hdmi->plat_data->configure_phy) {
+ dev_err(hdmi->dev, "%s requires platform support\n",
+ hdmi->phy->name);
+ return -ENODEV;
+ }
- return -ENODEV;
+ return 0;
}
static struct dw_hdmi *
@@ -62,6 +62,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);
enum drm_mode_status (*mode_valid)(struct drm_connector *connector,
struct drm_display_mode *mode);
};
@@ -77,4 +80,8 @@ void dw_hdmi_set_sample_rate(struct dw_hdmi *hdmi, unsigned int rate);
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__ */