diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2022-01-17 09:27:55 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2022-01-17 09:27:55 +0300 |
commit | b520085ca57982c4beeb9bb64b8f6018425cb61f (patch) | |
tree | 23ded815f2463d84203523802f53eb6706605a1d | |
parent | 4b3789512f018819e0c4b0776731dc4ce694c484 (diff) | |
parent | 762f99f4f3cb41a775b5157dd761217beba65873 (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
Pull input updates from Dmitry Torokhov:
"Updates to Goodix touchscreen driver (addition of pen support) and
Silead touchscreen driver (also addition of pen support and parsing of
embedded firmware to determine screen size), along with assorted fixes
for other drivers"
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input:
Input: ti_am335x_tsc - fix a typo in a comment
Input: zinitix - add compatible for bt532
Input: zinitix - handle proper supply names
dt-bindings: input/ts/zinitix: Convert to YAML, fix and extend
Input: axp20x-pek - revert "always register interrupt handlers" change
Input: gpio-keys - avoid clearing twice some memory
Input: byd - fix typo in a comment
Input: ucb1400_ts - remove redundant variable penup
Input: ti_am335x_tsc - lower the X and Y sampling time
Input: ti_am335x_tsc - fix STEPCONFIG setup for Z2
Input: ti_am335x_tsc - set ADCREFM for X configuration
Input: silead - add pen support
Input: silead - add support for EFI-embedded fw using different min/max coordinates
Input: goodix - 2 small fixes for pen support
Input: goodix - improve gpiod_get() error logging
Input: goodix - add pen support
Input: ff-core - correct magnitude setting for rumble compatibility
Input: palmas-pwrbutton - make a couple of arrays static const
Input: wacom_i2c - clean up the query device fields
Input: palmas-pwrbutton - use bitfield helpers
-rw-r--r-- | Documentation/devicetree/bindings/input/touchscreen/zinitix,bt400.yaml | 115 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/input/touchscreen/zinitix.txt | 40 | ||||
-rw-r--r-- | drivers/input/ff-core.c | 2 | ||||
-rw-r--r-- | drivers/input/keyboard/gpio_keys.c | 2 | ||||
-rw-r--r-- | drivers/input/misc/axp20x-pek.c | 72 | ||||
-rw-r--r-- | drivers/input/misc/palmas-pwrbutton.c | 9 | ||||
-rw-r--r-- | drivers/input/mouse/byd.c | 2 | ||||
-rw-r--r-- | drivers/input/touchscreen/goodix.c | 127 | ||||
-rw-r--r-- | drivers/input/touchscreen/goodix.h | 1 | ||||
-rw-r--r-- | drivers/input/touchscreen/silead.c | 172 | ||||
-rw-r--r-- | drivers/input/touchscreen/ti_am335x_tsc.c | 20 | ||||
-rw-r--r-- | drivers/input/touchscreen/ucb1400_ts.c | 4 | ||||
-rw-r--r-- | drivers/input/touchscreen/wacom_i2c.c | 44 | ||||
-rw-r--r-- | drivers/input/touchscreen/zinitix.c | 22 |
14 files changed, 507 insertions, 125 deletions
diff --git a/Documentation/devicetree/bindings/input/touchscreen/zinitix,bt400.yaml b/Documentation/devicetree/bindings/input/touchscreen/zinitix,bt400.yaml new file mode 100644 index 000000000000..b4e5ba7c0b49 --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/zinitix,bt400.yaml @@ -0,0 +1,115 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/input/touchscreen/zinitix,bt400.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Zinitix BT4xx and BT5xx series touchscreen controller bindings + +description: The Zinitix BT4xx and BT5xx series of touchscreen controllers + are Korea-produced touchscreens with embedded microcontrollers. The + BT4xx series was produced 2010-2013 and the BT5xx series 2013-2014. + +maintainers: + - Michael Srba <Michael.Srba@seznam.cz> + - Linus Walleij <linus.walleij@linaro.org> + +allOf: + - $ref: touchscreen.yaml# + +properties: + $nodename: + pattern: "^touchscreen(@.*)?$" + + compatible: + enum: + - zinitix,bt402 + - zinitix,bt403 + - zinitix,bt404 + - zinitix,bt412 + - zinitix,bt413 + - zinitix,bt431 + - zinitix,bt432 + - zinitix,bt531 + - zinitix,bt532 + - zinitix,bt538 + - zinitix,bt541 + - zinitix,bt548 + - zinitix,bt554 + - zinitix,at100 + + reg: + description: I2C address on the I2C bus + + clock-frequency: + description: I2C client clock frequency, defined for host when using + the device on the I2C bus + minimum: 0 + maximum: 400000 + + interrupts: + description: Interrupt to host + maxItems: 1 + + vcca-supply: + description: Analog power supply regulator on the VCCA pin + + vdd-supply: + description: Digital power supply regulator on the VDD pin. + In older device trees this can be the accidental name for the analog + supply on the VCCA pin, and in that case the deprecated vddo-supply is + used for the digital power supply. + + vddo-supply: + description: Deprecated name for the digital power supply, use vdd-supply + as this reflects the real name of the pin. If this supply is present, + the vdd-supply represents VCCA instead of VDD. Implementers should first + check for this property, and if it is present assume that the vdd-supply + represents the analog supply. + deprecated: true + + reset-gpios: + description: Reset line for the touchscreen, should be tagged + as GPIO_ACTIVE_LOW + + zinitix,mode: + description: Mode of reporting touch points. Some modes may not work + with a particular ts firmware for unknown reasons. Available modes are + 1 and 2. Mode 2 is the default and preferred. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [1, 2] + + touchscreen-size-x: true + touchscreen-size-y: true + touchscreen-fuzz-x: true + touchscreen-fuzz-y: true + +additionalProperties: false + +required: + - compatible + - reg + - interrupts + - touchscreen-size-x + - touchscreen-size-y + +examples: + - | + #include <dt-bindings/interrupt-controller/irq.h> + #include <dt-bindings/gpio/gpio.h> + i2c { + #address-cells = <1>; + #size-cells = <0>; + + touchscreen@20 { + compatible = "zinitix,bt541"; + reg = <0x20>; + interrupt-parent = <&gpio>; + interrupts = <13 IRQ_TYPE_EDGE_FALLING>; + vcca-supply = <®_vcca_tsp>; + vdd-supply = <®_vdd_tsp>; + touchscreen-size-x = <540>; + touchscreen-size-y = <960>; + zinitix,mode = <2>; + }; + }; diff --git a/Documentation/devicetree/bindings/input/touchscreen/zinitix.txt b/Documentation/devicetree/bindings/input/touchscreen/zinitix.txt deleted file mode 100644 index 446efb9f5f55..000000000000 --- a/Documentation/devicetree/bindings/input/touchscreen/zinitix.txt +++ /dev/null @@ -1,40 +0,0 @@ -Device tree bindings for Zinitx BT541 touchscreen controller - -Required properties: - - - compatible : Should be "zinitix,bt541" - - reg : I2C address of the chip. Should be 0x20 - - interrupts : Interrupt to which the chip is connected - -Optional properties: - - - vdd-supply : Analog power supply regulator on VCCA pin - - vddo-supply : Digital power supply regulator on VDD pin - - zinitix,mode : Mode of reporting touch points. Some modes may not work - with a particular ts firmware for unknown reasons. Available - modes are 1 and 2. Mode 2 is the default and preferred. - -The touchscreen-* properties are documented in touchscreen.txt in this -directory. - -Example: - - i2c@00000000 { - /* ... */ - - bt541@20 { - compatible = "zinitix,bt541"; - reg = <0x20>; - interrupt-parent = <&msmgpio>; - interrupts = <13 IRQ_TYPE_EDGE_FALLING>; - pinctrl-names = "default"; - pinctrl-0 = <&tsp_default>; - vdd-supply = <®_vdd_tsp>; - vddo-supply = <&pm8916_l6>; - touchscreen-size-x = <540>; - touchscreen-size-y = <960>; - zinitix,mode = <2>; - }; - - /* ... */ - }; diff --git a/drivers/input/ff-core.c b/drivers/input/ff-core.c index 1cf5deda06e1..fa8d1a466014 100644 --- a/drivers/input/ff-core.c +++ b/drivers/input/ff-core.c @@ -67,7 +67,7 @@ static int compat_effect(struct ff_device *ff, struct ff_effect *effect) effect->type = FF_PERIODIC; effect->u.periodic.waveform = FF_SINE; effect->u.periodic.period = 50; - effect->u.periodic.magnitude = max(magnitude, 0x7fff); + effect->u.periodic.magnitude = magnitude; effect->u.periodic.offset = 0; effect->u.periodic.phase = 0; effect->u.periodic.envelope.attack_length = 0; diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c index 8dbf1e69c90a..d75a8b179a8a 100644 --- a/drivers/input/keyboard/gpio_keys.c +++ b/drivers/input/keyboard/gpio_keys.c @@ -247,7 +247,7 @@ static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata, ssize_t error; int i; - bits = bitmap_zalloc(n_events, GFP_KERNEL); + bits = bitmap_alloc(n_events, GFP_KERNEL); if (!bits) return -ENOMEM; diff --git a/drivers/input/misc/axp20x-pek.c b/drivers/input/misc/axp20x-pek.c index e09b1fae42e1..04da7916eb70 100644 --- a/drivers/input/misc/axp20x-pek.c +++ b/drivers/input/misc/axp20x-pek.c @@ -206,11 +206,8 @@ ATTRIBUTE_GROUPS(axp20x); static irqreturn_t axp20x_pek_irq(int irq, void *pwr) { - struct axp20x_pek *axp20x_pek = pwr; - struct input_dev *idev = axp20x_pek->input; - - if (!idev) - return IRQ_HANDLED; + struct input_dev *idev = pwr; + struct axp20x_pek *axp20x_pek = input_get_drvdata(idev); /* * The power-button is connected to ground so a falling edge (dbf) @@ -229,9 +226,22 @@ static irqreturn_t axp20x_pek_irq(int irq, void *pwr) static int axp20x_pek_probe_input_device(struct axp20x_pek *axp20x_pek, struct platform_device *pdev) { + struct axp20x_dev *axp20x = axp20x_pek->axp20x; struct input_dev *idev; int error; + axp20x_pek->irq_dbr = platform_get_irq_byname(pdev, "PEK_DBR"); + if (axp20x_pek->irq_dbr < 0) + return axp20x_pek->irq_dbr; + axp20x_pek->irq_dbr = regmap_irq_get_virq(axp20x->regmap_irqc, + axp20x_pek->irq_dbr); + + axp20x_pek->irq_dbf = platform_get_irq_byname(pdev, "PEK_DBF"); + if (axp20x_pek->irq_dbf < 0) + return axp20x_pek->irq_dbf; + axp20x_pek->irq_dbf = regmap_irq_get_virq(axp20x->regmap_irqc, + axp20x_pek->irq_dbf); + axp20x_pek->input = devm_input_allocate_device(&pdev->dev); if (!axp20x_pek->input) return -ENOMEM; @@ -246,6 +256,24 @@ static int axp20x_pek_probe_input_device(struct axp20x_pek *axp20x_pek, input_set_drvdata(idev, axp20x_pek); + error = devm_request_any_context_irq(&pdev->dev, axp20x_pek->irq_dbr, + axp20x_pek_irq, 0, + "axp20x-pek-dbr", idev); + if (error < 0) { + dev_err(&pdev->dev, "Failed to request dbr IRQ#%d: %d\n", + axp20x_pek->irq_dbr, error); + return error; + } + + error = devm_request_any_context_irq(&pdev->dev, axp20x_pek->irq_dbf, + axp20x_pek_irq, 0, + "axp20x-pek-dbf", idev); + if (error < 0) { + dev_err(&pdev->dev, "Failed to request dbf IRQ#%d: %d\n", + axp20x_pek->irq_dbf, error); + return error; + } + error = input_register_device(idev); if (error) { dev_err(&pdev->dev, "Can't register input device: %d\n", @@ -253,6 +281,8 @@ static int axp20x_pek_probe_input_device(struct axp20x_pek *axp20x_pek, return error; } + device_init_wakeup(&pdev->dev, true); + return 0; } @@ -293,18 +323,6 @@ static int axp20x_pek_probe(struct platform_device *pdev) axp20x_pek->axp20x = dev_get_drvdata(pdev->dev.parent); - axp20x_pek->irq_dbr = platform_get_irq_byname(pdev, "PEK_DBR"); - if (axp20x_pek->irq_dbr < 0) - return axp20x_pek->irq_dbr; - axp20x_pek->irq_dbr = regmap_irq_get_virq( - axp20x_pek->axp20x->regmap_irqc, axp20x_pek->irq_dbr); - - axp20x_pek->irq_dbf = platform_get_irq_byname(pdev, "PEK_DBF"); - if (axp20x_pek->irq_dbf < 0) - return axp20x_pek->irq_dbf; - axp20x_pek->irq_dbf = regmap_irq_get_virq( - axp20x_pek->axp20x->regmap_irqc, axp20x_pek->irq_dbf); - if (axp20x_pek_should_register_input(axp20x_pek)) { error = axp20x_pek_probe_input_device(axp20x_pek, pdev); if (error) @@ -313,26 +331,6 @@ static int axp20x_pek_probe(struct platform_device *pdev) axp20x_pek->info = (struct axp20x_info *)match->driver_data; - error = devm_request_any_context_irq(&pdev->dev, axp20x_pek->irq_dbr, - axp20x_pek_irq, 0, - "axp20x-pek-dbr", axp20x_pek); - if (error < 0) { - dev_err(&pdev->dev, "Failed to request dbr IRQ#%d: %d\n", - axp20x_pek->irq_dbr, error); - return error; - } - - error = devm_request_any_context_irq(&pdev->dev, axp20x_pek->irq_dbf, - axp20x_pek_irq, 0, - "axp20x-pek-dbf", axp20x_pek); - if (error < 0) { - dev_err(&pdev->dev, "Failed to request dbf IRQ#%d: %d\n", - axp20x_pek->irq_dbf, error); - return error; - } - - device_init_wakeup(&pdev->dev, true); - platform_set_drvdata(pdev, axp20x_pek); return 0; diff --git a/drivers/input/misc/palmas-pwrbutton.c b/drivers/input/misc/palmas-pwrbutton.c index f9b05cf09ff5..2213e06b611d 100644 --- a/drivers/input/misc/palmas-pwrbutton.c +++ b/drivers/input/misc/palmas-pwrbutton.c @@ -15,6 +15,7 @@ * GNU General Public License for more details. */ +#include <linux/bitfield.h> #include <linux/init.h> #include <linux/input.h> #include <linux/interrupt.h> @@ -115,8 +116,8 @@ static void palmas_pwron_params_ofinit(struct device *dev, struct device_node *np; u32 val; int i, error; - u8 lpk_times[] = { 6, 8, 10, 12 }; - int pwr_on_deb_ms[] = { 15, 100, 500, 1000 }; + static const u8 lpk_times[] = { 6, 8, 10, 12 }; + static const int pwr_on_deb_ms[] = { 15, 100, 500, 1000 }; memset(config, 0, sizeof(*config)); @@ -192,8 +193,8 @@ static int palmas_pwron_probe(struct platform_device *pdev) * Setup default hardware shutdown option (long key press) * and debounce. */ - val = config.long_press_time_val << __ffs(PALMAS_LPK_TIME_MASK); - val |= config.pwron_debounce_val << __ffs(PALMAS_PWRON_DEBOUNCE_MASK); + val = FIELD_PREP(PALMAS_LPK_TIME_MASK, config.long_press_time_val) | + FIELD_PREP(PALMAS_PWRON_DEBOUNCE_MASK, config.pwron_debounce_val); error = palmas_update_bits(palmas, PALMAS_PMU_CONTROL_BASE, PALMAS_LONG_PRESS_KEY, PALMAS_LPK_TIME_MASK | diff --git a/drivers/input/mouse/byd.c b/drivers/input/mouse/byd.c index 6e0c5f5a2713..221a553f45cd 100644 --- a/drivers/input/mouse/byd.c +++ b/drivers/input/mouse/byd.c @@ -191,7 +191,7 @@ /* * The touchpad generates a mixture of absolute and relative packets, indicated - * by the the last byte of each packet being set to one of the following: + * by the last byte of each packet being set to one of the following: */ #define BYD_PACKET_ABSOLUTE 0xf8 #define BYD_PACKET_RELATIVE 0x00 diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index aaa3c455e01e..a3bfc7a41679 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -297,6 +297,108 @@ static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data) 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; + input->id.vendor = 0x0416; + 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) { int id = coor_data[0] & 0x0F; @@ -327,6 +429,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); } +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) { int touch_num; @@ -341,8 +451,7 @@ static void goodix_ts_report_key(struct goodix_ts_data *ts, u8 *data) input_report_key(ts->input_dev, ts->keymap[i], 1); } else { - for (i = 0; i < GOODIX_MAX_KEYS; i++) - input_report_key(ts->input_dev, ts->keymap[i], 0); + goodix_ts_release_keys(ts); } } @@ -364,6 +473,15 @@ static void goodix_process_events(struct goodix_ts_data *ts) if (touch_num < 0) 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 previously registered touches */ + } else { + goodix_ts_report_pen_up(ts); + } + goodix_ts_report_key(ts, point_data); for (i = 0; i < touch_num; i++) @@ -374,6 +492,7 @@ static void goodix_process_events(struct goodix_ts_data *ts) goodix_ts_report_touch_8b(ts, &point_data[1 + ts->contact_size * i]); +sync: input_mt_sync_frame(ts->input_dev); input_sync(ts->input_dev); } @@ -857,7 +976,7 @@ retry_get_irq_gpio: if (IS_ERR(gpiod)) { error = PTR_ERR(gpiod); if (error != -EPROBE_DEFER) - dev_dbg(dev, "Failed to get %s GPIO: %d\n", + dev_err(dev, "Failed to get %s GPIO: %d\n", GOODIX_GPIO_INT_NAME, error); return error; } @@ -874,7 +993,7 @@ retry_get_irq_gpio: if (IS_ERR(gpiod)) { error = PTR_ERR(gpiod); if (error != -EPROBE_DEFER) - dev_dbg(dev, "Failed to get %s GPIO: %d\n", + dev_err(dev, "Failed to get %s GPIO: %d\n", GOODIX_GPIO_RST_NAME, error); return error; } diff --git a/drivers/input/touchscreen/goodix.h b/drivers/input/touchscreen/goodix.h index 02065d1c3263..fa8602e78a64 100644 --- a/drivers/input/touchscreen/goodix.h +++ b/drivers/input/touchscreen/goodix.h @@ -76,6 +76,7 @@ struct goodix_chip_data { struct goodix_ts_data { struct i2c_client *client; struct input_dev *input_dev; + struct input_dev *input_pen; const struct goodix_chip_data *chip; const char *firmware_name; struct touchscreen_properties prop; diff --git a/drivers/input/touchscreen/silead.c b/drivers/input/touchscreen/silead.c index 1ee760bac0cf..3eef8c01090f 100644 --- a/drivers/input/touchscreen/silead.c +++ b/drivers/input/touchscreen/silead.c @@ -67,6 +67,7 @@ struct silead_ts_data { struct i2c_client *client; struct gpio_desc *gpio_power; struct input_dev *input; + struct input_dev *pen_input; struct regulator_bulk_data regulators[2]; char fw_name[64]; struct touchscreen_properties prop; @@ -75,6 +76,13 @@ struct silead_ts_data { struct input_mt_pos pos[SILEAD_MAX_FINGERS]; int slots[SILEAD_MAX_FINGERS]; int id[SILEAD_MAX_FINGERS]; + u32 efi_fw_min_max[4]; + bool efi_fw_min_max_set; + bool pen_supported; + bool pen_down; + u32 pen_x_res; + u32 pen_y_res; + int pen_up_count; }; struct silead_fw_data { @@ -82,6 +90,35 @@ struct silead_fw_data { u32 val; }; +static void silead_apply_efi_fw_min_max(struct silead_ts_data *data) +{ + struct input_absinfo *absinfo_x = &data->input->absinfo[ABS_MT_POSITION_X]; + struct input_absinfo *absinfo_y = &data->input->absinfo[ABS_MT_POSITION_Y]; + + if (!data->efi_fw_min_max_set) + return; + + absinfo_x->minimum = data->efi_fw_min_max[0]; + absinfo_x->maximum = data->efi_fw_min_max[1]; + absinfo_y->minimum = data->efi_fw_min_max[2]; + absinfo_y->maximum = data->efi_fw_min_max[3]; + + if (data->prop.invert_x) { + absinfo_x->maximum -= absinfo_x->minimum; + absinfo_x->minimum = 0; + } + + if (data->prop.invert_y) { + absinfo_y->maximum -= absinfo_y->minimum; + absinfo_y->minimum = 0; + } + + if (data->prop.swap_x_y) { + swap(absinfo_x->minimum, absinfo_y->minimum); + swap(absinfo_x->maximum, absinfo_y->maximum); + } +} + static int silead_ts_request_input_dev(struct silead_ts_data *data) { struct device *dev = &data->client->dev; @@ -97,6 +134,7 @@ static int silead_ts_request_input_dev(struct silead_ts_data *data) input_set_abs_params(data->input, ABS_MT_POSITION_X, 0, 4095, 0, 0); input_set_abs_params(data->input, ABS_MT_POSITION_Y, 0, 4095, 0, 0); touchscreen_parse_properties(data->input, true, &data->prop); + silead_apply_efi_fw_min_max(data); input_mt_init_slots(data->input, data->max_fingers, INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED | @@ -112,6 +150,40 @@ static int silead_ts_request_input_dev(struct silead_ts_data *data) error = input_register_device(data->input); if (error) { dev_err(dev, "Failed to register input device: %d\n", error); + return error; + } + + return 0; +} + +static int silead_ts_request_pen_input_dev(struct silead_ts_data *data) +{ + struct device *dev = &data->client->dev; + int error; + + if (!data->pen_supported) + return 0; + + data->pen_input = devm_input_allocate_device(dev); + if (!data->pen_input) + return -ENOMEM; + + input_set_abs_params(data->pen_input, ABS_X, 0, 4095, 0, 0); + input_set_abs_params(data->pen_input, ABS_Y, 0, 4095, 0, 0); + input_set_capability(data->pen_input, EV_KEY, BTN_TOUCH); + input_set_capability(data->pen_input, EV_KEY, BTN_TOOL_PEN); + set_bit(INPUT_PROP_DIRECT, data->pen_input->propbit); + touchscreen_parse_properties(data->pen_input, false, &data->prop); + input_abs_set_res(data->pen_input, ABS_X, data->pen_x_res); + input_abs_set_res(data->pen_input, ABS_Y, data->pen_y_res); + + data->pen_input->name = SILEAD_TS_NAME " pen"; + data->pen_input->phys = "input/pen"; + data->input->id.bustype = BUS_I2C; + + error = input_register_device(data->pen_input); + if (error) { + dev_err(dev, "Failed to register pen input device: %d\n", error); return error; } @@ -129,6 +201,45 @@ static void silead_ts_set_power(struct i2c_client *client, } } +static bool silead_ts_handle_pen_data(struct silead_ts_data *data, u8 *buf) +{ + u8 *coord = buf + SILEAD_POINT_DATA_LEN; + struct input_mt_pos pos; + + if (!data->pen_supported || buf[2] != 0x00 || buf[3] != 0x00) + return false; + + if (buf[0] == 0x00 && buf[1] == 0x00 && data->pen_down) { + data->pen_up_count++; + if (data->pen_up_count == 6) { + data->pen_down = false; + goto sync; + } + return true; + } + + if (buf[0] == 0x01 && buf[1] == 0x08) { + touchscreen_set_mt_pos(&pos, &data->prop, + get_unaligned_le16(&coord[SILEAD_POINT_X_OFF]) & 0xfff, + get_unaligned_le16(&coord[SILEAD_POINT_Y_OFF]) & 0xfff); + + input_report_abs(data->pen_input, ABS_X, pos.x); + input_report_abs(data->pen_input, ABS_Y, pos.y); + + data->pen_up_count = 0; + data->pen_down = true; + goto sync; + } + + return false; + +sync: + input_report_key(data->pen_input, BTN_TOOL_PEN, data->pen_down); + input_report_key(data->pen_input, BTN_TOUCH, data->pen_down); + input_sync(data->pen_input); + return true; +} + static void silead_ts_read_data(struct i2c_client *client) { struct silead_ts_data *data = i2c_get_clientdata(client); @@ -151,6 +262,9 @@ static void silead_ts_read_data(struct i2c_client *client) buf[0] = data->max_fingers; } + if (silead_ts_handle_pen_data(data, buf)) + goto sync; /* Pen is down, release all previous touches */ + touch_nr = 0; bufp = buf + SILEAD_POINT_DATA_LEN; for (i = 0; i < buf[0]; i++, bufp += SILEAD_POINT_DATA_LEN) { @@ -193,6 +307,7 @@ static void silead_ts_read_data(struct i2c_client *client) data->pos[i].y, data->id[i], data->slots[i]); } +sync: input_mt_sync_frame(input); input_report_key(input, KEY_LEFTMETA, softbutton_pressed); input_sync(input); @@ -282,17 +397,56 @@ static int silead_ts_load_fw(struct i2c_client *client) { struct device *dev = &client->dev; struct silead_ts_data *data = i2c_get_clientdata(client); - unsigned int fw_size, i; - const struct firmware *fw; + const struct firmware *fw = NULL; struct silead_fw_data *fw_data; + unsigned int fw_size, i; int error; dev_dbg(dev, "Firmware file name: %s", data->fw_name); - error = firmware_request_platform(&fw, data->fw_name, dev); + /* + * Unfortunately, at the time of writing this comment, we have been unable to + * get permission from Silead, or from device OEMs, to distribute the necessary + * Silead firmware files in linux-firmware. + * + * On a whole bunch of devices the UEFI BIOS code contains a touchscreen driver, + * which contains an embedded copy of the firmware. The fw-loader code has a + * "platform" fallback mechanism, which together with info on the firmware + * from drivers/platform/x86/touchscreen_dmi.c will use the firmware from the + * UEFI driver when the firmware is missing from /lib/firmware. This makes the + * touchscreen work OOTB without users needing to manually download the firmware. + * + * The firmware bundled with the original Windows/Android is usually newer then + * the firmware in the UEFI driver and it is better calibrated. This better + * calibration can lead to significant differences in the reported min/max + * coordinates. + * + * To deal with this we first try to load the firmware without "platform" + * fallback. If that fails we retry with "platform" fallback and if that + * succeeds we apply an (optional) set of alternative min/max values from the + * "silead,efi-fw-min-max" property. + */ + error = firmware_request_nowarn(&fw, data->fw_name, dev); if (error) { - dev_err(dev, "Firmware request error %d\n", error); - return error; + error = firmware_request_platform(&fw, data->fw_name, dev); + if (error) { + dev_err(dev, "Firmware request error %d\n", error); + return error; + } + + error = device_property_read_u32_array(dev, "silead,efi-fw-min-max", + data->efi_fw_min_max, + ARRAY_SIZE(data->efi_fw_min_max)); + if (!error) + data->efi_fw_min_max_set = true; + + /* The EFI (platform) embedded fw does not have pen support */ + if (data->pen_supported) { + dev_warn(dev, "Warning loading '%s' from filesystem failed, using EFI embedded copy.\n", + data->fw_name); + dev_warn(dev, "Warning pen support is known to be broken in the EFI embedded fw version\n"); + data->pen_supported = false; + } } fw_size = fw->size / sizeof(*fw_data); @@ -450,6 +604,10 @@ static void silead_ts_read_props(struct i2c_client *client) "silead/%s", str); else dev_dbg(dev, "Firmware file name read error. Using default."); + + data->pen_supported = device_property_read_bool(dev, "silead,pen-supported"); + device_property_read_u32(dev, "silead,pen-resolution-x", &data->pen_x_res); + device_property_read_u32(dev, "silead,pen-resolution-y", &data->pen_y_res); } #ifdef CONFIG_ACPI @@ -562,6 +720,10 @@ static int silead_ts_probe(struct i2c_client *client, if (error) return error; + error = silead_ts_request_pen_input_dev(data); + if (error) + return error; + error = devm_request_threaded_irq(dev, client->irq, NULL, silead_ts_threaded_irq_handler, IRQF_ONESHOT, client->name, data); diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c index 83e685557a19..f2fb6a9a1a57 100644 --- a/drivers/input/touchscreen/ti_am335x_tsc.c +++ b/drivers/input/touchscreen/ti_am335x_tsc.c @@ -126,12 +126,13 @@ static int titsc_config_wires(struct titsc *ts_dev) static void titsc_step_config(struct titsc *ts_dev) { unsigned int config; - int i; + int i, n; int end_step, first_step, tsc_steps; u32 stepenable; config = STEPCONFIG_MODE_HWSYNC | - STEPCONFIG_AVG_16 | ts_dev->bit_xp; + STEPCONFIG_AVG_16 | ts_dev->bit_xp | + STEPCONFIG_INM_ADCREFM; switch (ts_dev->wires) { case 4: config |= STEPCONFIG_INP(ts_dev->inp_yp) | ts_dev->bit_xn; @@ -150,9 +151,11 @@ static void titsc_step_config(struct titsc *ts_dev) first_step = TOTAL_STEPS - tsc_steps; /* Steps 16 to 16-coordinate_readouts is for X */ end_step = first_step + tsc_steps; + n = 0; for (i = end_step - ts_dev->coordinate_readouts; i < end_step; i++) { titsc_writel(ts_dev, REG_STEPCONFIG(i), config); - titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY); + titsc_writel(ts_dev, REG_STEPDELAY(i), + n++ == 0 ? STEPCONFIG_OPENDLY : 0); } config = 0; @@ -174,9 +177,11 @@ static void titsc_step_config(struct titsc *ts_dev) /* 1 ... coordinate_readouts is for Y */ end_step = first_step + ts_dev->coordinate_readouts; + n = 0; for (i = first_step; i < end_step; i++) { titsc_writel(ts_dev, REG_STEPCONFIG(i), config); - titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY); + titsc_writel(ts_dev, REG_STEPDELAY(i), + n++ == 0 ? STEPCONFIG_OPENDLY : 0); } /* Make CHARGECONFIG same as IDLECONFIG */ @@ -195,7 +200,10 @@ static void titsc_step_config(struct titsc *ts_dev) STEPCONFIG_OPENDLY); end_step++; - config |= STEPCONFIG_INP(ts_dev->inp_yn); + config = STEPCONFIG_MODE_HWSYNC | + STEPCONFIG_AVG_16 | ts_dev->bit_yp | + ts_dev->bit_xn | STEPCONFIG_INM_ADCREFM | + STEPCONFIG_INP(ts_dev->inp_yn); titsc_writel(ts_dev, REG_STEPCONFIG(end_step), config); titsc_writel(ts_dev, REG_STEPDELAY(end_step), STEPCONFIG_OPENDLY); @@ -310,7 +318,7 @@ static irqreturn_t titsc_irq(int irq, void *dev) /* * Calculate pressure using formula * Resistance(touch) = x plate resistance * - * x postion/4096 * ((z2 / z1) - 1) + * x position/4096 * ((z2 / z1) - 1) */ z = z1 - z2; z *= x; diff --git a/drivers/input/touchscreen/ucb1400_ts.c b/drivers/input/touchscreen/ucb1400_ts.c index e3f2c940ef3d..dfd3b35590c3 100644 --- a/drivers/input/touchscreen/ucb1400_ts.c +++ b/drivers/input/touchscreen/ucb1400_ts.c @@ -186,7 +186,6 @@ static irqreturn_t ucb1400_irq(int irqnr, void *devid) { struct ucb1400_ts *ucb = devid; unsigned int x, y, p; - bool penup; if (unlikely(irqnr != ucb->irq)) return IRQ_NONE; @@ -196,8 +195,7 @@ static irqreturn_t ucb1400_irq(int irqnr, void *devid) /* Start with a small delay before checking pendown state */ msleep(UCB1400_TS_POLL_PERIOD); - while (!ucb->stopped && !(penup = ucb1400_ts_pen_up(ucb))) { - + while (!ucb->stopped && !ucb1400_ts_pen_up(ucb)) { ucb1400_adc_enable(ucb->ac97); x = ucb1400_ts_read_xpos(ucb); y = ucb1400_ts_read_ypos(ucb); diff --git a/drivers/input/touchscreen/wacom_i2c.c b/drivers/input/touchscreen/wacom_i2c.c index fe4ea6204a4e..141754b2764c 100644 --- a/drivers/input/touchscreen/wacom_i2c.c +++ b/drivers/input/touchscreen/wacom_i2c.c @@ -24,12 +24,19 @@ #define WACOM_IN_PROXIMITY BIT(5) /* Registers */ -#define WACOM_CMD_QUERY0 0x04 -#define WACOM_CMD_QUERY1 0x00 -#define WACOM_CMD_QUERY2 0x33 -#define WACOM_CMD_QUERY3 0x02 -#define WACOM_CMD_THROW0 0x05 -#define WACOM_CMD_THROW1 0x00 +#define WACOM_COMMAND_LSB 0x04 +#define WACOM_COMMAND_MSB 0x00 + +#define WACOM_DATA_LSB 0x05 +#define WACOM_DATA_MSB 0x00 + +/* Report types */ +#define REPORT_FEATURE 0x30 + +/* Requests / operations */ +#define OPCODE_GET_REPORT 0x02 + +#define WACOM_QUERY_REPORT 3 #define WACOM_QUERY_SIZE 19 struct wacom_features { @@ -50,23 +57,24 @@ struct wacom_i2c { static int wacom_query_device(struct i2c_client *client, struct wacom_features *features) { - int ret; - u8 cmd1[] = { WACOM_CMD_QUERY0, WACOM_CMD_QUERY1, - WACOM_CMD_QUERY2, WACOM_CMD_QUERY3 }; - u8 cmd2[] = { WACOM_CMD_THROW0, WACOM_CMD_THROW1 }; + u8 get_query_data_cmd[] = { + WACOM_COMMAND_LSB, + WACOM_COMMAND_MSB, + REPORT_FEATURE | WACOM_QUERY_REPORT, + OPCODE_GET_REPORT, + WACOM_DATA_LSB, + WACOM_DATA_MSB, + }; u8 data[WACOM_QUERY_SIZE]; + int ret; + struct i2c_msg msgs[] = { + /* Request reading of feature ReportID: 3 (Pen Query Data) */ { .addr = client->addr, .flags = 0, - .len = sizeof(cmd1), - .buf = cmd1, - }, - { - .addr = client->addr, - .flags = 0, - .len = sizeof(cmd2), - .buf = cmd2, + .len = sizeof(get_query_data_cmd), + .buf = get_query_data_cmd, }, { .addr = client->addr, diff --git a/drivers/input/touchscreen/zinitix.c b/drivers/input/touchscreen/zinitix.c index 1e70b8d2a8d7..7c82c4f5fa6b 100644 --- a/drivers/input/touchscreen/zinitix.c +++ b/drivers/input/touchscreen/zinitix.c @@ -252,16 +252,27 @@ static int zinitix_init_touch(struct bt541_ts_data *bt541) static int zinitix_init_regulators(struct bt541_ts_data *bt541) { - struct i2c_client *client = bt541->client; + struct device *dev = &bt541->client->dev; int error; - bt541->supplies[0].supply = "vdd"; - bt541->supplies[1].supply = "vddo"; - error = devm_regulator_bulk_get(&client->dev, + /* + * Some older device trees have erroneous names for the regulators, + * so check if "vddo" is present and in that case use these names. + * Else use the proper supply names on the component. + */ + if (of_find_property(dev->of_node, "vddo-supply", NULL)) { + bt541->supplies[0].supply = "vdd"; + bt541->supplies[1].supply = "vddo"; + } else { + /* Else use the proper supply names */ + bt541->supplies[0].supply = "vcca"; + bt541->supplies[1].supply = "vdd"; + } + error = devm_regulator_bulk_get(dev, ARRAY_SIZE(bt541->supplies), bt541->supplies); if (error < 0) { - dev_err(&client->dev, "Failed to get regulators: %d\n", error); + dev_err(dev, "Failed to get regulators: %d\n", error); return error; } @@ -560,6 +571,7 @@ static SIMPLE_DEV_PM_OPS(zinitix_pm_ops, zinitix_suspend, zinitix_resume); #ifdef CONFIG_OF static const struct of_device_id zinitix_of_match[] = { + { .compatible = "zinitix,bt532" }, { .compatible = "zinitix,bt541" }, { } }; |