/************************************************************************** * * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA * All Rights Reserved. * * 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, sub license, 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 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 NON-INFRINGEMENT. IN NO EVENT SHALL * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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. * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial portions * of the Software. * * **************************************************************************/ /* * Authors: Thomas Hellström */ #include "drmP.h" #include "drm.h" #include "i915_drm.h" #include "i915_drv.h" /* * Initiate a sync flush if it's not already pending. */ static inline void i915_initiate_rwflush(struct drm_i915_private *dev_priv, struct drm_fence_class_manager *fc) { if ((fc->pending_flush & DRM_I915_FENCE_TYPE_RW) && !dev_priv->flush_pending) { dev_priv->flush_sequence = (uint32_t) READ_BREADCRUMB(dev_priv); dev_priv->flush_flags = fc->pending_flush; dev_priv->saved_flush_status = READ_HWSP(dev_priv, 0); I915_WRITE(INSTPM, (1 << 5) | (1 << 21)); dev_priv->flush_pending = 1; fc->pending_flush &= ~DRM_I915_FENCE_TYPE_RW; } } static inline void i915_report_rwflush(struct drm_device *dev, struct drm_i915_private *dev_priv) { if (unlikely(dev_priv->flush_pending)) { uint32_t flush_flags; uint32_t i_status; uint32_t flush_sequence; i_status = READ_HWSP(dev_priv, 0); if ((i_status & (1 << 12)) != (dev_priv->saved_flush_status & (1 << 12))) { flush_flags = dev_priv->flush_flags; flush_sequence = dev_priv->flush_sequence; dev_priv->flush_pending = 0; drm_fence_handler(dev, 0, flush_sequence, flush_flags, 0); } } } static void i915_fence_flush(struct drm_device *dev, uint32_t fence_class) { struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private; struct drm_fence_manager *fm = &dev->fm; struct drm_fence_class_manager *fc = &fm->fence_class[0]; unsigned long irq_flags; if (unlikely(!dev_priv)) return; write_lock_irqsave(&fm->lock, irq_flags); i915_initiate_rwflush(dev_priv, fc); write_unlock_irqrestore(&fm->lock, irq_flags); } static void i915_fence_poll(struct drm_device *dev, uint32_t fence_class, uint32_t waiting_types) { struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private; struct drm_fence_manager *fm = &dev->fm; struct drm_fence_class_manager *fc = &fm->fence_class[0]; uint32_t sequence; if (unlikely(!dev_priv)) return; /* * First, report any executed sync flush: */ i915_report_rwflush(dev, dev_priv); /* * Report A new breadcrumb, and adjust IRQs. */ if (waiting_types & DRM_FENCE_TYPE_EXE) { sequence = READ_BREADCRUMB(dev_priv); drm_fence_handler(dev, 0, sequence, DRM_FENCE_TYPE_EXE, 0); if (dev_priv->fence_irq_on && !(fc->waiting_types & DRM_FENCE_TYPE_EXE)) { i915_user_irq_off(dev); dev_priv->fence_irq_on = 0; } else if (!dev_priv->fence_irq_on && (fc->waiting_types & DRM_FENCE_TYPE_EXE)) { i915_user_irq_on(dev); dev_priv->fence_irq_on = 1; } } /* * There may be new RW flushes pending. Start them. */ i915_initiate_rwflush(dev_priv, fc); /* * And possibly, but unlikely, they finish immediately. */ i915_report_rwflush(dev, dev_priv); } static int i915_fence_emit_sequence(struct drm_device *dev, uint32_t class, uint32_t flags, uint32_t *sequence, uint32_t *native_type) { struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private; if (unlikely(!dev_priv)) return -EINVAL; i915_emit_irq(dev); *sequence = (uint32_t) dev_priv->counter; *native_type = DRM_FENCE_TYPE_EXE; if (flags & DRM_I915_FENCE_FLAG_FLUSHED) *native_type |= DRM_I915_FENCE_TYPE_RW; return 0; } void i915_fence_handler(struct drm_device *dev) { struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private; struct drm_fence_manager *fm = &dev->fm; struct drm_fence_class_manager *fc = &fm->fence_class[0]; write_lock(&fm->lock); if (likely(dev_priv->fence_irq_on)) i915_fence_poll(dev, 0, fc->waiting_types); write_unlock(&fm->lock); } /* * We need a separate wait function since we need to poll for * sync flushes. */ static int i915_fence_wait(struct drm_fence_object *fence, int lazy, int interruptible, uint32_t mask) { struct drm_device *dev = fence->dev; struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private; struct drm_fence_manager *fm = &dev->fm; struct drm_fence_class_manager *fc = &fm->fence_class[0]; int ret; unsigned long _end = jiffies + 3 * DRM_HZ; drm_fence_object_flush(fence, mask); if (likely(interruptible)) ret = wait_event_interruptible_timeout (fc->fence_queue, drm_fence_object_signaled(fence, DRM_FENCE_TYPE_EXE), 3 * DRM_HZ); else ret = wait_event_timeout (fc->fence_queue, drm_fence_object_signaled(fence, DRM_FENCE_TYPE_EXE), 3 * DRM_HZ); if (unlikely(ret == -ERESTARTSYS)) return -EAGAIN; if (unlikely(ret == 0)) return -EBUSY; if (likely(mask == DRM_FENCE_TYPE_EXE || drm_fence_object_signaled(fence, mask))) return 0; /* * Remove this code snippet when fixed. HWSTAM doesn't let * flush info through... */ if (unlikely(dev_priv && !dev_priv->irq_enabled)) { unsigned long irq_flags; DRM_ERROR("X server disabled IRQs before releasing frame buffer.\n"); msleep(100); dev_priv->flush_pending = 0; write_lock_irqsave(&fm->lock, irq_flags); drm_fence_handler(dev, fence->fence_class, fence->sequence, fence->type, 0); write_unlock_irqrestore(&fm->lock, irq_flags); } /* * Poll for sync flush completion. */ return drm_fence_wait_polling(fence, lazy, interruptible, mask, _end); } static uint32_t i915_fence_needed_flush(struct drm_fence_object *fence) { uint32_t flush_flags = fence->waiting_types & ~(DRM_FENCE_TYPE_EXE | fence->signaled_types); if (likely(flush_flags == 0 || ((flush_flags & ~fence->native_types) == 0) || (fence->signaled_types != DRM_FENCE_TYPE_EXE))) return 0; else { struct drm_device *dev = fence->dev; struct drm_i915_private *dev_priv = (struct drm_i915_private *) dev->dev_private; struct drm_fence_driver *driver = dev->driver->fence_driver; if (unlikely(!dev_priv)) return 0; if (dev_priv->flush_pending) { uint32_t diff = (dev_priv->flush_sequence - fence->sequence) & driver->sequence_mask; if (diff < driver->wrap_diff) return 0; } } return flush_flags; } struct drm_fence_driver i915_fence_driver = { .num_classes = 1, .wrap_diff = (1U << (BREADCRUMB_BITS - 1)), .flush_diff = (1U << (BREADCRUMB_BITS - 2)), .sequence_mask = BREADCRUMB_MASK, .has_irq = NULL, .emit = i915_fence_emit_sequence, .flush = i915_fence_flush, .poll = i915_fence_poll, .needed_flush = i915_fence_needed_flush, .wait = i915_fence_wait, }; href='#n152'>152 153 154
/*
 * Based on nv40_graph.c
 *  Someday this will all go away...
 */
