diff options
Diffstat (limited to 'src/core/hle')
| -rw-r--r-- | src/core/hle/service/y2r_u.cpp | 376 | ||||
| -rw-r--r-- | src/core/hle/service/y2r_u.h | 96 |
2 files changed, 309 insertions, 163 deletions
diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp index 73a0899dd..17cb4f0f0 100644 --- a/src/core/hle/service/y2r_u.cpp +++ b/src/core/hle/service/y2r_u.cpp | |||
| @@ -9,8 +9,8 @@ | |||
| 9 | #include "core/hle/hle.h" | 9 | #include "core/hle/hle.h" |
| 10 | #include "core/hle/kernel/event.h" | 10 | #include "core/hle/kernel/event.h" |
| 11 | #include "core/hle/service/y2r_u.h" | 11 | #include "core/hle/service/y2r_u.h" |
| 12 | #include "core/hw/y2r.h" | ||
| 12 | #include "core/mem_map.h" | 13 | #include "core/mem_map.h" |
| 13 | #include "core/memory.h" | ||
| 14 | 14 | ||
| 15 | #include "video_core/utils.h" | 15 | #include "video_core/utils.h" |
| 16 | #include "video_core/video_core.h" | 16 | #include "video_core/video_core.h" |
| @@ -20,47 +20,6 @@ | |||
| 20 | 20 | ||
| 21 | namespace Y2R_U { | 21 | namespace Y2R_U { |
| 22 | 22 | ||
| 23 | enum class InputFormat : u8 { | ||
| 24 | /// 8-bit input, with YUV components in separate planes and using 4:2:2 subsampling. | ||
| 25 | YUV422_Indiv8 = 0, | ||
| 26 | /// 8-bit input, with YUV components in separate planes and using 4:2:0 subsampling. | ||
| 27 | YUV420_Indiv8 = 1, | ||
| 28 | |||
| 29 | YUV422_INDIV_16 = 2, | ||
| 30 | YUV420_INDIV_16 = 3, | ||
| 31 | YUV422_BATCH = 4, | ||
| 32 | }; | ||
| 33 | |||
| 34 | enum class OutputFormat : u8 { | ||
| 35 | Rgb32 = 0, | ||
| 36 | Rgb24 = 1, | ||
| 37 | Rgb16_555 = 2, | ||
| 38 | Rgb16_565 = 3, | ||
| 39 | }; | ||
| 40 | |||
| 41 | enum class Rotation : u8 { | ||
| 42 | None = 0, | ||
| 43 | Clockwise_90 = 1, | ||
| 44 | Clockwise_180 = 2, | ||
| 45 | Clockwise_270 = 3, | ||
| 46 | }; | ||
| 47 | |||
| 48 | enum class BlockAlignment : u8 { | ||
| 49 | /// Image is output in linear format suitable for use as a framebuffer. | ||
| 50 | Linear = 0, | ||
| 51 | /// Image is output in tiled PICA format, suitable for use as a texture. | ||
| 52 | Block8x8 = 1, | ||
| 53 | }; | ||
| 54 | |||
| 55 | enum class StandardCoefficient : u8 { | ||
| 56 | ITU_Rec601 = 0, | ||
| 57 | ITU_Rec709 = 1, | ||
| 58 | ITU_Rec601_Scaling = 2, | ||
| 59 | ITU_Rec709_Scaling = 3, | ||
| 60 | }; | ||
| 61 | |||
| 62 | static Kernel::SharedPtr<Kernel::Event> completion_event; | ||
| 63 | |||
| 64 | struct ConversionParameters { | 23 | struct ConversionParameters { |
| 65 | InputFormat input_format; | 24 | InputFormat input_format; |
| 66 | OutputFormat output_format; | 25 | OutputFormat output_format; |
| @@ -74,28 +33,60 @@ struct ConversionParameters { | |||
| 74 | }; | 33 | }; |
| 75 | static_assert(sizeof(ConversionParameters) == 12, "ConversionParameters struct has incorrect size"); | 34 | static_assert(sizeof(ConversionParameters) == 12, "ConversionParameters struct has incorrect size"); |
| 76 | 35 | ||
| 77 | struct ConversionBuffer { | 36 | static Kernel::SharedPtr<Kernel::Event> completion_event; |
| 78 | VAddr address; | 37 | static ConversionConfiguration conversion; |
| 79 | u32 image_size; | ||
| 80 | u16 transfer_unit; | ||
| 81 | u16 stride; | ||
| 82 | }; | ||
| 83 | 38 | ||
| 84 | struct ConversionData { | 39 | static const CoefficientSet standard_coefficients[4] = { |
| 85 | ConversionParameters params; | 40 | {{ 0x100, 0x166, 0xB6, 0x58, 0x1C5, -0x166F, 0x10EE, -0x1C5B }}, // ITU_Rec601 |
| 86 | /// Input parameters for the Y (luma) plane | 41 | {{ 0x100, 0x193, 0x77, 0x2F, 0x1DB, -0x1933, 0xA7C, -0x1D51 }}, // ITU_Rec709 |
| 87 | ConversionBuffer src_Y; | 42 | {{ 0x12A, 0x198, 0xD0, 0x64, 0x204, -0x1BDE, 0x10F2, -0x229B }}, // ITU_Rec601_Scaling |
| 88 | /// Output parameters for the conversion results | 43 | {{ 0x12A, 0x1CA, 0x88, 0x36, 0x21C, -0x1F04, 0x99C, -0x2421 }}, // ITU_Rec709_Scaling |
| 89 | ConversionBuffer dst; | ||
| 90 | }; | 44 | }; |
| 91 | 45 | ||
| 92 | static ConversionData conversion; | 46 | ResultCode ConversionConfiguration::SetInputLineWidth(u16 width) { |
| 47 | if (width == 0 || width > 1024 || width % 8 != 0) { | ||
| 48 | return ResultCode(ErrorDescription::OutOfRange, ErrorModule::CAM, | ||
| 49 | ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E053FD | ||
| 50 | } | ||
| 51 | |||
| 52 | // Note: The hardware uses the register value 0 to represent a width of 1024, so for a width of | ||
| 53 | // 1024 the `camera` module would set the value 0 here, but we don't need to emulate this | ||
| 54 | // internal detail. | ||
| 55 | this->input_line_width = width; | ||
| 56 | return RESULT_SUCCESS; | ||
| 57 | } | ||
| 58 | |||
| 59 | ResultCode ConversionConfiguration::SetInputLines(u16 lines) { | ||
| 60 | if (lines == 0 || lines > 1024) { | ||
| 61 | return ResultCode(ErrorDescription::OutOfRange, ErrorModule::CAM, | ||
| 62 | ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E053FD | ||
| 63 | } | ||
| 64 | |||
| 65 | // Note: In what appears to be a bug, the `camera` module does not set the hardware register at | ||
| 66 | // all if `lines` is 1024, so the conversion uses the last value that was set. The intention | ||
| 67 | // was probably to set it to 0 like in SetInputLineWidth. | ||
| 68 | if (lines != 1024) { | ||
| 69 | this->input_lines = lines; | ||
| 70 | } | ||
| 71 | return RESULT_SUCCESS; | ||
| 72 | } | ||
| 73 | |||
| 74 | ResultCode ConversionConfiguration::SetStandardCoefficient(StandardCoefficient standard_coefficient) { | ||
| 75 | size_t index = static_cast<size_t>(standard_coefficient); | ||
| 76 | if (index >= 4) { | ||
| 77 | return ResultCode(ErrorDescription::InvalidEnumValue, ErrorModule::CAM, | ||
| 78 | ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E053ED | ||
| 79 | } | ||
| 80 | |||
| 81 | std::memcpy(coefficients.data(), standard_coefficients[index].data(), sizeof(coefficients)); | ||
| 82 | return RESULT_SUCCESS; | ||
| 83 | } | ||
| 93 | 84 | ||
| 94 | static void SetInputFormat(Service::Interface* self) { | 85 | static void SetInputFormat(Service::Interface* self) { |
| 95 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 86 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 96 | 87 | ||
| 97 | conversion.params.input_format = static_cast<InputFormat>(cmd_buff[1]); | 88 | conversion.input_format = static_cast<InputFormat>(cmd_buff[1]); |
| 98 | LOG_DEBUG(Service_Y2R, "called input_format=%u", conversion.params.input_format); | 89 | LOG_DEBUG(Service_Y2R, "called input_format=%hhu", conversion.input_format); |
| 99 | 90 | ||
| 100 | cmd_buff[1] = RESULT_SUCCESS.raw; | 91 | cmd_buff[1] = RESULT_SUCCESS.raw; |
| 101 | } | 92 | } |
| @@ -103,8 +94,8 @@ static void SetInputFormat(Service::Interface* self) { | |||
| 103 | static void SetOutputFormat(Service::Interface* self) { | 94 | static void SetOutputFormat(Service::Interface* self) { |
| 104 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 95 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 105 | 96 | ||
| 106 | conversion.params.output_format = static_cast<OutputFormat>(cmd_buff[1]); | 97 | conversion.output_format = static_cast<OutputFormat>(cmd_buff[1]); |
| 107 | LOG_DEBUG(Service_Y2R, "called output_format=%u", conversion.params.output_format); | 98 | LOG_DEBUG(Service_Y2R, "called output_format=%hhu", conversion.output_format); |
| 108 | 99 | ||
| 109 | cmd_buff[1] = RESULT_SUCCESS.raw; | 100 | cmd_buff[1] = RESULT_SUCCESS.raw; |
| 110 | } | 101 | } |
| @@ -112,8 +103,8 @@ static void SetOutputFormat(Service::Interface* self) { | |||
| 112 | static void SetRotation(Service::Interface* self) { | 103 | static void SetRotation(Service::Interface* self) { |
| 113 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 104 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 114 | 105 | ||
| 115 | conversion.params.rotation = static_cast<Rotation>(cmd_buff[1]); | 106 | conversion.rotation = static_cast<Rotation>(cmd_buff[1]); |
| 116 | LOG_DEBUG(Service_Y2R, "called rotation=%u", conversion.params.rotation); | 107 | LOG_DEBUG(Service_Y2R, "called rotation=%hhu", conversion.rotation); |
| 117 | 108 | ||
| 118 | cmd_buff[1] = RESULT_SUCCESS.raw; | 109 | cmd_buff[1] = RESULT_SUCCESS.raw; |
| 119 | } | 110 | } |
| @@ -121,10 +112,18 @@ static void SetRotation(Service::Interface* self) { | |||
| 121 | static void SetBlockAlignment(Service::Interface* self) { | 112 | static void SetBlockAlignment(Service::Interface* self) { |
| 122 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 113 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 123 | 114 | ||
| 124 | conversion.params.block_alignment = static_cast<BlockAlignment>(cmd_buff[1]); | 115 | conversion.block_alignment = static_cast<BlockAlignment>(cmd_buff[1]); |
| 125 | LOG_DEBUG(Service_Y2R, "called alignment=%u", conversion.params.block_alignment); | 116 | LOG_DEBUG(Service_Y2R, "called alignment=%hhu", conversion.block_alignment); |
| 117 | |||
| 118 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 119 | } | ||
| 120 | |||
| 121 | static void SetTransferEndInterrupt(Service::Interface* self) { | ||
| 122 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 126 | 123 | ||
| 124 | cmd_buff[0] = 0x000D0040; | ||
| 127 | cmd_buff[1] = RESULT_SUCCESS.raw; | 125 | cmd_buff[1] = RESULT_SUCCESS.raw; |
| 126 | LOG_DEBUG(Service_Y2R, "(STUBBED) called"); | ||
| 128 | } | 127 | } |
| 129 | 128 | ||
| 130 | /** | 129 | /** |
| @@ -147,11 +146,56 @@ static void SetSendingY(Service::Interface* self) { | |||
| 147 | conversion.src_Y.address = cmd_buff[1]; | 146 | conversion.src_Y.address = cmd_buff[1]; |
| 148 | conversion.src_Y.image_size = cmd_buff[2]; | 147 | conversion.src_Y.image_size = cmd_buff[2]; |
| 149 | conversion.src_Y.transfer_unit = cmd_buff[3]; | 148 | conversion.src_Y.transfer_unit = cmd_buff[3]; |
| 150 | conversion.src_Y.stride = cmd_buff[4]; | 149 | conversion.src_Y.gap = cmd_buff[4]; |
| 151 | u32 src_process_handle = cmd_buff[6]; | 150 | u32 src_process_handle = cmd_buff[6]; |
| 152 | LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, " | 151 | LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, " |
| 153 | "src_process_handle=0x%08X", conversion.src_Y.image_size, | 152 | "src_process_handle=0x%08X", conversion.src_Y.image_size, |
| 154 | conversion.src_Y.transfer_unit, conversion.src_Y.stride, src_process_handle); | 153 | conversion.src_Y.transfer_unit, conversion.src_Y.gap, src_process_handle); |
| 154 | |||
| 155 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 156 | } | ||
| 157 | |||
| 158 | static void SetSendingU(Service::Interface* self) { | ||
| 159 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 160 | |||
| 161 | conversion.src_U.address = cmd_buff[1]; | ||
| 162 | conversion.src_U.image_size = cmd_buff[2]; | ||
| 163 | conversion.src_U.transfer_unit = cmd_buff[3]; | ||
| 164 | conversion.src_U.gap = cmd_buff[4]; | ||
| 165 | u32 src_process_handle = cmd_buff[6]; | ||
| 166 | LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, " | ||
| 167 | "src_process_handle=0x%08X", conversion.src_U.image_size, | ||
| 168 | conversion.src_U.transfer_unit, conversion.src_U.gap, src_process_handle); | ||
| 169 | |||
| 170 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 171 | } | ||
| 172 | |||
| 173 | static void SetSendingV(Service::Interface* self) { | ||
| 174 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 175 | |||
| 176 | conversion.src_V.address = cmd_buff[1]; | ||
| 177 | conversion.src_V.image_size = cmd_buff[2]; | ||
| 178 | conversion.src_V.transfer_unit = cmd_buff[3]; | ||
| 179 | conversion.src_V.gap = cmd_buff[4]; | ||
| 180 | u32 src_process_handle = cmd_buff[6]; | ||
| 181 | LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, " | ||
| 182 | "src_process_handle=0x%08X", conversion.src_V.image_size, | ||
| 183 | conversion.src_V.transfer_unit, conversion.src_V.gap, src_process_handle); | ||
| 184 | |||
| 185 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 186 | } | ||
| 187 | |||
| 188 | static void SetSendingYUYV(Service::Interface* self) { | ||
| 189 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 190 | |||
| 191 | conversion.src_YUYV.address = cmd_buff[1]; | ||
| 192 | conversion.src_YUYV.image_size = cmd_buff[2]; | ||
| 193 | conversion.src_YUYV.transfer_unit = cmd_buff[3]; | ||
| 194 | conversion.src_YUYV.gap = cmd_buff[4]; | ||
| 195 | u32 src_process_handle = cmd_buff[6]; | ||
| 196 | LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, " | ||
| 197 | "src_process_handle=0x%08X", conversion.src_YUYV.image_size, | ||
| 198 | conversion.src_YUYV.transfer_unit, conversion.src_YUYV.gap, src_process_handle); | ||
| 155 | 199 | ||
| 156 | cmd_buff[1] = RESULT_SUCCESS.raw; | 200 | cmd_buff[1] = RESULT_SUCCESS.raw; |
| 157 | } | 201 | } |
| @@ -162,11 +206,11 @@ static void SetReceiving(Service::Interface* self) { | |||
| 162 | conversion.dst.address = cmd_buff[1]; | 206 | conversion.dst.address = cmd_buff[1]; |
| 163 | conversion.dst.image_size = cmd_buff[2]; | 207 | conversion.dst.image_size = cmd_buff[2]; |
| 164 | conversion.dst.transfer_unit = cmd_buff[3]; | 208 | conversion.dst.transfer_unit = cmd_buff[3]; |
| 165 | conversion.dst.stride = cmd_buff[4]; | 209 | conversion.dst.gap = cmd_buff[4]; |
| 166 | u32 dst_process_handle = cmd_buff[6]; | 210 | u32 dst_process_handle = cmd_buff[6]; |
| 167 | LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, " | 211 | LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, " |
| 168 | "dst_process_handle=0x%08X", conversion.dst.image_size, | 212 | "dst_process_handle=0x%08X", conversion.dst.image_size, |
| 169 | conversion.dst.transfer_unit, conversion.dst.stride, | 213 | conversion.dst.transfer_unit, conversion.dst.gap, |
| 170 | dst_process_handle); | 214 | dst_process_handle); |
| 171 | 215 | ||
| 172 | cmd_buff[1] = RESULT_SUCCESS.raw; | 216 | cmd_buff[1] = RESULT_SUCCESS.raw; |
| @@ -175,107 +219,54 @@ static void SetReceiving(Service::Interface* self) { | |||
| 175 | static void SetInputLineWidth(Service::Interface* self) { | 219 | static void SetInputLineWidth(Service::Interface* self) { |
| 176 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 220 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 177 | 221 | ||
| 178 | conversion.params.input_line_width = cmd_buff[1]; | 222 | LOG_DEBUG(Service_Y2R, "called input_line_width=%u", cmd_buff[1]); |
| 179 | LOG_DEBUG(Service_Y2R, "input_line_width=%u", conversion.params.input_line_width); | 223 | cmd_buff[1] = conversion.SetInputLineWidth(cmd_buff[1]).raw; |
| 180 | |||
| 181 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 182 | } | 224 | } |
| 183 | 225 | ||
| 184 | static void SetInputLines(Service::Interface* self) { | 226 | static void SetInputLines(Service::Interface* self) { |
| 185 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 227 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 186 | 228 | ||
| 187 | conversion.params.input_lines = cmd_buff[1]; | 229 | LOG_DEBUG(Service_Y2R, "called input_line_number=%u", cmd_buff[1]); |
| 188 | LOG_DEBUG(Service_Y2R, "input_line_number=%u", conversion.params.input_lines); | 230 | cmd_buff[1] = conversion.SetInputLines(cmd_buff[1]).raw; |
| 189 | |||
| 190 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 191 | } | 231 | } |
| 192 | 232 | ||
| 193 | static void StartConversion(Service::Interface* self) { | 233 | static void SetCoefficient(Service::Interface* self) { |
| 194 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 234 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 195 | 235 | ||
| 196 | const ConversionParameters& params = conversion.params; | 236 | const u16* coefficients = reinterpret_cast<const u16*>(&cmd_buff[1]); |
| 197 | 237 | std::memcpy(conversion.coefficients.data(), coefficients, sizeof(CoefficientSet)); | |
| 198 | const u8* srcY_buffer = Memory::GetPointer(conversion.src_Y.address); | 238 | LOG_DEBUG(Service_Y2R, "called coefficients=[%hX, %hX, %hX, %hX, %hX, %hX, %hX, %hX]", |
| 199 | u8* dst_buffer = Memory::GetPointer(conversion.dst.address); | 239 | coefficients[0], coefficients[1], coefficients[2], coefficients[3], |
| 200 | 240 | coefficients[4], coefficients[5], coefficients[6], coefficients[7]); | |
| 201 | // TODO: support color and other kinds of conversions | ||
| 202 | ASSERT(params.input_format == InputFormat::YUV422_Indiv8 | ||
| 203 | || params.input_format == InputFormat::YUV420_Indiv8); | ||
| 204 | ASSERT(params.output_format == OutputFormat::Rgb24); | ||
| 205 | ASSERT(params.rotation == Rotation::None); | ||
| 206 | const int bpp = 3; | ||
| 207 | |||
| 208 | switch (params.block_alignment) { | ||
| 209 | case BlockAlignment::Linear: | ||
| 210 | { | ||
| 211 | const size_t input_lines = params.input_lines; | ||
| 212 | const size_t input_line_width = params.input_line_width; | ||
| 213 | const size_t srcY_stride = conversion.src_Y.stride; | ||
| 214 | const size_t dst_stride = conversion.dst.stride; | ||
| 215 | |||
| 216 | size_t srcY_offset = 0; | ||
| 217 | size_t dst_offset = 0; | ||
| 218 | |||
| 219 | for (size_t line = 0; line < input_lines; ++line) { | ||
| 220 | for (size_t i = 0; i < input_line_width; ++i) { | ||
| 221 | u8 Y = srcY_buffer[srcY_offset]; | ||
| 222 | dst_buffer[dst_offset + 0] = Y; | ||
| 223 | dst_buffer[dst_offset + 1] = Y; | ||
| 224 | dst_buffer[dst_offset + 2] = Y; | ||
| 225 | |||
| 226 | srcY_offset += 1; | ||
| 227 | dst_offset += bpp; | ||
| 228 | } | ||
| 229 | srcY_offset += srcY_stride; | ||
| 230 | dst_offset += dst_stride; | ||
| 231 | } | ||
| 232 | break; | ||
| 233 | } | ||
| 234 | case BlockAlignment::Block8x8: | ||
| 235 | { | ||
| 236 | const size_t input_lines = params.input_lines; | ||
| 237 | const size_t input_line_width = params.input_line_width; | ||
| 238 | const size_t srcY_stride = conversion.src_Y.stride; | ||
| 239 | const size_t dst_transfer_unit = conversion.dst.transfer_unit; | ||
| 240 | const size_t dst_stride = conversion.dst.stride; | ||
| 241 | |||
| 242 | size_t srcY_offset = 0; | ||
| 243 | size_t dst_tile_line_offs = 0; | ||
| 244 | 241 | ||
| 245 | const size_t tile_size = 8 * 8 * bpp; | 242 | cmd_buff[1] = RESULT_SUCCESS.raw; |
| 243 | } | ||
| 246 | 244 | ||
| 247 | for (size_t line = 0; line < input_lines;) { | 245 | static void SetStandardCoefficient(Service::Interface* self) { |
| 248 | size_t max_line = line + 8; | 246 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 249 | 247 | ||
| 250 | for (; line < max_line; ++line) { | 248 | LOG_DEBUG(Service_Y2R, "called standard_coefficient=%u", cmd_buff[1]); |
| 251 | for (size_t x = 0; x < input_line_width; ++x) { | ||
| 252 | size_t tile_x = x / 8; | ||
| 253 | 249 | ||
| 254 | size_t dst_tile_offs = dst_tile_line_offs + tile_x * tile_size; | 250 | cmd_buff[1] = conversion.SetStandardCoefficient((StandardCoefficient)cmd_buff[1]).raw; |
| 255 | size_t tile_i = VideoCore::MortonInterleave((u32)x, (u32)line); | 251 | } |
| 256 | 252 | ||
| 257 | size_t dst_offset = dst_tile_offs + tile_i * bpp; | 253 | static void SetAlpha(Service::Interface* self) { |
| 254 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 258 | 255 | ||
| 259 | u8 Y = srcY_buffer[srcY_offset]; | 256 | conversion.alpha = cmd_buff[1]; |
| 260 | dst_buffer[dst_offset + 0] = Y; | 257 | LOG_DEBUG(Service_Y2R, "called alpha=%hu", conversion.alpha); |
| 261 | dst_buffer[dst_offset + 1] = Y; | ||
| 262 | dst_buffer[dst_offset + 2] = Y; | ||
| 263 | 258 | ||
| 264 | srcY_offset += 1; | 259 | cmd_buff[1] = RESULT_SUCCESS.raw; |
| 265 | } | 260 | } |
| 266 | 261 | ||
| 267 | srcY_offset += srcY_stride; | 262 | static void StartConversion(Service::Interface* self) { |
| 268 | } | 263 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 269 | 264 | ||
| 270 | dst_tile_line_offs += dst_transfer_unit + dst_stride; | 265 | HW::Y2R::PerformConversion(conversion); |
| 271 | } | ||
| 272 | break; | ||
| 273 | } | ||
| 274 | } | ||
| 275 | 266 | ||
| 276 | // dst_image_size would seem to be perfect for this, but it doesn't include the stride :( | 267 | // dst_image_size would seem to be perfect for this, but it doesn't include the gap :( |
| 277 | u32 total_output_size = params.input_lines * | 268 | u32 total_output_size = conversion.input_lines * |
| 278 | (conversion.dst.transfer_unit + conversion.dst.stride); | 269 | (conversion.dst.transfer_unit + conversion.dst.gap); |
| 279 | VideoCore::g_renderer->hw_rasterizer->NotifyFlush( | 270 | VideoCore::g_renderer->hw_rasterizer->NotifyFlush( |
| 280 | Memory::VirtualToPhysicalAddress(conversion.dst.address), total_output_size); | 271 | Memory::VirtualToPhysicalAddress(conversion.dst.address), total_output_size); |
| 281 | 272 | ||
| @@ -285,6 +276,14 @@ static void StartConversion(Service::Interface* self) { | |||
| 285 | cmd_buff[1] = RESULT_SUCCESS.raw; | 276 | cmd_buff[1] = RESULT_SUCCESS.raw; |
| 286 | } | 277 | } |
| 287 | 278 | ||
| 279 | static void StopConversion(Service::Interface* self) { | ||
| 280 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 281 | |||
| 282 | cmd_buff[0] = 0x00270040; | ||
| 283 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 284 | LOG_DEBUG(Service_Y2R, "called"); | ||
| 285 | } | ||
| 286 | |||
| 288 | /** | 287 | /** |
| 289 | * Y2R_U::IsBusyConversion service function | 288 | * Y2R_U::IsBusyConversion service function |
| 290 | * Outputs: | 289 | * Outputs: |
| @@ -306,15 +305,31 @@ static void SetConversionParams(Service::Interface* self) { | |||
| 306 | u32* cmd_buff = Kernel::GetCommandBuffer(); | 305 | u32* cmd_buff = Kernel::GetCommandBuffer(); |
| 307 | 306 | ||
| 308 | auto params = reinterpret_cast<const ConversionParameters*>(&cmd_buff[1]); | 307 | auto params = reinterpret_cast<const ConversionParameters*>(&cmd_buff[1]); |
| 309 | conversion.params = *params; | ||
| 310 | |||
| 311 | cmd_buff[0] = 0x00290000; // TODO verify | ||
| 312 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 313 | LOG_DEBUG(Service_Y2R, | 308 | LOG_DEBUG(Service_Y2R, |
| 314 | "called input_format=%hhu output_format=%hhu rotation=%hhu block_alignment=%hhu " | 309 | "called input_format=%hhu output_format=%hhu rotation=%hhu block_alignment=%hhu " |
| 315 | "input_line_width=%hX input_lines=%hu standard_coefficient=%hhu reserved=%hhu alpha=%hX", | 310 | "input_line_width=%hu input_lines=%hu standard_coefficient=%hhu " |
| 311 | "reserved=%hhu alpha=%hX", | ||
| 316 | params->input_format, params->output_format, params->rotation, params->block_alignment, | 312 | params->input_format, params->output_format, params->rotation, params->block_alignment, |
| 317 | params->input_line_width, params->input_lines, params->standard_coefficient); | 313 | params->input_line_width, params->input_lines, params->standard_coefficient, |
| 314 | params->reserved, params->alpha); | ||
| 315 | |||
| 316 | ResultCode result = RESULT_SUCCESS; | ||
| 317 | |||
| 318 | conversion.input_format = params->input_format; | ||
| 319 | conversion.output_format = params->output_format; | ||
| 320 | conversion.rotation = params->rotation; | ||
| 321 | conversion.block_alignment = params->block_alignment; | ||
| 322 | result = conversion.SetInputLineWidth(params->input_line_width); | ||
| 323 | if (result.IsError()) goto cleanup; | ||
| 324 | result = conversion.SetInputLines(params->input_lines); | ||
| 325 | if (result.IsError()) goto cleanup; | ||
| 326 | result = conversion.SetStandardCoefficient(params->standard_coefficient); | ||
| 327 | if (result.IsError()) goto cleanup; | ||
| 328 | conversion.alpha = params->alpha; | ||
| 329 | |||
| 330 | cleanup: | ||
| 331 | cmd_buff[0] = 0x00290040; // TODO verify | ||
| 332 | cmd_buff[1] = result.raw; | ||
| 318 | } | 333 | } |
| 319 | 334 | ||
| 320 | static void PingProcess(Service::Interface* self) { | 335 | static void PingProcess(Service::Interface* self) { |
| @@ -325,28 +340,63 @@ static void PingProcess(Service::Interface* self) { | |||
| 325 | LOG_WARNING(Service_Y2R, "(STUBBED) called"); | 340 | LOG_WARNING(Service_Y2R, "(STUBBED) called"); |
| 326 | } | 341 | } |
| 327 | 342 | ||
| 343 | static void DriverInitialize(Service::Interface* self) { | ||
| 344 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 345 | |||
| 346 | conversion.input_format = InputFormat::YUV422_Indiv8; | ||
| 347 | conversion.output_format = OutputFormat::RGBA8; | ||
| 348 | conversion.rotation = Rotation::None; | ||
| 349 | conversion.block_alignment = BlockAlignment::Linear; | ||
| 350 | conversion.coefficients.fill(0); | ||
| 351 | conversion.SetInputLineWidth(1024); | ||
| 352 | conversion.SetInputLines(1024); | ||
| 353 | conversion.alpha = 0; | ||
| 354 | |||
| 355 | ConversionBuffer zero_buffer = {}; | ||
| 356 | conversion.src_Y = zero_buffer; | ||
| 357 | conversion.src_U = zero_buffer; | ||
| 358 | conversion.src_V = zero_buffer; | ||
| 359 | conversion.dst = zero_buffer; | ||
| 360 | |||
| 361 | completion_event->Clear(); | ||
| 362 | |||
| 363 | cmd_buff[0] = 0x002B0040; | ||
| 364 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 365 | LOG_DEBUG(Service_Y2R, "called"); | ||
| 366 | } | ||
| 367 | |||
| 368 | static void DriverFinalize(Service::Interface* self) { | ||
| 369 | u32* cmd_buff = Kernel::GetCommandBuffer(); | ||
| 370 | |||
| 371 | cmd_buff[0] = 0x002C0040; | ||
| 372 | cmd_buff[1] = RESULT_SUCCESS.raw; | ||
| 373 | LOG_DEBUG(Service_Y2R, "called"); | ||
| 374 | } | ||
| 375 | |||
| 328 | const Interface::FunctionInfo FunctionTable[] = { | 376 | const Interface::FunctionInfo FunctionTable[] = { |
| 329 | {0x00010040, SetInputFormat, "SetInputFormat"}, | 377 | {0x00010040, SetInputFormat, "SetInputFormat"}, |
| 330 | {0x00030040, SetOutputFormat, "SetOutputFormat"}, | 378 | {0x00030040, SetOutputFormat, "SetOutputFormat"}, |
| 331 | {0x00050040, SetRotation, "SetRotation"}, | 379 | {0x00050040, SetRotation, "SetRotation"}, |
| 332 | {0x00070040, SetBlockAlignment, "SetBlockAlignment"}, | 380 | {0x00070040, SetBlockAlignment, "SetBlockAlignment"}, |
| 333 | {0x000D0040, nullptr, "SetTransferEndInterrupt"}, | 381 | {0x000D0040, SetTransferEndInterrupt, "SetTransferEndInterrupt"}, |
| 334 | {0x000F0000, GetTransferEndEvent, "GetTransferEndEvent"}, | 382 | {0x000F0000, GetTransferEndEvent, "GetTransferEndEvent"}, |
| 335 | {0x00100102, SetSendingY, "SetSendingY"}, | 383 | {0x00100102, SetSendingY, "SetSendingY"}, |
| 336 | {0x00110102, nullptr, "SetSendingU"}, | 384 | {0x00110102, SetSendingU, "SetSendingU"}, |
| 337 | {0x00120102, nullptr, "SetSendingV"}, | 385 | {0x00120102, SetSendingV, "SetSendingV"}, |
| 386 | {0x00130102, SetSendingYUYV, "SetSendingYUYV"}, | ||
| 338 | {0x00180102, SetReceiving, "SetReceiving"}, | 387 | {0x00180102, SetReceiving, "SetReceiving"}, |
| 339 | {0x001A0040, SetInputLineWidth, "SetInputLineWidth"}, | 388 | {0x001A0040, SetInputLineWidth, "SetInputLineWidth"}, |
| 340 | {0x001C0040, SetInputLines, "SetInputLines"}, | 389 | {0x001C0040, SetInputLines, "SetInputLines"}, |
| 341 | {0x00200040, nullptr, "SetStandardCoefficient"}, | 390 | {0x001E0100, SetCoefficient, "SetCoefficient"}, |
| 342 | {0x00220040, nullptr, "SetAlpha"}, | 391 | {0x00200040, SetStandardCoefficient, "SetStandardCoefficient"}, |
| 392 | {0x00220040, SetAlpha, "SetAlpha"}, | ||
| 343 | {0x00260000, StartConversion, "StartConversion"}, | 393 | {0x00260000, StartConversion, "StartConversion"}, |
| 344 | {0x00270000, nullptr, "StopConversion"}, | 394 | {0x00270000, StopConversion, "StopConversion"}, |
| 345 | {0x00280000, IsBusyConversion, "IsBusyConversion"}, | 395 | {0x00280000, IsBusyConversion, "IsBusyConversion"}, |
| 346 | {0x002901C0, SetConversionParams, "SetConversionParams"}, | 396 | {0x002901C0, SetConversionParams, "SetConversionParams"}, |
| 347 | {0x002A0000, PingProcess, "PingProcess"}, | 397 | {0x002A0000, PingProcess, "PingProcess"}, |
| 348 | {0x002B0000, nullptr, "DriverInitialize"}, | 398 | {0x002B0000, DriverInitialize, "DriverInitialize"}, |
| 349 | {0x002C0000, nullptr, "DriverFinalize"}, | 399 | {0x002C0000, DriverFinalize, "DriverFinalize"}, |
| 350 | }; | 400 | }; |
| 351 | 401 | ||
| 352 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 402 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
diff --git a/src/core/hle/service/y2r_u.h b/src/core/hle/service/y2r_u.h index 171aecfd1..7df47fcb9 100644 --- a/src/core/hle/service/y2r_u.h +++ b/src/core/hle/service/y2r_u.h | |||
| @@ -4,6 +4,10 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | ||
| 8 | |||
| 9 | #include "common/common_types.h" | ||
| 10 | |||
| 7 | #include "core/hle/service/service.h" | 11 | #include "core/hle/service/service.h" |
| 8 | 12 | ||
| 9 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 13 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| @@ -11,6 +15,98 @@ | |||
| 11 | 15 | ||
| 12 | namespace Y2R_U { | 16 | namespace Y2R_U { |
| 13 | 17 | ||
| 18 | enum class InputFormat : u8 { | ||
| 19 | /// 8-bit input, with YUV components in separate planes and 4:2:2 subsampling. | ||
| 20 | YUV422_Indiv8 = 0, | ||
| 21 | /// 8-bit input, with YUV components in separate planes and 4:2:0 subsampling. | ||
| 22 | YUV420_Indiv8 = 1, | ||
| 23 | |||
| 24 | /// 16-bit input (only LSB used), with YUV components in separate planes and 4:2:2 subsampling. | ||
| 25 | YUV422_Indiv16 = 2, | ||
| 26 | /// 16-bit input (only LSB used), with YUV components in separate planes and 4:2:0 subsampling. | ||
| 27 | YUV420_Indiv16 = 3, | ||
| 28 | |||
| 29 | /// 8-bit input, with a single interleaved stream in YUYV format and 4:2:2 subsampling. | ||
| 30 | YUYV422_Interleaved = 4, | ||
| 31 | }; | ||
| 32 | |||
| 33 | enum class OutputFormat : u8 { | ||
| 34 | RGBA8 = 0, | ||
| 35 | RGB8 = 1, | ||
| 36 | RGB5A1 = 2, | ||
| 37 | RGB565 = 3, | ||
| 38 | }; | ||
| 39 | |||
| 40 | enum class Rotation : u8 { | ||
| 41 | None = 0, | ||
| 42 | Clockwise_90 = 1, | ||
| 43 | Clockwise_180 = 2, | ||
| 44 | Clockwise_270 = 3, | ||
| 45 | }; | ||
| 46 | |||
| 47 | enum class BlockAlignment : u8 { | ||
| 48 | /// Image is output in linear format suitable for use as a framebuffer. | ||
| 49 | Linear = 0, | ||
| 50 | /// Image is output in tiled PICA format, suitable for use as a texture. | ||
| 51 | Block8x8 = 1, | ||
| 52 | }; | ||
| 53 | |||
| 54 | enum class StandardCoefficient : u8 { | ||
| 55 | /// ITU Rec. BT.601 primaries, with PC ranges. | ||
| 56 | ITU_Rec601 = 0, | ||
| 57 | /// ITU Rec. BT.709 primaries, with PC ranges. | ||
| 58 | ITU_Rec709 = 1, | ||
| 59 | /// ITU Rec. BT.601 primaries, with TV ranges. | ||
| 60 | ITU_Rec601_Scaling = 2, | ||
| 61 | /// ITU Rec. BT.709 primaries, with TV ranges. | ||
| 62 | ITU_Rec709_Scaling = 3, | ||
| 63 | }; | ||
| 64 | |||
| 65 | /** | ||
| 66 | * A set of coefficients configuring the RGB to YUV conversion. Coefficients 0-4 are unsigned 2.8 | ||
| 67 | * fixed pointer numbers representing entries on the conversion matrix, while coefficient 5-7 are | ||
| 68 | * signed 11.5 fixed point numbers added as offsets to the RGB result. | ||
| 69 | * | ||
| 70 | * The overall conversion process formula is: | ||
| 71 | * ``` | ||
| 72 | * R = trunc((c_0 * Y + c_1 * V) + c_5 + 0.75) | ||
| 73 | * G = trunc((c_0 * Y - c_3 * U - c_2 * V) + c_6 + 0.75) | ||
| 74 | * B = trunc((c_0 * Y + c_4 * U ) + c_7 + 0.75) | ||
| 75 | * ``` | ||
| 76 | */ | ||
| 77 | using CoefficientSet = std::array<s16, 8>; | ||
| 78 | |||
| 79 | struct ConversionBuffer { | ||
| 80 | /// Current reading/writing address of this buffer. | ||
| 81 | VAddr address; | ||
| 82 | /// Remaining amount of bytes to be DMAed, does not include the inter-trasfer gap. | ||
| 83 | u32 image_size; | ||
| 84 | /// Size of a single DMA transfer. | ||
| 85 | u16 transfer_unit; | ||
| 86 | /// Amount of bytes to be skipped between copying each `transfer_unit` bytes. | ||
| 87 | u16 gap; | ||
| 88 | }; | ||
| 89 | |||
| 90 | struct ConversionConfiguration { | ||
| 91 | InputFormat input_format; | ||
| 92 | OutputFormat output_format; | ||
| 93 | Rotation rotation; | ||
| 94 | BlockAlignment block_alignment; | ||
| 95 | u16 input_line_width; | ||
| 96 | u16 input_lines; | ||
| 97 | CoefficientSet coefficients; | ||
| 98 | u16 alpha; | ||
| 99 | |||
| 100 | /// Input parameters for the Y (luma) plane | ||
| 101 | ConversionBuffer src_Y, src_U, src_V, src_YUYV; | ||
| 102 | /// Output parameters for the conversion results | ||
| 103 | ConversionBuffer dst; | ||
| 104 | |||
| 105 | ResultCode SetInputLineWidth(u16 width); | ||
| 106 | ResultCode SetInputLines(u16 lines); | ||
| 107 | ResultCode SetStandardCoefficient(StandardCoefficient standard_coefficient); | ||
| 108 | }; | ||
| 109 | |||
| 14 | class Interface : public Service::Interface { | 110 | class Interface : public Service::Interface { |
| 15 | public: | 111 | public: |
| 16 | Interface(); | 112 | Interface(); |