Skip to content

Commit

Permalink
input: pmw3610: add few config options
Browse files Browse the repository at this point in the history
Add config options for resolution, force awake and smart mode.

Signed-off-by: Fabio Baltieri <[email protected]>
  • Loading branch information
fabiobaltieri authored and aescolar committed Mar 20, 2024
1 parent edbfe9a commit 2c5b992
Show file tree
Hide file tree
Showing 4 changed files with 200 additions and 2 deletions.
154 changes: 152 additions & 2 deletions drivers/input/input_pmw3610.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/spi.h>
#include <zephyr/input/input.h>
#include <zephyr/input/input_pmw3610.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_pmw3610, CONFIG_INPUT_LOG_LEVEL);
Expand All @@ -34,6 +36,7 @@ LOG_MODULE_REGISTER(input_pmw3610, CONFIG_INPUT_LOG_LEVEL);
#define PMW3610_REST1_RATE 0x1c
#define PMW3610_REST1_DOWNSHIFT 0x1d
#define PMW3610_OBSERVATION1 0x2d
#define PMW3610_SMART_MODE 0x32
#define PMW3610_POWER_UP_RESET 0x3a
#define PMW3610_SHUTDOWN 0x3b
#define PMW3610_SPI_CLK_ON_REQ 0x41
Expand All @@ -52,6 +55,10 @@ LOG_MODULE_REGISTER(input_pmw3610, CONFIG_INPUT_LOG_LEVEL);
#define BURST_SHUTTER_HI 5
#define BURST_SHUTTER_LO 6

#define BURST_DATA_LEN_NORMAL (BURST_DELTA_XY_H + 1)
#define BURST_DATA_LEN_SMART (BURST_SHUTTER_LO + 1)
#define BURST_DATA_LEN_MAX MAX(BURST_DATA_LEN_NORMAL, BURST_DATA_LEN_SMART)

/* Init sequence values */
#define OBSERVATION1_INIT_MASK 0x0f
#define PERFORMANCE_INIT 0x0d
Expand All @@ -66,31 +73,46 @@ LOG_MODULE_REGISTER(input_pmw3610, CONFIG_INPUT_LOG_LEVEL);
#define SPI_CLOCK_ON_REQ_OFF 0xb5
#define RES_STEP_INV_X_BIT 6
#define RES_STEP_INV_Y_BIT 5
#define RES_STEP_RES_MASK 0x1f
#define PERFORMANCE_FMODE_MASK (0x0f << 4)
#define PERFORMANCE_FMODE_NORMAL (0x00 << 4)
#define PERFORMANCE_FMODE_FORCE_AWAKE (0x0f << 4)
#define POWER_UP_WAKEUP 0x96
#define SHUTDOWN_ENABLE 0xe7
#define SPI_PAGE0_1 0xff
#define SPI_PAGE1_0 0x00
#define SHUTTER_SMART_THRESHOLD 45
#define SMART_MODE_ENABLE 0x00
#define SMART_MODE_DISABLE 0x80

#define PMW3610_DATA_SIZE_BITS 12

#define RESET_DELAY_MS 10
#define INIT_OBSERVATION_DELAY_MS 10
#define CLOCK_ON_DELAY_US 300

#define RES_STEP 200
#define RES_MIN 200
#define RES_MAX 3200

struct pmw3610_config {
struct spi_dt_spec spi;
struct gpio_dt_spec motion_gpio;
struct gpio_dt_spec reset_gpio;
uint16_t axis_x;
uint16_t axis_y;
int16_t res_cpi;
bool invert_x;
bool invert_y;
bool force_awake;
bool smart_mode;
};

struct pmw3610_data {
const struct device *dev;
struct k_work motion_work;
struct gpio_callback motion_cb;
bool smart_flag;
};

