162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * TILER container manager specification and support functions for TI
362306a36Sopenharmony_ci * TILER driver.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Author: Lajos Molnar <molnar@ti.com>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * All rights reserved.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or without
1062306a36Sopenharmony_ci * modification, are permitted provided that the following conditions
1162306a36Sopenharmony_ci * are met:
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * * Redistributions of source code must retain the above copyright
1462306a36Sopenharmony_ci *   notice, this list of conditions and the following disclaimer.
1562306a36Sopenharmony_ci *
1662306a36Sopenharmony_ci * * Redistributions in binary form must reproduce the above copyright
1762306a36Sopenharmony_ci *   notice, this list of conditions and the following disclaimer in the
1862306a36Sopenharmony_ci *   documentation and/or other materials provided with the distribution.
1962306a36Sopenharmony_ci *
2062306a36Sopenharmony_ci * * Neither the name of Texas Instruments Incorporated nor the names of
2162306a36Sopenharmony_ci *   its contributors may be used to endorse or promote products derived
2262306a36Sopenharmony_ci *   from this software without specific prior written permission.
2362306a36Sopenharmony_ci *
2462306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
2562306a36Sopenharmony_ci * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
2662306a36Sopenharmony_ci * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2762306a36Sopenharmony_ci * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
2862306a36Sopenharmony_ci * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
2962306a36Sopenharmony_ci * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
3062306a36Sopenharmony_ci * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
3162306a36Sopenharmony_ci * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
3262306a36Sopenharmony_ci * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
3362306a36Sopenharmony_ci * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
3462306a36Sopenharmony_ci * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3562306a36Sopenharmony_ci */
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci#ifndef TCM_H
3862306a36Sopenharmony_ci#define TCM_H
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistruct tcm;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci/* point */
4362306a36Sopenharmony_cistruct tcm_pt {
4462306a36Sopenharmony_ci	u16 x;
4562306a36Sopenharmony_ci	u16 y;
4662306a36Sopenharmony_ci};
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/* 1d or 2d area */
4962306a36Sopenharmony_cistruct tcm_area {
5062306a36Sopenharmony_ci	bool is2d;		/* whether area is 1d or 2d */
5162306a36Sopenharmony_ci	struct tcm    *tcm;	/* parent */
5262306a36Sopenharmony_ci	struct tcm_pt  p0;
5362306a36Sopenharmony_ci	struct tcm_pt  p1;
5462306a36Sopenharmony_ci};
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistruct tcm {
5762306a36Sopenharmony_ci	u16 width, height;	/* container dimensions */
5862306a36Sopenharmony_ci	int lut_id;		/* Lookup table identifier */
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	unsigned int y_offset;	/* offset to use for y coordinates */
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	spinlock_t lock;
6362306a36Sopenharmony_ci	unsigned long *bitmap;
6462306a36Sopenharmony_ci	size_t map_size;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	/* function table */
6762306a36Sopenharmony_ci	s32 (*reserve_2d)(struct tcm *tcm, u16 height, u16 width, u16 align,
6862306a36Sopenharmony_ci			  s16 offset, u16 slot_bytes,
6962306a36Sopenharmony_ci			  struct tcm_area *area);
7062306a36Sopenharmony_ci	s32 (*reserve_1d)(struct tcm *tcm, u32 slots, struct tcm_area *area);
7162306a36Sopenharmony_ci	s32 (*free)(struct tcm *tcm, struct tcm_area *area);
7262306a36Sopenharmony_ci	void (*deinit)(struct tcm *tcm);
7362306a36Sopenharmony_ci};
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci/*=============================================================================
7662306a36Sopenharmony_ci    BASIC TILER CONTAINER MANAGER INTERFACE
7762306a36Sopenharmony_ci=============================================================================*/
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci/*
8062306a36Sopenharmony_ci * NOTE:
8162306a36Sopenharmony_ci *
8262306a36Sopenharmony_ci * Since some basic parameter checking is done outside the TCM algorithms,
8362306a36Sopenharmony_ci * TCM implementation do NOT have to check the following:
8462306a36Sopenharmony_ci *
8562306a36Sopenharmony_ci *   area pointer is NULL
8662306a36Sopenharmony_ci *   width and height fits within container
8762306a36Sopenharmony_ci *   number of pages is more than the size of the container
8862306a36Sopenharmony_ci *
8962306a36Sopenharmony_ci */
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_cistruct tcm *sita_init(u16 width, u16 height);
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci/**
9562306a36Sopenharmony_ci * Deinitialize tiler container manager.
9662306a36Sopenharmony_ci *
9762306a36Sopenharmony_ci * @param tcm	Pointer to container manager.
9862306a36Sopenharmony_ci *
9962306a36Sopenharmony_ci * @return 0 on success, non-0 error value on error.  The call
10062306a36Sopenharmony_ci *	   should free as much memory as possible and meaningful
10162306a36Sopenharmony_ci *	   even on failure.  Some error codes: -ENODEV: invalid
10262306a36Sopenharmony_ci *	   manager.
10362306a36Sopenharmony_ci */
10462306a36Sopenharmony_cistatic inline void tcm_deinit(struct tcm *tcm)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	if (tcm)
10762306a36Sopenharmony_ci		tcm->deinit(tcm);
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci/**
11162306a36Sopenharmony_ci * Reserves a 2D area in the container.
11262306a36Sopenharmony_ci *
11362306a36Sopenharmony_ci * @param tcm		Pointer to container manager.
11462306a36Sopenharmony_ci * @param height	Height(in pages) of area to be reserved.
11562306a36Sopenharmony_ci * @param width		Width(in pages) of area to be reserved.
11662306a36Sopenharmony_ci * @param align		Alignment requirement for top-left corner of area. Not
11762306a36Sopenharmony_ci *			all values may be supported by the container manager,
11862306a36Sopenharmony_ci *			but it must support 0 (1), 32 and 64.
11962306a36Sopenharmony_ci *			0 value is equivalent to 1.
12062306a36Sopenharmony_ci * @param offset	Offset requirement, in bytes.  This is the offset
12162306a36Sopenharmony_ci *			from a 4KiB aligned virtual address.
12262306a36Sopenharmony_ci * @param slot_bytes	Width of slot in bytes
12362306a36Sopenharmony_ci * @param area		Pointer to where the reserved area should be stored.
12462306a36Sopenharmony_ci *
12562306a36Sopenharmony_ci * @return 0 on success.  Non-0 error code on failure.  Also,
12662306a36Sopenharmony_ci *	   the tcm field of the area will be set to NULL on
12762306a36Sopenharmony_ci *	   failure.  Some error codes: -ENODEV: invalid manager,
12862306a36Sopenharmony_ci *	   -EINVAL: invalid area, -ENOMEM: not enough space for
12962306a36Sopenharmony_ci *	    allocation.
13062306a36Sopenharmony_ci */
13162306a36Sopenharmony_cistatic inline s32 tcm_reserve_2d(struct tcm *tcm, u16 width, u16 height,
13262306a36Sopenharmony_ci				u16 align, s16 offset, u16 slot_bytes,
13362306a36Sopenharmony_ci				struct tcm_area *area)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	/* perform rudimentary error checking */
13662306a36Sopenharmony_ci	s32 res = tcm  == NULL ? -ENODEV :
13762306a36Sopenharmony_ci		(area == NULL || width == 0 || height == 0 ||
13862306a36Sopenharmony_ci		 /* align must be a 2 power */
13962306a36Sopenharmony_ci		 (align & (align - 1))) ? -EINVAL :
14062306a36Sopenharmony_ci		(height > tcm->height || width > tcm->width) ? -ENOMEM : 0;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	if (!res) {
14362306a36Sopenharmony_ci		area->is2d = true;
14462306a36Sopenharmony_ci		res = tcm->reserve_2d(tcm, height, width, align, offset,
14562306a36Sopenharmony_ci					slot_bytes, area);
14662306a36Sopenharmony_ci		area->tcm = res ? NULL : tcm;
14762306a36Sopenharmony_ci	}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	return res;
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci/**
15362306a36Sopenharmony_ci * Reserves a 1D area in the container.
15462306a36Sopenharmony_ci *
15562306a36Sopenharmony_ci * @param tcm		Pointer to container manager.
15662306a36Sopenharmony_ci * @param slots		Number of (contiguous) slots to reserve.
15762306a36Sopenharmony_ci * @param area		Pointer to where the reserved area should be stored.
15862306a36Sopenharmony_ci *
15962306a36Sopenharmony_ci * @return 0 on success.  Non-0 error code on failure.  Also,
16062306a36Sopenharmony_ci *	   the tcm field of the area will be set to NULL on
16162306a36Sopenharmony_ci *	   failure.  Some error codes: -ENODEV: invalid manager,
16262306a36Sopenharmony_ci *	   -EINVAL: invalid area, -ENOMEM: not enough space for
16362306a36Sopenharmony_ci *	    allocation.
16462306a36Sopenharmony_ci */
16562306a36Sopenharmony_cistatic inline s32 tcm_reserve_1d(struct tcm *tcm, u32 slots,
16662306a36Sopenharmony_ci				 struct tcm_area *area)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	/* perform rudimentary error checking */
16962306a36Sopenharmony_ci	s32 res = tcm  == NULL ? -ENODEV :
17062306a36Sopenharmony_ci		(area == NULL || slots == 0) ? -EINVAL :
17162306a36Sopenharmony_ci		slots > (tcm->width * (u32) tcm->height) ? -ENOMEM : 0;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	if (!res) {
17462306a36Sopenharmony_ci		area->is2d = false;
17562306a36Sopenharmony_ci		res = tcm->reserve_1d(tcm, slots, area);
17662306a36Sopenharmony_ci		area->tcm = res ? NULL : tcm;
17762306a36Sopenharmony_ci	}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	return res;
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci/**
18362306a36Sopenharmony_ci * Free a previously reserved area from the container.
18462306a36Sopenharmony_ci *
18562306a36Sopenharmony_ci * @param area	Pointer to area reserved by a prior call to
18662306a36Sopenharmony_ci *		tcm_reserve_1d or tcm_reserve_2d call, whether
18762306a36Sopenharmony_ci *		it was successful or not. (Note: all fields of
18862306a36Sopenharmony_ci *		the structure must match.)
18962306a36Sopenharmony_ci *
19062306a36Sopenharmony_ci * @return 0 on success.  Non-0 error code on failure.  Also, the tcm
19162306a36Sopenharmony_ci *	   field of the area is set to NULL on success to avoid subsequent
19262306a36Sopenharmony_ci *	   freeing.  This call will succeed even if supplying
19362306a36Sopenharmony_ci *	   the area from a failed reserved call.
19462306a36Sopenharmony_ci */
19562306a36Sopenharmony_cistatic inline s32 tcm_free(struct tcm_area *area)
19662306a36Sopenharmony_ci{
19762306a36Sopenharmony_ci	s32 res = 0; /* free succeeds by default */
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	if (area && area->tcm) {
20062306a36Sopenharmony_ci		res = area->tcm->free(area->tcm, area);
20162306a36Sopenharmony_ci		if (res == 0)
20262306a36Sopenharmony_ci			area->tcm = NULL;
20362306a36Sopenharmony_ci	}
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	return res;
20662306a36Sopenharmony_ci}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci/*=============================================================================
20962306a36Sopenharmony_ci    HELPER FUNCTION FOR ANY TILER CONTAINER MANAGER
21062306a36Sopenharmony_ci=============================================================================*/
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci/**
21362306a36Sopenharmony_ci * This method slices off the topmost 2D slice from the parent area, and stores
21462306a36Sopenharmony_ci * it in the 'slice' parameter.  The 'parent' parameter will get modified to
21562306a36Sopenharmony_ci * contain the remaining portion of the area.  If the whole parent area can
21662306a36Sopenharmony_ci * fit in a 2D slice, its tcm pointer is set to NULL to mark that it is no
21762306a36Sopenharmony_ci * longer a valid area.
21862306a36Sopenharmony_ci *
21962306a36Sopenharmony_ci * @param parent	Pointer to a VALID parent area that will get modified
22062306a36Sopenharmony_ci * @param slice		Pointer to the slice area that will get modified
22162306a36Sopenharmony_ci */
22262306a36Sopenharmony_cistatic inline void tcm_slice(struct tcm_area *parent, struct tcm_area *slice)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	*slice = *parent;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	/* check if we need to slice */
22762306a36Sopenharmony_ci	if (slice->tcm && !slice->is2d &&
22862306a36Sopenharmony_ci		slice->p0.y != slice->p1.y &&
22962306a36Sopenharmony_ci		(slice->p0.x || (slice->p1.x != slice->tcm->width - 1))) {
23062306a36Sopenharmony_ci		/* set end point of slice (start always remains) */
23162306a36Sopenharmony_ci		slice->p1.x = slice->tcm->width - 1;
23262306a36Sopenharmony_ci		slice->p1.y = (slice->p0.x) ? slice->p0.y : slice->p1.y - 1;
23362306a36Sopenharmony_ci		/* adjust remaining area */
23462306a36Sopenharmony_ci		parent->p0.x = 0;
23562306a36Sopenharmony_ci		parent->p0.y = slice->p1.y + 1;
23662306a36Sopenharmony_ci	} else {
23762306a36Sopenharmony_ci		/* mark this as the last slice */
23862306a36Sopenharmony_ci		parent->tcm = NULL;
23962306a36Sopenharmony_ci	}
24062306a36Sopenharmony_ci}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci/* Verify if a tcm area is logically valid */
24362306a36Sopenharmony_cistatic inline bool tcm_area_is_valid(struct tcm_area *area)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	return area && area->tcm &&
24662306a36Sopenharmony_ci		/* coordinate bounds */
24762306a36Sopenharmony_ci		area->p1.x < area->tcm->width &&
24862306a36Sopenharmony_ci		area->p1.y < area->tcm->height &&
24962306a36Sopenharmony_ci		area->p0.y <= area->p1.y &&
25062306a36Sopenharmony_ci		/* 1D coordinate relationship + p0.x check */
25162306a36Sopenharmony_ci		((!area->is2d &&
25262306a36Sopenharmony_ci		  area->p0.x < area->tcm->width &&
25362306a36Sopenharmony_ci		  area->p0.x + area->p0.y * area->tcm->width <=
25462306a36Sopenharmony_ci		  area->p1.x + area->p1.y * area->tcm->width) ||
25562306a36Sopenharmony_ci		 /* 2D coordinate relationship */
25662306a36Sopenharmony_ci		 (area->is2d &&
25762306a36Sopenharmony_ci		  area->p0.x <= area->p1.x));
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci/* see if a coordinate is within an area */
26162306a36Sopenharmony_cistatic inline bool __tcm_is_in(struct tcm_pt *p, struct tcm_area *a)
26262306a36Sopenharmony_ci{
26362306a36Sopenharmony_ci	u16 i;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	if (a->is2d) {
26662306a36Sopenharmony_ci		return p->x >= a->p0.x && p->x <= a->p1.x &&
26762306a36Sopenharmony_ci		       p->y >= a->p0.y && p->y <= a->p1.y;
26862306a36Sopenharmony_ci	} else {
26962306a36Sopenharmony_ci		i = p->x + p->y * a->tcm->width;
27062306a36Sopenharmony_ci		return i >= a->p0.x + a->p0.y * a->tcm->width &&
27162306a36Sopenharmony_ci		       i <= a->p1.x + a->p1.y * a->tcm->width;
27262306a36Sopenharmony_ci	}
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci/* calculate area width */
27662306a36Sopenharmony_cistatic inline u16 __tcm_area_width(struct tcm_area *area)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	return area->p1.x - area->p0.x + 1;
27962306a36Sopenharmony_ci}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci/* calculate area height */
28262306a36Sopenharmony_cistatic inline u16 __tcm_area_height(struct tcm_area *area)
28362306a36Sopenharmony_ci{
28462306a36Sopenharmony_ci	return area->p1.y - area->p0.y + 1;
28562306a36Sopenharmony_ci}
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci/* calculate number of slots in an area */
28862306a36Sopenharmony_cistatic inline u16 __tcm_sizeof(struct tcm_area *area)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	return area->is2d ?
29162306a36Sopenharmony_ci		__tcm_area_width(area) * __tcm_area_height(area) :
29262306a36Sopenharmony_ci		(area->p1.x - area->p0.x + 1) + (area->p1.y - area->p0.y) *
29362306a36Sopenharmony_ci							area->tcm->width;
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ci#define tcm_sizeof(area) __tcm_sizeof(&(area))
29662306a36Sopenharmony_ci#define tcm_awidth(area) __tcm_area_width(&(area))
29762306a36Sopenharmony_ci#define tcm_aheight(area) __tcm_area_height(&(area))
29862306a36Sopenharmony_ci#define tcm_is_in(pt, area) __tcm_is_in(&(pt), &(area))
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci/* limit a 1D area to the first N pages */
30162306a36Sopenharmony_cistatic inline s32 tcm_1d_limit(struct tcm_area *a, u32 num_pg)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci	if (__tcm_sizeof(a) < num_pg)
30462306a36Sopenharmony_ci		return -ENOMEM;
30562306a36Sopenharmony_ci	if (!num_pg)
30662306a36Sopenharmony_ci		return -EINVAL;
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci	a->p1.x = (a->p0.x + num_pg - 1) % a->tcm->width;
30962306a36Sopenharmony_ci	a->p1.y = a->p0.y + ((a->p0.x + num_pg - 1) / a->tcm->width);
31062306a36Sopenharmony_ci	return 0;
31162306a36Sopenharmony_ci}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci/**
31462306a36Sopenharmony_ci * Iterate through 2D slices of a valid area. Behaves
31562306a36Sopenharmony_ci * syntactically as a for(;;) statement.
31662306a36Sopenharmony_ci *
31762306a36Sopenharmony_ci * @param var		Name of a local variable of type 'struct
31862306a36Sopenharmony_ci *			tcm_area *' that will get modified to
31962306a36Sopenharmony_ci *			contain each slice.
32062306a36Sopenharmony_ci * @param area		Pointer to the VALID parent area. This
32162306a36Sopenharmony_ci *			structure will not get modified
32262306a36Sopenharmony_ci *			throughout the loop.
32362306a36Sopenharmony_ci *
32462306a36Sopenharmony_ci */
32562306a36Sopenharmony_ci#define tcm_for_each_slice(var, area, safe) \
32662306a36Sopenharmony_ci	for (safe = area, \
32762306a36Sopenharmony_ci	     tcm_slice(&safe, &var); \
32862306a36Sopenharmony_ci	     var.tcm; tcm_slice(&safe, &var))
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci#endif
331