diff mbox

[v3,3/4] drm/dp: Add DisplayPort link helpers

Message ID 1389711336-29702-4-git-send-email-treding@nvidia.com (mailing list archive)
State New, archived
Headers show

Commit Message

Thierry Reding Jan. 14, 2014, 2:55 p.m. UTC
Add a helper to probe a DP link (read out the supported DPCD revision,
maximum rate, link count and capabilities) as well as power up the DP
link and configure it accordingly.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
Changes in v3:
- split into drm_dp_link_power_up() and drm_dp_link_configure()
- do not change sink state for DPCD versions earlier than 1.1
- sleep for 1-2 ms after setting local sink to D0 state
- read and write consecutive registers where possible
- read DPCD revision when link is probed
- remove duplicate kerneldoc

 drivers/gpu/drm/drm_dp_helper.c | 94 +++++++++++++++++++++++++++++++++++++++++
 include/drm/drm_dp_helper.h     | 17 ++++++++
 2 files changed, 111 insertions(+)

Comments

Alex Deucher Jan. 14, 2014, 3:52 p.m. UTC | #1
On Tue, Jan 14, 2014 at 9:55 AM, Thierry Reding
<thierry.reding@gmail.com> wrote:
> Add a helper to probe a DP link (read out the supported DPCD revision,
> maximum rate, link count and capabilities) as well as power up the DP
> link and configure it accordingly.
>
> Signed-off-by: Thierry Reding <treding@nvidia.com>
> ---
> Changes in v3:
> - split into drm_dp_link_power_up() and drm_dp_link_configure()
> - do not change sink state for DPCD versions earlier than 1.1
> - sleep for 1-2 ms after setting local sink to D0 state
> - read and write consecutive registers where possible
> - read DPCD revision when link is probed
> - remove duplicate kerneldoc
>
>  drivers/gpu/drm/drm_dp_helper.c | 94 +++++++++++++++++++++++++++++++++++++++++
>  include/drm/drm_dp_helper.h     | 17 ++++++++
>  2 files changed, 111 insertions(+)
>
> diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c
> index 572637456713..ac2e12789634 100644
> --- a/drivers/gpu/drm/drm_dp_helper.c
> +++ b/drivers/gpu/drm/drm_dp_helper.c
> @@ -472,3 +472,97 @@ int drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux,
>                                 DP_LINK_STATUS_SIZE);
>  }
>  EXPORT_SYMBOL(drm_dp_dpcd_read_link_status);
> +
> +/**
> + * drm_dp_link_probe() - probe a DisplayPort link for capabilities
> + * @aux: DisplayPort AUX channel
> + * @link: pointer to structure in which to return link capabilities
> + *
> + * The structure filled in by this function can usually be passed directly
> + * into drm_dp_link_power_up() and drm_dp_link_configure() to power up and
> + * configure the link based on the link's capabilities.
> + *
> + * Returns 0 on success or a negative error code on failure.
> + */
> +int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link)
> +{
> +       u8 values[3];
> +       int err;
> +
> +       memset(link, 0, sizeof(*link));
> +
> +       err = drm_dp_dpcd_read(aux, DP_DPCD_REV, values, sizeof(values));
> +       if (err < 0)
> +               return err;
> +
> +       link->revision = values[0];
> +       link->rate = drm_dp_bw_code_to_link_rate(values[1]);
> +       link->num_lanes = values[2] & DP_MAX_LANE_COUNT_MASK;
> +
> +       if (values[2] & DP_ENHANCED_FRAME_CAP)
> +               link->capabilities |= DP_LINK_CAP_ENHANCED_FRAMING;
> +
> +       return 0;
> +}
> +
> +/**
> + * drm_dp_link_power_up() - power up a DisplayPort link
> + * @aux: DisplayPort AUX channel
> + * @link: pointer to a structure containing the link configuration
> + *
> + * Returns 0 on success or a negative error code on failure.
> + */
> +int drm_dp_link_power_up(struct drm_dp_aux *aux, struct drm_dp_link *link)
> +{
> +       u8 value;
> +       int err;
> +
> +       /* DP_SET_POWER register is only available on DPCD v1.1 and later */
> +       if (link->revision < 0x11)
> +               return 0;
> +
> +       err = drm_dp_dpcd_readb(aux, DP_SET_POWER, &value);
> +       if (err < 0)
> +               return err;
> +
> +       value &= ~DP_SET_POWER_MASK;
> +       value |= DP_SET_POWER_D0;
> +
> +       err = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value);
> +       if (err < 0)
> +               return err;
> +
> +       /*
> +        * According to the DP 1.1 specification, a "Sink Device must exit the
> +        * power saving state within 1 ms" (Section 2.5.3.1, Table 5-52, "Sink
> +        * Control Field" (register 0x600).
> +        */
> +       usleep_range(1000, 2000);
> +
> +       return 0;
> +}
> +
> +/**
> + * drm_dp_link_configure() - configure a DisplayPort link
> + * @aux: DisplayPort AUX cahnnel
> + * @link: pointer to a structure containing the link configuration
> + *
> + * Returns 0 on seccuss or a negative error code on failure.

spelling.  success.


Alex
Thierry Reding Jan. 15, 2014, 9 a.m. UTC | #2
On Tue, Jan 14, 2014 at 10:52:55AM -0500, Alex Deucher wrote:
> On Tue, Jan 14, 2014 at 9:55 AM, Thierry Reding <thierry.reding@gmail.com> wrote:
[...]
> > +/**
> > + * drm_dp_link_configure() - configure a DisplayPort link
> > + * @aux: DisplayPort AUX cahnnel
> > + * @link: pointer to a structure containing the link configuration
> > + *
> > + * Returns 0 on seccuss or a negative error code on failure.
> 
> spelling.  success.

Good catch. Thanks! There's also "cahnnel" above and when running a
spell checker it pointed out a few others. I've rolled fixes for all of
them into a v4, which I guess I'll resend anyway because I forgot to
attach the Tegra eDP implementation that I promised.

Thierry
Jani Nikula Jan. 17, 2014, 1:22 p.m. UTC | #3
On Tue, 14 Jan 2014, Thierry Reding <thierry.reding@gmail.com> wrote:
> Add a helper to probe a DP link (read out the supported DPCD revision,
> maximum rate, link count and capabilities) as well as power up the DP
> link and configure it accordingly.
>
> Signed-off-by: Thierry Reding <treding@nvidia.com>
> ---
> Changes in v3:
> - split into drm_dp_link_power_up() and drm_dp_link_configure()
> - do not change sink state for DPCD versions earlier than 1.1
> - sleep for 1-2 ms after setting local sink to D0 state
> - read and write consecutive registers where possible
> - read DPCD revision when link is probed
> - remove duplicate kerneldoc
>
>  drivers/gpu/drm/drm_dp_helper.c | 94 +++++++++++++++++++++++++++++++++++++++++
>  include/drm/drm_dp_helper.h     | 17 ++++++++
>  2 files changed, 111 insertions(+)
>
> diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c
> index 572637456713..ac2e12789634 100644
> --- a/drivers/gpu/drm/drm_dp_helper.c
> +++ b/drivers/gpu/drm/drm_dp_helper.c
> @@ -472,3 +472,97 @@ int drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux,
>  				DP_LINK_STATUS_SIZE);
>  }
>  EXPORT_SYMBOL(drm_dp_dpcd_read_link_status);
> +
> +/**
> + * drm_dp_link_probe() - probe a DisplayPort link for capabilities
> + * @aux: DisplayPort AUX channel
> + * @link: pointer to structure in which to return link capabilities
> + *
> + * The structure filled in by this function can usually be passed directly
> + * into drm_dp_link_power_up() and drm_dp_link_configure() to power up and
> + * configure the link based on the link's capabilities.
> + *
> + * Returns 0 on success or a negative error code on failure.
> + */
> +int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link)
> +{
> +	u8 values[3];
> +	int err;
> +
> +	memset(link, 0, sizeof(*link));
> +
> +	err = drm_dp_dpcd_read(aux, DP_DPCD_REV, values, sizeof(values));
> +	if (err < 0)
> +		return err;
> +
> +	link->revision = values[0];
> +	link->rate = drm_dp_bw_code_to_link_rate(values[1]);
> +	link->num_lanes = values[2] & DP_MAX_LANE_COUNT_MASK;
> +
> +	if (values[2] & DP_ENHANCED_FRAME_CAP)
> +		link->capabilities |= DP_LINK_CAP_ENHANCED_FRAMING;

Since DP_DPCD_REV == 0, you could use the #defines for the indexes (if
you're going to send another version anyway). Ditto below for
drm_dp_link_configure.

Other than that nitpick, the series looks good to me. If we face any
issues migrating i915 on top of this, we can iron them out later on.

On the series,

Reviewed-by: Jani Nikula <jani.nikula@intel.com>


> +
> +	return 0;
> +}
> +
> +/**
> + * drm_dp_link_power_up() - power up a DisplayPort link
> + * @aux: DisplayPort AUX channel
> + * @link: pointer to a structure containing the link configuration
> + *
> + * Returns 0 on success or a negative error code on failure.
> + */
> +int drm_dp_link_power_up(struct drm_dp_aux *aux, struct drm_dp_link *link)
> +{
> +	u8 value;
> +	int err;
> +
> +	/* DP_SET_POWER register is only available on DPCD v1.1 and later */
> +	if (link->revision < 0x11)
> +		return 0;
> +
> +	err = drm_dp_dpcd_readb(aux, DP_SET_POWER, &value);
> +	if (err < 0)
> +		return err;
> +
> +	value &= ~DP_SET_POWER_MASK;
> +	value |= DP_SET_POWER_D0;
> +
> +	err = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value);
> +	if (err < 0)
> +		return err;
> +
> +	/*
> +	 * According to the DP 1.1 specification, a "Sink Device must exit the
> +	 * power saving state within 1 ms" (Section 2.5.3.1, Table 5-52, "Sink
> +	 * Control Field" (register 0x600).
> +	 */
> +	usleep_range(1000, 2000);
> +
> +	return 0;
> +}
> +
> +/**
> + * drm_dp_link_configure() - configure a DisplayPort link
> + * @aux: DisplayPort AUX cahnnel
> + * @link: pointer to a structure containing the link configuration
> + *
> + * Returns 0 on seccuss or a negative error code on failure.
> + */
> +int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link)
> +{
> +	u8 values[2];
> +	int err;
> +
> +	values[0] = drm_dp_link_rate_to_bw_code(link->rate);
> +	values[1] = link->num_lanes;
> +
> +	if (link->capabilities & DP_LINK_CAP_ENHANCED_FRAMING)
> +		values[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
> +
> +	err = drm_dp_dpcd_write(aux, DP_LINK_BW_SET, values, sizeof(values));
> +	if (err < 0)
> +		return err;
> +
> +	return 0;
> +}
> diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h
> index 8af695277a84..c7b3c736c40a 100644
> --- a/include/drm/drm_dp_helper.h
> +++ b/include/drm/drm_dp_helper.h
> @@ -291,6 +291,7 @@
>  #define DP_SET_POWER                        0x600
>  # define DP_SET_POWER_D0                    0x1
>  # define DP_SET_POWER_D3                    0x2
> +# define DP_SET_POWER_MASK                  0x3
>  
>  #define DP_PSR_ERROR_STATUS                 0x2006  /* XXX 1.2? */
>  # define DP_PSR_LINK_CRC_ERROR              (1 << 0)
> @@ -468,4 +469,20 @@ static inline ssize_t drm_dp_dpcd_writeb(struct drm_dp_aux *aux,
>  int drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux,
>  				 u8 status[DP_LINK_STATUS_SIZE]);
>  
> +/*
> + * DisplayPort link
> + */
> +#define DP_LINK_CAP_ENHANCED_FRAMING (1 << 0)
> +
> +struct drm_dp_link {
> +	unsigned char revision;
> +	unsigned int rate;
> +	unsigned int num_lanes;
> +	unsigned long capabilities;
> +};
> +
> +int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link);
> +int drm_dp_link_power_up(struct drm_dp_aux *aux, struct drm_dp_link *link);
> +int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link);
> +
>  #endif /* _DRM_DP_HELPER_H_ */
> -- 
> 1.8.4.2
>
Thierry Reding Jan. 21, 2014, 7:30 p.m. UTC | #4
On Fri, Jan 17, 2014 at 03:22:08PM +0200, Jani Nikula wrote:
> On Tue, 14 Jan 2014, Thierry Reding <thierry.reding@gmail.com> wrote:
[...]
> > +int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link)
> > +{
> > +	u8 values[3];
> > +	int err;
> > +
> > +	memset(link, 0, sizeof(*link));
> > +
> > +	err = drm_dp_dpcd_read(aux, DP_DPCD_REV, values, sizeof(values));
> > +	if (err < 0)
> > +		return err;
> > +
> > +	link->revision = values[0];
> > +	link->rate = drm_dp_bw_code_to_link_rate(values[1]);
> > +	link->num_lanes = values[2] & DP_MAX_LANE_COUNT_MASK;
> > +
> > +	if (values[2] & DP_ENHANCED_FRAME_CAP)
> > +		link->capabilities |= DP_LINK_CAP_ENHANCED_FRAMING;
> 
> Since DP_DPCD_REV == 0, you could use the #defines for the indexes (if
> you're going to send another version anyway). Ditto below for
> drm_dp_link_configure.

We write to DP_LINK_BW_SET in drm_dp_link_configure() so I don't think
we can apply the same trick there. Also I'm not sure if it's really
worth having that here. Given that it only works if we actually read
DP_DPCD_REV, the number of locations where it can be done is fairly
small and they will look asymmetric with respect to other functions
using the drm_dp_dpcd_read/write() helpers.

So unless you feel strongly I'd prefer not to do this.

> Other than that nitpick, the series looks good to me. If we face any
> issues migrating i915 on top of this, we can iron them out later on.
> 
> On the series,
> 
> Reviewed-by: Jani Nikula <jani.nikula@intel.com>

Thanks!

Thierry
diff mbox

Patch

diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c
index 572637456713..ac2e12789634 100644
--- a/drivers/gpu/drm/drm_dp_helper.c
+++ b/drivers/gpu/drm/drm_dp_helper.c
@@ -472,3 +472,97 @@  int drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux,
 				DP_LINK_STATUS_SIZE);
 }
 EXPORT_SYMBOL(drm_dp_dpcd_read_link_status);
