mirror of
				https://github.com/tiagovignatti/intel-gpu-tools.git
				synced 2025-11-04 03:58:27 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			448 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			448 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Copyright © 2014 Intel Corporation
 | 
						|
 *
 | 
						|
 * Permission is hereby granted, free of charge, to any person obtaining a
 | 
						|
 * copy of this software and associated documentation files (the "Software"),
 | 
						|
 * to deal in the Software without restriction, including without limitation
 | 
						|
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 | 
						|
 * and/or sell copies of the Software, and to permit persons to whom the
 | 
						|
 * Software is furnished to do so, subject to the following conditions:
 | 
						|
 *
 | 
						|
 * The above copyright notice and this permission notice (including the next
 | 
						|
 * paragraph) shall be included in all copies or substantial portions of the
 | 
						|
 * Software.
 | 
						|
 *
 | 
						|
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
						|
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
						|
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 | 
						|
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
						|
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 | 
						|
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 | 
						|
 * DEALINGS IN THE SOFTWARE.
 | 
						|
 */
 | 
						|
 | 
						|
#include <stdint.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdbool.h>
 | 
						|
#include <stddef.h>
 | 
						|
#include <string.h>
 | 
						|
 | 
						|
/**
 | 
						|
 * container_of - cast a member of a structure out to the containing structure
 | 
						|
 * @ptr:	the pointer to the member.
 | 
						|
 * @type:	the type of the container struct this is embedded in.
 | 
						|
 * @member:	the name of the member within the struct.
 | 
						|
 *
 | 
						|
 */
 | 
						|
#define container_of(ptr, type, member) ({			\
 | 
						|
	typeof( ((type *)0)->member ) *__mptr = (ptr);	\
 | 
						|
	(type *)( (char *)__mptr - offsetof(type,member) );})
 | 
						|
 | 
						|
#define div_u64(a, b)	((a) / (b))
 | 
						|
 | 
						|
/*
 | 
						|
 * Stub a few defines/structures
 | 
						|
 */
 | 
						|
 | 
						|
#define I915_MAX_PIPES	3
 | 
						|
#define I915_MAX_PLANES	3
 | 
						|
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
 | 
						|
 | 
						|
#define for_each_pipe(p) for ((p) = 0; (p) < 3; (p)++)
 | 
						|
#define for_each_plane(pipe, p) for ((p) = 0; (p) < 3; (p)++)
 | 
						|
 | 
						|
#define for_each_crtc(dev, crtc) \
 | 
						|
	for (int i = 0; i < 3 && (crtc = &crtcs[i].base); i++)
 | 
						|
 | 
						|
#define for_each_intel_crtc(dev, intel_crtc) \
 | 
						|
	for (int i = 0; i < 3, intel_crtc = &crtcs[i]; i++)
 | 
						|
 | 
						|
enum pipe {
 | 
						|
	PIPE_A,
 | 
						|
	PIPE_B,
 | 
						|
	PIPE_C,
 | 
						|
};
 | 
						|
 | 
						|
enum plane {
 | 
						|
	PLANE_1,
 | 
						|
	PLANE_2,
 | 
						|
	PLANE_3,
 | 
						|
};
 | 
						|
 | 
						|
#define pipe_name(p) ((p) + 'A')
 | 
						|
 | 
						|
struct drm_device {
 | 
						|
	void *dev_private;
 | 
						|
};
 | 
						|
 | 
						|
struct drm_i915_private {
 | 
						|
	struct drm_device *dev;
 | 
						|
};
 | 
						|
 | 
						|
struct drm_crtc {
 | 
						|
	struct drm_device *dev;
 | 
						|
	bool active;
 | 
						|
};
 | 
						|
 | 
						|
static bool intel_crtc_active(struct drm_crtc *crtc)
 | 
						|
{
 | 
						|
	return crtc->active;
 | 
						|
}
 | 
						|
 | 
						|
struct intel_crtc {
 | 
						|
	struct drm_crtc base;
 | 
						|
	enum pipe pipe;
 | 
						|
};
 | 
						|
 | 
						|
static int intel_num_planes(struct intel_crtc *crtc)
 | 
						|
{
 | 
						|
	return 3;
 | 
						|
}
 | 
						|
 | 
						|
struct intel_crtc crtcs[I915_MAX_PIPES];
 | 
						|
 | 
						|
#define to_intel_crtc(x) container_of(x, struct intel_crtc, base)
 | 
						|
 | 
						|
/*
 | 
						|
 * DDB code
 | 
						|
 */
 | 
						|
 | 
						|
struct intel_wm_config {
 | 
						|
	unsigned int num_pipes_active;
 | 
						|
};
 | 
						|
 | 
						|
