blob: acd0aa7bdfad75b3c424210e6f737a82005482b5 [file] [log] [blame]
Dominik Behr83010f82016-03-18 18:43:08 -07001/*
2 * Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
3 * Use of this source code is governed by a BSD-style license that can be
4 * found in the LICENSE file.
5 */
6
7#include <drm_fourcc.h>
8#include <errno.h>
9#include <fcntl.h>
10#include <stdbool.h>
11#include <stddef.h>
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15#include <sys/mman.h>
16#include <time.h>
17#include <unistd.h>
18
19#include "util.h"
20#include "fb.h"
21
22static int fb_buffer_create(fb_t* fb,
23 int* pitch)
24{
25 struct drm_mode_create_dumb create_dumb;
26 struct drm_mode_destroy_dumb destroy_dumb;
Drew Davenportf6d4ab02018-04-30 16:38:11 -060027 uint32_t* fb_buffer;
Dominik Behr83010f82016-03-18 18:43:08 -070028 int ret;
29
30 memset(&create_dumb, 0, sizeof (create_dumb));
31 create_dumb.bpp = 32;
Dominik Behr57b5c722018-08-08 18:52:31 -070032 create_dumb.width = fb->drm->console_mode_info.hdisplay;
33 create_dumb.height = fb->drm->console_mode_info.vdisplay;
Dominik Behr83010f82016-03-18 18:43:08 -070034
35 ret = drmIoctl(fb->drm->fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb);
36 if (ret) {
37 LOG(ERROR, "CREATE_DUMB failed");
38 return ret;
39 }
40
41 fb->buffer_properties.size = create_dumb.size;
42 fb->buffer_handle = create_dumb.handle;
43
44 struct drm_mode_map_dumb map_dumb;
45 map_dumb.handle = create_dumb.handle;
46 ret = drmIoctl(fb->drm->fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb);
47 if (ret) {
48 LOG(ERROR, "MAP_DUMB failed");
49 goto destroy_buffer;
50 }
51
52 fb->lock.map_offset = map_dumb.offset;
53
54 uint32_t offset = 0;
Dominik Behr57b5c722018-08-08 18:52:31 -070055 ret = drmModeAddFB2(fb->drm->fd, fb->drm->console_mode_info.hdisplay, fb->drm->console_mode_info.vdisplay,
Dominik Behr83010f82016-03-18 18:43:08 -070056 DRM_FORMAT_XRGB8888, &create_dumb.handle,
57 &create_dumb.pitch, &offset, &fb->fb_id, 0);
58 if (ret) {
59 LOG(ERROR, "drmModeAddFB2 failed");
60 goto destroy_buffer;
61 }
62
63 *pitch = create_dumb.pitch;
64
Drew Davenportf6d4ab02018-04-30 16:38:11 -060065 fb_buffer = fb_lock(fb);
66 if (fb_buffer) {
67 memset(fb_buffer, 0, fb->buffer_properties.size);
68 fb_unlock(fb);
69 }
70
Dominik Behr83010f82016-03-18 18:43:08 -070071 return 0;
72
73destroy_buffer:
74 destroy_dumb.handle = create_dumb.handle;
75
76 drmIoctl(fb->drm->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_dumb);
77
78 return ret;
79}
80
81void fb_buffer_destroy(fb_t* fb)
82{
83 struct drm_mode_destroy_dumb destroy_dumb;
84
85 if (fb->buffer_handle <= 0)
Dominik Behr6e0f6fd2016-12-02 17:54:08 -080086 goto unref_drm;
Dominik Behr83010f82016-03-18 18:43:08 -070087
Dominik Behr6e0f6fd2016-12-02 17:54:08 -080088 drm_rmfb(fb->drm, fb->fb_id);
Dominik Behr83010f82016-03-18 18:43:08 -070089 fb->fb_id = 0;
90 destroy_dumb.handle = fb->buffer_handle;
91 drmIoctl(fb->drm->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_dumb);
92 fb->buffer_handle = 0;
93 fb->lock.map = NULL;
zhuo-haoe9980902016-07-28 12:05:36 +080094 fb->lock.count = 0;
Dominik Behr6e0f6fd2016-12-02 17:54:08 -080095unref_drm:
96 if (fb->drm) {
97 drm_delref(fb->drm);
98 fb->drm = NULL;
99 }
Dominik Behr83010f82016-03-18 18:43:08 -0700100}
101
102static bool parse_edid_dtd(uint8_t* dtd, drmModeModeInfo* mode,
103 int32_t* hdisplay_size, int32_t* vdisplay_size) {
104 int32_t clock;
105 int32_t hactive, hbl, hso, hsw, hsize;
106 int32_t vactive, vbl, vso, vsw, vsize;
107
108 clock = ((int32_t)dtd[DTD_PCLK_HI] << 8) | dtd[DTD_PCLK_LO];
109 if (!clock)
110 return false;
111
112 hactive = ((int32_t)(dtd[DTD_HABL_HI] & 0xf0) << 4) + dtd[DTD_HA_LO];
113 vactive = ((int32_t)(dtd[DTD_VABL_HI] & 0xf0) << 4) + dtd[DTD_VA_LO];
114 hbl = ((int32_t)(dtd[DTD_HABL_HI] & 0x0f) << 8) + dtd[DTD_HBL_LO];
115 vbl = ((int32_t)(dtd[DTD_VABL_HI] & 0x0f) << 8) + dtd[DTD_VBL_LO];
116 hso = ((int32_t)(dtd[DTD_HVSX_HI] & 0xc0) << 2) + dtd[DTD_HSO_LO];
117 vso = ((int32_t)(dtd[DTD_HVSX_HI] & 0x0c) << 2) + (dtd[DTD_VSX_LO] >> 4);
118 hsw = ((int32_t)(dtd[DTD_HVSX_HI] & 0x30) << 4) + dtd[DTD_HSW_LO];
119 vsw = ((int32_t)(dtd[DTD_HVSX_HI] & 0x03) << 4) + (dtd[DTD_VSX_LO] & 0xf);
120 hsize = ((int32_t)(dtd[DTD_HVSIZE_HI] & 0xf0) << 4) + dtd[DTD_HSIZE_LO];
121 vsize = ((int32_t)(dtd[DTD_HVSIZE_HI] & 0x0f) << 8) + dtd[DTD_VSIZE_LO];
122
123 mode->clock = clock * 10;
124 mode->hdisplay = hactive;
125 mode->vdisplay = vactive;
126 mode->hsync_start = hactive + hso;
127 mode->vsync_start = vactive + vso;
128 mode->hsync_end = mode->hsync_start + hsw;
129 mode->vsync_end = mode->vsync_start + vsw;
130 mode->htotal = hactive + hbl;
131 mode->vtotal = vactive + vbl;
132 *hdisplay_size = hsize;
133 *vdisplay_size = vsize;
134 return true;
135}
136
137static bool parse_edid_dtd_display_size(drm_t* drm, int32_t* hsize_mm, int32_t* vsize_mm) {
Dominik Behr57b5c722018-08-08 18:52:31 -0700138 drmModeModeInfo* mode = &drm->console_mode_info;
Dominik Behr83010f82016-03-18 18:43:08 -0700139
140 for (int i = 0; i < EDID_N_DTDS; i++) {
141 uint8_t* dtd = (uint8_t*)&drm->edid[EDID_DTD_BASE + i * DTD_SIZE];
142 drmModeModeInfo dtd_mode;
143 int32_t hdisplay_size, vdisplay_size;
144 if (!parse_edid_dtd(dtd, &dtd_mode, &hdisplay_size, &vdisplay_size) ||
145 mode->clock != dtd_mode.clock ||
146 mode->hdisplay != dtd_mode.hdisplay ||
147 mode->vdisplay != dtd_mode.vdisplay ||
148 mode->hsync_start != dtd_mode.hsync_start ||
149 mode->vsync_start != dtd_mode.vsync_start ||
150 mode->hsync_end != dtd_mode.hsync_end ||
151 mode->vsync_end != dtd_mode.vsync_end ||
152 mode->htotal != dtd_mode.htotal ||
153 mode->vtotal != dtd_mode.vtotal)
154 continue;
155 *hsize_mm = hdisplay_size;
156 *vsize_mm = vdisplay_size;
157 return true;
158 }
159 return false;
160}
161
162int fb_buffer_init(fb_t* fb)
163{
164 int32_t width, height, pitch;
165 int32_t hsize_mm, vsize_mm;
166 int r;
167
zhuo-hao8f99f432016-08-02 17:58:26 +0800168 /* reuse the buffer_properties if it was set before */
169 if (!fb->buffer_properties.width || !fb->buffer_properties.height ||
170 !fb->buffer_properties.pitch || !fb->buffer_properties.scaling) {
171 /* some reasonable defaults */
172 fb->buffer_properties.width = 640;
173 fb->buffer_properties.height = 480;
174 fb->buffer_properties.pitch = 640 * 4;
175 fb->buffer_properties.scaling = 1;
176 }
Dominik Behr83010f82016-03-18 18:43:08 -0700177
178 fb->drm = drm_addref();
179
180 if (!fb->drm) {
181 LOG(WARNING, "No monitor available, running headless!");
182 return -ENODEV;
183 }
184
Dominik Behr57b5c722018-08-08 18:52:31 -0700185 width = fb->drm->console_mode_info.hdisplay;
186 height = fb->drm->console_mode_info.vdisplay;
Dominik Behr83010f82016-03-18 18:43:08 -0700187
188 r = fb_buffer_create(fb, &pitch);
189 if (r < 0) {
190 LOG(ERROR, "fb_buffer_create failed");
191 return r;
192 }
193
194 fb->buffer_properties.width = width;
195 fb->buffer_properties.height = height;
196 fb->buffer_properties.pitch = pitch;
197
Dominik Behrbb728f32019-09-03 17:52:13 -0700198/*
199 for reference, since it is not available in headers right now
200 DRM_MODE_PANEL_ORIENTATION_UNKNOWN = -1,
201 DRM_MODE_PANEL_ORIENTATION_NORMAL = 0,
202 DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP,
203 DRM_MODE_PANEL_ORIENTATION_LEFT_UP,
204 DRM_MODE_PANEL_ORIENTATION_RIGHT_UP,
205*/
206 switch (fb->drm->panel_orientation) {
207 case 1:
208 fb->buffer_properties.rotation = DRM_MODE_ROTATE_180;
209 break;
210 case 2:
211 fb->buffer_properties.rotation = DRM_MODE_ROTATE_270;
212 break;
213 case 3:
214 fb->buffer_properties.rotation = DRM_MODE_ROTATE_90;
215 break;
216 default:
217 fb->buffer_properties.rotation = DRM_MODE_ROTATE_0;
218 }
219
Dominik Behr57b5c722018-08-08 18:52:31 -0700220 hsize_mm = fb->drm->console_mmWidth;
221 vsize_mm = fb->drm->console_mmHeight;
Dominik Behr83010f82016-03-18 18:43:08 -0700222 if (drm_read_edid(fb->drm))
223 parse_edid_dtd_display_size(fb->drm, &hsize_mm, &vsize_mm);
224
225 if (hsize_mm) {
226 int dots_per_cm = width * 10 / hsize_mm;
227 if (dots_per_cm > 133)
228 fb->buffer_properties.scaling = 4;
Brian Norrisa2af9ca2018-02-23 15:03:23 -0800229 else if (dots_per_cm > 105)
Dominik Behr83010f82016-03-18 18:43:08 -0700230 fb->buffer_properties.scaling = 3;
231 else if (dots_per_cm > 67)
232 fb->buffer_properties.scaling = 2;
233 }
234
235 return 0;
236}
237
238fb_t* fb_init(void)
239{
240 fb_t* fb;
241
242 fb = (fb_t*)calloc(1, sizeof(fb_t));
243 if (!fb)
244 return NULL;
245
246 fb_buffer_init(fb);
247
248 return fb;
249}
250
251void fb_close(fb_t* fb)
252{
253 if (!fb)
254 return;
255
256 fb_buffer_destroy(fb);
257
258 free(fb);
259}
260
261int32_t fb_setmode(fb_t* fb)
262{
263 /* headless mode */
264 if (!drm_valid(fb->drm))
265 return 0;
266
267 return drm_setmode(fb->drm, fb->fb_id);
268}
269
270uint32_t* fb_lock(fb_t* fb)
271{
272 if (fb->lock.count == 0 && fb->buffer_handle > 0) {
273 fb->lock.map =
274 mmap(0, fb->buffer_properties.size, PROT_READ | PROT_WRITE,
275 MAP_SHARED, fb->drm->fd, fb->lock.map_offset);
276 if (fb->lock.map == MAP_FAILED) {
277 LOG(ERROR, "mmap failed");
278 return NULL;
279 }
280 }
zhuo-haoe9980902016-07-28 12:05:36 +0800281
282 if (fb->lock.map)
283 fb->lock.count++;
Dominik Behr83010f82016-03-18 18:43:08 -0700284
285 return fb->lock.map;
286}
287
288void fb_unlock(fb_t* fb)
289{
290 if (fb->lock.count > 0)
291 fb->lock.count--;
292 else
293 LOG(ERROR, "fb locking unbalanced");
294
295 if (fb->lock.count == 0 && fb->buffer_handle > 0) {
zhuo-hao150ce792016-05-26 14:42:22 +0800296 int32_t ret;
Dominik Behr83010f82016-03-18 18:43:08 -0700297 struct drm_clip_rect clip_rect = {
298 0, 0, fb->buffer_properties.width, fb->buffer_properties.height
299 };
300 munmap(fb->lock.map, fb->buffer_properties.size);
zhuo-hao150ce792016-05-26 14:42:22 +0800301 ret = drmModeDirtyFB(fb->drm->fd, fb->fb_id, &clip_rect, 1);
Nicolas Boichatdb3923a2021-03-23 09:08:56 +0800302 if (ret) {
303 int loglevel = ERROR;
304 /* Do not print "normal" errors by default. */
305 if (errno == ENOSYS || errno == EACCES)
306 loglevel = DEBUG;
307 LOG(loglevel, "drmModeDirtyFB failed: %d %m", errno);
308 }
Dominik Behr83010f82016-03-18 18:43:08 -0700309 }
310}
311
312int32_t fb_getwidth(fb_t* fb)
313{
Dominik Behrbb728f32019-09-03 17:52:13 -0700314 switch (fb->buffer_properties.rotation) {
315 case DRM_MODE_ROTATE_90:
316 case DRM_MODE_ROTATE_270:
317 return fb->buffer_properties.height;
318 break;
319 case DRM_MODE_ROTATE_0:
320 case DRM_MODE_ROTATE_180:
321 default:
322 return fb->buffer_properties.width;
323 }
Dominik Behr83010f82016-03-18 18:43:08 -0700324}
325
326int32_t fb_getheight(fb_t* fb)
327{
Dominik Behrbb728f32019-09-03 17:52:13 -0700328 switch (fb->buffer_properties.rotation) {
329 case DRM_MODE_ROTATE_90:
330 case DRM_MODE_ROTATE_270:
331 return fb->buffer_properties.width;
332 break;
333 case DRM_MODE_ROTATE_0:
334 case DRM_MODE_ROTATE_180:
335 default:
336 return fb->buffer_properties.height;
337 }
Dominik Behr83010f82016-03-18 18:43:08 -0700338}
339
340int32_t fb_getscaling(fb_t* fb)
341{
342 return fb->buffer_properties.scaling;
343}
Dominik Behrbb728f32019-09-03 17:52:13 -0700344
345bool
346fb_stepper_init(fb_stepper_t *s, fb_t *fb, int32_t x, int32_t y, uint32_t width, uint32_t height)
347{
348 s->fb = fb;
349 s->start_x = x;
350 s->start_y = y;
351 s->w = width;
352 s->h = height;
353 s->x = 0;
354 s->y = 0;
355 s->pitch_div_4 = s->fb->buffer_properties.pitch >> 2;
356
357 /* quick check if whole rect is outside fb */
358 if (x + width <= 0 || y + height <= 0)
359 return false;
360
361 switch (s->fb->buffer_properties.rotation) {
362 case DRM_MODE_ROTATE_90:
363 case DRM_MODE_ROTATE_270:
364 s->max_x = s->fb->buffer_properties.height;
365 s->max_y = s->fb->buffer_properties.width;
366 break;
367 case DRM_MODE_ROTATE_180:
368 case DRM_MODE_ROTATE_0:
369 default:
370 s->max_x = s->fb->buffer_properties.width;
371 s->max_y = s->fb->buffer_properties.height;
372 }
373
374 if (x >= s->max_x
375 || y >= s->max_y)
376 return false;
377
378 switch (s->fb->buffer_properties.rotation) {
379 case DRM_MODE_ROTATE_90:
380 s->m[0][0] = 0;
381 s->m[0][1] = -1;
382 s->m[0][2] = s->fb->buffer_properties.width - 1;
383
384 s->m[1][0] = 1;
385 s->m[1][1] = 0;
386 s->m[1][2] = 0;
387 break;
388 case DRM_MODE_ROTATE_270:
389 s->m[0][0] = 0;
390 s->m[0][1] = 1;
391 s->m[0][2] = 0;
392
393 s->m[1][0] = -1;
394 s->m[1][1] = 0;
395 s->m[1][2] = s->fb->buffer_properties.height - 1;
396 break;
397 case DRM_MODE_ROTATE_180:
398 s->m[0][0] = -1;
399 s->m[0][1] = 0;
400 s->m[0][2] = s->fb->buffer_properties.width - 1;
401
402 s->m[1][0] = 0;
403 s->m[1][1] = -1;
404 s->m[1][2] = s->fb->buffer_properties.height - 1;
405 break;
406 case DRM_MODE_ROTATE_0:
407 default:
408 s->m[0][0] = 1;
409 s->m[0][1] = 0;
410 s->m[0][2] = 0;
411
412 s->m[1][0] = 0;
413 s->m[1][1] = 1;
414 s->m[1][2] = 0;
415 }
416
417 return true;
418}