summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--builddefs/common_features.mk4
-rw-r--r--docs/feature_pointing_device.md65
-rw-r--r--drivers/sensors/azoteq_iqs5xx.c315
-rw-r--r--drivers/sensors/azoteq_iqs5xx.h193
-rw-r--r--quantum/pointing_device/pointing_device.h3
-rw-r--r--quantum/pointing_device/pointing_device_drivers.c113
6 files changed, 692 insertions, 1 deletions
diff --git a/builddefs/common_features.mk b/builddefs/common_features.mk
index fbaf087ad1..60f1cbd837 100644
--- a/builddefs/common_features.mk
+++ b/builddefs/common_features.mk
@@ -120,7 +120,7 @@ ifeq ($(strip $(MOUSEKEY_ENABLE)), yes)
MOUSE_ENABLE := yes
endif
-VALID_POINTING_DEVICE_DRIVER_TYPES := adns5050 adns9800 analog_joystick cirque_pinnacle_i2c cirque_pinnacle_spi paw3204 pmw3320 pmw3360 pmw3389 pimoroni_trackball custom
+VALID_POINTING_DEVICE_DRIVER_TYPES := adns5050 adns9800 analog_joystick azoteq_iqs5xx cirque_pinnacle_i2c cirque_pinnacle_spi paw3204 pmw3320 pmw3360 pmw3389 pimoroni_trackball custom
ifeq ($(strip $(POINTING_DEVICE_ENABLE)), yes)
ifeq ($(filter $(POINTING_DEVICE_DRIVER),$(VALID_POINTING_DEVICE_DRIVER_TYPES)),)
$(call CATASTROPHIC_ERROR,Invalid POINTING_DEVICE_DRIVER,POINTING_DEVICE_DRIVER="$(POINTING_DEVICE_DRIVER)" is not a valid pointing device type)
@@ -140,6 +140,8 @@ ifeq ($(strip $(POINTING_DEVICE_ENABLE)), yes)
SPI_DRIVER_REQUIRED = yes
else ifeq ($(strip $(POINTING_DEVICE_DRIVER)), analog_joystick)
ANALOG_DRIVER_REQUIRED = yes
+ else ifeq ($(strip $(POINTING_DEVICE_DRIVER)), azoteq_iqs5xx)
+ I2C_DRIVER_REQUIRED = yes
else ifeq ($(strip $(POINTING_DEVICE_DRIVER)), cirque_pinnacle_i2c)
I2C_DRIVER_REQUIRED = yes
SRC += drivers/sensors/cirque_pinnacle.c
diff --git a/docs/feature_pointing_device.md b/docs/feature_pointing_device.md
index 909eff826d..c4bbd29d6d 100644
--- a/docs/feature_pointing_device.md
+++ b/docs/feature_pointing_device.md
@@ -74,6 +74,71 @@ The Analog Joystick is an analog (ADC) driven sensor. There are a variety of jo
| `ANALOG_JOYSTICK_SPEED_MAX` | (Optional) The maximum value used for motion. | `2` |
| `ANALOG_JOYSTICK_CLICK_PIN` | (Optional) The pin wired up to the press switch of the analog stick. | _not defined_ |
+### Azoteq IQS5XX Trackpad
+
+To use a Azoteq IQS5XX trackpad, add this to your `rules.mk`:
+
+```make
+POINTING_DEVICE_DRIVER = azoteq_iqs5xx
+```
+
+This supports the IQS525, IQS550 and IQS572 controllers, with the latter two being used in the TPS43 and TPS65 trackpads.
+
+#### Device settings
+
+Specific device profiles are provided which set the required values for dimensions and resolution.
+
+| Setting | Description |
+| -------------------------------- | ---------------------------------------------------------- |
+| `AZOTEQ_IQS5XX_TPS43` | (Pick One) Sets resolution/mm to TPS43 specifications. |
+| `AZOTEQ_IQS5XX_TPS65` | (Pick One) Sets resolution/mm to TPS65 specifications. |
+
+?> If using one of the above defines you can skip to gesture settings.
+
+| Setting | Description | Default |
+| -------------------------------- | ---------------------------------------------------------- | ------------- |
+| `AZOTEQ_IQS5XX_WIDTH_MM` | (Required) Width of the trackpad sensor in millimeters. | _not defined_ |
+| `AZOTEQ_IQS5XX_HEIGHT_MM` | (Required) Height of the trackpad sensor in millimeters. | _not defined_ |
+| `AZOTEQ_IQS5XX_RESOLUTION_X` | (Optional) Specify X resolution for CPI calculation. | _not defined_ |
+| `AZOTEQ_IQS5XX_RESOLUTION_Y` | (Optional) Specify Y resolution for CPI calculation. | _not defined_ |
+
+**`AZOTEQ_IQS5XX_RESOLUTION_X/Y`** fall back resolutions are provided within the driver based on controller model.
+
+| I2C Setting | Description | Default |
+| ------------------------- | ------------------------------------------------------------------------------- | ------- |
+| `AZOTEQ_IQS5XX_ADDRESS` | (Optional) Sets the I2C Address for the Azoteq trackpad | `0xE8` |
+| `AZOTEQ_IQS5XX_TIMEOUT_MS`| (Optional) The timeout for i2c communication with in milliseconds. | `10` |
+
+#### Gesture settings
+
+| Setting | Description | Default |
+| ----------------------------------------- | ------------------------------------------------------------------------------------ | ----------- |
+| `AZOTEQ_IQS5XX_TAP_ENABLE` | (Optional) Enable single finger tap. (Left click) | `true` |
+| `AZOTEQ_IQS5XX_TWO_FINGER_TAP_ENABLE` | (Optional) Enable two finger tap. (Right click) | `true` |
+| `AZOTEQ_IQS5XX_PRESS_AND_HOLD_ENABLE` | (Optional) Emulates holding left click to select text. | `false` |
+| `AZOTEQ_IQS5XX_SWIPE_X_ENABLE` | (Optional) Enable swipe gestures X+ (Mouse Button 5) / X- (Mouse Button 4) | `false` |
+| `AZOTEQ_IQS5XX_SWIPE_Y_ENABLE` | (Optional) Enable swipe gestures Y+ (Mouse Button 3) / Y- (Mouse Button 6) | `false` |
+| `AZOTEQ_IQS5XX_ZOOM_ENABLE` | (Optional) Enable zoom gestures Zoom Out (Mouse Button 7) / Zoom In (Mouse Button 8) | `false` |
+| `AZOTEQ_IQS5XX_SCROLL_ENABLE` | (Optional) Enable scrolling using two fingers. | `true` |
+| `AZOTEQ_IQS5XX_TAP_TIME` | (Optional) Maximum time in ms for tap to be registered. | `150` |
+| `AZOTEQ_IQS5XX_TAP_DISTANCE` | (Optional) Maximum deviation in pixels before single tap is no longer valid. | `25` |
+| `AZOTEQ_IQS5XX_HOLD_TIME` | (Optional) Minimum time in ms for press and hold. | `300` |
+| `AZOTEQ_IQS5XX_SWIPE_INITIAL_TIME` | (Optional) Maximum time to travel initial distance before swipe is registered. | `150` |
+| `AZOTEQ_IQS5XX_SWIPE_INITIAL_DISTANCE` | (Optional) Minimum travel in pixels before swipe is registered. | `300` |
+| `AZOTEQ_IQS5XX_SWIPE_CONSECUTIVE_TIME` | (Optional) Maximum time to travel consecutive distance before swipe is registered. | `0` |
+| `AZOTEQ_IQS5XX_SWIPE_CONSECUTIVE_DISTANCE`| (Optional) Minimum travel in pixels before a consecutive swipe is registered. | `2000` |
+| `AZOTEQ_IQS5XX_SCROLL_INITIAL_DISTANCE` | (Optional) Minimum travel in pixels before scroll is registered. | `50` |
+| `AZOTEQ_IQS5XX_ZOOM_INITIAL_DISTANCE` | (Optional) Minimum travel in pixels before zoom is registered. | `50` |
+| `AZOTEQ_IQS5XX_ZOOM_CONSECUTIVE_DISTANCE` | (Optional) Maximum time to travel zoom distance before zoom is registered. | `25` |
+
+#### Rotation settings
+
+| Setting | Description | Default |
+| ---------------------------- | ---------------------------------------------------------- | ------------- |
+| `AZOTEQ_IQS5XX_ROTATION_90` | (Optional) Configures hardware for 90 degree rotation. | _not defined_ |
+| `AZOTEQ_IQS5XX_ROTATION_180` | (Optional) Configures hardware for 180 degree rotation. | _not defined_ |
+| `AZOTEQ_IQS5XX_ROTATION_270` | (Optional) Configures hardware for 270 degree rotation. | _not defined_ |
+
### Cirque Trackpad
To use the Cirque Trackpad sensor, add this to your `rules.mk`:
diff --git a/drivers/sensors/azoteq_iqs5xx.c b/drivers/sensors/azoteq_iqs5xx.c
new file mode 100644
index 0000000000..521f558b5f
--- /dev/null
+++ b/drivers/sensors/azoteq_iqs5xx.c
@@ -0,0 +1,315 @@
+// Copyright 2023 Dasky (@daskygit)
+// Copyright 2023 George Norton (@george-norton)
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "azoteq_iqs5xx.h"
+#include "pointing_device_internal.h"
+#include "wait.h"
+
+#ifndef AZOTEQ_IQS5XX_ADDRESS
+# define AZOTEQ_IQS5XX_ADDRESS (0x74 << 1)
+#endif
+#ifndef AZOTEQ_IQS5XX_TIMEOUT_MS
+# define AZOTEQ_IQS5XX_TIMEOUT_MS 10
+#endif
+
+#define AZOTEQ_IQS5XX_REG_PRODUCT_NUMBER 0x0000
+#define AZOTEQ_IQS5XX_REG_PREVIOUS_CYCLE_TIME 0x000C
+#define AZOTEQ_IQS5XX_REG_SYSTEM_CONTROL_1 0x0432
+#define AZOTEQ_IQS5XX_REG_REPORT_RATE_ACTIVE 0x057A
+#define AZOTEQ_IQS5XX_REG_SYSTEM_CONFIG_0 0x058E
+#define AZOTEQ_IQS5XX_REG_SYSTEM_CONFIG_1 0x058F
+#define AZOTEQ_IQS5XX_REG_X_RESOLUTION 0x066E
+#define AZOTEQ_IQS5XX_REG_XY_CONFIG_0 0x0669
+#define AZOTEQ_IQS5XX_REG_Y_RESOLUTION 0x0670
+#define AZOTEQ_IQS5XX_REG_SINGLE_FINGER_GESTURES 0x06B7
+#define AZOTEQ_IQS5XX_REG_END_COMMS 0xEEEE
+
+// Gesture configuration
+#ifndef AZOTEQ_IQS5XX_TAP_ENABLE
+# define AZOTEQ_IQS5XX_TAP_ENABLE true
+#endif
+#ifndef AZOTEQ_IQS5XX_PRESS_AND_HOLD_ENABLE
+# define AZOTEQ_IQS5XX_PRESS_AND_HOLD_ENABLE false
+#endif
+#ifndef AZOTEQ_IQS5XX_TWO_FINGER_TAP_ENABLE
+# define AZOTEQ_IQS5XX_TWO_FINGER_TAP_ENABLE true
+#endif
+#ifndef AZOTEQ_IQS5XX_SCROLL_ENABLE
+# define AZOTEQ_IQS5XX_SCROLL_ENABLE true
+#endif
+#ifndef AZOTEQ_IQS5XX_SWIPE_X_ENABLE
+# define AZOTEQ_IQS5XX_SWIPE_X_ENABLE false
+#endif
+#ifndef AZOTEQ_IQS5XX_SWIPE_Y_ENABLE
+# define AZOTEQ_IQS5XX_SWIPE_Y_ENABLE false
+#endif
+#ifndef AZOTEQ_IQS5XX_ZOOM_ENABLE
+# define AZOTEQ_IQS5XX_ZOOM_ENABLE false
+#endif
+#ifndef AZOTEQ_IQS5XX_TAP_TIME
+# define AZOTEQ_IQS5XX_TAP_TIME 0x96
+#endif
+#ifndef AZOTEQ_IQS5XX_TAP_DISTANCE
+# define AZOTEQ_IQS5XX_TAP_DISTANCE 0x19
+#endif
+#ifndef AZOTEQ_IQS5XX_HOLD_TIME
+# define AZOTEQ_IQS5XX_HOLD_TIME 0x12C
+#endif
+#ifndef AZOTEQ_IQS5XX_SWIPE_INITIAL_TIME
+# define AZOTEQ_IQS5XX_SWIPE_INITIAL_TIME 0x64 // 0x96
+#endif
+#ifndef AZOTEQ_IQS5XX_SWIPE_INITIAL_DISTANCE
+# define AZOTEQ_IQS5XX_SWIPE_INITIAL_DISTANCE 0x12C
+#endif
+#ifndef AZOTEQ_IQS5XX_SWIPE_CONSECUTIVE_TIME
+# define AZOTEQ_IQS5XX_SWIPE_CONSECUTIVE_TIME 0x0
+#endif
+#ifndef AZOTEQ_IQS5XX_SWIPE_CONSECUTIVE_DISTANCE
+# define AZOTEQ_IQS5XX_SWIPE_CONSECUTIVE_DISTANCE 0x7D0
+#endif
+#ifndef AZOTEQ_IQS5XX_SCROLL_INITIAL_DISTANCE
+# define AZOTEQ_IQS5XX_SCROLL_INITIAL_DISTANCE 0x32
+#endif
+#ifndef AZOTEQ_IQS5XX_ZOOM_INITIAL_DISTANCE
+# define AZOTEQ_IQS5XX_ZOOM_INITIAL_DISTANCE 0x32
+#endif
+#ifndef AZOTEQ_IQS5XX_ZOOM_CONSECUTIVE_DISTANCE
+# define AZOTEQ_IQS5XX_ZOOM_CONSECUTIVE_DISTANCE 0x19
+#endif
+
+#if defined(AZOTEQ_IQS5XX_TPS43)
+# define AZOTEQ_IQS5XX_WIDTH_MM 43
+# define AZOTEQ_IQS5XX_HEIGHT_MM 40
+# define AZOTEQ_IQS5XX_RESOLUTION_X 2048
+# define AZOTEQ_IQS5XX_RESOLUTION_Y 1792
+#elif defined(AZOTEQ_IQS5XX_TPS65)
+# define AZOTEQ_IQS5XX_WIDTH_MM 65
+# define AZOTEQ_IQS5XX_HEIGHT_MM 49
+# define AZOTEQ_IQS5XX_RESOLUTION_X 3072
+# define AZOTEQ_IQS5XX_RESOLUTION_Y 2048
+#elif !defined(AZOTEQ_IQS5XX_WIDTH_MM) && !defined(AZOTEQ_IQS5XX_HEIGHT_MM)
+# error "You must define one of the available azoteq trackpads or specify at least the width and height"
+#endif
+
+#define DIVIDE_UNSIGNED_ROUND(numerator, denominator) (((numerator) + ((denominator) / 2)) / (denominator))
+#define AZOTEQ_IQS5XX_INCH_TO_RESOLUTION_X(inch) (DIVIDE_UNSIGNED_ROUND((inch) * (uint32_t)AZOTEQ_IQS5XX_WIDTH_MM * 10, 254))
+#define AZOTEQ_IQS5XX_RESOLUTION_X_TO_INCH(px) (DIVIDE_UNSIGNED_ROUND((px) * (uint32_t)254, AZOTEQ_IQS5XX_WIDTH_MM * 10))
+#define AZOTEQ_IQS5XX_INCH_TO_RESOLUTION_Y(inch) (DIVIDE_UNSIGNED_ROUND((inch) * (uint32_t)AZOTEQ_IQS5XX_HEIGHT_MM * 10, 254))
+#define AZOTEQ_IQS5XX_RESOLUTION_Y_TO_INCH(px) (DIVIDE_UNSIGNED_ROUND((px) * (uint32_t)254, AZOTEQ_IQS5XX_HEIGHT_MM * 10))
+
+static uint16_t azoteq_iqs5xx_product_number = AZOTEQ_IQS5XX_UNKNOWN;
+
+static struct {
+ uint16_t resolution_x;
+ uint16_t resolution_y;
+} azoteq_iqs5xx_device_resolution_t;
+
+i2c_status_t azoteq_iqs5xx_wake(void) {
+ uint8_t data = 0;
+ i2c_status_t status = i2c_readReg16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_PREVIOUS_CYCLE_TIME, (uint8_t *)&data, sizeof(data), 1);
+ i2c_stop();
+ wait_us(150);
+ return status;
+}
+i2c_status_t azoteq_iqs5xx_end_session(void) {
+ const uint8_t END_BYTE = 1; // any data
+ return i2c_writeReg16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_END_COMMS, &END_BYTE, 1, AZOTEQ_IQS5XX_TIMEOUT_MS);
+}
+
+i2c_status_t azoteq_iqs5xx_get_base_data(azoteq_iqs5xx_base_data_t *base_data) {
+ i2c_status_t status = i2c_readReg16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_PREVIOUS_CYCLE_TIME, (uint8_t *)base_data, 10, AZOTEQ_IQS5XX_TIMEOUT_MS);
+ if (status == I2C_STATUS_SUCCESS) {
+ azoteq_iqs5xx_end_session();
+ }
+ return status;
+}
+
+i2c_status_t azoteq_iqs5xx_get_report_rate(azoteq_iqs5xx_report_rate_t *report_rate, azoteq_iqs5xx_charging_modes_t mode, bool end_session) {
+ if (mode > AZOTEQ_IQS5XX_LP2) {
+ pd_dprintf("IQS5XX - Invalid mode for get report rate.\n");
+ return I2C_STATUS_ERROR;
+ }
+ uint16_t selected_reg = AZOTEQ_IQS5XX_REG_REPORT_RATE_ACTIVE + (2 * mode);
+ i2c_status_t status = i2c_readReg16(AZOTEQ_IQS5XX_ADDRESS, selected_reg, (uint8_t *)report_rate, 2, AZOTEQ_IQS5XX_TIMEOUT_MS);
+ if (end_session) {
+ azoteq_iqs5xx_end_session();
+ }
+ return status;
+}
+
+i2c_status_t azoteq_iqs5xx_set_report_rate(uint16_t report_rate_ms, azoteq_iqs5xx_charging_modes_t mode, bool end_session) {
+ if (mode > AZOTEQ_IQS5XX_LP2) {
+ pd_dprintf("IQS5XX - Invalid mode for set report rate.\n");
+ return I2C_STATUS_ERROR;
+ }
+ uint16_t selected_reg = AZOTEQ_IQS5XX_REG_REPORT_RATE_ACTIVE + (2 * mode);
+ azoteq_iqs5xx_report_rate_t report_rate = {0};
+ report_rate.h = (uint8_t)((report_rate_ms >> 8) & 0xFF);
+ report_rate.l = (uint8_t)(report_rate_ms & 0xFF);
+ i2c_status_t status = i2c_writeReg16(AZOTEQ_IQS5XX_ADDRESS, selected_reg, (uint8_t *)&report_rate, 2, AZOTEQ_IQS5XX_TIMEOUT_MS);
+ if (end_session) {
+ azoteq_iqs5xx_end_session();
+ }
+ return status;
+}
+
+i2c_status_t azoteq_iqs5xx_set_reati(bool enabled, bool end_session) {
+ azoteq_iqs5xx_system_config_0_t config = {0};
+ i2c_status_t status = i2c_readReg16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_SYSTEM_CONFIG_0, (uint8_t *)&config, sizeof(azoteq_iqs5xx_system_config_0_t), AZOTEQ_IQS5XX_TIMEOUT_MS);
+ if (status == I2C_STATUS_SUCCESS) {
+ config.reati = enabled;
+ status = i2c_writeReg16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_SYSTEM_CONFIG_0, (uint8_t *)&config, sizeof(azoteq_iqs5xx_system_config_0_t), AZOTEQ_IQS5XX_TIMEOUT_MS);
+ }
+ if (end_session) {
+ azoteq_iqs5xx_end_session();
+ }
+ return status;
+}
+
+i2c_status_t azoteq_iqs5xx_set_event_mode(bool enabled, bool end_session) {
+ azoteq_iqs5xx_system_config_1_t config = {0};
+ i2c_status_t status = i2c_readReg16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_SYSTEM_CONFIG_1, (uint8_t *)&config, sizeof(azoteq_iqs5xx_system_config_1_t), AZOTEQ_IQS5XX_TIMEOUT_MS);
+ if (status == I2C_STATUS_SUCCESS) {
+ config.event_mode = enabled;
+ config.touch_event = true;
+ config.tp_event = true;
+ config.prox_event = false;
+ config.snap_event = false;
+ config.reati_event = false;
+ config.alp_prox_event = false;
+ config.gesture_event = true;
+ status = i2c_writeReg16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_SYSTEM_CONFIG_1, (uint8_t *)&config, sizeof(azoteq_iqs5xx_system_config_1_t), AZOTEQ_IQS5XX_TIMEOUT_MS);
+ }
+ if (end_session) {
+ azoteq_iqs5xx_end_session();
+ }
+ return status;
+}
+
+i2c_status_t azoteq_iqs5xx_set_gesture_config(bool end_session) {
+ azoteq_iqs5xx_gesture_config_t config = {0};
+ i2c_status_t status = i2c_readReg16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_SINGLE_FINGER_GESTURES, (uint8_t *)&config, sizeof(azoteq_iqs5xx_gesture_config_t), AZOTEQ_IQS5XX_TIMEOUT_MS);
+ pd_dprintf("azo scroll: %d\n", config.multi_finger_gestures.scroll);
+ if (status == I2C_STATUS_SUCCESS) {
+ config.single_finger_gestures.single_tap = AZOTEQ_IQS5XX_TAP_ENABLE;
+ config.single_finger_gestures.press_and_hold = AZOTEQ_IQS5XX_PRESS_AND_HOLD_ENABLE;
+ config.single_finger_gestures.swipe_x_plus = AZOTEQ_IQS5XX_SWIPE_X_ENABLE;
+ config.single_finger_gestures.swipe_x_minus = AZOTEQ_IQS5XX_SWIPE_X_ENABLE;
+ config.single_finger_gestures.swipe_y_plus = AZOTEQ_IQS5XX_SWIPE_Y_ENABLE;
+ config.single_finger_gestures.swipe_y_minus = AZOTEQ_IQS5XX_SWIPE_Y_ENABLE;
+ config.multi_finger_gestures.two_finger_tap = AZOTEQ_IQS5XX_TWO_FINGER_TAP_ENABLE;
+ config.multi_finger_gestures.scroll = AZOTEQ_IQS5XX_SCROLL_ENABLE;
+ config.multi_finger_gestures.zoom = AZOTEQ_IQS5XX_ZOOM_ENABLE;
+ config.tap_time = AZOTEQ_IQS5XX_SWAP_H_L_BYTES(AZOTEQ_IQS5XX_TAP_TIME);
+ config.tap_distance = AZOTEQ_IQS5XX_SWAP_H_L_BYTES(AZOTEQ_IQS5XX_TAP_DISTANCE);
+ config.hold_time = AZOTEQ_IQS5XX_SWAP_H_L_BYTES(AZOTEQ_IQS5XX_HOLD_TIME);
+ config.swipe_initial_time = AZOTEQ_IQS5XX_SWAP_H_L_BYTES(AZOTEQ_IQS5XX_SWIPE_INITIAL_TIME);
+ config.swipe_initial_distance = AZOTEQ_IQS5XX_SWAP_H_L_BYTES(AZOTEQ_IQS5XX_SWIPE_INITIAL_DISTANCE);
+ config.swipe_consecutive_time = AZOTEQ_IQS5XX_SWAP_H_L_BYTES(AZOTEQ_IQS5XX_SWIPE_CONSECUTIVE_TIME);
+ config.swipe_consecutive_distance = AZOTEQ_IQS5XX_SWAP_H_L_BYTES(AZOTEQ_IQS5XX_SWIPE_CONSECUTIVE_DISTANCE);
+ config.scroll_initial_distance = AZOTEQ_IQS5XX_SWAP_H_L_BYTES(AZOTEQ_IQS5XX_SCROLL_INITIAL_DISTANCE);
+ config.zoom_initial_distance = AZOTEQ_IQS5XX_SWAP_H_L_BYTES(AZOTEQ_IQS5XX_ZOOM_INITIAL_DISTANCE);
+ config.zoom_consecutive_distance = AZOTEQ_IQS5XX_SWAP_H_L_BYTES(AZOTEQ_IQS5XX_ZOOM_CONSECUTIVE_DISTANCE);
+ status = i2c_writeReg16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_SINGLE_FINGER_GESTURES, (uint8_t *)&config, sizeof(azoteq_iqs5xx_gesture_config_t), AZOTEQ_IQS5XX_TIMEOUT_MS);
+ }
+ if (end_session) {
+ azoteq_iqs5xx_end_session();
+ }
+ return status;
+}
+
+i2c_status_t azoteq_iqs5xx_set_xy_config(bool flip_x, bool flip_y, bool switch_xy, bool palm_reject, bool end_session) {
+ azoteq_iqs5xx_xy_config_0_t config = {0};
+ i2c_status_t status = i2c_readReg16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_XY_CONFIG_0, (uint8_t *)&config, sizeof(azoteq_iqs5xx_xy_config_0_t), AZOTEQ_IQS5XX_TIMEOUT_MS);
+ if (status == I2C_STATUS_SUCCESS) {
+ if (flip_x) {
+ config.flip_x = !config.flip_x;
+ }
+ if (flip_y) {
+ config.flip_y = !config.flip_y;
+ }
+ if (switch_xy) {
+ config.switch_xy_axis = !config.switch_xy_axis;
+ }
+ config.palm_reject = palm_reject;
+ status = i2c_writeReg16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_XY_CONFIG_0, (uint8_t *)&config, sizeof(azoteq_iqs5xx_xy_config_0_t), AZOTEQ_IQS5XX_TIMEOUT_MS);
+ }
+ if (end_session) {
+ azoteq_iqs5xx_end_session();
+ }
+ return status;
+}
+
+i2c_status_t azoteq_iqs5xx_reset_suspend(bool reset, bool suspend, bool end_session) {
+ azoteq_iqs5xx_system_control_1_t config = {0};
+ i2c_status_t status = i2c_readReg16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_SYSTEM_CONTROL_1, (uint8_t *)&config, sizeof(azoteq_iqs5xx_system_control_1_t), AZOTEQ_IQS5XX_TIMEOUT_MS);
+ if (status == I2C_STATUS_SUCCESS) {
+ config.reset = reset;
+ config.suspend = suspend;
+ status = i2c_writeReg16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_SYSTEM_CONTROL_1, (uint8_t *)&config, sizeof(azoteq_iqs5xx_system_control_1_t), AZOTEQ_IQS5XX_TIMEOUT_MS);
+ }
+ if (end_session) {
+ azoteq_iqs5xx_end_session();
+ }
+ return status;
+}
+
+void azoteq_iqs5xx_set_cpi(uint16_t cpi) {
+ if (azoteq_iqs5xx_product_number != AZOTEQ_IQS5XX_UNKNOWN) {
+ azoteq_iqs5xx_resolution_t resolution = {0};
+ resolution.x_resolution = AZOTEQ_IQS5XX_SWAP_H_L_BYTES(MIN(azoteq_iqs5xx_device_resolution_t.resolution_x, AZOTEQ_IQS5XX_INCH_TO_RESOLUTION_X(cpi)));
+ resolution.y_resolution = AZOTEQ_IQS5XX_SWAP_H_L_BYTES(MIN(azoteq_iqs5xx_device_resolution_t.resolution_y, AZOTEQ_IQS5XX_INCH_TO_RESOLUTION_Y(cpi)));
+ i2c_writeReg16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_X_RESOLUTION, (uint8_t *)&resolution, sizeof(azoteq_iqs5xx_resolution_t), AZOTEQ_IQS5XX_TIMEOUT_MS);
+ }
+}
+
+uint16_t azoteq_iqs5xx_get_cpi(void) {
+ if (azoteq_iqs5xx_product_number != AZOTEQ_IQS5XX_UNKNOWN) {
+ azoteq_iqs5xx_resolution_t resolution = {0};
+ i2c_status_t status = i2c_readReg16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_X_RESOLUTION, (uint8_t *)&resolution, sizeof(azoteq_iqs5xx_resolution_t), AZOTEQ_IQS5XX_TIMEOUT_MS);
+ if (status == I2C_STATUS_SUCCESS) {
+ return AZOTEQ_IQS5XX_RESOLUTION_X_TO_INCH(AZOTEQ_IQS5XX_SWAP_H_L_BYTES(resolution.x_resolution));
+ }
+ }
+ return 0;
+}
+
+uint16_t azoteq_iqs5xx_get_product(void) {
+ i2c_status_t status = i2c_readReg16(AZOTEQ_IQS5XX_ADDRESS, AZOTEQ_IQS5XX_REG_PRODUCT_NUMBER, (uint8_t *)&azoteq_iqs5xx_product_number, sizeof(uint16_t), AZOTEQ_IQS5XX_TIMEOUT_MS);
+ if (status == I2C_STATUS_SUCCESS) {
+ azoteq_iqs5xx_product_number = AZOTEQ_IQS5XX_SWAP_H_L_BYTES(azoteq_iqs5xx_product_number);
+ }
+ pd_dprintf("AZOTEQ: Product number %u\n", azoteq_iqs5xx_product_number);
+ return azoteq_iqs5xx_product_number;
+}
+
+void azoteq_iqs5xx_setup_resolution(void) {
+#if !defined(AZOTEQ_IQS5XX_RESOLUTION_X) && !defined(AZOTEQ_IQS5XX_RESOLUTION_Y)
+ switch (azoteq_iqs5xx_product_number) {
+ case AZOTEQ_IQS550:
+ azoteq_iqs5xx_device_resolution_t.resolution_x = 3584;
+ azoteq_iqs5xx_device_resolution_t.resolution_y = 2304;
+ break;
+ case AZOTEQ_IQS572:
+ azoteq_iqs5xx_device_resolution_t.resolution_x = 2048;
+ azoteq_iqs5xx_device_resolution_t.resolution_y = 1792;
+ break;
+ case AZOTEQ_IQS525:
+ azoteq_iqs5xx_device_resolution_t.resolution_x = 1280;
+ azoteq_iqs5xx_device_resolution_t.resolution_y = 768;
+ break;
+ default:
+ // shouldn't be here
+ azoteq_iqs5xx_device_resolution_t.resolution_x = 0;
+ azoteq_iqs5xx_device_resolution_t.resolution_y = 0;
+ break;
+ }
+#endif
+#ifdef AZOTEQ_IQS5XX_RESOLUTION_X
+ azoteq_iqs5xx_device_resolution_t.resolution_x = AZOTEQ_IQS5XX_RESOLUTION_X;
+#endif
+#ifdef AZOTEQ_IQS5XX_RESOLUTION_Y
+ azoteq_iqs5xx_device_resolution_t.resolution_y = AZOTEQ_IQS5XX_RESOLUTION_Y;
+#endif
+}
diff --git a/drivers/sensors/azoteq_iqs5xx.h b/drivers/sensors/azoteq_iqs5xx.h
new file mode 100644
index 0000000000..704ec2bab3
--- /dev/null
+++ b/drivers/sensors/azoteq_iqs5xx.h
@@ -0,0 +1,193 @@
+// Copyright 2023 Dasky (@daskygit)
+// Copyright 2023 George Norton (@george-norton)
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include "i2c_master.h"
+#include "pointing_device.h"
+#include "util.h"
+
+typedef enum {
+ AZOTEQ_IQS5XX_UNKNOWN,
+ AZOTEQ_IQS550 = 40,
+ AZOTEQ_IQS525 = 52,
+ AZOTEQ_IQS572 = 58,
+} azoteq_iqs5xx_product_numbers_t;
+typedef enum {
+ AZOTEQ_IQS5XX_ACTIVE,
+ AZOTEQ_IQS5XX_IDLE_TOUCH,
+ AZOTEQ_IQS5XX_IDLE,
+ AZOTEQ_IQS5XX_LP1,
+ AZOTEQ_IQS5XX_LP2,
+} azoteq_iqs5xx_charging_modes_t;
+
+typedef struct {
+ uint8_t h : 8;
+ uint8_t l : 8;
+} azoteq_iqs5xx_report_rate_t;
+
+typedef struct PACKED {
+ bool single_tap : 1; // Single tap gesture status
+ bool press_and_hold : 1; // Press and hold gesture status
+ bool swipe_x_neg : 1; // Swipe in negative X direction status
+ bool swipe_x_pos : 1; // Swipe in positive X direction status
+ bool swipe_y_pos : 1; // Swipe in positive Y direction status
+ bool swipe_y_neg : 1; // Swipe in negative Y direction status
+ uint8_t _unused : 2; // unused
+} azoteq_iqs5xx_gesture_events_0_t;
+
+typedef struct PACKED {
+ bool two_finger_tap : 1; // Two finger tap gesture status
+ bool scroll : 1; // Scroll status
+ bool zoom : 1; // Zoom gesture status
+ uint8_t _unused : 5; // unused
+} azoteq_iqs5xx_gesture_events_1_t;
+
+typedef struct PACKED {
+ azoteq_iqs5xx_charging_modes_t charging_mode : 3; // Indicates current mode
+ bool ati_error : 1; //
+ bool reati_occurred : 1; //
+ bool alp_ati_error : 1; //
+ bool alp_reati_occurred : 1; //
+ bool show_reset : 1; //
+} azoteq_iqs5xx_system_info_0_t;
+
+typedef struct PACKED {
+ bool tp_movement : 1; //
+ bool palm_detect : 1; // Palm detect status
+ bool too_many_fingers : 1; // Total finger status
+ bool rr_missed : 1; // Report rate status
+ bool snap_toggle : 1; // Change in any snap channel status
+ bool switch_state : 1; // Status of input pin SW_IN
+ uint8_t _unused : 2; // unused
+} azoteq_iqs5xx_system_info_1_t;
+
+typedef struct {
+ uint8_t h : 8;
+ uint8_t l : 8;
+} azoteq_iqs5xx_relative_xy_t;
+
+typedef struct {
+ uint8_t previous_cycle_time;
+ azoteq_iqs5xx_gesture_events_0_t gesture_events_0;
+ azoteq_iqs5xx_gesture_events_1_t gesture_events_1;
+ azoteq_iqs5xx_system_info_0_t system_info_0;
+ azoteq_iqs5xx_system_info_1_t system_info_1;
+ uint8_t number_of_fingers;
+ azoteq_iqs5xx_relative_xy_t x;
+ azoteq_iqs5xx_relative_xy_t y;
+} azoteq_iqs5xx_base_data_t;
+
+_Static_assert(sizeof(azoteq_iqs5xx_base_data_t) == 10, "azoteq_iqs5xx_basic_report_t should be 10 bytes");
+
+typedef struct {
+ uint8_t number_of_fingers;
+ azoteq_iqs5xx_relative_xy_t x;
+ azoteq_iqs5xx_relative_xy_t y;
+} azoteq_iqs5xx_report_data_t;
+
+_Static_assert(sizeof(azoteq_iqs5xx_report_data_t) == 5, "azoteq_iqs5xx_report_data_t should be 5 bytes");
+
+typedef struct PACKED {
+ bool sw_input : 1;
+ bool sw_input_select : 1;
+ bool reati : 1;
+ bool alp_reati : 1;
+ bool sw_input_event : 1;
+ bool wdt : 1;
+ bool setup_complete : 1;
+ bool manual_control : 1;
+} azoteq_iqs5xx_system_config_0_t;
+
+typedef struct PACKED {
+ bool event_mode : 1;
+ bool gesture_event : 1;
+ bool tp_event : 1;
+ bool reati_event : 1;
+ bool alp_prox_event : 1;
+ bool snap_event : 1;
+ bool touch_event : 1;
+ bool prox_event : 1;
+} azoteq_iqs5xx_system_config_1_t;
+
+typedef struct PACKED {
+ bool flip_x : 1;
+ bool flip_y : 1;
+ bool switch_xy_axis : 1;
+ bool palm_reject : 1;
+ uint8_t _unused : 4;
+} azoteq_iqs5xx_xy_config_0_t;
+
+typedef struct PACKED {
+ bool suspend : 1;
+ bool reset : 1;
+ int8_t _unused : 6;
+} azoteq_iqs5xx_system_control_1_t;
+
+typedef struct PACKED {
+ bool single_tap : 1;
+ bool press_and_hold : 1;
+ bool swipe_x_minus : 1;
+ bool swipe_x_plus : 1;
+ bool swipe_y_plus : 1;
+ bool swipe_y_minus : 1;
+ int8_t _unused : 2;
+} azoteq_iqs5xx_single_finger_gesture_enable_t;
+
+typedef struct PACKED {
+ bool two_finger_tap : 1;
+ bool scroll : 1;
+ bool zoom : 1;
+ int8_t _unused : 5;
+} azoteq_iqs5xx_multi_finger_gesture_enable_t;
+
+typedef struct PACKED {
+ azoteq_iqs5xx_single_finger_gesture_enable_t single_finger_gestures;
+ azoteq_iqs5xx_multi_finger_gesture_enable_t multi_finger_gestures;
+ uint16_t tap_time;
+ uint16_t tap_distance;
+ uint16_t hold_time;
+ uint16_t swipe_initial_time;
+ uint16_t swipe_initial_distance;
+ uint16_t swipe_consecutive_time;
+ uint16_t swipe_consecutive_distance;
+ int8_t swipe_angle;
+ uint16_t scroll_initial_distance;
+ int8_t scroll_angle;
+ uint16_t zoom_initial_distance;
+ uint16_t zoom_consecutive_distance;
+} azoteq_iqs5xx_gesture_config_t;
+
+_Static_assert(sizeof(azoteq_iqs5xx_gesture_config_t) == 24, "azoteq_iqs5xx_gesture_config_t should be 24 bytes");
+
+typedef struct {
+ uint16_t x_resolution;
+ uint16_t y_resolution;
+} azoteq_iqs5xx_resolution_t;
+
+#define AZOTEQ_IQS5XX_COMBINE_H_L_BYTES(h, l) ((int16_t)(h << 8) | l)
+#define AZOTEQ_IQS5XX_SWAP_H_L_BYTES(b) ((uint16_t)((b & 0xff) << 8) | (b >> 8))
+
+#ifndef AZOTEQ_IQS5XX_REPORT_RATE
+# define AZOTEQ_IQS5XX_REPORT_RATE 10
+#endif
+#if !defined(POINTING_DEVICE_TASK_THROTTLE_MS) && !defined(POINTING_DEVICE_MOTION_PIN)
+# define POINTING_DEVICE_TASK_THROTTLE_MS AZOTEQ_IQS5XX_REPORT_RATE
+#endif
+
+void azoteq_iqs5xx_init(void);
+i2c_status_t azoteq_iqs5xx_wake(void);
+report_mouse_t azoteq_iqs5xx_get_report(report_mouse_t mouse_report);
+i2c_status_t azoteq_iqs5xx_get_report_rate(azoteq_iqs5xx_report_rate_t *report_rate, azoteq_iqs5xx_charging_modes_t mode, bool end_session);
+i2c_status_t azoteq_iqs5xx_set_report_rate(uint16_t report_rate_ms, azoteq_iqs5xx_charging_modes_t mode, bool end_session);
+i2c_status_t azoteq_iqs5xx_set_event_mode(bool enabled, bool end_session);
+i2c_status_t azoteq_iqs5xx_set_reati(bool enabled, bool end_session);
+i2c_status_t azoteq_iqs5xx_set_gesture_config(bool end_session);
+i2c_status_t azoteq_iqs5xx_set_xy_config(bool flip_x, bool flip_y, bool switch_xy, bool palm_reject, bool end_session);
+i2c_status_t azoteq_iqs5xx_reset_suspend(bool reset, bool suspend, bool end_session);
+i2c_status_t azoteq_iqs5xx_get_base_data(azoteq_iqs5xx_base_data_t *base_data);
+void azoteq_iqs5xx_set_cpi(uint16_t cpi);
+uint16_t azoteq_iqs5xx_get_cpi(void);
+uint16_t azoteq_iqs5xx_get_product(void);
+void azoteq_iqs5xx_setup_resolution(void);
diff --git a/quantum/pointing_device/pointing_device.h b/quantum/pointing_device/pointing_device.h
index afd653eaee..1cd4b0b5e6 100644
--- a/quantum/pointing_device/pointing_device.h
+++ b/quantum/pointing_device/pointing_device.h
@@ -39,6 +39,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
# include "analog.h"
# include "drivers/sensors/analog_joystick.h"
# define POINTING_DEVICE_MOTION_PIN_ACTIVE_LOW
+#elif defined(POINTING_DEVICE_DRIVER_azoteq_iqs5xx)
+# include "i2c_master.h"
+# include "drivers/sensors/azoteq_iqs5xx.h"
#elif defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_i2c) || defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_spi)
# include "drivers/sensors/cirque_pinnacle.h"
# include "drivers/sensors/cirque_pinnacle_gestures.h"
diff --git a/quantum/pointing_device/pointing_device_drivers.c b/quantum/pointing_device/pointing_device_drivers.c
index 9a4315f76f..bf131c6eda 100644
--- a/quantum/pointing_device/pointing_device_drivers.c
+++ b/quantum/pointing_device/pointing_device_drivers.c
@@ -115,6 +115,119 @@ const pointing_device_driver_t pointing_device_driver = {
};
// clang-format on
+#elif defined(POINTING_DEVICE_DRIVER_azoteq_iqs5xx)
+
+static i2c_status_t azoteq_iqs5xx_init_status = 1;
+
+void azoteq_iqs5xx_init(void) {
+ i2c_init();
+ azoteq_iqs5xx_wake();
+ azoteq_iqs5xx_reset_suspend(true, false, true);
+ wait_ms(100);
+ azoteq_iqs5xx_wake();
+ if (azoteq_iqs5xx_get_product() != AZOTEQ_IQS5XX_UNKNOWN) {
+ azoteq_iqs5xx_setup_resolution();
+ azoteq_iqs5xx_init_status = azoteq_iqs5xx_set_report_rate(AZOTEQ_IQS5XX_REPORT_RATE, AZOTEQ_IQS5XX_ACTIVE, false);
+ azoteq_iqs5xx_init_status |= azoteq_iqs5xx_set_event_mode(false, false);
+ azoteq_iqs5xx_init_status |= azoteq_iqs5xx_set_reati(true, false);
+# if defined(AZOTEQ_IQS5XX_ROTATION_90)
+ azoteq_iqs5xx_init_status |= azoteq_iqs5xx_set_xy_config(false, true, true, true, false);
+# elif defined(AZOTEQ_IQS5XX_ROTATION_180)
+ azoteq_iqs5xx_init_status |= azoteq_iqs5xx_set_xy_config(true, true, false, true, false);
+# elif defined(AZOTEQ_IQS5XX_ROTATION_270)
+ azoteq_iqs5xx_init_status |= azoteq_iqs5xx_set_xy_config(true, false, true, true, false);
+# else
+ azoteq_iqs5xx_init_status |= azoteq_iqs5xx_set_xy_config(false, false, false, true, false);
+# endif
+ azoteq_iqs5xx_init_status |= azoteq_iqs5xx_set_gesture_config(true);
+ wait_ms(AZOTEQ_IQS5XX_REPORT_RATE + 1);
+ }
+};
+
+report_mouse_t azoteq_iqs5xx_get_report(report_mouse_t mouse_report) {
+ report_mouse_t temp_report = {0};
+ static uint8_t previous_button_state = 0;
+ static uint8_t read_error_count = 0;
+
+ if (azoteq_iqs5xx_init_status == I2C_STATUS_SUCCESS) {
+ azoteq_iqs5xx_base_data_t base_data = {0};
+# if !defined(POINTING_DEVICE_MOTION_PIN)
+ azoteq_iqs5xx_wake();
+# endif
+ i2c_status_t status = azoteq_iqs5xx_get_base_data(&base_data);
+ bool ignore_movement = false;
+
+ if (status == I2C_STATUS_SUCCESS) {
+ // pd_dprintf("IQS5XX - previous cycle time: %d \n", base_data.previous_cycle_time);
+ read_error_count = 0;
+ if (base_data.gesture_events_0.single_tap || base_data.gesture_events_0.press_and_hold) {
+ pd_dprintf("IQS5XX - Single tap/hold.\n");
+ temp_report.buttons = pointing_device_handle_buttons(temp_report.buttons, true, POINTING_DEVICE_BUTTON1);
+ } else if (base_data.gesture_events_1.two_finger_tap) {
+ pd_dprintf("IQS5XX - Two finger tap.\n");
+ temp_report.buttons = pointing_device_handle_buttons(temp_report.buttons, true, POINTING_DEVICE_BUTTON2);
+ } else if (base_data.gesture_events_0.swipe_x_neg) {
+ pd_dprintf("IQS5XX - X-.\n");
+ temp_report.buttons = pointing_device_handle_buttons(temp_report.buttons, true, POINTING_DEVICE_BUTTON4);
+ ignore_movement = true;
+ } else if (base_data.gesture_events_0.swipe_x_pos) {
+ pd_dprintf("IQS5XX - X+.\n");
+ temp_report.buttons = pointing_device_handle_buttons(temp_report.buttons, true, POINTING_DEVICE_BUTTON5);
+ ignore_movement = true;
+ } else if (base_data.gesture_events_0.swipe_y_neg) {
+ pd_dprintf("IQS5XX - Y-.\n");
+ temp_report.buttons = pointing_device_handle_buttons(temp_report.buttons, true, POINTING_DEVICE_BUTTON6);
+ ignore_movement = true;
+ } else if (base_data.gesture_events_0.swipe_y_pos) {
+ pd_dprintf("IQS5XX - Y+.\n");
+ temp_report.buttons = pointing_device_handle_buttons(temp_report.buttons, true, POINTING_DEVICE_BUTTON3);
+ ignore_movement = true;
+ } else if (base_data.gesture_events_1.zoom) {
+ if (AZOTEQ_IQS5XX_COMBINE_H_L_BYTES(base_data.x.h, base_data.x.l) < 0) {
+ pd_dprintf("IQS5XX - Zoom out.\n");
+ temp_report.buttons = pointing_device_handle_buttons(temp_report.buttons, true, POINTING_DEVICE_BUTTON7);
+ } else if (AZOTEQ_IQS5XX_COMBINE_H_L_BYTES(base_data.x.h, base_data.x.l) > 0) {
+ pd_dprintf("IQS5XX - Zoom in.\n");
+ temp_report.buttons = pointing_device_handle_buttons(temp_report.buttons, true, POINTING_DEVICE_BUTTON8);
+ }
+ } else if (base_data.gesture_events_1.scroll) {
+ pd_dprintf("IQS5XX - Scroll.\n");
+ temp_report.h = CONSTRAIN_HID(AZOTEQ_IQS5XX_COMBINE_H_L_BYTES(base_data.x.h, base_data.x.l));
+ temp_report.v = CONSTRAIN_HID(AZOTEQ_IQS5XX_COMBINE_H_L_BYTES(base_data.y.h, base_data.y.l));
+ }
+ if (base_data.number_of_fingers == 1 && !ignore_movement) {
+ temp_report.x = CONSTRAIN_HID_XY(AZOTEQ_IQS5XX_COMBINE_H_L_BYTES(base_data.x.h, base_data.x.l));
+ temp_report.y = CONSTRAIN_HID_XY(AZOTEQ_IQS5XX_COMBINE_H_L_BYTES(base_data.y.h, base_data.y.l));
+ }
+
+ previous_button_state = temp_report.buttons;
+
+ } else {
+ if (read_error_count > 10) {
+ read_error_count = 0;
+ previous_button_state = 0;
+ } else {
+ read_error_count++;
+ }
+ temp_report.buttons = previous_button_state;
+ pd_dprintf("IQS5XX - get report failed: %d \n", status);
+ }
+ } else {
+ pd_dprintf("IQS5XX - Init failed: %d \n", azoteq_iqs5xx_init_status);
+ }
+
+ return temp_report;
+}
+
+// clang-format off
+const pointing_device_driver_t pointing_device_driver = {
+ .init = azoteq_iqs5xx_init,
+ .get_report = azoteq_iqs5xx_get_report,
+ .set_cpi = azoteq_iqs5xx_set_cpi,
+ .get_cpi = azoteq_iqs5xx_get_cpi
+};
+// clang-format on
+
#elif defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_i2c) || defined(POINTING_DEVICE_DRIVER_cirque_pinnacle_spi)
# ifdef POINTING_DEVICE_GESTURES_CURSOR_GLIDE_ENABLE
static bool cursor_glide_enable = true;