summaryrefslogtreecommitdiff
path: root/drivers/media/video/tiler/tiler-geom.c
blob: f95ae5c9ef9855da9e8f0f130f2099994a8e3f89 (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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
/*
 * tiler-geom.c
 *
 * TILER geometry 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 <linux/module.h>
#include "_tiler.h"

/* bits representing the same slot in DMM-TILER hw-block */
#define SLOT_WIDTH_BITS		6
#define SLOT_HEIGHT_BITS	6

/* bits reserved to describe coordinates in DMM-TILER hw-block */
#define CONT_WIDTH_BITS		14
#define CONT_HEIGHT_BITS	13

static struct tiler_geom geom[TILER_FORMATS] = {
	{
		.x_shft = 0,
		.y_shft = 0,
	},
	{
		.x_shft = 0,
		.y_shft = 1,
	},
	{
		.x_shft = 1,
		.y_shft = 1,
	},
	{
		.x_shft = SLOT_WIDTH_BITS,
		.y_shft = SLOT_HEIGHT_BITS,
	},
};

/* tiler space addressing bitfields */
#define MASK_XY_FLIP		(1 << 31)
#define MASK_Y_INVERT		(1 << 30)
#define MASK_X_INVERT		(1 << 29)
#define SHIFT_ACC_MODE		27
#define MASK_ACC_MODE		3

/* calculated constants */
#define TILER_PAGE		(1 << (SLOT_WIDTH_BITS + SLOT_HEIGHT_BITS))
#define TILER_WIDTH		(1 << (CONT_WIDTH_BITS - SLOT_WIDTH_BITS))
#define TILER_HEIGHT		(1 << (CONT_HEIGHT_BITS - SLOT_HEIGHT_BITS))

#define VIEW_SIZE		(1u << (CONT_WIDTH_BITS + CONT_HEIGHT_BITS))
#define VIEW_MASK		(VIEW_SIZE - 1u)

#define MASK(bits) ((1 << (bits)) - 1)

#define TILER_FMT(x)	((enum tiler_fmt) \
		((x >> SHIFT_ACC_MODE) & MASK_ACC_MODE))

#define MASK_VIEW		(MASK_X_INVERT | MASK_Y_INVERT | MASK_XY_FLIP)

/* location of the various tiler views in physical address space */
#define TILVIEW_8BIT	0x60000000u
#define TILVIEW_16BIT	(TILVIEW_8BIT  + VIEW_SIZE)
#define TILVIEW_32BIT	(TILVIEW_16BIT + VIEW_SIZE)
#define TILVIEW_PAGE	(TILVIEW_32BIT + VIEW_SIZE)
#define TILVIEW_END	(TILVIEW_PAGE  + VIEW_SIZE)

/* create tsptr by adding view orientation and access mode */
#define TIL_ADDR(x, orient, a)\
	((u32) (x) | (orient) | ((a) << SHIFT_ACC_MODE))

bool is_tiler_addr(u32 phys)
{
	return phys >= TILVIEW_8BIT && phys < TILVIEW_END;
}
EXPORT_SYMBOL(is_tiler_addr);

u32 tiler_bpp(const struct tiler_block_t *b)
{
	enum tiler_fmt fmt = tiler_fmt(b->phys);
	BUG_ON(fmt == TILFMT_INVALID);

	return geom[fmt].bpp_m;
}
EXPORT_SYMBOL(tiler_bpp);

/* return the stride of a tiler-block in tiler space */
static inline s32 tiler_stride(u32 tsptr)
{
	enum tiler_fmt fmt = TILER_FMT(tsptr);

	if (fmt == TILFMT_PAGE)
		return 0;
	else if (tsptr & MASK_XY_FLIP)
		return 1 << (CONT_HEIGHT_BITS + geom[fmt].x_shft);
	else
		return 1 << (CONT_WIDTH_BITS + geom[fmt].y_shft);
}

u32 tiler_pstride(const struct tiler_block_t *b)
{
	enum tiler_fmt fmt = tiler_fmt(b->phys);
	BUG_ON(fmt == TILFMT_INVALID);

	/* return the virtual stride for page mode */
	if (fmt == TILFMT_PAGE)
		return tiler_vstride(b);

	return tiler_stride(b->phys & ~MASK_VIEW);
}
EXPORT_SYMBOL(tiler_pstride);

