GCC Code Coverage Report


Directory: ./
File: src/ppd-action-amdgpu-panel-power.c
Date: 2024-09-13 00:56:02
Exec Total Coverage
Lines: 103 115 89.6%
Functions: 14 14 100.0%
Branches: 39 58 67.2%

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 "AmdgpuPanel"
11
12 #include "config.h"
13
14 #include <gudev/gudev.h>
15
16 #include "ppd-action-amdgpu-panel-power.h"
17 #include "ppd-profile.h"
18 #include "ppd-utils.h"
19
20 #define PANEL_POWER_SYSFS_NAME "amdgpu/panel_power_savings"
21 #define PANEL_STATUS_SYSFS_NAME "status"
22
23 /**
24 * SECTION:ppd-action-amdgpu-panel-power
25 * @Short_description: Power savings for eDP connected displays
26 * @Title: AMDGPU Panel power action
27 *
28 * The AMDGPU panel power action utilizes the sysfs attribute present on some DRM
29 * connectors for amdgpu called "panel_power_savings". This will use an AMD specific
30 * hardware feature for a power savings profile for the panel.
31 *
32 */
33
34 struct _PpdActionAmdgpuPanelPower
35 {
36 PpdAction parent_instance;
37 PpdProfile last_profile;
38
39 GUdevClient *client;
40
41 gint panel_power_saving;
42 gboolean valid_battery;
43 gboolean on_battery;
44 gdouble battery_level;
45 };
46
47
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 (PpdActionAmdgpuPanelPower, ppd_action_amdgpu_panel_power, PPD_TYPE_ACTION)
48
49 static GObject*
50 134 ppd_action_amdgpu_panel_power_constructor (GType type,
51 guint n_construct_params,
52 GObjectConstructParam *construct_params)
53 {
54 134 GObject *object;
55
56 134 object = G_OBJECT_CLASS (ppd_action_amdgpu_panel_power_parent_class)->constructor (type,
57 n_construct_params,
58 construct_params);
59 134 g_object_set (object,
60 "action-name", "amdgpu_panel_power",
61 NULL);
62
63 134 return object;
64 }
65
66 static gboolean
67 18 panel_connected (GUdevDevice *device)
68 {
69 18 const char *value;
70 36 g_autofree gchar *stripped = NULL;
71
72 18 value = g_udev_device_get_sysfs_attr_uncached (device, PANEL_STATUS_SYSFS_NAME);
73
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
18 if (!value)
74 return FALSE;
75
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
36 stripped = g_strchomp (g_strdup (value));
76
77 18 return g_strcmp0 (stripped, "connected") == 0;
78 }
79
80 static gboolean
81 14 set_panel_power (PpdActionAmdgpuPanelPower *self, gint power, GError **error)
82 {
83 14 GList *devices, *l;
84
85 14 devices = g_udev_client_query_by_subsystem (self->client, "drm");
86
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (devices == NULL) {
87 g_set_error_literal (error,
88 G_IO_ERROR,
89 G_IO_ERROR_NOT_FOUND,
90 "no drm devices found");
91 return FALSE;
92 }
93
94
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 6 times.
20 for (l = devices; l != NULL; l = l->next) {
95 14 GUdevDevice *dev = l->data;
96 14 const char *value;
97 14 guint64 parsed;
98
99 14 value = g_udev_device_get_devtype (dev);
100
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
14 if (g_strcmp0 (value, "drm_connector") != 0)
101 continue;
102
103
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
14 if (!panel_connected (dev))
104 continue;
105
106 14 value = g_udev_device_get_sysfs_attr_uncached (dev, PANEL_POWER_SYSFS_NAME);
107
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (!value)
108 continue;
109
110 14 parsed = g_ascii_strtoull (value, NULL, 10);
111
112 /* overflow check */
113
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (parsed == G_MAXUINT64) {
114 g_set_error (error,
115 G_IO_ERROR,
116 G_IO_ERROR_INVALID_DATA,
117 "cannot parse %s as caused overflow",
118 value);
119 return FALSE;
120 }
121
122
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 8 times.
14 if (parsed == power)
123 6 continue;
124
125
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 8 times.
8 if (!ppd_utils_write_sysfs_int (dev, PANEL_POWER_SYSFS_NAME, power, error))
126 return FALSE;
127
128 break;
129 }
130
131 14 g_list_free_full (devices, g_object_unref);
132
133 14 return TRUE;
134 }
135
136 static gboolean
137 14 ppd_action_amdgpu_panel_update_target (PpdActionAmdgpuPanelPower *self,
138 GError **error)
139 {
140 14 gint target = 0;
141
142 /* only activate if we know that we're on battery */
143
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 if (self->on_battery) {
144
2/3
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
14 switch (self->last_profile) {
145 6 case PPD_PROFILE_POWER_SAVER:
146
3/4
✓ Branch 0 taken 6 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 2 times.
6 if (!self->battery_level || self->battery_level >= 50)
147 target = 0;
148
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 2 times.
4 else if (self->battery_level > 30)
149 target = 1;
150
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 else if (self->battery_level > 20 && self->battery_level <= 30)
151 target = 2;
152 else /* < 20 */
153 2 target = 3;
154 break;
155 8 case PPD_PROFILE_BALANCED:
156
4/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 4 times.
8 if (!self->battery_level || self->battery_level >= 30)
157 target = 0;
158 else
159 4 target = 1;
160 break;
161 case PPD_PROFILE_PERFORMANCE:
162 target = 0;
163 break;
164 }
165 }
166
167 14 g_info("Updating panel to %d due to 🔋 %d (%f)", target, self->on_battery, self->battery_level);
168
1/2
✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
14 if (!set_panel_power (self, target, error))
169 return FALSE;
170 14 self->panel_power_saving = target;
171
172 14 return TRUE;
173 }
174
175 static gboolean
176 14 ppd_action_amdgpu_panel_power_activate_profile (PpdAction *action,
177 PpdProfile profile,
178 GError **error)
179 {
180 14 PpdActionAmdgpuPanelPower *self = PPD_ACTION_AMDGPU_PANEL_POWER (action);
181 14 self->last_profile = profile;
182
183
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 2 times.
14 if (!self->valid_battery) {
184 12 g_debug ("upower not available; battery data might be stale");
185 12 return TRUE;
186 }
187
188 2 return ppd_action_amdgpu_panel_update_target (self, error);
189 }
190
191 static gboolean
192 4 ppd_action_amdgpu_panel_power_power_changed (PpdAction *action,
193 PpdPowerChangedReason reason,
194 GError **error)
195 {
196 4 PpdActionAmdgpuPanelPower *self = PPD_ACTION_AMDGPU_PANEL_POWER (action);
197
198
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
4 switch (reason) {
199 2 case PPD_POWER_CHANGED_REASON_UNKNOWN:
200 2 self->valid_battery = FALSE;
201 2 return TRUE;
202 case PPD_POWER_CHANGED_REASON_AC:
203 self->on_battery = FALSE;
204 break;
205 2 case PPD_POWER_CHANGED_REASON_BATTERY:
206 2 self->on_battery = TRUE;
207 2 break;
208 default:
209 g_assert_not_reached ();
210 }
211
212 2 self->valid_battery = TRUE;
213
214 2 return ppd_action_amdgpu_panel_update_target (self, error);
215 }
216
217 static gboolean
218 10 ppd_action_amdgpu_panel_power_battery_changed (PpdAction *action,
219 gdouble val,
220 GError **error)
221 {
222 10 PpdActionAmdgpuPanelPower *self = PPD_ACTION_AMDGPU_PANEL_POWER (action);
223
224 10 self->battery_level = val;
225
226 10 return ppd_action_amdgpu_panel_update_target (self, error);
227 }
228
229 static void
230 4 udev_uevent_cb (GUdevClient *client,
231 gchar *action,
232 GUdevDevice *device,
233 gpointer user_data)
234 {
235 4 PpdActionAmdgpuPanelPower *self = user_data;
236
237
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
4 if (!g_str_equal (action, "add"))
238 return;
239
240
1/2
✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
4 if (!g_udev_device_has_sysfs_attr (device, PANEL_POWER_SYSFS_NAME))
241 return;
242
243
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 2 times.
4 if (!panel_connected (device))
244 return;
245
246 2 g_debug ("Updating panel power saving for '%s' to '%d'",
247 g_udev_device_get_sysfs_path (device),
248 self->panel_power_saving);
249 2 ppd_utils_write_sysfs_int (device, PANEL_POWER_SYSFS_NAME,
250 2 self->panel_power_saving, NULL);
251 }
252
253 static PpdProbeResult
254 132 ppd_action_amdgpu_panel_power_probe (PpdAction *action)
255 {
256 132 return ppd_utils_match_cpu_vendor ("AuthenticAMD") ? PPD_PROBE_RESULT_SUCCESS : PPD_PROBE_RESULT_FAIL;
257 }
258
259 static void
260 134 ppd_action_amdgpu_panel_power_finalize (GObject *object)
261 {
262 134 PpdActionAmdgpuPanelPower *action;
263
264 134 action = PPD_ACTION_AMDGPU_PANEL_POWER (object);
265
1/2
✓ Branch 0 taken 134 times.
✗ Branch 1 not taken.
134 g_clear_object (&action->client);
266 134 G_OBJECT_CLASS (ppd_action_amdgpu_panel_power_parent_class)->finalize (object);
267 134 }
268
269 static void
270 130 ppd_action_amdgpu_panel_power_class_init (PpdActionAmdgpuPanelPowerClass *klass)
271 {
272 130 GObjectClass *object_class;
273 130 PpdActionClass *driver_class;
274
275 130 object_class = G_OBJECT_CLASS(klass);
276 130 object_class->constructor = ppd_action_amdgpu_panel_power_constructor;
277 130 object_class->finalize = ppd_action_amdgpu_panel_power_finalize;
278
279 130 driver_class = PPD_ACTION_CLASS(klass);
280 130 driver_class->probe = ppd_action_amdgpu_panel_power_probe;
281 130 driver_class->activate_profile = ppd_action_amdgpu_panel_power_activate_profile;
282 130 driver_class->power_changed = ppd_action_amdgpu_panel_power_power_changed;
283 130 driver_class->battery_changed = ppd_action_amdgpu_panel_power_battery_changed;
284 }
285
286 static void
287 134 ppd_action_amdgpu_panel_power_init (PpdActionAmdgpuPanelPower *self)
288 {
289 134 const gchar * const subsystem[] = { "drm", NULL };
290
291 134 self->client = g_udev_client_new (subsystem);
292 134 g_signal_connect_object (G_OBJECT (self->client), "uevent",
293 G_CALLBACK (udev_uevent_cb), self, 0);
294 134 }
295