From: Shashank Sharma <shashank.sharma@xxxxxxxxx> During the HDMI complaince tests, most of the HDMI analyzers issue a soft HPD, to automate the tests. This process keeps the HDMI cable connected, and DDC chhanel alive. HDMI detect() logic relies on EDID readability, to decide if its a HDMI connect or disconnect event. But in this case, the EDID is always readable, as HDMI cable is physically connected and DDC channel is UP, so detect() always reports a HDMI connect even if its intended to be a HDMI disconnect call. So if HDMI compliance is enabled, we should rely on the live status register, than EDID availability. This patch adds: 1. One kernel command line parameter for i915 module, which indicates if we want to support HDMI compliance, for this platform. 2. A new function to read EDID, which gets called only in case of HDMI compliance support is required. This function reads EDID only if live status register is up. The normal code flow doesn't get effected if kernel command line parameter is not enabled. 3. After various experiments on VLV2 board, with various HDMI monitors we have seen that, with few monitors, the live status register gets set after a slight delay, and then stays reliably. To support such monitors, there is a busy-loop added, with a max delay upto 50ms, with a status check after every 10ms. Please see the comment in intel_hdmi_get_edid. Signed-off-by: Shashank Sharma <shashank.sharma@xxxxxxxxx> --- drivers/gpu/drm/i915/i915_drv.h | 1 + drivers/gpu/drm/i915/i915_params.c | 6 +++ drivers/gpu/drm/i915/intel_drv.h | 1 + drivers/gpu/drm/i915/intel_hdmi.c | 91 +++++++++++++++++++++++++++++++++++++- 4 files changed, 97 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index 2a372f2..19e4f97 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -2162,6 +2162,7 @@ struct i915_params { bool disable_vtd_wa; int use_mmio_flip; bool mmio_debug; + bool hdmi_compliance_support; }; extern struct i915_params i915 __read_mostly; diff --git a/drivers/gpu/drm/i915/i915_params.c b/drivers/gpu/drm/i915/i915_params.c index 62ee830..27dcc1a 100644 --- a/drivers/gpu/drm/i915/i915_params.c +++ b/drivers/gpu/drm/i915/i915_params.c @@ -50,6 +50,7 @@ struct i915_params i915 __read_mostly = { .disable_vtd_wa = 0, .use_mmio_flip = 0, .mmio_debug = 0, + .hdmi_compliance_support = 0, }; module_param_named(modeset, i915.modeset, int, 0400); @@ -167,3 +168,8 @@ module_param_named(mmio_debug, i915.mmio_debug, bool, 0600); MODULE_PARM_DESC(mmio_debug, "Enable the MMIO debug code (default: false). This may negatively " "affect performance."); + +module_param_named(hdmi_compliance_support, i915.hdmi_compliance_support, + bool, 0400); +MODULE_PARM_DESC(hdmi_compliance_support, + "Support for HDMI compliance in i915 driver 1=On 0=Off"); diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h index 185a45a..0b3798a 100644 --- a/drivers/gpu/drm/i915/intel_drv.h +++ b/drivers/gpu/drm/i915/intel_drv.h @@ -111,6 +111,7 @@ #define INTEL_DSI_COMMAND_MODE 1 #define INTEL_HDMI_EDID_CACHING_SEC 60 +#define INTEL_HDMI_LIVE_STATUS_DELAY_MS 10 struct intel_framebuffer { struct drm_framebuffer base; diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c index 8dc3970..06eb9de 100644 --- a/drivers/gpu/drm/i915/intel_hdmi.c +++ b/drivers/gpu/drm/i915/intel_hdmi.c @@ -978,6 +978,90 @@ static void intel_hdmi_invalidate_edid(struct work_struct *work) DRM_DEBUG_DRIVER("cleaned up cached EDID\n"); } +/* Get HDMI live status */ +static bool intel_hdmi_live_status(struct drm_device *dev, + struct intel_hdmi *intel_hdmi) +{ + uint32_t status_bit; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_digital_port *intel_dig_port = + hdmi_to_dig_port(intel_hdmi); + + DRM_DEBUG_KMS("Reading Live status\n"); + + /* Live status bit is 29 for PORT_B, 28 for C and 27 for D */ + status_bit = (PORTB_HOTPLUG_LIVE_STATUS_VLV >> + (PORT_B - intel_dig_port->port)); + + return I915_READ(PORT_HOTPLUG_STAT) & status_bit; +} + +/* Read DDC and get EDID */ +struct edid *intel_hdmi_get_edid(struct drm_connector *connector, bool force) +{ + bool live_status = false; + struct edid *new_edid = NULL; + struct i2c_adapter *adapter; + struct drm_device *dev = connector->dev; + struct drm_i915_private *dev_priv = dev->dev_private; + struct intel_hdmi *intel_hdmi = intel_attached_hdmi(connector); + u8 retry = 5; + + if (!intel_hdmi) { + DRM_ERROR("Invalid input to get hdmi\n"); + return NULL; + } + + /* + * Read EDID only when live status is up. + * This condition is required for HDMI analyzers, where + * there is no physical disconnect of HDMI cable, but + * only a HPD is issued. In this case, if we decide the status of + * HDMI based on EDID availability from DDC channel, its always + * available. So rely on live status. + */ + while (retry--) { + /* + * Many HDMI monitors set the live status after some delay + * Add some tolerance for them. From few experiments on VLV + * with a set of monitors, we got to know that a delay close to + * 50 ms can cover this gap. + * Retry reading live status 5 times, in 10ms duration each + * But make sure that you be well within limit of a HDCP unplug + * which can be as small as 100ms + */ + live_status = intel_hdmi_live_status(dev, intel_hdmi); + if (!live_status) + mdelay(INTEL_HDMI_LIVE_STATUS_DELAY_MS); + else + break; + } + + /* + * Read EDID only if live status is up OR we are forced + * with a knife + */ + if (live_status || force) { + DRM_DEBUG_DRIVER("HDMI Live status is up\n"); + adapter = intel_gmbus_get_adapter(dev_priv, + intel_hdmi->ddc_bus); + + if (!adapter) { + DRM_ERROR("Get_hdmi cant get adapter\n"); + return NULL; + } + + new_edid = drm_get_edid(connector, adapter); + if (!new_edid) { + DRM_ERROR("Get_hdmi cant read edid\n"); + return NULL; + } + + DRM_DEBUG_KMS("Live status up, got EDID"); + } + return new_edid; +} + static enum drm_connector_status intel_hdmi_detect(struct drm_connector *connector, bool force) { @@ -1035,9 +1119,12 @@ skip_optimization: * You are well deserving, dear code, as you have survived * all the optimizations. Now go and enjoy reading EDID */ - edid = drm_get_edid(connector, + if (i915.hdmi_compliance_support && (INTEL_INFO(dev)->gen >= 6)) + edid = intel_hdmi_get_edid(connector, false); + else + edid = drm_get_edid(connector, intel_gmbus_get_adapter(dev_priv, - intel_hdmi->ddc_bus)); + 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 -- 1.9.1 _______________________________________________ Intel-gfx mailing list Intel-gfx@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/intel-gfx