enum tiler_fmt tiler_fmt(u32 phys)
{
	if (!is_tiler_addr(phys))
		return TILFMT_INVALID;

	return TILER_FMT(phys);
}
EXPORT_SYMBOL(tiler_fmt);

/* returns the tiler geometry information for a format */
static const struct tiler_geom *get_geom(enum tiler_fmt fmt)
{
	if (fmt >= TILFMT_MIN && fmt <= TILFMT_MAX)
		return geom + fmt;
	return NULL;
}

/**
 * Returns the natural x and y coordinates for a pixel in tiler space address.
 * That is, the coordinates for the same pixel in the natural (non-rotated,
 * non-mirrored) view. This allows to uniquely identify a tiler pixel in any
 * view orientation.
 */
static void tiler_get_natural_xy(u32 tsptr, u32 *x, u32 *y)
{
	u32 x_bits, y_bits, offset;
	enum tiler_fmt fmt;

	fmt = TILER_FMT(tsptr);

	x_bits = CONT_WIDTH_BITS - geom[fmt].x_shft;
	y_bits = CONT_HEIGHT_BITS - geom[fmt].y_shft;
	offset = (tsptr & VIEW_MASK) >> (geom[fmt].x_shft + geom[fmt].y_shft);

	/* separate coordinate bitfields based on view orientation */
	if (tsptr & MASK_XY_FLIP) {
		*x = offset >> y_bits;
		*y = offset & MASK(y_bits);
	} else {
		*x = offset & MASK(x_bits);
		*y = offset >> x_bits;
	}

	/* account for mirroring */
	if (tsptr & MASK_X_INVERT)
		*x ^= MASK(x_bits);
	if (tsptr & MASK_Y_INVERT)
		*y ^= MASK(y_bits);
}

/* calculate the tiler space address of a pixel in a view orientation */
static u32 tiler_get_address(u32 orient, enum tiler_fmt fmt, u32 x, u32 y)
{
	u32 x_bits, y_bits, tmp, x_mask, y_mask, alignment;

	x_bits = CONT_WIDTH_BITS - geom[fmt].x_shft;
	y_bits = CONT_HEIGHT_BITS - geom[fmt].y_shft;
	alignment = geom[fmt].x_shft + geom[fmt].y_shft;

	/* validate coordinate */
	x_mask = MASK(x_bits);
	y_mask = MASK(y_bits);
	if (x < 0 || x > x_mask || y < 0 || y > y_mask)
		return 0;

	/* account for mirroring */
	if (orient & MASK_X_INVERT)
		x ^= x_mask;
	if (orient & MASK_Y_INVERT)
		y ^= y_mask;

	/* get coordinate address */
	if (orient & MASK_XY_FLIP)
		tmp = ((x << y_bits) + y);
	else
		tmp = ((y << x_bits) + x);

	return TIL_ADDR((tmp << alignment), orient, fmt);
}

void tilview_create(struct tiler_view_t *view, u32 phys, u32 width, u32 height)
{
	BUG_ON(!is_tiler_addr(phys));

	view->tsptr = phys & ~MASK_VIEW;
	view->bpp = geom[TILER_FMT(phys)].bpp_m;
	view->width = width;
	view->height = height;
	view->h_inc = view->bpp;
	view->v_inc = tiler_stride(view->tsptr);
}
EXPORT_SYMBOL(tilview_create);

void tilview_get(struct tiler_view_t *view, struct tiler_block_t *blk)
{
	view->tsptr = blk->phys & ~MASK_VIEW;
	view->bpp = tiler_bpp(blk);
	view->width = blk->width;
	view->height = blk->height;
	view->h_inc = view->bpp;
	view->v_inc = tiler_stride(view->tsptr);
}
EXPORT_SYMBOL(tilview_get);

s32 tilview_crop(struct tiler_view_t *view, u32 left, u32 top, u32 width,
								u32 height)
{
	/* check for valid crop */
	if (left + width < left || left + width > view->width ||
	    top + height < top || top + height > view->height)
		return -EINVAL;

	view->tsptr += left * view->h_inc + top * view->v_inc;
	view->width = width;
	view->height = height;
	return 0;
}
EXPORT_SYMBOL(tilview_crop);

