Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f22d6dda authored by Dave Airlie's avatar Dave Airlie
Browse files

Merge branch 'for-airlied' of /ssd/git/drm-nouveau-next into drm-linus

* 'for-airlied' of /ssd/git/drm-nouveau-next: (28 commits)
  drm/nv04: Fix set_operation software method.
  drm/nouveau: initialise DMA tracking parameters earlier
  drm/nouveau: use dma.max rather than pushbuf size for checking GET validity
  drm/nv04: differentiate between nv04/nv05
  drm/nouveau: Fix null deref in nouveau_fence_emit due to deleted fence
  drm/nv50: prevent a possible ctxprog hang
  drm/nouveau: have ttm's fault handler called directly
  drm/nv50: restore correct cache1 get/put address on fifoctx load
  drm/nouveau: create function for "dealing" with gpu lockup
  drm/nouveau: remove unused nouveau_channel_idle() function
  drm/nouveau: fix handling of fbcon colours in 8bpp
  drm/nv04: Context switching fixes.
  drm/nouveau: Use the software object for fencing.
  drm/nouveau: Allocate a per-channel instance of NV_SW.
  drm/nv50: make the blocksize depend on vram size
  drm/nouveau: better alignment of bo sizes and use roundup instead of ALIGN
  drm/nouveau: Don't skip card take down on nv0x.
  drm/nouveau: Implement nv42-nv43 TV load detection.
  drm/nouveau: Clean up the nv17-nv4x load detection code a bit.
  drm/nv50: fix fillrect color
  ...
parents 0c9d2c41 40c2298b
Loading
Loading
Loading
Loading
+2 −3
Original line number Diff line number Diff line
@@ -30,12 +30,11 @@ config DRM_NOUVEAU_DEBUG
	  via debugfs.

menu "I2C encoder or helper chips"
     depends on DRM && I2C
     depends on DRM && DRM_KMS_HELPER && I2C

config DRM_I2C_CH7006
	tristate "Chrontel ch7006 TV encoder"
	depends on DRM_NOUVEAU
	default m
	default m if DRM_NOUVEAU
	help
	  Support for Chrontel ch7006 and similar TV encoders, found
	  on some nVidia video cards.
+166 −77
Original line number Diff line number Diff line
@@ -33,10 +33,13 @@
#include "nouveau_drv.h"
#include "nouveau_dma.h"

#include <linux/log2.h>

static void
nouveau_bo_del_ttm(struct ttm_buffer_object *bo)
{
	struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev);
	struct drm_device *dev = dev_priv->dev;
	struct nouveau_bo *nvbo = nouveau_bo(bo);

	ttm_bo_kunmap(&nvbo->kmap);
@@ -44,85 +47,115 @@ nouveau_bo_del_ttm(struct ttm_buffer_object *bo)
	if (unlikely(nvbo->gem))
		DRM_ERROR("bo %p still attached to GEM object\n", bo);

	if (nvbo->tile)
		nv10_mem_expire_tiling(dev, nvbo->tile, NULL);

	spin_lock(&dev_priv->ttm.bo_list_lock);
	list_del(&nvbo->head);
	spin_unlock(&dev_priv->ttm.bo_list_lock);
	kfree(nvbo);
}

int
nouveau_bo_new(struct drm_device *dev, struct nouveau_channel *chan,
	       int size, int align, uint32_t flags, uint32_t tile_mode,
	       uint32_t tile_flags, bool no_vm, bool mappable,
	       struct nouveau_bo **pnvbo)
