pvr_hwrt.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. // SPDX-License-Identifier: GPL-2.0-only OR MIT
  2. /* Copyright (c) 2023 Imagination Technologies Ltd. */
  3. #include "pvr_free_list.h"
  4. #include "pvr_hwrt.h"
  5. #include "pvr_gem.h"
  6. #include "pvr_rogue_cr_defs_client.h"
  7. #include "pvr_rogue_fwif.h"
  8. #include <drm/drm_gem.h>
  9. #include <linux/bitops.h>
  10. #include <linux/math.h>
  11. #include <linux/slab.h>
  12. #include <linux/xarray.h>
  13. #include <uapi/drm/pvr_drm.h>
  14. static_assert(ROGUE_FWIF_NUM_RTDATAS == 2);
  15. static_assert(ROGUE_FWIF_NUM_GEOMDATAS == 1);
  16. static_assert(ROGUE_FWIF_NUM_RTDATA_FREELISTS == 2);
  17. /*
  18. * struct pvr_rt_mtile_info - Render target macrotile information
  19. */
  20. struct pvr_rt_mtile_info {
  21. u32 mtile_x[3];
  22. u32 mtile_y[3];
  23. u32 tile_max_x;
  24. u32 tile_max_y;
  25. u32 tile_size_x;
  26. u32 tile_size_y;
  27. u32 num_tiles_x;
  28. u32 num_tiles_y;
  29. };
  30. /* Size of Shadow Render Target Cache entry */
  31. #define SRTC_ENTRY_SIZE sizeof(u32)
  32. /* Size of Renders Accumulation Array entry */
  33. #define RAA_ENTRY_SIZE sizeof(u32)
  34. static int
  35. hwrt_init_kernel_structure(struct pvr_file *pvr_file,
  36. struct drm_pvr_ioctl_create_hwrt_dataset_args *args,
  37. struct pvr_hwrt_dataset *hwrt)
  38. {
  39. struct pvr_device *pvr_dev = pvr_file->pvr_dev;
  40. int err;
  41. int i;
  42. hwrt->pvr_dev = pvr_dev;
  43. hwrt->max_rts = args->layers;
  44. /* Get pointers to the free lists */
  45. for (i = 0; i < ARRAY_SIZE(hwrt->free_lists); i++) {
  46. hwrt->free_lists[i] = pvr_free_list_lookup(pvr_file, args->free_list_handles[i]);
  47. if (!hwrt->free_lists[i]) {
  48. err = -EINVAL;
  49. goto err_put_free_lists;
  50. }
  51. }
  52. if (hwrt->free_lists[ROGUE_FW_LOCAL_FREELIST]->current_pages <
  53. pvr_get_free_list_min_pages(pvr_dev)) {
  54. err = -EINVAL;
  55. goto err_put_free_lists;
  56. }
  57. return 0;
  58. err_put_free_lists:
  59. for (i = 0; i < ARRAY_SIZE(hwrt->free_lists); i++) {
  60. pvr_free_list_put(hwrt->free_lists[i]);
  61. hwrt->free_lists[i] = NULL;
  62. }
  63. return err;
  64. }
  65. static void
  66. hwrt_fini_kernel_structure(struct pvr_hwrt_dataset *hwrt)
  67. {
  68. int i;
  69. for (i = 0; i < ARRAY_SIZE(hwrt->free_lists); i++) {
  70. pvr_free_list_put(hwrt->free_lists[i]);
  71. hwrt->free_lists[i] = NULL;
  72. }
  73. }
  74. static void
  75. hwrt_fini_common_fw_structure(struct pvr_hwrt_dataset *hwrt)
  76. {
  77. pvr_fw_object_destroy(hwrt->common_fw_obj);
  78. }
  79. static int
  80. get_cr_isp_mtile_size_val(struct pvr_device *pvr_dev, u32 samples,
  81. struct pvr_rt_mtile_info *info, u32 *value_out)
  82. {
  83. u32 x = info->mtile_x[0];
  84. u32 y = info->mtile_y[0];
  85. u32 samples_per_pixel;
  86. int err;
  87. err = PVR_FEATURE_VALUE(pvr_dev, isp_samples_per_pixel, &samples_per_pixel);
  88. if (err)
  89. return err;
  90. if (samples_per_pixel == 1) {
  91. if (samples >= 4)
  92. x <<= 1;
  93. if (samples >= 2)
  94. y <<= 1;
  95. } else if (samples_per_pixel == 2) {
  96. if (samples >= 8)
  97. x <<= 1;
  98. if (samples >= 4)
  99. y <<= 1;
  100. } else if (samples_per_pixel == 4) {
  101. if (samples >= 8)
  102. y <<= 1;
  103. } else {
  104. WARN(true, "Unsupported ISP samples per pixel value");
  105. return -EINVAL;
  106. }
  107. *value_out = ((x << ROGUE_CR_ISP_MTILE_SIZE_X_SHIFT) & ~ROGUE_CR_ISP_MTILE_SIZE_X_CLRMSK) |
  108. ((y << ROGUE_CR_ISP_MTILE_SIZE_Y_SHIFT) & ~ROGUE_CR_ISP_MTILE_SIZE_Y_CLRMSK);
  109. return 0;
  110. }
  111. static int
  112. get_cr_multisamplectl_val(u32 samples, bool y_flip, u64 *value_out)
  113. {
  114. static const struct {
  115. u8 x[8];
  116. u8 y[8];
  117. } sample_positions[4] = {
  118. /* 1 sample */
  119. {
  120. .x = { 8 },
  121. .y = { 8 },
  122. },
  123. /* 2 samples */
  124. {
  125. .x = { 12, 4 },
  126. .y = { 12, 4 },
  127. },
  128. /* 4 samples */
  129. {
  130. .x = { 6, 14, 2, 10 },
  131. .y = { 2, 6, 10, 14 },
  132. },
  133. /* 8 samples */
  134. {
  135. .x = { 9, 7, 13, 5, 3, 1, 11, 15 },
  136. .y = { 5, 11, 9, 3, 13, 7, 15, 1 },
  137. },
  138. };
  139. const int idx = fls(samples) - 1;
  140. u64 value = 0;
  141. if (idx < 0 || idx > 3)
  142. return -EINVAL;
  143. for (u32 i = 0; i < 8; i++) {
  144. value |= ((u64)sample_positions[idx].x[i]) << (i * 8);
  145. if (y_flip)
  146. value |= (((u64)(16 - sample_positions[idx].y[i]) & 0xf)) << (i * 8 + 4);
  147. else
  148. value |= ((u64)sample_positions[idx].y[i]) << (i * 8 + 4);
  149. }
  150. *value_out = value;
  151. return 0;
  152. }
  153. static int
  154. get_cr_te_aa_val(struct pvr_device *pvr_dev, u32 samples, u32 *value_out)
  155. {
  156. u32 samples_per_pixel;
  157. u32 value = 0;
  158. int err = 0;
  159. err = PVR_FEATURE_VALUE(pvr_dev, isp_samples_per_pixel, &samples_per_pixel);
  160. if (err)
  161. return err;
  162. switch (samples_per_pixel) {
  163. case 1:
  164. if (samples >= 2)
  165. value |= ROGUE_CR_TE_AA_Y_EN;
  166. if (samples >= 4)
  167. value |= ROGUE_CR_TE_AA_X_EN;
  168. break;
  169. case 2:
  170. if (samples >= 2)
  171. value |= ROGUE_CR_TE_AA_X2_EN;
  172. if (samples >= 4)
  173. value |= ROGUE_CR_TE_AA_Y_EN;
  174. if (samples >= 8)
  175. value |= ROGUE_CR_TE_AA_X_EN;
  176. break;
  177. case 4:
  178. if (samples >= 2)
  179. value |= ROGUE_CR_TE_AA_X2_EN;
  180. if (samples >= 4)
  181. value |= ROGUE_CR_TE_AA_Y2_EN;
  182. if (samples >= 8)
  183. value |= ROGUE_CR_TE_AA_Y_EN;
  184. break;
  185. default:
  186. WARN(true, "Unsupported ISP samples per pixel value");
  187. return -EINVAL;
  188. }
  189. *value_out = value;
  190. return 0;
  191. }
  192. static void
  193. hwrtdata_common_init(void *cpu_ptr, void *priv)
  194. {
  195. struct pvr_hwrt_dataset *hwrt = priv;
  196. memcpy(cpu_ptr, &hwrt->common, sizeof(hwrt->common));
  197. }
  198. static int
  199. hwrt_init_common_fw_structure(struct pvr_file *pvr_file,
  200. struct drm_pvr_ioctl_create_hwrt_dataset_args *args,
  201. struct pvr_hwrt_dataset *hwrt)
  202. {
  203. struct drm_pvr_create_hwrt_geom_data_args *geom_data_args = &args->geom_data_args;
  204. struct pvr_device *pvr_dev = pvr_file->pvr_dev;
  205. struct pvr_rt_mtile_info info;
  206. int err;
  207. err = PVR_FEATURE_VALUE(pvr_dev, tile_size_x, &info.tile_size_x);
  208. if (WARN_ON(err))
  209. return err;
  210. err = PVR_FEATURE_VALUE(pvr_dev, tile_size_y, &info.tile_size_y);
  211. if (WARN_ON(err))
  212. return err;
  213. info.num_tiles_x = DIV_ROUND_UP(args->width, info.tile_size_x);
  214. info.num_tiles_y = DIV_ROUND_UP(args->height, info.tile_size_y);
  215. if (PVR_HAS_FEATURE(pvr_dev, simple_parameter_format_version)) {
  216. u32 parameter_format;
  217. err = PVR_FEATURE_VALUE(pvr_dev, simple_parameter_format_version,
  218. &parameter_format);
  219. if (WARN_ON(err))
  220. return err;
  221. WARN_ON(parameter_format != 2);
  222. /*
  223. * Set up 16 macrotiles with a multiple of 2x2 tiles per macrotile, which is
  224. * aligned to a tile group.
  225. */
  226. info.mtile_x[0] = DIV_ROUND_UP(info.num_tiles_x, 8) * 2;
  227. info.mtile_y[0] = DIV_ROUND_UP(info.num_tiles_y, 8) * 2;
  228. info.mtile_x[1] = 0;
  229. info.mtile_y[1] = 0;
  230. info.mtile_x[2] = 0;
  231. info.mtile_y[2] = 0;
  232. info.tile_max_x = round_up(info.num_tiles_x, 2) - 1;
  233. info.tile_max_y = round_up(info.num_tiles_y, 2) - 1;
  234. } else {
  235. /* Set up 16 macrotiles with a multiple of 4x4 tiles per macrotile. */
  236. info.mtile_x[0] = round_up(DIV_ROUND_UP(info.num_tiles_x, 4), 4);
  237. info.mtile_y[0] = round_up(DIV_ROUND_UP(info.num_tiles_y, 4), 4);
  238. info.mtile_x[1] = info.mtile_x[0] * 2;
  239. info.mtile_y[1] = info.mtile_y[0] * 2;
  240. info.mtile_x[2] = info.mtile_x[0] * 3;
  241. info.mtile_y[2] = info.mtile_y[0] * 3;
  242. info.tile_max_x = info.num_tiles_x - 1;
  243. info.tile_max_y = info.num_tiles_y - 1;
  244. }
  245. hwrt->common.geom_caches_need_zeroing = false;
  246. hwrt->common.isp_merge_lower_x = args->isp_merge_lower_x;
  247. hwrt->common.isp_merge_lower_y = args->isp_merge_lower_y;
  248. hwrt->common.isp_merge_upper_x = args->isp_merge_upper_x;
  249. hwrt->common.isp_merge_upper_y = args->isp_merge_upper_y;
  250. hwrt->common.isp_merge_scale_x = args->isp_merge_scale_x;
  251. hwrt->common.isp_merge_scale_y = args->isp_merge_scale_y;
  252. err = get_cr_multisamplectl_val(args->samples, false,
  253. &hwrt->common.multi_sample_ctl);
  254. if (err)
  255. return err;
  256. err = get_cr_multisamplectl_val(args->samples, true,
  257. &hwrt->common.flipped_multi_sample_ctl);
  258. if (err)
  259. return err;
  260. hwrt->common.mtile_stride = info.mtile_x[0] * info.mtile_y[0];
  261. err = get_cr_te_aa_val(pvr_dev, args->samples, &hwrt->common.teaa);
  262. if (err)
  263. return err;
  264. hwrt->common.screen_pixel_max =
  265. (((args->width - 1) << ROGUE_CR_PPP_SCREEN_PIXXMAX_SHIFT) &
  266. ~ROGUE_CR_PPP_SCREEN_PIXXMAX_CLRMSK) |
  267. (((args->height - 1) << ROGUE_CR_PPP_SCREEN_PIXYMAX_SHIFT) &
  268. ~ROGUE_CR_PPP_SCREEN_PIXYMAX_CLRMSK);
  269. hwrt->common.te_screen =
  270. ((info.tile_max_x << ROGUE_CR_TE_SCREEN_XMAX_SHIFT) &
  271. ~ROGUE_CR_TE_SCREEN_XMAX_CLRMSK) |
  272. ((info.tile_max_y << ROGUE_CR_TE_SCREEN_YMAX_SHIFT) &
  273. ~ROGUE_CR_TE_SCREEN_YMAX_CLRMSK);
  274. hwrt->common.te_mtile1 =
  275. ((info.mtile_x[0] << ROGUE_CR_TE_MTILE1_X1_SHIFT) & ~ROGUE_CR_TE_MTILE1_X1_CLRMSK) |
  276. ((info.mtile_x[1] << ROGUE_CR_TE_MTILE1_X2_SHIFT) & ~ROGUE_CR_TE_MTILE1_X2_CLRMSK) |
  277. ((info.mtile_x[2] << ROGUE_CR_TE_MTILE1_X3_SHIFT) & ~ROGUE_CR_TE_MTILE1_X3_CLRMSK);
  278. hwrt->common.te_mtile2 =
  279. ((info.mtile_y[0] << ROGUE_CR_TE_MTILE2_Y1_SHIFT) & ~ROGUE_CR_TE_MTILE2_Y1_CLRMSK) |
  280. ((info.mtile_y[1] << ROGUE_CR_TE_MTILE2_Y2_SHIFT) & ~ROGUE_CR_TE_MTILE2_Y2_CLRMSK) |
  281. ((info.mtile_y[2] << ROGUE_CR_TE_MTILE2_Y3_SHIFT) & ~ROGUE_CR_TE_MTILE2_Y3_CLRMSK);
  282. err = get_cr_isp_mtile_size_val(pvr_dev, args->samples, &info,
  283. &hwrt->common.isp_mtile_size);
  284. if (err)
  285. return err;
  286. hwrt->common.tpc_stride = geom_data_args->tpc_stride;
  287. hwrt->common.tpc_size = geom_data_args->tpc_size;
  288. hwrt->common.rgn_header_size = args->region_header_size;
  289. err = pvr_fw_object_create(pvr_dev, sizeof(struct rogue_fwif_hwrtdata_common),
  290. PVR_BO_FW_FLAGS_DEVICE_UNCACHED, hwrtdata_common_init, hwrt,
  291. &hwrt->common_fw_obj);
  292. return err;
  293. }
  294. static void
  295. hwrt_fw_data_init(void *cpu_ptr, void *priv)
  296. {
  297. struct pvr_hwrt_data *hwrt_data = priv;
  298. memcpy(cpu_ptr, &hwrt_data->data, sizeof(hwrt_data->data));
  299. }
  300. static int
  301. hwrt_data_init_fw_structure(struct pvr_file *pvr_file,
  302. struct pvr_hwrt_dataset *hwrt,
  303. struct drm_pvr_ioctl_create_hwrt_dataset_args *args,
  304. struct drm_pvr_create_hwrt_rt_data_args *rt_data_args,
  305. struct pvr_hwrt_data *hwrt_data)
  306. {
  307. struct drm_pvr_create_hwrt_geom_data_args *geom_data_args = &args->geom_data_args;
  308. struct pvr_device *pvr_dev = pvr_file->pvr_dev;
  309. struct rogue_fwif_rta_ctl *rta_ctl;
  310. int free_list_i;
  311. int err;
  312. pvr_fw_object_get_fw_addr(hwrt->common_fw_obj,
  313. &hwrt_data->data.hwrt_data_common_fw_addr);
  314. for (free_list_i = 0; free_list_i < ARRAY_SIZE(hwrt->free_lists); free_list_i++) {
  315. pvr_fw_object_get_fw_addr(hwrt->free_lists[free_list_i]->fw_obj,
  316. &hwrt_data->data.freelists_fw_addr[free_list_i]);
  317. }
  318. hwrt_data->data.tail_ptrs_dev_addr = geom_data_args->tpc_dev_addr;
  319. hwrt_data->data.vheap_table_dev_addr = geom_data_args->vheap_table_dev_addr;
  320. hwrt_data->data.rtc_dev_addr = geom_data_args->rtc_dev_addr;
  321. hwrt_data->data.pm_mlist_dev_addr = rt_data_args->pm_mlist_dev_addr;
  322. hwrt_data->data.macrotile_array_dev_addr = rt_data_args->macrotile_array_dev_addr;
  323. hwrt_data->data.rgn_header_dev_addr = rt_data_args->region_header_dev_addr;
  324. rta_ctl = &hwrt_data->data.rta_ctl;
  325. rta_ctl->render_target_index = 0;
  326. rta_ctl->active_render_targets = 0;
  327. rta_ctl->valid_render_targets_fw_addr = 0;
  328. rta_ctl->rta_num_partial_renders_fw_addr = 0;
  329. rta_ctl->max_rts = args->layers;
  330. if (args->layers > 1) {
  331. err = pvr_fw_object_create(pvr_dev, args->layers * SRTC_ENTRY_SIZE,
  332. PVR_BO_FW_FLAGS_DEVICE_UNCACHED,
  333. NULL, NULL, &hwrt_data->srtc_obj);
  334. if (err)
  335. return err;
  336. pvr_fw_object_get_fw_addr(hwrt_data->srtc_obj,
  337. &rta_ctl->valid_render_targets_fw_addr);
  338. err = pvr_fw_object_create(pvr_dev, args->layers * RAA_ENTRY_SIZE,
  339. PVR_BO_FW_FLAGS_DEVICE_UNCACHED,
  340. NULL, NULL, &hwrt_data->raa_obj);
  341. if (err)
  342. goto err_put_shadow_rt_cache;
  343. pvr_fw_object_get_fw_addr(hwrt_data->raa_obj,
  344. &rta_ctl->rta_num_partial_renders_fw_addr);
  345. }
  346. err = pvr_fw_object_create(pvr_dev, sizeof(struct rogue_fwif_hwrtdata),
  347. PVR_BO_FW_FLAGS_DEVICE_UNCACHED,
  348. hwrt_fw_data_init, hwrt_data, &hwrt_data->fw_obj);
  349. if (err)
  350. goto err_put_raa_obj;
  351. pvr_free_list_add_hwrt(hwrt->free_lists[0], hwrt_data);
  352. return 0;
  353. err_put_raa_obj:
  354. if (args->layers > 1)
  355. pvr_fw_object_destroy(hwrt_data->raa_obj);
  356. err_put_shadow_rt_cache:
  357. if (args->layers > 1)
  358. pvr_fw_object_destroy(hwrt_data->srtc_obj);
  359. return err;
  360. }
  361. static void
  362. hwrt_data_fini_fw_structure(struct pvr_hwrt_dataset *hwrt, int hwrt_nr)
  363. {
  364. struct pvr_hwrt_data *hwrt_data = &hwrt->data[hwrt_nr];
  365. pvr_free_list_remove_hwrt(hwrt->free_lists[0], hwrt_data);
  366. if (hwrt->max_rts > 1) {
  367. pvr_fw_object_destroy(hwrt_data->raa_obj);
  368. pvr_fw_object_destroy(hwrt_data->srtc_obj);
  369. }
  370. pvr_fw_object_destroy(hwrt_data->fw_obj);
  371. }
  372. /**
  373. * pvr_hwrt_dataset_create() - Create a new HWRT dataset
  374. * @pvr_file: Pointer to pvr_file structure.
  375. * @args: Creation arguments from userspace.
  376. *
  377. * Return:
  378. * * Pointer to new HWRT, or
  379. * * ERR_PTR(-%ENOMEM) on out of memory.
  380. */
  381. struct pvr_hwrt_dataset *
  382. pvr_hwrt_dataset_create(struct pvr_file *pvr_file,
  383. struct drm_pvr_ioctl_create_hwrt_dataset_args *args)
  384. {
  385. struct pvr_hwrt_dataset *hwrt;
  386. int err, i = 0;
  387. /* Create and fill out the kernel structure */
  388. hwrt = kzalloc(sizeof(*hwrt), GFP_KERNEL);
  389. if (!hwrt)
  390. return ERR_PTR(-ENOMEM);
  391. err = hwrt_init_kernel_structure(pvr_file, args, hwrt);
  392. if (err < 0)
  393. goto err_free;
  394. err = hwrt_init_common_fw_structure(pvr_file, args, hwrt);
  395. if (err < 0)
  396. goto err_fini_kernel_structure;
  397. for (; i < ARRAY_SIZE(hwrt->data); i++) {
  398. err = hwrt_data_init_fw_structure(pvr_file, hwrt, args,
  399. &args->rt_data_args[i],
  400. &hwrt->data[i]);
  401. if (err < 0)
  402. goto err_fini_data_structures;
  403. hwrt->data[i].hwrt_dataset = hwrt;
  404. }
  405. kref_init(&hwrt->ref_count);
  406. return hwrt;
  407. err_fini_data_structures:
  408. while (--i >= 0)
  409. hwrt_data_fini_fw_structure(hwrt, i);
  410. err_fini_kernel_structure:
  411. hwrt_fini_kernel_structure(hwrt);
  412. err_free:
  413. kfree(hwrt);
  414. return ERR_PTR(err);
  415. }
  416. static void
  417. pvr_hwrt_dataset_release(struct kref *ref_count)
  418. {
  419. struct pvr_hwrt_dataset *hwrt =
  420. container_of(ref_count, struct pvr_hwrt_dataset, ref_count);
  421. for (int i = ARRAY_SIZE(hwrt->data) - 1; i >= 0; i--) {
  422. WARN_ON(pvr_fw_structure_cleanup(hwrt->pvr_dev, ROGUE_FWIF_CLEANUP_HWRTDATA,
  423. hwrt->data[i].fw_obj, 0));
  424. hwrt_data_fini_fw_structure(hwrt, i);
  425. }
  426. hwrt_fini_common_fw_structure(hwrt);
  427. hwrt_fini_kernel_structure(hwrt);
  428. kfree(hwrt);
  429. }
  430. /**
  431. * pvr_destroy_hwrt_datasets_for_file: Destroy any HWRT datasets associated
  432. * with the given file.
  433. * @pvr_file: Pointer to pvr_file structure.
  434. *
  435. * Removes all HWRT datasets associated with @pvr_file from the device
  436. * hwrt_dataset list and drops initial references. HWRT datasets will then be
  437. * destroyed once all outstanding references are dropped.
  438. */
  439. void pvr_destroy_hwrt_datasets_for_file(struct pvr_file *pvr_file)
  440. {
  441. struct pvr_hwrt_dataset *hwrt;
  442. unsigned long handle;
  443. xa_for_each(&pvr_file->hwrt_handles, handle, hwrt) {
  444. (void)hwrt;
  445. pvr_hwrt_dataset_put(xa_erase(&pvr_file->hwrt_handles, handle));
  446. }
  447. }
  448. /**
  449. * pvr_hwrt_dataset_put() - Release reference on HWRT dataset
  450. * @hwrt: Pointer to HWRT dataset to release reference on
  451. */
  452. void
  453. pvr_hwrt_dataset_put(struct pvr_hwrt_dataset *hwrt)
  454. {
  455. if (hwrt)
  456. kref_put(&hwrt->ref_count, pvr_hwrt_dataset_release);
  457. }