| // SPDX-License-Identifier: GPL-2.0-only |
| |
| /* Copyright (c) 2020-2025, The Linux Foundation. All rights reserved. */ |
| |
| #include <drm/drm_file.h> |
| #include <drm/drm_managed.h> |
| #include <linux/device.h> |
| #include <linux/kernel.h> |
| #include <linux/kobject.h> |
| #include <linux/mutex.h> |
| #include <linux/sysfs.h> |
| |
| #include "qaic.h" |
| |
| #define NAME_LEN 14 |
| |
| struct dbc_attribute { |
| struct device_attribute dev_attr; |
| u32 dbc_id; |
| char name[NAME_LEN]; |
| }; |
| |
| static ssize_t dbc_state_show(struct device *dev, struct device_attribute *a, char *buf) |
| { |
| struct dbc_attribute *dbc_attr = container_of(a, struct dbc_attribute, dev_attr); |
| struct drm_minor *minor = dev_get_drvdata(dev); |
| struct qaic_device *qdev; |
| |
| qdev = to_qaic_device(minor->dev); |
| return sysfs_emit(buf, "%d\n", qdev->dbc[dbc_attr->dbc_id].state); |
| } |
| |
| void set_dbc_state(struct qaic_device *qdev, u32 dbc_id, unsigned int state) |
| { |
| struct device *kdev = to_accel_kdev(qdev->qddev); |
| char *envp[3] = {}; |
| char state_str[16]; |
| char id_str[12]; |
| |
| envp[0] = id_str; |
| envp[1] = state_str; |
| |
| if (state >= DBC_STATE_MAX) |
| return; |
| if (dbc_id >= qdev->num_dbc) |
| return; |
| if (state == qdev->dbc[dbc_id].state) |
| return; |
| |
| scnprintf(id_str, ARRAY_SIZE(id_str), "DBC_ID=%d", dbc_id); |
| scnprintf(state_str, ARRAY_SIZE(state_str), "DBC_STATE=%d", state); |
| |
| qdev->dbc[dbc_id].state = state; |
| kobject_uevent_env(&kdev->kobj, KOBJ_CHANGE, envp); |
| } |
| |
| int qaic_sysfs_init(struct qaic_drm_device *qddev) |
| { |
| struct device *kdev = to_accel_kdev(qddev); |
| struct drm_device *drm = to_drm(qddev); |
| u32 num_dbc = qddev->qdev->num_dbc; |
| struct dbc_attribute *dbc_attrs; |
| int i, ret; |
| |
| dbc_attrs = drmm_kcalloc(drm, num_dbc, sizeof(*dbc_attrs), GFP_KERNEL); |
| if (!dbc_attrs) |
| return -ENOMEM; |
| |
| for (i = 0; i < num_dbc; ++i) { |
| struct dbc_attribute *dbc_attr = &dbc_attrs[i]; |
| |
| sysfs_attr_init(&dbc_attr->dev_attr.attr); |
| dbc_attr->dbc_id = i; |
| scnprintf(dbc_attr->name, NAME_LEN, "dbc%d_state", i); |
| dbc_attr->dev_attr.attr.name = dbc_attr->name; |
| dbc_attr->dev_attr.attr.mode = 0444; |
| dbc_attr->dev_attr.show = dbc_state_show; |
| ret = sysfs_create_file(&kdev->kobj, &dbc_attr->dev_attr.attr); |
| if (ret) { |
| int j; |
| |
| for (j = 0; j < i; ++j) { |
| dbc_attr = &dbc_attrs[j]; |
| sysfs_remove_file(&kdev->kobj, &dbc_attr->dev_attr.attr); |
| } |
| drmm_kfree(drm, dbc_attrs); |
| return ret; |
| } |
| } |
| |
| qddev->sysfs_attrs = dbc_attrs; |
| return 0; |
| } |
| |
| void qaic_sysfs_remove(struct qaic_drm_device *qddev) |
| { |
| struct dbc_attribute *dbc_attrs = qddev->sysfs_attrs; |
| struct device *kdev = to_accel_kdev(qddev); |
| u32 num_dbc = qddev->qdev->num_dbc; |
| int i; |
| |
| if (!dbc_attrs) |
| return; |
| |
| qddev->sysfs_attrs = NULL; |
| for (i = 0; i < num_dbc; ++i) |
| sysfs_remove_file(&kdev->kobj, &dbc_attrs[i].dev_attr.attr); |
| drmm_kfree(to_drm(qddev), dbc_attrs); |
| } |