weston: add QMS flow [1/1]

PD#SWPL-172026

Problem:
add QMS flow

Solution:
add QMS flow

Verify:
s7d

Change-Id: I05de5006973b20228908134b62962c6d2fbbfb12
Signed-off-by: limin.tian <limin.tian@amlogic.com>
diff --git a/libweston/modepolicy/DisplayAdapter.cpp b/libweston/modepolicy/DisplayAdapter.cpp
index d0fedc3..03fb3b8 100644
--- a/libweston/modepolicy/DisplayAdapter.cpp
+++ b/libweston/modepolicy/DisplayAdapter.cpp
@@ -121,6 +121,9 @@
     DA_DEFINE(FORCE_HDR_MODE, "0", update_sys_node_by_file);
     DA_DEFINE(EDID_ATTR, "0", update_sys_node_by_property);
     DA_DEFINE(FRAC_RATE_POLICY, "0", update_sys_node_by_property);
+    DA_DEFINE(VRR_SUPPORTED, "0", update_sys_node_by_property);
+    DA_DEFINE(VRR_ENABLED, "0", update_sys_node_by_property);
+    DA_DEFINE(BRR_UPDATE, "0", update_sys_node_by_property);
 
 #define DA_SET_NODE(ID, NODE) \
         display_attrs[DA_##ID].sysfs_node = NODE
@@ -154,6 +157,9 @@
     DA_SET_NODE(FORCE_HDR_MODE, "/sys/module/aml_media/parameters/force_output");
     DA_SET_NODE(EDID_ATTR, "EDID");
     DA_SET_NODE(FRAC_RATE_POLICY, "FRAC_RATE_POLICY");
+    DA_SET_NODE(VRR_SUPPORTED, "vrr_capable");
+    DA_SET_NODE(VRR_ENABLED, "VRR_ENABLED");
+    DA_SET_NODE(BRR_UPDATE, "brr_update");
 
 #define DA_SET_READ_ONLY(ID) \
         display_attrs[DA_##ID].is_read_only = true
@@ -162,6 +168,7 @@
     DA_SET_READ_ONLY(AMDV_STATUS);
     DA_SET_READ_ONLY(HDR_CAP);
     DA_SET_READ_ONLY(HDR_CAP2);
+    DA_SET_READ_ONLY(VRR_SUPPORTED);
 #define DA_SET_WRITE_ONLY(ID) \
         display_attrs[DA_##ID].is_write_only= true
     // DA_SET_WRITE_ONLY(AMDV_MODE);
@@ -176,11 +183,14 @@
         DA_SET_OBJ_ID(EDID_ATTR, conn->connector_id);
         DA_SET_OBJ_ID(FRAC_RATE_POLICY, conn->connector_id);
         DA_SET_OBJ_ID(HDMI_COLOR_ATTR, conn->connector_id);
+        DA_SET_OBJ_ID(VRR_SUPPORTED, conn->connector_id);
     }
     if (crtc) {
         DA_SET_OBJ_ID(AMDV_MODE, crtc->crtc_id);
         DA_SET_OBJ_ID(HDR_POLICY, crtc->crtc_id);
         DA_SET_OBJ_ID(AMDV_ENABLE, crtc->crtc_id);
+        DA_SET_OBJ_ID(VRR_ENABLED, crtc->crtc_id);
+        DA_SET_OBJ_ID(BRR_UPDATE, crtc->crtc_id);
     }
 }
 
diff --git a/libweston/modepolicy/DisplayAdapter.h b/libweston/modepolicy/DisplayAdapter.h
index 6eb4699..578e775 100644
--- a/libweston/modepolicy/DisplayAdapter.h
+++ b/libweston/modepolicy/DisplayAdapter.h
@@ -80,6 +80,11 @@
 #define DISPLAY_FORCE_HDR_MODE                  "FORCE HDR Mode"
 #define DISPLAY_EDID_ATTR                       "HDMI EDID"
 #define DISPLAY_FRAC_RATE_POLICY                "FRAC RATE POLICY"
+#define DISPLAY_VRR_SUPPORTED                   "VRR_SUPPORTED"
+#define DISPLAY_VRR_ENABLED                     "VRR_ENABLED"
+#define DISPLAY_BRR_UPDATE                      "BRR_UPDATE"
+
+
 
 #define MAX_BUF_LEN MESON_MAX_STR_LEN
 
@@ -104,6 +109,9 @@
     DA_FORCE_HDR_MODE,
     DA_EDID_ATTR,
     DA_FRAC_RATE_POLICY,
+    DA_VRR_SUPPORTED,
+    DA_VRR_ENABLED,
+    DA_BRR_UPDATE,
     DA_DISPLAY_ATTRIBUTE__COUNT
 };
 
