Skip to content

Commit

Permalink
drm/i915: use GMBUS to manage i2c links
Browse files Browse the repository at this point in the history
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 <[email protected]>
Signed-off-by: Chris Wilson <[email protected]>
  • Loading branch information
ickle committed Sep 18, 2010
1 parent 373a3cf commit f899fc6
Show file tree
Hide file tree
Showing 21 changed files with 441 additions and 419 deletions.
1 change: 0 additions & 1 deletion drivers/gpu/drm/drm_edid.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include "drmP.h"
#include "drm_edid.h"
#include "drm_edid_modes.h"
Expand Down
9 changes: 3 additions & 6 deletions drivers/gpu/drm/i915/dvo_ch7017.c
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,6 @@ static void ch7017_dpms(struct intel_dvo_device *dvo, int mode);
static bool ch7017_read(struct intel_dvo_device *dvo, int addr, uint8_t *val)
{
struct i2c_adapter *adapter = dvo->i2c_bus;
struct intel_i2c_chan *i2cbus = container_of(adapter, struct intel_i2c_chan, adapter);
u8 out_buf[2];
u8 in_buf[2];

Expand All @@ -190,7 +189,7 @@ static bool ch7017_read(struct intel_dvo_device *dvo, int addr, uint8_t *val)
out_buf[0] = addr;
out_buf[1] = 0;

if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) {
if (i2c_transfer(adapter, msgs, 2) == 2) {
*val= in_buf[0];
return true;
};
Expand All @@ -201,7 +200,6 @@ static bool ch7017_read(struct intel_dvo_device *dvo, int addr, uint8_t *val)
static bool ch7017_write(struct intel_dvo_device *dvo, int addr, uint8_t val)
{
struct i2c_adapter *adapter = dvo->i2c_bus;
struct intel_i2c_chan *i2cbus = container_of(adapter, struct intel_i2c_chan, adapter);
uint8_t out_buf[2];
struct i2c_msg msg = {
.addr = dvo->slave_addr,
Expand All @@ -213,7 +211,7 @@ static bool ch7017_write(struct intel_dvo_device *dvo, int addr, uint8_t val)
out_buf[0] = addr;
out_buf[1] = val;

if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1)
if (i2c_transfer(adapter, &msg, 1) == 1)
return true;

return false;
Expand All @@ -223,7 +221,6 @@ static bool ch7017_write(struct intel_dvo_device *dvo, int addr, uint8_t val)
static bool ch7017_init(struct intel_dvo_device *dvo,
struct i2c_adapter *adapter)
{
struct intel_i2c_chan *i2cbus = container_of(adapter, struct intel_i2c_chan, adapter);
struct ch7017_priv *priv;
uint8_t val;

Expand All @@ -242,7 +239,7 @@ static bool ch7017_init(struct intel_dvo_device *dvo,
val != CH7019_DEVICE_ID_VALUE) {
DRM_DEBUG_KMS("ch701x not detected, got %d: from %s "
"Slave %d.\n",
val, i2cbus->adapter.name,dvo->slave_addr);
val, adapter->name,dvo->slave_addr);
goto fail;
}

Expand Down
10 changes: 4 additions & 6 deletions drivers/gpu/drm/i915/dvo_ch7xxx.c
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@ static bool ch7xxx_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
{
struct ch7xxx_priv *ch7xxx= dvo->dev_priv;
struct i2c_adapter *adapter = dvo->i2c_bus;
struct intel_i2c_chan *i2cbus = container_of(adapter, struct intel_i2c_chan, adapter);
u8 out_buf[2];
u8 in_buf[2];

Expand All @@ -135,14 +134,14 @@ static bool ch7xxx_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
out_buf[0] = addr;
out_buf[1] = 0;

if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) {
if (i2c_transfer(adapter, msgs, 2) == 2) {
*ch = in_buf[0];
return true;
};

if (!ch7xxx->quiet) {
DRM_DEBUG_KMS("Unable to read register 0x%02x from %s:%02x.\n",
addr, i2cbus->adapter.name, dvo->slave_addr);
addr, adapter->name, dvo->slave_addr);
}
return false;
}
Expand All @@ -152,7 +151,6 @@ static bool ch7xxx_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
{
struct ch7xxx_priv *ch7xxx = dvo->dev_priv;
struct i2c_adapter *adapter = dvo->i2c_bus;
struct intel_i2c_chan *i2cbus = container_of(adapter, struct intel_i2c_chan, adapter);
uint8_t out_buf[2];
struct i2c_msg msg = {
.addr = dvo->slave_addr,
Expand All @@ -164,12 +162,12 @@ static bool ch7xxx_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
out_buf[0] = addr;
out_buf[1] = ch;

if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1)
if (i2c_transfer(adapter, &msg, 1) == 1)
return true;

if (!ch7xxx->quiet) {
DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n",
addr, i2cbus->adapter.name, dvo->slave_addr);
addr, adapter->name, dvo->slave_addr);
}

