From patchwork Tue Aug 12 12:38:20 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Sharma, Shashank" X-Patchwork-Id: 4712621 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 6E299C0338 for ; Tue, 12 Aug 2014 12:33:59 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 622332015E for ; Tue, 12 Aug 2014 12:33:58 +0000 (UTC) Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by mail.kernel.org (Postfix) with ESMTP id 3EE9520120 for ; Tue, 12 Aug 2014 12:33:57 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id BF7326E30E; Tue, 12 Aug 2014 05:33:56 -0700 (PDT) X-Original-To: intel-gfx@lists.freedesktop.org Delivered-To: intel-gfx@lists.freedesktop.org Received: from mga09.intel.com (mga09.intel.com [134.134.136.24]) by gabe.freedesktop.org (Postfix) with ESMTP id 7D2456E30E for ; Tue, 12 Aug 2014 05:33:55 -0700 (PDT) Received: from orsmga001.jf.intel.com ([10.7.209.18]) by orsmga102.jf.intel.com with ESMTP; 12 Aug 2014 05:27:44 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.01,849,1400050800"; d="scan'208";a="557391224" Received: from shashanks-desktop.iind.intel.com ([10.223.26.11]) by orsmga001.jf.intel.com with ESMTP; 12 Aug 2014 05:33:28 -0700 From: shashank.sharma@intel.com To: intel-gfx@lists.freedesktop.org, daniel.vetter@intel.com, shobhit.kumar@intel.com Date: Tue, 12 Aug 2014 18:08:20 +0530 Message-Id: <1407847101-21654-2-git-send-email-shashank.sharma@intel.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1407847101-21654-1-git-send-email-shashank.sharma@intel.com> References: <1407847101-21654-1-git-send-email-shashank.sharma@intel.com> Subject: [Intel-gfx] [PATCH 1/2] drm/i915: Optimize HDMI EDID reads 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.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, 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 From: Shashank Sharma The current hdmi_detect() function is getting called from many places, few of these are: 1. HDMI hot plug interrupt bottom half 2. get_resources() IOCTL family 3. drm_helper_probe_single_connector_modes() family 4. output_poll_execute() 5. status_show() etc... Every time this function is called, it goes and reads HDMI EDID over DDC channel. Ideally, reading EDID is only required when there is a real hot plug, and then for all IOCTL and userspace detect functions can be answered using this same EDID. The current patch adds EDID caching for a finite duration (1 minute) This is how it works: 1. With in this caching duration, when usespace get_resource and other modeset_detect calls get called, we can use the cached EDID. 2. Even the get_mode function can use the cached EDID. 3. A delayed work will clear the cached EDID after the timeout. 4. If there is a real HDMI hotplug, within the caching duration, the cached EDID is updates, and a new delayed work is scheduled. Signed-off-by: Shashank Sharma --- drivers/gpu/drm/i915/intel_drv.h | 4 ++ drivers/gpu/drm/i915/intel_hdmi.c | 92 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 90 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 28d185d..185a45a 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -110,6 +110,8 @@ #define INTEL_DSI_VIDEO_MODE 0 #define INTEL_DSI_COMMAND_MODE 1 +#define INTEL_HDMI_EDID_CACHING_SEC 60 + struct intel_framebuffer { struct drm_framebuffer base; struct drm_i915_gem_object *obj; @@ -507,6 +509,8 @@ struct intel_hdmi { enum hdmi_force_audio force_audio; bool rgb_quant_range_selectable; enum hdmi_picture_aspect aspect_ratio; + struct edid *edid; + struct delayed_work edid_work; void (*write_infoframe)(struct drm_encoder *encoder, enum hdmi_infoframe_type type, const void *frame, ssize_t len); diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 5f47d35..8dc3970 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -962,6 +962,22 @@ bool intel_hdmi_compute_config(struct intel_encoder *encoder, return true; } +/* Work function to invalidate EDID caching */ +static void intel_hdmi_invalidate_edid(struct work_struct *work) +{ + struct intel_hdmi *intel_hdmi = container_of(to_delayed_work(work), + struct intel_hdmi, edid_work); + struct drm_device *dev = intel_hdmi_to_dev(intel_hdmi); + struct drm_mode_config *mode_config = &dev->mode_config; + + mutex_lock(&mode_config->mutex); + /* Checkpatch says kfree is NULL protected */ + kfree(intel_hdmi->edid); + intel_hdmi->edid = NULL; + mutex_unlock(&mode_config->mutex); + DRM_DEBUG_DRIVER("cleaned up cached EDID\n"); +} + static enum drm_connector_status intel_hdmi_detect(struct drm_connector *connector, bool force) { @@ -978,15 +994,58 @@ intel_hdmi_detect(struct drm_connector *connector, bool force) DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", connector->base.id, connector->name); + /* + * hdmi_detect() gets called from both get_resource() + * and HDMI hpd bottom half work function. + * Its not required to read EDID for every detect call until it's is + * from a hot plug. Lets cache the EDID as soon as we get + * a HPD, and then try to re-use this for all the non hpd detact calls + * coming with in a finite duration. + */ + if (INTEL_INFO(dev)->gen < 6) + /* Do not break old platforms */ + goto skip_optimization; + + /* If call is from HPD, do check real status by reading EDID */ + if (!force) + goto skip_optimization; + + /* This is a non-hpd call, lets see if we can optimize this */ + if (intel_hdmi->edid) { + /* + * So this is a non-hpd call, within the duration when + * EDID caching is valid. So previous status (valid) + * of connector is re-usable. + */ + if (connector->status != connector_status_unknown) { + DRM_DEBUG_DRIVER("Reporting force status\n"); + return connector->status; + } + } + +skip_optimization: power_domain = intel_display_port_power_domain(intel_encoder); intel_display_power_get(dev_priv, power_domain); intel_hdmi->has_hdmi_sink = false; intel_hdmi->has_audio = false; intel_hdmi->rgb_quant_range_selectable = false; + + /* + * You are well deserving, dear code, as you have survived + * all the optimizations. Now go and enjoy reading EDID + */ edid = drm_get_edid(connector, - intel_gmbus_get_adapter(dev_priv, - intel_hdmi->ddc_bus)); + intel_gmbus_get_adapter(dev_priv, + intel_hdmi->ddc_bus)); + /* + * Now when we have read new EDID, update cached EDID with + * latest (both NULL or non NULL). Cancel the delayed work + * which cleans up the cached EDID. Re-schedule if required. + */ + kfree(intel_hdmi->edid); + intel_hdmi->edid = edid; + cancel_delayed_work_sync(&intel_hdmi->edid_work); if (edid) { if (edid->input & DRM_EDID_INPUT_DIGITAL) { @@ -997,8 +1056,17 @@ intel_hdmi_detect(struct drm_connector *connector, bool force) intel_hdmi->has_audio = drm_detect_monitor_audio(edid); intel_hdmi->rgb_quant_range_selectable = drm_rgb_quant_range_selectable(edid); + /* + * Allow re-use of cached EDID for 60 sec, as + * userspace modeset should happen within this + * duration, and multiple detect calls will be + * handled using cached EDID. + */ + schedule_delayed_work(&intel_hdmi->edid_work, + msecs_to_jiffies( + INTEL_HDMI_EDID_CACHING_SEC + * 1000)); } - kfree(edid); } if (status == connector_status_connected) { @@ -1027,13 +1095,22 @@ static int intel_hdmi_get_modes(struct drm_connector *connector) power_domain = intel_display_port_power_domain(intel_encoder); intel_display_power_get(dev_priv, power_domain); - - ret = intel_ddc_get_modes(connector, + /* + * GEN6 and + have software support for EDID caching, so + * use cached_edid from detect call, if available. + */ + if (intel_hdmi->edid && (INTEL_INFO(connector->dev)->gen >= 6)) { + ret = intel_connector_update_modes(connector, + intel_hdmi->edid); + DRM_DEBUG_DRIVER("Using cached EDID, got %d modes\n", ret); + } else { + ret = intel_ddc_get_modes(connector, intel_gmbus_get_adapter(dev_priv, intel_hdmi->ddc_bus)); + DRM_DEBUG_DRIVER("Read EDID, got %d modes\n", ret); + } intel_display_power_put(dev_priv, power_domain); - return ret; } @@ -1661,5 +1738,8 @@ void intel_hdmi_init(struct drm_device *dev, int hdmi_reg, enum port port) intel_dig_port->hdmi.hdmi_reg = hdmi_reg; intel_dig_port->dp.output_reg = 0; + /* Work function to invalidate cached EDID after timeout */ + INIT_DELAYED_WORK(&(intel_dig_port->hdmi.edid_work), + intel_hdmi_invalidate_edid); intel_hdmi_init_connector(intel_dig_port, intel_connector); }