1f64122c1SDave Airlie /* 2f64122c1SDave Airlie * Copyright 2013 Red Hat Inc. 3f64122c1SDave Airlie * 4f64122c1SDave Airlie * Permission is hereby granted, free of charge, to any person obtaining a 5f64122c1SDave Airlie * copy of this software and associated documentation files (the "Software"), 6f64122c1SDave Airlie * to deal in the Software without restriction, including without limitation 7f64122c1SDave Airlie * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8f64122c1SDave Airlie * and/or sell copies of the Software, and to permit persons to whom the 9f64122c1SDave Airlie * Software is furnished to do so, subject to the following conditions: 10f64122c1SDave Airlie * 11f64122c1SDave Airlie * The above copyright notice and this permission notice shall be included in 12f64122c1SDave Airlie * all copies or substantial portions of the Software. 13f64122c1SDave Airlie * 14f64122c1SDave Airlie * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15f64122c1SDave Airlie * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16f64122c1SDave Airlie * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17f64122c1SDave Airlie * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 18f64122c1SDave Airlie * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 19f64122c1SDave Airlie * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 20f64122c1SDave Airlie * OTHER DEALINGS IN THE SOFTWARE. 21f64122c1SDave Airlie * 22f64122c1SDave Airlie * Authors: Dave Airlie 23f64122c1SDave Airlie * Alon Levy 24f64122c1SDave Airlie */ 25f64122c1SDave Airlie 26f64122c1SDave Airlie #include "qxl_drv.h" 27f64122c1SDave Airlie #include "qxl_object.h" 28f64122c1SDave Airlie 29f64122c1SDave Airlie #include <linux/io-mapping.h> 30f64122c1SDave Airlie static void qxl_ttm_bo_destroy(struct ttm_buffer_object *tbo) 31f64122c1SDave Airlie { 32f64122c1SDave Airlie struct qxl_bo *bo; 33f64122c1SDave Airlie struct qxl_device *qdev; 34f64122c1SDave Airlie 353ebf1c6dSFrediano Ziglio bo = to_qxl_bo(tbo); 36f64122c1SDave Airlie qdev = (struct qxl_device *)bo->gem_base.dev->dev_private; 37f64122c1SDave Airlie 38f64122c1SDave Airlie qxl_surface_evict(qdev, bo, false); 39f64122c1SDave Airlie mutex_lock(&qdev->gem.mutex); 40f64122c1SDave Airlie list_del_init(&bo->list); 41f64122c1SDave Airlie mutex_unlock(&qdev->gem.mutex); 42f64122c1SDave Airlie drm_gem_object_release(&bo->gem_base); 43f64122c1SDave Airlie kfree(bo); 44f64122c1SDave Airlie } 45f64122c1SDave Airlie 46f64122c1SDave Airlie bool qxl_ttm_bo_is_qxl_bo(struct ttm_buffer_object *bo) 47f64122c1SDave Airlie { 48f64122c1SDave Airlie if (bo->destroy == &qxl_ttm_bo_destroy) 49f64122c1SDave Airlie return true; 50f64122c1SDave Airlie return false; 51f64122c1SDave Airlie } 52f64122c1SDave Airlie 534f49ec92SDave Airlie void qxl_ttm_placement_from_domain(struct qxl_bo *qbo, u32 domain, bool pinned) 54f64122c1SDave Airlie { 55f64122c1SDave Airlie u32 c = 0; 564f49ec92SDave Airlie u32 pflag = pinned ? TTM_PL_FLAG_NO_EVICT : 0; 571b000494SShayenne da Luz Moura unsigned int i; 58f64122c1SDave Airlie 59f64122c1SDave Airlie qbo->placement.placement = qbo->placements; 60f64122c1SDave Airlie qbo->placement.busy_placement = qbo->placements; 6162c8ba7cSDave Airlie if (domain == QXL_GEM_DOMAIN_VRAM) 62f1217ed0SChristian König qbo->placements[c++].flags = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_VRAM | pflag; 6362c8ba7cSDave Airlie if (domain == QXL_GEM_DOMAIN_SURFACE) 64283cde69SChristian König qbo->placements[c++].flags = TTM_PL_FLAG_CACHED | TTM_PL_FLAG_PRIV | pflag; 6562c8ba7cSDave Airlie if (domain == QXL_GEM_DOMAIN_CPU) 66f1217ed0SChristian König qbo->placements[c++].flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM | pflag; 67f64122c1SDave Airlie if (!c) 68f1217ed0SChristian König qbo->placements[c++].flags = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; 69f64122c1SDave Airlie qbo->placement.num_placement = c; 70f64122c1SDave Airlie qbo->placement.num_busy_placement = c; 71f1217ed0SChristian König for (i = 0; i < c; ++i) { 72f1217ed0SChristian König qbo->placements[i].fpfn = 0; 73f1217ed0SChristian König qbo->placements[i].lpfn = 0; 74f1217ed0SChristian König } 75f64122c1SDave Airlie } 76f64122c1SDave Airlie 77f64122c1SDave Airlie int qxl_bo_create(struct qxl_device *qdev, 784f49ec92SDave Airlie unsigned long size, bool kernel, bool pinned, u32 domain, 79f64122c1SDave Airlie struct qxl_surface *surf, 80f64122c1SDave Airlie struct qxl_bo **bo_ptr) 81f64122c1SDave Airlie { 82f64122c1SDave Airlie struct qxl_bo *bo; 83f64122c1SDave Airlie enum ttm_bo_type type; 84f64122c1SDave Airlie int r; 85f64122c1SDave Airlie 86f64122c1SDave Airlie if (kernel) 87f64122c1SDave Airlie type = ttm_bo_type_kernel; 88f64122c1SDave Airlie else 89f64122c1SDave Airlie type = ttm_bo_type_device; 90f64122c1SDave Airlie *bo_ptr = NULL; 91f64122c1SDave Airlie bo = kzalloc(sizeof(struct qxl_bo), GFP_KERNEL); 92f64122c1SDave Airlie if (bo == NULL) 93f64122c1SDave Airlie return -ENOMEM; 94f64122c1SDave Airlie size = roundup(size, PAGE_SIZE); 95cbdded7fSGabriel Krisman Bertazi r = drm_gem_object_init(&qdev->ddev, &bo->gem_base, size); 96f64122c1SDave Airlie if (unlikely(r)) { 97f64122c1SDave Airlie kfree(bo); 98f64122c1SDave Airlie return r; 99f64122c1SDave Airlie } 100f64122c1SDave Airlie bo->type = domain; 1014f49ec92SDave Airlie bo->pin_count = pinned ? 1 : 0; 102f64122c1SDave Airlie bo->surface_id = 0; 103f64122c1SDave Airlie INIT_LIST_HEAD(&bo->list); 1048002db63SDave Airlie 105f64122c1SDave Airlie if (surf) 106f64122c1SDave Airlie bo->surf = *surf; 107f64122c1SDave Airlie 1084f49ec92SDave Airlie qxl_ttm_placement_from_domain(bo, domain, pinned); 109f64122c1SDave Airlie 110f64122c1SDave Airlie r = ttm_bo_init(&qdev->mman.bdev, &bo->tbo, size, type, 111724daa4fSChristian König &bo->placement, 0, !kernel, size, 112f4f4e3e3SMaarten Lankhorst NULL, NULL, &qxl_ttm_bo_destroy); 113f64122c1SDave Airlie if (unlikely(r != 0)) { 114f64122c1SDave Airlie if (r != -ERESTARTSYS) 115cbdded7fSGabriel Krisman Bertazi dev_err(qdev->ddev.dev, 116f64122c1SDave Airlie "object_init failed for (%lu, 0x%08X)\n", 117f64122c1SDave Airlie size, domain); 118f64122c1SDave Airlie return r; 119f64122c1SDave Airlie } 120f64122c1SDave Airlie *bo_ptr = bo; 121f64122c1SDave Airlie return 0; 122f64122c1SDave Airlie } 123f64122c1SDave Airlie 124f64122c1SDave Airlie int qxl_bo_kmap(struct qxl_bo *bo, void **ptr) 125f64122c1SDave Airlie { 126f64122c1SDave Airlie bool is_iomem; 127f64122c1SDave Airlie int r; 128f64122c1SDave Airlie 129f64122c1SDave Airlie if (bo->kptr) { 130f64122c1SDave Airlie if (ptr) 131f64122c1SDave Airlie *ptr = bo->kptr; 132f64122c1SDave Airlie return 0; 133f64122c1SDave Airlie } 134f64122c1SDave Airlie r = ttm_bo_kmap(&bo->tbo, 0, bo->tbo.num_pages, &bo->kmap); 135f64122c1SDave Airlie if (r) 136f64122c1SDave Airlie return r; 137f64122c1SDave Airlie bo->kptr = ttm_kmap_obj_virtual(&bo->kmap, &is_iomem); 138f64122c1SDave Airlie if (ptr) 139f64122c1SDave Airlie *ptr = bo->kptr; 140f64122c1SDave Airlie return 0; 141f64122c1SDave Airlie } 142f64122c1SDave Airlie 143f64122c1SDave Airlie void *qxl_bo_kmap_atomic_page(struct qxl_device *qdev, 144f64122c1SDave Airlie struct qxl_bo *bo, int page_offset) 145f64122c1SDave Airlie { 146f64122c1SDave Airlie struct ttm_mem_type_manager *man = &bo->tbo.bdev->man[bo->tbo.mem.mem_type]; 147f64122c1SDave Airlie void *rptr; 148f64122c1SDave Airlie int ret; 149f64122c1SDave Airlie struct io_mapping *map; 150f64122c1SDave Airlie 151f64122c1SDave Airlie if (bo->tbo.mem.mem_type == TTM_PL_VRAM) 152f64122c1SDave Airlie map = qdev->vram_mapping; 153283cde69SChristian König else if (bo->tbo.mem.mem_type == TTM_PL_PRIV) 154f64122c1SDave Airlie map = qdev->surface_mapping; 155f64122c1SDave Airlie else 156f64122c1SDave Airlie goto fallback; 157f64122c1SDave Airlie 158f64122c1SDave Airlie (void) ttm_mem_io_lock(man, false); 159f64122c1SDave Airlie ret = ttm_mem_io_reserve(bo->tbo.bdev, &bo->tbo.mem); 160f64122c1SDave Airlie ttm_mem_io_unlock(man); 161f64122c1SDave Airlie 162f64122c1SDave Airlie return io_mapping_map_atomic_wc(map, bo->tbo.mem.bus.offset + page_offset); 163f64122c1SDave Airlie fallback: 164f64122c1SDave Airlie if (bo->kptr) { 165f64122c1SDave Airlie rptr = bo->kptr + (page_offset * PAGE_SIZE); 166f64122c1SDave Airlie return rptr; 167f64122c1SDave Airlie } 168f64122c1SDave Airlie 169f64122c1SDave Airlie ret = qxl_bo_kmap(bo, &rptr); 170f64122c1SDave Airlie if (ret) 171f64122c1SDave Airlie return NULL; 172f64122c1SDave Airlie 173f64122c1SDave Airlie rptr += page_offset * PAGE_SIZE; 174f64122c1SDave Airlie return rptr; 175f64122c1SDave Airlie } 176f64122c1SDave Airlie 177f64122c1SDave Airlie void qxl_bo_kunmap(struct qxl_bo *bo) 178f64122c1SDave Airlie { 179f64122c1SDave Airlie if (bo->kptr == NULL) 180f64122c1SDave Airlie return; 181f64122c1SDave Airlie bo->kptr = NULL; 182f64122c1SDave Airlie ttm_bo_kunmap(&bo->kmap); 183f64122c1SDave Airlie } 184f64122c1SDave Airlie 185f64122c1SDave Airlie void qxl_bo_kunmap_atomic_page(struct qxl_device *qdev, 186f64122c1SDave Airlie struct qxl_bo *bo, void *pmap) 187f64122c1SDave Airlie { 188f64122c1SDave Airlie struct ttm_mem_type_manager *man = &bo->tbo.bdev->man[bo->tbo.mem.mem_type]; 189f64122c1SDave Airlie struct io_mapping *map; 190f64122c1SDave Airlie 191f64122c1SDave Airlie if (bo->tbo.mem.mem_type == TTM_PL_VRAM) 192f64122c1SDave Airlie map = qdev->vram_mapping; 193283cde69SChristian König else if (bo->tbo.mem.mem_type == TTM_PL_PRIV) 194f64122c1SDave Airlie map = qdev->surface_mapping; 195f64122c1SDave Airlie else 196f64122c1SDave Airlie goto fallback; 197f64122c1SDave Airlie 198f64122c1SDave Airlie io_mapping_unmap_atomic(pmap); 199f64122c1SDave Airlie 200f64122c1SDave Airlie (void) ttm_mem_io_lock(man, false); 201f64122c1SDave Airlie ttm_mem_io_free(bo->tbo.bdev, &bo->tbo.mem); 202f64122c1SDave Airlie ttm_mem_io_unlock(man); 203f64122c1SDave Airlie return ; 204f64122c1SDave Airlie fallback: 205f64122c1SDave Airlie qxl_bo_kunmap(bo); 206f64122c1SDave Airlie } 207f64122c1SDave Airlie 208f64122c1SDave Airlie void qxl_bo_unref(struct qxl_bo **bo) 209f64122c1SDave Airlie { 210f64122c1SDave Airlie if ((*bo) == NULL) 211f64122c1SDave Airlie return; 212e07154e2SFrediano Ziglio 2132793c1d7SSantha Meena Ramamoorthy drm_gem_object_put_unlocked(&(*bo)->gem_base); 214f64122c1SDave Airlie *bo = NULL; 215f64122c1SDave Airlie } 216f64122c1SDave Airlie 217f64122c1SDave Airlie struct qxl_bo *qxl_bo_ref(struct qxl_bo *bo) 218f64122c1SDave Airlie { 2192793c1d7SSantha Meena Ramamoorthy drm_gem_object_get(&bo->gem_base); 220f64122c1SDave Airlie return bo; 221f64122c1SDave Airlie } 222f64122c1SDave Airlie 22345dfe577SGerd Hoffmann static int __qxl_bo_pin(struct qxl_bo *bo, u32 domain, u64 *gpu_addr) 224f64122c1SDave Airlie { 22519be5570SChristian König struct ttm_operation_ctx ctx = { false, false }; 2265472bdeeSGabriel Krisman Bertazi struct drm_device *ddev = bo->gem_base.dev; 2274f49ec92SDave Airlie int r; 228f64122c1SDave Airlie 229f64122c1SDave Airlie if (bo->pin_count) { 230f64122c1SDave Airlie bo->pin_count++; 231f64122c1SDave Airlie if (gpu_addr) 232f64122c1SDave Airlie *gpu_addr = qxl_bo_gpu_offset(bo); 233f64122c1SDave Airlie return 0; 234f64122c1SDave Airlie } 2354f49ec92SDave Airlie qxl_ttm_placement_from_domain(bo, domain, true); 23619be5570SChristian König r = ttm_bo_validate(&bo->tbo, &bo->placement, &ctx); 237f64122c1SDave Airlie if (likely(r == 0)) { 238f64122c1SDave Airlie bo->pin_count = 1; 239f64122c1SDave Airlie if (gpu_addr != NULL) 240f64122c1SDave Airlie *gpu_addr = qxl_bo_gpu_offset(bo); 241f64122c1SDave Airlie } 242f64122c1SDave Airlie if (unlikely(r != 0)) 2435472bdeeSGabriel Krisman Bertazi dev_err(ddev->dev, "%p pin failed\n", bo); 244f64122c1SDave Airlie return r; 245f64122c1SDave Airlie } 246f64122c1SDave Airlie 24745dfe577SGerd Hoffmann static int __qxl_bo_unpin(struct qxl_bo *bo) 248f64122c1SDave Airlie { 24919be5570SChristian König struct ttm_operation_ctx ctx = { false, false }; 2505472bdeeSGabriel Krisman Bertazi struct drm_device *ddev = bo->gem_base.dev; 251f64122c1SDave Airlie int r, i; 252f64122c1SDave Airlie 253f64122c1SDave Airlie if (!bo->pin_count) { 2545472bdeeSGabriel Krisman Bertazi dev_warn(ddev->dev, "%p unpin not necessary\n", bo); 255f64122c1SDave Airlie return 0; 256f64122c1SDave Airlie } 257f64122c1SDave Airlie bo->pin_count--; 258f64122c1SDave Airlie if (bo->pin_count) 259f64122c1SDave Airlie return 0; 260f64122c1SDave Airlie for (i = 0; i < bo->placement.num_placement; i++) 261f1217ed0SChristian König bo->placements[i].flags &= ~TTM_PL_FLAG_NO_EVICT; 26219be5570SChristian König r = ttm_bo_validate(&bo->tbo, &bo->placement, &ctx); 263f64122c1SDave Airlie if (unlikely(r != 0)) 2645472bdeeSGabriel Krisman Bertazi dev_err(ddev->dev, "%p validate failed for unpin\n", bo); 265f64122c1SDave Airlie return r; 266f64122c1SDave Airlie } 267f64122c1SDave Airlie 268715a11faSGabriel Krisman Bertazi /* 269715a11faSGabriel Krisman Bertazi * Reserve the BO before pinning the object. If the BO was reserved 270715a11faSGabriel Krisman Bertazi * beforehand, use the internal version directly __qxl_bo_pin. 271715a11faSGabriel Krisman Bertazi * 272715a11faSGabriel Krisman Bertazi */ 273715a11faSGabriel Krisman Bertazi int qxl_bo_pin(struct qxl_bo *bo, u32 domain, u64 *gpu_addr) 274715a11faSGabriel Krisman Bertazi { 275715a11faSGabriel Krisman Bertazi int r; 276715a11faSGabriel Krisman Bertazi 277715a11faSGabriel Krisman Bertazi r = qxl_bo_reserve(bo, false); 278715a11faSGabriel Krisman Bertazi if (r) 279715a11faSGabriel Krisman Bertazi return r; 280715a11faSGabriel Krisman Bertazi 281715a11faSGabriel Krisman Bertazi r = __qxl_bo_pin(bo, bo->type, NULL); 282715a11faSGabriel Krisman Bertazi qxl_bo_unreserve(bo); 283715a11faSGabriel Krisman Bertazi return r; 284715a11faSGabriel Krisman Bertazi } 285715a11faSGabriel Krisman Bertazi 286715a11faSGabriel Krisman Bertazi /* 287715a11faSGabriel Krisman Bertazi * Reserve the BO before pinning the object. If the BO was reserved 288715a11faSGabriel Krisman Bertazi * beforehand, use the internal version directly __qxl_bo_unpin. 289715a11faSGabriel Krisman Bertazi * 290715a11faSGabriel Krisman Bertazi */ 291715a11faSGabriel Krisman Bertazi int qxl_bo_unpin(struct qxl_bo *bo) 292715a11faSGabriel Krisman Bertazi { 293715a11faSGabriel Krisman Bertazi int r; 294715a11faSGabriel Krisman Bertazi 295715a11faSGabriel Krisman Bertazi r = qxl_bo_reserve(bo, false); 296715a11faSGabriel Krisman Bertazi if (r) 297715a11faSGabriel Krisman Bertazi return r; 298715a11faSGabriel Krisman Bertazi 299715a11faSGabriel Krisman Bertazi r = __qxl_bo_unpin(bo); 300715a11faSGabriel Krisman Bertazi qxl_bo_unreserve(bo); 301715a11faSGabriel Krisman Bertazi return r; 302715a11faSGabriel Krisman Bertazi } 303715a11faSGabriel Krisman Bertazi 304f64122c1SDave Airlie void qxl_bo_force_delete(struct qxl_device *qdev) 305f64122c1SDave Airlie { 306f64122c1SDave Airlie struct qxl_bo *bo, *n; 307f64122c1SDave Airlie 308f64122c1SDave Airlie if (list_empty(&qdev->gem.objects)) 309f64122c1SDave Airlie return; 310cbdded7fSGabriel Krisman Bertazi dev_err(qdev->ddev.dev, "Userspace still has active objects !\n"); 311f64122c1SDave Airlie list_for_each_entry_safe(bo, n, &qdev->gem.objects, list) { 312cbdded7fSGabriel Krisman Bertazi dev_err(qdev->ddev.dev, "%p %p %lu %lu force free\n", 313f64122c1SDave Airlie &bo->gem_base, bo, (unsigned long)bo->gem_base.size, 314f64122c1SDave Airlie *((unsigned long *)&bo->gem_base.refcount)); 315f64122c1SDave Airlie mutex_lock(&qdev->gem.mutex); 316f64122c1SDave Airlie list_del_init(&bo->list); 317f64122c1SDave Airlie mutex_unlock(&qdev->gem.mutex); 318f64122c1SDave Airlie /* this should unref the ttm bo */ 3192793c1d7SSantha Meena Ramamoorthy drm_gem_object_put_unlocked(&bo->gem_base); 320f64122c1SDave Airlie } 321f64122c1SDave Airlie } 322f64122c1SDave Airlie 323f64122c1SDave Airlie int qxl_bo_init(struct qxl_device *qdev) 324f64122c1SDave Airlie { 325f64122c1SDave Airlie return qxl_ttm_init(qdev); 326f64122c1SDave Airlie } 327f64122c1SDave Airlie 328f64122c1SDave Airlie void qxl_bo_fini(struct qxl_device *qdev) 329f64122c1SDave Airlie { 330f64122c1SDave Airlie qxl_ttm_fini(qdev); 331f64122c1SDave Airlie } 332f64122c1SDave Airlie 333f64122c1SDave Airlie int qxl_bo_check_id(struct qxl_device *qdev, struct qxl_bo *bo) 334f64122c1SDave Airlie { 335f64122c1SDave Airlie int ret; 336408799ebSShayenne da Luz Moura 337f64122c1SDave Airlie if (bo->type == QXL_GEM_DOMAIN_SURFACE && bo->surface_id == 0) { 338f64122c1SDave Airlie /* allocate a surface id for this surface now */ 339f64122c1SDave Airlie ret = qxl_surface_id_alloc(qdev, bo); 340f64122c1SDave Airlie if (ret) 341f64122c1SDave Airlie return ret; 342f64122c1SDave Airlie 343f64122c1SDave Airlie ret = qxl_hw_surface_alloc(qdev, bo, NULL); 344f64122c1SDave Airlie if (ret) 345f64122c1SDave Airlie return ret; 346f64122c1SDave Airlie } 347f64122c1SDave Airlie return 0; 348f64122c1SDave Airlie } 349f64122c1SDave Airlie 350b86487a6SDave Airlie int qxl_surf_evict(struct qxl_device *qdev) 351b86487a6SDave Airlie { 352283cde69SChristian König return ttm_bo_evict_mm(&qdev->mman.bdev, TTM_PL_PRIV); 353b86487a6SDave Airlie } 354d84300bfSDave Airlie 355d84300bfSDave Airlie int qxl_vram_evict(struct qxl_device *qdev) 356d84300bfSDave Airlie { 357d84300bfSDave Airlie return ttm_bo_evict_mm(&qdev->mman.bdev, TTM_PL_VRAM); 358d84300bfSDave Airlie } 359