#include <linux/debugfs.h>
#include "xe_bo.h"
#include "xe_device_types.h"
#include "xe_configfs.h"
#include "xe_psmi.h"
static bool psmi_enabled(struct xe_device *xe)
{
return xe_configfs_get_psmi_enabled(to_pci_dev(xe->drm.dev));
}
static void psmi_free_object(struct xe_bo *bo)
{
xe_bo_lock(bo, NULL);
xe_bo_unpin(bo);
xe_bo_unlock(bo);
xe_bo_put(bo);
}
static void psmi_cleanup(struct xe_device *xe)
{
unsigned long id, region_mask = xe->psmi.region_mask;
struct xe_bo *bo;
for_each_set_bit(id, ®ion_mask,
ARRAY_SIZE(xe->psmi.capture_obj)) {
xe_assert(xe, id);
bo = xe->psmi.capture_obj[id];
if (bo) {
psmi_free_object(bo);
xe->psmi.capture_obj[id] = NULL;
}
}
}
static struct xe_bo *psmi_alloc_object(struct xe_device *xe,
unsigned int id, size_t bo_size)
{
struct xe_tile *tile;
xe_assert(xe, id);
xe_assert(xe, bo_size);
tile = &xe->tiles[id - 1];
return xe_bo_create_pin_range_novm(xe, tile, bo_size, 0, ~0ull,
ttm_bo_type_kernel,
XE_BO_FLAG_VRAM_IF_DGFX(tile) |
XE_BO_FLAG_PINNED |
XE_BO_FLAG_PINNED_LATE_RESTORE |
XE_BO_FLAG_NEEDS_CPU_ACCESS);
}
static int psmi_resize_object(struct xe_device *xe, size_t size)
{
unsigned long id, region_mask = xe->psmi.region_mask;
struct xe_bo *bo = NULL;
int err = 0;
psmi_cleanup(xe);
if (!size)
return 0;
for_each_set_bit(id, ®ion_mask,
ARRAY_SIZE(xe->psmi.capture_obj)) {
xe_assert(xe, id);
bo = psmi_alloc_object(xe, id, size);
if (IS_ERR(bo)) {
err = PTR_ERR(bo);
break;
}
xe->psmi.capture_obj[id] = bo;
drm_info(&xe->drm,
"PSMI capture size requested: %zu bytes, allocated: %lu:%zu\n",
size, id, bo ? xe_bo_size(bo) : 0);
}
if (err)
psmi_cleanup(xe);
return err;
}
static int psmi_debugfs_capture_addr_show(struct seq_file *m, void *data)
{
struct xe_device *xe = m->private;
unsigned long id, region_mask;
struct xe_bo *bo;
u64 val;
region_mask = xe->psmi.region_mask;
for_each_set_bit(id, ®ion_mask,
ARRAY_SIZE(xe->psmi.capture_obj)) {
xe_assert(xe, id);
bo = xe->psmi.capture_obj[id];
if (!bo)
continue;
val = __xe_bo_addr(bo, 0, PAGE_SIZE);
seq_printf(m, "%ld: 0x%llx\n", id, val);
}
return 0;
}
static int psmi_debugfs_capture_size_get(void *data, u64 *val)
{
unsigned long id, region_mask;
struct xe_device *xe = data;
struct xe_bo *bo;
region_mask = xe->psmi.region_mask;
for_each_set_bit(id, ®ion_mask,
ARRAY_SIZE(xe->psmi.capture_obj)) {
xe_assert(xe, id);
bo = xe->psmi.capture_obj[id];
if (bo) {
*val = xe_bo_size(bo);
return 0;
}
}
*val = 0;
return 0;
}
static int psmi_debugfs_capture_size_set(void *data, u64 val)
{
struct xe_device *xe = data;
if (!xe->psmi.region_mask)
return -EINVAL;
return psmi_resize_object(xe, val);
}
static int psmi_debugfs_capture_region_mask_get(void *data, u64 *val)
{
struct xe_device *xe = data;
*val = xe->psmi.region_mask;
return 0;
}
static int psmi_debugfs_capture_region_mask_set(void *data, u64 region_mask)
{
struct xe_device *xe = data;
u64 size = 0;
if (region_mask & 0x1)
return -EOPNOTSUPP;
if (!region_mask || region_mask & ~xe->info.mem_region_mask)
return -EINVAL;
psmi_debugfs_capture_size_get(xe, &size);
if (size)
return -EBUSY;
xe->psmi.region_mask = region_mask;
return 0;
}
DEFINE_SHOW_ATTRIBUTE(psmi_debugfs_capture_addr);
DEFINE_DEBUGFS_ATTRIBUTE(psmi_debugfs_capture_region_mask_fops,
psmi_debugfs_capture_region_mask_get,
psmi_debugfs_capture_region_mask_set,
"0x%llx\n");
DEFINE_DEBUGFS_ATTRIBUTE(psmi_debugfs_capture_size_fops,
psmi_debugfs_capture_size_get,
psmi_debugfs_capture_size_set,
"%lld\n");
void xe_psmi_debugfs_register(struct xe_device *xe)
{
struct drm_minor *minor;
if (!psmi_enabled(xe))
return;
minor = xe->drm.primary;
if (!minor->debugfs_root)
return;
debugfs_create_file("psmi_capture_addr",
0400, minor->debugfs_root, xe,
&psmi_debugfs_capture_addr_fops);
debugfs_create_file("psmi_capture_region_mask",
0600, minor->debugfs_root, xe,
&psmi_debugfs_capture_region_mask_fops);
debugfs_create_file("psmi_capture_size",
0600, minor->debugfs_root, xe,
&psmi_debugfs_capture_size_fops);
}
static void psmi_fini(void *arg)
{
psmi_cleanup(arg);
}
int xe_psmi_init(struct xe_device *xe)
{
if (!psmi_enabled(xe))
return 0;
return devm_add_action(xe->drm.dev, psmi_fini, xe);
}