From patchwork Tue Jul 20 22:44:45 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jesse Barnes X-Patchwork-Id: 113125 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by demeter.kernel.org (8.14.4/8.14.3) with ESMTP id o6KMkCUV014248 for ; Tue, 20 Jul 2010 22:46:50 GMT Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id B99CF9EB49 for ; Tue, 20 Jul 2010 15:46:12 -0700 (PDT) X-Original-To: intel-gfx@lists.freedesktop.org Delivered-To: intel-gfx@lists.freedesktop.org Received: from cpoproxy2-pub.bluehost.com (cpoproxy2-pub.bluehost.com [67.222.39.38]) by gabe.freedesktop.org (Postfix) with SMTP id 2B5D89EB39 for ; Tue, 20 Jul 2010 15:44:37 -0700 (PDT) Received: (qmail 20489 invoked by uid 0); 20 Jul 2010 22:44:36 -0000 Received: from unknown (HELO box514.bluehost.com) (74.220.219.114) by cpoproxy2.bluehost.com with SMTP; 20 Jul 2010 22:44:36 -0000 DomainKey-Signature: a=rsa-sha1; q=dns; c=nofws; s=default; d=virtuousgeek.org; h=Received:Date:From:To:Subject:Message-ID:In-Reply-To:References:X-Mailer:Mime-Version:Content-Type:Content-Transfer-Encoding:X-Identified-User; b=Iqn0n+tk3hAwc4CvcuVe86yNIjoht180oZIxI1h1rnwtZwZUIQqiwSLTGugZtoNLDW26NtHmcXX8wwpP3S3X90XJnMAjvzmW8Sl9vWAUxOU5P/rEUK1LTVXtM5IURVfm; Received: from [75.110.194.140] (helo=localhost) by box514.bluehost.com with esmtpsa (TLSv1:AES128-SHA:128) (Exim 4.69) (envelope-from ) id 1ObLYC-0004hv-CQ; Tue, 20 Jul 2010 16:44:36 -0600 Date: Tue, 20 Jul 2010 15:44:45 -0700 From: Jesse Barnes To: intel-gfx@lists.freedesktop.org, dri-devel@lists.freedesktop.org Message-ID: <20100720154445.06c62f25@virtuousgeek.org> In-Reply-To: <20100720154417.32091370@virtuousgeek.org> References: <20100720154417.32091370@virtuousgeek.org> X-Mailer: Claws Mail 3.7.6 (GTK+ 2.18.9; x86_64-redhat-linux-gnu) Mime-Version: 1.0 X-Identified-User: {10642:box514.bluehost.com:virtuous:virtuousgeek.org} {sentby:smtp auth 75.110.194.140 authed with jbarnes@virtuousgeek.org} Subject: [Intel-gfx] [PATCH 2/2] drm/i915: use GMBUS for EDID fetching X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.11 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: intel-gfx-bounces+patchwork-intel-gfx=patchwork.kernel.org@lists.freedesktop.org Errors-To: intel-gfx-bounces+patchwork-intel-gfx=patchwork.kernel.org@lists.freedesktop.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter.kernel.org [140.211.167.41]); Tue, 20 Jul 2010 22:46:51 +0000 (UTC) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 42c6024..b89599c 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -507,12 +507,52 @@ # define GPIO_DATA_VAL_IN (1 << 12) # define GPIO_DATA_PULLUP_DISABLE (1 << 13) -#define GMBUS0 0x5100 -#define GMBUS1 0x5104 -#define GMBUS2 0x5108 -#define GMBUS3 0x510c -#define GMBUS4 0x5110 -#define GMBUS5 0x5120 +#define GMBUS0 0x5100 /* clock/port select */ +#define GMBUS_RATE_100KHZ (0<<8) +#define GMBUS_RATE_50KHZ (1<<8) +#define GMBUS_RATE_400KHZ (2<<8) /* reserved on Pineview */ +#define GMBUS_RATE_1MHZ (3<<8) /* reserved on Pineview */ +#define GMBUS_HOLD_EXT (1<<7) /* 300ns hold time, rsvd on Pineview */ +#define GMBUS_PORT_DISABLED 0 +#define GMBUS_PORT_SSC 1 +#define GMBUS_PORT_VGADDC 2 +#define GMBUS_PORT_PANEL 3 +#define GMBUS_PORT_DPC 4 /* HDMIC */ +#define GMBUS_PORT_DPB 5 /* SDVO, HDMIB */ +#define GMBUS_PORT_DPD 6 /* HDMID */ + /* 7 reserved */ +#define GMBUS1 0x5104 /* command/status */ +#define GMBUS_SW_CLR_INT (1<<31) +#define GMBUS_SW_RDY (1<<30) +#define GMBUS_ENT (1<<29) /* enable timeout */ +#define GMBUS_CYCLE_NONE (0<<25) +#define GMBUS_CYCLE_NI_NS_WAIT (1<<25) +#define GMBUS_CYCLE_I_NS_WAIT (3<<25) +#define GMBUS_CYCLE_STOP (4<<25) +#define GMBUS_CYCLE_NI_STOP (5<<25) +#define GMBUS_CYCLE_I_STOP (7<<25) +#define GMBUS_BYTE_COUNT_SHIFT 16 +#define GMBUS_SLAVE_INDEX_SHIFT 8 +#define GMBUS_SLAVE_ADDR_SHIFT 1 +#define GMBUS_SLAVE_READ (1<<0) +#define GMBUS_SLAVE_WRITE (0<<0) +#define GMBUS2 0x5108 /* status */ +#define GMBUS_INUSE (1<<15) +#define GMBUS_HW_WAIT_PHASE (1<<14) +#define GMBUS_STALL_TIMEOUT (1<<13) +#define GMBUS_INT (1<<12) +#define GMBUS_HW_RDY (1<<11) +#define GMBUS_SATOER (1<<10) +#define GMBUS_ACTIVE (1<<9) +#define GMBUS3 0x510c /* data buffer bytes 3-0 */ +#define GMBUS4 0x5110 /* interrupt mask (Pineview+) */ +#define GMBUS_SLAVE_TIMEOUT_EN (1<<4) +#define GMBUS_NAK_EN (1<<3) +#define GMBUS_IDLE_EN (1<<2) +#define GMBUS_HW_WAIT_EN (1<<1) +#define GMBUS_HW_RDY_EN (1<<0) +#define GMBUS5 0x5120 /* byte index */ +#define GMBUS_2BYTE_INDEX_EN (1<<31) /* * Clock control & power management diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c index ee0732b..60dc11b 100644 --- a/drivers/gpu/drm/i915/intel_crt.c +++ b/drivers/gpu/drm/i915/intel_crt.c @@ -453,6 +453,12 @@ static int intel_crt_get_modes(struct drm_connector *connector) struct i2c_adapter *ddc_bus; struct drm_device *dev = connector->dev; + /* Try GMBUS first */ + ret = intel_gmbus_get_modes(connector, 0); + if (ret) { + DRM_DEBUG_DRIVER("got EDID from GMBUS\n"); + goto end; + } ret = intel_ddc_get_modes(connector, intel_encoder->ddc_bus); if (ret || !IS_G4X(dev)) @@ -466,6 +472,7 @@ static int intel_crt_get_modes(struct drm_connector *connector) "DDC bus registration failed for CRTDDC_D.\n"); goto end; } + /* Try to get modes by GPIOD port */ ret = intel_ddc_get_modes(connector, ddc_bus); intel_i2c_destroy(ddc_bus); diff --git a/drivers/gpu/drm/i915/intel_modes.c b/drivers/gpu/drm/i915/intel_modes.c index 35d15f8..c9b946d 100644 --- a/drivers/gpu/drm/i915/intel_modes.c +++ b/drivers/gpu/drm/i915/intel_modes.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2007 Dave Airlie - * Copyright (c) 2007 Intel Corporation + * Copyright (c) 2007, 2010 Intel Corporation * Jesse Barnes * * Permission is hereby granted, free of charge, to any person obtaining a @@ -89,3 +89,165 @@ int intel_ddc_get_modes(struct drm_connector *connector, return ret; } + +static void intel_dump_gmbus(drm_i915_private_t *dev_priv) +{ + DRM_DEBUG_DRIVER("GMBUS0: 0x%08x\n", I915_READ(GMBUS0)); + DRM_DEBUG_DRIVER("GMBUS1: 0x%08x\n", I915_READ(GMBUS1)); + DRM_DEBUG_DRIVER("GMBUS2: 0x%08x\n", I915_READ(GMBUS2)); + DRM_DEBUG_DRIVER("GMBUS3: 0x%08x\n", I915_READ(GMBUS3)); + DRM_DEBUG_DRIVER("GMBUS4: 0x%08x\n", I915_READ(GMBUS4)); + DRM_DEBUG_DRIVER("GMBUS5: 0x%08x\n", I915_READ(GMBUS5)); +} + +static bool intel_gmbus_wait_hw(drm_i915_private_t *dev_priv) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(500); + u32 gmbus2; + bool ret = false; + + do { + gmbus2 = I915_READ(GMBUS2); + + if (time_after(jiffies, timeout) || + (gmbus2 & GMBUS_SATOER)) { + DRM_DEBUG_DRIVER("timeout waiting for GMBUS hw\n"); + intel_dump_gmbus(dev_priv); + ret = true; + break; + } + } while(!(gmbus2 & GMBUS_HW_RDY)); + + return ret; +} + +static bool intel_gmbus_wait_idle(drm_i915_private_t *dev_priv) +{ + unsigned long timeout = jiffies + msecs_to_jiffies(500); + bool ret = false; + + while (I915_READ(GMBUS2) & GMBUS_ACTIVE) { + if (time_after(jiffies, timeout)) { + DRM_DEBUG_DRIVER("timeout waiting for GMBUS idle\n"); + intel_dump_gmbus(dev_priv); + ret = true; + break; + } + } + + return ret; +} + +static void intel_gmbus_reset(drm_i915_private_t *dev_priv) +{ + I915_WRITE(GMBUS1, GMBUS_SW_CLR_INT); + POSTING_READ(GMBUS1); + I915_WRITE(GMBUS1, 0); + + if (intel_gmbus_wait_idle(dev_priv)) + DRM_DEBUG_DRIVER("warning: gmbus reset timed out\n"); +} + +struct intel_gmbus_edid_data { + struct drm_device *dev; + int pin; +}; + +static int intel_gmbus_edid_read(void *data, unsigned char *buf, int block, + int len) +{ + struct intel_gmbus_edid_data *edid_read_data = data; + drm_i915_private_t *dev_priv = edid_read_data->dev->dev_private; + u32 gmbus_port; + int pin = edid_read_data->pin, i; + unsigned char start = block * EDID_LENGTH; + + if (pin != 0) { + DRM_DEBUG_DRIVER("pin not supported\n"); + return -EINVAL; + } + + gmbus_port = GMBUS_PORT_VGADDC; + + intel_gmbus_reset(dev_priv); + + I915_WRITE(GMBUS0, GMBUS_RATE_100KHZ | gmbus_port); + + /* Write start address to DDC port */ + I915_WRITE(GMBUS3, start); + I915_WRITE(GMBUS1, GMBUS_CYCLE_NI_STOP | (1 << GMBUS_BYTE_COUNT_SHIFT) | + (DDC_ADDR << GMBUS_SLAVE_ADDR_SHIFT) | + GMBUS_SLAVE_WRITE); + I915_WRITE(GMBUS1, I915_READ(GMBUS1) | GMBUS_SW_RDY); + if (intel_gmbus_wait_hw(dev_priv)) { + DRM_DEBUG_DRIVER("GMBUS timeout waiting for hw ready\n"); + return -ETIMEDOUT; + } + + /* Start data transfer into buf */ + I915_WRITE(GMBUS1, GMBUS_CYCLE_NI_STOP | + (len << GMBUS_BYTE_COUNT_SHIFT) | + (DDC_ADDR << GMBUS_SLAVE_ADDR_SHIFT) | + GMBUS_SLAVE_READ); + I915_WRITE(GMBUS1, I915_READ(GMBUS1) | GMBUS_SW_RDY); + for (i = 0; i < len; i += 4) { + u32 data; + if (intel_gmbus_wait_hw(dev_priv)) + break; + data = I915_READ(GMBUS3); + buf[i] = data & 0xff; + if (i + 1 < len) + buf[i+1] = (data >> 8) & 0xff; + if (i + 2 < len) + buf[i+2] = (data >> 16) & 0xff; + if (i + 3 < len) + buf[i+3] = (data >> 24) & 0xff; + } + + /* Send STOP (ignore timeouts) and clear GMBUS0 */ + intel_gmbus_wait_hw(dev_priv); + I915_WRITE(GMBUS1, GMBUS_CYCLE_STOP | + (DDC_ADDR << GMBUS_SLAVE_ADDR_SHIFT)); + I915_WRITE(GMBUS1, I915_READ(GMBUS1) | GMBUS_SW_RDY); + intel_gmbus_wait_idle(dev_priv); + I915_WRITE(GMBUS0, 0); + + return 0; +} + +/** + * intel_gmbus_get_modes - get modes using DDC over GMBUS + * @connector: connector in question + * @pin: pin to use + * + * There are 8 pins available: + * 0 - VGA DAC DDC + * 1 - i2c data (SSC clock control) + * 2 - LVDS DDC + * 3 - DVOA DDC (reserved in 9xx+) + * 4 - i2c data (add2 control) + * 5 - DVOA data (reserved in 9xx+) + * 6 - thermal sensor (reserved in 9xx+) + * 7 - trusted output + * The above are the recommended pin connections, they may vary from + * platform to platform; the VBT should contain the actual mappings. + */ +int intel_gmbus_get_modes(struct drm_connector *connector, int pin) +{ + struct edid *edid; + struct intel_gmbus_edid_data data = { + .dev = connector->dev, + .pin = pin, + }; + int ret = 0; + + edid = drm_get_edid(connector, intel_gmbus_edid_read, &data); + if (edid) { + drm_mode_connector_update_edid_property(connector, edid); + ret = drm_add_edid_modes(connector, edid); + connector->display_info.raw_edid = NULL; + kfree(edid); + } + + return ret; +}