blob: 21a76f033322f88d810fa6de4483acf53afc2837 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <lib/fidl/coding.h>
#include <zircon/assert.h>
#include <zircon/device/display-controller.h>
#include <zircon/pixelformat.h>
#include <zircon/process.h>
#include <zircon/syscalls.h>
#include <zircon/types.h>
#include "fuchsia/display/c/fidl.h"
#include "lib/framebuffer/framebuffer.h"
static int32_t dc_fd = -1;
static zx_handle_t dc_handle = ZX_HANDLE_INVALID;
static int32_t txid;
static int32_t display_id;
static int32_t layer_id;
static int32_t width;
static int32_t height;
static int32_t stride;
static zx_pixel_format_t format;
static bool type_set;
static uint32_t image_type;
static zx_handle_t vmo = ZX_HANDLE_INVALID;
static bool inited = false;
static bool in_single_buffer_mode;
static zx_status_t set_layer_config(int32_t layer_id, uint32_t width, uint32_t height,
zx_pixel_format_t format, int32_t type) {
fuchsia_display_ControllerSetLayerPrimaryConfigRequest layer_cfg_msg;
layer_cfg_msg.hdr.ordinal = fuchsia_display_ControllerSetLayerPrimaryConfigOrdinal;
layer_cfg_msg.layer_id = layer_id;
layer_cfg_msg.image_config.width = width;
layer_cfg_msg.image_config.height = height;
layer_cfg_msg.image_config.pixel_format = format;
layer_cfg_msg.image_config.type = type;
return zx_channel_write(dc_handle, 0, &layer_cfg_msg, sizeof(layer_cfg_msg), NULL, 0);
}
zx_status_t fb_bind(bool single_buffer, const char** err_msg_out) {
const char* err_msg;
if (!err_msg_out) {
err_msg_out = &err_msg;
}
*err_msg_out = "";
if (inited) {
*err_msg_out = "framebufer already initialzied";
return ZX_ERR_ALREADY_BOUND;
}
// TODO(stevensd): Don't hardcode display controller 0
zx_status_t status;
dc_fd = open("/dev/class/display-controller/000", O_RDWR);
if (dc_fd < 0) {
*err_msg_out = "Failed to open display controller";
status = ZX_ERR_NO_RESOURCES;
goto err;
}
if (ioctl_display_controller_get_handle(dc_fd, &dc_handle) != sizeof(zx_handle_t)) {
*err_msg_out = "Failed to get display controller handle";
status = ZX_ERR_INTERNAL;
goto err;
}
zx_handle_t observed;
uint32_t signals = ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED;
if ((status = zx_object_wait_one(dc_handle,
signals, ZX_TIME_INFINITE, &observed)) != ZX_OK) {
*err_msg_out = "Failed waiting for display";
goto err;
}
if (observed & ZX_CHANNEL_PEER_CLOSED) {
*err_msg_out = "Display controller connection closed";
status = ZX_ERR_PEER_CLOSED;
goto err;
}
uint8_t bytes[ZX_CHANNEL_MAX_MSG_BYTES];
uint32_t actual_bytes, actual_handles;
if ((status = zx_channel_read(dc_handle, 0, bytes, NULL, ZX_CHANNEL_MAX_MSG_BYTES, 0,
&actual_bytes, &actual_handles)) != ZX_OK) {
*err_msg_out = "Reading display addded callback failed";
goto err;
}
if ((status = fidl_decode(&fuchsia_display_ControllerDisplaysChangedEventTable,
bytes, actual_bytes, NULL, 0, err_msg_out)) != ZX_OK) {
goto err;
}
// We're guaranteed that added contains at least one display, since we haven't
// been notified of any displays to remove.
fuchsia_display_ControllerDisplaysChangedEvent* changes =
(fuchsia_display_ControllerDisplaysChangedEvent*) bytes;
fuchsia_display_Info* display = (fuchsia_display_Info*) changes->added.data;
fuchsia_display_Mode* mode = (fuchsia_display_Mode*) display->modes.data;
zx_pixel_format_t pixel_format = ((int32_t*)(display->pixel_format.data))[0];
fuchsia_display_ControllerComputeLinearImageStrideRequest stride_msg;
stride_msg.hdr.ordinal = fuchsia_display_ControllerComputeLinearImageStrideOrdinal;
stride_msg.hdr.txid = txid++;
stride_msg.width = mode->horizontal_resolution;
stride_msg.pixel_format = pixel_format;
fuchsia_display_ControllerComputeLinearImageStrideResponse stride_rsp;
zx_channel_call_args_t stride_call = {};
stride_call.wr_bytes = &stride_msg;
stride_call.rd_bytes = &stride_rsp;
stride_call.wr_num_bytes = sizeof(stride_msg);
stride_call.rd_num_bytes = sizeof(stride_rsp);
zx_status_t read_status;
if ((status = zx_channel_call(dc_handle, 0, ZX_TIME_INFINITE, &stride_call,
&actual_bytes, &actual_handles, &read_status)) != ZX_OK) {
*err_msg_out = "Failed to get linear stride";
goto err;
}
fuchsia_display_ControllerCreateLayerRequest create_layer_msg;
create_layer_msg.hdr.ordinal = fuchsia_display_ControllerCreateLayerOrdinal;
fuchsia_display_ControllerCreateLayerResponse create_layer_rsp;
zx_channel_call_args_t call_args = {};
call_args.wr_bytes = &create_layer_msg;
call_args.rd_bytes = &create_layer_rsp;
call_args.wr_num_bytes = sizeof(create_layer_msg);
call_args.rd_num_bytes = sizeof(create_layer_rsp);
if ((status = zx_channel_call(dc_handle, 0, ZX_TIME_INFINITE, &call_args,
&actual_bytes, &actual_handles, &read_status)) != ZX_OK) {
*err_msg_out = "Create layer call failed";
status = (status == ZX_ERR_CALL_FAILED ? read_status : status);
goto err;
}
if (create_layer_rsp.res != ZX_OK) {
*err_msg_out = "Failed to create layer";
status = create_layer_rsp.res;
goto err;
}
uint8_t fidl_bytes[sizeof(fuchsia_display_ControllerSetDisplayLayersRequest)
+ FIDL_ALIGN(sizeof(uint64_t))];
fuchsia_display_ControllerSetDisplayLayersRequest* set_display_layer_request =
(fuchsia_display_ControllerSetDisplayLayersRequest*) fidl_bytes;
*(uint64_t*)(fidl_bytes + sizeof(fuchsia_display_ControllerSetDisplayLayersRequest)) =
create_layer_rsp.layer_id;
set_display_layer_request->hdr.ordinal = fuchsia_display_ControllerSetDisplayLayersOrdinal;
set_display_layer_request->display_id = display->id;
set_display_layer_request->layer_ids.count = 1;
set_display_layer_request->layer_ids.data = (void*) FIDL_ALLOC_PRESENT;
if ((status = zx_channel_write(dc_handle, 0, fidl_bytes,
sizeof(fidl_bytes), NULL, 0)) != ZX_OK) {
*err_msg_out = "Failed to set display layers";
goto err;
}
if ((status = set_layer_config(create_layer_rsp.layer_id, mode->horizontal_resolution,
mode->vertical_resolution, pixel_format,
IMAGE_TYPE_SIMPLE)) != ZX_OK) {
*err_msg_out = "Failed to set layer config";
goto err;
}
display_id = display->id;
layer_id = create_layer_rsp.layer_id;
width = mode->horizontal_resolution;
height = mode->vertical_resolution;
format = pixel_format;
stride = stride_rsp.stride;
type_set = false;
inited = true;
if (single_buffer) {
uint32_t size = stride * height * ZX_PIXEL_FORMAT_BYTES(format);
fuchsia_display_ControllerAllocateVmoRequest alloc_msg;
alloc_msg.hdr.ordinal = fuchsia_display_ControllerAllocateVmoOrdinal;
alloc_msg.hdr.txid = txid++;
alloc_msg.size = size;
fuchsia_display_ControllerAllocateVmoResponse alloc_rsp;
zx_channel_call_args_t call_args = {};
call_args.wr_bytes = &alloc_msg;
call_args.rd_bytes = &alloc_rsp;
call_args.rd_handles = &vmo;
call_args.wr_num_bytes = sizeof(alloc_msg);
call_args.rd_num_bytes = sizeof(alloc_rsp);
call_args.rd_num_handles = 1;
uint32_t actual_bytes, actual_handles;
zx_status_t read_status;
if ((status = zx_channel_call(dc_handle, 0, ZX_TIME_INFINITE, &call_args,
&actual_bytes, &actual_handles, &read_status)) != ZX_OK) {
*err_msg_out = "Failed vmo alloc call";
goto err;
}
if (alloc_rsp.res != ZX_OK) {
status = alloc_rsp.res;
*err_msg_out = "Failed to alloc vmo";
goto err;
}
// Failure to set the cache policy isn't a fatal error
zx_vmo_set_cache_policy(vmo, ZX_CACHE_POLICY_WRITE_COMBINING);
zx_handle_t dup;
if ((status = zx_handle_duplicate(vmo, ZX_RIGHT_SAME_RIGHTS, &dup)) != ZX_OK) {
*err_msg_out = "Couldn't duplicate vmo\n";;
goto err;
}
// fb_(present|import)_image expect to not be in single buffer
// mode, so make sure this is false for now. It will get set properly later.
in_single_buffer_mode = false;
uint64_t image_id;
if ((status = fb_import_image(dup, 0, &image_id)) != ZX_OK) {
*err_msg_out = "Couldn't import framebuffer";
goto err;
}
if ((status = fb_present_image(image_id, -1, -1, -1)) != ZX_OK) {
*err_msg_out = "Failed to present single_buffer mode framebuffer";
goto err;
}
}
in_single_buffer_mode = single_buffer;
return ZX_OK;
err:
inited = false;
zx_handle_close(dc_handle);
dc_handle = ZX_HANDLE_INVALID;
if (dc_fd >= 0) {
close(dc_fd);
dc_fd = -1;
}
zx_handle_close(vmo);
vmo = ZX_HANDLE_INVALID;
return status;
}
void fb_release() {
if (!inited) {
return;
}
zx_handle_close(dc_handle);
dc_handle = ZX_HANDLE_INVALID;
close(dc_fd);
dc_fd = -1;
if (in_single_buffer_mode) {
zx_handle_close(vmo);
vmo = ZX_HANDLE_INVALID;
}
inited = false;
}
void fb_get_config(uint32_t* width_out, uint32_t* height_out,
uint32_t* linear_stride_px_out, zx_pixel_format_t* format_out) {
ZX_ASSERT(inited);
*width_out = width;
*height_out = height;
*format_out = format;
*linear_stride_px_out = stride;
}
zx_handle_t fb_get_single_buffer() {
ZX_ASSERT(inited && in_single_buffer_mode);
return vmo;
}
zx_status_t fb_import_image(zx_handle_t handle, uint32_t type, uint64_t *id_out) {
ZX_ASSERT(inited && !in_single_buffer_mode);
zx_status_t status;
if (type_set && type != image_type) {
return ZX_ERR_BAD_STATE;
} else if (!type_set && type != IMAGE_TYPE_SIMPLE) {
if ((status = set_layer_config(layer_id, width, height, format, type)) != ZX_OK) {
return status;
}
image_type = type;
type_set = true;
}
fuchsia_display_ControllerImportVmoImageRequest import_msg;
import_msg.hdr.ordinal = fuchsia_display_ControllerImportVmoImageOrdinal;
import_msg.hdr.txid = txid++;
import_msg.image_config.height = height;
import_msg.image_config.width = width;
import_msg.image_config.pixel_format = format;
import_msg.image_config.type = type;
import_msg.vmo = FIDL_HANDLE_PRESENT;
import_msg.offset = 0;
fuchsia_display_ControllerImportVmoImageResponse import_rsp;
zx_channel_call_args_t import_call = {};
import_call.wr_bytes = &import_msg;
import_call.wr_handles = &handle;
import_call.rd_bytes = &import_rsp;
import_call.wr_num_bytes = sizeof(import_msg);
import_call.wr_num_handles = 1;
import_call.rd_num_bytes = sizeof(import_rsp);
uint32_t actual_bytes, actual_handles;
zx_status_t read_status;
if ((status = zx_channel_call(dc_handle, 0, ZX_TIME_INFINITE, &import_call,
&actual_bytes, &actual_handles, &read_status)) != ZX_OK) {
if (status == ZX_ERR_CALL_FAILED) {
return read_status;
} else {
zx_handle_close(handle);
return status;
}
}
if (import_rsp.res != ZX_OK) {
return import_rsp.res;
}
*id_out = import_rsp.image_id;
return ZX_OK;
}
void fb_release_image(uint64_t image_id) {
ZX_ASSERT(inited && !in_single_buffer_mode);
fuchsia_display_ControllerReleaseEventRequest release_img_msg;
release_img_msg.hdr.ordinal = fuchsia_display_ControllerReleaseEventOrdinal;
release_img_msg.hdr.txid = txid++;
release_img_msg.id = image_id;
// There's nothing meaningful to do if this call fails
zx_channel_write(dc_handle, 0, &release_img_msg, sizeof(release_img_msg), NULL, 0);
}
zx_status_t fb_import_event(zx_handle_t handle, uint64_t id) {
ZX_ASSERT(inited && !in_single_buffer_mode);
zx_status_t status;
fuchsia_display_ControllerImportEventRequest import_evt_msg;
import_evt_msg.hdr.ordinal = fuchsia_display_ControllerImportEventOrdinal;
import_evt_msg.hdr.txid = txid++;
import_evt_msg.id = id;
import_evt_msg.event = FIDL_HANDLE_PRESENT;
if ((status = zx_channel_write(dc_handle, 0, &import_evt_msg,
sizeof(import_evt_msg), &handle, 1)) != ZX_OK) {
zx_handle_close(handle);
return status;
}
return ZX_OK;
}
void fb_release_event(uint64_t id) {
ZX_ASSERT(inited && !in_single_buffer_mode);
fuchsia_display_ControllerReleaseEventRequest release_evt_msg;
release_evt_msg.hdr.ordinal = fuchsia_display_ControllerReleaseEventOrdinal;
release_evt_msg.hdr.txid = txid++;
release_evt_msg.id = id;
// There's nothing meaningful we can do if this call fails
zx_channel_write(dc_handle, 0, &release_evt_msg, sizeof(release_evt_msg), NULL, 0);
}
zx_status_t fb_present_image(uint64_t image_id, uint64_t wait_event_id,
uint64_t present_event_id, uint64_t signal_event_id) {
ZX_ASSERT(inited && !in_single_buffer_mode);
zx_status_t status;
fuchsia_display_ControllerSetLayerImageRequest set_msg;
set_msg.hdr.ordinal = fuchsia_display_ControllerSetLayerImageOrdinal;
set_msg.hdr.txid = txid++;
set_msg.layer_id = layer_id;
set_msg.image_id = image_id;
set_msg.wait_event_id = wait_event_id;
set_msg.present_event_id = present_event_id;
set_msg.signal_event_id = signal_event_id;
if ((status = zx_channel_write(dc_handle, 0, &set_msg, sizeof(set_msg), NULL, 0)) != ZX_OK) {
return status;
}
// It's not necessary to validate the configuration, since we're guaranteed that a single
// fullscreen framebuffer on a single monitor will work.
fuchsia_display_ControllerApplyConfigRequest apply_msg;
apply_msg.hdr.txid = txid++;
apply_msg.hdr.ordinal = fuchsia_display_ControllerApplyConfigOrdinal;
return zx_channel_write(dc_handle, 0, &apply_msg, sizeof(apply_msg), NULL, 0);
}
zx_status_t fb_alloc_image_buffer(zx_handle_t* vmo_out) {
uint32_t size = stride * height * ZX_PIXEL_FORMAT_BYTES(format);
fuchsia_display_ControllerAllocateVmoRequest alloc_msg;
alloc_msg.hdr.ordinal = fuchsia_display_ControllerAllocateVmoOrdinal;
alloc_msg.hdr.txid = txid++;
alloc_msg.size = size;
fuchsia_display_ControllerAllocateVmoResponse alloc_rsp;
zx_channel_call_args_t call_args = {};
call_args.wr_bytes = &alloc_msg;
call_args.rd_bytes = &alloc_rsp;
call_args.rd_handles = vmo_out;
call_args.wr_num_bytes = sizeof(alloc_msg);
call_args.rd_num_bytes = sizeof(alloc_rsp);
call_args.rd_num_handles = 1;
uint32_t actual_bytes, actual_handles;
zx_status_t read_status, status;
if ((status = zx_channel_call(dc_handle, 0, ZX_TIME_INFINITE, &call_args,
&actual_bytes, &actual_handles, &read_status)) != ZX_OK) {
if (alloc_rsp.res != ZX_OK) {
status = alloc_rsp.res;
}
return status;
}
return ZX_OK;
}