drm/amd/display: Implement stats logging
authorAnthony Koo <Anthony.Koo@amd.com>
Thu, 22 Feb 2018 14:50:25 +0000 (09:50 -0500)
committerAlex Deucher <alexander.deucher@amd.com>
Wed, 14 Mar 2018 20:08:47 +0000 (15:08 -0500)
Stats will be used for debug purposes

Signed-off-by: Anthony Koo <Anthony.Koo@amd.com>
Reviewed-by: Tony Cheng <Tony.Cheng@amd.com>
Acked-by: Harry Wentland <harry.wentland@amd.com>
Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
drivers/gpu/drm/amd/display/dc/basics/logger.c
drivers/gpu/drm/amd/display/include/logger_types.h
drivers/gpu/drm/amd/display/modules/freesync/freesync.c
drivers/gpu/drm/amd/display/modules/inc/mod_freesync.h
drivers/gpu/drm/amd/display/modules/inc/mod_stats.h [new file with mode: 0644]
drivers/gpu/drm/amd/display/modules/stats/stats.c [new file with mode: 0644]

index 180a9d69d3511c34443c00d268cb846134d12007..31bee054f43a78d166041a08bae6dcea5d42cd10 100644 (file)
@@ -60,7 +60,8 @@ static const struct dc_log_type_info log_type_info_tbl[] = {
                {LOG_EVENT_LINK_LOSS,       "LinkLoss"},
                {LOG_EVENT_UNDERFLOW,       "Underflow"},
                {LOG_IF_TRACE,              "InterfaceTrace"},
-               {LOG_DTN,                   "DTN"}
+               {LOG_DTN,                   "DTN"},
+               {LOG_PROFILING,             "Profiling"}
 };
 
 
index b727f5eeb3a95dab358aee4f80a08557190f6726..427796bdc14ae663c4494a22b8f734f04f43ad0a 100644 (file)
@@ -98,6 +98,7 @@ enum dc_log_type {
        LOG_EVENT_UNDERFLOW,
        LOG_IF_TRACE,
        LOG_PERF_TRACE,
+       LOG_PROFILING,
 
        LOG_SECTION_TOTAL_COUNT
 };
index b4723af368a51fd451c633997909863904d85636..e849b704f2f60b19932c9a42ad46fc840310bbf2 100644 (file)
@@ -33,7 +33,7 @@
 /* Refresh rate ramp at a fixed rate of 65 Hz/second */
 #define STATIC_SCREEN_RAMP_DELTA_REFRESH_RATE_PER_FRAME ((1000 / 60) * 65)
 /* Number of elements in the render times cache array */
-#define RENDER_TIMES_MAX_COUNT 20
+#define RENDER_TIMES_MAX_COUNT 10
 /* Threshold to exit BTR (to avoid frequent enter-exits at the lower limit) */
 #define BTR_EXIT_MARGIN 2000
 /* Number of consecutive frames to check before entering/exiting fixed refresh*/
@@ -52,7 +52,7 @@ struct gradual_static_ramp {
        unsigned int ramp_current_frame_duration_in_ns;
 };
 
