[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#689178: No support for modern nVidia cards (GT 610) - System unusable!



Kees de Jong wrote:

> I'm still forced to the Gnome fall-back mode with nouveau.noaccel=0 as
> kernel parameter. In the attachment is the dmesg output you requested
> with the other parameters. Hope it helps.

Looks like I gave the wrong parameter to set the size of the log
buffer --- the log's truncated at the beginning.  No matter.

The start of a series to backport nvd9 support to the 3.2.y kernel
(for wheezy) is attached.  The first 9 patches are already in wheezy
and only included here for reference.  Patch 10 disables acceleration
on nvd9, both because the free firmware is not finished and because
that means fewer patches to backport to get it working.  The patches
after that are a semi-random collection of patches that seemed vaguely
relevant --- I didn't finish, but I'm sending this out now so they
don't get lost.

An alternative approach would be to take drivers/gpu/drm/nouveau from
3.6 wholesale and make minimal changes to get it working on a 3.2.y
kernel.  That's probably easier to keep maintained, so I might work on
that next.
From: Ben Skeggs <bskeggs@redhat.com>
Date: Fri, 16 Mar 2012 12:40:17 +1000
Subject: drm/nouveau/ttm: always do buffer moves on kernel channel

commit accf94969f226ddfe7dd3a6a76ce093ace839b26 upstream.

There was once good reasons for wanting the drm to be able to use M2MF etc
on user channels, but they're not relevant anymore.  For the general
buffer move case, we've already lost by transferring between vram/sysmem
already so the context switching overhead is minimal in comparison.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
 drivers/gpu/drm/nouveau/nouveau_bo.c  |   11 +++--------
 drivers/gpu/drm/nouveau/nouveau_drv.h |    2 --
 drivers/gpu/drm/nouveau/nouveau_gem.c |   10 +---------
 3 files changed, 4 insertions(+), 19 deletions(-)

diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c
index d5af0893a9e9..671157fa4610 100644
--- a/drivers/gpu/drm/nouveau/nouveau_bo.c
+++ b/drivers/gpu/drm/nouveau/nouveau_bo.c
@@ -685,16 +685,12 @@ nouveau_bo_move_m2mf(struct ttm_buffer_object *bo, int evict, bool intr,
 		     struct ttm_mem_reg *new_mem)
 {
 	struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev);
+	struct nouveau_channel *chan = chan = dev_priv->channel;
 	struct nouveau_bo *nvbo = nouveau_bo(bo);
 	struct ttm_mem_reg *old_mem = &bo->mem;
-	struct nouveau_channel *chan;
 	int ret;
 
-	chan = nvbo->channel;
-	if (!chan) {
-		chan = dev_priv->channel;
-		mutex_lock_nested(&chan->mutex, NOUVEAU_KCHANNEL_MUTEX);
-	}
+	mutex_lock_nested(&chan->mutex, NOUVEAU_KCHANNEL_MUTEX);
 
 	/* create temporary vmas for the transfer and attach them to the
 	 * old nouveau_mem node, these will get cleaned up after ttm has
@@ -726,8 +722,7 @@ nouveau_bo_move_m2mf(struct ttm_buffer_object *bo, int evict, bool intr,
 	}
 
 out:
-	if (chan == dev_priv->channel)
-		mutex_unlock(&chan->mutex);
+	mutex_unlock(&chan->mutex);
 	return ret;
 }
 
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index 4c0be3a4ed88..7b5fc0b0a02c 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -113,8 +113,6 @@ struct nouveau_bo {
 	int pbbo_index;
 	bool validate_mapped;
 
-	struct nouveau_channel *channel;
-
 	struct list_head vma_list;
 	unsigned page_shift;
 
diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c
index 7ce3fde40743..ed52a6f41613 100644
--- a/drivers/gpu/drm/nouveau/nouveau_gem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_gem.c
@@ -426,9 +426,7 @@ validate_list(struct nouveau_channel *chan, struct list_head *list,
 			return ret;
 		}
 
-		nvbo->channel = (b->read_domains & (1 << 31)) ? NULL : chan;
 		ret = nouveau_bo_validate(nvbo, true, false, false);
-		nvbo->channel = NULL;
 		if (unlikely(ret)) {
 			if (ret != -ERESTARTSYS)
 				NV_ERROR(dev, "fail ttm_validate\n");
@@ -678,19 +676,13 @@ nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
 		return PTR_ERR(bo);
 	}
 
-	/* Mark push buffers as being used on PFIFO, the validation code
-	 * will then make sure that if the pushbuf bo moves, that they
-	 * happen on the kernel channel, which will in turn cause a sync
-	 * to happen before we try and submit the push buffer.
-	 */
+	/* Ensure all push buffers are on validate list */
 	for (i = 0; i < req->nr_push; i++) {
 		if (push[i].bo_index >= req->nr_buffers) {
 			NV_ERROR(dev, "push %d buffer not in list\n", i);
 			ret = -EINVAL;
 			goto out_prevalid;
 		}
-
-		bo[push[i].bo_index].read_domains |= (1 << 31);
 	}
 
 	/* Validate buffer list */
-- 
1.7.10.4

From: Ben Skeggs <bskeggs@redhat.com>
Date: Fri, 16 Mar 2012 00:09:54 +1000
Subject: drm/nouveau: remove subchannel names from places where it doesn't matter

commit b5b2e5988bd18a2f6e3f192adf7439599de00d3f upstream.

These are FIFO methods, it doesn't matter what subchannel is being used.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
[jrnieder@gmail.com: backport to pre-3.3 (drm/nvd0/disp: add support
 for page flipping, 2011-11-12) kernels by skipping nvd0_display.c
 part]
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
 drivers/gpu/drm/nouveau/nouveau_drv.h   |   23 ++++++++++++++++++-----
 drivers/gpu/drm/nouveau/nouveau_fence.c |   24 ++++++++++++------------
 drivers/gpu/drm/nouveau/nv50_display.c  |   12 ++++++------
 3 files changed, 36 insertions(+), 23 deletions(-)

diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index 7b5fc0b0a02c..ba7a3eab56f1 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -1660,13 +1660,26 @@ nv44_graph_class(struct drm_device *dev)
 #define NV_MEM_TYPE_VM 0x7f
 #define NV_MEM_COMP_VM 0x03
 
+/* FIFO methods */
+#define NV01_SUBCHAN_OBJECT                                          0x00000000
+#define NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH                          0x00000010
+#define NV84_SUBCHAN_SEMAPHORE_ADDRESS_LOW                           0x00000014
+#define NV84_SUBCHAN_SEMAPHORE_SEQUENCE                              0x00000018
+#define NV84_SUBCHAN_SEMAPHORE_TRIGGER                               0x0000001c
+#define NV84_SUBCHAN_SEMAPHORE_TRIGGER_ACQUIRE_EQUAL                 0x00000001
+#define NV84_SUBCHAN_SEMAPHORE_TRIGGER_WRITE_LONG                    0x00000002
+#define NV84_SUBCHAN_SEMAPHORE_TRIGGER_ACQUIRE_GEQUAL                0x00000004
+#define NV84_SUBCHAN_NOTIFY_INTR                                     0x00000020
+#define NV84_SUBCHAN_WRCACHE_FLUSH                                   0x00000024
+#define NV10_SUBCHAN_REF_CNT                                         0x00000050
+#define NV11_SUBCHAN_DMA_SEMAPHORE                                   0x00000060
+#define NV11_SUBCHAN_SEMAPHORE_OFFSET                                0x00000064
+#define NV11_SUBCHAN_SEMAPHORE_ACQUIRE                               0x00000068
+#define NV11_SUBCHAN_SEMAPHORE_RELEASE                               0x0000006c
+#define NV40_SUBCHAN_YIELD                                           0x00000080
+
 /* NV_SW object class */
 #define NV_SW                                                        0x0000506e
-#define NV_SW_DMA_SEMAPHORE                                          0x00000060
-#define NV_SW_SEMAPHORE_OFFSET                                       0x00000064
-#define NV_SW_SEMAPHORE_ACQUIRE                                      0x00000068
-#define NV_SW_SEMAPHORE_RELEASE                                      0x0000006c
-#define NV_SW_YIELD                                                  0x00000080
 #define NV_SW_DMA_VBLSEM                                             0x0000018c
 #define NV_SW_VBLSEM_OFFSET                                          0x00000400
 #define NV_SW_VBLSEM_RELEASE_VALUE                                   0x00000404
diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c
index 2f6daae68b9d..f676ecd3fd3c 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fence.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fence.c
@@ -165,9 +165,9 @@ nouveau_fence_emit(struct nouveau_fence *fence)
 
 	if (USE_REFCNT(dev)) {
 		if (dev_priv->card_type < NV_C0)
-			BEGIN_RING(chan, NvSubSw, 0x0050, 1);
+			BEGIN_RING(chan, 0, NV10_SUBCHAN_REF_CNT, 1);
 		else
-			BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0050, 1);
+			BEGIN_NVC0(chan, 2, 0, NV10_SUBCHAN_REF_CNT, 1);
 	} else {
 		BEGIN_RING(chan, NvSubSw, 0x0150, 1);
 	}
@@ -344,7 +344,7 @@ semaphore_acquire(struct nouveau_channel *chan, struct nouveau_semaphore *sema)
 		if (ret)
 			return ret;
 
-		BEGIN_RING(chan, NvSubSw, NV_SW_DMA_SEMAPHORE, 3);
+		BEGIN_RING(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 3);
 		OUT_RING  (chan, NvSema);
 		OUT_RING  (chan, offset);
 		OUT_RING  (chan, 1);
@@ -354,9 +354,9 @@ semaphore_acquire(struct nouveau_channel *chan, struct nouveau_semaphore *sema)
 		if (ret)
 			return ret;
 
-		BEGIN_RING(chan, NvSubSw, NV_SW_DMA_SEMAPHORE, 1);
+		BEGIN_RING(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 1);
 		OUT_RING  (chan, chan->vram_handle);
-		BEGIN_RING(chan, NvSubSw, 0x0010, 4);
+		BEGIN_RING(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4);
 		OUT_RING  (chan, upper_32_bits(offset));
 		OUT_RING  (chan, lower_32_bits(offset));
 		OUT_RING  (chan, 1);
@@ -366,7 +366,7 @@ semaphore_acquire(struct nouveau_channel *chan, struct nouveau_semaphore *sema)
 		if (ret)
 			return ret;
 
-		BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0010, 4);
+		BEGIN_NVC0(chan, 2, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4);
 		OUT_RING  (chan, upper_32_bits(offset));
 		OUT_RING  (chan, lower_32_bits(offset));
 		OUT_RING  (chan, 1);
@@ -397,10 +397,10 @@ semaphore_release(struct nouveau_channel *chan, struct nouveau_semaphore *sema)
 		if (ret)
 			return ret;
 
-		BEGIN_RING(chan, NvSubSw, NV_SW_DMA_SEMAPHORE, 2);
+		BEGIN_RING(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 2);
 		OUT_RING  (chan, NvSema);
 		OUT_RING  (chan, offset);
-		BEGIN_RING(chan, NvSubSw, NV_SW_SEMAPHORE_RELEASE, 1);
+		BEGIN_RING(chan, 0, NV11_SUBCHAN_SEMAPHORE_RELEASE, 1);
 		OUT_RING  (chan, 1);
 	} else
 	if (dev_priv->chipset < 0xc0) {
@@ -408,9 +408,9 @@ semaphore_release(struct nouveau_channel *chan, struct nouveau_semaphore *sema)
 		if (ret)
 			return ret;
 
-		BEGIN_RING(chan, NvSubSw, NV_SW_DMA_SEMAPHORE, 1);
+		BEGIN_RING(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 1);
 		OUT_RING  (chan, chan->vram_handle);
-		BEGIN_RING(chan, NvSubSw, 0x0010, 4);
+		BEGIN_RING(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4);
 		OUT_RING  (chan, upper_32_bits(offset));
 		OUT_RING  (chan, lower_32_bits(offset));
 		OUT_RING  (chan, 1);
@@ -420,7 +420,7 @@ semaphore_release(struct nouveau_channel *chan, struct nouveau_semaphore *sema)
 		if (ret)
 			return ret;
 
-		BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0010, 4);
+		BEGIN_NVC0(chan, 2, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4);
 		OUT_RING  (chan, upper_32_bits(offset));
 		OUT_RING  (chan, lower_32_bits(offset));
 		OUT_RING  (chan, 1);
@@ -510,7 +510,7 @@ nouveau_fence_channel_init(struct nouveau_channel *chan)
 		if (ret)
 			return ret;
 
