Chris, happy with this revision? On Fri, Oct 21, 2016 at 09:22:13AM +0530, Nautiyal Ankit wrote: > From: Ramalingam C <ramalingam.c@xxxxxxxxx> > > Idleness DRRS: > By default the DRRS state will be at DRRS_HIGH_RR. When a Display > content is Idle for more than 1Sec Idleness will be declared and > DRRS_LOW_RR will be invoked, changing the refresh rate to the > lower most refresh rate supported by the panel. As soon as there > is a display content change there will be a DRRS state transition > as DRRS_LOW_RR--> DRRS_HIGH_RR, changing the refresh rate to the > highest refresh rate supported by the panel. > > To test this, Idleness DRRS IGT will probe the DRRS state at below > instances and compare with the expected state. > > Instance Expected State > 1. Immediately after rendering the still image DRRS_HIGH_RR > 2. After a delay of 1.2Sec DRRS_LOW_RR > 3. After changing the frame buffer DRRS_HIGH_RR > 4. After a delay of 1.2Sec DRRS_LOW_RR > 5. After changing the frame buffer DRRS_HIGH_RR > 6. After a delay of 1.2Sec DRRS_LOW_RR > > The test checks the driver DRRS state from the debugfs entry. To check the > actual refresh-rate, a separate thread counts the number of vblanks > received per sec. The refresh-rate calculated is checked against the > expected refresh-rate with a tolerance value of 2. > > This patch is a continuation of the earlier work > https://patchwork.freedesktop.org/patch/45472/ towards igt for idleness > > DRRS. The code is tested on Broxton BXT_T platform. > > v2: Addressed the comments and suggestions from Vlad, Marius. > The signoff details from the earlier work are also included. > > v3: Modified vblank rate calculation by using reply-sequence, provided by > drmWaitVBlank, as suggested by Chris Wilson. > > Signed-off-by: Ramalingam C <ramalingam.c@xxxxxxxxx> > Signed-off-by: Vandana Kannan <vandana.kannan@xxxxxxxxx> > Signed-off-by: aknautiy <ankit.k.nautiyal@xxxxxxxxx> > --- > tests/Makefile.sources | 1 + > tests/kms_drrs.c | 599 +++++++++++++++++++++++++++++++++++++++++++++++++ > 2 files changed, 600 insertions(+) > create mode 100644 tests/kms_drrs.c > > diff --git a/tests/Makefile.sources b/tests/Makefile.sources > index a837977..5f31521 100644 > --- a/tests/Makefile.sources > +++ b/tests/Makefile.sources > @@ -91,6 +91,7 @@ TESTS_progs_M = \ > kms_cursor_crc \ > kms_cursor_legacy \ > kms_draw_crc \ > + kms_drrs \ > kms_fbc_crc \ > kms_fbcon_fbt \ > kms_flip \ > diff --git a/tests/kms_drrs.c b/tests/kms_drrs.c > new file mode 100644 > index 0000000..bd5a135 > --- /dev/null > +++ b/tests/kms_drrs.c > @@ -0,0 +1,599 @@ > +/* > + * Copyright © 2016 Intel Corporation > + * > + * 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 (including the next > + * paragraph) 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 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 "drmtest.h" > +#include "igt_debugfs.h" > +#include "igt_kms.h" > +#include "intel_chipset.h" > +#include "intel_batchbuffer.h" > +#include "ioctl_wrappers.h" > +#include <time.h> > +#include <pthread.h> > +#include <stdlib.h> > +#include <sys/time.h> > +IGT_TEST_DESCRIPTION( > +"Performs write operations and then waits for DRRS to invoke the" > +"Low Refresh Rate and then disturbs the contents of the screen once" > +"again hence DRRS revert back to High Refresh Rate(Default)."); > + > +#define DRRS_STATUS_BYTES_CNT 1000 > +#define SET 1 > +#define RESET 0 > + > +/* > + * Structure to store data to create 2 framebuffers, fb[0] and fb[1] on a given > + * display. To disturb the content of the screen, we replace fb[0] by fb[1] and > + * vice versa. > + */ > +typedef struct { > + int drm_fd; > + uint32_t devid; > + uint32_t handle[2]; > + igt_display_t display; > + igt_output_t *output; > + enum pipe pipe; > + igt_plane_t *primary; > + struct igt_fb fb[2]; > + uint32_t fb_id[2]; > +} data_t; > + > +/* > + * Structure to count vblank and note the starting time of the counter > + */ > +typedef struct { > + unsigned int vbl_count; > + struct timeval start; > +} vbl_info; > + > +/* > + * Condition variables,mutex, and shared variables for thread synchronization > + */ > +pthread_mutex_t rr_mutex; > +pthread_cond_t cv_rr_calc_requested; > +pthread_cond_t cv_rr_calc_completed; > +int rr_calc_requested = RESET; > +int rr_calc_completed = RESET; > +int thread_flag = SET; > +int refresh_rate_shared = -1; > + > +/* > + * Structure for refresh rate type > + */ > +typedef struct{ > + int rate; > + const char *name; > +} refresh_rate_t; > + > +static inline uint32_t pipe_select(int pipe) > +{ > + if (pipe > 1) > + return pipe << DRM_VBLANK_HIGH_CRTC_SHIFT; > + else if (pipe > 0) > + return DRM_VBLANK_SECONDARY; > + else > + return 0; > +} > + > +/* > + * igt_millisec_elapsed: > + * Calculates the no. of millisec elapsed since timeval start > + */ > +static double igt_millisec_elapsed(const struct timeval *start) > +{ > + struct timeval curr; > + double res; > + > + gettimeofday(&curr, NULL); > + return (1e6*(curr.tv_sec - start->tv_sec) + > + (curr.tv_usec - start->tv_usec)/1000.00); > +} > + > +/* > + * read_drrs_status : > + * Func to read the DRRS status from debugfs > + */ > +static bool read_drrs_status(char *str) > +{ > + FILE *status; > + int cnt; > + > + status = igt_debugfs_fopen("i915_drrs_status", "r"); > + igt_assert(status); > + > + cnt = fread(str, DRRS_STATUS_BYTES_CNT - 1, 1, status); > + if (!cnt) { > + if (!feof(status)) { > + igt_info("ERROR: %d at fread\n", ferror(status)); > + return false; > + } > + clearerr(status); > + } > + fclose(status); > + return true; > +} > + > +/* > + * is_drrs_supported : > + * Func to check for DRRS support > + */ > +static bool is_drrs_supported(void) > +{ > + char str[DRRS_STATUS_BYTES_CNT] = {}; > + > + if (!read_drrs_status(str)) { > + igt_info("ERROR: Debugfs read FAILED\n"); > + return false; > + } > + return strstr(str, "DRRS Supported: Yes") != NULL; > +} > + > +/* > + * is_drrs_enabled : > + * Func to check if DRRS is enabled by driver > + */ > +static bool is_drrs_enabled(void) > +{ > + char str[DRRS_STATUS_BYTES_CNT] = {}; > + > + if (!read_drrs_status(str)) { > + igt_info("ERROR: Debugfs read FAILED\n"); > + return false; > + } > + return strstr(str, "Idleness DRRS: Disabled") == NULL; > +} > + > +/* > + * prepare_crtc : > + * Func to prepare crtc for the given display > + */ > +static bool prepare_crtc(data_t *data) > +{ > + if (data == NULL) > + return false; > + > + igt_display_t *display = &data->display; > + igt_output_t *output = data->output; > + > + /* select the pipe we want to use */ > + igt_output_set_pipe(output, data->pipe); > + igt_display_commit(display); > + > + if (!output->valid) { > + igt_output_set_pipe(output, PIPE_ANY); > + igt_display_commit(display); > + return false; > + } > + > + return true; > +} > +/* > + * vbl_rate : > + * calculates the vblank rate by counting vblanks between start and end > + * period, divided by time between start and end. > + */ > +static double vbl_rate(const union drm_wait_vblank *start, > + const union drm_wait_vblank *end) { > + double s, e; > + > + s = start->reply.tval_sec + 1e-6 * start->reply.tval_usec; > + e = end->reply.tval_sec + 1e-6 * end->reply.tval_usec; > + return ((end->reply.sequence - start->reply.sequence) / (e - s)); > +} > + > +/* > + * calculate_refresh_rate : > + * Func to calculate the refresh rate by counting > + * vblanks in 1 sec (1000 ms). It returns the calculated refresh rate on > + * success. Returns -1 in case of error. > + */ > +int calculate_refresh_rate(data_t *data) > +{ > +#define PERIOD_MS 1000 > + union drm_wait_vblank start, end; > + struct timeval start_tv; > + > + memset(&start, 0, sizeof(start)); > + start.request.type = DRM_VBLANK_RELATIVE | pipe_select(data->pipe); > + if (drmWaitVBlank(data->drm_fd, &start) != 0) { > + igt_info("ERROR : drmWaitVBlank Failed!\n"); > + return -1; > + } > + gettimeofday(&start_tv, NULL); > + while (igt_millisec_elapsed(&start_tv) < PERIOD_MS) { > + memset(&end, 0, sizeof(end)); > + end.request.type = DRM_VBLANK_RELATIVE | > + pipe_select(data->pipe); > + end.request.sequence = 1; > + if (drmWaitVBlank(data->drm_fd, &end) != 0) { > + igt_info("ERROR : drmWaitVBlank Failed!\n"); > + return -1; > + } > + } > + return vbl_rate(&start, &end); > +} > + > +/* > + * worker_thread_func : > + * Func which is run by a worker thread to calculate the refresh_rate, > + * when signalled by the master thread. > + */ > +void *worker_thread_func(void *ptr) > +{ > + data_t *data = (data_t *) ptr; > + > + while (1) { > + int refresh_rate = 0; > + /*wait for signal from master*/ > + pthread_mutex_lock(&rr_mutex); > + while (!rr_calc_requested) > + pthread_cond_wait(&cv_rr_calc_requested, &rr_mutex); > + > + /* checkpoint for thread termination */ > + if (thread_flag == RESET) { > + pthread_mutex_unlock(&rr_mutex); > + pthread_exit(NULL); > + } > + rr_calc_requested = RESET; > + pthread_mutex_unlock(&rr_mutex); > + /*calculate refresh_rate*/ > + refresh_rate = calculate_refresh_rate(data); > + > + /* signal the master */ > + pthread_mutex_lock(&rr_mutex); > + refresh_rate_shared = refresh_rate; > + rr_calc_completed = SET; > + pthread_mutex_unlock(&rr_mutex); > + pthread_cond_signal(&cv_rr_calc_completed); > + } > + return NULL; > +} > + > +/* > + * check_refresh_rate : > + * Func to check the refresh rate calculated, against the expected refresh > + * rate. It signals the worker thread to calculate the refresh- > + * rate, and checks if the correct refresh rate is switched. > + * Returns 0 in case of pass , 1 in case of failure, and -1 for error. > + */ > + > +static int check_refresh_rate(data_t *data, refresh_rate_t *expected_rr) > +{ > +/* > + * The calculated refresh-rate is an approximation to estimated refresh-rate. > + * The tolerance threshold for the difference is set to 2. Difference more than > + * 2 is considered as failure for the check_refresh_rate. > + */ > +#define TOLERANCE_THRESHOLD 2 > + int refresh_rate = -1; > + char drrs_status[DRRS_STATUS_BYTES_CNT] = {}; > + > + /* Signal worker thread to calculate refresh rate */ > + pthread_mutex_lock(&rr_mutex); > + rr_calc_requested = SET; > + pthread_mutex_unlock(&rr_mutex); > + pthread_cond_signal(&cv_rr_calc_requested); > + > + /* Get the DRRS Status from the debugfs entry */ > + if (!read_drrs_status(drrs_status)) { > + igt_info("ERROR : DRRS debugfs read FAILED\n"); > + return -1; > + } > + /* Wait for the refresh rate calculator thread */ > + pthread_mutex_lock(&rr_mutex); > + while (!rr_calc_completed) > + pthread_cond_wait(&cv_rr_calc_completed, &rr_mutex); > + rr_calc_completed = RESET; > + refresh_rate = refresh_rate_shared; > + pthread_mutex_unlock(&rr_mutex); > + if (refresh_rate < 0) { > + igt_info("ERROR : Refresh rate calculation FAILED\n"); > + return -1; > + } > + igt_info("INFO : Actual Refresh_rate based on Vblank Counts/sec = %d\n" > + , refresh_rate); > + > + /* Compare with Expected Refresh Rate*/ > + igt_info("INFO: Expected Refresh-Rate %d\n", expected_rr->rate); > + if (strstr(drrs_status, expected_rr->name) != NULL > + && abs(refresh_rate - expected_rr->rate) <= TOLERANCE_THRESHOLD) { > + igt_info("INFO: Expected %s : PASSED\n", expected_rr->name); > + return 0; > + } > + > + igt_info("INFO : Expected %s : FAILED.\n", expected_rr->name); > + return 1; > +} > + > +/* > + * clean_up : > + * Func to stop the worker thread and destroy thread syncronization > + * structures. > + */ > +void clean_up(pthread_t rr_thread) > +{ > + /* Stop the worker thread */ > + pthread_mutex_lock(&rr_mutex); > + thread_flag = RESET; > + rr_calc_requested = SET; > + pthread_mutex_unlock(&rr_mutex); > + pthread_cond_signal(&cv_rr_calc_requested); > + pthread_join(rr_thread, NULL); > + > + /* destroy the condition variables and mutex */ > + pthread_cond_destroy(&cv_rr_calc_requested); > + pthread_cond_destroy(&cv_rr_calc_completed); > + pthread_mutex_destroy(&rr_mutex); > +} > + > +/* > + * execute_test: > + * This function > + * 1. Initializes the thread sync structures. > + * 2. Creates a worker thread which can be signaled to estimate > + * the refresh rates based on counting vblanks per sec. > + * 3. Creates two different Framebuffer fb0, fb1 > + * and apply them in the interval of 1.2Sec. So between FB change > + * DRRS should toggle from DRRS_HIGH_RR -> DRRS_LOW_RR. > + * And on next FB change DRRS_LOW_RR -> DRRS_HIGH_RR. > + * > + * These expected transitions are verified using the kernel debugfs > + * and the refresh-rate, calculated by counting vblanks per sec by the > + * worker thread. > + */ > +static int execute_test(data_t *data) > +{ > + igt_display_t *display = &data->display; > + igt_output_t *output = data->output; > + drmModeModeInfo *mode, *supported_modes; > + int test_failed = 0; > + int ret, i, count_modes; > + refresh_rate_t high_rr, low_rr; > + pthread_t rr_thread; > + > + /*initialize the condition variables and mutex*/ > + pthread_cond_init(&cv_rr_calc_requested, NULL); > + pthread_cond_init(&cv_rr_calc_completed, NULL); > + pthread_mutex_init(&rr_mutex, NULL); > + thread_flag = SET; > + rr_calc_requested = RESET; > + rr_calc_completed = RESET; > + /* create a thread for calculating the refresh rate */ > + ret = pthread_create(&rr_thread, NULL, worker_thread_func, > + (void *) data); > + if (ret == -1) { > + igt_info("ERROR : Refresh rate thread creation FAILED\n"); > + clean_up(rr_thread); > + return -1; > + } > + > + high_rr.name = "DRRS_HIGH_RR"; > + high_rr.rate = 0; > + low_rr.name = "DRRS_LOW_RR"; > + low_rr.rate = 0; > + /* get the max and min supported refresh rates for the display */ > + supported_modes = output->config.connector->modes; > + count_modes = output->config.connector->count_modes; > + /* minimum 2 modes are required for DRRS */ > + if (count_modes < 2) { > + igt_info("ERROR: Minimum 2 modes required for DRRS\n"); > + return -1; > + } > + for (i = 0; i < count_modes; i++) { > + int rr = supported_modes[i].vrefresh; > + > + if (i == 0) { > + high_rr.rate = low_rr.rate = rr; > + continue; > + } > + if (high_rr.rate < rr) > + high_rr.rate = rr; > + if (low_rr.rate > rr) > + low_rr.rate = rr; > + } > + data->primary = igt_output_get_plane(data->output, IGT_PLANE_PRIMARY); > + mode = igt_output_get_mode(data->output); > + data->fb_id[0] = igt_create_color_fb(data->drm_fd, mode->hdisplay, > + mode->vdisplay, > + DRM_FORMAT_XRGB8888, > + LOCAL_DRM_FORMAT_MOD_NONE, > + 0.0, 100.1, 0.0, &data->fb[0]); > + igt_assert(data->fb_id[0]); > + data->fb_id[1] = igt_create_color_fb(data->drm_fd, mode->hdisplay, > + mode->vdisplay, > + DRM_FORMAT_XRGB8888, > + LOCAL_DRM_FORMAT_MOD_NONE, > + 100.1, 0.0, 0.0, > + &data->fb[1]); > + igt_assert(data->fb_id[1]); > + > + data->handle[0] = data->fb[0].gem_handle; > + data->handle[1] = data->fb[1].gem_handle; > + > + /* scanout = fb[1] */ > + igt_plane_set_fb(data->primary, &data->fb[1]); > + igt_display_commit(display); > + if (!is_drrs_enabled()) { > + igt_info("INFO : DRRS not enabled\n"); > + igt_plane_set_fb(data->primary, NULL); > + igt_output_set_pipe(output, PIPE_ANY); > + igt_display_commit(display); > + igt_remove_fb(data->drm_fd, &data->fb[0]); > + igt_remove_fb(data->drm_fd, &data->fb[1]); > + clean_up(rr_thread); > + return -1; > + } > + > + /* expecting High RR */ > + ret = check_refresh_rate(data, &high_rr); > + if (ret == -1) > + return -1; > + if (ret == 1) > + test_failed = 1; > + /* 1.2Sec to Enter the Idleness and check for Low Refresh Rate */ > + usleep(1200000); > + ret = check_refresh_rate(data, &low_rr); > + if (ret == -1) > + return -1; > + if (ret == 1) > + test_failed = 1; > + > + /* scanout = fb[0] and check for High Refresh Rate*/ > + igt_plane_set_fb(data->primary, &data->fb[0]); > + igt_display_commit(display); > + ret = check_refresh_rate(data, &high_rr); > + if (ret == -1) > + return -1; > + if (ret == 1) > + test_failed = 1; > + > + /* 1.2Sec to Enter the Idleness and check for Low Refresh Rate */ > + usleep(1200000); > + ret = check_refresh_rate(data, &low_rr); > + if (ret == -1) > + return -1; > + if (ret == 1) > + test_failed = 1; > + > + /* scanout = fb[1] and check for High Refresh Rate*/ > + igt_plane_set_fb(data->primary, &data->fb[1]); > + igt_display_commit(display); > + ret = check_refresh_rate(data, &high_rr); > + if (ret == -1) > + return -1; > + if (ret == 1) > + test_failed = 1; > + > + /* 1.2Sec to Enter the Idleness */ > + usleep(1200000); > + ret = check_refresh_rate(data, &low_rr); > + if (ret == -1) > + return -1; > + if (ret == 1) > + test_failed = 1; > + > + clean_up(rr_thread); > + return test_failed; > +} > + > +/* > + * finish_crtc : > + * Func to free the framebuffers after the test completion. > + */ > +static void finish_crtc(data_t *data) > +{ > + igt_plane_set_fb(data->primary, NULL); > + igt_output_set_pipe(data->output, PIPE_ANY); > + igt_display_commit(&data->display); > + > + igt_remove_fb(data->drm_fd, &data->fb[0]); > + igt_remove_fb(data->drm_fd, &data->fb[1]); > +} > + > +/* > + * reset_display : > + * Func to reset the display structures after the test completion. > + */ > +static void reset_display(data_t *data) > +{ > + igt_display_t *display = &data->display; > + > + for_each_connected_output(display, data->output) { > + if (data->output->valid) { > + data->primary = igt_output_get_plane(data->output, > + IGT_PLANE_PRIMARY); > + igt_plane_set_fb(data->primary, NULL); > + } > + igt_output_set_pipe(data->output, PIPE_ANY); > + } > +} > +/* > + * run_test : > + * Func to run the test for the eDP display. > + */ > +static void run_test(data_t *data) > +{ > + igt_display_t *display = &data->display; > + int ret; > + > + reset_display(data); > + /* Check for eDP Connector on PIPE A ,as eDP goes normally on PIPE_A */ > + for_each_connected_output(display, data->output) { > + drmModeConnectorPtr c = data->output->config.connector; > + > + if (c->connector_type != DRM_MODE_CONNECTOR_eDP || > + c->connection != DRM_MODE_CONNECTED) > + continue; > + data->pipe = PIPE_A; > + igt_assert_f(prepare_crtc(data), "Failed to prepare CRTC\n"); > + igt_info("INFO : Beginning %s on pipe %s,connector %s\n", > + igt_test_name(), > + kmstest_pipe_name(data->pipe), > + igt_output_name(data->output)); > + > + ret = execute_test(data); > + igt_skip_on_f(ret == -1, > + "INFO :%s on pipe %s, connector %s:SKIPPED\n", > + igt_test_name(), > + kmstest_pipe_name(data->pipe), > + igt_output_name(data->output)); > + igt_fail_on_f(ret == 1, > + "INFO :%s on pipe %s, connector %s:FAILED\n", > + igt_test_name(), > + kmstest_pipe_name(data->pipe), > + igt_output_name(data->output)); > + > + igt_assert_f(ret == 0, > + "INFO :%s on pipe %s, connector %s: PASSED\n", > + igt_test_name(), > + kmstest_pipe_name(data->pipe), > + igt_output_name(data->output)); > + > + finish_crtc(data); > + } > +} > + > +igt_simple_main > +{ > + data_t data = {}; > + > + igt_skip_on_simulation(); > + data.drm_fd = drm_open_driver_master(DRIVER_INTEL); > + if (data.drm_fd == -1) { > + igt_info("ERROR : invalid fd\n"); > + return; > + } > + data.devid = intel_get_drm_devid(data.drm_fd); > + if (data.devid < 0) { > + igt_info("ERROR : invalid dev id\n"); > + return; > + } > + /* Check if the DRRS is supported. > + * If yes call the Idleness DRRS test > + */ > + igt_require_f(is_drrs_supported(), > + "DRRS not supported:check VBT/panel caps\n"); > + igt_display_init(&data.display, data.drm_fd); > + run_test(&data); > + igt_display_fini(&data.display); > +} > -- > 2.7.4 > > _______________________________________________ > Intel-gfx mailing list > Intel-gfx@xxxxxxxxxxxxxxxxxxxxx > https://lists.freedesktop.org/mailman/listinfo/intel-gfx _______________________________________________ Intel-gfx mailing list Intel-gfx@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/intel-gfx