|
@@ -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
|