/* * Copyright (c) 2007, Intel Corporation. * All Rights Reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * * Authors: Thomas Hellstrom */ #include #include "psb_drv.h" #include "psb_pvr_glue.h" static inline uint32_t psb_gtt_mask_pte(uint32_t pfn, int type) { uint32_t mask = PSB_PTE_VALID; if (type & PSB_MMU_CACHED_MEMORY) mask |= PSB_PTE_CACHED; if (type & PSB_MMU_RO_MEMORY) mask |= PSB_PTE_RO; if (type & PSB_MMU_WO_MEMORY) mask |= PSB_PTE_WO; return (pfn << PAGE_SHIFT) | mask; } struct psb_gtt *psb_gtt_alloc(struct drm_device *dev) { struct psb_gtt *tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); if (!tmp) return NULL; init_rwsem(&tmp->sem); tmp->dev = dev; return tmp; } void psb_gtt_takedown(struct psb_gtt *pg, int free) { struct drm_psb_private *dev_priv = pg->dev->dev_private; if (!pg) return; if (pg->gtt_map) { iounmap(pg->gtt_map); pg->gtt_map = NULL; } if (pg->initialized) { pci_write_config_word(pg->dev->pdev, PSB_GMCH_CTRL, pg->gmch_ctrl); PSB_WVDC32(pg->pge_ctl, PSB_PGETBL_CTL); (void) PSB_RVDC32(PSB_PGETBL_CTL); } if (free) kfree(pg); } int psb_gtt_init(struct psb_gtt *pg, int resume) { struct drm_device *dev = pg->dev; struct drm_psb_private *dev_priv = dev->dev_private; unsigned gtt_pages; unsigned long stolen_size, vram_stolen_size, ci_stolen_size; unsigned long rar_stolen_size; unsigned i, num_pages; unsigned pfn_base; uint32_t ci_pages, vram_pages; uint32_t tt_pages; uint32_t *ttm_gtt_map; uint32_t dvmt_mode = 0; int ret = 0; uint32_t pte; pci_read_config_word(dev->pdev, PSB_GMCH_CTRL, &pg->gmch_ctrl); pci_write_config_word(dev->pdev, PSB_GMCH_CTRL, pg->gmch_ctrl | _PSB_GMCH_ENABLED); pg->pge_ctl = PSB_RVDC32(PSB_PGETBL_CTL); PSB_WVDC32(pg->pge_ctl | _PSB_PGETBL_ENABLED, PSB_PGETBL_CTL); (void) PSB_RVDC32(PSB_PGETBL_CTL); pg->initialized = 1; pg->gtt_phys_start = pg->pge_ctl & PAGE_MASK; pg->gatt_start = pci_resource_start(dev->pdev, PSB_GATT_RESOURCE); /* fix me: video mmu has hw bug to access 0x0D0000000, * then make gatt start at 0x0e000,0000 */ pg->mmu_gatt_start = PSB_MEM_TT_START; pg->gtt_start = pci_resource_start(dev->pdev, PSB_GTT_RESOURCE); gtt_pages = pci_resource_len(dev->pdev, PSB_GTT_RESOURCE) >> PAGE_SHIFT; pg->gatt_pages = pci_resource_len(dev->pdev, PSB_GATT_RESOURCE) >> PAGE_SHIFT; pci_read_config_dword(dev->pdev, PSB_BSM, &pg->stolen_base); vram_stolen_size = pg->gtt_phys_start - pg->stolen_base - PAGE_SIZE; /* CI is not included in the stolen size since the TOPAZ MMU bug */ ci_stolen_size = dev_priv->ci_region_size; /* Don't add CI & RAR share buffer space * managed by TTM to stolen_size */ stolen_size = vram_stolen_size; rar_stolen_size = dev_priv->imr_region_size; printk(KERN_INFO"GMMADR(region 0) start: 0x%08x (%dM).\n", pg->gatt_start, pg->gatt_pages / 256); printk(KERN_INFO"GTTADR(region 3) start: 0x%08x (can map %dM RAM), and actual RAM base 0x%08x.\n", pg->gtt_start, gtt_pages * 4, pg->gtt_phys_start); printk(KERN_INFO"Stole memory information \n"); printk(KERN_INFO" base in RAM: 0x%x \n", pg->stolen_base); printk(KERN_INFO" size: %luK, calculated by (GTT RAM base) - (Stolen base), seems wrong\n", vram_stolen_size / 1024); dvmt_mode = (pg->gmch_ctrl >> 4) & 0x7; printk(KERN_INFO" the correct size should be: %dM(dvmt mode=%d) \n", (dvmt_mode == 1) ? 1 : (2 << (dvmt_mode - 1)), dvmt_mode); if (ci_stolen_size > 0) printk(KERN_INFO"CI Stole memory: RAM base = 0x%08x, size = %lu M \n", dev_priv->ci_region_start, ci_stolen_size / 1024 / 1024); if (rar_stolen_size > 0) printk(KERN_INFO"RAR Stole memory: RAM base = 0x%08x, size = %lu M \n", dev_priv->imr_region_start, rar_stolen_size / 1024 / 1024); if (resume && (gtt_pages != pg->gtt_pages) && (stolen_size != pg->stolen_size)) { DRM_ERROR("GTT resume error.\n"); ret = -EINVAL; goto out_err; } pg->gtt_pages = gtt_pages; pg->stolen_size = stolen_size; pg->vram_stolen_size = vram_stolen_size; pg->ci_stolen_size = ci_stolen_size; pg->rar_stolen_size = rar_stolen_size; pg->gtt_map = ioremap_nocache(pg->gtt_phys_start, gtt_pages << PAGE_SHIFT); if (!pg->gtt_map) { DRM_ERROR("Failure to map gtt.\n"); ret = -ENOMEM; goto out_err; } pg->vram_addr = ioremap_wc(pg->stolen_base, stolen_size); if (!pg->vram_addr) { DRM_ERROR("Failure to map stolen base.\n"); ret = -ENOMEM; goto out_err; } tt_pages = (pg->gatt_pages < PSB_TT_PRIV0_PLIMIT) ? (pg->gatt_pages) : PSB_TT_PRIV0_PLIMIT; ttm_gtt_map = pg->gtt_map + tt_pages / 2; /* * insert vram stolen pages. */ pfn_base = pg->stolen_base >> PAGE_SHIFT; vram_pages = num_pages = vram_stolen_size >> PAGE_SHIFT; printk(KERN_INFO"Set up %d stolen pages starting at 0x%08x, GTT offset %dK\n", num_pages, pfn_base, 0); for (i = 0; i < num_pages; ++i) { pte = psb_gtt_mask_pte(pfn_base + i, 0); iowrite32(pte, pg->gtt_map + i); } /* * Init rest of gtt managed by IMG. */ pfn_base = page_to_pfn(dev_priv->scratch_page); pte = psb_gtt_mask_pte(pfn_base, 0); for (; i < tt_pages / 2; ++i) iowrite32(pte, pg->gtt_map + i); /* * insert CI stolen pages */ pfn_base = dev_priv->ci_region_start >> PAGE_SHIFT; ci_pages = num_pages = ci_stolen_size >> PAGE_SHIFT; printk(KERN_INFO"Set up %d CI stolen pages starting at 0x%08x, GTT offset %dK\n", num_pages, pfn_base, (ttm_gtt_map - pg->gtt_map) * 4); for (i = 0; i < num_pages; ++i) { pte = psb_gtt_mask_pte(pfn_base + i, 0); iowrite32(pte, ttm_gtt_map + i); } /* * Init rest of gtt managed by TTM. */ pfn_base = page_to_pfn(dev_priv->scratch_page); pte = psb_gtt_mask_pte(pfn_base, 0); PSB_DEBUG_INIT("Initializing the rest of a total " "of %d gtt pages.\n", pg->gatt_pages); for (; i < pg->gatt_pages - tt_pages / 2; ++i) iowrite32(pte, ttm_gtt_map + i); (void) ioread32(pg->gtt_map + i - 1); return 0; out_err: psb_gtt_takedown(pg, 0); return ret; } int psb_gtt_insert_pages(struct psb_gtt *pg, struct page **pages, unsigned offset_pages, unsigned num_pages, unsigned desired_tile_stride, unsigned hw_tile_stride, int type) { unsigned rows = 1; unsigned add; unsigned row_add; unsigned i; unsigned j; uint32_t *cur_page = NULL; uint32_t pte; if (hw_tile_stride) rows = num_pages / desired_tile_stride; else desired_tile_stride = num_pages; add = desired_tile_stride; row_add = hw_tile_stride; down_read(&pg->sem); for (i = 0; i < rows; ++i) { cur_page = pg->gtt_map + offset_pages; for (j = 0; j < desired_tile_stride; ++j) { pte = psb_gtt_mask_pte(page_to_pfn(*pages++), type); iowrite32(pte, cur_page++); } offset_pages += add; } (void) ioread32(cur_page - 1); up_read(&pg->sem); return 0; } static int psb_gtt_insert_pfn_list(struct psb_gtt *pg, u32 *pfn_list, unsigned offset_pages, unsigned num_pages, unsigned desired_tile_stride, unsigned hw_tile_stride, int type) { unsigned rows = 1; unsigned add; unsigned row_add; unsigned i; unsigned j; uint32_t *cur_page = NULL; uint32_t pte; if (!pg || !pfn_list) return -EINVAL; if (hw_tile_stride) rows = num_pages / desired_tile_stride; else desired_tile_stride = num_pages; add = desired_tile_stride; row_add = hw_tile_stride; down_read(&pg->sem); for (i = 0; i < rows; ++i) { cur_page = pg->gtt_map + offset_pages; for (j = 0; j < desired_tile_stride; ++j) { pte = psb_gtt_mask_pte(*pfn_list++, type); iowrite32(pte, cur_page++); } offset_pages += add; } (void) ioread32(cur_page - 1); up_read(&pg->sem); return 0; } int psb_gtt_insert_phys_addresses(struct psb_gtt *pg, IMG_CPU_PHYADDR *pPhysFrames, unsigned offset_pages, unsigned num_pages, int type) { unsigned j; uint32_t *cur_page = NULL; uint32_t pte; /* printk("Allocatng IMG GTT mem at %x (pages %d)\n",offset_pages,num_pages); */ down_read(&pg->sem); cur_page = pg->gtt_map + offset_pages; for (j = 0; j < num_pages; ++j) { pte = psb_gtt_mask_pte((pPhysFrames++)->uiAddr >> PAGE_SHIFT, type); iowrite32(pte, cur_page++); /* printk("PTE %d: %x/%x\n",j,(pPhysFrames-1)->uiAddr,pte); */ } (void) ioread32(cur_page - 1); up_read(&pg->sem); return 0; } int psb_gtt_remove_pages(struct psb_gtt *pg, unsigned offset_pages, unsigned num_pages, unsigned desired_tile_stride, unsigned hw_tile_stride, int rc_prot) { struct drm_psb_private *dev_priv = pg->dev->dev_private; unsigned rows = 1; unsigned add; unsigned row_add; unsigned i; unsigned j; uint32_t *cur_page = NULL; unsigned pfn_base = page_to_pfn(dev_priv->scratch_page); uint32_t pte = psb_gtt_mask_pte(pfn_base, 0); if (hw_tile_stride) rows = num_pages / desired_tile_stride; else desired_tile_stride = num_pages; add = desired_tile_stride; row_add = hw_tile_stride; if (rc_prot) down_read(&pg->sem); for (i = 0; i < rows; ++i) { cur_page = pg->gtt_map + offset_pages; for (j = 0; j < desired_tile_stride; ++j) iowrite32(pte, cur_page++); offset_pages += add; } (void) ioread32(cur_page - 1); if (rc_prot) up_read(&pg->sem); return 0; } int psb_gtt_mm_init(struct psb_gtt *pg) { struct psb_gtt_mm *gtt_mm; struct drm_psb_private *dev_priv = pg->dev->dev_private; struct drm_open_hash *ht; struct drm_mm *mm; int ret; uint32_t tt_start; uint32_t tt_size; if (!pg || !pg->initialized) { DRM_DEBUG("Invalid gtt struct\n"); return -EINVAL; } gtt_mm = kzalloc(sizeof(struct psb_gtt_mm), GFP_KERNEL); if (!gtt_mm) return -ENOMEM; spin_lock_init(>t_mm->lock); ht = >t_mm->hash; ret = drm_ht_create(ht, 20); if (ret) { DRM_DEBUG("Create hash table failed(%d)\n", ret); goto err_free; } tt_start = (pg->stolen_size + PAGE_SIZE - 1) >> PAGE_SHIFT; tt_start = (tt_start < pg->gatt_pages) ? tt_start : pg->gatt_pages; tt_size = (pg->gatt_pages < PSB_TT_PRIV0_PLIMIT) ? (pg->gatt_pages) : PSB_TT_PRIV0_PLIMIT; mm = >t_mm->base; /*will use tt_start ~ 128M for IMG TT buffers*/ drm_mm_init(mm, tt_start, ((tt_size / 2) - tt_start)); gtt_mm->count = 0; dev_priv->gtt_mm = gtt_mm; DRM_INFO("PSB GTT mem manager ready, tt_start %ld, tt_size %ld pages\n", (unsigned long)tt_start, (unsigned long)((tt_size / 2) - tt_start)); return 0; err_mm_init: drm_ht_remove(ht); err_free: kfree(gtt_mm); return ret; } /** * Delete all hash entries; */ void psb_gtt_mm_takedown(void) { return; } static int psb_gtt_mm_get_ht_by_pid_locked(struct psb_gtt_mm *mm, u32 tgid, struct psb_gtt_hash_entry **hentry) { struct drm_hash_item *entry; struct psb_gtt_hash_entry *psb_entry; int ret; ret = drm_ht_find_item(&mm->hash, tgid, &entry); if (ret) { DRM_DEBUG("Cannot find entry pid=%d\n", tgid); return ret; } psb_entry = container_of(entry, struct psb_gtt_hash_entry, item); if (!psb_entry) { DRM_DEBUG("Invalid entry"); return -EINVAL; } *hentry = psb_entry; return 0; } static int psb_gtt_mm_insert_ht_locked(struct psb_gtt_mm *mm, u32 tgid, struct psb_gtt_hash_entry *hentry) { struct drm_hash_item *item; int ret; if (!hentry) { DRM_DEBUG("Invalid parameters\n"); return -EINVAL; } item = &hentry->item; item->key = tgid; /** * NOTE: drm_ht_insert_item will perform such a check ret = psb_gtt_mm_get_ht_by_pid(mm, tgid, &tmp); if (!ret) { DRM_DEBUG("Entry already exists for pid %ld\n", tgid); return -EAGAIN; } */ /*Insert the given entry*/ ret = drm_ht_insert_item(&mm->hash, item); if (ret) { DRM_DEBUG("Insert failure\n"); return ret; } mm->count++; return 0; } static int psb_gtt_mm_alloc_insert_ht(struct psb_gtt_mm *mm, u32 tgid, struct psb_gtt_hash_entry **entry) { struct psb_gtt_hash_entry *hentry; int ret; /*if the hentry for this tgid exists, just get it and return*/ spin_lock(&mm->lock); ret = psb_gtt_mm_get_ht_by_pid_locked(mm, tgid, &hentry); if (!ret) { DRM_DEBUG("Entry for tgid %d exists, hentry %p\n", tgid, hentry); *entry = hentry; spin_unlock(&mm->lock); return 0; } spin_unlock(&mm->lock); DRM_DEBUG("Entry for tgid %d doesn't exist, will create it\n", tgid); hentry = kzalloc(sizeof(struct psb_gtt_hash_entry), GFP_KERNEL); if (!hentry) { DRM_DEBUG("Kmalloc failed\n"); return -ENOMEM; } ret = drm_ht_create(&hentry->ht, 20); if (ret) { DRM_DEBUG("Create hash table failed\n"); goto failed_drm_ht_create; } spin_lock(&mm->lock); ret = psb_gtt_mm_insert_ht_locked(mm, tgid, hentry); spin_unlock(&mm->lock); if (ret) { DRM_DEBUG("Insert hash table entry failed\n"); goto failed_psb_gtt_mm_insert_ht_locked; } *entry = hentry; return ret; failed_psb_gtt_mm_insert_ht_locked: drm_ht_remove(&hentry->ht); failed_drm_ht_create: kfree(hentry); return ret; } static struct psb_gtt_hash_entry * psb_gtt_mm_remove_ht_locked(struct psb_gtt_mm *mm, u32 tgid) { struct psb_gtt_hash_entry *tmp; int ret; ret = psb_gtt_mm_get_ht_by_pid_locked(mm, tgid, &tmp); if (ret) { DRM_DEBUG("Cannot find entry pid %d\n", tgid); return NULL; } /*remove it from ht*/ drm_ht_remove_item(&mm->hash, &tmp->item); mm->count--; return tmp; } static int psb_gtt_mm_remove_free_ht_locked(struct psb_gtt_mm *mm, u32 tgid) { struct psb_gtt_hash_entry *entry; entry = psb_gtt_mm_remove_ht_locked(mm, tgid); if (!entry) { DRM_DEBUG("Invalid entry"); return -EINVAL; } /*delete ht*/ drm_ht_remove(&entry->ht); /*free this entry*/ kfree(entry); return 0; } static int psb_gtt_mm_get_mem_mapping_locked(struct drm_open_hash *ht, u32 key, struct psb_gtt_mem_mapping **hentry) { struct drm_hash_item *entry; struct psb_gtt_mem_mapping *mapping; int ret; ret = drm_ht_find_item(ht, key, &entry); if (ret) { DRM_DEBUG("Cannot find key %d\n", key); return ret; } mapping = container_of(entry, struct psb_gtt_mem_mapping, item); if (!mapping) { DRM_DEBUG("Invalid entry\n"); return -EINVAL; } *hentry = mapping; return 0; } static int psb_gtt_mm_get_mem_mapping_anyused_locked(struct drm_open_hash *ht, struct psb_gtt_mem_mapping **hentry) { struct drm_hash_item *entry; struct psb_gtt_mem_mapping *mapping; int ret; ret = drm_ht_find_item_anyused(ht, &entry); if (ret) { DRM_DEBUG("Cannot find\n"); return ret; } mapping = container_of(entry, struct psb_gtt_mem_mapping, item); if (!mapping) { DRM_DEBUG("Invalid entry\n"); return -EINVAL; } *hentry = mapping; return 0; } static int psb_gtt_mm_insert_mem_mapping_locked(struct drm_open_hash *ht, u32 key, struct psb_gtt_mem_mapping *hentry) { struct drm_hash_item *item; struct psb_gtt_hash_entry *entry; int ret; if (!ht || !hentry) { DRM_DEBUG("hentry is NULL\n"); return -EINVAL; } item = &hentry->item; item->key = key; ret = drm_ht_insert_item(ht, item); if (ret) { DRM_DEBUG("insert_item failed\n"); return ret; } entry = container_of(ht, struct psb_gtt_hash_entry, ht); if (entry) entry->count++; return 0; } static int psb_gtt_mm_alloc_insert_mem_mapping(struct psb_gtt_mm *mm, struct drm_open_hash *ht, u32 key, struct drm_mm_node *node, struct psb_gtt_mem_mapping **entry) { struct psb_gtt_mem_mapping *mapping; int ret; if (!node || !ht) { DRM_DEBUG("parameter error\n"); return -EINVAL; } /*try to get this mem_map */ spin_lock(&mm->lock); ret = psb_gtt_mm_get_mem_mapping_locked(ht, key, &mapping); if (!ret) { DRM_DEBUG("mapping entry for key %d exists, entry %p\n", key, mapping); *entry = mapping; spin_unlock(&mm->lock); return 0; } spin_unlock(&mm->lock); DRM_DEBUG("Mapping entry for key %d doesn't exist, will create it\n", key); mapping = kzalloc(sizeof(struct psb_gtt_mem_mapping), GFP_KERNEL); if (!mapping) { DRM_DEBUG("kmalloc failed\n"); return -ENOMEM; } mapping->node = node; spin_lock(&mm->lock); ret = psb_gtt_mm_insert_mem_mapping_locked(ht, key, mapping); spin_unlock(&mm->lock); if (!ret) *entry = mapping; return ret; } static struct psb_gtt_mem_mapping * psb_gtt_mm_remove_mem_mapping_locked(struct drm_open_hash *ht, u32 key) { struct psb_gtt_mem_mapping *tmp; struct psb_gtt_hash_entry *entry; int ret; if (!ht) { DRM_DEBUG("hash table is NULL\n"); return NULL; } ret = psb_gtt_mm_get_mem_mapping_locked(ht, key, &tmp); if (ret) { DRM_DEBUG("Cannot find key %d\n", key); return NULL; } drm_ht_remove_item(ht, &tmp->item); entry = container_of(ht, struct psb_gtt_hash_entry, ht); if (entry) entry->count--; return tmp; } static struct psb_gtt_mem_mapping * psb_gtt_mm_remove_mem_mapping_anyused_locked(struct drm_open_hash *ht) { struct psb_gtt_mem_mapping *tmp; struct psb_gtt_hash_entry *entry; int ret; if (!ht) { DRM_DEBUG("hash table is NULL\n"); return NULL; } ret = psb_gtt_mm_get_mem_mapping_anyused_locked(ht, &tmp); if (ret) { DRM_DEBUG("Cannot find any used\n"); return NULL; } drm_ht_remove_item(ht, &tmp->item); entry = container_of(ht, struct psb_gtt_hash_entry, ht); if (entry) entry->count--; return tmp; } static int psb_gtt_mm_remove_free_mem_mapping_locked(struct drm_open_hash *ht, u32 key, struct drm_mm_node **node) { struct psb_gtt_mem_mapping *entry; entry = psb_gtt_mm_remove_mem_mapping_locked(ht, key); if (!entry) { DRM_DEBUG("entry is NULL\n"); return -EINVAL; } *node = entry->node; kfree(entry); return 0; } static int psb_gtt_mm_remove_free_mem_mapping_anyused_locked (struct drm_open_hash *ht, struct drm_mm_node **node) { struct psb_gtt_mem_mapping *entry; entry = psb_gtt_mm_remove_mem_mapping_anyused_locked(ht); if (!entry) { DRM_DEBUG("entry is NULL\n"); return -EINVAL; } *node = entry->node; kfree(entry); return 0; } static int psb_gtt_add_node(struct psb_gtt_mm *mm, u32 tgid, u32 key, struct drm_mm_node *node, struct psb_gtt_mem_mapping **entry) { struct psb_gtt_hash_entry *hentry; struct psb_gtt_mem_mapping *mapping; int ret; ret = psb_gtt_mm_alloc_insert_ht(mm, tgid, &hentry); if (ret) { DRM_DEBUG("alloc_insert failed\n"); return ret; } ret = psb_gtt_mm_alloc_insert_mem_mapping(mm, &hentry->ht, key, node, &mapping); if (ret) { DRM_DEBUG("mapping alloc_insert failed\n"); return ret; } *entry = mapping; return 0; } static int psb_gtt_remove_node(struct psb_gtt_mm *mm, u32 tgid, u32 key, struct drm_mm_node **node) { struct psb_gtt_hash_entry *hentry; struct drm_mm_node *tmp; int ret; spin_lock(&mm->lock); ret = psb_gtt_mm_get_ht_by_pid_locked(mm, tgid, &hentry); if (ret) { DRM_DEBUG("Cannot find entry for pid %d\n", tgid); spin_unlock(&mm->lock); return ret; } spin_unlock(&mm->lock); /*remove mapping entry*/ spin_lock(&mm->lock); ret = psb_gtt_mm_remove_free_mem_mapping_locked(&hentry->ht, key, &tmp); if (ret) { DRM_DEBUG("remove_free failed\n"); spin_unlock(&mm->lock); return ret; } *node = tmp; /*check the count of mapping entry*/ if (!hentry->count) { DRM_DEBUG("count of mapping entry is zero, tgid=%d\n", tgid); psb_gtt_mm_remove_free_ht_locked(mm, tgid); } spin_unlock(&mm->lock); return 0; } static int psb_gtt_remove_node_anyused(struct psb_gtt_mm *mm, u32 tgid, struct drm_mm_node **node) { struct psb_gtt_hash_entry *hentry; struct drm_mm_node *tmp; int ret; spin_lock(&mm->lock); ret = psb_gtt_mm_get_ht_by_pid_locked(mm, tgid, &hentry); if (ret) { spin_unlock(&mm->lock); return ret; } spin_unlock(&mm->lock); /*remove mapping entry*/ spin_lock(&mm->lock); ret = psb_gtt_mm_remove_free_mem_mapping_anyused_locked(&hentry->ht, &tmp); if (ret) { DRM_DEBUG("remove_free failed\n"); spin_unlock(&mm->lock); return ret; } *node = tmp; /*check the count of mapping entry*/ if (!hentry->count) { DRM_DEBUG("count of mapping entry is zero, tgid=%d\n", tgid); psb_gtt_mm_remove_free_ht_locked(mm, tgid); } spin_unlock(&mm->lock); return 0; } static int psb_gtt_mm_alloc_mem(struct psb_gtt_mm *mm, uint32_t pages, uint32_t align, struct drm_mm_node **node) { struct drm_mm_node *tmp_node; int ret; do { ret = drm_mm_pre_get(&mm->base); if (unlikely(ret)) { DRM_DEBUG("drm_mm_pre_get error\n"); return ret; } spin_lock(&mm->lock); tmp_node = drm_mm_search_free(&mm->base, pages, align, 1); if (unlikely(!tmp_node)) { DRM_DEBUG("No free node found\n"); spin_unlock(&mm->lock); break; } tmp_node = drm_mm_get_block_atomic(tmp_node, pages, align); spin_unlock(&mm->lock); } while (!tmp_node); if (!tmp_node) { DRM_DEBUG("Node allocation failed\n"); return -ENOMEM; } *node = tmp_node; return 0; } static void psb_gtt_mm_free_mem(struct psb_gtt_mm *mm, struct drm_mm_node *node) { spin_lock(&mm->lock); drm_mm_put_block(node); spin_unlock(&mm->lock); } int psb_gtt_map_meminfo(struct drm_device *dev, IMG_HANDLE hKernelMemInfo, uint32_t page_align, uint32_t *offset) { struct drm_psb_private *dev_priv = (struct drm_psb_private *)dev->dev_private; PVRSRV_KERNEL_MEM_INFO *psKernelMemInfo; struct psb_gtt_mm *mm = dev_priv->gtt_mm; struct psb_gtt *pg = dev_priv->pg; uint32_t size, pages, offset_pages; void *kmem; struct drm_mm_node *node; u32 *pfn_list = 0; struct psb_gtt_mem_mapping *mapping = NULL; int ret; ret = psb_get_meminfo_by_handle(dev_priv, hKernelMemInfo, &psKernelMemInfo); if (ret) { DRM_DEBUG("Cannot find kernelMemInfo handle %d\n", (int)hKernelMemInfo); return -EINVAL; } DRM_DEBUG("Got psKernelMemInfo %p for handle %x\n", psKernelMemInfo, (u32)hKernelMemInfo); size = psKernelMemInfo->uAllocSize; kmem = psKernelMemInfo->pvLinAddrKM; pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; DRM_DEBUG("KerMemInfo size %d, cpuVadr %p, pages %d, osMemHdl %p\n", size, kmem, pages, psKernelMemInfo->sMemBlk.hOSMemHandle); if (!kmem) DRM_DEBUG("kmem is NULL"); /*get pages*/ ret = psb_get_pages_by_mem_handle(dev_priv, psKernelMemInfo->sMemBlk.hOSMemHandle, &pfn_list, pages); if (ret) { DRM_DEBUG("get pages error\n"); return ret; } DRM_DEBUG("get %d pages\n", pages); /*alloc memory in TT apeture*/ ret = psb_gtt_mm_alloc_mem(mm, pages, page_align, &node); if (ret) { DRM_DEBUG("alloc TT memory error\n"); goto failed_pages_alloc; } /*update psb_gtt_mm*/ ret = psb_gtt_add_node(mm, (u32)psb_get_tgid(), (u32)hKernelMemInfo, node, &mapping); if (ret) { DRM_DEBUG("add_node failed"); goto failed_add_node; } node = mapping->node; offset_pages = node->start; DRM_DEBUG("get free node for %d pages, offset %d pages", pages, offset_pages); /*update gtt*/ psb_gtt_insert_pfn_list(pg, pfn_list, (unsigned)offset_pages, (unsigned)pages, 0, 0, 0); /*free pfn_list if allocated*/ kfree(pfn_list); *offset = offset_pages; return 0; failed_add_node: psb_gtt_mm_free_mem(mm, node); failed_pages_alloc: kfree(pfn_list); return ret; } static int psb_gtt_unmap_common(struct drm_device *dev, unsigned int ui32TaskId, unsigned int hHandle) { struct drm_psb_private *dev_priv = (struct drm_psb_private *)dev->dev_private; struct psb_gtt_mm *mm = dev_priv->gtt_mm; struct psb_gtt *pg = dev_priv->pg; uint32_t pages, offset_pages; struct drm_mm_node *node; int ret; ret = psb_gtt_remove_node(mm, (u32)ui32TaskId, (u32)hHandle, &node); if (ret) { DRM_DEBUG("remove node failed\n"); return ret; } /*remove gtt entries*/ offset_pages = node->start; pages = node->size; psb_gtt_remove_pages(pg, offset_pages, pages, 0, 0, 1); /*free tt node*/ psb_gtt_mm_free_mem(mm, node); return 0; } static int psb_gtt_unmap_anyused(struct drm_device *dev, unsigned int ui32TaskId) { struct drm_psb_private *dev_priv = (struct drm_psb_private *)dev->dev_private; struct psb_gtt_mm *mm = dev_priv->gtt_mm; struct psb_gtt *pg = dev_priv->pg; uint32_t pages, offset_pages; struct drm_mm_node *node; int ret; ret = psb_gtt_remove_node_anyused(mm, (u32)ui32TaskId, &node); if (ret) { DRM_DEBUG("remove node failed\n"); return ret; } /*remove gtt entries*/ offset_pages = node->start; pages = node->size; psb_gtt_remove_pages(pg, offset_pages, pages, 0, 0, 1); /*free tt node*/ psb_gtt_mm_free_mem(mm, node); return 0; } int psb_gtt_unmap_meminfo(struct drm_device *dev, IMG_HANDLE hKernelMemInfo) { return psb_gtt_unmap_common(dev, psb_get_tgid(), (unsigned long)hKernelMemInfo); } int psb_gtt_map_vaddr(struct drm_device *dev, unsigned long vaddr, uint32_t size, uint32_t page_align, uint32_t *offset) { struct drm_psb_private *dev_priv = (struct drm_psb_private *)dev->dev_private; struct psb_gtt_mm *mm = dev_priv->gtt_mm; struct psb_gtt *pg = dev_priv->pg; uint32_t pages, offset_pages; struct drm_mm_node *node; u32 *pfn_list = 0; struct psb_gtt_mem_mapping *mapping = NULL; int ret; /*get pages*/ ret = psb_get_vaddr_pages(vaddr, size, &pfn_list, &pages); if (ret) { DRM_DEBUG("get pages error\n"); return ret; } DRM_DEBUG("get %d pages\n", pages); /*alloc memory in TT apeture*/ ret = psb_gtt_mm_alloc_mem(mm, pages, page_align, &node); if (ret) { DRM_DEBUG("alloc TT memory error\n"); goto failed_pages_alloc; } /*update psb_gtt_mm*/ ret = psb_gtt_add_node(mm, (u32)psb_get_tgid(), vaddr, node, &mapping); if (ret) { DRM_DEBUG("add_node failed"); goto failed_add_node; } node = mapping->node; offset_pages = node->start; DRM_DEBUG("get free node for %d pages, offset %d pages", pages, offset_pages); /*update gtt*/ psb_gtt_insert_pfn_list(pg, pfn_list, (unsigned)offset_pages, (unsigned)pages, 0, 0, 0); /*free pfn_list if allocated*/ kfree(pfn_list); *offset = offset_pages; return 0; failed_add_node: psb_gtt_mm_free_mem(mm, node); failed_pages_alloc: kfree(pfn_list); return ret; } static int psb_gtt_unmap_vaddr(struct drm_device *dev, uint32_t vaddr, uint32_t size) { return psb_gtt_unmap_common(dev, psb_get_tgid(), vaddr); } int psb_gtt_map_meminfo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct psb_gtt_mapping_arg *arg = (struct psb_gtt_mapping_arg *)data; uint32_t *offset_pages = &arg->offset_pages; uint32_t page_align = arg->page_align; uint32_t device_id = arg->bcd_device_id; uint32_t buffer_id = arg->bcd_buffer_id; uint32_t *buffer_count = &arg->bcd_buffer_count; uint32_t *buffer_stride = &arg->bcd_buffer_stride; unsigned long vaddr = arg->vaddr; uint32_t size = arg->size; uint32_t type = arg->type; DRM_DEBUG("\n"); switch (type) { case PSB_GTT_MAP_TYPE_MEMINFO: return psb_gtt_map_meminfo(dev, arg->hKernelMemInfo, page_align, offset_pages); case PSB_GTT_MAP_TYPE_VIRTUAL: return psb_gtt_map_vaddr(dev, vaddr, size, page_align, offset_pages); default: DRM_ERROR("unsupported buffer type %d\n", type); return -EINVAL; } } int psb_gtt_unmap_meminfo_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct psb_gtt_mapping_arg *arg = (struct psb_gtt_mapping_arg *)data; uint32_t device_id = arg->bcd_device_id; uint32_t buffer_id = arg->bcd_buffer_id; uint32_t vaddr = arg->vaddr; uint32_t size = arg->size; uint32_t type = arg->type; DRM_DEBUG("\n"); switch (type) { case PSB_GTT_MAP_TYPE_MEMINFO: return psb_gtt_unmap_meminfo(dev, arg->hKernelMemInfo); case PSB_GTT_MAP_TYPE_VIRTUAL: return psb_gtt_unmap_vaddr(dev, vaddr, size); default: DRM_ERROR("unsupported buffer type %d\n", type); return -EINVAL; } } int psb_gtt_map_pvr_memory(struct drm_device *dev, unsigned int hHandle, unsigned int ui32TaskId, IMG_CPU_PHYADDR *pPages, unsigned int ui32PagesNum, unsigned int *ui32Offset) { struct drm_psb_private *dev_priv = (struct drm_psb_private *)dev->dev_private; struct psb_gtt_mm *mm = dev_priv->gtt_mm; struct psb_gtt *pg = dev_priv->pg; uint32_t size, pages, offset_pages; struct drm_mm_node *node = NULL; struct psb_gtt_mem_mapping *mapping = NULL; int ret; size = ui32PagesNum * PAGE_SIZE; pages = 0; /*alloc memory in TT apeture*/ ret = psb_gtt_mm_alloc_mem(mm, ui32PagesNum, 0, &node); if (ret) { DRM_DEBUG("alloc TT memory error\n"); goto failed_pages_alloc; } /*update psb_gtt_mm*/ ret = psb_gtt_add_node(mm, (u32)ui32TaskId, (u32)hHandle, node, &mapping); if (ret) { DRM_DEBUG("add_node failed"); goto failed_add_node; } node = mapping->node; offset_pages = node->start; DRM_DEBUG("get free node for %d pages, offset %d pages", pages, offset_pages); /*update gtt*/ psb_gtt_insert_phys_addresses(pg, pPages, (unsigned)offset_pages, (unsigned)ui32PagesNum, 0); *ui32Offset = offset_pages; return 0; failed_add_node: psb_gtt_mm_free_mem(mm, node); failed_pages_alloc: return ret; } int psb_gtt_unmap_pvr_memory(struct drm_device *dev, unsigned int hHandle, unsigned int ui32TaskId) { return psb_gtt_unmap_common(dev, ui32TaskId, hHandle); } int psb_gtt_free_ht_for_tgid(struct drm_device *dev, unsigned int ui32TaskId) { while (!psb_gtt_unmap_anyused(dev, ui32TaskId)) ; return 0; }