Merge branch 'next' into for-linus
Prepare input updates for 5.16 merge window.
This commit is contained in:
commit
efe6f16c6f
|
@ -9,6 +9,7 @@ Required properties:
|
|||
"microchip,cap1106"
|
||||
"microchip,cap1126"
|
||||
"microchip,cap1188"
|
||||
"microchip,cap1206"
|
||||
|
||||
reg: The I2C slave address of the device.
|
||||
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/input/cypress-sf.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Cypress StreetFighter touchkey controller
|
||||
|
||||
maintainers:
|
||||
- Yassine Oudjana <y.oudjana@protonmail.com>
|
||||
|
||||
allOf:
|
||||
- $ref: input.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: cypress,sf3155
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
avdd-supply:
|
||||
description: Regulator for AVDD analog voltage
|
||||
|
||||
vdd-supply:
|
||||
description: Regulator for VDD digital voltage
|
||||
|
||||
linux,keycodes:
|
||||
minItems: 1
|
||||
maxItems: 8
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- avdd-supply
|
||||
- vdd-supply
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/input/input.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
touchkey@28 {
|
||||
compatible = "cypress,sf3155";
|
||||
reg = <0x28>;
|
||||
interrupt-parent = <&msmgpio>;
|
||||
interrupts = <77 IRQ_TYPE_EDGE_FALLING>;
|
||||
avdd-supply = <&vreg_l6a_1p8>;
|
||||
vdd-supply = <&vdd_3v2_tp>;
|
||||
linux,keycodes = <KEY_BACK KEY_MENU>;
|
||||
};
|
||||
};
|
12
MAINTAINERS
12
MAINTAINERS
|
@ -4301,7 +4301,7 @@ CHIPONE ICN8318 I2C TOUCHSCREEN DRIVER
|
|||
M: Hans de Goede <hdegoede@redhat.com>
|
||||
L: linux-input@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/input/touchscreen/chipone_icn8318.txt
|
||||
F: Documentation/devicetree/bindings/input/touchscreen/chipone,icn8318.yaml
|
||||
F: drivers/input/touchscreen/chipone_icn8318.c
|
||||
|
||||
CHIPONE ICN8505 I2C TOUCHSCREEN DRIVER
|
||||
|
@ -5006,6 +5006,13 @@ L: linux-input@vger.kernel.org
|
|||
S: Maintained
|
||||
F: drivers/input/touchscreen/cy8ctma140.c
|
||||
|
||||
CYPRESS STREETFIGHTER TOUCHKEYS DRIVER
|
||||
M: Yassine Oudjana <y.oudjana@protonmail.com>
|
||||
L: linux-input@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/input/cypress-sf.yaml
|
||||
F: drivers/input/keyboard/cypress-sf.c
|
||||
|
||||
CYTTSP TOUCHSCREEN DRIVER
|
||||
M: Linus Walleij <linus.walleij@linaro.org>
|
||||
L: linux-input@vger.kernel.org
|
||||
|
@ -7709,9 +7716,10 @@ F: drivers/media/usb/go7007/
|
|||
|
||||
GOODIX TOUCHSCREEN
|
||||
M: Bastien Nocera <hadess@hadess.net>
|
||||
M: Hans de Goede <hdegoede@redhat.com>
|
||||
L: linux-input@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/input/touchscreen/goodix.c
|
||||
F: drivers/input/touchscreen/goodix*
|
||||
|
||||
GOOGLE ETHERNET DRIVERS
|
||||
M: Catherine Sullivan <csully@google.com>
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <linux/input.h>
|
||||
#include <linux/gameport.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/seq_buf.h>
|
||||
#include <linux/timex.h>
|
||||
#include <linux/timekeeping.h>
|
||||
|
||||
|
@ -338,23 +339,24 @@ static void analog_calibrate_timer(struct analog_port *port)
|
|||
|
||||
static void analog_name(struct analog *analog)
|
||||
{
|
||||
snprintf(analog->name, sizeof(analog->name), "Analog %d-axis %d-button",
|
||||
struct seq_buf s;
|
||||
|
||||
seq_buf_init(&s, analog->name, sizeof(analog->name));
|
||||
seq_buf_printf(&s, "Analog %d-axis %d-button",
|
||||
hweight8(analog->mask & ANALOG_AXES_STD),
|
||||
hweight8(analog->mask & ANALOG_BTNS_STD) + !!(analog->mask & ANALOG_BTNS_CHF) * 2 +
|
||||
hweight16(analog->mask & ANALOG_BTNS_GAMEPAD) + !!(analog->mask & ANALOG_HBTN_CHF) * 4);
|
||||
|
||||
if (analog->mask & ANALOG_HATS_ALL)
|
||||
snprintf(analog->name, sizeof(analog->name), "%s %d-hat",
|
||||
analog->name, hweight16(analog->mask & ANALOG_HATS_ALL));
|
||||
seq_buf_printf(&s, " %d-hat",
|
||||
hweight16(analog->mask & ANALOG_HATS_ALL));
|
||||
|
||||
if (analog->mask & ANALOG_HAT_FCS)
|
||||
strlcat(analog->name, " FCS", sizeof(analog->name));
|
||||
seq_buf_printf(&s, " FCS");
|
||||
if (analog->mask & ANALOG_ANY_CHF)
|
||||
strlcat(analog->name, (analog->mask & ANALOG_SAITEK) ? " Saitek" : " CHF",
|
||||
sizeof(analog->name));
|
||||
seq_buf_printf(&s, (analog->mask & ANALOG_SAITEK) ? " Saitek" : " CHF");
|
||||
|
||||
strlcat(analog->name, (analog->mask & ANALOG_GAMEPAD) ? " gamepad": " joystick",
|
||||
sizeof(analog->name));
|
||||
seq_buf_printf(&s, (analog->mask & ANALOG_GAMEPAD) ? " gamepad" : " joystick");
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -92,7 +92,7 @@ static int iforce_usb_get_id(struct iforce *iforce, u8 id,
|
|||
id,
|
||||
USB_TYPE_VENDOR | USB_DIR_IN |
|
||||
USB_RECIP_INTERFACE,
|
||||
0, 0, buf, IFORCE_MAX_LENGTH, HZ);
|
||||
0, 0, buf, IFORCE_MAX_LENGTH, 1000);
|
||||
if (status < 0) {
|
||||
dev_err(&iforce_usb->intf->dev,
|
||||
"usb_submit_urb failed: %d\n", status);
|
||||
|
|
|
@ -83,7 +83,7 @@ static const struct tmdc_model {
|
|||
const signed char *axes;
|
||||
const short *buttons;
|
||||
} tmdc_models[] = {
|
||||
{ 1, "ThrustMaster Millenium 3D Inceptor", 6, 2, { 4, 2 }, { 4, 6 }, tmdc_abs, tmdc_btn_joy },
|
||||
{ 1, "ThrustMaster Millennium 3D Inceptor", 6, 2, { 4, 2 }, { 4, 6 }, tmdc_abs, tmdc_btn_joy },
|
||||
{ 3, "ThrustMaster Rage 3D Gamepad", 2, 0, { 8, 2 }, { 0, 0 }, tmdc_abs, tmdc_btn_pad },
|
||||
{ 4, "ThrustMaster Attack Throttle", 5, 2, { 4, 6 }, { 4, 2 }, tmdc_abs_at, tmdc_btn_at },
|
||||
{ 8, "ThrustMaster FragMaster", 4, 0, { 8, 2 }, { 0, 0 }, tmdc_abs_fm, tmdc_btn_fm },
|
||||
|
|
|
@ -791,4 +791,14 @@ config KEYBOARD_MTK_PMIC
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called pmic-keys.
|
||||
|
||||
config KEYBOARD_CYPRESS_SF
|
||||
tristate "Cypress StreetFighter touchkey support"
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here if you want to enable support for Cypress StreetFighter
|
||||
touchkeys.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called cypress-sf.
|
||||
|
||||
endif
|
||||
|
|
|
@ -17,6 +17,7 @@ obj-$(CONFIG_KEYBOARD_BCM) += bcm-keypad.o
|
|||
obj-$(CONFIG_KEYBOARD_CAP11XX) += cap11xx.o
|
||||
obj-$(CONFIG_KEYBOARD_CLPS711X) += clps711x-keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_CROS_EC) += cros_ec_keyb.o
|
||||
obj-$(CONFIG_KEYBOARD_CYPRESS_SF) += cypress-sf.o
|
||||
obj-$(CONFIG_KEYBOARD_DAVINCI) += davinci_keyscan.o
|
||||
obj-$(CONFIG_KEYBOARD_DLINK_DIR685) += dlink-dir685-touchkeys.o
|
||||
obj-$(CONFIG_KEYBOARD_EP93XX) += ep93xx_keypad.o
|
||||
|
|
|
@ -91,18 +91,21 @@ struct cap11xx_hw_model {
|
|||
u8 product_id;
|
||||
unsigned int num_channels;
|
||||
unsigned int num_leds;
|
||||
bool no_gain;
|
||||
};
|
||||
|
||||
enum {
|
||||
CAP1106,
|
||||
CAP1126,
|
||||
CAP1188,
|
||||
CAP1206,
|
||||
};
|
||||
|
||||
static const struct cap11xx_hw_model cap11xx_devices[] = {
|
||||
[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 },
|
||||
[CAP1106] = { .product_id = 0x55, .num_channels = 6, .num_leds = 0, .no_gain = false },
|
||||
[CAP1126] = { .product_id = 0x53, .num_channels = 6, .num_leds = 2, .no_gain = false },
|
||||
[CAP1188] = { .product_id = 0x50, .num_channels = 8, .num_leds = 8, .no_gain = false },
|
||||
[CAP1206] = { .product_id = 0x67, .num_channels = 6, .num_leds = 0, .no_gain = true },
|
||||
};
|
||||
|
||||
static const struct reg_default cap11xx_reg_defaults[] = {
|
||||
|
@ -378,17 +381,24 @@ static int cap11xx_i2c_probe(struct i2c_client *i2c_client,
|
|||
node = dev->of_node;
|
||||
|
||||
if (!of_property_read_u32(node, "microchip,sensor-gain", &gain32)) {
|
||||
if (is_power_of_2(gain32) && gain32 <= 8)
|
||||
if (cap->no_gain)
|
||||
dev_warn(dev,
|
||||
"This version doesn't support sensor gain\n");
|
||||
else if (is_power_of_2(gain32) && gain32 <= 8)
|
||||
gain = ilog2(gain32);
|
||||
else
|
||||
dev_err(dev, "Invalid sensor-gain value %d\n", gain32);
|
||||
}
|
||||
|
||||
if (of_property_read_bool(node, "microchip,irq-active-high")) {
|
||||
error = regmap_update_bits(priv->regmap, CAP11XX_REG_CONFIG2,
|
||||
CAP11XX_REG_CONFIG2_ALT_POL, 0);
|
||||
if (error)
|
||||
return error;
|
||||
if (id->driver_data != CAP1206) {
|
||||
if (of_property_read_bool(node, "microchip,irq-active-high")) {
|
||||
error = regmap_update_bits(priv->regmap,
|
||||
CAP11XX_REG_CONFIG2,
|
||||
CAP11XX_REG_CONFIG2_ALT_POL,
|
||||
0);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
/* Provide some useful defaults */
|
||||
|
@ -398,11 +408,14 @@ static int cap11xx_i2c_probe(struct i2c_client *i2c_client,
|
|||
of_property_read_u32_array(node, "linux,keycodes",
|
||||
priv->keycodes, cap->num_channels);
|
||||
|
||||
error = regmap_update_bits(priv->regmap, CAP11XX_REG_MAIN_CONTROL,
|
||||
CAP11XX_REG_MAIN_CONTROL_GAIN_MASK,
|
||||
gain << CAP11XX_REG_MAIN_CONTROL_GAIN_SHIFT);
|
||||
if (error)
|
||||
return error;
|
||||
if (!cap->no_gain) {
|
||||
error = regmap_update_bits(priv->regmap,
|
||||
CAP11XX_REG_MAIN_CONTROL,
|
||||
CAP11XX_REG_MAIN_CONTROL_GAIN_MASK,
|
||||
gain << CAP11XX_REG_MAIN_CONTROL_GAIN_SHIFT);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Disable autorepeat. The Linux input system has its own handling. */
|
||||
error = regmap_write(priv->regmap, CAP11XX_REG_REPEAT_RATE, 0);
|
||||
|
@ -470,6 +483,7 @@ static const struct of_device_id cap11xx_dt_ids[] = {
|
|||
{ .compatible = "microchip,cap1106", },
|
||||
{ .compatible = "microchip,cap1126", },
|
||||
{ .compatible = "microchip,cap1188", },
|
||||
{ .compatible = "microchip,cap1206", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, cap11xx_dt_ids);
|
||||
|
@ -478,6 +492,7 @@ static const struct i2c_device_id cap11xx_i2c_ids[] = {
|
|||
{ "cap1106", CAP1106 },
|
||||
{ "cap1126", CAP1126 },
|
||||
{ "cap1188", CAP1188 },
|
||||
{ "cap1206", CAP1206 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, cap11xx_i2c_ids);
|
||||
|
|
|
@ -0,0 +1,224 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Cypress StreetFighter Touchkey Driver
|
||||
*
|
||||
* Copyright (c) 2021 Yassine Oudjana <y.oudjana@protonmail.com>
|
||||
*/
|
||||
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
#define CYPRESS_SF_DEV_NAME "cypress-sf"
|
||||
|
||||
#define CYPRESS_SF_REG_BUTTON_STATUS 0x4a
|
||||
|
||||
struct cypress_sf_data {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input_dev;
|
||||
struct regulator_bulk_data regulators[2];
|
||||
u32 *keycodes;
|
||||
unsigned long keystates;
|
||||
int num_keys;
|
||||
};
|
||||
|
||||
static irqreturn_t cypress_sf_irq_handler(int irq, void *devid)
|
||||
{
|
||||
struct cypress_sf_data *touchkey = devid;
|
||||
unsigned long keystates, changed;
|
||||
bool new_state;
|
||||
int val, key;
|
||||
|
||||
val = i2c_smbus_read_byte_data(touchkey->client,
|
||||
CYPRESS_SF_REG_BUTTON_STATUS);
|
||||
if (val < 0) {
|
||||
dev_err(&touchkey->client->dev,
|
||||
"Failed to read button status: %d", val);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
keystates = val;
|
||||
|
||||
bitmap_xor(&changed, &keystates, &touchkey->keystates,
|
||||
touchkey->num_keys);
|
||||
|
||||
for_each_set_bit(key, &changed, touchkey->num_keys) {
|
||||
new_state = keystates & BIT(key);
|
||||
dev_dbg(&touchkey->client->dev,
|
||||
"Key %d changed to %d", key, new_state);
|
||||
input_report_key(touchkey->input_dev,
|
||||
touchkey->keycodes[key], new_state);
|
||||
}
|
||||
|
||||
input_sync(touchkey->input_dev);
|
||||
touchkey->keystates = keystates;
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int cypress_sf_probe(struct i2c_client *client)
|
||||
{
|
||||
struct cypress_sf_data *touchkey;
|
||||
int key, error;
|
||||
|
||||
touchkey = devm_kzalloc(&client->dev, sizeof(*touchkey), GFP_KERNEL);
|
||||
if (!touchkey)
|
||||
return -ENOMEM;
|
||||
|
||||
touchkey->client = client;
|
||||
i2c_set_clientdata(client, touchkey);
|
||||
|
||||
touchkey->regulators[0].supply = "vdd";
|
||||
touchkey->regulators[1].supply = "avdd";
|
||||
|
||||
error = devm_regulator_bulk_get(&client->dev,
|
||||
ARRAY_SIZE(touchkey->regulators),
|
||||
touchkey->regulators);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "Failed to get regulators: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
touchkey->num_keys = device_property_read_u32_array(&client->dev,
|
||||
"linux,keycodes",
|
||||
NULL, 0);
|
||||
if (touchkey->num_keys < 0) {
|
||||
/* Default key count */
|
||||
touchkey->num_keys = 2;
|
||||
}
|
||||
|
||||
touchkey->keycodes = devm_kcalloc(&client->dev,
|
||||
touchkey->num_keys,
|
||||
sizeof(*touchkey->keycodes),
|
||||
GFP_KERNEL);
|
||||
if (!touchkey->keycodes)
|
||||
return -ENOMEM;
|
||||
|
||||
error = device_property_read_u32_array(&client->dev, "linux,keycodes",
|
||||
touchkey->keycodes,
|
||||
touchkey->num_keys);
|
||||
|
||||
if (error) {
|
||||
dev_warn(&client->dev,
|
||||
"Failed to read keycodes: %d, using defaults\n",
|
||||
error);
|
||||
|
||||
/* Default keycodes */
|
||||
touchkey->keycodes[0] = KEY_BACK;
|
||||
touchkey->keycodes[1] = KEY_MENU;
|
||||
}
|
||||
|
||||
error = regulator_bulk_enable(ARRAY_SIZE(touchkey->regulators),
|
||||
touchkey->regulators);
|
||||
if (error) {
|
||||
dev_err(&client->dev,
|
||||
"Failed to enable regulators: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
touchkey->input_dev = devm_input_allocate_device(&client->dev);
|
||||
if (!touchkey->input_dev) {
|
||||
dev_err(&client->dev, "Failed to allocate input device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
touchkey->input_dev->name = CYPRESS_SF_DEV_NAME;
|
||||
touchkey->input_dev->id.bustype = BUS_I2C;
|
||||
|
||||
for (key = 0; key < touchkey->num_keys; ++key)
|
||||
input_set_capability(touchkey->input_dev,
|
||||
EV_KEY, touchkey->keycodes[key]);
|
||||
|
||||
error = input_register_device(touchkey->input_dev);
|
||||
if (error) {
|
||||
dev_err(&client->dev,
|
||||
"Failed to register input device: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
NULL, cypress_sf_irq_handler,
|
||||
IRQF_ONESHOT,
|
||||
CYPRESS_SF_DEV_NAME, touchkey);
|
||||
if (error) {
|
||||
dev_err(&client->dev,
|
||||
"Failed to register threaded irq: %d", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
};
|
||||
|
||||
static int __maybe_unused cypress_sf_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct cypress_sf_data *touchkey = i2c_get_clientdata(client);
|
||||
int error;
|
||||
|
||||
disable_irq(client->irq);
|
||||
|
||||
error = regulator_bulk_disable(ARRAY_SIZE(touchkey->regulators),
|
||||
touchkey->regulators);
|
||||
if (error) {
|
||||
dev_err(dev, "Failed to disable regulators: %d", error);
|
||||
enable_irq(client->irq);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused cypress_sf_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct cypress_sf_data *touchkey = i2c_get_clientdata(client);
|
||||
int error;
|
||||
|
||||
error = regulator_bulk_enable(ARRAY_SIZE(touchkey->regulators),
|
||||
touchkey->regulators);
|
||||
if (error) {
|
||||
dev_err(dev, "Failed to enable regulators: %d", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
enable_irq(client->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(cypress_sf_pm_ops,
|
||||
cypress_sf_suspend, cypress_sf_resume);
|
||||
|
||||
static struct i2c_device_id cypress_sf_id_table[] = {
|
||||
{ CYPRESS_SF_DEV_NAME, 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, cypress_sf_id_table);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id cypress_sf_of_match[] = {
|
||||
{ .compatible = "cypress,sf3155", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, cypress_sf_of_match);
|
||||
#endif
|
||||
|
||||
static struct i2c_driver cypress_sf_driver = {
|
||||
.driver = {
|
||||
.name = CYPRESS_SF_DEV_NAME,
|
||||
.pm = &cypress_sf_pm_ops,
|
||||
.of_match_table = of_match_ptr(cypress_sf_of_match),
|
||||
},
|
||||
.id_table = cypress_sf_id_table,
|
||||
.probe_new = cypress_sf_probe,
|
||||
};
|
||||
module_i2c_driver(cypress_sf_driver);
|
||||
|
||||
MODULE_AUTHOR("Yassine Oudjana <y.oudjana@protonmail.com>");
|
||||
MODULE_DESCRIPTION("Cypress StreetFighter Touchkey Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
|
@ -17,6 +17,7 @@
|
|||
* flag.
|
||||
*/
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
@ -26,6 +27,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/soc/cirrus/ep93xx.h>
|
||||
#include <linux/platform_data/keypad-ep93xx.h>
|
||||
#include <linux/pm_wakeirq.h>
|
||||
|
||||
/*
|
||||
* Keypad Interface Register offsets
|
||||
|
@ -35,28 +37,28 @@
|
|||
#define KEY_REG 0x08 /* Key Value Capture register */
|
||||
|
||||
/* Key Scan Initialization Register bit defines */
|
||||
#define KEY_INIT_DBNC_MASK (0x00ff0000)
|
||||
#define KEY_INIT_DBNC_SHIFT (16)
|
||||
#define KEY_INIT_DIS3KY (1<<15)
|
||||
#define KEY_INIT_DIAG (1<<14)
|
||||
#define KEY_INIT_BACK (1<<13)
|
||||
#define KEY_INIT_T2 (1<<12)
|
||||
#define KEY_INIT_PRSCL_MASK (0x000003ff)
|
||||
#define KEY_INIT_PRSCL_SHIFT (0)
|
||||
#define KEY_INIT_DBNC_MASK GENMASK(23, 16)
|
||||
#define KEY_INIT_DBNC_SHIFT 16
|
||||
#define KEY_INIT_DIS3KY BIT(15)
|
||||
#define KEY_INIT_DIAG BIT(14)
|
||||
#define KEY_INIT_BACK BIT(13)
|
||||
#define KEY_INIT_T2 BIT(12)
|
||||
#define KEY_INIT_PRSCL_MASK GENMASK(9, 0)
|
||||
#define KEY_INIT_PRSCL_SHIFT 0
|
||||
|
||||
/* Key Scan Diagnostic Register bit defines */
|
||||
#define KEY_DIAG_MASK (0x0000003f)
|
||||
#define KEY_DIAG_SHIFT (0)
|
||||
#define KEY_DIAG_MASK GENMASK(5, 0)
|
||||
#define KEY_DIAG_SHIFT 0
|
||||
|
||||
/* Key Value Capture Register bit defines */
|
||||
#define KEY_REG_K (1<<15)
|
||||
#define KEY_REG_INT (1<<14)
|
||||
#define KEY_REG_2KEYS (1<<13)
|
||||
#define KEY_REG_1KEY (1<<12)
|
||||
#define KEY_REG_KEY2_MASK (0x00000fc0)
|
||||
#define KEY_REG_KEY2_SHIFT (6)
|
||||
#define KEY_REG_KEY1_MASK (0x0000003f)
|
||||
#define KEY_REG_KEY1_SHIFT (0)
|
||||
#define KEY_REG_K BIT(15)
|
||||
#define KEY_REG_INT BIT(14)
|
||||
#define KEY_REG_2KEYS BIT(13)
|
||||
#define KEY_REG_1KEY BIT(12)
|
||||
#define KEY_REG_KEY2_MASK GENMASK(11, 6)
|
||||
#define KEY_REG_KEY2_SHIFT 6
|
||||
#define KEY_REG_KEY1_MASK GENMASK(5, 0)
|
||||
#define KEY_REG_KEY1_SHIFT 0
|
||||
|
||||
#define EP93XX_MATRIX_SIZE (EP93XX_MATRIX_ROWS * EP93XX_MATRIX_COLS)
|
||||
|
||||
|
@ -175,8 +177,7 @@ static void ep93xx_keypad_close(struct input_dev *pdev)
|
|||
}
|
||||
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int ep93xx_keypad_suspend(struct device *dev)
|
||||
static int __maybe_unused ep93xx_keypad_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct ep93xx_keypad *keypad = platform_get_drvdata(pdev);
|
||||
|
@ -191,21 +192,15 @@ static int ep93xx_keypad_suspend(struct device *dev)
|
|||
|
||||
mutex_unlock(&input_dev->mutex);
|
||||
|
||||
if (device_may_wakeup(&pdev->dev))
|
||||
enable_irq_wake(keypad->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ep93xx_keypad_resume(struct device *dev)
|
||||
static int __maybe_unused ep93xx_keypad_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct ep93xx_keypad *keypad = platform_get_drvdata(pdev);
|
||||
struct input_dev *input_dev = keypad->input_dev;
|
||||
|
||||
if (device_may_wakeup(&pdev->dev))
|
||||
disable_irq_wake(keypad->irq);
|
||||
|
||||
mutex_lock(&input_dev->mutex);
|
||||
|
||||
if (input_device_enabled(input_dev)) {
|
||||
|
@ -220,11 +215,17 @@ static int ep93xx_keypad_resume(struct device *dev)
|
|||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(ep93xx_keypad_pm_ops,
|
||||
ep93xx_keypad_suspend, ep93xx_keypad_resume);
|
||||
|
||||
static void ep93xx_keypad_release_gpio_action(void *_pdev)
|
||||
{
|
||||
struct platform_device *pdev = _pdev;
|
||||
|
||||
ep93xx_keypad_release_gpio(pdev);
|
||||
}
|
||||
|
||||
static int ep93xx_keypad_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ep93xx_keypad *keypad;
|
||||
|
@ -233,61 +234,46 @@ static int ep93xx_keypad_probe(struct platform_device *pdev)
|
|||
struct resource *res;
|
||||
int err;
|
||||
|
||||
keypad = kzalloc(sizeof(struct ep93xx_keypad), GFP_KERNEL);
|
||||
keypad = devm_kzalloc(&pdev->dev, sizeof(*keypad), GFP_KERNEL);
|
||||
if (!keypad)
|
||||
return -ENOMEM;
|
||||
|
||||
keypad->pdata = dev_get_platdata(&pdev->dev);
|
||||
if (!keypad->pdata) {
|
||||
err = -EINVAL;
|
||||
goto failed_free;
|
||||
}
|
||||
if (!keypad->pdata)
|
||||
return -EINVAL;
|
||||
|
||||
keymap_data = keypad->pdata->keymap_data;
|
||||
if (!keymap_data) {
|
||||
err = -EINVAL;
|
||||
goto failed_free;
|
||||
}
|
||||
if (!keymap_data)
|
||||
return -EINVAL;
|
||||
|
||||
keypad->irq = platform_get_irq(pdev, 0);
|
||||
if (keypad->irq < 0) {
|
||||
err = keypad->irq;
|
||||
goto failed_free;
|
||||
}
|
||||
if (keypad->irq < 0)
|
||||
return keypad->irq;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
err = -ENXIO;
|
||||
goto failed_free;
|
||||
}
|
||||
if (!res)
|
||||
return -ENXIO;
|
||||
|
||||
res = request_mem_region(res->start, resource_size(res), pdev->name);
|
||||
if (!res) {
|
||||
err = -EBUSY;
|
||||
goto failed_free;
|
||||
}
|
||||
|
||||
keypad->mmio_base = ioremap(res->start, resource_size(res));
|
||||
if (keypad->mmio_base == NULL) {
|
||||
err = -ENXIO;
|
||||
goto failed_free_mem;
|
||||
}
|
||||
keypad->mmio_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(keypad->mmio_base))
|
||||
return PTR_ERR(keypad->mmio_base);
|
||||
|
||||
err = ep93xx_keypad_acquire_gpio(pdev);
|
||||
if (err)
|
||||
goto failed_free_io;
|
||||
return err;
|
||||
|
||||
keypad->clk = clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(keypad->clk)) {
|
||||
err = PTR_ERR(keypad->clk);
|
||||
goto failed_free_gpio;
|
||||
}
|
||||
err = devm_add_action_or_reset(&pdev->dev,
|
||||
ep93xx_keypad_release_gpio_action, pdev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
input_dev = input_allocate_device();
|
||||
if (!input_dev) {
|
||||
err = -ENOMEM;
|
||||
goto failed_put_clk;
|
||||
}
|
||||
keypad->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(keypad->clk))
|
||||
return PTR_ERR(keypad->clk);
|
||||
|
||||
input_dev = devm_input_allocate_device(&pdev->dev);
|
||||
if (!input_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
keypad->input_dev = input_dev;
|
||||
|
||||
|
@ -295,70 +281,40 @@ static int ep93xx_keypad_probe(struct platform_device *pdev)
|
|||
input_dev->id.bustype = BUS_HOST;
|
||||
input_dev->open = ep93xx_keypad_open;
|
||||
input_dev->close = ep93xx_keypad_close;
|
||||
input_dev->dev.parent = &pdev->dev;
|
||||
|
||||
err = matrix_keypad_build_keymap(keymap_data, NULL,
|
||||
EP93XX_MATRIX_ROWS, EP93XX_MATRIX_COLS,
|
||||
keypad->keycodes, input_dev);
|
||||
if (err)
|
||||
goto failed_free_dev;
|
||||
return err;
|
||||
|
||||
if (keypad->pdata->flags & EP93XX_KEYPAD_AUTOREPEAT)
|
||||
__set_bit(EV_REP, input_dev->evbit);
|
||||
input_set_drvdata(input_dev, keypad);
|
||||
|
||||
err = request_irq(keypad->irq, ep93xx_keypad_irq_handler,
|
||||
0, pdev->name, keypad);
|
||||
err = devm_request_irq(&pdev->dev, keypad->irq,
|
||||
ep93xx_keypad_irq_handler,
|
||||
0, pdev->name, keypad);
|
||||
if (err)
|
||||
goto failed_free_dev;
|
||||
return err;
|
||||
|
||||
err = input_register_device(input_dev);
|
||||
if (err)
|
||||
goto failed_free_irq;
|
||||
return err;
|
||||
|
||||
platform_set_drvdata(pdev, keypad);
|
||||
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
err = dev_pm_set_wake_irq(&pdev->dev, keypad->irq);
|
||||
if (err)
|
||||
dev_warn(&pdev->dev, "failed to set up wakeup irq: %d\n", err);
|
||||
|
||||
return 0;
|
||||
|
||||
failed_free_irq:
|
||||
free_irq(keypad->irq, keypad);
|
||||
failed_free_dev:
|
||||
input_free_device(input_dev);
|
||||
failed_put_clk:
|
||||
clk_put(keypad->clk);
|
||||
failed_free_gpio:
|
||||
ep93xx_keypad_release_gpio(pdev);
|
||||
failed_free_io:
|
||||
iounmap(keypad->mmio_base);
|
||||
failed_free_mem:
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
failed_free:
|
||||
kfree(keypad);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ep93xx_keypad_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ep93xx_keypad *keypad = platform_get_drvdata(pdev);
|
||||
struct resource *res;
|
||||
|
||||
free_irq(keypad->irq, keypad);
|
||||
|
||||
if (keypad->enabled)
|
||||
clk_disable(keypad->clk);
|
||||
clk_put(keypad->clk);
|
||||
|
||||
input_unregister_device(keypad->input_dev);
|
||||
|
||||
ep93xx_keypad_release_gpio(pdev);
|
||||
|
||||
iounmap(keypad->mmio_base);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
|
||||
kfree(keypad);
|
||||
dev_pm_clear_wake_irq(&pdev->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -107,9 +107,9 @@ static struct regulator *mpr121_vdd_supply_init(struct device *dev)
|
|||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
err = devm_add_action(dev, mpr121_vdd_supply_disable, vdd_supply);
|
||||
err = devm_add_action_or_reset(dev, mpr121_vdd_supply_disable,
|
||||
vdd_supply);
|
||||
if (err) {
|
||||
regulator_disable(vdd_supply);
|
||||
dev_err(dev, "failed to add disable regulator action: %d\n",
|
||||
err);
|
||||
return ERR_PTR(err);
|
||||
|
|
|
@ -190,8 +190,7 @@ static int omap_kp_probe(struct platform_device *pdev)
|
|||
row_shift = get_count_order(pdata->cols);
|
||||
keycodemax = pdata->rows << row_shift;
|
||||
|
||||
omap_kp = kzalloc(sizeof(struct omap_kp) +
|
||||
keycodemax * sizeof(unsigned short), GFP_KERNEL);
|
||||
omap_kp = kzalloc(struct_size(omap_kp, keymap, keycodemax), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!omap_kp || !input_dev) {
|
||||
kfree(omap_kp);
|
||||
|
|
|
@ -156,6 +156,8 @@ static irqreturn_t tm2_touchkey_irq_handler(int irq, void *devid)
|
|||
goto out;
|
||||
}
|
||||
|
||||
input_event(touchkey->input_dev, EV_MSC, MSC_SCAN, index);
|
||||
|
||||
if (data & TM2_TOUCHKEY_BIT_PRESS_EV) {
|
||||
for (i = 0; i < touchkey->num_keycodes; i++)
|
||||
input_report_key(touchkey->input_dev,
|
||||
|
@ -250,6 +252,11 @@ static int tm2_touchkey_probe(struct i2c_client *client,
|
|||
touchkey->input_dev->name = TM2_TOUCHKEY_DEV_NAME;
|
||||
touchkey->input_dev->id.bustype = BUS_I2C;
|
||||
|
||||
touchkey->input_dev->keycode = touchkey->keycodes;
|
||||
touchkey->input_dev->keycodemax = touchkey->num_keycodes;
|
||||
touchkey->input_dev->keycodesize = sizeof(touchkey->keycodes[0]);
|
||||
|
||||
input_set_capability(touchkey->input_dev, EV_MSC, MSC_SCAN);
|
||||
for (i = 0; i < touchkey->num_keycodes; i++)
|
||||
input_set_capability(touchkey->input_dev, EV_KEY,
|
||||
touchkey->keycodes[i]);
|
||||
|
|
|
@ -103,7 +103,9 @@ static int adxl34x_i2c_remove(struct i2c_client *client)
|
|||
{
|
||||
struct adxl34x *ac = i2c_get_clientdata(client);
|
||||
|
||||
return adxl34x_remove(ac);
|
||||
adxl34x_remove(ac);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused adxl34x_i2c_suspend(struct device *dev)
|
||||
|
|
|
@ -91,7 +91,9 @@ static int adxl34x_spi_remove(struct spi_device *spi)
|
|||
{
|
||||
struct adxl34x *ac = spi_get_drvdata(spi);
|
||||
|
||||
return adxl34x_remove(ac);
|
||||
adxl34x_remove(ac);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused adxl34x_spi_suspend(struct device *dev)
|
||||
|
|
|
@ -237,7 +237,7 @@ static const struct adxl34x_platform_data adxl34x_default_init = {
|
|||
|
||||
static void adxl34x_get_triple(struct adxl34x *ac, struct axis_triple *axis)
|
||||
{
|
||||
short buf[3];
|
||||
__le16 buf[3];
|
||||
|
||||
ac->bops->read_block(ac->dev, DATAX0, DATAZ1 - DATAX0 + 1, buf);
|
||||
|
||||
|
@ -896,15 +896,13 @@ struct adxl34x *adxl34x_probe(struct device *dev, int irq,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(adxl34x_probe);
|
||||
|
||||
int adxl34x_remove(struct adxl34x *ac)
|
||||
void adxl34x_remove(struct adxl34x *ac)
|
||||
{
|
||||
sysfs_remove_group(&ac->dev->kobj, &adxl34x_attr_group);
|
||||
free_irq(ac->irq, ac);
|
||||
input_unregister_device(ac->input);
|
||||
dev_dbg(ac->dev, "unregistered accelerometer\n");
|
||||
kfree(ac);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(adxl34x_remove);
|
||||
|
||||
|
|
|
@ -25,6 +25,6 @@ void adxl34x_resume(struct adxl34x *ac);
|
|||
struct adxl34x *adxl34x_probe(struct device *dev, int irq,
|
||||
bool fifo_delay_default,
|
||||
const struct adxl34x_bus_ops *bops);
|
||||
int adxl34x_remove(struct adxl34x *ac);
|
||||
void adxl34x_remove(struct adxl34x *ac);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -149,12 +149,19 @@ static const struct of_device_id ariel_pwrbutton_of_match[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(of, ariel_pwrbutton_of_match);
|
||||
|
||||
static const struct spi_device_id ariel_pwrbutton_spi_ids[] = {
|
||||
{ .name = "wyse-ariel-ec-input" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, ariel_pwrbutton_spi_ids);
|
||||
|
||||
static struct spi_driver ariel_pwrbutton_driver = {
|
||||
.driver = {
|
||||
.name = "dell-wyse-ariel-ec-input",
|
||||
.of_match_table = ariel_pwrbutton_of_match,
|
||||
},
|
||||
.probe = ariel_pwrbutton_probe,
|
||||
.id_table = ariel_pwrbutton_spi_ids,
|
||||
};
|
||||
module_spi_driver(ariel_pwrbutton_driver);
|
||||
|
||||
|
|
|
@ -54,9 +54,13 @@ static irqreturn_t powerbutton_irq(int irq, void *_button)
|
|||
static int cpcap_power_button_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct cpcap_power_button *button;
|
||||
int irq = platform_get_irq(pdev, 0);
|
||||
int irq;
|
||||
int err;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
button = devm_kmalloc(&pdev->dev, sizeof(*button), GFP_KERNEL);
|
||||
if (!button)
|
||||
return -ENOMEM;
|
||||
|
@ -73,7 +77,6 @@ static int cpcap_power_button_probe(struct platform_device *pdev)
|
|||
|
||||
button->idev->name = "cpcap-pwrbutton";
|
||||
button->idev->phys = "cpcap-pwrbutton/input0";
|
||||
button->idev->dev.parent = button->dev;
|
||||
input_set_capability(button->idev, EV_KEY, KEY_POWER);
|
||||
|
||||
err = devm_request_threaded_irq(&pdev->dev, irq, NULL,
|
||||
|
|
|
@ -424,5 +424,4 @@ module_platform_driver(max77693_haptic_driver);
|
|||
MODULE_AUTHOR("Jaewon Kim <jaewon02.kim@samsung.com>");
|
||||
MODULE_AUTHOR("Krzysztof Kozlowski <krzk@kernel.org>");
|
||||
MODULE_DESCRIPTION("MAXIM 77693/77843 Haptic driver");
|
||||
MODULE_ALIAS("platform:max77693-haptic");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/**
|
||||
/*
|
||||
* MAX8925 ONKEY driver
|
||||
*
|
||||
* Copyright (C) 2009 Marvell International Ltd.
|
||||
|
|
|
@ -210,6 +210,11 @@ static int palmas_pwron_probe(struct platform_device *pdev)
|
|||
INIT_DELAYED_WORK(&pwron->input_work, palmas_power_button_work);
|
||||
|
||||
pwron->irq = platform_get_irq(pdev, 0);
|
||||
if (pwron->irq < 0) {
|
||||
error = pwron->irq;
|
||||
goto err_free_input;
|
||||
}
|
||||
|
||||
error = request_threaded_irq(pwron->irq, NULL, pwron_irq,
|
||||
IRQF_TRIGGER_HIGH |
|
||||
IRQF_TRIGGER_LOW |
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#define PON_PS_HOLD_RST_CTL2 0x5b
|
||||
#define PON_PS_HOLD_ENABLE BIT(7)
|
||||
#define PON_PS_HOLD_TYPE_MASK 0x0f
|
||||
#define PON_PS_HOLD_TYPE_WARM_RESET 1
|
||||
#define PON_PS_HOLD_TYPE_SHUTDOWN 4
|
||||
#define PON_PS_HOLD_TYPE_HARD_RESET 7
|
||||
|
||||
|
@ -99,7 +100,10 @@ static int pm8941_reboot_notify(struct notifier_block *nb,
|
|||
break;
|
||||
case SYS_RESTART:
|
||||
default:
|
||||
reset_type = PON_PS_HOLD_TYPE_HARD_RESET;
|
||||
if (reboot_mode == REBOOT_WARM)
|
||||
reset_type = PON_PS_HOLD_TYPE_WARM_RESET;
|
||||
else
|
||||
reset_type = PON_PS_HOLD_TYPE_HARD_RESET;
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -517,6 +517,19 @@ static void elantech_report_trackpoint(struct psmouse *psmouse,
|
|||
case 0x16008020U:
|
||||
case 0x26800010U:
|
||||
case 0x36808000U:
|
||||
|
||||
/*
|
||||
* This firmware misreport coordinates for trackpoint
|
||||
* occasionally. Discard packets outside of [-127, 127] range
|
||||
* to prevent cursor jumps.
|
||||
*/
|
||||
if (packet[4] == 0x80 || packet[5] == 0x80 ||
|
||||
packet[1] >> 7 == packet[4] >> 7 ||
|
||||
packet[2] >> 7 == packet[5] >> 7) {
|
||||
elantech_debug("discarding packet [%6ph]\n", packet);
|
||||
break;
|
||||
|
||||
}
|
||||
x = packet[4] - (int)((packet[1]^0x80) << 1);
|
||||
y = (int)((packet[2]^0x80) << 1) - packet[5];
|
||||
|
||||
|
|
|
@ -90,6 +90,7 @@ int rmi_register_transport_device(struct rmi_transport_dev *xport)
|
|||
|
||||
rmi_dev->dev.bus = &rmi_bus_type;
|
||||
rmi_dev->dev.type = &rmi_device_type;
|
||||
rmi_dev->dev.parent = xport->dev;
|
||||
|
||||
xport->rmi_dev = rmi_dev;
|
||||
|
||||
|
|
|
@ -272,6 +272,13 @@ static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = {
|
|||
DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S6230"),
|
||||
},
|
||||
},
|
||||
{
|
||||
/* Fujitsu Lifebook T725 laptop */
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T725"),
|
||||
},
|
||||
},
|
||||
{
|
||||
/* Fujitsu Lifebook U745 */
|
||||
.matches = {
|
||||
|
@ -840,6 +847,13 @@ static const struct dmi_system_id __initconst i8042_dmi_notimeout_table[] = {
|
|||
DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK AH544"),
|
||||
},
|
||||
},
|
||||
{
|
||||
/* Fujitsu Lifebook T725 laptop */
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T725"),
|
||||
},
|
||||
},
|
||||
{
|
||||
/* Fujitsu U574 laptop */
|
||||
/* https://bugzilla.kernel.org/show_bug.cgi?id=69731 */
|
||||
|
|
|
@ -425,6 +425,7 @@ config TOUCHSCREEN_HYCON_HY46XX
|
|||
config TOUCHSCREEN_ILI210X
|
||||
tristate "Ilitek ILI210X based touchscreen"
|
||||
depends on I2C
|
||||
select CRC_CCITT
|
||||
help
|
||||
Say Y here if you have a ILI210X based touchscreen
|
||||
controller. This driver supports models ILI2102,
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
# Each configuration option enables a list of files.
|
||||
|
||||
wm97xx-ts-y := wm97xx-core.o
|
||||
goodix_ts-y := goodix.o goodix_fwupload.o
|
||||
|
||||
obj-$(CONFIG_TOUCHSCREEN_88PM860X) += 88pm860x-ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_AD7877) += ad7877.o
|
||||
|
@ -44,7 +45,7 @@ obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o
|
|||
obj-$(CONFIG_TOUCHSCREEN_EGALAX_SERIAL) += egalax_ts_serial.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_EXC3000) += exc3000.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_HIDEEP) += hideep.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_ILITEK) += ilitek_ts_i2c.o
|
||||
|
|
|
@ -101,10 +101,6 @@ struct ads7846 {
|
|||
struct spi_device *spi;
|
||||
struct regulator *reg;
|
||||
|
||||
#if IS_ENABLED(CONFIG_HWMON)
|
||||
struct device *hwmon;
|
||||
#endif
|
||||
|
||||
u16 model;
|
||||
u16 vref_mv;
|
||||
u16 vref_delay_usecs;
|
||||
|
@ -142,13 +138,18 @@ struct ads7846 {
|
|||
|
||||
int (*filter)(void *data, int data_idx, int *val);
|
||||
void *filter_data;
|
||||
void (*filter_cleanup)(void *data);
|
||||
int (*get_pendown_state)(void);
|
||||
int gpio_pendown;
|
||||
|
||||
void (*wait_for_sync)(void);
|
||||
};
|
||||
|
||||
enum ads7846_filter {
|
||||
ADS7846_FILTER_OK,
|
||||
ADS7846_FILTER_REPEAT,
|
||||
ADS7846_FILTER_IGNORE,
|
||||
};
|
||||
|
||||
/* leave chip selected when we're done, for quicker re-select? */
|
||||
#if 0
|
||||
#define CS_CHANGE(xfer) ((xfer).cs_change = 1)
|
||||
|
@ -549,6 +550,8 @@ __ATTRIBUTE_GROUPS(ads7846_attr);
|
|||
|
||||
static int ads784x_hwmon_register(struct spi_device *spi, struct ads7846 *ts)
|
||||
{
|
||||
struct device *hwmon;
|
||||
|
||||
/* hwmon sensors need a reference voltage */
|
||||
switch (ts->model) {
|
||||
case 7846:
|
||||
|
@ -569,17 +572,11 @@ static int ads784x_hwmon_register(struct spi_device *spi, struct ads7846 *ts)
|
|||
break;
|
||||
}
|
||||
|
||||
ts->hwmon = hwmon_device_register_with_groups(&spi->dev, spi->modalias,
|
||||
ts, ads7846_attr_groups);
|
||||
hwmon = devm_hwmon_device_register_with_groups(&spi->dev,
|
||||
spi->modalias, ts,
|
||||
ads7846_attr_groups);
|
||||
|
||||
return PTR_ERR_OR_ZERO(ts->hwmon);
|
||||
}
|
||||
|
||||
static void ads784x_hwmon_unregister(struct spi_device *spi,
|
||||
struct ads7846 *ts)
|
||||
{
|
||||
if (ts->hwmon)
|
||||
hwmon_device_unregister(ts->hwmon);
|
||||
return PTR_ERR_OR_ZERO(hwmon);
|
||||
}
|
||||
|
||||
#else
|
||||
|
@ -588,11 +585,6 @@ static inline int ads784x_hwmon_register(struct spi_device *spi,
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void ads784x_hwmon_unregister(struct spi_device *spi,
|
||||
struct ads7846 *ts)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
static ssize_t ads7846_pen_down_show(struct device *dev,
|
||||
|
@ -1014,8 +1006,8 @@ static int ads7846_setup_pendown(struct spi_device *spi,
|
|||
ts->get_pendown_state = pdata->get_pendown_state;
|
||||
} else if (gpio_is_valid(pdata->gpio_pendown)) {
|
||||
|
||||
err = gpio_request_one(pdata->gpio_pendown, GPIOF_IN,
|
||||
"ads7846_pendown");
|
||||
err = devm_gpio_request_one(&spi->dev, pdata->gpio_pendown,
|
||||
GPIOF_IN, "ads7846_pendown");
|
||||
if (err) {
|
||||
dev_err(&spi->dev,
|
||||
"failed to request/setup pendown GPIO%d: %d\n",
|
||||
|
@ -1212,24 +1204,30 @@ static const struct ads7846_platform_data *ads7846_probe_dt(struct device *dev)
|
|||
}
|
||||
#endif
|
||||
|
||||
static void ads7846_regulator_disable(void *regulator)
|
||||
{
|
||||
regulator_disable(regulator);
|
||||
}
|
||||
|
||||
static int ads7846_probe(struct spi_device *spi)
|
||||
{
|
||||
const struct ads7846_platform_data *pdata;
|
||||
struct ads7846 *ts;
|
||||
struct device *dev = &spi->dev;
|
||||
struct ads7846_packet *packet;
|
||||
struct input_dev *input_dev;
|
||||
unsigned long irq_flags;
|
||||
int err;
|
||||
|
||||
if (!spi->irq) {
|
||||
dev_dbg(&spi->dev, "no IRQ?\n");
|
||||
dev_dbg(dev, "no IRQ?\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* don't exceed max specified sample rate */
|
||||
if (spi->max_speed_hz > (125000 * SAMPLE_BITS)) {
|
||||
dev_err(&spi->dev, "f(sample) %d KHz?\n",
|
||||
(spi->max_speed_hz/SAMPLE_BITS)/1000);
|
||||
dev_err(dev, "f(sample) %d KHz?\n",
|
||||
(spi->max_speed_hz/SAMPLE_BITS)/1000);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
@ -1245,13 +1243,17 @@ static int ads7846_probe(struct spi_device *spi)
|
|||
if (err < 0)
|
||||
return err;
|
||||
|
||||
ts = kzalloc(sizeof(struct ads7846), GFP_KERNEL);
|
||||
packet = kzalloc(sizeof(struct ads7846_packet), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!ts || !packet || !input_dev) {
|
||||
err = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
}
|
||||
ts = devm_kzalloc(dev, sizeof(struct ads7846), GFP_KERNEL);
|
||||
if (!ts)
|
||||
return -ENOMEM;
|
||||
|
||||
packet = devm_kzalloc(dev, sizeof(struct ads7846_packet), GFP_KERNEL);
|
||||
if (!packet)
|
||||
return -ENOMEM;
|
||||
|
||||
input_dev = devm_input_allocate_device(dev);
|
||||
if (!input_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
spi_set_drvdata(spi, ts);
|
||||
|
||||
|
@ -1262,13 +1264,11 @@ static int ads7846_probe(struct spi_device *spi)
|
|||
mutex_init(&ts->lock);
|
||||
init_waitqueue_head(&ts->wait);
|
||||
|
||||
pdata = dev_get_platdata(&spi->dev);
|
||||
pdata = dev_get_platdata(dev);
|
||||
if (!pdata) {
|
||||
pdata = ads7846_probe_dt(&spi->dev);
|
||||
if (IS_ERR(pdata)) {
|
||||
err = PTR_ERR(pdata);
|
||||
goto err_free_mem;
|
||||
}
|
||||
pdata = ads7846_probe_dt(dev);
|
||||
if (IS_ERR(pdata))
|
||||
return PTR_ERR(pdata);
|
||||
}
|
||||
|
||||
ts->model = pdata->model ? : 7846;
|
||||
|
@ -1276,15 +1276,7 @@ static int ads7846_probe(struct spi_device *spi)
|
|||
ts->x_plate_ohms = pdata->x_plate_ohms ? : 400;
|
||||
ts->vref_mv = pdata->vref_mv;
|
||||
|
||||
if (pdata->filter != NULL) {
|
||||
if (pdata->filter_init != NULL) {
|
||||
err = pdata->filter_init(pdata, &ts->filter_data);
|
||||
if (err < 0)
|
||||
goto err_free_mem;
|
||||
}
|
||||
ts->filter = pdata->filter;
|
||||
ts->filter_cleanup = pdata->filter_cleanup;
|
||||
} else if (pdata->debounce_max) {
|
||||
if (pdata->debounce_max) {
|
||||
ts->debounce_max = pdata->debounce_max;
|
||||
if (ts->debounce_max < 2)
|
||||
ts->debounce_max = 2;
|
||||
|
@ -1298,7 +1290,7 @@ static int ads7846_probe(struct spi_device *spi)
|
|||
|
||||
err = ads7846_setup_pendown(spi, ts, pdata);
|
||||
if (err)
|
||||
goto err_cleanup_filter;
|
||||
return err;
|
||||
|
||||
if (pdata->penirq_recheck_delay_usecs)
|
||||
ts->penirq_recheck_delay_usecs =
|
||||
|
@ -1306,15 +1298,16 @@ static int ads7846_probe(struct spi_device *spi)
|
|||
|
||||
ts->wait_for_sync = pdata->wait_for_sync ? : null_wait_for_sync;
|
||||
|
||||
snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&spi->dev));
|
||||
snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev));
|
||||
snprintf(ts->name, sizeof(ts->name), "ADS%d Touchscreen", ts->model);
|
||||
|
||||
input_dev->name = ts->name;
|
||||
input_dev->phys = ts->phys;
|
||||
input_dev->dev.parent = &spi->dev;
|
||||
|
||||
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
|
||||
input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
|
||||
input_dev->id.bustype = BUS_SPI;
|
||||
input_dev->id.product = pdata->model;
|
||||
|
||||
input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
|
||||
input_set_abs_params(input_dev, ABS_X,
|
||||
pdata->x_min ? : 0,
|
||||
pdata->x_max ? : MAX_12BIT,
|
||||
|
@ -1345,125 +1338,84 @@ static int ads7846_probe(struct spi_device *spi)
|
|||
|
||||
ads7846_setup_spi_msg(ts, pdata);
|
||||
|
||||
ts->reg = regulator_get(&spi->dev, "vcc");
|
||||
ts->reg = devm_regulator_get(dev, "vcc");
|
||||
if (IS_ERR(ts->reg)) {
|
||||
err = PTR_ERR(ts->reg);
|
||||
dev_err(&spi->dev, "unable to get regulator: %d\n", err);
|
||||
goto err_free_gpio;
|
||||
dev_err(dev, "unable to get regulator: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = regulator_enable(ts->reg);
|
||||
if (err) {
|
||||
dev_err(&spi->dev, "unable to enable regulator: %d\n", err);
|
||||
goto err_put_regulator;
|
||||
dev_err(dev, "unable to enable regulator: %d\n", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = devm_add_action_or_reset(dev, ads7846_regulator_disable, ts->reg);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
irq_flags = pdata->irq_flags ? : IRQF_TRIGGER_FALLING;
|
||||
irq_flags |= IRQF_ONESHOT;
|
||||
|
||||
err = request_threaded_irq(spi->irq, ads7846_hard_irq, ads7846_irq,
|
||||
irq_flags, spi->dev.driver->name, ts);
|
||||
if (err && !pdata->irq_flags) {
|
||||
dev_info(&spi->dev,
|
||||
err = devm_request_threaded_irq(dev, spi->irq,
|
||||
ads7846_hard_irq, ads7846_irq,
|
||||
irq_flags, dev->driver->name, ts);
|
||||
if (err && err != -EPROBE_DEFER && !pdata->irq_flags) {
|
||||
dev_info(dev,
|
||||
"trying pin change workaround on irq %d\n", spi->irq);
|
||||
irq_flags |= IRQF_TRIGGER_RISING;
|
||||
err = request_threaded_irq(spi->irq,
|
||||
ads7846_hard_irq, ads7846_irq,
|
||||
irq_flags, spi->dev.driver->name, ts);
|
||||
err = devm_request_threaded_irq(dev, spi->irq,
|
||||
ads7846_hard_irq, ads7846_irq,
|
||||
irq_flags, dev->driver->name,
|
||||
ts);
|
||||
}
|
||||
|
||||
if (err) {
|
||||
dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq);
|
||||
goto err_disable_regulator;
|
||||
dev_dbg(dev, "irq %d busy?\n", spi->irq);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = ads784x_hwmon_register(spi, ts);
|
||||
if (err)
|
||||
goto err_free_irq;
|
||||
return err;
|
||||
|
||||
dev_info(&spi->dev, "touchscreen, irq %d\n", spi->irq);
|
||||
dev_info(dev, "touchscreen, irq %d\n", spi->irq);
|
||||
|
||||
/*
|
||||
* Take a first sample, leaving nPENIRQ active and vREF off; avoid
|
||||
* the touchscreen, in case it's not connected.
|
||||
*/
|
||||
if (ts->model == 7845)
|
||||
ads7845_read12_ser(&spi->dev, PWRDOWN);
|
||||
ads7845_read12_ser(dev, PWRDOWN);
|
||||
else
|
||||
(void) ads7846_read12_ser(&spi->dev, READ_12BIT_SER(vaux));
|
||||
(void) ads7846_read12_ser(dev, READ_12BIT_SER(vaux));
|
||||
|
||||
err = sysfs_create_group(&spi->dev.kobj, &ads784x_attr_group);
|
||||
err = devm_device_add_group(dev, &ads784x_attr_group);
|
||||
if (err)
|
||||
goto err_remove_hwmon;
|
||||
return err;
|
||||
|
||||
err = input_register_device(input_dev);
|
||||
if (err)
|
||||
goto err_remove_attr_group;
|
||||
return err;
|
||||
|
||||
device_init_wakeup(&spi->dev, pdata->wakeup);
|
||||
device_init_wakeup(dev, pdata->wakeup);
|
||||
|
||||
/*
|
||||
* If device does not carry platform data we must have allocated it
|
||||
* when parsing DT data.
|
||||
*/
|
||||
if (!dev_get_platdata(&spi->dev))
|
||||
devm_kfree(&spi->dev, (void *)pdata);
|
||||
if (!dev_get_platdata(dev))
|
||||
devm_kfree(dev, (void *)pdata);
|
||||
|
||||
return 0;
|
||||
|
||||
err_remove_attr_group:
|
||||
sysfs_remove_group(&spi->dev.kobj, &ads784x_attr_group);
|
||||
err_remove_hwmon:
|
||||
ads784x_hwmon_unregister(spi, ts);
|
||||
err_free_irq:
|
||||
free_irq(spi->irq, ts);
|
||||
err_disable_regulator:
|
||||
regulator_disable(ts->reg);
|
||||
err_put_regulator:
|
||||
regulator_put(ts->reg);
|
||||
err_free_gpio:
|
||||
if (!ts->get_pendown_state)
|
||||
gpio_free(ts->gpio_pendown);
|
||||
err_cleanup_filter:
|
||||
if (ts->filter_cleanup)
|
||||
ts->filter_cleanup(ts->filter_data);
|
||||
err_free_mem:
|
||||
input_free_device(input_dev);
|
||||
kfree(packet);
|
||||
kfree(ts);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ads7846_remove(struct spi_device *spi)
|
||||
{
|
||||
struct ads7846 *ts = spi_get_drvdata(spi);
|
||||
|
||||
sysfs_remove_group(&spi->dev.kobj, &ads784x_attr_group);
|
||||
|
||||
ads7846_disable(ts);
|
||||
free_irq(ts->spi->irq, ts);
|
||||
|
||||
input_unregister_device(ts->input);
|
||||
|
||||
ads784x_hwmon_unregister(spi, ts);
|
||||
|
||||
regulator_put(ts->reg);
|
||||
|
||||
if (!ts->get_pendown_state) {
|
||||
/*
|
||||
* If we are not using specialized pendown method we must
|
||||
* have been relying on gpio we set up ourselves.
|
||||
*/
|
||||
gpio_free(ts->gpio_pendown);
|
||||
}
|
||||
|
||||
if (ts->filter_cleanup)
|
||||
ts->filter_cleanup(ts->filter_data);
|
||||
|
||||
kfree(ts->packet);
|
||||
kfree(ts);
|
||||
|
||||
dev_dbg(&spi->dev, "unregistered touchscreen\n");
|
||||
ads7846_stop(ts);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1439,11 +1439,11 @@ static int elants_i2c_probe(struct i2c_client *client)
|
|||
if (error)
|
||||
return error;
|
||||
|
||||
error = devm_add_action(&client->dev, elants_i2c_power_off, ts);
|
||||
error = devm_add_action_or_reset(&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;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,20 +14,15 @@
|
|||
#include <linux/kernel.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/input/touchscreen.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/of.h>
|
||||
#include <asm/unaligned.h>
|
||||
#include "goodix.h"
|
||||
|
||||
#define GOODIX_GPIO_INT_NAME "irq"
|
||||
#define GOODIX_GPIO_RST_NAME "reset"
|
||||
|
@ -38,22 +33,11 @@
|
|||
#define GOODIX_CONTACT_SIZE 8
|
||||
#define GOODIX_MAX_CONTACT_SIZE 9
|
||||
#define GOODIX_MAX_CONTACTS 10
|
||||
#define GOODIX_MAX_KEYS 7
|
||||
|
||||
#define GOODIX_CONFIG_MIN_LENGTH 186
|
||||
#define GOODIX_CONFIG_911_LENGTH 186
|
||||
#define GOODIX_CONFIG_967_LENGTH 228
|
||||
#define GOODIX_CONFIG_GT9X_LENGTH 240
|
||||
#define GOODIX_CONFIG_MAX_LENGTH 240
|
||||
|
||||
/* Register defines */
|
||||
#define GOODIX_REG_COMMAND 0x8040
|
||||
#define GOODIX_CMD_SCREEN_OFF 0x05
|
||||
|
||||
#define GOODIX_READ_COOR_ADDR 0x814E
|
||||
#define GOODIX_GT1X_REG_CONFIG_DATA 0x8050
|
||||
#define GOODIX_GT9X_REG_CONFIG_DATA 0x8047
|
||||
#define GOODIX_REG_ID 0x8140
|
||||
|
||||
#define GOODIX_BUFFER_STATUS_READY BIT(7)
|
||||
#define GOODIX_HAVE_KEY BIT(4)
|
||||
|
@ -68,55 +52,11 @@
|
|||
#define ACPI_GPIO_SUPPORT
|
||||
#endif
|
||||
|
||||
struct goodix_ts_data;
|
||||
|
||||
enum goodix_irq_pin_access_method {
|
||||
IRQ_PIN_ACCESS_NONE,
|
||||
IRQ_PIN_ACCESS_GPIO,
|
||||
IRQ_PIN_ACCESS_ACPI_GPIO,
|
||||
IRQ_PIN_ACCESS_ACPI_METHOD,
|
||||
};
|
||||
|
||||
struct goodix_chip_data {
|
||||
u16 config_addr;
|
||||
int config_len;
|
||||
int (*check_config)(struct goodix_ts_data *ts, const u8 *cfg, int len);
|
||||
void (*calc_config_checksum)(struct goodix_ts_data *ts);
|
||||
};
|
||||
|
||||
struct goodix_chip_id {
|
||||
const char *id;
|
||||
const struct goodix_chip_data *data;
|
||||
};
|
||||
|
||||
#define GOODIX_ID_MAX_LEN 4
|
||||
|
||||
struct goodix_ts_data {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input_dev;
|
||||
const struct goodix_chip_data *chip;
|
||||
struct touchscreen_properties prop;
|
||||
unsigned int max_touch_num;
|
||||
unsigned int int_trigger_type;
|
||||
struct regulator *avdd28;
|
||||
struct regulator *vddio;
|
||||
struct gpio_desc *gpiod_int;
|
||||
struct gpio_desc *gpiod_rst;
|
||||
int gpio_count;
|
||||
int gpio_int_idx;
|
||||
char id[GOODIX_ID_MAX_LEN + 1];
|
||||
u16 version;
|
||||
const char *cfg_name;
|
||||
bool reset_controller_at_probe;
|
||||
bool load_cfg_from_disk;
|
||||
struct completion firmware_loading_complete;
|
||||
unsigned long irq_flags;
|
||||
enum goodix_irq_pin_access_method irq_pin_access_method;
|
||||
unsigned int contact_size;
|
||||
u8 config[GOODIX_CONFIG_MAX_LENGTH];
|
||||
unsigned short keymap[GOODIX_MAX_KEYS];
|
||||
};
|
||||
|
||||
static int goodix_check_cfg_8(struct goodix_ts_data *ts,
|
||||
const u8 *cfg, int len);
|
||||
static int goodix_check_cfg_16(struct goodix_ts_data *ts,
|
||||
|
@ -260,8 +200,7 @@ static const struct dmi_system_id inverted_x_screen[] = {
|
|||
* @buf: raw write data buffer.
|
||||
* @len: length of the buffer to write
|
||||
*/
|
||||
static int goodix_i2c_read(struct i2c_client *client,
|
||||
u16 reg, u8 *buf, int len)
|
||||
int goodix_i2c_read(struct i2c_client *client, u16 reg, u8 *buf, int len)
|
||||
{
|
||||
struct i2c_msg msgs[2];
|
||||
__be16 wbuf = cpu_to_be16(reg);
|
||||
|
@ -278,7 +217,13 @@ static int goodix_i2c_read(struct i2c_client *client,
|
|||
msgs[1].buf = buf;
|
||||
|
||||
ret = i2c_transfer(client->adapter, msgs, 2);
|
||||
return ret < 0 ? ret : (ret != ARRAY_SIZE(msgs) ? -EIO : 0);
|
||||
if (ret >= 0)
|
||||
ret = (ret == ARRAY_SIZE(msgs) ? 0 : -EIO);
|
||||
|
||||
if (ret)
|
||||
dev_err(&client->dev, "Error reading %d bytes from 0x%04x: %d\n",
|
||||
len, reg, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -289,8 +234,7 @@ static int goodix_i2c_read(struct i2c_client *client,
|
|||
* @buf: raw data buffer to write.
|
||||
* @len: length of the buffer to write
|
||||
*/
|
||||
static int goodix_i2c_write(struct i2c_client *client, u16 reg, const u8 *buf,
|
||||
unsigned len)
|
||||
int goodix_i2c_write(struct i2c_client *client, u16 reg, const u8 *buf, int len)
|
||||
{
|
||||
u8 *addr_buf;
|
||||
struct i2c_msg msg;
|
||||
|
@ -310,11 +254,18 @@ static int goodix_i2c_write(struct i2c_client *client, u16 reg, const u8 *buf,
|
|||
msg.len = len + 2;
|
||||
|
||||
ret = i2c_transfer(client->adapter, &msg, 1);
|
||||
if (ret >= 0)
|
||||
ret = (ret == 1 ? 0 : -EIO);
|
||||
|
||||
kfree(addr_buf);
|
||||
return ret < 0 ? ret : (ret != 1 ? -EIO : 0);
|
||||
|
||||
if (ret)
|
||||
dev_err(&client->dev, "Error writing %d bytes to 0x%04x: %d\n",
|
||||
len, reg, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int goodix_i2c_write_u8(struct i2c_client *client, u16 reg, u8 value)
|
||||
int goodix_i2c_write_u8(struct i2c_client *client, u16 reg, u8 value)
|
||||
{
|
||||
return goodix_i2c_write(client, reg, &value, sizeof(value));
|
||||
}
|
||||
|
@ -353,11 +304,8 @@ static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data)
|
|||
do {
|
||||
error = goodix_i2c_read(ts->client, addr, data,
|
||||
header_contact_keycode_size);
|
||||
if (error) {
|
||||
dev_err(&ts->client->dev, "I2C transfer error: %d\n",
|
||||
error);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
if (data[0] & GOODIX_BUFFER_STATUS_READY) {
|
||||
touch_num = data[0] & 0x0f;
|
||||
|
@ -378,6 +326,11 @@ static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data)
|
|||
return touch_num;
|
||||
}
|
||||
|
||||
if (data[0] == 0 && ts->firmware_name) {
|
||||
if (goodix_handle_fw_request(ts))
|
||||
return 0;
|
||||
}
|
||||
|
||||
usleep_range(1000, 2000); /* Poll every 1 - 2 ms */
|
||||
} while (time_before(jiffies, max_timeout));
|
||||
|
||||
|
@ -480,9 +433,7 @@ static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id)
|
|||
struct goodix_ts_data *ts = dev_id;
|
||||
|
||||
goodix_process_events(ts);
|
||||
|
||||
if (goodix_i2c_write_u8(ts->client, GOODIX_READ_COOR_ADDR, 0) < 0)
|
||||
dev_err(&ts->client->dev, "I2C write end_cmd error\n");
|
||||
goodix_i2c_write_u8(ts->client, GOODIX_READ_COOR_ADDR, 0);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
@ -598,7 +549,7 @@ static int goodix_check_cfg(struct goodix_ts_data *ts, const u8 *cfg, int len)
|
|||
* @cfg: config firmware to write to device
|
||||
* @len: config data length
|
||||
*/
|
||||
static int goodix_send_cfg(struct goodix_ts_data *ts, const u8 *cfg, int len)
|
||||
int goodix_send_cfg(struct goodix_ts_data *ts, const u8 *cfg, int len)
|
||||
{
|
||||
int error;
|
||||
|
||||
|
@ -607,11 +558,9 @@ static int goodix_send_cfg(struct goodix_ts_data *ts, const u8 *cfg, int len)
|
|||
return error;
|
||||
|
||||
error = goodix_i2c_write(ts->client, ts->chip->config_addr, cfg, len);
|
||||
if (error) {
|
||||
dev_err(&ts->client->dev, "Failed to write config data: %d",
|
||||
error);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
dev_dbg(&ts->client->dev, "Config sent successfully.");
|
||||
|
||||
/* Let the firmware reconfigure itself, so sleep for 10ms */
|
||||
|
@ -696,21 +645,66 @@ static int goodix_irq_direction_input(struct goodix_ts_data *ts)
|
|||
return -EINVAL; /* Never reached */
|
||||
}
|
||||
|
||||
static int goodix_int_sync(struct goodix_ts_data *ts)
|
||||
int goodix_int_sync(struct goodix_ts_data *ts)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = goodix_irq_direction_output(ts, 0);
|
||||
if (error)
|
||||
return error;
|
||||
goto error;
|
||||
|
||||
msleep(50); /* T5: 50ms */
|
||||
|
||||
error = goodix_irq_direction_input(ts);
|
||||
if (error)
|
||||
return error;
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
dev_err(&ts->client->dev, "Controller irq sync failed.\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* goodix_reset_no_int_sync - Reset device, leaving interrupt line in output mode
|
||||
*
|
||||
* @ts: goodix_ts_data pointer
|
||||
*/
|
||||
int goodix_reset_no_int_sync(struct goodix_ts_data *ts)
|
||||
{
|
||||
int error;
|
||||
|
||||
/* begin select I2C slave addr */
|
||||
error = gpiod_direction_output(ts->gpiod_rst, 0);
|
||||
if (error)
|
||||
goto error;
|
||||
|
||||
msleep(20); /* T2: > 10ms */
|
||||
|
||||
/* HIGH: 0x28/0x29, LOW: 0xBA/0xBB */
|
||||
error = goodix_irq_direction_output(ts, ts->client->addr == 0x14);
|
||||
if (error)
|
||||
goto error;
|
||||
|
||||
usleep_range(100, 2000); /* T3: > 100us */
|
||||
|
||||
error = gpiod_direction_output(ts->gpiod_rst, 1);
|
||||
if (error)
|
||||
goto error;
|
||||
|
||||
usleep_range(6000, 10000); /* T4: > 5ms */
|
||||
|
||||
/* end select I2C slave addr */
|
||||
error = gpiod_direction_input(ts->gpiod_rst);
|
||||
if (error)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
dev_err(&ts->client->dev, "Controller reset failed.\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -722,36 +716,11 @@ static int goodix_reset(struct goodix_ts_data *ts)
|
|||
{
|
||||
int error;
|
||||
|
||||
/* begin select I2C slave addr */
|
||||
error = gpiod_direction_output(ts->gpiod_rst, 0);
|
||||
error = goodix_reset_no_int_sync(ts);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
msleep(20); /* T2: > 10ms */
|
||||
|
||||
/* HIGH: 0x28/0x29, LOW: 0xBA/0xBB */
|
||||
error = goodix_irq_direction_output(ts, ts->client->addr == 0x14);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
usleep_range(100, 2000); /* T3: > 100us */
|
||||
|
||||
error = gpiod_direction_output(ts->gpiod_rst, 1);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
usleep_range(6000, 10000); /* T4: > 5ms */
|
||||
|
||||
/* end select I2C slave addr */
|
||||
error = gpiod_direction_input(ts->gpiod_rst);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = goodix_int_sync(ts);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
return 0;
|
||||
return goodix_int_sync(ts);
|
||||
}
|
||||
|
||||
#ifdef ACPI_GPIO_SUPPORT
|
||||
|
@ -976,14 +945,19 @@ static void goodix_read_config(struct goodix_ts_data *ts)
|
|||
int x_max, y_max;
|
||||
int error;
|
||||
|
||||
error = goodix_i2c_read(ts->client, ts->chip->config_addr,
|
||||
ts->config, ts->chip->config_len);
|
||||
if (error) {
|
||||
dev_warn(&ts->client->dev, "Error reading config: %d\n",
|
||||
error);
|
||||
ts->int_trigger_type = GOODIX_INT_TRIGGER;
|
||||
ts->max_touch_num = GOODIX_MAX_CONTACTS;
|
||||
return;
|
||||
/*
|
||||
* On controllers where we need to upload the firmware
|
||||
* (controllers without flash) ts->config already has the config
|
||||
* at this point and the controller itself does not have it yet!
|
||||
*/
|
||||
if (!ts->firmware_name) {
|
||||
error = goodix_i2c_read(ts->client, ts->chip->config_addr,
|
||||
ts->config, ts->chip->config_len);
|
||||
if (error) {
|
||||
ts->int_trigger_type = GOODIX_INT_TRIGGER;
|
||||
ts->max_touch_num = GOODIX_MAX_CONTACTS;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ts->int_trigger_type = ts->config[TRIGGER_LOC] & 0x03;
|
||||
|
@ -1011,10 +985,8 @@ static int goodix_read_version(struct goodix_ts_data *ts)
|
|||
char id_str[GOODIX_ID_MAX_LEN + 1];
|
||||
|
||||
error = goodix_i2c_read(ts->client, GOODIX_REG_ID, buf, sizeof(buf));
|
||||
if (error) {
|
||||
dev_err(&ts->client->dev, "read version failed: %d\n", error);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
memcpy(id_str, buf, GOODIX_ID_MAX_LEN);
|
||||
id_str[GOODIX_ID_MAX_LEN] = 0;
|
||||
|
@ -1040,13 +1012,10 @@ static int goodix_i2c_test(struct i2c_client *client)
|
|||
u8 test;
|
||||
|
||||
while (retry++ < 2) {
|
||||
error = goodix_i2c_read(client, GOODIX_REG_ID,
|
||||
&test, 1);
|
||||
error = goodix_i2c_read(client, GOODIX_REG_ID, &test, 1);
|
||||
if (!error)
|
||||
return 0;
|
||||
|
||||
dev_err(&client->dev, "i2c test failed attempt %d: %d\n",
|
||||
retry, error);
|
||||
msleep(20);
|
||||
}
|
||||
|
||||
|
@ -1182,7 +1151,16 @@ static void goodix_config_cb(const struct firmware *cfg, void *ctx)
|
|||
struct goodix_ts_data *ts = ctx;
|
||||
int error;
|
||||
|
||||
if (cfg) {
|
||||
if (ts->firmware_name) {
|
||||
if (!cfg)
|
||||
goto err_release_cfg;
|
||||
|
||||
error = goodix_check_cfg(ts, cfg->data, cfg->size);
|
||||
if (error)
|
||||
goto err_release_cfg;
|
||||
|
||||
memcpy(ts->config, cfg->data, cfg->size);
|
||||
} else if (cfg) {
|
||||
/* send device configuration to the firmware */
|
||||
error = goodix_send_cfg(ts, cfg->data, cfg->size);
|
||||
if (error)
|
||||
|
@ -1208,6 +1186,7 @@ static int goodix_ts_probe(struct i2c_client *client,
|
|||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct goodix_ts_data *ts;
|
||||
const char *cfg_name;
|
||||
int error;
|
||||
|
||||
dev_dbg(&client->dev, "I2C Address: 0x%02x\n", client->addr);
|
||||
|
@ -1257,10 +1236,8 @@ reset:
|
|||
if (ts->reset_controller_at_probe) {
|
||||
/* reset the controller */
|
||||
error = goodix_reset(ts);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "Controller reset failed.\n");
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
error = goodix_i2c_test(client);
|
||||
|
@ -1275,20 +1252,27 @@ reset:
|
|||
return error;
|
||||
}
|
||||
|
||||
error = goodix_read_version(ts);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "Read version failed.\n");
|
||||
error = goodix_firmware_check(ts);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = goodix_read_version(ts);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
ts->chip = goodix_get_chip_data(ts->id);
|
||||
|
||||
if (ts->load_cfg_from_disk) {
|
||||
/* update device config */
|
||||
ts->cfg_name = devm_kasprintf(&client->dev, GFP_KERNEL,
|
||||
"goodix_%s_cfg.bin", ts->id);
|
||||
if (!ts->cfg_name)
|
||||
return -ENOMEM;
|
||||
error = device_property_read_string(&client->dev,
|
||||
"goodix,config-name",
|
||||
&cfg_name);
|
||||
if (!error)
|
||||
snprintf(ts->cfg_name, sizeof(ts->cfg_name),
|
||||
"goodix/%s", cfg_name);
|
||||
else
|
||||
snprintf(ts->cfg_name, sizeof(ts->cfg_name),
|
||||
"goodix_%s_cfg.bin", ts->id);
|
||||
|
||||
error = request_firmware_nowait(THIS_MODULE, true, ts->cfg_name,
|
||||
&client->dev, GFP_KERNEL, ts,
|
||||
|
@ -1338,6 +1322,9 @@ static int __maybe_unused goodix_suspend(struct device *dev)
|
|||
/* Free IRQ as IRQ pin is used as output in the suspend sequence */
|
||||
goodix_free_irq(ts);
|
||||
|
||||
/* Save reference (calibration) info if necessary */
|
||||
goodix_save_bak_ref(ts);
|
||||
|
||||
/* Output LOW on the INT pin for 5 ms */
|
||||
error = goodix_irq_direction_output(ts, 0);
|
||||
if (error) {
|
||||
|
@ -1350,7 +1337,6 @@ static int __maybe_unused goodix_suspend(struct device *dev)
|
|||
error = goodix_i2c_write_u8(ts->client, GOODIX_REG_COMMAND,
|
||||
GOODIX_CMD_SCREEN_OFF);
|
||||
if (error) {
|
||||
dev_err(&ts->client->dev, "Screen off command failed\n");
|
||||
goodix_irq_direction_input(ts);
|
||||
goodix_request_irq(ts);
|
||||
return -EAGAIN;
|
||||
|
@ -1393,19 +1379,14 @@ static int __maybe_unused goodix_resume(struct device *dev)
|
|||
|
||||
error = goodix_i2c_read(ts->client, ts->chip->config_addr,
|
||||
&config_ver, 1);
|
||||
if (error)
|
||||
dev_warn(dev, "Error reading config version: %d, resetting controller\n",
|
||||
error);
|
||||
else if (config_ver != ts->config[0])
|
||||
if (!error && config_ver != ts->config[0])
|
||||
dev_info(dev, "Config version mismatch %d != %d, resetting controller\n",
|
||||
config_ver, ts->config[0]);
|
||||
|
||||
if (error != 0 || config_ver != ts->config[0]) {
|
||||
error = goodix_reset(ts);
|
||||
if (error) {
|
||||
dev_err(dev, "Controller reset failed.\n");
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
error = goodix_send_cfg(ts, ts->config, ts->chip->config_len);
|
||||
if (error)
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
#ifndef __GOODIX_H__
|
||||
#define __GOODIX_H__
|
||||
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/input/touchscreen.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
||||
/* Register defines */
|
||||
#define GOODIX_REG_MISCTL_DSP_CTL 0x4010
|
||||
#define GOODIX_REG_MISCTL_SRAM_BANK 0x4048
|
||||
#define GOODIX_REG_MISCTL_MEM_CD_EN 0x4049
|
||||
#define GOODIX_REG_MISCTL_CACHE_EN 0x404B
|
||||
#define GOODIX_REG_MISCTL_TMR0_EN 0x40B0
|
||||
#define GOODIX_REG_MISCTL_SWRST 0x4180
|
||||
#define GOODIX_REG_MISCTL_CPU_SWRST_PULSE 0x4184
|
||||
#define GOODIX_REG_MISCTL_BOOTCTL 0x4190
|
||||
#define GOODIX_REG_MISCTL_BOOT_OPT 0x4218
|
||||
#define GOODIX_REG_MISCTL_BOOT_CTL 0x5094
|
||||
|
||||
#define GOODIX_REG_FW_SIG 0x8000
|
||||
#define GOODIX_FW_SIG_LEN 10
|
||||
|
||||
#define GOODIX_REG_MAIN_CLK 0x8020
|
||||
#define GOODIX_MAIN_CLK_LEN 6
|
||||
|
||||
#define GOODIX_REG_COMMAND 0x8040
|
||||
#define GOODIX_CMD_SCREEN_OFF 0x05
|
||||
|
||||
#define GOODIX_REG_SW_WDT 0x8041
|
||||
|
||||
#define GOODIX_REG_REQUEST 0x8043
|
||||
#define GOODIX_RQST_RESPONDED 0x00
|
||||
#define GOODIX_RQST_CONFIG 0x01
|
||||
#define GOODIX_RQST_BAK_REF 0x02
|
||||
#define GOODIX_RQST_RESET 0x03
|
||||
#define GOODIX_RQST_MAIN_CLOCK 0x04
|
||||
/*
|
||||
* Unknown request which gets send by the controller aprox.
|
||||
* every 34 seconds once it is up and running.
|
||||
*/
|
||||
#define GOODIX_RQST_UNKNOWN 0x06
|
||||
#define GOODIX_RQST_IDLE 0xFF
|
||||
|
||||
#define GOODIX_REG_STATUS 0x8044
|
||||
|
||||
#define GOODIX_GT1X_REG_CONFIG_DATA 0x8050
|
||||
#define GOODIX_GT9X_REG_CONFIG_DATA 0x8047
|
||||
#define GOODIX_REG_ID 0x8140
|
||||
#define GOODIX_READ_COOR_ADDR 0x814E
|
||||
#define GOODIX_REG_BAK_REF 0x99D0
|
||||
|
||||
#define GOODIX_ID_MAX_LEN 4
|
||||
#define GOODIX_CONFIG_MAX_LENGTH 240
|
||||
#define GOODIX_MAX_KEYS 7
|
||||
|
||||
enum goodix_irq_pin_access_method {
|
||||
IRQ_PIN_ACCESS_NONE,
|
||||
IRQ_PIN_ACCESS_GPIO,
|
||||
IRQ_PIN_ACCESS_ACPI_GPIO,
|
||||
IRQ_PIN_ACCESS_ACPI_METHOD,
|
||||
};
|
||||
|
||||
struct goodix_ts_data;
|
||||
|
||||
struct goodix_chip_data {
|
||||
u16 config_addr;
|
||||
int config_len;
|
||||
int (*check_config)(struct goodix_ts_data *ts, const u8 *cfg, int len);
|
||||
void (*calc_config_checksum)(struct goodix_ts_data *ts);
|
||||
};
|
||||
|
||||
struct goodix_ts_data {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input_dev;
|
||||
const struct goodix_chip_data *chip;
|
||||
const char *firmware_name;
|
||||
struct touchscreen_properties prop;
|
||||
unsigned int max_touch_num;
|
||||
unsigned int int_trigger_type;
|
||||
struct regulator *avdd28;
|
||||
struct regulator *vddio;
|
||||
struct gpio_desc *gpiod_int;
|
||||
struct gpio_desc *gpiod_rst;
|
||||
int gpio_count;
|
||||
int gpio_int_idx;
|
||||
char id[GOODIX_ID_MAX_LEN + 1];
|
||||
char cfg_name[64];
|
||||
u16 version;
|
||||
bool reset_controller_at_probe;
|
||||
bool load_cfg_from_disk;
|
||||
struct completion firmware_loading_complete;
|
||||
unsigned long irq_flags;
|
||||
enum goodix_irq_pin_access_method irq_pin_access_method;
|
||||
unsigned int contact_size;
|
||||
u8 config[GOODIX_CONFIG_MAX_LENGTH];
|
||||
unsigned short keymap[GOODIX_MAX_KEYS];
|
||||
u8 main_clk[GOODIX_MAIN_CLK_LEN];
|
||||
int bak_ref_len;
|
||||
u8 *bak_ref;
|
||||
};
|
||||
|
||||
int goodix_i2c_read(struct i2c_client *client, u16 reg, u8 *buf, int len);
|
||||
int goodix_i2c_write(struct i2c_client *client, u16 reg, const u8 *buf, int len);
|
||||
int goodix_i2c_write_u8(struct i2c_client *client, u16 reg, u8 value);
|
||||
int goodix_send_cfg(struct goodix_ts_data *ts, const u8 *cfg, int len);
|
||||
int goodix_int_sync(struct goodix_ts_data *ts);
|
||||
int goodix_reset_no_int_sync(struct goodix_ts_data *ts);
|
||||
|
||||
int goodix_firmware_check(struct goodix_ts_data *ts);
|
||||
bool goodix_handle_fw_request(struct goodix_ts_data *ts);
|
||||
void goodix_save_bak_ref(struct goodix_ts_data *ts);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,427 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Goodix Touchscreen firmware upload support
|
||||
*
|
||||
* Copyright (c) 2021 Hans de Goede <hdegoede@redhat.com>
|
||||
*
|
||||
* This is a rewrite of gt9xx_update.c from the Allwinner H3 BSP which is:
|
||||
* Copyright (c) 2010 - 2012 Goodix Technology.
|
||||
* Author: andrew@goodix.com
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/i2c.h>
|
||||
#include "goodix.h"
|
||||
|
||||
#define GOODIX_FW_HEADER_LENGTH sizeof(struct goodix_fw_header)
|
||||
#define GOODIX_FW_SECTION_LENGTH 0x2000
|
||||
#define GOODIX_FW_DSP_LENGTH 0x1000
|
||||
#define GOODIX_FW_UPLOAD_ADDRESS 0xc000
|
||||
|
||||
#define GOODIX_CFG_LOC_HAVE_KEY 7
|
||||
#define GOODIX_CFG_LOC_DRVA_NUM 27
|
||||
#define GOODIX_CFG_LOC_DRVB_NUM 28
|
||||
#define GOODIX_CFG_LOC_SENS_NUM 29
|
||||
|
||||
struct goodix_fw_header {
|
||||
u8 hw_info[4];
|
||||
u8 pid[8];
|
||||
u8 vid[2];
|
||||
} __packed;
|
||||
|
||||
static u16 goodix_firmware_checksum(const u8 *data, int size)
|
||||
{
|
||||
u16 checksum = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < size; i += 2)
|
||||
checksum += (data[i] << 8) + data[i + 1];
|
||||
|
||||
return checksum;
|
||||
}
|
||||
|
||||
static int goodix_firmware_verify(struct device *dev, const struct firmware *fw)
|
||||
{
|
||||
const struct goodix_fw_header *fw_header;
|
||||
size_t expected_size;
|
||||
const u8 *data;
|
||||
u16 checksum;
|
||||
char buf[9];
|
||||
|
||||
expected_size = GOODIX_FW_HEADER_LENGTH + 4 * GOODIX_FW_SECTION_LENGTH +
|
||||
GOODIX_FW_DSP_LENGTH;
|
||||
if (fw->size != expected_size) {
|
||||
dev_err(dev, "Firmware has wrong size, expected %zu got %zu\n",
|
||||
expected_size, fw->size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data = fw->data + GOODIX_FW_HEADER_LENGTH;
|
||||
checksum = goodix_firmware_checksum(data, 4 * GOODIX_FW_SECTION_LENGTH);
|
||||
if (checksum) {
|
||||
dev_err(dev, "Main firmware checksum error\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
data += 4 * GOODIX_FW_SECTION_LENGTH;
|
||||
checksum = goodix_firmware_checksum(data, GOODIX_FW_DSP_LENGTH);
|
||||
if (checksum) {
|
||||
dev_err(dev, "DSP firmware checksum error\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
fw_header = (const struct goodix_fw_header *)fw->data;
|
||||
dev_info(dev, "Firmware hardware info %02x%02x%02x%02x\n",
|
||||
fw_header->hw_info[0], fw_header->hw_info[1],
|
||||
fw_header->hw_info[2], fw_header->hw_info[3]);
|
||||
/* pid is a 8 byte buffer containing a string, weird I know */
|
||||
memcpy(buf, fw_header->pid, 8);
|
||||
buf[8] = 0;
|
||||
dev_info(dev, "Firmware PID: %s VID: %02x%02x\n", buf,
|
||||
fw_header->vid[0], fw_header->vid[1]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int goodix_enter_upload_mode(struct i2c_client *client)
|
||||
{
|
||||
int tries, error;
|
||||
u8 val;
|
||||
|
||||
tries = 200;
|
||||
do {
|
||||
error = goodix_i2c_write_u8(client,
|
||||
GOODIX_REG_MISCTL_SWRST, 0x0c);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = goodix_i2c_read(client,
|
||||
GOODIX_REG_MISCTL_SWRST, &val, 1);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (val == 0x0c)
|
||||
break;
|
||||
} while (--tries);
|
||||
|
||||
if (!tries) {
|
||||
dev_err(&client->dev, "Error could not hold ss51 & dsp\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* DSP_CK and DSP_ALU_CK PowerOn */
|
||||
error = goodix_i2c_write_u8(client, GOODIX_REG_MISCTL_DSP_CTL, 0x00);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/* Disable watchdog */
|
||||
error = goodix_i2c_write_u8(client, GOODIX_REG_MISCTL_TMR0_EN, 0x00);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/* Clear cache enable */
|
||||
error = goodix_i2c_write_u8(client, GOODIX_REG_MISCTL_CACHE_EN, 0x00);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/* Set boot from SRAM */
|
||||
error = goodix_i2c_write_u8(client, GOODIX_REG_MISCTL_BOOTCTL, 0x02);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/* Software reboot */
|
||||
error = goodix_i2c_write_u8(client,
|
||||
GOODIX_REG_MISCTL_CPU_SWRST_PULSE, 0x01);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/* Clear control flag */
|
||||
error = goodix_i2c_write_u8(client, GOODIX_REG_MISCTL_BOOTCTL, 0x00);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/* Set scramble */
|
||||
error = goodix_i2c_write_u8(client, GOODIX_REG_MISCTL_BOOT_OPT, 0x00);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/* Enable accessing code */
|
||||
error = goodix_i2c_write_u8(client, GOODIX_REG_MISCTL_MEM_CD_EN, 0x01);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int goodix_start_firmware(struct i2c_client *client)
|
||||
{
|
||||
int error;
|
||||
u8 val;
|
||||
|
||||
/* Init software watchdog */
|
||||
error = goodix_i2c_write_u8(client, GOODIX_REG_SW_WDT, 0xaa);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/* Release SS51 & DSP */
|
||||
error = goodix_i2c_write_u8(client, GOODIX_REG_MISCTL_SWRST, 0x00);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = goodix_i2c_read(client, GOODIX_REG_SW_WDT, &val, 1);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/* The value we've written to SW_WDT should have been cleared now */
|
||||
if (val == 0xaa) {
|
||||
dev_err(&client->dev, "Error SW_WDT reg not cleared on fw startup\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Re-init software watchdog */
|
||||
error = goodix_i2c_write_u8(client, GOODIX_REG_SW_WDT, 0xaa);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int goodix_firmware_upload(struct goodix_ts_data *ts)
|
||||
{
|
||||
const struct firmware *fw;
|
||||
char fw_name[64];
|
||||
const u8 *data;
|
||||
int error;
|
||||
|
||||
snprintf(fw_name, sizeof(fw_name), "goodix/%s", ts->firmware_name);
|
||||
|
||||
error = request_firmware(&fw, fw_name, &ts->client->dev);
|
||||
if (error) {
|
||||
dev_err(&ts->client->dev, "Firmware request error %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = goodix_firmware_verify(&ts->client->dev, fw);
|
||||
if (error)
|
||||
goto release;
|
||||
|
||||
error = goodix_reset_no_int_sync(ts);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = goodix_enter_upload_mode(ts->client);
|
||||
if (error)
|
||||
goto release;
|
||||
|
||||
/* Select SRAM bank 0 and upload section 1 & 2 */
|
||||
error = goodix_i2c_write_u8(ts->client,
|
||||
GOODIX_REG_MISCTL_SRAM_BANK, 0x00);
|
||||
if (error)
|
||||
goto release;
|
||||
|
||||
data = fw->data + GOODIX_FW_HEADER_LENGTH;
|
||||
error = goodix_i2c_write(ts->client, GOODIX_FW_UPLOAD_ADDRESS,
|
||||
data, 2 * GOODIX_FW_SECTION_LENGTH);
|
||||
if (error)
|
||||
goto release;
|
||||
|
||||
/* Select SRAM bank 1 and upload section 3 & 4 */
|
||||
error = goodix_i2c_write_u8(ts->client,
|
||||
GOODIX_REG_MISCTL_SRAM_BANK, 0x01);
|
||||
if (error)
|
||||
goto release;
|
||||
|
||||
data += 2 * GOODIX_FW_SECTION_LENGTH;
|
||||
error = goodix_i2c_write(ts->client, GOODIX_FW_UPLOAD_ADDRESS,
|
||||
data, 2 * GOODIX_FW_SECTION_LENGTH);
|
||||
if (error)
|
||||
goto release;
|
||||
|
||||
/* Select SRAM bank 2 and upload the DSP firmware */
|
||||
error = goodix_i2c_write_u8(ts->client,
|
||||
GOODIX_REG_MISCTL_SRAM_BANK, 0x02);
|
||||
if (error)
|
||||
goto release;
|
||||
|
||||
data += 2 * GOODIX_FW_SECTION_LENGTH;
|
||||
error = goodix_i2c_write(ts->client, GOODIX_FW_UPLOAD_ADDRESS,
|
||||
data, GOODIX_FW_DSP_LENGTH);
|
||||
if (error)
|
||||
goto release;
|
||||
|
||||
error = goodix_start_firmware(ts->client);
|
||||
if (error)
|
||||
goto release;
|
||||
|
||||
error = goodix_int_sync(ts);
|
||||
release:
|
||||
release_firmware(fw);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int goodix_prepare_bak_ref(struct goodix_ts_data *ts)
|
||||
{
|
||||
u8 have_key, driver_num, sensor_num;
|
||||
|
||||
if (ts->bak_ref)
|
||||
return 0; /* Already done */
|
||||
|
||||
have_key = (ts->config[GOODIX_CFG_LOC_HAVE_KEY] & 0x01);
|
||||
|
||||
driver_num = (ts->config[GOODIX_CFG_LOC_DRVA_NUM] & 0x1f) +
|
||||
(ts->config[GOODIX_CFG_LOC_DRVB_NUM] & 0x1f);
|
||||
if (have_key)
|
||||
driver_num--;
|
||||
|
||||
sensor_num = (ts->config[GOODIX_CFG_LOC_SENS_NUM] & 0x0f) +
|
||||
((ts->config[GOODIX_CFG_LOC_SENS_NUM] >> 4) & 0x0f);
|
||||
|
||||
dev_dbg(&ts->client->dev, "Drv %d Sen %d Key %d\n",
|
||||
driver_num, sensor_num, have_key);
|
||||
|
||||
ts->bak_ref_len = (driver_num * (sensor_num - 2) + 2) * 2;
|
||||
|
||||
ts->bak_ref = devm_kzalloc(&ts->client->dev,
|
||||
ts->bak_ref_len, GFP_KERNEL);
|
||||
if (!ts->bak_ref)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* The bak_ref array contains the backup of an array of (self/auto)
|
||||
* calibration related values which the Android version of the driver
|
||||
* stores on the filesystem so that it can be restored after reboot.
|
||||
* The mainline kernel never writes directly to the filesystem like
|
||||
* this, we always start will all the values which give a correction
|
||||
* factor in approx. the -20 - +20 range (in 2s complement) set to 0.
|
||||
*
|
||||
* Note the touchscreen works fine without restoring the reference
|
||||
* values after a reboot / power-cycle.
|
||||
*
|
||||
* The last 2 bytes are a 16 bits unsigned checksum which is expected
|
||||
* to make the addition al all 16 bit unsigned values in the array add
|
||||
* up to 1 (rather then the usual 0), so we must set the last byte to 1.
|
||||
*/
|
||||
ts->bak_ref[ts->bak_ref_len - 1] = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int goodix_send_main_clock(struct goodix_ts_data *ts)
|
||||
{
|
||||
u32 main_clk = 54; /* Default main clock */
|
||||
u8 checksum = 0;
|
||||
int i;
|
||||
|
||||
device_property_read_u32(&ts->client->dev,
|
||||
"goodix,main-clk", &main_clk);
|
||||
|
||||
for (i = 0; i < (GOODIX_MAIN_CLK_LEN - 1); i++) {
|
||||
ts->main_clk[i] = main_clk;
|
||||
checksum += main_clk;
|
||||
}
|
||||
|
||||
/* The value of all bytes combines must be 0 */
|
||||
ts->main_clk[GOODIX_MAIN_CLK_LEN - 1] = 256 - checksum;
|
||||
|
||||
return goodix_i2c_write(ts->client, GOODIX_REG_MAIN_CLK,
|
||||
ts->main_clk, GOODIX_MAIN_CLK_LEN);
|
||||
}
|
||||
|
||||
int goodix_firmware_check(struct goodix_ts_data *ts)
|
||||
{
|
||||
device_property_read_string(&ts->client->dev,
|
||||
"firmware-name", &ts->firmware_name);
|
||||
if (!ts->firmware_name)
|
||||
return 0;
|
||||
|
||||
if (ts->irq_pin_access_method == IRQ_PIN_ACCESS_NONE) {
|
||||
dev_err(&ts->client->dev, "Error no IRQ-pin access method, cannot upload fw.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
dev_info(&ts->client->dev, "Touchscreen controller needs fw-upload\n");
|
||||
ts->load_cfg_from_disk = true;
|
||||
|
||||
return goodix_firmware_upload(ts);
|
||||
}
|
||||
|
||||
bool goodix_handle_fw_request(struct goodix_ts_data *ts)
|
||||
{
|
||||
int error;
|
||||
u8 val;
|
||||
|
||||
error = goodix_i2c_read(ts->client, GOODIX_REG_REQUEST, &val, 1);
|
||||
if (error)
|
||||
return false;
|
||||
|
||||
switch (val) {
|
||||
case GOODIX_RQST_RESPONDED:
|
||||
/*
|
||||
* If we read back our own last ack the IRQ was not for
|
||||
* a request.
|
||||
*/
|
||||
return false;
|
||||
case GOODIX_RQST_CONFIG:
|
||||
error = goodix_send_cfg(ts, ts->config, ts->chip->config_len);
|
||||
if (error)
|
||||
return false;
|
||||
|
||||
break;
|
||||
case GOODIX_RQST_BAK_REF:
|
||||
error = goodix_prepare_bak_ref(ts);
|
||||
if (error)
|
||||
return false;
|
||||
|
||||
error = goodix_i2c_write(ts->client, GOODIX_REG_BAK_REF,
|
||||
ts->bak_ref, ts->bak_ref_len);
|
||||
if (error)
|
||||
return false;
|
||||
|
||||
break;
|
||||
case GOODIX_RQST_RESET:
|
||||
error = goodix_firmware_upload(ts);
|
||||
if (error)
|
||||
return false;
|
||||
|
||||
break;
|
||||
case GOODIX_RQST_MAIN_CLOCK:
|
||||
error = goodix_send_main_clock(ts);
|
||||
if (error)
|
||||
return false;
|
||||
|
||||
break;
|
||||
case GOODIX_RQST_UNKNOWN:
|
||||
case GOODIX_RQST_IDLE:
|
||||
break;
|
||||
default:
|
||||
dev_err_ratelimited(&ts->client->dev, "Unknown Request: 0x%02x\n", val);
|
||||
}
|
||||
|
||||
/* Ack the request */
|
||||
goodix_i2c_write_u8(ts->client,
|
||||
GOODIX_REG_REQUEST, GOODIX_RQST_RESPONDED);
|
||||
return true;
|
||||
}
|
||||
|
||||
void goodix_save_bak_ref(struct goodix_ts_data *ts)
|
||||
{
|
||||
int error;
|
||||
u8 val;
|
||||
|
||||
if (!ts->firmware_name)
|
||||
return;
|
||||
|
||||
error = goodix_i2c_read(ts->client, GOODIX_REG_STATUS, &val, 1);
|
||||
if (error)
|
||||
return;
|
||||
|
||||
if (!(val & 0x80))
|
||||
return;
|
||||
|
||||
error = goodix_i2c_read(ts->client, GOODIX_REG_BAK_REF,
|
||||
ts->bak_ref, ts->bak_ref_len);
|
||||
if (error) {
|
||||
memset(ts->bak_ref, 0, ts->bak_ref_len);
|
||||
ts->bak_ref[ts->bak_ref_len - 1] = 1;
|
||||
}
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
#include <linux/crc-ccitt.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/ihex.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/input/touchscreen.h>
|
||||
|
@ -12,7 +14,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
#define ILI2XXX_POLL_PERIOD 20
|
||||
#define ILI2XXX_POLL_PERIOD 15
|
||||
|
||||
#define ILI210X_DATA_SIZE 64
|
||||
#define ILI211X_DATA_SIZE 43
|
||||
|
@ -22,8 +24,23 @@
|
|||
/* Touchscreen commands */
|
||||
#define REG_TOUCHDATA 0x10
|
||||
#define REG_PANEL_INFO 0x20
|
||||
#define REG_FIRMWARE_VERSION 0x40
|
||||
#define REG_PROTOCOL_VERSION 0x42
|
||||
#define REG_KERNEL_VERSION 0x61
|
||||
#define REG_IC_BUSY 0x80
|
||||
#define REG_IC_BUSY_NOT_BUSY 0x50
|
||||
#define REG_GET_MODE 0xc0
|
||||
#define REG_GET_MODE_AP 0x5a
|
||||
#define REG_GET_MODE_BL 0x55
|
||||
#define REG_SET_MODE_AP 0xc1
|
||||
#define REG_SET_MODE_BL 0xc2
|
||||
#define REG_WRITE_DATA 0xc3
|
||||
#define REG_WRITE_ENABLE 0xc4
|
||||
#define REG_READ_DATA_CRC 0xc7
|
||||
#define REG_CALIBRATE 0xcc
|
||||
|
||||
#define ILI251X_FW_FILENAME "ilitek/ili251x.bin"
|
||||
|
||||
struct ili2xxx_chip {
|
||||
int (*read_reg)(struct i2c_client *client, u8 reg,
|
||||
void *buf, size_t len);
|
||||
|
@ -35,6 +52,7 @@ struct ili2xxx_chip {
|
|||
unsigned int max_touches;
|
||||
unsigned int resolution;
|
||||
bool has_calibrate_reg;
|
||||
bool has_firmware_proto;
|
||||
bool has_pressure_reg;
|
||||
};
|
||||
|
||||
|
@ -44,6 +62,10 @@ struct ili210x {
|
|||
struct gpio_desc *reset_gpio;
|
||||
struct touchscreen_properties prop;
|
||||
const struct ili2xxx_chip *chip;
|
||||
u8 version_firmware[8];
|
||||
u8 version_kernel[5];
|
||||
u8 version_proto[2];
|
||||
u8 ic_mode[2];
|
||||
bool stop;
|
||||
};
|
||||
|
||||
|
@ -202,15 +224,17 @@ static const struct ili2xxx_chip ili212x_chip = {
|
|||
.has_calibrate_reg = true,
|
||||
};
|
||||
|
||||
static int ili251x_read_reg(struct i2c_client *client,
|
||||
u8 reg, void *buf, size_t len)
|
||||
static int ili251x_read_reg_common(struct i2c_client *client,
|
||||
u8 reg, void *buf, size_t len,
|
||||
unsigned int delay)
|
||||
{
|
||||
int error;
|
||||
int ret;
|
||||
|
||||
ret = i2c_master_send(client, ®, 1);
|
||||
if (ret == 1) {
|
||||
usleep_range(5000, 5500);
|
||||
if (delay)
|
||||
usleep_range(delay, delay + 500);
|
||||
|
||||
ret = i2c_master_recv(client, buf, len);
|
||||
if (ret == len)
|
||||
|
@ -222,12 +246,18 @@ static int ili251x_read_reg(struct i2c_client *client,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int ili251x_read_reg(struct i2c_client *client,
|
||||
u8 reg, void *buf, size_t len)
|
||||
{
|
||||
return ili251x_read_reg_common(client, reg, buf, len, 5000);
|
||||
}
|
||||
|
||||
static int ili251x_read_touch_data(struct i2c_client *client, u8 *data)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = ili251x_read_reg(client, REG_TOUCHDATA,
|
||||
data, ILI251X_DATA_SIZE1);
|
||||
error = ili251x_read_reg_common(client, REG_TOUCHDATA,
|
||||
data, ILI251X_DATA_SIZE1, 0);
|
||||
if (!error && data[0] == 2) {
|
||||
error = i2c_master_recv(client, data + ILI251X_DATA_SIZE1,
|
||||
ILI251X_DATA_SIZE2);
|
||||
|
@ -268,6 +298,7 @@ static const struct ili2xxx_chip ili251x_chip = {
|
|||
.continue_polling = ili251x_check_continue_polling,
|
||||
.max_touches = 10,
|
||||
.has_calibrate_reg = true,
|
||||
.has_firmware_proto = true,
|
||||
.has_pressure_reg = true,
|
||||
};
|
||||
|
||||
|
@ -303,10 +334,13 @@ static irqreturn_t ili210x_irq(int irq, void *irq_data)
|
|||
const struct ili2xxx_chip *chip = priv->chip;
|
||||
u8 touchdata[ILI210X_DATA_SIZE] = { 0 };
|
||||
bool keep_polling;
|
||||
ktime_t time_next;
|
||||
s64 time_delta;
|
||||
bool touch;
|
||||
int error;
|
||||
|
||||
do {
|
||||
time_next = ktime_add_ms(ktime_get(), ILI2XXX_POLL_PERIOD);
|
||||
error = chip->get_touch_data(client, touchdata);
|
||||
if (error) {
|
||||
dev_err(&client->dev,
|
||||
|
@ -316,13 +350,201 @@ static irqreturn_t ili210x_irq(int irq, void *irq_data)
|
|||
|
||||
touch = ili210x_report_events(priv, touchdata);
|
||||
keep_polling = chip->continue_polling(touchdata, touch);
|
||||
if (keep_polling)
|
||||
msleep(ILI2XXX_POLL_PERIOD);
|
||||
if (keep_polling) {
|
||||
time_delta = ktime_us_delta(time_next, ktime_get());
|
||||
if (time_delta > 0)
|
||||
usleep_range(time_delta, time_delta + 1000);
|
||||
}
|
||||
} while (!priv->stop && keep_polling);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int ili251x_firmware_update_resolution(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct ili210x *priv = i2c_get_clientdata(client);
|
||||
u16 resx, resy;
|
||||
u8 rs[10];
|
||||
int error;
|
||||
|
||||
/* The firmware update blob might have changed the resolution. */
|
||||
error = priv->chip->read_reg(client, REG_PANEL_INFO, &rs, sizeof(rs));
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
resx = le16_to_cpup((__le16 *)rs);
|
||||
resy = le16_to_cpup((__le16 *)(rs + 2));
|
||||
|
||||
/* The value reported by the firmware is invalid. */
|
||||
if (!resx || resx == 0xffff || !resy || resy == 0xffff)
|
||||
return -EINVAL;
|
||||
|
||||
input_abs_set_max(priv->input, ABS_X, resx - 1);
|
||||
input_abs_set_max(priv->input, ABS_Y, resy - 1);
|
||||
input_abs_set_max(priv->input, ABS_MT_POSITION_X, resx - 1);
|
||||
input_abs_set_max(priv->input, ABS_MT_POSITION_Y, resy - 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t ili251x_firmware_update_firmware_version(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct ili210x *priv = i2c_get_clientdata(client);
|
||||
int error;
|
||||
u8 fw[8];
|
||||
|
||||
/* Get firmware version */
|
||||
error = priv->chip->read_reg(client, REG_FIRMWARE_VERSION,
|
||||
&fw, sizeof(fw));
|
||||
if (!error)
|
||||
memcpy(priv->version_firmware, fw, sizeof(fw));
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static ssize_t ili251x_firmware_update_kernel_version(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct ili210x *priv = i2c_get_clientdata(client);
|
||||
int error;
|
||||
u8 kv[5];
|
||||
|
||||
/* Get kernel version */
|
||||
error = priv->chip->read_reg(client, REG_KERNEL_VERSION,
|
||||
&kv, sizeof(kv));
|
||||
if (!error)
|
||||
memcpy(priv->version_kernel, kv, sizeof(kv));
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static ssize_t ili251x_firmware_update_protocol_version(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct ili210x *priv = i2c_get_clientdata(client);
|
||||
int error;
|
||||
u8 pv[2];
|
||||
|
||||
/* Get protocol version */
|
||||
error = priv->chip->read_reg(client, REG_PROTOCOL_VERSION,
|
||||
&pv, sizeof(pv));
|
||||
if (!error)
|
||||
memcpy(priv->version_proto, pv, sizeof(pv));
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static ssize_t ili251x_firmware_update_ic_mode(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct ili210x *priv = i2c_get_clientdata(client);
|
||||
int error;
|
||||
u8 md[2];
|
||||
|
||||
/* Get chip boot mode */
|
||||
error = priv->chip->read_reg(client, REG_GET_MODE, &md, sizeof(md));
|
||||
if (!error)
|
||||
memcpy(priv->ic_mode, md, sizeof(md));
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int ili251x_firmware_update_cached_state(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct ili210x *priv = i2c_get_clientdata(client);
|
||||
int error;
|
||||
|
||||
if (!priv->chip->has_firmware_proto)
|
||||
return 0;
|
||||
|
||||
/* Wait for firmware to boot and stabilize itself. */
|
||||
msleep(200);
|
||||
|
||||
/* Firmware does report valid information. */
|
||||
error = ili251x_firmware_update_resolution(dev);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = ili251x_firmware_update_firmware_version(dev);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = ili251x_firmware_update_kernel_version(dev);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = ili251x_firmware_update_protocol_version(dev);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = ili251x_firmware_update_ic_mode(dev);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t ili251x_firmware_version_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct ili210x *priv = i2c_get_clientdata(client);
|
||||
u8 *fw = priv->version_firmware;
|
||||
|
||||
return sysfs_emit(buf, "%02x%02x.%02x%02x.%02x%02x.%02x%02x\n",
|
||||
fw[0], fw[1], fw[2], fw[3],
|
||||
fw[4], fw[5], fw[6], fw[7]);
|
||||
}
|
||||
static DEVICE_ATTR(firmware_version, 0444, ili251x_firmware_version_show, NULL);
|
||||
|
||||
static ssize_t ili251x_kernel_version_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct ili210x *priv = i2c_get_clientdata(client);
|
||||
u8 *kv = priv->version_kernel;
|
||||
|
||||
return sysfs_emit(buf, "%02x.%02x.%02x.%02x.%02x\n",
|
||||
kv[0], kv[1], kv[2], kv[3], kv[4]);
|
||||
}
|
||||
static DEVICE_ATTR(kernel_version, 0444, ili251x_kernel_version_show, NULL);
|
||||
|
||||
static ssize_t ili251x_protocol_version_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct ili210x *priv = i2c_get_clientdata(client);
|
||||
u8 *pv = priv->version_proto;
|
||||
|
||||
return sysfs_emit(buf, "%02x.%02x\n", pv[0], pv[1]);
|
||||
}
|
||||
static DEVICE_ATTR(protocol_version, 0444, ili251x_protocol_version_show, NULL);
|
||||
|
||||
static ssize_t ili251x_mode_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct ili210x *priv = i2c_get_clientdata(client);
|
||||
u8 *md = priv->ic_mode;
|
||||
char *mode = "AP";
|
||||
|
||||
if (md[0] == REG_GET_MODE_AP) /* Application Mode */
|
||||
mode = "AP";
|
||||
else if (md[0] == REG_GET_MODE_BL) /* BootLoader Mode */
|
||||
mode = "BL";
|
||||
else /* Unknown Mode */
|
||||
mode = "??";
|
||||
|
||||
return sysfs_emit(buf, "%02x.%02x:%s\n", md[0], md[1], mode);
|
||||
}
|
||||
static DEVICE_ATTR(mode, 0444, ili251x_mode_show, NULL);
|
||||
|
||||
static ssize_t ili210x_calibrate(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
|
@ -349,24 +571,333 @@ static ssize_t ili210x_calibrate(struct device *dev,
|
|||
}
|
||||
static DEVICE_ATTR(calibrate, S_IWUSR, NULL, ili210x_calibrate);
|
||||
|
||||
static int ili251x_firmware_to_buffer(const struct firmware *fw,
|
||||
u8 **buf, u16 *ac_end, u16 *df_end)
|
||||
{
|
||||
const struct ihex_binrec *rec;
|
||||
u32 fw_addr, fw_last_addr = 0;
|
||||
u16 fw_len;
|
||||
u8 *fw_buf;
|
||||
int error;
|
||||
|
||||
/*
|
||||
* The firmware ihex blob can never be bigger than 64 kiB, so make this
|
||||
* simple -- allocate a 64 kiB buffer, iterate over the ihex blob records
|
||||
* once, copy them all into this buffer at the right locations, and then
|
||||
* do all operations on this linear buffer.
|
||||
*/
|
||||
fw_buf = kzalloc(SZ_64K, GFP_KERNEL);
|
||||
if (!fw_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
rec = (const struct ihex_binrec *)fw->data;
|
||||
while (rec) {
|
||||
fw_addr = be32_to_cpu(rec->addr);
|
||||
fw_len = be16_to_cpu(rec->len);
|
||||
|
||||
/* The last 32 Byte firmware block can be 0xffe0 */
|
||||
if (fw_addr + fw_len > SZ_64K || fw_addr > SZ_64K - 32) {
|
||||
error = -EFBIG;
|
||||
goto err_big;
|
||||
}
|
||||
|
||||
/* Find the last address before DF start address, that is AC end */
|
||||
if (fw_addr == 0xf000)
|
||||
*ac_end = fw_last_addr;
|
||||
fw_last_addr = fw_addr + fw_len;
|
||||
|
||||
memcpy(fw_buf + fw_addr, rec->data, fw_len);
|
||||
rec = ihex_next_binrec(rec);
|
||||
}
|
||||
|
||||
/* DF end address is the last address in the firmware blob */
|
||||
*df_end = fw_addr + fw_len;
|
||||
*buf = fw_buf;
|
||||
return 0;
|
||||
|
||||
err_big:
|
||||
kfree(fw_buf);
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Switch mode between Application and BootLoader */
|
||||
static int ili251x_switch_ic_mode(struct i2c_client *client, u8 cmd_mode)
|
||||
{
|
||||
struct ili210x *priv = i2c_get_clientdata(client);
|
||||
u8 cmd_wren[3] = { REG_WRITE_ENABLE, 0x5a, 0xa5 };
|
||||
u8 md[2];
|
||||
int error;
|
||||
|
||||
error = priv->chip->read_reg(client, REG_GET_MODE, md, sizeof(md));
|
||||
if (error)
|
||||
return error;
|
||||
/* Mode already set */
|
||||
if ((cmd_mode == REG_SET_MODE_AP && md[0] == REG_GET_MODE_AP) ||
|
||||
(cmd_mode == REG_SET_MODE_BL && md[0] == REG_GET_MODE_BL))
|
||||
return 0;
|
||||
|
||||
/* Unlock writes */
|
||||
error = i2c_master_send(client, cmd_wren, sizeof(cmd_wren));
|
||||
if (error != sizeof(cmd_wren))
|
||||
return -EINVAL;
|
||||
|
||||
mdelay(20);
|
||||
|
||||
/* Select mode (BootLoader or Application) */
|
||||
error = i2c_master_send(client, &cmd_mode, 1);
|
||||
if (error != 1)
|
||||
return -EINVAL;
|
||||
|
||||
mdelay(200); /* Reboot into bootloader takes a lot of time ... */
|
||||
|
||||
/* Read back mode */
|
||||
error = priv->chip->read_reg(client, REG_GET_MODE, md, sizeof(md));
|
||||
if (error)
|
||||
return error;
|
||||
/* Check if mode is correct now. */
|
||||
if ((cmd_mode == REG_SET_MODE_AP && md[0] == REG_GET_MODE_AP) ||
|
||||
(cmd_mode == REG_SET_MODE_BL && md[0] == REG_GET_MODE_BL))
|
||||
return 0;
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int ili251x_firmware_busy(struct i2c_client *client)
|
||||
{
|
||||
struct ili210x *priv = i2c_get_clientdata(client);
|
||||
int error, i = 0;
|
||||
u8 data;
|
||||
|
||||
do {
|
||||
/* The read_reg already contains suitable delay */
|
||||
error = priv->chip->read_reg(client, REG_IC_BUSY, &data, 1);
|
||||
if (error)
|
||||
return error;
|
||||
if (i++ == 100000)
|
||||
return -ETIMEDOUT;
|
||||
} while (data != REG_IC_BUSY_NOT_BUSY);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ili251x_firmware_write_to_ic(struct device *dev, u8 *fwbuf,
|
||||
u16 start, u16 end, u8 dataflash)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct ili210x *priv = i2c_get_clientdata(client);
|
||||
u8 cmd_crc = REG_READ_DATA_CRC;
|
||||
u8 crcrb[4] = { 0 };
|
||||
u8 fw_data[33];
|
||||
u16 fw_addr;
|
||||
int error;
|
||||
|
||||
/*
|
||||
* The DF (dataflash) needs 2 bytes offset for unknown reasons,
|
||||
* the AC (application) has 2 bytes CRC16-CCITT at the end.
|
||||
*/
|
||||
u16 crc = crc_ccitt(0, fwbuf + start + (dataflash ? 2 : 0),
|
||||
end - start - 2);
|
||||
|
||||
/* Unlock write to either AC (application) or DF (dataflash) area */
|
||||
u8 cmd_wr[10] = {
|
||||
REG_WRITE_ENABLE, 0x5a, 0xa5, dataflash,
|
||||
(end >> 16) & 0xff, (end >> 8) & 0xff, end & 0xff,
|
||||
(crc >> 16) & 0xff, (crc >> 8) & 0xff, crc & 0xff
|
||||
};
|
||||
|
||||
error = i2c_master_send(client, cmd_wr, sizeof(cmd_wr));
|
||||
if (error != sizeof(cmd_wr))
|
||||
return -EINVAL;
|
||||
|
||||
error = ili251x_firmware_busy(client);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
for (fw_addr = start; fw_addr < end; fw_addr += 32) {
|
||||
fw_data[0] = REG_WRITE_DATA;
|
||||
memcpy(&(fw_data[1]), fwbuf + fw_addr, 32);
|
||||
error = i2c_master_send(client, fw_data, 33);
|
||||
if (error != sizeof(fw_data))
|
||||
return error;
|
||||
error = ili251x_firmware_busy(client);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
error = i2c_master_send(client, &cmd_crc, 1);
|
||||
if (error != 1)
|
||||
return -EINVAL;
|
||||
|
||||
error = ili251x_firmware_busy(client);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = priv->chip->read_reg(client, REG_READ_DATA_CRC,
|
||||
&crcrb, sizeof(crcrb));
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/* Check CRC readback */
|
||||
if ((crcrb[0] != (crc & 0xff)) || crcrb[1] != ((crc >> 8) & 0xff))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ili251x_firmware_reset(struct i2c_client *client)
|
||||
{
|
||||
u8 cmd_reset[2] = { 0xf2, 0x01 };
|
||||
int error;
|
||||
|
||||
error = i2c_master_send(client, cmd_reset, sizeof(cmd_reset));
|
||||
if (error != sizeof(cmd_reset))
|
||||
return -EINVAL;
|
||||
|
||||
return ili251x_firmware_busy(client);
|
||||
}
|
||||
|
||||
static void ili251x_hardware_reset(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct ili210x *priv = i2c_get_clientdata(client);
|
||||
|
||||
/* Reset the controller */
|
||||
gpiod_set_value_cansleep(priv->reset_gpio, 1);
|
||||
usleep_range(10000, 15000);
|
||||
gpiod_set_value_cansleep(priv->reset_gpio, 0);
|
||||
msleep(300);
|
||||
}
|
||||
|
||||
static ssize_t ili210x_firmware_update_store(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
const char *fwname = ILI251X_FW_FILENAME;
|
||||
const struct firmware *fw;
|
||||
u16 ac_end, df_end;
|
||||
u8 *fwbuf;
|
||||
int error;
|
||||
int i;
|
||||
|
||||
error = request_ihex_firmware(&fw, fwname, dev);
|
||||
if (error) {
|
||||
dev_err(dev, "Failed to request firmware %s, error=%d\n",
|
||||
fwname, error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = ili251x_firmware_to_buffer(fw, &fwbuf, &ac_end, &df_end);
|
||||
release_firmware(fw);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/*
|
||||
* Disable touchscreen IRQ, so that we would not get spurious touch
|
||||
* interrupt during firmware update, and so that the IRQ handler won't
|
||||
* trigger and interfere with the firmware update. There is no bit in
|
||||
* the touch controller to disable the IRQs during update, so we have
|
||||
* to do it this way here.
|
||||
*/
|
||||
disable_irq(client->irq);
|
||||
|
||||
dev_dbg(dev, "Firmware update started, firmware=%s\n", fwname);
|
||||
|
||||
ili251x_hardware_reset(dev);
|
||||
|
||||
error = ili251x_firmware_reset(client);
|
||||
if (error)
|
||||
goto exit;
|
||||
|
||||
/* This may not succeed on first try, so re-try a few times. */
|
||||
for (i = 0; i < 5; i++) {
|
||||
error = ili251x_switch_ic_mode(client, REG_SET_MODE_BL);
|
||||
if (!error)
|
||||
break;
|
||||
}
|
||||
|
||||
if (error)
|
||||
goto exit;
|
||||
|
||||
dev_dbg(dev, "IC is now in BootLoader mode\n");
|
||||
|
||||
msleep(200); /* The bootloader seems to need some time too. */
|
||||
|
||||
error = ili251x_firmware_write_to_ic(dev, fwbuf, 0xf000, df_end, 1);
|
||||
if (error) {
|
||||
dev_err(dev, "DF firmware update failed, error=%d\n", error);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "DataFlash firmware written\n");
|
||||
|
||||
error = ili251x_firmware_write_to_ic(dev, fwbuf, 0x2000, ac_end, 0);
|
||||
if (error) {
|
||||
dev_err(dev, "AC firmware update failed, error=%d\n", error);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "Application firmware written\n");
|
||||
|
||||
/* This may not succeed on first try, so re-try a few times. */
|
||||
for (i = 0; i < 5; i++) {
|
||||
error = ili251x_switch_ic_mode(client, REG_SET_MODE_AP);
|
||||
if (!error)
|
||||
break;
|
||||
}
|
||||
|
||||
if (error)
|
||||
goto exit;
|
||||
|
||||
dev_dbg(dev, "IC is now in Application mode\n");
|
||||
|
||||
error = ili251x_firmware_update_cached_state(dev);
|
||||
if (error)
|
||||
goto exit;
|
||||
|
||||
error = count;
|
||||
|
||||
exit:
|
||||
ili251x_hardware_reset(dev);
|
||||
dev_dbg(dev, "Firmware update ended, error=%i\n", error);
|
||||
enable_irq(client->irq);
|
||||
kfree(fwbuf);
|
||||
return error;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(firmware_update, 0200, NULL, ili210x_firmware_update_store);
|
||||
|
||||
static struct attribute *ili210x_attributes[] = {
|
||||
&dev_attr_calibrate.attr,
|
||||
&dev_attr_firmware_update.attr,
|
||||
&dev_attr_firmware_version.attr,
|
||||
&dev_attr_kernel_version.attr,
|
||||
&dev_attr_protocol_version.attr,
|
||||
&dev_attr_mode.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static umode_t ili210x_calibrate_visible(struct kobject *kobj,
|
||||
static umode_t ili210x_attributes_visible(struct kobject *kobj,
|
||||
struct attribute *attr, int index)
|
||||
{
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct ili210x *priv = i2c_get_clientdata(client);
|
||||
|
||||
return priv->chip->has_calibrate_reg ? attr->mode : 0;
|
||||
/* Calibrate is present on all ILI2xxx which have calibrate register */
|
||||
if (attr == &dev_attr_calibrate.attr)
|
||||
return priv->chip->has_calibrate_reg ? attr->mode : 0;
|
||||
|
||||
/* Firmware/Kernel/Protocol/BootMode is implememted only for ILI251x */
|
||||
if (!priv->chip->has_firmware_proto)
|
||||
return 0;
|
||||
|
||||
return attr->mode;
|
||||
}
|
||||
|
||||
static const struct attribute_group ili210x_attr_group = {
|
||||
.attrs = ili210x_attributes,
|
||||
.is_visible = ili210x_calibrate_visible,
|
||||
.is_visible = ili210x_attributes_visible,
|
||||
};
|
||||
|
||||
static void ili210x_power_down(void *data)
|
||||
|
@ -449,6 +980,12 @@ static int ili210x_i2c_probe(struct i2c_client *client,
|
|||
input_set_abs_params(input, ABS_MT_POSITION_Y, 0, max_xy, 0, 0);
|
||||
if (priv->chip->has_pressure_reg)
|
||||
input_set_abs_params(input, ABS_MT_PRESSURE, 0, 0xa, 0, 0);
|
||||
error = ili251x_firmware_update_cached_state(dev);
|
||||
if (error) {
|
||||
dev_err(dev, "Unable to cache firmware information, err: %d\n",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
touchscreen_parse_properties(input, true, &priv->prop);
|
||||
|
||||
error = input_mt_init_slots(input, priv->chip->max_touches,
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#define RM_CMD_BOOT_READ 0x44 /* send wait bl data ready*/
|
||||
|
||||
#define RM_BOOT_RDY 0xFF /* bl data ready */
|
||||
#define RM_BOOT_CMD_READHWID 0x0E /* read hwid */
|
||||
|
||||
/* I2C main commands */
|
||||
#define RM_CMD_QUERY_BANK 0x2B
|
||||
|
@ -290,6 +291,44 @@ static int raydium_i2c_sw_reset(struct i2c_client *client)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int raydium_i2c_query_ts_bootloader_info(struct raydium_data *ts)
|
||||
{
|
||||
struct i2c_client *client = ts->client;
|
||||
static const u8 get_hwid[] = { RM_BOOT_CMD_READHWID,
|
||||
0x10, 0xc0, 0x01, 0x00, 0x04, 0x00 };
|
||||
u8 rbuf[5] = { 0 };
|
||||
u32 hw_ver;
|
||||
int error;
|
||||
|
||||
error = raydium_i2c_send(client, RM_CMD_BOOT_WRT,
|
||||
get_hwid, sizeof(get_hwid));
|
||||
if (error) {
|
||||
dev_err(&client->dev, "WRT HWID command failed: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = raydium_i2c_send(client, RM_CMD_BOOT_ACK, rbuf, 1);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "Ack HWID command failed: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = raydium_i2c_read(client, RM_CMD_BOOT_CHK, rbuf, sizeof(rbuf));
|
||||
if (error) {
|
||||
dev_err(&client->dev, "Read HWID command failed: %d (%4ph)\n",
|
||||
error, rbuf + 1);
|
||||
hw_ver = 0xffffffffUL;
|
||||
} else {
|
||||
hw_ver = get_unaligned_be32(rbuf + 1);
|
||||
}
|
||||
|
||||
ts->info.hw_ver = cpu_to_le32(hw_ver);
|
||||
ts->info.main_ver = 0xff;
|
||||
ts->info.sub_ver = 0xff;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int raydium_i2c_query_ts_info(struct raydium_data *ts)
|
||||
{
|
||||
struct i2c_client *client = ts->client;
|
||||
|
@ -388,13 +427,10 @@ static int raydium_i2c_initialize(struct raydium_data *ts)
|
|||
if (error)
|
||||
ts->boot_mode = RAYDIUM_TS_BLDR;
|
||||
|
||||
if (ts->boot_mode == RAYDIUM_TS_BLDR) {
|
||||
ts->info.hw_ver = cpu_to_le32(0xffffffffUL);
|
||||
ts->info.main_ver = 0xff;
|
||||
ts->info.sub_ver = 0xff;
|
||||
} else {
|
||||
if (ts->boot_mode == RAYDIUM_TS_BLDR)
|
||||
raydium_i2c_query_ts_bootloader_info(ts);
|
||||
else
|
||||
raydium_i2c_query_ts_info(ts);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
@ -1082,11 +1118,11 @@ static int raydium_i2c_probe(struct i2c_client *client,
|
|||
if (error)
|
||||
return error;
|
||||
|
||||
error = devm_add_action(&client->dev, raydium_i2c_power_off, ts);
|
||||
error = devm_add_action_or_reset(&client->dev,
|
||||
raydium_i2c_power_off, ts);
|
||||
if (error) {
|
||||
dev_err(&client->dev,
|
||||
"failed to install power off action: %d\n", error);
|
||||
raydium_i2c_power_off(ts);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
@ -1218,7 +1254,7 @@ static SIMPLE_DEV_PM_OPS(raydium_i2c_pm_ops,
|
|||
raydium_i2c_suspend, raydium_i2c_resume);
|
||||
|
||||
static const struct i2c_device_id raydium_i2c_id[] = {
|
||||
{ "raydium_i2c" , 0 },
|
||||
{ "raydium_i2c", 0 },
|
||||
{ "rm32380", 0 },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
|
|
|
@ -92,7 +92,7 @@ static int st1232_ts_wait_ready(struct st1232_ts_data *ts)
|
|||
unsigned int retries;
|
||||
int error;
|
||||
|
||||
for (retries = 10; retries; retries--) {
|
||||
for (retries = 100; retries; retries--) {
|
||||
error = st1232_ts_read_data(ts, REG_STATUS, 1);
|
||||
if (!error) {
|
||||
switch (ts->read_buf[0]) {
|
||||
|
@ -389,6 +389,7 @@ static struct i2c_driver st1232_ts_driver = {
|
|||
.driver = {
|
||||
.name = ST1232_TS_NAME,
|
||||
.of_match_table = st1232_ts_dt_ids,
|
||||
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
|
||||
.pm = &st1232_ts_pm_ops,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -45,7 +45,9 @@ static int tsc2004_probe(struct i2c_client *i2c,
|
|||
|
||||
static int tsc2004_remove(struct i2c_client *i2c)
|
||||
{
|
||||
return tsc200x_remove(&i2c->dev);
|
||||
tsc200x_remove(&i2c->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id tsc2004_idtable[] = {
|
||||
|
|
|
@ -66,7 +66,9 @@ static int tsc2005_probe(struct spi_device *spi)
|
|||
|
||||
static int tsc2005_remove(struct spi_device *spi)
|
||||
{
|
||||
return tsc200x_remove(&spi->dev);
|
||||
tsc200x_remove(&spi->dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
|
|
|
@ -577,15 +577,13 @@ disable_regulator:
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(tsc200x_probe);
|
||||
|
||||
int tsc200x_remove(struct device *dev)
|
||||
void tsc200x_remove(struct device *dev)
|
||||
{
|
||||
struct tsc200x *ts = dev_get_drvdata(dev);
|
||||
|
||||
sysfs_remove_group(&dev->kobj, &tsc200x_attr_group);
|
||||
|
||||
regulator_disable(ts->vio);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tsc200x_remove);
|
||||
|
||||
|
|
|
@ -74,6 +74,6 @@ extern const struct dev_pm_ops tsc200x_pm_ops;
|
|||
int tsc200x_probe(struct device *dev, int irq, const struct input_id *tsc_id,
|
||||
struct regmap *regmap,
|
||||
int (*tsc200x_cmd)(struct device *dev, u8 cmd));
|
||||
int tsc200x_remove(struct device *dev);
|
||||
void tsc200x_remove(struct device *dev);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
* <tobita.tatsunosuke@wacom.co.jp>
|
||||
*/
|
||||
|
||||
#include <linux/bits.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/i2c.h>
|
||||
|
@ -14,6 +15,15 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
/* Bitmasks (for data[3]) */
|
||||
#define WACOM_TIP_SWITCH BIT(0)
|
||||
#define WACOM_BARREL_SWITCH BIT(1)
|
||||
#define WACOM_ERASER BIT(2)
|
||||
#define WACOM_INVERT BIT(3)
|
||||
#define WACOM_BARREL_SWITCH_2 BIT(4)
|
||||
#define WACOM_IN_PROXIMITY BIT(5)
|
||||
|
||||
/* Registers */
|
||||
#define WACOM_CMD_QUERY0 0x04
|
||||
#define WACOM_CMD_QUERY1 0x00
|
||||
#define WACOM_CMD_QUERY2 0x33
|
||||
|
@ -99,19 +109,19 @@ static irqreturn_t wacom_i2c_irq(int irq, void *dev_id)
|
|||
if (error < 0)
|
||||
goto out;
|
||||
|
||||
tsw = data[3] & 0x01;
|
||||
ers = data[3] & 0x04;
|
||||
f1 = data[3] & 0x02;
|
||||
f2 = data[3] & 0x10;
|
||||
tsw = data[3] & WACOM_TIP_SWITCH;
|
||||
ers = data[3] & WACOM_ERASER;
|
||||
f1 = data[3] & WACOM_BARREL_SWITCH;
|
||||
f2 = data[3] & WACOM_BARREL_SWITCH_2;
|
||||
x = le16_to_cpup((__le16 *)&data[4]);
|
||||
y = le16_to_cpup((__le16 *)&data[6]);
|
||||
pressure = le16_to_cpup((__le16 *)&data[8]);
|
||||
|
||||
if (!wac_i2c->prox)
|
||||
wac_i2c->tool = (data[3] & 0x0c) ?
|
||||
wac_i2c->tool = (data[3] & (WACOM_ERASER | WACOM_INVERT)) ?
|
||||
BTN_TOOL_RUBBER : BTN_TOOL_PEN;
|
||||
|
||||
wac_i2c->prox = data[3] & 0x20;
|
||||
wac_i2c->prox = data[3] & WACOM_IN_PROXIMITY;
|
||||
|
||||
input_report_key(input, BTN_TOUCH, tsw || ers);
|
||||
input_report_key(input, wac_i2c->tool, wac_i2c->prox);
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _LINUX_CY8CTMG110_PDATA_H
|
||||
#define _LINUX_CY8CTMG110_PDATA_H
|
||||
|
||||
struct cy8ctmg110_pdata
|
||||
{
|
||||
int reset_pin; /* Reset pin is wired to this GPIO (optional) */
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,17 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/* linux/spi/ads7846.h */
|
||||
|
||||
/* Touchscreen characteristics vary between boards and models. The
|
||||
* platform_data for the device's "struct device" holds this information.
|
||||
*
|
||||
* It's OK if the min/max values are zero.
|
||||
*/
|
||||
enum ads7846_filter {
|
||||
ADS7846_FILTER_OK,
|
||||
ADS7846_FILTER_REPEAT,
|
||||
ADS7846_FILTER_IGNORE,
|
||||
};
|
||||
|
||||
struct ads7846_platform_data {
|
||||
u16 model; /* 7843, 7845, 7846, 7873. */
|
||||
u16 vref_delay_usecs; /* 0 for external vref; etc */
|
||||
|
@ -51,10 +40,6 @@ struct ads7846_platform_data {
|
|||
int gpio_pendown_debounce; /* platform specific debounce time for
|
||||
* the gpio_pendown */
|
||||
int (*get_pendown_state)(void);
|
||||
int (*filter_init) (const struct ads7846_platform_data *pdata,
|
||||
void **filter_data);
|
||||
int (*filter) (void *filter_data, int data_idx, int *val);
|
||||
void (*filter_cleanup)(void *filter_data);
|
||||
void (*wait_for_sync)(void);
|
||||
bool wakeup;
|
||||
unsigned long irq_flags;
|
||||
|
|
|
@ -32,6 +32,7 @@ EXPORT_SYMBOL(cad_pid);
|
|||
#define DEFAULT_REBOOT_MODE
|
||||
#endif
|
||||
enum reboot_mode reboot_mode DEFAULT_REBOOT_MODE;
|
||||
EXPORT_SYMBOL_GPL(reboot_mode);
|
||||
enum reboot_mode panic_reboot_mode = REBOOT_UNDEFINED;
|
||||
|
||||
/*
|
||||
|
|
Loading…
Reference in New Issue