/* calculate tilerspace address and stride after view orientation change */
static void reorient(struct tiler_view_t *view, u32 orient)
{
	u32 x, y;

	tiler_get_natural_xy(view->tsptr, &x, &y);
	view->tsptr = tiler_get_address(orient,
					TILER_FMT(view->tsptr), x, y);
	view->v_inc = tiler_stride(view->tsptr);
}

s32 tilview_rotate(struct tiler_view_t *view, s32 rotation)
{
	u32 orient;

	if (rotation % 90)
		return -EINVAL;

	/* normalize rotation to quarters */
	rotation = (rotation / 90) & 3;
	if (!rotation)
		return 0; /* nothing to do */

	/* PAGE mode view cannot be rotated */
	if (TILER_FMT(view->tsptr) == TILFMT_PAGE)
		return -EPERM;

	/*
	 * first adjust top-left corner. NOTE: it rotates counter-clockwise:
	 * 0 < 3
	 * v   ^
	 * 1 > 2
	 */
	if (rotation < 3)
		view->tsptr += (view->height - 1) * view->v_inc;
	if (rotation > 1)
		view->tsptr += (view->width - 1) * view->h_inc;

	/* then rotate view itself */
	orient = view->tsptr & MASK_VIEW;

	/* rotate first 2 quarters */
	if (rotation & 2) {
		orient ^= MASK_X_INVERT;
		orient ^= MASK_Y_INVERT;
	}

	/* rotate last quarter */
	if (rotation & 1) {
		orient ^= (orient & MASK_XY_FLIP) ?
			MASK_X_INVERT : MASK_Y_INVERT;

		/* swap x & y */
		orient ^= MASK_XY_FLIP;
		swap(view->height, view->width);
	}

	/* finally reorient view */
	reorient(view, orient);
	return 0;
}
EXPORT_SYMBOL(tilview_rotate);

s32 tilview_flip(struct tiler_view_t *view, bool flip_x, bool flip_y)
{
	u32 orient;
	orient = view->tsptr & MASK_VIEW;

	if (!flip_x && !flip_y)
		return 0; /* nothing to do */

	/* PAGE mode view cannot be flipped */
	if (TILER_FMT(view->tsptr) == TILFMT_PAGE)
		return -EPERM;

	/* adjust top-left corner */
	if (flip_x)
		view->tsptr += (view->width - 1) * view->h_inc;
	if (flip_y)
		view->tsptr += (view->height - 1) * view->v_inc;

	/* flip view orientation */
	if (orient & MASK_XY_FLIP)
		swap(flip_x, flip_y);

	if (flip_x)
		orient ^= MASK_X_INVERT;
	if (flip_y)
		orient ^= MASK_Y_INVERT;

	/* finally reorient view */
	reorient(view, orient);
	return 0;
}
EXPORT_SYMBOL(tilview_flip);

/* return the alias address for a coordinate */
static inline u32 alias_address(enum tiler_fmt fmt, u32 x, u32 y)
{
	return tiler_get_address(0, fmt, x, y) + TILVIEW_8BIT;
}

/* get the coordinates for an alias address */
static inline void alias_xy(u32 ssptr, u32 *x, u32 *y)
{
	tiler_get_natural_xy(ssptr & ~MASK_VIEW, x, y);
}

/* initialize shared geometric data */
void tiler_geom_init(struct tiler_ops *tiler)
{
	struct tiler_geom *g;

	tiler->xy = alias_xy;
	tiler->addr = alias_address;
	tiler->geom = get_geom;

	tiler->page   = TILER_PAGE;
	tiler->width  = TILER_WIDTH;
	tiler->height = TILER_HEIGHT;

	/* calculate geometry */
	for (g = geom; g < geom + TILER_FORMATS; g++) {
		g->bpp_m = g->bpp = 1 << (g->x_shft + g->y_shft);
		g->slot_w = 1 << (SLOT_WIDTH_BITS - g->x_shft);
		g->slot_h = 1 << (SLOT_HEIGHT_BITS - g->y_shft);
	}

	/* set bpp_m = 1 for page mode as most applications deal in byte data */
	geom[TILFMT_PAGE].bpp_m = 1;
}