-		BEGIN_RING(chan, NvSubSw, 0, 1);
+		BEGIN_RING(chan, NvSubSw, NV01_SUBCHAN_OBJECT, 1);
 		OUT_RING  (chan, NvSw);
 		FIRE_RING (chan);
 	}
diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c
index 06de250fe617..8dd50f027bbe 100644
--- a/drivers/gpu/drm/nouveau/nv50_display.c
+++ b/drivers/gpu/drm/nouveau/nv50_display.c
@@ -413,15 +413,15 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,
 		}
 
 		if (dev_priv->chipset < 0xc0) {
-			BEGIN_RING(chan, NvSubSw, 0x0060, 2);
+			BEGIN_RING(chan, 0, 0x0060, 2);
 			OUT_RING  (chan, NvEvoSema0 + nv_crtc->index);
 			OUT_RING  (chan, dispc->sem.offset);
-			BEGIN_RING(chan, NvSubSw, 0x006c, 1);
+			BEGIN_RING(chan, 0, 0x006c, 1);
 			OUT_RING  (chan, 0xf00d0000 | dispc->sem.value);
-			BEGIN_RING(chan, NvSubSw, 0x0064, 2);
+			BEGIN_RING(chan, 0, 0x0064, 2);
 			OUT_RING  (chan, dispc->sem.offset ^ 0x10);
 			OUT_RING  (chan, 0x74b1e000);
-			BEGIN_RING(chan, NvSubSw, 0x0060, 1);
+			BEGIN_RING(chan, 0, 0x0060, 1);
 			if (dev_priv->chipset < 0x84)
 				OUT_RING  (chan, NvSema);
 			else
@@ -429,12 +429,12 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,
 		} else {
 			u64 offset = chan->dispc_vma[nv_crtc->index].offset;
 			offset += dispc->sem.offset;
-			BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0010, 4);
+			BEGIN_NVC0(chan, 2, 0, 0x0010, 4);
 			OUT_RING  (chan, upper_32_bits(offset));
 			OUT_RING  (chan, lower_32_bits(offset));
 			OUT_RING  (chan, 0xf00d0000 | dispc->sem.value);
 			OUT_RING  (chan, 0x1002);
-			BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0010, 4);
+			BEGIN_NVC0(chan, 2, 0, 0x0010, 4);
 			OUT_RING  (chan, upper_32_bits(offset));
 			OUT_RING  (chan, lower_32_bits(offset ^ 0x10));
 			OUT_RING  (chan, 0x74b1e000);
-- 
1.7.10.4

From: Ben Skeggs <bskeggs@redhat.com>
Date: Wed, 21 Mar 2012 13:51:03 +1000
Subject: drm/nouveau: move fence sequence check to start of loop

commit b08abd4e9a11d637d3c2ff52b2ebbc1b3f686d06 upstream.

I want to be able to use REF_CNT from other places in the kernel without
pushing a fence object onto the list of emitted fences.

The current code makes an assumption that every time the acked sequence is
bumped that there's at least one fence on the list that'll be signalled.

This will no longer be true in the near future.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
 drivers/gpu/drm/nouveau/nouveau_fence.c |   11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c
index f676ecd3fd3c..c1dc20f6cb85 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fence.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fence.c
@@ -93,18 +93,17 @@ nouveau_fence_update(struct nouveau_channel *chan)
 	}
 
 	list_for_each_entry_safe(fence, tmp, &chan->fence.pending, entry) {
-		sequence = fence->sequence;
+		if (fence->sequence > chan->fence.sequence_ack)
+			break;
+
 		fence->signalled = true;
 		list_del(&fence->entry);
-
-		if (unlikely(fence->work))
+		if (fence->work)
 			fence->work(fence->priv, true);
 
 		kref_put(&fence->refcount, nouveau_fence_del);
-
-		if (sequence == chan->fence.sequence_ack)
-			break;
 	}
+
 out:
 	spin_unlock(&chan->fence.lock);
 }
-- 
1.7.10.4

From: Ben Skeggs <bskeggs@redhat.com>
Date: Wed, 21 Mar 2012 13:53:49 +1000
Subject: drm/nvc0-/disp: reimplement flip completion method as fifo method

commit d5316e251230c4e54a157349a362229c3d4daa32 upstream.

Removes need for M2MF subchannel usage on NVC0+.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
 drivers/gpu/drm/nouveau/nouveau_display.c |   14 +++++++----
 drivers/gpu/drm/nouveau/nouveau_drv.h     |    1 +
 drivers/gpu/drm/nouveau/nvc0_fifo.c       |   36 +++++++++++++++++++++++++----
 drivers/gpu/drm/nouveau/nvc0_graph.c      |    9 --------
 4 files changed, 42 insertions(+), 18 deletions(-)

diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
index 6adef062e986..e4cbdfc3ae19 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.c
+++ b/drivers/gpu/drm/nouveau/nouveau_display.c
@@ -243,15 +243,19 @@ nouveau_page_flip_emit(struct nouveau_channel *chan,
 		goto fail;
 
 	/* Emit the pageflip */
-	ret = RING_SPACE(chan, 2);
+	ret = RING_SPACE(chan, 3);
 	if (ret)
 		goto fail;
 
-	if (dev_priv->card_type < NV_C0)
+	if (dev_priv->card_type < NV_C0) {
 		BEGIN_RING(chan, NvSubSw, NV_SW_PAGE_FLIP, 1);
-	else
-		BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0500, 1);
-	OUT_RING  (chan, 0);
+		OUT_RING  (chan, 0x00000000);
+		OUT_RING  (chan, 0x00000000);
+	} else {
+		BEGIN_NVC0(chan, 2, 0, NV10_SUBCHAN_REF_CNT, 1);
+		OUT_RING  (chan, ++chan->fence.sequence);
+		BEGIN_NVC0(chan, 8, 0, NVSW_SUBCHAN_PAGE_FLIP, 0x0000);
+	}
 	FIRE_RING (chan);
 
 	ret = nouveau_fence_new(chan, pfence, true);
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index ba7a3eab56f1..9edc3cfbcab1 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -1672,6 +1672,7 @@ nv44_graph_class(struct drm_device *dev)
 #define NV84_SUBCHAN_NOTIFY_INTR                                     0x00000020
 #define NV84_SUBCHAN_WRCACHE_FLUSH                                   0x00000024
 #define NV10_SUBCHAN_REF_CNT                                         0x00000050
+#define NVSW_SUBCHAN_PAGE_FLIP                                       0x00000054
 #define NV11_SUBCHAN_DMA_SEMAPHORE                                   0x00000060
 #define NV11_SUBCHAN_SEMAPHORE_OFFSET                                0x00000064
 #define NV11_SUBCHAN_SEMAPHORE_ACQUIRE                               0x00000068
diff --git a/drivers/gpu/drm/nouveau/nvc0_fifo.c b/drivers/gpu/drm/nouveau/nvc0_fifo.c
index dcbe0d5d0241..50d68a7a1379 100644
--- a/drivers/gpu/drm/nouveau/nvc0_fifo.c
+++ b/drivers/gpu/drm/nouveau/nvc0_fifo.c
@@ -436,6 +436,24 @@ nvc0_fifo_isr_vm_fault(struct drm_device *dev, int unit)
 	printk(" on channel 0x%010llx\n", (u64)inst << 12);
 }
 
+static int
+nvc0_fifo_page_flip(struct drm_device *dev, u32 chid)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_channel *chan = NULL;
+	unsigned long flags;
+	int ret = -EINVAL;
+
+	spin_lock_irqsave(&dev_priv->channels.lock, flags);
+	if (likely(chid >= 0 && chid < dev_priv->engine.fifo.channels)) {
+		chan = dev_priv->channels.ptr[chid];
+		if (likely(chan))
+			ret = nouveau_finish_page_flip(chan, NULL);
+	}
+	spin_unlock_irqrestore(&dev_priv->channels.lock, flags);
+	return ret;
+}
+
 static void
 nvc0_fifo_isr_subfifo_intr(struct drm_device *dev, int unit)
 {
@@ -445,11 +463,21 @@ nvc0_fifo_isr_subfifo_intr(struct drm_device *dev, int unit)
 	u32 chid = nv_rd32(dev, 0x040120 + (unit * 0x2000)) & 0x7f;
 	u32 subc = (addr & 0x00070000);
 	u32 mthd = (addr & 0x00003ffc);
+	u32 show = stat;
 
-	NV_INFO(dev, "PSUBFIFO %d:", unit);
-	nouveau_bitfield_print(nvc0_fifo_subfifo_intr, stat);
-	NV_INFO(dev, "PSUBFIFO %d: ch %d subc %d mthd 0x%04x data 0x%08x\n",
-		unit, chid, subc, mthd, data);
+	if (stat & 0x00200000) {
+		if (mthd == 0x0054) {
+			if (!nvc0_fifo_page_flip(dev, chid))
+				show &= ~0x00200000;
+		}
+	}
+
+	if (show) {
+		NV_INFO(dev, "PFIFO%d:", unit);
+		nouveau_bitfield_print(nvc0_fifo_subfifo_intr, show);
+		NV_INFO(dev, "PFIFO%d: ch %d subc %d mthd 0x%04x data 0x%08x\n",
+			     unit, chid, subc, mthd, data);
+	}
 
 	nv_wr32(dev, 0x0400c0 + (unit * 0x2000), 0x80600008);
 	nv_wr32(dev, 0x040108 + (unit * 0x2000), stat);
diff --git a/drivers/gpu/drm/nouveau/nvc0_graph.c b/drivers/gpu/drm/nouveau/nvc0_graph.c
index ecfafd70cf0e..cb596e9330a9 100644
--- a/drivers/gpu/drm/nouveau/nvc0_graph.c
+++ b/drivers/gpu/drm/nouveau/nvc0_graph.c
@@ -333,14 +333,6 @@ nvc0_graph_fini(struct drm_device *dev, int engine, bool suspend)
 	return 0;
 }
 
-static int
-nvc0_graph_mthd_page_flip(struct nouveau_channel *chan,
-			  u32 class, u32 mthd, u32 data)
-{
-	nouveau_finish_page_flip(chan, NULL);
-	return 0;
-}
-
 static void
 nvc0_graph_init_obj418880(struct drm_device *dev)
 {
@@ -887,7 +879,6 @@ nvc0_graph_create(struct drm_device *dev)
 
 	NVOBJ_CLASS(dev, 0x902d, GR); /* 2D */
 	NVOBJ_CLASS(dev, 0x9039, GR); /* M2MF */
-	NVOBJ_MTHD (dev, 0x9039, 0x0500, nvc0_graph_mthd_page_flip);
 	NVOBJ_CLASS(dev, 0x9097, GR); /* 3D */
 	if (fermi >= 0x9197)
 		NVOBJ_CLASS(dev, 0x9197, GR); /* 3D (NVC1-) */
-- 
1.7.10.4

From: Ben Skeggs <bskeggs@redhat.com>
Date: Sun, 18 Mar 2012 00:40:41 +1000
Subject: drm/nouveau: remove m2mf creation on userspace channels

commit 48aca13f0167ae78c28c6b48d82a157a6692eecb upstream.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
 drivers/gpu/drm/nouveau/nouveau_channel.c |   19 ++++--
 drivers/gpu/drm/nouveau/nouveau_dma.c     |   61 +------------------
 drivers/gpu/drm/nouveau/nouveau_drv.h     |    3 +-
 drivers/gpu/drm/nouveau/nouveau_state.c   |   91 ++++++++++++++++++++++++-----
 4 files changed, 93 insertions(+), 81 deletions(-)

diff --git a/drivers/gpu/drm/nouveau/nouveau_channel.c b/drivers/gpu/drm/nouveau/nouveau_channel.c
index bb6ec9ef8676..b78d20e9dce0 100644
--- a/drivers/gpu/drm/nouveau/nouveau_channel.c
+++ b/drivers/gpu/drm/nouveau/nouveau_channel.c
@@ -122,7 +122,7 @@ nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret,
 	struct nouveau_fpriv *fpriv = nouveau_fpriv(file_priv);
 	struct nouveau_channel *chan;
 	unsigned long flags;
-	int ret;
+	int ret, i;
 
 	/* allocate and lock channel structure */
 	chan = kzalloc(sizeof(*chan), GFP_KERNEL);
@@ -184,7 +184,7 @@ nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret,
 		return ret;
 	}
 
-	nouveau_dma_pre_init(chan);
+	nouveau_dma_init(chan);
 	chan->user_put = 0x40;
 	chan->user_get = 0x44;
 
@@ -200,9 +200,18 @@ nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret,
 
 	pfifo->reassign(dev, true);
 
