1 /*
2  * Copyright (c) 2019 Intel Corp.
3  *
4  * This code attempts to be endian-agnostic. It manipulates the framebuffer
5  * address space only in 32-bit words (and assumes those words are 0xAARRGGBB).
6  *
7  * SPDX-License-Identifier: Apache-2.0
8  */
9 
10 #define DT_DRV_COMPAT intel_multiboot_framebuffer
11 
12 #include <errno.h>
13 
14 #include <zephyr/arch/x86/multiboot.h>
15 #include <zephyr/devicetree.h>
16 #include <zephyr/drivers/display.h>
17 #include <string.h>
18 
19 struct framebuf_dev_config {
20 	uint16_t width;
21 	uint16_t height;
22 };
23 
24 struct framebuf_dev_data {
25 	void *buffer;
26 	uint32_t pitch;
27 };
28 
framebuf_set_pixel_format(const struct device * dev,const enum display_pixel_format format)29 static int framebuf_set_pixel_format(const struct device *dev,
30 				     const enum display_pixel_format format)
31 {
32 	switch (format) {
33 	case PIXEL_FORMAT_ARGB_8888:
34 		return 0;
35 	default:
36 		return -ENOTSUP;
37 	}
38 }
39 
framebuf_set_orientation(const struct device * dev,const enum display_orientation orientation)40 static int framebuf_set_orientation(const struct device *dev,
41 				    const enum display_orientation orientation)
42 {
43 	switch (orientation) {
44 	case DISPLAY_ORIENTATION_NORMAL:
45 		return 0;
46 	default:
47 		return -ENOTSUP;
48 	}
49 }
50 
framebuf_get_capabilities(const struct device * dev,struct display_capabilities * caps)51 static void framebuf_get_capabilities(const struct device *dev,
52 				      struct display_capabilities *caps)
53 {
54 	const struct framebuf_dev_config *config = dev->config;
55 
56 	caps->x_resolution = config->width;
57 	caps->y_resolution = config->height;
58 	caps->supported_pixel_formats = PIXEL_FORMAT_ARGB_8888;
59 	caps->screen_info = 0;
60 	caps->current_pixel_format = PIXEL_FORMAT_ARGB_8888;
61 	caps->current_orientation = DISPLAY_ORIENTATION_NORMAL;
62 }
63 
framebuf_write(const struct device * dev,const uint16_t x,const uint16_t y,const struct display_buffer_descriptor * desc,const void * buf)64 static int framebuf_write(const struct device *dev, const uint16_t x,
65 			  const uint16_t y,
66 			  const struct display_buffer_descriptor *desc,
67 			  const void *buf)
68 {
69 	struct framebuf_dev_data *data = dev->data;
70 	uint32_t *dst = data->buffer;
71 	const uint32_t *src = buf;
72 	uint32_t row;
73 
74 	dst += x;
75 	dst += (y * data->pitch);
76 
77 	for (row = 0; row < desc->height; ++row) {
78 		(void) memcpy(dst, src, desc->width * sizeof(uint32_t));
79 		dst += data->pitch;
80 		src += desc->pitch;
81 	}
82 
83 	return 0;
84 }
85 
framebuf_read(const struct device * dev,const uint16_t x,const uint16_t y,const struct display_buffer_descriptor * desc,void * buf)86 static int framebuf_read(const struct device *dev, const uint16_t x,
87 			 const uint16_t y,
88 			 const struct display_buffer_descriptor *desc,
89 			 void *buf)
90 {
91 	struct framebuf_dev_data *data = dev->data;
92 	uint32_t *src = data->buffer;
93 	uint32_t *dst = buf;
94 	uint32_t row;
95 
96 	src += x;
97 	src += (y * data->pitch);
98 
99 	for (row = 0; row < desc->height; ++row) {
100 		(void) memcpy(dst, src, desc->width * sizeof(uint32_t));
101 		src += data->pitch;
102 		dst += desc->pitch;
103 	}
104 
105 	return 0;
106 }
107 
108 DEVICE_API(display, framebuf_display_api) = {
109 	.write = framebuf_write,
110 	.read = framebuf_read,
111 	.get_capabilities = framebuf_get_capabilities,
112 	.set_pixel_format = framebuf_set_pixel_format,
113 	.set_orientation = framebuf_set_orientation
114 };
115 
multiboot_framebuf_init(const struct device * dev)116 static int multiboot_framebuf_init(const struct device *dev)
117 {
118 	const struct framebuf_dev_config *config = dev->config;
119 	struct framebuf_dev_data *data = dev->data;
120 	struct multiboot_info *info = &multiboot_info;
121 
122 	if ((info->flags & MULTIBOOT_INFO_FLAGS_FB) &&
123 	    (info->fb_width >= config->width) &&
124 	    (info->fb_height >= config->height) &&
125 	    (info->fb_bpp == 32) && (info->fb_addr_hi == 0)) {
126 		/*
127 		 * We have a usable multiboot framebuffer - it is 32 bpp
128 		 * and at least as large as the requested dimensions. Compute
129 		 * the pitch and adjust the start address center our canvas.
130 		 */
131 
132 		uint16_t adj_x;
133 		uint16_t adj_y;
134 		uint32_t *buffer;
135 
136 		adj_x = info->fb_width - config->width;
137 		adj_y = info->fb_height - config->height;
138 		data->pitch = (info->fb_pitch / 4) + adj_x;
139 		adj_x /= 2U;
140 		adj_y /= 2U;
141 		buffer = (uint32_t *) (uintptr_t) info->fb_addr_lo;
142 		buffer += adj_x;
143 		buffer += adj_y * data->pitch;
144 		data->buffer = buffer;
145 		return 0;
146 	} else {
147 		return -ENOTSUP;
148 	}
149 }
150 
151 static const struct framebuf_dev_config config = {
152 	.width = DT_INST_PROP(0, width),
153 	.height = DT_INST_PROP(0, height),
154 };
155 
156 static struct framebuf_dev_data data;
157 
158 DEVICE_DT_INST_DEFINE(0, multiboot_framebuf_init, NULL, &data, &config,
159 		      PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
160 		      &framebuf_display_api);
161