-\r
-/****************************************************************************\r
- * Copyright (C) 2003-2006 by XGI Technology, Taiwan. \r
- * *\r
- * All Rights Reserved. *\r
- * *\r
- * Permission is hereby granted, free of charge, to any person obtaining\r
- * a copy of this software and associated documentation files (the \r
- * "Software"), to deal in the Software without restriction, including \r
- * without limitation on the rights to use, copy, modify, merge, \r
- * publish, distribute, sublicense, and/or sell copies of the Software, \r
- * and to permit persons to whom the Software is furnished to do so, \r
- * subject to the following conditions: \r
- * *\r
- * The above copyright notice and this permission notice (including the \r
- * next paragraph) shall be included in all copies or substantial \r
- * portions of the Software. \r
- * *\r
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, \r
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF \r
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND \r
- * NON-INFRINGEMENT. IN NO EVENT SHALL XGI AND/OR \r
- * ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, \r
- * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, \r
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER \r
- * DEALINGS IN THE SOFTWARE. \r
- ***************************************************************************/\r
-\r
-#include "xgi_types.h"\r
-#include "xgi_linux.h"\r
-#include "xgi_drv.h"\r
-#include "xgi_fb.h"\r
-\r
-#define XGI_FB_HEAP_START 0x1000000\r
-\r
-static xgi_mem_heap_t *xgi_fb_heap;\r
-static kmem_cache_t *xgi_fb_cache_block = NULL;\r
-extern struct list_head xgi_mempid_list;\r
-\r
-static xgi_mem_block_t *xgi_mem_new_node(void);\r
-static xgi_mem_block_t *xgi_mem_alloc(xgi_info_t *info, unsigned long size);\r
-static xgi_mem_block_t *xgi_mem_free(xgi_info_t *info, unsigned long offset);\r
-\r
-void xgi_fb_alloc(xgi_info_t *info,\r
- xgi_mem_req_t *req,\r
- xgi_mem_alloc_t *alloc)\r
-{\r
- xgi_mem_block_t *block;\r
- xgi_mem_pid_t *mempid_block;\r
-\r
- if (req->is_front)\r
- {\r
- alloc->location = LOCAL;\r
- alloc->bus_addr = info->fb.base;\r
- alloc->hw_addr = 0;\r
- XGI_INFO("Video RAM allocation on front buffer successfully! \n");\r
- }\r
- else\r
- {\r
- xgi_down(info->fb_sem);\r
- block = xgi_mem_alloc(info, req->size);\r
- xgi_up(info->fb_sem);\r
-\r
- if (block == NULL)\r
- {\r
- alloc->location = LOCAL;\r
- alloc->size = 0;\r
- alloc->bus_addr = 0;\r
- alloc->hw_addr = 0;\r
- XGI_ERROR("Video RAM allocation failed\n");\r
- }\r
- else\r
- {\r
- XGI_INFO("Video RAM allocation succeeded: 0x%p\n",\r
- (char *) block->offset);\r
- alloc->location = LOCAL;\r
- alloc->size = block->size;\r
- alloc->bus_addr = info->fb.base + block->offset;\r
- alloc->hw_addr = block->offset;\r
-\r
- /* manage mempid */\r
- mempid_block = kmalloc(sizeof(xgi_mem_pid_t), GFP_KERNEL);\r
- mempid_block->location = LOCAL;\r
- mempid_block->bus_addr = alloc->bus_addr;\r
- mempid_block->pid = alloc->pid;\r
-\r
- if (!mempid_block)\r
- XGI_ERROR("mempid_block alloc failed\n");\r
-\r
- XGI_INFO("Memory ProcessID add one fb block pid:%ld successfully! \n", mempid_block->pid);\r
- list_add(&mempid_block->list, &xgi_mempid_list);\r
- }\r
- }\r
-}\r
-\r
-void xgi_fb_free(xgi_info_t *info, unsigned long bus_addr)\r
-{\r
- xgi_mem_block_t *block;\r
- unsigned long offset = bus_addr - info->fb.base;\r
- xgi_mem_pid_t *mempid_block;\r
- xgi_mem_pid_t *mempid_freeblock = NULL;\r
- struct list_head *mempid_list;\r
-\r
- if (offset < 0)\r
- {\r
- XGI_INFO("free onscreen frame buffer successfully !\n");\r
- }\r
- else\r
- {\r
- xgi_down(info->fb_sem);\r
- block = xgi_mem_free(info, offset);\r
- xgi_up(info->fb_sem);\r
-\r
- if (block == NULL)\r
- {\r
- XGI_ERROR("xgi_mem_free() failed at base 0x%lx\n", offset);\r
- }\r
-\r
- /* manage mempid */\r
- mempid_list = xgi_mempid_list.next;\r
- while (mempid_list != &xgi_mempid_list)\r
- {\r
- mempid_block = list_entry(mempid_list, struct xgi_mem_pid_s, list);\r
- if (mempid_block->location == LOCAL && mempid_block->bus_addr == bus_addr)\r
- {\r
- mempid_freeblock = mempid_block;\r
- break;\r
- }\r
- mempid_list = mempid_list->next;\r
- }\r
- if (mempid_freeblock)\r
- {\r
- list_del(&mempid_freeblock->list);\r
- XGI_INFO("Memory ProcessID delete one fb block pid:%ld successfully! \n", mempid_freeblock->pid);\r
- kfree(mempid_freeblock);\r
- }\r
- }\r
-}\r
-\r
-int xgi_fb_heap_init(xgi_info_t *info)\r
-{\r
- xgi_mem_block_t *block;\r
-\r
- xgi_fb_heap = kmalloc(sizeof(xgi_mem_heap_t), GFP_KERNEL);\r
- if (!xgi_fb_heap)\r
- {\r
- XGI_ERROR("xgi_fb_heap alloc failed\n");\r
- return 0;\r
- }\r
-\r
- INIT_LIST_HEAD(&xgi_fb_heap->free_list);\r
- INIT_LIST_HEAD(&xgi_fb_heap->used_list);\r
- INIT_LIST_HEAD(&xgi_fb_heap->sort_list);\r
-\r
- xgi_fb_cache_block = kmem_cache_create("xgi_fb_block", sizeof(xgi_mem_block_t),\r
- 0, SLAB_HWCACHE_ALIGN, NULL, NULL);\r
-\r
- if (NULL == xgi_fb_cache_block)\r
- {\r
- XGI_ERROR("Fail to creat xgi_fb_block\n");\r
- goto fail1;\r
- }\r
-\r
- block = (xgi_mem_block_t *)kmem_cache_alloc(xgi_fb_cache_block, GFP_KERNEL);\r
- if (!block)\r
- {\r
- XGI_ERROR("kmem_cache_alloc failed\n");\r
- goto fail2;\r
- }\r
-\r
- block->offset = XGI_FB_HEAP_START;\r
- block->size = info->fb.size - XGI_FB_HEAP_START;\r
-\r
- list_add(&block->list, &xgi_fb_heap->free_list);\r
-\r
- xgi_fb_heap->max_freesize = info->fb.size - XGI_FB_HEAP_START;\r
-\r
- XGI_INFO("fb start offset: 0x%lx, memory size : 0x%lx\n", block->offset, block->size);\r
- XGI_INFO("xgi_fb_heap->max_freesize: 0x%lx \n", xgi_fb_heap->max_freesize);\r
-\r
- return 1;\r
-\r
-fail2:\r
- if (xgi_fb_cache_block)\r
- {\r
- kmem_cache_destroy(xgi_fb_cache_block);\r
- xgi_fb_cache_block = NULL;\r
- }\r
-fail1:\r
- if(xgi_fb_heap)\r
- {\r
- kfree(xgi_fb_heap);\r
- xgi_fb_heap = NULL;\r
- }\r
- return 0;\r
-}\r
-\r
-void xgi_fb_heap_cleanup(xgi_info_t *info)\r
-{\r
- struct list_head *free_list, *temp;\r
- xgi_mem_block_t *block;\r
- int i;\r
-\r
- if (xgi_fb_heap)\r
- {\r
- free_list = &xgi_fb_heap->free_list;\r
- for (i = 0; i < 3; i++, free_list++)\r
- {\r
- temp = free_list->next;\r
- while (temp != free_list)\r
- {\r
- block = list_entry(temp, struct xgi_mem_block_s, list);\r
- temp = temp->next;\r
-\r
- XGI_INFO("No. %d block->offset: 0x%lx block->size: 0x%lx \n",\r
- i, block->offset, block->size);\r
- //XGI_INFO("No. %d free block: 0x%p \n", i, block);\r
- kmem_cache_free(xgi_fb_cache_block, block);\r
- block = NULL;\r
- }\r
- }\r
- XGI_INFO("xgi_fb_heap: 0x%p \n", xgi_fb_heap);\r
- kfree(xgi_fb_heap);\r
- xgi_fb_heap = NULL;\r
- }\r
-\r
- if (xgi_fb_cache_block)\r
- {\r
- kmem_cache_destroy(xgi_fb_cache_block);\r
- xgi_fb_cache_block = NULL;\r
- }\r
-}\r
-\r
-static xgi_mem_block_t * xgi_mem_new_node(void)\r
-{\r
- xgi_mem_block_t *block;\r
-\r
- block = (xgi_mem_block_t *)kmem_cache_alloc(xgi_fb_cache_block, GFP_KERNEL);\r
- if (!block)\r
- {\r
- XGI_ERROR("kmem_cache_alloc failed\n");\r
- return NULL;\r
- }\r
-\r
- return block;\r
-}\r
-\r
-#if 0\r
-static void xgi_mem_insert_node_after(xgi_mem_list_t *list,\r
- xgi_mem_block_t *current,\r
- xgi_mem_block_t *block);\r
-static void xgi_mem_insert_node_before(xgi_mem_list_t *list,\r
- xgi_mem_block_t *current,\r
- xgi_mem_block_t *block);\r
-static void xgi_mem_insert_node_head(xgi_mem_list_t *list,\r
- xgi_mem_block_t *block);\r
-static void xgi_mem_insert_node_tail(xgi_mem_list_t *list,\r
- xgi_mem_block_t *block);\r
-static void xgi_mem_delete_node(xgi_mem_list_t *list,\r
- xgi_mem_block_t *block);\r
-/*\r
- * insert node:block after node:current\r
- */\r
-static void xgi_mem_insert_node_after(xgi_mem_list_t *list,\r
- xgi_mem_block_t *current,\r
- xgi_mem_block_t *block)\r
-{\r
- block->prev = current;\r
- block->next = current->next;\r
- current->next = block;\r
-\r
- if (current == list->tail)\r
- {\r
- list->tail = block;\r
- }\r
- else\r
- {\r
- block->next->prev = block;\r
- }\r
-}\r
-\r
-/*\r
- * insert node:block before node:current\r
- */\r
-static void xgi_mem_insert_node_before(xgi_mem_list_t *list,\r
- xgi_mem_block_t *current,\r
- xgi_mem_block_t *block)\r
-{\r
- block->prev = current->prev;\r
- block->next = current;\r
- current->prev = block;\r
- if (current == list->head)\r
- {\r
- list->head = block;\r
- }\r
- else\r
- {\r
- block->prev->next = block;\r
- }\r
-}\r
-void xgi_mem_insert_node_head(xgi_mem_list_t *list,\r
- xgi_mem_block_t *block)\r
-{\r
- block->next = list->head;\r
- block->prev = NULL;\r
-\r
- if (NULL == list->head)\r
- {\r
- list->tail = block;\r
- }\r
- else\r
- {\r
- list->head->prev = block;\r
- }\r
- list->head = block;\r
-}\r
-\r
-static void xgi_mem_insert_node_tail(xgi_mem_list_t *list,\r
- xgi_mem_block_t *block)\r
-\r
-{\r
- block->next = NULL;\r
- block->prev = list->tail;\r
- if (NULL == list->tail)\r
- {\r
- list->head = block;\r
- }\r
- else\r
- {\r
- list->tail->next = block;\r
- }\r
- list->tail = block;\r
-}\r
-\r
-static void xgi_mem_delete_node(xgi_mem_list_t *list,\r
- xgi_mem_block_t *block)\r
-{\r
- if (block == list->head)\r
- {\r
- list->head = block->next;\r
- }\r
- if (block == list->tail)\r
- {\r
- list->tail = block->prev;\r
- }\r
-\r
- if (block->prev)\r
- {\r
- block->prev->next = block->next;\r
- }\r
- if (block->next)\r
- {\r
- block->next->prev = block->prev;\r
- }\r
-\r
- block->next = block->prev = NULL;\r
-}\r
-#endif\r
-static xgi_mem_block_t *xgi_mem_alloc(xgi_info_t *info, unsigned long originalSize)\r
-{\r
- struct list_head *free_list;\r
- xgi_mem_block_t *block, *free_block, *used_block;\r
-\r
- unsigned long size = (originalSize + PAGE_SIZE - 1) & PAGE_MASK;\r
-\r
- XGI_INFO("Original 0x%lx bytes requested, really 0x%lx allocated\n", originalSize, size);\r
-\r
- if (size == 0)\r
- {\r
- XGI_ERROR("size == 0\n");\r
- return (NULL);\r
- }\r
- XGI_INFO("max_freesize: 0x%lx \n", xgi_fb_heap->max_freesize);\r
- if (size > xgi_fb_heap->max_freesize)\r
- {\r
- XGI_ERROR("size: 0x%lx is bigger than frame buffer total free size: 0x%lx !\n",\r
- size, xgi_fb_heap->max_freesize);\r
- return (NULL);\r
- }\r
-\r
- free_list = xgi_fb_heap->free_list.next;\r
-\r
- while (free_list != &xgi_fb_heap->free_list)\r
- {\r
- XGI_INFO("free_list: 0x%px \n", free_list);\r
- block = list_entry(free_list, struct xgi_mem_block_s, list);\r
- if (size <= block->size)\r
- {\r
- break;\r
- }\r
- free_list = free_list->next;\r
- }\r
-\r
- if (free_list == &xgi_fb_heap->free_list)\r
- {\r
- XGI_ERROR("Can't allocate %ldk size from frame buffer memory !\n", size/1024);\r
- return (NULL);\r
- }\r
-\r
- free_block = block;\r
- XGI_INFO("alloc size: 0x%lx from offset: 0x%lx size: 0x%lx \n",\r
- size, free_block->offset, free_block->size);\r
-\r
- if (size == free_block->size)\r
- {\r
- used_block = free_block;\r
- XGI_INFO("size == free_block->size: free_block = 0x%p\n", free_block);\r
- list_del(&free_block->list);\r
- }\r
- else\r
- {\r
- used_block = xgi_mem_new_node();\r
-\r
- if (used_block == NULL) return (NULL);\r
-\r
- if (used_block == free_block)\r
- {\r
- XGI_ERROR("used_block == free_block = 0x%p\n", used_block);\r
- }\r
-\r
- used_block->offset = free_block->offset;\r
- used_block->size = size;\r
-\r
- free_block->offset += size;\r
- free_block->size -= size;\r
- }\r
-\r
- xgi_fb_heap->max_freesize -= size;\r
-\r
- list_add(&used_block->list, &xgi_fb_heap->used_list);\r
-\r
- return (used_block);\r
-}\r
-\r
-static xgi_mem_block_t *xgi_mem_free(xgi_info_t *info, unsigned long offset)\r
-{\r
- struct list_head *free_list, *used_list;\r
- xgi_mem_block_t *used_block = NULL, *block = NULL;\r
- xgi_mem_block_t *prev, *next;\r
-\r
- unsigned long upper;\r
- unsigned long lower;\r
-\r
- used_list = xgi_fb_heap->used_list.next;\r
- while (used_list != &xgi_fb_heap->used_list)\r
- {\r
- block = list_entry(used_list, struct xgi_mem_block_s, list);\r
- if (block->offset == offset)\r
- {\r
- break;\r
- }\r
- used_list = used_list->next;\r
- }\r
-\r
- if (used_list == &xgi_fb_heap->used_list)\r
- {\r
- XGI_ERROR("can't find block: 0x%lx to free!\n", offset);\r
- return (NULL);\r
- }\r
-\r
- used_block = block;\r
- XGI_INFO("used_block: 0x%p, offset = 0x%lx, size = 0x%lx\n",\r
- used_block, used_block->offset, used_block->size);\r
-\r
- xgi_fb_heap->max_freesize += used_block->size;\r
-\r
- prev = next = NULL;\r
- upper = used_block->offset + used_block->size;\r
- lower = used_block->offset;\r
-\r
- free_list = xgi_fb_heap->free_list.next;\r
- while (free_list != &xgi_fb_heap->free_list)\r
- {\r
- block = list_entry(free_list, struct xgi_mem_block_s, list);\r
-\r
- if (block->offset == upper)\r
- {\r
- next = block;\r
- }\r
- else if ((block->offset + block->size) == lower)\r
- {\r
- prev = block;\r
- }\r
- free_list = free_list->next;\r
- }\r
-\r
- XGI_INFO("next = 0x%p, prev = 0x%p\n", next, prev);\r
- list_del(&used_block->list);\r
-\r
- if (prev && next)\r
- {\r
- prev->size += (used_block->size + next->size);\r
- list_del(&next->list);\r
- XGI_INFO("free node 0x%p\n", next);\r
- kmem_cache_free(xgi_fb_cache_block, next);\r
- kmem_cache_free(xgi_fb_cache_block, used_block);\r
-\r
- next = NULL;\r
- used_block = NULL;\r
- return (prev);\r
- }\r
-\r
- if (prev)\r
- {\r
- prev->size += used_block->size;\r
- XGI_INFO("free node 0x%p\n", used_block);\r
- kmem_cache_free(xgi_fb_cache_block, used_block);\r
- used_block = NULL;\r
- return (prev);\r
- }\r
-\r
- if (next)\r
- {\r
- next->size += used_block->size;\r
- next->offset = used_block->offset;\r
- XGI_INFO("free node 0x%p\n", used_block);\r
- kmem_cache_free(xgi_fb_cache_block, used_block);\r
- used_block = NULL;\r
- return (next);\r
- }\r
-\r
- list_add(&used_block->list, &xgi_fb_heap->free_list);\r
- XGI_INFO("Recycled free node %p, offset = 0x%lx, size = 0x%lx\n",\r
- used_block, used_block->offset, used_block->size);\r
-\r
- return (used_block);\r
-}\r
-\r
+/****************************************************************************
+ * Copyright (C) 2003-2006 by XGI Technology, Taiwan.
+ *
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation on the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, 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:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * XGI 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.
+ ***************************************************************************/
+
+#include "xgi_drv.h"
+
+#define XGI_FB_HEAP_START 0x1000000
+
+int xgi_alloc(struct xgi_info * info, struct xgi_mem_alloc * alloc,
+ struct drm_file * filp)
+{
+ struct drm_memblock_item *block;
+ const char *const mem_name = (alloc->location == XGI_MEMLOC_LOCAL)
+ ? "on-card" : "GART";
+
+
+ if ((alloc->location != XGI_MEMLOC_LOCAL)
+ && (alloc->location != XGI_MEMLOC_NON_LOCAL)) {
+ DRM_ERROR("Invalid memory pool (0x%08x) specified.\n",
+ alloc->location);
+ return -EINVAL;
+ }
+
+ if ((alloc->location == XGI_MEMLOC_LOCAL)
+ ? !info->fb_heap_initialized : !info->pcie_heap_initialized) {
+ DRM_ERROR("Attempt to allocate from uninitialized memory "
+ "pool (0x%08x).\n", alloc->location);
+ return -EINVAL;
+ }
+
+ mutex_lock(&info->dev->struct_mutex);
+ block = drm_sman_alloc(&info->sman, alloc->location, alloc->size,
+ 0, (unsigned long) filp);
+ mutex_unlock(&info->dev->struct_mutex);
+
+ if (block == NULL) {
+ alloc->size = 0;
+ DRM_ERROR("%s memory allocation failed\n", mem_name);
+ return -ENOMEM;
+ } else {
+ alloc->offset = (*block->mm->offset)(block->mm,
+ block->mm_info);
+ alloc->hw_addr = alloc->offset;
+ alloc->index = block->user_hash.key;
+
+ if (block->user_hash.key != (unsigned long) alloc->index) {
+ DRM_ERROR("%s truncated handle %lx for pool %d "
+ "offset %x\n",
+ __func__, block->user_hash.key,
+ alloc->location, alloc->offset);
+ }
+
+ if (alloc->location == XGI_MEMLOC_NON_LOCAL) {
+ alloc->hw_addr += info->pcie.base;
+ }
+
+ DRM_DEBUG("%s memory allocation succeeded: 0x%x\n",
+ mem_name, alloc->offset);
+ }
+
+ return 0;
+}
+
+
+int xgi_alloc_ioctl(struct drm_device * dev, void * data,
+ struct drm_file * filp)
+{
+ struct xgi_info *info = dev->dev_private;
+
+ return xgi_alloc(info, (struct xgi_mem_alloc *) data, filp);
+}
+
+
+int xgi_free(struct xgi_info * info, unsigned long index,
+ struct drm_file * filp)
+{
+ int err;
+
+ mutex_lock(&info->dev->struct_mutex);
+ err = drm_sman_free_key(&info->sman, index);
+ mutex_unlock(&info->dev->struct_mutex);
+
+ return err;
+}
+
+
+int xgi_free_ioctl(struct drm_device * dev, void * data,
+ struct drm_file * filp)
+{
+ struct xgi_info *info = dev->dev_private;
+
+ return xgi_free(info, *(unsigned long *) data, filp);
+}
+
+
+int xgi_fb_heap_init(struct xgi_info * info)
+{
+ int err;
+
+ mutex_lock(&info->dev->struct_mutex);
+ err = drm_sman_set_range(&info->sman, XGI_MEMLOC_LOCAL,
+ XGI_FB_HEAP_START,
+ info->fb.size - XGI_FB_HEAP_START);
+ mutex_unlock(&info->dev->struct_mutex);
+
+ info->fb_heap_initialized = (err == 0);
+ return err;
+}