-	ret = nouveau_dma_init(chan);
-	if (!ret)
-		ret = nouveau_fence_channel_init(chan);
+	/* Insert NOPs for NOUVEAU_DMA_SKIPS */
+	ret = RING_SPACE(chan, NOUVEAU_DMA_SKIPS);
+	if (ret) {
+		nouveau_channel_put(&chan);
+		return ret;
+	}
+
+	for (i = 0; i < NOUVEAU_DMA_SKIPS; i++)
+		OUT_RING  (chan, 0x00000000);
+	FIRE_RING(chan);
+
+	ret = nouveau_fence_channel_init(chan);
 	if (ret) {
 		nouveau_channel_put(&chan);
 		return ret;
diff --git a/drivers/gpu/drm/nouveau/nouveau_dma.c b/drivers/gpu/drm/nouveau/nouveau_dma.c
index 00bc6eaad558..b911273c3c3d 100644
--- a/drivers/gpu/drm/nouveau/nouveau_dma.c
+++ b/drivers/gpu/drm/nouveau/nouveau_dma.c
@@ -31,7 +31,7 @@
 #include "nouveau_ramht.h"
 
 void
-nouveau_dma_pre_init(struct nouveau_channel *chan)
+nouveau_dma_init(struct nouveau_channel *chan)
 {
 	struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
 	struct nouveau_bo *pushbuf = chan->pushbuf_bo;
@@ -54,65 +54,6 @@ nouveau_dma_pre_init(struct nouveau_channel *chan)
 	chan->dma.free = chan->dma.max - chan->dma.cur;
 }
 
-int
-nouveau_dma_init(struct nouveau_channel *chan)
-{
-	struct drm_device *dev = chan->dev;
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	int ret, i;
-
-	if (dev_priv->card_type >= NV_C0) {
-		ret = nouveau_gpuobj_gr_new(chan, 0x9039, 0x9039);
-		if (ret)
-			return ret;
-
-		ret = RING_SPACE(chan, 2);
-		if (ret)
-			return ret;
-
-		BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0000, 1);
-		OUT_RING  (chan, 0x00009039);
-		FIRE_RING (chan);
-		return 0;
-	}
-
-	/* Create NV_MEMORY_TO_MEMORY_FORMAT for buffer moves */
-	ret = nouveau_gpuobj_gr_new(chan, NvM2MF, dev_priv->card_type < NV_50 ?
-				    0x0039 : 0x5039);
-	if (ret)
-		return ret;
-
-	/* NV_MEMORY_TO_MEMORY_FORMAT requires a notifier object */
-	ret = nouveau_notifier_alloc(chan, NvNotify0, 32, 0xfe0, 0x1000,
-				     &chan->m2mf_ntfy);
-	if (ret)
-		return ret;
-
-	/* Insert NOPS for NOUVEAU_DMA_SKIPS */
-	ret = RING_SPACE(chan, NOUVEAU_DMA_SKIPS);
-	if (ret)
-		return ret;
-
-	for (i = 0; i < NOUVEAU_DMA_SKIPS; i++)
-		OUT_RING(chan, 0);
-
-	/* Initialise NV_MEMORY_TO_MEMORY_FORMAT */
-	ret = RING_SPACE(chan, 6);
-	if (ret)
-		return ret;
-	BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_NAME, 1);
-	OUT_RING  (chan, NvM2MF);
-	BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_DMA_NOTIFY, 3);
-	OUT_RING  (chan, NvNotify0);
-	OUT_RING  (chan, chan->vram_handle);
-	OUT_RING  (chan, chan->gart_handle);
-
-	/* Sit back and pray the channel works.. */
-	FIRE_RING(chan);
-
-	return 0;
-}
-
 void
 OUT_RINGp(struct nouveau_channel *chan, const void *data, unsigned nr_dwords)
 {
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index 9edc3cfbcab1..f1dd6b55f357 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -1030,8 +1030,7 @@ nouveau_debugfs_channel_fini(struct nouveau_channel *chan)
 #endif
 
 /* nouveau_dma.c */
-extern void nouveau_dma_pre_init(struct nouveau_channel *);
-extern int  nouveau_dma_init(struct nouveau_channel *);
+extern void nouveau_dma_init(struct nouveau_channel *);
 extern int  nouveau_dma_wait(struct nouveau_channel *, int slots, int size);
 
 /* nouveau_acpi.c */
diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c
index d8831ab42bb9..57623378593c 100644
--- a/drivers/gpu/drm/nouveau/nouveau_state.c
+++ b/drivers/gpu/drm/nouveau/nouveau_state.c
@@ -547,6 +547,75 @@ static bool nouveau_switcheroo_can_switch(struct pci_dev *pdev)
 	return can_switch;
 }
 