static void
nouveau_bo_fixup_align(struct drm_device *dev,
		       uint32_t tile_mode, uint32_t tile_flags,
		       int *align, int *size)
{
	struct drm_nouveau_private *dev_priv = dev->dev_private;
	struct nouveau_bo *nvbo;
	int ret, n = 0;

	nvbo = kzalloc(sizeof(struct nouveau_bo), GFP_KERNEL);
	if (!nvbo)
		return -ENOMEM;
	INIT_LIST_HEAD(&nvbo->head);
	INIT_LIST_HEAD(&nvbo->entry);
	nvbo->mappable = mappable;
	nvbo->no_vm = no_vm;
	nvbo->tile_mode = tile_mode;
	nvbo->tile_flags = tile_flags;

	/*
	 * Some of the tile_flags have a periodic structure of N*4096 bytes,
	 * align to to that as well as the page size. Overallocate memory to
	 * avoid corruption of other buffer objects.
	 */
	if (dev_priv->card_type == NV_50) {
		uint32_t block_size = nouveau_mem_fb_amount(dev) >> 15;
		int i;

		switch (tile_flags) {
		case 0x1800:
		case 0x2800:
		case 0x4800:
		case 0x7a00:
		if (dev_priv->chipset >= 0xA0) {
			/* This is based on high end cards with 448 bits
			 * memory bus, could be different elsewhere.*/
			size += 6 * 28672;
			/* 8 * 28672 is the actual alignment requirement,
			 * but we must also align to page size. */
			align = 2 * 8 * 28672;
		} else if (dev_priv->chipset >= 0x90) {
			size += 3 * 16384;
			align = 12 * 16834;
			*size = roundup(*size, block_size);
			if (is_power_of_2(block_size)) {
				*size += 3 * block_size;
				for (i = 1; i < 10; i++) {
					*align = 12 * i * block_size;
					if (!(*align % 65536))
						break;
				}
			} else {
			size += 3 * 8192;
			/* 12 * 8192 is the actual alignment requirement,
			 * but we must also align to page size. */
			align = 2 * 12 * 8192;
				*size += 6 * block_size;
				for (i = 1; i < 10; i++) {
					*align = 8 * i * block_size;
					if (!(*align % 65536))
						break;
				}
			}
			break;
		default:
			break;
		}

	align >>= PAGE_SHIFT;
	} else {
		if (tile_mode) {
			if (dev_priv->chipset >= 0x40) {
				*align = 65536;
				*size = roundup(*size, 64 * tile_mode);

			} else if (dev_priv->chipset >= 0x30) {
				*align = 32768;
				*size = roundup(*size, 64 * tile_mode);

			} else if (dev_priv->chipset >= 0x20) {
				*align = 16384;
				*size = roundup(*size, 64 * tile_mode);

			} else if (dev_priv->chipset >= 0x10) {
				*align = 16384;
				*size = roundup(*size, 32 * tile_mode);
			}
		}
	}

	/* ALIGN works only on powers of two. */
	*size = roundup(*size, PAGE_SIZE);

	size = (size + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
	if (dev_priv->card_type == NV_50) {
		size = (size + 65535) & ~65535;
		if (align < (65536 / PAGE_SIZE))
			align = (65536 / PAGE_SIZE);
		*size = roundup(*size, 65536);
		*align = max(65536, *align);
	}
}

	if (flags & TTM_PL_FLAG_VRAM)
		nvbo->placements[n++] = TTM_PL_FLAG_VRAM | TTM_PL_MASK_CACHING;
	if (flags & TTM_PL_FLAG_TT)
		nvbo->placements[n++] = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING;
int
nouveau_bo_new(struct drm_device *dev, struct nouveau_channel *chan,
	       int size, int align, uint32_t flags, uint32_t tile_mode,
	       uint32_t tile_flags, bool no_vm, bool mappable,
	       struct nouveau_bo **pnvbo)
{
	struct drm_nouveau_private *dev_priv = dev->dev_private;
	struct nouveau_bo *nvbo;
	int ret = 0;

	nvbo = kzalloc(sizeof(struct nouveau_bo), GFP_KERNEL);
	if (!nvbo)
		return -ENOMEM;
	INIT_LIST_HEAD(&nvbo->head);
	INIT_LIST_HEAD(&nvbo->entry);
	nvbo->mappable = mappable;
	nvbo->no_vm = no_vm;
	nvbo->tile_mode = tile_mode;
	nvbo->tile_flags = tile_flags;

	nouveau_bo_fixup_align(dev, tile_mode, tile_flags, &align, &size);
	align >>= PAGE_SHIFT;

	nvbo->placement.fpfn = 0;
	nvbo->placement.lpfn = mappable ? dev_priv->fb_mappable_pages : 0;
	nvbo->placement.placement = nvbo->placements;
	nvbo->placement.busy_placement = nvbo->placements;
	nvbo->placement.num_placement = n;
	nvbo->placement.num_busy_placement = n;
	nouveau_bo_placement_set(nvbo, flags);

	nvbo->channel = chan;
	nouveau_bo_placement_set(nvbo, flags);
	ret = ttm_bo_init(&dev_priv->ttm.bdev, &nvbo->bo, size,
			  ttm_bo_type_device, &nvbo->placement, align, 0,
			  false, NULL, size, nouveau_bo_del_ttm);
@@ -421,6 +454,7 @@ nouveau_bo_evict_flags(struct ttm_buffer_object *bo, struct ttm_placement *pl)
/* GPU-assisted copy using NV_MEMORY_TO_MEMORY_FORMAT, can access
 * TTM_PL_{VRAM,TT} directly.
 */

static int
nouveau_bo_move_accel_cleanup(struct nouveau_channel *chan,
			      struct nouveau_bo *nvbo, bool evict, bool no_wait,
@@ -455,11 +489,12 @@ nouveau_bo_mem_ctxdma(struct nouveau_bo *nvbo, struct nouveau_channel *chan,
}

static int
nouveau_bo_move_m2mf(struct ttm_buffer_object *bo, int evict, int no_wait,
		     struct ttm_mem_reg *old_mem, struct ttm_mem_reg *new_mem)
nouveau_bo_move_m2mf(struct ttm_buffer_object *bo, int evict, bool intr,
		     int no_wait, struct ttm_mem_reg *new_mem)
{
	struct nouveau_bo *nvbo = nouveau_bo(bo);
	struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev);
	struct ttm_mem_reg *old_mem = &bo->mem;
	struct nouveau_channel *chan;
	uint64_t src_offset, dst_offset;
	uint32_t page_count;
@@ -547,7 +582,7 @@ nouveau_bo_move_flipd(struct ttm_buffer_object *bo, bool evict, bool intr,

	placement.fpfn = placement.lpfn = 0;
	placement.num_placement = placement.num_busy_placement = 1;
	placement.placement = &placement_memtype;
	placement.placement = placement.busy_placement = &placement_memtype;

	tmp_mem = *new_mem;
	tmp_mem.mm_node = NULL;
@@ -559,7 +594,7 @@ nouveau_bo_move_flipd(struct ttm_buffer_object *bo, bool evict, bool intr,
	if (ret)
		goto out;

	ret = nouveau_bo_move_m2mf(bo, true, no_wait, &bo->mem, &tmp_mem);
	ret = nouveau_bo_move_m2mf(bo, true, intr, no_wait, &tmp_mem);
	if (ret)
		goto out;

@@ -585,7 +620,7 @@ nouveau_bo_move_flips(struct ttm_buffer_object *bo, bool evict, bool intr,

	placement.fpfn = placement.lpfn = 0;
	placement.num_placement = placement.num_busy_placement = 1;
	placement.placement = &placement_memtype;
	placement.placement = placement.busy_placement = &placement_memtype;

	tmp_mem = *new_mem;
	tmp_mem.mm_node = NULL;
@@ -597,7 +632,7 @@ nouveau_bo_move_flips(struct ttm_buffer_object *bo, bool evict, bool intr,
	if (ret)
		goto out;

	ret = nouveau_bo_move_m2mf(bo, true, no_wait, &bo->mem, new_mem);
	ret = nouveau_bo_move_m2mf(bo, evict, intr, no_wait, new_mem);
	if (ret)
		goto out;

@@ -612,52 +647,106 @@ nouveau_bo_move_flips(struct ttm_buffer_object *bo, bool evict, bool intr,
}

static int
nouveau_bo_move(struct ttm_buffer_object *bo, bool evict, bool intr,
		bool no_wait, struct ttm_mem_reg *new_mem)
nouveau_bo_vm_bind(struct ttm_buffer_object *bo, struct ttm_mem_reg *new_mem,
		   struct nouveau_tile_reg **new_tile)
{
	struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev);
	struct nouveau_bo *nvbo = nouveau_bo(bo);
	struct drm_device *dev = dev_priv->dev;
	struct ttm_mem_reg *old_mem = &bo->mem;
	struct nouveau_bo *nvbo = nouveau_bo(bo);
	uint64_t offset;
	int ret;

	if (dev_priv->card_type == NV_50 && new_mem->mem_type == TTM_PL_VRAM &&
	    !nvbo->no_vm) {
		uint64_t offset = new_mem->mm_node->start << PAGE_SHIFT;
	if (nvbo->no_vm || new_mem->mem_type != TTM_PL_VRAM) {
		/* Nothing to do. */
		*new_tile = NULL;
		return 0;
	}

	offset = new_mem->mm_node->start << PAGE_SHIFT;

	if (dev_priv->card_type == NV_50) {
		ret = nv50_mem_vm_bind_linear(dev,
					      offset + dev_priv->vm_vram_base,
					      new_mem->size, nvbo->tile_flags,
					      offset);
		if (ret)
			return ret;

	} else if (dev_priv->card_type >= NV_10) {
		*new_tile = nv10_mem_set_tiling(dev, offset, new_mem->size,
						nvbo->tile_mode);
	}

	return 0;
}

static void
nouveau_bo_vm_cleanup(struct ttm_buffer_object *bo,
		      struct nouveau_tile_reg *new_tile,
		      struct nouveau_tile_reg **old_tile)
{
	struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev);
	struct drm_device *dev = dev_priv->dev;

	if (dev_priv->card_type >= NV_10 &&
	    dev_priv->card_type < NV_50) {
		if (*old_tile)
			nv10_mem_expire_tiling(dev, *old_tile, bo->sync_obj);

		*old_tile = new_tile;
	}
}

static int
nouveau_bo_move(struct ttm_buffer_object *bo, bool evict, bool intr,
		bool no_wait, struct ttm_mem_reg *new_mem)
{
	struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev);
	struct nouveau_bo *nvbo = nouveau_bo(bo);
	struct ttm_mem_reg *old_mem = &bo->mem;
	struct nouveau_tile_reg *new_tile = NULL;
	int ret = 0;

	ret = nouveau_bo_vm_bind(bo, new_mem, &new_tile);
	if (ret)
		return ret;

	/* Software copy if the card isn't up and running yet. */
	if (dev_priv->init_state != NOUVEAU_CARD_INIT_DONE ||
	    !dev_priv->channel)
		return ttm_bo_move_memcpy(bo, evict, no_wait, new_mem);
	    !dev_priv->channel) {
		ret = ttm_bo_move_memcpy(bo, evict, no_wait, new_mem);
		goto out;
	}

	/* Fake bo copy. */
	if (old_mem->mem_type == TTM_PL_SYSTEM && !bo->ttm) {
		BUG_ON(bo->mem.mm_node != NULL);
		bo->mem = *new_mem;
		new_mem->mm_node = NULL;
		return 0;
		goto out;
	}

	if (new_mem->mem_type == TTM_PL_SYSTEM) {
		if (old_mem->mem_type == TTM_PL_SYSTEM)
			return ttm_bo_move_memcpy(bo, evict, no_wait, new_mem);
		if (nouveau_bo_move_flipd(bo, evict, intr, no_wait, new_mem))
			return ttm_bo_move_memcpy(bo, evict, no_wait, new_mem);
	} else if (old_mem->mem_type == TTM_PL_SYSTEM) {
		if (nouveau_bo_move_flips(bo, evict, intr, no_wait, new_mem))
			return ttm_bo_move_memcpy(bo, evict, no_wait, new_mem);
	} else {
		if (nouveau_bo_move_m2mf(bo, evict, no_wait, old_mem, new_mem))
			return ttm_bo_move_memcpy(bo, evict, no_wait, new_mem);
	}
	/* Hardware assisted copy. */
	if (new_mem->mem_type == TTM_PL_SYSTEM)
		ret = nouveau_bo_move_flipd(bo, evict, intr, no_wait, new_mem);
	else if (old_mem->mem_type == TTM_PL_SYSTEM)
		ret = nouveau_bo_move_flips(bo, evict, intr, no_wait, new_mem);
	else
		ret = nouveau_bo_move_m2mf(bo, evict, intr, no_wait, new_mem);

	return 0;
	if (!ret)
		goto out;

	/* Fallback to software copy. */
	ret = ttm_bo_move_memcpy(bo, evict, no_wait, new_mem);

out:
	if (ret)
		nouveau_bo_vm_cleanup(bo, NULL, &new_tile);
	else
		nouveau_bo_vm_cleanup(bo, new_tile, &nvbo->tile);

	return ret;
}

static int
+5 −42
Original line number Diff line number Diff line
@@ -158,6 +158,8 @@ nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret,
		return ret;
	}

	nouveau_dma_pre_init(chan);

	/* Locate channel's user control regs */
	if (dev_priv->card_type < NV_40)
		user = NV03_USER(channel);
@@ -235,47 +237,6 @@ nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret,
	return 0;
}

int
nouveau_channel_idle(struct nouveau_channel *chan)
{
	struct drm_device *dev = chan->dev;
	struct drm_nouveau_private *dev_priv = dev->dev_private;
	struct nouveau_engine *engine = &dev_priv->engine;
	uint32_t caches;
	int idle;

	if (!chan) {
		NV_ERROR(dev, "no channel...\n");
		return 1;
	}

	caches = nv_rd32(dev, NV03_PFIFO_CACHES);
	nv_wr32(dev, NV03_PFIFO_CACHES, caches & ~1);

	if (engine->fifo.channel_id(dev) != chan->id) {
		struct nouveau_gpuobj *ramfc =
			chan->ramfc ? chan->ramfc->gpuobj : NULL;

		if (!ramfc) {
			NV_ERROR(dev, "No RAMFC for channel %d\n", chan->id);
			return 1;
		}

		engine->instmem.prepare_access(dev, false);
		if (nv_ro32(dev, ramfc, 0) != nv_ro32(dev, ramfc, 1))
			idle = 0;
		else
			idle = 1;
		engine->instmem.finish_access(dev);
	} else {
		idle = (nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_GET) ==
			nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUT));
	}

	nv_wr32(dev, NV03_PFIFO_CACHES, caches);
	return idle;
}

/* stops a fifo */
void
nouveau_channel_free(struct nouveau_channel *chan)
@@ -414,7 +375,9 @@ nouveau_ioctl_fifo_alloc(struct drm_device *dev, void *data,
		init->subchan[0].grclass = 0x0039;
	else
		init->subchan[0].grclass = 0x5039;
	init->nr_subchan = 1;
	init->subchan[1].handle = NvSw;
	init->subchan[1].grclass = NV_SW;
	init->nr_subchan = 2;

	/* Named memory object area */
	ret = drm_gem_handle_create(file_priv, chan->notifier_bo->gem,
+27 −7
Original line number Diff line number Diff line
@@ -29,12 +29,22 @@
#include "nouveau_drv.h"
#include "nouveau_dma.h"

void
nouveau_dma_pre_init(struct nouveau_channel *chan)
{
	chan->dma.max  = (chan->pushbuf_bo->bo.mem.size >> 2) - 2;
	chan->dma.put  = 0;
	chan->dma.cur  = chan->dma.put;
	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;
	struct nouveau_gpuobj *m2mf = NULL;
	struct nouveau_gpuobj *nvsw = NULL;
	int ret, i;

	/* Create NV_MEMORY_TO_MEMORY_FORMAT for buffer moves */
@@ -47,6 +57,15 @@ nouveau_dma_init(struct nouveau_channel *chan)
	if (ret)
		return ret;

	/* Create an NV_SW object for various sync purposes */
	ret = nouveau_gpuobj_sw_new(chan, NV_SW, &nvsw);
	if (ret)
		return ret;

	ret = nouveau_gpuobj_ref_add(dev, chan, NvSw, nvsw, NULL);
	if (ret)
		return ret;

	/* NV_MEMORY_TO_MEMORY_FORMAT requires a notifier object */
	ret = nouveau_notifier_alloc(chan, NvNotify0, 32, &chan->m2mf_ntfy);
	if (ret)
@@ -64,12 +83,6 @@ nouveau_dma_init(struct nouveau_channel *chan)
			return ret;
	}

	/* Initialise DMA vars */
	chan->dma.max  = (chan->pushbuf_bo->bo.mem.size >> 2) - 2;
	chan->dma.put  = 0;
	chan->dma.cur  = chan->dma.put;
	chan->dma.free = chan->dma.max - chan->dma.cur;

	/* Insert NOPS for NOUVEAU_DMA_SKIPS */
	ret = RING_SPACE(chan, NOUVEAU_DMA_SKIPS);
	if (ret)
@@ -87,6 +100,13 @@ nouveau_dma_init(struct nouveau_channel *chan)
	BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_DMA_NOTIFY, 1);
	OUT_RING(chan, NvNotify0);

	/* Initialise NV_SW */
	ret = RING_SPACE(chan, 2);
	if (ret)
		return ret;
	BEGIN_RING(chan, NvSubSw, 0, 1);
	OUT_RING(chan, NvSw);

	/* Sit back and pray the channel works.. */
	FIRE_RING(chan);

@@ -113,7 +133,7 @@ READ_GET(struct nouveau_channel *chan, uint32_t *get)

	val = nvchan_rd32(chan, chan->user_get);
	if (val < chan->pushbuf_base ||
	    val >= chan->pushbuf_base + chan->pushbuf_bo->bo.mem.size) {
	    val > chan->pushbuf_base + (chan->dma.max << 2)) {
		/* meaningless to dma_wait() except to know whether the
		 * GPU has stalled or not
		 */
+6 −4
Original line number Diff line number Diff line
@@ -46,10 +46,11 @@
/* Hardcoded object assignments to subchannels (subchannel id). */
enum {
	NvSubM2MF	= 0,
	NvSub2D		= 1,
	NvSubCtxSurf2D  = 1,
	NvSubGdiRect    = 2,
	NvSubImageBlit  = 3
	NvSubSw		= 1,
	NvSub2D		= 2,
	NvSubCtxSurf2D  = 2,
	NvSubGdiRect    = 3,
	NvSubImageBlit  = 4
};

/* Object handles. */
@@ -67,6 +68,7 @@ enum {
	NvClipRect	= 0x8000000b,
	NvGdiRect	= 0x8000000c,
	NvImageBlit	= 0x8000000d,
	NvSw		= 0x8000000e,

	/* G80+ display objects */
	NvEvoVRAM	= 0x01000000,
Loading