diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
index b315f01..b77574e 100644
--- a/drivers/gpu/drm/i915/i915_debugfs.c
+++ b/drivers/gpu/drm/i915/i915_debugfs.c
@@ -33,6 +33,7 @@
 #include <linux/slab.h>
 #include <linux/export.h>
 #include <linux/list_sort.h>
+#include <linux/string.h>
 #include <asm/msr-index.h>
 #include <drm/drmP.h>
 #include "intel_drv.h"
@@ -51,6 +52,121 @@ static const char *yesno(int v)
 	return v ? "yes" : "no";
 }
 
+#define MAX_DP_CONFIG_LINE_COUNT	64
+
+enum dp_config_param {
+	DP_CONFIG_PARAM_CONNECTOR = 0,
+	DP_CONFIG_PARAM_CONNECTOR_ID,
+	DP_CONFIG_PARAM_LINK_RATE,
+	DP_CONFIG_PARAM_LANE_COUNT,
+	DP_CONFIG_PARAM_VOLTAGE_SWING,
+	DP_CONFIG_PARAM_PREEMPHASIS,
+	DP_CONFIG_PARAM_HRES,
+	DP_CONFIG_PARAM_VRES,
+	DP_CONFIG_PARAM_BPP,
+	DP_PARAMETER_COUNT,
+	DP_CONFIG_PARAM_INVALID = -1
+};
+
+struct dp_config {
+	enum dp_config_param type;
+	unsigned long value;
+};
+
+static char *dp_get_config_str(int for_token)
+{
+	char *s;
+
+	switch (for_token) {
+	case DP_CONFIG_PARAM_CONNECTOR:
+		s = "Connector";
+		break;
+	case DP_CONFIG_PARAM_CONNECTOR_ID:
+		s = "Connector ID";
+		break;
+	case DP_CONFIG_PARAM_LINK_RATE:
+		s = "Link Rate";
+		break;
+	case DP_CONFIG_PARAM_LANE_COUNT:
+		s = "Lane Count";
+		break;
+	case DP_CONFIG_PARAM_VOLTAGE_SWING:
+		s = "Voltage Swing Level";
+		break;
+	case DP_CONFIG_PARAM_PREEMPHASIS:
+		s = "Preemphasis Level";
+		break;
+	case DP_CONFIG_PARAM_HRES:
+		s = "Horizontal Resolution";
+		break;
+	case DP_CONFIG_PARAM_VRES:
+		s = "Vertical Resolution";
+		break;
+	case DP_CONFIG_PARAM_BPP:
+		s = "Bits Per Pixel";
+		break;
+	default:
+		s = "";
+		break;
+	}
+
+	return s;
+}
+
+static void dp_print_string(struct seq_file *m,
+			    enum dp_config_param for_param,
+			    void *data)
+{
+	char *format_string;
+	unsigned long output_value;
+
+	switch (for_param) {
+	case DP_CONFIG_PARAM_CONNECTOR:
+		/* Special case for the string parameter */
+		format_string = "Connector:             %s\n";
+		seq_printf(m, format_string, (char *) data);
+		break;
+	case DP_CONFIG_PARAM_CONNECTOR_ID:
+		format_string = "Connector ID:          %u\n";
+		output_value = *(unsigned int *) data;
+		break;
+	case DP_CONFIG_PARAM_LINK_RATE:
+		format_string = "Link Rate:             %02x\n";
+		output_value = *(unsigned char *) data;
+		break;
+	case DP_CONFIG_PARAM_LANE_COUNT:
+		format_string = "Lane Count:            %u\n";
+		output_value = *(unsigned int *) data;
+		break;
+	case DP_CONFIG_PARAM_VOLTAGE_SWING:
+		format_string = "Voltage Swing Level:   %u\n";
+		output_value = *(unsigned char *) data;
+		break;
+	case DP_CONFIG_PARAM_PREEMPHASIS:
+		format_string = "Preemphasis Level:     %u\n";
+		output_value = *(unsigned char *) data;
+		break;
+	case DP_CONFIG_PARAM_HRES:
+		format_string = "Horizontal Resolution: %d\n";
+		output_value = *(int *) data;
+		break;
+	case DP_CONFIG_PARAM_VRES:
+		format_string = "Vertical Resolution:   %d\n";
+		output_value = *(int *) data;
+		break;
+	case DP_CONFIG_PARAM_BPP:
+		format_string = "Bits Per Pixel:        %u\n";
+		output_value = *(unsigned char *) data;
+		break;
+	default:
+		format_string = NULL;
+		break;
+	}
+
+	if (format_string != NULL && for_param != DP_CONFIG_PARAM_CONNECTOR)
+		seq_printf(m, format_string, output_value);
+}
+
 /* As the drm_debugfs_init() routines are called before dev->dev_private is
  * allocated we need to hook into the minor for release. */
 static int