#include "drmP.h"
#include "drm.h"
#include "nouveau_drv.h"
#include "nouveau_drm.h"

/*
 *  This is obviously not the correct size. 
 */
#define NV30_GRCTX_SIZE (23840)

/*TODO: deciper what each offset in the context represents. The below
 *      contexts are taken from dumps just after the 3D object is
 *      created.
 */
static void nv30_graph_context_init(drm_device_t *dev, struct mem_block *ctx)
{
	drm_nouveau_private_t *dev_priv = dev->dev_private;
	int i;
        
        INSTANCE_WR(ctx, 0x28/4,  0x10000000);
        INSTANCE_WR(ctx, 0x40c/4, 0x00000101);
        INSTANCE_WR(ctx, 0x420/4, 0x00000111);
        INSTANCE_WR(ctx, 0x424/4, 0x00000060);
        INSTANCE_WR(ctx, 0x440/4, 0x00000080);
        INSTANCE_WR(ctx, 0x444/4, 0xffff0000);
        INSTANCE_WR(ctx, 0x448/4, 0x00000001);
        INSTANCE_WR(ctx, 0x45c/4, 0x44400000);
        INSTANCE_WR(ctx, 0x448/4, 0xffff0000);
        INSTANCE_WR(ctx, 0x4dc/4, 0xfff00000);
        INSTANCE_WR(ctx, 0x4e0/4, 0xfff00000);
        INSTANCE_WR(ctx, 0x4e8/4, 0x00011100);

        for (i = 0x504; i <= 0x540; i += 4)
                INSTANCE_WR(ctx, i/4, 0x7ff00000);

        INSTANCE_WR(ctx, 0x54c/4, 0x4b7fffff);
        INSTANCE_WR(ctx, 0x588/4, 0x00000080);
        INSTANCE_WR(ctx, 0x58c/4, 0x30201000);
        INSTANCE_WR(ctx, 0x590/4, 0x70605040);
        INSTANCE_WR(ctx, 0x594/4, 0xb8a89888);
        INSTANCE_WR(ctx, 0x598/4, 0xf8e8d8c8);
        INSTANCE_WR(ctx, 0x5ac/4, 0xb0000000);

        for (i = 0x604; i <= 0x640; i += 4)
                INSTANCE_WR(ctx, i/4, 0x00010588);

        for (i = 0x644; i <= 0x680; i += 4)
                INSTANCE_WR(ctx, i/4, 0x00030303);

        for (i = 0x6c4; i <= 0x700; i += 4)
                INSTANCE_WR(ctx, i/4, 0x0008aae4);

        for (i = 0x704; i <= 0x740; i += 4)
                INSTANCE_WR(ctx, i/4, 0x1012000);

        for (i = 0x744; i <= 0x780; i += 4)
                INSTANCE_WR(ctx, i/4, 0x0080008);

        INSTANCE_WR(ctx, 0x860/4, 0x00040000);
        INSTANCE_WR(ctx, 0x864/4, 0x00010000);
        INSTANCE_WR(ctx, 0x868/4, 0x00040000);
        INSTANCE_WR(ctx, 0x86c/4, 0x00040000);
        INSTANCE_WR(ctx, 0x870/4, 0x00040000);
        INSTANCE_WR(ctx, 0x874/4, 0x00040000);

        for (i = 0x00; i <= 0x1170; i += 0x10)
        {
                INSTANCE_WR(ctx, (0x1f24 + i)/4, 0x000c001b);
                INSTANCE_WR(ctx, (0x1f20 + i)/4, 0x0436086c);
                INSTANCE_WR(ctx, (0x1f1c + i)/4, 0x10700ff9);
        }

        INSTANCE_WR(ctx, 0x30bc/4, 0x0000ffff);
        INSTANCE_WR(ctx, 0x30c0/4, 0x0000ffff);
        INSTANCE_WR(ctx, 0x30c4/4, 0x0000ffff);
        INSTANCE_WR(ctx, 0x30c8/4, 0x0000ffff);

        INSTANCE_WR(ctx, 0x380c/4, 0x3f800000);
        INSTANCE_WR(ctx, 0x3450/4, 0x3f800000);
        INSTANCE_WR(ctx, 0x3820/4, 0x3f800000);
        INSTANCE_WR(ctx, 0x3854/4, 0x3f800000);
        INSTANCE_WR(ctx, 0x3850/4, 0x3f000000);
        INSTANCE_WR(ctx, 0x384c/4, 0x40000000);
        INSTANCE_WR(ctx, 0x3868/4, 0xbf800000);
        INSTANCE_WR(ctx, 0x3860/4, 0x3f800000);
        INSTANCE_WR(ctx, 0x386c/4, 0x40000000);
        INSTANCE_WR(ctx, 0x3870/4, 0xbf800000);

        for (i = 0x4e0; i <= 0x4e1c; i += 4)
                INSTANCE_WR(ctx, i/4, 0x001c527d);
        INSTANCE_WR(ctx, 0x4e40, 0x001c527c);

        INSTANCE_WR(ctx, 0x5680/4, 0x000a0000);
        INSTANCE_WR(ctx, 0x87c/4, 0x10000000);
        INSTANCE_WR(ctx, 0x28/4, 0x10000011);
}