-struct time_cache {
+struct freesync_time {
        /* video (48Hz feature) related */
        unsigned int update_duration_in_ns;
 
@@ -64,6 +64,9 @@ struct time_cache {
 
        unsigned int render_times_index;
        unsigned int render_times[RENDER_TIMES_MAX_COUNT];
+
+       unsigned int min_window;
+       unsigned int max_window;
 };
 
 struct below_the_range {
@@ -98,11 +101,14 @@ struct freesync_state {
        bool static_screen;
        bool video;
 
+       unsigned int vmin;
+       unsigned int vmax;
+
+       struct freesync_time time;
+
        unsigned int nominal_refresh_rate_in_micro_hz;
        bool windowed_fullscreen;
 
-       struct time_cache time;
-
        struct gradual_static_ramp static_ramp;
        struct below_the_range btr;
        struct fixed_refresh fixed_refresh;
@@ -124,9 +130,9 @@ struct freesync_registry_options {
 struct core_freesync {
        struct mod_freesync public;
        struct dc *dc;
+       struct freesync_registry_options opts;
        struct freesync_entity *map;
        int num_entities;
-       struct freesync_registry_options opts;
 };
 
 #define MOD_FREESYNC_TO_CORE(mod_freesync)\
@@ -146,7 +152,7 @@ struct mod_freesync *mod_freesync_create(struct dc *dc)
                goto fail_alloc_context;
 
        core_freesync->map = kzalloc(sizeof(struct freesync_entity) * MOD_FREESYNC_MAX_CONCURRENT_STREAMS,
-                                    GFP_KERNEL);
+                                       GFP_KERNEL);
 
        if (core_freesync->map == NULL)
                goto fail_alloc_map;
@@ -330,6 +336,25 @@ bool mod_freesync_remove_stream(struct mod_freesync *mod_freesync,
        return true;
 }
 
+static void adjust_vmin_vmax(struct core_freesync *core_freesync,
+                               struct dc_stream_state **streams,
+                               int num_streams,
+                               int map_index,
+                               unsigned int v_total_min,
+                               unsigned int v_total_max)
+{
+       if (num_streams == 0 || streams == NULL || num_streams > 1)
+               return;
+
+       core_freesync->map[map_index].state.vmin = v_total_min;
+       core_freesync->map[map_index].state.vmax = v_total_max;
+
+       dc_stream_adjust_vmin_vmax(core_freesync->dc, streams,
+                               num_streams, v_total_min,
+                               v_total_max);
+}
+
+
 static void update_stream_freesync_context(struct core_freesync *core_freesync,
                struct dc_stream_state *stream)
 {
@@ -588,9 +613,10 @@ static bool set_freesync_on_streams(struct core_freesync *core_freesync,
                                update_stream_freesync_context(core_freesync,
                                                streams[stream_idx]);
 
-                               dc_stream_adjust_vmin_vmax(core_freesync->dc, streams,
-                                                          num_streams, v_total_min,
-                                                          v_total_max);
+                               adjust_vmin_vmax(core_freesync, streams,
+                                               num_streams, map_index,
+                                               v_total_min,
+                                               v_total_max);
 
                                return true;
 
@@ -613,9 +639,10 @@ static bool set_freesync_on_streams(struct core_freesync *core_freesync,
                                                core_freesync,
                                                streams[stream_idx]);
 
-                                       dc_stream_adjust_vmin_vmax(
-                                               core_freesync->dc, streams,
-                                               num_streams, v_total_nominal,
+                                       adjust_vmin_vmax(
+                                               core_freesync, streams,
+                                               num_streams, map_index,
+                                               v_total_nominal,
                                                v_total_nominal);
                                }
                                return true;
@@ -632,9 +659,10 @@ static bool set_freesync_on_streams(struct core_freesync *core_freesync,
                                        core_freesync,
                                        streams[stream_idx]);
 
-                               dc_stream_adjust_vmin_vmax(core_freesync->dc, streams,
-                                                          num_streams, v_total_nominal,
-                                                          v_total_nominal);
+                               adjust_vmin_vmax(core_freesync, streams,
+                                               num_streams, map_index,
+                                               v_total_nominal,
+                                               v_total_nominal);
 
                                /* Reset the cached variables */
                                reset_freesync_state_variables(state);
@@ -650,9 +678,10 @@ static bool set_freesync_on_streams(struct core_freesync *core_freesync,
                         * not support freesync because a former stream has
                         * be programmed
                         */
-                       dc_stream_adjust_vmin_vmax(core_freesync->dc, streams,
-                                                  num_streams, v_total_nominal,
-                                                  v_total_nominal);
+                       adjust_vmin_vmax(core_freesync, streams,
+                                               num_streams, map_index,
+                                               v_total_nominal,
+                                               v_total_nominal);
                        /* Reset the cached variables */
                        reset_freesync_state_variables(state);
                }
@@ -769,8 +798,9 @@ void mod_freesync_handle_v_update(struct mod_freesync *mod_freesync,
                        vmin = inserted_frame_v_total;
 
                        /* Program V_TOTAL */
-                       dc_stream_adjust_vmin_vmax(core_freesync->dc, streams,
-                                                  num_streams, vmin, vmax);
+                       adjust_vmin_vmax(core_freesync, streams,
+                                               num_streams, index,
+                                               vmin, vmax);
                }
 
                if (state->btr.frame_counter > 0)
@@ -804,9 +834,10 @@ void mod_freesync_handle_v_update(struct mod_freesync *mod_freesync,
                update_stream_freesync_context(core_freesync, streams[0]);
 
                /* Program static screen ramp values */
-               dc_stream_adjust_vmin_vmax(core_freesync->dc, streams,
-                                          num_streams, v_total,
-                                          v_total);
+               adjust_vmin_vmax(core_freesync, streams,
+                                       num_streams, index,
+                                       v_total,
+                                       v_total);
 
                triggers.overlay_update = true;
                triggers.surface_update = true;
@@ -1063,9 +1094,9 @@ bool mod_freesync_override_min_max(struct mod_freesync *mod_freesync,
                                max_refresh);
 
                /* Program vtotal min/max */
-               dc_stream_adjust_vmin_vmax(core_freesync->dc, &streams, 1,
-                                          state->freesync_range.vmin,
-                                          state->freesync_range.vmax);
+               adjust_vmin_vmax(core_freesync, &streams, 1, index,
+                               state->freesync_range.vmin,
+                               state->freesync_range.vmax);
        }
 
        if (min_refresh != 0 &&
@@ -1399,11 +1430,9 @@ static void apply_fixed_refresh(struct core_freesync *core_freesync,
        } else {
 
                vmin = state->freesync_range.vmin;
-
                vmax = vmin;
-
-               dc_stream_adjust_vmin_vmax(core_freesync->dc, &stream,
-                                          1, vmin, vmax);
+               adjust_vmin_vmax(core_freesync, &stream, map_index,
+                                       1, vmin, vmax);
        }
 }
 
@@ -1457,3 +1486,43 @@ void mod_freesync_pre_update_plane_addresses(struct mod_freesync *mod_freesync,
 
        }
 }
+
+void mod_freesync_get_settings(struct mod_freesync *mod_freesync,
+               struct dc_stream_state **streams, int num_streams,
+               unsigned int *v_total_min, unsigned int *v_total_max,
+               unsigned int *event_triggers,
+               unsigned int *window_min, unsigned int *window_max,
+               unsigned int *lfc_mid_point_in_us,
+               unsigned int *inserted_frames,
+               unsigned int *inserted_duration_in_us)
+{
+       unsigned int stream_index, map_index;
+       struct core_freesync *core_freesync = NULL;
+
+       if (mod_freesync == NULL)
+               return;
+
+       core_freesync = MOD_FREESYNC_TO_CORE(mod_freesync);
+
+       for (stream_index = 0; stream_index < num_streams; stream_index++) {
+
+               map_index = map_index_from_stream(core_freesync,
+                                               streams[stream_index]);
+
+               if (core_freesync->map[map_index].caps->supported) {
+                       struct freesync_state state =
+                                       core_freesync->map[map_index].state;
+                       *v_total_min = state.vmin;
+                       *v_total_max = state.vmax;
+                       *event_triggers = 0;
+                       *window_min = state.time.min_window;
+                       *window_max = state.time.max_window;
+                       *lfc_mid_point_in_us = state.btr.mid_point_in_us;
+                       *inserted_frames = state.btr.frames_to_insert;
+                       *inserted_duration_in_us =
+                                       state.btr.inserted_frame_duration_in_us;
+               }
+
+       }
+}
+
index 84b53425f2c8726b30ee96bf96861594fd20af79..f083e1619dbeca89f146d167bd7a93c0d699830c 100644 (file)
@@ -164,4 +164,13 @@ void mod_freesync_pre_update_plane_addresses(struct mod_freesync *mod_freesync,
                struct dc_stream_state **streams, int num_streams,
                unsigned int curr_time_stamp);
 
+void mod_freesync_get_settings(struct mod_freesync *mod_freesync,
+               struct dc_stream_state **streams, int num_streams,
+               unsigned int *v_total_min, unsigned int *v_total_max,
+               unsigned int *event_triggers,
+               unsigned int *window_min, unsigned int *window_max,
+               unsigned int *lfc_mid_point_in_us,
+               unsigned int *inserted_frames,
+               unsigned int *inserted_duration_in_us);
+
 #endif
diff --git a/drivers/gpu/drm/amd/display/modules/inc/mod_stats.h b/drivers/gpu/drm/amd/display/modules/inc/mod_stats.h
new file mode 100644 (file)
index 0000000..3230e2a
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2016 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ *
+ * Authors: AMD
+ *
+ */
+
+#ifndef MODULES_INC_MOD_STATS_H_
+#define MODULES_INC_MOD_STATS_H_
+
+#include "dm_services.h"
+
+struct mod_stats {
+       int dummy;
+};
+
+struct mod_stats_caps {
+       bool dummy;
+};
+
+struct mod_stats *mod_stats_create(struct dc *dc);
+
+void mod_stats_destroy(struct mod_stats *mod_stats);
+
+bool mod_stats_init(struct mod_stats *mod_stats);
+
+void mod_stats_dump(struct mod_stats *mod_stats);
+
+void mod_stats_reset_data(struct mod_stats *mod_stats);
+
+void mod_stats_update_flip(struct mod_stats *mod_stats,
+               unsigned long timestamp_in_ns);
+
+void mod_stats_update_vupdate(struct mod_stats *mod_stats,
+               unsigned long timestamp_in_ns);
+
+void mod_stats_update_freesync(struct mod_stats *mod_stats,
+               unsigned int v_total_min,
+               unsigned int v_total_max,
+               unsigned int event_triggers,
+               unsigned int window_min,
+               unsigned int window_max,
+               unsigned int lfc_mid_point_in_us,
+               unsigned int inserted_frames,
+               unsigned int inserted_frame_duration_in_us);
+
+#endif /* MODULES_INC_MOD_STATS_H_ */
diff --git a/drivers/gpu/drm/amd/display/modules/stats/stats.c b/drivers/gpu/drm/amd/display/modules/stats/stats.c
new file mode 100644 (file)
index 0000000..041f87b
--- /dev/null
@@ -0,0 +1,334 @@
+/*
+ * Copyright 2016 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * 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 COPYRIGHT HOLDER(S) OR AUTHOR(S) 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.
+ *
+ * Authors: AMD
+ *
+ */
+
+#include "mod_stats.h"
+#include "dm_services.h"
+#include "dc.h"
+#include "core_types.h"
+
+#define DAL_STATS_ENABLE_REGKEY                        "DalStatsEnable"
+#define DAL_STATS_ENABLE_REGKEY_DEFAULT                0x00000001
+#define DAL_STATS_ENABLE_REGKEY_ENABLED                0x00000001
+
+#define DAL_STATS_ENTRIES_REGKEY               "DalStatsEntries"
+#define DAL_STATS_ENTRIES_REGKEY_DEFAULT       0x00350000
+#define DAL_STATS_ENTRIES_REGKEY_MAX           0x01000000
+
+#define MOD_STATS_NUM_VSYNCS                   5
+
+struct stats_time_cache {
+       unsigned long flip_timestamp_in_ns;
+       unsigned long vupdate_timestamp_in_ns;
+
+       unsigned int render_time_in_us;
+       unsigned int avg_render_time_in_us_last_ten;
+       unsigned int v_sync_time_in_us[MOD_STATS_NUM_VSYNCS];
+       unsigned int num_vsync_between_flips;
+
+       unsigned int flip_to_vsync_time_in_us;
+       unsigned int vsync_to_flip_time_in_us;
+
+       unsigned int min_window;
+       unsigned int max_window;
+       unsigned int v_total_min;
+       unsigned int v_total_max;
+       unsigned int event_triggers;
+
+       unsigned int lfc_mid_point_in_us;
+       unsigned int num_frames_inserted;
+       unsigned int inserted_duration_in_us;
+
+       unsigned int flags;
+};
+
+struct core_stats {
+       struct mod_stats public;
+       struct dc *dc;
+
+       struct stats_time_cache *time;
+       unsigned int index;
+
+       bool enabled;
+       unsigned int entries;
+};
+
+#define MOD_STATS_TO_CORE(mod_stats)\
+               container_of(mod_stats, struct core_stats, public)
+
+bool mod_stats_init(struct mod_stats *mod_stats)
+{
+       bool result = false;
+       struct core_stats *core_stats = NULL;
+       struct dc *dc = NULL;
+
+       if (mod_stats == NULL)
+               return false;
+
+       core_stats = MOD_STATS_TO_CORE(mod_stats);
+       dc = core_stats->dc;
+
+       return result;
+}
+
+struct mod_stats *mod_stats_create(struct dc *dc)
+{
+       struct core_stats *core_stats = NULL;
+       struct persistent_data_flag flag;
+       unsigned int reg_data;
+       int i = 0;
+
+       core_stats = kzalloc(sizeof(struct core_stats), GFP_KERNEL);
+
+       if (core_stats == NULL)
+               goto fail_alloc_context;
+
+       if (dc == NULL)
+               goto fail_construct;
+
+       core_stats->dc = dc;
+
+       core_stats->enabled = DAL_STATS_ENABLE_REGKEY_DEFAULT;
+       if (dm_read_persistent_data(dc->ctx, NULL, NULL,
+                       DAL_STATS_ENABLE_REGKEY,
+                       &reg_data, sizeof(unsigned int), &flag))
+               core_stats->enabled = reg_data;
+
+       core_stats->entries = DAL_STATS_ENTRIES_REGKEY_DEFAULT;
+       if (dm_read_persistent_data(dc->ctx, NULL, NULL,
+                       DAL_STATS_ENTRIES_REGKEY,
+                       &reg_data, sizeof(unsigned int), &flag)) {
+               if (reg_data > DAL_STATS_ENTRIES_REGKEY_MAX)
+                       core_stats->entries = DAL_STATS_ENTRIES_REGKEY_MAX;
+               else
+                       core_stats->entries = reg_data;
+       }
+
+       core_stats->time = kzalloc(sizeof(struct stats_time_cache) * core_stats->entries,
+                                       GFP_KERNEL);
+
+       if (core_stats->time == NULL)
+               goto fail_construct;
+
+       /* Purposely leave index 0 unused so we don't need special logic to
+        * handle calculation cases that depend on previous flip data.
+        */
+       core_stats->index = 1;
+
+       return &core_stats->public;
+
+fail_construct:
+       kfree(core_stats);
+
+fail_alloc_context:
+       return NULL;
+}
+
+void mod_stats_destroy(struct mod_stats *mod_stats)
+{
+       if (mod_stats != NULL) {
+               struct core_stats *core_stats = MOD_STATS_TO_CORE(mod_stats);
+
+               if (core_stats->time != NULL)
+                       kfree(core_stats->time);
+
+               kfree(core_stats);
+       }
+}
+
+void mod_stats_dump(struct mod_stats *mod_stats)
+{
+       struct dc  *dc = NULL;
+       struct dal_logger *logger = NULL;
+       struct core_stats *core_stats = NULL;
+       struct stats_time_cache *time = NULL;
+       unsigned int index = 0;
+
+       if (mod_stats == NULL)
+               return;
+
+       core_stats = MOD_STATS_TO_CORE(mod_stats);
+       dc = core_stats->dc;
+       logger = dc->ctx->logger;
+       time = core_stats->time;
+
+       //LogEntry* pLog = GetLog()->Open(LogMajor_ISR, LogMinor_ISR_FreeSyncSW);
+
+       //if (!pLog->IsDummyEntry())
+       {
+               dm_logger_write(logger, LOG_PROFILING, "==Display Caps==\n");
+               dm_logger_write(logger, LOG_PROFILING, "\n");
+               dm_logger_write(logger, LOG_PROFILING, "\n");
+
+               dm_logger_write(logger, LOG_PROFILING, "==Stats==\n");
+               dm_logger_write(logger, LOG_PROFILING,
+                       "render avgRender minWindow midPoint maxWindow vsyncToFlip flipToVsync #vsyncBetweenFlip #frame insertDuration vTotalMin vTotalMax eventTrigs vSyncTime1 vSyncTime2 vSyncTime3 vSyncTime4 vSyncTime5 flags\n");
+
+               for (int i = 0; i < core_stats->index && i < core_stats->entries; i++) {
+                       dm_logger_write(logger, LOG_PROFILING,
+                                       "%u  %u  %u  %u  %u  %u  %u  %u  %u  %u  %u  %u  %u  %u  %u  %u  %u  %u  %u\n",
+                                       time[i].render_time_in_us,
+                                       time[i].avg_render_time_in_us_last_ten,
+                                       time[i].min_window,
+                                       time[i].lfc_mid_point_in_us,
+                                       time[i].max_window,
+                                       time[i].vsync_to_flip_time_in_us,
+                                       time[i].flip_to_vsync_time_in_us,
+                                       time[i].num_vsync_between_flips,
+                                       time[i].num_frames_inserted,
+                                       time[i].inserted_duration_in_us,
+                                       time[i].v_total_min,
+                                       time[i].v_total_max,
+                                       time[i].event_triggers,
+                                       time[i].v_sync_time_in_us[0],
+                                       time[i].v_sync_time_in_us[1],
+                                       time[i].v_sync_time_in_us[2],
+                                       time[i].v_sync_time_in_us[3],
+                                       time[i].v_sync_time_in_us[4],
+                                       time[i].flags);
+               }
+       }
+       //GetLog()->Close(pLog);
+       //GetLog()->UnSetLogMask(LogMajor_ISR, LogMinor_ISR_FreeSyncSW);
+}
+
+void mod_stats_reset_data(struct mod_stats *mod_stats)
+{
+       struct core_stats *core_stats = NULL;
+       struct stats_time_cache *time = NULL;
+       unsigned int index = 0;
+
+       if (mod_stats == NULL)
+               return;
+
+       core_stats = MOD_STATS_TO_CORE(mod_stats);
+
+       memset(core_stats->time, 0,
+               sizeof(struct stats_time_cache) * core_stats->entries);
+
+       core_stats->index = 0;
+}
+
+void mod_stats_update_flip(struct mod_stats *mod_stats,
+               unsigned long timestamp_in_ns)
+{
+       struct core_stats *core_stats = NULL;
+       struct stats_time_cache *time = NULL;
+       unsigned int index = 0;
+
+       if (mod_stats == NULL)
+               return;
+
+       core_stats = MOD_STATS_TO_CORE(mod_stats);
+
+       if (core_stats->index >= core_stats->entries)
+               return;
+
+       time = core_stats->time;
+       index = core_stats->index;
+
+       time[index].flip_timestamp_in_ns = timestamp_in_ns;
+       time[index].render_time_in_us =
+               timestamp_in_ns - time[index - 1].flip_timestamp_in_ns;
+
+       if (index >= 10) {
+               for (unsigned int i = 0; i < 10; i++)
+                       time[index].avg_render_time_in_us_last_ten +=
+                                       time[index - i].render_time_in_us;
+               time[index].avg_render_time_in_us_last_ten /= 10;
+       }
+
+       if (time[index].num_vsync_between_flips > 0)
+               time[index].vsync_to_flip_time_in_us =
+                       timestamp_in_ns - time[index].vupdate_timestamp_in_ns;
+       else
+               time[index].vsync_to_flip_time_in_us =
+                       timestamp_in_ns - time[index - 1].vupdate_timestamp_in_ns;
+
+       core_stats->index++;
+}
+
+void mod_stats_update_vupdate(struct mod_stats *mod_stats,
+               unsigned long timestamp_in_ns)
+{
+       struct core_stats *core_stats = NULL;
+       struct stats_time_cache *time = NULL;
+       unsigned int index = 0;
+
+       if (mod_stats == NULL)
+               return;
+
+       core_stats = MOD_STATS_TO_CORE(mod_stats);
+
+       if (core_stats->index >= core_stats->entries)
+               return;
+
+       time = core_stats->time;
+       index = core_stats->index;
+
+       time[index].vupdate_timestamp_in_ns = timestamp_in_ns;
+       if (time[index].num_vsync_between_flips < MOD_STATS_NUM_VSYNCS)
+               time[index].v_sync_time_in_us[time[index].num_vsync_between_flips] =
+                       timestamp_in_ns - time[index - 1].vupdate_timestamp_in_ns;
+       time[index].flip_to_vsync_time_in_us =
+               timestamp_in_ns - time[index - 1].flip_timestamp_in_ns;
+
+       time[index].num_vsync_between_flips++;
+}
+
+void mod_stats_update_freesync(struct mod_stats *mod_stats,
+               unsigned int v_total_min,
+               unsigned int v_total_max,
+               unsigned int event_triggers,
+               unsigned int window_min,
+               unsigned int window_max,
+               unsigned int lfc_mid_point_in_us,
+               unsigned int inserted_frames,
+               unsigned int inserted_duration_in_us)
+{
+       struct core_stats *core_stats = NULL;
+       struct stats_time_cache *time = NULL;
+       unsigned int index = 0;
+
+       if (mod_stats == NULL)
+               return;
+
+       core_stats = MOD_STATS_TO_CORE(mod_stats);
+
+       if (core_stats->index >= core_stats->entries)
+               return;
+
+       time = core_stats->time;
+       index = core_stats->index;
+
+       time[index].v_total_min = v_total_min;
+       time[index].v_total_max = v_total_max;
+       time[index].event_triggers = event_triggers;
+       time[index].min_window = window_min;
+       time[index].max_window = window_max;
+       time[index].lfc_mid_point_in_us = lfc_mid_point_in_us;
+       time[index].num_frames_inserted = inserted_frames;
+       time[index].inserted_duration_in_us = inserted_duration_in_us;
+}
+