/*
|
* drm_display.cpp - drm display
|
*
|
* Copyright (c) 2015 Intel Corporation
|
*
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
* you may not use this file except in compliance with the License.
|
* You may obtain a copy of the License at
|
*
|
* http://www.apache.org/licenses/LICENSE-2.0
|
*
|
* Unless required by applicable law or agreed to in writing, software
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* See the License for the specific language governing permissions and
|
* limitations under the License.
|
*
|
* Author: John Ye <john.ye@intel.com>
|
*/
|
|
|
#include "drm_display.h"
|
#include "drm_v4l2_buffer.h"
|
#include "drm_bo_buffer.h"
|
#include <drm_fourcc.h>
|
#include <sys/ioctl.h>
|
#include <fcntl.h>
|
|
|
#define DEFAULT_DRM_DEVICE "i915"
|
#define DEFAULT_DRM_BUSID "PCI:00:02:00"
|
#define DEFAULT_DRM_BATCH_SIZE 0x80000
|
|
namespace XCam {
|
|
SmartPtr<DrmDisplay> DrmDisplay::_instance(NULL);
|
Mutex DrmDisplay::_mutex;
|
|
static std::atomic<uint32_t> global_signal_index(0);
|
|
bool DrmDisplay::_preview_flag = false;
|
|
bool
|
DrmDisplay::set_preview (bool flag) {
|
if (_instance.ptr () && flag != _preview_flag)
|
return false;
|
_preview_flag = flag;
|
return true;
|
};
|
|
SmartPtr<DrmDisplay>
|
DrmDisplay::instance ()
|
{
|
SmartLock lock(_mutex);
|
if (_instance.ptr())
|
return _instance;
|
_instance = new DrmDisplay ();
|
return _instance;
|
}
|
|
DrmDisplay::DrmDisplay (const char *module)
|
: _module(NULL)
|
, _fd (-1)
|
, _buf_manager (NULL)
|
, _display_mode (DRM_DISPLAY_MODE_NONE)
|
, _crtc_index (-1)
|
, _crtc_id (0)
|
, _con_id (0)
|
, _encoder_id (0)
|
, _plane_id (0)
|
, _connector (NULL)
|
, _is_render_inited (false)
|
, _format (0)
|
, _width (0)
|
, _height (0)
|
{
|
xcam_mem_clear(_compose);
|
|
if (module)
|
_module = strndup (module, XCAM_MAX_STR_SIZE);
|
else
|
_module = strndup (DEFAULT_DRM_DEVICE, XCAM_MAX_STR_SIZE);
|
|
if (!_preview_flag) {
|
_fd = open_drivers ("/dev/dri/renderD", 128);
|
}
|
|
if (_fd < 0)
|
_fd = open_drivers ("/dev/dri/card", 0);
|
|
if (_fd < 0) {
|
_fd = drmOpen (_module, DEFAULT_DRM_BUSID);
|
if (_fd >= 0 && !is_authenticated (_fd, DEFAULT_DRM_BUSID)) {
|
drmClose (_fd);
|
_fd = -1;
|
}
|
}
|
|
if (_fd < 0) {
|
XCAM_LOG_WARNING ("please try root privilege if without X server");
|
XCAM_LOG_ERROR ("failed to open drm device %s", XCAM_STR (_module));
|
}
|
|
_buf_manager = drm_intel_bufmgr_gem_init (_fd, DEFAULT_DRM_BATCH_SIZE);
|
drm_intel_bufmgr_gem_enable_reuse (_buf_manager);
|
}
|
|
DrmDisplay::~DrmDisplay()
|
{
|
_display_buf.release ();
|
|
if (_buf_manager)
|
drm_intel_bufmgr_destroy (_buf_manager);
|
if (_fd >= 0)
|
drmClose (_fd);
|
if (_module)
|
xcam_free (_module);
|
};
|
|
int
|
DrmDisplay::open_drivers (const char *base_path, int base_id)
|
{
|
int fd = -1;
|
char dev_path [32];
|
XCAM_ASSERT (base_path);
|
|
for (int i = 0; i < 16; i++) {
|
sprintf (dev_path, "%s%d", base_path, base_id + i);
|
if (access (dev_path, F_OK) != 0)
|
continue;
|
|
fd = open_driver (dev_path);
|
if (fd >= 0)
|
break;
|
}
|
|
return fd;
|
}
|
|
int
|
DrmDisplay::open_driver (const char *dev_path)
|
{
|
XCAM_ASSERT (dev_path);
|
|
int fd = open (dev_path, O_RDWR);
|
if (fd < 0) {
|
XCAM_LOG_ERROR ("failed to open %s", dev_path);
|
return -1;
|
}
|
|
if (!strncmp (dev_path, "/dev/dri/card", 13)) {
|
if (!is_authenticated (fd, dev_path)) {
|
close (fd);
|
return -1;
|
}
|
}
|
|
return fd;
|
}
|
|
bool
|
DrmDisplay::is_authenticated (int fd, const char *msg)
|
{
|
drm_client_t client;
|
memset (&client, 0, sizeof (drm_client_t));
|
if (ioctl (fd, DRM_IOCTL_GET_CLIENT, &client) == -1) {
|
XCAM_LOG_ERROR ("failed to get drm client");
|
return false;
|
}
|
|
if (!client.auth) {
|
XCAM_LOG_ERROR ("%s is not authenticated", msg);
|
return false;
|
}
|
|
return true;
|
}
|
|
uint32_t
|
DrmDisplay::to_drm_fourcc (uint32_t fourcc_of_v4l2)
|
{
|
switch (fourcc_of_v4l2) {
|
case V4L2_PIX_FMT_RGB565:
|
return DRM_FORMAT_RGB565;
|
default:
|
break;
|
}
|
return fourcc_of_v4l2;
|
}
|
|
XCamReturn
|
DrmDisplay::get_crtc(drmModeRes *res)
|
{
|
_crtc_index = -1;
|
|
drmModeEncoderPtr encoder = drmModeGetEncoder(_fd, _encoder_id);
|
XCAM_FAIL_RETURN(ERROR, encoder, XCAM_RETURN_ERROR_PARAM,
|
"drmModeGetEncoder failed: %s", strerror(errno));
|
|
_crtc_id = encoder->crtc_id;
|
drmModeFreeEncoder(encoder);
|
|
for (int i = 0; i < res->count_crtcs; i++) {
|
if (_crtc_id == res->crtcs[i]) {
|
_crtc_index = i;
|
break;
|
}
|
}
|
XCAM_FAIL_RETURN(ERROR, _crtc_index != -1, XCAM_RETURN_ERROR_PARAM,
|
"CRTC %d not found", _crtc_id);
|
|
return XCAM_RETURN_NO_ERROR;
|
}
|
|
XCamReturn
|
DrmDisplay::get_connector(drmModeRes *res)
|
{
|
XCAM_FAIL_RETURN(ERROR, res->count_connectors > 0, XCAM_RETURN_ERROR_PARAM,
|
"No connector found");
|
for(int i = 0; i < res->count_connectors; ++i) {
|
_connector = drmModeGetConnector(_fd, res->connectors[i]);
|
if(_connector && _connector->connection == DRM_MODE_CONNECTED) {
|
_con_id = res->connectors[i];
|
_encoder_id = res->encoders[i];
|
_mode = *_connector->modes;
|
}
|
drmModeFreeConnector(_connector);
|
}
|
XCAM_FAIL_RETURN(ERROR, _connector, XCAM_RETURN_ERROR_PARAM,
|
"drmModeGetConnector failed: %s", strerror(errno));
|
|
return XCAM_RETURN_NO_ERROR;
|
}
|
|
|
XCamReturn
|
DrmDisplay::get_plane()
|
{
|
drmModePlaneResPtr planes = drmModeGetPlaneResources(_fd);
|
XCAM_FAIL_RETURN(ERROR, planes, XCAM_RETURN_ERROR_PARAM,
|
"failed to query planes: %s", strerror(errno));
|
|
drmModePlanePtr plane = NULL;
|
for (uint32_t i = 0; i < planes->count_planes; i++) {
|
if (plane) {
|
drmModeFreePlane(plane);
|
plane = NULL;
|
}
|
plane = drmModeGetPlane(_fd, planes->planes[i]);
|
XCAM_FAIL_RETURN(ERROR, plane, XCAM_RETURN_ERROR_PARAM,
|
"failed to query plane %d: %s", i, strerror(errno));
|
|
if (plane->crtc_id || !(plane->possible_crtcs & (1 << _crtc_index))) {
|
continue;
|
}
|
|
for (uint32_t j = 0; j < plane->count_formats; j++) {
|
// found a plane matching the requested format
|
if (plane->formats[j] == _format) {
|
_plane_id = plane->plane_id;
|
drmModeFreePlane(plane);
|
drmModeFreePlaneResources(planes);
|
return XCAM_RETURN_NO_ERROR;
|
}
|
}
|
}
|
|
if (plane)
|
drmModeFreePlane(plane);
|
|
drmModeFreePlaneResources(planes);
|
|
return XCAM_RETURN_ERROR_PARAM;
|
}
|
|
XCamReturn
|
DrmDisplay::render_init (
|
uint32_t con_id,
|
uint32_t crtc_id,
|
uint32_t width,
|
uint32_t height,
|
uint32_t format,
|
const struct v4l2_rect* compose)
|
{
|
XCamReturn ret = XCAM_RETURN_NO_ERROR;
|
|
if (is_render_inited ())
|
return ret;
|
|
_con_id = con_id;
|
_crtc_id = crtc_id;
|
_width = width;
|
_height = height;
|
_format = to_drm_fourcc (format);
|
_compose = *compose;
|
_crtc_index = -1;
|
_plane_id = 0;
|
_connector = NULL;
|
|
drmModeRes *resource = drmModeGetResources(_fd);
|
XCAM_FAIL_RETURN(ERROR, resource, XCAM_RETURN_ERROR_PARAM,
|
"failed to query Drm Mode resources: %s", strerror(errno));
|
|
ret = get_connector(resource);
|
XCAM_FAIL_RETURN(ERROR, ret == XCAM_RETURN_NO_ERROR,
|
XCAM_RETURN_ERROR_PARAM,
|
"failed to get connector %s", strerror(errno));
|
|
ret = get_crtc(resource);
|
XCAM_FAIL_RETURN(ERROR, ret == XCAM_RETURN_NO_ERROR,
|
XCAM_RETURN_ERROR_PARAM,
|
"failed to get CRTC %s", strerror(errno));
|
|
ret = get_plane();
|
XCAM_FAIL_RETURN(ERROR, ret == XCAM_RETURN_NO_ERROR,
|
XCAM_RETURN_ERROR_PARAM,
|
"failed to get plane with required format %s", strerror(errno));
|
|
drmModeFreeResources(resource);
|
if (_display_mode == DRM_DISPLAY_MODE_OVERLAY)
|
_is_render_inited = true;
|
return XCAM_RETURN_NO_ERROR;
|
}
|
|
|
SmartPtr<V4l2Buffer>
|
DrmDisplay::create_drm_buf (
|
const struct v4l2_format &format,
|
const uint32_t index,
|
const enum v4l2_buf_type buf_type)
|
{
|
struct drm_mode_create_dumb gem;
|
struct drm_prime_handle prime;
|
struct v4l2_buffer v4l2_buf;
|
int ret = 0;
|
|
xcam_mem_clear (gem);
|
xcam_mem_clear (prime);
|
xcam_mem_clear (v4l2_buf);
|
|
gem.width = format.fmt.pix.bytesperline;
|
gem.height = format.fmt.pix.height;
|
gem.bpp = 8;
|
ret = xcam_device_ioctl (_fd, DRM_IOCTL_MODE_CREATE_DUMB, &gem);
|
XCAM_ASSERT (ret >= 0);
|
|
prime.handle = gem.handle;
|
ret = xcam_device_ioctl (_fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &prime);
|
if (ret < 0) {
|
XCAM_LOG_WARNING ("create drm failed on DRM_IOCTL_PRIME_HANDLE_TO_FD");
|
return NULL;
|
}
|
|
v4l2_buf.index = index;
|
v4l2_buf.type = buf_type;
|
v4l2_buf.memory = V4L2_MEMORY_DMABUF;
|
v4l2_buf.m.fd = prime.fd;
|
v4l2_buf.length = XCAM_MAX (format.fmt.pix.sizeimage, gem.size); // todo check gem.size and format.fmt.pix.length
|
XCAM_LOG_DEBUG ("create drm buffer size:%lld", gem.size);
|
return new DrmV4l2Buffer (gem.handle, v4l2_buf, format, _instance);
|
}
|
|
XCamReturn
|
DrmDisplay::render_setup_frame_buffer (SmartPtr<VideoBuffer> &buf)
|
{
|
XCamReturn ret = XCAM_RETURN_NO_ERROR;
|
VideoBufferInfo video_info = buf->get_video_info ();
|
uint32_t fourcc = video_info.format;
|
uint32_t fb_handle = 0;
|
uint32_t bo_handle = 0;
|
uint32_t bo_handles[4] = { 0 };
|
FB fb;
|
SmartPtr<V4l2BufferProxy> v4l2_proxy;
|
SmartPtr<DrmBoBuffer> bo_buf;
|
|
v4l2_proxy = buf.dynamic_cast_ptr<V4l2BufferProxy> ();
|
bo_buf = buf.dynamic_cast_ptr<DrmBoBuffer> ();
|
if (v4l2_proxy.ptr ()) {
|
struct drm_prime_handle prime;
|
memset(&prime, 0, sizeof (prime));
|
prime.fd = v4l2_proxy->get_v4l2_dma_fd();
|
|
ret = (XCamReturn) xcam_device_ioctl(_fd, DRM_IOCTL_PRIME_FD_TO_HANDLE, &prime);
|
if (ret) {
|
XCAM_LOG_WARNING("FD_TO_PRIME_HANDLE failed: %s", strerror(errno));
|
return XCAM_RETURN_ERROR_IOCTL;
|
}
|
bo_handle = prime.handle;
|
} else if (bo_buf.ptr ()) {
|
const drm_intel_bo* bo = bo_buf->get_bo ();
|
XCAM_ASSERT (bo);
|
bo_handle = bo->handle;
|
} else {
|
XCAM_ASSERT (false);
|
XCAM_LOG_WARNING("drm setup framebuffer doesn't support this buffer");
|
return XCAM_RETURN_ERROR_PARAM;
|
}
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
bo_handles [i] = bo_handle;
|
}
|
|
ret = (XCamReturn) drmModeAddFB2(_fd, video_info.width, video_info.height, fourcc, bo_handles,
|
video_info.strides, video_info.offsets, &fb_handle, 0);
|
|
fb.fb_handle = fb_handle;
|
fb.index = global_signal_index++;
|
_buf_fb_handles[buf.ptr ()] = fb;
|
|
XCAM_FAIL_RETURN(ERROR, ret == XCAM_RETURN_NO_ERROR, XCAM_RETURN_ERROR_PARAM,
|
"drmModeAddFB2 failed: %s", strerror(errno));
|
|
return ret;
|
}
|
|
XCamReturn
|
DrmDisplay::set_crtc (const FB &fb)
|
{
|
XCamReturn ret = XCAM_RETURN_NO_ERROR;
|
uint32_t fb_handle = fb.fb_handle;
|
//uint32_t index = fb.index;
|
|
if( !_is_render_inited) {
|
ret = (XCamReturn) drmModeSetCrtc(_fd, _crtc_id, fb_handle, 0,
|
0, &_con_id, 1, &_mode);
|
XCAM_FAIL_RETURN(ERROR, ret == XCAM_RETURN_NO_ERROR, XCAM_RETURN_ERROR_IOCTL,
|
"failed to set crct via drm: %s", strerror(errno));
|
_is_render_inited = true;
|
}
|
return ret;
|
}
|
|
XCamReturn
|
DrmDisplay::set_plane (const FB &fb)
|
{
|
XCamReturn ret = XCAM_RETURN_NO_ERROR;
|
uint32_t fb_handle = fb.fb_handle;
|
//uint32_t index = fb.index;
|
|
ret = (XCamReturn) drmModeSetPlane(_fd, _plane_id, _crtc_id,
|
fb_handle, 0,
|
_compose.left, _compose.top,
|
_compose.width, _compose.height,
|
0, 0, _width << 16, _height << 16);
|
XCAM_FAIL_RETURN(ERROR, ret == XCAM_RETURN_NO_ERROR, XCAM_RETURN_ERROR_IOCTL,
|
"failed to set plane via drm: %s", strerror(errno));
|
#if 0
|
drmVBlank vblank;
|
vblank.request.type = (drmVBlankSeqType) (DRM_VBLANK_EVENT | DRM_VBLANK_RELATIVE);
|
vblank.request.sequence = 1;
|
vblank.request.signal = (unsigned long) index;
|
ret = (XCamReturn) drmWaitVBlank(_fd, &vblank);
|
XCAM_FAIL_RETURN(ERROR, ret == XCAM_RETURN_NO_ERROR, XCAM_RETURN_ERROR_IOCTL,
|
"failed to wait vblank: %s", strerror(errno));
|
#endif
|
return XCAM_RETURN_NO_ERROR;
|
}
|
|
XCamReturn
|
DrmDisplay::page_flip (const FB &fb)
|
{
|
XCamReturn ret;
|
uint32_t fb_handle = fb.fb_handle;
|
uint32_t index = fb.index;
|
|
ret = (XCamReturn) drmModePageFlip(_fd, _crtc_id, fb_handle,
|
DRM_MODE_PAGE_FLIP_EVENT,
|
(void*)(unsigned long) index);
|
XCAM_FAIL_RETURN(ERROR, ret == XCAM_RETURN_NO_ERROR, XCAM_RETURN_ERROR_IOCTL,
|
"failed on page flip: %s", strerror(errno));
|
|
drmEventContext evctx;
|
struct timeval timeout = { .tv_sec = 3, .tv_usec = 0 };
|
fd_set fds;
|
memset(&evctx, 0, sizeof evctx);
|
evctx.version = DRM_EVENT_CONTEXT_VERSION;
|
evctx.vblank_handler = NULL;
|
//evctx.page_flip_handler = page_flip_handler;
|
FD_ZERO(&fds);
|
FD_SET(_fd, &fds);
|
select(_fd + 1, &fds, NULL, NULL, &timeout);
|
drmHandleEvent(_fd, &evctx);
|
|
return XCAM_RETURN_NO_ERROR;
|
}
|
|
XCamReturn
|
DrmDisplay::render_buffer(SmartPtr<VideoBuffer> &buf)
|
{
|
XCamReturn ret = XCAM_RETURN_NO_ERROR;
|
FBMap::iterator iter = _buf_fb_handles.find (buf.ptr ());
|
XCAM_FAIL_RETURN(
|
ERROR,
|
iter != _buf_fb_handles.end (),
|
XCAM_RETURN_ERROR_PARAM,
|
"buffer not register on framebuf");
|
if(_display_mode == DRM_DISPLAY_MODE_OVERLAY)
|
ret = _plane_id ? set_plane(iter->second) : page_flip(iter->second);
|
else if(_display_mode == DRM_DISPLAY_MODE_PRIMARY) {
|
ret = set_crtc (iter->second);
|
ret = page_flip (iter->second);
|
}
|
_display_buf = buf;
|
|
return ret;
|
}
|
|
SmartPtr<DrmBoBuffer>
|
DrmDisplay::convert_to_drm_bo_buf (SmartPtr<DrmDisplay> &self, SmartPtr<VideoBuffer> &buf_in)
|
{
|
drm_intel_bo *bo = NULL;
|
int dma_fd = 0;
|
SmartPtr<DrmBoBuffer> new_bo_buf;
|
SmartPtr<DrmBoData> bo_data;
|
|
XCAM_ASSERT (self.ptr () == this);
|
XCAM_ASSERT (buf_in.ptr ());
|
|
new_bo_buf = buf_in.dynamic_cast_ptr<DrmBoBuffer> ();
|
if (new_bo_buf.ptr ())
|
return new_bo_buf;
|
|
const VideoBufferInfo video_info = buf_in->get_video_info ();
|
dma_fd = buf_in->get_fd ();
|
if (dma_fd < 0) {
|
XCAM_LOG_DEBUG ("DrmDisplay only support dma buffer conversion to drm bo by now");
|
return NULL;
|
}
|
|
bo = drm_intel_bo_gem_create_from_prime (_buf_manager, dma_fd, video_info.size);
|
if (bo == NULL) {
|
XCAM_LOG_WARNING ("convert dma fd to drm bo failed");
|
return NULL;
|
}
|
bo_data = new DrmBoData (self, bo);
|
bo_data->set_prime_fd (dma_fd, false);
|
new_bo_buf = new DrmBoBuffer (video_info, bo_data);
|
new_bo_buf->set_parent (buf_in);
|
new_bo_buf->set_timestamp (buf_in->get_timestamp ());
|
return new_bo_buf;
|
}
|
|
SmartPtr<DrmBoData>
|
DrmDisplay::create_drm_bo (SmartPtr<DrmDisplay> &self, const VideoBufferInfo &info)
|
{
|
SmartPtr<DrmBoData> new_bo;
|
|
XCAM_ASSERT (_buf_manager);
|
XCAM_ASSERT (self.ptr() == this);
|
drm_intel_bo *bo = drm_intel_bo_alloc (
|
_buf_manager, "xcam drm bo buf", info.size, 0x1000);
|
|
new_bo = new DrmBoData (self, bo);
|
return new_bo;
|
}
|
|
drm_intel_bo *
|
DrmDisplay::create_drm_bo_from_fd (int32_t fd, uint32_t size)
|
{
|
drm_intel_bo *bo = NULL;
|
XCAM_ASSERT (_buf_manager);
|
bo = drm_intel_bo_gem_create_from_prime (_buf_manager, fd, size);
|
|
XCAM_ASSERT (bo);
|
return bo;
|
}
|
|
|
};
|