summaryrefslogtreecommitdiffstats
path: root/src/lib/edid_fill_fb.c
blob: e4fb08c3fdac3995723d452af1d7263bed0fec5f (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
/* SPDX-License-Identifier: MIT */

#include <console/console.h>
#include <edid.h>
#include <boot/coreboot_tables.h>
#include <framebuffer_info.h>
#include <string.h>
#include <stdlib.h>
#include <commonlib/list.h>

struct fb_info {
	struct list_node node;
	struct lb_framebuffer fb;
};
static struct list_node list;

/*
 * Allocate a new framebuffer info struct on heap.
 * Returns NULL on error.
 */
static struct fb_info *fb_new_framebuffer_info(void)
{
	struct fb_info *ret;
	ret = malloc(sizeof(struct fb_info));
	if (ret)
		memset(ret, 0, sizeof(struct fb_info));

	return ret;
}

/*
 * Fills a provided framebuffer info struct and adds it to the internal list if it's
 * valid. Returns NULL on error.
 */
struct fb_info *
fb_add_framebuffer_info_ex(const struct lb_framebuffer *fb)
{
	struct fb_info *info;
	uint8_t bpp_mask;

	/* Validate input */
	if (!fb || !fb->x_resolution || !fb->y_resolution || !fb->bytes_per_line ||
	    !fb->bits_per_pixel) {
		printk(BIOS_ERR, "%s: Invalid framebuffer data provided\n", __func__);
		return NULL;
	}

	bpp_mask = fb->blue_mask_size + fb->green_mask_size + fb->red_mask_size +
		fb->reserved_mask_size;
	if (bpp_mask > fb->bits_per_pixel) {
		printk(BIOS_ERR,
		       "%s: channel bit mask=%d is greater than BPP=%d ."
		       " This is a driver bug. Framebuffer is invalid.\n",
		       __func__, bpp_mask, fb->bits_per_pixel);
		return NULL;
	} else if (bpp_mask != fb->bits_per_pixel) {
		printk(BIOS_WARNING,
		       "%s: channel bit mask=%d and BPP=%d don't match."
		       " This is a driver bug.\n",
		       __func__, bpp_mask, fb->bits_per_pixel);
	}

	info = fb_new_framebuffer_info();
	if (!info)
		return NULL;

	printk(BIOS_INFO, "framebuffer_info: bytes_per_line: %d, bits_per_pixel: %d\n "
			  "                  x_res x y_res: %d x %d, size: %d at 0x%llx\n",
			fb->bytes_per_line, fb->bits_per_pixel, fb->x_resolution,
			fb->y_resolution, (fb->bytes_per_line * fb->y_resolution),
			fb->physical_address);

	/* Update */
	info->fb = *fb;

	list_insert_after(&info->node, &list);

	return info;
}

/*
 * Allocates a new framebuffer info struct and fills it for 32/24/16bpp framebuffers.
 * Intended for drivers that only support reporting the current information or have a single
 * modeset invocation.
 *
 * Complex drivers should use fb_add_framebuffer_info_ex() instead.
 */
struct fb_info *
fb_add_framebuffer_info(uintptr_t fb_addr, uint32_t x_resolution,
			uint32_t y_resolution, uint32_t bytes_per_line,
			uint8_t bits_per_pixel)
{
	struct fb_info *info = NULL;

	switch (bits_per_pixel) {
	case 32:
	case 24: {
		/* FIXME: 24 BPP might be RGB8 or XRGB8 */
		/* packed into 4-byte words */

		const struct lb_framebuffer fb = {
			.physical_address    = fb_addr,
			.x_resolution        = x_resolution,
			.y_resolution        = y_resolution,
			.bytes_per_line      = bytes_per_line,
			.bits_per_pixel      = bits_per_pixel,
			.red_mask_pos        = 16,
			.red_mask_size       = 8,
			.green_mask_pos      = 8,
			.green_mask_size     = 8,
			.blue_mask_pos       = 0,
			.blue_mask_size      = 8,
			.reserved_mask_pos   = 24,
			.reserved_mask_size  = 8,
			.orientation         = LB_FB_ORIENTATION_NORMAL,
		};

		info = fb_add_framebuffer_info_ex(&fb);
		break;
	}
	case 16: {
		/* packed into 2-byte words */
		const struct lb_framebuffer fb = {
			.physical_address   = fb_addr,
			.x_resolution       = x_resolution,
			.y_resolution       = y_resolution,
			.bytes_per_line     = bytes_per_line,
			.bits_per_pixel     = 16,
			.red_mask_pos       = 11,
			.red_mask_size      = 5,
			.green_mask_pos     = 5,
			.green_mask_size    = 6,
			.blue_mask_pos      = 0,
			.blue_mask_size     = 5,
			.reserved_mask_pos  = 0,
			.reserved_mask_size = 0,
			.orientation        = LB_FB_ORIENTATION_NORMAL,
		};
		info = fb_add_framebuffer_info_ex(&fb);
		break;
	}
	default:
		printk(BIOS_ERR, "%s: unsupported BPP %d\n", __func__, bits_per_pixel);
	}
	if (!info)
		printk(BIOS_ERR, "%s: failed to add framebuffer info\n", __func__);

	return info;
}

void fb_set_orientation(struct fb_info *info, enum lb_fb_orientation orientation)
{
	if (!info)
		return;

	info->fb.orientation = orientation;
}

/*
 * Take an edid, and create a framebuffer.
 */
struct fb_info *fb_new_framebuffer_info_from_edid(const struct edid *edid,
							 uintptr_t fb_addr)
{
	return fb_add_framebuffer_info(fb_addr, edid->x_resolution, edid->y_resolution,
		edid->bytes_per_line, edid->framebuffer_bits_per_pixel);
}

int fill_lb_framebuffer(struct lb_framebuffer *framebuffer)
{
	struct fb_info *i;

	list_for_each(i, list, node) {
		//TODO: Add support for advertising all framebuffers in this list
		*framebuffer = i->fb;
		return 0;
	}
	return -1;
}