forked from Minki/linux
Merge branch 'next' into for-linus
Prepare input updates for 5.1 merge window.
This commit is contained in:
commit
65e91e2845
@ -1,13 +1,19 @@
|
||||
Samsung tm2-touchkey
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "cypress,tm2-touchkey"
|
||||
- compatible:
|
||||
* "cypress,tm2-touchkey" - for the touchkey found on the tm2 board
|
||||
* "cypress,midas-touchkey" - for the touchkey found on midas boards
|
||||
* "cypress,aries-touchkey" - for the touchkey found on aries boards
|
||||
- reg: I2C address of the chip.
|
||||
- interrupts: interrupt to which the chip is connected (see interrupt
|
||||
binding[0]).
|
||||
- vcc-supply : internal regulator output. 1.8V
|
||||
- vdd-supply : power supply for IC 3.3V
|
||||
|
||||
Optional properties:
|
||||
- linux,keycodes: array of keycodes (max 4), default KEY_PHONE and KEY_BACK
|
||||
|
||||
[0]: Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
|
||||
|
||||
Example:
|
||||
@ -21,5 +27,6 @@ Example:
|
||||
interrupts = <2 IRQ_TYPE_EDGE_FALLING>;
|
||||
vcc-supply=<&ldo32_reg>;
|
||||
vdd-supply=<&ldo33_reg>;
|
||||
linux,keycodes = <KEY_PHONE KEY_BACK>;
|
||||
};
|
||||
};
|
||||
|
25
Documentation/devicetree/bindings/input/ilitek,ili2xxx.txt
Normal file
25
Documentation/devicetree/bindings/input/ilitek,ili2xxx.txt
Normal file
@ -0,0 +1,25 @@
|
||||
Ilitek ILI210x/ILI251x touchscreen controller
|
||||
|
||||
Required properties:
|
||||
- compatible:
|
||||
ilitek,ili210x for ILI210x
|
||||
ilitek,ili251x for ILI251x
|
||||
|
||||
- reg: The I2C address of the device
|
||||
|
||||
- interrupts: The sink for the touchscreen's IRQ output
|
||||
See ../interrupt-controller/interrupts.txt
|
||||
|
||||
Optional properties for main touchpad device:
|
||||
|
||||
- reset-gpios: GPIO specifier for the touchscreen's reset pin (active low)
|
||||
|
||||
Example:
|
||||
|
||||
touchscreen@41 {
|
||||
compatible = "ilitek,ili251x";
|
||||
reg = <0x41>;
|
||||
interrupt-parent = <&gpio4>;
|
||||
interrupts = <7 IRQ_TYPE_EDGE_FALLING>;
|
||||
reset-gpios = <&gpio5 21 GPIO_ACTIVE_LOW>;
|
||||
};
|
36
Documentation/devicetree/bindings/input/msm-vibrator.txt
Normal file
36
Documentation/devicetree/bindings/input/msm-vibrator.txt
Normal file
@ -0,0 +1,36 @@
|
||||
* Device tree bindings for the Qualcomm MSM vibrator
|
||||
|
||||
Required properties:
|
||||
|
||||
- compatible: Should be one of
|
||||
"qcom,msm8226-vibrator"
|
||||
"qcom,msm8974-vibrator"
|
||||
- reg: the base address and length of the IO memory for the registers.
|
||||
- pinctrl-names: set to default.
|
||||
- pinctrl-0: phandles pointing to pin configuration nodes. See
|
||||
Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
|
||||
- clock-names: set to pwm
|
||||
- clocks: phandle of the clock. See
|
||||
Documentation/devicetree/bindings/clock/clock-bindings.txt
|
||||
- enable-gpios: GPIO that enables the vibrator.
|
||||
|
||||
Optional properties:
|
||||
|
||||
- vcc-supply: phandle to the regulator that provides power to the sensor.
|
||||
|
||||
Example from a LG Nexus 5 (hammerhead) phone:
|
||||
|
||||
vibrator@fd8c3450 {
|
||||
reg = <0xfd8c3450 0x400>;
|
||||
compatible = "qcom,msm8974-vibrator";
|
||||
|
||||
vcc-supply = <&pm8941_l19>;
|
||||
|
||||
clocks = <&mmcc CAMSS_GP1_CLK>;
|
||||
clock-names = "pwm";
|
||||
|
||||
enable-gpios = <&msmgpio 60 GPIO_ACTIVE_HIGH>;
|
||||
|
||||
pinctrl-names = "default";
|
||||
pinctrl-0 = <&vibrator_pin>;
|
||||
};
|
@ -1,11 +1,12 @@
|
||||
FocalTech EDT-FT5x06 Polytouch driver
|
||||
=====================================
|
||||
|
||||
There are 3 variants of the chip for various touch panel sizes
|
||||
There are 5 variants of the chip for various touch panel sizes
|
||||
FT5206GE1 2.8" .. 3.8"
|
||||
FT5306DE4 4.3" .. 7"
|
||||
FT5406EE8 7" .. 8.9"
|
||||
FT5506EEG 7" .. 8.9"
|
||||
FT5726NEI 5.7” .. 11.6"
|
||||
|
||||
The software interface is identical for all those chips, so that
|
||||
currently there is no need for the driver to distinguish between the
|
||||
@ -19,6 +20,7 @@ Required properties:
|
||||
or: "edt,edt-ft5306"
|
||||
or: "edt,edt-ft5406"
|
||||
or: "edt,edt-ft5506"
|
||||
or: "evervision,ev-ft5726"
|
||||
or: "focaltech,ft6236"
|
||||
|
||||
- reg: I2C slave address of the chip (0x38)
|
||||
@ -42,6 +44,15 @@ Optional properties:
|
||||
|
||||
- offset: allows setting the edge compensation in the range from
|
||||
0 to 31.
|
||||
|
||||
- offset-x: Same as offset, but applies only to the horizontal position.
|
||||
Range from 0 to 80, only supported by evervision,ev-ft5726
|
||||
devices.
|
||||
|
||||
- offset-y: Same as offset, but applies only to the vertical position.
|
||||
Range from 0 to 80, only supported by evervision,ev-ft5726
|
||||
devices.
|
||||
|
||||
- touchscreen-size-x : See touchscreen.txt
|
||||
- touchscreen-size-y : See touchscreen.txt
|
||||
- touchscreen-fuzz-x : See touchscreen.txt
|
||||
|
@ -3,6 +3,7 @@ Device tree bindings for Goodix GT9xx series touchscreen controller
|
||||
Required properties:
|
||||
|
||||
- compatible : Should be "goodix,gt1151"
|
||||
or "goodix,gt5688"
|
||||
or "goodix,gt911"
|
||||
or "goodix,gt9110"
|
||||
or "goodix,gt912"
|
||||
@ -18,11 +19,14 @@ Optional properties:
|
||||
- irq-gpios : GPIO pin used for IRQ. The driver uses the
|
||||
interrupt gpio pin as output to reset the device.
|
||||
- reset-gpios : GPIO pin used for reset
|
||||
- touchscreen-inverted-x
|
||||
- touchscreen-inverted-y
|
||||
- touchscreen-size-x
|
||||
- touchscreen-size-y
|
||||
- touchscreen-swapped-x-y
|
||||
|
||||
- touchscreen-inverted-x : X axis is inverted (boolean)
|
||||
- touchscreen-inverted-y : Y axis is inverted (boolean)
|
||||
- touchscreen-swapped-x-y : X and Y axis are swapped (boolean)
|
||||
(swapping is done after inverting the axis)
|
||||
The touchscreen-* properties are documented in touchscreen.txt in this
|
||||
directory.
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -1,13 +1,17 @@
|
||||
* Sitronix st1232 touchscreen controller
|
||||
* Sitronix st1232 or st1633 touchscreen controller
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "sitronix,st1232"
|
||||
- compatible: must contain one of
|
||||
* "sitronix,st1232"
|
||||
* "sitronix,st1633"
|
||||
- reg: I2C address of the chip
|
||||
- interrupts: interrupt to which the chip is connected
|
||||
|
||||
Optional properties:
|
||||
- gpios: a phandle to the reset GPIO
|
||||
|
||||
For additional optional properties see: touchscreen.txt
|
||||
|
||||
Example:
|
||||
|
||||
i2c@00000000 {
|
||||
|
@ -1,10 +1,17 @@
|
||||
* Semtech SX8654 I2C Touchscreen Controller
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "semtech,sx8654"
|
||||
- compatible: must be one of the following, depending on the model:
|
||||
"semtech,sx8650"
|
||||
"semtech,sx8654"
|
||||
"semtech,sx8655"
|
||||
"semtech,sx8656"
|
||||
- reg: i2c slave address
|
||||
- interrupts: touch controller interrupt
|
||||
|
||||
Optional properties:
|
||||
- reset-gpios: GPIO specification for the NRST input
|
||||
|
||||
Example:
|
||||
|
||||
sx8654@48 {
|
||||
@ -12,4 +19,5 @@ Example:
|
||||
reg = <0x48>;
|
||||
interrupt-parent = <&gpio6>;
|
||||
interrupts = <3 IRQ_TYPE_EDGE_FALLING>;
|
||||
reset-gpios = <&gpio4 2 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
|
@ -259,7 +259,7 @@ static unsigned char db9_saturn_read_packet(struct parport *port, unsigned char
|
||||
db9_saturn_write_sub(port, type, 3, powered, 0);
|
||||
return data[0] = 0xe3;
|
||||
}
|
||||
/* else: fall through */
|
||||
/* fall through */
|
||||
default:
|
||||
return data[0];
|
||||
}
|
||||
|
@ -1015,8 +1015,18 @@ static int __maybe_unused gpio_keys_resume(struct device *dev)
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(gpio_keys_pm_ops, gpio_keys_suspend, gpio_keys_resume);
|
||||
|
||||
static void gpio_keys_shutdown(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = gpio_keys_suspend(&pdev->dev);
|
||||
if (ret)
|
||||
dev_err(&pdev->dev, "failed to shutdown\n");
|
||||
}
|
||||
|
||||
static struct platform_driver gpio_keys_device_driver = {
|
||||
.probe = gpio_keys_probe,
|
||||
.shutdown = gpio_keys_shutdown,
|
||||
.driver = {
|
||||
.name = "gpio-keys",
|
||||
.pm = &gpio_keys_pm_ops,
|
||||
|
@ -113,9 +113,8 @@ static int mcs_touchkey_probe(struct i2c_client *client,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data = kzalloc(sizeof(struct mcs_touchkey_data) +
|
||||
sizeof(data->keycodes[0]) * (pdata->key_maxval + 1),
|
||||
GFP_KERNEL);
|
||||
data = kzalloc(struct_size(data, keycodes, pdata->key_maxval + 1),
|
||||
GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!data || !input_dev) {
|
||||
dev_err(&client->dev, "Failed to allocate memory\n");
|
||||
|
@ -14,18 +14,17 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/mfd/mt6323/registers.h>
|
||||
#include <linux/mfd/mt6397/registers.h>
|
||||
#include <linux/mfd/mt6397/core.h>
|
||||
#include <linux/mfd/mt6397/registers.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#define MTK_PMIC_PWRKEY_RST_EN_MASK 0x1
|
||||
#define MTK_PMIC_PWRKEY_RST_EN_SHIFT 6
|
||||
|
@ -68,7 +68,6 @@ struct qt2160_data {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input;
|
||||
struct delayed_work dwork;
|
||||
spinlock_t lock; /* Protects canceling/rescheduling of dwork */
|
||||
unsigned short keycodes[ARRAY_SIZE(qt2160_key2code)];
|
||||
u16 key_matrix;
|
||||
#ifdef CONFIG_LEDS_CLASS
|
||||
@ -212,22 +211,15 @@ static int qt2160_get_key_matrix(struct qt2160_data *qt2160)
|
||||
static irqreturn_t qt2160_irq(int irq, void *_qt2160)
|
||||
{
|
||||
struct qt2160_data *qt2160 = _qt2160;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&qt2160->lock, flags);
|
||||
|
||||
mod_delayed_work(system_wq, &qt2160->dwork, 0);
|
||||
|
||||
spin_unlock_irqrestore(&qt2160->lock, flags);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void qt2160_schedule_read(struct qt2160_data *qt2160)
|
||||
{
|
||||
spin_lock_irq(&qt2160->lock);
|
||||
schedule_delayed_work(&qt2160->dwork, QT2160_CYCLE_INTERVAL);
|
||||
spin_unlock_irq(&qt2160->lock);
|
||||
}
|
||||
|
||||
static void qt2160_worker(struct work_struct *work)
|
||||
@ -391,7 +383,6 @@ static int qt2160_probe(struct i2c_client *client,
|
||||
qt2160->client = client;
|
||||
qt2160->input = input;
|
||||
INIT_DELAYED_WORK(&qt2160->dwork, qt2160_worker);
|
||||
spin_lock_init(&qt2160->lock);
|
||||
|
||||
input->name = "AT42QT2160 Touch Sense Keyboard";
|
||||
input->id.bustype = BUS_I2C;
|
||||
|
@ -219,9 +219,7 @@ static int tca6416_keypad_probe(struct i2c_client *client,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
chip = kzalloc(sizeof(struct tca6416_keypad_chip) +
|
||||
pdata->nbuttons * sizeof(struct tca6416_button),
|
||||
GFP_KERNEL);
|
||||
chip = kzalloc(struct_size(chip, buttons, pdata->nbuttons), GFP_KERNEL);
|
||||
input = input_allocate_device();
|
||||
if (!chip || !input) {
|
||||
error = -ENOMEM;
|
||||
|
@ -22,12 +22,14 @@
|
||||
#include <linux/leds.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#define TM2_TOUCHKEY_DEV_NAME "tm2-touchkey"
|
||||
#define TM2_TOUCHKEY_KEYCODE_REG 0x03
|
||||
#define TM2_TOUCHKEY_BASE_REG 0x00
|
||||
|
||||
#define ARIES_TOUCHKEY_CMD_LED_ON 0x1
|
||||
#define ARIES_TOUCHKEY_CMD_LED_OFF 0x2
|
||||
#define TM2_TOUCHKEY_CMD_LED_ON 0x10
|
||||
#define TM2_TOUCHKEY_CMD_LED_OFF 0x20
|
||||
#define TM2_TOUCHKEY_BIT_PRESS_EV BIT(3)
|
||||
@ -35,9 +37,13 @@
|
||||
#define TM2_TOUCHKEY_LED_VOLTAGE_MIN 2500000
|
||||
#define TM2_TOUCHKEY_LED_VOLTAGE_MAX 3300000
|
||||
|
||||
enum {
|
||||
TM2_TOUCHKEY_KEY_MENU = 0x1,
|
||||
TM2_TOUCHKEY_KEY_BACK,
|
||||
struct touchkey_variant {
|
||||
u8 keycode_reg;
|
||||
u8 base_reg;
|
||||
u8 cmd_led_on;
|
||||
u8 cmd_led_off;
|
||||
bool no_reg;
|
||||
bool fixed_regulator;
|
||||
};
|
||||
|
||||
struct tm2_touchkey_data {
|
||||
@ -46,9 +52,33 @@ struct tm2_touchkey_data {
|
||||
struct led_classdev led_dev;
|
||||
struct regulator *vdd;
|
||||
struct regulator_bulk_data regulators[2];
|
||||
const struct touchkey_variant *variant;
|
||||
u32 keycodes[4];
|
||||
int num_keycodes;
|
||||
};
|
||||
|
||||
static void tm2_touchkey_led_brightness_set(struct led_classdev *led_dev,
|
||||
static const struct touchkey_variant tm2_touchkey_variant = {
|
||||
.keycode_reg = 0x03,
|
||||
.base_reg = 0x00,
|
||||
.cmd_led_on = TM2_TOUCHKEY_CMD_LED_ON,
|
||||
.cmd_led_off = TM2_TOUCHKEY_CMD_LED_OFF,
|
||||
};
|
||||
|
||||
static const struct touchkey_variant midas_touchkey_variant = {
|
||||
.keycode_reg = 0x00,
|
||||
.base_reg = 0x00,
|
||||
.cmd_led_on = TM2_TOUCHKEY_CMD_LED_ON,
|
||||
.cmd_led_off = TM2_TOUCHKEY_CMD_LED_OFF,
|
||||
};
|
||||
|
||||
static struct touchkey_variant aries_touchkey_variant = {
|
||||
.no_reg = true,
|
||||
.fixed_regulator = true,
|
||||
.cmd_led_on = ARIES_TOUCHKEY_CMD_LED_ON,
|
||||
.cmd_led_off = ARIES_TOUCHKEY_CMD_LED_OFF,
|
||||
};
|
||||
|
||||
static int tm2_touchkey_led_brightness_set(struct led_classdev *led_dev,
|
||||
enum led_brightness brightness)
|
||||
{
|
||||
struct tm2_touchkey_data *touchkey =
|
||||
@ -58,15 +88,19 @@ static void tm2_touchkey_led_brightness_set(struct led_classdev *led_dev,
|
||||
|
||||
if (brightness == LED_OFF) {
|
||||
volt = TM2_TOUCHKEY_LED_VOLTAGE_MIN;
|
||||
data = TM2_TOUCHKEY_CMD_LED_OFF;
|
||||
data = touchkey->variant->cmd_led_off;
|
||||
} else {
|
||||
volt = TM2_TOUCHKEY_LED_VOLTAGE_MAX;
|
||||
data = TM2_TOUCHKEY_CMD_LED_ON;
|
||||
data = touchkey->variant->cmd_led_on;
|
||||
}
|
||||
|
||||
regulator_set_voltage(touchkey->vdd, volt, volt);
|
||||
i2c_smbus_write_byte_data(touchkey->client,
|
||||
TM2_TOUCHKEY_BASE_REG, data);
|
||||
if (!touchkey->variant->fixed_regulator)
|
||||
regulator_set_voltage(touchkey->vdd, volt, volt);
|
||||
|
||||
return touchkey->variant->no_reg ?
|
||||
i2c_smbus_write_byte(touchkey->client, data) :
|
||||
i2c_smbus_write_byte_data(touchkey->client,
|
||||
touchkey->variant->base_reg, data);
|
||||
}
|
||||
|
||||
static int tm2_touchkey_power_enable(struct tm2_touchkey_data *touchkey)
|
||||
@ -96,49 +130,57 @@ static irqreturn_t tm2_touchkey_irq_handler(int irq, void *devid)
|
||||
{
|
||||
struct tm2_touchkey_data *touchkey = devid;
|
||||
int data;
|
||||
int key;
|
||||
int index;
|
||||
int i;
|
||||
|
||||
data = i2c_smbus_read_byte_data(touchkey->client,
|
||||
TM2_TOUCHKEY_KEYCODE_REG);
|
||||
if (touchkey->variant->no_reg)
|
||||
data = i2c_smbus_read_byte(touchkey->client);
|
||||
else
|
||||
data = i2c_smbus_read_byte_data(touchkey->client,
|
||||
touchkey->variant->keycode_reg);
|
||||
if (data < 0) {
|
||||
dev_err(&touchkey->client->dev,
|
||||
"failed to read i2c data: %d\n", data);
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (data & TM2_TOUCHKEY_BIT_KEYCODE) {
|
||||
case TM2_TOUCHKEY_KEY_MENU:
|
||||
key = KEY_PHONE;
|
||||
break;
|
||||
|
||||
case TM2_TOUCHKEY_KEY_BACK:
|
||||
key = KEY_BACK;
|
||||
break;
|
||||
|
||||
default:
|
||||
index = (data & TM2_TOUCHKEY_BIT_KEYCODE) - 1;
|
||||
if (index < 0 || index >= touchkey->num_keycodes) {
|
||||
dev_warn(&touchkey->client->dev,
|
||||
"unhandled keycode, data %#02x\n", data);
|
||||
"invalid keycode index %d\n", index);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (data & TM2_TOUCHKEY_BIT_PRESS_EV) {
|
||||
input_report_key(touchkey->input_dev, KEY_PHONE, 0);
|
||||
input_report_key(touchkey->input_dev, KEY_BACK, 0);
|
||||
for (i = 0; i < touchkey->num_keycodes; i++)
|
||||
input_report_key(touchkey->input_dev,
|
||||
touchkey->keycodes[i], 0);
|
||||
} else {
|
||||
input_report_key(touchkey->input_dev, key, 1);
|
||||
input_report_key(touchkey->input_dev,
|
||||
touchkey->keycodes[index], 1);
|
||||
}
|
||||
|
||||
input_sync(touchkey->input_dev);
|
||||
|
||||
out:
|
||||
if (touchkey->variant->fixed_regulator &&
|
||||
data & TM2_TOUCHKEY_BIT_PRESS_EV) {
|
||||
/* touch turns backlight on, so make sure we're in sync */
|
||||
if (touchkey->led_dev.brightness == LED_OFF)
|
||||
tm2_touchkey_led_brightness_set(&touchkey->led_dev,
|
||||
LED_OFF);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int tm2_touchkey_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct device_node *np = client->dev.of_node;
|
||||
struct tm2_touchkey_data *touchkey;
|
||||
int error;
|
||||
int i;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA)) {
|
||||
@ -153,6 +195,8 @@ static int tm2_touchkey_probe(struct i2c_client *client,
|
||||
touchkey->client = client;
|
||||
i2c_set_clientdata(client, touchkey);
|
||||
|
||||
touchkey->variant = of_device_get_match_data(&client->dev);
|
||||
|
||||
touchkey->regulators[0].supply = "vcc";
|
||||
touchkey->regulators[1].supply = "vdd";
|
||||
error = devm_regulator_bulk_get(&client->dev,
|
||||
@ -166,6 +210,16 @@ static int tm2_touchkey_probe(struct i2c_client *client,
|
||||
/* Save VDD for easy access */
|
||||
touchkey->vdd = touchkey->regulators[1].consumer;
|
||||
|
||||
touchkey->num_keycodes = of_property_read_variable_u32_array(np,
|
||||
"linux,keycodes", touchkey->keycodes, 0,
|
||||
ARRAY_SIZE(touchkey->keycodes));
|
||||
if (touchkey->num_keycodes <= 0) {
|
||||
/* default keycodes */
|
||||
touchkey->keycodes[0] = KEY_PHONE;
|
||||
touchkey->keycodes[1] = KEY_BACK;
|
||||
touchkey->num_keycodes = 2;
|
||||
}
|
||||
|
||||
error = tm2_touchkey_power_enable(touchkey);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "failed to power up device: %d\n", error);
|
||||
@ -190,8 +244,9 @@ static int tm2_touchkey_probe(struct i2c_client *client,
|
||||
touchkey->input_dev->name = TM2_TOUCHKEY_DEV_NAME;
|
||||
touchkey->input_dev->id.bustype = BUS_I2C;
|
||||
|
||||
input_set_capability(touchkey->input_dev, EV_KEY, KEY_PHONE);
|
||||
input_set_capability(touchkey->input_dev, EV_KEY, KEY_BACK);
|
||||
for (i = 0; i < touchkey->num_keycodes; i++)
|
||||
input_set_capability(touchkey->input_dev, EV_KEY,
|
||||
touchkey->keycodes[i]);
|
||||
|
||||
error = input_register_device(touchkey->input_dev);
|
||||
if (error) {
|
||||
@ -212,9 +267,10 @@ static int tm2_touchkey_probe(struct i2c_client *client,
|
||||
|
||||
/* led device */
|
||||
touchkey->led_dev.name = TM2_TOUCHKEY_DEV_NAME;
|
||||
touchkey->led_dev.brightness = LED_FULL;
|
||||
touchkey->led_dev.brightness = LED_ON;
|
||||
touchkey->led_dev.max_brightness = LED_ON;
|
||||
touchkey->led_dev.brightness_set = tm2_touchkey_led_brightness_set;
|
||||
touchkey->led_dev.brightness_set_blocking =
|
||||
tm2_touchkey_led_brightness_set;
|
||||
|
||||
error = devm_led_classdev_register(&client->dev, &touchkey->led_dev);
|
||||
if (error) {
|
||||
@ -223,6 +279,9 @@ static int tm2_touchkey_probe(struct i2c_client *client,
|
||||
return error;
|
||||
}
|
||||
|
||||
if (touchkey->variant->fixed_regulator)
|
||||
tm2_touchkey_led_brightness_set(&touchkey->led_dev, LED_ON);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -262,7 +321,16 @@ static const struct i2c_device_id tm2_touchkey_id_table[] = {
|
||||
MODULE_DEVICE_TABLE(i2c, tm2_touchkey_id_table);
|
||||
|
||||
static const struct of_device_id tm2_touchkey_of_match[] = {
|
||||
{ .compatible = "cypress,tm2-touchkey", },
|
||||
{
|
||||
.compatible = "cypress,tm2-touchkey",
|
||||
.data = &tm2_touchkey_variant,
|
||||
}, {
|
||||
.compatible = "cypress,midas-touchkey",
|
||||
.data = &midas_touchkey_variant,
|
||||
}, {
|
||||
.compatible = "cypress,aries-touchkey",
|
||||
.data = &aries_touchkey_variant,
|
||||
},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, tm2_touchkey_of_match);
|
||||
|
@ -117,6 +117,16 @@ config INPUT_E3X0_BUTTON
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called e3x0_button.
|
||||
|
||||
config INPUT_MSM_VIBRATOR
|
||||
tristate "Qualcomm MSM vibrator driver"
|
||||
select INPUT_FF_MEMLESS
|
||||
help
|
||||
Support for the vibrator that is found on various Qualcomm MSM
|
||||
SOCs.
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called msm_vibrator.
|
||||
|
||||
config INPUT_PCSPKR
|
||||
tristate "PC Speaker support"
|
||||
depends on PCSPKR_PLATFORM
|
||||
|
@ -48,6 +48,7 @@ obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o
|
||||
obj-$(CONFIG_INPUT_MAX8997_HAPTIC) += max8997_haptic.o
|
||||
obj-$(CONFIG_INPUT_MC13783_PWRBUTTON) += mc13783-pwrbutton.o
|
||||
obj-$(CONFIG_INPUT_MMA8450) += mma8450.o
|
||||
obj-$(CONFIG_INPUT_MSM_VIBRATOR) += msm-vibrator.o
|
||||
obj-$(CONFIG_INPUT_PALMAS_PWRBUTTON) += palmas-pwrbutton.o
|
||||
obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o
|
||||
obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o
|
||||
|
@ -39,8 +39,6 @@ struct ims_pcu_gamepad {
|
||||
|
||||
struct ims_pcu_backlight {
|
||||
struct led_classdev cdev;
|
||||
struct work_struct work;
|
||||
enum led_brightness desired_brightness;
|
||||
char name[32];
|
||||
};
|
||||
|
||||
@ -949,14 +947,14 @@ out:
|
||||
|
||||
#define IMS_PCU_MAX_BRIGHTNESS 31998
|
||||
|
||||
static void ims_pcu_backlight_work(struct work_struct *work)
|
||||
static int ims_pcu_backlight_set_brightness(struct led_classdev *cdev,
|
||||
enum led_brightness value)
|
||||
{
|
||||
struct ims_pcu_backlight *backlight =
|
||||
container_of(work, struct ims_pcu_backlight, work);
|
||||
container_of(cdev, struct ims_pcu_backlight, cdev);
|
||||
struct ims_pcu *pcu =
|
||||
container_of(backlight, struct ims_pcu, backlight);
|
||||
int desired_brightness = backlight->desired_brightness;
|
||||
__le16 br_val = cpu_to_le16(desired_brightness);
|
||||
__le16 br_val = cpu_to_le16(value);
|
||||
int error;
|
||||
|
||||
mutex_lock(&pcu->cmd_mutex);
|
||||
@ -966,19 +964,11 @@ static void ims_pcu_backlight_work(struct work_struct *work)
|
||||
if (error && error != -ENODEV)
|
||||
dev_warn(pcu->dev,
|
||||
"Failed to set desired brightness %u, error: %d\n",
|
||||
desired_brightness, error);
|
||||
value, error);
|
||||
|
||||
mutex_unlock(&pcu->cmd_mutex);
|
||||
}
|
||||
|
||||
static void ims_pcu_backlight_set_brightness(struct led_classdev *cdev,
|
||||
enum led_brightness value)
|
||||
{
|
||||
struct ims_pcu_backlight *backlight =
|
||||
container_of(cdev, struct ims_pcu_backlight, cdev);
|
||||
|
||||
backlight->desired_brightness = value;
|
||||
schedule_work(&backlight->work);
|
||||
return error;
|
||||
}
|
||||
|
||||
static enum led_brightness
|
||||
@ -1015,14 +1005,14 @@ static int ims_pcu_setup_backlight(struct ims_pcu *pcu)
|
||||
struct ims_pcu_backlight *backlight = &pcu->backlight;
|
||||
int error;
|
||||
|
||||
INIT_WORK(&backlight->work, ims_pcu_backlight_work);
|
||||
snprintf(backlight->name, sizeof(backlight->name),
|
||||
"pcu%d::kbd_backlight", pcu->device_no);
|
||||
|
||||
backlight->cdev.name = backlight->name;
|
||||
backlight->cdev.max_brightness = IMS_PCU_MAX_BRIGHTNESS;
|
||||
backlight->cdev.brightness_get = ims_pcu_backlight_get_brightness;
|
||||
backlight->cdev.brightness_set = ims_pcu_backlight_set_brightness;
|
||||
backlight->cdev.brightness_set_blocking =
|
||||
ims_pcu_backlight_set_brightness;
|
||||
|
||||
error = led_classdev_register(pcu->dev, &backlight->cdev);
|
||||
if (error) {
|
||||
@ -1040,7 +1030,6 @@ static void ims_pcu_destroy_backlight(struct ims_pcu *pcu)
|
||||
struct ims_pcu_backlight *backlight = &pcu->backlight;
|
||||
|
||||
led_classdev_unregister(&backlight->cdev);
|
||||
cancel_work_sync(&backlight->work);
|
||||
}
|
||||
|
||||
|
||||
|
281
drivers/input/misc/msm-vibrator.c
Normal file
281
drivers/input/misc/msm-vibrator.c
Normal file
@ -0,0 +1,281 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Qualcomm MSM vibrator driver
|
||||
*
|
||||
* Copyright (c) 2018 Brian Masney <masneyb@onstation.org>
|
||||
*
|
||||
* Based on qcom,pwm-vibrator.c from:
|
||||
* Copyright (c) 2018 Jonathan Marek <jonathan@marek.ca>
|
||||
*
|
||||
* Based on msm_pwm_vibrator.c from downstream Android sources:
|
||||
* Copyright (C) 2009-2014 LGE, Inc.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#define REG_CMD_RCGR 0x00
|
||||
#define REG_CFG_RCGR 0x04
|
||||
#define REG_M 0x08
|
||||
#define REG_N 0x0C
|
||||
#define REG_D 0x10
|
||||
#define REG_CBCR 0x24
|
||||
#define MMSS_CC_M_DEFAULT 1
|
||||
|
||||
struct msm_vibrator {
|
||||
struct input_dev *input;
|
||||
struct mutex mutex;
|
||||
struct work_struct worker;
|
||||
void __iomem *base;
|
||||
struct regulator *vcc;
|
||||
struct clk *clk;
|
||||
struct gpio_desc *enable_gpio;
|
||||
u16 magnitude;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
static void msm_vibrator_write(struct msm_vibrator *vibrator, int offset,
|
||||
u32 value)
|
||||
{
|
||||
writel(value, vibrator->base + offset);
|
||||
}
|
||||
|
||||
static int msm_vibrator_start(struct msm_vibrator *vibrator)
|
||||
{
|
||||
int d_reg_val, ret = 0;
|
||||
|
||||
mutex_lock(&vibrator->mutex);
|
||||
|
||||
if (!vibrator->enabled) {
|
||||
ret = clk_set_rate(vibrator->clk, 24000);
|
||||
if (ret) {
|
||||
dev_err(&vibrator->input->dev,
|
||||
"Failed to set clock rate: %d\n", ret);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(vibrator->clk);
|
||||
if (ret) {
|
||||
dev_err(&vibrator->input->dev,
|
||||
"Failed to enable clock: %d\n", ret);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
ret = regulator_enable(vibrator->vcc);
|
||||
if (ret) {
|
||||
dev_err(&vibrator->input->dev,
|
||||
"Failed to enable regulator: %d\n", ret);
|
||||
clk_disable(vibrator->clk);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
gpiod_set_value_cansleep(vibrator->enable_gpio, 1);
|
||||
|
||||
vibrator->enabled = true;
|
||||
}
|
||||
|
||||
d_reg_val = 127 - ((126 * vibrator->magnitude) / 0xffff);
|
||||
msm_vibrator_write(vibrator, REG_CFG_RCGR,
|
||||
(2 << 12) | /* dual edge mode */
|
||||
(0 << 8) | /* cxo */
|
||||
(7 << 0));
|
||||
msm_vibrator_write(vibrator, REG_M, 1);
|
||||
msm_vibrator_write(vibrator, REG_N, 128);
|
||||
msm_vibrator_write(vibrator, REG_D, d_reg_val);
|
||||
msm_vibrator_write(vibrator, REG_CMD_RCGR, 1);
|
||||
msm_vibrator_write(vibrator, REG_CBCR, 1);
|
||||
|
||||
unlock:
|
||||
mutex_unlock(&vibrator->mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void msm_vibrator_stop(struct msm_vibrator *vibrator)
|
||||
{
|
||||
mutex_lock(&vibrator->mutex);
|
||||
|
||||
if (vibrator->enabled) {
|
||||
gpiod_set_value_cansleep(vibrator->enable_gpio, 0);
|
||||
regulator_disable(vibrator->vcc);
|
||||
clk_disable(vibrator->clk);
|
||||
vibrator->enabled = false;
|
||||
}
|
||||
|
||||
mutex_unlock(&vibrator->mutex);
|
||||
}
|
||||
|
||||
static void msm_vibrator_worker(struct work_struct *work)
|
||||
{
|
||||
struct msm_vibrator *vibrator = container_of(work,
|
||||
struct msm_vibrator,
|
||||
worker);
|
||||
|
||||
if (vibrator->magnitude)
|
||||
msm_vibrator_start(vibrator);
|
||||
else
|
||||
msm_vibrator_stop(vibrator);
|
||||
}
|
||||
|
||||
static int msm_vibrator_play_effect(struct input_dev *dev, void *data,
|
||||
struct ff_effect *effect)
|
||||
{
|
||||
struct msm_vibrator *vibrator = input_get_drvdata(dev);
|
||||
|
||||
mutex_lock(&vibrator->mutex);
|
||||
|
||||
if (effect->u.rumble.strong_magnitude > 0)
|
||||
vibrator->magnitude = effect->u.rumble.strong_magnitude;
|
||||
else
|
||||
vibrator->magnitude = effect->u.rumble.weak_magnitude;
|
||||
|
||||
mutex_unlock(&vibrator->mutex);
|
||||
|
||||
schedule_work(&vibrator->worker);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void msm_vibrator_close(struct input_dev *input)
|
||||
{
|
||||
struct msm_vibrator *vibrator = input_get_drvdata(input);
|
||||
|
||||
cancel_work_sync(&vibrator->worker);
|
||||
msm_vibrator_stop(vibrator);
|
||||
}
|
||||
|
||||
static int msm_vibrator_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct msm_vibrator *vibrator;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
vibrator = devm_kzalloc(&pdev->dev, sizeof(*vibrator), GFP_KERNEL);
|
||||
if (!vibrator)
|
||||
return -ENOMEM;
|
||||
|
||||
vibrator->input = devm_input_allocate_device(&pdev->dev);
|
||||
if (!vibrator->input)
|
||||
return -ENOMEM;
|
||||
|
||||
vibrator->vcc = devm_regulator_get(&pdev->dev, "vcc");
|
||||
if (IS_ERR(vibrator->vcc)) {
|
||||
if (PTR_ERR(vibrator->vcc) != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "Failed to get regulator: %ld\n",
|
||||
PTR_ERR(vibrator->vcc));
|
||||
return PTR_ERR(vibrator->vcc);
|
||||
}
|
||||
|
||||
vibrator->enable_gpio = devm_gpiod_get(&pdev->dev, "enable",
|
||||
GPIOD_OUT_LOW);
|
||||
if (IS_ERR(vibrator->enable_gpio)) {
|
||||
if (PTR_ERR(vibrator->enable_gpio) != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "Failed to get enable gpio: %ld\n",
|
||||
PTR_ERR(vibrator->enable_gpio));
|
||||
return PTR_ERR(vibrator->enable_gpio);
|
||||
}
|
||||
|
||||
vibrator->clk = devm_clk_get(&pdev->dev, "pwm");
|
||||
if (IS_ERR(vibrator->clk)) {
|
||||
if (PTR_ERR(vibrator->clk) != -EPROBE_DEFER)
|
||||
dev_err(&pdev->dev, "Failed to lookup pwm clock: %ld\n",
|
||||
PTR_ERR(vibrator->clk));
|
||||
return PTR_ERR(vibrator->clk);
|
||||
}
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "Failed to get platform resource\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
vibrator->base = devm_ioremap(&pdev->dev, res->start,
|
||||
resource_size(res));
|
||||
if (!vibrator->base) {
|
||||
dev_err(&pdev->dev, "Failed to iomap resource.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
vibrator->enabled = false;
|
||||
mutex_init(&vibrator->mutex);
|
||||
INIT_WORK(&vibrator->worker, msm_vibrator_worker);
|
||||
|
||||
vibrator->input->name = "msm-vibrator";
|
||||
vibrator->input->id.bustype = BUS_HOST;
|
||||
vibrator->input->close = msm_vibrator_close;
|
||||
|
||||
input_set_drvdata(vibrator->input, vibrator);
|
||||
input_set_capability(vibrator->input, EV_FF, FF_RUMBLE);
|
||||
|
||||
ret = input_ff_create_memless(vibrator->input, NULL,
|
||||
msm_vibrator_play_effect);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to create ff memless: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = input_register_device(vibrator->input);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Failed to register input device: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, vibrator);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused msm_vibrator_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct msm_vibrator *vibrator = platform_get_drvdata(pdev);
|
||||
|
||||
cancel_work_sync(&vibrator->worker);
|
||||
|
||||
if (vibrator->enabled)
|
||||
msm_vibrator_stop(vibrator);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused msm_vibrator_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct msm_vibrator *vibrator = platform_get_drvdata(pdev);
|
||||
|
||||
if (vibrator->enabled)
|
||||
msm_vibrator_start(vibrator);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(msm_vibrator_pm_ops, msm_vibrator_suspend,
|
||||
msm_vibrator_resume);
|
||||
|
||||
static const struct of_device_id msm_vibrator_of_match[] = {
|
||||
{ .compatible = "qcom,msm8226-vibrator" },
|
||||
{ .compatible = "qcom,msm8974-vibrator" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, msm_vibrator_of_match);
|
||||
|
||||
static struct platform_driver msm_vibrator_driver = {
|
||||
.probe = msm_vibrator_probe,
|
||||
.driver = {
|
||||
.name = "msm-vibrator",
|
||||
.pm = &msm_vibrator_pm_ops,
|
||||
.of_match_table = of_match_ptr(msm_vibrator_of_match),
|
||||
},
|
||||
};
|
||||
module_platform_driver(msm_vibrator_driver);
|
||||
|
||||
MODULE_AUTHOR("Brian Masney <masneyb@onstation.org>");
|
||||
MODULE_DESCRIPTION("Qualcomm MSM vibrator driver");
|
||||
MODULE_LICENSE("GPL");
|
@ -185,6 +185,10 @@ static int soc_button_parse_btn_desc(struct device *dev,
|
||||
info->name = "power";
|
||||
info->event_code = KEY_POWER;
|
||||
info->wakeup = true;
|
||||
} else if (upage == 0x01 && usage == 0xca) {
|
||||
info->name = "rotation lock switch";
|
||||
info->event_type = EV_SW;
|
||||
info->event_code = SW_ROTATE_LOCK;
|
||||
} else if (upage == 0x07 && usage == 0xe3) {
|
||||
info->name = "home";
|
||||
info->event_code = KEY_LEFTMETA;
|
||||
@ -373,7 +377,7 @@ static struct soc_button_info soc_button_PNP0C40[] = {
|
||||
{ "home", 1, EV_KEY, KEY_LEFTMETA, false, true },
|
||||
{ "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false },
|
||||
{ "volume_down", 3, EV_KEY, KEY_VOLUMEDOWN, true, false },
|
||||
{ "rotation_lock", 4, EV_SW, SW_ROTATE_LOCK, false, false },
|
||||
{ "rotation_lock", 4, EV_KEY, KEY_ROTATE_LOCK_TOGGLE, false, false },
|
||||
{ }
|
||||
};
|
||||
|
||||
|
@ -219,7 +219,6 @@ struct synaptics_i2c {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input;
|
||||
struct delayed_work dwork;
|
||||
spinlock_t lock;
|
||||
int no_data_count;
|
||||
int no_decel_param;
|
||||
int reduce_report_param;
|
||||
@ -369,23 +368,11 @@ static bool synaptics_i2c_get_input(struct synaptics_i2c *touch)
|
||||
return xy_delta || gesture;
|
||||
}
|
||||
|
||||
static void synaptics_i2c_reschedule_work(struct synaptics_i2c *touch,
|
||||
unsigned long delay)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&touch->lock, flags);
|
||||
|
||||
mod_delayed_work(system_wq, &touch->dwork, delay);
|
||||
|
||||
spin_unlock_irqrestore(&touch->lock, flags);
|
||||
}
|
||||
|
||||
static irqreturn_t synaptics_i2c_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct synaptics_i2c *touch = dev_id;
|
||||
|
||||
synaptics_i2c_reschedule_work(touch, 0);
|
||||
mod_delayed_work(system_wq, &touch->dwork, 0);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
@ -461,7 +448,7 @@ static void synaptics_i2c_work_handler(struct work_struct *work)
|
||||
* We poll the device once in THREAD_IRQ_SLEEP_SECS and
|
||||
* if error is detected, we try to reset and reconfigure the touchpad.
|
||||
*/
|
||||
synaptics_i2c_reschedule_work(touch, delay);
|
||||
mod_delayed_work(system_wq, &touch->dwork, delay);
|
||||
}
|
||||
|
||||
static int synaptics_i2c_open(struct input_dev *input)
|
||||
@ -474,7 +461,7 @@ static int synaptics_i2c_open(struct input_dev *input)
|
||||
return ret;
|
||||
|
||||
if (polling_req)
|
||||
synaptics_i2c_reschedule_work(touch,
|
||||
mod_delayed_work(system_wq, &touch->dwork,
|
||||
msecs_to_jiffies(NO_DATA_SLEEP_MSECS));
|
||||
|
||||
return 0;
|
||||
@ -530,7 +517,6 @@ static struct synaptics_i2c *synaptics_i2c_touch_create(struct i2c_client *clien
|
||||
touch->scan_rate_param = scan_rate;
|
||||
set_scan_rate(touch, scan_rate);
|
||||
INIT_DELAYED_WORK(&touch->dwork, synaptics_i2c_work_handler);
|
||||
spin_lock_init(&touch->lock);
|
||||
|
||||
return touch;
|
||||
}
|
||||
@ -637,7 +623,7 @@ static int __maybe_unused synaptics_i2c_resume(struct device *dev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
synaptics_i2c_reschedule_work(touch,
|
||||
mod_delayed_work(system_wq, &touch->dwork,
|
||||
msecs_to_jiffies(NO_DATA_SLEEP_MSECS));
|
||||
|
||||
return 0;
|
||||
|
@ -53,12 +53,11 @@ static struct resource *kbd_res;
|
||||
|
||||
static int sparc_i8042_probe(struct platform_device *op)
|
||||
{
|
||||
struct device_node *dp = op->dev.of_node;
|
||||
struct device_node *dp;
|
||||
|
||||
dp = dp->child;
|
||||
while (dp) {
|
||||
if (!strcmp(dp->name, OBP_PS2KBD_NAME1) ||
|
||||
!strcmp(dp->name, OBP_PS2KBD_NAME2)) {
|
||||
for_each_child_of_node(op->dev.of_node, dp) {
|
||||
if (of_node_name_eq(dp, OBP_PS2KBD_NAME1) ||
|
||||
of_node_name_eq(dp, OBP_PS2KBD_NAME2)) {
|
||||
struct platform_device *kbd = of_find_device_by_node(dp);
|
||||
unsigned int irq = kbd->archdata.irqs[0];
|
||||
if (irq == 0xffffffff)
|
||||
@ -67,16 +66,14 @@ static int sparc_i8042_probe(struct platform_device *op)
|
||||
kbd_iobase = of_ioremap(&kbd->resource[0],
|
||||
0, 8, "kbd");
|
||||
kbd_res = &kbd->resource[0];
|
||||
} else if (!strcmp(dp->name, OBP_PS2MS_NAME1) ||
|
||||
!strcmp(dp->name, OBP_PS2MS_NAME2)) {
|
||||
} else if (of_node_name_eq(dp, OBP_PS2MS_NAME1) ||
|
||||
of_node_name_eq(dp, OBP_PS2MS_NAME2)) {
|
||||
struct platform_device *ms = of_find_device_by_node(dp);
|
||||
unsigned int irq = ms->archdata.irqs[0];
|
||||
if (irq == 0xffffffff)
|
||||
irq = op->archdata.irqs[0];
|
||||
i8042_aux_irq = irq;
|
||||
}
|
||||
|
||||
dp = dp->sibling;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -109,8 +106,9 @@ static struct platform_driver sparc_i8042_driver = {
|
||||
static int __init i8042_platform_init(void)
|
||||
{
|
||||
struct device_node *root = of_find_node_by_path("/");
|
||||
const char *name = of_get_property(root, "name", NULL);
|
||||
|
||||
if (!strcmp(root->name, "SUNW,JavaStation-1")) {
|
||||
if (name && !strcmp(name, "SUNW,JavaStation-1")) {
|
||||
/* Hardcoded values for MrCoffee. */
|
||||
i8042_kbd_irq = i8042_aux_irq = 13 | 0x20;
|
||||
kbd_iobase = ioremap(0x71300060, 8);
|
||||
@ -139,8 +137,9 @@ static int __init i8042_platform_init(void)
|
||||
static inline void i8042_platform_exit(void)
|
||||
{
|
||||
struct device_node *root = of_find_node_by_path("/");
|
||||
const char *name = of_get_property(root, "name", NULL);
|
||||
|
||||
if (strcmp(root->name, "SUNW,JavaStation-1"))
|
||||
if (!name || strcmp(name, "SUNW,JavaStation-1"))
|
||||
platform_driver_unregister(&sparc_i8042_driver);
|
||||
}
|
||||
|
||||
|
@ -1168,11 +1168,11 @@ config TOUCHSCREEN_SIS_I2C
|
||||
module will be called sis_i2c.
|
||||
|
||||
config TOUCHSCREEN_ST1232
|
||||
tristate "Sitronix ST1232 touchscreen controllers"
|
||||
tristate "Sitronix ST1232 or ST1633 touchscreen controllers"
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here if you want to support Sitronix ST1232
|
||||
touchscreen controller.
|
||||
Say Y here if you want to support the Sitronix ST1232
|
||||
or ST1633 touchscreen controller.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
|
@ -246,11 +246,14 @@ static void ad7879_timer(struct timer_list *t)
|
||||
static irqreturn_t ad7879_irq(int irq, void *handle)
|
||||
{
|
||||
struct ad7879 *ts = handle;
|
||||
int error;
|
||||
|
||||
regmap_bulk_read(ts->regmap, AD7879_REG_XPLUS,
|
||||
ts->conversion_data, AD7879_NR_SENSE);
|
||||
|
||||
if (!ad7879_report(ts))
|
||||
error = regmap_bulk_read(ts->regmap, AD7879_REG_XPLUS,
|
||||
ts->conversion_data, AD7879_NR_SENSE);
|
||||
if (error)
|
||||
dev_err_ratelimited(ts->dev, "failed to read %#02x: %d\n",
|
||||
AD7879_REG_XPLUS, error);
|
||||
else if (!ad7879_report(ts))
|
||||
mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/debugfs.h>
|
||||
@ -53,6 +54,11 @@
|
||||
#define M09_REGISTER_NUM_X 0x94
|
||||
#define M09_REGISTER_NUM_Y 0x95
|
||||
|
||||
#define EV_REGISTER_THRESHOLD 0x40
|
||||
#define EV_REGISTER_GAIN 0x41
|
||||
#define EV_REGISTER_OFFSET_Y 0x45
|
||||
#define EV_REGISTER_OFFSET_X 0x46
|
||||
|
||||
#define NO_REGISTER 0xff
|
||||
|
||||
#define WORK_REGISTER_OPMODE 0x3c
|
||||
@ -73,6 +79,7 @@ enum edt_ver {
|
||||
EDT_M06,
|
||||
EDT_M09,
|
||||
EDT_M12,
|
||||
EV_FT,
|
||||
GENERIC_FT,
|
||||
};
|
||||
|
||||
@ -81,6 +88,8 @@ struct edt_reg_addr {
|
||||
int reg_report_rate;
|
||||
int reg_gain;
|
||||
int reg_offset;
|
||||
int reg_offset_x;
|
||||
int reg_offset_y;
|
||||
int reg_num_x;
|
||||
int reg_num_y;
|
||||
};
|
||||
@ -106,6 +115,8 @@ struct edt_ft5x06_ts_data {
|
||||
int threshold;
|
||||
int gain;
|
||||
int offset;
|
||||
int offset_x;
|
||||
int offset_y;
|
||||
int report_rate;
|
||||
int max_support_points;
|
||||
|
||||
@ -190,6 +201,7 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
|
||||
|
||||
case EDT_M09:
|
||||
case EDT_M12:
|
||||
case EV_FT:
|
||||
case GENERIC_FT:
|
||||
cmd = 0x0;
|
||||
offset = 3;
|
||||
@ -242,6 +254,10 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
|
||||
|
||||
x = ((buf[0] << 8) | buf[1]) & 0x0fff;
|
||||
y = ((buf[2] << 8) | buf[3]) & 0x0fff;
|
||||
/* The FT5x26 send the y coordinate first */
|
||||
if (tsdata->version == EV_FT)
|
||||
swap(x, y);
|
||||
|
||||
id = (buf[2] >> 4) & 0x0f;
|
||||
down = type != TOUCH_EVENT_UP;
|
||||
|
||||
@ -275,8 +291,10 @@ static int edt_ft5x06_register_write(struct edt_ft5x06_ts_data *tsdata,
|
||||
wrbuf[3] = wrbuf[0] ^ wrbuf[1] ^ wrbuf[2];
|
||||
return edt_ft5x06_ts_readwrite(tsdata->client, 4,
|
||||
wrbuf, 0, NULL);
|
||||
/* fallthrough */
|
||||
case EDT_M09:
|
||||
case EDT_M12:
|
||||
case EV_FT:
|
||||
case GENERIC_FT:
|
||||
wrbuf[0] = addr;
|
||||
wrbuf[1] = value;
|
||||
@ -315,8 +333,10 @@ static int edt_ft5x06_register_read(struct edt_ft5x06_ts_data *tsdata,
|
||||
}
|
||||
break;
|
||||
|
||||
/* fallthrough */
|
||||
case EDT_M09:
|
||||
case EDT_M12:
|
||||
case EV_FT:
|
||||
case GENERIC_FT:
|
||||
wrbuf[0] = addr;
|
||||
error = edt_ft5x06_ts_readwrite(tsdata->client, 1,
|
||||
@ -339,9 +359,10 @@ struct edt_ft5x06_attribute {
|
||||
u8 limit_high;
|
||||
u8 addr_m06;
|
||||
u8 addr_m09;
|
||||
u8 addr_ev;
|
||||
};
|
||||
|
||||
#define EDT_ATTR(_field, _mode, _addr_m06, _addr_m09, \
|
||||
#define EDT_ATTR(_field, _mode, _addr_m06, _addr_m09, _addr_ev, \
|
||||
_limit_low, _limit_high) \
|
||||
struct edt_ft5x06_attribute edt_ft5x06_attr_##_field = { \
|
||||
.dattr = __ATTR(_field, _mode, \
|
||||
@ -350,6 +371,7 @@ struct edt_ft5x06_attribute {
|
||||
.field_offset = offsetof(struct edt_ft5x06_ts_data, _field), \
|
||||
.addr_m06 = _addr_m06, \
|
||||
.addr_m09 = _addr_m09, \
|
||||
.addr_ev = _addr_ev, \
|
||||
.limit_low = _limit_low, \
|
||||
.limit_high = _limit_high, \
|
||||
}
|
||||
@ -386,6 +408,10 @@ static ssize_t edt_ft5x06_setting_show(struct device *dev,
|
||||
addr = attr->addr_m09;
|
||||
break;
|
||||
|
||||
case EV_FT:
|
||||
addr = attr->addr_ev;
|
||||
break;
|
||||
|
||||
default:
|
||||
error = -ENODEV;
|
||||
goto out;
|
||||
@ -457,6 +483,10 @@ static ssize_t edt_ft5x06_setting_store(struct device *dev,
|
||||
addr = attr->addr_m09;
|
||||
break;
|
||||
|
||||
case EV_FT:
|
||||
addr = attr->addr_ev;
|
||||
break;
|
||||
|
||||
default:
|
||||
error = -ENODEV;
|
||||
goto out;
|
||||
@ -480,20 +510,28 @@ out:
|
||||
|
||||
/* m06, m09: range 0-31, m12: range 0-5 */
|
||||
static EDT_ATTR(gain, S_IWUSR | S_IRUGO, WORK_REGISTER_GAIN,
|
||||
M09_REGISTER_GAIN, 0, 31);
|
||||
M09_REGISTER_GAIN, EV_REGISTER_GAIN, 0, 31);
|
||||
/* m06, m09: range 0-31, m12: range 0-16 */
|
||||
static EDT_ATTR(offset, S_IWUSR | S_IRUGO, WORK_REGISTER_OFFSET,
|
||||
M09_REGISTER_OFFSET, 0, 31);
|
||||
M09_REGISTER_OFFSET, NO_REGISTER, 0, 31);
|
||||
/* m06, m09, m12: no supported, ev_ft: range 0-80 */
|
||||
static EDT_ATTR(offset_x, S_IWUSR | S_IRUGO, NO_REGISTER, NO_REGISTER,
|
||||
EV_REGISTER_OFFSET_X, 0, 80);
|
||||
/* m06, m09, m12: no supported, ev_ft: range 0-80 */
|
||||
static EDT_ATTR(offset_y, S_IWUSR | S_IRUGO, NO_REGISTER, NO_REGISTER,
|
||||
EV_REGISTER_OFFSET_Y, 0, 80);
|
||||
/* m06: range 20 to 80, m09: range 0 to 30, m12: range 1 to 255... */
|
||||
static EDT_ATTR(threshold, S_IWUSR | S_IRUGO, WORK_REGISTER_THRESHOLD,
|
||||
M09_REGISTER_THRESHOLD, 0, 255);
|
||||
M09_REGISTER_THRESHOLD, EV_REGISTER_THRESHOLD, 0, 255);
|
||||
/* m06: range 3 to 14, m12: (0x64: 100Hz) */
|
||||
static EDT_ATTR(report_rate, S_IWUSR | S_IRUGO, WORK_REGISTER_REPORT_RATE,
|
||||
NO_REGISTER, 0, 255);
|
||||
NO_REGISTER, NO_REGISTER, 0, 255);
|
||||
|
||||
static struct attribute *edt_ft5x06_attrs[] = {
|
||||
&edt_ft5x06_attr_gain.dattr.attr,
|
||||
&edt_ft5x06_attr_offset.dattr.attr,
|
||||
&edt_ft5x06_attr_offset_x.dattr.attr,
|
||||
&edt_ft5x06_attr_offset_y.dattr.attr,
|
||||
&edt_ft5x06_attr_threshold.dattr.attr,
|
||||
&edt_ft5x06_attr_report_rate.dattr.attr,
|
||||
NULL
|
||||
@ -605,8 +643,15 @@ static int edt_ft5x06_work_mode(struct edt_ft5x06_ts_data *tsdata)
|
||||
tsdata->threshold);
|
||||
edt_ft5x06_register_write(tsdata, reg_addr->reg_gain,
|
||||
tsdata->gain);
|
||||
edt_ft5x06_register_write(tsdata, reg_addr->reg_offset,
|
||||
tsdata->offset);
|
||||
if (reg_addr->reg_offset != NO_REGISTER)
|
||||
edt_ft5x06_register_write(tsdata, reg_addr->reg_offset,
|
||||
tsdata->offset);
|
||||
if (reg_addr->reg_offset_x != NO_REGISTER)
|
||||
edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_x,
|
||||
tsdata->offset_x);
|
||||
if (reg_addr->reg_offset_y != NO_REGISTER)
|
||||
edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_y,
|
||||
tsdata->offset_y);
|
||||
if (reg_addr->reg_report_rate != NO_REGISTER)
|
||||
edt_ft5x06_register_write(tsdata, reg_addr->reg_report_rate,
|
||||
tsdata->report_rate);
|
||||
@ -867,6 +912,16 @@ static int edt_ft5x06_ts_identify(struct i2c_client *client,
|
||||
case 0x5a: /* Solomon Goldentek Display */
|
||||
snprintf(model_name, EDT_NAME_LEN, "GKTW50SCED1R0");
|
||||
break;
|
||||
case 0x59: /* Evervision Display with FT5xx6 TS */
|
||||
tsdata->version = EV_FT;
|
||||
error = edt_ft5x06_ts_readwrite(client, 1, "\x53",
|
||||
1, rdbuf);
|
||||
if (error)
|
||||
return error;
|
||||
strlcpy(fw_version, rdbuf, 1);
|
||||
snprintf(model_name, EDT_NAME_LEN,
|
||||
"EVERVISION-FT5726NEi");
|
||||
break;
|
||||
default:
|
||||
snprintf(model_name, EDT_NAME_LEN,
|
||||
"generic ft5x06 (%02x)",
|
||||
@ -902,6 +957,18 @@ static void edt_ft5x06_ts_get_defaults(struct device *dev,
|
||||
edt_ft5x06_register_write(tsdata, reg_addr->reg_offset, val);
|
||||
tsdata->offset = val;
|
||||
}
|
||||
|
||||
error = device_property_read_u32(dev, "offset-x", &val);
|
||||
if (!error) {
|
||||
edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_x, val);
|
||||
tsdata->offset_x = val;
|
||||
}
|
||||
|
||||
error = device_property_read_u32(dev, "offset-y", &val);
|
||||
if (!error) {
|
||||
edt_ft5x06_register_write(tsdata, reg_addr->reg_offset_y, val);
|
||||
tsdata->offset_y = val;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@ -912,7 +979,15 @@ edt_ft5x06_ts_get_parameters(struct edt_ft5x06_ts_data *tsdata)
|
||||
tsdata->threshold = edt_ft5x06_register_read(tsdata,
|
||||
reg_addr->reg_threshold);
|
||||
tsdata->gain = edt_ft5x06_register_read(tsdata, reg_addr->reg_gain);
|
||||
tsdata->offset = edt_ft5x06_register_read(tsdata, reg_addr->reg_offset);
|
||||
if (reg_addr->reg_offset != NO_REGISTER)
|
||||
tsdata->offset =
|
||||
edt_ft5x06_register_read(tsdata, reg_addr->reg_offset);
|
||||
if (reg_addr->reg_offset_x != NO_REGISTER)
|
||||
tsdata->offset_x = edt_ft5x06_register_read(tsdata,
|
||||
reg_addr->reg_offset_x);
|
||||
if (reg_addr->reg_offset_y != NO_REGISTER)
|
||||
tsdata->offset_y = edt_ft5x06_register_read(tsdata,
|
||||
reg_addr->reg_offset_y);
|
||||
if (reg_addr->reg_report_rate != NO_REGISTER)
|
||||
tsdata->report_rate = edt_ft5x06_register_read(tsdata,
|
||||
reg_addr->reg_report_rate);
|
||||
@ -940,6 +1015,8 @@ edt_ft5x06_ts_set_regs(struct edt_ft5x06_ts_data *tsdata)
|
||||
reg_addr->reg_report_rate = WORK_REGISTER_REPORT_RATE;
|
||||
reg_addr->reg_gain = WORK_REGISTER_GAIN;
|
||||
reg_addr->reg_offset = WORK_REGISTER_OFFSET;
|
||||
reg_addr->reg_offset_x = NO_REGISTER;
|
||||
reg_addr->reg_offset_y = NO_REGISTER;
|
||||
reg_addr->reg_num_x = WORK_REGISTER_NUM_X;
|
||||
reg_addr->reg_num_y = WORK_REGISTER_NUM_Y;
|
||||
break;
|
||||
@ -950,15 +1027,30 @@ edt_ft5x06_ts_set_regs(struct edt_ft5x06_ts_data *tsdata)
|
||||
reg_addr->reg_report_rate = NO_REGISTER;
|
||||
reg_addr->reg_gain = M09_REGISTER_GAIN;
|
||||
reg_addr->reg_offset = M09_REGISTER_OFFSET;
|
||||
reg_addr->reg_offset_x = NO_REGISTER;
|
||||
reg_addr->reg_offset_y = NO_REGISTER;
|
||||
reg_addr->reg_num_x = M09_REGISTER_NUM_X;
|
||||
reg_addr->reg_num_y = M09_REGISTER_NUM_Y;
|
||||
break;
|
||||
|
||||
case EV_FT:
|
||||
reg_addr->reg_threshold = EV_REGISTER_THRESHOLD;
|
||||
reg_addr->reg_gain = EV_REGISTER_GAIN;
|
||||
reg_addr->reg_offset = NO_REGISTER;
|
||||
reg_addr->reg_offset_x = EV_REGISTER_OFFSET_X;
|
||||
reg_addr->reg_offset_y = EV_REGISTER_OFFSET_Y;
|
||||
reg_addr->reg_num_x = NO_REGISTER;
|
||||
reg_addr->reg_num_y = NO_REGISTER;
|
||||
reg_addr->reg_report_rate = NO_REGISTER;
|
||||
break;
|
||||
|
||||
case GENERIC_FT:
|
||||
/* this is a guesswork */
|
||||
reg_addr->reg_threshold = M09_REGISTER_THRESHOLD;
|
||||
reg_addr->reg_gain = M09_REGISTER_GAIN;
|
||||
reg_addr->reg_offset = M09_REGISTER_OFFSET;
|
||||
reg_addr->reg_offset_x = NO_REGISTER;
|
||||
reg_addr->reg_offset_y = NO_REGISTER;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1155,6 +1247,7 @@ static const struct edt_i2c_chip_data edt_ft6236_data = {
|
||||
static const struct i2c_device_id edt_ft5x06_ts_id[] = {
|
||||
{ .name = "edt-ft5x06", .driver_data = (long)&edt_ft5x06_data },
|
||||
{ .name = "edt-ft5506", .driver_data = (long)&edt_ft5506_data },
|
||||
{ .name = "ev-ft5726", .driver_data = (long)&edt_ft5506_data },
|
||||
/* Note no edt- prefix for compatibility with the ft6236.c driver */
|
||||
{ .name = "ft6236", .driver_data = (long)&edt_ft6236_data },
|
||||
{ /* sentinel */ }
|
||||
@ -1167,6 +1260,7 @@ static const struct of_device_id edt_ft5x06_of_match[] = {
|
||||
{ .compatible = "edt,edt-ft5306", .data = &edt_ft5x06_data },
|
||||
{ .compatible = "edt,edt-ft5406", .data = &edt_ft5x06_data },
|
||||
{ .compatible = "edt,edt-ft5506", .data = &edt_ft5506_data },
|
||||
{ .compatible = "evervision,ev-ft5726", .data = &edt_ft5506_data },
|
||||
/* Note focaltech vendor prefix for compatibility with ft6236.c */
|
||||
{ .compatible = "focaltech,ft6236", .data = &edt_ft6236_data },
|
||||
{ /* sentinel */ }
|
||||
|
@ -216,6 +216,7 @@ static const struct goodix_chip_data *goodix_get_chip_data(u16 id)
|
||||
{
|
||||
switch (id) {
|
||||
case 1151:
|
||||
case 5688:
|
||||
return >1x_chip_data;
|
||||
|
||||
case 911:
|
||||
@ -692,7 +693,9 @@ static int goodix_configure_dev(struct goodix_ts_data *ts)
|
||||
touchscreen_parse_properties(ts->input_dev, true, &ts->prop);
|
||||
|
||||
if (!ts->prop.max_x || !ts->prop.max_y || !ts->max_touch_num) {
|
||||
dev_err(&ts->client->dev, "Invalid config, using defaults\n");
|
||||
dev_err(&ts->client->dev,
|
||||
"Invalid config (%d, %d, %d), using defaults\n",
|
||||
ts->prop.max_x, ts->prop.max_y, ts->max_touch_num);
|
||||
ts->prop.max_x = GOODIX_MAX_WIDTH - 1;
|
||||
ts->prop.max_y = GOODIX_MAX_HEIGHT - 1;
|
||||
ts->max_touch_num = GOODIX_MAX_CONTACTS;
|
||||
@ -942,6 +945,7 @@ MODULE_DEVICE_TABLE(acpi, goodix_acpi_match);
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id goodix_of_match[] = {
|
||||
{ .compatible = "goodix,gt1151" },
|
||||
{ .compatible = "goodix,gt5688" },
|
||||
{ .compatible = "goodix,gt911" },
|
||||
{ .compatible = "goodix,gt9110" },
|
||||
{ .compatible = "goodix,gt912" },
|
||||
|
@ -4,11 +4,15 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/input/touchscreen.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/input/ili210x.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#define MAX_TOUCHES 2
|
||||
#define ILI210X_TOUCHES 2
|
||||
#define ILI251X_TOUCHES 10
|
||||
#define DEFAULT_POLL_PERIOD 20
|
||||
|
||||
/* Touchscreen commands */
|
||||
@ -17,41 +21,32 @@
|
||||
#define REG_FIRMWARE_VERSION 0x40
|
||||
#define REG_CALIBRATE 0xcc
|
||||
|
||||
struct finger {
|
||||
u8 x_low;
|
||||
u8 x_high;
|
||||
u8 y_low;
|
||||
u8 y_high;
|
||||
} __packed;
|
||||
|
||||
struct touchdata {
|
||||
u8 status;
|
||||
struct finger finger[MAX_TOUCHES];
|
||||
} __packed;
|
||||
|
||||
struct panel_info {
|
||||
struct finger finger_max;
|
||||
u8 xchannel_num;
|
||||
u8 ychannel_num;
|
||||
} __packed;
|
||||
|
||||
struct firmware_version {
|
||||
u8 id;
|
||||
u8 major;
|
||||
u8 minor;
|
||||
} __packed;
|
||||
|
||||
enum ili2xxx_model {
|
||||
MODEL_ILI210X,
|
||||
MODEL_ILI251X,
|
||||
};
|
||||
|
||||
struct ili210x {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input;
|
||||
bool (*get_pendown_state)(void);
|
||||
unsigned int poll_period;
|
||||
struct delayed_work dwork;
|
||||
struct gpio_desc *reset_gpio;
|
||||
struct touchscreen_properties prop;
|
||||
enum ili2xxx_model model;
|
||||
unsigned int max_touches;
|
||||
};
|
||||
|
||||
static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct ili210x *priv = i2c_get_clientdata(client);
|
||||
struct i2c_msg msg[2] = {
|
||||
{
|
||||
.addr = client->addr,
|
||||
@ -67,7 +62,38 @@ static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf,
|
||||
}
|
||||
};
|
||||
|
||||
if (i2c_transfer(client->adapter, msg, 2) != 2) {
|
||||
if (priv->model == MODEL_ILI251X) {
|
||||
if (i2c_transfer(client->adapter, msg, 1) != 1) {
|
||||
dev_err(&client->dev, "i2c transfer failed\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
usleep_range(5000, 5500);
|
||||
|
||||
if (i2c_transfer(client->adapter, msg + 1, 1) != 1) {
|
||||
dev_err(&client->dev, "i2c transfer failed\n");
|
||||
return -EIO;
|
||||
}
|
||||
} else {
|
||||
if (i2c_transfer(client->adapter, msg, 2) != 2) {
|
||||
dev_err(&client->dev, "i2c transfer failed\n");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ili210x_read(struct i2c_client *client, void *buf, size_t len)
|
||||
{
|
||||
struct i2c_msg msg = {
|
||||
.addr = client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = len,
|
||||
.buf = buf,
|
||||
};
|
||||
|
||||
if (i2c_transfer(client->adapter, &msg, 1) != 1) {
|
||||
dev_err(&client->dev, "i2c transfer failed\n");
|
||||
return -EIO;
|
||||
}
|
||||
@ -75,42 +101,72 @@ static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ili210x_report_events(struct input_dev *input,
|
||||
const struct touchdata *touchdata)
|
||||
static bool ili210x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata,
|
||||
unsigned int finger,
|
||||
unsigned int *x, unsigned int *y)
|
||||
{
|
||||
if (finger >= ILI210X_TOUCHES)
|
||||
return false;
|
||||
|
||||
if (touchdata[0] & BIT(finger))
|
||||
return false;
|
||||
|
||||
*x = get_unaligned_be16(touchdata + 1 + (finger * 4) + 0);
|
||||
*y = get_unaligned_be16(touchdata + 1 + (finger * 4) + 2);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ili251x_touchdata_to_coords(struct ili210x *priv, u8 *touchdata,
|
||||
unsigned int finger,
|
||||
unsigned int *x, unsigned int *y)
|
||||
{
|
||||
if (finger >= ILI251X_TOUCHES)
|
||||
return false;
|
||||
|
||||
*x = get_unaligned_be16(touchdata + 1 + (finger * 5) + 0);
|
||||
if (!(*x & BIT(15))) /* Touch indication */
|
||||
return false;
|
||||
|
||||
*x &= 0x3fff;
|
||||
*y = get_unaligned_be16(touchdata + 1 + (finger * 5) + 2);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ili210x_report_events(struct ili210x *priv, u8 *touchdata)
|
||||
{
|
||||
struct input_dev *input = priv->input;
|
||||
int i;
|
||||
bool touch;
|
||||
unsigned int x, y;
|
||||
const struct finger *finger;
|
||||
bool contact = false, touch = false;
|
||||
unsigned int x = 0, y = 0;
|
||||
|
||||
for (i = 0; i < MAX_TOUCHES; i++) {
|
||||
input_mt_slot(input, i);
|
||||
|
||||
finger = &touchdata->finger[i];
|
||||
|
||||
touch = touchdata->status & (1 << i);
|
||||
input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
|
||||
if (touch) {
|
||||
x = finger->x_low | (finger->x_high << 8);
|
||||
y = finger->y_low | (finger->y_high << 8);
|
||||
|
||||
input_report_abs(input, ABS_MT_POSITION_X, x);
|
||||
input_report_abs(input, ABS_MT_POSITION_Y, y);
|
||||
for (i = 0; i < priv->max_touches; i++) {
|
||||
if (priv->model == MODEL_ILI210X) {
|
||||
touch = ili210x_touchdata_to_coords(priv, touchdata,
|
||||
i, &x, &y);
|
||||
} else if (priv->model == MODEL_ILI251X) {
|
||||
touch = ili251x_touchdata_to_coords(priv, touchdata,
|
||||
i, &x, &y);
|
||||
if (touch)
|
||||
contact = true;
|
||||
}
|
||||
|
||||
input_mt_slot(input, i);
|
||||
input_mt_report_slot_state(input, MT_TOOL_FINGER, touch);
|
||||
if (!touch)
|
||||
continue;
|
||||
touchscreen_report_pos(input, &priv->prop, x, y,
|
||||
true);
|
||||
}
|
||||
|
||||
input_mt_report_pointer_emulation(input, false);
|
||||
input_sync(input);
|
||||
}
|
||||
|
||||
static bool get_pendown_state(const struct ili210x *priv)
|
||||
{
|
||||
bool state = false;
|
||||
if (priv->model == MODEL_ILI210X)
|
||||
contact = touchdata[0] & 0xf3;
|
||||
|
||||
if (priv->get_pendown_state)
|
||||
state = priv->get_pendown_state();
|
||||
|
||||
return state;
|
||||
return contact;
|
||||
}
|
||||
|
||||
static void ili210x_work(struct work_struct *work)
|
||||
@ -118,20 +174,29 @@ static void ili210x_work(struct work_struct *work)
|
||||
struct ili210x *priv = container_of(work, struct ili210x,
|
||||
dwork.work);
|
||||
struct i2c_client *client = priv->client;
|
||||
struct touchdata touchdata;
|
||||
int error;
|
||||
u8 touchdata[64] = { 0 };
|
||||
bool touch;
|
||||
int error = -EINVAL;
|
||||
|
||||
if (priv->model == MODEL_ILI210X) {
|
||||
error = ili210x_read_reg(client, REG_TOUCHDATA,
|
||||
touchdata, sizeof(touchdata));
|
||||
} else if (priv->model == MODEL_ILI251X) {
|
||||
error = ili210x_read_reg(client, REG_TOUCHDATA,
|
||||
touchdata, 31);
|
||||
if (!error && touchdata[0] == 2)
|
||||
error = ili210x_read(client, &touchdata[31], 20);
|
||||
}
|
||||
|
||||
error = ili210x_read_reg(client, REG_TOUCHDATA,
|
||||
&touchdata, sizeof(touchdata));
|
||||
if (error) {
|
||||
dev_err(&client->dev,
|
||||
"Unable to get touchdata, err = %d\n", error);
|
||||
return;
|
||||
}
|
||||
|
||||
ili210x_report_events(priv->input, &touchdata);
|
||||
touch = ili210x_report_events(priv, touchdata);
|
||||
|
||||
if ((touchdata.status & 0xf3) || get_pendown_state(priv))
|
||||
if (touch)
|
||||
schedule_delayed_work(&priv->dwork,
|
||||
msecs_to_jiffies(priv->poll_period));
|
||||
}
|
||||
@ -180,30 +245,76 @@ static const struct attribute_group ili210x_attr_group = {
|
||||
.attrs = ili210x_attributes,
|
||||
};
|
||||
|
||||
static void ili210x_power_down(void *data)
|
||||
{
|
||||
struct gpio_desc *reset_gpio = data;
|
||||
|
||||
gpiod_set_value_cansleep(reset_gpio, 1);
|
||||
}
|
||||
|
||||
static void ili210x_cancel_work(void *data)
|
||||
{
|
||||
struct ili210x *priv = data;
|
||||
|
||||
cancel_delayed_work_sync(&priv->dwork);
|
||||
}
|
||||
|
||||
static int ili210x_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
const struct ili210x_platform_data *pdata = dev_get_platdata(dev);
|
||||
struct ili210x *priv;
|
||||
struct gpio_desc *reset_gpio;
|
||||
struct input_dev *input;
|
||||
struct panel_info panel;
|
||||
struct firmware_version firmware;
|
||||
int xmax, ymax;
|
||||
enum ili2xxx_model model;
|
||||
int error;
|
||||
|
||||
dev_dbg(dev, "Probing for ILI210X I2C Touschreen driver");
|
||||
model = (enum ili2xxx_model)id->driver_data;
|
||||
|
||||
if (!pdata) {
|
||||
dev_err(dev, "No platform data!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
dev_dbg(dev, "Probing for ILI210X I2C Touschreen driver");
|
||||
|
||||
if (client->irq <= 0) {
|
||||
dev_err(dev, "No IRQ!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(reset_gpio))
|
||||
return PTR_ERR(reset_gpio);
|
||||
|
||||
if (reset_gpio) {
|
||||
error = devm_add_action_or_reset(dev, ili210x_power_down,
|
||||
reset_gpio);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
usleep_range(50, 100);
|
||||
gpiod_set_value_cansleep(reset_gpio, 0);
|
||||
msleep(100);
|
||||
}
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
input = devm_input_allocate_device(dev);
|
||||
if (!input)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->client = client;
|
||||
priv->input = input;
|
||||
priv->poll_period = DEFAULT_POLL_PERIOD;
|
||||
INIT_DELAYED_WORK(&priv->dwork, ili210x_work);
|
||||
priv->reset_gpio = reset_gpio;
|
||||
priv->model = model;
|
||||
if (model == MODEL_ILI210X)
|
||||
priv->max_touches = ILI210X_TOUCHES;
|
||||
if (model == MODEL_ILI251X)
|
||||
priv->max_touches = ILI251X_TOUCHES;
|
||||
|
||||
i2c_set_clientdata(client, priv);
|
||||
|
||||
/* Get firmware version */
|
||||
error = ili210x_read_reg(client, REG_FIRMWARE_VERSION,
|
||||
&firmware, sizeof(firmware));
|
||||
@ -213,70 +324,40 @@ static int ili210x_i2c_probe(struct i2c_client *client,
|
||||
return error;
|
||||
}
|
||||
|
||||
/* get panel info */
|
||||
error = ili210x_read_reg(client, REG_PANEL_INFO, &panel, sizeof(panel));
|
||||
if (error) {
|
||||
dev_err(dev, "Failed to get panel information, err: %d\n",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
|
||||
xmax = panel.finger_max.x_low | (panel.finger_max.x_high << 8);
|
||||
ymax = panel.finger_max.y_low | (panel.finger_max.y_high << 8);
|
||||
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
input = input_allocate_device();
|
||||
if (!priv || !input) {
|
||||
error = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
priv->client = client;
|
||||
priv->input = input;
|
||||
priv->get_pendown_state = pdata->get_pendown_state;
|
||||
priv->poll_period = pdata->poll_period ? : DEFAULT_POLL_PERIOD;
|
||||
INIT_DELAYED_WORK(&priv->dwork, ili210x_work);
|
||||
|
||||
/* Setup input device */
|
||||
input->name = "ILI210x Touchscreen";
|
||||
input->id.bustype = BUS_I2C;
|
||||
input->dev.parent = dev;
|
||||
|
||||
__set_bit(EV_SYN, input->evbit);
|
||||
__set_bit(EV_KEY, input->evbit);
|
||||
__set_bit(EV_ABS, input->evbit);
|
||||
__set_bit(BTN_TOUCH, input->keybit);
|
||||
|
||||
/* Single touch */
|
||||
input_set_abs_params(input, ABS_X, 0, xmax, 0, 0);
|
||||
input_set_abs_params(input, ABS_Y, 0, ymax, 0, 0);
|
||||
|
||||
/* Multi touch */
|
||||
input_mt_init_slots(input, MAX_TOUCHES, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_X, 0, xmax, 0, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ymax, 0, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_X, 0, 0xffff, 0, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_Y, 0, 0xffff, 0, 0);
|
||||
touchscreen_parse_properties(input, true, &priv->prop);
|
||||
input_mt_init_slots(input, priv->max_touches, INPUT_MT_DIRECT);
|
||||
|
||||
i2c_set_clientdata(client, priv);
|
||||
error = devm_add_action(dev, ili210x_cancel_work, priv);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = request_irq(client->irq, ili210x_irq, pdata->irq_flags,
|
||||
client->name, priv);
|
||||
error = devm_request_irq(dev, client->irq, ili210x_irq, 0,
|
||||
client->name, priv);
|
||||
if (error) {
|
||||
dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n",
|
||||
error);
|
||||
goto err_free_mem;
|
||||
return error;
|
||||
}
|
||||
|
||||
error = sysfs_create_group(&dev->kobj, &ili210x_attr_group);
|
||||
error = devm_device_add_group(dev, &ili210x_attr_group);
|
||||
if (error) {
|
||||
dev_err(dev, "Unable to create sysfs attributes, err: %d\n",
|
||||
error);
|
||||
goto err_free_irq;
|
||||
return error;
|
||||
}
|
||||
|
||||
error = input_register_device(priv->input);
|
||||
if (error) {
|
||||
dev_err(dev, "Cannot register input device, err: %d\n", error);
|
||||
goto err_remove_sysfs;
|
||||
return error;
|
||||
}
|
||||
|
||||
device_init_wakeup(dev, 1);
|
||||
@ -286,28 +367,6 @@ static int ili210x_i2c_probe(struct i2c_client *client,
|
||||
client->irq, firmware.id, firmware.major, firmware.minor);
|
||||
|
||||
return 0;
|
||||
|
||||
err_remove_sysfs:
|
||||
sysfs_remove_group(&dev->kobj, &ili210x_attr_group);
|
||||
err_free_irq:
|
||||
free_irq(client->irq, priv);
|
||||
err_free_mem:
|
||||
input_free_device(input);
|
||||
kfree(priv);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int ili210x_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
struct ili210x *priv = i2c_get_clientdata(client);
|
||||
|
||||
sysfs_remove_group(&client->dev.kobj, &ili210x_attr_group);
|
||||
free_irq(priv->client->irq, priv);
|
||||
cancel_delayed_work_sync(&priv->dwork);
|
||||
input_unregister_device(priv->input);
|
||||
kfree(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused ili210x_i2c_suspend(struct device *dev)
|
||||
@ -334,19 +393,27 @@ static SIMPLE_DEV_PM_OPS(ili210x_i2c_pm,
|
||||
ili210x_i2c_suspend, ili210x_i2c_resume);
|
||||
|
||||
static const struct i2c_device_id ili210x_i2c_id[] = {
|
||||
{ "ili210x", 0 },
|
||||
{ "ili210x", MODEL_ILI210X },
|
||||
{ "ili251x", MODEL_ILI251X },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ili210x_i2c_id);
|
||||
|
||||
static const struct of_device_id ili210x_dt_ids[] = {
|
||||
{ .compatible = "ilitek,ili210x", .data = (void *)MODEL_ILI210X },
|
||||
{ .compatible = "ilitek,ili251x", .data = (void *)MODEL_ILI251X },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ili210x_dt_ids);
|
||||
|
||||
static struct i2c_driver ili210x_ts_driver = {
|
||||
.driver = {
|
||||
.name = "ili210x_i2c",
|
||||
.pm = &ili210x_i2c_pm,
|
||||
.of_match_table = ili210x_dt_ids,
|
||||
},
|
||||
.id_table = ili210x_i2c_id,
|
||||
.probe = ili210x_i2c_probe,
|
||||
.remove = ili210x_i2c_remove,
|
||||
};
|
||||
|
||||
module_i2c_driver(ili210x_ts_driver);
|
||||
|
@ -11,25 +11,19 @@
|
||||
*/
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/pm_qos.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/input/touchscreen.h>
|
||||
|
||||
#define ST1232_TS_NAME "st1232-ts"
|
||||
|
||||
#define MIN_X 0x00
|
||||
#define MIN_Y 0x00
|
||||
#define MAX_X 0x31f /* (800 - 1) */
|
||||
#define MAX_Y 0x1df /* (480 - 1) */
|
||||
#define MAX_AREA 0xff
|
||||
#define MAX_FINGERS 2
|
||||
#define ST1633_TS_NAME "st1633-ts"
|
||||
|
||||
struct st1232_ts_finger {
|
||||
u16 x;
|
||||
@ -38,12 +32,25 @@ struct st1232_ts_finger {
|
||||
bool is_valid;
|
||||
};
|
||||
|
||||
struct st_chip_info {
|
||||
bool have_z;
|
||||
u16 max_x;
|
||||
u16 max_y;
|
||||
u16 max_area;
|
||||
u16 max_fingers;
|
||||
u8 start_reg;
|
||||
};
|
||||
|
||||
struct st1232_ts_data {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input_dev;
|
||||
struct st1232_ts_finger finger[MAX_FINGERS];
|
||||
struct touchscreen_properties prop;
|
||||
struct dev_pm_qos_request low_latency_req;
|
||||
int reset_gpio;
|
||||
struct gpio_desc *reset_gpio;
|
||||
const struct st_chip_info *chip_info;
|
||||
int read_buf_len;
|
||||
u8 *read_buf;
|
||||
struct st1232_ts_finger *finger;
|
||||
};
|
||||
|
||||
static int st1232_ts_read_data(struct st1232_ts_data *ts)
|
||||
@ -52,40 +59,35 @@ static int st1232_ts_read_data(struct st1232_ts_data *ts)
|
||||
struct i2c_client *client = ts->client;
|
||||
struct i2c_msg msg[2];
|
||||
int error;
|
||||
u8 start_reg;
|
||||
u8 buf[10];
|
||||
int i, y;
|
||||
u8 start_reg = ts->chip_info->start_reg;
|
||||
u8 *buf = ts->read_buf;
|
||||
|
||||
/* read touchscreen data from ST1232 */
|
||||
/* read touchscreen data */
|
||||
msg[0].addr = client->addr;
|
||||
msg[0].flags = 0;
|
||||
msg[0].len = 1;
|
||||
msg[0].buf = &start_reg;
|
||||
start_reg = 0x10;
|
||||
|
||||
msg[1].addr = ts->client->addr;
|
||||
msg[1].flags = I2C_M_RD;
|
||||
msg[1].len = sizeof(buf);
|
||||
msg[1].len = ts->read_buf_len;
|
||||
msg[1].buf = buf;
|
||||
|
||||
error = i2c_transfer(client->adapter, msg, 2);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
/* get "valid" bits */
|
||||
finger[0].is_valid = buf[2] >> 7;
|
||||
finger[1].is_valid = buf[5] >> 7;
|
||||
for (i = 0, y = 0; i < ts->chip_info->max_fingers; i++, y += 3) {
|
||||
finger[i].is_valid = buf[i + y] >> 7;
|
||||
if (finger[i].is_valid) {
|
||||
finger[i].x = ((buf[i + y] & 0x0070) << 4) | buf[i + 1];
|
||||
finger[i].y = ((buf[i + y] & 0x0007) << 8) | buf[i + 2];
|
||||
|
||||
/* get xy coordinate */
|
||||
if (finger[0].is_valid) {
|
||||
finger[0].x = ((buf[2] & 0x0070) << 4) | buf[3];
|
||||
finger[0].y = ((buf[2] & 0x0007) << 8) | buf[4];
|
||||
finger[0].t = buf[8];
|
||||
}
|
||||
|
||||
if (finger[1].is_valid) {
|
||||
finger[1].x = ((buf[5] & 0x0070) << 4) | buf[6];
|
||||
finger[1].y = ((buf[5] & 0x0007) << 8) | buf[7];
|
||||
finger[1].t = buf[9];
|
||||
/* st1232 includes a z-axis / touch strength */
|
||||
if (ts->chip_info->have_z)
|
||||
finger[i].t = buf[i + 6];
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -104,13 +106,16 @@ static irqreturn_t st1232_ts_irq_handler(int irq, void *dev_id)
|
||||
goto end;
|
||||
|
||||
/* multi touch protocol */
|
||||
for (i = 0; i < MAX_FINGERS; i++) {
|
||||
for (i = 0; i < ts->chip_info->max_fingers; i++) {
|
||||
if (!finger[i].is_valid)
|
||||
continue;
|
||||
|
||||
input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, finger[i].t);
|
||||
input_report_abs(input_dev, ABS_MT_POSITION_X, finger[i].x);
|
||||
input_report_abs(input_dev, ABS_MT_POSITION_Y, finger[i].y);
|
||||
if (ts->chip_info->have_z)
|
||||
input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR,
|
||||
finger[i].t);
|
||||
|
||||
touchscreen_report_pos(input_dev, &ts->prop,
|
||||
finger[i].x, finger[i].y, true);
|
||||
input_mt_sync(input_dev);
|
||||
count++;
|
||||
}
|
||||
@ -138,17 +143,45 @@ end:
|
||||
|
||||
static void st1232_ts_power(struct st1232_ts_data *ts, bool poweron)
|
||||
{
|
||||
if (gpio_is_valid(ts->reset_gpio))
|
||||
gpio_direction_output(ts->reset_gpio, poweron);
|
||||
if (ts->reset_gpio)
|
||||
gpiod_set_value_cansleep(ts->reset_gpio, !poweron);
|
||||
}
|
||||
|
||||
static const struct st_chip_info st1232_chip_info = {
|
||||
.have_z = true,
|
||||
.max_x = 0x31f, /* 800 - 1 */
|
||||
.max_y = 0x1df, /* 480 -1 */
|
||||
.max_area = 0xff,
|
||||
.max_fingers = 2,
|
||||
.start_reg = 0x12,
|
||||
};
|
||||
|
||||
static const struct st_chip_info st1633_chip_info = {
|
||||
.have_z = false,
|
||||
.max_x = 0x13f, /* 320 - 1 */
|
||||
.max_y = 0x1df, /* 480 -1 */
|
||||
.max_area = 0x00,
|
||||
.max_fingers = 5,
|
||||
.start_reg = 0x12,
|
||||
};
|
||||
|
||||
static int st1232_ts_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
const struct st_chip_info *match;
|
||||
struct st1232_ts_data *ts;
|
||||
struct st1232_ts_finger *finger;
|
||||
struct input_dev *input_dev;
|
||||
int error;
|
||||
|
||||
match = device_get_match_data(&client->dev);
|
||||
if (!match && id)
|
||||
match = (const void *)id->driver_data;
|
||||
if (!match) {
|
||||
dev_err(&client->dev, "unknown device model\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
||||
dev_err(&client->dev, "need I2C_FUNC_I2C\n");
|
||||
return -EIO;
|
||||
@ -163,6 +196,19 @@ static int st1232_ts_probe(struct i2c_client *client,
|
||||
if (!ts)
|
||||
return -ENOMEM;
|
||||
|
||||
ts->chip_info = match;
|
||||
ts->finger = devm_kcalloc(&client->dev,
|
||||
ts->chip_info->max_fingers, sizeof(*finger),
|
||||
GFP_KERNEL);
|
||||
if (!ts->finger)
|
||||
return -ENOMEM;
|
||||
|
||||
/* allocate a buffer according to the number of registers to read */
|
||||
ts->read_buf_len = ts->chip_info->max_fingers * 4;
|
||||
ts->read_buf = devm_kzalloc(&client->dev, ts->read_buf_len, GFP_KERNEL);
|
||||
if (!ts->read_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
input_dev = devm_input_allocate_device(&client->dev);
|
||||
if (!input_dev)
|
||||
return -ENOMEM;
|
||||
@ -170,15 +216,13 @@ static int st1232_ts_probe(struct i2c_client *client,
|
||||
ts->client = client;
|
||||
ts->input_dev = input_dev;
|
||||
|
||||
ts->reset_gpio = of_get_gpio(client->dev.of_node, 0);
|
||||
if (gpio_is_valid(ts->reset_gpio)) {
|
||||
error = devm_gpio_request(&client->dev, ts->reset_gpio, NULL);
|
||||
if (error) {
|
||||
dev_err(&client->dev,
|
||||
"Unable to request GPIO pin %d.\n",
|
||||
ts->reset_gpio);
|
||||
return error;
|
||||
}
|
||||
ts->reset_gpio = devm_gpiod_get_optional(&client->dev, NULL,
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(ts->reset_gpio)) {
|
||||
error = PTR_ERR(ts->reset_gpio);
|
||||
dev_err(&client->dev, "Unable to request GPIO pin: %d.\n",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
|
||||
st1232_ts_power(ts, true);
|
||||
@ -192,9 +236,16 @@ static int st1232_ts_probe(struct i2c_client *client,
|
||||
__set_bit(EV_KEY, input_dev->evbit);
|
||||
__set_bit(EV_ABS, input_dev->evbit);
|
||||
|
||||
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, MAX_AREA, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_MT_POSITION_X, MIN_X, MAX_X, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_MT_POSITION_Y, MIN_Y, MAX_Y, 0, 0);
|
||||
if (ts->chip_info->have_z)
|
||||
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0,
|
||||
ts->chip_info->max_area, 0, 0);
|
||||
|
||||
input_set_abs_params(input_dev, ABS_MT_POSITION_X,
|
||||
0, ts->chip_info->max_x, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
|
||||
0, ts->chip_info->max_y, 0, 0);
|
||||
|
||||
touchscreen_parse_properties(input_dev, true, &ts->prop);
|
||||
|
||||
error = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
NULL, st1232_ts_irq_handler,
|
||||
@ -261,13 +312,15 @@ static SIMPLE_DEV_PM_OPS(st1232_ts_pm_ops,
|
||||
st1232_ts_suspend, st1232_ts_resume);
|
||||
|
||||
static const struct i2c_device_id st1232_ts_id[] = {
|
||||
{ ST1232_TS_NAME, 0 },
|
||||
{ ST1232_TS_NAME, (unsigned long)&st1232_chip_info },
|
||||
{ ST1633_TS_NAME, (unsigned long)&st1633_chip_info },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, st1232_ts_id);
|
||||
|
||||
static const struct of_device_id st1232_ts_dt_ids[] = {
|
||||
{ .compatible = "sitronix,st1232", },
|
||||
{ .compatible = "sitronix,st1232", .data = &st1232_chip_info },
|
||||
{ .compatible = "sitronix,st1633", .data = &st1633_chip_info },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, st1232_ts_dt_ids);
|
||||
@ -286,5 +339,6 @@ static struct i2c_driver st1232_ts_driver = {
|
||||
module_i2c_driver(st1232_ts_driver);
|
||||
|
||||
MODULE_AUTHOR("Tony SIM <chinyeow.sim.xt@renesas.com>");
|
||||
MODULE_AUTHOR("Martin Kepplinger <martin.kepplinger@ginzinger.com>");
|
||||
MODULE_DESCRIPTION("SITRONIX ST1232 Touchscreen Controller Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -106,27 +106,29 @@ struct stmfts_data {
|
||||
bool running;
|
||||
};
|
||||
|
||||
static void stmfts_brightness_set(struct led_classdev *led_cdev,
|
||||
static int stmfts_brightness_set(struct led_classdev *led_cdev,
|
||||
enum led_brightness value)
|
||||
{
|
||||
struct stmfts_data *sdata = container_of(led_cdev,
|
||||
struct stmfts_data, led_cdev);
|
||||
int err;
|
||||
|
||||
if (value == sdata->led_status || !sdata->ledvdd)
|
||||
return;
|
||||
|
||||
if (!value) {
|
||||
regulator_disable(sdata->ledvdd);
|
||||
} else {
|
||||
err = regulator_enable(sdata->ledvdd);
|
||||
if (err)
|
||||
dev_warn(&sdata->client->dev,
|
||||
"failed to disable ledvdd regulator: %d\n",
|
||||
err);
|
||||
if (value != sdata->led_status && sdata->ledvdd) {
|
||||
if (!value) {
|
||||
regulator_disable(sdata->ledvdd);
|
||||
} else {
|
||||
err = regulator_enable(sdata->ledvdd);
|
||||
if (err) {
|
||||
dev_warn(&sdata->client->dev,
|
||||
"failed to disable ledvdd regulator: %d\n",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
sdata->led_status = value;
|
||||
}
|
||||
|
||||
sdata->led_status = value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum led_brightness stmfts_brightness_get(struct led_classdev *led_cdev)
|
||||
@ -608,7 +610,7 @@ static int stmfts_enable_led(struct stmfts_data *sdata)
|
||||
sdata->led_cdev.name = STMFTS_DEV_NAME;
|
||||
sdata->led_cdev.max_brightness = LED_ON;
|
||||
sdata->led_cdev.brightness = LED_OFF;
|
||||
sdata->led_cdev.brightness_set = stmfts_brightness_set;
|
||||
sdata->led_cdev.brightness_set_blocking = stmfts_brightness_set;
|
||||
sdata->led_cdev.brightness_get = stmfts_brightness_get;
|
||||
|
||||
err = devm_led_classdev_register(&sdata->client->dev, &sdata->led_cdev);
|
||||
|
@ -27,12 +27,16 @@
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/input.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/touchscreen.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
/* register addresses */
|
||||
#define I2C_REG_TOUCH0 0x00
|
||||
@ -42,25 +46,28 @@
|
||||
#define I2C_REG_IRQSRC 0x23
|
||||
#define I2C_REG_SOFTRESET 0x3f
|
||||
|
||||
#define I2C_REG_SX8650_STAT 0x05
|
||||
#define SX8650_STAT_CONVIRQ BIT(7)
|
||||
|
||||
/* commands */
|
||||
#define CMD_READ_REGISTER 0x40
|
||||
#define CMD_MANUAL 0xc0
|
||||
#define CMD_PENTRG 0xe0
|
||||
|
||||
/* value for I2C_REG_SOFTRESET */
|
||||
#define SOFTRESET_VALUE 0xde
|
||||
|
||||
/* bits for I2C_REG_IRQSRC */
|
||||
#define IRQ_PENTOUCH_TOUCHCONVDONE 0x08
|
||||
#define IRQ_PENRELEASE 0x04
|
||||
#define IRQ_PENTOUCH_TOUCHCONVDONE BIT(3)
|
||||
#define IRQ_PENRELEASE BIT(2)
|
||||
|
||||
/* bits for RegTouch1 */
|
||||
#define CONDIRQ 0x20
|
||||
#define RPDNT_100K 0x00
|
||||
#define FILT_7SA 0x03
|
||||
|
||||
/* bits for I2C_REG_CHANMASK */
|
||||
#define CONV_X 0x80
|
||||
#define CONV_Y 0x40
|
||||
#define CONV_X BIT(7)
|
||||
#define CONV_Y BIT(6)
|
||||
|
||||
/* coordinates rate: higher nibble of CTRL0 register */
|
||||
#define RATE_MANUAL 0x00
|
||||
@ -69,13 +76,122 @@
|
||||
/* power delay: lower nibble of CTRL0 register */
|
||||
#define POWDLY_1_1MS 0x0b
|
||||
|
||||
/* for sx8650, as we have no pen release IRQ there: timeout in ns following the
|
||||
* last PENIRQ after which we assume the pen is lifted.
|
||||
*/
|
||||
#define SX8650_PENIRQ_TIMEOUT msecs_to_jiffies(10)
|
||||
|
||||
#define MAX_12BIT ((1 << 12) - 1)
|
||||
#define MAX_I2C_READ_LEN 10 /* see datasheet section 5.1.5 */
|
||||
|
||||
/* channel definition */
|
||||
#define CH_X 0x00
|
||||
#define CH_Y 0x01
|
||||
|
||||
struct sx865x_data {
|
||||
u8 cmd_manual;
|
||||
u8 chan_mask;
|
||||
bool has_irq_penrelease;
|
||||
bool has_reg_irqmask;
|
||||
irq_handler_t irqh;
|
||||
};
|
||||
|
||||
struct sx8654 {
|
||||
struct input_dev *input;
|
||||
struct i2c_client *client;
|
||||
struct gpio_desc *gpio_reset;
|
||||
|
||||
spinlock_t lock; /* for input reporting from irq/timer */
|
||||
struct timer_list timer;
|
||||
|
||||
struct touchscreen_properties props;
|
||||
|
||||
const struct sx865x_data *data;
|
||||
};
|
||||
|
||||
static inline void sx865x_penrelease(struct sx8654 *ts)
|
||||
{
|
||||
struct input_dev *input_dev = ts->input;
|
||||
|
||||
input_report_key(input_dev, BTN_TOUCH, 0);
|
||||
input_sync(input_dev);
|
||||
}
|
||||
|
||||
static void sx865x_penrelease_timer_handler(struct timer_list *t)
|
||||
{
|
||||
struct sx8654 *ts = from_timer(ts, t, timer);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ts->lock, flags);
|
||||
sx865x_penrelease(ts);
|
||||
spin_unlock_irqrestore(&ts->lock, flags);
|
||||
dev_dbg(&ts->client->dev, "penrelease by timer\n");
|
||||
}
|
||||
|
||||
static irqreturn_t sx8650_irq(int irq, void *handle)
|
||||
{
|
||||
struct sx8654 *ts = handle;
|
||||
struct device *dev = &ts->client->dev;
|
||||
int len, i;
|
||||
unsigned long flags;
|
||||
u8 stat;
|
||||
u16 x, y;
|
||||
u16 ch;
|
||||
u16 chdata;
|
||||
__be16 data[MAX_I2C_READ_LEN / sizeof(__be16)];
|
||||
u8 nchan = hweight32(ts->data->chan_mask);
|
||||
u8 readlen = nchan * sizeof(*data);
|
||||
|
||||
stat = i2c_smbus_read_byte_data(ts->client, CMD_READ_REGISTER
|
||||
| I2C_REG_SX8650_STAT);
|
||||
|
||||
if (!(stat & SX8650_STAT_CONVIRQ)) {
|
||||
dev_dbg(dev, "%s ignore stat [0x%02x]", __func__, stat);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
len = i2c_master_recv(ts->client, (u8 *)data, readlen);
|
||||
if (len != readlen) {
|
||||
dev_dbg(dev, "ignore short recv (%d)\n", len);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&ts->lock, flags);
|
||||
|
||||
x = 0;
|
||||
y = 0;
|
||||
for (i = 0; i < nchan; i++) {
|
||||
chdata = be16_to_cpu(data[i]);
|
||||
|
||||
if (unlikely(chdata == 0xFFFF)) {
|
||||
dev_dbg(dev, "invalid qualified data @ %d\n", i);
|
||||
continue;
|
||||
} else if (unlikely(chdata & 0x8000)) {
|
||||
dev_warn(dev, "hibit @ %d [0x%04x]\n", i, chdata);
|
||||
continue;
|
||||
}
|
||||
|
||||
ch = chdata >> 12;
|
||||
if (ch == CH_X)
|
||||
x = chdata & MAX_12BIT;
|
||||
else if (ch == CH_Y)
|
||||
y = chdata & MAX_12BIT;
|
||||
else
|
||||
dev_warn(dev, "unknown channel %d [0x%04x]\n", ch,
|
||||
chdata);
|
||||
}
|
||||
|
||||
touchscreen_report_pos(ts->input, &ts->props, x, y, false);
|
||||
input_report_key(ts->input, BTN_TOUCH, 1);
|
||||
input_sync(ts->input);
|
||||
dev_dbg(dev, "point(%4d,%4d)\n", x, y);
|
||||
|
||||
mod_timer(&ts->timer, jiffies + SX8650_PENIRQ_TIMEOUT);
|
||||
spin_unlock_irqrestore(&ts->lock, flags);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t sx8654_irq(int irq, void *handle)
|
||||
{
|
||||
struct sx8654 *sx8654 = handle;
|
||||
@ -112,8 +228,8 @@ static irqreturn_t sx8654_irq(int irq, void *handle)
|
||||
x = ((data[0] & 0xf) << 8) | (data[1]);
|
||||
y = ((data[2] & 0xf) << 8) | (data[3]);
|
||||
|
||||
input_report_abs(sx8654->input, ABS_X, x);
|
||||
input_report_abs(sx8654->input, ABS_Y, y);
|
||||
touchscreen_report_pos(sx8654->input, &sx8654->props, x, y,
|
||||
false);
|
||||
input_report_key(sx8654->input, BTN_TOUCH, 1);
|
||||
input_sync(sx8654->input);
|
||||
|
||||
@ -124,6 +240,25 @@ out:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int sx8654_reset(struct sx8654 *ts)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (ts->gpio_reset) {
|
||||
gpiod_set_value_cansleep(ts->gpio_reset, 1);
|
||||
udelay(2); /* Tpulse > 1µs */
|
||||
gpiod_set_value_cansleep(ts->gpio_reset, 0);
|
||||
} else {
|
||||
dev_dbg(&ts->client->dev, "NRST unavailable, try softreset\n");
|
||||
err = i2c_smbus_write_byte_data(ts->client, I2C_REG_SOFTRESET,
|
||||
SOFTRESET_VALUE);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sx8654_open(struct input_dev *dev)
|
||||
{
|
||||
struct sx8654 *sx8654 = input_get_drvdata(dev);
|
||||
@ -157,14 +292,17 @@ static void sx8654_close(struct input_dev *dev)
|
||||
|
||||
disable_irq(client->irq);
|
||||
|
||||
if (!sx8654->data->has_irq_penrelease)
|
||||
del_timer_sync(&sx8654->timer);
|
||||
|
||||
/* enable manual mode mode */
|
||||
error = i2c_smbus_write_byte(client, CMD_MANUAL);
|
||||
error = i2c_smbus_write_byte(client, sx8654->data->cmd_manual);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "writing command CMD_MANUAL failed");
|
||||
return;
|
||||
}
|
||||
|
||||
error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH0, 0);
|
||||
error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH0, RATE_MANUAL);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "writing to I2C_REG_TOUCH0 failed");
|
||||
return;
|
||||
@ -186,6 +324,31 @@ static int sx8654_probe(struct i2c_client *client,
|
||||
if (!sx8654)
|
||||
return -ENOMEM;
|
||||
|
||||
sx8654->gpio_reset = devm_gpiod_get_optional(&client->dev, "reset",
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(sx8654->gpio_reset)) {
|
||||
error = PTR_ERR(sx8654->gpio_reset);
|
||||
if (error != -EPROBE_DEFER)
|
||||
dev_err(&client->dev, "unable to get reset-gpio: %d\n",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
dev_dbg(&client->dev, "got GPIO reset pin\n");
|
||||
|
||||
sx8654->data = device_get_match_data(&client->dev);
|
||||
if (!sx8654->data)
|
||||
sx8654->data = (const struct sx865x_data *)id->driver_data;
|
||||
if (!sx8654->data) {
|
||||
dev_err(&client->dev, "invalid or missing device data\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!sx8654->data->has_irq_penrelease) {
|
||||
dev_dbg(&client->dev, "use timer for penrelease\n");
|
||||
timer_setup(&sx8654->timer, sx865x_penrelease_timer_handler, 0);
|
||||
spin_lock_init(&sx8654->lock);
|
||||
}
|
||||
|
||||
input = devm_input_allocate_device(&client->dev);
|
||||
if (!input)
|
||||
return -ENOMEM;
|
||||
@ -201,43 +364,46 @@ static int sx8654_probe(struct i2c_client *client,
|
||||
input_set_abs_params(input, ABS_X, 0, MAX_12BIT, 0, 0);
|
||||
input_set_abs_params(input, ABS_Y, 0, MAX_12BIT, 0, 0);
|
||||
|
||||
touchscreen_parse_properties(input, false, &sx8654->props);
|
||||
|
||||
sx8654->client = client;
|
||||
sx8654->input = input;
|
||||
|
||||
input_set_drvdata(sx8654->input, sx8654);
|
||||
|
||||
error = i2c_smbus_write_byte_data(client, I2C_REG_SOFTRESET,
|
||||
SOFTRESET_VALUE);
|
||||
error = sx8654_reset(sx8654);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "writing softreset value failed");
|
||||
dev_err(&client->dev, "reset failed");
|
||||
return error;
|
||||
}
|
||||
|
||||
error = i2c_smbus_write_byte_data(client, I2C_REG_CHANMASK,
|
||||
CONV_X | CONV_Y);
|
||||
sx8654->data->chan_mask);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "writing to I2C_REG_CHANMASK failed");
|
||||
return error;
|
||||
}
|
||||
|
||||
error = i2c_smbus_write_byte_data(client, I2C_REG_IRQMASK,
|
||||
IRQ_PENTOUCH_TOUCHCONVDONE |
|
||||
IRQ_PENRELEASE);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "writing to I2C_REG_IRQMASK failed");
|
||||
return error;
|
||||
if (sx8654->data->has_reg_irqmask) {
|
||||
error = i2c_smbus_write_byte_data(client, I2C_REG_IRQMASK,
|
||||
IRQ_PENTOUCH_TOUCHCONVDONE |
|
||||
IRQ_PENRELEASE);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "writing I2C_REG_IRQMASK failed");
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH1,
|
||||
CONDIRQ | FILT_7SA);
|
||||
CONDIRQ | RPDNT_100K | FILT_7SA);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "writing to I2C_REG_TOUCH1 failed");
|
||||
return error;
|
||||
}
|
||||
|
||||
error = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
NULL, sx8654_irq,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
NULL, sx8654->data->irqh,
|
||||
IRQF_ONESHOT,
|
||||
client->name, sx8654);
|
||||
if (error) {
|
||||
dev_err(&client->dev,
|
||||
@ -256,17 +422,48 @@ static int sx8654_probe(struct i2c_client *client,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct sx865x_data sx8650_data = {
|
||||
.cmd_manual = 0xb0,
|
||||
.has_irq_penrelease = false,
|
||||
.has_reg_irqmask = false,
|
||||
.chan_mask = (CONV_X | CONV_Y),
|
||||
.irqh = sx8650_irq,
|
||||
};
|
||||
|
||||
static const struct sx865x_data sx8654_data = {
|
||||
.cmd_manual = 0xc0,
|
||||
.has_irq_penrelease = true,
|
||||
.has_reg_irqmask = true,
|
||||
.chan_mask = (CONV_X | CONV_Y),
|
||||
.irqh = sx8654_irq,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id sx8654_of_match[] = {
|
||||
{ .compatible = "semtech,sx8654", },
|
||||
{ },
|
||||
{
|
||||
.compatible = "semtech,sx8650",
|
||||
.data = &sx8650_data,
|
||||
}, {
|
||||
.compatible = "semtech,sx8654",
|
||||
.data = &sx8654_data,
|
||||
}, {
|
||||
.compatible = "semtech,sx8655",
|
||||
.data = &sx8654_data,
|
||||
}, {
|
||||
.compatible = "semtech,sx8656",
|
||||
.data = &sx8654_data,
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sx8654_of_match);
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id sx8654_id_table[] = {
|
||||
{ "semtech_sx8654", 0 },
|
||||
{ },
|
||||
{ .name = "semtech_sx8650", .driver_data = (long)&sx8650_data },
|
||||
{ .name = "semtech_sx8654", .driver_data = (long)&sx8654_data },
|
||||
{ .name = "semtech_sx8655", .driver_data = (long)&sx8654_data },
|
||||
{ .name = "semtech_sx8656", .driver_data = (long)&sx8654_data },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, sx8654_id_table);
|
||||
|
||||
|
@ -507,10 +507,8 @@ static int titsc_remove(struct platform_device *pdev)
|
||||
static int __maybe_unused titsc_suspend(struct device *dev)
|
||||
{
|
||||
struct titsc *ts_dev = dev_get_drvdata(dev);
|
||||
struct ti_tscadc_dev *tscadc_dev;
|
||||
unsigned int idle;
|
||||
|
||||
tscadc_dev = ti_tscadc_dev_get(to_platform_device(dev));
|
||||
if (device_may_wakeup(dev)) {
|
||||
titsc_writel(ts_dev, REG_IRQSTATUS, TSC_IRQENB_MASK);
|
||||
idle = titsc_readl(ts_dev, REG_IRQENABLE);
|
||||
@ -524,9 +522,7 @@ static int __maybe_unused titsc_suspend(struct device *dev)
|
||||
static int __maybe_unused titsc_resume(struct device *dev)
|
||||
{
|
||||
struct titsc *ts_dev = dev_get_drvdata(dev);
|
||||
struct ti_tscadc_dev *tscadc_dev;
|
||||
|
||||
tscadc_dev = ti_tscadc_dev_get(to_platform_device(dev));
|
||||
if (device_may_wakeup(dev)) {
|
||||
titsc_writel(ts_dev, REG_IRQWAKEUP,
|
||||
0x00);
|
||||
|
@ -1,11 +0,0 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _ILI210X_H
|
||||
#define _ILI210X_H
|
||||
|
||||
struct ili210x_platform_data {
|
||||
unsigned long irq_flags;
|
||||
unsigned int poll_period;
|
||||
bool (*get_pendown_state)(void);
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user