diff options
| author | 2017-10-12 21:45:06 -0400 | |
|---|---|---|
| committer | 2017-10-12 21:45:06 -0400 | |
| commit | 15983dcfdc2e9648b07c910a1ad2a2813e8b7fa7 (patch) | |
| tree | 38e5af4732f8c145a169a7de15c6827c40c1f8db /src | |
| parent | Remove more 3DS-specific code. (diff) | |
| download | yuzu-15983dcfdc2e9648b07c910a1ad2a2813e8b7fa7.tar.gz yuzu-15983dcfdc2e9648b07c910a1ad2a2813e8b7fa7.tar.xz yuzu-15983dcfdc2e9648b07c910a1ad2a2813e8b7fa7.zip | |
Remove more 3DS-specific code.
Diffstat (limited to 'src')
| -rw-r--r-- | src/citra_qt/debugger/graphics/graphics.cpp | 2 | ||||
| -rw-r--r-- | src/core/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/core/hle/service/dsp_dsp.cpp | 581 | ||||
| -rw-r--r-- | src/core/hle/service/gsp_gpu.cpp | 778 | ||||
| -rw-r--r-- | src/core/hle/service/gsp_lcd.cpp | 29 | ||||
| -rw-r--r-- | src/core/hle/service/gsp_lcd.h | 22 | ||||
| -rw-r--r-- | src/core/hle/service/service.cpp | 2 |
7 files changed, 2 insertions, 1414 deletions
diff --git a/src/citra_qt/debugger/graphics/graphics.cpp b/src/citra_qt/debugger/graphics/graphics.cpp index 6a76adeae..8154363a2 100644 --- a/src/citra_qt/debugger/graphics/graphics.cpp +++ b/src/citra_qt/debugger/graphics/graphics.cpp | |||
| @@ -6,7 +6,7 @@ | |||
| 6 | #include "citra_qt/debugger/graphics/graphics.h" | 6 | #include "citra_qt/debugger/graphics/graphics.h" |
| 7 | #include "citra_qt/util/util.h" | 7 | #include "citra_qt/util/util.h" |
| 8 | 8 | ||
| 9 | extern GraphicsDebugger g_debugger; | 9 | GraphicsDebugger g_debugger; |
| 10 | 10 | ||
| 11 | GPUCommandStreamItemModel::GPUCommandStreamItemModel(QObject* parent) | 11 | GPUCommandStreamItemModel::GPUCommandStreamItemModel(QObject* parent) |
| 12 | : QAbstractListModel(parent), command_count(0) { | 12 | : QAbstractListModel(parent), command_count(0) { |
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 7606e50fc..8fa12bef3 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -50,7 +50,6 @@ set(SRCS | |||
| 50 | hle/romfs.cpp | 50 | hle/romfs.cpp |
| 51 | hle/service/dsp_dsp.cpp | 51 | hle/service/dsp_dsp.cpp |
| 52 | hle/service/gsp_gpu.cpp | 52 | hle/service/gsp_gpu.cpp |
| 53 | hle/service/gsp_lcd.cpp | ||
| 54 | hle/service/hid/hid.cpp | 53 | hle/service/hid/hid.cpp |
| 55 | hle/service/service.cpp | 54 | hle/service/service.cpp |
| 56 | hle/service/sm/sm.cpp | 55 | hle/service/sm/sm.cpp |
| @@ -139,7 +138,6 @@ set(HEADERS | |||
| 139 | hle/romfs.h | 138 | hle/romfs.h |
| 140 | hle/service/dsp_dsp.h | 139 | hle/service/dsp_dsp.h |
| 141 | hle/service/gsp_gpu.h | 140 | hle/service/gsp_gpu.h |
| 142 | hle/service/gsp_lcd.h | ||
| 143 | hle/service/hid/hid.h | 141 | hle/service/hid/hid.h |
| 144 | hle/service/service.h | 142 | hle/service/service.h |
| 145 | hle/service/sm/sm.h | 143 | hle/service/sm/sm.h |
diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp index 42f8950f9..7eb1a01b7 100644 --- a/src/core/hle/service/dsp_dsp.cpp +++ b/src/core/hle/service/dsp_dsp.cpp | |||
| @@ -2,605 +2,24 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | ||
| 6 | #include <array> | ||
| 7 | #include <cinttypes> | ||
| 8 | #include "audio_core/hle/pipe.h" | 5 | #include "audio_core/hle/pipe.h" |
| 9 | #include "common/assert.h" | ||
| 10 | #include "common/hash.h" | ||
| 11 | #include "common/logging/log.h" | ||
| 12 | #include "core/hle/ipc.h" | ||
| 13 | #include "core/hle/kernel/event.h" | ||
| 14 | #include "core/hle/kernel/handle_table.h" | ||
| 15 | #include "core/hle/result.h" | ||
| 16 | #include "core/hle/service/dsp_dsp.h" | 6 | #include "core/hle/service/dsp_dsp.h" |
| 17 | #include "core/memory.h" | ||
| 18 | 7 | ||
| 19 | using DspPipe = DSP::HLE::DspPipe; | 8 | using DspPipe = DSP::HLE::DspPipe; |
| 20 | 9 | ||
| 21 | namespace Service { | 10 | namespace Service { |
| 22 | namespace DSP_DSP { | 11 | namespace DSP_DSP { |
| 23 | 12 | ||
| 24 | static Kernel::SharedPtr<Kernel::Event> semaphore_event; | ||
| 25 | |||
| 26 | /// There are three types of interrupts | ||
| 27 | enum class InterruptType { Zero, One, Pipe }; | ||
| 28 | constexpr size_t NUM_INTERRUPT_TYPE = 3; | ||
| 29 | |||
| 30 | class InterruptEvents final { | ||
| 31 | public: | ||
| 32 | void Signal(InterruptType type, DspPipe pipe) { | ||
| 33 | Kernel::SharedPtr<Kernel::Event>& event = Get(type, pipe); | ||
| 34 | if (event) { | ||
| 35 | event->Signal(); | ||
| 36 | } | ||
| 37 | } | ||
| 38 | |||
| 39 | Kernel::SharedPtr<Kernel::Event>& Get(InterruptType type, DspPipe dsp_pipe) { | ||
| 40 | switch (type) { | ||
| 41 | case InterruptType::Zero: | ||
| 42 | return zero; | ||
| 43 | case InterruptType::One: | ||
| 44 | return one; | ||
| 45 | case InterruptType::Pipe: { | ||
| 46 | const size_t pipe_index = static_cast<size_t>(dsp_pipe); | ||
| 47 | ASSERT(pipe_index < DSP::HLE::NUM_DSP_PIPE); | ||
| 48 | return pipe[pipe_index]; | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | UNREACHABLE_MSG("Invalid interrupt type = %zu", static_cast<size_t>(type)); | ||
| 53 | } | ||
| 54 | |||
| 55 | bool HasTooManyEventsRegistered() const { | ||
| 56 | // Actual service implementation only has 6 'slots' for interrupts. | ||
| 57 | constexpr size_t max_number_of_interrupt_events = 6; | ||
| 58 | |||
| 59 | size_t number = | ||
| 60 | std::count_if(pipe.begin(), pipe.end(), [](const auto& evt) { return evt != nullptr; }); | ||
| 61 | |||
| 62 | if (zero != nullptr) | ||
| 63 | number++; | ||
| 64 | if (one != nullptr) | ||
| 65 | number++; | ||
| 66 | |||
| 67 | return number >= max_number_of_interrupt_events; | ||
| 68 | } | ||
| 69 | |||
| 70 | private: | ||
| 71 | /// Currently unknown purpose | ||
| 72 | Kernel::SharedPtr<Kernel::Event> zero = nullptr; | ||
| 73 | /// Currently unknown purpose | ||
| 74 | Kernel::SharedPtr<Kernel::Event> one = nullptr; | ||
| 75 | /// Each DSP pipe has an associated interrupt | ||
| 76 | std::array<Kernel::SharedPtr<Kernel::Event>, DSP::HLE::NUM_DSP_PIPE> pipe = {{}}; | ||
| 77 | }; | ||
| 78 | |||
| 79 | static InterruptEvents interrupt_events; | ||
| 80 | |||
| 81 | // DSP Interrupts: | ||
| 82 | // The audio-pipe interrupt occurs every frame tick. Userland programs normally have a thread | ||
| 83 | // that's waiting for an interrupt event. Immediately after this interrupt event, userland | ||
| 84 | // normally updates the state in the next region and increments the relevant frame counter by | ||
| 85 | // two. | ||
| 86 | void SignalPipeInterrupt(DspPipe pipe) { | 13 | void SignalPipeInterrupt(DspPipe pipe) { |
| 87 | interrupt_events.Signal(InterruptType::Pipe, pipe); | ||
| 88 | } | ||
| 89 | |||
| 90 | /** | ||
| 91 | * DSP_DSP::ConvertProcessAddressFromDspDram service function | ||
| 92 | * Inputs: | ||
| 93 | * 1 : Address | ||
| 94 | * Outputs: | ||
| 95 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 96 | * 2 : (inaddr << 1) + 0x1FF40000 (where 0x1FF00000 is the DSP RAM address) | ||
| 97 | */ | ||
| 98 | static void ConvertProcessAddressFromDspDram(Service::Interface* self) { | ||
| 99 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 100 | |||
| 101 | u32 addr = cmd_buff[1]; | ||
| 102 | |||
| 103 | cmd_buff[0] = IPC::MakeHeader(0xC, 2, 0); | ||
| 104 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 105 | |||
| 106 | // TODO(merry): There is a per-region offset missing in this calculation (that seems to be | ||
| 107 | // always zero). | ||
| 108 | cmd_buff[2] = (addr << 1) + (Memory::DSP_RAM_VADDR + 0x40000); | ||
| 109 | |||
| 110 | LOG_DEBUG(Service_DSP, "addr=0x%08X", addr); | ||
| 111 | } | ||
| 112 | |||
| 113 | /** | ||
| 114 | * DSP_DSP::LoadComponent service function | ||
| 115 | * Inputs: | ||
| 116 | * 1 : Size | ||
| 117 | * 2 : Program mask (observed only half word used) | ||
| 118 | * 3 : Data mask (observed only half word used) | ||
| 119 | * 4 : (size << 4) | 0xA | ||
| 120 | * 5 : Buffer address | ||
| 121 | * Outputs: | ||
| 122 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 123 | * 2 : Component loaded, 0 on not loaded, 1 on loaded | ||
| 124 | */ | ||
| 125 | static void LoadComponent(Service::Interface* self) { | ||
| 126 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 127 | |||
| 128 | u32 size = cmd_buff[1]; | ||
| 129 | u32 prog_mask = cmd_buff[2]; | ||
| 130 | u32 data_mask = cmd_buff[3]; | ||
| 131 | u32 desc = cmd_buff[4]; | ||
| 132 | u32 buffer = cmd_buff[5]; | ||
| 133 | |||
| 134 | cmd_buff[0] = IPC::MakeHeader(0x11, 2, 2); | ||
| 135 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 136 | cmd_buff[2] = 1; // Pretend that we actually loaded the DSP firmware | ||
| 137 | cmd_buff[3] = desc; | ||
| 138 | cmd_buff[4] = buffer; | ||
| 139 | |||
| 140 | // TODO(bunnei): Implement real DSP firmware loading | ||
| 141 | |||
| 142 | ASSERT(Memory::IsValidVirtualAddress(buffer)); | ||
| 143 | |||
| 144 | std::vector<u8> component_data(size); | ||
| 145 | Memory::ReadBlock(buffer, component_data.data(), component_data.size()); | ||
| 146 | |||
| 147 | LOG_INFO(Service_DSP, "Firmware hash: %#" PRIx64, | ||
| 148 | Common::ComputeHash64(component_data.data(), component_data.size())); | ||
| 149 | // Some versions of the firmware have the location of DSP structures listed here. | ||
| 150 | if (size > 0x37C) { | ||
| 151 | LOG_INFO(Service_DSP, "Structures hash: %#" PRIx64, | ||
| 152 | Common::ComputeHash64(component_data.data() + 0x340, 60)); | ||
| 153 | } | ||
| 154 | |||
| 155 | LOG_WARNING(Service_DSP, | ||
| 156 | "(STUBBED) called size=0x%X, prog_mask=0x%08X, data_mask=0x%08X, buffer=0x%08X", | ||
| 157 | size, prog_mask, data_mask, buffer); | ||
| 158 | } | ||
| 159 | |||
| 160 | /** | ||
| 161 | * DSP_DSP::GetSemaphoreEventHandle service function | ||
| 162 | * Outputs: | ||
| 163 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 164 | * 3 : Semaphore event handle | ||
| 165 | */ | ||
| 166 | static void GetSemaphoreEventHandle(Service::Interface* self) { | ||
| 167 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 168 | |||
| 169 | cmd_buff[0] = IPC::MakeHeader(0x16, 1, 2); | ||
| 170 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 171 | // cmd_buff[2] not set | ||
| 172 | cmd_buff[3] = Kernel::g_handle_table.Create(semaphore_event).Unwrap(); // Event handle | ||
| 173 | |||
| 174 | LOG_WARNING(Service_DSP, "(STUBBED) called"); | ||
| 175 | } | ||
| 176 | |||
| 177 | /** | ||
| 178 | * DSP_DSP::FlushDataCache service function | ||
| 179 | * | ||
| 180 | * This Function is a no-op, We aren't emulating the CPU cache any time soon. | ||
| 181 | * | ||
| 182 | * Inputs: | ||
| 183 | * 1 : Address | ||
| 184 | * 2 : Size | ||
| 185 | * 3 : Value 0, some descriptor for the KProcess Handle | ||
| 186 | * 4 : KProcess handle | ||
| 187 | * Outputs: | ||
| 188 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 189 | */ | ||
| 190 | static void FlushDataCache(Service::Interface* self) { | ||
| 191 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 192 | u32 address = cmd_buff[1]; | ||
| 193 | u32 size = cmd_buff[2]; | ||
| 194 | u32 process = cmd_buff[4]; | ||
| 195 | |||
| 196 | cmd_buff[0] = IPC::MakeHeader(0x13, 1, 0); | ||
| 197 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 198 | |||
| 199 | LOG_TRACE(Service_DSP, "called address=0x%08X, size=0x%X, process=0x%08X", address, size, | ||
| 200 | process); | ||
| 201 | } | 14 | } |
| 202 | 15 | ||
| 203 | /** | ||
| 204 | * DSP_DSP::RegisterInterruptEvents service function | ||
| 205 | * Inputs: | ||
| 206 | * 1 : Interrupt Type | ||
| 207 | * 2 : Pipe Number | ||
| 208 | * 4 : Interrupt event handle | ||
| 209 | * Outputs: | ||
| 210 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 211 | */ | ||
| 212 | static void RegisterInterruptEvents(Service::Interface* self) { | ||
| 213 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 214 | |||
| 215 | u32 type_index = cmd_buff[1]; | ||
| 216 | u32 pipe_index = cmd_buff[2]; | ||
| 217 | u32 event_handle = cmd_buff[4]; | ||
| 218 | |||
| 219 | ASSERT_MSG(type_index < NUM_INTERRUPT_TYPE && pipe_index < DSP::HLE::NUM_DSP_PIPE, | ||
| 220 | "Invalid type or pipe: type = %u, pipe = %u", type_index, pipe_index); | ||
| 221 | |||
| 222 | InterruptType type = static_cast<InterruptType>(cmd_buff[1]); | ||
| 223 | DspPipe pipe = static_cast<DspPipe>(cmd_buff[2]); | ||
| 224 | |||
| 225 | cmd_buff[0] = IPC::MakeHeader(0x15, 1, 0); | ||
| 226 | |||
| 227 | if (event_handle) { | ||
| 228 | auto evt = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[4]); | ||
| 229 | |||
| 230 | if (!evt) { | ||
| 231 | LOG_INFO(Service_DSP, "Invalid event handle! type=%u, pipe=%u, event_handle=0x%08X", | ||
| 232 | type_index, pipe_index, event_handle); | ||
| 233 | ASSERT(false); // TODO: This should really be handled at an IPC translation layer. | ||
| 234 | } | ||
| 235 | |||
| 236 | if (interrupt_events.HasTooManyEventsRegistered()) { | ||
| 237 | LOG_INFO(Service_DSP, "Ran out of space to register interrupts (Attempted to register " | ||
| 238 | "type=%u, pipe=%u, event_handle=0x%08X)", | ||
| 239 | type_index, pipe_index, event_handle); | ||
| 240 | cmd_buff[1] = ResultCode(ErrorDescription::InvalidResultValue, ErrorModule::DSP, | ||
| 241 | ErrorSummary::OutOfResource, ErrorLevel::Status) | ||
| 242 | .raw; | ||
| 243 | return; | ||
| 244 | } | ||
| 245 | |||
| 246 | interrupt_events.Get(type, pipe) = evt; | ||
| 247 | LOG_INFO(Service_DSP, "Registered type=%u, pipe=%u, event_handle=0x%08X", type_index, | ||
| 248 | pipe_index, event_handle); | ||
| 249 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 250 | } else { | ||
| 251 | interrupt_events.Get(type, pipe) = nullptr; | ||
| 252 | LOG_INFO(Service_DSP, "Unregistered interrupt=%u, channel=%u, event_handle=0x%08X", | ||
| 253 | type_index, pipe_index, event_handle); | ||
| 254 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 255 | } | ||
| 256 | } | ||
| 257 | |||
| 258 | /** | ||
| 259 | * DSP_DSP::SetSemaphore service function | ||
| 260 | * Inputs: | ||
| 261 | * 1 : Unknown (observed only half word used) | ||
| 262 | * Outputs: | ||
| 263 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 264 | */ | ||
| 265 | static void SetSemaphore(Service::Interface* self) { | ||
| 266 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 267 | |||
| 268 | cmd_buff[0] = IPC::MakeHeader(0x7, 1, 0); | ||
| 269 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 270 | |||
| 271 | LOG_WARNING(Service_DSP, "(STUBBED) called"); | ||
| 272 | } | ||
| 273 | |||
| 274 | /** | ||
| 275 | * DSP_DSP::WriteProcessPipe service function | ||
| 276 | * Inputs: | ||
| 277 | * 1 : Pipe Number | ||
| 278 | * 2 : Size | ||
| 279 | * 3 : (size << 14) | 0x402 | ||
| 280 | * 4 : Buffer | ||
| 281 | * Outputs: | ||
| 282 | * 0 : Return header | ||
| 283 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 284 | */ | ||
| 285 | static void WriteProcessPipe(Service::Interface* self) { | ||
| 286 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 287 | |||
| 288 | u32 pipe_index = cmd_buff[1]; | ||
| 289 | u32 size = cmd_buff[2]; | ||
| 290 | u32 buffer = cmd_buff[4]; | ||
| 291 | |||
| 292 | DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(pipe_index); | ||
| 293 | |||
| 294 | if (IPC::StaticBufferDesc(size, 1) != cmd_buff[3]) { | ||
| 295 | LOG_ERROR(Service_DSP, "IPC static buffer descriptor failed validation (0x%X). pipe=%u, " | ||
| 296 | "size=0x%X, buffer=0x%08X", | ||
| 297 | cmd_buff[3], pipe_index, size, buffer); | ||
| 298 | cmd_buff[0] = IPC::MakeHeader(0, 1, 0); | ||
| 299 | cmd_buff[1] = IPC::ERR_INVALID_BUFFER_DESCRIPTOR.raw; | ||
| 300 | return; | ||
| 301 | } | ||
| 302 | |||
| 303 | ASSERT_MSG(Memory::IsValidVirtualAddress(buffer), | ||
| 304 | "Invalid Buffer: pipe=%u, size=0x%X, buffer=0x%08X", pipe, size, buffer); | ||
| 305 | |||
| 306 | std::vector<u8> message(size); | ||
| 307 | for (u32 i = 0; i < size; i++) { | ||
| 308 | message[i] = Memory::Read8(buffer + i); | ||
| 309 | } | ||
| 310 | |||
| 311 | // This behaviour was confirmed by RE. | ||
| 312 | // The likely reason for this is that games tend to pass in garbage at these bytes | ||
| 313 | // because they read random bytes off the stack. | ||
| 314 | switch (pipe) { | ||
| 315 | case DSP::HLE::DspPipe::Audio: | ||
| 316 | ASSERT(message.size() >= 4); | ||
| 317 | message[2] = 0; | ||
| 318 | message[3] = 0; | ||
| 319 | break; | ||
| 320 | case DSP::HLE::DspPipe::Binary: | ||
| 321 | ASSERT(message.size() >= 8); | ||
| 322 | message[4] = 1; | ||
| 323 | message[5] = 0; | ||
| 324 | message[6] = 0; | ||
| 325 | message[7] = 0; | ||
| 326 | break; | ||
| 327 | } | ||
| 328 | |||
| 329 | DSP::HLE::PipeWrite(pipe, message); | ||
| 330 | |||
| 331 | cmd_buff[0] = IPC::MakeHeader(0xD, 1, 0); | ||
| 332 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 333 | |||
| 334 | LOG_DEBUG(Service_DSP, "pipe=%u, size=0x%X, buffer=0x%08X", pipe_index, size, buffer); | ||
| 335 | } | ||
| 336 | |||
| 337 | /** | ||
| 338 | * DSP_DSP::ReadPipeIfPossible service function | ||
| 339 | * A pipe is a means of communication between the ARM11 and DSP that occurs on | ||
| 340 | * hardware by writing to/reading from the DSP registers at 0x10203000. | ||
| 341 | * Pipes are used for initialisation. See also DSP::HLE::PipeRead. | ||
| 342 | * Inputs: | ||
| 343 | * 1 : Pipe Number | ||
| 344 | * 2 : Unknown | ||
| 345 | * 3 : Size in bytes of read (observed only lower half word used) | ||
| 346 | * 0x41 : Virtual address of memory buffer to write pipe contents to | ||
| 347 | * Outputs: | ||
| 348 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 349 | * 2 : Number of bytes read from pipe | ||
| 350 | */ | ||
| 351 | static void ReadPipeIfPossible(Service::Interface* self) { | ||
| 352 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 353 | |||
| 354 | u32 pipe_index = cmd_buff[1]; | ||
| 355 | u32 unknown = cmd_buff[2]; | ||
| 356 | u32 size = cmd_buff[3] & 0xFFFF; // Lower 16 bits are size | ||
| 357 | VAddr addr = cmd_buff[0x41]; | ||
| 358 | |||
| 359 | DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(pipe_index); | ||
| 360 | |||
| 361 | ASSERT_MSG(Memory::IsValidVirtualAddress(addr), | ||
| 362 | "Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe, unknown, | ||
| 363 | size, addr); | ||
| 364 | |||
| 365 | cmd_buff[0] = IPC::MakeHeader(0x10, 1, 2); | ||
| 366 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 367 | if (DSP::HLE::GetPipeReadableSize(pipe) >= size) { | ||
| 368 | std::vector<u8> response = DSP::HLE::PipeRead(pipe, size); | ||
| 369 | |||
| 370 | Memory::WriteBlock(addr, response.data(), response.size()); | ||
| 371 | |||
| 372 | cmd_buff[2] = static_cast<u32>(response.size()); | ||
| 373 | } else { | ||
| 374 | cmd_buff[2] = 0; // Return no data | ||
| 375 | } | ||
| 376 | cmd_buff[3] = IPC::StaticBufferDesc(size, 0); | ||
| 377 | cmd_buff[4] = addr; | ||
| 378 | |||
| 379 | LOG_DEBUG(Service_DSP, | ||
| 380 | "pipe=%u, unknown=0x%08X, size=0x%X, buffer=0x%08X, return cmd_buff[2]=0x%08X", | ||
| 381 | pipe_index, unknown, size, addr, cmd_buff[2]); | ||
| 382 | } | ||
| 383 | |||
| 384 | /** | ||
| 385 | * DSP_DSP::ReadPipe service function | ||
| 386 | * Inputs: | ||
| 387 | * 1 : Pipe Number | ||
| 388 | * 2 : Unknown | ||
| 389 | * 3 : Size in bytes of read (observed only lower half word used) | ||
| 390 | * 0x41 : Virtual address of memory buffer to write pipe contents to | ||
| 391 | * Outputs: | ||
| 392 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 393 | * 2 : Number of bytes read from pipe | ||
| 394 | */ | ||
| 395 | static void ReadPipe(Service::Interface* self) { | ||
| 396 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 397 | |||
| 398 | u32 pipe_index = cmd_buff[1]; | ||
| 399 | u32 unknown = cmd_buff[2]; | ||
| 400 | u32 size = cmd_buff[3] & 0xFFFF; // Lower 16 bits are size | ||
| 401 | VAddr addr = cmd_buff[0x41]; | ||
| 402 | |||
| 403 | DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(pipe_index); | ||
| 404 | |||
| 405 | ASSERT_MSG(Memory::IsValidVirtualAddress(addr), | ||
| 406 | "Invalid addr: pipe=0x%08X, unknown=0x%08X, size=0x%X, buffer=0x%08X", pipe, unknown, | ||
| 407 | size, addr); | ||
| 408 | |||
| 409 | if (DSP::HLE::GetPipeReadableSize(pipe) >= size) { | ||
| 410 | std::vector<u8> response = DSP::HLE::PipeRead(pipe, size); | ||
| 411 | |||
| 412 | Memory::WriteBlock(addr, response.data(), response.size()); | ||
| 413 | |||
| 414 | cmd_buff[0] = IPC::MakeHeader(0xE, 2, 2); | ||
| 415 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 416 | cmd_buff[2] = static_cast<u32>(response.size()); | ||
| 417 | cmd_buff[3] = IPC::StaticBufferDesc(size, 0); | ||
| 418 | cmd_buff[4] = addr; | ||
| 419 | } else { | ||
| 420 | // No more data is in pipe. Hardware hangs in this case; this should never happen. | ||
| 421 | UNREACHABLE(); | ||
| 422 | } | ||
| 423 | |||
| 424 | LOG_DEBUG(Service_DSP, | ||
| 425 | "pipe=%u, unknown=0x%08X, size=0x%X, buffer=0x%08X, return cmd_buff[2]=0x%08X", | ||
| 426 | pipe_index, unknown, size, addr, cmd_buff[2]); | ||
| 427 | } | ||
| 428 | |||
| 429 | /** | ||
| 430 | * DSP_DSP::GetPipeReadableSize service function | ||
| 431 | * Inputs: | ||
| 432 | * 1 : Pipe Number | ||
| 433 | * 2 : Unknown | ||
| 434 | * Outputs: | ||
| 435 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 436 | * 2 : Number of bytes readable from pipe | ||
| 437 | */ | ||
| 438 | static void GetPipeReadableSize(Service::Interface* self) { | ||
| 439 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 440 | |||
| 441 | u32 pipe_index = cmd_buff[1]; | ||
| 442 | u32 unknown = cmd_buff[2]; | ||
| 443 | |||
| 444 | DSP::HLE::DspPipe pipe = static_cast<DSP::HLE::DspPipe>(pipe_index); | ||
| 445 | |||
| 446 | cmd_buff[0] = IPC::MakeHeader(0xF, 2, 0); | ||
| 447 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 448 | cmd_buff[2] = static_cast<u32>(DSP::HLE::GetPipeReadableSize(pipe)); | ||
| 449 | |||
| 450 | LOG_DEBUG(Service_DSP, "pipe=%u, unknown=0x%08X, return cmd_buff[2]=0x%08X", pipe_index, | ||
| 451 | unknown, cmd_buff[2]); | ||
| 452 | } | ||
| 453 | |||
| 454 | /** | ||
| 455 | * DSP_DSP::SetSemaphoreMask service function | ||
| 456 | * Inputs: | ||
| 457 | * 1 : Mask | ||
| 458 | * Outputs: | ||
| 459 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 460 | */ | ||
| 461 | static void SetSemaphoreMask(Service::Interface* self) { | ||
| 462 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 463 | |||
| 464 | u32 mask = cmd_buff[1]; | ||
| 465 | |||
| 466 | cmd_buff[0] = IPC::MakeHeader(0x17, 1, 0); | ||
| 467 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 468 | |||
| 469 | LOG_WARNING(Service_DSP, "(STUBBED) called mask=0x%08X", mask); | ||
| 470 | } | ||
| 471 | |||
| 472 | /** | ||
| 473 | * DSP_DSP::GetHeadphoneStatus service function | ||
| 474 | * Inputs: | ||
| 475 | * 1 : None | ||
| 476 | * Outputs: | ||
| 477 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 478 | * 2 : The headphone status response, 0 = Not using headphones?, | ||
| 479 | * 1 = using headphones? | ||
| 480 | */ | ||
| 481 | static void GetHeadphoneStatus(Service::Interface* self) { | ||
| 482 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 483 | |||
| 484 | cmd_buff[0] = IPC::MakeHeader(0x1F, 2, 0); | ||
| 485 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 486 | cmd_buff[2] = 0; // Not using headphones | ||
| 487 | |||
| 488 | LOG_DEBUG(Service_DSP, "called"); | ||
| 489 | } | ||
| 490 | |||
| 491 | /** | ||
| 492 | * DSP_DSP::RecvData service function | ||
| 493 | * This function reads a value out of a DSP register. | ||
| 494 | * Inputs: | ||
| 495 | * 1 : Register Number | ||
| 496 | * Outputs: | ||
| 497 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 498 | * 2 : Value in the register | ||
| 499 | * Notes: | ||
| 500 | * This function has only been observed being called with a register number of 0. | ||
| 501 | */ | ||
| 502 | static void RecvData(Service::Interface* self) { | ||
| 503 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 504 | |||
| 505 | u32 register_number = cmd_buff[1]; | ||
| 506 | |||
| 507 | ASSERT_MSG(register_number == 0, "Unknown register_number %u", register_number); | ||
| 508 | |||
| 509 | // Application reads this after requesting DSP shutdown, to verify the DSP has indeed shutdown | ||
| 510 | // or slept. | ||
| 511 | |||
| 512 | cmd_buff[0] = IPC::MakeHeader(0x1, 2, 0); | ||
| 513 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 514 | switch (DSP::HLE::GetDspState()) { | ||
| 515 | case DSP::HLE::DspState::On: | ||
| 516 | cmd_buff[2] = 0; | ||
| 517 | break; | ||
| 518 | case DSP::HLE::DspState::Off: | ||
| 519 | case DSP::HLE::DspState::Sleeping: | ||
| 520 | cmd_buff[2] = 1; | ||
| 521 | break; | ||
| 522 | default: | ||
| 523 | UNREACHABLE(); | ||
| 524 | break; | ||
| 525 | } | ||
| 526 | |||
| 527 | LOG_DEBUG(Service_DSP, "register_number=%u", register_number); | ||
| 528 | } | ||
| 529 | |||
| 530 | /** | ||
| 531 | * DSP_DSP::RecvDataIsReady service function | ||
| 532 | * This function checks whether a DSP register is ready to be read. | ||
| 533 | * Inputs: | ||
| 534 | * 1 : Register Number | ||
| 535 | * Outputs: | ||
| 536 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 537 | * 2 : non-zero == ready | ||
| 538 | * Note: | ||
| 539 | * This function has only been observed being called with a register number of 0. | ||
| 540 | */ | ||
| 541 | static void RecvDataIsReady(Service::Interface* self) { | ||
| 542 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 543 | |||
| 544 | u32 register_number = cmd_buff[1]; | ||
| 545 | |||
| 546 | ASSERT_MSG(register_number == 0, "Unknown register_number %u", register_number); | ||
| 547 | |||
| 548 | cmd_buff[0] = IPC::MakeHeader(0x2, 2, 0); | ||
| 549 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 550 | cmd_buff[2] = 1; // Ready to read | ||
| 551 | |||
| 552 | LOG_DEBUG(Service_DSP, "register_number=%u", register_number); | ||
| 553 | } | ||
| 554 | |||
| 555 | const Interface::FunctionInfo FunctionTable[] = { | ||
| 556 | {0x00010040, RecvData, "RecvData"}, | ||
| 557 | {0x00020040, RecvDataIsReady, "RecvDataIsReady"}, | ||
| 558 | {0x00030080, nullptr, "SendData"}, | ||
| 559 | {0x00040040, nullptr, "SendDataIsEmpty"}, | ||
| 560 | {0x000500C2, nullptr, "SendFifoEx"}, | ||
| 561 | {0x000600C0, nullptr, "RecvFifoEx"}, | ||
| 562 | {0x00070040, SetSemaphore, "SetSemaphore"}, | ||
| 563 | {0x00080000, nullptr, "GetSemaphore"}, | ||
| 564 | {0x00090040, nullptr, "ClearSemaphore"}, | ||
| 565 | {0x000A0040, nullptr, "MaskSemaphore"}, | ||
| 566 | {0x000B0000, nullptr, "CheckSemaphoreRequest"}, | ||
| 567 | {0x000C0040, ConvertProcessAddressFromDspDram, "ConvertProcessAddressFromDspDram"}, | ||
| 568 | {0x000D0082, WriteProcessPipe, "WriteProcessPipe"}, | ||
| 569 | {0x000E00C0, ReadPipe, "ReadPipe"}, | ||
| 570 | {0x000F0080, GetPipeReadableSize, "GetPipeReadableSize"}, | ||
| 571 | {0x001000C0, ReadPipeIfPossible, "ReadPipeIfPossible"}, | ||
| 572 | {0x001100C2, LoadComponent, "LoadComponent"}, | ||
| 573 | {0x00120000, nullptr, "UnloadComponent"}, | ||
| 574 | {0x00130082, FlushDataCache, "FlushDataCache"}, | ||
| 575 | {0x00140082, nullptr, "InvalidateDCache"}, | ||
| 576 | {0x00150082, RegisterInterruptEvents, "RegisterInterruptEvents"}, | ||
| 577 | {0x00160000, GetSemaphoreEventHandle, "GetSemaphoreEventHandle"}, | ||
| 578 | {0x00170040, SetSemaphoreMask, "SetSemaphoreMask"}, | ||
| 579 | {0x00180040, nullptr, "GetPhysicalAddress"}, | ||
| 580 | {0x00190040, nullptr, "GetVirtualAddress"}, | ||
| 581 | {0x001A0042, nullptr, "SetIirFilterI2S1_cmd1"}, | ||
| 582 | {0x001B0042, nullptr, "SetIirFilterI2S1_cmd2"}, | ||
| 583 | {0x001C0082, nullptr, "SetIirFilterEQ"}, | ||
| 584 | {0x001D00C0, nullptr, "ReadMultiEx_SPI2"}, | ||
| 585 | {0x001E00C2, nullptr, "WriteMultiEx_SPI2"}, | ||
| 586 | {0x001F0000, GetHeadphoneStatus, "GetHeadphoneStatus"}, | ||
| 587 | {0x00200040, nullptr, "ForceHeadphoneOut"}, | ||
| 588 | {0x00210000, nullptr, "GetIsDspOccupied"}, | ||
| 589 | }; | ||
| 590 | |||
| 591 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 16 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 592 | // Interface class | 17 | // Interface class |
| 593 | 18 | ||
| 594 | Interface::Interface() { | 19 | Interface::Interface() { |
| 595 | semaphore_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "DSP_DSP::semaphore_event"); | ||
| 596 | interrupt_events = {}; | ||
| 597 | |||
| 598 | Register(FunctionTable); | ||
| 599 | } | 20 | } |
| 600 | 21 | ||
| 601 | Interface::~Interface() { | 22 | Interface::~Interface() { |
| 602 | semaphore_event = nullptr; | ||
| 603 | interrupt_events = {}; | ||
| 604 | } | 23 | } |
| 605 | 24 | ||
| 606 | } // namespace DSP_DSP | 25 | } // namespace DSP_DSP |
diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp index 88684b82d..757011e26 100644 --- a/src/core/hle/service/gsp_gpu.cpp +++ b/src/core/hle/service/gsp_gpu.cpp | |||
| @@ -2,417 +2,11 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/bit_field.h" | ||
| 6 | #include "common/microprofile.h" | ||
| 7 | #include "core/core.h" | ||
| 8 | #include "core/hle/ipc.h" | ||
| 9 | #include "core/hle/kernel/event.h" | ||
| 10 | #include "core/hle/kernel/handle_table.h" | ||
| 11 | #include "core/hle/kernel/shared_memory.h" | ||
| 12 | #include "core/hle/result.h" | ||
| 13 | #include "core/hle/service/gsp_gpu.h" | 5 | #include "core/hle/service/gsp_gpu.h" |
| 14 | #include "core/hw/gpu.h" | ||
| 15 | #include "core/hw/hw.h" | ||
| 16 | #include "core/hw/lcd.h" | ||
| 17 | #include "core/memory.h" | ||
| 18 | #include "video_core/debug_utils/debug_utils.h" | ||
| 19 | #include "video_core/gpu_debugger.h" | ||
| 20 | |||
| 21 | // Main graphics debugger object - TODO: Here is probably not the best place for this | ||
| 22 | GraphicsDebugger g_debugger; | ||
| 23 | 6 | ||
| 24 | namespace Service { | 7 | namespace Service { |
| 25 | namespace GSP { | 8 | namespace GSP { |
| 26 | 9 | ||
| 27 | // Beginning address of HW regs | ||
| 28 | const u32 REGS_BEGIN = 0x1EB00000; | ||
| 29 | |||
| 30 | namespace ErrCodes { | ||
| 31 | enum { | ||
| 32 | // TODO(purpasmart): Check if this name fits its actual usage | ||
| 33 | OutofRangeOrMisalignedAddress = 513, | ||
| 34 | FirstInitialization = 519, | ||
| 35 | }; | ||
| 36 | } | ||
| 37 | |||
| 38 | constexpr ResultCode RESULT_FIRST_INITIALIZATION(ErrCodes::FirstInitialization, ErrorModule::GX, | ||
| 39 | ErrorSummary::Success, ErrorLevel::Success); | ||
| 40 | constexpr ResultCode ERR_REGS_OUTOFRANGE_OR_MISALIGNED(ErrCodes::OutofRangeOrMisalignedAddress, | ||
| 41 | ErrorModule::GX, | ||
| 42 | ErrorSummary::InvalidArgument, | ||
| 43 | ErrorLevel::Usage); // 0xE0E02A01 | ||
| 44 | constexpr ResultCode ERR_REGS_MISALIGNED(ErrorDescription::MisalignedSize, ErrorModule::GX, | ||
| 45 | ErrorSummary::InvalidArgument, | ||
| 46 | ErrorLevel::Usage); // 0xE0E02BF2 | ||
| 47 | constexpr ResultCode ERR_REGS_INVALID_SIZE(ErrorDescription::InvalidSize, ErrorModule::GX, | ||
| 48 | ErrorSummary::InvalidArgument, | ||
| 49 | ErrorLevel::Usage); // 0xE0E02BEC | ||
| 50 | |||
| 51 | /// Event triggered when GSP interrupt has been signalled | ||
| 52 | Kernel::SharedPtr<Kernel::Event> g_interrupt_event; | ||
| 53 | /// GSP shared memoryings | ||
| 54 | Kernel::SharedPtr<Kernel::SharedMemory> g_shared_memory; | ||
| 55 | /// Thread index into interrupt relay queue | ||
| 56 | u32 g_thread_id = 0; | ||
| 57 | |||
| 58 | static bool gpu_right_acquired = false; | ||
| 59 | static bool first_initialization = true; | ||
| 60 | /// Gets a pointer to a thread command buffer in GSP shared memory | ||
| 61 | static inline u8* GetCommandBuffer(u32 thread_id) { | ||
| 62 | return g_shared_memory->GetPointer(0x800 + (thread_id * sizeof(CommandBuffer))); | ||
| 63 | } | ||
| 64 | |||
| 65 | FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_index) { | ||
| 66 | DEBUG_ASSERT_MSG(screen_index < 2, "Invalid screen index"); | ||
| 67 | |||
| 68 | // For each thread there are two FrameBufferUpdate fields | ||
| 69 | u32 offset = 0x200 + (2 * thread_id + screen_index) * sizeof(FrameBufferUpdate); | ||
| 70 | u8* ptr = g_shared_memory->GetPointer(offset); | ||
| 71 | return reinterpret_cast<FrameBufferUpdate*>(ptr); | ||
| 72 | } | ||
| 73 | |||
| 74 | /// Gets a pointer to the interrupt relay queue for a given thread index | ||
| 75 | static inline InterruptRelayQueue* GetInterruptRelayQueue(u32 thread_id) { | ||
| 76 | u8* ptr = g_shared_memory->GetPointer(sizeof(InterruptRelayQueue) * thread_id); | ||
| 77 | return reinterpret_cast<InterruptRelayQueue*>(ptr); | ||
| 78 | } | ||
| 79 | |||
| 80 | /** | ||
| 81 | * Writes a single GSP GPU hardware registers with a single u32 value | ||
| 82 | * (For internal use.) | ||
| 83 | * | ||
| 84 | * @param base_address The address of the register in question | ||
| 85 | * @param data Data to be written | ||
| 86 | */ | ||
| 87 | static void WriteSingleHWReg(u32 base_address, u32 data) { | ||
| 88 | DEBUG_ASSERT_MSG((base_address & 3) == 0 && base_address < 0x420000, | ||
| 89 | "Write address out of range or misaligned"); | ||
| 90 | HW::Write<u32>(base_address + REGS_BEGIN, data); | ||
| 91 | } | ||
| 92 | |||
| 93 | /** | ||
| 94 | * Writes sequential GSP GPU hardware registers using an array of source data | ||
| 95 | * | ||
| 96 | * @param base_address The address of the first register in the sequence | ||
| 97 | * @param size_in_bytes The number of registers to update (size of data) | ||
| 98 | * @param data_vaddr A pointer to the source data | ||
| 99 | * @return RESULT_SUCCESS if the parameters are valid, error code otherwise | ||
| 100 | */ | ||
| 101 | static ResultCode WriteHWRegs(u32 base_address, u32 size_in_bytes, VAddr data_vaddr) { | ||
| 102 | // This magic number is verified to be done by the gsp module | ||
| 103 | const u32 max_size_in_bytes = 0x80; | ||
| 104 | |||
| 105 | if (base_address & 3 || base_address >= 0x420000) { | ||
| 106 | LOG_ERROR(Service_GSP, | ||
| 107 | "Write address was out of range or misaligned! (address=0x%08x, size=0x%08x)", | ||
| 108 | base_address, size_in_bytes); | ||
| 109 | return ERR_REGS_OUTOFRANGE_OR_MISALIGNED; | ||
| 110 | } else if (size_in_bytes <= max_size_in_bytes) { | ||
| 111 | if (size_in_bytes & 3) { | ||
| 112 | LOG_ERROR(Service_GSP, "Misaligned size 0x%08x", size_in_bytes); | ||
| 113 | return ERR_REGS_MISALIGNED; | ||
| 114 | } else { | ||
| 115 | while (size_in_bytes > 0) { | ||
| 116 | WriteSingleHWReg(base_address, Memory::Read32(data_vaddr)); | ||
| 117 | |||
| 118 | size_in_bytes -= 4; | ||
| 119 | data_vaddr += 4; | ||
| 120 | base_address += 4; | ||
| 121 | } | ||
| 122 | return RESULT_SUCCESS; | ||
| 123 | } | ||
| 124 | |||
| 125 | } else { | ||
| 126 | LOG_ERROR(Service_GSP, "Out of range size 0x%08x", size_in_bytes); | ||
| 127 | return ERR_REGS_INVALID_SIZE; | ||
| 128 | } | ||
| 129 | } | ||
| 130 | |||
| 131 | /** | ||
| 132 | * Updates sequential GSP GPU hardware registers using parallel arrays of source data and masks. | ||
| 133 | * For each register, the value is updated only where the mask is high | ||
| 134 | * | ||
| 135 | * @param base_address The address of the first register in the sequence | ||
| 136 | * @param size_in_bytes The number of registers to update (size of data) | ||
| 137 | * @param data_vaddr A virtual address to the source data to use for updates | ||
| 138 | * @param masks_vaddr A virtual address to the masks | ||
| 139 | * @return RESULT_SUCCESS if the parameters are valid, error code otherwise | ||
| 140 | */ | ||
| 141 | static ResultCode WriteHWRegsWithMask(u32 base_address, u32 size_in_bytes, VAddr data_vaddr, | ||
| 142 | VAddr masks_vaddr) { | ||
| 143 | // This magic number is verified to be done by the gsp module | ||
| 144 | const u32 max_size_in_bytes = 0x80; | ||
| 145 | |||
| 146 | if (base_address & 3 || base_address >= 0x420000) { | ||
| 147 | LOG_ERROR(Service_GSP, | ||
| 148 | "Write address was out of range or misaligned! (address=0x%08x, size=0x%08x)", | ||
| 149 | base_address, size_in_bytes); | ||
| 150 | return ERR_REGS_OUTOFRANGE_OR_MISALIGNED; | ||
| 151 | } else if (size_in_bytes <= max_size_in_bytes) { | ||
| 152 | if (size_in_bytes & 3) { | ||
| 153 | LOG_ERROR(Service_GSP, "Misaligned size 0x%08x", size_in_bytes); | ||
| 154 | return ERR_REGS_MISALIGNED; | ||
| 155 | } else { | ||
| 156 | while (size_in_bytes > 0) { | ||
| 157 | const u32 reg_address = base_address + REGS_BEGIN; | ||
| 158 | |||
| 159 | u32 reg_value; | ||
| 160 | HW::Read<u32>(reg_value, reg_address); | ||
| 161 | |||
| 162 | u32 data = Memory::Read32(data_vaddr); | ||
| 163 | u32 mask = Memory::Read32(masks_vaddr); | ||
| 164 | |||
| 165 | // Update the current value of the register only for set mask bits | ||
| 166 | reg_value = (reg_value & ~mask) | (data & mask); | ||
| 167 | |||
| 168 | WriteSingleHWReg(base_address, reg_value); | ||
| 169 | |||
| 170 | size_in_bytes -= 4; | ||
| 171 | data_vaddr += 4; | ||
| 172 | masks_vaddr += 4; | ||
| 173 | base_address += 4; | ||
| 174 | } | ||
| 175 | return RESULT_SUCCESS; | ||
| 176 | } | ||
| 177 | |||
| 178 | } else { | ||
| 179 | LOG_ERROR(Service_GSP, "Out of range size 0x%08x", size_in_bytes); | ||
| 180 | return ERR_REGS_INVALID_SIZE; | ||
| 181 | } | ||
| 182 | } | ||
| 183 | |||
| 184 | /** | ||
| 185 | * GSP_GPU::WriteHWRegs service function | ||
| 186 | * | ||
| 187 | * Writes sequential GSP GPU hardware registers | ||
| 188 | * | ||
| 189 | * Inputs: | ||
| 190 | * 1 : address of first GPU register | ||
| 191 | * 2 : number of registers to write sequentially | ||
| 192 | * 4 : pointer to source data array | ||
| 193 | */ | ||
| 194 | static void WriteHWRegs(Interface* self) { | ||
| 195 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 196 | u32 reg_addr = cmd_buff[1]; | ||
| 197 | u32 size = cmd_buff[2]; | ||
| 198 | VAddr src = cmd_buff[4]; | ||
| 199 | |||
| 200 | cmd_buff[1] = WriteHWRegs(reg_addr, size, src).raw; | ||
| 201 | } | ||
| 202 | |||
| 203 | /** | ||
| 204 | * GSP_GPU::WriteHWRegsWithMask service function | ||
| 205 | * | ||
| 206 | * Updates sequential GSP GPU hardware registers using masks | ||
| 207 | * | ||
| 208 | * Inputs: | ||
| 209 | * 1 : address of first GPU register | ||
| 210 | * 2 : number of registers to update sequentially | ||
| 211 | * 4 : pointer to source data array | ||
| 212 | * 6 : pointer to mask array | ||
| 213 | */ | ||
| 214 | static void WriteHWRegsWithMask(Interface* self) { | ||
| 215 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 216 | u32 reg_addr = cmd_buff[1]; | ||
| 217 | u32 size = cmd_buff[2]; | ||
| 218 | |||
| 219 | VAddr src_data = cmd_buff[4]; | ||
| 220 | VAddr mask_data = cmd_buff[6]; | ||
| 221 | |||
| 222 | cmd_buff[1] = WriteHWRegsWithMask(reg_addr, size, src_data, mask_data).raw; | ||
| 223 | } | ||
| 224 | |||
| 225 | /// Read a GSP GPU hardware register | ||
| 226 | static void ReadHWRegs(Interface* self) { | ||
| 227 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 228 | u32 reg_addr = cmd_buff[1]; | ||
| 229 | u32 size = cmd_buff[2]; | ||
| 230 | |||
| 231 | // TODO: Return proper error codes | ||
| 232 | if (reg_addr + size >= 0x420000) { | ||
| 233 | LOG_ERROR(Service_GSP, "Read address out of range! (address=0x%08x, size=0x%08x)", reg_addr, | ||
| 234 | size); | ||
| 235 | return; | ||
| 236 | } | ||
| 237 | |||
| 238 | // size should be word-aligned | ||
| 239 | if ((size % 4) != 0) { | ||
| 240 | LOG_ERROR(Service_GSP, "Invalid size 0x%08x", size); | ||
| 241 | return; | ||
| 242 | } | ||
| 243 | |||
| 244 | VAddr dst_vaddr = cmd_buff[0x41]; | ||
| 245 | |||
| 246 | while (size > 0) { | ||
| 247 | u32 value; | ||
| 248 | HW::Read<u32>(value, reg_addr + REGS_BEGIN); | ||
| 249 | |||
| 250 | Memory::Write32(dst_vaddr, value); | ||
| 251 | |||
| 252 | size -= 4; | ||
| 253 | dst_vaddr += 4; | ||
| 254 | reg_addr += 4; | ||
| 255 | } | ||
| 256 | } | ||
| 257 | |||
| 258 | ResultCode SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) { | ||
| 259 | u32 base_address = 0x400000; | ||
| 260 | PAddr phys_address_left = Memory::VirtualToPhysicalAddress(info.address_left); | ||
| 261 | PAddr phys_address_right = Memory::VirtualToPhysicalAddress(info.address_right); | ||
| 262 | if (info.active_fb == 0) { | ||
| 263 | WriteSingleHWReg( | ||
| 264 | base_address + | ||
| 265 | 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_left1)), | ||
| 266 | phys_address_left); | ||
| 267 | WriteSingleHWReg( | ||
| 268 | base_address + | ||
| 269 | 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_right1)), | ||
| 270 | phys_address_right); | ||
| 271 | } else { | ||
| 272 | WriteSingleHWReg( | ||
| 273 | base_address + | ||
| 274 | 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_left2)), | ||
| 275 | phys_address_left); | ||
| 276 | WriteSingleHWReg( | ||
| 277 | base_address + | ||
| 278 | 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].address_right2)), | ||
| 279 | phys_address_right); | ||
| 280 | } | ||
| 281 | WriteSingleHWReg(base_address + | ||
| 282 | 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].stride)), | ||
| 283 | info.stride); | ||
| 284 | WriteSingleHWReg( | ||
| 285 | base_address + | ||
| 286 | 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].color_format)), | ||
| 287 | info.format); | ||
| 288 | WriteSingleHWReg( | ||
| 289 | base_address + 4 * static_cast<u32>(GPU_REG_INDEX(framebuffer_config[screen_id].active_fb)), | ||
| 290 | info.shown_fb); | ||
| 291 | |||
| 292 | if (Pica::g_debug_context) | ||
| 293 | Pica::g_debug_context->OnEvent(Pica::DebugContext::Event::BufferSwapped, nullptr); | ||
| 294 | |||
| 295 | if (screen_id == 0) { | ||
| 296 | MicroProfileFlip(); | ||
| 297 | Core::System::GetInstance().perf_stats.EndGameFrame(); | ||
| 298 | } | ||
| 299 | |||
| 300 | return RESULT_SUCCESS; | ||
| 301 | } | ||
| 302 | |||
| 303 | /** | ||
| 304 | * GSP_GPU::SetBufferSwap service function | ||
| 305 | * | ||
| 306 | * Updates GPU display framebuffer configuration using the specified parameters. | ||
| 307 | * | ||
| 308 | * Inputs: | ||
| 309 | * 1 : Screen ID (0 = top screen, 1 = bottom screen) | ||
| 310 | * 2-7 : FrameBufferInfo structure | ||
| 311 | * Outputs: | ||
| 312 | * 1: Result code | ||
| 313 | */ | ||
| 314 | static void SetBufferSwap(Interface* self) { | ||
| 315 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 316 | u32 screen_id = cmd_buff[1]; | ||
| 317 | FrameBufferInfo* fb_info = (FrameBufferInfo*)&cmd_buff[2]; | ||
| 318 | |||
| 319 | cmd_buff[1] = SetBufferSwap(screen_id, *fb_info).raw; | ||
| 320 | } | ||
| 321 | |||
| 322 | /** | ||
| 323 | * GSP_GPU::FlushDataCache service function | ||
| 324 | * | ||
| 325 | * This Function is a no-op, We aren't emulating the CPU cache any time soon. | ||
| 326 | * | ||
| 327 | * Inputs: | ||
| 328 | * 1 : Address | ||
| 329 | * 2 : Size | ||
| 330 | * 3 : Value 0, some descriptor for the KProcess Handle | ||
| 331 | * 4 : KProcess handle | ||
| 332 | * Outputs: | ||
| 333 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 334 | */ | ||
| 335 | static void FlushDataCache(Interface* self) { | ||
| 336 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 337 | u32 address = cmd_buff[1]; | ||
| 338 | u32 size = cmd_buff[2]; | ||
| 339 | u32 process = cmd_buff[4]; | ||
| 340 | |||
| 341 | // TODO(purpasmart96): Verify return header on HW | ||
| 342 | |||
| 343 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 344 | |||
| 345 | LOG_DEBUG(Service_GSP, "(STUBBED) called address=0x%08X, size=0x%08X, process=0x%08X", address, | ||
| 346 | size, process); | ||
| 347 | } | ||
| 348 | |||
| 349 | /** | ||
| 350 | * GSP_GPU::SetAxiConfigQoSMode service function | ||
| 351 | * Inputs: | ||
| 352 | * 1 : Mode, unused in emulator | ||
| 353 | * Outputs: | ||
| 354 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 355 | */ | ||
| 356 | static void SetAxiConfigQoSMode(Interface* self) { | ||
| 357 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 358 | u32 mode = cmd_buff[1]; | ||
| 359 | |||
| 360 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 361 | |||
| 362 | LOG_DEBUG(Service_GSP, "(STUBBED) called mode=0x%08X", mode); | ||
| 363 | } | ||
| 364 | |||
| 365 | /** | ||
| 366 | * GSP_GPU::RegisterInterruptRelayQueue service function | ||
| 367 | * Inputs: | ||
| 368 | * 1 : "Flags" field, purpose is unknown | ||
| 369 | * 3 : Handle to GSP synchronization event | ||
| 370 | * Outputs: | ||
| 371 | * 1 : Result of function, 0x2A07 on success, otherwise error code | ||
| 372 | * 2 : Thread index into GSP command buffer | ||
| 373 | * 4 : Handle to GSP shared memory | ||
| 374 | */ | ||
| 375 | static void RegisterInterruptRelayQueue(Interface* self) { | ||
| 376 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 377 | u32 flags = cmd_buff[1]; | ||
| 378 | |||
| 379 | g_interrupt_event = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[3]); | ||
| 380 | // TODO(mailwl): return right error code instead assert | ||
| 381 | ASSERT_MSG((g_interrupt_event != nullptr), "handle is not valid!"); | ||
| 382 | |||
| 383 | g_interrupt_event->name = "GSP_GPU::interrupt_event"; | ||
| 384 | |||
| 385 | if (first_initialization) { | ||
| 386 | // This specific code is required for a successful initialization, rather than 0 | ||
| 387 | first_initialization = false; | ||
| 388 | cmd_buff[1] = RESULT_FIRST_INITIALIZATION.raw; | ||
| 389 | } else { | ||
| 390 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 391 | } | ||
| 392 | cmd_buff[2] = g_thread_id++; // Thread ID | ||
| 393 | cmd_buff[4] = Kernel::g_handle_table.Create(g_shared_memory).Unwrap(); // GSP shared memory | ||
| 394 | |||
| 395 | g_interrupt_event->Signal(); // TODO(bunnei): Is this correct? | ||
| 396 | |||
| 397 | LOG_WARNING(Service_GSP, "called, flags=0x%08X", flags); | ||
| 398 | } | ||
| 399 | |||
| 400 | /** | ||
| 401 | * GSP_GPU::UnregisterInterruptRelayQueue service function | ||
| 402 | * Outputs: | ||
| 403 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 404 | */ | ||
| 405 | static void UnregisterInterruptRelayQueue(Interface* self) { | ||
| 406 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 407 | |||
| 408 | g_thread_id = 0; | ||
| 409 | g_interrupt_event = nullptr; | ||
| 410 | |||
| 411 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 412 | |||
| 413 | LOG_WARNING(Service_GSP, "(STUBBED) called"); | ||
| 414 | } | ||
| 415 | |||
| 416 | /** | 10 | /** |
| 417 | * Signals that the specified interrupt type has occurred to userland code | 11 | * Signals that the specified interrupt type has occurred to userland code |
| 418 | * @param interrupt_id ID of interrupt that is being signalled | 12 | * @param interrupt_id ID of interrupt that is being signalled |
| @@ -420,383 +14,13 @@ static void UnregisterInterruptRelayQueue(Interface* self) { | |||
| 420 | * @todo This probably does not belong in the GSP module, instead move to video_core | 14 | * @todo This probably does not belong in the GSP module, instead move to video_core |
| 421 | */ | 15 | */ |
| 422 | void SignalInterrupt(InterruptId interrupt_id) { | 16 | void SignalInterrupt(InterruptId interrupt_id) { |
| 423 | if (!gpu_right_acquired) { | 17 | UNIMPLEMENTED(); |
| 424 | return; | ||
| 425 | } | ||
| 426 | if (nullptr == g_interrupt_event) { | ||
| 427 | LOG_WARNING(Service_GSP, "cannot synchronize until GSP event has been created!"); | ||
| 428 | return; | ||
| 429 | } | ||
| 430 | if (nullptr == g_shared_memory) { | ||
| 431 | LOG_WARNING(Service_GSP, "cannot synchronize until GSP shared memory has been created!"); | ||
| 432 | return; | ||
| 433 | } | ||
| 434 | for (int thread_id = 0; thread_id < 0x4; ++thread_id) { | ||
| 435 | InterruptRelayQueue* interrupt_relay_queue = GetInterruptRelayQueue(thread_id); | ||
| 436 | u8 next = interrupt_relay_queue->index; | ||
| 437 | next += interrupt_relay_queue->number_interrupts; | ||
| 438 | next = next % 0x34; // 0x34 is the number of interrupt slots | ||
| 439 | |||
| 440 | interrupt_relay_queue->number_interrupts += 1; | ||
| 441 | |||
| 442 | interrupt_relay_queue->slot[next] = interrupt_id; | ||
| 443 | interrupt_relay_queue->error_code = 0x0; // No error | ||
| 444 | |||
| 445 | // Update framebuffer information if requested | ||
| 446 | // TODO(yuriks): Confirm where this code should be called. It is definitely updated without | ||
| 447 | // executing any GSP commands, only waiting on the event. | ||
| 448 | int screen_id = | ||
| 449 | (interrupt_id == InterruptId::PDC0) ? 0 : (interrupt_id == InterruptId::PDC1) ? 1 : -1; | ||
| 450 | if (screen_id != -1) { | ||
| 451 | FrameBufferUpdate* info = GetFrameBufferInfo(thread_id, screen_id); | ||
| 452 | if (info->is_dirty) { | ||
| 453 | SetBufferSwap(screen_id, info->framebuffer_info[info->index]); | ||
| 454 | info->is_dirty.Assign(false); | ||
| 455 | } | ||
| 456 | } | ||
| 457 | } | ||
| 458 | g_interrupt_event->Signal(); | ||
| 459 | } | ||
| 460 | |||
| 461 | MICROPROFILE_DEFINE(GPU_GSP_DMA, "GPU", "GSP DMA", MP_RGB(100, 0, 255)); | ||
| 462 | |||
| 463 | /// Executes the next GSP command | ||
| 464 | static void ExecuteCommand(const Command& command, u32 thread_id) { | ||
| 465 | // Utility function to convert register ID to address | ||
| 466 | static auto WriteGPURegister = [](u32 id, u32 data) { | ||
| 467 | GPU::Write<u32>(0x1EF00000 + 4 * id, data); | ||
| 468 | }; | ||
| 469 | |||
| 470 | switch (command.id) { | ||
| 471 | |||
| 472 | // GX request DMA - typically used for copying memory from GSP heap to VRAM | ||
| 473 | case CommandId::REQUEST_DMA: { | ||
| 474 | MICROPROFILE_SCOPE(GPU_GSP_DMA); | ||
| 475 | |||
| 476 | // TODO: Consider attempting rasterizer-accelerated surface blit if that usage is ever | ||
| 477 | // possible/likely | ||
| 478 | Memory::RasterizerFlushVirtualRegion(command.dma_request.source_address, | ||
| 479 | command.dma_request.size, Memory::FlushMode::Flush); | ||
| 480 | Memory::RasterizerFlushVirtualRegion(command.dma_request.dest_address, | ||
| 481 | command.dma_request.size, | ||
| 482 | Memory::FlushMode::FlushAndInvalidate); | ||
| 483 | |||
| 484 | // TODO(Subv): These memory accesses should not go through the application's memory mapping. | ||
| 485 | // They should go through the GSP module's memory mapping. | ||
| 486 | Memory::CopyBlock(command.dma_request.dest_address, command.dma_request.source_address, | ||
| 487 | command.dma_request.size); | ||
| 488 | SignalInterrupt(InterruptId::DMA); | ||
| 489 | break; | ||
| 490 | } | ||
| 491 | // TODO: This will need some rework in the future. (why?) | ||
| 492 | case CommandId::SUBMIT_GPU_CMDLIST: { | ||
| 493 | auto& params = command.submit_gpu_cmdlist; | ||
| 494 | |||
| 495 | if (params.do_flush) { | ||
| 496 | // This flag flushes the command list (params.address, params.size) from the cache. | ||
| 497 | // Command lists are not processed by the hardware renderer, so we don't need to | ||
| 498 | // actually flush them in Citra. | ||
| 499 | } | ||
| 500 | |||
| 501 | WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(command_processor_config.address)), | ||
| 502 | Memory::VirtualToPhysicalAddress(params.address) >> 3); | ||
| 503 | WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(command_processor_config.size)), | ||
| 504 | params.size); | ||
| 505 | |||
| 506 | // TODO: Not sure if we are supposed to always write this .. seems to trigger processing | ||
| 507 | // though | ||
| 508 | WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(command_processor_config.trigger)), 1); | ||
| 509 | |||
| 510 | // TODO(yuriks): Figure out the meaning of the `flags` field. | ||
| 511 | |||
| 512 | break; | ||
| 513 | } | ||
| 514 | |||
| 515 | // It's assumed that the two "blocks" behave equivalently. | ||
| 516 | // Presumably this is done simply to allow two memory fills to run in parallel. | ||
| 517 | case CommandId::SET_MEMORY_FILL: { | ||
| 518 | auto& params = command.memory_fill; | ||
| 519 | |||
| 520 | if (params.start1 != 0) { | ||
| 521 | WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[0].address_start)), | ||
| 522 | Memory::VirtualToPhysicalAddress(params.start1) >> 3); | ||
| 523 | WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[0].address_end)), | ||
| 524 | Memory::VirtualToPhysicalAddress(params.end1) >> 3); | ||
| 525 | WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[0].value_32bit)), | ||
| 526 | params.value1); | ||
| 527 | WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[0].control)), | ||
| 528 | params.control1); | ||
| 529 | } | ||
| 530 | |||
| 531 | if (params.start2 != 0) { | ||
| 532 | WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[1].address_start)), | ||
| 533 | Memory::VirtualToPhysicalAddress(params.start2) >> 3); | ||
| 534 | WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[1].address_end)), | ||
| 535 | Memory::VirtualToPhysicalAddress(params.end2) >> 3); | ||
| 536 | WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[1].value_32bit)), | ||
| 537 | params.value2); | ||
| 538 | WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(memory_fill_config[1].control)), | ||
| 539 | params.control2); | ||
| 540 | } | ||
| 541 | break; | ||
| 542 | } | ||
| 543 | |||
| 544 | case CommandId::SET_DISPLAY_TRANSFER: { | ||
| 545 | auto& params = command.display_transfer; | ||
| 546 | WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(display_transfer_config.input_address)), | ||
| 547 | Memory::VirtualToPhysicalAddress(params.in_buffer_address) >> 3); | ||
| 548 | WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(display_transfer_config.output_address)), | ||
| 549 | Memory::VirtualToPhysicalAddress(params.out_buffer_address) >> 3); | ||
| 550 | WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(display_transfer_config.input_size)), | ||
| 551 | params.in_buffer_size); | ||
| 552 | WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(display_transfer_config.output_size)), | ||
| 553 | params.out_buffer_size); | ||
| 554 | WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(display_transfer_config.flags)), | ||
| 555 | params.flags); | ||
| 556 | WriteGPURegister(static_cast<u32>(GPU_REG_INDEX(display_transfer_config.trigger)), 1); | ||
| 557 | break; | ||
| 558 | } | ||
| 559 | |||
| 560 | case CommandId::SET_TEXTURE_COPY: { | ||
| 561 | auto& params = command.texture_copy; | ||
| 562 | WriteGPURegister((u32)GPU_REG_INDEX(display_transfer_config.input_address), | ||
| 563 | Memory::VirtualToPhysicalAddress(params.in_buffer_address) >> 3); | ||
| 564 | WriteGPURegister((u32)GPU_REG_INDEX(display_transfer_config.output_address), | ||
| 565 | Memory::VirtualToPhysicalAddress(params.out_buffer_address) >> 3); | ||
| 566 | WriteGPURegister((u32)GPU_REG_INDEX(display_transfer_config.texture_copy.size), | ||
| 567 | params.size); | ||
| 568 | WriteGPURegister((u32)GPU_REG_INDEX(display_transfer_config.texture_copy.input_size), | ||
| 569 | params.in_width_gap); | ||
| 570 | WriteGPURegister((u32)GPU_REG_INDEX(display_transfer_config.texture_copy.output_size), | ||
| 571 | params.out_width_gap); | ||
| 572 | WriteGPURegister((u32)GPU_REG_INDEX(display_transfer_config.flags), params.flags); | ||
| 573 | |||
| 574 | // NOTE: Actual GSP ORs 1 with current register instead of overwriting. Doesn't seem to | ||
| 575 | // matter. | ||
| 576 | WriteGPURegister((u32)GPU_REG_INDEX(display_transfer_config.trigger), 1); | ||
| 577 | break; | ||
| 578 | } | ||
| 579 | |||
| 580 | case CommandId::CACHE_FLUSH: { | ||
| 581 | // NOTE: Rasterizer flushing handled elsewhere in CPU read/write and other GPU handlers | ||
| 582 | // Use command.cache_flush.regions to implement this handler | ||
| 583 | break; | ||
| 584 | } | ||
| 585 | |||
| 586 | default: | ||
| 587 | LOG_ERROR(Service_GSP, "unknown command 0x%08X", (int)command.id.Value()); | ||
| 588 | } | ||
| 589 | |||
| 590 | if (Pica::g_debug_context) | ||
| 591 | Pica::g_debug_context->OnEvent(Pica::DebugContext::Event::GSPCommandProcessed, | ||
| 592 | (void*)&command); | ||
| 593 | } | ||
| 594 | |||
| 595 | /** | ||
| 596 | * GSP_GPU::SetLcdForceBlack service function | ||
| 597 | * | ||
| 598 | * Enable or disable REG_LCDCOLORFILL with the color black. | ||
| 599 | * | ||
| 600 | * Inputs: | ||
| 601 | * 1: Black color fill flag (0 = don't fill, !0 = fill) | ||
| 602 | * Outputs: | ||
| 603 | * 1: Result code | ||
| 604 | */ | ||
| 605 | static void SetLcdForceBlack(Interface* self) { | ||
| 606 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 607 | |||
| 608 | bool enable_black = cmd_buff[1] != 0; | ||
| 609 | LCD::Regs::ColorFill data = {0}; | ||
| 610 | |||
| 611 | // Since data is already zeroed, there is no need to explicitly set | ||
| 612 | // the color to black (all zero). | ||
| 613 | data.is_enabled.Assign(enable_black); | ||
| 614 | |||
| 615 | LCD::Write(HW::VADDR_LCD + 4 * LCD_REG_INDEX(color_fill_top), data.raw); // Top LCD | ||
| 616 | LCD::Write(HW::VADDR_LCD + 4 * LCD_REG_INDEX(color_fill_bottom), data.raw); // Bottom LCD | ||
| 617 | |||
| 618 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 619 | } | 18 | } |
| 620 | 19 | ||
| 621 | /// This triggers handling of the GX command written to the command buffer in shared memory. | ||
| 622 | static void TriggerCmdReqQueue(Interface* self) { | ||
| 623 | // Iterate through each thread's command queue... | ||
| 624 | for (unsigned thread_id = 0; thread_id < 0x4; ++thread_id) { | ||
| 625 | CommandBuffer* command_buffer = (CommandBuffer*)GetCommandBuffer(thread_id); | ||
| 626 | |||
| 627 | // Iterate through each command... | ||
| 628 | for (unsigned i = 0; i < command_buffer->number_commands; ++i) { | ||
| 629 | g_debugger.GXCommandProcessed((u8*)&command_buffer->commands[i]); | ||
| 630 | |||
| 631 | // Decode and execute command | ||
| 632 | ExecuteCommand(command_buffer->commands[i], thread_id); | ||
| 633 | |||
| 634 | // Indicates that command has completed | ||
| 635 | command_buffer->number_commands.Assign(command_buffer->number_commands - 1); | ||
| 636 | } | ||
| 637 | } | ||
| 638 | |||
| 639 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 640 | cmd_buff[1] = 0; // No error | ||
| 641 | } | ||
| 642 | |||
| 643 | /** | ||
| 644 | * GSP_GPU::ImportDisplayCaptureInfo service function | ||
| 645 | * | ||
| 646 | * Returns information about the current framebuffer state | ||
| 647 | * | ||
| 648 | * Inputs: | ||
| 649 | * 0: Header 0x00180000 | ||
| 650 | * Outputs: | ||
| 651 | * 0: Header Code[0x00180240] | ||
| 652 | * 1: Result code | ||
| 653 | * 2: Left framebuffer virtual address for the main screen | ||
| 654 | * 3: Right framebuffer virtual address for the main screen | ||
| 655 | * 4: Main screen framebuffer format | ||
| 656 | * 5: Main screen framebuffer width | ||
| 657 | * 6: Left framebuffer virtual address for the bottom screen | ||
| 658 | * 7: Right framebuffer virtual address for the bottom screen | ||
| 659 | * 8: Bottom screen framebuffer format | ||
| 660 | * 9: Bottom screen framebuffer width | ||
| 661 | */ | ||
| 662 | static void ImportDisplayCaptureInfo(Interface* self) { | ||
| 663 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 664 | |||
| 665 | // TODO(Subv): We're always returning the framebuffer structures for thread_id = 0, | ||
| 666 | // because we only support a single running application at a time. | ||
| 667 | // This should always return the framebuffer data that is currently displayed on the screen. | ||
| 668 | |||
| 669 | u32 thread_id = 0; | ||
| 670 | |||
| 671 | FrameBufferUpdate* top_screen = GetFrameBufferInfo(thread_id, 0); | ||
| 672 | FrameBufferUpdate* bottom_screen = GetFrameBufferInfo(thread_id, 1); | ||
| 673 | |||
| 674 | cmd_buff[0] = IPC::MakeHeader(0x18, 0x9, 0); | ||
| 675 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 676 | // Top Screen | ||
| 677 | cmd_buff[2] = top_screen->framebuffer_info[top_screen->index].address_left; | ||
| 678 | cmd_buff[3] = top_screen->framebuffer_info[top_screen->index].address_right; | ||
| 679 | cmd_buff[4] = top_screen->framebuffer_info[top_screen->index].format; | ||
| 680 | cmd_buff[5] = top_screen->framebuffer_info[top_screen->index].stride; | ||
| 681 | // Bottom Screen | ||
| 682 | cmd_buff[6] = bottom_screen->framebuffer_info[bottom_screen->index].address_left; | ||
| 683 | cmd_buff[7] = bottom_screen->framebuffer_info[bottom_screen->index].address_right; | ||
| 684 | cmd_buff[8] = bottom_screen->framebuffer_info[bottom_screen->index].format; | ||
| 685 | cmd_buff[9] = bottom_screen->framebuffer_info[bottom_screen->index].stride; | ||
| 686 | |||
| 687 | LOG_WARNING(Service_GSP, "called"); | ||
| 688 | } | ||
| 689 | |||
| 690 | /** | ||
| 691 | * GSP_GPU::AcquireRight service function | ||
| 692 | * Outputs: | ||
| 693 | * 1: Result code | ||
| 694 | */ | ||
| 695 | static void AcquireRight(Interface* self) { | ||
| 696 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 697 | |||
| 698 | gpu_right_acquired = true; | ||
| 699 | |||
| 700 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 701 | |||
| 702 | LOG_WARNING(Service_GSP, "called"); | ||
| 703 | } | ||
| 704 | |||
| 705 | /** | ||
| 706 | * GSP_GPU::ReleaseRight service function | ||
| 707 | * Outputs: | ||
| 708 | * 1: Result code | ||
| 709 | */ | ||
| 710 | static void ReleaseRight(Interface* self) { | ||
| 711 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 712 | |||
| 713 | gpu_right_acquired = false; | ||
| 714 | |||
| 715 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 716 | |||
| 717 | LOG_WARNING(Service_GSP, "called"); | ||
| 718 | } | ||
| 719 | |||
| 720 | /** | ||
| 721 | * GSP_GPU::StoreDataCache service function | ||
| 722 | * | ||
| 723 | * This Function is a no-op, We aren't emulating the CPU cache any time soon. | ||
| 724 | * | ||
| 725 | * Inputs: | ||
| 726 | * 0 : Header code [0x001F0082] | ||
| 727 | * 1 : Address | ||
| 728 | * 2 : Size | ||
| 729 | * 3 : Value 0, some descriptor for the KProcess Handle | ||
| 730 | * 4 : KProcess handle | ||
| 731 | * Outputs: | ||
| 732 | * 1 : Result of function, 0 on success, otherwise error code | ||
| 733 | */ | ||
| 734 | static void StoreDataCache(Interface* self) { | ||
| 735 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 736 | u32 address = cmd_buff[1]; | ||
| 737 | u32 size = cmd_buff[2]; | ||
| 738 | u32 process = cmd_buff[4]; | ||
| 739 | |||
| 740 | cmd_buff[0] = IPC::MakeHeader(0x1F, 0x1, 0); | ||
| 741 | cmd_buff[1] = RESULT_SUCCESS.raw; // No error | ||
| 742 | |||
| 743 | LOG_DEBUG(Service_GSP, "(STUBBED) called address=0x%08X, size=0x%08X, process=0x%08X", address, | ||
| 744 | size, process); | ||
| 745 | } | ||
| 746 | |||
| 747 | const Interface::FunctionInfo FunctionTable[] = { | ||
| 748 | {0x00010082, WriteHWRegs, "WriteHWRegs"}, | ||
| 749 | {0x00020084, WriteHWRegsWithMask, "WriteHWRegsWithMask"}, | ||
| 750 | {0x00030082, nullptr, "WriteHWRegRepeat"}, | ||
| 751 | {0x00040080, ReadHWRegs, "ReadHWRegs"}, | ||
| 752 | {0x00050200, SetBufferSwap, "SetBufferSwap"}, | ||
| 753 | {0x00060082, nullptr, "SetCommandList"}, | ||
| 754 | {0x000700C2, nullptr, "RequestDma"}, | ||
| 755 | {0x00080082, FlushDataCache, "FlushDataCache"}, | ||
| 756 | {0x00090082, nullptr, "InvalidateDataCache"}, | ||
| 757 | {0x000A0044, nullptr, "RegisterInterruptEvents"}, | ||
| 758 | {0x000B0040, SetLcdForceBlack, "SetLcdForceBlack"}, | ||
| 759 | {0x000C0000, TriggerCmdReqQueue, "TriggerCmdReqQueue"}, | ||
| 760 | {0x000D0140, nullptr, "SetDisplayTransfer"}, | ||
| 761 | {0x000E0180, nullptr, "SetTextureCopy"}, | ||
| 762 | {0x000F0200, nullptr, "SetMemoryFill"}, | ||
| 763 | {0x00100040, SetAxiConfigQoSMode, "SetAxiConfigQoSMode"}, | ||
| 764 | {0x00110040, nullptr, "SetPerfLogMode"}, | ||
| 765 | {0x00120000, nullptr, "GetPerfLog"}, | ||
| 766 | {0x00130042, RegisterInterruptRelayQueue, "RegisterInterruptRelayQueue"}, | ||
| 767 | {0x00140000, UnregisterInterruptRelayQueue, "UnregisterInterruptRelayQueue"}, | ||
| 768 | {0x00150002, nullptr, "TryAcquireRight"}, | ||
| 769 | {0x00160042, AcquireRight, "AcquireRight"}, | ||
| 770 | {0x00170000, ReleaseRight, "ReleaseRight"}, | ||
| 771 | {0x00180000, ImportDisplayCaptureInfo, "ImportDisplayCaptureInfo"}, | ||
| 772 | {0x00190000, nullptr, "SaveVramSysArea"}, | ||
| 773 | {0x001A0000, nullptr, "RestoreVramSysArea"}, | ||
| 774 | {0x001B0000, nullptr, "ResetGpuCore"}, | ||
| 775 | {0x001C0040, nullptr, "SetLedForceOff"}, | ||
| 776 | {0x001D0040, nullptr, "SetTestCommand"}, | ||
| 777 | {0x001E0080, nullptr, "SetInternalPriorities"}, | ||
| 778 | {0x001F0082, StoreDataCache, "StoreDataCache"}, | ||
| 779 | }; | ||
| 780 | |||
| 781 | GSP_GPU::GSP_GPU() { | 20 | GSP_GPU::GSP_GPU() { |
| 782 | Register(FunctionTable); | ||
| 783 | |||
| 784 | g_interrupt_event = nullptr; | ||
| 785 | |||
| 786 | using Kernel::MemoryPermission; | ||
| 787 | g_shared_memory = Kernel::SharedMemory::Create(nullptr, 0x1000, MemoryPermission::ReadWrite, | ||
| 788 | MemoryPermission::ReadWrite, 0, | ||
| 789 | Kernel::MemoryRegion::BASE, "GSP:SharedMemory"); | ||
| 790 | |||
| 791 | g_thread_id = 0; | ||
| 792 | gpu_right_acquired = false; | ||
| 793 | first_initialization = true; | ||
| 794 | } | 21 | } |
| 795 | 22 | ||
| 796 | GSP_GPU::~GSP_GPU() { | 23 | GSP_GPU::~GSP_GPU() { |
| 797 | g_interrupt_event = nullptr; | ||
| 798 | g_shared_memory = nullptr; | ||
| 799 | gpu_right_acquired = false; | ||
| 800 | } | 24 | } |
| 801 | 25 | ||
| 802 | } // namespace GSP | 26 | } // namespace GSP |
diff --git a/src/core/hle/service/gsp_lcd.cpp b/src/core/hle/service/gsp_lcd.cpp deleted file mode 100644 index 89cb4a3cc..000000000 --- a/src/core/hle/service/gsp_lcd.cpp +++ /dev/null | |||
| @@ -1,29 +0,0 @@ | |||
| 1 | // Copyright 2015 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "core/hle/service/gsp_lcd.h" | ||
| 6 | |||
| 7 | namespace Service { | ||
| 8 | namespace GSP { | ||
| 9 | |||
| 10 | const Interface::FunctionInfo FunctionTable[] = { | ||
| 11 | // clang-format off | ||
| 12 | {0x000A0080, nullptr, "SetBrightnessRaw"}, | ||
| 13 | {0x000B0080, nullptr, "SetBrightness"}, | ||
| 14 | {0x000F0000, nullptr, "PowerOnAllBacklights"}, | ||
| 15 | {0x00100000, nullptr, "PowerOffAllBacklights"}, | ||
| 16 | {0x00110040, nullptr, "PowerOnBacklight"}, | ||
| 17 | {0x00120040, nullptr, "PowerOffBacklight"}, | ||
| 18 | {0x00130040, nullptr, "SetLedForceOff"}, | ||
| 19 | {0x00140000, nullptr, "GetVendor"}, | ||
| 20 | {0x00150040, nullptr, "GetBrightness"}, | ||
| 21 | // clang-format on | ||
| 22 | }; | ||
| 23 | |||
| 24 | GSP_LCD::GSP_LCD() { | ||
| 25 | Register(FunctionTable); | ||
| 26 | } | ||
| 27 | |||
| 28 | } // namespace GSP | ||
| 29 | } // namespace Service | ||
diff --git a/src/core/hle/service/gsp_lcd.h b/src/core/hle/service/gsp_lcd.h deleted file mode 100644 index e9686a5e7..000000000 --- a/src/core/hle/service/gsp_lcd.h +++ /dev/null | |||
| @@ -1,22 +0,0 @@ | |||
| 1 | // Copyright 2015 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/hle/service/service.h" | ||
| 8 | |||
| 9 | namespace Service { | ||
| 10 | namespace GSP { | ||
| 11 | |||
| 12 | class GSP_LCD final : public Interface { | ||
| 13 | public: | ||
| 14 | GSP_LCD(); | ||
| 15 | |||
| 16 | std::string GetPortName() const override { | ||
| 17 | return "gsp::Lcd"; | ||
| 18 | } | ||
| 19 | }; | ||
| 20 | |||
| 21 | } // namespace GSP | ||
| 22 | } // namespace Service | ||
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 20ea2c389..e1f22ca97 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp | |||
| @@ -15,7 +15,6 @@ | |||
| 15 | #include "core/hle/kernel/handle_table.h" | 15 | #include "core/hle/kernel/handle_table.h" |
| 16 | #include "core/hle/service/dsp_dsp.h" | 16 | #include "core/hle/service/dsp_dsp.h" |
| 17 | #include "core/hle/service/gsp_gpu.h" | 17 | #include "core/hle/service/gsp_gpu.h" |
| 18 | #include "core/hle/service/gsp_lcd.h" | ||
| 19 | #include "core/hle/service/hid/hid.h" | 18 | #include "core/hle/service/hid/hid.h" |
| 20 | #include "core/hle/service/service.h" | 19 | #include "core/hle/service/service.h" |
| 21 | #include "core/hle/service/sm/sm.h" | 20 | #include "core/hle/service/sm/sm.h" |
| @@ -190,7 +189,6 @@ void Init() { | |||
| 190 | 189 | ||
| 191 | AddService(new DSP_DSP::Interface); | 190 | AddService(new DSP_DSP::Interface); |
| 192 | AddService(new GSP::GSP_GPU); | 191 | AddService(new GSP::GSP_GPU); |
| 193 | AddService(new GSP::GSP_LCD); | ||
| 194 | 192 | ||
| 195 | LOG_DEBUG(Service, "initialized OK"); | 193 | LOG_DEBUG(Service, "initialized OK"); |
| 196 | } | 194 | } |