y2r_u.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787
  1. // Copyright 2015 Citra Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <cstring>
  5. #include "common/common_funcs.h"
  6. #include "common/common_types.h"
  7. #include "common/logging/log.h"
  8. #include "core/hle/ipc.h"
  9. #include "core/hle/ipc_helpers.h"
  10. #include "core/hle/kernel/event.h"
  11. #include "core/hle/kernel/kernel.h"
  12. #include "core/hle/service/y2r_u.h"
  13. #include "core/hw/y2r.h"
  14. namespace Service {
  15. namespace Y2R {
  16. struct ConversionParameters {
  17. InputFormat input_format;
  18. OutputFormat output_format;
  19. Rotation rotation;
  20. BlockAlignment block_alignment;
  21. u16 input_line_width;
  22. u16 input_lines;
  23. StandardCoefficient standard_coefficient;
  24. u8 padding;
  25. u16 alpha;
  26. };
  27. static_assert(sizeof(ConversionParameters) == 12, "ConversionParameters struct has incorrect size");
  28. static Kernel::SharedPtr<Kernel::Event> completion_event;
  29. static ConversionConfiguration conversion;
  30. static DitheringWeightParams dithering_weight_params;
  31. static u32 temporal_dithering_enabled = 0;
  32. static u32 transfer_end_interrupt_enabled = 0;
  33. static u32 spacial_dithering_enabled = 0;
  34. static const CoefficientSet standard_coefficients[4] = {
  35. {{0x100, 0x166, 0xB6, 0x58, 0x1C5, -0x166F, 0x10EE, -0x1C5B}}, // ITU_Rec601
  36. {{0x100, 0x193, 0x77, 0x2F, 0x1DB, -0x1933, 0xA7C, -0x1D51}}, // ITU_Rec709
  37. {{0x12A, 0x198, 0xD0, 0x64, 0x204, -0x1BDE, 0x10F2, -0x229B}}, // ITU_Rec601_Scaling
  38. {{0x12A, 0x1CA, 0x88, 0x36, 0x21C, -0x1F04, 0x99C, -0x2421}}, // ITU_Rec709_Scaling
  39. };
  40. ResultCode ConversionConfiguration::SetInputLineWidth(u16 width) {
  41. if (width == 0 || width > 1024 || width % 8 != 0) {
  42. return ResultCode(ErrorDescription::OutOfRange, ErrorModule::CAM,
  43. ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E053FD
  44. }
  45. // Note: The hardware uses the register value 0 to represent a width of 1024, so for a width of
  46. // 1024 the `camera` module would set the value 0 here, but we don't need to emulate this
  47. // internal detail.
  48. this->input_line_width = width;
  49. return RESULT_SUCCESS;
  50. }
  51. ResultCode ConversionConfiguration::SetInputLines(u16 lines) {
  52. if (lines == 0 || lines > 1024) {
  53. return ResultCode(ErrorDescription::OutOfRange, ErrorModule::CAM,
  54. ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E053FD
  55. }
  56. // Note: In what appears to be a bug, the `camera` module does not set the hardware register at
  57. // all if `lines` is 1024, so the conversion uses the last value that was set. The intention
  58. // was probably to set it to 0 like in SetInputLineWidth.
  59. if (lines != 1024) {
  60. this->input_lines = lines;
  61. }
  62. return RESULT_SUCCESS;
  63. }
  64. ResultCode ConversionConfiguration::SetStandardCoefficient(
  65. StandardCoefficient standard_coefficient) {
  66. size_t index = static_cast<size_t>(standard_coefficient);
  67. if (index >= ARRAY_SIZE(standard_coefficients)) {
  68. return ResultCode(ErrorDescription::InvalidEnumValue, ErrorModule::CAM,
  69. ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E053ED
  70. }
  71. std::memcpy(coefficients.data(), standard_coefficients[index].data(), sizeof(coefficients));
  72. return RESULT_SUCCESS;
  73. }
  74. static void SetInputFormat(Interface* self) {
  75. u32* cmd_buff = Kernel::GetCommandBuffer();
  76. conversion.input_format = static_cast<InputFormat>(cmd_buff[1]);
  77. cmd_buff[0] = IPC::MakeHeader(0x1, 1, 0);
  78. cmd_buff[1] = RESULT_SUCCESS.raw;
  79. LOG_DEBUG(Service_Y2R, "called input_format=%hhu", conversion.input_format);
  80. }
  81. static void GetInputFormat(Interface* self) {
  82. u32* cmd_buff = Kernel::GetCommandBuffer();
  83. cmd_buff[0] = IPC::MakeHeader(0x2, 2, 0);
  84. cmd_buff[1] = RESULT_SUCCESS.raw;
  85. cmd_buff[2] = static_cast<u32>(conversion.input_format);
  86. LOG_DEBUG(Service_Y2R, "called input_format=%hhu", conversion.input_format);
  87. }
  88. static void SetOutputFormat(Interface* self) {
  89. u32* cmd_buff = Kernel::GetCommandBuffer();
  90. conversion.output_format = static_cast<OutputFormat>(cmd_buff[1]);
  91. cmd_buff[0] = IPC::MakeHeader(0x3, 1, 0);
  92. cmd_buff[1] = RESULT_SUCCESS.raw;
  93. LOG_DEBUG(Service_Y2R, "called output_format=%hhu", conversion.output_format);
  94. }
  95. static void GetOutputFormat(Interface* self) {
  96. u32* cmd_buff = Kernel::GetCommandBuffer();
  97. cmd_buff[0] = IPC::MakeHeader(0x4, 2, 0);
  98. cmd_buff[1] = RESULT_SUCCESS.raw;
  99. cmd_buff[2] = static_cast<u32>(conversion.output_format);
  100. LOG_DEBUG(Service_Y2R, "called output_format=%hhu", conversion.output_format);
  101. }
  102. static void SetRotation(Interface* self) {
  103. u32* cmd_buff = Kernel::GetCommandBuffer();
  104. conversion.rotation = static_cast<Rotation>(cmd_buff[1]);
  105. cmd_buff[0] = IPC::MakeHeader(0x5, 1, 0);
  106. cmd_buff[1] = RESULT_SUCCESS.raw;
  107. LOG_DEBUG(Service_Y2R, "called rotation=%hhu", conversion.rotation);
  108. }
  109. static void GetRotation(Interface* self) {
  110. u32* cmd_buff = Kernel::GetCommandBuffer();
  111. cmd_buff[0] = IPC::MakeHeader(0x6, 2, 0);
  112. cmd_buff[1] = RESULT_SUCCESS.raw;
  113. cmd_buff[2] = static_cast<u32>(conversion.rotation);
  114. LOG_DEBUG(Service_Y2R, "called rotation=%hhu", conversion.rotation);
  115. }
  116. static void SetBlockAlignment(Interface* self) {
  117. u32* cmd_buff = Kernel::GetCommandBuffer();
  118. conversion.block_alignment = static_cast<BlockAlignment>(cmd_buff[1]);
  119. cmd_buff[0] = IPC::MakeHeader(0x7, 1, 0);
  120. cmd_buff[1] = RESULT_SUCCESS.raw;
  121. LOG_DEBUG(Service_Y2R, "called block_alignment=%hhu", conversion.block_alignment);
  122. }
  123. static void GetBlockAlignment(Interface* self) {
  124. u32* cmd_buff = Kernel::GetCommandBuffer();
  125. cmd_buff[0] = IPC::MakeHeader(0x8, 2, 0);
  126. cmd_buff[1] = RESULT_SUCCESS.raw;
  127. cmd_buff[2] = static_cast<u32>(conversion.block_alignment);
  128. LOG_DEBUG(Service_Y2R, "called block_alignment=%hhu", conversion.block_alignment);
  129. }
  130. /**
  131. * Y2R_U::SetSpacialDithering service function
  132. * Inputs:
  133. * 1 : u8, 0 = Disabled, 1 = Enabled
  134. * Outputs:
  135. * 1 : Result of function, 0 on success, otherwise error code
  136. */
  137. static void SetSpacialDithering(Interface* self) {
  138. u32* cmd_buff = Kernel::GetCommandBuffer();
  139. spacial_dithering_enabled = cmd_buff[1] & 0xF;
  140. cmd_buff[0] = IPC::MakeHeader(0x9, 1, 0);
  141. cmd_buff[1] = RESULT_SUCCESS.raw;
  142. LOG_WARNING(Service_Y2R, "(STUBBED) called");
  143. }
  144. /**
  145. * Y2R_U::GetSpacialDithering service function
  146. * Outputs:
  147. * 1 : Result of function, 0 on success, otherwise error code
  148. * 2 : u8, 0 = Disabled, 1 = Enabled
  149. */
  150. static void GetSpacialDithering(Interface* self) {
  151. IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0xA, 2, 0);
  152. rb.Push(RESULT_SUCCESS);
  153. rb.Push(spacial_dithering_enabled != 0);
  154. LOG_WARNING(Service_Y2R, "(STUBBED) called");
  155. }
  156. /**
  157. * Y2R_U::SetTemporalDithering service function
  158. * Inputs:
  159. * 1 : u8, 0 = Disabled, 1 = Enabled
  160. * Outputs:
  161. * 1 : Result of function, 0 on success, otherwise error code
  162. */
  163. static void SetTemporalDithering(Interface* self) {
  164. u32* cmd_buff = Kernel::GetCommandBuffer();
  165. temporal_dithering_enabled = cmd_buff[1] & 0xF;
  166. cmd_buff[0] = IPC::MakeHeader(0xB, 1, 0);
  167. cmd_buff[1] = RESULT_SUCCESS.raw;
  168. LOG_WARNING(Service_Y2R, "(STUBBED) called");
  169. }
  170. /**
  171. * Y2R_U::GetTemporalDithering service function
  172. * Outputs:
  173. * 1 : Result of function, 0 on success, otherwise error code
  174. * 2 : u8, 0 = Disabled, 1 = Enabled
  175. */
  176. static void GetTemporalDithering(Interface* self) {
  177. u32* cmd_buff = Kernel::GetCommandBuffer();
  178. cmd_buff[0] = IPC::MakeHeader(0xC, 2, 0);
  179. cmd_buff[1] = RESULT_SUCCESS.raw;
  180. cmd_buff[2] = temporal_dithering_enabled;
  181. LOG_WARNING(Service_Y2R, "(STUBBED) called");
  182. }
  183. /**
  184. * Y2R_U::SetTransferEndInterrupt service function
  185. * Inputs:
  186. * 1 : u8, 0 = Disabled, 1 = Enabled
  187. * Outputs:
  188. * 1 : Result of function, 0 on success, otherwise error code
  189. */
  190. static void SetTransferEndInterrupt(Interface* self) {
  191. u32* cmd_buff = Kernel::GetCommandBuffer();
  192. transfer_end_interrupt_enabled = cmd_buff[1] & 0xf;
  193. cmd_buff[0] = IPC::MakeHeader(0xD, 1, 0);
  194. cmd_buff[1] = RESULT_SUCCESS.raw;
  195. LOG_WARNING(Service_Y2R, "(STUBBED) called");
  196. }
  197. /**
  198. * Y2R_U::GetTransferEndInterrupt service function
  199. * Outputs:
  200. * 1 : Result of function, 0 on success, otherwise error code
  201. * 2 : u8, 0 = Disabled, 1 = Enabled
  202. */
  203. static void GetTransferEndInterrupt(Interface* self) {
  204. u32* cmd_buff = Kernel::GetCommandBuffer();
  205. cmd_buff[0] = IPC::MakeHeader(0xE, 2, 0);
  206. cmd_buff[1] = RESULT_SUCCESS.raw;
  207. cmd_buff[2] = transfer_end_interrupt_enabled;
  208. LOG_WARNING(Service_Y2R, "(STUBBED) called");
  209. }
  210. /**
  211. * Y2R_U::GetTransferEndEvent service function
  212. * Outputs:
  213. * 1 : Result of function, 0 on success, otherwise error code
  214. * 3 : The handle of the completion event
  215. */
  216. static void GetTransferEndEvent(Interface* self) {
  217. u32* cmd_buff = Kernel::GetCommandBuffer();
  218. cmd_buff[0] = IPC::MakeHeader(0xF, 2, 0);
  219. cmd_buff[1] = RESULT_SUCCESS.raw;
  220. cmd_buff[3] = Kernel::g_handle_table.Create(completion_event).Unwrap();
  221. LOG_DEBUG(Service_Y2R, "called");
  222. }
  223. static void SetSendingY(Interface* self) {
  224. // The helper should be passed by argument to the function
  225. IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x00100102);
  226. conversion.src_Y.address = rp.Pop<u32>();
  227. conversion.src_Y.image_size = rp.Pop<u32>();
  228. conversion.src_Y.transfer_unit = rp.Pop<u32>();
  229. conversion.src_Y.gap = rp.Pop<u32>();
  230. Kernel::Handle src_process_handle = rp.PopHandle();
  231. IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
  232. rb.Push(RESULT_SUCCESS);
  233. LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, "
  234. "src_process_handle=0x%08X",
  235. conversion.src_Y.image_size, conversion.src_Y.transfer_unit, conversion.src_Y.gap,
  236. src_process_handle);
  237. }
  238. static void SetSendingU(Interface* self) {
  239. // The helper should be passed by argument to the function
  240. IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x00110102);
  241. conversion.src_U.address = rp.Pop<u32>();
  242. conversion.src_U.image_size = rp.Pop<u32>();
  243. conversion.src_U.transfer_unit = rp.Pop<u32>();
  244. conversion.src_U.gap = rp.Pop<u32>();
  245. Kernel::Handle src_process_handle = rp.PopHandle();
  246. IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
  247. rb.Push(RESULT_SUCCESS);
  248. LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, "
  249. "src_process_handle=0x%08X",
  250. conversion.src_U.image_size, conversion.src_U.transfer_unit, conversion.src_U.gap,
  251. src_process_handle);
  252. }
  253. static void SetSendingV(Interface* self) {
  254. u32* cmd_buff = Kernel::GetCommandBuffer();
  255. conversion.src_V.address = cmd_buff[1];
  256. conversion.src_V.image_size = cmd_buff[2];
  257. conversion.src_V.transfer_unit = cmd_buff[3];
  258. conversion.src_V.gap = cmd_buff[4];
  259. cmd_buff[0] = IPC::MakeHeader(0x12, 1, 0);
  260. cmd_buff[1] = RESULT_SUCCESS.raw;
  261. LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, "
  262. "src_process_handle=0x%08X",
  263. conversion.src_V.image_size, conversion.src_V.transfer_unit, conversion.src_V.gap,
  264. cmd_buff[6]);
  265. }
  266. static void SetSendingYUYV(Interface* self) {
  267. u32* cmd_buff = Kernel::GetCommandBuffer();
  268. conversion.src_YUYV.address = cmd_buff[1];
  269. conversion.src_YUYV.image_size = cmd_buff[2];
  270. conversion.src_YUYV.transfer_unit = cmd_buff[3];
  271. conversion.src_YUYV.gap = cmd_buff[4];
  272. cmd_buff[0] = IPC::MakeHeader(0x13, 1, 0);
  273. cmd_buff[1] = RESULT_SUCCESS.raw;
  274. LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, "
  275. "src_process_handle=0x%08X",
  276. conversion.src_YUYV.image_size, conversion.src_YUYV.transfer_unit,
  277. conversion.src_YUYV.gap, cmd_buff[6]);
  278. }
  279. /**
  280. * Y2R::IsFinishedSendingYuv service function
  281. * Output:
  282. * 1 : Result of the function, 0 on success, otherwise error code
  283. * 2 : u8, 0 = Not Finished, 1 = Finished
  284. */
  285. static void IsFinishedSendingYuv(Interface* self) {
  286. u32* cmd_buff = Kernel::GetCommandBuffer();
  287. cmd_buff[0] = IPC::MakeHeader(0x14, 2, 0);
  288. cmd_buff[1] = RESULT_SUCCESS.raw;
  289. cmd_buff[2] = 1;
  290. LOG_WARNING(Service_Y2R, "(STUBBED) called");
  291. }
  292. /**
  293. * Y2R::IsFinishedSendingY service function
  294. * Output:
  295. * 1 : Result of the function, 0 on success, otherwise error code
  296. * 2 : u8, 0 = Not Finished, 1 = Finished
  297. */
  298. static void IsFinishedSendingY(Interface* self) {
  299. u32* cmd_buff = Kernel::GetCommandBuffer();
  300. cmd_buff[0] = IPC::MakeHeader(0x15, 2, 0);
  301. cmd_buff[1] = RESULT_SUCCESS.raw;
  302. cmd_buff[2] = 1;
  303. LOG_WARNING(Service_Y2R, "(STUBBED) called");
  304. }
  305. /**
  306. * Y2R::IsFinishedSendingU service function
  307. * Output:
  308. * 1 : Result of the function, 0 on success, otherwise error code
  309. * 2 : u8, 0 = Not Finished, 1 = Finished
  310. */
  311. static void IsFinishedSendingU(Interface* self) {
  312. u32* cmd_buff = Kernel::GetCommandBuffer();
  313. cmd_buff[0] = IPC::MakeHeader(0x16, 2, 0);
  314. cmd_buff[1] = RESULT_SUCCESS.raw;
  315. cmd_buff[2] = 1;
  316. LOG_WARNING(Service_Y2R, "(STUBBED) called");
  317. }
  318. /**
  319. * Y2R::IsFinishedSendingV service function
  320. * Output:
  321. * 1 : Result of the function, 0 on success, otherwise error code
  322. * 2 : u8, 0 = Not Finished, 1 = Finished
  323. */
  324. static void IsFinishedSendingV(Interface* self) {
  325. u32* cmd_buff = Kernel::GetCommandBuffer();
  326. cmd_buff[0] = IPC::MakeHeader(0x17, 2, 0);
  327. cmd_buff[1] = RESULT_SUCCESS.raw;
  328. cmd_buff[2] = 1;
  329. LOG_WARNING(Service_Y2R, "(STUBBED) called");
  330. }
  331. static void SetReceiving(Interface* self) {
  332. u32* cmd_buff = Kernel::GetCommandBuffer();
  333. conversion.dst.address = cmd_buff[1];
  334. conversion.dst.image_size = cmd_buff[2];
  335. conversion.dst.transfer_unit = cmd_buff[3];
  336. conversion.dst.gap = cmd_buff[4];
  337. cmd_buff[0] = IPC::MakeHeader(0x18, 1, 0);
  338. cmd_buff[1] = RESULT_SUCCESS.raw;
  339. LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, "
  340. "dst_process_handle=0x%08X",
  341. conversion.dst.image_size, conversion.dst.transfer_unit, conversion.dst.gap,
  342. cmd_buff[6]);
  343. }
  344. /**
  345. * Y2R::IsFinishedReceiving service function
  346. * Output:
  347. * 1 : Result of the function, 0 on success, otherwise error code
  348. * 2 : u8, 0 = Not Finished, 1 = Finished
  349. */
  350. static void IsFinishedReceiving(Interface* self) {
  351. u32* cmd_buff = Kernel::GetCommandBuffer();
  352. cmd_buff[0] = IPC::MakeHeader(0x19, 2, 0);
  353. cmd_buff[1] = RESULT_SUCCESS.raw;
  354. cmd_buff[2] = 1;
  355. LOG_WARNING(Service_Y2R, "(STUBBED) called");
  356. }
  357. static void SetInputLineWidth(Interface* self) {
  358. u32* cmd_buff = Kernel::GetCommandBuffer();
  359. cmd_buff[0] = IPC::MakeHeader(0x1A, 1, 0);
  360. cmd_buff[1] = conversion.SetInputLineWidth(cmd_buff[1]).raw;
  361. LOG_DEBUG(Service_Y2R, "called input_line_width=%u", cmd_buff[1]);
  362. }
  363. static void GetInputLineWidth(Interface* self) {
  364. u32* cmd_buff = Kernel::GetCommandBuffer();
  365. cmd_buff[0] = IPC::MakeHeader(0x1B, 2, 0);
  366. cmd_buff[1] = RESULT_SUCCESS.raw;
  367. cmd_buff[2] = conversion.input_line_width;
  368. LOG_DEBUG(Service_Y2R, "called input_line_width=%u", conversion.input_line_width);
  369. }
  370. static void SetInputLines(Interface* self) {
  371. u32* cmd_buff = Kernel::GetCommandBuffer();
  372. cmd_buff[0] = IPC::MakeHeader(0x1C, 1, 0);
  373. cmd_buff[1] = conversion.SetInputLines(cmd_buff[1]).raw;
  374. LOG_DEBUG(Service_Y2R, "called input_lines=%u", cmd_buff[1]);
  375. }
  376. static void GetInputLines(Interface* self) {
  377. u32* cmd_buff = Kernel::GetCommandBuffer();
  378. cmd_buff[0] = IPC::MakeHeader(0x1D, 2, 0);
  379. cmd_buff[1] = RESULT_SUCCESS.raw;
  380. cmd_buff[2] = static_cast<u32>(conversion.input_lines);
  381. LOG_DEBUG(Service_Y2R, "called input_lines=%u", conversion.input_lines);
  382. }
  383. static void SetCoefficient(Interface* self) {
  384. u32* cmd_buff = Kernel::GetCommandBuffer();
  385. const u16* coefficients = reinterpret_cast<const u16*>(&cmd_buff[1]);
  386. std::memcpy(conversion.coefficients.data(), coefficients, sizeof(CoefficientSet));
  387. cmd_buff[0] = IPC::MakeHeader(0x1E, 1, 0);
  388. cmd_buff[1] = RESULT_SUCCESS.raw;
  389. LOG_DEBUG(Service_Y2R, "called coefficients=[%hX, %hX, %hX, %hX, %hX, %hX, %hX, %hX]",
  390. coefficients[0], coefficients[1], coefficients[2], coefficients[3], coefficients[4],
  391. coefficients[5], coefficients[6], coefficients[7]);
  392. }
  393. static void GetCoefficient(Interface* self) {
  394. u32* cmd_buff = Kernel::GetCommandBuffer();
  395. cmd_buff[0] = IPC::MakeHeader(0x1F, 5, 0);
  396. cmd_buff[1] = RESULT_SUCCESS.raw;
  397. std::memcpy(&cmd_buff[2], conversion.coefficients.data(), sizeof(CoefficientSet));
  398. LOG_DEBUG(Service_Y2R, "called");
  399. }
  400. static void SetStandardCoefficient(Interface* self) {
  401. u32* cmd_buff = Kernel::GetCommandBuffer();
  402. u32 index = cmd_buff[1];
  403. cmd_buff[0] = IPC::MakeHeader(0x20, 1, 0);
  404. cmd_buff[1] = conversion.SetStandardCoefficient((StandardCoefficient)index).raw;
  405. LOG_DEBUG(Service_Y2R, "called standard_coefficient=%u", index);
  406. }
  407. static void GetStandardCoefficient(Interface* self) {
  408. u32* cmd_buff = Kernel::GetCommandBuffer();
  409. u32 index = cmd_buff[1];
  410. if (index < ARRAY_SIZE(standard_coefficients)) {
  411. cmd_buff[0] = IPC::MakeHeader(0x21, 5, 0);
  412. cmd_buff[1] = RESULT_SUCCESS.raw;
  413. std::memcpy(&cmd_buff[2], &standard_coefficients[index], sizeof(CoefficientSet));
  414. LOG_DEBUG(Service_Y2R, "called standard_coefficient=%u ", index);
  415. } else {
  416. cmd_buff[0] = IPC::MakeHeader(0x21, 1, 0);
  417. cmd_buff[1] = ResultCode(ErrorDescription::InvalidEnumValue, ErrorModule::CAM,
  418. ErrorSummary::InvalidArgument, ErrorLevel::Usage)
  419. .raw;
  420. LOG_ERROR(Service_Y2R, "called standard_coefficient=%u The argument is invalid!", index);
  421. }
  422. }
  423. static void SetAlpha(Interface* self) {
  424. u32* cmd_buff = Kernel::GetCommandBuffer();
  425. conversion.alpha = cmd_buff[1];
  426. cmd_buff[0] = IPC::MakeHeader(0x22, 1, 0);
  427. cmd_buff[1] = RESULT_SUCCESS.raw;
  428. LOG_DEBUG(Service_Y2R, "called alpha=%hu", conversion.alpha);
  429. }
  430. static void GetAlpha(Interface* self) {
  431. u32* cmd_buff = Kernel::GetCommandBuffer();
  432. cmd_buff[0] = IPC::MakeHeader(0x23, 2, 0);
  433. cmd_buff[1] = RESULT_SUCCESS.raw;
  434. cmd_buff[2] = conversion.alpha;
  435. LOG_DEBUG(Service_Y2R, "called alpha=%hu", conversion.alpha);
  436. }
  437. static void SetDitheringWeightParams(Interface* self) {
  438. IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x24, 8, 0); // 0x240200
  439. rp.PopRaw(dithering_weight_params);
  440. IPC::RequestBuilder rb = rp.MakeBuilder(1, 0);
  441. rb.Push(RESULT_SUCCESS);
  442. LOG_DEBUG(Service_Y2R, "called");
  443. }
  444. static void GetDitheringWeightParams(Interface* self) {
  445. u32* cmd_buff = Kernel::GetCommandBuffer();
  446. cmd_buff[0] = IPC::MakeHeader(0x25, 9, 0);
  447. cmd_buff[1] = RESULT_SUCCESS.raw;
  448. std::memcpy(&cmd_buff[2], &dithering_weight_params, sizeof(DitheringWeightParams));
  449. LOG_DEBUG(Service_Y2R, "called");
  450. }
  451. static void StartConversion(Interface* self) {
  452. u32* cmd_buff = Kernel::GetCommandBuffer();
  453. // dst_image_size would seem to be perfect for this, but it doesn't include the gap :(
  454. u32 total_output_size =
  455. conversion.input_lines * (conversion.dst.transfer_unit + conversion.dst.gap);
  456. Memory::RasterizerFlushVirtualRegion(conversion.dst.address, total_output_size,
  457. Memory::FlushMode::FlushAndInvalidate);
  458. HW::Y2R::PerformConversion(conversion);
  459. completion_event->Signal();
  460. cmd_buff[0] = IPC::MakeHeader(0x26, 1, 0);
  461. cmd_buff[1] = RESULT_SUCCESS.raw;
  462. LOG_DEBUG(Service_Y2R, "called");
  463. }
  464. static void StopConversion(Interface* self) {
  465. u32* cmd_buff = Kernel::GetCommandBuffer();
  466. cmd_buff[0] = IPC::MakeHeader(0x27, 1, 0);
  467. cmd_buff[1] = RESULT_SUCCESS.raw;
  468. LOG_DEBUG(Service_Y2R, "called");
  469. }
  470. /**
  471. * Y2R_U::IsBusyConversion service function
  472. * Outputs:
  473. * 1 : Result of function, 0 on success, otherwise error code
  474. * 2 : 1 if there's a conversion running, otherwise 0.
  475. */
  476. static void IsBusyConversion(Interface* self) {
  477. u32* cmd_buff = Kernel::GetCommandBuffer();
  478. cmd_buff[0] = IPC::MakeHeader(0x28, 2, 0);
  479. cmd_buff[1] = RESULT_SUCCESS.raw;
  480. cmd_buff[2] = 0; // StartConversion always finishes immediately
  481. LOG_DEBUG(Service_Y2R, "called");
  482. }
  483. /**
  484. * Y2R_U::SetPackageParameter service function
  485. */
  486. static void SetPackageParameter(Interface* self) {
  487. u32* cmd_buff = Kernel::GetCommandBuffer();
  488. auto params = reinterpret_cast<const ConversionParameters*>(&cmd_buff[1]);
  489. conversion.input_format = params->input_format;
  490. conversion.output_format = params->output_format;
  491. conversion.rotation = params->rotation;
  492. conversion.block_alignment = params->block_alignment;
  493. ResultCode result = conversion.SetInputLineWidth(params->input_line_width);
  494. if (result.IsError())
  495. goto cleanup;
  496. result = conversion.SetInputLines(params->input_lines);
  497. if (result.IsError())
  498. goto cleanup;
  499. result = conversion.SetStandardCoefficient(params->standard_coefficient);
  500. if (result.IsError())
  501. goto cleanup;
  502. conversion.padding = params->padding;
  503. conversion.alpha = params->alpha;
  504. cleanup:
  505. cmd_buff[0] = IPC::MakeHeader(0x29, 1, 0);
  506. cmd_buff[1] = result.raw;
  507. LOG_DEBUG(
  508. Service_Y2R,
  509. "called input_format=%hhu output_format=%hhu rotation=%hhu block_alignment=%hhu "
  510. "input_line_width=%hu input_lines=%hu standard_coefficient=%hhu reserved=%hhu alpha=%hX",
  511. params->input_format, params->output_format, params->rotation, params->block_alignment,
  512. params->input_line_width, params->input_lines, params->standard_coefficient,
  513. params->padding, params->alpha);
  514. }
  515. static void PingProcess(Interface* self) {
  516. u32* cmd_buff = Kernel::GetCommandBuffer();
  517. cmd_buff[0] = IPC::MakeHeader(0x2A, 2, 0);
  518. cmd_buff[1] = RESULT_SUCCESS.raw;
  519. cmd_buff[2] = 0;
  520. LOG_WARNING(Service_Y2R, "(STUBBED) called");
  521. }
  522. static void DriverInitialize(Interface* self) {
  523. u32* cmd_buff = Kernel::GetCommandBuffer();
  524. conversion.input_format = InputFormat::YUV422_Indiv8;
  525. conversion.output_format = OutputFormat::RGBA8;
  526. conversion.rotation = Rotation::None;
  527. conversion.block_alignment = BlockAlignment::Linear;
  528. conversion.coefficients.fill(0);
  529. conversion.SetInputLineWidth(1024);
  530. conversion.SetInputLines(1024);
  531. conversion.alpha = 0;
  532. ConversionBuffer zero_buffer = {};
  533. conversion.src_Y = zero_buffer;
  534. conversion.src_U = zero_buffer;
  535. conversion.src_V = zero_buffer;
  536. conversion.dst = zero_buffer;
  537. completion_event->Clear();
  538. cmd_buff[0] = IPC::MakeHeader(0x2B, 1, 0);
  539. cmd_buff[1] = RESULT_SUCCESS.raw;
  540. LOG_DEBUG(Service_Y2R, "called");
  541. }
  542. static void DriverFinalize(Interface* self) {
  543. u32* cmd_buff = Kernel::GetCommandBuffer();
  544. cmd_buff[0] = IPC::MakeHeader(0x2C, 1, 0);
  545. cmd_buff[1] = RESULT_SUCCESS.raw;
  546. LOG_DEBUG(Service_Y2R, "called");
  547. }
  548. static void GetPackageParameter(Interface* self) {
  549. u32* cmd_buff = Kernel::GetCommandBuffer();
  550. cmd_buff[0] = IPC::MakeHeader(0x2D, 4, 0);
  551. cmd_buff[1] = RESULT_SUCCESS.raw;
  552. std::memcpy(&cmd_buff[2], &conversion, sizeof(ConversionParameters));
  553. LOG_DEBUG(Service_Y2R, "called");
  554. }
  555. const Interface::FunctionInfo FunctionTable[] = {
  556. {0x00010040, SetInputFormat, "SetInputFormat"},
  557. {0x00020000, GetInputFormat, "GetInputFormat"},
  558. {0x00030040, SetOutputFormat, "SetOutputFormat"},
  559. {0x00040000, GetOutputFormat, "GetOutputFormat"},
  560. {0x00050040, SetRotation, "SetRotation"},
  561. {0x00060000, GetRotation, "GetRotation"},
  562. {0x00070040, SetBlockAlignment, "SetBlockAlignment"},
  563. {0x00080000, GetBlockAlignment, "GetBlockAlignment"},
  564. {0x00090040, SetSpacialDithering, "SetSpacialDithering"},
  565. {0x000A0000, GetSpacialDithering, "GetSpacialDithering"},
  566. {0x000B0040, SetTemporalDithering, "SetTemporalDithering"},
  567. {0x000C0000, GetTemporalDithering, "GetTemporalDithering"},
  568. {0x000D0040, SetTransferEndInterrupt, "SetTransferEndInterrupt"},
  569. {0x000E0000, GetTransferEndInterrupt, "GetTransferEndInterrupt"},
  570. {0x000F0000, GetTransferEndEvent, "GetTransferEndEvent"},
  571. {0x00100102, SetSendingY, "SetSendingY"},
  572. {0x00110102, SetSendingU, "SetSendingU"},
  573. {0x00120102, SetSendingV, "SetSendingV"},
  574. {0x00130102, SetSendingYUYV, "SetSendingYUYV"},
  575. {0x00140000, IsFinishedSendingYuv, "IsFinishedSendingYuv"},
  576. {0x00150000, IsFinishedSendingY, "IsFinishedSendingY"},
  577. {0x00160000, IsFinishedSendingU, "IsFinishedSendingU"},
  578. {0x00170000, IsFinishedSendingV, "IsFinishedSendingV"},
  579. {0x00180102, SetReceiving, "SetReceiving"},
  580. {0x00190000, IsFinishedReceiving, "IsFinishedReceiving"},
  581. {0x001A0040, SetInputLineWidth, "SetInputLineWidth"},
  582. {0x001B0000, GetInputLineWidth, "GetInputLineWidth"},
  583. {0x001C0040, SetInputLines, "SetInputLines"},
  584. {0x001D0000, GetInputLines, "GetInputLines"},
  585. {0x001E0100, SetCoefficient, "SetCoefficient"},
  586. {0x001F0000, GetCoefficient, "GetCoefficient"},
  587. {0x00200040, SetStandardCoefficient, "SetStandardCoefficient"},
  588. {0x00210040, GetStandardCoefficient, "GetStandardCoefficient"},
  589. {0x00220040, SetAlpha, "SetAlpha"},
  590. {0x00230000, GetAlpha, "GetAlpha"},
  591. {0x00240200, SetDitheringWeightParams, "SetDitheringWeightParams"},
  592. {0x00250000, GetDitheringWeightParams, "GetDitheringWeightParams"},
  593. {0x00260000, StartConversion, "StartConversion"},
  594. {0x00270000, StopConversion, "StopConversion"},
  595. {0x00280000, IsBusyConversion, "IsBusyConversion"},
  596. {0x002901C0, SetPackageParameter, "SetPackageParameter"},
  597. {0x002A0000, PingProcess, "PingProcess"},
  598. {0x002B0000, DriverInitialize, "DriverInitialize"},
  599. {0x002C0000, DriverFinalize, "DriverFinalize"},
  600. {0x002D0000, GetPackageParameter, "GetPackageParameter"},
  601. };
  602. Y2R_U::Y2R_U() {
  603. completion_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "Y2R:Completed");
  604. std::memset(&conversion, 0, sizeof(conversion));
  605. Register(FunctionTable);
  606. }
  607. Y2R_U::~Y2R_U() {
  608. completion_event = nullptr;
  609. }
  610. } // namespace Y2R
  611. } // namespace Service