/* * Copyright (C) Texas Instruments - http://www.ti.com/ * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include "hwc_dev.h" static pthread_t vsync_thread; static pthread_mutex_t vsync_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t vsync_cond; static bool vsync_loop_active = false; nsecs_t vsync_rate; static struct timespec diff(struct timespec start, struct timespec end) { struct timespec temp; if ((end.tv_nsec - start.tv_nsec) < 0) { temp.tv_sec = end.tv_sec-start.tv_sec - 1; temp.tv_nsec = 1000000000 + end.tv_nsec-start.tv_nsec; } else { temp.tv_sec = end.tv_sec - start.tv_sec; temp.tv_nsec = end.tv_nsec - start.tv_nsec; } return temp; } static void *vsync_loop(void *data) { struct timespec tp, tp_next, tp_sleep; nsecs_t now = 0, period = vsync_rate, next_vsync = 0, next_fake_vsync = 0, sleep = 0; omap_hwc_device_t *hwc_dev = (omap_hwc_device_t *)data; tp_sleep.tv_sec = tp_sleep.tv_nsec = 0; bool reset_timers = true; setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY); for (;;) { pthread_mutex_lock(&vsync_mutex); period = vsync_rate; /* re-read rate */ while (!vsync_loop_active) { pthread_cond_wait(&vsync_cond, &vsync_mutex); } pthread_mutex_unlock(&vsync_mutex); clock_gettime(CLOCK_MONOTONIC, &tp); now = (tp.tv_sec * 1000000000) + tp.tv_nsec; next_vsync = next_fake_vsync; sleep = next_vsync - now; if (sleep < 0) { /* we missed, find where the next vsync should be */ sleep = (period - ((now - next_vsync) % period)); next_vsync = now + sleep; } next_fake_vsync = next_vsync + period; tp_next.tv_sec = (next_vsync / 1000000000); tp_next.tv_nsec = (next_vsync % 1000000000); tp_sleep = diff(tp, tp_next); nanosleep(&tp_sleep, NULL); if (hwc_dev->procs && hwc_dev->procs->vsync) { hwc_dev->procs->vsync(hwc_dev->procs, 0, next_vsync); } } return NULL; } bool use_sw_vsync() { char board[PROPERTY_VALUE_MAX]; bool rv = false; property_get("ro.product.board", board, ""); if ((strncmp("blaze", board, PROPERTY_VALUE_MAX) == 0) || (strncmp("panda5", board, PROPERTY_VALUE_MAX) == 0)) { /* TODO: panda5 really should support h/w vsync */ rv = true; } else { char value[PROPERTY_VALUE_MAX]; property_get("persist.hwc.sw_vsync", value, "0"); int use_sw_vsync = atoi(value); rv = use_sw_vsync > 0; } ALOGI("Expecting %s vsync for %s", rv ? "s/w" : "h/w", board); return rv; } void init_sw_vsync(omap_hwc_device_t *hwc_dev) { pthread_cond_init(&vsync_cond, NULL); pthread_create(&vsync_thread, NULL, vsync_loop, (void *)hwc_dev); } void start_sw_vsync() { char refresh_rate[PROPERTY_VALUE_MAX]; property_get("persist.hwc.sw_vsync_rate", refresh_rate, "60"); pthread_mutex_lock(&vsync_mutex); int rate = atoi(refresh_rate); if (rate <= 0) rate = 60; vsync_rate = 1000000000 / rate; if (vsync_loop_active) { pthread_mutex_unlock(&vsync_mutex); return; } vsync_loop_active = true; pthread_mutex_unlock(&vsync_mutex); pthread_cond_signal(&vsync_cond); } void stop_sw_vsync() { pthread_mutex_lock(&vsync_mutex); if (!vsync_loop_active) { pthread_mutex_unlock(&vsync_mutex); return; } vsync_loop_active = false; pthread_mutex_unlock(&vsync_mutex); pthread_cond_signal(&vsync_cond); }