From b222f2673354c65e178cbcba610e7883a05f5bf3 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Sat, 11 Sep 2010 21:48:25 +0100 Subject: drm/i915/i2c: The bit-banging interface controls the delay, drop ours Remove our redundant udelay() as the timings are already handled by the i2c-algo-bit controller. Signed-off-by: Chris Wilson --- drivers/gpu/drm/i915/intel_i2c.c | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) (limited to 'drivers/gpu/drm/i915/intel_i2c.c') diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c index c2649c7df14c..de03989d6df3 100644 --- a/drivers/gpu/drm/i915/intel_i2c.c +++ b/drivers/gpu/drm/i915/intel_i2c.c @@ -38,16 +38,18 @@ void intel_i2c_quirk_set(struct drm_device *dev, bool enable) { struct drm_i915_private *dev_priv = dev->dev_private; + u32 val; /* When using bit bashing for I2C, this bit needs to be set to 1 */ if (!IS_PINEVIEW(dev)) return; + + val = I915_READ(DSPCLK_GATE_D); if (enable) - I915_WRITE(DSPCLK_GATE_D, - I915_READ(DSPCLK_GATE_D) | DPCUNIT_CLOCK_GATE_DISABLE); + val |= DPCUNIT_CLOCK_GATE_DISABLE; else - I915_WRITE(DSPCLK_GATE_D, - I915_READ(DSPCLK_GATE_D) & (~DPCUNIT_CLOCK_GATE_DISABLE)); + val &= ~DPCUNIT_CLOCK_GATE_DISABLE; + I915_WRITE(DSPCLK_GATE_D, val); } /* @@ -60,20 +62,14 @@ static int get_clock(void *data) { struct intel_i2c_chan *chan = data; struct drm_i915_private *dev_priv = chan->drm_dev->dev_private; - u32 val; - - val = I915_READ(chan->reg); - return ((val & GPIO_CLOCK_VAL_IN) != 0); + return (I915_READ(chan->reg) & GPIO_CLOCK_VAL_IN) != 0; } static int get_data(void *data) { struct intel_i2c_chan *chan = data; struct drm_i915_private *dev_priv = chan->drm_dev->dev_private; - u32 val; - - val = I915_READ(chan->reg); - return ((val & GPIO_DATA_VAL_IN) != 0); + return (I915_READ(chan->reg) & GPIO_DATA_VAL_IN) != 0; } static void set_clock(void *data, int state_high) @@ -94,7 +90,7 @@ static void set_clock(void *data, int state_high) clock_bits = GPIO_CLOCK_DIR_OUT | GPIO_CLOCK_DIR_MASK | GPIO_CLOCK_VAL_MASK; I915_WRITE(chan->reg, reserved | clock_bits); - udelay(I2C_RISEFALL_TIME); /* wait for the line to change state */ + POSTING_READ(chan->reg); } static void set_data(void *data, int state_high) @@ -116,7 +112,7 @@ static void set_data(void *data, int state_high) GPIO_DATA_VAL_MASK; I915_WRITE(chan->reg, reserved | data_bits); - udelay(I2C_RISEFALL_TIME); /* wait for the line to change state */ + POSTING_READ(chan->reg); } /* Clears the GMBUS setup. Our driver doesn't make use of the GMBUS I2C @@ -129,11 +125,10 @@ intel_i2c_reset_gmbus(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; - if (HAS_PCH_SPLIT(dev)) { + if (HAS_PCH_SPLIT(dev)) I915_WRITE(PCH_GMBUS0, 0); - } else { + else I915_WRITE(GMBUS0, 0); - } } /** @@ -177,7 +172,7 @@ struct i2c_adapter *intel_i2c_create(struct drm_device *dev, const u32 reg, chan->algo.setscl = set_clock; chan->algo.getsda = get_data; chan->algo.getscl = get_clock; - chan->algo.udelay = 20; + chan->algo.udelay = I2C_RISEFALL_TIME; chan->algo.timeout = usecs_to_jiffies(2200); chan->algo.data = chan; @@ -191,9 +186,10 @@ struct i2c_adapter *intel_i2c_create(struct drm_device *dev, const u32 reg, /* JJJ: raise SCL and SDA? */ intel_i2c_quirk_set(dev, true); set_data(chan, 1); + udelay(I2C_RISEFALL_TIME); set_clock(chan, 1); + udelay(I2C_RISEFALL_TIME); intel_i2c_quirk_set(dev, false); - udelay(20); return &chan->adapter; -- cgit v1.2.3 From 890f3359f7b84d7015104360d647ccac5f515542 Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 14 Sep 2010 16:46:59 +0100 Subject: drm/i915/i2c: Track the parent encoder rather than just the dev The SDVO proxy i2c adapter wants to be able to use information stored in the encoder, so pass that through intel_i2c rather than iterate over all known encoders every time. Signed-off-by: Chris Wilson --- drivers/gpu/drm/i915/intel_i2c.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) (limited to 'drivers/gpu/drm/i915/intel_i2c.c') diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c index de03989d6df3..d3d65a9cfba1 100644 --- a/drivers/gpu/drm/i915/intel_i2c.c +++ b/drivers/gpu/drm/i915/intel_i2c.c @@ -58,25 +58,31 @@ void intel_i2c_quirk_set(struct drm_device *dev, bool enable) #define I2C_RISEFALL_TIME 20 +static inline struct drm_i915_private * +get_dev_priv(struct intel_i2c_chan *chan) +{ + return chan->encoder->base.dev->dev_private; +} + static int get_clock(void *data) { struct intel_i2c_chan *chan = data; - struct drm_i915_private *dev_priv = chan->drm_dev->dev_private; + struct drm_i915_private *dev_priv = get_dev_priv(chan); return (I915_READ(chan->reg) & GPIO_CLOCK_VAL_IN) != 0; } static int get_data(void *data) { struct intel_i2c_chan *chan = data; - struct drm_i915_private *dev_priv = chan->drm_dev->dev_private; + struct drm_i915_private *dev_priv = get_dev_priv(chan); return (I915_READ(chan->reg) & GPIO_DATA_VAL_IN) != 0; } static void set_clock(void *data, int state_high) { struct intel_i2c_chan *chan = data; - struct drm_device *dev = chan->drm_dev; - struct drm_i915_private *dev_priv = chan->drm_dev->dev_private; + struct drm_i915_private *dev_priv = get_dev_priv(chan); + struct drm_device *dev = dev_priv->dev; u32 reserved = 0, clock_bits; /* On most chips, these bits must be preserved in software. */ @@ -96,8 +102,8 @@ static void set_clock(void *data, int state_high) static void set_data(void *data, int state_high) { struct intel_i2c_chan *chan = data; - struct drm_device *dev = chan->drm_dev; - struct drm_i915_private *dev_priv = chan->drm_dev->dev_private; + struct drm_i915_private *dev_priv = get_dev_priv(chan); + struct drm_device *dev = dev_priv->dev; u32 reserved = 0, data_bits; /* On most chips, these bits must be preserved in software. */ @@ -153,16 +159,18 @@ intel_i2c_reset_gmbus(struct drm_device *dev) * %GPIOH * see PRM for details on how these different busses are used. */ -struct i2c_adapter *intel_i2c_create(struct drm_device *dev, const u32 reg, +struct i2c_adapter *intel_i2c_create(struct intel_encoder *encoder, + const u32 reg, const char *name) { struct intel_i2c_chan *chan; + struct drm_device *dev = encoder->base.dev; chan = kzalloc(sizeof(struct intel_i2c_chan), GFP_KERNEL); if (!chan) goto out_free; - chan->drm_dev = dev; + chan->encoder = encoder; chan->reg = reg; snprintf(chan->adapter.name, I2C_NAME_SIZE, "intel drm %s", name); chan->adapter.owner = THIS_MODULE; @@ -178,7 +186,7 @@ struct i2c_adapter *intel_i2c_create(struct drm_device *dev, const u32 reg, i2c_set_adapdata(&chan->adapter, chan); - if(i2c_bit_add_bus(&chan->adapter)) + if (i2c_bit_add_bus(&chan->adapter)) goto out_free; intel_i2c_reset_gmbus(dev); -- cgit v1.2.3 From f899fc64cda8569d0529452aafc0da31c042df2e Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 20 Jul 2010 15:44:45 -0700 Subject: drm/i915: use GMBUS to manage i2c links Use the GMBUS interface rather than direct bit banging to grab the EDID over DDC (and for other forms of auxiliary communication with external display controllers). The hope is that this method will be much faster and more reliable than bit banging for fetching EDIDs from buggy monitors or through switches, though we still preserve the bit banging as a fallback in case GMBUS fails. Based on an original patch by Jesse Barnes. Cc: Jesse Barnes Signed-off-by: Chris Wilson --- drivers/gpu/drm/i915/intel_i2c.c | 381 +++++++++++++++++++++++++++------------ 1 file changed, 268 insertions(+), 113 deletions(-) (limited to 'drivers/gpu/drm/i915/intel_i2c.c') diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c index d3d65a9cfba1..6f4d128935ac 100644 --- a/drivers/gpu/drm/i915/intel_i2c.c +++ b/drivers/gpu/drm/i915/intel_i2c.c @@ -1,6 +1,6 @@ /* * Copyright (c) 2006 Dave Airlie - * Copyright © 2006-2008 Intel Corporation + * Copyright © 2006-2008,2010 Intel Corporation * Jesse Barnes * * Permission is hereby granted, free of charge, to any person obtaining a @@ -24,10 +24,9 @@ * * Authors: * Eric Anholt + * Chris Wilson */ #include -#include -#include #include #include "drmP.h" #include "drm.h" @@ -35,13 +34,33 @@ #include "i915_drm.h" #include "i915_drv.h" -void intel_i2c_quirk_set(struct drm_device *dev, bool enable) +/* Intel GPIO access functions */ + +#define I2C_RISEFALL_TIME 20 + +struct intel_gpio { + struct i2c_adapter adapter; + struct i2c_algo_bit_data algo; + struct drm_i915_private *dev_priv; + u32 reg; +}; + +void +intel_i2c_reset(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; + if (HAS_PCH_SPLIT(dev)) + I915_WRITE(PCH_GMBUS0, 0); + else + I915_WRITE(GMBUS0, 0); +} + +static void intel_i2c_quirk_set(struct drm_i915_private *dev_priv, bool enable) +{ u32 val; /* When using bit bashing for I2C, this bit needs to be set to 1 */ - if (!IS_PINEVIEW(dev)) + if (!IS_PINEVIEW(dev_priv->dev)) return; val = I915_READ(DSPCLK_GATE_D); @@ -52,42 +71,30 @@ void intel_i2c_quirk_set(struct drm_device *dev, bool enable) I915_WRITE(DSPCLK_GATE_D, val); } -/* - * Intel GPIO access functions - */ - -#define I2C_RISEFALL_TIME 20 - -static inline struct drm_i915_private * -get_dev_priv(struct intel_i2c_chan *chan) -{ - return chan->encoder->base.dev->dev_private; -} - static int get_clock(void *data) { - struct intel_i2c_chan *chan = data; - struct drm_i915_private *dev_priv = get_dev_priv(chan); - return (I915_READ(chan->reg) & GPIO_CLOCK_VAL_IN) != 0; + struct intel_gpio *gpio = data; + struct drm_i915_private *dev_priv = gpio->dev_priv; + return (I915_READ(gpio->reg) & GPIO_CLOCK_VAL_IN) != 0; } static int get_data(void *data) { - struct intel_i2c_chan *chan = data; - struct drm_i915_private *dev_priv = get_dev_priv(chan); - return (I915_READ(chan->reg) & GPIO_DATA_VAL_IN) != 0; + struct intel_gpio *gpio = data; + struct drm_i915_private *dev_priv = gpio->dev_priv; + return (I915_READ(gpio->reg) & GPIO_DATA_VAL_IN) != 0; } static void set_clock(void *data, int state_high) { - struct intel_i2c_chan *chan = data; - struct drm_i915_private *dev_priv = get_dev_priv(chan); + struct intel_gpio *gpio = data; + struct drm_i915_private *dev_priv = gpio->dev_priv; struct drm_device *dev = dev_priv->dev; u32 reserved = 0, clock_bits; /* On most chips, these bits must be preserved in software. */ if (!IS_I830(dev) && !IS_845G(dev)) - reserved = I915_READ(chan->reg) & (GPIO_DATA_PULLUP_DISABLE | + reserved = I915_READ(gpio->reg) & (GPIO_DATA_PULLUP_DISABLE | GPIO_CLOCK_PULLUP_DISABLE); if (state_high) @@ -95,20 +102,21 @@ static void set_clock(void *data, int state_high) else clock_bits = GPIO_CLOCK_DIR_OUT | GPIO_CLOCK_DIR_MASK | GPIO_CLOCK_VAL_MASK; - I915_WRITE(chan->reg, reserved | clock_bits); - POSTING_READ(chan->reg); + + I915_WRITE(gpio->reg, reserved | clock_bits); + POSTING_READ(gpio->reg); } static void set_data(void *data, int state_high) { - struct intel_i2c_chan *chan = data; - struct drm_i915_private *dev_priv = get_dev_priv(chan); + struct intel_gpio *gpio = data; + struct drm_i915_private *dev_priv = gpio->dev_priv; struct drm_device *dev = dev_priv->dev; u32 reserved = 0, data_bits; /* On most chips, these bits must be preserved in software. */ if (!IS_I830(dev) && !IS_845G(dev)) - reserved = I915_READ(chan->reg) & (GPIO_DATA_PULLUP_DISABLE | + reserved = I915_READ(gpio->reg) & (GPIO_DATA_PULLUP_DISABLE | GPIO_CLOCK_PULLUP_DISABLE); if (state_high) @@ -117,111 +125,258 @@ static void set_data(void *data, int state_high) data_bits = GPIO_DATA_DIR_OUT | GPIO_DATA_DIR_MASK | GPIO_DATA_VAL_MASK; - I915_WRITE(chan->reg, reserved | data_bits); - POSTING_READ(chan->reg); + I915_WRITE(gpio->reg, reserved | data_bits); + POSTING_READ(gpio->reg); } -/* Clears the GMBUS setup. Our driver doesn't make use of the GMBUS I2C - * engine, but if the BIOS leaves it enabled, then that can break our use - * of the bit-banging I2C interfaces. This is notably the case with the - * Mac Mini in EFI mode. - */ -void -intel_i2c_reset_gmbus(struct drm_device *dev) +static struct i2c_adapter * +intel_gpio_create(struct drm_i915_private *dev_priv, u32 pin) { - struct drm_i915_private *dev_priv = dev->dev_private; + static const int map_pin_to_reg[] = { + 0, + GPIOB, + GPIOA, + GPIOC, + GPIOD, + GPIOE, + GPIOF, + }; + struct intel_gpio *gpio; - if (HAS_PCH_SPLIT(dev)) - I915_WRITE(PCH_GMBUS0, 0); - else - I915_WRITE(GMBUS0, 0); -} + if (pin < 1 || pin > 7) + return NULL; -/** - * intel_i2c_create - instantiate an Intel i2c bus using the specified GPIO reg - * @dev: DRM device - * @output: driver specific output device - * @reg: GPIO reg to use - * @name: name for this bus - * @slave_addr: slave address (if fixed) - * - * Creates and registers a new i2c bus with the Linux i2c layer, for use - * in output probing and control (e.g. DDC or SDVO control functions). - * - * Possible values for @reg include: - * %GPIOA - * %GPIOB - * %GPIOC - * %GPIOD - * %GPIOE - * %GPIOF - * %GPIOG - * %GPIOH - * see PRM for details on how these different busses are used. - */ -struct i2c_adapter *intel_i2c_create(struct intel_encoder *encoder, - const u32 reg, - const char *name) -{ - struct intel_i2c_chan *chan; - struct drm_device *dev = encoder->base.dev; + gpio = kzalloc(sizeof(struct intel_gpio), GFP_KERNEL); + if (gpio == NULL) + return NULL; - chan = kzalloc(sizeof(struct intel_i2c_chan), GFP_KERNEL); - if (!chan) - goto out_free; + gpio->reg = map_pin_to_reg[pin]; + if (HAS_PCH_SPLIT(dev_priv->dev)) + gpio->reg += PCH_GPIOA - GPIOA; + gpio->dev_priv = dev_priv; - chan->encoder = encoder; - chan->reg = reg; - snprintf(chan->adapter.name, I2C_NAME_SIZE, "intel drm %s", name); - chan->adapter.owner = THIS_MODULE; - chan->adapter.algo_data = &chan->algo; - chan->adapter.dev.parent = &dev->pdev->dev; - chan->algo.setsda = set_data; - chan->algo.setscl = set_clock; - chan->algo.getsda = get_data; - chan->algo.getscl = get_clock; - chan->algo.udelay = I2C_RISEFALL_TIME; - chan->algo.timeout = usecs_to_jiffies(2200); - chan->algo.data = chan; - - i2c_set_adapdata(&chan->adapter, chan); - - if (i2c_bit_add_bus(&chan->adapter)) + snprintf(gpio->adapter.name, I2C_NAME_SIZE, "GPIO %d", pin); + gpio->adapter.owner = THIS_MODULE; + gpio->adapter.algo_data = &gpio->algo; + gpio->adapter.dev.parent = &dev_priv->dev->pdev->dev; + gpio->algo.setsda = set_data; + gpio->algo.setscl = set_clock; + gpio->algo.getsda = get_data; + gpio->algo.getscl = get_clock; + gpio->algo.udelay = I2C_RISEFALL_TIME; + gpio->algo.timeout = usecs_to_jiffies(2200); + gpio->algo.data = gpio; + + if (i2c_bit_add_bus(&gpio->adapter)) goto out_free; - intel_i2c_reset_gmbus(dev); + intel_i2c_reset(dev_priv->dev); /* JJJ: raise SCL and SDA? */ - intel_i2c_quirk_set(dev, true); - set_data(chan, 1); + intel_i2c_quirk_set(dev_priv, true); + set_data(gpio, 1); udelay(I2C_RISEFALL_TIME); - set_clock(chan, 1); + set_clock(gpio, 1); udelay(I2C_RISEFALL_TIME); - intel_i2c_quirk_set(dev, false); + intel_i2c_quirk_set(dev_priv, false); - return &chan->adapter; + return &gpio->adapter; out_free: - kfree(chan); + kfree(gpio); return NULL; } +static int +quirk_i2c_transfer(struct drm_i915_private *dev_priv, + struct i2c_adapter *adapter, + struct i2c_msg *msgs, + int num) +{ + int ret; + + intel_i2c_reset(dev_priv->dev); + + intel_i2c_quirk_set(dev_priv, true); + ret = i2c_transfer(adapter, msgs, num); + intel_i2c_quirk_set(dev_priv, false); + + return ret; +} + +static int +gmbus_xfer(struct i2c_adapter *adapter, + struct i2c_msg *msgs, + int num) +{ + struct intel_gmbus *bus = container_of(adapter, + struct intel_gmbus, + adapter); + struct drm_i915_private *dev_priv = adapter->algo_data; + int i, speed, reg_offset; + + if (bus->force_bitbanging) + return quirk_i2c_transfer(dev_priv, bus->force_bitbanging, msgs, num); + + reg_offset = HAS_PCH_SPLIT(dev_priv->dev) ? PCH_GMBUS0 - GMBUS0 : 0; + + speed = GMBUS_RATE_100KHZ; + if (INTEL_INFO(dev_priv->dev)->gen > 4 || IS_G4X(dev_priv->dev)) { + if (bus->pin == GMBUS_PORT_DPB) /* SDVO only? */ + speed = GMBUS_RATE_1MHZ; + else + speed = GMBUS_RATE_400KHZ; + } + I915_WRITE(GMBUS0 + reg_offset, speed | bus->pin); + + for (i = 0; i < num; i++) { + u16 len = msgs[i].len; + u8 *buf = msgs[i].buf; + + if (msgs[i].flags & I2C_M_RD) { + I915_WRITE(GMBUS1 + reg_offset, + GMBUS_CYCLE_WAIT | (i + 1 == num ? GMBUS_CYCLE_STOP : 0) | + (len << GMBUS_BYTE_COUNT_SHIFT) | + (msgs[i].addr << GMBUS_SLAVE_ADDR_SHIFT) | + GMBUS_SLAVE_READ | GMBUS_SW_RDY); + do { + u32 val, loop = 0; + + if (wait_for(I915_READ(GMBUS2 + reg_offset) & (GMBUS_SATOER | GMBUS_HW_RDY), 50)) + goto timeout; + if (I915_READ(GMBUS2 + reg_offset) & GMBUS_SATOER) + return 0; + + val = I915_READ(GMBUS3 + reg_offset); + do { + *buf++ = val & 0xff; + val >>= 8; + } while (--len && ++loop < 4); + } while (len); + } else { + u32 val = 0, loop = 0; + + BUG_ON(msgs[i].len > 4); + + do { + val |= *buf++ << (loop*8); + } while (--len && +loop < 4); + + I915_WRITE(GMBUS3 + reg_offset, val); + I915_WRITE(GMBUS1 + reg_offset, + (i + 1 == num ? GMBUS_CYCLE_STOP : GMBUS_CYCLE_WAIT ) | + (msgs[i].len << GMBUS_BYTE_COUNT_SHIFT) | + (msgs[i].addr << GMBUS_SLAVE_ADDR_SHIFT) | + GMBUS_SLAVE_WRITE | GMBUS_SW_RDY); + } + + if (i + 1 < num && wait_for(I915_READ(GMBUS2 + reg_offset) & (GMBUS_SATOER | GMBUS_HW_WAIT_PHASE), 50)) + goto timeout; + if (I915_READ(GMBUS2 + reg_offset) & GMBUS_SATOER) + return 0; + } + + return num; + +timeout: + DRM_INFO("GMBUS timed out, falling back to bit banging on pin %d\n", bus->pin); + /* Hardware may not support GMBUS over these pins? Try GPIO bitbanging instead. */ + bus->force_bitbanging = intel_gpio_create(dev_priv, bus->pin); + if (!bus->force_bitbanging) + return -ENOMEM; + + return quirk_i2c_transfer(dev_priv, bus->force_bitbanging, msgs, num); +} + +static u32 gmbus_func(struct i2c_adapter *adapter) +{ + return (I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | + /* I2C_FUNC_10BIT_ADDR | */ + I2C_FUNC_SMBUS_READ_BLOCK_DATA | + I2C_FUNC_SMBUS_BLOCK_PROC_CALL); +} + +static const struct i2c_algorithm gmbus_algorithm = { + .master_xfer = gmbus_xfer, + .functionality = gmbus_func +}; + /** - * intel_i2c_destroy - unregister and free i2c bus resources - * @output: channel to free - * - * Unregister the adapter from the i2c layer, then free the structure. + * intel_gmbus_setup - instantiate all Intel i2c GMBuses + * @dev: DRM device */ -void intel_i2c_destroy(struct i2c_adapter *adapter) +int intel_setup_gmbus(struct drm_device *dev) +{ + static const char *names[] = { + "disabled", + "ssc", + "vga", + "panel", + "dpc", + "dpb", + "dpd", + "reserved" + }; + struct drm_i915_private *dev_priv = dev->dev_private; + int ret, i; + + dev_priv->gmbus = kcalloc(sizeof(struct intel_gmbus), GMBUS_NUM_PORTS, + GFP_KERNEL); + if (dev_priv->gmbus == NULL) + return -ENOMEM; + + for (i = 0; i < GMBUS_NUM_PORTS; i++) { + struct intel_gmbus *bus = &dev_priv->gmbus[i]; + + bus->adapter.owner = THIS_MODULE; + bus->adapter.class = I2C_CLASS_DDC; + snprintf(bus->adapter.name, + I2C_NAME_SIZE, + "gmbus %s", + names[i]); + + bus->adapter.dev.parent = &dev->pdev->dev; + bus->adapter.algo_data = dev_priv; + + bus->adapter.algo = &gmbus_algorithm; + ret = i2c_add_adapter(&bus->adapter); + if (ret) + goto err; + + bus->pin = i; + } + + intel_i2c_reset(dev_priv->dev); + + return 0; + +err: + while (--i) { + struct intel_gmbus *bus = &dev_priv->gmbus[i]; + i2c_del_adapter(&bus->adapter); + } + kfree(dev_priv->gmbus); + dev_priv->gmbus = NULL; + return ret; +} + +void intel_teardown_gmbus(struct drm_device *dev) { - struct intel_i2c_chan *chan; + struct drm_i915_private *dev_priv = dev->dev_private; + int i; - if (!adapter) + if (dev_priv->gmbus == NULL) return; - chan = container_of(adapter, - struct intel_i2c_chan, - adapter); - i2c_del_adapter(&chan->adapter); - kfree(chan); + for (i = 0; i < GMBUS_NUM_PORTS; i++) { + struct intel_gmbus *bus = &dev_priv->gmbus[i]; + if (bus->force_bitbanging) { + i2c_del_adapter(bus->force_bitbanging); + kfree(bus->force_bitbanging); + } + i2c_del_adapter(&bus->adapter); + } + + kfree(dev_priv->gmbus); + dev_priv->gmbus = NULL; } -- cgit v1.2.3 From e957d7720a2797b31231616014b68f4f6203145e Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Fri, 24 Sep 2010 12:52:03 +0100 Subject: drm/i915/sdvo: Fix GMBUSification Besides a couple of bugs when writing more than a single byte along the GMBUS, SDVO was completely failing whilst trying to use GMBUS, so use bit banging instead. Signed-off-by: Chris Wilson --- drivers/gpu/drm/i915/intel_i2c.c | 181 +++++++++++++++++++++++++++------------ 1 file changed, 124 insertions(+), 57 deletions(-) (limited to 'drivers/gpu/drm/i915/intel_i2c.c') diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c index 6f4d128935ac..91920247d4ff 100644 --- a/drivers/gpu/drm/i915/intel_i2c.c +++ b/drivers/gpu/drm/i915/intel_i2c.c @@ -38,6 +38,12 @@ #define I2C_RISEFALL_TIME 20 +static inline struct intel_gmbus * +to_intel_gmbus(struct i2c_adapter *i2c) +{ + return container_of(i2c, struct intel_gmbus, adapter); +} + struct intel_gpio { struct i2c_adapter adapter; struct i2c_algo_bit_data algo; @@ -71,10 +77,27 @@ static void intel_i2c_quirk_set(struct drm_i915_private *dev_priv, bool enable) I915_WRITE(DSPCLK_GATE_D, val); } +static u32 get_reserved(struct intel_gpio *gpio) +{ + struct drm_i915_private *dev_priv = gpio->dev_priv; + struct drm_device *dev = dev_priv->dev; + u32 reserved = 0; + + /* On most chips, these bits must be preserved in software. */ + if (!IS_I830(dev) && !IS_845G(dev)) + reserved = I915_READ(gpio->reg) & (GPIO_DATA_PULLUP_DISABLE | + GPIO_CLOCK_PULLUP_DISABLE); + + return reserved; +} + static int get_clock(void *data) { struct intel_gpio *gpio = data; struct drm_i915_private *dev_priv = gpio->dev_priv; + u32 reserved = get_reserved(gpio); + I915_WRITE(gpio->reg, reserved | GPIO_CLOCK_DIR_MASK); + I915_WRITE(gpio->reg, reserved); return (I915_READ(gpio->reg) & GPIO_CLOCK_VAL_IN) != 0; } @@ -82,6 +105,9 @@ static int get_data(void *data) { struct intel_gpio *gpio = data; struct drm_i915_private *dev_priv = gpio->dev_priv; + u32 reserved = get_reserved(gpio); + I915_WRITE(gpio->reg, reserved | GPIO_DATA_DIR_MASK); + I915_WRITE(gpio->reg, reserved); return (I915_READ(gpio->reg) & GPIO_DATA_VAL_IN) != 0; } @@ -89,13 +115,8 @@ static void set_clock(void *data, int state_high) { struct intel_gpio *gpio = data; struct drm_i915_private *dev_priv = gpio->dev_priv; - struct drm_device *dev = dev_priv->dev; - u32 reserved = 0, clock_bits; - - /* On most chips, these bits must be preserved in software. */ - if (!IS_I830(dev) && !IS_845G(dev)) - reserved = I915_READ(gpio->reg) & (GPIO_DATA_PULLUP_DISABLE | - GPIO_CLOCK_PULLUP_DISABLE); + u32 reserved = get_reserved(gpio); + u32 clock_bits; if (state_high) clock_bits = GPIO_CLOCK_DIR_IN | GPIO_CLOCK_DIR_MASK; @@ -111,13 +132,8 @@ static void set_data(void *data, int state_high) { struct intel_gpio *gpio = data; struct drm_i915_private *dev_priv = gpio->dev_priv; - struct drm_device *dev = dev_priv->dev; - u32 reserved = 0, data_bits; - - /* On most chips, these bits must be preserved in software. */ - if (!IS_I830(dev) && !IS_845G(dev)) - reserved = I915_READ(gpio->reg) & (GPIO_DATA_PULLUP_DISABLE | - GPIO_CLOCK_PULLUP_DISABLE); + u32 reserved = get_reserved(gpio); + u32 data_bits; if (state_high) data_bits = GPIO_DATA_DIR_IN | GPIO_DATA_DIR_MASK; @@ -155,7 +171,7 @@ intel_gpio_create(struct drm_i915_private *dev_priv, u32 pin) gpio->reg += PCH_GPIOA - GPIOA; gpio->dev_priv = dev_priv; - snprintf(gpio->adapter.name, I2C_NAME_SIZE, "GPIO %d", pin); + snprintf(gpio->adapter.name, I2C_NAME_SIZE, "GPIO%c", "?BACDEF?"[pin]); gpio->adapter.owner = THIS_MODULE; gpio->adapter.algo_data = &gpio->algo; gpio->adapter.dev.parent = &dev_priv->dev->pdev->dev; @@ -170,16 +186,6 @@ intel_gpio_create(struct drm_i915_private *dev_priv, u32 pin) if (i2c_bit_add_bus(&gpio->adapter)) goto out_free; - intel_i2c_reset(dev_priv->dev); - - /* JJJ: raise SCL and SDA? */ - intel_i2c_quirk_set(dev_priv, true); - set_data(gpio, 1); - udelay(I2C_RISEFALL_TIME); - set_clock(gpio, 1); - udelay(I2C_RISEFALL_TIME); - intel_i2c_quirk_set(dev_priv, false); - return &gpio->adapter; out_free: @@ -188,17 +194,27 @@ out_free: } static int -quirk_i2c_transfer(struct drm_i915_private *dev_priv, - struct i2c_adapter *adapter, - struct i2c_msg *msgs, - int num) +intel_i2c_quirk_xfer(struct drm_i915_private *dev_priv, + struct i2c_adapter *adapter, + struct i2c_msg *msgs, + int num) { + struct intel_gpio *gpio = container_of(adapter, + struct intel_gpio, + adapter); int ret; intel_i2c_reset(dev_priv->dev); intel_i2c_quirk_set(dev_priv, true); - ret = i2c_transfer(adapter, msgs, num); + set_data(gpio, 1); + set_clock(gpio, 1); + udelay(I2C_RISEFALL_TIME); + + ret = adapter->algo->master_xfer(adapter, msgs, num); + + set_data(gpio, 1); + set_clock(gpio, 1); intel_i2c_quirk_set(dev_priv, false); return ret; @@ -213,21 +229,15 @@ gmbus_xfer(struct i2c_adapter *adapter, struct intel_gmbus, adapter); struct drm_i915_private *dev_priv = adapter->algo_data; - int i, speed, reg_offset; + int i, reg_offset; - if (bus->force_bitbanging) - return quirk_i2c_transfer(dev_priv, bus->force_bitbanging, msgs, num); + if (bus->force_bit) + return intel_i2c_quirk_xfer(dev_priv, + bus->force_bit, msgs, num); reg_offset = HAS_PCH_SPLIT(dev_priv->dev) ? PCH_GMBUS0 - GMBUS0 : 0; - speed = GMBUS_RATE_100KHZ; - if (INTEL_INFO(dev_priv->dev)->gen > 4 || IS_G4X(dev_priv->dev)) { - if (bus->pin == GMBUS_PORT_DPB) /* SDVO only? */ - speed = GMBUS_RATE_1MHZ; - else - speed = GMBUS_RATE_400KHZ; - } - I915_WRITE(GMBUS0 + reg_offset, speed | bus->pin); + I915_WRITE(GMBUS0 + reg_offset, bus->reg0); for (i = 0; i < num; i++) { u16 len = msgs[i].len; @@ -239,6 +249,7 @@ gmbus_xfer(struct i2c_adapter *adapter, (len << GMBUS_BYTE_COUNT_SHIFT) | (msgs[i].addr << GMBUS_SLAVE_ADDR_SHIFT) | GMBUS_SLAVE_READ | GMBUS_SW_RDY); + POSTING_READ(GMBUS2+reg_offset); do { u32 val, loop = 0; @@ -254,20 +265,35 @@ gmbus_xfer(struct i2c_adapter *adapter, } while (--len && ++loop < 4); } while (len); } else { - u32 val = 0, loop = 0; - - BUG_ON(msgs[i].len > 4); + u32 val, loop; + val = loop = 0; do { - val |= *buf++ << (loop*8); - } while (--len && +loop < 4); + val |= *buf++ << (8 * loop); + } while (--len && ++loop < 4); I915_WRITE(GMBUS3 + reg_offset, val); I915_WRITE(GMBUS1 + reg_offset, - (i + 1 == num ? GMBUS_CYCLE_STOP : GMBUS_CYCLE_WAIT ) | + (i + 1 == num ? GMBUS_CYCLE_STOP : GMBUS_CYCLE_WAIT) | (msgs[i].len << GMBUS_BYTE_COUNT_SHIFT) | (msgs[i].addr << GMBUS_SLAVE_ADDR_SHIFT) | GMBUS_SLAVE_WRITE | GMBUS_SW_RDY); + POSTING_READ(GMBUS2+reg_offset); + + while (len) { + if (wait_for(I915_READ(GMBUS2 + reg_offset) & (GMBUS_SATOER | GMBUS_HW_RDY), 50)) + goto timeout; + if (I915_READ(GMBUS2 + reg_offset) & GMBUS_SATOER) + return 0; + + val = loop = 0; + do { + val |= *buf++ << (8 * loop); + } while (--len && ++loop < 4); + + I915_WRITE(GMBUS3 + reg_offset, val); + POSTING_READ(GMBUS2+reg_offset); + } } if (i + 1 < num && wait_for(I915_READ(GMBUS2 + reg_offset) & (GMBUS_SATOER | GMBUS_HW_WAIT_PHASE), 50)) @@ -279,17 +305,25 @@ gmbus_xfer(struct i2c_adapter *adapter, return num; timeout: - DRM_INFO("GMBUS timed out, falling back to bit banging on pin %d\n", bus->pin); + DRM_INFO("GMBUS timed out, falling back to bit banging on pin %d [%s]\n", + bus->reg0 & 0xff, bus->adapter.name); /* Hardware may not support GMBUS over these pins? Try GPIO bitbanging instead. */ - bus->force_bitbanging = intel_gpio_create(dev_priv, bus->pin); - if (!bus->force_bitbanging) + bus->force_bit = intel_gpio_create(dev_priv, bus->reg0 & 0xff); + if (!bus->force_bit) return -ENOMEM; - return quirk_i2c_transfer(dev_priv, bus->force_bitbanging, msgs, num); + return intel_i2c_quirk_xfer(dev_priv, bus->force_bit, msgs, num); } static u32 gmbus_func(struct i2c_adapter *adapter) { + struct intel_gmbus *bus = container_of(adapter, + struct intel_gmbus, + adapter); + + if (bus->force_bit) + bus->force_bit->algo->functionality(bus->force_bit); + return (I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | /* I2C_FUNC_10BIT_ADDR | */ I2C_FUNC_SMBUS_READ_BLOCK_DATA | @@ -307,15 +341,15 @@ static const struct i2c_algorithm gmbus_algorithm = { */ int intel_setup_gmbus(struct drm_device *dev) { - static const char *names[] = { + static const char *names[GMBUS_NUM_PORTS] = { "disabled", "ssc", "vga", "panel", "dpc", "dpb", - "dpd", "reserved" + "dpd", }; struct drm_i915_private *dev_priv = dev->dev_private; int ret, i; @@ -343,7 +377,8 @@ int intel_setup_gmbus(struct drm_device *dev) if (ret) goto err; - bus->pin = i; + /* By default use a conservative clock rate */ + bus->reg0 = i | GMBUS_RATE_100KHZ; } intel_i2c_reset(dev_priv->dev); @@ -360,6 +395,38 @@ err: return ret; } +void intel_gmbus_set_speed(struct i2c_adapter *adapter, int speed) +{ + struct intel_gmbus *bus = to_intel_gmbus(adapter); + + /* speed: + * 0x0 = 100 KHz + * 0x1 = 50 KHz + * 0x2 = 400 KHz + * 0x3 = 1000 Khz + */ + bus->reg0 = (bus->reg0 & ~(0x3 << 8)) | (speed << 8); +} + +void intel_gmbus_force_bit(struct i2c_adapter *adapter, bool force_bit) +{ + struct intel_gmbus *bus = to_intel_gmbus(adapter); + + if (force_bit) { + if (bus->force_bit == NULL) { + struct drm_i915_private *dev_priv = adapter->algo_data; + bus->force_bit = intel_gpio_create(dev_priv, + bus->reg0 & 0xff); + } + } else { + if (bus->force_bit) { + i2c_del_adapter(bus->force_bit); + kfree(bus->force_bit); + bus->force_bit = NULL; + } + } +} + void intel_teardown_gmbus(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; @@ -370,9 +437,9 @@ void intel_teardown_gmbus(struct drm_device *dev) for (i = 0; i < GMBUS_NUM_PORTS; i++) { struct intel_gmbus *bus = &dev_priv->gmbus[i]; - if (bus->force_bitbanging) { - i2c_del_adapter(bus->force_bitbanging); - kfree(bus->force_bitbanging); + if (bus->force_bit) { + i2c_del_adapter(bus->force_bit); + kfree(bus->force_bit); } i2c_del_adapter(&bus->adapter); } -- cgit v1.2.3 From cb8ea7527b813dd6e19fb07328f7867a5f0a8d0a Mon Sep 17 00:00:00 2001 From: Chris Wilson Date: Tue, 28 Sep 2010 13:35:47 +0100 Subject: drm/i915: Use i2c bit banging instead of GMBUS There are several reported instances of GMBUS failing to successfully read the EDID, so revert back to bit banging until the issue is resolved. Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=30371 Signed-off-by: Chris Wilson --- drivers/gpu/drm/i915/intel_i2c.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/gpu/drm/i915/intel_i2c.c') diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c index 91920247d4ff..2449a74d4d80 100644 --- a/drivers/gpu/drm/i915/intel_i2c.c +++ b/drivers/gpu/drm/i915/intel_i2c.c @@ -379,6 +379,9 @@ int intel_setup_gmbus(struct drm_device *dev) /* By default use a conservative clock rate */ bus->reg0 = i | GMBUS_RATE_100KHZ; + + /* XXX force bit banging until GMBUS is fully debugged */ + bus->force_bit = intel_gpio_create(dev_priv, i); } intel_i2c_reset(dev_priv->dev); -- cgit v1.2.3 From 7b5337ddbaf7e4b71ef6fd6307c6f9ef84f636e9 Mon Sep 17 00:00:00 2001 From: Zhenyu Wang Date: Wed, 13 Oct 2010 16:40:12 +0800 Subject: drm/i915: Fix GPIO pin to register mapping In i2c GPIO fallback, index 6 is reserved for nothing. Signed-off-by: Zhenyu Wang Signed-off-by: Chris Wilson --- drivers/gpu/drm/i915/intel_i2c.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/gpu/drm/i915/intel_i2c.c') diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c index 2449a74d4d80..2be4f728ed0c 100644 --- a/drivers/gpu/drm/i915/intel_i2c.c +++ b/drivers/gpu/drm/i915/intel_i2c.c @@ -155,6 +155,7 @@ intel_gpio_create(struct drm_i915_private *dev_priv, u32 pin) GPIOC, GPIOD, GPIOE, + 0, GPIOF, }; struct intel_gpio *gpio; -- cgit v1.2.3