vkms_config.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649
  1. // SPDX-License-Identifier: GPL-2.0+
  2. #include <linux/slab.h>
  3. #include <drm/drm_print.h>
  4. #include <drm/drm_debugfs.h>
  5. #include <kunit/visibility.h>
  6. #include "vkms_config.h"
  7. struct vkms_config *vkms_config_create(const char *dev_name)
  8. {
  9. struct vkms_config *config;
  10. config = kzalloc_obj(*config);
  11. if (!config)
  12. return ERR_PTR(-ENOMEM);
  13. config->dev_name = kstrdup_const(dev_name, GFP_KERNEL);
  14. if (!config->dev_name) {
  15. kfree(config);
  16. return ERR_PTR(-ENOMEM);
  17. }
  18. INIT_LIST_HEAD(&config->planes);
  19. INIT_LIST_HEAD(&config->crtcs);
  20. INIT_LIST_HEAD(&config->encoders);
  21. INIT_LIST_HEAD(&config->connectors);
  22. return config;
  23. }
  24. EXPORT_SYMBOL_IF_KUNIT(vkms_config_create);
  25. struct vkms_config *vkms_config_default_create(bool enable_cursor,
  26. bool enable_writeback,
  27. bool enable_overlay,
  28. bool enable_plane_pipeline)
  29. {
  30. struct vkms_config *config;
  31. struct vkms_config_plane *plane_cfg;
  32. struct vkms_config_crtc *crtc_cfg;
  33. struct vkms_config_encoder *encoder_cfg;
  34. struct vkms_config_connector *connector_cfg;
  35. int n;
  36. config = vkms_config_create(DEFAULT_DEVICE_NAME);
  37. if (IS_ERR(config))
  38. return config;
  39. plane_cfg = vkms_config_create_plane(config);
  40. if (IS_ERR(plane_cfg))
  41. goto err_alloc;
  42. vkms_config_plane_set_type(plane_cfg, DRM_PLANE_TYPE_PRIMARY);
  43. crtc_cfg = vkms_config_create_crtc(config);
  44. if (IS_ERR(crtc_cfg))
  45. goto err_alloc;
  46. vkms_config_crtc_set_writeback(crtc_cfg, enable_writeback);
  47. if (vkms_config_plane_attach_crtc(plane_cfg, crtc_cfg))
  48. goto err_alloc;
  49. vkms_config_plane_set_default_pipeline(plane_cfg, enable_plane_pipeline);
  50. if (enable_overlay) {
  51. for (n = 0; n < NUM_OVERLAY_PLANES; n++) {
  52. plane_cfg = vkms_config_create_plane(config);
  53. if (IS_ERR(plane_cfg))
  54. goto err_alloc;
  55. vkms_config_plane_set_type(plane_cfg,
  56. DRM_PLANE_TYPE_OVERLAY);
  57. vkms_config_plane_set_default_pipeline(plane_cfg, enable_plane_pipeline);
  58. if (vkms_config_plane_attach_crtc(plane_cfg, crtc_cfg))
  59. goto err_alloc;
  60. }
  61. }
  62. if (enable_cursor) {
  63. plane_cfg = vkms_config_create_plane(config);
  64. if (IS_ERR(plane_cfg))
  65. goto err_alloc;
  66. vkms_config_plane_set_type(plane_cfg, DRM_PLANE_TYPE_CURSOR);
  67. vkms_config_plane_set_default_pipeline(plane_cfg, enable_plane_pipeline);
  68. if (vkms_config_plane_attach_crtc(plane_cfg, crtc_cfg))
  69. goto err_alloc;
  70. }
  71. encoder_cfg = vkms_config_create_encoder(config);
  72. if (IS_ERR(encoder_cfg))
  73. goto err_alloc;
  74. if (vkms_config_encoder_attach_crtc(encoder_cfg, crtc_cfg))
  75. goto err_alloc;
  76. connector_cfg = vkms_config_create_connector(config);
  77. if (IS_ERR(connector_cfg))
  78. goto err_alloc;
  79. if (vkms_config_connector_attach_encoder(connector_cfg, encoder_cfg))
  80. goto err_alloc;
  81. return config;
  82. err_alloc:
  83. vkms_config_destroy(config);
  84. return ERR_PTR(-ENOMEM);
  85. }
  86. EXPORT_SYMBOL_IF_KUNIT(vkms_config_default_create);
  87. void vkms_config_destroy(struct vkms_config *config)
  88. {
  89. struct vkms_config_plane *plane_cfg, *plane_tmp;
  90. struct vkms_config_crtc *crtc_cfg, *crtc_tmp;
  91. struct vkms_config_encoder *encoder_cfg, *encoder_tmp;
  92. struct vkms_config_connector *connector_cfg, *connector_tmp;
  93. list_for_each_entry_safe(plane_cfg, plane_tmp, &config->planes, link)
  94. vkms_config_destroy_plane(plane_cfg);
  95. list_for_each_entry_safe(crtc_cfg, crtc_tmp, &config->crtcs, link)
  96. vkms_config_destroy_crtc(config, crtc_cfg);
  97. list_for_each_entry_safe(encoder_cfg, encoder_tmp, &config->encoders, link)
  98. vkms_config_destroy_encoder(config, encoder_cfg);
  99. list_for_each_entry_safe(connector_cfg, connector_tmp, &config->connectors, link)
  100. vkms_config_destroy_connector(connector_cfg);
  101. kfree_const(config->dev_name);
  102. kfree(config);
  103. }
  104. EXPORT_SYMBOL_IF_KUNIT(vkms_config_destroy);
  105. static bool valid_plane_number(const struct vkms_config *config)
  106. {
  107. struct drm_device *dev = config->dev ? &config->dev->drm : NULL;
  108. size_t n_planes;
  109. n_planes = list_count_nodes((struct list_head *)&config->planes);
  110. if (n_planes <= 0 || n_planes >= 32) {
  111. drm_info(dev, "The number of planes must be between 1 and 31\n");
  112. return false;
  113. }
  114. return true;
  115. }
  116. static bool valid_planes_for_crtc(const struct vkms_config *config,
  117. struct vkms_config_crtc *crtc_cfg)
  118. {
  119. struct drm_device *dev = config->dev ? &config->dev->drm : NULL;
  120. struct vkms_config_plane *plane_cfg;
  121. bool has_primary_plane = false;
  122. bool has_cursor_plane = false;
  123. vkms_config_for_each_plane(config, plane_cfg) {
  124. struct vkms_config_crtc *possible_crtc;
  125. unsigned long idx = 0;
  126. enum drm_plane_type type;
  127. type = vkms_config_plane_get_type(plane_cfg);
  128. vkms_config_plane_for_each_possible_crtc(plane_cfg, idx, possible_crtc) {
  129. if (possible_crtc != crtc_cfg)
  130. continue;
  131. if (type == DRM_PLANE_TYPE_PRIMARY) {
  132. if (has_primary_plane) {
  133. drm_info(dev, "Multiple primary planes\n");
  134. return false;
  135. }
  136. has_primary_plane = true;
  137. } else if (type == DRM_PLANE_TYPE_CURSOR) {
  138. if (has_cursor_plane) {
  139. drm_info(dev, "Multiple cursor planes\n");
  140. return false;
  141. }
  142. has_cursor_plane = true;
  143. }
  144. }
  145. }
  146. if (!has_primary_plane) {
  147. drm_info(dev, "Primary plane not found\n");
  148. return false;
  149. }
  150. return true;
  151. }
  152. static bool valid_plane_possible_crtcs(const struct vkms_config *config)
  153. {
  154. struct drm_device *dev = config->dev ? &config->dev->drm : NULL;
  155. struct vkms_config_plane *plane_cfg;
  156. vkms_config_for_each_plane(config, plane_cfg) {
  157. if (xa_empty(&plane_cfg->possible_crtcs)) {
  158. drm_info(dev, "All planes must have at least one possible CRTC\n");
  159. return false;
  160. }
  161. }
  162. return true;
  163. }
  164. static bool valid_crtc_number(const struct vkms_config *config)
  165. {
  166. struct drm_device *dev = config->dev ? &config->dev->drm : NULL;
  167. size_t n_crtcs;
  168. n_crtcs = list_count_nodes((struct list_head *)&config->crtcs);
  169. if (n_crtcs <= 0 || n_crtcs >= 32) {
  170. drm_info(dev, "The number of CRTCs must be between 1 and 31\n");
  171. return false;
  172. }
  173. return true;
  174. }
  175. static bool valid_encoder_number(const struct vkms_config *config)
  176. {
  177. struct drm_device *dev = config->dev ? &config->dev->drm : NULL;
  178. size_t n_encoders;
  179. n_encoders = list_count_nodes((struct list_head *)&config->encoders);
  180. if (n_encoders <= 0 || n_encoders >= 32) {
  181. drm_info(dev, "The number of encoders must be between 1 and 31\n");
  182. return false;
  183. }
  184. return true;
  185. }
  186. static bool valid_encoder_possible_crtcs(const struct vkms_config *config)
  187. {
  188. struct drm_device *dev = config->dev ? &config->dev->drm : NULL;
  189. struct vkms_config_crtc *crtc_cfg;
  190. struct vkms_config_encoder *encoder_cfg;
  191. vkms_config_for_each_encoder(config, encoder_cfg) {
  192. if (xa_empty(&encoder_cfg->possible_crtcs)) {
  193. drm_info(dev, "All encoders must have at least one possible CRTC\n");
  194. return false;
  195. }
  196. }
  197. vkms_config_for_each_crtc(config, crtc_cfg) {
  198. bool crtc_has_encoder = false;
  199. vkms_config_for_each_encoder(config, encoder_cfg) {
  200. struct vkms_config_crtc *possible_crtc;
  201. unsigned long idx = 0;
  202. vkms_config_encoder_for_each_possible_crtc(encoder_cfg,
  203. idx, possible_crtc) {
  204. if (possible_crtc == crtc_cfg)
  205. crtc_has_encoder = true;
  206. }
  207. }
  208. if (!crtc_has_encoder) {
  209. drm_info(dev, "All CRTCs must have at least one possible encoder\n");
  210. return false;
  211. }
  212. }
  213. return true;
  214. }
  215. static bool valid_connector_number(const struct vkms_config *config)
  216. {
  217. struct drm_device *dev = config->dev ? &config->dev->drm : NULL;
  218. size_t n_connectors;
  219. n_connectors = list_count_nodes((struct list_head *)&config->connectors);
  220. if (n_connectors <= 0 || n_connectors >= 32) {
  221. drm_info(dev, "The number of connectors must be between 1 and 31\n");
  222. return false;
  223. }
  224. return true;
  225. }
  226. static bool valid_connector_possible_encoders(const struct vkms_config *config)
  227. {
  228. struct drm_device *dev = config->dev ? &config->dev->drm : NULL;
  229. struct vkms_config_connector *connector_cfg;
  230. vkms_config_for_each_connector(config, connector_cfg) {
  231. if (xa_empty(&connector_cfg->possible_encoders)) {
  232. drm_info(dev,
  233. "All connectors must have at least one possible encoder\n");
  234. return false;
  235. }
  236. }
  237. return true;
  238. }
  239. bool vkms_config_is_valid(const struct vkms_config *config)
  240. {
  241. struct vkms_config_crtc *crtc_cfg;
  242. if (!valid_plane_number(config))
  243. return false;
  244. if (!valid_crtc_number(config))
  245. return false;
  246. if (!valid_encoder_number(config))
  247. return false;
  248. if (!valid_connector_number(config))
  249. return false;
  250. if (!valid_plane_possible_crtcs(config))
  251. return false;
  252. vkms_config_for_each_crtc(config, crtc_cfg) {
  253. if (!valid_planes_for_crtc(config, crtc_cfg))
  254. return false;
  255. }
  256. if (!valid_encoder_possible_crtcs(config))
  257. return false;
  258. if (!valid_connector_possible_encoders(config))
  259. return false;
  260. return true;
  261. }
  262. EXPORT_SYMBOL_IF_KUNIT(vkms_config_is_valid);
  263. static int vkms_config_show(struct seq_file *m, void *data)
  264. {
  265. struct drm_debugfs_entry *entry = m->private;
  266. struct drm_device *dev = entry->dev;
  267. struct vkms_device *vkmsdev = drm_device_to_vkms_device(dev);
  268. const char *dev_name;
  269. struct vkms_config_plane *plane_cfg;
  270. struct vkms_config_crtc *crtc_cfg;
  271. struct vkms_config_encoder *encoder_cfg;
  272. struct vkms_config_connector *connector_cfg;
  273. dev_name = vkms_config_get_device_name((struct vkms_config *)vkmsdev->config);
  274. seq_printf(m, "dev_name=%s\n", dev_name);
  275. vkms_config_for_each_plane(vkmsdev->config, plane_cfg) {
  276. seq_puts(m, "plane:\n");
  277. seq_printf(m, "\ttype=%d\n",
  278. vkms_config_plane_get_type(plane_cfg));
  279. }
  280. vkms_config_for_each_crtc(vkmsdev->config, crtc_cfg) {
  281. seq_puts(m, "crtc:\n");
  282. seq_printf(m, "\twriteback=%d\n",
  283. vkms_config_crtc_get_writeback(crtc_cfg));
  284. }
  285. vkms_config_for_each_encoder(vkmsdev->config, encoder_cfg)
  286. seq_puts(m, "encoder\n");
  287. vkms_config_for_each_connector(vkmsdev->config, connector_cfg) {
  288. seq_puts(m, "connector:\n");
  289. seq_printf(m, "\tstatus=%d\n",
  290. vkms_config_connector_get_status(connector_cfg));
  291. }
  292. return 0;
  293. }
  294. static const struct drm_debugfs_info vkms_config_debugfs_list[] = {
  295. { "vkms_config", vkms_config_show, 0 },
  296. };
  297. void vkms_config_register_debugfs(struct vkms_device *vkms_device)
  298. {
  299. drm_debugfs_add_files(&vkms_device->drm, vkms_config_debugfs_list,
  300. ARRAY_SIZE(vkms_config_debugfs_list));
  301. }
  302. struct vkms_config_plane *vkms_config_create_plane(struct vkms_config *config)
  303. {
  304. struct vkms_config_plane *plane_cfg;
  305. plane_cfg = kzalloc_obj(*plane_cfg);
  306. if (!plane_cfg)
  307. return ERR_PTR(-ENOMEM);
  308. plane_cfg->config = config;
  309. plane_cfg->default_pipeline = false;
  310. vkms_config_plane_set_type(plane_cfg, DRM_PLANE_TYPE_OVERLAY);
  311. xa_init_flags(&plane_cfg->possible_crtcs, XA_FLAGS_ALLOC);
  312. list_add_tail(&plane_cfg->link, &config->planes);
  313. return plane_cfg;
  314. }
  315. EXPORT_SYMBOL_IF_KUNIT(vkms_config_create_plane);
  316. void vkms_config_destroy_plane(struct vkms_config_plane *plane_cfg)
  317. {
  318. xa_destroy(&plane_cfg->possible_crtcs);
  319. list_del(&plane_cfg->link);
  320. kfree(plane_cfg);
  321. }
  322. EXPORT_SYMBOL_IF_KUNIT(vkms_config_destroy_plane);
  323. int __must_check vkms_config_plane_attach_crtc(struct vkms_config_plane *plane_cfg,
  324. struct vkms_config_crtc *crtc_cfg)
  325. {
  326. struct vkms_config_crtc *possible_crtc;
  327. unsigned long idx = 0;
  328. u32 crtc_idx = 0;
  329. if (plane_cfg->config != crtc_cfg->config)
  330. return -EINVAL;
  331. vkms_config_plane_for_each_possible_crtc(plane_cfg, idx, possible_crtc) {
  332. if (possible_crtc == crtc_cfg)
  333. return -EEXIST;
  334. }
  335. return xa_alloc(&plane_cfg->possible_crtcs, &crtc_idx, crtc_cfg,
  336. xa_limit_32b, GFP_KERNEL);
  337. }
  338. EXPORT_SYMBOL_IF_KUNIT(vkms_config_plane_attach_crtc);
  339. void vkms_config_plane_detach_crtc(struct vkms_config_plane *plane_cfg,
  340. struct vkms_config_crtc *crtc_cfg)
  341. {
  342. struct vkms_config_crtc *possible_crtc;
  343. unsigned long idx = 0;
  344. vkms_config_plane_for_each_possible_crtc(plane_cfg, idx, possible_crtc) {
  345. if (possible_crtc == crtc_cfg)
  346. xa_erase(&plane_cfg->possible_crtcs, idx);
  347. }
  348. }
  349. EXPORT_SYMBOL_IF_KUNIT(vkms_config_plane_detach_crtc);
  350. struct vkms_config_crtc *vkms_config_create_crtc(struct vkms_config *config)
  351. {
  352. struct vkms_config_crtc *crtc_cfg;
  353. crtc_cfg = kzalloc_obj(*crtc_cfg);
  354. if (!crtc_cfg)
  355. return ERR_PTR(-ENOMEM);
  356. crtc_cfg->config = config;
  357. vkms_config_crtc_set_writeback(crtc_cfg, false);
  358. list_add_tail(&crtc_cfg->link, &config->crtcs);
  359. return crtc_cfg;
  360. }
  361. EXPORT_SYMBOL_IF_KUNIT(vkms_config_create_crtc);
  362. void vkms_config_destroy_crtc(struct vkms_config *config,
  363. struct vkms_config_crtc *crtc_cfg)
  364. {
  365. struct vkms_config_plane *plane_cfg;
  366. struct vkms_config_encoder *encoder_cfg;
  367. vkms_config_for_each_plane(config, plane_cfg)
  368. vkms_config_plane_detach_crtc(plane_cfg, crtc_cfg);
  369. vkms_config_for_each_encoder(config, encoder_cfg)
  370. vkms_config_encoder_detach_crtc(encoder_cfg, crtc_cfg);
  371. list_del(&crtc_cfg->link);
  372. kfree(crtc_cfg);
  373. }
  374. EXPORT_SYMBOL_IF_KUNIT(vkms_config_destroy_crtc);
  375. /**
  376. * vkms_config_crtc_get_plane() - Return the first attached plane to a CRTC with
  377. * the specific type
  378. * @config: Configuration containing the CRTC and the plane
  379. * @crtc_cfg: Only find planes attached to this CRTC
  380. * @type: Plane type to search
  381. *
  382. * Returns:
  383. * The first plane found attached to @crtc_cfg with the type @type.
  384. */
  385. static struct vkms_config_plane *vkms_config_crtc_get_plane(const struct vkms_config *config,
  386. struct vkms_config_crtc *crtc_cfg,
  387. enum drm_plane_type type)
  388. {
  389. struct vkms_config_plane *plane_cfg;
  390. struct vkms_config_crtc *possible_crtc;
  391. enum drm_plane_type current_type;
  392. unsigned long idx = 0;
  393. vkms_config_for_each_plane(config, plane_cfg) {
  394. current_type = vkms_config_plane_get_type(plane_cfg);
  395. vkms_config_plane_for_each_possible_crtc(plane_cfg, idx, possible_crtc) {
  396. if (possible_crtc == crtc_cfg && current_type == type)
  397. return plane_cfg;
  398. }
  399. }
  400. return NULL;
  401. }
  402. struct vkms_config_plane *vkms_config_crtc_primary_plane(const struct vkms_config *config,
  403. struct vkms_config_crtc *crtc_cfg)
  404. {
  405. return vkms_config_crtc_get_plane(config, crtc_cfg, DRM_PLANE_TYPE_PRIMARY);
  406. }
  407. EXPORT_SYMBOL_IF_KUNIT(vkms_config_crtc_primary_plane);
  408. struct vkms_config_plane *vkms_config_crtc_cursor_plane(const struct vkms_config *config,
  409. struct vkms_config_crtc *crtc_cfg)
  410. {
  411. return vkms_config_crtc_get_plane(config, crtc_cfg, DRM_PLANE_TYPE_CURSOR);
  412. }
  413. EXPORT_SYMBOL_IF_KUNIT(vkms_config_crtc_cursor_plane);
  414. struct vkms_config_encoder *vkms_config_create_encoder(struct vkms_config *config)
  415. {
  416. struct vkms_config_encoder *encoder_cfg;
  417. encoder_cfg = kzalloc_obj(*encoder_cfg);
  418. if (!encoder_cfg)
  419. return ERR_PTR(-ENOMEM);
  420. encoder_cfg->config = config;
  421. xa_init_flags(&encoder_cfg->possible_crtcs, XA_FLAGS_ALLOC);
  422. list_add_tail(&encoder_cfg->link, &config->encoders);
  423. return encoder_cfg;
  424. }
  425. EXPORT_SYMBOL_IF_KUNIT(vkms_config_create_encoder);
  426. void vkms_config_destroy_encoder(struct vkms_config *config,
  427. struct vkms_config_encoder *encoder_cfg)
  428. {
  429. struct vkms_config_connector *connector_cfg;
  430. vkms_config_for_each_connector(config, connector_cfg)
  431. vkms_config_connector_detach_encoder(connector_cfg, encoder_cfg);
  432. xa_destroy(&encoder_cfg->possible_crtcs);
  433. list_del(&encoder_cfg->link);
  434. kfree(encoder_cfg);
  435. }
  436. EXPORT_SYMBOL_IF_KUNIT(vkms_config_destroy_encoder);
  437. int __must_check vkms_config_encoder_attach_crtc(struct vkms_config_encoder *encoder_cfg,
  438. struct vkms_config_crtc *crtc_cfg)
  439. {
  440. struct vkms_config_crtc *possible_crtc;
  441. unsigned long idx = 0;
  442. u32 crtc_idx = 0;
  443. if (encoder_cfg->config != crtc_cfg->config)
  444. return -EINVAL;
  445. vkms_config_encoder_for_each_possible_crtc(encoder_cfg, idx, possible_crtc) {
  446. if (possible_crtc == crtc_cfg)
  447. return -EEXIST;
  448. }
  449. return xa_alloc(&encoder_cfg->possible_crtcs, &crtc_idx, crtc_cfg,
  450. xa_limit_32b, GFP_KERNEL);
  451. }
  452. EXPORT_SYMBOL_IF_KUNIT(vkms_config_encoder_attach_crtc);
  453. void vkms_config_encoder_detach_crtc(struct vkms_config_encoder *encoder_cfg,
  454. struct vkms_config_crtc *crtc_cfg)
  455. {
  456. struct vkms_config_crtc *possible_crtc;
  457. unsigned long idx = 0;
  458. vkms_config_encoder_for_each_possible_crtc(encoder_cfg, idx, possible_crtc) {
  459. if (possible_crtc == crtc_cfg)
  460. xa_erase(&encoder_cfg->possible_crtcs, idx);
  461. }
  462. }
  463. EXPORT_SYMBOL_IF_KUNIT(vkms_config_encoder_detach_crtc);
  464. struct vkms_config_connector *vkms_config_create_connector(struct vkms_config *config)
  465. {
  466. struct vkms_config_connector *connector_cfg;
  467. connector_cfg = kzalloc_obj(*connector_cfg);
  468. if (!connector_cfg)
  469. return ERR_PTR(-ENOMEM);
  470. connector_cfg->config = config;
  471. connector_cfg->status = connector_status_connected;
  472. xa_init_flags(&connector_cfg->possible_encoders, XA_FLAGS_ALLOC);
  473. list_add_tail(&connector_cfg->link, &config->connectors);
  474. return connector_cfg;
  475. }
  476. EXPORT_SYMBOL_IF_KUNIT(vkms_config_create_connector);
  477. void vkms_config_destroy_connector(struct vkms_config_connector *connector_cfg)
  478. {
  479. xa_destroy(&connector_cfg->possible_encoders);
  480. list_del(&connector_cfg->link);
  481. kfree(connector_cfg);
  482. }
  483. EXPORT_SYMBOL_IF_KUNIT(vkms_config_destroy_connector);
  484. int __must_check vkms_config_connector_attach_encoder(struct vkms_config_connector *connector_cfg,
  485. struct vkms_config_encoder *encoder_cfg)
  486. {
  487. struct vkms_config_encoder *possible_encoder;
  488. unsigned long idx = 0;
  489. u32 encoder_idx = 0;
  490. if (connector_cfg->config != encoder_cfg->config)
  491. return -EINVAL;
  492. vkms_config_connector_for_each_possible_encoder(connector_cfg, idx,
  493. possible_encoder) {
  494. if (possible_encoder == encoder_cfg)
  495. return -EEXIST;
  496. }
  497. return xa_alloc(&connector_cfg->possible_encoders, &encoder_idx,
  498. encoder_cfg, xa_limit_32b, GFP_KERNEL);
  499. }
  500. EXPORT_SYMBOL_IF_KUNIT(vkms_config_connector_attach_encoder);
  501. void vkms_config_connector_detach_encoder(struct vkms_config_connector *connector_cfg,
  502. struct vkms_config_encoder *encoder_cfg)
  503. {
  504. struct vkms_config_encoder *possible_encoder;
  505. unsigned long idx = 0;
  506. vkms_config_connector_for_each_possible_encoder(connector_cfg, idx,
  507. possible_encoder) {
  508. if (possible_encoder == encoder_cfg)
  509. xa_erase(&connector_cfg->possible_encoders, idx);
  510. }
  511. }
  512. EXPORT_SYMBOL_IF_KUNIT(vkms_config_connector_detach_encoder);