1 /* 2 * SImple Tiler Allocator (SiTA): 2D and 1D allocation(reservation) algorithm 3 * 4 * Authors: Ravi Ramachandra <r.ramachandra@ti.com>, 5 * Lajos Molnar <molnar@ti.com> 6 * Andy Gross <andy.gross@ti.com> 7 * 8 * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ 9 * 10 * This package is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License version 2 as 12 * published by the Free Software Foundation. 13 * 14 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 16 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 17 * 18 */ 19 #include <linux/init.h> 20 #include <linux/module.h> 21 #include <linux/errno.h> 22 #include <linux/sched.h> 23 #include <linux/wait.h> 24 #include <linux/bitmap.h> 25 #include <linux/slab.h> 26 #include "tcm.h" 27 28 static unsigned long mask[8]; 29 /* 30 * pos position in bitmap 31 * w width in slots 32 * h height in slots 33 * map ptr to bitmap 34 * stride slots in a row 35 */ 36 static void free_slots(unsigned long pos, u16 w, u16 h, 37 unsigned long *map, u16 stride) 38 { 39 int i; 40 41 for (i = 0; i < h; i++, pos += stride) 42 bitmap_clear(map, pos, w); 43 } 44 45 /* 46 * w width in slots 47 * pos ptr to position 48 * map ptr to bitmap 49 * num_bits number of bits in bitmap 50 */ 51 static int r2l_b2t_1d(u16 w, unsigned long *pos, unsigned long *map, 52 size_t num_bits) 53 { 54 unsigned long search_count = 0; 55 unsigned long bit; 56 bool area_found = false; 57 58 *pos = num_bits - w; 59 60 while (search_count < num_bits) { 61 bit = find_next_bit(map, num_bits, *pos); 62 63 if (bit - *pos >= w) { 64 /* found a long enough free area */ 65 bitmap_set(map, *pos, w); 66 area_found = true; 67 break; 68 } 69 70 search_count = num_bits - bit + w; 71 *pos = bit - w; 72 } 73 74 return (area_found) ? 0 : -ENOMEM; 75 } 76 77 /* 78 * w = width in slots 79 * h = height in slots 80 * a = align in slots (mask, 2^n-1, 0 is unaligned) 81 * offset = offset in bytes from 4KiB 82 * pos = position in bitmap for buffer 83 * map = bitmap ptr 84 * num_bits = size of bitmap 85 * stride = bits in one row of container 86 */ 87 static int l2r_t2b(u16 w, u16 h, u16 a, s16 offset, 88 unsigned long *pos, unsigned long slot_bytes, 89 unsigned long *map, size_t num_bits, size_t slot_stride) 90 { 91 int i; 92 unsigned long index; 93 bool area_free = false; 94 unsigned long slots_per_band = PAGE_SIZE / slot_bytes; 95 unsigned long bit_offset = (offset > 0) ? offset / slot_bytes : 0; 96 unsigned long curr_bit = bit_offset; 97 98 /* reset alignment to 1 if we are matching a specific offset */ 99 /* adjust alignment - 1 to get to the format expected in bitmaps */ 100 a = (offset > 0) ? 0 : a - 1; 101 102 /* FIXME Return error if slots_per_band > stride */ 103 104 while (curr_bit < num_bits) { 105 *pos = bitmap_find_next_zero_area(map, num_bits, curr_bit, w, 106 a); 107 108 /* skip forward if we are not at right offset */ 109 if (bit_offset > 0 && (*pos % slots_per_band != bit_offset)) { 110 curr_bit = ALIGN(*pos, slots_per_band) + bit_offset; 111 continue; 112 } 113 114 /* skip forward to next row if we overlap end of row */ 115 if ((*pos % slot_stride) + w > slot_stride) { 116 curr_bit = ALIGN(*pos, slot_stride) + bit_offset; 117 continue; 118 } 119 120 /* TODO: Handle overlapping 4K boundaries */ 121 122 /* break out of look if we will go past end of container */ 123 if ((*pos + slot_stride * h) > num_bits) 124 break; 125 126 /* generate mask that represents out matching pattern */ 127 bitmap_clear(mask, 0, slot_stride); 128 bitmap_set(mask, (*pos % BITS_PER_LONG), w); 129 130 /* assume the area is free until we find an overlap */ 131 area_free = true; 132 133 /* check subsequent rows to see if complete area is free */ 134 for (i = 1; i < h; i++) { 135 index = *pos / BITS_PER_LONG + i * 8; 136 if (bitmap_intersects(&map[index], mask, 137 (*pos % BITS_PER_LONG) + w)) { 138 area_free = false; 139 break; 140 } 141 } 142 143 if (area_free) 144 break; 145 146 /* go forward past this match */ 147 if (bit_offset > 0) 148 curr_bit = ALIGN(*pos, slots_per_band) + bit_offset; 149 else 150 curr_bit = *pos + a + 1; 151 } 152 153 if (area_free) { 154 /* set area as in-use. iterate over rows */ 155 for (i = 0, index = *pos; i < h; i++, index += slot_stride) 156 bitmap_set(map, index, w); 157 } 158 159 return (area_free) ? 0 : -ENOMEM; 160 } 161 162 static s32 sita_reserve_1d(struct tcm *tcm, u32 num_slots, 163 struct tcm_area *area) 164 { 165 unsigned long pos; 166 int ret; 167 168 spin_lock(&(tcm->lock)); 169 ret = r2l_b2t_1d(num_slots, &pos, tcm->bitmap, tcm->map_size); 170 if (!ret) { 171 area->p0.x = pos % tcm->width; 172 area->p0.y = pos / tcm->width; 173 area->p1.x = (pos + num_slots - 1) % tcm->width; 174 area->p1.y = (pos + num_slots - 1) / tcm->width; 175 } 176 spin_unlock(&(tcm->lock)); 177 178 return ret; 179 } 180 181 static s32 sita_reserve_2d(struct tcm *tcm, u16 h, u16 w, u16 align, 182 s16 offset, u16 slot_bytes, 183 struct tcm_area *area) 184 { 185 unsigned long pos; 186 int ret; 187 188 spin_lock(&(tcm->lock)); 189 ret = l2r_t2b(w, h, align, offset, &pos, slot_bytes, tcm->bitmap, 190 tcm->map_size, tcm->width); 191 192 if (!ret) { 193 area->p0.x = pos % tcm->width; 194 area->p0.y = pos / tcm->width; 195 area->p1.x = area->p0.x + w - 1; 196 area->p1.y = area->p0.y + h - 1; 197 } 198 spin_unlock(&(tcm->lock)); 199 200 return ret; 201 } 202 203 static void sita_deinit(struct tcm *tcm) 204 { 205 kfree(tcm); 206 } 207 208 static s32 sita_free(struct tcm *tcm, struct tcm_area *area) 209 { 210 unsigned long pos; 211 u16 w, h; 212 213 pos = area->p0.x + area->p0.y * tcm->width; 214 if (area->is2d) { 215 w = area->p1.x - area->p0.x + 1; 216 h = area->p1.y - area->p0.y + 1; 217 } else { 218 w = area->p1.x + area->p1.y * tcm->width - pos + 1; 219 h = 1; 220 } 221 222 spin_lock(&(tcm->lock)); 223 free_slots(pos, w, h, tcm->bitmap, tcm->width); 224 spin_unlock(&(tcm->lock)); 225 return 0; 226 } 227 228 struct tcm *sita_init(u16 width, u16 height) 229 { 230 struct tcm *tcm; 231 size_t map_size = BITS_TO_LONGS(width*height) * sizeof(unsigned long); 232 233 if (width == 0 || height == 0) 234 return NULL; 235 236 tcm = kzalloc(sizeof(*tcm) + map_size, GFP_KERNEL); 237 if (!tcm) 238 goto error; 239 240 /* Updating the pointers to SiTA implementation APIs */ 241 tcm->height = height; 242 tcm->width = width; 243 tcm->reserve_2d = sita_reserve_2d; 244 tcm->reserve_1d = sita_reserve_1d; 245 tcm->free = sita_free; 246 tcm->deinit = sita_deinit; 247 248 spin_lock_init(&tcm->lock); 249 tcm->bitmap = (unsigned long *)(tcm + 1); 250 bitmap_clear(tcm->bitmap, 0, width*height); 251 252 tcm->map_size = width*height; 253 254 return tcm; 255 256 error: 257 kfree(tcm); 258 return NULL; 259 } 260