1/* $NetBSD: via_mm.c,v 1.5 2021/12/18 23:45:44 riastradh Exp $ */ 2 3/* 4 * Copyright 2006 Tungsten Graphics Inc., Bismarck, ND., USA. 5 * All rights reserved. 6 * 7 * Permission is hereby granted, free of charge, to any person obtaining a 8 * copy of this software and associated documentation files (the "Software"), 9 * to deal in the Software without restriction, including without limitation 10 * the rights to use, copy, modify, merge, publish, distribute, sub license, 11 * and/or sell copies of the Software, and to permit persons to whom the 12 * Software is furnished to do so, subject to the following conditions: 13 * 14 * The above copyright notice and this permission notice (including the 15 * next paragraph) shall be included in all copies or substantial portions 16 * of the Software. 17 * 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 21 * THE AUTHORS OR COPYRIGHT HOLDERS AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, 22 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 23 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 24 * DEALINGS IN THE SOFTWARE. 25 */ 26/* 27 * Authors: Thomas Hellstr��m <thomas-at-tungstengraphics-dot-com> 28 */ 29 30#include <sys/cdefs.h> 31__KERNEL_RCSID(0, "$NetBSD: via_mm.c,v 1.5 2021/12/18 23:45:44 riastradh Exp $"); 32 33#include <linux/slab.h> 34 35#include <drm/drm_device.h> 36#include <drm/drm_file.h> 37#include <drm/drm_irq.h> 38#include <drm/via_drm.h> 39 40#include "via_drv.h" 41 42#define VIA_MM_ALIGN_SHIFT 4 43#define VIA_MM_ALIGN_MASK ((1 << VIA_MM_ALIGN_SHIFT) - 1) 44 45struct via_memblock { 46 struct drm_mm_node mm_node; 47 struct list_head owner_list; 48}; 49 50int via_agp_init(struct drm_device *dev, void *data, struct drm_file *file_priv) 51{ 52 drm_via_agp_t *agp = data; 53 drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; 54 55 mutex_lock(&dev->struct_mutex); 56 drm_mm_init(&dev_priv->agp_mm, 0, agp->size >> VIA_MM_ALIGN_SHIFT); 57 58 dev_priv->agp_initialized = 1; 59 dev_priv->agp_offset = agp->offset; 60 mutex_unlock(&dev->struct_mutex); 61 62 DRM_DEBUG("offset = %u, size = %u\n", agp->offset, agp->size); 63 return 0; 64} 65 66int via_fb_init(struct drm_device *dev, void *data, struct drm_file *file_priv) 67{ 68 drm_via_fb_t *fb = data; 69 drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; 70 71 mutex_lock(&dev->struct_mutex); 72 drm_mm_init(&dev_priv->vram_mm, 0, fb->size >> VIA_MM_ALIGN_SHIFT); 73 74 dev_priv->vram_initialized = 1; 75 dev_priv->vram_offset = fb->offset; 76 77 mutex_unlock(&dev->struct_mutex); 78 DRM_DEBUG("offset = %u, size = %u\n", fb->offset, fb->size); 79 80 return 0; 81 82} 83 84int via_final_context(struct drm_device *dev, int context) 85{ 86 drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; 87 88 via_release_futex(dev_priv, context); 89 90 /* Linux specific until context tracking code gets ported to BSD */ 91 /* Last context, perform cleanup */ 92 if (list_is_singular(&dev->ctxlist)) { 93 DRM_DEBUG("Last Context\n"); 94 drm_irq_uninstall(dev); 95 via_cleanup_futex(dev_priv); 96 via_do_cleanup_map(dev); 97 } 98 return 1; 99} 100 101void via_lastclose(struct drm_device *dev) 102{ 103 drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; 104 105 if (!dev_priv) 106 return; 107 108 mutex_lock(&dev->struct_mutex); 109 if (dev_priv->vram_initialized) { 110 drm_mm_takedown(&dev_priv->vram_mm); 111 dev_priv->vram_initialized = 0; 112 } 113 if (dev_priv->agp_initialized) { 114 drm_mm_takedown(&dev_priv->agp_mm); 115 dev_priv->agp_initialized = 0; 116 } 117 mutex_unlock(&dev->struct_mutex); 118} 119 120int via_mem_alloc(struct drm_device *dev, void *data, 121 struct drm_file *file) 122{ 123 drm_via_mem_t *mem = data; 124 int retval = 0, user_key; 125 struct via_memblock *item; 126 drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; 127 struct via_file_private *file_priv = file->driver_priv; 128 unsigned long tmpSize; 129 130 if (mem->type > VIA_MEM_AGP) { 131 DRM_ERROR("Unknown memory type allocation\n"); 132 return -EINVAL; 133 } 134 idr_preload(GFP_KERNEL); 135 mutex_lock(&dev->struct_mutex); 136 if (0 == ((mem->type == VIA_MEM_VIDEO) ? dev_priv->vram_initialized : 137 dev_priv->agp_initialized)) { 138 DRM_ERROR 139 ("Attempt to allocate from uninitialized memory manager.\n"); 140 mutex_unlock(&dev->struct_mutex); 141 idr_preload_end(); 142 return -EINVAL; 143 } 144 145 item = kzalloc(sizeof(*item), GFP_KERNEL); 146 if (!item) { 147 retval = -ENOMEM; 148 goto fail_alloc; 149 } 150 151 tmpSize = (mem->size + VIA_MM_ALIGN_MASK) >> VIA_MM_ALIGN_SHIFT; 152 if (mem->type == VIA_MEM_AGP) 153 retval = drm_mm_insert_node(&dev_priv->agp_mm, 154 &item->mm_node, 155 tmpSize); 156 else 157 retval = drm_mm_insert_node(&dev_priv->vram_mm, 158 &item->mm_node, 159 tmpSize); 160 if (retval) 161 goto fail_alloc; 162 163 retval = idr_alloc(&dev_priv->object_idr, item, 1, 0, GFP_KERNEL); 164 if (retval < 0) 165 goto fail_idr; 166 user_key = retval; 167 168 list_add(&item->owner_list, &file_priv->obj_list); 169 mutex_unlock(&dev->struct_mutex); 170 idr_preload_end(); 171 172 mem->offset = ((mem->type == VIA_MEM_VIDEO) ? 173 dev_priv->vram_offset : dev_priv->agp_offset) + 174 ((item->mm_node.start) << VIA_MM_ALIGN_SHIFT); 175 mem->index = user_key; 176 177 return 0; 178 179fail_idr: 180 drm_mm_remove_node(&item->mm_node); 181fail_alloc: 182 kfree(item); 183 mutex_unlock(&dev->struct_mutex); 184 idr_preload_end(); 185 186 mem->offset = 0; 187 mem->size = 0; 188 mem->index = 0; 189 DRM_DEBUG("Video memory allocation failed\n"); 190 191 return retval; 192} 193 194int via_mem_free(struct drm_device *dev, void *data, struct drm_file *file_priv) 195{ 196 drm_via_private_t *dev_priv = dev->dev_private; 197 drm_via_mem_t *mem = data; 198 struct via_memblock *obj; 199 200 mutex_lock(&dev->struct_mutex); 201 obj = idr_find(&dev_priv->object_idr, mem->index); 202 if (obj == NULL) { 203 mutex_unlock(&dev->struct_mutex); 204 return -EINVAL; 205 } 206 207 idr_remove(&dev_priv->object_idr, mem->index); 208 list_del(&obj->owner_list); 209 drm_mm_remove_node(&obj->mm_node); 210 kfree(obj); 211 mutex_unlock(&dev->struct_mutex); 212 213 DRM_DEBUG("free = 0x%lx\n", mem->index); 214 215 return 0; 216} 217 218 219void via_reclaim_buffers_locked(struct drm_device *dev, 220 struct drm_file *file) 221{ 222 struct via_file_private *file_priv = file->driver_priv; 223 struct via_memblock *entry, *next; 224 225 if (!(dev->master && file->master->lock.hw_lock)) 226 return; 227 228 drm_legacy_idlelock_take(&file->master->lock); 229 230 mutex_lock(&dev->struct_mutex); 231 if (list_empty(&file_priv->obj_list)) { 232 mutex_unlock(&dev->struct_mutex); 233 drm_legacy_idlelock_release(&file->master->lock); 234 235 return; 236 } 237 238 via_driver_dma_quiescent(dev); 239 240 list_for_each_entry_safe(entry, next, &file_priv->obj_list, 241 owner_list) { 242 list_del(&entry->owner_list); 243 drm_mm_remove_node(&entry->mm_node); 244 kfree(entry); 245 } 246 mutex_unlock(&dev->struct_mutex); 247 248 drm_legacy_idlelock_release(&file->master->lock); 249 250 return; 251} 252