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__ */