Home | History | Annotate | Download | only in xcore
      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 (at) 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
     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>
     52 DrmDisplay::instance ()
     53 {
     54     SmartLock lock(_mutex);
     55     if (_instance.ptr())
     56         return _instance;
     57     _instance = new DrmDisplay ();
     58     return _instance;
     59 }
     60 
     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 
    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
    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
    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
    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
    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
    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
    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
    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
    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>
    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
    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
    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
    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
    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
    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>
    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>
    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 *
    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