2011-12-05 19:19:21 -06:00
/*
* tcm - sita . c
*
* SImple Tiler Allocator ( SiTA ) : 2 D and 1 D allocation ( reservation ) algorithm
*
* Authors : Ravi Ramachandra < r . ramachandra @ ti . com > ,
* Lajos Molnar < molnar @ ti . com >
2015-08-12 11:24:38 +03:00
* Andy Gross < andy . gross @ ti . com >
2011-12-05 19:19:21 -06:00
*
2015-08-12 11:24:38 +03:00
* Copyright ( C ) 2012 Texas Instruments , Inc .
2011-12-05 19:19:21 -06:00
*
* 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
* published by the Free Software Foundation .
*
* THIS PACKAGE IS PROVIDED ` ` AS IS ' ' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES , INCLUDING , WITHOUT LIMITATION , THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE .
*
*/
2015-08-12 11:24:38 +03:00
# include <linux/init.h>
# include <linux/module.h>
# include <linux/errno.h>
# include <linux/sched.h>
# include <linux/wait.h>
# include <linux/bitmap.h>
2011-12-05 19:19:21 -06:00
# include <linux/slab.h>
2015-08-12 11:24:38 +03:00
# include "tcm.h"
2011-12-05 19:19:21 -06:00
2015-08-12 11:24:38 +03:00
static unsigned long mask [ 8 ] ;
/*
* pos position in bitmap
* w width in slots
* h height in slots
* map ptr to bitmap
* stride slots in a row
2011-12-05 19:19:21 -06:00
*/
2015-08-12 11:24:38 +03:00
static void free_slots ( unsigned long pos , uint16_t w , uint16_t h ,
unsigned long * map , uint16_t stride )
2011-12-05 19:19:21 -06:00
{
2015-08-12 11:24:38 +03:00
int i ;
2011-12-05 19:19:21 -06:00
2015-08-12 11:24:38 +03:00
for ( i = 0 ; i < h ; i + + , pos + = stride )
bitmap_clear ( map , pos , w ) ;
2011-12-05 19:19:21 -06:00
}
2015-08-12 11:24:38 +03:00
/*
* w width in slots
* pos ptr to position
* map ptr to bitmap
* num_bits number of bits in bitmap
2011-12-05 19:19:21 -06:00
*/
2015-08-12 11:24:38 +03:00
static int r2l_b2t_1d ( uint16_t w , unsigned long * pos , unsigned long * map ,
size_t num_bits )
2011-12-05 19:19:21 -06:00
{
2015-08-12 11:24:38 +03:00
unsigned long search_count = 0 ;
unsigned long bit ;
bool area_found = false ;
2011-12-05 19:19:21 -06:00
2015-08-12 11:24:38 +03:00
* pos = num_bits - w ;
2011-12-05 19:19:21 -06:00
2015-08-12 11:24:38 +03:00
while ( search_count < num_bits ) {
bit = find_next_bit ( map , num_bits , * pos ) ;
2011-12-05 19:19:21 -06:00
2015-08-12 11:24:38 +03:00
if ( bit - * pos > = w ) {
/* found a long enough free area */
bitmap_set ( map , * pos , w ) ;
area_found = true ;
break ;
}
2011-12-05 19:19:21 -06:00
2015-08-12 11:24:38 +03:00
search_count = num_bits - bit + w ;
* pos = bit - w ;
}
return ( area_found ) ? 0 : - ENOMEM ;
2011-12-05 19:19:21 -06:00
}
2015-08-12 11:24:38 +03:00
/*
* w = width in slots
* h = height in slots
* a = align in slots ( mask , 2 ^ n - 1 , 0 is unaligned )
* offset = offset in bytes from 4 KiB
* pos = position in bitmap for buffer
* map = bitmap ptr
* num_bits = size of bitmap
* stride = bits in one row of container
2011-12-05 19:19:21 -06:00
*/
2015-08-12 11:24:38 +03:00
static int l2r_t2b ( uint16_t w , uint16_t h , uint16_t a , int16_t offset ,
unsigned long * pos , unsigned long slot_bytes ,
unsigned long * map , size_t num_bits , size_t slot_stride )
2011-12-05 19:19:21 -06:00
{
2015-08-12 11:24:38 +03:00
int i ;
unsigned long index ;
bool area_free ;
unsigned long slots_per_band = PAGE_SIZE / slot_bytes ;
unsigned long bit_offset = ( offset > 0 ) ? offset / slot_bytes : 0 ;
unsigned long curr_bit = bit_offset ;
/* reset alignment to 1 if we are matching a specific offset */
/* adjust alignment - 1 to get to the format expected in bitmaps */
a = ( offset > 0 ) ? 0 : a - 1 ;
/* FIXME Return error if slots_per_band > stride */
while ( curr_bit < num_bits ) {
* pos = bitmap_find_next_zero_area ( map , num_bits , curr_bit , w ,
a ) ;
/* skip forward if we are not at right offset */
if ( bit_offset > 0 & & ( * pos % slots_per_band ! = bit_offset ) ) {
curr_bit = ALIGN ( * pos , slots_per_band ) + bit_offset ;
continue ;
}
2011-12-05 19:19:21 -06:00
2015-08-12 11:24:38 +03:00
/* skip forward to next row if we overlap end of row */
if ( ( * pos % slot_stride ) + w > slot_stride ) {
curr_bit = ALIGN ( * pos , slot_stride ) + bit_offset ;
continue ;
}
2011-12-05 19:19:21 -06:00
2015-08-12 11:24:38 +03:00
/* TODO: Handle overlapping 4K boundaries */
2011-12-05 19:19:21 -06:00
2015-08-12 11:24:38 +03:00
/* break out of look if we will go past end of container */
if ( ( * pos + slot_stride * h ) > num_bits )
break ;
2011-12-05 19:19:21 -06:00
2015-08-12 11:24:38 +03:00
/* generate mask that represents out matching pattern */
bitmap_clear ( mask , 0 , slot_stride ) ;
bitmap_set ( mask , ( * pos % BITS_PER_LONG ) , w ) ;
2011-12-05 19:19:21 -06:00
2015-08-12 11:24:38 +03:00
/* assume the area is free until we find an overlap */
area_free = true ;
2011-12-05 19:19:21 -06:00
2015-08-12 11:24:38 +03:00
/* check subsequent rows to see if complete area is free */
for ( i = 1 ; i < h ; i + + ) {
index = * pos / BITS_PER_LONG + i * 8 ;
if ( bitmap_intersects ( & map [ index ] , mask ,
( * pos % BITS_PER_LONG ) + w ) ) {
area_free = false ;
2011-12-05 19:19:21 -06:00
break ;
}
}
2015-08-12 11:24:38 +03:00
if ( area_free )
2011-12-05 19:19:21 -06:00
break ;
2015-08-12 11:24:38 +03:00
/* go forward past this match */
if ( bit_offset > 0 )
curr_bit = ALIGN ( * pos , slots_per_band ) + bit_offset ;
else
curr_bit = * pos + a + 1 ;
}
2011-12-05 19:19:21 -06:00
2015-08-12 11:24:38 +03:00
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 ) ;
2011-12-05 19:19:21 -06:00
}
2015-08-12 11:24:38 +03:00
return ( area_free ) ? 0 : - ENOMEM ;
2011-12-05 19:19:21 -06:00
}
2015-08-12 11:24:38 +03:00
static s32 sita_reserve_1d ( struct tcm * tcm , u32 num_slots ,
struct tcm_area * area )
2011-12-05 19:19:21 -06:00
{
2015-08-12 11:24:38 +03:00
unsigned long pos ;
int ret ;
spin_lock ( & ( tcm - > lock ) ) ;
ret = r2l_b2t_1d ( num_slots , & pos , tcm - > bitmap , tcm - > map_size ) ;
if ( ! ret ) {
area - > p0 . x = pos % tcm - > width ;
area - > p0 . y = pos / tcm - > width ;
area - > p1 . x = ( pos + num_slots - 1 ) % tcm - > width ;
area - > p1 . y = ( pos + num_slots - 1 ) / tcm - > width ;
2011-12-05 19:19:21 -06:00
}
2015-08-12 11:24:38 +03:00
spin_unlock ( & ( tcm - > lock ) ) ;
2011-12-05 19:19:21 -06:00
2015-08-12 11:24:38 +03:00
return ret ;
2011-12-05 19:19:21 -06:00
}
2015-08-12 11:24:38 +03:00
static s32 sita_reserve_2d ( struct tcm * tcm , u16 h , u16 w , u16 align ,
int16_t offset , uint16_t slot_bytes ,
struct tcm_area * area )
2011-12-05 19:19:21 -06:00
{
2015-08-12 11:24:38 +03:00
unsigned long pos ;
int ret ;
spin_lock ( & ( tcm - > lock ) ) ;
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 ;
2011-12-05 19:19:21 -06:00
}
2015-08-12 11:24:38 +03:00
spin_unlock ( & ( tcm - > lock ) ) ;
2011-12-05 19:19:21 -06:00
return ret ;
}
2015-08-12 11:24:38 +03:00
static void sita_deinit ( struct tcm * tcm )
2011-12-05 19:19:21 -06:00
{
2015-08-12 11:24:38 +03:00
kfree ( tcm ) ;
2011-12-05 19:19:21 -06:00
}
2015-08-12 11:24:38 +03:00
static s32 sita_free ( struct tcm * tcm , struct tcm_area * area )
2011-12-05 19:19:21 -06:00
{
2015-08-12 11:24:38 +03:00
unsigned long pos ;
uint16_t w , h ;
2011-12-05 19:19:21 -06:00
2015-08-12 11:24:38 +03:00
pos = area - > p0 . x + area - > p0 . y * tcm - > width ;
if ( area - > is2d ) {
w = area - > p1 . x - area - > p0 . x + 1 ;
h = area - > p1 . y - area - > p0 . y + 1 ;
} else {
w = area - > p1 . x + area - > p1 . y * tcm - > width - pos + 1 ;
h = 1 ;
2011-12-05 19:19:21 -06:00
}
2015-08-12 11:24:38 +03:00
spin_lock ( & ( tcm - > lock ) ) ;
free_slots ( pos , w , h , tcm - > bitmap , tcm - > width ) ;
spin_unlock ( & ( tcm - > lock ) ) ;
return 0 ;
2011-12-05 19:19:21 -06:00
}
2015-08-12 11:24:38 +03:00
struct tcm * sita_init ( u16 width , u16 height )
2011-12-05 19:19:21 -06:00
{
2015-08-12 11:24:38 +03:00
struct tcm * tcm ;
size_t map_size = BITS_TO_LONGS ( width * height ) * sizeof ( unsigned long ) ;
2011-12-05 19:19:21 -06:00
2015-08-12 11:24:38 +03:00
if ( width = = 0 | | height = = 0 )
return NULL ;
2011-12-05 19:19:21 -06:00
2015-08-12 11:24:38 +03:00
tcm = kzalloc ( sizeof ( * tcm ) + map_size , GFP_KERNEL ) ;
if ( ! tcm )
goto error ;
2011-12-05 19:19:21 -06:00
2015-08-12 11:24:38 +03:00
/* 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 ;
2011-12-05 19:19:21 -06:00
2015-08-12 11:24:38 +03:00
spin_lock_init ( & tcm - > lock ) ;
tcm - > bitmap = ( unsigned long * ) ( tcm + 1 ) ;
bitmap_clear ( tcm - > bitmap , 0 , width * height ) ;
2011-12-05 19:19:21 -06:00
2015-08-12 11:24:38 +03:00
tcm - > map_size = width * height ;
2011-12-05 19:19:21 -06:00
2015-08-12 11:24:38 +03:00
return tcm ;
2011-12-05 19:19:21 -06:00
2015-08-12 11:24:38 +03:00
error :
kfree ( tcm ) ;
return NULL ;
2011-12-05 19:19:21 -06:00
}