| From 7a486124f02c28397f4c399151d91135e3943774 Mon Sep 17 00:00:00 2001 |
| From: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> |
| Date: Mon, 6 Mar 2017 01:36:15 +0200 |
| Subject: [PATCH 230/286] 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: Laurent Pinchart <laurent.pinchart+renesas@ideasonboard.com> |
| Tested-by: Neil Armstrong <narmstrong@baylibre.com> |
| Reviewed-by: Jose Abreu <joabreu@synopsys.com> |
| Signed-off-by: Archit Taneja <architt@codeaurora.org> |
| Link: http://patchwork.freedesktop.org/patch/msgid/20170305233615.11993-1-laurent.pinchart+renesas@ideasonboard.com |
| (cherry picked from commit f1585f6e29f5aba34e2cd6e3db9f0dd33b046809) |
| Signed-off-by: Simon Horman <horms+renesas@verge.net.au> |
| --- |
| drivers/gpu/drm/bridge/dw-hdmi.c | 95 +++++++++++++++++++++++++++------------ |
| include/drm/bridge/dw_hdmi.h | 18 ++++++- |
| 2 files changed, 82 insertions(+), 31 deletions(-) |
| |
| --- 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 |
| HDMI_VP_CONF); |
| } |
| |
| +/* ----------------------------------------------------------------------------- |
| + * Synopsys PHY Handling |
| + */ |
| + |
| static inline void hdmi_phy_test_clear(struct dw_hdmi *hdmi, |
| unsigned char bit) |
| { |
| @@ -917,7 +925,7 @@ static void dw_hdmi_phy_sel_interface_co |
| |
| 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; |
| u16 val; |
| |
| @@ -951,7 +959,7 @@ static void dw_hdmi_phy_power_off(struct |
| |
| 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; |
| u8 val; |
| |
| @@ -987,6 +995,7 @@ static int dw_hdmi_phy_power_on(struct d |
| |
| 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_ |
| 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_ |
| 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_hd |
| 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_h |
| 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_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_hd |
| |
| 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_conn |
| 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_ |
| |
| 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 * |
| 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); |
| |
| --- 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, |