/* 
 | 
 * drm_device.cpp - DRM Device Implementation 
 | 
 * 
 | 
 *  Copyright (c) 2021 Rockchip 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. 
 | 
 * 
 | 
 */ 
 | 
#include "drm_device.h" 
 | 
  
 | 
#include <fcntl.h> 
 | 
#include <sys/mman.h> 
 | 
#include <xf86drm.h> 
 | 
  
 | 
#include <string> 
 | 
  
 | 
#include "xcam_common.h" 
 | 
#include "xcam_log.h" 
 | 
  
 | 
extern "C" { 
 | 
/* assume large file support exists */ 
 | 
#define drm_mmap(addr, length, prot, flags, fd, offset) \ 
 | 
    mmap(addr, length, prot, flags, fd, offset) 
 | 
  
 | 
static inline int drm_munmap(void* addr, size_t length) { 
 | 
    /* Copied from configure code generated by AC_SYS_LARGEFILE */ 
 | 
#define LARGE_OFF_T ((((off_t)1 << 31) << 31) - 1 + (((off_t)1 << 31) << 31)) 
 | 
    static_assert(LARGE_OFF_T % 2147483629 == 721 && 
 | 
                  LARGE_OFF_T % 2147483647 == 1); 
 | 
#undef LARGE_OFF_T 
 | 
    return munmap(addr, length); 
 | 
} 
 | 
} 
 | 
  
 | 
namespace XCam { 
 | 
  
 | 
DrmDevice::DrmDevice() : fd_(Open()) {} 
 | 
  
 | 
DrmDevice::~DrmDevice() = default; 
 | 
  
 | 
int DrmDevice::Open() { 
 | 
    std::string devName(DRM_DIR_NAME); 
 | 
    uint64_t has_dumb = 0; 
 | 
    int fd; 
 | 
  
 | 
    devName.append("/card"); 
 | 
    devName.append(std::to_string(0)); 
 | 
    fd = drmOpen("rockchip", devName.c_str()); 
 | 
    if (fd < 0) { 
 | 
        LOGE("Failed to open DRM module rockchip"); 
 | 
        return -1; 
 | 
    } 
 | 
  
 | 
    drmVersionPtr version = drmGetVersion(fd); 
 | 
    if (version == nullptr) { 
 | 
        LOGE("Failed to get DRM version"); 
 | 
        ::close(fd); 
 | 
        return -1; 
 | 
    } 
 | 
    LOGI("Opened DRM device %s: driver %s version V%d.%d.%d", devName.c_str(), 
 | 
         version->name, version->version_major, version->version_minor, 
 | 
         version->version_patchlevel); 
 | 
    drmFreeVersion(version); 
 | 
  
 | 
    if (drmGetCap(fd, DRM_CAP_DUMB_BUFFER, &has_dumb) < 0 || !has_dumb) { 
 | 
        LOGE("DRM device does not support dumb buffer"); 
 | 
        ::close(fd); 
 | 
        return -1; 
 | 
    } 
 | 
  
 | 
    return fd; 
 | 
} 
 | 
  
 | 
void DrmDevice::Close() { 
 | 
    if (fd_.Get() >= 0) { 
 | 
        drmClose(fd_.Get()); 
 | 
    } 
 | 
} 
 | 
  
 | 
bool DrmDevice::Available() { return !!drmAvailable(); } 
 | 
  
 | 
std::unique_ptr<DrmDumbObject> DrmDevice::CreateDumbObject( 
 | 
    unsigned int width, unsigned int height, unsigned int bpp, 
 | 
    unsigned int num_planes) { 
 | 
    struct drm_mode_create_dumb arg; 
 | 
    int ret; 
 | 
    // @cody TODO: use make_unique if use C++ > C++14 
 | 
    std::unique_ptr<DrmDumbObject> bo = 
 | 
        std::unique_ptr<DrmDumbObject>(new DrmDumbObject()); 
 | 
  
 | 
    memset(&arg, 0, sizeof(arg)); 
 | 
    arg.bpp    = bpp; 
 | 
    arg.width  = width; 
 | 
    arg.height = height; 
 | 
  
 | 
    bo->num_planes = num_planes; 
 | 
    for (unsigned int plane = 0; plane < num_planes; plane++) { 
 | 
        ret = drmIoctl(fd_.Get(), DRM_IOCTL_MODE_CREATE_DUMB, &arg); 
 | 
        if (ret) { 
 | 
            LOGE("failed to create dumb buffer: %s", strerror(errno)); 
 | 
            return nullptr; 
 | 
        } 
 | 
  
 | 
        ret = drmPrimeHandleToFD(fd_.Get(), arg.handle, DRM_CLOEXEC | DRM_RDWR, 
 | 
                                 &bo->fds[plane]); 
 | 
        if (ret) { 
 | 
            LOGE("failed to create dumb buffer: %s", strerror(errno)); 
 | 
            return nullptr; 
 | 
        } 
 | 
        assert(bo->fds[plane] >= 0); 
 | 
  
 | 
        bo->handles[plane] = arg.handle; 
 | 
        bo->sizes[plane]   = arg.size; 
 | 
        bo->strides[plane] = arg.pitch; 
 | 
    } 
 | 
  
 | 
    return bo; 
 | 
} 
 | 
  
 | 
XCamReturn DrmDevice::DestroyDumbObject( 
 | 
    const std::unique_ptr<DrmDumbObject>& bo) { 
 | 
    struct drm_mode_destroy_dumb arg; 
 | 
    int ret; 
 | 
  
 | 
    assert(fd_.Get() >= 0); 
 | 
  
 | 
    memset(&arg, 0, sizeof(arg)); 
 | 
    for (int plane = 0; plane < bo->num_planes; plane++) { 
 | 
        if (bo->handles[plane] == 0) { 
 | 
            continue; 
 | 
        } 
 | 
        arg.handle = bo->handles[plane]; 
 | 
  
 | 
        ret = drmIoctl(fd_.Get(), DRM_IOCTL_MODE_DESTROY_DUMB, &arg); 
 | 
        if (ret) { 
 | 
            LOGE("failed to destroy dumb buffer: %s", strerror(errno)); 
 | 
            // return XCAM_RETURN_ERROR_FAILED; 
 | 
        } 
 | 
    } 
 | 
  
 | 
    return XCAM_RETURN_NO_ERROR; 
 | 
} 
 | 
  
 | 
XCamReturn DrmDevice::RequestMapDumbObject( 
 | 
    const std::unique_ptr<DrmDumbObject>& bo, unsigned int plane) { 
 | 
    struct drm_mode_map_dumb arg; 
 | 
    void* map; 
 | 
    int ret; 
 | 
  
 | 
    assert(fd_.Get() >= 0); 
 | 
  
 | 
    memset(&arg, 0, sizeof(arg)); 
 | 
    arg.handle = bo->handles[plane]; 
 | 
    ret        = drmIoctl(fd_.Get(), DRM_IOCTL_MODE_MAP_DUMB, &arg); 
 | 
    if (ret) { 
 | 
        LOGE("drm ioctrl failed map %s", strerror(errno)); 
 | 
        return XCAM_RETURN_ERROR_FAILED; 
 | 
    } 
 | 
  
 | 
    return XCAM_RETURN_NO_ERROR; 
 | 
} 
 | 
  
 | 
}  // namespace RkCam 
 |