xref: /aosp_15_r20/external/igt-gpu-tools/tools/skl_ddb_allocation.c (revision d83cc019efdc2edc6c4b16e9034a3ceb8d35d77c)
1 /*
2  * Copyright © 2014 Intel Corporation
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  */
23 
24 #include <stdint.h>
25 #include <stdio.h>
26 #include <stdbool.h>
27 #include <stddef.h>
28 #include <string.h>
29 
30 /**
31  * container_of - cast a member of a structure out to the containing structure
32  * @ptr:	the pointer to the member.
33  * @type:	the type of the container struct this is embedded in.
34  * @member:	the name of the member within the struct.
35  *
36  */
37 #define container_of(ptr, type, member) ({			\
38 	typeof( ((type *)0)->member ) *__mptr = (ptr);	\
39 	(type *)( (char *)__mptr - offsetof(type,member) );})
40 
41 #define div_u64(a, b)	((a) / (b))
42 
43 /*
44  * Stub a few defines/structures
45  */
46 
47 #define I915_MAX_PIPES	3
48 #define I915_MAX_PLANES	3
49 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
50 
51 #define for_each_pipe(p) for ((p) = 0; (p) < 3; (p)++)
52 #define for_each_plane(pipe, p) for ((p) = 0; (p) < 3; (p)++)
53 
54 #define for_each_crtc(dev, crtc) \
55 	for (int i = 0; i < 3 && (crtc = &crtcs[i].base); i++)
56 
57 #define for_each_intel_crtc(dev, intel_crtc) \
58 	for (int i = 0; i < 3, intel_crtc = &crtcs[i]; i++)
59 
60 enum pipe {
61 	PIPE_A,
62 	PIPE_B,
63 	PIPE_C,
64 };
65 
66 enum plane {
67 	PLANE_1,
68 	PLANE_2,
69 	PLANE_3,
70 };
71 
72 #define pipe_name(p) ((p) + 'A')
73 
74 struct drm_device {
75 	void *dev_private;
76 };
77 
78 struct drm_i915_private {
79 	struct drm_device *dev;
80 };
81 
82 struct drm_crtc {
83 	struct drm_device *dev;
84 	bool active;
85 };
86 
intel_crtc_active(struct drm_crtc * crtc)87 static bool intel_crtc_active(struct drm_crtc *crtc)
88 {
89 	return crtc->active;
90 }
91 
92 struct intel_crtc {
93 	struct drm_crtc base;
94 	enum pipe pipe;
95 };
96 
intel_num_planes(struct intel_crtc * crtc)97 static int intel_num_planes(struct intel_crtc *crtc)
98 {
99 	return 3;
100 }
101 
102 struct intel_crtc crtcs[I915_MAX_PIPES];
103 
104 #define to_intel_crtc(x) container_of(x, struct intel_crtc, base)
105 
106 /*
107  * DDB code
108  */
109 
110 struct intel_wm_config {
111 	unsigned int num_pipes_active;
112 };
113 
114 struct intel_plane_wm_parameters {
115 	uint32_t horiz_pixels;
116 	uint32_t vert_pixels;
117 	uint8_t bytes_per_pixel;
118 	bool enabled;
119 	bool scaled;
120 };
121 
122 struct skl_pipe_wm_parameters {
123 	bool active;
124 	uint32_t pipe_htotal;
125 	uint32_t pixel_rate; /* in KHz */
126 	struct intel_plane_wm_parameters plane[I915_MAX_PLANES];
127 	struct intel_plane_wm_parameters cursor;
128 };
129 
130 struct skl_ddb_entry {
131 	uint16_t start, end;	/* in number of blocks. 'end' is exclusive */
132 };
133 
skl_ddb_entry_size(const struct skl_ddb_entry * entry)134 static inline uint16_t skl_ddb_entry_size(const struct skl_ddb_entry *entry)
135 {
136 	/* end not set, clearly no allocation here. start can be 0 though */
137 	if (entry->end == 0)
138 		return 0;
139 
140 	return entry->end - entry->start;
141 }
142 
skl_ddb_entry_equal(const struct skl_ddb_entry * e1,const struct skl_ddb_entry * e2)143 static inline bool skl_ddb_entry_equal(const struct skl_ddb_entry *e1,
144 				       const struct skl_ddb_entry *e2)
145 {
146 	if (e1->start == e2->start && e1->end == e2->end)
147 		return true;
148 
149 	return false;
150 }
151 
152 struct skl_ddb_allocation {
153 	struct skl_ddb_entry plane[I915_MAX_PIPES][I915_MAX_PLANES];
154 	struct skl_ddb_entry cursor[I915_MAX_PIPES];
155 };
156 
157 /*
158  * On gen9, we need to allocate Display Data Buffer (DDB) portions to the
159  * different active planes.
160  */
161 
162 #define SKL_DDB_SIZE		896	/* in blocks */
163 
164 static void
skl_ddb_get_pipe_allocation_limits(struct drm_device * dev,struct drm_crtc * for_crtc,const struct intel_wm_config * config,const struct skl_pipe_wm_parameters * params,struct skl_ddb_entry * alloc)165 skl_ddb_get_pipe_allocation_limits(struct drm_device *dev,
166 				   struct drm_crtc *for_crtc,
167 				   const struct intel_wm_config *config,
168 				   const struct skl_pipe_wm_parameters *params,
169 				   struct skl_ddb_entry *alloc /* out */)
170 {
171 	struct drm_crtc *crtc;
172 	unsigned int pipe_size, ddb_size;
173 	int nth_active_pipe;
174 
175 	if (!params->active) {
176 		alloc->start = 0;
177 		alloc->end = 0;
178 		return;
179 	}
180 
181 	ddb_size = SKL_DDB_SIZE;
182 	ddb_size -= 4; /* 4 blocks for bypass path allocation */
183 
184 	nth_active_pipe = 0;
185 	for_each_crtc(dev, crtc) {
186 		if (!intel_crtc_active(crtc))
187 			continue;
188 
189 		if (crtc == for_crtc)
190 			break;
191 
192 		nth_active_pipe++;
193 	}
194 
195 	pipe_size = ddb_size / config->num_pipes_active;
196 	alloc->start = nth_active_pipe * ddb_size / config->num_pipes_active;
197 	alloc->end = alloc->start + pipe_size;
198 }
199 
skl_cursor_allocation(const struct intel_wm_config * config)200 static unsigned int skl_cursor_allocation(const struct intel_wm_config *config)
201 {
202 	if (config->num_pipes_active == 1)
203 		return 32;
204 
205 	return 8;
206 }
207 
208 static unsigned int
skl_plane_relative_data_rate(const struct intel_plane_wm_parameters * p)209 skl_plane_relative_data_rate(const struct intel_plane_wm_parameters *p)
210 {
211 	return p->horiz_pixels * p->vert_pixels * p->bytes_per_pixel;
212 }
213 
214 /*
215  * We don't overflow 32 bits. Worst case is 3 planes enabled, each fetching
216  * a 8192x4096@32bpp framebuffer:
217  *   3 * 4096 * 8192  * 4 < 2^32
218  */
219 static unsigned int
skl_get_total_relative_data_rate(struct intel_crtc * intel_crtc,const struct skl_pipe_wm_parameters * params)220 skl_get_total_relative_data_rate(struct intel_crtc *intel_crtc,
221 				 const struct skl_pipe_wm_parameters *params)
222 {
223 	unsigned int total_data_rate = 0;
224 	int plane;
225 
226 	for (plane = 0; plane < intel_num_planes(intel_crtc); plane++) {
227 		const struct intel_plane_wm_parameters *p;
228 
229 		p = &params->plane[plane];
230 		if (!p->enabled)
231 			continue;
232 
233 		total_data_rate += skl_plane_relative_data_rate(p);
234 	}
235 
236 	return total_data_rate;
237 }
238 
239 static void
skl_allocate_pipe_ddb(struct drm_crtc * crtc,const struct intel_wm_config * config,const struct skl_pipe_wm_parameters * params,struct skl_ddb_allocation * ddb)240 skl_allocate_pipe_ddb(struct drm_crtc *crtc,
241 		      const struct intel_wm_config *config,
242 		      const struct skl_pipe_wm_parameters *params,
243 		      struct skl_ddb_allocation *ddb /* out */)
244 {
245 	struct drm_device *dev = crtc->dev;
246 	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
247 	enum pipe pipe = intel_crtc->pipe;
248 	struct skl_ddb_entry alloc;
249 	uint16_t alloc_size, start, cursor_blocks;
250 	uint16_t minimum[I915_MAX_PLANES];
251 	unsigned int total_data_rate;
252 	int plane;
253 
254 	skl_ddb_get_pipe_allocation_limits(dev, crtc, config, params, &alloc);
255 	alloc_size = skl_ddb_entry_size(&alloc);
256 	if (alloc_size == 0) {
257 		memset(ddb->plane[pipe], 0, sizeof(ddb->plane[pipe]));
258 		memset(&ddb->cursor[pipe], 0, sizeof(ddb->cursor[pipe]));
259 		return;
260 	}
261 
262 	cursor_blocks = skl_cursor_allocation(config);
263 	ddb->cursor[pipe].start = alloc.end - cursor_blocks;
264 	ddb->cursor[pipe].end = alloc.end;
265 
266 	alloc_size -= cursor_blocks;
267 	alloc.end -= cursor_blocks;
268 
269 	/* 1. Allocate the mininum required blocks for each active plane */
270 	for_each_plane(pipe, plane) {
271 		const struct intel_plane_wm_parameters *p;
272 
273 		p = &params->plane[plane];
274 		if (!p->enabled)
275 			continue;
276 
277 		minimum[plane] = 8;
278 		alloc_size -= minimum[plane];
279 	}
280 
281 	/*
282 	 * 2. Distribute the remaining space in proportion to the amount of
283 	 * data each plane needs to fetch from memory.
284 	 *
285 	 * FIXME: we may not allocate every single block here.
286 	 */
287 	total_data_rate = skl_get_total_relative_data_rate(intel_crtc, params);
288 
289 	start = alloc.start;
290 	for (plane = 0; plane < intel_num_planes(intel_crtc); plane++) {
291 		const struct intel_plane_wm_parameters *p;
292 		unsigned int data_rate;
293 		uint16_t plane_blocks;
294 
295 		p = &params->plane[plane];
296 		if (!p->enabled)
297 			continue;
298 
299 		data_rate = skl_plane_relative_data_rate(p);
300 
301 		/*
302 		 * promote the expression to 64 bits to avoid overflowing, the
303 		 * result is < available as data_rate / total_data_rate < 1
304 		 */
305 		plane_blocks = minimum[plane];
306 		plane_blocks += div_u64((uint64_t)alloc_size * data_rate,
307 					total_data_rate);
308 
309 		ddb->plane[pipe][plane].start = start;
310 		ddb->plane[pipe][plane].end = start + plane_blocks;
311 
312 		start += plane_blocks;
313 	}
314 
315 }
316 
skl_ddb_check_entry(struct skl_ddb_entry * entry,int16_t * cursor)317 static void skl_ddb_check_entry(struct skl_ddb_entry *entry, int16_t *cursor)
318 {
319 
320 	if (skl_ddb_entry_size(entry) == 0)
321 		return;
322 
323 	/* check that ->start is the next available block */
324 	if (entry->start < *cursor)
325 		printf("error: allocation overlaps previous block\n");
326 	else if (entry->start >= *cursor + 1)
327 		printf("warning: allocation leaves a hole\n");
328 
329 	*cursor = entry->end;
330 }
331 
skl_ddb_check_last_allocation(int16_t cursor)332 static void skl_ddb_check_last_allocation(int16_t cursor)
333 {
334 	uint16_t last_offset = SKL_DDB_SIZE - 4;
335 
336 	if (cursor < last_offset)
337 		printf("warning: %d blocks not allocated\n",
338 		       last_offset - cursor);
339 	else if (cursor > last_offset)
340 		printf("error: allocation greater than available space\n");
341 }
342 
skl_ddb_print(struct skl_ddb_allocation * ddb)343 static void skl_ddb_print(struct skl_ddb_allocation *ddb)
344 {
345 	struct skl_ddb_entry *entry;
346 	enum pipe pipe;
347 	int plane;
348 	int16_t cursor = 0;
349 
350 	printf("%-15s%8s%8s%8s\n", "", "Start", "End", "Size");
351 
352 	for_each_pipe(pipe) {
353 		printf("Pipe %c\n", pipe_name(pipe));
354 
355 		for_each_plane(pipe, plane) {
356 			entry = &ddb->plane[pipe][plane];
357 
358 			printf("  Plane%-8d%8u%8u%8u\n", plane + 1,
359 			       entry->start, entry->end,
360 			       skl_ddb_entry_size(entry));
361 
362 			skl_ddb_check_entry(entry, &cursor);
363 		}
364 
365 		entry = &ddb->cursor[pipe];
366 		printf("  %-13s%8u%8u%8u\n", "Cursor", entry->start,
367 		       entry->end, skl_ddb_entry_size(entry));
368 
369 		skl_ddb_check_entry(entry, &cursor);
370 	}
371 
372 	skl_ddb_check_last_allocation(cursor);
373 }
374 
375 static struct drm_device drm_device;
376 static struct drm_i915_private drm_i915_private;
377 
init_stub(void)378 static void init_stub(void)
379 {
380 	int i;
381 
382 	drm_device.dev_private = &drm_i915_private;
383 	drm_i915_private.dev = &drm_device;
384 
385 	for (i = 0; i < I915_MAX_PIPES; i++) {
386 		crtcs[i].base.dev = &drm_device;
387 		crtcs[i].pipe = i;
388 	}
389 }
390 
391 struct wm_input {
392 	struct intel_wm_config config;
393 	struct skl_pipe_wm_parameters params[I915_MAX_PIPES];
394 };
395 
wm_input_reset(struct wm_input * in)396 static void wm_input_reset(struct wm_input *in)
397 {
398 	memset(in, 0, sizeof(*in));
399 }
400 
wm_enable_plane(struct wm_input * in,enum pipe pipe,enum plane plane,uint32_t width,uint32_t height,int bpp)401 static void wm_enable_plane(struct wm_input *in,
402 			    enum pipe pipe, enum plane plane,
403 			    uint32_t width, uint32_t height, int bpp)
404 {
405 	enum pipe i;
406 
407 	in->params[pipe].active = 1;
408 
409 	in->config.num_pipes_active = 0;
410 	for_each_pipe(i)
411 		if (in->params[i].active)
412 			in->config.num_pipes_active++;
413 
414 	in->params[pipe].plane[plane].horiz_pixels = width;
415 	in->params[pipe].plane[plane].vert_pixels = height;
416 	in->params[pipe].plane[plane].bytes_per_pixel = bpp;
417 	in->params[pipe].plane[plane].enabled = true;
418 }
419 
skl_ddb_allocate(struct wm_input * in,struct skl_ddb_allocation * out)420 static void skl_ddb_allocate(struct wm_input *in,
421 			     struct skl_ddb_allocation *out)
422 {
423 	struct drm_crtc *crtc;
424 
425 	for_each_crtc(, crtc) {
426 		enum pipe pipe = to_intel_crtc(crtc)->pipe;
427 
428 		skl_allocate_pipe_ddb(crtc,
429 				      &in->config, &in->params[pipe], out);
430 	}
431 }
432 
main(int argc,char ** argv)433 int main(int argc, char **argv)
434 {
435 	struct wm_input in;
436 	static struct skl_ddb_allocation ddb;
437 
438 	init_stub();
439 
440 	wm_input_reset(&in);
441 	wm_enable_plane(&in, PIPE_A, PLANE_1, 1280, 1024, 4);
442 	wm_enable_plane(&in, PIPE_A, PLANE_2,  100,  100, 4);
443 	skl_ddb_allocate(&in, &ddb);
444 	skl_ddb_print(&ddb);
445 
446 	return 0;
447 }
448