blob: 3077f6a7b7dc9a9b54a37fb4e62754a28c5f545f [file] [log] [blame]
/**************************************************************************
* 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
};