/*
* Copyright (C) 2012 The Android Open Source Project
*
* 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 <pthread.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <cutils/log.h>
#include <hardware/hwcomposer.h>
#include <sync/sync.h>
struct ranchu_hwc_composer_device_1 {
hwc_composer_device_1_t base; // constant after init
const hwc_procs_t *procs; // constant after init
pthread_t vsync_thread; // constant after init
int32_t vsync_period_ns; // constant after init
framebuffer_device_t* fbdev; // constant after init
pthread_mutex_t vsync_lock;
bool vsync_callback_enabled; // protected by this->vsync_lock
};
static int hwc_prepare(hwc_composer_device_1_t* dev __unused,
size_t numDisplays, hwc_display_contents_1_t** displays) {
if (!numDisplays || !displays) return 0;
hwc_display_contents_1_t* contents = displays[HWC_DISPLAY_PRIMARY];
if (!contents) return 0;
for (size_t i = 0; i < contents->numHwLayers; i++) {
// We do not handle any layers, so set composition type of any non
// HWC_FRAMEBUFFER_TARGET layer to to HWC_FRAMEBUFFER.
if (contents->hwLayers[i].compositionType == HWC_FRAMEBUFFER_TARGET) {
continue;
}
contents->hwLayers[i].compositionType = HWC_FRAMEBUFFER;
}
return 0;
}
static int hwc_set(struct hwc_composer_device_1* dev,size_t numDisplays,
hwc_display_contents_1_t** displays) {
struct ranchu_hwc_composer_device_1* pdev = (struct ranchu_hwc_composer_device_1*)dev;
if (!numDisplays || !displays) {
return 0;
}
hwc_display_contents_1_t* contents = displays[HWC_DISPLAY_PRIMARY];
int retireFenceFd = -1;
int err = 0;
for (size_t layer = 0; layer < contents->numHwLayers; layer++) {
hwc_layer_1_t* fb_layer = &contents->hwLayers[layer];
int releaseFenceFd = -1;
if (fb_layer->acquireFenceFd > 0) {
const int kAcquireWarningMS= 3000;
err = sync_wait(fb_layer->acquireFenceFd, kAcquireWarningMS);
if (err < 0 && errno == ETIME) {
ALOGE("hwcomposer waited on fence %d for %d ms",
fb_layer->acquireFenceFd, kAcquireWarningMS);
}
close(fb_layer->acquireFenceFd);
if (fb_layer->compositionType != HWC_FRAMEBUFFER_TARGET) {
ALOGE("hwcomposer found acquire fence on layer %d which is not an"
"HWC_FRAMEBUFFER_TARGET layer", layer);
}
releaseFenceFd = dup(fb_layer->acquireFenceFd);
fb_layer->acquireFenceFd = -1;
}
if (fb_layer->compositionType != HWC_FRAMEBUFFER_TARGET) {
continue;
}
pdev->fbdev->post(pdev->fbdev, fb_layer->handle);
fb_layer->releaseFenceFd = releaseFenceFd;
if (releaseFenceFd > 0) {
if (retireFenceFd == -1) {
retireFenceFd = dup(releaseFenceFd);
} else {
int mergedFenceFd = sync_merge("hwc_set retireFence",
releaseFenceFd, retireFenceFd);
close(retireFenceFd);
retireFenceFd = mergedFenceFd;
}
}
}
contents->retireFenceFd = retireFenceFd;
return err;
}
static int hwc_query(struct hwc_composer_device_1* dev, int what, int* value) {
struct ranchu_hwc_composer_device_1* pdev =
(struct ranchu_hwc_composer_device_1*)dev;
switch (what) {
case HWC_BACKGROUND_LAYER_SUPPORTED:
// we do not support the background layer
value[0] = 0;
break;
case HWC_VSYNC_PERIOD:
value[0] = pdev->vsync_period_ns;
break;
default:
// unsupported query
ALOGE("%s badness unsupported query what=%d", __FUNCTION__, what);
return -EINVAL;
}
return 0;
}
static int hwc_event_control(struct hwc_composer_device_1* dev, int dpy __unused,
int event, int enabled) {
struct ranchu_hwc_composer_device_1* pdev =
(struct ranchu_hwc_composer_device_1*)dev;
int ret = -EINVAL;
// enabled can only be 0 or 1
if (!(enabled & ~1)) {
if (event == HWC_EVENT_VSYNC) {
pthread_mutex_lock(&pdev->vsync_lock);
pdev->vsync_callback_enabled=enabled;
pthread_mutex_unlock(&pdev->vsync_lock);
ret = 0;
}
}
return ret;
}
static int hwc_blank(struct hwc_composer_device_1* dev __unused, int disp,
int blank __unused) {
if (disp != HWC_DISPLAY_PRIMARY) {
return -EINVAL;
}
return 0;
}
static void hwc_dump(hwc_composer_device_1* dev __unused, char* buff __unused,
int buff_len __unused) {
// This is run when running dumpsys.
// No-op for now.
}
static int hwc_get_display_configs(struct hwc_composer_device_1* dev __unused,
int disp, uint32_t* configs, size_t* numConfigs) {
if (*numConfigs == 0) {
return 0;
}
if (disp == HWC_DISPLAY_PRIMARY) {
configs[0] = 0;
*numConfigs = 1;
return 0;
}
return -EINVAL;
}
static int32_t hwc_attribute(struct ranchu_hwc_composer_device_1* pdev,
const uint32_t attribute) {
switch(attribute) {
case HWC_DISPLAY_VSYNC_PERIOD:
return pdev->vsync_period_ns;
case HWC_DISPLAY_WIDTH:
return pdev->fbdev->width;
case HWC_DISPLAY_HEIGHT:
return pdev->fbdev->height;
case HWC_DISPLAY_DPI_X:
return pdev->fbdev->xdpi*1000;
case HWC_DISPLAY_DPI_Y:
return pdev->fbdev->ydpi*1000;
default:
ALOGE("unknown display attribute %u", attribute);
return -EINVAL;
}
}
static int hwc_get_display_attributes(struct hwc_composer_device_1* dev __unused,
int disp, uint32_t config __unused,
const uint32_t* attributes, int32_t* values) {
struct ranchu_hwc_composer_device_1* pdev = (struct ranchu_hwc_composer_device_1*)dev;
for (int i = 0; attributes[i] != HWC_DISPLAY_NO_ATTRIBUTE; i++) {
if (disp == HWC_DISPLAY_PRIMARY) {
values[i] = hwc_attribute(pdev, attributes[i]);
} else {
ALOGE("unknown display type %u", disp);
return -EINVAL;
}
}
return 0;
}
static int hwc_close(hw_device_t* dev) {
struct ranchu_hwc_composer_device_1* pdev = (struct ranchu_hwc_composer_device_1*)dev;
pthread_kill(pdev->vsync_thread, SIGTERM);
pthread_join(pdev->vsync_thread, NULL);
free(dev);
return 0;
}
static void* hwc_vsync_thread(void* data) {
struct ranchu_hwc_composer_device_1* pdev = (struct ranchu_hwc_composer_device_1*)data;
setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY);
struct timespec rt;
if (clock_gettime(CLOCK_MONOTONIC, &rt) == -1) {
ALOGE("%s:%d error in vsync thread clock_gettime: %s",
__FILE__, __LINE__, strerror(errno));
}
const int log_interval = 60;
int64_t last_logged = rt.tv_sec;
int sent = 0;
int last_sent = 0;
bool vsync_enabled = false;
struct timespec wait_time;
wait_time.tv_sec = 0;
wait_time.tv_nsec = pdev->vsync_period_ns;
while (true) {
int err = nanosleep(&wait_time, NULL);
if (err == -1) {
if (errno == EINTR) {
break;
}
ALOGE("error in vsync thread: %s", strerror(errno));
}
pthread_mutex_lock(&pdev->vsync_lock);
vsync_enabled = pdev->vsync_callback_enabled;
pthread_mutex_unlock(&pdev->vsync_lock);
if (!vsync_enabled) {
continue;
}
if (clock_gettime(CLOCK_MONOTONIC, &rt) == -1) {
ALOGE("%s:%d error in vsync thread clock_gettime: %s",
__FILE__, __LINE__, strerror(errno));
}
int64_t timestamp = int64_t(rt.tv_sec) * 1e9 + rt.tv_nsec;
pdev->procs->vsync(pdev->procs, 0, timestamp);
if (rt.tv_sec - last_logged >= log_interval) {
ALOGD("hw_composer sent %d syncs in %ds", sent - last_sent, rt.tv_sec - last_logged);
last_logged = rt.tv_sec;
last_sent = sent;
}
++sent;
}
return NULL;
}
static void hwc_register_procs(struct hwc_composer_device_1* dev,
hwc_procs_t const* procs) {
struct ranchu_hwc_composer_device_1* pdev = (struct ranchu_hwc_composer_device_1*)dev;
pdev->procs = procs;
}
static int hwc_open(const struct hw_module_t* module, const char* name,
struct hw_device_t** device) {
int ret = 0;
if (strcmp(name, HWC_HARDWARE_COMPOSER)) {
ALOGE("%s called with bad name %s", __FUNCTION__, name);
return -EINVAL;
}
ranchu_hwc_composer_device_1 *pdev = new ranchu_hwc_composer_device_1();
if (!pdev) {
ALOGE("%s failed to allocate dev", __FUNCTION__);
return -ENOMEM;
}
pdev->base.common.tag = HARDWARE_DEVICE_TAG;
pdev->base.common.version = HWC_DEVICE_API_VERSION_1_1;
pdev->base.common.module = const_cast<hw_module_t *>(module);
pdev->base.common.close = hwc_close;
pdev->base.prepare = hwc_prepare;
pdev->base.set = hwc_set;
pdev->base.eventControl = hwc_event_control;
pdev->base.blank = hwc_blank;
pdev->base.query = hwc_query;
pdev->base.registerProcs = hwc_register_procs;
pdev->base.dump = hwc_dump;
pdev->base.getDisplayConfigs = hwc_get_display_configs;
pdev->base.getDisplayAttributes = hwc_get_display_attributes;
pdev->vsync_period_ns = 1000*1000*1000/60; // vsync is 60 hz
hw_module_t const* hw_module;
ret = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &hw_module);
if (ret != 0) {
ALOGE("ranchu_hw_composer hwc_open %s module not found", GRALLOC_HARDWARE_MODULE_ID);
return ret;
}
ret = framebuffer_open(hw_module, &pdev->fbdev);
if (ret != 0) {
ALOGE("ranchu_hw_composer hwc_open could not open framebuffer");
}
pthread_mutex_init(&pdev->vsync_lock, NULL);
pdev->vsync_callback_enabled = false;
ret = pthread_create (&pdev->vsync_thread, NULL, hwc_vsync_thread, pdev);
if (ret) {
ALOGE("ranchu_hw_composer could not start vsync_thread\n");
}
*device = &pdev->base.common;
return ret;
}
static struct hw_module_methods_t hwc_module_methods = {
open: hwc_open,
};
hwc_module_t HAL_MODULE_INFO_SYM = {
common: {
tag: HARDWARE_MODULE_TAG,
module_api_version: HWC_MODULE_API_VERSION_0_1,
hal_api_version: HARDWARE_HAL_API_VERSION,
id: HWC_HARDWARE_MODULE_ID,
name: "Android Emulator hwcomposer module",
author: "The Android Open Source Project",
methods: &hwc_module_methods,
}
};