blob: ccf748c33b7e8a521fcaa401a212e06c5dda6de9 [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.
#ifndef MSD_VSL_DEVICE_H
#define MSD_VSL_DEVICE_H
#include <memory>
#include <mutex>
#include <thread>
#include "gpu_features.h"
#include "magma_util/macros.h"
#include "magma_util/register_io.h"
#include "magma_vsl_gc_types.h"
#include "msd.h"
#include "msd_vsl_connection.h"
#include "page_table_arrays.h"
#include "page_table_slot_allocator.h"
#include "platform_bus_mapper.h"
#include "platform_device.h"
#include "platform_semaphore.h"
#include "ringbuffer.h"
class MsdVslDevice : public msd_device_t, public MsdVslConnection::Owner {
public:
// Creates a device for the given |device_handle| and returns ownership.
static std::unique_ptr<MsdVslDevice> Create(void* device_handle);
MsdVslDevice() { magic_ = kMagic; }
virtual ~MsdVslDevice();
uint32_t device_id() { return device_id_; }
bool IsIdle();
bool StopRingbuffer();
std::unique_ptr<MsdVslConnection> Open(msd_client_id_t client_id);
magma_status_t ChipIdentity(magma_vsl_gc_chip_identity* out_identity);
magma_status_t ChipOption(magma_vsl_gc_chip_option* out_option);
static MsdVslDevice* cast(msd_device_t* dev) {
DASSERT(dev);
DASSERT(dev->magic_ == kMagic);
return static_cast<MsdVslDevice*>(dev);
}
private:
// The hardware provides 30 bits for interrupt events and 2 bits for errors.
static constexpr uint32_t kNumEvents = 30;
struct Event {
bool allocated = false;
bool submitted = false;
// TODO(fxb/43238): this should link to the command buffer which stores the semaphores.
std::shared_ptr<magma::PlatformSemaphore> signal;
};
bool Init(void* device_handle);
bool HardwareInit();
void Reset();
void DisableInterrupts();
// Processes the hardware interrupts.
int InterruptThreadLoop();
// Events for triggering interrupts.
bool AllocInterruptEvent(uint32_t* out_event_id);
bool FreeInterruptEvent(uint32_t event_id);
// Writes a new interrupt event to the end of the ringbuffer. The event must have been allocated
// using |AllocInterruptEvent|.
bool WriteInterruptEvent(uint32_t event_id, std::shared_ptr<magma::PlatformSemaphore> signal);
bool CompleteInterruptEvent(uint32_t event_id);
// Returns true if starting the ringbuffer succeeded, or the ringbuffer was already running.
bool StartRingbuffer(std::shared_ptr<AddressSpace> address_space);
// Adds a WAIT-LINK to the end of the ringbuffer.
bool AddRingbufferWaitLink();
// Modifies the last WAIT in the ringbuffer to link to |gpu_addr|.
// |wait_link_offset| is the offset into the ringbuffer of the WAIT-LINK to replace.
// |dest_prefetch| is the prefetch of the buffer we are linking to.
bool LinkRingbuffer(uint32_t wait_link_offset, uint32_t gpu_addr, uint32_t dest_prefetch);
// Writes a LINK command at the end of the given buffer.
bool WriteLinkCommand(magma::PlatformBuffer* buf, uint32_t length,
uint16_t prefetch, uint32_t link_addr);
// Returns whether the device became idle before |timeout_ms| elapsed.
bool WaitUntilIdle(uint32_t timeout_ms);
bool LoadInitialAddressSpace(std::shared_ptr<AddressSpace>, uint32_t address_space_index);
// If |prefetch_out| is not null, it will be populated with the prefetch that was submitted
// to the device.
bool SubmitCommandBufferNoMmu(uint64_t bus_addr, uint32_t length,
uint16_t* prefetch_out = nullptr);
bool SubmitCommandBuffer(std::shared_ptr<AddressSpace>, uint32_t address_space_index,
magma::PlatformBuffer* buf, uint32_t gpu_addr, uint32_t length,
uint32_t event_id, std::shared_ptr<magma::PlatformSemaphore> signal,
uint16_t* prefetch_out);
magma::RegisterIo* register_io() { return register_io_.get(); }
// MsdVslConnection::Owner
magma::PlatformBusMapper* GetBusMapper() override { return bus_mapper_.get(); }
void ConnectionReleased(MsdVslConnection* connection) override {
page_table_slot_allocator_->Free(connection->page_table_array_slot());
}
PageTableArrays* page_table_arrays() { return page_table_arrays_.get(); }
static const uint32_t kMagic = 0x64657669; //"devi"
std::unique_ptr<magma::PlatformDevice> platform_device_;
std::unique_ptr<magma::RegisterIo> register_io_;
std::unique_ptr<GpuFeatures> gpu_features_;
uint32_t device_id_ = 0;
std::unique_ptr<magma::PlatformBusMapper> bus_mapper_;
std::unique_ptr<PageTableArrays> page_table_arrays_;
std::unique_ptr<PageTableSlotAllocator> page_table_slot_allocator_;
// The command queue.
std::unique_ptr<Ringbuffer> ringbuffer_;
std::thread interrupt_thread_;
std::unique_ptr<magma::PlatformInterrupt> interrupt_;
std::atomic_bool stop_interrupt_thread_{false};
MAGMA_GUARDED(events_mutex_) Event events_[kNumEvents] = {};
// TODO(fxb/43235): this can be removed once we process events on the device thread.
std::mutex events_mutex_;
friend class TestMsdVslDevice;
friend class TestEvents;
friend class TestEvents_AllocAndFree_Test;
friend class TestEvents_Submit_Test;
friend class TestEvents_Write_Test;
friend class MsdVslDeviceTest_FetchEngineDma_Test;
friend class MsdVslDeviceTest_LoadAddressSpace_Test;
};
#endif // MSD_VSL_DEVICE_H