+static void
+nouveau_card_channel_fini(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+	if (dev_priv->channel)
+		nouveau_channel_put_unlocked(&dev_priv->channel);
+}
+
+static int
+nouveau_card_channel_init(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_channel *chan;
+	int ret, oclass;
+
+	ret = nouveau_channel_alloc(dev, &chan, NULL, NvDmaFB, NvDmaTT);
+	dev_priv->channel = chan;
+	if (ret)
+		return ret;
+
+	mutex_unlock(&dev_priv->channel->mutex);
+
+	if (dev_priv->card_type <= NV_50) {
+		if (dev_priv->card_type < NV_50)
+			oclass = 0x0039;
+		else
+			oclass = 0x5039;
+
+		ret = nouveau_gpuobj_gr_new(chan, NvM2MF, oclass);
+		if (ret)
+			goto error;
+
+		ret = nouveau_notifier_alloc(chan, NvNotify0, 32, 0xfe0, 0x1000,
+					     &chan->m2mf_ntfy);
+		if (ret)
+			goto error;
+
+		ret = RING_SPACE(chan, 6);
+		if (ret)
+			goto error;
+
+		BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_NAME, 1);
+		OUT_RING  (chan, NvM2MF);
+		BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_DMA_NOTIFY, 3);
+		OUT_RING  (chan, NvNotify0);
+		OUT_RING  (chan, chan->vram_handle);
+		OUT_RING  (chan, chan->gart_handle);
+	} else
+	if (dev_priv->card_type <= NV_C0) {
+		ret = nouveau_gpuobj_gr_new(chan, 0x9039, 0x9039);
+		if (ret)
+			goto error;
+
+		ret = RING_SPACE(chan, 2);
+		if (ret)
+			goto error;
+
+		BEGIN_NVC0(chan, 2, NvSubM2MF, 0x0000, 1);
+		OUT_RING  (chan, 0x00009039);
+	}
+
+	FIRE_RING (chan);
+error:
+	if (ret)
+		nouveau_card_channel_fini(dev);
+	return ret;
+}
+
 int
 nouveau_card_init(struct drm_device *dev)
 {
@@ -738,17 +807,14 @@ nouveau_card_init(struct drm_device *dev)
 
 	nouveau_backlight_init(dev);
 
-	if (dev_priv->eng[NVOBJ_ENGINE_GR]) {
-		ret = nouveau_fence_init(dev);
-		if (ret)
-			goto out_disp;
+	ret = nouveau_fence_init(dev);
+	if (ret)
+		goto out_disp;
 
-		ret = nouveau_channel_alloc(dev, &dev_priv->channel, NULL,
-					    NvDmaFB, NvDmaTT);
+	if (!dev_priv->noaccel) {
+		ret = nouveau_card_channel_init(dev);
 		if (ret)
 			goto out_fence;
-
-		mutex_unlock(&dev_priv->channel->mutex);
 	}
 
 	if (dev->mode_config.num_crtc) {
@@ -763,7 +829,7 @@ nouveau_card_init(struct drm_device *dev)
 	return 0;
 
 out_chan:
-	nouveau_channel_put_unlocked(&dev_priv->channel);
+	nouveau_card_channel_fini(dev);
 out_fence:
 	nouveau_fence_fini(dev);
 out_disp:
@@ -823,11 +889,8 @@ static void nouveau_card_takedown(struct drm_device *dev)
 		drm_vblank_cleanup(dev);
 	}
 
-	if (dev_priv->channel) {
-		nouveau_channel_put_unlocked(&dev_priv->channel);
-		nouveau_fence_fini(dev);
-	}
-
+	nouveau_card_channel_fini(dev);
+	nouveau_fence_fini(dev);
 	nouveau_backlight_exit(dev);
 	engine->display.destroy(dev);
 	drm_mode_config_cleanup(dev);
-- 
1.7.10.4

From: Ben Skeggs <bskeggs@redhat.com>
Date: Thu, 29 Mar 2012 20:24:34 +1000
Subject: drm/nouveau: inform userspace of relaxed kernel subchannel requirements

commit 02bfc2881e0d5b23147211bb6420798d946a7b5c upstream.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
 drivers/gpu/drm/nouveau/nouveau_channel.c |   11 ++---------
 1 file changed, 2 insertions(+), 9 deletions(-)

diff --git a/drivers/gpu/drm/nouveau/nouveau_channel.c b/drivers/gpu/drm/nouveau/nouveau_channel.c
index b78d20e9dce0..930db08a9b81 100644
--- a/drivers/gpu/drm/nouveau/nouveau_channel.c
+++ b/drivers/gpu/drm/nouveau/nouveau_channel.c
@@ -434,18 +434,11 @@ nouveau_ioctl_fifo_alloc(struct drm_device *dev, void *data,
 	}
 
 	if (dev_priv->card_type < NV_C0) {
-		init->subchan[0].handle = NvM2MF;
-		if (dev_priv->card_type < NV_50)
-			init->subchan[0].grclass = 0x0039;
-		else
-			init->subchan[0].grclass = 0x5039;
+		init->subchan[0].handle = 0x00000000;
+		init->subchan[0].grclass = 0x0000;
 		init->subchan[1].handle = NvSw;
 		init->subchan[1].grclass = NV_SW;
 		init->nr_subchan = 2;
-	} else {
-		init->subchan[0].handle  = 0x9039;
-		init->subchan[0].grclass = 0x9039;
-		init->nr_subchan = 1;
 	}
 
 	/* Named memory object area */
-- 
1.7.10.4

From: Ben Skeggs <bskeggs@redhat.com>
Date: Fri, 16 Mar 2012 13:45:09 +1000
Subject: drm/nouveau: oops, increase channel dispc_vma to 4

commit 27100ac95a8eee0b083e46bfa67b229ac641d28c upstream.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
 drivers/gpu/drm/nouveau/nouveau_drv.h |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index f1dd6b55f357..0aa55d057ece 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -290,7 +290,7 @@ struct nouveau_channel {
 
 	uint32_t sw_subchannel[8];
 
-	struct nouveau_vma dispc_vma[2];
+	struct nouveau_vma dispc_vma[4];
 	struct {
 		struct nouveau_gpuobj *vblsem;
 		uint32_t vblsem_head;
-- 
1.7.10.4

From: Ben Skeggs <bskeggs@redhat.com>
Date: Fri, 16 Mar 2012 15:32:16 +1000
Subject: drm/nvd0/disp: ignore clock set if no pclk

commit dd62608bcc8a629c4c583fb50b90003fd5213516 upstream.

This happens somehow during init on a machine I have, and leads to a
divide-by-zero.

Lets avoid that...

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
 drivers/gpu/drm/nouveau/nvd0_display.c |    4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c
index 3002d8283a1a..cfa95d700269 100644
--- a/drivers/gpu/drm/nouveau/nvd0_display.c
+++ b/drivers/gpu/drm/nouveau/nvd0_display.c
@@ -1048,7 +1048,9 @@ nvd0_display_unk2_handler(struct drm_device *dev, u32 crtc, u32 mask)
 	}
 
 	pclk = nv_rd32(dev, 0x660450 + (crtc * 0x300)) / 1000;
-	if (mask & 0x00010000) {
+	NV_DEBUG_KMS(dev, "PDISP: crtc %d pclk %d mask 0x%08x\n",
+			  crtc, pclk, mask);
+	if (pclk && (mask & 0x00010000)) {
 		nv50_crtc_set_clock(dev, crtc, pclk);
 	}
 
-- 
1.7.10.4

From: Ben Skeggs <bskeggs@redhat.com>
Date: Fri, 16 Mar 2012 12:44:34 +1000
Subject: drm/nouveau: bump version to 1.0.0

commit f887c425f9eeed8ffbca64c8be45da62b07096c0 upstream.

The time has come to get a proper version number that we can change to
indicate new features etc, rather than the lock-step 0.0.XX that we
previously had.

libdrm has recognised this version as compatible with 0.0.16 since 2.4.22,
so hopefully any breakage people see should be very minimal.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
 drivers/gpu/drm/nouveau/nouveau_drv.h |    8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index 0aa55d057ece..0a9e5fe9f251 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -26,15 +26,15 @@
 #define __NOUVEAU_DRV_H__
 
 #define DRIVER_AUTHOR		"Stephane Marchesin"
-#define DRIVER_EMAIL		"dri-devel@lists.sourceforge.net"
+#define DRIVER_EMAIL		"nouveau@lists.freedesktop.org"
 
 #define DRIVER_NAME		"nouveau"
 #define DRIVER_DESC		"nVidia Riva/TNT/GeForce"
-#define DRIVER_DATE		"20090420"
+#define DRIVER_DATE		"20120316"
 
-#define DRIVER_MAJOR		0
+#define DRIVER_MAJOR		1
 #define DRIVER_MINOR		0
-#define DRIVER_PATCHLEVEL	16
+#define DRIVER_PATCHLEVEL	0
 
 #define NOUVEAU_FAMILY   0x0000FFFF
 #define NOUVEAU_FLAGS    0xFFFF0000
-- 
1.7.10.4

From: Ben Skeggs <bskeggs@redhat.com>
Date: Mon, 11 Jul 2011 15:57:54 +1000
Subject: drm/nvc0: disable accelaration for nvd9 cards

Extracted from upstream commit 06784090ecb3f925616fc797164a74b03d5c0968
which adds partial nvd9 support.

Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
 drivers/gpu/drm/nouveau/nouveau_state.c |    4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c
index 57623378593c..387e13baf1e0 100644
--- a/drivers/gpu/drm/nouveau/nouveau_state.c
+++ b/drivers/gpu/drm/nouveau/nouveau_state.c
@@ -1173,13 +1173,11 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
 	dev_priv->noaccel = !!nouveau_noaccel;
 	if (nouveau_noaccel == -1) {
 		switch (dev_priv->chipset) {
-#if 0
-		case 0xXX: /* known broken */
+		case 0xd9: /* known broken */
 			NV_INFO(dev, "acceleration disabled by default, pass "
 				     "noaccel=0 to force enable\n");
 			dev_priv->noaccel = true;
 			break;
-#endif
 		default:
 			dev_priv->noaccel = false;
 			break;
-- 
1.7.10.4

From: Ben Skeggs <bskeggs@redhat.com>
Date: Mon, 6 Feb 2012 11:42:29 +1000
Subject: drm/nvd9/pm: oops, fix timing calc

commit a94ba1fcac417d0b72f73fb77e730279ca9203c3 upstream.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
[jrnieder@gmail.com: backport to pre-3.4 (drm/nouveau/pm: calculate
 memory timings at perflvl creation time, 2012-01-17) kernels]
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
 drivers/gpu/drm/nouveau/nouveau_mem.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c
index 36bec4807701..8dbe516df0e5 100644
--- a/drivers/gpu/drm/nouveau/nouveau_mem.c
+++ b/drivers/gpu/drm/nouveau/nouveau_mem.c
@@ -693,7 +693,7 @@ nouveau_mem_timing_init(struct drm_device *dev)
 			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){
 			nv50_mem_timing_entry(dev,&P,hdr,(struct nouveau_pm_tbl_entry*) entry,magic_number,&pm->memtimings.timing[i]);
-		} else if(dev_priv->card_type == NV_C0) {
+		} else if(dev_priv->card_type == NV_C0 || dev_priv->card_type == NV_D0) {
 			nvc0_mem_timing_entry(dev,hdr,(struct nouveau_pm_tbl_entry*) entry,&pm->memtimings.timing[i]);
 		}
 	}
-- 
1.7.10.4

From: Ben Skeggs <bskeggs@redhat.com>
Date: Fri, 11 Nov 2011 20:26:44 +1000
Subject: drm/nvd0/disp: hook evo up to debugging

commit 27517ddbdcc59ab43ed18d7bbe53680d428bbd20 upstream.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
 drivers/gpu/drm/nouveau/nvd0_display.c |   13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c
index cfa95d700269..720920796ccb 100644
--- a/drivers/gpu/drm/nouveau/nvd0_display.c
+++ b/drivers/gpu/drm/nouveau/nvd0_display.c
@@ -84,6 +84,9 @@ evo_wait(struct drm_device *dev, int id, int nr)
 		put = 0;
 	}
 
+	if (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO)
+		NV_INFO(dev, "Evo%d: %p START\n", id, disp->evo[id].ptr + put);
+
 	return disp->evo[id].ptr + put;
 }
 
@@ -91,6 +94,16 @@ static void
 evo_kick(u32 *push, struct drm_device *dev, int id)
 {
 	struct nvd0_display *disp = nvd0_display(dev);
+
+	if (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO) {
+		u32 curp = nv_rd32(dev, 0x640000 + (id * 0x1000)) >> 2;
+		u32 *cur = disp->evo[id].ptr + curp;
+
+		while (cur < push)
+			NV_INFO(dev, "Evo%d: 0x%08x\n", id, *cur++);
+		NV_INFO(dev, "Evo%d: %p KICK!\n", id, push);
+	}
+
 	nv_wr32(dev, 0x640000 + (id * 0x1000), (push - disp->evo[id].ptr) << 2);
 }
 
-- 
1.7.10.4

From: Martin Peres <martin.peres@ensi-bourges.fr>
Date: Sat, 22 Oct 2011 01:40:40 +0200
Subject: drm/nvd0: read temperature as we did on nv84+ boards

commit 6109183794a711d80c08705d477d2a19b437d5c1 upstream.

Signed-off-by: Martin Peres <martin.peres@labri.fr>
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
 drivers/gpu/drm/nouveau/nouveau_state.c |    1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c
index 387e13baf1e0..9faf952ca68f 100644
--- a/drivers/gpu/drm/nouveau/nouveau_state.c
+++ b/drivers/gpu/drm/nouveau/nouveau_state.c
@@ -471,6 +471,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
 		engine->vram.get		= nvc0_vram_new;
 		engine->vram.put		= nv50_vram_del;
 		engine->vram.flags_valid	= nvc0_vram_flags_valid;
+		engine->pm.temp_get		= nv84_temp_get;
 		engine->pm.clocks_get		= nvc0_pm_clocks_get;
 		engine->pm.voltage_get		= nouveau_voltage_gpio_get;
 		engine->pm.voltage_set		= nouveau_voltage_gpio_set;
-- 
1.7.10.4

From: Ben Skeggs <bskeggs@redhat.com>
Date: Wed, 5 Oct 2011 11:05:07 +1000
Subject: drm/nouveau/hdmi: build ELD from EDID, notify audio driver of its presence

commit 25575b414c2137a16b313bdfdeab570b70080f37 upstream.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
 drivers/gpu/drm/nouveau/Makefile       |    2 +-
 drivers/gpu/drm/nouveau/nouveau_drv.h  |    3 +
 drivers/gpu/drm/nouveau/nouveau_hdmi.c |  103 ++++++++++++++++++++++++++++++++
 drivers/gpu/drm/nouveau/nv50_sor.c     |    4 ++
 4 files changed, 111 insertions(+), 1 deletion(-)
 create mode 100644 drivers/gpu/drm/nouveau/nouveau_hdmi.c

diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile
index 35ef5b1e3566..1f79ac52c8d1 100644
--- a/drivers/gpu/drm/nouveau/Makefile
+++ b/drivers/gpu/drm/nouveau/Makefile
@@ -9,7 +9,7 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \
              nouveau_bo.o nouveau_fence.o nouveau_gem.o nouveau_ttm.o \
              nouveau_hw.o nouveau_calc.o nouveau_bios.o nouveau_i2c.o \
              nouveau_display.o nouveau_connector.o nouveau_fbcon.o \
-             nouveau_dp.o nouveau_ramht.o \
+             nouveau_hdmi.o nouveau_dp.o nouveau_ramht.o \
 	     nouveau_pm.o nouveau_volt.o nouveau_perf.o nouveau_temp.o \
 	     nouveau_mm.o nouveau_vm.o \
              nv04_timer.o \
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index 0a9e5fe9f251..0026976eee67 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -1093,6 +1093,9 @@ int nouveau_ttm_global_init(struct drm_nouveau_private *);
 void nouveau_ttm_global_release(struct drm_nouveau_private *);
 int nouveau_ttm_mmap(struct file *, struct vm_area_struct *);
 
+/* nouveau_hdmi.c */
+void nouveau_hdmi_mode_set(struct drm_encoder *, struct drm_display_mode *);
+
 /* nouveau_dp.c */
 int nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr,
 		     uint8_t *data, int data_nr);
diff --git a/drivers/gpu/drm/nouveau/nouveau_hdmi.c b/drivers/gpu/drm/nouveau/nouveau_hdmi.c
new file mode 100644
index 000000000000..489a2418a227
--- /dev/null
+++ b/drivers/gpu/drm/nouveau/nouveau_hdmi.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2011 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors: Ben Skeggs
+ */
+
+#include "drmP.h"
+#include "nouveau_drv.h"
+#include "nouveau_connector.h"
+#include "nouveau_encoder.h"
+
+static bool
+hdmi_sor(struct drm_encoder *encoder)
+{
+	struct drm_nouveau_private *dev_priv = encoder->dev->dev_private;
+	if (dev_priv->chipset < 0xa3)
+		return false;
+	return true;
+}
+
+static void
+nouveau_audio_disconnect(struct drm_encoder *encoder)
+{
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct drm_device *dev = encoder->dev;
+	int or = nv_encoder->or * 0x800;
+
+	if (hdmi_sor(encoder)) {
+		nv_mask(dev, 0x61c448 + or, 0x00000003, 0x00000000);
+	}
+}
+
+static void
+nouveau_audio_mode_set(struct drm_encoder *encoder,
+		       struct drm_display_mode *mode)
+{
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct nouveau_connector *nv_connector;
+	struct drm_device *dev = encoder->dev;
+	u32 or = nv_encoder->or * 0x800;
+	int i;
+
+	nv_connector = nouveau_encoder_connector_get(nv_encoder);
+	if (!drm_detect_monitor_audio(nv_connector->edid)) {
+		nouveau_audio_disconnect(encoder);
+		return;
+	}
+
+	if (hdmi_sor(encoder)) {
+		nv_mask(dev, 0x61c448 + or, 0x00000001, 0x00000001);
+
+		drm_edid_to_eld(&nv_connector->base, nv_connector->edid);
+		if (nv_connector->base.eld[0]) {
+			u8 *eld = nv_connector->base.eld;
+			for (i = 0; i < eld[2] * 4; i++)
+				nv_wr32(dev, 0x61c440 + or, (i << 8) | eld[i]);
+			for (i = eld[2] * 4; i < 0x60; i++)
+				nv_wr32(dev, 0x61c440 + or, (i << 8) | 0x00);
+			nv_mask(dev, 0x61c448 + or, 0x00000002, 0x00000002);
+		}
+	}
+}
+
+static void
+nouveau_hdmi_disconnect(struct drm_encoder *encoder)
+{
+	nouveau_audio_disconnect(encoder);
+}
+
+void
+nouveau_hdmi_mode_set(struct drm_encoder *encoder,
+		      struct drm_display_mode *mode)
+{
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct nouveau_connector *nv_connector;
+
+	nv_connector = nouveau_encoder_connector_get(nv_encoder);
+	if (!mode || !nv_connector || !nv_connector->edid ||
+	    !drm_detect_hdmi_monitor(nv_connector->edid)) {
+		nouveau_hdmi_disconnect(encoder);
+		return;
+	}
+
+	nouveau_audio_mode_set(encoder, mode);
+}
diff --git a/drivers/gpu/drm/nouveau/nv50_sor.c b/drivers/gpu/drm/nouveau/nv50_sor.c
index 2633aa8554eb..2a638aeb5061 100644
--- a/drivers/gpu/drm/nouveau/nv50_sor.c
+++ b/drivers/gpu/drm/nouveau/nv50_sor.c
@@ -60,6 +60,8 @@ nv50_sor_disconnect(struct drm_encoder *encoder)
 	BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1);
 	OUT_RING  (evo, 0);
 
+	nouveau_hdmi_mode_set(encoder, NULL);
+
 	nv_encoder->crtc = NULL;
 	nv_encoder->last_dpms = DRM_MODE_DPMS_OFF;
 }
@@ -203,6 +205,8 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
 				mode_ctl = 0x0500;
 		} else
 			mode_ctl = 0x0200;
+
+		nouveau_hdmi_mode_set(encoder, mode);
 		break;
 	case OUTPUT_DP:
 		nv_connector = nouveau_encoder_connector_get(nv_encoder);
-- 
1.7.10.4

From: Ben Skeggs <bskeggs@redhat.com>
Date: Wed, 5 Oct 2011 14:52:02 +1000
Subject: drm/nouveau/hdmi: add hdmi register accessors to handle hdmi block move