return false;
Expand Down
10 changes: 4 additions & 6 deletions drivers/gpu/drm/i915/dvo_ivch.c
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,6 @@ static bool ivch_read(struct intel_dvo_device *dvo, int addr, uint16_t *data)
{
struct ivch_priv *priv = dvo->dev_priv;
struct i2c_adapter *adapter = dvo->i2c_bus;
struct intel_i2c_chan *i2cbus = container_of(adapter, struct intel_i2c_chan, adapter);
u8 out_buf[1];
u8 in_buf[2];

Expand All @@ -193,15 +192,15 @@ static bool ivch_read(struct intel_dvo_device *dvo, int addr, uint16_t *data)

out_buf[0] = addr;

if (i2c_transfer(&i2cbus->adapter, msgs, 3) == 3) {
if (i2c_transfer(adapter, msgs, 3) == 3) {
*data = (in_buf[1] << 8) | in_buf[0];
return true;
};

if (!priv->quiet) {
DRM_DEBUG_KMS("Unable to read register 0x%02x from "
"%s:%02x.\n",
addr, i2cbus->adapter.name, dvo->slave_addr);
addr, adapter->name, dvo->slave_addr);
}
return false;
}
Expand All @@ -211,7 +210,6 @@ static bool ivch_write(struct intel_dvo_device *dvo, int addr, uint16_t data)
{
struct ivch_priv *priv = dvo->dev_priv;
struct i2c_adapter *adapter = dvo->i2c_bus;
struct intel_i2c_chan *i2cbus = container_of(adapter, struct intel_i2c_chan, adapter);
u8 out_buf[3];
struct i2c_msg msg = {
.addr = dvo->slave_addr,
Expand All @@ -224,12 +222,12 @@ static bool ivch_write(struct intel_dvo_device *dvo, int addr, uint16_t data)
out_buf[1] = data & 0xff;
out_buf[2] = data >> 8;

if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1)
if (i2c_transfer(adapter, &msg, 1) == 1)
return true;

if (!priv->quiet) {
DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n",
addr, i2cbus->adapter.name, dvo->slave_addr);
addr, adapter->name, dvo->slave_addr);
}

return false;
Expand Down
10 changes: 4 additions & 6 deletions drivers/gpu/drm/i915/dvo_sil164.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ static bool sil164_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
{
struct sil164_priv *sil = dvo->dev_priv;
struct i2c_adapter *adapter = dvo->i2c_bus;
struct intel_i2c_chan *i2cbus = container_of(adapter, struct intel_i2c_chan, adapter);
u8 out_buf[2];
u8 in_buf[2];

Expand All @@ -91,14 +90,14 @@ static bool sil164_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
out_buf[0] = addr;
out_buf[1] = 0;

if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) {
if (i2c_transfer(adapter, msgs, 2) == 2) {
*ch = in_buf[0];
return true;
};

if (!sil->quiet) {
DRM_DEBUG_KMS("Unable to read register 0x%02x from %s:%02x.\n",
addr, i2cbus->adapter.name, dvo->slave_addr);
addr, adapter->name, dvo->slave_addr);
}
return false;
}
Expand All @@ -107,7 +106,6 @@ static bool sil164_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
{
struct sil164_priv *sil= dvo->dev_priv;
struct i2c_adapter *adapter = dvo->i2c_bus;
struct intel_i2c_chan *i2cbus = container_of(adapter, struct intel_i2c_chan, adapter);
uint8_t out_buf[2];
struct i2c_msg msg = {
.addr = dvo->slave_addr,
Expand All @@ -119,12 +117,12 @@ static bool sil164_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
out_buf[0] = addr;
out_buf[1] = ch;

if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1)
if (i2c_transfer(adapter, &msg, 1) == 1)
return true;

if (!sil->quiet) {
DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n",
addr, i2cbus->adapter.name, dvo->slave_addr);
addr, adapter->name, dvo->slave_addr);
}

