Commit 49f13b09 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:

 - a new driver for the Azoteq IQS269A capacitive touch controller

 - a new driver for the Cypress CY8CTMA140 touchscreen

 - updates to Elan and ft5x06 touchscreen drivers

 - assorted driver fixes

 - msm-vibrator has been removed as we have a more generic solution

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (28 commits)
  Input: adi - work around module name confict
  Input: iqs269a - add missing I2C dependency
  Input: elants - refactor elants_i2c_execute_command()
  Input: elants - override touchscreen info with DT properties
  Input: elants - remove unused axes
  Input: add support for Azoteq IQS269A
  dt-bindings: input: Add bindings for Azoteq IQS269A
  Input: imx_sc_key - use devm_add_action_or_reset() to handle all cleanups
  Input: remove msm-vibrator driver
  dt-bindings: Input: remove msm-vibrator
  Input: elants_i2c - provide an attribute to show calibration count
  Input: introduce input_mt_report_slot_inactive()
  dt-bindings: input: touchscreen: elants_i2c: convert to YAML
  Input: add driver for the Cypress CY8CTMA140 touchscreen
  dt-bindings: touchscreen: Add CY8CTMA140 bindings
  Input: edt-ft5x06 - prefer asynchronous probe
  Input: edt-ft5x06 - improve power management operations
  Input: edt-ft5x06 - move parameter restore into helper
  Input: edt-ft5x06 - fix get_default register write access
  Input: atkbd - receive and use physcode->keycode mapping from FW
  ...
