Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
Pull input subsystem updates from Dmitry Torokhov: "Drivers, drivers, drivers... No interesting input core changes this time" * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (74 commits) Input: elan_i2c - use iap_version to get firmware information Input: max8997_haptic - fix module alias Input: elan_i2c - fix typos for validpage_count Input: psmouse - add small delay for IBM trackpoint pass-through mode Input: synaptics - fix handling of disabling gesture mode Input: elan_i2c - enable ELAN0100 acpi panels Input: gpio-keys - report error when disabling unsupported key Input: sur40 - fix error return code Input: sentelic - silence some underflow warnings Input: zhenhua - switch to using bitrev8() Input: cros_ec_keyb - replace KEYBOARD_CROS_EC dependency Input: cap11xx - add LED support Input: elants_i2c - fix for devm_gpiod_get API change Input: elan_i2c - enable asynchronous probing Input: elants_i2c - enable asynchronous probing Input: elants_i2c - wire up regulator support Input: do not emit unneeded EV_SYN when suspending Input: elants_i2c - disable idle mode before updating firmware MAINTAINERS: Add maintainer for atmel_mxt_ts Input: atmel_mxt_ts - remove warning on zero T44 count ...
This commit is contained in:
commit
51e771c0d2
@ -64,7 +64,7 @@ Optional properties:
|
||||
pendown-gpio (u32).
|
||||
pendown-gpio GPIO handle describing the pin the !PENIRQ
|
||||
line is connected to.
|
||||
linux,wakeup use any event on touchscreen as wakeup event.
|
||||
wakeup-source use any event on touchscreen as wakeup event.
|
||||
|
||||
|
||||
Example for a TSC2046 chip connected to an McSPI controller of an OMAP SoC::
|
||||
|
@ -55,5 +55,24 @@ i2c_controller {
|
||||
<105>, /* KEY_LEFT */
|
||||
<109>, /* KEY_PAGEDOWN */
|
||||
<104>; /* KEY_PAGEUP */
|
||||
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
usr@0 {
|
||||
label = "cap11xx:green:usr0";
|
||||
reg = <0>;
|
||||
};
|
||||
|
||||
usr@1 {
|
||||
label = "cap11xx:green:usr1";
|
||||
reg = <1>;
|
||||
};
|
||||
|
||||
alive@2 {
|
||||
label = "cap11xx:green:alive";
|
||||
reg = <2>;
|
||||
linux,default_trigger = "heartbeat";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
44
Documentation/devicetree/bindings/input/cypress,cyapa.txt
Normal file
44
Documentation/devicetree/bindings/input/cypress,cyapa.txt
Normal file
@ -0,0 +1,44 @@
|
||||
Cypress I2C Touchpad
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "cypress,cyapa".
|
||||
- reg: I2C address of the chip.
|
||||
- interrupt-parent: a phandle for the interrupt controller (see interrupt
|
||||
binding[0]).
|
||||
- interrupts: interrupt to which the chip is connected (see interrupt
|
||||
binding[0]).
|
||||
|
||||
Optional properties:
|
||||
- wakeup-source: touchpad 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]).
|
||||
- vcc-supply: a phandle for the regulator supplying 3.3V power.
|
||||
|
||||
[0]: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
|
||||
[1]: Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
|
||||
|
||||
Example:
|
||||
&i2c0 {
|
||||
/* ... */
|
||||
|
||||
/* Cypress Gen3 touchpad */
|
||||
touchpad@67 {
|
||||
compatible = "cypress,cyapa";
|
||||
reg = <0x24>;
|
||||
interrupt-parent = <&gpio>;
|
||||
interrupts = <2 IRQ_TYPE_EDGE_FALLING>; /* GPIO 2 */
|
||||
wakeup-source;
|
||||
};
|
||||
|
||||
/* Cypress Gen5 and later touchpad */
|
||||
touchpad@24 {
|
||||
compatible = "cypress,cyapa";
|
||||
reg = <0x24>;
|
||||
interrupt-parent = <&gpio>;
|
||||
interrupts = <2 IRQ_TYPE_EDGE_FALLING>; /* GPIO 2 */
|
||||
wakeup-source;
|
||||
};
|
||||
|
||||
/* ... */
|
||||
};
|
@ -13,6 +13,9 @@ Optional properties:
|
||||
- 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
|
||||
|
@ -20,7 +20,7 @@ Optional subnode-properties:
|
||||
If not specified defaults to <1> == EV_KEY.
|
||||
- debounce-interval: Debouncing interval time in milliseconds.
|
||||
If not specified defaults to 5.
|
||||
- gpio-key,wakeup: Boolean, button can wake-up the system.
|
||||
- wakeup-source: Boolean, button can wake-up the system.
|
||||
|
||||
Example nodes:
|
||||
|
||||
|
@ -23,7 +23,7 @@ Optional subnode-properties:
|
||||
If not specified defaults to <1> == EV_KEY.
|
||||
- debounce-interval: Debouncing interval time in milliseconds.
|
||||
If not specified defaults to 5.
|
||||
- gpio-key,wakeup: Boolean, button can wake-up the system.
|
||||
- wakeup-source: Boolean, button can wake-up the system.
|
||||
- linux,can-disable: Boolean, indicates that button is connected
|
||||
to dedicated (not shared) interrupt which can be disabled to
|
||||
suppress events from the button.
|
||||
|
@ -19,7 +19,7 @@ Required Properties:
|
||||
|
||||
Optional Properties:
|
||||
- linux,no-autorepeat: do no enable autorepeat feature.
|
||||
- linux,wakeup: use any event on keypad as wakeup event.
|
||||
- wakeup-source: use any event on keypad as wakeup event.
|
||||
- debounce-delay-ms: debounce interval in milliseconds
|
||||
- col-scan-delay-us: delay, measured in microseconds, that is needed
|
||||
before we can scan keypad after activating column gpio
|
||||
|
@ -33,7 +33,7 @@ PROPERTIES
|
||||
Value type: <bool>
|
||||
Definition: don't enable autorepeat feature.
|
||||
|
||||
- linux,keypad-wakeup:
|
||||
- wakeup-source:
|
||||
Usage: optional
|
||||
Value type: <bool>
|
||||
Definition: use any event on keypad as wakeup event.
|
||||
|
@ -36,9 +36,11 @@ Required Board Specific Properties:
|
||||
- pinctrl-0: Should specify pin control groups used for this controller.
|
||||
- pinctrl-names: Should contain only one value - "default".
|
||||
|
||||
Optional Properties:
|
||||
- wakeup-source: use any event on keypad as wakeup event.
|
||||
|
||||
Optional Properties specific to linux:
|
||||
- linux,keypad-no-autorepeat: do no enable autorepeat feature.
|
||||
- linux,keypad-wakeup: use any event on keypad as wakeup event.
|
||||
|
||||
|
||||
Example:
|
||||
|
@ -8,6 +8,9 @@ Required properties:
|
||||
- touchscreen-size-x: horizontal resolution of touchscreen (in pixels)
|
||||
- touchscreen-size-y: vertical resolution of touchscreen (in pixels)
|
||||
|
||||
Optional properties:
|
||||
- reset-gpio: GPIO connected to the RESET line of the chip
|
||||
|
||||
Example:
|
||||
|
||||
i2c@00000000 {
|
||||
|
@ -4,12 +4,12 @@ Required properties:
|
||||
- compatible: must be "neonode,zforce"
|
||||
- reg: I2C address of the chip
|
||||
- interrupts: interrupt to which the chip is connected
|
||||
- gpios: gpios the chip is connected to
|
||||
first one is the interrupt gpio and second one the reset gpio
|
||||
- reset-gpios: reset gpio the chip is connected to
|
||||
- x-size: horizontal resolution of touchscreen
|
||||
- y-size: vertical resolution of touchscreen
|
||||
|
||||
Optional properties:
|
||||
- irq-gpios : interrupt gpio the chip is connected to
|
||||
- vdd-supply: Regulator controlling the controller supply
|
||||
|
||||
Example:
|
||||
@ -23,8 +23,8 @@ Example:
|
||||
interrupts = <2 0>;
|
||||
vdd-supply = <®_zforce_vdd>;
|
||||
|
||||
gpios = <&gpio5 6 0>, /* INT */
|
||||
<&gpio5 9 0>; /* RST */
|
||||
reset-gpios = <&gpio5 9 0>; /* RST */
|
||||
irq-gpios = <&gpio5 6 0>; /* IRQ, optional */
|
||||
|
||||
x-size = <800>;
|
||||
y-size = <600>;
|
||||
|
@ -55,7 +55,7 @@ Optional nodes:
|
||||
- linux,keymap: the definition can be found in
|
||||
bindings/input/matrix-keymap.txt
|
||||
- linux,no-autorepeat: do no enable autorepeat feature.
|
||||
- linux,wakeup: use any event on keypad as wakeup event.
|
||||
- wakeup-source: use any event on keypad as wakeup event.
|
||||
|
||||
Example:
|
||||
|
||||
@ -84,7 +84,6 @@ tc35893@44 {
|
||||
keypad,num-columns = <8>;
|
||||
keypad,num-rows = <8>;
|
||||
linux,no-autorepeat;
|
||||
linux,wakeup;
|
||||
linux,keymap = <0x0301006b
|
||||
0x04010066
|
||||
0x06040072
|
||||
@ -103,5 +102,6 @@ tc35893@44 {
|
||||
0x01030039
|
||||
0x07060069
|
||||
0x050500d9>;
|
||||
wakeup-source;
|
||||
};
|
||||
};
|
||||
|
@ -55,6 +55,7 @@ cortina Cortina Systems, Inc.
|
||||
cosmic Cosmic Circuits
|
||||
crystalfontz Crystalfontz America, Inc.
|
||||
cubietech Cubietech, Ltd.
|
||||
cypress Cypress Semiconductor Corporation
|
||||
dallas Maxim Integrated Products (formerly Dallas Semiconductor)
|
||||
davicom DAVICOM Semiconductor, Inc.
|
||||
delta Delta Electronics, Inc.
|
||||
|
@ -1317,6 +1317,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
|
||||
<bus_id>,<clkrate>
|
||||
|
||||
i8042.debug [HW] Toggle i8042 debug mode
|
||||
i8042.unmask_kbd_data
|
||||
[HW] Enable printing of interrupt data from the KBD port
|
||||
(disabled by default, and as a pre-condition
|
||||
requires that i8042.debug=1 be enabled)
|
||||
i8042.direct [HW] Put keyboard port into non-translated mode
|
||||
i8042.dumbkbd [HW] Pretend that controller can only read data from
|
||||
keyboard and cannot control its state
|
||||
|
@ -1932,6 +1932,14 @@ W: http://atmelwlandriver.sourceforge.net/
|
||||
S: Maintained
|
||||
F: drivers/net/wireless/atmel*
|
||||
|
||||
ATMEL MAXTOUCH DRIVER
|
||||
M: Nick Dyer <nick.dyer@itdev.co.uk>
|
||||
T: git git://github.com/atmel-maxtouch/linux.git
|
||||
S: Supported
|
||||
F: Documentation/devicetree/bindings/input/atmel,maxtouch.txt
|
||||
F: drivers/input/touchscreen/atmel_mxt_ts.c
|
||||
F: include/linux/platform_data/atmel_mxt_ts.h
|
||||
|
||||
ATTO EXPRESSSAS SAS/SATA RAID SCSI DRIVER
|
||||
M: Bradley Grove <linuxdrivers@attotech.com>
|
||||
L: linux-scsi@vger.kernel.org
|
||||
|
@ -2293,6 +2293,8 @@ static const struct hid_device_id hid_ignore_list[] = {
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DELORME, USB_DEVICE_ID_DELORME_EM_LT20) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, 0x0004) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_DREAM_CHEEKY, 0x000a) },
|
||||
{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, 0x0400) },
|
||||
{ HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, 0x0401) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ETT, USB_DEVICE_ID_TC5UH) },
|
||||
{ HID_USB_DEVICE(USB_VENDOR_ID_ETT, USB_DEVICE_ID_TC4UM) },
|
||||
|
@ -343,8 +343,7 @@ int input_ff_create(struct input_dev *dev, unsigned int max_effects)
|
||||
__set_bit(EV_FF, dev->evbit);
|
||||
|
||||
/* Copy "true" bits into ff device bitmap */
|
||||
for (i = 0; i <= FF_MAX; i++)
|
||||
if (test_bit(i, dev->ffbit))
|
||||
for_each_set_bit(i, dev->ffbit, FF_CNT)
|
||||
__set_bit(i, ff->ffbit);
|
||||
|
||||
/* we can emulate RUMBLE with periodic effects */
|
||||
|
@ -674,13 +674,19 @@ EXPORT_SYMBOL(input_close_device);
|
||||
*/
|
||||
static void input_dev_release_keys(struct input_dev *dev)
|
||||
{
|
||||
bool need_sync = false;
|
||||
int code;
|
||||
|
||||
if (is_event_supported(EV_KEY, dev->evbit, EV_MAX)) {
|
||||
for_each_set_bit(code, dev->key, KEY_CNT)
|
||||
for_each_set_bit(code, dev->key, KEY_CNT) {
|
||||
input_pass_event(dev, EV_KEY, code, 0);
|
||||
memset(dev->key, 0, sizeof(dev->key));
|
||||
need_sync = true;
|
||||
}
|
||||
|
||||
if (need_sync)
|
||||
input_pass_event(dev, EV_SYN, SYN_REPORT, 1);
|
||||
|
||||
memset(dev->key, 0, sizeof(dev->key));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -859,8 +859,7 @@ static int joydev_connect(struct input_handler *handler, struct input_dev *dev,
|
||||
joydev->handle.handler = handler;
|
||||
joydev->handle.private = joydev;
|
||||
|
||||
for (i = 0; i < ABS_CNT; i++)
|
||||
if (test_bit(i, dev->absbit)) {
|
||||
for_each_set_bit(i, dev->absbit, ABS_CNT) {
|
||||
joydev->absmap[i] = joydev->nabs;
|
||||
joydev->abspam[joydev->nabs] = i;
|
||||
joydev->nabs++;
|
||||
|
@ -47,6 +47,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/bitrev.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/serio.h>
|
||||
|
||||
@ -72,16 +73,6 @@ struct zhenhua {
|
||||
char phys[32];
|
||||
};
|
||||
|
||||
|
||||
/* bits in all incoming bytes needs to be "reversed" */
|
||||
static int zhenhua_bitreverse(int x)
|
||||
{
|
||||
x = ((x & 0xaa) >> 1) | ((x & 0x55) << 1);
|
||||
x = ((x & 0xcc) >> 2) | ((x & 0x33) << 2);
|
||||
x = ((x & 0xf0) >> 4) | ((x & 0x0f) << 4);
|
||||
return x;
|
||||
}
|
||||
|
||||
/*
|
||||
* zhenhua_process_packet() decodes packets the driver receives from the
|
||||
* RC transmitter. It updates the data accordingly.
|
||||
@ -120,7 +111,7 @@ static irqreturn_t zhenhua_interrupt(struct serio *serio, unsigned char data, un
|
||||
return IRQ_HANDLED; /* wrong MSB -- ignore this byte */
|
||||
|
||||
if (zhenhua->idx < ZHENHUA_MAX_LENGTH)
|
||||
zhenhua->data[zhenhua->idx++] = zhenhua_bitreverse(data);
|
||||
zhenhua->data[zhenhua->idx++] = bitrev8(data);
|
||||
|
||||
if (zhenhua->idx == ZHENHUA_MAX_LENGTH) {
|
||||
zhenhua_process_packet(zhenhua);
|
||||
|
@ -187,7 +187,7 @@ config KEYBOARD_EP93XX
|
||||
|
||||
config KEYBOARD_GPIO
|
||||
tristate "GPIO Buttons"
|
||||
depends on GPIOLIB
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
help
|
||||
This driver implements support for buttons connected
|
||||
to GPIO pins of various CPUs (and some other chips).
|
||||
@ -253,7 +253,7 @@ config KEYBOARD_TCA8418
|
||||
|
||||
config KEYBOARD_MATRIX
|
||||
tristate "GPIO driven matrix keypad support"
|
||||
depends on GPIOLIB
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
select INPUT_MATRIXKMAP
|
||||
help
|
||||
Enable support for GPIO driven matrix keypad.
|
||||
@ -689,7 +689,7 @@ config KEYBOARD_W90P910
|
||||
config KEYBOARD_CROS_EC
|
||||
tristate "ChromeOS EC keyboard"
|
||||
select INPUT_MATRIXKMAP
|
||||
depends on CROS_EC_PROTO
|
||||
depends on MFD_CROS_EC
|
||||
help
|
||||
Say Y here to enable the matrix keyboard used by ChromeOS devices
|
||||
and implemented on the ChromeOS EC. You must enable one bus option
|
||||
|
@ -1097,7 +1097,6 @@ MODULE_DEVICE_TABLE(i2c, adp5589_id);
|
||||
static struct i2c_driver adp5589_driver = {
|
||||
.driver = {
|
||||
.name = KBUILD_MODNAME,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &adp5589_dev_pm_ops,
|
||||
},
|
||||
.probe = adp5589_probe,
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/leds.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/i2c.h>
|
||||
@ -47,6 +48,20 @@
|
||||
#define CAP11XX_REG_CONFIG2 0x44
|
||||
#define CAP11XX_REG_CONFIG2_ALT_POL BIT(6)
|
||||
#define CAP11XX_REG_SENSOR_BASE_CNT(X) (0x50 + (X))
|
||||
#define CAP11XX_REG_LED_POLARITY 0x73
|
||||
#define CAP11XX_REG_LED_OUTPUT_CONTROL 0x74
|
||||
|
||||
#define CAP11XX_REG_LED_DUTY_CYCLE_1 0x90
|
||||
#define CAP11XX_REG_LED_DUTY_CYCLE_2 0x91
|
||||
#define CAP11XX_REG_LED_DUTY_CYCLE_3 0x92
|
||||
#define CAP11XX_REG_LED_DUTY_CYCLE_4 0x93
|
||||
|
||||
#define CAP11XX_REG_LED_DUTY_MIN_MASK (0x0f)
|
||||
#define CAP11XX_REG_LED_DUTY_MIN_MASK_SHIFT (0)
|
||||
#define CAP11XX_REG_LED_DUTY_MAX_MASK (0xf0)
|
||||
#define CAP11XX_REG_LED_DUTY_MAX_MASK_SHIFT (4)
|
||||
#define CAP11XX_REG_LED_DUTY_MAX_VALUE (15)
|
||||
|
||||
#define CAP11XX_REG_SENSOR_CALIB (0xb1 + (X))
|
||||
#define CAP11XX_REG_SENSOR_CALIB_LSB1 0xb9
|
||||
#define CAP11XX_REG_SENSOR_CALIB_LSB2 0xba
|
||||
@ -56,10 +71,23 @@
|
||||
|
||||
#define CAP11XX_MANUFACTURER_ID 0x5d
|
||||
|
||||
#ifdef CONFIG_LEDS_CLASS
|
||||
struct cap11xx_led {
|
||||
struct cap11xx_priv *priv;
|
||||
struct led_classdev cdev;
|
||||
struct work_struct work;
|
||||
u32 reg;
|
||||
enum led_brightness new_brightness;
|
||||
};
|
||||
#endif
|
||||
|
||||
struct cap11xx_priv {
|
||||
struct regmap *regmap;
|
||||
struct input_dev *idev;
|
||||
|
||||
struct cap11xx_led *leds;
|
||||
int num_leds;
|
||||
|
||||
/* config */
|
||||
u32 keycodes[];
|
||||
};
|
||||
@ -67,6 +95,7 @@ struct cap11xx_priv {
|
||||
struct cap11xx_hw_model {
|
||||
u8 product_id;
|
||||
unsigned int num_channels;
|
||||
unsigned int num_leds;
|
||||
};
|
||||
|
||||
enum {
|
||||
@ -76,9 +105,9 @@ enum {
|
||||
};
|
||||
|
||||
static const struct cap11xx_hw_model cap11xx_devices[] = {
|
||||
[CAP1106] = { .product_id = 0x55, .num_channels = 6 },
|
||||
[CAP1126] = { .product_id = 0x53, .num_channels = 6 },
|
||||
[CAP1188] = { .product_id = 0x50, .num_channels = 8 },
|
||||
[CAP1106] = { .product_id = 0x55, .num_channels = 6, .num_leds = 0 },
|
||||
[CAP1126] = { .product_id = 0x53, .num_channels = 6, .num_leds = 2 },
|
||||
[CAP1188] = { .product_id = 0x50, .num_channels = 8, .num_leds = 8 },
|
||||
};
|
||||
|
||||
static const struct reg_default cap11xx_reg_defaults[] = {
|
||||
@ -111,6 +140,7 @@ static const struct reg_default cap11xx_reg_defaults[] = {
|
||||
{ CAP11XX_REG_STANDBY_SENSITIVITY, 0x02 },
|
||||
{ CAP11XX_REG_STANDBY_THRESH, 0x40 },
|
||||
{ CAP11XX_REG_CONFIG2, 0x40 },
|
||||
{ CAP11XX_REG_LED_POLARITY, 0x00 },
|
||||
{ CAP11XX_REG_SENSOR_CALIB_LSB1, 0x00 },
|
||||
{ CAP11XX_REG_SENSOR_CALIB_LSB2, 0x00 },
|
||||
};
|
||||
@ -177,6 +207,12 @@ out:
|
||||
|
||||
static int cap11xx_set_sleep(struct cap11xx_priv *priv, bool sleep)
|
||||
{
|
||||
/*
|
||||
* DLSEEP mode will turn off all LEDS, prevent this
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_LEDS_CLASS) && priv->num_leds)
|
||||
return 0;
|
||||
|
||||
return regmap_update_bits(priv->regmap, CAP11XX_REG_MAIN_CONTROL,
|
||||
CAP11XX_REG_MAIN_CONTROL_DLSEEP,
|
||||
sleep ? CAP11XX_REG_MAIN_CONTROL_DLSEEP : 0);
|
||||
@ -196,6 +232,104 @@ static void cap11xx_input_close(struct input_dev *idev)
|
||||
cap11xx_set_sleep(priv, true);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_LEDS_CLASS
|
||||
static void cap11xx_led_work(struct work_struct *work)
|
||||
{
|
||||
struct cap11xx_led *led = container_of(work, struct cap11xx_led, work);
|
||||
struct cap11xx_priv *priv = led->priv;
|
||||
int value = led->new_brightness;
|
||||
|
||||
/*
|
||||
* All LEDs share the same duty cycle as this is a HW limitation.
|
||||
* Brightness levels per LED are either 0 (OFF) and 1 (ON).
|
||||
*/
|
||||
regmap_update_bits(priv->regmap, CAP11XX_REG_LED_OUTPUT_CONTROL,
|
||||
BIT(led->reg), value ? BIT(led->reg) : 0);
|
||||
}
|
||||
|
||||
static void cap11xx_led_set(struct led_classdev *cdev,
|
||||
enum led_brightness value)
|
||||
{
|
||||
struct cap11xx_led *led = container_of(cdev, struct cap11xx_led, cdev);
|
||||
|
||||
if (led->new_brightness == value)
|
||||
return;
|
||||
|
||||
led->new_brightness = value;
|
||||
schedule_work(&led->work);
|
||||
}
|
||||
|
||||
static int cap11xx_init_leds(struct device *dev,
|
||||
struct cap11xx_priv *priv, int num_leds)
|
||||
{
|
||||
struct device_node *node = dev->of_node, *child;
|
||||
struct cap11xx_led *led;
|
||||
int cnt = of_get_child_count(node);
|
||||
int error;
|
||||
|
||||
if (!num_leds || !cnt)
|
||||
return 0;
|
||||
|
||||
if (cnt > num_leds)
|
||||
return -EINVAL;
|
||||
|
||||
led = devm_kcalloc(dev, cnt, sizeof(struct cap11xx_led), GFP_KERNEL);
|
||||
if (!led)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->leds = led;
|
||||
|
||||
error = regmap_update_bits(priv->regmap,
|
||||
CAP11XX_REG_LED_OUTPUT_CONTROL, 0xff, 0);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = regmap_update_bits(priv->regmap, CAP11XX_REG_LED_DUTY_CYCLE_4,
|
||||
CAP11XX_REG_LED_DUTY_MAX_MASK,
|
||||
CAP11XX_REG_LED_DUTY_MAX_VALUE <<
|
||||
CAP11XX_REG_LED_DUTY_MAX_MASK_SHIFT);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
for_each_child_of_node(node, child) {
|
||||
u32 reg;
|
||||
|
||||
led->cdev.name =
|
||||
of_get_property(child, "label", NULL) ? : child->name;
|
||||
led->cdev.default_trigger =
|
||||
of_get_property(child, "linux,default-trigger", NULL);
|
||||
led->cdev.flags = 0;
|
||||
led->cdev.brightness_set = cap11xx_led_set;
|
||||
led->cdev.max_brightness = 1;
|
||||
led->cdev.brightness = LED_OFF;
|
||||
|
||||
error = of_property_read_u32(child, "reg", ®);
|
||||
if (error != 0 || reg >= num_leds)
|
||||
return -EINVAL;
|
||||
|
||||
led->reg = reg;
|
||||
led->priv = priv;
|
||||
|
||||
INIT_WORK(&led->work, cap11xx_led_work);
|
||||
|
||||
error = devm_led_classdev_register(dev, &led->cdev);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
priv->num_leds++;
|
||||
led++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int cap11xx_init_leds(struct device *dev,
|
||||
struct cap11xx_priv *priv, int num_leds)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int cap11xx_i2c_probe(struct i2c_client *i2c_client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
@ -316,6 +450,10 @@ static int cap11xx_i2c_probe(struct i2c_client *i2c_client,
|
||||
priv->idev->open = cap11xx_input_open;
|
||||
priv->idev->close = cap11xx_input_close;
|
||||
|
||||
error = cap11xx_init_leds(dev, priv, cap->num_leds);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
input_set_drvdata(priv->idev, priv);
|
||||
|
||||
/*
|
||||
@ -361,7 +499,6 @@ MODULE_DEVICE_TABLE(i2c, cap11xx_i2c_ids);
|
||||
static struct i2c_driver cap11xx_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "cap11xx",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = cap11xx_dt_ids,
|
||||
},
|
||||
.id_table = cap11xx_i2c_ids,
|
||||
|
@ -239,6 +239,11 @@ static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata,
|
||||
}
|
||||
}
|
||||
|
||||
if (i == ddata->pdata->nbuttons) {
|
||||
error = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mutex_lock(&ddata->disable_lock);
|
||||
|
||||
for (i = 0; i < ddata->pdata->nbuttons; i++) {
|
||||
@ -655,7 +660,9 @@ gpio_keys_get_devtree_pdata(struct device *dev)
|
||||
if (of_property_read_u32(pp, "linux,input-type", &button->type))
|
||||
button->type = EV_KEY;
|
||||
|
||||
button->wakeup = !!of_get_property(pp, "gpio-key,wakeup", NULL);
|
||||
button->wakeup = of_property_read_bool(pp, "wakeup-source") ||
|
||||
/* legacy name */
|
||||
of_property_read_bool(pp, "gpio-key,wakeup");
|
||||
|
||||
button->can_disable = !!of_get_property(pp, "linux,can-disable", NULL);
|
||||
|
||||
|
@ -152,7 +152,10 @@ static struct gpio_keys_platform_data *gpio_keys_polled_get_devtree_pdata(struct
|
||||
&button->type))
|
||||
button->type = EV_KEY;
|
||||
|
||||
button->wakeup = fwnode_property_present(child, "gpio-key,wakeup");
|
||||
button->wakeup =
|
||||
fwnode_property_read_bool(child, "wakeup-source") ||
|
||||
/* legacy name */
|
||||
fwnode_property_read_bool(child, "gpio-key,wakeup");
|
||||
|
||||
if (fwnode_property_read_u32(child, "debounce-interval",
|
||||
&button->debounce_interval))
|
||||
|
@ -223,7 +223,6 @@ MODULE_DEVICE_TABLE(i2c, lm8333_id);
|
||||
static struct i2c_driver lm8333_driver = {
|
||||
.driver = {
|
||||
.name = "lm8333",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = lm8333_probe,
|
||||
.remove = lm8333_remove,
|
||||
|
@ -425,8 +425,10 @@ matrix_keypad_parse_dt(struct device *dev)
|
||||
|
||||
if (of_get_property(np, "linux,no-autorepeat", NULL))
|
||||
pdata->no_autorepeat = true;
|
||||
if (of_get_property(np, "linux,wakeup", NULL))
|
||||
pdata->wakeup = true;
|
||||
|
||||
pdata->wakeup = of_property_read_bool(np, "wakeup-source") ||
|
||||
of_property_read_bool(np, "linux,wakeup"); /* legacy */
|
||||
|
||||
if (of_get_property(np, "gpio-activelow", NULL))
|
||||
pdata->active_low = true;
|
||||
|
||||
|
@ -265,7 +265,6 @@ MODULE_DEVICE_TABLE(i2c, mcs_touchkey_id);
|
||||
static struct i2c_driver mcs_touchkey_driver = {
|
||||
.driver = {
|
||||
.name = "mcs_touchkey",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &mcs_touchkey_pm_ops,
|
||||
},
|
||||
.probe = mcs_touchkey_probe,
|
||||
|
@ -305,7 +305,6 @@ MODULE_DEVICE_TABLE(i2c, mpr121_id);
|
||||
static struct i2c_driver mpr_touchkey_driver = {
|
||||
.driver = {
|
||||
.name = "mpr121",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &mpr121_touchkey_pm_ops,
|
||||
},
|
||||
.id_table = mpr121_id,
|
||||
|
@ -507,6 +507,7 @@ static void pmic8xxx_kp_close(struct input_dev *dev)
|
||||
*/
|
||||
static int pmic8xxx_kp_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
unsigned int rows, cols;
|
||||
bool repeat;
|
||||
bool wakeup;
|
||||
@ -524,10 +525,11 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
repeat = !of_property_read_bool(pdev->dev.of_node,
|
||||
"linux,input-no-autorepeat");
|
||||
wakeup = of_property_read_bool(pdev->dev.of_node,
|
||||
"linux,keypad-wakeup");
|
||||
repeat = !of_property_read_bool(np, "linux,input-no-autorepeat");
|
||||
|
||||
wakeup = of_property_read_bool(np, "wakeup-source") ||
|
||||
/* legacy name */
|
||||
of_property_read_bool(np, "linux,keypad-wakeup");
|
||||
|
||||
kp = devm_kzalloc(&pdev->dev, sizeof(*kp), GFP_KERNEL);
|
||||
if (!kp)
|
||||
|
@ -277,7 +277,6 @@ MODULE_DEVICE_TABLE(i2c, qt1070_id);
|
||||
static struct i2c_driver qt1070_driver = {
|
||||
.driver = {
|
||||
.name = "qt1070",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &qt1070_pm_ops,
|
||||
},
|
||||
.id_table = qt1070_id,
|
||||
|
@ -497,7 +497,6 @@ MODULE_DEVICE_TABLE(i2c, qt2160_idtable);
|
||||
static struct i2c_driver qt2160_driver = {
|
||||
.driver = {
|
||||
.name = "qt2160",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
|
||||
.id_table = qt2160_idtable,
|
||||
|
@ -299,8 +299,10 @@ samsung_keypad_parse_dt(struct device *dev)
|
||||
if (of_get_property(np, "linux,input-no-autorepeat", NULL))
|
||||
pdata->no_autorepeat = true;
|
||||
|
||||
if (of_get_property(np, "linux,input-wakeup", NULL))
|
||||
pdata->wakeup = true;
|
||||
pdata->wakeup = of_property_read_bool(np, "wakeup-source") ||
|
||||
/* legacy name */
|
||||
of_property_read_bool(np, "linux,input-wakeup");
|
||||
|
||||
|
||||
return pdata;
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mfd/tc3589x.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
/* Maximum supported keypad matrix row/columns size */
|
||||
#define TC3589x_MAX_KPROW 8
|
||||
@ -352,7 +353,10 @@ tc3589x_keypad_of_probe(struct device *dev)
|
||||
}
|
||||
|
||||
plat->no_autorepeat = of_property_read_bool(np, "linux,no-autorepeat");
|
||||
plat->enable_wakeup = of_property_read_bool(np, "linux,wakeup");
|
||||
|
||||
plat->enable_wakeup = of_property_read_bool(np, "wakeup-source") ||
|
||||
/* legacy name */
|
||||
of_property_read_bool(np, "linux,wakeup");
|
||||
|
||||
/* The custom delay format is ms/16 */
|
||||
of_property_read_u32(np, "debounce-delay-ms", &debounce_ms);
|
||||
@ -386,12 +390,15 @@ static int tc3589x_keypad_probe(struct platform_device *pdev)
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
keypad = kzalloc(sizeof(struct tc_keypad), GFP_KERNEL);
|
||||
input = input_allocate_device();
|
||||
if (!keypad || !input) {
|
||||
dev_err(&pdev->dev, "failed to allocate keypad memory\n");
|
||||
error = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
keypad = devm_kzalloc(&pdev->dev, sizeof(struct tc_keypad),
|
||||
GFP_KERNEL);
|
||||
if (!keypad)
|
||||
return -ENOMEM;
|
||||
|
||||
input = devm_input_allocate_device(&pdev->dev);
|
||||
if (!input) {
|
||||
dev_err(&pdev->dev, "failed to allocate input device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
keypad->board = plat;
|
||||
@ -410,7 +417,7 @@ static int tc3589x_keypad_probe(struct platform_device *pdev)
|
||||
NULL, input);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "Failed to build keymap\n");
|
||||
goto err_free_mem;
|
||||
return error;
|
||||
}
|
||||
|
||||
keypad->keymap = input->keycode;
|
||||
@ -421,20 +428,23 @@ static int tc3589x_keypad_probe(struct platform_device *pdev)
|
||||
|
||||
input_set_drvdata(input, keypad);
|
||||
|
||||
error = request_threaded_irq(irq, NULL, tc3589x_keypad_irq,
|
||||
tc3589x_keypad_disable(keypad);
|
||||
|
||||
error = devm_request_threaded_irq(&pdev->dev, irq,
|
||||
NULL, tc3589x_keypad_irq,
|
||||
plat->irqtype | IRQF_ONESHOT,
|
||||
"tc3589x-keypad", keypad);
|
||||
if (error < 0) {
|
||||
if (error) {
|
||||
dev_err(&pdev->dev,
|
||||
"Could not allocate irq %d,error %d\n",
|
||||
irq, error);
|
||||
goto err_free_mem;
|
||||
return error;
|
||||
}
|
||||
|
||||
error = input_register_device(input);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "Could not register input device\n");
|
||||
goto err_free_irq;
|
||||
return error;
|
||||
}
|
||||
|
||||
/* let platform decide if keypad is a wakeup source or not */
|
||||
@ -443,30 +453,6 @@ static int tc3589x_keypad_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, keypad);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_irq:
|
||||
free_irq(irq, keypad);
|
||||
err_free_mem:
|
||||
input_free_device(input);
|
||||
kfree(keypad);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int tc3589x_keypad_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct tc_keypad *keypad = platform_get_drvdata(pdev);
|
||||
int irq = platform_get_irq(pdev, 0);
|
||||
|
||||
if (!keypad->keypad_stopped)
|
||||
tc3589x_keypad_disable(keypad);
|
||||
|
||||
free_irq(irq, keypad);
|
||||
|
||||
input_unregister_device(keypad->input);
|
||||
|
||||
kfree(keypad);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -518,7 +504,6 @@ static struct platform_driver tc3589x_keypad_driver = {
|
||||
.pm = &tc3589x_keypad_dev_pm_ops,
|
||||
},
|
||||
.probe = tc3589x_keypad_probe,
|
||||
.remove = tc3589x_keypad_remove,
|
||||
};
|
||||
module_platform_driver(tc3589x_keypad_driver);
|
||||
|
||||
|
@ -404,7 +404,6 @@ MODULE_ALIAS("i2c:tca8418");
|
||||
static struct i2c_driver tca8418_keypad_driver = {
|
||||
.driver = {
|
||||
.name = TCA8418_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(tca8418_dt_ids),
|
||||
},
|
||||
.probe = tca8418_keypad_probe,
|
||||
|
@ -247,7 +247,7 @@ config INPUT_APANEL
|
||||
config INPUT_GP2A
|
||||
tristate "Sharp GP2AP002A00F I2C Proximity/Opto sensor driver"
|
||||
depends on I2C
|
||||
depends on GPIOLIB
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
help
|
||||
Say Y here if you have a Sharp GP2AP002A00F proximity/als combo-chip
|
||||
hooked to an I2C bus.
|
||||
@ -257,7 +257,7 @@ config INPUT_GP2A
|
||||
|
||||
config INPUT_GPIO_BEEPER
|
||||
tristate "Generic GPIO Beeper support"
|
||||
depends on GPIOLIB
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
help
|
||||
Say Y here if you have a beeper connected to a GPIO pin.
|
||||
|
||||
@ -266,7 +266,7 @@ config INPUT_GPIO_BEEPER
|
||||
|
||||
config INPUT_GPIO_TILT_POLLED
|
||||
tristate "Polled GPIO tilt switch"
|
||||
depends on GPIOLIB
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
select INPUT_POLLDEV
|
||||
help
|
||||
This driver implements support for tilt switches connected
|
||||
@ -557,7 +557,7 @@ config INPUT_PWM_BEEPER
|
||||
|
||||
config INPUT_GPIO_ROTARY_ENCODER
|
||||
tristate "Rotary encoders connected to GPIO pins"
|
||||
depends on GPIOLIB
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
help
|
||||
Say Y here to add support for rotary encoders connected to GPIO lines.
|
||||
Check file:Documentation/input/rotary-encoder.txt for more
|
||||
@ -764,7 +764,8 @@ config INPUT_SOC_BUTTON_ARRAY
|
||||
|
||||
config INPUT_DRV260X_HAPTICS
|
||||
tristate "TI DRV260X haptics support"
|
||||
depends on INPUT && I2C && GPIOLIB
|
||||
depends on INPUT && I2C
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
select INPUT_FF_MEMLESS
|
||||
select REGMAP_I2C
|
||||
help
|
||||
|
@ -158,7 +158,6 @@ MODULE_DEVICE_TABLE(of, adxl34x_of_id);
|
||||
static struct i2c_driver adxl34x_driver = {
|
||||
.driver = {
|
||||
.name = "adxl34x",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &adxl34x_i2c_pm,
|
||||
.of_match_table = of_match_ptr(adxl34x_of_id),
|
||||
},
|
||||
|
@ -170,8 +170,8 @@ static int arizona_haptics_probe(struct platform_device *pdev)
|
||||
|
||||
INIT_WORK(&haptics->work, arizona_haptics_work);
|
||||
|
||||
haptics->input_dev = input_allocate_device();
|
||||
if (haptics->input_dev == NULL) {
|
||||
haptics->input_dev = devm_input_allocate_device(&pdev->dev);
|
||||
if (!haptics->input_dev) {
|
||||
dev_err(arizona->dev, "Failed to allocate input device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
@ -188,41 +188,23 @@ static int arizona_haptics_probe(struct platform_device *pdev)
|
||||
if (ret < 0) {
|
||||
dev_err(arizona->dev, "input_ff_create_memless() failed: %d\n",
|
||||
ret);
|
||||
goto err_ialloc;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = input_register_device(haptics->input_dev);
|
||||
if (ret < 0) {
|
||||
dev_err(arizona->dev, "couldn't register input device: %d\n",
|
||||
ret);
|
||||
goto err_iff;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, haptics);
|
||||
|
||||
return 0;
|
||||
|
||||
err_iff:
|
||||
if (haptics->input_dev)
|
||||
input_ff_destroy(haptics->input_dev);
|
||||
err_ialloc:
|
||||
input_free_device(haptics->input_dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int arizona_haptics_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct arizona_haptics *haptics = platform_get_drvdata(pdev);
|
||||
|
||||
input_unregister_device(haptics->input_dev);
|
||||
platform_set_drvdata(pdev, haptics);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver arizona_haptics_driver = {
|
||||
.probe = arizona_haptics_probe,
|
||||
.remove = arizona_haptics_remove,
|
||||
.driver = {
|
||||
.name = "arizona-haptics",
|
||||
},
|
||||
|
@ -333,10 +333,9 @@ static void bma150_report_xyz(struct bma150_data *bma150)
|
||||
y = ((0xc0 & data[2]) >> 6) | (data[3] << 2);
|
||||
z = ((0xc0 & data[4]) >> 6) | (data[5] << 2);
|
||||
|
||||
/* sign extension */
|
||||
x = (s16) (x << 6) >> 6;
|
||||
y = (s16) (y << 6) >> 6;
|
||||
z = (s16) (z << 6) >> 6;
|
||||
x = sign_extend32(x, 9);
|
||||
y = sign_extend32(y, 9);
|
||||
z = sign_extend32(z, 9);
|
||||
|
||||
input_report_abs(bma150->input, ABS_X, x);
|
||||
input_report_abs(bma150->input, ABS_Y, y);
|
||||
@ -654,7 +653,6 @@ MODULE_DEVICE_TABLE(i2c, bma150_id);
|
||||
|
||||
static struct i2c_driver bma150_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = BMA150_DRIVER,
|
||||
.pm = &bma150_pm,
|
||||
},
|
||||
|
@ -118,7 +118,6 @@ static struct i2c_driver cma3000_i2c_driver = {
|
||||
.id_table = cma3000_i2c_id,
|
||||
.driver = {
|
||||
.name = "cma3000_i2c_accl",
|
||||
.owner = THIS_MODULE,
|
||||
#ifdef CONFIG_PM
|
||||
.pm = &cma3000_i2c_pm_ops,
|
||||
#endif
|
||||
|
@ -204,7 +204,7 @@ struct drv260x_data {
|
||||
int overdrive_voltage;
|
||||
};
|
||||
|
||||
static struct reg_default drv260x_reg_defs[] = {
|
||||
static const struct reg_default drv260x_reg_defs[] = {
|
||||
{ DRV260X_STATUS, 0xe0 },
|
||||
{ DRV260X_MODE, 0x40 },
|
||||
{ DRV260X_RT_PB_IN, 0x00 },
|
||||
@ -720,7 +720,6 @@ static struct i2c_driver drv260x_driver = {
|
||||
.probe = drv260x_probe,
|
||||
.driver = {
|
||||
.name = "drv260x-haptics",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(drv260x_of_match),
|
||||
.pm = &drv260x_pm_ops,
|
||||
},
|
||||
|
@ -74,7 +74,7 @@ static const u8 drv2665_sine_wave_form[] = {
|
||||
0x9b, 0x9f, 0xa5, 0xad, 0xb8, 0xc4, 0xd2, 0xe0, 0xf0, 0x00,
|
||||
};
|
||||
|
||||
static struct reg_default drv2665_reg_defs[] = {
|
||||
static const struct reg_default drv2665_reg_defs[] = {
|
||||
{ DRV2665_STATUS, 0x02 },
|
||||
{ DRV2665_CTRL_1, 0x28 },
|
||||
{ DRV2665_CTRL_2, 0x40 },
|
||||
@ -309,7 +309,6 @@ static struct i2c_driver drv2665_driver = {
|
||||
.probe = drv2665_probe,
|
||||
.driver = {
|
||||
.name = "drv2665-haptics",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(drv2665_of_match),
|
||||
.pm = &drv2665_pm_ops,
|
||||
},
|
||||
|
@ -116,7 +116,7 @@ struct drv2667_data {
|
||||
u32 frequency;
|
||||
};
|
||||
|
||||
static struct reg_default drv2667_reg_defs[] = {
|
||||
static const struct reg_default drv2667_reg_defs[] = {
|
||||
{ DRV2667_STATUS, 0x02 },
|
||||
{ DRV2667_CTRL_1, 0x28 },
|
||||
{ DRV2667_CTRL_2, 0x40 },
|
||||
@ -484,7 +484,6 @@ static struct i2c_driver drv2667_driver = {
|
||||
.probe = drv2667_probe,
|
||||
.driver = {
|
||||
.name = "drv2667-haptics",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(drv2667_of_match),
|
||||
.pm = &drv2667_pm_ops,
|
||||
},
|
||||
|
@ -267,11 +267,11 @@ 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,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &gp2a_pm,
|
||||
},
|
||||
.probe = gp2a_probe,
|
||||
|
@ -658,7 +658,6 @@ MODULE_DEVICE_TABLE(i2c, kxtj9_id);
|
||||
static struct i2c_driver kxtj9_driver = {
|
||||
.driver = {
|
||||
.name = NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &kxtj9_pm_ops,
|
||||
},
|
||||
.probe = kxtj9_probe,
|
||||
|
@ -394,7 +394,7 @@ static const struct platform_device_id max8997_haptic_id[] = {
|
||||
{ "max8997-haptic", 0 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, max8997_haptic_id);
|
||||
MODULE_DEVICE_TABLE(platform, max8997_haptic_id);
|
||||
|
||||
static struct platform_driver max8997_haptic_driver = {
|
||||
.driver = {
|
||||
@ -407,7 +407,6 @@ static struct platform_driver max8997_haptic_driver = {
|
||||
};
|
||||
module_platform_driver(max8997_haptic_driver);
|
||||
|
||||
MODULE_ALIAS("platform:max8997-haptic");
|
||||
MODULE_AUTHOR("Donggeun Kim <dg77.kim@samsung.com>");
|
||||
MODULE_DESCRIPTION("max8997_haptic driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -466,7 +466,6 @@ MODULE_DEVICE_TABLE(of, mpu3050_of_match);
|
||||
static struct i2c_driver mpu3050_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "mpu3050",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &mpu3050_pm,
|
||||
.of_match_table = mpu3050_of_match,
|
||||
},
|
||||
|
@ -208,7 +208,6 @@ MODULE_DEVICE_TABLE(i2c, pcf8574_kp_id);
|
||||
static struct i2c_driver pcf8574_kp_driver = {
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
#ifdef CONFIG_PM
|
||||
.pm = &pcf8574_kp_pm_ops,
|
||||
#endif
|
||||
|
@ -20,17 +20,72 @@
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#define PON_CNTL_1 0x1C
|
||||
#define PON_CNTL_PULL_UP BIT(7)
|
||||
#define PON_CNTL_TRIG_DELAY_MASK (0x7)
|
||||
#define PON_CNTL_1_PULL_UP_EN 0xe0
|
||||
#define PON_CNTL_1_USB_PWR_EN 0x10
|
||||
#define PON_CNTL_1_WD_EN_RESET 0x08
|
||||
|
||||
#define PM8058_SLEEP_CTRL 0x02b
|
||||
#define PM8921_SLEEP_CTRL 0x10a
|
||||
|
||||
#define SLEEP_CTRL_SMPL_EN_RESET 0x04
|
||||
|
||||
/* Regulator master enable addresses */
|
||||
#define REG_PM8058_VREG_EN_MSM 0x018
|
||||
#define REG_PM8058_VREG_EN_GRP_5_4 0x1c8
|
||||
|
||||
/* Regulator control registers for shutdown/reset */
|
||||
#define PM8058_S0_CTRL 0x004
|
||||
#define PM8058_S1_CTRL 0x005
|
||||
#define PM8058_S3_CTRL 0x111
|
||||
#define PM8058_L21_CTRL 0x120
|
||||
#define PM8058_L22_CTRL 0x121
|
||||
|
||||
#define PM8058_REGULATOR_ENABLE_MASK 0x80
|
||||
#define PM8058_REGULATOR_ENABLE 0x80
|
||||
#define PM8058_REGULATOR_DISABLE 0x00
|
||||
#define PM8058_REGULATOR_PULL_DOWN_MASK 0x40
|
||||
#define PM8058_REGULATOR_PULL_DOWN_EN 0x40
|
||||
|
||||
/* Buck CTRL register */
|
||||
#define PM8058_SMPS_LEGACY_VREF_SEL 0x20
|
||||
#define PM8058_SMPS_LEGACY_VPROG_MASK 0x1f
|
||||
#define PM8058_SMPS_ADVANCED_BAND_MASK 0xC0
|
||||
#define PM8058_SMPS_ADVANCED_BAND_SHIFT 6
|
||||
#define PM8058_SMPS_ADVANCED_VPROG_MASK 0x3f
|
||||
|
||||
/* Buck TEST2 registers for shutdown/reset */
|
||||
#define PM8058_S0_TEST2 0x084
|
||||
#define PM8058_S1_TEST2 0x085
|
||||
#define PM8058_S3_TEST2 0x11a
|
||||
|
||||
#define PM8058_REGULATOR_BANK_WRITE 0x80
|
||||
#define PM8058_REGULATOR_BANK_MASK 0x70
|
||||
#define PM8058_REGULATOR_BANK_SHIFT 4
|
||||
#define PM8058_REGULATOR_BANK_SEL(n) ((n) << PM8058_REGULATOR_BANK_SHIFT)
|
||||
|
||||
/* Buck TEST2 register bank 1 */
|
||||
#define PM8058_SMPS_LEGACY_VLOW_SEL 0x01
|
||||
|
||||
/* Buck TEST2 register bank 7 */
|
||||
#define PM8058_SMPS_ADVANCED_MODE_MASK 0x02
|
||||
#define PM8058_SMPS_ADVANCED_MODE 0x02
|
||||
#define PM8058_SMPS_LEGACY_MODE 0x00
|
||||
|
||||
/**
|
||||
* struct pmic8xxx_pwrkey - pmic8xxx pwrkey information
|
||||
* @key_press_irq: key press irq number
|
||||
* @regmap: device regmap
|
||||
* @shutdown_fn: shutdown configuration function
|
||||
*/
|
||||
struct pmic8xxx_pwrkey {
|
||||
int key_press_irq;
|
||||
struct regmap *regmap;
|
||||
int (*shutdown_fn)(struct pmic8xxx_pwrkey *, bool);
|
||||
};
|
||||
|
||||
static irqreturn_t pwrkey_press_irq(int irq, void *_pwr)
|
||||
@ -76,6 +131,212 @@ static int __maybe_unused pmic8xxx_pwrkey_resume(struct device *dev)
|
||||
static SIMPLE_DEV_PM_OPS(pm8xxx_pwr_key_pm_ops,
|
||||
pmic8xxx_pwrkey_suspend, pmic8xxx_pwrkey_resume);
|
||||
|
||||
static void pmic8xxx_pwrkey_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
struct pmic8xxx_pwrkey *pwrkey = platform_get_drvdata(pdev);
|
||||
int error;
|
||||
u8 mask, val;
|
||||
bool reset = system_state == SYSTEM_RESTART;
|
||||
|
||||
if (pwrkey->shutdown_fn) {
|
||||
error = pwrkey->shutdown_fn(pwrkey, reset);
|
||||
if (error)
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Select action to perform (reset or shutdown) when PS_HOLD goes low.
|
||||
* Also ensure that KPD, CBL0, and CBL1 pull ups are enabled and that
|
||||
* USB charging is enabled.
|
||||
*/
|
||||
mask = PON_CNTL_1_PULL_UP_EN | PON_CNTL_1_USB_PWR_EN;
|
||||
mask |= PON_CNTL_1_WD_EN_RESET;
|
||||
val = mask;
|
||||
if (!reset)
|
||||
val &= ~PON_CNTL_1_WD_EN_RESET;
|
||||
|
||||
regmap_update_bits(pwrkey->regmap, PON_CNTL_1, mask, val);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set an SMPS regulator to be disabled in its CTRL register, but enabled
|
||||
* in the master enable register. Also set it's pull down enable bit.
|
||||
* Take care to make sure that the output voltage doesn't change if switching
|
||||
* from advanced mode to legacy mode.
|
||||
*/
|
||||
static int pm8058_disable_smps_locally_set_pull_down(struct regmap *regmap,
|
||||
u16 ctrl_addr, u16 test2_addr, u16 master_enable_addr,
|
||||
u8 master_enable_bit)
|
||||
{
|
||||
int error;
|
||||
u8 vref_sel, vlow_sel, band, vprog, bank;
|
||||
unsigned int reg;
|
||||
|
||||
bank = PM8058_REGULATOR_BANK_SEL(7);
|
||||
error = regmap_write(regmap, test2_addr, bank);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = regmap_read(regmap, test2_addr, ®);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
reg &= PM8058_SMPS_ADVANCED_MODE_MASK;
|
||||
/* Check if in advanced mode. */
|
||||
if (reg == PM8058_SMPS_ADVANCED_MODE) {
|
||||
/* Determine current output voltage. */
|
||||
error = regmap_read(regmap, ctrl_addr, ®);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
band = reg & PM8058_SMPS_ADVANCED_BAND_MASK;
|
||||
band >>= PM8058_SMPS_ADVANCED_BAND_SHIFT;
|
||||
switch (band) {
|
||||
case 3:
|
||||
vref_sel = 0;
|
||||
vlow_sel = 0;
|
||||
break;
|
||||
case 2:
|
||||
vref_sel = PM8058_SMPS_LEGACY_VREF_SEL;
|
||||
vlow_sel = 0;
|
||||
break;
|
||||
case 1:
|
||||
vref_sel = PM8058_SMPS_LEGACY_VREF_SEL;
|
||||
vlow_sel = PM8058_SMPS_LEGACY_VLOW_SEL;
|
||||
break;
|
||||
default:
|
||||
pr_err("%s: regulator already disabled\n", __func__);
|
||||
return -EPERM;
|
||||
}
|
||||
vprog = reg & PM8058_SMPS_ADVANCED_VPROG_MASK;
|
||||
/* Round up if fine step is in use. */
|
||||
vprog = (vprog + 1) >> 1;
|
||||
if (vprog > PM8058_SMPS_LEGACY_VPROG_MASK)
|
||||
vprog = PM8058_SMPS_LEGACY_VPROG_MASK;
|
||||
|
||||
/* Set VLOW_SEL bit. */
|
||||
bank = PM8058_REGULATOR_BANK_SEL(1);
|
||||
error = regmap_write(regmap, test2_addr, bank);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = regmap_update_bits(regmap, test2_addr,
|
||||
PM8058_REGULATOR_BANK_WRITE | PM8058_REGULATOR_BANK_MASK
|
||||
| PM8058_SMPS_LEGACY_VLOW_SEL,
|
||||
PM8058_REGULATOR_BANK_WRITE |
|
||||
PM8058_REGULATOR_BANK_SEL(1) | vlow_sel);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/* Switch to legacy mode */
|
||||
bank = PM8058_REGULATOR_BANK_SEL(7);
|
||||
error = regmap_write(regmap, test2_addr, bank);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = regmap_update_bits(regmap, test2_addr,
|
||||
PM8058_REGULATOR_BANK_WRITE |
|
||||
PM8058_REGULATOR_BANK_MASK |
|
||||
PM8058_SMPS_ADVANCED_MODE_MASK,
|
||||
PM8058_REGULATOR_BANK_WRITE |
|
||||
PM8058_REGULATOR_BANK_SEL(7) |
|
||||
PM8058_SMPS_LEGACY_MODE);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/* Enable locally, enable pull down, keep voltage the same. */
|
||||
error = regmap_update_bits(regmap, ctrl_addr,
|
||||
PM8058_REGULATOR_ENABLE_MASK |
|
||||
PM8058_REGULATOR_PULL_DOWN_MASK |
|
||||
PM8058_SMPS_LEGACY_VREF_SEL |
|
||||
PM8058_SMPS_LEGACY_VPROG_MASK,
|
||||
PM8058_REGULATOR_ENABLE | PM8058_REGULATOR_PULL_DOWN_EN
|
||||
| vref_sel | vprog);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Enable in master control register. */
|
||||
error = regmap_update_bits(regmap, master_enable_addr,
|
||||
master_enable_bit, master_enable_bit);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/* Disable locally and enable pull down. */
|
||||
return regmap_update_bits(regmap, ctrl_addr,
|
||||
PM8058_REGULATOR_ENABLE_MASK | PM8058_REGULATOR_PULL_DOWN_MASK,
|
||||
PM8058_REGULATOR_DISABLE | PM8058_REGULATOR_PULL_DOWN_EN);
|
||||
}
|
||||
|
||||
static int pm8058_disable_ldo_locally_set_pull_down(struct regmap *regmap,
|
||||
u16 ctrl_addr, u16 master_enable_addr, u8 master_enable_bit)
|
||||
{
|
||||
int error;
|
||||
|
||||
/* Enable LDO in master control register. */
|
||||
error = regmap_update_bits(regmap, master_enable_addr,
|
||||
master_enable_bit, master_enable_bit);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/* Disable LDO in CTRL register and set pull down */
|
||||
return regmap_update_bits(regmap, ctrl_addr,
|
||||
PM8058_REGULATOR_ENABLE_MASK | PM8058_REGULATOR_PULL_DOWN_MASK,
|
||||
PM8058_REGULATOR_DISABLE | PM8058_REGULATOR_PULL_DOWN_EN);
|
||||
}
|
||||
|
||||
static int pm8058_pwrkey_shutdown(struct pmic8xxx_pwrkey *pwrkey, bool reset)
|
||||
{
|
||||
int error;
|
||||
struct regmap *regmap = pwrkey->regmap;
|
||||
u8 mask, val;
|
||||
|
||||
/* When shutting down, enable active pulldowns on important rails. */
|
||||
if (!reset) {
|
||||
/* Disable SMPS's 0,1,3 locally and set pulldown enable bits. */
|
||||
pm8058_disable_smps_locally_set_pull_down(regmap,
|
||||
PM8058_S0_CTRL, PM8058_S0_TEST2,
|
||||
REG_PM8058_VREG_EN_MSM, BIT(7));
|
||||
pm8058_disable_smps_locally_set_pull_down(regmap,
|
||||
PM8058_S1_CTRL, PM8058_S1_TEST2,
|
||||
REG_PM8058_VREG_EN_MSM, BIT(6));
|
||||
pm8058_disable_smps_locally_set_pull_down(regmap,
|
||||
PM8058_S3_CTRL, PM8058_S3_TEST2,
|
||||
REG_PM8058_VREG_EN_GRP_5_4, BIT(7) | BIT(4));
|
||||
/* Disable LDO 21 locally and set pulldown enable bit. */
|
||||
pm8058_disable_ldo_locally_set_pull_down(regmap,
|
||||
PM8058_L21_CTRL, REG_PM8058_VREG_EN_GRP_5_4,
|
||||
BIT(1));
|
||||
}
|
||||
|
||||
/*
|
||||
* Fix-up: Set regulator LDO22 to 1.225 V in high power mode. Leave its
|
||||
* pull-down state intact. This ensures a safe shutdown.
|
||||
*/
|
||||
error = regmap_update_bits(regmap, PM8058_L22_CTRL, 0xbf, 0x93);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/* Enable SMPL if resetting is desired */
|
||||
mask = SLEEP_CTRL_SMPL_EN_RESET;
|
||||
val = 0;
|
||||
if (reset)
|
||||
val = mask;
|
||||
return regmap_update_bits(regmap, PM8058_SLEEP_CTRL, mask, val);
|
||||
}
|
||||
|
||||
static int pm8921_pwrkey_shutdown(struct pmic8xxx_pwrkey *pwrkey, bool reset)
|
||||
{
|
||||
struct regmap *regmap = pwrkey->regmap;
|
||||
u8 mask = SLEEP_CTRL_SMPL_EN_RESET;
|
||||
u8 val = 0;
|
||||
|
||||
/* Enable SMPL if resetting is desired */
|
||||
if (reset)
|
||||
val = mask;
|
||||
return regmap_update_bits(regmap, PM8921_SLEEP_CTRL, mask, val);
|
||||
}
|
||||
|
||||
static int pmic8xxx_pwrkey_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct input_dev *pwr;
|
||||
@ -109,6 +370,8 @@ static int pmic8xxx_pwrkey_probe(struct platform_device *pdev)
|
||||
if (!pwrkey)
|
||||
return -ENOMEM;
|
||||
|
||||
pwrkey->shutdown_fn = of_device_get_match_data(&pdev->dev);
|
||||
pwrkey->regmap = regmap;
|
||||
pwrkey->key_press_irq = key_press_irq;
|
||||
|
||||
pwr = devm_input_allocate_device(&pdev->dev);
|
||||
@ -182,8 +445,8 @@ static int pmic8xxx_pwrkey_remove(struct platform_device *pdev)
|
||||
}
|
||||
|
||||
static const struct of_device_id pm8xxx_pwr_key_id_table[] = {
|
||||
{ .compatible = "qcom,pm8058-pwrkey" },
|
||||
{ .compatible = "qcom,pm8921-pwrkey" },
|
||||
{ .compatible = "qcom,pm8058-pwrkey", .data = &pm8058_pwrkey_shutdown },
|
||||
{ .compatible = "qcom,pm8921-pwrkey", .data = &pm8921_pwrkey_shutdown },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pm8xxx_pwr_key_id_table);
|
||||
@ -191,6 +454,7 @@ MODULE_DEVICE_TABLE(of, pm8xxx_pwr_key_id_table);
|
||||
static struct platform_driver pmic8xxx_pwrkey_driver = {
|
||||
.probe = pmic8xxx_pwrkey_probe,
|
||||
.remove = pmic8xxx_pwrkey_remove,
|
||||
.shutdown = pmic8xxx_pwrkey_shutdown,
|
||||
.driver = {
|
||||
.name = "pm8xxx-pwrkey",
|
||||
.pm = &pm8xxx_pwr_key_pm_ops,
|
||||
|
@ -320,10 +320,8 @@ static int uinput_validate_absbits(struct input_dev *dev)
|
||||
* Check if absmin/absmax/absfuzz/absflat are sane.
|
||||
*/
|
||||
|
||||
for (cnt = 0; cnt < ABS_CNT; cnt++) {
|
||||
for_each_set_bit(cnt, dev->absbit, ABS_CNT) {
|
||||
int min, max;
|
||||
if (!test_bit(cnt, dev->absbit))
|
||||
continue;
|
||||
|
||||
min = input_abs_get_min(dev, cnt);
|
||||
max = input_abs_get_max(dev, cnt);
|
||||
@ -416,7 +414,7 @@ static int uinput_setup_device(struct uinput_device *udev,
|
||||
dev->id.product = user_dev->id.product;
|
||||
dev->id.version = user_dev->id.version;
|
||||
|
||||
for (i = 0; i < ABS_CNT; i++) {
|
||||
for_each_set_bit(i, dev->absbit, ABS_CNT) {
|
||||
input_abs_set_max(dev, i, user_dev->absmax[i]);
|
||||
input_abs_set_min(dev, i, user_dev->absmin[i]);
|
||||
input_abs_set_fuzz(dev, i, user_dev->absfuzz[i]);
|
||||
|
@ -341,7 +341,7 @@ config MOUSE_VSXXXAA
|
||||
|
||||
config MOUSE_GPIO
|
||||
tristate "GPIO mouse"
|
||||
depends on GPIOLIB
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
select INPUT_POLLDEV
|
||||
help
|
||||
This driver simulates a mouse on GPIO lines of various CPUs (and some
|
||||
|
@ -24,7 +24,7 @@ obj-$(CONFIG_MOUSE_SYNAPTICS_I2C) += synaptics_i2c.o
|
||||
obj-$(CONFIG_MOUSE_SYNAPTICS_USB) += synaptics_usb.o
|
||||
obj-$(CONFIG_MOUSE_VSXXXAA) += vsxxxaa.o
|
||||
|
||||
cyapatp-objs := cyapa.o cyapa_gen3.o cyapa_gen5.o
|
||||
cyapatp-objs := cyapa.o cyapa_gen3.o cyapa_gen5.o cyapa_gen6.o
|
||||
psmouse-objs := psmouse-base.o synaptics.o focaltech.o
|
||||
|
||||
psmouse-$(CONFIG_MOUSE_PS2_ALPS) += alps.o
|
||||
|
@ -6,7 +6,7 @@
|
||||
* Daniel Kurtz <djkurtz@chromium.org>
|
||||
* Benson Leung <bleung@chromium.org>
|
||||
*
|
||||
* Copyright (C) 2011-2014 Cypress Semiconductor, Inc.
|
||||
* Copyright (C) 2011-2015 Cypress Semiconductor, Inc.
|
||||
* Copyright (C) 2011-2012 Google, Inc.
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
@ -21,10 +21,12 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/of.h>
|
||||
#include "cyapa.h"
|
||||
|
||||
|
||||
@ -39,11 +41,33 @@ const char product_id[] = "CYTRA";
|
||||
|
||||
static int cyapa_reinitialize(struct cyapa *cyapa);
|
||||
|
||||
static inline bool cyapa_is_bootloader_mode(struct cyapa *cyapa)
|
||||
bool cyapa_is_pip_bl_mode(struct cyapa *cyapa)
|
||||
{
|
||||
if (cyapa->gen == CYAPA_GEN6 && cyapa->state == CYAPA_STATE_GEN6_BL)
|
||||
return true;
|
||||
|
||||
if (cyapa->gen == CYAPA_GEN5 && cyapa->state == CYAPA_STATE_GEN5_BL)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cyapa_is_pip_app_mode(struct cyapa *cyapa)
|
||||
{
|
||||
if (cyapa->gen == CYAPA_GEN6 && cyapa->state == CYAPA_STATE_GEN6_APP)
|
||||
return true;
|
||||
|
||||
if (cyapa->gen == CYAPA_GEN5 && cyapa->state == CYAPA_STATE_GEN5_APP)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool cyapa_is_bootloader_mode(struct cyapa *cyapa)
|
||||
{
|
||||
if (cyapa_is_pip_bl_mode(cyapa))
|
||||
return true;
|
||||
|
||||
if (cyapa->gen == CYAPA_GEN3 &&
|
||||
cyapa->state >= CYAPA_STATE_BL_BUSY &&
|
||||
cyapa->state <= CYAPA_STATE_BL_ACTIVE)
|
||||
@ -54,7 +78,7 @@ static inline bool cyapa_is_bootloader_mode(struct cyapa *cyapa)
|
||||
|
||||
static inline bool cyapa_is_operational_mode(struct cyapa *cyapa)
|
||||
{
|
||||
if (cyapa->gen == CYAPA_GEN5 && cyapa->state == CYAPA_STATE_GEN5_APP)
|
||||
if (cyapa_is_pip_app_mode(cyapa))
|
||||
return true;
|
||||
|
||||
if (cyapa->gen == CYAPA_GEN3 && cyapa->state == CYAPA_STATE_OP)
|
||||
@ -188,6 +212,15 @@ static int cyapa_get_state(struct cyapa *cyapa)
|
||||
if (!error)
|
||||
goto out_detected;
|
||||
}
|
||||
if (cyapa->gen == CYAPA_GEN_UNKNOWN ||
|
||||
cyapa->gen == CYAPA_GEN6 ||
|
||||
cyapa->gen == CYAPA_GEN5) {
|
||||
error = cyapa_pip_state_parse(cyapa,
|
||||
status, BL_STATUS_SIZE);
|
||||
if (!error)
|
||||
goto out_detected;
|
||||
}
|
||||
/* For old Gen5 trackpads detecting. */
|
||||
if ((cyapa->gen == CYAPA_GEN_UNKNOWN ||
|
||||
cyapa->gen == CYAPA_GEN5) &&
|
||||
!smbus && even_addr) {
|
||||
@ -284,6 +317,9 @@ static int cyapa_check_is_operational(struct cyapa *cyapa)
|
||||
return error;
|
||||
|
||||
switch (cyapa->gen) {
|
||||
case CYAPA_GEN6:
|
||||
cyapa->ops = &cyapa_gen6_ops;
|
||||
break;
|
||||
case CYAPA_GEN5:
|
||||
cyapa->ops = &cyapa_gen5_ops;
|
||||
break;
|
||||
@ -306,7 +342,7 @@ static int cyapa_check_is_operational(struct cyapa *cyapa)
|
||||
|
||||
/*
|
||||
* Returns 0 on device detected, negative errno on no device detected.
|
||||
* And when the device is detected and opertaional, it will be reset to
|
||||
* And when the device is detected and operational, it will be reset to
|
||||
* full power active mode automatically.
|
||||
*/
|
||||
static int cyapa_detect(struct cyapa *cyapa)
|
||||
@ -333,6 +369,7 @@ static int cyapa_open(struct input_dev *input)
|
||||
{
|
||||
struct cyapa *cyapa = input_get_drvdata(input);
|
||||
struct i2c_client *client = cyapa->client;
|
||||
struct device *dev = &client->dev;
|
||||
int error;
|
||||
|
||||
error = mutex_lock_interruptible(&cyapa->state_sync_lock);
|
||||
@ -346,10 +383,9 @@ static int cyapa_open(struct input_dev *input)
|
||||
* when in operational mode.
|
||||
*/
|
||||
error = cyapa->ops->set_power_mode(cyapa,
|
||||
PWR_MODE_FULL_ACTIVE, 0);
|
||||
PWR_MODE_FULL_ACTIVE, 0, false);
|
||||
if (error) {
|
||||
dev_warn(&client->dev,
|
||||
"set active power failed: %d\n", error);
|
||||
dev_warn(dev, "set active power failed: %d\n", error);
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
@ -361,10 +397,14 @@ static int cyapa_open(struct input_dev *input)
|
||||
}
|
||||
|
||||
enable_irq(client->irq);
|
||||
if (!pm_runtime_enabled(&client->dev)) {
|
||||
pm_runtime_set_active(&client->dev);
|
||||
pm_runtime_enable(&client->dev);
|
||||
if (!pm_runtime_enabled(dev)) {
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
}
|
||||
|
||||
pm_runtime_get_sync(dev);
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_sync_autosuspend(dev);
|
||||
out:
|
||||
mutex_unlock(&cyapa->state_sync_lock);
|
||||
return error;
|
||||
@ -374,16 +414,17 @@ static void cyapa_close(struct input_dev *input)
|
||||
{
|
||||
struct cyapa *cyapa = input_get_drvdata(input);
|
||||
struct i2c_client *client = cyapa->client;
|
||||
struct device *dev = &cyapa->client->dev;
|
||||
|
||||
mutex_lock(&cyapa->state_sync_lock);
|
||||
|
||||
disable_irq(client->irq);
|
||||
if (pm_runtime_enabled(&client->dev))
|
||||
pm_runtime_disable(&client->dev);
|
||||
pm_runtime_set_suspended(&client->dev);
|
||||
if (pm_runtime_enabled(dev))
|
||||
pm_runtime_disable(dev);
|
||||
pm_runtime_set_suspended(dev);
|
||||
|
||||
if (cyapa->operational)
|
||||
cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0);
|
||||
cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0, false);
|
||||
|
||||
mutex_unlock(&cyapa->state_sync_lock);
|
||||
}
|
||||
@ -443,6 +484,7 @@ static int cyapa_create_input_dev(struct cyapa *cyapa)
|
||||
if (cyapa->gen >= CYAPA_GEN5) {
|
||||
input_set_abs_params(input, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0);
|
||||
input_set_abs_params(input, ABS_MT_WIDTH_MINOR, 0, 255, 0, 0);
|
||||
input_set_abs_params(input, ABS_DISTANCE, 0, 1, 0, 0);
|
||||
}
|
||||
|
||||
input_abs_set_res(input, ABS_MT_POSITION_X,
|
||||
@ -492,7 +534,7 @@ static void cyapa_enable_irq_for_cmd(struct cyapa *cyapa)
|
||||
*/
|
||||
if (!input || cyapa->operational)
|
||||
cyapa->ops->set_power_mode(cyapa,
|
||||
PWR_MODE_FULL_ACTIVE, 0);
|
||||
PWR_MODE_FULL_ACTIVE, 0, false);
|
||||
/* Gen3 always using polling mode for command. */
|
||||
if (cyapa->gen >= CYAPA_GEN5)
|
||||
enable_irq(cyapa->client->irq);
|
||||
@ -507,7 +549,8 @@ static void cyapa_disable_irq_for_cmd(struct cyapa *cyapa)
|
||||
if (cyapa->gen >= CYAPA_GEN5)
|
||||
disable_irq(cyapa->client->irq);
|
||||
if (!input || cyapa->operational)
|
||||
cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0);
|
||||
cyapa->ops->set_power_mode(cyapa,
|
||||
PWR_MODE_OFF, 0, false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -563,6 +606,8 @@ static int cyapa_initialize(struct cyapa *cyapa)
|
||||
error = cyapa_gen3_ops.initialize(cyapa);
|
||||
if (!error)
|
||||
error = cyapa_gen5_ops.initialize(cyapa);
|
||||
if (!error)
|
||||
error = cyapa_gen6_ops.initialize(cyapa);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
@ -572,7 +617,7 @@ static int cyapa_initialize(struct cyapa *cyapa)
|
||||
|
||||
/* Power down the device until we need it. */
|
||||
if (cyapa->operational)
|
||||
cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0);
|
||||
cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -588,7 +633,8 @@ static int cyapa_reinitialize(struct cyapa *cyapa)
|
||||
|
||||
/* Avoid command failures when TP was in OFF state. */
|
||||
if (cyapa->operational)
|
||||
cyapa->ops->set_power_mode(cyapa, PWR_MODE_FULL_ACTIVE, 0);
|
||||
cyapa->ops->set_power_mode(cyapa,
|
||||
PWR_MODE_FULL_ACTIVE, 0, false);
|
||||
|
||||
error = cyapa_detect(cyapa);
|
||||
if (error)
|
||||
@ -607,7 +653,8 @@ out:
|
||||
if (!input || !input->users) {
|
||||
/* Reset to power OFF state to save power when no user open. */
|
||||
if (cyapa->operational)
|
||||
cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0);
|
||||
cyapa->ops->set_power_mode(cyapa,
|
||||
PWR_MODE_OFF, 0, false);
|
||||
} else if (!error && cyapa->operational) {
|
||||
/*
|
||||
* Make sure only enable runtime PM when device is
|
||||
@ -615,6 +662,10 @@ out:
|
||||
*/
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
pm_runtime_get_sync(dev);
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_sync_autosuspend(dev);
|
||||
}
|
||||
|
||||
return error;
|
||||
@ -624,27 +675,44 @@ static irqreturn_t cyapa_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct cyapa *cyapa = dev_id;
|
||||
struct device *dev = &cyapa->client->dev;
|
||||
int error;
|
||||
|
||||
pm_runtime_get_sync(dev);
|
||||
if (device_may_wakeup(dev))
|
||||
pm_wakeup_event(dev, 0);
|
||||
|
||||
/* Interrupt event maybe cuased by host command to trackpad device. */
|
||||
/* Interrupt event can be caused by host command to trackpad device. */
|
||||
if (cyapa->ops->irq_cmd_handler(cyapa)) {
|
||||
/*
|
||||
* Interrupt event maybe from trackpad device input reporting.
|
||||
*/
|
||||
if (!cyapa->input) {
|
||||
/*
|
||||
* Still in probling or in firware image
|
||||
* udpating or reading.
|
||||
* Still in probing or in firmware image
|
||||
* updating or reading.
|
||||
*/
|
||||
cyapa->ops->sort_empty_output_data(cyapa,
|
||||
NULL, NULL, NULL);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!cyapa->operational || cyapa->ops->irq_handler(cyapa)) {
|
||||
if (cyapa->operational) {
|
||||
error = cyapa->ops->irq_handler(cyapa);
|
||||
|
||||
/*
|
||||
* Apply runtime power management to touch report event
|
||||
* except the events caused by the command responses.
|
||||
* Note:
|
||||
* It will introduce about 20~40 ms additional delay
|
||||
* time in receiving for first valid touch report data.
|
||||
* The time is used to execute device runtime resume
|
||||
* process.
|
||||
*/
|
||||
pm_runtime_get_sync(dev);
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_sync_autosuspend(dev);
|
||||
}
|
||||
|
||||
if (!cyapa->operational || error) {
|
||||
if (!mutex_trylock(&cyapa->state_sync_lock)) {
|
||||
cyapa->ops->sort_empty_output_data(cyapa,
|
||||
NULL, NULL, NULL);
|
||||
@ -656,8 +724,6 @@ static irqreturn_t cyapa_irq(int irq, void *dev_id)
|
||||
}
|
||||
|
||||
out:
|
||||
pm_runtime_mark_last_busy(dev);
|
||||
pm_runtime_put_sync_autosuspend(dev);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
@ -1051,12 +1117,12 @@ static ssize_t cyapa_update_fw_store(struct device *dev,
|
||||
dev_dbg(dev, "firmware update successfully done.\n");
|
||||
|
||||
/*
|
||||
* Redetect trackpad device states because firmware update process
|
||||
* Re-detect trackpad device states because firmware update process
|
||||
* will reset trackpad device into bootloader mode.
|
||||
*/
|
||||
ret = cyapa_reinitialize(cyapa);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to redetect after updated: %d\n", ret);
|
||||
dev_err(dev, "failed to re-detect after updated: %d\n", ret);
|
||||
error = error ? error : ret;
|
||||
}
|
||||
|
||||
@ -1120,9 +1186,11 @@ static char *cyapa_state_to_string(struct cyapa *cyapa)
|
||||
case CYAPA_STATE_BL_ACTIVE:
|
||||
return "bootloader active";
|
||||
case CYAPA_STATE_GEN5_BL:
|
||||
case CYAPA_STATE_GEN6_BL:
|
||||
return "bootloader";
|
||||
case CYAPA_STATE_OP:
|
||||
case CYAPA_STATE_GEN5_APP:
|
||||
case CYAPA_STATE_GEN6_APP:
|
||||
return "operational"; /* Normal valid state. */
|
||||
default:
|
||||
return "invalid mode";
|
||||
@ -1175,6 +1243,13 @@ static void cyapa_remove_sysfs_group(void *data)
|
||||
sysfs_remove_group(&cyapa->client->dev.kobj, &cyapa_sysfs_group);
|
||||
}
|
||||
|
||||
static void cyapa_disable_regulator(void *data)
|
||||
{
|
||||
struct cyapa *cyapa = data;
|
||||
|
||||
regulator_disable(cyapa->vcc);
|
||||
}
|
||||
|
||||
static int cyapa_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *dev_id)
|
||||
{
|
||||
@ -1208,6 +1283,27 @@ static int cyapa_probe(struct i2c_client *client,
|
||||
sprintf(cyapa->phys, "i2c-%d-%04x/input0", client->adapter->nr,
|
||||
client->addr);
|
||||
|
||||
cyapa->vcc = devm_regulator_get(dev, "vcc");
|
||||
if (IS_ERR(cyapa->vcc)) {
|
||||
error = PTR_ERR(cyapa->vcc);
|
||||
dev_err(dev, "failed to get vcc regulator: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = regulator_enable(cyapa->vcc);
|
||||
if (error) {
|
||||
dev_err(dev, "failed to enable regulator: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = devm_add_action(dev, cyapa_disable_regulator, cyapa);
|
||||
if (error) {
|
||||
cyapa_disable_regulator(cyapa);
|
||||
dev_err(dev, "failed to add disable regulator action: %d\n",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = cyapa_initialize(cyapa);
|
||||
if (error) {
|
||||
dev_err(dev, "failed to detect and initialize tp device.\n");
|
||||
@ -1296,12 +1392,19 @@ static int __maybe_unused cyapa_suspend(struct device *dev)
|
||||
power_mode = device_may_wakeup(dev) ? cyapa->suspend_power_mode
|
||||
: PWR_MODE_OFF;
|
||||
error = cyapa->ops->set_power_mode(cyapa, power_mode,
|
||||
cyapa->suspend_sleep_time);
|
||||
cyapa->suspend_sleep_time, true);
|
||||
if (error)
|
||||
dev_err(dev, "suspend set power mode failed: %d\n",
|
||||
error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable proximity interrupt when system idle, want true touch to
|
||||
* wake the system.
|
||||
*/
|
||||
if (cyapa->dev_pwr_mode != PWR_MODE_OFF)
|
||||
cyapa->ops->set_proximity(cyapa, false);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
cyapa->irq_wake = (enable_irq_wake(client->irq) == 0);
|
||||
|
||||
@ -1322,7 +1425,10 @@ static int __maybe_unused cyapa_resume(struct device *dev)
|
||||
cyapa->irq_wake = false;
|
||||
}
|
||||
|
||||
/* Update device states and runtime PM states. */
|
||||
/*
|
||||
* Update device states and runtime PM states.
|
||||
* Re-Enable proximity interrupt after enter operational mode.
|
||||
*/
|
||||
error = cyapa_reinitialize(cyapa);
|
||||
if (error)
|
||||
dev_warn(dev, "failed to reinitialize TP device: %d\n", error);
|
||||
@ -1340,7 +1446,8 @@ static int __maybe_unused cyapa_runtime_suspend(struct device *dev)
|
||||
|
||||
error = cyapa->ops->set_power_mode(cyapa,
|
||||
cyapa->runtime_suspend_power_mode,
|
||||
cyapa->runtime_suspend_sleep_time);
|
||||
cyapa->runtime_suspend_sleep_time,
|
||||
false);
|
||||
if (error)
|
||||
dev_warn(dev, "runtime suspend failed: %d\n", error);
|
||||
|
||||
@ -1352,7 +1459,8 @@ static int __maybe_unused cyapa_runtime_resume(struct device *dev)
|
||||
struct cyapa *cyapa = dev_get_drvdata(dev);
|
||||
int error;
|
||||
|
||||
error = cyapa->ops->set_power_mode(cyapa, PWR_MODE_FULL_ACTIVE, 0);
|
||||
error = cyapa->ops->set_power_mode(cyapa,
|
||||
PWR_MODE_FULL_ACTIVE, 0, false);
|
||||
if (error)
|
||||
dev_warn(dev, "runtime resume failed: %d\n", error);
|
||||
|
||||
@ -1374,17 +1482,26 @@ MODULE_DEVICE_TABLE(i2c, cyapa_id_table);
|
||||
static const struct acpi_device_id cyapa_acpi_id[] = {
|
||||
{ "CYAP0000", 0 }, /* Gen3 trackpad with 0x67 I2C address. */
|
||||
{ "CYAP0001", 0 }, /* Gen5 trackpad with 0x24 I2C address. */
|
||||
{ "CYAP0002", 0 }, /* Gen6 trackpad with 0x24 I2C address. */
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, cyapa_acpi_id);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id cyapa_of_match[] = {
|
||||
{ .compatible = "cypress,cyapa" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, cyapa_of_match);
|
||||
#endif
|
||||
|
||||
static struct i2c_driver cyapa_driver = {
|
||||
.driver = {
|
||||
.name = "cyapa",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &cyapa_pm_ops,
|
||||
.acpi_match_table = ACPI_PTR(cyapa_acpi_id),
|
||||
.of_match_table = of_match_ptr(cyapa_of_match),
|
||||
},
|
||||
|
||||
.probe = cyapa_probe,
|
||||
|
@ -3,7 +3,7 @@
|
||||
*
|
||||
* Author: Dudley Du <dudl@cypress.com>
|
||||
*
|
||||
* Copyright (C) 2014 Cypress Semiconductor, Inc.
|
||||
* Copyright (C) 2014-2015 Cypress Semiconductor, Inc.
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file COPYING in the main directory of this archive for
|
||||
@ -19,6 +19,7 @@
|
||||
#define CYAPA_GEN_UNKNOWN 0x00 /* unknown protocol. */
|
||||
#define CYAPA_GEN3 0x03 /* support MT-protocol B with tracking ID. */
|
||||
#define CYAPA_GEN5 0x05 /* support TrueTouch GEN5 trackpad device. */
|
||||
#define CYAPA_GEN6 0x06 /* support TrueTouch GEN6 trackpad device. */
|
||||
|
||||
#define CYAPA_NAME "Cypress APA Trackpad (cyapa)"
|
||||
|
||||
@ -159,12 +160,89 @@
|
||||
|
||||
#define AUTOSUSPEND_DELAY 2000 /* unit : ms */
|
||||
|
||||
#define UNINIT_SLEEP_TIME 0xFFFF
|
||||
#define UNINIT_PWR_MODE 0xFF
|
||||
|
||||
#define BTN_ONLY_MODE_NAME "buttononly"
|
||||
#define OFF_MODE_NAME "off"
|
||||
|
||||
/* Common macros for PIP interface. */
|
||||
#define PIP_HID_DESCRIPTOR_ADDR 0x0001
|
||||
#define PIP_REPORT_DESCRIPTOR_ADDR 0x0002
|
||||
#define PIP_INPUT_REPORT_ADDR 0x0003
|
||||
#define PIP_OUTPUT_REPORT_ADDR 0x0004
|
||||
#define PIP_CMD_DATA_ADDR 0x0006
|
||||
|
||||
#define PIP_RETRIEVE_DATA_STRUCTURE 0x24
|
||||
#define PIP_CMD_CALIBRATE 0x28
|
||||
#define PIP_BL_CMD_VERIFY_APP_INTEGRITY 0x31
|
||||
#define PIP_BL_CMD_GET_BL_INFO 0x38
|
||||
#define PIP_BL_CMD_PROGRAM_VERIFY_ROW 0x39
|
||||
#define PIP_BL_CMD_LAUNCH_APP 0x3b
|
||||
#define PIP_BL_CMD_INITIATE_BL 0x48
|
||||
#define PIP_INVALID_CMD 0xff
|
||||
|
||||
#define PIP_HID_DESCRIPTOR_SIZE 32
|
||||
#define PIP_HID_APP_REPORT_ID 0xf7
|
||||
#define PIP_HID_BL_REPORT_ID 0xff
|
||||
|
||||
#define PIP_BL_CMD_REPORT_ID 0x40
|
||||
#define PIP_BL_RESP_REPORT_ID 0x30
|
||||
#define PIP_APP_CMD_REPORT_ID 0x2f
|
||||
#define PIP_APP_RESP_REPORT_ID 0x1f
|
||||
|
||||
#define PIP_READ_SYS_INFO_CMD_LENGTH 7
|
||||
#define PIP_BL_READ_APP_INFO_CMD_LENGTH 13
|
||||
#define PIP_MIN_BL_CMD_LENGTH 13
|
||||
#define PIP_MIN_BL_RESP_LENGTH 11
|
||||
#define PIP_MIN_APP_CMD_LENGTH 7
|
||||
#define PIP_MIN_APP_RESP_LENGTH 5
|
||||
#define PIP_UNSUPPORTED_CMD_RESP_LENGTH 6
|
||||
#define PIP_READ_SYS_INFO_RESP_LENGTH 71
|
||||
#define PIP_BL_APP_INFO_RESP_LENGTH 30
|
||||
#define PIP_BL_GET_INFO_RESP_LENGTH 19
|
||||
|
||||
#define PIP_BL_PLATFORM_VER_SHIFT 4
|
||||
#define PIP_BL_PLATFORM_VER_MASK 0x0f
|
||||
|
||||
#define PIP_PRODUCT_FAMILY_MASK 0xf000
|
||||
#define PIP_PRODUCT_FAMILY_TRACKPAD 0x1000
|
||||
|
||||
#define PIP_DEEP_SLEEP_STATE_ON 0x00
|
||||
#define PIP_DEEP_SLEEP_STATE_OFF 0x01
|
||||
#define PIP_DEEP_SLEEP_STATE_MASK 0x03
|
||||
#define PIP_APP_DEEP_SLEEP_REPORT_ID 0xf0
|
||||
#define PIP_DEEP_SLEEP_RESP_LENGTH 5
|
||||
#define PIP_DEEP_SLEEP_OPCODE 0x08
|
||||
#define PIP_DEEP_SLEEP_OPCODE_MASK 0x0f
|
||||
|
||||
#define PIP_RESP_LENGTH_OFFSET 0
|
||||
#define PIP_RESP_LENGTH_SIZE 2
|
||||
#define PIP_RESP_REPORT_ID_OFFSET 2
|
||||
#define PIP_RESP_RSVD_OFFSET 3
|
||||
#define PIP_RESP_RSVD_KEY 0x00
|
||||
#define PIP_RESP_BL_SOP_OFFSET 4
|
||||
#define PIP_SOP_KEY 0x01 /* Start of Packet */
|
||||
#define PIP_EOP_KEY 0x17 /* End of Packet */
|
||||
#define PIP_RESP_APP_CMD_OFFSET 4
|
||||
#define GET_PIP_CMD_CODE(reg) ((reg) & 0x7f)
|
||||
#define PIP_RESP_STATUS_OFFSET 5
|
||||
|
||||
#define VALID_CMD_RESP_HEADER(resp, cmd) \
|
||||
(((resp)[PIP_RESP_REPORT_ID_OFFSET] == PIP_APP_RESP_REPORT_ID) && \
|
||||
((resp)[PIP_RESP_RSVD_OFFSET] == PIP_RESP_RSVD_KEY) && \
|
||||
(GET_PIP_CMD_CODE((resp)[PIP_RESP_APP_CMD_OFFSET]) == (cmd)))
|
||||
|
||||
#define PIP_CMD_COMPLETE_SUCCESS(resp_data) \
|
||||
((resp_data)[PIP_RESP_STATUS_OFFSET] == 0x00)
|
||||
|
||||
/* Variables to record latest gen5 trackpad power states. */
|
||||
#define UNINIT_SLEEP_TIME 0xffff
|
||||
#define UNINIT_PWR_MODE 0xff
|
||||
#define PIP_DEV_SET_PWR_STATE(cyapa, s) ((cyapa)->dev_pwr_mode = (s))
|
||||
#define PIP_DEV_GET_PWR_STATE(cyapa) ((cyapa)->dev_pwr_mode)
|
||||
#define PIP_DEV_SET_SLEEP_TIME(cyapa, t) ((cyapa)->dev_sleep_time = (t))
|
||||
#define PIP_DEV_GET_SLEEP_TIME(cyapa) ((cyapa)->dev_sleep_time)
|
||||
#define PIP_DEV_UNINIT_SLEEP_TIME(cyapa) \
|
||||
(((cyapa)->dev_sleep_time) == UNINIT_SLEEP_TIME)
|
||||
|
||||
/* The touch.id is used as the MT slot id, thus max MT slot is 15 */
|
||||
#define CYAPA_MAX_MT_SLOTS 15
|
||||
|
||||
@ -195,10 +273,12 @@ struct cyapa_dev_ops {
|
||||
int (*sort_empty_output_data)(struct cyapa *,
|
||||
u8 *, int *, cb_sort);
|
||||
|
||||
int (*set_power_mode)(struct cyapa *, u8, u16);
|
||||
int (*set_power_mode)(struct cyapa *, u8, u16, bool);
|
||||
|
||||
int (*set_proximity)(struct cyapa *, bool);
|
||||
};
|
||||
|
||||
struct cyapa_gen5_cmd_states {
|
||||
struct cyapa_pip_cmd_states {
|
||||
struct mutex cmd_lock;
|
||||
struct completion cmd_ready;
|
||||
atomic_t cmd_issued;
|
||||
@ -214,7 +294,7 @@ struct cyapa_gen5_cmd_states {
|
||||
};
|
||||
|
||||
union cyapa_cmd_states {
|
||||
struct cyapa_gen5_cmd_states gen5;
|
||||
struct cyapa_pip_cmd_states pip;
|
||||
};
|
||||
|
||||
enum cyapa_state {
|
||||
@ -225,6 +305,14 @@ enum cyapa_state {
|
||||
CYAPA_STATE_OP,
|
||||
CYAPA_STATE_GEN5_BL,
|
||||
CYAPA_STATE_GEN5_APP,
|
||||
CYAPA_STATE_GEN6_BL,
|
||||
CYAPA_STATE_GEN6_APP,
|
||||
};
|
||||
|
||||
struct gen6_interval_setting {
|
||||
u16 active_interval;
|
||||
u16 lp1_interval;
|
||||
u16 lp2_interval;
|
||||
};
|
||||
|
||||
/* The main device structure */
|
||||
@ -233,6 +321,7 @@ struct cyapa {
|
||||
u8 status[BL_STATUS_SIZE];
|
||||
bool operational; /* true: ready for data reporting; false: not. */
|
||||
|
||||
struct regulator *vcc;
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input;
|
||||
char phys[32]; /* Device physical location */
|
||||
@ -246,9 +335,11 @@ struct cyapa {
|
||||
u16 runtime_suspend_sleep_time;
|
||||
u8 dev_pwr_mode;
|
||||
u16 dev_sleep_time;
|
||||
struct gen6_interval_setting gen6_interval_setting;
|
||||
|
||||
/* Read from query data region. */
|
||||
char product_id[16];
|
||||
u8 platform_ver; /* Platform version. */
|
||||
u8 fw_maj_ver; /* Firmware major version. */
|
||||
u8 fw_min_ver; /* Firmware minor version. */
|
||||
u8 btn_capability;
|
||||
@ -259,7 +350,7 @@ struct cyapa {
|
||||
int physical_size_y;
|
||||
|
||||
/* Used in ttsp and truetouch based trackpad devices. */
|
||||
u8 x_origin; /* X Axis Origin: 0 = left side; 1 = rigth side. */
|
||||
u8 x_origin; /* X Axis Origin: 0 = left side; 1 = right side. */
|
||||
u8 y_origin; /* Y Axis Origin: 0 = top; 1 = bottom. */
|
||||
int electrodes_x; /* Number of electrodes on the X Axis*/
|
||||
int electrodes_y; /* Number of electrodes on the Y Axis*/
|
||||
@ -293,9 +384,51 @@ int cyapa_poll_state(struct cyapa *cyapa, unsigned int timeout);
|
||||
u8 cyapa_sleep_time_to_pwr_cmd(u16 sleep_time);
|
||||
u16 cyapa_pwr_cmd_to_sleep_time(u8 pwr_mode);
|
||||
|
||||
ssize_t cyapa_i2c_pip_read(struct cyapa *cyapa, u8 *buf, size_t size);
|
||||
ssize_t cyapa_i2c_pip_write(struct cyapa *cyapa, u8 *buf, size_t size);
|
||||
int cyapa_empty_pip_output_data(struct cyapa *cyapa,
|
||||
u8 *buf, int *len, cb_sort func);
|
||||
int cyapa_i2c_pip_cmd_irq_sync(struct cyapa *cyapa,
|
||||
u8 *cmd, int cmd_len,
|
||||
u8 *resp_data, int *resp_len,
|
||||
unsigned long timeout,
|
||||
cb_sort func,
|
||||
bool irq_mode);
|
||||
int cyapa_pip_state_parse(struct cyapa *cyapa, u8 *reg_data, int len);
|
||||
bool cyapa_pip_sort_system_info_data(struct cyapa *cyapa, u8 *buf, int len);
|
||||
bool cyapa_sort_tsg_pip_bl_resp_data(struct cyapa *cyapa, u8 *data, int len);
|
||||
int cyapa_pip_deep_sleep(struct cyapa *cyapa, u8 state);
|
||||
bool cyapa_sort_tsg_pip_app_resp_data(struct cyapa *cyapa, u8 *data, int len);
|
||||
int cyapa_pip_bl_exit(struct cyapa *cyapa);
|
||||
int cyapa_pip_bl_enter(struct cyapa *cyapa);
|
||||
|
||||
|
||||
bool cyapa_is_pip_bl_mode(struct cyapa *cyapa);
|
||||
bool cyapa_is_pip_app_mode(struct cyapa *cyapa);
|
||||
int cyapa_pip_cmd_state_initialize(struct cyapa *cyapa);
|
||||
|
||||
int cyapa_pip_resume_scanning(struct cyapa *cyapa);
|
||||
int cyapa_pip_suspend_scanning(struct cyapa *cyapa);
|
||||
|
||||
int cyapa_pip_check_fw(struct cyapa *cyapa, const struct firmware *fw);
|
||||
int cyapa_pip_bl_initiate(struct cyapa *cyapa, const struct firmware *fw);
|
||||
int cyapa_pip_do_fw_update(struct cyapa *cyapa, const struct firmware *fw);
|
||||
int cyapa_pip_bl_activate(struct cyapa *cyapa);
|
||||
int cyapa_pip_bl_deactivate(struct cyapa *cyapa);
|
||||
ssize_t cyapa_pip_do_calibrate(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count);
|
||||
int cyapa_pip_set_proximity(struct cyapa *cyapa, bool enable);
|
||||
|
||||
bool cyapa_pip_irq_cmd_handler(struct cyapa *cyapa);
|
||||
int cyapa_pip_irq_handler(struct cyapa *cyapa);
|
||||
|
||||
|
||||
extern u8 pip_read_sys_info[];
|
||||
extern u8 pip_bl_read_app_info[];
|
||||
extern const char product_id[];
|
||||
extern const struct cyapa_dev_ops cyapa_gen3_ops;
|
||||
extern const struct cyapa_dev_ops cyapa_gen5_ops;
|
||||
extern const struct cyapa_dev_ops cyapa_gen6_ops;
|
||||
|
||||
#endif
|
||||
|
@ -6,7 +6,7 @@
|
||||
* Daniel Kurtz <djkurtz@chromium.org>
|
||||
* Benson Leung <bleung@chromium.org>
|
||||
*
|
||||
* Copyright (C) 2011-2014 Cypress Semiconductor, Inc.
|
||||
* Copyright (C) 2011-2015 Cypress Semiconductor, Inc.
|
||||
* Copyright (C) 2011-2012 Google, Inc.
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
@ -950,7 +950,7 @@ static u16 cyapa_get_wait_time_for_pwr_cmd(u8 pwr_mode)
|
||||
* Device power mode can only be set when device is in operational mode.
|
||||
*/
|
||||
static int cyapa_gen3_set_power_mode(struct cyapa *cyapa, u8 power_mode,
|
||||
u16 always_unused)
|
||||
u16 always_unused, bool is_suspend_unused)
|
||||
{
|
||||
int ret;
|
||||
u8 power;
|
||||
@ -999,6 +999,11 @@ static int cyapa_gen3_set_power_mode(struct cyapa *cyapa, u8 power_mode,
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cyapa_gen3_set_proximity(struct cyapa *cyapa, bool enable)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static int cyapa_gen3_get_query_data(struct cyapa *cyapa)
|
||||
{
|
||||
u8 query_data[QUERY_DATA_SIZE];
|
||||
@ -1107,7 +1112,7 @@ static int cyapa_gen3_do_operational_check(struct cyapa *cyapa)
|
||||
* may cause problems, so we set the power mode first here.
|
||||
*/
|
||||
error = cyapa_gen3_set_power_mode(cyapa,
|
||||
PWR_MODE_FULL_ACTIVE, 0);
|
||||
PWR_MODE_FULL_ACTIVE, 0, false);
|
||||
if (error)
|
||||
dev_err(dev, "%s: set full power mode failed: %d\n",
|
||||
__func__, error);
|
||||
@ -1156,7 +1161,7 @@ static bool cyapa_gen3_irq_cmd_handler(struct cyapa *cyapa)
|
||||
* so, stop cyapa_gen3_irq_handler to continue process to
|
||||
* avoid unwanted to error detecting and processing.
|
||||
*
|
||||
* And also, avoid the periodicly accerted interrupts to be processed
|
||||
* And also, avoid the periodically asserted interrupts to be processed
|
||||
* as touch inputs when gen3 failed to launch into application mode,
|
||||
* which will cause gen3 stays in bootloader mode.
|
||||
*/
|
||||
@ -1243,4 +1248,6 @@ const struct cyapa_dev_ops cyapa_gen3_ops = {
|
||||
.irq_cmd_handler = cyapa_gen3_irq_cmd_handler,
|
||||
.sort_empty_output_data = cyapa_gen3_empty_output_data,
|
||||
.set_power_mode = cyapa_gen3_set_power_mode,
|
||||
|
||||
.set_proximity = cyapa_gen3_set_proximity,
|
||||
};
|
||||
|
File diff suppressed because it is too large
Load Diff
749
drivers/input/mouse/cyapa_gen6.c
Normal file
749
drivers/input/mouse/cyapa_gen6.c
Normal file
@ -0,0 +1,749 @@
|
||||
/*
|
||||
* Cypress APA trackpad with I2C interface
|
||||
*
|
||||
* Author: Dudley Du <dudl@cypress.com>
|
||||
*
|
||||
* Copyright (C) 2015 Cypress Semiconductor, Inc.
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file COPYING in the main directory of this archive for
|
||||
* more details.
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include <linux/crc-itu-t.h>
|
||||
#include "cyapa.h"
|
||||
|
||||
|
||||
#define GEN6_ENABLE_CMD_IRQ 0x41
|
||||
#define GEN6_DISABLE_CMD_IRQ 0x42
|
||||
#define GEN6_ENABLE_DEV_IRQ 0x43
|
||||
#define GEN6_DISABLE_DEV_IRQ 0x44
|
||||
|
||||
#define GEN6_POWER_MODE_ACTIVE 0x01
|
||||
#define GEN6_POWER_MODE_LP_MODE1 0x02
|
||||
#define GEN6_POWER_MODE_LP_MODE2 0x03
|
||||
#define GEN6_POWER_MODE_BTN_ONLY 0x04
|
||||
|
||||
#define GEN6_SET_POWER_MODE_INTERVAL 0x47
|
||||
#define GEN6_GET_POWER_MODE_INTERVAL 0x48
|
||||
|
||||
#define GEN6_MAX_RX_NUM 14
|
||||
#define GEN6_RETRIEVE_DATA_ID_RX_ATTENURATOR_IDAC 0x00
|
||||
#define GEN6_RETRIEVE_DATA_ID_ATTENURATOR_TRIM 0x12
|
||||
|
||||
|
||||
struct pip_app_cmd_head {
|
||||
__le16 addr;
|
||||
__le16 length;
|
||||
u8 report_id;
|
||||
u8 resv; /* Reserved, must be 0 */
|
||||
u8 cmd_code; /* bit7: resv, set to 0; bit6~0: command code.*/
|
||||
} __packed;
|
||||
|
||||
struct pip_app_resp_head {
|
||||
__le16 length;
|
||||
u8 report_id;
|
||||
u8 resv; /* Reserved, must be 0 */
|
||||
u8 cmd_code; /* bit7: TGL; bit6~0: command code.*/
|
||||
/*
|
||||
* The value of data_status can be the first byte of data or
|
||||
* the command status or the unsupported command code depending on the
|
||||
* requested command code.
|
||||
*/
|
||||
u8 data_status;
|
||||
} __packed;
|
||||
|
||||
struct pip_fixed_info {
|
||||
u8 silicon_id_high;
|
||||
u8 silicon_id_low;
|
||||
u8 family_id;
|
||||
};
|
||||
|
||||
static u8 pip_get_bl_info[] = {
|
||||
0x04, 0x00, 0x0B, 0x00, 0x40, 0x00, 0x01, 0x38,
|
||||
0x00, 0x00, 0x70, 0x9E, 0x17
|
||||
};
|
||||
|
||||
static bool cyapa_sort_pip_hid_descriptor_data(struct cyapa *cyapa,
|
||||
u8 *buf, int len)
|
||||
{
|
||||
if (len != PIP_HID_DESCRIPTOR_SIZE)
|
||||
return false;
|
||||
|
||||
if (buf[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_APP_REPORT_ID ||
|
||||
buf[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_BL_REPORT_ID)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int cyapa_get_pip_fixed_info(struct cyapa *cyapa,
|
||||
struct pip_fixed_info *pip_info, bool is_bootloader)
|
||||
{
|
||||
u8 resp_data[PIP_READ_SYS_INFO_RESP_LENGTH];
|
||||
int resp_len;
|
||||
u16 product_family;
|
||||
int error;
|
||||
|
||||
if (is_bootloader) {
|
||||
/* Read Bootloader Information to determine Gen5 or Gen6. */
|
||||
resp_len = sizeof(resp_data);
|
||||
error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
|
||||
pip_get_bl_info, sizeof(pip_get_bl_info),
|
||||
resp_data, &resp_len,
|
||||
2000, cyapa_sort_tsg_pip_bl_resp_data,
|
||||
false);
|
||||
if (error || resp_len < PIP_BL_GET_INFO_RESP_LENGTH)
|
||||
return error ? error : -EIO;
|
||||
|
||||
pip_info->family_id = resp_data[8];
|
||||
pip_info->silicon_id_low = resp_data[10];
|
||||
pip_info->silicon_id_high = resp_data[11];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Get App System Information to determine Gen5 or Gen6. */
|
||||
resp_len = sizeof(resp_data);
|
||||
error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
|
||||
pip_read_sys_info, PIP_READ_SYS_INFO_CMD_LENGTH,
|
||||
resp_data, &resp_len,
|
||||
2000, cyapa_pip_sort_system_info_data, false);
|
||||
if (error || resp_len < PIP_READ_SYS_INFO_RESP_LENGTH)
|
||||
return error ? error : -EIO;
|
||||
|
||||
product_family = get_unaligned_le16(&resp_data[7]);
|
||||
if ((product_family & PIP_PRODUCT_FAMILY_MASK) !=
|
||||
PIP_PRODUCT_FAMILY_TRACKPAD)
|
||||
return -EINVAL;
|
||||
|
||||
pip_info->family_id = resp_data[19];
|
||||
pip_info->silicon_id_low = resp_data[21];
|
||||
pip_info->silicon_id_high = resp_data[22];
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
int cyapa_pip_state_parse(struct cyapa *cyapa, u8 *reg_data, int len)
|
||||
{
|
||||
u8 cmd[] = { 0x01, 0x00};
|
||||
struct pip_fixed_info pip_info;
|
||||
u8 resp_data[PIP_HID_DESCRIPTOR_SIZE];
|
||||
int resp_len;
|
||||
bool is_bootloader;
|
||||
int error;
|
||||
|
||||
cyapa->state = CYAPA_STATE_NO_DEVICE;
|
||||
|
||||
/* Try to wake from it deep sleep state if it is. */
|
||||
cyapa_pip_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_ON);
|
||||
|
||||
/* Empty the buffer queue to get fresh data with later commands. */
|
||||
cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
|
||||
|
||||
/*
|
||||
* Read description info from trackpad device to determine running in
|
||||
* APP mode or Bootloader mode.
|
||||
*/
|
||||
resp_len = PIP_HID_DESCRIPTOR_SIZE;
|
||||
error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
|
||||
cmd, sizeof(cmd),
|
||||
resp_data, &resp_len,
|
||||
300,
|
||||
cyapa_sort_pip_hid_descriptor_data,
|
||||
false);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (resp_data[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_BL_REPORT_ID)
|
||||
is_bootloader = true;
|
||||
else if (resp_data[PIP_RESP_REPORT_ID_OFFSET] == PIP_HID_APP_REPORT_ID)
|
||||
is_bootloader = false;
|
||||
else
|
||||
return -EAGAIN;
|
||||
|
||||
/* Get PIP fixed information to determine Gen5 or Gen6. */
|
||||
memset(&pip_info, 0, sizeof(struct pip_fixed_info));
|
||||
error = cyapa_get_pip_fixed_info(cyapa, &pip_info, is_bootloader);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (pip_info.family_id == 0x9B && pip_info.silicon_id_high == 0x0B) {
|
||||
cyapa->gen = CYAPA_GEN6;
|
||||
cyapa->state = is_bootloader ? CYAPA_STATE_GEN6_BL
|
||||
: CYAPA_STATE_GEN6_APP;
|
||||
} else if (pip_info.family_id == 0x91 &&
|
||||
pip_info.silicon_id_high == 0x02) {
|
||||
cyapa->gen = CYAPA_GEN5;
|
||||
cyapa->state = is_bootloader ? CYAPA_STATE_GEN5_BL
|
||||
: CYAPA_STATE_GEN5_APP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cyapa_gen6_read_sys_info(struct cyapa *cyapa)
|
||||
{
|
||||
u8 resp_data[PIP_READ_SYS_INFO_RESP_LENGTH];
|
||||
int resp_len;
|
||||
u16 product_family;
|
||||
u8 rotat_align;
|
||||
int error;
|
||||
|
||||
/* Get App System Information to determine Gen5 or Gen6. */
|
||||
resp_len = sizeof(resp_data);
|
||||
error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
|
||||
pip_read_sys_info, PIP_READ_SYS_INFO_CMD_LENGTH,
|
||||
resp_data, &resp_len,
|
||||
2000, cyapa_pip_sort_system_info_data, false);
|
||||
if (error || resp_len < sizeof(resp_data))
|
||||
return error ? error : -EIO;
|
||||
|
||||
product_family = get_unaligned_le16(&resp_data[7]);
|
||||
if ((product_family & PIP_PRODUCT_FAMILY_MASK) !=
|
||||
PIP_PRODUCT_FAMILY_TRACKPAD)
|
||||
return -EINVAL;
|
||||
|
||||
cyapa->platform_ver = (resp_data[67] >> PIP_BL_PLATFORM_VER_SHIFT) &
|
||||
PIP_BL_PLATFORM_VER_MASK;
|
||||
cyapa->fw_maj_ver = resp_data[9];
|
||||
cyapa->fw_min_ver = resp_data[10];
|
||||
|
||||
cyapa->electrodes_x = resp_data[33];
|
||||
cyapa->electrodes_y = resp_data[34];
|
||||
|
||||
cyapa->physical_size_x = get_unaligned_le16(&resp_data[35]) / 100;
|
||||
cyapa->physical_size_y = get_unaligned_le16(&resp_data[37]) / 100;
|
||||
|
||||
cyapa->max_abs_x = get_unaligned_le16(&resp_data[39]);
|
||||
cyapa->max_abs_y = get_unaligned_le16(&resp_data[41]);
|
||||
|
||||
cyapa->max_z = get_unaligned_le16(&resp_data[43]);
|
||||
|
||||
cyapa->x_origin = resp_data[45] & 0x01;
|
||||
cyapa->y_origin = resp_data[46] & 0x01;
|
||||
|
||||
cyapa->btn_capability = (resp_data[70] << 3) & CAPABILITY_BTN_MASK;
|
||||
|
||||
memcpy(&cyapa->product_id[0], &resp_data[51], 5);
|
||||
cyapa->product_id[5] = '-';
|
||||
memcpy(&cyapa->product_id[6], &resp_data[56], 6);
|
||||
cyapa->product_id[12] = '-';
|
||||
memcpy(&cyapa->product_id[13], &resp_data[62], 2);
|
||||
cyapa->product_id[15] = '\0';
|
||||
|
||||
rotat_align = resp_data[68];
|
||||
if (rotat_align) {
|
||||
cyapa->electrodes_rx = cyapa->electrodes_y;
|
||||
cyapa->electrodes_rx = cyapa->electrodes_y;
|
||||
} else {
|
||||
cyapa->electrodes_rx = cyapa->electrodes_x;
|
||||
cyapa->electrodes_rx = cyapa->electrodes_y;
|
||||
}
|
||||
cyapa->aligned_electrodes_rx = (cyapa->electrodes_rx + 3) & ~3u;
|
||||
|
||||
if (!cyapa->electrodes_x || !cyapa->electrodes_y ||
|
||||
!cyapa->physical_size_x || !cyapa->physical_size_y ||
|
||||
!cyapa->max_abs_x || !cyapa->max_abs_y || !cyapa->max_z)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cyapa_gen6_bl_read_app_info(struct cyapa *cyapa)
|
||||
{
|
||||
u8 resp_data[PIP_BL_APP_INFO_RESP_LENGTH];
|
||||
int resp_len;
|
||||
int error;
|
||||
|
||||
resp_len = sizeof(resp_data);
|
||||
error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
|
||||
pip_bl_read_app_info, PIP_BL_READ_APP_INFO_CMD_LENGTH,
|
||||
resp_data, &resp_len,
|
||||
500, cyapa_sort_tsg_pip_bl_resp_data, false);
|
||||
if (error || resp_len < PIP_BL_APP_INFO_RESP_LENGTH ||
|
||||
!PIP_CMD_COMPLETE_SUCCESS(resp_data))
|
||||
return error ? error : -EIO;
|
||||
|
||||
cyapa->fw_maj_ver = resp_data[8];
|
||||
cyapa->fw_min_ver = resp_data[9];
|
||||
|
||||
cyapa->platform_ver = (resp_data[12] >> PIP_BL_PLATFORM_VER_SHIFT) &
|
||||
PIP_BL_PLATFORM_VER_MASK;
|
||||
|
||||
memcpy(&cyapa->product_id[0], &resp_data[13], 5);
|
||||
cyapa->product_id[5] = '-';
|
||||
memcpy(&cyapa->product_id[6], &resp_data[18], 6);
|
||||
cyapa->product_id[12] = '-';
|
||||
memcpy(&cyapa->product_id[13], &resp_data[24], 2);
|
||||
cyapa->product_id[15] = '\0';
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static int cyapa_gen6_config_dev_irq(struct cyapa *cyapa, u8 cmd_code)
|
||||
{
|
||||
u8 cmd[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, cmd_code };
|
||||
u8 resp_data[6];
|
||||
int resp_len;
|
||||
int error;
|
||||
|
||||
resp_len = sizeof(resp_data);
|
||||
error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd),
|
||||
resp_data, &resp_len,
|
||||
500, cyapa_sort_tsg_pip_app_resp_data, false);
|
||||
if (error || !VALID_CMD_RESP_HEADER(resp_data, cmd_code) ||
|
||||
!PIP_CMD_COMPLETE_SUCCESS(resp_data)
|
||||
)
|
||||
return error < 0 ? error : -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cyapa_gen6_set_proximity(struct cyapa *cyapa, bool enable)
|
||||
{
|
||||
int error;
|
||||
|
||||
cyapa_gen6_config_dev_irq(cyapa, GEN6_DISABLE_CMD_IRQ);
|
||||
error = cyapa_pip_set_proximity(cyapa, enable);
|
||||
cyapa_gen6_config_dev_irq(cyapa, GEN6_ENABLE_CMD_IRQ);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int cyapa_gen6_change_power_state(struct cyapa *cyapa, u8 power_mode)
|
||||
{
|
||||
u8 cmd[] = { 0x04, 0x00, 0x06, 0x00, 0x2f, 0x00, 0x46, power_mode };
|
||||
u8 resp_data[6];
|
||||
int resp_len;
|
||||
int error;
|
||||
|
||||
resp_len = sizeof(resp_data);
|
||||
error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd),
|
||||
resp_data, &resp_len,
|
||||
500, cyapa_sort_tsg_pip_app_resp_data, false);
|
||||
if (error || !VALID_CMD_RESP_HEADER(resp_data, 0x46))
|
||||
return error < 0 ? error : -EINVAL;
|
||||
|
||||
/* New power state applied in device not match the set power state. */
|
||||
if (resp_data[5] != power_mode)
|
||||
return -EAGAIN;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cyapa_gen6_set_interval_setting(struct cyapa *cyapa,
|
||||
struct gen6_interval_setting *interval_setting)
|
||||
{
|
||||
struct gen6_set_interval_cmd {
|
||||
__le16 addr;
|
||||
__le16 length;
|
||||
u8 report_id;
|
||||
u8 rsvd; /* Reserved, must be 0 */
|
||||
u8 cmd_code;
|
||||
__le16 active_interval;
|
||||
__le16 lp1_interval;
|
||||
__le16 lp2_interval;
|
||||
} __packed set_interval_cmd;
|
||||
u8 resp_data[11];
|
||||
int resp_len;
|
||||
int error;
|
||||
|
||||
memset(&set_interval_cmd, 0, sizeof(set_interval_cmd));
|
||||
put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, &set_interval_cmd.addr);
|
||||
put_unaligned_le16(sizeof(set_interval_cmd) - 2,
|
||||
&set_interval_cmd.length);
|
||||
set_interval_cmd.report_id = PIP_APP_CMD_REPORT_ID;
|
||||
set_interval_cmd.cmd_code = GEN6_SET_POWER_MODE_INTERVAL;
|
||||
put_unaligned_le16(interval_setting->active_interval,
|
||||
&set_interval_cmd.active_interval);
|
||||
put_unaligned_le16(interval_setting->lp1_interval,
|
||||
&set_interval_cmd.lp1_interval);
|
||||
put_unaligned_le16(interval_setting->lp2_interval,
|
||||
&set_interval_cmd.lp2_interval);
|
||||
|
||||
resp_len = sizeof(resp_data);
|
||||
error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
|
||||
(u8 *)&set_interval_cmd, sizeof(set_interval_cmd),
|
||||
resp_data, &resp_len,
|
||||
500, cyapa_sort_tsg_pip_app_resp_data, false);
|
||||
if (error ||
|
||||
!VALID_CMD_RESP_HEADER(resp_data, GEN6_SET_POWER_MODE_INTERVAL))
|
||||
return error < 0 ? error : -EINVAL;
|
||||
|
||||
/* Get the real set intervals from response. */
|
||||
interval_setting->active_interval = get_unaligned_le16(&resp_data[5]);
|
||||
interval_setting->lp1_interval = get_unaligned_le16(&resp_data[7]);
|
||||
interval_setting->lp2_interval = get_unaligned_le16(&resp_data[9]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cyapa_gen6_get_interval_setting(struct cyapa *cyapa,
|
||||
struct gen6_interval_setting *interval_setting)
|
||||
{
|
||||
u8 cmd[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00,
|
||||
GEN6_GET_POWER_MODE_INTERVAL };
|
||||
u8 resp_data[11];
|
||||
int resp_len;
|
||||
int error;
|
||||
|
||||
resp_len = sizeof(resp_data);
|
||||
error = cyapa_i2c_pip_cmd_irq_sync(cyapa, cmd, sizeof(cmd),
|
||||
resp_data, &resp_len,
|
||||
500, cyapa_sort_tsg_pip_app_resp_data, false);
|
||||
if (error ||
|
||||
!VALID_CMD_RESP_HEADER(resp_data, GEN6_GET_POWER_MODE_INTERVAL))
|
||||
return error < 0 ? error : -EINVAL;
|
||||
|
||||
interval_setting->active_interval = get_unaligned_le16(&resp_data[5]);
|
||||
interval_setting->lp1_interval = get_unaligned_le16(&resp_data[7]);
|
||||
interval_setting->lp2_interval = get_unaligned_le16(&resp_data[9]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cyapa_gen6_deep_sleep(struct cyapa *cyapa, u8 state)
|
||||
{
|
||||
u8 ping[] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, 0x00 };
|
||||
|
||||
if (state == PIP_DEEP_SLEEP_STATE_ON)
|
||||
/*
|
||||
* Send ping command to notify device prepare for wake up
|
||||
* when it's in deep sleep mode. At this time, device will
|
||||
* response nothing except an I2C NAK.
|
||||
*/
|
||||
cyapa_i2c_pip_write(cyapa, ping, sizeof(ping));
|
||||
|
||||
return cyapa_pip_deep_sleep(cyapa, state);
|
||||
}
|
||||
|
||||
static int cyapa_gen6_set_power_mode(struct cyapa *cyapa,
|
||||
u8 power_mode, u16 sleep_time, bool is_suspend)
|
||||
{
|
||||
struct device *dev = &cyapa->client->dev;
|
||||
struct gen6_interval_setting *interval_setting =
|
||||
&cyapa->gen6_interval_setting;
|
||||
u8 lp_mode;
|
||||
int error;
|
||||
|
||||
if (cyapa->state != CYAPA_STATE_GEN6_APP)
|
||||
return 0;
|
||||
|
||||
if (PIP_DEV_GET_PWR_STATE(cyapa) == UNINIT_PWR_MODE) {
|
||||
/*
|
||||
* Assume TP in deep sleep mode when driver is loaded,
|
||||
* avoid driver unload and reload command IO issue caused by TP
|
||||
* has been set into deep sleep mode when unloading.
|
||||
*/
|
||||
PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_OFF);
|
||||
}
|
||||
|
||||
if (PIP_DEV_UNINIT_SLEEP_TIME(cyapa) &&
|
||||
PIP_DEV_GET_PWR_STATE(cyapa) != PWR_MODE_OFF)
|
||||
PIP_DEV_SET_SLEEP_TIME(cyapa, UNINIT_SLEEP_TIME);
|
||||
|
||||
if (PIP_DEV_GET_PWR_STATE(cyapa) == power_mode) {
|
||||
if (power_mode == PWR_MODE_OFF ||
|
||||
power_mode == PWR_MODE_FULL_ACTIVE ||
|
||||
power_mode == PWR_MODE_BTN_ONLY ||
|
||||
PIP_DEV_GET_SLEEP_TIME(cyapa) == sleep_time) {
|
||||
/* Has in correct power mode state, early return. */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (power_mode == PWR_MODE_OFF) {
|
||||
cyapa_gen6_config_dev_irq(cyapa, GEN6_DISABLE_CMD_IRQ);
|
||||
|
||||
error = cyapa_gen6_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_OFF);
|
||||
if (error) {
|
||||
dev_err(dev, "enter deep sleep fail: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_OFF);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* When trackpad in power off mode, it cannot change to other power
|
||||
* state directly, must be wake up from sleep firstly, then
|
||||
* continue to do next power sate change.
|
||||
*/
|
||||
if (PIP_DEV_GET_PWR_STATE(cyapa) == PWR_MODE_OFF) {
|
||||
error = cyapa_gen6_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_ON);
|
||||
if (error) {
|
||||
dev_err(dev, "deep sleep wake fail: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable device assert interrupts for command response to avoid
|
||||
* disturbing system suspending or hibernating process.
|
||||
*/
|
||||
cyapa_gen6_config_dev_irq(cyapa, GEN6_DISABLE_CMD_IRQ);
|
||||
|
||||
if (power_mode == PWR_MODE_FULL_ACTIVE) {
|
||||
error = cyapa_gen6_change_power_state(cyapa,
|
||||
GEN6_POWER_MODE_ACTIVE);
|
||||
if (error) {
|
||||
dev_err(dev, "change to active fail: %d\n", error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_FULL_ACTIVE);
|
||||
|
||||
/* Sync the interval setting from device. */
|
||||
cyapa_gen6_get_interval_setting(cyapa, interval_setting);
|
||||
|
||||
} else if (power_mode == PWR_MODE_BTN_ONLY) {
|
||||
error = cyapa_gen6_change_power_state(cyapa,
|
||||
GEN6_POWER_MODE_BTN_ONLY);
|
||||
if (error) {
|
||||
dev_err(dev, "fail to button only mode: %d\n", error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_BTN_ONLY);
|
||||
} else {
|
||||
/*
|
||||
* Gen6 internally supports to 2 low power scan interval time,
|
||||
* so can help to switch power mode quickly.
|
||||
* such as runtime suspend and system suspend.
|
||||
*/
|
||||
if (interval_setting->lp1_interval == sleep_time) {
|
||||
lp_mode = GEN6_POWER_MODE_LP_MODE1;
|
||||
} else if (interval_setting->lp2_interval == sleep_time) {
|
||||
lp_mode = GEN6_POWER_MODE_LP_MODE2;
|
||||
} else {
|
||||
if (interval_setting->lp1_interval == 0) {
|
||||
interval_setting->lp1_interval = sleep_time;
|
||||
lp_mode = GEN6_POWER_MODE_LP_MODE1;
|
||||
} else {
|
||||
interval_setting->lp2_interval = sleep_time;
|
||||
lp_mode = GEN6_POWER_MODE_LP_MODE2;
|
||||
}
|
||||
cyapa_gen6_set_interval_setting(cyapa,
|
||||
interval_setting);
|
||||
}
|
||||
|
||||
error = cyapa_gen6_change_power_state(cyapa, lp_mode);
|
||||
if (error) {
|
||||
dev_err(dev, "set power state to 0x%02x failed: %d\n",
|
||||
lp_mode, error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
PIP_DEV_SET_SLEEP_TIME(cyapa, sleep_time);
|
||||
PIP_DEV_SET_PWR_STATE(cyapa,
|
||||
cyapa_sleep_time_to_pwr_cmd(sleep_time));
|
||||
}
|
||||
|
||||
out:
|
||||
cyapa_gen6_config_dev_irq(cyapa, GEN6_ENABLE_CMD_IRQ);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int cyapa_gen6_initialize(struct cyapa *cyapa)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cyapa_pip_retrieve_data_structure(struct cyapa *cyapa,
|
||||
u16 read_offset, u16 read_len, u8 data_id,
|
||||
u8 *data, int *data_buf_lens)
|
||||
{
|
||||
struct retrieve_data_struct_cmd {
|
||||
struct pip_app_cmd_head head;
|
||||
__le16 read_offset;
|
||||
__le16 read_length;
|
||||
u8 data_id;
|
||||
} __packed cmd;
|
||||
u8 resp_data[GEN6_MAX_RX_NUM + 10];
|
||||
int resp_len;
|
||||
int error;
|
||||
|
||||
memset(&cmd, 0, sizeof(cmd));
|
||||
put_unaligned_le16(PIP_OUTPUT_REPORT_ADDR, &cmd.head.addr);
|
||||
put_unaligned_le16(sizeof(cmd), &cmd.head.length - 2);
|
||||
cmd.head.report_id = PIP_APP_CMD_REPORT_ID;
|
||||
cmd.head.cmd_code = PIP_RETRIEVE_DATA_STRUCTURE;
|
||||
put_unaligned_le16(read_offset, &cmd.read_offset);
|
||||
put_unaligned_le16(read_len, &cmd.read_length);
|
||||
cmd.data_id = data_id;
|
||||
|
||||
resp_len = sizeof(resp_data);
|
||||
error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
|
||||
(u8 *)&cmd, sizeof(cmd),
|
||||
resp_data, &resp_len,
|
||||
500, cyapa_sort_tsg_pip_app_resp_data,
|
||||
true);
|
||||
if (error || !PIP_CMD_COMPLETE_SUCCESS(resp_data) ||
|
||||
resp_data[6] != data_id ||
|
||||
!VALID_CMD_RESP_HEADER(resp_data, PIP_RETRIEVE_DATA_STRUCTURE))
|
||||
return (error < 0) ? error : -EAGAIN;
|
||||
|
||||
read_len = get_unaligned_le16(&resp_data[7]);
|
||||
if (*data_buf_lens < read_len) {
|
||||
*data_buf_lens = read_len;
|
||||
return -ENOBUFS;
|
||||
}
|
||||
|
||||
memcpy(data, &resp_data[10], read_len);
|
||||
*data_buf_lens = read_len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t cyapa_gen6_show_baseline(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct cyapa *cyapa = dev_get_drvdata(dev);
|
||||
u8 data[GEN6_MAX_RX_NUM];
|
||||
int data_len;
|
||||
int size = 0;
|
||||
int i;
|
||||
int error;
|
||||
int resume_error;
|
||||
|
||||
if (!cyapa_is_pip_app_mode(cyapa))
|
||||
return -EBUSY;
|
||||
|
||||
/* 1. Suspend Scanning*/
|
||||
error = cyapa_pip_suspend_scanning(cyapa);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/* 2. IDAC and RX Attenuator Calibration Data (Center Frequency). */
|
||||
data_len = sizeof(data);
|
||||
error = cyapa_pip_retrieve_data_structure(cyapa, 0, data_len,
|
||||
GEN6_RETRIEVE_DATA_ID_RX_ATTENURATOR_IDAC,
|
||||
data, &data_len);
|
||||
if (error)
|
||||
goto resume_scanning;
|
||||
|
||||
size = scnprintf(buf, PAGE_SIZE, "%d %d %d %d %d %d ",
|
||||
data[0], /* RX Attenuator Mutual */
|
||||
data[1], /* IDAC Mutual */
|
||||
data[2], /* RX Attenuator Self RX */
|
||||
data[3], /* IDAC Self RX */
|
||||
data[4], /* RX Attenuator Self TX */
|
||||
data[5] /* IDAC Self TX */
|
||||
);
|
||||
|
||||
/* 3. Read Attenuator Trim. */
|
||||
data_len = sizeof(data);
|
||||
error = cyapa_pip_retrieve_data_structure(cyapa, 0, data_len,
|
||||
GEN6_RETRIEVE_DATA_ID_ATTENURATOR_TRIM,
|
||||
data, &data_len);
|
||||
if (error)
|
||||
goto resume_scanning;
|
||||
|
||||
/* set attenuator trim values. */
|
||||
for (i = 0; i < data_len; i++)
|
||||
size += scnprintf(buf + size, PAGE_SIZE - size, "%d ", data[i]);
|
||||
size += scnprintf(buf + size, PAGE_SIZE - size, "\n");
|
||||
|
||||
resume_scanning:
|
||||
/* 4. Resume Scanning*/
|
||||
resume_error = cyapa_pip_resume_scanning(cyapa);
|
||||
if (resume_error || error) {
|
||||
memset(buf, 0, PAGE_SIZE);
|
||||
return resume_error ? resume_error : error;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static int cyapa_gen6_operational_check(struct cyapa *cyapa)
|
||||
{
|
||||
struct device *dev = &cyapa->client->dev;
|
||||
int error;
|
||||
|
||||
if (cyapa->gen != CYAPA_GEN6)
|
||||
return -ENODEV;
|
||||
|
||||
switch (cyapa->state) {
|
||||
case CYAPA_STATE_GEN6_BL:
|
||||
error = cyapa_pip_bl_exit(cyapa);
|
||||
if (error) {
|
||||
/* Try to update trackpad product information. */
|
||||
cyapa_gen6_bl_read_app_info(cyapa);
|
||||
goto out;
|
||||
}
|
||||
|
||||
cyapa->state = CYAPA_STATE_GEN6_APP;
|
||||
|
||||
case CYAPA_STATE_GEN6_APP:
|
||||
/*
|
||||
* If trackpad device in deep sleep mode,
|
||||
* the app command will fail.
|
||||
* So always try to reset trackpad device to full active when
|
||||
* the device state is required.
|
||||
*/
|
||||
error = cyapa_gen6_set_power_mode(cyapa,
|
||||
PWR_MODE_FULL_ACTIVE, 0, false);
|
||||
if (error)
|
||||
dev_warn(dev, "%s: failed to set power active mode.\n",
|
||||
__func__);
|
||||
|
||||
/* By default, the trackpad proximity function is enabled. */
|
||||
error = cyapa_pip_set_proximity(cyapa, true);
|
||||
if (error)
|
||||
dev_warn(dev, "%s: failed to enable proximity.\n",
|
||||
__func__);
|
||||
|
||||
/* Get trackpad product information. */
|
||||
error = cyapa_gen6_read_sys_info(cyapa);
|
||||
if (error)
|
||||
goto out;
|
||||
/* Only support product ID starting with CYTRA */
|
||||
if (memcmp(cyapa->product_id, product_id,
|
||||
strlen(product_id)) != 0) {
|
||||
dev_err(dev, "%s: unknown product ID (%s)\n",
|
||||
__func__, cyapa->product_id);
|
||||
error = -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
error = -EINVAL;
|
||||
}
|
||||
|
||||
out:
|
||||
return error;
|
||||
}
|
||||
|
||||
const struct cyapa_dev_ops cyapa_gen6_ops = {
|
||||
.check_fw = cyapa_pip_check_fw,
|
||||
.bl_enter = cyapa_pip_bl_enter,
|
||||
.bl_initiate = cyapa_pip_bl_initiate,
|
||||
.update_fw = cyapa_pip_do_fw_update,
|
||||
.bl_activate = cyapa_pip_bl_activate,
|
||||
.bl_deactivate = cyapa_pip_bl_deactivate,
|
||||
|
||||
.show_baseline = cyapa_gen6_show_baseline,
|
||||
.calibrate_store = cyapa_pip_do_calibrate,
|
||||
|
||||
.initialize = cyapa_gen6_initialize,
|
||||
|
||||
.state_parse = cyapa_pip_state_parse,
|
||||
.operational_check = cyapa_gen6_operational_check,
|
||||
|
||||
.irq_handler = cyapa_pip_irq_handler,
|
||||
.irq_cmd_handler = cyapa_pip_irq_cmd_handler,
|
||||
.sort_empty_output_data = cyapa_empty_pip_output_data,
|
||||
.set_power_mode = cyapa_gen6_set_power_mode,
|
||||
|
||||
.set_proximity = cyapa_gen6_set_proximity,
|
||||
};
|
@ -4,7 +4,7 @@
|
||||
* Copyright (c) 2013 ELAN Microelectronics Corp.
|
||||
*
|
||||
* Author: 林政維 (Duson Lin) <dusonlin@emc.com.tw>
|
||||
* Version: 1.5.9
|
||||
* Version: 1.6.0
|
||||
*
|
||||
* Based on cyapa driver:
|
||||
* copyright (c) 2011-2012 Cypress Semiconductor, Inc.
|
||||
@ -40,7 +40,7 @@
|
||||
#include "elan_i2c.h"
|
||||
|
||||
#define DRIVER_NAME "elan_i2c"
|
||||
#define ELAN_DRIVER_VERSION "1.5.9"
|
||||
#define ELAN_DRIVER_VERSION "1.6.0"
|
||||
#define ETP_MAX_PRESSURE 255
|
||||
#define ETP_FWIDTH_REDUCE 90
|
||||
#define ETP_FINGER_WIDTH 15
|
||||
@ -84,7 +84,7 @@ struct elan_tp_data {
|
||||
int pressure_adjustment;
|
||||
u8 mode;
|
||||
u8 ic_type;
|
||||
u16 fw_vaildpage_count;
|
||||
u16 fw_validpage_count;
|
||||
u16 fw_signature_address;
|
||||
|
||||
bool irq_wake;
|
||||
@ -94,25 +94,28 @@ struct elan_tp_data {
|
||||
bool baseline_ready;
|
||||
};
|
||||
|
||||
static int elan_get_fwinfo(u8 ic_type, u16 *vaildpage_count,
|
||||
static int elan_get_fwinfo(u8 iap_version, u16 *validpage_count,
|
||||
u16 *signature_address)
|
||||
{
|
||||
switch(ic_type) {
|
||||
switch (iap_version) {
|
||||
case 0x08:
|
||||
*validpage_count = 512;
|
||||
break;
|
||||
case 0x09:
|
||||
*vaildpage_count = 768;
|
||||
*validpage_count = 768;
|
||||
break;
|
||||
case 0x0D:
|
||||
*vaildpage_count = 896;
|
||||
*validpage_count = 896;
|
||||
break;
|
||||
default:
|
||||
/* unknown ic type clear value */
|
||||
*vaildpage_count = 0;
|
||||
*validpage_count = 0;
|
||||
*signature_address = 0;
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
*signature_address =
|
||||
(*vaildpage_count * ETP_FW_PAGE_SIZE) - ETP_FW_SIGNATURE_SIZE;
|
||||
(*validpage_count * ETP_FW_PAGE_SIZE) - ETP_FW_SIGNATURE_SIZE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -261,11 +264,11 @@ static int elan_query_device_info(struct elan_tp_data *data)
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = elan_get_fwinfo(data->ic_type, &data->fw_vaildpage_count,
|
||||
error = elan_get_fwinfo(data->iap_version, &data->fw_validpage_count,
|
||||
&data->fw_signature_address);
|
||||
if (error) {
|
||||
dev_err(&data->client->dev,
|
||||
"unknown ic type %d\n", data->ic_type);
|
||||
"unknown iap version %d\n", data->iap_version);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -353,7 +356,7 @@ static int __elan_update_firmware(struct elan_tp_data *data,
|
||||
iap_start_addr = get_unaligned_le16(&fw->data[ETP_IAP_START_ADDR * 2]);
|
||||
|
||||
boot_page_count = (iap_start_addr * 2) / ETP_FW_PAGE_SIZE;
|
||||
for (i = boot_page_count; i < data->fw_vaildpage_count; i++) {
|
||||
for (i = boot_page_count; i < data->fw_validpage_count; i++) {
|
||||
u16 checksum = 0;
|
||||
const u8 *page = &fw->data[i * ETP_FW_PAGE_SIZE];
|
||||
|
||||
@ -1165,6 +1168,8 @@ MODULE_DEVICE_TABLE(i2c, elan_id);
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id elan_acpi_id[] = {
|
||||
{ "ELAN0000", 0 },
|
||||
{ "ELAN0100", 0 },
|
||||
{ "ELAN0600", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, elan_acpi_id);
|
||||
@ -1181,10 +1186,10 @@ MODULE_DEVICE_TABLE(of, elan_of_match);
|
||||
static struct i2c_driver elan_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &elan_pm_ops,
|
||||
.acpi_match_table = ACPI_PTR(elan_acpi_id),
|
||||
.of_match_table = of_match_ptr(elan_of_match),
|
||||
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||
},
|
||||
.probe = elan_probe,
|
||||
.id_table = elan_id,
|
||||
|
@ -1540,6 +1540,10 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv)
|
||||
if (error)
|
||||
goto err_clear_drvdata;
|
||||
|
||||
/* give PT device some time to settle down before probing */
|
||||
if (serio->id.type == SERIO_PS_PSTHRU)
|
||||
usleep_range(10000, 15000);
|
||||
|
||||
if (psmouse_probe(psmouse) < 0) {
|
||||
error = -ENODEV;
|
||||
goto err_close_serio;
|
||||
|
@ -432,7 +432,7 @@ static int fsp_onpad_hscr(struct psmouse *psmouse, bool enable)
|
||||
static ssize_t fsp_attr_set_setreg(struct psmouse *psmouse, void *data,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int reg, val;
|
||||
unsigned int reg, val;
|
||||
char *rest;
|
||||
ssize_t retval;
|
||||
|
||||
@ -440,7 +440,7 @@ static ssize_t fsp_attr_set_setreg(struct psmouse *psmouse, void *data,
|
||||
if (rest == buf || *rest != ' ' || reg > 0xff)
|
||||
return -EINVAL;
|
||||
|
||||
retval = kstrtoint(rest + 1, 16, &val);
|
||||
retval = kstrtouint(rest + 1, 16, &val);
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
@ -476,9 +476,10 @@ static ssize_t fsp_attr_set_getreg(struct psmouse *psmouse, void *data,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct fsp_data *pad = psmouse->private;
|
||||
int reg, val, err;
|
||||
unsigned int reg, val;
|
||||
int err;
|
||||
|
||||
err = kstrtoint(buf, 16, ®);
|
||||
err = kstrtouint(buf, 16, ®);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
@ -511,9 +512,10 @@ static ssize_t fsp_attr_show_pagereg(struct psmouse *psmouse,
|
||||
static ssize_t fsp_attr_set_pagereg(struct psmouse *psmouse, void *data,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
int val, err;
|
||||
unsigned int val;
|
||||
int err;
|
||||
|
||||
err = kstrtoint(buf, 16, &val);
|
||||
err = kstrtouint(buf, 16, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
@ -519,14 +519,18 @@ static int synaptics_set_mode(struct psmouse *psmouse)
|
||||
struct synaptics_data *priv = psmouse->private;
|
||||
|
||||
priv->mode = 0;
|
||||
if (priv->absolute_mode)
|
||||
|
||||
if (priv->absolute_mode) {
|
||||
priv->mode |= SYN_BIT_ABSOLUTE_MODE;
|
||||
if (priv->disable_gesture)
|
||||
priv->mode |= SYN_BIT_DISABLE_GESTURE;
|
||||
if (psmouse->rate >= 80)
|
||||
priv->mode |= SYN_BIT_HIGH_RATE;
|
||||
if (SYN_CAP_EXTENDED(priv->capabilities))
|
||||
priv->mode |= SYN_BIT_W_MODE;
|
||||
}
|
||||
|
||||
if (!SYN_MODE_WMODE(priv->mode) && priv->disable_gesture)
|
||||
priv->mode |= SYN_BIT_DISABLE_GESTURE;
|
||||
|
||||
if (psmouse->rate >= 80)
|
||||
priv->mode |= SYN_BIT_HIGH_RATE;
|
||||
|
||||
if (synaptics_mode_cmd(psmouse, priv->mode))
|
||||
return -1;
|
||||
|
@ -655,7 +655,6 @@ MODULE_DEVICE_TABLE(i2c, synaptics_i2c_id_table);
|
||||
static struct i2c_driver synaptics_i2c_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &synaptics_i2c_pm,
|
||||
},
|
||||
|
||||
|
@ -175,9 +175,9 @@ static int amba_kmi_remove(struct amba_device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amba_kmi_resume(struct amba_device *dev)
|
||||
static int __maybe_unused amba_kmi_resume(struct device *dev)
|
||||
{
|
||||
struct amba_kmi_port *kmi = amba_get_drvdata(dev);
|
||||
struct amba_kmi_port *kmi = dev_get_drvdata(dev);
|
||||
|
||||
/* kick the serio layer to rescan this port */
|
||||
serio_reconnect(kmi->io);
|
||||
@ -185,6 +185,8 @@ static int amba_kmi_resume(struct amba_device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(amba_kmi_dev_pm_ops, NULL, amba_kmi_resume);
|
||||
|
||||
static struct amba_id amba_kmi_idtable[] = {
|
||||
{
|
||||
.id = 0x00041050,
|
||||
@ -199,11 +201,11 @@ static struct amba_driver ambakmi_driver = {
|
||||
.drv = {
|
||||
.name = "kmi-pl050",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &amba_kmi_dev_pm_ops,
|
||||
},
|
||||
.id_table = amba_kmi_idtable,
|
||||
.probe = amba_kmi_probe,
|
||||
.remove = amba_kmi_remove,
|
||||
.resume = amba_kmi_resume,
|
||||
};
|
||||
|
||||
module_amba_driver(ambakmi_driver);
|
||||
|
@ -88,6 +88,10 @@ MODULE_PARM_DESC(nopnp, "Do not use PNP to detect controller settings");
|
||||
static bool i8042_debug;
|
||||
module_param_named(debug, i8042_debug, bool, 0600);
|
||||
MODULE_PARM_DESC(debug, "Turn i8042 debugging mode on and off");
|
||||
|
||||
static bool i8042_unmask_kbd_data;
|
||||
module_param_named(unmask_kbd_data, i8042_unmask_kbd_data, bool, 0600);
|
||||
MODULE_PARM_DESC(unmask_kbd_data, "Unconditional enable (may reveal sensitive data) of normally sanitize-filtered kbd data traffic debug log [pre-condition: i8042.debug=1 enabled]");
|
||||
#endif
|
||||
|
||||
static bool i8042_bypass_aux_irq_test;
|
||||
@ -116,6 +120,7 @@ struct i8042_port {
|
||||
struct serio *serio;
|
||||
int irq;
|
||||
bool exists;
|
||||
bool driver_bound;
|
||||
signed char mux;
|
||||
};
|
||||
|
||||
@ -133,6 +138,7 @@ static bool i8042_kbd_irq_registered;
|
||||
static bool i8042_aux_irq_registered;
|
||||
static unsigned char i8042_suppress_kbd_ack;
|
||||
static struct platform_device *i8042_platform_device;
|
||||
static struct notifier_block i8042_kbd_bind_notifier_block;
|
||||
|
||||
static irqreturn_t i8042_interrupt(int irq, void *dev_id);
|
||||
static bool (*i8042_platform_filter)(unsigned char data, unsigned char str,
|
||||
@ -528,8 +534,8 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id)
|
||||
port = &i8042_ports[port_no];
|
||||
serio = port->exists ? port->serio : NULL;
|
||||
|
||||
dbg("%02x <- i8042 (interrupt, %d, %d%s%s)\n",
|
||||
data, port_no, irq,
|
||||
filter_dbg(port->driver_bound, data, "<- i8042 (interrupt, %d, %d%s%s)\n",
|
||||
port_no, irq,
|
||||
dfl & SERIO_PARITY ? ", bad parity" : "",
|
||||
dfl & SERIO_TIMEOUT ? ", timeout" : "");
|
||||
|
||||
@ -1438,6 +1444,29 @@ static int __init i8042_setup_kbd(void)
|
||||
return error;
|
||||
}
|
||||
|
||||
static int i8042_kbd_bind_notifier(struct notifier_block *nb,
|
||||
unsigned long action, void *data)
|
||||
{
|
||||
struct device *dev = data;
|
||||
struct serio *serio = to_serio_port(dev);
|
||||
struct i8042_port *port = serio->port_data;
|
||||
|
||||
if (serio != i8042_ports[I8042_KBD_PORT_NO].serio)
|
||||
return 0;
|
||||
|
||||
switch (action) {
|
||||
case BUS_NOTIFY_BOUND_DRIVER:
|
||||
port->driver_bound = true;
|
||||
break;
|
||||
|
||||
case BUS_NOTIFY_UNBIND_DRIVER:
|
||||
port->driver_bound = false;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init i8042_probe(struct platform_device *dev)
|
||||
{
|
||||
int error;
|
||||
@ -1507,6 +1536,10 @@ static struct platform_driver i8042_driver = {
|
||||
.shutdown = i8042_shutdown,
|
||||
};
|
||||
|
||||
static struct notifier_block i8042_kbd_bind_notifier_block = {
|
||||
.notifier_call = i8042_kbd_bind_notifier,
|
||||
};
|
||||
|
||||
static int __init i8042_init(void)
|
||||
{
|
||||
struct platform_device *pdev;
|
||||
@ -1528,6 +1561,7 @@ static int __init i8042_init(void)
|
||||
goto err_platform_exit;
|
||||
}
|
||||
|
||||
bus_register_notifier(&serio_bus, &i8042_kbd_bind_notifier_block);
|
||||
panic_blink = i8042_panic_blink;
|
||||
|
||||
return 0;
|
||||
@ -1543,6 +1577,7 @@ static void __exit i8042_exit(void)
|
||||
platform_driver_unregister(&i8042_driver);
|
||||
i8042_platform_exit();
|
||||
|
||||
bus_unregister_notifier(&serio_bus, &i8042_kbd_bind_notifier_block);
|
||||
panic_blink = NULL;
|
||||
}
|
||||
|
||||
|
@ -73,6 +73,17 @@ static unsigned long i8042_start_time;
|
||||
printk(KERN_DEBUG KBUILD_MODNAME ": [%d] " format, \
|
||||
(int) (jiffies - i8042_start_time), ##arg); \
|
||||
} while (0)
|
||||
|
||||
#define filter_dbg(filter, data, format, args...) \
|
||||
do { \
|
||||
if (!i8042_debug) \
|
||||
break; \
|
||||
\
|
||||
if (!filter || i8042_unmask_kbd_data) \
|
||||
dbg("%02x " format, data, ##args); \
|
||||
else \
|
||||
dbg("** " format, ##args); \
|
||||
} while (0)
|
||||
#else
|
||||
#define dbg_init() do { } while (0)
|
||||
#define dbg(format, arg...) \
|
||||
@ -80,6 +91,8 @@ static unsigned long i8042_start_time;
|
||||
if (0) \
|
||||
printk(KERN_DEBUG pr_fmt(format), ##arg); \
|
||||
} while (0)
|
||||
|
||||
#define filter_dbg(filter, data, format, args...) do { } while (0)
|
||||
#endif
|
||||
|
||||
#endif /* _I8042_H */
|
||||
|
@ -49,8 +49,6 @@ static DEFINE_MUTEX(serio_mutex);
|
||||
|
||||
static LIST_HEAD(serio_list);
|
||||
|
||||
static struct bus_type serio_bus;
|
||||
|
||||
static void serio_add_port(struct serio *serio);
|
||||
static int serio_reconnect_port(struct serio *serio);
|
||||
static void serio_disconnect_port(struct serio *serio);
|
||||
@ -1017,7 +1015,7 @@ irqreturn_t serio_interrupt(struct serio *serio,
|
||||
}
|
||||
EXPORT_SYMBOL(serio_interrupt);
|
||||
|
||||
static struct bus_type serio_bus = {
|
||||
struct bus_type serio_bus = {
|
||||
.name = "serio",
|
||||
.drv_groups = serio_driver_groups,
|
||||
.match = serio_bus_match,
|
||||
@ -1029,6 +1027,7 @@ static struct bus_type serio_bus = {
|
||||
.pm = &serio_pm_ops,
|
||||
#endif
|
||||
};
|
||||
EXPORT_SYMBOL(serio_bus);
|
||||
|
||||
static int __init serio_init(void)
|
||||
{
|
||||
|
@ -11,9 +11,9 @@ menuconfig INPUT_TOUCHSCREEN
|
||||
|
||||
if INPUT_TOUCHSCREEN
|
||||
|
||||
config OF_TOUCHSCREEN
|
||||
config TOUCHSCREEN_PROPERTIES
|
||||
def_tristate INPUT
|
||||
depends on INPUT && OF
|
||||
depends on INPUT
|
||||
|
||||
config TOUCHSCREEN_88PM860X
|
||||
tristate "Marvell 88PM860x touchscreen"
|
||||
@ -118,7 +118,7 @@ config TOUCHSCREEN_ATMEL_MXT
|
||||
config TOUCHSCREEN_AUO_PIXCIR
|
||||
tristate "AUO in-cell touchscreen using Pixcir ICs"
|
||||
depends on I2C
|
||||
depends on GPIOLIB
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
help
|
||||
Say Y here if you have a AUO display with in-cell touchscreen
|
||||
using Pixcir ICs.
|
||||
@ -142,7 +142,7 @@ config TOUCHSCREEN_BU21013
|
||||
|
||||
config TOUCHSCREEN_CHIPONE_ICN8318
|
||||
tristate "chipone icn8318 touchscreen controller"
|
||||
depends on GPIOLIB
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
depends on I2C
|
||||
depends on OF
|
||||
help
|
||||
@ -156,7 +156,7 @@ config TOUCHSCREEN_CHIPONE_ICN8318
|
||||
config TOUCHSCREEN_CY8CTMG110
|
||||
tristate "cy8ctmg110 touchscreen"
|
||||
depends on I2C
|
||||
depends on GPIOLIB
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
help
|
||||
Say Y here if you have a cy8ctmg110 capacitive touchscreen on
|
||||
an AAVA device.
|
||||
@ -917,6 +917,7 @@ config TOUCHSCREEN_TSC_SERIO
|
||||
config TOUCHSCREEN_TSC2005
|
||||
tristate "TSC2005 based touchscreens"
|
||||
depends on SPI_MASTER
|
||||
select REGMAP_SPI
|
||||
help
|
||||
Say Y here if you have a TSC2005 based touchscreen.
|
||||
|
||||
@ -1029,7 +1030,7 @@ config TOUCHSCREEN_TPS6507X
|
||||
config TOUCHSCREEN_ZFORCE
|
||||
tristate "Neonode zForce infrared touchscreens"
|
||||
depends on I2C
|
||||
depends on GPIOLIB
|
||||
depends on GPIOLIB || COMPILE_TEST
|
||||
help
|
||||
Say Y here if you have a touchscreen using the zforce
|
||||
infraread technology from Neonode.
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
wm97xx-ts-y := wm97xx-core.o
|
||||
|
||||
obj-$(CONFIG_OF_TOUCHSCREEN) += of_touchscreen.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_PROPERTIES) += of_touchscreen.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_88PM860X) += 88pm860x-ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_AD7877) += ad7877.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_AD7879) += ad7879.o
|
||||
|
@ -94,7 +94,6 @@ MODULE_DEVICE_TABLE(i2c, ad7879_id);
|
||||
static struct i2c_driver ad7879_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "ad7879",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &ad7879_pm_ops,
|
||||
},
|
||||
.probe = ad7879_i2c_probe,
|
||||
|
@ -1234,7 +1234,8 @@ static const struct ads7846_platform_data *ads7846_probe_dt(struct device *dev)
|
||||
of_property_read_u32(node, "ti,pendown-gpio-debounce",
|
||||
&pdata->gpio_pendown_debounce);
|
||||
|
||||
pdata->wakeup = of_property_read_bool(node, "linux,wakeup");
|
||||
pdata->wakeup = of_property_read_bool(node, "wakeup-source") ||
|
||||
of_property_read_bool(node, "linux,wakeup");
|
||||
|
||||
pdata->gpio_pendown = of_get_named_gpio(dev->of_node, "pendown-gpio", 0);
|
||||
|
||||
|
@ -166,7 +166,6 @@ MODULE_DEVICE_TABLE(of, ar1021_i2c_of_match);
|
||||
static struct i2c_driver ar1021_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "ar1021_i2c",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &ar1021_i2c_pm,
|
||||
.of_match_table = ar1021_i2c_of_match,
|
||||
},
|
||||
|
@ -22,34 +22,20 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c/atmel_mxt_ts.h>
|
||||
#include <linux/platform_data/atmel_mxt_ts.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
/* Version */
|
||||
#define MXT_VER_20 20
|
||||
#define MXT_VER_21 21
|
||||
#define MXT_VER_22 22
|
||||
|
||||
/* Firmware files */
|
||||
#define MXT_FW_NAME "maxtouch.fw"
|
||||
#define MXT_CFG_NAME "maxtouch.cfg"
|
||||
#define MXT_CFG_MAGIC "OBP_RAW V1"
|
||||
|
||||
/* Registers */
|
||||
#define MXT_INFO 0x00
|
||||
#define MXT_FAMILY_ID 0x00
|
||||
#define MXT_VARIANT_ID 0x01
|
||||
#define MXT_VERSION 0x02
|
||||
#define MXT_BUILD 0x03
|
||||
#define MXT_MATRIX_X_SIZE 0x04
|
||||
#define MXT_MATRIX_Y_SIZE 0x05
|
||||
#define MXT_OBJECT_NUM 0x06
|
||||
#define MXT_OBJECT_START 0x07
|
||||
|
||||
#define MXT_OBJECT_SIZE 6
|
||||
#define MXT_INFO_CHECKSUM_SIZE 3
|
||||
#define MXT_MAX_BLOCK_WRITE 256
|
||||
@ -103,21 +89,16 @@
|
||||
#define MXT_T6_STATUS_COMSERR (1 << 2)
|
||||
|
||||
/* MXT_GEN_POWER_T7 field */
|
||||
#define MXT_POWER_IDLEACQINT 0
|
||||
#define MXT_POWER_ACTVACQINT 1
|
||||
#define MXT_POWER_ACTV2IDLETO 2
|
||||
struct t7_config {
|
||||
u8 idle;
|
||||
u8 active;
|
||||
} __packed;
|
||||
|
||||
/* MXT_GEN_ACQUIRE_T8 field */
|
||||
#define MXT_ACQUIRE_CHRGTIME 0
|
||||
#define MXT_ACQUIRE_TCHDRIFT 2
|
||||
#define MXT_ACQUIRE_DRIFTST 3
|
||||
#define MXT_ACQUIRE_TCHAUTOCAL 4
|
||||
#define MXT_ACQUIRE_SYNC 5
|
||||
#define MXT_ACQUIRE_ATCHCALST 6
|
||||
#define MXT_ACQUIRE_ATCHCALSTHR 7
|
||||
#define MXT_POWER_CFG_RUN 0
|
||||
#define MXT_POWER_CFG_DEEPSLEEP 1
|
||||
|
||||
/* MXT_TOUCH_MULTI_T9 field */
|
||||
#define MXT_TOUCH_CTRL 0
|
||||
#define MXT_T9_CTRL 0
|
||||
#define MXT_T9_ORIENT 9
|
||||
#define MXT_T9_RANGE 18
|
||||
|
||||
@ -139,51 +120,10 @@ struct t9_range {
|
||||
/* MXT_TOUCH_MULTI_T9 orient */
|
||||
#define MXT_T9_ORIENT_SWITCH (1 << 0)
|
||||
|
||||
/* MXT_PROCI_GRIPFACE_T20 field */
|
||||
#define MXT_GRIPFACE_CTRL 0
|
||||
#define MXT_GRIPFACE_XLOGRIP 1
|
||||
#define MXT_GRIPFACE_XHIGRIP 2
|
||||
#define MXT_GRIPFACE_YLOGRIP 3
|
||||
#define MXT_GRIPFACE_YHIGRIP 4
|
||||
#define MXT_GRIPFACE_MAXTCHS 5
|
||||
#define MXT_GRIPFACE_SZTHR1 7
|
||||
#define MXT_GRIPFACE_SZTHR2 8
|
||||
#define MXT_GRIPFACE_SHPTHR1 9
|
||||
#define MXT_GRIPFACE_SHPTHR2 10
|
||||
#define MXT_GRIPFACE_SUPEXTTO 11
|
||||
|
||||
/* MXT_PROCI_NOISE field */
|
||||
#define MXT_NOISE_CTRL 0
|
||||
#define MXT_NOISE_OUTFLEN 1
|
||||
#define MXT_NOISE_GCAFUL_LSB 3
|
||||
#define MXT_NOISE_GCAFUL_MSB 4
|
||||
#define MXT_NOISE_GCAFLL_LSB 5
|
||||
#define MXT_NOISE_GCAFLL_MSB 6
|
||||
#define MXT_NOISE_ACTVGCAFVALID 7
|
||||
#define MXT_NOISE_NOISETHR 8
|
||||
#define MXT_NOISE_FREQHOPSCALE 10
|
||||
#define MXT_NOISE_FREQ0 11
|
||||
#define MXT_NOISE_FREQ1 12
|
||||
#define MXT_NOISE_FREQ2 13
|
||||
#define MXT_NOISE_FREQ3 14
|
||||
#define MXT_NOISE_FREQ4 15
|
||||
#define MXT_NOISE_IDLEGCAFVALID 16
|
||||
|
||||
/* MXT_SPT_COMMSCONFIG_T18 */
|
||||
#define MXT_COMMS_CTRL 0
|
||||
#define MXT_COMMS_CMD 1
|
||||
|
||||
/* MXT_SPT_CTECONFIG_T28 field */
|
||||
#define MXT_CTE_CTRL 0
|
||||
#define MXT_CTE_CMD 1
|
||||
#define MXT_CTE_MODE 2
|
||||
#define MXT_CTE_IDLEGCAFDEPTH 3
|
||||
#define MXT_CTE_ACTVGCAFDEPTH 4
|
||||
#define MXT_CTE_VOLTAGE 5
|
||||
|
||||
#define MXT_VOLTAGE_DEFAULT 2700000
|
||||
#define MXT_VOLTAGE_STEP 10000
|
||||
|
||||
/* Define for MXT_GEN_COMMAND_T6 */
|
||||
#define MXT_BOOT_VALUE 0xa5
|
||||
#define MXT_RESET_VALUE 0x01
|
||||
@ -291,6 +231,7 @@ struct mxt_data {
|
||||
u8 last_message_count;
|
||||
u8 num_touchids;
|
||||
u8 multitouch;
|
||||
struct t7_config t7_cfg;
|
||||
|
||||
/* Cached parameters from object table */
|
||||
u16 T5_address;
|
||||
@ -997,16 +938,15 @@ static irqreturn_t mxt_process_messages_t44(struct mxt_data *data)
|
||||
|
||||
count = data->msg_buf[0];
|
||||
|
||||
if (count == 0) {
|
||||
/*
|
||||
* This condition is caused by the CHG line being configured
|
||||
* in Mode 0. It results in unnecessary I2C operations but it
|
||||
* is benign.
|
||||
* This condition may be caused by the CHG line being configured in
|
||||
* Mode 0. It results in unnecessary I2C operations but it is benign.
|
||||
*/
|
||||
dev_dbg(dev, "Interrupt triggered but zero messages\n");
|
||||
if (count == 0)
|
||||
return IRQ_NONE;
|
||||
} else if (count > data->max_reportid) {
|
||||
dev_err(dev, "T44 count %d exceeded max report id\n", count);
|
||||
|
||||
if (count > data->max_reportid) {
|
||||
dev_warn(dev, "T44 count %d exceeded max report id\n", count);
|
||||
count = data->max_reportid;
|
||||
}
|
||||
|
||||
@ -1157,7 +1097,9 @@ static int mxt_soft_reset(struct mxt_data *data)
|
||||
struct device *dev = &data->client->dev;
|
||||
int ret = 0;
|
||||
|
||||
dev_info(dev, "Resetting chip\n");
|
||||
dev_info(dev, "Resetting device\n");
|
||||
|
||||
disable_irq(data->irq);
|
||||
|
||||
reinit_completion(&data->reset_completion);
|
||||
|
||||
@ -1165,6 +1107,11 @@ static int mxt_soft_reset(struct mxt_data *data)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Ignore CHG line for 100ms after reset */
|
||||
msleep(100);
|
||||
|
||||
enable_irq(data->irq);
|
||||
|
||||
ret = mxt_wait_for_completion(data, &data->reset_completion,
|
||||
MXT_RESET_TIMEOUT);
|
||||
if (ret)
|
||||
@ -1361,6 +1308,8 @@ static int mxt_upload_cfg_mem(struct mxt_data *data, unsigned int cfg_start,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxt_init_t7_power_cfg(struct mxt_data *data);
|
||||
|
||||
/*
|
||||
* mxt_update_cfg - download configuration to chip
|
||||
*
|
||||
@ -1508,6 +1457,9 @@ static int mxt_update_cfg(struct mxt_data *data, const struct firmware *cfg)
|
||||
|
||||
dev_info(dev, "Config successfully updated\n");
|
||||
|
||||
/* T7 config may have changed */
|
||||
mxt_init_t7_power_cfg(data);
|
||||
|
||||
release_mem:
|
||||
kfree(config_mem);
|
||||
return ret;
|
||||
@ -1533,7 +1485,7 @@ static int mxt_get_info(struct mxt_data *data)
|
||||
int error;
|
||||
|
||||
/* Read 7-byte info block starting at address 0 */
|
||||
error = __mxt_read_reg(client, MXT_INFO, sizeof(*info), info);
|
||||
error = __mxt_read_reg(client, 0, sizeof(*info), info);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
@ -1905,6 +1857,8 @@ static int mxt_initialize_input_device(struct mxt_data *data)
|
||||
if (pdata->t19_num_keys) {
|
||||
mxt_set_up_as_touchpad(input_dev, data);
|
||||
mt_flags |= INPUT_MT_POINTER;
|
||||
} else {
|
||||
mt_flags |= INPUT_MT_DIRECT;
|
||||
}
|
||||
|
||||
/* For multi touch */
|
||||
@ -2051,6 +2005,60 @@ err_free_object_table:
|
||||
return error;
|
||||
}
|
||||
|
||||
static int mxt_set_t7_power_cfg(struct mxt_data *data, u8 sleep)
|
||||
{
|
||||
struct device *dev = &data->client->dev;
|
||||
int error;
|
||||
struct t7_config *new_config;
|
||||
struct t7_config deepsleep = { .active = 0, .idle = 0 };
|
||||
|
||||
if (sleep == MXT_POWER_CFG_DEEPSLEEP)
|
||||
new_config = &deepsleep;
|
||||
else
|
||||
new_config = &data->t7_cfg;
|
||||
|
||||
error = __mxt_write_reg(data->client, data->T7_address,
|
||||
sizeof(data->t7_cfg), new_config);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
dev_dbg(dev, "Set T7 ACTV:%d IDLE:%d\n",
|
||||
new_config->active, new_config->idle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxt_init_t7_power_cfg(struct mxt_data *data)
|
||||
{
|
||||
struct device *dev = &data->client->dev;
|
||||
int error;
|
||||
bool retry = false;
|
||||
|
||||
recheck:
|
||||
error = __mxt_read_reg(data->client, data->T7_address,
|
||||
sizeof(data->t7_cfg), &data->t7_cfg);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (data->t7_cfg.active == 0 || data->t7_cfg.idle == 0) {
|
||||
if (!retry) {
|
||||
dev_dbg(dev, "T7 cfg zero, resetting\n");
|
||||
mxt_soft_reset(data);
|
||||
retry = true;
|
||||
goto recheck;
|
||||
} else {
|
||||
dev_dbg(dev, "T7 cfg zero after reset, overriding\n");
|
||||
data->t7_cfg.active = 20;
|
||||
data->t7_cfg.idle = 100;
|
||||
return mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN);
|
||||
}
|
||||
}
|
||||
|
||||
dev_dbg(dev, "Initialized power cfg: ACTV %d, IDLE %d\n",
|
||||
data->t7_cfg.active, data->t7_cfg.idle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mxt_configure_objects(struct mxt_data *data,
|
||||
const struct firmware *cfg)
|
||||
{
|
||||
@ -2058,6 +2066,12 @@ static int mxt_configure_objects(struct mxt_data *data,
|
||||
struct mxt_info *info = &data->info;
|
||||
int error;
|
||||
|
||||
error = mxt_init_t7_power_cfg(data);
|
||||
if (error) {
|
||||
dev_err(dev, "Failed to initialize power cfg\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
if (cfg) {
|
||||
error = mxt_update_cfg(data, cfg);
|
||||
if (error)
|
||||
@ -2346,14 +2360,41 @@ static const struct attribute_group mxt_attr_group = {
|
||||
|
||||
static void mxt_start(struct mxt_data *data)
|
||||
{
|
||||
switch (data->pdata->suspend_mode) {
|
||||
case MXT_SUSPEND_T9_CTRL:
|
||||
mxt_soft_reset(data);
|
||||
|
||||
/* Touch enable */
|
||||
mxt_write_object(data, data->multitouch, MXT_TOUCH_CTRL, 0x83);
|
||||
/* 0x83 = SCANEN | RPTEN | ENABLE */
|
||||
mxt_write_object(data,
|
||||
MXT_TOUCH_MULTI_T9, MXT_T9_CTRL, 0x83);
|
||||
break;
|
||||
|
||||
case MXT_SUSPEND_DEEP_SLEEP:
|
||||
default:
|
||||
mxt_set_t7_power_cfg(data, MXT_POWER_CFG_RUN);
|
||||
|
||||
/* Recalibrate since chip has been in deep sleep */
|
||||
mxt_t6_command(data, MXT_COMMAND_CALIBRATE, 1, false);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static void mxt_stop(struct mxt_data *data)
|
||||
{
|
||||
switch (data->pdata->suspend_mode) {
|
||||
case MXT_SUSPEND_T9_CTRL:
|
||||
/* Touch disable */
|
||||
mxt_write_object(data, data->multitouch, MXT_TOUCH_CTRL, 0);
|
||||
mxt_write_object(data,
|
||||
MXT_TOUCH_MULTI_T9, MXT_T9_CTRL, 0);
|
||||
break;
|
||||
|
||||
case MXT_SUSPEND_DEEP_SLEEP:
|
||||
default:
|
||||
mxt_set_t7_power_cfg(data, MXT_POWER_CFG_DEEPSLEEP);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int mxt_input_open(struct input_dev *dev)
|
||||
@ -2376,19 +2417,18 @@ static void mxt_input_close(struct input_dev *dev)
|
||||
static const struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client)
|
||||
{
|
||||
struct mxt_platform_data *pdata;
|
||||
struct device_node *np = client->dev.of_node;
|
||||
u32 *keymap;
|
||||
u32 keycode;
|
||||
int proplen, i, ret;
|
||||
int proplen, ret;
|
||||
|
||||
if (!client->dev.of_node)
|
||||
if (!np)
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
if (of_find_property(client->dev.of_node, "linux,gpio-keymap",
|
||||
&proplen)) {
|
||||
if (of_find_property(np, "linux,gpio-keymap", &proplen)) {
|
||||
pdata->t19_num_keys = proplen / sizeof(u32);
|
||||
|
||||
keymap = devm_kzalloc(&client->dev,
|
||||
@ -2397,18 +2437,17 @@ static const struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client)
|
||||
if (!keymap)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
for (i = 0; i < pdata->t19_num_keys; i++) {
|
||||
ret = of_property_read_u32_index(client->dev.of_node,
|
||||
"linux,gpio-keymap", i, &keycode);
|
||||
ret = of_property_read_u32_array(np, "linux,gpio-keymap",
|
||||
keymap, pdata->t19_num_keys);
|
||||
if (ret)
|
||||
keycode = KEY_RESERVED;
|
||||
|
||||
keymap[i] = keycode;
|
||||
}
|
||||
dev_warn(&client->dev,
|
||||
"Couldn't read linux,gpio-keymap: %d\n", ret);
|
||||
|
||||
pdata->t19_keymap = keymap;
|
||||
}
|
||||
|
||||
pdata->suspend_mode = MXT_SUSPEND_DEEP_SLEEP;
|
||||
|
||||
return pdata;
|
||||
}
|
||||
#else
|
||||
@ -2609,6 +2648,9 @@ static int __maybe_unused mxt_suspend(struct device *dev)
|
||||
struct mxt_data *data = i2c_get_clientdata(client);
|
||||
struct input_dev *input_dev = data->input_dev;
|
||||
|
||||
if (!input_dev)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&input_dev->mutex);
|
||||
|
||||
if (input_dev->users)
|
||||
@ -2625,7 +2667,8 @@ static int __maybe_unused mxt_resume(struct device *dev)
|
||||
struct mxt_data *data = i2c_get_clientdata(client);
|
||||
struct input_dev *input_dev = data->input_dev;
|
||||
|
||||
mxt_soft_reset(data);
|
||||
if (!input_dev)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&input_dev->mutex);
|
||||
|
||||
@ -2666,7 +2709,6 @@ MODULE_DEVICE_TABLE(i2c, mxt_id);
|
||||
static struct i2c_driver mxt_driver = {
|
||||
.driver = {
|
||||
.name = "atmel_mxt_ts",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(mxt_of_match),
|
||||
.acpi_match_table = ACPI_PTR(mxt_acpi_id),
|
||||
.pm = &mxt_pm_ops,
|
||||
|
@ -686,7 +686,6 @@ MODULE_DEVICE_TABLE(of, auo_pixcir_ts_dt_idtable);
|
||||
|
||||
static struct i2c_driver auo_pixcir_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "auo_pixcir_ts",
|
||||
.pm = &auo_pixcir_pm_ops,
|
||||
.of_match_table = of_match_ptr(auo_pixcir_ts_dt_idtable),
|
||||
|
@ -716,7 +716,6 @@ MODULE_DEVICE_TABLE(i2c, bu21013_id);
|
||||
static struct i2c_driver bu21013_driver = {
|
||||
.driver = {
|
||||
.name = DRIVER_TP,
|
||||
.owner = THIS_MODULE,
|
||||
#ifdef CONFIG_PM
|
||||
.pm = &bu21013_dev_pm_ops,
|
||||
#endif
|
||||
|
@ -300,7 +300,6 @@ MODULE_DEVICE_TABLE(i2c, icn8318_i2c_id);
|
||||
|
||||
static struct i2c_driver icn8318_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "chipone_icn8318",
|
||||
.pm = &icn8318_pm_ops,
|
||||
.of_match_table = icn8318_of_match,
|
||||
|
@ -347,7 +347,6 @@ MODULE_DEVICE_TABLE(i2c, cy8ctmg110_idtable);
|
||||
|
||||
static struct i2c_driver cy8ctmg110_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = CY8CTMG110_DRIVER_NAME,
|
||||
.pm = &cy8ctmg110_pm,
|
||||
},
|
||||
|
@ -74,7 +74,6 @@ MODULE_DEVICE_TABLE(i2c, cyttsp4_i2c_id);
|
||||
static struct i2c_driver cyttsp4_i2c_driver = {
|
||||
.driver = {
|
||||
.name = CYTTSP4_I2C_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &cyttsp4_pm_ops,
|
||||
},
|
||||
.probe = cyttsp4_i2c_probe,
|
||||
|
@ -74,7 +74,6 @@ MODULE_DEVICE_TABLE(i2c, cyttsp_i2c_id);
|
||||
static struct i2c_driver cyttsp_i2c_driver = {
|
||||
.driver = {
|
||||
.name = CY_I2C_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &cyttsp_pm_ops,
|
||||
},
|
||||
.probe = cyttsp_i2c_probe,
|
||||
|
@ -1041,7 +1041,7 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client,
|
||||
0, tsdata->num_y * 64 - 1, 0, 0);
|
||||
|
||||
if (!pdata)
|
||||
touchscreen_parse_of_params(input, true);
|
||||
touchscreen_parse_properties(input, true);
|
||||
|
||||
error = input_mt_init_slots(input, MAX_SUPPORT_POINTS, INPUT_MT_DIRECT);
|
||||
if (error) {
|
||||
@ -1134,7 +1134,6 @@ MODULE_DEVICE_TABLE(of, edt_ft5x06_of_match);
|
||||
|
||||
static struct i2c_driver edt_ft5x06_ts_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "edt_ft5x06",
|
||||
.of_match_table = of_match_ptr(edt_ft5x06_of_match),
|
||||
.pm = &edt_ft5x06_ts_pm_ops,
|
||||
|
@ -264,11 +264,11 @@ static const struct of_device_id egalax_ts_dt_ids[] = {
|
||||
{ .compatible = "eeti,egalax_ts" },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, egalax_ts_dt_ids);
|
||||
|
||||
static struct i2c_driver egalax_ts_driver = {
|
||||
.driver = {
|
||||
.name = "egalax_ts",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &egalax_ts_pm_ops,
|
||||
.of_match_table = egalax_ts_dt_ids,
|
||||
},
|
||||
|
@ -38,6 +38,8 @@
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
/* Device, Driver information */
|
||||
@ -102,6 +104,9 @@
|
||||
/* calibration timeout definition */
|
||||
#define ELAN_CALI_TIMEOUT_MSEC 10000
|
||||
|
||||
#define ELAN_POWERON_DELAY_USEC 500
|
||||
#define ELAN_RESET_DELAY_MSEC 20
|
||||
|
||||
enum elants_state {
|
||||
ELAN_STATE_NORMAL,
|
||||
ELAN_WAIT_QUEUE_HEADER,
|
||||
@ -118,6 +123,10 @@ struct elants_data {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input;
|
||||
|
||||
struct regulator *vcc33;
|
||||
struct regulator *vccio;
|
||||
struct gpio_desc *reset_gpio;
|
||||
|
||||
u16 fw_version;
|
||||
u8 test_version;
|
||||
u8 solution_version;
|
||||
@ -141,6 +150,7 @@ struct elants_data {
|
||||
u8 buf[MAX_PACKET_SIZE];
|
||||
|
||||
bool wake_irq_enabled;
|
||||
bool keep_power_in_suspend;
|
||||
};
|
||||
|
||||
static int elants_i2c_send(struct i2c_client *client,
|
||||
@ -605,6 +615,7 @@ static int elants_i2c_do_update_firmware(struct i2c_client *client,
|
||||
const u8 enter_iap[] = { 0x45, 0x49, 0x41, 0x50 };
|
||||
const u8 enter_iap2[] = { 0x54, 0x00, 0x12, 0x34 };
|
||||
const u8 iap_ack[] = { 0x55, 0xaa, 0x33, 0xcc };
|
||||
const u8 close_idle[] = {0x54, 0x2c, 0x01, 0x01};
|
||||
u8 buf[HEADER_SIZE];
|
||||
u16 send_id;
|
||||
int page, n_fw_pages;
|
||||
@ -617,8 +628,13 @@ static int elants_i2c_do_update_firmware(struct i2c_client *client,
|
||||
} else {
|
||||
/* Start IAP Procedure */
|
||||
dev_dbg(&client->dev, "Normal IAP procedure\n");
|
||||
/* Close idle mode */
|
||||
error = elants_i2c_send(client, close_idle, sizeof(close_idle));
|
||||
if (error)
|
||||
dev_err(&client->dev, "Failed close idle: %d\n", error);
|
||||
msleep(60);
|
||||
elants_i2c_sw_reset(client);
|
||||
|
||||
msleep(20);
|
||||
error = elants_i2c_send(client, enter_iap, sizeof(enter_iap));
|
||||
}
|
||||
|
||||
@ -1052,6 +1068,67 @@ static void elants_i2c_remove_sysfs_group(void *_data)
|
||||
sysfs_remove_group(&ts->client->dev.kobj, &elants_attribute_group);
|
||||
}
|
||||
|
||||
static int elants_i2c_power_on(struct elants_data *ts)
|
||||
{
|
||||
int error;
|
||||
|
||||
/*
|
||||
* If we do not have reset gpio assume platform firmware
|
||||
* controls regulators and does power them on for us.
|
||||
*/
|
||||
if (IS_ERR_OR_NULL(ts->reset_gpio))
|
||||
return 0;
|
||||
|
||||
gpiod_set_value_cansleep(ts->reset_gpio, 1);
|
||||
|
||||
error = regulator_enable(ts->vcc33);
|
||||
if (error) {
|
||||
dev_err(&ts->client->dev,
|
||||
"failed to enable vcc33 regulator: %d\n",
|
||||
error);
|
||||
goto release_reset_gpio;
|
||||
}
|
||||
|
||||
error = regulator_enable(ts->vccio);
|
||||
if (error) {
|
||||
dev_err(&ts->client->dev,
|
||||
"failed to enable vccio regulator: %d\n",
|
||||
error);
|
||||
regulator_disable(ts->vcc33);
|
||||
goto release_reset_gpio;
|
||||
}
|
||||
|
||||
/*
|
||||
* We need to wait a bit after powering on controller before
|
||||
* we are allowed to release reset GPIO.
|
||||
*/
|
||||
udelay(ELAN_POWERON_DELAY_USEC);
|
||||
|
||||
release_reset_gpio:
|
||||
gpiod_set_value_cansleep(ts->reset_gpio, 0);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
msleep(ELAN_RESET_DELAY_MSEC);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void elants_i2c_power_off(void *_data)
|
||||
{
|
||||
struct elants_data *ts = _data;
|
||||
|
||||
if (!IS_ERR_OR_NULL(ts->reset_gpio)) {
|
||||
/*
|
||||
* Activate reset gpio to prevent leakage through the
|
||||
* pin once we shut off power to the controller.
|
||||
*/
|
||||
gpiod_set_value_cansleep(ts->reset_gpio, 1);
|
||||
regulator_disable(ts->vccio);
|
||||
regulator_disable(ts->vcc33);
|
||||
}
|
||||
}
|
||||
|
||||
static int elants_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
@ -1066,13 +1143,6 @@ static int elants_i2c_probe(struct i2c_client *client,
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
/* Make sure there is something at this address */
|
||||
if (i2c_smbus_xfer(client->adapter, client->addr, 0,
|
||||
I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0) {
|
||||
dev_err(&client->dev, "nothing at this address\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
ts = devm_kzalloc(&client->dev, sizeof(struct elants_data), GFP_KERNEL);
|
||||
if (!ts)
|
||||
return -ENOMEM;
|
||||
@ -1083,6 +1153,62 @@ static int elants_i2c_probe(struct i2c_client *client,
|
||||
ts->client = client;
|
||||
i2c_set_clientdata(client, ts);
|
||||
|
||||
ts->vcc33 = devm_regulator_get(&client->dev, "vcc33");
|
||||
if (IS_ERR(ts->vcc33)) {
|
||||
error = PTR_ERR(ts->vcc33);
|
||||
if (error != -EPROBE_DEFER)
|
||||
dev_err(&client->dev,
|
||||
"Failed to get 'vcc33' regulator: %d\n",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
|
||||
ts->vccio = devm_regulator_get(&client->dev, "vccio");
|
||||
if (IS_ERR(ts->vccio)) {
|
||||
error = PTR_ERR(ts->vccio);
|
||||
if (error != -EPROBE_DEFER)
|
||||
dev_err(&client->dev,
|
||||
"Failed to get 'vccio' regulator: %d\n",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
|
||||
ts->reset_gpio = devm_gpiod_get(&client->dev, "reset", GPIOD_OUT_LOW);
|
||||
if (IS_ERR(ts->reset_gpio)) {
|
||||
error = PTR_ERR(ts->reset_gpio);
|
||||
|
||||
if (error == -EPROBE_DEFER)
|
||||
return error;
|
||||
|
||||
if (error != -ENOENT && error != -ENOSYS) {
|
||||
dev_err(&client->dev,
|
||||
"failed to get reset gpio: %d\n",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
|
||||
ts->keep_power_in_suspend = true;
|
||||
}
|
||||
|
||||
error = elants_i2c_power_on(ts);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = devm_add_action(&client->dev, elants_i2c_power_off, ts);
|
||||
if (error) {
|
||||
dev_err(&client->dev,
|
||||
"failed to install power off action: %d\n", error);
|
||||
elants_i2c_power_off(ts);
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Make sure there is something at this address */
|
||||
if (i2c_smbus_xfer(client->adapter, client->addr, 0,
|
||||
I2C_SMBUS_READ, 0, I2C_SMBUS_BYTE, &dummy) < 0) {
|
||||
dev_err(&client->dev, "nothing at this address\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
error = elants_i2c_initialize(ts);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "failed to initialize: %d\n", error);
|
||||
@ -1190,17 +1316,23 @@ static int __maybe_unused elants_i2c_suspend(struct device *dev)
|
||||
|
||||
disable_irq(client->irq);
|
||||
|
||||
if (device_may_wakeup(dev) || ts->keep_power_in_suspend) {
|
||||
for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
|
||||
error = elants_i2c_send(client, set_sleep_cmd,
|
||||
sizeof(set_sleep_cmd));
|
||||
if (!error)
|
||||
break;
|
||||
|
||||
dev_err(&client->dev, "suspend command failed: %d\n", error);
|
||||
dev_err(&client->dev,
|
||||
"suspend command failed: %d\n", error);
|
||||
}
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
ts->wake_irq_enabled = (enable_irq_wake(client->irq) == 0);
|
||||
ts->wake_irq_enabled =
|
||||
(enable_irq_wake(client->irq) == 0);
|
||||
} else {
|
||||
elants_i2c_power_off(ts);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1216,13 +1348,19 @@ static int __maybe_unused elants_i2c_resume(struct device *dev)
|
||||
if (device_may_wakeup(dev) && ts->wake_irq_enabled)
|
||||
disable_irq_wake(client->irq);
|
||||
|
||||
if (ts->keep_power_in_suspend) {
|
||||
for (retry_cnt = 0; retry_cnt < MAX_RETRIES; retry_cnt++) {
|
||||
error = elants_i2c_send(client, set_active_cmd,
|
||||
sizeof(set_active_cmd));
|
||||
if (!error)
|
||||
break;
|
||||
|
||||
dev_err(&client->dev, "resume command failed: %d\n", error);
|
||||
dev_err(&client->dev,
|
||||
"resume command failed: %d\n", error);
|
||||
}
|
||||
} else {
|
||||
elants_i2c_power_on(ts);
|
||||
elants_i2c_initialize(ts);
|
||||
}
|
||||
|
||||
ts->state = ELAN_STATE_NORMAL;
|
||||
@ -1261,10 +1399,10 @@ static struct i2c_driver elants_i2c_driver = {
|
||||
.id_table = elants_i2c_id,
|
||||
.driver = {
|
||||
.name = DEVICE_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &elants_i2c_pm_ops,
|
||||
.acpi_match_table = ACPI_PTR(elants_acpi_id),
|
||||
.of_match_table = of_match_ptr(elants_of_match),
|
||||
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||
},
|
||||
};
|
||||
module_i2c_driver(elants_i2c_driver);
|
||||
|
@ -420,6 +420,7 @@ static const struct i2c_device_id goodix_ts_id[] = {
|
||||
{ "GDIX1001:00", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, goodix_ts_id);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
static const struct acpi_device_id goodix_acpi_match[] = {
|
||||
@ -448,7 +449,6 @@ static struct i2c_driver goodix_ts_driver = {
|
||||
.id_table = goodix_ts_id,
|
||||
.driver = {
|
||||
.name = "Goodix-TS",
|
||||
.owner = THIS_MODULE,
|
||||
.acpi_match_table = ACPI_PTR(goodix_acpi_match),
|
||||
.of_match_table = of_match_ptr(goodix_of_match),
|
||||
},
|
||||
|
@ -343,7 +343,6 @@ MODULE_DEVICE_TABLE(i2c, ili210x_i2c_id);
|
||||
static struct i2c_driver ili210x_ts_driver = {
|
||||
.driver = {
|
||||
.name = "ili210x_i2c",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &ili210x_i2c_pm,
|
||||
},
|
||||
.id_table = ili210x_i2c_id,
|
||||
|
@ -229,7 +229,6 @@ MODULE_DEVICE_TABLE(i2c, max11801_ts_id);
|
||||
static struct i2c_driver max11801_ts_driver = {
|
||||
.driver = {
|
||||
.name = "max11801_ts",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.id_table = max11801_ts_id,
|
||||
.probe = max11801_ts_probe,
|
||||
|
@ -572,12 +572,12 @@ static const struct of_device_id mms114_dt_match[] = {
|
||||
{ .compatible = "melfas,mms114" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mms114_dt_match);
|
||||
#endif
|
||||
|
||||
static struct i2c_driver mms114_driver = {
|
||||
.driver = {
|
||||
.name = "mms114",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &mms114_pm_ops,
|
||||
.of_match_table = of_match_ptr(mms114_dt_match),
|
||||
},
|
||||
|
@ -9,12 +9,12 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/of.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/input/touchscreen.h>
|
||||
|
||||
static bool touchscreen_get_prop_u32(struct device_node *np,
|
||||
static bool touchscreen_get_prop_u32(struct device *dev,
|
||||
const char *property,
|
||||
unsigned int default_value,
|
||||
unsigned int *value)
|
||||
@ -22,7 +22,7 @@ static bool touchscreen_get_prop_u32(struct device_node *np,
|
||||
u32 val;
|
||||
int error;
|
||||
|
||||
error = of_property_read_u32(np, property, &val);
|
||||
error = device_property_read_u32(dev, property, &val);
|
||||
if (error) {
|
||||
*value = default_value;
|
||||
return false;
|
||||
@ -39,13 +39,9 @@ static void touchscreen_set_params(struct input_dev *dev,
|
||||
struct input_absinfo *absinfo;
|
||||
|
||||
if (!test_bit(axis, dev->absbit)) {
|
||||
/*
|
||||
* Emit a warning only if the axis is not a multitouch
|
||||
* axis, which might not be set by the driver.
|
||||
*/
|
||||
if (!input_is_mt_axis(axis))
|
||||
dev_warn(&dev->dev,
|
||||
"DT specifies parameters but the axis is not set up\n");
|
||||
"DT specifies parameters but the axis %lu is not set up\n",
|
||||
axis);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -55,52 +51,58 @@ static void touchscreen_set_params(struct input_dev *dev,
|
||||
}
|
||||
|
||||
/**
|
||||
* touchscreen_parse_of_params - parse common touchscreen DT properties
|
||||
* @dev: device that should be parsed
|
||||
* touchscreen_parse_properties - parse common touchscreen DT properties
|
||||
* @input: input device that should be parsed
|
||||
* @multitouch: specifies whether parsed properties should be applied to
|
||||
* single-touch or multi-touch axes
|
||||
*
|
||||
* This function parses common DT properties for touchscreens and setups the
|
||||
* input device accordingly. The function keeps previously setuped default
|
||||
* input device accordingly. The function keeps previously set up default
|
||||
* values if no value is specified via DT.
|
||||
*/
|
||||
void touchscreen_parse_of_params(struct input_dev *dev, bool multitouch)
|
||||
void touchscreen_parse_properties(struct input_dev *input, bool multitouch)
|
||||
{
|
||||
struct device_node *np = dev->dev.parent->of_node;
|
||||
struct device *dev = input->dev.parent;
|
||||
unsigned int axis;
|
||||
unsigned int maximum, fuzz;
|
||||
bool data_present;
|
||||
|
||||
input_alloc_absinfo(dev);
|
||||
if (!dev->absinfo)
|
||||
input_alloc_absinfo(input);
|
||||
if (!input->absinfo)
|
||||
return;
|
||||
|
||||
axis = multitouch ? ABS_MT_POSITION_X : ABS_X;
|
||||
data_present = touchscreen_get_prop_u32(np, "touchscreen-size-x",
|
||||
input_abs_get_max(dev, axis),
|
||||
data_present = touchscreen_get_prop_u32(dev, "touchscreen-size-x",
|
||||
input_abs_get_max(input,
|
||||
axis) + 1,
|
||||
&maximum) |
|
||||
touchscreen_get_prop_u32(np, "touchscreen-fuzz-x",
|
||||
input_abs_get_fuzz(dev, axis),
|
||||
touchscreen_get_prop_u32(dev, "touchscreen-fuzz-x",
|
||||
input_abs_get_fuzz(input, axis),
|
||||
&fuzz);
|
||||
if (data_present)
|
||||
touchscreen_set_params(dev, axis, maximum, fuzz);
|
||||
touchscreen_set_params(input, axis, maximum - 1, fuzz);
|
||||
|
||||
axis = multitouch ? ABS_MT_POSITION_Y : ABS_Y;
|
||||
data_present = touchscreen_get_prop_u32(np, "touchscreen-size-y",
|
||||
input_abs_get_max(dev, axis),
|
||||
data_present = touchscreen_get_prop_u32(dev, "touchscreen-size-y",
|
||||
input_abs_get_max(input,
|
||||
axis) + 1,
|
||||
&maximum) |
|
||||
touchscreen_get_prop_u32(np, "touchscreen-fuzz-y",
|
||||
input_abs_get_fuzz(dev, axis),
|
||||
touchscreen_get_prop_u32(dev, "touchscreen-fuzz-y",
|
||||
input_abs_get_fuzz(input, axis),
|
||||
&fuzz);
|
||||
if (data_present)
|
||||
touchscreen_set_params(dev, axis, maximum, fuzz);
|
||||
touchscreen_set_params(input, axis, maximum - 1, fuzz);
|
||||
|
||||
axis = multitouch ? ABS_MT_PRESSURE : ABS_PRESSURE;
|
||||
data_present = touchscreen_get_prop_u32(np, "touchscreen-max-pressure",
|
||||
input_abs_get_max(dev, axis),
|
||||
data_present = touchscreen_get_prop_u32(dev,
|
||||
"touchscreen-max-pressure",
|
||||
input_abs_get_max(input, axis),
|
||||
&maximum) |
|
||||
touchscreen_get_prop_u32(np, "touchscreen-fuzz-pressure",
|
||||
input_abs_get_fuzz(dev, axis),
|
||||
touchscreen_get_prop_u32(dev,
|
||||
"touchscreen-fuzz-pressure",
|
||||
input_abs_get_fuzz(input, axis),
|
||||
&fuzz);
|
||||
if (data_present)
|
||||
touchscreen_set_params(dev, axis, maximum, fuzz);
|
||||
touchscreen_set_params(input, axis, maximum, fuzz);
|
||||
}
|
||||
EXPORT_SYMBOL(touchscreen_parse_of_params);
|
||||
EXPORT_SYMBOL(touchscreen_parse_properties);
|
||||
|
@ -24,20 +24,23 @@
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/input/pixcir_ts.h>
|
||||
#include <linux/input/touchscreen.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
/*#include <linux/of.h>*/
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_data/pixcir_i2c_ts.h>
|
||||
|
||||
#define PIXCIR_MAX_SLOTS 5 /* Max fingers supported by driver */
|
||||
|
||||
struct pixcir_i2c_ts_data {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input;
|
||||
const struct pixcir_ts_platform_data *pdata;
|
||||
bool running;
|
||||
struct gpio_desc *gpio_attb;
|
||||
struct gpio_desc *gpio_reset;
|
||||
const struct pixcir_i2c_chip_data *chip;
|
||||
int max_fingers; /* Max fingers supported in this instance */
|
||||
bool running;
|
||||
};
|
||||
|
||||
struct pixcir_touch {
|
||||
@ -60,7 +63,7 @@ static void pixcir_ts_parse(struct pixcir_i2c_ts_data *tsdata,
|
||||
u8 touch;
|
||||
int ret, i;
|
||||
int readsize;
|
||||
const struct pixcir_i2c_chip_data *chip = &tsdata->pdata->chip;
|
||||
const struct pixcir_i2c_chip_data *chip = tsdata->chip;
|
||||
|
||||
memset(report, 0, sizeof(struct pixcir_report_data));
|
||||
|
||||
@ -113,13 +116,13 @@ static void pixcir_ts_report(struct pixcir_i2c_ts_data *ts,
|
||||
struct pixcir_touch *touch;
|
||||
int n, i, slot;
|
||||
struct device *dev = &ts->client->dev;
|
||||
const struct pixcir_i2c_chip_data *chip = &ts->pdata->chip;
|
||||
const struct pixcir_i2c_chip_data *chip = ts->chip;
|
||||
|
||||
n = report->num_touches;
|
||||
if (n > PIXCIR_MAX_SLOTS)
|
||||
n = PIXCIR_MAX_SLOTS;
|
||||
|
||||
if (!chip->has_hw_ids) {
|
||||
if (!ts->chip->has_hw_ids) {
|
||||
for (i = 0; i < n; i++) {
|
||||
touch = &report->touches[i];
|
||||
pos[i].x = touch->x;
|
||||
@ -161,7 +164,6 @@ static void pixcir_ts_report(struct pixcir_i2c_ts_data *ts,
|
||||
static irqreturn_t pixcir_ts_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct pixcir_i2c_ts_data *tsdata = dev_id;
|
||||
const struct pixcir_ts_platform_data *pdata = tsdata->pdata;
|
||||
struct pixcir_report_data report;
|
||||
|
||||
while (tsdata->running) {
|
||||
@ -171,7 +173,7 @@ static irqreturn_t pixcir_ts_isr(int irq, void *dev_id)
|
||||
/* report it */
|
||||
pixcir_ts_report(tsdata, &report);
|
||||
|
||||
if (gpio_get_value(pdata->gpio_attb)) {
|
||||
if (gpiod_get_value_cansleep(tsdata->gpio_attb)) {
|
||||
if (report.num_touches) {
|
||||
/*
|
||||
* Last report with no finger up?
|
||||
@ -189,6 +191,17 @@ static irqreturn_t pixcir_ts_isr(int irq, void *dev_id)
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void pixcir_reset(struct pixcir_i2c_ts_data *tsdata)
|
||||
{
|
||||
if (!IS_ERR_OR_NULL(tsdata->gpio_reset)) {
|
||||
gpiod_set_value_cansleep(tsdata->gpio_reset, 1);
|
||||
ndelay(100); /* datasheet section 1.2.3 says 80ns min. */
|
||||
gpiod_set_value_cansleep(tsdata->gpio_reset, 0);
|
||||
/* wait for controller ready. 100ms guess. */
|
||||
msleep(100);
|
||||
}
|
||||
}
|
||||
|
||||
static int pixcir_set_power_mode(struct pixcir_i2c_ts_data *ts,
|
||||
enum pixcir_power_mode mode)
|
||||
{
|
||||
@ -411,46 +424,26 @@ static SIMPLE_DEV_PM_OPS(pixcir_dev_pm_ops,
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id pixcir_of_match[];
|
||||
|
||||
static struct pixcir_ts_platform_data *pixcir_parse_dt(struct device *dev)
|
||||
static int pixcir_parse_dt(struct device *dev,
|
||||
struct pixcir_i2c_ts_data *tsdata)
|
||||
{
|
||||
struct pixcir_ts_platform_data *pdata;
|
||||
struct device_node *np = dev->of_node;
|
||||
const struct of_device_id *match;
|
||||
|
||||
match = of_match_device(of_match_ptr(pixcir_of_match), dev);
|
||||
if (!match)
|
||||
return ERR_PTR(-EINVAL);
|
||||
return -EINVAL;
|
||||
|
||||
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
tsdata->chip = (const struct pixcir_i2c_chip_data *)match->data;
|
||||
if (!tsdata->chip)
|
||||
return -EINVAL;
|
||||
|
||||
pdata->chip = *(const struct pixcir_i2c_chip_data *)match->data;
|
||||
|
||||
pdata->gpio_attb = of_get_named_gpio(np, "attb-gpio", 0);
|
||||
/* gpio_attb validity is checked in probe */
|
||||
|
||||
if (of_property_read_u32(np, "touchscreen-size-x", &pdata->x_max)) {
|
||||
dev_err(dev, "Failed to get touchscreen-size-x property\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
pdata->x_max -= 1;
|
||||
|
||||
if (of_property_read_u32(np, "touchscreen-size-y", &pdata->y_max)) {
|
||||
dev_err(dev, "Failed to get touchscreen-size-y property\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
pdata->y_max -= 1;
|
||||
|
||||
dev_dbg(dev, "%s: x %d, y %d, gpio %d\n", __func__,
|
||||
pdata->x_max + 1, pdata->y_max + 1, pdata->gpio_attb);
|
||||
|
||||
return pdata;
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static struct pixcir_ts_platform_data *pixcir_parse_dt(struct device *dev)
|
||||
static int pixcir_parse_dt(struct device *dev,
|
||||
struct pixcir_i2c_ts_data *tsdata)
|
||||
{
|
||||
return ERR_PTR(-EINVAL);
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -460,36 +453,30 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client,
|
||||
const struct pixcir_ts_platform_data *pdata =
|
||||
dev_get_platdata(&client->dev);
|
||||
struct device *dev = &client->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct pixcir_i2c_ts_data *tsdata;
|
||||
struct input_dev *input;
|
||||
int error;
|
||||
|
||||
if (np && !pdata) {
|
||||
pdata = pixcir_parse_dt(dev);
|
||||
if (IS_ERR(pdata))
|
||||
return PTR_ERR(pdata);
|
||||
}
|
||||
tsdata = devm_kzalloc(dev, sizeof(*tsdata), GFP_KERNEL);
|
||||
if (!tsdata)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!pdata) {
|
||||
if (pdata) {
|
||||
tsdata->chip = &pdata->chip;
|
||||
} else if (dev->of_node) {
|
||||
error = pixcir_parse_dt(dev, tsdata);
|
||||
if (error)
|
||||
return error;
|
||||
} else {
|
||||
dev_err(&client->dev, "platform data not defined\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!gpio_is_valid(pdata->gpio_attb)) {
|
||||
dev_err(dev, "Invalid gpio_attb in pdata\n");
|
||||
if (!tsdata->chip->max_fingers) {
|
||||
dev_err(dev, "Invalid max_fingers in chip data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!pdata->chip.max_fingers) {
|
||||
dev_err(dev, "Invalid max_fingers in pdata\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
tsdata = devm_kzalloc(dev, sizeof(*tsdata), GFP_KERNEL);
|
||||
if (!tsdata)
|
||||
return -ENOMEM;
|
||||
|
||||
input = devm_input_allocate_device(dev);
|
||||
if (!input) {
|
||||
dev_err(dev, "Failed to allocate input device\n");
|
||||
@ -498,7 +485,6 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client,
|
||||
|
||||
tsdata->client = client;
|
||||
tsdata->input = input;
|
||||
tsdata->pdata = pdata;
|
||||
|
||||
input->name = client->name;
|
||||
input->id.bustype = BUS_I2C;
|
||||
@ -506,15 +492,21 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client,
|
||||
input->close = pixcir_input_close;
|
||||
input->dev.parent = &client->dev;
|
||||
|
||||
__set_bit(EV_KEY, input->evbit);
|
||||
__set_bit(EV_ABS, input->evbit);
|
||||
__set_bit(BTN_TOUCH, input->keybit);
|
||||
input_set_abs_params(input, ABS_X, 0, pdata->x_max, 0, 0);
|
||||
input_set_abs_params(input, ABS_Y, 0, pdata->y_max, 0, 0);
|
||||
if (pdata) {
|
||||
input_set_abs_params(input, ABS_MT_POSITION_X, 0, pdata->x_max, 0, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_Y, 0, pdata->y_max, 0, 0);
|
||||
} else {
|
||||
input_set_capability(input, EV_ABS, ABS_MT_POSITION_X);
|
||||
input_set_capability(input, EV_ABS, ABS_MT_POSITION_Y);
|
||||
touchscreen_parse_properties(input, true);
|
||||
if (!input_abs_get_max(input, ABS_MT_POSITION_X) ||
|
||||
!input_abs_get_max(input, ABS_MT_POSITION_Y)) {
|
||||
dev_err(dev, "Touchscreen size is not specified\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
tsdata->max_fingers = tsdata->pdata->chip.max_fingers;
|
||||
tsdata->max_fingers = tsdata->chip->max_fingers;
|
||||
if (tsdata->max_fingers > PIXCIR_MAX_SLOTS) {
|
||||
tsdata->max_fingers = PIXCIR_MAX_SLOTS;
|
||||
dev_info(dev, "Limiting maximum fingers to %d\n",
|
||||
@ -530,10 +522,18 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client,
|
||||
|
||||
input_set_drvdata(input, tsdata);
|
||||
|
||||
error = devm_gpio_request_one(dev, pdata->gpio_attb,
|
||||
GPIOF_DIR_IN, "pixcir_i2c_attb");
|
||||
if (error) {
|
||||
dev_err(dev, "Failed to request ATTB gpio\n");
|
||||
tsdata->gpio_attb = devm_gpiod_get(dev, "attb", GPIOD_IN);
|
||||
if (IS_ERR(tsdata->gpio_attb)) {
|
||||
error = PTR_ERR(tsdata->gpio_attb);
|
||||
dev_err(dev, "Failed to request ATTB gpio: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
tsdata->gpio_reset = devm_gpiod_get_optional(dev, "reset",
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(tsdata->gpio_reset)) {
|
||||
error = PTR_ERR(tsdata->gpio_reset);
|
||||
dev_err(dev, "Failed to request RESET gpio: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -545,6 +545,8 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client,
|
||||
return error;
|
||||
}
|
||||
|
||||
pixcir_reset(tsdata);
|
||||
|
||||
/* Always be in IDLE mode to save power, device supports auto wake */
|
||||
error = pixcir_set_power_mode(tsdata, PIXCIR_POWER_IDLE);
|
||||
if (error) {
|
||||
@ -602,7 +604,6 @@ MODULE_DEVICE_TABLE(of, pixcir_of_match);
|
||||
|
||||
static struct i2c_driver pixcir_i2c_ts_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "pixcir_ts",
|
||||
.pm = &pixcir_dev_pm_ops,
|
||||
.of_match_table = of_match_ptr(pixcir_of_match),
|
||||
|
@ -296,7 +296,6 @@ static struct i2c_driver st1232_ts_driver = {
|
||||
.id_table = st1232_ts_id,
|
||||
.driver = {
|
||||
.name = ST1232_TS_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(st1232_ts_dt_ids),
|
||||
.pm = &st1232_ts_pm_ops,
|
||||
},
|
||||
|
@ -581,6 +581,7 @@ static int sur40_probe(struct usb_interface *interface,
|
||||
sur40->alloc_ctx = vb2_dma_sg_init_ctx(sur40->dev);
|
||||
if (IS_ERR(sur40->alloc_ctx)) {
|
||||
dev_err(sur40->dev, "Can't allocate buffer context");
|
||||
error = PTR_ERR(sur40->alloc_ctx);
|
||||
goto err_unreg_v4l2;
|
||||
}
|
||||
|
||||
|
@ -30,10 +30,11 @@
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/spi/tsc2005.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
|
||||
/*
|
||||
* The touchscreen interface operates as follows:
|
||||
@ -61,16 +62,24 @@
|
||||
#define TSC2005_CMD_12BIT 0x04
|
||||
|
||||
/* control byte 0 */
|
||||
#define TSC2005_REG_READ 0x0001
|
||||
#define TSC2005_REG_PND0 0x0002
|
||||
#define TSC2005_REG_X 0x0000
|
||||
#define TSC2005_REG_Y 0x0008
|
||||
#define TSC2005_REG_Z1 0x0010
|
||||
#define TSC2005_REG_Z2 0x0018
|
||||
#define TSC2005_REG_TEMP_HIGH 0x0050
|
||||
#define TSC2005_REG_CFR0 0x0060
|
||||
#define TSC2005_REG_CFR1 0x0068
|
||||
#define TSC2005_REG_CFR2 0x0070
|
||||
#define TSC2005_REG_READ 0x01 /* R/W access */
|
||||
#define TSC2005_REG_PND0 0x02 /* Power Not Down Control */
|
||||
#define TSC2005_REG_X (0x0 << 3)
|
||||
#define TSC2005_REG_Y (0x1 << 3)
|
||||
#define TSC2005_REG_Z1 (0x2 << 3)
|
||||
#define TSC2005_REG_Z2 (0x3 << 3)
|
||||
#define TSC2005_REG_AUX (0x4 << 3)
|
||||
#define TSC2005_REG_TEMP1 (0x5 << 3)
|
||||
#define TSC2005_REG_TEMP2 (0x6 << 3)
|
||||
#define TSC2005_REG_STATUS (0x7 << 3)
|
||||
#define TSC2005_REG_AUX_HIGH (0x8 << 3)
|
||||
#define TSC2005_REG_AUX_LOW (0x9 << 3)
|
||||
#define TSC2005_REG_TEMP_HIGH (0xA << 3)
|
||||
#define TSC2005_REG_TEMP_LOW (0xB << 3)
|
||||
#define TSC2005_REG_CFR0 (0xC << 3)
|
||||
#define TSC2005_REG_CFR1 (0xD << 3)
|
||||
#define TSC2005_REG_CFR2 (0xE << 3)
|
||||
#define TSC2005_REG_CONV_FUNC (0xF << 3)
|
||||
|
||||
/* configuration register 0 */
|
||||
#define TSC2005_CFR0_PRECHARGE_276US 0x0040
|
||||
@ -112,20 +121,37 @@
|
||||
#define TSC2005_SPI_MAX_SPEED_HZ 10000000
|
||||
#define TSC2005_PENUP_TIME_MS 40
|
||||
|
||||
struct tsc2005_spi_rd {
|
||||
struct spi_transfer spi_xfer;
|
||||
u32 spi_tx;
|
||||
u32 spi_rx;
|
||||
static const struct regmap_range tsc2005_writable_ranges[] = {
|
||||
regmap_reg_range(TSC2005_REG_AUX_HIGH, TSC2005_REG_CFR2),
|
||||
};
|
||||
|
||||
static const struct regmap_access_table tsc2005_writable_table = {
|
||||
.yes_ranges = tsc2005_writable_ranges,
|
||||
.n_yes_ranges = ARRAY_SIZE(tsc2005_writable_ranges),
|
||||
};
|
||||
|
||||
static struct regmap_config tsc2005_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 16,
|
||||
.reg_stride = 0x08,
|
||||
.max_register = 0x78,
|
||||
.read_flag_mask = TSC2005_REG_READ,
|
||||
.write_flag_mask = TSC2005_REG_PND0,
|
||||
.wr_table = &tsc2005_writable_table,
|
||||
.use_single_rw = true,
|
||||
};
|
||||
|
||||
struct tsc2005_data {
|
||||
u16 x;
|
||||
u16 y;
|
||||
u16 z1;
|
||||
u16 z2;
|
||||
} __packed;
|
||||
#define TSC2005_DATA_REGS 4
|
||||
|
||||
struct tsc2005 {
|
||||
struct spi_device *spi;
|
||||
|
||||
struct spi_message spi_read_msg;
|
||||
struct tsc2005_spi_rd spi_x;
|
||||
struct tsc2005_spi_rd spi_y;
|
||||
struct tsc2005_spi_rd spi_z1;
|
||||
struct tsc2005_spi_rd spi_z2;
|
||||
struct regmap *regmap;
|
||||
|
||||
struct input_dev *idev;
|
||||
char phys[32];
|
||||
@ -154,7 +180,7 @@ struct tsc2005 {
|
||||
|
||||
struct regulator *vio;
|
||||
|
||||
int reset_gpio;
|
||||
struct gpio_desc *reset_gpio;
|
||||
void (*set_reset)(bool enable);
|
||||
};
|
||||
|
||||
@ -182,62 +208,6 @@ static int tsc2005_cmd(struct tsc2005 *ts, u8 cmd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tsc2005_write(struct tsc2005 *ts, u8 reg, u16 value)
|
||||
{
|
||||
u32 tx = ((reg | TSC2005_REG_PND0) << 16) | value;
|
||||
struct spi_transfer xfer = {
|
||||
.tx_buf = &tx,
|
||||
.len = 4,
|
||||
.bits_per_word = 24,
|
||||
};
|
||||
struct spi_message msg;
|
||||
int error;
|
||||
|
||||
spi_message_init(&msg);
|
||||
spi_message_add_tail(&xfer, &msg);
|
||||
|
||||
error = spi_sync(ts->spi, &msg);
|
||||
if (error) {
|
||||
dev_err(&ts->spi->dev,
|
||||
"%s: failed, register: %x, value: %x, error: %d\n",
|
||||
__func__, reg, value, error);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tsc2005_setup_read(struct tsc2005_spi_rd *rd, u8 reg, bool last)
|
||||
{
|
||||
memset(rd, 0, sizeof(*rd));
|
||||
|
||||
rd->spi_tx = (reg | TSC2005_REG_READ) << 16;
|
||||
rd->spi_xfer.tx_buf = &rd->spi_tx;
|
||||
rd->spi_xfer.rx_buf = &rd->spi_rx;
|
||||
rd->spi_xfer.len = 4;
|
||||
rd->spi_xfer.bits_per_word = 24;
|
||||
rd->spi_xfer.cs_change = !last;
|
||||
}
|
||||
|
||||
static int tsc2005_read(struct tsc2005 *ts, u8 reg, u16 *value)
|
||||
{
|
||||
struct tsc2005_spi_rd spi_rd;
|
||||
struct spi_message msg;
|
||||
int error;
|
||||
|
||||
tsc2005_setup_read(&spi_rd, reg, true);
|
||||
|
||||
spi_message_init(&msg);
|
||||
spi_message_add_tail(&spi_rd.spi_xfer, &msg);
|
||||
|
||||
error = spi_sync(ts->spi, &msg);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
*value = spi_rd.spi_rx;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tsc2005_update_pen_state(struct tsc2005 *ts,
|
||||
int x, int y, int pressure)
|
||||
{
|
||||
@ -266,26 +236,23 @@ static irqreturn_t tsc2005_irq_thread(int irq, void *_ts)
|
||||
struct tsc2005 *ts = _ts;
|
||||
unsigned long flags;
|
||||
unsigned int pressure;
|
||||
u32 x, y;
|
||||
u32 z1, z2;
|
||||
struct tsc2005_data tsdata;
|
||||
int error;
|
||||
|
||||
/* read the coordinates */
|
||||
error = spi_sync(ts->spi, &ts->spi_read_msg);
|
||||
error = regmap_bulk_read(ts->regmap, TSC2005_REG_X, &tsdata,
|
||||
TSC2005_DATA_REGS);
|
||||
if (unlikely(error))
|
||||
goto out;
|
||||
|
||||
x = ts->spi_x.spi_rx;
|
||||
y = ts->spi_y.spi_rx;
|
||||
z1 = ts->spi_z1.spi_rx;
|
||||
z2 = ts->spi_z2.spi_rx;
|
||||
|
||||
/* validate position */
|
||||
if (unlikely(x > MAX_12BIT || y > MAX_12BIT))
|
||||
if (unlikely(tsdata.x > MAX_12BIT || tsdata.y > MAX_12BIT))
|
||||
goto out;
|
||||
|
||||
/* Skip reading if the pressure components are out of range */
|
||||
if (unlikely(z1 == 0 || z2 > MAX_12BIT || z1 >= z2))
|
||||
if (unlikely(tsdata.z1 == 0 || tsdata.z2 > MAX_12BIT))
|
||||
goto out;
|
||||
if (unlikely(tsdata.z1 >= tsdata.z2))
|
||||
goto out;
|
||||
|
||||
/*
|
||||
@ -293,8 +260,8 @@ static irqreturn_t tsc2005_irq_thread(int irq, void *_ts)
|
||||
* the value before pen-up - that implies SPI fed us stale data
|
||||
*/
|
||||
if (!ts->pen_down &&
|
||||
ts->in_x == x && ts->in_y == y &&
|
||||
ts->in_z1 == z1 && ts->in_z2 == z2) {
|
||||
ts->in_x == tsdata.x && ts->in_y == tsdata.y &&
|
||||
ts->in_z1 == tsdata.z1 && ts->in_z2 == tsdata.z2) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
@ -302,20 +269,20 @@ static irqreturn_t tsc2005_irq_thread(int irq, void *_ts)
|
||||
* At this point we are happy we have a valid and useful reading.
|
||||
* Remember it for later comparisons. We may now begin downsampling.
|
||||
*/
|
||||
ts->in_x = x;
|
||||
ts->in_y = y;
|
||||
ts->in_z1 = z1;
|
||||
ts->in_z2 = z2;
|
||||
ts->in_x = tsdata.x;
|
||||
ts->in_y = tsdata.y;
|
||||
ts->in_z1 = tsdata.z1;
|
||||
ts->in_z2 = tsdata.z2;
|
||||
|
||||
/* Compute touch pressure resistance using equation #1 */
|
||||
pressure = x * (z2 - z1) / z1;
|
||||
pressure = tsdata.x * (tsdata.z2 - tsdata.z1) / tsdata.z1;
|
||||
pressure = pressure * ts->x_plate_ohm / 4096;
|
||||
if (unlikely(pressure > MAX_12BIT))
|
||||
goto out;
|
||||
|
||||
spin_lock_irqsave(&ts->lock, flags);
|
||||
|
||||
tsc2005_update_pen_state(ts, x, y, pressure);
|
||||
tsc2005_update_pen_state(ts, tsdata.x, tsdata.y, pressure);
|
||||
mod_timer(&ts->penup_timer,
|
||||
jiffies + msecs_to_jiffies(TSC2005_PENUP_TIME_MS));
|
||||
|
||||
@ -338,9 +305,9 @@ static void tsc2005_penup_timer(unsigned long data)
|
||||
|
||||
static void tsc2005_start_scan(struct tsc2005 *ts)
|
||||
{
|
||||
tsc2005_write(ts, TSC2005_REG_CFR0, TSC2005_CFR0_INITVALUE);
|
||||
tsc2005_write(ts, TSC2005_REG_CFR1, TSC2005_CFR1_INITVALUE);
|
||||
tsc2005_write(ts, TSC2005_REG_CFR2, TSC2005_CFR2_INITVALUE);
|
||||
regmap_write(ts->regmap, TSC2005_REG_CFR0, TSC2005_CFR0_INITVALUE);
|
||||
regmap_write(ts->regmap, TSC2005_REG_CFR1, TSC2005_CFR1_INITVALUE);
|
||||
regmap_write(ts->regmap, TSC2005_REG_CFR2, TSC2005_CFR2_INITVALUE);
|
||||
tsc2005_cmd(ts, TSC2005_CMD_NORMAL);
|
||||
}
|
||||
|
||||
@ -351,8 +318,8 @@ static void tsc2005_stop_scan(struct tsc2005 *ts)
|
||||
|
||||
static void tsc2005_set_reset(struct tsc2005 *ts, bool enable)
|
||||
{
|
||||
if (ts->reset_gpio >= 0)
|
||||
gpio_set_value(ts->reset_gpio, enable);
|
||||
if (ts->reset_gpio)
|
||||
gpiod_set_value_cansleep(ts->reset_gpio, enable);
|
||||
else if (ts->set_reset)
|
||||
ts->set_reset(enable);
|
||||
}
|
||||
@ -388,11 +355,10 @@ static ssize_t tsc2005_selftest_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
struct tsc2005 *ts = spi_get_drvdata(spi);
|
||||
u16 temp_high;
|
||||
u16 temp_high_orig;
|
||||
u16 temp_high_test;
|
||||
struct tsc2005 *ts = dev_get_drvdata(dev);
|
||||
unsigned int temp_high;
|
||||
unsigned int temp_high_orig;
|
||||
unsigned int temp_high_test;
|
||||
bool success = true;
|
||||
int error;
|
||||
|
||||
@ -403,7 +369,7 @@ static ssize_t tsc2005_selftest_show(struct device *dev,
|
||||
*/
|
||||
__tsc2005_disable(ts);
|
||||
|
||||
error = tsc2005_read(ts, TSC2005_REG_TEMP_HIGH, &temp_high_orig);
|
||||
error = regmap_read(ts->regmap, TSC2005_REG_TEMP_HIGH, &temp_high_orig);
|
||||
if (error) {
|
||||
dev_warn(dev, "selftest failed: read error %d\n", error);
|
||||
success = false;
|
||||
@ -412,14 +378,14 @@ static ssize_t tsc2005_selftest_show(struct device *dev,
|
||||
|
||||
temp_high_test = (temp_high_orig - 1) & MAX_12BIT;
|
||||
|
||||
error = tsc2005_write(ts, TSC2005_REG_TEMP_HIGH, temp_high_test);
|
||||
error = regmap_write(ts->regmap, TSC2005_REG_TEMP_HIGH, temp_high_test);
|
||||
if (error) {
|
||||
dev_warn(dev, "selftest failed: write error %d\n", error);
|
||||
success = false;
|
||||
goto out;
|
||||
}
|
||||
|
||||
error = tsc2005_read(ts, TSC2005_REG_TEMP_HIGH, &temp_high);
|
||||
error = regmap_read(ts->regmap, TSC2005_REG_TEMP_HIGH, &temp_high);
|
||||
if (error) {
|
||||
dev_warn(dev, "selftest failed: read error %d after write\n",
|
||||
error);
|
||||
@ -442,7 +408,7 @@ static ssize_t tsc2005_selftest_show(struct device *dev,
|
||||
goto out;
|
||||
|
||||
/* test that the reset really happened */
|
||||
error = tsc2005_read(ts, TSC2005_REG_TEMP_HIGH, &temp_high);
|
||||
error = regmap_read(ts->regmap, TSC2005_REG_TEMP_HIGH, &temp_high);
|
||||
if (error) {
|
||||
dev_warn(dev, "selftest failed: read error %d after reset\n",
|
||||
error);
|
||||
@ -474,8 +440,7 @@ static umode_t tsc2005_attr_is_visible(struct kobject *kobj,
|
||||
struct attribute *attr, int n)
|
||||
{
|
||||
struct device *dev = container_of(kobj, struct device, kobj);
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
struct tsc2005 *ts = spi_get_drvdata(spi);
|
||||
struct tsc2005 *ts = dev_get_drvdata(dev);
|
||||
umode_t mode = attr->mode;
|
||||
|
||||
if (attr == &dev_attr_selftest.attr) {
|
||||
@ -495,7 +460,7 @@ static void tsc2005_esd_work(struct work_struct *work)
|
||||
{
|
||||
struct tsc2005 *ts = container_of(work, struct tsc2005, esd_work.work);
|
||||
int error;
|
||||
u16 r;
|
||||
unsigned int r;
|
||||
|
||||
if (!mutex_trylock(&ts->mutex)) {
|
||||
/*
|
||||
@ -511,7 +476,7 @@ static void tsc2005_esd_work(struct work_struct *work)
|
||||
goto out;
|
||||
|
||||
/* We should be able to read register without disabling interrupts. */
|
||||
error = tsc2005_read(ts, TSC2005_REG_CFR0, &r);
|
||||
error = regmap_read(ts->regmap, TSC2005_REG_CFR0, &r);
|
||||
if (!error &&
|
||||
!((r ^ TSC2005_CFR0_INITVALUE) & TSC2005_CFR0_RW_MASK)) {
|
||||
goto out;
|
||||
@ -575,20 +540,6 @@ static void tsc2005_close(struct input_dev *input)
|
||||
mutex_unlock(&ts->mutex);
|
||||
}
|
||||
|
||||
static void tsc2005_setup_spi_xfer(struct tsc2005 *ts)
|
||||
{
|
||||
tsc2005_setup_read(&ts->spi_x, TSC2005_REG_X, false);
|
||||
tsc2005_setup_read(&ts->spi_y, TSC2005_REG_Y, false);
|
||||
tsc2005_setup_read(&ts->spi_z1, TSC2005_REG_Z1, false);
|
||||
tsc2005_setup_read(&ts->spi_z2, TSC2005_REG_Z2, true);
|
||||
|
||||
spi_message_init(&ts->spi_read_msg);
|
||||
spi_message_add_tail(&ts->spi_x.spi_xfer, &ts->spi_read_msg);
|
||||
spi_message_add_tail(&ts->spi_y.spi_xfer, &ts->spi_read_msg);
|
||||
spi_message_add_tail(&ts->spi_z1.spi_xfer, &ts->spi_read_msg);
|
||||
spi_message_add_tail(&ts->spi_z2.spi_xfer, &ts->spi_read_msg);
|
||||
}
|
||||
|
||||
static int tsc2005_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct tsc2005_platform_data *pdata = dev_get_platdata(&spi->dev);
|
||||
@ -653,37 +604,30 @@ static int tsc2005_probe(struct spi_device *spi)
|
||||
ts->spi = spi;
|
||||
ts->idev = input_dev;
|
||||
|
||||
ts->regmap = devm_regmap_init_spi(spi, &tsc2005_regmap_config);
|
||||
if (IS_ERR(ts->regmap))
|
||||
return PTR_ERR(ts->regmap);
|
||||
|
||||
ts->x_plate_ohm = x_plate_ohm;
|
||||
ts->esd_timeout = esd_timeout;
|
||||
|
||||
if (np) {
|
||||
ts->reset_gpio = of_get_named_gpio(np, "reset-gpios", 0);
|
||||
if (ts->reset_gpio == -EPROBE_DEFER)
|
||||
return ts->reset_gpio;
|
||||
if (ts->reset_gpio < 0) {
|
||||
dev_err(&spi->dev, "error acquiring reset gpio: %d\n",
|
||||
ts->reset_gpio);
|
||||
return ts->reset_gpio;
|
||||
}
|
||||
|
||||
error = devm_gpio_request_one(&spi->dev, ts->reset_gpio, 0,
|
||||
"reset-gpios");
|
||||
if (error) {
|
||||
dev_err(&spi->dev, "error requesting reset gpio: %d\n",
|
||||
error);
|
||||
ts->reset_gpio = devm_gpiod_get_optional(&spi->dev, "reset",
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(ts->reset_gpio)) {
|
||||
error = PTR_ERR(ts->reset_gpio);
|
||||
dev_err(&spi->dev, "error acquiring reset gpio: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
ts->vio = devm_regulator_get(&spi->dev, "vio");
|
||||
ts->vio = devm_regulator_get_optional(&spi->dev, "vio");
|
||||
if (IS_ERR(ts->vio)) {
|
||||
error = PTR_ERR(ts->vio);
|
||||
dev_err(&spi->dev, "vio regulator missing (%d)", error);
|
||||
return error;
|
||||
}
|
||||
} else {
|
||||
ts->reset_gpio = -1;
|
||||
|
||||
if (!ts->reset_gpio && pdata)
|
||||
ts->set_reset = pdata->set_reset;
|
||||
}
|
||||
|
||||
mutex_init(&ts->mutex);
|
||||
|
||||
@ -692,8 +636,6 @@ static int tsc2005_probe(struct spi_device *spi)
|
||||
|
||||
INIT_DELAYED_WORK(&ts->esd_work, tsc2005_esd_work);
|
||||
|
||||
tsc2005_setup_spi_xfer(ts);
|
||||
|
||||
snprintf(ts->phys, sizeof(ts->phys),
|
||||
"%s/input-ts", dev_name(&spi->dev));
|
||||
|
||||
@ -709,7 +651,7 @@ static int tsc2005_probe(struct spi_device *spi)
|
||||
input_set_abs_params(input_dev, ABS_PRESSURE, 0, max_p, fudge_p, 0);
|
||||
|
||||
if (np)
|
||||
touchscreen_parse_of_params(input_dev, false);
|
||||
touchscreen_parse_properties(input_dev, false);
|
||||
|
||||
input_dev->open = tsc2005_open;
|
||||
input_dev->close = tsc2005_close;
|
||||
@ -735,7 +677,7 @@ static int tsc2005_probe(struct spi_device *spi)
|
||||
return error;
|
||||
}
|
||||
|
||||
spi_set_drvdata(spi, ts);
|
||||
dev_set_drvdata(&spi->dev, ts);
|
||||
error = sysfs_create_group(&spi->dev.kobj, &tsc2005_attr_group);
|
||||
if (error) {
|
||||
dev_err(&spi->dev,
|
||||
@ -763,7 +705,7 @@ disable_regulator:
|
||||
|
||||
static int tsc2005_remove(struct spi_device *spi)
|
||||
{
|
||||
struct tsc2005 *ts = spi_get_drvdata(spi);
|
||||
struct tsc2005 *ts = dev_get_drvdata(&spi->dev);
|
||||
|
||||
sysfs_remove_group(&spi->dev.kobj, &tsc2005_attr_group);
|
||||
|
||||
@ -775,8 +717,7 @@ static int tsc2005_remove(struct spi_device *spi)
|
||||
|
||||
static int __maybe_unused tsc2005_suspend(struct device *dev)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
struct tsc2005 *ts = spi_get_drvdata(spi);
|
||||
struct tsc2005 *ts = dev_get_drvdata(dev);
|
||||
|
||||
mutex_lock(&ts->mutex);
|
||||
|
||||
@ -792,8 +733,7 @@ static int __maybe_unused tsc2005_suspend(struct device *dev)
|
||||
|
||||
static int __maybe_unused tsc2005_resume(struct device *dev)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
struct tsc2005 *ts = spi_get_drvdata(spi);
|
||||
struct tsc2005 *ts = dev_get_drvdata(dev);
|
||||
|
||||
mutex_lock(&ts->mutex);
|
||||
|
||||
|
@ -482,7 +482,6 @@ MODULE_DEVICE_TABLE(of, tsc2007_of_match);
|
||||
|
||||
static struct i2c_driver tsc2007_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "tsc2007",
|
||||
.of_match_table = of_match_ptr(tsc2007_of_match),
|
||||
},
|
||||
|
@ -271,7 +271,6 @@ MODULE_DEVICE_TABLE(i2c, wacom_i2c_id);
|
||||
static struct i2c_driver wacom_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "wacom_i2c",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &wacom_i2c_pm,
|
||||
},
|
||||
|
||||
|
@ -23,7 +23,7 @@
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#define WDT87XX_NAME "wdt87xx_i2c"
|
||||
#define WDT87XX_DRV_VER "0.9.6"
|
||||
#define WDT87XX_DRV_VER "0.9.7"
|
||||
#define WDT87XX_FW_NAME "wdt87xx_fw.bin"
|
||||
#define WDT87XX_CFG_NAME "wdt87xx_cfg.bin"
|
||||
|
||||
@ -85,6 +85,11 @@
|
||||
#define CTL_PARAM_OFFSET_PHY_H 24
|
||||
#define CTL_PARAM_OFFSET_FACTOR 32
|
||||
|
||||
/* The definition of the device descriptor */
|
||||
#define WDT_GD_DEVICE 1
|
||||
#define DEV_DESC_OFFSET_VID 8
|
||||
#define DEV_DESC_OFFSET_PID 10
|
||||
|
||||
/* Communication commands */
|
||||
#define PACKET_SIZE 56
|
||||
#define VND_REQ_READ 0x06
|
||||
@ -152,6 +157,7 @@
|
||||
/* Controller requires minimum 300us between commands */
|
||||
#define WDT_COMMAND_DELAY_MS 2
|
||||
#define WDT_FLASH_WRITE_DELAY_MS 4
|
||||
#define WDT_FW_RESET_TIME 2500
|
||||
|
||||
struct wdt87xx_sys_param {
|
||||
u16 fw_id;
|
||||
@ -165,6 +171,8 @@ struct wdt87xx_sys_param {
|
||||
u16 scaling_factor;
|
||||
u32 max_x;
|
||||
u32 max_y;
|
||||
u16 vendor_id;
|
||||
u16 product_id;
|
||||
};
|
||||
|
||||
struct wdt87xx_data {
|
||||
@ -208,6 +216,32 @@ static int wdt87xx_i2c_xfer(struct i2c_client *client,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wdt87xx_get_desc(struct i2c_client *client, u8 desc_idx,
|
||||
u8 *buf, size_t len)
|
||||
{
|
||||
u8 tx_buf[] = { 0x22, 0x00, 0x10, 0x0E, 0x23, 0x00 };
|
||||
int error;
|
||||
|
||||
tx_buf[2] |= desc_idx & 0xF;
|
||||
|
||||
error = wdt87xx_i2c_xfer(client, tx_buf, sizeof(tx_buf),
|
||||
buf, len);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "get desc failed: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
if (buf[0] != len) {
|
||||
dev_err(&client->dev, "unexpected response to get desc: %d\n",
|
||||
buf[0]);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mdelay(WDT_COMMAND_DELAY_MS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wdt87xx_get_string(struct i2c_client *client, u8 str_idx,
|
||||
u8 *buf, size_t len)
|
||||
{
|
||||
@ -373,7 +407,7 @@ static int wdt87xx_sw_reset(struct i2c_client *client)
|
||||
}
|
||||
|
||||
/* Wait the device to be ready */
|
||||
msleep(200);
|
||||
msleep(WDT_FW_RESET_TIME);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -403,6 +437,15 @@ static int wdt87xx_get_sysparam(struct i2c_client *client,
|
||||
u8 buf[PKT_READ_SIZE];
|
||||
int error;
|
||||
|
||||
error = wdt87xx_get_desc(client, WDT_GD_DEVICE, buf, 18);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "failed to get device desc\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
param->vendor_id = get_unaligned_le16(buf + DEV_DESC_OFFSET_VID);
|
||||
param->product_id = get_unaligned_le16(buf + DEV_DESC_OFFSET_PID);
|
||||
|
||||
error = wdt87xx_get_string(client, STRIDX_PARAMETERS, buf, 34);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "failed to get parameters\n");
|
||||
@ -994,6 +1037,8 @@ static int wdt87xx_ts_create_input_device(struct wdt87xx_data *wdt)
|
||||
|
||||
input->name = "WDT87xx Touchscreen";
|
||||
input->id.bustype = BUS_I2C;
|
||||
input->id.vendor = wdt->param.vendor_id;
|
||||
input->id.product = wdt->param.product_id;
|
||||
input->phys = wdt->phys;
|
||||
|
||||
input_set_abs_params(input, ABS_MT_POSITION_X, 0,
|
||||
|
@ -24,14 +24,13 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/platform_data/zforce_ts.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#define WAIT_TIMEOUT msecs_to_jiffies(1000)
|
||||
|
||||
@ -120,6 +119,9 @@ struct zforce_ts {
|
||||
|
||||
struct regulator *reg_vdd;
|
||||
|
||||
struct gpio_desc *gpio_int;
|
||||
struct gpio_desc *gpio_rst;
|
||||
|
||||
bool suspending;
|
||||
bool suspended;
|
||||
bool boot_complete;
|
||||
@ -161,6 +163,16 @@ static int zforce_command(struct zforce_ts *ts, u8 cmd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void zforce_reset_assert(struct zforce_ts *ts)
|
||||
{
|
||||
gpiod_set_value_cansleep(ts->gpio_rst, 1);
|
||||
}
|
||||
|
||||
static void zforce_reset_deassert(struct zforce_ts *ts)
|
||||
{
|
||||
gpiod_set_value_cansleep(ts->gpio_rst, 0);
|
||||
}
|
||||
|
||||
static int zforce_send_wait(struct zforce_ts *ts, const char *buf, int len)
|
||||
{
|
||||
struct i2c_client *client = ts->client;
|
||||
@ -479,7 +491,6 @@ static irqreturn_t zforce_irq_thread(int irq, void *dev_id)
|
||||
{
|
||||
struct zforce_ts *ts = dev_id;
|
||||
struct i2c_client *client = ts->client;
|
||||
const struct zforce_ts_platdata *pdata = ts->pdata;
|
||||
int ret;
|
||||
u8 payload_buffer[FRAME_MAXSIZE];
|
||||
u8 *payload;
|
||||
@ -499,7 +510,16 @@ static irqreturn_t zforce_irq_thread(int irq, void *dev_id)
|
||||
if (!ts->suspending && device_may_wakeup(&client->dev))
|
||||
pm_stay_awake(&client->dev);
|
||||
|
||||
while (!gpio_get_value(pdata->gpio_int)) {
|
||||
/*
|
||||
* Run at least once and exit the loop if
|
||||
* - the optional interrupt GPIO isn't specified
|
||||
* (there is only one packet read per ISR invocation, then)
|
||||
* or
|
||||
* - the GPIO isn't active any more
|
||||
* (packet read until the level GPIO indicates that there is
|
||||
* no IRQ any more)
|
||||
*/
|
||||
do {
|
||||
ret = zforce_read_packet(ts, payload_buffer);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev,
|
||||
@ -566,7 +586,7 @@ static irqreturn_t zforce_irq_thread(int irq, void *dev_id)
|
||||
payload[RESPONSE_ID]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (gpiod_get_value_cansleep(ts->gpio_int));
|
||||
|
||||
if (!ts->suspending && device_may_wakeup(&client->dev))
|
||||
pm_relax(&client->dev);
|
||||
@ -690,7 +710,7 @@ static void zforce_reset(void *data)
|
||||
{
|
||||
struct zforce_ts *ts = data;
|
||||
|
||||
gpio_set_value(ts->pdata->gpio_rst, 0);
|
||||
zforce_reset_assert(ts);
|
||||
|
||||
udelay(10);
|
||||
|
||||
@ -712,18 +732,6 @@ static struct zforce_ts_platdata *zforce_parse_dt(struct device *dev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
pdata->gpio_int = of_get_gpio(np, 0);
|
||||
if (!gpio_is_valid(pdata->gpio_int)) {
|
||||
dev_err(dev, "failed to get interrupt gpio\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
pdata->gpio_rst = of_get_gpio(np, 1);
|
||||
if (!gpio_is_valid(pdata->gpio_rst)) {
|
||||
dev_err(dev, "failed to get reset gpio\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
if (of_property_read_u32(np, "x-size", &pdata->x_max)) {
|
||||
dev_err(dev, "failed to get x-size property\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
@ -755,21 +763,50 @@ static int zforce_probe(struct i2c_client *client,
|
||||
if (!ts)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = devm_gpio_request_one(&client->dev, pdata->gpio_int, GPIOF_IN,
|
||||
"zforce_ts_int");
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "request of gpio %d failed, %d\n",
|
||||
pdata->gpio_int, ret);
|
||||
ts->gpio_rst = devm_gpiod_get_optional(&client->dev, "reset",
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(ts->gpio_rst)) {
|
||||
ret = PTR_ERR(ts->gpio_rst);
|
||||
dev_err(&client->dev,
|
||||
"failed to request reset GPIO: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_gpio_request_one(&client->dev, pdata->gpio_rst,
|
||||
GPIOF_OUT_INIT_LOW, "zforce_ts_rst");
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "request of gpio %d failed, %d\n",
|
||||
pdata->gpio_rst, ret);
|
||||
if (ts->gpio_rst) {
|
||||
ts->gpio_int = devm_gpiod_get_optional(&client->dev, "irq",
|
||||
GPIOD_IN);
|
||||
if (IS_ERR(ts->gpio_int)) {
|
||||
ret = PTR_ERR(ts->gpio_int);
|
||||
dev_err(&client->dev,
|
||||
"failed to request interrupt GPIO: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* Deprecated GPIO handling for compatibility
|
||||
* with legacy binding.
|
||||
*/
|
||||
|
||||
/* INT GPIO */
|
||||
ts->gpio_int = devm_gpiod_get_index(&client->dev, NULL, 0,
|
||||
GPIOD_IN);
|
||||
if (IS_ERR(ts->gpio_int)) {
|
||||
ret = PTR_ERR(ts->gpio_int);
|
||||
dev_err(&client->dev,
|
||||
"failed to request interrupt GPIO: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* RST GPIO */
|
||||
ts->gpio_rst = devm_gpiod_get_index(&client->dev, NULL, 1,
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(ts->gpio_rst)) {
|
||||
ret = PTR_ERR(ts->gpio_rst);
|
||||
dev_err(&client->dev,
|
||||
"failed to request reset GPIO: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ts->reg_vdd = devm_regulator_get_optional(&client->dev, "vdd");
|
||||
if (IS_ERR(ts->reg_vdd)) {
|
||||
@ -863,7 +900,7 @@ static int zforce_probe(struct i2c_client *client,
|
||||
i2c_set_clientdata(client, ts);
|
||||
|
||||
/* let the controller boot */
|
||||
gpio_set_value(pdata->gpio_rst, 1);
|
||||
zforce_reset_deassert(ts);
|
||||
|
||||
ts->command_waiting = NOTIFICATION_BOOTCOMPLETE;
|
||||
if (wait_for_completion_timeout(&ts->command_done, WAIT_TIMEOUT) == 0)
|
||||
@ -917,7 +954,6 @@ MODULE_DEVICE_TABLE(of, zforce_dt_idtable);
|
||||
|
||||
static struct i2c_driver zforce_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "zforce-ts",
|
||||
.pm = &zforce_pm_ops,
|
||||
.of_match_table = of_match_ptr(zforce_dt_idtable),
|
||||
|
@ -23,7 +23,7 @@
|
||||
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c/atmel_mxt_ts.h>
|
||||
#include <linux/platform_data/atmel_mxt_ts.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
@ -111,6 +111,7 @@ static struct mxt_platform_data atmel_224s_tp_platform_data = {
|
||||
.irqflags = IRQF_TRIGGER_FALLING,
|
||||
.t19_num_keys = ARRAY_SIZE(mxt_t19_keys),
|
||||
.t19_keymap = mxt_t19_keys,
|
||||
.suspend_mode = MXT_SUSPEND_T9_CTRL,
|
||||
};
|
||||
|
||||
static struct i2c_board_info atmel_224s_tp_device = {
|
||||
@ -121,6 +122,7 @@ static struct i2c_board_info atmel_224s_tp_device = {
|
||||
|
||||
static struct mxt_platform_data atmel_1664s_platform_data = {
|
||||
.irqflags = IRQF_TRIGGER_FALLING,
|
||||
.suspend_mode = MXT_SUSPEND_T9_CTRL,
|
||||
};
|
||||
|
||||
static struct i2c_board_info atmel_1664s_device = {
|
||||
|
@ -9,15 +9,8 @@
|
||||
#ifndef _TOUCHSCREEN_H
|
||||
#define _TOUCHSCREEN_H
|
||||
|
||||
#include <linux/input.h>
|
||||
struct input_dev;
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
void touchscreen_parse_of_params(struct input_dev *dev, bool multitouch);
|
||||
#else
|
||||
static inline void touchscreen_parse_of_params(struct input_dev *dev,
|
||||
bool multitouch)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
void touchscreen_parse_properties(struct input_dev *dev, bool multitouch);
|
||||
|
||||
#endif
|
||||
|
@ -10,16 +10,22 @@
|
||||
* option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef __LINUX_ATMEL_MXT_TS_H
|
||||
#define __LINUX_ATMEL_MXT_TS_H
|
||||
#ifndef __LINUX_PLATFORM_DATA_ATMEL_MXT_TS_H
|
||||
#define __LINUX_PLATFORM_DATA_ATMEL_MXT_TS_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
enum mxt_suspend_mode {
|
||||
MXT_SUSPEND_DEEP_SLEEP = 0,
|
||||
MXT_SUSPEND_T9_CTRL = 1,
|
||||
};
|
||||
|
||||
/* The platform data for the Atmel maXTouch touchscreen driver */
|
||||
struct mxt_platform_data {
|
||||
unsigned long irqflags;
|
||||
u8 t19_num_keys;
|
||||
const unsigned int *t19_keymap;
|
||||
enum mxt_suspend_mode suspend_mode;
|
||||
};
|
||||
|
||||
#endif /* __LINUX_ATMEL_MXT_TS_H */
|
||||
#endif /* __LINUX_PLATFORM_DATA_ATMEL_MXT_TS_H */
|
@ -57,7 +57,6 @@ struct pixcir_i2c_chip_data {
|
||||
struct pixcir_ts_platform_data {
|
||||
int x_max;
|
||||
int y_max;
|
||||
int gpio_attb; /* GPIO connected to ATTB line */
|
||||
struct pixcir_i2c_chip_data chip;
|
||||
};
|
||||
|
@ -16,9 +16,6 @@
|
||||
#define _LINUX_INPUT_ZFORCE_TS_H
|
||||
|
||||
struct zforce_ts_platdata {
|
||||
int gpio_int;
|
||||
int gpio_rst;
|
||||
|
||||
unsigned int x_max;
|
||||
unsigned int y_max;
|
||||
};
|
||||
|
@ -18,6 +18,8 @@
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <uapi/linux/serio.h>
|
||||
|
||||
extern struct bus_type serio_bus;
|
||||
|
||||
struct serio {
|
||||
void *port_data;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user