18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright 2006 Tungsten Graphics Inc., Bismarck, ND., USA.
38c2ecf20Sopenharmony_ci * All rights reserved.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
68c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
78c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation
88c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sub license,
98c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
108c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice (including the
138c2ecf20Sopenharmony_ci * next paragraph) shall be included in all copies or substantial portions
148c2ecf20Sopenharmony_ci * of the Software.
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
178c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
188c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
198c2ecf20Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
208c2ecf20Sopenharmony_ci * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
218c2ecf20Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
228c2ecf20Sopenharmony_ci * DEALINGS IN THE SOFTWARE.
238c2ecf20Sopenharmony_ci */
248c2ecf20Sopenharmony_ci/*
258c2ecf20Sopenharmony_ci * Authors: Thomas Hellström <thomas-at-tungstengraphics-dot-com>
268c2ecf20Sopenharmony_ci */
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#include <linux/slab.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#include <drm/drm_device.h>
318c2ecf20Sopenharmony_ci#include <drm/drm_file.h>
328c2ecf20Sopenharmony_ci#include <drm/drm_irq.h>
338c2ecf20Sopenharmony_ci#include <drm/via_drm.h>
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#include "via_drv.h"
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#define VIA_MM_ALIGN_SHIFT 4
388c2ecf20Sopenharmony_ci#define VIA_MM_ALIGN_MASK ((1 << VIA_MM_ALIGN_SHIFT) - 1)
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistruct via_memblock {
418c2ecf20Sopenharmony_ci	struct drm_mm_node mm_node;
428c2ecf20Sopenharmony_ci	struct list_head owner_list;
438c2ecf20Sopenharmony_ci};
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ciint via_agp_init(struct drm_device *dev, void *data, struct drm_file *file_priv)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	drm_via_agp_t *agp = data;
488c2ecf20Sopenharmony_ci	drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	mutex_lock(&dev->struct_mutex);
518c2ecf20Sopenharmony_ci	drm_mm_init(&dev_priv->agp_mm, 0, agp->size >> VIA_MM_ALIGN_SHIFT);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	dev_priv->agp_initialized = 1;
548c2ecf20Sopenharmony_ci	dev_priv->agp_offset = agp->offset;
558c2ecf20Sopenharmony_ci	mutex_unlock(&dev->struct_mutex);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	DRM_DEBUG("offset = %u, size = %u\n", agp->offset, agp->size);
588c2ecf20Sopenharmony_ci	return 0;
598c2ecf20Sopenharmony_ci}
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ciint via_fb_init(struct drm_device *dev, void *data, struct drm_file *file_priv)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	drm_via_fb_t *fb = data;
648c2ecf20Sopenharmony_ci	drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	mutex_lock(&dev->struct_mutex);
678c2ecf20Sopenharmony_ci	drm_mm_init(&dev_priv->vram_mm, 0, fb->size >> VIA_MM_ALIGN_SHIFT);
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	dev_priv->vram_initialized = 1;
708c2ecf20Sopenharmony_ci	dev_priv->vram_offset = fb->offset;
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	mutex_unlock(&dev->struct_mutex);
738c2ecf20Sopenharmony_ci	DRM_DEBUG("offset = %u, size = %u\n", fb->offset, fb->size);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	return 0;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ciint via_final_context(struct drm_device *dev, int context)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	via_release_futex(dev_priv, context);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	/* Linux specific until context tracking code gets ported to BSD */
868c2ecf20Sopenharmony_ci	/* Last context, perform cleanup */
878c2ecf20Sopenharmony_ci	if (list_is_singular(&dev->ctxlist)) {
888c2ecf20Sopenharmony_ci		DRM_DEBUG("Last Context\n");
898c2ecf20Sopenharmony_ci		drm_irq_uninstall(dev);
908c2ecf20Sopenharmony_ci		via_cleanup_futex(dev_priv);
918c2ecf20Sopenharmony_ci		via_do_cleanup_map(dev);
928c2ecf20Sopenharmony_ci	}
938c2ecf20Sopenharmony_ci	return 1;
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_civoid via_lastclose(struct drm_device *dev)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	if (!dev_priv)
1018c2ecf20Sopenharmony_ci		return;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	mutex_lock(&dev->struct_mutex);
1048c2ecf20Sopenharmony_ci	if (dev_priv->vram_initialized) {
1058c2ecf20Sopenharmony_ci		drm_mm_takedown(&dev_priv->vram_mm);
1068c2ecf20Sopenharmony_ci		dev_priv->vram_initialized = 0;
1078c2ecf20Sopenharmony_ci	}
1088c2ecf20Sopenharmony_ci	if (dev_priv->agp_initialized) {
1098c2ecf20Sopenharmony_ci		drm_mm_takedown(&dev_priv->agp_mm);
1108c2ecf20Sopenharmony_ci		dev_priv->agp_initialized = 0;
1118c2ecf20Sopenharmony_ci	}
1128c2ecf20Sopenharmony_ci	mutex_unlock(&dev->struct_mutex);
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ciint via_mem_alloc(struct drm_device *dev, void *data,
1168c2ecf20Sopenharmony_ci		  struct drm_file *file)
1178c2ecf20Sopenharmony_ci{
1188c2ecf20Sopenharmony_ci	drm_via_mem_t *mem = data;
1198c2ecf20Sopenharmony_ci	int retval = 0, user_key;
1208c2ecf20Sopenharmony_ci	struct via_memblock *item;
1218c2ecf20Sopenharmony_ci	drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private;
1228c2ecf20Sopenharmony_ci	struct via_file_private *file_priv = file->driver_priv;
1238c2ecf20Sopenharmony_ci	unsigned long tmpSize;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	if (mem->type > VIA_MEM_AGP) {
1268c2ecf20Sopenharmony_ci		DRM_ERROR("Unknown memory type allocation\n");
1278c2ecf20Sopenharmony_ci		return -EINVAL;
1288c2ecf20Sopenharmony_ci	}
1298c2ecf20Sopenharmony_ci	mutex_lock(&dev->struct_mutex);
1308c2ecf20Sopenharmony_ci	if (0 == ((mem->type == VIA_MEM_VIDEO) ? dev_priv->vram_initialized :
1318c2ecf20Sopenharmony_ci		      dev_priv->agp_initialized)) {
1328c2ecf20Sopenharmony_ci		DRM_ERROR
1338c2ecf20Sopenharmony_ci		    ("Attempt to allocate from uninitialized memory manager.\n");
1348c2ecf20Sopenharmony_ci		mutex_unlock(&dev->struct_mutex);
1358c2ecf20Sopenharmony_ci		return -EINVAL;
1368c2ecf20Sopenharmony_ci	}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	item = kzalloc(sizeof(*item), GFP_KERNEL);
1398c2ecf20Sopenharmony_ci	if (!item) {
1408c2ecf20Sopenharmony_ci		retval = -ENOMEM;
1418c2ecf20Sopenharmony_ci		goto fail_alloc;
1428c2ecf20Sopenharmony_ci	}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	tmpSize = (mem->size + VIA_MM_ALIGN_MASK) >> VIA_MM_ALIGN_SHIFT;
1458c2ecf20Sopenharmony_ci	if (mem->type == VIA_MEM_AGP)
1468c2ecf20Sopenharmony_ci		retval = drm_mm_insert_node(&dev_priv->agp_mm,
1478c2ecf20Sopenharmony_ci					    &item->mm_node,
1488c2ecf20Sopenharmony_ci					    tmpSize);
1498c2ecf20Sopenharmony_ci	else
1508c2ecf20Sopenharmony_ci		retval = drm_mm_insert_node(&dev_priv->vram_mm,
1518c2ecf20Sopenharmony_ci					    &item->mm_node,
1528c2ecf20Sopenharmony_ci					    tmpSize);
1538c2ecf20Sopenharmony_ci	if (retval)
1548c2ecf20Sopenharmony_ci		goto fail_alloc;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	retval = idr_alloc(&dev_priv->object_idr, item, 1, 0, GFP_KERNEL);
1578c2ecf20Sopenharmony_ci	if (retval < 0)
1588c2ecf20Sopenharmony_ci		goto fail_idr;
1598c2ecf20Sopenharmony_ci	user_key = retval;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	list_add(&item->owner_list, &file_priv->obj_list);
1628c2ecf20Sopenharmony_ci	mutex_unlock(&dev->struct_mutex);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	mem->offset = ((mem->type == VIA_MEM_VIDEO) ?
1658c2ecf20Sopenharmony_ci		      dev_priv->vram_offset : dev_priv->agp_offset) +
1668c2ecf20Sopenharmony_ci	    ((item->mm_node.start) << VIA_MM_ALIGN_SHIFT);
1678c2ecf20Sopenharmony_ci	mem->index = user_key;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	return 0;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_cifail_idr:
1728c2ecf20Sopenharmony_ci	drm_mm_remove_node(&item->mm_node);
1738c2ecf20Sopenharmony_cifail_alloc:
1748c2ecf20Sopenharmony_ci	kfree(item);
1758c2ecf20Sopenharmony_ci	mutex_unlock(&dev->struct_mutex);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	mem->offset = 0;
1788c2ecf20Sopenharmony_ci	mem->size = 0;
1798c2ecf20Sopenharmony_ci	mem->index = 0;
1808c2ecf20Sopenharmony_ci	DRM_DEBUG("Video memory allocation failed\n");
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	return retval;
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ciint via_mem_free(struct drm_device *dev, void *data, struct drm_file *file_priv)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	drm_via_private_t *dev_priv = dev->dev_private;
1888c2ecf20Sopenharmony_ci	drm_via_mem_t *mem = data;
1898c2ecf20Sopenharmony_ci	struct via_memblock *obj;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	mutex_lock(&dev->struct_mutex);
1928c2ecf20Sopenharmony_ci	obj = idr_find(&dev_priv->object_idr, mem->index);
1938c2ecf20Sopenharmony_ci	if (obj == NULL) {
1948c2ecf20Sopenharmony_ci		mutex_unlock(&dev->struct_mutex);
1958c2ecf20Sopenharmony_ci		return -EINVAL;
1968c2ecf20Sopenharmony_ci	}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	idr_remove(&dev_priv->object_idr, mem->index);
1998c2ecf20Sopenharmony_ci	list_del(&obj->owner_list);
2008c2ecf20Sopenharmony_ci	drm_mm_remove_node(&obj->mm_node);
2018c2ecf20Sopenharmony_ci	kfree(obj);
2028c2ecf20Sopenharmony_ci	mutex_unlock(&dev->struct_mutex);
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	DRM_DEBUG("free = 0x%lx\n", mem->index);
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	return 0;
2078c2ecf20Sopenharmony_ci}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_civoid via_reclaim_buffers_locked(struct drm_device *dev,
2118c2ecf20Sopenharmony_ci				struct drm_file *file)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	struct via_file_private *file_priv = file->driver_priv;
2148c2ecf20Sopenharmony_ci	struct via_memblock *entry, *next;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	if (!(dev->master && file->master->lock.hw_lock))
2178c2ecf20Sopenharmony_ci		return;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	drm_legacy_idlelock_take(&file->master->lock);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	mutex_lock(&dev->struct_mutex);
2228c2ecf20Sopenharmony_ci	if (list_empty(&file_priv->obj_list)) {
2238c2ecf20Sopenharmony_ci		mutex_unlock(&dev->struct_mutex);
2248c2ecf20Sopenharmony_ci		drm_legacy_idlelock_release(&file->master->lock);
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci		return;
2278c2ecf20Sopenharmony_ci	}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	via_driver_dma_quiescent(dev);
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	list_for_each_entry_safe(entry, next, &file_priv->obj_list,
2328c2ecf20Sopenharmony_ci				 owner_list) {
2338c2ecf20Sopenharmony_ci		list_del(&entry->owner_list);
2348c2ecf20Sopenharmony_ci		drm_mm_remove_node(&entry->mm_node);
2358c2ecf20Sopenharmony_ci		kfree(entry);
2368c2ecf20Sopenharmony_ci	}
2378c2ecf20Sopenharmony_ci	mutex_unlock(&dev->struct_mutex);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	drm_legacy_idlelock_release(&file->master->lock);
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	return;
2428c2ecf20Sopenharmony_ci}
243