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 130 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 130 times.
✓ Branch 3 taken 130 times.
✗ Branch 4 not taken.
|
788 | G_DEFINE_TYPE (PpdActionAmdgpuDpm, ppd_action_amdgpu_dpm, PPD_TYPE_ACTION) |
40 | |||
41 | static GObject* | ||
42 | 134 | ppd_action_amdgpu_dpm_constructor (GType type, | |
43 | guint n_construct_params, | ||
44 | GObjectConstructParam *construct_params) | ||
45 | { | ||
46 | 134 | GObject *object; | |
47 | |||
48 | 134 | object = G_OBJECT_CLASS (ppd_action_amdgpu_dpm_parent_class)->constructor (type, | |
49 | n_construct_params, | ||
50 | construct_params); | ||
51 | 134 | g_object_set (object, | |
52 | "action-name", "amdgpu_dpm", | ||
53 | NULL); | ||
54 | |||
55 | 134 | return object; | |
56 | } | ||
57 | |||
58 | static gboolean | ||
59 | 16 | ppd_action_amdgpu_dpm_update_target (PpdActionAmdgpuDpm *self, GError **error) | |
60 | { | ||
61 | 32 | g_autolist (GUdevDevice) devices = NULL; | |
62 | 16 | const gchar *target; | |
63 | |||
64 |
2/3✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
|
16 | switch (self->last_profile) { |
65 | case PPD_PROFILE_POWER_SAVER: | ||
66 | target = "low"; | ||
67 | break; | ||
68 | 10 | case PPD_PROFILE_BALANCED: | |
69 | case PPD_PROFILE_PERFORMANCE: | ||
70 | 10 | target = "auto"; | |
71 | 10 | break; | |
72 | ✗ | default: | |
73 | ✗ | g_assert_not_reached (); | |
74 | } | ||
75 | |||
76 | 16 | devices = g_udev_client_query_by_subsystem (self->client, "drm"); | |
77 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
|
16 | if (devices == NULL) { |
78 | ✗ | g_set_error_literal (error, | |
79 | G_IO_ERROR, | ||
80 | G_IO_ERROR_NOT_FOUND, | ||
81 | "no drm devices found"); | ||
82 | ✗ | return FALSE; | |
83 | } | ||
84 | |||
85 |
2/2✓ Branch 0 taken 16 times.
✓ Branch 1 taken 16 times.
|
32 | for (GList *l = devices; l != NULL; l = l->next) { |
86 | 16 | GUdevDevice *dev = l->data; | |
87 | 16 | const char *value; | |
88 | |||
89 | 16 | value = g_udev_device_get_devtype (dev); | |
90 |
2/2✓ Branch 1 taken 8 times.
✓ Branch 2 taken 8 times.
|
16 | if (g_strcmp0 (value, "drm_minor") != 0) |
91 | 8 | continue; | |
92 | |||
93 | 8 | value = g_udev_device_get_sysfs_attr_uncached (dev, DPM_SYSFS_NAME); | |
94 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
|
8 | if (!value) |
95 | ✗ | continue; | |
96 | |||
97 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 6 times.
|
8 | if (g_strcmp0 (value, target) == 0) { |
98 | 2 | g_info ("Device %s already set to %s", g_udev_device_get_sysfs_path (dev), target); | |
99 | 2 | continue; | |
100 | } | ||
101 | |||
102 |
2/2✓ Branch 1 taken 4 times.
✓ Branch 2 taken 2 times.
|
6 | if (g_strcmp0 (value, "manual") == 0) { |
103 | 4 | g_info ("Device %s is in manual mode, not changing", g_udev_device_get_sysfs_path (dev)); | |
104 | 4 | continue; | |
105 | } | ||
106 | |||
107 | 2 | g_info ("Setting device %s to %s", g_udev_device_get_sysfs_path (dev), target); | |
108 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
|
2 | if (!ppd_utils_write_sysfs (dev, DPM_SYSFS_NAME, target, error)) |
109 | return FALSE; | ||
110 | } | ||
111 | |||
112 | return TRUE; | ||
113 | } | ||
114 | |||
115 | static gboolean | ||
116 | 16 | ppd_action_amdgpu_dpm_activate_profile (PpdAction *action, | |
117 | PpdProfile profile, | ||
118 | GError **error) | ||
119 | { | ||
120 | 16 | PpdActionAmdgpuDpm *self = PPD_ACTION_AMDGPU_DPM (action); | |
121 | 16 | self->last_profile = profile; | |
122 | |||
123 | 16 | return ppd_action_amdgpu_dpm_update_target (self, error); | |
124 | } | ||
125 | |||
126 | static void | ||
127 | 4 | udev_uevent_cb (GUdevClient *client, | |
128 | gchar *action, | ||
129 | GUdevDevice *device, | ||
130 | gpointer user_data) | ||
131 | { | ||
132 | 4 | PpdActionAmdgpuDpm *self = user_data; | |
133 | |||
134 | 4 | g_debug ("Device %s %s", g_udev_device_get_sysfs_path (device), action); | |
135 | |||
136 |
1/2✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
|
4 | if (!g_str_equal (action, "add")) |
137 | return; | ||
138 | |||
139 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
|
4 | if (!g_udev_device_has_sysfs_attr (device, DPM_SYSFS_NAME)) |
140 | return; | ||
141 | |||
142 | ✗ | ppd_action_amdgpu_dpm_update_target (self, NULL); | |
143 | } | ||
144 | |||
145 | static PpdProbeResult | ||
146 | 134 | ppd_action_amdgpu_dpm_probe (PpdAction *action) | |
147 | { | ||
148 | 134 | return ppd_utils_match_cpu_vendor ("AuthenticAMD") ? | |
149 | 134 | PPD_PROBE_RESULT_SUCCESS : PPD_PROBE_RESULT_FAIL; | |
150 | } | ||
151 | |||
152 | static void | ||
153 | 134 | ppd_action_amdgpu_dpm_finalize (GObject *object) | |
154 | { | ||
155 | 134 | PpdActionAmdgpuDpm *action; | |
156 | |||
157 | 134 | action = PPD_ACTION_AMDGPU_DPM (object); | |
158 |
1/2✓ Branch 0 taken 134 times.
✗ Branch 1 not taken.
|
134 | g_clear_object (&action->client); |
159 | 134 | G_OBJECT_CLASS (ppd_action_amdgpu_dpm_parent_class)->finalize (object); | |
160 | 134 | } | |
161 | |||
162 | static void | ||
163 | 130 | ppd_action_amdgpu_dpm_class_init (PpdActionAmdgpuDpmClass *klass) | |
164 | { | ||
165 | 130 | GObjectClass *object_class; | |
166 | 130 | PpdActionClass *driver_class; | |
167 | |||
168 | 130 | object_class = G_OBJECT_CLASS(klass); | |
169 | 130 | object_class->constructor = ppd_action_amdgpu_dpm_constructor; | |
170 | 130 | object_class->finalize = ppd_action_amdgpu_dpm_finalize; | |
171 | |||
172 | 130 | driver_class = PPD_ACTION_CLASS(klass); | |
173 | 130 | driver_class->probe = ppd_action_amdgpu_dpm_probe; | |
174 | 130 | driver_class->activate_profile = ppd_action_amdgpu_dpm_activate_profile; | |
175 | } | ||
176 | |||
177 | static void | ||
178 | 134 | ppd_action_amdgpu_dpm_init (PpdActionAmdgpuDpm *self) | |
179 | { | ||
180 | 134 | const gchar * const subsystem[] = { "drm", NULL }; | |
181 | |||
182 | 134 | self->client = g_udev_client_new (subsystem); | |
183 | 134 | g_signal_connect_object (G_OBJECT (self->client), "uevent", | |
184 | G_CALLBACK (udev_uevent_cb), self, 0); | ||
185 | 134 | } | |
186 |