drm: bridge: dw-hdmi: Create PHY operations
authorLaurent Pinchart <laurent.pinchart+renesas@ideasonboard.com>
Sun, 5 Mar 2017 23:36:15 +0000 (01:36 +0200)
committerArchit Taneja <architt@codeaurora.org>
Fri, 10 Mar 2017 10:05:13 +0000 (15:35 +0530)
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
drivers/gpu/drm/bridge/dw-hdmi.c
include/drm/bridge/dw_hdmi.h

index c25eac8ba47bc47a61ee05eacc3aea0189ce29a2..cb2703862be26c46d0adb1146490d3db26cc76c2 100644 (file)
@@ -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)
 {
@@ -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)
 {
-       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 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;
        u8 val;
 
@@ -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);
 
index b080a171a23f28134e1e840abef457fe2cd8d1c1..0f583ca7e66efb09a9c40ee46b8188b3676c6703 100644 (file)
@@ -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,