Merge 843b99b69b
into 709441412a
This commit is contained in:
commit
3661b18d4f
17 changed files with 1168 additions and 381 deletions
|
@ -27,7 +27,8 @@ target_sources(app PRIVATE src/matrix_transform.c)
|
|||
target_sources(app PRIVATE src/sensors.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_WPM app PRIVATE src/wpm.c)
|
||||
target_sources(app PRIVATE src/event_manager.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/ext_power_generic.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/power_domain.c)
|
||||
target_sources_ifdef(CONFIG_ZMK_EXT_POWER app PRIVATE src/dev_pm_policy.c)
|
||||
target_sources(app PRIVATE src/events/activity_state_changed.c)
|
||||
target_sources(app PRIVATE src/events/position_state_changed.c)
|
||||
target_sources(app PRIVATE src/events/sensor_event.c)
|
||||
|
|
33
app/Kconfig
33
app/Kconfig
|
@ -280,6 +280,10 @@ endmenu
|
|||
|
||||
menu "Power Management"
|
||||
|
||||
config ZMK_EXT_POWER
|
||||
bool "Enable support to control external power output"
|
||||
default y
|
||||
|
||||
config ZMK_IDLE_TIMEOUT
|
||||
int "Milliseconds of inactivity before entering idle state (OLED shutoff, etc)"
|
||||
default 30000
|
||||
|
@ -288,22 +292,35 @@ config ZMK_SLEEP
|
|||
bool "Enable deep sleep support"
|
||||
imply USB
|
||||
|
||||
if ZMK_SLEEP
|
||||
config POWER_DOMAIN
|
||||
default y
|
||||
|
||||
if ZMK_EXT_POWER
|
||||
|
||||
config POWER_DOMAIN_GPIO
|
||||
default y
|
||||
|
||||
endif
|
||||
|
||||
config PM_DEVICE
|
||||
default y
|
||||
|
||||
config PM_DEVICE_POWER_DOMAIN
|
||||
default y
|
||||
|
||||
config PM_DEVICE_POWER_DOMAIN_DYNAMIC
|
||||
default y
|
||||
|
||||
config PM_DEVICE_POWER_DOMAIN_DYNAMIC_NUM
|
||||
default 10
|
||||
|
||||
config PM_DEVICE_RUNTIME
|
||||
default y
|
||||
|
||||
config ZMK_IDLE_SLEEP_TIMEOUT
|
||||
int "Milliseconds of inactivity before entering deep sleep"
|
||||
default 900000
|
||||
|
||||
#ZMK_SLEEP
|
||||
endif
|
||||
|
||||
config ZMK_EXT_POWER
|
||||
bool "Enable support to control external power output"
|
||||
default y
|
||||
|
||||
#Power Management
|
||||
endmenu
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
/ {
|
||||
chosen {
|
||||
zmk,battery = &vbatt;
|
||||
zmk,default-power-domain = &pd_ext_power;
|
||||
};
|
||||
|
||||
ext-power {
|
||||
|
@ -18,6 +19,12 @@
|
|||
control-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
|
||||
pd_ext_power: pd_ext_power {
|
||||
compatible = "power-domain-gpio";
|
||||
label = "PD_EXT_POWER";
|
||||
enable-gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
|
||||
vbatt: vbatt {
|
||||
compatible = "zmk,battery-voltage-divider";
|
||||
label = "BATTERY";
|
||||
|
|
|
@ -45,6 +45,7 @@
|
|||
|
||||
&i2c0 {
|
||||
compatible = "nordic,nrf-twi";
|
||||
power-domain = <&pd_ext_power>;
|
||||
sda-pin = <17>;
|
||||
scl-pin = <20>;
|
||||
};
|
||||
|
|
|
@ -10,13 +10,14 @@
|
|||
/ {
|
||||
chosen {
|
||||
zmk,battery = &vbatt;
|
||||
zmk,default-power-domain = &pd_ext_power;
|
||||
};
|
||||
|
||||
ext-power {
|
||||
compatible = "zmk,ext-power-generic";
|
||||
label = "EXT_POWER";
|
||||
control-gpios = <&gpio0 13 GPIO_ACTIVE_HIGH>;
|
||||
init-delay-ms = <50>;
|
||||
pd_ext_power: pd_ext_power {
|
||||
compatible = "power-domain-gpio";
|
||||
label = "PD_EXT_POWER";
|
||||
enable-gpios = <&gpio0 13 GPIO_ACTIVE_HIGH>;
|
||||
startup-delay-us = <50000>;
|
||||
};
|
||||
|
||||
vbatt: vbatt {
|
||||
|
|
|
@ -6,3 +6,11 @@ description: External power control Behavior
|
|||
compatible: "zmk,behavior-ext-power"
|
||||
|
||||
include: one_param.yaml
|
||||
|
||||
properties:
|
||||
power-domain:
|
||||
required: false
|
||||
type: phandle
|
||||
description: |
|
||||
|
||||
Power domain the behavior should execute the action on.
|
||||
|
|
32
app/dts/bindings/zmk,dev-pm-policy.yaml
Normal file
32
app/dts/bindings/zmk,dev-pm-policy.yaml
Normal file
|
@ -0,0 +1,32 @@
|
|||
# Copyright (c) 2020 The ZMK Contributors
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
description: Device Power Management Policy
|
||||
|
||||
compatible: "zmk,dev-pm-policy"
|
||||
|
||||
properties:
|
||||
label:
|
||||
type: string
|
||||
required: true
|
||||
|
||||
device:
|
||||
type: phandle
|
||||
required: true
|
||||
description: |
|
||||
|
||||
The device or power domain on which the power management policy should be run on.
|
||||
|
||||
auto-off-on-idle:
|
||||
type: boolean
|
||||
required: false
|
||||
description: |
|
||||
|
||||
Automatically turn off a device or power domain when the keyboard is idle.
|
||||
|
||||
usb-auto-toggle:
|
||||
type: boolean
|
||||
required: false
|
||||
description: |
|
||||
|
||||
Automatically turn off a device or power domain when usb is disconnected and turn on when usb is connected.
|
|
@ -1,104 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <zephyr/types.h>
|
||||
#include <stddef.h>
|
||||
#include <device.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @cond INTERNAL_HIDDEN
|
||||
*
|
||||
* Behavior driver API definition and system call entry points.
|
||||
*
|
||||
* (Internal use only.)
|
||||
*/
|
||||
|
||||
typedef int (*ext_power_enable_t)(const struct device *dev);
|
||||
typedef int (*ext_power_disable_t)(const struct device *dev);
|
||||
typedef int (*ext_power_get_t)(const struct device *dev);
|
||||
|
||||
__subsystem struct ext_power_api {
|
||||
ext_power_enable_t enable;
|
||||
ext_power_disable_t disable;
|
||||
ext_power_get_t get;
|
||||
};
|
||||
/**
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Enable the external power output
|
||||
* @param dev Pointer to the device structure for the driver instance.
|
||||
*
|
||||
* @retval 0 If successful.
|
||||
* @retval Negative errno code if failure.
|
||||
*/
|
||||
__syscall int ext_power_enable(const struct device *dev);
|
||||
|
||||
static inline int z_impl_ext_power_enable(const struct device *dev) {
|
||||
const struct ext_power_api *api = (const struct ext_power_api *)dev->api;
|
||||
|
||||
if (api->enable == NULL) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return api->enable(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disable the external power output
|
||||
* @param dev Pointer to the device structure for the driver instance.
|
||||
*
|
||||
* @retval 0 If successful.
|
||||
* @retval Negative errno code if failure.
|
||||
*/
|
||||
__syscall int ext_power_disable(const struct device *dev);
|
||||
|
||||
static inline int z_impl_ext_power_disable(const struct device *dev) {
|
||||
const struct ext_power_api *api = (const struct ext_power_api *)dev->api;
|
||||
|
||||
if (api->disable == NULL) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return api->disable(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the current status of the external power output
|
||||
* @param dev Pointer to the device structure for the driver instance.
|
||||
*
|
||||
* @retval 0 If ext power is disabled.
|
||||
* @retval 1 if ext power is enabled.
|
||||
* @retval Negative errno code if failure.
|
||||
*/
|
||||
__syscall int ext_power_get(const struct device *dev);
|
||||
|
||||
static inline int z_impl_ext_power_get(const struct device *dev) {
|
||||
const struct ext_power_api *api = (const struct ext_power_api *)dev->api;
|
||||
|
||||
if (api->get == NULL) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return api->get(dev);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <syscalls/ext_power.h>
|
89
app/include/zmk/power_domain.h
Normal file
89
app/include/zmk/power_domain.h
Normal file
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright (c) 2020 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <pm/device.h>
|
||||
|
||||
#pragma once
|
||||
|
||||
#if CONFIG_ZMK_EXT_POWER
|
||||
|
||||
const int zmk_power_domain_disable(const struct device *pd_dev, bool save_state);
|
||||
const int zmk_power_domain_enable(const struct device *pd_dev, bool save_state);
|
||||
const int zmk_power_domain_toggle(const struct device *pd_dev, bool save_state);
|
||||
|
||||
|
||||
/*
|
||||
* What's the difference between get_state_actual and get_state_user_intended?
|
||||
*
|
||||
* We distinguish whether power is actually on and whether the user wants the
|
||||
* power to be on.
|
||||
*
|
||||
* For example, when the user turns on the power, the user intended state is
|
||||
* ON, bt internal features may despite that turn the power off temporarily.
|
||||
*
|
||||
* For example, the auto-off-on-idle feature cuts the power on idle and changes
|
||||
* the actual state to on, but the intended state stays as on. When activity
|
||||
* resumes, it checks whether the user wants the power to be on, and if yes,
|
||||
* enables the actual power again.
|
||||
*
|
||||
*/
|
||||
const int zmk_power_domain_get_state_actual(const struct device *pd_dev);
|
||||
const int zmk_power_domain_get_state_user_intended(const struct device *pd_dev);
|
||||
|
||||
|
||||
/*
|
||||
* Power Domain manager helper function
|
||||
*
|
||||
* In order to receive power domain notifications in ZMK devices, such as the
|
||||
* display and rgb_underglow, we need to create a power domain manager device
|
||||
* that has a power domain configured.
|
||||
*
|
||||
* This sets the power domain to a dynamically created device and syncs the
|
||||
* state of the device to the power domain.
|
||||
*/
|
||||
int zmk_power_domain_init_power_domain_manager_helper(const struct device *dev, const struct device *pd_dev);
|
||||
|
||||
/**
|
||||
* @cond INTERNAL_HIDDEN
|
||||
*
|
||||
* Internal helper functions
|
||||
*/
|
||||
|
||||
#define DEFAULT_POWER_DOMAIN DT_CHOSEN(zmk_default_power_domain)
|
||||
|
||||
/** @brief ZMK Power Domain Actions. */
|
||||
enum zmk_power_domain_action {
|
||||
/** Turn the Power Domain off. */
|
||||
ZMK_PD_ACTION_TURN_OFF,
|
||||
/** Turn the Power Domain on. */
|
||||
ZMK_PD_ACTION_TURN_ON,
|
||||
/** Toggle the Power Domain*/
|
||||
ZMK_PD_ACTION_TOGGLE,
|
||||
};
|
||||
const int zmk_power_domain_run_action(const struct device *pd_dev, enum zmk_power_domain_action zmk_action, bool save_state);
|
||||
int zmk_power_domain_save_state();
|
||||
|
||||
const struct device * zmk_power_domain_get_default();
|
||||
struct zmk_power_domain_data *zmk_power_domain_get_pd_data_for_pd(const struct device *pd_dev);
|
||||
struct zmk_power_domain_data *zmk_power_domain_get_pd_data_by_name(const char *name);
|
||||
void zmk_power_domain_set_pd_data_default();
|
||||
const char *zmk_pm_device_action_str(enum pm_device_action action);
|
||||
const char *zmk_pm_action_str(enum zmk_power_domain_action action);
|
||||
|
||||
/**
|
||||
* @endcond
|
||||
*/
|
||||
|
||||
#else
|
||||
|
||||
const inline int zmk_power_domain_disable(const struct device *pd_dev, bool save_state) { return 0; };
|
||||
const inline int zmk_power_domain_enable(const struct device *pd_dev, bool save_state) { return 0; };
|
||||
const inline int zmk_power_domain_toggle(const struct device *pd_dev, bool save_state) { return 0; };
|
||||
const inline int zmk_power_domain_get_state_actual(const struct device *pd_dev) { return 0; };
|
||||
const inline int zmk_power_domain_get_state_user_intended(const struct device *pd_dev) { return 0; };
|
||||
inline int zmk_power_domain_init_power_domain_manager_helper(const struct device *dev, const struct device *pd_dev) { return 0; };
|
||||
|
||||
#endif
|
|
@ -16,6 +16,8 @@ int zmk_rgb_underglow_toggle();
|
|||
int zmk_rgb_underglow_get_state(bool *state);
|
||||
int zmk_rgb_underglow_on();
|
||||
int zmk_rgb_underglow_off();
|
||||
int zmk_rgb_underglow_resume();
|
||||
int zmk_rgb_underglow_pause();
|
||||
int zmk_rgb_underglow_cycle_effect(int direction);
|
||||
int zmk_rgb_underglow_calc_effect(int direction);
|
||||
int zmk_rgb_underglow_select_effect(int effect);
|
||||
|
@ -26,4 +28,6 @@ int zmk_rgb_underglow_change_hue(int direction);
|
|||
int zmk_rgb_underglow_change_sat(int direction);
|
||||
int zmk_rgb_underglow_change_brt(int direction);
|
||||
int zmk_rgb_underglow_change_spd(int direction);
|
||||
int zmk_rgb_underglow_set_hsb(struct zmk_led_hsb color);
|
||||
int zmk_rgb_underglow_set_hsb(struct zmk_led_hsb color);
|
||||
|
||||
int zmk_rgb_underglow_init_power_domain_manager(const struct device *dev);
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
#include <device.h>
|
||||
#include <devicetree.h>
|
||||
#include <drivers/behavior.h>
|
||||
#include <drivers/ext_power.h>
|
||||
#include <zmk/power_domain.h>
|
||||
|
||||
#include <dt-bindings/zmk/ext_power.h>
|
||||
|
||||
|
@ -18,17 +18,18 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
|||
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
|
||||
struct zmk_behavior_ext_power_config {
|
||||
const struct device *pd;
|
||||
};
|
||||
|
||||
static int
|
||||
on_keymap_binding_convert_central_state_dependent_params(struct zmk_behavior_binding *binding,
|
||||
struct zmk_behavior_binding_event event) {
|
||||
const struct device *ext_power = device_get_binding("EXT_POWER");
|
||||
if (ext_power == NULL) {
|
||||
LOG_ERR("Unable to retrieve ext_power device: %d", binding->param1);
|
||||
return -EIO;
|
||||
}
|
||||
const struct device *dev = device_get_binding(binding->behavior_dev);
|
||||
const struct zmk_behavior_ext_power_config *config = (struct zmk_behavior_ext_power_config *)dev->config;
|
||||
|
||||
if (binding->param1 == EXT_POWER_TOGGLE_CMD) {
|
||||
binding->param1 = ext_power_get(ext_power) > 0 ? EXT_POWER_OFF_CMD : EXT_POWER_ON_CMD;
|
||||
binding->param1 = zmk_power_domain_get_state_actual(config->pd) == 1 ? EXT_POWER_OFF_CMD : EXT_POWER_ON_CMD;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -36,22 +37,17 @@ on_keymap_binding_convert_central_state_dependent_params(struct zmk_behavior_bin
|
|||
|
||||
static int on_keymap_binding_pressed(struct zmk_behavior_binding *binding,
|
||||
struct zmk_behavior_binding_event event) {
|
||||
const struct device *ext_power = device_get_binding("EXT_POWER");
|
||||
if (ext_power == NULL) {
|
||||
LOG_ERR("Unable to retrieve ext_power device: %d", binding->param1);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
const struct device *dev = device_get_binding(binding->behavior_dev);
|
||||
const struct zmk_behavior_ext_power_config *config = (struct zmk_behavior_ext_power_config *)dev->config;
|
||||
|
||||
switch (binding->param1) {
|
||||
case EXT_POWER_OFF_CMD:
|
||||
return ext_power_disable(ext_power);
|
||||
return zmk_power_domain_disable(config->pd, true);
|
||||
case EXT_POWER_ON_CMD:
|
||||
return ext_power_enable(ext_power);
|
||||
return zmk_power_domain_enable(config->pd, true);
|
||||
case EXT_POWER_TOGGLE_CMD:
|
||||
if (ext_power_get(ext_power) > 0)
|
||||
return ext_power_disable(ext_power);
|
||||
else
|
||||
return ext_power_enable(ext_power);
|
||||
zmk_power_domain_toggle(config->pd, true);
|
||||
default:
|
||||
LOG_ERR("Unknown ext_power command: %d", binding->param1);
|
||||
}
|
||||
|
@ -64,7 +60,9 @@ static int on_keymap_binding_released(struct zmk_behavior_binding *binding,
|
|||
return ZMK_BEHAVIOR_OPAQUE;
|
||||
}
|
||||
|
||||
static int behavior_ext_power_init(const struct device *dev) { return 0; };
|
||||
static int behavior_ext_power_init(const struct device *dev) {
|
||||
return 0;
|
||||
};
|
||||
|
||||
static const struct behavior_driver_api behavior_ext_power_driver_api = {
|
||||
.binding_convert_central_state_dependent_params =
|
||||
|
@ -74,7 +72,25 @@ static const struct behavior_driver_api behavior_ext_power_driver_api = {
|
|||
.locality = BEHAVIOR_LOCALITY_GLOBAL,
|
||||
};
|
||||
|
||||
DEVICE_DT_INST_DEFINE(0, behavior_ext_power_init, NULL, NULL, NULL, APPLICATION,
|
||||
CONFIG_APPLICATION_INIT_PRIORITY, &behavior_ext_power_driver_api);
|
||||
#define KP_INST(n) \
|
||||
static struct zmk_behavior_ext_power_config zmk_behavior_ext_power_config_##n = { \
|
||||
.pd = COND_CODE_1( \
|
||||
DT_INST_NODE_HAS_PROP(n, power_domain), \
|
||||
DEVICE_DT_GET(DT_INST_PHANDLE(n, power_domain)), \
|
||||
NULL \
|
||||
), \
|
||||
}; \
|
||||
DEVICE_DT_INST_DEFINE( \
|
||||
n, \
|
||||
behavior_ext_power_init, \
|
||||
NULL, \
|
||||
NULL, \
|
||||
&zmk_behavior_ext_power_config_##n, \
|
||||
APPLICATION, \
|
||||
CONFIG_APPLICATION_INIT_PRIORITY, \
|
||||
&behavior_ext_power_driver_api \
|
||||
);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(KP_INST)
|
||||
|
||||
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
||||
|
|
220
app/src/dev_pm_policy.c
Normal file
220
app/src/dev_pm_policy.c
Normal file
|
@ -0,0 +1,220 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Commonwealth Scientific and Industrial Research
|
||||
* Organisation (CSIRO) ABN 41 687 119 230.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT zmk_dev_pm_policy
|
||||
|
||||
#include <device.h>
|
||||
#include <devicetree.h>
|
||||
#include <zmk/power_domain.h>
|
||||
#include <zmk/usb.h>
|
||||
#include <zmk/events/activity_state_changed.h>
|
||||
#include <zmk/events/usb_conn_state_changed.h>
|
||||
|
||||
#include <logging/log.h>
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
// TODO: Add check if dev is power domain or device
|
||||
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
|
||||
// Device PM Policy config struct
|
||||
struct zmk_dev_pm_policy_config {
|
||||
const struct device *device;
|
||||
const char *device_compatible;
|
||||
bool auto_off_on_idle;
|
||||
bool usb_auto_toggle;
|
||||
};
|
||||
|
||||
// Array that contains all policy devices that we can loop through during
|
||||
// events.
|
||||
// Limited to 10 policies.
|
||||
#define MAX_POLICY_NUM 10
|
||||
int policy_count = 0;
|
||||
const struct device *policies[MAX_POLICY_NUM];
|
||||
|
||||
/*
|
||||
* Enabling & Disabling of devices
|
||||
*/
|
||||
|
||||
void zmk_dev_pm_policy_enable_device(const struct device *dev, bool save_state) {
|
||||
const struct zmk_dev_pm_policy_config *cfg = dev->config;
|
||||
|
||||
LOG_DBG("Enabling device `%s` with compatible `%s`.", cfg->device->name, cfg->device_compatible);
|
||||
|
||||
if(strcmp(cfg->device_compatible, "power-domain-gpio") == 0) {
|
||||
|
||||
zmk_power_domain_enable(cfg->device, save_state);
|
||||
} else { // Regular Device
|
||||
|
||||
pm_device_action_run(cfg->device, PM_DEVICE_ACTION_RESUME);
|
||||
}
|
||||
}
|
||||
|
||||
void zmk_dev_pm_policy_disable_device(const struct device *dev, bool save_state) {
|
||||
const struct zmk_dev_pm_policy_config *cfg = dev->config;
|
||||
|
||||
LOG_DBG("Disabling device `%s` with compatible `%s`.", cfg->device->name, cfg->device_compatible);
|
||||
|
||||
if(strcmp(cfg->device_compatible, "power-domain-gpio") == 0) {
|
||||
|
||||
zmk_power_domain_disable(cfg->device, save_state);
|
||||
} else { // Regular Device
|
||||
|
||||
pm_device_action_run(cfg->device, PM_DEVICE_ACTION_TURN_OFF);
|
||||
}
|
||||
}
|
||||
|
||||
const int zmk_dev_pm_policy_get_device_state(const struct device *dev) {
|
||||
const struct zmk_dev_pm_policy_config *cfg = dev->config;
|
||||
|
||||
if(strcmp(cfg->device_compatible, "power-domain-gpio") == 0) {
|
||||
|
||||
return zmk_power_domain_get_state_user_intended(cfg->device);
|
||||
} else { // Regular Device
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* USB Event Handler
|
||||
*/
|
||||
|
||||
void zmk_dev_pm_policy_auto_toggle_usb(const struct device *dev) {
|
||||
LOG_DBG("Doing usb auto toggling for `%s`.", dev->name);
|
||||
|
||||
if (zmk_usb_is_powered() == true) {
|
||||
LOG_INF("USB power was connected. Enabling external power for device `%s`.", dev->name);
|
||||
zmk_dev_pm_policy_enable_device(dev, true);
|
||||
|
||||
} else {
|
||||
LOG_INF("USB power was removed. Disabling external power for device `%s`.", dev->name);
|
||||
zmk_dev_pm_policy_disable_device(dev, true);
|
||||
}
|
||||
}
|
||||
|
||||
static int zmk_dev_pm_policy_usb_event_handler(const zmk_event_t *eh) {
|
||||
if (as_zmk_usb_conn_state_changed(eh)) {
|
||||
LOG_DBG("USB conn state changed: %d", zmk_usb_is_powered());
|
||||
|
||||
for(int i=0; i < policy_count; i++) {
|
||||
const struct device *dev = policies[i];
|
||||
const struct zmk_dev_pm_policy_config *cfg = dev->config;
|
||||
|
||||
if(cfg->usb_auto_toggle == true) {
|
||||
zmk_dev_pm_policy_auto_toggle_usb(dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
ZMK_LISTENER(zmk_dev_pm_policy_usb, zmk_dev_pm_policy_usb_event_handler);
|
||||
ZMK_SUBSCRIPTION(zmk_dev_pm_policy_usb, zmk_usb_conn_state_changed);
|
||||
|
||||
|
||||
/*
|
||||
* Activity Event Handler
|
||||
*/
|
||||
|
||||
void zmk_dev_pm_policy_auto_activity_toggle(const struct device *dev, struct zmk_activity_state_changed *ev) {
|
||||
|
||||
if(zmk_dev_pm_policy_get_device_state(dev) != 1) {
|
||||
// User has turned off the power domain.
|
||||
// Therefore we don't need want to do activity toggling.
|
||||
return;
|
||||
}
|
||||
|
||||
const struct zmk_dev_pm_policy_config *cfg = dev->config;
|
||||
|
||||
// Enable or disable the device / power domain, but don't save the new
|
||||
// state in the settings
|
||||
switch (ev->state) {
|
||||
case ZMK_ACTIVITY_ACTIVE:
|
||||
LOG_INF("Device became active - Enabling power for device `%s`.", cfg->device->name);
|
||||
zmk_dev_pm_policy_enable_device(dev, false);
|
||||
break;
|
||||
case ZMK_ACTIVITY_IDLE:
|
||||
LOG_INF("Device became idle - Disabling power for device `%s`.", cfg->device->name);
|
||||
zmk_dev_pm_policy_disable_device(dev, false);
|
||||
break;
|
||||
case ZMK_ACTIVITY_SLEEP:
|
||||
LOG_DBG("Device going to sleep - Doing nothing.");
|
||||
break;
|
||||
default:
|
||||
LOG_WRN("Unhandled activity state: %d", ev->state);
|
||||
}
|
||||
}
|
||||
|
||||
int zmk_dev_pm_policy_activity_event_handler(const zmk_event_t *eh) {
|
||||
struct zmk_activity_state_changed *ev = as_zmk_activity_state_changed(eh);
|
||||
if (ev == NULL) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
LOG_DBG("Activity state changed to: %d", ev->state);
|
||||
|
||||
for(int i=0; i < policy_count; i++) {
|
||||
const struct device *dev = policies[i];
|
||||
const struct zmk_dev_pm_policy_config *cfg = dev->config;
|
||||
|
||||
if(cfg->auto_off_on_idle == true) {
|
||||
zmk_dev_pm_policy_auto_activity_toggle(dev, ev);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
ZMK_LISTENER(zmk_dev_pm_policy_activity, zmk_dev_pm_policy_activity_event_handler);
|
||||
ZMK_SUBSCRIPTION(zmk_dev_pm_policy_activity, zmk_activity_state_changed);
|
||||
|
||||
|
||||
/*
|
||||
* Initialization
|
||||
*/
|
||||
|
||||
static int zmk_dev_pm_policy_init(const struct device *dev)
|
||||
{
|
||||
LOG_DBG("Initing zmk_dev_pm_policy_init: %s", dev->name);
|
||||
const struct zmk_dev_pm_policy_config *cfg = dev->config;
|
||||
|
||||
if(policy_count < MAX_POLICY_NUM) {
|
||||
policies[policy_count] = dev;
|
||||
policy_count++;
|
||||
|
||||
if(cfg->usb_auto_toggle == true) {
|
||||
zmk_dev_pm_policy_auto_toggle_usb(dev);
|
||||
}
|
||||
} else {
|
||||
LOG_ERR("Could not add dev_pm_policy `%s` to policies list, because the number of policies exceeds the maximum number of %d.", dev->name, MAX_POLICY_NUM);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define CONFIG_zmk_dev_pm_policy_INIT_PRIORITY 99
|
||||
#define KP_INST(id) \
|
||||
static const struct zmk_dev_pm_policy_config zmk_dev_pm_policy_##id = { \
|
||||
.device = DEVICE_DT_GET(DT_INST_PHANDLE(id, device)), \
|
||||
.device_compatible = DT_PROP_BY_IDX(DT_INST_PHANDLE(id, device), compatible, 0), \
|
||||
.auto_off_on_idle = DT_INST_PROP(id, auto_off_on_idle), \
|
||||
.usb_auto_toggle = DT_INST_PROP(id, usb_auto_toggle), \
|
||||
}; \
|
||||
DEVICE_DT_INST_DEFINE( \
|
||||
id, \
|
||||
zmk_dev_pm_policy_init, \
|
||||
NULL, \
|
||||
NULL, \
|
||||
&zmk_dev_pm_policy_##id, \
|
||||
APPLICATION, \
|
||||
CONFIG_zmk_dev_pm_policy_INIT_PRIORITY, \
|
||||
NULL \
|
||||
);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(KP_INST)
|
||||
|
||||
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
|
@ -8,6 +8,7 @@
|
|||
#include <init.h>
|
||||
#include <device.h>
|
||||
#include <devicetree.h>
|
||||
#include <pm/device.h>
|
||||
|
||||
#include <logging/log.h>
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
@ -18,11 +19,20 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
|||
#include <zmk/event_manager.h>
|
||||
#include <zmk/events/activity_state_changed.h>
|
||||
#include <zmk/display/status_screen.h>
|
||||
#include <zmk/power_domain.h>
|
||||
|
||||
#define ZMK_DISPLAY_NAME CONFIG_LVGL_DISPLAY_DEV_NAME
|
||||
|
||||
#if CONFIG_ZMK_EXT_POWER
|
||||
|
||||
#define DISPLAY_POWER_DOMAIN DT_LABEL(DT_PHANDLE(DT_CHOSEN(zephyr_display), power_domain))
|
||||
|
||||
#endif
|
||||
|
||||
static const struct device *display;
|
||||
static bool initialized = false;
|
||||
bool power_domain_enabled = true;
|
||||
bool keyboard_active = true;
|
||||
|
||||
static lv_obj_t *screen;
|
||||
|
||||
|
@ -32,6 +42,8 @@ void display_tick_cb(struct k_work *work) { lv_task_handler(); }
|
|||
|
||||
#define TICK_MS 10
|
||||
|
||||
int zmk_display_init_power_domain_manager(const struct device *dev);
|
||||
|
||||
K_WORK_DEFINE(display_tick_work, display_tick_cb);
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_DISPLAY_WORK_QUEUE_DEDICATED)
|
||||
|
@ -85,6 +97,8 @@ static void stop_display_updates() {
|
|||
|
||||
int zmk_display_is_initialized() { return initialized; }
|
||||
|
||||
// This function must be called from `main()`, otherwise the firmware will
|
||||
// crash in zmk_display_status_screen.
|
||||
int zmk_display_init() {
|
||||
LOG_DBG("");
|
||||
|
||||
|
@ -109,7 +123,10 @@ int zmk_display_init() {
|
|||
|
||||
lv_scr_load(screen);
|
||||
|
||||
start_display_updates();
|
||||
if(power_domain_enabled == true) {
|
||||
LOG_DBG("Starting display updates");
|
||||
start_display_updates();
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
|
||||
|
@ -125,11 +142,25 @@ int display_event_handler(const zmk_event_t *eh) {
|
|||
|
||||
switch (ev->state) {
|
||||
case ZMK_ACTIVITY_ACTIVE:
|
||||
start_display_updates();
|
||||
keyboard_active = true;
|
||||
|
||||
if(power_domain_enabled) {
|
||||
LOG_DBG("Starting display updates, because keyboard is active and power domain enabled.");
|
||||
start_display_updates();
|
||||
} else {
|
||||
LOG_DBG("Not enabling display updates even though keyboard is active, because power domain is disabled.");
|
||||
}
|
||||
break;
|
||||
case ZMK_ACTIVITY_IDLE:
|
||||
case ZMK_ACTIVITY_SLEEP:
|
||||
stop_display_updates();
|
||||
keyboard_active = false;
|
||||
|
||||
if(power_domain_enabled) {
|
||||
LOG_DBG("Stopping display updates, because keyboard is not active and power domain enabled.");
|
||||
stop_display_updates();
|
||||
} else {
|
||||
LOG_DBG("Not stopping display updates even though keyboard is idle, because power domain is disabled.");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOG_WRN("Unhandled activity state: %d", ev->state);
|
||||
|
@ -140,3 +171,54 @@ int display_event_handler(const zmk_event_t *eh) {
|
|||
|
||||
ZMK_LISTENER(display, display_event_handler);
|
||||
ZMK_SUBSCRIPTION(display, zmk_activity_state_changed);
|
||||
|
||||
#if CONFIG_ZMK_EXT_POWER
|
||||
|
||||
static int zmk_display_pm_action(const struct device *dev, enum pm_device_action action) {
|
||||
LOG_DBG("In zmk_display_pm_action on dev %s with action: %d", dev->name, action);
|
||||
|
||||
switch (action) {
|
||||
case PM_DEVICE_ACTION_RESUME:
|
||||
power_domain_enabled = true;
|
||||
|
||||
if(initialized == true) {
|
||||
LOG_DBG("Power domain enabled - Enabling display updates");
|
||||
start_display_updates();
|
||||
}
|
||||
|
||||
break;
|
||||
case PM_DEVICE_ACTION_TURN_OFF:
|
||||
power_domain_enabled = false;
|
||||
|
||||
if(initialized == true) {
|
||||
LOG_DBG("Power domain disabled - Disabling display updates");
|
||||
stop_display_updates();
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
LOG_ERR("PM Action %d not implemented", action);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int zmk_display_init_power_domain_manager(const struct device *dev) {
|
||||
|
||||
LOG_DBG("");
|
||||
const struct device *power_domain = device_get_binding(DISPLAY_POWER_DOMAIN);
|
||||
if (power_domain == NULL) {
|
||||
|
||||
LOG_ERR("Unable to retrieve display's power_domain device... is the power-domain property set?");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return zmk_power_domain_init_power_domain_manager_helper(dev, power_domain);
|
||||
}
|
||||
|
||||
PM_DEVICE_DEFINE(pd_manager_display, zmk_display_pm_action);
|
||||
DEVICE_DEFINE(pd_manager_display, "pd_manager_display", &zmk_display_init_power_domain_manager, PM_DEVICE_GET(pd_manager_display), NULL, NULL, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY, NULL);
|
||||
|
||||
#endif // CONFIG_ZMK_EXT_POWER
|
||||
|
|
|
@ -1,214 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT zmk_ext_power_generic
|
||||
|
||||
#include <stdio.h>
|
||||
#include <device.h>
|
||||
#include <pm/device.h>
|
||||
#include <init.h>
|
||||
#include <kernel.h>
|
||||
#include <settings/settings.h>
|
||||
#include <drivers/gpio.h>
|
||||
#include <drivers/ext_power.h>
|
||||
|
||||
#if DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT)
|
||||
|
||||
#include <logging/log.h>
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
struct ext_power_generic_config {
|
||||
const char *label;
|
||||
const uint8_t pin;
|
||||
const uint8_t flags;
|
||||
const uint16_t init_delay_ms;
|
||||
};
|
||||
|
||||
struct ext_power_generic_data {
|
||||
const struct device *gpio;
|
||||
bool status;
|
||||
#if IS_ENABLED(CONFIG_SETTINGS)
|
||||
bool settings_init;
|
||||
#endif
|
||||
};
|
||||
|
||||
#if IS_ENABLED(CONFIG_SETTINGS)
|
||||
static void ext_power_save_state_work(struct k_work *work) {
|
||||
char setting_path[40];
|
||||
const struct device *ext_power = device_get_binding(DT_INST_LABEL(0));
|
||||
struct ext_power_generic_data *data = ext_power->data;
|
||||
|
||||
snprintf(setting_path, 40, "ext_power/state/%s", DT_INST_LABEL(0));
|
||||
settings_save_one(setting_path, &data->status, sizeof(data->status));
|
||||
}
|
||||
|
||||
static struct k_work_delayable ext_power_save_work;
|
||||
#endif
|
||||
|
||||
int ext_power_save_state() {
|
||||
#if IS_ENABLED(CONFIG_SETTINGS)
|
||||
int ret = k_work_reschedule(&ext_power_save_work, K_MSEC(CONFIG_ZMK_SETTINGS_SAVE_DEBOUNCE));
|
||||
return MIN(ret, 0);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int ext_power_generic_enable(const struct device *dev) {
|
||||
struct ext_power_generic_data *data = dev->data;
|
||||
const struct ext_power_generic_config *config = dev->config;
|
||||
|
||||
if (gpio_pin_set(data->gpio, config->pin, 1)) {
|
||||
LOG_WRN("Failed to set ext-power control pin");
|
||||
return -EIO;
|
||||
}
|
||||
data->status = true;
|
||||
return ext_power_save_state();
|
||||
}
|
||||
|
||||
static int ext_power_generic_disable(const struct device *dev) {
|
||||
struct ext_power_generic_data *data = dev->data;
|
||||
const struct ext_power_generic_config *config = dev->config;
|
||||
|
||||
if (gpio_pin_set(data->gpio, config->pin, 0)) {
|
||||
LOG_WRN("Failed to clear ext-power control pin");
|
||||
return -EIO;
|
||||
}
|
||||
data->status = false;
|
||||
return ext_power_save_state();
|
||||
}
|
||||
|
||||
static int ext_power_generic_get(const struct device *dev) {
|
||||
struct ext_power_generic_data *data = dev->data;
|
||||
return data->status;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_SETTINGS)
|
||||
static int ext_power_settings_set(const char *name, size_t len, settings_read_cb read_cb,
|
||||
void *cb_arg) {
|
||||
const char *next;
|
||||
int rc;
|
||||
|
||||
if (settings_name_steq(name, DT_INST_LABEL(0), &next) && !next) {
|
||||
const struct device *ext_power = DEVICE_DT_GET(DT_DRV_INST(0));
|
||||
struct ext_power_generic_data *data = ext_power->data;
|
||||
|
||||
if (len != sizeof(data->status)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = read_cb(cb_arg, &data->status, sizeof(data->status));
|
||||
if (rc >= 0) {
|
||||
data->settings_init = true;
|
||||
|
||||
if (ext_power == NULL) {
|
||||
LOG_ERR("Unable to retrieve ext_power device: %s", DT_INST_LABEL(0));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (data->status) {
|
||||
ext_power_generic_enable(ext_power);
|
||||
} else {
|
||||
ext_power_generic_disable(ext_power);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
struct settings_handler ext_power_conf = {.name = "ext_power/state",
|
||||
.h_set = ext_power_settings_set};
|
||||
#endif
|
||||
|
||||
static int ext_power_generic_init(const struct device *dev) {
|
||||
struct ext_power_generic_data *data = dev->data;
|
||||
const struct ext_power_generic_config *config = dev->config;
|
||||
|
||||
data->gpio = device_get_binding(config->label);
|
||||
if (data->gpio == NULL) {
|
||||
LOG_ERR("Failed to get ext-power control device");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (gpio_pin_configure(data->gpio, config->pin, config->flags | GPIO_OUTPUT)) {
|
||||
LOG_ERR("Failed to configure ext-power control pin");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_SETTINGS)
|
||||
settings_subsys_init();
|
||||
|
||||
int err = settings_register(&ext_power_conf);
|
||||
if (err) {
|
||||
LOG_ERR("Failed to register the ext_power settings handler (err %d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
k_work_init_delayable(&ext_power_save_work, ext_power_save_state_work);
|
||||
|
||||
// Set default value (on) if settings isn't set
|
||||
settings_load_subtree("ext_power");
|
||||
if (!data->settings_init) {
|
||||
|
||||
data->status = true;
|
||||
k_work_schedule(&ext_power_save_work, K_NO_WAIT);
|
||||
|
||||
ext_power_enable(dev);
|
||||
}
|
||||
#else
|
||||
// Default to the ext_power being open when no settings
|
||||
ext_power_enable(dev);
|
||||
#endif
|
||||
|
||||
if (config->init_delay_ms) {
|
||||
k_msleep(config->init_delay_ms);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_DEVICE
|
||||
static int ext_power_generic_pm_action(const struct device *dev, enum pm_device_action action) {
|
||||
switch (action) {
|
||||
case PM_DEVICE_ACTION_RESUME:
|
||||
ext_power_generic_enable(dev);
|
||||
return 0;
|
||||
case PM_DEVICE_ACTION_SUSPEND:
|
||||
ext_power_generic_disable(dev);
|
||||
return 0;
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_PM_DEVICE */
|
||||
|
||||
static const struct ext_power_generic_config config = {
|
||||
.label = DT_INST_GPIO_LABEL(0, control_gpios),
|
||||
.pin = DT_INST_GPIO_PIN(0, control_gpios),
|
||||
.flags = DT_INST_GPIO_FLAGS(0, control_gpios),
|
||||
.init_delay_ms = DT_INST_PROP_OR(0, init_delay_ms, 0)};
|
||||
|
||||
static struct ext_power_generic_data data = {
|
||||
.status = false,
|
||||
#if IS_ENABLED(CONFIG_SETTINGS)
|
||||
.settings_init = false,
|
||||
#endif
|
||||
};
|
||||
|
||||
static const struct ext_power_api api = {.enable = ext_power_generic_enable,
|
||||
.disable = ext_power_generic_disable,
|
||||
.get = ext_power_generic_get};
|
||||
|
||||
#define ZMK_EXT_POWER_INIT_PRIORITY 81
|
||||
|
||||
PM_DEVICE_DT_INST_DEFINE(0, ext_power_generic_pm_action);
|
||||
DEVICE_DT_INST_DEFINE(0, ext_power_generic_init, PM_DEVICE_DT_INST_GET(0), &data, &config,
|
||||
POST_KERNEL, ZMK_EXT_POWER_INIT_PRIORITY, &api);
|
||||
|
||||
#endif /* DT_HAS_COMPAT_STATUS_OKAY(DT_DRV_COMPAT) */
|
|
@ -15,7 +15,6 @@ LOG_MODULE_REGISTER(zmk, CONFIG_ZMK_LOG_LEVEL);
|
|||
#include <zmk/matrix.h>
|
||||
#include <zmk/kscan.h>
|
||||
#include <zmk/display.h>
|
||||
#include <drivers/ext_power.h>
|
||||
|
||||
#define ZMK_KSCAN_DEV DT_LABEL(ZMK_MATRIX_NODE_ID)
|
||||
|
||||
|
|
547
app/src/power_domain.c
Normal file
547
app/src/power_domain.c
Normal file
|
@ -0,0 +1,547 @@
|
|||
/*
|
||||
* Copyright (c) 2020 The ZMK Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <device.h>
|
||||
#include <pm/device.h>
|
||||
#include <pm/device_runtime.h>
|
||||
#include <init.h>
|
||||
#include <kernel.h>
|
||||
#include <settings/settings.h>
|
||||
#include <drivers/gpio.h>
|
||||
#include <zmk/power_domain.h>
|
||||
#include <zmk/events/activity_state_changed.h>
|
||||
|
||||
#include <logging/log.h>
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
||||
/*
|
||||
* Macros to get a list of power domains
|
||||
*/
|
||||
|
||||
// Generates a count of power_domain_gpio devices
|
||||
// For example, if you have 2 power domains, then `PD_COUNT()`, will generate:
|
||||
// 0 + 1 + 1
|
||||
// Which is 2 :)
|
||||
#define _PD_COUNT_F(id) \
|
||||
+ 1
|
||||
#define PD_COUNT() \
|
||||
0 DT_FOREACH_STATUS_OKAY(power_domain_gpio, _PD_COUNT_F)
|
||||
|
||||
// Generate the power_domains array definition.
|
||||
// Should be used with DT_FOREACH_STATUS_OKAY.
|
||||
#define GEN_PD_ARRAY(id) \
|
||||
{ \
|
||||
.pd = DEVICE_DT_GET(id), \
|
||||
.state_user_intended = false, \
|
||||
.settings_init = false, \
|
||||
},
|
||||
|
||||
|
||||
/*
|
||||
* Global Variables
|
||||
*/
|
||||
|
||||
// Struct that stores both power domain device pointers as well as whether
|
||||
// the user desires for the domain to be on or off
|
||||
struct zmk_power_domain_data {
|
||||
const struct device *pd;
|
||||
bool state_user_intended;
|
||||
bool settings_init;
|
||||
};
|
||||
|
||||
// Count of available power domains
|
||||
#define POWER_DOMAIN_COUNT PD_COUNT()
|
||||
|
||||
// Initialize array of structs with available power domains using above macro
|
||||
struct zmk_power_domain_data pd_data_list[] = {
|
||||
DT_FOREACH_STATUS_OKAY(power_domain_gpio, GEN_PD_ARRAY)
|
||||
};
|
||||
|
||||
// Default power domain. Set by chosen node `zmk,default-power-domain`
|
||||
// and if not present to first initialized power domain.
|
||||
struct zmk_power_domain_data *pd_data_default = NULL;
|
||||
|
||||
// Whether settings have been initialized yet
|
||||
bool zmk_pd_settings_init = false;
|
||||
|
||||
/*
|
||||
* End-User Functions
|
||||
*
|
||||
* These functions call `pm_device_action_run()`...
|
||||
* - which in turn calls the callback `zmk_power_domain_pm_action()`...
|
||||
* - which then calls `zmk_power_domain_set_gpio_pin()`,
|
||||
* - which actually sets the control pin that enables or disables the power.
|
||||
*
|
||||
* `pm_device_action_run()` also calls `_pm_action()` callbacks of the devices
|
||||
* assigned to the power-domain so that they can shutdown down and
|
||||
* re-initialize.
|
||||
*/
|
||||
|
||||
const int zmk_power_domain_disable(const struct device *pd_dev, bool save_state) {
|
||||
return zmk_power_domain_run_action(pd_dev, ZMK_PD_ACTION_TURN_OFF, save_state);
|
||||
}
|
||||
|
||||
const int zmk_power_domain_enable(const struct device *pd_dev, bool save_state) {
|
||||
return zmk_power_domain_run_action(pd_dev, ZMK_PD_ACTION_TURN_ON, save_state);
|
||||
}
|
||||
|
||||
const int zmk_power_domain_toggle(const struct device *pd_dev, bool save_state) {
|
||||
return zmk_power_domain_run_action(pd_dev, ZMK_PD_ACTION_TOGGLE, save_state);
|
||||
}
|
||||
|
||||
const int zmk_power_domain_get_state_actual(const struct device *pd_dev) {
|
||||
if(pd_dev == NULL) {
|
||||
pd_dev = pd_data_default->pd;
|
||||
if(pd_dev == NULL) {
|
||||
LOG_ERR("Could not get power domain state: Found now power domain.");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
enum pm_device_state pm_state;
|
||||
if(pm_device_state_get(pd_dev, &pm_state) != 0) {
|
||||
|
||||
LOG_ERR("Could not get pm device state for power domain `%s`", pd_dev->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if(pm_state == PM_DEVICE_STATE_ACTIVE) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
const int zmk_power_domain_get_state_user_intended(const struct device *pd_dev) {
|
||||
|
||||
if(pd_dev == NULL) {
|
||||
pd_dev = pd_data_default->pd;
|
||||
if(pd_dev == NULL) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
struct zmk_power_domain_data *pd_data = zmk_power_domain_get_pd_data_for_pd(pd_dev);
|
||||
return (const int)pd_data->state_user_intended;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Power Domain Run Action Helper Function
|
||||
*/
|
||||
|
||||
const int zmk_power_domain_run_action(const struct device *pd_dev, enum zmk_power_domain_action zmk_action, bool save_state) {
|
||||
if(pd_dev == NULL) {
|
||||
pd_dev = pd_data_default->pd;
|
||||
if(pd_dev == NULL) {
|
||||
|
||||
LOG_ERR("Could not run power domain action: Found no power domains.");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_DBG("In zmk_power_domain_run_action %s on pd `%s`.", zmk_pm_action_str(zmk_action), pd_dev->name);
|
||||
|
||||
enum pm_device_state pm_state;
|
||||
if(pm_device_state_get(pd_dev, &pm_state) != 0) {
|
||||
LOG_ERR("Could not get pm device state for power domain `%s`", pd_dev->name);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
LOG_DBG("Current pm_device_state of power domain `%s`: %s.", pd_dev->name, pm_device_state_str(pm_state));
|
||||
|
||||
if(zmk_action == ZMK_PD_ACTION_TOGGLE) {
|
||||
if(pm_state == PM_DEVICE_STATE_ACTIVE) {
|
||||
zmk_action = ZMK_PD_ACTION_TURN_OFF;
|
||||
} else {
|
||||
zmk_action = ZMK_PD_ACTION_TURN_ON;
|
||||
}
|
||||
}
|
||||
|
||||
enum pm_device_action zephyr_action;
|
||||
switch (zmk_action) {
|
||||
case ZMK_PD_ACTION_TURN_OFF:
|
||||
zephyr_action = PM_DEVICE_ACTION_TURN_OFF;
|
||||
break;
|
||||
|
||||
case ZMK_PD_ACTION_TURN_ON:
|
||||
// Only PM_DEVICE_ACTION_RESUME is a valid option for turning on.
|
||||
// While PM_DEVICE_ACTION_TURN_ON may seem like the correct action,
|
||||
// it actually sets the power domain to state `suspended`, which
|
||||
// doesn't allow further ON / OFF actions.
|
||||
zephyr_action = PM_DEVICE_ACTION_RESUME;
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_ERR("Action %d not implemented", zmk_action);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
LOG_INF("Running pm_device_action %s on pd `%s`.", pm_device_action_str(zephyr_action), pd_dev->name);
|
||||
|
||||
pm_device_action_run(pd_dev, zephyr_action);
|
||||
|
||||
LOG_DBG("Finished running pm_device_action %s on pd `%s`.", pm_device_action_str(zephyr_action), pd_dev->name);
|
||||
|
||||
if(save_state == true) {
|
||||
struct zmk_power_domain_data *pd_data = zmk_power_domain_get_pd_data_for_pd(pd_dev);
|
||||
pd_data->state_user_intended = (bool) zmk_action;
|
||||
|
||||
return zmk_power_domain_save_state();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* State Saving
|
||||
*/
|
||||
#if IS_ENABLED(CONFIG_SETTINGS)
|
||||
|
||||
static struct k_work_delayable zmk_power_domain_save_work;
|
||||
|
||||
static void zmk_power_domain_save_state_work(struct k_work *work) {
|
||||
LOG_DBG("");
|
||||
|
||||
for(int i=0; i < POWER_DOMAIN_COUNT; i++) {
|
||||
struct zmk_power_domain_data *pd_data = &pd_data_list[i];
|
||||
|
||||
char setting_path[40];
|
||||
snprintf(setting_path, sizeof(setting_path), "power_domain/state/%s", pd_data->pd->name);
|
||||
|
||||
LOG_DBG("Saving `%d` to `%s`", pd_data->state_user_intended, setting_path);
|
||||
|
||||
settings_save_one(setting_path, &pd_data->state_user_intended, sizeof(pd_data->state_user_intended));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int zmk_power_domain_save_state() {
|
||||
LOG_DBG("");
|
||||
|
||||
#if IS_ENABLED(CONFIG_SETTINGS)
|
||||
int ret = k_work_reschedule(&zmk_power_domain_save_work, K_MSEC(CONFIG_ZMK_SETTINGS_SAVE_DEBOUNCE));
|
||||
return MIN(ret, 0);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
// This function is called when settings are loaded from flash by
|
||||
// `settings_load_subtree`.
|
||||
// It's called once for each power domain state that has been stored.
|
||||
static int zmk_power_domain_settings_set(const char *name, size_t len, settings_read_cb read_cb, void *cb_arg) {
|
||||
LOG_DBG("Restoring settings for power domain %s", name);
|
||||
|
||||
// Since we used `.name = "power_domain/state",` in `zmk_pd_settings_conf`,
|
||||
// the name variable contains only the last part of the key, which is the
|
||||
// name of the power domain.
|
||||
struct zmk_power_domain_data *pd_data = zmk_power_domain_get_pd_data_by_name(name);
|
||||
|
||||
if(pd_data == NULL) {
|
||||
LOG_WRN("Could not restore state for power domain %s, because it does not exist in device tree.", name);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (len != sizeof(pd_data->state_user_intended)) {
|
||||
LOG_WRN("Could not restore state for power domain %s, because the size of the value is invalid.", name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int rc = read_cb(
|
||||
cb_arg,
|
||||
&pd_data->state_user_intended,
|
||||
sizeof(pd_data->state_user_intended)
|
||||
);
|
||||
if (rc >= 0) {
|
||||
LOG_INF("Restored state for power domain %s: %d", name, pd_data->state_user_intended);
|
||||
|
||||
pd_data->settings_init = true;
|
||||
|
||||
if(pd_data->state_user_intended == true) {
|
||||
zmk_power_domain_enable(pd_data->pd, false);
|
||||
} else {
|
||||
zmk_power_domain_disable(pd_data->pd, false);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
LOG_WRN("Could not restore state for power domain %s, because value could not be read: %d.", name, rc);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
struct settings_handler zmk_pd_settings_conf = {
|
||||
.name = "power_domain/state",
|
||||
.h_set = zmk_power_domain_settings_set,
|
||||
};
|
||||
|
||||
int zmk_power_domain_init_state_saving(const struct device *pd_dev) {
|
||||
#if IS_ENABLED(CONFIG_SETTINGS)
|
||||
LOG_DBG("");
|
||||
|
||||
settings_subsys_init();
|
||||
|
||||
int err = settings_register(&zmk_pd_settings_conf);
|
||||
if (err) {
|
||||
LOG_ERR("Failed to register the power domain settings handler (err %d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
k_work_init_delayable(&zmk_power_domain_save_work, zmk_power_domain_save_state_work);
|
||||
|
||||
// This will load the settings and then call
|
||||
// `zmk_power_domain_settings_set`, which will enable power
|
||||
settings_load_subtree("power_domain");
|
||||
|
||||
// Here we check if any of the power domains didn't have a saved state
|
||||
// and enable them as the default action.
|
||||
for(int i=0; i < POWER_DOMAIN_COUNT; i++) {
|
||||
if(pd_data_list[i].settings_init != true) {
|
||||
LOG_INF("Found no existing settings for power domain %s. Setting it to enabled.", pd_data_list[i].pd->name);
|
||||
|
||||
k_work_schedule(&zmk_power_domain_save_work, K_NO_WAIT);
|
||||
|
||||
zmk_power_domain_enable(pd_data_list[i].pd, true);
|
||||
pd_data_list[i].settings_init = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Init Functions
|
||||
*/
|
||||
|
||||
// Zephyr's power_domain_gpio device is initialized at POST_KERNEL, 75.
|
||||
//
|
||||
// This function is ran right after that at POST_KERNEL, 76.
|
||||
//
|
||||
// Here we enable the power control pins so that power is available to all
|
||||
// devices connected to the power domains during their initialization.
|
||||
//
|
||||
// This is important for some devices, such as the ssd1306 oled display driver.
|
||||
// Without power, it will fail initialization.
|
||||
//
|
||||
// Once all devices are successfuly initialized,
|
||||
// `zmk_power_domain_manager_post_boot_init` is run, which loads the user
|
||||
// settings and turns on or off the power domains based on the user's
|
||||
// preference.
|
||||
//
|
||||
// To catch log output add the following to your config:
|
||||
// `CONFIG_LOG_PROCESS_THREAD_STARTUP_DELAY_MS=4000`
|
||||
static int zmk_power_domain_manager_init(const struct device *dev) {
|
||||
LOG_DBG("");
|
||||
|
||||
if(POWER_DOMAIN_COUNT == 0) {
|
||||
LOG_WRN("No power domain devices configured. Exiting.");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
zmk_power_domain_set_pd_data_default();
|
||||
|
||||
for(int i=0; i < POWER_DOMAIN_COUNT; i++) {
|
||||
const struct device *pd = pd_data_list[i].pd;
|
||||
|
||||
LOG_DBG("Turning on domain: %s",pd->name);
|
||||
zmk_power_domain_enable(pd, false);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// power_domain_gpio inits at 75, so we init right after that
|
||||
#define ZMK_POWER_DOMAIN_MANAGER_INIT_PRIORITY 76
|
||||
SYS_INIT(zmk_power_domain_manager_init, POST_KERNEL, ZMK_POWER_DOMAIN_MANAGER_INIT_PRIORITY);
|
||||
|
||||
|
||||
// This function is run after all other devices have initialized.
|
||||
// Before this function is run `zmk_power_domain_init` is run, which activates
|
||||
// the power domain's control pins to ensure all devices that depend on them
|
||||
// have power to initialize.
|
||||
//
|
||||
// After they are all initialized, this function is run, which loads the user
|
||||
// settings and enables or disables power depending on the last state before
|
||||
// the boot.
|
||||
static int zmk_power_domain_manager_post_boot_init(const struct device *dev) {
|
||||
LOG_DBG("");
|
||||
|
||||
if(POWER_DOMAIN_COUNT == 0) {
|
||||
LOG_WRN("No power domain devices configured. Exiting.");
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_SETTINGS)
|
||||
|
||||
// This will load the previous power state from the settings and then
|
||||
// enable or disable the power in `zmk_power_domain_settings_set`.
|
||||
zmk_power_domain_init_state_saving(dev);
|
||||
|
||||
#else
|
||||
LOG_INF("Settings are disabled. Setting all power domains to enabled");
|
||||
|
||||
for(int i=0; i < POWER_DOMAIN_COUNT; i++) {
|
||||
const struct device *pd = pd_data_list[i].pd;
|
||||
|
||||
LOG_DBG("Turning on domain: %s",pd->name);
|
||||
zmk_power_domain_enable(pd, true);
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Run this post_boot_init function at the second lowest priority.
|
||||
// This ensures this function is run after all other devices have been
|
||||
// initialized, except for the dev_pm_policy devices, which must be run after
|
||||
// this.
|
||||
#define ZMK_POWER_DOMAIN_MANAGER_POST_BOOT_INIT_PRIORITY 98
|
||||
SYS_INIT(zmk_power_domain_manager_post_boot_init, APPLICATION, ZMK_POWER_DOMAIN_MANAGER_POST_BOOT_INIT_PRIORITY);
|
||||
|
||||
|
||||
/*
|
||||
* Activity Event Handler
|
||||
*/
|
||||
|
||||
int zmk_power_domain_activity_event_handler(const zmk_event_t *eh) {
|
||||
struct zmk_activity_state_changed *ev = as_zmk_activity_state_changed(eh);
|
||||
if (ev == NULL) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
switch (ev->state) {
|
||||
case ZMK_ACTIVITY_ACTIVE:
|
||||
LOG_DBG("Device became active - Doing nothing.");
|
||||
break;
|
||||
case ZMK_ACTIVITY_IDLE:
|
||||
LOG_DBG("Device became idle - Doing nothing.");
|
||||
break;
|
||||
case ZMK_ACTIVITY_SLEEP:
|
||||
LOG_DBG("Device going to sleep - Disabling power.");
|
||||
|
||||
for(int i=0; i < POWER_DOMAIN_COUNT; i++) {
|
||||
const struct device *pd_dev = pd_data_list[i].pd;
|
||||
|
||||
zmk_power_domain_disable(pd_dev, false);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
LOG_WRN("Unhandled activity state: %d", ev->state);
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
ZMK_LISTENER(zmk_power_domain, zmk_power_domain_activity_event_handler);
|
||||
ZMK_SUBSCRIPTION(zmk_power_domain, zmk_activity_state_changed);
|
||||
|
||||
|
||||
/*
|
||||
* Helper functions
|
||||
*/
|
||||
|
||||
int zmk_power_domain_init_power_domain_manager_helper(const struct device *dev, const struct device *pd_dev) {
|
||||
|
||||
LOG_DBG("Setting `%s` power domain to `%s`", dev->name, pd_dev->name);
|
||||
|
||||
// This function requires a zephyr fork with this commit:
|
||||
// https://github.com/zephyrproject-rtos/zephyr/commit/0b13b44a662a1baadd4ed370441fbe8a74b4d531
|
||||
//
|
||||
// It dynamically adds a power domain to a device.
|
||||
// This way the user doesn't need to manually create power domain
|
||||
// management device in the device tree.
|
||||
if(pm_device_power_domain_add(dev, pd_dev) != 0) {
|
||||
LOG_ERR("Could not set power domain of %s.", dev->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Get the state of the actual power domain and then set
|
||||
// the state of the manager device to the same state.
|
||||
// We have to do this, because this device is initialized
|
||||
// after the power domain is initialized and runs it's actions
|
||||
int domain_state = zmk_power_domain_get_state_actual(pd_dev);
|
||||
if(domain_state == 0) {
|
||||
|
||||
pm_device_action_run(dev, PM_DEVICE_ACTION_TURN_OFF);
|
||||
} else if(domain_state == 1) {
|
||||
|
||||
// By default the power domain manager device is initialized as
|
||||
// on / active.
|
||||
// If we run RESUME, it will not run the callback, because it's
|
||||
// already in the on / active / resumed state.
|
||||
// So, first we set it to off without triggering the off action
|
||||
// and then we run the RESUME action.
|
||||
pm_device_runtime_init_off(dev);
|
||||
pm_device_action_run(dev, PM_DEVICE_ACTION_RESUME);
|
||||
} else {
|
||||
LOG_ERR("Could not determine power domain state to correctly init power domain manager %s...", dev->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *zmk_pm_action_str(enum zmk_power_domain_action action) {
|
||||
switch (action) {
|
||||
case ZMK_PD_ACTION_TURN_OFF:
|
||||
return "turn_off";
|
||||
case ZMK_PD_ACTION_TURN_ON:
|
||||
return "turn_on";
|
||||
case ZMK_PD_ACTION_TOGGLE:
|
||||
return "toggle";
|
||||
default:
|
||||
return "unknown_action";
|
||||
}
|
||||
}
|
||||
|
||||
struct zmk_power_domain_data *zmk_power_domain_get_pd_data_for_pd(const struct device *pd_dev) {
|
||||
LOG_DBG("");
|
||||
|
||||
struct zmk_power_domain_data *pd_data = NULL;
|
||||
|
||||
for(int i=0; i < POWER_DOMAIN_COUNT; i++) {
|
||||
if(pd_dev == pd_data_list[i].pd) {
|
||||
pd_data = &pd_data_list[i];
|
||||
LOG_DBG("Found desired pd_data for power domain: %s", pd_data->pd->name);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return pd_data;
|
||||
}
|
||||
|
||||
struct zmk_power_domain_data *zmk_power_domain_get_pd_data_by_name(const char *name) {
|
||||
|
||||
for(int i=0; i < POWER_DOMAIN_COUNT; i++) {
|
||||
if(strcmp(name, pd_data_list[i].pd->name) == 0) {
|
||||
LOG_DBG("Found PD for name `%s`: %p", name, &pd_data_list[i]);
|
||||
return &pd_data_list[i];
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void zmk_power_domain_set_pd_data_default() {
|
||||
const struct device *pd_dev_default = DEVICE_DT_GET(DEFAULT_POWER_DOMAIN);
|
||||
|
||||
if(pd_dev_default != NULL) {
|
||||
pd_data_default = zmk_power_domain_get_pd_data_for_pd(pd_dev_default);
|
||||
} else {
|
||||
pd_data_default = &pd_data_list[0];
|
||||
|
||||
LOG_ERR("Default power domain chosen node is not set. Setting to first power domain.");
|
||||
}
|
||||
|
||||
LOG_INF("Set default power domain to: %s", pd_data_default->pd->name);
|
||||
}
|
|
@ -15,8 +15,9 @@
|
|||
#include <logging/log.h>
|
||||
|
||||
#include <drivers/led_strip.h>
|
||||
#include <drivers/ext_power.h>
|
||||
#include <pm/device.h>
|
||||
|
||||
#include <zmk/power_domain.h>
|
||||
#include <zmk/rgb_underglow.h>
|
||||
|
||||
LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
||||
|
@ -24,6 +25,12 @@ LOG_MODULE_DECLARE(zmk, CONFIG_ZMK_LOG_LEVEL);
|
|||
#define STRIP_LABEL DT_LABEL(DT_CHOSEN(zmk_underglow))
|
||||
#define STRIP_NUM_PIXELS DT_PROP(DT_CHOSEN(zmk_underglow), chain_length)
|
||||
|
||||
#if CONFIG_ZMK_EXT_POWER
|
||||
|
||||
#define STRIP_POWER_DOMAIN DT_LABEL(DT_PHANDLE(DT_CHOSEN(zmk_underglow), power_domain))
|
||||
|
||||
#endif
|
||||
|
||||
#define HUE_MAX 360
|
||||
#define SAT_MAX 100
|
||||
#define BRT_MAX 100
|
||||
|
@ -53,9 +60,7 @@ static struct led_rgb pixels[STRIP_NUM_PIXELS];
|
|||
|
||||
static struct rgb_underglow_state state;
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_RGB_UNDERGLOW_EXT_POWER)
|
||||
static const struct device *ext_power;
|
||||
#endif
|
||||
static const struct device *power_domain;
|
||||
|
||||
static struct zmk_led_hsb hsb_scale_min_max(struct zmk_led_hsb hsb) {
|
||||
hsb.b = CONFIG_ZMK_RGB_UNDERGLOW_BRT_MIN +
|
||||
|
@ -223,7 +228,7 @@ static void zmk_rgb_underglow_save_state_work() {
|
|||
static struct k_work_delayable underglow_save_work;
|
||||
#endif
|
||||
|
||||
static int zmk_rgb_underglow_init(const struct device *_arg) {
|
||||
static int zmk_rgb_underglow_init(const struct device *dev) {
|
||||
led_strip = device_get_binding(STRIP_LABEL);
|
||||
if (led_strip) {
|
||||
LOG_INF("Found LED strip device %s", STRIP_LABEL);
|
||||
|
@ -232,13 +237,6 @@ static int zmk_rgb_underglow_init(const struct device *_arg) {
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_RGB_UNDERGLOW_EXT_POWER)
|
||||
ext_power = device_get_binding("EXT_POWER");
|
||||
if (ext_power == NULL) {
|
||||
LOG_ERR("Unable to retrieve ext_power device: EXT_POWER");
|
||||
}
|
||||
#endif
|
||||
|
||||
state = (struct rgb_underglow_state){
|
||||
color : {
|
||||
h : CONFIG_ZMK_RGB_UNDERGLOW_HUE_START,
|
||||
|
@ -256,7 +254,7 @@ static int zmk_rgb_underglow_init(const struct device *_arg) {
|
|||
|
||||
int err = settings_register(&rgb_conf);
|
||||
if (err) {
|
||||
LOG_ERR("Failed to register the ext_power settings handler (err %d)", err);
|
||||
LOG_ERR("Failed to register the rgb_underglow settings handler (err %d)", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -267,6 +265,12 @@ static int zmk_rgb_underglow_init(const struct device *_arg) {
|
|||
|
||||
k_timer_start(&underglow_tick, K_NO_WAIT, K_MSEC(50));
|
||||
|
||||
#if CONFIG_ZMK_EXT_POWER
|
||||
|
||||
zmk_rgb_underglow_init_power_domain_manager(dev);
|
||||
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -292,17 +296,17 @@ int zmk_rgb_underglow_on() {
|
|||
return -ENODEV;
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_RGB_UNDERGLOW_EXT_POWER)
|
||||
if (ext_power != NULL) {
|
||||
int rc = ext_power_enable(ext_power);
|
||||
if (power_domain != NULL) {
|
||||
int rc = zmk_power_domain_enable(power_domain, true);
|
||||
if (rc != 0) {
|
||||
LOG_ERR("Unable to enable EXT_POWER: %d", rc);
|
||||
LOG_ERR("Unable to enable power domain: %d", rc);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
state.on = true;
|
||||
state.animation_step = 0;
|
||||
k_timer_start(&underglow_tick, K_NO_WAIT, K_MSEC(50));
|
||||
zmk_rgb_underglow_resume();
|
||||
|
||||
return zmk_rgb_underglow_save_state();
|
||||
}
|
||||
|
@ -312,14 +316,28 @@ int zmk_rgb_underglow_off() {
|
|||
return -ENODEV;
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZMK_RGB_UNDERGLOW_EXT_POWER)
|
||||
if (ext_power != NULL) {
|
||||
int rc = ext_power_disable(ext_power);
|
||||
if (power_domain != NULL) {
|
||||
int rc = zmk_power_domain_disable(power_domain, true);
|
||||
if (rc != 0) {
|
||||
LOG_ERR("Unable to disable EXT_POWER: %d", rc);
|
||||
LOG_ERR("Unable to disable power domain: %d", rc);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
zmk_rgb_underglow_pause();
|
||||
|
||||
state.on = false;
|
||||
|
||||
return zmk_rgb_underglow_save_state();
|
||||
}
|
||||
|
||||
int zmk_rgb_underglow_resume() {
|
||||
k_timer_start(&underglow_tick, K_NO_WAIT, K_MSEC(50));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int zmk_rgb_underglow_pause() {
|
||||
for (int i = 0; i < STRIP_NUM_PIXELS; i++) {
|
||||
pixels[i] = (struct led_rgb){r : 0, g : 0, b : 0};
|
||||
}
|
||||
|
@ -327,9 +345,8 @@ int zmk_rgb_underglow_off() {
|
|||
led_strip_update_rgb(led_strip, pixels, STRIP_NUM_PIXELS);
|
||||
|
||||
k_timer_stop(&underglow_tick);
|
||||
state.on = false;
|
||||
|
||||
return zmk_rgb_underglow_save_state();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int zmk_rgb_underglow_calc_effect(int direction) {
|
||||
|
@ -444,4 +461,68 @@ int zmk_rgb_underglow_change_spd(int direction) {
|
|||
return zmk_rgb_underglow_save_state();
|
||||
}
|
||||
|
||||
SYS_INIT(zmk_rgb_underglow_init, APPLICATION, CONFIG_APPLICATION_INIT_PRIORITY);
|
||||
#if CONFIG_ZMK_EXT_POWER
|
||||
|
||||
static int zmk_rgb_underglow_pm_action(const struct device *dev, enum pm_device_action action) {
|
||||
LOG_DBG("In zmk_rgb_underglow_pm_action on dev %s with action: %d", dev->name, action);
|
||||
|
||||
switch (action) {
|
||||
case PM_DEVICE_ACTION_RESUME:
|
||||
if(state.on == true) {
|
||||
LOG_DBG("Resuming underglow tick");
|
||||
zmk_rgb_underglow_resume();
|
||||
} else {
|
||||
LOG_DBG("Underglow state off, therefore not resuming.");
|
||||
}
|
||||
|
||||
break;
|
||||
case PM_DEVICE_ACTION_TURN_OFF:
|
||||
if(state.on == true) {
|
||||
LOG_DBG("Pausing underglow tick");
|
||||
zmk_rgb_underglow_pause();
|
||||
} else {
|
||||
LOG_DBG("Underglow state off, therefore not pausing.");
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
LOG_ERR("PM Action %d not implemented", action);
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int zmk_rgb_underglow_init_power_domain_manager(const struct device *dev) {
|
||||
power_domain = device_get_binding(STRIP_POWER_DOMAIN);
|
||||
if (power_domain == NULL) {
|
||||
|
||||
LOG_ERR("Unable to retrieve power_domain device... is the power-domain property set?");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return zmk_power_domain_init_power_domain_manager_helper(dev, power_domain);
|
||||
}
|
||||
|
||||
PM_DEVICE_DEFINE(pd_manager_rgb_underglow, zmk_rgb_underglow_pm_action);
|
||||
|
||||
#endif // CONFIG_ZMK_EXT_POWER
|
||||
|
||||
DEVICE_DEFINE(
|
||||
pd_manager_rgb_underglow,
|
||||
"pd_manager_rgb_underglow",
|
||||
&zmk_rgb_underglow_init,
|
||||
|
||||
#if CONFIG_ZMK_EXT_POWER
|
||||
PM_DEVICE_GET(pd_manager_rgb_underglow),
|
||||
#else
|
||||
NULL,
|
||||
#endif
|
||||
|
||||
NULL,
|
||||
NULL,
|
||||
APPLICATION,
|
||||
CONFIG_APPLICATION_INIT_PRIORITY,
|
||||
NULL
|
||||
);
|
||||
|
|
Loading…
Add table
Reference in a new issue