diff --git a/drivers/input/input_pinnacle.c b/drivers/input/input_pinnacle.c index d5c7416..7e44ff2 100644 --- a/drivers/input/input_pinnacle.c +++ b/drivers/input/input_pinnacle.c @@ -21,6 +21,12 @@ static int pinnacle_write(const struct device *dev, const uint8_t addr, const ui return config->write(dev, addr, val); } +// Now that we are counting ZIDLEs it would be very bad to miss one. But in my testing I see that happen (rarely - once every +// couple of days of usage). The fact that the current irq system is edge triggered probably isn't great for this reason. +// But for now just have the touch controller emit NUM_ZIDLE_PAD extra idles +#define NUM_ZIDLE 3 +#define NUM_ZIDLE_PAD 2 + #if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) static int pinnacle_i2c_seq_read(const struct device *dev, const uint8_t addr, uint8_t *buf, @@ -117,7 +123,7 @@ static int pinnacle_spi_write(const struct device *dev, const uint8_t addr, cons static int set_int(const struct device *dev, const bool en) { const struct pinnacle_config *config = dev->config; int ret = gpio_pin_interrupt_configure_dt(&config->dr, - en ? GPIO_INT_EDGE_TO_ACTIVE : GPIO_INT_DISABLE); + en ? GPIO_INT_LEVEL_ACTIVE : GPIO_INT_DISABLE); if (ret < 0) { LOG_ERR("can't set interrupt"); } @@ -229,8 +235,166 @@ static int pinnacle_era_write(const struct device *dev, const uint16_t addr, uin return ret; } -static void pinnacle_report_data(const struct device *dev) { +static void pinnacle_send_rel(const struct device *dev, int8_t dx, int8_t dy) { + const struct pinnacle_config *config = dev->config; + struct pinnacle_data *data = dev->data; + + pinnacle_clear_status(dev); + set_int(dev, true); + + bool must_send = false; + + uint8_t btn = data->last_btn; + if (!config->no_taps && (btn || data->btn_cache)) { + for (int i = 0; i < 3; i++) { + uint8_t btn_val = btn & BIT(i); + if (btn_val != (data->btn_cache & BIT(i))) { + input_report_key(dev, INPUT_BTN_0 + i, btn_val ? 1 : 0, false, K_FOREVER); + must_send = true; + } + } + } + + data->btn_cache = btn; + bool is_touching = (data->last_z > 0); + bool touch_changed = false; + if(is_touching) { + must_send = true; + + if(data->num_z_idle > 0) { // we just recently had z idles + touch_changed = true; + data->num_z_idle = 0; + dx = 0; + dy = 0; // starting a new press, must reset deltas + } + } else { + data->num_z_idle++; + if(data->num_z_idle == NUM_ZIDLE) { + touch_changed = true; + } + dx = 0; + dy = 0; + } + + LOG_DBG("Rel move: touch_changed=%d z=%d dx=%d dy=%d", touch_changed, data->last_z, dx, dy); + + if(touch_changed) + { + // Finalize the input event only if we have something to report + input_report_key(dev, INPUT_BTN_TOUCH, is_touching ? 1 : 0, false, K_FOREVER); + must_send = true; + } + + if(must_send) { + input_report_rel(dev, INPUT_REL_X, dx, false, K_FOREVER); + input_report_rel(dev, INPUT_REL_Y, dy, true, K_FOREVER); + } +} + +static void pinnacle_send_abs(const struct device *dev) { const struct pinnacle_config *config = dev->config; + struct pinnacle_data *data = dev->data; + int16_t x = data->last_x; + int16_t y = data->last_y; + int8_t z = data->last_z; + + LOG_DBG("Clearing status bit"); + pinnacle_clear_status(dev); + set_int(dev, true); + + uint8_t btn = data->last_btn; + if (!config->no_taps && (btn || data->btn_cache)) { + for (int i = 0; i < 3; i++) { + uint8_t btn_val = btn & BIT(i); + if (btn_val != (data->btn_cache & BIT(i))) { + input_report_key(dev, INPUT_BTN_0 + i, btn_val ? 1 : 0, false, K_FOREVER); + } + } + } + + data->btn_cache = btn; + + if (z > 0) { + if (x < config->absolute_mode_clamp_min_x) { + x = config->absolute_mode_clamp_min_x; + } else if (x > config->absolute_mode_clamp_max_x) { + x = config->absolute_mode_clamp_max_x; + } + if (y < config->absolute_mode_clamp_min_y) { + y = config->absolute_mode_clamp_min_y; + } else if (y > config->absolute_mode_clamp_max_y) { + y = config->absolute_mode_clamp_max_y; + } + + // scale to be in the configured interval + x = ((x - config->absolute_mode_clamp_min_x) * config->absolute_mode_scale_to_width) / (config->absolute_mode_clamp_max_x - config->absolute_mode_clamp_min_x); + y = ((y - config->absolute_mode_clamp_min_y) * config->absolute_mode_scale_to_height) / (config->absolute_mode_clamp_max_y - config->absolute_mode_clamp_min_y); + + input_report_abs(dev, INPUT_ABS_X, x, false, K_FOREVER); + input_report_abs(dev, INPUT_ABS_Y, y, true, K_FOREVER); + } + + return; +} + +static int pinnacle_read_abs(const struct device *dev) { + uint8_t packet[6]; + int ret; + ret = pinnacle_seq_read(dev, PINNACLE_STATUS1, packet, 1); + if (ret < 0) { + LOG_ERR("read status: %d", ret); + return ret; + } + if (!(packet[0] & PINNACLE_STATUS1_SW_DR)) { + return -1; + } + ret = pinnacle_seq_read(dev, PINNACLE_2_2_PACKET0, packet, 6); + if (ret < 0) { + LOG_ERR("read packet: %d", ret); + return ret; + } + struct pinnacle_data *data = dev->data; + // TODO: Enable SW3-SW5 as well + data->last_btn = packet[0] & + (PINNACLE_PACKET0_BTN_PRIM | PINNACLE_PACKET0_BTN_SEC | PINNACLE_PACKET0_BTN_AUX); + uint8_t x_low = packet[2]; + uint8_t y_low = packet[3]; + uint8_t xy_high = packet[4]; + data->last_x = ((xy_high & 0x0F) << 8) | x_low; + data->last_y = ((xy_high & 0xF0) << 4) | y_low; + data->last_z = (uint8_t)(packet[5] & 0x1F); + + LOG_DBG("button: %d, x: %d y: %d z: %d", data->last_btn, data->last_x, data->last_y, data->last_z); + return 0; +} + +static void pinnacle_report_data_abs(const struct device *dev) { + int ret = pinnacle_read_abs(dev); + if (ret == 0) { + pinnacle_send_abs(dev); + } +} + +static void pinnacle_report_data_abs_rel(const struct device *dev) { + struct pinnacle_data *data = dev->data; + int16_t old_x = data->last_x; + int16_t old_y = data->last_y; + + int ret = pinnacle_read_abs(dev); + + if (ret == 0) { + int16_t dx = data->last_x - old_x; + int16_t dy = data->last_y - old_y; + const struct pinnacle_config *config = dev->config; + + dx /= config->abs_rel_divisor; + dy /= config->abs_rel_divisor; + + pinnacle_send_rel(dev, (int8_t) dx, (int8_t) dy); + } +} + +static void pinnacle_report_data_rel(const struct device *dev) { uint8_t packet[3]; int ret; ret = pinnacle_seq_read(dev, PINNACLE_STATUS1, packet, 1); @@ -254,7 +418,7 @@ static void pinnacle_report_data(const struct device *dev) { LOG_HEXDUMP_DBG(packet, 3, "Pinnacle Packets"); struct pinnacle_data *data = dev->data; - uint8_t btn = packet[0] & + data->last_btn = packet[0] & (PINNACLE_PACKET0_BTN_PRIM | PINNACLE_PACKET0_BTN_SEC | PINNACLE_PACKET0_BTN_AUX); int8_t dx = (int8_t)packet[1]; @@ -267,39 +431,30 @@ static void pinnacle_report_data(const struct device *dev) { WRITE_BIT(dy, 7, 1); } - if (data->in_int) { - LOG_DBG("Clearing status bit"); - ret = pinnacle_clear_status(dev); - data->in_int = true; - } - - if (!config->no_taps && (btn || data->btn_cache)) { - for (int i = 0; i < 3; i++) { - uint8_t btn_val = btn & BIT(i); - if (btn_val != (data->btn_cache & BIT(i))) { - input_report_key(dev, INPUT_BTN_0 + i, btn_val ? 1 : 0, false, K_FOREVER); - } - } - } - - data->btn_cache = btn; - - input_report_rel(dev, INPUT_REL_X, dx, false, K_FOREVER); - input_report_rel(dev, INPUT_REL_Y, dy, true, K_FOREVER); - - return; + // always claim touch changed + data->last_z = 1; + pinnacle_send_rel(dev, (int8_t) dx, (int8_t) dy); } static void pinnacle_work_cb(struct k_work *work) { struct pinnacle_data *data = CONTAINER_OF(work, struct pinnacle_data, work); - pinnacle_report_data(data->dev); + const struct device *dev = data->dev; + const struct pinnacle_config *config = dev->config; + + if (config->absolute_mode) { + pinnacle_report_data_abs(dev); + } else if (config->abs_rel_divisor) { + pinnacle_report_data_abs_rel(dev); + } else { + pinnacle_report_data_rel(dev); + } } static void pinnacle_gpio_cb(const struct device *port, struct gpio_callback *cb, uint32_t pins) { struct pinnacle_data *data = CONTAINER_OF(cb, struct pinnacle_data, gpio_cb); LOG_DBG("HW DR asserted"); - data->in_int = true; + set_int(data->dev, false); // mask the int until we've handled it (now level triggered) k_work_submit(&data->work); } @@ -434,7 +589,6 @@ static int pinnacle_init(const struct device *dev) { LOG_DBG("Found device with FW ID: 0x%02x, Version: 0x%02x", fw_id[0], fw_id[1]); - data->in_int = false; k_msleep(10); ret = pinnacle_write(dev, PINNACLE_STATUS1, 0); // Clear CC if (ret < 0) { @@ -448,7 +602,7 @@ static int pinnacle_init(const struct device *dev) { return ret; } k_msleep(20); - ret = pinnacle_write(dev, PINNACLE_Z_IDLE, 0x05); // No Z-Idle packets + ret = pinnacle_write(dev, PINNACLE_Z_IDLE, NUM_ZIDLE + NUM_ZIDLE_PAD); if (ret < 0) { LOG_ERR("can't write %d", ret); return ret; @@ -480,7 +634,7 @@ static int pinnacle_init(const struct device *dev) { uint8_t packet[1]; ret = pinnacle_seq_read(dev, PINNACLE_SLEEP_INTERVAL, packet, 1); - + if (ret >= 0) { LOG_DBG("Default sleep interval %d", packet[0]); } @@ -508,6 +662,12 @@ static int pinnacle_init(const struct device *dev) { return ret; } uint8_t feed_cfg1 = PINNACLE_FEED_CFG1_EN_FEED; + if (config->absolute_mode || config->abs_rel_divisor) { + feed_cfg1 |= PINNACLE_FEED_CFG1_ABS_MODE; + LOG_ERR("Using absolute mode"); + } else { + LOG_ERR("Using relative mode"); + } if (config->x_invert) { feed_cfg1 |= PINNACLE_FEED_CFG1_INV_X; } @@ -576,6 +736,14 @@ static int pinnacle_pm_action(const struct device *dev, enum pm_device_action ac .sleep_en = DT_INST_PROP(n, sleep), \ .no_taps = DT_INST_PROP(n, no_taps), \ .no_secondary_tap = DT_INST_PROP(n, no_secondary_tap), \ + .absolute_mode = DT_INST_PROP(n, absolute_mode), \ + .abs_rel_divisor = DT_INST_PROP(n, abs_rel_divisor), \ + .absolute_mode_scale_to_width = DT_INST_PROP(n, absolute_mode_scale_to_width), \ + .absolute_mode_scale_to_height = DT_INST_PROP(n, absolute_mode_scale_to_height), \ + .absolute_mode_clamp_min_x = DT_INST_PROP(n, absolute_mode_clamp_min_x), \ + .absolute_mode_clamp_max_x = DT_INST_PROP(n, absolute_mode_clamp_max_x), \ + .absolute_mode_clamp_min_y = DT_INST_PROP(n, absolute_mode_clamp_min_y), \ + .absolute_mode_clamp_max_y = DT_INST_PROP(n, absolute_mode_clamp_max_y), \ .x_axis_z_min = DT_INST_PROP_OR(n, x_axis_z_min, 5), \ .y_axis_z_min = DT_INST_PROP_OR(n, y_axis_z_min, 4), \ .sensitivity = DT_INST_ENUM_IDX_OR(n, sensitivity, PINNACLE_SENSITIVITY_1X), \ diff --git a/drivers/input/input_pinnacle.h b/drivers/input/input_pinnacle.h index ed22e42..bc10fce 100644 --- a/drivers/input/input_pinnacle.h +++ b/drivers/input/input_pinnacle.h @@ -73,8 +73,12 @@ #define PINNACLE_PACKET0_Y_SIGN BIT(5) // Y delta sign struct pinnacle_data { - uint8_t btn_cache; - bool in_int; + uint8_t btn_cache; // the prior button reading + uint8_t last_btn; // the current button reading + int8_t last_z; + int8_t num_z_idle; + int16_t last_x, last_y; // last abs reading + const struct device *dev; struct gpio_callback gpio_cb; struct k_work work; @@ -100,9 +104,11 @@ struct pinnacle_config { pinnacle_seq_read_t seq_read; pinnacle_write_t write; - bool rotate_90, sleep_en, no_taps, no_secondary_tap, x_invert, y_invert; + bool rotate_90, sleep_en, no_taps, no_secondary_tap, x_invert, y_invert, absolute_mode; + uint8_t abs_rel_divisor; enum pinnacle_sensitivity sensitivity; uint8_t x_axis_z_min, y_axis_z_min; + uint16_t absolute_mode_scale_to_width, absolute_mode_scale_to_height, absolute_mode_clamp_min_x, absolute_mode_clamp_max_x, absolute_mode_clamp_min_y, absolute_mode_clamp_max_y; const struct gpio_dt_spec dr; }; diff --git a/dts/bindings/input/cirque,pinnacle-common.yaml b/dts/bindings/input/cirque,pinnacle-common.yaml index caefe3e..fd48a13 100644 --- a/dts/bindings/input/cirque,pinnacle-common.yaml +++ b/dts/bindings/input/cirque,pinnacle-common.yaml @@ -28,4 +28,42 @@ properties: y-axis-z-min: type: int default: 4 + absolute-mode: + type: boolean + abs-rel-divisor: + type: int + description: If non zero, uses pad in absmode, but sends relative events (and touch press/release) + default: 0 + absolute-mode-scale-to-width: + type: int + default: 1024 + description: Scale reported x values to [0-1024] + absolute-mode-scale-to-height: + type: int + default: 1024 + description: Scale reported y values to [0-1024] + absolute-mode-clamp-min-x: + type: int + default: 128 + description: | + Specification says that active reporting of absolute position + is: 128 ≤ X ≤1920 and 64 ≤ Y ≤ 1472, but on my touchpad, I need other values. + absolute-mode-clamp-max-x: + type: int + default: 1920 + description: | + Specification says that active reporting of absolute position + is: 128 ≤ X ≤1920 and 64 ≤ Y ≤ 1472, but on my touchpad, I need other values. + absolute-mode-clamp-min-y: + type: int + default: 64 + description: | + Specification says that active reporting of absolute position + is: 128 ≤ X ≤1920 and 64 ≤ Y ≤ 1472, but on my touchpad, I need other values. + absolute-mode-clamp-max-y: + type: int + default: 1472 + description: | + Specification says that active reporting of absolute position + is: 128 ≤ X ≤1920 and 64 ≤ Y ≤ 1472, but on my touchpad, I need other values.