commit 52c7bcdb6749a3920739640ca791e1f741f139d0 upstream.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
 drivers/gpu/drm/nouveau/nouveau_hdmi.c |   33 +++++++++++++++++++++++++++++++-
 drivers/gpu/drm/nouveau/nv50_sor.c     |    6 +++---
 2 files changed, 35 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/nouveau/nouveau_hdmi.c b/drivers/gpu/drm/nouveau/nouveau_hdmi.c
index 489a2418a227..d8540a64a8d3 100644
--- a/drivers/gpu/drm/nouveau/nouveau_hdmi.c
+++ b/drivers/gpu/drm/nouveau/nouveau_hdmi.c
@@ -26,6 +26,7 @@
 #include "nouveau_drv.h"
 #include "nouveau_connector.h"
 #include "nouveau_encoder.h"
+#include "nouveau_crtc.h"
 
 static bool
 hdmi_sor(struct drm_encoder *encoder)
@@ -36,12 +37,42 @@ hdmi_sor(struct drm_encoder *encoder)
 	return true;
 }
 
+static inline u32
+hdmi_base(struct drm_encoder *encoder)
+{
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct nouveau_crtc *nv_crtc = nouveau_crtc(nv_encoder->crtc);
+	if (!hdmi_sor(encoder))
+		return 0x616500 + (nv_crtc->index * 0x800);
+	return 0x61c500 + (nv_encoder->or * 0x800);
+}
+
+static void
+hdmi_wr32(struct drm_encoder *encoder, u32 reg, u32 val)
+{
+	nv_wr32(encoder->dev, hdmi_base(encoder) + reg, val);
+}
+
+static u32
+hdmi_rd32(struct drm_encoder *encoder, u32 reg)
+{
+	return nv_rd32(encoder->dev, hdmi_base(encoder) + reg);
+}
+
+static u32
+hdmi_mask(struct drm_encoder *encoder, u32 reg, u32 mask, u32 val)
+{
+	u32 tmp = hdmi_rd32(encoder, reg);
+	hdmi_wr32(encoder, reg, (tmp & ~mask) | val);
+	return tmp;
+}
+
 static void
 nouveau_audio_disconnect(struct drm_encoder *encoder)
 {
 	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
 	struct drm_device *dev = encoder->dev;
-	int or = nv_encoder->or * 0x800;
+	u32 or = nv_encoder->or * 0x800;
 
 	if (hdmi_sor(encoder)) {
 		nv_mask(dev, 0x61c448 + or, 0x00000003, 0x00000000);
diff --git a/drivers/gpu/drm/nouveau/nv50_sor.c b/drivers/gpu/drm/nouveau/nv50_sor.c
index 2a638aeb5061..da603b1d8e3f 100644
--- a/drivers/gpu/drm/nouveau/nv50_sor.c
+++ b/drivers/gpu/drm/nouveau/nv50_sor.c
@@ -195,6 +195,7 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
 
 	NV_DEBUG_KMS(dev, "or %d type %d -> crtc %d\n",
 		     nv_encoder->or, nv_encoder->dcb->type, crtc->index);
+	nv_encoder->crtc = encoder->crtc;
 
 	switch (nv_encoder->dcb->type) {
 	case OUTPUT_TMDS:
@@ -206,7 +207,7 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
 		} else
 			mode_ctl = 0x0200;
 
-		nouveau_hdmi_mode_set(encoder, mode);
+		nouveau_hdmi_mode_set(encoder, adjusted_mode);
 		break;
 	case OUTPUT_DP:
 		nv_connector = nouveau_encoder_connector_get(nv_encoder);
@@ -243,12 +244,11 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
 	ret = RING_SPACE(evo, 2);
 	if (ret) {
 		NV_ERROR(dev, "no space while connecting SOR\n");
+		nv_encoder->crtc = NULL;
 		return;
 	}
 	BEGIN_RING(evo, 0, NV50_EVO_SOR(nv_encoder->or, MODE_CTRL), 1);
 	OUT_RING(evo, mode_ctl);
-
-	nv_encoder->crtc = encoder->crtc;
 }
 
 static struct drm_crtc *
-- 
1.7.10.4

From: Ben Skeggs <bskeggs@redhat.com>
Date: Wed, 5 Oct 2011 14:59:14 +1000
Subject: drm/nouveau/hdmi: enable sending of avi/audio infoframes

commit b2337f2333c0bdefc9b230da17ed7188e4eb7f6c upstream.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
 drivers/gpu/drm/nouveau/nouveau_hdmi.c |  103 ++++++++++++++++++++++++++++++++
 1 file changed, 103 insertions(+)

diff --git a/drivers/gpu/drm/nouveau/nouveau_hdmi.c b/drivers/gpu/drm/nouveau/nouveau_hdmi.c
index d8540a64a8d3..3b4120f0626a 100644
--- a/drivers/gpu/drm/nouveau/nouveau_hdmi.c
+++ b/drivers/gpu/drm/nouveau/nouveau_hdmi.c
@@ -111,9 +111,106 @@ nouveau_audio_mode_set(struct drm_encoder *encoder,
 }
 
 static void
+nouveau_hdmi_infoframe(struct drm_encoder *encoder, u32 ctrl, u8 *frame)
+{
+	/* calculate checksum for the infoframe */
+	u8 sum = 0, i;
+	for (i = 0; i < frame[2]; i++)
+		sum += frame[i];
+	frame[3] = 256 - sum;
+
+	/* disable infoframe, and write header */
+	hdmi_mask(encoder, ctrl + 0x00, 0x00000001, 0x00000000);
+	hdmi_wr32(encoder, ctrl + 0x08, *(u32 *)frame & 0xffffff);
+
+	/* register scans tell me the audio infoframe has only one set of
+	 * subpack regs, according to tegra (gee nvidia, it'd be nice if we
+	 * could get those docs too!), the hdmi block pads out the rest of
+	 * the packet on its own.
+	 */
+	if (ctrl == 0x020)
+		frame[2] = 6;
+
+	/* write out checksum and data, weird weird 7 byte register pairs */
+	for (i = 0; i < frame[2] + 1; i += 7) {
+		u32 rsubpack = ctrl + 0x0c + ((i / 7) * 8);
+		u32 *subpack = (u32 *)&frame[3 + i];
+		hdmi_wr32(encoder, rsubpack + 0, subpack[0]);
+		hdmi_wr32(encoder, rsubpack + 4, subpack[1] & 0xffffff);
+	}
+
+	/* enable the infoframe */
+	hdmi_mask(encoder, ctrl, 0x00000001, 0x00000001);
+}
+
+static void
+nouveau_hdmi_video_infoframe(struct drm_encoder *encoder,
+			     struct drm_display_mode *mode)
+{
+	const u8 Y = 0, A = 0, B = 0, S = 0, C = 0, M = 0, R = 0;
+	const u8 ITC = 0, EC = 0, Q = 0, SC = 0, VIC = 0, PR = 0;
+	const u8 bar_top = 0, bar_bottom = 0, bar_left = 0, bar_right = 0;
+	u8 frame[20];
+
+	frame[0x00] = 0x82; /* AVI infoframe */
+	frame[0x01] = 0x02; /* version */
+	frame[0x02] = 0x0d; /* length */
+	frame[0x03] = 0x00;
+	frame[0x04] = (Y << 5) | (A << 4) | (B << 2) | S;
+	frame[0x05] = (C << 6) | (M << 4) | R;
+	frame[0x06] = (ITC << 7) | (EC << 4) | (Q << 2) | SC;
+	frame[0x07] = VIC;
+	frame[0x08] = PR;
+	frame[0x09] = bar_top & 0xff;
+	frame[0x0a] = bar_top >> 8;
+	frame[0x0b] = bar_bottom & 0xff;
+	frame[0x0c] = bar_bottom >> 8;
+	frame[0x0d] = bar_left & 0xff;
+	frame[0x0e] = bar_left >> 8;
+	frame[0x0f] = bar_right & 0xff;
+	frame[0x10] = bar_right >> 8;
+	frame[0x11] = 0x00;
+	frame[0x12] = 0x00;
+	frame[0x13] = 0x00;
+
+	nouveau_hdmi_infoframe(encoder, 0x020, frame);
+}
+
+static void
+nouveau_hdmi_audio_infoframe(struct drm_encoder *encoder,
+			     struct drm_display_mode *mode)
+{
+	const u8 CT = 0x00, CC = 0x01, ceaSS = 0x00, SF = 0x00, FMT = 0x00;
+	const u8 CA = 0x00, DM_INH = 0, LSV = 0x00;
+	u8 frame[12];
+
+	frame[0x00] = 0x84;	/* Audio infoframe */
+	frame[0x01] = 0x01;	/* version */
+	frame[0x02] = 0x0a;	/* length */
+	frame[0x03] = 0x00;
+	frame[0x04] = (CT << 4) | CC;
+	frame[0x05] = (SF << 2) | ceaSS;
+	frame[0x06] = FMT;
+	frame[0x07] = CA;
+	frame[0x08] = (DM_INH << 7) | (LSV << 3);
+	frame[0x09] = 0x00;
+	frame[0x0a] = 0x00;
+	frame[0x0b] = 0x00;
+
+	nouveau_hdmi_infoframe(encoder, 0x000, frame);
+}
+
+static void
 nouveau_hdmi_disconnect(struct drm_encoder *encoder)
 {
 	nouveau_audio_disconnect(encoder);
+
+	/* disable audio and avi infoframes */
+	hdmi_mask(encoder, 0x000, 0x00000001, 0x00000000);
+	hdmi_mask(encoder, 0x020, 0x00000001, 0x00000000);
+
+	/* disable hdmi */
+	hdmi_mask(encoder, 0x0a4, 0x40000000, 0x00000000);
 }
 
 void
@@ -130,5 +227,11 @@ nouveau_hdmi_mode_set(struct drm_encoder *encoder,
 		return;
 	}
 
+	/* enable hdmi */
+	hdmi_mask(encoder, 0x0a4, 0x40000000, 0x40000000);
+
+	nouveau_hdmi_video_infoframe(encoder, mode);
+	nouveau_hdmi_audio_infoframe(encoder, mode);
+
 	nouveau_audio_mode_set(encoder, mode);
 }
-- 
1.7.10.4

From: Ben Skeggs <bskeggs@redhat.com>
Date: Wed, 9 Nov 2011 10:03:01 +1000
Subject: drm/nouveau/hdmi: enable audio for nva3:nvd0 chipsets

commit 50a01fe06e25b271661c6691bc0907ad5ca2c718 upstream.

Pre-nva3 will likely require far more extensive setup, and nvd9 needs to
be checked to find its SOR_HDMI/SOR_AUDIO blocks.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
 drivers/gpu/drm/nouveau/nouveau_hdmi.c |   27 ++++++++++++++++++++++++---
 1 file changed, 24 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/nouveau/nouveau_hdmi.c b/drivers/gpu/drm/nouveau/nouveau_hdmi.c