struct intel_plane_wm_parameters {
 | 
						|
	uint32_t horiz_pixels;
 | 
						|
	uint32_t vert_pixels;
 | 
						|
	uint8_t bytes_per_pixel;
 | 
						|
	bool enabled;
 | 
						|
	bool scaled;
 | 
						|
};
 | 
						|
 | 
						|
struct skl_pipe_wm_parameters {
 | 
						|
	bool active;
 | 
						|
	uint32_t pipe_htotal;
 | 
						|
	uint32_t pixel_rate; /* in KHz */
 | 
						|
	struct intel_plane_wm_parameters plane[I915_MAX_PLANES];
 | 
						|
	struct intel_plane_wm_parameters cursor;
 | 
						|
};
 | 
						|
 | 
						|
struct skl_ddb_entry {
 | 
						|
	uint16_t start, end;	/* in number of blocks. 'end' is exclusive */
 | 
						|
};
 | 
						|
 | 
						|
static inline uint16_t skl_ddb_entry_size(const struct skl_ddb_entry *entry)
 | 
						|
{
 | 
						|
	/* end not set, clearly no allocation here. start can be 0 though */
 | 
						|
	if (entry->end == 0)
 | 
						|
		return 0;
 | 
						|
 | 
						|
	return entry->end - entry->start;
 | 
						|
}
 | 
						|
 | 
						|
