summaryrefslogtreecommitdiff
path: root/drivers/input/touchscreen
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2016-03-18 07:51:52 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2016-03-18 07:51:52 +0300
commit10fdfee7f7fd8d4a6a8455ac4c9fbbc51d79b9f7 (patch)
treeace3300ec40d2a253c59a70ee37eb76fa53c859f /drivers/input/touchscreen
parent0f49fc95b86fc77b867d643e2d38bc9f28035ed4 (diff)
parent4d2508a55990c9b0fc1afb2c2cc5636712fd30f7 (diff)
downloadlinux-10fdfee7f7fd8d4a6a8455ac4c9fbbc51d79b9f7.tar.xz
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
Pull input updates from Dmitry Torokhov: "The most notable item is addition of support for Synaptics RMI4 protocol which is native protocol for all current Synaptics devices (touchscreens, touchpads). In later releases we'll switch devices using HID and PS/2 protocol emulation to RMI4. You will also get: - BYD PS/2 touchpad protocol support for psmouse - MELFAS MIP4 Touchscreen driver - rotary encoder was moved away from legacy platform data and to generic device properties API, devm_* API, and can now handle encoders using more than 2 GPIOs - Cypress touchpad driver was switched to devm_* API and device properties - other assorted driver fixes" * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (40 commits) ARM: pxa/raumfeld: use PROPERTY_ENTRY_INTEGER to define props Input: synaptics-rmi4 - using logical instead of bitwise AND Input: powermate - fix oops with malicious USB descriptors Input: snvs_pwrkey - fix returned value check of syscon_regmap_lookup_by_phandle() MAINTAINERS: add devicetree bindings to Input Drivers section Input: synaptics-rmi4 - add device tree support to the SPI transport driver Input: synaptics-rmi4 - add SPI transport driver Input: synaptics-rmi4 - add support for F30 Input: synaptics-rmi4 - add support for F12 Input: synaptics-rmi4 - add device tree support for 2d sensors and F11 Input: synaptics-rmi4 - add support for 2D sensors and F11 Input: synaptics-rmi4 - add device tree support for RMI4 I2C devices Input: synaptics-rmi4 - add I2C transport driver Input: synaptics-rmi4 - add support for Synaptics RMI4 devices Input: ad7879 - add device tree support Input: ad7879 - fix default x/y axis assignment Input: ad7879 - move header to platform_data directory Input: ts4800 - add hardware dependency Input: cyapa - fix for losing events during device power transitions Input: sh_keysc - remove dependency on SUPERH ...
Diffstat (limited to 'drivers/input/touchscreen')
-rw-r--r--drivers/input/touchscreen/Kconfig17
-rw-r--r--drivers/input/touchscreen/Makefile1
-rw-r--r--drivers/input/touchscreen/ad7879-i2c.c10
-rw-r--r--drivers/input/touchscreen/ad7879-spi.c10
-rw-r--r--drivers/input/touchscreen/ad7879.c160
-rw-r--r--drivers/input/touchscreen/cyttsp_core.c194
-rw-r--r--drivers/input/touchscreen/cyttsp_core.h10
-rw-r--r--drivers/input/touchscreen/cyttsp_i2c.c10
-rw-r--r--drivers/input/touchscreen/cyttsp_spi.c10
-rw-r--r--drivers/input/touchscreen/melfas_mip4.c1517
-rw-r--r--drivers/input/touchscreen/stmpe-ts.c31
11 files changed, 1816 insertions, 154 deletions
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 66c62641b59a..1f99e7f30e27 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -334,7 +334,7 @@ config TOUCHSCREEN_FUJITSU
config TOUCHSCREEN_GOODIX
tristate "Goodix I2C touchscreen"
depends on I2C
- depends on GPIOLIB
+ depends on GPIOLIB || COMPILE_TEST
help
Say Y here if you have the Goodix touchscreen (such as one
installed in Onda v975w tablets) connected to your
@@ -491,6 +491,17 @@ config TOUCHSCREEN_MMS114
To compile this driver as a module, choose M here: the
module will be called mms114.
+config TOUCHSCREEN_MELFAS_MIP4
+ tristate "MELFAS MIP4 Touchscreen"
+ depends on I2C
+ help
+ Say Y here if you have a MELFAS MIP4 Touchscreen device.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here:
+ the module will be called melfas_mip4.
+
config TOUCHSCREEN_MTOUCH
tristate "MicroTouch serial touchscreens"
select SERIO
@@ -941,6 +952,7 @@ config TOUCHSCREEN_TOUCHIT213
config TOUCHSCREEN_TS4800
tristate "TS-4800 touchscreen"
depends on HAS_IOMEM && OF
+ depends on SOC_IMX51 || COMPILE_TEST
select MFD_SYSCON
select INPUT_POLLDEV
help
@@ -1112,7 +1124,8 @@ config TOUCHSCREEN_ZFORCE
config TOUCHSCREEN_COLIBRI_VF50
tristate "Toradex Colibri on board touchscreen driver"
- depends on GPIOLIB && IIO && VF610_ADC
+ depends on IIO && VF610_ADC
+ depends on GPIOLIB || COMPILE_TEST
help
Say Y here if you have a Colibri VF50 and plan to use
the on-board provided 4-wire touchscreen driver.
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 968ff12e3132..4b518c76e0d7 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_TOUCHSCREEN_LPC32XX) += lpc32xx_ts.o
obj-$(CONFIG_TOUCHSCREEN_MAX11801) += max11801_ts.o
obj-$(CONFIG_TOUCHSCREEN_MC13783) += mc13783_ts.o
obj-$(CONFIG_TOUCHSCREEN_MCS5000) += mcs5000_ts.o
+obj-$(CONFIG_TOUCHSCREEN_MELFAS_MIP4) += melfas_mip4.o
obj-$(CONFIG_TOUCHSCREEN_MIGOR) += migor_ts.o
obj-$(CONFIG_TOUCHSCREEN_MMS114) += mms114.o
obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o
diff --git a/drivers/input/touchscreen/ad7879-i2c.c b/drivers/input/touchscreen/ad7879-i2c.c
index d66962c5b1c2..58f72e0246ab 100644
--- a/drivers/input/touchscreen/ad7879-i2c.c
+++ b/drivers/input/touchscreen/ad7879-i2c.c
@@ -10,6 +10,7 @@
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/types.h>
+#include <linux/of.h>
#include <linux/pm.h>
#include "ad7879.h"
@@ -91,10 +92,19 @@ static const struct i2c_device_id ad7879_id[] = {
};
MODULE_DEVICE_TABLE(i2c, ad7879_id);
+#ifdef CONFIG_OF
+static const struct of_device_id ad7879_i2c_dt_ids[] = {
+ { .compatible = "adi,ad7879-1", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ad7879_i2c_dt_ids);
+#endif
+
static struct i2c_driver ad7879_i2c_driver = {
.driver = {
.name = "ad7879",
.pm = &ad7879_pm_ops,
+ .of_match_table = of_match_ptr(ad7879_i2c_dt_ids),
},
.probe = ad7879_i2c_probe,
.remove = ad7879_i2c_remove,
diff --git a/drivers/input/touchscreen/ad7879-spi.c b/drivers/input/touchscreen/ad7879-spi.c
index 48033c2689ab..d42b6b9af191 100644
--- a/drivers/input/touchscreen/ad7879-spi.c
+++ b/drivers/input/touchscreen/ad7879-spi.c
@@ -10,6 +10,7 @@
#include <linux/pm.h>
#include <linux/spi/spi.h>
#include <linux/module.h>
+#include <linux/of.h>
#include "ad7879.h"
@@ -146,10 +147,19 @@ static int ad7879_spi_remove(struct spi_device *spi)
return 0;
}
+#ifdef CONFIG_OF
+static const struct of_device_id ad7879_spi_dt_ids[] = {
+ { .compatible = "adi,ad7879", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ad7879_spi_dt_ids);
+#endif
+
static struct spi_driver ad7879_spi_driver = {
.driver = {
.name = "ad7879",
.pm = &ad7879_pm_ops,
+ .of_match_table = of_match_ptr(ad7879_spi_dt_ids),
},
.probe = ad7879_spi_probe,
.remove = ad7879_spi_remove,
diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c
index 16b5cc2196f2..69d299d5dd00 100644
--- a/drivers/input/touchscreen/ad7879.c
+++ b/drivers/input/touchscreen/ad7879.c
@@ -31,7 +31,8 @@
#include <linux/i2c.h>
#include <linux/gpio.h>
-#include <linux/spi/ad7879.h>
+#include <linux/input/touchscreen.h>
+#include <linux/platform_data/ad7879.h>
#include <linux/module.h>
#include "ad7879.h"
@@ -94,8 +95,8 @@
#define AD7879_TEMP_BIT (1<<1)
enum {
- AD7879_SEQ_XPOS = 0,
- AD7879_SEQ_YPOS = 1,
+ AD7879_SEQ_YPOS = 0,
+ AD7879_SEQ_XPOS = 1,
AD7879_SEQ_Z1 = 2,
AD7879_SEQ_Z2 = 3,
AD7879_NR_SENSE = 4,
@@ -126,7 +127,6 @@ struct ad7879 {
u8 pen_down_acc_interval;
u8 median;
u16 x_plate_ohms;
- u16 pressure_max;
u16 cmd_crtl1;
u16 cmd_crtl2;
u16 cmd_crtl3;
@@ -170,10 +170,10 @@ static int ad7879_report(struct ad7879 *ts)
* filter. The combination of these two techniques provides a robust
* solution, discarding the spurious noise in the signal and keeping
* only the data of interest. The size of both filters is
- * programmable. (dev.platform_data, see linux/spi/ad7879.h) Other
- * user-programmable conversion controls include variable acquisition
- * time, and first conversion delay. Up to 16 averages can be taken
- * per conversion.
+ * programmable. (dev.platform_data, see linux/platform_data/ad7879.h)
+ * Other user-programmable conversion controls include variable
+ * acquisition time, and first conversion delay. Up to 16 averages can
+ * be taken per conversion.
*/
if (likely(x && z1)) {
@@ -186,7 +186,7 @@ static int ad7879_report(struct ad7879 *ts)
* Sample found inconsistent, pressure is beyond
* the maximum. Don't report it to user space.
*/
- if (Rt > ts->pressure_max)
+ if (Rt > input_abs_get_max(input_dev, ABS_PRESSURE))
return -EINVAL;
/*
@@ -469,7 +469,7 @@ static void ad7879_gpio_remove(struct ad7879 *ts)
{
const struct ad7879_platform_data *pdata = dev_get_platdata(ts->dev);
- if (pdata->gpio_export)
+ if (pdata && pdata->gpio_export)
gpiochip_remove(&ts->gc);
}
@@ -485,6 +485,32 @@ static inline void ad7879_gpio_remove(struct ad7879 *ts)
}
#endif
+static int ad7879_parse_dt(struct device *dev, struct ad7879 *ts)
+{
+ int err;
+ u32 tmp;
+
+ err = device_property_read_u32(dev, "adi,resistance-plate-x", &tmp);
+ if (err) {
+ dev_err(dev, "failed to get resistance-plate-x property\n");
+ return err;
+ }
+ ts->x_plate_ohms = (u16)tmp;
+
+ device_property_read_u8(dev, "adi,first-conversion-delay",
+ &ts->first_conversion_delay);
+ device_property_read_u8(dev, "adi,acquisition-time",
+ &ts->acquisition_time);
+ device_property_read_u8(dev, "adi,median-filter-size", &ts->median);
+ device_property_read_u8(dev, "adi,averaging", &ts->averaging);
+ device_property_read_u8(dev, "adi,conversion-interval",
+ &ts->pen_down_acc_interval);
+
+ ts->swap_xy = device_property_read_bool(dev, "touchscreen-swapped-x-y");
+
+ return 0;
+}
+
struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq,
const struct ad7879_bus_ops *bops)
{
@@ -495,41 +521,44 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq,
u16 revid;
if (!irq) {
- dev_err(dev, "no IRQ?\n");
- err = -EINVAL;
- goto err_out;
+ dev_err(dev, "No IRQ specified\n");
+ return ERR_PTR(-EINVAL);
}
- if (!pdata) {
- dev_err(dev, "no platform data?\n");
- err = -EINVAL;
- goto err_out;
+ ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
+ if (!ts)
+ return ERR_PTR(-ENOMEM);
+
+ if (pdata) {
+ /* Platform data use swapped axis (backward compatibility) */
+ ts->swap_xy = !pdata->swap_xy;
+
+ ts->x_plate_ohms = pdata->x_plate_ohms ? : 400;
+
+ ts->first_conversion_delay = pdata->first_conversion_delay;
+ ts->acquisition_time = pdata->acquisition_time;
+ ts->averaging = pdata->averaging;
+ ts->pen_down_acc_interval = pdata->pen_down_acc_interval;
+ ts->median = pdata->median;
+ } else if (dev->of_node) {
+ ad7879_parse_dt(dev, ts);
+ } else {
+ dev_err(dev, "No platform data\n");
+ return ERR_PTR(-EINVAL);
}
- ts = kzalloc(sizeof(*ts), GFP_KERNEL);
- input_dev = input_allocate_device();
- if (!ts || !input_dev) {
- err = -ENOMEM;
- goto err_free_mem;
+ input_dev = devm_input_allocate_device(dev);
+ if (!input_dev) {
+ dev_err(dev, "Failed to allocate input device\n");
+ return ERR_PTR(-ENOMEM);
}
ts->bops = bops;
ts->dev = dev;
ts->input = input_dev;
ts->irq = irq;
- ts->swap_xy = pdata->swap_xy;
setup_timer(&ts->timer, ad7879_timer, (unsigned long) ts);
-
- ts->x_plate_ohms = pdata->x_plate_ohms ? : 400;
- ts->pressure_max = pdata->pressure_max ? : ~0;
-
- ts->first_conversion_delay = pdata->first_conversion_delay;
- ts->acquisition_time = pdata->acquisition_time;
- ts->averaging = pdata->averaging;
- ts->pen_down_acc_interval = pdata->pen_down_acc_interval;
- ts->median = pdata->median;
-
snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev));
input_dev->name = "AD7879 Touchscreen";
@@ -550,21 +579,33 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq,
__set_bit(EV_KEY, input_dev->evbit);
__set_bit(BTN_TOUCH, input_dev->keybit);
- input_set_abs_params(input_dev, ABS_X,
- pdata->x_min ? : 0,
- pdata->x_max ? : MAX_12BIT,
- 0, 0);
- input_set_abs_params(input_dev, ABS_Y,
- pdata->y_min ? : 0,
- pdata->y_max ? : MAX_12BIT,
- 0, 0);
- input_set_abs_params(input_dev, ABS_PRESSURE,
- pdata->pressure_min, pdata->pressure_max, 0, 0);
+ if (pdata) {
+ input_set_abs_params(input_dev, ABS_X,
+ pdata->x_min ? : 0,
+ pdata->x_max ? : MAX_12BIT,
+ 0, 0);
+ input_set_abs_params(input_dev, ABS_Y,
+ pdata->y_min ? : 0,
+ pdata->y_max ? : MAX_12BIT,
+ 0, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE,
+ pdata->pressure_min,
+ pdata->pressure_max ? : ~0,
+ 0, 0);
+ } else {
+ input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0);
+ touchscreen_parse_properties(input_dev, false);
+ if (!input_abs_get_max(input_dev, ABS_PRESSURE)) {
+ dev_err(dev, "Touchscreen pressure is not specified\n");
+ return ERR_PTR(-EINVAL);
+ }
+ }
err = ad7879_write(ts, AD7879_REG_CTRL2, AD7879_RESET);
if (err < 0) {
dev_err(dev, "Failed to write %s\n", input_dev->name);
- goto err_free_mem;
+ return ERR_PTR(err);
}
revid = ad7879_read(ts, AD7879_REG_REVID);
@@ -573,8 +614,7 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq,
if (input_dev->id.product != devid) {
dev_err(dev, "Failed to probe %s (%x vs %x)\n",
input_dev->name, devid, revid);
- err = -ENODEV;
- goto err_free_mem;
+ return ERR_PTR(-ENODEV);
}
ts->cmd_crtl3 = AD7879_YPLUS_BIT |
@@ -594,23 +634,25 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq,
AD7879_ACQ(ts->acquisition_time) |
AD7879_TMR(ts->pen_down_acc_interval);
- err = request_threaded_irq(ts->irq, NULL, ad7879_irq,
- IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
- dev_name(dev), ts);
+ err = devm_request_threaded_irq(dev, ts->irq, NULL, ad7879_irq,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ dev_name(dev), ts);
if (err) {
- dev_err(dev, "irq %d busy?\n", ts->irq);
- goto err_free_mem;
+ dev_err(dev, "Failed to request IRQ: %d\n", err);
+ return ERR_PTR(err);
}
__ad7879_disable(ts);
err = sysfs_create_group(&dev->kobj, &ad7879_attr_group);
if (err)
- goto err_free_irq;
+ goto err_out;
- err = ad7879_gpio_add(ts, pdata);
- if (err)
- goto err_remove_attr;
+ if (pdata) {
+ err = ad7879_gpio_add(ts, pdata);
+ if (err)
+ goto err_remove_attr;
+ }
err = input_register_device(input_dev);
if (err)
@@ -622,11 +664,6 @@ err_remove_gpio:
ad7879_gpio_remove(ts);
err_remove_attr:
sysfs_remove_group(&dev->kobj, &ad7879_attr_group);
-err_free_irq:
- free_irq(ts->irq, ts);
-err_free_mem:
- input_free_device(input_dev);
- kfree(ts);
err_out:
return ERR_PTR(err);
}
@@ -636,9 +673,6 @@ void ad7879_remove(struct ad7879 *ts)
{
ad7879_gpio_remove(ts);
sysfs_remove_group(&ts->dev->kobj, &ad7879_attr_group);
- free_irq(ts->irq, ts);
- input_unregister_device(ts->input);
- kfree(ts);
}
EXPORT_SYMBOL(ad7879_remove);
diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c
index 5b74e8b84e79..91cda8f8119d 100644
--- a/drivers/input/touchscreen/cyttsp_core.c
+++ b/drivers/input/touchscreen/cyttsp_core.c
@@ -30,9 +30,12 @@
#include <linux/delay.h>
#include <linux/input.h>
#include <linux/input/mt.h>
+#include <linux/input/touchscreen.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
+#include <linux/property.h>
+#include <linux/gpio/consumer.h>
#include "cyttsp_core.h"
@@ -57,6 +60,7 @@
#define CY_DELAY_DFLT 20 /* ms */
#define CY_DELAY_MAX 500
#define CY_ACT_DIST_DFLT 0xF8
+#define CY_ACT_DIST_MASK 0x0F
#define CY_HNDSHK_BIT 0x80
/* device mode bits */
#define CY_OPERATE_MODE 0x00
@@ -120,7 +124,7 @@ static int ttsp_send_command(struct cyttsp *ts, u8 cmd)
static int cyttsp_handshake(struct cyttsp *ts)
{
- if (ts->pdata->use_hndshk)
+ if (ts->use_hndshk)
return ttsp_send_command(ts,
ts->xy_data.hst_mode ^ CY_HNDSHK_BIT);
@@ -142,9 +146,9 @@ static int cyttsp_exit_bl_mode(struct cyttsp *ts)
u8 bl_cmd[sizeof(bl_command)];
memcpy(bl_cmd, bl_command, sizeof(bl_command));
- if (ts->pdata->bl_keys)
+ if (ts->bl_keys)
memcpy(&bl_cmd[sizeof(bl_command) - CY_NUM_BL_KEYS],
- ts->pdata->bl_keys, CY_NUM_BL_KEYS);
+ ts->bl_keys, CY_NUM_BL_KEYS);
error = ttsp_write_block_data(ts, CY_REG_BASE,
sizeof(bl_cmd), bl_cmd);
@@ -217,14 +221,14 @@ static int cyttsp_set_sysinfo_regs(struct cyttsp *ts)
{
int retval = 0;
- if (ts->pdata->act_intrvl != CY_ACT_INTRVL_DFLT ||
- ts->pdata->tch_tmout != CY_TCH_TMOUT_DFLT ||
- ts->pdata->lp_intrvl != CY_LP_INTRVL_DFLT) {
+ if (ts->act_intrvl != CY_ACT_INTRVL_DFLT ||
+ ts->tch_tmout != CY_TCH_TMOUT_DFLT ||
+ ts->lp_intrvl != CY_LP_INTRVL_DFLT) {
u8 intrvl_ray[] = {
- ts->pdata->act_intrvl,
- ts->pdata->tch_tmout,
- ts->pdata->lp_intrvl
+ ts->act_intrvl,
+ ts->tch_tmout,
+ ts->lp_intrvl
};
/* set intrvl registers */
@@ -236,6 +240,16 @@ static int cyttsp_set_sysinfo_regs(struct cyttsp *ts)
return retval;
}
+static void cyttsp_hard_reset(struct cyttsp *ts)
+{
+ if (ts->reset_gpio) {
+ gpiod_set_value_cansleep(ts->reset_gpio, 1);
+ msleep(CY_DELAY_DFLT);
+ gpiod_set_value_cansleep(ts->reset_gpio, 0);
+ msleep(CY_DELAY_DFLT);
+ }
+}
+
static int cyttsp_soft_reset(struct cyttsp *ts)
{
unsigned long timeout;
@@ -263,7 +277,7 @@ out:
static int cyttsp_act_dist_setup(struct cyttsp *ts)
{
- u8 act_dist_setup = ts->pdata->act_dist;
+ u8 act_dist_setup = ts->act_dist;
/* Init gesture; active distance setup */
return ttsp_write_block_data(ts, CY_REG_ACT_DIST,
@@ -528,45 +542,110 @@ static void cyttsp_close(struct input_dev *dev)
cyttsp_disable(ts);
}
+static int cyttsp_parse_properties(struct cyttsp *ts)
+{
+ struct device *dev = ts->dev;
+ u32 dt_value;
+ int ret;
+
+ ts->bl_keys = devm_kzalloc(dev, CY_NUM_BL_KEYS, GFP_KERNEL);
+ if (!ts->bl_keys)
+ return -ENOMEM;
+
+ /* Set some default values */
+ ts->use_hndshk = false;
+ ts->act_dist = CY_ACT_DIST_DFLT;
+ ts->act_intrvl = CY_ACT_INTRVL_DFLT;
+ ts->tch_tmout = CY_TCH_TMOUT_DFLT;
+ ts->lp_intrvl = CY_LP_INTRVL_DFLT;
+
+ ret = device_property_read_u8_array(dev, "bootloader-key",
+ ts->bl_keys, CY_NUM_BL_KEYS);
+ if (ret) {
+ dev_err(dev,
+ "bootloader-key property could not be retrieved\n");
+ return ret;
+ }
+
+ ts->use_hndshk = device_property_present(dev, "use-handshake");
+
+ if (!device_property_read_u32(dev, "active-distance", &dt_value)) {
+ if (dt_value > 15) {
+ dev_err(dev, "active-distance (%u) must be [0-15]\n",
+ dt_value);
+ return -EINVAL;
+ }
+ ts->act_dist &= ~CY_ACT_DIST_MASK;
+ ts->act_dist |= dt_value;
+ }
+
+ if (!device_property_read_u32(dev, "active-interval-ms", &dt_value)) {
+ if (dt_value > 255) {
+ dev_err(dev, "active-interval-ms (%u) must be [0-255]\n",
+ dt_value);
+ return -EINVAL;
+ }
+ ts->act_intrvl = dt_value;
+ }
+
+ if (!device_property_read_u32(dev, "lowpower-interval-ms", &dt_value)) {
+ if (dt_value > 2550) {
+ dev_err(dev, "lowpower-interval-ms (%u) must be [0-2550]\n",
+ dt_value);
+ return -EINVAL;
+ }
+ /* Register value is expressed in 0.01s / bit */
+ ts->lp_intrvl = dt_value / 10;
+ }
+
+ if (!device_property_read_u32(dev, "touch-timeout-ms", &dt_value)) {
+ if (dt_value > 2550) {
+ dev_err(dev, "touch-timeout-ms (%u) must be [0-2550]\n",
+ dt_value);
+ return -EINVAL;
+ }
+ /* Register value is expressed in 0.01s / bit */
+ ts->tch_tmout = dt_value / 10;
+ }
+
+ return 0;
+}
+
struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops,
struct device *dev, int irq, size_t xfer_buf_size)
{
- const struct cyttsp_platform_data *pdata = dev_get_platdata(dev);
struct cyttsp *ts;
struct input_dev *input_dev;
int error;
- if (!pdata || !pdata->name || irq <= 0) {
- error = -EINVAL;
- goto err_out;
- }
+ ts = devm_kzalloc(dev, sizeof(*ts) + xfer_buf_size, GFP_KERNEL);
+ if (!ts)
+ return ERR_PTR(-ENOMEM);
- ts = kzalloc(sizeof(*ts) + xfer_buf_size, GFP_KERNEL);
- input_dev = input_allocate_device();
- if (!ts || !input_dev) {
- error = -ENOMEM;
- goto err_free_mem;
- }
+ input_dev = devm_input_allocate_device(dev);
+ if (!input_dev)
+ return ERR_PTR(-ENOMEM);
ts->dev = dev;
ts->input = input_dev;
- ts->pdata = dev_get_platdata(dev);
ts->bus_ops = bus_ops;
ts->irq = irq;
+ ts->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(ts->reset_gpio)) {
+ error = PTR_ERR(ts->reset_gpio);
+ dev_err(dev, "Failed to request reset gpio, error %d\n", error);
+ return ERR_PTR(error);
+ }
+
+ error = cyttsp_parse_properties(ts);
+ if (error)
+ return ERR_PTR(error);
+
init_completion(&ts->bl_ready);
snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev));
- if (pdata->init) {
- error = pdata->init();
- if (error) {
- dev_err(ts->dev, "platform init failed, err: %d\n",
- error);
- goto err_free_mem;
- }
- }
-
- input_dev->name = pdata->name;
+ input_dev->name = "Cypress TTSP TouchScreen";
input_dev->phys = ts->phys;
input_dev->id.bustype = bus_ops->bustype;
input_dev->dev.parent = ts->dev;
@@ -576,63 +655,44 @@ struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops,
input_set_drvdata(input_dev, ts);
- __set_bit(EV_ABS, input_dev->evbit);
- input_set_abs_params(input_dev, ABS_MT_POSITION_X,
- 0, pdata->maxx, 0, 0);
- input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
- 0, pdata->maxy, 0, 0);
- input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
- 0, CY_MAXZ, 0, 0);
+ input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_X);
+ input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_Y);
+ touchscreen_parse_properties(input_dev, true);
- input_mt_init_slots(input_dev, CY_MAX_ID, 0);
+ error = input_mt_init_slots(input_dev, CY_MAX_ID, 0);
+ if (error) {
+ dev_err(dev, "Unable to init MT slots.\n");
+ return ERR_PTR(error);
+ }
- error = request_threaded_irq(ts->irq, NULL, cyttsp_irq,
- IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
- pdata->name, ts);
+ error = devm_request_threaded_irq(dev, ts->irq, NULL, cyttsp_irq,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "cyttsp", ts);
if (error) {
dev_err(ts->dev, "failed to request IRQ %d, err: %d\n",
ts->irq, error);
- goto err_platform_exit;
+ return ERR_PTR(error);
}
disable_irq(ts->irq);
+ cyttsp_hard_reset(ts);
+
error = cyttsp_power_on(ts);
if (error)
- goto err_free_irq;
+ return ERR_PTR(error);
error = input_register_device(input_dev);
if (error) {
dev_err(ts->dev, "failed to register input device: %d\n",
error);
- goto err_free_irq;
+ return ERR_PTR(error);
}
return ts;
-
-err_free_irq:
- free_irq(ts->irq, ts);
-err_platform_exit:
- if (pdata->exit)
- pdata->exit();
-err_free_mem:
- input_free_device(input_dev);
- kfree(ts);
-err_out:
- return ERR_PTR(error);
}
EXPORT_SYMBOL_GPL(cyttsp_probe);
-void cyttsp_remove(struct cyttsp *ts)
-{
- free_irq(ts->irq, ts);
- input_unregister_device(ts->input);
- if (ts->pdata->exit)
- ts->pdata->exit();
- kfree(ts);
-}
-EXPORT_SYMBOL_GPL(cyttsp_remove);
-
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard touchscreen driver core");
MODULE_AUTHOR("Cypress");
diff --git a/drivers/input/touchscreen/cyttsp_core.h b/drivers/input/touchscreen/cyttsp_core.h
index 07074110a902..7835e2bacf5a 100644
--- a/drivers/input/touchscreen/cyttsp_core.h
+++ b/drivers/input/touchscreen/cyttsp_core.h
@@ -129,7 +129,6 @@ struct cyttsp {
int irq;
struct input_dev *input;
char phys[32];
- const struct cyttsp_platform_data *pdata;
const struct cyttsp_bus_ops *bus_ops;
struct cyttsp_bootloader_data bl_data;
struct cyttsp_sysinfo_data sysinfo_data;
@@ -138,12 +137,19 @@ struct cyttsp {
enum cyttsp_state state;
bool suspended;
+ struct gpio_desc *reset_gpio;
+ bool use_hndshk;
+ u8 act_dist;
+ u8 act_intrvl;
+ u8 tch_tmout;
+ u8 lp_intrvl;
+ u8 *bl_keys;
+
u8 xfer_buf[] ____cacheline_aligned;
};
struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops,
struct device *dev, int irq, size_t xfer_buf_size);
-void cyttsp_remove(struct cyttsp *ts);
int cyttsp_i2c_write_block_data(struct device *dev, u8 *xfer_buf, u16 addr,
u8 length, const void *values);
diff --git a/drivers/input/touchscreen/cyttsp_i2c.c b/drivers/input/touchscreen/cyttsp_i2c.c
index eee51b3f2e3f..1edfdba96ede 100644
--- a/drivers/input/touchscreen/cyttsp_i2c.c
+++ b/drivers/input/touchscreen/cyttsp_i2c.c
@@ -56,15 +56,6 @@ static int cyttsp_i2c_probe(struct i2c_client *client,
return 0;
}
-static int cyttsp_i2c_remove(struct i2c_client *client)
-{
- struct cyttsp *ts = i2c_get_clientdata(client);
-
- cyttsp_remove(ts);
-
- return 0;
-}
-
static const struct i2c_device_id cyttsp_i2c_id[] = {
{ CY_I2C_NAME, 0 },
{ }
@@ -77,7 +68,6 @@ static struct i2c_driver cyttsp_i2c_driver = {
.pm = &cyttsp_pm_ops,
},
.probe = cyttsp_i2c_probe,
- .remove = cyttsp_i2c_remove,
.id_table = cyttsp_i2c_id,
};
diff --git a/drivers/input/touchscreen/cyttsp_spi.c b/drivers/input/touchscreen/cyttsp_spi.c
index bbeeb2488b57..3c9d18b1b6ef 100644
--- a/drivers/input/touchscreen/cyttsp_spi.c
+++ b/drivers/input/touchscreen/cyttsp_spi.c
@@ -170,22 +170,12 @@ static int cyttsp_spi_probe(struct spi_device *spi)
return 0;
}
-static int cyttsp_spi_remove(struct spi_device *spi)
-{
- struct cyttsp *ts = spi_get_drvdata(spi);
-
- cyttsp_remove(ts);
-
- return 0;
-}
-
static struct spi_driver cyttsp_spi_driver = {
.driver = {
.name = CY_SPI_NAME,
.pm = &cyttsp_pm_ops,
},
.probe = cyttsp_spi_probe,
- .remove = cyttsp_spi_remove,
};
module_spi_driver(cyttsp_spi_driver);
diff --git a/drivers/input/touchscreen/melfas_mip4.c b/drivers/input/touchscreen/melfas_mip4.c
new file mode 100644
index 000000000000..892729734c51
--- /dev/null
+++ b/drivers/input/touchscreen/melfas_mip4.c
@@ -0,0 +1,1517 @@
+/*
+ * MELFAS MIP4 Touchscreen
+ *
+ * Copyright (C) 2016 MELFAS Inc.
+ *
+ * Author : Sangwon Jee <jeesw@melfas.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/acpi.h>
+#include <linux/delay.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/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <asm/unaligned.h>
+
+#define MIP4_DEVICE_NAME "mip4_ts"
+
+/*****************************************************************
+ * Protocol
+ * Version : MIP 4.0 Rev 4.6
+ *****************************************************************/
+
+/* Address */
+#define MIP4_R0_BOOT 0x00
+#define MIP4_R1_BOOT_MODE 0x01
+#define MIP4_R1_BOOT_BUF_ADDR 0x10
+#define MIP4_R1_BOOT_STATUS 0x20
+#define MIP4_R1_BOOT_CMD 0x30
+#define MIP4_R1_BOOT_TARGET_ADDR 0x40
+#define MIP4_R1_BOOT_SIZE 0x44
+
+#define MIP4_R0_INFO 0x01
+#define MIP4_R1_INFO_PRODUCT_NAME 0x00
+#define MIP4_R1_INFO_RESOLUTION_X 0x10
+#define MIP4_R1_INFO_RESOLUTION_Y 0x12
+#define MIP4_R1_INFO_NODE_NUM_X 0x14
+#define MIP4_R1_INFO_NODE_NUM_Y 0x15
+#define MIP4_R1_INFO_KEY_NUM 0x16
+#define MIP4_R1_INFO_PRESSURE_NUM 0x17
+#define MIP4_R1_INFO_LENGTH_X 0x18
+#define MIP4_R1_INFO_LENGTH_Y 0x1A
+#define MIP4_R1_INFO_PPM_X 0x1C
+#define MIP4_R1_INFO_PPM_Y 0x1D
+#define MIP4_R1_INFO_VERSION_BOOT 0x20
+#define MIP4_R1_INFO_VERSION_CORE 0x22
+#define MIP4_R1_INFO_VERSION_APP 0x24
+#define MIP4_R1_INFO_VERSION_PARAM 0x26
+#define MIP4_R1_INFO_SECT_BOOT_START 0x30
+#define MIP4_R1_INFO_SECT_BOOT_END 0x31
+#define MIP4_R1_INFO_SECT_CORE_START 0x32
+#define MIP4_R1_INFO_SECT_CORE_END 0x33
+#define MIP4_R1_INFO_SECT_APP_START 0x34
+#define MIP4_R1_INFO_SECT_APP_END 0x35
+#define MIP4_R1_INFO_SECT_PARAM_START 0x36
+#define MIP4_R1_INFO_SECT_PARAM_END 0x37
+#define MIP4_R1_INFO_BUILD_DATE 0x40
+#define MIP4_R1_INFO_BUILD_TIME 0x44
+#define MIP4_R1_INFO_CHECKSUM_PRECALC 0x48
+#define MIP4_R1_INFO_CHECKSUM_REALTIME 0x4A
+#define MIP4_R1_INFO_PROTOCOL_NAME 0x50
+#define MIP4_R1_INFO_PROTOCOL_VERSION 0x58
+#define MIP4_R1_INFO_IC_ID 0x70
+#define MIP4_R1_INFO_IC_NAME 0x71
+#define MIP4_R1_INFO_IC_VENDOR_ID 0x75
+#define MIP4_R1_INFO_IC_HW_CATEGORY 0x77
+#define MIP4_R1_INFO_CONTACT_THD_SCR 0x78
+#define MIP4_R1_INFO_CONTACT_THD_KEY 0x7A
+
+#define MIP4_R0_EVENT 0x02
+#define MIP4_R1_EVENT_SUPPORTED_FUNC 0x00
+#define MIP4_R1_EVENT_FORMAT 0x04
+#define MIP4_R1_EVENT_SIZE 0x06
+#define MIP4_R1_EVENT_PACKET_INFO 0x10
+#define MIP4_R1_EVENT_PACKET_DATA 0x11
+
+#define MIP4_R0_CTRL 0x06
+#define MIP4_R1_CTRL_READY_STATUS 0x00
+#define MIP4_R1_CTRL_EVENT_READY 0x01
+#define MIP4_R1_CTRL_MODE 0x10
+#define MIP4_R1_CTRL_EVENT_TRIGGER_TYPE 0x11
+#define MIP4_R1_CTRL_RECALIBRATE 0x12
+#define MIP4_R1_CTRL_POWER_STATE 0x13
+#define MIP4_R1_CTRL_GESTURE_TYPE 0x14
+#define MIP4_R1_CTRL_DISABLE_ESD_ALERT 0x18
+#define MIP4_R1_CTRL_CHARGER_MODE 0x19
+#define MIP4_R1_CTRL_HIGH_SENS_MODE 0x1A
+#define MIP4_R1_CTRL_WINDOW_MODE 0x1B
+#define MIP4_R1_CTRL_PALM_REJECTION 0x1C
+#define MIP4_R1_CTRL_EDGE_CORRECTION 0x1D
+#define MIP4_R1_CTRL_ENTER_GLOVE_MODE 0x1E
+#define MIP4_R1_CTRL_I2C_ON_LPM 0x1F
+#define MIP4_R1_CTRL_GESTURE_DEBUG 0x20
+#define MIP4_R1_CTRL_PALM_EVENT 0x22
+#define MIP4_R1_CTRL_PROXIMITY_SENSING 0x23
+
+/* Value */
+#define MIP4_BOOT_MODE_BOOT 0x01
+#define MIP4_BOOT_MODE_APP 0x02
+
+#define MIP4_BOOT_STATUS_BUSY 0x05
+#define MIP4_BOOT_STATUS_ERROR 0x0E
+#define MIP4_BOOT_STATUS_DONE 0xA0
+
+#define MIP4_BOOT_CMD_MASS_ERASE 0x15
+#define MIP4_BOOT_CMD_PROGRAM 0x54
+#define MIP4_BOOT_CMD_ERASE 0x8F
+#define MIP4_BOOT_CMD_WRITE 0xA5
+#define MIP4_BOOT_CMD_READ 0xC2
+
+#define MIP4_EVENT_INPUT_TYPE_KEY 0
+#define MIP4_EVENT_INPUT_TYPE_SCREEN 1
+#define MIP4_EVENT_INPUT_TYPE_PROXIMITY 2
+
+#define I2C_RETRY_COUNT 3 /* 2~ */
+
+#define MIP4_BUF_SIZE 128
+#define MIP4_MAX_FINGERS 10
+#define MIP4_MAX_KEYS 4
+
+#define MIP4_TOUCH_MAJOR_MIN 0
+#define MIP4_TOUCH_MAJOR_MAX 255
+#define MIP4_TOUCH_MINOR_MIN 0
+#define MIP4_TOUCH_MINOR_MAX 255
+#define MIP4_PRESSURE_MIN 0
+#define MIP4_PRESSURE_MAX 255
+
+#define MIP4_FW_NAME "melfas_mip4.fw"
+#define MIP4_FW_UPDATE_DEBUG 0 /* 0 (default) or 1 */
+
+struct mip4_fw_version {
+ u16 boot;
+ u16 core;
+ u16 app;
+ u16 param;
+};
+
+struct mip4_ts {
+ struct i2c_client *client;
+ struct input_dev *input;
+ struct gpio_desc *gpio_ce;
+
+ char phys[32];
+ char product_name[16];
+
+ unsigned int max_x;
+ unsigned int max_y;
+ u8 node_x;
+ u8 node_y;
+ u8 node_key;
+ unsigned int ppm_x;
+ unsigned int ppm_y;
+
+ struct mip4_fw_version fw_version;
+
+ unsigned int event_size;
+ unsigned int event_format;
+
+ unsigned int key_num;
+ unsigned short key_code[MIP4_MAX_KEYS];
+
+ bool wake_irq_enabled;
+
+ u8 buf[MIP4_BUF_SIZE];
+};
+
+static int mip4_i2c_xfer(struct mip4_ts *ts,
+ char *write_buf, unsigned int write_len,
+ char *read_buf, unsigned int read_len)
+{
+ struct i2c_msg msg[] = {
+ {
+ .addr = ts->client->addr,
+ .flags = 0,
+ .buf = write_buf,
+ .len = write_len,
+ }, {
+ .addr = ts->client->addr,
+ .flags = I2C_M_RD,
+ .buf = read_buf,
+ .len = read_len,
+ },
+ };
+ int retry = I2C_RETRY_COUNT;
+ int res;
+ int error;
+
+ do {
+ res = i2c_transfer(ts->client->adapter, msg, ARRAY_SIZE(msg));
+ if (res == ARRAY_SIZE(msg))
+ return 0;
+
+ error = res < 0 ? res : -EIO;
+ dev_err(&ts->client->dev,
+ "%s - i2c_transfer failed: %d (%d)\n",
+ __func__, error, res);
+ } while (--retry);
+
+ return error;
+}
+
+static void mip4_parse_fw_version(const u8 *buf, struct mip4_fw_version *v)
+{
+ v->boot = get_unaligned_le16(buf + 0);
+ v->core = get_unaligned_le16(buf + 2);
+ v->app = get_unaligned_le16(buf + 4);
+ v->param = get_unaligned_le16(buf + 6);
+}
+
+/*
+ * Read chip firmware version
+ */
+static int mip4_get_fw_version(struct mip4_ts *ts)
+{
+ u8 cmd[] = { MIP4_R0_INFO, MIP4_R1_INFO_VERSION_BOOT };
+ u8 buf[sizeof(ts->fw_version)];
+ int error;
+
+ error = mip4_i2c_xfer(ts, cmd, sizeof(cmd), buf, sizeof(buf));
+ if (error) {
+ memset(&ts->fw_version, 0xff, sizeof(ts->fw_version));
+ return error;
+ }
+
+ mip4_parse_fw_version(buf, &ts->fw_version);
+
+ return 0;
+}
+
+/*
+ * Fetch device characteristics
+ */
+static int mip4_query_device(struct mip4_ts *ts)
+{
+ int error;
+ u8 cmd[2];
+ u8 buf[14];
+
+ /* Product name */
+ cmd[0] = MIP4_R0_INFO;
+ cmd[1] = MIP4_R1_INFO_PRODUCT_NAME;
+ error = mip4_i2c_xfer(ts, cmd, sizeof(cmd),
+ ts->product_name, sizeof(ts->product_name));
+ if (error)
+ dev_warn(&ts->client->dev,
+ "Failed to retrieve product name: %d\n", error);
+ else
+ dev_dbg(&ts->client->dev, "product name: %.*s\n",
+ (int)sizeof(ts->product_name), ts->product_name);
+
+ /* Firmware version */
+ error = mip4_get_fw_version(ts);
+ if (error)
+ dev_warn(&ts->client->dev,
+ "Failed to retrieve FW version: %d\n", error);
+ else
+ dev_dbg(&ts->client->dev, "F/W Version: %04X %04X %04X %04X\n",
+ ts->fw_version.boot, ts->fw_version.core,
+ ts->fw_version.app, ts->fw_version.param);
+
+ /* Resolution */
+ cmd[0] = MIP4_R0_INFO;
+ cmd[1] = MIP4_R1_INFO_RESOLUTION_X;
+ error = mip4_i2c_xfer(ts, cmd, sizeof(cmd), buf, 14);
+ if (error) {
+ dev_warn(&ts->client->dev,
+ "Failed to retrieve touchscreen parameters: %d\n",
+ error);
+ } else {
+ ts->max_x = get_unaligned_le16(&buf[0]);
+ ts->max_y = get_unaligned_le16(&buf[2]);
+ dev_dbg(&ts->client->dev, "max_x: %d, max_y: %d\n",
+ ts->max_x, ts->max_y);
+
+ ts->node_x = buf[4];
+ ts->node_y = buf[5];
+ ts->node_key = buf[6];
+ dev_dbg(&ts->client->dev,
+ "node_x: %d, node_y: %d, node_key: %d\n",
+ ts->node_x, ts->node_y, ts->node_key);
+
+ ts->ppm_x = buf[12];
+ ts->ppm_y = buf[13];
+ dev_dbg(&ts->client->dev, "ppm_x: %d, ppm_y: %d\n",
+ ts->ppm_x, ts->ppm_y);
+
+ /* Key ts */
+ if (ts->node_key > 0)
+ ts->key_num = ts->node_key;
+ }
+
+ /* Protocol */
+ cmd[0] = MIP4_R0_EVENT;
+ cmd[1] = MIP4_R1_EVENT_SUPPORTED_FUNC;
+ error = mip4_i2c_xfer(ts, cmd, sizeof(cmd), buf, 7);
+ if (error) {
+ dev_warn(&ts->client->dev,
+ "Failed to retrieve device type: %d\n", error);
+ ts->event_format = 0xff;
+ } else {
+ ts->event_format = get_unaligned_le16(&buf[4]);
+ ts->event_size = buf[6];
+ dev_dbg(&ts->client->dev, "event_format: %d, event_size: %d\n",
+ ts->event_format, ts->event_size);
+
+ if (ts->event_format == 2 || ts->event_format > 3)
+ dev_warn(&ts->client->dev,
+ "Unknown event format %d\n", ts->event_format);
+ }
+
+ return 0;
+}
+
+static int mip4_power_on(struct mip4_ts *ts)
+{
+ if (ts->gpio_ce) {
+ gpiod_set_value_cansleep(ts->gpio_ce, 1);
+
+ /* Booting delay : 200~300ms */
+ usleep_range(200 * 1000, 300 * 1000);
+ }
+
+ return 0;
+}
+
+static void mip4_power_off(struct mip4_ts *ts)
+{
+ if (ts->gpio_ce)
+ gpiod_set_value_cansleep(ts->gpio_ce, 0);
+}
+
+/*
+ * Clear touch input event status
+ */
+static void mip4_clear_input(struct mip4_ts *ts)
+{
+ int i;
+
+ /* Screen */
+ for (i = 0; i < MIP4_MAX_FINGERS; i++) {
+ input_mt_slot(ts->input, i);
+ input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, 0);
+ }
+
+ /* Keys */
+ for (i = 0; i < ts->key_num; i++)
+ input_report_key(ts->input, ts->key_code[i], 0);
+
+ input_sync(ts->input);
+}
+
+static int mip4_enable(struct mip4_ts *ts)
+{
+ int error;
+
+ error = mip4_power_on(ts);
+ if (error)
+ return error;
+
+ enable_irq(ts->client->irq);
+
+ return 0;
+}
+
+static void mip4_disable(struct mip4_ts *ts)
+{
+ disable_irq(ts->client->irq);
+
+ mip4_power_off(ts);
+
+ mip4_clear_input(ts);
+}
+
+/*****************************************************************
+ * Input handling
+ *****************************************************************/
+
+static void mip4_report_keys(struct mip4_ts *ts, u8 *packet)
+{
+ u8 key;
+ bool down;
+
+ switch (ts->event_format) {
+ case 0:
+ case 1:
+ key = packet[0] & 0x0F;
+ down = packet[0] & 0x80;
+ break;
+
+ case 3:
+ default:
+ key = packet[0] & 0x0F;
+ down = packet[1] & 0x01;
+ break;
+ }
+
+ /* Report key event */
+ if (key >= 1 && key <= ts->key_num) {
+ unsigned short keycode = ts->key_code[key - 1];
+
+ dev_dbg(&ts->client->dev,
+ "Key - ID: %d, keycode: %d, state: %d\n",
+ key, keycode, down);
+
+ input_event(ts->input, EV_MSC, MSC_SCAN, keycode);
+ input_report_key(ts->input, keycode, down);
+
+ } else {
+ dev_err(&ts->client->dev, "Unknown key: %d\n", key);
+ }
+}
+
+static void mip4_report_touch(struct mip4_ts *ts, u8 *packet)
+{
+ int id;
+ bool hover;
+ bool palm;
+ bool state;
+ u16 x, y;
+ u8 pressure_stage = 0;
+ u8 pressure;
+ u8 size;
+ u8 touch_major;
+ u8 touch_minor;
+
+ switch (ts->event_format) {
+ case 0:
+ case 1:
+ /* Touch only */
+ state = packet[0] & BIT(7);
+ hover = packet[0] & BIT(5);
+ palm = packet[0] & BIT(4);
+ id = (packet[0] & 0x0F) - 1;
+ x = ((packet[1] & 0x0F) << 8) | packet[2];
+ y = (((packet[1] >> 4) & 0x0F) << 8) |
+ packet[3];
+ pressure = packet[4];
+ size = packet[5];
+ if (ts->event_format == 0) {
+ touch_major = packet[5];
+ touch_minor = packet[5];
+ } else {
+ touch_major = packet[6];
+ touch_minor = packet[7];
+ }
+ break;
+
+ case 3:
+ default:
+ /* Touch + Force(Pressure) */
+ id = (packet[0] & 0x0F) - 1;
+ hover = packet[1] & BIT(2);
+ palm = packet[1] & BIT(1);
+ state = packet[1] & BIT(0);
+ x = ((packet[2] & 0x0F) << 8) | packet[3];
+ y = (((packet[2] >> 4) & 0x0F) << 8) |
+ packet[4];
+ size = packet[6];
+ pressure_stage = (packet[7] & 0xF0) >> 4;
+ pressure = ((packet[7] & 0x0F) << 8) |
+ packet[8];
+ touch_major = packet[9];
+ touch_minor = packet[10];
+ break;
+ }
+
+ dev_dbg(&ts->client->dev,
+ "Screen - Slot: %d State: %d X: %04d Y: %04d Z: %d\n",
+ id, state, x, y, pressure);
+
+ if (unlikely(id < 0 || id >= MIP4_MAX_FINGERS)) {
+ dev_err(&ts->client->dev, "Screen - invalid slot ID: %d\n", id);
+ } else if (state) {
+ /* Press or Move event */
+ input_mt_slot(ts->input, id);
+ input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, true);
+ input_report_abs(ts->input, ABS_MT_POSITION_X, x);
+ input_report_abs(ts->input, ABS_MT_POSITION_Y, y);
+ input_report_abs(ts->input, ABS_MT_PRESSURE, pressure);
+ input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, touch_major);
+ input_report_abs(ts->input, ABS_MT_TOUCH_MINOR, touch_minor);
+ } else {
+ /* Release event */
+ input_mt_slot(ts->input, id);
+ input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, 0);
+ }
+
+ input_mt_sync_frame(ts->input);
+}
+
+static int mip4_handle_packet(struct mip4_ts *ts, u8 *packet)
+{
+ u8 type;
+
+ switch (ts->event_format) {
+ case 0:
+ case 1:
+ type = (packet[0] & 0x40) >> 6;
+ break;
+
+ case 3:
+ type = (packet[0] & 0xF0) >> 4;
+ break;
+
+ default:
+ /* Should not happen unless we have corrupted firmware */
+ return -EINVAL;
+ }
+
+ dev_dbg(&ts->client->dev, "Type: %d\n", type);
+
+ /* Report input event */
+ switch (type) {
+ case MIP4_EVENT_INPUT_TYPE_KEY:
+ mip4_report_keys(ts, packet);
+ break;
+
+ case MIP4_EVENT_INPUT_TYPE_SCREEN:
+ mip4_report_touch(ts, packet);
+ break;
+
+ default:
+ dev_err(&ts->client->dev, "Unknown event type: %d\n", type);
+ break;
+ }
+
+ return 0;
+}
+
+static irqreturn_t mip4_interrupt(int irq, void *dev_id)
+{
+ struct mip4_ts *ts = dev_id;
+ struct i2c_client *client = ts->client;
+ unsigned int i;
+ int error;
+ u8 cmd[2];
+ u8 size;
+ bool alert;
+
+ /* Read packet info */
+ cmd[0] = MIP4_R0_EVENT;
+ cmd[1] = MIP4_R1_EVENT_PACKET_INFO;
+ error = mip4_i2c_xfer(ts, cmd, sizeof(cmd), ts->buf, 1);
+ if (error) {
+ dev_err(&client->dev,
+ "Failed to read packet info: %d\n", error);
+ goto out;
+ }
+
+ size = ts->buf[0] & 0x7F;
+ alert = ts->buf[0] & BIT(7);
+ dev_dbg(&client->dev, "packet size: %d, alert: %d\n", size, alert);
+
+ /* Check size */
+ if (!size) {
+ dev_err(&client->dev, "Empty packet\n");
+ goto out;
+ }
+
+ /* Read packet data */
+ cmd[0] = MIP4_R0_EVENT;
+ cmd[1] = MIP4_R1_EVENT_PACKET_DATA;
+ error = mip4_i2c_xfer(ts, cmd, sizeof(cmd), ts->buf, size);
+ if (error) {
+ dev_err(&client->dev,
+ "Failed to read packet data: %d\n", error);
+ goto out;
+ }
+
+ if (alert) {
+ dev_dbg(&client->dev, "Alert: %d\n", ts->buf[0]);
+ } else {
+ for (i = 0; i < size; i += ts->event_size) {
+ error = mip4_handle_packet(ts, &ts->buf[i]);
+ if (error)
+ break;
+ }
+
+ input_sync(ts->input);
+ }
+
+out:
+ return IRQ_HANDLED;
+}
+
+static int mip4_input_open(struct input_dev *dev)
+{
+ struct mip4_ts *ts = input_get_drvdata(dev);
+
+ return mip4_enable(ts);
+}
+
+static void mip4_input_close(struct input_dev *dev)
+{
+ struct mip4_ts *ts = input_get_drvdata(dev);
+
+ mip4_disable(ts);
+}
+
+/*****************************************************************
+ * Firmware update
+ *****************************************************************/
+
+/* Firmware Info */
+#define MIP4_BL_PAGE_SIZE 512 /* 512 */
+#define MIP4_BL_PACKET_SIZE 512 /* 512, 256, 128, 64, ... */
+
+/*
+ * Firmware binary tail info
+ */
+
+struct mip4_bin_tail {
+ u8 tail_mark[4];
+ u8 chip_name[4];
+
+ __le32 bin_start_addr;
+ __le32 bin_length;
+
+ __le16 ver_boot;
+ __le16 ver_core;
+ __le16 ver_app;
+ __le16 ver_param;
+
+ u8 boot_start;
+ u8 boot_end;
+ u8 core_start;
+ u8 core_end;
+ u8 app_start;
+ u8 app_end;
+ u8 param_start;
+ u8 param_end;
+
+ u8 checksum_type;
+ u8 hw_category;
+
+ __le16 param_id;
+ __le32 param_length;
+ __le32 build_date;
+ __le32 build_time;
+
+ __le32 reserved1;
+ __le32 reserved2;
+ __le16 reserved3;
+ __le16 tail_size;
+ __le32 crc;
+} __packed;
+
+#define MIP4_BIN_TAIL_MARK "MBT\001"
+#define MIP4_BIN_TAIL_SIZE (sizeof(struct mip4_bin_tail))
+
+/*
+* Bootloader - Read status
+*/
+static int mip4_bl_read_status(struct mip4_ts *ts)
+{
+ u8 cmd[] = { MIP4_R0_BOOT, MIP4_R1_BOOT_STATUS };
+ u8 result;
+ struct i2c_msg msg[] = {
+ {
+ .addr = ts->client->addr,
+ .flags = 0,
+ .buf = cmd,
+ .len = sizeof(cmd),
+ }, {
+ .addr = ts->client->addr,
+ .flags = I2C_M_RD,
+ .buf = &result,
+ .len = sizeof(result),
+ },
+ };
+ int ret;
+ int error;
+ int retry = 1000;
+
+ do {
+ ret = i2c_transfer(ts->client->adapter, msg, ARRAY_SIZE(msg));
+ if (ret != ARRAY_SIZE(msg)) {
+ error = ret < 0 ? ret : -EIO;
+ dev_err(&ts->client->dev,
+ "Failed to read bootloader status: %d\n",
+ error);
+ return error;
+ }
+
+ switch (result) {
+ case MIP4_BOOT_STATUS_DONE:
+ dev_dbg(&ts->client->dev, "%s - done\n", __func__);
+ return 0;
+
+ case MIP4_BOOT_STATUS_ERROR:
+ dev_err(&ts->client->dev, "Bootloader failure\n");
+ return -EIO;
+
+ case MIP4_BOOT_STATUS_BUSY:
+ dev_dbg(&ts->client->dev, "%s - Busy\n", __func__);
+ error = -EBUSY;
+ break;
+
+ default:
+ dev_err(&ts->client->dev,
+ "Unexpected bootloader status: %#02x\n",
+ result);
+ error = -EINVAL;
+ break;
+ }
+
+ usleep_range(1000, 2000);
+ } while (--retry);
+
+ return error;
+}
+
+/*
+* Bootloader - Change mode
+*/
+static int mip4_bl_change_mode(struct mip4_ts *ts, u8 mode)
+{
+ u8 mode_chg_cmd[] = { MIP4_R0_BOOT, MIP4_R1_BOOT_MODE, mode };
+ u8 mode_read_cmd[] = { MIP4_R0_BOOT, MIP4_R1_BOOT_MODE };
+ u8 result;
+ struct i2c_msg msg[] = {
+ {
+ .addr = ts->client->addr,
+ .flags = 0,
+ .buf = mode_read_cmd,
+ .len = sizeof(mode_read_cmd),
+ }, {
+ .addr = ts->client->addr,
+ .flags = I2C_M_RD,
+ .buf = &result,
+ .len = sizeof(result),
+ },
+ };
+ int retry = 10;
+ int ret;
+ int error;
+
+ do {
+ /* Send mode change command */
+ ret = i2c_master_send(ts->client,
+ mode_chg_cmd, sizeof(mode_chg_cmd));
+ if (ret != sizeof(mode_chg_cmd)) {
+ error = ret < 0 ? ret : -EIO;
+ dev_err(&ts->client->dev,
+ "Failed to send %d mode change: %d (%d)\n",
+ mode, error, ret);
+ return error;
+ }
+
+ dev_dbg(&ts->client->dev,
+ "Sent mode change request (mode: %d)\n", mode);
+
+ /* Wait */
+ msleep(1000);
+
+ /* Verify target mode */
+ ret = i2c_transfer(ts->client->adapter, msg, ARRAY_SIZE(msg));
+ if (ret != ARRAY_SIZE(msg)) {
+ error = ret < 0 ? ret : -EIO;
+ dev_err(&ts->client->dev,
+ "Failed to read device mode: %d\n", error);
+ return error;
+ }
+
+ dev_dbg(&ts->client->dev,
+ "Current device mode: %d, want: %d\n", result, mode);
+
+ if (result == mode)
+ return 0;
+
+ } while (--retry);
+
+ return -EIO;
+}
+
+/*
+ * Bootloader - Start bootloader mode
+ */
+static int mip4_bl_enter(struct mip4_ts *ts)
+{
+ return mip4_bl_change_mode(ts, MIP4_BOOT_MODE_BOOT);
+}
+
+/*
+ * Bootloader - Exit bootloader mode
+ */
+static int mip4_bl_exit(struct mip4_ts *ts)
+{
+ return mip4_bl_change_mode(ts, MIP4_BOOT_MODE_APP);
+}
+
+static int mip4_bl_get_address(struct mip4_ts *ts, u16 *buf_addr)
+{
+ u8 cmd[] = { MIP4_R0_BOOT, MIP4_R1_BOOT_BUF_ADDR };
+ u8 result[sizeof(u16)];
+ struct i2c_msg msg[] = {
+ {
+ .addr = ts->client->addr,
+ .flags = 0,
+ .buf = cmd,
+ .len = sizeof(cmd),
+ }, {
+ .addr = ts->client->addr,
+ .flags = I2C_M_RD,
+ .buf = result,
+ .len = sizeof(result),
+ },
+ };
+ int ret;
+ int error;
+
+ ret = i2c_transfer(ts->client->adapter, msg, ARRAY_SIZE(msg));
+ if (ret != ARRAY_SIZE(msg)) {
+ error = ret < 0 ? ret : -EIO;
+ dev_err(&ts->client->dev,
+ "Failed to retrieve bootloader buffer address: %d\n",
+ error);
+ return error;
+ }
+
+ *buf_addr = get_unaligned_le16(result);
+ dev_dbg(&ts->client->dev,
+ "Bootloader buffer address %#04x\n", *buf_addr);
+
+ return 0;
+}
+
+static int mip4_bl_program_page(struct mip4_ts *ts, int offset,
+ const u8 *data, int length, u16 buf_addr)
+{
+ u8 cmd[6];
+ u8 *data_buf;
+ u16 buf_offset;
+ int ret;
+ int error;
+
+ dev_dbg(&ts->client->dev, "Writing page @%#06x (%d)\n",
+ offset, length);
+
+ if (length > MIP4_BL_PAGE_SIZE || length % MIP4_BL_PACKET_SIZE) {
+ dev_err(&ts->client->dev,
+ "Invalid page length: %d\n", length);
+ return -EINVAL;
+ }
+
+ data_buf = kmalloc(2 + MIP4_BL_PACKET_SIZE, GFP_KERNEL);
+ if (!data_buf)
+ return -ENOMEM;
+
+ /* Addr */
+ cmd[0] = MIP4_R0_BOOT;
+ cmd[1] = MIP4_R1_BOOT_TARGET_ADDR;
+ put_unaligned_le32(offset, &cmd[2]);
+ ret = i2c_master_send(ts->client, cmd, 6);
+ if (ret != 6) {
+ error = ret < 0 ? ret : -EIO;
+ dev_err(&ts->client->dev,
+ "Failed to send write page address: %d\n", error);
+ goto out;
+ }
+
+ /* Size */
+ cmd[0] = MIP4_R0_BOOT;
+ cmd[1] = MIP4_R1_BOOT_SIZE;
+ put_unaligned_le32(length, &cmd[2]);
+ ret = i2c_master_send(ts->client, cmd, 6);
+ if (ret != 6) {
+ error = ret < 0 ? ret : -EIO;
+ dev_err(&ts->client->dev,
+ "Failed to send write page size: %d\n", error);
+ goto out;
+ }
+
+ /* Data */
+ for (buf_offset = 0;
+ buf_offset < length;
+ buf_offset += MIP4_BL_PACKET_SIZE) {
+ dev_dbg(&ts->client->dev,
+ "writing chunk at %#04x (size %d)\n",
+ buf_offset, MIP4_BL_PACKET_SIZE);
+ put_unaligned_be16(buf_addr + buf_offset, data_buf);
+ memcpy(&data_buf[2], &data[buf_offset], MIP4_BL_PACKET_SIZE);
+ ret = i2c_master_send(ts->client,
+ data_buf, 2 + MIP4_BL_PACKET_SIZE);
+ if (ret != 2 + MIP4_BL_PACKET_SIZE) {
+ error = ret < 0 ? ret : -EIO;
+ dev_err(&ts->client->dev,
+ "Failed to read chunk at %#04x (size %d): %d\n",
+ buf_offset, MIP4_BL_PACKET_SIZE, error);
+ goto out;
+ }
+ }
+
+ /* Command */
+ cmd[0] = MIP4_R0_BOOT;
+ cmd[1] = MIP4_R1_BOOT_CMD;
+ cmd[2] = MIP4_BOOT_CMD_PROGRAM;
+ ret = i2c_master_send(ts->client, cmd, 3);
+ if (ret != 3) {
+ error = ret < 0 ? ret : -EIO;
+ dev_err(&ts->client->dev,
+ "Failed to send 'write' command: %d\n", error);
+ goto out;
+ }
+
+ /* Status */
+ error = mip4_bl_read_status(ts);
+
+out:
+ kfree(data_buf);
+ return error ? error : 0;
+}
+
+static int mip4_bl_verify_page(struct mip4_ts *ts, int offset,
+ const u8 *data, int length, int buf_addr)
+{
+ u8 cmd[8];
+ u8 *read_buf;
+ int buf_offset;
+ struct i2c_msg msg[] = {
+ {
+ .addr = ts->client->addr,
+ .flags = 0,
+ .buf = cmd,
+ .len = 2,
+ }, {
+ .addr = ts->client->addr,
+ .flags = I2C_M_RD,
+ .len = MIP4_BL_PACKET_SIZE,
+ },
+ };
+ int ret;
+ int error;
+
+ dev_dbg(&ts->client->dev, "Validating page @%#06x (%d)\n",
+ offset, length);
+
+ /* Addr */
+ cmd[0] = MIP4_R0_BOOT;
+ cmd[1] = MIP4_R1_BOOT_TARGET_ADDR;
+ put_unaligned_le32(offset, &cmd[2]);
+ ret = i2c_master_send(ts->client, cmd, 6);
+ if (ret != 6) {
+ error = ret < 0 ? ret : -EIO;
+ dev_err(&ts->client->dev,
+ "Failed to send read page address: %d\n", error);
+ return error;
+ }
+
+ /* Size */
+ cmd[0] = MIP4_R0_BOOT;
+ cmd[1] = MIP4_R1_BOOT_SIZE;
+ put_unaligned_le32(length, &cmd[2]);
+ ret = i2c_master_send(ts->client, cmd, 6);
+ if (ret != 6) {
+ error = ret < 0 ? ret : -EIO;
+ dev_err(&ts->client->dev,
+ "Failed to send read page size: %d\n", error);
+ return error;
+ }
+
+ /* Command */
+ cmd[0] = MIP4_R0_BOOT;
+ cmd[1] = MIP4_R1_BOOT_CMD;
+ cmd[2] = MIP4_BOOT_CMD_READ;
+ ret = i2c_master_send(ts->client, cmd, 3);
+ if (ret != 3) {
+ error = ret < 0 ? ret : -EIO;
+ dev_err(&ts->client->dev,
+ "Failed to send 'read' command: %d\n", error);
+ return error;
+ }
+
+ /* Status */
+ error = mip4_bl_read_status(ts);
+ if (error)
+ return error;
+
+ /* Read */
+ msg[1].buf = read_buf = kmalloc(MIP4_BL_PACKET_SIZE, GFP_KERNEL);
+ if (!read_buf)
+ return -ENOMEM;
+
+ for (buf_offset = 0;
+ buf_offset < length;
+ buf_offset += MIP4_BL_PACKET_SIZE) {
+ dev_dbg(&ts->client->dev,
+ "reading chunk at %#04x (size %d)\n",
+ buf_offset, MIP4_BL_PACKET_SIZE);
+ put_unaligned_be16(buf_addr + buf_offset, cmd);
+ ret = i2c_transfer(ts->client->adapter, msg, ARRAY_SIZE(msg));
+ if (ret != ARRAY_SIZE(msg)) {
+ error = ret < 0 ? ret : -EIO;
+ dev_err(&ts->client->dev,
+ "Failed to read chunk at %#04x (size %d): %d\n",
+ buf_offset, MIP4_BL_PACKET_SIZE, error);
+ break;
+ }
+
+ if (memcmp(&data[buf_offset], read_buf, MIP4_BL_PACKET_SIZE)) {
+ dev_err(&ts->client->dev,
+ "Failed to validate chunk at %#04x (size %d)\n",
+ buf_offset, MIP4_BL_PACKET_SIZE);
+#if MIP4_FW_UPDATE_DEBUG
+ print_hex_dump(KERN_DEBUG,
+ MIP4_DEVICE_NAME " F/W File: ",
+ DUMP_PREFIX_OFFSET, 16, 1,
+ data + offset, MIP4_BL_PACKET_SIZE,
+ false);
+ print_hex_dump(KERN_DEBUG,
+ MIP4_DEVICE_NAME " F/W Chip: ",
+ DUMP_PREFIX_OFFSET, 16, 1,
+ read_buf, MIP4_BL_PAGE_SIZE, false);
+#endif
+ error = -EINVAL;
+ break;
+ }
+ }
+
+ kfree(read_buf);
+ return error ? error : 0;
+}
+
+/*
+ * Flash chip firmware
+ */
+static int mip4_flash_fw(struct mip4_ts *ts,
+ const u8 *fw_data, u32 fw_size, u32 fw_offset)
+{
+ struct i2c_client *client = ts->client;
+ int offset;
+ u16 buf_addr;
+ int error, error2;
+
+ /* Enter bootloader mode */
+ dev_dbg(&client->dev, "Entering bootloader mode\n");
+
+ error = mip4_bl_enter(ts);
+ if (error) {
+ dev_err(&client->dev,
+ "Failed to enter bootloader mode: %d\n",
+ error);
+ return error;
+ }
+
+ /* Read info */
+ error = mip4_bl_get_address(ts, &buf_addr);
+ if (error)
+ goto exit_bl;
+
+ /* Program & Verify */
+ dev_dbg(&client->dev,
+ "Program & Verify, page size: %d, packet size: %d\n",
+ MIP4_BL_PAGE_SIZE, MIP4_BL_PACKET_SIZE);
+
+ for (offset = fw_offset;
+ offset < fw_offset + fw_size;
+ offset += MIP4_BL_PAGE_SIZE) {
+ /* Program */
+ error = mip4_bl_program_page(ts, offset, fw_data + offset,
+ MIP4_BL_PAGE_SIZE, buf_addr);
+ if (error)
+ break;
+
+ /* Verify */
+ error = mip4_bl_verify_page(ts, offset, fw_data + offset,
+ MIP4_BL_PAGE_SIZE, buf_addr);
+ if (error)
+ break;
+ }
+
+exit_bl:
+ /* Exit bootloader mode */
+ dev_dbg(&client->dev, "Exiting bootloader mode\n");
+
+ error2 = mip4_bl_exit(ts);
+ if (error2) {
+ dev_err(&client->dev,
+ "Failed to exit bootloader mode: %d\n", error2);
+ if (!error)
+ error = error2;
+ }
+
+ /* Reset chip */
+ mip4_power_off(ts);
+ mip4_power_on(ts);
+
+ mip4_query_device(ts);
+
+ /* Refresh device parameters */
+ input_set_abs_params(ts->input, ABS_MT_POSITION_X, 0, ts->max_x, 0, 0);
+ input_set_abs_params(ts->input, ABS_MT_POSITION_Y, 0, ts->max_y, 0, 0);
+ input_set_abs_params(ts->input, ABS_X, 0, ts->max_x, 0, 0);
+ input_set_abs_params(ts->input, ABS_Y, 0, ts->max_y, 0, 0);
+ input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->ppm_x);
+ input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->ppm_y);
+ input_abs_set_res(ts->input, ABS_X, ts->ppm_x);
+ input_abs_set_res(ts->input, ABS_Y, ts->ppm_y);
+
+ return error ? error : 0;
+}
+
+static int mip4_parse_firmware(struct mip4_ts *ts, const struct firmware *fw,
+ u32 *fw_offset_start, u32 *fw_size,
+ const struct mip4_bin_tail **pfw_info)
+{
+ const struct mip4_bin_tail *fw_info;
+ struct mip4_fw_version fw_version;
+ u16 tail_size;
+
+ if (fw->size < MIP4_BIN_TAIL_SIZE) {
+ dev_err(&ts->client->dev,
+ "Invalid firmware, size mismatch (tail %zd vs %zd)\n",
+ MIP4_BIN_TAIL_SIZE, fw->size);
+ return -EINVAL;
+ }
+
+ fw_info = (const void *)&fw->data[fw->size - MIP4_BIN_TAIL_SIZE];
+
+#if MIP4_FW_UPDATE_DEBUG
+ print_hex_dump(KERN_ERR, MIP4_DEVICE_NAME " Bin Info: ",
+ DUMP_PREFIX_OFFSET, 16, 1, *fw_info, tail_size, false);
+#endif
+
+ tail_size = get_unaligned_le16(&fw_info->tail_size);
+ if (tail_size != MIP4_BIN_TAIL_SIZE) {
+ dev_err(&ts->client->dev,
+ "wrong tail size: %d (expected %zd)\n",
+ tail_size, MIP4_BIN_TAIL_SIZE);
+ return -EINVAL;
+ }
+
+ /* Check bin format */
+ if (memcmp(fw_info->tail_mark, MIP4_BIN_TAIL_MARK,
+ sizeof(fw_info->tail_mark))) {
+ dev_err(&ts->client->dev,
+ "unable to locate tail marker (%*ph vs %*ph)\n",
+ (int)sizeof(fw_info->tail_mark), fw_info->tail_mark,
+ (int)sizeof(fw_info->tail_mark), MIP4_BIN_TAIL_MARK);
+ return -EINVAL;
+ }
+
+ *fw_offset_start = get_unaligned_le32(&fw_info->bin_start_addr);
+ *fw_size = get_unaligned_le32(&fw_info->bin_length);
+
+ dev_dbg(&ts->client->dev,
+ "F/W Data offset: %#08x, size: %d\n",
+ *fw_offset_start, *fw_size);
+
+ if (*fw_size % MIP4_BL_PAGE_SIZE) {
+ dev_err(&ts->client->dev,
+ "encoded fw length %d is not multiple of pages (%d)\n",
+ *fw_size, MIP4_BL_PAGE_SIZE);
+ return -EINVAL;
+ }
+
+ if (fw->size != *fw_offset_start + *fw_size) {
+ dev_err(&ts->client->dev,
+ "Wrong firmware size, expected %d bytes, got %zd\n",
+ *fw_offset_start + *fw_size, fw->size);
+ return -EINVAL;
+ }
+
+ mip4_parse_fw_version((const u8 *)&fw_info->ver_boot, &fw_version);
+
+ dev_dbg(&ts->client->dev,
+ "F/W file version %04X %04X %04X %04X\n",
+ fw_version.boot, fw_version.core,
+ fw_version.app, fw_version.param);
+
+ dev_dbg(&ts->client->dev, "F/W chip version: %04X %04X %04X %04X\n",
+ ts->fw_version.boot, ts->fw_version.core,
+ ts->fw_version.app, ts->fw_version.param);
+
+ /* Check F/W type */
+ if (fw_version.boot != 0xEEEE && fw_version.boot != 0xFFFF &&
+ fw_version.core == 0xEEEE &&
+ fw_version.app == 0xEEEE &&
+ fw_version.param == 0xEEEE) {
+ dev_dbg(&ts->client->dev, "F/W type: Bootloader\n");
+ } else if (fw_version.boot == 0xEEEE &&
+ fw_version.core != 0xEEEE && fw_version.core != 0xFFFF &&
+ fw_version.app != 0xEEEE && fw_version.app != 0xFFFF &&
+ fw_version.param != 0xEEEE && fw_version.param != 0xFFFF) {
+ dev_dbg(&ts->client->dev, "F/W type: Main\n");
+ } else {
+ dev_err(&ts->client->dev, "Wrong firmware type\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int mip4_execute_fw_update(struct mip4_ts *ts, const struct firmware *fw)
+{
+ const struct mip4_bin_tail *fw_info;
+ u32 fw_start_offset;
+ u32 fw_size;
+ int retires = 3;
+ int error;
+
+ error = mip4_parse_firmware(ts, fw,
+ &fw_start_offset, &fw_size, &fw_info);
+ if (error)
+ return error;
+
+ if (ts->input->users) {
+ disable_irq(ts->client->irq);
+ } else {
+ error = mip4_power_on(ts);
+ if (error)
+ return error;
+ }
+
+ /* Update firmware */
+ do {
+ error = mip4_flash_fw(ts, fw->data, fw_size, fw_start_offset);
+ if (!error)
+ break;
+ } while (--retires);
+
+ if (error)
+ dev_err(&ts->client->dev,
+ "Failed to flash firmware: %d\n", error);
+
+ /* Enable IRQ */
+ if (ts->input->users)
+ enable_irq(ts->client->irq);
+ else
+ mip4_power_off(ts);
+
+ return error ? error : 0;
+}
+
+static ssize_t mip4_sysfs_fw_update(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mip4_ts *ts = i2c_get_clientdata(client);
+ const struct firmware *fw;
+ int error;
+
+ error = request_firmware(&fw, MIP4_FW_NAME, dev);
+ if (error) {
+ dev_err(&ts->client->dev,
+ "Failed to retrieve firmware %s: %d\n",
+ MIP4_FW_NAME, error);
+ return error;
+ }
+
+ /*
+ * Take input mutex to prevent racing with itself and also with
+ * userspace opening and closing the device and also suspend/resume
+ * transitions.
+ */
+ mutex_lock(&ts->input->mutex);
+
+ error = mip4_execute_fw_update(ts, fw);
+
+ mutex_unlock(&ts->input->mutex);
+
+ release_firmware(fw);
+
+ if (error) {
+ dev_err(&ts->client->dev,
+ "Firmware update failed: %d\n", error);
+ return error;
+ }
+
+ return count;
+}
+
+static DEVICE_ATTR(update_fw, S_IWUSR, NULL, mip4_sysfs_fw_update);
+
+static ssize_t mip4_sysfs_read_fw_version(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mip4_ts *ts = i2c_get_clientdata(client);
+ size_t count;
+
+ /* Take lock to prevent racing with firmware update */
+ mutex_lock(&ts->input->mutex);
+
+ count = snprintf(buf, PAGE_SIZE, "%04X %04X %04X %04X\n",
+ ts->fw_version.boot, ts->fw_version.core,
+ ts->fw_version.app, ts->fw_version.param);
+
+ mutex_unlock(&ts->input->mutex);
+
+ return count;
+}
+
+static DEVICE_ATTR(fw_version, S_IRUGO, mip4_sysfs_read_fw_version, NULL);
+
+static struct attribute *mip4_attrs[] = {
+ &dev_attr_fw_version.attr,
+ &dev_attr_update_fw.attr,
+ NULL,
+};
+
+static const struct attribute_group mip4_attr_group = {
+ .attrs = mip4_attrs,
+};
+
+static void mip4_sysfs_remove(void *_data)
+{
+ struct mip4_ts *ts = _data;
+
+ sysfs_remove_group(&ts->client->dev.kobj, &mip4_attr_group);
+}
+
+static int mip4_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct mip4_ts *ts;
+ struct input_dev *input;
+ int error;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "Not supported I2C adapter\n");
+ return -ENXIO;
+ }
+
+ ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL);
+ if (!ts)
+ return -ENOMEM;
+
+ input = devm_input_allocate_device(&client->dev);
+ if (!input)
+ return -ENOMEM;
+
+ ts->client = client;
+ ts->input = input;
+
+ snprintf(ts->phys, sizeof(ts->phys),
+ "%s/input0", dev_name(&client->dev));
+
+ ts->gpio_ce = devm_gpiod_get_optional(&client->dev,
+ "ce", GPIOD_OUT_LOW);
+ if (IS_ERR(ts->gpio_ce)) {
+ error = PTR_ERR(ts->gpio_ce);
+ if (error != EPROBE_DEFER)
+ dev_err(&client->dev,
+ "Failed to get gpio: %d\n", error);
+ return error;
+ }
+
+ error = mip4_power_on(ts);
+ if (error)
+ return error;
+ error = mip4_query_device(ts);
+ mip4_power_off(ts);
+ if (error)
+ return error;
+
+ input->name = "MELFAS MIP4 Touchscreen";
+ input->phys = ts->phys;
+
+ input->id.bustype = BUS_I2C;
+ input->id.vendor = 0x13c5;
+
+ input->open = mip4_input_open;
+ input->close = mip4_input_close;
+
+ input_set_drvdata(input, ts);
+
+ input->keycode = ts->key_code;
+ input->keycodesize = sizeof(*ts->key_code);
+ input->keycodemax = ts->key_num;
+
+ input_set_abs_params(input, ABS_MT_POSITION_X, 0, ts->max_x, 0, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ts->max_y, 0, 0);
+ input_set_abs_params(input, ABS_MT_PRESSURE,
+ MIP4_PRESSURE_MIN, MIP4_PRESSURE_MAX, 0, 0);
+ input_set_abs_params(input, ABS_MT_TOUCH_MAJOR,
+ MIP4_TOUCH_MAJOR_MIN, MIP4_TOUCH_MAJOR_MAX, 0, 0);
+ input_set_abs_params(input, ABS_MT_TOUCH_MINOR,
+ MIP4_TOUCH_MINOR_MIN, MIP4_TOUCH_MINOR_MAX, 0, 0);
+ input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->ppm_x);
+ input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->ppm_y);
+
+ error = input_mt_init_slots(input, MIP4_MAX_FINGERS, INPUT_MT_DIRECT);
+ if (error)
+ return error;
+
+ i2c_set_clientdata(client, ts);
+
+ error = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, mip4_interrupt,
+ IRQF_ONESHOT, MIP4_DEVICE_NAME, ts);
+ if (error) {
+ dev_err(&client->dev,
+ "Failed to request interrupt %d: %d\n",
+ client->irq, error);
+ return error;
+ }
+
+ disable_irq(client->irq);
+
+ error = input_register_device(input);
+ if (error) {
+ dev_err(&client->dev,
+ "Failed to register input device: %d\n", error);
+ return error;
+ }
+
+ error = sysfs_create_group(&client->dev.kobj, &mip4_attr_group);
+ if (error) {
+ dev_err(&client->dev,
+ "Failed to create sysfs attribute group: %d\n", error);
+ return error;
+ }
+
+ error = devm_add_action(&client->dev, mip4_sysfs_remove, ts);
+ if (error) {
+ mip4_sysfs_remove(ts);
+ dev_err(&client->dev,
+ "Failed to install sysfs remoce action: %d\n", error);
+ return error;
+ }
+
+ return 0;
+}
+
+static int __maybe_unused mip4_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mip4_ts *ts = i2c_get_clientdata(client);
+ struct input_dev *input = ts->input;
+
+ mutex_lock(&input->mutex);
+
+ if (device_may_wakeup(dev))
+ ts->wake_irq_enabled = enable_irq_wake(client->irq) == 0;
+ else if (input->users)
+ mip4_disable(ts);
+
+ mutex_unlock(&input->mutex);
+
+ return 0;
+}
+
+static int __maybe_unused mip4_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mip4_ts *ts = i2c_get_clientdata(client);
+ struct input_dev *input = ts->input;
+
+ mutex_lock(&input->mutex);
+
+ if (ts->wake_irq_enabled)
+ disable_irq_wake(client->irq);
+ else if (input->users)
+ mip4_enable(ts);
+
+ mutex_unlock(&input->mutex);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(mip4_pm_ops, mip4_suspend, mip4_resume);
+
+#ifdef CONFIG_OF
+static const struct of_device_id mip4_of_match[] = {
+ { .compatible = "melfas,"MIP4_DEVICE_NAME, },
+ { },
+};
+MODULE_DEVICE_TABLE(of, mip4_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id mip4_acpi_match[] = {
+ { "MLFS0000", 0},
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, mip4_acpi_match);
+#endif
+
+static const struct i2c_device_id mip4_i2c_ids[] = {
+ { MIP4_DEVICE_NAME, 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, mip4_i2c_ids);
+
+static struct i2c_driver mip4_driver = {
+ .id_table = mip4_i2c_ids,
+ .probe = mip4_probe,
+ .driver = {
+ .name = MIP4_DEVICE_NAME,
+ .of_match_table = of_match_ptr(mip4_of_match),
+ .acpi_match_table = ACPI_PTR(mip4_acpi_match),
+ .pm = &mip4_pm_ops,
+ },
+};
+module_i2c_driver(mip4_driver);
+
+MODULE_DESCRIPTION("MELFAS MIP4 Touchscreen");
+MODULE_VERSION("2016.03.03");
+MODULE_AUTHOR("Sangwon Jee <jeesw@melfas.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/stmpe-ts.c b/drivers/input/touchscreen/stmpe-ts.c
index e414d43e5159..2a78e27b4495 100644
--- a/drivers/input/touchscreen/stmpe-ts.c
+++ b/drivers/input/touchscreen/stmpe-ts.c
@@ -63,6 +63,37 @@
#define STMPE_TS_NAME "stmpe-ts"
#define XY_MASK 0xfff
+/**
+ * struct stmpe_touch - stmpe811 touch screen controller state
+ * @stmpe: pointer back to STMPE MFD container
+ * @idev: registered input device
+ * @work: a work item used to scan the device
+ * @dev: a pointer back to the MFD cell struct device*
+ * @sample_time: ADC converstion time in number of clock.
+ * (0 -> 36 clocks, 1 -> 44 clocks, 2 -> 56 clocks, 3 -> 64 clocks,
+ * 4 -> 80 clocks, 5 -> 96 clocks, 6 -> 144 clocks),
+ * recommended is 4.
+ * @mod_12b: ADC Bit mode (0 -> 10bit ADC, 1 -> 12bit ADC)
+ * @ref_sel: ADC reference source
+ * (0 -> internal reference, 1 -> external reference)
+ * @adc_freq: ADC Clock speed
+ * (0 -> 1.625 MHz, 1 -> 3.25 MHz, 2 || 3 -> 6.5 MHz)
+ * @ave_ctrl: Sample average control
+ * (0 -> 1 sample, 1 -> 2 samples, 2 -> 4 samples, 3 -> 8 samples)
+ * @touch_det_delay: Touch detect interrupt delay
+ * (0 -> 10 us, 1 -> 50 us, 2 -> 100 us, 3 -> 500 us,
+ * 4-> 1 ms, 5 -> 5 ms, 6 -> 10 ms, 7 -> 50 ms)
+ * recommended is 3
+ * @settling: Panel driver settling time
+ * (0 -> 10 us, 1 -> 100 us, 2 -> 500 us, 3 -> 1 ms,
+ * 4 -> 5 ms, 5 -> 10 ms, 6 for 50 ms, 7 -> 100 ms)
+ * recommended is 2
+ * @fraction_z: Length of the fractional part in z
+ * (fraction_z ([0..7]) = Count of the fractional part)
+ * recommended is 7
+ * @i_drive: current limit value of the touchscreen drivers
+ * (0 -> 20 mA typical 35 mA max, 1 -> 50 mA typical 80 mA max)
+ */
struct stmpe_touch {
struct stmpe *stmpe;
struct input_dev *idev;