ODROID-M1/M1S: Fix to enable multi-touch.

Change-Id: If20f88babbf7b4a457fb39bf5ab98ba5cdbe9456
This commit is contained in:
Chris
2024-04-24 09:13:38 +09:00
parent 9b71948426
commit 671d4d85ec

View File

@@ -120,7 +120,7 @@ struct goodix_input_report {
#define GOODIX_INT_TRIGGER 1
#define GOODIX_CONTACT_SIZE 8
#define GOODIX_MAX_CONTACT_SIZE 9
#define GOODIX_MAX_CONTACTS 10
#define GOODIX_MAX_CONTACTS 11
#define GOODIX_MAX_KEYS 7
#define GOODIX_CONFIG_MIN_LENGTH 186
@@ -177,6 +177,7 @@ struct goodix_chip_id {
struct goodix_ts_data {
struct i2c_client *client;
struct input_dev *input_dev;
struct input_dev *pen_dev;
const struct goodix_chip_data *chip;
struct touchscreen_properties prop;
unsigned int max_touch_num;
@@ -462,6 +463,7 @@ static void goodix_populate_report(struct goodix_ts_data *ts, u8 *data, struct g
}
/* End: Adding this function to work with the goodix_input_report struct (Adya) */
static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data)
{
unsigned long max_timeout;
@@ -519,32 +521,57 @@ static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data)
return -ENOMSG;
}
static void goodix_ts_report_touch_8b(struct goodix_ts_data *ts, u8 *coor_data,
struct goodix_input_report *report)
/**
* Modified version of the original goodix_ts_report_touch in order to make
* use of the goodix_input_report struct and to report the inputs to the
* correct logical devices.
* (as we have two of these: one for the touchscreen and one for the pen)
*/
static void goodix_ts_report_mt_slots(struct goodix_ts_data *ts,
struct goodix_input_report *report)
{
int id = coor_data[0] & 0x0F;
int i;
struct goodix_point *point = report->points;
u16 cur_touch = 0;
static u16 prev_touch;
input_mt_slot(ts->input_dev, id);
input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true);
touchscreen_report_pos(ts->input_dev, &ts->prop,
point->x, point->y, true);
input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, point->w);
input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, point->w);
}
for (i = 0; i < GOODIX_MAX_CONTACTS; i++) {
if (report->touch_num && i == point->id) {
if (point->tool_type == GOODIX_TOOL_PEN) {
input_mt_slot(ts->pen_dev, point->id);
input_mt_report_slot_state(ts->pen_dev, MT_TOOL_PEN, true);
touchscreen_report_pos(ts->pen_dev, &ts->prop,
point->x, point->y, true);
input_report_abs(ts->pen_dev, ABS_MT_TOUCH_MAJOR, point->w);
input_report_abs(ts->pen_dev, ABS_MT_PRESSURE, point->w);
} else {
input_mt_slot(ts->input_dev, point->id);
input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true);
touchscreen_report_pos(ts->input_dev, &ts->prop,
point->x, point->y, true);
input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, point->w);
input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, point->w);
}
static void goodix_ts_report_touch_9b(struct goodix_ts_data *ts, u8 *coor_data,
struct goodix_input_report *report)
{
int id = coor_data[1] & 0x0F;
struct goodix_point *point = report->points;
cur_touch |= (0b1 << point->id);
point++;
}
else if (prev_touch & (0b1 << i)) {
if (i == GOODIX_STYLUS_POINT_ID) {
input_mt_slot(ts->pen_dev, i);
input_mt_report_slot_state(ts->pen_dev, MT_TOOL_PEN, false);
} else {
input_mt_slot(ts->input_dev, i);
input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, false);
}
}
}
input_mt_slot(ts->input_dev, id);
input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true);
touchscreen_report_pos(ts->input_dev, &ts->prop,
point->x, point->y, true);
input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR, point->w);
input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, point->w);
input_mt_sync_frame(ts->pen_dev);
input_sync(ts->pen_dev);
input_mt_sync_frame(ts->input_dev);
input_sync(ts->input_dev);
prev_touch = cur_touch;
}
static void goodix_ts_report_key(struct goodix_ts_data *ts, u8 *data)
@@ -581,7 +608,7 @@ static void goodix_process_events(struct goodix_ts_data *ts)
struct goodix_input_report report = { .points = points };
int touch_num;
int i;
static u8 prev_keys = 0;
touch_num = goodix_ts_read_input_report(ts, point_data);
if (touch_num < 0)
@@ -591,16 +618,21 @@ static void goodix_process_events(struct goodix_ts_data *ts)
goodix_ts_report_key(ts, point_data);
for (i = 0; i < touch_num; i++)
if (ts->contact_size == 9)
goodix_ts_report_touch_9b(ts,
&point_data[1 + ts->contact_size * i], &report);
else
goodix_ts_report_touch_8b(ts,
&point_data[1 + ts->contact_size * i], &report);
/*
* Note: Stylus buttons' state is only reported when the tip of the pen
* is in contact with the touchscreen frame. I didn't notice this until
* now and find it quite weird. Is it supposed to work this way ?
* (Apparently it does not work like that on every Goodix device)
*/
if (GOODIX_KEYDOWN_EVENT(report.keys) || GOODIX_KEYDOWN_EVENT(prev_keys)) {
input_report_key(ts->pen_dev, BTN_STYLUS,
GOODIX_IS_STYLUS_BTN_DOWN(report.keys, GOODIX_STYLUS_BTN1));
input_report_key(ts->pen_dev, BTN_STYLUS2,
GOODIX_IS_STYLUS_BTN_DOWN(report.keys, GOODIX_STYLUS_BTN2));
prev_keys = report.keys;
}
input_mt_sync_frame(ts->input_dev);
input_sync(ts->input_dev);
goodix_ts_report_mt_slots(ts, &report);
}
/**
@@ -1103,7 +1135,8 @@ retry_get_irq_gpio:
*
* Must be called during probe
*/
static void goodix_read_config(struct goodix_ts_data *ts)
static void goodix_read_config(struct goodix_ts_data *ts,
struct input_dev *dev)
{
int x_max, y_max;
int error;
@@ -1124,8 +1157,8 @@ static void goodix_read_config(struct goodix_ts_data *ts)
x_max = get_unaligned_le16(&ts->config[RESOLUTION_LOC]);
y_max = get_unaligned_le16(&ts->config[RESOLUTION_LOC + 2]);
if (x_max && y_max) {
input_abs_set_max(ts->input_dev, ABS_MT_POSITION_X, x_max - 1);
input_abs_set_max(ts->input_dev, ABS_MT_POSITION_Y, y_max - 1);
input_abs_set_max(dev, ABS_MT_POSITION_X, x_max - 1);
input_abs_set_max(dev, ABS_MT_POSITION_Y, y_max - 1);
}
ts->chip->calc_config_checksum(ts);
@@ -1256,6 +1289,9 @@ static int goodix_configure_dev(struct goodix_ts_data *ts)
ts->input_dev->keycodesize = sizeof(ts->keymap[0]);
ts->input_dev->keycodemax = GOODIX_MAX_KEYS;
ts->input_dev->evbit[0] |= BIT_MASK(EV_SYN)
| BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
/* Capacitive Windows/Home button on some devices */
for (i = 0; i < GOODIX_MAX_KEYS; ++i) {
if (i == 0)
@@ -1273,7 +1309,7 @@ static int goodix_configure_dev(struct goodix_ts_data *ts)
retry_read_config:
/* Read configuration and apply touchscreen parameters */
goodix_read_config(ts);
goodix_read_config(ts, ts->input_dev);
/* Try overriding touchscreen parameters via device properties */
touchscreen_parse_properties(ts->input_dev, true, &ts->prop);
@@ -1281,7 +1317,10 @@ retry_read_config:
if (!ts->prop.max_x || !ts->prop.max_y || !ts->max_touch_num) {
if (!ts->reset_controller_at_probe &&
ts->irq_pin_access_method != IRQ_PIN_ACCESS_NONE) {
dev_info(&ts->client->dev, "Config not set, resetting controller\n");
//dev_info(&ts->client->dev, "Config not set, resetting controller\n");
dev_err(&ts->client->dev,
"Invalid config (%d, %d, %d), using defaults\n",
ts->prop.max_x, ts->prop.max_y, ts->max_touch_num);
/* Retry after a controller reset */
ts->reset_controller_at_probe = true;
error = goodix_reset(ts);
@@ -1341,6 +1380,99 @@ retry_read_config:
return 0;
}
/**
* Adding this function to add a new logical device that will be solely
* focused on handling inputs that are recognized by the controller
* as being originated by an active pen/stylus.
*/
static int goodix_configure_pen_dev(struct goodix_ts_data *ts)
{
int error;
ts->pen_dev = devm_input_allocate_device(&ts->client->dev);
if (!ts->pen_dev) {
dev_err(&ts->client->dev, "Failed to allocate pen device.");
return -ENOMEM;
}
ts->pen_dev->name = "Goodix Active Stylus Pen";
ts->pen_dev->phys = "input/pen";
ts->pen_dev->id.bustype = BUS_I2C;
ts->pen_dev->id.vendor = 0x0416;
if (kstrtou16(ts->id, 10, &ts->pen_dev->id.product))
ts->pen_dev->id.product = 0x1001;
ts->pen_dev->id.version = ts->version;
ts->pen_dev->evbit[0] |= BIT_MASK(EV_SYN)
| BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
__set_bit(BTN_STYLUS, ts->pen_dev->keybit);
__set_bit(BTN_STYLUS2, ts->pen_dev->keybit);
/* Capacitive Windows/Home button on some devices */
input_set_capability(ts->pen_dev, EV_KEY, KEY_LEFTMETA);
input_set_capability(ts->pen_dev, EV_KEY, BTN_STYLUS);
input_set_capability(ts->pen_dev, EV_KEY, BTN_STYLUS2);
input_set_capability(ts->pen_dev, EV_ABS, ABS_MT_POSITION_X);
input_set_capability(ts->pen_dev, EV_ABS, ABS_MT_POSITION_Y);
input_set_abs_params(ts->pen_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0);
input_set_abs_params(ts->pen_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
input_set_abs_params(ts->pen_dev, ABS_MT_PRESSURE, 0, 1024, 0, 0);
input_set_abs_params(ts->pen_dev, ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0);
goodix_read_config(ts, ts->pen_dev);
/* Try overriding touchscreen parameters via device properties */
touchscreen_parse_properties(ts->pen_dev, true, &ts->prop);
if (!ts->prop.max_x || !ts->prop.max_y || !ts->max_touch_num) {
dev_err(&ts->client->dev,
"Invalid config (%d, %d, %d), using defaults\n",
ts->prop.max_x, ts->prop.max_y, ts->max_touch_num);
ts->prop.max_x = GOODIX_MAX_WIDTH - 1;
ts->prop.max_y = GOODIX_MAX_HEIGHT - 1;
ts->max_touch_num = GOODIX_MAX_CONTACTS;
input_abs_set_max(ts->pen_dev,
ABS_MT_POSITION_X, ts->prop.max_x);
input_abs_set_max(ts->pen_dev,
ABS_MT_POSITION_Y, ts->prop.max_y);
}
if (dmi_check_system(nine_bytes_report)) {
ts->contact_size = 9;
dev_dbg(&ts->client->dev,
"Non-standard 9-bytes report format quirk\n");
}
if (dmi_check_system(inverted_x_screen)) {
ts->prop.invert_x = true;
dev_dbg(&ts->client->dev,
"Applying 'inverted x screen' quirk\n");
}
goodix_apply_corrections(ts, ts->pen_dev);
error = input_mt_init_slots(ts->pen_dev, ts->max_touch_num,
INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
if (error) {
dev_err(&ts->client->dev,
"Failed to initialize MT slots: %d", error);
return error;
}
error = input_register_device(ts->pen_dev);
if (error) {
dev_err(&ts->client->dev,
"Failed to register pen device: %d", error);
return error;
}
return 0;
}
/**
* goodix_config_cb - Callback to finish device init
*
@@ -1362,6 +1494,7 @@ static void goodix_config_cb(const struct firmware *cfg, void *ctx)
}
goodix_configure_dev(ts);
goodix_configure_pen_dev(ts);
err_release_cfg:
release_firmware(cfg);
@@ -1484,6 +1617,11 @@ reset:
error = goodix_configure_dev(ts);
if (error)
return error;
error = goodix_configure_pen_dev(ts);
if (error)
return error;
}
return 0;