static inline bool skl_ddb_entry_equal(const struct skl_ddb_entry *e1,
 | 
						|
				       const struct skl_ddb_entry *e2)
 | 
						|
{
 | 
						|
	if (e1->start == e2->start && e1->end == e2->end)
 | 
						|
		return true;
 | 
						|
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
struct skl_ddb_allocation {
 | 
						|
	struct skl_ddb_entry plane[I915_MAX_PIPES][I915_MAX_PLANES];
 | 
						|
	struct skl_ddb_entry cursor[I915_MAX_PIPES];
 | 
						|
};
 | 
						|
 | 
						|
/*
 | 
						|
 * On gen9, we need to allocate Display Data Buffer (DDB) portions to the
 | 
						|
 * different active planes.
 | 
						|
 */
 | 
						|
 | 
						|
#define SKL_DDB_SIZE		896	/* in blocks */
 | 
						|
 | 
						|
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 /* out */)
 | 
						|
{
 | 
						|
	struct drm_crtc *crtc;
 | 
						|
	unsigned int pipe_size, ddb_size;
 | 
						|
	int nth_active_pipe;
 | 
						|
 | 
						|
	if (!params->active) {
 | 
						|
		alloc->start = 0;
 | 
						|
		alloc->end = 0;
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	ddb_size = SKL_DDB_SIZE;
 | 
						|
	ddb_size -= 4; /* 4 blocks for bypass path allocation */
 | 
						|
 | 
						|
	nth_active_pipe = 0;
 | 
						|
	for_each_crtc(dev, crtc) {
 | 
						|
		if (!intel_crtc_active(crtc))
 | 
						|
			continue;
 | 
						|
 | 
						|
		if (crtc == for_crtc)
 | 
						|
			break;
 | 
						|
 | 
						|
		nth_active_pipe++;
 | 
						|
	}
 | 
						|
 | 
						|
	pipe_size = ddb_size / config->num_pipes_active;
 | 
						|
	alloc->start = nth_active_pipe * ddb_size / config->num_pipes_active;
 | 
						|
	alloc->end = alloc->start + pipe_size;
 | 
						|
}
 | 
						|
 | 
						|
static unsigned int skl_cursor_allocation(const struct intel_wm_config *config)
 | 
						|
{
 | 
						|
	if (config->num_pipes_active == 1)
 | 
						|
		return 32;
 | 
						|
 | 
						|
	return 8;
 | 
						|
}
 | 
						|
 | 
						|
static unsigned int
 | 
						|
skl_plane_relative_data_rate(const struct intel_plane_wm_parameters *p)
 | 
						|
{
 | 
						|
	return p->horiz_pixels * p->vert_pixels * p->bytes_per_pixel;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * We don't overflow 32 bits. Worst case is 3 planes enabled, each fetching
 | 
						|
 * a 8192x4096@32bpp framebuffer:
 | 
						|
 *   3 * 4096 * 8192  * 4 < 2^32
 | 
						|
 */
 | 
						|
static unsigned int
 | 
						|
skl_get_total_relative_data_rate(struct intel_crtc *intel_crtc,
 | 
						|
				 const struct skl_pipe_wm_parameters *params)
 | 
						|
{
 | 
						|
	unsigned int total_data_rate = 0;
 | 
						|
	int plane;
 | 
						|
 | 
						|
	for (plane = 0; plane < intel_num_planes(intel_crtc); plane++) {
 | 
						|
		const struct intel_plane_wm_parameters *p;
 | 
						|
 | 
						|
		p = ¶ms->plane[plane];
 | 
						|
		if (!p->enabled)
 | 
						|
			continue;
 | 
						|
 | 
						|
		total_data_rate += skl_plane_relative_data_rate(p);
 | 
						|
	}
 | 
						|
 | 
						|
	return total_data_rate;
 | 
						|
}
 | 
						|
 | 
						|
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 /* out */)
 | 
						|
{
 | 
						|
	struct drm_device *dev = crtc->dev;
 | 
						|
	struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
 | 
						|
	enum pipe pipe = intel_crtc->pipe;
 | 
						|
	struct skl_ddb_entry alloc;
 | 
						|
	uint16_t alloc_size, start, cursor_blocks;
 | 
						|
	uint16_t minimum[I915_MAX_PLANES];
 | 
						|
	unsigned int total_data_rate;
 | 
						|
	int plane;
 | 
						|
 | 
						|
	skl_ddb_get_pipe_allocation_limits(dev, crtc, config, params, &alloc);
 | 
						|
	alloc_size = skl_ddb_entry_size(&alloc);
 | 
						|
	if (alloc_size == 0) {
 | 
						|
		memset(ddb->plane[pipe], 0, sizeof(ddb->plane[pipe]));
 | 
						|
		memset(&ddb->cursor[pipe], 0, sizeof(ddb->cursor[pipe]));
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	cursor_blocks = skl_cursor_allocation(config);
 | 
						|
	ddb->cursor[pipe].start = alloc.end - cursor_blocks;
 | 
						|
	ddb->cursor[pipe].end = alloc.end;
 | 
						|
 | 
						|
	alloc_size -= cursor_blocks;
 | 
						|
	alloc.end -= cursor_blocks;
 | 
						|
 | 
						|
	/* 1. Allocate the mininum required blocks for each active plane */
 | 
						|
	for_each_plane(pipe, plane) {
 | 
						|
		const struct intel_plane_wm_parameters *p;
 | 
						|
 | 
						|
		p = ¶ms->plane[plane];
 | 
						|
		if (!p->enabled)
 | 
						|
			continue;
 | 
						|
 | 
						|
		minimum[plane] = 8;
 | 
						|
		alloc_size -= minimum[plane];
 | 
						|
	}
 | 
						|
 | 
						|
	/*
 | 
						|
	 * 2. Distribute the remaining space in proportion to the amount of
 | 
						|
	 * data each plane needs to fetch from memory.
 | 
						|
	 *
 | 
						|
	 * FIXME: we may not allocate every single block here.
 | 
						|
	 */
 | 
						|
	total_data_rate = skl_get_total_relative_data_rate(intel_crtc, params);
 | 
						|
 | 
						|
	start = alloc.start;
 | 
						|
	for (plane = 0; plane < intel_num_planes(intel_crtc); plane++) {
 | 
						|
		const struct intel_plane_wm_parameters *p;
 | 
						|
		unsigned int data_rate;
 | 
						|
		uint16_t plane_blocks;
 | 
						|
 | 
						|
		p = ¶ms->plane[plane];
 | 
						|
		if (!p->enabled)
 | 
						|
			continue;
 | 
						|
 | 
						|
		data_rate = skl_plane_relative_data_rate(p);
 | 
						|
 | 
						|
		/*
 | 
						|
		 * promote the expression to 64 bits to avoid overflowing, the
 | 
						|
		 * result is < available as data_rate / total_data_rate < 1
 | 
						|
		 */
 | 
						|
		plane_blocks = minimum[plane];
 | 
						|
		plane_blocks += div_u64((uint64_t)alloc_size * data_rate,
 | 
						|
					total_data_rate);
 | 
						|
 | 
						|
		ddb->plane[pipe][plane].start = start;
 | 
						|
		ddb->plane[pipe][plane].end = start + plane_blocks;
 | 
						|
 | 
						|
		start += plane_blocks;
 | 
						|
	}
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
static void skl_ddb_check_entry(struct skl_ddb_entry *entry, int16_t *cursor)
 | 
						|
{
 | 
						|
 | 
						|
	if (skl_ddb_entry_size(entry) == 0)
 | 
						|
		return;
 | 
						|
 | 
						|
	/* check that ->start is the next available block */
 | 
						|
	if (entry->start < *cursor)
 | 
						|
		printf("error: allocation overlaps previous block\n");
 | 
						|
	else if (entry->start >= *cursor + 1)
 | 
						|
		printf("warning: allocation leaves a hole\n");
 | 
						|
 | 
						|
	*cursor = entry->end;
 | 
						|
}
 | 
						|
 | 
						|
static void skl_ddb_check_last_allocation(int16_t cursor)
 | 
						|
{
 | 
						|
	uint16_t last_offset = SKL_DDB_SIZE - 4;
 | 
						|
 | 
						|
	if (cursor < last_offset)
 | 
						|
		printf("warning: %d blocks not allocated\n",
 | 
						|
		       last_offset - cursor);
 | 
						|
	else if (cursor > last_offset)
 | 
						|
		printf("error: allocation greater than available space\n");
 | 
						|
}
 | 
						|
 | 
						|
static void skl_ddb_print(struct skl_ddb_allocation *ddb)
 | 
						|
{
 | 
						|
	struct skl_ddb_entry *entry;
 | 
						|
	enum pipe pipe;
 | 
						|
	int plane;
 | 
						|
	int16_t cursor = 0;
 | 
						|
 | 
						|
	printf("%-15s%8s%8s%8s\n", "", "Start", "End", "Size");
 | 
						|
 | 
						|
	for_each_pipe(pipe) {
 | 
						|
		printf("Pipe %c\n", pipe_name(pipe));
 | 
						|
 | 
						|
		for_each_plane(pipe, plane) {
 | 
						|
			entry = &ddb->plane[pipe][plane];
 | 
						|
 | 
						|
			printf("  Plane%-8d%8u%8u%8u\n", plane + 1,
 | 
						|
			       entry->start, entry->end,
 | 
						|
			       skl_ddb_entry_size(entry));
 | 
						|
 | 
						|
			skl_ddb_check_entry(entry, &cursor);
 | 
						|
		}
 | 
						|
 | 
						|
		entry = &ddb->cursor[pipe];
 | 
						|
		printf("  %-13s%8u%8u%8u\n", "Cursor", entry->start,
 | 
						|
		       entry->end, skl_ddb_entry_size(entry));
 | 
						|
 | 
						|
		skl_ddb_check_entry(entry, &cursor);
 | 
						|
	}
 | 
						|
 | 
						|
	skl_ddb_check_last_allocation(cursor);
 | 
						|
}
 | 
						|
 | 
						|
static struct drm_device drm_device;
 | 
						|
static struct drm_i915_private drm_i915_private;
 | 
						|
 | 
						|
static void init_stub(void)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
 | 
						|
	drm_device.dev_private = &drm_i915_private;
 | 
						|
	drm_i915_private.dev = &drm_device;
 | 
						|
 | 
						|
	for (i = 0; i < I915_MAX_PIPES; i++) {
 | 
						|
		crtcs[i].base.dev = &drm_device;
 | 
						|
		crtcs[i].pipe = i;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
struct wm_input {
 | 
						|
	struct intel_wm_config config;
 | 
						|
	struct skl_pipe_wm_parameters params[I915_MAX_PIPES];
 | 
						|
};
 | 
						|
 | 
						|
static void wm_input_reset(struct wm_input *in)
 | 
						|
{
 | 
						|
	memset(in, 0, sizeof(*in));
 | 
						|
}
 | 
						|
 | 
						|
static void wm_enable_plane(struct wm_input *in,
 | 
						|
			    enum pipe pipe, enum plane plane,
 | 
						|
			    uint32_t width, uint32_t height, int bpp)
 | 
						|
{
 | 
						|
	enum pipe i;
 | 
						|
 | 
						|
	in->params[pipe].active = 1;
 | 
						|
 | 
						|
	in->config.num_pipes_active = 0;
 | 
						|
	for_each_pipe(i)
 | 
						|
		if (in->params[i].active)
 | 
						|
			in->config.num_pipes_active++;
 | 
						|
 | 
						|
	in->params[pipe].plane[plane].horiz_pixels = width;
 | 
						|
	in->params[pipe].plane[plane].vert_pixels = height;
 | 
						|
	in->params[pipe].plane[plane].bytes_per_pixel = bpp;
 | 
						|
	in->params[pipe].plane[plane].enabled = true;
 | 
						|
}
 | 
						|
 | 
						|
static void skl_ddb_allocate(struct wm_input *in,
 | 
						|
			     struct skl_ddb_allocation *out)
 | 
						|
{
 | 
						|
	struct drm_crtc *crtc;
 | 
						|
 | 
						|
	for_each_crtc(, crtc) {
 | 
						|
		enum pipe pipe = to_intel_crtc(crtc)->pipe;
 | 
						|
 | 
						|
		skl_allocate_pipe_ddb(crtc,
 | 
						|
				      &in->config, &in->params[pipe], out);
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
int main(int argc, char **argv)
 | 
						|
{
 | 
						|
	struct wm_input in;
 | 
						|
	static struct skl_ddb_allocation ddb;
 | 
						|
 | 
						|
	init_stub();
 | 
						|
 | 
						|
	wm_input_reset(&in);
 | 
						|
	wm_enable_plane(&in, PIPE_A, PLANE_1, 1280, 1024, 4);
 | 
						|
	wm_enable_plane(&in, PIPE_A, PLANE_2,  100,  100, 4);
 | 
						|
	skl_ddb_allocate(&in, &ddb);
 | 
						|
	skl_ddb_print(&ddb);
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 |