summaryrefslogtreecommitdiff
path: root/touch_mf_mode.c
blob: 57154759d1032c069ecb0cd5fe1370918e4a649b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
// 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;
}