NV: Expose the nvdisp_disp0 device and a weak reference to the nvdrv:a service.

NVFlinger will call into the nvdisp_disp0 device to perform screen flips, bypassing the ioctl interface.

We now have the address of the framebuffer to draw, we just need to actually put it on the screen.
This commit is contained in:
Subv 2018-01-08 21:30:22 -05:00 committed by bunnei
parent e21fbd9ae5
commit 34ae2ec644
6 changed files with 276 additions and 196 deletions

View file

@ -8,9 +8,13 @@
namespace Service {
namespace NVDRV {
std::weak_ptr<NVDRV_A> nvdrv_a;
void InstallInterfaces(SM::ServiceManager& service_manager) {
std::make_shared<NVDRV_A>()->InstallAsService(service_manager);
auto nvdrv = std::make_shared<NVDRV_A>();
nvdrv->InstallAsService(service_manager);
nvdrv_a = nvdrv;
}
} // namespace nvdrv
} // namespace NVDRV
} // namespace Service

View file

@ -4,6 +4,8 @@
#pragma once
#include <memory>
#include <unordered_map>
#include <vector>
#include "common/common_types.h"
#include "core/hle/service/service.h"
@ -18,6 +20,98 @@ public:
virtual u32 ioctl(u32 command, const std::vector<u8>& input, std::vector<u8>& output) = 0;
};
class nvmap : public nvdevice {
public:
/// Returns the allocated address of an nvmap object given its handle.
VAddr GetObjectAddress(u32 handle) const;
u32 ioctl(u32 command, const std::vector<u8>& input, std::vector<u8>& output) override;
private:
// Represents an nvmap object.
struct Object {
enum class Status { Created, Allocated };
u32 id;
u32 size;
u32 flags;
u32 align;
u8 kind;
VAddr addr;
Status status;
};
u32 next_handle = 1;
u32 next_id = 1;
std::unordered_map<u32, std::shared_ptr<Object>> handles;
enum IoctlCommands {
IocCreateCommand = 0xC0080101,
IocFromIdCommand = 0xC0080103,
IocAllocCommand = 0xC0200104,
IocParamCommand = 0xC00C0109,
IocGetIdCommand = 0xC008010E
};
struct IocCreateParams {
// Input
u32_le size;
// Output
u32_le handle;
};
struct IocAllocParams {
// Input
u32_le handle;
u32_le heap_mask;
u32_le flags;
u32_le align;
u8 kind;
INSERT_PADDING_BYTES(7);
u64_le addr;
};
struct IocGetIdParams {
// Output
u32_le id;
// Input
u32_le handle;
};
struct IocFromIdParams {
// Input
u32_le id;
// Output
u32_le handle;
};
struct IocParamParams {
// Input
u32_le handle;
u32_le type;
// Output
u32_le value;
};
u32 IocCreate(const std::vector<u8>& input, std::vector<u8>& output);
u32 IocAlloc(const std::vector<u8>& input, std::vector<u8>& output);
u32 IocGetId(const std::vector<u8>& input, std::vector<u8>& output);
u32 IocFromId(const std::vector<u8>& input, std::vector<u8>& output);
u32 IocParam(const std::vector<u8>& input, std::vector<u8>& output);
};
class nvdisp_disp0 : public nvdevice {
public:
nvdisp_disp0(std::shared_ptr<nvmap> nvmap_dev) : nvdevice(), nvmap_dev(std::move(nvmap_dev)) {}
~nvdisp_disp0() = default;
u32 ioctl(u32 command, const std::vector<u8>& input, std::vector<u8>& output) override;
/// Performs a screen flip, drawing the buffer pointed to by the handle.
void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride);
private:
std::shared_ptr<nvmap> nvmap_dev;
};
/// Registers all NVDRV services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager);

View file

