1d722e3fbSopenharmony_ci/* 2d722e3fbSopenharmony_ci * Copyright (C) 2016 Etnaviv Project 3d722e3fbSopenharmony_ci * 4d722e3fbSopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a 5d722e3fbSopenharmony_ci * copy of this software and associated documentation files (the "Software"), 6d722e3fbSopenharmony_ci * to deal in the Software without restriction, including without limitation 7d722e3fbSopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8d722e3fbSopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the 9d722e3fbSopenharmony_ci * Software is furnished to do so, subject to the following conditions: 10d722e3fbSopenharmony_ci * 11d722e3fbSopenharmony_ci * The above copyright notice and this permission notice (including the next 12d722e3fbSopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the 13d722e3fbSopenharmony_ci * Software. 14d722e3fbSopenharmony_ci * 15d722e3fbSopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16d722e3fbSopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17d722e3fbSopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18d722e3fbSopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19d722e3fbSopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20d722e3fbSopenharmony_ci * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21d722e3fbSopenharmony_ci * SOFTWARE. 22d722e3fbSopenharmony_ci * 23d722e3fbSopenharmony_ci * Authors: 24d722e3fbSopenharmony_ci * Christian Gmeiner <christian.gmeiner@gmail.com> 25d722e3fbSopenharmony_ci */ 26d722e3fbSopenharmony_ci 27d722e3fbSopenharmony_ci#include "etnaviv_priv.h" 28d722e3fbSopenharmony_ci#include "etnaviv_drmif.h" 29d722e3fbSopenharmony_ci 30d722e3fbSopenharmony_cidrm_private void bo_del(struct etna_bo *bo); 31d722e3fbSopenharmony_cidrm_private extern pthread_mutex_t table_lock; 32d722e3fbSopenharmony_ci 33d722e3fbSopenharmony_cistatic void add_bucket(struct etna_bo_cache *cache, int size) 34d722e3fbSopenharmony_ci{ 35d722e3fbSopenharmony_ci unsigned i = cache->num_buckets; 36d722e3fbSopenharmony_ci 37d722e3fbSopenharmony_ci assert(i < ARRAY_SIZE(cache->cache_bucket)); 38d722e3fbSopenharmony_ci 39d722e3fbSopenharmony_ci list_inithead(&cache->cache_bucket[i].list); 40d722e3fbSopenharmony_ci cache->cache_bucket[i].size = size; 41d722e3fbSopenharmony_ci cache->num_buckets++; 42d722e3fbSopenharmony_ci} 43d722e3fbSopenharmony_ci 44d722e3fbSopenharmony_cidrm_private void etna_bo_cache_init(struct etna_bo_cache *cache) 45d722e3fbSopenharmony_ci{ 46d722e3fbSopenharmony_ci unsigned long size, cache_max_size = 64 * 1024 * 1024; 47d722e3fbSopenharmony_ci 48d722e3fbSopenharmony_ci /* OK, so power of two buckets was too wasteful of memory. 49d722e3fbSopenharmony_ci * Give 3 other sizes between each power of two, to hopefully 50d722e3fbSopenharmony_ci * cover things accurately enough. (The alternative is 51d722e3fbSopenharmony_ci * probably to just go for exact matching of sizes, and assume 52d722e3fbSopenharmony_ci * that for things like composited window resize the tiled 53d722e3fbSopenharmony_ci * width/height alignment and rounding of sizes to pages will 54d722e3fbSopenharmony_ci * get us useful cache hit rates anyway) 55d722e3fbSopenharmony_ci */ 56d722e3fbSopenharmony_ci add_bucket(cache, 4096); 57d722e3fbSopenharmony_ci add_bucket(cache, 4096 * 2); 58d722e3fbSopenharmony_ci add_bucket(cache, 4096 * 3); 59d722e3fbSopenharmony_ci 60d722e3fbSopenharmony_ci /* Initialize the linked lists for BO reuse cache. */ 61d722e3fbSopenharmony_ci for (size = 4 * 4096; size <= cache_max_size; size *= 2) { 62d722e3fbSopenharmony_ci add_bucket(cache, size); 63d722e3fbSopenharmony_ci add_bucket(cache, size + size * 1 / 4); 64d722e3fbSopenharmony_ci add_bucket(cache, size + size * 2 / 4); 65d722e3fbSopenharmony_ci add_bucket(cache, size + size * 3 / 4); 66d722e3fbSopenharmony_ci } 67d722e3fbSopenharmony_ci} 68d722e3fbSopenharmony_ci 69d722e3fbSopenharmony_ci/* Frees older cached buffers. Called under table_lock */ 70d722e3fbSopenharmony_cidrm_private void etna_bo_cache_cleanup(struct etna_bo_cache *cache, time_t time) 71d722e3fbSopenharmony_ci{ 72d722e3fbSopenharmony_ci unsigned i; 73d722e3fbSopenharmony_ci 74d722e3fbSopenharmony_ci if (cache->time == time) 75d722e3fbSopenharmony_ci return; 76d722e3fbSopenharmony_ci 77d722e3fbSopenharmony_ci for (i = 0; i < cache->num_buckets; i++) { 78d722e3fbSopenharmony_ci struct etna_bo_bucket *bucket = &cache->cache_bucket[i]; 79d722e3fbSopenharmony_ci struct etna_bo *bo; 80d722e3fbSopenharmony_ci 81d722e3fbSopenharmony_ci while (!LIST_IS_EMPTY(&bucket->list)) { 82d722e3fbSopenharmony_ci bo = LIST_ENTRY(struct etna_bo, bucket->list.next, list); 83d722e3fbSopenharmony_ci 84d722e3fbSopenharmony_ci /* keep things in cache for at least 1 second: */ 85d722e3fbSopenharmony_ci if (time && ((time - bo->free_time) <= 1)) 86d722e3fbSopenharmony_ci break; 87d722e3fbSopenharmony_ci 88d722e3fbSopenharmony_ci list_del(&bo->list); 89d722e3fbSopenharmony_ci bo_del(bo); 90d722e3fbSopenharmony_ci } 91d722e3fbSopenharmony_ci } 92d722e3fbSopenharmony_ci 93d722e3fbSopenharmony_ci cache->time = time; 94d722e3fbSopenharmony_ci} 95d722e3fbSopenharmony_ci 96d722e3fbSopenharmony_cistatic struct etna_bo_bucket *get_bucket(struct etna_bo_cache *cache, uint32_t size) 97d722e3fbSopenharmony_ci{ 98d722e3fbSopenharmony_ci unsigned i; 99d722e3fbSopenharmony_ci 100d722e3fbSopenharmony_ci /* hmm, this is what intel does, but I suppose we could calculate our 101d722e3fbSopenharmony_ci * way to the correct bucket size rather than looping.. 102d722e3fbSopenharmony_ci */ 103d722e3fbSopenharmony_ci for (i = 0; i < cache->num_buckets; i++) { 104d722e3fbSopenharmony_ci struct etna_bo_bucket *bucket = &cache->cache_bucket[i]; 105d722e3fbSopenharmony_ci if (bucket->size >= size) { 106d722e3fbSopenharmony_ci return bucket; 107d722e3fbSopenharmony_ci } 108d722e3fbSopenharmony_ci } 109d722e3fbSopenharmony_ci 110d722e3fbSopenharmony_ci return NULL; 111d722e3fbSopenharmony_ci} 112d722e3fbSopenharmony_ci 113d722e3fbSopenharmony_cistatic int is_idle(struct etna_bo *bo) 114d722e3fbSopenharmony_ci{ 115d722e3fbSopenharmony_ci return etna_bo_cpu_prep(bo, 116d722e3fbSopenharmony_ci DRM_ETNA_PREP_READ | 117d722e3fbSopenharmony_ci DRM_ETNA_PREP_WRITE | 118d722e3fbSopenharmony_ci DRM_ETNA_PREP_NOSYNC) == 0; 119d722e3fbSopenharmony_ci} 120d722e3fbSopenharmony_ci 121d722e3fbSopenharmony_cistatic struct etna_bo *find_in_bucket(struct etna_bo_bucket *bucket, uint32_t flags) 122d722e3fbSopenharmony_ci{ 123d722e3fbSopenharmony_ci struct etna_bo *bo = NULL, *tmp; 124d722e3fbSopenharmony_ci 125d722e3fbSopenharmony_ci pthread_mutex_lock(&table_lock); 126d722e3fbSopenharmony_ci 127d722e3fbSopenharmony_ci if (LIST_IS_EMPTY(&bucket->list)) 128d722e3fbSopenharmony_ci goto out_unlock; 129d722e3fbSopenharmony_ci 130d722e3fbSopenharmony_ci LIST_FOR_EACH_ENTRY_SAFE(bo, tmp, &bucket->list, list) { 131d722e3fbSopenharmony_ci /* skip BOs with different flags */ 132d722e3fbSopenharmony_ci if (bo->flags != flags) 133d722e3fbSopenharmony_ci continue; 134d722e3fbSopenharmony_ci 135d722e3fbSopenharmony_ci /* check if the first BO with matching flags is idle */ 136d722e3fbSopenharmony_ci if (is_idle(bo)) { 137d722e3fbSopenharmony_ci list_delinit(&bo->list); 138d722e3fbSopenharmony_ci goto out_unlock; 139d722e3fbSopenharmony_ci } 140d722e3fbSopenharmony_ci 141d722e3fbSopenharmony_ci /* If the oldest BO is still busy, don't try younger ones */ 142d722e3fbSopenharmony_ci break; 143d722e3fbSopenharmony_ci } 144d722e3fbSopenharmony_ci 145d722e3fbSopenharmony_ci /* There was no matching buffer found */ 146d722e3fbSopenharmony_ci bo = NULL; 147d722e3fbSopenharmony_ci 148d722e3fbSopenharmony_ciout_unlock: 149d722e3fbSopenharmony_ci pthread_mutex_unlock(&table_lock); 150d722e3fbSopenharmony_ci 151d722e3fbSopenharmony_ci return bo; 152d722e3fbSopenharmony_ci} 153d722e3fbSopenharmony_ci 154d722e3fbSopenharmony_ci/* allocate a new (un-tiled) buffer object 155d722e3fbSopenharmony_ci * 156d722e3fbSopenharmony_ci * NOTE: size is potentially rounded up to bucket size 157d722e3fbSopenharmony_ci */ 158d722e3fbSopenharmony_cidrm_private struct etna_bo *etna_bo_cache_alloc(struct etna_bo_cache *cache, uint32_t *size, 159d722e3fbSopenharmony_ci uint32_t flags) 160d722e3fbSopenharmony_ci{ 161d722e3fbSopenharmony_ci struct etna_bo *bo; 162d722e3fbSopenharmony_ci struct etna_bo_bucket *bucket; 163d722e3fbSopenharmony_ci 164d722e3fbSopenharmony_ci *size = ALIGN(*size, 4096); 165d722e3fbSopenharmony_ci bucket = get_bucket(cache, *size); 166d722e3fbSopenharmony_ci 167d722e3fbSopenharmony_ci /* see if we can be green and recycle: */ 168d722e3fbSopenharmony_ci if (bucket) { 169d722e3fbSopenharmony_ci *size = bucket->size; 170d722e3fbSopenharmony_ci bo = find_in_bucket(bucket, flags); 171d722e3fbSopenharmony_ci if (bo) { 172d722e3fbSopenharmony_ci atomic_set(&bo->refcnt, 1); 173d722e3fbSopenharmony_ci etna_device_ref(bo->dev); 174d722e3fbSopenharmony_ci return bo; 175d722e3fbSopenharmony_ci } 176d722e3fbSopenharmony_ci } 177d722e3fbSopenharmony_ci 178d722e3fbSopenharmony_ci return NULL; 179d722e3fbSopenharmony_ci} 180d722e3fbSopenharmony_ci 181d722e3fbSopenharmony_cidrm_private int etna_bo_cache_free(struct etna_bo_cache *cache, struct etna_bo *bo) 182d722e3fbSopenharmony_ci{ 183d722e3fbSopenharmony_ci struct etna_bo_bucket *bucket = get_bucket(cache, bo->size); 184d722e3fbSopenharmony_ci 185d722e3fbSopenharmony_ci /* see if we can be green and recycle: */ 186d722e3fbSopenharmony_ci if (bucket) { 187d722e3fbSopenharmony_ci struct timespec time; 188d722e3fbSopenharmony_ci 189d722e3fbSopenharmony_ci clock_gettime(CLOCK_MONOTONIC, &time); 190d722e3fbSopenharmony_ci 191d722e3fbSopenharmony_ci bo->free_time = time.tv_sec; 192d722e3fbSopenharmony_ci list_addtail(&bo->list, &bucket->list); 193d722e3fbSopenharmony_ci etna_bo_cache_cleanup(cache, time.tv_sec); 194d722e3fbSopenharmony_ci 195d722e3fbSopenharmony_ci /* bo's in the bucket cache don't have a ref and 196d722e3fbSopenharmony_ci * don't hold a ref to the dev: 197d722e3fbSopenharmony_ci */ 198d722e3fbSopenharmony_ci etna_device_del_locked(bo->dev); 199d722e3fbSopenharmony_ci 200d722e3fbSopenharmony_ci return 0; 201d722e3fbSopenharmony_ci } 202d722e3fbSopenharmony_ci 203d722e3fbSopenharmony_ci return -1; 204d722e3fbSopenharmony_ci} 205