drm_framebuffer_test.c 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Test cases for the drm_framebuffer functions
  4. *
  5. * Copyright (c) 2022 Maíra Canal <mairacanal@riseup.net>
  6. */
  7. #include <kunit/device.h>
  8. #include <kunit/test.h>
  9. #include <drm/drm_device.h>
  10. #include <drm/drm_drv.h>
  11. #include <drm/drm_mode.h>
  12. #include <drm/drm_framebuffer.h>
  13. #include <drm/drm_fourcc.h>
  14. #include <drm/drm_kunit_helpers.h>
  15. #include <drm/drm_print.h>
  16. #include "../drm_crtc_internal.h"
  17. #define MIN_WIDTH 4
  18. #define MAX_WIDTH 4096
  19. #define MIN_HEIGHT 4
  20. #define MAX_HEIGHT 4096
  21. #define DRM_MODE_FB_INVALID BIT(2)
  22. struct drm_framebuffer_test {
  23. int buffer_created;
  24. struct drm_mode_fb_cmd2 cmd;
  25. const char *name;
  26. };
  27. static const struct drm_framebuffer_test drm_framebuffer_create_cases[] = {
  28. { .buffer_created = 1, .name = "ABGR8888 normal sizes",
  29. .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_ABGR8888,
  30. .handles = { 1, 0, 0 }, .pitches = { 4 * 600, 0, 0 },
  31. }
  32. },
  33. { .buffer_created = 1, .name = "ABGR8888 max sizes",
  34. .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
  35. .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 },
  36. }
  37. },
  38. { .buffer_created = 1, .name = "ABGR8888 pitch greater than min required",
  39. .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
  40. .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH + 1, 0, 0 },
  41. }
  42. },
  43. { .buffer_created = 0, .name = "ABGR8888 pitch less than min required",
  44. .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
  45. .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH - 1, 0, 0 },
  46. }
  47. },
  48. { .buffer_created = 0, .name = "ABGR8888 Invalid width",
  49. .cmd = { .width = MAX_WIDTH + 1, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
  50. .handles = { 1, 0, 0 }, .pitches = { 4 * (MAX_WIDTH + 1), 0, 0 },
  51. }
  52. },
  53. { .buffer_created = 0, .name = "ABGR8888 Invalid buffer handle",
  54. .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
  55. .handles = { 0, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 },
  56. }
  57. },
  58. { .buffer_created = 0, .name = "No pixel format",
  59. .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = 0,
  60. .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 },
  61. }
  62. },
  63. { .buffer_created = 0, .name = "ABGR8888 Width 0",
  64. .cmd = { .width = 0, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
  65. .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 },
  66. }
  67. },
  68. { .buffer_created = 0, .name = "ABGR8888 Height 0",
  69. .cmd = { .width = MAX_WIDTH, .height = 0, .pixel_format = DRM_FORMAT_ABGR8888,
  70. .handles = { 1, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 },
  71. }
  72. },
  73. { .buffer_created = 0, .name = "ABGR8888 Out of bound height * pitch combination",
  74. .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
  75. .handles = { 1, 0, 0 }, .offsets = { UINT_MAX - 1, 0, 0 },
  76. .pitches = { 4 * MAX_WIDTH, 0, 0 },
  77. }
  78. },
  79. { .buffer_created = 1, .name = "ABGR8888 Large buffer offset",
  80. .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
  81. .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 },
  82. .pitches = { 4 * MAX_WIDTH, 0, 0 },
  83. }
  84. },
  85. /*
  86. * All entries in members that represents per-plane values (@modifier, @handles,
  87. * @pitches and @offsets) must be zero when unused.
  88. */
  89. { .buffer_created = 0, .name = "ABGR8888 Buffer offset for inexistent plane",
  90. .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
  91. .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, UINT_MAX / 2, 0 },
  92. .pitches = { 4 * MAX_WIDTH, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS,
  93. }
  94. },
  95. { .buffer_created = 0, .name = "ABGR8888 Invalid flag",
  96. .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
  97. .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 },
  98. .pitches = { 4 * MAX_WIDTH, 0, 0 }, .flags = DRM_MODE_FB_INVALID,
  99. }
  100. },
  101. { .buffer_created = 1, .name = "ABGR8888 Set DRM_MODE_FB_MODIFIERS without modifiers",
  102. .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
  103. .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 },
  104. .pitches = { 4 * MAX_WIDTH, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS,
  105. }
  106. },
  107. { .buffer_created = 1, .name = "ABGR8888 Valid buffer modifier",
  108. .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
  109. .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 },
  110. .pitches = { 4 * MAX_WIDTH, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS,
  111. .modifier = { AFBC_FORMAT_MOD_YTR, 0, 0 },
  112. }
  113. },
  114. { .buffer_created = 0,
  115. .name = "ABGR8888 Invalid buffer modifier(DRM_FORMAT_MOD_SAMSUNG_64_32_TILE)",
  116. .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
  117. .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 },
  118. .pitches = { 4 * MAX_WIDTH, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS,
  119. .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0, 0 },
  120. }
  121. },
  122. { .buffer_created = 1, .name = "ABGR8888 Extra pitches without DRM_MODE_FB_MODIFIERS",
  123. .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
  124. .handles = { 1, 0, 0 }, .offsets = { UINT_MAX / 2, 0, 0 },
  125. .pitches = { 4 * MAX_WIDTH, 4 * MAX_WIDTH, 0 },
  126. }
  127. },
  128. { .buffer_created = 0, .name = "ABGR8888 Extra pitches with DRM_MODE_FB_MODIFIERS",
  129. .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_ABGR8888,
  130. .handles = { 1, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS,
  131. .pitches = { 4 * MAX_WIDTH, 4 * MAX_WIDTH, 0 },
  132. }
  133. },
  134. { .buffer_created = 1, .name = "NV12 Normal sizes",
  135. .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_NV12,
  136. .handles = { 1, 1, 0 }, .pitches = { 600, 600, 0 },
  137. }
  138. },
  139. { .buffer_created = 1, .name = "NV12 Max sizes",
  140. .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12,
  141. .handles = { 1, 1, 0 }, .pitches = { MAX_WIDTH, MAX_WIDTH, 0 },
  142. }
  143. },
  144. { .buffer_created = 0, .name = "NV12 Invalid pitch",
  145. .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12,
  146. .handles = { 1, 1, 0 }, .pitches = { MAX_WIDTH, MAX_WIDTH - 1, 0 },
  147. }
  148. },
  149. { .buffer_created = 0, .name = "NV12 Invalid modifier/missing DRM_MODE_FB_MODIFIERS flag",
  150. .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12,
  151. .handles = { 1, 1, 0 }, .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0, 0 },
  152. .pitches = { MAX_WIDTH, MAX_WIDTH, 0 },
  153. }
  154. },
  155. { .buffer_created = 0, .name = "NV12 different modifier per-plane",
  156. .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12,
  157. .handles = { 1, 1, 0 }, .flags = DRM_MODE_FB_MODIFIERS,
  158. .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0, 0 },
  159. .pitches = { MAX_WIDTH, MAX_WIDTH, 0 },
  160. }
  161. },
  162. { .buffer_created = 1, .name = "NV12 with DRM_FORMAT_MOD_SAMSUNG_64_32_TILE",
  163. .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12,
  164. .handles = { 1, 1, 0 }, .flags = DRM_MODE_FB_MODIFIERS,
  165. .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE,
  166. DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0 },
  167. .pitches = { MAX_WIDTH, MAX_WIDTH, 0 },
  168. }
  169. },
  170. { .buffer_created = 0, .name = "NV12 Valid modifiers without DRM_MODE_FB_MODIFIERS",
  171. .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12,
  172. .handles = { 1, 1, 0 }, .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE,
  173. DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, 0 },
  174. .pitches = { MAX_WIDTH, MAX_WIDTH, 0 },
  175. }
  176. },
  177. { .buffer_created = 0, .name = "NV12 Modifier for inexistent plane",
  178. .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12,
  179. .handles = { 1, 1, 0 }, .flags = DRM_MODE_FB_MODIFIERS,
  180. .modifier = { DRM_FORMAT_MOD_SAMSUNG_64_32_TILE, DRM_FORMAT_MOD_SAMSUNG_64_32_TILE,
  181. DRM_FORMAT_MOD_SAMSUNG_64_32_TILE },
  182. .pitches = { MAX_WIDTH, MAX_WIDTH, 0 },
  183. }
  184. },
  185. { .buffer_created = 0, .name = "NV12 Handle for inexistent plane",
  186. .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_NV12,
  187. .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS,
  188. .pitches = { MAX_WIDTH, MAX_WIDTH, 0 },
  189. }
  190. },
  191. { .buffer_created = 1, .name = "NV12 Handle for inexistent plane without DRM_MODE_FB_MODIFIERS",
  192. .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_NV12,
  193. .handles = { 1, 1, 1 }, .pitches = { 600, 600, 600 },
  194. }
  195. },
  196. { .buffer_created = 1, .name = "YVU420 DRM_MODE_FB_MODIFIERS set without modifier",
  197. .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_YVU420,
  198. .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS,
  199. .pitches = { 600, 300, 300 },
  200. }
  201. },
  202. { .buffer_created = 1, .name = "YVU420 Normal sizes",
  203. .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_YVU420,
  204. .handles = { 1, 1, 1 }, .pitches = { 600, 300, 300 },
  205. }
  206. },
  207. { .buffer_created = 1, .name = "YVU420 Max sizes",
  208. .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420,
  209. .handles = { 1, 1, 1 }, .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2),
  210. DIV_ROUND_UP(MAX_WIDTH, 2) },
  211. }
  212. },
  213. { .buffer_created = 0, .name = "YVU420 Invalid pitch",
  214. .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420,
  215. .handles = { 1, 1, 1 }, .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2) - 1,
  216. DIV_ROUND_UP(MAX_WIDTH, 2) },
  217. }
  218. },
  219. { .buffer_created = 1, .name = "YVU420 Different pitches",
  220. .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420,
  221. .handles = { 1, 1, 1 }, .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2) + 1,
  222. DIV_ROUND_UP(MAX_WIDTH, 2) + 7 },
  223. }
  224. },
  225. { .buffer_created = 1, .name = "YVU420 Different buffer offsets/pitches",
  226. .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420,
  227. .handles = { 1, 1, 1 }, .offsets = { MAX_WIDTH, MAX_WIDTH +
  228. MAX_WIDTH * MAX_HEIGHT, MAX_WIDTH + 2 * MAX_WIDTH * MAX_HEIGHT },
  229. .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2) + 1,
  230. DIV_ROUND_UP(MAX_WIDTH, 2) + 7 },
  231. }
  232. },
  233. { .buffer_created = 0,
  234. .name = "YVU420 Modifier set just for plane 0, without DRM_MODE_FB_MODIFIERS",
  235. .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420,
  236. .handles = { 1, 1, 1 }, .modifier = { AFBC_FORMAT_MOD_SPARSE, 0, 0 },
  237. .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) },
  238. }
  239. },
  240. { .buffer_created = 0,
  241. .name = "YVU420 Modifier set just for planes 0, 1, without DRM_MODE_FB_MODIFIERS",
  242. .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420,
  243. .handles = { 1, 1, 1 },
  244. .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, 0 },
  245. .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) },
  246. }
  247. },
  248. { .buffer_created = 0,
  249. .name = "YVU420 Modifier set just for plane 0, 1, with DRM_MODE_FB_MODIFIERS",
  250. .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420,
  251. .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS,
  252. .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, 0 },
  253. .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) },
  254. }
  255. },
  256. { .buffer_created = 1, .name = "YVU420 Valid modifier",
  257. .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420,
  258. .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS,
  259. .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE,
  260. AFBC_FORMAT_MOD_SPARSE },
  261. .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) },
  262. }
  263. },
  264. { .buffer_created = 0, .name = "YVU420 Different modifiers per plane",
  265. .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420,
  266. .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS,
  267. .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE | AFBC_FORMAT_MOD_YTR,
  268. AFBC_FORMAT_MOD_SPARSE },
  269. .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) },
  270. }
  271. },
  272. { .buffer_created = 0, .name = "YVU420 Modifier for inexistent plane",
  273. .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YVU420,
  274. .handles = { 1, 1, 1 }, .flags = DRM_MODE_FB_MODIFIERS,
  275. .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE,
  276. AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE },
  277. .pitches = { MAX_WIDTH, DIV_ROUND_UP(MAX_WIDTH, 2), DIV_ROUND_UP(MAX_WIDTH, 2) },
  278. }
  279. },
  280. { .buffer_created = 0, .name = "YUV420_10BIT Invalid modifier(DRM_FORMAT_MOD_LINEAR)",
  281. .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_YUV420_10BIT,
  282. .handles = { 1, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS,
  283. .modifier = { DRM_FORMAT_MOD_LINEAR, 0, 0 },
  284. .pitches = { MAX_WIDTH, 0, 0 },
  285. }
  286. },
  287. { .buffer_created = 1, .name = "X0L2 Normal sizes",
  288. .cmd = { .width = 600, .height = 600, .pixel_format = DRM_FORMAT_X0L2,
  289. .handles = { 1, 0, 0 }, .pitches = { 1200, 0, 0 }
  290. }
  291. },
  292. { .buffer_created = 1, .name = "X0L2 Max sizes",
  293. .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2,
  294. .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH, 0, 0 }
  295. }
  296. },
  297. { .buffer_created = 0, .name = "X0L2 Invalid pitch",
  298. .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2,
  299. .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH - 1, 0, 0 }
  300. }
  301. },
  302. { .buffer_created = 1, .name = "X0L2 Pitch greater than minimum required",
  303. .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2,
  304. .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH + 1, 0, 0 }
  305. }
  306. },
  307. { .buffer_created = 0, .name = "X0L2 Handle for inexistent plane",
  308. .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2,
  309. .handles = { 1, 1, 0 }, .flags = DRM_MODE_FB_MODIFIERS,
  310. .pitches = { 2 * MAX_WIDTH + 1, 0, 0 }
  311. }
  312. },
  313. { .buffer_created = 1,
  314. .name = "X0L2 Offset for inexistent plane, without DRM_MODE_FB_MODIFIERS set",
  315. .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2,
  316. .handles = { 1, 0, 0 }, .offsets = { 0, 0, 3 },
  317. .pitches = { 2 * MAX_WIDTH + 1, 0, 0 }
  318. }
  319. },
  320. { .buffer_created = 0, .name = "X0L2 Modifier without DRM_MODE_FB_MODIFIERS set",
  321. .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2,
  322. .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH + 1, 0, 0 },
  323. .modifier = { AFBC_FORMAT_MOD_SPARSE, 0, 0 },
  324. }
  325. },
  326. { .buffer_created = 1, .name = "X0L2 Valid modifier",
  327. .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT, .pixel_format = DRM_FORMAT_X0L2,
  328. .handles = { 1, 0, 0 }, .pitches = { 2 * MAX_WIDTH + 1, 0, 0 },
  329. .modifier = { AFBC_FORMAT_MOD_SPARSE, 0, 0 }, .flags = DRM_MODE_FB_MODIFIERS,
  330. }
  331. },
  332. { .buffer_created = 0, .name = "X0L2 Modifier for inexistent plane",
  333. .cmd = { .width = MAX_WIDTH, .height = MAX_HEIGHT,
  334. .pixel_format = DRM_FORMAT_X0L2, .handles = { 1, 0, 0 },
  335. .pitches = { 2 * MAX_WIDTH + 1, 0, 0 },
  336. .modifier = { AFBC_FORMAT_MOD_SPARSE, AFBC_FORMAT_MOD_SPARSE, 0 },
  337. .flags = DRM_MODE_FB_MODIFIERS,
  338. }
  339. },
  340. };
  341. /*
  342. * This struct is intended to provide a way to mocked functions communicate
  343. * with the outer test when it can't be achieved by using its return value. In
  344. * this way, the functions that receive the mocked drm_device, for example, can
  345. * grab a reference to this and actually return something to be used on some
  346. * expectation.
  347. */
  348. struct drm_framebuffer_test_priv {
  349. struct drm_device dev;
  350. bool buffer_created;
  351. bool buffer_freed;
  352. };
  353. static struct drm_framebuffer *fb_create_mock(struct drm_device *dev,
  354. struct drm_file *file_priv,
  355. const struct drm_format_info *info,
  356. const struct drm_mode_fb_cmd2 *mode_cmd)
  357. {
  358. struct drm_framebuffer_test_priv *priv = container_of(dev, typeof(*priv), dev);
  359. priv->buffer_created = true;
  360. return ERR_PTR(-EINVAL);
  361. }
  362. static struct drm_mode_config_funcs mock_config_funcs = {
  363. .fb_create = fb_create_mock,
  364. };
  365. static int drm_framebuffer_test_init(struct kunit *test)
  366. {
  367. struct device *parent;
  368. struct drm_framebuffer_test_priv *priv;
  369. struct drm_device *dev;
  370. parent = drm_kunit_helper_alloc_device(test);
  371. KUNIT_ASSERT_NOT_ERR_OR_NULL(test, parent);
  372. priv = drm_kunit_helper_alloc_drm_device(test, parent, typeof(*priv),
  373. dev, 0);
  374. KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv);
  375. dev = &priv->dev;
  376. dev->mode_config.min_width = MIN_WIDTH;
  377. dev->mode_config.max_width = MAX_WIDTH;
  378. dev->mode_config.min_height = MIN_HEIGHT;
  379. dev->mode_config.max_height = MAX_HEIGHT;
  380. dev->mode_config.funcs = &mock_config_funcs;
  381. test->priv = priv;
  382. return 0;
  383. }
  384. static void drm_test_framebuffer_create(struct kunit *test)
  385. {
  386. const struct drm_framebuffer_test *params = test->param_value;
  387. struct drm_framebuffer_test_priv *priv = test->priv;
  388. struct drm_device *dev = &priv->dev;
  389. priv->buffer_created = false;
  390. drm_internal_framebuffer_create(dev, &params->cmd, NULL);
  391. KUNIT_EXPECT_EQ(test, params->buffer_created, priv->buffer_created);
  392. }
  393. static void drm_framebuffer_test_to_desc(const struct drm_framebuffer_test *t, char *desc)
  394. {
  395. strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE);
  396. }
  397. KUNIT_ARRAY_PARAM(drm_framebuffer_create, drm_framebuffer_create_cases,
  398. drm_framebuffer_test_to_desc);
  399. /* Tries to create a framebuffer with modifiers without drm_device supporting it */
  400. static void drm_test_framebuffer_modifiers_not_supported(struct kunit *test)
  401. {
  402. struct drm_framebuffer_test_priv *priv = test->priv;
  403. struct drm_device *dev = &priv->dev;
  404. struct drm_framebuffer *fb;
  405. /* A valid cmd with modifier */
  406. struct drm_mode_fb_cmd2 cmd = {
  407. .width = MAX_WIDTH, .height = MAX_HEIGHT,
  408. .pixel_format = DRM_FORMAT_ABGR8888, .handles = { 1, 0, 0 },
  409. .offsets = { UINT_MAX / 2, 0, 0 }, .pitches = { 4 * MAX_WIDTH, 0, 0 },
  410. .flags = DRM_MODE_FB_MODIFIERS,
  411. };
  412. priv->buffer_created = false;
  413. dev->mode_config.fb_modifiers_not_supported = 1;
  414. fb = drm_internal_framebuffer_create(dev, &cmd, NULL);
  415. KUNIT_EXPECT_EQ(test, priv->buffer_created, false);
  416. KUNIT_EXPECT_EQ(test, PTR_ERR(fb), -EINVAL);
  417. }
  418. /* Parameters for testing drm_framebuffer_check_src_coords function */
  419. struct drm_framebuffer_check_src_coords_case {
  420. const char *name;
  421. const int expect;
  422. const unsigned int fb_size;
  423. const uint32_t src_x;
  424. const uint32_t src_y;
  425. /* Deltas to be applied on source */
  426. const uint32_t dsrc_w;
  427. const uint32_t dsrc_h;
  428. };
  429. static const struct drm_framebuffer_check_src_coords_case
  430. drm_framebuffer_check_src_coords_cases[] = {
  431. { .name = "Success: source fits into fb",
  432. .expect = 0,
  433. },
  434. { .name = "Fail: overflowing fb with x-axis coordinate",
  435. .expect = -ENOSPC, .src_x = 1, .fb_size = UINT_MAX,
  436. },
  437. { .name = "Fail: overflowing fb with y-axis coordinate",
  438. .expect = -ENOSPC, .src_y = 1, .fb_size = UINT_MAX,
  439. },
  440. { .name = "Fail: overflowing fb with source width",
  441. .expect = -ENOSPC, .dsrc_w = 1, .fb_size = UINT_MAX - 1,
  442. },
  443. { .name = "Fail: overflowing fb with source height",
  444. .expect = -ENOSPC, .dsrc_h = 1, .fb_size = UINT_MAX - 1,
  445. },
  446. };
  447. static void drm_test_framebuffer_check_src_coords(struct kunit *test)
  448. {
  449. const struct drm_framebuffer_check_src_coords_case *params = test->param_value;
  450. const uint32_t src_x = params->src_x;
  451. const uint32_t src_y = params->src_y;
  452. const uint32_t src_w = (params->fb_size << 16) + params->dsrc_w;
  453. const uint32_t src_h = (params->fb_size << 16) + params->dsrc_h;
  454. const struct drm_framebuffer fb = {
  455. .width = params->fb_size,
  456. .height = params->fb_size
  457. };
  458. int ret;
  459. ret = drm_framebuffer_check_src_coords(src_x, src_y, src_w, src_h, &fb);
  460. KUNIT_EXPECT_EQ(test, ret, params->expect);
  461. }
  462. static void
  463. check_src_coords_test_to_desc(const struct drm_framebuffer_check_src_coords_case *t,
  464. char *desc)
  465. {
  466. strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE);
  467. }
  468. KUNIT_ARRAY_PARAM(check_src_coords, drm_framebuffer_check_src_coords_cases,
  469. check_src_coords_test_to_desc);
  470. /*
  471. * Test if drm_framebuffer_cleanup() really pops out the framebuffer object
  472. * from device's fb_list and decrement the number of framebuffers for that
  473. * device, which is the only things it does.
  474. */
  475. static void drm_test_framebuffer_cleanup(struct kunit *test)
  476. {
  477. struct drm_framebuffer_test_priv *priv = test->priv;
  478. struct drm_device *dev = &priv->dev;
  479. struct list_head *fb_list = &dev->mode_config.fb_list;
  480. struct drm_format_info format = { };
  481. struct drm_framebuffer fb1 = { .dev = dev, .format = &format };
  482. struct drm_framebuffer fb2 = { .dev = dev, .format = &format };
  483. /* This will result on [fb_list] -> fb2 -> fb1 */
  484. drm_framebuffer_init(dev, &fb1, NULL);
  485. drm_framebuffer_init(dev, &fb2, NULL);
  486. drm_framebuffer_cleanup(&fb1);
  487. /* Now fb2 is the only one element on fb_list */
  488. KUNIT_ASSERT_TRUE(test, list_is_singular(&fb2.head));
  489. KUNIT_ASSERT_EQ(test, dev->mode_config.num_fb, 1);
  490. drm_framebuffer_cleanup(&fb2);
  491. /* Now fb_list is empty */
  492. KUNIT_ASSERT_TRUE(test, list_empty(fb_list));
  493. KUNIT_ASSERT_EQ(test, dev->mode_config.num_fb, 0);
  494. }
  495. /*
  496. * Initialize a framebuffer, lookup its id and test if the returned reference
  497. * matches.
  498. */
  499. static void drm_test_framebuffer_lookup(struct kunit *test)
  500. {
  501. struct drm_framebuffer_test_priv *priv = test->priv;
  502. struct drm_device *dev = &priv->dev;
  503. struct drm_format_info format = { };
  504. struct drm_framebuffer expected_fb = { .dev = dev, .format = &format };
  505. struct drm_framebuffer *returned_fb;
  506. uint32_t id = 0;
  507. int ret;
  508. ret = drm_framebuffer_init(dev, &expected_fb, NULL);
  509. KUNIT_ASSERT_EQ(test, ret, 0);
  510. id = expected_fb.base.id;
  511. /* Looking for expected_fb */
  512. returned_fb = drm_framebuffer_lookup(dev, NULL, id);
  513. KUNIT_EXPECT_PTR_EQ(test, returned_fb, &expected_fb);
  514. drm_framebuffer_put(returned_fb);
  515. drm_framebuffer_cleanup(&expected_fb);
  516. }
  517. /* Try to lookup an id that is not linked to a framebuffer */
  518. static void drm_test_framebuffer_lookup_inexistent(struct kunit *test)
  519. {
  520. struct drm_framebuffer_test_priv *priv = test->priv;
  521. struct drm_device *dev = &priv->dev;
  522. struct drm_framebuffer *fb;
  523. uint32_t id = 0;
  524. /* Looking for an inexistent framebuffer */
  525. fb = drm_framebuffer_lookup(dev, NULL, id);
  526. KUNIT_EXPECT_NULL(test, fb);
  527. }
  528. /*
  529. * Test if drm_framebuffer_init initializes the framebuffer successfully,
  530. * asserting that its modeset object struct and its refcount are correctly
  531. * set and that strictly one framebuffer is initialized.
  532. */
  533. static void drm_test_framebuffer_init(struct kunit *test)
  534. {
  535. struct drm_framebuffer_test_priv *priv = test->priv;
  536. struct drm_device *dev = &priv->dev;
  537. struct drm_format_info format = { };
  538. struct drm_framebuffer fb1 = { .dev = dev, .format = &format };
  539. struct drm_framebuffer_funcs funcs = { };
  540. int ret;
  541. ret = drm_framebuffer_init(dev, &fb1, &funcs);
  542. KUNIT_ASSERT_EQ(test, ret, 0);
  543. /* Check if fb->funcs is actually set to the drm_framebuffer_funcs passed on */
  544. KUNIT_EXPECT_PTR_EQ(test, fb1.funcs, &funcs);
  545. /* The fb->comm must be set to the current running process */
  546. KUNIT_EXPECT_STREQ(test, fb1.comm, current->comm);
  547. /* The fb->base must be successfully initialized */
  548. KUNIT_EXPECT_NE(test, fb1.base.id, 0);
  549. KUNIT_EXPECT_EQ(test, fb1.base.type, DRM_MODE_OBJECT_FB);
  550. KUNIT_EXPECT_EQ(test, kref_read(&fb1.base.refcount), 1);
  551. KUNIT_EXPECT_PTR_EQ(test, fb1.base.free_cb, &drm_framebuffer_free);
  552. /* There must be just that one fb initialized */
  553. KUNIT_EXPECT_EQ(test, dev->mode_config.num_fb, 1);
  554. KUNIT_EXPECT_PTR_EQ(test, dev->mode_config.fb_list.prev, &fb1.head);
  555. KUNIT_EXPECT_PTR_EQ(test, dev->mode_config.fb_list.next, &fb1.head);
  556. drm_framebuffer_cleanup(&fb1);
  557. }
  558. /* Try to init a framebuffer without setting its format */
  559. static void drm_test_framebuffer_init_bad_format(struct kunit *test)
  560. {
  561. struct drm_framebuffer_test_priv *priv = test->priv;
  562. struct drm_device *dev = &priv->dev;
  563. struct drm_framebuffer fb1 = { .dev = dev, .format = NULL };
  564. struct drm_framebuffer_funcs funcs = { };
  565. int ret;
  566. /* Fails if fb.format isn't set */
  567. ret = drm_framebuffer_init(dev, &fb1, &funcs);
  568. KUNIT_EXPECT_EQ(test, ret, -EINVAL);
  569. }
  570. /*
  571. * Test calling drm_framebuffer_init() passing a framebuffer linked to a
  572. * different drm_device parent from the one passed on the first argument, which
  573. * must fail.
  574. */
  575. static void drm_test_framebuffer_init_dev_mismatch(struct kunit *test)
  576. {
  577. struct drm_framebuffer_test_priv *priv = test->priv;
  578. struct drm_device *right_dev = &priv->dev;
  579. struct drm_device *wrong_dev;
  580. struct device *wrong_dev_parent;
  581. struct drm_format_info format = { };
  582. struct drm_framebuffer fb1 = { .dev = right_dev, .format = &format };
  583. struct drm_framebuffer_funcs funcs = { };
  584. int ret;
  585. wrong_dev_parent = kunit_device_register(test, "drm-kunit-wrong-device-mock");
  586. KUNIT_ASSERT_NOT_ERR_OR_NULL(test, wrong_dev_parent);
  587. wrong_dev = __drm_kunit_helper_alloc_drm_device(test, wrong_dev_parent,
  588. sizeof(struct drm_device),
  589. 0, 0);
  590. KUNIT_ASSERT_NOT_ERR_OR_NULL(test, wrong_dev);
  591. /* Fails if fb->dev doesn't point to the drm_device passed on first arg */
  592. ret = drm_framebuffer_init(wrong_dev, &fb1, &funcs);
  593. KUNIT_EXPECT_EQ(test, ret, -EINVAL);
  594. }
  595. static void destroy_free_mock(struct drm_framebuffer *fb)
  596. {
  597. struct drm_framebuffer_test_priv *priv = container_of(fb->dev, typeof(*priv), dev);
  598. priv->buffer_freed = true;
  599. }
  600. static struct drm_framebuffer_funcs framebuffer_funcs_free_mock = {
  601. .destroy = destroy_free_mock,
  602. };
  603. /*
  604. * In summary, the drm_framebuffer_free() function must implicitly call
  605. * fb->funcs->destroy() and garantee that the framebufer object is unregistered
  606. * from the drm_device idr pool.
  607. */
  608. static void drm_test_framebuffer_free(struct kunit *test)
  609. {
  610. struct drm_framebuffer_test_priv *priv = test->priv;
  611. struct drm_device *dev = &priv->dev;
  612. struct drm_mode_object *obj;
  613. struct drm_framebuffer fb = {
  614. .dev = dev,
  615. .funcs = &framebuffer_funcs_free_mock,
  616. };
  617. int id, ret;
  618. priv->buffer_freed = false;
  619. /*
  620. * Mock a framebuffer that was not unregistered at the moment of the
  621. * drm_framebuffer_free() call.
  622. */
  623. ret = drm_mode_object_add(dev, &fb.base, DRM_MODE_OBJECT_FB);
  624. KUNIT_ASSERT_EQ(test, ret, 0);
  625. id = fb.base.id;
  626. drm_framebuffer_free(&fb.base.refcount);
  627. /* The framebuffer object must be unregistered */
  628. obj = drm_mode_object_find(dev, NULL, id, DRM_MODE_OBJECT_FB);
  629. KUNIT_EXPECT_PTR_EQ(test, obj, NULL);
  630. KUNIT_EXPECT_EQ(test, fb.base.id, 0);
  631. /* Test if fb->funcs->destroy() was called */
  632. KUNIT_EXPECT_EQ(test, priv->buffer_freed, true);
  633. }
  634. static struct kunit_case drm_framebuffer_tests[] = {
  635. KUNIT_CASE_PARAM(drm_test_framebuffer_check_src_coords, check_src_coords_gen_params),
  636. KUNIT_CASE(drm_test_framebuffer_cleanup),
  637. KUNIT_CASE_PARAM(drm_test_framebuffer_create, drm_framebuffer_create_gen_params),
  638. KUNIT_CASE(drm_test_framebuffer_free),
  639. KUNIT_CASE(drm_test_framebuffer_init),
  640. KUNIT_CASE(drm_test_framebuffer_init_bad_format),
  641. KUNIT_CASE(drm_test_framebuffer_init_dev_mismatch),
  642. KUNIT_CASE(drm_test_framebuffer_lookup),
  643. KUNIT_CASE(drm_test_framebuffer_lookup_inexistent),
  644. KUNIT_CASE(drm_test_framebuffer_modifiers_not_supported),
  645. { }
  646. };
  647. static struct kunit_suite drm_framebuffer_test_suite = {
  648. .name = "drm_framebuffer",
  649. .init = drm_framebuffer_test_init,
  650. .test_cases = drm_framebuffer_tests,
  651. };
  652. kunit_test_suite(drm_framebuffer_test_suite);
  653. MODULE_DESCRIPTION("Test cases for the drm_framebuffer functions");
  654. MODULE_LICENSE("GPL");