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 ¶meter_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