static int pmw3610_read(const struct device *dev,
Expand Down Expand Up @@ -169,11 +191,18 @@ static void pmw3610_motion_work_handler(struct k_work *work)
work, struct pmw3610_data, motion_work);
const struct device *dev = data->dev;
const struct pmw3610_config *cfg = dev->config;
uint8_t burst_data[4];
uint8_t burst_data[BURST_DATA_LEN_MAX];
uint8_t burst_data_len;
int32_t x, y;
int ret;

ret = pmw3610_read(dev, PMW3610_BURST_READ, burst_data, sizeof(burst_data));
if (cfg->smart_mode) {
burst_data_len = BURST_DATA_LEN_SMART;
} else {
burst_data_len = BURST_DATA_LEN_NORMAL;
}

ret = pmw3610_read(dev, PMW3610_BURST_READ, burst_data, burst_data_len);
if (ret < 0) {
return;
}
Expand All @@ -190,6 +219,34 @@ static void pmw3610_motion_work_handler(struct k_work *work)

input_report_rel(data->dev, cfg->axis_x, x, false, K_FOREVER);
input_report_rel(data->dev, cfg->axis_y, y, true, K_FOREVER);

if (cfg->smart_mode) {
uint16_t shutter_val = sys_get_be16(&burst_data[BURST_SHUTTER_HI]);

if (data->smart_flag && shutter_val < SHUTTER_SMART_THRESHOLD) {
pmw3610_spi_clk_on(dev);

ret = pmw3610_write_reg(dev, PMW3610_SMART_MODE, SMART_MODE_ENABLE);
if (ret < 0) {
return;
}

pmw3610_spi_clk_off(dev);

data->smart_flag = false;
} else if (!data->smart_flag && shutter_val > SHUTTER_SMART_THRESHOLD) {
pmw3610_spi_clk_on(dev);

ret = pmw3610_write_reg(dev, PMW3610_SMART_MODE, SMART_MODE_DISABLE);
if (ret < 0) {
return;
}

pmw3610_spi_clk_off(dev);

data->smart_flag = true;
}
}
}

static void pmw3610_motion_handler(const struct device *gpio_dev,
Expand All @@ -202,6 +259,87 @@ static void pmw3610_motion_handler(const struct device *gpio_dev,
k_work_submit(&data->motion_work);
}

int pmw3610_set_resolution(const struct device *dev, uint16_t res_cpi)
{
uint8_t val;
int ret;

if (!IN_RANGE(res_cpi, RES_MIN, RES_MAX)) {
LOG_ERR("res_cpi out of range: %d", res_cpi);
return -EINVAL;
}

ret = pmw3610_spi_clk_on(dev);
if (ret < 0) {
return ret;
}

ret = pmw3610_write_reg(dev, PWM3610_SPI_PAGE0, SPI_PAGE0_1);
if (ret < 0) {
return ret;
}

ret = pmw3610_read_reg(dev, PMW3610_RES_STEP, &val);
if (ret < 0) {
return ret;
}

val &= ~RES_STEP_RES_MASK;
val |= res_cpi / RES_STEP;

ret = pmw3610_write_reg(dev, PMW3610_RES_STEP, val);
if (ret < 0) {
return ret;
}

ret = pmw3610_write_reg(dev, PWM3610_SPI_PAGE1, SPI_PAGE1_0);
if (ret < 0) {
return ret;
}

ret = pmw3610_spi_clk_off(dev);
if (ret < 0) {
return ret;
}

return 0;
}

int pmw3610_force_awake(const struct device *dev, bool enable)
{
uint8_t val;
int ret;

ret = pmw3610_read_reg(dev, PMW3610_PERFORMANCE, &val);
if (ret < 0) {
return ret;
}

val &= ~PERFORMANCE_FMODE_MASK;
if (enable) {
val |= PERFORMANCE_FMODE_FORCE_AWAKE;
} else {
val |= PERFORMANCE_FMODE_NORMAL;
}

ret = pmw3610_spi_clk_on(dev);
if (ret < 0) {
return ret;
}

ret = pmw3610_write_reg(dev, PMW3610_PERFORMANCE, val);
if (ret < 0) {
return ret;
}

ret = pmw3610_spi_clk_off(dev);
if (ret < 0) {
return ret;
}

return 0;
}

static int pmw3610_configure(const struct device *dev)
{
const struct pmw3610_config *cfg = dev->config;
Expand Down Expand Up @@ -320,6 +458,12 @@ static int pmw3610_configure(const struct device *dev)
}
}

