// SPDX-License-Identifier: GPL-2.0-or-later /* * Goodix "Berlin" Touchscreen IC driver * Copyright (C) 2020 - 2021 Goodix, Inc. * Copyright (C) 2023 Linaro Ltd. * * Based on goodix_ts_berlin driver. * * This driver is distinct from goodix.c since hardware interface * is different enough to require a new driver. * None of the register address or data structure are close enough * to the previous generations. * * Currently the driver only handles Multitouch events with already * programmed firmware and "config" for "Revision A/D" Berlin IC. * * Support is missing for: * - ESD Management * - Firmware update/flashing * - "Config" update/flashing * - Stylus Events * - Gesture Events * - Support for revision B
*/
/* Runtime parameters extracted from IC_INFO buffer */
u32 touch_data_addr;
conststruct goodix_berlin_ic_data *ic_data;
struct goodix_berlin_event event;
};
staticbool goodix_berlin_checksum_valid(const u8 *data, int size)
{
u32 cal_checksum = 0;
u16 r_checksum; int i;
if (size < GOODIX_BERLIN_CHECKSUM_SIZE) returnfalse;
for (i = 0; i < size - GOODIX_BERLIN_CHECKSUM_SIZE; i++)
cal_checksum += data[i];
r_checksum = get_unaligned_le16(&data[i]);
return (u16)cal_checksum == r_checksum;
}
staticbool goodix_berlin_is_dummy_data(struct goodix_berlin_core *cd, const u8 *data, int size)
{ int i;
/* * If the device is missing or doesn't respond the buffer * could be filled with bus default line state, 0x00 or 0xff, * so declare success the first time we encounter neither.
*/ for (i = 0; i < size; i++) if (data[i] > 0 && data[i] < 0xff) returnfalse;
returntrue;
}
staticint goodix_berlin_dev_confirm(struct goodix_berlin_core *cd)
{
u8 tx_buf[8], rx_buf[8]; int retry = 3; int error;
memset(tx_buf, GOODIX_BERLIN_DEV_CONFIRM_VAL, sizeof(tx_buf)); while (retry--) {
error = regmap_raw_write(cd->regmap,
GOODIX_BERLIN_BOOTOPTION_ADDR,
tx_buf, sizeof(tx_buf)); if (error) return error;
error = regmap_raw_read(cd->regmap,
GOODIX_BERLIN_BOOTOPTION_ADDR,
rx_buf, sizeof(rx_buf)); if (error) return error;
if (!memcmp(tx_buf, rx_buf, sizeof(tx_buf))) return 0;
afe_data = kzalloc(GOODIX_BERLIN_IC_INFO_MAX_LEN, GFP_KERNEL); if (!afe_data) return -ENOMEM;
error = regmap_raw_read(cd->regmap, cd->ic_data->ic_info_addr,
&length_raw, sizeof(length_raw)); if (error) {
dev_err(cd->dev, "failed get ic info length, %d\n", error); return error;
}
length = le16_to_cpu(length_raw); if (length >= GOODIX_BERLIN_IC_INFO_MAX_LEN) {
dev_err(cd->dev, "invalid ic info length %d\n", length); return -EINVAL;
}
error = regmap_raw_read(cd->regmap, cd->ic_data->ic_info_addr, afe_data,
length); if (error) {
dev_err(cd->dev, "failed get ic info data, %d\n", error); return error;
}
/* check whether the data is valid (ex. bus default values) */ if (goodix_berlin_is_dummy_data(cd, afe_data, length)) {
dev_err(cd->dev, "fw info data invalid\n"); return -EINVAL;
}
if (!goodix_berlin_checksum_valid(afe_data, length)) {
dev_err(cd->dev, "fw info checksum error\n"); return -EINVAL;
}
error = goodix_berlin_parse_ic_info(cd, afe_data, length); if (error) return error;
/* check some key info */ if (!cd->touch_data_addr) {
dev_err(cd->dev, "touch_data_addr is null\n"); return -EINVAL;
}
type = FIELD_GET(GOODIX_BERLIN_POINT_TYPE_MASK, t->status); if (type == GOODIX_BERLIN_POINT_TYPE_STYLUS ||
type == GOODIX_BERLIN_POINT_TYPE_STYLUS_HOVER) {
dev_warn_once(cd->dev, "Stylus event type not handled\n"); continue;
}
id = FIELD_GET(GOODIX_BERLIN_TOUCH_ID_MASK, t->status); if (id >= GOODIX_BERLIN_MAX_TOUCH) {
dev_warn_ratelimited(cd->dev, "invalid finger id %d\n", id); continue;
}
staticvoid goodix_berlin_touch_handler(struct goodix_berlin_core *cd)
{
u8 touch_num; int error;
touch_num = FIELD_GET(GOODIX_BERLIN_TOUCH_COUNT_MASK,
cd->event.hdr.request_type); if (touch_num > GOODIX_BERLIN_MAX_TOUCH) {
dev_warn(cd->dev, "invalid touch num %d\n", touch_num); return;
}
if (touch_num > 2) { /* read additional contact data if more than 2 touch events */
error = goodix_berlin_get_remaining_contacts(cd, touch_num); if (error) return;
}
if (touch_num) { int len = touch_num * GOODIX_BERLIN_TOUCH_SIZE +
GOODIX_BERLIN_CHECKSUM_SIZE; if (!goodix_berlin_checksum_valid(cd->event.data, len)) {
dev_err(cd->dev, "touch data checksum error: %*ph\n",
len, cd->event.data); return;
}
}
/* * First, read buffer with space for 2 touch events: * - GOODIX_BERLIN_HEADER_SIZE = 8 bytes * - GOODIX_BERLIN_TOUCH_SIZE * 2 = 16 bytes * - GOODIX_BERLIN_CHECKLSUM_SIZE = 2 bytes * For a total of 26 bytes. * * If only a single finger is reported, we will read 8 bytes more than * needed: * - bytes 0-7: Header (GOODIX_BERLIN_HEADER_SIZE) * - bytes 8-15: Finger 0 Data * - bytes 24-25: Checksum * - bytes 18-25: Unused 8 bytes * * If 2 fingers are reported, we would have read the exact needed * amount of data and checksum would be at the end of the buffer: * - bytes 0-7: Header (GOODIX_BERLIN_HEADER_SIZE) * - bytes 8-15: Finger 0 Bytes 0-7 * - bytes 16-23: Finger 1 Bytes 0-7 * - bytes 24-25: Checksum * * If more than 2 fingers were reported, the "Checksum" bytes would * in fact contain part of the next finger data, and then * goodix_berlin_get_remaining_contacts() would complete the buffer * with the missing bytes, including the trailing checksum. * For example, if 3 fingers are reported, then we would do: * Read 1: * - bytes 0-7: Header (GOODIX_BERLIN_HEADER_SIZE) * - bytes 8-15: Finger 0 Bytes 0-7 * - bytes 16-23: Finger 1 Bytes 0-7 * - bytes 24-25: Finger 2 Bytes 0-1 * Read 2 (with length of (3 - 2) * 8 = 8 bytes): * - bytes 26-31: Finger 2 Bytes 2-7 * - bytes 32-33: Checksum
*/
error = regmap_raw_read(cd->regmap, cd->touch_data_addr,
&cd->event,
GOODIX_BERLIN_HEADER_SIZE +
2 * GOODIX_BERLIN_TOUCH_SIZE +
GOODIX_BERLIN_CHECKSUM_SIZE); if (error) {
dev_warn_ratelimited(cd->dev, "failed get event head data: %d\n", error); goto out;
}
if (cd->event.hdr.status == 0) goto out;
if (!goodix_berlin_checksum_valid((u8 *)&cd->event.hdr,
GOODIX_BERLIN_HEADER_SIZE)) {
dev_warn_ratelimited(cd->dev, "touch head checksum error: %*ph\n",
(int)GOODIX_BERLIN_HEADER_SIZE,
&cd->event.hdr); goto out_clear;
}
if (cd->event.hdr.status & GOODIX_BERLIN_TOUCH_EVENT)
goodix_berlin_touch_handler(cd);
if (cd->event.hdr.status & GOODIX_BERLIN_REQUEST_EVENT) { switch (cd->event.hdr.request_type) { case GOODIX_BERLIN_REQUEST_CODE_RESET: if (cd->reset_gpio)
goodix_berlin_request_handle_reset(cd); break;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.