diff --git a/libweston/modepolicy/ModePolicy.cpp b/libweston/modepolicy/ModePolicy.cpp
index da65cca..5ef7fed 100644
--- a/libweston/modepolicy/ModePolicy.cpp
+++ b/libweston/modepolicy/ModePolicy.cpp
@@ -12,6 +12,8 @@
 #include <inttypes.h>
 #include <sys/utsname.h>
 #include <memory>
+#include <sys/ioctl.h>
+
 //#include <systemcontrol.h>
 static int g_activeLevel= 3;
 
@@ -96,6 +98,7 @@
     mDisplayHeight = 0;
     mFracMode = MODE_FRACTION;
     mEnvLock = PTHREAD_MUTEX_INITIALIZER;
+    mSeamlessSwitchEnabled = true;
 }
 
 ModePolicy::ModePolicy(std::shared_ptr<DisplayAdapter> adapter, const uint32_t displayId) {
@@ -114,6 +117,7 @@
     mDisplayHeight = 0;
     mFracMode = MODE_FRACTION;
     mEnvLock = PTHREAD_MUTEX_INITIALIZER;
+    mSeamlessSwitchEnabled = true;
 }
 
 ModePolicy::~ModePolicy() {
@@ -1126,6 +1130,7 @@
             mModeConType = MESON_MODE_HDMI;
             break;
     }
+
     return 0;
 }
 
@@ -2427,6 +2432,8 @@
 
         //set hdmi mode
         setDisplayMode(final_displaymode);
+        setQMSVRR( curDisplayMode, final_displaymode );
+
         /* phy already turned on after write display/mode node */
         phy_enabled_already     = true;
     } else {
@@ -2931,6 +2938,9 @@
         conPtr->modes_capacity = conPtr->modes_size;
     }
 
+    loadVrrModeGroups();
+    groupDisplayModes();
+
     int i = 0;
     for (auto it = mModes.begin(); it != mModes.end(); it++) {
         drmMode2MesonMode(conPtr->modes[i], it->second);
@@ -2978,6 +2988,222 @@
     return ret;
 }
 
