blob: 5d9cb7b85efb42e954127879f0d25c7884d1e825 [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;
27 int ret;
28
29 memset(&create_dumb, 0, sizeof (create_dumb));
30 create_dumb.bpp = 32;
31 create_dumb.width = fb->drm->crtc->mode.hdisplay;
32 create_dumb.height = fb->drm->crtc->mode.vdisplay;
33
34 ret = drmIoctl(fb->drm->fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb);
35 if (ret) {
36 LOG(ERROR, "CREATE_DUMB failed");
37 return ret;
38 }
39
40 fb->buffer_properties.size = create_dumb.size;
41 fb->buffer_handle = create_dumb.handle;
42
43 struct drm_mode_map_dumb map_dumb;
44 map_dumb.handle = create_dumb.handle;
45 ret = drmIoctl(fb->drm->fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb);
46 if (ret) {
47 LOG(ERROR, "MAP_DUMB failed");
48 goto destroy_buffer;
49 }
50
51 fb->lock.map_offset = map_dumb.offset;
52
53 uint32_t offset = 0;
54 ret = drmModeAddFB2(fb->drm->fd, fb->drm->crtc->mode.hdisplay, fb->drm->crtc->mode.vdisplay,
55 DRM_FORMAT_XRGB8888, &create_dumb.handle,
56 &create_dumb.pitch, &offset, &fb->fb_id, 0);
57 if (ret) {
58 LOG(ERROR, "drmModeAddFB2 failed");
59 goto destroy_buffer;
60 }
61
62 *pitch = create_dumb.pitch;
63
64 return 0;
65
66destroy_buffer:
67 destroy_dumb.handle = create_dumb.handle;
68
69 drmIoctl(fb->drm->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_dumb);
70
71 return ret;
72}
73
74void fb_buffer_destroy(fb_t* fb)
75{
76 struct drm_mode_destroy_dumb destroy_dumb;
77
78 if (fb->buffer_handle <= 0)
79 return;
80
81 drmModeRmFB(fb->drm->fd, fb->fb_id);
82 fb->fb_id = 0;
83 destroy_dumb.handle = fb->buffer_handle;
84 drmIoctl(fb->drm->fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_dumb);
85 fb->buffer_handle = 0;
86 fb->lock.map = NULL;
zhuo-haoe9980902016-07-28 12:05:36 +080087 fb->lock.count = 0;
Dominik Behr83010f82016-03-18 18:43:08 -070088 drm_delref(fb->drm);
89 fb->drm = NULL;
90}
91
92static bool parse_edid_dtd(uint8_t* dtd, drmModeModeInfo* mode,
93 int32_t* hdisplay_size, int32_t* vdisplay_size) {
94 int32_t clock;
95 int32_t hactive, hbl, hso, hsw, hsize;
96 int32_t vactive, vbl, vso, vsw, vsize;
97
98 clock = ((int32_t)dtd[DTD_PCLK_HI] << 8) | dtd[DTD_PCLK_LO];
99 if (!clock)
100 return false;
101
102 hactive = ((int32_t)(dtd[DTD_HABL_HI] & 0xf0) << 4) + dtd[DTD_HA_LO];
103 vactive = ((int32_t)(dtd[DTD_VABL_HI] & 0xf0) << 4) + dtd[DTD_VA_LO];
104 hbl = ((int32_t)(dtd[DTD_HABL_HI] & 0x0f) << 8) + dtd[DTD_HBL_LO];
105 vbl = ((int32_t)(dtd[DTD_VABL_HI] & 0x0f) << 8) + dtd[DTD_VBL_LO];
106 hso = ((int32_t)(dtd[DTD_HVSX_HI] & 0xc0) << 2) + dtd[DTD_HSO_LO];
107 vso = ((int32_t)(dtd[DTD_HVSX_HI] & 0x0c) << 2) + (dtd[DTD_VSX_LO] >> 4);
108 hsw = ((int32_t)(dtd[DTD_HVSX_HI] & 0x30) << 4) + dtd[DTD_HSW_LO];
109 vsw = ((int32_t)(dtd[DTD_HVSX_HI] & 0x03) << 4) + (dtd[DTD_VSX_LO] & 0xf);
110 hsize = ((int32_t)(dtd[DTD_HVSIZE_HI] & 0xf0) << 4) + dtd[DTD_HSIZE_LO];
111 vsize = ((int32_t)(dtd[DTD_HVSIZE_HI] & 0x0f) << 8) + dtd[DTD_VSIZE_LO];
112
113 mode->clock = clock * 10;
114 mode->hdisplay = hactive;
115 mode->vdisplay = vactive;
116 mode->hsync_start = hactive + hso;
117 mode->vsync_start = vactive + vso;
118 mode->hsync_end = mode->hsync_start + hsw;
119 mode->vsync_end = mode->vsync_start + vsw;
120 mode->htotal = hactive + hbl;
121 mode->vtotal = vactive + vbl;
122 *hdisplay_size = hsize;
123 *vdisplay_size = vsize;
124 return true;
125}
126
127static bool parse_edid_dtd_display_size(drm_t* drm, int32_t* hsize_mm, int32_t* vsize_mm) {
128 drmModeModeInfo* mode = &drm->crtc->mode;
129
130 for (int i = 0; i < EDID_N_DTDS; i++) {
131 uint8_t* dtd = (uint8_t*)&drm->edid[EDID_DTD_BASE + i * DTD_SIZE];
132 drmModeModeInfo dtd_mode;
133 int32_t hdisplay_size, vdisplay_size;
134 if (!parse_edid_dtd(dtd, &dtd_mode, &hdisplay_size, &vdisplay_size) ||
135 mode->clock != dtd_mode.clock ||
136 mode->hdisplay != dtd_mode.hdisplay ||
137 mode->vdisplay != dtd_mode.vdisplay ||
138 mode->hsync_start != dtd_mode.hsync_start ||
139 mode->vsync_start != dtd_mode.vsync_start ||
140 mode->hsync_end != dtd_mode.hsync_end ||
141 mode->vsync_end != dtd_mode.vsync_end ||
142 mode->htotal != dtd_mode.htotal ||
143 mode->vtotal != dtd_mode.vtotal)
144 continue;
145 *hsize_mm = hdisplay_size;
146 *vsize_mm = vdisplay_size;
147 return true;
148 }
149 return false;
150}
151
152int fb_buffer_init(fb_t* fb)
153{
154 int32_t width, height, pitch;
155 int32_t hsize_mm, vsize_mm;
156 int r;
157
zhuo-hao8f99f432016-08-02 17:58:26 +0800158 /* reuse the buffer_properties if it was set before */
159 if (!fb->buffer_properties.width || !fb->buffer_properties.height ||
160 !fb->buffer_properties.pitch || !fb->buffer_properties.scaling) {
161 /* some reasonable defaults */
162 fb->buffer_properties.width = 640;
163 fb->buffer_properties.height = 480;
164 fb->buffer_properties.pitch = 640 * 4;
165 fb->buffer_properties.scaling = 1;
166 }
Dominik Behr83010f82016-03-18 18:43:08 -0700167
168 fb->drm = drm_addref();
169
170 if (!fb->drm) {
171 LOG(WARNING, "No monitor available, running headless!");
172 return -ENODEV;
173 }
174
175 width = fb->drm->crtc->mode.hdisplay;
176 height = fb->drm->crtc->mode.vdisplay;
177
178 r = fb_buffer_create(fb, &pitch);
179 if (r < 0) {
180 LOG(ERROR, "fb_buffer_create failed");
181 return r;
182 }
183
184 fb->buffer_properties.width = width;
185 fb->buffer_properties.height = height;
186 fb->buffer_properties.pitch = pitch;
187
188 hsize_mm = fb->drm->main_monitor_connector->mmWidth;
189 vsize_mm = fb->drm->main_monitor_connector->mmHeight;
190 if (drm_read_edid(fb->drm))
191 parse_edid_dtd_display_size(fb->drm, &hsize_mm, &vsize_mm);
192
193 if (hsize_mm) {
194 int dots_per_cm = width * 10 / hsize_mm;
195 if (dots_per_cm > 133)
196 fb->buffer_properties.scaling = 4;
197 else if (dots_per_cm > 100)
198 fb->buffer_properties.scaling = 3;
199 else if (dots_per_cm > 67)
200 fb->buffer_properties.scaling = 2;
201 }
202
203 return 0;
204}
205
206fb_t* fb_init(void)
207{
208 fb_t* fb;
209
210 fb = (fb_t*)calloc(1, sizeof(fb_t));
211 if (!fb)
212 return NULL;
213
214 fb_buffer_init(fb);
215
216 return fb;
217}
218
219void fb_close(fb_t* fb)
220{
221 if (!fb)
222 return;
223
224 fb_buffer_destroy(fb);
225
226 free(fb);
227}
228
229int32_t fb_setmode(fb_t* fb)
230{
231 /* headless mode */
232 if (!drm_valid(fb->drm))
233 return 0;
234
235 return drm_setmode(fb->drm, fb->fb_id);
236}
237
238uint32_t* fb_lock(fb_t* fb)
239{
240 if (fb->lock.count == 0 && fb->buffer_handle > 0) {
241 fb->lock.map =
242 mmap(0, fb->buffer_properties.size, PROT_READ | PROT_WRITE,
243 MAP_SHARED, fb->drm->fd, fb->lock.map_offset);
244 if (fb->lock.map == MAP_FAILED) {
245 LOG(ERROR, "mmap failed");
246 return NULL;
247 }
248 }
zhuo-haoe9980902016-07-28 12:05:36 +0800249
250 if (fb->lock.map)
251 fb->lock.count++;
Dominik Behr83010f82016-03-18 18:43:08 -0700252
253 return fb->lock.map;
254}
255
256void fb_unlock(fb_t* fb)
257{
258 if (fb->lock.count > 0)
259 fb->lock.count--;
260 else
261 LOG(ERROR, "fb locking unbalanced");
262
263 if (fb->lock.count == 0 && fb->buffer_handle > 0) {
zhuo-hao150ce792016-05-26 14:42:22 +0800264 int32_t ret;
Dominik Behr83010f82016-03-18 18:43:08 -0700265 struct drm_clip_rect clip_rect = {
266 0, 0, fb->buffer_properties.width, fb->buffer_properties.height
267 };
268 munmap(fb->lock.map, fb->buffer_properties.size);
zhuo-hao150ce792016-05-26 14:42:22 +0800269 ret = drmModeDirtyFB(fb->drm->fd, fb->fb_id, &clip_rect, 1);
Daniel Kurtze6d42802016-07-14 13:01:30 +0800270 if (ret && errno != ENOSYS)
zhuo-hao150ce792016-05-26 14:42:22 +0800271 LOG(ERROR, "drmModeDirtyFB failed: %m");
Dominik Behr83010f82016-03-18 18:43:08 -0700272 }
273}
274
275int32_t fb_getwidth(fb_t* fb)
276{
277 return fb->buffer_properties.width;
278}
279
280int32_t fb_getheight(fb_t* fb)
281{
282 return fb->buffer_properties.height;
283}
284
285int32_t fb_getpitch(fb_t* fb)
286{
287 return fb->buffer_properties.pitch;
288}
289
290int32_t fb_getscaling(fb_t* fb)
291{
292 return fb->buffer_properties.scaling;
293}