1 /* $NetBSD: sis_mm.c,v 1.4 2021/12/18 23:45:44 riastradh Exp $ */ 2 3 /************************************************************************** 4 * 5 * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA. 6 * All Rights Reserved. 7 * 8 * Permission is hereby granted, free of charge, to any person obtaining a 9 * copy of this software and associated documentation files (the 10 * "Software"), to deal in the Software without restriction, including 11 * without limitation the rights to use, copy, modify, merge, publish, 12 * distribute, sub license, and/or sell copies of the Software, and to 13 * permit persons to whom the Software is furnished to do so, subject to 14 * the following conditions: 15 * 16 * The above copyright notice and this permission notice (including the 17 * next paragraph) shall be included in all copies or substantial portions 18 * of the Software. 19 * 20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL 23 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, 24 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 25 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 26 * USE OR OTHER DEALINGS IN THE SOFTWARE. 27 * 28 * 29 **************************************************************************/ 30 31 /* 32 * Authors: 33 * Thomas Hellstrm <thomas-at-tungstengraphics-dot-com> 34 */ 35 36 #include <sys/cdefs.h> 37 __KERNEL_RCSID(0, "$NetBSD: sis_mm.c,v 1.4 2021/12/18 23:45:44 riastradh Exp $"); 38 39 #include <video/sisfb.h> 40 41 #include <drm/drm_device.h> 42 #include <drm/drm_file.h> 43 #include <drm/sis_drm.h> 44 45 #include "sis_drv.h" 46 47 48 #define VIDEO_TYPE 0 49 #define AGP_TYPE 1 50 51 52 struct sis_memblock { 53 struct drm_mm_node mm_node; 54 struct sis_memreq req; 55 struct list_head owner_list; 56 }; 57 58 #if defined(CONFIG_FB_SIS) || defined(CONFIG_FB_SIS_MODULE) 59 /* fb management via fb device */ 60 61 #define SIS_MM_ALIGN_SHIFT 0 62 #define SIS_MM_ALIGN_MASK 0 63 64 #else /* CONFIG_FB_SIS[_MODULE] */ 65 66 #define SIS_MM_ALIGN_SHIFT 4 67 #define SIS_MM_ALIGN_MASK ((1 << SIS_MM_ALIGN_SHIFT) - 1) 68 69 #endif /* CONFIG_FB_SIS[_MODULE] */ 70 71 static int sis_fb_init(struct drm_device *dev, void *data, struct drm_file *file_priv) 72 { 73 drm_sis_private_t *dev_priv = dev->dev_private; 74 drm_sis_fb_t *fb = data; 75 76 mutex_lock(&dev->struct_mutex); 77 /* Unconditionally init the drm_mm, even though we don't use it when the 78 * fb sis driver is available - make cleanup easier. */ 79 drm_mm_init(&dev_priv->vram_mm, 0, fb->size >> SIS_MM_ALIGN_SHIFT); 80 81 dev_priv->vram_initialized = 1; 82 dev_priv->vram_offset = fb->offset; 83 84 mutex_unlock(&dev->struct_mutex); 85 DRM_DEBUG("offset = %lu, size = %lu\n", fb->offset, fb->size); 86 87 return 0; 88 } 89 90 static int sis_drm_alloc(struct drm_device *dev, struct drm_file *file, 91 void *data, int pool) 92 { 93 drm_sis_private_t *dev_priv = dev->dev_private; 94 drm_sis_mem_t *mem = data; 95 int retval = 0, user_key; 96 struct sis_memblock *item; 97 struct sis_file_private *file_priv = file->driver_priv; 98 unsigned long offset; 99 100 idr_preload(GFP_KERNEL); 101 mutex_lock(&dev->struct_mutex); 102 103 if (0 == ((pool == 0) ? dev_priv->vram_initialized : 104 dev_priv->agp_initialized)) { 105 DRM_ERROR 106 ("Attempt to allocate from uninitialized memory manager.\n"); 107 mutex_unlock(&dev->struct_mutex); 108 idr_preload_end(); 109 return -EINVAL; 110 } 111 112 item = kzalloc(sizeof(*item), GFP_KERNEL); 113 if (!item) { 114 retval = -ENOMEM; 115 goto fail_alloc; 116 } 117 118 mem->size = (mem->size + SIS_MM_ALIGN_MASK) >> SIS_MM_ALIGN_SHIFT; 119 if (pool == AGP_TYPE) { 120 retval = drm_mm_insert_node(&dev_priv->agp_mm, 121 &item->mm_node, 122 mem->size); 123 offset = item->mm_node.start; 124 } else { 125 #if defined(CONFIG_FB_SIS) || defined(CONFIG_FB_SIS_MODULE) 126 item->req.size = mem->size; 127 sis_malloc(&item->req); 128 if (item->req.size == 0) 129 retval = -ENOMEM; 130 offset = item->req.offset; 131 #else 132 retval = drm_mm_insert_node(&dev_priv->vram_mm, 133 &item->mm_node, 134 mem->size); 135 offset = item->mm_node.start; 136 #endif 137 } 138 if (retval) 139 goto fail_alloc; 140 141 retval = idr_alloc(&dev_priv->object_idr, item, 1, 0, GFP_KERNEL); 142 if (retval < 0) 143 goto fail_idr; 144 user_key = retval; 145 146 list_add(&item->owner_list, &file_priv->obj_list); 147 mutex_unlock(&dev->struct_mutex); 148 idr_preload_end(); 149 150 mem->offset = ((pool == 0) ? 151 dev_priv->vram_offset : dev_priv->agp_offset) + 152 (offset << SIS_MM_ALIGN_SHIFT); 153 mem->free = user_key; 154 mem->size = mem->size << SIS_MM_ALIGN_SHIFT; 155 156 return 0; 157 158 fail_idr: 159 drm_mm_remove_node(&item->mm_node); 160 fail_alloc: 161 kfree(item); 162 mutex_unlock(&dev->struct_mutex); 163 idr_preload_end(); 164 165 mem->offset = 0; 166 mem->size = 0; 167 mem->free = 0; 168 169 DRM_DEBUG("alloc %d, size = %ld, offset = %ld\n", pool, mem->size, 170 mem->offset); 171 172 return retval; 173 } 174 175 static int sis_drm_free(struct drm_device *dev, void *data, struct drm_file *file_priv) 176 { 177 drm_sis_private_t *dev_priv = dev->dev_private; 178 drm_sis_mem_t *mem = data; 179 struct sis_memblock *obj; 180 181 mutex_lock(&dev->struct_mutex); 182 obj = idr_find(&dev_priv->object_idr, mem->free); 183 if (obj == NULL) { 184 mutex_unlock(&dev->struct_mutex); 185 return -EINVAL; 186 } 187 188 idr_remove(&dev_priv->object_idr, mem->free); 189 list_del(&obj->owner_list); 190 if (drm_mm_node_allocated(&obj->mm_node)) 191 drm_mm_remove_node(&obj->mm_node); 192 #if defined(CONFIG_FB_SIS) || defined(CONFIG_FB_SIS_MODULE) 193 else 194 sis_free(obj->req.offset); 195 #endif 196 kfree(obj); 197 mutex_unlock(&dev->struct_mutex); 198 DRM_DEBUG("free = 0x%lx\n", mem->free); 199 200 return 0; 201 } 202 203 static int sis_fb_alloc(struct drm_device *dev, void *data, 204 struct drm_file *file_priv) 205 { 206 return sis_drm_alloc(dev, file_priv, data, VIDEO_TYPE); 207 } 208 209 static int sis_ioctl_agp_init(struct drm_device *dev, void *data, 210 struct drm_file *file_priv) 211 { 212 drm_sis_private_t *dev_priv = dev->dev_private; 213 drm_sis_agp_t *agp = data; 214 dev_priv = dev->dev_private; 215 216 mutex_lock(&dev->struct_mutex); 217 drm_mm_init(&dev_priv->agp_mm, 0, agp->size >> SIS_MM_ALIGN_SHIFT); 218 219 dev_priv->agp_initialized = 1; 220 dev_priv->agp_offset = agp->offset; 221 mutex_unlock(&dev->struct_mutex); 222 223 DRM_DEBUG("offset = %lu, size = %lu\n", agp->offset, agp->size); 224 return 0; 225 } 226 227 static int sis_ioctl_agp_alloc(struct drm_device *dev, void *data, 228 struct drm_file *file_priv) 229 { 230 231 return sis_drm_alloc(dev, file_priv, data, AGP_TYPE); 232 } 233 234 static drm_local_map_t *sis_reg_init(struct drm_device *dev) 235 { 236 struct drm_map_list *entry; 237 drm_local_map_t *map; 238 239 list_for_each_entry(entry, &dev->maplist, head) { 240 map = entry->map; 241 if (!map) 242 continue; 243 if (map->type == _DRM_REGISTERS) 244 return map; 245 } 246 return NULL; 247 } 248 249 int sis_idle(struct drm_device *dev) 250 { 251 drm_sis_private_t *dev_priv = dev->dev_private; 252 uint32_t idle_reg; 253 unsigned long end; 254 int i; 255 256 if (dev_priv->idle_fault) 257 return 0; 258 259 if (dev_priv->mmio == NULL) { 260 dev_priv->mmio = sis_reg_init(dev); 261 if (dev_priv->mmio == NULL) { 262 DRM_ERROR("Could not find register map.\n"); 263 return 0; 264 } 265 } 266 267 /* 268 * Implement a device switch here if needed 269 */ 270 271 if (dev_priv->chipset != SIS_CHIP_315) 272 return 0; 273 274 /* 275 * Timeout after 3 seconds. We cannot use DRM_WAIT_ON here 276 * because its polling frequency is too low. 277 */ 278 279 end = jiffies + (HZ * 3); 280 281 for (i = 0; i < 4; ++i) { 282 do { 283 idle_reg = SIS_READ(0x85cc); 284 } while (!time_after_eq(jiffies, end) && 285 ((idle_reg & 0x80000000) != 0x80000000)); 286 } 287 288 if (time_after_eq(jiffies, end)) { 289 DRM_ERROR("Graphics engine idle timeout. " 290 "Disabling idle check\n"); 291 dev_priv->idle_fault = 1; 292 } 293 294 /* 295 * The caller never sees an error code. It gets trapped 296 * in libdrm. 297 */ 298 299 return 0; 300 } 301 302 303 void sis_lastclose(struct drm_device *dev) 304 { 305 drm_sis_private_t *dev_priv = dev->dev_private; 306 307 if (!dev_priv) 308 return; 309 310 mutex_lock(&dev->struct_mutex); 311 if (dev_priv->vram_initialized) { 312 drm_mm_takedown(&dev_priv->vram_mm); 313 dev_priv->vram_initialized = 0; 314 } 315 if (dev_priv->agp_initialized) { 316 drm_mm_takedown(&dev_priv->agp_mm); 317 dev_priv->agp_initialized = 0; 318 } 319 dev_priv->mmio = NULL; 320 mutex_unlock(&dev->struct_mutex); 321 } 322 323 void sis_reclaim_buffers_locked(struct drm_device *dev, 324 struct drm_file *file) 325 { 326 struct sis_file_private *file_priv = file->driver_priv; 327 struct sis_memblock *entry, *next; 328 329 if (!(dev->master && file->master->lock.hw_lock)) 330 return; 331 332 drm_legacy_idlelock_take(&file->master->lock); 333 334 mutex_lock(&dev->struct_mutex); 335 if (list_empty(&file_priv->obj_list)) { 336 mutex_unlock(&dev->struct_mutex); 337 drm_legacy_idlelock_release(&file->master->lock); 338 339 return; 340 } 341 342 sis_idle(dev); 343 344 345 list_for_each_entry_safe(entry, next, &file_priv->obj_list, 346 owner_list) { 347 list_del(&entry->owner_list); 348 if (drm_mm_node_allocated(&entry->mm_node)) 349 drm_mm_remove_node(&entry->mm_node); 350 #if defined(CONFIG_FB_SIS) || defined(CONFIG_FB_SIS_MODULE) 351 else 352 sis_free(entry->req.offset); 353 #endif 354 kfree(entry); 355 } 356 mutex_unlock(&dev->struct_mutex); 357 358 drm_legacy_idlelock_release(&file->master->lock); 359 360 return; 361 } 362 363 const struct drm_ioctl_desc sis_ioctls[] = { 364 DRM_IOCTL_DEF_DRV(SIS_FB_ALLOC, sis_fb_alloc, DRM_AUTH), 365 DRM_IOCTL_DEF_DRV(SIS_FB_FREE, sis_drm_free, DRM_AUTH), 366 DRM_IOCTL_DEF_DRV(SIS_AGP_INIT, sis_ioctl_agp_init, DRM_AUTH | DRM_MASTER | DRM_ROOT_ONLY), 367 DRM_IOCTL_DEF_DRV(SIS_AGP_ALLOC, sis_ioctl_agp_alloc, DRM_AUTH), 368 DRM_IOCTL_DEF_DRV(SIS_AGP_FREE, sis_drm_free, DRM_AUTH), 369 DRM_IOCTL_DEF_DRV(SIS_FB_INIT, sis_fb_init, DRM_AUTH | DRM_MASTER | DRM_ROOT_ONLY), 370 }; 371 372 int sis_max_ioctl = ARRAY_SIZE(sis_ioctls); 373