1/* 2 * tcm-sita.c 3 * 4 * SImple Tiler Allocator (SiTA): 2D and 1D allocation(reservation) algorithm 5 * 6 * Authors: Ravi Ramachandra <r.ramachandra@ti.com>, 7 * Lajos Molnar <molnar@ti.com> 8 * 9 * Copyright (C) 2009-2010 Texas Instruments, Inc. 10 * 11 * This package is free software; you can redistribute it and/or modify 12 * it under the terms of the GNU General Public License version 2 as 13 * published by the Free Software Foundation. 14 * 15 * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 17 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 18 * 19 */ 20#include <linux/slab.h> 21#include <linux/spinlock.h> 22 23#include "tcm-sita.h" 24 25#define ALIGN_DOWN(value, align) ((value) & ~((align) - 1)) 26 27/* Individual selection criteria for different scan areas */ 28static s32 CR_L2R_T2B = CR_BIAS_HORIZONTAL; 29static s32 CR_R2L_T2B = CR_DIAGONAL_BALANCE; 30 31/********************************************* 32 * TCM API - Sita Implementation 33 *********************************************/ 34static s32 sita_reserve_2d(struct tcm *tcm, u16 h, u16 w, u8 align, 35 struct tcm_area *area); 36static s32 sita_reserve_1d(struct tcm *tcm, u32 slots, struct tcm_area *area); 37static s32 sita_free(struct tcm *tcm, struct tcm_area *area); 38static void sita_deinit(struct tcm *tcm); 39 40/********************************************* 41 * Main Scanner functions 42 *********************************************/ 43static s32 scan_areas_and_find_fit(struct tcm *tcm, u16 w, u16 h, u16 align, 44 struct tcm_area *area); 45 46static s32 scan_l2r_t2b(struct tcm *tcm, u16 w, u16 h, u16 align, 47 struct tcm_area *field, struct tcm_area *area); 48 49static s32 scan_r2l_t2b(struct tcm *tcm, u16 w, u16 h, u16 align, 50 struct tcm_area *field, struct tcm_area *area); 51 52static s32 scan_r2l_b2t_one_dim(struct tcm *tcm, u32 num_slots, 53 struct tcm_area *field, struct tcm_area *area); 54 55/********************************************* 56 * Support Infrastructure Methods 57 *********************************************/ 58static s32 is_area_free(struct tcm_area ***map, u16 x0, u16 y0, u16 w, u16 h); 59 60static s32 update_candidate(struct tcm *tcm, u16 x0, u16 y0, u16 w, u16 h, 61 struct tcm_area *field, s32 criteria, 62 struct score *best); 63 64static void get_nearness_factor(struct tcm_area *field, 65 struct tcm_area *candidate, 66 struct nearness_factor *nf); 67 68static void get_neighbor_stats(struct tcm *tcm, struct tcm_area *area, 69 struct neighbor_stats *stat); 70 71static void fill_area(struct tcm *tcm, 72 struct tcm_area *area, struct tcm_area *parent); 73 74 75/*********************************************/ 76 77/********************************************* 78 * Utility Methods 79 *********************************************/ 80struct tcm *sita_init(u16 width, u16 height, struct tcm_pt *attr) 81{ 82 struct tcm *tcm; 83 struct sita_pvt *pvt; 84 struct tcm_area area = {0}; 85 s32 i; 86 87 if (width == 0 || height == 0) 88 return NULL; 89 90 tcm = kmalloc(sizeof(*tcm), GFP_KERNEL); 91 pvt = kmalloc(sizeof(*pvt), GFP_KERNEL); 92 if (!tcm || !pvt) 93 goto error; 94 95 memset(tcm, 0, sizeof(*tcm)); 96 memset(pvt, 0, sizeof(*pvt)); 97 98 /* Updating the pointers to SiTA implementation APIs */ 99 tcm->height = height; 100 tcm->width = width; 101 tcm->reserve_2d = sita_reserve_2d; 102 tcm->reserve_1d = sita_reserve_1d; 103 tcm->free = sita_free; 104 tcm->deinit = sita_deinit; 105 tcm->pvt = (void *)pvt; 106 107 spin_lock_init(&(pvt->lock)); 108 109 /* Creating tam map */ 110 pvt->map = kmalloc(sizeof(*pvt->map) * tcm->width, GFP_KERNEL); 111 if (!pvt->map) 112 goto error; 113 114 for (i = 0; i < tcm->width; i++) { 115 pvt->map[i] = 116 kmalloc(sizeof(**pvt->map) * tcm->height, 117 GFP_KERNEL); 118 if (pvt->map[i] == NULL) { 119 while (i--) 120 kfree(pvt->map[i]); 121 kfree(pvt->map); 122 goto error; 123 } 124 } 125 126 if (attr && attr->x <= tcm->width && attr->y <= tcm->height) { 127 pvt->div_pt.x = attr->x; 128 pvt->div_pt.y = attr->y; 129 130 } else { 131 /* Defaulting to 3:1 ratio on width for 2D area split */ 132 /* Defaulting to 3:1 ratio on height for 2D and 1D split */ 133 pvt->div_pt.x = (tcm->width * 3) / 4; 134 pvt->div_pt.y = (tcm->height * 3) / 4; 135 } 136 137 spin_lock(&(pvt->lock)); 138 assign(&area, 0, 0, width - 1, height - 1); 139 fill_area(tcm, &area, NULL); 140 spin_unlock(&(pvt->lock)); 141 return tcm; 142 143error: 144 kfree(tcm); 145 kfree(pvt); 146 return NULL; 147} 148 149static void sita_deinit(struct tcm *tcm) 150{ 151 struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt; 152 struct tcm_area area = {0}; 153 s32 i; 154 155 area.p1.x = tcm->width - 1; 156 area.p1.y = tcm->height - 1; 157 158 spin_lock(&(pvt->lock)); 159 fill_area(tcm, &area, NULL); 160 spin_unlock(&(pvt->lock)); 161 162 for (i = 0; i < tcm->height; i++) 163 kfree(pvt->map[i]); 164 kfree(pvt->map); 165 kfree(pvt); 166} 167 168/** 169 * Reserve a 1D area in the container 170 * 171 * @param num_slots size of 1D area 172 * @param area pointer to the area that will be populated with the 173 * reserved area 174 * 175 * @return 0 on success, non-0 error value on failure. 176 */ 177static s32 sita_reserve_1d(struct tcm *tcm, u32 num_slots, 178 struct tcm_area *area) 179{ 180 s32 ret; 181 struct tcm_area field = {0}; 182 struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt; 183 184 spin_lock(&(pvt->lock)); 185 186 /* Scanning entire container */ 187 assign(&field, tcm->width - 1, tcm->height - 1, 0, 0); 188 189 ret = scan_r2l_b2t_one_dim(tcm, num_slots, &field, area); 190 if (!ret) 191 /* update map */ 192 fill_area(tcm, area, area); 193 194 spin_unlock(&(pvt->lock)); 195 return ret; 196} 197 198/** 199 * Reserve a 2D area in the container 200 * 201 * @param w width 202 * @param h height 203 * @param area pointer to the area that will be populated with the reesrved 204 * area 205 * 206 * @return 0 on success, non-0 error value on failure. 207 */ 208static s32 sita_reserve_2d(struct tcm *tcm, u16 h, u16 w, u8 align, 209 struct tcm_area *area) 210{ 211 s32 ret; 212 struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt; 213 214 /* not supporting more than 64 as alignment */ 215 if (align > 64) 216 return -EINVAL; 217 218 /* we prefer 1, 32 and 64 as alignment */ 219 align = align <= 1 ? 1 : align <= 32 ? 32 : 64; 220 221 spin_lock(&(pvt->lock)); 222 ret = scan_areas_and_find_fit(tcm, w, h, align, area); 223 if (!ret) 224 /* update map */ 225 fill_area(tcm, area, area); 226 227 spin_unlock(&(pvt->lock)); 228 return ret; 229} 230 231/** 232 * Unreserve a previously allocated 2D or 1D area 233 * @param area area to be freed 234 * @return 0 - success 235 */ 236static s32 sita_free(struct tcm *tcm, struct tcm_area *area) 237{ 238 struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt; 239 240 spin_lock(&(pvt->lock)); 241 242 /* check that this is in fact an existing area */ 243 WARN_ON(pvt->map[area->p0.x][area->p0.y] != area || 244 pvt->map[area->p1.x][area->p1.y] != area); 245 246 /* Clear the contents of the associated tiles in the map */ 247 fill_area(tcm, area, NULL); 248 249 spin_unlock(&(pvt->lock)); 250 251 return 0; 252} 253 254/** 255 * Note: In general the cordinates in the scan field area relevant to the can 256 * sweep directions. The scan origin (e.g. top-left corner) will always be 257 * the p0 member of the field. Therfore, for a scan from top-left p0.x <= p1.x 258 * and p0.y <= p1.y; whereas, for a scan from bottom-right p1.x <= p0.x and p1.y 259 * <= p0.y 260 */ 261 262/** 263 * Raster scan horizontally right to left from top to bottom to find a place for 264 * a 2D area of given size inside a scan field. 265 * 266 * @param w width of desired area 267 * @param h height of desired area 268 * @param align desired area alignment 269 * @param area pointer to the area that will be set to the best position 270 * @param field area to scan (inclusive) 271 * 272 * @return 0 on success, non-0 error value on failure. 273 */ 274static s32 scan_r2l_t2b(struct tcm *tcm, u16 w, u16 h, u16 align, 275 struct tcm_area *field, struct tcm_area *area) 276{ 277 s32 x, y; 278 s16 start_x, end_x, start_y, end_y, found_x = -1; 279 struct tcm_area ***map = ((struct sita_pvt *)tcm->pvt)->map; 280 struct score best = {{0}, {0}, {0}, 0}; 281 282 start_x = field->p0.x; 283 end_x = field->p1.x; 284 start_y = field->p0.y; 285 end_y = field->p1.y; 286 287 /* check scan area co-ordinates */ 288 if (field->p0.x < field->p1.x || 289 field->p1.y < field->p0.y) 290 return -EINVAL; 291 292 /* check if allocation would fit in scan area */ 293 if (w > LEN(start_x, end_x) || h > LEN(end_y, start_y)) 294 return -ENOSPC; 295 296 /* adjust start_x and end_y, as allocation would not fit beyond */ 297 start_x = ALIGN_DOWN(start_x - w + 1, align); /* - 1 to be inclusive */ 298 end_y = end_y - h + 1; 299 300 /* check if allocation would still fit in scan area */ 301 if (start_x < end_x) 302 return -ENOSPC; 303 304 /* scan field top-to-bottom, right-to-left */ 305 for (y = start_y; y <= end_y; y++) { 306 for (x = start_x; x >= end_x; x -= align) { 307 if (is_area_free(map, x, y, w, h)) { 308 found_x = x; 309 310 /* update best candidate */ 311 if (update_candidate(tcm, x, y, w, h, field, 312 CR_R2L_T2B, &best)) 313 goto done; 314 315 /* change upper x bound */ 316 end_x = x + 1; 317 break; 318 } else if (map[x][y] && map[x][y]->is2d) { 319 /* step over 2D areas */ 320 x = ALIGN(map[x][y]->p0.x - w + 1, align); 321 } 322 } 323 324 /* break if you find a free area shouldering the scan field */ 325 if (found_x == start_x) 326 break; 327 } 328 329 if (!best.a.tcm) 330 return -ENOSPC; 331done: 332 assign(area, best.a.p0.x, best.a.p0.y, best.a.p1.x, best.a.p1.y); 333 return 0; 334} 335 336/** 337 * Raster scan horizontally left to right from top to bottom to find a place for 338 * a 2D area of given size inside a scan field. 339 * 340 * @param w width of desired area 341 * @param h height of desired area 342 * @param align desired area alignment 343 * @param area pointer to the area that will be set to the best position 344 * @param field area to scan (inclusive) 345 * 346 * @return 0 on success, non-0 error value on failure. 347 */ 348static s32 scan_l2r_t2b(struct tcm *tcm, u16 w, u16 h, u16 align, 349 struct tcm_area *field, struct tcm_area *area) 350{ 351 s32 x, y; 352 s16 start_x, end_x, start_y, end_y, found_x = -1; 353 struct tcm_area ***map = ((struct sita_pvt *)tcm->pvt)->map; 354 struct score best = {{0}, {0}, {0}, 0}; 355 356 start_x = field->p0.x; 357 end_x = field->p1.x; 358 start_y = field->p0.y; 359 end_y = field->p1.y; 360 361 /* check scan area co-ordinates */ 362 if (field->p1.x < field->p0.x || 363 field->p1.y < field->p0.y) 364 return -EINVAL; 365 366 /* check if allocation would fit in scan area */ 367 if (w > LEN(end_x, start_x) || h > LEN(end_y, start_y)) 368 return -ENOSPC; 369 370 start_x = ALIGN(start_x, align); 371 372 /* check if allocation would still fit in scan area */ 373 if (w > LEN(end_x, start_x)) 374 return -ENOSPC; 375 376 /* adjust end_x and end_y, as allocation would not fit beyond */ 377 end_x = end_x - w + 1; /* + 1 to be inclusive */ 378 end_y = end_y - h + 1; 379 380 /* scan field top-to-bottom, left-to-right */ 381 for (y = start_y; y <= end_y; y++) { 382 for (x = start_x; x <= end_x; x += align) { 383 if (is_area_free(map, x, y, w, h)) { 384 found_x = x; 385 386 /* update best candidate */ 387 if (update_candidate(tcm, x, y, w, h, field, 388 CR_L2R_T2B, &best)) 389 goto done; 390 /* change upper x bound */ 391 end_x = x - 1; 392 393 break; 394 } else if (map[x][y] && map[x][y]->is2d) { 395 /* step over 2D areas */ 396 x = ALIGN_DOWN(map[x][y]->p1.x, align); 397 } 398 } 399 400 /* break if you find a free area shouldering the scan field */ 401 if (found_x == start_x) 402 break; 403 } 404 405 if (!best.a.tcm) 406 return -ENOSPC; 407done: 408 assign(area, best.a.p0.x, best.a.p0.y, best.a.p1.x, best.a.p1.y); 409 return 0; 410} 411 412/** 413 * Raster scan horizontally right to left from bottom to top to find a place 414 * for a 1D area of given size inside a scan field. 415 * 416 * @param num_slots size of desired area 417 * @param align desired area alignment 418 * @param area pointer to the area that will be set to the best 419 * position 420 * @param field area to scan (inclusive) 421 * 422 * @return 0 on success, non-0 error value on failure. 423 */ 424static s32 scan_r2l_b2t_one_dim(struct tcm *tcm, u32 num_slots, 425 struct tcm_area *field, struct tcm_area *area) 426{ 427 s32 found = 0; 428 s16 x, y; 429 struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt; 430 struct tcm_area *p; 431 432 /* check scan area co-ordinates */ 433 if (field->p0.y < field->p1.y) 434 return -EINVAL; 435 436 /** 437 * Currently we only support full width 1D scan field, which makes sense 438 * since 1D slot-ordering spans the full container width. 439 */ 440 if (tcm->width != field->p0.x - field->p1.x + 1) 441 return -EINVAL; 442 443 /* check if allocation would fit in scan area */ 444 if (num_slots > tcm->width * LEN(field->p0.y, field->p1.y)) 445 return -ENOSPC; 446 447 x = field->p0.x; 448 y = field->p0.y; 449 450 /* find num_slots consecutive free slots to the left */ 451 while (found < num_slots) { 452 if (y < 0) 453 return -ENOSPC; 454 455 /* remember bottom-right corner */ 456 if (found == 0) { 457 area->p1.x = x; 458 area->p1.y = y; 459 } 460 461 /* skip busy regions */ 462 p = pvt->map[x][y]; 463 if (p) { 464 /* move to left of 2D areas, top left of 1D */ 465 x = p->p0.x; 466 if (!p->is2d) 467 y = p->p0.y; 468 469 /* start over */ 470 found = 0; 471 } else { 472 /* count consecutive free slots */ 473 found++; 474 if (found == num_slots) 475 break; 476 } 477 478 /* move to the left */ 479 if (x == 0) 480 y--; 481 x = (x ? : tcm->width) - 1; 482 483 } 484 485 /* set top-left corner */ 486 area->p0.x = x; 487 area->p0.y = y; 488 return 0; 489} 490 491/** 492 * Find a place for a 2D area of given size inside a scan field based on its 493 * alignment needs. 494 * 495 * @param w width of desired area 496 * @param h height of desired area 497 * @param align desired area alignment 498 * @param area pointer to the area that will be set to the best position 499 * 500 * @return 0 on success, non-0 error value on failure. 501 */ 502static s32 scan_areas_and_find_fit(struct tcm *tcm, u16 w, u16 h, u16 align, 503 struct tcm_area *area) 504{ 505 s32 ret = 0; 506 struct tcm_area field = {0}; 507 u16 boundary_x, boundary_y; 508 struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt; 509 510 if (align > 1) { 511 /* prefer top-left corner */ 512 boundary_x = pvt->div_pt.x - 1; 513 boundary_y = pvt->div_pt.y - 1; 514 515 /* expand width and height if needed */ 516 if (w > pvt->div_pt.x) 517 boundary_x = tcm->width - 1; 518 if (h > pvt->div_pt.y) 519 boundary_y = tcm->height - 1; 520 521 assign(&field, 0, 0, boundary_x, boundary_y); 522 ret = scan_l2r_t2b(tcm, w, h, align, &field, area); 523 524 /* scan whole container if failed, but do not scan 2x */ 525 if (ret != 0 && (boundary_x != tcm->width - 1 || 526 boundary_y != tcm->height - 1)) { 527 /* scan the entire container if nothing found */ 528 assign(&field, 0, 0, tcm->width - 1, tcm->height - 1); 529 ret = scan_l2r_t2b(tcm, w, h, align, &field, area); 530 } 531 } else if (align == 1) { 532 /* prefer top-right corner */ 533 boundary_x = pvt->div_pt.x; 534 boundary_y = pvt->div_pt.y - 1; 535 536 /* expand width and height if needed */ 537 if (w > (tcm->width - pvt->div_pt.x)) 538 boundary_x = 0; 539 if (h > pvt->div_pt.y) 540 boundary_y = tcm->height - 1; 541 542 assign(&field, tcm->width - 1, 0, boundary_x, boundary_y); 543 ret = scan_r2l_t2b(tcm, w, h, align, &field, area); 544 545 /* scan whole container if failed, but do not scan 2x */ 546 if (ret != 0 && (boundary_x != 0 || 547 boundary_y != tcm->height - 1)) { 548 /* scan the entire container if nothing found */ 549 assign(&field, tcm->width - 1, 0, 0, tcm->height - 1); 550 ret = scan_r2l_t2b(tcm, w, h, align, &field, 551 area); 552 } 553 } 554 555 return ret; 556} 557 558/* check if an entire area is free */ 559static s32 is_area_free(struct tcm_area ***map, u16 x0, u16 y0, u16 w, u16 h) 560{ 561 u16 x = 0, y = 0; 562 for (y = y0; y < y0 + h; y++) { 563 for (x = x0; x < x0 + w; x++) { 564 if (map[x][y]) 565 return false; 566 } 567 } 568 return true; 569} 570 571/* fills an area with a parent tcm_area */ 572static void fill_area(struct tcm *tcm, struct tcm_area *area, 573 struct tcm_area *parent) 574{ 575 s32 x, y; 576 struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt; 577 struct tcm_area a, a_; 578 579 /* set area's tcm; otherwise, enumerator considers it invalid */ 580 area->tcm = tcm; 581 582 tcm_for_each_slice(a, *area, a_) { 583 for (x = a.p0.x; x <= a.p1.x; ++x) 584 for (y = a.p0.y; y <= a.p1.y; ++y) 585 pvt->map[x][y] = parent; 586 587 } 588} 589 590/** 591 * Compares a candidate area to the current best area, and if it is a better 592 * fit, it updates the best to this one. 593 * 594 * @param x0, y0, w, h top, left, width, height of candidate area 595 * @param field scan field 596 * @param criteria scan criteria 597 * @param best best candidate and its scores 598 * 599 * @return 1 (true) if the candidate area is known to be the final best, so no 600 * more searching should be performed 601 */ 602static s32 update_candidate(struct tcm *tcm, u16 x0, u16 y0, u16 w, u16 h, 603 struct tcm_area *field, s32 criteria, 604 struct score *best) 605{ 606 struct score me; /* score for area */ 607 608 /* 609 * NOTE: For horizontal bias we always give the first found, because our 610 * scan is horizontal-raster-based and the first candidate will always 611 * have the horizontal bias. 612 */ 613 bool first = criteria & CR_BIAS_HORIZONTAL; 614 615 assign(&me.a, x0, y0, x0 + w - 1, y0 + h - 1); 616 617 /* calculate score for current candidate */ 618 if (!first) { 619 get_neighbor_stats(tcm, &me.a, &me.n); 620 me.neighs = me.n.edge + me.n.busy; 621 get_nearness_factor(field, &me.a, &me.f); 622 } 623 624 /* the 1st candidate is always the best */ 625 if (!best->a.tcm) 626 goto better; 627 628 BUG_ON(first); 629 630 /* diagonal balance check */ 631 if ((criteria & CR_DIAGONAL_BALANCE) && 632 best->neighs <= me.neighs && 633 (best->neighs < me.neighs || 634 /* this implies that neighs and occupied match */ 635 best->n.busy < me.n.busy || 636 (best->n.busy == me.n.busy && 637 /* check the nearness factor */ 638 best->f.x + best->f.y > me.f.x + me.f.y))) 639 goto better; 640 641 /* not better, keep going */ 642 return 0; 643 644better: 645 /* save current area as best */ 646 memcpy(best, &me, sizeof(me)); 647 best->a.tcm = tcm; 648 return first; 649} 650 651/** 652 * Calculate the nearness factor of an area in a search field. The nearness 653 * factor is smaller if the area is closer to the search origin. 654 */ 655static void get_nearness_factor(struct tcm_area *field, struct tcm_area *area, 656 struct nearness_factor *nf) 657{ 658 /** 659 * Using signed math as field coordinates may be reversed if 660 * search direction is right-to-left or bottom-to-top. 661 */ 662 nf->x = (s32)(area->p0.x - field->p0.x) * 1000 / 663 (field->p1.x - field->p0.x); 664 nf->y = (s32)(area->p0.y - field->p0.y) * 1000 / 665 (field->p1.y - field->p0.y); 666} 667 668/* get neighbor statistics */ 669static void get_neighbor_stats(struct tcm *tcm, struct tcm_area *area, 670 struct neighbor_stats *stat) 671{ 672 s16 x = 0, y = 0; 673 struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt; 674 675 /* Clearing any exisiting values */ 676 memset(stat, 0, sizeof(*stat)); 677 678 /* process top & bottom edges */ 679 for (x = area->p0.x; x <= area->p1.x; x++) { 680 if (area->p0.y == 0) 681 stat->edge++; 682 else if (pvt->map[x][area->p0.y - 1]) 683 stat->busy++; 684 685 if (area->p1.y == tcm->height - 1) 686 stat->edge++; 687 else if (pvt->map[x][area->p1.y + 1]) 688 stat->busy++; 689 } 690 691 /* process left & right edges */ 692 for (y = area->p0.y; y <= area->p1.y; ++y) { 693 if (area->p0.x == 0) 694 stat->edge++; 695 else if (pvt->map[area->p0.x - 1][y]) 696 stat->busy++; 697 698 if (area->p1.x == tcm->width - 1) 699 stat->edge++; 700 else if (pvt->map[area->p1.x + 1][y]) 701 stat->busy++; 702 } 703} 704