Message ID | 20200904115354.25336-14-ville.syrjala@linux.intel.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | drm/i915: Pimp DP DFP handling | expand |
On Fri, 2020-09-04 at 14:53 +0300, Ville Syrjala wrote: > From: Ville Syrjälä <ville.syrjala@linux.intel.com> > > The downstream facing port caps in the DPCD can give us a hint > as to what kind of display mode the sink can use if it doesn't > have an EDID. Use that information to pick a suitable mode. > > Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com> > --- > drivers/gpu/drm/drm_dp_helper.c | 54 +++++++++++++++++++++++++++++++++ > drivers/gpu/drm/drm_edid.c | 19 ++++++++++++ > include/drm/drm_dp_helper.h | 12 ++++++++ > include/drm/drm_edid.h | 4 +++ > 4 files changed, 89 insertions(+) > > diff --git a/drivers/gpu/drm/drm_dp_helper.c > b/drivers/gpu/drm/drm_dp_helper.c > index f567428f2aef..0d5e9bcf11d0 100644 > --- a/drivers/gpu/drm/drm_dp_helper.c > +++ b/drivers/gpu/drm/drm_dp_helper.c > @@ -808,6 +808,60 @@ int drm_dp_downstream_max_bpc(const u8 > dpcd[DP_RECEIVER_CAP_SIZE], > } > EXPORT_SYMBOL(drm_dp_downstream_max_bpc); > > +/** > + * drm_dp_downstream_mode() - return a mode for downstream facing port > + * @dpcd: DisplayPort configuration data > + * @port_cap: port capabilities > + * > + * Provides a suitable mode for downstream facing ports without EDID. > + * > + * Returns a new drm_display_mode on success or NULL on failure > + */ > +struct drm_display_mode * > +drm_dp_downstream_mode(struct drm_device *dev, > + const u8 dpcd[DP_RECEIVER_CAP_SIZE], > + const u8 port_cap[4]) > + > +{ > + u8 vic; > + > + if (!drm_dp_is_branch(dpcd)) > + return NULL; > + > + if (dpcd[DP_DPCD_REV] < 0x11) > + return NULL; > + > + switch (port_cap[0] & DP_DS_PORT_TYPE_MASK) { > + case DP_DS_PORT_TYPE_NON_EDID: > + switch (port_cap[0] & DP_DS_NON_EDID_MASK) { > + case DP_DS_NON_EDID_720x480i_60: > + vic = 6; > + break; > + case DP_DS_NON_EDID_720x480i_50: > + vic = 21; > + break; > + case DP_DS_NON_EDID_1920x1080i_60: > + vic = 5; > + break; > + case DP_DS_NON_EDID_1920x1080i_50: > + vic = 20; > + break; > + case DP_DS_NON_EDID_1280x720_60: > + vic = 4; > + break; > + case DP_DS_NON_EDID_1280x720_50: > + vic = 19; > + break; > + default: > + return NULL; > + } > + return drm_display_mode_from_cea_vic(dev, vic); > + default: > + return NULL; > + } > +} > +EXPORT_SYMBOL(drm_dp_downstream_mode); > + > /** > * drm_dp_downstream_id() - identify branch device > * @aux: DisplayPort AUX channel > diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c > index 6840f0530a38..b9419fed6c28 100644 > --- a/drivers/gpu/drm/drm_edid.c > +++ b/drivers/gpu/drm/drm_edid.c > @@ -3738,6 +3738,25 @@ drm_add_cmdb_modes(struct drm_connector *connector, > u8 svd) > bitmap_set(hdmi->y420_cmdb_modes, vic, 1); > } > > +struct drm_display_mode * > +drm_display_mode_from_cea_vic(struct drm_device *dev, > + u8 video_code) > +{ > + const struct drm_display_mode *cea_mode; > + struct drm_display_mode *newmode; > + > + cea_mode = cea_mode_for_vic(video_code); > + if (!cea_mode) > + return NULL; > + > + newmode = drm_mode_duplicate(dev, cea_mode); > + if (!newmode) > + return NULL; > + > + return newmode; > +} > +EXPORT_SYMBOL(drm_display_mode_from_cea_vic); Forgot the kdocs > + > static int > do_cea_modes(struct drm_connector *connector, const u8 *db, u8 len) > { > diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h > index 6812a3e0de8d..fbba4a0f7366 100644 > --- a/include/drm/drm_dp_helper.h > +++ b/include/drm/drm_dp_helper.h > @@ -28,6 +28,8 @@ > #include <linux/types.h> > #include <drm/drm_connector.h> > > +struct drm_device; > + > /* > * Unless otherwise noted, all values are from the DP 1.1a spec. Note that > * DP and DPCD versions are independent. Differences from 1.0 are not > noted, > @@ -385,6 +387,13 @@ > # define DP_DS_PORT_TYPE_DP_DUALMODE 5 > # define DP_DS_PORT_TYPE_WIRELESS 6 > # define DP_DS_PORT_HPD (1 << 3) > +# define DP_DS_NON_EDID_MASK (0xf << 4) > +# define DP_DS_NON_EDID_720x480i_60 (1 << 4) > +# define DP_DS_NON_EDID_720x480i_50 (2 << 4) > +# define DP_DS_NON_EDID_1920x1080i_60 (3 << 4) > +# define DP_DS_NON_EDID_1920x1080i_50 (4 << 4) > +# define DP_DS_NON_EDID_1280x720_60 (5 << 4) > +# define DP_DS_NON_EDID_1280x720_50 (7 << 4) > /* offset 1 for VGA is maximum megapixels per second / 8 */ > /* offset 1 for DVI/HDMI is maximum TMDS clock in Mbps / 2.5 */ > /* offset 2 for VGA/DVI/HDMI */ > @@ -1654,6 +1663,9 @@ int drm_dp_downstream_min_tmds_clock(const u8 > dpcd[DP_RECEIVER_CAP_SIZE], > int drm_dp_downstream_max_bpc(const u8 dpcd[DP_RECEIVER_CAP_SIZE], > const u8 port_cap[4], > const struct edid *edid); > +struct drm_display_mode *drm_dp_downstream_mode(struct drm_device *dev, > + const u8 > dpcd[DP_RECEIVER_CAP_SIZE], > + const u8 port_cap[4]); > int drm_dp_downstream_id(struct drm_dp_aux *aux, char id[6]); > void drm_dp_downstream_debug(struct seq_file *m, > const u8 dpcd[DP_RECEIVER_CAP_SIZE], > diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h > index cfa4f5af49af..b27a0e2169c8 100644 > --- a/include/drm/drm_edid.h > +++ b/include/drm/drm_edid.h > @@ -517,4 +517,8 @@ void drm_edid_get_monitor_name(struct edid *edid, char > *name, > struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev, > int hsize, int vsize, int fresh, > bool rb); > +struct drm_display_mode * > +drm_display_mode_from_cea_vic(struct drm_device *dev, > + u8 video_code); > + > #endif /* __DRM_EDID_H__ */
diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index f567428f2aef..0d5e9bcf11d0 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -808,6 +808,60 @@ int drm_dp_downstream_max_bpc(const u8 dpcd[DP_RECEIVER_CAP_SIZE], } EXPORT_SYMBOL(drm_dp_downstream_max_bpc); +/** + * drm_dp_downstream_mode() - return a mode for downstream facing port + * @dpcd: DisplayPort configuration data + * @port_cap: port capabilities + * + * Provides a suitable mode for downstream facing ports without EDID. + * + * Returns a new drm_display_mode on success or NULL on failure + */ +struct drm_display_mode * +drm_dp_downstream_mode(struct drm_device *dev, + const u8 dpcd[DP_RECEIVER_CAP_SIZE], + const u8 port_cap[4]) + +{ + u8 vic; + + if (!drm_dp_is_branch(dpcd)) + return NULL; + + if (dpcd[DP_DPCD_REV] < 0x11) + return NULL; + + switch (port_cap[0] & DP_DS_PORT_TYPE_MASK) { + case DP_DS_PORT_TYPE_NON_EDID: + switch (port_cap[0] & DP_DS_NON_EDID_MASK) { + case DP_DS_NON_EDID_720x480i_60: + vic = 6; + break; + case DP_DS_NON_EDID_720x480i_50: + vic = 21; + break; + case DP_DS_NON_EDID_1920x1080i_60: + vic = 5; + break; + case DP_DS_NON_EDID_1920x1080i_50: + vic = 20; + break; + case DP_DS_NON_EDID_1280x720_60: + vic = 4; + break; + case DP_DS_NON_EDID_1280x720_50: + vic = 19; + break; + default: + return NULL; + } + return drm_display_mode_from_cea_vic(dev, vic); + default: + return NULL; + } +} +EXPORT_SYMBOL(drm_dp_downstream_mode); + /** * drm_dp_downstream_id() - identify branch device * @aux: DisplayPort AUX channel diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 6840f0530a38..b9419fed6c28 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -3738,6 +3738,25 @@ drm_add_cmdb_modes(struct drm_connector *connector, u8 svd) bitmap_set(hdmi->y420_cmdb_modes, vic, 1); } +struct drm_display_mode * +drm_display_mode_from_cea_vic(struct drm_device *dev, + u8 video_code) +{ + const struct drm_display_mode *cea_mode; + struct drm_display_mode *newmode; + + cea_mode = cea_mode_for_vic(video_code); + if (!cea_mode) + return NULL; + + newmode = drm_mode_duplicate(dev, cea_mode); + if (!newmode) + return NULL; + + return newmode; +} +EXPORT_SYMBOL(drm_display_mode_from_cea_vic); + static int do_cea_modes(struct drm_connector *connector, const u8 *db, u8 len) { diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h index 6812a3e0de8d..fbba4a0f7366 100644 --- a/include/drm/drm_dp_helper.h +++ b/include/drm/drm_dp_helper.h @@ -28,6 +28,8 @@ #include <linux/types.h> #include <drm/drm_connector.h> +struct drm_device; + /* * Unless otherwise noted, all values are from the DP 1.1a spec. Note that * DP and DPCD versions are independent. Differences from 1.0 are not noted, @@ -385,6 +387,13 @@ # define DP_DS_PORT_TYPE_DP_DUALMODE 5 # define DP_DS_PORT_TYPE_WIRELESS 6 # define DP_DS_PORT_HPD (1 << 3) +# define DP_DS_NON_EDID_MASK (0xf << 4) +# define DP_DS_NON_EDID_720x480i_60 (1 << 4) +# define DP_DS_NON_EDID_720x480i_50 (2 << 4) +# define DP_DS_NON_EDID_1920x1080i_60 (3 << 4) +# define DP_DS_NON_EDID_1920x1080i_50 (4 << 4) +# define DP_DS_NON_EDID_1280x720_60 (5 << 4) +# define DP_DS_NON_EDID_1280x720_50 (7 << 4) /* offset 1 for VGA is maximum megapixels per second / 8 */ /* offset 1 for DVI/HDMI is maximum TMDS clock in Mbps / 2.5 */ /* offset 2 for VGA/DVI/HDMI */ @@ -1654,6 +1663,9 @@ int drm_dp_downstream_min_tmds_clock(const u8 dpcd[DP_RECEIVER_CAP_SIZE], int drm_dp_downstream_max_bpc(const u8 dpcd[DP_RECEIVER_CAP_SIZE], const u8 port_cap[4], const struct edid *edid); +struct drm_display_mode *drm_dp_downstream_mode(struct drm_device *dev, + const u8 dpcd[DP_RECEIVER_CAP_SIZE], + const u8 port_cap[4]); int drm_dp_downstream_id(struct drm_dp_aux *aux, char id[6]); void drm_dp_downstream_debug(struct seq_file *m, const u8 dpcd[DP_RECEIVER_CAP_SIZE], diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h index cfa4f5af49af..b27a0e2169c8 100644 --- a/include/drm/drm_edid.h +++ b/include/drm/drm_edid.h @@ -517,4 +517,8 @@ void drm_edid_get_monitor_name(struct edid *edid, char *name, struct drm_display_mode *drm_mode_find_dmt(struct drm_device *dev, int hsize, int vsize, int fresh, bool rb); +struct drm_display_mode * +drm_display_mode_from_cea_vic(struct drm_device *dev, + u8 video_code); + #endif /* __DRM_EDID_H__ */