Commit 0d6fa53f authored by Andy Gross's avatar Andy Gross Committed by Tomi Valkeinen

drm/omap: Use bitmaps for TILER placement

Modified Tiler placement to utilize bitmaps for bookkeeping and
all placement algorithms.  This resulted in a substantial savings
in time for all Tiler reservation and free operations.  Typical
savings are in the range of 28% decrease in time taken with larger
buffers showing a 80%+ decrease.
Signed-off-by: default avatarAndy Gross <andy.gross@ti.com>
Signed-off-by: default avatarTomi Valkeinen <tomi.valkeinen@ti.com>
parent 73d77107
...@@ -363,6 +363,7 @@ struct tiler_block *tiler_reserve_2d(enum tiler_fmt fmt, uint16_t w, ...@@ -363,6 +363,7 @@ struct tiler_block *tiler_reserve_2d(enum tiler_fmt fmt, uint16_t w,
u32 min_align = 128; u32 min_align = 128;
int ret; int ret;
unsigned long flags; unsigned long flags;
size_t slot_bytes;
BUG_ON(!validfmt(fmt)); BUG_ON(!validfmt(fmt));
...@@ -371,13 +372,15 @@ struct tiler_block *tiler_reserve_2d(enum tiler_fmt fmt, uint16_t w, ...@@ -371,13 +372,15 @@ struct tiler_block *tiler_reserve_2d(enum tiler_fmt fmt, uint16_t w,
h = DIV_ROUND_UP(h, geom[fmt].slot_h); h = DIV_ROUND_UP(h, geom[fmt].slot_h);
/* convert alignment to slots */ /* convert alignment to slots */
min_align = max(min_align, (geom[fmt].slot_w * geom[fmt].cpp)); slot_bytes = geom[fmt].slot_w * geom[fmt].cpp;
align = ALIGN(align, min_align); min_align = max(min_align, slot_bytes);
align /= geom[fmt].slot_w * geom[fmt].cpp; align = (align > min_align) ? ALIGN(align, min_align) : min_align;
align /= slot_bytes;
block->fmt = fmt; block->fmt = fmt;
ret = tcm_reserve_2d(containers[fmt], w, h, align, &block->area); ret = tcm_reserve_2d(containers[fmt], w, h, align, -1, slot_bytes,
&block->area);
if (ret) { if (ret) {
kfree(block); kfree(block);
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
...@@ -739,8 +742,7 @@ static int omap_dmm_probe(struct platform_device *dev) ...@@ -739,8 +742,7 @@ static int omap_dmm_probe(struct platform_device *dev)
programming during reill operations */ programming during reill operations */
for (i = 0; i < omap_dmm->num_lut; i++) { for (i = 0; i < omap_dmm->num_lut; i++) {
omap_dmm->tcm[i] = sita_init(omap_dmm->container_width, omap_dmm->tcm[i] = sita_init(omap_dmm->container_width,
omap_dmm->container_height, omap_dmm->container_height);
NULL);
if (!omap_dmm->tcm[i]) { if (!omap_dmm->tcm[i]) {
dev_err(&dev->dev, "failed to allocate container\n"); dev_err(&dev->dev, "failed to allocate container\n");
......
...@@ -5,8 +5,9 @@ ...@@ -5,8 +5,9 @@
* *
* Authors: Ravi Ramachandra <r.ramachandra@ti.com>, * Authors: Ravi Ramachandra <r.ramachandra@ti.com>,
* Lajos Molnar <molnar@ti.com> * Lajos Molnar <molnar@ti.com>
* Andy Gross <andy.gross@ti.com>
* *
* Copyright (C) 2009-2010 Texas Instruments, Inc. * Copyright (C) 2012 Texas Instruments, Inc.
* *
* This package is free software; you can redistribute it and/or modify * This package is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
...@@ -17,684 +18,244 @@ ...@@ -17,684 +18,244 @@
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
* *
*/ */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/bitmap.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/spinlock.h> #include "tcm.h"
#include "tcm-sita.h" static unsigned long mask[8];
/*
#define ALIGN_DOWN(value, align) ((value) & ~((align) - 1)) * pos position in bitmap
* w width in slots
/* Individual selection criteria for different scan areas */ * h height in slots
static s32 CR_L2R_T2B = CR_BIAS_HORIZONTAL; * map ptr to bitmap
static s32 CR_R2L_T2B = CR_DIAGONAL_BALANCE; * stride slots in a row
/*********************************************
* TCM API - Sita Implementation
*********************************************/
static s32 sita_reserve_2d(struct tcm *tcm, u16 h, u16 w, u8 align,
struct tcm_area *area);
static s32 sita_reserve_1d(struct tcm *tcm, u32 slots, struct tcm_area *area);
static s32 sita_free(struct tcm *tcm, struct tcm_area *area);
static void sita_deinit(struct tcm *tcm);
/*********************************************
* Main Scanner functions
*********************************************/
static s32 scan_areas_and_find_fit(struct tcm *tcm, u16 w, u16 h, u16 align,
struct tcm_area *area);
static s32 scan_l2r_t2b(struct tcm *tcm, u16 w, u16 h, u16 align,
struct tcm_area *field, struct tcm_area *area);
static s32 scan_r2l_t2b(struct tcm *tcm, u16 w, u16 h, u16 align,
struct tcm_area *field, struct tcm_area *area);
static s32 scan_r2l_b2t_one_dim(struct tcm *tcm, u32 num_slots,
struct tcm_area *field, struct tcm_area *area);
/*********************************************
* Support Infrastructure Methods
*********************************************/
static s32 is_area_free(struct tcm_area ***map, u16 x0, u16 y0, u16 w, u16 h);
static s32 update_candidate(struct tcm *tcm, u16 x0, u16 y0, u16 w, u16 h,
struct tcm_area *field, s32 criteria,
struct score *best);
static void get_nearness_factor(struct tcm_area *field,
struct tcm_area *candidate,
struct nearness_factor *nf);
static void get_neighbor_stats(struct tcm *tcm, struct tcm_area *area,
struct neighbor_stats *stat);
static void fill_area(struct tcm *tcm,
struct tcm_area *area, struct tcm_area *parent);
/*********************************************/
/*********************************************
* Utility Methods
*********************************************/
struct tcm *sita_init(u16 width, u16 height, struct tcm_pt *attr)
{
struct tcm *tcm;
struct sita_pvt *pvt;
struct tcm_area area = {0};
s32 i;
if (width == 0 || height == 0)
return NULL;
tcm = kzalloc(sizeof(*tcm), GFP_KERNEL);
pvt = kzalloc(sizeof(*pvt), GFP_KERNEL);
if (!tcm || !pvt)
goto error;
/* Updating the pointers to SiTA implementation APIs */
tcm->height = height;
tcm->width = width;
tcm->reserve_2d = sita_reserve_2d;
tcm->reserve_1d = sita_reserve_1d;
tcm->free = sita_free;
tcm->deinit = sita_deinit;
tcm->pvt = (void *)pvt;
spin_lock_init(&(pvt->lock));
/* Creating tam map */
pvt->map = kmalloc(sizeof(*pvt->map) * tcm->width, GFP_KERNEL);
if (!pvt->map)
goto error;
for (i = 0; i < tcm->width; i++) {
pvt->map[i] =
kmalloc(sizeof(**pvt->map) * tcm->height,
GFP_KERNEL);
if (pvt->map[i] == NULL) {
while (i--)
kfree(pvt->map[i]);
kfree(pvt->map);
goto error;
}
}
if (attr && attr->x <= tcm->width && attr->y <= tcm->height) {
pvt->div_pt.x = attr->x;
pvt->div_pt.y = attr->y;
} else {
/* Defaulting to 3:1 ratio on width for 2D area split */
/* Defaulting to 3:1 ratio on height for 2D and 1D split */
pvt->div_pt.x = (tcm->width * 3) / 4;
pvt->div_pt.y = (tcm->height * 3) / 4;
}
spin_lock(&(pvt->lock));
assign(&area, 0, 0, width - 1, height - 1);
fill_area(tcm, &area, NULL);
spin_unlock(&(pvt->lock));
return tcm;
error:
kfree(tcm);
kfree(pvt);
return NULL;
}
static void sita_deinit(struct tcm *tcm)
{
struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
struct tcm_area area = {0};
s32 i;
area.p1.x = tcm->width - 1;
area.p1.y = tcm->height - 1;
spin_lock(&(pvt->lock));
fill_area(tcm, &area, NULL);
spin_unlock(&(pvt->lock));
for (i = 0; i < tcm->height; i++)
kfree(pvt->map[i]);
kfree(pvt->map);
kfree(pvt);
}
/**
* Reserve a 1D area in the container
*
* @param num_slots size of 1D area
* @param area pointer to the area that will be populated with the
* reserved area
*
* @return 0 on success, non-0 error value on failure.
*/
static s32 sita_reserve_1d(struct tcm *tcm, u32 num_slots,
struct tcm_area *area)
{
s32 ret;
struct tcm_area field = {0};
struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
spin_lock(&(pvt->lock));
/* Scanning entire container */
assign(&field, tcm->width - 1, tcm->height - 1, 0, 0);
ret = scan_r2l_b2t_one_dim(tcm, num_slots, &field, area);
if (!ret)
/* update map */
fill_area(tcm, area, area);
spin_unlock(&(pvt->lock));
return ret;
}
/**
* Reserve a 2D area in the container
*
* @param w width
* @param h height
* @param area pointer to the area that will be populated with the reserved
* area
*
* @return 0 on success, non-0 error value on failure.
*/ */
static s32 sita_reserve_2d(struct tcm *tcm, u16 h, u16 w, u8 align, static void free_slots(unsigned long pos, uint16_t w, uint16_t h,
struct tcm_area *area) unsigned long *map, uint16_t stride)
{ {
s32 ret; int i;
struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
/* not supporting more than 64 as alignment */
if (align > 64)
return -EINVAL;
/* we prefer 1, 32 and 64 as alignment */ for (i = 0; i < h; i++, pos += stride)
align = align <= 1 ? 1 : align <= 32 ? 32 : 64; bitmap_clear(map, pos, w);
spin_lock(&(pvt->lock));
ret = scan_areas_and_find_fit(tcm, w, h, align, area);
if (!ret)
/* update map */
fill_area(tcm, area, area);
spin_unlock(&(pvt->lock));
return ret;
} }
/** /*
* Unreserve a previously allocated 2D or 1D area * w width in slots
* @param area area to be freed * pos ptr to position
* @return 0 - success * map ptr to bitmap
* num_bits number of bits in bitmap
*/ */
static s32 sita_free(struct tcm *tcm, struct tcm_area *area) static int r2l_b2t_1d(uint16_t w, unsigned long *pos, unsigned long *map,
size_t num_bits)
{ {
struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt; unsigned long search_count = 0;
unsigned long bit;
bool area_found = false;
spin_lock(&(pvt->lock)); *pos = num_bits - w;
/* check that this is in fact an existing area */
WARN_ON(pvt->map[area->p0.x][area->p0.y] != area ||
pvt->map[area->p1.x][area->p1.y] != area);
/* Clear the contents of the associated tiles in the map */
fill_area(tcm, area, NULL);
spin_unlock(&(pvt->lock));
return 0;
}
/** while (search_count < num_bits) {
* Note: In general the cordinates in the scan field area relevant to the can bit = find_next_bit(map, num_bits, *pos);
* sweep directions. The scan origin (e.g. top-left corner) will always be
* the p0 member of the field. Therfore, for a scan from top-left p0.x <= p1.x
* and p0.y <= p1.y; whereas, for a scan from bottom-right p1.x <= p0.x and p1.y
* <= p0.y
*/
/** if (bit - *pos >= w) {
* Raster scan horizontally right to left from top to bottom to find a place for /* found a long enough free area */
* a 2D area of given size inside a scan field. bitmap_set(map, *pos, w);
* area_found = true;
* @param w width of desired area
* @param h height of desired area
* @param align desired area alignment
* @param area pointer to the area that will be set to the best position
* @param field area to scan (inclusive)
*
* @return 0 on success, non-0 error value on failure.
*/
static s32 scan_r2l_t2b(struct tcm *tcm, u16 w, u16 h, u16 align,
struct tcm_area *field, struct tcm_area *area)
{
s32 x, y;
s16 start_x, end_x, start_y, end_y, found_x = -1;
struct tcm_area ***map = ((struct sita_pvt *)tcm->pvt)->map;
struct score best = {{0}, {0}, {0}, 0};
start_x = field->p0.x;
end_x = field->p1.x;
start_y = field->p0.y;
end_y = field->p1.y;
/* check scan area co-ordinates */
if (field->p0.x < field->p1.x ||
field->p1.y < field->p0.y)
return -EINVAL;
/* check if allocation would fit in scan area */
if (w > LEN(start_x, end_x) || h > LEN(end_y, start_y))
return -ENOSPC;
/* adjust start_x and end_y, as allocation would not fit beyond */
start_x = ALIGN_DOWN(start_x - w + 1, align); /* - 1 to be inclusive */
end_y = end_y - h + 1;
/* check if allocation would still fit in scan area */
if (start_x < end_x)
return -ENOSPC;
/* scan field top-to-bottom, right-to-left */
for (y = start_y; y <= end_y; y++) {
for (x = start_x; x >= end_x; x -= align) {
if (is_area_free(map, x, y, w, h)) {
found_x = x;
/* update best candidate */
if (update_candidate(tcm, x, y, w, h, field,
CR_R2L_T2B, &best))
goto done;
/* change upper x bound */
end_x = x + 1;
break; break;
} else if (map[x][y] && map[x][y]->is2d) {
/* step over 2D areas */
x = ALIGN(map[x][y]->p0.x - w + 1, align);
}
} }
/* break if you find a free area shouldering the scan field */ search_count = num_bits - bit + w;
if (found_x == start_x) *pos = bit - w;
break;
} }
if (!best.a.tcm) return (area_found) ? 0 : -ENOMEM;
return -ENOSPC;
done:
assign(area, best.a.p0.x, best.a.p0.y, best.a.p1.x, best.a.p1.y);
return 0;
} }
/** /*
* Raster scan horizontally left to right from top to bottom to find a place for * w = width in slots
* a 2D area of given size inside a scan field. * h = height in slots
* * a = align in slots (mask, 2^n-1, 0 is unaligned)
* @param w width of desired area * offset = offset in bytes from 4KiB
* @param h height of desired area * pos = position in bitmap for buffer
* @param align desired area alignment * map = bitmap ptr
* @param area pointer to the area that will be set to the best position * num_bits = size of bitmap
* @param field area to scan (inclusive) * stride = bits in one row of container
*
* @return 0 on success, non-0 error value on failure.
*/ */
static s32 scan_l2r_t2b(struct tcm *tcm, u16 w, u16 h, u16 align, static int l2r_t2b(uint16_t w, uint16_t h, uint16_t a, int16_t offset,
struct tcm_area *field, struct tcm_area *area) unsigned long *pos, unsigned long slot_bytes,
unsigned long *map, size_t num_bits, size_t slot_stride)
{ {
s32 x, y; int i;
s16 start_x, end_x, start_y, end_y, found_x = -1; unsigned long index;
struct tcm_area ***map = ((struct sita_pvt *)tcm->pvt)->map; bool area_free;
struct score best = {{0}, {0}, {0}, 0}; unsigned long slots_per_band = PAGE_SIZE / slot_bytes;
unsigned long bit_offset = (offset > 0) ? offset / slot_bytes : 0;
start_x = field->p0.x; unsigned long curr_bit = bit_offset;
end_x = field->p1.x;
start_y = field->p0.y; /* reset alignment to 1 if we are matching a specific offset */
end_y = field->p1.y; /* adjust alignment - 1 to get to the format expected in bitmaps */
a = (offset > 0) ? 0 : a - 1;
/* check scan area co-ordinates */
if (field->p1.x < field->p0.x || /* FIXME Return error if slots_per_band > stride */
field->p1.y < field->p0.y)
return -EINVAL; while (curr_bit < num_bits) {
*pos = bitmap_find_next_zero_area(map, num_bits, curr_bit, w,
/* check if allocation would fit in scan area */ a);
if (w > LEN(end_x, start_x) || h > LEN(end_y, start_y))
return -ENOSPC; /* skip forward if we are not at right offset */
if (bit_offset > 0 && (*pos % slots_per_band != bit_offset)) {
start_x = ALIGN(start_x, align); curr_bit = ALIGN(*pos, slots_per_band) + bit_offset;
continue;
/* check if allocation would still fit in scan area */
if (w > LEN(end_x, start_x))
return -ENOSPC;
/* adjust end_x and end_y, as allocation would not fit beyond */
end_x = end_x - w + 1; /* + 1 to be inclusive */
end_y = end_y - h + 1;
/* scan field top-to-bottom, left-to-right */
for (y = start_y; y <= end_y; y++) {
for (x = start_x; x <= end_x; x += align) {
if (is_area_free(map, x, y, w, h)) {
found_x = x;
/* update best candidate */
if (update_candidate(tcm, x, y, w, h, field,
CR_L2R_T2B, &best))
goto done;
/* change upper x bound */
end_x = x - 1;
break;
} else if (map[x][y] && map[x][y]->is2d) {
/* step over 2D areas */
x = ALIGN_DOWN(map[x][y]->p1.x, align);
}
} }
/* break if you find a free area shouldering the scan field */ /* skip forward to next row if we overlap end of row */
if (found_x == start_x) if ((*pos % slot_stride) + w > slot_stride) {
break; curr_bit = ALIGN(*pos, slot_stride) + bit_offset;
continue;
} }
if (!best.a.tcm) /* TODO: Handle overlapping 4K boundaries */
return -ENOSPC;
done:
assign(area, best.a.p0.x, best.a.p0.y, best.a.p1.x, best.a.p1.y);
return 0;
}
/**
* Raster scan horizontally right to left from bottom to top to find a place
* for a 1D area of given size inside a scan field.
*
* @param num_slots size of desired area
* @param align desired area alignment
* @param area pointer to the area that will be set to the best
* position
* @param field area to scan (inclusive)
*
* @return 0 on success, non-0 error value on failure.
*/
static s32 scan_r2l_b2t_one_dim(struct tcm *tcm, u32 num_slots,
struct tcm_area *field, struct tcm_area *area)
{
s32 found = 0;
s16 x, y;
struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
struct tcm_area *p;
/* check scan area co-ordinates */
if (field->p0.y < field->p1.y)
return -EINVAL;
/**
* Currently we only support full width 1D scan field, which makes sense
* since 1D slot-ordering spans the full container width.
*/
if (tcm->width != field->p0.x - field->p1.x + 1)
return -EINVAL;
/* check if allocation would fit in scan area */ /* break out of look if we will go past end of container */
if (num_slots > tcm->width * LEN(field->p0.y, field->p1.y)) if ((*pos + slot_stride * h) > num_bits)
return -ENOSPC; break;
x = field->p0.x; /* generate mask that represents out matching pattern */
y = field->p0.y; bitmap_clear(mask, 0, slot_stride);
bitmap_set(mask, (*pos % BITS_PER_LONG), w);
/* find num_slots consecutive free slots to the left */ /* assume the area is free until we find an overlap */
while (found < num_slots) { area_free = true;
if (y < 0)
return -ENOSPC;
/* remember bottom-right corner */ /* check subsequent rows to see if complete area is free */
if (found == 0) { for (i = 1; i < h; i++) {
area->p1.x = x; index = *pos / BITS_PER_LONG + i * 8;
area->p1.y = y; if (bitmap_intersects(&map[index], mask,
(*pos % BITS_PER_LONG) + w)) {
area_free = false;
break;
}
} }
/* skip busy regions */ if (area_free)
p = pvt->map[x][y];
if (p) {
/* move to left of 2D areas, top left of 1D */
x = p->p0.x;
if (!p->is2d)
y = p->p0.y;
/* start over */
found = 0;
} else {
/* count consecutive free slots */
found++;
if (found == num_slots)
break; break;
}
/* move to the left */ /* go forward past this match */
if (x == 0) if (bit_offset > 0)
y--; curr_bit = ALIGN(*pos, slots_per_band) + bit_offset;
x = (x ? : tcm->width) - 1; else
curr_bit = *pos + a + 1;
}
if (area_free) {
/* set area as in-use. iterate over rows */
for (i = 0, index = *pos; i < h; i++, index += slot_stride)
bitmap_set(map, index, w);
} }
/* set top-left corner */ return (area_free) ? 0 : -ENOMEM;
area->p0.x = x;
area->p0.y = y;
return 0;
} }
/** static s32 sita_reserve_1d(struct tcm *tcm, u32 num_slots,
* Find a place for a 2D area of given size inside a scan field based on its
* alignment needs.
*
* @param w width of desired area
* @param h height of desired area
* @param align desired area alignment
* @param area pointer to the area that will be set to the best position
*
* @return 0 on success, non-0 error value on failure.
*/
static s32 scan_areas_and_find_fit(struct tcm *tcm, u16 w, u16 h, u16 align,
struct tcm_area *area) struct tcm_area *area)
{ {
s32 ret = 0; unsigned long pos;
struct tcm_area field = {0}; int ret;
u16 boundary_x, boundary_y;
struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt; spin_lock(&(tcm->lock));
ret = r2l_b2t_1d(num_slots, &pos, tcm->bitmap, tcm->map_size);
if (align > 1) { if (!ret) {
/* prefer top-left corner */ area->p0.x = pos % tcm->width;
boundary_x = pvt->div_pt.x - 1; area->p0.y = pos / tcm->width;
boundary_y = pvt->div_pt.y - 1; area->p1.x = (pos + num_slots - 1) % tcm->width;
area->p1.y = (pos + num_slots - 1) / tcm->width;
/* expand width and height if needed */
if (w > pvt->div_pt.x)
boundary_x = tcm->width - 1;
if (h > pvt->div_pt.y)
boundary_y = tcm->height - 1;
assign(&field, 0, 0, boundary_x, boundary_y);
ret = scan_l2r_t2b(tcm, w, h, align, &field, area);
/* scan whole container if failed, but do not scan 2x */
if (ret != 0 && (boundary_x != tcm->width - 1 ||
boundary_y != tcm->height - 1)) {
/* scan the entire container if nothing found */
assign(&field, 0, 0, tcm->width - 1, tcm->height - 1);
ret = scan_l2r_t2b(tcm, w, h, align, &field, area);
}
} else if (align == 1) {
/* prefer top-right corner */
boundary_x = pvt->div_pt.x;
boundary_y = pvt->div_pt.y - 1;
/* expand width and height if needed */
if (w > (tcm->width - pvt->div_pt.x))
boundary_x = 0;
if (h > pvt->div_pt.y)
boundary_y = tcm->height - 1;
assign(&field, tcm->width - 1, 0, boundary_x, boundary_y);
ret = scan_r2l_t2b(tcm, w, h, align, &field, area);
/* scan whole container if failed, but do not scan 2x */
if (ret != 0 && (boundary_x != 0 ||
boundary_y != tcm->height - 1)) {
/* scan the entire container if nothing found */
assign(&field, tcm->width - 1, 0, 0, tcm->height - 1);
ret = scan_r2l_t2b(tcm, w, h, align, &field,
area);
}
} }
spin_unlock(&(tcm->lock));
return ret; return ret;
} }
/* check if an entire area is free */ static s32 sita_reserve_2d(struct tcm *tcm, u16 h, u16 w, u16 align,
static s32 is_area_free(struct tcm_area ***map, u16 x0, u16 y0, u16 w, u16 h) int16_t offset, uint16_t slot_bytes,
struct tcm_area *area)
{ {
u16 x = 0, y = 0; unsigned long pos;
for (y = y0; y < y0 + h; y++) { int ret;
for (x = x0; x < x0 + w; x++) {
if (map[x][y]) spin_lock(&(tcm->lock));
return false; ret = l2r_t2b(w, h, align, offset, &pos, slot_bytes, tcm->bitmap,
} tcm->map_size, tcm->width);
if (!ret) {
area->p0.x = pos % tcm->width;
area->p0.y = pos / tcm->width;
area->p1.x = area->p0.x + w - 1;
area->p1.y = area->p0.y + h - 1;
} }
return true; spin_unlock(&(tcm->lock));
return ret;
} }
/* fills an area with a parent tcm_area */ static void sita_deinit(struct tcm *tcm)
static void fill_area(struct tcm *tcm, struct tcm_area *area,
struct tcm_area *parent)
{ {
s32 x, y; kfree(tcm);
struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
struct tcm_area a, a_;
/* set area's tcm; otherwise, enumerator considers it invalid */
area->tcm = tcm;
tcm_for_each_slice(a, *area, a_) {
for (x = a.p0.x; x <= a.p1.x; ++x)
for (y = a.p0.y; y <= a.p1.y; ++y)
pvt->map[x][y] = parent;
}
} }
/** static s32 sita_free(struct tcm *tcm, struct tcm_area *area)
* Compares a candidate area to the current best area, and if it is a better
* fit, it updates the best to this one.
*
* @param x0, y0, w, h top, left, width, height of candidate area
* @param field scan field
* @param criteria scan criteria
* @param best best candidate and its scores
*
* @return 1 (true) if the candidate area is known to be the final best, so no
* more searching should be performed
*/
static s32 update_candidate(struct tcm *tcm, u16 x0, u16 y0, u16 w, u16 h,
struct tcm_area *field, s32 criteria,
struct score *best)
{ {
struct score me; /* score for area */ unsigned long pos;
uint16_t w, h;
/* pos = area->p0.x + area->p0.y * tcm->width;
* NOTE: For horizontal bias we always give the first found, because our if (area->is2d) {
* scan is horizontal-raster-based and the first candidate will always w = area->p1.x - area->p0.x + 1;
* have the horizontal bias. h = area->p1.y - area->p0.y + 1;
*/ } else {
bool first = criteria & CR_BIAS_HORIZONTAL; w = area->p1.x + area->p1.y * tcm->width - pos + 1;
h = 1;
assign(&me.a, x0, y0, x0 + w - 1, y0 + h - 1);
/* calculate score for current candidate */
if (!first) {
get_neighbor_stats(tcm, &me.a, &me.n);
me.neighs = me.n.edge + me.n.busy;
get_nearness_factor(field, &me.a, &me.f);
} }
/* the 1st candidate is always the best */ spin_lock(&(tcm->lock));
if (!best->a.tcm) free_slots(pos, w, h, tcm->bitmap, tcm->width);
goto better; spin_unlock(&(tcm->lock));
return 0;
}
BUG_ON(first); struct tcm *sita_init(u16 width, u16 height)
{
struct tcm *tcm;
size_t map_size = BITS_TO_LONGS(width*height) * sizeof(unsigned long);
/* diagonal balance check */ if (width == 0 || height == 0)
if ((criteria & CR_DIAGONAL_BALANCE) && return NULL;
best->neighs <= me.neighs &&
(best->neighs < me.neighs ||
/* this implies that neighs and occupied match */
best->n.busy < me.n.busy ||
(best->n.busy == me.n.busy &&
/* check the nearness factor */
best->f.x + best->f.y > me.f.x + me.f.y)))
goto better;
/* not better, keep going */ tcm = kzalloc(sizeof(*tcm) + map_size, GFP_KERNEL);
return 0; if (!tcm)
goto error;
better: /* Updating the pointers to SiTA implementation APIs */
/* save current area as best */ tcm->height = height;
memcpy(best, &me, sizeof(me)); tcm->width = width;
best->a.tcm = tcm; tcm->reserve_2d = sita_reserve_2d;
return first; tcm->reserve_1d = sita_reserve_1d;
} tcm->free = sita_free;
tcm->deinit = sita_deinit;
/** spin_lock_init(&tcm->lock);
* Calculate the nearness factor of an area in a search field. The nearness tcm->bitmap = (unsigned long *)(tcm + 1);
* factor is smaller if the area is closer to the search origin. bitmap_clear(tcm->bitmap, 0, width*height);
*/
static void get_nearness_factor(struct tcm_area *field, struct tcm_area *area,
struct nearness_factor *nf)
{
/**
* Using signed math as field coordinates may be reversed if
* search direction is right-to-left or bottom-to-top.
*/
nf->x = (s32)(area->p0.x - field->p0.x) * 1000 /
(field->p1.x - field->p0.x);
nf->y = (s32)(area->p0.y - field->p0.y) * 1000 /
(field->p1.y - field->p0.y);
}
/* get neighbor statistics */ tcm->map_size = width*height;
static void get_neighbor_stats(struct tcm *tcm, struct tcm_area *area,
struct neighbor_stats *stat)
{
s16 x = 0, y = 0;
struct sita_pvt *pvt = (struct sita_pvt *)tcm->pvt;
/* Clearing any exisiting values */
memset(stat, 0, sizeof(*stat));
/* process top & bottom edges */
for (x = area->p0.x; x <= area->p1.x; x++) {
if (area->p0.y == 0)
stat->edge++;
else if (pvt->map[x][area->p0.y - 1])
stat->busy++;
if (area->p1.y == tcm->height - 1)
stat->edge++;
else if (pvt->map[x][area->p1.y + 1])
stat->busy++;
}
/* process left & right edges */ return tcm;
for (y = area->p0.y; y <= area->p1.y; ++y) {
if (area->p0.x == 0) error:
stat->edge++; kfree(tcm);
else if (pvt->map[area->p0.x - 1][y]) return NULL;
stat->busy++;
if (area->p1.x == tcm->width - 1)
stat->edge++;
else if (pvt->map[area->p1.x + 1][y])
stat->busy++;
}
} }
...@@ -61,18 +61,17 @@ struct tcm { ...@@ -61,18 +61,17 @@ struct tcm {
unsigned int y_offset; /* offset to use for y coordinates */ unsigned int y_offset; /* offset to use for y coordinates */
/* 'pvt' structure shall contain any tcm details (attr) along with spinlock_t lock;
linked list of allocated areas and mutex for mutually exclusive access unsigned long *bitmap;
to the list. It may also contain copies of width and height to notice size_t map_size;
any changes to the publicly available width and height fields. */
void *pvt;
/* function table */ /* function table */
s32 (*reserve_2d)(struct tcm *tcm, u16 height, u16 width, u8 align, s32 (*reserve_2d)(struct tcm *tcm, u16 height, u16 width, u16 align,
int16_t offset, uint16_t slot_bytes,
struct tcm_area *area); struct tcm_area *area);
s32 (*reserve_1d)(struct tcm *tcm, u32 slots, struct tcm_area *area); s32 (*reserve_1d)(struct tcm *tcm, u32 slots, struct tcm_area *area);
s32 (*free) (struct tcm *tcm, struct tcm_area *area); s32 (*free)(struct tcm *tcm, struct tcm_area *area);
void (*deinit) (struct tcm *tcm); void (*deinit)(struct tcm *tcm);
}; };
/*============================================================================= /*=============================================================================
...@@ -91,7 +90,7 @@ struct tcm { ...@@ -91,7 +90,7 @@ struct tcm {
* *
*/ */
struct tcm *sita_init(u16 width, u16 height, struct tcm_pt *attr); struct tcm *sita_init(u16 width, u16 height);
/** /**
...@@ -120,6 +119,9 @@ static inline void tcm_deinit(struct tcm *tcm) ...@@ -120,6 +119,9 @@ static inline void tcm_deinit(struct tcm *tcm)
* all values may be supported by the container manager, * all values may be supported by the container manager,
* but it must support 0 (1), 32 and 64. * but it must support 0 (1), 32 and 64.
* 0 value is equivalent to 1. * 0 value is equivalent to 1.
* @param offset Offset requirement, in bytes. This is the offset
* from a 4KiB aligned virtual address.
* @param slot_bytes Width of slot in bytes
* @param area Pointer to where the reserved area should be stored. * @param area Pointer to where the reserved area should be stored.
* *
* @return 0 on success. Non-0 error code on failure. Also, * @return 0 on success. Non-0 error code on failure. Also,
...@@ -129,7 +131,8 @@ static inline void tcm_deinit(struct tcm *tcm) ...@@ -129,7 +131,8 @@ static inline void tcm_deinit(struct tcm *tcm)
* allocation. * allocation.
*/ */
static inline s32 tcm_reserve_2d(struct tcm *tcm, u16 width, u16 height, static inline s32 tcm_reserve_2d(struct tcm *tcm, u16 width, u16 height,
u16 align, struct tcm_area *area) u16 align, int16_t offset, uint16_t slot_bytes,
struct tcm_area *area)
{ {
/* perform rudimentary error checking */ /* perform rudimentary error checking */
s32 res = tcm == NULL ? -ENODEV : s32 res = tcm == NULL ? -ENODEV :
...@@ -140,7 +143,8 @@ static inline s32 tcm_reserve_2d(struct tcm *tcm, u16 width, u16 height, ...@@ -140,7 +143,8 @@ static inline s32 tcm_reserve_2d(struct tcm *tcm, u16 width, u16 height,
if (!res) { if (!res) {
area->is2d = true; area->is2d = true;
res = tcm->reserve_2d(tcm, height, width, align, area); res = tcm->reserve_2d(tcm, height, width, align, offset,
slot_bytes, area);
area->tcm = res ? NULL : tcm; area->tcm = res ? NULL : tcm;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment