]> Pileus Git - ~andy/linux/blobdiff - drivers/gpu/drm/nouveau/nouveau_bios.c
drm/nv50: DCB quirk for Dell M6300
[~andy/linux] / drivers / gpu / drm / nouveau / nouveau_bios.c
index abc382a9918bfe9be71c33cb2cba83ee07a25d08..3e274d57d9bbab53c95a47e7e88d8b23cf025d87 100644 (file)
@@ -26,6 +26,7 @@
 #define NV_DEBUG_NOTRACE
 #include "nouveau_drv.h"
 #include "nouveau_hw.h"
+#include "nouveau_encoder.h"
 
 /* these defines are made up */
 #define NV_CIO_CRE_44_HEADA 0x0
@@ -177,6 +178,25 @@ out:
        pci_disable_rom(dev->pdev);
 }
 
+static void load_vbios_acpi(struct drm_device *dev, uint8_t *data)
+{
+       int i;
+       int ret;
+       int size = 64 * 1024;
+
+       if (!nouveau_acpi_rom_supported(dev->pdev))
+               return;
+
+       for (i = 0; i < (size / ROM_BIOS_PAGE); i++) {
+               ret = nouveau_acpi_get_bios_chunk(data,
+                                                 (i * ROM_BIOS_PAGE),
+                                                 ROM_BIOS_PAGE);
+               if (ret <= 0)
+                       break;
+       }
+       return;
+}
+
 struct methods {
        const char desc[8];
        void (*loadbios)(struct drm_device *, uint8_t *);
@@ -190,6 +210,7 @@ static struct methods nv04_methods[] = {
 };
 
 static struct methods nv50_methods[] = {
+       { "ACPI", load_vbios_acpi, true },
        { "PRAMIN", load_vbios_pramin, true },
        { "PROM", load_vbios_prom, false },
        { "PCIROM", load_vbios_pci, true },
@@ -256,6 +277,11 @@ static bool NVShadowVBIOS(struct drm_device *dev, uint8_t *data)
 struct init_tbl_entry {
        char *name;
        uint8_t id;
+       /* Return:
+        *  > 0: success, length of opcode
+        *    0: success, but abort further parsing of table (INIT_DONE etc)
+        *  < 0: failure, table parsing will be aborted
+        */
        int (*handler)(struct nvbios *, uint16_t, struct init_exec *);
 };
 
@@ -709,6 +735,83 @@ static int dcb_entry_idx_from_crtchead(struct drm_device *dev)
        return dcb_entry;
 }
 
+static int
+read_dcb_i2c_entry(struct drm_device *dev, int dcb_version, uint8_t *i2ctable, int index, struct dcb_i2c_entry *i2c)
+{
+       uint8_t dcb_i2c_ver = dcb_version, headerlen = 0, entry_len = 4;
+       int i2c_entries = DCB_MAX_NUM_I2C_ENTRIES;
+       int recordoffset = 0, rdofs = 1, wrofs = 0;
+       uint8_t port_type = 0;
+
+       if (!i2ctable)
+               return -EINVAL;
+
+       if (dcb_version >= 0x30) {
+               if (i2ctable[0] != dcb_version) /* necessary? */
+                       NV_WARN(dev,
+                               "DCB I2C table version mismatch (%02X vs %02X)\n",
+                               i2ctable[0], dcb_version);
+               dcb_i2c_ver = i2ctable[0];
+               headerlen = i2ctable[1];
+               if (i2ctable[2] <= DCB_MAX_NUM_I2C_ENTRIES)
+                       i2c_entries = i2ctable[2];
+               else
+                       NV_WARN(dev,
+                               "DCB I2C table has more entries than indexable "
+                               "(%d entries, max %d)\n", i2ctable[2],
+                               DCB_MAX_NUM_I2C_ENTRIES);
+               entry_len = i2ctable[3];
+               /* [4] is i2c_default_indices, read in parse_dcb_table() */
+       }
+       /*
+        * It's your own fault if you call this function on a DCB 1.1 BIOS --
+        * the test below is for DCB 1.2
+        */
+       if (dcb_version < 0x14) {
+               recordoffset = 2;
+               rdofs = 0;
+               wrofs = 1;
+       }
+
+       if (index == 0xf)
+               return 0;
+       if (index >= i2c_entries) {
+               NV_ERROR(dev, "DCB I2C index too big (%d >= %d)\n",
+                        index, i2ctable[2]);
+               return -ENOENT;
+       }
+       if (i2ctable[headerlen + entry_len * index + 3] == 0xff) {
+               NV_ERROR(dev, "DCB I2C entry invalid\n");
+               return -EINVAL;
+       }
+
+       if (dcb_i2c_ver >= 0x30) {
+               port_type = i2ctable[headerlen + recordoffset + 3 + entry_len * index];
+
+               /*
+                * Fixup for chips using same address offset for read and
+                * write.
+                */
+               if (port_type == 4)     /* seen on C51 */
+                       rdofs = wrofs = 1;
+               if (port_type >= 5)     /* G80+ */
+                       rdofs = wrofs = 0;
+       }
+
+       if (dcb_i2c_ver >= 0x40) {
+               if (port_type != 5 && port_type != 6)
+                       NV_WARN(dev, "DCB I2C table has port type %d\n", port_type);
+
+               i2c->entry = ROM32(i2ctable[headerlen + recordoffset + entry_len * index]);
+       }
+
+       i2c->port_type = port_type;
+       i2c->read = i2ctable[headerlen + recordoffset + rdofs + entry_len * index];
+       i2c->write = i2ctable[headerlen + recordoffset + wrofs + entry_len * index];
+
+       return 0;
+}
+
 static struct nouveau_i2c_chan *
 init_i2c_device_find(struct drm_device *dev, int i2c_index)
 {
@@ -727,6 +830,20 @@ init_i2c_device_find(struct drm_device *dev, int i2c_index)
        }
        if (i2c_index == 0x80)  /* g80+ */
                i2c_index = dcb->i2c_default_indices & 0xf;
+       else
+       if (i2c_index == 0x81)
+               i2c_index = (dcb->i2c_default_indices & 0xf0) >> 4;
+
+       if (i2c_index >= DCB_MAX_NUM_I2C_ENTRIES) {
+               NV_ERROR(dev, "invalid i2c_index 0x%x\n", i2c_index);
+               return NULL;
+       }
+
+       /* Make sure i2c table entry has been parsed, it may not
+        * have been if this is a bus not referenced by a DCB encoder
+        */
+       read_dcb_i2c_entry(dev, dcb->version, dcb->i2c_table,
+                          i2c_index, &dcb->i2c[i2c_index]);
 
        return nouveau_i2c_find(dev, i2c_index);
 }
