Commit b520085c authored by Linus Torvalds's avatar Linus Torvalds

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
parents 4b378951 762f99f4
# 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 = <&reg_vcca_tsp>;
vdd-supply = <&reg_vdd_tsp>;
touchscreen-size-x = <540>;
touchscreen-size-y = <960>;
zinitix,mode = <2>;
};
};
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 = <&reg_vdd_tsp>;
vddo-supply = <&pm8916_l6>;
touchscreen-size-x = <540>;
touchscreen-size-y = <960>;
zinitix,mode = <2>;
};
/* ... */
};
......@@ -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;
......
......@@ -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;
......
......@@ -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;
......
......@@ -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 |
......
......@@ -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
......
......@@ -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 @@ static int goodix_get_gpio_config(struct goodix_ts_data *ts)
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 @@ static int goodix_get_gpio_config(struct goodix_ts_data *ts)
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;
}
......
......@@ -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;
......
......@@ -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);
......
......@@ -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;
......
......@@ -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);
......
......@@ -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,
......
......@@ -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" },
{ }
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment