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) || defined(CONFIG_FB_SIS_MODULE) 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 *)(unsigned long)~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[_MODULE] */ 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[_MODULE] */ 84 85static int sis_fb_init(struct drm_device *dev, void *data, struct drm_file *file_priv) 86{ 87 drm_sis_private_t *dev_priv = dev->dev_private; 88 drm_sis_fb_t *fb = data; 89 int ret; 90 91 mutex_lock(&dev->struct_mutex); 92#if defined(CONFIG_FB_SIS) || defined(CONFIG_FB_SIS_MODULE) 93 { 94 struct drm_sman_mm sman_mm; 95 sman_mm.private = (void *)0xFFFFFFFF; 96 sman_mm.allocate = sis_sman_mm_allocate; 97 sman_mm.free = sis_sman_mm_free; 98 sman_mm.destroy = sis_sman_mm_destroy; 99 sman_mm.offset = sis_sman_mm_offset; 100 ret = 101 drm_sman_set_manager(&dev_priv->sman, VIDEO_TYPE, &sman_mm); 102 } 103#else 104 ret = drm_sman_set_range(&dev_priv->sman, VIDEO_TYPE, 0, 105 fb->size >> SIS_MM_ALIGN_SHIFT); 106#endif 107 108 if (ret) { 109 DRM_ERROR("VRAM memory manager initialisation error\n"); 110 mutex_unlock(&dev->struct_mutex); 111 return ret; 112 } 113 114 dev_priv->vram_initialized = 1; 115 dev_priv->vram_offset = fb->offset; 116 117 mutex_unlock(&dev->struct_mutex); 118 DRM_DEBUG("offset = %u, size = %u\n", fb->offset, fb->size); 119 120 return 0; 121} 122 123static int sis_drm_alloc(struct drm_device *dev, struct drm_file *file_priv, 124 void *data, int pool) 125{ 126 drm_sis_private_t *dev_priv = dev->dev_private; 127 drm_sis_mem_t *mem = data; 128 int retval = 0; 129 struct drm_memblock_item *item; 130 131 mutex_lock(&dev->struct_mutex); 132 133 if (0 == ((pool == 0) ? dev_priv->vram_initialized : 134 dev_priv->agp_initialized)) { 135 DRM_ERROR 136 ("Attempt to allocate from uninitialized memory manager.\n"); 137 mutex_unlock(&dev->struct_mutex); 138 return -EINVAL; 139 } 140 141 mem->size = (mem->size + SIS_MM_ALIGN_MASK) >> SIS_MM_ALIGN_SHIFT; 142 item = drm_sman_alloc(&dev_priv->sman, pool, mem->size, 0, 143 (unsigned long)file_priv); 144 145 mutex_unlock(&dev->struct_mutex); 146 if (item) { 147 mem->offset = ((pool == 0) ? 148 dev_priv->vram_offset : dev_priv->agp_offset) + 149 (item->mm-> 150 offset(item->mm, item->mm_info) << SIS_MM_ALIGN_SHIFT); 151 mem->free = item->user_hash.key; 152 mem->size = mem->size << SIS_MM_ALIGN_SHIFT; 153 } else { 154 mem->offset = 0; 155 mem->size = 0; 156 mem->free = 0; 157 retval = -ENOMEM; 158 } 159 160 DRM_DEBUG("alloc %d, size = %d, offset = %d\n", pool, mem->size, 161 mem->offset); 162 163 return retval; 164} 165 166static int sis_drm_free(struct drm_device *dev, void *data, struct drm_file *file_priv) 167{ 168 drm_sis_private_t *dev_priv = dev->dev_private; 169 drm_sis_mem_t *mem = data; 170 int ret; 171 172 mutex_lock(&dev->struct_mutex); 173 ret = drm_sman_free_key(&dev_priv->sman, mem->free); 174 mutex_unlock(&dev->struct_mutex); 175 DRM_DEBUG("free = 0x%lx\n", mem->free); 176 177 return ret; 178} 179 180static int sis_fb_alloc(struct drm_device *dev, void *data, 181 struct drm_file *file_priv) 182{ 183 return sis_drm_alloc(dev, file_priv, data, VIDEO_TYPE); 184} 185 186static int sis_ioctl_agp_init(struct drm_device *dev, void *data, 187 struct drm_file *file_priv) 188{ 189 drm_sis_private_t *dev_priv = dev->dev_private; 190 drm_sis_agp_t *agp = data; 191 int ret; 192 dev_priv = dev->dev_private; 193 194 mutex_lock(&dev->struct_mutex); 195 ret = drm_sman_set_range(&dev_priv->sman, AGP_TYPE, 0, 196 agp->size >> SIS_MM_ALIGN_SHIFT); 197 198 if (ret) { 199 DRM_ERROR("AGP memory manager initialisation error\n"); 200 mutex_unlock(&dev->struct_mutex); 201 return ret; 202 } 203 204 dev_priv->agp_initialized = 1; 205 dev_priv->agp_offset = agp->offset; 206 mutex_unlock(&dev->struct_mutex); 207 208 DRM_DEBUG("offset = %u, size = %u\n", agp->offset, agp->size); 209 return 0; 210} 211 212static int sis_ioctl_agp_alloc(struct drm_device *dev, void *data, 213 struct drm_file *file_priv) 214{ 215 216 return sis_drm_alloc(dev, file_priv, data, AGP_TYPE); 217} 218 219static drm_local_map_t *sis_reg_init(struct drm_device *dev) 220{ 221 struct drm_map_list *entry; 222 drm_local_map_t *map; 223 224 list_for_each_entry(entry, &dev->maplist, head) { 225 map = entry->map; 226 if (!map) 227 continue; 228 if (map->type == _DRM_REGISTERS) 229 return map; 230 } 231 return NULL; 232} 233 234int sis_idle(struct drm_device *dev) 235{ 236 drm_sis_private_t *dev_priv = dev->dev_private; 237 uint32_t idle_reg; 238 unsigned long end; 239 int i; 240 241 if (dev_priv->idle_fault) 242 return 0; 243 244 if (dev_priv->mmio == NULL) { 245 dev_priv->mmio = sis_reg_init(dev); 246 if (dev_priv->mmio == NULL) { 247 DRM_ERROR("Could not find register map.\n"); 248 return 0; 249 } 250 } 251 252 /* 253 * Implement a device switch here if needed 254 */ 255 256 if (dev_priv->chipset != SIS_CHIP_315) 257 return 0; 258 259 /* 260 * Timeout after 3 seconds. We cannot use DRM_WAIT_ON here 261 * because its polling frequency is too low. 262 */ 263 264 end = jiffies + (DRM_HZ * 3); 265 266 for (i = 0; i < 4; ++i) { 267 do { 268 idle_reg = SIS_READ(0x85cc); 269 } while (!time_after_eq(jiffies, end) && 270 ((idle_reg & 0x80000000) != 0x80000000)); 271 } 272 273 if (time_after_eq(jiffies, end)) { 274 DRM_ERROR("Graphics engine idle timeout. " 275 "Disabling idle check\n"); 276 dev_priv->idle_fault = 1; 277 } 278 279 /* 280 * The caller never sees an error code. It gets trapped 281 * in libdrm. 282 */ 283 284 return 0; 285} 286 287 288void sis_lastclose(struct drm_device *dev) 289{ 290 drm_sis_private_t *dev_priv = dev->dev_private; 291 292 if (!dev_priv) 293 return; 294 295 mutex_lock(&dev->struct_mutex); 296 drm_sman_cleanup(&dev_priv->sman); 297 dev_priv->vram_initialized = 0; 298 dev_priv->agp_initialized = 0; 299 dev_priv->mmio = NULL; 300 mutex_unlock(&dev->struct_mutex); 301} 302 303void sis_reclaim_buffers_locked(struct drm_device *dev, 304 struct drm_file *file_priv) 305{ 306 drm_sis_private_t *dev_priv = dev->dev_private; 307 308 mutex_lock(&dev->struct_mutex); 309 if (drm_sman_owner_clean(&dev_priv->sman, (unsigned long)file_priv)) { 310 mutex_unlock(&dev->struct_mutex); 311 return; 312 } 313 314 if (dev->driver->dma_quiescent) 315 dev->driver->dma_quiescent(dev); 316 317 drm_sman_owner_cleanup(&dev_priv->sman, (unsigned long)file_priv); 318 mutex_unlock(&dev->struct_mutex); 319 return; 320} 321 322struct drm_ioctl_desc sis_ioctls[] = { 323 DRM_IOCTL_DEF_DRV(SIS_FB_ALLOC, sis_fb_alloc, DRM_AUTH), 324 DRM_IOCTL_DEF_DRV(SIS_FB_FREE, sis_drm_free, DRM_AUTH), 325 DRM_IOCTL_DEF_DRV(SIS_AGP_INIT, sis_ioctl_agp_init, DRM_AUTH | DRM_MASTER | DRM_ROOT_ONLY), 326 DRM_IOCTL_DEF_DRV(SIS_AGP_ALLOC, sis_ioctl_agp_alloc, DRM_AUTH), 327 DRM_IOCTL_DEF_DRV(SIS_AGP_FREE, sis_drm_free, DRM_AUTH), 328 DRM_IOCTL_DEF_DRV(SIS_FB_INIT, sis_fb_init, DRM_AUTH | DRM_MASTER | DRM_ROOT_ONLY), 329}; 330 331int sis_max_ioctl = DRM_ARRAY_SIZE(sis_ioctls); 332