@@ -818,7 +935,7 @@ init_io_restrict_prog(struct nvbios *bios, uint16_t offset,
                NV_ERROR(bios->dev,
                         "0x%04X: Config 0x%02X exceeds maximal bound 0x%02X\n",
                         offset, config, count);
-               return 0;
+               return len;
        }
 
        configval = ROM32(bios->data[offset + 11 + config * 4]);
@@ -920,7 +1037,7 @@ init_io_restrict_pll(struct nvbios *bios, uint16_t offset,
                NV_ERROR(bios->dev,
                         "0x%04X: Config 0x%02X exceeds maximal bound 0x%02X\n",
                         offset, config, count);
-               return 0;
+               return len;
        }
 
        freq = ROM16(bios->data[offset + 12 + config * 2]);
@@ -1066,6 +1183,130 @@ init_io_flag_condition(struct nvbios *bios, uint16_t offset,
        return 2;
 }
 
+static int
+init_dp_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+       /*
+        * INIT_DP_CONDITION   opcode: 0x3A ('')
+        *
+        * offset      (8 bit): opcode
+        * offset + 1  (8 bit): "sub" opcode
+        * offset + 2  (8 bit): unknown
+        *
+        */
+
+       struct bit_displayport_encoder_table *dpe = NULL;
+       struct dcb_entry *dcb = bios->display.output;
+       struct drm_device *dev = bios->dev;
+       uint8_t cond = bios->data[offset + 1];
+       int dummy;
+
+       BIOSLOG(bios, "0x%04X: subop 0x%02X\n", offset, cond);
+
+       if (!iexec->execute)
+               return 3;
+
+       dpe = nouveau_bios_dp_table(dev, dcb, &dummy);
+       if (!dpe) {
+               NV_ERROR(dev, "0x%04X: INIT_3A: no encoder table!!\n", offset);
+               return 3;
+       }
+
+       switch (cond) {
+       case 0:
+       {
+               struct dcb_connector_table_entry *ent =
+                       &bios->dcb.connector.entry[dcb->connector];
+
+               if (ent->type != DCB_CONNECTOR_eDP)
+                       iexec->execute = false;
+       }
+               break;
+       case 1:
+       case 2:
+               if (!(dpe->unknown & cond))
+                       iexec->execute = false;
+               break;
+       case 5:
+       {
+               struct nouveau_i2c_chan *auxch;
+               int ret;
+
+               auxch = nouveau_i2c_find(dev, bios->display.output->i2c_index);
+               if (!auxch) {
+                       NV_ERROR(dev, "0x%04X: couldn't get auxch\n", offset);
+                       return 3;
+               }
+
+               ret = nouveau_dp_auxch(auxch, 9, 0xd, &cond, 1);
+               if (ret) {
+                       NV_ERROR(dev, "0x%04X: auxch rd fail: %d\n", offset, ret);
+                       return 3;
+               }
+
+               if (cond & 1)
+                       iexec->execute = false;
+       }
+               break;
+       default:
+               NV_WARN(dev, "0x%04X: unknown INIT_3A op: %d\n", offset, cond);
+               break;
+       }
+
+       if (iexec->execute)
+               BIOSLOG(bios, "0x%04X: continuing to execute\n", offset);
+       else
+               BIOSLOG(bios, "0x%04X: skipping following commands\n", offset);
+
+       return 3;
+}
+
+static int
+init_op_3b(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+       /*
+        * INIT_3B   opcode: 0x3B ('')
+        *
+        * offset      (8 bit): opcode
+        * offset + 1  (8 bit): crtc index
+        *
+        */
+
+       uint8_t or = ffs(bios->display.output->or) - 1;
+       uint8_t index = bios->data[offset + 1];
+       uint8_t data;
+
+       if (!iexec->execute)
+               return 2;
+
+       data = bios_idxprt_rd(bios, 0x3d4, index);
+       bios_idxprt_wr(bios, 0x3d4, index, data & ~(1 << or));
+       return 2;
+}
+
+static int
+init_op_3c(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
+{
+       /*
+        * INIT_3C   opcode: 0x3C ('')
+        *
+        * offset      (8 bit): opcode
+        * offset + 1  (8 bit): crtc index
+        *
+        */
+
+       uint8_t or = ffs(bios->display.output->or) - 1;
+       uint8_t index = bios->data[offset + 1];
+       uint8_t data;
+
+       if (!iexec->execute)
+               return 2;
+
+       data = bios_idxprt_rd(bios, 0x3d4, index);
+       bios_idxprt_wr(bios, 0x3d4, index, data | (1 << or));
+       return 2;
+}
+
 static int
 init_idx_addr_latched(struct nvbios *bios, uint16_t offset,
                      struct init_exec *iexec)
@@ -1170,7 +1411,7 @@ init_io_restrict_pll2(struct nvbios *bios, uint16_t offset,
                NV_ERROR(bios->dev,
                         "0x%04X: Config 0x%02X exceeds maximal bound 0x%02X\n",
                         offset, config, count);
-               return 0;
+               return len;
        }
 
        freq = ROM32(bios->data[offset + 11 + config * 4]);
@@ -1230,13 +1471,13 @@ init_i2c_byte(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
         * "mask n" and OR it with "data n" before writing it back to the device
         */
 
+       struct drm_device *dev = bios->dev;
        uint8_t i2c_index = bios->data[offset + 1];
-       uint8_t i2c_address = bios->data[offset + 2];
+       uint8_t i2c_address = bios->data[offset + 2] >> 1;
        uint8_t count = bios->data[offset + 3];
-       int len = 4 + count * 3;
        struct nouveau_i2c_chan *chan;
-       struct i2c_msg msg;
-       int i;
+       int len = 4 + count * 3;
+       int ret, i;
 
        if (!iexec->execute)
                return len;
@@ -1245,36 +1486,41 @@ init_i2c_byte(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
                      "Count: 0x%02X\n",
                offset, i2c_index, i2c_address, count);
 
-       chan = init_i2c_device_find(bios->dev, i2c_index);
-       if (!chan)
-               return 0;
+       chan = init_i2c_device_find(dev, i2c_index);
+       if (!chan) {
+               NV_ERROR(dev, "0x%04X: i2c bus not found\n", offset);
+               return len;
+       }
 
        for (i = 0; i < count; i++) {
-               uint8_t i2c_reg = bios->data[offset + 4 + i * 3];
+               uint8_t reg = bios->data[offset + 4 + i * 3];
                uint8_t mask = bios->data[offset + 5 + i * 3];
                uint8_t data = bios->data[offset + 6 + i * 3];
-               uint8_t value;
+               union i2c_smbus_data val;
 
-               msg.addr = i2c_address;
-               msg.flags = I2C_M_RD;
-               msg.len = 1;
-               msg.buf = &value;
-               if (i2c_transfer(&chan->adapter, &msg, 1) != 1)
-                       return 0;
+               ret = i2c_smbus_xfer(&chan->adapter, i2c_address, 0,
+                                    I2C_SMBUS_READ, reg,
+                                    I2C_SMBUS_BYTE_DATA, &val);
+               if (ret < 0) {
+                       NV_ERROR(dev, "0x%04X: i2c rd fail: %d\n", offset, ret);
+                       return len;
+               }
 
                BIOSLOG(bios, "0x%04X: I2CReg: 0x%02X, Value: 0x%02X, "
                              "Mask: 0x%02X, Data: 0x%02X\n",
-                       offset, i2c_reg, value, mask, data);
+                       offset, reg, val.byte, mask, data);
 
-               value = (value & mask) | data;
+               if (!bios->execute)
+                       continue;
 
-               if (bios->execute) {
-                       msg.addr = i2c_address;
-                       msg.flags = 0;
-                       msg.len = 1;
-                       msg.buf = &value;
-                       if (i2c_transfer(&chan->adapter, &msg, 1) != 1)
-                               return 0;
+               val.byte &= mask;
+               val.byte |= data;
+               ret = i2c_smbus_xfer(&chan->adapter, i2c_address, 0,
+                                    I2C_SMBUS_WRITE, reg,
+                                    I2C_SMBUS_BYTE_DATA, &val);
+               if (ret < 0) {
+                       NV_ERROR(dev, "0x%04X: i2c wr fail: %d\n", offset, ret);
+                       return len;
                }
        }
 
@@ -1300,13 +1546,13 @@ init_zm_i2c_byte(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
         * "DCB I2C table entry index", set the register to "data n"
         */
 
+       struct drm_device *dev = bios->dev;
        uint8_t i2c_index = bios->data[offset + 1];
-       uint8_t i2c_address = bios->data[offset + 2];
+       uint8_t i2c_address = bios->data[offset + 2] >> 1;
        uint8_t count = bios->data[offset + 3];
-       int len = 4 + count * 2;
        struct nouveau_i2c_chan *chan;
-       struct i2c_msg msg;
-       int i;
+       int len = 4 + count * 2;
+       int ret, i;
 
        if (!iexec->execute)
                return len;
@@ -1315,24 +1561,30 @@ init_zm_i2c_byte(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
                      "Count: 0x%02X\n",
                offset, i2c_index, i2c_address, count);
 
-       chan = init_i2c_device_find(bios->dev, i2c_index);
-       if (!chan)
-               return 0;
+       chan = init_i2c_device_find(dev, i2c_index);
+       if (!chan) {
+               NV_ERROR(dev, "0x%04X: i2c bus not found\n", offset);
+               return len;
+       }
 
        for (i = 0; i < count; i++) {
-               uint8_t i2c_reg = bios->data[offset + 4 + i * 2];
-               uint8_t data = bios->data[offset + 5 + i * 2];
+               uint8_t reg = bios->data[offset + 4 + i * 2];
+               union i2c_smbus_data val;
+
+               val.byte = bios->data[offset + 5 + i * 2];
 
                BIOSLOG(bios, "0x%04X: I2CReg: 0x%02X, Data: 0x%02X\n",
-                       offset, i2c_reg, data);
-
-               if (bios->execute) {
-                       msg.addr = i2c_address;
-                       msg.flags = 0;
-                       msg.len = 1;
-                       msg.buf = &data;
-                       if (i2c_transfer(&chan->adapter, &msg, 1) != 1)
-                               return 0;
+                       offset, reg, val.byte);
+
+               if (!bios->execute)
+                       continue;
+
+               ret = i2c_smbus_xfer(&chan->adapter, i2c_address, 0,
+                                    I2C_SMBUS_WRITE, reg,
+                                    I2C_SMBUS_BYTE_DATA, &val);
+               if (ret < 0) {
+                       NV_ERROR(dev, "0x%04X: i2c wr fail: %d\n", offset, ret);
+                       return len;
                }
        }
 
@@ -1356,14 +1608,15 @@ init_zm_i2c(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
         * address" on the I2C bus given by "DCB I2C table entry index"
         */
 
+       struct drm_device *dev = bios->dev;
        uint8_t i2c_index = bios->data[offset + 1];
-       uint8_t i2c_address = bios->data[offset + 2];
+       uint8_t i2c_address = bios->data[offset + 2] >> 1;
        uint8_t count = bios->data[offset + 3];
        int len = 4 + count;
        struct nouveau_i2c_chan *chan;
        struct i2c_msg msg;
        uint8_t data[256];
-       int i;
+       int ret, i;
 
        if (!iexec->execute)
                return len;
@@ -1372,9 +1625,11 @@ init_zm_i2c(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
                      "Count: 0x%02X\n",
                offset, i2c_index, i2c_address, count);
 
-       chan = init_i2c_device_find(bios->dev, i2c_index);
-       if (!chan)
-               return 0;
+       chan = init_i2c_device_find(dev, i2c_index);
+       if (!chan) {
+               NV_ERROR(dev, "0x%04X: i2c bus not found\n", offset);
+               return len;
+       }
 
        for (i = 0; i < count; i++) {
                data[i] = bios->data[offset + 4 + i];
@@ -1387,8 +1642,11 @@ init_zm_i2c(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
                msg.flags = 0;
                msg.len = count;
                msg.buf = data;
-               if (i2c_transfer(&chan->adapter, &msg, 1) != 1)
-                       return 0;
+               ret = i2c_transfer(&chan->adapter, &msg, 1);
+               if (ret != 1) {
+                       NV_ERROR(dev, "0x%04X: i2c wr fail: %d\n", offset, ret);
+                       return len;
+               }
        }
 
        return len;
@@ -1412,6 +1670,7 @@ init_tmds(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
         * used -- see get_tmds_index_reg()
         */
 
+       struct drm_device *dev = bios->dev;
        uint8_t mlv = bios->data[offset + 1];
        uint32_t tmdsaddr = bios->data[offset + 2];
        uint8_t mask = bios->data[offset + 3];
@@ -1426,8 +1685,10 @@ init_tmds(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
                offset, mlv, tmdsaddr, mask, data);
 
        reg = get_tmds_index_reg(bios->dev, mlv);
-       if (!reg)
-               return 0;
+       if (!reg) {
+               NV_ERROR(dev, "0x%04X: no tmds_index_reg\n", offset);
+               return 5;
+       }
 
        bios_wr32(bios, reg,
                  tmdsaddr | NV_PRAMDAC_FP_TMDS_CONTROL_WRITE_DISABLE);
@@ -1457,6 +1718,7 @@ init_zm_tmds_group(struct nvbios *bios, uint16_t offset,
         * register is used -- see get_tmds_index_reg()
         */
 
+       struct drm_device *dev = bios->dev;
        uint8_t mlv = bios->data[offset + 1];
        uint8_t count = bios->data[offset + 2];
        int len = 3 + count * 2;
@@ -1470,8 +1732,10 @@ init_zm_tmds_group(struct nvbios *bios, uint16_t offset,
                offset, mlv, count);
 
        reg = get_tmds_index_reg(bios->dev, mlv);
-       if (!reg)
-               return 0;
+       if (!reg) {
+               NV_ERROR(dev, "0x%04X: no tmds_index_reg\n", offset);
+               return len;
+       }
 
        for (i = 0; i < count; i++) {
                uint8_t tmdsaddr = bios->data[offset + 3 + i * 2];
@@ -1910,7 +2174,8 @@ init_reset(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
        /* no iexec->execute check by design */
 
        pci_nv_19 = bios_rd32(bios, NV_PBUS_PCI_NV_19);
-       bios_wr32(bios, NV_PBUS_PCI_NV_19, 0);
+       bios_wr32(bios, NV_PBUS_PCI_NV_19, pci_nv_19 & ~0xf00);
+
        bios_wr32(bios, reg, value1);
 
        udelay(10);
@@ -1946,7 +2211,7 @@ init_configure_mem(struct nvbios *bios, uint16_t offset,
        uint32_t reg, data;
 
        if (bios->major_version > 2)
-               return 0;
+               return -ENODEV;
 
        bios_idxprt_wr(bios, NV_VIO_SRX, NV_VIO_SR_CLOCK_INDEX, bios_idxprt_rd(
                       bios, NV_VIO_SRX, NV_VIO_SR_CLOCK_INDEX) | 0x20);
@@ -2001,7 +2266,7 @@ init_configure_clk(struct nvbios *bios, uint16_t offset,
        int clock;
 
        if (bios->major_version > 2)
-               return 0;
+               return -ENODEV;
 
        clock = ROM16(bios->data[meminitoffs + 4]) * 10;
        setPLL(bios, NV_PRAMDAC_NVPLL_COEFF, clock);
@@ -2034,7 +2299,7 @@ init_configure_preinit(struct nvbios *bios, uint16_t offset,
        uint8_t cr3c = ((straps << 2) & 0xf0) | (straps & (1 << 6));
 
        if (bios->major_version > 2)
-               return 0;
+               return -ENODEV;
 
        bios_idxprt_wr(bios, NV_CIO_CRX__COLOR,
                             NV_CIO_CRE_SCRATCH4__INDEX, cr3c);
@@ -2579,7 +2844,7 @@ init_gpio(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
 
        if (dev_priv->card_type != NV_50) {
                NV_ERROR(bios->dev, "INIT_GPIO on unsupported chipset\n");
-               return -ENODEV;
+               return 1;
        }
 
        if (!iexec->execute)
@@ -2591,7 +2856,10 @@ init_gpio(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
 
                BIOSLOG(bios, "0x%04X: Entry: 0x%08X\n", offset, gpio->entry);
 
-               nv50_gpio_set(bios->dev, gpio->tag, gpio->state_default);
+               BIOSLOG(bios, "0x%04X: set gpio 0x%02x, state %d\n",
+                       offset, gpio->tag, gpio->state_default);
+               if (bios->execute)
+                       nv50_gpio_set(bios->dev, gpio->tag, gpio->state_default);
 
                /* The NVIDIA binary driver doesn't appear to actually do
                 * any of this, my VBIOS does however.
@@ -2648,17 +2916,17 @@ init_ram_restrict_zm_reg_group(struct nvbios *bios, uint16_t offset,
        uint8_t index;
        int i;
 
-
-       if (!iexec->execute)
-               return len;
-
+       /* critical! to know the length of the opcode */;
        if (!blocklen) {
                NV_ERROR(bios->dev,
                         "0x%04X: Zero block length - has the M table "
                         "been parsed?\n", offset);
-               return 0;
+               return -EINVAL;
        }
 
+       if (!iexec->execute)
+               return len;
+
        strap_ramcfg = (bios_rd32(bios, NV_PEXTDEV_BOOT_0) >> 2) & 0xf;
        index = bios->data[bios->ram_restrict_tbl_ptr + strap_ramcfg];
 
@@ -2840,14 +3108,14 @@ init_auxch(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
 
        if (!bios->display.output) {
                NV_ERROR(dev, "INIT_AUXCH: no active output\n");
-               return 0;
+               return len;
        }
 
        auxch = init_i2c_device_find(dev, bios->display.output->i2c_index);
        if (!auxch) {
                NV_ERROR(dev, "INIT_AUXCH: couldn't get auxch %d\n",
                         bios->display.output->i2c_index);
-               return 0;
+               return len;
        }
 
        if (!iexec->execute)
@@ -2860,7 +3128,7 @@ init_auxch(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
                ret = nouveau_dp_auxch(auxch, 9, addr, &data, 1);
                if (ret) {
                        NV_ERROR(dev, "INIT_AUXCH: rd auxch fail %d\n", ret);
-                       return 0;
+                       return len;
                }
 
                data &= bios->data[offset + 0];
@@ -2869,7 +3137,7 @@ init_auxch(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
                ret = nouveau_dp_auxch(auxch, 8, addr, &data, 1);
                if (ret) {
                        NV_ERROR(dev, "INIT_AUXCH: wr auxch fail %d\n", ret);
-                       return 0;
+                       return len;
                }
        }
 
@@ -2899,14 +3167,14 @@ init_zm_auxch(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
 
        if (!bios->display.output) {
                NV_ERROR(dev, "INIT_ZM_AUXCH: no active output\n");
-               return 0;
+               return len;
        }
 
        auxch = init_i2c_device_find(dev, bios->display.output->i2c_index);
        if (!auxch) {
                NV_ERROR(dev, "INIT_ZM_AUXCH: couldn't get auxch %d\n",
                         bios->display.output->i2c_index);
-               return 0;
+               return len;
        }
 
        if (!iexec->execute)
@@ -2917,7 +3185,7 @@ init_zm_auxch(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
                ret = nouveau_dp_auxch(auxch, 8, addr, &bios->data[offset], 1);
                if (ret) {
                        NV_ERROR(dev, "INIT_ZM_AUXCH: wr auxch fail %d\n", ret);
-                       return 0;
+                       return len;
                }
        }
 
@@ -2934,6 +3202,9 @@ static struct init_tbl_entry itbl_entry[] = {
        { "INIT_COPY"                         , 0x37, init_copy                       },
        { "INIT_NOT"                          , 0x38, init_not                        },
        { "INIT_IO_FLAG_CONDITION"            , 0x39, init_io_flag_condition          },
+       { "INIT_DP_CONDITION"                 , 0x3A, init_dp_condition               },
+       { "INIT_OP_3B"                        , 0x3B, init_op_3b                      },
+       { "INIT_OP_3C"                        , 0x3C, init_op_3c                      },
        { "INIT_INDEX_ADDRESS_LATCHED"        , 0x49, init_idx_addr_latched           },
        { "INIT_IO_RESTRICT_PLL2"             , 0x4A, init_io_restrict_pll2           },
        { "INIT_PLL2"                         , 0x4B, init_pll2                       },
@@ -3001,7 +3272,7 @@ parse_init_table(struct nvbios *bios, unsigned int offset,
         * is changed back to EXECUTE.
         */
 
-       int count = 0, i, res;
+       int count = 0, i, ret;
        uint8_t id;
 
        /*
@@ -3016,26 +3287,33 @@ parse_init_table(struct nvbios *bios, unsigned int offset,
                for (i = 0; itbl_entry[i].name && (itbl_entry[i].id != id); i++)
                        ;
 
-               if (itbl_entry[i].name) {
-                       BIOSLOG(bios, "0x%04X: [ (0x%02X) - %s ]\n",
-                               offset, itbl_entry[i].id, itbl_entry[i].name);
-
-                       /* execute eventual command handler */
-                       res = (*itbl_entry[i].handler)(bios, offset, iexec);
-                       if (!res)
-                               break;
-                       /*
-                        * Add the offset of the current command including all data
-                        * of that command. The offset will then be pointing on the
-                        * next op code.
-                        */
-                       offset += res;
-               } else {
+               if (!itbl_entry[i].name) {
                        NV_ERROR(bios->dev,
                                 "0x%04X: Init table command not found: "
                                 "0x%02X\n", offset, id);
                        return -ENOENT;
                }
+
+               BIOSLOG(bios, "0x%04X: [ (0x%02X) - %s ]\n", offset,
+                       itbl_entry[i].id, itbl_entry[i].name);
+
+               /* execute eventual command handler */
+               ret = (*itbl_entry[i].handler)(bios, offset, iexec);
+               if (ret < 0) {
+                       NV_ERROR(bios->dev, "0x%04X: Failed parsing init "
+                                "table opcode: %s %d\n", offset,
+                                itbl_entry[i].name, ret);
+               }
+
+               if (ret <= 0)
+                       break;
+
+               /*
+                * Add the offset of the current command including all data
+                * of that command. The offset will then be pointing on the
+                * next op code.
+                */
+               offset += ret;
        }
 
        if (offset >= bios->length)
@@ -3671,7 +3949,8 @@ int nouveau_bios_parse_lvds_table(struct drm_device *dev, int pxclk, bool *dl, b
 
 static uint8_t *
 bios_output_config_match(struct drm_device *dev, struct dcb_entry *dcbent,
-                        uint16_t record, int record_len, int record_nr)
+                        uint16_t record, int record_len, int record_nr,
+                        bool match_link)
 {
        struct drm_nouveau_private *dev_priv = dev->dev_private;
        struct nvbios *bios = &dev_priv->vbios;
@@ -3679,12 +3958,28 @@ bios_output_config_match(struct drm_device *dev, struct dcb_entry *dcbent,
        uint16_t table;
        int i, v;
 
+       switch (dcbent->type) {
+       case OUTPUT_TMDS:
+       case OUTPUT_LVDS:
+       case OUTPUT_DP:
+               break;
+       default:
+               match_link = false;
+               break;
+       }
+
        for (i = 0; i < record_nr; i++, record += record_len) {
                table = ROM16(bios->data[record]);
                if (!table)
                        continue;
                entry = ROM32(bios->data[table]);
 
+               if (match_link) {
+                       v = (entry & 0x00c00000) >> 22;
+                       if (!(v & dcbent->sorconf.link))
+                               continue;
+               }
+
                v = (entry & 0x000f0000) >> 16;
                if (!(v & dcbent->or))
                        continue;
@@ -3726,7 +4021,7 @@ nouveau_bios_dp_table(struct drm_device *dev, struct dcb_entry *dcbent,
        *length = table[4];
        return bios_output_config_match(dev, dcbent,
                                        bios->display.dp_table_ptr + table[1],
-                                       table[2], table[3]);
+                                       table[2], table[3], table[0] >= 0x21);
 }
 
 int
@@ -3815,7 +4110,7 @@ nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent,
                        dcbent->type, dcbent->location, dcbent->or);
        otable = bios_output_config_match(dev, dcbent, table[1] +
                                          bios->display.script_table_ptr,
-                                         table[2], table[3]);
+                                         table[2], table[3], table[0] >= 0x21);
        if (!otable) {
                NV_ERROR(dev, "Couldn't find matching output script table\n");
                return 1;
@@ -4285,31 +4580,32 @@ int get_pll_limits(struct drm_device *dev, uint32_t limit_match, struct pll_lims
                        break;
                }
 
-#if 0 /* for easy debugging */
-       ErrorF("pll.vco1.minfreq: %d\n", pll_lim->vco1.minfreq);
-       ErrorF("pll.vco1.maxfreq: %d\n", pll_lim->vco1.maxfreq);
-       ErrorF("pll.vco2.minfreq: %d\n", pll_lim->vco2.minfreq);
-       ErrorF("pll.vco2.maxfreq: %d\n", pll_lim->vco2.maxfreq);
-
-       ErrorF("pll.vco1.min_inputfreq: %d\n", pll_lim->vco1.min_inputfreq);
-       ErrorF("pll.vco1.max_inputfreq: %d\n", pll_lim->vco1.max_inputfreq);
-       ErrorF("pll.vco2.min_inputfreq: %d\n", pll_lim->vco2.min_inputfreq);
-       ErrorF("pll.vco2.max_inputfreq: %d\n", pll_lim->vco2.max_inputfreq);
-
-       ErrorF("pll.vco1.min_n: %d\n", pll_lim->vco1.min_n);
-       ErrorF("pll.vco1.max_n: %d\n", pll_lim->vco1.max_n);
-       ErrorF("pll.vco1.min_m: %d\n", pll_lim->vco1.min_m);
-       ErrorF("pll.vco1.max_m: %d\n", pll_lim->vco1.max_m);
-       ErrorF("pll.vco2.min_n: %d\n", pll_lim->vco2.min_n);
-       ErrorF("pll.vco2.max_n: %d\n", pll_lim->vco2.max_n);
-       ErrorF("pll.vco2.min_m: %d\n", pll_lim->vco2.min_m);
-       ErrorF("pll.vco2.max_m: %d\n", pll_lim->vco2.max_m);
-
-       ErrorF("pll.max_log2p: %d\n", pll_lim->max_log2p);
-       ErrorF("pll.log2p_bias: %d\n", pll_lim->log2p_bias);
-
-       ErrorF("pll.refclk: %d\n", pll_lim->refclk);
-#endif
+       NV_DEBUG(dev, "pll.vco1.minfreq: %d\n", pll_lim->vco1.minfreq);
+       NV_DEBUG(dev, "pll.vco1.maxfreq: %d\n", pll_lim->vco1.maxfreq);
+       NV_DEBUG(dev, "pll.vco1.min_inputfreq: %d\n", pll_lim->vco1.min_inputfreq);
+       NV_DEBUG(dev, "pll.vco1.max_inputfreq: %d\n", pll_lim->vco1.max_inputfreq);
+       NV_DEBUG(dev, "pll.vco1.min_n: %d\n", pll_lim->vco1.min_n);
+       NV_DEBUG(dev, "pll.vco1.max_n: %d\n", pll_lim->vco1.max_n);
+       NV_DEBUG(dev, "pll.vco1.min_m: %d\n", pll_lim->vco1.min_m);
+       NV_DEBUG(dev, "pll.vco1.max_m: %d\n", pll_lim->vco1.max_m);
+       if (pll_lim->vco2.maxfreq) {
+               NV_DEBUG(dev, "pll.vco2.minfreq: %d\n", pll_lim->vco2.minfreq);
+               NV_DEBUG(dev, "pll.vco2.maxfreq: %d\n", pll_lim->vco2.maxfreq);
+               NV_DEBUG(dev, "pll.vco2.min_inputfreq: %d\n", pll_lim->vco2.min_inputfreq);
+               NV_DEBUG(dev, "pll.vco2.max_inputfreq: %d\n", pll_lim->vco2.max_inputfreq);
+               NV_DEBUG(dev, "pll.vco2.min_n: %d\n", pll_lim->vco2.min_n);
+               NV_DEBUG(dev, "pll.vco2.max_n: %d\n", pll_lim->vco2.max_n);
+               NV_DEBUG(dev, "pll.vco2.min_m: %d\n", pll_lim->vco2.min_m);
+               NV_DEBUG(dev, "pll.vco2.max_m: %d\n", pll_lim->vco2.max_m);
+       }
+       if (!pll_lim->max_p) {
+               NV_DEBUG(dev, "pll.max_log2p: %d\n", pll_lim->max_log2p);
+               NV_DEBUG(dev, "pll.log2p_bias: %d\n", pll_lim->log2p_bias);
+       } else {
+               NV_DEBUG(dev, "pll.min_p: %d\n", pll_lim->min_p);
+               NV_DEBUG(dev, "pll.max_p: %d\n", pll_lim->max_p);
+       }
+       NV_DEBUG(dev, "pll.refclk: %d\n", pll_lim->refclk);
 
        return 0;
 }
@@ -4899,10 +5195,14 @@ static int parse_bmp_structure(struct drm_device *dev, struct nvbios *bios, unsi
        bios->legacy.i2c_indices.crt = bios->data[legacy_i2c_offset];
        bios->legacy.i2c_indices.tv = bios->data[legacy_i2c_offset + 1];
        bios->legacy.i2c_indices.panel = bios->data[legacy_i2c_offset + 2];
-       bios->dcb.i2c[0].write = bios->data[legacy_i2c_offset + 4];
-       bios->dcb.i2c[0].read = bios->data[legacy_i2c_offset + 5];
-       bios->dcb.i2c[1].write = bios->data[legacy_i2c_offset + 6];
-       bios->dcb.i2c[1].read = bios->data[legacy_i2c_offset + 7];
+       if (bios->data[legacy_i2c_offset + 4])
+               bios->dcb.i2c[0].write = bios->data[legacy_i2c_offset + 4];
+       if (bios->data[legacy_i2c_offset + 5])
+               bios->dcb.i2c[0].read = bios->data[legacy_i2c_offset + 5];
+       if (bios->data[legacy_i2c_offset + 6])
+               bios->dcb.i2c[1].write = bios->data[legacy_i2c_offset + 6];
+       if (bios->data[legacy_i2c_offset + 7])
+               bios->dcb.i2c[1].read = bios->data[legacy_i2c_offset + 7];
 
        if (bmplength > 74) {
                bios->fmaxvco = ROM32(bmp[67]);
@@ -4953,79 +5253,6 @@ static uint16_t findstr(uint8_t *data, int n, const uint8_t *str, int len)
        return 0;
 }
 
-static int
-read_dcb_i2c_entry(struct drm_device *dev, int dcb_version, uint8_t *i2ctable, int index, struct dcb_i2c_entry *i2c)
-{
-       uint8_t dcb_i2c_ver = dcb_version, headerlen = 0, entry_len = 4;
-       int i2c_entries = DCB_MAX_NUM_I2C_ENTRIES;
-       int recordoffset = 0, rdofs = 1, wrofs = 0;
-       uint8_t port_type = 0;
-
-       if (!i2ctable)
-               return -EINVAL;
-
-       if (dcb_version >= 0x30) {
-               if (i2ctable[0] != dcb_version) /* necessary? */
-                       NV_WARN(dev,
-                               "DCB I2C table version mismatch (%02X vs %02X)\n",
-                               i2ctable[0], dcb_version);
-               dcb_i2c_ver = i2ctable[0];
-               headerlen = i2ctable[1];
-               if (i2ctable[2] <= DCB_MAX_NUM_I2C_ENTRIES)
-                       i2c_entries = i2ctable[2];
-               else
-                       NV_WARN(dev,
-                               "DCB I2C table has more entries than indexable "
-                               "(%d entries, max %d)\n", i2ctable[2],
-                               DCB_MAX_NUM_I2C_ENTRIES);
-               entry_len = i2ctable[3];
-               /* [4] is i2c_default_indices, read in parse_dcb_table() */
-       }
-       /*
-        * It's your own fault if you call this function on a DCB 1.1 BIOS --
-        * the test below is for DCB 1.2
-        */
-       if (dcb_version < 0x14) {
-               recordoffset = 2;
-               rdofs = 0;
-               wrofs = 1;
-       }
-
-       if (index == 0xf)
-               return 0;
-       if (index >= i2c_entries) {
-               NV_ERROR(dev, "DCB I2C index too big (%d >= %d)\n",
-                        index, i2ctable[2]);
-               return -ENOENT;
-       }
-       if (i2ctable[headerlen + entry_len * index + 3] == 0xff) {
-               NV_ERROR(dev, "DCB I2C entry invalid\n");
-               return -EINVAL;
-       }
-
-       if (dcb_i2c_ver >= 0x30) {
-               port_type = i2ctable[headerlen + recordoffset + 3 + entry_len * index];
-
-               /*
-                * Fixup for chips using same address offset for read and
-                * write.
-                */
-               if (port_type == 4)     /* seen on C51 */
-                       rdofs = wrofs = 1;
-               if (port_type >= 5)     /* G80+ */
-                       rdofs = wrofs = 0;
-       }
-
-       if (dcb_i2c_ver >= 0x40 && port_type != 5 && port_type != 6)
-               NV_WARN(dev, "DCB I2C table has port type %d\n", port_type);
-
-       i2c->port_type = port_type;
-       i2c->read = i2ctable[headerlen + recordoffset + rdofs + entry_len * index];
-       i2c->write = i2ctable[headerlen + recordoffset + wrofs + entry_len * index];
-
-       return 0;
-}
-
 static struct dcb_gpio_entry *
 new_gpio_entry(struct nvbios *bios)
 {
@@ -5379,12 +5606,6 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb,
        entry->bus = (conn >> 16) & 0xf;
        entry->location = (conn >> 20) & 0x3;
        entry->or = (conn >> 24) & 0xf;
-       /*
-        * Normal entries consist of a single bit, but dual link has the
-        * next most significant bit set too
-        */
-       entry->duallink_possible =
-                       ((1 << (ffs(entry->or) - 1)) * 3 == entry->or);
 
        switch (entry->type) {
        case OUTPUT_ANALOG:
@@ -5468,6 +5689,16 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb,
                break;
        }
 
+       if (dcb->version < 0x40) {
+               /* Normal entries consist of a single bit, but dual link has
+                * the next most significant bit set too
+                */
+               entry->duallink_possible =
+                       ((1 << (ffs(entry->or) - 1)) * 3 == entry->or);
+       } else {
+               entry->duallink_possible = (entry->sorconf.link == 3);
+       }
+
        /* unsure what DCB version introduces this, 3.0? */
        if (conf & 0x100000)
                entry->i2c_upper_default = true;
@@ -5610,6 +5841,31 @@ void merge_like_dcb_entries(struct drm_device *dev, struct dcb_table *dcb)
        dcb->entries = newentries;
 }
 
+static bool
+apply_dcb_encoder_quirks(struct drm_device *dev, int idx, u32 *conn, u32 *conf)
+{
+       /* Dell Precision M6300
+        *   DCB entry 2: 02025312 00000010
+        *   DCB entry 3: 02026312 00000020
+        *
+        * Identical, except apparently a different connector on a
+        * different SOR link.  Not a clue how we're supposed to know
+        * which one is in use if it even shares an i2c line...
+        *
+        * Ignore the connector on the second SOR link to prevent
+        * nasty problems until this is sorted (assuming it's not a
+        * VBIOS bug).
+        */
+       if ((dev->pdev->device == 0x040d) &&
+           (dev->pdev->subsystem_vendor == 0x1028) &&
+           (dev->pdev->subsystem_device == 0x019b)) {
+               if (*conn == 0x02026312 && *conf == 0x00000020)
+                       return false;
+       }
+
+       return true;
+}
+
 static int
 parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool twoHeads)
 {
@@ -5743,6 +5999,9 @@ parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool twoHeads)
                if ((connection & 0x0000000f) == 0x0000000f)
                        continue;
 
+               if (!apply_dcb_encoder_quirks(dev, i, &connection, &config))
+                       continue;
+
                NV_TRACEWARN(dev, "Raw DCB entry %d: %08x %08x\n",
                             dcb->entries, connection, config);
 
@@ -6051,6 +6310,30 @@ nouveau_bios_i2c_devices_takedown(struct drm_device *dev)
                nouveau_i2c_fini(dev, entry);
 }
 
+static bool
+nouveau_bios_posted(struct drm_device *dev)
+{
+       struct drm_nouveau_private *dev_priv = dev->dev_private;
+       bool was_locked;
+       unsigned htotal;
+
+       if (dev_priv->chipset >= NV_50) {
+               if (NVReadVgaCrtc(dev, 0, 0x00) == 0 &&
+                   NVReadVgaCrtc(dev, 0, 0x1a) == 0)
+                       return false;
+               return true;
+       }
+
+       was_locked = NVLockVgaCrtcs(dev, false);
+       htotal  = NVReadVgaCrtc(dev, 0, 0x06);
+       htotal |= (NVReadVgaCrtc(dev, 0, 0x07) & 0x01) << 8;
+       htotal |= (NVReadVgaCrtc(dev, 0, 0x07) & 0x20) << 4;
+       htotal |= (NVReadVgaCrtc(dev, 0, 0x25) & 0x01) << 10;
+       htotal |= (NVReadVgaCrtc(dev, 0, 0x41) & 0x01) << 11;
+       NVLockVgaCrtcs(dev, was_locked);
+       return (htotal != 0);
+}
+
 int
 nouveau_bios_init(struct drm_device *dev)
 {
@@ -6085,11 +6368,9 @@ nouveau_bios_init(struct drm_device *dev)
        bios->execute = false;
 
        /* ... unless card isn't POSTed already */
-       if (dev_priv->card_type >= NV_10 &&
-           NVReadVgaCrtc(dev, 0, 0x00) == 0 &&
-           NVReadVgaCrtc(dev, 0, 0x1a) == 0) {
+       if (!nouveau_bios_posted(dev)) {
                NV_INFO(dev, "Adaptor not initialised\n");
-               if (dev_priv->card_type < NV_50) {
+               if (dev_priv->card_type < NV_40) {
                        NV_ERROR(dev, "Unable to POST this chipset\n");
                        return -ENODEV;
                }