diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/hle/service/gsp_gpu.cpp | 97 |
1 files changed, 91 insertions, 6 deletions
diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp index 4c3ac845b..dcc1b6942 100644 --- a/src/core/hle/service/gsp_gpu.cpp +++ b/src/core/hle/service/gsp_gpu.cpp | |||
| @@ -48,20 +48,42 @@ static inline InterruptRelayQueue* GetInterruptRelayQueue(u32 thread_id) { | |||
| 48 | return reinterpret_cast<InterruptRelayQueue*>(ptr.ValueOr(nullptr)); | 48 | return reinterpret_cast<InterruptRelayQueue*>(ptr.ValueOr(nullptr)); |
| 49 | } | 49 | } |
| 50 | 50 | ||
| 51 | static void WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) { | 51 | /** |
| 52 | * Checks if the parameters in a register write call are valid and logs in the case that | ||
| 53 | * they are not | ||
| 54 | * @param base_address The first address in the sequence of registers that will be written | ||
| 55 | * @param size_in_bytes The number of registers that will be written | ||
| 56 | * @return true if the parameters are valid, false otherwise | ||
| 57 | */ | ||
| 58 | static bool CheckWriteParameters(u32 base_address, u32 size_in_bytes) { | ||
| 52 | // TODO: Return proper error codes | 59 | // TODO: Return proper error codes |
| 53 | if (base_address + size_in_bytes >= 0x420000) { | 60 | if (base_address + size_in_bytes >= 0x420000) { |
| 54 | LOG_ERROR(Service_GSP, "Write address out of range! (address=0x%08x, size=0x%08x)", | 61 | LOG_ERROR(Service_GSP, "Write address out of range! (address=0x%08x, size=0x%08x)", |
| 55 | base_address, size_in_bytes); | 62 | base_address, size_in_bytes); |
| 56 | return; | 63 | return false; |
| 57 | } | 64 | } |
| 58 | 65 | ||
| 59 | // size should be word-aligned | 66 | // size should be word-aligned |
| 60 | if ((size_in_bytes % 4) != 0) { | 67 | if ((size_in_bytes % 4) != 0) { |
| 61 | LOG_ERROR(Service_GSP, "Invalid size 0x%08x", size_in_bytes); | 68 | LOG_ERROR(Service_GSP, "Invalid size 0x%08x", size_in_bytes); |
| 62 | return; | 69 | return false; |
| 63 | } | 70 | } |
| 64 | 71 | ||
| 72 | return true; | ||
| 73 | } | ||
| 74 | |||
| 75 | /** | ||
| 76 | * Writes sequential GSP GPU hardware registers using an array of source data | ||
| 77 | * | ||
| 78 | * @param base_address The address of the first register in the sequence | ||
| 79 | * @param size_in_bytes The number of registers to update (size of data) | ||
| 80 | * @param data A pointer to the source data | ||
| 81 | */ | ||
| 82 | static void WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) { | ||
| 83 | // TODO: Return proper error codes | ||
| 84 | if (!CheckWriteParameters(base_address, size_in_bytes)) | ||
| 85 | return; | ||
| 86 | |||
| 65 | while (size_in_bytes > 0) { | 87 | while (size_in_bytes > 0) { |
| 66 | GPU::Write<u32>(base_address + 0x1EB00000, *data); | 88 | GPU::Write<u32>(base_address + 0x1EB00000, *data); |
| 67 | 89 | ||
| @@ -71,17 +93,80 @@ static void WriteHWRegs(u32 base_address, u32 size_in_bytes, const u32* data) { | |||
| 71 | } | 93 | } |
| 72 | } | 94 | } |
| 73 | 95 | ||
| 74 | /// Write a GSP GPU hardware register | 96 | /** |
| 97 | * GSP_GPU::WriteHWRegs service function | ||
| 98 | * | ||
| 99 | * Writes sequential GSP GPU hardware registers | ||
| 100 | * | ||
| 101 | * Inputs: | ||
| 102 | * 1 : address of first GPU register | ||
| 103 | * 2 : number of registers to write sequentially | ||
| 104 | * 4 : pointer to source data array | ||
| 105 | */ | ||
| 75 | static void WriteHWRegs(Service::Interface* self) { | 106 | static void WriteHWRegs(Service::Interface* self) { |
| 76 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 107 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 77 | u32 reg_addr = cmd_buff[1]; | 108 | u32 reg_addr = cmd_buff[1]; |
| 78 | u32 size = cmd_buff[2]; | 109 | u32 size = cmd_buff[2]; |
| 79 | 110 | ||
| 80 | u32* src = (u32*)Memory::GetPointer(cmd_buff[0x4]); | 111 | u32* src = (u32*)Memory::GetPointer(cmd_buff[4]); |
| 81 | 112 | ||
| 82 | WriteHWRegs(reg_addr, size, src); | 113 | WriteHWRegs(reg_addr, size, src); |
| 83 | } | 114 | } |
| 84 | 115 | ||
| 116 | /** | ||
| 117 | * Updates sequential GSP GPU hardware registers using parallel arrays of source data and masks. | ||
| 118 | * For each register, the value is updated only where the mask is high | ||
| 119 | * | ||
| 120 | * @param base_address The address of the first register in the sequence | ||
| 121 | * @param size_in_bytes The number of registers to update (size of data) | ||
| 122 | * @param data A pointer to the source data to use for updates | ||
| 123 | * @param masks A pointer to the masks | ||
| 124 | */ | ||
| 125 | static void WriteHWRegsWithMask(u32 base_address, u32 size_in_bytes, const u32* data, const u32* masks) { | ||
| 126 | // TODO: Return proper error codes | ||
| 127 | if (!CheckWriteParameters(base_address, size_in_bytes)) | ||
| 128 | return; | ||
| 129 | |||
| 130 | while (size_in_bytes > 0) { | ||
| 131 | const u32 reg_address = base_address + 0x1EB00000; | ||
| 132 | |||
| 133 | u32 reg_value; | ||
| 134 | GPU::Read<u32>(reg_value, reg_address); | ||
| 135 | |||
| 136 | // Update the current value of the register only for set mask bits | ||
| 137 | reg_value = (reg_value & ~*masks) | (*data | *masks); | ||
| 138 | |||
| 139 | GPU::Write<u32>(reg_address, reg_value); | ||
| 140 | |||
| 141 | size_in_bytes -= 4; | ||
| 142 | ++data; | ||
| 143 | ++masks; | ||
| 144 | base_address += 4; | ||
| 145 | } | ||
| 146 | } | ||
| 147 | |||
| 148 | /** | ||
| 149 | * GSP_GPU::WriteHWRegsWithMask service function | ||
| 150 | * | ||
| 151 | * Updates sequential GSP GPU hardware registers using masks | ||
| 152 | * | ||
| 153 | * Inputs: | ||
| 154 | * 1 : address of first GPU register | ||
| 155 | * 2 : number of registers to update sequentially | ||
| 156 | * 4 : pointer to source data array | ||
| 157 | * 6 : pointer to mask array | ||
| 158 | */ | ||
| 159 | static void WriteHWRegsWithMask(Service::Interface* self) { | ||
| 160 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 161 | u32 reg_addr = cmd_buff[1]; | ||
| 162 | u32 size = cmd_buff[2]; | ||
| 163 | |||
| 164 | u32* src_data = (u32*)Memory::GetPointer(cmd_buff[4]); | ||
| 165 | u32* mask_data = (u32*)Memory::GetPointer(cmd_buff[6]); | ||
| 166 | |||
| 167 | WriteHWRegsWithMask(reg_addr, size, src_data, mask_data); | ||
| 168 | } | ||
| 169 | |||
| 85 | /// Read a GSP GPU hardware register | 170 | /// Read a GSP GPU hardware register |
| 86 | static void ReadHWRegs(Service::Interface* self) { | 171 | static void ReadHWRegs(Service::Interface* self) { |
| 87 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 172 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| @@ -350,7 +435,7 @@ static void TriggerCmdReqQueue(Service::Interface* self) { | |||
| 350 | 435 | ||
| 351 | const Interface::FunctionInfo FunctionTable[] = { | 436 | const Interface::FunctionInfo FunctionTable[] = { |
| 352 | {0x00010082, WriteHWRegs, "WriteHWRegs"}, | 437 | {0x00010082, WriteHWRegs, "WriteHWRegs"}, |
| 353 | {0x00020084, nullptr, "WriteHWRegsWithMask"}, | 438 | {0x00020084, WriteHWRegsWithMask, "WriteHWRegsWithMask"}, |
| 354 | {0x00030082, nullptr, "WriteHWRegRepeat"}, | 439 | {0x00030082, nullptr, "WriteHWRegRepeat"}, |
| 355 | {0x00040080, ReadHWRegs, "ReadHWRegs"}, | 440 | {0x00040080, ReadHWRegs, "ReadHWRegs"}, |
| 356 | {0x00050200, SetBufferSwap, "SetBufferSwap"}, | 441 | {0x00050200, SetBufferSwap, "SetBufferSwap"}, |