1 /*
2 * drm_display.cpp - drm display
3 *
4 * Copyright (c) 2015 Intel Corporation
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 * Author: John Ye <john.ye@intel.com>
19 */
20
21
22 #include "drm_display.h"
23 #include "drm_v4l2_buffer.h"
24 #include "drm_bo_buffer.h"
25 #include <drm_fourcc.h>
26 #include <sys/ioctl.h>
27 #include <fcntl.h>
28
29
30 #define DEFAULT_DRM_DEVICE "i915"
31 #define DEFAULT_DRM_BUSID "PCI:00:02:00"
32 #define DEFAULT_DRM_BATCH_SIZE 0x80000
33
34 namespace XCam {
35
36 SmartPtr<DrmDisplay> DrmDisplay::_instance(NULL);
37 Mutex DrmDisplay::_mutex;
38
39 static std::atomic<uint32_t> global_signal_index(0);
40
41 bool DrmDisplay::_preview_flag = false;
42
43 bool
set_preview(bool flag)44 DrmDisplay::set_preview (bool flag) {
45 if (_instance.ptr () && flag != _preview_flag)
46 return false;
47 _preview_flag = flag;
48 return true;
49 };
50
51 SmartPtr<DrmDisplay>
instance()52 DrmDisplay::instance ()
53 {
54 SmartLock lock(_mutex);
55 if (_instance.ptr())
56 return _instance;
57 _instance = new DrmDisplay ();
58 return _instance;
59 }
60
DrmDisplay(const char * module)61 DrmDisplay::DrmDisplay (const char *module)
62 : _module(NULL)
63 , _fd (-1)
64 , _buf_manager (NULL)
65 , _display_mode (DRM_DISPLAY_MODE_NONE)
66 , _crtc_index (-1)
67 , _crtc_id (0)
68 , _con_id (0)
69 , _encoder_id (0)
70 , _plane_id (0)
71 , _connector (NULL)
72 , _is_render_inited (false)
73 , _format (0)
74 , _width (0)
75 , _height (0)
76 {
77 xcam_mem_clear(_compose);
78
79 if (module)
80 _module = strndup (module, XCAM_MAX_STR_SIZE);
81 else
82 _module = strndup (DEFAULT_DRM_DEVICE, XCAM_MAX_STR_SIZE);
83
84 if (!_preview_flag) {
85 _fd = open_drivers ("/dev/dri/renderD", 128);
86 }
87
88 if (_fd < 0)
89 _fd = open_drivers ("/dev/dri/card", 0);
90
91 if (_fd < 0) {
92 _fd = drmOpen (_module, DEFAULT_DRM_BUSID);
93 if (_fd >= 0 && !is_authenticated (_fd, DEFAULT_DRM_BUSID)) {
94 drmClose (_fd);
95 _fd = -1;
96 }
97 }
98
99 if (_fd < 0) {
100 XCAM_LOG_WARNING ("please try root privilege if without X server");
101 XCAM_LOG_ERROR ("failed to open drm device %s", XCAM_STR (_module));
102 }
103
104 _buf_manager = drm_intel_bufmgr_gem_init (_fd, DEFAULT_DRM_BATCH_SIZE);
105 drm_intel_bufmgr_gem_enable_reuse (_buf_manager);
106 }
107
~DrmDisplay()108 DrmDisplay::~DrmDisplay()
109 {
110 _display_buf.release ();
111
112 if (_buf_manager)
113 drm_intel_bufmgr_destroy (_buf_manager);
114 if (_fd >= 0)
115 drmClose (_fd);
116 if (_module)
117 xcam_free (_module);
118 };
119
120 int
open_drivers(const char * base_path,int base_id)121 DrmDisplay::open_drivers (const char *base_path, int base_id)
122 {
123 int fd = -1;
124 char dev_path [32];
125 XCAM_ASSERT (base_path);
126
127 for (int i = 0; i < 16; i++) {
128 sprintf (dev_path, "%s%d", base_path, base_id + i);
129 if (access (dev_path, F_OK) != 0)
130 continue;
131
132 fd = open_driver (dev_path);
133 if (fd >= 0)
134 break;
135 }
136
137 return fd;
138 }
139
140 int
open_driver(const char * dev_path)141 DrmDisplay::open_driver (const char *dev_path)
142 {
143 XCAM_ASSERT (dev_path);
144
145 int fd = open (dev_path, O_RDWR);
146 if (fd < 0) {
147 XCAM_LOG_ERROR ("failed to open %s", dev_path);
148 return -1;
149 }
150
151 if (!strncmp (dev_path, "/dev/dri/card", 13)) {
152 if (!is_authenticated (fd, dev_path)) {
153 close (fd);
154 return -1;
155 }
156 }
157
158 return fd;
159 }
160
161 bool
is_authenticated(int fd,const char * msg)162 DrmDisplay::is_authenticated (int fd, const char *msg)
163 {
164 drm_client_t client;
165 memset (&client, 0, sizeof (drm_client_t));
166 if (ioctl (fd, DRM_IOCTL_GET_CLIENT, &client) == -1) {
167 XCAM_LOG_ERROR ("failed to get drm client");
168 return false;
169 }
170
171 if (!client.auth) {
172 XCAM_LOG_ERROR ("%s is not authenticated", msg);
173 return false;
174 }
175
176 return true;
177 }
178
179 uint32_t
to_drm_fourcc(uint32_t fourcc_of_v4l2)180 DrmDisplay::to_drm_fourcc (uint32_t fourcc_of_v4l2)
181 {
182 switch (fourcc_of_v4l2) {
183 case V4L2_PIX_FMT_RGB565:
184 return DRM_FORMAT_RGB565;
185 default:
186 break;
187 }
188 return fourcc_of_v4l2;
189 }
190
191 XCamReturn
get_crtc(drmModeRes * res)192 DrmDisplay::get_crtc(drmModeRes *res)
193 {
194 _crtc_index = -1;
195
196 drmModeEncoderPtr encoder = drmModeGetEncoder(_fd, _encoder_id);
197 XCAM_FAIL_RETURN(ERROR, encoder, XCAM_RETURN_ERROR_PARAM,
198 "drmModeGetEncoder failed: %s", strerror(errno));
199
200 _crtc_id = encoder->crtc_id;
201 drmModeFreeEncoder(encoder);
202
203 for (int i = 0; i < res->count_crtcs; i++) {
204 if (_crtc_id == res->crtcs[i]) {
205 _crtc_index = i;
206 break;
207 }
208 }
209 XCAM_FAIL_RETURN(ERROR, _crtc_index != -1, XCAM_RETURN_ERROR_PARAM,
210 "CRTC %d not found", _crtc_id);
211
212 return XCAM_RETURN_NO_ERROR;
213 }
214
215 XCamReturn
get_connector(drmModeRes * res)216 DrmDisplay::get_connector(drmModeRes *res)
217 {
218 XCAM_FAIL_RETURN(ERROR, res->count_connectors > 0, XCAM_RETURN_ERROR_PARAM,
219 "No connector found");
220 for(int i = 0; i < res->count_connectors; ++i) {
221 _connector = drmModeGetConnector(_fd, res->connectors[i]);
222 if(_connector && _connector->connection == DRM_MODE_CONNECTED) {
223 _con_id = res->connectors[i];
224 _encoder_id = res->encoders[i];
225 _mode = *_connector->modes;
226 }
227 drmModeFreeConnector(_connector);
228 }
229 XCAM_FAIL_RETURN(ERROR, _connector, XCAM_RETURN_ERROR_PARAM,
230 "drmModeGetConnector failed: %s", strerror(errno));
231
232 return XCAM_RETURN_NO_ERROR;
233 }
234
235
236 XCamReturn
get_plane()237 DrmDisplay::get_plane()
238 {
239 drmModePlaneResPtr planes = drmModeGetPlaneResources(_fd);
240 XCAM_FAIL_RETURN(ERROR, planes, XCAM_RETURN_ERROR_PARAM,
241 "failed to query planes: %s", strerror(errno));
242
243 drmModePlanePtr plane = NULL;
244 for (uint32_t i = 0; i < planes->count_planes; i++) {
245 if (plane) {
246 drmModeFreePlane(plane);
247 plane = NULL;
248 }
249 plane = drmModeGetPlane(_fd, planes->planes[i]);
250 XCAM_FAIL_RETURN(ERROR, plane, XCAM_RETURN_ERROR_PARAM,
251 "failed to query plane %d: %s", i, strerror(errno));
252
253 if (plane->crtc_id || !(plane->possible_crtcs & (1 << _crtc_index))) {
254 continue;
255 }
256
257 for (uint32_t j = 0; j < plane->count_formats; j++) {
258 // found a plane matching the requested format
259 if (plane->formats[j] == _format) {
260 _plane_id = plane->plane_id;
261 drmModeFreePlane(plane);
262 drmModeFreePlaneResources(planes);
263 return XCAM_RETURN_NO_ERROR;
264 }
265 }
266 }
267
268 if (plane)
269 drmModeFreePlane(plane);
270
271 drmModeFreePlaneResources(planes);
272
273 return XCAM_RETURN_ERROR_PARAM;
274 }
275
276 XCamReturn
render_init(uint32_t con_id,uint32_t crtc_id,uint32_t width,uint32_t height,uint32_t format,const struct v4l2_rect * compose)277 DrmDisplay::render_init (
278 uint32_t con_id,
279 uint32_t crtc_id,
280 uint32_t width,
281 uint32_t height,
282 uint32_t format,
283 const struct v4l2_rect* compose)
284 {
285 XCamReturn ret = XCAM_RETURN_NO_ERROR;
286
287 if (is_render_inited ())
288 return ret;
289
290 _con_id = con_id;
291 _crtc_id = crtc_id;
292 _width = width;
293 _height = height;
294 _format = to_drm_fourcc (format);
295 _compose = *compose;
296 _crtc_index = -1;
297 _plane_id = 0;
298 _connector = NULL;
299
300 drmModeRes *resource = drmModeGetResources(_fd);
301 XCAM_FAIL_RETURN(ERROR, resource, XCAM_RETURN_ERROR_PARAM,
302 "failed to query Drm Mode resources: %s", strerror(errno));
303
304 ret = get_connector(resource);
305 XCAM_FAIL_RETURN(ERROR, ret == XCAM_RETURN_NO_ERROR,
306 XCAM_RETURN_ERROR_PARAM,
307 "failed to get connector %s", strerror(errno));
308
309 ret = get_crtc(resource);
310 XCAM_FAIL_RETURN(ERROR, ret == XCAM_RETURN_NO_ERROR,
311 XCAM_RETURN_ERROR_PARAM,
312 "failed to get CRTC %s", strerror(errno));
313
314 ret = get_plane();
315 XCAM_FAIL_RETURN(ERROR, ret == XCAM_RETURN_NO_ERROR,
316 XCAM_RETURN_ERROR_PARAM,
317 "failed to get plane with required format %s", strerror(errno));
318
319 drmModeFreeResources(resource);
320 if (_display_mode == DRM_DISPLAY_MODE_OVERLAY)
321 _is_render_inited = true;
322 return XCAM_RETURN_NO_ERROR;
323 }
324
325
326 SmartPtr<V4l2Buffer>
create_drm_buf(const struct v4l2_format & format,const uint32_t index,const enum v4l2_buf_type buf_type)327 DrmDisplay::create_drm_buf (
328 const struct v4l2_format &format,
329 const uint32_t index,
330 const enum v4l2_buf_type buf_type)
331 {
332 struct drm_mode_create_dumb gem;
333 struct drm_prime_handle prime;
334 struct v4l2_buffer v4l2_buf;
335 int ret = 0;
336
337 xcam_mem_clear (gem);
338 xcam_mem_clear (prime);
339 xcam_mem_clear (v4l2_buf);
340
341 gem.width = format.fmt.pix.bytesperline;
342 gem.height = format.fmt.pix.height;
343 gem.bpp = 8;
344 ret = xcam_device_ioctl (_fd, DRM_IOCTL_MODE_CREATE_DUMB, &gem);
345 XCAM_ASSERT (ret >= 0);
346
347 prime.handle = gem.handle;
348 ret = xcam_device_ioctl (_fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &prime);
349 if (ret < 0) {
350 XCAM_LOG_WARNING ("create drm failed on DRM_IOCTL_PRIME_HANDLE_TO_FD");
351 return NULL;
352 }
353
354 v4l2_buf.index = index;
355 v4l2_buf.type = buf_type;
356 v4l2_buf.memory = V4L2_MEMORY_DMABUF;
357 v4l2_buf.m.fd = prime.fd;
358 v4l2_buf.length = XCAM_MAX (format.fmt.pix.sizeimage, gem.size); // todo check gem.size and format.fmt.pix.length
359 XCAM_LOG_DEBUG ("create drm buffer size:%lld", gem.size);
360 return new DrmV4l2Buffer (gem.handle, v4l2_buf, format, _instance);
361 }
362
363 XCamReturn
render_setup_frame_buffer(SmartPtr<VideoBuffer> & buf)364 DrmDisplay::render_setup_frame_buffer (SmartPtr<VideoBuffer> &buf)
365 {
366 XCamReturn ret = XCAM_RETURN_NO_ERROR;
367 VideoBufferInfo video_info = buf->get_video_info ();
368 uint32_t fourcc = video_info.format;
369 uint32_t fb_handle = 0;
370 uint32_t bo_handle = 0;
371 uint32_t bo_handles[4] = { 0 };
372 FB fb;
373 SmartPtr<V4l2BufferProxy> v4l2_proxy;
374 SmartPtr<DrmBoBuffer> bo_buf;
375
376 v4l2_proxy = buf.dynamic_cast_ptr<V4l2BufferProxy> ();
377 bo_buf = buf.dynamic_cast_ptr<DrmBoBuffer> ();
378 if (v4l2_proxy.ptr ()) {
379 struct drm_prime_handle prime;
380 memset(&prime, 0, sizeof (prime));
381 prime.fd = v4l2_proxy->get_v4l2_dma_fd();
382
383 ret = (XCamReturn) xcam_device_ioctl(_fd, DRM_IOCTL_PRIME_FD_TO_HANDLE, &prime);
384 if (ret) {
385 XCAM_LOG_WARNING("FD_TO_PRIME_HANDLE failed: %s", strerror(errno));
386 return XCAM_RETURN_ERROR_IOCTL;
387 }
388 bo_handle = prime.handle;
389 } else if (bo_buf.ptr ()) {
390 const drm_intel_bo* bo = bo_buf->get_bo ();
391 XCAM_ASSERT (bo);
392 bo_handle = bo->handle;
393 } else {
394 XCAM_ASSERT (false);
395 XCAM_LOG_WARNING("drm setup framebuffer doesn't support this buffer");
396 return XCAM_RETURN_ERROR_PARAM;
397 }
398
399 for (uint32_t i = 0; i < 4; ++i) {
400 bo_handles [i] = bo_handle;
401 }
402
403 ret = (XCamReturn) drmModeAddFB2(_fd, video_info.width, video_info.height, fourcc, bo_handles,
404 video_info.strides, video_info.offsets, &fb_handle, 0);
405
406 fb.fb_handle = fb_handle;
407 fb.index = global_signal_index++;
408 _buf_fb_handles[buf.ptr ()] = fb;
409
410 XCAM_FAIL_RETURN(ERROR, ret == XCAM_RETURN_NO_ERROR, XCAM_RETURN_ERROR_PARAM,
411 "drmModeAddFB2 failed: %s", strerror(errno));
412
413 return ret;
414 }
415
416 XCamReturn
set_crtc(const FB & fb)417 DrmDisplay::set_crtc (const FB &fb)
418 {
419 XCamReturn ret = XCAM_RETURN_NO_ERROR;
420 uint32_t fb_handle = fb.fb_handle;
421 //uint32_t index = fb.index;
422
423 if( !_is_render_inited) {
424 ret = (XCamReturn) drmModeSetCrtc(_fd, _crtc_id, fb_handle, 0,
425 0, &_con_id, 1, &_mode);
426 XCAM_FAIL_RETURN(ERROR, ret == XCAM_RETURN_NO_ERROR, XCAM_RETURN_ERROR_IOCTL,
427 "failed to set crct via drm: %s", strerror(errno));
428 _is_render_inited = true;
429 }
430 return ret;
431 }
432
433 XCamReturn
set_plane(const FB & fb)434 DrmDisplay::set_plane (const FB &fb)
435 {
436 XCamReturn ret = XCAM_RETURN_NO_ERROR;
437 uint32_t fb_handle = fb.fb_handle;
438 //uint32_t index = fb.index;
439
440 ret = (XCamReturn) drmModeSetPlane(_fd, _plane_id, _crtc_id,
441 fb_handle, 0,
442 _compose.left, _compose.top,
443 _compose.width, _compose.height,
444 0, 0, _width << 16, _height << 16);
445 XCAM_FAIL_RETURN(ERROR, ret == XCAM_RETURN_NO_ERROR, XCAM_RETURN_ERROR_IOCTL,
446 "failed to set plane via drm: %s", strerror(errno));
447 #if 0
448 drmVBlank vblank;
449 vblank.request.type = (drmVBlankSeqType) (DRM_VBLANK_EVENT | DRM_VBLANK_RELATIVE);
450 vblank.request.sequence = 1;
451 vblank.request.signal = (unsigned long) index;
452 ret = (XCamReturn) drmWaitVBlank(_fd, &vblank);
453 XCAM_FAIL_RETURN(ERROR, ret == XCAM_RETURN_NO_ERROR, XCAM_RETURN_ERROR_IOCTL,
454 "failed to wait vblank: %s", strerror(errno));
455 #endif
456 return XCAM_RETURN_NO_ERROR;
457 }
458
459 XCamReturn
page_flip(const FB & fb)460 DrmDisplay::page_flip (const FB &fb)
461 {
462 XCamReturn ret;
463 uint32_t fb_handle = fb.fb_handle;
464 uint32_t index = fb.index;
465
466 ret = (XCamReturn) drmModePageFlip(_fd, _crtc_id, fb_handle,
467 DRM_MODE_PAGE_FLIP_EVENT,
468 (void*)(unsigned long) index);
469 XCAM_FAIL_RETURN(ERROR, ret == XCAM_RETURN_NO_ERROR, XCAM_RETURN_ERROR_IOCTL,
470 "failed on page flip: %s", strerror(errno));
471
472 drmEventContext evctx;
473 struct timeval timeout = { .tv_sec = 3, .tv_usec = 0 };
474 fd_set fds;
475 memset(&evctx, 0, sizeof evctx);
476 evctx.version = DRM_EVENT_CONTEXT_VERSION;
477 evctx.vblank_handler = NULL;
478 //evctx.page_flip_handler = page_flip_handler;
479 FD_ZERO(&fds);
480 FD_SET(_fd, &fds);
481 select(_fd + 1, &fds, NULL, NULL, &timeout);
482 drmHandleEvent(_fd, &evctx);
483
484 return XCAM_RETURN_NO_ERROR;
485 }
486
487 XCamReturn
render_buffer(SmartPtr<VideoBuffer> & buf)488 DrmDisplay::render_buffer(SmartPtr<VideoBuffer> &buf)
489 {
490 XCamReturn ret = XCAM_RETURN_NO_ERROR;
491 FBMap::iterator iter = _buf_fb_handles.find (buf.ptr ());
492 XCAM_FAIL_RETURN(
493 ERROR,
494 iter != _buf_fb_handles.end (),
495 XCAM_RETURN_ERROR_PARAM,
496 "buffer not register on framebuf");
497 if(_display_mode == DRM_DISPLAY_MODE_OVERLAY)
498 ret = _plane_id ? set_plane(iter->second) : page_flip(iter->second);
499 else if(_display_mode == DRM_DISPLAY_MODE_PRIMARY) {
500 ret = set_crtc (iter->second);
501 ret = page_flip (iter->second);
502 }
503 _display_buf = buf;
504
505 return ret;
506 }
507
508 SmartPtr<DrmBoBuffer>
convert_to_drm_bo_buf(SmartPtr<DrmDisplay> & self,SmartPtr<VideoBuffer> & buf_in)509 DrmDisplay::convert_to_drm_bo_buf (SmartPtr<DrmDisplay> &self, SmartPtr<VideoBuffer> &buf_in)
510 {
511 drm_intel_bo *bo = NULL;
512 int dma_fd = 0;
513 SmartPtr<DrmBoBuffer> new_bo_buf;
514 SmartPtr<DrmBoData> bo_data;
515
516 XCAM_ASSERT (self.ptr () == this);
517 XCAM_ASSERT (buf_in.ptr ());
518
519 new_bo_buf = buf_in.dynamic_cast_ptr<DrmBoBuffer> ();
520 if (new_bo_buf.ptr ())
521 return new_bo_buf;
522
523 const VideoBufferInfo video_info = buf_in->get_video_info ();
524 dma_fd = buf_in->get_fd ();
525 if (dma_fd < 0) {
526 XCAM_LOG_DEBUG ("DrmDisplay only support dma buffer conversion to drm bo by now");
527 return NULL;
528 }
529
530 bo = drm_intel_bo_gem_create_from_prime (_buf_manager, dma_fd, video_info.size);
531 if (bo == NULL) {
532 XCAM_LOG_WARNING ("convert dma fd to drm bo failed");
533 return NULL;
534 }
535 bo_data = new DrmBoData (self, bo);
536 bo_data->set_prime_fd (dma_fd, false);
537 new_bo_buf = new DrmBoBuffer (video_info, bo_data);
538 new_bo_buf->set_parent (buf_in);
539 new_bo_buf->set_timestamp (buf_in->get_timestamp ());
540 return new_bo_buf;
541 }
542
543 SmartPtr<DrmBoData>
create_drm_bo(SmartPtr<DrmDisplay> & self,const VideoBufferInfo & info)544 DrmDisplay::create_drm_bo (SmartPtr<DrmDisplay> &self, const VideoBufferInfo &info)
545 {
546 SmartPtr<DrmBoData> new_bo;
547
548 XCAM_ASSERT (_buf_manager);
549 XCAM_ASSERT (self.ptr() == this);
550 drm_intel_bo *bo = drm_intel_bo_alloc (
551 _buf_manager, "xcam drm bo buf", info.size, 0x1000);
552
553 new_bo = new DrmBoData (self, bo);
554 return new_bo;
555 }
556
557 drm_intel_bo *
create_drm_bo_from_fd(int32_t fd,uint32_t size)558 DrmDisplay::create_drm_bo_from_fd (int32_t fd, uint32_t size)
559 {
560 drm_intel_bo *bo = NULL;
561 XCAM_ASSERT (_buf_manager);
562 bo = drm_intel_bo_gem_create_from_prime (_buf_manager, fd, size);
563
564 XCAM_ASSERT (bo);
565 return bo;
566 }
567
568
569 };
570