index 3b4120f0626a..59ea1c14eca0 100644
--- a/drivers/gpu/drm/nouveau/nouveau_hdmi.c
+++ b/drivers/gpu/drm/nouveau/nouveau_hdmi.c
@@ -219,6 +219,8 @@ nouveau_hdmi_mode_set(struct drm_encoder *encoder,
 {
 	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
 	struct nouveau_connector *nv_connector;
+	struct drm_device *dev = encoder->dev;
+	u32 max_ac_packet, rekey;
 
 	nv_connector = nouveau_encoder_connector_get(nv_encoder);
 	if (!mode || !nv_connector || !nv_connector->edid ||
@@ -227,11 +229,30 @@ nouveau_hdmi_mode_set(struct drm_encoder *encoder,
 		return;
 	}
 
-	/* enable hdmi */
-	hdmi_mask(encoder, 0x0a4, 0x40000000, 0x40000000);
-
 	nouveau_hdmi_video_infoframe(encoder, mode);
 	nouveau_hdmi_audio_infoframe(encoder, mode);
 
+	hdmi_mask(encoder, 0x0d0, 0x00070001, 0x00010001); /* SPARE, HW_CTS */
+	hdmi_mask(encoder, 0x068, 0x00010101, 0x00000000); /* ACR_CTRL, ?? */
+	hdmi_mask(encoder, 0x078, 0x80000000, 0x80000000); /* ACR_0441_ENABLE */
+
+	nv_mask(dev, 0x61733c, 0x00100000, 0x00100000); /* RESETF */
+	nv_mask(dev, 0x61733c, 0x10000000, 0x10000000); /* LOOKUP_EN */
+	nv_mask(dev, 0x61733c, 0x00100000, 0x00000000); /* !RESETF */
+
+	/* value matches nvidia binary driver, and tegra constant */
+	rekey = 56;
+
+	max_ac_packet  = mode->htotal - mode->hdisplay;
+	max_ac_packet -= rekey;
+	max_ac_packet -= 18; /* constant from tegra */
+	max_ac_packet /= 32;
+
+	/* enable hdmi */
+	hdmi_mask(encoder, 0x0a4, 0x5f1f003f, 0x40000000 | /* enable */
+					      0x1f000000 | /* unknown */
+					      max_ac_packet << 16 |
+					      rekey);
+
 	nouveau_audio_mode_set(encoder, mode);
 }
-- 
1.7.10.4

From: Ben Skeggs <bskeggs@redhat.com>
Date: Fri, 11 Nov 2011 18:13:13 +1000
Subject: drm/nvd0/disp: send eld to the audio codec

commit 78951d2226e13d4991073f1a81c4dc13aaf4a4fc upstream.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
 drivers/gpu/drm/nouveau/nvd0_display.c |   66 ++++++++++++++++++++++++++++++--
 1 file changed, 63 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c
index 720920796ccb..bfeb17538134 100644
--- a/drivers/gpu/drm/nouveau/nvd0_display.c
+++ b/drivers/gpu/drm/nouveau/nvd0_display.c
@@ -773,6 +773,61 @@ nvd0_dac_create(struct drm_connector *connector, struct dcb_entry *dcbe)
 }
 
 /******************************************************************************
+ * Audio
+ *****************************************************************************/
+static void
+nvd0_audio_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode)
+{
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct nouveau_connector *nv_connector;
+	struct drm_device *dev = encoder->dev;
+	int i, or = nv_encoder->or * 0x30;
+
+	nv_connector = nouveau_encoder_connector_get(nv_encoder);
+	if (!drm_detect_monitor_audio(nv_connector->edid))
+		return;
+
+	nv_mask(dev, 0x10ec10 + or, 0x80000003, 0x80000001);
+
+	drm_edid_to_eld(&nv_connector->base, nv_connector->edid);
+	if (nv_connector->base.eld[0]) {
+		u8 *eld = nv_connector->base.eld;
+
+		for (i = 0; i < eld[2] * 4; i++)
+			nv_wr32(dev, 0x10ec00 + or, (i << 8) | eld[i]);
+		for (i = eld[2] * 4; i < 0x60; i++)
+			nv_wr32(dev, 0x10ec00 + or, (i << 8) | 0x00);
+
+		nv_mask(dev, 0x10ec10 + or, 0x80000002, 0x80000002);
+	}
+}
+
+static void
+nvd0_audio_disconnect(struct drm_encoder *encoder)
+{
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct drm_device *dev = encoder->dev;
+	int or = nv_encoder->or * 0x30;
+
+	nv_mask(dev, 0x10ec10 + or, 0x80000003, 0x80000000);
+}
+
+/******************************************************************************
+ * HDMI
+ *****************************************************************************/
+static void
+nvd0_hdmi_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode)
+{
+	nvd0_audio_mode_set(encoder, mode);
+}
+
+static void
+nvd0_hdmi_disconnect(struct drm_encoder *encoder)
+{
+	nvd0_audio_disconnect(encoder);
+}
+
+/******************************************************************************
  * SOR
  *****************************************************************************/
 static void
@@ -842,7 +897,8 @@ static void
 nvd0_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode,
 		  struct drm_display_mode *mode)
 {
-	struct drm_nouveau_private *dev_priv = encoder->dev->dev_private;
+	struct drm_device *dev = encoder->dev;
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
 	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
 	struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
 	struct nouveau_connector *nv_connector;
@@ -865,6 +921,8 @@ nvd0_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode,
 		or_config = (mode_ctrl & 0x00000f00) >> 8;
 		if (mode->clock >= 165000)
 			or_config |= 0x0100;
+
+		nvd0_hdmi_mode_set(encoder, mode);
 		break;
 	case OUTPUT_LVDS:
 		or_config = (mode_ctrl & 0x00000f00) >> 8;
@@ -902,12 +960,12 @@ nvd0_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode,
 
 	nvd0_sor_dpms(encoder, DRM_MODE_DPMS_ON);
 
-	push = evo_wait(encoder->dev, 0, 4);
+	push = evo_wait(dev, 0, 4);
 	if (push) {
 		evo_mthd(push, 0x0200 + (nv_encoder->or * 0x20), 2);
 		evo_data(push, mode_ctrl);
 		evo_data(push, or_config);
-		evo_kick(push, encoder->dev, 0);
+		evo_kick(push, dev, 0);
 	}
 
 	nv_encoder->crtc = encoder->crtc;
@@ -932,6 +990,8 @@ nvd0_sor_disconnect(struct drm_encoder *encoder)
 			evo_kick(push, dev, 0);
 		}
 
+		nvd0_hdmi_disconnect(encoder);
+
 		nv_encoder->crtc = NULL;
 		nv_encoder->last_dpms = DRM_MODE_DPMS_OFF;
 	}
-- 
1.7.10.4

From: Ben Skeggs <bskeggs@redhat.com>
Date: Fri, 11 Nov 2011 19:51:20 +1000
Subject: drm/nvd0/disp: enable hdmi on sor if hdmi monitor present

commit 64d9cc04ec08d36c2b39f04d4994313d3901a85c upstream.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
 drivers/gpu/drm/nouveau/nvd0_display.c |   44 ++++++++++++++++++++++++++++++++
 1 file changed, 44 insertions(+)

diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c
index bfeb17538134..6c2d6591df1c 100644
--- a/drivers/gpu/drm/nouveau/nvd0_display.c
+++ b/drivers/gpu/drm/nouveau/nvd0_display.c
@@ -818,13 +818,57 @@ nvd0_audio_disconnect(struct drm_encoder *encoder)
 static void
 nvd0_hdmi_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode)
 {
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
+	struct nouveau_connector *nv_connector;
+	struct drm_device *dev = encoder->dev;
+	int head = nv_crtc->index * 0x800;
+	u32 rekey = 56; /* binary driver, and tegra constant */
+	u32 max_ac_packet;
+
+	nv_connector = nouveau_encoder_connector_get(nv_encoder);
+	if (!drm_detect_hdmi_monitor(nv_connector->edid))
+		return;
+
+	max_ac_packet  = mode->htotal - mode->hdisplay;
+	max_ac_packet -= rekey;
+	max_ac_packet -= 18; /* constant from tegra */
+	max_ac_packet /= 32;
+
+	/* AVI InfoFrame */
+	nv_mask(dev, 0x616714 + head, 0x00000001, 0x00000000);
+	nv_wr32(dev, 0x61671c + head, 0x000d0282);
+	nv_wr32(dev, 0x616720 + head, 0x0000006f);
+	nv_wr32(dev, 0x616724 + head, 0x00000000);
+	nv_wr32(dev, 0x616728 + head, 0x00000000);
+	nv_wr32(dev, 0x61672c + head, 0x00000000);
+	nv_mask(dev, 0x616714 + head, 0x00000001, 0x00000001);
+
+	/* ??? InfoFrame? */
+	nv_mask(dev, 0x6167a4 + head, 0x00000001, 0x00000000);
+	nv_wr32(dev, 0x6167ac + head, 0x00000010);
+	nv_mask(dev, 0x6167a4 + head, 0x00000001, 0x00000001);
+
+	/* HDMI_CTRL */
+	nv_mask(dev, 0x616798 + head, 0x401f007f, 0x40000000 | rekey |
+						  max_ac_packet << 16);
+
 	nvd0_audio_mode_set(encoder, mode);
 }
 
 static void
 nvd0_hdmi_disconnect(struct drm_encoder *encoder)
 {
+	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
+	struct nouveau_crtc *nv_crtc = nouveau_crtc(nv_encoder->crtc);
+	struct drm_device *dev = encoder->dev;
+	int head = nv_crtc->index * 0x800;
+
 	nvd0_audio_disconnect(encoder);
+
+	nv_mask(dev, 0x616798 + head, 0x40000000, 0x00000000);
+	nv_mask(dev, 0x6167a4 + head, 0x00000001, 0x00000000);
+	nv_mask(dev, 0x616714 + head, 0x00000001, 0x00000000);
 }
 
 /******************************************************************************
-- 
1.7.10.4

From: Ben Skeggs <bskeggs@redhat.com>
Date: Fri, 11 Nov 2011 23:39:22 +1000
Subject: drm/nvd0/disp: update crtc timing calculations for interlace/doublescan

commit 2d1d898b4684ab86fb27ece7d69e4e145a7be9d2 upstream.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
 drivers/gpu/drm/nouveau/nvd0_display.c |   52 +++++++++++++++++++++-----------
 1 file changed, 34 insertions(+), 18 deletions(-)

diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c
index 6c2d6591df1c..99873fc24c92 100644
--- a/drivers/gpu/drm/nouveau/nvd0_display.c
+++ b/drivers/gpu/drm/nouveau/nvd0_display.c
@@ -346,21 +346,35 @@ nvd0_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode,
 {
 	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
 	struct nouveau_connector *nv_connector;
-	u32 htotal = mode->htotal;
-	u32 vtotal = mode->vtotal;
-	u32 hsyncw = mode->hsync_end - mode->hsync_start - 1;
-	u32 vsyncw = mode->vsync_end - mode->vsync_start - 1;
-	u32 hfrntp = mode->hsync_start - mode->hdisplay;
-	u32 vfrntp = mode->vsync_start - mode->vdisplay;
-	u32 hbackp = mode->htotal - mode->hsync_end;
-	u32 vbackp = mode->vtotal - mode->vsync_end;
-	u32 hss2be = hsyncw + hbackp;
-	u32 vss2be = vsyncw + vbackp;
-	u32 hss2de = htotal - hfrntp;
-	u32 vss2de = vtotal - vfrntp;
+	u32 ilace = (mode->flags & DRM_MODE_FLAG_INTERLACE) ? 2 : 1;
+	u32 vscan = (mode->flags & DRM_MODE_FLAG_DBLSCAN) ? 2 : 1;
+	u32 hactive, hsynce, hbackp, hfrontp, hblanke, hblanks;
+	u32 vactive, vsynce, vbackp, vfrontp, vblanke, vblanks;
+	u32 vblan2e = 0, vblan2s = 1;
+	u32 magic = 0x31ec6000;
 	u32 syncs, *push;
 	int ret;
 
+	hactive = mode->htotal;
+	hsynce  = mode->hsync_end - mode->hsync_start - 1;
+	hbackp  = mode->htotal - mode->hsync_end;
+	hblanke = hsynce + hbackp;
+	hfrontp = mode->hsync_start - mode->hdisplay;
+	hblanks = mode->htotal - hfrontp - 1;
+
+	vactive = mode->vtotal * vscan / ilace;
+	vsynce  = ((mode->vsync_end - mode->vsync_start) * vscan / ilace) - 1;
+	vbackp  = (mode->vtotal - mode->vsync_end) * vscan / ilace;
+	vblanke = vsynce + vbackp;
+	vfrontp = (mode->vsync_start - mode->vdisplay) * vscan / ilace;
+	vblanks = vactive - vfrontp - 1;
+	if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
+		vblan2e = vactive + vsynce + vbackp;
+		vblan2s = vblan2e + (mode->vdisplay * vscan / ilace);
+		vactive = (vactive * 2) + 1;
+		magic  |= 0x00000001;
+	}
+
 	syncs = 0x00000001;
 	if (mode->flags & DRM_MODE_FLAG_NHSYNC)
 		syncs |= 0x00000008;
@@ -373,20 +387,22 @@ nvd0_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode,
 
 	push = evo_wait(crtc->dev, 0, 64);
 	if (push) {
-		evo_mthd(push, 0x0410 + (nv_crtc->index * 0x300), 5);
+		evo_mthd(push, 0x0410 + (nv_crtc->index * 0x300), 6);
 		evo_data(push, 0x00000000);
-		evo_data(push, (vtotal << 16) | htotal);
-		evo_data(push, (vsyncw << 16) | hsyncw);
-		evo_data(push, (vss2be << 16) | hss2be);
-		evo_data(push, (vss2de << 16) | hss2de);
+		evo_data(push, (vactive << 16) | hactive);
+		evo_data(push, ( vsynce << 16) | hsynce);
+		evo_data(push, (vblanke << 16) | hblanke);
+		evo_data(push, (vblanks << 16) | hblanks);
+		evo_data(push, (vblan2e << 16) | vblan2s);
 		evo_mthd(push, 0x042c + (nv_crtc->index * 0x300), 1);
 		evo_data(push, 0x00000000); /* ??? */
 		evo_mthd(push, 0x0450 + (nv_crtc->index * 0x300), 3);
 		evo_data(push, mode->clock * 1000);
 		evo_data(push, 0x00200000); /* ??? */
 		evo_data(push, mode->clock * 1000);
