blob: 3e939c2eed393e253f99c18e6f8cdff87eeb5b07 [file] [log] [blame]
// Copyright 2017 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 <ddk/binding.h>
#include <ddk/debug.h>
#include <ddk/device.h>
#include <ddk/driver.h>
#include <ddk/platform-defs.h>
#include <ddk/protocol/platform/device.h>
#include <fuchsia/gpu/magma/c/fidl.h>
#include <zircon/process.h>
#include <zircon/types.h>
#include <memory>
#include "magma_util/macros.h"
#include "platform_trace.h"
#include "sys_driver/magma_driver.h"
#include "sys_driver/magma_system_device.h"
#if MAGMA_TEST_DRIVER
void magma_indriver_test(zx_device_t* device);
#endif
struct gpu_device {
zx_device_t* parent_device;
zx_device_t* zx_device;
std::unique_ptr<MagmaDriver> magma_driver;
std::shared_ptr<MagmaSystemDevice> magma_system_device;
std::mutex magma_mutex;
};
gpu_device* get_gpu_device(void* context) { return static_cast<gpu_device*>(context); }
static zx_status_t magma_start(gpu_device* gpu)
{
gpu->magma_system_device = gpu->magma_driver->CreateDevice(gpu->parent_device);
if (!gpu->magma_system_device)
return DRET_MSG(ZX_ERR_NO_RESOURCES, "Failed to create device");
return ZX_OK;
}
static zx_status_t magma_stop(gpu_device* gpu)
{
gpu->magma_system_device->Shutdown();
gpu->magma_system_device.reset();
return ZX_OK;
}
static zx_status_t device_open(void* context, zx_device_t** out, uint32_t flags) { return ZX_OK; }
static zx_status_t device_close(void* context, uint32_t flags) { return ZX_OK; }
static zx_status_t device_fidl_query(void* context, uint64_t query_id, fidl_txn_t* transaction)
{
DLOG("device_fidl_query");
gpu_device* device = get_gpu_device(context);
uint64_t result;
switch (query_id) {
case MAGMA_QUERY_DEVICE_ID:
result = device->magma_system_device->GetDeviceId();
break;
case MAGMA_QUERY_IS_TEST_RESTART_SUPPORTED:
#if MAGMA_TEST_DRIVER
result = 1;
#else
result = 0;
#endif
default:
if (!device->magma_system_device->Query(query_id, &result))
return DRET_MSG(ZX_ERR_INVALID_ARGS, "unhandled query param 0x%" PRIx64, result);
}
DLOG("query query_id 0x%" PRIx64 " returning 0x%" PRIx64, query_id, result);
zx_status_t status = fuchsia_gpu_magma_DeviceQuery_reply(transaction, result);
if (status != ZX_OK)
return DRET_MSG(ZX_ERR_INTERNAL, "magma_DeviceQuery_reply failed: %d", status);
return ZX_OK;
}
static zx_status_t device_fidl_connect(void* context, uint64_t client_id, fidl_txn_t* transaction)
{
DLOG("magma_DeviceConnectOrdinal");
gpu_device* device = get_gpu_device(context);
auto connection = MagmaSystemDevice::Open(device->magma_system_device, client_id);
if (!connection)
return DRET_MSG(ZX_ERR_INVALID_ARGS, "MagmaSystemDevice::Open failed");
zx_status_t status = fuchsia_gpu_magma_DeviceConnect_reply(
transaction, connection->GetClientEndpoint(), connection->GetClientNotificationEndpoint());
if (status != ZX_OK)
return DRET_MSG(ZX_ERR_INTERNAL, "magma_DeviceConnect_reply failed: %d", status);
device->magma_system_device->StartConnectionThread(std::move(connection));
return ZX_OK;
}
static zx_status_t device_fidl_dump_state(void* context, uint32_t dump_type)
{
DLOG("device_fidl_dump_state");
if (dump_type & ~(MAGMA_DUMP_TYPE_NORMAL | MAGMA_DUMP_TYPE_PERF_COUNTERS |
MAGMA_DUMP_TYPE_PERF_COUNTER_ENABLE))
return DRET_MSG(ZX_ERR_INVALID_ARGS, "Invalid dump type %d", dump_type);
gpu_device* device = get_gpu_device(context);
std::unique_lock<std::mutex> lock(device->magma_mutex);
if (device->magma_system_device)
device->magma_system_device->DumpStatus(dump_type);
return ZX_OK;
}
static zx_status_t device_fidl_test_restart(void* context)
{
#if MAGMA_TEST_DRIVER
DLOG("device_fidl_test_restart");
gpu_device* device = get_gpu_device(context);
std::unique_lock<std::mutex> lock(device->magma_mutex);
zx_status_t status = magma_stop(device);
if (status != ZX_OK)
return DRET_MSG(status, "magma_stop failed");
return magma_start(device);
#else
return ZX_ERR_NOT_SUPPORTED;
#endif
}
static fuchsia_gpu_magma_Device_ops_t device_fidl_ops = {
.Query = device_fidl_query,
.Connect = device_fidl_connect,
.DumpState = device_fidl_dump_state,
.TestRestart = device_fidl_test_restart,
};
static zx_status_t device_message(void* context, fidl_msg_t* message, fidl_txn_t* transaction)
{
return fuchsia_gpu_magma_Device_dispatch(context, transaction, message, &device_fidl_ops);
}
static void device_release(void* context)
{
gpu_device* device = get_gpu_device(context);
{
std::unique_lock<std::mutex> lock(device->magma_mutex);
magma_stop(device);
}
delete device;
}
static zx_protocol_device_t device_proto = {
.version = DEVICE_OPS_VERSION,
.open = device_open,
.close = device_close,
.message = device_message,
.release = device_release,
};
static zx_status_t driver_bind(void* context, zx_device_t* parent)
{
magma::log(magma::LOG_INFO, "driver_bind: binding\n");
auto gpu = std::make_unique<gpu_device>();
if (!gpu)
return ZX_ERR_NO_MEMORY;
gpu->parent_device = parent;
if (magma::PlatformTrace::Get())
magma::PlatformTrace::Get()->Initialize();
gpu->magma_driver = MagmaDriver::Create();
#if MAGMA_TEST_DRIVER
DLOG("running magma indriver test");
magma_indriver_test(parent);
#endif
zx_status_t status = magma_start(gpu.get());
if (status != ZX_OK)
return status;
device_add_args_t args = {};
args.version = DEVICE_ADD_ARGS_VERSION;
args.name = "magma_gpu";
args.ctx = gpu.get();
args.ops = &device_proto;
args.proto_id = ZX_PROTOCOL_GPU;
status = device_add(parent, &args, &gpu->zx_device);
if (status != ZX_OK)
return DRET_MSG(status, "device_add failed");
gpu.release();
return ZX_OK;
}
zx_driver_ops_t msd_driver_ops = {
.version = DRIVER_OPS_VERSION,
.bind = driver_bind,
};