Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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 - http://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
 28static 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 */
 36static 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 */
 51static 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 */
 87static 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
162static 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
181static 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
203static void sita_deinit(struct tcm *tcm)
204{
205	kfree(tcm);
206}
207
208static 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
228struct 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
256error:
257	kfree(tcm);
258	return NULL;
259}