-		evo_mthd(push, 0x0404 + (nv_crtc->index * 0x300), 1);
+		evo_mthd(push, 0x0404 + (nv_crtc->index * 0x300), 2);
 		evo_data(push, syncs);
+		evo_data(push, magic);
 		evo_kick(push, crtc->dev, 0);
 	}
 
-- 
1.7.10.4

From: Ben Skeggs <bskeggs@redhat.com>
Date: Thu, 6 Oct 2011 13:29:05 +1000
Subject: drm/nouveau: add overscan compensation connector properties

commit b29caa5885e85bbda7c84ea55721b9e79718583a upstream.

Exposes the same connector properties as the Radeon implementation, however
their behaviour isn't exactly the same.  The primary difference being that
unless both hborder/vborder have been defined by the user, the driver will
keep the aspect ratio of the overscanned area the same as the mode the
display is programmed for.

Enabled for digital outputs on GeForce 8 and up, excluding GF119.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
 drivers/gpu/drm/nouveau/nouveau_connector.c |   78 ++++++++++++++++++--
 drivers/gpu/drm/nouveau/nouveau_connector.h |    5 +-
 drivers/gpu/drm/nouveau/nouveau_drv.h       |   10 +++
 drivers/gpu/drm/nouveau/nouveau_state.c     |   37 +++++++++-
 drivers/gpu/drm/nouveau/nv50_crtc.c         |  105 ++++++++++++++++-----------
 5 files changed, 181 insertions(+), 54 deletions(-)

diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
index 1e72db509ebb..b573dd40ff71 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -420,15 +420,21 @@ static int
 nouveau_connector_set_property(struct drm_connector *connector,
 			       struct drm_property *property, uint64_t value)
 {
+	struct drm_nouveau_private *dev_priv = connector->dev->dev_private;
+	struct nouveau_display_engine *disp = &dev_priv->engine.display;
 	struct nouveau_connector *nv_connector = nouveau_connector(connector);
 	struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
 	struct drm_encoder *encoder = to_drm_encoder(nv_encoder);
 	struct drm_device *dev = connector->dev;
+	struct nouveau_crtc *nv_crtc;
 	int ret;
 
+	nv_crtc = NULL;
+	if (connector->encoder && connector->encoder->crtc)
+		nv_crtc = nouveau_crtc(connector->encoder->crtc);
+
 	/* Scaling mode */
 	if (property == dev->mode_config.scaling_mode_property) {
-		struct nouveau_crtc *nv_crtc = NULL;
 		bool modeset = false;
 
 		switch (value) {
@@ -454,8 +460,6 @@ nouveau_connector_set_property(struct drm_connector *connector,
 			modeset = true;
 		nv_connector->scaling_mode = value;
 
-		if (connector->encoder && connector->encoder->crtc)
-			nv_crtc = nouveau_crtc(connector->encoder->crtc);
 		if (!nv_crtc)
 			return 0;
 
@@ -475,18 +479,56 @@ nouveau_connector_set_property(struct drm_connector *connector,
 		return 0;
 	}
 
+	/* Underscan */
+	if (property == disp->underscan_property) {
+		if (nv_connector->underscan != value) {
+			nv_connector->underscan = value;
+			if (!nv_crtc || !nv_crtc->set_scale)
+				return 0;
+
+			return nv_crtc->set_scale(nv_crtc,
+						  nv_connector->scaling_mode,
+						  true);
+		}
+
+		return 0;
+	}
+
+	if (property == disp->underscan_hborder_property) {
+		if (nv_connector->underscan_hborder != value) {
+			nv_connector->underscan_hborder = value;
+			if (!nv_crtc || !nv_crtc->set_scale)
+				return 0;
+
+			return nv_crtc->set_scale(nv_crtc,
+						  nv_connector->scaling_mode,
+						  true);
+		}
+
+		return 0;
+	}
+
+	if (property == disp->underscan_vborder_property) {
+		if (nv_connector->underscan_vborder != value) {
+			nv_connector->underscan_vborder = value;
+			if (!nv_crtc || !nv_crtc->set_scale)
+				return 0;
+
+			return nv_crtc->set_scale(nv_crtc,
+						  nv_connector->scaling_mode,
+						  true);
+		}
+
+		return 0;
+	}
+
 	/* Dithering */
 	if (property == dev->mode_config.dithering_mode_property) {
-		struct nouveau_crtc *nv_crtc = NULL;
-
 		if (value == DRM_MODE_DITHERING_ON)
 			nv_connector->use_dithering = true;
 		else
 			nv_connector->use_dithering = false;
 
-		if (connector->encoder && connector->encoder->crtc)
-			nv_crtc = nouveau_crtc(connector->encoder->crtc);
-
 		if (!nv_crtc || !nv_crtc->set_dither)
 			return 0;
 
@@ -828,6 +870,7 @@ nouveau_connector_create(struct drm_device *dev, int index)
 {
 	const struct drm_connector_funcs *funcs = &nouveau_connector_funcs;
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_display_engine *disp = &dev_priv->engine.display;
 	struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
 	struct nouveau_connector *nv_connector = NULL;
 	struct dcb_connector_table_entry *dcb = NULL;
@@ -912,6 +955,25 @@ nouveau_connector_create(struct drm_device *dev, int index)
 		drm_connector_attach_property(connector, dev->mode_config.dvi_i_select_subconnector_property, 0);
 	}
 
+	/* Add overscan compensation options to digital outputs */
+	if ((dev_priv->card_type == NV_50 ||
+	     dev_priv->card_type == NV_C0) &&
+	    (dcb->type == DCB_CONNECTOR_DVI_D ||
+	     dcb->type == DCB_CONNECTOR_DVI_I ||
+	     dcb->type == DCB_CONNECTOR_HDMI_0 ||
+	     dcb->type == DCB_CONNECTOR_HDMI_1 ||
+	     dcb->type == DCB_CONNECTOR_DP)) {
+		drm_connector_attach_property(connector,
+					      disp->underscan_property,
+					      UNDERSCAN_OFF);
+		drm_connector_attach_property(connector,
+					      disp->underscan_hborder_property,
+					      0);
+		drm_connector_attach_property(connector,
+					      disp->underscan_vborder_property,
+					      0);
+	}
+
 	switch (dcb->type) {
 	case DCB_CONNECTOR_VGA:
 		if (dev_priv->card_type >= NV_50) {
diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h
index 711b1e9203af..5bcaabbd375c 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.h
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.h
@@ -35,8 +35,11 @@ struct nouveau_connector {
 
 	struct dcb_connector_table_entry *dcb;
 
-	int scaling_mode;
 	bool use_dithering;
+	int scaling_mode;
+	enum nouveau_underscan_type underscan;
+	u32 underscan_hborder;
+	u32 underscan_vborder;
 
 	struct nouveau_encoder *detected_encoder;
 	struct edid *edid;
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index 0026976eee67..adc704870e3c 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -386,6 +386,12 @@ struct nouveau_fifo_engine {
 	void (*tlb_flush)(struct drm_device *dev);
 };
 
+enum nouveau_underscan_type {
+	UNDERSCAN_OFF,
+	UNDERSCAN_ON,
+	UNDERSCAN_AUTO,
+};
+
 struct nouveau_display_engine {
 	void *priv;
 	int (*early_init)(struct drm_device *);
@@ -393,6 +399,10 @@ struct nouveau_display_engine {
 	int (*create)(struct drm_device *);
 	int (*init)(struct drm_device *);
 	void (*destroy)(struct drm_device *);
+
+	struct drm_property *underscan_property;
+	struct drm_property *underscan_hborder_property;
+	struct drm_property *underscan_vborder_property;
 };
 
 struct nouveau_gpio_engine {
diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c
index 9faf952ca68f..a36cee053781 100644
--- a/drivers/gpu/drm/nouveau/nouveau_state.c
+++ b/drivers/gpu/drm/nouveau/nouveau_state.c
@@ -617,12 +617,24 @@ error:
 	return ret;
 }
 
+
+struct drm_prop_enum_list {
+	int type;
+	char *name;
+};
+
+static struct drm_prop_enum_list nouveau_underscan_enum_list[] = {
+	{ UNDERSCAN_OFF, "off" },
+	{ UNDERSCAN_ON, "on" },
+	{ UNDERSCAN_AUTO, "auto" },
+};
+
 int
 nouveau_card_init(struct drm_device *dev)
 {
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
 	struct nouveau_engine *engine;
-	int ret, e = 0;
+	int ret, cnt, i, e = 0;
 
 	vga_client_register(dev->pdev, dev, NULL, nouveau_vga_set_decode);
 	vga_switcheroo_register_client(dev->pdev, nouveau_switcheroo_set_state,
@@ -786,6 +798,29 @@ nouveau_card_init(struct drm_device *dev)
 	drm_mode_config_init(dev);
 	drm_mode_create_scaling_mode_property(dev);
 	drm_mode_create_dithering_property(dev);
+
+	cnt = ARRAY_SIZE(nouveau_underscan_enum_list);
+	engine->display.underscan_property =
+		drm_property_create(dev, DRM_MODE_PROP_ENUM,
+				    "underscan", cnt);
+	for (i = 0; i < cnt; i++) {
+		drm_property_add_enum(engine->display.underscan_property, i,
+				      nouveau_underscan_enum_list[i].type,
+				      nouveau_underscan_enum_list[i].name);
+	}
+
+	engine->display.underscan_hborder_property =
+		drm_property_create(dev, DRM_MODE_PROP_RANGE,
+				    "underscan hborder", 2);
+	engine->display.underscan_hborder_property->values[0] = 0;
+	engine->display.underscan_hborder_property->values[1] = 128;
+
+	engine->display.underscan_vborder_property =
+		drm_property_create(dev, DRM_MODE_PROP_RANGE,
+				    "underscan vborder", 2);
+	engine->display.underscan_vborder_property->values[0] = 0;
+	engine->display.underscan_vborder_property->values[1] = 128;
+
 	dev->mode_config.funcs = (void *)&nouveau_mode_config_funcs;
 	dev->mode_config.fb_base = pci_resource_start(dev->pdev, 1);
 	dev->mode_config.min_width = 0;
diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c
index 882080e0b4f5..03d4d811ed7b 100644
--- a/drivers/gpu/drm/nouveau/nv50_crtc.c
+++ b/drivers/gpu/drm/nouveau/nv50_crtc.c
@@ -182,73 +182,90 @@ nouveau_crtc_connector_get(struct nouveau_crtc *nv_crtc)
 static int
 nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, int scaling_mode, bool update)
 {
-	struct nouveau_connector *nv_connector =
-		nouveau_crtc_connector_get(nv_crtc);
+	struct nouveau_connector *nv_connector;
 	struct drm_device *dev = nv_crtc->base.dev;
 	struct nouveau_channel *evo = nv50_display(dev)->master;
-	struct drm_display_mode *native_mode = NULL;
 	struct drm_display_mode *mode = &nv_crtc->base.mode;
-	uint32_t outX, outY, horiz, vert;
+	u32 ctrl = 0, oX, oY;
 	int ret;
 
 	NV_DEBUG_KMS(dev, "\n");
 
-	switch (scaling_mode) {
-	case DRM_MODE_SCALE_NONE:
-		break;
-	default:
-		if (!nv_connector || !nv_connector->native_mode) {
-			NV_ERROR(dev, "No native mode, forcing panel scaling\n");
-			scaling_mode = DRM_MODE_SCALE_NONE;
-		} else {
-			native_mode = nv_connector->native_mode;
-		}
-		break;
+	nv_connector = nouveau_crtc_connector_get(nv_crtc);
+	if (!nv_connector || !nv_connector->native_mode) {
+		NV_ERROR(dev, "no native mode, forcing panel scaling\n");
+		scaling_mode = DRM_MODE_SCALE_NONE;
 	}
 
-	switch (scaling_mode) {
-	case DRM_MODE_SCALE_ASPECT:
-		horiz = (native_mode->hdisplay << 19) / mode->hdisplay;
-		vert = (native_mode->vdisplay << 19) / mode->vdisplay;
+	/* start off at the resolution we programmed the crtc for, this
+	 * effectively handles NONE/FULL scaling
+	 */
+	if (scaling_mode != DRM_MODE_SCALE_NONE) {
+		oX = nv_connector->native_mode->hdisplay;
+		oY = nv_connector->native_mode->vdisplay;
+	} else {
+		oX = mode->hdisplay;
+		oY = mode->vdisplay;
+	}
 
-		if (vert > horiz) {
-			outX = (mode->hdisplay * horiz) >> 19;
-			outY = (mode->vdisplay * horiz) >> 19;
+	/* add overscan compensation if necessary, will keep the aspect
+	 * ratio the same as the backend mode unless overridden by the
+	 * user setting both hborder and vborder properties.
+	 */
+	if (nv_connector && ( nv_connector->underscan == UNDERSCAN_ON ||
+			     (nv_connector->underscan == UNDERSCAN_AUTO &&
+			      nv_connector->edid &&
+			      drm_detect_hdmi_monitor(nv_connector->edid)))) {
+		u32 bX = nv_connector->underscan_hborder;
+		u32 bY = nv_connector->underscan_vborder;
+		u32 aspect = (oY << 19) / oX;
+
+		if (bX) {
+			oX -= (bX * 2);
+			if (bY) oY -= (bY * 2);
+			else    oY  = ((oX * aspect) + (aspect / 2)) >> 19;
 		} else {
-			outX = (mode->hdisplay * vert) >> 19;
-			outY = (mode->vdisplay * vert) >> 19;
+			oX -= (oX >> 4) + 32;
+			if (bY) oY -= (bY * 2);
+			else    oY  = ((oX * aspect) + (aspect / 2)) >> 19;
 		}
-		break;
-	case DRM_MODE_SCALE_FULLSCREEN:
-		outX = native_mode->hdisplay;
-		outY = native_mode->vdisplay;
-		break;
+	}
+
+	/* handle CENTER/ASPECT scaling, taking into account the areas
+	 * removed already for overscan compensation
+	 */
+	switch (scaling_mode) {
 	case DRM_MODE_SCALE_CENTER:
-	case DRM_MODE_SCALE_NONE:
+		oX = min((u32)mode->hdisplay, oX);
+		oY = min((u32)mode->vdisplay, oY);
+		/* fall-through */
+	case DRM_MODE_SCALE_ASPECT:
+		if (oY < oX) {
+			u32 aspect = (mode->hdisplay << 19) / mode->vdisplay;
+			oX = ((oY * aspect) + (aspect / 2)) >> 19;
+		} else {
+			u32 aspect = (mode->vdisplay << 19) / mode->hdisplay;
+			oY = ((oX * aspect) + (aspect / 2)) >> 19;
+		}
+		break;
 	default:
-		outX = mode->hdisplay;
-		outY = mode->vdisplay;
 		break;
 	}
 
+	if (mode->hdisplay != oX || mode->vdisplay != oY ||
+	    mode->flags & DRM_MODE_FLAG_INTERLACE ||
+	    mode->flags & DRM_MODE_FLAG_DBLSCAN)
+		ctrl |= NV50_EVO_CRTC_SCALE_CTRL_ACTIVE;
+
 	ret = RING_SPACE(evo, update ? 7 : 5);
 	if (ret)
 		return ret;
 
-	/* Got a better name for SCALER_ACTIVE? */
-	/* One day i've got to really figure out why this is needed. */
 	BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, SCALE_CTRL), 1);
-	if ((mode->flags & DRM_MODE_FLAG_DBLSCAN) ||
-	    (mode->flags & DRM_MODE_FLAG_INTERLACE) ||
-	    mode->hdisplay != outX || mode->vdisplay != outY) {
-		OUT_RING(evo, NV50_EVO_CRTC_SCALE_CTRL_ACTIVE);
-	} else {
-		OUT_RING(evo, NV50_EVO_CRTC_SCALE_CTRL_INACTIVE);
-	}
-
+	OUT_RING  (evo, ctrl);
 	BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, SCALE_RES1), 2);
-	OUT_RING(evo, outY << 16 | outX);
-	OUT_RING(evo, outY << 16 | outX);
+	OUT_RING  (evo, oY << 16 | oX);
+	OUT_RING  (evo, oY << 16 | oX);
 
 	if (update) {
 		BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1);
-- 
1.7.10.4

From: Ben Skeggs <bskeggs@redhat.com>
Date: Mon, 17 Oct 2011 10:38:10 +1000
Subject: drm/nouveau: no need to pass parameters into set_scale/dither

commit 488ff207f98650c6a1f077e432b541f3cdcb7ab8 upstream.

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
 drivers/gpu/drm/nouveau/nouveau_connector.c |   17 +++++------------
 drivers/gpu/drm/nouveau/nouveau_crtc.h      |    4 ++--
 drivers/gpu/drm/nouveau/nv50_crtc.c         |   16 ++++++++++------
 drivers/gpu/drm/nouveau/nvd0_display.c      |   17 +++++++++--------
 4 files changed, 26 insertions(+), 28 deletions(-)

diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
index b573dd40ff71..7f5b6f35ae38 100644
--- a/drivers/gpu/drm/nouveau/nouveau_connector.c
+++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
@@ -471,7 +471,7 @@ nouveau_connector_set_property(struct drm_connector *connector,
 			if (!ret)
 				return -EINVAL;
 		} else {
-			ret = nv_crtc->set_scale(nv_crtc, value, true);
+			ret = nv_crtc->set_scale(nv_crtc, true);
 			if (ret)
 				return ret;
 		}
@@ -486,9 +486,7 @@ nouveau_connector_set_property(struct drm_connector *connector,
 			if (!nv_crtc || !nv_crtc->set_scale)
 				return 0;
 
-			return nv_crtc->set_scale(nv_crtc,
-						  nv_connector->scaling_mode,
-						  true);
+			return nv_crtc->set_scale(nv_crtc, true);
 		}
 
 		return 0;
@@ -500,9 +498,7 @@ nouveau_connector_set_property(struct drm_connector *connector,
 			if (!nv_crtc || !nv_crtc->set_scale)
 				return 0;
 
-			return nv_crtc->set_scale(nv_crtc,
-						  nv_connector->scaling_mode,
-						  true);
+			return nv_crtc->set_scale(nv_crtc, true);
 		}
 
 		return 0;
@@ -514,9 +510,7 @@ nouveau_connector_set_property(struct drm_connector *connector,
 			if (!nv_crtc || !nv_crtc->set_scale)
 				return 0;
 
-			return nv_crtc->set_scale(nv_crtc,
-						  nv_connector->scaling_mode,
-						  true);
+			return nv_crtc->set_scale(nv_crtc, true);
 		}
 
 		return 0;
@@ -532,8 +526,7 @@ nouveau_connector_set_property(struct drm_connector *connector,
 		if (!nv_crtc || !nv_crtc->set_dither)
 			return 0;
 
-		return nv_crtc->set_dither(nv_crtc, nv_connector->use_dithering,
-					   true);
+		return nv_crtc->set_dither(nv_crtc, true);
 	}
 
 	if (nv_encoder && nv_encoder->dcb->type == OUTPUT_TV)
diff --git a/drivers/gpu/drm/nouveau/nouveau_crtc.h b/drivers/gpu/drm/nouveau/nouveau_crtc.h
index bf8e1289953d..b9b1980b5d8c 100644
--- a/drivers/gpu/drm/nouveau/nouveau_crtc.h
+++ b/drivers/gpu/drm/nouveau/nouveau_crtc.h
@@ -67,8 +67,8 @@ struct nouveau_crtc {
 		int depth;
 	} lut;
 
-	int (*set_dither)(struct nouveau_crtc *crtc, bool on, bool update);
-	int (*set_scale)(struct nouveau_crtc *crtc, int mode, bool update);
+	int (*set_dither)(struct nouveau_crtc *crtc, bool update);
+	int (*set_scale)(struct nouveau_crtc *crtc, bool update);
 };
 
 static inline struct nouveau_crtc *nouveau_crtc(struct drm_crtc *crtc)
diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c
index 03d4d811ed7b..1b88c6c4a6b9 100644
--- a/drivers/gpu/drm/nouveau/nv50_crtc.c
+++ b/drivers/gpu/drm/nouveau/nv50_crtc.c
@@ -132,10 +132,12 @@ nv50_crtc_blank(struct nouveau_crtc *nv_crtc, bool blanked)
 }
 
 static int
-nv50_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool on, bool update)
+nv50_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool update)
 {
 	struct drm_device *dev = nv_crtc->base.dev;
 	struct nouveau_channel *evo = nv50_display(dev)->master;
+	struct nouveau_connector *nv_connector =
+		nouveau_crtc_connector_get(nv_crtc);
 	int ret;
 
 	NV_DEBUG_KMS(dev, "\n");
@@ -147,7 +149,7 @@ nv50_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool on, bool update)
 	}
 
 	BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, DITHER_CTRL), 1);
-	if (on)
+	if (nv_connector->use_dithering)
 		OUT_RING(evo, NV50_EVO_CRTC_DITHER_CTRL_ON);
 	else
 		OUT_RING(evo, NV50_EVO_CRTC_DITHER_CTRL_OFF);
@@ -180,14 +182,14 @@ nouveau_crtc_connector_get(struct nouveau_crtc *nv_crtc)
 }
 
 static int
-nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, int scaling_mode, bool update)
+nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, bool update)
 {
 	struct nouveau_connector *nv_connector;
 	struct drm_device *dev = nv_crtc->base.dev;
 	struct nouveau_channel *evo = nv50_display(dev)->master;
 	struct drm_display_mode *mode = &nv_crtc->base.mode;
+	int scaling_mode, ret;
 	u32 ctrl = 0, oX, oY;
-	int ret;
 
 	NV_DEBUG_KMS(dev, "\n");
 
@@ -195,6 +197,8 @@ nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, int scaling_mode, bool update)
 	if (!nv_connector || !nv_connector->native_mode) {
 		NV_ERROR(dev, "no native mode, forcing panel scaling\n");
 		scaling_mode = DRM_MODE_SCALE_NONE;
+	} else {
+		scaling_mode = nv_connector->scaling_mode;
 	}
 
 	/* start off at the resolution we programmed the crtc for, this
@@ -692,8 +696,8 @@ nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
 	BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, SCALE_CENTER_OFFSET), 1);
 	OUT_RING(evo, NV50_EVO_CRTC_SCALE_CENTER_OFFSET_VAL(0, 0));
 
-	nv_crtc->set_dither(nv_crtc, nv_connector->use_dithering, false);
-	nv_crtc->set_scale(nv_crtc, nv_connector->scaling_mode, false);
+	nv_crtc->set_dither(nv_crtc, false);
+	nv_crtc->set_scale(nv_crtc, false);
 
 	return nv50_crtc_do_mode_set_base(crtc, old_fb, x, y, false);
 }
diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c
index 99873fc24c92..0303488db2ba 100644
--- a/drivers/gpu/drm/nouveau/nvd0_display.c
+++ b/drivers/gpu/drm/nouveau/nvd0_display.c
@@ -120,13 +120,14 @@ nvd0_display_crtc_get(struct drm_encoder *encoder)
  * CRTC
  *****************************************************************************/
 static int
-nvd0_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool on, bool update)
+nvd0_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool update)
 {
+	struct nouveau_connector *nv_connector;
 	struct drm_device *dev = nv_crtc->base.dev;
-	u32 *push, mode;
+	u32 *push, mode = 0;
 
-	mode = 0x00000000;
-	if (on) {
+	nv_connector = nouveau_crtc_connector_get(nv_crtc);
+	if (nv_connector->use_dithering) {
 		/* 0x11: 6bpc dynamic 2x2
 		 * 0x13: 8bpc dynamic 2x2
 		 * 0x19: 6bpc static 2x2
@@ -152,7 +153,7 @@ nvd0_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool on, bool update)
 }
 
 static int
-nvd0_crtc_set_scale(struct nouveau_crtc *nv_crtc, int type, bool update)
+nvd0_crtc_set_scale(struct nouveau_crtc *nv_crtc, bool update)
 {
 	struct drm_display_mode *mode = &nv_crtc->base.mode;
 	struct drm_device *dev = nv_crtc->base.dev;
@@ -168,7 +169,7 @@ nvd0_crtc_set_scale(struct nouveau_crtc *nv_crtc, int type, bool update)
 		u32 xratio = (native->hdisplay << 19) / mode->hdisplay;
 		u32 yratio = (native->vdisplay << 19) / mode->vdisplay;
 
-		switch (type) {
+		switch (nv_connector->scaling_mode) {
 		case DRM_MODE_SCALE_ASPECT:
 			if (xratio > yratio) {
 				outX = (mode->hdisplay * yratio) >> 19;
@@ -407,8 +408,8 @@ nvd0_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode,
 	}
 
 	nv_connector = nouveau_crtc_connector_get(nv_crtc);
-	nvd0_crtc_set_dither(nv_crtc, nv_connector->use_dithering, false);
-	nvd0_crtc_set_scale(nv_crtc, nv_connector->scaling_mode, false);
+	nvd0_crtc_set_dither(nv_crtc, false);
+	nvd0_crtc_set_scale(nv_crtc, false);
 	nvd0_crtc_set_image(nv_crtc, crtc->fb, x, y, false);
 	return 0;
 }
-- 
1.7.10.4


Reply to: