fbd-dev-led-qcom: Support QCOM LPG LED single color

QCOM LPG LEDs expose a hw_pattern sysfs entry which accepts a limited
set of patterns but allows to use dedicated hardware for blinking the
LED. This way, battery life can be improved and the LED still blinks,
even if the device is suspended.
This commit is contained in:
Dylan Van Assche
2024-06-09 11:21:20 +02:00
committed by Guido Günther
parent 22e4c33606
commit 10e6d3cba8
6 changed files with 302 additions and 0 deletions
+16
View File
@@ -0,0 +1,16 @@
/*
* Copyright (C) 2023 Dylan Van Assche <me@dylanvanassche.be>
*
* SPDX-License-Identifier: GPL-3.0+
*/
#pragma once
#include "fbd-dev-led-qcom.h"
#include <glib-object.h>
G_BEGIN_DECLS
gboolean fbd_dev_led_probe_qcom (FbdDevLed *led, GError **error);
G_END_DECLS
+118
View File
@@ -0,0 +1,118 @@
/*
* Copyright (C) 2023 Dylan Van Assche <me@dylanvanassche.be>
*
* SPDX-License-Identifier: GPL-3.0+
*/
#define G_LOG_DOMAIN "fbd-dev-led-qcom"
#include "fbd-dev-led-priv.h"
#include "fbd-dev-led-qcom.h"
#include "fbd-dev-led-qcom-priv.h"
#include "fbd-enums.h"
#include "fbd-udev.h"
#include <gio/gio.h>
#define LED_HW_PATTERN_ATTR "hw_pattern"
#define LED_REPEAT_ATTR "repeat"
#define LED_REPEAT_INFINITY "-1"
#define QCOM_LPG_MAX_PAUSE_MS 511
typedef struct _FbdDevLedQcom {
FbdDevLed parent;
} FbdDevLedQcom;
G_DEFINE_TYPE (FbdDevLedQcom, fbd_dev_led_qcom, FBD_TYPE_DEV_LED)
static gboolean
fbd_dev_led_qcom_probe (FbdDevLed *led, GError **error)
{
GUdevDevice *dev = fbd_dev_led_get_device (led);
const gchar *name;
const gchar * const *index;
name = g_udev_device_get_name (dev);
index = g_udev_device_get_sysfs_attr_as_strv (dev, LED_HW_PATTERN_ATTR);
if (index == NULL) {
g_set_error (error,
G_FILE_ERROR, G_FILE_ERROR_FAILED,
"%s is no QCOM LED with HW support", name);
return FALSE;
}
return FBD_DEV_LED_CLASS (fbd_dev_led_qcom_parent_class)->probe(led, error);
}
static gboolean
fbd_dev_led_qcom_start_periodic (FbdDevLed *led,
guint max_brightness_percentage,
guint freq)
{
GUdevDevice *dev = fbd_dev_led_get_device (led);
gdouble max;
gdouble t;
g_autofree gchar *str = NULL;
g_autoptr (GError) err = NULL;
gboolean success;
g_return_val_if_fail (max_brightness_percentage <= 100.0, FALSE);
max = fbd_dev_led_get_max_brightness (led) * (max_brightness_percentage / 100.0);
g_debug ("Using hw_pattern interface for QCOM LPG LED");
/* QCOM LPG LEDs can only pause up to 511ms */
/* ms mHz T/2 */
t = 1000.0 * 1000.0 / freq / 2.0;
if (t > QCOM_LPG_MAX_PAUSE_MS)
t = QCOM_LPG_MAX_PAUSE_MS;
str = g_strdup_printf ("0 %d 0 0 %d %d %d 0\n", (gint)t, (gint)max, (gint)t, (gint)max);
success = fbd_udev_set_sysfs_path_attr_as_string (dev, LED_REPEAT_ATTR, LED_REPEAT_INFINITY, &err);
if (!success) {
g_warning ("Failed to set LED repeat: %s", err->message);
goto failure;
}
success = fbd_udev_set_sysfs_path_attr_as_string (dev, LED_HW_PATTERN_ATTR, str, &err);
if (!success) {
g_warning ("Failed to set LED hw_pattern: %s", err->message);
goto failure;
}
g_debug ("Freq %d mHz, Brightness: %d%%, Blink pattern: %s, HW: yes", freq, max_brightness_percentage, str);
return TRUE;
failure:
g_warning("Falling back to software pattern");
return FBD_DEV_LED_CLASS (fbd_dev_led_qcom_parent_class)->start_periodic (
led, max_brightness_percentage, freq);
}
static void
fbd_dev_led_qcom_class_init (FbdDevLedQcomClass *klass)
{
FbdDevLedClass *fbd_dev_led_class = FBD_DEV_LED_CLASS (klass);
fbd_dev_led_class->probe = fbd_dev_led_qcom_probe;
fbd_dev_led_class->start_periodic = fbd_dev_led_qcom_start_periodic;
}
static void
fbd_dev_led_qcom_init (FbdDevLedQcom *self)
{
}
FbdDevLed *
fbd_dev_led_qcom_new (GUdevDevice *dev, GError **err)
{
return g_initable_new (FBD_TYPE_DEV_LED_QCOM, NULL, err, "dev", dev, NULL);
}
+18
View File
@@ -0,0 +1,18 @@
/*
* Copyright (C) 2023 Dylan Van Assche <me@dylanvanassche.be>
*
* SPDX-License-Identifier: GPL-3.0+
*/
#pragma once
#include "fbd-dev-led.h"
G_BEGIN_DECLS
#define FBD_TYPE_DEV_LED_QCOM fbd_dev_led_qcom_get_type()
G_DECLARE_FINAL_TYPE (FbdDevLedQcom, fbd_dev_led_qcom, FBD, DEV_LED_QCOM, FbdDevLed)
FbdDevLed *fbd_dev_led_qcom_new (GUdevDevice *dev, GError **err);
G_END_DECLS
+1
View File
@@ -17,6 +17,7 @@ sources = [
'fbd-dev-sound.c',
'fbd-dev-led.c',
'fbd-dev-led-multicolor.c',
'fbd-dev-led-qcom.c',
'fbd-dev-leds.c',
'fbd-event.c',
'fbd-feedback-base.c',