aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/media/video/tiler/tiler-reserve.c
blob: c5a4f9c3b0e57c53b4bb96abd1f71e51024d256d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/*
 * tiler-reserve.c
 *
 * TILER driver area reservation functions for TI TILER hardware block.
 *
 * Author: Lajos Molnar <molnar@ti.com>
 *
 * Copyright (C) 2009-2010 Texas Instruments, Inc.
 *
 * 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.
 */

#include "_tiler.h"

static struct tiler_ops *ops;	/* shared methods and variables */

/**
 * Calculate the maximum number buffers that can be packed next to each other,
 * and the area they occupy. This method is used for both 2D and NV12 packing.
 *
 * @author a0194118 (7/16/2010)
 *
 * @param o	desired offset
 * @param w	width of one block (>0)
 * @param a	desired alignment
 * @param b	band width (each block must occupy the same number of bands)
 * @param n	pointer to the desired number of blocks to pack.  It will be
 *		updated with the maximum number of blocks that can be packed.
 * @param _area	pointer to store total area needed
 *
 * @return packing efficiency (0-1024)
 */
u32 tiler_best2pack(u16 o, u16 a, u16 b, u16 w, u16 *n, u16 *_area)
{
	u16 m = 0, max_n = *n;		/* m is mostly n - 1 */
	u16 e = ALIGN(w, a);		/* effective width of one block */
	u32 eff, best_eff = 0;		/* best values */
	u16 stride = ALIGN(o + w, b);	/* block stride */
	u16 area = stride;		/* area needed (for m + 1 blocks) */

	/* NOTE: block #m+1 occupies the range (o + m * e, o + m * e + w) */

	/* see how many blocks we can pack */
	while (m < max_n &&
		/* blocks must fit in tiler container */
		o + m * e + w <= ops->width &&
		/* block stride must be correct */
		stride == ALIGN(area - o - m * e, b)) {

		m++;
		eff = m * w * 1024 / area;
		if (eff > best_eff) {
			/* store packing for best efficiency & smallest area */
			best_eff = eff;
			*n = m;
			if (_area)
				*_area = area;
		}
		/* update area */
		area = ALIGN(o + m * e + w, b);
	}

	return best_eff;
}

/**
 * We also optimize packing regular 2D areas as the auto-packing may result in
 * sub-optimal efficiency. This is most pronounced if the area is wider than
 * half a PAGE_SIZE (e.g. 2048 in 8-bit mode, or 1024 in 16-bit mode).
 */

/* reserve 2d blocks */
static void reserve_blocks(u32 n, enum tiler_fmt fmt, u32 width, u32 height,
			   u32 gid,
			   struct security_info *si)
{
	u32 bpt, res = 0, i;
	u16 a, band, w, h, n_try;
	struct gid_info *gi;
	const struct tiler_geom *g;

	/* Check input parameters for correctness, and support */
	if (!width || !height || !n ||
	    fmt < TILFMT_8BIT || fmt > TILFMT_32BIT)
		return;

	/* tiler slot in bytes */
	g = ops->geom(fmt);
	bpt = g->slot_w * g->bpp;

	/*
	 *  For blocks narrower than half PAGE_SIZE the default allocation is
	 *  sufficient.  Also check for basic area info.
	 */
	if (width * g->bpp * 2 <= PAGE_SIZE ||
	    ops->analize(fmt, width, height, &w, &h, &band, &a))
		return;

	/* get group id */
	gi = ops->get_gi(si, gid);
	if (!gi)
		return;

	/* reserve in groups until failed or all is reserved */
	for (i = 0; i < n && res >= 0; i += res + 1) {
		/* blocks to allocate in one area */
		n_try = min(n - i, ops->width);
		tiler_best2pack(0, a, band, w, &n_try, NULL);

		res = -1;
		while (n_try > 1) {
			/* adjust res so we fail on 0 return value */
			res = ops->lay_2d(fmt, n_try, w, h, band, a,
						gi, &gi->reserved) - 1;
			if (res >= 0)
				break;

			/* reduce n if failed to allocate area */
			n_try--;
		}
	}
	/* keep reserved blocks even if failed to reserve all */

	ops->release_gi(gi);
}

/* unreserve blocks for a group id */
static void unreserve_blocks(u32 gid, struct security_info *si)
{
	struct gid_info *gi;

	gi = ops->get_gi(si, gid);
	if (!gi)
		return;

	ops->release(&gi->reserved);

	ops->release_gi(gi);
}

/* initialize shared method pointers and global static variables */
void tiler_reserve_init(struct tiler_ops *tiler)
{
	ops = tiler;

	ops->reserve = reserve_blocks;
	ops->unreserve = unreserve_blocks;
}