diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 3b9f2a394a90..8c2d7fc65ee6 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -598,6 +598,18 @@ config TOUCHSCREEN_WACOM_I2C To compile this driver as a module, choose M here: the module will be called wacom_i2c. +config TOUCHSCREEN_WACOM_W9013 + tristate "Wacom W9013 penabled serial touchscreen" + depends on I2C + help + Say Y here if you have an Wacom W9013 penabled serial touchscreen + connected to your system. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called wacom_w9013. + config TOUCHSCREEN_LPC32XX tristate "LPC32XX touchscreen controller" depends on ARCH_LPC32XX diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index c1c47465604f..3c6245be3c4e 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -106,6 +106,7 @@ obj-$(CONFIG_TOUCHSCREEN_TSC2007) += tsc2007.o obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001) += wacom_w8001.o obj-$(CONFIG_TOUCHSCREEN_WACOM_I2C) += wacom_i2c.o +obj-$(CONFIG_TOUCHSCREEN_WACOM_W9013) += wacom_w9013.o obj-$(CONFIG_TOUCHSCREEN_WDT87XX_I2C) += wdt87xx_i2c.o obj-$(CONFIG_TOUCHSCREEN_WM831X) += wm831x-ts.o obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o diff --git a/drivers/input/touchscreen/wacom_w9013.c b/drivers/input/touchscreen/wacom_w9013.c new file mode 100644 index 000000000000..89db5a90a7b4 --- /dev/null +++ b/drivers/input/touchscreen/wacom_w9013.c @@ -0,0 +1,471 @@ +/* + * Wacom Penabled Driver for I2C + * + * Copyright (c) 2011 - 2013 Tatsunosuke Tobita, Wacom. + * + * + * 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 of 2 of the License, + * or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +//#define ORIGIN_COORD + +static int exchange_x_y_flag = 0; +static int revert_x_flag = 0; +static int revert_y_flag = 0; + +static int screen_max_x = 20280; +static int screen_max_y = 13942; + +#define WACOM_CMD_QUERY0 0x04 +#define WACOM_CMD_QUERY1 0x00 +#define WACOM_CMD_QUERY2 0x33 +#define WACOM_CMD_QUERY3 0x02 +#define WACOM_CMD_THROW0 0x05 +#define WACOM_CMD_THROW1 0x00 +#define WACOM_QUERY_SIZE 19 + +struct wacom_features { + int x_max; + int y_max; + int pressure_max; + char fw_version; +}; + +/*HID specific register*/ +#define HID_DESC_REGISTER 1 +#define COMM_REG 0x04 +#define DATA_REG 0x05 + +typedef struct hid_descriptor { + u16 wHIDDescLength; + u16 bcdVersion; + u16 wReportDescLength; + u16 wReportDescRegister; + u16 wInputRegister; + u16 wMaxInputLength; + u16 wOutputRegister; + u16 wMaxOutputLength; + u16 wCommandRegister; + u16 wDataRegister; + u16 wVendorID; + u16 wProductID; + u16 wVersion; + u16 RESERVED_HIGH; + u16 RESERVED_LOW; +} HID_DESC; + +struct wacom_i2c { + struct wacom_features *features; + struct i2c_client *client; + struct input_dev *input; + u8 data[WACOM_QUERY_SIZE]; + bool prox; + int tool; +}; +static int get_hid_desc(struct i2c_client *client, + struct hid_descriptor *hid_desc) +{ + int ret = -1; + char cmd[] = {HID_DESC_REGISTER, 0x00}; + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = sizeof(cmd), + .buf = cmd, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = sizeof(HID_DESC), + .buf = (char *)hid_desc, + }, + }; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) + return ret; + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + printk("******************************\n"); + printk("wacom firmware vesrsion:0x%x\n", hid_desc->wVersion); + printk("******************************\n"); + + ret = 0; +//out: + return ret; +} + + +static int wacom_query_device(struct i2c_client *client, + struct wacom_features *features) +{ + int ret; + u8 cmd1[] = { WACOM_CMD_QUERY0, WACOM_CMD_QUERY1, + WACOM_CMD_QUERY2, WACOM_CMD_QUERY3 }; + u8 cmd2[] = { WACOM_CMD_THROW0, WACOM_CMD_THROW1 }; + u8 data[WACOM_QUERY_SIZE]; + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .flags = 0, + .len = sizeof(cmd1), + .buf = cmd1, + }, + { + .addr = client->addr, + .flags = 0, + .len = sizeof(cmd2), + .buf = cmd2, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = sizeof(data), + .buf = data, + }, + }; + + ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (ret < 0) + return ret; + if (ret != ARRAY_SIZE(msgs)) + return -EIO; + + features->x_max = get_unaligned_le16(&data[3]); + features->y_max = get_unaligned_le16(&data[5]); + features->pressure_max = get_unaligned_le16(&data[11]); + features->fw_version = get_unaligned_le16(&data[13]); + printk("Wacom source screen x_max:%d, y_max:%d, pressure:%d, fw:%d\n", + features->x_max, features->y_max, + features->pressure_max, features->fw_version); + + if (1 == exchange_x_y_flag) { + swap(features->x_max, features->y_max); + } + screen_max_x = features->x_max; + screen_max_y = features->y_max; + printk("Wacom desc screen x_max:%d, y_max:%d\n", features->x_max, features->y_max); + + return 0; +} + +static irqreturn_t wacom_i2c_irq(int irq, void *dev_id) +{ + struct wacom_i2c *wac_i2c = dev_id; + struct input_dev *input = wac_i2c->input; + //struct wacom_features *features = wac_i2c->features; + u8 *data = wac_i2c->data; + unsigned int x, y, pressure; + unsigned char tsw, f1, f2, ers; + int error; + + error = i2c_master_recv(wac_i2c->client, + wac_i2c->data, sizeof(wac_i2c->data)); + if (error < 0) + goto out; + + tsw = data[3] & 0x01; + ers = data[3] & 0x04; + f1 = data[3] & 0x02; + f2 = data[3] & 0x10; + x = le16_to_cpup((__le16 *)&data[4]); + y = le16_to_cpup((__le16 *)&data[6]); + pressure = le16_to_cpup((__le16 *)&data[8]); + + if (!wac_i2c->prox) + wac_i2c->tool = (data[3] & 0x0c) ? + BTN_TOOL_RUBBER : BTN_TOOL_PEN; + + wac_i2c->prox = data[3] & 0x20; + + if (1 == exchange_x_y_flag) { + swap(x, y); + } + if (1 == revert_x_flag) { + x = screen_max_x - x; + } + if (1 == revert_y_flag) { + y = screen_max_y - y; + } + + input_report_key(input, BTN_TOUCH, tsw || ers); + input_report_key(input, wac_i2c->tool, wac_i2c->prox); + input_report_key(input, BTN_STYLUS, f1); + input_report_key(input, BTN_STYLUS2, f2); + input_report_abs(input, ABS_X, x); + input_report_abs(input, ABS_Y, y); + input_report_abs(input, ABS_PRESSURE, pressure); + input_sync(input); + +out: + return IRQ_HANDLED; +} + +static int wacom_i2c_open(struct input_dev *dev) +{ + struct wacom_i2c *wac_i2c = input_get_drvdata(dev); + struct i2c_client *client = wac_i2c->client; + + enable_irq(client->irq); + + return 0; +} + +static void wacom_i2c_close(struct input_dev *dev) +{ + struct wacom_i2c *wac_i2c = input_get_drvdata(dev); + struct i2c_client *client = wac_i2c->client; + + disable_irq(client->irq); +} + +static int g_irq_gpio = -1; +static int wacom_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct wacom_i2c *wac_i2c; + struct input_dev *input; + struct wacom_features features = { 0 }; + HID_DESC hid_desc = { 0 }; + struct device_node *wac_np; + int reset_gpio, irq_gpio = -1, pen_detect_gpio; + int error; + + printk("wacom_i2c_probe\n"); + wac_np = client->dev.of_node; + if (!wac_np) { + dev_err(&client->dev, "get device node error\n"); + return -ENODEV; + } + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "i2c_check_functionality error\n"); + return -EIO; + } + of_property_read_u32(wac_np, "revert_x", &revert_x_flag); + of_property_read_u32(wac_np, "revert_y", &revert_y_flag); + of_property_read_u32(wac_np, "xy_exchange", &exchange_x_y_flag); + + error = wacom_query_device(client, &features); + if (error) + return error; + + error = get_hid_desc(client, &hid_desc); + if (error) + return error; + + + wac_i2c = kzalloc(sizeof(*wac_i2c), GFP_KERNEL); + input = input_allocate_device(); + if (!wac_i2c || !input) { + error = -ENOMEM; + goto err_free_mem; + } + + reset_gpio = of_get_named_gpio(wac_np, "gpio_rst", 0); + if (!gpio_is_valid(reset_gpio)) { + dev_err(&client->dev, "no gpio_rst pin available\n"); + goto err_free_mem; + } + + error = devm_gpio_request_one(&client->dev, reset_gpio, GPIOF_OUT_INIT_LOW, "gpio-rst"); + if (error < 0) { + goto err_free_mem; + } + gpio_direction_output(reset_gpio, 0); + msleep(100); + gpio_direction_output(reset_gpio, 1); + + pen_detect_gpio = of_get_named_gpio(wac_np, "gpio_detect", 0); + if (!gpio_is_valid(pen_detect_gpio)) { + dev_err(&client->dev, "no pen_detect_gpio pin available\n"); + goto err_free_reset_gpio; + } + error = devm_gpio_request_one(&client->dev, pen_detect_gpio, GPIOF_IN, "gpio_detect"); + if (error < 0) { + goto err_free_reset_gpio; + } + + irq_gpio = of_get_named_gpio(wac_np, "gpio_intr", 0); + if (!gpio_is_valid(irq_gpio)) { + dev_err(&client->dev, "no gpio_intr pin available\n"); + goto err_free_pen_detect_gpio; + } + g_irq_gpio = irq_gpio; +#if 1 + error = devm_gpio_request_one(&client->dev, irq_gpio, GPIOF_IN, "gpio_intr"); + if (error < 0) { + goto err_free_pen_detect_gpio; + } +#endif + client->irq = gpio_to_irq(irq_gpio); + //printk("wacom_i2c_probe irq=%d, irq_gpio=%d\n",client->irq, irq_gpio); + if (client->irq < 0) { + dev_err(&client->dev, "Unable to get irq number for GPIO %d, error %d\n", irq_gpio, client->irq); + goto err_free_irq_gpio; + } + + wac_i2c->features = &features; + wac_i2c->client = client; + wac_i2c->input = input; + + input->name = "Wacom I2C Digitizer"; + input->id.bustype = BUS_I2C; + input->id.vendor = 0x56a; + //input->id.version = features.fw_version; + input->id.version = hid_desc.wVersion; + + input->dev.parent = &client->dev; + input->open = wacom_i2c_open; + input->close = wacom_i2c_close; + + input->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + + __set_bit(BTN_TOOL_PEN, input->keybit); + __set_bit(BTN_TOOL_RUBBER, input->keybit); + __set_bit(BTN_STYLUS, input->keybit); + __set_bit(BTN_STYLUS2, input->keybit); + __set_bit(BTN_TOUCH, input->keybit); + __set_bit(INPUT_PROP_DIRECT, input->propbit); + + input_set_abs_params(input, ABS_X, 0, features.x_max, 0, 0); + input_set_abs_params(input, ABS_Y, 0, features.y_max, 0, 0); + input_set_abs_params(input, ABS_PRESSURE, 0, features.pressure_max, 0, 0); + + input_set_drvdata(input, wac_i2c); + + error = request_threaded_irq(client->irq, NULL, wacom_i2c_irq, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "wacom", wac_i2c); + if (error) { + dev_err(&client->dev, + "Failed to enable IRQ, error: %d\n", error); + goto err_free_mem; + } + + /* Disable the IRQ, we'll enable it in wac_i2c_open() */ + disable_irq(client->irq); + + error = input_register_device(wac_i2c->input); + if (error) { + dev_err(&client->dev, + "Failed to register input device, error: %d\n", error); + goto err_free_irq; + } + + i2c_set_clientdata(client, wac_i2c); + return 0; + +err_free_irq: + free_irq(client->irq, wac_i2c); +err_free_reset_gpio: +err_free_pen_detect_gpio: +err_free_irq_gpio: +err_free_mem: + input_free_device(input); + kfree(wac_i2c); + + return error; +} + +static int wacom_i2c_remove(struct i2c_client *client) +{ + struct wacom_i2c *wac_i2c = i2c_get_clientdata(client); + + free_irq(client->irq, wac_i2c); + input_unregister_device(wac_i2c->input); + kfree(wac_i2c); + + return 0; +} + +static int __maybe_unused wacom_i2c_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + disable_irq(client->irq); + + return 0; +} + +static int __maybe_unused wacom_i2c_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + enable_irq(client->irq); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(wacom_i2c_pm, wacom_i2c_suspend, wacom_i2c_resume); + +static const struct i2c_device_id wacom_i2c_id[] = { + { "wacom", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, wacom_i2c_id); + +static const struct of_device_id wacom_dt_ids[] = { + { + .compatible = "wacom,w9013", + .data = (void *) &wacom_i2c_id[0], + }, { + /* sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, wacom_dt_ids); + +static struct i2c_driver wacom_i2c_driver = { + .driver = { + .name = "wacom", + .owner = THIS_MODULE, + .of_match_table = wacom_dt_ids, + .pm = &wacom_i2c_pm, + }, + + .probe = wacom_i2c_probe, + .remove = wacom_i2c_remove, + .id_table = wacom_i2c_id, +}; + +static int __init wacom_init(void) +{ + return i2c_add_driver(&wacom_i2c_driver); +} + +static void __exit wacom_exit(void) +{ + i2c_del_driver(&wacom_i2c_driver); +} + +/* + * Module entry points + */ +subsys_initcall(wacom_init); +//late_initcall(wacom_init); +module_exit(wacom_exit); + +//module_i2c_driver(wacom_i2c_driver); + +MODULE_AUTHOR("Tatsunosuke Tobita "); +MODULE_DESCRIPTION("WACOM EMR I2C Driver"); +MODULE_LICENSE("GPL");