mirror of
https://github.com/liquidraver/ZephCore.git
synced 2026-09-02 17:33:43 +00:00
130 lines
3.6 KiB
C
130 lines
3.6 KiB
C
/*
|
|
* ZephCore - Haptic feedback (DRV2605)
|
|
* Copyright (c) 2026 ZephCore
|
|
* SPDX-License-Identifier: MIT
|
|
*/
|
|
|
|
#include "haptic.h"
|
|
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/device.h>
|
|
#include <zephyr/logging/log.h>
|
|
|
|
LOG_MODULE_REGISTER(zephcore_haptic, CONFIG_ZEPHCORE_SENSORS_LOG_LEVEL);
|
|
|
|
#if DT_HAS_COMPAT_STATUS_OKAY(ti_drv2605)
|
|
|
|
#include <zephyr/drivers/gpio.h>
|
|
#include <zephyr/drivers/haptics.h>
|
|
#include <zephyr/drivers/haptics/drv2605.h>
|
|
|
|
#define HAPTIC_NODE DT_COMPAT_GET_ANY_STATUS_OKAY(ti_drv2605)
|
|
|
|
#if DT_PROP(HAPTIC_NODE, zephyr_deferred_init)
|
|
/* Wake a deferred-init DRV2605 and run its init here, where the part's supply
|
|
* rail has been up since boot instead of microseconds.
|
|
*
|
|
* The enable pin is asserted *before* device_init() on purpose. Upstream's
|
|
* drv2605_init() spends its DRV2605_POWER_UP_DELAY_US before it configures the
|
|
* GPIOs, so EN rises and the very next thing it does is read the status
|
|
* register over I2C — no settling time at all between the two. Raising EN
|
|
* ourselves and waiting turns that race into a plain sequence; the driver's own
|
|
* gpio_pin_configure_dt() to OUTPUT_ACTIVE afterwards is then a no-op. */
|
|
static void haptic_deferred_init(const struct device *dev)
|
|
{
|
|
static const struct gpio_dt_spec en =
|
|
GPIO_DT_SPEC_GET_OR(HAPTIC_NODE, en_gpios, {0});
|
|
int rc;
|
|
|
|
if (en.port != NULL && gpio_is_ready_dt(&en)) {
|
|
if (gpio_pin_configure_dt(&en, GPIO_OUTPUT_ACTIVE) == 0) {
|
|
/* 250 us is the datasheet figure; 1 ms costs nothing on a
|
|
* one-shot boot path and covers the rail as well. */
|
|
k_msleep(1);
|
|
}
|
|
}
|
|
|
|
rc = device_init(dev);
|
|
LOG_DBG("haptic deferred init: rc=%d ready=%d", rc,
|
|
(int)device_is_ready(dev));
|
|
}
|
|
#else
|
|
static inline void haptic_deferred_init(const struct device *dev) { ARG_UNUSED(dev); }
|
|
#endif
|
|
|
|
static const struct device *haptic_dev;
|
|
static bool haptic_enabled = true;
|
|
|
|
/* Library effect 1 = "Strong Click - 100%", short enough to sit under a
|
|
* notification melody without outlasting it. */
|
|
static struct drv2605_rom_data rom_data = {
|
|
.trigger = DRV2605_MODE_INTERNAL_TRIGGER,
|
|
.library = DRV2605_LIBRARY_LRA,
|
|
.seq_regs = { 1 },
|
|
};
|
|
|
|
int haptic_init(void)
|
|
{
|
|
const union drv2605_config_data cfg = { .rom_data = &rom_data };
|
|
int rc;
|
|
|
|
haptic_dev = DEVICE_DT_GET(HAPTIC_NODE);
|
|
if (!device_is_ready(haptic_dev)) {
|
|
haptic_deferred_init(haptic_dev);
|
|
}
|
|
if (!device_is_ready(haptic_dev)) {
|
|
/* The DRV2605 driver logs its own reason at debug level, so say
|
|
* enough here to tell "chip absent" from "bus down". */
|
|
const struct device *bus = DEVICE_DT_GET(DT_BUS(HAPTIC_NODE));
|
|
|
|
LOG_WRN("haptic %s not ready (addr 0x%02x, bus %s %s) — "
|
|
"build with CONFIG_HAPTICS_LOG_LEVEL_DBG for the cause",
|
|
haptic_dev->name, (unsigned int)DT_REG_ADDR(HAPTIC_NODE),
|
|
bus->name, device_is_ready(bus) ? "up" : "DOWN");
|
|
haptic_dev = NULL;
|
|
return -ENODEV;
|
|
}
|
|
|
|
rc = drv2605_haptic_config(haptic_dev, DRV2605_HAPTICS_SOURCE_ROM, &cfg);
|
|
if (rc < 0) {
|
|
LOG_WRN("haptic config failed: %d", rc);
|
|
haptic_dev = NULL;
|
|
return rc;
|
|
}
|
|
|
|
LOG_INF("haptic driver ready");
|
|
return 0;
|
|
}
|
|
|
|
void haptic_pulse(void)
|
|
{
|
|
if (!haptic_dev || !haptic_enabled) {
|
|
return;
|
|
}
|
|
|
|
(void)haptics_start_output(haptic_dev);
|
|
}
|
|
|
|
void haptic_set_enabled(bool enabled)
|
|
{
|
|
haptic_enabled = enabled;
|
|
|
|
if (!enabled && haptic_dev) {
|
|
(void)haptics_stop_output(haptic_dev);
|
|
}
|
|
}
|
|
|
|
bool haptic_available(void)
|
|
{
|
|
return haptic_dev != NULL;
|
|
}
|
|
|
|
#else /* no ti,drv2605 node — link-compatible stubs */
|
|
|
|
int haptic_init(void) { return -ENODEV; }
|
|
void haptic_pulse(void) { }
|
|
void haptic_set_enabled(bool enabled) { ARG_UNUSED(enabled); }
|
|
bool haptic_available(void) { return false; }
|
|
|
|
#endif
|