Line | Branch | Exec | Source |
---|---|---|---|
1 | /* | ||
2 | * Copyright (c) 2024 Advanced Micro Devices | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms of the GNU General Public License version 3 as published by | ||
6 | * the Free Software Foundation. | ||
7 | * | ||
8 | */ | ||
9 | |||
10 | #define G_LOG_DOMAIN "AmdgpuDpm" | ||
11 | |||
12 | #include "config.h" | ||
13 | |||
14 | #include <gudev/gudev.h> | ||
15 | |||
16 | #include "ppd-action-amdgpu-dpm.h" | ||
17 | #include "ppd-profile.h" | ||
18 | #include "ppd-utils.h" | ||
19 | |||
20 | #define DPM_SYSFS_NAME "device/power_dpm_force_performance_level" | ||
21 | |||
22 | /** | ||
23 | * SECTION:ppd-action-amdgpu-dpm | ||
24 | * @Short_description: Power savings for GPU clocks | ||
25 | * @Title: AMDGPU DPM clock control | ||
26 | * | ||
27 | * The AMDGPU DPM clock control action utilizes the sysfs attribute present on some DRM | ||
28 | * connectors for amdgpu called "power_dpm_force_performance_level". | ||
29 | */ | ||
30 | |||
31 | struct _PpdActionAmdgpuDpm | ||
32 | { | ||
33 | PpdAction parent_instance; | ||
34 | PpdProfile last_profile; | ||
35 | |||
36 | GUdevClient *client; | ||
37 | }; | ||
38 | |||
39 |
4/5✓ Branch 0 taken 142 times.
✓ Branch 1 taken 16 times.
✓ Branch 2 taken 142 times.
✓ Branch 3 taken 142 times.
✗ Branch 4 not taken.
|
884 | G_DEFINE_TYPE (PpdActionAmdgpuDpm, ppd_action_amdgpu_dpm, PPD_TYPE_ACTION) |
40 | |||
41 | static GObject* | ||
42 | 158 | ppd_action_amdgpu_dpm_constructor (GType type, | |
43 | guint n_construct_params, | ||
44 | GObjectConstructParam *construct_params) | ||
45 | { | ||
46 | 158 | GObject *object; | |
47 | |||
48 | 158 | object = G_OBJECT_CLASS (ppd_action_amdgpu_dpm_parent_class)->constructor (type, | |
49 | n_construct_params, | ||
50 | construct_params); | ||
51 | 158 | g_object_set (object, | |
52 | "action-name", "amdgpu_dpm", | ||
53 | "action-description", "Adjust GPU dynamic power management", | ||
54 | "action-optin", TRUE, | ||
55 | NULL); | ||
56 | |||
57 | 158 | return object; | |
58 | } | ||
59 | |||
60 | static gboolean | ||
61 | 4 | ppd_action_amdgpu_dpm_update_target (PpdActionAmdgpuDpm *self, GError **error) | |
62 | { | ||
63 | 8 | g_autolist (GUdevDevice) devices = NULL; | |
64 | 4 | const gchar *target; | |
65 | |||
66 |
2/3✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
|
4 | switch (self->last_profile) { |
67 | case PPD_PROFILE_POWER_SAVER: | ||
68 | target = "low"; | ||
69 | break; | ||
70 | 2 | case PPD_PROFILE_BALANCED: | |
71 | case PPD_PROFILE_PERFORMANCE: | ||
72 | 2 | target = "auto"; | |
73 | 2 | break; | |
74 | ✗ | default: | |
75 | ✗ | g_assert_not_reached (); | |
76 | } | ||
77 | |||
78 | 4 | devices = g_udev_client_query_by_subsystem (self->client, "drm"); | |
79 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | if (devices == NULL) { |
80 | ✗ | g_set_error_literal (error, | |
81 | G_IO_ERROR, | ||
82 | G_IO_ERROR_NOT_FOUND, | ||
83 | "no drm devices found"); | ||
84 | ✗ | return FALSE; | |
85 | } | ||
86 | |||
87 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 4 times.
|
8 | for (GList *l = devices; l != NULL; l = l->next) { |
88 | 4 | GUdevDevice *dev = l->data; | |
89 | 4 | const char *value; | |
90 | |||
91 | 4 | value = g_udev_device_get_devtype (dev); | |
92 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
|
4 | if (g_strcmp0 (value, "drm_minor") != 0) |
93 | ✗ | continue; | |
94 | |||
95 | 4 | value = g_udev_device_get_sysfs_attr_uncached (dev, DPM_SYSFS_NAME); | |
96 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | if (!value) |
97 | ✗ | continue; | |
98 | |||
99 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 3 times.
|
4 | if (g_strcmp0 (value, target) == 0) { |
100 | 1 | g_info ("Device %s already set to %s", g_udev_device_get_sysfs_path (dev), target); | |
101 | 1 | continue; | |
102 | } | ||
103 | |||
104 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1 times.
|
3 | if (g_strcmp0 (value, "manual") == 0) { |
105 | 2 | g_info ("Device %s is in manual mode, not changing", g_udev_device_get_sysfs_path (dev)); | |
106 | 2 | continue; | |
107 | } | ||
108 | |||
109 | 1 | g_info ("Setting device %s to %s", g_udev_device_get_sysfs_path (dev), target); | |
110 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
|
1 | if (!ppd_utils_write_sysfs (dev, DPM_SYSFS_NAME, target, error)) |
111 | return FALSE; | ||
112 | } | ||
113 | |||
114 | return TRUE; | ||
115 | } | ||
116 | |||
117 | static gboolean | ||
118 | 4 | ppd_action_amdgpu_dpm_activate_profile (PpdAction *action, | |
119 | PpdProfile profile, | ||
120 | GError **error) | ||
121 | { | ||
122 | 4 | PpdActionAmdgpuDpm *self = PPD_ACTION_AMDGPU_DPM (action); | |
123 | 4 | self->last_profile = profile; | |
124 | |||
125 | 4 | return ppd_action_amdgpu_dpm_update_target (self, error); | |
126 | } | ||
127 | |||
128 | static void | ||
129 | 2 | udev_uevent_cb (GUdevClient *client, | |
130 | gchar *action, | ||
131 | GUdevDevice *device, | ||
132 | gpointer user_data) | ||
133 | { | ||
134 | 2 | PpdActionAmdgpuDpm *self = user_data; | |
135 | |||
136 | 2 | g_debug ("Device %s %s", g_udev_device_get_sysfs_path (device), action); | |
137 | |||
138 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
2 | if (!g_str_equal (action, "add")) |
139 | return; | ||
140 | |||
141 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
|
2 | if (!g_udev_device_has_sysfs_attr (device, DPM_SYSFS_NAME)) |
142 | return; | ||
143 | |||
144 | ✗ | ppd_action_amdgpu_dpm_update_target (self, NULL); | |
145 | } | ||
146 | |||
147 | static PpdProbeResult | ||
148 | 2 | ppd_action_amdgpu_dpm_probe (PpdAction *action) | |
149 | { | ||
150 | 2 | return ppd_utils_match_cpu_vendor ("AuthenticAMD") ? | |
151 | 2 | PPD_PROBE_RESULT_SUCCESS : PPD_PROBE_RESULT_FAIL; | |
152 | } | ||
153 | |||
154 | static void | ||
155 | 158 | ppd_action_amdgpu_dpm_finalize (GObject *object) | |
156 | { | ||
157 | 158 | PpdActionAmdgpuDpm *action; | |
158 | |||
159 | 158 | action = PPD_ACTION_AMDGPU_DPM (object); | |
160 |
1/2✓ Branch 0 taken 158 times.
✗ Branch 1 not taken.
|
158 | g_clear_object (&action->client); |
161 | 158 | G_OBJECT_CLASS (ppd_action_amdgpu_dpm_parent_class)->finalize (object); | |
162 | 158 | } | |
163 | |||
164 | static void | ||
165 | 142 | ppd_action_amdgpu_dpm_class_init (PpdActionAmdgpuDpmClass *klass) | |
166 | { | ||
167 | 142 | GObjectClass *object_class; | |
168 | 142 | PpdActionClass *driver_class; | |
169 | |||
170 | 142 | object_class = G_OBJECT_CLASS(klass); | |
171 | 142 | object_class->constructor = ppd_action_amdgpu_dpm_constructor; | |
172 | 142 | object_class->finalize = ppd_action_amdgpu_dpm_finalize; | |
173 | |||
174 | 142 | driver_class = PPD_ACTION_CLASS(klass); | |
175 | 142 | driver_class->probe = ppd_action_amdgpu_dpm_probe; | |
176 | 142 | driver_class->activate_profile = ppd_action_amdgpu_dpm_activate_profile; | |
177 | } | ||
178 | |||
179 | static void | ||
180 | 158 | ppd_action_amdgpu_dpm_init (PpdActionAmdgpuDpm *self) | |
181 | { | ||
182 | 158 | const gchar * const subsystem[] = { "drm", NULL }; | |
183 | |||
184 | 158 | self->client = g_udev_client_new (subsystem); | |
185 | 158 | g_signal_connect_object (G_OBJECT (self->client), "uevent", | |
186 | G_CALLBACK (udev_uevent_cb), self, 0); | ||
187 | 158 | } | |
188 |