Serenity Operating System
1#ifdef __OpenBSD__
2#include "FB.h"
3#include <AK/Assertions.h>
4#include <AK/LogStream.h>
5#include <sys/cdefs.h>
6#include <sys/ioctl.h>
7#include <sys/mman.h>
8#include <sys/time.h>
9#include <dev/wscons/wsconsio.h>
10
11/* XXX yuck - this doesn't live in /usr/include though */
12#include "/usr/src/sys/dev/pci/drm/include/uapi/drm/i915_drm.h"
13
14static struct {
15 int width;
16 int height;
17 int pitch;
18 size_t size;
19 uint64_t res_conn_buf[10];
20} drm_screen;
21
22#define MAX_FBS 2
23
24static struct openbsd_fb {
25 void *fb;
26 int crtc_id;
27 struct drm_mode_crtc crtc;
28} drm_fbs[MAX_FBS];
29
30#define DRM_DEVICE "/dev/drm0"
31
32int fb_create_buffers(int fd);
33
34int fb_get_size_in_bytes(int fd, size_t* out)
35{
36 if (!drm_fbs[0].fb)
37 fb_create_buffers(fd);
38
39 *out = drm_screen.size;
40
41 return 0;
42}
43
44int fb_get_resolution(int fd, FBResolution* info)
45{
46 if (!drm_fbs[0].fb)
47 fb_create_buffers(fd);
48
49 info->pitch = drm_screen.pitch;
50 info->width = drm_screen.width;
51 info->height = drm_screen.height;
52 return 0;
53}
54
55int fb_set_resolution(int fd, FBResolution* info)
56{
57 // we can't actually change the resolution, so just keep it at what it's at
58 if (fb_get_resolution(fd, info) != 0)
59 return -1;
60
61 // but take this opportunity to put wscons into dumb (non-text) mode to setup for mmap
62 int mode = WSDISPLAYIO_MODE_DUMBFB;
63 if (ioctl(fd, WSDISPLAYIO_SMODE, &mode) == -1) {
64 perror("WSDISPLAYIO_SMODE");
65 return -1;
66 }
67
68 return 0;
69}
70
71void *fb_get_addr(int fd, int index)
72{
73 if (!drm_fbs[0].fb)
74 fb_create_buffers(fd);
75
76 return drm_fbs[index].fb;
77}
78
79int fb_get_buffer(int fd, int* index)
80{
81 dbg() << "fb_get_buffer not supported";
82 (void)fd;
83 (void)index;
84 return -1;
85}
86
87int fb_set_buffer(int fd, int index)
88{
89 struct drm_mode_crtc_page_flip flip;
90
91 if (index >= MAX_FBS)
92 return -1;
93
94 if (!drm_fbs[index].fb)
95 fb_create_buffers(fd);
96
97 memset(&flip, 0, sizeof(flip));
98 flip.fb_id = drm_fbs[index].crtc.fb_id;
99 flip.crtc_id = drm_fbs[index].crtc.crtc_id;
100 flip.user_data = (u64)drm_fbs[index].fb;
101 // TODO: check for DRM_CAP_ASYNC_PAGE_FLIP and use DRM_MODE_PAGE_FLIP_ASYNC
102 flip.flags = 0;
103
104 if (ioctl(fd, DRM_IOCTL_MODE_PAGE_FLIP, &flip) != 0) {
105 if (errno != EBUSY) {
106 perror("DRM_IOCTL_MODE_PAGE_FLIP");
107 return -1;
108 }
109 }
110
111 return 0;
112}
113
114int fb_create_buffers(int fd)
115{
116 int ret = -1;
117
118 memset(&drm_screen, 0, sizeof(drm_screen));
119 memset(&drm_fbs, 0, sizeof(drm_fbs));
120
121 if (ioctl(fd, DRM_IOCTL_SET_MASTER, 0) != 0) {
122 perror("DRM_IOCTL_SET_MASTER");
123 return -1;
124 }
125
126 struct drm_mode_card_res res;
127 memset(&res, 0, sizeof(res));
128 if (ioctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res) != 0) {
129 perror("DRM_IOCTL_MODE_GETRESOURCES");
130 return -1;
131 }
132
133 for (auto i = 0; i < (int)res.count_connectors; i++) {
134 uint64_t res_fb_buf[10] = { 0 }, res_crtc_buf[10] = { 0 }, res_enc_buf[10] = { 0 };
135 res.fb_id_ptr = (u64)res_fb_buf;
136 res.crtc_id_ptr = (u64)res_crtc_buf;
137 res.connector_id_ptr = (u64)drm_screen.res_conn_buf;
138 res.encoder_id_ptr = (u64)res_enc_buf;
139 if (ioctl(fd, DRM_IOCTL_MODE_GETRESOURCES, &res) != 0) {
140 perror("DRM_IOCTL_MODE_GETRESOURCES");
141 return -1;
142 }
143
144 struct drm_mode_modeinfo conn_mode_buf[20];
145 uint64_t conn_prop_buf[20] = { 0 }, conn_propval_buf[20] = { 0 }, conn_enc_buf[20] = { 0 };
146 struct drm_mode_get_connector conn;
147 memset(&conn_mode_buf, 0, sizeof(conn_mode_buf));
148 memset(&conn, 0, sizeof(conn));
149 conn.connector_id = (u64)drm_screen.res_conn_buf[i];
150 if (ioctl(fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn) != 0) {
151 perror("DRM_IOCTL_MODE_GETCONNECTOR");
152 continue;
153 }
154
155 conn.modes_ptr = (u64)conn_mode_buf;
156 conn.props_ptr = (u64)conn_prop_buf;
157 conn.prop_values_ptr = (u64)conn_propval_buf;
158 conn.encoders_ptr = (u64)conn_enc_buf;
159 if (ioctl(fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn) != 0) {
160 perror("DRM_IOCTL_MODE_GETCONNECTOR");
161 continue;
162 }
163
164 if (conn.count_encoders < 1 || conn.count_modes < 1 || !conn.encoder_id || !conn.connection)
165 continue;
166
167 for (auto j = 0; j <= 1; j++) {
168 struct drm_mode_create_dumb create_dumb;
169 struct drm_mode_map_dumb map_dumb;
170 struct drm_mode_fb_cmd cmd_dumb;
171
172 memset(&create_dumb, 0, sizeof(create_dumb));
173
174 create_dumb.width = conn_mode_buf[0].hdisplay;
175 create_dumb.height = conn_mode_buf[0].vdisplay;
176 create_dumb.bpp = 32;
177 if (ioctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb) != 0) {
178 perror("DRM_IOCTL_MODE_CREATE_DUMB");
179 goto bail;
180 }
181
182 cmd_dumb.width = create_dumb.width;
183 cmd_dumb.height = create_dumb.height;
184 cmd_dumb.bpp = create_dumb.bpp;
185 cmd_dumb.pitch = create_dumb.pitch;
186 cmd_dumb.depth = create_dumb.bpp;
187 cmd_dumb.handle = create_dumb.handle;
188 if (ioctl(fd, DRM_IOCTL_MODE_ADDFB, &cmd_dumb) != 0) {
189 perror("DRM_IOCTL_MODE_ADDFB");
190 goto bail;
191 }
192
193 map_dumb.handle = create_dumb.handle;
194 if (ioctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb) != 0) {
195 perror("DRM_IOCTL_MODE_MAP_DUMB");
196 goto bail;
197 }
198
199 drm_screen.width = create_dumb.width;
200 drm_screen.height = create_dumb.height;
201 drm_screen.pitch = create_dumb.pitch;
202 drm_screen.size = create_dumb.size;
203
204 drm_fbs[j].fb = mmap(nullptr, drm_screen.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, map_dumb.offset);
205
206 struct drm_mode_get_encoder enc;
207 enc.encoder_id = conn.encoder_id;
208 if (ioctl(fd, DRM_IOCTL_MODE_GETENCODER, &enc) != 0) {
209 perror("DRM_IOCTL_MODE_GETENCODER");
210 munmap(drm_fbs[j].fb, drm_screen.size);
211 drm_fbs[j].fb = nullptr;
212 goto bail;
213 }
214
215 drm_fbs[j].crtc.crtc_id = enc.crtc_id;
216 if (ioctl(fd, DRM_IOCTL_MODE_GETCRTC, &drm_fbs[j].crtc) != 0) {
217 perror("DRM_IOCTL_MODE_GETCRTC");
218 munmap(drm_fbs[j].fb, drm_screen.size);
219 drm_fbs[j].fb = nullptr;
220 goto bail;
221 }
222
223 drm_fbs[j].crtc.fb_id = cmd_dumb.fb_id;
224 drm_fbs[j].crtc.set_connectors_ptr = (uint64_t)&drm_screen.res_conn_buf[i];
225 drm_fbs[j].crtc.count_connectors = 1;
226 drm_fbs[j].crtc.mode = conn_mode_buf[0];
227 drm_fbs[j].crtc.mode_valid = 1;
228 }
229
230 // we only need one set of buffers
231 ret = 0;
232 break;
233 }
234
235 if (ioctl(fd, DRM_IOCTL_MODE_SETCRTC, &drm_fbs[0].crtc) != 0)
236 perror("DRM_IOCTL_MODE_SETCRTC");
237
238bail:
239 if (ioctl(fd, DRM_IOCTL_DROP_MASTER, 0) != 0) {
240 perror("DRM_IOCTL_DROP_MASTER");
241 ASSERT_NOT_REACHED();
242 }
243
244 return ret;
245}
246#endif