if (cfg->res_cpi > 0) {
pmw3610_set_resolution(dev, cfg->res_cpi);
}

pmw3610_force_awake(dev, cfg->force_awake);

return 0;
}

Expand Down Expand Up @@ -411,14 +555,20 @@ static int pmw3610_pm_action(const struct device *dev,
SPI_MODE_CPOL | SPI_MODE_CPHA | SPI_TRANSFER_MSB)

#define PMW3610_INIT(n) \
BUILD_ASSERT(IN_RANGE(DT_INST_PROP_OR(n, res_cpi, RES_MIN), \
RES_MIN, RES_MAX), "invalid res-cpi"); \
\
static const struct pmw3610_config pmw3610_cfg_##n = { \
.spi = SPI_DT_SPEC_INST_GET(n, PMW3610_SPI_MODE, 0), \
.motion_gpio = GPIO_DT_SPEC_INST_GET(n, motion_gpios), \
.reset_gpio = GPIO_DT_SPEC_INST_GET_OR(n, reset_gpios, {}), \
.axis_x = DT_INST_PROP(n, zephyr_axis_x), \
.axis_y = DT_INST_PROP(n, zephyr_axis_y), \
.res_cpi = DT_INST_PROP_OR(n, res_cpi, -1), \
.invert_x = DT_INST_PROP(n, invert_x), \
.invert_y = DT_INST_PROP(n, invert_y), \
.force_awake = DT_INST_PROP(n, force_awake), \
.smart_mode = DT_INST_PROP(n, smart_mode), \
}; \
\
static struct pmw3610_data pmw3610_data_##n; \
Expand Down
20 changes: 20 additions & 0 deletions dts/bindings/input/pixart,pmw3610.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ properties:
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.
res-cpi:
type: int
description: |
CPI resolution for the sensor, range from 200 to 3200, rounded down to
the closest supported value in increments of 200. This can also be
changed in runtime using the pmw3610_set_resolution() API.
invert-x:
type: boolean
description: |
Expand All @@ -42,3 +49,16 @@ properties:
type: boolean
description: |
Invert Y axis values.
force-awake:
type: boolean
description: |
Initialize the sensor in "force awake" mode. This can also be enabled or
disabled in runtime by the application using the pmw3610_force_awake()
API.
smart-mode:
type: boolean
description: |
Enable the "smart mode" algorithm as described in the device datasheet.
This should improve sensor tracking across a wider range of surfaces.
26 changes: 26 additions & 0 deletions include/zephyr/input/input_pmw3610.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright 2024 Google LLC
*
* SPDX-License-Identifier: Apache-2.0
*/

#ifndef ZEPHYR_INCLUDE_INPUT_PMW3610_H_
#define ZEPHYR_INCLUDE_INPUT_PMW3610_H_

/**
* @brief Set resolution on a pmw3610 device
*
* @param dev pmw3610 device.
* @param res_cpi CPI resolution, 200 to 3200.
*/
int pmw3610_set_resolution(const struct device *dev, uint16_t res_cpi);

/**
* @brief Set force awake mode on a pmw3610 device
*
* @param dev pmw3610 device.
* @param enable whether to enable or disable force awake mode.
*/
int pmw3610_force_awake(const struct device *dev, bool enable);

#endif /* ZEPHYR_INCLUDE_INPUT_PMW3610_H_ */
2 changes: 2 additions & 0 deletions tests/drivers/build_all/input/app.overlay
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,8 @@
zephyr,axis-y = <1>;
invert-x;
invert-y;
force-awake;
smart-mode;
};
};
};
Expand Down

0 comments on commit 2c5b992

Please sign in to comment.