📄 via_dma.c
字号:
/* via_dma.c -- DMA support for the VIA Unichrome/Pro * * Copyright 2003 Tungsten Graphics, Inc., Cedar Park, Texas. * All Rights Reserved. * * Copyright 2004 Digeo, Inc., Palo Alto, CA, U.S.A. * All Rights Reserved. * * Copyright 2004 The Unichrome project. * 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 * 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. * * Authors: * Tungsten Graphics, * Erdi Chen, * Thomas Hellstrom. */#include "drmP.h"#include "drm.h"#include "via_drm.h"#include "via_drv.h"#include "via_3d_reg.h"#define CMDBUF_ALIGNMENT_SIZE (0x100)#define CMDBUF_ALIGNMENT_MASK (0x0ff)/* defines for VIA 3D registers */#define VIA_REG_STATUS 0x400#define VIA_REG_TRANSET 0x43C#define VIA_REG_TRANSPACE 0x440/* VIA_REG_STATUS(0x400): Engine Status */#define VIA_CMD_RGTR_BUSY 0x00000080 /* Command Regulator is busy */#define VIA_2D_ENG_BUSY 0x00000001 /* 2D Engine is busy */#define VIA_3D_ENG_BUSY 0x00000002 /* 3D Engine is busy */#define VIA_VR_QUEUE_BUSY 0x00020000 /* Virtual Queue is busy */#define SetReg2DAGP(nReg, nData) { \ *((uint32_t *)(vb)) = ((nReg) >> 2) | HALCYON_HEADER1; \ *((uint32_t *)(vb) + 1) = (nData); \ vb = ((uint32_t *)vb) + 2; \ dev_priv->dma_low +=8; \}#define via_flush_write_combine() DRM_MEMORYBARRIER()#define VIA_OUT_RING_QW(w1,w2) \ *vb++ = (w1); \ *vb++ = (w2); \ dev_priv->dma_low += 8;static void via_cmdbuf_start(drm_via_private_t * dev_priv);static void via_cmdbuf_pause(drm_via_private_t * dev_priv);static void via_cmdbuf_reset(drm_via_private_t * dev_priv);static void via_cmdbuf_rewind(drm_via_private_t * dev_priv);static int via_wait_idle(drm_via_private_t * dev_priv);static void via_pad_cache(drm_via_private_t * dev_priv, int qwords);/* * Free space in command buffer. */static uint32_t via_cmdbuf_space(drm_via_private_t * dev_priv){ uint32_t agp_base = dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr; uint32_t hw_addr = *(dev_priv->hw_addr_ptr) - agp_base; return ((hw_addr <= dev_priv->dma_low) ? (dev_priv->dma_high + hw_addr - dev_priv->dma_low) : (hw_addr - dev_priv->dma_low));}/* * How much does the command regulator lag behind? */static uint32_t via_cmdbuf_lag(drm_via_private_t * dev_priv){ uint32_t agp_base = dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr; uint32_t hw_addr = *(dev_priv->hw_addr_ptr) - agp_base; return ((hw_addr <= dev_priv->dma_low) ? (dev_priv->dma_low - hw_addr) : (dev_priv->dma_wrap + dev_priv->dma_low - hw_addr));}/* * Check that the given size fits in the buffer, otherwise wait. */static inline intvia_cmdbuf_wait(drm_via_private_t * dev_priv, unsigned int size){ uint32_t agp_base = dev_priv->dma_offset + (uint32_t) dev_priv->agpAddr; uint32_t cur_addr, hw_addr, next_addr; volatile uint32_t *hw_addr_ptr; uint32_t count; hw_addr_ptr = dev_priv->hw_addr_ptr; cur_addr = dev_priv->dma_low; next_addr = cur_addr + size + 512 * 1024; count = 1000000; do { hw_addr = *hw_addr_ptr - agp_base; if (count-- == 0) { DRM_ERROR ("via_cmdbuf_wait timed out hw %x cur_addr %x next_addr %x\n", hw_addr, cur_addr, next_addr); return -1; } } while ((cur_addr < hw_addr) && (next_addr >= hw_addr)); return 0;}/* * Checks whether buffer head has reach the end. Rewind the ring buffer * when necessary. * * Returns virtual pointer to ring buffer. */static inline uint32_t *via_check_dma(drm_via_private_t * dev_priv, unsigned int size){ if ((dev_priv->dma_low + size + 4 * CMDBUF_ALIGNMENT_SIZE) > dev_priv->dma_high) { via_cmdbuf_rewind(dev_priv); } if (via_cmdbuf_wait(dev_priv, size) != 0) { return NULL; } return (uint32_t *) (dev_priv->dma_ptr + dev_priv->dma_low);}int via_dma_cleanup(drm_device_t * dev){ if (dev->dev_private) { drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; if (dev_priv->ring.virtual_start) { via_cmdbuf_reset(dev_priv); drm_core_ioremapfree(&dev_priv->ring.map, dev); dev_priv->ring.virtual_start = NULL; } } return 0;}static int via_initialize(drm_device_t * dev, drm_via_private_t * dev_priv, drm_via_dma_init_t * init){ if (!dev_priv || !dev_priv->mmio) { DRM_ERROR("via_dma_init called before via_map_init\n"); return DRM_ERR(EFAULT); } if (dev_priv->ring.virtual_start != NULL) { DRM_ERROR("%s called again without calling cleanup\n", __FUNCTION__); return DRM_ERR(EFAULT); } if (!dev->agp || !dev->agp->base) { DRM_ERROR("%s called with no agp memory available\n", __FUNCTION__); return DRM_ERR(EFAULT); } dev_priv->ring.map.offset = dev->agp->base + init->offset; dev_priv->ring.map.size = init->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) { via_dma_cleanup(dev); DRM_ERROR("can not ioremap virtual address for" " ring buffer\n"); return DRM_ERR(ENOMEM); } dev_priv->ring.virtual_start = dev_priv->ring.map.handle; dev_priv->dma_ptr = dev_priv->ring.virtual_start; dev_priv->dma_low = 0; dev_priv->dma_high = init->size; dev_priv->dma_wrap = init->size; dev_priv->dma_offset = init->offset; dev_priv->last_pause_ptr = NULL; dev_priv->hw_addr_ptr = dev_priv->mmio->handle + init->reg_pause_addr; via_cmdbuf_start(dev_priv); return 0;}int via_dma_init(DRM_IOCTL_ARGS){ DRM_DEVICE; drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; drm_via_dma_init_t init; int retcode = 0; DRM_COPY_FROM_USER_IOCTL(init, (drm_via_dma_init_t __user *) data, sizeof(init)); switch (init.func) { case VIA_INIT_DMA: if (!capable(CAP_SYS_ADMIN)) retcode = DRM_ERR(EPERM); else retcode = via_initialize(dev, dev_priv, &init); break; case VIA_CLEANUP_DMA: if (!capable(CAP_SYS_ADMIN)) retcode = DRM_ERR(EPERM); else retcode = via_dma_cleanup(dev); break; case VIA_DMA_INITIALIZED: retcode = (dev_priv->ring.virtual_start != NULL) ? 0 : DRM_ERR(EFAULT); break; default: retcode = DRM_ERR(EINVAL); break; } return retcode;}static int via_dispatch_cmdbuffer(drm_device_t * dev, drm_via_cmdbuffer_t * cmd){ drm_via_private_t *dev_priv; uint32_t *vb; int ret; dev_priv = (drm_via_private_t *) dev->dev_private; if (dev_priv->ring.virtual_start == NULL) { DRM_ERROR("%s called without initializing AGP ring buffer.\n", __FUNCTION__); return DRM_ERR(EFAULT); } if (cmd->size > VIA_PCI_BUF_SIZE) { return DRM_ERR(ENOMEM); } if (DRM_COPY_FROM_USER(dev_priv->pci_buf, cmd->buf, cmd->size)) return DRM_ERR(EFAULT); /* * Running this function on AGP memory is dead slow. Therefore * we run it on a temporary cacheable system memory buffer and * copy it to AGP memory when ready. */ if ((ret = via_verify_command_stream((uint32_t *) dev_priv->pci_buf, cmd->size, dev, 1))) { return ret; } vb = via_check_dma(dev_priv, (cmd->size < 0x100) ? 0x102 : cmd->size); if (vb == NULL) { return DRM_ERR(EAGAIN); } memcpy(vb, dev_priv->pci_buf, cmd->size); dev_priv->dma_low += cmd->size; /* * Small submissions somehow stalls the CPU. (AGP cache effects?) * pad to greater size. */ if (cmd->size < 0x100) via_pad_cache(dev_priv, (0x100 - cmd->size) >> 3); via_cmdbuf_pause(dev_priv); return 0;}int via_driver_dma_quiescent(drm_device_t * dev){ drm_via_private_t *dev_priv = dev->dev_private; if (!via_wait_idle(dev_priv)) { return DRM_ERR(EBUSY); } return 0;}int via_flush_ioctl(DRM_IOCTL_ARGS){ DRM_DEVICE; LOCK_TEST_WITH_RETURN(dev, filp); return via_driver_dma_quiescent(dev);}int via_cmdbuffer(DRM_IOCTL_ARGS){ DRM_DEVICE; drm_via_cmdbuffer_t cmdbuf; int ret; LOCK_TEST_WITH_RETURN(dev, filp); DRM_COPY_FROM_USER_IOCTL(cmdbuf, (drm_via_cmdbuffer_t __user *) data, sizeof(cmdbuf)); DRM_DEBUG("via cmdbuffer, buf %p size %lu\n", cmdbuf.buf, cmdbuf.size); ret = via_dispatch_cmdbuffer(dev, &cmdbuf); if (ret) { return ret; } return 0;}extern intvia_parse_command_stream(drm_device_t * dev, const uint32_t * buf, unsigned int size);static int via_dispatch_pci_cmdbuffer(drm_device_t * dev, drm_via_cmdbuffer_t * cmd){ drm_via_private_t *dev_priv = dev->dev_private; int ret; if (cmd->size > VIA_PCI_BUF_SIZE) { return DRM_ERR(ENOMEM); } if (DRM_COPY_FROM_USER(dev_priv->pci_buf, cmd->buf, cmd->size))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -