sis_mm.c 8.3 KB
Newer Older
1
/**************************************************************************
L
Linus Torvalds 已提交
2
 *
3 4
 * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA.
 * All Rights Reserved.
L
Linus Torvalds 已提交
5 6
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
7 8 9 10 11 12
 * 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, sub license, 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:
D
Dave Airlie 已提交
13
 *
14 15 16
 * The above copyright notice and this permission notice (including the
 * next paragraph) shall be included in all copies or substantial portions
 * of the Software.
D
Dave Airlie 已提交
17
 *
L
Linus Torvalds 已提交
18 19
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 21 22 23 24
 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
 * THE COPYRIGHT HOLDERS, AUTHORS 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.
D
Dave Airlie 已提交
25 26
 *
 *
27 28 29 30 31
 **************************************************************************/

/*
 * Authors:
 *    Thomas Hellstrm <thomas-at-tungstengraphics-dot-com>
L
Linus Torvalds 已提交
32 33 34 35 36
 */

#include "drmP.h"
#include "sis_drm.h"
#include "sis_drv.h"
37

L
Linus Torvalds 已提交
38 39
#include <video/sisfb.h>

D
Dave Airlie 已提交
40
#define VIDEO_TYPE 0
L
Linus Torvalds 已提交
41 42
#define AGP_TYPE 1

43 44
#define SIS_MM_ALIGN_SHIFT 4
#define SIS_MM_ALIGN_MASK ( (1 << SIS_MM_ALIGN_SHIFT) - 1)
L
Linus Torvalds 已提交
45

46 47
#if defined(CONFIG_FB_SIS)
/* fb management via fb device */
D
Dave Airlie 已提交
48

49 50
#define SIS_MM_ALIGN_SHIFT 0
#define SIS_MM_ALIGN_MASK 0
L
Linus Torvalds 已提交
51

52 53
static void *sis_sman_mm_allocate(void *private, unsigned long size,
				  unsigned alignment)
D
Dave Airlie 已提交
54
{
55
	struct sis_memreq req;
L
Linus Torvalds 已提交
56

57 58 59 60 61 62
	req.size = size;
	sis_malloc(&req);
	if (req.size == 0)
		return NULL;
	else
		return (void *)~req.offset;
L
Linus Torvalds 已提交
63 64
}

65
static void sis_sman_mm_free(void *private, void *ref)
L
Linus Torvalds 已提交
66
{
67
	sis_free(~((unsigned long)ref));
L
Linus Torvalds 已提交
68 69
}

70
static void sis_sman_mm_destroy(void *private)
L
Linus Torvalds 已提交
71
{
72
	;
L
Linus Torvalds 已提交
73 74
}

75
unsigned long sis_sman_mm_offset(void *private, void *ref)
L
Linus Torvalds 已提交
76
{
77
	return ~((unsigned long)ref);
L
Linus Torvalds 已提交
78 79
}

80
#endif
L
Linus Torvalds 已提交
81

D
Dave Airlie 已提交
82
static int sis_fb_init(DRM_IOCTL_ARGS)
L
Linus Torvalds 已提交
83 84 85 86
{
	DRM_DEVICE;
	drm_sis_private_t *dev_priv = dev->dev_private;
	drm_sis_fb_t fb;
87
	int ret;
L
Linus Torvalds 已提交
88

D
Dave Airlie 已提交
89
	DRM_COPY_FROM_USER_IOCTL(fb, (drm_sis_fb_t __user *) data, sizeof(fb));
L
Linus Torvalds 已提交
90

91 92 93 94 95 96 97 98 99 100 101
	mutex_lock(&dev->struct_mutex);
#if defined(CONFIG_FB_SIS)
	{
		drm_sman_mm_t sman_mm;
		sman_mm.private = (void *)0xFFFFFFFF;
		sman_mm.allocate = sis_sman_mm_allocate;
		sman_mm.free = sis_sman_mm_free;
		sman_mm.destroy = sis_sman_mm_destroy;
		sman_mm.offset = sis_sman_mm_offset;
		ret =
		    drm_sman_set_manager(&dev_priv->sman, VIDEO_TYPE, &sman_mm);
L
Linus Torvalds 已提交
102
	}
103 104 105 106
#else
	ret = drm_sman_set_range(&dev_priv->sman, VIDEO_TYPE, 0,
				 fb.size >> SIS_MM_ALIGN_SHIFT);
#endif
L
Linus Torvalds 已提交
107

108 109 110 111 112
	if (ret) {
		DRM_ERROR("VRAM memory manager initialisation error\n");
		mutex_unlock(&dev->struct_mutex);
		return ret;
	}
L
Linus Torvalds 已提交
113

114
	dev_priv->vram_initialized = 1;
115
	dev_priv->vram_offset = fb.offset;
L
Linus Torvalds 已提交
116

117
	mutex_unlock(&dev->struct_mutex);
L
Linus Torvalds 已提交
118 119 120 121 122
	DRM_DEBUG("offset = %u, size = %u", fb.offset, fb.size);

	return 0;
}

123 124
static int sis_drm_alloc(drm_device_t * dev, drm_file_t * priv,
			 unsigned long data, int pool)
L
Linus Torvalds 已提交
125 126
{
	drm_sis_private_t *dev_priv = dev->dev_private;
127 128
	drm_sis_mem_t __user *argp = (drm_sis_mem_t __user *) data;
	drm_sis_mem_t mem;
L
Linus Torvalds 已提交
129
	int retval = 0;
130
	drm_memblock_item_t *item;
L
Linus Torvalds 已提交
131

132 133 134 135
	DRM_COPY_FROM_USER_IOCTL(mem, argp, sizeof(mem));

	mutex_lock(&dev->struct_mutex);

136
	if (0 == ((pool == 0) ? dev_priv->vram_initialized :
137 138 139
		      dev_priv->agp_initialized)) {
		DRM_ERROR
		    ("Attempt to allocate from uninitialized memory manager.\n");
L
Linus Torvalds 已提交
140
		return DRM_ERR(EINVAL);
141
	}
D
Dave Airlie 已提交
142

143 144 145 146 147 148 149 150 151 152 153 154
	mem.size = (mem.size + SIS_MM_ALIGN_MASK) >> SIS_MM_ALIGN_SHIFT;
	item = drm_sman_alloc(&dev_priv->sman, pool, mem.size, 0,
			      (unsigned long)priv);

	mutex_unlock(&dev->struct_mutex);
	if (item) {
		mem.offset = ((pool == 0) ?
			      dev_priv->vram_offset : dev_priv->agp_offset) +
		    (item->mm->
		     offset(item->mm, item->mm_info) << SIS_MM_ALIGN_SHIFT);
		mem.free = item->user_hash.key;
		mem.size = mem.size << SIS_MM_ALIGN_SHIFT;
L
Linus Torvalds 已提交
155
	} else {
156 157 158 159
		mem.offset = 0;
		mem.size = 0;
		mem.free = 0;
		retval = DRM_ERR(ENOMEM);
L
Linus Torvalds 已提交
160 161
	}

162
	DRM_COPY_TO_USER_IOCTL(argp, mem, sizeof(mem));
L
Linus Torvalds 已提交
163

164 165
	DRM_DEBUG("alloc %d, size = %d, offset = %d\n", pool, mem.size,
		  mem.offset);
L
Linus Torvalds 已提交
166 167 168 169

	return retval;
}

170
static int sis_drm_free(DRM_IOCTL_ARGS)
L
Linus Torvalds 已提交
171 172 173
{
	DRM_DEVICE;
	drm_sis_private_t *dev_priv = dev->dev_private;
174 175
	drm_sis_mem_t mem;
	int ret;
L
Linus Torvalds 已提交
176

177 178
	DRM_COPY_FROM_USER_IOCTL(mem, (drm_sis_mem_t __user *) data,
				 sizeof(mem));
L
Linus Torvalds 已提交
179

180 181 182 183
	mutex_lock(&dev->struct_mutex);
	ret = drm_sman_free_key(&dev_priv->sman, mem.free);
	mutex_unlock(&dev->struct_mutex);
	DRM_DEBUG("free = 0x%lx\n", mem.free);
L
Linus Torvalds 已提交
184

185
	return ret;
L
Linus Torvalds 已提交
186 187
}

188 189 190 191 192
static int sis_fb_alloc(DRM_IOCTL_ARGS)
{
	DRM_DEVICE;
	return sis_drm_alloc(dev, priv, data, VIDEO_TYPE);
}
L
Linus Torvalds 已提交
193

D
Dave Airlie 已提交
194
static int sis_ioctl_agp_init(DRM_IOCTL_ARGS)
L
Linus Torvalds 已提交
195 196 197 198
{
	DRM_DEVICE;
	drm_sis_private_t *dev_priv = dev->dev_private;
	drm_sis_agp_t agp;
199 200
	int ret;
	dev_priv = dev->dev_private;
L
Linus Torvalds 已提交
201

D
Dave Airlie 已提交
202 203
	DRM_COPY_FROM_USER_IOCTL(agp, (drm_sis_agp_t __user *) data,
				 sizeof(agp));
204 205 206 207 208 209 210 211 212
	mutex_lock(&dev->struct_mutex);
	ret = drm_sman_set_range(&dev_priv->sman, AGP_TYPE, 0,
				 agp.size >> SIS_MM_ALIGN_SHIFT);

	if (ret) {
		DRM_ERROR("AGP memory manager initialisation error\n");
		mutex_unlock(&dev->struct_mutex);
		return ret;
	}
L
Linus Torvalds 已提交
213

214
	dev_priv->agp_initialized = 1;
215 216
	dev_priv->agp_offset = agp.offset;
	mutex_unlock(&dev->struct_mutex);
L
Linus Torvalds 已提交
217 218 219 220 221

	DRM_DEBUG("offset = %u, size = %u", agp.offset, agp.size);
	return 0;
}

