| // 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 "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; |
| |
| 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, |
| }; |