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