| /************************************************************************** |
| * Copyright (c) 2007, Intel Corporation. |
| * |
| * 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 <thomas-at-tungstengraphics.com> |
| */ |
| #include "ttm/ttm_placement.h" |
| #include "ttm/ttm_execbuf_util.h" |
| #include "psb_ttm_fence_api.h" |
| #include <drm/drmP.h> |
| #include "psb_drv.h" |
| |
| #define DRM_MEM_TTM 26 |
| |
| struct drm_psb_ttm_backend { |
| struct ttm_backend base; |
| struct page **pages; |
| unsigned int desired_tile_stride; |
| unsigned int hw_tile_stride; |
| int mem_type; |
| unsigned long offset; |
| unsigned long num_pages; |
| }; |
| |
| /* |
| * MSVDX/TOPAZ GPU virtual space looks like this |
| * (We currently use only one MMU context). |
| * PSB_MEM_MMU_START: from 0x00000000~0xe000000, for generic buffers |
| * TTM_PL_CI: from 0xe0000000+half GTT space, for camear/video buffer sharing |
| * TTM_PL_RAR: from TTM_PL_CI+CI size, for RAR/video buffer sharing |
| * TTM_PL_TT: from TTM_PL_RAR+RAR size, for buffers need to mapping into GTT |
| */ |
| static int psb_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, |
| struct ttm_mem_type_manager *man) |
| { |
| |
| struct drm_psb_private *dev_priv = |
| container_of(bdev, struct drm_psb_private, bdev); |
| struct psb_gtt *pg = dev_priv->pg; |
| |
| switch (type) { |
| case TTM_PL_SYSTEM: |
| man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; |
| man->available_caching = TTM_PL_FLAG_CACHED | |
| TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_WC; |
| man->default_caching = TTM_PL_FLAG_CACHED; |
| break; |
| case DRM_PSB_MEM_MMU: |
| man->func = &ttm_bo_manager_func; |
| man->flags = TTM_MEMTYPE_FLAG_MAPPABLE | |
| TTM_MEMTYPE_FLAG_CMA; |
| man->gpu_offset = PSB_MEM_MMU_START; |
| man->available_caching = TTM_PL_FLAG_CACHED | |
| TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_WC; |
| man->default_caching = TTM_PL_FLAG_WC; |
| break; |
| case TTM_PL_CI: |
| man->func = &ttm_bo_manager_func; |
| man->flags = TTM_MEMTYPE_FLAG_MAPPABLE | |
| TTM_MEMTYPE_FLAG_FIXED; |
| man->gpu_offset = pg->mmu_gatt_start + (pg->ci_start); |
| man->available_caching = TTM_PL_FLAG_UNCACHED; |
| man->default_caching = TTM_PL_FLAG_UNCACHED; |
| break; |
| case TTM_PL_RAR: /* Unmappable RAR memory */ |
| man->func = &ttm_bo_manager_func; |
| man->flags = TTM_MEMTYPE_FLAG_MAPPABLE | |
| TTM_MEMTYPE_FLAG_FIXED; |
| man->available_caching = TTM_PL_FLAG_UNCACHED; |
| man->default_caching = TTM_PL_FLAG_UNCACHED; |
| man->gpu_offset = pg->mmu_gatt_start + (pg->rar_start); |
| break; |
| case TTM_PL_TT: /* Mappable GATT memory */ |
| man->func = &ttm_bo_manager_func; |
| #ifdef PSB_WORKING_HOST_MMU_ACCESS |
| man->flags = TTM_MEMTYPE_FLAG_MAPPABLE; |
| #else |
| man->flags = TTM_MEMTYPE_FLAG_MAPPABLE | |
| TTM_MEMTYPE_FLAG_CMA; |
| #endif |
| man->available_caching = TTM_PL_FLAG_CACHED | |
| TTM_PL_FLAG_UNCACHED | TTM_PL_FLAG_WC; |
| man->default_caching = TTM_PL_FLAG_WC; |
| man->gpu_offset = pg->mmu_gatt_start + |
| (pg->rar_start + dev_priv->rar_region_size); |
| break; |
| default: |
| DRM_ERROR("Unsupported memory type %u\n", (unsigned) type); |
| return -EINVAL; |
| } |
| return 0; |
| } |
| |
| |
| static void psb_evict_mask(struct ttm_buffer_object *bo, |
| struct ttm_placement *placement) |
| { |
| static uint32_t cur_placement; |
| |
| cur_placement = bo->mem.placement & ~TTM_PL_MASK_MEM; |
| cur_placement |= TTM_PL_FLAG_SYSTEM; |
| |
| placement->fpfn = 0; |
| placement->lpfn = 0; |
| placement->num_placement = 1; |
| placement->placement = &cur_placement; |
| placement->num_busy_placement = 0; |
| placement->busy_placement = NULL; |
| |
| /* all buffers evicted to system memory */ |
| /* return cur_placement | TTM_PL_FLAG_SYSTEM; */ |
| } |
| |
| static int psb_invalidate_caches(struct ttm_bo_device *bdev, |
| uint32_t placement) |
| { |
| return 0; |
| } |
| |
| static int psb_move_blit(struct ttm_buffer_object *bo, |
| bool evict, bool no_wait, |
| struct ttm_mem_reg *new_mem) |
| { |
| BUG(); |
| return 0; |
| } |
| |
| /* |
| * Flip destination ttm into GATT, |
| * then blit and subsequently move out again. |
| */ |
| |
| static int psb_move_flip(struct ttm_buffer_object *bo, |
| bool evict, bool interruptible, bool no_wait, |
| struct ttm_mem_reg *new_mem) |
| { |
| /*struct ttm_bo_device *bdev = bo->bdev;*/ |
| struct ttm_mem_reg tmp_mem; |
| int ret; |
| struct ttm_placement placement; |
| uint32_t flags = TTM_PL_FLAG_TT; |
| |
| tmp_mem = *new_mem; |
| tmp_mem.mm_node = NULL; |
| |
| placement.fpfn = 0; |
| placement.lpfn = 0; |
| placement.num_placement = 1; |
| placement.placement = &flags; |
| placement.num_busy_placement = 0; /* FIXME */ |
| placement.busy_placement = NULL; |
| |
| ret = ttm_bo_mem_space(bo, &placement, &tmp_mem, interruptible, |
| false, no_wait); |
| if (ret) |
| return ret; |
| ret = ttm_tt_bind(bo->ttm, &tmp_mem); |
| if (ret) |
| goto out_cleanup; |
| ret = psb_move_blit(bo, true, no_wait, &tmp_mem); |
| if (ret) |
| goto out_cleanup; |
| |
| ret = ttm_bo_move_ttm(bo, evict, false, no_wait, new_mem); |
| out_cleanup: |
| if (tmp_mem.mm_node) { |
| drm_mm_put_block(tmp_mem.mm_node); |
| tmp_mem.mm_node = NULL; |
| } |
| return ret; |
| } |
| |
| static int psb_move(struct ttm_buffer_object *bo, |
| bool evict, bool interruptible, bool no_wait_reserve, |
| bool no_wait, struct ttm_mem_reg *new_mem) |
| { |
| struct ttm_mem_reg *old_mem = &bo->mem; |
| |
| if ((old_mem->mem_type == TTM_PL_RAR) || |
| (new_mem->mem_type == TTM_PL_RAR)) { |
| if (old_mem->mm_node) { |
| spin_lock(&bo->glob->lru_lock); |
| drm_mm_put_block(old_mem->mm_node); |
| spin_unlock(&bo->glob->lru_lock); |
| } |
| old_mem->mm_node = NULL; |
| *old_mem = *new_mem; |
| } else if (old_mem->mem_type == TTM_PL_SYSTEM) { |
| return ttm_bo_move_memcpy(bo, evict, false, no_wait, new_mem); |
| } else if (new_mem->mem_type == TTM_PL_SYSTEM) { |
| int ret = psb_move_flip(bo, evict, interruptible, |
| no_wait, new_mem); |
| if (unlikely(ret != 0)) { |
| if (ret == -ERESTART) |
| return ret; |
| else |
| return ttm_bo_move_memcpy(bo, evict, false, |
| no_wait, new_mem); |
| } |
| } else { |
| if (psb_move_blit(bo, evict, no_wait, new_mem)) |
| return ttm_bo_move_memcpy(bo, evict, false, no_wait, |
| new_mem); |
| } |
| return 0; |
| } |
| |
| static int drm_psb_tbe_populate(struct ttm_backend *backend, |
| unsigned long num_pages, |
| struct page **pages, |
| struct page *dummy_read_page, |
| dma_addr_t *dma_addrs) |
| { |
| struct drm_psb_ttm_backend *psb_be = |
| container_of(backend, struct drm_psb_ttm_backend, base); |
| |
| psb_be->pages = pages; |
| return 0; |
| } |
| |
| static int drm_psb_tbe_unbind(struct ttm_backend *backend) |
| { |
| struct ttm_bo_device *bdev = backend->bdev; |
| struct drm_psb_private *dev_priv = |
| container_of(bdev, struct drm_psb_private, bdev); |
| struct drm_psb_ttm_backend *psb_be = |
| container_of(backend, struct drm_psb_ttm_backend, base); |
| struct psb_mmu_pd *pd = psb_mmu_get_default_pd(dev_priv->mmu); |
| /* struct ttm_mem_type_manager *man = &bdev->man[psb_be->mem_type]; */ |
| |
| if (psb_be->mem_type == TTM_PL_TT) { |
| uint32_t gatt_p_offset = |
| (psb_be->offset - dev_priv->pg->mmu_gatt_start) |
| >> PAGE_SHIFT; |
| |
| (void) psb_gtt_remove_pages(dev_priv->pg, gatt_p_offset, |
| psb_be->num_pages, |
| psb_be->desired_tile_stride, |
| psb_be->hw_tile_stride, 0); |
| } |
| |
| psb_mmu_remove_pages(pd, psb_be->offset, |
| psb_be->num_pages, |
| psb_be->desired_tile_stride, |
| psb_be->hw_tile_stride); |
| |
| return 0; |
| } |
| |
| static int drm_psb_tbe_bind(struct ttm_backend *backend, |
| struct ttm_mem_reg *bo_mem) |
| { |
| struct ttm_bo_device *bdev = backend->bdev; |
| struct drm_psb_private *dev_priv = |
| container_of(bdev, struct drm_psb_private, bdev); |
| struct drm_psb_ttm_backend *psb_be = |
| container_of(backend, struct drm_psb_ttm_backend, base); |
| struct psb_mmu_pd *pd = psb_mmu_get_default_pd(dev_priv->mmu); |
| struct ttm_mem_type_manager *man = &bdev->man[bo_mem->mem_type]; |
| struct drm_mm_node *mm_node = bo_mem->mm_node; |
| int type; |
| int ret = 0; |
| |
| psb_be->mem_type = bo_mem->mem_type; |
| psb_be->num_pages = bo_mem->num_pages; |
| psb_be->desired_tile_stride = 0; |
| psb_be->hw_tile_stride = 0; |
| psb_be->offset = (mm_node->start << PAGE_SHIFT) + |
| man->gpu_offset; |
| |
| type = |
| (bo_mem-> |
| placement & TTM_PL_FLAG_CACHED) ? PSB_MMU_CACHED_MEMORY : 0; |
| |
| if (psb_be->mem_type == TTM_PL_TT) { |
| uint32_t gatt_p_offset = |
| (psb_be->offset - dev_priv->pg->mmu_gatt_start) |
| >> PAGE_SHIFT; |
| |
| ret = psb_gtt_insert_pages(dev_priv->pg, psb_be->pages, |
| gatt_p_offset, |
| psb_be->num_pages, |
| psb_be->desired_tile_stride, |
| psb_be->hw_tile_stride, type); |
| } |
| |
| ret = psb_mmu_insert_pages(pd, psb_be->pages, |
| psb_be->offset, psb_be->num_pages, |
| psb_be->desired_tile_stride, |
| psb_be->hw_tile_stride, type); |
| if (ret) |
| goto out_err; |
| |
| return 0; |
| out_err: |
| drm_psb_tbe_unbind(backend); |
| return ret; |
| |
| } |
| |
| static void drm_psb_tbe_clear(struct ttm_backend *backend) |
| { |
| struct drm_psb_ttm_backend *psb_be = |
| container_of(backend, struct drm_psb_ttm_backend, base); |
| |
| psb_be->pages = NULL; |
| return; |
| } |
| |
| static void drm_psb_tbe_destroy(struct ttm_backend *backend) |
| { |
| struct drm_psb_ttm_backend *psb_be = |
| container_of(backend, struct drm_psb_ttm_backend, base); |
| |
| if (backend) |
| kfree(psb_be); |
| } |
| |
| static struct ttm_backend_func psb_ttm_backend = { |
| .populate = drm_psb_tbe_populate, |
| .clear = drm_psb_tbe_clear, |
| .bind = drm_psb_tbe_bind, |
| .unbind = drm_psb_tbe_unbind, |
| .destroy = drm_psb_tbe_destroy, |
| }; |
| |
| static struct ttm_backend *drm_psb_tbe_init(struct ttm_bo_device *bdev) |
| { |
| struct drm_psb_ttm_backend *psb_be; |
| |
| psb_be = kzalloc(sizeof(*psb_be), GFP_KERNEL); |
| if (!psb_be) |
| return NULL; |
| psb_be->pages = NULL; |
| psb_be->base.func = &psb_ttm_backend; |
| psb_be->base.bdev = bdev; |
| return &psb_be->base; |
| } |
| |
| static int psb_ttm_io_mem_reserve(struct ttm_bo_device *bdev, |
| struct ttm_mem_reg *mem) |
| { |
| struct ttm_mem_type_manager *man = &bdev->man[mem->mem_type]; |
| struct drm_psb_private *dev_priv = |
| container_of(bdev, struct drm_psb_private, bdev); |
| struct psb_gtt *pg = dev_priv->pg; |
| struct drm_mm_node *mm_node = mem->mm_node; |
| |
| mem->bus.addr = NULL; |
| mem->bus.offset = 0; |
| mem->bus.size = mem->num_pages << PAGE_SHIFT; |
| mem->bus.base = 0; |
| mem->bus.is_iomem = false; |
| if (!(man->flags & TTM_MEMTYPE_FLAG_MAPPABLE)) |
| return -EINVAL; |
| switch (mem->mem_type) { |
| case TTM_PL_SYSTEM: |
| /* system memory */ |
| return 0; |
| case TTM_PL_TT: |
| mem->bus.offset = mm_node->start << PAGE_SHIFT; |
| mem->bus.base = pg->gatt_start; |
| mem->bus.is_iomem = false; |
| /* Don't know whether it is IO_MEM, this flag |
| used in vm_fault handle */ |
| break; |
| case DRM_PSB_MEM_MMU: |
| mem->bus.offset = mm_node->start << PAGE_SHIFT; |
| mem->bus.base = 0x00000000; |
| break; |
| case TTM_PL_CI: |
| mem->bus.offset = mm_node->start << PAGE_SHIFT; |
| mem->bus.base = dev_priv->ci_region_start;; |
| mem->bus.is_iomem = true; |
| break; |
| case TTM_PL_RAR: |
| mem->bus.offset = mm_node->start << PAGE_SHIFT; |
| mem->bus.base = dev_priv->rar_region_start;; |
| mem->bus.is_iomem = true; |
| break; |
| default: |
| return -EINVAL; |
| } |
| return 0; |
| } |
| |
| static void psb_ttm_io_mem_free(struct ttm_bo_device *bdev, |
| struct ttm_mem_reg *mem) |
| { |
| } |
| |
| /* |
| * Use this memory type priority if no eviction is needed. |
| */ |
| /* |
| static uint32_t psb_mem_prios[] = { |
| TTM_PL_CI, |
| TTM_PL_RAR, |
| TTM_PL_TT, |
| DRM_PSB_MEM_MMU, |
| TTM_PL_SYSTEM |
| }; |
| */ |
| /* |
| * Use this memory type priority if need to evict. |
| */ |
| /* |
| static uint32_t psb_busy_prios[] = { |
| TTM_PL_TT, |
| TTM_PL_CI, |
| TTM_PL_RAR, |
| DRM_PSB_MEM_MMU, |
| TTM_PL_SYSTEM |
| }; |
| */ |
| struct ttm_bo_driver psb_ttm_bo_driver = { |
| /* |
| .mem_type_prio = psb_mem_prios, |
| .mem_busy_prio = psb_busy_prios, |
| .num_mem_type_prio = ARRAY_SIZE(psb_mem_prios), |
| .num_mem_busy_prio = ARRAY_SIZE(psb_busy_prios), |
| */ |
| .create_ttm_backend_entry = &drm_psb_tbe_init, |
| .invalidate_caches = &psb_invalidate_caches, |
| .init_mem_type = &psb_init_mem_type, |
| .evict_flags = &psb_evict_mask, |
| .move = &psb_move, |
| .verify_access = &psb_verify_access, |
| .sync_obj_signaled = &ttm_fence_sync_obj_signaled, |
| .sync_obj_wait = &ttm_fence_sync_obj_wait, |
| .sync_obj_flush = &ttm_fence_sync_obj_flush, |
| .sync_obj_unref = &ttm_fence_sync_obj_unref, |
| .sync_obj_ref = &ttm_fence_sync_obj_ref, |
| .io_mem_reserve = &psb_ttm_io_mem_reserve, |
| .io_mem_free = &psb_ttm_io_mem_free |
| }; |