@ -18,9 +18,16 @@ public:
}
};
class nvmap : public nvdevice {
public:
u32 ioctl(u32 command, const std::vector<u8>& input, std::vector<u8>& output) override {
VAddr nvmap::GetObjectAddress(u32 handle) const {
auto itr = handles.find(handle);
ASSERT(itr != handles.end());
auto object = itr->second;
ASSERT(object->status == Object::Status::Allocated);
return object->addr;
}
u32 nvmap::ioctl(u32 command, const std::vector<u8>& input, std::vector<u8>& output) {
switch (command) {
case IocCreateCommand:
return IocCreate(input, output);
@ -37,72 +44,7 @@ public:
ASSERT(false, "Unimplemented");
}
private:
// Represents an nvmap object.
struct Object {
enum class Status { Created, Allocated };
u32 id;
u32 size;
u32 flags;
u32 align;
u8 kind;
u64 addr;
Status status;
};
u32 next_handle = 1;
u32 next_id = 1;
std::unordered_map<u32, std::shared_ptr<Object>> handles;
enum IoctlCommands {
IocCreateCommand = 0xC0080101,
IocFromIdCommand = 0xC0080103,
IocAllocCommand = 0xC0200104,
IocParamCommand = 0xC00C0109,
IocGetIdCommand = 0xC008010E
};
struct IocCreateParams {
// Input
u32_le size;
// Output
u32_le handle;
};
struct IocAllocParams {
// Input
u32_le handle;
u32_le heap_mask;
u32_le flags;
u32_le align;
u8 kind;
INSERT_PADDING_BYTES(7);
u64_le addr;
};
struct IocGetIdParams {
// Output
u32_le id;
// Input
u32_le handle;
};
struct IocFromIdParams {
// Input
u32_le id;
// Output
u32_le handle;
};
struct IocParamParams {
// Input
u32_le handle;
u32_le type;
// Output
u32_le value;
};
u32 IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
IocCreateParams params;
std::memcpy(&params, input.data(), sizeof(params));
@ -123,7 +65,7 @@ private:
return 0;
}
u32 IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
IocAllocParams params;
std::memcpy(&params, input.data(), sizeof(params));
@ -143,7 +85,7 @@ private:
return 0;
}
u32 IocGetId(const std::vector<u8>& input, std::vector<u8>& output) {
u32 nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) {
IocGetIdParams params;
std::memcpy(&params, input.data(), sizeof(params));
@ -158,7 +100,7 @@ private:
return 0;
}
u32 IocFromId(const std::vector<u8>& input, std::vector<u8>& output) {
u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) {
IocFromIdParams params;
std::memcpy(&params, input.data(), sizeof(params));
@ -178,7 +120,7 @@ private:
return 0;
}
u32 IocParam(const std::vector<u8>& input, std::vector<u8>& output) {
u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) {
enum class ParamTypes { Size = 1, Alignment = 2, Base = 3, Heap = 4, Kind = 5, Compr = 6 };
IocParamParams params;
@ -213,7 +155,19 @@ private:
std::memcpy(output.data(), &params, sizeof(params));
return 0;
}
};
u32 nvdisp_disp0::ioctl(u32 command, const std::vector<u8>& input, std::vector<u8>& output) {
ASSERT(false, "Unimplemented");
return 0;
}
void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height,
u32 stride) {
VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle);
LOG_WARNING(Service,
"Drawing from address %llx offset %08X Width %u Height %u Stride %u Format %u",
addr, offset, width, height, stride, format);
}
void NVDRV_A::Open(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
@ -275,8 +229,10 @@ NVDRV_A::NVDRV_A() : ServiceFramework("nvdrv:a") {
};
RegisterHandlers(functions);
auto nvmap_dev = std::make_shared<nvmap>();
devices["/dev/nvhost-as-gpu"] = std::make_shared<nvhost_as_gpu>();
devices["/dev/nvmap"] = std::make_shared<nvmap>();
devices["/dev/nvmap"] = nvmap_dev;
devices["/dev/nvdisp_disp0"] = std::make_shared<nvdisp_disp0>(nvmap_dev);
}
} // namespace NVDRV

View file

@ -4,8 +4,9 @@
#pragma once
#include "core/hle/service/service.h"
#include <memory>
#include "core/hle/service/service.h"
#include "core/hle/service/nvdrv/nvdrv.h"
namespace Service {
namespace NVDRV {
@ -15,6 +16,15 @@ public:
NVDRV_A();
~NVDRV_A() = default;
/// Returns a pointer to one of the available devices, identified by its name.
template <typename T>
std::shared_ptr<T> GetDevice(std::string name) {
auto itr = devices.find(name);
if (itr == devices.end())
return nullptr;
return std::static_pointer_cast<T>(itr->second);
}
private:
void Open(Kernel::HLERequestContext& ctx);
void Ioctl(Kernel::HLERequestContext& ctx);
@ -26,5 +36,7 @@ private:
std::unordered_map<std::string, std::shared_ptr<nvdevice>> devices;
};
extern std::weak_ptr<NVDRV_A> nvdrv_a;
} // namespace NVDRV
} // namespace Service

View file

@ -6,6 +6,7 @@
#include "common/scope_exit.h"
#include "core/core_timing.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/nvdrv/nvdrv_a.h"
#include "core/hle/service/vi/vi.h"
#include "core/hle/service/vi/vi_m.h"
@ -743,7 +744,19 @@ void NVFlinger::Compose() {
continue;
}
// TODO(Subv): Send the buffer to the GPU for drawing.
auto& igbp_buffer = buffer->igbp_buffer;
// Now send the buffer to the GPU for drawing.
auto nvdrv = NVDRV::nvdrv_a.lock();
ASSERT(nvdrv);
// TODO(Subv): Support more than just disp0. The display device selection is probably based
// on which display we're drawing (Default, Internal, External, etc)
auto nvdisp = nvdrv->GetDevice<NVDRV::nvdisp_disp0>("/dev/nvdisp_disp0");
ASSERT(nvdisp);
nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.format,
igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride);
buffer_queue->ReleaseBuffer(buffer->slot);
}

View file

@ -25,7 +25,8 @@ struct IGBPBuffer {
u32_le gpu_buffer_id;
INSERT_PADDING_WORDS(17);
u32_le nvmap_handle;
INSERT_PADDING_WORDS(61);
u32_le offset;
INSERT_PADDING_WORDS(60);
};
static_assert(sizeof(IGBPBuffer) == 0x16C, "IGBPBuffer has wrong size");