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:
committed by
Guido Günther
parent
22e4c33606
commit
10e6d3cba8
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user