/*
|
* Copyright (C) 2020 Rockchip Electronics Co., Ltd.
|
*
|
* This software is available to you under a choice of one of two
|
* licenses. You may choose to be licensed under the terms of the GNU
|
* General Public License (GPL), available from the file
|
* COPYING in the main directory of this source tree, or the
|
* OpenIB.org BSD license below:
|
*
|
* Redistribution and use in source and binary forms, with or
|
* without modification, are permitted provided that the following
|
* conditions are met:
|
*
|
* - Redistributions of source code must retain the above
|
* copyright notice, this list of conditions and the following
|
* disclaimer.
|
*
|
* - Redistributions in binary form must reproduce the above
|
* copyright notice, this list of conditions and the following
|
* disclaimer in the documentation and/or other materials
|
* provided with the distribution.
|
*
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
* SOFTWARE.
|
*/
|
#include <libdrm/drm_mode.h>
|
#include <stdbool.h>
|
#include <stdint.h>
|
#include <stddef.h>
|
#include <xf86drmMode.h>
|
#include <sys/mman.h>
|
#include <unistd.h>
|
#include "rkdrm_display.h"
|
|
#define IS_SUPPORT_WINID_PROPERTY 0
|
#define INT_MAX 0xff
|
#define COLOR_KEY 0xFFFFFFFF
|
|
static bool drm_plane_set_property (int fd, drmModePlane * plane,
|
const char *prop_name, uint64_t prop_value)
|
{
|
drmModeObjectPropertiesPtr props;
|
drmModePropertyPtr prop;
|
int i, ret = -1;
|
|
props = drmModeObjectGetProperties (fd, plane->plane_id,
|
DRM_MODE_OBJECT_PLANE);
|
if (!props)
|
return false;
|
|
for (i = 0; i < props->count_props; i++) {
|
prop = drmModeGetProperty (fd, props->props[i]);
|
if (prop && !strcmp (prop->name, prop_name)) {
|
ret = drmModeObjectSetProperty (fd, plane->plane_id,
|
DRM_MODE_OBJECT_PLANE, props->props[i], prop_value);
|
}
|
drmModeFreeProperty (prop);
|
}
|
|
drmModeFreeObjectProperties (props);
|
return ret < 0 ? false : true;
|
}
|
|
/**
|
* drm_fb_get_bpp_depth - get the bpp/depth values for format
|
* @format: pixel format (DRM_FORMAT_*)
|
* @depth: storage for the depth value
|
* @bpp: storage for the bpp value
|
*
|
* This only supports RGB formats here for compat with code that doesn't use
|
* pixel formats directly yet.
|
*/
|
void drm_fb_get_bpp_depth(uint32_t format, unsigned int *depth, int *bpp)
|
{
|
switch (format) {
|
case DRM_FORMAT_C8:
|
case DRM_FORMAT_RGB332:
|
case DRM_FORMAT_BGR233:
|
*depth = 8;
|
*bpp = 8;
|
break;
|
case DRM_FORMAT_XRGB1555:
|
case DRM_FORMAT_XBGR1555:
|
case DRM_FORMAT_RGBX5551:
|
case DRM_FORMAT_BGRX5551:
|
case DRM_FORMAT_ARGB1555:
|
case DRM_FORMAT_ABGR1555:
|
case DRM_FORMAT_RGBA5551:
|
case DRM_FORMAT_BGRA5551:
|
*depth = 15;
|
*bpp = 16;
|
break;
|
case DRM_FORMAT_RGB565:
|
case DRM_FORMAT_BGR565:
|
*depth = 16;
|
*bpp = 16;
|
break;
|
case DRM_FORMAT_RGB888:
|
case DRM_FORMAT_BGR888:
|
*depth = 24;
|
*bpp = 24;
|
break;
|
case DRM_FORMAT_XRGB8888:
|
case DRM_FORMAT_XBGR8888:
|
case DRM_FORMAT_RGBX8888:
|
case DRM_FORMAT_BGRX8888:
|
*depth = 24;
|
*bpp = 32;
|
break;
|
case DRM_FORMAT_XRGB2101010:
|
case DRM_FORMAT_XBGR2101010:
|
case DRM_FORMAT_RGBX1010102:
|
case DRM_FORMAT_BGRX1010102:
|
case DRM_FORMAT_ARGB2101010:
|
case DRM_FORMAT_ABGR2101010:
|
case DRM_FORMAT_RGBA1010102:
|
case DRM_FORMAT_BGRA1010102:
|
*depth = 30;
|
*bpp = 32;
|
break;
|
case DRM_FORMAT_ARGB8888:
|
case DRM_FORMAT_ABGR8888:
|
case DRM_FORMAT_RGBA8888:
|
case DRM_FORMAT_BGRA8888:
|
*depth = 32;
|
*bpp = 32;
|
break;
|
default:
|
printf("unsupported pixel format %d\n", format);
|
*depth = 0;
|
*bpp = 0;
|
break;
|
}
|
}
|
|
int drm_format_to_bpp(uint32_t format)
|
{
|
unsigned int depth;
|
int bpp;
|
|
switch(format) {
|
case DRM_FORMAT_NV12:
|
case DRM_FORMAT_NV21:
|
case DRM_FORMAT_YUV420:
|
case DRM_FORMAT_YUV422:
|
case DRM_FORMAT_YUV444:
|
return 8;
|
default:
|
drm_fb_get_bpp_depth(format, &depth, &bpp);
|
return bpp;
|
}
|
}
|
|
int drmGetBuffer(int fd, int width, int height, int format,
|
struct drm_buf *buffer)
|
{
|
struct drm_mode_create_dumb alloc_arg;
|
struct drm_mode_map_dumb mmap_arg;
|
struct drm_mode_destroy_dumb destory_arg;
|
uint32_t handles[4], pitches[4], offsets[4];
|
int bpp, ret;
|
void *map;
|
|
if (fd < 0 || !width || !height) {
|
printf("%s: invalid parameters\n", __func__);
|
return -EINVAL;
|
}
|
|
bpp = drm_format_to_bpp(format);
|
|
memset(&alloc_arg, 0, sizeof(alloc_arg));
|
alloc_arg.bpp = bpp;
|
alloc_arg.width = width;
|
if (format == DRM_FORMAT_NV12)
|
alloc_arg.height = height * 3 / 2;
|
else
|
alloc_arg.height = height;
|
|
ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &alloc_arg);
|
if (ret) {
|
printf("failed to create dumb buffer: %s\n", strerror(errno));
|
return ret;
|
}
|
//HACK of gpu 64 bytes align?
|
if (format == DRM_FORMAT_NV12)
|
alloc_arg.pitch = width;
|
|
memset(&mmap_arg, 0, sizeof(mmap_arg));
|
mmap_arg.handle = alloc_arg.handle;
|
|
ret = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &mmap_arg);
|
if (ret) {
|
printf("failed to create map dumb: %s\n", strerror(errno));
|
ret = -EINVAL;
|
goto destory_dumb;
|
}
|
|
map = mmap(0, alloc_arg.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, mmap_arg.offset);
|
if (map == MAP_FAILED) {
|
printf("failed to mmap buffer: %s\n", strerror(errno));
|
ret = -EINVAL;
|
goto destory_dumb;
|
}
|
|
ret = drmPrimeHandleToFD(fd, alloc_arg.handle, 0, &buffer->dmabuf_fd);
|
if (ret) {
|
printf("failed to get dmabuf fd: %s\n", strerror(errno));
|
munmap(map, alloc_arg.size);
|
ret = -EINVAL;
|
goto destory_dumb;
|
}
|
|
handles[0] = alloc_arg.handle;
|
pitches[0] = alloc_arg.pitch;
|
offsets[0] = 0;
|
|
if (format == DRM_FORMAT_NV12) {
|
handles[1] = alloc_arg.handle;
|
pitches[1] = pitches[0];
|
offsets[1] = pitches[0] * height;
|
}
|
|
ret = drmModeAddFB2(fd, width, height, format, handles,
|
pitches, offsets, (uint32_t*) &buffer->fb_id, 0);
|
if (ret)
|
printf("failed to create fb_id %d\n", ret);
|
|
buffer->handle = alloc_arg.handle;
|
buffer->pitch = alloc_arg.pitch;
|
buffer->size = alloc_arg.size;
|
buffer->map = map;
|
|
destory_dumb:
|
memset(&destory_arg, 0, sizeof(destory_arg));
|
destory_arg.handle = alloc_arg.handle;
|
drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destory_arg);
|
|
return ret;
|
}
|
|
int drmPutBuffer(int fd, struct drm_buf *buffer)
|
{
|
if (buffer) {
|
drmModeRmFB(fd, buffer->fb_id);
|
close(buffer->dmabuf_fd);
|
return munmap(buffer->map, buffer->size);
|
}
|
|
return -EINVAL;
|
}
|
|
int drmSetDpmsMode(uint32_t dpms_mode, struct drm_dev *dev)
|
{
|
uint32_t connector_id;
|
uint32_t dpms_prop_id;
|
int ret = 0;
|
|
if (dev->drm_fd < 0 || !dev->dpms_prop)
|
return -ENODEV;
|
|
connector_id = dev->connector->connector_id;
|
dpms_prop_id = dev->dpms_prop->prop_id;
|
|
ret = drmModeConnectorSetProperty(dev->drm_fd, connector_id, dpms_prop_id, dpms_mode);
|
if (ret) {
|
printf("Failed to set dpms mode %d ret=%d\n", dpms_mode, ret);
|
return ret;
|
}
|
|
return 0;
|
}
|
|
static int drmGetPlaneType(int fd, drmModePlanePtr p)
|
{
|
drmModeObjectPropertiesPtr props;
|
drmModePropertyPtr prop;
|
int i, type = -1;
|
|
props = drmModeObjectGetProperties(fd, p->plane_id,
|
DRM_MODE_OBJECT_PLANE);
|
if (!props) {
|
printf("failed to found props plane[%d] %s\n",
|
p->plane_id, strerror(errno));
|
return -errno;
|
}
|
|
for (i = 0; i < props->count_props; i++) {
|
prop = drmModeGetProperty(fd, props->props[i]);
|
if (!strcmp(prop->name, "type")) {
|
type = props->prop_values[i];
|
drmModeFreeProperty(prop);
|
break;
|
}
|
drmModeFreeProperty(prop);
|
}
|
|
drmModeFreeObjectProperties(props);
|
return type;
|
}
|
|
static int drmFillPlaneProp(int fd, struct drm_dev_plane *pp)
|
{
|
drmModeObjectPropertiesPtr props;
|
drmModePropertyPtr prop;
|
struct plane_prop *plane_prop = &pp->plane_prop;
|
drmModePlanePtr p = pp->p;
|
int i;
|
|
props = drmModeObjectGetProperties(fd, p->plane_id,
|
DRM_MODE_OBJECT_PLANE);
|
if (!props) {
|
printf("failed to found props plane[%d] %s\n",
|
p->plane_id, strerror(errno));
|
return -errno;
|
}
|
|
pp->zpos_max = INT_MAX;
|
for (i = 0; i < props->count_props; i++) {
|
prop = drmModeGetProperty(fd, props->props[i]);
|
if (!strcmp(prop->name, "CRTC_ID"))
|
plane_prop->crtc_id = prop->prop_id;
|
else if (!strcmp(prop->name, "FB_ID"))
|
plane_prop->fb_id = prop->prop_id;
|
else if (!strcmp(prop->name, "SRC_X"))
|
plane_prop->src_x = prop->prop_id;
|
else if (!strcmp(prop->name, "SRC_Y"))
|
plane_prop->src_y = prop->prop_id;
|
else if (!strcmp(prop->name, "SRC_W"))
|
plane_prop->src_w = prop->prop_id;
|
else if (!strcmp(prop->name, "SRC_H"))
|
plane_prop->src_h = prop->prop_id;
|
else if (!strcmp(prop->name, "CRTC_X"))
|
plane_prop->crtc_x = prop->prop_id;
|
else if (!strcmp(prop->name, "CRTC_Y"))
|
plane_prop->crtc_y = prop->prop_id;
|
else if (!strcmp(prop->name, "CRTC_W"))
|
plane_prop->crtc_w = prop->prop_id;
|
else if (!strcmp(prop->name, "CRTC_H"))
|
plane_prop->crtc_h = prop->prop_id;
|
else if (!strcmp(prop->name, "ZPOS")) {
|
plane_prop->zpos = prop->prop_id;
|
pp->zpos_max = prop->values[1];
|
}
|
|
drmModeFreeProperty(prop);
|
}
|
drmModeFreeObjectProperties(props);
|
|
return 0;
|
}
|
|
static drmModePlanePtr drmGetPlaneByType(int fd, int crtc_index, int type)
|
{
|
drmModePlanePtr plane = NULL;
|
drmModePlaneResPtr plane_res;
|
int i;
|
|
plane_res = drmModeGetPlaneResources(fd);
|
for (i = 0; i < plane_res->count_planes; i++) {
|
drmModePlanePtr p = drmModeGetPlane(fd, plane_res->planes[i]);
|
|
if (!(p->possible_crtcs & (1 << crtc_index))) {
|
drmModeFreePlane(p);
|
continue;
|
}
|
if (drmGetPlaneType(fd, p) == type) {
|
plane = p;
|
break;
|
}
|
drmModeFreePlane(p);
|
}
|
|
drmModeFreePlaneResources(plane_res);
|
|
return plane;
|
}
|
|
drmModeConnectorPtr drmFoundConn(int fd, drmModeResPtr res)
|
{
|
drmModeConnectorPtr connector = NULL;
|
int i;
|
|
for (i = 0; i < res->count_connectors; ++i) {
|
drmModeConnectorPtr c;
|
c = drmModeGetConnector(fd, res->connectors[i]);
|
if (!c)
|
continue;
|
if (c->connector_type == DRM_MODE_CONNECTOR_DSI ||
|
c->connector_type == DRM_MODE_CONNECTOR_eDP ||
|
c->connector_type == DRM_MODE_CONNECTOR_DPI ||
|
c->connector_type == DRM_MODE_CONNECTOR_LVDS) {
|
connector = c;
|
break;
|
}
|
drmModeFreeConnector(c);
|
}
|
|
return connector;
|
}
|
|
static drmModePropertyPtr drmFoundDPMS(int fd, drmModeConnectorPtr connector)
|
{
|
drmModePropertyPtr dpms_prop = NULL;
|
drmModeObjectPropertiesPtr props;
|
int i;
|
|
props = drmModeObjectGetProperties(fd, connector->connector_id,
|
DRM_MODE_OBJECT_CONNECTOR);
|
if (!props) {
|
printf("failed to found props connector[%d] %s\n",
|
connector->connector_id, strerror(errno));
|
goto out;
|
}
|
for (i = 0; i < props->count_props; i++) {
|
drmModePropertyPtr prop;
|
prop = drmModeGetProperty(fd, props->props[i]);
|
if (!strcmp(prop->name, "DPMS")) {
|
dpms_prop = prop;
|
goto out;
|
}
|
drmModeFreeProperty(prop);
|
}
|
|
out:
|
drmModeFreeObjectProperties(props);
|
return dpms_prop;
|
}
|
|
static drmModeCrtcPtr drmFoundCrtc(int fd, drmModeResPtr res,
|
drmModeConnector * conn, int *crtc_index)
|
{
|
int i;
|
int crtc_id;
|
drmModeEncoder *enc;
|
drmModeCrtc *crtc;
|
uint32_t crtcs_for_connector = 0;
|
|
crtc_id = -1;
|
for (i = 0; i < res->count_encoders; i++) {
|
enc = drmModeGetEncoder (fd, res->encoders[i]);
|
if (enc) {
|
if (enc->encoder_id == conn->encoder_id) {
|
crtc_id = enc->crtc_id;
|
drmModeFreeEncoder (enc);
|
break;
|
}
|
drmModeFreeEncoder (enc);
|
}
|
}
|
|
/* If no active crtc was found, pick the first possible crtc */
|
if (crtc_id == -1) {
|
for (i = 0; i < conn->count_encoders; i++) {
|
enc = drmModeGetEncoder (fd, conn->encoders[i]);
|
crtcs_for_connector |= enc->possible_crtcs;
|
drmModeFreeEncoder (enc);
|
}
|
|
if (crtcs_for_connector != 0)
|
crtc_id = res->crtcs[ffs (crtcs_for_connector) - 1];
|
}
|
|
if (crtc_id == -1)
|
return NULL;
|
|
for (i = 0; i < res->count_crtcs; i++) {
|
crtc = drmModeGetCrtc (fd, res->crtcs[i]);
|
if (crtc) {
|
if (crtc_id == crtc->crtc_id) {
|
if (crtc_index)
|
*crtc_index= i;
|
return crtc;
|
}
|
drmModeFreeCrtc (crtc);
|
}
|
}
|
|
return NULL;
|
}
|
|
int drmInit(struct drm_dev *dev)
|
{
|
drmModeResPtr res;
|
drmModeCrtcPtr crtc = NULL;
|
drmModeConnectorPtr connector = NULL;
|
drmModePlanePtr plane = NULL;
|
int fd, ret;
|
int crtc_index;
|
|
fd = drmOpen("rockchip", NULL);
|
if (fd < 0) {
|
printf("failed to open rockchip drm: %s\n",
|
strerror(errno));
|
return fd;
|
}
|
dev->drm_fd = fd;
|
|
ret = drmSetClientCap(fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
|
if (ret) {
|
printf("Failed to set atomic cap %s", strerror(errno));
|
return ret;
|
}
|
|
ret = drmSetClientCap(fd, DRM_CLIENT_CAP_ATOMIC, 1);
|
if (ret) {
|
printf("Failed to set atomic cap %s", strerror(errno));
|
return ret;
|
}
|
|
res = drmModeGetResources(fd);
|
if (!res) {
|
printf("Failed to get resources: %s\n",
|
strerror(errno));
|
return -ENODEV;
|
}
|
|
/* found connector */
|
connector = drmFoundConn(fd, res);
|
if (!connector) {
|
printf("failed to find connector\n");
|
ret = -ENODEV;
|
goto err_conn;
|
}
|
/* found connector dpms prop */
|
dev->dpms_prop = drmFoundDPMS(fd, connector);
|
if (!dev->dpms_prop) {
|
ret = -ENODEV;
|
goto err_dpms;
|
}
|
|
/* found crtc that connect to connector */
|
crtc = drmFoundCrtc(fd, res, connector, &crtc_index);
|
if (!crtc) {
|
printf("failed to find crtc connect to connector \n");
|
ret = -ENODEV;
|
goto err_crtc;
|
}
|
|
plane = drmGetPlaneByType(fd, crtc_index, DRM_PLANE_TYPE_PRIMARY);
|
if (!plane) {
|
printf("failed to found expected plane\n");
|
ret = -ENODEV;
|
goto err_plane_primary;
|
}
|
dev->plane_primary.p = plane;
|
|
plane = drmGetPlaneByType(fd, crtc_index, DRM_PLANE_TYPE_OVERLAY);
|
if (!plane) {
|
printf("failed to found expected plane\n");
|
ret = -ENODEV;
|
goto err_plane_overlay;
|
}
|
dev->plane_overlay.p = plane;
|
|
drmModeFreeResources(res);
|
|
drmFillPlaneProp(fd, &dev->plane_primary);
|
drmFillPlaneProp(fd, &dev->plane_overlay);
|
dev->crtc_index = crtc_index;
|
dev->crtc = crtc;
|
dev->connector = connector;
|
|
if (!drm_plane_set_property(fd, dev->plane_primary.p, "ZPOS", 1)) {
|
printf("%s: set ZPOS property failed!\n", __func__);
|
goto err_plane_overlay;
|
}
|
if (!drm_plane_set_property(fd, dev->plane_overlay.p, "ZPOS", 0)) {
|
printf("%s: set ZPOS property failed!\n", __func__);
|
goto err_plane_overlay;
|
}
|
|
return 0;
|
|
err_plane_overlay:
|
drmModeFreePlane(dev->plane_primary.p);
|
err_plane_primary:
|
drmModeFreeCrtc(crtc);
|
err_crtc:
|
drmModeFreeProperty(dev->dpms_prop);
|
err_dpms:
|
drmModeFreeConnector(connector);
|
err_conn:
|
drmModeFreeResources(res);
|
|
return ret;
|
}
|
|
int drmDeinit(struct drm_dev *dev)
|
{
|
if (dev->plane_primary.p)
|
drmModeFreePlane(dev->plane_primary.p);
|
if (dev->plane_overlay.p)
|
drmModeFreePlane(dev->plane_overlay.p);
|
if (dev->dpms_prop)
|
drmModeFreeProperty(dev->dpms_prop);
|
if (dev->crtc)
|
drmModeFreeCrtc(dev->crtc);
|
if (dev->connector)
|
drmModeFreeConnector(dev->connector);
|
if (dev->drm_fd > 0)
|
drmClose(dev->drm_fd);
|
|
return 0;
|
}
|
|
int drmCommit(struct drm_buf *buffer, int width, int height,
|
int x_off, int y_off, struct drm_dev *dev, int plane_type)
|
{
|
drmModeAtomicReq *req;
|
drmModeCrtcPtr crtc = dev->crtc;
|
drmModePlanePtr plane;
|
struct plane_prop *plane_prop;
|
int plane_zpos;
|
uint32_t flags = 0;
|
int ret;
|
|
if (dev->drm_fd < 0 || !buffer) {
|
printf("%s: invalid parameters\n", __func__);
|
return -EINVAL;
|
}
|
|
if (plane_type == DRM_PLANE_TYPE_PRIMARY) {
|
plane = dev->plane_primary.p;
|
plane_zpos = dev->plane_primary.zpos_max;
|
plane_prop = &dev->plane_primary.plane_prop;
|
} else {
|
plane = dev->plane_overlay.p;
|
plane_zpos = dev->plane_overlay.zpos_max;
|
plane_prop = &dev->plane_overlay.plane_prop;
|
}
|
|
width = width == 0 ? crtc->mode.hdisplay : width;
|
height = height == 0? crtc->mode.vdisplay : height;
|
|
req = drmModeAtomicAlloc();
|
|
#define DRM_ATOMIC_ADD_PROP(object_id, value) \
|
ret = drmModeAtomicAddProperty(req, plane->plane_id, object_id, value); \
|
if (ret < 0) \
|
printf("Failed to add prop[%d] to [%d]", value, object_id);
|
DRM_ATOMIC_ADD_PROP(plane_prop->crtc_id, crtc->crtc_id);
|
DRM_ATOMIC_ADD_PROP(plane_prop->fb_id, buffer->fb_id);
|
DRM_ATOMIC_ADD_PROP(plane_prop->src_x, 0);
|
DRM_ATOMIC_ADD_PROP(plane_prop->src_y, 0);
|
DRM_ATOMIC_ADD_PROP(plane_prop->src_w, width << 16);
|
DRM_ATOMIC_ADD_PROP(plane_prop->src_h, height << 16);
|
DRM_ATOMIC_ADD_PROP(plane_prop->crtc_x, x_off);
|
DRM_ATOMIC_ADD_PROP(plane_prop->crtc_y, y_off);
|
DRM_ATOMIC_ADD_PROP(plane_prop->crtc_w, width);
|
DRM_ATOMIC_ADD_PROP(plane_prop->crtc_h, height);
|
//DRM_ATOMIC_ADD_PROP(plane_prop->zpos, plane_zpos);
|
|
// flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
|
ret = drmModeAtomicCommit(dev->drm_fd, req, flags, NULL);
|
if (ret)
|
printf("atomic: couldn't commit new state: %s\n", strerror(errno));
|
|
drmModeAtomicFree(req);
|
|
return ret;
|
}
|