+bool ModePolicy::isSeamlessMode(const drm_mode_info_t & mode, const drm_mode_info_t &groupMode)
+{
+    if (mHdrCapabilities.AMDV_4K30_Supported == 1) {
+        if (mode.pixelW == FB_SIZE_4K_W && mode.pixelH == FB_SIZE_4K_H) {
+            auto refreshA = ceilf(mode.refreshRate);
+            auto refreshB = ceilf(groupMode.refreshRate);
+            if ((refreshA > REFRESH_RATE_30 && refreshB <= REFRESH_RATE_30) ||
+                    (refreshA <= REFRESH_RATE_30 && refreshB > REFRESH_RATE_30)) {
+                return false;
+            }
+        }
+    }
+
+    for (int32_t i = 0; i < mVrrModeGroup.num; i++) {
+        if (mVrrModeGroup.groups[i].width == mode.pixelW
+                && mVrrModeGroup.groups[i].height == mode.pixelH) {
+            if (((mode.refreshRate - mVrrModeGroup.groups[i].vrr_min) >= 0
+                    //frac refresh rate
+                    || std::abs(mode.refreshRate - (mVrrModeGroup.groups[i].vrr_min * 1000) / (float)1001) < 0.001)
+                        && (mode.refreshRate - mVrrModeGroup.groups[i].vrr_max <= 0)) {
+                if (mVrrModeGroup.groups[i].width == groupMode.pixelW
+                        && mVrrModeGroup.groups[i].height == groupMode.pixelH) {
+                    if (((groupMode.refreshRate - mVrrModeGroup.groups[i].vrr_min) >= 0
+                        //frac refresh rate
+                        || std::abs(groupMode.refreshRate - (mVrrModeGroup.groups[i].vrr_min * 1000) / (float)1001) < 0.001)
+                            && (groupMode.refreshRate - mVrrModeGroup.groups[i].vrr_max <= 0)) {
+                        return true;
+                    } else {
+                        return false;
+                    }
+                }
+            }
+        }
+    }
+    return false;
+}
+
+bool ModePolicy::supportVrr()
+{
+    std::string vrr_cap;
+    getDisplayAttribute(DISPLAY_VRR_SUPPORTED, vrr_cap);
+    if (atoi(vrr_cap.c_str()) == 1)
+        return true;
+    return false;
+}
+
+int32_t  ModePolicy::loadVrrModeGroups()
+{
+    if (!(mDisplayType == DISPLAY_TYPE_TV)) {
+        if (!(supportVrr()) || !mSeamlessSwitchEnabled) {
+            return 0;
+        }
+    }
+
+    memset(&mVrrModeGroup, 0, sizeof(mVrrModeGroup));
+    mVrrModeGroup.conn_id = mConnector->connector_id;
+    auto ret = ioctl(mDrmFd, DRM_IOCTL_MESON_GET_VRR_RANGE, &mVrrModeGroup);
+
+    MESON_LOGD("\n %s %d conn_id:%d mDrmFd:%d mVrrModeGroup.num:%d\n",__FUNCTION__,__LINE__, mVrrModeGroup.conn_id, mDrmFd, mVrrModeGroup.num);
+    if (ret) {
+        MESON_LOGE("DRM_IOCTL_MESON_GET_VRR_RANGE error ret %d  %s(%d)", ret, strerror(errno), errno);
+        return -EINVAL;
+    }
+
+    return 0;
+}
+
+bool ModePolicy::isVrrGroupedMode(const drm_mode_info_t & mode)
+{
+    bool ret = true;
+    int count = 0;
+    drm_mode_info_t mesonMode = {
+        .name              = "INVALID_MODE",
+        .dpiX              = 0,
+        .dpiY              = 0,
+        .pixelW            = 0,
+        .pixelH            = 0,
+        .refreshRate       = 0,
+        .groupId           = 0,
+    };
+    for (auto it = mModes.begin(); it != mModes.end(); ++it) {
+        MESON_LOGD("\n isVrrGroupedMode:mode name: %s mModes name:%s\n", mode.name, it->second.name);
+        if (strncmp(it->second.name, mode.name, DRM_DISPLAY_MODE_LEN) == 0) {
+            mesonMode = it->second;
+            break;
+        }
+    }
+    for (auto it = mModes.begin(); it != mModes.end(); ++it) {
+        MESON_LOGD("\n isVrrGroupedMode:mode(%d %d) mModes(%d %d) group id:(%d %d)\n",
+            mesonMode.pixelW, mesonMode.pixelH, it->second.pixelW, it->second.pixelH, mesonMode.groupId ,it->second.groupId);
+        if (it->second.pixelW == mesonMode.pixelW && it->second.pixelH == mesonMode.pixelH
+            && mesonMode.groupId == it->second.groupId) {
+            count++;
+        }
+    }
+    if (count <= 1)
+        ret = false;
+    return ret;
+}
+
+int32_t ModePolicy::getDrmModeInfoFromName(char* name , drm_mode_info_t* drmMode)
+{
+    int ret = -1;
+    if (!name || !drmMode) {
+        MESON_LOGE("\n invalid parameter\n");
+        return ret;
+    }
+    for (auto & mode : mModes) {
+         auto & itMode = mode.second;
+         if ( strcmp (itMode.name, name) ==0 ) {
+             *drmMode = itMode;
+              ret = 0;
+              break;
+         }
+    }
+    return ret;
+}
+
+int32_t ModePolicy::groupDisplayModes()
+{
+    /* no need to regenerate groupId if without QMS/VRR support */
+    if ( mDisplayType != DISPLAY_TYPE_TV) {
+        if (!(supportVrr()) || !mSeamlessSwitchEnabled) {
+            return 0;
+        }
+    }
+    /* clear the old group modes */
+    mMesonGroupModes.clear();
+
+    for (auto & mode : mModes) {
+        auto & itMode = mode.second;
+        bool needRegroup = true;
+        for (auto & groupModes : mMesonGroupModes) {
+            int groupId = groupModes.first;
+            auto& itGroupModes = groupModes.second;
+
+            // do not check interlace mode,
+            // interlace mode does not support vrr
+            if (strstr(itMode.name, "i") != NULL)
+                break;
+
+            // only support resolution >= 720P
+            if (itMode.pixelW < 1280 || itMode.pixelH < 720)
+                break;
+
+            if (!itGroupModes.empty()) {
+                /* only need check the first item*/
+                drm_mode_info_t *gmodePtr = itGroupModes[0];
+                if (gmodePtr->pixelW == itMode.pixelW && gmodePtr->pixelH == itMode.pixelH
+                            && isSeamlessMode(itMode, *gmodePtr)) {
+                    itMode.groupId = groupId;
+                    itGroupModes.push_back(&itMode);
+                    needRegroup = false;
+                    break;
+                }
+            }
+        }
+
+        if (needRegroup) {
+            itMode.groupId = mMesonGroupModes.size();
+            auto iter = mMesonGroupModes.emplace(mMesonGroupModes.size(),
+                    std::vector<drm_mode_info_t *> ());
+            auto & modesWithSameGroup = iter.first->second;
+            modesWithSameGroup.push_back(&itMode);
+        }
+    }
+
+    return 0;
+}
+
+void  ModePolicy::updateDrmfd(int drmFd)
+{
+    MESON_LOGD("updateDrmfd:%d\n", drmFd);
+    if (drmFd >= 0)
+        mDrmFd = drmFd;
+}
+
+void ModePolicy::seamlessSwitchEnabled(bool enable)
+{
+    MESON_LOGD("seamlessSwitchEnabled:%d\n", enable);
+    mSeamlessSwitchEnabled = enable;
+}
+
+void ModePolicy::setQMSVRR(char* curDisplayMode, char* final_displaymode )
+{
+    if (!curDisplayMode || !final_displaymode) {
+        MESON_LOGE("\n %s %d invalid parameter\n", __FUNCTION__,__LINE__);
+        return;
+    }
+    drm_mode_info_t curDrmMode;
+    drm_mode_info_t finalDrmMode;
+    bool isSeamless = false;
+    getDrmModeInfoFromName(curDisplayMode, &curDrmMode);
+    getDrmModeInfoFromName(final_displaymode, &finalDrmMode);
+    isSeamless = (curDrmMode.groupId == finalDrmMode.groupId);
+    MESON_LOGD("\n %s %d curDisplayMode:%s, final_displaymode:%s isSeamless:%d\n",
+                __FUNCTION__,__LINE__, curDisplayMode, final_displaymode, isSeamless);
+    int enableVrr = 0;
+    int updateBrr = isSeamless ? 0 : 1;
+    char sEnableVrr[3] = {'\0'};
+    char sUpdateBrr[3] = {'\0'};
+    if (supportVrr()) {
+        bool modeIsVrrMode = true;
+        if (updateBrr) {
+            modeIsVrrMode = isVrrGroupedMode(finalDrmMode);
+        }
+        enableVrr = (mSeamlessSwitchEnabled && modeIsVrrMode) ? 1 : 0;
+        sprintf(sUpdateBrr, "%d",updateBrr);
+        MESON_LOGD("\n %s %d mSeamlessSwitchEnabled:%d, modeIsVrrMode:%d enableVrr:%d, updateBrr:%d\n",
+                __FUNCTION__,__LINE__, mSeamlessSwitchEnabled, modeIsVrrMode, enableVrr, updateBrr);
+        setDisplayAttribute(DISPLAY_BRR_UPDATE, sUpdateBrr);
+    }
+    sprintf(sEnableVrr, "%d",enableVrr);
+    setDisplayAttribute(DISPLAY_VRR_ENABLED, sEnableVrr);
+
+}
 int32_t bindCrtcAndConnector(drmModeCrtc *crtc, drmModeConnector *conn)
 {
     int ret = -1;
@@ -3094,3 +3320,15 @@
         return g_Policy->getModeNameForPix(name, width, height, refresh / 1000, flags);
     return false;
 }
