Hi Laurent, On 01-03-2017 22:39, Laurent Pinchart wrote: > 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: Laurent Pinchart <laurent.pinchart+renesas@xxxxxxxxxxxxxxxx> Reviewed-by: Jose Abreu <joabreu@xxxxxxxxxxxx> Best regards, Jose Miguel Abreu > --- > drivers/gpu/drm/bridge/dw-hdmi.c | 91 ++++++++++++++++++++++++++++------------ > include/drm/bridge/dw_hdmi.h | 18 +++++++- > 2 files changed, 80 insertions(+), 29 deletions(-) > > diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c > index 0aa3ad404f77..cb2703862be2 100644 > --- a/drivers/gpu/drm/bridge/dw-hdmi.c > +++ b/drivers/gpu/drm/bridge/dw-hdmi.c > @@ -141,8 +141,12 @@ struct dw_hdmi { > u8 edid[HDMI_EDID_LEN]; > bool cable_plugin; > > - const struct dw_hdmi_phy_data *phy; > - bool phy_enabled; > + struct { > + const struct dw_hdmi_phy_ops *ops; > + const char *name; > + void *data; > + bool enabled; > + } phy; > > struct drm_display_mode previous_mode; > > @@ -831,6 +835,10 @@ static void hdmi_video_packetize(struct dw_hdmi *hdmi) > HDMI_VP_CONF); > } > > +/* ----------------------------------------------------------------------------- > + * Synopsys PHY Handling > + */ > + > static inline void hdmi_phy_test_clear(struct dw_hdmi *hdmi, > unsigned char bit) > { > @@ -987,6 +995,7 @@ static int dw_hdmi_phy_power_on(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_mpll_config *mpll_config = pdata->mpll_cfg; > const struct dw_hdmi_curr_ctrl *curr_ctrl = pdata->cur_ctr; > @@ -1019,7 +1028,7 @@ static int hdmi_phy_configure(struct dw_hdmi *hdmi) > dw_hdmi_phy_power_off(hdmi); > > /* Leave low power consumption mode by asserting SVSRET. */ > - if (hdmi->phy->has_svsret) > + if (phy->has_svsret) > dw_hdmi_phy_enable_svsret(hdmi, 1); > > /* PHY reset. The reset signal is active high on Gen2 PHYs. */ > @@ -1057,7 +1066,8 @@ static int hdmi_phy_configure(struct dw_hdmi *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; > > @@ -1071,10 +1081,31 @@ static int dw_hdmi_phy_init(struct dw_hdmi *hdmi) > return ret; > } > > - hdmi->phy_enabled = true; > 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) > { > u8 de; > @@ -1289,16 +1320,6 @@ static void hdmi_av_composer(struct dw_hdmi *hdmi, > 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 */ > 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) > hdmi_av_composer(hdmi, mode); > > /* 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) > return ret; > + hdmi->phy.enabled = true; > > /* HDMI Initialization Step B.3 */ > dw_hdmi_enable_video_path(hdmi); > @@ -1548,7 +1570,11 @@ static void dw_hdmi_poweron(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; > } > > @@ -1611,8 +1637,7 @@ dw_hdmi_connector_detect(struct drm_connector *connector, bool force) > dw_hdmi_update_phy_mask(hdmi); > mutex_unlock(&hdmi->mutex); > > - return hdmi_readb(hdmi, HDMI_PHY_STAT0) & HDMI_PHY_HPD ? > - connector_status_connected : connector_status_disconnected; > + return hdmi->phy.ops->read_hpd(hdmi, hdmi->phy.data); > } > > 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) > > 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) { > 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; > } > } > > - if (phy_type == DW_HDMI_PHY_VENDOR_PHY) > - dev_err(hdmi->dev, "Unsupported vendor HDMI PHY\n"); > - else > - dev_err(hdmi->dev, "Unsupported HDMI PHY type (%02x)\n", > - phy_type); > - > + dev_err(hdmi->dev, "Unsupported HDMI PHY type (%02x)\n", phy_type); > return -ENODEV; > } > > @@ -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", > hdmi->version >> 12, hdmi->version & 0xfff, > prod_id1 & HDMI_PRODUCT_ID1_HDCP ? "with" : "without", > - hdmi->phy->name); > + hdmi->phy.name); > > initialize_hdmi_ih_mutes(hdmi); > > diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h > index b080a171a23f..0f583ca7e66e 100644 > --- a/include/drm/bridge/dw_hdmi.h > +++ b/include/drm/bridge/dw_hdmi.h > @@ -57,13 +57,27 @@ struct dw_hdmi_phy_config { > 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 { > 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_curr_ctrl *cur_ctr; > 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,