xref: /linux/drivers/gpu/drm/imagination/pvr_hwrt.c (revision 41c177cf354126a22443b5c80cec9fdd313e67e1)
16eedddabSSarah Walker // SPDX-License-Identifier: GPL-2.0-only OR MIT
26eedddabSSarah Walker /* Copyright (c) 2023 Imagination Technologies Ltd. */
36eedddabSSarah Walker 
46eedddabSSarah Walker #include "pvr_free_list.h"
56eedddabSSarah Walker #include "pvr_hwrt.h"
66eedddabSSarah Walker #include "pvr_gem.h"
76eedddabSSarah Walker #include "pvr_rogue_cr_defs_client.h"
86eedddabSSarah Walker #include "pvr_rogue_fwif.h"
96eedddabSSarah Walker 
106eedddabSSarah Walker #include <drm/drm_gem.h>
116eedddabSSarah Walker #include <linux/bitops.h>
126eedddabSSarah Walker #include <linux/math.h>
136eedddabSSarah Walker #include <linux/slab.h>
146eedddabSSarah Walker #include <linux/xarray.h>
156eedddabSSarah Walker #include <uapi/drm/pvr_drm.h>
166eedddabSSarah Walker 
176eedddabSSarah Walker static_assert(ROGUE_FWIF_NUM_RTDATAS == 2);
186eedddabSSarah Walker static_assert(ROGUE_FWIF_NUM_GEOMDATAS == 1);
196eedddabSSarah Walker static_assert(ROGUE_FWIF_NUM_RTDATA_FREELISTS == 2);
206eedddabSSarah Walker 
216eedddabSSarah Walker /*
226eedddabSSarah Walker  * struct pvr_rt_mtile_info - Render target macrotile information
236eedddabSSarah Walker  */
246eedddabSSarah Walker struct pvr_rt_mtile_info {
256eedddabSSarah Walker 	u32 mtile_x[3];
266eedddabSSarah Walker 	u32 mtile_y[3];
276eedddabSSarah Walker 	u32 tile_max_x;
286eedddabSSarah Walker 	u32 tile_max_y;
296eedddabSSarah Walker 	u32 tile_size_x;
306eedddabSSarah Walker 	u32 tile_size_y;
316eedddabSSarah Walker 	u32 num_tiles_x;
326eedddabSSarah Walker 	u32 num_tiles_y;
336eedddabSSarah Walker };
346eedddabSSarah Walker 
356eedddabSSarah Walker /* Size of Shadow Render Target Cache entry */
366eedddabSSarah Walker #define SRTC_ENTRY_SIZE sizeof(u32)
376eedddabSSarah Walker /* Size of Renders Accumulation Array entry */
386eedddabSSarah Walker #define RAA_ENTRY_SIZE sizeof(u32)
396eedddabSSarah Walker 
406eedddabSSarah Walker static int
hwrt_init_kernel_structure(struct pvr_file * pvr_file,struct drm_pvr_ioctl_create_hwrt_dataset_args * args,struct pvr_hwrt_dataset * hwrt)416eedddabSSarah Walker hwrt_init_kernel_structure(struct pvr_file *pvr_file,
426eedddabSSarah Walker 			   struct drm_pvr_ioctl_create_hwrt_dataset_args *args,
436eedddabSSarah Walker 			   struct pvr_hwrt_dataset *hwrt)
446eedddabSSarah Walker {
456eedddabSSarah Walker 	struct pvr_device *pvr_dev = pvr_file->pvr_dev;
466eedddabSSarah Walker 	int err;
476eedddabSSarah Walker 	int i;
486eedddabSSarah Walker 
496eedddabSSarah Walker 	hwrt->pvr_dev = pvr_dev;
506eedddabSSarah Walker 	hwrt->max_rts = args->layers;
516eedddabSSarah Walker 
526eedddabSSarah Walker 	/* Get pointers to the free lists */
536eedddabSSarah Walker 	for (i = 0; i < ARRAY_SIZE(hwrt->free_lists); i++) {
546eedddabSSarah Walker 		hwrt->free_lists[i] = pvr_free_list_lookup(pvr_file,  args->free_list_handles[i]);
556eedddabSSarah Walker 		if (!hwrt->free_lists[i]) {
566eedddabSSarah Walker 			err = -EINVAL;
576eedddabSSarah Walker 			goto err_put_free_lists;
586eedddabSSarah Walker 		}
596eedddabSSarah Walker 	}
606eedddabSSarah Walker 
616eedddabSSarah Walker 	if (hwrt->free_lists[ROGUE_FW_LOCAL_FREELIST]->current_pages <
626eedddabSSarah Walker 	    pvr_get_free_list_min_pages(pvr_dev)) {
636eedddabSSarah Walker 		err = -EINVAL;
646eedddabSSarah Walker 		goto err_put_free_lists;
656eedddabSSarah Walker 	}
666eedddabSSarah Walker 
676eedddabSSarah Walker 	return 0;
686eedddabSSarah Walker 
696eedddabSSarah Walker err_put_free_lists:
706eedddabSSarah Walker 	for (i = 0; i < ARRAY_SIZE(hwrt->free_lists); i++) {
716eedddabSSarah Walker 		pvr_free_list_put(hwrt->free_lists[i]);
726eedddabSSarah Walker 		hwrt->free_lists[i] = NULL;
736eedddabSSarah Walker 	}
746eedddabSSarah Walker 
756eedddabSSarah Walker 	return err;
766eedddabSSarah Walker }
776eedddabSSarah Walker 
786eedddabSSarah Walker static void
hwrt_fini_kernel_structure(struct pvr_hwrt_dataset * hwrt)796eedddabSSarah Walker hwrt_fini_kernel_structure(struct pvr_hwrt_dataset *hwrt)
806eedddabSSarah Walker {
816eedddabSSarah Walker 	int i;
826eedddabSSarah Walker 
836eedddabSSarah Walker 	for (i = 0; i < ARRAY_SIZE(hwrt->free_lists); i++) {
846eedddabSSarah Walker 		pvr_free_list_put(hwrt->free_lists[i]);
856eedddabSSarah Walker 		hwrt->free_lists[i] = NULL;
866eedddabSSarah Walker 	}
876eedddabSSarah Walker }
886eedddabSSarah Walker 
896eedddabSSarah Walker static void
hwrt_fini_common_fw_structure(struct pvr_hwrt_dataset * hwrt)906eedddabSSarah Walker hwrt_fini_common_fw_structure(struct pvr_hwrt_dataset *hwrt)
916eedddabSSarah Walker {
926eedddabSSarah Walker 	pvr_fw_object_destroy(hwrt->common_fw_obj);
936eedddabSSarah Walker }
946eedddabSSarah Walker 
956eedddabSSarah Walker static int
get_cr_isp_mtile_size_val(struct pvr_device * pvr_dev,u32 samples,struct pvr_rt_mtile_info * info,u32 * value_out)966eedddabSSarah Walker get_cr_isp_mtile_size_val(struct pvr_device *pvr_dev, u32 samples,
976eedddabSSarah Walker 			  struct pvr_rt_mtile_info *info, u32 *value_out)
986eedddabSSarah Walker {
996eedddabSSarah Walker 	u32 x = info->mtile_x[0];
1006eedddabSSarah Walker 	u32 y = info->mtile_y[0];
1016eedddabSSarah Walker 	u32 samples_per_pixel;
1026eedddabSSarah Walker 	int err;
1036eedddabSSarah Walker 
1046eedddabSSarah Walker 	err = PVR_FEATURE_VALUE(pvr_dev, isp_samples_per_pixel, &samples_per_pixel);
1056eedddabSSarah Walker 	if (err)
1066eedddabSSarah Walker 		return err;
1076eedddabSSarah Walker 
1086eedddabSSarah Walker 	if (samples_per_pixel == 1) {
1096eedddabSSarah Walker 		if (samples >= 4)
1106eedddabSSarah Walker 			x <<= 1;
1116eedddabSSarah Walker 		if (samples >= 2)
1126eedddabSSarah Walker 			y <<= 1;
1136eedddabSSarah Walker 	} else if (samples_per_pixel == 2) {
1146eedddabSSarah Walker 		if (samples >= 8)
1156eedddabSSarah Walker 			x <<= 1;
1166eedddabSSarah Walker 		if (samples >= 4)
1176eedddabSSarah Walker 			y <<= 1;
1186eedddabSSarah Walker 	} else if (samples_per_pixel == 4) {
1196eedddabSSarah Walker 		if (samples >= 8)
1206eedddabSSarah Walker 			y <<= 1;
1216eedddabSSarah Walker 	} else {
1226eedddabSSarah Walker 		WARN(true, "Unsupported ISP samples per pixel value");
1236eedddabSSarah Walker 		return -EINVAL;
1246eedddabSSarah Walker 	}
1256eedddabSSarah Walker 
1266eedddabSSarah Walker 	*value_out = ((x << ROGUE_CR_ISP_MTILE_SIZE_X_SHIFT) & ~ROGUE_CR_ISP_MTILE_SIZE_X_CLRMSK) |
1276eedddabSSarah Walker 		     ((y << ROGUE_CR_ISP_MTILE_SIZE_Y_SHIFT) & ~ROGUE_CR_ISP_MTILE_SIZE_Y_CLRMSK);
1286eedddabSSarah Walker 
1296eedddabSSarah Walker 	return 0;
1306eedddabSSarah Walker }
1316eedddabSSarah Walker 
1326eedddabSSarah Walker static int
get_cr_multisamplectl_val(u32 samples,bool y_flip,u64 * value_out)1336eedddabSSarah Walker get_cr_multisamplectl_val(u32 samples, bool y_flip, u64 *value_out)
1346eedddabSSarah Walker {
1356eedddabSSarah Walker 	static const struct {
1366eedddabSSarah Walker 		u8 x[8];
1376eedddabSSarah Walker 		u8 y[8];
1386eedddabSSarah Walker 	} sample_positions[4] = {
1396eedddabSSarah Walker 		/* 1 sample */
1406eedddabSSarah Walker 		{
1416eedddabSSarah Walker 			.x = { 8 },
1426eedddabSSarah Walker 			.y = { 8 },
1436eedddabSSarah Walker 		},
1446eedddabSSarah Walker 		/* 2 samples */
1456eedddabSSarah Walker 		{
1466eedddabSSarah Walker 			.x = { 12, 4 },
1476eedddabSSarah Walker 			.y = { 12, 4 },
1486eedddabSSarah Walker 		},
1496eedddabSSarah Walker 		/* 4 samples */
1506eedddabSSarah Walker 		{
1516eedddabSSarah Walker 			.x = { 6, 14, 2, 10 },
1526eedddabSSarah Walker 			.y = { 2, 6, 10, 14 },
1536eedddabSSarah Walker 		},
1546eedddabSSarah Walker 		/* 8 samples */
1556eedddabSSarah Walker 		{
1566eedddabSSarah Walker 			.x = { 9, 7, 13, 5, 3, 1, 11, 15 },
1576eedddabSSarah Walker 			.y = { 5, 11, 9, 3, 13, 7, 15, 1 },
1586eedddabSSarah Walker 		},
1596eedddabSSarah Walker 	};
1606eedddabSSarah Walker 	const int idx = fls(samples) - 1;
1616eedddabSSarah Walker 	u64 value = 0;
1626eedddabSSarah Walker 
1636eedddabSSarah Walker 	if (idx < 0 || idx > 3)
1646eedddabSSarah Walker 		return -EINVAL;
1656eedddabSSarah Walker 
1666eedddabSSarah Walker 	for (u32 i = 0; i < 8; i++) {
1676eedddabSSarah Walker 		value |= ((u64)sample_positions[idx].x[i]) << (i * 8);
1686eedddabSSarah Walker 		if (y_flip)
1696eedddabSSarah Walker 			value |= (((u64)(16 - sample_positions[idx].y[i]) & 0xf)) << (i * 8 + 4);
1706eedddabSSarah Walker 		else
1716eedddabSSarah Walker 			value |= ((u64)sample_positions[idx].y[i]) << (i * 8 + 4);
1726eedddabSSarah Walker 	}
1736eedddabSSarah Walker 
1746eedddabSSarah Walker 	*value_out = value;
1756eedddabSSarah Walker 
1766eedddabSSarah Walker 	return 0;
1776eedddabSSarah Walker }
1786eedddabSSarah Walker 
1796eedddabSSarah Walker static int
get_cr_te_aa_val(struct pvr_device * pvr_dev,u32 samples,u32 * value_out)1806eedddabSSarah Walker get_cr_te_aa_val(struct pvr_device *pvr_dev, u32 samples, u32 *value_out)
1816eedddabSSarah Walker {
1826eedddabSSarah Walker 	u32 samples_per_pixel;
1836eedddabSSarah Walker 	u32 value = 0;
1846eedddabSSarah Walker 	int err = 0;
1856eedddabSSarah Walker 
1866eedddabSSarah Walker 	err = PVR_FEATURE_VALUE(pvr_dev, isp_samples_per_pixel, &samples_per_pixel);
1876eedddabSSarah Walker 	if (err)
1886eedddabSSarah Walker 		return err;
1896eedddabSSarah Walker 
1906eedddabSSarah Walker 	switch (samples_per_pixel) {
1916eedddabSSarah Walker 	case 1:
1926eedddabSSarah Walker 		if (samples >= 2)
1936eedddabSSarah Walker 			value |= ROGUE_CR_TE_AA_Y_EN;
1946eedddabSSarah Walker 		if (samples >= 4)
1956eedddabSSarah Walker 			value |= ROGUE_CR_TE_AA_X_EN;
1966eedddabSSarah Walker 		break;
1976eedddabSSarah Walker 	case 2:
1986eedddabSSarah Walker 		if (samples >= 2)
1996eedddabSSarah Walker 			value |= ROGUE_CR_TE_AA_X2_EN;
2006eedddabSSarah Walker 		if (samples >= 4)
2016eedddabSSarah Walker 			value |= ROGUE_CR_TE_AA_Y_EN;
2026eedddabSSarah Walker 		if (samples >= 8)
2036eedddabSSarah Walker 			value |= ROGUE_CR_TE_AA_X_EN;
2046eedddabSSarah Walker 		break;
2056eedddabSSarah Walker 	case 4:
2066eedddabSSarah Walker 		if (samples >= 2)
2076eedddabSSarah Walker 			value |= ROGUE_CR_TE_AA_X2_EN;
2086eedddabSSarah Walker 		if (samples >= 4)
2096eedddabSSarah Walker 			value |= ROGUE_CR_TE_AA_Y2_EN;
2106eedddabSSarah Walker 		if (samples >= 8)
2116eedddabSSarah Walker 			value |= ROGUE_CR_TE_AA_Y_EN;
2126eedddabSSarah Walker 		break;
2136eedddabSSarah Walker 	default:
2146eedddabSSarah Walker 		WARN(true, "Unsupported ISP samples per pixel value");
2156eedddabSSarah Walker 		return -EINVAL;
2166eedddabSSarah Walker 	}
2176eedddabSSarah Walker 
2186eedddabSSarah Walker 	*value_out = value;
2196eedddabSSarah Walker 
2206eedddabSSarah Walker 	return 0;
2216eedddabSSarah Walker }
2226eedddabSSarah Walker 
2236eedddabSSarah Walker static void
hwrtdata_common_init(void * cpu_ptr,void * priv)2246eedddabSSarah Walker hwrtdata_common_init(void *cpu_ptr, void *priv)
2256eedddabSSarah Walker {
2266eedddabSSarah Walker 	struct pvr_hwrt_dataset *hwrt = priv;
2276eedddabSSarah Walker 
2286eedddabSSarah Walker 	memcpy(cpu_ptr, &hwrt->common, sizeof(hwrt->common));
2296eedddabSSarah Walker }
2306eedddabSSarah Walker 
2316eedddabSSarah Walker static int
hwrt_init_common_fw_structure(struct pvr_file * pvr_file,struct drm_pvr_ioctl_create_hwrt_dataset_args * args,struct pvr_hwrt_dataset * hwrt)2326eedddabSSarah Walker hwrt_init_common_fw_structure(struct pvr_file *pvr_file,
2336eedddabSSarah Walker 			      struct drm_pvr_ioctl_create_hwrt_dataset_args *args,
2346eedddabSSarah Walker 			      struct pvr_hwrt_dataset *hwrt)
2356eedddabSSarah Walker {
2366eedddabSSarah Walker 	struct drm_pvr_create_hwrt_geom_data_args *geom_data_args = &args->geom_data_args;
2376eedddabSSarah Walker 	struct pvr_device *pvr_dev = pvr_file->pvr_dev;
2386eedddabSSarah Walker 	struct pvr_rt_mtile_info info;
2396eedddabSSarah Walker 	int err;
2406eedddabSSarah Walker 
2416eedddabSSarah Walker 	err = PVR_FEATURE_VALUE(pvr_dev, tile_size_x, &info.tile_size_x);
2426eedddabSSarah Walker 	if (WARN_ON(err))
2436eedddabSSarah Walker 		return err;
2446eedddabSSarah Walker 
2456eedddabSSarah Walker 	err = PVR_FEATURE_VALUE(pvr_dev, tile_size_y, &info.tile_size_y);
2466eedddabSSarah Walker 	if (WARN_ON(err))
2476eedddabSSarah Walker 		return err;
2486eedddabSSarah Walker 
2496eedddabSSarah Walker 	info.num_tiles_x = DIV_ROUND_UP(args->width, info.tile_size_x);
2506eedddabSSarah Walker 	info.num_tiles_y = DIV_ROUND_UP(args->height, info.tile_size_y);
2516eedddabSSarah Walker 
2526eedddabSSarah Walker 	if (PVR_HAS_FEATURE(pvr_dev, simple_parameter_format_version)) {
2536eedddabSSarah Walker 		u32 parameter_format;
2546eedddabSSarah Walker 
2556eedddabSSarah Walker 		err = PVR_FEATURE_VALUE(pvr_dev, simple_parameter_format_version,
2566eedddabSSarah Walker 					&parameter_format);
2576eedddabSSarah Walker 		if (WARN_ON(err))
2586eedddabSSarah Walker 			return err;
2596eedddabSSarah Walker 
2606eedddabSSarah Walker 		WARN_ON(parameter_format != 2);
2616eedddabSSarah Walker 
2626eedddabSSarah Walker 		/*
2636eedddabSSarah Walker 		 * Set up 16 macrotiles with a multiple of 2x2 tiles per macrotile, which is
2646eedddabSSarah Walker 		 * aligned to a tile group.
2656eedddabSSarah Walker 		 */
2666eedddabSSarah Walker 		info.mtile_x[0] = DIV_ROUND_UP(info.num_tiles_x, 8) * 2;
2676eedddabSSarah Walker 		info.mtile_y[0] = DIV_ROUND_UP(info.num_tiles_y, 8) * 2;
2686eedddabSSarah Walker 		info.mtile_x[1] = 0;
2696eedddabSSarah Walker 		info.mtile_y[1] = 0;
2706eedddabSSarah Walker 		info.mtile_x[2] = 0;
2716eedddabSSarah Walker 		info.mtile_y[2] = 0;
2726eedddabSSarah Walker 		info.tile_max_x = round_up(info.num_tiles_x, 2) - 1;
2736eedddabSSarah Walker 		info.tile_max_y = round_up(info.num_tiles_y, 2) - 1;
2746eedddabSSarah Walker 	} else {
2756eedddabSSarah Walker 		/* Set up 16 macrotiles with a multiple of 4x4 tiles per macrotile. */
2766eedddabSSarah Walker 		info.mtile_x[0] = round_up(DIV_ROUND_UP(info.num_tiles_x, 4), 4);
2776eedddabSSarah Walker 		info.mtile_y[0] = round_up(DIV_ROUND_UP(info.num_tiles_y, 4), 4);
2786eedddabSSarah Walker 		info.mtile_x[1] = info.mtile_x[0] * 2;
2796eedddabSSarah Walker 		info.mtile_y[1] = info.mtile_y[0] * 2;
2806eedddabSSarah Walker 		info.mtile_x[2] = info.mtile_x[0] * 3;
2816eedddabSSarah Walker 		info.mtile_y[2] = info.mtile_y[0] * 3;
2826eedddabSSarah Walker 		info.tile_max_x = info.num_tiles_x - 1;
2836eedddabSSarah Walker 		info.tile_max_y = info.num_tiles_y - 1;
2846eedddabSSarah Walker 	}
2856eedddabSSarah Walker 
2866eedddabSSarah Walker 	hwrt->common.geom_caches_need_zeroing = false;
2876eedddabSSarah Walker 
2886eedddabSSarah Walker 	hwrt->common.isp_merge_lower_x = args->isp_merge_lower_x;
2896eedddabSSarah Walker 	hwrt->common.isp_merge_lower_y = args->isp_merge_lower_y;
2906eedddabSSarah Walker 	hwrt->common.isp_merge_upper_x = args->isp_merge_upper_x;
2916eedddabSSarah Walker 	hwrt->common.isp_merge_upper_y = args->isp_merge_upper_y;
2926eedddabSSarah Walker 	hwrt->common.isp_merge_scale_x = args->isp_merge_scale_x;
2936eedddabSSarah Walker 	hwrt->common.isp_merge_scale_y = args->isp_merge_scale_y;
2946eedddabSSarah Walker 
2956eedddabSSarah Walker 	err = get_cr_multisamplectl_val(args->samples, false,
2966eedddabSSarah Walker 					&hwrt->common.multi_sample_ctl);
2976eedddabSSarah Walker 	if (err)
2986eedddabSSarah Walker 		return err;
2996eedddabSSarah Walker 
3006eedddabSSarah Walker 	err = get_cr_multisamplectl_val(args->samples, true,
3016eedddabSSarah Walker 					&hwrt->common.flipped_multi_sample_ctl);
3026eedddabSSarah Walker 	if (err)
3036eedddabSSarah Walker 		return err;
3046eedddabSSarah Walker 
3056eedddabSSarah Walker 	hwrt->common.mtile_stride = info.mtile_x[0] * info.mtile_y[0];
3066eedddabSSarah Walker 
3076eedddabSSarah Walker 	err = get_cr_te_aa_val(pvr_dev, args->samples, &hwrt->common.teaa);
3086eedddabSSarah Walker 	if (err)
3096eedddabSSarah Walker 		return err;
3106eedddabSSarah Walker 
3116eedddabSSarah Walker 	hwrt->common.screen_pixel_max =
3126eedddabSSarah Walker 		(((args->width - 1) << ROGUE_CR_PPP_SCREEN_PIXXMAX_SHIFT) &
3136eedddabSSarah Walker 		 ~ROGUE_CR_PPP_SCREEN_PIXXMAX_CLRMSK) |
3146eedddabSSarah Walker 		(((args->height - 1) << ROGUE_CR_PPP_SCREEN_PIXYMAX_SHIFT) &
3156eedddabSSarah Walker 		 ~ROGUE_CR_PPP_SCREEN_PIXYMAX_CLRMSK);
3166eedddabSSarah Walker 
3176eedddabSSarah Walker 	hwrt->common.te_screen =
3186eedddabSSarah Walker 		((info.tile_max_x << ROGUE_CR_TE_SCREEN_XMAX_SHIFT) &
3196eedddabSSarah Walker 		 ~ROGUE_CR_TE_SCREEN_XMAX_CLRMSK) |
3206eedddabSSarah Walker 		((info.tile_max_y << ROGUE_CR_TE_SCREEN_YMAX_SHIFT) &
3216eedddabSSarah Walker 		 ~ROGUE_CR_TE_SCREEN_YMAX_CLRMSK);
3226eedddabSSarah Walker 	hwrt->common.te_mtile1 =
3236eedddabSSarah Walker 		((info.mtile_x[0] << ROGUE_CR_TE_MTILE1_X1_SHIFT) & ~ROGUE_CR_TE_MTILE1_X1_CLRMSK) |
3246eedddabSSarah Walker 		((info.mtile_x[1] << ROGUE_CR_TE_MTILE1_X2_SHIFT) & ~ROGUE_CR_TE_MTILE1_X2_CLRMSK) |
3256eedddabSSarah Walker 		((info.mtile_x[2] << ROGUE_CR_TE_MTILE1_X3_SHIFT) & ~ROGUE_CR_TE_MTILE1_X3_CLRMSK);
3266eedddabSSarah Walker 	hwrt->common.te_mtile2 =
3276eedddabSSarah Walker 		((info.mtile_y[0] << ROGUE_CR_TE_MTILE2_Y1_SHIFT) & ~ROGUE_CR_TE_MTILE2_Y1_CLRMSK) |
3286eedddabSSarah Walker 		((info.mtile_y[1] << ROGUE_CR_TE_MTILE2_Y2_SHIFT) & ~ROGUE_CR_TE_MTILE2_Y2_CLRMSK) |
3296eedddabSSarah Walker 		((info.mtile_y[2] << ROGUE_CR_TE_MTILE2_Y3_SHIFT) & ~ROGUE_CR_TE_MTILE2_Y3_CLRMSK);
3306eedddabSSarah Walker 
3316eedddabSSarah Walker 	err = get_cr_isp_mtile_size_val(pvr_dev, args->samples, &info,
3326eedddabSSarah Walker 					&hwrt->common.isp_mtile_size);
3336eedddabSSarah Walker 	if (err)
3346eedddabSSarah Walker 		return err;
3356eedddabSSarah Walker 
3366eedddabSSarah Walker 	hwrt->common.tpc_stride = geom_data_args->tpc_stride;
3376eedddabSSarah Walker 	hwrt->common.tpc_size = geom_data_args->tpc_size;
3386eedddabSSarah Walker 
3396eedddabSSarah Walker 	hwrt->common.rgn_header_size = args->region_header_size;
3406eedddabSSarah Walker 
3416eedddabSSarah Walker 	err = pvr_fw_object_create(pvr_dev, sizeof(struct rogue_fwif_hwrtdata_common),
3426eedddabSSarah Walker 				   PVR_BO_FW_FLAGS_DEVICE_UNCACHED, hwrtdata_common_init, hwrt,
3436eedddabSSarah Walker 				   &hwrt->common_fw_obj);
3446eedddabSSarah Walker 
3456eedddabSSarah Walker 	return err;
3466eedddabSSarah Walker }
3476eedddabSSarah Walker 
3486eedddabSSarah Walker static void
hwrt_fw_data_init(void * cpu_ptr,void * priv)3496eedddabSSarah Walker hwrt_fw_data_init(void *cpu_ptr, void *priv)
3506eedddabSSarah Walker {
3516eedddabSSarah Walker 	struct pvr_hwrt_data *hwrt_data = priv;
3526eedddabSSarah Walker 
3536eedddabSSarah Walker 	memcpy(cpu_ptr, &hwrt_data->data, sizeof(hwrt_data->data));
3546eedddabSSarah Walker }
3556eedddabSSarah Walker 
3566eedddabSSarah Walker static int
hwrt_data_init_fw_structure(struct pvr_file * pvr_file,struct pvr_hwrt_dataset * hwrt,struct drm_pvr_ioctl_create_hwrt_dataset_args * args,struct drm_pvr_create_hwrt_rt_data_args * rt_data_args,struct pvr_hwrt_data * hwrt_data)3576eedddabSSarah Walker hwrt_data_init_fw_structure(struct pvr_file *pvr_file,
3586eedddabSSarah Walker 			    struct pvr_hwrt_dataset *hwrt,
3596eedddabSSarah Walker 			    struct drm_pvr_ioctl_create_hwrt_dataset_args *args,
3606eedddabSSarah Walker 			    struct drm_pvr_create_hwrt_rt_data_args *rt_data_args,
3616eedddabSSarah Walker 			    struct pvr_hwrt_data *hwrt_data)
3626eedddabSSarah Walker {
3636eedddabSSarah Walker 	struct drm_pvr_create_hwrt_geom_data_args *geom_data_args = &args->geom_data_args;
3646eedddabSSarah Walker 	struct pvr_device *pvr_dev = pvr_file->pvr_dev;
3656eedddabSSarah Walker 	struct rogue_fwif_rta_ctl *rta_ctl;
3666eedddabSSarah Walker 	int free_list_i;
3676eedddabSSarah Walker 	int err;
3686eedddabSSarah Walker 
3696eedddabSSarah Walker 	pvr_fw_object_get_fw_addr(hwrt->common_fw_obj,
3706eedddabSSarah Walker 				  &hwrt_data->data.hwrt_data_common_fw_addr);
3716eedddabSSarah Walker 
3726eedddabSSarah Walker 	for (free_list_i = 0; free_list_i < ARRAY_SIZE(hwrt->free_lists); free_list_i++) {
3736eedddabSSarah Walker 		pvr_fw_object_get_fw_addr(hwrt->free_lists[free_list_i]->fw_obj,
3746eedddabSSarah Walker 					  &hwrt_data->data.freelists_fw_addr[free_list_i]);
3756eedddabSSarah Walker 	}
3766eedddabSSarah Walker 
3776eedddabSSarah Walker 	hwrt_data->data.tail_ptrs_dev_addr = geom_data_args->tpc_dev_addr;
3786eedddabSSarah Walker 	hwrt_data->data.vheap_table_dev_addr = geom_data_args->vheap_table_dev_addr;
3796eedddabSSarah Walker 	hwrt_data->data.rtc_dev_addr = geom_data_args->rtc_dev_addr;
3806eedddabSSarah Walker 
3816eedddabSSarah Walker 	hwrt_data->data.pm_mlist_dev_addr = rt_data_args->pm_mlist_dev_addr;
3826eedddabSSarah Walker 	hwrt_data->data.macrotile_array_dev_addr = rt_data_args->macrotile_array_dev_addr;
3836eedddabSSarah Walker 	hwrt_data->data.rgn_header_dev_addr = rt_data_args->region_header_dev_addr;
3846eedddabSSarah Walker 
3856eedddabSSarah Walker 	rta_ctl = &hwrt_data->data.rta_ctl;
3866eedddabSSarah Walker 
3876eedddabSSarah Walker 	rta_ctl->render_target_index = 0;
3886eedddabSSarah Walker 	rta_ctl->active_render_targets = 0;
3896eedddabSSarah Walker 	rta_ctl->valid_render_targets_fw_addr = 0;
3906eedddabSSarah Walker 	rta_ctl->rta_num_partial_renders_fw_addr = 0;
3916eedddabSSarah Walker 	rta_ctl->max_rts = args->layers;
3926eedddabSSarah Walker 
3936eedddabSSarah Walker 	if (args->layers > 1) {
3946eedddabSSarah Walker 		err = pvr_fw_object_create(pvr_dev, args->layers * SRTC_ENTRY_SIZE,
3956eedddabSSarah Walker 					   PVR_BO_FW_FLAGS_DEVICE_UNCACHED,
3966eedddabSSarah Walker 					   NULL, NULL, &hwrt_data->srtc_obj);
3976eedddabSSarah Walker 		if (err)
3986eedddabSSarah Walker 			return err;
3996eedddabSSarah Walker 		pvr_fw_object_get_fw_addr(hwrt_data->srtc_obj,
4006eedddabSSarah Walker 					  &rta_ctl->valid_render_targets_fw_addr);
4016eedddabSSarah Walker 
4026eedddabSSarah Walker 		err = pvr_fw_object_create(pvr_dev, args->layers * RAA_ENTRY_SIZE,
4036eedddabSSarah Walker 					   PVR_BO_FW_FLAGS_DEVICE_UNCACHED,
4046eedddabSSarah Walker 					   NULL, NULL, &hwrt_data->raa_obj);
4056eedddabSSarah Walker 		if (err)
4066eedddabSSarah Walker 			goto err_put_shadow_rt_cache;
4076eedddabSSarah Walker 		pvr_fw_object_get_fw_addr(hwrt_data->raa_obj,
4086eedddabSSarah Walker 					  &rta_ctl->rta_num_partial_renders_fw_addr);
4096eedddabSSarah Walker 	}
4106eedddabSSarah Walker 
4116eedddabSSarah Walker 	err = pvr_fw_object_create(pvr_dev, sizeof(struct rogue_fwif_hwrtdata),
4126eedddabSSarah Walker 				   PVR_BO_FW_FLAGS_DEVICE_UNCACHED,
4136eedddabSSarah Walker 				   hwrt_fw_data_init, hwrt_data, &hwrt_data->fw_obj);
4146eedddabSSarah Walker 	if (err)
4156eedddabSSarah Walker 		goto err_put_raa_obj;
4166eedddabSSarah Walker 
4176eedddabSSarah Walker 	pvr_free_list_add_hwrt(hwrt->free_lists[0], hwrt_data);
4186eedddabSSarah Walker 
4196eedddabSSarah Walker 	return 0;
4206eedddabSSarah Walker 
4216eedddabSSarah Walker err_put_raa_obj:
4226eedddabSSarah Walker 	if (args->layers > 1)
4236eedddabSSarah Walker 		pvr_fw_object_destroy(hwrt_data->raa_obj);
4246eedddabSSarah Walker 
4256eedddabSSarah Walker err_put_shadow_rt_cache:
4266eedddabSSarah Walker 	if (args->layers > 1)
4276eedddabSSarah Walker 		pvr_fw_object_destroy(hwrt_data->srtc_obj);
4286eedddabSSarah Walker 
4296eedddabSSarah Walker 	return err;
4306eedddabSSarah Walker }
4316eedddabSSarah Walker 
4326eedddabSSarah Walker static void
hwrt_data_fini_fw_structure(struct pvr_hwrt_dataset * hwrt,int hwrt_nr)4336eedddabSSarah Walker hwrt_data_fini_fw_structure(struct pvr_hwrt_dataset *hwrt, int hwrt_nr)
4346eedddabSSarah Walker {
4356eedddabSSarah Walker 	struct pvr_hwrt_data *hwrt_data = &hwrt->data[hwrt_nr];
4366eedddabSSarah Walker 
4376eedddabSSarah Walker 	pvr_free_list_remove_hwrt(hwrt->free_lists[0], hwrt_data);
4386eedddabSSarah Walker 
4396eedddabSSarah Walker 	if (hwrt->max_rts > 1) {
4406eedddabSSarah Walker 		pvr_fw_object_destroy(hwrt_data->raa_obj);
4416eedddabSSarah Walker 		pvr_fw_object_destroy(hwrt_data->srtc_obj);
4426eedddabSSarah Walker 	}
4436eedddabSSarah Walker 
4446eedddabSSarah Walker 	pvr_fw_object_destroy(hwrt_data->fw_obj);
4456eedddabSSarah Walker }
4466eedddabSSarah Walker 
4476eedddabSSarah Walker /**
4486eedddabSSarah Walker  * pvr_hwrt_dataset_create() - Create a new HWRT dataset
4496eedddabSSarah Walker  * @pvr_file: Pointer to pvr_file structure.
4506eedddabSSarah Walker  * @args: Creation arguments from userspace.
4516eedddabSSarah Walker  *
4526eedddabSSarah Walker  * Return:
4536eedddabSSarah Walker  *  * Pointer to new HWRT, or
4546eedddabSSarah Walker  *  * ERR_PTR(-%ENOMEM) on out of memory.
4556eedddabSSarah Walker  */
4566eedddabSSarah Walker struct pvr_hwrt_dataset *
pvr_hwrt_dataset_create(struct pvr_file * pvr_file,struct drm_pvr_ioctl_create_hwrt_dataset_args * args)4576eedddabSSarah Walker pvr_hwrt_dataset_create(struct pvr_file *pvr_file,
4586eedddabSSarah Walker 			struct drm_pvr_ioctl_create_hwrt_dataset_args *args)
4596eedddabSSarah Walker {
4606eedddabSSarah Walker 	struct pvr_hwrt_dataset *hwrt;
461*f1f55ed3SDonald Robson 	int err, i = 0;
4626eedddabSSarah Walker 
4636eedddabSSarah Walker 	/* Create and fill out the kernel structure */
4646eedddabSSarah Walker 	hwrt = kzalloc(sizeof(*hwrt), GFP_KERNEL);
4656eedddabSSarah Walker 
4666eedddabSSarah Walker 	if (!hwrt)
4676eedddabSSarah Walker 		return ERR_PTR(-ENOMEM);
4686eedddabSSarah Walker 
4696eedddabSSarah Walker 	err = hwrt_init_kernel_structure(pvr_file, args, hwrt);
4706eedddabSSarah Walker 	if (err < 0)
4716eedddabSSarah Walker 		goto err_free;
4726eedddabSSarah Walker 
4736eedddabSSarah Walker 	err = hwrt_init_common_fw_structure(pvr_file, args, hwrt);
4746eedddabSSarah Walker 	if (err < 0)
475*f1f55ed3SDonald Robson 		goto err_fini_kernel_structure;
4766eedddabSSarah Walker 
477*f1f55ed3SDonald Robson 	for (; i < ARRAY_SIZE(hwrt->data); i++) {
4786eedddabSSarah Walker 		err = hwrt_data_init_fw_structure(pvr_file, hwrt, args,
4796eedddabSSarah Walker 						  &args->rt_data_args[i],
4806eedddabSSarah Walker 						  &hwrt->data[i]);
481*f1f55ed3SDonald Robson 		if (err < 0)
482*f1f55ed3SDonald Robson 			goto err_fini_data_structures;
4836eedddabSSarah Walker 
4846eedddabSSarah Walker 		hwrt->data[i].hwrt_dataset = hwrt;
4856eedddabSSarah Walker 	}
4866eedddabSSarah Walker 
487*f1f55ed3SDonald Robson 	kref_init(&hwrt->ref_count);
4886eedddabSSarah Walker 	return hwrt;
4896eedddabSSarah Walker 
490*f1f55ed3SDonald Robson err_fini_data_structures:
491*f1f55ed3SDonald Robson 	while (--i >= 0)
492*f1f55ed3SDonald Robson 		hwrt_data_fini_fw_structure(hwrt, i);
493*f1f55ed3SDonald Robson 
494*f1f55ed3SDonald Robson err_fini_kernel_structure:
495*f1f55ed3SDonald Robson 	hwrt_fini_kernel_structure(hwrt);
496*f1f55ed3SDonald Robson 
4976eedddabSSarah Walker err_free:
498*f1f55ed3SDonald Robson 	kfree(hwrt);
4996eedddabSSarah Walker 
5006eedddabSSarah Walker 	return ERR_PTR(err);
5016eedddabSSarah Walker }
5026eedddabSSarah Walker 
5036eedddabSSarah Walker static void
pvr_hwrt_dataset_release(struct kref * ref_count)5046eedddabSSarah Walker pvr_hwrt_dataset_release(struct kref *ref_count)
5056eedddabSSarah Walker {
5066eedddabSSarah Walker 	struct pvr_hwrt_dataset *hwrt =
5076eedddabSSarah Walker 		container_of(ref_count, struct pvr_hwrt_dataset, ref_count);
5086eedddabSSarah Walker 
5096eedddabSSarah Walker 	for (int i = ARRAY_SIZE(hwrt->data) - 1; i >= 0; i--) {
5106eedddabSSarah Walker 		WARN_ON(pvr_fw_structure_cleanup(hwrt->pvr_dev, ROGUE_FWIF_CLEANUP_HWRTDATA,
5116eedddabSSarah Walker 						 hwrt->data[i].fw_obj, 0));
5126eedddabSSarah Walker 		hwrt_data_fini_fw_structure(hwrt, i);
5136eedddabSSarah Walker 	}
5146eedddabSSarah Walker 
5156eedddabSSarah Walker 	hwrt_fini_common_fw_structure(hwrt);
5166eedddabSSarah Walker 	hwrt_fini_kernel_structure(hwrt);
5176eedddabSSarah Walker 
5186eedddabSSarah Walker 	kfree(hwrt);
5196eedddabSSarah Walker }
5206eedddabSSarah Walker 
5216eedddabSSarah Walker /**
5226eedddabSSarah Walker  * pvr_destroy_hwrt_datasets_for_file: Destroy any HWRT datasets associated
5236eedddabSSarah Walker  * with the given file.
5246eedddabSSarah Walker  * @pvr_file: Pointer to pvr_file structure.
5256eedddabSSarah Walker  *
5266eedddabSSarah Walker  * Removes all HWRT datasets associated with @pvr_file from the device
5276eedddabSSarah Walker  * hwrt_dataset list and drops initial references. HWRT datasets will then be
5286eedddabSSarah Walker  * destroyed once all outstanding references are dropped.
5296eedddabSSarah Walker  */
pvr_destroy_hwrt_datasets_for_file(struct pvr_file * pvr_file)5306eedddabSSarah Walker void pvr_destroy_hwrt_datasets_for_file(struct pvr_file *pvr_file)
5316eedddabSSarah Walker {
5326eedddabSSarah Walker 	struct pvr_hwrt_dataset *hwrt;
5336eedddabSSarah Walker 	unsigned long handle;
5346eedddabSSarah Walker 
5356eedddabSSarah Walker 	xa_for_each(&pvr_file->hwrt_handles, handle, hwrt) {
5366eedddabSSarah Walker 		(void)hwrt;
5376eedddabSSarah Walker 		pvr_hwrt_dataset_put(xa_erase(&pvr_file->hwrt_handles, handle));
5386eedddabSSarah Walker 	}
5396eedddabSSarah Walker }
5406eedddabSSarah Walker 
5416eedddabSSarah Walker /**
5426eedddabSSarah Walker  * pvr_hwrt_dataset_put() - Release reference on HWRT dataset
5436eedddabSSarah Walker  * @hwrt: Pointer to HWRT dataset to release reference on
5446eedddabSSarah Walker  */
5456eedddabSSarah Walker void
pvr_hwrt_dataset_put(struct pvr_hwrt_dataset * hwrt)5466eedddabSSarah Walker pvr_hwrt_dataset_put(struct pvr_hwrt_dataset *hwrt)
5476eedddabSSarah Walker {
5486eedddabSSarah Walker 	if (hwrt)
5496eedddabSSarah Walker 		kref_put(&hwrt->ref_count, pvr_hwrt_dataset_release);
5506eedddabSSarah Walker }
551