input: add a pat912x driver
Add an initial input driver for the PixArt PAT9125EL, just core functionalities for now, will add more configuration properties at a later stage. Signed-off-by: Fabio Baltieri <fabiobaltieri@google.com>
This commit is contained in:
parent
38b63e9e77
commit
3538335f5a
|
@ -18,6 +18,7 @@ zephyr_library_sources_ifdef(CONFIG_INPUT_GT911 input_gt911.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_INPUT_ITE_IT8XXX2_KBD input_ite_it8xxx2_kbd.c)
|
zephyr_library_sources_ifdef(CONFIG_INPUT_ITE_IT8XXX2_KBD input_ite_it8xxx2_kbd.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_INPUT_KBD_MATRIX input_kbd_matrix.c)
|
zephyr_library_sources_ifdef(CONFIG_INPUT_KBD_MATRIX input_kbd_matrix.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_INPUT_NPCX_KBD input_npcx_kbd.c)
|
zephyr_library_sources_ifdef(CONFIG_INPUT_NPCX_KBD input_npcx_kbd.c)
|
||||||
|
zephyr_library_sources_ifdef(CONFIG_INPUT_PAT912X input_pat912x.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_INPUT_STMPE811 input_stmpe811.c)
|
zephyr_library_sources_ifdef(CONFIG_INPUT_STMPE811 input_stmpe811.c)
|
||||||
zephyr_library_sources_ifdef(CONFIG_INPUT_XPT2046 input_xpt2046.c)
|
zephyr_library_sources_ifdef(CONFIG_INPUT_XPT2046 input_xpt2046.c)
|
||||||
# zephyr-keep-sorted-stop
|
# zephyr-keep-sorted-stop
|
||||||
|
|
|
@ -20,6 +20,7 @@ source "drivers/input/Kconfig.gt911"
|
||||||
source "drivers/input/Kconfig.it8xxx2"
|
source "drivers/input/Kconfig.it8xxx2"
|
||||||
source "drivers/input/Kconfig.kbd_matrix"
|
source "drivers/input/Kconfig.kbd_matrix"
|
||||||
source "drivers/input/Kconfig.npcx"
|
source "drivers/input/Kconfig.npcx"
|
||||||
|
source "drivers/input/Kconfig.pat912x"
|
||||||
source "drivers/input/Kconfig.sdl"
|
source "drivers/input/Kconfig.sdl"
|
||||||
source "drivers/input/Kconfig.stmpe811"
|
source "drivers/input/Kconfig.stmpe811"
|
||||||
source "drivers/input/Kconfig.xpt2046"
|
source "drivers/input/Kconfig.xpt2046"
|
||||||
|
|
10
drivers/input/Kconfig.pat912x
Normal file
10
drivers/input/Kconfig.pat912x
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
# Copyright 2024 Google LLC
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
config INPUT_PAT912X
|
||||||
|
bool "PAT912X miniature optical navigation chip input driver"
|
||||||
|
default y
|
||||||
|
depends on DT_HAS_PIXART_PAT912X_ENABLED
|
||||||
|
select I2C
|
||||||
|
help
|
||||||
|
PAT912X miniature optical navigation chip input driver
|
268
drivers/input/input_pat912x.c
Normal file
268
drivers/input/input_pat912x.c
Normal file
|
@ -0,0 +1,268 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 Google LLC
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define DT_DRV_COMPAT pixart_pat912x
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <zephyr/device.h>
|
||||||
|
#include <zephyr/drivers/gpio.h>
|
||||||
|
#include <zephyr/drivers/i2c.h>
|
||||||
|
#include <zephyr/input/input.h>
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
#include <zephyr/logging/log.h>
|
||||||
|
#include <zephyr/pm/device.h>
|
||||||
|
#include <zephyr/pm/device_runtime.h>
|
||||||
|
#include <zephyr/sys/byteorder.h>
|
||||||
|
#include <zephyr/sys/util.h>
|
||||||
|
|
||||||
|
LOG_MODULE_REGISTER(input_pat912x, CONFIG_INPUT_LOG_LEVEL);
|
||||||
|
|
||||||
|
#define PAT912X_PRODUCT_ID1 0x00
|
||||||
|
#define PAT912X_PRODUCT_ID2 0x01
|
||||||
|
#define PAT912X_MOTION_STATUS 0x02
|
||||||
|
#define PAT912X_DELTA_X_LO 0x03
|
||||||
|
#define PAT912X_DELTA_Y_LO 0x04
|
||||||
|
#define PAT912X_OPERATION_MODE 0x05
|
||||||
|
#define PAT912X_CONFIGURATION 0x06
|
||||||
|
#define PAT912X_WRITE_PROTECT 0x09
|
||||||
|
#define PAT912X_SLEEP1 0x0a
|
||||||
|
#define PAT912X_SLEEP2 0x0b
|
||||||
|
#define PAT912X_RES_X 0x0d
|
||||||
|
#define PAT912X_RES_Y 0x0e
|
||||||
|
#define PAT912X_DELTA_XY_HI 0x12
|
||||||
|
#define PAT912X_SHUTTER 0x14
|
||||||
|
#define PAT912X_FRAME_AVG 0x17
|
||||||
|
#define PAT912X_ORIENTATION 0x19
|
||||||
|
|
||||||
|
#define PRODUCT_ID_PAT9125EL 0x3191
|
||||||
|
|
||||||
|
#define CONFIGURATION_RESET 0x97
|
||||||
|
#define CONFIGURATION_CLEAR 0x17
|
||||||
|
#define CONFIGURATION_PD_ENH BIT(3)
|
||||||
|
#define WRITE_PROTECT_ENABLE 0x00
|
||||||
|
#define WRITE_PROTECT_DISABLE 0x5a
|
||||||
|
#define MOTION_STATUS_MOTION BIT(7)
|
||||||
|
|
||||||
|
#define PAT912X_DATA_SIZE_BITS 12
|
||||||
|
|
||||||
|
#define RESET_DELAY_MS 2
|
||||||
|
|
||||||
|
struct pat912x_config {
|
||||||
|
struct i2c_dt_spec i2c;
|
||||||
|
struct gpio_dt_spec motion_gpio;
|
||||||
|
int32_t axis_x;
|
||||||
|
int32_t axis_y;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pat912x_data {
|
||||||
|
const struct device *dev;
|
||||||
|
struct k_work motion_work;
|
||||||
|
struct gpio_callback motion_cb;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void pat912x_motion_work_handler(struct k_work *work)
|
||||||
|
{
|
||||||
|
struct pat912x_data *data = CONTAINER_OF(
|
||||||
|
work, struct pat912x_data, motion_work);
|
||||||
|
const struct device *dev = data->dev;
|
||||||
|
const struct pat912x_config *cfg = dev->config;
|
||||||
|
int32_t x, y;
|
||||||
|
uint8_t val;
|
||||||
|
uint8_t xy[2];
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = i2c_reg_read_byte_dt(&cfg->i2c, PAT912X_MOTION_STATUS, &val);
|
||||||
|
if (ret < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((val & MOTION_STATUS_MOTION) == 0x00) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = i2c_burst_read_dt(&cfg->i2c, PAT912X_DELTA_X_LO, xy, sizeof(xy));
|
||||||
|
if (ret < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
x = xy[0];
|
||||||
|
y = xy[1];
|
||||||
|
|
||||||
|
ret = i2c_reg_read_byte_dt(&cfg->i2c, PAT912X_DELTA_XY_HI, &val);
|
||||||
|
if (ret < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
y |= (val << 8) & 0xf00;
|
||||||
|
x |= (val << 4) & 0xf00;
|
||||||
|
|
||||||
|
x = sign_extend(x, PAT912X_DATA_SIZE_BITS - 1);
|
||||||
|
y = sign_extend(y, PAT912X_DATA_SIZE_BITS - 1);
|
||||||
|
|
||||||
|
LOG_DBG("x=%4d y=%4d", x, y);
|
||||||
|
|
||||||
|
if (cfg->axis_x >= 0) {
|
||||||
|
bool sync = cfg->axis_y < 0;
|
||||||
|
|
||||||
|
input_report_rel(data->dev, cfg->axis_x, x, sync, K_FOREVER);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cfg->axis_y >= 0) {
|
||||||
|
input_report_rel(data->dev, cfg->axis_y, y, true, K_FOREVER);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Trigger one more scan in case more data is available. */
|
||||||
|
k_work_submit(&data->motion_work);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pat912x_motion_handler(const struct device *gpio_dev,
|
||||||
|
struct gpio_callback *cb,
|
||||||
|
uint32_t pins)
|
||||||
|
{
|
||||||
|
struct pat912x_data *data = CONTAINER_OF(
|
||||||
|
cb, struct pat912x_data, motion_cb);
|
||||||
|
|
||||||
|
k_work_submit(&data->motion_work);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pat912x_configure(const struct device *dev)
|
||||||
|
{
|
||||||
|
const struct pat912x_config *cfg = dev->config;
|
||||||
|
uint8_t id[2];
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = i2c_burst_read_dt(&cfg->i2c, PAT912X_PRODUCT_ID1, id, sizeof(id));
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sys_get_be16(id) != PRODUCT_ID_PAT9125EL) {
|
||||||
|
LOG_ERR("Invalid product id: %04x", sys_get_be16(id));
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Software reset */
|
||||||
|
|
||||||
|
i2c_reg_write_byte_dt(&cfg->i2c, PAT912X_CONFIGURATION, CONFIGURATION_RESET);
|
||||||
|
/* no ret value check, the device NACKs */
|
||||||
|
|
||||||
|
k_sleep(K_MSEC(RESET_DELAY_MS));
|
||||||
|
|
||||||
|
ret = i2c_reg_write_byte_dt(&cfg->i2c, PAT912X_CONFIGURATION, CONFIGURATION_CLEAR);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pat912x_init(const struct device *dev)
|
||||||
|
{
|
||||||
|
const struct pat912x_config *cfg = dev->config;
|
||||||
|
struct pat912x_data *data = dev->data;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!i2c_is_ready_dt(&cfg->i2c)) {
|
||||||
|
LOG_ERR("%s is not ready", cfg->i2c.bus->name);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->dev = dev;
|
||||||
|
|
||||||
|
k_work_init(&data->motion_work, pat912x_motion_work_handler);
|
||||||
|
|
||||||
|
if (!gpio_is_ready_dt(&cfg->motion_gpio)) {
|
||||||
|
LOG_ERR("%s is not ready", cfg->motion_gpio.port->name);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = gpio_pin_configure_dt(&cfg->motion_gpio, GPIO_INPUT);
|
||||||
|
if (ret != 0) {
|
||||||
|
LOG_ERR("Motion pin configuration failed: %d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = gpio_pin_interrupt_configure_dt(&cfg->motion_gpio,
|
||||||
|
GPIO_INT_EDGE_TO_ACTIVE);
|
||||||
|
if (ret != 0) {
|
||||||
|
LOG_ERR("Motion interrupt configuration failed: %d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
gpio_init_callback(&data->motion_cb, pat912x_motion_handler,
|
||||||
|
BIT(cfg->motion_gpio.pin));
|
||||||
|
|
||||||
|
ret = pat912x_configure(dev);
|
||||||
|
if (ret != 0) {
|
||||||
|
LOG_ERR("Device configuration failed: %d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = gpio_add_callback_dt(&cfg->motion_gpio, &data->motion_cb);
|
||||||
|
if (ret < 0) {
|
||||||
|
LOG_ERR("Could not set motion callback: %d", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Trigger an initial read to clear any pending motion status.*/
|
||||||
|
k_work_submit(&data->motion_work);
|
||||||
|
|
||||||
|
ret = pm_device_runtime_enable(dev);
|
||||||
|
if (ret < 0) {
|
||||||
|
LOG_ERR("Failed to enable runtime power management");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_PM_DEVICE
|
||||||
|
static int pat912x_pm_action(const struct device *dev,
|
||||||
|
enum pm_device_action action)
|
||||||
|
{
|
||||||
|
const struct pat912x_config *cfg = dev->config;
|
||||||
|
uint8_t val;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case PM_DEVICE_ACTION_SUSPEND:
|
||||||
|
val = CONFIGURATION_PD_ENH;
|
||||||
|
break;
|
||||||
|
case PM_DEVICE_ACTION_RESUME:
|
||||||
|
val = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = i2c_reg_update_byte_dt(&cfg->i2c, PAT912X_CONFIGURATION,
|
||||||
|
CONFIGURATION_PD_ENH, val);
|
||||||
|
if (ret < 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define PAT912X_INIT(n) \
|
||||||
|
static const struct pat912x_config pat912x_cfg_##n = { \
|
||||||
|
.i2c = I2C_DT_SPEC_INST_GET(n), \
|
||||||
|
.motion_gpio = GPIO_DT_SPEC_INST_GET(n, motion_gpios), \
|
||||||
|
.axis_x = DT_INST_PROP_OR(n, zephyr_axis_x, -1), \
|
||||||
|
.axis_y = DT_INST_PROP_OR(n, zephyr_axis_y, -1), \
|
||||||
|
}; \
|
||||||
|
\
|
||||||
|
static struct pat912x_data pat912x_data_##n; \
|
||||||
|
\
|
||||||
|
PM_DEVICE_DT_INST_DEFINE(n, pat912x_pm_action); \
|
||||||
|
\
|
||||||
|
DEVICE_DT_INST_DEFINE(n, pat912x_init, PM_DEVICE_DT_INST_GET(n), \
|
||||||
|
&pat912x_data_##n, &pat912x_cfg_##n, \
|
||||||
|
POST_KERNEL, CONFIG_INPUT_INIT_PRIORITY, \
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
DT_INST_FOREACH_STATUS_OKAY(PAT912X_INIT)
|
27
dts/bindings/input/pixart,pat912x.yaml
Normal file
27
dts/bindings/input/pixart,pat912x.yaml
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
# Copyright 2024 Google LLC
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
description: PAT9125EL Miniature Optical Navigation Chip
|
||||||
|
|
||||||
|
compatible: "pixart,pat912x"
|
||||||
|
|
||||||
|
include: i2c-device.yaml
|
||||||
|
|
||||||
|
properties:
|
||||||
|
motion-gpios:
|
||||||
|
type: phandle-array
|
||||||
|
required: true
|
||||||
|
description:
|
||||||
|
GPIO connected to the motion pin, active low.
|
||||||
|
|
||||||
|
zephyr,axis-x:
|
||||||
|
type: int
|
||||||
|
description: |
|
||||||
|
The input code for the X axis to report for the device, typically any of
|
||||||
|
INPUT_REL_*. No report produced for the device X axis if unspecified.
|
||||||
|
|
||||||
|
zephyr,axis-y:
|
||||||
|
type: int
|
||||||
|
description: |
|
||||||
|
The input code for the Y axis to report for the device, typically any of
|
||||||
|
INPUT_REL_*. No report produced for the device Y axis if unspecified.
|
|
@ -197,6 +197,14 @@
|
||||||
touch-average-control = <1>;
|
touch-average-control = <1>;
|
||||||
tracking-index = <0>;
|
tracking-index = <0>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pat@5 {
|
||||||
|
compatible = "pixart,pat912x";
|
||||||
|
reg = <0x5>;
|
||||||
|
motion-gpios = <&test_gpio 0 0>;
|
||||||
|
zephyr,axis-x = <0>;
|
||||||
|
zephyr,axis-y = <1>;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
spi@2 {
|
spi@2 {
|
||||||
|
|
Loading…
Reference in a new issue