parents 09102704 751ad34f
Elantech I2C Touchscreen
Required properties:
- compatible: must be "elan,ekth3500".
- reg: I2C address of the chip.
- interrupts: interrupt to which the chip is connected (see interrupt
binding[0]).
Optional properties:
- wakeup-source: touchscreen can be used as a wakeup source.
- pinctrl-names: should be "default" (see pinctrl binding [1]).
- pinctrl-0: a phandle pointing to the pin settings for the device (see
pinctrl binding [1]).
- reset-gpios: reset gpio the chip is connected to.
- vcc33-supply: a phandle for the regulator supplying 3.3V power.
- vccio-supply: a phandle for the regulator supplying IO power.
[0]: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
[1]: Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
Example:
&i2c1 {
/* ... */
touchscreen@10 {
compatible = "elan,ekth3500";
reg = <0x10>;
interrupt-parent = <&gpio4>;
interrupts = <0x0 IRQ_TYPE_EDGE_FALLING>;
wakeup-source;
};
/* ... */
};
This diff is collapsed.
* Device tree bindings for the Qualcomm MSM vibrator
Required properties:
- compatible: Should be one of
"qcom,msm8226-vibrator"
"qcom,msm8974-vibrator"
- reg: the base address and length of the IO memory for the registers.
- pinctrl-names: set to default.
- pinctrl-0: phandles pointing to pin configuration nodes. See
Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
- clock-names: set to pwm
- clocks: phandle of the clock. See
Documentation/devicetree/bindings/clock/clock-bindings.txt
- enable-gpios: GPIO that enables the vibrator.
Optional properties:
- vcc-supply: phandle to the regulator that provides power to the sensor.
Example from a LG Nexus 5 (hammerhead) phone:
vibrator@fd8c3450 {
reg = <0xfd8c3450 0x400>;
compatible = "qcom,msm8974-vibrator";
vcc-supply = <&pm8941_l19>;
clocks = <&mmcc CAMSS_GP1_CLK>;
clock-names = "pwm";
enable-gpios = <&msmgpio 60 GPIO_ACTIVE_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&vibrator_pin>;
};
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/input/touchscreen/cypress,cy8ctma140.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Cypress CY8CTMA140 series touchscreen controller bindings
maintainers:
- Linus Walleij <linus.walleij@linaro.org>
allOf:
- $ref: touchscreen.yaml#
properties:
compatible:
const: cypress,cy8ctma140
reg:
const: 0x20
clock-frequency:
description: I2C client clock frequency, defined for host
minimum: 100000
maximum: 400000
interrupts:
maxItems: 1
vcpin-supply:
description: Analog power supply regulator on VCPIN pin
vdd-supply:
description: Digital power supply regulator on VDD pin
touchscreen-inverted-x: true
touchscreen-inverted-y: true
touchscreen-size-x: true
touchscreen-size-y: true
touchscreen-swapped-x-y: true
touchscreen-max-pressure: true
additionalProperties: false
required:
- compatible
- reg
- interrupts
- touchscreen-size-x
- touchscreen-size-y
- touchscreen-max-pressure
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
touchscreen@20 {
compatible = "cypress,cy8ctma140";
reg = <0x20>;
touchscreen-size-x = <480>;
touchscreen-size-y = <800>;
touchscreen-max-pressure = <255>;
interrupt-parent = <&gpio6>;
interrupts = <26 IRQ_TYPE_EDGE_FALLING>;
vdd-supply = <&ab8500_ldo_aux2_reg>;
vcpin-supply = <&ab8500_ldo_aux2_reg>;
};
};
...
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: "http://devicetree.org/schemas/input/touchscreen/elan,elants_i2c.yaml#"
$schema: "http://devicetree.org/meta-schemas/core.yaml#"
title: Elantech I2C Touchscreen
maintainers:
- David Heidelberg <david@ixit.cz>
allOf:
- $ref: touchscreen.yaml#
properties:
compatible:
enum:
- elan,ektf3624
- elan,ekth3500
reg:
maxItems: 1
interrupts:
maxItems: 1
wakeup-source:
type: boolean
description: touchscreen can be used as a wakeup source.
reset-gpios:
description: reset gpio the chip is connected to.
vcc33-supply:
description: a phandle for the regulator supplying 3.3V power.
vccio-supply:
description: a phandle for the regulator supplying IO power.
touchscreen-inverted-x: true
touchscreen-inverted-y: true
touchscreen-size-x: true
touchscreen-size-y: true
touchscreen-swapped-x-y: true
additionalProperties: false
required:
- compatible
- reg
- interrupts
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
touchscreen@10 {
compatible = "elan,ekth3500";
reg = <0x10>;
interrupt-parent = <&gpio4>;
interrupts = <0x0 IRQ_TYPE_EDGE_FALLING>;
wakeup-source;
};
};
* MELFAS MMS114/MMS152 touchscreen controller * MELFAS MMS114/MMS152/MMS345L touchscreen controller
Required properties: Required properties:
- compatible: should be one of: - compatible: should be one of:
- "melfas,mms114" - "melfas,mms114"
- "melfas,mms152" - "melfas,mms152"
- "melfas,mms345l"
- reg: I2C address of the chip - reg: I2C address of the chip
- interrupts: interrupt to which the chip is connected - interrupts: interrupt to which the chip is connected
- touchscreen-size-x: See [1] - touchscreen-size-x: See [1]
......
...@@ -4710,6 +4710,12 @@ Q: http://patchwork.linuxtv.org/project/linux-media/list/ ...@@ -4710,6 +4710,12 @@ Q: http://patchwork.linuxtv.org/project/linux-media/list/
T: git git://linuxtv.org/anttip/media_tree.git T: git git://linuxtv.org/anttip/media_tree.git
F: drivers/media/common/cypress_firmware* F: drivers/media/common/cypress_firmware*
CYPRESS CY8CTMA140 TOUCHSCREEN DRIVER
M: Linus Walleij <linus.walleij@linaro.org>
L: linux-input@vger.kernel.org
S: Maintained
F: drivers/input/touchscreen/cy8ctma140.c
CYTTSP TOUCHSCREEN DRIVER CYTTSP TOUCHSCREEN DRIVER
M: Ferruh Yigit <fery@cypress.com> M: Ferruh Yigit <fery@cypress.com>
L: linux-input@vger.kernel.org L: linux-input@vger.kernel.org
......
...@@ -387,8 +387,7 @@ static int u1_raw_event(struct alps_dev *hdata, u8 *data, int size) ...@@ -387,8 +387,7 @@ static int u1_raw_event(struct alps_dev *hdata, u8 *data, int size)
input_report_abs(hdata->input, input_report_abs(hdata->input,
ABS_MT_PRESSURE, z); ABS_MT_PRESSURE, z);
} else { } else {
input_mt_report_slot_state(hdata->input, input_mt_report_slot_inactive(hdata->input);
MT_TOOL_FINGER, 0);
} }
} }
......
...@@ -899,7 +899,7 @@ static void mt_release_pending_palms(struct mt_device *td, ...@@ -899,7 +899,7 @@ static void mt_release_pending_palms(struct mt_device *td,
clear_bit(slotnum, app->pending_palm_slots); clear_bit(slotnum, app->pending_palm_slots);
input_mt_slot(input, slotnum); input_mt_slot(input, slotnum);
input_mt_report_slot_state(input, MT_TOOL_PALM, false); input_mt_report_slot_inactive(input);
need_sync = true; need_sync = true;
} }
...@@ -1643,9 +1643,7 @@ static void mt_release_contacts(struct hid_device *hid) ...@@ -1643,9 +1643,7 @@ static void mt_release_contacts(struct hid_device *hid)
if (mt) { if (mt) {
for (i = 0; i < mt->num_slots; i++) { for (i = 0; i < mt->num_slots; i++) {
input_mt_slot(input_dev, i); input_mt_slot(input_dev, i);
input_mt_report_slot_state(input_dev, input_mt_report_slot_inactive(input_dev);
MT_TOOL_FINGER,
false);
} }
input_mt_sync_frame(input_dev); input_mt_sync_frame(input_dev);
input_sync(input_dev); input_sync(input_dev);
......
...@@ -282,7 +282,8 @@ static void evdev_pass_values(struct evdev_client *client, ...@@ -282,7 +282,8 @@ static void evdev_pass_values(struct evdev_client *client,
spin_unlock(&client->buffer_lock); spin_unlock(&client->buffer_lock);
if (wakeup) if (wakeup)
wake_up_interruptible(&evdev->wait); wake_up_interruptible_poll(&evdev->wait,
EPOLLIN | EPOLLOUT | EPOLLRDNORM | EPOLLWRNORM);
} }
/* /*
...@@ -429,7 +430,7 @@ static void evdev_hangup(struct evdev *evdev) ...@@ -429,7 +430,7 @@ static void evdev_hangup(struct evdev *evdev)
kill_fasync(&client->fasync, SIGIO, POLL_HUP); kill_fasync(&client->fasync, SIGIO, POLL_HUP);
spin_unlock(&evdev->client_lock); spin_unlock(&evdev->client_lock);
wake_up_interruptible(&evdev->wait); wake_up_interruptible_poll(&evdev->wait, EPOLLHUP | EPOLLERR);
} }
static int evdev_release(struct inode *inode, struct file *file) static int evdev_release(struct inode *inode, struct file *file)
...@@ -945,7 +946,7 @@ static int evdev_revoke(struct evdev *evdev, struct evdev_client *client, ...@@ -945,7 +946,7 @@ static int evdev_revoke(struct evdev *evdev, struct evdev_client *client,
client->revoked = true; client->revoked = true;
evdev_ungrab(evdev, client); evdev_ungrab(evdev, client);
input_flush_device(&evdev->handle, file); input_flush_device(&evdev->handle, file);
wake_up_interruptible(&evdev->wait); wake_up_interruptible_poll(&evdev->wait, EPOLLHUP | EPOLLERR);
return 0; return 0;
} }
......
...@@ -45,6 +45,7 @@ config JOYSTICK_A3D ...@@ -45,6 +45,7 @@ config JOYSTICK_A3D
config JOYSTICK_ADI config JOYSTICK_ADI
tristate "Logitech ADI digital joysticks and gamepads" tristate "Logitech ADI digital joysticks and gamepads"
select GAMEPORT select GAMEPORT
depends on ADI!=m # avoid module name conflict
help help
Say Y here if you have a Logitech controller using the ADI Say Y here if you have a Logitech controller using the ADI
protocol over the PC gameport. protocol over the PC gameport.
......
...@@ -701,7 +701,7 @@ config KEYBOARD_SPEAR ...@@ -701,7 +701,7 @@ config KEYBOARD_SPEAR
Say Y here if you want to use the SPEAR keyboard. Say Y here if you want to use the SPEAR keyboard.
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called spear-keboard. module will be called spear-keyboard.
config KEYBOARD_TC3589X config KEYBOARD_TC3589X
tristate "TC3589X Keypad support" tristate "TC3589X Keypad support"
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <linux/libps2.h> #include <linux/libps2.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/dmi.h> #include <linux/dmi.h>
#include <linux/property.h>
#define DRIVER_DESC "AT and PS/2 keyboard driver" #define DRIVER_DESC "AT and PS/2 keyboard driver"
...@@ -63,6 +64,11 @@ static bool atkbd_terminal; ...@@ -63,6 +64,11 @@ static bool atkbd_terminal;
module_param_named(terminal, atkbd_terminal, bool, 0); module_param_named(terminal, atkbd_terminal, bool, 0);
MODULE_PARM_DESC(terminal, "Enable break codes on an IBM Terminal keyboard connected via AT/PS2"); MODULE_PARM_DESC(terminal, "Enable break codes on an IBM Terminal keyboard connected via AT/PS2");
#define MAX_FUNCTION_ROW_KEYS 24
#define SCANCODE(keymap) ((keymap >> 16) & 0xFFFF)
#define KEYCODE(keymap) (keymap & 0xFFFF)
/* /*
* Scancode to keycode tables. These are just the default setting, and * Scancode to keycode tables. These are just the default setting, and
* are loadable via a userland utility. * are loadable via a userland utility.
...@@ -230,6 +236,9 @@ struct atkbd { ...@@ -230,6 +236,9 @@ struct atkbd {
/* Serializes reconnect(), attr->set() and event work */ /* Serializes reconnect(), attr->set() and event work */
struct mutex mutex; struct mutex mutex;
u32 function_row_physmap[MAX_FUNCTION_ROW_KEYS];
int num_function_row_keys;
}; };
/* /*
...@@ -283,6 +292,7 @@ static struct device_attribute atkbd_attr_##_name = \ ...@@ -283,6 +292,7 @@ static struct device_attribute atkbd_attr_##_name = \
__ATTR(_name, S_IRUGO, atkbd_do_show_##_name, NULL); __ATTR(_name, S_IRUGO, atkbd_do_show_##_name, NULL);
ATKBD_DEFINE_RO_ATTR(err_count); ATKBD_DEFINE_RO_ATTR(err_count);
ATKBD_DEFINE_RO_ATTR(function_row_physmap);
static struct attribute *atkbd_attributes[] = { static struct attribute *atkbd_attributes[] = {
&atkbd_attr_extra.attr, &atkbd_attr_extra.attr,
...@@ -292,11 +302,42 @@ static struct attribute *atkbd_attributes[] = { ...@@ -292,11 +302,42 @@ static struct attribute *atkbd_attributes[] = {
&atkbd_attr_softrepeat.attr, &atkbd_attr_softrepeat.attr,
&atkbd_attr_softraw.attr, &atkbd_attr_softraw.attr,
&atkbd_attr_err_count.attr, &atkbd_attr_err_count.attr,
&atkbd_attr_function_row_physmap.attr,
NULL NULL
}; };
static ssize_t atkbd_show_function_row_physmap(struct atkbd *atkbd, char *buf)
{
ssize_t size = 0;
int i;
if (!atkbd->num_function_row_keys)
return 0;
for (i = 0; i < atkbd->num_function_row_keys; i++)
size += scnprintf(buf + size, PAGE_SIZE - size, "%02X ",
atkbd->function_row_physmap[i]);
size += scnprintf(buf + size, PAGE_SIZE - size, "\n");
return size;
}
static umode_t atkbd_attr_is_visible(struct kobject *kobj,
struct attribute *attr, int i)
{
struct device *dev = container_of(kobj, struct device, kobj);
struct serio *serio = to_serio_port(dev);
struct atkbd *atkbd = serio_get_drvdata(serio);
if (attr == &atkbd_attr_function_row_physmap.attr &&
!atkbd->num_function_row_keys)
return 0;
return attr->mode;
}
static struct attribute_group atkbd_attribute_group = { static struct attribute_group atkbd_attribute_group = {
.attrs = atkbd_attributes, .attrs = atkbd_attributes,
.is_visible = atkbd_attr_is_visible,
}; };
static const unsigned int xl_table[] = { static const unsigned int xl_table[] = {
...@@ -994,6 +1035,39 @@ static unsigned int atkbd_oqo_01plus_scancode_fixup(struct atkbd *atkbd, ...@@ -994,6 +1035,39 @@ static unsigned int atkbd_oqo_01plus_scancode_fixup(struct atkbd *atkbd,
return code; return code;
} }
static int atkbd_get_keymap_from_fwnode(struct atkbd *atkbd)
{
struct device *dev = &atkbd->ps2dev.serio->dev;
int i, n;
u32 *ptr;
u16 scancode, keycode;
/* Parse "linux,keymap" property */
n = device_property_count_u32(dev, "linux,keymap");
if (n <= 0 || n > ATKBD_KEYMAP_SIZE)
return -ENXIO;
ptr = kcalloc(n, sizeof(u32), GFP_KERNEL);
if (!ptr)
return -ENOMEM;
if (device_property_read_u32_array(dev, "linux,keymap", ptr, n)) {
dev_err(dev, "problem parsing FW keymap property\n");
kfree(ptr);
return -EINVAL;
}
memset(atkbd->keycode, 0, sizeof(atkbd->keycode));
for (i = 0; i < n; i++) {
scancode = SCANCODE(ptr[i]);
keycode = KEYCODE(ptr[i]);
atkbd->keycode[scancode] = keycode;
}
kfree(ptr);
return 0;
}
/* /*
* atkbd_set_keycode_table() initializes keyboard's keycode table * atkbd_set_keycode_table() initializes keyboard's keycode table
* according to the selected scancode set * according to the selected scancode set
...@@ -1001,13 +1075,16 @@ static unsigned int atkbd_oqo_01plus_scancode_fixup(struct atkbd *atkbd, ...@@ -1001,13 +1075,16 @@ static unsigned int atkbd_oqo_01plus_scancode_fixup(struct atkbd *atkbd,
static void atkbd_set_keycode_table(struct atkbd *atkbd) static void atkbd_set_keycode_table(struct atkbd *atkbd)
{ {
struct device *dev = &atkbd->ps2dev.serio->dev;
unsigned int scancode; unsigned int scancode;
int i, j; int i, j;
memset(atkbd->keycode, 0, sizeof(atkbd->keycode)); memset(atkbd->keycode, 0, sizeof(atkbd->keycode));
bitmap_zero(atkbd->force_release_mask, ATKBD_KEYMAP_SIZE); bitmap_zero(atkbd->force_release_mask, ATKBD_KEYMAP_SIZE);
if (atkbd->translated) { if (!atkbd_get_keymap_from_fwnode(atkbd)) {
dev_dbg(dev, "Using FW keymap\n");
} else if (atkbd->translated) {
for (i = 0; i < 128; i++) { for (i = 0; i < 128; i++) {
scancode = atkbd_unxlate_table[i]; scancode = atkbd_unxlate_table[i];
atkbd->keycode[i] = atkbd_set2_keycode[scancode]; atkbd->keycode[i] = atkbd_set2_keycode[scancode];
...@@ -1121,6 +1198,22 @@ static void atkbd_set_device_attrs(struct atkbd *atkbd) ...@@ -1121,6 +1198,22 @@ static void atkbd_set_device_attrs(struct atkbd *atkbd)
} }
} }
static void atkbd_parse_fwnode_data(struct serio *serio)
{
struct atkbd *atkbd = serio_get_drvdata(serio);
struct device *dev = &serio->dev;
int n;
/* Parse "function-row-physmap" property */
n = device_property_count_u32(dev, "function-row-physmap");
if (n > 0 && n <= MAX_FUNCTION_ROW_KEYS &&
!device_property_read_u32_array(dev, "function-row-physmap",
atkbd->function_row_physmap, n)) {
atkbd->num_function_row_keys = n;
dev_dbg(dev, "FW reported %d function-row key locations\n", n);
}
}
/* /*
* atkbd_connect() is called when the serio module finds an interface * atkbd_connect() is called when the serio module finds an interface
* that isn't handled yet by an appropriate device driver. We check if * that isn't handled yet by an appropriate device driver. We check if
...@@ -1184,6 +1277,8 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv) ...@@ -1184,6 +1277,8 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv)
atkbd->id = 0xab00; atkbd->id = 0xab00;
} }
atkbd_parse_fwnode_data(serio);
atkbd_set_keycode_table(atkbd); atkbd_set_keycode_table(atkbd);
atkbd_set_device_attrs(atkbd); atkbd_set_device_attrs(atkbd);
......
...@@ -99,6 +99,15 @@ static void imx_sc_check_for_events(struct work_struct *work) ...@@ -99,6 +99,15 @@ static void imx_sc_check_for_events(struct work_struct *work)
msecs_to_jiffies(REPEAT_INTERVAL)); msecs_to_jiffies(REPEAT_INTERVAL));
} }
static void imx_sc_key_action(void *data)
{
struct imx_key_drv_data *priv = data;
imx_scu_irq_group_enable(SC_IRQ_GROUP_WAKE, SC_IRQ_BUTTON, false);
imx_scu_irq_unregister_notifier(&priv->key_notifier);
cancel_delayed_work_sync(&priv->check_work);
}
static int imx_sc_key_probe(struct platform_device *pdev) static int imx_sc_key_probe(struct platform_device *pdev)
{ {
struct imx_key_drv_data *priv; struct imx_key_drv_data *priv;
...@@ -149,27 +158,16 @@ static int imx_sc_key_probe(struct platform_device *pdev) ...@@ -149,27 +158,16 @@ static int imx_sc_key_probe(struct platform_device *pdev)
return error; return error;
} }
error = devm_add_action_or_reset(&pdev->dev, imx_sc_key_action, &priv);
if (error)
return error;
priv->key_notifier.notifier_call = imx_sc_key_notify; priv->key_notifier.notifier_call = imx_sc_key_notify;
error = imx_scu_irq_register_notifier(&priv->key_notifier); error = imx_scu_irq_register_notifier(&priv->key_notifier);
if (error) { if (error)
imx_scu_irq_group_enable(SC_IRQ_GROUP_WAKE, SC_IRQ_BUTTON,
false);
dev_err(&pdev->dev, "failed to register scu notifier\n"); dev_err(&pdev->dev, "failed to register scu notifier\n");
return error;
}
return 0;
}
static int imx_sc_key_remove(struct platform_device *pdev)
{
struct imx_key_drv_data *priv = platform_get_drvdata(pdev);
imx_scu_irq_group_enable(SC_IRQ_GROUP_WAKE, SC_IRQ_BUTTON, false);
imx_scu_irq_unregister_notifier(&priv->key_notifier);
cancel_delayed_work_sync(&priv->check_work);
return 0; return error;
} }
static const struct of_device_id imx_sc_key_ids[] = { static const struct of_device_id imx_sc_key_ids[] = {
...@@ -184,7 +182,6 @@ static struct platform_driver imx_sc_key_driver = { ...@@ -184,7 +182,6 @@ static struct platform_driver imx_sc_key_driver = {
.of_match_table = imx_sc_key_ids, .of_match_table = imx_sc_key_ids,
}, },
.probe = imx_sc_key_probe, .probe = imx_sc_key_probe,
.remove = imx_sc_key_remove,
}; };
module_platform_driver(imx_sc_key_driver); module_platform_driver(imx_sc_key_driver);
......
...@@ -374,5 +374,5 @@ static void __exit tca6416_keypad_exit(void) ...@@ -374,5 +374,5 @@ static void __exit tca6416_keypad_exit(void)
module_exit(tca6416_keypad_exit); module_exit(tca6416_keypad_exit);
MODULE_AUTHOR("Sriramakrishnan <srk@ti.com>"); MODULE_AUTHOR("Sriramakrishnan <srk@ti.com>");
MODULE_DESCRIPTION("Keypad driver over tca6146 IO expander"); MODULE_DESCRIPTION("Keypad driver over tca6416 IO expander");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
...@@ -117,16 +117,6 @@ config INPUT_E3X0_BUTTON ...@@ -117,16 +117,6 @@ config INPUT_E3X0_BUTTON
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called e3x0_button. module will be called e3x0_button.
config INPUT_MSM_VIBRATOR
tristate "Qualcomm MSM vibrator driver"
select INPUT_FF_MEMLESS
help
Support for the vibrator that is found on various Qualcomm MSM
SOCs.
To compile this driver as a module, choose M here: the module
will be called msm_vibrator.
config INPUT_PCSPKR config INPUT_PCSPKR
tristate "PC Speaker support" tristate "PC Speaker support"
depends on PCSPKR_PLATFORM depends on PCSPKR_PLATFORM
...@@ -265,17 +255,6 @@ config INPUT_APANEL ...@@ -265,17 +255,6 @@ config INPUT_APANEL
To compile this driver as a module, choose M here: the module will To compile this driver as a module, choose M here: the module will
be called apanel. be called apanel.
config INPUT_GP2A
tristate "Sharp GP2AP002A00F I2C Proximity/Opto sensor driver"
depends on I2C
depends on GPIOLIB || COMPILE_TEST
help
Say Y here if you have a Sharp GP2AP002A00F proximity/als combo-chip
hooked to an I2C bus.
To compile this driver as a module, choose M here: the
module will be called gp2ap002a00f.
config INPUT_GPIO_BEEPER config INPUT_GPIO_BEEPER
tristate "Generic GPIO Beeper support" tristate "Generic GPIO Beeper support"
depends on GPIOLIB || COMPILE_TEST depends on GPIOLIB || COMPILE_TEST
...@@ -739,6 +718,17 @@ config INPUT_IMS_PCU ...@@ -739,6 +718,17 @@ config INPUT_IMS_PCU
To compile this driver as a module, choose M here: the module will be To compile this driver as a module, choose M here: the module will be
called ims_pcu. called ims_pcu.
config INPUT_IQS269A
tristate "Azoteq IQS269A capacitive touch controller"
depends on I2C
select REGMAP_I2C
help
Say Y to enable support for the Azoteq IQS269A capacitive
touch controller.
To compile this driver as a module, choose M here: the
module will be called iqs269a.
config INPUT_CMA3000 config INPUT_CMA3000
tristate "VTI CMA3000 Tri-axis accelerometer" tristate "VTI CMA3000 Tri-axis accelerometer"
help help
......
...@@ -33,13 +33,13 @@ obj-$(CONFIG_INPUT_E3X0_BUTTON) += e3x0-button.o ...@@ -33,13 +33,13 @@ obj-$(CONFIG_INPUT_E3X0_BUTTON) += e3x0-button.o
obj-$(CONFIG_INPUT_DRV260X_HAPTICS) += drv260x.o obj-$(CONFIG_INPUT_DRV260X_HAPTICS) += drv260x.o
obj-$(CONFIG_INPUT_DRV2665_HAPTICS) += drv2665.o obj-$(CONFIG_INPUT_DRV2665_HAPTICS) += drv2665.o
obj-$(CONFIG_INPUT_DRV2667_HAPTICS) += drv2667.o obj-$(CONFIG_INPUT_DRV2667_HAPTICS) += drv2667.o
obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o
obj-$(CONFIG_INPUT_GPIO_BEEPER) += gpio-beeper.o obj-$(CONFIG_INPUT_GPIO_BEEPER) += gpio-beeper.o
obj-$(CONFIG_INPUT_GPIO_DECODER) += gpio_decoder.o obj-$(CONFIG_INPUT_GPIO_DECODER) += gpio_decoder.o
obj-$(CONFIG_INPUT_GPIO_VIBRA) += gpio-vibra.o obj-$(CONFIG_INPUT_GPIO_VIBRA) += gpio-vibra.o
obj-$(CONFIG_INPUT_HISI_POWERKEY) += hisi_powerkey.o obj-$(CONFIG_INPUT_HISI_POWERKEY) += hisi_powerkey.o
obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o
obj-$(CONFIG_INPUT_IMS_PCU) += ims-pcu.o obj-$(CONFIG_INPUT_IMS_PCU) += ims-pcu.o
obj-$(CONFIG_INPUT_IQS269A) += iqs269a.o
obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o
obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o
obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o
...@@ -50,7 +50,6 @@ obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o ...@@ -50,7 +50,6 @@ obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o
obj-$(CONFIG_INPUT_MAX8997_HAPTIC) += max8997_haptic.o obj-$(CONFIG_INPUT_MAX8997_HAPTIC) += max8997_haptic.o
obj-$(CONFIG_INPUT_MC13783_PWRBUTTON) += mc13783-pwrbutton.o obj-$(CONFIG_INPUT_MC13783_PWRBUTTON) += mc13783-pwrbutton.o
obj-$(CONFIG_INPUT_MMA8450) += mma8450.o obj-$(CONFIG_INPUT_MMA8450) += mma8450.o
obj-$(CONFIG_INPUT_MSM_VIBRATOR) += msm-vibrator.o
obj-$(CONFIG_INPUT_PALMAS_PWRBUTTON) += palmas-pwrbutton.o obj-$(CONFIG_INPUT_PALMAS_PWRBUTTON) += palmas-pwrbutton.o
obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o
obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2011 Sony Ericsson Mobile Communications Inc.
*
* Author: Courtney Cavin <courtney.cavin@sonyericsson.com>
* Prepared for up-stream by: Oskar Andero <oskar.andero@sonyericsson.com>
*/
#include <linux/i2c.h>
#include <linux/irq.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/input/gp2ap002a00f.h>
struct gp2a_data {
struct input_dev *input;
const struct gp2a_platform_data *pdata;
struct i2c_client *i2c_client;
};
enum gp2a_addr {
GP2A_ADDR_PROX = 0x0,
GP2A_ADDR_GAIN = 0x1,
GP2A_ADDR_HYS = 0x2,
GP2A_ADDR_CYCLE = 0x3,
GP2A_ADDR_OPMOD = 0x4,
GP2A_ADDR_CON = 0x6
};
enum gp2a_controls {
/* Software Shutdown control: 0 = shutdown, 1 = normal operation */
GP2A_CTRL_SSD = 0x01
};
static int gp2a_report(struct gp2a_data *dt)
{
int vo = gpio_get_value(dt->pdata->vout_gpio);
input_report_switch(dt->input, SW_FRONT_PROXIMITY, !vo);
input_sync(dt->input);
return 0;
}
static irqreturn_t gp2a_irq(int irq, void *handle)
{
struct gp2a_data *dt = handle;
gp2a_report(dt);
return IRQ_HANDLED;
}
static int gp2a_enable(struct gp2a_data *dt)
{
return i2c_smbus_write_byte_data(dt->i2c_client, GP2A_ADDR_OPMOD,
GP2A_CTRL_SSD);
}
static int gp2a_disable(struct gp2a_data *dt)
{
return i2c_smbus_write_byte_data(dt->i2c_client, GP2A_ADDR_OPMOD,
0x00);
}
static int gp2a_device_open(struct input_dev *dev)
{
struct gp2a_data *dt = input_get_drvdata(dev);
int error;
error = gp2a_enable(dt);
if (error < 0) {
dev_err(&dt->i2c_client->dev,
"unable to activate, err %d\n", error);
return error;
}
gp2a_report(dt);
return 0;
}
static void gp2a_device_close(struct input_dev *dev)
{
struct gp2a_data *dt = input_get_drvdata(dev);
int error;
error = gp2a_disable(dt);
if (error < 0)
dev_err(&dt->i2c_client->dev,
"unable to deactivate, err %d\n", error);
}
static int gp2a_initialize(struct gp2a_data *dt)
{
int error;
error = i2c_smbus_write_byte_data(dt->i2c_client, GP2A_ADDR_GAIN,
0x08);
if (error < 0)
return error;
error = i2c_smbus_write_byte_data(dt->i2c_client, GP2A_ADDR_HYS,
0xc2);
if (error < 0)
return error;
error = i2c_smbus_write_byte_data(dt->i2c_client, GP2A_ADDR_CYCLE,
0x04);
if (error < 0)
return error;
error = gp2a_disable(dt);
return error;
}
static int gp2a_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
const struct gp2a_platform_data *pdata = dev_get_platdata(&client->dev);
struct gp2a_data *dt;
int error;
if (!pdata)
return -EINVAL;
if (pdata->hw_setup) {
error = pdata->hw_setup(client);
if (error < 0)
return error;
}
error = gpio_request_one(pdata->vout_gpio, GPIOF_IN, GP2A_I2C_NAME);
if (error)
goto err_hw_shutdown;
dt = kzalloc(sizeof(struct gp2a_data), GFP_KERNEL);
if (!dt) {
error = -ENOMEM;
goto err_free_gpio;
}
dt->pdata = pdata;
dt->i2c_client = client;
error = gp2a_initialize(dt);
if (error < 0)
goto err_free_mem;
dt->input = input_allocate_device();
if (!dt->input) {
error = -ENOMEM;
goto err_free_mem;
}
input_set_drvdata(dt->input, dt);
dt->input->open = gp2a_device_open;
dt->input->close = gp2a_device_close;
dt->input->name = GP2A_I2C_NAME;
dt->input->id.bustype = BUS_I2C;
dt->input->dev.parent = &client->dev;
input_set_capability(dt->input, EV_SW, SW_FRONT_PROXIMITY);
error = request_threaded_irq(client->irq, NULL, gp2a_irq,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
IRQF_ONESHOT,
GP2A_I2C_NAME, dt);
if (error) {
dev_err(&client->dev, "irq request failed\n");
goto err_free_input_dev;
}
error = input_register_device(dt->input);
if (error) {
dev_err(&client->dev, "device registration failed\n");
goto err_free_irq;
}
device_init_wakeup(&client->dev, pdata->wakeup);
i2c_set_clientdata(client, dt);
return 0;
err_free_irq:
free_irq(client->irq, dt);
err_free_input_dev:
input_free_device(dt->input);
err_free_mem:
kfree(dt);
err_free_gpio:
gpio_free(pdata->vout_gpio);
err_hw_shutdown:
if (pdata->hw_shutdown)
pdata->hw_shutdown(client);
return error;
}
static int gp2a_remove(struct i2c_client *client)
{
struct gp2a_data *dt = i2c_get_clientdata(client);
const struct gp2a_platform_data *pdata = dt->pdata;
free_irq(client->irq, dt);
input_unregister_device(dt->input);
kfree(dt);
gpio_free(pdata->vout_gpio);
if (pdata->hw_shutdown)
pdata->hw_shutdown(client);
return 0;
}
static int __maybe_unused gp2a_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct gp2a_data *dt = i2c_get_clientdata(client);
int retval = 0;
if (device_may_wakeup(&client->dev)) {
enable_irq_wake(client->irq);
} else {
mutex_lock(&dt->input->mutex);
if (dt->input->users)
retval = gp2a_disable(dt);
mutex_unlock(&dt->input->mutex);
}
return retval;
}
static int __maybe_unused gp2a_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct gp2a_data *dt = i2c_get_clientdata(client);
int retval = 0;
if (device_may_wakeup(&client->dev)) {
disable_irq_wake(client->irq);
} else {
mutex_lock(&dt->input->mutex);
if (dt->input->users)
retval = gp2a_enable(dt);
mutex_unlock(&dt->input->mutex);
}
return retval;
}
static SIMPLE_DEV_PM_OPS(gp2a_pm, gp2a_suspend, gp2a_resume);
static const struct i2c_device_id gp2a_i2c_id[] = {
{ GP2A_I2C_NAME, 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, gp2a_i2c_id);
static struct i2c_driver gp2a_i2c_driver = {
.driver = {
.name = GP2A_I2C_NAME,
.pm = &gp2a_pm,
},
.probe = gp2a_probe,
.remove = gp2a_remove,
.id_table = gp2a_i2c_id,
};
module_i2c_driver(gp2a_i2c_driver);
MODULE_AUTHOR("Courtney Cavin <courtney.cavin@sonyericsson.com>");
MODULE_DESCRIPTION("Sharp GP2AP002A00F I2C Proximity/Opto sensor driver");
MODULE_LICENSE("GPL v2");
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0+
/*
* Qualcomm MSM vibrator driver
*
* Copyright (c) 2018 Brian Masney <masneyb@onstation.org>
*
* Based on qcom,pwm-vibrator.c from:
* Copyright (c) 2018 Jonathan Marek <jonathan@marek.ca>
*
* Based on msm_pwm_vibrator.c from downstream Android sources:
* Copyright (C) 2009-2014 LGE, Inc.
*/
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/gpio/consumer.h>
#include <linux/input.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#define REG_CMD_RCGR 0x00
#define REG_CFG_RCGR 0x04
#define REG_M 0x08
#define REG_N 0x0C
#define REG_D 0x10
#define REG_CBCR 0x24
#define MMSS_CC_M_DEFAULT 1
struct msm_vibrator {
struct input_dev *input;
struct mutex mutex;
struct work_struct worker;
void __iomem *base;
struct regulator *vcc;
struct clk *clk;
struct gpio_desc *enable_gpio;
u16 magnitude;
bool enabled;
};
static void msm_vibrator_write(struct msm_vibrator *vibrator, int offset,
u32 value)
{
writel(value, vibrator->base + offset);
}
static int msm_vibrator_start(struct msm_vibrator *vibrator)
{
int d_reg_val, ret = 0;
mutex_lock(&vibrator->mutex);
if (!vibrator->enabled) {
ret = clk_set_rate(vibrator->clk, 24000);
if (ret) {
dev_err(&vibrator->input->dev,
"Failed to set clock rate: %d\n", ret);
goto unlock;
}
ret = clk_prepare_enable(vibrator->clk);
if (ret) {
dev_err(&vibrator->input->dev,
"Failed to enable clock: %d\n", ret);
goto unlock;
}
ret = regulator_enable(vibrator->vcc);
if (ret) {
dev_err(&vibrator->input->dev,
"Failed to enable regulator: %d\n", ret);
clk_disable(vibrator->clk);
goto unlock;
}
gpiod_set_value_cansleep(vibrator->enable_gpio, 1);
vibrator->enabled = true;
}
d_reg_val = 127 - ((126 * vibrator->magnitude) / 0xffff);
msm_vibrator_write(vibrator, REG_CFG_RCGR,
(2 << 12) | /* dual edge mode */
(0 << 8) | /* cxo */
(7 << 0));
msm_vibrator_write(vibrator, REG_M, 1);
msm_vibrator_write(vibrator, REG_N, 128);
msm_vibrator_write(vibrator, REG_D, d_reg_val);
msm_vibrator_write(vibrator, REG_CMD_RCGR, 1);
msm_vibrator_write(vibrator, REG_CBCR, 1);
unlock:
mutex_unlock(&vibrator->mutex);
return ret;
}
static void msm_vibrator_stop(struct msm_vibrator *vibrator)
{
mutex_lock(&vibrator->mutex);
if (vibrator->enabled) {
gpiod_set_value_cansleep(vibrator->enable_gpio, 0);
regulator_disable(vibrator->vcc);
clk_disable(vibrator->clk);
vibrator->enabled = false;
}
mutex_unlock(&vibrator->mutex);
}
static void msm_vibrator_worker(struct work_struct *work)
{
struct msm_vibrator *vibrator = container_of(work,
struct msm_vibrator,
worker);
if (vibrator->magnitude)
msm_vibrator_start(vibrator);
else
msm_vibrator_stop(vibrator);
}
static int msm_vibrator_play_effect(struct input_dev *dev, void *data,
struct ff_effect *effect)
{
struct msm_vibrator *vibrator = input_get_drvdata(dev);
mutex_lock(&vibrator->mutex);
if (effect->u.rumble.strong_magnitude > 0)
vibrator->magnitude = effect->u.rumble.strong_magnitude;
else
vibrator->magnitude = effect->u.rumble.weak_magnitude;
mutex_unlock(&vibrator->mutex);
schedule_work(&vibrator->worker);
return 0;
}
static void msm_vibrator_close(struct input_dev *input)
{
struct msm_vibrator *vibrator = input_get_drvdata(input);
cancel_work_sync(&vibrator->worker);
msm_vibrator_stop(vibrator);
}
static int msm_vibrator_probe(struct platform_device *pdev)
{
struct msm_vibrator *vibrator;
struct resource *res;
int ret;
vibrator = devm_kzalloc(&pdev->dev, sizeof(*vibrator), GFP_KERNEL);
if (!vibrator)
return -ENOMEM;
vibrator->input = devm_input_allocate_device(&pdev->dev);
if (!vibrator->input)
return -ENOMEM;
vibrator->vcc = devm_regulator_get(&pdev->dev, "vcc");
if (IS_ERR(vibrator->vcc)) {
if (PTR_ERR(vibrator->vcc) != -EPROBE_DEFER)
dev_err(&pdev->dev, "Failed to get regulator: %ld\n",
PTR_ERR(vibrator->vcc));
return PTR_ERR(vibrator->vcc);
}
vibrator->enable_gpio = devm_gpiod_get(&pdev->dev, "enable",
GPIOD_OUT_LOW);
if (IS_ERR(vibrator->enable_gpio)) {
if (PTR_ERR(vibrator->enable_gpio) != -EPROBE_DEFER)
dev_err(&pdev->dev, "Failed to get enable gpio: %ld\n",
PTR_ERR(vibrator->enable_gpio));
return PTR_ERR(vibrator->enable_gpio);
}
vibrator->clk = devm_clk_get(&pdev->dev, "pwm");
if (IS_ERR(vibrator->clk)) {
if (PTR_ERR(vibrator->clk) != -EPROBE_DEFER)
dev_err(&pdev->dev, "Failed to lookup pwm clock: %ld\n",
PTR_ERR(vibrator->clk));
return PTR_ERR(vibrator->clk);
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "Failed to get platform resource\n");
return -ENODEV;
}
vibrator->base = devm_ioremap(&pdev->dev, res->start,
resource_size(res));
if (!vibrator->base) {
dev_err(&pdev->dev, "Failed to iomap resource.\n");
return -ENOMEM;
}
vibrator->enabled = false;
mutex_init(&vibrator->mutex);
INIT_WORK(&vibrator->worker, msm_vibrator_worker);
vibrator->input->name = "msm-vibrator";
vibrator->input->id.bustype = BUS_HOST;
vibrator->input->close = msm_vibrator_close;
input_set_drvdata(vibrator->input, vibrator);
input_set_capability(vibrator->input, EV_FF, FF_RUMBLE);
ret = input_ff_create_memless(vibrator->input, NULL,
msm_vibrator_play_effect);
if (ret) {
dev_err(&pdev->dev, "Failed to create ff memless: %d", ret);
return ret;
}
ret = input_register_device(vibrator->input);
if (ret) {
dev_err(&pdev->dev, "Failed to register input device: %d", ret);
return ret;
}
platform_set_drvdata(pdev, vibrator);
return 0;
}
static int __maybe_unused msm_vibrator_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct msm_vibrator *vibrator = platform_get_drvdata(pdev);
cancel_work_sync(&vibrator->worker);
if (vibrator->enabled)
msm_vibrator_stop(vibrator);
return 0;
}
static int __maybe_unused msm_vibrator_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct msm_vibrator *vibrator = platform_get_drvdata(pdev);
if (vibrator->enabled)
msm_vibrator_start(vibrator);
return 0;
}
static SIMPLE_DEV_PM_OPS(msm_vibrator_pm_ops, msm_vibrator_suspend,
msm_vibrator_resume);
static const struct of_device_id msm_vibrator_of_match[] = {
{ .compatible = "qcom,msm8226-vibrator" },
{ .compatible = "qcom,msm8974-vibrator" },
{},
};
MODULE_DEVICE_TABLE(of, msm_vibrator_of_match);
static struct platform_driver msm_vibrator_driver = {
.probe = msm_vibrator_probe,
.driver = {
.name = "msm-vibrator",
.pm = &msm_vibrator_pm_ops,
.of_match_table = of_match_ptr(msm_vibrator_of_match),
},
};
module_platform_driver(msm_vibrator_driver);
MODULE_AUTHOR("Brian Masney <masneyb@onstation.org>");
MODULE_DESCRIPTION("Qualcomm MSM vibrator driver");
MODULE_LICENSE("GPL");
...@@ -146,7 +146,7 @@ static void xenkbd_handle_mt_event(struct xenkbd_info *info, ...@@ -146,7 +146,7 @@ static void xenkbd_handle_mt_event(struct xenkbd_info *info,
break; break;
case XENKBD_MT_EV_UP: case XENKBD_MT_EV_UP:
input_mt_report_slot_state(info->mtouch, MT_TOOL_FINGER, false); input_mt_report_slot_inactive(info->mtouch);
break; break;
case XENKBD_MT_EV_SYN: case XENKBD_MT_EV_SYN:
......
...@@ -938,7 +938,7 @@ static void elan_report_contact(struct elan_tp_data *data, ...@@ -938,7 +938,7 @@ static void elan_report_contact(struct elan_tp_data *data,
input_report_abs(input, ABS_MT_TOUCH_MINOR, minor); input_report_abs(input, ABS_MT_TOUCH_MINOR, minor);
} else { } else {
input_mt_slot(input, contact_num); input_mt_slot(input, contact_num);
input_mt_report_slot_state(input, MT_TOOL_FINGER, false); input_mt_report_slot_inactive(input);
} }
} }
......
...@@ -945,6 +945,7 @@ static int i8042_pnp_kbd_probe(struct pnp_dev *dev, const struct pnp_device_id * ...@@ -945,6 +945,7 @@ static int i8042_pnp_kbd_probe(struct pnp_dev *dev, const struct pnp_device_id *
} }
i8042_pnp_id_to_string(dev->id, i8042_kbd_firmware_id, i8042_pnp_id_to_string(dev->id, i8042_kbd_firmware_id,
sizeof(i8042_kbd_firmware_id)); sizeof(i8042_kbd_firmware_id));
i8042_kbd_fwnode = dev_fwnode(&dev->dev);
/* Keyboard ports are always supposed to be wakeup-enabled */ /* Keyboard ports are always supposed to be wakeup-enabled */
device_set_wakeup_enable(&dev->dev, true); device_set_wakeup_enable(&dev->dev, true);
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <linux/i8042.h> #include <linux/i8042.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/suspend.h> #include <linux/suspend.h>
#include <linux/property.h>
#include <asm/io.h> #include <asm/io.h>
...@@ -124,6 +125,7 @@ MODULE_PARM_DESC(unmask_kbd_data, "Unconditional enable (may reveal sensitive da ...@@ -124,6 +125,7 @@ MODULE_PARM_DESC(unmask_kbd_data, "Unconditional enable (may reveal sensitive da
static bool i8042_bypass_aux_irq_test; static bool i8042_bypass_aux_irq_test;
static char i8042_kbd_firmware_id[128]; static char i8042_kbd_firmware_id[128];
static char i8042_aux_firmware_id[128]; static char i8042_aux_firmware_id[128];
static struct fwnode_handle *i8042_kbd_fwnode;
#include "i8042.h" #include "i8042.h"
...@@ -1335,6 +1337,7 @@ static int __init i8042_create_kbd_port(void) ...@@ -1335,6 +1337,7 @@ static int __init i8042_create_kbd_port(void)
strlcpy(serio->phys, I8042_KBD_PHYS_DESC, sizeof(serio->phys)); strlcpy(serio->phys, I8042_KBD_PHYS_DESC, sizeof(serio->phys));
strlcpy(serio->firmware_id, i8042_kbd_firmware_id, strlcpy(serio->firmware_id, i8042_kbd_firmware_id,
sizeof(serio->firmware_id)); sizeof(serio->firmware_id));
set_primary_fwnode(&serio->dev, i8042_kbd_fwnode);
port->serio = serio; port->serio = serio;
port->irq = I8042_KBD_IRQ; port->irq = I8042_KBD_IRQ;
......
...@@ -201,6 +201,18 @@ config TOUCHSCREEN_CHIPONE_ICN8505 ...@@ -201,6 +201,18 @@ config TOUCHSCREEN_CHIPONE_ICN8505
To compile this driver as a module, choose M here: the To compile this driver as a module, choose M here: the
module will be called chipone_icn8505. module will be called chipone_icn8505.
config TOUCHSCREEN_CY8CTMA140
tristate "cy8ctma140 touchscreen"
depends on I2C
help
Say Y here if you have a Cypress CY8CTMA140 capacitive
touchscreen also just known as "TMA140"
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called cy8ctma140.
config TOUCHSCREEN_CY8CTMG110 config TOUCHSCREEN_CY8CTMG110
tristate "cy8ctmg110 touchscreen" tristate "cy8ctmg110 touchscreen"
depends on I2C depends on I2C
......
...@@ -22,6 +22,7 @@ obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o ...@@ -22,6 +22,7 @@ obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o
obj-$(CONFIG_TOUCHSCREEN_BU21029) += bu21029_ts.o obj-$(CONFIG_TOUCHSCREEN_BU21029) += bu21029_ts.o
obj-$(CONFIG_TOUCHSCREEN_CHIPONE_ICN8318) += chipone_icn8318.o obj-$(CONFIG_TOUCHSCREEN_CHIPONE_ICN8318) += chipone_icn8318.o
obj-$(CONFIG_TOUCHSCREEN_CHIPONE_ICN8505) += chipone_icn8505.o obj-$(CONFIG_TOUCHSCREEN_CHIPONE_ICN8505) += chipone_icn8505.o
obj-$(CONFIG_TOUCHSCREEN_CY8CTMA140) += cy8ctma140.o
obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o
obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE) += cyttsp_core.o obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE) += cyttsp_core.o
obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o cyttsp_i2c_common.o obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o cyttsp_i2c_common.o
......
...@@ -822,8 +822,7 @@ static void mxt_proc_t9_message(struct mxt_data *data, u8 *message) ...@@ -822,8 +822,7 @@ static void mxt_proc_t9_message(struct mxt_data *data, u8 *message)
* have happened. * have happened.
*/ */
if (status & MXT_T9_RELEASE) { if (status & MXT_T9_RELEASE) {
input_mt_report_slot_state(input_dev, input_mt_report_slot_inactive(input_dev);
MT_TOOL_FINGER, 0);
mxt_input_sync(data); mxt_input_sync(data);
} }
...@@ -839,7 +838,7 @@ static void mxt_proc_t9_message(struct mxt_data *data, u8 *message) ...@@ -839,7 +838,7 @@ static void mxt_proc_t9_message(struct mxt_data *data, u8 *message)
input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, area); input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, area);
} else { } else {
/* Touch no longer active, close out slot */ /* Touch no longer active, close out slot */
input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, 0); input_mt_report_slot_inactive(input_dev);
} }
data->update_input = true; data->update_input = true;
...@@ -947,7 +946,7 @@ static void mxt_proc_t100_message(struct mxt_data *data, u8 *message) ...@@ -947,7 +946,7 @@ static void mxt_proc_t100_message(struct mxt_data *data, u8 *message)
dev_dbg(dev, "[%u] release\n", id); dev_dbg(dev, "[%u] release\n", id);
/* close out slot */ /* close out slot */
input_mt_report_slot_state(input_dev, 0, 0); input_mt_report_slot_inactive(input_dev);
} }
data->update_input = true; data->update_input = true;
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for Cypress CY8CTMA140 (TMA140) touchscreen
* (C) 2020 Linus Walleij <linus.walleij@linaro.org>
* (C) 2007 Cypress
* (C) 2007 Google, Inc.
*
* Inspired by the tma140_skomer.c driver in the Samsung GT-S7710 code
* drop. The GT-S7710 is codenamed "Skomer", the code also indicates
* that the same touchscreen was used in a product called "Lucas".
*
* The code drop for GT-S7710 also contains a firmware downloader and
* 15 (!) versions of the firmware drop from Cypress. But here we assume
* the firmware got downloaded to the touchscreen flash successfully and
* just use it to read the fingers. The shipped vendor driver does the
* same.
*/
#include <asm/unaligned.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/input.h>
#include <linux/input/touchscreen.h>
#include <linux/input/mt.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/i2c.h>
#include <linux/regulator/consumer.h>
#include <linux/delay.h>
#define CY8CTMA140_NAME "cy8ctma140"
#define CY8CTMA140_MAX_FINGERS 4
#define CY8CTMA140_GET_FINGERS 0x00
#define CY8CTMA140_GET_FW_INFO 0x19
/* This message also fits some bytes for touchkeys, if used */
#define CY8CTMA140_PACKET_SIZE 31
#define CY8CTMA140_INVALID_BUFFER_BIT 5
struct cy8ctma140 {
struct input_dev *input;
struct touchscreen_properties props;
struct device *dev;
struct i2c_client *client;
struct regulator_bulk_data regulators[2];
u8 prev_fingers;
u8 prev_f1id;
u8 prev_f2id;
};
static void cy8ctma140_report(struct cy8ctma140 *ts, u8 *data, int n_fingers)
{
static const u8 contact_offsets[] = { 0x03, 0x09, 0x10, 0x16 };
u8 *buf;
u16 x, y;
u8 w;
u8 id;
int slot;
int i;
for (i = 0; i < n_fingers; i++) {
buf = &data[contact_offsets[i]];
/*
* Odd contacts have contact ID in the lower nibble of
* the preceding byte, whereas even contacts have it in
* the upper nibble of the following byte.
*/
id = i % 2 ? buf[-1] & 0x0f : buf[5] >> 4;
slot = input_mt_get_slot_by_key(ts->input, id);
if (slot < 0)
continue;
x = get_unaligned_be16(buf);
y = get_unaligned_be16(buf + 2);
w = buf[4];
dev_dbg(ts->dev, "finger %d: ID %02x (%d, %d) w: %d\n",
slot, id, x, y, w);
input_mt_slot(ts->input, slot);
input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, true);
touchscreen_report_pos(ts->input, &ts->props, x, y, true);
input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, w);
}
input_mt_sync_frame(ts->input);
input_sync(ts->input);
}
static irqreturn_t cy8ctma140_irq_thread(int irq, void *d)
{
struct cy8ctma140 *ts = d;
u8 cmdbuf[] = { CY8CTMA140_GET_FINGERS };
u8 buf[CY8CTMA140_PACKET_SIZE];
struct i2c_msg msg[] = {
{
.addr = ts->client->addr,
.flags = 0,
.len = sizeof(cmdbuf),
.buf = cmdbuf,
}, {
.addr = ts->client->addr,
.flags = I2C_M_RD,
.len = sizeof(buf),
.buf = buf,
},
};
u8 n_fingers;
int ret;
ret = i2c_transfer(ts->client->adapter, msg, ARRAY_SIZE(msg));
if (ret != ARRAY_SIZE(msg)) {
if (ret < 0)
dev_err(ts->dev, "error reading message: %d\n", ret);
else
dev_err(ts->dev, "wrong number of messages\n");
goto out;
}
if (buf[1] & BIT(CY8CTMA140_INVALID_BUFFER_BIT)) {
dev_dbg(ts->dev, "invalid event\n");
goto out;
}
n_fingers = buf[2] & 0x0f;
if (n_fingers > CY8CTMA140_MAX_FINGERS) {
dev_err(ts->dev, "unexpected number of fingers: %d\n",
n_fingers);
goto out;
}
cy8ctma140_report(ts, buf, n_fingers);
out:
return IRQ_HANDLED;
}
static int cy8ctma140_init(struct cy8ctma140 *ts)
{
u8 addr[1];
u8 buf[5];
int ret;
addr[0] = CY8CTMA140_GET_FW_INFO;
ret = i2c_master_send(ts->client, addr, 1);
if (ret < 0) {
dev_err(ts->dev, "error sending FW info message\n");
return ret;
}
ret = i2c_master_recv(ts->client, buf, 5);
if (ret < 0) {
dev_err(ts->dev, "error receiving FW info message\n");
return ret;
}
if (ret != 5) {
dev_err(ts->dev, "got only %d bytes\n", ret);
return -EIO;
}
dev_dbg(ts->dev, "vendor %c%c, HW ID %.2d, FW ver %.4d\n",
buf[0], buf[1], buf[3], buf[4]);
return 0;
}
static int cy8ctma140_power_up(struct cy8ctma140 *ts)
{
int error;
error = regulator_bulk_enable(ARRAY_SIZE(ts->regulators),
ts->regulators);
if (error) {
dev_err(ts->dev, "failed to enable regulators\n");
return error;
}
msleep(250);
return 0;
}
static void cy8ctma140_power_down(struct cy8ctma140 *ts)
{
regulator_bulk_disable(ARRAY_SIZE(ts->regulators),
ts->regulators);
}
/* Called from the registered devm action */
static void cy8ctma140_power_off_action(void *d)
{
struct cy8ctma140 *ts = d;
cy8ctma140_power_down(ts);
}
static int cy8ctma140_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct cy8ctma140 *ts;
struct input_dev *input;
struct device *dev = &client->dev;
int error;
ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
if (!ts)
return -ENOMEM;
input = devm_input_allocate_device(dev);
if (!input)
return -ENOMEM;
ts->dev = dev;
ts->client = client;
ts->input = input;
input_set_capability(input, EV_ABS, ABS_MT_POSITION_X);
input_set_capability(input, EV_ABS, ABS_MT_POSITION_Y);
/* One byte for width 0..255 so this is the limit */
input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
/*
* This sets up event max/min capabilities and fuzz.
* Some DT properties are compulsory so we do not need
* to provide defaults for X/Y max or pressure max.
*
* We just initialize a very simple MT touchscreen here,
* some devices use the capability of this touchscreen to
* provide touchkeys, and in that case this needs to be
* extended to handle touchkey input.
*
* The firmware takes care of finger tracking and dropping
* invalid ranges.
*/
touchscreen_parse_properties(input, true, &ts->props);
input_abs_set_fuzz(input, ABS_MT_POSITION_X, 0);
input_abs_set_fuzz(input, ABS_MT_POSITION_Y, 0);
error = input_mt_init_slots(input, CY8CTMA140_MAX_FINGERS,
INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
if (error)
return error;
input->name = CY8CTMA140_NAME;
input->id.bustype = BUS_I2C;
input_set_drvdata(input, ts);
/*
* VCPIN is the analog voltage supply
* VDD is the digital voltage supply
* since the voltage range of VDD overlaps that of VCPIN,
* many designs to just supply both with a single voltage
* source of ~3.3 V.
*/
ts->regulators[0].supply = "vcpin";
ts->regulators[1].supply = "vdd";
error = devm_regulator_bulk_get(dev, ARRAY_SIZE(ts->regulators),
ts->regulators);
if (error) {
if (error != -EPROBE_DEFER)
dev_err(dev, "Failed to get regulators %d\n",
error);
return error;
}
error = cy8ctma140_power_up(ts);
if (error)
return error;
error = devm_add_action_or_reset(dev, cy8ctma140_power_off_action, ts);
if (error) {
dev_err(dev, "failed to install power off handler\n");
return error;
}
error = devm_request_threaded_irq(dev, client->irq,
NULL, cy8ctma140_irq_thread,
IRQF_ONESHOT, CY8CTMA140_NAME, ts);
if (error) {
dev_err(dev, "irq %d busy? error %d\n", client->irq, error);
return error;
}
error = cy8ctma140_init(ts);
if (error)
return error;
error = input_register_device(input);
if (error)
return error;
i2c_set_clientdata(client, ts);
return 0;
}
static int __maybe_unused cy8ctma140_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct cy8ctma140 *ts = i2c_get_clientdata(client);
if (!device_may_wakeup(&client->dev))
cy8ctma140_power_down(ts);
return 0;
}
static int __maybe_unused cy8ctma140_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct cy8ctma140 *ts = i2c_get_clientdata(client);
int error;
if (!device_may_wakeup(&client->dev)) {
error = cy8ctma140_power_up(ts);
if (error)
return error;
}
return 0;
}
static SIMPLE_DEV_PM_OPS(cy8ctma140_pm, cy8ctma140_suspend, cy8ctma140_resume);
static const struct i2c_device_id cy8ctma140_idtable[] = {
{ CY8CTMA140_NAME, 0 },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, cy8ctma140_idtable);
static const struct of_device_id cy8ctma140_of_match[] = {
{ .compatible = "cypress,cy8ctma140", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, cy8ctma140_of_match);
static struct i2c_driver cy8ctma140_driver = {
.driver = {
.name = CY8CTMA140_NAME,
.pm = &cy8ctma140_pm,
.of_match_table = cy8ctma140_of_match,
},
.id_table = cy8ctma140_idtable,
.probe = cy8ctma140_probe,
};
module_i2c_driver(cy8ctma140_driver);
MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
MODULE_DESCRIPTION("CY8CTMA140 TouchScreen Driver");
MODULE_LICENSE("GPL v2");
...@@ -744,8 +744,7 @@ static void cyttsp4_report_slot_liftoff(struct cyttsp4_mt_data *md, ...@@ -744,8 +744,7 @@ static void cyttsp4_report_slot_liftoff(struct cyttsp4_mt_data *md,
for (t = 0; t < max_slots; t++) { for (t = 0; t < max_slots; t++) {
input_mt_slot(md->input, t); input_mt_slot(md->input, t);
input_mt_report_slot_state(md->input, input_mt_report_slot_inactive(md->input);
MT_TOOL_FINGER, false);
} }
} }
...@@ -845,7 +844,7 @@ static void cyttsp4_final_sync(struct input_dev *input, int max_slots, int *ids) ...@@ -845,7 +844,7 @@ static void cyttsp4_final_sync(struct input_dev *input, int max_slots, int *ids)
if (ids[t]) if (ids[t])
continue; continue;
input_mt_slot(input, t); input_mt_slot(input, t);
input_mt_report_slot_state(input, MT_TOOL_FINGER, false); input_mt_report_slot_inactive(input);
} }
input_sync(input); input_sync(input);
......
...@@ -340,7 +340,7 @@ static void cyttsp_report_tchdata(struct cyttsp *ts) ...@@ -340,7 +340,7 @@ static void cyttsp_report_tchdata(struct cyttsp *ts)
continue; continue;
input_mt_slot(input, i); input_mt_slot(input, i);
input_mt_report_slot_state(input, MT_TOOL_FINGER, false); input_mt_report_slot_inactive(input);
} }
input_sync(input); input_sync(input);
......
...@@ -38,6 +38,9 @@ ...@@ -38,6 +38,9 @@
#define WORK_REGISTER_NUM_X 0x33 #define WORK_REGISTER_NUM_X 0x33
#define WORK_REGISTER_NUM_Y 0x34 #define WORK_REGISTER_NUM_Y 0x34
#define PMOD_REGISTER_ACTIVE 0x00
#define PMOD_REGISTER_HIBERNATE 0x03
#define M09_REGISTER_THRESHOLD 0x80 #define M09_REGISTER_THRESHOLD 0x80
#define M09_REGISTER_GAIN 0x92 #define M09_REGISTER_GAIN 0x92
#define M09_REGISTER_OFFSET 0x93 #define M09_REGISTER_OFFSET 0x93
...@@ -53,6 +56,7 @@ ...@@ -53,6 +56,7 @@
#define WORK_REGISTER_OPMODE 0x3c #define WORK_REGISTER_OPMODE 0x3c
#define FACTORY_REGISTER_OPMODE 0x01 #define FACTORY_REGISTER_OPMODE 0x01
#define PMOD_REGISTER_OPMODE 0xa5
#define TOUCH_EVENT_DOWN 0x00 #define TOUCH_EVENT_DOWN 0x00
#define TOUCH_EVENT_UP 0x01 #define TOUCH_EVENT_UP 0x01
...@@ -65,6 +69,12 @@ ...@@ -65,6 +69,12 @@
#define EDT_RAW_DATA_RETRIES 100 #define EDT_RAW_DATA_RETRIES 100
#define EDT_RAW_DATA_DELAY 1000 /* usec */ #define EDT_RAW_DATA_DELAY 1000 /* usec */
enum edt_pmode {
EDT_PMODE_NOT_SUPPORTED,
EDT_PMODE_HIBERNATE,
EDT_PMODE_POWEROFF,
};
enum edt_ver { enum edt_ver {
EDT_M06, EDT_M06,
EDT_M09, EDT_M09,
...@@ -103,6 +113,7 @@ struct edt_ft5x06_ts_data { ...@@ -103,6 +113,7 @@ struct edt_ft5x06_ts_data {
struct mutex mutex; struct mutex mutex;
bool factory_mode; bool factory_mode;
enum edt_pmode suspend_mode;
int threshold; int threshold;
int gain; int gain;
int offset; int offset;
...@@ -527,6 +538,29 @@ static const struct attribute_group edt_ft5x06_attr_group = { ...@@ -527,6 +538,29 @@ static const struct attribute_group edt_ft5x06_attr_group = {
.attrs = edt_ft5x06_attrs, .attrs = edt_ft5x06_attrs,
}; };
static void edt_ft5x06_restore_reg_parameters(struct edt_ft5x06_ts_data *tsdata)
{
struct edt_reg_addr *reg_addr = &tsdata->reg_addr;
edt_ft5x06_register_write(tsdata, reg_addr->reg_threshold,
tsdata->threshold);
edt_ft5x06_register_write(tsdata, reg_addr->reg_gain,
tsdata->gain);
if (reg_addr->reg_offset != NO_REGISTER)
edt_ft5x06_register_write(tsdata, reg_addr->reg_offset,
tsdata->offset);
if (reg_addr->reg_offset_x != NO_REGISTER)
edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_x,
tsdata->offset_x);
if (reg_addr->reg_offset_y != NO_REGISTER)
edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_y,
tsdata->offset_y);
if (reg_addr->reg_report_rate != NO_REGISTER)
edt_ft5x06_register_write(tsdata, reg_addr->reg_report_rate,
tsdata->report_rate);
}
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
static int edt_ft5x06_factory_mode(struct edt_ft5x06_ts_data *tsdata) static int edt_ft5x06_factory_mode(struct edt_ft5x06_ts_data *tsdata)
{ {
...@@ -592,7 +626,6 @@ static int edt_ft5x06_work_mode(struct edt_ft5x06_ts_data *tsdata) ...@@ -592,7 +626,6 @@ static int edt_ft5x06_work_mode(struct edt_ft5x06_ts_data *tsdata)
{ {
struct i2c_client *client = tsdata->client; struct i2c_client *client = tsdata->client;
int retries = EDT_SWITCH_MODE_RETRIES; int retries = EDT_SWITCH_MODE_RETRIES;
struct edt_reg_addr *reg_addr = &tsdata->reg_addr;
int ret; int ret;
int error; int error;
...@@ -624,24 +657,7 @@ static int edt_ft5x06_work_mode(struct edt_ft5x06_ts_data *tsdata) ...@@ -624,24 +657,7 @@ static int edt_ft5x06_work_mode(struct edt_ft5x06_ts_data *tsdata)
kfree(tsdata->raw_buffer); kfree(tsdata->raw_buffer);
tsdata->raw_buffer = NULL; tsdata->raw_buffer = NULL;
/* restore parameters */ edt_ft5x06_restore_reg_parameters(tsdata);
edt_ft5x06_register_write(tsdata, reg_addr->reg_threshold,
tsdata->threshold);
edt_ft5x06_register_write(tsdata, reg_addr->reg_gain,
tsdata->gain);
if (reg_addr->reg_offset != NO_REGISTER)
edt_ft5x06_register_write(tsdata, reg_addr->reg_offset,
tsdata->offset);
if (reg_addr->reg_offset_x != NO_REGISTER)
edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_x,
tsdata->offset_x);
if (reg_addr->reg_offset_y != NO_REGISTER)
edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_y,
tsdata->offset_y);
if (reg_addr->reg_report_rate != NO_REGISTER)
edt_ft5x06_register_write(tsdata, reg_addr->reg_report_rate,
tsdata->report_rate);
enable_irq(client->irq); enable_irq(client->irq);
return 0; return 0;
...@@ -762,9 +778,8 @@ static const struct file_operations debugfs_raw_data_fops = { ...@@ -762,9 +778,8 @@ static const struct file_operations debugfs_raw_data_fops = {
.read = edt_ft5x06_debugfs_raw_data_read, .read = edt_ft5x06_debugfs_raw_data_read,
}; };
static void static void edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata,
edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata, const char *debugfs_name)
const char *debugfs_name)
{ {
tsdata->debug_dir = debugfs_create_dir(debugfs_name, NULL); tsdata->debug_dir = debugfs_create_dir(debugfs_name, NULL);
...@@ -777,8 +792,7 @@ edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata, ...@@ -777,8 +792,7 @@ edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata,
tsdata->debug_dir, tsdata, &debugfs_raw_data_fops); tsdata->debug_dir, tsdata, &debugfs_raw_data_fops);
} }
static void static void edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata)
edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata)
{ {
debugfs_remove_recursive(tsdata->debug_dir); debugfs_remove_recursive(tsdata->debug_dir);
kfree(tsdata->raw_buffer); kfree(tsdata->raw_buffer);
...@@ -786,14 +800,17 @@ edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata) ...@@ -786,14 +800,17 @@ edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata)
#else #else
static inline void static int edt_ft5x06_factory_mode(struct edt_ft5x06_ts_data *tsdata)
edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata, {
const char *debugfs_name) return -ENOSYS;
}
static void edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata,
const char *debugfs_name)
{ {
} }
static inline void static void edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata)
edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata)
{ {
} }
...@@ -938,19 +955,25 @@ static void edt_ft5x06_ts_get_defaults(struct device *dev, ...@@ -938,19 +955,25 @@ static void edt_ft5x06_ts_get_defaults(struct device *dev,
error = device_property_read_u32(dev, "offset", &val); error = device_property_read_u32(dev, "offset", &val);
if (!error) { if (!error) {
edt_ft5x06_register_write(tsdata, reg_addr->reg_offset, val); if (reg_addr->reg_offset != NO_REGISTER)
edt_ft5x06_register_write(tsdata,
reg_addr->reg_offset, val);
tsdata->offset = val; tsdata->offset = val;
} }
error = device_property_read_u32(dev, "offset-x", &val); error = device_property_read_u32(dev, "offset-x", &val);
if (!error) { if (!error) {
edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_x, val); if (reg_addr->reg_offset_x != NO_REGISTER)
edt_ft5x06_register_write(tsdata,
reg_addr->reg_offset_x, val);
tsdata->offset_x = val; tsdata->offset_x = val;
} }
error = device_property_read_u32(dev, "offset-y", &val); error = device_property_read_u32(dev, "offset-y", &val);
if (!error) { if (!error) {
edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_y, val); if (reg_addr->reg_offset_y != NO_REGISTER)
edt_ft5x06_register_write(tsdata,
reg_addr->reg_offset_y, val);
tsdata->offset_y = val; tsdata->offset_y = val;
} }
} }
...@@ -1114,6 +1137,19 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, ...@@ -1114,6 +1137,19 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client,
return error; return error;
} }
/*
* Check which sleep modes we can support. Power-off requieres the
* reset-pin to ensure correct power-down/power-up behaviour. Start with
* the EDT_PMODE_POWEROFF test since this is the deepest possible sleep
* mode.
*/
if (tsdata->reset_gpio)
tsdata->suspend_mode = EDT_PMODE_POWEROFF;
else if (tsdata->wake_gpio)
tsdata->suspend_mode = EDT_PMODE_HIBERNATE;
else
tsdata->suspend_mode = EDT_PMODE_NOT_SUPPORTED;
if (tsdata->wake_gpio) { if (tsdata->wake_gpio) {
usleep_range(5000, 6000); usleep_range(5000, 6000);
gpiod_set_value_cansleep(tsdata->wake_gpio, 1); gpiod_set_value_cansleep(tsdata->wake_gpio, 1);
...@@ -1227,6 +1263,102 @@ static int edt_ft5x06_ts_remove(struct i2c_client *client) ...@@ -1227,6 +1263,102 @@ static int edt_ft5x06_ts_remove(struct i2c_client *client)
return 0; return 0;
} }
static int __maybe_unused edt_ft5x06_ts_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
struct gpio_desc *reset_gpio = tsdata->reset_gpio;
int ret;
if (device_may_wakeup(dev))
return 0;
if (tsdata->suspend_mode == EDT_PMODE_NOT_SUPPORTED)
return 0;
/* Enter hibernate mode. */
ret = edt_ft5x06_register_write(tsdata, PMOD_REGISTER_OPMODE,
PMOD_REGISTER_HIBERNATE);
if (ret)
dev_warn(dev, "Failed to set hibernate mode\n");
if (tsdata->suspend_mode == EDT_PMODE_HIBERNATE)
return 0;
/*
* Power-off according the datasheet. Cut the power may leaf the irq
* line in an undefined state depending on the host pull resistor
* settings. Disable the irq to avoid adjusting each host till the
* device is back in a full functional state.
*/
disable_irq(tsdata->client->irq);
gpiod_set_value_cansleep(reset_gpio, 1);
usleep_range(1000, 2000);
ret = regulator_disable(tsdata->vcc);
if (ret)
dev_warn(dev, "Failed to disable vcc\n");
return 0;
}
static int __maybe_unused edt_ft5x06_ts_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
int ret = 0;
if (device_may_wakeup(dev))
return 0;
if (tsdata->suspend_mode == EDT_PMODE_NOT_SUPPORTED)
return 0;
if (tsdata->suspend_mode == EDT_PMODE_POWEROFF) {
struct gpio_desc *reset_gpio = tsdata->reset_gpio;
/*
* We can't check if the regulator is a dummy or a real
* regulator. So we need to specify the 5ms reset time (T_rst)
* here instead of the 100us T_rtp time. We also need to wait
* 300ms in case it was a real supply and the power was cutted
* of. Toggle the reset pin is also a way to exit the hibernate
* mode.
*/
gpiod_set_value_cansleep(reset_gpio, 1);
usleep_range(5000, 6000);
ret = regulator_enable(tsdata->vcc);
if (ret) {
dev_err(dev, "Failed to enable vcc\n");
return ret;
}
usleep_range(1000, 2000);
gpiod_set_value_cansleep(reset_gpio, 0);
msleep(300);
edt_ft5x06_restore_reg_parameters(tsdata);
enable_irq(tsdata->client->irq);
if (tsdata->factory_mode)
ret = edt_ft5x06_factory_mode(tsdata);
} else {
struct gpio_desc *wake_gpio = tsdata->wake_gpio;
gpiod_set_value_cansleep(wake_gpio, 0);
usleep_range(5000, 6000);
gpiod_set_value_cansleep(wake_gpio, 1);
}
return ret;
}
static SIMPLE_DEV_PM_OPS(edt_ft5x06_ts_pm_ops,
edt_ft5x06_ts_suspend, edt_ft5x06_ts_resume);
static const struct edt_i2c_chip_data edt_ft5x06_data = { static const struct edt_i2c_chip_data edt_ft5x06_data = {
.max_support_points = 5, .max_support_points = 5,
}; };
...@@ -1265,6 +1397,8 @@ static struct i2c_driver edt_ft5x06_ts_driver = { ...@@ -1265,6 +1397,8 @@ static struct i2c_driver edt_ft5x06_ts_driver = {
.driver = { .driver = {
.name = "edt_ft5x06", .name = "edt_ft5x06",
.of_match_table = edt_ft5x06_of_match, .of_match_table = edt_ft5x06_of_match,
.pm = &edt_ft5x06_ts_pm_ops,
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
}, },
.id_table = edt_ft5x06_ts_id, .id_table = edt_ft5x06_ts_id,
.probe = edt_ft5x06_ts_probe, .probe = edt_ft5x06_ts_probe,
......
This diff is collapsed.
...@@ -391,7 +391,7 @@ static void mip4_clear_input(struct mip4_ts *ts) ...@@ -391,7 +391,7 @@ static void mip4_clear_input(struct mip4_ts *ts)
/* Screen */ /* Screen */
for (i = 0; i < MIP4_MAX_FINGERS; i++) { for (i = 0; i < MIP4_MAX_FINGERS; i++) {
input_mt_slot(ts->input, i); input_mt_slot(ts->input, i);
input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, 0); input_mt_report_slot_inactive(ts->input);
} }
/* Keys */ /* Keys */
...@@ -534,7 +534,7 @@ static void mip4_report_touch(struct mip4_ts *ts, u8 *packet) ...@@ -534,7 +534,7 @@ static void mip4_report_touch(struct mip4_ts *ts, u8 *packet)
} else { } else {
/* Release event */ /* Release event */
input_mt_slot(ts->input, id); input_mt_slot(ts->input, id);
input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, 0); input_mt_report_slot_inactive(ts->input);
} }
input_mt_sync_frame(ts->input); input_mt_sync_frame(ts->input);
......
...@@ -54,6 +54,7 @@ ...@@ -54,6 +54,7 @@
enum mms_type { enum mms_type {
TYPE_MMS114 = 114, TYPE_MMS114 = 114,
TYPE_MMS152 = 152, TYPE_MMS152 = 152,
TYPE_MMS345L = 345,
}; };
struct mms114_data { struct mms114_data {
...@@ -250,6 +251,15 @@ static int mms114_get_version(struct mms114_data *data) ...@@ -250,6 +251,15 @@ static int mms114_get_version(struct mms114_data *data)
int error; int error;
switch (data->type) { switch (data->type) {
case TYPE_MMS345L:
error = __mms114_read_reg(data, MMS152_FW_REV, 3, buf);
if (error)
return error;
dev_info(dev, "TSP FW Rev: bootloader 0x%x / core 0x%x / config 0x%x\n",
buf[0], buf[1], buf[2]);
break;
case TYPE_MMS152: case TYPE_MMS152:
error = __mms114_read_reg(data, MMS152_FW_REV, 3, buf); error = __mms114_read_reg(data, MMS152_FW_REV, 3, buf);
if (error) if (error)
...@@ -287,8 +297,8 @@ static int mms114_setup_regs(struct mms114_data *data) ...@@ -287,8 +297,8 @@ static int mms114_setup_regs(struct mms114_data *data)
if (error < 0) if (error < 0)
return error; return error;
/* MMS152 has no configuration or power on registers */ /* Only MMS114 has configuration and power on registers */
if (data->type == TYPE_MMS152) if (data->type != TYPE_MMS114)
return 0; return 0;
error = mms114_set_active(data, true); error = mms114_set_active(data, true);
...@@ -547,7 +557,7 @@ static int __maybe_unused mms114_suspend(struct device *dev) ...@@ -547,7 +557,7 @@ static int __maybe_unused mms114_suspend(struct device *dev)
/* Release all touch */ /* Release all touch */
for (id = 0; id < MMS114_MAX_TOUCH; id++) { for (id = 0; id < MMS114_MAX_TOUCH; id++) {
input_mt_slot(input_dev, id); input_mt_slot(input_dev, id);
input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false); input_mt_report_slot_inactive(input_dev);
} }
input_mt_report_pointer_emulation(input_dev, true); input_mt_report_pointer_emulation(input_dev, true);
...@@ -597,6 +607,9 @@ static const struct of_device_id mms114_dt_match[] = { ...@@ -597,6 +607,9 @@ static const struct of_device_id mms114_dt_match[] = {
}, { }, {
.compatible = "melfas,mms152", .compatible = "melfas,mms152",
.data = (void *)TYPE_MMS152, .data = (void *)TYPE_MMS152,
}, {
.compatible = "melfas,mms345l",
.data = (void *)TYPE_MMS345L,
}, },
{ } { }
}; };
......
...@@ -100,7 +100,7 @@ static void rpi_ts_poll(struct input_dev *input) ...@@ -100,7 +100,7 @@ static void rpi_ts_poll(struct input_dev *input)
released_ids = ts->known_ids & ~modified_ids; released_ids = ts->known_ids & ~modified_ids;
for_each_set_bit(i, &released_ids, RPI_TS_MAX_SUPPORTED_POINTS) { for_each_set_bit(i, &released_ids, RPI_TS_MAX_SUPPORTED_POINTS) {
input_mt_slot(input, i); input_mt_slot(input, i);
input_mt_report_slot_state(input, MT_TOOL_FINGER, 0); input_mt_report_slot_inactive(input);
modified_ids &= ~(BIT(i)); modified_ids &= ~(BIT(i));
} }
ts->known_ids = modified_ids; ts->known_ids = modified_ids;
......
...@@ -198,7 +198,7 @@ static void stmfts_report_contact_release(struct stmfts_data *sdata, ...@@ -198,7 +198,7 @@ static void stmfts_report_contact_release(struct stmfts_data *sdata,
u8 slot_id = (event[0] & STMFTS_MASK_TOUCH_ID) >> 4; u8 slot_id = (event[0] & STMFTS_MASK_TOUCH_ID) >> 4;
input_mt_slot(sdata->input, slot_id); input_mt_slot(sdata->input, slot_id);
input_mt_report_slot_state(sdata->input, MT_TOOL_FINGER, false); input_mt_report_slot_inactive(sdata->input);
input_sync(sdata->input); input_sync(sdata->input);
} }
......
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _GP2AP002A00F_H_
#define _GP2AP002A00F_H_
#include <linux/i2c.h>
#define GP2A_I2C_NAME "gp2ap002a00f"
/**
* struct gp2a_platform_data - Sharp gp2ap002a00f proximity platform data
* @vout_gpio: The gpio connected to the object detected pin (VOUT)
* @wakeup: Set to true if the proximity can wake the device from suspend
* @hw_setup: Callback for setting up hardware such as gpios and vregs
* @hw_shutdown: Callback for properly shutting down hardware
*/
struct gp2a_platform_data {
int vout_gpio;
bool wakeup;
int (*hw_setup)(struct i2c_client *client);
int (*hw_shutdown)(struct i2c_client *client);
};
#endif
...@@ -100,6 +100,11 @@ static inline bool input_is_mt_axis(int axis) ...@@ -100,6 +100,11 @@ static inline bool input_is_mt_axis(int axis)
bool input_mt_report_slot_state(struct input_dev *dev, bool input_mt_report_slot_state(struct input_dev *dev,
unsigned int tool_type, bool active); unsigned int tool_type, bool active);
static inline void input_mt_report_slot_inactive(struct input_dev *dev)
{
input_mt_report_slot_state(dev, 0, false);
}
void input_mt_report_finger_count(struct input_dev *dev, int count); void input_mt_report_finger_count(struct input_dev *dev, int count);
void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count); void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count);
void input_mt_drop_unused(struct input_dev *dev); void input_mt_drop_unused(struct input_dev *dev);
......
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