Commit f1585f6e authored by Laurent Pinchart's avatar Laurent Pinchart Committed by Archit Taneja

drm: bridge: dw-hdmi: Create PHY operations

The HDMI TX controller support different PHYs whose programming
interface can vary significantly, especially with vendor PHYs that are
not provided by Synopsys. To support them, create a PHY operation
structure that can be provided by the platform glue layer. The existing
PHY handling code (limited to Synopsys PHY support) is refactored into a
set of default PHY operations that are used automatically when the
platform glue doesn't provide its own operations.
Signed-off-by: default avatarLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Tested-by: default avatarNeil Armstrong <narmstrong@baylibre.com>
Reviewed-by: default avatarJose Abreu <joabreu@synopsys.com>
Signed-off-by: default avatarArchit Taneja <architt@codeaurora.org>
Link: http://patchwork.freedesktop.org/patch/msgid/20170305233615.11993-1-laurent.pinchart+renesas@ideasonboard.com
parent 181e0ef0
...@@ -141,8 +141,12 @@ struct dw_hdmi { ...@@ -141,8 +141,12 @@ struct dw_hdmi {
u8 edid[HDMI_EDID_LEN]; u8 edid[HDMI_EDID_LEN];
bool cable_plugin; bool cable_plugin;
const struct dw_hdmi_phy_data *phy; struct {
bool phy_enabled; const struct dw_hdmi_phy_ops *ops;
const char *name;
void *data;
bool enabled;
} phy;
struct drm_display_mode previous_mode; struct drm_display_mode previous_mode;
...@@ -831,6 +835,10 @@ static void hdmi_video_packetize(struct dw_hdmi *hdmi) ...@@ -831,6 +835,10 @@ static void hdmi_video_packetize(struct dw_hdmi *hdmi)
HDMI_VP_CONF); HDMI_VP_CONF);
} }
/* -----------------------------------------------------------------------------
* Synopsys PHY Handling
*/
static inline void hdmi_phy_test_clear(struct dw_hdmi *hdmi, static inline void hdmi_phy_test_clear(struct dw_hdmi *hdmi,
unsigned char bit) unsigned char bit)
{ {
...@@ -917,7 +925,7 @@ static void dw_hdmi_phy_sel_interface_control(struct dw_hdmi *hdmi, u8 enable) ...@@ -917,7 +925,7 @@ static void dw_hdmi_phy_sel_interface_control(struct dw_hdmi *hdmi, u8 enable)
static void dw_hdmi_phy_power_off(struct dw_hdmi *hdmi) static void dw_hdmi_phy_power_off(struct dw_hdmi *hdmi)
{ {
const struct dw_hdmi_phy_data *phy = hdmi->phy; const struct dw_hdmi_phy_data *phy = hdmi->phy.data;
unsigned int i; unsigned int i;
u16 val; u16 val;
...@@ -951,7 +959,7 @@ static void dw_hdmi_phy_power_off(struct dw_hdmi *hdmi) ...@@ -951,7 +959,7 @@ static void dw_hdmi_phy_power_off(struct dw_hdmi *hdmi)
static int dw_hdmi_phy_power_on(struct dw_hdmi *hdmi) static int dw_hdmi_phy_power_on(struct dw_hdmi *hdmi)
{ {
const struct dw_hdmi_phy_data *phy = hdmi->phy; const struct dw_hdmi_phy_data *phy = hdmi->phy.data;
unsigned int i; unsigned int i;
u8 val; u8 val;
...@@ -987,6 +995,7 @@ static int dw_hdmi_phy_power_on(struct dw_hdmi *hdmi) ...@@ -987,6 +995,7 @@ static int dw_hdmi_phy_power_on(struct dw_hdmi *hdmi)
static int hdmi_phy_configure(struct dw_hdmi *hdmi) 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; 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_mpll_config *mpll_config = pdata->mpll_cfg;
const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr; const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr;
...@@ -1019,7 +1028,7 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi) ...@@ -1019,7 +1028,7 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi)
dw_hdmi_phy_power_off(hdmi); dw_hdmi_phy_power_off(hdmi);
/* Leave low power consumption mode by asserting SVSRET. */ /* Leave low power consumption mode by asserting SVSRET. */
if (hdmi->phy->has_svsret) if (phy->has_svsret)
dw_hdmi_phy_enable_svsret(hdmi, 1); dw_hdmi_phy_enable_svsret(hdmi, 1);
/* PHY reset. The reset signal is active high on Gen2 PHYs. */ /* PHY reset. The reset signal is active high on Gen2 PHYs. */
...@@ -1057,7 +1066,8 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi) ...@@ -1057,7 +1066,8 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi)
return dw_hdmi_phy_power_on(hdmi); return dw_hdmi_phy_power_on(hdmi);
} }
static int dw_hdmi_phy_init(struct dw_hdmi *hdmi) static int dw_hdmi_phy_init(struct dw_hdmi *hdmi, void *data,
struct drm_display_mode *mode)
{ {
int i, ret; int i, ret;
...@@ -1071,10 +1081,31 @@ static int dw_hdmi_phy_init(struct dw_hdmi *hdmi) ...@@ -1071,10 +1081,31 @@ static int dw_hdmi_phy_init(struct dw_hdmi *hdmi)
return ret; return ret;
} }
hdmi->phy_enabled = true;
return 0; return 0;
} }
static void dw_hdmi_phy_disable(struct dw_hdmi *hdmi, void *data)
{
dw_hdmi_phy_power_off(hdmi);
}
static enum drm_connector_status dw_hdmi_phy_read_hpd(struct dw_hdmi *hdmi,
void *data)
{
return hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD ?
connector_status_connected : connector_status_disconnected;
}
static const struct dw_hdmi_phy_ops dw_hdmi_synopsys_phy_ops = {
.init = dw_hdmi_phy_init,
.disable = dw_hdmi_phy_disable,
.read_hpd = dw_hdmi_phy_read_hpd,
};
/* -----------------------------------------------------------------------------
* HDMI TX Setup
*/
static void hdmi_tx_hdcp_config(struct dw_hdmi *hdmi) static void hdmi_tx_hdcp_config(struct dw_hdmi *hdmi)
{ {
u8 de; u8 de;
...@@ -1289,16 +1320,6 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi, ...@@ -1289,16 +1320,6 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi,
hdmi_writeb(hdmi, vsync_len, HDMI_FC_VSYNCINWIDTH); hdmi_writeb(hdmi, vsync_len, HDMI_FC_VSYNCINWIDTH);
} }
static void dw_hdmi_phy_disable(struct dw_hdmi *hdmi)
{
if (!hdmi->phy_enabled)
return;
dw_hdmi_phy_power_off(hdmi);
hdmi->phy_enabled = false;
}
/* HDMI Initialization Step B.4 */ /* HDMI Initialization Step B.4 */
static void dw_hdmi_enable_video_path(struct dw_hdmi *hdmi) static void dw_hdmi_enable_video_path(struct dw_hdmi *hdmi)
{ {
...@@ -1431,9 +1452,10 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode) ...@@ -1431,9 +1452,10 @@ static int dw_hdmi_setup(struct dw_hdmi *hdmi, struct drm_display_mode *mode)
hdmi_av_composer(hdmi, mode); hdmi_av_composer(hdmi, mode);
/* HDMI Initializateion Step B.2 */ /* HDMI Initializateion Step B.2 */
ret = dw_hdmi_phy_init(hdmi); ret = hdmi->phy.ops->init(hdmi, hdmi->phy.data, &hdmi->previous_mode);
if (ret) if (ret)
return ret; return ret;
hdmi->phy.enabled = true;
/* HDMI Initialization Step B.3 */ /* HDMI Initialization Step B.3 */
dw_hdmi_enable_video_path(hdmi); dw_hdmi_enable_video_path(hdmi);
...@@ -1548,7 +1570,11 @@ static void dw_hdmi_poweron(struct dw_hdmi *hdmi) ...@@ -1548,7 +1570,11 @@ static void dw_hdmi_poweron(struct dw_hdmi *hdmi)
static void dw_hdmi_poweroff(struct dw_hdmi *hdmi) static void dw_hdmi_poweroff(struct dw_hdmi *hdmi)
{ {
dw_hdmi_phy_disable(hdmi); if (hdmi->phy.enabled) {
hdmi->phy.ops->disable(hdmi, hdmi->phy.data);
hdmi->phy.enabled = false;
}
hdmi->bridge_is_on = false; hdmi->bridge_is_on = false;
} }
...@@ -1611,8 +1637,7 @@ dw_hdmi_connector_detect(struct drm_connector *connector, bool force) ...@@ -1611,8 +1637,7 @@ dw_hdmi_connector_detect(struct drm_connector *connector, bool force)
dw_hdmi_update_phy_mask(hdmi); dw_hdmi_update_phy_mask(hdmi);
mutex_unlock(&hdmi->mutex); mutex_unlock(&hdmi->mutex);
return hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD ? return hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data);
connector_status_connected : connector_status_disconnected;
} }
static int dw_hdmi_connector_get_modes(struct drm_connector *connector) static int dw_hdmi_connector_get_modes(struct drm_connector *connector)
...@@ -1898,19 +1923,31 @@ static int dw_hdmi_detect_phy(struct dw_hdmi *hdmi) ...@@ -1898,19 +1923,31 @@ static int dw_hdmi_detect_phy(struct dw_hdmi *hdmi)
phy_type = hdmi_readb(hdmi, HDMI_CONFIG2_ID); phy_type = hdmi_readb(hdmi, HDMI_CONFIG2_ID);
if (phy_type == DW_HDMI_PHY_VENDOR_PHY) {
/* Vendor PHYs require support from the glue layer. */
if (!hdmi->plat_data->phy_ops || !hdmi->plat_data->phy_name) {
dev_err(hdmi->dev,
"Vendor HDMI PHY not supported by glue layer\n");
return -ENODEV;
}
hdmi->phy.ops = hdmi->plat_data->phy_ops;
hdmi->phy.data = hdmi->plat_data->phy_data;
hdmi->phy.name = hdmi->plat_data->phy_name;
return 0;
}
/* Synopsys PHYs are handled internally. */
for (i = 0; i < ARRAY_SIZE(dw_hdmi_phys); ++i) { for (i = 0; i < ARRAY_SIZE(dw_hdmi_phys); ++i) {
if (dw_hdmi_phys[i].type == phy_type) { if (dw_hdmi_phys[i].type == phy_type) {
hdmi->phy = &dw_hdmi_phys[i]; hdmi->phy.ops = &dw_hdmi_synopsys_phy_ops;
hdmi->phy.name = dw_hdmi_phys[i].name;
hdmi->phy.data = (void *)&dw_hdmi_phys[i];
return 0; return 0;
} }
} }
if (phy_type == DW_HDMI_PHY_VENDOR_PHY) dev_err(hdmi->dev, "Unsupported HDMI PHY type (%02x)\n", phy_type);
dev_err(hdmi->dev, "Unsupported vendor HDMI PHY\n");
else
dev_err(hdmi->dev, "Unsupported HDMI PHY type (%02x)\n",
phy_type);
return -ENODEV; return -ENODEV;
} }
...@@ -2031,7 +2068,7 @@ __dw_hdmi_probe(struct platform_device *pdev, ...@@ -2031,7 +2068,7 @@ __dw_hdmi_probe(struct platform_device *pdev,
dev_info(dev, "Detected HDMI TX controller v%x.%03x %s HDCP (%s)\n", dev_info(dev, "Detected HDMI TX controller v%x.%03x %s HDCP (%s)\n",
hdmi->version >> 12, hdmi->version & 0xfff, hdmi->version >> 12, hdmi->version & 0xfff,
prod_id1 & HDMI_PRODUCT_ID1_HDCP ? "with" : "without", prod_id1 & HDMI_PRODUCT_ID1_HDCP ? "with" : "without",
hdmi->phy->name); hdmi->phy.name);
initialize_hdmi_ih_mutes(hdmi); initialize_hdmi_ih_mutes(hdmi);
......
...@@ -57,13 +57,27 @@ struct dw_hdmi_phy_config { ...@@ -57,13 +57,27 @@ struct dw_hdmi_phy_config {
u16 vlev_ctr; /* voltage level control */ u16 vlev_ctr; /* voltage level control */
}; };
struct dw_hdmi_phy_ops {
int (*init)(struct dw_hdmi *hdmi, void *data,
struct drm_display_mode *mode);
void (*disable)(struct dw_hdmi *hdmi, void *data);
enum drm_connector_status (*read_hpd)(struct dw_hdmi *hdmi, void *data);
};
struct dw_hdmi_plat_data { struct dw_hdmi_plat_data {
enum dw_hdmi_devtype dev_type; enum dw_hdmi_devtype dev_type;
enum drm_mode_status (*mode_valid)(struct drm_connector *connector,
struct drm_display_mode *mode);
/* Vendor PHY support */
const struct dw_hdmi_phy_ops *phy_ops;
const char *phy_name;
void *phy_data;
/* Synopsys PHY support */
const struct dw_hdmi_mpll_config *mpll_cfg; const struct dw_hdmi_mpll_config *mpll_cfg;
const struct dw_hdmi_curr_ctrl *cur_ctr; const struct dw_hdmi_curr_ctrl *cur_ctr;
const struct dw_hdmi_phy_config *phy_config; const struct dw_hdmi_phy_config *phy_config;
enum drm_mode_status (*mode_valid)(struct drm_connector *connector,
struct drm_display_mode *mode);
}; };
int dw_hdmi_probe(struct platform_device *pdev, int dw_hdmi_probe(struct platform_device *pdev,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment