1/************************************************************************** 2 * 3 * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA. 4 * All Rights Reserved. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the 8 * "Software"), to deal in the Software without restriction, including 9 * without limitation the rights to use, copy, modify, merge, publish, 10 * distribute, sub license, and/or sell copies of the Software, and to 11 * permit persons to whom the Software is furnished to do so, subject to 12 * 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 COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, 22 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 23 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 24 * USE OR OTHER DEALINGS IN THE SOFTWARE. 25 * 26 * 27 **************************************************************************/ 28 29/* 30 * Authors: 31 * Thomas Hellstr�m <thomas-at-tungstengraphics-dot-com> 32 */ 33 34#include "drmP.h" 35#include "sis_drm.h" 36#include "sis_drv.h" 37 38#include <video/sisfb.h> 39 40#define VIDEO_TYPE 0 41#define AGP_TYPE 1 42 43 44#if defined(CONFIG_FB_SIS) 45/* fb management via fb device */ 46 47#define SIS_MM_ALIGN_SHIFT 0 48#define SIS_MM_ALIGN_MASK 0 49 50static void *sis_sman_mm_allocate(void *private, unsigned long size, 51 unsigned alignment) 52{ 53 struct sis_memreq req; 54 55 req.size = size; 56 sis_malloc(&req); 57 if (req.size == 0) 58 return NULL; 59 else 60 return (void *)~req.offset; 61} 62 63static void sis_sman_mm_free(void *private, void *ref) 64{ 65 sis_free(~((unsigned long)ref)); 66} 67 68static void sis_sman_mm_destroy(void *private) 69{ 70 ; 71} 72 73static unsigned long sis_sman_mm_offset(void *private, void *ref) 74{ 75 return ~((unsigned long)ref); 76} 77 78#else /* CONFIG_FB_SIS */ 79 80#define SIS_MM_ALIGN_SHIFT 4 81#define SIS_MM_ALIGN_MASK ( (1 << SIS_MM_ALIGN_SHIFT) - 1) 82 83#endif /* CONFIG_FB_SIS */ 84 85static int sis_fb_init(DRM_IOCTL_ARGS) 86{ 87 DRM_DEVICE; 88 drm_sis_private_t *dev_priv = dev->dev_private; 89 drm_sis_fb_t fb; 90 int ret; 91 92 DRM_COPY_FROM_USER_IOCTL(fb, (drm_sis_fb_t __user *) data, sizeof(fb)); 93 94 mutex_lock(&dev->struct_mutex); 95#if defined(CONFIG_FB_SIS) 96 { 97 drm_sman_mm_t sman_mm; 98 sman_mm.private = (void *)0xFFFFFFFF; 99 sman_mm.allocate = sis_sman_mm_allocate; 100 sman_mm.free = sis_sman_mm_free; 101 sman_mm.destroy = sis_sman_mm_destroy; 102 sman_mm.offset = sis_sman_mm_offset; 103 ret = 104 drm_sman_set_manager(&dev_priv->sman, VIDEO_TYPE, &sman_mm); 105 } 106#else 107 ret = drm_sman_set_range(&dev_priv->sman, VIDEO_TYPE, 0, 108 fb.size >> SIS_MM_ALIGN_SHIFT); 109#endif 110 111 if (ret) { 112 DRM_ERROR("VRAM memory manager initialisation error\n"); 113 mutex_unlock(&dev->struct_mutex); 114 return ret; 115 } 116 117 dev_priv->vram_initialized = 1; 118 dev_priv->vram_offset = fb.offset; 119 120 mutex_unlock(&dev->struct_mutex); 121 DRM_DEBUG("offset = %u, size = %u", fb.offset, fb.size); 122 123 return 0; 124} 125 126static int sis_drm_alloc(drm_device_t * dev, drm_file_t * priv, 127 unsigned long data, int pool) 128{ 129 drm_sis_private_t *dev_priv = dev->dev_private; 130 drm_sis_mem_t __user *argp = (drm_sis_mem_t __user *) data; 131 drm_sis_mem_t mem; 132 int retval = 0; 133 drm_memblock_item_t *item; 134 135 DRM_COPY_FROM_USER_IOCTL(mem, argp, sizeof(mem)); 136 137 mutex_lock(&dev->struct_mutex); 138 139 if (0 == ((pool == 0) ? dev_priv->vram_initialized : 140 dev_priv->agp_initialized)) { 141 DRM_ERROR 142 ("Attempt to allocate from uninitialized memory manager.\n"); 143 return DRM_ERR(EINVAL); 144 } 145 146 mem.size = (mem.size + SIS_MM_ALIGN_MASK) >> SIS_MM_ALIGN_SHIFT; 147 item = drm_sman_alloc(&dev_priv->sman, pool, mem.size, 0, 148 (unsigned long)priv); 149 150 mutex_unlock(&dev->struct_mutex); 151 if (item) { 152 mem.offset = ((pool == 0) ? 153 dev_priv->vram_offset : dev_priv->agp_offset) + 154 (item->mm-> 155 offset(item->mm, item->mm_info) << SIS_MM_ALIGN_SHIFT); 156 mem.free = item->user_hash.key; 157 mem.size = mem.size << SIS_MM_ALIGN_SHIFT; 158 } else { 159 mem.offset = 0; 160 mem.size = 0; 161 mem.free = 0; 162 retval = DRM_ERR(ENOMEM); 163 } 164 165 DRM_COPY_TO_USER_IOCTL(argp, mem, sizeof(mem)); 166 167 DRM_DEBUG("alloc %d, size = %d, offset = %d\n", pool, mem.size, 168 mem.offset); 169 170 return retval; 171} 172 173static int sis_drm_free(DRM_IOCTL_ARGS) 174{ 175 DRM_DEVICE; 176 drm_sis_private_t *dev_priv = dev->dev_private; 177 drm_sis_mem_t mem; 178 int ret; 179 180 DRM_COPY_FROM_USER_IOCTL(mem, (drm_sis_mem_t __user *) data, 181 sizeof(mem)); 182 183 mutex_lock(&dev->struct_mutex); 184 ret = drm_sman_free_key(&dev_priv->sman, mem.free); 185 mutex_unlock(&dev->struct_mutex); 186 DRM_DEBUG("free = 0x%lx\n", mem.free); 187 188 return ret; 189} 190 191static int sis_fb_alloc(DRM_IOCTL_ARGS) 192{ 193 DRM_DEVICE; 194 return sis_drm_alloc(dev, priv, data, VIDEO_TYPE); 195} 196 197static int sis_ioctl_agp_init(DRM_IOCTL_ARGS) 198{ 199 DRM_DEVICE; 200 drm_sis_private_t *dev_priv = dev->dev_private; 201 drm_sis_agp_t agp; 202 int ret; 203 dev_priv = dev->dev_private; 204 205 DRM_COPY_FROM_USER_IOCTL(agp, (drm_sis_agp_t __user *) data, 206 sizeof(agp)); 207 mutex_lock(&dev->struct_mutex); 208 ret = drm_sman_set_range(&dev_priv->sman, AGP_TYPE, 0, 209 agp.size >> SIS_MM_ALIGN_SHIFT); 210 211 if (ret) { 212 DRM_ERROR("AGP memory manager initialisation error\n"); 213 mutex_unlock(&dev->struct_mutex); 214 return ret; 215 } 216 217 dev_priv->agp_initialized = 1; 218 dev_priv->agp_offset = agp.offset; 219 mutex_unlock(&dev->struct_mutex); 220 221 DRM_DEBUG("offset = %u, size = %u", agp.offset, agp.size); 222 return 0; 223} 224 225static int sis_ioctl_agp_alloc(DRM_IOCTL_ARGS) 226{ 227 DRM_DEVICE; 228 229 return sis_drm_alloc(dev, priv, data, AGP_TYPE); 230} 231 232static drm_local_map_t *sis_reg_init(drm_device_t *dev) 233{ 234 drm_map_list_t *entry; 235 drm_local_map_t *map; 236 237 list_for_each_entry(entry, &dev->maplist->head, head) { 238 map = entry->map; 239 if (!map) 240 continue; 241 if (map->type == _DRM_REGISTERS) { 242 return map; 243 } 244 } 245 return NULL; 246} 247 248int sis_idle(drm_device_t *dev) 249{ 250 drm_sis_private_t *dev_priv = dev->dev_private; 251 uint32_t idle_reg; 252 unsigned long end; 253 int i; 254 255 if (dev_priv->idle_fault) 256 return 0; 257 258 if (dev_priv->mmio == NULL) { 259 dev_priv->mmio = sis_reg_init(dev); 260 if (dev_priv->mmio == NULL) { 261 DRM_ERROR("Could not find register map.\n"); 262 return 0; 263 } 264 } 265 266 /* 267 * Implement a device switch here if needed 268 */ 269 270 if (dev_priv->chipset != SIS_CHIP_315) 271 return 0; 272 273 /* 274 * Timeout after 3 seconds. We cannot use DRM_WAIT_ON here 275 * because its polling frequency is too low. 276 */ 277 278 end = jiffies + (DRM_HZ * 3); 279 280 for (i=0; i<4; ++i) { 281 do { 282 idle_reg = SIS_READ(0x85cc); 283 } while ( !time_after_eq(jiffies, end) && 284 ((idle_reg & 0x80000000) != 0x80000000)); 285 } 286 287 if (time_after_eq(jiffies, end)) { 288 DRM_ERROR("Graphics engine idle timeout. " 289 "Disabling idle check\n"); 290 dev_priv->idle_fault = 1; 291 } 292 293 /* 294 * The caller never sees an error code. It gets trapped 295 * in libdrm. 296 */ 297 298 return 0; 299} 300 301 302void sis_lastclose(struct drm_device *dev) 303{ 304 drm_sis_private_t *dev_priv = dev->dev_private; 305 306 if (!dev_priv) 307 return; 308 309 mutex_lock(&dev->struct_mutex); 310 drm_sman_cleanup(&dev_priv->sman); 311 dev_priv->vram_initialized = 0; 312 dev_priv->agp_initialized = 0; 313 dev_priv->mmio = NULL; 314 mutex_unlock(&dev->struct_mutex); 315} 316 317void sis_reclaim_buffers_locked(drm_device_t * dev, struct file *filp) 318{ 319 drm_sis_private_t *dev_priv = dev->dev_private; 320 drm_file_t *priv = filp->private_data; 321 322 mutex_lock(&dev->struct_mutex); 323 if (drm_sman_owner_clean(&dev_priv->sman, (unsigned long)priv)) { 324 mutex_unlock(&dev->struct_mutex); 325 return; 326 } 327 328 if (dev->driver->dma_quiescent) { 329 dev->driver->dma_quiescent(dev); 330 } 331 332 drm_sman_owner_cleanup(&dev_priv->sman, (unsigned long)priv); 333 mutex_unlock(&dev->struct_mutex); 334 return; 335} 336 337drm_ioctl_desc_t sis_ioctls[] = { 338 [DRM_IOCTL_NR(DRM_SIS_FB_ALLOC)] = {sis_fb_alloc, DRM_AUTH}, 339 [DRM_IOCTL_NR(DRM_SIS_FB_FREE)] = {sis_drm_free, DRM_AUTH}, 340 [DRM_IOCTL_NR(DRM_SIS_AGP_INIT)] = 341 {sis_ioctl_agp_init, DRM_AUTH | DRM_MASTER | DRM_ROOT_ONLY}, 342 [DRM_IOCTL_NR(DRM_SIS_AGP_ALLOC)] = {sis_ioctl_agp_alloc, DRM_AUTH}, 343 [DRM_IOCTL_NR(DRM_SIS_AGP_FREE)] = {sis_drm_free, DRM_AUTH}, 344 [DRM_IOCTL_NR(DRM_SIS_FB_INIT)] = 345 {sis_fb_init, DRM_AUTH | DRM_MASTER | DRM_ROOT_ONLY} 346}; 347 348int sis_max_ioctl = DRM_ARRAY_SIZE(sis_ioctls); 349