return false;
Expand Down
10 changes: 4 additions & 6 deletions drivers/gpu/drm/i915/dvo_tfp410.c
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ static bool tfp410_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
{
struct tfp410_priv *tfp = dvo->dev_priv;
struct i2c_adapter *adapter = dvo->i2c_bus;
struct intel_i2c_chan *i2cbus = container_of(adapter, struct intel_i2c_chan, adapter);
u8 out_buf[2];
u8 in_buf[2];

Expand All @@ -116,14 +115,14 @@ static bool tfp410_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
out_buf[0] = addr;
out_buf[1] = 0;

if (i2c_transfer(&i2cbus->adapter, msgs, 2) == 2) {
if (i2c_transfer(adapter, msgs, 2) == 2) {
*ch = in_buf[0];
return true;
};

if (!tfp->quiet) {
DRM_DEBUG_KMS("Unable to read register 0x%02x from %s:%02x.\n",
addr, i2cbus->adapter.name, dvo->slave_addr);
addr, adapter->name, dvo->slave_addr);
}
return false;
}
Expand All @@ -132,7 +131,6 @@ static bool tfp410_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
{
struct tfp410_priv *tfp = dvo->dev_priv;
struct i2c_adapter *adapter = dvo->i2c_bus;
struct intel_i2c_chan *i2cbus = container_of(adapter, struct intel_i2c_chan, adapter);
uint8_t out_buf[2];
struct i2c_msg msg = {
.addr = dvo->slave_addr,
Expand All @@ -144,12 +142,12 @@ static bool tfp410_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
out_buf[0] = addr;
out_buf[1] = ch;

if (i2c_transfer(&i2cbus->adapter, &msg, 1) == 1)
if (i2c_transfer(adapter, &msg, 1) == 1)
return true;

if (!tfp->quiet) {
DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n",
addr, i2cbus->adapter.name, dvo->slave_addr);
addr, adapter->name, dvo->slave_addr);
}

return false;
Expand Down
2 changes: 2 additions & 0 deletions drivers/gpu/drm/i915/i915_dma.c
Original file line number Diff line number Diff line change
Expand Up @@ -2001,6 +2001,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)

/* Try to make sure MCHBAR is enabled before poking at it */
intel_setup_mchbar(dev);
intel_setup_gmbus(dev);
intel_opregion_setup(dev);

i915_gem_load(dev);
Expand Down Expand Up @@ -2155,6 +2156,7 @@ int i915_driver_unload(struct drm_device *dev)
intel_cleanup_overlay(dev);
}

intel_teardown_gmbus(dev);
intel_teardown_mchbar(dev);

destroy_workqueue(dev_priv->wq);
Expand Down
14 changes: 13 additions & 1 deletion drivers/gpu/drm/i915/i915_drv.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "intel_bios.h"
#include "intel_ringbuffer.h"
#include <linux/io-mapping.h>
#include <linux/i2c.h>
#include <drm/intel-gtt.h>

/* General customization:
Expand Down Expand Up @@ -246,6 +247,12 @@ typedef struct drm_i915_private {

void __iomem *regs;

struct intel_gmbus {
struct i2c_adapter adapter;
struct i2c_adapter *force_bitbanging;
int pin;
} *gmbus;

struct pci_dev *bridge_dev;
struct intel_ring_buffer render_ring;
struct intel_ring_buffer bsd_ring;
Expand Down Expand Up @@ -339,7 +346,7 @@ typedef struct drm_i915_private {

struct notifier_block lid_notifier;

int crt_ddc_bus; /* 0 = unknown, else GPIO to use for CRT DDC */
int crt_ddc_pin;
struct drm_i915_fence_reg fence_regs[16]; /* assume 965 */
int fence_reg_start; /* 4 if userland hasn't ioctl'd us yet */
int num_fence_regs; /* 8 on pre-965, 16 otherwise */
Expand Down Expand Up @@ -1070,6 +1077,11 @@ extern int i915_restore_state(struct drm_device *dev);
extern int i915_save_state(struct drm_device *dev);
extern int i915_restore_state(struct drm_device *dev);

/* intel_i2c.c */
extern int intel_setup_gmbus(struct drm_device *dev);
extern void intel_teardown_gmbus(struct drm_device *dev);
extern void intel_i2c_reset(struct drm_device *dev);

/* intel_opregion.c */
extern int intel_opregion_setup(struct drm_device *dev);
#ifdef CONFIG_ACPI
Expand Down
51 changes: 45 additions & 6 deletions drivers/gpu/drm/i915/i915_reg.h
Original file line number Diff line number Diff line change
Expand Up @@ -583,12 +583,51 @@
# 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 */
/* 6 reserved */
#define GMBUS_PORT_DPD 7 /* HDMID */
#define GMBUS_NUM_PORTS 8
#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_WAIT (1<<25)
#define GMBUS_CYCLE_INDEX (2<<25)
#define GMBUS_CYCLE_STOP (4<<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
Expand Down
4 changes: 1 addition & 3 deletions drivers/gpu/drm/i915/i915_suspend.c
Original file line number Diff line number Diff line change
Expand Up @@ -860,9 +860,7 @@ int i915_restore_state(struct drm_device *dev)
for (i = 0; i < 3; i++)
I915_WRITE(SWF30 + (i << 2), dev_priv->saveSWF2[i]);

/* I2C state */
intel_i2c_reset_gmbus(dev);
intel_i2c_reset(dev);

return 0;
}

Loading

0 comments on commit f899fc6

Please sign in to comment.