diff --git a/4.5/wacom_i2c.c b/4.5/wacom_i2c.c index a0ac5bda..10aebd3b 100644 --- a/4.5/wacom_i2c.c +++ b/4.5/wacom_i2c.c @@ -2,8 +2,8 @@ /* * Wacom Penabled Driver for I2C * - * Copyright (c) 2011 - 2013 Tatsunosuke Tobita, Wacom. - * + * Copyright (c) 2011 - 2023 Tatsunosuke Tobita, Wacom. + * */ #include "../config.h" @@ -52,18 +52,44 @@ #define OPCODE_GET_REPORT 0x02 #define WACOM_QUERY_REPORT 3 -#define WACOM_QUERY_SIZE 19 +#define WACOM_QUERY_SIZE 22 + +/* Resolutions */ +#define XY_RESOLUTION 100 /* Distance : SI Linear Unit with exponent -3 */ +#define DIST_RESOLUTION 10 /* Distance : SI Linear Unit with exponent -2. + * This covers 'Z' resolution too */ +#define TILT_RESOLUTION 5730 /* Degrees : English Rotation with exponent -2 */ + +/* Generation selction */ +#define WACOM_BG9 0 /* G9 or earlier neither height nor tilt is supported */ +#define WACOM_AG12 1 /* After G12 the IC supports "height" + * which is "ABS_DISTANCE" event */ +#define MAX_LEN_BG9 10 /* Packet length for G9 or eralier */ +#define MAX_LEN_G12 15 /* Length for G12 */ +#define MAX_LEN_AG14 17 /* Length for G14 or later */ + +#define DISTANCE_MAX 255 + +struct feature_support { + bool distance; + bool tilt; +}; struct wacom_features { + struct feature_support support; int x_max; int y_max; int pressure_max; - char fw_version; + int distance_max; + int tilt_x_max; + int tilt_y_max; char fw_version; + unsigned char generation; }; struct wacom_i2c { struct i2c_client *client; struct input_dev *input; + struct wacom_features features; u8 data[WACOM_QUERY_SIZE]; bool prox; int tool; @@ -107,14 +133,31 @@ static int wacom_query_device(struct i2c_client *client, features->x_max = get_unaligned_le16(&data[3]); features->y_max = get_unaligned_le16(&data[5]); + features->distance_max = data[16]; features->pressure_max = get_unaligned_le16(&data[11]); features->fw_version = get_unaligned_le16(&data[13]); + features->tilt_x_max = get_unaligned_le16(&data[17]); + features->tilt_y_max = get_unaligned_le16(&data[19]); + features->distance_max = data[16]; + + if (features->distance_max) + features->support.distance = true; + + if ((features->tilt_x_max && features->tilt_y_max)) + features->support.tilt = true; dev_dbg(&client->dev, - "x_max:%d, y_max:%d, pressure:%d, fw:%d\n", + "x_max: %d, y_max: %d, pressure: %d, fw: %d, distance: %d," + " tilt_x_max: %d, tilt_y_max: %d \n", features->x_max, features->y_max, - features->pressure_max, features->fw_version); - + features->pressure_max, features->fw_version, + features->distance_max, + features->tilt_x_max, features->tilt_y_max); + + if (!features->support.distance && !features->support.tilt) + features->generation = WACOM_BG9; + else if (features->distance_max == DISTANCE_MAX) + features->generation = WACOM_AG12; return 0; } @@ -122,9 +165,12 @@ static irqreturn_t wacom_i2c_irq(int irq, void *dev_id) { struct wacom_i2c *wac_i2c = dev_id; struct input_dev *input = wac_i2c->input; + struct wacom_features *features = &wac_i2c->features; u8 *data = wac_i2c->data; unsigned int x, y, pressure; unsigned char tsw, f1, f2, ers; + short tilt_x, tilt_y; + short distance = 0; int error; error = i2c_master_recv(wac_i2c->client, @@ -146,6 +192,23 @@ static irqreturn_t wacom_i2c_irq(int irq, void *dev_id) wac_i2c->prox = data[3] & WACOM_IN_PROXIMITY; + if (features->generation) { + /* Tilt (signed) */ + tilt_x = le16_to_cpup((__le16 *)&data[11]); + tilt_y = le16_to_cpup((__le16 *)&data[13]); + input_report_abs(input, ABS_TILT_X, tilt_x); + input_report_abs(input, ABS_TILT_Y, tilt_y); + + /* Hover height */ + if (data[0] == MAX_LEN_G12) { + distance = data[10]; + } else if (data[0] >= MAX_LEN_AG14) { + distance = le16_to_cpup((__le16 *)&data[15]); + distance = -distance; /* The output is negative. Make it positive */ + } + input_report_abs(input, ABS_DISTANCE, distance); + } + input_report_key(input, BTN_TOUCH, tsw || ers); input_report_key(input, wac_i2c->tool, wac_i2c->prox); input_report_key(input, BTN_STYLUS, f1); @@ -187,7 +250,7 @@ static int wacom_i2c_probe(struct i2c_client *client) struct device *dev = &client->dev; struct wacom_i2c *wac_i2c; struct input_dev *input; - struct wacom_features features = { 0 }; + struct wacom_features *features; int error; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { @@ -195,14 +258,15 @@ static int wacom_i2c_probe(struct i2c_client *client) return -EIO; } - error = wacom_query_device(client, &features); - if (error) - return error; - wac_i2c = devm_kzalloc(dev, sizeof(*wac_i2c), GFP_KERNEL); if (!wac_i2c) return -ENOMEM; + features = &wac_i2c->features; + error = wacom_query_device(client, features); + if (error) + return error; + wac_i2c->client = client; input = devm_input_allocate_device(dev); @@ -214,22 +278,40 @@ static int wacom_i2c_probe(struct i2c_client *client) input->name = "Wacom I2C Digitizer"; input->id.bustype = BUS_I2C; input->id.vendor = 0x56a; - input->id.version = features.fw_version; + input->id.version = features->fw_version; + input->dev.parent = &client->dev; input->open = wacom_i2c_open; input->close = wacom_i2c_close; input->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + __set_bit(INPUT_PROP_DIRECT, input->propbit); __set_bit(BTN_TOOL_PEN, input->keybit); __set_bit(BTN_TOOL_RUBBER, input->keybit); __set_bit(BTN_STYLUS, input->keybit); __set_bit(BTN_STYLUS2, input->keybit); __set_bit(BTN_TOUCH, input->keybit); - input_set_abs_params(input, ABS_X, 0, features.x_max, 0, 0); - input_set_abs_params(input, ABS_Y, 0, features.y_max, 0, 0); + input_set_abs_params(input, ABS_X, 0, features->x_max, 0, 0); + input_set_abs_params(input, ABS_Y, 0, features->y_max, 0, 0); input_set_abs_params(input, ABS_PRESSURE, - 0, features.pressure_max, 0, 0); + 0, features->pressure_max, 0, 0); + input_abs_set_res(input, ABS_X, XY_RESOLUTION); + input_abs_set_res(input, ABS_Y, XY_RESOLUTION); + + if (features->generation & 0xff) { /* G12/G14 */ + /* Tilt X & Y property setting */ + input_set_abs_params(input, ABS_TILT_X, -features->tilt_x_max, + features->tilt_x_max, 0, 0); + input_set_abs_params(input, ABS_TILT_Y, -features->tilt_y_max, + features->tilt_y_max, 0, 0); + input_abs_set_res(input, ABS_TILT_X, TILT_RESOLUTION); + input_abs_set_res(input, ABS_TILT_Y, TILT_RESOLUTION); + + /* Distance property setting follows Linux Input subsystem event */ + input_set_abs_params(input, ABS_DISTANCE, 0, features->distance_max, 0, 0); + input_abs_set_res(input, ABS_DISTANCE, DIST_RESOLUTION); + } input_set_drvdata(input, wac_i2c); @@ -278,10 +360,23 @@ static const struct i2c_device_id wacom_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, wacom_i2c_id); +#ifdef CONFIG_OF +static const struct of_device_id wacom_i2c_of_match_table[] = { + { .compatible = "emr,wacom_i2c" }, + {} +}; +MODULE_DEVICE_TABLE(of, wacom_i2c_of_match_table); +#endif + static struct i2c_driver wacom_i2c_driver = { .driver = { .name = "wacom_i2c", .pm = &wacom_i2c_pm, + +#ifdef CONFIG_OF + .of_match_table = of_match_ptr(wacom_i2c_of_match_table), +#endif + }, .probe = wacom_i2c_probe,