diff options
| author | 2016-04-24 10:21:10 +0100 | |
|---|---|---|
| committer | 2016-04-27 06:35:06 +0100 | |
| commit | ff6db69c6052f674265c453932a3dc7637c46412 (patch) | |
| tree | 74f174997c4ee9376a77bbe35ef59fbd3be77237 /src | |
| parent | DSP/Pipe: There are 8 pipes (diff) | |
| download | yuzu-ff6db69c6052f674265c453932a3dc7637c46412.tar.gz yuzu-ff6db69c6052f674265c453932a3dc7637c46412.tar.xz yuzu-ff6db69c6052f674265c453932a3dc7637c46412.zip | |
DSP_DSP: Updated interrupt implementation
Diffstat (limited to 'src')
| -rw-r--r-- | src/audio_core/audio_core.cpp | 7 | ||||
| -rw-r--r-- | src/audio_core/hle/pipe.cpp | 4 | ||||
| -rw-r--r-- | src/core/hle/service/dsp_dsp.cpp | 129 | ||||
| -rw-r--r-- | src/core/hle/service/dsp_dsp.h | 19 |
4 files changed, 113 insertions, 46 deletions
diff --git a/src/audio_core/audio_core.cpp b/src/audio_core/audio_core.cpp index 894f46990..0685eaf85 100644 --- a/src/audio_core/audio_core.cpp +++ b/src/audio_core/audio_core.cpp | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include "audio_core/audio_core.h" | 5 | #include "audio_core/audio_core.h" |
| 6 | #include "audio_core/hle/dsp.h" | 6 | #include "audio_core/hle/dsp.h" |
| 7 | #include "audio_core/hle/pipe.h" | ||
| 7 | 8 | ||
| 8 | #include "core/core_timing.h" | 9 | #include "core/core_timing.h" |
| 9 | #include "core/hle/kernel/vm_manager.h" | 10 | #include "core/hle/kernel/vm_manager.h" |
| @@ -17,10 +18,8 @@ static constexpr u64 audio_frame_ticks = 1310252ull; ///< Units: ARM11 cycles | |||
| 17 | 18 | ||
| 18 | static void AudioTickCallback(u64 /*userdata*/, int cycles_late) { | 19 | static void AudioTickCallback(u64 /*userdata*/, int cycles_late) { |
| 19 | if (DSP::HLE::Tick()) { | 20 | if (DSP::HLE::Tick()) { |
| 20 | // HACK: We're not signaling the interrups when they should be, but just firing them all off together. | 21 | // TODO(merry): Signal all the other interrupts as appropriate. |
| 21 | // It should be only (interrupt_id = 2, channel_id = 2) that's signalled here. | 22 | DSP_DSP::SignalPipeInterrupt(DSP::HLE::DspPipe::Audio); |
| 22 | // TODO(merry): Understand when the other interrupts are fired. | ||
| 23 | DSP_DSP::SignalAllInterrupts(); | ||
| 24 | } | 23 | } |
| 25 | 24 | ||
| 26 | // Reschedule recurrent event | 25 | // Reschedule recurrent event |
diff --git a/src/audio_core/hle/pipe.cpp b/src/audio_core/hle/pipe.cpp index 7ec97dfda..03280780f 100644 --- a/src/audio_core/hle/pipe.cpp +++ b/src/audio_core/hle/pipe.cpp | |||
| @@ -12,6 +12,8 @@ | |||
| 12 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| 13 | #include "common/logging/log.h" | 13 | #include "common/logging/log.h" |
| 14 | 14 | ||
| 15 | #include "core/hle/service/dsp_dsp.h" | ||
| 16 | |||
| 15 | namespace DSP { | 17 | namespace DSP { |
| 16 | namespace HLE { | 18 | namespace HLE { |
| 17 | 19 | ||
| @@ -97,6 +99,8 @@ static void AudioPipeWriteStructAddresses() { | |||
| 97 | for (u16 addr : struct_addresses) { | 99 | for (u16 addr : struct_addresses) { |
| 98 | WriteU16(DspPipe::Audio, addr); | 100 | WriteU16(DspPipe::Audio, addr); |
| 99 | } | 101 | } |
| 102 | // Signal that we have data on this pipe. | ||
| 103 | DSP_DSP::SignalPipeInterrupt(DspPipe::Audio); | ||
| 100 | } | 104 | } |
| 101 | 105 | ||
| 102 | void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) { | 106 | void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) { |
diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp index 751cb3d61..e9ef50f86 100644 --- a/src/core/hle/service/dsp_dsp.cpp +++ b/src/core/hle/service/dsp_dsp.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 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> | ||
| 5 | #include <cinttypes> | 6 | #include <cinttypes> |
| 6 | 7 | ||
| 7 | #include "audio_core/hle/pipe.h" | 8 | #include "audio_core/hle/pipe.h" |
| @@ -12,6 +13,8 @@ | |||
| 12 | #include "core/hle/kernel/event.h" | 13 | #include "core/hle/kernel/event.h" |
| 13 | #include "core/hle/service/dsp_dsp.h" | 14 | #include "core/hle/service/dsp_dsp.h" |
| 14 | 15 | ||
| 16 | using DspPipe = DSP::HLE::DspPipe; | ||
| 17 | |||
| 15 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 18 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 16 | // Namespace DSP_DSP | 19 | // Namespace DSP_DSP |
| 17 | 20 | ||
| @@ -19,29 +22,71 @@ namespace DSP_DSP { | |||
| 19 | 22 | ||
| 20 | static Kernel::SharedPtr<Kernel::Event> semaphore_event; | 23 | static Kernel::SharedPtr<Kernel::Event> semaphore_event; |
| 21 | 24 | ||
| 22 | struct PairHash { | 25 | /// There are three types of interrupts |
| 23 | template <typename T, typename U> | 26 | enum class InterruptType { |
| 24 | std::size_t operator()(const std::pair<T, U> &x) const { | 27 | Zero, One, Pipe |
| 25 | // TODO(yuriks): Replace with better hash combining function. | 28 | }; |
| 26 | return std::hash<T>()(x.first) ^ std::hash<U>()(x.second); | 29 | constexpr size_t NUM_INTERRUPT_TYPE = 3; |
| 30 | |||
| 31 | class InterruptEvents final { | ||
| 32 | public: | ||
| 33 | void Signal(InterruptType type, DspPipe pipe) { | ||
| 34 | Kernel::SharedPtr<Kernel::Event>& event = Get(type, pipe); | ||
| 35 | if (event) { | ||
| 36 | event->Signal(); | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | Kernel::SharedPtr<Kernel::Event>& Get(InterruptType type, DspPipe dsp_pipe) { | ||
| 41 | switch (type) { | ||
| 42 | case InterruptType::Zero: | ||
| 43 | return zero; | ||
| 44 | case InterruptType::One: | ||
| 45 | return one; | ||
| 46 | case InterruptType::Pipe: { | ||
| 47 | const size_t pipe_index = static_cast<size_t>(dsp_pipe); | ||
| 48 | ASSERT(pipe_index < DSP::HLE::NUM_DSP_PIPE); | ||
| 49 | return pipe[pipe_index]; | ||
| 50 | } | ||
| 51 | } | ||
| 52 | |||
| 53 | UNREACHABLE_MSG("Invalid interrupt type = %zu", static_cast<size_t>(type)); | ||
| 54 | } | ||
| 55 | |||
| 56 | bool HasTooManyEventsRegistered() const { | ||
| 57 | // Actual service implementation only has 6 'slots' for interrupts. | ||
| 58 | constexpr size_t max_number_of_interrupt_events = 6; | ||
| 59 | |||
| 60 | size_t number = std::count_if(pipe.begin(), pipe.end(), [](const auto& evt) { | ||
| 61 | return evt != nullptr; | ||
| 62 | }); | ||
| 63 | |||
| 64 | if (zero != nullptr) | ||
| 65 | number++; | ||
| 66 | if (one != nullptr) | ||
| 67 | number++; | ||
| 68 | |||
| 69 | return number >= max_number_of_interrupt_events; | ||
| 27 | } | 70 | } |
| 71 | |||
| 72 | private: | ||
| 73 | /// Currently unknown purpose | ||
| 74 | Kernel::SharedPtr<Kernel::Event> zero = nullptr; | ||
| 75 | /// Currently unknown purpose | ||
| 76 | Kernel::SharedPtr<Kernel::Event> one = nullptr; | ||
| 77 | /// Each DSP pipe has an associated interrupt | ||
| 78 | std::array<Kernel::SharedPtr<Kernel::Event>, DSP::HLE::NUM_DSP_PIPE> pipe = {{}}; | ||
| 28 | }; | 79 | }; |
| 29 | 80 | ||
| 30 | /// Map of (audio interrupt number, channel number) to Kernel::Events. See: RegisterInterruptEvents | 81 | static InterruptEvents interrupt_events; |
| 31 | static std::unordered_map<std::pair<u32, u32>, Kernel::SharedPtr<Kernel::Event>, PairHash> interrupt_events; | ||
| 32 | 82 | ||
| 33 | // DSP Interrupts: | 83 | // DSP Interrupts: |
| 34 | // Interrupt #2 occurs every frame tick. Userland programs normally have a thread that's waiting | 84 | // The audio-pipe interrupt occurs every frame tick. Userland programs normally have a thread |
| 35 | // for an interrupt event. Immediately after this interrupt event, userland normally updates the | 85 | // that's waiting for an interrupt event. Immediately after this interrupt event, userland |
| 36 | // state in the next region and increments the relevant frame counter by two. | 86 | // normally updates the state in the next region and increments the relevant frame counter by |
| 37 | void SignalAllInterrupts() { | 87 | // two. |
| 38 | // HACK: The other interrupts have currently unknown purpose, we trigger them each tick in any case. | 88 | void SignalPipeInterrupt(DspPipe pipe) { |
| 39 | for (auto& interrupt_event : interrupt_events) | 89 | interrupt_events.Signal(InterruptType::Pipe, pipe); |
| 40 | interrupt_event.second->Signal(); | ||
| 41 | } | ||
| 42 | |||
| 43 | void SignalInterrupt(u32 interrupt, u32 channel) { | ||
| 44 | interrupt_events[std::make_pair(interrupt, channel)]->Signal(); | ||
| 45 | } | 90 | } |
| 46 | 91 | ||
| 47 | /** | 92 | /** |
| @@ -147,8 +192,8 @@ static void FlushDataCache(Service::Interface* self) { | |||
| 147 | /** | 192 | /** |
| 148 | * DSP_DSP::RegisterInterruptEvents service function | 193 | * DSP_DSP::RegisterInterruptEvents service function |
| 149 | * Inputs: | 194 | * Inputs: |
| 150 | * 1 : Interrupt Number | 195 | * 1 : Interrupt Type |
| 151 | * 2 : Channel Number | 196 | * 2 : Pipe Number |
| 152 | * 4 : Interrupt event handle | 197 | * 4 : Interrupt event handle |
| 153 | * Outputs: | 198 | * Outputs: |
| 154 | * 1 : Result of function, 0 on success, otherwise error code | 199 | * 1 : Result of function, 0 on success, otherwise error code |
| @@ -156,23 +201,40 @@ static void FlushDataCache(Service::Interface* self) { | |||
| 156 | static void RegisterInterruptEvents(Service::Interface* self) { | 201 | static void RegisterInterruptEvents(Service::Interface* self) { |
| 157 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 202 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 158 | 203 | ||
| 159 | u32 interrupt = cmd_buff[1]; | 204 | u32 type_index = cmd_buff[1]; |
| 160 | u32 channel = cmd_buff[2]; | 205 | u32 pipe_index = cmd_buff[2]; |
| 161 | u32 event_handle = cmd_buff[4]; | 206 | u32 event_handle = cmd_buff[4]; |
| 162 | 207 | ||
| 208 | ASSERT_MSG(type_index < NUM_INTERRUPT_TYPE && pipe_index < DSP::HLE::NUM_DSP_PIPE, | ||
| 209 | "Invalid type or pipe: type = %u, pipe = %u", type_index, pipe_index); | ||
| 210 | |||
| 211 | InterruptType type = static_cast<InterruptType>(cmd_buff[1]); | ||
| 212 | DspPipe pipe = static_cast<DspPipe>(cmd_buff[2]); | ||
| 213 | |||
| 214 | cmd_buff[0] = IPC::MakeHeader(0x15, 1, 0); | ||
| 215 | |||
| 163 | if (event_handle) { | 216 | if (event_handle) { |
| 164 | auto evt = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[4]); | 217 | auto evt = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[4]); |
| 165 | if (evt) { | 218 | |
| 166 | interrupt_events[std::make_pair(interrupt, channel)] = evt; | 219 | if (!evt) { |
| 167 | cmd_buff[1] = RESULT_SUCCESS.raw; | 220 | LOG_INFO(Service_DSP, "Invalid event handle! type=%u, pipe=%u, event_handle=0x%08X", type_index, pipe_index, event_handle); |
| 168 | LOG_INFO(Service_DSP, "Registered interrupt=%u, channel=%u, event_handle=0x%08X", interrupt, channel, event_handle); | 221 | ASSERT(false); // TODO: This should really be handled at an IPC translation layer. |
| 169 | } else { | 222 | } |
| 170 | LOG_CRITICAL(Service_DSP, "Invalid event handle! interrupt=%u, channel=%u, event_handle=0x%08X", interrupt, channel, event_handle); | 223 | |
| 171 | ASSERT(false); // This should really be handled at a IPC translation layer. | 224 | if (interrupt_events.HasTooManyEventsRegistered()) { |
| 225 | LOG_INFO(Service_DSP, "Ran out of space to register interrupts (Attempted to register type=%u, pipe=%u, event_handle=0x%08X)", | ||
| 226 | type_index, pipe_index, event_handle); | ||
| 227 | cmd_buff[1] = ResultCode(ErrorDescription::InvalidResultValue, ErrorModule::DSP, ErrorSummary::OutOfResource, ErrorLevel::Status).raw; | ||
| 228 | return; | ||
| 172 | } | 229 | } |
| 230 | |||
| 231 | interrupt_events.Get(type, pipe) = evt; | ||
| 232 | LOG_INFO(Service_DSP, "Registered type=%u, pipe=%u, event_handle=0x%08X", type_index, pipe_index, event_handle); | ||
| 233 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 173 | } else { | 234 | } else { |
| 174 | interrupt_events.erase(std::make_pair(interrupt, channel)); | 235 | interrupt_events.Get(type, pipe) = nullptr; |
| 175 | LOG_INFO(Service_DSP, "Unregistered interrupt=%u, channel=%u, event_handle=0x%08X", interrupt, channel, event_handle); | 236 | LOG_INFO(Service_DSP, "Unregistered interrupt=%u, channel=%u, event_handle=0x%08X", type_index, pipe_index, event_handle); |
| 237 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 176 | } | 238 | } |
| 177 | } | 239 | } |
| 178 | 240 | ||
| @@ -194,7 +256,7 @@ static void SetSemaphore(Service::Interface* self) { | |||
| 194 | /** | 256 | /** |
| 195 | * DSP_DSP::WriteProcessPipe service function | 257 | * DSP_DSP::WriteProcessPipe service function |
| 196 | * Inputs: | 258 | * Inputs: |
| 197 | * 1 : Channel | 259 | * 1 : Pipe Number |
| 198 | * 2 : Size | 260 | * 2 : Size |
| 199 | * 3 : (size << 14) | 0x402 | 261 | * 3 : (size << 14) | 0x402 |
| 200 | * 4 : Buffer | 262 | * 4 : Buffer |
| @@ -457,13 +519,14 @@ const Interface::FunctionInfo FunctionTable[] = { | |||
| 457 | 519 | ||
| 458 | Interface::Interface() { | 520 | Interface::Interface() { |
| 459 | semaphore_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "DSP_DSP::semaphore_event"); | 521 | semaphore_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "DSP_DSP::semaphore_event"); |
| 522 | interrupt_events = {}; | ||
| 460 | 523 | ||
| 461 | Register(FunctionTable); | 524 | Register(FunctionTable); |
| 462 | } | 525 | } |
| 463 | 526 | ||
| 464 | Interface::~Interface() { | 527 | Interface::~Interface() { |
| 465 | semaphore_event = nullptr; | 528 | semaphore_event = nullptr; |
| 466 | interrupt_events.clear(); | 529 | interrupt_events = {}; |
| 467 | } | 530 | } |
| 468 | 531 | ||
| 469 | } // namespace | 532 | } // namespace |
diff --git a/src/core/hle/service/dsp_dsp.h b/src/core/hle/service/dsp_dsp.h index 32b89e9bb..22f6687cc 100644 --- a/src/core/hle/service/dsp_dsp.h +++ b/src/core/hle/service/dsp_dsp.h | |||
| @@ -8,6 +8,12 @@ | |||
| 8 | 8 | ||
| 9 | #include "core/hle/service/service.h" | 9 | #include "core/hle/service/service.h" |
| 10 | 10 | ||
| 11 | namespace DSP { | ||
| 12 | namespace HLE { | ||
| 13 | enum class DspPipe; | ||
| 14 | } | ||
| 15 | } | ||
| 16 | |||
| 11 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 17 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 12 | // Namespace DSP_DSP | 18 | // Namespace DSP_DSP |
| 13 | 19 | ||
| @@ -23,15 +29,10 @@ public: | |||
| 23 | } | 29 | } |
| 24 | }; | 30 | }; |
| 25 | 31 | ||
| 26 | /// Signal all audio related interrupts. | ||
| 27 | void SignalAllInterrupts(); | ||
| 28 | |||
| 29 | /** | 32 | /** |
| 30 | * Signal a specific audio related interrupt based on interrupt id and channel id. | 33 | * Signal a specific DSP related interrupt of type == InterruptType::Pipe, pipe == pipe. |
| 31 | * @param interrupt_id The interrupt id | 34 | * @param pipe The DSP pipe for which to signal an interrupt for. |
| 32 | * @param channel_id The channel id | ||
| 33 | * The significance of various values of interrupt_id and channel_id is not yet known. | ||
| 34 | */ | 35 | */ |
| 35 | void SignalInterrupt(u32 interrupt_id, u32 channel_id); | 36 | void SignalPipeInterrupt(DSP::HLE::DspPipe pipe); |
| 36 | 37 | ||
| 37 | } // namespace | 38 | } // namespace DSP_DSP |