From 8a7601098887e6c723a3f32b9283370d480e90e2 Mon Sep 17 00:00:00 2001 From: Yuri Kunde Schlesner Date: Sun, 21 Jun 2015 07:25:28 -0300 Subject: [PATCH 1/2] Add y2r:u commands --- libctru/include/3ds.h | 1 + libctru/include/3ds/services/y2r.h | 117 +++++++++ libctru/source/services/y2r.c | 395 +++++++++++++++++++++++++++++ 3 files changed, 513 insertions(+) create mode 100644 libctru/include/3ds/services/y2r.h create mode 100644 libctru/source/services/y2r.c diff --git a/libctru/include/3ds.h b/libctru/include/3ds.h index 91ef308..4b213a5 100644 --- a/libctru/include/3ds.h +++ b/libctru/include/3ds.h @@ -36,6 +36,7 @@ extern "C" { #include <3ds/services/mvd.h> #include <3ds/services/news.h> #include <3ds/services/qtm.h> +#include <3ds/services/y2r.h> #include <3ds/services/hb.h> #include <3ds/gpu/gx.h> diff --git a/libctru/include/3ds/services/y2r.h b/libctru/include/3ds/services/y2r.h new file mode 100644 index 0000000..bd25bf0 --- /dev/null +++ b/libctru/include/3ds/services/y2r.h @@ -0,0 +1,117 @@ +#pragma once +#include <3ds/types.h> + +typedef enum +{ + INPUT_YUV422_INDIV_8 = 0x0, + INPUT_YUV420_INDIV_8 = 0x1, + INPUT_YUV422_INDIV_16 = 0x2, + INPUT_YUV420_INDIV_16 = 0x3, + INPUT_YUV422_BATCH = 0x4, +} Y2R_InputFormat; + +typedef enum +{ + OUTPUT_RGB_32 = 0x0, + OUTPUT_RGB_24 = 0x1, + OUTPUT_RGB_16_555 = 0x2, + OUTPUT_RGB_16_565 = 0x3, +} Y2R_OutputFormat; + +typedef enum +{ + ROTATION_NONE = 0x0, + ROTATION_CLOCKWISE_90 = 0x1, + ROTATION_CLOCKWISE_180 = 0x2, + ROTATION_CLOCKWISE_270 = 0x3, +} Y2R_Rotation; + +typedef enum +{ + BLOCK_LINE = 0x0, + BLOCK_8_BY_8 = 0x1, +} Y2R_BlockAlignment; + +typedef enum +{ + COEFFICIENT_ITU_R_BT_601 = 0x0, + COEFFICIENT_ITU_R_BT_709 = 0x1, + COEFFICIENT_ITU_R_BT_601_SCALING = 0x2, + COEFFICIENT_ITU_R_BT_709_SCALING = 0x3, +} Y2R_StandardCoefficient; + +typedef struct +{ + Y2R_InputFormat input_format : 8; + Y2R_OutputFormat output_format : 8; + Y2R_Rotation rotation : 8; + Y2R_BlockAlignment block_alignment : 8; + u16 input_line_width; + u16 input_lines; + Y2R_StandardCoefficient standard_coefficient : 8; + u8 unused; + u16 alpha; +} Y2R_ConversionParams; + +/** + * A set of coefficients configuring the RGB to YUV conversion. Coefficients 0-4 are unsigned 2.8 + * fixed pointer numbers representing entries on the conversion matrix, while coefficient 5-7 are + * signed 11.5 fixed point numbers added as offsets to the RGB result. + * + * The overall conversion process formula is: + * ``` + * R = trunc((rgb_Y * Y + r_V * V) + 0.75 + r_offset) + * G = trunc((rgb_Y * Y - g_U * U - g_V * V) + 0.75 + g_offset) + * B = trunc((rgb_Y * Y + b_U * U ) + 0.75 + b_offset) + * ``` + */ +typedef struct +{ + u16 rgb_Y; + u16 r_V; + u16 g_V; + u16 g_U; + u16 b_U; + u16 r_offset; + u16 g_offset; + u16 b_offset; +} Y2R_ColorCoefficients; + +Result y2rInit(); +Result y2rExit(); + +Result Y2RU_SetInputFormat(Y2R_InputFormat format); +Result Y2RU_SetOutputFormat(Y2R_OutputFormat format); +Result Y2RU_SetRotation(Y2R_Rotation rotation); +Result Y2RU_SetBlockAlignment(Y2R_BlockAlignment alignment); + +Result Y2RU_SetTransferEndInterrupt(bool should_interrupt); +Result Y2RU_GetTransferEndEvent(Handle* end_event); +Result Y2RU_SetSendingY(const void* src_buf, u32 image_size, u16 transfer_unit, u16 transfer_gap); +Result Y2RU_SetSendingU(const void* src_buf, u32 image_size, u16 transfer_unit, u16 transfer_gap); +Result Y2RU_SetSendingV(const void* src_buf, u32 image_size, u16 transfer_unit, u16 transfer_gap); +Result Y2RU_SetSendingYUYV(const void* src_buf, u32 image_size, u16 transfer_unit, u16 transfer_gap); +Result Y2RU_IsDoneSendingYUYV(bool* is_done); +Result Y2RU_IsDoneSendingY(bool* is_done); +Result Y2RU_IsDoneSendingU(bool* is_done); +Result Y2RU_IsDoneSendingV(bool* is_done); +Result Y2RU_SetReceiving(void* dst_buf, u32 image_size, u16 transfer_unit, u16 transfer_gap); +Result Y2RU_IsDoneReceiving(bool* is_done); +Result Y2RU_SetInputLineWidth(u16 line_width); +Result Y2RU_SetInputLines(u16 num_lines); +Result Y2RU_SetCoefficient(const Y2R_ColorCoefficients* coefficient); +Result Y2RU_SetStandardCoefficient(Y2R_StandardCoefficient coefficient); + +Result Y2RU_SetAlpha(u16 alpha); +Result Y2RU_SetUnknownParams(const u16 params[16]); +Result Y2RU_StartConversion(void); +Result Y2RU_StopConversion(void); +Result Y2RU_IsBusyConversion(bool* is_busy); +Result Y2RU_SetConversionParams(const Y2R_ConversionParams* params); + +/* Seems to check whether y2r is ready to be used */ +Result Y2RU_PingProcess(u8* ping); + +Result Y2RU_DriverInitialize(void); +Result Y2RU_DriverFinalize(void); + diff --git a/libctru/source/services/y2r.c b/libctru/source/services/y2r.c new file mode 100644 index 0000000..60101a6 --- /dev/null +++ b/libctru/source/services/y2r.c @@ -0,0 +1,395 @@ +#include +#include +#include <3ds/services/y2r.h> +#include <3ds/srv.h> +#include <3ds/svc.h> +#include <3ds/types.h> + +Handle y2rHandle = 0; +static bool initialized = false; + +Result y2rInit(void) +{ + Result ret = 0; + + if (initialized) return 0; + + if (y2rHandle == 0) + { + ret = srvGetServiceHandle(&y2rHandle, "y2r:u"); + if (ret < 0) return ret; + } + + ret = Y2RU_DriverInitialize(); + if (ret < 0) return ret; + initialized = true; + + return 0; +} + +Result y2rExit(void) +{ + Result ret = 0; + + if (initialized) + { + ret = Y2RU_DriverFinalize(); + if (ret < 0) return ret; + } + + if (y2rHandle != 0) + { + ret = svcCloseHandle(y2rHandle); + if (ret < 0) return ret; + y2rHandle = 0; + } + + return 0; +} + +Result Y2RU_SetInputFormat(Y2R_InputFormat format) +{ + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = 0x00010040; + cmdbuf[1] = format; + + if ((ret = svcSendSyncRequest(y2rHandle)) != 0) return ret; + return cmdbuf[1]; +} + +Result Y2RU_SetOutputFormat(Y2R_OutputFormat format) +{ + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = 0x00030040; + cmdbuf[1] = format; + + if ((ret = svcSendSyncRequest(y2rHandle)) != 0) return ret; + return cmdbuf[1]; +} + +Result Y2RU_SetRotation(Y2R_Rotation rotation) +{ + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = 0x00050040; + cmdbuf[1] = rotation; + + if ((ret = svcSendSyncRequest(y2rHandle)) != 0) return ret; + return cmdbuf[1]; +} + +Result Y2RU_SetBlockAlignment(Y2R_BlockAlignment alignment) +{ + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = 0x00070040; + cmdbuf[1] = alignment; + + if ((ret = svcSendSyncRequest(y2rHandle)) != 0) return ret; + return cmdbuf[1]; +} + +Result Y2RU_SetTransferEndInterrupt(bool should_interrupt) +{ + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = 0x000D0040; + cmdbuf[1] = should_interrupt; + + if ((ret = svcSendSyncRequest(y2rHandle)) != 0) return ret; + return cmdbuf[1]; +} + +Result Y2RU_GetTransferEndEvent(Handle* end_event) +{ + if (*end_event != 0) + { + svcCloseHandle(*end_event); + *end_event = 0; + } + + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = 0x000F0000; + + if ((ret = svcSendSyncRequest(y2rHandle)) != 0) return ret; + + *end_event = cmdbuf[3]; + return cmdbuf[1]; +} + +Result Y2RU_SetSendingY(const void* src_buf, u32 image_size, u16 transfer_unit, u16 transfer_gap) +{ + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = 0x00100102; + cmdbuf[1] = (u32)src_buf; + cmdbuf[2] = image_size; + cmdbuf[3] = transfer_unit; + cmdbuf[4] = transfer_gap; + cmdbuf[5] = 0; + cmdbuf[6] = 0xFFFF8001; + + if ((ret = svcSendSyncRequest(y2rHandle)) != 0) return ret; + return cmdbuf[1]; +} + +Result Y2RU_SetSendingU(const void* src_buf, u32 image_size, u16 transfer_unit, u16 transfer_gap) +{ + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = 0x00110102; + cmdbuf[1] = (u32)src_buf; + cmdbuf[2] = image_size; + cmdbuf[3] = transfer_unit; + cmdbuf[4] = transfer_gap; + cmdbuf[5] = 0; + cmdbuf[6] = 0xFFFF8001; + + if ((ret = svcSendSyncRequest(y2rHandle)) != 0) return ret; + return cmdbuf[1]; +} + +Result Y2RU_SetSendingV(const void* src_buf, u32 image_size, u16 transfer_unit, u16 transfer_gap) +{ + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = 0x00120102; + cmdbuf[1] = (u32)src_buf; + cmdbuf[2] = image_size; + cmdbuf[3] = transfer_unit; + cmdbuf[4] = transfer_gap; + cmdbuf[5] = 0; + cmdbuf[6] = 0xFFFF8001; + + if ((ret = svcSendSyncRequest(y2rHandle)) != 0) return ret; + return cmdbuf[1]; +} + +Result Y2RU_SetSendingYUYV(const void* src_buf, u32 image_size, u16 transfer_unit, u16 transfer_gap) +{ + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = 0x00130102; + cmdbuf[1] = (u32)src_buf; + cmdbuf[2] = image_size; + cmdbuf[3] = transfer_unit; + cmdbuf[4] = transfer_gap; + cmdbuf[5] = 0; + cmdbuf[6] = 0xFFFF8001; + + if ((ret = svcSendSyncRequest(y2rHandle)) != 0) return ret; + return cmdbuf[1]; +} + +Result Y2RU_IsDoneSendingYUYV(bool* is_done) +{ + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = 0x00140000; + + if ((ret = svcSendSyncRequest(y2rHandle)) != 0) return ret; + *is_done = cmdbuf[2] & 0xFF; + return cmdbuf[1]; +} + +Result Y2RU_IsDoneSendingY(bool* is_done) +{ + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = 0x00150000; + + if ((ret = svcSendSyncRequest(y2rHandle)) != 0) return ret; + *is_done = cmdbuf[2] & 0xFF; + return cmdbuf[1]; +} + +Result Y2RU_IsDoneSendingU(bool* is_done) +{ + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = 0x00160000; + + if ((ret = svcSendSyncRequest(y2rHandle)) != 0) return ret; + *is_done = cmdbuf[2] & 0xFF; + return cmdbuf[1]; +} + +Result Y2RU_IsDoneSendingV(bool* is_done) +{ + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = 0x00170000; + + if ((ret = svcSendSyncRequest(y2rHandle)) != 0) return ret; + *is_done = cmdbuf[2] & 0xFF; + return cmdbuf[1]; +} + +Result Y2RU_SetReceiving(void* dst_buf, u32 image_size, u16 transfer_unit, u16 transfer_gap) +{ + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = 0x00180102; + cmdbuf[1] = (u32)dst_buf; + cmdbuf[2] = image_size; + cmdbuf[3] = transfer_unit; + cmdbuf[4] = transfer_gap; + cmdbuf[5] = 0; + cmdbuf[6] = 0xFFFF8001; + + if ((ret = svcSendSyncRequest(y2rHandle)) != 0) return ret; + return cmdbuf[1]; +} + +Result Y2RU_IsDoneReceiving(bool* is_done) +{ + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = 0x00190000; + + if ((ret = svcSendSyncRequest(y2rHandle)) != 0) return ret; + *is_done = cmdbuf[2] & 0xFF; + return cmdbuf[1]; +} + +Result Y2RU_SetInputLineWidth(u16 line_width) +{ + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = 0x001A0040; + cmdbuf[1] = line_width; + + if ((ret = svcSendSyncRequest(y2rHandle)) != 0) return ret; + return cmdbuf[1]; +} + +Result Y2RU_SetInputLines(u16 num_lines) +{ + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = 0x001C0040; + cmdbuf[1] = num_lines; + + if ((ret = svcSendSyncRequest(y2rHandle)) != 0) return ret; + return cmdbuf[1]; +} + +Result Y2RU_SetCoefficient(const Y2R_ColorCoefficients* coefficient) +{ + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = 0x001E0100; + memcpy(&cmdbuf[1], coefficient, sizeof(Y2R_ColorCoefficients)); + + if ((ret = svcSendSyncRequest(y2rHandle)) != 0) return ret; + return cmdbuf[1]; +} + +Result Y2RU_SetStandardCoefficient(Y2R_StandardCoefficient coefficient) +{ + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = 0x00200040; + cmdbuf[1] = coefficient; + + if ((ret = svcSendSyncRequest(y2rHandle)) != 0) return ret; + return cmdbuf[1]; +} + +Result Y2RU_SetAlpha(u16 alpha) +{ + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = 0x00220040; + cmdbuf[1] = alpha; + + if ((ret = svcSendSyncRequest(y2rHandle)) != 0) return ret; + return cmdbuf[1]; +} + +Result Y2RU_SetUnknownParams(const u16 params[16]) +{ + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = 0x00240200; + memcpy(&cmdbuf[1], params, sizeof(u16) * 16); + + if ((ret = svcSendSyncRequest(y2rHandle)) != 0) return ret; + return cmdbuf[1]; +} + +Result Y2RU_StartConversion(void) +{ + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = 0x00260000; + + if ((ret = svcSendSyncRequest(y2rHandle)) != 0) return ret; + return cmdbuf[1]; +} + +Result Y2RU_StopConversion(void) +{ + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = 0x00270000; + + if ((ret = svcSendSyncRequest(y2rHandle)) != 0) return ret; + return cmdbuf[1]; +} + +Result Y2RU_IsBusyConversion(bool* is_busy) +{ + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = 0x00280000; + + if ((ret = svcSendSyncRequest(y2rHandle)) != 0) return ret; + *is_busy = cmdbuf[2] & 0xFF; + return cmdbuf[1]; +} + +Result Y2RU_SetConversionParams(const Y2R_ConversionParams* params) +{ + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = 0x002900C0; + memcpy(&cmdbuf[1], params, sizeof(Y2R_ConversionParams)); + + if ((ret = svcSendSyncRequest(y2rHandle)) != 0) return ret; + return cmdbuf[1]; +} + +Result Y2RU_PingProcess(u8* ping) +{ + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = 0x002A0000; + + if ((ret = svcSendSyncRequest(y2rHandle)) != 0) return ret; + *ping = (u8)cmdbuf[2]; + return cmdbuf[1]; +} + +Result Y2RU_DriverInitialize(void) +{ + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = 0x002B0000; + + if ((ret = svcSendSyncRequest(y2rHandle)) != 0) return ret; + return cmdbuf[1]; +} + +Result Y2RU_DriverFinalize(void) +{ + Result ret = 0; + u32* cmdbuf = getThreadCommandBuffer(); + cmdbuf[0] = 0x002C0000; + + if ((ret = svcSendSyncRequest(y2rHandle)) != 0) return ret; + return cmdbuf[1]; +} From 364b9834db224b9fe4095243795b547488211c0a Mon Sep 17 00:00:00 2001 From: Lectem Date: Sun, 21 Jun 2015 13:15:10 +0200 Subject: [PATCH 2/2] Documented y2r Also aligned enums for readability and changed SetCoefficient to SetCoefficients --- libctru/include/3ds/services/y2r.h | 355 ++++++++++++++++++++++++----- libctru/source/services/y2r.c | 4 +- 2 files changed, 306 insertions(+), 53 deletions(-) diff --git a/libctru/include/3ds/services/y2r.h b/libctru/include/3ds/services/y2r.h index bd25bf0..40b284b 100644 --- a/libctru/include/3ds/services/y2r.h +++ b/libctru/include/3ds/services/y2r.h @@ -1,69 +1,71 @@ +/** + * @file y2r.h + */ #pragma once #include <3ds/types.h> +/** + * @brief Input color formats + * + * For the 16-bit per component formats, bits 15-8 are padding and 7-0 contains the value. + */ typedef enum { - INPUT_YUV422_INDIV_8 = 0x0, - INPUT_YUV420_INDIV_8 = 0x1, - INPUT_YUV422_INDIV_16 = 0x2, - INPUT_YUV420_INDIV_16 = 0x3, - INPUT_YUV422_BATCH = 0x4, + INPUT_YUV422_INDIV_8 = 0x0, ///< 8-bit per component, planar YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples).\n Usually named YUV422P. + INPUT_YUV420_INDIV_8 = 0x1, ///< 8-bit per component, planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples).\n Usually named YUV420P. + INPUT_YUV422_INDIV_16 = 0x2, ///< 16-bit per component, planar YUV 4:2:2, 32bpp, (1 Cr & Cb sample per 2x1 Y samples).\n Usually named YUV422P16. + INPUT_YUV420_INDIV_16 = 0x3, ///< 16-bit per component, planar YUV 4:2:0, 24bpp, (1 Cr & Cb sample per 2x2 Y samples).\n Usually named YUV420P16. + INPUT_YUV422_BATCH = 0x4, ///< 8-bit per component, packed YUV 4:2:2, 16bpp, (Y0 Cb Y1 Cr).\n Usually named YUYV422. } Y2R_InputFormat; +/** + * @brief Output color formats + * + * Those are the same as the framebuffer and GPU texture formats. + */ typedef enum { - OUTPUT_RGB_32 = 0x0, - OUTPUT_RGB_24 = 0x1, - OUTPUT_RGB_16_555 = 0x2, + OUTPUT_RGB_32 = 0x0, ///< The alpha component is the 8-bit value set by @ref Y2RU_SetAlpha + OUTPUT_RGB_24 = 0x1, + OUTPUT_RGB_16_555 = 0x2, ///< The alpha bit is the 7th bit of the alpha value set by @ref Y2RU_SetAlpha OUTPUT_RGB_16_565 = 0x3, } Y2R_OutputFormat; +/** + * @brief Rotation to be applied to the output + */ typedef enum { - ROTATION_NONE = 0x0, - ROTATION_CLOCKWISE_90 = 0x1, + ROTATION_NONE = 0x0, + ROTATION_CLOCKWISE_90 = 0x1, ROTATION_CLOCKWISE_180 = 0x2, ROTATION_CLOCKWISE_270 = 0x3, } Y2R_Rotation; +/** + * @brief Block alignment of output + * + * Defines the way the output will be laid out in memory. + */ typedef enum { - BLOCK_LINE = 0x0, - BLOCK_8_BY_8 = 0x1, + BLOCK_LINE = 0x0, ///< The result buffer will be laid out in linear format, the usual way. + BLOCK_8_BY_8 = 0x1, ///< The result will be stored as 8x8 blocks in Z-order.\n Useful for textures since it is the format used by the PICA200. } Y2R_BlockAlignment; -typedef enum -{ - COEFFICIENT_ITU_R_BT_601 = 0x0, - COEFFICIENT_ITU_R_BT_709 = 0x1, - COEFFICIENT_ITU_R_BT_601_SCALING = 0x2, - COEFFICIENT_ITU_R_BT_709_SCALING = 0x3, -} Y2R_StandardCoefficient; - -typedef struct -{ - Y2R_InputFormat input_format : 8; - Y2R_OutputFormat output_format : 8; - Y2R_Rotation rotation : 8; - Y2R_BlockAlignment block_alignment : 8; - u16 input_line_width; - u16 input_lines; - Y2R_StandardCoefficient standard_coefficient : 8; - u8 unused; - u16 alpha; -} Y2R_ConversionParams; - /** + * @brief Coefficients of the YUV->RGB conversion formula. + * * A set of coefficients configuring the RGB to YUV conversion. Coefficients 0-4 are unsigned 2.8 * fixed pointer numbers representing entries on the conversion matrix, while coefficient 5-7 are * signed 11.5 fixed point numbers added as offsets to the RGB result. * * The overall conversion process formula is: - * ``` + * @code * R = trunc((rgb_Y * Y + r_V * V) + 0.75 + r_offset) * G = trunc((rgb_Y * Y - g_U * U - g_V * V) + 0.75 + g_offset) * B = trunc((rgb_Y * Y + b_U * U ) + 0.75 + b_offset) - * ``` + * @endcode */ typedef struct { @@ -77,38 +79,289 @@ typedef struct u16 b_offset; } Y2R_ColorCoefficients; +/** + * @brief Preset conversion coefficients based on ITU standards for the YUV->RGB formula. + * + * For more details refer to @ref Y2R_ColorCoefficients + */ +typedef enum +{ + COEFFICIENT_ITU_R_BT_601 = 0x0, ///< Coefficients from the ITU-R BT.601 standard with PC ranges. + COEFFICIENT_ITU_R_BT_709 = 0x1, ///< Coefficients from the ITU-R BT.709 standard with PC ranges. + COEFFICIENT_ITU_R_BT_601_SCALING = 0x2, ///< Coefficients from the ITU-R BT.601 standard with TV ranges. + COEFFICIENT_ITU_R_BT_709_SCALING = 0x3, ///< Coefficients from the ITU-R BT.709 standard with TV ranges. +} Y2R_StandardCoefficient; + +/** + * @brief Structure used to configure all parameters at once. + * + * You can send a batch of configuration parameters using this structure and @ref Y2RU_SetConversionParams. + * + */ +typedef struct +{ + Y2R_InputFormat input_format : 8; ///< Value passed to @ref Y2RU_SetInputFormat + Y2R_OutputFormat output_format : 8; ///< Value passed to @ref Y2RU_SetOutputFormat + Y2R_Rotation rotation : 8; ///< Value passed to @ref Y2RU_SetRotation + Y2R_BlockAlignment block_alignment : 8; ///< Value passed to @ref Y2RU_SetBlockAlignment + u16 input_line_width; ///< Value passed to @ref Y2RU_SetInputLineWidth + u16 input_lines; ///< Value passed to @ref Y2RU_SetInputLines + Y2R_StandardCoefficient standard_coefficient : 8; ///< Value passed to @ref Y2RU_SetStandardCoefficient + u8 unused; + u16 alpha; ///< Value passed to @ref Y2RU_SetAlpha +} Y2R_ConversionParams; + + +/** + * @brief Initializes the y2r service. + * + * This will internally get the handle of the service, and on success call Y2RU_DriverInitialize. + */ Result y2rInit(); + + +/** + * @brief Closes the y2r service. + * + * This will internally call Y2RU_DriverFinalize and close the handle of the service. + */ Result y2rExit(); + +/** + * @brief Used to configure the input format. + * + * @note Prefer using @ref Y2RU_SetConversionParams if you have to set multiple parameters. + */ Result Y2RU_SetInputFormat(Y2R_InputFormat format); + + +/** + * @brief Used to configure the output format. + * + * @note Prefer using @ref Y2RU_SetConversionParams if you have to set multiple parameters. + */ Result Y2RU_SetOutputFormat(Y2R_OutputFormat format); + +/** + * @brief Used to configure the rotation of the output. + * + * It seems to apply the rotation per batch of 8 lines, so the output will be (height/8) images of size 8 x width. + * + * @note Prefer using @ref Y2RU_SetConversionParams if you have to set multiple parameters. + */ Result Y2RU_SetRotation(Y2R_Rotation rotation); + +/** + * @brief Used to configure the alignment of the output buffer. + * + * @note Prefer using @ref Y2RU_SetConversionParams if you have to set multiple parameters. + */ Result Y2RU_SetBlockAlignment(Y2R_BlockAlignment alignment); -Result Y2RU_SetTransferEndInterrupt(bool should_interrupt); -Result Y2RU_GetTransferEndEvent(Handle* end_event); -Result Y2RU_SetSendingY(const void* src_buf, u32 image_size, u16 transfer_unit, u16 transfer_gap); -Result Y2RU_SetSendingU(const void* src_buf, u32 image_size, u16 transfer_unit, u16 transfer_gap); -Result Y2RU_SetSendingV(const void* src_buf, u32 image_size, u16 transfer_unit, u16 transfer_gap); -Result Y2RU_SetSendingYUYV(const void* src_buf, u32 image_size, u16 transfer_unit, u16 transfer_gap); -Result Y2RU_IsDoneSendingYUYV(bool* is_done); -Result Y2RU_IsDoneSendingY(bool* is_done); -Result Y2RU_IsDoneSendingU(bool* is_done); -Result Y2RU_IsDoneSendingV(bool* is_done); -Result Y2RU_SetReceiving(void* dst_buf, u32 image_size, u16 transfer_unit, u16 transfer_gap); -Result Y2RU_IsDoneReceiving(bool* is_done); +/** + * @brief Used to configure the width of the image. + * @param line_width Width of the image in pixels. Must be a multiple of 8, up to 1024. + * + * @note Prefer using @ref Y2RU_SetConversionParams if you have to set multiple parameters. + */ Result Y2RU_SetInputLineWidth(u16 line_width); + +/** + * @brief Used to configure the height of the image. + * @param num_lines Number of lines to be converted. + * + * A multiple of 8 seems to be preferred. + * If using the @ref BLOCK_8_BY_8 mode, it must be a multiple of 8. + * + * @note Prefer using @ref Y2RU_SetConversionParams if you have to set multiple parameters. + */ Result Y2RU_SetInputLines(u16 num_lines); -Result Y2RU_SetCoefficient(const Y2R_ColorCoefficients* coefficient); + +/** + * @brief Used to configure the color conversion formula. + * + * See @ref Y2R_ColorCoefficients for more information about the coefficients. + * + * @note Prefer using @ref Y2RU_SetConversionParams if you have to set multiple parameters. + */ +Result Y2RU_SetCoefficients(const Y2R_ColorCoefficients* coefficients); + +/** + * @brief Used to configure the color conversion formula with ITU stantards coefficients. + * + * See @ref Y2R_ColorCoefficients for more information about the coefficients. + * + * @note Prefer using @ref Y2RU_SetConversionParams if you have to set multiple parameters. + */ Result Y2RU_SetStandardCoefficient(Y2R_StandardCoefficient coefficient); +/** + * @brief Used to configure the alpha value of the output. + * @param alpha 8-bit value to be used for the output when the format requires it. + * + * @note Prefer using @ref Y2RU_SetConversionParams if you have to set multiple parameters. + */ Result Y2RU_SetAlpha(u16 alpha); + +/** + * @brief Used to enable the end of conversion interrupt. + * @param should_interrupt Enables the interrupt if true, disable it if false. + * + * It is possible to fire an interrupt when the conversion is finished, and that the DMA is done copying the data. + * This interrupt will then be used to fire an event. See @ref Y2RU_GetTransferEndEvent. + * By default the interrupt is enabled. + * + * @note It seems that the event can be fired too soon in some cases, depending the transfer_unit size.\n Please see the note at @ref Y2RU_SetReceiving + */ +Result Y2RU_SetTransferEndInterrupt(bool should_interrupt); + +/** + * @brief Gets an handle to the end of conversion event. + * @param end_event Pointer to the event handle to be set to the end of conversion event. It isn't necessary to create or close this handle. + * + * To enable this event you have to use @code{C} Y2RU_SetTransferEndInterrupt(true);@endcode + * The event will be triggered when the corresponding interrupt is fired. + * + * @note It is recommended to use a timeout when waiting on this event, as it sometimes (but rarely) isn't triggered. + */ +Result Y2RU_GetTransferEndEvent(Handle* end_event); + +/** + * @brief Configures the Y plane buffer. + * @param src_buf A pointer to the beginning of your Y data buffer. + * @param image_size The total size of the data buffer. + * @param transfer_unit Specifies the size of 1 DMA transfer. Usually set to 1 line. This has to be a divisor of image_size. + * @param transfer_unit Specifies the gap (offset) to be added after each transfer. Can be used to convert images with stride or only a part of it. + * + * This specifies the Y data buffer for the planar input formats (INPUT_YUV42*_INDIV_*). + * The actual transfer will only happen after calling @ref Y2RU_StartConversion. + */ +Result Y2RU_SetSendingY(const void* src_buf, u32 image_size, u16 transfer_unit, u16 transfer_gap); + +/** + * @brief Configures the U plane buffer. + * @param src_buf A pointer to the beginning of your Y data buffer. + * @param image_size The total size of the data buffer. + * @param transfer_unit Specifies the size of 1 DMA transfer. Usually set to 1 line. This has to be a divisor of image_size. + * @param transfer_unit Specifies the gap (offset) to be added after each transfer. Can be used to convert images with stride or only a part of it. + * + * This specifies the U data buffer for the planar input formats (INPUT_YUV42*_INDIV_*). + * The actual transfer will only happen after calling @ref Y2RU_StartConversion. + */ +Result Y2RU_SetSendingU(const void* src_buf, u32 image_size, u16 transfer_unit, u16 transfer_gap); + +/** + * @brief Configures the V plane buffer. + * @param src_buf A pointer to the beginning of your Y data buffer. + * @param image_size The total size of the data buffer. + * @param transfer_unit Specifies the size of 1 DMA transfer. Usually set to 1 line. This has to be a divisor of image_size. + * @param transfer_unit Specifies the gap (offset) to be added after each transfer. Can be used to convert images with stride or only a part of it. + * + * This specifies the V data buffer for the planar input formats (INPUT_YUV42*_INDIV_*). + * The actual transfer will only happen after calling @ref Y2RU_StartConversion. + */ +Result Y2RU_SetSendingV(const void* src_buf, u32 image_size, u16 transfer_unit, u16 transfer_gap); + +/** + * @brief Configures the YUYV source buffer. + * @param src_buf A pointer to the beginning of your Y data buffer. + * @param image_size The total size of the data buffer. + * @param transfer_unit Specifies the size of 1 DMA transfer. Usually set to 1 line. This has to be a divisor of image_size. + * @param transfer_unit Specifies the gap (offset) to be added after each transfer. Can be used to convert images with stride or only a part of it. + * + * This specifies the YUYV data buffer for the packed input format @ref INPUT_YUV422_BATCH. + * The actual transfer will only happen after calling @ref Y2RU_StartConversion. + */ +Result Y2RU_SetSendingYUYV(const void* src_buf, u32 image_size, u16 transfer_unit, u16 transfer_gap); + +/** + * @brief Configures the destination buffer. + * @param src_buf A pointer to the beginning of your destination buffer in FCRAM + * @param image_size The total size of the data buffer. + * @param transfer_unit Specifies the size of 1 DMA transfer. Usually set to 1 line. This has to be a divisor of image_size. + * @param transfer_unit Specifies the gap (offset) to be added after each transfer. Can be used to convert images with stride or only a part of it. + * + * This specifies the destination buffer of the conversion. + * The actual transfer will only happen after calling @ref Y2RU_StartConversion. + * The buffer does NOT need to be allocated in the linear heap. + * + * @note + * It seems that depending on the size of the image and of the transfer unit,\n + * it is possible for the end of conversion interrupt to be triggered right after the conversion began.\n + * One line as transfer_unit seems to trigger this issue for 400x240, setting to 2/4/8 lines fixes it. + * + * @note Setting a transfer_unit of 4 or 8 lines seems to bring the best results in terms of speed for a 400x240 image. + */ +Result Y2RU_SetReceiving(void* dst_buf, u32 image_size, u16 transfer_unit, u16 transfer_gap); + +/** + * @brief Checks if the DMA has finished sending the Y buffer. + * @param is_done pointer to the boolean that will hold the result + * + * True if the DMA has finished transferring the Y plane, false otherwise. To be used with @ref Y2RU_SetSendingY. + */ +Result Y2RU_IsDoneSendingY(bool* is_done); + +/** + * @brief Checks if the DMA has finished sending the U buffer. + * @param is_done pointer to the boolean that will hold the result + * + * True if the DMA has finished transferring the U plane, false otherwise. To be used with @ref Y2RU_SetSendingU. + */ +Result Y2RU_IsDoneSendingU(bool* is_done); + +/** + * @brief Checks if the DMA has finished sending the V buffer. + * @param is_done pointer to the boolean that will hold the result + * + * True if the DMA has finished transferring the V plane, false otherwise. To be used with @ref Y2RU_SetSendingV. + */ +Result Y2RU_IsDoneSendingV(bool* is_done); + +/** + * @brief Checks if the DMA has finished sending the YUYV buffer. + * @param is_done pointer to the boolean that will hold the result + * + * True if the DMA has finished transferring the YUYV buffer, false otherwise. To be used with @ref Y2RU_SetSendingYUYV. + */ +Result Y2RU_IsDoneSendingYUYV(bool* is_done); + +/** + * @brief Checks if the DMA has finished sending the converted result. + * @param is_done pointer to the boolean that will hold the result + * + * True if the DMA has finished transferring data to your destination buffer, false otherwise. + */ +Result Y2RU_IsDoneReceiving(bool* is_done); + Result Y2RU_SetUnknownParams(const u16 params[16]); -Result Y2RU_StartConversion(void); -Result Y2RU_StopConversion(void); -Result Y2RU_IsBusyConversion(bool* is_busy); + +/** + * @brief Sets all the parameters of Y2R_ConversionParams at once. + * + * Faster than calling the individual value through Y2R_Set* because only one system call is made. + */ Result Y2RU_SetConversionParams(const Y2R_ConversionParams* params); +/** + * @brief Starts the conversion process + */ +Result Y2RU_StartConversion(void); + +/** + * @brief Cancels the conversion + */ +Result Y2RU_StopConversion(void); + +/** + * @brief Check if the conversion and DMA transfer are finished + * + * This can have the same problems as the event and interrupt. See @ref Y2RU_SetTransferEndInterrupt. + */ +Result Y2RU_IsBusyConversion(bool* is_busy); + + /* Seems to check whether y2r is ready to be used */ Result Y2RU_PingProcess(u8* ping); diff --git a/libctru/source/services/y2r.c b/libctru/source/services/y2r.c index 60101a6..bb26f56 100644 --- a/libctru/source/services/y2r.c +++ b/libctru/source/services/y2r.c @@ -277,12 +277,12 @@ Result Y2RU_SetInputLines(u16 num_lines) return cmdbuf[1]; } -Result Y2RU_SetCoefficient(const Y2R_ColorCoefficients* coefficient) +Result Y2RU_SetCoefficients(const Y2R_ColorCoefficients* coefficients) { Result ret = 0; u32* cmdbuf = getThreadCommandBuffer(); cmdbuf[0] = 0x001E0100; - memcpy(&cmdbuf[1], coefficient, sizeof(Y2R_ColorCoefficients)); + memcpy(&cmdbuf[1], coefficients, sizeof(Y2R_ColorCoefficients)); if ((ret = svcSendSyncRequest(y2rHandle)) != 0) return ret; return cmdbuf[1];