From cab7faca5e446b84e829d57d2095035d72edba09 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Wed, 27 Jun 2012 09:53:47 +0200 Subject: Input: MT - Include win8 support The newly released HID protocol for win8 multitouch devices is capable of transmitting more information about each touch. In particular, it includes details useful for touch alignment. This patch completes the MT protocol with the ABS_MT_TOOL_X/Y events, and documents how to map win8 devices. Cc: Stephane Chatty Cc: Benjamin Tissoires Cc: Peter Hutterer Acked-by: Chase Douglas Signed-off-by: Henrik Rydberg --- include/linux/input.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/input.h b/include/linux/input.h index a81671453575..cacb95343a7e 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -806,18 +806,20 @@ struct input_keymap_entry { #define ABS_MT_WIDTH_MAJOR 0x32 /* Major axis of approaching ellipse */ #define ABS_MT_WIDTH_MINOR 0x33 /* Minor axis (omit if circular) */ #define ABS_MT_ORIENTATION 0x34 /* Ellipse orientation */ -#define ABS_MT_POSITION_X 0x35 /* Center X ellipse position */ -#define ABS_MT_POSITION_Y 0x36 /* Center Y ellipse position */ +#define ABS_MT_POSITION_X 0x35 /* Center X touch position */ +#define ABS_MT_POSITION_Y 0x36 /* Center Y touch position */ #define ABS_MT_TOOL_TYPE 0x37 /* Type of touching device */ #define ABS_MT_BLOB_ID 0x38 /* Group a set of packets as a blob */ #define ABS_MT_TRACKING_ID 0x39 /* Unique ID of initiated contact */ #define ABS_MT_PRESSURE 0x3a /* Pressure on contact area */ #define ABS_MT_DISTANCE 0x3b /* Contact hover distance */ +#define ABS_MT_TOOL_X 0x3c /* Center X tool position */ +#define ABS_MT_TOOL_Y 0x3d /* Center Y tool position */ #ifdef __KERNEL__ /* Implementation details, userspace should not care about these */ #define ABS_MT_FIRST ABS_MT_TOUCH_MAJOR -#define ABS_MT_LAST ABS_MT_DISTANCE +#define ABS_MT_LAST ABS_MT_TOOL_Y #endif #define ABS_MAX 0x3f -- cgit v1.2.3-59-g8ed1b From 6680884a44207efe8ef6bc56b1c932cce2b89994 Mon Sep 17 00:00:00 2001 From: Michael Hennerich Date: Fri, 6 Jul 2012 10:44:19 -0700 Subject: Input: ad7879 - add option to correct xy axis Sebastian Zenker reported that driver swaps x and y samples when the touchscreen leads are connected in accordance with the datasheet specification. Transposed axis can be typically corrected by touch screen calibration however this bug also negatively influences touch pressure measurements. Add an option to correct x and y axis. Signed-off-by: Michael Hennerich Reported-and-tested-by: Sebastian Zenker Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/ad7879.c | 5 +++++ include/linux/spi/ad7879.h | 2 ++ 2 files changed, 7 insertions(+) (limited to 'include/linux') diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c index e2482b40da51..e60709261951 100644 --- a/drivers/input/touchscreen/ad7879.c +++ b/drivers/input/touchscreen/ad7879.c @@ -118,6 +118,7 @@ struct ad7879 { unsigned int irq; bool disabled; /* P: input->mutex */ bool suspended; /* P: input->mutex */ + bool swap_xy; u16 conversion_data[AD7879_NR_SENSE]; char phys[32]; u8 first_conversion_delay; @@ -161,6 +162,9 @@ static int ad7879_report(struct ad7879 *ts) z1 = ts->conversion_data[AD7879_SEQ_Z1] & MAX_12BIT; z2 = ts->conversion_data[AD7879_SEQ_Z2] & MAX_12BIT; + if (ts->swap_xy) + swap(x, y); + /* * The samples processed here are already preprocessed by the AD7879. * The preprocessing function consists of a median and an averaging @@ -520,6 +524,7 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq, 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); diff --git a/include/linux/spi/ad7879.h b/include/linux/spi/ad7879.h index 6334cee1a3be..58368be0b4c0 100644 --- a/include/linux/spi/ad7879.h +++ b/include/linux/spi/ad7879.h @@ -12,6 +12,8 @@ struct ad7879_platform_data { u16 y_min, y_max; u16 pressure_min, pressure_max; + bool swap_xy; /* swap x and y axes */ + /* [0..255] 0=OFF Starts at 1=550us and goes * all the way to 9.440ms in steps of 35us. */ -- cgit v1.2.3-59-g8ed1b From 07b8481d4aff73d6f451f25e74ea10240ff5131e Mon Sep 17 00:00:00 2001 From: Joonyoung Shim Date: Fri, 13 Jul 2012 00:25:45 -0700 Subject: Input: add MELFAS mms114 touchscreen driver This is a initial driver for new touchscreen chip mms114 of MELFAS. It uses I2C interface and supports 10 multi touch. Signed-off-by: Joonyoung Shim Signed-off-by: Kyungmin Park Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 12 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/mms114.c | 544 +++++++++++++++++++++++++++++++++++++ include/linux/i2c/mms114.h | 24 ++ 4 files changed, 581 insertions(+) create mode 100644 drivers/input/touchscreen/mms114.c create mode 100644 include/linux/i2c/mms114.h (limited to 'include/linux') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 98d263504eea..73bd2f6b82ec 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -369,6 +369,18 @@ config TOUCHSCREEN_MCS5000 To compile this driver as a module, choose M here: the module will be called mcs5000_ts. +config TOUCHSCREEN_MMS114 + tristate "MELFAS MMS114 touchscreen" + depends on I2C + help + Say Y here if you have the MELFAS MMS114 touchscreen controller + chip in your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called mms114. + config TOUCHSCREEN_MTOUCH tristate "MicroTouch serial touchscreens" select SERIO diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index eb8bfe1c1a46..5920c60f999d 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_TOUCHSCREEN_MAX11801) += max11801_ts.o obj-$(CONFIG_TOUCHSCREEN_MC13783) += mc13783_ts.o obj-$(CONFIG_TOUCHSCREEN_MCS5000) += mcs5000_ts.o obj-$(CONFIG_TOUCHSCREEN_MIGOR) += migor_ts.o +obj-$(CONFIG_TOUCHSCREEN_MMS114) += mms114.o obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o diff --git a/drivers/input/touchscreen/mms114.c b/drivers/input/touchscreen/mms114.c new file mode 100644 index 000000000000..49c44bbf548d --- /dev/null +++ b/drivers/input/touchscreen/mms114.c @@ -0,0 +1,544 @@ +/* + * Copyright (C) 2012 Samsung Electronics Co.Ltd + * Author: Joonyoung Shim + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +/* Write only registers */ +#define MMS114_MODE_CONTROL 0x01 +#define MMS114_OPERATION_MODE_MASK 0xE +#define MMS114_ACTIVE (1 << 1) + +#define MMS114_XY_RESOLUTION_H 0x02 +#define MMS114_X_RESOLUTION 0x03 +#define MMS114_Y_RESOLUTION 0x04 +#define MMS114_CONTACT_THRESHOLD 0x05 +#define MMS114_MOVING_THRESHOLD 0x06 + +/* Read only registers */ +#define MMS114_PACKET_SIZE 0x0F +#define MMS114_INFOMATION 0x10 +#define MMS114_TSP_REV 0xF0 + +/* Minimum delay time is 50us between stop and start signal of i2c */ +#define MMS114_I2C_DELAY 50 + +/* 200ms needs after power on */ +#define MMS114_POWERON_DELAY 200 + +/* Touchscreen absolute values */ +#define MMS114_MAX_AREA 0xff + +#define MMS114_MAX_TOUCH 10 +#define MMS114_PACKET_NUM 8 + +/* Touch type */ +#define MMS114_TYPE_NONE 0 +#define MMS114_TYPE_TOUCHSCREEN 1 +#define MMS114_TYPE_TOUCHKEY 2 + +struct mms114_data { + struct i2c_client *client; + struct input_dev *input_dev; + struct regulator *core_reg; + struct regulator *io_reg; + const struct mms114_platform_data *pdata; + + /* Use cache data for mode control register(write only) */ + u8 cache_mode_control; +}; + +struct mms114_touch { + u8 id:4, reserved_bit4:1, type:2, pressed:1; + u8 x_hi:4, y_hi:4; + u8 x_lo; + u8 y_lo; + u8 width; + u8 strength; + u8 reserved[2]; +} __packed; + +static int __mms114_read_reg(struct mms114_data *data, unsigned int reg, + unsigned int len, u8 *val) +{ + struct i2c_client *client = data->client; + struct i2c_msg xfer[2]; + u8 buf = reg & 0xff; + int error; + + if (reg <= MMS114_MODE_CONTROL && reg + len > MMS114_MODE_CONTROL) + BUG(); + + /* Write register: use repeated start */ + xfer[0].addr = client->addr; + xfer[0].flags = I2C_M_TEN | I2C_M_NOSTART; + xfer[0].len = 1; + xfer[0].buf = &buf; + + /* Read data */ + xfer[1].addr = client->addr; + xfer[1].flags = I2C_M_RD; + xfer[1].len = len; + xfer[1].buf = val; + + error = i2c_transfer(client->adapter, xfer, 2); + if (error != 2) { + dev_err(&client->dev, + "%s: i2c transfer failed (%d)\n", __func__, error); + return error < 0 ? error : -EIO; + } + udelay(MMS114_I2C_DELAY); + + return 0; +} + +static int mms114_read_reg(struct mms114_data *data, unsigned int reg) +{ + u8 val; + int error; + + if (reg == MMS114_MODE_CONTROL) + return data->cache_mode_control; + + error = __mms114_read_reg(data, reg, 1, &val); + return error < 0 ? error : val; +} + +static int mms114_write_reg(struct mms114_data *data, unsigned int reg, + unsigned int val) +{ + struct i2c_client *client = data->client; + u8 buf[2]; + int error; + + buf[0] = reg & 0xff; + buf[1] = val & 0xff; + + error = i2c_master_send(client, buf, 2); + if (error != 2) { + dev_err(&client->dev, + "%s: i2c send failed (%d)\n", __func__, error); + return error < 0 ? error : -EIO; + } + udelay(MMS114_I2C_DELAY); + + if (reg == MMS114_MODE_CONTROL) + data->cache_mode_control = val; + + return 0; +} + +static void mms114_process_mt(struct mms114_data *data, struct mms114_touch *touch) +{ + const struct mms114_platform_data *pdata = data->pdata; + struct i2c_client *client = data->client; + struct input_dev *input_dev = data->input_dev; + unsigned int id; + unsigned int x; + unsigned int y; + + if (touch->id > MMS114_MAX_TOUCH) { + dev_err(&client->dev, "Wrong touch id (%d)\n", touch->id); + return; + } + + if (touch->type != MMS114_TYPE_TOUCHSCREEN) { + dev_err(&client->dev, "Wrong touch type (%d)\n", touch->type); + return; + } + + id = touch->id - 1; + x = touch->x_lo | touch->x_hi << 8; + y = touch->y_lo | touch->y_hi << 8; + if (x > pdata->x_size || y > pdata->y_size) { + dev_dbg(&client->dev, + "Wrong touch coordinates (%d, %d)\n", x, y); + return; + } + + if (pdata->x_invert) + x = pdata->x_size - x; + if (pdata->y_invert) + y = pdata->y_size - y; + + dev_dbg(&client->dev, + "id: %d, type: %d, pressed: %d, x: %d, y: %d, width: %d, strength: %d\n", + id, touch->type, touch->pressed, + x, y, touch->width, touch->strength); + + input_mt_slot(input_dev, id); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, touch->pressed); + + if (touch->pressed) { + input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, touch->width); + 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_PRESSURE, touch->strength); + } +} + +static irqreturn_t mms114_interrupt(int irq, void *dev_id) +{ + struct mms114_data *data = dev_id; + struct input_dev *input_dev = data->input_dev; + struct mms114_touch touch[MMS114_MAX_TOUCH]; + int packet_size; + int touch_size; + int index; + int error; + + mutex_lock(&input_dev->mutex); + if (!input_dev->users) { + mutex_unlock(&input_dev->mutex); + goto out; + } + mutex_unlock(&input_dev->mutex); + + packet_size = mms114_read_reg(data, MMS114_PACKET_SIZE); + if (packet_size <= 0) + goto out; + + touch_size = packet_size / MMS114_PACKET_NUM; + + error = __mms114_read_reg(data, MMS114_INFOMATION, packet_size, + (u8 *)touch); + if (error < 0) + goto out; + + for (index = 0; index < touch_size; index++) + mms114_process_mt(data, touch + index); + + input_mt_report_pointer_emulation(data->input_dev, true); + input_sync(data->input_dev); + +out: + return IRQ_HANDLED; +} + +static int mms114_set_active(struct mms114_data *data, bool active) +{ + int val; + + val = mms114_read_reg(data, MMS114_MODE_CONTROL); + if (val < 0) + return val; + + val &= ~MMS114_OPERATION_MODE_MASK; + + /* If active is false, sleep mode */ + if (active) + val |= MMS114_ACTIVE; + + return mms114_write_reg(data, MMS114_MODE_CONTROL, val); +} + +static int mms114_get_version(struct mms114_data *data) +{ + struct device *dev = &data->client->dev; + u8 buf[6]; + int error; + + error = __mms114_read_reg(data, MMS114_TSP_REV, 6, buf); + if (error < 0) + return error; + + dev_info(dev, "TSP Rev: 0x%x, HW Rev: 0x%x, Firmware Ver: 0x%x\n", + buf[0], buf[1], buf[3]); + + return 0; +} + +static int mms114_setup_regs(struct mms114_data *data) +{ + const struct mms114_platform_data *pdata = data->pdata; + int val; + int error; + + error = mms114_get_version(data); + if (error < 0) + return error; + + error = mms114_set_active(data, true); + if (error < 0) + return error; + + val = (pdata->x_size >> 8) & 0xf; + val |= ((pdata->y_size >> 8) & 0xf) << 4; + error = mms114_write_reg(data, MMS114_XY_RESOLUTION_H, val); + if (error < 0) + return error; + + val = pdata->x_size & 0xff; + error = mms114_write_reg(data, MMS114_X_RESOLUTION, val); + if (error < 0) + return error; + + val = pdata->y_size & 0xff; + error = mms114_write_reg(data, MMS114_Y_RESOLUTION, val); + if (error < 0) + return error; + + if (pdata->contact_threshold) { + error = mms114_write_reg(data, MMS114_CONTACT_THRESHOLD, + pdata->contact_threshold); + if (error < 0) + return error; + } + + if (pdata->moving_threshold) { + error = mms114_write_reg(data, MMS114_MOVING_THRESHOLD, + pdata->moving_threshold); + if (error < 0) + return error; + } + + return 0; +} + +static int mms114_start(struct mms114_data *data) +{ + struct i2c_client *client = data->client; + int error; + + if (data->core_reg) + regulator_enable(data->core_reg); + if (data->io_reg) + regulator_enable(data->io_reg); + mdelay(MMS114_POWERON_DELAY); + + error = mms114_setup_regs(data); + if (error < 0) + return error; + + if (data->pdata->cfg_pin) + data->pdata->cfg_pin(true); + + enable_irq(client->irq); + + return 0; +} + +static void mms114_stop(struct mms114_data *data) +{ + struct i2c_client *client = data->client; + + disable_irq(client->irq); + + if (data->pdata->cfg_pin) + data->pdata->cfg_pin(false); + + if (data->io_reg) + regulator_disable(data->io_reg); + if (data->core_reg) + regulator_disable(data->core_reg); +} + +static int mms114_input_open(struct input_dev *dev) +{ + struct mms114_data *data = input_get_drvdata(dev); + + return mms114_start(data); +} + +static void mms114_input_close(struct input_dev *dev) +{ + struct mms114_data *data = input_get_drvdata(dev); + + mms114_stop(data); +} + +static int __devinit mms114_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct mms114_data *data; + struct input_dev *input_dev; + int error; + + if (!client->dev.platform_data) { + dev_err(&client->dev, "Need platform data\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_PROTOCOL_MANGLING)) { + dev_err(&client->dev, + "Need i2c bus that supports protocol mangling\n"); + return -ENODEV; + } + + data = kzalloc(sizeof(struct mms114_data), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!data || !input_dev) { + dev_err(&client->dev, "Failed to allocate memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + data->client = client; + data->input_dev = input_dev; + data->pdata = client->dev.platform_data; + + input_dev->name = "MELPAS MMS114 Touchscreen"; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &client->dev; + input_dev->open = mms114_input_open; + input_dev->close = mms114_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_abs_params(input_dev, ABS_X, 0, data->pdata->x_size, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, data->pdata->y_size, 0, 0); + + /* For multi touch */ + input_mt_init_slots(input_dev, MMS114_MAX_TOUCH); + input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, + 0, MMS114_MAX_AREA, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_X, + 0, data->pdata->x_size, 0, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, + 0, data->pdata->y_size, 0, 0); + input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 255, 0, 0); + + input_set_drvdata(input_dev, data); + i2c_set_clientdata(client, data); + + data->core_reg = regulator_get(&client->dev, "avdd"); + if (IS_ERR(data->core_reg)) { + error = PTR_ERR(data->core_reg); + dev_err(&client->dev, + "Unable to get the Core regulator (%d)\n", error); + goto err_free_mem; + } + + data->io_reg = regulator_get(&client->dev, "vdd"); + if (IS_ERR(data->io_reg)) { + error = PTR_ERR(data->io_reg); + dev_err(&client->dev, + "Unable to get the IO regulator (%d)\n", error); + goto err_core_reg; + } + + error = request_threaded_irq(client->irq, NULL, mms114_interrupt, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "mms114", data); + if (error) { + dev_err(&client->dev, "Failed to register interrupt\n"); + goto err_io_reg; + } + disable_irq(client->irq); + + error = input_register_device(data->input_dev); + if (error) + goto err_free_irq; + + return 0; + +err_free_irq: + free_irq(client->irq, data); +err_io_reg: + regulator_put(data->io_reg); +err_core_reg: + regulator_put(data->core_reg); +err_free_mem: + input_free_device(input_dev); + kfree(data); + return error; +} + +static int __devexit mms114_remove(struct i2c_client *client) +{ + struct mms114_data *data = i2c_get_clientdata(client); + + free_irq(client->irq, data); + regulator_put(data->io_reg); + regulator_put(data->core_reg); + input_unregister_device(data->input_dev); + kfree(data); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int mms114_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mms114_data *data = i2c_get_clientdata(client); + struct input_dev *input_dev = data->input_dev; + int id; + + /* Release all touch */ + for (id = 0; id < MMS114_MAX_TOUCH; id++) { + input_mt_slot(input_dev, id); + input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false); + } + + input_mt_report_pointer_emulation(input_dev, true); + input_sync(input_dev); + + mutex_lock(&input_dev->mutex); + if (input_dev->users) + mms114_stop(data); + mutex_unlock(&input_dev->mutex); + + return 0; +} + +static int mms114_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mms114_data *data = i2c_get_clientdata(client); + struct input_dev *input_dev = data->input_dev; + int error; + + mutex_lock(&input_dev->mutex); + if (input_dev->users) { + error = mms114_start(data); + if (error < 0) { + mutex_unlock(&input_dev->mutex); + return error; + } + } + mutex_unlock(&input_dev->mutex); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(mms114_pm_ops, mms114_suspend, mms114_resume); + +static const struct i2c_device_id mms114_id[] = { + { "mms114", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mms114_id); + +static struct i2c_driver mms114_driver = { + .driver = { + .name = "mms114", + .owner = THIS_MODULE, + .pm = &mms114_pm_ops, + }, + .probe = mms114_probe, + .remove = __devexit_p(mms114_remove), + .id_table = mms114_id, +}; + +module_i2c_driver(mms114_driver); + +/* Module information */ +MODULE_AUTHOR("Joonyoung Shim "); +MODULE_DESCRIPTION("MELFAS mms114 Touchscreen driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/i2c/mms114.h b/include/linux/i2c/mms114.h new file mode 100644 index 000000000000..5722ebfb2738 --- /dev/null +++ b/include/linux/i2c/mms114.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2012 Samsung Electronics Co.Ltd + * Author: Joonyoung Shim + * + * 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 Foundationr + */ + +#ifndef __LINUX_MMS114_H +#define __LINUX_MMS114_H + +struct mms114_platform_data { + unsigned int x_size; + unsigned int y_size; + unsigned int contact_threshold; + unsigned int moving_threshold; + bool x_invert; + bool y_invert; + + void (*cfg_pin)(bool); +}; + +#endif /* __LINUX_MMS114_H */ -- cgit v1.2.3-59-g8ed1b