/* * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. */ /* * drm_memory.h -- Memory management wrappers for DRM -*- linux-c -*- * Created: Thu Feb 4 14:00:34 1999 by faith@valinux.com */ /* * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. * Copyright (c) 2009, 2012, Intel Corporation. * 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, 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 * VA LINUX SYSTEMS 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: * Rickard E. (Rik) Faith * Gareth Hughes * */ #include "drmP.h" #ifdef __x86 #include "drm_linux_list.h" #endif /* Device memory access structure */ typedef struct drm_device_iomap { uint_t paddr; /* physical address */ uint_t size; /* size of mapping */ caddr_t kvaddr; /* kernel virtual address */ ddi_acc_handle_t acc_handle; /* data access handle */ } drm_device_iomap_t; void * drm_alloc(size_t size, int area) { _NOTE(ARGUNUSED(area)) return (kmem_zalloc(1 * size, KM_NOSLEEP)); } void * drm_calloc(size_t nmemb, size_t size, int area) { _NOTE(ARGUNUSED(area)) return (kmem_zalloc(size * nmemb, KM_NOSLEEP)); } void * drm_realloc(void *oldpt, size_t oldsize, size_t size, int area) { _NOTE(ARGUNUSED(area)) void *pt; pt = kmem_zalloc(1 * size, KM_NOSLEEP); if (pt == NULL) { DRM_ERROR("pt is NULL strange"); return (NULL); } if (oldpt && oldsize) { bcopy(oldpt, pt, min(oldsize, size)); kmem_free(oldpt, oldsize); } return (pt); } void drm_free(void *pt, size_t size, int area) { _NOTE(ARGUNUSED(area)) if (pt) kmem_free(pt, size); } int drm_get_pci_index_reg(dev_info_t *dip, uint_t paddr, uint_t size, off_t *off) { _NOTE(ARGUNUSED(size)) pci_regspec_t *regs = NULL; int len; uint_t regbase, regsize; int nregs, i; int regnum; regnum = -1; if (ddi_dev_nregs(dip, &nregs) == DDI_FAILURE) { DRM_ERROR("ddi_dev_nregs() failed"); return (-1); } if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "assigned-addresses", (caddr_t)®s, &len) != DDI_PROP_SUCCESS) { DRM_ERROR("ddi_getlongprop() failed"); if (regs) kmem_free(regs, (size_t)len); return (-1); } for (i = 0; i < nregs; i ++) { regbase = (uint_t)regs[i].pci_phys_low; regsize = (uint_t)regs[i].pci_size_low; if ((uint_t)paddr >= regbase && (uint_t)paddr < (regbase + regsize)) { regnum = i + 1; *off = (off_t)(paddr - regbase); break; } } if (regs) kmem_free(regs, (size_t)len); return (regnum); } /* data access attributes structure for register access */ static ddi_device_acc_attr_t dev_attr = { DDI_DEVICE_ATTR_V0, #ifdef _BIG_ENDIAN DDI_STRUCTURE_LE_ACC, #else DDI_NEVERSWAP_ACC, #endif DDI_STRICTORDER_ACC, DDI_FLAGERR_ACC }; static int __ioremap(dev_info_t *dip, drm_device_iomap_t *iomap) { off_t offset; int regnum; int ret; regnum = drm_get_pci_index_reg( dip, iomap->paddr, iomap->size, &offset); if (regnum < 0) { DRM_ERROR("can not find register entry: " "paddr=0x%x, size=0x%x", iomap->paddr, iomap->size); return -ENXIO; } ret = ddi_regs_map_setup(dip, regnum, (caddr_t *)&(iomap->kvaddr), (offset_t)offset, (offset_t)iomap->size, &dev_attr, &iomap->acc_handle); if (ret != DDI_SUCCESS) { DRM_ERROR("failed to map regs: " "regnum=%d, offset=0x%lx", regnum, offset); iomap->acc_handle = NULL; return -EFAULT; } return 0; } int drm_ioremap(struct drm_device *dev, struct drm_local_map *map) { struct drm_device_iomap iomap; int ret; DRM_DEBUG("\n"); iomap.paddr = map->offset; iomap.size = map->size; ret = __ioremap(dev->devinfo, &iomap); if (ret) { DRM_ERROR("__ioremap failed: paddr=0x%lx, size=0x%lx", map->offset, map->size); return (ret); } map->handle = (void *)iomap.kvaddr; map->acc_handle = iomap.acc_handle; DRM_DEBUG( "map->handle=%p, map->size=%lx", (void *)map->handle, map->size); return (0); } void drm_ioremapfree(struct drm_local_map *map) { if (map->acc_handle) ddi_regs_map_free(&map->acc_handle); } #ifdef __x86 struct drm_iomem { void *addr; size_t size; struct list_head head; }; struct list_head drm_iomem_list; void * drm_sun_ioremap(uint64_t paddr, size_t size, uint32_t mode) { struct drm_iomem *iomem; void *addr; if (mode == DRM_MEM_CACHED) mode = GFXP_MEMORY_CACHED; else if (mode == DRM_MEM_UNCACHED) mode = GFXP_MEMORY_UNCACHED; else if (mode == DRM_MEM_WC) mode = GFXP_MEMORY_WRITECOMBINED; else return (NULL); addr = (void *)gfxp_alloc_kernel_space(size); if(!addr) return (NULL); gfxp_load_kernel_space(paddr, size, mode, addr); iomem = kmem_zalloc(sizeof(*iomem), KM_NOSLEEP); if(!iomem){ gfxp_unmap_kernel_space(addr, size); return (NULL); } iomem->addr = addr; iomem->size = size; INIT_LIST_HEAD(&iomem->head); list_add(&iomem->head, &drm_iomem_list, (caddr_t)iomem); return (addr); } void drm_sun_iounmap(void *addr) { struct drm_iomem *iomem; list_for_each_entry(iomem, struct drm_iomem, &drm_iomem_list, head) { if (iomem->addr == addr) { gfxp_unmap_kernel_space(addr, iomem->size); list_del(&iomem->head); kmem_free(iomem, sizeof(*iomem)); break; } } } #endif /* x86 */