+
+/**
+ * drm_dp_link_probe() - probe a DisplayPort link for capabilities
+ * @aux: DisplayPort AUX channel
+ * @link: pointer to structure in which to return link capabilities
+ *
+ * The structure filled in by this function can usually be passed directly
+ * into drm_dp_link_power_up() and drm_dp_link_configure() to power up and
+ * configure the link based on the link's capabilities.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link)
+{
+	u8 values[3];
+	int err;
+
+	memset(link, 0, sizeof(*link));
+
+	err = drm_dp_dpcd_read(aux, DP_DPCD_REV, values, sizeof(values));
+	if (err < 0)
+		return err;
+
+	link->revision = values[0];
+	link->rate = drm_dp_bw_code_to_link_rate(values[1]);
+	link->num_lanes = values[2] & DP_MAX_LANE_COUNT_MASK;
+
+	if (values[2] & DP_ENHANCED_FRAME_CAP)
+		link->capabilities |= DP_LINK_CAP_ENHANCED_FRAMING;
+
+	return 0;
+}
+
+/**
+ * drm_dp_link_power_up() - power up a DisplayPort link
+ * @aux: DisplayPort AUX channel
+ * @link: pointer to a structure containing the link configuration
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int drm_dp_link_power_up(struct drm_dp_aux *aux, struct drm_dp_link *link)
+{
+	u8 value;
+	int err;
+
+	/* DP_SET_POWER register is only available on DPCD v1.1 and later */
+	if (link->revision < 0x11)
+		return 0;
+
+	err = drm_dp_dpcd_readb(aux, DP_SET_POWER, &value);
+	if (err < 0)
+		return err;
+
+	value &= ~DP_SET_POWER_MASK;
+	value |= DP_SET_POWER_D0;
+
+	err = drm_dp_dpcd_writeb(aux, DP_SET_POWER, value);
+	if (err < 0)
+		return err;
+
+	/*
+	 * According to the DP 1.1 specification, a "Sink Device must exit the
+	 * power saving state within 1 ms" (Section 2.5.3.1, Table 5-52, "Sink
+	 * Control Field" (register 0x600).
+	 */
+	usleep_range(1000, 2000);
+
+	return 0;
+}
+
+/**
+ * drm_dp_link_configure() - configure a DisplayPort link
+ * @aux: DisplayPort AUX cahnnel
+ * @link: pointer to a structure containing the link configuration
+ *
+ * Returns 0 on seccuss or a negative error code on failure.
+ */
+int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link)
+{
+	u8 values[2];
+	int err;
+
+	values[0] = drm_dp_link_rate_to_bw_code(link->rate);
+	values[1] = link->num_lanes;
+
+	if (link->capabilities & DP_LINK_CAP_ENHANCED_FRAMING)
+		values[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
+
+	err = drm_dp_dpcd_write(aux, DP_LINK_BW_SET, values, sizeof(values));
+	if (err < 0)
+		return err;
+
+	return 0;
+}
diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h
index 8af695277a84..c7b3c736c40a 100644
--- a/include/drm/drm_dp_helper.h
+++ b/include/drm/drm_dp_helper.h
@@ -291,6 +291,7 @@ 
 #define DP_SET_POWER                        0x600
 # define DP_SET_POWER_D0                    0x1
 # define DP_SET_POWER_D3                    0x2
+# define DP_SET_POWER_MASK                  0x3
 
 #define DP_PSR_ERROR_STATUS                 0x2006  /* XXX 1.2? */
 # define DP_PSR_LINK_CRC_ERROR              (1 << 0)
@@ -468,4 +469,20 @@  static inline ssize_t drm_dp_dpcd_writeb(struct drm_dp_aux *aux,
 int drm_dp_dpcd_read_link_status(struct drm_dp_aux *aux,
 				 u8 status[DP_LINK_STATUS_SIZE]);
 
+/*
+ * DisplayPort link
+ */
+#define DP_LINK_CAP_ENHANCED_FRAMING (1 << 0)
+
+struct drm_dp_link {
+	unsigned char revision;
+	unsigned int rate;
+	unsigned int num_lanes;
+	unsigned long capabilities;
+};
+
+int drm_dp_link_probe(struct drm_dp_aux *aux, struct drm_dp_link *link);
+int drm_dp_link_power_up(struct drm_dp_aux *aux, struct drm_dp_link *link);
+int drm_dp_link_configure(struct drm_dp_aux *aux, struct drm_dp_link *link);
+
 #endif /* _DRM_DP_HELPER_H_ */