summaryrefslogtreecommitdiff
path: root/driver/touchpad_gt7288.c
blob: 945f35c0d44173b46ae0626d30885e54b2fb2fe3 (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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
/* Copyright 2019 The Chromium OS Authors. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include <stdbool.h>

#include "common.h"
#include "console.h"
#include "hooks.h"
#include "i2c.h"
#include "task.h"
#include "touchpad_gt7288.h"
#include "util.h"

/* Define this to enable various warning messages during report parsing. */
#undef DEBUG_CHECKS

#define CPRINTS(format, args...) cprints(CC_TOUCHPAD, format, ## args)

#define RETURN_ERROR(fn) do { \
	int error = (fn); \
	if (error != EC_SUCCESS) \
		return error; \
} while (0)

#define GT7288_SLAVE_ADDRESS 0x14

#define GT7288_REPORT_ID_PTP 0x04

#define GT7288_BUTTON_STATE_UP 0x80
#define GT7288_BUTTON_STATE_DOWN 0x81

#define GT7288_REG_HID_DESCRIPTOR 0x0001
#define GT7288_REG_REPORT_DESCRIPTOR 0x0002

#define GT7288_HID_DESCRIPTOR_LENGTH 0x1E
#define GT7288_REPORT_DESCRIPTOR_LENGTH 0x1AE
#define GT7288_REPORT_LENGTH 16

/**
 * gt7288_read_desc() - Read a descriptor using the Conventional Read Mode.
 * @register_id: the register containing the descriptor to read.
 * @data: the data that is read.
 * @max_length: the maximum length of data.
 *
 * Return: EC_SUCCESS or an error code.
 */
static int gt7288_read_desc(uint16_t register_id, uint8_t *data,
			    size_t max_length)
{
	uint8_t reg_bytes[] = {
		register_id & 0xFF, (register_id & 0xFF00) >> 8
	};
	return i2c_xfer(CONFIG_TOUCHPAD_I2C_PORT, GT7288_SLAVE_ADDRESS,
			reg_bytes, sizeof(reg_bytes), data, max_length);
}

int gt7288_get_version_info(struct gt7288_version_info *info)
{
	uint8_t data[GT7288_HID_DESCRIPTOR_LENGTH];

	RETURN_ERROR(gt7288_read_desc(GT7288_REG_HID_DESCRIPTOR, data,
				      sizeof(data)));
	info->product_id = UINT16_FROM_BYTE_ARRAY_LE(data, 22);
	info->version_id = UINT16_FROM_BYTE_ARRAY_LE(data, 24);
	return EC_SUCCESS;
}

static void gt7288_translate_contact(const uint8_t *data,
				     struct gt7288_contact *contact)
{
	if (IS_ENABLED(DEBUG_CHECKS)) {
		uint8_t report_id = data[2];

		if (report_id != GT7288_REPORT_ID_PTP) {
			CPRINTS("WARNING: unexpected report ID 0x%02X (expected 0x%02X).",
				report_id, GT7288_REPORT_ID_PTP);
		}
	}

	contact->id = data[3] >> 4;
	/* Note: these bits appear to be in the wrong order in the programming
	 * guide, verified by experimentation.
	 */
	contact->tip = (data[3] & BIT(1)) >> 1;
	contact->confidence = data[3] & BIT(0);
	contact->x = UINT16_FROM_BYTE_ARRAY_LE(data, 4);
	contact->y = UINT16_FROM_BYTE_ARRAY_LE(data, 6);
	contact->width = data[12];
	contact->height = data[13];
}

static int gt7288_read(uint8_t *data, size_t max_length)
{
	return i2c_xfer(CONFIG_TOUCHPAD_I2C_PORT, GT7288_SLAVE_ADDRESS,
			NULL, 0, data, max_length);
}

