drm/nv50: DCB quirk for Dell M6300
[cascardo/linux.git] / drivers / gpu / drm / nouveau / nouveau_bios.c
index 9ba2dea..3e274d5 100644 (file)
@@ -834,7 +834,7 @@ init_i2c_device_find(struct drm_device *dev, int i2c_index)
        if (i2c_index == 0x81)
                i2c_index = (dcb->i2c_default_indices & 0xf0) >> 4;
 
-       if (i2c_index > DCB_MAX_NUM_I2C_ENTRIES) {
+       if (i2c_index >= DCB_MAX_NUM_I2C_ENTRIES) {
                NV_ERROR(dev, "invalid i2c_index 0x%x\n", i2c_index);
                return NULL;
        }
@@ -935,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 -EINVAL;
+               return len;
        }
 
        configval = ROM32(bios->data[offset + 11 + config * 4]);
@@ -1037,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 -EINVAL;
+               return len;
        }
 
        freq = ROM16(bios->data[offset + 12 + config * 2]);
@@ -1209,7 +1209,7 @@ init_dp_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
        dpe = nouveau_bios_dp_table(dev, dcb, &dummy);
        if (!dpe) {
                NV_ERROR(dev, "0x%04X: INIT_3A: no encoder table!!\n", offset);
-               return -EINVAL;
+               return 3;
        }
 
        switch (cond) {
@@ -1233,12 +1233,16 @@ init_dp_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
                int ret;
 
                auxch = nouveau_i2c_find(dev, bios->display.output->i2c_index);
-               if (!auxch)
-                       return -ENODEV;
+               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)
-                       return ret;
+               if (ret) {
+                       NV_ERROR(dev, "0x%04X: auxch rd fail: %d\n", offset, ret);
+                       return 3;
+               }
 
                if (cond & 1)
                        iexec->execute = false;
@@ -1407,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 -EINVAL;
+               return len;
        }
 
        freq = ROM32(bios->data[offset + 11 + config * 4]);
@@ -1467,6 +1471,7 @@ 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] >> 1;
        uint8_t count = bios->data[offset + 3];
@@ -1481,9 +1486,11 @@ 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 -ENODEV;
+       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 reg = bios->data[offset + 4 + i * 3];
@@ -1494,8 +1501,10 @@ init_i2c_byte(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
                ret = i2c_smbus_xfer(&chan->adapter, i2c_address, 0,
                                     I2C_SMBUS_READ, reg,
                                     I2C_SMBUS_BYTE_DATA, &val);
-               if (ret < 0)
-                       return ret;
+               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",
@@ -1509,8 +1518,10 @@ init_i2c_byte(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
                ret = i2c_smbus_xfer(&chan->adapter, i2c_address, 0,
                                     I2C_SMBUS_WRITE, reg,
                                     I2C_SMBUS_BYTE_DATA, &val);
-               if (ret < 0)
-                       return ret;
+               if (ret < 0) {
+                       NV_ERROR(dev, "0x%04X: i2c wr fail: %d\n", offset, ret);
+                       return len;
+               }
        }
 
        return len;
@@ -1535,6 +1546,7 @@ 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] >> 1;
        uint8_t count = bios->data[offset + 3];
@@ -1549,9 +1561,11 @@ 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 -ENODEV;
+       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 reg = bios->data[offset + 4 + i * 2];
@@ -1568,8 +1582,10 @@ init_zm_i2c_byte(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
                ret = i2c_smbus_xfer(&chan->adapter, i2c_address, 0,
                                     I2C_SMBUS_WRITE, reg,
                                     I2C_SMBUS_BYTE_DATA, &val);
-               if (ret < 0)
-                       return ret;
+               if (ret < 0) {
+                       NV_ERROR(dev, "0x%04X: i2c wr fail: %d\n", offset, ret);
+                       return len;
+               }
        }
 
        return len;
@@ -1592,6 +1608,7 @@ 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] >> 1;
        uint8_t count = bios->data[offset + 3];
@@ -1599,7 +1616,7 @@ init_zm_i2c(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
        struct nouveau_i2c_chan *chan;
        struct i2c_msg msg;
        uint8_t data[256];
-       int i;
+       int ret, i;
 
        if (!iexec->execute)
                return len;
@@ -1608,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 -ENODEV;
+       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];
@@ -1623,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 -EIO;
+               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;
@@ -1648,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];
@@ -1662,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 -EINVAL;
+       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);
@@ -1693,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;
@@ -1706,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 -EINVAL;
+       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];
@@ -2146,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);
@@ -2815,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)
@@ -2887,10 +2916,7 @@ 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 "
@@ -2898,6 +2924,9 @@ init_ram_restrict_zm_reg_group(struct nvbios *bios, uint16_t offset,
                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];
 
@@ -3079,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 -EINVAL;
+               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 -ENODEV;
+               return len;
        }
 
        if (!iexec->execute)
@@ -3099,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 ret;
+                       return len;
                }
 
                data &= bios->data[offset + 0];
@@ -3108,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 ret;
+                       return len;
                }
        }
 
@@ -3138,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 -EINVAL;
+               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 -ENODEV;
+               return len;
        }
 
        if (!iexec->execute)
@@ -3156,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 ret;
+                       return len;
                }
        }
 
@@ -3920,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;
@@ -3928,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;
@@ -3975,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
@@ -4064,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;
@@ -5149,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]);
@@ -5791,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)
 {
@@ -5924,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);