From patchwork Thu Oct 9 15:38:05 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Todd Previte X-Patchwork-Id: 5058951 Return-Path: X-Original-To: patchwork-intel-gfx@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 47AEFC11AD for ; Thu, 9 Oct 2014 15:38:57 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 84DA52013D for ; Thu, 9 Oct 2014 15:38:54 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.kernel.org (Postfix) with ESMTP id 180BB2017D for ; Thu, 9 Oct 2014 15:38:53 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 8DEDA6E383; Thu, 9 Oct 2014 08:38:52 -0700 (PDT) X-Original-To: intel-gfx@lists.freedesktop.org Delivered-To: intel-gfx@lists.freedesktop.org Received: from mail-yh0-f54.google.com (mail-yh0-f54.google.com [209.85.213.54]) by gabe.freedesktop.org (Postfix) with ESMTP id 544126E382 for ; Thu, 9 Oct 2014 08:38:51 -0700 (PDT) Received: by mail-yh0-f54.google.com with SMTP id z6so853892yhz.27 for ; Thu, 09 Oct 2014 08:38:51 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=xC71N+QWEDb5Fel2mKEQOn5rg1QMrIGzFVVbC5ak5vg=; b=jSHNaMlSHqjl0MUql1Cl1BVan0eH3hWLJb632Oe2U+2ozpTChbmGkc/qwA5nGg+SJz PaCTZ4wMrIDu3a1nMBu/gxiBv1k3aI58FClGgtMr53rI2xiRnTSpGA+FkZGWxkfi+kSK PB0Zg8uZYo4iKSRYdyo8h/NGCeVycHydGXPcngi1b4kelHSSHp5oito3gKsfZ0SNwqdM X43/9Mf0mZg67q1USAuHBVl8/KZGhH9W2mI8YiOpTc5cp5qr3J/FrBEdsKzkCL9jCn1l QmyDdGf8ybQLvo+TfpXaNTH1Mnqbn/4cSrf5RDUa+/YCPEm9xPeC2J6HUFiY3kvJBkOP wpMw== X-Received: by 10.70.96.74 with SMTP id dq10mr34197pdb.165.1412869130950; Thu, 09 Oct 2014 08:38:50 -0700 (PDT) Received: from localhost.localdomain (ip72-201-95-47.ph.ph.cox.net. [72.201.95.47]) by mx.google.com with ESMTPSA id y1sm844710pbw.89.2014.10.09.08.38.49 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 09 Oct 2014 08:38:49 -0700 (PDT) From: Todd Previte To: intel-gfx@lists.freedesktop.org Date: Thu, 9 Oct 2014 08:38:05 -0700 Message-Id: <1412869090-48010-6-git-send-email-tprevite@gmail.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1412869090-48010-1-git-send-email-tprevite@gmail.com> References: <1412869090-48010-1-git-send-email-tprevite@gmail.com> Subject: [Intel-gfx] [PATCH 05/10] drm/i915: Add debugfs interface for Displayport debug and compliance testing X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: intel-gfx-bounces@lists.freedesktop.org Sender: "Intel-gfx" X-Spam-Status: No, score=-4.1 required=5.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_MED, T_DKIM_INVALID, T_RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Adds an interface that allows for Displayport configuration information to be accessed through debugfs. The information paramters provided here are as follows: Link rate Lane count Bits per pixel Voltage swing level Preemphasis level Display mode These parameters are intended to be used by userspace applications that need access to the link configuration of any active Displayport connection. Additionally, applications can make adjustments to these parameters through this interface to allow userspace application to have fine-grained control over link training paramters. Signed-off-by: Todd Previte --- drivers/gpu/drm/i915/i915_debugfs.c | 389 ++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/i915/intel_dp.c | 13 +- 2 files changed, 397 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index da4036d..2dada18 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include "intel_drv.h" @@ -51,6 +52,46 @@ static const char *yesno(int v) return v ? "yes" : "no"; } +#define DP_PARAMETER_COUNT 8 +#define MAX_DP_CONFIG_LINE_COUNT 64 + +enum dp_config_param { + DP_CONFIG_PARAM_INVALID = -1, + DP_CONFIG_PARAM_CONNECTOR = 0, + 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 +}; + +struct dp_config { + enum dp_config_param type; + unsigned long value; +}; + +const char *dp_conf_tokens[DP_PARAMETER_COUNT] = { + "Connector", + "Link Rate", + "Lane Count", + "Voltage Swing Level", + "Preemphasis Level", + "Horizontal Resolution", + "Vertical Resolution", + "Bits Per Pixel" +}; +/* Strings for writing out dp_configs */ +#define DP_CONF_STR_CONNECTOR "Connector: %s\n" +#define DP_CONF_STR_LINKRATE "Link Rate: %02x\n" +#define DP_CONF_STR_LANES "Lane Count: %u\n" +#define DP_CONF_STR_VSWING "Voltage Swing Level: %u\n" +#define DP_CONF_STR_PREEMP "Preemphasis Level: %u\n" +#define DP_CONF_STR_HRES "Horizontal Resolution: %d\n" +#define DP_CONF_STR_VRES "Vertical Resolution: %d\n" +#define DP_CONF_STR_BPP "Bits Per Pixel: %u\n" + /* 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 @@ -3505,6 +3546,353 @@ static const struct file_operations i915_display_crc_ctl_fops = { .write = display_crc_ctl_write }; +static void displayport_show_config_info(struct seq_file *m, + struct intel_connector *intel_connector) +{ + struct intel_encoder *intel_encoder = intel_connector->encoder; + struct intel_dp *intel_dp; + struct intel_crtc *icrtc; + int pipe_bpp, hres, vres; + uint8_t vs[4], pe[4]; + struct drm_display_mode *mode; + int i = 0; + + if (intel_encoder) { + 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) >> 3; + } + if (intel_encoder->type == INTEL_OUTPUT_DISPLAYPORT) { + if (intel_encoder->new_crtc) { + pipe_bpp = intel_encoder->new_crtc->config.pipe_bpp; + mode = &intel_encoder->new_crtc->config.adjusted_mode; + hres = mode->hdisplay; + vres = mode->vdisplay; + } else if (intel_encoder->base.crtc) { + icrtc = to_intel_crtc(intel_encoder->base.crtc); + pipe_bpp = icrtc->config.pipe_bpp; + mode = &icrtc->config.adjusted_mode; + hres = mode->hdisplay; + vres = mode->vdisplay; + } else { + pipe_bpp = 0; + hres = vres = 0; + } + seq_printf(m, DP_CONF_STR_CONNECTOR, + intel_connector->base.name); + seq_printf(m, DP_CONF_STR_LINKRATE, intel_dp->link_bw); + seq_printf(m, DP_CONF_STR_LANES, intel_dp->lane_count); + seq_printf(m, DP_CONF_STR_VSWING, vs[0]); + seq_printf(m, DP_CONF_STR_PREEMP, pe[0]); + seq_printf(m, DP_CONF_STR_HRES, hres); + seq_printf(m, DP_CONF_STR_VRES, vres); + seq_printf(m, DP_CONF_STR_BPP, pipe_bpp); + } + } +} + +enum dp_config_param displayport_get_config_param_type(char *input_string) +{ + enum dp_config_param param_type = DP_CONFIG_PARAM_INVALID; + int i = 0; + + for (i = 0; i < DP_PARAMETER_COUNT; i++) { + if (strncmp(input_string, dp_conf_tokens[i], + strlen(dp_conf_tokens[i])) == 0) { + param_type = (enum dp_config_param) i; + break; + } + } + return param_type; +} + +int value_for_config_param(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 tokenize_dp_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 displayport_parse_config(char *input_buffer, + ssize_t buffer_size, + struct intel_dp_link_config *dp_conf) +{ + 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 = tokenize_dp_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 = displayport_get_config_param_type(lines[i]); + if (parm_type != DP_CONFIG_PARAM_INVALID) { + status = value_for_config_param(parm_type, + lines[i+1], + &parm_value); + if (status == 0) { + parms[parm_type].type = parm_type; + parms[parm_type].value = parm_value; + } + } + } + + for (i = 1; i < DP_PARAMETER_COUNT; i++) + DRM_DEBUG_DRIVER("%s = %lu\n", + dp_conf_tokens[parms[i].type], + parms[i].value); + + /* Validate any/all incoming parameters */ + strcpy(dp_conf->connector_name, + (char *)parms[DP_CONFIG_PARAM_CONNECTOR].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) { + dp_conf->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) { + dp_conf->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) { + dp_conf->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) { + dp_conf->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) { + dp_conf->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) { + dp_conf->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) { + dp_conf->vres = + parms[DP_CONFIG_PARAM_VRES].value; + } else + return -EINVAL; + + return status; +} + +static int displayport_config_ctl_show(struct seq_file *m, void *data) +{ + struct drm_device *dev = m->private; + struct drm_connector *connector; + struct intel_encoder *intel_encoder; + struct intel_connector *intel_connector; + + if (!dev) + return -ENODEV; + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + intel_connector = to_intel_connector(connector); + intel_encoder = intel_connector->encoder; + if (connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) { + if (intel_encoder->type == INTEL_OUTPUT_DISPLAYPORT || + intel_encoder->type == INTEL_OUTPUT_UNKNOWN) { + if (connector->status == connector_status_connected) { + displayport_show_config_info(m, intel_connector); + } else { + seq_printf(m, DP_CONF_STR_CONNECTOR, + intel_connector->base.name); + seq_printf(m, "disconnected\n"); + } + } + } + } + + return 0; +} + +static int displayport_config_ctl_open(struct inode *inode, + struct file *file) +{ + struct drm_device *dev = inode->i_private; + + return single_open(file, displayport_config_ctl_show, dev); +} + +static ssize_t 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 intel_encoder *intel_encoder; + struct intel_connector *intel_connector; + struct intel_dp *intel_dp; + struct intel_dp_link_config dp_conf; + + m = file->private_data; + if (!m) { + status = -ENODEV; + return status; + } + dev = m->private; + + if (!dev) { + status = -ENODEV; + return status; + } + + 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'; + memset(&dp_conf, 0, sizeof(struct intel_dp_link_config)); + DRM_DEBUG_DRIVER("Copied %d bytes from user\n", (unsigned int)len); + status = displayport_parse_config(input_buffer, len, &dp_conf); + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + intel_connector = to_intel_connector(connector); + intel_encoder = intel_connector->encoder; + if (connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) { + if (intel_encoder->type == INTEL_OUTPUT_DISPLAYPORT || + intel_encoder->type == INTEL_OUTPUT_UNKNOWN) { + intel_dp = enc_to_intel_dp(&intel_encoder->base); + memcpy(&intel_dp->compliance_config, + &dp_conf, + sizeof(struct intel_dp_link_config)); + } + } + } + + +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 = displayport_config_ctl_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .write = displayport_config_ctl_write +}; + static void wm_latency_show(struct seq_file *m, const uint16_t wm[5]) { struct drm_device *dev = m->private; @@ -4208,6 +4596,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_dp.c b/drivers/gpu/drm/i915/intel_dp.c index a7acc61..b3ddd15 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -3968,7 +3968,7 @@ intel_dp_get_sink_irq_esi(struct intel_dp *intel_dp, u8 *sink_irq_vector) static uint8_t intel_dp_autotest_link_training(struct intel_dp *intel_dp) { - uint8_t test_result = DP_TEST_NAK; + uint8_t test_result = DP_TEST_ACK; return test_result; } @@ -4013,21 +4013,25 @@ intel_dp_handle_test_request(struct intel_dp *intel_dp) DRM_DEBUG_KMS("Executing LINK_TRAINING request\n"); intel_dp->compliance_test_data = DP_TEST_LINK_TRAINING; response = intel_dp_autotest_link_training(intel_dp); + status = intel_dp_signal_userspace(intel_dp); break; case DP_TEST_LINK_VIDEO_PATTERN: DRM_DEBUG_KMS("Executing TEST_PATTERN request\n"); intel_dp->compliance_test_data = DP_TEST_LINK_VIDEO_PATTERN; response = intel_dp_autotest_video_pattern(intel_dp); + status = intel_dp_signal_userspace(intel_dp); break; case DP_TEST_LINK_EDID_READ: DRM_DEBUG_KMS("Executing EDID request\n"); intel_dp->compliance_test_data = DP_TEST_LINK_EDID_READ; response = intel_dp_autotest_edid(intel_dp); + status = intel_dp_signal_userspace(intel_dp); break; case DP_TEST_LINK_PHY_TEST_PATTERN: DRM_DEBUG_KMS("Executing PHY_PATTERN request\n"); intel_dp->compliance_test_data = DP_TEST_LINK_PHY_TEST_PATTERN; response = intel_dp_autotest_phy_pattern(intel_dp); + status = intel_dp_signal_userspace(intel_dp); break; /* FAUX is optional in DP 1.2*/ case DP_TEST_LINK_FAUX_PATTERN: @@ -4038,10 +4042,9 @@ intel_dp_handle_test_request(struct intel_dp *intel_dp) DRM_DEBUG_KMS("Unhandled test request\n"); break; } - if (status != 0) { - response = DP_TEST_NAK; - DRM_DEBUG_KMS("Error %d processing test request\n", status); - } + if (status != 0) + DRM_DEBUG_KMS("Status code %d processing test request\n", + status); status = drm_dp_dpcd_write(&intel_dp->aux, DP_TEST_RESPONSE, &response, 1);