blob: d766e802daa948af5ba7eb598b543e3409b15ea8 [file] [log] [blame]
/*
* Copyright (c) 2021 Amlogic, Inc. All rights reserved.
*
* This source code is subject to the terms and conditions defined in the
* file 'LICENSE' which is part of this source code package.
*
* Description:
*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#include <linux/string.h>
#include "libdrm_meson_connector.h"
#include "libdrm_meson_property.h"
#include "meson_drm_display.h"
#define DEFAULT_CARD "/dev/dri/card0"
#ifndef XDG_RUNTIME_DIR
#define XDG_RUNTIME_DIR "/run"
#endif
static int meson_drm_setprop(int obj_id, char* prop_name, int prop_value );
static uint32_t _getHDRSupportedList(uint64_t hdrlist, uint64_t dvlist);
struct mesonConnector* get_current_connector(int drmFd);
#define FRAC_RATE_POLICY "/sys/class/amhdmitx/amhdmitx0/frac_rate_policy"
static int _amsysfs_get_sysfs_str(const char *path, char *valstr, int size);
static int _get_frac_rate_policy();
static int meson_drm_setprop(int obj_id, char* prop_name, int prop_value )
{
int ret = -1;
printf(" meson_drm_setprop: obj_id %d, prop_name: %s, prop_value:%d\n",obj_id, prop_name,prop_value);
char* xdgRunDir = getenv("XDG_RUNTIME_DIR");
if (!xdgRunDir)
xdgRunDir = XDG_RUNTIME_DIR;
if (prop_name) {
do {
char cmdBuf[512] = {'\0'};
snprintf(cmdBuf, sizeof(cmdBuf)-1, "export XDG_RUNTIME_DIR=%s;westeros-gl-console set property -s %d:%s:%d | grep \"Response\"",
xdgRunDir, obj_id, prop_name, prop_value);
printf("Executing '%s'\n", cmdBuf);
FILE* fp = popen(cmdBuf, "r");
if (NULL != fp) {
char output[64] = {'\0'};
while (fgets(output, sizeof(output)-1, fp)) {
if (strlen(output) && strstr(output, "[0:")) {
ret = 0;
printf("\n meson_drm_setprop:%s\n",output);
}
}
pclose(fp);
} else {
printf("meson_drm_setprop: popen failed\n");
}
if (ret != 0 ) {
if (strcmp(xdgRunDir, XDG_RUNTIME_DIR) == 0) {
break;
}
xdgRunDir = XDG_RUNTIME_DIR;
}
} while (ret != 0);
}
return ret;
}
static uint32_t _getHDRSupportedList(uint64_t hdrlist, uint64_t dvlist)
{
uint32_t ret = 0;
printf("\n _getHDRSupportedList hdrlist:%llu, dvlist:%llu\n", hdrlist, dvlist);
if (!!(hdrlist & 0x1))
ret = ret | (0x1 << (int)MESON_DRM_HDR10PLUS);
if (!!(dvlist & 0x1A))
ret = ret | (0x1 << (int)MESON_DRM_DOLBYVISION_STD);
if (!!(dvlist & 0xE0))
ret = ret | (0x1 << (int)MESON_DRM_DOLBYVISION_LL);
if (!!(hdrlist & 0x8))
ret = ret | (0x1 << (int)MESON_DRM_HDR10_ST2084);
if (!!(hdrlist & 0x4))
ret = ret | (0x1 << (int)MESON_DRM_HDR10_TRADITIONAL);
if (!!(hdrlist & 0x10))
ret = ret | (0x1 << (int)MESON_DRM_HDR_HLG);
if (!!(hdrlist & 0x2))
ret = ret | (0x1 << (int)MESON_DRM_SDR);
return ret;
}
int meson_drm_setMode(DisplayMode* mode)
{
int ret = -1;
char cmdBuf[512] = {'\0'};
char output[64] = {'\0'};
char modeSet[20] = {'\0'};
char* xdgRunDir = getenv("XDG_RUNTIME_DIR");
if (!xdgRunDir)
xdgRunDir = XDG_RUNTIME_DIR;
if (!mode) {
ret = -1;
} else {
sprintf(modeSet, "%dx%d%c%d", mode->w, mode->h, mode->interlace ? 'i':'p',mode->vrefresh);
do {
snprintf(cmdBuf, sizeof(cmdBuf)-1, "export XDG_RUNTIME_DIR=%s;westeros-gl-console set mode %s | grep \"Response\"",
xdgRunDir, modeSet);
printf("Executing '%s'\n", cmdBuf);
/* FIXME: popen in use */
FILE* fp = popen(cmdBuf, "r");
if (NULL != fp) {
while (fgets(output, sizeof(output)-1, fp)) {
if (strlen(output) && strstr(output, "[0:")) {
ret = 0;
} else {
ret = -1;
}
}
pclose(fp);
} else {
printf(" popen failed\n");
ret = -1;
}
if (ret != 0 ) {
if (strcmp(xdgRunDir, XDG_RUNTIME_DIR) == 0) {
printf("meson_drm_setMode: failed !!\n");
break;
}
xdgRunDir = XDG_RUNTIME_DIR;
}
} while (ret != 0);
}
return ret;
}
int meson_drm_getMode(DisplayMode* modeInfo)
{
int ret = -1;
struct mesonConnector* conn = NULL;
drmModeModeInfo* mode = NULL;
int drmFd = -1;
if (modeInfo == NULL) {
printf("\n %s %d modeInfo == NULL return\n",__FUNCTION__,__LINE__);
return ret;
}
drmFd = meson_drm_open();
conn = mesonConnectorCreate(drmFd, DRM_MODE_CONNECTOR_HDMIA);
if ( conn ) {
mode = mesonConnectorGetCurMode(drmFd, conn);
if (mode) {
modeInfo->w = mode->hdisplay;
modeInfo->h = mode->vdisplay;
modeInfo->vrefresh = mode->vrefresh;
modeInfo->interlace = mode->flags & DRM_MODE_FLAG_INTERLACE;
strcpy(modeInfo->name, mode->name);
free(mode);
mode = NULL;
ret = 0;
} else {
printf("\n %s %d mode get fail \n",__FUNCTION__,__LINE__);
}
} else {
printf("\n %s %d conn create fail \n",__FUNCTION__,__LINE__);
}
if (conn)
mesonConnectorDestroy(drmFd,conn);
if (drmFd >= 0 )
close(drmFd);
return ret;
}
int meson_drm_getRxSurportedModes( DisplayMode** modes, int* modeCount )
{
int ret = -1;
int drmFd = -1;
struct mesonConnector* conn = NULL;
drmFd = meson_drm_open();
conn = mesonConnectorCreate(drmFd, DRM_MODE_CONNECTOR_HDMIA);
if (conn == NULL || drmFd < 0)
{
printf("\n%s %d connector create fail\n",__FUNCTION__,__LINE__);
}
drmModeModeInfo* modeall = NULL;
int count = 0;
int i = 0;
if (0 != mesonConnectorGetModes(conn, drmFd, &modeall, &count))
goto out;
DisplayMode* modestemp = (DisplayMode*)calloc(count, sizeof(DisplayMode));
for (i = 0; i < count; i++)
{
modestemp[i].w = modeall[i].hdisplay;
modestemp[i].h = modeall[i].vdisplay;
modestemp[i].vrefresh = modeall[i].vrefresh;
modestemp[i].interlace = (modeall[i].flags & DRM_MODE_FLAG_INTERLACE) ? true : false;
strcpy(modestemp[i].name, modeall[i].name );
}
*modeCount = count;
*modes = modestemp;
ret = 0;
out:
if (conn)
mesonConnectorDestroy(drmFd,conn);
if (drmFd >= 0 )
close(drmFd);
return ret;
}
int meson_drm_getRxPreferredMode( DisplayMode* mode)
{
int ret = -1;
int i = 0;
int count = 0;
drmModeModeInfo* modes = NULL;
int drmFd = -1;
struct mesonConnector* conn = NULL;
drmFd = meson_drm_open();
conn = mesonConnectorCreate(drmFd, DRM_MODE_CONNECTOR_HDMIA);
if (conn == NULL || drmFd < 0)
{
printf("\n%s %d connector create fail\n",__FUNCTION__,__LINE__);
}
if (0 != mesonConnectorGetModes(conn, drmFd, &modes, &count))
goto out;
for (i = 0; i < count; i++)
{
if (modes[i].type & DRM_MODE_TYPE_PREFERRED)
{
mode->w = modes[i].hdisplay;
mode->h = modes[i].vdisplay;
mode->interlace = (modes[i].flags & DRM_MODE_FLAG_INTERLACE) ? true : false;
strcpy(mode->name, modes[i].name );
break;
}
}
ret = 0;
out:
if (conn)
mesonConnectorDestroy(drmFd,conn);
if (drmFd >= 0 )
close(drmFd);
return ret;
}
int meson_drm_getEDID( int * data_Len, char **data)
{
int ret = -1;
int i = 0;
int count = 0;
char* edid_data = NULL;
int drmFd = -1;
struct mesonConnector* conn = NULL;
drmFd = meson_drm_open();
conn = mesonConnectorCreate(drmFd, DRM_MODE_CONNECTOR_HDMIA);
if (conn == NULL || drmFd < 0)
{
printf("\n%s %d connector create fail\n",__FUNCTION__,__LINE__);
}
if (0 != mesonConnectorGetEdidBlob(conn, &count, &edid_data))
goto out;
char* edid = (char*)calloc(count, sizeof(char));
for (i = 0; i < count; i++)
{
edid[i] = edid_data[i];
}
*data_Len = count;
*data = edid;
ret = 0;
out:
if (conn)
mesonConnectorDestroy(drmFd,conn);
if (drmFd >= 0 )
close(drmFd);
return ret;
}
int meson_drm_getRxSurportedEOTF(ENUM_DRM_HDMITX_PROP_EOTF* EOTFs)
{
//get from EDID
return 0;
}
ENUM_MESON_DRM_CONNECTION meson_drm_getConnection()
{
ENUM_MESON_DRM_CONNECTION ret = MESON_DRM_UNKNOWNCONNECTION;
int drmFd = -1;
struct mesonConnector* conn = NULL;
drmFd = meson_drm_open();
conn = mesonConnectorCreate(drmFd, DRM_MODE_CONNECTOR_HDMIA);
if (conn == NULL || drmFd < 0)
{
printf("\n%s %d connector create fail\n",__FUNCTION__,__LINE__);
}
if (conn) {
int ConnectState = -1;
ConnectState = mesonConnectorGetConnectState(conn);
if (ConnectState == 1) {
ret = MESON_DRM_CONNECTED;
} else if (ConnectState == 2) {
ret = MESON_DRM_DISCONNECTED;
} else {
ret = MESON_DRM_UNKNOWNCONNECTION;
}
} else {
printf("\n drm open fail\n");
}
if (conn)
mesonConnectorDestroy(drmFd,conn);
if (drmFd >= 0 )
close(drmFd);
return ret;
}
int meson_drm_set_prop( ENUM_MESON_DRM_PROP enProp, int prop_value )
{
int ret = -1;
int objID = -1;
char propName[50] = {'\0'};
bool force1_4 = false;
if (enProp >= ENUM_DRM_PROP_MAX) {
printf("\n%s %d invalid para\n",__FUNCTION__,__LINE__);
goto out;
}
int drmFd = -1;
struct mesonConnector* conn = NULL;
drmFd = meson_drm_open();
conn = mesonConnectorCreate(drmFd, DRM_MODE_CONNECTOR_HDMIA);
if (conn == NULL || drmFd < 0)
{
printf("\n%s %d connector create fail\n",__FUNCTION__,__LINE__);
goto out;
}
switch (enProp)
{
case ENUM_DRM_PROP_HDMI_ENABLE:
{
objID = mesonConnectorGetId(conn);
sprintf( propName, "%s", MESON_DRM_HDMITX_PROP_AVMUTE);
prop_value = prop_value ? 0:1;
break;
}
case ENUM_DRM_PROP_HDMITX_EOTF:
{
objID = mesonConnectorGetCRTCId(conn);
sprintf( propName, "%s", MESON_DRM_HDMITX_PROP_EOTF);
break;
}
case ENUM_DRM_PROP_CONTENT_PROTECTION:
{
objID = mesonConnectorGetId(conn);
sprintf( propName, "%s", DRM_CONNECTOR_PROP_CONTENT_PROTECTION);
break;
}
case ENUM_DRM_PROP_HDCP_VERSION:
{
objID = mesonConnectorGetId(conn);
sprintf( propName, "%s", DRM_CONNECTOR_PROP_CONTENT_TYPE);
if (ENUM_HDCP_VERSION_FORCE_1_4 == prop_value)
{
prop_value = 0;
force1_4 = true;
}
break;
}
case ENUM_DRM_PROP_HDR_POLICY:
{
objID = mesonConnectorGetCRTCId(conn);
sprintf( propName, "%s", DRM_CONNECTOR_PROP_TX_HDR_POLICY);
break;
}
case ENUM_DRM_PROP_HDMI_ASPECT_RATIO:
{
objID = mesonConnectorGetId(conn);
sprintf( propName, "%s", DRM_CONNECTOR_PROP_TX_ASPECT_RATIO);
break;
}
case ENUM_DRM_PROP_HDMI_DV_ENABLE:
{
objID = mesonConnectorGetCRTCId(conn);
sprintf( propName, "%s", DRM_CONNECTOR_PROP_DV_ENABLE);
}
default:
break;
}
meson_drm_setprop(objID, propName, prop_value);
if (enProp == ENUM_DRM_PROP_HDCP_VERSION)
{
objID = mesonConnectorGetId(conn);
sprintf( propName, "%s", DRM_CONNECTOR_PROP_HDCP_PRIORITY);
int priority = 0;
if (force1_4)
{
priority = 1;
}
meson_drm_setprop(objID, propName, priority);
}
ret = 0;
out:
if (conn)
mesonConnectorDestroy(drmFd,conn);
if (drmFd >= 0 )
close(drmFd);
return ret;
}
int meson_drm_get_prop( ENUM_MESON_DRM_PROP enProp, uint32_t* prop_value )
{
int ret = -1;
int objID = -1;
int objtype = -1;
char propName[50] = {'\0'};
if (!prop_value || enProp >= ENUM_DRM_PROP_MAX) {
printf("\n%s %d invalid para\n",__FUNCTION__,__LINE__);
goto out;
}
int drmFd = -1;
struct mesonConnector* conn = NULL;
drmFd = meson_drm_open();
conn = mesonConnectorCreate(drmFd, DRM_MODE_CONNECTOR_HDMIA);
if (conn == NULL || drmFd < 0)
{
printf("\n%s %d connector create fail\n",__FUNCTION__,__LINE__);
goto out;
}
switch (enProp)
{
case ENUM_DRM_PROP_HDMI_ENABLE:
{
objID = mesonConnectorGetId(conn);
objtype = DRM_MODE_OBJECT_CONNECTOR;
sprintf( propName, "%s", MESON_DRM_HDMITX_PROP_AVMUTE);
break;
}
case ENUM_DRM_PROP_HDMITX_EOTF:
{
objID = mesonConnectorGetCRTCId(conn);
objtype = DRM_MODE_OBJECT_CRTC;
sprintf( propName, "%s", MESON_DRM_HDMITX_PROP_EOTF);
break;
}
case ENUM_DRM_PROP_CONTENT_PROTECTION:
{
objID = mesonConnectorGetId(conn);
objtype = DRM_MODE_OBJECT_CONNECTOR;
sprintf( propName, "%s", DRM_CONNECTOR_PROP_CONTENT_PROTECTION);
break;
}
case ENUM_DRM_PROP_HDCP_VERSION:
{
objID = mesonConnectorGetId(conn);
objtype = DRM_MODE_OBJECT_CONNECTOR;
sprintf( propName, "%s", DRM_CONNECTOR_PROP_CONTENT_TYPE);
break;
}
case ENUM_DRM_PROP_HDR_POLICY:
{
objID = mesonConnectorGetCRTCId(conn);
sprintf( propName, "%s", DRM_CONNECTOR_PROP_TX_HDR_POLICY);
objtype = DRM_MODE_OBJECT_CRTC;
break;
}
case ENUM_DRM_PROP_GETRX_HDCP_SUPPORTED_VERS:
{
objID = mesonConnectorGetId(conn);
objtype = DRM_MODE_OBJECT_CONNECTOR;
sprintf( propName, "%s", DRM_CONNECTOR_PROP_RX_HDCP_SUPPORTED_VER);
break;
}
case ENUM_DRM_PROP_GETRX_HDR_CAP:
{
objID = mesonConnectorGetId(conn);
objtype = DRM_MODE_OBJECT_CONNECTOR;
sprintf( propName, "%s", DRM_CONNECTOR_PROP_RX_HDR_CAP);
break;
}
case ENUM_DRM_PROP_GETTX_HDR_MODE:
{
objID = mesonConnectorGetId(conn);
objtype = DRM_MODE_OBJECT_CONNECTOR;
sprintf( propName, "%s", DRM_CONNECTOR_PROP_TX_HDR_MODE);
break;
}
case ENUM_DRM_PROP_HDMI_ASPECT_RATIO:
{
objID = mesonConnectorGetId(conn);
objtype = DRM_MODE_OBJECT_CONNECTOR;
sprintf( propName, "%s", DRM_CONNECTOR_PROP_TX_ASPECT_RATIO);
break;
}
case ENUM_DRM_PROP_HDMI_DV_ENABLE:
{
objID = mesonConnectorGetCRTCId(conn);
objtype = DRM_MODE_OBJECT_CRTC;
sprintf( propName, "%s", DRM_CONNECTOR_PROP_DV_ENABLE);
break;
}
case ENUM_DRM_PROP_GETRX_HDCP_AUTHMODE:
{
objID = mesonConnectorGetId(conn);
objtype = DRM_MODE_OBJECT_CONNECTOR;
sprintf( propName, "%s", DRM_CONNECTOR_PROP_TX_HDCP_AUTH_MODE);
break;
}
default:
break;
}
struct mesonProperty* meson_prop = NULL;
meson_prop = mesonPropertyCreate(drmFd, objID, objtype, propName);
if (!meson_prop) {
printf("\n meson_prop create fail\n");
goto out;
}
uint64_t value = mesonPropertyGetValue(meson_prop);
printf("\n prop value:%llu objID:%d,name:%s\n",value, objID,propName);
if (enProp == ENUM_DRM_PROP_HDMI_ENABLE)
value = value ? 0:1;
*prop_value = (uint32_t)value;
if (enProp == ENUM_DRM_PROP_GETRX_HDR_CAP)
{
objID = mesonConnectorGetId(conn);
objtype = DRM_MODE_OBJECT_CONNECTOR;
sprintf( propName, "%s", DRM_CONNECTOR_PROP_RX_DV_CAP);
struct mesonProperty* meson_prop_dv = NULL;
meson_prop_dv = mesonPropertyCreate(drmFd, objID, objtype, propName);
uint64_t value_2 = mesonPropertyGetValue(meson_prop_dv);
mesonPropertyDestroy(meson_prop_dv);
*prop_value = _getHDRSupportedList(value, value_2);
}
if (enProp == ENUM_DRM_PROP_GETRX_HDCP_SUPPORTED_VERS)
{
*prop_value = 0;
if (value == 14)
*prop_value = 0x1 << 0;
if (value == 22)
*prop_value = 0x1 << 1;
if (value == 36)
*prop_value = 0x1 | (0x1 << 1);
}
if (enProp == ENUM_DRM_PROP_HDCP_VERSION)
{
if (*prop_value == 0)
{
objID = mesonConnectorGetId(conn);
objtype = DRM_MODE_OBJECT_CONNECTOR;
sprintf( propName, "%s", DRM_CONNECTOR_PROP_HDCP_PRIORITY);
struct mesonProperty* meson_prop_HDCP = NULL;
meson_prop_HDCP = mesonPropertyCreate(drmFd, objID, objtype, propName);
uint64_t value_3 = mesonPropertyGetValue(meson_prop_HDCP);
mesonPropertyDestroy(meson_prop_HDCP);
printf("\n prop value:%llu objID:%d,name:%s\n",value_3, objID,propName);
if (value_3 == 1)
*prop_value = 2;
}
}
mesonPropertyDestroy(meson_prop);
ret = 0;
out:
if (conn)
mesonConnectorDestroy(drmFd,conn);
if (drmFd >= 0 )
close(drmFd);
return ret;
}
int meson_drm_open()
{
int ret_fd = -1;
const char *card;
card= getenv("WESTEROS_DRM_CARD");
if ( !card ) {
card = DEFAULT_CARD;
}
ret_fd = open(card, O_RDONLY|O_CLOEXEC);
if ( ret_fd < 0 )
printf("\n drm card:%s open fail\n",card);
else
drmDropMaster(ret_fd);
return ret_fd;
}
struct mesonConnector* get_current_connector(int drmFd)
{
struct mesonConnector* connectorHDMI = NULL;
struct mesonConnector* connectorLVDS = NULL;
struct mesonConnector* connectorCVBS = NULL;
int HDMIconnected = 0;
int TVConnected = 0;
connectorHDMI = mesonConnectorCreate(drmFd, DRM_MODE_CONNECTOR_HDMIA);
if (connectorHDMI)
HDMIconnected = mesonConnectorGetConnectState(connectorHDMI);
if (HDMIconnected == 1) {
return connectorHDMI;
} else {
connectorLVDS = mesonConnectorCreate(drmFd, DRM_MODE_CONNECTOR_LVDS);
if (connectorLVDS)
TVConnected = mesonConnectorGetConnectState(connectorLVDS);
if (TVConnected == 1) {
return connectorLVDS;
} else {
connectorCVBS = mesonConnectorCreate(drmFd, DRM_MODE_CONNECTOR_TV);
return connectorCVBS;
}
}
}
void meson_drm_close_fd(int drmFd)
{
if (drmFd >= 0)
close(drmFd);
}
static int _amsysfs_get_sysfs_str(const char *path, char *valstr, int size)
{
int fd;
fd = open(path, O_RDONLY);
if (fd >= 0) {
memset(valstr,0,size);
read(fd, valstr, size - 1);
valstr[strlen(valstr)] = '\0';
close(fd);
} else {
sprintf(valstr, "%s", "fail");
return -1;
};
return 0;
}
static int _get_frac_rate_policy()
{
int ret = 0;
char buffer[10] = {'\0'};
if (0 == _amsysfs_get_sysfs_str(FRAC_RATE_POLICY, buffer, sizeof(buffer)-1) ) {
if (strstr(buffer, "1"))
ret = 1;
}
return ret;
}
int meson_drm_get_vblank_time(int drmFd, int nextVsync,uint64_t *vblankTime, uint64_t *refreshInterval)
{
int ret = -1;
int rc = -1;
struct mesonConnector* connector = NULL;
drmModeModeInfo* mode = NULL;
if (drmFd < 0) {
printf("\n drmFd error\n");
goto out;
}
if (nextVsync < 0)
nextVsync = 0;
connector = get_current_connector(drmFd);
if (connector != NULL ) {
mode = mesonConnectorGetCurMode(drmFd, connector);
if (mode) {
*refreshInterval = (1000000LL+(mode->vrefresh/2)) / mode->vrefresh;
if ( ( mode->vrefresh == 60 || mode->vrefresh == 30 || mode->vrefresh == 24
|| mode->vrefresh == 120 || mode->vrefresh == 240 )
&& _get_frac_rate_policy() == 1 ) {
*refreshInterval = (1000000LL+(mode->vrefresh/2)) * 1001 / mode->vrefresh / 1000;
}
free(mode);
}
}
drmVBlank vbl;
vbl.request.type= DRM_VBLANK_RELATIVE;
vbl.request.sequence= nextVsync;
vbl.request.signal= 0;
rc = drmWaitVBlank(drmFd, &vbl );
if (rc != 0 ) {
printf("drmWaitVBlank failed: rc %d errno %d",rc, errno);
ret = -1;
goto out;
}
if ((rc == 0) && (vbl.reply.tval_sec > 0 || vbl.reply.tval_usec > 0)) {
*vblankTime = vbl.reply.tval_sec * 1000000LL + vbl.reply.tval_usec;
}
ret = 0;
out:
mesonConnectorDestroy(drmFd, connector);
return ret;
}