├── .clang-format ├── CMakeLists.txt ├── Kconfig ├── drivers ├── CMakeLists.txt ├── Kconfig └── input │ ├── CMakeLists.txt │ ├── Kconfig │ ├── input_pinnacle.c │ ├── input_pinnacle.h │ └── zmk_pinnacle_idle_sleeper.c ├── dts └── bindings │ ├── input │ ├── cirque,pinnacle-common.yaml │ ├── cirque,pinnacle-i2c.yaml │ └── cirque,pinnacle-spi.yaml │ └── vendor-prefixes.txt └── zephyr └── module.yml /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | IndentWidth: 4 3 | ColumnLimit: 100 4 | SortIncludes: false 5 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_subdirectory(drivers) 3 | -------------------------------------------------------------------------------- /Kconfig: -------------------------------------------------------------------------------- 1 | rsource "drivers/Kconfig" 2 | -------------------------------------------------------------------------------- /drivers/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_subdirectory_ifdef(CONFIG_INPUT input) 3 | -------------------------------------------------------------------------------- /drivers/Kconfig: -------------------------------------------------------------------------------- 1 | rsource "input/Kconfig" 2 | -------------------------------------------------------------------------------- /drivers/input/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2024 The ZMK Contributors 2 | # SPDX-License-Identifier: MIT 3 | 4 | zephyr_library_amend() 5 | 6 | zephyr_library_sources_ifdef(CONFIG_INPUT_PINNACLE input_pinnacle.c) 7 | 8 | target_sources_ifdef(CONFIG_ZMK_INPUT_PINNACLE_IDLE_SLEEPER app PRIVATE zmk_pinnacle_idle_sleeper.c) 9 | -------------------------------------------------------------------------------- /drivers/input/Kconfig: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022 The ZMK Contributors 2 | # SPDX-License-Identifier: MIT 3 | 4 | menuconfig INPUT_PINNACLE 5 | bool "Cirque Pinnacle trackpads" 6 | default y 7 | depends on GPIO 8 | depends on SPI || I2C 9 | depends on INPUT 10 | depends on DT_HAS_CIRQUE_PINNACLE_ENABLED 11 | help 12 | Enable driver for Cirque Pinnacle trackpads 13 | 14 | if INPUT_PINNACLE 15 | 16 | config INPUT_PINNACLE_INIT_PRIORITY 17 | int "Cirque Pinnacle initialization priority" 18 | default INPUT_INIT_PRIORITY 19 | 20 | if ZMK_MOUSE 21 | 22 | config ZMK_INPUT_PINNACLE_IDLE_SLEEPER 23 | bool "Pinnacle Sleep linked to ZMK idle state" 24 | default n 25 | 26 | endif 27 | 28 | endif 29 | -------------------------------------------------------------------------------- /drivers/input/input_pinnacle.c: -------------------------------------------------------------------------------- 1 | #define DT_DRV_COMPAT cirque_pinnacle 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include "input_pinnacle.h" 11 | 12 | LOG_MODULE_REGISTER(pinnacle, CONFIG_INPUT_LOG_LEVEL); 13 | 14 | static int pinnacle_seq_read(const struct device *dev, const uint8_t addr, uint8_t *buf, 15 | const uint8_t len) { 16 | const struct pinnacle_config *config = dev->config; 17 | return config->seq_read(dev, addr, buf, len); 18 | } 19 | static int pinnacle_write(const struct device *dev, const uint8_t addr, const uint8_t val) { 20 | const struct pinnacle_config *config = dev->config; 21 | return config->write(dev, addr, val); 22 | } 23 | 24 | #if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) 25 | 26 | static int pinnacle_i2c_seq_read(const struct device *dev, const uint8_t addr, uint8_t *buf, 27 | const uint8_t len) { 28 | const struct pinnacle_config *config = dev->config; 29 | return i2c_burst_read_dt(&config->bus.i2c, PINNACLE_READ | addr, buf, len); 30 | } 31 | 32 | static int pinnacle_i2c_write(const struct device *dev, const uint8_t addr, const uint8_t val) { 33 | const struct pinnacle_config *config = dev->config; 34 | return i2c_reg_write_byte_dt(&config->bus.i2c, PINNACLE_WRITE | addr, val); 35 | } 36 | 37 | #endif // DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) 38 | 39 | #if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) 40 | 41 | static int pinnacle_spi_seq_read(const struct device *dev, const uint8_t addr, uint8_t *buf, 42 | const uint8_t len) { 43 | const struct pinnacle_config *config = dev->config; 44 | uint8_t tx_buffer[len + 3], rx_dummy[3]; 45 | tx_buffer[0] = PINNACLE_READ | addr; 46 | memset(&tx_buffer[1], PINNACLE_AUTOINC, len + 2); 47 | 48 | const struct spi_buf tx_buf[2] = { 49 | { 50 | .buf = tx_buffer, 51 | .len = len + 3, 52 | }, 53 | }; 54 | const struct spi_buf_set tx = { 55 | .buffers = tx_buf, 56 | .count = 1, 57 | }; 58 | struct spi_buf rx_buf[2] = { 59 | { 60 | .buf = rx_dummy, 61 | .len = 3, 62 | }, 63 | { 64 | .buf = buf, 65 | .len = len, 66 | }, 67 | }; 68 | const struct spi_buf_set rx = { 69 | .buffers = rx_buf, 70 | .count = 2, 71 | }; 72 | int ret = spi_transceive_dt(&config->bus.spi, &tx, &rx); 73 | 74 | return ret; 75 | } 76 | 77 | static int pinnacle_spi_write(const struct device *dev, const uint8_t addr, const uint8_t val) { 78 | const struct pinnacle_config *config = dev->config; 79 | uint8_t tx_buffer[2] = {PINNACLE_WRITE | addr, val}; 80 | uint8_t rx_buffer[2]; 81 | 82 | const struct spi_buf tx_buf = { 83 | .buf = tx_buffer, 84 | .len = 2, 85 | }; 86 | const struct spi_buf_set tx = { 87 | .buffers = &tx_buf, 88 | .count = 1, 89 | }; 90 | 91 | const struct spi_buf rx_buf = { 92 | .buf = rx_buffer, 93 | .len = 2, 94 | }; 95 | const struct spi_buf_set rx = { 96 | .buffers = &rx_buf, 97 | .count = 1, 98 | }; 99 | 100 | const int ret = spi_transceive_dt(&config->bus.spi, &tx, &rx); 101 | 102 | if (ret < 0) { 103 | LOG_ERR("spi ret: %d", ret); 104 | } 105 | 106 | if (rx_buffer[1] != PINNACLE_FILLER) { 107 | LOG_ERR("bad ret val %d - %d", rx_buffer[0], rx_buffer[1]); 108 | return -EIO; 109 | } 110 | 111 | k_usleep(50); 112 | 113 | return ret; 114 | } 115 | #endif // DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) 116 | 117 | static int set_int(const struct device *dev, const bool en) { 118 | const struct pinnacle_config *config = dev->config; 119 | int ret = gpio_pin_interrupt_configure_dt(&config->dr, 120 | en ? GPIO_INT_EDGE_TO_ACTIVE : GPIO_INT_DISABLE); 121 | if (ret < 0) { 122 | LOG_ERR("can't set interrupt"); 123 | } 124 | 125 | return ret; 126 | } 127 | 128 | static int pinnacle_clear_status(const struct device *dev) { 129 | int ret = pinnacle_write(dev, PINNACLE_STATUS1, 0); 130 | if (ret < 0) { 131 | LOG_ERR("Failed to clear STATUS1 register: %d", ret); 132 | } 133 | 134 | return ret; 135 | } 136 | 137 | static int pinnacle_era_read(const struct device *dev, const uint16_t addr, uint8_t *val) { 138 | int ret; 139 | 140 | set_int(dev, false); 141 | 142 | ret = pinnacle_write(dev, PINNACLE_REG_ERA_HIGH_BYTE, (uint8_t)(addr >> 8)); 143 | if (ret < 0) { 144 | LOG_ERR("Failed to write ERA high byte (%d)", ret); 145 | return -EIO; 146 | } 147 | 148 | ret = pinnacle_write(dev, PINNACLE_REG_ERA_LOW_BYTE, (uint8_t)(addr & 0x00FF)); 149 | if (ret < 0) { 150 | LOG_ERR("Failed to write ERA low byte (%d)", ret); 151 | return -EIO; 152 | } 153 | 154 | ret = pinnacle_write(dev, PINNACLE_REG_ERA_CONTROL, PINNACLE_ERA_CONTROL_READ); 155 | if (ret < 0) { 156 | LOG_ERR("Failed to write ERA control (%d)", ret); 157 | return -EIO; 158 | } 159 | 160 | uint8_t control_val; 161 | do { 162 | 163 | ret = pinnacle_seq_read(dev, PINNACLE_REG_ERA_CONTROL, &control_val, 1); 164 | if (ret < 0) { 165 | LOG_ERR("Failed to read ERA control (%d)", ret); 166 | return -EIO; 167 | } 168 | 169 | } while (control_val != 0x00); 170 | 171 | ret = pinnacle_seq_read(dev, PINNACLE_REG_ERA_VALUE, val, 1); 172 | 173 | if (ret < 0) { 174 | LOG_ERR("Failed to read ERA value (%d)", ret); 175 | return -EIO; 176 | } 177 | 178 | ret = pinnacle_clear_status(dev); 179 | 180 | set_int(dev, true); 181 | 182 | return ret; 183 | } 184 | 185 | static int pinnacle_era_write(const struct device *dev, const uint16_t addr, uint8_t val) { 186 | int ret; 187 | 188 | set_int(dev, false); 189 | 190 | ret = pinnacle_write(dev, PINNACLE_REG_ERA_VALUE, val); 191 | if (ret < 0) { 192 | LOG_ERR("Failed to write ERA value (%d)", ret); 193 | return -EIO; 194 | } 195 | 196 | ret = pinnacle_write(dev, PINNACLE_REG_ERA_HIGH_BYTE, (uint8_t)(addr >> 8)); 197 | if (ret < 0) { 198 | LOG_ERR("Failed to write ERA high byte (%d)", ret); 199 | return -EIO; 200 | } 201 | 202 | ret = pinnacle_write(dev, PINNACLE_REG_ERA_LOW_BYTE, (uint8_t)(addr & 0x00FF)); 203 | if (ret < 0) { 204 | LOG_ERR("Failed to write ERA low byte (%d)", ret); 205 | return -EIO; 206 | } 207 | 208 | ret = pinnacle_write(dev, PINNACLE_REG_ERA_CONTROL, PINNACLE_ERA_CONTROL_WRITE); 209 | if (ret < 0) { 210 | LOG_ERR("Failed to write ERA control (%d)", ret); 211 | return -EIO; 212 | } 213 | 214 | uint8_t control_val; 215 | do { 216 | 217 | ret = pinnacle_seq_read(dev, PINNACLE_REG_ERA_CONTROL, &control_val, 1); 218 | if (ret < 0) { 219 | LOG_ERR("Failed to read ERA control (%d)", ret); 220 | return -EIO; 221 | } 222 | 223 | } while (control_val != 0x00); 224 | 225 | ret = pinnacle_clear_status(dev); 226 | 227 | set_int(dev, true); 228 | 229 | return ret; 230 | } 231 | 232 | static void pinnacle_report_data(const struct device *dev) { 233 | const struct pinnacle_config *config = dev->config; 234 | uint8_t packet[3]; 235 | int ret; 236 | ret = pinnacle_seq_read(dev, PINNACLE_STATUS1, packet, 1); 237 | if (ret < 0) { 238 | LOG_ERR("read status: %d", ret); 239 | return; 240 | } 241 | 242 | LOG_HEXDUMP_DBG(packet, 1, "Pinnacle Status1"); 243 | 244 | // Ignore 0xFF packets that indicate communcation failure, or if SW_DR isn't asserted 245 | if (packet[0] == 0xFF || !(packet[0] & PINNACLE_STATUS1_SW_DR)) { 246 | return; 247 | } 248 | ret = pinnacle_seq_read(dev, PINNACLE_2_2_PACKET0, packet, 3); 249 | if (ret < 0) { 250 | LOG_ERR("read packet: %d", ret); 251 | return; 252 | } 253 | 254 | LOG_HEXDUMP_DBG(packet, 3, "Pinnacle Packets"); 255 | 256 | struct pinnacle_data *data = dev->data; 257 | uint8_t btn = packet[0] & 258 | (PINNACLE_PACKET0_BTN_PRIM | PINNACLE_PACKET0_BTN_SEC | PINNACLE_PACKET0_BTN_AUX); 259 | 260 | int8_t dx = (int8_t)packet[1]; 261 | int8_t dy = (int8_t)packet[2]; 262 | 263 | if (packet[0] & PINNACLE_PACKET0_X_SIGN) { 264 | WRITE_BIT(dx, 7, 1); 265 | } 266 | if (packet[0] & PINNACLE_PACKET0_Y_SIGN) { 267 | WRITE_BIT(dy, 7, 1); 268 | } 269 | 270 | if (data->in_int) { 271 | LOG_DBG("Clearing status bit"); 272 | ret = pinnacle_clear_status(dev); 273 | data->in_int = true; 274 | } 275 | 276 | if (!config->no_taps && (btn || data->btn_cache)) { 277 | for (int i = 0; i < 3; i++) { 278 | uint8_t btn_val = btn & BIT(i); 279 | if (btn_val != (data->btn_cache & BIT(i))) { 280 | input_report_key(dev, INPUT_BTN_0 + i, btn_val ? 1 : 0, false, K_FOREVER); 281 | } 282 | } 283 | } 284 | 285 | data->btn_cache = btn; 286 | 287 | input_report_rel(dev, INPUT_REL_X, dx, false, K_FOREVER); 288 | input_report_rel(dev, INPUT_REL_Y, dy, true, K_FOREVER); 289 | 290 | return; 291 | } 292 | 293 | static void pinnacle_work_cb(struct k_work *work) { 294 | struct pinnacle_data *data = CONTAINER_OF(work, struct pinnacle_data, work); 295 | pinnacle_report_data(data->dev); 296 | } 297 | 298 | static void pinnacle_gpio_cb(const struct device *port, struct gpio_callback *cb, uint32_t pins) { 299 | struct pinnacle_data *data = CONTAINER_OF(cb, struct pinnacle_data, gpio_cb); 300 | 301 | LOG_DBG("HW DR asserted"); 302 | data->in_int = true; 303 | k_work_submit(&data->work); 304 | } 305 | 306 | static int pinnacle_adc_sensitivity_reg_value(enum pinnacle_sensitivity sensitivity) { 307 | switch (sensitivity) { 308 | case PINNACLE_SENSITIVITY_1X: 309 | return PINNACLE_TRACKING_ADC_CONFIG_1X; 310 | case PINNACLE_SENSITIVITY_2X: 311 | return PINNACLE_TRACKING_ADC_CONFIG_2X; 312 | case PINNACLE_SENSITIVITY_3X: 313 | return PINNACLE_TRACKING_ADC_CONFIG_3X; 314 | case PINNACLE_SENSITIVITY_4X: 315 | return PINNACLE_TRACKING_ADC_CONFIG_4X; 316 | default: 317 | return PINNACLE_TRACKING_ADC_CONFIG_1X; 318 | } 319 | } 320 | 321 | static int pinnacle_tune_edge_sensitivity(const struct device *dev) { 322 | const struct pinnacle_config *config = dev->config; 323 | int ret; 324 | 325 | uint8_t x_val; 326 | ret = pinnacle_era_read(dev, PINNACLE_ERA_REG_X_AXIS_WIDE_Z_MIN, &x_val); 327 | if (ret < 0) { 328 | LOG_WRN("Failed to read X val"); 329 | return ret; 330 | } 331 | 332 | LOG_WRN("X val: %d", x_val); 333 | 334 | uint8_t y_val; 335 | ret = pinnacle_era_read(dev, PINNACLE_ERA_REG_Y_AXIS_WIDE_Z_MIN, &y_val); 336 | if (ret < 0) { 337 | LOG_WRN("Failed to read Y val"); 338 | return ret; 339 | } 340 | 341 | LOG_WRN("Y val: %d", y_val); 342 | 343 | ret = pinnacle_era_write(dev, PINNACLE_ERA_REG_X_AXIS_WIDE_Z_MIN, config->x_axis_z_min); 344 | if (ret < 0) { 345 | LOG_ERR("Failed to set X-Axis Min-Z %d", ret); 346 | return ret; 347 | } 348 | ret = pinnacle_era_write(dev, PINNACLE_ERA_REG_Y_AXIS_WIDE_Z_MIN, config->y_axis_z_min); 349 | if (ret < 0) { 350 | LOG_ERR("Failed to set Y-Axis Min-Z %d", ret); 351 | return ret; 352 | } 353 | return 0; 354 | } 355 | 356 | static int pinnacle_set_adc_tracking_sensitivity(const struct device *dev) { 357 | const struct pinnacle_config *config = dev->config; 358 | 359 | uint8_t val; 360 | int ret = pinnacle_era_read(dev, PINNACLE_ERA_REG_TRACKING_ADC_CONFIG, &val); 361 | if (ret < 0) { 362 | LOG_ERR("Failed to get ADC sensitivity %d", ret); 363 | } 364 | 365 | val &= 0x3F; 366 | val |= pinnacle_adc_sensitivity_reg_value(config->sensitivity); 367 | 368 | ret = pinnacle_era_write(dev, PINNACLE_ERA_REG_TRACKING_ADC_CONFIG, val); 369 | if (ret < 0) { 370 | LOG_ERR("Failed to set ADC sensitivity %d", ret); 371 | } 372 | ret = pinnacle_era_read(dev, PINNACLE_ERA_REG_TRACKING_ADC_CONFIG, &val); 373 | if (ret < 0) { 374 | LOG_ERR("Failed to get ADC sensitivity %d", ret); 375 | } 376 | 377 | return ret; 378 | } 379 | 380 | static int pinnacle_force_recalibrate(const struct device *dev) { 381 | uint8_t val; 382 | int ret = pinnacle_seq_read(dev, PINNACLE_CAL_CFG, &val, 1); 383 | if (ret < 0) { 384 | LOG_ERR("Failed to get cal config %d", ret); 385 | } 386 | 387 | val |= 0x01; 388 | ret = pinnacle_write(dev, PINNACLE_CAL_CFG, val); 389 | if (ret < 0) { 390 | LOG_ERR("Failed to force calibration %d", ret); 391 | } 392 | 393 | do { 394 | pinnacle_seq_read(dev, PINNACLE_CAL_CFG, &val, 1); 395 | } while (val & 0x01); 396 | 397 | return ret; 398 | } 399 | 400 | int pinnacle_set_sleep(const struct device *dev, bool enabled) { 401 | uint8_t sys_cfg; 402 | int ret = pinnacle_seq_read(dev, PINNACLE_SYS_CFG, &sys_cfg, 1); 403 | if (ret < 0) { 404 | LOG_ERR("can't read sys config %d", ret); 405 | return ret; 406 | } 407 | 408 | if (((sys_cfg & PINNACLE_SYS_CFG_EN_SLEEP) != 0) == enabled) { 409 | return 0; 410 | } 411 | 412 | LOG_DBG("Setting sleep: %s", (enabled ? "on" : "off")); 413 | WRITE_BIT(sys_cfg, PINNACLE_SYS_CFG_EN_SLEEP_BIT, enabled ? 1 : 0); 414 | 415 | ret = pinnacle_write(dev, PINNACLE_SYS_CFG, sys_cfg); 416 | if (ret < 0) { 417 | LOG_ERR("can't write sleep config %d", ret); 418 | return ret; 419 | } 420 | 421 | return ret; 422 | } 423 | 424 | static int pinnacle_init(const struct device *dev) { 425 | struct pinnacle_data *data = dev->data; 426 | const struct pinnacle_config *config = dev->config; 427 | int ret; 428 | 429 | uint8_t fw_id[2]; 430 | ret = pinnacle_seq_read(dev, PINNACLE_FW_ID, fw_id, 2); 431 | if (ret < 0) { 432 | LOG_ERR("Failed to get the FW ID %d", ret); 433 | } 434 | 435 | LOG_DBG("Found device with FW ID: 0x%02x, Version: 0x%02x", fw_id[0], fw_id[1]); 436 | 437 | data->in_int = false; 438 | k_msleep(10); 439 | ret = pinnacle_write(dev, PINNACLE_STATUS1, 0); // Clear CC 440 | if (ret < 0) { 441 | LOG_ERR("can't write %d", ret); 442 | return ret; 443 | } 444 | k_usleep(50); 445 | ret = pinnacle_write(dev, PINNACLE_SYS_CFG, PINNACLE_SYS_CFG_RESET); 446 | if (ret < 0) { 447 | LOG_ERR("can't reset %d", ret); 448 | return ret; 449 | } 450 | k_msleep(20); 451 | ret = pinnacle_write(dev, PINNACLE_Z_IDLE, 0x05); // No Z-Idle packets 452 | if (ret < 0) { 453 | LOG_ERR("can't write %d", ret); 454 | return ret; 455 | } 456 | 457 | ret = pinnacle_set_adc_tracking_sensitivity(dev); 458 | if (ret < 0) { 459 | LOG_ERR("Failed to set ADC sensitivity %d", ret); 460 | return ret; 461 | } 462 | 463 | ret = pinnacle_tune_edge_sensitivity(dev); 464 | if (ret < 0) { 465 | LOG_ERR("Failed to tune edge sensitivity %d", ret); 466 | return ret; 467 | } 468 | ret = pinnacle_force_recalibrate(dev); 469 | if (ret < 0) { 470 | LOG_ERR("Failed to force recalibration %d", ret); 471 | return ret; 472 | } 473 | 474 | if (config->sleep_en) { 475 | ret = pinnacle_set_sleep(dev, true); 476 | if (ret < 0) { 477 | return ret; 478 | } 479 | } 480 | 481 | uint8_t packet[1]; 482 | ret = pinnacle_seq_read(dev, PINNACLE_SLEEP_INTERVAL, packet, 1); 483 | 484 | if (ret >= 0) { 485 | LOG_DBG("Default sleep interval %d", packet[0]); 486 | } 487 | 488 | ret = pinnacle_write(dev, PINNACLE_SLEEP_INTERVAL, 255); 489 | if (ret <= 0) { 490 | LOG_DBG("Failed to update sleep interaval %d", ret); 491 | } 492 | 493 | uint8_t feed_cfg2 = PINNACLE_FEED_CFG2_EN_IM | PINNACLE_FEED_CFG2_EN_BTN_SCRL; 494 | if (config->no_taps) { 495 | feed_cfg2 |= PINNACLE_FEED_CFG2_DIS_TAP; 496 | } 497 | 498 | if (config->no_secondary_tap) { 499 | feed_cfg2 |= PINNACLE_FEED_CFG2_DIS_SEC; 500 | } 501 | 502 | if (config->rotate_90) { 503 | feed_cfg2 |= PINNACLE_FEED_CFG2_ROTATE_90; 504 | } 505 | ret = pinnacle_write(dev, PINNACLE_FEED_CFG2, feed_cfg2); 506 | if (ret < 0) { 507 | LOG_ERR("can't write %d", ret); 508 | return ret; 509 | } 510 | uint8_t feed_cfg1 = PINNACLE_FEED_CFG1_EN_FEED; 511 | if (config->x_invert) { 512 | feed_cfg1 |= PINNACLE_FEED_CFG1_INV_X; 513 | } 514 | 515 | if (config->y_invert) { 516 | feed_cfg1 |= PINNACLE_FEED_CFG1_INV_Y; 517 | } 518 | if (feed_cfg1) { 519 | ret = pinnacle_write(dev, PINNACLE_FEED_CFG1, feed_cfg1); 520 | } 521 | if (ret < 0) { 522 | LOG_ERR("can't write %d", ret); 523 | return ret; 524 | } 525 | 526 | data->dev = dev; 527 | 528 | pinnacle_clear_status(dev); 529 | 530 | gpio_pin_configure_dt(&config->dr, GPIO_INPUT); 531 | gpio_init_callback(&data->gpio_cb, pinnacle_gpio_cb, BIT(config->dr.pin)); 532 | ret = gpio_add_callback(config->dr.port, &data->gpio_cb); 533 | if (ret < 0) { 534 | LOG_ERR("Failed to set DR callback: %d", ret); 535 | return -EIO; 536 | } 537 | 538 | k_work_init(&data->work, pinnacle_work_cb); 539 | 540 | pinnacle_write(dev, PINNACLE_FEED_CFG1, feed_cfg1); 541 | 542 | set_int(dev, true); 543 | 544 | return 0; 545 | } 546 | 547 | #if IS_ENABLED(CONFIG_PM_DEVICE) 548 | 549 | static int pinnacle_pm_action(const struct device *dev, enum pm_device_action action) { 550 | switch (action) { 551 | case PM_DEVICE_ACTION_SUSPEND: 552 | return set_int(dev, false); 553 | case PM_DEVICE_ACTION_RESUME: 554 | return set_int(dev, true); 555 | default: 556 | return -ENOTSUP; 557 | } 558 | } 559 | 560 | #endif // IS_ENABLED(CONFIG_PM_DEVICE) 561 | 562 | #define PINNACLE_INST(n) \ 563 | static struct pinnacle_data pinnacle_data_##n; \ 564 | static const struct pinnacle_config pinnacle_config_##n = { \ 565 | COND_CODE_1(DT_INST_ON_BUS(n, i2c), \ 566 | (.bus = {.i2c = I2C_DT_SPEC_INST_GET(n)}, .seq_read = pinnacle_i2c_seq_read, \ 567 | .write = pinnacle_i2c_write), \ 568 | (.bus = {.spi = SPI_DT_SPEC_INST_GET(n, \ 569 | SPI_OP_MODE_MASTER | SPI_WORD_SET(8) | \ 570 | SPI_TRANSFER_MSB | SPI_MODE_CPHA, \ 571 | 0)}, \ 572 | .seq_read = pinnacle_spi_seq_read, .write = pinnacle_spi_write)), \ 573 | .rotate_90 = DT_INST_PROP(n, rotate_90), \ 574 | .x_invert = DT_INST_PROP(n, x_invert), \ 575 | .y_invert = DT_INST_PROP(n, y_invert), \ 576 | .sleep_en = DT_INST_PROP(n, sleep), \ 577 | .no_taps = DT_INST_PROP(n, no_taps), \ 578 | .no_secondary_tap = DT_INST_PROP(n, no_secondary_tap), \ 579 | .x_axis_z_min = DT_INST_PROP_OR(n, x_axis_z_min, 5), \ 580 | .y_axis_z_min = DT_INST_PROP_OR(n, y_axis_z_min, 4), \ 581 | .sensitivity = DT_INST_ENUM_IDX_OR(n, sensitivity, PINNACLE_SENSITIVITY_1X), \ 582 | .dr = GPIO_DT_SPEC_GET_OR(DT_DRV_INST(n), dr_gpios, {}), \ 583 | }; \ 584 | PM_DEVICE_DT_INST_DEFINE(n, pinnacle_pm_action); \ 585 | DEVICE_DT_INST_DEFINE(n, pinnacle_init, PM_DEVICE_DT_INST_GET(n), &pinnacle_data_##n, \ 586 | &pinnacle_config_##n, POST_KERNEL, CONFIG_INPUT_PINNACLE_INIT_PRIORITY, \ 587 | NULL); 588 | 589 | DT_INST_FOREACH_STATUS_OKAY(PINNACLE_INST) 590 | -------------------------------------------------------------------------------- /drivers/input/input_pinnacle.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define PINNACLE_READ 0xA0 8 | #define PINNACLE_WRITE 0x80 9 | 10 | #define PINNACLE_AUTOINC 0xFC 11 | #define PINNACLE_FILLER 0xFB 12 | 13 | // Registers 14 | #define PINNACLE_FW_ID 0x00 // ASIC ID. 15 | #define PINNACLE_FW_VER 0x01 // Firmware Version Firmware revision number. 16 | #define PINNACLE_STATUS1 0x02 // Contains status flags about the state of Pinnacle. 17 | #define PINNACLE_STATUS1_SW_DR BIT(2) 18 | #define PINNACLE_STATUS1_SW_CC BIT(3) 19 | #define PINNACLE_SYS_CFG 0x03 // Contains system operation and configuration bits. 20 | #define PINNACLE_SYS_CFG_EN_SLEEP_BIT 2 21 | #define PINNACLE_SYS_CFG_EN_SLEEP BIT(2) 22 | #define PINNACLE_SYS_CFG_SHUTDOWN BIT(1) 23 | #define PINNACLE_SYS_CFG_RESET BIT(0) 24 | 25 | #define PINNACLE_FEED_CFG1 0x04 // Contains feed operation and configuration bits. 26 | #define PINNACLE_FEED_CFG1_EN_FEED BIT(0) 27 | #define PINNACLE_FEED_CFG1_ABS_MODE BIT(1) 28 | #define PINNACLE_FEED_CFG1_DIS_FILT BIT(2) 29 | #define PINNACLE_FEED_CFG1_DIS_X BIT(3) 30 | #define PINNACLE_FEED_CFG1_DIS_Y BIT(4) 31 | #define PINNACLE_FEED_CFG1_INV_X BIT(6) 32 | #define PINNACLE_FEED_CFG1_INV_Y BIT(7) 33 | #define PINNACLE_FEED_CFG2 0x05 // Contains feed operation and configuration bits. 34 | #define PINNACLE_FEED_CFG2_EN_IM BIT(0) // Intellimouse 35 | #define PINNACLE_FEED_CFG2_DIS_TAP BIT(1) // Disable all taps 36 | #define PINNACLE_FEED_CFG2_DIS_SEC BIT(2) // Disable secondary tap 37 | #define PINNACLE_FEED_CFG2_DIS_SCRL BIT(3) // Disable scroll 38 | #define PINNACLE_FEED_CFG2_DIS_GE BIT(4) // Disable GlideExtend 39 | #define PINNACLE_FEED_CFG2_EN_BTN_SCRL BIT(6) // Enable Button Scroll 40 | #define PINNACLE_FEED_CFG2_ROTATE_90 BIT(7) // Swap X & Y 41 | #define PINNACLE_CAL_CFG 0x07 // Contains calibration configuration bits. 42 | #define PINNACLE_PS2_AUX 0x08 // Contains Data register for PS/2 Aux Control. 43 | #define PINNACLE_SAMPLE 0x09 // Sample Rate Number of samples generated per second. 44 | #define PINNACLE_Z_IDLE 0x0A // Number of Z=0 packets sent when Z goes from >0 to 0. 45 | #define PINNACLE_Z_SCALER 0x0B // Contains the pen Z_On threshold. 46 | #define PINNACLE_SLEEP_INTERVAL 0x0C // Sleep Interval 47 | #define PINNACLE_SLEEP_TIMER 0x0D // Sleep Timer 48 | #define PINNACLE_AG_PACKET0 0x10 // trackpad Data (Pinnacle AG) 49 | #define PINNACLE_2_2_PACKET0 0x12 // trackpad Data 50 | #define PINNACLE_REG_COUNT 0x18 51 | 52 | #define PINNACLE_REG_ERA_VALUE 0x1B 53 | #define PINNACLE_REG_ERA_HIGH_BYTE 0x1C 54 | #define PINNACLE_REG_ERA_LOW_BYTE 0x1D 55 | #define PINNACLE_REG_ERA_CONTROL 0x1E 56 | 57 | #define PINNACLE_ERA_CONTROL_READ 0x01 58 | #define PINNACLE_ERA_CONTROL_WRITE 0x02 59 | 60 | #define PINNACLE_ERA_REG_X_AXIS_WIDE_Z_MIN 0x0149 61 | #define PINNACLE_ERA_REG_Y_AXIS_WIDE_Z_MIN 0x0168 62 | #define PINNACLE_ERA_REG_TRACKING_ADC_CONFIG 0x0187 63 | 64 | #define PINNACLE_TRACKING_ADC_CONFIG_1X 0x00 65 | #define PINNACLE_TRACKING_ADC_CONFIG_2X 0x40 66 | #define PINNACLE_TRACKING_ADC_CONFIG_3X 0x80 67 | #define PINNACLE_TRACKING_ADC_CONFIG_4X 0xC0 68 | 69 | #define PINNACLE_PACKET0_BTN_PRIM BIT(0) // Primary button 70 | #define PINNACLE_PACKET0_BTN_SEC BIT(1) // Secondary button 71 | #define PINNACLE_PACKET0_BTN_AUX BIT(2) // Auxiliary (middle?) button 72 | #define PINNACLE_PACKET0_X_SIGN BIT(4) // X delta sign 73 | #define PINNACLE_PACKET0_Y_SIGN BIT(5) // Y delta sign 74 | 75 | struct pinnacle_data { 76 | uint8_t btn_cache; 77 | bool in_int; 78 | const struct device *dev; 79 | struct gpio_callback gpio_cb; 80 | struct k_work work; 81 | }; 82 | 83 | enum pinnacle_sensitivity { 84 | PINNACLE_SENSITIVITY_1X, 85 | PINNACLE_SENSITIVITY_2X, 86 | PINNACLE_SENSITIVITY_3X, 87 | PINNACLE_SENSITIVITY_4X, 88 | }; 89 | 90 | typedef int (*pinnacle_seq_read_t)(const struct device *dev, const uint8_t addr, uint8_t *buf, 91 | const uint8_t len); 92 | typedef int (*pinnacle_write_t)(const struct device *dev, const uint8_t addr, const uint8_t val); 93 | 94 | struct pinnacle_config { 95 | union { 96 | struct i2c_dt_spec i2c; 97 | struct spi_dt_spec spi; 98 | } bus; 99 | 100 | pinnacle_seq_read_t seq_read; 101 | pinnacle_write_t write; 102 | 103 | bool rotate_90, sleep_en, no_taps, no_secondary_tap, x_invert, y_invert; 104 | enum pinnacle_sensitivity sensitivity; 105 | uint8_t x_axis_z_min, y_axis_z_min; 106 | const struct gpio_dt_spec dr; 107 | }; 108 | 109 | int pinnacle_set_sleep(const struct device *dev, bool enabled); 110 | -------------------------------------------------------------------------------- /drivers/input/zmk_pinnacle_idle_sleeper.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "input_pinnacle.h" 8 | 9 | #include 10 | 11 | LOG_MODULE_REGISTER(pinnacle_sleeper, CONFIG_INPUT_LOG_LEVEL); 12 | 13 | #define GET_PINNACLE(node_id) DEVICE_DT_GET(node_id), 14 | 15 | static const struct device *pinnacle_devs[] = { 16 | DT_FOREACH_STATUS_OKAY(cirque_pinnacle, GET_PINNACLE) 17 | }; 18 | 19 | static int on_activity_state(const zmk_event_t *eh) { 20 | struct zmk_activity_state_changed *state_ev = as_zmk_activity_state_changed(eh); 21 | 22 | if (!state_ev) { 23 | LOG_WRN("NO EVENT, leaving early"); 24 | return 0; 25 | } 26 | 27 | bool sleep = state_ev->state == ZMK_ACTIVITY_ACTIVE ? 0 : 1; 28 | for (size_t i = 0; i < ARRAY_SIZE(pinnacle_devs); i++) { 29 | pinnacle_set_sleep(pinnacle_devs[i], sleep); 30 | } 31 | 32 | return 0; 33 | } 34 | 35 | ZMK_LISTENER(zmk_pinnacle_idle_sleeper, on_activity_state); 36 | ZMK_SUBSCRIPTION(zmk_pinnacle_idle_sleeper, zmk_activity_state_changed); -------------------------------------------------------------------------------- /dts/bindings/input/cirque,pinnacle-common.yaml: -------------------------------------------------------------------------------- 1 | properties: 2 | dr-gpios: 3 | type: phandle-array 4 | description: Data ready pin for the trackpad 5 | rotate-90: 6 | type: boolean 7 | x-invert: 8 | type: boolean 9 | y-invert: 10 | type: boolean 11 | sleep: 12 | type: boolean 13 | no-secondary-tap: 14 | type: boolean 15 | no-taps: 16 | type: boolean 17 | sensitivity: 18 | type: string 19 | enum: 20 | - 1x 21 | - 2x 22 | - 3x 23 | - 4x 24 | description: ADC attenuation (sensitivity) setting. 25 | x-axis-z-min: 26 | type: int 27 | default: 5 28 | y-axis-z-min: 29 | type: int 30 | default: 4 31 | 32 | -------------------------------------------------------------------------------- /dts/bindings/input/cirque,pinnacle-i2c.yaml: -------------------------------------------------------------------------------- 1 | description: | 2 | Sensor driver for the Cirque Pinnacle trackpad ASICs, using the I2C interface 3 | 4 | compatible: "cirque,pinnacle" 5 | 6 | include: ["i2c-device.yaml", "cirque,pinnacle-common.yaml"] 7 | -------------------------------------------------------------------------------- /dts/bindings/input/cirque,pinnacle-spi.yaml: -------------------------------------------------------------------------------- 1 | description: | 2 | Sensor driver for the Cirque Pinnacle trackpad ASICs, using the SPI interface 3 | 4 | compatible: "cirque,pinnacle" 5 | 6 | include: ["spi-device.yaml", "cirque,pinnacle-common.yaml"] 7 | -------------------------------------------------------------------------------- /dts/bindings/vendor-prefixes.txt: -------------------------------------------------------------------------------- 1 | cirque Cirque Corporation 2 | -------------------------------------------------------------------------------- /zephyr/module.yml: -------------------------------------------------------------------------------- 1 | build: 2 | cmake: . 3 | kconfig: Kconfig 4 | settings: 5 | dts_root: . 6 | --------------------------------------------------------------------------------