From 486a45c2a6c19b159602d044ab601a92cd81f524 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 11 Nov 2011 10:22:19 +1000 Subject: drm/nouveau/i2c: do parsing of i2c-related vbios info in nouveau_i2c.c Not much point parsing the vbios data into a struct which is only used once to parse the data into another struct, go directly from vbios to nouveau_i2c_chan. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_i2c.c | 302 ++++++++++++++++++++++------------ 1 file changed, 200 insertions(+), 102 deletions(-) (limited to 'drivers/gpu/drm/nouveau/nouveau_i2c.c') diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.c b/drivers/gpu/drm/nouveau/nouveau_i2c.c index d39b2202b197..36ffcb84f55a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_i2c.c +++ b/drivers/gpu/drm/nouveau/nouveau_i2c.c @@ -109,13 +109,6 @@ nv4e_i2c_getsda(void *data) return !!((nv_rd32(dev, i2c->rd) >> 16) & 8); } -static const uint32_t nv50_i2c_port[] = { - 0x00e138, 0x00e150, 0x00e168, 0x00e180, - 0x00e254, 0x00e274, 0x00e764, 0x00e780, - 0x00e79c, 0x00e7b8 -}; -#define NV50_I2C_PORTS ARRAY_SIZE(nv50_i2c_port) - static int nv50_i2c_getscl(void *data) { @@ -125,7 +118,6 @@ nv50_i2c_getscl(void *data) return !!(nv_rd32(dev, i2c->rd) & 1); } - static int nv50_i2c_getsda(void *data) { @@ -166,125 +158,233 @@ nvd0_i2c_getsda(void *data) return !!(nv_rd32(i2c->dev, i2c->rd) & 0x20); } +static const uint32_t nv50_i2c_port[] = { + 0x00e138, 0x00e150, 0x00e168, 0x00e180, + 0x00e254, 0x00e274, 0x00e764, 0x00e780, + 0x00e79c, 0x00e7b8 +}; + +static u8 * +i2c_table(struct drm_device *dev, u8 *version) +{ + u8 *dcb = dcb_table(dev), *i2c = NULL; + if (dcb) { + if (dcb[0] >= 0x15) + i2c = ROMPTR(dev, dcb[2]); + if (dcb[0] >= 0x30) + i2c = ROMPTR(dev, dcb[4]); + } + + /* early revisions had no version number, use dcb version */ + if (i2c) { + *version = dcb[0]; + if (*version >= 0x30) + *version = i2c[0]; + } + + return i2c; +} + int -nouveau_i2c_init(struct drm_device *dev, struct dcb_i2c_entry *entry, int index) +nouveau_i2c_init(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct nouveau_i2c_chan *i2c; - int ret; - - if (entry->chan) - return -EEXIST; + struct nvbios *bios = &dev_priv->vbios; + struct nouveau_i2c_chan *port; + u8 *i2c, *entry, legacy[2][4] = {}; + u8 version, entries, recordlen; + int ret, i; + + INIT_LIST_HEAD(&dev_priv->i2c_ports); + + i2c = i2c_table(dev, &version); + if (!i2c) { + u8 *bmp = &bios->data[bios->offset]; + if (bios->type != NVBIOS_BMP) + return -ENODEV; + + legacy[0][0] = NV_CIO_CRE_DDC_WR__INDEX; + legacy[0][1] = NV_CIO_CRE_DDC_STATUS__INDEX; + legacy[1][0] = NV_CIO_CRE_DDC0_WR__INDEX; + legacy[1][1] = NV_CIO_CRE_DDC0_STATUS__INDEX; + + /* BMP (from v4.0) has i2c info in the structure, it's in a + * fixed location on earlier VBIOS + */ + if (bmp[5] < 4) + i2c = &bios->data[0x48]; + else + i2c = &bmp[0x36]; + + if (i2c[4]) legacy[0][0] = i2c[4]; + if (i2c[5]) legacy[0][1] = i2c[5]; + if (i2c[6]) legacy[1][0] = i2c[6]; + if (i2c[7]) legacy[1][1] = i2c[7]; + } - if (dev_priv->card_type >= NV_50 && - dev_priv->card_type <= NV_C0 && entry->read >= NV50_I2C_PORTS) { - NV_ERROR(dev, "unknown i2c port %d\n", entry->read); - return -EINVAL; + if (i2c && version >= 0x30) { + entry = i2c[1] + i2c; + entries = i2c[2]; + recordlen = i2c[3]; + } else + if (i2c) { + entry = i2c; + entries = 16; + recordlen = 4; + } else { + entry = legacy[0]; + entries = 2; + recordlen = 4; } - i2c = kzalloc(sizeof(*i2c), GFP_KERNEL); - if (i2c == NULL) - return -ENOMEM; - - switch (entry->port_type) { - case 0: - i2c->bit.setsda = nv04_i2c_setsda; - i2c->bit.setscl = nv04_i2c_setscl; - i2c->bit.getsda = nv04_i2c_getsda; - i2c->bit.getscl = nv04_i2c_getscl; - i2c->rd = entry->read; - i2c->wr = entry->write; - break; - case 4: - i2c->bit.setsda = nv4e_i2c_setsda; - i2c->bit.setscl = nv4e_i2c_setscl; - i2c->bit.getsda = nv4e_i2c_getsda; - i2c->bit.getscl = nv4e_i2c_getscl; - i2c->rd = 0x600800 + entry->read; - i2c->wr = 0x600800 + entry->write; - break; - case 5: - i2c->bit.setsda = nv50_i2c_setsda; - i2c->bit.setscl = nv50_i2c_setscl; - if (dev_priv->card_type < NV_D0) { - i2c->bit.getsda = nv50_i2c_getsda; - i2c->bit.getscl = nv50_i2c_getscl; - i2c->rd = nv50_i2c_port[entry->read]; - i2c->wr = i2c->rd; + for (i = 0; i < entries; i++, entry += recordlen) { + port = kzalloc(sizeof(*port), GFP_KERNEL); + if (port == NULL) { + nouveau_i2c_fini(dev); + return -ENOMEM; + } + + port->type = entry[3]; + if (version < 0x30) { + port->type &= 0x07; + if (port->type == 0x07) + port->type = 0xff; + } + + if (port->type == 0xff) { + kfree(port); + continue; + } + + switch (port->type) { + case 0: /* NV04:NV50 */ + port->wr = entry[0]; + port->rd = entry[1]; + port->bit.setsda = nv04_i2c_setsda; + port->bit.setscl = nv04_i2c_setscl; + port->bit.getsda = nv04_i2c_getsda; + port->bit.getscl = nv04_i2c_getscl; + break; + case 4: /* NV4E */ + port->wr = 0x600800 + entry[1]; + port->rd = port->wr; + port->bit.setsda = nv4e_i2c_setsda; + port->bit.setscl = nv4e_i2c_setscl; + port->bit.getsda = nv4e_i2c_getsda; + port->bit.getscl = nv4e_i2c_getscl; + break; + case 5: /* NV50- */ + port->wr = entry[0] & 0x0f; + if (dev_priv->card_type < NV_D0) { + if (port->wr >= ARRAY_SIZE(nv50_i2c_port)) + break; + port->wr = nv50_i2c_port[port->wr]; + port->rd = port->wr; + port->bit.getsda = nv50_i2c_getsda; + port->bit.getscl = nv50_i2c_getscl; + } else { + port->wr = 0x00d014 + (port->wr * 0x20); + port->rd = port->wr; + port->bit.getsda = nvd0_i2c_getsda; + port->bit.getscl = nvd0_i2c_getscl; + } + port->bit.setsda = nv50_i2c_setsda; + port->bit.setscl = nv50_i2c_setscl; + break; + case 6: /* NV50- DP AUX */ + port->wr = entry[0]; + port->rd = port->wr; + port->adapter.algo = &nouveau_dp_i2c_algo; + break; + default: + break; + } + + if (!port->adapter.algo && !port->wr) { + NV_ERROR(dev, "I2C%d: type %d index %x/%x unknown\n", + i, port->type, port->wr, port->rd); + kfree(port); + continue; + } + + snprintf(port->adapter.name, sizeof(port->adapter.name), + "nouveau-%s-%d", pci_name(dev->pdev), i); + port->adapter.owner = THIS_MODULE; + port->adapter.dev.parent = &dev->pdev->dev; + port->dev = dev; + port->index = i; + port->dcb = ROM32(entry[0]); + i2c_set_adapdata(&port->adapter, i2c); + + if (port->adapter.algo != &nouveau_dp_i2c_algo) { + port->adapter.algo_data = &port->bit; + port->bit.udelay = 40; + port->bit.timeout = usecs_to_jiffies(5000); + port->bit.data = port; + ret = i2c_bit_add_bus(&port->adapter); } else { - i2c->bit.getsda = nvd0_i2c_getsda; - i2c->bit.getscl = nvd0_i2c_getscl; - i2c->rd = 0x00d014 + (entry->read * 0x20); - i2c->wr = i2c->rd; + port->adapter.algo = &nouveau_dp_i2c_algo; + ret = i2c_add_adapter(&port->adapter); } - break; - case 6: - i2c->rd = entry->read; - i2c->wr = entry->write; - break; - default: - NV_ERROR(dev, "DCB I2C port type %d unknown\n", - entry->port_type); - kfree(i2c); - return -EINVAL; - } - snprintf(i2c->adapter.name, sizeof(i2c->adapter.name), - "nouveau-%s-%d", pci_name(dev->pdev), index); - i2c->adapter.owner = THIS_MODULE; - i2c->adapter.dev.parent = &dev->pdev->dev; - i2c->dev = dev; - i2c_set_adapdata(&i2c->adapter, i2c); - - if (entry->port_type < 6) { - i2c->adapter.algo_data = &i2c->bit; - i2c->bit.udelay = 40; - i2c->bit.timeout = usecs_to_jiffies(5000); - i2c->bit.data = i2c; - ret = i2c_bit_add_bus(&i2c->adapter); - } else { - i2c->adapter.algo = &nouveau_dp_i2c_algo; - ret = i2c_add_adapter(&i2c->adapter); - } + if (ret) { + NV_ERROR(dev, "I2C%d: failed register: %d\n", i, ret); + kfree(port); + continue; + } - if (ret) { - NV_ERROR(dev, "Failed to register i2c %d\n", index); - kfree(i2c); - return ret; + list_add_tail(&port->head, &dev_priv->i2c_ports); } - entry->chan = i2c; return 0; } void -nouveau_i2c_fini(struct drm_device *dev, struct dcb_i2c_entry *entry) +nouveau_i2c_fini(struct drm_device *dev) { - if (!entry->chan) - return; + struct drm_nouveau_private *dev_priv = dev->dev_private; + struct nouveau_i2c_chan *port, *tmp; - i2c_del_adapter(&entry->chan->adapter); - kfree(entry->chan); - entry->chan = NULL; + list_for_each_entry_safe(port, tmp, &dev_priv->i2c_ports, head) { + i2c_del_adapter(&port->adapter); + kfree(port); + } } struct nouveau_i2c_chan * -nouveau_i2c_find(struct drm_device *dev, int index) +nouveau_i2c_find(struct drm_device *dev, u8 index) { struct drm_nouveau_private *dev_priv = dev->dev_private; - struct dcb_i2c_entry *i2c = &dev_priv->vbios.dcb.i2c[index]; + struct nouveau_i2c_chan *port; + + if (index == NV_I2C_DEFAULT(0) || + index == NV_I2C_DEFAULT(1)) { + u8 version, *i2c = i2c_table(dev, &version); + if (i2c && version >= 0x30) { + if (index == NV_I2C_DEFAULT(0)) + index = (i2c[4] & 0x0f); + else + index = (i2c[4] & 0xf0) >> 4; + } else { + index = 2; + } + } - if (index >= DCB_MAX_NUM_I2C_ENTRIES) - return NULL; + list_for_each_entry(port, &dev_priv->i2c_ports, head) { + if (port->index == index) + break; + } - if (dev_priv->card_type >= NV_50 && (i2c->entry & 0x00000100)) { - uint32_t reg = 0xe500, val; + if (&port->head == &dev_priv->i2c_ports) + return NULL; - if (i2c->port_type == 6) { - reg += i2c->read * 0x50; + if (dev_priv->card_type >= NV_50 && (port->dcb & 0x00000100)) { + u32 reg = 0x00e500, val; + if (port->type == 6) { + reg += port->rd * 0x50; val = 0x2002; } else { - reg += ((i2c->entry & 0x1e00) >> 9) * 0x50; + reg += ((port->dcb & 0x1e00) >> 9) * 0x50; val = 0xe001; } @@ -294,9 +394,7 @@ nouveau_i2c_find(struct drm_device *dev, int index) nv_mask(dev, reg + 0x00, 0x0000f003, val); } - if (!i2c->chan && nouveau_i2c_init(dev, i2c, index)) - return NULL; - return i2c->chan; + return port; } bool -- cgit v1.2.3 From 2bdb06e3cff066c546fb41152bc582a5ec73e899 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Thu, 17 Nov 2011 13:56:14 +1000 Subject: drm/nouveau/i2c: tidy up bit-bang helpers, also fixing nv50 setsda bug Was using nv_mask, which is bad. Reading the reg senses the current line states, which aren't necessarily the states we're trying to drive the lines to. Fixed to store SCL driver state just as we already do for SDA. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_dp.c | 6 +- drivers/gpu/drm/nouveau/nouveau_i2c.c | 230 +++++++++++++--------------------- drivers/gpu/drm/nouveau/nouveau_i2c.h | 6 +- 3 files changed, 94 insertions(+), 148 deletions(-) (limited to 'drivers/gpu/drm/nouveau/nouveau_i2c.c') diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 5722fd55764b..07bac3602453 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -578,7 +578,7 @@ nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate) dp.dcb = nv_encoder->dcb; dp.crtc = nv_crtc->index; - dp.auxch = auxch->rd; + dp.auxch = auxch->drive; dp.or = nv_encoder->or; dp.link = !(nv_encoder->dcb->sorconf.link & 1); dp.dpcd = nv_encoder->dp.dpcd; @@ -653,7 +653,7 @@ nouveau_dp_detect(struct drm_encoder *encoder) if (!auxch) return false; - ret = auxch_tx(dev, auxch->rd, 9, DP_DPCD_REV, dpcd, 8); + ret = auxch_tx(dev, auxch->drive, 9, DP_DPCD_REV, dpcd, 8); if (ret) return false; @@ -681,7 +681,7 @@ int nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr, uint8_t *data, int data_nr) { - return auxch_tx(auxch->dev, auxch->rd, cmd, addr, data, data_nr); + return auxch_tx(auxch->dev, auxch->drive, cmd, addr, data, data_nr); } static int diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.c b/drivers/gpu/drm/nouveau/nouveau_i2c.c index 36ffcb84f55a..d0d581440d5e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_i2c.c +++ b/drivers/gpu/drm/nouveau/nouveau_i2c.c @@ -30,132 +30,83 @@ #include "nouveau_hw.h" static void -nv04_i2c_setscl(void *data, int state) +i2c_drive_scl(void *data, int state) { - struct nouveau_i2c_chan *i2c = data; - struct drm_device *dev = i2c->dev; - uint8_t val; - - val = (NVReadVgaCrtc(dev, 0, i2c->wr) & 0xd0) | (state ? 0x20 : 0); - NVWriteVgaCrtc(dev, 0, i2c->wr, val | 0x01); -} - -static void -nv04_i2c_setsda(void *data, int state) -{ - struct nouveau_i2c_chan *i2c = data; - struct drm_device *dev = i2c->dev; - uint8_t val; - - val = (NVReadVgaCrtc(dev, 0, i2c->wr) & 0xe0) | (state ? 0x10 : 0); - NVWriteVgaCrtc(dev, 0, i2c->wr, val | 0x01); -} - -static int -nv04_i2c_getscl(void *data) -{ - struct nouveau_i2c_chan *i2c = data; - struct drm_device *dev = i2c->dev; - - return !!(NVReadVgaCrtc(dev, 0, i2c->rd) & 4); -} - -static int -nv04_i2c_getsda(void *data) -{ - struct nouveau_i2c_chan *i2c = data; - struct drm_device *dev = i2c->dev; - - return !!(NVReadVgaCrtc(dev, 0, i2c->rd) & 8); -} - -static void -nv4e_i2c_setscl(void *data, int state) -{ - struct nouveau_i2c_chan *i2c = data; - struct drm_device *dev = i2c->dev; - uint8_t val; - - val = (nv_rd32(dev, i2c->wr) & 0xd0) | (state ? 0x20 : 0); - nv_wr32(dev, i2c->wr, val | 0x01); -} - -static void -nv4e_i2c_setsda(void *data, int state) -{ - struct nouveau_i2c_chan *i2c = data; - struct drm_device *dev = i2c->dev; - uint8_t val; - - val = (nv_rd32(dev, i2c->wr) & 0xe0) | (state ? 0x10 : 0); - nv_wr32(dev, i2c->wr, val | 0x01); -} - -static int -nv4e_i2c_getscl(void *data) -{ - struct nouveau_i2c_chan *i2c = data; - struct drm_device *dev = i2c->dev; - - return !!((nv_rd32(dev, i2c->rd) >> 16) & 4); -} - -static int -nv4e_i2c_getsda(void *data) -{ - struct nouveau_i2c_chan *i2c = data; - struct drm_device *dev = i2c->dev; - - return !!((nv_rd32(dev, i2c->rd) >> 16) & 8); -} - -static int -nv50_i2c_getscl(void *data) -{ - struct nouveau_i2c_chan *i2c = data; - struct drm_device *dev = i2c->dev; - - return !!(nv_rd32(dev, i2c->rd) & 1); -} - -static int -nv50_i2c_getsda(void *data) -{ - struct nouveau_i2c_chan *i2c = data; - struct drm_device *dev = i2c->dev; - - return !!(nv_rd32(dev, i2c->rd) & 2); -} - -static void -nv50_i2c_setscl(void *data, int state) -{ - struct nouveau_i2c_chan *i2c = data; - - nv_wr32(i2c->dev, i2c->wr, 4 | (i2c->data ? 2 : 0) | (state ? 1 : 0)); + struct nouveau_i2c_chan *port = data; + if (port->type == 0) { + u8 val = NVReadVgaCrtc(port->dev, 0, port->drive); + if (state) val |= 0x20; + else val &= 0xdf; + NVWriteVgaCrtc(port->dev, 0, port->drive, val | 0x01); + } else + if (port->type == 4) { + nv_mask(port->dev, port->drive, 0x2f, state ? 0x21 : 0x01); + } else + if (port->type == 5) { + if (state) port->state |= 0x01; + else port->state &= 0xfe; + nv_wr32(port->dev, port->drive, 4 | port->state); + } } static void -nv50_i2c_setsda(void *data, int state) +i2c_drive_sda(void *data, int state) { - struct nouveau_i2c_chan *i2c = data; - - nv_mask(i2c->dev, i2c->wr, 0x00000006, 4 | (state ? 2 : 0)); - i2c->data = state; + struct nouveau_i2c_chan *port = data; + if (port->type == 0) { + u8 val = NVReadVgaCrtc(port->dev, 0, port->drive); + if (state) val |= 0x10; + else val &= 0xef; + NVWriteVgaCrtc(port->dev, 0, port->drive, val | 0x01); + } else + if (port->type == 4) { + nv_mask(port->dev, port->drive, 0x1f, state ? 0x11 : 0x01); + } else + if (port->type == 5) { + if (state) port->state |= 0x02; + else port->state &= 0xfd; + nv_wr32(port->dev, port->drive, 4 | port->state); + } } static int -nvd0_i2c_getscl(void *data) +i2c_sense_scl(void *data) { - struct nouveau_i2c_chan *i2c = data; - return !!(nv_rd32(i2c->dev, i2c->rd) & 0x10); + struct nouveau_i2c_chan *port = data; + struct drm_nouveau_private *dev_priv = port->dev->dev_private; + if (port->type == 0) { + return !!(NVReadVgaCrtc(port->dev, 0, port->sense) & 0x04); + } else + if (port->type == 4) { + return !!(nv_rd32(port->dev, port->sense) & 0x00040000); + } else + if (port->type == 5) { + if (dev_priv->card_type < NV_D0) + return !!(nv_rd32(port->dev, port->sense) & 0x01); + else + return !!(nv_rd32(port->dev, port->sense) & 0x10); + } + return 0; } static int -nvd0_i2c_getsda(void *data) +i2c_sense_sda(void *data) { - struct nouveau_i2c_chan *i2c = data; - return !!(nv_rd32(i2c->dev, i2c->rd) & 0x20); + struct nouveau_i2c_chan *port = data; + struct drm_nouveau_private *dev_priv = port->dev->dev_private; + if (port->type == 0) { + return !!(NVReadVgaCrtc(port->dev, 0, port->sense) & 0x08); + } else + if (port->type == 4) { + return !!(nv_rd32(port->dev, port->sense) & 0x00080000); + } else + if (port->type == 5) { + if (dev_priv->card_type < NV_D0) + return !!(nv_rd32(port->dev, port->sense) & 0x02); + else + return !!(nv_rd32(port->dev, port->sense) & 0x20); + } + return 0; } static const uint32_t nv50_i2c_port[] = { @@ -258,51 +209,37 @@ nouveau_i2c_init(struct drm_device *dev) switch (port->type) { case 0: /* NV04:NV50 */ - port->wr = entry[0]; - port->rd = entry[1]; - port->bit.setsda = nv04_i2c_setsda; - port->bit.setscl = nv04_i2c_setscl; - port->bit.getsda = nv04_i2c_getsda; - port->bit.getscl = nv04_i2c_getscl; + port->drive = entry[0]; + port->sense = entry[1]; break; case 4: /* NV4E */ - port->wr = 0x600800 + entry[1]; - port->rd = port->wr; - port->bit.setsda = nv4e_i2c_setsda; - port->bit.setscl = nv4e_i2c_setscl; - port->bit.getsda = nv4e_i2c_getsda; - port->bit.getscl = nv4e_i2c_getscl; + port->drive = 0x600800 + entry[1]; + port->sense = port->drive; break; case 5: /* NV50- */ - port->wr = entry[0] & 0x0f; + port->drive = entry[0] & 0x0f; if (dev_priv->card_type < NV_D0) { - if (port->wr >= ARRAY_SIZE(nv50_i2c_port)) + if (port->drive >= ARRAY_SIZE(nv50_i2c_port)) break; - port->wr = nv50_i2c_port[port->wr]; - port->rd = port->wr; - port->bit.getsda = nv50_i2c_getsda; - port->bit.getscl = nv50_i2c_getscl; + port->drive = nv50_i2c_port[port->drive]; + port->sense = port->drive; } else { - port->wr = 0x00d014 + (port->wr * 0x20); - port->rd = port->wr; - port->bit.getsda = nvd0_i2c_getsda; - port->bit.getscl = nvd0_i2c_getscl; + port->drive = 0x00d014 + (port->drive * 0x20); + port->sense = port->drive; } - port->bit.setsda = nv50_i2c_setsda; - port->bit.setscl = nv50_i2c_setscl; break; case 6: /* NV50- DP AUX */ - port->wr = entry[0]; - port->rd = port->wr; + port->drive = entry[0]; + port->sense = port->drive; port->adapter.algo = &nouveau_dp_i2c_algo; break; default: break; } - if (!port->adapter.algo && !port->wr) { + if (!port->adapter.algo && !port->drive) { NV_ERROR(dev, "I2C%d: type %d index %x/%x unknown\n", - i, port->type, port->wr, port->rd); + i, port->type, port->drive, port->sense); kfree(port); continue; } @@ -321,6 +258,15 @@ nouveau_i2c_init(struct drm_device *dev) port->bit.udelay = 40; port->bit.timeout = usecs_to_jiffies(5000); port->bit.data = port; + port->bit.setsda = i2c_drive_sda; + port->bit.setscl = i2c_drive_scl; + port->bit.getsda = i2c_sense_sda; + port->bit.getscl = i2c_sense_scl; + + i2c_drive_scl(port, 0); + i2c_drive_sda(port, 1); + i2c_drive_scl(port, 1); + ret = i2c_bit_add_bus(&port->adapter); } else { port->adapter.algo = &nouveau_dp_i2c_algo; @@ -381,7 +327,7 @@ nouveau_i2c_find(struct drm_device *dev, u8 index) if (dev_priv->card_type >= NV_50 && (port->dcb & 0x00000100)) { u32 reg = 0x00e500, val; if (port->type == 6) { - reg += port->rd * 0x50; + reg += port->drive * 0x50; val = 0x2002; } else { reg += ((port->dcb & 0x1e00) >> 9) * 0x50; diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.h b/drivers/gpu/drm/nouveau/nouveau_i2c.h index cf5f67d51fba..1d083893a4d7 100644 --- a/drivers/gpu/drm/nouveau/nouveau_i2c.h +++ b/drivers/gpu/drm/nouveau/nouveau_i2c.h @@ -39,9 +39,9 @@ struct nouveau_i2c_chan { u8 index; u8 type; u32 dcb; - unsigned rd; - unsigned wr; - unsigned data; + u32 drive; + u32 sense; + u32 state; }; int nouveau_i2c_init(struct drm_device *); -- cgit v1.2.3 From 9e3b6b99075a01ce47379eee74365aece14242f3 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Tue, 22 Nov 2011 22:36:18 +1000 Subject: drm/nouveau/i2c: fix debug message Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_i2c.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers/gpu/drm/nouveau/nouveau_i2c.c') diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.c b/drivers/gpu/drm/nouveau/nouveau_i2c.c index d0d581440d5e..116ef3193fc7 100644 --- a/drivers/gpu/drm/nouveau/nouveau_i2c.c +++ b/drivers/gpu/drm/nouveau/nouveau_i2c.c @@ -375,9 +375,13 @@ nouveau_i2c_identify(struct drm_device *dev, const char *what, struct nouveau_i2c_chan *i2c = nouveau_i2c_find(dev, index); int i; - NV_DEBUG(dev, "Probing %ss on I2C bus: %d\n", what, index); + if (!i2c) { + NV_DEBUG(dev, "No bus when probing %s on %d\n", what, index); + return -ENODEV; + } - for (i = 0; i2c && info[i].addr; i++) { + NV_DEBUG(dev, "Probing %ss on I2C bus: %d\n", what, i2c->index); + for (i = 0; info[i].addr; i++) { if (nouveau_probe_i2c_addr(i2c, info[i].addr) && (!match || match(i2c, &info[i]))) { NV_INFO(dev, "Detected %s: %s\n", what, info[i].type); @@ -386,6 +390,5 @@ nouveau_i2c_identify(struct drm_device *dev, const char *what, } NV_DEBUG(dev, "No devices found.\n"); - return -ENODEV; } -- cgit v1.2.3 From f553b79c03f0dbd52f6f03abe8233a2bef8cbd0d Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Wed, 21 Dec 2011 18:09:12 +1000 Subject: drm/nouveau/i2c: handle bit-banging ourselves i2c-algo-bit doesn't actually work very well on one card I have access to (NVS 300), random single-bit errors occur most of the time - what we're doing now is closer to what xf86i2c.c does. The original plan was to figure out why i2c-algo-bit fails on the NVS 300, and fix it. However, while investigating I discovered i2c-algo-bit calls cond_resched(), which makes it a bad idea for us to be using as we execute VBIOS scripts from a tasklet, and there may very well be i2c transfers as a result. So, since I already wrote this code in userspace to track down the NVS 300 bug, and it's not really much code - lets use it. Signed-off-by: Ben Skeggs --- drivers/gpu/drm/nouveau/nouveau_i2c.c | 199 ++++++++++++++++++++++++++++++---- drivers/gpu/drm/nouveau/nouveau_i2c.h | 1 - 2 files changed, 178 insertions(+), 22 deletions(-) (limited to 'drivers/gpu/drm/nouveau/nouveau_i2c.c') diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.c b/drivers/gpu/drm/nouveau/nouveau_i2c.c index 116ef3193fc7..820ae7f52044 100644 --- a/drivers/gpu/drm/nouveau/nouveau_i2c.c +++ b/drivers/gpu/drm/nouveau/nouveau_i2c.c @@ -29,6 +29,10 @@ #include "nouveau_i2c.h" #include "nouveau_hw.h" +#define T_TIMEOUT 2200000 +#define T_RISEFALL 1000 +#define T_HOLD 5000 + static void i2c_drive_scl(void *data, int state) { @@ -109,6 +113,175 @@ i2c_sense_sda(void *data) return 0; } +static void +i2c_delay(struct nouveau_i2c_chan *port, u32 nsec) +{ + udelay((nsec + 500) / 1000); +} + +static bool +i2c_raise_scl(struct nouveau_i2c_chan *port) +{ + u32 timeout = T_TIMEOUT / T_RISEFALL; + + i2c_drive_scl(port, 1); + do { + i2c_delay(port, T_RISEFALL); + } while (!i2c_sense_scl(port) && --timeout); + + return timeout != 0; +} + +static int +i2c_start(struct nouveau_i2c_chan *port) +{ + int ret = 0; + + port->state = i2c_sense_scl(port); + port->state |= i2c_sense_sda(port) << 1; + if (port->state != 3) { + i2c_drive_scl(port, 0); + i2c_drive_sda(port, 1); + if (!i2c_raise_scl(port)) + ret = -EBUSY; + } + + i2c_drive_sda(port, 0); + i2c_delay(port, T_HOLD); + i2c_drive_scl(port, 0); + i2c_delay(port, T_HOLD); + return ret; +} + +static void +i2c_stop(struct nouveau_i2c_chan *port) +{ + i2c_drive_scl(port, 0); + i2c_drive_sda(port, 0); + i2c_delay(port, T_RISEFALL); + + i2c_drive_scl(port, 1); + i2c_delay(port, T_HOLD); + i2c_drive_sda(port, 1); + i2c_delay(port, T_HOLD); +} + +static int +i2c_bitw(struct nouveau_i2c_chan *port, int sda) +{ + i2c_drive_sda(port, sda); + i2c_delay(port, T_RISEFALL); + + if (!i2c_raise_scl(port)) + return -ETIMEDOUT; + i2c_delay(port, T_HOLD); + + i2c_drive_scl(port, 0); + i2c_delay(port, T_HOLD); + return 0; +} + +static int +i2c_bitr(struct nouveau_i2c_chan *port) +{ + int sda; + + i2c_drive_sda(port, 1); + i2c_delay(port, T_RISEFALL); + + if (!i2c_raise_scl(port)) + return -ETIMEDOUT; + i2c_delay(port, T_HOLD); + + sda = i2c_sense_sda(port); + + i2c_drive_scl(port, 0); + i2c_delay(port, T_HOLD); + return sda; +} + +static int +i2c_get_byte(struct nouveau_i2c_chan *port, u8 *byte, bool last) +{ + int i, bit; + + *byte = 0; + for (i = 7; i >= 0; i--) { + bit = i2c_bitr(port); + if (bit < 0) + return bit; + *byte |= bit << i; + } + + return i2c_bitw(port, last ? 1 : 0); +} + +static int +i2c_put_byte(struct nouveau_i2c_chan *port, u8 byte) +{ + int i, ret; + for (i = 7; i >= 0; i--) { + ret = i2c_bitw(port, !!(byte & (1 << i))); + if (ret < 0) + return ret; + } + + ret = i2c_bitr(port); + if (ret == 1) /* nack */ + ret = -EIO; + return ret; +} + +static int +i2c_addr(struct nouveau_i2c_chan *port, struct i2c_msg *msg) +{ + u32 addr = msg->addr << 1; + if (msg->flags & I2C_M_RD) + addr |= 1; + return i2c_put_byte(port, addr); +} + +static int +i2c_bit_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + struct nouveau_i2c_chan *port = (struct nouveau_i2c_chan *)adap; + struct i2c_msg *msg = msgs; + int ret = 0, mcnt = num; + + while (!ret && mcnt--) { + u8 remaining = msg->len; + u8 *ptr = msg->buf; + + ret = i2c_start(port); + if (ret == 0) + ret = i2c_addr(port, msg); + + if (msg->flags & I2C_M_RD) { + while (!ret && remaining--) + ret = i2c_get_byte(port, ptr++, !remaining); + } else { + while (!ret && remaining--) + ret = i2c_put_byte(port, *ptr++); + } + + msg++; + } + + i2c_stop(port); + return (ret < 0) ? ret : num; +} + +static u32 +i2c_bit_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +const struct i2c_algorithm i2c_bit_algo = { + .master_xfer = i2c_bit_xfer, + .functionality = i2c_bit_func +}; + static const uint32_t nv50_i2c_port[] = { 0x00e138, 0x00e150, 0x00e168, 0x00e180, 0x00e254, 0x00e274, 0x00e764, 0x00e780, @@ -211,10 +384,12 @@ nouveau_i2c_init(struct drm_device *dev) case 0: /* NV04:NV50 */ port->drive = entry[0]; port->sense = entry[1]; + port->adapter.algo = &i2c_bit_algo; break; case 4: /* NV4E */ port->drive = 0x600800 + entry[1]; port->sense = port->drive; + port->adapter.algo = &i2c_bit_algo; break; case 5: /* NV50- */ port->drive = entry[0] & 0x0f; @@ -227,6 +402,7 @@ nouveau_i2c_init(struct drm_device *dev) port->drive = 0x00d014 + (port->drive * 0x20); port->sense = port->drive; } + port->adapter.algo = &i2c_bit_algo; break; case 6: /* NV50- DP AUX */ port->drive = entry[0]; @@ -237,7 +413,7 @@ nouveau_i2c_init(struct drm_device *dev) break; } - if (!port->adapter.algo && !port->drive) { + if (!port->adapter.algo) { NV_ERROR(dev, "I2C%d: type %d index %x/%x unknown\n", i, port->type, port->drive, port->sense); kfree(port); @@ -253,26 +429,7 @@ nouveau_i2c_init(struct drm_device *dev) port->dcb = ROM32(entry[0]); i2c_set_adapdata(&port->adapter, i2c); - if (port->adapter.algo != &nouveau_dp_i2c_algo) { - port->adapter.algo_data = &port->bit; - port->bit.udelay = 40; - port->bit.timeout = usecs_to_jiffies(5000); - port->bit.data = port; - port->bit.setsda = i2c_drive_sda; - port->bit.setscl = i2c_drive_scl; - port->bit.getsda = i2c_sense_sda; - port->bit.getscl = i2c_sense_scl; - - i2c_drive_scl(port, 0); - i2c_drive_sda(port, 1); - i2c_drive_scl(port, 1); - - ret = i2c_bit_add_bus(&port->adapter); - } else { - port->adapter.algo = &nouveau_dp_i2c_algo; - ret = i2c_add_adapter(&port->adapter); - } - + ret = i2c_add_adapter(&port->adapter); if (ret) { NV_ERROR(dev, "I2C%d: failed register: %d\n", i, ret); kfree(port); diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.h b/drivers/gpu/drm/nouveau/nouveau_i2c.h index 1d083893a4d7..4d2e4e9031be 100644 --- a/drivers/gpu/drm/nouveau/nouveau_i2c.h +++ b/drivers/gpu/drm/nouveau/nouveau_i2c.h @@ -34,7 +34,6 @@ struct nouveau_i2c_chan { struct i2c_adapter adapter; struct drm_device *dev; - struct i2c_algo_bit_data bit; struct list_head head; u8 index; u8 type; -- cgit v1.2.3