/* r300_cmdbuf.c -- Command buffer emission for R300 -*- linux-c -*- * * Copyright (C) The Weather Channel, Inc. 2002. * Copyright (C) 2004 Nicolai Haehnle. * All Rights Reserved. * * The Weather Channel (TM) funded Tungsten Graphics to develop the * initial release of the Radeon 8500 driver under the XFree86 license. * This notice must be preserved. * * 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 (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 NONINFRINGEMENT. IN NO EVENT SHALL * PRECISION INSIGHT 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: * Nicolai Haehnle */ #include "drmP.h" #include "drm.h" #include "radeon_drm.h" #include "radeon_drv.h" #include "r300_reg.h" #define R300_SIMULTANEOUS_CLIPRECTS 4 /* Values for R300_RE_CLIPRECT_CNTL depending on the number of cliprects */ static const int r300_cliprect_cntl[4] = { 0xAAAA, 0xEEEE, 0xFEFE, 0xFFFE }; /** * Emit up to R300_SIMULTANEOUS_CLIPRECTS cliprects from the given command * buffer, starting with index n. */ static int r300_emit_cliprects(drm_radeon_private_t* dev_priv, drm_radeon_cmd_buffer_t* cmdbuf, int n) { drm_clip_rect_t box; int nr; int i; RING_LOCALS; nr = cmdbuf->nbox - n; if (nr > R300_SIMULTANEOUS_CLIPRECTS) nr = R300_SIMULTANEOUS_CLIPRECTS; DRM_DEBUG("%i cliprects\n", nr); if (nr) { BEGIN_RING(6 + nr*2); OUT_RING( CP_PACKET0( R300_RE_CLIPRECT_TL_0, nr*2 - 1 ) ); for(i = 0; i < nr; ++i) { if (DRM_COPY_FROM_USER_UNCHECKED(&box, &cmdbuf->boxes[n+i], sizeof(box))) { DRM_ERROR("copy cliprect faulted\n"); return DRM_ERR(EFAULT); } box.x1 = (box.x1 + R300_CLIPRECT_OFFSET) & R300_CLIPRECT_MASK; box.y1 = (box.y1 + R300_CLIPRECT_OFFSET) & R300_CLIPRECT_MASK; box.x2 = (box.x2 + R300_CLIPRECT_OFFSET) & R300_CLIPRECT_MASK; box.y2 = (box.y2 + R300_CLIPRECT_OFFSET) & R300_CLIPRECT_MASK; OUT_RING((box.x1 << R300_CLIPRECT_X_SHIFT) | (box.y1 << R300_CLIPRECT_Y_SHIFT)); OUT_RING((box.x2 << R300_CLIPRECT_X_SHIFT) | (box.y2 << R300_CLIPRECT_Y_SHIFT)); } OUT_RING_REG( R300_RE_CLIPRECT_CNTL, r300_cliprect_cntl[nr-1] ); /* TODO/SECURITY: Force scissors to a safe value, otherwise the * client might be able to trample over memory. * The impact should be very limited, but I'd rather be safe than * sorry. */ OUT_RING( CP_PACKET0( R300_RE_SCISSORS_TL, 1 ) ); OUT_RING( 0 ); OUT_RING( R300_SCISSORS_X_MASK | R300_SCISSORS_Y_MASK ); ADVANCE_RING(); } else { /* Why we allow zero cliprect rendering: * There are some commands in a command buffer that must be submitted * even when there are no cliprects, e.g. DMA buffer discard * or state setting (though state setting could be avoided by * simulating a loss of context). * * Now since the cmdbuf interface is so chaotic right now (and is * bound to remain that way for a bit until things settle down), * it is basically impossible to filter out the commands that are * necessary and those that aren't. * * So I choose the safe way and don't do any filtering at all; * instead, I simply set up the engine so that all rendering * can't produce any fragments. */ BEGIN_RING(2); OUT_RING_REG( R300_RE_CLIPRECT_CNTL, 0 ); ADVANCE_RING(); } return 0; } u8 r300_reg_flags[0x10000>>2]; void r300_init_reg_flags(void) { int i; memset(r300_reg_flags, 0, 0x10000>>2); #define ADD_RANGE_MARK(reg, count,mark) \ for(i=((reg)>>2);i<((reg)>>2)+(count);i++)\ r300_reg_flags[i]|=(mark); #define MARK_SAFE 1 #define MARK_CHECK_OFFSET 2 #define ADD_RANGE(reg, count) ADD_RANGE_MARK(reg, count, MARK_SAFE) /* these match cmducs() command in r300_driver/r300/r300_cmdbuf.c */ ADD_RANGE(R300_SE_VPORT_XSCALE, 6); ADD_RANGE(0x2080, 1); ADD_RANGE(R300_SE_VTE_CNTL, 2); ADD_RANGE(0x2134, 2); ADD_RANGE(0x2140, 1); ADD_RANGE(R300_VAP_INPUT_CNTL_0, 2); ADD_RANGE(0x21DC, 1); ADD_RANGE(0x221C, 1); ADD_RANGE(0x2220, 4); ADD_RANGE(0x2288, 1); ADD_RANGE(R300_VAP_OUTPUT_VTX_FMT_0, 2); ADD_RANGE(R300_VAP_PVS_CNTL_1, 3); ADD_RANGE(R300_GB_ENABLE, 1); ADD_RANGE(R300_GB_MSPOS0, 5); ADD_RANGE(R300_TX_ENABLE, 1); ADD_RANGE(0x4200, 4); ADD_RANGE(0x4214, 1); ADD_RANGE(R300_RE_POINTSIZE, 1); ADD_RANGE(0x4230, 3); ADD_RANGE(R300_RE_LINE_CNT, 1); ADD_RANGE(0x4238, 1); ADD_RANGE(0x4260, 3); ADD_RANGE(0x4274, 4); ADD_RANGE(0x4288, 5); ADD_RANGE(0x42A0, 1); ADD_RANGE(R300_RE_ZBIAS_T_FACTOR, 4); ADD_RANGE(0x42B4, 1); ADD_RANGE(R300_RE_CULL_CNTL, 1); ADD_RANGE(0x42C0, 2); ADD_RANGE(R300_RS_CNTL_0, 2); ADD_RANGE(R300_RS_INTERP_0, 8); ADD_RANGE(R300_RS_ROUTE_0, 8); ADD_RANGE(0x43A4, 2); ADD_RANGE(0x43E8, 1); ADD_RANGE(R300_PFS_CNTL_0, 3); ADD_RANGE(R300_PFS_NODE_0, 4); ADD_RANGE(R300_PFS_TEXI_0, 64); ADD_RANGE(0x46A4, 5); ADD_RANGE(R300_PFS_INSTR0_0, 64); ADD_RANGE(R300_PFS_INSTR1_0, 64); ADD_RANGE(R300_PFS_INSTR2_0, 64); ADD_RANGE(R300_PFS_INSTR3_0, 64); ADD_RANGE(0x4BC0, 1); ADD_RANGE(0x4BC8, 3); ADD_RANGE(R300_PP_ALPHA_TEST, 2); ADD_RANGE(0x4BD8, 1); ADD_RANGE(R300_PFS_PARAM_0_X, 64); ADD_RANGE(0x4E00, 1); ADD_RANGE(R300_RB3D_CBLEND, 2); ADD_RANGE(R300_RB3D_COLORMASK, 1); ADD_RANGE(0x4E10, 3); ADD_RANGE_MARK(R300_RB3D_COLOROFFSET0, 1, MARK_CHECK_OFFSET); /* check offset */ ADD_RANGE(R300_RB3D_COLORPITCH0, 1); ADD_RANGE(0x4E50, 9); ADD_RANGE(0x4E88, 1); ADD_RANGE(0x4EA0, 2); ADD_RANGE(R300_RB3D_ZSTENCIL_CNTL_0, 3); ADD_RANGE(0x4F10, 4); ADD_RANGE_MARK(R300_RB3D_DEPTHOFFSET, 1, MARK_CHECK_OFFSET); /* check offset */ ADD_RANGE(R300_RB3D_DEPTHPITCH, 1); ADD_RANGE(0x4F28, 1); ADD_RANGE(0x4F30, 2); ADD_RANGE(0x4F44, 1); ADD_RANGE(0x4F54, 1); ADD_RANGE(R300_TX_FILTER_0, 16); ADD_RANGE(R300_TX_UNK1_0, 16); ADD_RANGE(R300_TX_SIZE_0, 16); ADD_RANGE(R300_TX_FORMAT_0, 16); /* Texture offset is dangerous and needs more checking */ ADD_RANGE_MARK(R300_TX_OFFSET_0, 16, MARK_CHECK_OFFSET); ADD_RANGE(R300_TX_UNK4_0, 16); ADD_RANGE(R300_TX_BORDER_COLOR_0, 16); /* Sporadic registers used as primitives are emitted */ ADD_RANGE(0x4f18, 1); ADD_RANGE(R300_RB3D_DSTCACHE_CTLSTAT, 1); ADD_RANGE(R300_VAP_INPUT_ROUTE_0_0, 8); ADD_RANGE(R300_VAP_INPUT_ROUTE_1_0, 8); } static __inline__ int r300_check_range(unsigned reg, int count) { int i; if(reg & ~0xffff)return -1; for(i=(reg>>2);i<(reg>>2)+count;i++) if(r300_reg_flags[i]!=MARK_SAFE)return 1; return 0; } /* we expect offsets passed to the framebuffer to be either within video memory or within AGP space */ static __inline__ int r300_check_offset(drm_radeon_private_t* dev_priv, u32 offset) { /* we realy want to check against end of video aperture but this value is not being kept. This code is correct for now (does the same thing as the code that sets MC_FB_LOCATION) in radeon_cp.c */ if((offset>=dev_priv->fb_location) && (offsetgart_vm_start))return 0; if((offset>=dev_priv->gart_vm_start) && (offsetgart_vm_start+dev_priv->gart_size))return 0; return 1; } static __inline__ int r300_emit_carefully_checked_packet0(drm_radeon_private_t* dev_priv, drm_radeon_cmd_buffer_t* cmdbuf, drm_r300_cmd_header_t header) { int reg; int sz; int i; int values[64]; RING_LOCALS; sz = header.packet0.count; reg = (header.packet0.reghi << 8) | header.packet0.reglo; if((sz>64)||(sz<0)){ DRM_ERROR("Cannot emit more than 64 values at a time (reg=%04x sz=%d)\n", reg, sz); return DRM_ERR(EINVAL); } for(i=0;ibuf)[i]; switch(r300_reg_flags[(reg>>2)+i]){ case MARK_SAFE: break; case MARK_CHECK_OFFSET: if(r300_check_offset(dev_priv, (u32)values[i])){ DRM_ERROR("Offset failed range check (reg=%04x sz=%d)\n", reg, sz); return DRM_ERR(EINVAL); } break; default: DRM_ERROR("Register %04x failed check as flag=%02x\n", reg+i*4, r300_reg_flags[(reg>>2)+i]); return DRM_ERR(EINVAL); } } BEGIN_RING(1+sz); OUT_RING( CP_PACKET0( reg, sz-1 ) ); OUT_RING_TABLE( values, sz ); ADVANCE_RING(); cmdbuf->buf += sz*4; cmdbuf->bufsz -= sz*4; return 0; } /** * Emits a packet0 setting arbitrary registers. * Called by r300_do_cp_cmdbuf. * * Note that checks are performed on contents and addresses of the registers */ static __inline__ int r300_emit_packet0(drm_radeon_private_t* dev_priv, drm_radeon_cmd_buffer_t* cmdbuf, drm_r300_cmd_header_t header) { int reg; int sz; RING_LOCALS; sz = header.packet0.count; reg = (header.packet0.reghi << 8) | header.packet0.reglo; if (!sz) return 0; if (sz*4 > cmdbuf->bufsz) return DRM_ERR(EINVAL); if (reg+sz*4 >= 0x10000){ DRM_ERROR("No such registers in hardware reg=%04x sz=%d\n", reg, sz); return DRM_ERR(EINVAL); } if(r300_check_range(reg, sz)){ /* go and check everything */ return r300_emit_carefully_checked_packet0(dev_priv, cmdbuf, header); } /* the rest of the data is safe to emit, whatever the values the user passed */ BEGIN_RING(1+sz); OUT_RING( CP_PACKET0( reg, sz-1 ) ); OUT_RING_TABLE( (int __user*)cmdbuf->buf, sz ); ADVANCE_RING(); cmdbuf->buf += sz*4; cmdbuf->bufsz -= sz*4; return 0; } /** * Uploads user-supplied vertex program instructions or parameters onto * the graphics card. * Called by r300_do_cp_cmdbuf. */ static __inline__ int r300_emit_vpu(drm_radeon_private_t* dev_priv, drm_radeon_cmd_buffer_t* cmdbuf, drm_r300_cmd_header_t header) { int sz; int addr; RING_LOCALS; sz = header.vpu.count; addr = (header.vpu.adrhi << 8) | header.vpu.adrlo; if (!sz) return 0; if (sz*16 > cmdbuf->bufsz) return DRM_ERR(EINVAL); BEGIN_RING(5+sz*4); /* Wait for VAP to come to senses.. */ /* there is no need to emit it multiple times, (only once before VAP is programmed, but this optimization is for later */ OUT_RING_REG( R300_VAP_PVS_WAITIDLE, 0 ); OUT_RING_REG( R300_VAP_PVS_UPLOAD_ADDRESS, addr ); OUT_RING( CP_PACKET0_TABLE( R300_VAP_PVS_UPLOAD_DATA, sz*4 - 1 ) ); OUT_RING_TABLE( (int __user*)cmdbuf->buf, sz*4 ); ADVANCE_RING(); cmdbuf->buf += sz*16; cmdbuf->bufsz -= sz*16; return 0; } /** * Emit a clear packet from userspace. * Called by r300_emit_packet3. */ static __inline__ int r300_emit_clear(drm_radeon_private_t* dev_priv, drm_radeon_cmd_buffer_t* cmdbuf) { RING_LOCALS; if (8*4 > cmdbuf->bufsz) return DRM_ERR(EINVAL); BEGIN_RING(10); OUT_RING( CP_PACKET3( R200_3D_DRAW_IMMD_2, 8 ) ); OUT_RING( R300_PRIM_TYPE_POINT|R300_PRIM_WALK_RING| (1<buf, 8 ); ADVANCE_RING(); cmdbuf->buf += 8*4; cmdbuf->bufsz -= 8*4; return 0; } static __inline__ int r300_emit_3d_load_vbpntr(drm_radeon_private_t* dev_priv, drm_radeon_cmd_buffer_t* cmdbuf, u32 header) { int count, i,k; #define MAX_ARRAY_PACKET 64 u32 payload[MAX_ARRAY_PACKET]; u32 narrays; RING_LOCALS; count=(header>>16) & 0x3fff; if((count+1)>MAX_ARRAY_PACKET){ DRM_ERROR("Too large payload in 3D_LOAD_VBPNTR (count=%d)\n", count); return DRM_ERR(EINVAL); } memset(payload, 0, MAX_ARRAY_PACKET*4); memcpy(payload, cmdbuf->buf+4, (count+1)*4); /* carefully check packet contents */ narrays=payload[0]; k=0; i=1; while((kbuf += (count+2)*4; cmdbuf->bufsz -= (count+2)*4; return 0; } static __inline__ int r300_emit_raw_packet3(drm_radeon_private_t* dev_priv, drm_radeon_cmd_buffer_t* cmdbuf) { u32 header; int count; RING_LOCALS; if (4 > cmdbuf->bufsz) return DRM_ERR(EINVAL); /* Fixme !! This simply emits a packet without much checking. We need to be smarter. */ /* obtain first word - actual packet3 header */ header = *(u32 __user*)cmdbuf->buf; /* Is it packet 3 ? */ if( (header>>30)!=0x3 ) #include "drmP.h" #include "nouveau_drv.h" #define NV_CTXDMA_PAGE_SHIFT 12 #define NV_CTXDMA_PAGE_SIZE (1 << NV_CTXDMA_PAGE_SHIFT) #define NV_CTXDMA_PAGE_MASK (NV_CTXDMA_PAGE_SIZE - 1) struct nouveau_sgdma_be { struct drm_ttm_backend backend; struct drm_device *dev; int pages; int pages_populated; dma_addr_t *pagelist; int is_bound; unsigned int pte_start; }; static int nouveau_sgdma_needs_ub_cache_adjust(struct drm_ttm_backend *be) { return ((be->flags & DRM_BE_FLAG_BOUND_CACHED) ? 0 : 1); } static int nouveau_sgdma_populate(struct drm_ttm_backend *be, unsigned long num_pages, struct page **pages) { struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be; int p, d, o; DRM_DEBUG("num_pages = %ld\n", num_pages); if (nvbe->pagelist) return -EINVAL; nvbe->pages = (num_pages << PAGE_SHIFT) >> NV_CTXDMA_PAGE_SHIFT; nvbe->pagelist = drm_alloc(nvbe->pages*sizeof(dma_addr_t), DRM_MEM_PAGES); nvbe->pages_populated = d = 0; for (p = 0; p < num_pages; p++) { for (o = 0; o < PAGE_SIZE; o += NV_CTXDMA_PAGE_SIZE) { nvbe->pagelist[d] = pci_map_page(nvbe->dev->pdev, pages[p], o, NV_CTXDMA_PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); if (pci_dma_mapping_error(nvbe->pagelist[d])) { be->func->clear(be); DRM_ERROR("pci_map_page failed\n"); return -EINVAL; } nvbe->pages_populated = ++d; } } return 0; } static void nouveau_sgdma_clear(struct drm_ttm_backend *be) { struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be; int d; DRM_DEBUG("\n"); if (nvbe && nvbe->pagelist) { if (nvbe->is_bound) be->func->unbind(be); for (d = 0; d < nvbe->pages_populated; d--) { pci_unmap_page(nvbe->dev->pdev, nvbe->pagelist[d], NV_CTXDMA_PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); } drm_free(nvbe->pagelist, nvbe->pages*sizeof(dma_addr_t), DRM_MEM_PAGES); } } static int nouveau_sgdma_bind(struct drm_ttm_backend *be, unsigned long pg_start, int cached) { struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be; struct drm_nouveau_private *dev_priv = nvbe->dev->dev_private; struct nouveau_gpuobj *gpuobj = dev_priv->gart_info.sg_ctxdma; uint64_t offset = (pg_start << PAGE_SHIFT); uint32_t i; DRM_DEBUG("pg=0x%lx (0x%llx), cached=%d\n", pg_start, offset, cached); if (offset & NV_CTXDMA_PAGE_MASK) return -EINVAL; nvbe->pte_start = (offset >> NV_CTXDMA_PAGE_SHIFT); if (dev_priv->card_type < NV_50) nvbe->pte_start += 2; /* skip ctxdma header */ for (i = nvbe->pte_start; i < nvbe->pte_start + nvbe->pages; i++) { uint64_t pteval = nvbe->pagelist[i - nvbe->pte_start]; if (pteval & NV_CTXDMA_PAGE_MASK) { DRM_ERROR("Bad pteval 0x%llx\n", pteval); return -EINVAL; } if (dev_priv->card_type < NV_50) { INSTANCE_WR(gpuobj, i, pteval | 3); } else { INSTANCE_WR(gpuobj, (i<<1)+0, pteval | 0x21); INSTANCE_WR(gpuobj, (i<<1)+1, 0x00000000); } } nvbe->is_bound = 1; return 0; } static int nouveau_sgdma_unbind(struct drm_ttm_backend *be) { struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be; struct drm_nouveau_private *dev_priv = nvbe->dev->dev_private; DRM_DEBUG("\n"); if (nvbe->is_bound) { struct nouveau_gpuobj *gpuobj = dev_priv->gart_info.sg_ctxdma; unsigned int pte; pte = nvbe->pte_start; while (pte < (nvbe->pte_start + nvbe->pages)) { uint64_t pteval = dev_priv->gart_info.sg_dummy_bus; if (dev_priv->card_type < NV_50) { INSTANCE_WR(gpuobj, pte, pteval | 3); } else { INSTANCE_WR(gpuobj, (pte<<1)+0, 0x00000010); INSTANCE_WR(gpuobj, (pte<<1)+1, 0x00000004); } pte++; } nvbe->is_bound = 0; } return 0; } static void nouveau_sgdma_destroy(struct drm_ttm_backend *be) { DRM_DEBUG("\n"); if (be) { struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be; if (nvbe) { if (nvbe->pagelist) be->func->clear(be); drm_ctl_free(nvbe, sizeof(*nvbe), DRM_MEM_TTM); } } } static struct drm_ttm_backend_func nouveau_sgdma_backend = { .needs_ub_cache_adjust = nouveau_sgdma_needs_ub_cache_adjust, .populate = nouveau_sgdma_populate, .clear = nouveau_sgdma_clear, .bind = nouveau_sgdma_bind, .unbind = nouveau_sgdma_unbind, .destroy = nouveau_sgdma_destroy }; struct drm_ttm_backend * nouveau_sgdma_init_ttm(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_sgdma_be *nvbe; if (!dev_priv->gart_info.sg_ctxdma) return NULL; nvbe = drm_ctl_calloc(1, sizeof(*nvbe), DRM_MEM_TTM); if (!nvbe) return NULL; nvbe->dev = dev; nvbe->backend.func = &nouveau_sgdma_backend; nvbe->backend.mem_type = DRM_BO_MEM_TT; return &nvbe->backend; } int nouveau_sgdma_init(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct nouveau_gpuobj *gpuobj = NULL; uint32_t aper_size, obj_size; int i, ret; if (dev_priv->card_type < NV_50) { aper_size = (64 * 1024 * 1024); obj_size = (aper_size >> NV_CTXDMA_PAGE_SHIFT) * 4; obj_size += 8; /* ctxdma header */ } else { /* 1 entire VM page table */ aper_size = (512 * 1024 * 1024); obj_size = (aper_size >> NV_CTXDMA_PAGE_SHIFT) * 8; } if ((ret = nouveau_gpuobj_new(dev, -1, obj_size, 16, NVOBJ_FLAG_ALLOW_NO_REFS | NVOBJ_FLAG_ZERO_ALLOC | NVOBJ_FLAG_ZERO_FREE, &gpuobj))) { DRM_ERROR("Error creating sgdma object: %d\n", ret); return ret; } if (dev_priv->card_type < NV_50) { dev_priv->gart_info.sg_dummy_page = alloc_page(GFP_KERNEL|__GFP_DMA32); SetPageLocked(dev_priv->gart_info.sg_dummy_page); dev_priv->gart_info.sg_dummy_bus = pci_map_page(dev->pdev, dev_priv->gart_info.sg_dummy_page, 0, PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); /* Maybe use NV_DMA_TARGET_AGP for PCIE? NVIDIA do this, and * confirmed to work on c51. Perhaps means NV_DMA_TARGET_PCIE * on those cards? */ INSTANCE_WR(gpuobj, 0, NV_CLASS_DMA_IN_MEMORY | (1 << 12) /* PT present */ | (0 << 13) /* PT *not* linear */ | (NV_DMA_ACCESS_RW << 14) | (NV_DMA_TARGET_PCI << 16)); INSTANCE_WR(gpuobj, 1, aper_size - 1); for (i=2; i<2+(aper_size>>12); i++) { INSTANCE_WR(gpuobj, i, dev_priv->gart_info.sg_dummy_bus | 3); } } else { for (i=0; i<obj_size; i+=8) { INSTANCE_WR(gpuobj, (i+0)/4, 0); //x00000010); INSTANCE_WR(gpuobj, (i+4)/4, 0); //0x00000004); } } dev_priv->gart_info.type = NOUVEAU_GART_SGDMA; dev_priv->gart_info.aper_base = 0; dev_priv->gart_info.aper_size = aper_size; dev_priv->gart_info.sg_ctxdma = gpuobj; return 0; } void nouveau_sgdma_takedown(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; if (dev_priv->gart_info.sg_dummy_page) { pci_unmap_page(dev->pdev, dev_priv->gart_info.sg_dummy_bus, NV_CTXDMA_PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); unlock_page(dev_priv->gart_info.sg_dummy_page); __free_page(dev_priv->gart_info.sg_dummy_page); dev_priv->gart_info.sg_dummy_page = NULL; dev_priv->gart_info.sg_dummy_bus = 0; } nouveau_gpuobj_del(dev, &dev_priv->gart_info.sg_ctxdma); } int nouveau_sgdma_nottm_hack_init(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct drm_ttm_backend *be; struct drm_scatter_gather sgreq; int ret; dev_priv->gart_info.sg_be = nouveau_sgdma_init_ttm(dev); if (!dev_priv->gart_info.sg_be) return -ENOMEM; be = dev_priv->gart_info.sg_be; /* Hack the aperture size down to the amount of system memory * we're going to bind into it. */ if (dev_priv->gart_info.aper_size > 32*1024*1024) dev_priv->gart_info.aper_size = 32*1024*1024; sgreq.size = dev_priv->gart_info.aper_size; if ((ret = drm_sg_alloc(dev, &sgreq))) { DRM_ERROR("drm_sg_alloc failed: %d\n", ret); return ret; } dev_priv->gart_info.sg_handle = sgreq.handle; if ((ret = be->func->populate(be, dev->sg->pages, dev->sg->pagelist))) { DRM_ERROR("failed populate: %d\n", ret); return ret; } if ((ret = be->func->bind(be, 0, 0))) { DRM_ERROR("failed bind: %d\n", ret); return ret; } return 0; } void nouveau_sgdma_nottm_hack_takedown(struct drm_device *dev) { }