From 708ec89f8a0ded52b4144399d3db470328269ec9 Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Nov 28 2011 02:10:12 +0000 Subject: nouveau: fix two instances of an oops in ttm clear() (rhbz#751753) --- diff --git a/drm-nouveau-updates.patch b/drm-nouveau-updates.patch index 1e7c927..cd9bded 100644 --- a/drm-nouveau-updates.patch +++ b/drm-nouveau-updates.patch @@ -781,10 +781,10 @@ index 890d50e..7226f41 100644 else page_shift = 12; diff --git a/drivers/gpu/drm/nouveau/nouveau_channel.c b/drivers/gpu/drm/nouveau/nouveau_channel.c -index b0d753f..a319d56 100644 +index 0e3241c..bb6ec9e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_channel.c +++ b/drivers/gpu/drm/nouveau/nouveau_channel.c -@@ -411,13 +411,17 @@ nouveau_ioctl_fifo_alloc(struct drm_device *dev, void *data, +@@ -412,13 +412,17 @@ nouveau_ioctl_fifo_alloc(struct drm_device *dev, void *data, return ret; init->channel = chan->id; @@ -960,12 +960,19 @@ diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouv index 7beb82a..de5efe7 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c -@@ -28,418 +28,619 @@ +@@ -28,557 +28,669 @@ #include "nouveau_i2c.h" #include "nouveau_connector.h" #include "nouveau_encoder.h" +#include "nouveau_crtc.h" -+ + +-static int +-auxch_rd(struct drm_encoder *encoder, int address, uint8_t *buf, int size) +-{ +- struct drm_device *dev = encoder->dev; +- struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); +- struct nouveau_i2c_chan *auxch; +- int ret; +/****************************************************************************** + * aux channel util functions + *****************************************************************************/ @@ -975,34 +982,38 @@ index 7beb82a..de5efe7 100644 + } \ +} while (0) +#define AUX_ERR(fmt, args...) NV_ERROR(dev, "AUXCH(%d): " fmt, ch, ##args) -+ + +- auxch = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index); +- if (!auxch) +- return -ENODEV; +- +- ret = nouveau_dp_auxch(auxch, 9, address, buf, size); +- if (ret) +- return ret; +- +- return 0; +static void +auxch_fini(struct drm_device *dev, int ch) +{ + nv_mask(dev, 0x00e4e4 + (ch * 0x50), 0x00310000, 0x00000000); -+} + } static int --auxch_rd(struct drm_encoder *encoder, int address, uint8_t *buf, int size) +-auxch_wr(struct drm_encoder *encoder, int address, uint8_t *buf, int size) +auxch_init(struct drm_device *dev, int ch) { - struct drm_device *dev = encoder->dev; - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nouveau_i2c_chan *auxch; - int ret; -- -- auxch = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index); -- if (!auxch) -- return -ENODEV; -- -- ret = nouveau_dp_auxch(auxch, 9, address, buf, size); -- if (ret) -- return ret; + const u32 unksel = 1; /* nfi which to use, or if it matters.. */ + const u32 ureq = unksel ? 0x00100000 : 0x00200000; + const u32 urep = unksel ? 0x01000000 : 0x02000000; + u32 ctrl, timeout; -+ + +- auxch = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index); +- if (!auxch) +- return -ENODEV; + /* wait up to 1ms for any previous transaction to be done... */ + timeout = 1000; + do { @@ -1013,7 +1024,9 @@ index 7beb82a..de5efe7 100644 + return -EBUSY; + } + } while (ctrl & 0x03010000); -+ + +- ret = nouveau_dp_auxch(auxch, 8, address, buf, size); +- return ret; + /* set some magic, and wait up to 1ms for it to appear */ + nv_mask(dev, 0x00e4e4 + (ch * 0x50), 0x00300000, ureq); + timeout = 1000; @@ -1026,52 +1039,44 @@ index 7beb82a..de5efe7 100644 + return -EBUSY; + } + } while ((ctrl & 0x03000000) != urep); - - return 0; ++ ++ return 0; } static int --auxch_wr(struct drm_encoder *encoder, int address, uint8_t *buf, int size) +-nouveau_dp_lane_count_set(struct drm_encoder *encoder, uint8_t cmd) +auxch_tx(struct drm_device *dev, int ch, u8 type, u32 addr, u8 *data, u8 size) { - struct drm_device *dev = encoder->dev; - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); -- struct nouveau_i2c_chan *auxch; -- int ret; +- uint32_t tmp; +- int or = nv_encoder->or, link = !(nv_encoder->dcb->sorconf.link & 1); + u32 ctrl, stat, timeout, retries; + u32 xbuf[4] = {}; + int ret, i; -- auxch = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index); -- if (!auxch) -- return -ENODEV; +- tmp = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)); +- tmp &= ~(NV50_SOR_DP_CTRL_ENHANCED_FRAME_ENABLED | +- NV50_SOR_DP_CTRL_LANE_MASK); +- tmp |= ((1 << (cmd & DP_LANE_COUNT_MASK)) - 1) << 16; +- if (cmd & DP_LANE_COUNT_ENHANCED_FRAME_EN) +- tmp |= NV50_SOR_DP_CTRL_ENHANCED_FRAME_ENABLED; +- nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), tmp); + AUX_DBG("%d: 0x%08x %d\n", type, addr, size); -- ret = nouveau_dp_auxch(auxch, 8, address, buf, size); -- return ret; +- return auxch_wr(encoder, DP_LANE_COUNT_SET, &cmd, 1); -} + ret = auxch_init(dev, ch); + if (ret) + goto out; -static int --nouveau_dp_lane_count_set(struct drm_encoder *encoder, uint8_t cmd) +-nouveau_dp_link_bw_set(struct drm_encoder *encoder, uint8_t cmd) -{ - struct drm_device *dev = encoder->dev; - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - uint32_t tmp; -- int or = nv_encoder->or, link = !(nv_encoder->dcb->sorconf.link & 1); -- -- tmp = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)); -- tmp &= ~(NV50_SOR_DP_CTRL_ENHANCED_FRAME_ENABLED | -- NV50_SOR_DP_CTRL_LANE_MASK); -- tmp |= ((1 << (cmd & DP_LANE_COUNT_MASK)) - 1) << 16; -- if (cmd & DP_LANE_COUNT_ENHANCED_FRAME_EN) -- tmp |= NV50_SOR_DP_CTRL_ENHANCED_FRAME_ENABLED; -- nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), tmp); -- -- return auxch_wr(encoder, DP_LANE_COUNT_SET, &cmd, 1); --} +- int reg = 0x614300 + (nv_encoder->or * 0x800); + stat = nv_rd32(dev, 0x00e4e8 + (ch * 0x50)); + if (!(stat & 0x10000000)) { + AUX_DBG("sink not detected\n"); @@ -1079,13 +1084,11 @@ index 7beb82a..de5efe7 100644 + goto out; + } --static int --nouveau_dp_link_bw_set(struct drm_encoder *encoder, uint8_t cmd) --{ -- struct drm_device *dev = encoder->dev; -- struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); -- uint32_t tmp; -- int reg = 0x614300 + (nv_encoder->or * 0x800); +- tmp = nv_rd32(dev, reg); +- tmp &= 0xfff3ffff; +- if (cmd == DP_LINK_BW_2_7) +- tmp |= 0x00040000; +- nv_wr32(dev, reg, tmp); + if (!(type & 1)) { + memcpy(xbuf, data, size); + for (i = 0; i < 16; i += 4) { @@ -1094,17 +1097,23 @@ index 7beb82a..de5efe7 100644 + } + } -- tmp = nv_rd32(dev, reg); -- tmp &= 0xfff3ffff; -- if (cmd == DP_LINK_BW_2_7) -- tmp |= 0x00040000; -- nv_wr32(dev, reg, tmp); +- return auxch_wr(encoder, DP_LINK_BW_SET, &cmd, 1); +-} + ctrl = nv_rd32(dev, 0x00e4e4 + (ch * 0x50)); + ctrl &= ~0x0001f0ff; + ctrl |= type << 12; + ctrl |= size - 1; + nv_wr32(dev, 0x00e4e0 + (ch * 0x50), addr); -+ + +-static int +-nouveau_dp_link_train_set(struct drm_encoder *encoder, int pattern) +-{ +- struct drm_device *dev = encoder->dev; +- struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); +- uint32_t tmp; +- uint8_t cmd; +- int or = nv_encoder->or, link = !(nv_encoder->dcb->sorconf.link & 1); +- int ret; + /* retry transaction a number of times on failure... */ + ret = -EREMOTEIO; + for (retries = 0; retries < 32; retries++) { @@ -1113,10 +1122,21 @@ index 7beb82a..de5efe7 100644 + nv_wr32(dev, 0x00e4e4 + (ch * 0x50), 0x00000000 | ctrl); + if (retries) + udelay(400); -+ + +- tmp = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)); +- tmp &= ~NV50_SOR_DP_CTRL_TRAINING_PATTERN; +- tmp |= (pattern << 24); +- nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), tmp); + /* transaction request, wait up to 1ms for it to complete */ + nv_wr32(dev, 0x00e4e4 + (ch * 0x50), 0x00010000 | ctrl); -+ + +- ret = auxch_rd(encoder, DP_TRAINING_PATTERN_SET, &cmd, 1); +- if (ret) +- return ret; +- cmd &= ~DP_TRAINING_PATTERN_MASK; +- cmd |= (pattern & DP_TRAINING_PATTERN_MASK); +- return auxch_wr(encoder, DP_TRAINING_PATTERN_SET, &cmd, 1); +-} + timeout = 1000; + do { + ctrl = nv_rd32(dev, 0x00e4e4 + (ch * 0x50)); @@ -1127,8 +1147,14 @@ index 7beb82a..de5efe7 100644 + } + } while (ctrl & 0x00010000); -- return auxch_wr(encoder, DP_LINK_BW_SET, &cmd, 1); --} +-static int +-nouveau_dp_max_voltage_swing(struct drm_encoder *encoder) +-{ +- struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); +- struct drm_device *dev = encoder->dev; +- struct bit_displayport_encoder_table_entry *dpse; +- struct bit_displayport_encoder_table *dpe; +- int i, dpe_headerlen, max_vs = 0; + /* read status, and check if transaction completed ok */ + stat = nv_mask(dev, 0x00e4e8 + (ch * 0x50), 0, 0); + if (!(stat & 0x000f0f00)) { @@ -1136,43 +1162,32 @@ index 7beb82a..de5efe7 100644 + break; + } --static int --nouveau_dp_link_train_set(struct drm_encoder *encoder, int pattern) --{ -- struct drm_device *dev = encoder->dev; -- struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); -- uint32_t tmp; -- uint8_t cmd; -- int or = nv_encoder->or, link = !(nv_encoder->dcb->sorconf.link & 1); -- int ret; +- dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen); +- if (!dpe) +- return false; +- dpse = (void *)((char *)dpe + dpe_headerlen); + AUX_DBG("%02d 0x%08x 0x%08x\n", retries, ctrl, stat); + } -- tmp = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link)); -- tmp &= ~NV50_SOR_DP_CTRL_TRAINING_PATTERN; -- tmp |= (pattern << 24); -- nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), tmp); +- for (i = 0; i < dpe_headerlen; i++, dpse++) { +- if (dpse->vs_level > max_vs) +- max_vs = dpse->vs_level; + if (type & 1) { + for (i = 0; i < 16; i += 4) { + xbuf[i / 4] = nv_rd32(dev, 0x00e4d0 + (ch * 0x50) + i); + AUX_DBG("rd 0x%08x\n", xbuf[i / 4]); + } + memcpy(data, xbuf, size); -+ } + } -- ret = auxch_rd(encoder, DP_TRAINING_PATTERN_SET, &cmd, 1); -- if (ret) -- return ret; -- cmd &= ~DP_TRAINING_PATTERN_MASK; -- cmd |= (pattern & DP_TRAINING_PATTERN_MASK); -- return auxch_wr(encoder, DP_TRAINING_PATTERN_SET, &cmd, 1); +- return max_vs; +out: + auxch_fini(dev, ch); + return ret; } -static int --nouveau_dp_max_voltage_swing(struct drm_encoder *encoder) +-nouveau_dp_max_pre_emphasis(struct drm_encoder *encoder, int vs) +static u32 +dp_link_bw_get(struct drm_device *dev, int or, int link) { @@ -1180,21 +1195,24 @@ index 7beb82a..de5efe7 100644 - struct drm_device *dev = encoder->dev; - struct bit_displayport_encoder_table_entry *dpse; - struct bit_displayport_encoder_table *dpe; -- int i, dpe_headerlen, max_vs = 0; +- int i, dpe_headerlen, max_pre = 0; - - dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen); - if (!dpe) - return false; - dpse = (void *)((char *)dpe + dpe_headerlen); +- +- for (i = 0; i < dpe_headerlen; i++, dpse++) { +- if (dpse->vs_level != vs) +- continue; + u32 ctrl = nv_rd32(dev, 0x614300 + (or * 0x800)); + if (!(ctrl & 0x000c0000)) + return 162000; + return 270000; +} -- for (i = 0; i < dpe_headerlen; i++, dpse++) { -- if (dpse->vs_level > max_vs) -- max_vs = dpse->vs_level; +- if (dpse->pre_level > max_pre) +- max_pre = dpse->pre_level; +static int +dp_lane_count_get(struct drm_device *dev, int or, int link) +{ @@ -1206,40 +1224,52 @@ index 7beb82a..de5efe7 100644 + return 4; } - -- return max_vs; +- return max_pre; } --static int --nouveau_dp_max_pre_emphasis(struct drm_encoder *encoder, int vs) +-static bool +-nouveau_dp_link_train_adjust(struct drm_encoder *encoder, uint8_t *config) +void +nouveau_dp_tu_update(struct drm_device *dev, int or, int link, u32 clk, u32 bpp) { - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct drm_device *dev = encoder->dev; -- struct bit_displayport_encoder_table_entry *dpse; - struct bit_displayport_encoder_table *dpe; -- int i, dpe_headerlen, max_pre = 0; +- int ret, i, dpe_headerlen, vs = 0, pre = 0; +- uint8_t request[2]; + const u32 symbol = 100000; + int bestTU = 0, bestVTUi = 0, bestVTUf = 0, bestVTUa = 0; + int TU, VTUi, VTUf, VTUa; + u64 link_data_rate, link_ratio, unk; + u32 best_diff = 64 * symbol; + u32 link_nr, link_bw, r; -+ + +- dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen); +- if (!dpe) +- return false; + /* calculate packed data rate for each lane */ + link_nr = dp_lane_count_get(dev, or, link); + link_data_rate = (clk * bpp / 8) / link_nr; -+ + +- ret = auxch_rd(encoder, DP_ADJUST_REQUEST_LANE0_1, request, 2); +- if (ret) +- return false; + /* calculate ratio of packed data rate to link symbol rate */ + link_bw = dp_link_bw_get(dev, or, link); + link_ratio = link_data_rate * symbol; + r = do_div(link_ratio, link_bw); -+ + +- NV_DEBUG_KMS(dev, "\t\tadjust 0x%02x 0x%02x\n", request[0], request[1]); + for (TU = 64; TU >= 32; TU--) { + /* calculate average number of valid symbols in each TU */ + u32 tu_valid = link_ratio * TU; + u32 calc, diff; -+ + +- /* Keep all lanes at the same level.. */ +- for (i = 0; i < nv_encoder->dp.link_nr; i++) { +- int lane_req = (request[i >> 1] >> ((i & 1) << 2)) & 0xf; +- int lane_vs = lane_req & 3; +- int lane_pre = (lane_req >> 2) & 3; + /* find a hw representation for the fraction.. */ + VTUi = tu_valid / symbol; + calc = VTUi * symbol; @@ -1249,7 +1279,12 @@ index 7beb82a..de5efe7 100644 + VTUf = symbol / (symbol - diff); + if (symbol - (VTUf * diff)) + VTUf++; -+ + +- if (lane_vs > vs) +- vs = lane_vs; +- if (lane_pre > pre) +- pre = lane_pre; +- } + if (VTUf <= 15) { + VTUa = 1; + calc += symbol - (symbol / VTUf); @@ -1264,10 +1299,9 @@ index 7beb82a..de5efe7 100644 + calc += symbol / VTUf; + } -- dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen); -- if (!dpe) -- return false; -- dpse = (void *)((char *)dpe + dpe_headerlen); +- if (vs >= nouveau_dp_max_voltage_swing(encoder)) { +- vs = nouveau_dp_max_voltage_swing(encoder); +- vs |= 4; + diff = calc - tu_valid; + } else { + /* no remainder, but the hw doesn't like the fractional @@ -1278,10 +1312,7 @@ index 7beb82a..de5efe7 100644 + VTUf = 1; + VTUi--; + } - -- for (i = 0; i < dpe_headerlen; i++, dpse++) { -- if (dpse->vs_level != vs) -- continue; ++ + if (diff < best_diff) { + best_diff = diff; + bestTU = TU; @@ -1291,23 +1322,27 @@ index 7beb82a..de5efe7 100644 + if (diff == 0) + break; + } -+ } + } -- if (dpse->pre_level > max_pre) -- max_pre = dpse->pre_level; +- if (pre >= nouveau_dp_max_pre_emphasis(encoder, vs & 3)) { +- pre = nouveau_dp_max_pre_emphasis(encoder, vs & 3); +- pre |= 4; + if (!bestTU) { + NV_ERROR(dev, "DP: unable to find suitable config\n"); + return; } -- return max_pre; +- /* Update the configuration for all lanes.. */ +- for (i = 0; i < nv_encoder->dp.link_nr; i++) +- config[i] = (pre << 3) | vs; + /* XXX close to vbios numbers, but not right */ + unk = (symbol - link_ratio) * bestTU; + unk *= link_ratio; + r = do_div(unk, symbol); + r = do_div(unk, symbol); + unk += 6; -+ + +- return true; + nv_mask(dev, NV50_SOR_DP_CTRL(or, link), 0x000001fc, bestTU << 2); + nv_mask(dev, NV50_SOR_DP_SCFG(or, link), 0x010f7f3f, bestVTUa << 24 | + bestVTUf << 16 | @@ -1316,63 +1351,63 @@ index 7beb82a..de5efe7 100644 } -static bool --nouveau_dp_link_train_adjust(struct drm_encoder *encoder, uint8_t *config) +-nouveau_dp_link_train_commit(struct drm_encoder *encoder, uint8_t *config) +u8 * +nouveau_dp_bios_data(struct drm_device *dev, struct dcb_entry *dcb, u8 **entry) { - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct drm_device *dev = encoder->dev; +- struct bit_displayport_encoder_table_entry *dpse; - struct bit_displayport_encoder_table *dpe; -- int ret, i, dpe_headerlen, vs = 0, pre = 0; -- uint8_t request[2]; +- int or = nv_encoder->or, link = !(nv_encoder->dcb->sorconf.link & 1); +- int dpe_headerlen, ret, i; - +- NV_DEBUG_KMS(dev, "\t\tconfig 0x%02x 0x%02x 0x%02x 0x%02x\n", +- config[0], config[1], config[2], config[3]); ++ struct drm_nouveau_private *dev_priv = dev->dev_private; ++ struct nvbios *bios = &dev_priv->vbios; ++ struct bit_entry d; ++ u8 *table; ++ int i; + - dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen); - if (!dpe) - return false; -- -- ret = auxch_rd(encoder, DP_ADJUST_REQUEST_LANE0_1, request, 2); -- if (ret) -- return false; -- -- NV_DEBUG_KMS(dev, "\t\tadjust 0x%02x 0x%02x\n", request[0], request[1]); -- -- /* Keep all lanes at the same level.. */ -- for (i = 0; i < nv_encoder->dp.link_nr; i++) { -- int lane_req = (request[i >> 1] >> ((i & 1) << 2)) & 0xf; -- int lane_vs = lane_req & 3; -- int lane_pre = (lane_req >> 2) & 3; -+ struct drm_nouveau_private *dev_priv = dev->dev_private; -+ struct nvbios *bios = &dev_priv->vbios; -+ struct bit_entry d; -+ u8 *table; -+ int i; -+ +- dpse = (void *)((char *)dpe + dpe_headerlen); + if (bit_table(dev, 'd', &d)) { + NV_ERROR(dev, "BIT 'd' table not found\n"); + return NULL; + } -- if (lane_vs > vs) -- vs = lane_vs; -- if (lane_pre > pre) -- pre = lane_pre; +- for (i = 0; i < dpe->record_nr; i++, dpse++) { +- if (dpse->vs_level == (config[0] & 3) && +- dpse->pre_level == ((config[0] >> 3) & 3)) +- break; + if (d.version != 1) { + NV_ERROR(dev, "BIT 'd' table version %d unknown\n", d.version); + return NULL; } +- BUG_ON(i == dpe->record_nr); -- if (vs >= nouveau_dp_max_voltage_swing(encoder)) { -- vs = nouveau_dp_max_voltage_swing(encoder); -- vs |= 4; +- for (i = 0; i < nv_encoder->dp.link_nr; i++) { +- const int shift[4] = { 16, 8, 0, 24 }; +- uint32_t mask = 0xff << shift[i]; +- uint32_t reg0, reg1, reg2; + table = ROMPTR(bios, d.data[0]); + if (!table) { + NV_ERROR(dev, "displayport table pointer invalid\n"); + return NULL; - } ++ } -- if (pre >= nouveau_dp_max_pre_emphasis(encoder, vs & 3)) { -- pre = nouveau_dp_max_pre_emphasis(encoder, vs & 3); -- pre |= 4; +- reg0 = nv_rd32(dev, NV50_SOR_DP_UNK118(or, link)) & ~mask; +- reg0 |= (dpse->reg0 << shift[i]); +- reg1 = nv_rd32(dev, NV50_SOR_DP_UNK120(or, link)) & ~mask; +- reg1 |= (dpse->reg1 << shift[i]); +- reg2 = nv_rd32(dev, NV50_SOR_DP_UNK130(or, link)) & 0xffff00ff; +- reg2 |= (dpse->reg2 << 8); +- nv_wr32(dev, NV50_SOR_DP_UNK118(or, link), reg0); +- nv_wr32(dev, NV50_SOR_DP_UNK120(or, link), reg1); +- nv_wr32(dev, NV50_SOR_DP_UNK130(or, link), reg2); + switch (table[0]) { + case 0x20: + case 0x21: @@ -1383,9 +1418,9 @@ index 7beb82a..de5efe7 100644 + return NULL; } -- /* Update the configuration for all lanes.. */ -- for (i = 0; i < nv_encoder->dp.link_nr; i++) -- config[i] = (pre << 3) | vs; +- ret = auxch_wr(encoder, DP_TRAINING_LANE0_SET, config, 4); +- if (ret) +- return false; + for (i = 0; i < table[3]; i++) { + *entry = ROMPTR(bios, table[table[1] + (i * table[2])]); + if (*entry && bios_encoder_match(dcb, ROM32((*entry)[0]))) @@ -1397,15 +1432,8 @@ index 7beb82a..de5efe7 100644 + return NULL; } --static bool --nouveau_dp_link_train_commit(struct drm_encoder *encoder, uint8_t *config) --{ -- struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); -- struct drm_device *dev = encoder->dev; -- struct bit_displayport_encoder_table_entry *dpse; -- struct bit_displayport_encoder_table *dpe; -- int or = nv_encoder->or, link = !(nv_encoder->dcb->sorconf.link & 1); -- int dpe_headerlen, ret, i; +-bool +-nouveau_dp_link_train(struct drm_encoder *encoder) +/****************************************************************************** + * link training + *****************************************************************************/ @@ -1423,20 +1451,36 @@ index 7beb82a..de5efe7 100644 + u8 stat[6]; + u8 conf[4]; +}; - -- NV_DEBUG_KMS(dev, "\t\tconfig 0x%02x 0x%02x 0x%02x 0x%02x\n", -- config[0], config[1], config[2], config[3]); ++ +static void +dp_set_link_config(struct drm_device *dev, struct dp_state *dp) -+{ -+ struct drm_nouveau_private *dev_priv = dev->dev_private; + { +- struct drm_device *dev = encoder->dev; + struct drm_nouveau_private *dev_priv = dev->dev_private; +- struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; +- struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); +- struct nouveau_connector *nv_connector; +- struct bit_displayport_encoder_table *dpe; +- int dpe_headerlen; +- uint8_t config[4], status[3]; +- bool cr_done, cr_max_vs, eq_done, hpd_state; +- int ret = 0, i, tries, voltage; + int or = dp->or, link = dp->link; + u8 *entry, sink[2]; + u32 dp_ctrl; + u16 script; -+ + +- NV_DEBUG_KMS(dev, "link training!!\n"); + NV_DEBUG_KMS(dev, "%d lanes at %d KB/s\n", dp->link_nr, dp->link_bw); -+ + +- nv_connector = nouveau_encoder_connector_get(nv_encoder); +- if (!nv_connector) +- return false; +- +- dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen); +- if (!dpe) { +- NV_ERROR(dev, "SOR-%d: no DP encoder table!\n", nv_encoder->or); +- return false; + /* set selected link rate on source */ + switch (dp->link_bw) { + case 270000: @@ -1447,16 +1491,15 @@ index 7beb82a..de5efe7 100644 + nv_mask(dev, 0x614300 + (or * 0x800), 0x000c0000, 0x00000000); + sink[0] = DP_LINK_BW_1_62; + break; -+ } + } -- dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen); -- if (!dpe) -- return false; -- dpse = (void *)((char *)dpe + dpe_headerlen); +- /* disable hotplug detect, this flips around on some panels during +- * link training. + /* offset +0x0a of each dp encoder table entry is a pointer to another + * table, that has (among other things) pointers to more scripts that + * need to be executed, this time depending on link speed. -+ */ + */ +- hpd_state = pgpio->irq_enable(dev, nv_connector->dcb->gpio_tag, false); + entry = ROMPTR(&dev_priv->vbios, dp->entry[10]); + if (entry) { + if (dp->table[0] < 0x30) { @@ -1469,90 +1512,77 @@ index 7beb82a..de5efe7 100644 + script = ROM16(entry[1]); + } -- for (i = 0; i < dpe->record_nr; i++, dpse++) { -- if (dpse->vs_level == (config[0] & 3) && -- dpse->pre_level == ((config[0] >> 3) & 3)) -- break; +- if (dpe->script0) { +- NV_DEBUG_KMS(dev, "SOR-%d: running DP script 0\n", nv_encoder->or); +- nouveau_bios_run_init_table(dev, le16_to_cpu(dpe->script0), +- nv_encoder->dcb); + nouveau_bios_run_init_table(dev, script, dp->dcb, dp->crtc); } -- BUG_ON(i == dpe->record_nr); -- -- for (i = 0; i < nv_encoder->dp.link_nr; i++) { -- const int shift[4] = { 16, 8, 0, 24 }; -- uint32_t mask = 0xff << shift[i]; -- uint32_t reg0, reg1, reg2; -- -- reg0 = nv_rd32(dev, NV50_SOR_DP_UNK118(or, link)) & ~mask; -- reg0 |= (dpse->reg0 << shift[i]); -- reg1 = nv_rd32(dev, NV50_SOR_DP_UNK120(or, link)) & ~mask; -- reg1 |= (dpse->reg1 << shift[i]); -- reg2 = nv_rd32(dev, NV50_SOR_DP_UNK130(or, link)) & 0xffff00ff; -- reg2 |= (dpse->reg2 << 8); -- nv_wr32(dev, NV50_SOR_DP_UNK118(or, link), reg0); -- nv_wr32(dev, NV50_SOR_DP_UNK120(or, link), reg1); -- nv_wr32(dev, NV50_SOR_DP_UNK130(or, link), reg2); -+ + +-train: +- cr_done = eq_done = false; + /* configure lane count on the source */ + dp_ctrl = ((1 << dp->link_nr) - 1) << 16; + sink[1] = dp->link_nr; + if (dp->dpcd[2] & DP_ENHANCED_FRAME_CAP) { + dp_ctrl |= 0x00004000; + sink[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; - } ++ } -- ret = auxch_wr(encoder, DP_TRAINING_LANE0_SET, config, 4); -- if (ret) -- return false; +- /* set link configuration */ +- NV_DEBUG_KMS(dev, "\tbegin train: bw %d, lanes %d\n", +- nv_encoder->dp.link_bw, nv_encoder->dp.link_nr); + nv_mask(dev, NV50_SOR_DP_CTRL(or, link), 0x001f4000, dp_ctrl); -- return true; +- ret = nouveau_dp_link_bw_set(encoder, nv_encoder->dp.link_bw); +- if (ret) +- return false; + /* inform the sink of the new configuration */ + auxch_tx(dev, dp->auxch, 8, DP_LINK_BW_SET, sink, 2); - } ++} --bool --nouveau_dp_link_train(struct drm_encoder *encoder) +- config[0] = nv_encoder->dp.link_nr; +- if (nv_encoder->dp.dpcd_version >= 0x11 && +- nv_encoder->dp.enhanced_frame) +- config[0] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; +static void +dp_set_training_pattern(struct drm_device *dev, struct dp_state *dp, u8 tp) - { -- struct drm_device *dev = encoder->dev; -- struct drm_nouveau_private *dev_priv = dev->dev_private; -- struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; -- struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); -- struct nouveau_connector *nv_connector; -- struct bit_displayport_encoder_table *dpe; -- int dpe_headerlen; -- uint8_t config[4], status[3]; -- bool cr_done, cr_max_vs, eq_done, hpd_state; -- int ret = 0, i, tries, voltage; ++{ + u8 sink_tp; -- NV_DEBUG_KMS(dev, "link training!!\n"); +- ret = nouveau_dp_lane_count_set(encoder, config[0]); +- if (ret) +- return false; + NV_DEBUG_KMS(dev, "training pattern %d\n", tp); -- nv_connector = nouveau_encoder_connector_get(nv_encoder); -- if (!nv_connector) -- return false; +- /* clock recovery */ +- NV_DEBUG_KMS(dev, "\tbegin cr\n"); +- ret = nouveau_dp_link_train_set(encoder, DP_TRAINING_PATTERN_1); +- if (ret) +- goto stop; + nv_mask(dev, NV50_SOR_DP_CTRL(dp->or, dp->link), 0x0f000000, tp << 24); -- dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen); -- if (!dpe) { -- NV_ERROR(dev, "SOR-%d: no DP encoder table!\n", nv_encoder->or); -- return false; -- } +- tries = 0; +- voltage = -1; +- memset(config, 0x00, sizeof(config)); +- for (;;) { +- if (!nouveau_dp_link_train_commit(encoder, config)) +- break; + auxch_tx(dev, dp->auxch, 9, DP_TRAINING_PATTERN_SET, &sink_tp, 1); + sink_tp &= ~DP_TRAINING_PATTERN_MASK; + sink_tp |= tp; + auxch_tx(dev, dp->auxch, 8, DP_TRAINING_PATTERN_SET, &sink_tp, 1); +} -- /* disable hotplug detect, this flips around on some panels during -- * link training. -- */ -- hpd_state = pgpio->irq_enable(dev, nv_connector->dcb->gpio_tag, false); +- udelay(100); +static const u8 nv50_lane_map[] = { 16, 8, 0, 24 }; +static const u8 nvaf_lane_map[] = { 24, 16, 8, 0 }; -+ + +- ret = auxch_rd(encoder, DP_LANE0_1_STATUS, status, 2); +- if (ret) +- break; +- NV_DEBUG_KMS(dev, "\t\tstatus: 0x%02x 0x%02x\n", +- status[0], status[1]); +static int +dp_link_train_commit(struct drm_device *dev, struct dp_state *dp) +{ @@ -1562,12 +1592,21 @@ index 7beb82a..de5efe7 100644 + int link = dp->link; + int or = dp->or; + int i; -+ + +- cr_done = true; +- cr_max_vs = false; +- for (i = 0; i < nv_encoder->dp.link_nr; i++) { +- int lane = (status[i >> 1] >> ((i & 1) * 4)) & 0xf; + if (dev_priv->chipset != 0xaf) + shifts = nv50_lane_map; + else + shifts = nvaf_lane_map; -+ + +- if (!(lane & DP_LANE_CR_DONE)) { +- cr_done = false; +- if (config[i] & DP_TRAIN_MAX_PRE_EMPHASIS_REACHED) +- cr_max_vs = true; +- break; + for (i = 0; i < dp->link_nr; i++) { + u8 *conf = dp->entry + dp->table[4]; + u8 lane = (dp->stat[4 + (i >> 1)] >> ((i & 1) * 4)) & 0xf; @@ -1591,8 +1630,13 @@ index 7beb82a..de5efe7 100644 + conf += dp->table[5]; + if (conf >= last) + return -EINVAL; -+ } -+ + } +- } + +- if ((config[0] & DP_TRAIN_VOLTAGE_SWING_MASK) != voltage) { +- voltage = config[0] & DP_TRAIN_VOLTAGE_SWING_MASK; +- tries = 0; +- } + conf += 2; + } else { + /* no lookup table anymore, set entries for each @@ -1605,69 +1649,57 @@ index 7beb82a..de5efe7 100644 + case 2: lpre += 7; break; + case 3: lpre += 9; break; + } -+ + +- if (cr_done || cr_max_vs || (++tries == 5)) +- break; + conf = conf + (lpre * dp->table[5]); + conf++; + } -- if (dpe->script0) { -- NV_DEBUG_KMS(dev, "SOR-%d: running DP script 0\n", nv_encoder->or); -- nouveau_bios_run_init_table(dev, le16_to_cpu(dpe->script0), -- nv_encoder->dcb); +- if (!nouveau_dp_link_train_adjust(encoder, config)) +- break; + drv |= conf[0] << shifts[i]; + pre |= conf[1] << shifts[i]; + unk = (unk & ~0x0000ff00) | (conf[2] << 8); } --train: -- cr_done = eq_done = false; +- if (!cr_done) +- goto stop; + nv_mask(dev, NV50_SOR_DP_UNK118(or, link), mask, drv); + nv_mask(dev, NV50_SOR_DP_UNK120(or, link), mask, pre); + nv_mask(dev, NV50_SOR_DP_UNK130(or, link), 0x0000ff0f, unk); - -- /* set link configuration */ -- NV_DEBUG_KMS(dev, "\tbegin train: bw %d, lanes %d\n", -- nv_encoder->dp.link_bw, nv_encoder->dp.link_nr); ++ + return auxch_tx(dev, dp->auxch, 8, DP_TRAINING_LANE0_SET, dp->conf, 4); +} - -- ret = nouveau_dp_link_bw_set(encoder, nv_encoder->dp.link_bw); -- if (ret) -- return false; ++ +static int +dp_link_train_update(struct drm_device *dev, struct dp_state *dp, u32 delay) +{ + int ret; - -- config[0] = nv_encoder->dp.link_nr; -- if (nv_encoder->dp.dpcd_version >= 0x11 && -- nv_encoder->dp.enhanced_frame) -- config[0] |= DP_LANE_COUNT_ENHANCED_FRAME_EN; ++ + udelay(delay); -- ret = nouveau_dp_lane_count_set(encoder, config[0]); +- /* channel equalisation */ +- NV_DEBUG_KMS(dev, "\tbegin eq\n"); +- ret = nouveau_dp_link_train_set(encoder, DP_TRAINING_PATTERN_2); + ret = auxch_tx(dev, dp->auxch, 9, DP_LANE0_1_STATUS, dp->stat, 6); if (ret) -- return false; +- goto stop; + return ret; -- /* clock recovery */ -- NV_DEBUG_KMS(dev, "\tbegin cr\n"); -- ret = nouveau_dp_link_train_set(encoder, DP_TRAINING_PATTERN_1); -- if (ret) -- goto stop; +- for (tries = 0; tries <= 5; tries++) { +- udelay(400); + NV_DEBUG_KMS(dev, "status %02x %02x %02x %02x %02x %02x\n", + dp->stat[0], dp->stat[1], dp->stat[2], dp->stat[3], + dp->stat[4], dp->stat[5]); + return 0; +} -- tries = 0; -- voltage = -1; -- memset(config, 0x00, sizeof(config)); -- for (;;) { -- if (!nouveau_dp_link_train_commit(encoder, config)) +- ret = auxch_rd(encoder, DP_LANE0_1_STATUS, status, 3); +- if (ret) - break; +- NV_DEBUG_KMS(dev, "\t\tstatus: 0x%02x 0x%02x\n", +- status[0], status[1]); +static int +dp_link_train_cr(struct drm_device *dev, struct dp_state *dp) +{ @@ -1675,136 +1707,61 @@ index 7beb82a..de5efe7 100644 + int voltage = dp->conf[0] & DP_TRAIN_VOLTAGE_SWING_MASK; + int tries = 0, i; -- udelay(100); +- eq_done = true; +- if (!(status[2] & DP_INTERLANE_ALIGN_DONE)) +- eq_done = false; + dp_set_training_pattern(dev, dp, DP_TRAINING_PATTERN_1); -- ret = auxch_rd(encoder, DP_LANE0_1_STATUS, status, 2); -- if (ret) +- for (i = 0; eq_done && i < nv_encoder->dp.link_nr; i++) { +- int lane = (status[i >> 1] >> ((i & 1) * 4)) & 0xf; + do { + if (dp_link_train_commit(dev, dp) || + dp_link_train_update(dev, dp, 100)) - break; -- NV_DEBUG_KMS(dev, "\t\tstatus: 0x%02x 0x%02x\n", -- status[0], status[1]); ++ break; - cr_done = true; -- cr_max_vs = false; -- for (i = 0; i < nv_encoder->dp.link_nr; i++) { -- int lane = (status[i >> 1] >> ((i & 1) * 4)) & 0xf; -- ++ cr_done = true; + for (i = 0; i < dp->link_nr; i++) { + u8 lane = (dp->stat[i >> 1] >> ((i & 1) * 4)) & 0xf; if (!(lane & DP_LANE_CR_DONE)) { cr_done = false; -- if (config[i] & DP_TRAIN_MAX_PRE_EMPHASIS_REACHED) -- cr_max_vs = true; + if (dp->conf[i] & DP_TRAIN_MAX_SWING_REACHED) + abort = true; break; } - } ++ } -- if ((config[0] & DP_TRAIN_VOLTAGE_SWING_MASK) != voltage) { -- voltage = config[0] & DP_TRAIN_VOLTAGE_SWING_MASK; +- if (!(lane & DP_LANE_CHANNEL_EQ_DONE) || +- !(lane & DP_LANE_SYMBOL_LOCKED)) { +- eq_done = false; +- break; +- } + if ((dp->conf[0] & DP_TRAIN_VOLTAGE_SWING_MASK) != voltage) { + voltage = dp->conf[0] & DP_TRAIN_VOLTAGE_SWING_MASK; - tries = 0; ++ tries = 0; } + } while (!cr_done && !abort && ++tries < 5); -- if (cr_done || cr_max_vs || (++tries == 5)) -- break; -- -- if (!nouveau_dp_link_train_adjust(encoder, config)) +- if (eq_done || !cr_done) - break; -- } -- -- if (!cr_done) -- goto stop; + return cr_done ? 0 : -1; +} -- /* channel equalisation */ -- NV_DEBUG_KMS(dev, "\tbegin eq\n"); -- ret = nouveau_dp_link_train_set(encoder, DP_TRAINING_PATTERN_2); -- if (ret) -- goto stop; +- if (!nouveau_dp_link_train_adjust(encoder, config) || +- !nouveau_dp_link_train_commit(encoder, config)) +- break; +- } +static int +dp_link_train_eq(struct drm_device *dev, struct dp_state *dp) +{ + bool eq_done, cr_done = true; + int tries = 0, i; -- for (tries = 0; tries <= 5; tries++) { -- udelay(400); -+ dp_set_training_pattern(dev, dp, DP_TRAINING_PATTERN_2); - -- ret = auxch_rd(encoder, DP_LANE0_1_STATUS, status, 3); -- if (ret) -+ do { -+ if (dp_link_train_update(dev, dp, 400)) - break; -- NV_DEBUG_KMS(dev, "\t\tstatus: 0x%02x 0x%02x\n", -- status[0], status[1]); - -- eq_done = true; -- if (!(status[2] & DP_INTERLANE_ALIGN_DONE)) -- eq_done = false; -- -- for (i = 0; eq_done && i < nv_encoder->dp.link_nr; i++) { -- int lane = (status[i >> 1] >> ((i & 1) * 4)) & 0xf; -- -- if (!(lane & DP_LANE_CR_DONE)) { -+ eq_done = !!(dp->stat[2] & DP_INTERLANE_ALIGN_DONE); -+ for (i = 0; i < dp->link_nr && eq_done; i++) { -+ u8 lane = (dp->stat[i >> 1] >> ((i & 1) * 4)) & 0xf; -+ if (!(lane & DP_LANE_CR_DONE)) - cr_done = false; -- break; -- } -- - if (!(lane & DP_LANE_CHANNEL_EQ_DONE) || -- !(lane & DP_LANE_SYMBOL_LOCKED)) { -+ !(lane & DP_LANE_SYMBOL_LOCKED)) - eq_done = false; -- break; -- } - } - -- if (eq_done || !cr_done) -+ if (dp_link_train_commit(dev, dp)) - break; -+ } while (!eq_done && cr_done && ++tries <= 5); - -- if (!nouveau_dp_link_train_adjust(encoder, config) || -- !nouveau_dp_link_train_commit(encoder, config)) -- break; -- } -+ return eq_done ? 0 : -1; -+} - -stop: - /* end link training */ - ret = nouveau_dp_link_train_set(encoder, DP_TRAINING_PATTERN_DISABLE); - if (ret) -+bool -+nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate) -+{ -+ struct drm_nouveau_private *dev_priv = encoder->dev->dev_private; -+ struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; -+ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); -+ struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); -+ struct nouveau_connector *nv_connector = -+ nouveau_encoder_connector_get(nv_encoder); -+ struct drm_device *dev = encoder->dev; -+ struct nouveau_i2c_chan *auxch; -+ const u32 bw_list[] = { 270000, 162000, 0 }; -+ const u32 *link_bw = bw_list; -+ struct dp_state dp; -+ -+ auxch = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index); -+ if (!auxch) - return false; +- return false; ++ dp_set_training_pattern(dev, dp, DP_TRAINING_PATTERN_2); - /* retry at a lower setting, if possible */ - if (!ret && !(eq_done && cr_done)) { @@ -1814,90 +1771,53 @@ index 7beb82a..de5efe7 100644 - nv_encoder->dp.link_bw = DP_LINK_BW_1_62; - goto train; - } -+ dp.table = nouveau_dp_bios_data(dev, nv_encoder->dcb, &dp.entry); -+ if (!dp.table) -+ return -EINVAL; -+ -+ dp.dcb = nv_encoder->dcb; -+ dp.crtc = nv_crtc->index; -+ dp.auxch = auxch->rd; -+ dp.or = nv_encoder->or; -+ dp.link = !(nv_encoder->dcb->sorconf.link & 1); -+ dp.dpcd = nv_encoder->dp.dpcd; -+ -+ /* some sinks toggle hotplug in response to some of the actions -+ * we take during link training (DP_SET_POWER is one), we need -+ * to ignore them for the moment to avoid races. -+ */ -+ pgpio->irq_enable(dev, nv_connector->dcb->gpio_tag, false); -+ -+ /* enable down-spreading, if possible */ -+ if (dp.table[1] >= 16) { -+ u16 script = ROM16(dp.entry[14]); -+ if (nv_encoder->dp.dpcd[3] & 1) -+ script = ROM16(dp.entry[12]); -+ -+ nouveau_bios_run_init_table(dev, script, dp.dcb, dp.crtc); - } - -- if (dpe->script1) { -- NV_DEBUG_KMS(dev, "SOR-%d: running DP script 1\n", nv_encoder->or); -- nouveau_bios_run_init_table(dev, le16_to_cpu(dpe->script1), -- nv_encoder->dcb); -+ /* execute pre-train script from vbios */ -+ nouveau_bios_run_init_table(dev, ROM16(dp.entry[6]), dp.dcb, dp.crtc); -+ -+ /* start off at highest link rate supported by encoder and display */ -+ while (*link_bw > nv_encoder->dp.link_bw) -+ link_bw++; -+ -+ while (link_bw[0]) { -+ /* find minimum required lane count at this link rate */ -+ dp.link_nr = nv_encoder->dp.link_nr; -+ while ((dp.link_nr >> 1) * link_bw[0] > datarate) -+ dp.link_nr >>= 1; -+ -+ /* drop link rate to minimum with this lane count */ -+ while ((link_bw[1] * dp.link_nr) > datarate) -+ link_bw++; -+ dp.link_bw = link_bw[0]; -+ -+ /* program selected link configuration */ -+ dp_set_link_config(dev, &dp); -+ -+ /* attempt to train the link at this configuration */ -+ memset(dp.stat, 0x00, sizeof(dp.stat)); -+ if (!dp_link_train_cr(dev, &dp) && -+ !dp_link_train_eq(dev, &dp)) -+ break; -+ -+ /* retry at lower rate */ -+ link_bw++; - } +- } ++ do { ++ if (dp_link_train_update(dev, dp, 400)) ++ break; + +- if (dpe->script1) { +- NV_DEBUG_KMS(dev, "SOR-%d: running DP script 1\n", nv_encoder->or); +- nouveau_bios_run_init_table(dev, le16_to_cpu(dpe->script1), +- nv_encoder->dcb); +- } ++ eq_done = !!(dp->stat[2] & DP_INTERLANE_ALIGN_DONE); ++ for (i = 0; i < dp->link_nr && eq_done; i++) { ++ u8 lane = (dp->stat[i >> 1] >> ((i & 1) * 4)) & 0xf; ++ if (!(lane & DP_LANE_CR_DONE)) ++ cr_done = false; ++ if (!(lane & DP_LANE_CHANNEL_EQ_DONE) || ++ !(lane & DP_LANE_SYMBOL_LOCKED)) ++ eq_done = false; ++ } - /* re-enable hotplug detect */ - pgpio->irq_enable(dev, nv_connector->dcb->gpio_tag, hpd_state); -+ /* finish link training */ -+ dp_set_training_pattern(dev, &dp, DP_TRAINING_PATTERN_DISABLE); ++ if (dp_link_train_commit(dev, dp)) ++ break; ++ } while (!eq_done && cr_done && ++tries <= 5); - return eq_done; -+ /* execute post-train script from vbios */ -+ nouveau_bios_run_init_table(dev, ROM16(dp.entry[8]), dp.dcb, dp.crtc); -+ -+ /* re-enable hotplug detect */ -+ pgpio->irq_enable(dev, nv_connector->dcb->gpio_tag, true); -+ return true; ++ return eq_done ? 0 : -1; } bool -@@ -447,31 +648,34 @@ nouveau_dp_detect(struct drm_encoder *encoder) +-nouveau_dp_detect(struct drm_encoder *encoder) ++nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate) { ++ struct drm_nouveau_private *dev_priv = encoder->dev->dev_private; ++ struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio; struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); ++ struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); ++ struct nouveau_connector *nv_connector = ++ nouveau_encoder_connector_get(nv_encoder); struct drm_device *dev = encoder->dev; - uint8_t dpcd[4]; +- int ret; + struct nouveau_i2c_chan *auxch; -+ u8 *dpcd = nv_encoder->dp.dpcd; - int ret; ++ const u32 bw_list[] = { 270000, 162000, 0 }; ++ const u32 *link_bw = bw_list; ++ struct dp_state dp; - ret = auxch_rd(encoder, 0x0000, dpcd, 4); - if (ret) @@ -1910,47 +1830,64 @@ index 7beb82a..de5efe7 100644 - nv_encoder->dcb->dpconf.link_bw, - nv_encoder->dcb->dpconf.link_nr, - dpcd[1], dpcd[2] & 0x0f, dpcd[0]); -+ ret = auxch_tx(dev, auxch->rd, 9, DP_DPCD_REV, dpcd, 8); -+ if (ret) -+ return false; ++ dp.table = nouveau_dp_bios_data(dev, nv_encoder->dcb, &dp.entry); ++ if (!dp.table) ++ return -EINVAL; - nv_encoder->dp.dpcd_version = dpcd[0]; -+ nv_encoder->dp.link_bw = 27000 * dpcd[1]; -+ nv_encoder->dp.link_nr = dpcd[2] & DP_MAX_LANE_COUNT_MASK; ++ dp.dcb = nv_encoder->dcb; ++ dp.crtc = nv_crtc->index; ++ dp.auxch = auxch->rd; ++ dp.or = nv_encoder->or; ++ dp.link = !(nv_encoder->dcb->sorconf.link & 1); ++ dp.dpcd = nv_encoder->dp.dpcd; - nv_encoder->dp.link_bw = dpcd[1]; - if (nv_encoder->dp.link_bw != DP_LINK_BW_1_62 && - !nv_encoder->dcb->dpconf.link_bw) - nv_encoder->dp.link_bw = DP_LINK_BW_1_62; -+ NV_DEBUG_KMS(dev, "display: %dx%d dpcd 0x%02x\n", -+ nv_encoder->dp.link_nr, nv_encoder->dp.link_bw, dpcd[0]); -+ NV_DEBUG_KMS(dev, "encoder: %dx%d\n", -+ nv_encoder->dcb->dpconf.link_nr, -+ nv_encoder->dcb->dpconf.link_bw); ++ /* some sinks toggle hotplug in response to some of the actions ++ * we take during link training (DP_SET_POWER is one), we need ++ * to ignore them for the moment to avoid races. ++ */ ++ pgpio->irq_enable(dev, nv_connector->dcb->gpio_tag, false); - nv_encoder->dp.link_nr = dpcd[2] & DP_MAX_LANE_COUNT_MASK; - if (nv_encoder->dp.link_nr > nv_encoder->dcb->dpconf.link_nr) -+ if (nv_encoder->dcb->dpconf.link_nr < nv_encoder->dp.link_nr) - nv_encoder->dp.link_nr = nv_encoder->dcb->dpconf.link_nr; -+ if (nv_encoder->dcb->dpconf.link_bw < nv_encoder->dp.link_bw) -+ nv_encoder->dp.link_bw = nv_encoder->dcb->dpconf.link_bw; +- nv_encoder->dp.link_nr = nv_encoder->dcb->dpconf.link_nr; ++ /* enable down-spreading, if possible */ ++ if (dp.table[1] >= 16) { ++ u16 script = ROM16(dp.entry[14]); ++ if (nv_encoder->dp.dpcd[3] & 1) ++ script = ROM16(dp.entry[12]); - nv_encoder->dp.enhanced_frame = (dpcd[2] & DP_ENHANCED_FRAME_CAP); -+ NV_DEBUG_KMS(dev, "maximum: %dx%d\n", -+ nv_encoder->dp.link_nr, nv_encoder->dp.link_bw); ++ nouveau_bios_run_init_table(dev, script, dp.dcb, dp.crtc); ++ } - return true; - } -@@ -480,105 +684,13 @@ int - nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr, - uint8_t *data, int data_nr) - { +- return true; +-} ++ /* execute pre-train script from vbios */ ++ nouveau_bios_run_init_table(dev, ROM16(dp.entry[6]), dp.dcb, dp.crtc); + +-int +-nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr, +- uint8_t *data, int data_nr) +-{ - struct drm_device *dev = auxch->dev; - uint32_t tmp, ctrl, stat = 0, data32[4] = {}; - int ret = 0, i, index = auxch->rd; -- ++ /* start off at highest link rate supported by encoder and display */ ++ while (*link_bw > nv_encoder->dp.link_bw) ++ link_bw++; + - NV_DEBUG_KMS(dev, "ch %d cmd %d addr 0x%x len %d\n", index, cmd, addr, data_nr); -- ++ while (link_bw[0]) { ++ /* find minimum required lane count at this link rate */ ++ dp.link_nr = nv_encoder->dp.link_nr; ++ while ((dp.link_nr >> 1) * link_bw[0] > datarate) ++ dp.link_nr >>= 1; + - tmp = nv_rd32(dev, NV50_AUXCH_CTRL(auxch->rd)); - nv_wr32(dev, NV50_AUXCH_CTRL(auxch->rd), tmp | 0x00100000); - tmp = nv_rd32(dev, NV50_AUXCH_CTRL(auxch->rd)); @@ -1959,19 +1896,32 @@ index 7beb82a..de5efe7 100644 - ret = -EIO; - goto out; - } -- ++ /* drop link rate to minimum with this lane count */ ++ while ((link_bw[1] * dp.link_nr) > datarate) ++ link_bw++; ++ dp.link_bw = link_bw[0]; ++ ++ /* program selected link configuration */ ++ dp_set_link_config(dev, &dp); + - for (i = 0; i < 3; i++) { - tmp = nv_rd32(dev, NV50_AUXCH_STAT(auxch->rd)); - if (tmp & NV50_AUXCH_STAT_STATE_READY) -- break; ++ /* attempt to train the link at this configuration */ ++ memset(dp.stat, 0x00, sizeof(dp.stat)); ++ if (!dp_link_train_cr(dev, &dp) && ++ !dp_link_train_eq(dev, &dp)) + break; - udelay(100); - } -- + - if (i == 3) { - ret = -EBUSY; - goto out; -- } -- ++ /* retry at lower rate */ ++ link_bw++; + } + - if (!(cmd & 1)) { - memcpy(data32, data, data_nr); - for (i = 0; i < 4; i++) { @@ -1979,13 +1929,17 @@ index 7beb82a..de5efe7 100644 - nv_wr32(dev, NV50_AUXCH_DATA_OUT(index, i), data32[i]); - } - } -- ++ /* finish link training */ ++ dp_set_training_pattern(dev, &dp, DP_TRAINING_PATTERN_DISABLE); + - nv_wr32(dev, NV50_AUXCH_ADDR(index), addr); - ctrl = nv_rd32(dev, NV50_AUXCH_CTRL(index)); - ctrl &= ~(NV50_AUXCH_CTRL_CMD | NV50_AUXCH_CTRL_LEN); - ctrl |= (cmd << NV50_AUXCH_CTRL_CMD_SHIFT); - ctrl |= ((data_nr - 1) << NV50_AUXCH_CTRL_LEN_SHIFT); -- ++ /* execute post-train script from vbios */ ++ nouveau_bios_run_init_table(dev, ROM16(dp.entry[8]), dp.dcb, dp.crtc); + - for (i = 0; i < 16; i++) { - nv_wr32(dev, NV50_AUXCH_CTRL(index), ctrl | 0x80000000); - nv_wr32(dev, NV50_AUXCH_CTRL(index), ctrl); @@ -1997,34 +1951,64 @@ index 7beb82a..de5efe7 100644 - ret = -EBUSY; - goto out; - } -- ++ /* re-enable hotplug detect */ ++ pgpio->irq_enable(dev, nv_connector->dcb->gpio_tag, true); ++ return true; ++} ++ ++bool ++nouveau_dp_detect(struct drm_encoder *encoder) ++{ ++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); ++ struct drm_device *dev = encoder->dev; ++ struct nouveau_i2c_chan *auxch; ++ u8 *dpcd = nv_encoder->dp.dpcd; ++ int ret; + - udelay(400); -- ++ auxch = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index); ++ if (!auxch) ++ return false; + - stat = nv_rd32(dev, NV50_AUXCH_STAT(index)); - if ((stat & NV50_AUXCH_STAT_REPLY_AUX) != - NV50_AUXCH_STAT_REPLY_AUX_DEFER) - break; - } -- ++ ret = auxch_tx(dev, auxch->rd, 9, DP_DPCD_REV, dpcd, 8); ++ if (ret) ++ return false; + - if (i == 16) { - NV_ERROR(dev, "auxch DEFER too many times, bailing\n"); - ret = -EREMOTEIO; - goto out; - } -- ++ nv_encoder->dp.link_bw = 27000 * dpcd[1]; ++ nv_encoder->dp.link_nr = dpcd[2] & DP_MAX_LANE_COUNT_MASK; + - if (cmd & 1) { - if ((stat & NV50_AUXCH_STAT_COUNT) != data_nr) { - ret = -EREMOTEIO; - goto out; - } -- ++ NV_DEBUG_KMS(dev, "display: %dx%d dpcd 0x%02x\n", ++ nv_encoder->dp.link_nr, nv_encoder->dp.link_bw, dpcd[0]); ++ NV_DEBUG_KMS(dev, "encoder: %dx%d\n", ++ nv_encoder->dcb->dpconf.link_nr, ++ nv_encoder->dcb->dpconf.link_bw); + - for (i = 0; i < 4; i++) { - data32[i] = nv_rd32(dev, NV50_AUXCH_DATA_IN(index, i)); - NV_DEBUG_KMS(dev, "rd %d: 0x%08x\n", i, data32[i]); - } - memcpy(data, data32, data_nr); - } -- ++ if (nv_encoder->dcb->dpconf.link_nr < nv_encoder->dp.link_nr) ++ nv_encoder->dp.link_nr = nv_encoder->dcb->dpconf.link_nr; ++ if (nv_encoder->dcb->dpconf.link_bw < nv_encoder->dp.link_bw) ++ nv_encoder->dp.link_bw = nv_encoder->dcb->dpconf.link_bw; + -out: - tmp = nv_rd32(dev, NV50_AUXCH_CTRL(auxch->rd)); - nv_wr32(dev, NV50_AUXCH_CTRL(auxch->rd), tmp & ~0x00100000); @@ -2033,10 +2017,18 @@ index 7beb82a..de5efe7 100644 - NV_ERROR(dev, "expected bit 24 == 0, got 0x%08x\n", tmp); - ret = -EIO; - } -- ++ NV_DEBUG_KMS(dev, "maximum: %dx%d\n", ++ nv_encoder->dp.link_nr, nv_encoder->dp.link_bw); + - udelay(400); -- ++ return true; ++} + - return ret ? ret : (stat & NV50_AUXCH_STAT_REPLY); ++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); } @@ -2165,9 +2157,6 @@ index d7d51de..29837da 100644 u32 shader; - u32 unk05; - u32 unk0a; -- -- u8 voltage; -- u8 fanspeed; + u32 rop; + u32 copy; + u32 daemon; @@ -2177,7 +2166,9 @@ index d7d51de..29837da 100644 + u32 hub01; /* nvc0- */ + u32 hub06; /* nvc0- */ + u32 hub07; /* nvc0- */ -+ + +- u8 voltage; +- u8 fanspeed; + u32 volt_min; /* microvolts */ + u32 volt_max; + u8 fanspeed; @@ -2410,7 +2401,7 @@ index ae69b61..e5d6e3f 100644 - #endif /* __NOUVEAU_ENCODER_H__ */ diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c -index c919cfc..81116cf 100644 +index ae22dfa..2f6daae 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.c +++ b/drivers/gpu/drm/nouveau/nouveau_fence.c @@ -519,7 +519,7 @@ nouveau_fence_channel_init(struct nouveau_channel *chan) @@ -2848,7 +2839,9 @@ index f9ae2fc..36bec48 100644 - /* XXX: reg_100240? */ - } timing->id = i; -- ++ timing->WR = entry[0]; ++ timing->CL = entry[2]; + - NV_DEBUG(dev, "Entry %d: 220: %08x %08x %08x %08x\n", i, - timing->reg_100220, timing->reg_100224, - timing->reg_100228, timing->reg_10022c); @@ -2856,9 +2849,6 @@ index f9ae2fc..36bec48 100644 - timing->reg_100230, timing->reg_100234, - timing->reg_100238, timing->reg_10023c); - NV_DEBUG(dev, " 240: %08x\n", timing->reg_100240); -+ timing->WR = entry[0]; -+ timing->CL = entry[2]; -+ + if(dev_priv->card_type <= NV_40) { + nv40_mem_timing_entry(dev,hdr,(struct nouveau_pm_tbl_entry*) entry,magic_number,&pm->memtimings.timing[i]); + } else if(dev_priv->card_type == NV_50){ @@ -2993,7 +2983,12 @@ index 1640dec..b29ffb3 100644 - struct nouveau_mm *rmm; - struct nouveau_mm_node *heap; + struct nouveau_mm_node *node; -+ + +- heap = kzalloc(sizeof(*heap), GFP_KERNEL); +- if (!heap) +- return -ENOMEM; +- heap->offset = roundup(offset, block); +- heap->length = rounddown(offset + length, block) - heap->offset; + if (block) { + mutex_init(&mm->mutex); + INIT_LIST_HEAD(&mm->nodes); @@ -3002,20 +2997,12 @@ index 1640dec..b29ffb3 100644 + mm->heap_nodes = 0; + } -- heap = kzalloc(sizeof(*heap), GFP_KERNEL); -- if (!heap) -+ node = kzalloc(sizeof(*node), GFP_KERNEL); -+ if (!node) - return -ENOMEM; -- heap->offset = roundup(offset, block); -- heap->length = rounddown(offset + length, block) - heap->offset; -+ node->offset = roundup(offset, mm->block_size); -+ node->length = rounddown(offset + length, mm->block_size) - node->offset; - - rmm = kzalloc(sizeof(*rmm), GFP_KERNEL); - if (!rmm) { - kfree(heap); -- return -ENOMEM; ++ node = kzalloc(sizeof(*node), GFP_KERNEL); ++ if (!node) + return -ENOMEM; - } - rmm->block_size = block; - mutex_init(&rmm->mutex); @@ -3023,7 +3010,9 @@ index 1640dec..b29ffb3 100644 - INIT_LIST_HEAD(&rmm->free); - list_add(&heap->nl_entry, &rmm->nodes); - list_add(&heap->fl_entry, &rmm->free); -- ++ node->offset = roundup(offset, mm->block_size); ++ node->length = rounddown(offset + length, mm->block_size) - node->offset; + - *prmm = rmm; + list_add_tail(&node->nl_entry, &mm->nodes); + list_add_tail(&node->fl_entry, &mm->free); @@ -3038,15 +3027,14 @@ index 1640dec..b29ffb3 100644 - struct nouveau_mm *rmm = *prmm; struct nouveau_mm_node *node, *heap = - list_first_entry(&rmm->nodes, struct nouveau_mm_node, nl_entry); -- ++ list_first_entry(&mm->nodes, struct nouveau_mm_node, nl_entry); ++ int nodes = 0; + - if (!list_is_singular(&rmm->nodes)) { - printk(KERN_ERR "nouveau_mm not empty at destroy time!\n"); - list_for_each_entry(node, &rmm->nodes, nl_entry) { - printk(KERN_ERR "0x%02x: 0x%08x 0x%08x\n", - node->type, node->offset, node->length); -+ list_first_entry(&mm->nodes, struct nouveau_mm_node, nl_entry); -+ int nodes = 0; -+ + list_for_each_entry(node, &mm->nodes, nl_entry) { + if (nodes++ == mm->heap_nodes) { + printk(KERN_ERR "nouveau_mm in use at destroy time!\n"); @@ -3621,7 +3609,7 @@ index f18cdfc..43a96b9 100644 #define NV50_PDISPLAY_USER(i) ((i) * 0x1000 + 0x00640000) diff --git a/drivers/gpu/drm/nouveau/nouveau_sgdma.c b/drivers/gpu/drm/nouveau/nouveau_sgdma.c -index 2706cb3..b75258a 100644 +index 2706cb3..c8a463b 100644 --- a/drivers/gpu/drm/nouveau/nouveau_sgdma.c +++ b/drivers/gpu/drm/nouveau/nouveau_sgdma.c @@ -12,8 +12,8 @@ struct nouveau_sgdma_be { @@ -3693,7 +3681,7 @@ index 2706cb3..b75258a 100644 } return 0; -@@ -72,25 +57,16 @@ static void +@@ -72,26 +57,20 @@ static void nouveau_sgdma_clear(struct ttm_backend *be) { struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be; @@ -3721,9 +3709,13 @@ index 2706cb3..b75258a 100644 - nvbe->pages = NULL; - nvbe->ttm_alloced = NULL; - nvbe->nr_pages = 0; ++ nvbe->unmap_pages = false; } ++ ++ nvbe->pages = NULL; } + static void diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index 10656e4..82478e0 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c @@ -5809,9 +5801,8 @@ index d43c46c..8c979b3 100644 -{ - struct drm_nouveau_private *dev_priv = dev->dev_private; - int i; -- -- NV_DEBUG(dev, "\n"); +- NV_DEBUG(dev, "\n"); + /* upload context program, initialise ctxctl defaults */ + nv_wr32(dev, 0x400324, 0x00000000); + for (i = 0; i < pgraph->ctxprog_size; i++) @@ -5823,7 +5814,7 @@ index d43c46c..8c979b3 100644 + nv_wr32(dev, 0x400724, 0x00000000); + nv_wr32(dev, 0x40032c, 0x00000000); + nv_wr32(dev, 0x400320, 4); /* CTXCTL_CMD = NEWCTXDMA */ -+ + + /* some unknown zcull magic */ switch (dev_priv->chipset & 0xf0) { case 0x50: @@ -6088,14 +6079,13 @@ index e4b2b9e..618c144 100644 - 0x00, 0x00 - }; - const u32 *map = pll_map; -- ++ struct drm_nouveau_private *dev_priv = dev->dev_private; ++ u32 sctl, sdiv, sclk; + - while (map[1]) { - if (id == map[1]) - return map[0]; - map += 2; -+ struct drm_nouveau_private *dev_priv = dev->dev_private; -+ u32 sctl, sdiv, sclk; -+ + /* refclk for the 0xe8xx plls is a fixed frequency */ + if (clk >= 0x40) { + if (dev_priv->chipset == 0xaf) { @@ -6178,8 +6168,7 @@ index e4b2b9e..618c144 100644 + NV_DEBUG(dev, "no clock for 0x%04x/0x%02x\n", pll, clk); + return 0; + } - -- ret = get_pll_limits(dev, id, &pll); ++ + switch (khz) { + case 27000: + reg->clk = 0x00000100; @@ -6209,7 +6198,8 @@ index e4b2b9e..618c144 100644 + return oclk; + } + } -+ + +- ret = get_pll_limits(dev, id, &pll); + if (!pll) { + NV_ERROR(dev, "bad freq %02x: %d %d\n", clk, khz, sclk); + return -ERANGE; @@ -6325,23 +6315,20 @@ index e4b2b9e..618c144 100644 + int ret; - ret = get_pll_limits(dev, id, &limits); +- if (ret < 0) +- return (ret == -ENOENT) ? NULL : ERR_PTR(ret); + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return ERR_PTR(-ENOMEM); -+ -+ ret = calc_clk(dev, 0x10, 0x4200, perflvl->core, &info->nclk); - if (ret < 0) -- return (ret == -ENOENT) ? NULL : ERR_PTR(ret); -+ goto out; - off = nva3_pm_pll_offset(id); - if (id < 0) - return ERR_PTR(-EINVAL); -+ ret = calc_clk(dev, 0x11, 0x4220, perflvl->shader, &info->sclk); ++ ret = calc_clk(dev, 0x10, 0x4200, perflvl->core, &info->nclk); + if (ret < 0) + goto out; -+ ret = calc_clk(dev, 0x12, 0x4000, perflvl->memory, &info->mclk); ++ ret = calc_clk(dev, 0x11, 0x4220, perflvl->shader, &info->sclk); + if (ret < 0) + goto out; @@ -6353,7 +6340,10 @@ index e4b2b9e..618c144 100644 - pll->src1 = 0x004160 + (off * 4); - pll->ctrl = limits.reg + 0; - pll->coef = limits.reg + 4; -- ++ ret = calc_clk(dev, 0x12, 0x4000, perflvl->memory, &info->mclk); ++ if (ret < 0) ++ goto out; + - /* If target clock is within [-2, 3) MHz of a divisor, we'll - * use that instead of calculating MNP values - */ diff --git a/kernel.spec b/kernel.spec index d233d3f..b6b0ed5 100644 --- a/kernel.spec +++ b/kernel.spec @@ -54,7 +54,7 @@ Summary: The Linux kernel # For non-released -rc kernels, this will be appended after the rcX and # gitX tags, so a 3 here would become part of release "0.rcX.gitX.3" # -%global baserelease 1 +%global baserelease 2 %global fedora_build %{baserelease} # base_sublevel is the kernel version we're starting with and patching @@ -2177,6 +2177,9 @@ fi # and build. %changelog +* Mon Nov 28 2011 Ben Skeggs 3.1.3-2 +- nouveau: fix two instances of an oops in ttm clear() (rhbz#751753) + * Sun Nov 26 2011 Chuck Ebbert 3.1.3-1 - Linux 3.1.3