+void updateDrmfd(int fd)
+{
+    MESON_LOGD("\n %s %d fd:%d, g_Policy.get():%p\n",__FUNCTION__,__LINE__,fd, g_Policy.get());
+    if (g_Policy.get())
+       g_Policy->updateDrmfd(fd);
+}
+void seamlessSwitchEnabled(bool enable)
+{
+    if (g_Policy.get())
+       g_Policy->seamlessSwitchEnabled(enable);
+}
+
diff --git a/libweston/modepolicy/ModePolicy.h b/libweston/modepolicy/ModePolicy.h
index d6d73cf..8cc64e5 100644
--- a/libweston/modepolicy/ModePolicy.h
+++ b/libweston/modepolicy/ModePolicy.h
@@ -30,6 +30,7 @@
 #include "modepolicyfunc.h"
 #include <ubootenv.h>
 #include "DisplayAdapter.h"
+#include <linux/amlogic/drm/meson_drm.h>
 
 using namespace std;
 
@@ -195,6 +196,10 @@
 #define FULL_WIDTH_1080                 1920
 #define FULL_HEIGHT_1080                1080
 
+#define FB_SIZE_4K_W 3840
+#define FB_SIZE_4K_H 2160
+#define REFRESH_RATE_30 (30)
+
 static const char *PROFIX_UBOOTENV_VAR = "ubootenv.var.";
 
 #ifndef PROPERTY_VALUE_MAX