int nv30_graph_context_create(drm_device_t *dev, int channel)
{
	drm_nouveau_private_t *dev_priv =
		(drm_nouveau_private_t *)dev->dev_private;
	struct nouveau_fifo *chan = &dev_priv->fifos[channel];
	void (*ctx_init)(drm_device_t *, struct mem_block *);
	unsigned int ctx_size;
	int i;

	switch (dev_priv->chipset) {
	default:
		ctx_size = NV30_GRCTX_SIZE;
		ctx_init = nv30_graph_context_init;
		break;
	}

	/* Alloc and clear RAMIN to store the context */
	chan->ramin_grctx = nouveau_instmem_alloc(dev, ctx_size, 4);
	if (!chan->ramin_grctx)
		return DRM_ERR(ENOMEM);
	for (i=0; i<ctx_size; i+=4)
		INSTANCE_WR(chan->ramin_grctx, i/4, 0x00000000);

	/* Initialise default context values */
	ctx_init(dev, chan->ramin_grctx);
        
        INSTANCE_WR(chan->ramin_grctx, 10, channel << 24); /* CTX_USER */
        INSTANCE_WR(dev_priv->ctx_table, channel, nouveau_chip_instance_get(dev, chan->ramin_grctx));

	return 0;
}

int nv30_graph_init(drm_device_t *dev)
{
	drm_nouveau_private_t *dev_priv =
		(drm_nouveau_private_t *)dev->dev_private;
	int i;

        /* Create Context Pointer Table */
        dev_priv->ctx_table_size = 32 * 4;
        dev_priv->ctx_table = nouveau_instmem_alloc(dev, dev_priv->ctx_table_size, 4);
        if (!dev_priv->ctx_table)
                return DRM_ERR(ENOMEM);

        for (i=0; i< dev_priv->ctx_table_size; i+=4)
                INSTANCE_WR(dev_priv->ctx_table, i/4, 0x00000000);

        NV_WRITE(NV10_PGRAPH_CHANNEL_CTX_TABLE, nouveau_chip_instance_get(dev, dev_priv->ctx_table));

	return 0;
}