blob: 15280e305e01d117fe8094a4321e0a04bf630c7e [file] [log] [blame]
/*
* Copyright (C) 2016 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 <fcntl.h>
#include <cutils/log.h>
#include <inttypes.h>
#include <cstdlib>
#include <vector>
#include "hwc2.h"
static void hwc2_vsync(void* /*data*/, int /*dpy_id*/, uint64_t /*timestamp*/)
{
return;
}
static void hwc2_hotplug(void *data, int dpy_id, bool connected)
{
hwc2_dev *dev = static_cast<hwc2_dev *>(data);
dev->hotplug(static_cast<hwc2_display_t>(dpy_id),
(connected)? HWC2_CONNECTION_CONNECTED:
HWC2_CONNECTION_DISCONNECTED);
}
static void hwc2_custom_event(void* /*data*/, int /*dpy_id*/,
struct adf_event* /*event*/)
{
return;
}
const struct adf_hwc_event_callbacks hwc2_adfhwc_callbacks = {
.vsync = hwc2_vsync,
.hotplug = hwc2_hotplug,
.custom_event = hwc2_custom_event,
};
hwc2_dev::hwc2_dev()
: callback_handler(),
displays(),
adf_helper(nullptr) { }
hwc2_dev::~hwc2_dev()
{
if (adf_helper)
adf_hwc_close(adf_helper);
hwc2_display::reset_ids();
}
hwc2_error_t hwc2_dev::get_display_type(hwc2_display_t dpy_id,
hwc2_display_type_t *out_type) const
{
auto it = displays.find(dpy_id);
if (it == displays.end()) {
ALOGE("dpy %" PRIu64 ": invalid display handle", dpy_id);
return HWC2_ERROR_BAD_DISPLAY;
}
*out_type = it->second.get_type();
return HWC2_ERROR_NONE;
}
hwc2_error_t hwc2_dev::create_layer(hwc2_display_t dpy_id, hwc2_layer_t *out_layer)
{
auto it = displays.find(dpy_id);
if (it == displays.end()) {
ALOGE("dpy %" PRIu64 ": invalid display handle", dpy_id);
return HWC2_ERROR_BAD_DISPLAY;
}
return it->second.create_layer(out_layer);
}
hwc2_error_t hwc2_dev::destroy_layer(hwc2_display_t dpy_id, hwc2_layer_t lyr_id)
{
auto it = displays.find(dpy_id);
if (it == displays.end()) {
ALOGE("dpy %" PRIu64 ": invalid display handle", dpy_id);
return HWC2_ERROR_BAD_DISPLAY;
}
return it->second.destroy_layer(lyr_id);
}
void hwc2_dev::hotplug(hwc2_display_t dpy_id, hwc2_connection_t connection)
{
auto it = displays.find(dpy_id);
if (it == displays.end()) {
ALOGW("dpy %" PRIu64 ": invalid display handle preventing hotplug"
" callback", dpy_id);
return;
}
hwc2_error_t ret = it->second.set_connection(connection);
if (ret != HWC2_ERROR_NONE)
return;
callback_handler.call_hotplug(dpy_id, connection);
}
hwc2_error_t hwc2_dev::register_callback(hwc2_callback_descriptor_t descriptor,
hwc2_callback_data_t callback_data, hwc2_function_pointer_t pointer)
{
if (descriptor == HWC2_CALLBACK_INVALID) {
ALOGE("invalid callback descriptor %u", descriptor);
return HWC2_ERROR_BAD_PARAMETER;
}
return callback_handler.register_callback(descriptor, callback_data,
pointer);
}
int hwc2_dev::open_adf_device()
{
adf_id_t *dev_ids = nullptr;
int ret;
ssize_t n_devs = adf_devices(&dev_ids);
if (n_devs < 0) {
ALOGE("failed to enumerate adf devices: %s", strerror(n_devs));
return n_devs;
}
std::vector<int> intf_fds;
for (ssize_t idx = 0; idx < n_devs; idx++) {
int intf_fd = open_adf_display(dev_ids[idx]);
if (intf_fd >= 0)
intf_fds.push_back(intf_fd);
}
if (displays.empty()) {
ALOGE("failed to open any physical displays");
ret = -EINVAL;
goto err_dpy_open;
}
ret = adf_hwc_open(intf_fds.data(), intf_fds.size(),
&hwc2_adfhwc_callbacks, this, &adf_helper);
if (ret < 0) {
ALOGE("failed to open adf helper: %s", strerror(ret));
goto err_hwc_open;
}
for (auto &dpy: displays) {
ret = dpy.second.retrieve_display_configs(adf_helper);
if (ret < 0) {
ALOGE("dpy %" PRIu64 ": failed to retrieve display configs: %s",
dpy.second.get_id(), strerror(ret));
goto err_rtrv;
}
}
for (auto &dpy: displays)
callback_handler.call_hotplug(dpy.second.get_id(),
dpy.second.get_connection());
free(dev_ids);
return 0;
err_rtrv:
adf_hwc_close(adf_helper);
err_hwc_open:
displays.clear();
err_dpy_open:
free(dev_ids);
return ret;
}
int hwc2_dev::open_adf_display(adf_id_t adf_id) {
struct adf_device adf_dev;
int ret = adf_device_open(adf_id, O_RDWR, &adf_dev);
if (ret < 0) {
ALOGE("failed to open adf%u device: %s", adf_id, strerror(ret));
return ret;
}
int intf_fd = adf_interface_open(&adf_dev, 0, O_RDWR);
if (intf_fd < 0) {
ALOGE("failed to open adf%u interface: %s", adf_id, strerror(intf_fd));
adf_device_close(&adf_dev);
return intf_fd;
}
struct adf_interface_data intf;
ret = adf_get_interface_data(intf_fd, &intf);
if (ret < 0) {
ALOGE("failed to get adf%u interface data: %s", adf_id, strerror(ret));
close(intf_fd);
adf_device_close(&adf_dev);
return ret;
}
hwc2_display_t dpy_id = hwc2_display::get_next_id();
displays.emplace(std::piecewise_construct, std::forward_as_tuple(dpy_id),
std::forward_as_tuple(dpy_id, intf_fd, adf_dev,
(intf.hotplug_detect)? HWC2_CONNECTION_CONNECTED:
HWC2_CONNECTION_DISCONNECTED, HWC2_DISPLAY_TYPE_PHYSICAL));
adf_free_interface_data(&intf);
return intf_fd;
}