@@ -3701,6 +3817,360 @@ static const struct file_operations i915_display_crc_ctl_fops = {
 	.write = display_crc_ctl_write
 };
 
+static void dp_show_config(struct seq_file *m,
+			   struct drm_connector *connector)
+{
+	struct intel_dp *intel_dp;
+	struct intel_crtc *crtc;
+	struct intel_connector *intel_connector = to_intel_connector(connector);
+	struct intel_encoder *intel_encoder = intel_connector->encoder;
+	int pipe_bpp, hres, vres;
+	uint8_t vs[4], pe[4];
+	struct drm_display_mode *mode;
+	int i = 0;
+
+	if (!intel_encoder)
+		return;
+
+	if (intel_encoder->type != INTEL_OUTPUT_DISPLAYPORT)
+		return;
+
+	intel_dp = enc_to_intel_dp(&intel_encoder->base);
+
+	for (i = 0; i < intel_dp->lane_count; i++) {
+		vs[i] = intel_dp->train_set[i]
+			& DP_TRAIN_VOLTAGE_SWING_MASK;
+		pe[i] = (intel_dp->train_set[i]
+		      & DP_TRAIN_PRE_EMPHASIS_MASK) >>
+			DP_TRAIN_PRE_EMPHASIS_SHIFT;
+	}
+
+	/* Output mode info only if a valid CRTC exists */
+	if (intel_encoder->base.crtc) {
+		crtc = to_intel_crtc(intel_encoder->base.crtc);
+		pipe_bpp = crtc->config->pipe_bpp;
+		mode = &crtc->config->base.adjusted_mode;
+		hres = mode->hdisplay;
+		vres = mode->vdisplay;
+	} else {
+		pipe_bpp = 0;
+		hres = vres = 0;
+	}
+
+	dp_print_string(m,
+			DP_CONFIG_PARAM_CONNECTOR,
+			connector->name);
+	dp_print_string(m,
+			DP_CONFIG_PARAM_CONNECTOR_ID,
+			&connector->base.id);
+	dp_print_string(m,
+			DP_CONFIG_PARAM_LINK_RATE,
+			&intel_dp->link_bw);
+	dp_print_string(m,
+			DP_CONFIG_PARAM_LANE_COUNT,
+			&intel_dp->lane_count);
+	dp_print_string(m,
+			DP_CONFIG_PARAM_VOLTAGE_SWING,
+			&vs[0]);
+	dp_print_string(m,
+			DP_CONFIG_PARAM_PREEMPHASIS,
+			&pe[0]);
+	dp_print_string(m,
+			DP_CONFIG_PARAM_HRES,
+			&hres);
+	dp_print_string(m,
+			DP_CONFIG_PARAM_VRES,
+			&vres);
+	dp_print_string(m,
+			DP_CONFIG_PARAM_BPP,
+			&pipe_bpp);
+}
+
+enum dp_config_param dp_get_param_type(char *input_string)
+{
+	enum dp_config_param param_type = DP_CONFIG_PARAM_INVALID;
+	char *s;
+	int i = 0;
+
+	for (i = 0; i < DP_PARAMETER_COUNT; i++) {
+		s = dp_get_config_str(i);
+		if (strncmp(input_string, s,
+			    strlen(s)) == 0) {
+			param_type = (enum dp_config_param) i;
+			break;
+		}
+	}
+	return param_type;
+}
+
+int dp_get_param_value(enum dp_config_param param,
+			   char *input_string,
+			   void *output_value)
+{
+	int status = 0;
+	unsigned int *out_ptr = (unsigned int *)output_value;
+	char *index = input_string;
+
+	switch (param) {
+	case DP_CONFIG_PARAM_CONNECTOR:
+		output_value = (char *)index;
+		break;
+	case DP_CONFIG_PARAM_LINK_RATE:
+	case DP_CONFIG_PARAM_LANE_COUNT:
+	case DP_CONFIG_PARAM_VOLTAGE_SWING:
+	case DP_CONFIG_PARAM_PREEMPHASIS:
+		status = kstrtol(index, 16, (long *)out_ptr);
+		break;
+	case DP_CONFIG_PARAM_HRES:
+	case DP_CONFIG_PARAM_VRES:
+	case DP_CONFIG_PARAM_BPP:
+		status = kstrtol(index, 10, (long *)out_ptr);
+		break;
+	default:
+		/* Unhandled case */
+		status = -EINVAL;
+		*out_ptr = 0;
+		break;
+	}
+
+	return status;
+}
+
+int dp_tokenize_config(char *input_buffer, char *output[])
+{
+	char *base = input_buffer, *index, *end;
+	int line_count = 0;
+	int i = 0, len = 0;
+	int done = 0;
+
+	if (!input_buffer)
+		return 0;
+
+	while (!done) {
+		index = strpbrk(base, ":");
+		if (index) {
+			len = index - base;
+			*index = '\0';
+			index++;
+			/* Save the type string */
+			output[i] = base;
+			i++;
+			line_count++;
+			end = strpbrk(index, "\n\0");
+			if (end) {
+				*end = '\0';
+				/* Eat up whitespace */
+				while (*index <= 0x20)
+					index++;
+				output[i] = index;
+				i++;
+				line_count++;
+			} else
+				done = 1;
+			/* Move to the next section of the string */
+			base = end + 1;
+		} else
+			done = 1;
+	}
+	return line_count;
+}
+
+static int dp_parse_config(char *input_buffer,
+			   ssize_t buffer_size,
+			   struct intel_dp *intel_dp)
+{
+	int status = 0;
+	char *lines[MAX_DP_CONFIG_LINE_COUNT];
+	int i = 0;
+	struct dp_config parms[DP_PARAMETER_COUNT];
+	int line_count = 0;
+	char *buffer = input_buffer;
+	enum dp_config_param parm_type;
+	unsigned long parm_value;
+
+	line_count = dp_tokenize_config(buffer, lines);
+
+	if (line_count == 0) {
+		DRM_DEBUG_DRIVER("No lines to process\n");
+		return 0;
+	}
+
+	for (i = 0; i < line_count; i += 2) {
+		parm_type = dp_get_param_type(lines[i]);
+		if (parm_type != DP_CONFIG_PARAM_INVALID) {
+			status = dp_get_param_value(parm_type,
+							lines[i+1],
+							&parm_value);
+			if (status == 0) {
+				parms[parm_type].type = parm_type;
+				parms[parm_type].value = parm_value;
+			}
+		}
+	}
+
+	if (parms[DP_CONFIG_PARAM_LINK_RATE].value == 0x06 ||
+	    parms[DP_CONFIG_PARAM_LINK_RATE].value == 0x0a ||
+	    parms[DP_CONFIG_PARAM_LINK_RATE].value == 0x14) {
+		intel_dp->compliance_config.link_rate =
+			parms[DP_CONFIG_PARAM_LINK_RATE].value;
+	} else
+		return -EINVAL;
+
+	if (parms[DP_CONFIG_PARAM_LANE_COUNT].value == 0x01 ||
+	    parms[DP_CONFIG_PARAM_LANE_COUNT].value == 0x02 ||
+	    parms[DP_CONFIG_PARAM_LANE_COUNT].value == 0x04) {
+		intel_dp->compliance_config.lane_count =
+			parms[DP_CONFIG_PARAM_LANE_COUNT].value;
+	} else
+		return -EINVAL;
+
+	if (parms[DP_CONFIG_PARAM_VOLTAGE_SWING].value <= 0x03 &&
+	    parms[DP_CONFIG_PARAM_VOLTAGE_SWING].value >= 0x00) {
+		intel_dp->compliance_config.vswing_level =
+			parms[DP_CONFIG_PARAM_VOLTAGE_SWING].value;
+	} else
+		return -EINVAL;
+
+	if (parms[DP_CONFIG_PARAM_PREEMPHASIS].value <= 0x03 &&
+	    parms[DP_CONFIG_PARAM_PREEMPHASIS].value >= 0x00) {
+		intel_dp->compliance_config.preemp_level =
+			parms[DP_CONFIG_PARAM_PREEMPHASIS].value;
+	} else
+		return -EINVAL;
+
+	if (parms[DP_CONFIG_PARAM_BPP].value == 18 ||
+	    parms[DP_CONFIG_PARAM_BPP].value == 24 ||
+	    parms[DP_CONFIG_PARAM_BPP].value == 30 ||
+	    parms[DP_CONFIG_PARAM_BPP].value == 36) {
+		intel_dp->compliance_config.bits_per_pixel =
+			parms[DP_CONFIG_PARAM_PREEMPHASIS].value;
+	} else
+		return -EINVAL;
+
+	if (parms[DP_CONFIG_PARAM_HRES].value > 0 &&
+	    parms[DP_CONFIG_PARAM_HRES].value <= 8192) {
+		intel_dp->compliance_config.hres =
+			parms[DP_CONFIG_PARAM_HRES].value;
+	} else
+		return -EINVAL;
+
+	if (parms[DP_CONFIG_PARAM_VRES].value > 0 &&
+	    parms[DP_CONFIG_PARAM_VRES].value <= 8192) {
+		intel_dp->compliance_config.vres =
+			parms[DP_CONFIG_PARAM_VRES].value;
+	} else
+		return -EINVAL;
+
+	return status;
+}
+
+static int i915_displayport_config_ctl_show(struct seq_file *m, void *data)
+{
+	struct drm_device *dev = m->private;
+	struct drm_connector *connector;
+	struct list_head *connector_list = &dev->mode_config.connector_list;
+
+	if (!dev)
+		return -ENODEV;
+
+	list_for_each_entry(connector, connector_list, head) {
+
+		if (connector->connector_type !=
+		    DRM_MODE_CONNECTOR_DisplayPort)
+			continue;
+
+		if (connector->status == connector_status_connected) {
+			dp_show_config(m, connector);
+		} else {
+			dp_print_string(m,
+					DP_CONFIG_PARAM_CONNECTOR,
+					connector->name);
+			dp_print_string(m,
+					DP_CONFIG_PARAM_CONNECTOR_ID,
+					&connector->base.id);
+			seq_puts(m, "disconnected\n");
+		}
+	}
+
+	return 0;
+}
+
+static int i915_displayport_config_ctl_open(struct inode *inode,
+				       struct file *file)
+{
+	struct drm_device *dev = inode->i_private;
+
+	return single_open(file, i915_displayport_config_ctl_show, dev);
+}
+
+static ssize_t i915_displayport_config_ctl_write(struct file *file,
+					    const char __user *ubuf,
+					    size_t len, loff_t *offp)
+{
+	char *input_buffer;
+	int status = 0;
+	struct seq_file *m;
+	struct drm_device *dev;
+	struct drm_connector *connector;
+	struct list_head *connector_list;
+	struct intel_dp *intel_dp;
+
+	m = file->private_data;
+	if (!m) {
+		status = -ENODEV;
+		return status;
+	}
+	dev = m->private;
+
+	if (!dev) {
+		status = -ENODEV;
+		return status;
+	}
+	connector_list = &dev->mode_config.connector_list;
+
+	if (len == 0)
+		return 0;
+
+	input_buffer = kmalloc(len + 1, GFP_KERNEL);
+	if (!input_buffer)
+		return -ENOMEM;
+
+	if (copy_from_user(input_buffer, ubuf, len)) {
+		status = -EFAULT;
+		goto out;
+	}
+
+	input_buffer[len] = '\0';
+	DRM_DEBUG_DRIVER("Copied %d bytes from user\n", (unsigned int)len);
+
+	list_for_each_entry(connector, connector_list, head) {
+		if (connector->connector_type ==
+		    DRM_MODE_CONNECTOR_DisplayPort &&
+		    connector->status == connector_status_connected) {
+			intel_dp = enc_to_intel_dp(connector->encoder);
+			status = dp_parse_config(input_buffer, len, intel_dp);
+			if (status < 0)
+				goto out;
+		}
+	}
+out:
+	kfree(input_buffer);
+	if (status < 0)
+		return status;
+
+	*offp += len;
+	return len;
+}
+
+static const struct file_operations i915_displayport_config_ctl_fops = {
+	.owner = THIS_MODULE,
+	.open = i915_displayport_config_ctl_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+	.write = i915_displayport_config_ctl_write
+};
+
 static void wm_latency_show(struct seq_file *m, const uint16_t wm[8])
 {
 	struct drm_device *dev = m->private;
@@ -4450,6 +4920,7 @@ static const struct i915_debugfs_files {
 	{"i915_spr_wm_latency", &i915_spr_wm_latency_fops},
 	{"i915_cur_wm_latency", &i915_cur_wm_latency_fops},
 	{"i915_fbc_false_color", &i915_fbc_fc_fops},
+	{"i915_displayport_config_ctl", &i915_displayport_config_ctl_fops}
 };
 
 void intel_display_crc_init(struct drm_device *dev)
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 74b30c1..c813d3c 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -592,6 +592,18 @@ struct intel_hdmi {
 struct intel_dp_mst_encoder;
 #define DP_MAX_DOWNSTREAM_PORTS		0x10
 
+struct intel_dp_link_config {
+	uint8_t lane_count;
+	uint8_t link_rate;
+	uint8_t vswing_level;
+	uint8_t preemp_level;
+	uint32_t hres;
+	uint32_t vres;
+	uint8_t bits_per_pixel;
+	struct drm_display_mode *compliance_mode;
+	char connector_name[12];
+};
+
 struct intel_dp {
 	uint32_t output_reg;
 	uint32_t aux_ch_ctl_reg;
@@ -651,6 +663,7 @@ struct intel_dp {
 	/* Displayport compliance testing */
 	unsigned long compliance_test_data;
 	bool compliance_testing_active;
+	struct intel_dp_link_config compliance_config;
 };
 
 struct intel_digital_port {