int gt7288_read_ptp_report(struct gt7288_ptp_report *report)
{
	size_t i;
	uint8_t data[GT7288_REPORT_LENGTH];

	RETURN_ERROR(gt7288_read(data, sizeof(data)));
	report->timestamp = UINT16_FROM_BYTE_ARRAY_LE(data, 8);

	if (data[10] > GT7288_MAX_CONTACTS) {
		if (IS_ENABLED(DEBUG_CHECKS))
			CPRINTS("ERROR: too many contacts (%d > %d).",
				data[10], GT7288_MAX_CONTACTS);
		return EC_ERROR_HW_INTERNAL;
	}
	report->num_contacts = data[10];

	if (IS_ENABLED(DEBUG_CHECKS) && data[11] != GT7288_BUTTON_STATE_UP &&
	    data[11] != GT7288_BUTTON_STATE_DOWN) {
		CPRINTS("WARNING: unexpected button state 0x%02X (expected 0x%02X or 0x%02X).",
			data[11], GT7288_BUTTON_STATE_UP,
			GT7288_BUTTON_STATE_DOWN);
	}
	report->button_down = data[11] == GT7288_BUTTON_STATE_DOWN;

	gt7288_translate_contact(data, &report->contacts[0]);

	for (i = 1; i < report->num_contacts; i++) {
		RETURN_ERROR(gt7288_read(data, sizeof(data)));
		gt7288_translate_contact(data, &report->contacts[i]);
	}
	return EC_SUCCESS;
}

#ifdef CONFIG_CMD_GT7288
static int command_gt7288_read_desc(int argc, char **argv)
{
	uint16_t register_id;
	long parsed_arg;
	char *end;
	int i;
	uint8_t data[GT7288_HID_DESCRIPTOR_LENGTH];

	if (argc != 2)
		return EC_ERROR_PARAM_COUNT;

	parsed_arg = strtoi(argv[1], &end, 0);
	if (parsed_arg < 0 || parsed_arg > UINT16_MAX || end == argv[1])
		return EC_ERROR_PARAM1;
	register_id = parsed_arg;

	RETURN_ERROR(gt7288_read_desc(register_id, data, sizeof(data)));

	ccprintf("Data: ");
	for (i = 0; i < sizeof(data); i++)
		ccprintf("%02X ", data[i]);
	ccprintf("\n");
	return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(gt7288_desc, command_gt7288_read_desc,
			"register",
			"Read a descriptor on the GT7288");

static int command_gt7288_read_report_descriptor(int argc, char **argv)
{
	int i;
	uint8_t data[64];
	size_t bytes_read = 0;

	if (argc != 1)
		return EC_ERROR_PARAM_COUNT;

	/* The report descriptor is bigger than the Maxim I2C code can handle in
	 * one go, so we have to split it into chunks.
	 */
	RETURN_ERROR(gt7288_read_desc(GT7288_REG_REPORT_DESCRIPTOR, NULL, 0));
	ccprintf("Report descriptor: ");
	while (bytes_read < GT7288_REPORT_DESCRIPTOR_LENGTH) {
		size_t bytes_to_read =
			MIN(GT7288_REPORT_DESCRIPTOR_LENGTH - bytes_read,
			    sizeof(data));
		RETURN_ERROR(gt7288_read(data, bytes_to_read));

		for (i = 0; i < sizeof(data); i++)
			ccprintf("%02X ", data[i]);

		bytes_read += bytes_to_read;
	}
	ccprintf("\n");
	return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(gt7288_repdesc, command_gt7288_read_report_descriptor,
			"", "Read the report descriptor on the GT7288");

static int command_gt7288_ver(int argc, char **argv)
{
	struct gt7288_version_info info;

	if (argc != 1)
		return EC_ERROR_PARAM_COUNT;

	RETURN_ERROR(gt7288_get_version_info(&info));
	ccprintf("Product ID: 0x%04X\n", info.product_id);
	ccprintf("Version ID: 0x%04X\n", info.version_id);
	return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(gt7288_ver, command_gt7288_ver, "",
			"Read version information from the GT7288");

static int command_gt7288_report(int argc, char **argv)
{
	int i;
	struct gt7288_ptp_report report;

	RETURN_ERROR(gt7288_read_ptp_report(&report));
	ccprintf("Timestamp %d, button %s, %d contacts\n", report.timestamp,
		 report.button_down ? "down" : "up", report.num_contacts);
	if (report.num_contacts == 0)
		return EC_SUCCESS;

	ccprintf("ID,    X,    Y, width, height, tip, confidence\n");
	for (i = 0; i < report.num_contacts; i++) {
		struct gt7288_contact *contact = &report.contacts[i];

		ccprintf("%2d, %4d, %4d, %5d, %6d, %3d, %10d\n", contact->id,
			 contact->x, contact->y, contact->width,
			 contact->height, contact->tip, contact->confidence);
	}

	return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(gt7288_rep, command_gt7288_report, "",
			"Read a report from the GT7288.");
#endif /* CONFIG_CMD_GT7288 */