D
Dave Airlie 已提交
222
static int sis_ioctl_agp_alloc(DRM_IOCTL_ARGS)
L
Linus Torvalds 已提交
223 224 225
{
	DRM_DEVICE;

226
	return sis_drm_alloc(dev, priv, data, AGP_TYPE);
L
Linus Torvalds 已提交
227 228
}

T
Thomas Hellstrom 已提交
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
static drm_local_map_t *sis_reg_init(drm_device_t *dev)
{
	drm_map_list_t *entry;
	drm_local_map_t *map;

	list_for_each_entry(entry, &dev->maplist->head, head) {
		map = entry->map;
		if (!map)
			continue;
		if (map->type == _DRM_REGISTERS) {
			return map;
		}
	}
	return NULL;
}

int sis_idle(drm_device_t *dev)
{
	drm_sis_private_t *dev_priv = dev->dev_private;
	uint32_t idle_reg;
	unsigned long end;
	int i;

	if (dev_priv->idle_fault)
		return 0;

	if (dev_priv->mmio == NULL) {
		dev_priv->mmio = sis_reg_init(dev);
		if (dev_priv->mmio == NULL) {
			DRM_ERROR("Could not find register map.\n");
			return 0;
		}
	}
	
	/*
	 * Implement a device switch here if needed
	 */

	if (dev_priv->chipset != SIS_CHIP_315)
		return 0;

	/*
	 * Timeout after 3 seconds. We cannot use DRM_WAIT_ON here
	 * because its polling frequency is too low.
	 */

	end = jiffies + (DRM_HZ * 3);

	for (i=0; i<4; ++i) {
		do {
			idle_reg = SIS_READ(0x85cc);
		} while ( !time_after_eq(jiffies, end) &&
			  ((idle_reg & 0x80000000) != 0x80000000));
	}

	if (time_after_eq(jiffies, end)) {
		DRM_ERROR("Graphics engine idle timeout. "
			  "Disabling idle check\n");
287
		dev_priv->idle_fault = 1;
T
Thomas Hellstrom 已提交
288 289 290 291 292 293 294 295 296 297 298
	}

	/*
	 * The caller never sees an error code. It gets trapped
	 * in libdrm.
	 */

	return 0;
}


299
void sis_lastclose(struct drm_device *dev)
L
Linus Torvalds 已提交
300 301 302
{
	drm_sis_private_t *dev_priv = dev->dev_private;

303 304
	if (!dev_priv)
		return;
L
Linus Torvalds 已提交
305

306 307
	mutex_lock(&dev->struct_mutex);
	drm_sman_cleanup(&dev_priv->sman);
308 309
	dev_priv->vram_initialized = 0;
	dev_priv->agp_initialized = 0;
T
Thomas Hellstrom 已提交
310
	dev_priv->mmio = NULL;
311
	mutex_unlock(&dev->struct_mutex);
L
Linus Torvalds 已提交
312 313
}

314
void sis_reclaim_buffers_locked(drm_device_t * dev, struct file *filp)
L
Linus Torvalds 已提交
315
{
316 317
	drm_sis_private_t *dev_priv = dev->dev_private;
	drm_file_t *priv = filp->private_data;
L
Linus Torvalds 已提交
318

319 320 321 322
	mutex_lock(&dev->struct_mutex);
	if (drm_sman_owner_clean(&dev_priv->sman, (unsigned long)priv)) {
		mutex_unlock(&dev->struct_mutex);
		return;
L
Linus Torvalds 已提交
323 324
	}

325 326
	if (dev->driver->dma_quiescent) {
		dev->driver->dma_quiescent(dev);
D
Dave Airlie 已提交
327 328
	}

329 330 331
	drm_sman_owner_cleanup(&dev_priv->sman, (unsigned long)priv);
	mutex_unlock(&dev->struct_mutex);
	return;
L
Linus Torvalds 已提交
332 333 334
}

drm_ioctl_desc_t sis_ioctls[] = {
335
	[DRM_IOCTL_NR(DRM_SIS_FB_ALLOC)] = {sis_fb_alloc, DRM_AUTH},
336 337 338
	[DRM_IOCTL_NR(DRM_SIS_FB_FREE)] = {sis_drm_free, DRM_AUTH},
	[DRM_IOCTL_NR(DRM_SIS_AGP_INIT)] =
	    {sis_ioctl_agp_init, DRM_AUTH | DRM_MASTER | DRM_ROOT_ONLY},
339
	[DRM_IOCTL_NR(DRM_SIS_AGP_ALLOC)] = {sis_ioctl_agp_alloc, DRM_AUTH},
340 341 342
	[DRM_IOCTL_NR(DRM_SIS_AGP_FREE)] = {sis_drm_free, DRM_AUTH},
	[DRM_IOCTL_NR(DRM_SIS_FB_INIT)] =
	    {sis_fb_init, DRM_AUTH | DRM_MASTER | DRM_ROOT_ONLY}
L
Linus Torvalds 已提交
343 344 345
};

int sis_max_ioctl = DRM_ARRAY_SIZE(sis_ioctls);