| // SPDX-License-Identifier: GPL-2.0 | |
| /* | |
| * Sysfs APIs for Google Pixel devices. | |
| * | |
| * Copyright 2022 Google LLC. | |
| */ | |
| #include "touch_mf_mode.h" | |
| /* Update a state machine used to toggle control of the touch IC's motion | |
| * filter. | |
| */ | |
| int touch_mf_update_state(struct touch_mf *tmf, u8 touches) | |
| { | |
| /* Motion filter timeout, in milliseconds */ | |
| const u32 mf_timeout_ms = 500; | |
| u8 next_state = TOUCH_MF_STATE_UNFILTERED; | |
| mutex_lock(&tmf->update_mutex); | |
| tmf->touches = touches; | |
| if (tmf->mode == TOUCH_MF_MODE_UNFILTERED) { | |
| next_state = TOUCH_MF_STATE_UNFILTERED; | |
| } else if (tmf->mode == TOUCH_MF_MODE_DYNAMIC) { | |
| /* Determine the next filter state. The motion filter is enabled | |
| * by default and it is disabled while a single finger is | |
| * touching the screen. If another finger is touched down or if | |
| * a timeout expires, the motion filter is reenabled and remains | |
| * enabled until all fingers are lifted. | |
| */ | |
| next_state = tmf->state; | |
| switch (tmf->state) { | |
| case TOUCH_MF_STATE_FILTERED: | |
| if (touches == 1) { | |
| next_state = TOUCH_MF_STATE_UNFILTERED; | |
| tmf->downtime = ktime_get(); | |
| } | |
| break; | |
| case TOUCH_MF_STATE_UNFILTERED: | |
| if (touches == 0) { | |
| next_state = TOUCH_MF_STATE_FILTERED; | |
| } else if (touches > 1 || | |
| ktime_after(ktime_get(), | |
| ktime_add_ms(tmf->downtime, | |
| mf_timeout_ms))) { | |
| next_state = TOUCH_MF_STATE_LOCKED; | |
| } | |
| break; | |
| case TOUCH_MF_STATE_LOCKED: | |
| if (touches == 0) { | |
| next_state = TOUCH_MF_STATE_FILTERED; | |
| } | |
| break; | |
| } | |
| } else if (tmf->mode == TOUCH_MF_MODE_FILTERED) { | |
| next_state = TOUCH_MF_STATE_FILTERED; | |
| } else if (tmf->mode == TOUCH_MF_MODE_AUTO_REPORT) { | |
| next_state = TOUCH_MF_STATE_UNFILTERED; | |
| } | |
| /* Update continuously report switch if needed */ | |
| if ((next_state == TOUCH_MF_STATE_UNFILTERED) != | |
| (tmf->state == TOUCH_MF_STATE_UNFILTERED)) { | |
| if (tmf->set_continuously_report_enabled != NULL) { | |
| tmf->set_continuously_report_enabled(&tmf->pdev->dev, | |
| next_state == TOUCH_MF_STATE_UNFILTERED); | |
| } | |
| } | |
| tmf->state = next_state; | |
| mutex_unlock(&tmf->update_mutex); | |
| return 0; | |
| } | |
| int touch_mf_set_mode(struct touch_mf *tmf, enum touch_mf_mode mode) | |
| { | |
| int ret = 0; | |
| if ((mode < TOUCH_MF_MODE_UNFILTERED) || | |
| (mode > TOUCH_MF_MODE_AUTO_REPORT)) { | |
| ret = -EINVAL; | |
| } else { | |
| tmf->mode = mode; | |
| touch_mf_update_state(tmf, tmf->touches); | |
| } | |
| return ret; | |
| } | |
| int touch_mf_init(struct touch_mf *tmf) | |
| { | |
| /* init motion filter mode */ | |
| tmf->mode = TOUCH_MF_MODE_DYNAMIC; | |
| tmf->touches = 0; | |
| mutex_init(&tmf->update_mutex); | |
| return 0; | |
| } |