@@ -347,6 +352,8 @@
     bool updateEnv();
     bool getModeNameForPix(char *name,
         int32_t width, int32_t height, uint32_t refresh, uint32_t flags);
+    void updateDrmfd(int drmFd);
+    void seamlessSwitchEnabled(bool enable);
 
     // void dump(String8 &dumpstr) override;
 
@@ -454,6 +461,13 @@
     void getSupportedModes();
     bool isModeSupported(drm_mode_info_t mode);
     void setDisplay(output_mode_state state);
+    bool isSeamlessMode(const drm_mode_info_t & mode, const drm_mode_info_t &groupMode);
+    bool supportVrr();
+    int32_t loadVrrModeGroups();
+    bool isVrrGroupedMode(const drm_mode_info_t & mode);
+    int32_t getDrmModeInfoFromName(char* name , drm_mode_info_t* drmMode);
+    int32_t groupDisplayModes();
+    void setQMSVRR(char* curDisplayMode, char* final_displaymode );
 
 protected:
     static void * threadMain(void * data);
@@ -504,4 +518,8 @@
     int32_t mFracMode;
     // std::shared_ptr<HDCPTxAuth> mTxAuth;
     // for hdr control
+    struct drm_vrr_mode_groups mVrrModeGroup;
+    std::map<uint32_t, std::vector<drm_mode_info_t *>> mMesonGroupModes;
+	int mDrmFd;
+	bool mSeamlessSwitchEnabled;
 };
diff --git a/libweston/modepolicy/modepolicy_aml.cpp b/libweston/modepolicy/modepolicy_aml.cpp
index cea57b6..5c1e26a 100644
--- a/libweston/modepolicy/modepolicy_aml.cpp
+++ b/libweston/modepolicy/modepolicy_aml.cpp
@@ -12,6 +12,7 @@
 	"color_space",
 	"color_depth"
 };
+static int weston_set_property(int id, const char *name, int value);
 
 static weston_ctx_list *weston_get_ctx_list()
 {
@@ -763,7 +764,10 @@
 	if (!ctx) {
 		ctx = (weston_ctx *)calloc(1, sizeof(*ctx));
 		ctx->scaling = -1;
+		ctx->enableVrr = true;
 		ctx->state = ctx_list->state;
+		ctx->drm_fd = fd;
+		MESON_LOGD("\n init_mode_policy_without_mode :%d\n", fd);
 		wl_list_init(&ctx->prop_list);
 		wl_list_insert(&ctx_list->ctx_list, &ctx->link);
 	}
@@ -891,6 +895,8 @@
 		return NULL;
 
 	initModePolicyFun(ctx->crtc, ctx->conn, callback);
+	updateDrmfd(ctx->drm_fd);
+	seamlessSwitchEnabled(ctx->enableVrr);
 	if (mode) {
 		if (get_mode_name_for_weston_mode(ctx, mode, name))
 			setActiveConfig(name);
diff --git a/libweston/modepolicy/modepolicy_aml.h b/libweston/modepolicy/modepolicy_aml.h
index cf9bf86..4de2f48 100644
--- a/libweston/modepolicy/modepolicy_aml.h
+++ b/libweston/modepolicy/modepolicy_aml.h
@@ -35,6 +35,7 @@
 	uint32_t state;
 	struct wl_list prop_list;
 	struct wl_list link;
+	bool enableVrr;
 } weston_ctx;
 
 typedef struct _weston_ctx_list {
diff --git a/libweston/modepolicy/modepolicyfunc.h b/libweston/modepolicy/modepolicyfunc.h
index 8701f90..d4749d7 100644
--- a/libweston/modepolicy/modepolicyfunc.h
+++ b/libweston/modepolicy/modepolicyfunc.h
@@ -42,6 +42,8 @@
 bool updateEnv();
 bool getModeNameForPix(char *name,
     int32_t width, int32_t height, uint32_t refresh, uint32_t flags);
+void seamlessSwitchEnabled(bool enable);
+void updateDrmfd(int fd);
 
 #ifdef __cplusplus
 }