diff options
author | Ben Skeggs <bskeggs@redhat.com> | 2011-11-11 04:22:19 +0400 |
---|---|---|
committer | Ben Skeggs <bskeggs@redhat.com> | 2011-12-21 13:01:40 +0400 |
commit | 486a45c2a6c19b159602d044ab601a92cd81f524 (patch) | |
tree | 350a368208768873667cd1a659a6d264a76f7c91 /drivers/gpu/drm/nouveau/nouveau_i2c.c | |
parent | 6b5a81a2e783f26a69fc262b3c393f0b391c8613 (diff) | |
download | linux-486a45c2a6c19b159602d044ab601a92cd81f524.tar.xz |
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 <bskeggs@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/nouveau/nouveau_i2c.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_i2c.c | 302 |
1 files changed, 200 insertions, 102 deletions
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 |