diff --git a/drivers/sensor/pixart/CMakeLists.txt b/drivers/sensor/pixart/CMakeLists.txt index 010b153254b..73f35838608 100644 --- a/drivers/sensor/pixart/CMakeLists.txt +++ b/drivers/sensor/pixart/CMakeLists.txt @@ -1,6 +1,10 @@ # Copyright (c) 2025 Croxel Inc. # Copyright (c) 2025 CogniPilot Foundation +# Copyright (c) 2025 Paul Timke # SPDX-License-Identifier: Apache-2.0 +# zephyr-keep-sorted-start add_subdirectory_ifdef(CONFIG_PAA3905 paa3905) +add_subdirectory_ifdef(CONFIG_PAJ7620 paj7620) add_subdirectory_ifdef(CONFIG_PAT9136 pat9136) +# zephyr-keep-sorted-stop diff --git a/drivers/sensor/pixart/Kconfig b/drivers/sensor/pixart/Kconfig index 68d521e2600..14db51f6f52 100644 --- a/drivers/sensor/pixart/Kconfig +++ b/drivers/sensor/pixart/Kconfig @@ -1,8 +1,10 @@ # Copyright (c) 2025 Croxel Inc. # Copyright (c) 2025 CogniPilot Foundation +# Copyright (c) 2025 Paul Timke # SPDX-License-Identifier: Apache-2.0 # zephyr-keep-sorted-start source "drivers/sensor/pixart/paa3905/Kconfig" +source "drivers/sensor/pixart/paj7620/Kconfig" source "drivers/sensor/pixart/pat9136/Kconfig" # zephyr-keep-sorted-stop diff --git a/drivers/sensor/pixart/paj7620/CMakeLists.txt b/drivers/sensor/pixart/paj7620/CMakeLists.txt new file mode 100644 index 00000000000..b115ffef01c --- /dev/null +++ b/drivers/sensor/pixart/paj7620/CMakeLists.txt @@ -0,0 +1,7 @@ +# Copyright (c) 2025 Paul Timke +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() + +zephyr_library_sources(paj7620.c) +zephyr_library_sources_ifdef(CONFIG_PAJ7620_TRIGGER paj7620_trigger.c) diff --git a/drivers/sensor/pixart/paj7620/Kconfig b/drivers/sensor/pixart/paj7620/Kconfig new file mode 100644 index 00000000000..6dca4e7ab15 --- /dev/null +++ b/drivers/sensor/pixart/paj7620/Kconfig @@ -0,0 +1,21 @@ +# PAJ7620 gesture sensor configuration options + +# Copyright (c) 2025 Paul Timke +# SPDX-License-Identifier: Apache-2.0 + +menuconfig PAJ7620 + bool "PAJ7620 gesture sensor driver" + default y + depends on DT_HAS_PIXART_PAJ7620_ENABLED + select I2C + help + Enable driver for the PAJ7620 gesture sensor + +if PAJ7620 + +module = PAJ7620 +thread_priority = 10 +thread_stack_size = 1024 +source "drivers/sensor/Kconfig.trigger_template" + +endif # PAJ7620 diff --git a/drivers/sensor/pixart/paj7620/paj7620.c b/drivers/sensor/pixart/paj7620/paj7620.c new file mode 100644 index 00000000000..d729fe75bf9 --- /dev/null +++ b/drivers/sensor/pixart/paj7620/paj7620.c @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2025 Paul Timke + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT pixart_paj7620 + +#include +#include +#include +#include +#include +#include +#include + +#include "paj7620.h" +#include "paj7620_reg.h" + +LOG_MODULE_REGISTER(paj7620, CONFIG_SENSOR_LOG_LEVEL); + +static int paj7620_select_register_bank(const struct device *dev, enum paj7620_mem_bank bank) +{ + int ret; + uint8_t bank_selection; + const struct paj7620_config *config = dev->config; + + switch (bank) { + case PAJ7620_MEMBANK_0: + bank_selection = PAJ7620_VAL_BANK_SEL_BANK_0; + break; + case PAJ7620_MEMBANK_1: + bank_selection = PAJ7620_VAL_BANK_SEL_BANK_1; + break; + default: + LOG_ERR("Nonexistent memory bank %d", (int)bank); + return -EINVAL; + } + + ret = i2c_reg_write_byte_dt(&config->i2c, PAJ7620_REG_BANK_SEL, bank_selection); + if (ret < 0) { + LOG_ERR("Failed to change memory bank"); + return ret; + } + + return 0; +} + +static int paj7620_get_hw_id(const struct device *dev, uint16_t *result) +{ + uint8_t ret; + uint8_t hw_id[2]; + const struct paj7620_config *config = dev->config; + + /* Part ID is stored in bank 0 */ + ret = paj7620_select_register_bank(dev, PAJ7620_MEMBANK_0); + if (ret < 0) { + return ret; + } + + ret = i2c_reg_read_byte_dt(&config->i2c, PAJ7620_REG_PART_ID_LSB, &hw_id[0]); + if (ret < 0) { + LOG_ERR("Failed to read hardware ID"); + return ret; + } + + ret = i2c_reg_read_byte_dt(&config->i2c, PAJ7620_REG_PART_ID_MSB, &hw_id[1]); + if (ret < 0) { + LOG_ERR("Failed to read hardware ID"); + return ret; + } + + *result = sys_get_le16(hw_id); + LOG_DBG("Obtained hardware ID 0x%04x", *result); + + return 0; +} + +static int paj7620_write_initial_reg_settings(const struct device *dev) +{ + int ret; + uint8_t reg_addr; + uint8_t value; + const struct paj7620_config *config = dev->config; + + /** + * Initializes registers with default values according to section 8.1 + * from Datasheet v1.5: + * https://files.seeedstudio.com/wiki/Grove_Gesture_V_1.0/res/PAJ7620U2_DS_v1.5_05012022_Confidential.pdf + */ + + for (int i = 0; i < ARRAY_SIZE(initial_register_array); i++) { + reg_addr = initial_register_array[i][0]; + value = initial_register_array[i][1]; + + ret = i2c_reg_write_byte_dt(&config->i2c, reg_addr, value); + if (ret < 0) { + return ret; + } + } + + return ret; +} + +static int paj7620_set_sampling_rate(const struct device *dev, const struct sensor_value *val) +{ + int ret; + int fps; + const struct paj7620_config *config = dev->config; + int64_t uval = val->val1 * 1000000 + val->val2; + + if (uval <= 120000000) { + fps = PAJ7620_NORMAL_SPEED; + } else if (uval <= 240000000) { + fps = PAJ7620_GAME_SPEED; + } else { + LOG_ERR("Unsupported sample rate"); + return -ENOTSUP; + } + + ret = paj7620_select_register_bank(dev, PAJ7620_MEMBANK_1); + if (ret < 0) { + return ret; + } + + ret = i2c_reg_write_byte_dt(&config->i2c, PAJ7620_REG_R_IDLE_TIME_LSB, fps); + if (ret < 0) { + LOG_ERR("Failed to set sample rate"); + return ret; + } + + ret = paj7620_select_register_bank(dev, PAJ7620_MEMBANK_0); + if (ret < 0) { + return ret; + } + + LOG_DBG("Sample rate set to %s mode", fps == PAJ7620_GAME_SPEED ? "game" : "normal"); + + return 0; +} + +static int paj7620_sample_fetch(const struct device *dev, enum sensor_channel chan) +{ + int ret; + struct paj7620_data *data = dev->data; + const struct paj7620_config *config = dev->config; + uint8_t gest_data[2]; + + if (chan != SENSOR_CHAN_ALL + && ((enum sensor_channel_paj7620)chan != SENSOR_CHAN_PAJ7620_GESTURES)) { + return -ENOTSUP; + } + + /* We read from REG_INT_FLAG_1 and REG_INT_FLAG_2 even on polling mode + * (without using interrupts) because that's where the gesture + * detection flags are set. + * NOTE: A set bit means that the corresponding gesture has been detected + */ + + ret = i2c_burst_read_dt(&config->i2c, PAJ7620_REG_INT_FLAG_1, gest_data, sizeof(gest_data)); + if (ret < 0) { + LOG_ERR("Failed to read gesture data"); + return ret; + } + + data->gesture_flags = sys_get_le16(gest_data); + + return 0; +} + +static int paj7620_channel_get(const struct device *dev, + enum sensor_channel chan, + struct sensor_value *val) +{ + struct paj7620_data *data = dev->data; + + switch ((uint32_t)chan) { + case SENSOR_CHAN_PAJ7620_GESTURES: + val->val1 = data->gesture_flags; + val->val2 = 0; + break; + default: + LOG_ERR("Unsupported sensor channel"); + return -ENOTSUP; + } + + return 0; +} + +static int paj7620_attr_set(const struct device *dev, + enum sensor_channel chan, + enum sensor_attribute attr, + const struct sensor_value *val) +{ + int ret; + + if (chan != SENSOR_CHAN_ALL + && ((enum sensor_channel_paj7620)chan != SENSOR_CHAN_PAJ7620_GESTURES)) { + return -ENOTSUP; + } + + switch ((uint32_t)attr) { + case SENSOR_ATTR_SAMPLING_FREQUENCY: + ret = paj7620_set_sampling_rate(dev, val); + break; + + default: + return -ENOTSUP; + } + + return ret; +} + +static int paj7620_init(const struct device *dev) +{ + int ret; + uint16_t hw_id; + const struct paj7620_config *config = dev->config; + + if (!i2c_is_ready_dt(&config->i2c)) { + LOG_ERR("I2C bus device not ready"); + return -ENODEV; + } + + /** + * According to the datasheet section 8.1, we must wait this amount + * of time for sensor to stabilize after power up + */ + k_usleep(PAJ7620_POWERUP_STABILIZATION_TIME_US); + + /** + * Make a write to the sensor to wake it up. After waking it, the + * the sensor still needs some time to be ready to listen. Without it, + * it may NACK subsequent transactions. + */ + (void)paj7620_select_register_bank(dev, PAJ7620_MEMBANK_0); + k_usleep(PAJ7620_WAKEUP_TIME_US); + + /** Verify this is not some other sensor with the same address */ + ret = paj7620_get_hw_id(dev, &hw_id); + if (ret < 0) { + return ret; + } + + if (hw_id != PAJ7620_PART_ID) { + LOG_ERR("Hardware ID 0x%04x does not match for PAJ7620", hw_id); + return -ENOTSUP; + } + + /** Initialize settings (it defaults to gesture mode) */ + ret = paj7620_write_initial_reg_settings(dev); + if (ret < 0) { + LOG_ERR("Failed to initialize device registers"); + return ret; + } + + if (IS_ENABLED(CONFIG_PAJ7620_TRIGGER)) { + ret = paj7620_trigger_init(dev); + if (ret < 0) { + LOG_ERR("Failed to enable interrupts"); + return ret; + } + } + + return 0; +} + +static DEVICE_API(sensor, paj7620_driver_api) = { + .sample_fetch = paj7620_sample_fetch, + .channel_get = paj7620_channel_get, + .attr_set = paj7620_attr_set, +#if CONFIG_PAJ7620_TRIGGER + .trigger_set = paj7620_trigger_set, +#endif +}; + +#define PAJ7620_INIT(n) \ + static const struct paj7620_config paj7620_config_##n = { \ + .i2c = I2C_DT_SPEC_INST_GET(n), \ + IF_ENABLED(CONFIG_PAJ7620_TRIGGER, \ + (.int_gpio = GPIO_DT_SPEC_INST_GET_OR(n, int_gpios, {0}))) \ + }; \ + \ + static struct paj7620_data paj7620_data_##n; \ + \ + SENSOR_DEVICE_DT_INST_DEFINE(n, \ + paj7620_init, \ + NULL, \ + &paj7620_data_##n, \ + &paj7620_config_##n, \ + POST_KERNEL, \ + CONFIG_SENSOR_INIT_PRIORITY, \ + &paj7620_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(PAJ7620_INIT); diff --git a/drivers/sensor/pixart/paj7620/paj7620.h b/drivers/sensor/pixart/paj7620/paj7620.h new file mode 100644 index 00000000000..688bf0ad86b --- /dev/null +++ b/drivers/sensor/pixart/paj7620/paj7620.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2025 Paul Timke + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_PAJ7620_H_ +#define ZEPHYR_DRIVERS_SENSOR_PAJ7620_H_ + +#include +#include +#include +#include +#include + +/** Sensor hardcoded Part ID */ +#define PAJ7620_PART_ID ((PAJ7620_VAL_PART_ID_MSB << 8) | (PAJ7620_VAL_PART_ID_LSB & 0x00FF)) + +/** Sensor FPS values */ +#define PAJ7620_NORMAL_SPEED 0xAC /* Normal speed 120 fps */ +#define PAJ7620_GAME_SPEED 0x30 /* Game mode speed 240 fps */ + +/** Sensor stabilization time microseconds (us) */ +#define PAJ7620_POWERUP_STABILIZATION_TIME_US 700 + +/** Sensor stabilization time after I2C wakeup (us). + * PAJ7620 still needs some time to wake up after waking it + * with an I2C write. This value was obtained experimentally + */ +#define PAJ7620_WAKEUP_TIME_US 200 + +enum paj7620_mem_bank { + PAJ7620_MEMBANK_0 = 0, + PAJ7620_MEMBANK_1, +}; + +struct paj7620_config { + const struct i2c_dt_spec i2c; +#if CONFIG_PAJ7620_TRIGGER + struct gpio_dt_spec int_gpio; +#endif +}; + +struct paj7620_data { + struct k_sem sem; + uint16_t gesture_flags; + +#ifdef CONFIG_PAJ7620_TRIGGER + const struct device *dev; + struct gpio_callback gpio_cb; + sensor_trigger_handler_t motion_handler; + const struct sensor_trigger *motion_trig; +#endif +#ifdef CONFIG_PAJ7620_TRIGGER_OWN_THREAD + K_KERNEL_STACK_MEMBER(thread_stack, CONFIG_PAJ7620_THREAD_STACK_SIZE); + struct k_thread thread; + struct k_sem trig_sem; +#endif +#ifdef CONFIG_PAJ7620_TRIGGER_GLOBAL_THREAD + struct k_work work; +#endif +}; + +int paj7620_trigger_init(const struct device *dev); +int paj7620_trigger_set(const struct device *dev, + const struct sensor_trigger *trig, + sensor_trigger_handler_t handler); + +#endif /* ZEPHYR_DRIVERS_SENSOR_PAJ7620_H_ */ diff --git a/drivers/sensor/pixart/paj7620/paj7620_reg.h b/drivers/sensor/pixart/paj7620/paj7620_reg.h new file mode 100644 index 00000000000..b4a4e667255 --- /dev/null +++ b/drivers/sensor/pixart/paj7620/paj7620_reg.h @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2025 Paul Timke + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_DRIVERS_SENSOR_PAJ7620_REG_H_ +#define ZEPHYR_DRIVERS_SENSOR_PAJ7620_REG_H_ + +#include + +/** + * @file + * @brief PAJ7620 Register addresses and values. Not all registers from the + * data sheet are listed here, only the ones directly used by the driver. + * For more information about the registers, refer to: + * https://files.seeedstudio.com/wiki/Grove_Gesture_V_1.0/res/PAJ7620U2_DS_v1.5_05012022_Confidential.pdf + */ + +/** + * BANK 0 REGISTER ADDRESSES + */ + +/* Chip / Version ID */ +#define PAJ7620_REG_PART_ID_LSB 0x00 +#define PAJ7620_REG_PART_ID_MSB 0x01 + +/** Register bank select */ +#define PAJ7620_REG_BANK_SEL 0xEF + +/** Interrupt Controls */ +/* Note: If the corresponding bit is 1, the corresponding interrupt is enabled */ +#define PAJ7620_REG_MCU_INT_CTRL 0x40 /* Configure auto clean and active high/low */ +#define PAJ7620_REG_INT_1_EN 0x41 /* Enable int 1 interrupts */ +#define PAJ7620_REG_INT_2_EN 0x42 /* Enable int 2 interrupts */ +#define PAJ7620_REG_INT_FLAG_1 0x43 /* Gesture detection results */ +#define PAJ7620_REG_INT_FLAG_2 0x44 /* Gesture detection results (wave and others) */ + +/** + * BANK 0 REGISTER VALUES AND MASKS + */ +#define PAJ7620_VAL_PART_ID_LSB 0x20 +#define PAJ7620_VAL_PART_ID_MSB 0x76 + +/** Register bank select values */ +#define PAJ7620_VAL_BANK_SEL_BANK_0 0x00 +#define PAJ7620_VAL_BANK_SEL_BANK_1 0x01 + +/** Interrupt controls masks and values */ +#define PAJ7620_MASK_MCU_INT_FLAG_GCLR BIT(1) +#define PAJ7620_VAL_MCU_INT_FLAG_AUTO_CLEAN_DISABLE 0x00 +#define PAJ7620_VAL_MCU_INT_FLAG_AUTO_CLEAN_ENABLE 0x01 + +#define PAJ7620_MASK_MCU_INT_FLAG_INV BIT(4) +#define PAJ7620_VAL_MCU_INT_FLAG_PIN_ACTIVE_LOW 0x00 +#define PAJ7620_VAL_MCU_INT_FLAG_PIN_ACTIVE_HIGH 0x01 + +#define PAJ7620_MASK_ALL_GESTURE_INTS_ENABLE 0xFF +#define PAJ7620_MASK_ALL_GESTURE_INTS_DISABLE 0x00 + +/** + * BANK 1 REGISTER ADDRESSES + */ +#define PAJ7620_REG_R_IDLE_TIME_LSB 0x65 +#define PAJ7620_REG_R_IDLE_TIME_MSB 0x66 + +/** + * INITIALIZATION ARRAYS + * The following 'initial_register_array' is taken 'as is' from Section 8 + * (Firmware Guides) of the PAJ7620 datasheet v1.5. + * It encodes pairs of register addresses and values for those registers + * needed to initialize the sensor or change its operation mode + * + * Reference: + * https://files.seeedstudio.com/wiki/Grove_Gesture_V_1.0/res/PAJ7620U2_DS_v1.5_05012022_Confidential.pdf + */ + +static const uint8_t initial_register_array[][2] = { + {0xEF, 0x00}, /* Select memory bank 0 */ + {0x41, 0xFF}, /* Enable gesture interrupts */ + {0x42, 0x01}, + {0x46, 0x2D}, + {0x47, 0x0F}, + {0x48, 0x80}, + {0x49, 0x00}, + {0x4A, 0x40}, + {0x4B, 0x00}, + {0x4C, 0x20}, + {0x4D, 0x00}, + {0x51, 0x10}, + {0x5C, 0x02}, + {0x5E, 0x10}, + {0x80, 0x41}, + {0x81, 0x44}, + {0x82, 0x0C}, + {0x83, 0x20}, + {0x84, 0x20}, + {0x85, 0x00}, + {0x86, 0x10}, + {0x87, 0x00}, + {0x8B, 0x01}, + {0x8D, 0x00}, + {0x90, 0x0C}, + {0x91, 0x0C}, + {0x93, 0x0D}, + {0x94, 0x0A}, + {0x95, 0x0A}, + {0x96, 0x0C}, + {0x97, 0x05}, + {0x9A, 0x14}, + {0x9C, 0x3F}, + {0x9F, 0xF9}, + {0xA0, 0x48}, + {0xA5, 0x19}, + {0xCC, 0x19}, + {0xCD, 0x0B}, + {0xCE, 0x13}, + {0xCF, 0x62}, + {0xD0, 0x21}, + {0xEF, 0x01}, /* Select memory bank 1 */ + {0x00, 0x1E}, + {0x01, 0x1E}, + {0x02, 0x0F}, + {0x03, 0x0F}, + {0x04, 0x02}, + {0x25, 0x01}, + {0x26, 0x00}, + {0x27, 0x39}, + {0x28, 0x7F}, + {0x29, 0x08}, + {0x30, 0x03}, + {0x3E, 0xFF}, + {0x5E, 0x3D}, + {0x65, 0xAC}, /* Set fps to 'normal' mode */ + {0x66, 0x00}, + {0x67, 0x97}, + {0x68, 0x01}, + {0x69, 0xCD}, + {0x6A, 0x01}, + {0x6B, 0xB0}, + {0x6C, 0x04}, + {0x6D, 0x2C}, + {0x6E, 0x01}, + {0x72, 0x01}, + {0x73, 0x35}, + {0x74, 0x00}, /* Set to gesture mode */ + {0x77, 0x01}, + {0xEF, 0x00}, /* Reselect memory bank 0 */ +}; + +#endif /* ZEPHYR_DRIVERS_SENSOR_PAJ7620_REG_H_ */ diff --git a/drivers/sensor/pixart/paj7620/paj7620_trigger.c b/drivers/sensor/pixart/paj7620/paj7620_trigger.c new file mode 100644 index 00000000000..b5388ac5ea9 --- /dev/null +++ b/drivers/sensor/pixart/paj7620/paj7620_trigger.c @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2025 Paul Timke + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT pixart_paj7620 + +#include "paj7620.h" +#include +#include + +LOG_MODULE_REGISTER(paj7620, CONFIG_SENSOR_LOG_LEVEL); + +static void paj7620_gpio_callback(const struct device *dev, + struct gpio_callback *cb, + uint32_t pin_mask) +{ + struct paj7620_data *data = + CONTAINER_OF(cb, struct paj7620_data, gpio_cb); + const struct paj7620_config *config = data->dev->config; + + if ((pin_mask & BIT(config->int_gpio.pin)) == 0U) { + return; + } + +#ifdef CONFIG_PAJ7620_TRIGGER_OWN_THREAD + k_sem_give(&data->trig_sem); +#elif CONFIG_PAJ7620_TRIGGER_GLOBAL_THREAD + k_work_submit(&data->work); +#endif +} + +static void paj7620_handle_int(const struct device *dev) +{ + struct paj7620_data *data = dev->data; + + if (data->motion_handler) { + data->motion_handler(dev, data->motion_trig); + } +} + +#ifdef CONFIG_PAJ7620_TRIGGER_OWN_THREAD +static void paj7620_thread_main(void *p1, void *p2, void *p3) +{ + ARG_UNUSED(p1); + ARG_UNUSED(p2); + + struct paj7620_data *data = p1; + + while (1) { + k_sem_take(&data->trig_sem, K_FOREVER); + paj7620_handle_int(data->dev); + } +} +#endif + +#ifdef CONFIG_PAJ7620_TRIGGER_GLOBAL_THREAD +static void paj7620_work_handler(struct k_work *work) +{ + struct paj7620_data *data = + CONTAINER_OF(work, struct paj7620_data, work); + + paj7620_handle_int(data->dev); +} +#endif + +int paj7620_trigger_set(const struct device *dev, + const struct sensor_trigger *trig, + sensor_trigger_handler_t handler) +{ + struct paj7620_data *data = dev->data; + const struct paj7620_config *cfg = dev->config; + + if (cfg->int_gpio.port == NULL) { + return -ENOTSUP; + } + + if (trig->type != SENSOR_TRIG_MOTION) { + LOG_ERR("Unsupported sensor trigger"); + return -ENOTSUP; + } + + data->motion_handler = handler; + data->motion_trig = trig; + + if (handler == NULL) { + return gpio_pin_interrupt_configure_dt(&cfg->int_gpio, GPIO_INT_DISABLE); + } + + return gpio_pin_interrupt_configure_dt(&cfg->int_gpio, GPIO_INT_EDGE_FALLING); +} + +int paj7620_trigger_init(const struct device *dev) +{ + int ret; + const struct paj7620_config *config = dev->config; + struct paj7620_data *data = dev->data; + + data->dev = dev; + +#ifdef CONFIG_PAJ7620_TRIGGER_OWN_THREAD + k_sem_init(&data->trig_sem, 0, K_SEM_MAX_LIMIT); + k_thread_create(&data->thread, + data->thread_stack, + K_KERNEL_STACK_SIZEOF(data->thread_stack), + paj7620_thread_main, + data, + NULL, + NULL, + K_PRIO_COOP(CONFIG_PAJ7620_THREAD_PRIORITY), + 0, + K_NO_WAIT); +#elif CONFIG_PAJ7620_TRIGGER_GLOBAL_THREAD + data->work.handler = paj7620_work_handler; +#endif + + /* Configure GPIO */ + if (!gpio_is_ready_dt(&config->int_gpio)) { + LOG_ERR("GPIO device not ready"); + return -ENODEV; + } + + ret = gpio_pin_configure_dt(&config->int_gpio, GPIO_INPUT); + if (ret < 0) { + return ret; + } + + gpio_init_callback(&data->gpio_cb, paj7620_gpio_callback, BIT(config->int_gpio.pin)); + + ret = gpio_add_callback(config->int_gpio.port, &data->gpio_cb); + if (ret < 0) { + return ret; + } + + return 0; +} diff --git a/dts/bindings/sensor/pixart,paj7620.yaml b/dts/bindings/sensor/pixart,paj7620.yaml new file mode 100644 index 00000000000..2fec7adb76d --- /dev/null +++ b/dts/bindings/sensor/pixart,paj7620.yaml @@ -0,0 +1,17 @@ +# Copyright (c) 2025 Paul Timke +# SPDX-License-Identifier: Apache-2.0 + +description: Pixart PAJ7620 gesture sensor + +compatible: "pixart,paj7620" + +include: [sensor-device.yaml, i2c-device.yaml] + +properties: + int-gpios: + type: phandle-array + description: | + INT pin + This pin defaults to active low when produced by the sensor. + The property value should ensure the flags properly describe + the signal that is presented to the driver. diff --git a/include/zephyr/drivers/sensor/paj7620.h b/include/zephyr/drivers/sensor/paj7620.h new file mode 100644 index 00000000000..125d77d58dd --- /dev/null +++ b/include/zephyr/drivers/sensor/paj7620.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2025 Paul Timke + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Extended Public API for PAJ7620 sensor + * + * Some capabilities of the sensor cannot be expressed + * within the sensor driver abstraction + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_SENSOR_PAJ7620_H_ +#define ZEPHYR_INCLUDE_DRIVERS_SENSOR_PAJ7620_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +#include + +#define PAJ7620_FLAG_GES_UP BIT(0) +#define PAJ7620_FLAG_GES_DOWN BIT(1) +#define PAJ7620_FLAG_GES_LEFT BIT(2) +#define PAJ7620_FLAG_GES_RIGHT BIT(3) +#define PAJ7620_FLAG_GES_FORWARD BIT(4) +#define PAJ7620_FLAG_GES_BACKWARD BIT(5) +#define PAJ7620_FLAG_GES_CLOCKWISE BIT(6) +#define PAJ7620_FLAG_GES_COUNTERCLOCKWISE BIT(7) +#define PAJ7620_FLAG_GES_WAVE BIT(8) + +enum sensor_channel_paj7620 { + /** + * This channel will contain gesture data as a bitmask where each + * set bit represents a detected gesture. The possible gestures + * that can be detected along with their corresponding bit are given + * by the PAJ7620_FLAG_GES_X macros + */ + SENSOR_CHAN_PAJ7620_GESTURES = SENSOR_CHAN_PRIV_START +}; + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* ZEPHYR_INCLUDE_DRIVERS_SENSOR_PAJ7620_H_ */