i915_dma.c

来自「linux 内核源代码」· C语言 代码 · 共 851 行 · 第 1/2 页

C
851
字号
/* i915_dma.c -- DMA support for the I915 -*- linux-c -*- *//* * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas. * 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 above copyright notice and this permission notice (including the * next paragraph) 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 NON-INFRINGEMENT. * IN NO EVENT SHALL TUNGSTEN GRAPHICS 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. * */#include "drmP.h"#include "drm.h"#include "i915_drm.h"#include "i915_drv.h"#define IS_I965G(dev) (dev->pci_device == 0x2972 || \		       dev->pci_device == 0x2982 || \		       dev->pci_device == 0x2992 || \		       dev->pci_device == 0x29A2 || \		       dev->pci_device == 0x2A02 || \		       dev->pci_device == 0x2A12)#define IS_G33(dev) (dev->pci_device == 0x29b2 || \		     dev->pci_device == 0x29c2 || \		     dev->pci_device == 0x29d2)/* Really want an OS-independent resettable timer.  Would like to have * this loop run for (eg) 3 sec, but have the timer reset every time * the head pointer changes, so that EBUSY only happens if the ring * actually stalls for (eg) 3 seconds. */int i915_wait_ring(struct drm_device * dev, int n, const char *caller){	drm_i915_private_t *dev_priv = dev->dev_private;	drm_i915_ring_buffer_t *ring = &(dev_priv->ring);	u32 last_head = I915_READ(LP_RING + RING_HEAD) & HEAD_ADDR;	int i;	for (i = 0; i < 10000; i++) {		ring->head = I915_READ(LP_RING + RING_HEAD) & HEAD_ADDR;		ring->space = ring->head - (ring->tail + 8);		if (ring->space < 0)			ring->space += ring->Size;		if (ring->space >= n)			return 0;		dev_priv->sarea_priv->perf_boxes |= I915_BOX_WAIT;		if (ring->head != last_head)			i = 0;		last_head = ring->head;	}	return -EBUSY;}void i915_kernel_lost_context(struct drm_device * dev){	drm_i915_private_t *dev_priv = dev->dev_private;	drm_i915_ring_buffer_t *ring = &(dev_priv->ring);	ring->head = I915_READ(LP_RING + RING_HEAD) & HEAD_ADDR;	ring->tail = I915_READ(LP_RING + RING_TAIL) & TAIL_ADDR;	ring->space = ring->head - (ring->tail + 8);	if (ring->space < 0)		ring->space += ring->Size;	if (ring->head == ring->tail)		dev_priv->sarea_priv->perf_boxes |= I915_BOX_RING_EMPTY;}static int i915_dma_cleanup(struct drm_device * dev){	/* Make sure interrupts are disabled here because the uninstall ioctl	 * may not have been called from userspace and after dev_private	 * is freed, it's too late.	 */	if (dev->irq)		drm_irq_uninstall(dev);	if (dev->dev_private) {		drm_i915_private_t *dev_priv =		    (drm_i915_private_t *) dev->dev_private;		if (dev_priv->ring.virtual_start) {			drm_core_ioremapfree(&dev_priv->ring.map, dev);		}		if (dev_priv->status_page_dmah) {			drm_pci_free(dev, dev_priv->status_page_dmah);			/* Need to rewrite hardware status page */			I915_WRITE(0x02080, 0x1ffff000);		}		if (dev_priv->status_gfx_addr) {			dev_priv->status_gfx_addr = 0;			drm_core_ioremapfree(&dev_priv->hws_map, dev);			I915_WRITE(0x2080, 0x1ffff000);		}		drm_free(dev->dev_private, sizeof(drm_i915_private_t),			 DRM_MEM_DRIVER);		dev->dev_private = NULL;	}	return 0;}static int i915_initialize(struct drm_device * dev,			   drm_i915_private_t * dev_priv,			   drm_i915_init_t * init){	memset(dev_priv, 0, sizeof(drm_i915_private_t));	dev_priv->sarea = drm_getsarea(dev);	if (!dev_priv->sarea) {		DRM_ERROR("can not find sarea!\n");		dev->dev_private = (void *)dev_priv;		i915_dma_cleanup(dev);		return -EINVAL;	}	dev_priv->mmio_map = drm_core_findmap(dev, init->mmio_offset);	if (!dev_priv->mmio_map) {		dev->dev_private = (void *)dev_priv;		i915_dma_cleanup(dev);		DRM_ERROR("can not find mmio map!\n");		return -EINVAL;	}	dev_priv->sarea_priv = (drm_i915_sarea_t *)	    ((u8 *) dev_priv->sarea->handle + init->sarea_priv_offset);	dev_priv->ring.Start = init->ring_start;	dev_priv->ring.End = init->ring_end;	dev_priv->ring.Size = init->ring_size;	dev_priv->ring.tail_mask = dev_priv->ring.Size - 1;	dev_priv->ring.map.offset = init->ring_start;	dev_priv->ring.map.size = init->ring_size;	dev_priv->ring.map.type = 0;	dev_priv->ring.map.flags = 0;	dev_priv->ring.map.mtrr = 0;	drm_core_ioremap(&dev_priv->ring.map, dev);	if (dev_priv->ring.map.handle == NULL) {		dev->dev_private = (void *)dev_priv;		i915_dma_cleanup(dev);		DRM_ERROR("can not ioremap virtual address for"			  " ring buffer\n");		return -ENOMEM;	}	dev_priv->ring.virtual_start = dev_priv->ring.map.handle;	dev_priv->cpp = init->cpp;	dev_priv->back_offset = init->back_offset;	dev_priv->front_offset = init->front_offset;	dev_priv->current_page = 0;	dev_priv->sarea_priv->pf_current_page = dev_priv->current_page;	/* We are using separate values as placeholders for mechanisms for	 * private backbuffer/depthbuffer usage.	 */	dev_priv->use_mi_batchbuffer_start = 0;	if (IS_I965G(dev)) /* 965 doesn't support older method */		dev_priv->use_mi_batchbuffer_start = 1;	/* Allow hardware batchbuffers unless told otherwise.	 */	dev_priv->allow_batchbuffer = 1;	/* Program Hardware Status Page */	if (!IS_G33(dev)) {		dev_priv->status_page_dmah =			drm_pci_alloc(dev, PAGE_SIZE, PAGE_SIZE, 0xffffffff);		if (!dev_priv->status_page_dmah) {			dev->dev_private = (void *)dev_priv;			i915_dma_cleanup(dev);			DRM_ERROR("Can not allocate hardware status page\n");			return -ENOMEM;		}		dev_priv->hw_status_page = dev_priv->status_page_dmah->vaddr;		dev_priv->dma_status_page = dev_priv->status_page_dmah->busaddr;		memset(dev_priv->hw_status_page, 0, PAGE_SIZE);		I915_WRITE(0x02080, dev_priv->dma_status_page);	}	DRM_DEBUG("Enabled hardware status page\n");	dev->dev_private = (void *)dev_priv;	return 0;}static int i915_dma_resume(struct drm_device * dev){	drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;	DRM_DEBUG("%s\n", __FUNCTION__);	if (!dev_priv->sarea) {		DRM_ERROR("can not find sarea!\n");		return -EINVAL;	}	if (!dev_priv->mmio_map) {		DRM_ERROR("can not find mmio map!\n");		return -EINVAL;	}	if (dev_priv->ring.map.handle == NULL) {		DRM_ERROR("can not ioremap virtual address for"			  " ring buffer\n");		return -ENOMEM;	}	/* Program Hardware Status Page */	if (!dev_priv->hw_status_page) {		DRM_ERROR("Can not find hardware status page\n");		return -EINVAL;	}	DRM_DEBUG("hw status page @ %p\n", dev_priv->hw_status_page);	if (dev_priv->status_gfx_addr != 0)		I915_WRITE(0x02080, dev_priv->status_gfx_addr);	else		I915_WRITE(0x02080, dev_priv->dma_status_page);	DRM_DEBUG("Enabled hardware status page\n");	return 0;}static int i915_dma_init(struct drm_device *dev, void *data,			 struct drm_file *file_priv){	drm_i915_private_t *dev_priv;	drm_i915_init_t *init = data;	int retcode = 0;	switch (init->func) {	case I915_INIT_DMA:		dev_priv = drm_alloc(sizeof(drm_i915_private_t),				     DRM_MEM_DRIVER);		if (dev_priv == NULL)			return -ENOMEM;		retcode = i915_initialize(dev, dev_priv, init);		break;	case I915_CLEANUP_DMA:		retcode = i915_dma_cleanup(dev);		break;	case I915_RESUME_DMA:		retcode = i915_dma_resume(dev);		break;	default:		retcode = -EINVAL;		break;	}	return retcode;}/* Implement basically the same security restrictions as hardware does * for MI_BATCH_NON_SECURE.  These can be made stricter at any time. * * Most of the calculations below involve calculating the size of a * particular instruction.  It's important to get the size right as * that tells us where the next instruction to check is.  Any illegal * instruction detected will be given a size of zero, which is a * signal to abort the rest of the buffer. */static int do_validate_cmd(int cmd){	switch (((cmd >> 29) & 0x7)) {	case 0x0:		switch ((cmd >> 23) & 0x3f) {		case 0x0:			return 1;	/* MI_NOOP */		case 0x4:			return 1;	/* MI_FLUSH */		default:			return 0;	/* disallow everything else */		}		break;	case 0x1:		return 0;	/* reserved */	case 0x2:		return (cmd & 0xff) + 2;	/* 2d commands */	case 0x3:		if (((cmd >> 24) & 0x1f) <= 0x18)			return 1;		switch ((cmd >> 24) & 0x1f) {		case 0x1c:			return 1;		case 0x1d:			switch ((cmd >> 16) & 0xff) {			case 0x3:				return (cmd & 0x1f) + 2;			case 0x4:				return (cmd & 0xf) + 2;			default:				return (cmd & 0xffff) + 2;			}		case 0x1e:			if (cmd & (1 << 23))				return (cmd & 0xffff) + 1;			else				return 1;		case 0x1f:			if ((cmd & (1 << 23)) == 0)	/* inline vertices */				return (cmd & 0x1ffff) + 2;			else if (cmd & (1 << 17))	/* indirect random */				if ((cmd & 0xffff) == 0)					return 0;	/* unknown length, too hard */				else					return (((cmd & 0xffff) + 1) / 2) + 1;			else				return 2;	/* indirect sequential */		default:			return 0;		}	default:		return 0;	}	return 0;}static int validate_cmd(int cmd){	int ret = do_validate_cmd(cmd);/* 	printk("validate_cmd( %x ): %d\n", cmd, ret); */	return ret;}static int i915_emit_cmds(struct drm_device * dev, int __user * buffer, int dwords){	drm_i915_private_t *dev_priv = dev->dev_private;	int i;	RING_LOCALS;	if ((dwords+1) * sizeof(int) >= dev_priv->ring.Size - 8)		return -EINVAL;	BEGIN_LP_RING((dwords+1)&~1);	for (i = 0; i < dwords;) {		int cmd, sz;		if (DRM_COPY_FROM_USER_UNCHECKED(&cmd, &buffer[i], sizeof(cmd)))			return -EINVAL;		if ((sz = validate_cmd(cmd)) == 0 || i + sz > dwords)			return -EINVAL;		OUT_RING(cmd);		while (++i, --sz) {			if (DRM_COPY_FROM_USER_UNCHECKED(&cmd, &buffer[i],							 sizeof(cmd))) {				return -EINVAL;			}			OUT_RING(cmd);		}	}	if (dwords & 1)		OUT_RING(0);	ADVANCE_LP_RING();	return 0;}static int i915_emit_box(struct drm_device * dev,			 struct drm_clip_rect __user * boxes,			 int i, int DR1, int DR4){	drm_i915_private_t *dev_priv = dev->dev_private;	struct drm_clip_rect box;	RING_LOCALS;	if (DRM_COPY_FROM_USER_UNCHECKED(&box, &boxes[i], sizeof(box))) {		return -EFAULT;	}	if (box.y2 <= box.y1 || box.x2 <= box.x1 || box.y2 <= 0 || box.x2 <= 0) {		DRM_ERROR("Bad box %d,%d..%d,%d\n",			  box.x1, box.y1, box.x2, box.y2);		return -EINVAL;	}	if (IS_I965G(dev)) {		BEGIN_LP_RING(4);		OUT_RING(GFX_OP_DRAWRECT_INFO_I965);		OUT_RING((box.x1 & 0xffff) | (box.y1 << 16));		OUT_RING(((box.x2 - 1) & 0xffff) | ((box.y2 - 1) << 16));		OUT_RING(DR4);		ADVANCE_LP_RING();	} else {		BEGIN_LP_RING(6);		OUT_RING(GFX_OP_DRAWRECT_INFO);		OUT_RING(DR1);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?