aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/input/touchscreen
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/touchscreen')
-rw-r--r--drivers/input/touchscreen/Kconfig43
-rw-r--r--drivers/input/touchscreen/Makefile3
-rw-r--r--drivers/input/touchscreen/ar1021_i2c.c2
-rw-r--r--drivers/input/touchscreen/atmel_mxt_ts.c532
-rw-r--r--drivers/input/touchscreen/bcm_iproc_tsc.c522
-rw-r--r--drivers/input/touchscreen/chipone_icn8318.c316
-rw-r--r--drivers/input/touchscreen/edt-ft5x06.c6
-rw-r--r--drivers/input/touchscreen/elants_i2c.c14
-rw-r--r--drivers/input/touchscreen/goodix.c36
-rw-r--r--drivers/input/touchscreen/of_touchscreen.c62
-rw-r--r--drivers/input/touchscreen/sun4i-ts.c46
-rw-r--r--drivers/input/touchscreen/sur40.c434
-rw-r--r--drivers/input/touchscreen/sx8654.c286
-rw-r--r--drivers/input/touchscreen/tsc2007.c11
-rw-r--r--drivers/input/touchscreen/usbtouchscreen.c11
15 files changed, 2212 insertions, 112 deletions
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 6261fd6d7c3c..80f6386709bf 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -140,6 +140,19 @@ config TOUCHSCREEN_BU21013
To compile this driver as a module, choose M here: the
module will be called bu21013_ts.
+config TOUCHSCREEN_CHIPONE_ICN8318
+ tristate "chipone icn8318 touchscreen controller"
+ depends on GPIOLIB
+ depends on I2C
+ depends on OF
+ help
+ Say Y here if you have a ChipOne icn8318 based I2C touchscreen.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called chipone_icn8318.
+
config TOUCHSCREEN_CY8CTMG110
tristate "cy8ctmg110 touchscreen"
depends on I2C
@@ -297,11 +310,12 @@ config TOUCHSCREEN_FUJITSU
config TOUCHSCREEN_GOODIX
tristate "Goodix I2C touchscreen"
- depends on I2C && ACPI
+ depends on I2C
help
Say Y here if you have the Goodix touchscreen (such as one
installed in Onda v975w tablets) connected to your
- system.
+ system. It also supports 5-finger chip models, which can be
+ found on ARM tablets, like Wexler TAB7200 and MSI Primo73.
If unsure, say N.
@@ -323,6 +337,18 @@ config TOUCHSCREEN_ILI210X
To compile this driver as a module, choose M here: the
module will be called ili210x.
+config TOUCHSCREEN_IPROC
+ tristate "IPROC touch panel driver support"
+ depends on ARCH_BCM_IPROC || COMPILE_TEST
+ help
+ Say Y here if you want to add support for the IPROC touch
+ controller to your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bcm_iproc_tsc.
+
config TOUCHSCREEN_S3C2410
tristate "Samsung S3C2410/generic touchscreen input driver"
depends on ARCH_S3C24XX || SAMSUNG_DEV_TS
@@ -954,7 +980,9 @@ config TOUCHSCREEN_SUN4I
config TOUCHSCREEN_SUR40
tristate "Samsung SUR40 (Surface 2.0/PixelSense) touchscreen"
depends on USB
+ depends on MEDIA_USB_SUPPORT
select INPUT_POLLDEV
+ select VIDEOBUF2_DMA_SG
help
Say Y here if you want support for the Samsung SUR40 touchscreen
(also known as Microsoft Surface 2.0 or Microsoft PixelSense).
@@ -962,6 +990,17 @@ config TOUCHSCREEN_SUR40
To compile this driver as a module, choose M here: the
module will be called sur40.
+config TOUCHSCREEN_SX8654
+ tristate "Semtech SX8654 touchscreen"
+ depends on I2C
+ help
+ Say Y here if you have a Semtech SX8654 touchscreen controller.
+
+ If unsure, say N
+
+ To compile this driver as a module, choose M here: the
+ module will be called sx8654.
+
config TOUCHSCREEN_TPS6507X
tristate "TPS6507x based touchscreens"
depends on I2C
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 0242fea2102a..44deea743d02 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_TOUCHSCREEN_AR1021_I2C) += ar1021_i2c.o
obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT) += atmel_mxt_ts.o
obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR) += auo-pixcir-ts.o
obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o
+obj-$(CONFIG_TOUCHSCREEN_CHIPONE_ICN8318) += chipone_icn8318.o
obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o
obj-$(CONFIG_TOUCHSCREEN_CYTTSP_CORE) += cyttsp_core.o
obj-$(CONFIG_TOUCHSCREEN_CYTTSP_I2C) += cyttsp_i2c.o cyttsp_i2c_common.o
@@ -39,6 +40,7 @@ obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o
obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o
obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o
obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o
+obj-$(CONFIG_TOUCHSCREEN_IPROC) += bcm_iproc_tsc.o
obj-$(CONFIG_TOUCHSCREEN_LPC32XX) += lpc32xx_ts.o
obj-$(CONFIG_TOUCHSCREEN_MAX11801) += max11801_ts.o
obj-$(CONFIG_TOUCHSCREEN_MC13783) += mc13783_ts.o
@@ -79,5 +81,6 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_ATMEL) += atmel-wm97xx.o
obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o
obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o
obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o
+obj-$(CONFIG_TOUCHSCREEN_SX8654) += sx8654.o
obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o
obj-$(CONFIG_TOUCHSCREEN_ZFORCE) += zforce_ts.o
diff --git a/drivers/input/touchscreen/ar1021_i2c.c b/drivers/input/touchscreen/ar1021_i2c.c
index ba30578e296e..f0b954d46a25 100644
--- a/drivers/input/touchscreen/ar1021_i2c.c
+++ b/drivers/input/touchscreen/ar1021_i2c.c
@@ -157,7 +157,7 @@ static const struct i2c_device_id ar1021_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, ar1021_i2c_id);
-static struct of_device_id ar1021_i2c_of_match[] = {
+static const struct of_device_id ar1021_i2c_of_match[] = {
{ .compatible = "microchip,ar1021-i2c", },
{ }
};
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 95ee92a91bd2..40b98dda8f38 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -14,6 +14,8 @@
*
*/
+#include <linux/acpi.h>
+#include <linux/dmi.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/completion.h>
@@ -25,6 +27,7 @@
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/slab.h>
+#include <asm/unaligned.h>
/* Version */
#define MXT_VER_20 20
@@ -79,6 +82,7 @@
#define MXT_SPT_DIGITIZER_T43 43
#define MXT_SPT_MESSAGECOUNT_T44 44
#define MXT_SPT_CTECONFIG_T46 46
+#define MXT_TOUCH_MULTITOUCHSCREEN_T100 100
/* MXT_GEN_MESSAGE_T5 object */
#define MXT_RPTID_NOMSG 0xff
@@ -185,6 +189,36 @@ struct t9_range {
#define MXT_RESET_VALUE 0x01
#define MXT_BACKUP_VALUE 0x55
+/* T100 Multiple Touch Touchscreen */
+#define MXT_T100_CTRL 0
+#define MXT_T100_CFG1 1
+#define MXT_T100_TCHAUX 3
+#define MXT_T100_XRANGE 13
+#define MXT_T100_YRANGE 24
+
+#define MXT_T100_CFG_SWITCHXY BIT(5)
+
+#define MXT_T100_TCHAUX_VECT BIT(0)
+#define MXT_T100_TCHAUX_AMPL BIT(1)
+#define MXT_T100_TCHAUX_AREA BIT(2)
+
+#define MXT_T100_DETECT BIT(7)
+#define MXT_T100_TYPE_MASK 0x70
+
+enum t100_type {
+ MXT_T100_TYPE_FINGER = 1,
+ MXT_T100_TYPE_PASSIVE_STYLUS = 2,
+ MXT_T100_TYPE_HOVERING_FINGER = 4,
+ MXT_T100_TYPE_GLOVE = 5,
+ MXT_T100_TYPE_LARGE_TOUCH = 6,
+};
+
+#define MXT_DISTANCE_ACTIVE_TOUCH 0
+#define MXT_DISTANCE_HOVERING 1
+
+#define MXT_TOUCH_MAJOR_DEFAULT 1
+#define MXT_PRESSURE_DEFAULT 1
+
/* Delay times */
#define MXT_BACKUP_TIME 50 /* msec */
#define MXT_RESET_TIME 200 /* msec */
@@ -244,6 +278,9 @@ struct mxt_data {
unsigned int max_y;
bool in_bootloader;
u16 mem_size;
+ u8 t100_aux_ampl;
+ u8 t100_aux_area;
+ u8 t100_aux_vect;
u8 max_reportid;
u32 config_crc;
u32 info_crc;
@@ -253,6 +290,7 @@ struct mxt_data {
bool update_input;
u8 last_message_count;
u8 num_touchids;
+ u8 multitouch;
/* Cached parameters from object table */
u16 T5_address;
@@ -264,6 +302,8 @@ struct mxt_data {
u8 T9_reportid_max;
u8 T19_reportid;
u16 T44_address;
+ u8 T100_reportid_min;
+ u8 T100_reportid_max;
/* for fw update in bootloader */
struct completion bl_completion;
@@ -771,6 +811,114 @@ static void mxt_proc_t9_message(struct mxt_data *data, u8 *message)
data->update_input = true;
}
+static void mxt_proc_t100_message(struct mxt_data *data, u8 *message)
+{
+ struct device *dev = &data->client->dev;
+ struct input_dev *input_dev = data->input_dev;
+ int id;
+ u8 status;
+ u8 type = 0;
+ u16 x;
+ u16 y;
+ int distance = 0;
+ int tool = 0;
+ u8 major = 0;
+ u8 pressure = 0;
+ u8 orientation = 0;
+
+ id = message[0] - data->T100_reportid_min - 2;
+
+ /* ignore SCRSTATUS events */
+ if (id < 0)
+ return;
+
+ status = message[1];
+ x = get_unaligned_le16(&message[2]);
+ y = get_unaligned_le16(&message[4]);
+
+ if (status & MXT_T100_DETECT) {
+ type = (status & MXT_T100_TYPE_MASK) >> 4;
+
+ switch (type) {
+ case MXT_T100_TYPE_HOVERING_FINGER:
+ tool = MT_TOOL_FINGER;
+ distance = MXT_DISTANCE_HOVERING;
+
+ if (data->t100_aux_vect)
+ orientation = message[data->t100_aux_vect];
+
+ break;
+
+ case MXT_T100_TYPE_FINGER:
+ case MXT_T100_TYPE_GLOVE:
+ tool = MT_TOOL_FINGER;
+ distance = MXT_DISTANCE_ACTIVE_TOUCH;
+
+ if (data->t100_aux_area)
+ major = message[data->t100_aux_area];
+
+ if (data->t100_aux_ampl)
+ pressure = message[data->t100_aux_ampl];
+
+ if (data->t100_aux_vect)
+ orientation = message[data->t100_aux_vect];
+
+ break;
+
+ case MXT_T100_TYPE_PASSIVE_STYLUS:
+ tool = MT_TOOL_PEN;
+
+ /*
+ * Passive stylus is reported with size zero so
+ * hardcode.
+ */
+ major = MXT_TOUCH_MAJOR_DEFAULT;
+
+ if (data->t100_aux_ampl)
+ pressure = message[data->t100_aux_ampl];
+
+ break;
+
+ case MXT_T100_TYPE_LARGE_TOUCH:
+ /* Ignore suppressed touch */
+ break;
+
+ default:
+ dev_dbg(dev, "Unexpected T100 type\n");
+ return;
+ }
+ }
+
+ /*
+ * Values reported should be non-zero if tool is touching the
+ * device
+ */
+ if (!pressure && type != MXT_T100_TYPE_HOVERING_FINGER)
+ pressure = MXT_PRESSURE_DEFAULT;
+
+ input_mt_slot(input_dev, id);
+
+ if (status & MXT_T100_DETECT) {
+ dev_dbg(dev, "[%u] type:%u x:%u y:%u a:%02X p:%02X v:%02X\n",
+ id, type, x, y, major, pressure, orientation);
+
+ input_mt_report_slot_state(input_dev, tool, 1);
+ input_report_abs(input_dev, ABS_MT_POSITION_X, x);
+ input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
+ input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, major);
+ input_report_abs(input_dev, ABS_MT_PRESSURE, pressure);
+ input_report_abs(input_dev, ABS_MT_DISTANCE, distance);
+ input_report_abs(input_dev, ABS_MT_ORIENTATION, orientation);
+ } else {
+ dev_dbg(dev, "[%u] release\n", id);
+
+ /* close out slot */
+ input_mt_report_slot_state(input_dev, 0, 0);
+ }
+
+ data->update_input = true;
+}
+
static int mxt_proc_message(struct mxt_data *data, u8 *message)
{
u8 report_id = message[0];
@@ -786,9 +934,12 @@ static int mxt_proc_message(struct mxt_data *data, u8 *message)
* is not yet registered.
*/
mxt_dump_message(data, message);
- } else if (report_id >= data->T9_reportid_min
- && report_id <= data->T9_reportid_max) {
+ } else if (report_id >= data->T9_reportid_min &&
+ report_id <= data->T9_reportid_max) {
mxt_proc_t9_message(data, message);
+ } else if (report_id >= data->T100_reportid_min &&
+ report_id <= data->T100_reportid_max) {
+ mxt_proc_t100_message(data, message);
} else if (report_id == data->T19_reportid) {
mxt_input_button(data, message);
data->update_input = true;
@@ -1411,6 +1562,8 @@ static void mxt_free_object_table(struct mxt_data *data)
data->T9_reportid_max = 0;
data->T19_reportid = 0;
data->T44_address = 0;
+ data->T100_reportid_min = 0;
+ data->T100_reportid_max = 0;
data->max_reportid = 0;
}
@@ -1487,6 +1640,7 @@ static int mxt_get_object_table(struct mxt_data *data)
data->T7_address = object->start_address;
break;
case MXT_TOUCH_MULTI_T9:
+ data->multitouch = MXT_TOUCH_MULTI_T9;
data->T9_reportid_min = min_id;
data->T9_reportid_max = max_id;
data->num_touchids = object->num_report_ids
@@ -1498,6 +1652,13 @@ static int mxt_get_object_table(struct mxt_data *data)
case MXT_SPT_GPIOPWM_T19:
data->T19_reportid = min_id;
break;
+ case MXT_TOUCH_MULTITOUCHSCREEN_T100:
+ data->multitouch = MXT_TOUCH_MULTITOUCHSCREEN_T100;
+ data->T100_reportid_min = min_id;
+ data->T100_reportid_max = max_id;
+ /* first two report IDs reserved */
+ data->num_touchids = object->num_report_ids - 2;
+ break;
}
end_address = object->start_address
@@ -1582,22 +1743,138 @@ static int mxt_read_t9_resolution(struct mxt_data *data)
return 0;
}
+static int mxt_read_t100_config(struct mxt_data *data)
+{
+ struct i2c_client *client = data->client;
+ int error;
+ struct mxt_object *object;
+ u16 range_x, range_y;
+ u8 cfg, tchaux;
+ u8 aux;
+
+ object = mxt_get_object(data, MXT_TOUCH_MULTITOUCHSCREEN_T100);
+ if (!object)
+ return -EINVAL;
+
+ error = __mxt_read_reg(client,
+ object->start_address + MXT_T100_XRANGE,
+ sizeof(range_x), &range_x);
+ if (error)
+ return error;
+
+ le16_to_cpus(&range_x);
+
+ error = __mxt_read_reg(client,
+ object->start_address + MXT_T100_YRANGE,
+ sizeof(range_y), &range_y);
+ if (error)
+ return error;
+
+ le16_to_cpus(&range_y);
+
+ error = __mxt_read_reg(client,
+ object->start_address + MXT_T100_CFG1,
+ 1, &cfg);
+ if (error)
+ return error;
+
+ error = __mxt_read_reg(client,
+ object->start_address + MXT_T100_TCHAUX,
+ 1, &tchaux);
+ if (error)
+ return error;
+
+ /* Handle default values */
+ if (range_x == 0)
+ range_x = 1023;
+
+ if (range_y == 0)
+ range_y = 1023;
+
+ if (cfg & MXT_T100_CFG_SWITCHXY) {
+ data->max_x = range_y;
+ data->max_y = range_x;
+ } else {
+ data->max_x = range_x;
+ data->max_y = range_y;
+ }
+
+ /* allocate aux bytes */
+ aux = 6;
+
+ if (tchaux & MXT_T100_TCHAUX_VECT)
+ data->t100_aux_vect = aux++;
+
+ if (tchaux & MXT_T100_TCHAUX_AMPL)
+ data->t100_aux_ampl = aux++;
+
+ if (tchaux & MXT_T100_TCHAUX_AREA)
+ data->t100_aux_area = aux++;
+
+ dev_dbg(&client->dev,
+ "T100 aux mappings vect:%u ampl:%u area:%u\n",
+ data->t100_aux_vect, data->t100_aux_ampl, data->t100_aux_area);
+
+ dev_info(&client->dev,
+ "T100 Touchscreen size X%uY%u\n", data->max_x, data->max_y);
+
+ return 0;
+}
+
static int mxt_input_open(struct input_dev *dev);
static void mxt_input_close(struct input_dev *dev);
-static int mxt_initialize_t9_input_device(struct mxt_data *data)
+static void mxt_set_up_as_touchpad(struct input_dev *input_dev,
+ struct mxt_data *data)
{
- struct device *dev = &data->client->dev;
const struct mxt_platform_data *pdata = data->pdata;
+ int i;
+
+ input_dev->name = "Atmel maXTouch Touchpad";
+
+ __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit);
+
+ input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM);
+ input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM);
+ input_abs_set_res(input_dev, ABS_MT_POSITION_X,
+ MXT_PIXELS_PER_MM);
+ input_abs_set_res(input_dev, ABS_MT_POSITION_Y,
+ MXT_PIXELS_PER_MM);
+
+ for (i = 0; i < pdata->t19_num_keys; i++)
+ if (pdata->t19_keymap[i] != KEY_RESERVED)
+ input_set_capability(input_dev, EV_KEY,
+ pdata->t19_keymap[i]);
+}
+
+static int mxt_initialize_input_device(struct mxt_data *data)
+{
+ const struct mxt_platform_data *pdata = data->pdata;
+ struct device *dev = &data->client->dev;
struct input_dev *input_dev;
int error;
unsigned int num_mt_slots;
unsigned int mt_flags = 0;
- int i;
- error = mxt_read_t9_resolution(data);
- if (error)
- dev_warn(dev, "Failed to initialize T9 resolution\n");
+ switch (data->multitouch) {
+ case MXT_TOUCH_MULTI_T9:
+ num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1;
+ error = mxt_read_t9_resolution(data);
+ if (error)
+ dev_warn(dev, "Failed to initialize T9 resolution\n");
+ break;
+
+ case MXT_TOUCH_MULTITOUCHSCREEN_T100:
+ num_mt_slots = data->num_touchids;
+ error = mxt_read_t100_config(data);
+ if (error)
+ dev_warn(dev, "Failed to read T100 config\n");
+ break;
+
+ default:
+ dev_err(dev, "Invalid multitouch object\n");
+ return -EINVAL;
+ }
input_dev = input_allocate_device();
if (!input_dev) {
@@ -1612,54 +1889,76 @@ static int mxt_initialize_t9_input_device(struct mxt_data *data)
input_dev->open = mxt_input_open;
input_dev->close = mxt_input_close;
- __set_bit(EV_ABS, input_dev->evbit);
- __set_bit(EV_KEY, input_dev->evbit);
- __set_bit(BTN_TOUCH, input_dev->keybit);
+ input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
- if (pdata->t19_num_keys) {
- __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit);
+ /* For single touch */
+ input_set_abs_params(input_dev, ABS_X, 0, data->max_x, 0, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, data->max_y, 0, 0);
- for (i = 0; i < pdata->t19_num_keys; i++)
- if (pdata->t19_keymap[i] != KEY_RESERVED)
- input_set_capability(input_dev, EV_KEY,
- pdata->t19_keymap[i]);
+ if (data->multitouch == MXT_TOUCH_MULTI_T9 ||
+ (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
+ data->t100_aux_ampl)) {
+ input_set_abs_params(input_dev, ABS_PRESSURE, 0, 255, 0, 0);
+ }
+ /* If device has buttons we assume it is a touchpad */
+ if (pdata->t19_num_keys) {
+ mxt_set_up_as_touchpad(input_dev, data);
mt_flags |= INPUT_MT_POINTER;
-
- input_abs_set_res(input_dev, ABS_X, MXT_PIXELS_PER_MM);
- input_abs_set_res(input_dev, ABS_Y, MXT_PIXELS_PER_MM);
- input_abs_set_res(input_dev, ABS_MT_POSITION_X,
- MXT_PIXELS_PER_MM);
- input_abs_set_res(input_dev, ABS_MT_POSITION_Y,
- MXT_PIXELS_PER_MM);
-
- input_dev->name = "Atmel maXTouch Touchpad";
}
- /* For single touch */
- input_set_abs_params(input_dev, ABS_X,
- 0, data->max_x, 0, 0);
- input_set_abs_params(input_dev, ABS_Y,
- 0, data->max_y, 0, 0);
- input_set_abs_params(input_dev, ABS_PRESSURE,
- 0, 255, 0, 0);
-
/* For multi touch */
- num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1;
error = input_mt_init_slots(input_dev, num_mt_slots, mt_flags);
if (error) {
dev_err(dev, "Error %d initialising slots\n", error);
goto err_free_mem;
}
- input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
- 0, MXT_MAX_AREA, 0, 0);
+ if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100) {
+ input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE,
+ 0, MT_TOOL_MAX, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_DISTANCE,
+ MXT_DISTANCE_ACTIVE_TOUCH,
+ MXT_DISTANCE_HOVERING,
+ 0, 0);
+ }
+
input_set_abs_params(input_dev, ABS_MT_POSITION_X,
0, data->max_x, 0, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
0, data->max_y, 0, 0);
- input_set_abs_params(input_dev, ABS_MT_PRESSURE,
- 0, 255, 0, 0);
+
+ if (data->multitouch == MXT_TOUCH_MULTI_T9 ||
+ (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
+ data->t100_aux_area)) {
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
+ 0, MXT_MAX_AREA, 0, 0);
+ }
+
+ if (data->multitouch == MXT_TOUCH_MULTI_T9 ||
+ (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
+ data->t100_aux_ampl)) {
+ input_set_abs_params(input_dev, ABS_MT_PRESSURE,
+ 0, 255, 0, 0);
+ }
+
+ if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
+ data->t100_aux_vect) {
+ input_set_abs_params(input_dev, ABS_MT_ORIENTATION,
+ 0, 255, 0, 0);
+ }
+
+ if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
+ data->t100_aux_ampl) {
+ input_set_abs_params(input_dev, ABS_MT_PRESSURE,
+ 0, 255, 0, 0);
+ }
+
+ if (data->multitouch == MXT_TOUCH_MULTITOUCHSCREEN_T100 &&
+ data->t100_aux_vect) {
+ input_set_abs_params(input_dev, ABS_MT_ORIENTATION,
+ 0, 255, 0, 0);
+ }
input_set_drvdata(input_dev, data);
@@ -1765,9 +2064,13 @@ static int mxt_configure_objects(struct mxt_data *data,
dev_warn(dev, "Error %d updating config\n", error);
}
- error = mxt_initialize_t9_input_device(data);
- if (error)
- return error;
+ if (data->multitouch) {
+ error = mxt_initialize_input_device(data);
+ if (error)
+ return error;
+ } else {
+ dev_warn(dev, "No touch object detected\n");
+ }
dev_info(dev,
"Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u\n",
@@ -2044,15 +2347,13 @@ static const struct attribute_group mxt_attr_group = {
static void mxt_start(struct mxt_data *data)
{
/* Touch enable */
- mxt_write_object(data,
- MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0x83);
+ mxt_write_object(data, data->multitouch, MXT_TOUCH_CTRL, 0x83);
}
static void mxt_stop(struct mxt_data *data)
{
/* Touch disable */
- mxt_write_object(data,
- MXT_TOUCH_MULTI_T9, MXT_TOUCH_CTRL, 0);
+ mxt_write_object(data, data->multitouch, MXT_TOUCH_CTRL, 0);
}
static int mxt_input_open(struct input_dev *dev)
@@ -2072,7 +2373,7 @@ static void mxt_input_close(struct input_dev *dev)
}
#ifdef CONFIG_OF
-static struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client)
+static const struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client)
{
struct mxt_platform_data *pdata;
u32 *keymap;
@@ -2080,7 +2381,7 @@ static struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client)
int proplen, i, ret;
if (!client->dev.of_node)
- return ERR_PTR(-ENODEV);
+ return ERR_PTR(-ENOENT);
pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
@@ -2111,25 +2412,132 @@ static struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client)
return pdata;
}
#else
-static struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client)
+static const struct mxt_platform_data *mxt_parse_dt(struct i2c_client *client)
{
- dev_dbg(&client->dev, "No platform data specified\n");
- return ERR_PTR(-EINVAL);
+ return ERR_PTR(-ENOENT);
+}
+#endif
+
+#ifdef CONFIG_ACPI
+
+struct mxt_acpi_platform_data {
+ const char *hid;
+ struct mxt_platform_data pdata;
+};
+
+static unsigned int samus_touchpad_buttons[] = {
+ KEY_RESERVED,
+ KEY_RESERVED,
+ KEY_RESERVED,
+ BTN_LEFT
+};
+
+static struct mxt_acpi_platform_data samus_platform_data[] = {
+ {
+ /* Touchpad */
+ .hid = "ATML0000",
+ .pdata = {
+ .t19_num_keys = ARRAY_SIZE(samus_touchpad_buttons),
+ .t19_keymap = samus_touchpad_buttons,
+ },
+ },
+ {
+ /* Touchscreen */
+ .hid = "ATML0001",
+ },
+ { }
+};
+
+static const struct dmi_system_id mxt_dmi_table[] = {
+ {
+ /* 2015 Google Pixel */
+ .ident = "Chromebook Pixel 2",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "GOOGLE"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Samus"),
+ },
+ .driver_data = samus_platform_data,
+ },
+ { }
+};
+
+static const struct mxt_platform_data *mxt_parse_acpi(struct i2c_client *client)
+{
+ struct acpi_device *adev;
+ const struct dmi_system_id *system_id;
+ const struct mxt_acpi_platform_data *acpi_pdata;
+
+ /*
+ * Ignore ACPI devices representing bootloader mode.
+ *
+ * This is a bit of a hack: Google Chromebook BIOS creates ACPI
+ * devices for both application and bootloader modes, but we are
+ * interested in application mode only (if device is in bootloader
+ * mode we'll end up switching into application anyway). So far
+ * application mode addresses were all above 0x40, so we'll use it
+ * as a threshold.
+ */
+ if (client->addr < 0x40)
+ return ERR_PTR(-ENXIO);
+
+ adev = ACPI_COMPANION(&client->dev);
+ if (!adev)
+ return ERR_PTR(-ENOENT);
+
+ system_id = dmi_first_match(mxt_dmi_table);
+ if (!system_id)
+ return ERR_PTR(-ENOENT);
+
+ acpi_pdata = system_id->driver_data;
+ if (!acpi_pdata)
+ return ERR_PTR(-ENOENT);
+
+ while (acpi_pdata->hid) {
+ if (!strcmp(acpi_device_hid(adev), acpi_pdata->hid))
+ return &acpi_pdata->pdata;
+
+ acpi_pdata++;
+ }
+
+ return ERR_PTR(-ENOENT);
+}
+#else
+static const struct mxt_platform_data *mxt_parse_acpi(struct i2c_client *client)
+{
+ return ERR_PTR(-ENOENT);
}
#endif
+static const struct mxt_platform_data *
+mxt_get_platform_data(struct i2c_client *client)
+{
+ const struct mxt_platform_data *pdata;
+
+ pdata = dev_get_platdata(&client->dev);
+ if (pdata)
+ return pdata;
+
+ pdata = mxt_parse_dt(client);
+ if (!IS_ERR(pdata) || PTR_ERR(pdata) != -ENOENT)
+ return pdata;
+
+ pdata = mxt_parse_acpi(client);
+ if (!IS_ERR(pdata) || PTR_ERR(pdata) != -ENOENT)
+ return pdata;
+
+ dev_err(&client->dev, "No platform data specified\n");
+ return ERR_PTR(-EINVAL);
+}
+
static int mxt_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
struct mxt_data *data;
const struct mxt_platform_data *pdata;
int error;
- pdata = dev_get_platdata(&client->dev);
- if (!pdata) {
- pdata = mxt_parse_dt(client);
- if (IS_ERR(pdata))
- return PTR_ERR(pdata);
- }
+ pdata = mxt_get_platform_data(client);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
data = kzalloc(sizeof(struct mxt_data), GFP_KERNEL);
if (!data) {
@@ -2237,6 +2645,15 @@ static const struct of_device_id mxt_of_match[] = {
};
MODULE_DEVICE_TABLE(of, mxt_of_match);
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id mxt_acpi_id[] = {
+ { "ATML0000", 0 }, /* Touchpad */
+ { "ATML0001", 0 }, /* Touchscreen */
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, mxt_acpi_id);
+#endif
+
static const struct i2c_device_id mxt_id[] = {
{ "qt602240_ts", 0 },
{ "atmel_mxt_ts", 0 },
@@ -2251,6 +2668,7 @@ static struct i2c_driver mxt_driver = {
.name = "atmel_mxt_ts",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(mxt_of_match),
+ .acpi_match_table = ACPI_PTR(mxt_acpi_id),
.pm = &mxt_pm_ops,
},
.probe = mxt_probe,
diff --git a/drivers/input/touchscreen/bcm_iproc_tsc.c b/drivers/input/touchscreen/bcm_iproc_tsc.c
new file mode 100644
index 000000000000..ae460a5c93d5
--- /dev/null
+++ b/drivers/input/touchscreen/bcm_iproc_tsc.c
@@ -0,0 +1,522 @@
+/*
+* Copyright (C) 2015 Broadcom Corporation
+*
+* 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 version 2.
+*
+* This program is distributed "as is" WITHOUT ANY WARRANTY of any
+* kind, whether express or implied; without even the implied warranty
+* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*/
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/keyboard.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <asm/irq.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/serio.h>
+
+#define IPROC_TS_NAME "iproc-ts"
+
+#define PEN_DOWN_STATUS 1
+#define PEN_UP_STATUS 0
+
+#define X_MIN 0
+#define Y_MIN 0
+#define X_MAX 0xFFF
+#define Y_MAX 0xFFF
+
+/* Value given by controller for invalid coordinate. */
+#define INVALID_COORD 0xFFFFFFFF
+
+/* Register offsets */
+#define REGCTL1 0x00
+#define REGCTL2 0x04
+#define INTERRUPT_THRES 0x08
+#define INTERRUPT_MASK 0x0c
+
+#define INTERRUPT_STATUS 0x10
+#define CONTROLLER_STATUS 0x14
+#define FIFO_DATA 0x18
+#define FIFO_DATA_X_Y_MASK 0xFFFF
+#define ANALOG_CONTROL 0x1c
+
+#define AUX_DATA 0x20
+#define DEBOUNCE_CNTR_STAT 0x24
+#define SCAN_CNTR_STAT 0x28
+#define REM_CNTR_STAT 0x2c
+
+#define SETTLING_TIMER_STAT 0x30
+#define SPARE_REG 0x34
+#define SOFT_BYPASS_CONTROL 0x38
+#define SOFT_BYPASS_DATA 0x3c
+
+
+/* Bit values for INTERRUPT_MASK and INTERRUPT_STATUS regs */
+#define TS_PEN_INTR_MASK BIT(0)
+#define TS_FIFO_INTR_MASK BIT(2)
+
+/* Bit values for CONTROLLER_STATUS reg1 */
+#define TS_PEN_DOWN BIT(0)
+
+/* Shift values for control reg1 */
+#define SCANNING_PERIOD_SHIFT 24
+#define DEBOUNCE_TIMEOUT_SHIFT 16
+#define SETTLING_TIMEOUT_SHIFT 8
+#define TOUCH_TIMEOUT_SHIFT 0
+
+/* Shift values for coordinates from fifo */
+#define X_COORD_SHIFT 0
+#define Y_COORD_SHIFT 16
+
+/* Bit values for REGCTL2 */
+#define TS_CONTROLLER_EN_BIT BIT(16)
+#define TS_CONTROLLER_AVGDATA_SHIFT 8
+#define TS_CONTROLLER_AVGDATA_MASK (0x7 << TS_CONTROLLER_AVGDATA_SHIFT)
+#define TS_CONTROLLER_PWR_LDO BIT(5)
+#define TS_CONTROLLER_PWR_ADC BIT(4)
+#define TS_CONTROLLER_PWR_BGP BIT(3)
+#define TS_CONTROLLER_PWR_TS BIT(2)
+#define TS_WIRE_MODE_BIT BIT(1)
+
+#define dbg_reg(dev, priv, reg) \
+ dev_dbg(dev, "%20s= 0x%08x\n", #reg, readl((priv)->regs + reg))
+
+struct tsc_param {
+ /* Each step is 1024 us. Valid 1-256 */
+ u32 scanning_period;
+
+ /* Each step is 512 us. Valid 0-255 */
+ u32 debounce_timeout;
+
+ /*
+ * The settling duration (in ms) is the amount of time the tsc
+ * waits to allow the voltage to settle after turning on the
+ * drivers in detection mode. Valid values: 0-11
+ * 0 = 0.008 ms
+ * 1 = 0.01 ms
+ * 2 = 0.02 ms
+ * 3 = 0.04 ms
+ * 4 = 0.08 ms
+ * 5 = 0.16 ms
+ * 6 = 0.32 ms
+ * 7 = 0.64 ms
+ * 8 = 1.28 ms
+ * 9 = 2.56 ms
+ * 10 = 5.12 ms
+ * 11 = 10.24 ms
+ */
+ u32 settling_timeout;
+
+ /* touch timeout in sample counts */
+ u32 touch_timeout;
+
+ /*
+ * Number of data samples which are averaged before a final data point
+ * is placed into the FIFO
+ */
+ u32 average_data;
+
+ /* FIFO threshold */
+ u32 fifo_threshold;
+
+ /* Optional standard touchscreen properties. */
+ u32 max_x;
+ u32 max_y;
+ u32 fuzz_x;
+ u32 fuzz_y;
+ bool invert_x;
+ bool invert_y;
+};
+
+struct iproc_ts_priv {
+ struct platform_device *pdev;
+ struct input_dev *idev;
+
+ void __iomem *regs;
+ struct clk *tsc_clk;
+
+ int pen_status;
+ struct tsc_param cfg_params;
+};
+
+/*
+ * Set default values the same as hardware reset values
+ * except for fifo_threshold with is set to 1.
+ */
+static const struct tsc_param iproc_default_config = {
+ .scanning_period = 0x5, /* 1 to 256 */
+ .debounce_timeout = 0x28, /* 0 to 255 */
+ .settling_timeout = 0x7, /* 0 to 11 */
+ .touch_timeout = 0xa, /* 0 to 255 */
+ .average_data = 5, /* entry 5 = 32 pts */
+ .fifo_threshold = 1, /* 0 to 31 */
+ .max_x = X_MAX,
+ .max_y = Y_MAX,
+};
+
+static void ts_reg_dump(struct iproc_ts_priv *priv)
+{
+ struct device *dev = &priv->pdev->dev;
+
+ dbg_reg(dev, priv, REGCTL1);
+ dbg_reg(dev, priv, REGCTL2);
+ dbg_reg(dev, priv, INTERRUPT_THRES);
+ dbg_reg(dev, priv, INTERRUPT_MASK);
+ dbg_reg(dev, priv, INTERRUPT_STATUS);
+ dbg_reg(dev, priv, CONTROLLER_STATUS);
+ dbg_reg(dev, priv, FIFO_DATA);
+ dbg_reg(dev, priv, ANALOG_CONTROL);
+ dbg_reg(dev, priv, AUX_DATA);
+ dbg_reg(dev, priv, DEBOUNCE_CNTR_STAT);
+ dbg_reg(dev, priv, SCAN_CNTR_STAT);
+ dbg_reg(dev, priv, REM_CNTR_STAT);
+ dbg_reg(dev, priv, SETTLING_TIMER_STAT);
+ dbg_reg(dev, priv, SPARE_REG);
+ dbg_reg(dev, priv, SOFT_BYPASS_CONTROL);
+ dbg_reg(dev, priv, SOFT_BYPASS_DATA);
+}
+
+static irqreturn_t iproc_touchscreen_interrupt(int irq, void *data)
+{
+ struct platform_device *pdev = data;
+ struct iproc_ts_priv *priv = platform_get_drvdata(pdev);
+ u32 intr_status;
+ u32 raw_coordinate;
+ u16 x;
+ u16 y;
+ int i;
+ bool needs_sync = false;
+
+ intr_status = readl(priv->regs + INTERRUPT_STATUS);
+ intr_status &= TS_PEN_INTR_MASK | TS_FIFO_INTR_MASK;
+ if (intr_status == 0)
+ return IRQ_NONE;
+
+ /* Clear all interrupt status bits, write-1-clear */
+ writel(intr_status, priv->regs + INTERRUPT_STATUS);
+
+ /* Pen up/down */
+ if (intr_status & TS_PEN_INTR_MASK) {
+ if (readl(priv->regs + CONTROLLER_STATUS) & TS_PEN_DOWN)
+ priv->pen_status = PEN_DOWN_STATUS;
+ else
+ priv->pen_status = PEN_UP_STATUS;
+
+ input_report_key(priv->idev, BTN_TOUCH, priv->pen_status);
+ needs_sync = true;
+
+ dev_dbg(&priv->pdev->dev,
+ "pen up-down (%d)\n", priv->pen_status);
+ }
+
+ /* coordinates in FIFO exceed the theshold */
+ if (intr_status & TS_FIFO_INTR_MASK) {
+ for (i = 0; i < priv->cfg_params.fifo_threshold; i++) {
+ raw_coordinate = readl(priv->regs + FIFO_DATA);
+ if (raw_coordinate == INVALID_COORD)
+ continue;
+
+ /*
+ * The x and y coordinate are 16 bits each
+ * with the x in the lower 16 bits and y in the
+ * upper 16 bits.
+ */
+ x = (raw_coordinate >> X_COORD_SHIFT) &
+ FIFO_DATA_X_Y_MASK;
+ y = (raw_coordinate >> Y_COORD_SHIFT) &
+ FIFO_DATA_X_Y_MASK;
+
+ /* We only want to retain the 12 msb of the 16 */
+ x = (x >> 4) & 0x0FFF;
+ y = (y >> 4) & 0x0FFF;
+
+ /* adjust x y according to lcd tsc mount angle */
+ if (priv->cfg_params.invert_x)
+ x = priv->cfg_params.max_x - x;
+
+ if (priv->cfg_params.invert_y)
+ y = priv->cfg_params.max_y - y;
+
+ input_report_abs(priv->idev, ABS_X, x);
+ input_report_abs(priv->idev, ABS_Y, y);
+ needs_sync = true;
+
+ dev_dbg(&priv->pdev->dev, "xy (0x%x 0x%x)\n", x, y);
+ }
+ }
+
+ if (needs_sync)
+ input_sync(priv->idev);
+
+ return IRQ_HANDLED;
+}
+
+static int iproc_ts_start(struct input_dev *idev)
+{
+ struct iproc_ts_priv *priv = input_get_drvdata(idev);
+ u32 val;
+ int error;
+
+ /* Enable clock */
+ error = clk_prepare_enable(priv->tsc_clk);
+ if (error) {
+ dev_err(&priv->pdev->dev, "%s clk_prepare_enable failed %d\n",
+ __func__, error);
+ return error;
+ }
+
+ /*
+ * Interrupt is generated when:
+ * FIFO reaches the int_th value, and pen event(up/down)
+ */
+ val = TS_PEN_INTR_MASK | TS_FIFO_INTR_MASK;
+ writel(val, priv->regs + INTERRUPT_MASK);
+
+ writel(priv->cfg_params.fifo_threshold, priv->regs + INTERRUPT_THRES);
+
+ /* Initialize control reg1 */
+ val = 0;
+ val |= priv->cfg_params.scanning_period << SCANNING_PERIOD_SHIFT;
+ val |= priv->cfg_params.debounce_timeout << DEBOUNCE_TIMEOUT_SHIFT;
+ val |= priv->cfg_params.settling_timeout << SETTLING_TIMEOUT_SHIFT;
+ val |= priv->cfg_params.touch_timeout << TOUCH_TIMEOUT_SHIFT;
+ writel(val, priv->regs + REGCTL1);
+
+ /* Try to clear all interrupt status */
+ val = readl(priv->regs + INTERRUPT_STATUS);
+ val |= TS_FIFO_INTR_MASK | TS_PEN_INTR_MASK;
+ writel(val, priv->regs + INTERRUPT_STATUS);
+
+ /* Initialize control reg2 */
+ val = readl(priv->regs + REGCTL2);
+ val |= TS_CONTROLLER_EN_BIT | TS_WIRE_MODE_BIT;
+
+ val &= ~TS_CONTROLLER_AVGDATA_MASK;
+ val |= priv->cfg_params.average_data << TS_CONTROLLER_AVGDATA_SHIFT;
+
+ val &= ~(TS_CONTROLLER_PWR_LDO | /* PWR up LDO */
+ TS_CONTROLLER_PWR_ADC | /* PWR up ADC */
+ TS_CONTROLLER_PWR_BGP | /* PWR up BGP */
+ TS_CONTROLLER_PWR_TS); /* PWR up TS */
+
+ writel(val, priv->regs + REGCTL2);
+
+ ts_reg_dump(priv);
+
+ return 0;
+}
+
+static void iproc_ts_stop(struct input_dev *dev)
+{
+ u32 val;
+ struct iproc_ts_priv *priv = input_get_drvdata(dev);
+
+ writel(0, priv->regs + INTERRUPT_MASK); /* Disable all interrupts */
+
+ /* Only power down touch screen controller */
+ val = readl(priv->regs + REGCTL2);
+ val |= TS_CONTROLLER_PWR_TS;
+ writel(val, priv->regs + REGCTL2);
+
+ clk_disable(priv->tsc_clk);
+}
+
+static int iproc_get_tsc_config(struct device *dev, struct iproc_ts_priv *priv)
+{
+ struct device_node *np = dev->of_node;
+ u32 val;
+
+ priv->cfg_params = iproc_default_config;
+
+ if (!np)
+ return 0;
+
+ if (of_property_read_u32(np, "scanning_period", &val) >= 0) {
+ if (val < 1 || val > 256) {
+ dev_err(dev, "scanning_period (%u) must be [1-256]\n",
+ val);
+ return -EINVAL;
+ }
+ priv->cfg_params.scanning_period = val;
+ }
+
+ if (of_property_read_u32(np, "debounce_timeout", &val) >= 0) {
+ if (val > 255) {
+ dev_err(dev, "debounce_timeout (%u) must be [0-255]\n",
+ val);
+ return -EINVAL;
+ }
+ priv->cfg_params.debounce_timeout = val;
+ }
+
+ if (of_property_read_u32(np, "settling_timeout", &val) >= 0) {
+ if (val > 11) {
+ dev_err(dev, "settling_timeout (%u) must be [0-11]\n",
+ val);
+ return -EINVAL;
+ }
+ priv->cfg_params.settling_timeout = val;
+ }
+
+ if (of_property_read_u32(np, "touch_timeout", &val) >= 0) {
+ if (val > 255) {
+ dev_err(dev, "touch_timeout (%u) must be [0-255]\n",
+ val);
+ return -EINVAL;
+ }
+ priv->cfg_params.touch_timeout = val;
+ }
+
+ if (of_property_read_u32(np, "average_data", &val) >= 0) {
+ if (val > 8) {
+ dev_err(dev, "average_data (%u) must be [0-8]\n", val);
+ return -EINVAL;
+ }
+ priv->cfg_params.average_data = val;
+ }
+
+ if (of_property_read_u32(np, "fifo_threshold", &val) >= 0) {
+ if (val > 31) {
+ dev_err(dev, "fifo_threshold (%u)) must be [0-31]\n",
+ val);
+ return -EINVAL;
+ }
+ priv->cfg_params.fifo_threshold = val;
+ }
+
+ /* Parse optional properties. */
+ of_property_read_u32(np, "touchscreen-size-x", &priv->cfg_params.max_x);
+ of_property_read_u32(np, "touchscreen-size-y", &priv->cfg_params.max_y);
+
+ of_property_read_u32(np, "touchscreen-fuzz-x",
+ &priv->cfg_params.fuzz_x);
+ of_property_read_u32(np, "touchscreen-fuzz-y",
+ &priv->cfg_params.fuzz_y);
+
+ priv->cfg_params.invert_x =
+ of_property_read_bool(np, "touchscreen-inverted-x");
+ priv->cfg_params.invert_y =
+ of_property_read_bool(np, "touchscreen-inverted-y");
+
+ return 0;
+}
+
+static int iproc_ts_probe(struct platform_device *pdev)
+{
+ struct iproc_ts_priv *priv;
+ struct input_dev *idev;
+ struct resource *res;
+ int irq;
+ int error;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ /* touchscreen controller memory mapped regs */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ priv->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->regs)) {
+ error = PTR_ERR(priv->regs);
+ dev_err(&pdev->dev, "unable to map I/O memory: %d\n", error);
+ return error;
+ }
+
+ priv->tsc_clk = devm_clk_get(&pdev->dev, "tsc_clk");
+ if (IS_ERR(priv->tsc_clk)) {
+ error = PTR_ERR(priv->tsc_clk);
+ dev_err(&pdev->dev,
+ "failed getting clock tsc_clk: %d\n", error);
+ return error;
+ }
+
+ priv->pdev = pdev;
+ error = iproc_get_tsc_config(&pdev->dev, priv);
+ if (error) {
+ dev_err(&pdev->dev, "get_tsc_config failed: %d\n", error);
+ return error;
+ }
+
+ idev = devm_input_allocate_device(&pdev->dev);
+ if (!idev) {
+ dev_err(&pdev->dev, "failed to allocate input device\n");
+ return -ENOMEM;
+ }
+
+ priv->idev = idev;
+ priv->pen_status = PEN_UP_STATUS;
+
+ /* Set input device info */
+ idev->name = IPROC_TS_NAME;
+ idev->dev.parent = &pdev->dev;
+
+ idev->id.bustype = BUS_HOST;
+ idev->id.vendor = SERIO_UNKNOWN;
+ idev->id.product = 0;
+ idev->id.version = 0;
+
+ idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ __set_bit(BTN_TOUCH, idev->keybit);
+
+ input_set_abs_params(idev, ABS_X, X_MIN, priv->cfg_params.max_x,
+ priv->cfg_params.fuzz_x, 0);
+ input_set_abs_params(idev, ABS_Y, Y_MIN, priv->cfg_params.max_y,
+ priv->cfg_params.fuzz_y, 0);
+
+ idev->open = iproc_ts_start;
+ idev->close = iproc_ts_stop;
+
+ input_set_drvdata(idev, priv);
+ platform_set_drvdata(pdev, priv);
+
+ /* get interrupt */
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "platform_get_irq failed: %d\n", irq);
+ return irq;
+ }
+
+ error = devm_request_irq(&pdev->dev, irq,
+ iproc_touchscreen_interrupt,
+ IRQF_SHARED, IPROC_TS_NAME, pdev);
+ if (error)
+ return error;
+
+ error = input_register_device(priv->idev);
+ if (error) {
+ dev_err(&pdev->dev,
+ "failed to register input device: %d\n", error);
+ return error;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id iproc_ts_of_match[] = {
+ {.compatible = "brcm,iproc-touchscreen", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, iproc_ts_of_match);
+
+static struct platform_driver iproc_ts_driver = {
+ .probe = iproc_ts_probe,
+ .driver = {
+ .name = IPROC_TS_NAME,
+ .of_match_table = of_match_ptr(iproc_ts_of_match),
+ },
+};
+
+module_platform_driver(iproc_ts_driver);
+
+MODULE_DESCRIPTION("IPROC Touchscreen driver");
+MODULE_AUTHOR("Broadcom");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/touchscreen/chipone_icn8318.c b/drivers/input/touchscreen/chipone_icn8318.c
new file mode 100644
index 000000000000..32e9db0e04bf
--- /dev/null
+++ b/drivers/input/touchscreen/chipone_icn8318.c
@@ -0,0 +1,316 @@
+/*
+ * Driver for ChipOne icn8318 i2c touchscreen controller
+ *
+ * Copyright (c) 2015 Red Hat Inc.
+ *
+ * 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.
+ *
+ * Red Hat authors:
+ * Hans de Goede <hdegoede@redhat.com>
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+
+#define ICN8318_REG_POWER 4
+#define ICN8318_REG_TOUCHDATA 16
+
+#define ICN8318_POWER_ACTIVE 0
+#define ICN8318_POWER_MONITOR 1
+#define ICN8318_POWER_HIBERNATE 2
+
+#define ICN8318_MAX_TOUCHES 5
+
+struct icn8318_touch {
+ __u8 slot;
+ __be16 x;
+ __be16 y;
+ __u8 pressure; /* Seems more like finger width then pressure really */
+ __u8 event;
+/* The difference between 2 and 3 is unclear */
+#define ICN8318_EVENT_NO_DATA 1 /* No finger seen yet since wakeup */
+#define ICN8318_EVENT_UPDATE1 2 /* New or updated coordinates */
+#define ICN8318_EVENT_UPDATE2 3 /* New or updated coordinates */
+#define ICN8318_EVENT_END 4 /* Finger lifted */
+} __packed;
+
+struct icn8318_touch_data {
+ __u8 softbutton;
+ __u8 touch_count;
+ struct icn8318_touch touches[ICN8318_MAX_TOUCHES];
+} __packed;
+
+struct icn8318_data {
+ struct i2c_client *client;
+ struct input_dev *input;
+ struct gpio_desc *wake_gpio;
+ u32 max_x;
+ u32 max_y;
+ bool invert_x;
+ bool invert_y;
+ bool swap_x_y;
+};
+
+static int icn8318_read_touch_data(struct i2c_client *client,
+ struct icn8318_touch_data *touch_data)
+{
+ u8 reg = ICN8318_REG_TOUCHDATA;
+ struct i2c_msg msg[2] = {
+ {
+ .addr = client->addr,
+ .len = 1,
+ .buf = &reg
+ },
+ {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .len = sizeof(struct icn8318_touch_data),
+ .buf = (u8 *)touch_data
+ }
+ };
+
+ return i2c_transfer(client->adapter, msg, 2);
+}
+
+static inline bool icn8318_touch_active(u8 event)
+{
+ return (event == ICN8318_EVENT_UPDATE1) ||
+ (event == ICN8318_EVENT_UPDATE2);
+}
+
+static irqreturn_t icn8318_irq(int irq, void *dev_id)
+{
+ struct icn8318_data *data = dev_id;
+ struct device *dev = &data->client->dev;
+ struct icn8318_touch_data touch_data;
+ int i, ret, x, y;
+
+ ret = icn8318_read_touch_data(data->client, &touch_data);
+ if (ret < 0) {
+ dev_err(dev, "Error reading touch data: %d\n", ret);
+ return IRQ_HANDLED;
+ }
+
+ if (touch_data.softbutton) {
+ /*
+ * Other data is invalid when a softbutton is pressed.
+ * This needs some extra devicetree bindings to map the icn8318
+ * softbutton codes to evdev codes. Currently no known devices
+ * use this.
+ */
+ return IRQ_HANDLED;
+ }
+
+ if (touch_data.touch_count > ICN8318_MAX_TOUCHES) {
+ dev_warn(dev, "Too much touches %d > %d\n",
+ touch_data.touch_count, ICN8318_MAX_TOUCHES);
+ touch_data.touch_count = ICN8318_MAX_TOUCHES;
+ }
+
+ for (i = 0; i < touch_data.touch_count; i++) {
+ struct icn8318_touch *touch = &touch_data.touches[i];
+ bool act = icn8318_touch_active(touch->event);
+
+ input_mt_slot(data->input, touch->slot);
+ input_mt_report_slot_state(data->input, MT_TOOL_FINGER, act);
+ if (!act)
+ continue;
+
+ x = be16_to_cpu(touch->x);
+ y = be16_to_cpu(touch->y);
+
+ if (data->invert_x)
+ x = data->max_x - x;
+
+ if (data->invert_y)
+ y = data->max_y - y;
+
+ if (!data->swap_x_y) {
+ input_event(data->input, EV_ABS, ABS_MT_POSITION_X, x);
+ input_event(data->input, EV_ABS, ABS_MT_POSITION_Y, y);
+ } else {
+ input_event(data->input, EV_ABS, ABS_MT_POSITION_X, y);
+ input_event(data->input, EV_ABS, ABS_MT_POSITION_Y, x);
+ }
+ }
+
+ input_mt_sync_frame(data->input);
+ input_sync(data->input);
+
+ return IRQ_HANDLED;
+}
+
+static int icn8318_start(struct input_dev *dev)
+{
+ struct icn8318_data *data = input_get_drvdata(dev);
+
+ enable_irq(data->client->irq);
+ gpiod_set_value_cansleep(data->wake_gpio, 1);
+
+ return 0;
+}
+
+static void icn8318_stop(struct input_dev *dev)
+{
+ struct icn8318_data *data = input_get_drvdata(dev);
+
+ disable_irq(data->client->irq);
+ i2c_smbus_write_byte_data(data->client, ICN8318_REG_POWER,
+ ICN8318_POWER_HIBERNATE);
+ gpiod_set_value_cansleep(data->wake_gpio, 0);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int icn8318_suspend(struct device *dev)
+{
+ struct icn8318_data *data = i2c_get_clientdata(to_i2c_client(dev));
+
+ mutex_lock(&data->input->mutex);
+ if (data->input->users)
+ icn8318_stop(data->input);
+ mutex_unlock(&data->input->mutex);
+
+ return 0;
+}
+
+static int icn8318_resume(struct device *dev)
+{
+ struct icn8318_data *data = i2c_get_clientdata(to_i2c_client(dev));
+
+ mutex_lock(&data->input->mutex);
+ if (data->input->users)
+ icn8318_start(data->input);
+ mutex_unlock(&data->input->mutex);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(icn8318_pm_ops, icn8318_suspend, icn8318_resume);
+
+static int icn8318_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct device_node *np = dev->of_node;
+ struct icn8318_data *data;
+ struct input_dev *input;
+ u32 fuzz_x = 0, fuzz_y = 0;
+ int error;
+
+ if (!client->irq) {
+ dev_err(dev, "Error no irq specified\n");
+ return -EINVAL;
+ }
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->wake_gpio = devm_gpiod_get(dev, "wake", GPIOD_OUT_LOW);
+ if (IS_ERR(data->wake_gpio)) {
+ error = PTR_ERR(data->wake_gpio);
+ if (error != -EPROBE_DEFER)
+ dev_err(dev, "Error getting wake gpio: %d\n", error);
+ return error;
+ }
+
+ if (of_property_read_u32(np, "touchscreen-size-x", &data->max_x) ||
+ of_property_read_u32(np, "touchscreen-size-y", &data->max_y)) {
+ dev_err(dev, "Error touchscreen-size-x and/or -y missing\n");
+ return -EINVAL;
+ }
+
+ /* Optional */
+ of_property_read_u32(np, "touchscreen-fuzz-x", &fuzz_x);
+ of_property_read_u32(np, "touchscreen-fuzz-y", &fuzz_y);
+ data->invert_x = of_property_read_bool(np, "touchscreen-inverted-x");
+ data->invert_y = of_property_read_bool(np, "touchscreen-inverted-y");
+ data->swap_x_y = of_property_read_bool(np, "touchscreen-swapped-x-y");
+
+ input = devm_input_allocate_device(dev);
+ if (!input)
+ return -ENOMEM;
+
+ input->name = client->name;
+ input->id.bustype = BUS_I2C;
+ input->open = icn8318_start;
+ input->close = icn8318_stop;
+ input->dev.parent = dev;
+
+ if (!data->swap_x_y) {
+ input_set_abs_params(input, ABS_MT_POSITION_X, 0,
+ data->max_x, fuzz_x, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
+ data->max_y, fuzz_y, 0);
+ } else {
+ input_set_abs_params(input, ABS_MT_POSITION_X, 0,
+ data->max_y, fuzz_y, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_Y, 0,
+ data->max_x, fuzz_x, 0);
+ }
+
+ error = input_mt_init_slots(input, ICN8318_MAX_TOUCHES,
+ INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+ if (error)
+ return error;
+
+ data->client = client;
+ data->input = input;
+ input_set_drvdata(input, data);
+
+ error = devm_request_threaded_irq(dev, client->irq, NULL, icn8318_irq,
+ IRQF_ONESHOT, client->name, data);
+ if (error) {
+ dev_err(dev, "Error requesting irq: %d\n", error);
+ return error;
+ }
+
+ /* Stop device till opened */
+ icn8318_stop(data->input);
+
+ error = input_register_device(input);
+ if (error)
+ return error;
+
+ i2c_set_clientdata(client, data);
+
+ return 0;
+}
+
+static const struct of_device_id icn8318_of_match[] = {
+ { .compatible = "chipone,icn8318" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, icn8318_of_match);
+
+/* This is useless for OF-enabled devices, but it is needed by I2C subsystem */
+static const struct i2c_device_id icn8318_i2c_id[] = {
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, icn8318_i2c_id);
+
+static struct i2c_driver icn8318_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "chipone_icn8318",
+ .pm = &icn8318_pm_ops,
+ .of_match_table = icn8318_of_match,
+ },
+ .probe = icn8318_probe,
+ .id_table = icn8318_i2c_id,
+};
+
+module_i2c_driver(icn8318_driver);
+
+MODULE_DESCRIPTION("ChipOne icn8318 I2C Touchscreen Driver");
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c
index d4c24fb7704f..e6aef3e48bd9 100644
--- a/drivers/input/touchscreen/edt-ft5x06.c
+++ b/drivers/input/touchscreen/edt-ft5x06.c
@@ -37,6 +37,7 @@
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/input/mt.h>
+#include <linux/input/touchscreen.h>
#include <linux/input/edt-ft5x06.h>
#define MAX_SUPPORT_POINTS 5
@@ -1034,7 +1035,6 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client,
input->id.bustype = BUS_I2C;
input->dev.parent = &client->dev;
- __set_bit(EV_SYN, input->evbit);
__set_bit(EV_KEY, input->evbit);
__set_bit(EV_ABS, input->evbit);
__set_bit(BTN_TOUCH, input->keybit);
@@ -1044,6 +1044,10 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client,
0, tsdata->num_x * 64 - 1, 0, 0);
input_set_abs_params(input, ABS_MT_POSITION_Y,
0, tsdata->num_y * 64 - 1, 0, 0);
+
+ if (!pdata)
+ touchscreen_parse_of_params(input);
+
error = input_mt_init_slots(input, MAX_SUPPORT_POINTS, 0);
if (error) {
dev_err(&client->dev, "Unable to init MT slots.\n");
diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c
index 926c58e540c0..0efd766a545b 100644
--- a/drivers/input/touchscreen/elants_i2c.c
+++ b/drivers/input/touchscreen/elants_i2c.c
@@ -98,7 +98,6 @@
#define MAX_FW_UPDATE_RETRIES 30
#define ELAN_FW_PAGESIZE 132
-#define ELAN_FW_FILENAME "elants_i2c.bin"
/* calibration timeout definition */
#define ELAN_CALI_TIMEOUT_MSEC 10000
@@ -697,12 +696,19 @@ static int elants_i2c_fw_update(struct elants_data *ts)
{
struct i2c_client *client = ts->client;
const struct firmware *fw;
+ char *fw_name;
int error;
- error = request_firmware(&fw, ELAN_FW_FILENAME, &client->dev);
+ fw_name = kasprintf(GFP_KERNEL, "elants_i2c_%04x.bin", ts->hw_version);
+ if (!fw_name)
+ return -ENOMEM;
+
+ dev_info(&client->dev, "requesting fw name = %s\n", fw_name);
+ error = request_firmware(&fw, fw_name, &client->dev);
+ kfree(fw_name);
if (error) {
- dev_err(&client->dev, "failed to request firmware %s: %d\n",
- ELAN_FW_FILENAME, error);
+ dev_err(&client->dev, "failed to request firmware: %d\n",
+ error);
return error;
}
diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c
index ca196689f025..3af16984d57c 100644
--- a/drivers/input/touchscreen/goodix.c
+++ b/drivers/input/touchscreen/goodix.c
@@ -23,6 +23,8 @@
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
+#include <linux/acpi.h>
+#include <linux/of.h>
#include <asm/unaligned.h>
struct goodix_ts_data {
@@ -48,6 +50,7 @@ struct goodix_ts_data {
#define GOODIX_REG_VERSION 0x8140
#define RESOLUTION_LOC 1
+#define MAX_CONTACTS_LOC 5
#define TRIGGER_LOC 6
static const unsigned long goodix_irq_flags[] = {
@@ -99,7 +102,7 @@ static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data)
}
touch_num = data[0] & 0x0f;
- if (touch_num > GOODIX_MAX_CONTACTS)
+ if (touch_num > ts->max_touch_num)
return -EPROTO;
if (touch_num > 1) {
@@ -141,7 +144,7 @@ static void goodix_ts_report_touch(struct goodix_ts_data *ts, u8 *coor_data)
*/
static void goodix_process_events(struct goodix_ts_data *ts)
{
- u8 point_data[1 + GOODIX_CONTACT_SIZE * GOODIX_MAX_CONTACTS];
+ u8 point_data[1 + GOODIX_CONTACT_SIZE * ts->max_touch_num];
int touch_num;
int i;
@@ -202,21 +205,23 @@ static void goodix_read_config(struct goodix_ts_data *ts)
ts->abs_x_max = GOODIX_MAX_WIDTH;
ts->abs_y_max = GOODIX_MAX_HEIGHT;
ts->int_trigger_type = GOODIX_INT_TRIGGER;
+ ts->max_touch_num = GOODIX_MAX_CONTACTS;
return;
}
ts->abs_x_max = get_unaligned_le16(&config[RESOLUTION_LOC]);
ts->abs_y_max = get_unaligned_le16(&config[RESOLUTION_LOC + 2]);
- ts->int_trigger_type = (config[TRIGGER_LOC]) & 0x03;
- if (!ts->abs_x_max || !ts->abs_y_max) {
+ ts->int_trigger_type = config[TRIGGER_LOC] & 0x03;
+ ts->max_touch_num = config[MAX_CONTACTS_LOC] & 0x0f;
+ if (!ts->abs_x_max || !ts->abs_y_max || !ts->max_touch_num) {
dev_err(&ts->client->dev,
"Invalid config, using defaults\n");
ts->abs_x_max = GOODIX_MAX_WIDTH;
ts->abs_y_max = GOODIX_MAX_HEIGHT;
+ ts->max_touch_num = GOODIX_MAX_CONTACTS;
}
}
-
/**
* goodix_read_version - Read goodix touchscreen version
*
@@ -295,7 +300,7 @@ static int goodix_request_input_dev(struct goodix_ts_data *ts)
input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0);
input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
- input_mt_init_slots(ts->input_dev, GOODIX_MAX_CONTACTS,
+ input_mt_init_slots(ts->input_dev, ts->max_touch_num,
INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
ts->input_dev->name = "Goodix Capacitive TouchScreen";
@@ -372,11 +377,27 @@ static const struct i2c_device_id goodix_ts_id[] = {
{ }
};
+#ifdef CONFIG_ACPI
static const struct acpi_device_id goodix_acpi_match[] = {
{ "GDIX1001", 0 },
{ }
};
MODULE_DEVICE_TABLE(acpi, goodix_acpi_match);
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id goodix_of_match[] = {
+ { .compatible = "goodix,gt911" },
+ { .compatible = "goodix,gt9110" },
+ { .compatible = "goodix,gt912" },
+ { .compatible = "goodix,gt927" },
+ { .compatible = "goodix,gt9271" },
+ { .compatible = "goodix,gt928" },
+ { .compatible = "goodix,gt967" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, goodix_of_match);
+#endif
static struct i2c_driver goodix_ts_driver = {
.probe = goodix_ts_probe,
@@ -384,7 +405,8 @@ static struct i2c_driver goodix_ts_driver = {
.driver = {
.name = "Goodix-TS",
.owner = THIS_MODULE,
- .acpi_match_table = goodix_acpi_match,
+ .acpi_match_table = ACPI_PTR(goodix_acpi_match),
+ .of_match_table = of_match_ptr(goodix_of_match),
},
};
module_i2c_driver(goodix_ts_driver);
diff --git a/drivers/input/touchscreen/of_touchscreen.c b/drivers/input/touchscreen/of_touchscreen.c
index f8f9b84230b1..b82b5207c78b 100644
--- a/drivers/input/touchscreen/of_touchscreen.c
+++ b/drivers/input/touchscreen/of_touchscreen.c
@@ -11,8 +11,41 @@
#include <linux/of.h>
#include <linux/input.h>
+#include <linux/input/mt.h>
#include <linux/input/touchscreen.h>
+static u32 of_get_optional_u32(struct device_node *np,
+ const char *property)
+{
+ u32 val = 0;
+
+ of_property_read_u32(np, property, &val);
+
+ return val;
+}
+
+static void touchscreen_set_params(struct input_dev *dev,
+ unsigned long axis,
+ int max, int fuzz)
+{
+ struct input_absinfo *absinfo;
+
+ if (!test_bit(axis, dev->absbit)) {
+ /*
+ * Emit a warning only if the axis is not a multitouch
+ * axis, which might not be set by the driver.
+ */
+ if (!input_is_mt_axis(axis))
+ dev_warn(&dev->dev,
+ "DT specifies parameters but the axis is not set up\n");
+ return;
+ }
+
+ absinfo = &dev->absinfo[axis];
+ absinfo->maximum = max;
+ absinfo->fuzz = fuzz;
+}
+
/**
* touchscreen_parse_of_params - parse common touchscreen DT properties
* @dev: device that should be parsed
@@ -24,22 +57,31 @@
void touchscreen_parse_of_params(struct input_dev *dev)
{
struct device_node *np = dev->dev.parent->of_node;
- struct input_absinfo *absinfo;
+ u32 maximum, fuzz;
input_alloc_absinfo(dev);
if (!dev->absinfo)
return;
- absinfo = &dev->absinfo[ABS_X];
- of_property_read_u32(np, "touchscreen-size-x", &absinfo->maximum);
- of_property_read_u32(np, "touchscreen-fuzz-x", &absinfo->fuzz);
+ maximum = of_get_optional_u32(np, "touchscreen-size-x");
+ fuzz = of_get_optional_u32(np, "touchscreen-fuzz-x");
+ if (maximum || fuzz) {
+ touchscreen_set_params(dev, ABS_X, maximum, fuzz);
+ touchscreen_set_params(dev, ABS_MT_POSITION_X, maximum, fuzz);
+ }
- absinfo = &dev->absinfo[ABS_Y];
- of_property_read_u32(np, "touchscreen-size-y", &absinfo->maximum);
- of_property_read_u32(np, "touchscreen-fuzz-y", &absinfo->fuzz);
+ maximum = of_get_optional_u32(np, "touchscreen-size-y");
+ fuzz = of_get_optional_u32(np, "touchscreen-fuzz-y");
+ if (maximum || fuzz) {
+ touchscreen_set_params(dev, ABS_Y, maximum, fuzz);
+ touchscreen_set_params(dev, ABS_MT_POSITION_Y, maximum, fuzz);
+ }
- absinfo = &dev->absinfo[ABS_PRESSURE];
- of_property_read_u32(np, "touchscreen-max-pressure", &absinfo->maximum);
- of_property_read_u32(np, "touchscreen-fuzz-pressure", &absinfo->fuzz);
+ maximum = of_get_optional_u32(np, "touchscreen-max-pressure");
+ fuzz = of_get_optional_u32(np, "touchscreen-fuzz-pressure");
+ if (maximum || fuzz) {
+ touchscreen_set_params(dev, ABS_PRESSURE, maximum, fuzz);
+ touchscreen_set_params(dev, ABS_MT_PRESSURE, maximum, fuzz);
+ }
}
EXPORT_SYMBOL(touchscreen_parse_of_params);
diff --git a/drivers/input/touchscreen/sun4i-ts.c b/drivers/input/touchscreen/sun4i-ts.c
index b93a28b955fd..c0116994067d 100644
--- a/drivers/input/touchscreen/sun4i-ts.c
+++ b/drivers/input/touchscreen/sun4i-ts.c
@@ -30,6 +30,10 @@
* These kinds of heuristics are just asking for trouble (and don't belong
* in the kernel). So this driver offers straight forward, reliable single
* touch functionality only.
+ *
+ * s.a. A20 User Manual "1.15 TP" (Documentation/arm/sunxi/README)
+ * (looks like the description in the A20 User Manual v1.3 is better
+ * than the one in the A10 User Manual v.1.5)
*/
#include <linux/err.h>
@@ -193,7 +197,7 @@ static int sun4i_get_temp(const struct sun4i_ts_data *ts, long *temp)
if (ts->temp_data == -1)
return -EAGAIN;
- *temp = (ts->temp_data - ts->temp_offset) * ts->temp_step;
+ *temp = ts->temp_data * ts->temp_step - ts->temp_offset;
return 0;
}
@@ -246,6 +250,8 @@ static int sun4i_ts_probe(struct platform_device *pdev)
int error;
u32 reg;
bool ts_attached;
+ u32 tp_sensitive_adjust = 15;
+ u32 filter_type = 1;
ts = devm_kzalloc(dev, sizeof(struct sun4i_ts_data), GFP_KERNEL);
if (!ts)
@@ -255,22 +261,31 @@ static int sun4i_ts_probe(struct platform_device *pdev)
ts->ignore_fifo_data = true;
ts->temp_data = -1;
if (of_device_is_compatible(np, "allwinner,sun6i-a31-ts")) {
- /* Allwinner SDK has temperature = -271 + (value / 6) (C) */
- ts->temp_offset = 1626;
+ /* Allwinner SDK has temperature (C) = (value / 6) - 271 */
+ ts->temp_offset = 271000;
ts->temp_step = 167;
+ } else if (of_device_is_compatible(np, "allwinner,sun4i-a10-ts")) {
+ /*
+ * The A10 temperature sensor has quite a wide spread, these
+ * parameters are based on the averaging of the calibration
+ * results of 4 completely different boards, with a spread of
+ * temp_step from 0.096 - 0.170 and temp_offset from 176 - 331.
+ */
+ ts->temp_offset = 257000;
+ ts->temp_step = 133;
} else {
/*
* The user manuals do not contain the formula for calculating
* the temperature. The formula used here is from the AXP209,
* which is designed by X-Powers, an affiliate of Allwinner:
*
- * temperature = -144.7 + (value * 0.1)
+ * temperature (C) = (value * 0.1) - 144.7
*
* Allwinner does not have any documentation whatsoever for
* this hardware. Moreover, it is claimed that the sensor
* is inaccurate and cannot work properly.
*/
- ts->temp_offset = 1447;
+ ts->temp_offset = 144700;
ts->temp_step = 100;
}
@@ -313,14 +328,20 @@ static int sun4i_ts_probe(struct platform_device *pdev)
ts->base + TP_CTRL0);
/*
- * sensitive_adjust = 15 : max, which is not all that sensitive,
+ * tp_sensitive_adjust is an optional property
* tp_mode = 0 : only x and y coordinates, as we don't use dual touch
*/
- writel(TP_SENSITIVE_ADJUST(15) | TP_MODE_SELECT(0),
+ of_property_read_u32(np, "allwinner,tp-sensitive-adjust",
+ &tp_sensitive_adjust);
+ writel(TP_SENSITIVE_ADJUST(tp_sensitive_adjust) | TP_MODE_SELECT(0),
ts->base + TP_CTRL2);
- /* Enable median filter, type 1 : 5/3 */
- writel(FILTER_EN(1) | FILTER_TYPE(1), ts->base + TP_CTRL3);
+ /*
+ * Enable median and averaging filter, optional property for
+ * filter type.
+ */
+ of_property_read_u32(np, "allwinner,filter-type", &filter_type);
+ writel(FILTER_EN(1) | FILTER_TYPE(filter_type), ts->base + TP_CTRL3);
/* Enable temperature measurement, period 1953 (2 seconds) */
writel(TEMP_ENABLE(1) | TEMP_PERIOD(1953), ts->base + TP_TPR);
@@ -330,10 +351,10 @@ static int sun4i_ts_probe(struct platform_device *pdev)
* finally enable tp mode.
*/
reg = STYLUS_UP_DEBOUN(5) | STYLUS_UP_DEBOUN_EN(1);
- if (of_device_is_compatible(np, "allwinner,sun4i-a10-ts"))
- reg |= TP_MODE_EN(1);
- else
+ if (of_device_is_compatible(np, "allwinner,sun6i-a31-ts"))
reg |= SUN6I_TP_MODE_EN(1);
+ else
+ reg |= TP_MODE_EN(1);
writel(reg, ts->base + TP_CTRL1);
/*
@@ -383,6 +404,7 @@ static int sun4i_ts_remove(struct platform_device *pdev)
static const struct of_device_id sun4i_ts_of_match[] = {
{ .compatible = "allwinner,sun4i-a10-ts", },
+ { .compatible = "allwinner,sun5i-a13-ts", },
{ .compatible = "allwinner,sun6i-a31-ts", },
{ /* sentinel */ }
};
diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c
index f1cb05148b46..a24eba5ea843 100644
--- a/drivers/input/touchscreen/sur40.c
+++ b/drivers/input/touchscreen/sur40.c
@@ -1,7 +1,7 @@
/*
* Surface2.0/SUR40/PixelSense input driver
*
- * Copyright (c) 2013 by Florian 'floe' Echtler <floe@butterbrot.org>
+ * Copyright (c) 2014 by Florian 'floe' Echtler <floe@butterbrot.org>
*
* Derived from the USB Skeleton driver 1.1,
* Copyright (c) 2003 Greg Kroah-Hartman (greg@kroah.com)
@@ -12,6 +12,9 @@
* and from the generic hid-multitouch driver,
* Copyright (c) 2010-2012 Stephane Chatty <chatty@enac.fr>
*
+ * and from the v4l2-pci-skeleton driver,
+ * Copyright (c) Copyright 2014 Cisco Systems, Inc.
+ *
* 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
@@ -31,6 +34,11 @@
#include <linux/input-polldev.h>
#include <linux/input/mt.h>
#include <linux/usb/input.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-dma-sg.h>
/* read 512 bytes from endpoint 0x86 -> get header + blobs */
struct sur40_header {
@@ -82,9 +90,19 @@ struct sur40_data {
struct sur40_blob blobs[];
} __packed;
+/* read 512 bytes from endpoint 0x82 -> get header below
+ * continue reading 16k blocks until header.size bytes read */
+struct sur40_image_header {
+ __le32 magic; /* "SUBF" */
+ __le32 packet_id;
+ __le32 size; /* always 0x0007e900 = 960x540 */
+ __le32 timestamp; /* milliseconds (increases by 16 or 17 each frame) */
+ __le32 unknown; /* "epoch?" always 02/03 00 00 00 */
+} __packed;
/* version information */
#define DRIVER_SHORT "sur40"
+#define DRIVER_LONG "Samsung SUR40"
#define DRIVER_AUTHOR "Florian 'floe' Echtler <floe@butterbrot.org>"
#define DRIVER_DESC "Surface2.0/SUR40/PixelSense input driver"
@@ -99,6 +117,13 @@ struct sur40_data {
/* touch data endpoint */
#define TOUCH_ENDPOINT 0x86
+/* video data endpoint */
+#define VIDEO_ENDPOINT 0x82
+
+/* video header fields */
+#define VIDEO_HEADER_MAGIC 0x46425553
+#define VIDEO_PACKET_SIZE 16384
+
/* polling interval (ms) */
#define POLL_INTERVAL 10
@@ -113,21 +138,23 @@ struct sur40_data {
#define SUR40_GET_STATE 0xc5 /* 4 bytes state (?) */
#define SUR40_GET_SENSORS 0xb1 /* 8 bytes sensors */
-/*
- * Note: an earlier, non-public version of this driver used USB_RECIP_ENDPOINT
- * here by mistake which is very likely to have corrupted the firmware EEPROM
- * on two separate SUR40 devices. Thanks to Alan Stern who spotted this bug.
- * Should you ever run into a similar problem, the background story to this
- * incident and instructions on how to fix the corrupted EEPROM are available
- * at https://floe.butterbrot.org/matrix/hacking/surface/brick.html
-*/
-
+/* master device state */
struct sur40_state {
struct usb_device *usbdev;
struct device *dev;
struct input_polled_dev *input;
+ struct v4l2_device v4l2;
+ struct video_device vdev;
+ struct mutex lock;
+
+ struct vb2_queue queue;
+ struct vb2_alloc_ctx *alloc_ctx;
+ struct list_head buf_list;
+ spinlock_t qlock;
+ int sequence;
+
struct sur40_data *bulk_in_buffer;
size_t bulk_in_size;
u8 bulk_in_epaddr;
@@ -135,6 +162,27 @@ struct sur40_state {
char phys[64];
};
+struct sur40_buffer {
+ struct vb2_buffer vb;
+ struct list_head list;
+};
+
+/* forward declarations */
+static const struct video_device sur40_video_device;
+static const struct v4l2_pix_format sur40_video_format;
+static const struct vb2_queue sur40_queue;
+static void sur40_process_video(struct sur40_state *sur40);
+
+/*
+ * Note: an earlier, non-public version of this driver used USB_RECIP_ENDPOINT
+ * here by mistake which is very likely to have corrupted the firmware EEPROM
+ * on two separate SUR40 devices. Thanks to Alan Stern who spotted this bug.
+ * Should you ever run into a similar problem, the background story to this
+ * incident and instructions on how to fix the corrupted EEPROM are available
+ * at https://floe.butterbrot.org/matrix/hacking/surface/brick.html
+*/
+
+/* command wrapper */
static int sur40_command(struct sur40_state *dev,
u8 command, u16 index, void *buffer, u16 size)
{
@@ -247,7 +295,6 @@ static void sur40_report_blob(struct sur40_blob *blob, struct input_dev *input)
/* core function: poll for new input data */
static void sur40_poll(struct input_polled_dev *polldev)
{
-
struct sur40_state *sur40 = polldev->private;
struct input_dev *input = polldev->input;
int result, bulk_read, need_blobs, packet_blobs, i;
@@ -314,6 +361,86 @@ static void sur40_poll(struct input_polled_dev *polldev)
input_mt_sync_frame(input);
input_sync(input);
+
+ sur40_process_video(sur40);
+}
+
+/* deal with video data */
+static void sur40_process_video(struct sur40_state *sur40)
+{
+
+ struct sur40_image_header *img = (void *)(sur40->bulk_in_buffer);
+ struct sur40_buffer *new_buf;
+ struct usb_sg_request sgr;
+ struct sg_table *sgt;
+ int result, bulk_read;
+
+ if (!vb2_start_streaming_called(&sur40->queue))
+ return;
+
+ /* get a new buffer from the list */
+ spin_lock(&sur40->qlock);
+ if (list_empty(&sur40->buf_list)) {
+ dev_dbg(sur40->dev, "buffer queue empty\n");
+ spin_unlock(&sur40->qlock);
+ return;
+ }
+ new_buf = list_entry(sur40->buf_list.next, struct sur40_buffer, list);
+ list_del(&new_buf->list);
+ spin_unlock(&sur40->qlock);
+
+ /* retrieve data via bulk read */
+ result = usb_bulk_msg(sur40->usbdev,
+ usb_rcvbulkpipe(sur40->usbdev, VIDEO_ENDPOINT),
+ sur40->bulk_in_buffer, sur40->bulk_in_size,
+ &bulk_read, 1000);
+
+ if (result < 0) {
+ dev_err(sur40->dev, "error in usb_bulk_read\n");
+ goto err_poll;
+ }
+
+ if (bulk_read != sizeof(struct sur40_image_header)) {
+ dev_err(sur40->dev, "received %d bytes (%zd expected)\n",
+ bulk_read, sizeof(struct sur40_image_header));
+ goto err_poll;
+ }
+
+ if (le32_to_cpu(img->magic) != VIDEO_HEADER_MAGIC) {
+ dev_err(sur40->dev, "image magic mismatch\n");
+ goto err_poll;
+ }
+
+ if (le32_to_cpu(img->size) != sur40_video_format.sizeimage) {
+ dev_err(sur40->dev, "image size mismatch\n");
+ goto err_poll;
+ }
+
+ sgt = vb2_dma_sg_plane_desc(&new_buf->vb, 0);
+
+ result = usb_sg_init(&sgr, sur40->usbdev,
+ usb_rcvbulkpipe(sur40->usbdev, VIDEO_ENDPOINT), 0,
+ sgt->sgl, sgt->nents, sur40_video_format.sizeimage, 0);
+ if (result < 0) {
+ dev_err(sur40->dev, "error %d in usb_sg_init\n", result);
+ goto err_poll;
+ }
+
+ usb_sg_wait(&sgr);
+ if (sgr.status < 0) {
+ dev_err(sur40->dev, "error %d in usb_sg_wait\n", sgr.status);
+ goto err_poll;
+ }
+
+ /* mark as finished */
+ v4l2_get_timestamp(&new_buf->vb.v4l2_buf.timestamp);
+ new_buf->vb.v4l2_buf.sequence = sur40->sequence++;
+ new_buf->vb.v4l2_buf.field = V4L2_FIELD_NONE;
+ vb2_buffer_done(&new_buf->vb, VB2_BUF_STATE_DONE);
+ return;
+
+err_poll:
+ vb2_buffer_done(&new_buf->vb, VB2_BUF_STATE_ERROR);
}
/* Initialize input device parameters. */
@@ -377,6 +504,11 @@ static int sur40_probe(struct usb_interface *interface,
goto err_free_dev;
}
+ /* initialize locks/lists */
+ INIT_LIST_HEAD(&sur40->buf_list);
+ spin_lock_init(&sur40->qlock);
+ mutex_init(&sur40->lock);
+
/* Set up polled input device control structure */
poll_dev->private = sur40;
poll_dev->poll_interval = POLL_INTERVAL;
@@ -387,7 +519,7 @@ static int sur40_probe(struct usb_interface *interface,
/* Set up regular input device structure */
sur40_input_setup(poll_dev->input);
- poll_dev->input->name = "Samsung SUR40";
+ poll_dev->input->name = DRIVER_LONG;
usb_to_input_id(usbdev, &poll_dev->input->id);
usb_make_path(usbdev, sur40->phys, sizeof(sur40->phys));
strlcat(sur40->phys, "/input0", sizeof(sur40->phys));
@@ -408,6 +540,7 @@ static int sur40_probe(struct usb_interface *interface,
goto err_free_polldev;
}
+ /* register the polled input device */
error = input_register_polled_device(poll_dev);
if (error) {
dev_err(&interface->dev,
@@ -415,12 +548,54 @@ static int sur40_probe(struct usb_interface *interface,
goto err_free_buffer;
}
+ /* register the video master device */
+ snprintf(sur40->v4l2.name, sizeof(sur40->v4l2.name), "%s", DRIVER_LONG);
+ error = v4l2_device_register(sur40->dev, &sur40->v4l2);
+ if (error) {
+ dev_err(&interface->dev,
+ "Unable to register video master device.");
+ goto err_unreg_v4l2;
+ }
+
+ /* initialize the lock and subdevice */
+ sur40->queue = sur40_queue;
+ sur40->queue.drv_priv = sur40;
+ sur40->queue.lock = &sur40->lock;
+
+ /* initialize the queue */
+ error = vb2_queue_init(&sur40->queue);
+ if (error)
+ goto err_unreg_v4l2;
+
+ sur40->alloc_ctx = vb2_dma_sg_init_ctx(sur40->dev);
+ if (IS_ERR(sur40->alloc_ctx)) {
+ dev_err(sur40->dev, "Can't allocate buffer context");
+ goto err_unreg_v4l2;
+ }
+
+ sur40->vdev = sur40_video_device;
+ sur40->vdev.v4l2_dev = &sur40->v4l2;
+ sur40->vdev.lock = &sur40->lock;
+ sur40->vdev.queue = &sur40->queue;
+ video_set_drvdata(&sur40->vdev, sur40);
+
+ error = video_register_device(&sur40->vdev, VFL_TYPE_GRABBER, -1);
+ if (error) {
+ dev_err(&interface->dev,
+ "Unable to register video subdevice.");
+ goto err_unreg_video;
+ }
+
/* we can register the device now, as it is ready */
usb_set_intfdata(interface, sur40);
dev_dbg(&interface->dev, "%s is now attached\n", DRIVER_DESC);
return 0;
+err_unreg_video:
+ video_unregister_device(&sur40->vdev);
+err_unreg_v4l2:
+ v4l2_device_unregister(&sur40->v4l2);
err_free_buffer:
kfree(sur40->bulk_in_buffer);
err_free_polldev:
@@ -436,6 +611,10 @@ static void sur40_disconnect(struct usb_interface *interface)
{
struct sur40_state *sur40 = usb_get_intfdata(interface);
+ video_unregister_device(&sur40->vdev);
+ v4l2_device_unregister(&sur40->v4l2);
+ vb2_dma_sg_cleanup_ctx(sur40->alloc_ctx);
+
input_unregister_polled_device(sur40->input);
input_free_polled_device(sur40->input);
kfree(sur40->bulk_in_buffer);
@@ -445,12 +624,243 @@ static void sur40_disconnect(struct usb_interface *interface)
dev_dbg(&interface->dev, "%s is now disconnected\n", DRIVER_DESC);
}
+/*
+ * Setup the constraints of the queue: besides setting the number of planes
+ * per buffer and the size and allocation context of each plane, it also
+ * checks if sufficient buffers have been allocated. Usually 3 is a good
+ * minimum number: many DMA engines need a minimum of 2 buffers in the
+ * queue and you need to have another available for userspace processing.
+ */
+static int sur40_queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt,
+ unsigned int *nbuffers, unsigned int *nplanes,
+ unsigned int sizes[], void *alloc_ctxs[])
+{
+ struct sur40_state *sur40 = vb2_get_drv_priv(q);
+
+ if (q->num_buffers + *nbuffers < 3)
+ *nbuffers = 3 - q->num_buffers;
+
+ if (fmt && fmt->fmt.pix.sizeimage < sur40_video_format.sizeimage)
+ return -EINVAL;
+
+ *nplanes = 1;
+ sizes[0] = fmt ? fmt->fmt.pix.sizeimage : sur40_video_format.sizeimage;
+ alloc_ctxs[0] = sur40->alloc_ctx;
+
+ return 0;
+}
+
+/*
+ * Prepare the buffer for queueing to the DMA engine: check and set the
+ * payload size.
+ */
+static int sur40_buffer_prepare(struct vb2_buffer *vb)
+{
+ struct sur40_state *sur40 = vb2_get_drv_priv(vb->vb2_queue);
+ unsigned long size = sur40_video_format.sizeimage;
+
+ if (vb2_plane_size(vb, 0) < size) {
+ dev_err(&sur40->usbdev->dev, "buffer too small (%lu < %lu)\n",
+ vb2_plane_size(vb, 0), size);
+ return -EINVAL;
+ }
+
+ vb2_set_plane_payload(vb, 0, size);
+ return 0;
+}
+
+/*
+ * Queue this buffer to the DMA engine.
+ */
+static void sur40_buffer_queue(struct vb2_buffer *vb)
+{
+ struct sur40_state *sur40 = vb2_get_drv_priv(vb->vb2_queue);
+ struct sur40_buffer *buf = (struct sur40_buffer *)vb;
+
+ spin_lock(&sur40->qlock);
+ list_add_tail(&buf->list, &sur40->buf_list);
+ spin_unlock(&sur40->qlock);
+}
+
+static void return_all_buffers(struct sur40_state *sur40,
+ enum vb2_buffer_state state)
+{
+ struct sur40_buffer *buf, *node;
+
+ spin_lock(&sur40->qlock);
+ list_for_each_entry_safe(buf, node, &sur40->buf_list, list) {
+ vb2_buffer_done(&buf->vb, state);
+ list_del(&buf->list);
+ }
+ spin_unlock(&sur40->qlock);
+}
+
+/*
+ * Start streaming. First check if the minimum number of buffers have been
+ * queued. If not, then return -ENOBUFS and the vb2 framework will call
+ * this function again the next time a buffer has been queued until enough
+ * buffers are available to actually start the DMA engine.
+ */
+static int sur40_start_streaming(struct vb2_queue *vq, unsigned int count)
+{
+ struct sur40_state *sur40 = vb2_get_drv_priv(vq);
+
+ sur40->sequence = 0;
+ return 0;
+}
+
+/*
+ * Stop the DMA engine. Any remaining buffers in the DMA queue are dequeued
+ * and passed on to the vb2 framework marked as STATE_ERROR.
+ */
+static void sur40_stop_streaming(struct vb2_queue *vq)
+{
+ struct sur40_state *sur40 = vb2_get_drv_priv(vq);
+
+ /* Release all active buffers */
+ return_all_buffers(sur40, VB2_BUF_STATE_ERROR);
+}
+
+/* V4L ioctl */
+static int sur40_vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct sur40_state *sur40 = video_drvdata(file);
+
+ strlcpy(cap->driver, DRIVER_SHORT, sizeof(cap->driver));
+ strlcpy(cap->card, DRIVER_LONG, sizeof(cap->card));
+ usb_make_path(sur40->usbdev, cap->bus_info, sizeof(cap->bus_info));
+ cap->device_caps = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_READWRITE |
+ V4L2_CAP_STREAMING;
+ cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+ return 0;
+}
+
+static int sur40_vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *i)
+{
+ if (i->index != 0)
+ return -EINVAL;
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ i->std = V4L2_STD_UNKNOWN;
+ strlcpy(i->name, "In-Cell Sensor", sizeof(i->name));
+ i->capabilities = 0;
+ return 0;
+}
+
+static int sur40_vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+ return (i == 0) ? 0 : -EINVAL;
+}
+
+static int sur40_vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+ *i = 0;
+ return 0;
+}
+
+static int sur40_vidioc_fmt(struct file *file, void *priv,
+ struct v4l2_format *f)
+{
+ f->fmt.pix = sur40_video_format;
+ return 0;
+}
+
+static int sur40_vidioc_enum_fmt(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+{
+ if (f->index != 0)
+ return -EINVAL;
+ strlcpy(f->description, "8-bit greyscale", sizeof(f->description));
+ f->pixelformat = V4L2_PIX_FMT_GREY;
+ f->flags = 0;
+ return 0;
+}
+
static const struct usb_device_id sur40_table[] = {
{ USB_DEVICE(ID_MICROSOFT, ID_SUR40) }, /* Samsung SUR40 */
{ } /* terminating null entry */
};
MODULE_DEVICE_TABLE(usb, sur40_table);
+/* V4L2 structures */
+static const struct vb2_ops sur40_queue_ops = {
+ .queue_setup = sur40_queue_setup,
+ .buf_prepare = sur40_buffer_prepare,
+ .buf_queue = sur40_buffer_queue,
+ .start_streaming = sur40_start_streaming,
+ .stop_streaming = sur40_stop_streaming,
+ .wait_prepare = vb2_ops_wait_prepare,
+ .wait_finish = vb2_ops_wait_finish,
+};
+
+static const struct vb2_queue sur40_queue = {
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ /*
+ * VB2_USERPTR in currently not enabled: passing a user pointer to
+ * dma-sg will result in segment sizes that are not a multiple of
+ * 512 bytes, which is required by the host controller.
+ */
+ .io_modes = VB2_MMAP | VB2_READ | VB2_DMABUF,
+ .buf_struct_size = sizeof(struct sur40_buffer),
+ .ops = &sur40_queue_ops,
+ .mem_ops = &vb2_dma_sg_memops,
+ .timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC,
+ .min_buffers_needed = 3,
+};
+
+static const struct v4l2_file_operations sur40_video_fops = {
+ .owner = THIS_MODULE,
+ .open = v4l2_fh_open,
+ .release = vb2_fop_release,
+ .unlocked_ioctl = video_ioctl2,
+ .read = vb2_fop_read,
+ .mmap = vb2_fop_mmap,
+ .poll = vb2_fop_poll,
+};
+
+static const struct v4l2_ioctl_ops sur40_video_ioctl_ops = {
+
+ .vidioc_querycap = sur40_vidioc_querycap,
+
+ .vidioc_enum_fmt_vid_cap = sur40_vidioc_enum_fmt,
+ .vidioc_try_fmt_vid_cap = sur40_vidioc_fmt,
+ .vidioc_s_fmt_vid_cap = sur40_vidioc_fmt,
+ .vidioc_g_fmt_vid_cap = sur40_vidioc_fmt,
+
+ .vidioc_enum_input = sur40_vidioc_enum_input,
+ .vidioc_g_input = sur40_vidioc_g_input,
+ .vidioc_s_input = sur40_vidioc_s_input,
+
+ .vidioc_reqbufs = vb2_ioctl_reqbufs,
+ .vidioc_create_bufs = vb2_ioctl_create_bufs,
+ .vidioc_querybuf = vb2_ioctl_querybuf,
+ .vidioc_qbuf = vb2_ioctl_qbuf,
+ .vidioc_dqbuf = vb2_ioctl_dqbuf,
+ .vidioc_expbuf = vb2_ioctl_expbuf,
+
+ .vidioc_streamon = vb2_ioctl_streamon,
+ .vidioc_streamoff = vb2_ioctl_streamoff,
+};
+
+static const struct video_device sur40_video_device = {
+ .name = DRIVER_LONG,
+ .fops = &sur40_video_fops,
+ .ioctl_ops = &sur40_video_ioctl_ops,
+ .release = video_device_release_empty,
+};
+
+static const struct v4l2_pix_format sur40_video_format = {
+ .pixelformat = V4L2_PIX_FMT_GREY,
+ .width = SENSOR_RES_X / 2,
+ .height = SENSOR_RES_Y / 2,
+ .field = V4L2_FIELD_NONE,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+ .bytesperline = SENSOR_RES_X / 2,
+ .sizeimage = (SENSOR_RES_X/2) * (SENSOR_RES_Y/2),
+};
+
/* USB-specific object needed to register this driver with the USB subsystem. */
static struct usb_driver sur40_driver = {
.name = DRIVER_SHORT,
diff --git a/drivers/input/touchscreen/sx8654.c b/drivers/input/touchscreen/sx8654.c
new file mode 100644
index 000000000000..aecb9ad2e701
--- /dev/null
+++ b/drivers/input/touchscreen/sx8654.c
@@ -0,0 +1,286 @@
+/*
+ * Driver for Semtech SX8654 I2C touchscreen controller.
+ *
+ * Copyright (c) 2015 Armadeus Systems
+ * Sébastien Szymanski <sebastien.szymanski@armadeus.com>
+ *
+ * Using code from:
+ * - sx865x.c
+ * Copyright (c) 2013 U-MoBo Srl
+ * Pierluigi Passaro <p.passaro@u-mobo.com>
+ * - sx8650.c
+ * Copyright (c) 2009 Wayne Roberts
+ * - tsc2007.c
+ * Copyright (c) 2008 Kwangwoo Lee
+ * - ads7846.c
+ * Copyright (c) 2005 David Brownell
+ * Copyright (c) 2006 Nokia Corporation
+ * - corgi_ts.c
+ * Copyright (C) 2004-2005 Richard Purdie
+ * - omap_ts.[hc], ads7846.h, ts_osk.c
+ * Copyright (C) 2002 MontaVista Software
+ * Copyright (C) 2004 Texas Instruments
+ * Copyright (C) 2005 Dirk Behme
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+
+/* register addresses */
+#define I2C_REG_TOUCH0 0x00
+#define I2C_REG_TOUCH1 0x01
+#define I2C_REG_CHANMASK 0x04
+#define I2C_REG_IRQMASK 0x22
+#define I2C_REG_IRQSRC 0x23
+#define I2C_REG_SOFTRESET 0x3f
+
+/* commands */
+#define CMD_READ_REGISTER 0x40
+#define CMD_MANUAL 0xc0
+#define CMD_PENTRG 0xe0
+
+/* value for I2C_REG_SOFTRESET */
+#define SOFTRESET_VALUE 0xde
+
+/* bits for I2C_REG_IRQSRC */
+#define IRQ_PENTOUCH_TOUCHCONVDONE 0x08
+#define IRQ_PENRELEASE 0x04
+
+/* bits for RegTouch1 */
+#define CONDIRQ 0x20
+#define FILT_7SA 0x03
+
+/* bits for I2C_REG_CHANMASK */
+#define CONV_X 0x80
+#define CONV_Y 0x40
+
+/* coordinates rate: higher nibble of CTRL0 register */
+#define RATE_MANUAL 0x00
+#define RATE_5000CPS 0xf0
+
+/* power delay: lower nibble of CTRL0 register */
+#define POWDLY_1_1MS 0x0b
+
+#define MAX_12BIT ((1 << 12) - 1)
+
+struct sx8654 {
+ struct input_dev *input;
+ struct i2c_client *client;
+};
+
+static irqreturn_t sx8654_irq(int irq, void *handle)
+{
+ struct sx8654 *sx8654 = handle;
+ int irqsrc;
+ u8 data[4];
+ unsigned int x, y;
+ int retval;
+
+ irqsrc = i2c_smbus_read_byte_data(sx8654->client,
+ CMD_READ_REGISTER | I2C_REG_IRQSRC);
+ dev_dbg(&sx8654->client->dev, "irqsrc = 0x%x", irqsrc);
+
+ if (irqsrc < 0)
+ goto out;
+
+ if (irqsrc & IRQ_PENRELEASE) {
+ dev_dbg(&sx8654->client->dev, "pen release interrupt");
+
+ input_report_key(sx8654->input, BTN_TOUCH, 0);
+ input_sync(sx8654->input);
+ }
+
+ if (irqsrc & IRQ_PENTOUCH_TOUCHCONVDONE) {
+ dev_dbg(&sx8654->client->dev, "pen touch interrupt");
+
+ retval = i2c_master_recv(sx8654->client, data, sizeof(data));
+ if (retval != sizeof(data))
+ goto out;
+
+ /* invalid data */
+ if (unlikely(data[0] & 0x80 || data[2] & 0x80))
+ goto out;
+
+ x = ((data[0] & 0xf) << 8) | (data[1]);
+ y = ((data[2] & 0xf) << 8) | (data[3]);
+
+ input_report_abs(sx8654->input, ABS_X, x);
+ input_report_abs(sx8654->input, ABS_Y, y);
+ input_report_key(sx8654->input, BTN_TOUCH, 1);
+ input_sync(sx8654->input);
+
+ dev_dbg(&sx8654->client->dev, "point(%4d,%4d)\n", x, y);
+ }
+
+out:
+ return IRQ_HANDLED;
+}
+
+static int sx8654_open(struct input_dev *dev)
+{
+ struct sx8654 *sx8654 = input_get_drvdata(dev);
+ struct i2c_client *client = sx8654->client;
+ int error;
+
+ /* enable pen trigger mode */
+ error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH0,
+ RATE_5000CPS | POWDLY_1_1MS);
+ if (error) {
+ dev_err(&client->dev, "writing to I2C_REG_TOUCH0 failed");
+ return error;
+ }
+
+ error = i2c_smbus_write_byte(client, CMD_PENTRG);
+ if (error) {
+ dev_err(&client->dev, "writing command CMD_PENTRG failed");
+ return error;
+ }
+
+ enable_irq(client->irq);
+
+ return 0;
+}
+
+static void sx8654_close(struct input_dev *dev)
+{
+ struct sx8654 *sx8654 = input_get_drvdata(dev);
+ struct i2c_client *client = sx8654->client;
+ int error;
+
+ disable_irq(client->irq);
+
+ /* enable manual mode mode */
+ error = i2c_smbus_write_byte(client, CMD_MANUAL);
+ if (error) {
+ dev_err(&client->dev, "writing command CMD_MANUAL failed");
+ return;
+ }
+
+ error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH0, 0);
+ if (error) {
+ dev_err(&client->dev, "writing to I2C_REG_TOUCH0 failed");
+ return;
+ }
+}
+
+static int sx8654_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct sx8654 *sx8654;
+ struct input_dev *input;
+ int error;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_WORD_DATA))
+ return -ENXIO;
+
+ sx8654 = devm_kzalloc(&client->dev, sizeof(*sx8654), GFP_KERNEL);
+ if (!sx8654)
+ return -ENOMEM;
+
+ input = devm_input_allocate_device(&client->dev);
+ if (!sx8654)
+ return -ENOMEM;
+
+ input->name = "SX8654 I2C Touchscreen";
+ input->id.bustype = BUS_I2C;
+ input->dev.parent = &client->dev;
+ input->open = sx8654_open;
+ input->close = sx8654_close;
+
+ __set_bit(INPUT_PROP_DIRECT, input->propbit);
+ input_set_capability(input, EV_KEY, BTN_TOUCH);
+ input_set_abs_params(input, ABS_X, 0, MAX_12BIT, 0, 0);
+ input_set_abs_params(input, ABS_Y, 0, MAX_12BIT, 0, 0);
+
+ sx8654->client = client;
+ sx8654->input = input;
+
+ input_set_drvdata(sx8654->input, sx8654);
+
+ error = i2c_smbus_write_byte_data(client, I2C_REG_SOFTRESET,
+ SOFTRESET_VALUE);
+ if (error) {
+ dev_err(&client->dev, "writing softreset value failed");
+ return error;
+ }
+
+ error = i2c_smbus_write_byte_data(client, I2C_REG_CHANMASK,
+ CONV_X | CONV_Y);
+ if (error) {
+ dev_err(&client->dev, "writing to I2C_REG_CHANMASK failed");
+ return error;
+ }
+
+ error = i2c_smbus_write_byte_data(client, I2C_REG_IRQMASK,
+ IRQ_PENTOUCH_TOUCHCONVDONE |
+ IRQ_PENRELEASE);
+ if (error) {
+ dev_err(&client->dev, "writing to I2C_REG_IRQMASK failed");
+ return error;
+ }
+
+ error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH1,
+ CONDIRQ | FILT_7SA);
+ if (error) {
+ dev_err(&client->dev, "writing to I2C_REG_TOUCH1 failed");
+ return error;
+ }
+
+ error = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL, sx8654_irq,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ client->name, sx8654);
+ if (error) {
+ dev_err(&client->dev,
+ "Failed to enable IRQ %d, error: %d\n",
+ client->irq, error);
+ return error;
+ }
+
+ /* Disable the IRQ, we'll enable it in sx8654_open() */
+ disable_irq(client->irq);
+
+ error = input_register_device(sx8654->input);
+ if (error)
+ return error;
+
+ i2c_set_clientdata(client, sx8654);
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id sx8654_of_match[] = {
+ { .compatible = "semtech,sx8654", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, sx8654_of_match);
+#endif
+
+static const struct i2c_device_id sx8654_id_table[] = {
+ { "semtech_sx8654", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, sx8654_id_table);
+
+static struct i2c_driver sx8654_driver = {
+ .driver = {
+ .name = "sx8654",
+ .of_match_table = of_match_ptr(sx8654_of_match),
+ },
+ .id_table = sx8654_id_table,
+ .probe = sx8654_probe,
+};
+module_i2c_driver(sx8654_driver);
+
+MODULE_AUTHOR("Sébastien Szymanski <sebastien.szymanski@armadeus.com>");
+MODULE_DESCRIPTION("Semtech SX8654 I2C Touchscreen Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007.c
index 1bf9906b5a3f..ccc8aa615709 100644
--- a/drivers/input/touchscreen/tsc2007.c
+++ b/drivers/input/touchscreen/tsc2007.c
@@ -75,7 +75,7 @@ struct tsc2007 {
u16 model;
u16 x_plate_ohms;
u16 max_rt;
- unsigned long poll_period;
+ unsigned long poll_period; /* in jiffies */
int fuzzx;
int fuzzy;
int fuzzz;
@@ -214,8 +214,7 @@ static irqreturn_t tsc2007_soft_irq(int irq, void *handle)
dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt);
}
- wait_event_timeout(ts->wait, ts->stopped,
- msecs_to_jiffies(ts->poll_period));
+ wait_event_timeout(ts->wait, ts->stopped, ts->poll_period);
}
dev_dbg(&ts->client->dev, "UP\n");
@@ -314,9 +313,9 @@ static int tsc2007_probe_dt(struct i2c_client *client, struct tsc2007 *ts)
ts->fuzzz = val32;
if (!of_property_read_u64(np, "ti,poll-period", &val64))
- ts->poll_period = val64;
+ ts->poll_period = msecs_to_jiffies(val64);
else
- ts->poll_period = 1;
+ ts->poll_period = msecs_to_jiffies(1);
if (!of_property_read_u32(np, "ti,x-plate-ohms", &val32)) {
ts->x_plate_ohms = val32;
@@ -350,7 +349,7 @@ static int tsc2007_probe_pdev(struct i2c_client *client, struct tsc2007 *ts,
ts->model = pdata->model;
ts->x_plate_ohms = pdata->x_plate_ohms;
ts->max_rt = pdata->max_rt ? : MAX_12BIT;
- ts->poll_period = pdata->poll_period ? : 1;
+ ts->poll_period = msecs_to_jiffies(pdata->poll_period ? : 1);
ts->get_pendown_state = pdata->get_pendown_state;
ts->clear_penirq = pdata->clear_penirq;
ts->fuzzx = pdata->fuzzx;
diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c
index a0966331a89b..f2c6c352c55a 100644
--- a/drivers/input/touchscreen/usbtouchscreen.c
+++ b/drivers/input/touchscreen/usbtouchscreen.c
@@ -132,6 +132,7 @@ enum {
DEVTYPE_GUNZE,
DEVTYPE_DMC_TSC10,
DEVTYPE_IRTOUCH,
+ DEVTYPE_IRTOUCH_HIRES,
DEVTYPE_IDEALTEK,
DEVTYPE_GENERAL_TOUCH,
DEVTYPE_GOTOP,
@@ -198,6 +199,7 @@ static const struct usb_device_id usbtouch_devices[] = {
#ifdef CONFIG_TOUCHSCREEN_USB_IRTOUCH
{USB_DEVICE(0x595a, 0x0001), .driver_info = DEVTYPE_IRTOUCH},
{USB_DEVICE(0x6615, 0x0001), .driver_info = DEVTYPE_IRTOUCH},
+ {USB_DEVICE(0x6615, 0x0012), .driver_info = DEVTYPE_IRTOUCH_HIRES},
#endif
#ifdef CONFIG_TOUCHSCREEN_USB_IDEALTEK
@@ -1177,6 +1179,15 @@ static struct usbtouch_device_info usbtouch_dev_info[] = {
.rept_size = 8,
.read_data = irtouch_read_data,
},
+
+ [DEVTYPE_IRTOUCH_HIRES] = {
+ .min_xc = 0x0,
+ .max_xc = 0x7fff,
+ .min_yc = 0x0,
+ .max_yc = 0x7fff,
+ .rept_size = 8,
+ .read_data = irtouch_read_data,
+ },
#endif
#ifdef CONFIG_TOUCHSCREEN_USB_IDEALTEK