Input: goodix - add pen support
Some Goodix touchscreens have support for a (Goodix) active pen, add support for this. The info on how to detect when a pen is down and to detect when the stylus buttons are pressed was lifted from the out of tree Goodix driver with pen support written by Adya: https://gitlab.com/AdyaAdya/goodix-touchscreen-linux-driver/ Since there is no way to tell if pen support is present, the registering of the pen input_dev is delayed till the first pen event is detected. This has been tested on a Trekstor Surftab duo W1, a Chuwi Hi13 and a Cyberbook T116 tablet. BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=202161 BugLink: https://bugzilla.kernel.org/show_bug.cgi?id=204513 Signed-off-by: Hans de Goede <hdegoede@redhat.com> Link: https://lore.kernel.org/r/20211207100754.31155-3-hdegoede@redhat.com Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
This commit is contained in:
committed by
Dmitry Torokhov
parent
5d8dfaa71d
commit
5ede7f0cfb
@@ -296,6 +296,107 @@ static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data)
|
|||||||
return -ENOMSG;
|
return -ENOMSG;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct input_dev *goodix_create_pen_input(struct goodix_ts_data *ts)
|
||||||
|
{
|
||||||
|
struct device *dev = &ts->client->dev;
|
||||||
|
struct input_dev *input;
|
||||||
|
|
||||||
|
input = devm_input_allocate_device(dev);
|
||||||
|
if (!input)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
input_alloc_absinfo(input);
|
||||||
|
if (!input->absinfo) {
|
||||||
|
input_free_device(input);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
input->absinfo[ABS_X] = ts->input_dev->absinfo[ABS_MT_POSITION_X];
|
||||||
|
input->absinfo[ABS_Y] = ts->input_dev->absinfo[ABS_MT_POSITION_Y];
|
||||||
|
__set_bit(ABS_X, input->absbit);
|
||||||
|
__set_bit(ABS_Y, input->absbit);
|
||||||
|
input_set_abs_params(input, ABS_PRESSURE, 0, 255, 0, 0);
|
||||||
|
|
||||||
|
input_set_capability(input, EV_KEY, BTN_TOUCH);
|
||||||
|
input_set_capability(input, EV_KEY, BTN_TOOL_PEN);
|
||||||
|
input_set_capability(input, EV_KEY, BTN_STYLUS);
|
||||||
|
input_set_capability(input, EV_KEY, BTN_STYLUS2);
|
||||||
|
__set_bit(INPUT_PROP_DIRECT, input->propbit);
|
||||||
|
/*
|
||||||
|
* The resolution of these touchscreens is about 10 units/mm, the actual
|
||||||
|
* resolution does not matter much since we set INPUT_PROP_DIRECT.
|
||||||
|
* Userspace wants something here though, so just set it to 10 units/mm.
|
||||||
|
*/
|
||||||
|
input_abs_set_res(input, ABS_X, 10);
|
||||||
|
input_abs_set_res(input, ABS_Y, 10);
|
||||||
|
|
||||||
|
input->name = "Goodix Active Pen";
|
||||||
|
input->phys = "input/pen";
|
||||||
|
input->id.bustype = BUS_I2C;
|
||||||
|
if (kstrtou16(ts->id, 10, &input->id.product))
|
||||||
|
input->id.product = 0x1001;
|
||||||
|
input->id.version = ts->version;
|
||||||
|
|
||||||
|
if (input_register_device(input) != 0) {
|
||||||
|
input_free_device(input);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void goodix_ts_report_pen_down(struct goodix_ts_data *ts, u8 *data)
|
||||||
|
{
|
||||||
|
int input_x, input_y, input_w;
|
||||||
|
u8 key_value;
|
||||||
|
|
||||||
|
if (!ts->input_pen) {
|
||||||
|
ts->input_pen = goodix_create_pen_input(ts);
|
||||||
|
if (!ts->input_pen)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ts->contact_size == 9) {
|
||||||
|
input_x = get_unaligned_le16(&data[4]);
|
||||||
|
input_y = get_unaligned_le16(&data[6]);
|
||||||
|
input_w = get_unaligned_le16(&data[8]);
|
||||||
|
} else {
|
||||||
|
input_x = get_unaligned_le16(&data[2]);
|
||||||
|
input_y = get_unaligned_le16(&data[4]);
|
||||||
|
input_w = get_unaligned_le16(&data[6]);
|
||||||
|
}
|
||||||
|
|
||||||
|
touchscreen_report_pos(ts->input_pen, &ts->prop, input_x, input_y, false);
|
||||||
|
input_report_abs(ts->input_pen, ABS_PRESSURE, input_w);
|
||||||
|
|
||||||
|
input_report_key(ts->input_pen, BTN_TOUCH, 1);
|
||||||
|
input_report_key(ts->input_pen, BTN_TOOL_PEN, 1);
|
||||||
|
|
||||||
|
if (data[0] & GOODIX_HAVE_KEY) {
|
||||||
|
key_value = data[1 + ts->contact_size];
|
||||||
|
input_report_key(ts->input_pen, BTN_STYLUS, key_value & 0x10);
|
||||||
|
input_report_key(ts->input_pen, BTN_STYLUS2, key_value & 0x20);
|
||||||
|
} else {
|
||||||
|
input_report_key(ts->input_pen, BTN_STYLUS, 0);
|
||||||
|
input_report_key(ts->input_pen, BTN_STYLUS2, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
input_sync(ts->input_pen);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void goodix_ts_report_pen_up(struct goodix_ts_data *ts)
|
||||||
|
{
|
||||||
|
if (!ts->input_pen)
|
||||||
|
return;
|
||||||
|
|
||||||
|
input_report_key(ts->input_pen, BTN_TOUCH, 0);
|
||||||
|
input_report_key(ts->input_pen, BTN_TOOL_PEN, 0);
|
||||||
|
input_report_key(ts->input_pen, BTN_STYLUS, 0);
|
||||||
|
input_report_key(ts->input_pen, BTN_STYLUS2, 0);
|
||||||
|
|
||||||
|
input_sync(ts->input_pen);
|
||||||
|
}
|
||||||
|
|
||||||
static void goodix_ts_report_touch_8b(struct goodix_ts_data *ts, u8 *coor_data)
|
static void goodix_ts_report_touch_8b(struct goodix_ts_data *ts, u8 *coor_data)
|
||||||
{
|
{
|
||||||
int id = coor_data[0] & 0x0F;
|
int id = coor_data[0] & 0x0F;
|
||||||
@@ -326,6 +427,14 @@ static void goodix_ts_report_touch_9b(struct goodix_ts_data *ts, u8 *coor_data)
|
|||||||
input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, input_w);
|
input_report_abs(ts->input_dev, ABS_MT_WIDTH_MAJOR, input_w);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void goodix_ts_release_keys(struct goodix_ts_data *ts)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < GOODIX_MAX_KEYS; i++)
|
||||||
|
input_report_key(ts->input_dev, ts->keymap[i], 0);
|
||||||
|
}
|
||||||
|
|
||||||
static void goodix_ts_report_key(struct goodix_ts_data *ts, u8 *data)
|
static void goodix_ts_report_key(struct goodix_ts_data *ts, u8 *data)
|
||||||
{
|
{
|
||||||
int touch_num;
|
int touch_num;
|
||||||
@@ -340,8 +449,7 @@ static void goodix_ts_report_key(struct goodix_ts_data *ts, u8 *data)
|
|||||||
input_report_key(ts->input_dev,
|
input_report_key(ts->input_dev,
|
||||||
ts->keymap[i], 1);
|
ts->keymap[i], 1);
|
||||||
} else {
|
} else {
|
||||||
for (i = 0; i < GOODIX_MAX_KEYS; i++)
|
goodix_ts_release_keys(ts);
|
||||||
input_report_key(ts->input_dev, ts->keymap[i], 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -363,6 +471,15 @@ static void goodix_process_events(struct goodix_ts_data *ts)
|
|||||||
if (touch_num < 0)
|
if (touch_num < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
/* The pen being down is always reported as a single touch */
|
||||||
|
if (touch_num == 1 && (point_data[1] & 0x80)) {
|
||||||
|
goodix_ts_report_pen_down(ts, point_data);
|
||||||
|
goodix_ts_release_keys(ts);
|
||||||
|
goto sync; /* Release any previousle registered touches */
|
||||||
|
} else {
|
||||||
|
goodix_ts_report_pen_up(ts);
|
||||||
|
}
|
||||||
|
|
||||||
goodix_ts_report_key(ts, point_data);
|
goodix_ts_report_key(ts, point_data);
|
||||||
|
|
||||||
for (i = 0; i < touch_num; i++)
|
for (i = 0; i < touch_num; i++)
|
||||||
@@ -373,6 +490,7 @@ static void goodix_process_events(struct goodix_ts_data *ts)
|
|||||||
goodix_ts_report_touch_8b(ts,
|
goodix_ts_report_touch_8b(ts,
|
||||||
&point_data[1 + ts->contact_size * i]);
|
&point_data[1 + ts->contact_size * i]);
|
||||||
|
|
||||||
|
sync:
|
||||||
input_mt_sync_frame(ts->input_dev);
|
input_mt_sync_frame(ts->input_dev);
|
||||||
input_sync(ts->input_dev);
|
input_sync(ts->input_dev);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ struct goodix_chip_data {
|
|||||||
struct goodix_ts_data {
|
struct goodix_ts_data {
|
||||||
struct i2c_client *client;
|
struct i2c_client *client;
|
||||||
struct input_dev *input_dev;
|
struct input_dev *input_dev;
|
||||||
|
struct input_dev *input_pen;
|
||||||
const struct goodix_chip_data *chip;
|
const struct goodix_chip_data *chip;
|
||||||
const char *firmware_name;
|
const char *firmware_name;
|
||||||
struct touchscreen_properties prop;
|
struct touchscreen_properties prop;
|
||||||
|
|||||||
Reference in New Issue
Block a user