2016-02-21 14:13:52 +01:00
|
|
|
// Copyright 2016 Citra Emulator Project
|
|
|
|
// Licensed under GPLv2 or any later version
|
|
|
|
// Refer to the license.txt file included.
|
|
|
|
|
|
|
|
#include <array>
|
|
|
|
#include <vector>
|
2016-03-06 22:13:12 +01:00
|
|
|
#include "audio_core/hle/dsp.h"
|
2016-09-21 08:52:38 +02:00
|
|
|
#include "audio_core/hle/pipe.h"
|
2016-03-06 22:13:12 +01:00
|
|
|
#include "common/assert.h"
|
2016-02-21 14:13:52 +01:00
|
|
|
#include "common/common_types.h"
|
|
|
|
#include "common/logging/log.h"
|
2016-04-24 11:21:10 +02:00
|
|
|
#include "core/hle/service/dsp_dsp.h"
|
|
|
|
|
2016-02-21 14:13:52 +01:00
|
|
|
namespace DSP {
|
|
|
|
namespace HLE {
|
|
|
|
|
2016-03-06 22:13:12 +01:00
|
|
|
static DspState dsp_state = DspState::Off;
|
|
|
|
|
2016-04-24 10:12:15 +02:00
|
|
|
static std::array<std::vector<u8>, NUM_DSP_PIPE> pipe_data;
|
2016-02-21 14:13:52 +01:00
|
|
|
|
|
|
|
void ResetPipes() {
|
2016-03-06 22:13:12 +01:00
|
|
|
for (auto& data : pipe_data) {
|
|
|
|
data.clear();
|
|
|
|
}
|
|
|
|
dsp_state = DspState::Off;
|
2016-02-21 14:13:52 +01:00
|
|
|
}
|
|
|
|
|
2016-03-06 22:13:12 +01:00
|
|
|
std::vector<u8> PipeRead(DspPipe pipe_number, u32 length) {
|
2016-04-24 10:12:15 +02:00
|
|
|
const size_t pipe_index = static_cast<size_t>(pipe_number);
|
|
|
|
|
|
|
|
if (pipe_index >= NUM_DSP_PIPE) {
|
|
|
|
LOG_ERROR(Audio_DSP, "pipe_number = %zu invalid", pipe_index);
|
2016-03-06 22:13:12 +01:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2016-04-25 22:10:03 +02:00
|
|
|
if (length > UINT16_MAX) { // Can only read at most UINT16_MAX from the pipe
|
|
|
|
LOG_ERROR(Audio_DSP, "length of %u greater than max of %u", length, UINT16_MAX);
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2016-04-24 10:12:15 +02:00
|
|
|
std::vector<u8>& data = pipe_data[pipe_index];
|
2016-03-06 22:13:12 +01:00
|
|
|
|
|
|
|
if (length > data.size()) {
|
2016-09-18 02:38:01 +02:00
|
|
|
LOG_WARNING(
|
|
|
|
Audio_DSP,
|
|
|
|
"pipe_number = %zu is out of data, application requested read of %u but %zu remain",
|
|
|
|
pipe_index, length, data.size());
|
2016-04-25 22:10:03 +02:00
|
|
|
length = static_cast<u32>(data.size());
|
2016-02-21 14:13:52 +01:00
|
|
|
}
|
|
|
|
|
2016-03-06 22:13:12 +01:00
|
|
|
if (length == 0)
|
2016-02-21 14:13:52 +01:00
|
|
|
return {};
|
2016-03-06 22:13:12 +01:00
|
|
|
|
|
|
|
std::vector<u8> ret(data.begin(), data.begin() + length);
|
|
|
|
data.erase(data.begin(), data.begin() + length);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t GetPipeReadableSize(DspPipe pipe_number) {
|
2016-04-24 10:12:15 +02:00
|
|
|
const size_t pipe_index = static_cast<size_t>(pipe_number);
|
|
|
|
|
|
|
|
if (pipe_index >= NUM_DSP_PIPE) {
|
|
|
|
LOG_ERROR(Audio_DSP, "pipe_number = %zu invalid", pipe_index);
|
2016-03-06 22:13:12 +01:00
|
|
|
return 0;
|
2016-02-21 14:13:52 +01:00
|
|
|
}
|
|
|
|
|
2016-04-24 10:12:15 +02:00
|
|
|
return pipe_data[pipe_index].size();
|
2016-03-06 22:13:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void WriteU16(DspPipe pipe_number, u16 value) {
|
2016-04-24 10:12:15 +02:00
|
|
|
const size_t pipe_index = static_cast<size_t>(pipe_number);
|
|
|
|
|
|
|
|
std::vector<u8>& data = pipe_data.at(pipe_index);
|
2016-03-06 22:13:12 +01:00
|
|
|
// Little endian
|
|
|
|
data.emplace_back(value & 0xFF);
|
|
|
|
data.emplace_back(value >> 8);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void AudioPipeWriteStructAddresses() {
|
|
|
|
// These struct addresses are DSP dram addresses.
|
|
|
|
// See also: DSP_DSP::ConvertProcessAddressFromDspDram
|
|
|
|
static const std::array<u16, 15> struct_addresses = {
|
|
|
|
0x8000 + offsetof(SharedMemory, frame_counter) / 2,
|
|
|
|
0x8000 + offsetof(SharedMemory, source_configurations) / 2,
|
|
|
|
0x8000 + offsetof(SharedMemory, source_statuses) / 2,
|
|
|
|
0x8000 + offsetof(SharedMemory, adpcm_coefficients) / 2,
|
|
|
|
0x8000 + offsetof(SharedMemory, dsp_configuration) / 2,
|
|
|
|
0x8000 + offsetof(SharedMemory, dsp_status) / 2,
|
|
|
|
0x8000 + offsetof(SharedMemory, final_samples) / 2,
|
|
|
|
0x8000 + offsetof(SharedMemory, intermediate_mix_samples) / 2,
|
|
|
|
0x8000 + offsetof(SharedMemory, compressor) / 2,
|
|
|
|
0x8000 + offsetof(SharedMemory, dsp_debug) / 2,
|
|
|
|
0x8000 + offsetof(SharedMemory, unknown10) / 2,
|
|
|
|
0x8000 + offsetof(SharedMemory, unknown11) / 2,
|
|
|
|
0x8000 + offsetof(SharedMemory, unknown12) / 2,
|
|
|
|
0x8000 + offsetof(SharedMemory, unknown13) / 2,
|
2016-09-19 03:01:46 +02:00
|
|
|
0x8000 + offsetof(SharedMemory, unknown14) / 2,
|
|
|
|
};
|
2016-03-06 22:13:12 +01:00
|
|
|
|
|
|
|
// Begin with a u16 denoting the number of structs.
|
2016-04-25 22:10:03 +02:00
|
|
|
WriteU16(DspPipe::Audio, static_cast<u16>(struct_addresses.size()));
|
2016-03-06 22:13:12 +01:00
|
|
|
// Then write the struct addresses.
|
|
|
|
for (u16 addr : struct_addresses) {
|
|
|
|
WriteU16(DspPipe::Audio, addr);
|
2016-02-21 14:13:52 +01:00
|
|
|
}
|
2016-04-24 11:21:10 +02:00
|
|
|
// Signal that we have data on this pipe.
|
2016-12-10 13:51:50 +01:00
|
|
|
Service::DSP_DSP::SignalPipeInterrupt(DspPipe::Audio);
|
2016-03-06 22:13:12 +01:00
|
|
|
}
|
2016-02-21 14:13:52 +01:00
|
|
|
|
2016-03-06 22:13:12 +01:00
|
|
|
void PipeWrite(DspPipe pipe_number, const std::vector<u8>& buffer) {
|
|
|
|
switch (pipe_number) {
|
|
|
|
case DspPipe::Audio: {
|
|
|
|
if (buffer.size() != 4) {
|
2016-09-18 02:38:01 +02:00
|
|
|
LOG_ERROR(Audio_DSP, "DspPipe::Audio: Unexpected buffer length %zu was written",
|
|
|
|
buffer.size());
|
2016-03-06 22:13:12 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-09-19 03:01:46 +02:00
|
|
|
enum class StateChange {
|
2016-10-20 16:26:59 +02:00
|
|
|
Initialize = 0,
|
2016-09-19 03:01:46 +02:00
|
|
|
Shutdown = 1,
|
|
|
|
Wakeup = 2,
|
|
|
|
Sleep = 3,
|
|
|
|
};
|
2016-03-06 22:13:12 +01:00
|
|
|
|
|
|
|
// The difference between Initialize and Wakeup is that Input state is maintained
|
|
|
|
// when sleeping but isn't when turning it off and on again. (TODO: Implement this.)
|
|
|
|
// Waking up from sleep garbles some of the structs in the memory region. (TODO:
|
|
|
|
// Implement this.) Applications store away the state of these structs before
|
|
|
|
// sleeping and reset it back after wakeup on behalf of the DSP.
|
|
|
|
|
|
|
|
switch (static_cast<StateChange>(buffer[0])) {
|
2016-10-20 16:26:59 +02:00
|
|
|
case StateChange::Initialize:
|
2016-03-06 22:13:12 +01:00
|
|
|
LOG_INFO(Audio_DSP, "Application has requested initialization of DSP hardware");
|
|
|
|
ResetPipes();
|
|
|
|
AudioPipeWriteStructAddresses();
|
|
|
|
dsp_state = DspState::On;
|
|
|
|
break;
|
|
|
|
case StateChange::Shutdown:
|
|
|
|
LOG_INFO(Audio_DSP, "Application has requested shutdown of DSP hardware");
|
|
|
|
dsp_state = DspState::Off;
|
|
|
|
break;
|
|
|
|
case StateChange::Wakeup:
|
|
|
|
LOG_INFO(Audio_DSP, "Application has requested wakeup of DSP hardware");
|
|
|
|
ResetPipes();
|
|
|
|
AudioPipeWriteStructAddresses();
|
|
|
|
dsp_state = DspState::On;
|
|
|
|
break;
|
|
|
|
case StateChange::Sleep:
|
|
|
|
LOG_INFO(Audio_DSP, "Application has requested sleep of DSP hardware");
|
|
|
|
UNIMPLEMENTED();
|
|
|
|
dsp_state = DspState::Sleeping;
|
|
|
|
break;
|
|
|
|
default:
|
2016-09-18 02:38:01 +02:00
|
|
|
LOG_ERROR(Audio_DSP,
|
|
|
|
"Application has requested unknown state transition of DSP hardware %hhu",
|
|
|
|
buffer[0]);
|
2016-03-06 22:13:12 +01:00
|
|
|
dsp_state = DspState::Off;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
default:
|
2016-09-18 02:38:01 +02:00
|
|
|
LOG_CRITICAL(Audio_DSP, "pipe_number = %zu unimplemented",
|
|
|
|
static_cast<size_t>(pipe_number));
|
2016-03-06 22:13:12 +01:00
|
|
|
UNIMPLEMENTED();
|
|
|
|
return;
|
|
|
|
}
|
2016-02-21 14:13:52 +01:00
|
|
|
}
|
|
|
|
|
2016-03-06 22:13:12 +01:00
|
|
|
DspState GetDspState() {
|
|
|
|
return dsp_state;
|
2016-02-21 14:13:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace HLE
|
|
|
|
} // namespace DSP
|