[kernel/f15] nouveau: fix two instances of an oops in ttm clear() (rhbz#751753)

Josh Boyer jwboyer at fedoraproject.org
Mon Nov 28 21:10:26 UTC 2011


commit 27ec4e1df273046c07768b94c7d6f2662838566c
Author: Ben Skeggs <bskeggs at redhat.com>
Date:   Mon Nov 28 12:10:12 2011 +1000

    nouveau: fix two instances of an oops in ttm clear() (rhbz#751753)

 drm-nouveau-updates.patch |  998 ++++++++++++++++++++++-----------------------
 kernel.spec               |    5 +-
 2 files changed, 498 insertions(+), 505 deletions(-)
---
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 0239834..bd9edeb 100644
--- a/kernel.spec
+++ b/kernel.spec
@@ -42,7 +42,7 @@ Summary: The Linux kernel
 # When changing real_sublevel below, reset this by hand to 1
 # (or to 0 and then use rpmdev-bumpspec).
 #
-%global baserelease 1
+%global baserelease 2
 %global fedora_build %{baserelease}
 
 # real_sublevel is the 3.x kernel version we're starting with
@@ -1878,6 +1878,9 @@ fi
 # and build.
 
 %changelog
+* Mon Nov 28 2011 Ben Skeggs <bskeggs at redhat.com> 3.1.3-2
+- nouveau: fix two instances of an oops in ttm clear() (rhbz#751753)
+
 * Mon Nov 28 2011 Chuck Ebbert <cebbert at redhat.com> 2.6.41.3-1
 - Fake version 2.6.4X by changing UTSNAME
   (instead of changing the internal kernel version)


More information about the scm-commits mailing list