diff mbox

[v4,6/9] drm: bridge: dw-hdmi: Create PHY operations

Message ID 20170301223915.29888-7-laurent.pinchart+renesas@ideasonboard.com (mailing list archive)
State New, archived
Headers show

Commit Message

Laurent Pinchart March 1, 2017, 10:39 p.m. UTC
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>
---
 drivers/gpu/drm/bridge/dw-hdmi.c | 91 ++++++++++++++++++++++++++++------------
 include/drm/bridge/dw_hdmi.h     | 18 +++++++-
 2 files changed, 80 insertions(+), 29 deletions(-)

Comments

Jose Abreu March 2, 2017, 12:34 p.m. UTC | #1
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@ideasonboard.com>

Reviewed-by: Jose Abreu <joabreu@synopsys.com>

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,
diff mbox

Patch

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,