blob: c6e459958fb44a97b16ffeb8297b7df1d2c31046 [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;
32 create_dumb.width = fb->drm->crtc->mode.hdisplay;
33 create_dumb.height = fb->drm->crtc->mode.vdisplay;
34
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;
55 ret = drmModeAddFB2(fb->drm->fd, fb->drm->crtc->mode.hdisplay, fb->drm->crtc->mode.vdisplay,
56 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
66 fb_buffer = fb_lock(fb);
67 if (fb_buffer) {
68 memset(fb_buffer, 0, fb->buffer_properties.size);
69 fb_unlock(fb);
70 }
71
Dominik Behr83010f82016-03-18 18:43:08 -070072 return 0;
73
74destroy_buffer:
75 destroy_dumb.handle = create_dumb.handle;
76
77 drmIoctl(fb->drm->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_dumb);
78
79 return ret;
80}
81
82void fb_buffer_destroy(fb_t* fb)
83{
84 struct drm_mode_destroy_dumb destroy_dumb;
85
86 if (fb->buffer_handle <= 0)
Dominik Behr6e0f6fd2016-12-02 17:54:08 -080087 goto unref_drm;
Dominik Behr83010f82016-03-18 18:43:08 -070088
Dominik Behr6e0f6fd2016-12-02 17:54:08 -080089 drm_rmfb(fb->drm, fb->fb_id);
Dominik Behr83010f82016-03-18 18:43:08 -070090 fb->fb_id = 0;
91 destroy_dumb.handle = fb->buffer_handle;
92 drmIoctl(fb->drm->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_dumb);
93 fb->buffer_handle = 0;
94 fb->lock.map = NULL;
zhuo-haoe9980902016-07-28 12:05:36 +080095 fb->lock.count = 0;
Dominik Behr6e0f6fd2016-12-02 17:54:08 -080096unref_drm:
97 if (fb->drm) {
98 drm_delref(fb->drm);
99 fb->drm = NULL;
100 }
Dominik Behr83010f82016-03-18 18:43:08 -0700101}
102
103static bool parse_edid_dtd(uint8_t* dtd, drmModeModeInfo* mode,
104 int32_t* hdisplay_size, int32_t* vdisplay_size) {
105 int32_t clock;
106 int32_t hactive, hbl, hso, hsw, hsize;
107 int32_t vactive, vbl, vso, vsw, vsize;
108
109 clock = ((int32_t)dtd[DTD_PCLK_HI] << 8) | dtd[DTD_PCLK_LO];
110 if (!clock)
111 return false;
112
113 hactive = ((int32_t)(dtd[DTD_HABL_HI] & 0xf0) << 4) + dtd[DTD_HA_LO];
114 vactive = ((int32_t)(dtd[DTD_VABL_HI] & 0xf0) << 4) + dtd[DTD_VA_LO];
115 hbl = ((int32_t)(dtd[DTD_HABL_HI] & 0x0f) << 8) + dtd[DTD_HBL_LO];
116 vbl = ((int32_t)(dtd[DTD_VABL_HI] & 0x0f) << 8) + dtd[DTD_VBL_LO];
117 hso = ((int32_t)(dtd[DTD_HVSX_HI] & 0xc0) << 2) + dtd[DTD_HSO_LO];
118 vso = ((int32_t)(dtd[DTD_HVSX_HI] & 0x0c) << 2) + (dtd[DTD_VSX_LO] >> 4);
119 hsw = ((int32_t)(dtd[DTD_HVSX_HI] & 0x30) << 4) + dtd[DTD_HSW_LO];
120 vsw = ((int32_t)(dtd[DTD_HVSX_HI] & 0x03) << 4) + (dtd[DTD_VSX_LO] & 0xf);
121 hsize = ((int32_t)(dtd[DTD_HVSIZE_HI] & 0xf0) << 4) + dtd[DTD_HSIZE_LO];
122 vsize = ((int32_t)(dtd[DTD_HVSIZE_HI] & 0x0f) << 8) + dtd[DTD_VSIZE_LO];
123
124 mode->clock = clock * 10;
125 mode->hdisplay = hactive;
126 mode->vdisplay = vactive;
127 mode->hsync_start = hactive + hso;
128 mode->vsync_start = vactive + vso;
129 mode->hsync_end = mode->hsync_start + hsw;
130 mode->vsync_end = mode->vsync_start + vsw;
131 mode->htotal = hactive + hbl;
132 mode->vtotal = vactive + vbl;
133 *hdisplay_size = hsize;
134 *vdisplay_size = vsize;
135 return true;
136}
137
138static bool parse_edid_dtd_display_size(drm_t* drm, int32_t* hsize_mm, int32_t* vsize_mm) {
139 drmModeModeInfo* mode = &drm->crtc->mode;
140
141 for (int i = 0; i < EDID_N_DTDS; i++) {
142 uint8_t* dtd = (uint8_t*)&drm->edid[EDID_DTD_BASE + i * DTD_SIZE];
143 drmModeModeInfo dtd_mode;
144 int32_t hdisplay_size, vdisplay_size;
145 if (!parse_edid_dtd(dtd, &dtd_mode, &hdisplay_size, &vdisplay_size) ||
146 mode->clock != dtd_mode.clock ||
147 mode->hdisplay != dtd_mode.hdisplay ||
148 mode->vdisplay != dtd_mode.vdisplay ||
149 mode->hsync_start != dtd_mode.hsync_start ||
150 mode->vsync_start != dtd_mode.vsync_start ||
151 mode->hsync_end != dtd_mode.hsync_end ||
152 mode->vsync_end != dtd_mode.vsync_end ||
153 mode->htotal != dtd_mode.htotal ||
154 mode->vtotal != dtd_mode.vtotal)
155 continue;
156 *hsize_mm = hdisplay_size;
157 *vsize_mm = vdisplay_size;
158 return true;
159 }
160 return false;
161}
162
163int fb_buffer_init(fb_t* fb)
164{
165 int32_t width, height, pitch;
166 int32_t hsize_mm, vsize_mm;
167 int r;
168
zhuo-hao8f99f432016-08-02 17:58:26 +0800169 /* reuse the buffer_properties if it was set before */
170 if (!fb->buffer_properties.width || !fb->buffer_properties.height ||
171 !fb->buffer_properties.pitch || !fb->buffer_properties.scaling) {
172 /* some reasonable defaults */
173 fb->buffer_properties.width = 640;
174 fb->buffer_properties.height = 480;
175 fb->buffer_properties.pitch = 640 * 4;
176 fb->buffer_properties.scaling = 1;
177 }
Dominik Behr83010f82016-03-18 18:43:08 -0700178
179 fb->drm = drm_addref();
180
181 if (!fb->drm) {
182 LOG(WARNING, "No monitor available, running headless!");
183 return -ENODEV;
184 }
185
186 width = fb->drm->crtc->mode.hdisplay;
187 height = fb->drm->crtc->mode.vdisplay;
188
189 r = fb_buffer_create(fb, &pitch);
190 if (r < 0) {
191 LOG(ERROR, "fb_buffer_create failed");
192 return r;
193 }
194
195 fb->buffer_properties.width = width;
196 fb->buffer_properties.height = height;
197 fb->buffer_properties.pitch = pitch;
198
199 hsize_mm = fb->drm->main_monitor_connector->mmWidth;
200 vsize_mm = fb->drm->main_monitor_connector->mmHeight;
201 if (drm_read_edid(fb->drm))
202 parse_edid_dtd_display_size(fb->drm, &hsize_mm, &vsize_mm);
203
204 if (hsize_mm) {
205 int dots_per_cm = width * 10 / hsize_mm;
206 if (dots_per_cm > 133)
207 fb->buffer_properties.scaling = 4;
Brian Norrisa2af9ca2018-02-23 15:03:23 -0800208 else if (dots_per_cm > 105)
Dominik Behr83010f82016-03-18 18:43:08 -0700209 fb->buffer_properties.scaling = 3;
210 else if (dots_per_cm > 67)
211 fb->buffer_properties.scaling = 2;
212 }
213
214 return 0;
215}
216
217fb_t* fb_init(void)
218{
219 fb_t* fb;
220
221 fb = (fb_t*)calloc(1, sizeof(fb_t));
222 if (!fb)
223 return NULL;
224
225 fb_buffer_init(fb);
226
227 return fb;
228}
229
230void fb_close(fb_t* fb)
231{
232 if (!fb)
233 return;
234
235 fb_buffer_destroy(fb);
236
237 free(fb);
238}
239
240int32_t fb_setmode(fb_t* fb)
241{
242 /* headless mode */
243 if (!drm_valid(fb->drm))
244 return 0;
245
246 return drm_setmode(fb->drm, fb->fb_id);
247}
248
249uint32_t* fb_lock(fb_t* fb)
250{
251 if (fb->lock.count == 0 && fb->buffer_handle > 0) {
252 fb->lock.map =
253 mmap(0, fb->buffer_properties.size, PROT_READ | PROT_WRITE,
254 MAP_SHARED, fb->drm->fd, fb->lock.map_offset);
255 if (fb->lock.map == MAP_FAILED) {
256 LOG(ERROR, "mmap failed");
257 return NULL;
258 }
259 }
zhuo-haoe9980902016-07-28 12:05:36 +0800260
261 if (fb->lock.map)
262 fb->lock.count++;
Dominik Behr83010f82016-03-18 18:43:08 -0700263
264 return fb->lock.map;
265}
266
267void fb_unlock(fb_t* fb)
268{
269 if (fb->lock.count > 0)
270 fb->lock.count--;
271 else
272 LOG(ERROR, "fb locking unbalanced");
273
274 if (fb->lock.count == 0 && fb->buffer_handle > 0) {
zhuo-hao150ce792016-05-26 14:42:22 +0800275 int32_t ret;
Dominik Behr83010f82016-03-18 18:43:08 -0700276 struct drm_clip_rect clip_rect = {
277 0, 0, fb->buffer_properties.width, fb->buffer_properties.height
278 };
279 munmap(fb->lock.map, fb->buffer_properties.size);
zhuo-hao150ce792016-05-26 14:42:22 +0800280 ret = drmModeDirtyFB(fb->drm->fd, fb->fb_id, &clip_rect, 1);
Daniel Kurtze6d42802016-07-14 13:01:30 +0800281 if (ret && errno != ENOSYS)
zhuo-hao150ce792016-05-26 14:42:22 +0800282 LOG(ERROR, "drmModeDirtyFB failed: %m");
Dominik Behr83010f82016-03-18 18:43:08 -0700283 }
284}
285
286int32_t fb_getwidth(fb_t* fb)
287{
288 return fb->buffer_properties.width;
289}
290
291int32_t fb_getheight(fb_t* fb)
292{
293 return fb->buffer_properties.height;
294}
295
296int32_t fb_getpitch(fb_t* fb)
297{
298 return fb->buffer_properties.pitch;
299}
300
301int32_t fb_getscaling(fb_t* fb)
302{
303 return fb->buffer_properties.scaling;
304}