diff options
Diffstat (limited to 'drivers/input/touchscreen')
-rw-r--r-- | drivers/input/touchscreen/Kconfig | 43 | ||||
-rw-r--r-- | drivers/input/touchscreen/Makefile | 3 | ||||
-rw-r--r-- | drivers/input/touchscreen/ar1021_i2c.c | 2 | ||||
-rw-r--r-- | drivers/input/touchscreen/atmel_mxt_ts.c | 532 | ||||
-rw-r--r-- | drivers/input/touchscreen/bcm_iproc_tsc.c | 522 | ||||
-rw-r--r-- | drivers/input/touchscreen/chipone_icn8318.c | 316 | ||||
-rw-r--r-- | drivers/input/touchscreen/edt-ft5x06.c | 6 | ||||
-rw-r--r-- | drivers/input/touchscreen/elants_i2c.c | 14 | ||||
-rw-r--r-- | drivers/input/touchscreen/goodix.c | 36 | ||||
-rw-r--r-- | drivers/input/touchscreen/of_touchscreen.c | 62 | ||||
-rw-r--r-- | drivers/input/touchscreen/sun4i-ts.c | 46 | ||||
-rw-r--r-- | drivers/input/touchscreen/sur40.c | 434 | ||||
-rw-r--r-- | drivers/input/touchscreen/sx8654.c | 286 | ||||
-rw-r--r-- | drivers/input/touchscreen/tsc2007.c | 11 | ||||
-rw-r--r-- | drivers/input/touchscreen/usbtouchscreen.c | 11 |
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 = ® + }, + { + .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 |