From 11601a82d569267b016f808393bb8c26f855a7ea Mon Sep 17 00:00:00 2001 From: Jakub Bogusz Date: Mon, 27 Jan 2014 17:21:32 -0800 Subject: Input: wistron_btns - add FS AMILO Pro 8210 support This adds Fujitsu-Siemens AMILO Pro 8210 support to wistron_btns driver. Functions are very similar to already supported AMILO Pro 3505, but 8210 has WIFI led. Such functionality is needed to enable WiFi under Linux on 8210 when it cold boots with hardware rfkill enabled, without booting another operating system or running custom utility that calls appropriate BIOS function. Signed-off-by: Jakub Bogusz Signed-off-by: Dmitry Torokhov --- drivers/input/misc/wistron_btns.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/input/misc/wistron_btns.c b/drivers/input/misc/wistron_btns.c index b6505454bcc4..7b7add5061a5 100644 --- a/drivers/input/misc/wistron_btns.c +++ b/drivers/input/misc/wistron_btns.c @@ -277,6 +277,16 @@ static struct key_entry keymap_fs_amilo_pro_v3505[] __initdata = { { KE_END, 0 } }; +static struct key_entry keymap_fs_amilo_pro_v8210[] __initdata = { + { KE_KEY, 0x01, {KEY_HELP} }, /* Fn+F1 */ + { KE_KEY, 0x06, {KEY_DISPLAYTOGGLE} }, /* Fn+F4 */ + { KE_BLUETOOTH, 0x30 }, /* Fn+F10 */ + { KE_KEY, 0x31, {KEY_MAIL} }, /* mail button */ + { KE_KEY, 0x36, {KEY_WWW} }, /* www button */ + { KE_WIFI, 0x78 }, /* satelite dish button */ + { KE_END, FE_WIFI_LED } +}; + static struct key_entry keymap_fujitsu_n3510[] __initdata = { { KE_KEY, 0x11, {KEY_PROG1} }, { KE_KEY, 0x12, {KEY_PROG2} }, @@ -653,6 +663,15 @@ static const struct dmi_system_id dmi_ids[] __initconst = { }, .driver_data = keymap_fs_amilo_pro_v3505 }, + { + /* Fujitsu-Siemens Amilo Pro Edition V8210 */ + .callback = dmi_matched, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro Series V8210"), + }, + .driver_data = keymap_fs_amilo_pro_v8210 + }, { /* Fujitsu-Siemens Amilo M7400 */ .callback = dmi_matched, -- cgit v1.2.3 From 1f906f8376c2d53dca347b15c0caef71d91087b4 Mon Sep 17 00:00:00 2001 From: Alexey Khoroshilov Date: Mon, 27 Jan 2014 12:25:47 -0800 Subject: Input: gtco - fix usb_dev leak There is usb_get_dev() in gtco_probe(), but there is no usb_put_dev() anywhere in the driver. As pointed out by Dmitry Torokhov: The lifetime of gtco structure is already directly tied to lifetime of usb_dev: when destroying usb_dev driver core will call remove() function of currently bound driver (in our case gtco) which will destroy gtco memory. Taking additional reference is not needed here. Found by Linux Driver Verification project (linuxtesting.org). Signed-off-by: Alexey Khoroshilov Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/gtco.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/tablet/gtco.c b/drivers/input/tablet/gtco.c index caecffe8caff..858045694e9d 100644 --- a/drivers/input/tablet/gtco.c +++ b/drivers/input/tablet/gtco.c @@ -848,7 +848,7 @@ static int gtco_probe(struct usb_interface *usbinterface, gtco->inputdevice = input_dev; /* Save interface information */ - gtco->usbdev = usb_get_dev(interface_to_usbdev(usbinterface)); + gtco->usbdev = interface_to_usbdev(usbinterface); gtco->intf = usbinterface; /* Allocate some data for incoming reports */ -- cgit v1.2.3 From deb4981985ff2626a449cdeff2beaeb21986cfce Mon Sep 17 00:00:00 2001 From: Luis Ortega Date: Mon, 27 Jan 2014 12:27:06 -0800 Subject: Input: zforce - fix spelling errors Fixed a few spelling errors. Signed-off-by: Luis Ortega Acked-by: Heiko Stuebner Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/zforce_ts.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/input/touchscreen/zforce_ts.c b/drivers/input/touchscreen/zforce_ts.c index 4ffe4cc3b234..9ce551fea97b 100644 --- a/drivers/input/touchscreen/zforce_ts.c +++ b/drivers/input/touchscreen/zforce_ts.c @@ -64,7 +64,7 @@ #define RESPONSE_STATUS 0X1e /* - * Notifications are send by the touch controller without + * Notifications are sent by the touch controller without * being requested by the driver and include for example * touch indications */ @@ -103,8 +103,8 @@ struct zforce_point { * @suspended device suspended * @access_mutex serialize i2c-access, to keep multipart reads together * @command_done completion to wait for the command result - * @command_mutex serialize commands send to the ic - * @command_waiting the id of the command that that is currently waiting + * @command_mutex serialize commands sent to the ic + * @command_waiting the id of the command that is currently waiting * for a result * @command_result returned result of the command */ @@ -332,7 +332,7 @@ static int zforce_touch_event(struct zforce_ts *ts, u8 *payload) count = payload[0]; if (count > ZFORCE_REPORT_POINTS) { - dev_warn(&client->dev, "to many coordinates %d, expected max %d\n", + dev_warn(&client->dev, "too many coordinates %d, expected max %d\n", count, ZFORCE_REPORT_POINTS); count = ZFORCE_REPORT_POINTS; } @@ -789,7 +789,7 @@ static int zforce_probe(struct i2c_client *client, return ret; } - /* this gets the firmware version among other informations */ + /* this gets the firmware version among other information */ ret = zforce_command_wait(ts, COMMAND_STATUS); if (ret < 0) { dev_err(&client->dev, "couldn't get status, %d\n", ret); -- cgit v1.2.3 From ad697b96e67dc34ef69e3358c331c69dcd283c0d Mon Sep 17 00:00:00 2001 From: Luis Ortega Date: Mon, 27 Jan 2014 12:27:35 -0800 Subject: Input: zforce - fix lines exceeding 80 columns Fixed lines exceeding 80 characters long wherever possible, as per the coding style. Signed-off-by: Luis Ortega Acked-by: Heiko Stuebner Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/zforce_ts.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/input/touchscreen/zforce_ts.c b/drivers/input/touchscreen/zforce_ts.c index 9ce551fea97b..e87888c229ab 100644 --- a/drivers/input/touchscreen/zforce_ts.c +++ b/drivers/input/touchscreen/zforce_ts.c @@ -235,7 +235,8 @@ static int zforce_scan_frequency(struct zforce_ts *ts, u16 idle, u16 finger, (finger & 0xff), ((finger >> 8) & 0xff), (stylus & 0xff), ((stylus >> 8) & 0xff) }; - dev_dbg(&client->dev, "set scan frequency to (idle: %d, finger: %d, stylus: %d)\n", + dev_dbg(&client->dev, + "set scan frequency to (idle: %d, finger: %d, stylus: %d)\n", idle, finger, stylus); return zforce_send_wait(ts, &buf[0], ARRAY_SIZE(buf)); @@ -332,7 +333,8 @@ static int zforce_touch_event(struct zforce_ts *ts, u8 *payload) count = payload[0]; if (count > ZFORCE_REPORT_POINTS) { - dev_warn(&client->dev, "too many coordinates %d, expected max %d\n", + dev_warn(&client->dev, + "too many coordinates %d, expected max %d\n", count, ZFORCE_REPORT_POINTS); count = ZFORCE_REPORT_POINTS; } @@ -485,8 +487,8 @@ static irqreturn_t zforce_interrupt(int irq, void *dev_id) while (!gpio_get_value(pdata->gpio_int)) { ret = zforce_read_packet(ts, payload_buffer); if (ret < 0) { - dev_err(&client->dev, "could not read packet, ret: %d\n", - ret); + dev_err(&client->dev, + "could not read packet, ret: %d\n", ret); break; } @@ -530,7 +532,8 @@ static irqreturn_t zforce_interrupt(int irq, void *dev_id) payload[RESPONSE_DATA + 4]; ts->version_rev = (payload[RESPONSE_DATA + 7] << 8) | payload[RESPONSE_DATA + 6]; - dev_dbg(&ts->client->dev, "Firmware Version %04x:%04x %04x:%04x\n", + dev_dbg(&ts->client->dev, + "Firmware Version %04x:%04x %04x:%04x\n", ts->version_major, ts->version_minor, ts->version_build, ts->version_rev); @@ -543,7 +546,8 @@ static irqreturn_t zforce_interrupt(int irq, void *dev_id) break; default: - dev_err(&ts->client->dev, "unrecognized response id: 0x%x\n", + dev_err(&ts->client->dev, + "unrecognized response id: 0x%x\n", payload[RESPONSE_ID]); break; } @@ -609,7 +613,8 @@ static int zforce_suspend(struct device *dev) enable_irq_wake(client->irq); } else if (input->users) { - dev_dbg(&client->dev, "suspend without being a wakeup source\n"); + dev_dbg(&client->dev, + "suspend without being a wakeup source\n"); ret = zforce_stop(ts); if (ret) -- cgit v1.2.3 From 5aee41a60c4d53f65918c1ec0ab94c245f2c08af Mon Sep 17 00:00:00 2001 From: Luis Ortega Date: Mon, 27 Jan 2014 12:27:49 -0800 Subject: Input: zforce - remove unnecessary payload data checks The function zforce_read_packet() reads 2 values (bytes) of payload header, validates them and then proceeds to read the payload body. The function stores all these in a u8 buffer. The PAYLOAD_LENGTH check seems to be trying to detect an overflow error. However, since we are just reading a u8 value from the buffer, these checks are unnecessary and we should simply compare against zero. Signed-off-by: Luis Ortega Acked-by: Heiko Stuebner Tested-by: Heiko Stuebner - bq Cervantes (imx6sl) Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/zforce_ts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/zforce_ts.c b/drivers/input/touchscreen/zforce_ts.c index e87888c229ab..3ed024985a58 100644 --- a/drivers/input/touchscreen/zforce_ts.c +++ b/drivers/input/touchscreen/zforce_ts.c @@ -423,7 +423,7 @@ static int zforce_read_packet(struct zforce_ts *ts, u8 *buf) goto unlock; } - if (buf[PAYLOAD_LENGTH] <= 0 || buf[PAYLOAD_LENGTH] > 255) { + if (buf[PAYLOAD_LENGTH] == 0) { dev_err(&client->dev, "invalid payload length: %d\n", buf[PAYLOAD_LENGTH]); ret = -EIO; -- cgit v1.2.3 From d333b6062f1414622d1fe37314fa2ec9e3df402d Mon Sep 17 00:00:00 2001 From: Luis Ortega Date: Mon, 27 Jan 2014 12:28:33 -0800 Subject: Input: zforce - reduce stack memory allocated to frames A frame is a u8 array with the following structure: [PAYLOAD_HEADER, PAYLOAD_LENGTH, ...PAYLOAD_BODY...] PAYLOAD_BODY can be at most 255 bytes long, as it's size is represented by PAYLOAD_LENGTH. Therefore we can reduce the stack memory allocated to payload_buffer[] roughly by half, from 512 to 257 bytes. Signed-off-by: Luis Ortega Acked-by: Heiko Stuebner Tested-by: Heiko Stuebner - bq Cervantes (imx6sl) Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/zforce_ts.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/input/touchscreen/zforce_ts.c b/drivers/input/touchscreen/zforce_ts.c index 3ed024985a58..307f582eeabf 100644 --- a/drivers/input/touchscreen/zforce_ts.c +++ b/drivers/input/touchscreen/zforce_ts.c @@ -33,6 +33,7 @@ #define WAIT_TIMEOUT msecs_to_jiffies(1000) #define FRAME_START 0xee +#define FRAME_MAXSIZE 257 /* Offsets of the different parts of the payload the controller sends */ #define PAYLOAD_HEADER 0 @@ -464,7 +465,7 @@ static irqreturn_t zforce_interrupt(int irq, void *dev_id) struct i2c_client *client = ts->client; const struct zforce_ts_platdata *pdata = dev_get_platdata(&client->dev); int ret; - u8 payload_buffer[512]; + u8 payload_buffer[FRAME_MAXSIZE]; u8 *payload; /* -- cgit v1.2.3 From 8727336481a231ee986c214dde06338e7e52624c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Heiko=20St=C3=BCbner?= Date: Mon, 27 Jan 2014 12:32:38 -0800 Subject: Input: zforce - use internal pdata pointer instead of dev_get_platdata Devicetree support will be creating its own platfprm data structure that is not attached to the device. Let's use the internal pointer to the pdata instead of re-fetching it with dev_get_platdata(). Signed-off-by: Heiko Stuebner Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/zforce_ts.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/input/touchscreen/zforce_ts.c b/drivers/input/touchscreen/zforce_ts.c index 307f582eeabf..91f4c7cb5dd5 100644 --- a/drivers/input/touchscreen/zforce_ts.c +++ b/drivers/input/touchscreen/zforce_ts.c @@ -257,7 +257,7 @@ static int zforce_setconfig(struct zforce_ts *ts, char b1) static int zforce_start(struct zforce_ts *ts) { struct i2c_client *client = ts->client; - const struct zforce_ts_platdata *pdata = dev_get_platdata(&client->dev); + const struct zforce_ts_platdata *pdata = ts->pdata; int ret; dev_dbg(&client->dev, "starting device\n"); @@ -328,7 +328,7 @@ static int zforce_stop(struct zforce_ts *ts) static int zforce_touch_event(struct zforce_ts *ts, u8 *payload) { struct i2c_client *client = ts->client; - const struct zforce_ts_platdata *pdata = dev_get_platdata(&client->dev); + const struct zforce_ts_platdata *pdata = ts->pdata; struct zforce_point point; int count, i, num = 0; @@ -463,7 +463,7 @@ static irqreturn_t zforce_interrupt(int irq, void *dev_id) { struct zforce_ts *ts = dev_id; struct i2c_client *client = ts->client; - const struct zforce_ts_platdata *pdata = dev_get_platdata(&client->dev); + const struct zforce_ts_platdata *pdata = ts->pdata; int ret; u8 payload_buffer[FRAME_MAXSIZE]; u8 *payload; -- cgit v1.2.3 From 6f2e6c9b451b907b693b7fe3db3792e57796ffea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Heiko=20St=C3=BCbner?= Date: Mon, 27 Jan 2014 12:37:21 -0800 Subject: Input: zforce - add devicetree support This makes the zforce driver usable on devicetree-based platforms too. Signed-off-by: Heiko Stuebner Signed-off-by: Dmitry Torokhov --- .../bindings/input/touchscreen/zforce_ts.txt | 30 ++++++++++++ drivers/input/touchscreen/zforce_ts.c | 57 +++++++++++++++++++++- 2 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 Documentation/devicetree/bindings/input/touchscreen/zforce_ts.txt diff --git a/Documentation/devicetree/bindings/input/touchscreen/zforce_ts.txt b/Documentation/devicetree/bindings/input/touchscreen/zforce_ts.txt new file mode 100644 index 000000000000..2faf1f1fa39e --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/zforce_ts.txt @@ -0,0 +1,30 @@ +* Neonode infrared touchscreen controller + +Required properties: +- compatible: must be "neonode,zforce" +- reg: I2C address of the chip +- interrupts: interrupt to which the chip is connected +- gpios: gpios the chip is connected to + first one is the interrupt gpio and second one the reset gpio +- x-size: horizontal resolution of touchscreen +- y-size: vertical resolution of touchscreen + +Example: + + i2c@00000000 { + /* ... */ + + zforce_ts@50 { + compatible = "neonode,zforce"; + reg = <0x50>; + interrupts = <2 0>; + + gpios = <&gpio5 6 0>, /* INT */ + <&gpio5 9 0>; /* RST */ + + x-size = <800>; + y-size = <600>; + }; + + /* ... */ + }; diff --git a/drivers/input/touchscreen/zforce_ts.c b/drivers/input/touchscreen/zforce_ts.c index 91f4c7cb5dd5..bdc936cb8445 100644 --- a/drivers/input/touchscreen/zforce_ts.c +++ b/drivers/input/touchscreen/zforce_ts.c @@ -29,6 +29,8 @@ #include #include #include +#include +#include #define WAIT_TIMEOUT msecs_to_jiffies(1000) @@ -681,6 +683,45 @@ static void zforce_reset(void *data) gpio_set_value(ts->pdata->gpio_rst, 0); } +static struct zforce_ts_platdata *zforce_parse_dt(struct device *dev) +{ + struct zforce_ts_platdata *pdata; + struct device_node *np = dev->of_node; + + if (!np) + return ERR_PTR(-ENOENT); + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + dev_err(dev, "failed to allocate platform data\n"); + return ERR_PTR(-ENOMEM); + } + + pdata->gpio_int = of_get_gpio(np, 0); + if (!gpio_is_valid(pdata->gpio_int)) { + dev_err(dev, "failed to get interrupt gpio\n"); + return ERR_PTR(-EINVAL); + } + + pdata->gpio_rst = of_get_gpio(np, 1); + if (!gpio_is_valid(pdata->gpio_rst)) { + dev_err(dev, "failed to get reset gpio\n"); + return ERR_PTR(-EINVAL); + } + + if (of_property_read_u32(np, "x-size", &pdata->x_max)) { + dev_err(dev, "failed to get x-size property\n"); + return ERR_PTR(-EINVAL); + } + + if (of_property_read_u32(np, "y-size", &pdata->y_max)) { + dev_err(dev, "failed to get y-size property\n"); + return ERR_PTR(-EINVAL); + } + + return pdata; +} + static int zforce_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -689,8 +730,11 @@ static int zforce_probe(struct i2c_client *client, struct input_dev *input_dev; int ret; - if (!pdata) - return -EINVAL; + if (!pdata) { + pdata = zforce_parse_dt(&client->dev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + } ts = devm_kzalloc(&client->dev, sizeof(struct zforce_ts), GFP_KERNEL); if (!ts) @@ -826,11 +870,20 @@ static struct i2c_device_id zforce_idtable[] = { }; MODULE_DEVICE_TABLE(i2c, zforce_idtable); +#ifdef CONFIG_OF +static struct of_device_id zforce_dt_idtable[] = { + { .compatible = "neonode,zforce" }, + {}, +}; +MODULE_DEVICE_TABLE(of, zforce_dt_idtable); +#endif + static struct i2c_driver zforce_driver = { .driver = { .owner = THIS_MODULE, .name = "zforce-ts", .pm = &zforce_pm_ops, + .of_match_table = of_match_ptr(zforce_dt_idtable), }, .probe = zforce_probe, .id_table = zforce_idtable, -- cgit v1.2.3 From 9d51e801dba0c79ae979ef2f6928e402eb41009b Mon Sep 17 00:00:00 2001 From: Benjamin Tisssoires Date: Thu, 30 Jan 2014 17:16:36 -0800 Subject: Input: uinput - breaks by goto out in uinput_ioctl_handler The current implementation prevents us to add variable-length ioctl. Use a bunch of gotos instead of break to allow us to do so. No functional changes. Signed-off-by: Benjamin Tisssoires Reviewed-by: David Herrmann Signed-off-by: Dmitry Torokhov --- drivers/input/misc/uinput.c | 56 ++++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index 772835938a52..d8ae08d12abf 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -693,51 +693,51 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd, switch (cmd) { case UI_DEV_CREATE: retval = uinput_create_device(udev); - break; + goto out; case UI_DEV_DESTROY: uinput_destroy_device(udev); - break; + goto out; case UI_SET_EVBIT: retval = uinput_set_bit(arg, evbit, EV_MAX); - break; + goto out; case UI_SET_KEYBIT: retval = uinput_set_bit(arg, keybit, KEY_MAX); - break; + goto out; case UI_SET_RELBIT: retval = uinput_set_bit(arg, relbit, REL_MAX); - break; + goto out; case UI_SET_ABSBIT: retval = uinput_set_bit(arg, absbit, ABS_MAX); - break; + goto out; case UI_SET_MSCBIT: retval = uinput_set_bit(arg, mscbit, MSC_MAX); - break; + goto out; case UI_SET_LEDBIT: retval = uinput_set_bit(arg, ledbit, LED_MAX); - break; + goto out; case UI_SET_SNDBIT: retval = uinput_set_bit(arg, sndbit, SND_MAX); - break; + goto out; case UI_SET_FFBIT: retval = uinput_set_bit(arg, ffbit, FF_MAX); - break; + goto out; case UI_SET_SWBIT: retval = uinput_set_bit(arg, swbit, SW_MAX); - break; + goto out; case UI_SET_PROPBIT: retval = uinput_set_bit(arg, propbit, INPUT_PROP_MAX); - break; + goto out; case UI_SET_PHYS: if (udev->state == UIST_CREATED) { @@ -753,18 +753,18 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd, kfree(udev->dev->phys); udev->dev->phys = phys; - break; + goto out; case UI_BEGIN_FF_UPLOAD: retval = uinput_ff_upload_from_user(p, &ff_up); if (retval) - break; + goto out; req = uinput_request_find(udev, ff_up.request_id); if (!req || req->code != UI_FF_UPLOAD || !req->u.upload.effect) { retval = -EINVAL; - break; + goto out; } ff_up.retval = 0; @@ -775,65 +775,63 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd, memset(&ff_up.old, 0, sizeof(struct ff_effect)); retval = uinput_ff_upload_to_user(p, &ff_up); - break; + goto out; case UI_BEGIN_FF_ERASE: if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) { retval = -EFAULT; - break; + goto out; } req = uinput_request_find(udev, ff_erase.request_id); if (!req || req->code != UI_FF_ERASE) { retval = -EINVAL; - break; + goto out; } ff_erase.retval = 0; ff_erase.effect_id = req->u.effect_id; if (copy_to_user(p, &ff_erase, sizeof(ff_erase))) { retval = -EFAULT; - break; + goto out; } - break; + goto out; case UI_END_FF_UPLOAD: retval = uinput_ff_upload_from_user(p, &ff_up); if (retval) - break; + goto out; req = uinput_request_find(udev, ff_up.request_id); if (!req || req->code != UI_FF_UPLOAD || !req->u.upload.effect) { retval = -EINVAL; - break; + goto out; } req->retval = ff_up.retval; uinput_request_done(udev, req); - break; + goto out; case UI_END_FF_ERASE: if (copy_from_user(&ff_erase, p, sizeof(ff_erase))) { retval = -EFAULT; - break; + goto out; } req = uinput_request_find(udev, ff_erase.request_id); if (!req || req->code != UI_FF_ERASE) { retval = -EINVAL; - break; + goto out; } req->retval = ff_erase.retval; uinput_request_done(udev, req); - break; - - default: - retval = -EINVAL; + goto out; } + retval = -EINVAL; out: mutex_unlock(&udev->mutex); return retval; -- cgit v1.2.3 From e3480a61fca72d40d6dc4baaf37e94fcbfa95e19 Mon Sep 17 00:00:00 2001 From: Benjamin Tissoires Date: Thu, 30 Jan 2014 17:20:24 -0800 Subject: Input: uinput - add UI_GET_SYSNAME ioctl to retrieve the sysfs path uinput is used in the xorg-integration-tests suite and in the wayland test suite. These automated tests suites create many virtual input devices and then hook something to read these newly created devices. Currently, uinput does not provide the created input device, which means that we rely on an heuristic to guess which input node was created. The problem is that is heuristic is subjected to races between different uinput devices or even with physical devices. Having a way to retrieve the sysfs path allows us to find without any doubts the event node. Signed-off-by: Benjamin Tissoires Reviewed-by: David Herrmann Signed-off-by: Dmitry Torokhov --- drivers/input/misc/uinput.c | 43 +++++++++++++++++++++++++++++++++++++++++++ include/linux/uinput.h | 2 ++ include/uapi/linux/uinput.h | 13 ++++++++++++- 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index d8ae08d12abf..856936247500 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -20,6 +20,8 @@ * Author: Aristeu Sergio Rozanski Filho * * Changes/Revisions: + * 0.4 01/09/2014 (Benjamin Tissoires ) + * - add UI_GET_SYSNAME ioctl * 0.3 09/04/2006 (Anssi Hannula ) * - updated ff support for the changes in kernel interface * - added MODULE_VERSION @@ -670,6 +672,31 @@ static int uinput_ff_upload_from_user(const char __user *buffer, __ret; \ }) +static int uinput_str_to_user(void __user *dest, const char *str, + unsigned int maxlen) +{ + char __user *p = dest; + int len, ret; + + if (!str) + return -ENOENT; + + if (maxlen == 0) + return -EINVAL; + + len = strlen(str) + 1; + if (len > maxlen) + len = maxlen; + + ret = copy_to_user(p, str, len); + if (ret) + return -EFAULT; + + /* force terminating '\0' */ + ret = put_user(0, p + len - 1); + return ret ? -EFAULT : len; +} + static long uinput_ioctl_handler(struct file *file, unsigned int cmd, unsigned long arg, void __user *p) { @@ -679,6 +706,8 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd, struct uinput_ff_erase ff_erase; struct uinput_request *req; char *phys; + const char *name; + unsigned int size; retval = mutex_lock_interruptible(&udev->mutex); if (retval) @@ -831,6 +860,20 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd, goto out; } + size = _IOC_SIZE(cmd); + + /* Now check variable-length commands */ + switch (cmd & ~IOCSIZE_MASK) { + case UI_GET_SYSNAME(0): + if (udev->state != UIST_CREATED) { + retval = -ENOENT; + goto out; + } + name = dev_name(&udev->dev->dev); + retval = uinput_str_to_user(p, name, size); + goto out; + } + retval = -EINVAL; out: mutex_unlock(&udev->mutex); diff --git a/include/linux/uinput.h b/include/linux/uinput.h index 0a4487d3fb1e..0994c0d01a09 100644 --- a/include/linux/uinput.h +++ b/include/linux/uinput.h @@ -20,6 +20,8 @@ * Author: Aristeu Sergio Rozanski Filho * * Changes/Revisions: + * 0.4 01/09/2014 (Benjamin Tissoires ) + * - add UI_GET_SYSNAME ioctl * 0.3 24/05/2006 (Anssi Hannula ) * - update ff support for the changes in kernel interface * - add UINPUT_VERSION diff --git a/include/uapi/linux/uinput.h b/include/uapi/linux/uinput.h index fe46431593f9..0389b489bbba 100644 --- a/include/uapi/linux/uinput.h +++ b/include/uapi/linux/uinput.h @@ -20,6 +20,8 @@ * Author: Aristeu Sergio Rozanski Filho * * Changes/Revisions: + * 0.4 01/09/2014 (Benjamin Tissoires ) + * - add UI_GET_SYSNAME ioctl * 0.3 24/05/2006 (Anssi Hannula ) * - update ff support for the changes in kernel interface * - add UINPUT_VERSION @@ -35,7 +37,7 @@ #include #include -#define UINPUT_VERSION 3 +#define UINPUT_VERSION 4 struct uinput_ff_upload { @@ -73,6 +75,15 @@ struct uinput_ff_erase { #define UI_BEGIN_FF_ERASE _IOWR(UINPUT_IOCTL_BASE, 202, struct uinput_ff_erase) #define UI_END_FF_ERASE _IOW(UINPUT_IOCTL_BASE, 203, struct uinput_ff_erase) +/** + * UI_GET_SYSNAME - get the sysfs name of the created uinput device + * + * @return the sysfs name of the created virtual input device. + * The complete sysfs path is then /sys/devices/virtual/input/--NAME-- + * Usually, it is in the form "inputN" + */ +#define UI_GET_SYSNAME(len) _IOC(_IOC_READ, UINPUT_IOCTL_BASE, 300, len) + /* * To write a force-feedback-capable driver, the upload_effect * and erase_effect callbacks in input_dev must be implemented. -- cgit v1.2.3 From ad7647d92f61c6e78bf6dc3804da1e2acf0515b6 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 3 Jan 2014 20:54:37 -0800 Subject: Input: ims-pcu - fix error unwinding path in application mode We first create backlight and then input devices so we should destroy them in opposite order when handling errors. Signed-off-by: Dmitry Torokhov --- drivers/input/misc/ims-pcu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index e204f26b0011..5e8e90260037 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -1674,10 +1674,10 @@ static int ims_pcu_init_application_mode(struct ims_pcu *pcu) return 0; -err_destroy_backlight: - ims_pcu_destroy_backlight(pcu); err_destroy_buttons: ims_pcu_destroy_buttons(pcu); +err_destroy_backlight: + ims_pcu_destroy_backlight(pcu); return error; } -- cgit v1.2.3 From e5fcd269c4599c4d224333b20e3ffd5e910d4db3 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Wed, 12 Feb 2014 15:02:14 -0800 Subject: Input: ims-pcu - add commands supported by the new version of the FW New version of the PCU firmware supports two new commands: - IMS_PCU_CMD_OFN_SET_CONFIG which allows to write data to the registers of one finger navigation(OFN) chip present on the device - IMS_PCU_CMD_OFN_GET_CONFIG which allows to read data form the registers of said chip. This commit adds two helper functions to use those commands and sysfs attributes to use them. It also exposes some OFN configuration parameters via sysfs. Signed-off-by: Andrey Smirnov Signed-off-by: Dmitry Torokhov --- drivers/input/misc/ims-pcu.c | 254 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 248 insertions(+), 6 deletions(-) diff --git a/drivers/input/misc/ims-pcu.c b/drivers/input/misc/ims-pcu.c index 5e8e90260037..5a736397d9c8 100644 --- a/drivers/input/misc/ims-pcu.c +++ b/drivers/input/misc/ims-pcu.c @@ -51,6 +51,8 @@ struct ims_pcu_backlight { #define IMS_PCU_BL_VERSION_LEN (9 + 1) #define IMS_PCU_BL_RESET_REASON_LEN (2 + 1) +#define IMS_PCU_PCU_B_DEVICE_ID 5 + #define IMS_PCU_BUF_SIZE 128 struct ims_pcu { @@ -68,6 +70,9 @@ struct ims_pcu { char bl_version[IMS_PCU_BL_VERSION_LEN]; char reset_reason[IMS_PCU_BL_RESET_REASON_LEN]; int update_firmware_status; + u8 device_id; + + u8 ofn_reg_addr; struct usb_interface *ctrl_intf; @@ -371,6 +376,8 @@ static void ims_pcu_destroy_gamepad(struct ims_pcu *pcu) #define IMS_PCU_CMD_GET_DEVICE_ID 0xae #define IMS_PCU_CMD_SPECIAL_INFO 0xb0 #define IMS_PCU_CMD_BOOTLOADER 0xb1 /* Pass data to bootloader */ +#define IMS_PCU_CMD_OFN_SET_CONFIG 0xb3 +#define IMS_PCU_CMD_OFN_GET_CONFIG 0xb4 /* PCU responses */ #define IMS_PCU_RSP_STATUS 0xc0 @@ -389,6 +396,9 @@ static void ims_pcu_destroy_gamepad(struct ims_pcu *pcu) #define IMS_PCU_RSP_GET_DEVICE_ID 0xce #define IMS_PCU_RSP_SPECIAL_INFO 0xd0 #define IMS_PCU_RSP_BOOTLOADER 0xd1 /* Bootloader response */ +#define IMS_PCU_RSP_OFN_SET_CONFIG 0xd2 +#define IMS_PCU_RSP_OFN_GET_CONFIG 0xd3 + #define IMS_PCU_RSP_EVNT_BUTTONS 0xe0 /* Unsolicited, button state */ #define IMS_PCU_GAMEPAD_MASK 0x0001ff80UL /* Bits 7 through 16 */ @@ -1256,6 +1266,225 @@ static struct attribute_group ims_pcu_attr_group = { .attrs = ims_pcu_attrs, }; +/* Support for a separate OFN attribute group */ + +#define OFN_REG_RESULT_OFFSET 2 + +static int ims_pcu_read_ofn_config(struct ims_pcu *pcu, u8 addr, u8 *data) +{ + int error; + s16 result; + + error = ims_pcu_execute_command(pcu, OFN_GET_CONFIG, + &addr, sizeof(addr)); + if (error) + return error; + + result = (s16)get_unaligned_le16(pcu->cmd_buf + OFN_REG_RESULT_OFFSET); + if (result < 0) + return -EIO; + + /* We only need LSB */ + *data = pcu->cmd_buf[OFN_REG_RESULT_OFFSET]; + return 0; +} + +static int ims_pcu_write_ofn_config(struct ims_pcu *pcu, u8 addr, u8 data) +{ + u8 buffer[] = { addr, data }; + int error; + s16 result; + + error = ims_pcu_execute_command(pcu, OFN_SET_CONFIG, + &buffer, sizeof(buffer)); + if (error) + return error; + + result = (s16)get_unaligned_le16(pcu->cmd_buf + OFN_REG_RESULT_OFFSET); + if (result < 0) + return -EIO; + + return 0; +} + +static ssize_t ims_pcu_ofn_reg_data_show(struct device *dev, + struct device_attribute *dattr, + char *buf) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct ims_pcu *pcu = usb_get_intfdata(intf); + int error; + u8 data; + + mutex_lock(&pcu->cmd_mutex); + error = ims_pcu_read_ofn_config(pcu, pcu->ofn_reg_addr, &data); + mutex_unlock(&pcu->cmd_mutex); + + if (error) + return error; + + return scnprintf(buf, PAGE_SIZE, "%x\n", data); +} + +static ssize_t ims_pcu_ofn_reg_data_store(struct device *dev, + struct device_attribute *dattr, + const char *buf, size_t count) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct ims_pcu *pcu = usb_get_intfdata(intf); + int error; + u8 value; + + error = kstrtou8(buf, 0, &value); + if (error) + return error; + + mutex_lock(&pcu->cmd_mutex); + error = ims_pcu_write_ofn_config(pcu, pcu->ofn_reg_addr, value); + mutex_unlock(&pcu->cmd_mutex); + + return error ?: count; +} + +static DEVICE_ATTR(reg_data, S_IRUGO | S_IWUSR, + ims_pcu_ofn_reg_data_show, ims_pcu_ofn_reg_data_store); + +static ssize_t ims_pcu_ofn_reg_addr_show(struct device *dev, + struct device_attribute *dattr, + char *buf) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct ims_pcu *pcu = usb_get_intfdata(intf); + int error; + + mutex_lock(&pcu->cmd_mutex); + error = scnprintf(buf, PAGE_SIZE, "%x\n", pcu->ofn_reg_addr); + mutex_unlock(&pcu->cmd_mutex); + + return error; +} + +static ssize_t ims_pcu_ofn_reg_addr_store(struct device *dev, + struct device_attribute *dattr, + const char *buf, size_t count) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct ims_pcu *pcu = usb_get_intfdata(intf); + int error; + u8 value; + + error = kstrtou8(buf, 0, &value); + if (error) + return error; + + mutex_lock(&pcu->cmd_mutex); + pcu->ofn_reg_addr = value; + mutex_unlock(&pcu->cmd_mutex); + + return error ?: count; +} + +static DEVICE_ATTR(reg_addr, S_IRUGO | S_IWUSR, + ims_pcu_ofn_reg_addr_show, ims_pcu_ofn_reg_addr_store); + +struct ims_pcu_ofn_bit_attribute { + struct device_attribute dattr; + u8 addr; + u8 nr; +}; + +static ssize_t ims_pcu_ofn_bit_show(struct device *dev, + struct device_attribute *dattr, + char *buf) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct ims_pcu *pcu = usb_get_intfdata(intf); + struct ims_pcu_ofn_bit_attribute *attr = + container_of(dattr, struct ims_pcu_ofn_bit_attribute, dattr); + int error; + u8 data; + + mutex_lock(&pcu->cmd_mutex); + error = ims_pcu_read_ofn_config(pcu, attr->addr, &data); + mutex_unlock(&pcu->cmd_mutex); + + if (error) + return error; + + return scnprintf(buf, PAGE_SIZE, "%d\n", !!(data & (1 << attr->nr))); +} + +static ssize_t ims_pcu_ofn_bit_store(struct device *dev, + struct device_attribute *dattr, + const char *buf, size_t count) +{ + struct usb_interface *intf = to_usb_interface(dev); + struct ims_pcu *pcu = usb_get_intfdata(intf); + struct ims_pcu_ofn_bit_attribute *attr = + container_of(dattr, struct ims_pcu_ofn_bit_attribute, dattr); + int error; + int value; + u8 data; + + error = kstrtoint(buf, 0, &value); + if (error) + return error; + + if (value > 1) + return -EINVAL; + + mutex_lock(&pcu->cmd_mutex); + + error = ims_pcu_read_ofn_config(pcu, attr->addr, &data); + if (!error) { + if (value) + data |= 1U << attr->nr; + else + data &= ~(1U << attr->nr); + + error = ims_pcu_write_ofn_config(pcu, attr->addr, data); + } + + mutex_unlock(&pcu->cmd_mutex); + + return error ?: count; +} + +#define IMS_PCU_OFN_BIT_ATTR(_field, _addr, _nr) \ +struct ims_pcu_ofn_bit_attribute ims_pcu_ofn_attr_##_field = { \ + .dattr = __ATTR(_field, S_IWUSR | S_IRUGO, \ + ims_pcu_ofn_bit_show, ims_pcu_ofn_bit_store), \ + .addr = _addr, \ + .nr = _nr, \ +} + +static IMS_PCU_OFN_BIT_ATTR(engine_enable, 0x60, 7); +static IMS_PCU_OFN_BIT_ATTR(speed_enable, 0x60, 6); +static IMS_PCU_OFN_BIT_ATTR(assert_enable, 0x60, 5); +static IMS_PCU_OFN_BIT_ATTR(xyquant_enable, 0x60, 4); +static IMS_PCU_OFN_BIT_ATTR(xyscale_enable, 0x60, 1); + +static IMS_PCU_OFN_BIT_ATTR(scale_x2, 0x63, 6); +static IMS_PCU_OFN_BIT_ATTR(scale_y2, 0x63, 7); + +static struct attribute *ims_pcu_ofn_attrs[] = { + &dev_attr_reg_data.attr, + &dev_attr_reg_addr.attr, + &ims_pcu_ofn_attr_engine_enable.dattr.attr, + &ims_pcu_ofn_attr_speed_enable.dattr.attr, + &ims_pcu_ofn_attr_assert_enable.dattr.attr, + &ims_pcu_ofn_attr_xyquant_enable.dattr.attr, + &ims_pcu_ofn_attr_xyscale_enable.dattr.attr, + &ims_pcu_ofn_attr_scale_x2.dattr.attr, + &ims_pcu_ofn_attr_scale_y2.dattr.attr, + NULL +}; + +static struct attribute_group ims_pcu_ofn_attr_group = { + .name = "ofn", + .attrs = ims_pcu_ofn_attrs, +}; + static void ims_pcu_irq(struct urb *urb) { struct ims_pcu *pcu = urb->context; @@ -1624,7 +1853,6 @@ static int ims_pcu_init_application_mode(struct ims_pcu *pcu) static atomic_t device_no = ATOMIC_INIT(0); const struct ims_pcu_device_info *info; - u8 device_id; int error; error = ims_pcu_get_device_info(pcu); @@ -1633,7 +1861,7 @@ static int ims_pcu_init_application_mode(struct ims_pcu *pcu) return error; } - error = ims_pcu_identify_type(pcu, &device_id); + error = ims_pcu_identify_type(pcu, &pcu->device_id); if (error) { dev_err(pcu->dev, "Failed to identify device, error: %d\n", error); @@ -1645,9 +1873,9 @@ static int ims_pcu_init_application_mode(struct ims_pcu *pcu) return 0; } - if (device_id >= ARRAY_SIZE(ims_pcu_device_info) || - !ims_pcu_device_info[device_id].keymap) { - dev_err(pcu->dev, "Device ID %d is not valid\n", device_id); + if (pcu->device_id >= ARRAY_SIZE(ims_pcu_device_info) || + !ims_pcu_device_info[pcu->device_id].keymap) { + dev_err(pcu->dev, "Device ID %d is not valid\n", pcu->device_id); /* Same as above, punt to userspace */ return 0; } @@ -1655,11 +1883,21 @@ static int ims_pcu_init_application_mode(struct ims_pcu *pcu) /* Device appears to be operable, complete initialization */ pcu->device_no = atomic_inc_return(&device_no) - 1; + /* + * PCU-B devices, both GEN_1 and GEN_2 do not have OFN sensor + */ + if (pcu->device_id != IMS_PCU_PCU_B_DEVICE_ID) { + error = sysfs_create_group(&pcu->dev->kobj, + &ims_pcu_ofn_attr_group); + if (error) + return error; + } + error = ims_pcu_setup_backlight(pcu); if (error) return error; - info = &ims_pcu_device_info[device_id]; + info = &ims_pcu_device_info[pcu->device_id]; error = ims_pcu_setup_buttons(pcu, info->keymap, info->keymap_len); if (error) goto err_destroy_backlight; @@ -1691,6 +1929,10 @@ static void ims_pcu_destroy_application_mode(struct ims_pcu *pcu) ims_pcu_destroy_gamepad(pcu); ims_pcu_destroy_buttons(pcu); ims_pcu_destroy_backlight(pcu); + + if (pcu->device_id != IMS_PCU_PCU_B_DEVICE_ID) + sysfs_remove_group(&pcu->dev->kobj, + &ims_pcu_ofn_attr_group); } } -- cgit v1.2.3 From 1560a838f1a6402bdb2006cf30221e83106b0d02 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 13 Feb 2014 23:57:37 -0800 Subject: Input: s3c2410 - trivial cleanup in header file Commit 436d42c61c3e ("ARM: samsung: move platform_data definitions") moved the files to the current location but forgot to remove the pointer to its previous location. Clean it up. While at it also change the header file protection macros appropriately and remove whitespace errors. Signed-off-by: Sachin Kamat Signed-off-by: Dmitry Torokhov --- include/linux/platform_data/touchscreen-s3c2410.h | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/include/linux/platform_data/touchscreen-s3c2410.h b/include/linux/platform_data/touchscreen-s3c2410.h index 26fdb22e0fc2..58dc7c5ae63b 100644 --- a/include/linux/platform_data/touchscreen-s3c2410.h +++ b/include/linux/platform_data/touchscreen-s3c2410.h @@ -1,5 +1,4 @@ -/* arch/arm/plat-samsung/include/plat/ts.h - * +/* * Copyright (c) 2005 Arnaud Patard * * This program is free software; you can redistribute it and/or modify @@ -7,14 +6,14 @@ * published by the Free Software Foundation. */ -#ifndef __ASM_ARM_TS_H -#define __ASM_ARM_TS_H +#ifndef __TOUCHSCREEN_S3C2410_H +#define __TOUCHSCREEN_S3C2410_H struct s3c2410_ts_mach_info { - int delay; - int presc; - int oversampling_shift; - void (*cfg_gpio)(struct platform_device *dev); + int delay; + int presc; + int oversampling_shift; + void (*cfg_gpio)(struct platform_device *dev); }; extern void s3c24xx_ts_set_platdata(struct s3c2410_ts_mach_info *); @@ -22,4 +21,4 @@ extern void s3c24xx_ts_set_platdata(struct s3c2410_ts_mach_info *); /* defined by architecture to configure gpio */ extern void s3c24xx_ts_cfg_gpio(struct platform_device *dev); -#endif /* __ASM_ARM_TS_H */ +#endif /*__TOUCHSCREEN_S3C2410_H */ -- cgit v1.2.3 From 864d3c0fe41c5e9ea4a477509b670e7503bda84c Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 13 Feb 2014 23:38:43 -0800 Subject: Input: sirfsoc-onkey - implement open and close methods We can control whetehr device generates interrupts or not so let's implement open and close methods of input device so that we do not do any processing until there are users. Tested-by: Xianglong Du Signed-off-by: Dmitry Torokhov --- drivers/input/misc/sirfsoc-onkey.c | 53 +++++++++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/drivers/input/misc/sirfsoc-onkey.c b/drivers/input/misc/sirfsoc-onkey.c index e8897c36d21b..0755614c8ee5 100644 --- a/drivers/input/misc/sirfsoc-onkey.c +++ b/drivers/input/misc/sirfsoc-onkey.c @@ -49,6 +49,35 @@ static irqreturn_t sirfsoc_pwrc_isr(int irq, void *dev_id) return IRQ_HANDLED; } +static void sirfsoc_pwrc_toggle_interrupts(struct sirfsoc_pwrc_drvdata *pwrcdrv, + bool enable) +{ + u32 int_mask; + + int_mask = sirfsoc_rtc_iobrg_readl(pwrcdrv->pwrc_base + PWRC_INT_MASK); + if (enable) + int_mask |= PWRC_ON_KEY_BIT; + else + int_mask &= ~PWRC_ON_KEY_BIT; + sirfsoc_rtc_iobrg_writel(int_mask, pwrcdrv->pwrc_base + PWRC_INT_MASK); +} + +static int sirfsoc_pwrc_open(struct input_dev *input) +{ + struct sirfsoc_pwrc_drvdata *pwrcdrv = input_get_drvdata(input); + + sirfsoc_pwrc_toggle_interrupts(pwrcdrv, true); + + return 0; +} + +static void sirfsoc_pwrc_close(struct input_dev *input) +{ + struct sirfsoc_pwrc_drvdata *pwrcdrv = input_get_drvdata(input); + + sirfsoc_pwrc_toggle_interrupts(pwrcdrv, false); +} + static const struct of_device_id sirfsoc_pwrc_of_match[] = { { .compatible = "sirf,prima2-pwrc" }, {}, @@ -70,7 +99,7 @@ static int sirfsoc_pwrc_probe(struct platform_device *pdev) } /* - * we can't use of_iomap because pwrc is not mapped in memory, + * We can't use of_iomap because pwrc is not mapped in memory, * the so-called base address is only offset in rtciobrg */ error = of_property_read_u32(np, "reg", &pwrcdrv->pwrc_base); @@ -88,6 +117,14 @@ static int sirfsoc_pwrc_probe(struct platform_device *pdev) pwrcdrv->input->phys = "pwrc/input0"; pwrcdrv->input->evbit[0] = BIT_MASK(EV_PWR); + pwrcdrv->input->open = sirfsoc_pwrc_open; + pwrcdrv->input->close = sirfsoc_pwrc_close; + + input_set_drvdata(pwrcdrv->input, pwrcdrv); + + /* Make sure the device is quiesced */ + sirfsoc_pwrc_toggle_interrupts(pwrcdrv, false); + irq = platform_get_irq(pdev, 0); error = devm_request_irq(&pdev->dev, irq, sirfsoc_pwrc_isr, IRQF_SHARED, @@ -98,11 +135,6 @@ static int sirfsoc_pwrc_probe(struct platform_device *pdev) return error; } - sirfsoc_rtc_iobrg_writel( - sirfsoc_rtc_iobrg_readl(pwrcdrv->pwrc_base + PWRC_INT_MASK) | - PWRC_ON_KEY_BIT, - pwrcdrv->pwrc_base + PWRC_INT_MASK); - error = input_register_device(pwrcdrv->input); if (error) { dev_err(&pdev->dev, @@ -129,15 +161,16 @@ static int pwrc_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct sirfsoc_pwrc_drvdata *pwrcdrv = platform_get_drvdata(pdev); + struct input_dev *input = pwrcdrv->input; /* * Do not mask pwrc interrupt as we want pwrc work as a wakeup source * if users touch X_ONKEY_B, see arch/arm/mach-prima2/pm.c */ - sirfsoc_rtc_iobrg_writel( - sirfsoc_rtc_iobrg_readl( - pwrcdrv->pwrc_base + PWRC_INT_MASK) | PWRC_ON_KEY_BIT, - pwrcdrv->pwrc_base + PWRC_INT_MASK); + mutex_lock(&input->mutex); + if (input->users) + sirfsoc_pwrc_toggle_interrupts(pwrcdrv, true); + mutex_unlock(&input->mutex); return 0; } -- cgit v1.2.3 From 5099817eaf1c5377059b703496e4d32769d0f2d7 Mon Sep 17 00:00:00 2001 From: Barry Song Date: Fri, 14 Feb 2014 08:43:05 -0800 Subject: Input: sirfsoc-onkey - drop the IRQF_SHARED flag Since the IRQ handler always returns IRQ_HANDLED it means this irq is not a shared IRQ at all. Or at least, the SW is not self-consistent now. Signed-off-by: Barry Song Signed-off-by: Dmitry Torokhov --- drivers/input/misc/sirfsoc-onkey.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/misc/sirfsoc-onkey.c b/drivers/input/misc/sirfsoc-onkey.c index 0755614c8ee5..3b54cba650de 100644 --- a/drivers/input/misc/sirfsoc-onkey.c +++ b/drivers/input/misc/sirfsoc-onkey.c @@ -127,7 +127,7 @@ static int sirfsoc_pwrc_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); error = devm_request_irq(&pdev->dev, irq, - sirfsoc_pwrc_isr, IRQF_SHARED, + sirfsoc_pwrc_isr, 0, "sirfsoc_pwrc_int", pwrcdrv); if (error) { dev_err(&pdev->dev, "unable to claim irq %d, error: %d\n", -- cgit v1.2.3 From 3a80035bcbbaff1870a537b4ea57727290bded56 Mon Sep 17 00:00:00 2001 From: Xianglong Du Date: Fri, 14 Feb 2014 08:45:13 -0800 Subject: Input: sirfsoc-onkey - fix namespace pwrc_resume function This function lost namespace, this patch fixes it. Signed-off-by: Xianglong Du Signed-off-by: Barry Song Signed-off-by: Dmitry Torokhov --- drivers/input/misc/sirfsoc-onkey.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/input/misc/sirfsoc-onkey.c b/drivers/input/misc/sirfsoc-onkey.c index 3b54cba650de..d5b4a7674b9a 100644 --- a/drivers/input/misc/sirfsoc-onkey.c +++ b/drivers/input/misc/sirfsoc-onkey.c @@ -157,7 +157,7 @@ static int sirfsoc_pwrc_remove(struct platform_device *pdev) } #ifdef CONFIG_PM_SLEEP -static int pwrc_resume(struct device *dev) +static int sirfsoc_pwrc_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct sirfsoc_pwrc_drvdata *pwrcdrv = platform_get_drvdata(pdev); @@ -176,7 +176,7 @@ static int pwrc_resume(struct device *dev) } #endif -static SIMPLE_DEV_PM_OPS(sirfsoc_pwrc_pm_ops, NULL, pwrc_resume); +static SIMPLE_DEV_PM_OPS(sirfsoc_pwrc_pm_ops, NULL, sirfsoc_pwrc_resume); static struct platform_driver sirfsoc_pwrc_driver = { .probe = sirfsoc_pwrc_probe, -- cgit v1.2.3 From a5e466451939153cbec4d5446be139ba666ee28d Mon Sep 17 00:00:00 2001 From: Xianglong Du Date: Fri, 14 Feb 2014 08:45:56 -0800 Subject: Input: sirfsoc-onkey - use dev_get_drvdata instead of platform_get_drvdata In resume entry, use dev_get_drvdata() instead of to_platform_device(dev) + platform_get_drvdata(pdev). Signed-off-by: Xianglong Du Signed-off-by: Barry Song Signed-off-by: Dmitry Torokhov --- drivers/input/misc/sirfsoc-onkey.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/input/misc/sirfsoc-onkey.c b/drivers/input/misc/sirfsoc-onkey.c index d5b4a7674b9a..81cdf51f8246 100644 --- a/drivers/input/misc/sirfsoc-onkey.c +++ b/drivers/input/misc/sirfsoc-onkey.c @@ -143,7 +143,7 @@ static int sirfsoc_pwrc_probe(struct platform_device *pdev) return error; } - platform_set_drvdata(pdev, pwrcdrv); + dev_set_drvdata(&pdev->dev, pwrcdrv); device_init_wakeup(&pdev->dev, 1); return 0; @@ -159,8 +159,7 @@ static int sirfsoc_pwrc_remove(struct platform_device *pdev) #ifdef CONFIG_PM_SLEEP static int sirfsoc_pwrc_resume(struct device *dev) { - struct platform_device *pdev = to_platform_device(dev); - struct sirfsoc_pwrc_drvdata *pwrcdrv = platform_get_drvdata(pdev); + struct sirfsoc_pwrc_drvdata *pwrcdrv = dev_get_drvdata(dev); struct input_dev *input = pwrcdrv->input; /* -- cgit v1.2.3 From a1a7521064428fc1cf8e2942179525a22cde1d49 Mon Sep 17 00:00:00 2001 From: Xianglong Du Date: Sat, 15 Feb 2014 12:30:58 -0800 Subject: Input: sirfsoc-onkey - report release event by detecting pin status This change adds a delayed_work to detect the release of onkey since HW will not generate interrupt for it. At the same time, we move the KEY event to POWER instead of SUSPEND, which will be suitable for both Android and Linux. Userspace PowerManager Daemon will decide to suspend or shutdown based on how long we have touched onkey. Signed-off-by: Xianglong Du Signed-off-by: Rongjun Ying Signed-off-by: Barry Song Signed-off-by: Dmitry Torokhov --- drivers/input/misc/sirfsoc-onkey.c | 43 ++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/drivers/input/misc/sirfsoc-onkey.c b/drivers/input/misc/sirfsoc-onkey.c index 81cdf51f8246..a6e76080af5f 100644 --- a/drivers/input/misc/sirfsoc-onkey.c +++ b/drivers/input/misc/sirfsoc-onkey.c @@ -13,16 +13,41 @@ #include #include #include +#include struct sirfsoc_pwrc_drvdata { u32 pwrc_base; struct input_dev *input; + struct delayed_work work; }; #define PWRC_ON_KEY_BIT (1 << 0) #define PWRC_INT_STATUS 0xc #define PWRC_INT_MASK 0x10 +#define PWRC_PIN_STATUS 0x14 +#define PWRC_KEY_DETECT_UP_TIME 20 /* ms*/ + +static int sirfsoc_pwrc_is_on_key_down(struct sirfsoc_pwrc_drvdata *pwrcdrv) +{ + u32 state = sirfsoc_rtc_iobrg_readl(pwrcdrv->pwrc_base + + PWRC_PIN_STATUS); + return !(state & PWRC_ON_KEY_BIT); /* ON_KEY is active low */ +} + +static void sirfsoc_pwrc_report_event(struct work_struct *work) +{ + struct sirfsoc_pwrc_drvdata *pwrcdrv = + container_of(work, struct sirfsoc_pwrc_drvdata, work.work); + + if (sirfsoc_pwrc_is_on_key_down(pwrcdrv)) { + schedule_delayed_work(&pwrcdrv->work, + msecs_to_jiffies(PWRC_KEY_DETECT_UP_TIME)); + } else { + input_event(pwrcdrv->input, EV_KEY, KEY_POWER, 0); + input_sync(pwrcdrv->input); + } +} static irqreturn_t sirfsoc_pwrc_isr(int irq, void *dev_id) { @@ -34,17 +59,10 @@ static irqreturn_t sirfsoc_pwrc_isr(int irq, void *dev_id) sirfsoc_rtc_iobrg_writel(int_status & ~PWRC_ON_KEY_BIT, pwrcdrv->pwrc_base + PWRC_INT_STATUS); - /* - * For a typical Linux system, we report KEY_SUSPEND to trigger apm-power.c - * to queue a SUSPEND APM event - */ - input_event(pwrcdrv->input, EV_PWR, KEY_SUSPEND, 1); + input_event(pwrcdrv->input, EV_KEY, KEY_POWER, 1); input_sync(pwrcdrv->input); - - /* - * Todo: report KEY_POWER event for Android platforms, Android PowerManager - * will handle the suspend and powerdown/hibernation - */ + schedule_delayed_work(&pwrcdrv->work, + msecs_to_jiffies(PWRC_KEY_DETECT_UP_TIME)); return IRQ_HANDLED; } @@ -76,6 +94,7 @@ static void sirfsoc_pwrc_close(struct input_dev *input) struct sirfsoc_pwrc_drvdata *pwrcdrv = input_get_drvdata(input); sirfsoc_pwrc_toggle_interrupts(pwrcdrv, false); + cancel_delayed_work_sync(&pwrcdrv->work); } static const struct of_device_id sirfsoc_pwrc_of_match[] = { @@ -115,7 +134,9 @@ static int sirfsoc_pwrc_probe(struct platform_device *pdev) pwrcdrv->input->name = "sirfsoc pwrckey"; pwrcdrv->input->phys = "pwrc/input0"; - pwrcdrv->input->evbit[0] = BIT_MASK(EV_PWR); + pwrcdrv->input->evbit[0] = BIT_MASK(EV_KEY); + + INIT_DELAYED_WORK(&pwrcdrv->work, sirfsoc_pwrc_report_event); pwrcdrv->input->open = sirfsoc_pwrc_open; pwrcdrv->input->close = sirfsoc_pwrc_close; -- cgit v1.2.3 From a9728f9730c915bcd3d3010565946195b9f19e1d Mon Sep 17 00:00:00 2001 From: Barry Song Date: Fri, 14 Feb 2014 08:48:21 -0800 Subject: Input: sirfsoc-onkey - update copyright years to 2014 Happy the year of horse, 2014. ,((((^`\ (((( (6 \ ,((((( , \ ,,,_ ,((((( /"._ ,`, ((((\\ ,... ,(((( / `-.-' ))) ;' `"'"'""(((( ( ((( / ((( \ )) | | (( | . ' | )) \ _ ' `t ,.') ( | y;- -,-""'"-.\ \/ ) / ./ ) / `\ \ |./ ( ( / /' || \\ //'| || \\ _//'|| || )) |_/ || \_\ |_/ || `'" \_\ `'" Signed-off-by: Barry Song Signed-off-by: Dmitry Torokhov --- drivers/input/misc/sirfsoc-onkey.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/input/misc/sirfsoc-onkey.c b/drivers/input/misc/sirfsoc-onkey.c index a6e76080af5f..4d66c723cf08 100644 --- a/drivers/input/misc/sirfsoc-onkey.c +++ b/drivers/input/misc/sirfsoc-onkey.c @@ -1,7 +1,8 @@ /* * Power key driver for SiRF PrimaII * - * Copyright (c) 2013 Cambridge Silicon Radio Limited, a CSR plc group company. + * Copyright (c) 2013 - 2014 Cambridge Silicon Radio Limited, a CSR plc group + * company. * * Licensed under GPLv2 or later. */ -- cgit v1.2.3 From 7fb45edba8b5206e645572a682c946f67375447e Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 17 Feb 2014 11:31:36 -0800 Subject: Input: imx_keypad - Propagate the real error code on platform_get_irq() failure No need to return a 'fake' return value on platform_get_irq() failure. Just return the error code itself instead. Signed-off-by: Fabio Estevam Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/imx_keypad.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/input/keyboard/imx_keypad.c b/drivers/input/keyboard/imx_keypad.c index cbf4f8038cba..97ec33572e56 100644 --- a/drivers/input/keyboard/imx_keypad.c +++ b/drivers/input/keyboard/imx_keypad.c @@ -439,7 +439,7 @@ static int imx_keypad_probe(struct platform_device *pdev) irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(&pdev->dev, "no irq defined in platform data\n"); - return -EINVAL; + return irq; } input_dev = devm_input_allocate_device(&pdev->dev); @@ -449,7 +449,7 @@ static int imx_keypad_probe(struct platform_device *pdev) } keypad = devm_kzalloc(&pdev->dev, sizeof(struct imx_keypad), - GFP_KERNEL); + GFP_KERNEL); if (!keypad) { dev_err(&pdev->dev, "not enough memory for driver data\n"); return -ENOMEM; -- cgit v1.2.3 From 5fcd4d2c119309366aad5d8287e1329d9dee02e1 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Sun, 23 Mar 2014 17:23:35 -0700 Subject: Input: hp_sdc - use del_timer_sync() in exit path Make sure that no callback is running before we teardown the module. Signed-off-by: Thomas Gleixner Signed-off-by: Dmitry Torokhov --- drivers/input/serio/hp_sdc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/serio/hp_sdc.c b/drivers/input/serio/hp_sdc.c index d7a7e54f6465..852858e5d8d0 100644 --- a/drivers/input/serio/hp_sdc.c +++ b/drivers/input/serio/hp_sdc.c @@ -984,7 +984,7 @@ static void hp_sdc_exit(void) free_irq(hp_sdc.irq, &hp_sdc); write_unlock_irq(&hp_sdc.lock); - del_timer(&hp_sdc.kicker); + del_timer_sync(&hp_sdc.kicker); tasklet_kill(&hp_sdc.task); -- cgit v1.2.3 From 915a12232d1c4ef474b45a77eb89e0fceb24c2b5 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Tue, 11 Mar 2014 23:12:13 -0700 Subject: Input: olpc_apsp - fix dependencies of OLPC AP-SP driver I don't think the OLPC AP-SP driver is generally useful without OLPC support. So make it depend on OLPC, unless build testing is enabled. Signed-off-by: Jean Delvare Signed-off-by: Dmitry Torokhov --- drivers/input/serio/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig index aec54e283580..bc2d47431bdc 100644 --- a/drivers/input/serio/Kconfig +++ b/drivers/input/serio/Kconfig @@ -263,7 +263,7 @@ config SERIO_APBPS2 config SERIO_OLPC_APSP tristate "OLPC AP-SP input support" - depends on OF + depends on OLPC || COMPILE_TEST help Say Y here if you want support for the keyboard and touchpad included in the OLPC XO-1.75 and XO-4 laptops. -- cgit v1.2.3 From 86234eb7f149c30b3273b47fc8f5c67066e498d0 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Tue, 11 Mar 2014 23:09:08 -0700 Subject: Input: wistron_btns - simplify dependencies Kconfig symbol X86_32 was introduced in October 2005, it's about time to use it. This clears the last occurrence of the legacy "X86 && !X86_64" construct :-) Signed-off-by: Jean Delvare Signed-off-by: Dmitry Torokhov --- drivers/input/misc/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 7904ab05527a..7674f05d1fb3 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -269,7 +269,7 @@ config INPUT_COBALT_BTNS config INPUT_WISTRON_BTNS tristate "x86 Wistron laptop button interface" - depends on X86 && !X86_64 + depends on X86_32 select INPUT_POLLDEV select INPUT_SPARSEKMAP select NEW_LEDS -- cgit v1.2.3 From 415e02bd25b3c053c33db7d1228d207d81c0fb1b Mon Sep 17 00:00:00 2001 From: Anthony Olech Date: Mon, 17 Feb 2014 11:23:39 -0800 Subject: Input: da9052_onkey - use correct register bit for key status The wrong register bit of the DA9052/3 PMIC registers was used to determine the status on the ONKEY. Also a failure in reading the status register will no longer result in the work queue being rescheduled as that would result in a (potentially) endless retry. Signed-off-by: Anthony Olech Acked-by: David Dajun Chen Signed-off-by: Dmitry Torokhov --- drivers/input/misc/da9052_onkey.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/drivers/input/misc/da9052_onkey.c b/drivers/input/misc/da9052_onkey.c index 1f695f229ea8..184c8f21ab59 100644 --- a/drivers/input/misc/da9052_onkey.c +++ b/drivers/input/misc/da9052_onkey.c @@ -27,29 +27,32 @@ struct da9052_onkey { static void da9052_onkey_query(struct da9052_onkey *onkey) { - int key_stat; + int ret; - key_stat = da9052_reg_read(onkey->da9052, DA9052_EVENT_B_REG); - if (key_stat < 0) { + ret = da9052_reg_read(onkey->da9052, DA9052_STATUS_A_REG); + if (ret < 0) { dev_err(onkey->da9052->dev, - "Failed to read onkey event %d\n", key_stat); + "Failed to read onkey event err=%d\n", ret); } else { /* * Since interrupt for deassertion of ONKEY pin is not * generated, onkey event state determines the onkey * button state. */ - key_stat &= DA9052_EVENTB_ENONKEY; - input_report_key(onkey->input, KEY_POWER, key_stat); + bool pressed = !(ret & DA9052_STATUSA_NONKEY); + + input_report_key(onkey->input, KEY_POWER, pressed); input_sync(onkey->input); - } - /* - * Interrupt is generated only when the ONKEY pin is asserted. - * Hence the deassertion of the pin is simulated through work queue. - */ - if (key_stat) - schedule_delayed_work(&onkey->work, msecs_to_jiffies(50)); + /* + * Interrupt is generated only when the ONKEY pin + * is asserted. Hence the deassertion of the pin + * is simulated through work queue. + */ + if (pressed) + schedule_delayed_work(&onkey->work, + msecs_to_jiffies(50)); + } } static void da9052_onkey_work(struct work_struct *work) -- cgit v1.2.3 From 9732e5b0bf87a1b0457be1c5d38262babc4cd2ae Mon Sep 17 00:00:00 2001 From: Xianglong Du Date: Fri, 28 Mar 2014 00:38:37 -0700 Subject: Input: sirfsoc-onkey - set the capability of reporting KEY_POWER commit a1a7521064428fc1cf8 moved to report EV_KEY event(KEY_POWER) instead of reporting EV_PWR event(KEY_SUSPEND), but it didn't enable the capability, so the KEY_POWER will not be reported to userspace by input core. this patch fixes the issue. Signed-off-by: Xianglong Du Signed-off-by: Barry Song Signed-off-by: Dmitry Torokhov --- drivers/input/misc/sirfsoc-onkey.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/input/misc/sirfsoc-onkey.c b/drivers/input/misc/sirfsoc-onkey.c index 4d66c723cf08..e4104f9b2e6d 100644 --- a/drivers/input/misc/sirfsoc-onkey.c +++ b/drivers/input/misc/sirfsoc-onkey.c @@ -136,6 +136,7 @@ static int sirfsoc_pwrc_probe(struct platform_device *pdev) pwrcdrv->input->name = "sirfsoc pwrckey"; pwrcdrv->input->phys = "pwrc/input0"; pwrcdrv->input->evbit[0] = BIT_MASK(EV_KEY); + input_set_capability(pwrcdrv->input, EV_KEY, KEY_POWER); INIT_DELAYED_WORK(&pwrcdrv->work, sirfsoc_pwrc_report_event); -- cgit v1.2.3 From e545ef39e06e7b1df2ef9ddbbf665b74c4e1878a Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 28 Mar 2014 00:52:36 -0700 Subject: Input: remove obsolete tnetv107x drivers The tnetv107x platform is getting removed, so the touchscreen and keypad drivers for this platform will no longer be needed either. Signed-off-by: Arnd Bergmann Acked-by: Sekhar Nori Acked-by: Kevin Hilman Cc: Dmitry Torokhov Cc: linux-input@vger.kernel.org Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/Kconfig | 10 - drivers/input/keyboard/Makefile | 1 - drivers/input/keyboard/tnetv107x-keypad.c | 329 ------------------------- drivers/input/touchscreen/Kconfig | 9 - drivers/input/touchscreen/Makefile | 1 - drivers/input/touchscreen/tnetv107x-ts.c | 384 ------------------------------ 6 files changed, 734 deletions(-) delete mode 100644 drivers/input/keyboard/tnetv107x-keypad.c delete mode 100644 drivers/input/touchscreen/tnetv107x-ts.c diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index a673c9f3a0b9..935dcaf16646 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -595,16 +595,6 @@ config KEYBOARD_TC3589X To compile this driver as a module, choose M here: the module will be called tc3589x-keypad. -config KEYBOARD_TNETV107X - tristate "TI TNETV107X keypad support" - depends on ARCH_DAVINCI_TNETV107X - select INPUT_MATRIXKMAP - help - Say Y here if you want to use the TNETV107X keypad. - - To compile this driver as a module, choose M here: the - module will be called tnetv107x-keypad. - config KEYBOARD_TWL4030 tristate "TI TWL4030/TWL5030/TPS659x0 keypad support" depends on TWL4030_CORE diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index a699b6172303..81014d94a0c1 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -53,7 +53,6 @@ obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o obj-$(CONFIG_KEYBOARD_TC3589X) += tc3589x-keypad.o obj-$(CONFIG_KEYBOARD_TEGRA) += tegra-kbc.o -obj-$(CONFIG_KEYBOARD_TNETV107X) += tnetv107x-keypad.o obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o obj-$(CONFIG_KEYBOARD_W90P910) += w90p910_keypad.o diff --git a/drivers/input/keyboard/tnetv107x-keypad.c b/drivers/input/keyboard/tnetv107x-keypad.c deleted file mode 100644 index 086511c2121b..000000000000 --- a/drivers/input/keyboard/tnetv107x-keypad.c +++ /dev/null @@ -1,329 +0,0 @@ -/* - * Texas Instruments TNETV107X Keypad Driver - * - * Copyright (C) 2010 Texas Instruments - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define BITS(x) (BIT(x) - 1) - -#define KEYPAD_ROWS 9 -#define KEYPAD_COLS 9 - -#define DEBOUNCE_MIN 0x400ul -#define DEBOUNCE_MAX 0x3ffffffful - -struct keypad_regs { - u32 rev; - u32 mode; - u32 mask; - u32 pol; - u32 dclock; - u32 rclock; - u32 stable_cnt; - u32 in_en; - u32 out; - u32 out_en; - u32 in; - u32 lock; - u32 pres[3]; -}; - -#define keypad_read(kp, reg) __raw_readl(&(kp)->regs->reg) -#define keypad_write(kp, reg, val) __raw_writel(val, &(kp)->regs->reg) - -struct keypad_data { - struct input_dev *input_dev; - struct resource *res; - struct keypad_regs __iomem *regs; - struct clk *clk; - struct device *dev; - spinlock_t lock; - int irq_press; - int irq_release; - int rows, cols, row_shift; - int debounce_ms, active_low; - u32 prev_keys[3]; - unsigned short keycodes[]; -}; - -static irqreturn_t keypad_irq(int irq, void *data) -{ - struct keypad_data *kp = data; - int i, bit, val, row, col, code; - unsigned long flags; - u32 curr_keys[3]; - u32 change; - - spin_lock_irqsave(&kp->lock, flags); - - memset(curr_keys, 0, sizeof(curr_keys)); - if (irq == kp->irq_press) - for (i = 0; i < 3; i++) - curr_keys[i] = keypad_read(kp, pres[i]); - - for (i = 0; i < 3; i++) { - change = curr_keys[i] ^ kp->prev_keys[i]; - - while (change) { - bit = fls(change) - 1; - change ^= BIT(bit); - val = curr_keys[i] & BIT(bit); - bit += i * 32; - row = bit / KEYPAD_COLS; - col = bit % KEYPAD_COLS; - - code = MATRIX_SCAN_CODE(row, col, kp->row_shift); - input_event(kp->input_dev, EV_MSC, MSC_SCAN, code); - input_report_key(kp->input_dev, kp->keycodes[code], - val); - } - } - input_sync(kp->input_dev); - memcpy(kp->prev_keys, curr_keys, sizeof(curr_keys)); - - if (irq == kp->irq_press) - keypad_write(kp, lock, 0); /* Allow hardware updates */ - - spin_unlock_irqrestore(&kp->lock, flags); - - return IRQ_HANDLED; -} - -static int keypad_start(struct input_dev *dev) -{ - struct keypad_data *kp = input_get_drvdata(dev); - unsigned long mask, debounce, clk_rate_khz; - unsigned long flags; - - clk_enable(kp->clk); - clk_rate_khz = clk_get_rate(kp->clk) / 1000; - - spin_lock_irqsave(&kp->lock, flags); - - /* Initialize device registers */ - keypad_write(kp, mode, 0); - - mask = BITS(kp->rows) << KEYPAD_COLS; - mask |= BITS(kp->cols); - keypad_write(kp, mask, ~mask); - - keypad_write(kp, pol, kp->active_low ? 0 : 0x3ffff); - keypad_write(kp, stable_cnt, 3); - - debounce = kp->debounce_ms * clk_rate_khz; - debounce = clamp(debounce, DEBOUNCE_MIN, DEBOUNCE_MAX); - keypad_write(kp, dclock, debounce); - keypad_write(kp, rclock, 4 * debounce); - - keypad_write(kp, in_en, 1); - - spin_unlock_irqrestore(&kp->lock, flags); - - return 0; -} - -static void keypad_stop(struct input_dev *dev) -{ - struct keypad_data *kp = input_get_drvdata(dev); - - synchronize_irq(kp->irq_press); - synchronize_irq(kp->irq_release); - clk_disable(kp->clk); -} - -static int keypad_probe(struct platform_device *pdev) -{ - const struct matrix_keypad_platform_data *pdata; - const struct matrix_keymap_data *keymap_data; - struct device *dev = &pdev->dev; - struct keypad_data *kp; - int error = 0, sz, row_shift; - u32 rev = 0; - - pdata = dev_get_platdata(&pdev->dev); - if (!pdata) { - dev_err(dev, "cannot find device data\n"); - return -EINVAL; - } - - keymap_data = pdata->keymap_data; - if (!keymap_data) { - dev_err(dev, "cannot find keymap data\n"); - return -EINVAL; - } - - row_shift = get_count_order(pdata->num_col_gpios); - sz = offsetof(struct keypad_data, keycodes); - sz += (pdata->num_row_gpios << row_shift) * sizeof(kp->keycodes[0]); - kp = kzalloc(sz, GFP_KERNEL); - if (!kp) { - dev_err(dev, "cannot allocate device info\n"); - return -ENOMEM; - } - - kp->dev = dev; - kp->rows = pdata->num_row_gpios; - kp->cols = pdata->num_col_gpios; - kp->row_shift = row_shift; - platform_set_drvdata(pdev, kp); - spin_lock_init(&kp->lock); - - kp->irq_press = platform_get_irq_byname(pdev, "press"); - kp->irq_release = platform_get_irq_byname(pdev, "release"); - if (kp->irq_press < 0 || kp->irq_release < 0) { - dev_err(dev, "cannot determine device interrupts\n"); - error = -ENODEV; - goto error_res; - } - - kp->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!kp->res) { - dev_err(dev, "cannot determine register area\n"); - error = -ENODEV; - goto error_res; - } - - if (!request_mem_region(kp->res->start, resource_size(kp->res), - pdev->name)) { - dev_err(dev, "cannot claim register memory\n"); - kp->res = NULL; - error = -EINVAL; - goto error_res; - } - - kp->regs = ioremap(kp->res->start, resource_size(kp->res)); - if (!kp->regs) { - dev_err(dev, "cannot map register memory\n"); - error = -ENOMEM; - goto error_map; - } - - kp->clk = clk_get(dev, NULL); - if (IS_ERR(kp->clk)) { - dev_err(dev, "cannot claim device clock\n"); - error = PTR_ERR(kp->clk); - goto error_clk; - } - - error = request_threaded_irq(kp->irq_press, NULL, keypad_irq, - IRQF_ONESHOT, dev_name(dev), kp); - if (error < 0) { - dev_err(kp->dev, "Could not allocate keypad press key irq\n"); - goto error_irq_press; - } - - error = request_threaded_irq(kp->irq_release, NULL, keypad_irq, - IRQF_ONESHOT, dev_name(dev), kp); - if (error < 0) { - dev_err(kp->dev, "Could not allocate keypad release key irq\n"); - goto error_irq_release; - } - - kp->input_dev = input_allocate_device(); - if (!kp->input_dev) { - dev_err(dev, "cannot allocate input device\n"); - error = -ENOMEM; - goto error_input; - } - - kp->input_dev->name = pdev->name; - kp->input_dev->dev.parent = &pdev->dev; - kp->input_dev->open = keypad_start; - kp->input_dev->close = keypad_stop; - - clk_enable(kp->clk); - rev = keypad_read(kp, rev); - kp->input_dev->id.bustype = BUS_HOST; - kp->input_dev->id.product = ((rev >> 8) & 0x07); - kp->input_dev->id.version = ((rev >> 16) & 0xfff); - clk_disable(kp->clk); - - error = matrix_keypad_build_keymap(keymap_data, NULL, - kp->rows, kp->cols, - kp->keycodes, kp->input_dev); - if (error) { - dev_err(dev, "Failed to build keymap\n"); - goto error_reg; - } - - if (!pdata->no_autorepeat) - kp->input_dev->evbit[0] |= BIT_MASK(EV_REP); - input_set_capability(kp->input_dev, EV_MSC, MSC_SCAN); - - input_set_drvdata(kp->input_dev, kp); - - error = input_register_device(kp->input_dev); - if (error < 0) { - dev_err(dev, "Could not register input device\n"); - goto error_reg; - } - - return 0; - - -error_reg: - input_free_device(kp->input_dev); -error_input: - free_irq(kp->irq_release, kp); -error_irq_release: - free_irq(kp->irq_press, kp); -error_irq_press: - clk_put(kp->clk); -error_clk: - iounmap(kp->regs); -error_map: - release_mem_region(kp->res->start, resource_size(kp->res)); -error_res: - kfree(kp); - return error; -} - -static int keypad_remove(struct platform_device *pdev) -{ - struct keypad_data *kp = platform_get_drvdata(pdev); - - free_irq(kp->irq_press, kp); - free_irq(kp->irq_release, kp); - input_unregister_device(kp->input_dev); - clk_put(kp->clk); - iounmap(kp->regs); - release_mem_region(kp->res->start, resource_size(kp->res)); - kfree(kp); - - return 0; -} - -static struct platform_driver keypad_driver = { - .probe = keypad_probe, - .remove = keypad_remove, - .driver.name = "tnetv107x-keypad", - .driver.owner = THIS_MODULE, -}; -module_platform_driver(keypad_driver); - -MODULE_AUTHOR("Cyril Chemparathy"); -MODULE_DESCRIPTION("TNETV107X Keypad Driver"); -MODULE_ALIAS("platform:tnetv107x-keypad"); -MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 07e9e82029d1..68edc9db2c64 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -514,15 +514,6 @@ config TOUCHSCREEN_MIGOR To compile this driver as a module, choose M here: the module will be called migor_ts. -config TOUCHSCREEN_TNETV107X - tristate "TI TNETV107X touchscreen support" - depends on ARCH_DAVINCI_TNETV107X - help - Say Y here if you want to use the TNETV107X touchscreen. - - To compile this driver as a module, choose M here: the - module will be called tnetv107x-ts. - config TOUCHSCREEN_TOUCHRIGHT tristate "Touchright serial touchscreen" select SERIO diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 62801f213346..4bc954b7c7c3 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -56,7 +56,6 @@ obj-$(CONFIG_TOUCHSCREEN_ST1232) += st1232.o obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o obj-$(CONFIG_TOUCHSCREEN_SUR40) += sur40.o obj-$(CONFIG_TOUCHSCREEN_TI_AM335X_TSC) += ti_am335x_tsc.o -obj-$(CONFIG_TOUCHSCREEN_TNETV107X) += tnetv107x-ts.o obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o diff --git a/drivers/input/touchscreen/tnetv107x-ts.c b/drivers/input/touchscreen/tnetv107x-ts.c deleted file mode 100644 index c47827a26e3c..000000000000 --- a/drivers/input/touchscreen/tnetv107x-ts.c +++ /dev/null @@ -1,384 +0,0 @@ -/* - * Texas Instruments TNETV107X Touchscreen Driver - * - * Copyright (C) 2010 Texas Instruments - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define TSC_PENUP_POLL (HZ / 5) -#define IDLE_TIMEOUT 100 /* msec */ - -/* - * The first and last samples of a touch interval are usually garbage and need - * to be filtered out with these devices. The following definitions control - * the number of samples skipped. - */ -#define TSC_HEAD_SKIP 1 -#define TSC_TAIL_SKIP 1 -#define TSC_SKIP (TSC_HEAD_SKIP + TSC_TAIL_SKIP + 1) -#define TSC_SAMPLES (TSC_SKIP + 1) - -/* Register Offsets */ -struct tsc_regs { - u32 rev; - u32 tscm; - u32 bwcm; - u32 swc; - u32 adcchnl; - u32 adcdata; - u32 chval[4]; -}; - -/* TSC Mode Configuration Register (tscm) bits */ -#define WMODE BIT(0) -#define TSKIND BIT(1) -#define ZMEASURE_EN BIT(2) -#define IDLE BIT(3) -#define TSC_EN BIT(4) -#define STOP BIT(5) -#define ONE_SHOT BIT(6) -#define SINGLE BIT(7) -#define AVG BIT(8) -#define AVGNUM(x) (((x) & 0x03) << 9) -#define PVSTC(x) (((x) & 0x07) << 11) -#define PON BIT(14) -#define PONBG BIT(15) -#define AFERST BIT(16) - -/* ADC DATA Capture Register bits */ -#define DATA_VALID BIT(16) - -/* Register Access Macros */ -#define tsc_read(ts, reg) __raw_readl(&(ts)->regs->reg) -#define tsc_write(ts, reg, val) __raw_writel(val, &(ts)->regs->reg); -#define tsc_set_bits(ts, reg, val) \ - tsc_write(ts, reg, tsc_read(ts, reg) | (val)) -#define tsc_clr_bits(ts, reg, val) \ - tsc_write(ts, reg, tsc_read(ts, reg) & ~(val)) - -struct sample { - int x, y, p; -}; - -struct tsc_data { - struct input_dev *input_dev; - struct resource *res; - struct tsc_regs __iomem *regs; - struct timer_list timer; - spinlock_t lock; - struct clk *clk; - struct device *dev; - int sample_count; - struct sample samples[TSC_SAMPLES]; - int tsc_irq; -}; - -static int tsc_read_sample(struct tsc_data *ts, struct sample* sample) -{ - int x, y, z1, z2, t, p = 0; - u32 val; - - val = tsc_read(ts, chval[0]); - if (val & DATA_VALID) - x = val & 0xffff; - else - return -EINVAL; - - y = tsc_read(ts, chval[1]) & 0xffff; - z1 = tsc_read(ts, chval[2]) & 0xffff; - z2 = tsc_read(ts, chval[3]) & 0xffff; - - if (z1) { - t = ((600 * x) * (z2 - z1)); - p = t / (u32) (z1 << 12); - if (p < 0) - p = 0; - } - - sample->x = x; - sample->y = y; - sample->p = p; - - return 0; -} - -static void tsc_poll(unsigned long data) -{ - struct tsc_data *ts = (struct tsc_data *)data; - unsigned long flags; - int i, val, x, y, p; - - spin_lock_irqsave(&ts->lock, flags); - - if (ts->sample_count >= TSC_SKIP) { - input_report_abs(ts->input_dev, ABS_PRESSURE, 0); - input_report_key(ts->input_dev, BTN_TOUCH, 0); - input_sync(ts->input_dev); - } else if (ts->sample_count > 0) { - /* - * A touch event lasted less than our skip count. Salvage and - * report anyway. - */ - for (i = 0, val = 0; i < ts->sample_count; i++) - val += ts->samples[i].x; - x = val / ts->sample_count; - - for (i = 0, val = 0; i < ts->sample_count; i++) - val += ts->samples[i].y; - y = val / ts->sample_count; - - for (i = 0, val = 0; i < ts->sample_count; i++) - val += ts->samples[i].p; - p = val / ts->sample_count; - - input_report_abs(ts->input_dev, ABS_X, x); - input_report_abs(ts->input_dev, ABS_Y, y); - input_report_abs(ts->input_dev, ABS_PRESSURE, p); - input_report_key(ts->input_dev, BTN_TOUCH, 1); - input_sync(ts->input_dev); - } - - ts->sample_count = 0; - - spin_unlock_irqrestore(&ts->lock, flags); -} - -static irqreturn_t tsc_irq(int irq, void *dev_id) -{ - struct tsc_data *ts = (struct tsc_data *)dev_id; - struct sample *sample; - int index; - - spin_lock(&ts->lock); - - index = ts->sample_count % TSC_SAMPLES; - sample = &ts->samples[index]; - if (tsc_read_sample(ts, sample) < 0) - goto out; - - if (++ts->sample_count >= TSC_SKIP) { - index = (ts->sample_count - TSC_TAIL_SKIP - 1) % TSC_SAMPLES; - sample = &ts->samples[index]; - - input_report_abs(ts->input_dev, ABS_X, sample->x); - input_report_abs(ts->input_dev, ABS_Y, sample->y); - input_report_abs(ts->input_dev, ABS_PRESSURE, sample->p); - if (ts->sample_count == TSC_SKIP) - input_report_key(ts->input_dev, BTN_TOUCH, 1); - input_sync(ts->input_dev); - } - mod_timer(&ts->timer, jiffies + TSC_PENUP_POLL); -out: - spin_unlock(&ts->lock); - return IRQ_HANDLED; -} - -static int tsc_start(struct input_dev *dev) -{ - struct tsc_data *ts = input_get_drvdata(dev); - unsigned long timeout = jiffies + msecs_to_jiffies(IDLE_TIMEOUT); - u32 val; - - clk_enable(ts->clk); - - /* Go to idle mode, before any initialization */ - while (time_after(timeout, jiffies)) { - if (tsc_read(ts, tscm) & IDLE) - break; - } - - if (time_before(timeout, jiffies)) { - dev_warn(ts->dev, "timeout waiting for idle\n"); - clk_disable(ts->clk); - return -EIO; - } - - /* Configure TSC Control register*/ - val = (PONBG | PON | PVSTC(4) | ONE_SHOT | ZMEASURE_EN); - tsc_write(ts, tscm, val); - - /* Bring TSC out of reset: Clear AFE reset bit */ - val &= ~(AFERST); - tsc_write(ts, tscm, val); - - /* Configure all pins for hardware control*/ - tsc_write(ts, bwcm, 0); - - /* Finally enable the TSC */ - tsc_set_bits(ts, tscm, TSC_EN); - - return 0; -} - -static void tsc_stop(struct input_dev *dev) -{ - struct tsc_data *ts = input_get_drvdata(dev); - - tsc_clr_bits(ts, tscm, TSC_EN); - synchronize_irq(ts->tsc_irq); - del_timer_sync(&ts->timer); - clk_disable(ts->clk); -} - -static int tsc_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct tsc_data *ts; - int error = 0; - u32 rev = 0; - - ts = kzalloc(sizeof(struct tsc_data), GFP_KERNEL); - if (!ts) { - dev_err(dev, "cannot allocate device info\n"); - return -ENOMEM; - } - - ts->dev = dev; - spin_lock_init(&ts->lock); - setup_timer(&ts->timer, tsc_poll, (unsigned long)ts); - platform_set_drvdata(pdev, ts); - - ts->tsc_irq = platform_get_irq(pdev, 0); - if (ts->tsc_irq < 0) { - dev_err(dev, "cannot determine device interrupt\n"); - error = -ENODEV; - goto error_res; - } - - ts->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!ts->res) { - dev_err(dev, "cannot determine register area\n"); - error = -ENODEV; - goto error_res; - } - - if (!request_mem_region(ts->res->start, resource_size(ts->res), - pdev->name)) { - dev_err(dev, "cannot claim register memory\n"); - ts->res = NULL; - error = -EINVAL; - goto error_res; - } - - ts->regs = ioremap(ts->res->start, resource_size(ts->res)); - if (!ts->regs) { - dev_err(dev, "cannot map register memory\n"); - error = -ENOMEM; - goto error_map; - } - - ts->clk = clk_get(dev, NULL); - if (IS_ERR(ts->clk)) { - dev_err(dev, "cannot claim device clock\n"); - error = PTR_ERR(ts->clk); - goto error_clk; - } - - error = request_threaded_irq(ts->tsc_irq, NULL, tsc_irq, IRQF_ONESHOT, - dev_name(dev), ts); - if (error < 0) { - dev_err(ts->dev, "Could not allocate ts irq\n"); - goto error_irq; - } - - ts->input_dev = input_allocate_device(); - if (!ts->input_dev) { - dev_err(dev, "cannot allocate input device\n"); - error = -ENOMEM; - goto error_input; - } - input_set_drvdata(ts->input_dev, ts); - - ts->input_dev->name = pdev->name; - ts->input_dev->id.bustype = BUS_HOST; - ts->input_dev->dev.parent = &pdev->dev; - ts->input_dev->open = tsc_start; - ts->input_dev->close = tsc_stop; - - clk_enable(ts->clk); - rev = tsc_read(ts, rev); - ts->input_dev->id.product = ((rev >> 8) & 0x07); - ts->input_dev->id.version = ((rev >> 16) & 0xfff); - clk_disable(ts->clk); - - __set_bit(EV_KEY, ts->input_dev->evbit); - __set_bit(EV_ABS, ts->input_dev->evbit); - __set_bit(BTN_TOUCH, ts->input_dev->keybit); - - input_set_abs_params(ts->input_dev, ABS_X, 0, 0xffff, 5, 0); - input_set_abs_params(ts->input_dev, ABS_Y, 0, 0xffff, 5, 0); - input_set_abs_params(ts->input_dev, ABS_PRESSURE, 0, 4095, 128, 0); - - error = input_register_device(ts->input_dev); - if (error < 0) { - dev_err(dev, "failed input device registration\n"); - goto error_reg; - } - - return 0; - -error_reg: - input_free_device(ts->input_dev); -error_input: - free_irq(ts->tsc_irq, ts); -error_irq: - clk_put(ts->clk); -error_clk: - iounmap(ts->regs); -error_map: - release_mem_region(ts->res->start, resource_size(ts->res)); -error_res: - kfree(ts); - - return error; -} - -static int tsc_remove(struct platform_device *pdev) -{ - struct tsc_data *ts = platform_get_drvdata(pdev); - - input_unregister_device(ts->input_dev); - free_irq(ts->tsc_irq, ts); - clk_put(ts->clk); - iounmap(ts->regs); - release_mem_region(ts->res->start, resource_size(ts->res)); - kfree(ts); - - return 0; -} - -static struct platform_driver tsc_driver = { - .probe = tsc_probe, - .remove = tsc_remove, - .driver.name = "tnetv107x-ts", - .driver.owner = THIS_MODULE, -}; -module_platform_driver(tsc_driver); - -MODULE_AUTHOR("Cyril Chemparathy"); -MODULE_DESCRIPTION("TNETV107X Touchscreen Driver"); -MODULE_ALIAS("platform:tnetv107x-ts"); -MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 703e148875c838c430312da592d7ab3681ab5c81 Mon Sep 17 00:00:00 2001 From: Clinton Sprain Date: Wed, 26 Mar 2014 14:10:43 -0700 Subject: Input: appletouch - dial back fuzz setting Let's dial back the default fuzz setting for most devices using this driver, based on values from user feedback from forums and bug reports. Signed-off-by: Clinton Sprain Reviewed-by: Henrik Rydberg Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/appletouch.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/input/mouse/appletouch.c b/drivers/input/mouse/appletouch.c index 800ca7dfafc2..2745832f74b6 100644 --- a/drivers/input/mouse/appletouch.c +++ b/drivers/input/mouse/appletouch.c @@ -48,6 +48,7 @@ struct atp_info { int yfact; /* Y multiplication factor */ int datalen; /* size of USB transfers */ void (*callback)(struct urb *); /* callback function */ + int fuzz; /* fuzz touchpad generates */ }; static void atp_complete_geyser_1_2(struct urb *urb); @@ -61,6 +62,7 @@ static const struct atp_info fountain_info = { .yfact = 43, .datalen = 81, .callback = atp_complete_geyser_1_2, + .fuzz = 16, }; static const struct atp_info geyser1_info = { @@ -71,6 +73,7 @@ static const struct atp_info geyser1_info = { .yfact = 43, .datalen = 81, .callback = atp_complete_geyser_1_2, + .fuzz = 16, }; static const struct atp_info geyser2_info = { @@ -81,6 +84,7 @@ static const struct atp_info geyser2_info = { .yfact = 43, .datalen = 64, .callback = atp_complete_geyser_1_2, + .fuzz = 0, }; static const struct atp_info geyser3_info = { @@ -90,6 +94,7 @@ static const struct atp_info geyser3_info = { .yfact = 64, .datalen = 64, .callback = atp_complete_geyser_3_4, + .fuzz = 0, }; static const struct atp_info geyser4_info = { @@ -99,6 +104,7 @@ static const struct atp_info geyser4_info = { .yfact = 64, .datalen = 64, .callback = atp_complete_geyser_3_4, + .fuzz = 0, }; #define ATP_DEVICE(prod, info) \ @@ -155,9 +161,6 @@ MODULE_DEVICE_TABLE(usb, atp_table); #define ATP_XSENSORS 26 #define ATP_YSENSORS 16 -/* amount of fuzz this touchpad generates */ -#define ATP_FUZZ 16 - /* maximum pressure this driver will report */ #define ATP_PRESSURE 300 @@ -455,7 +458,7 @@ static void atp_detect_size(struct atp *dev) input_set_abs_params(dev->input, ABS_X, 0, (dev->info->xsensors_17 - 1) * dev->info->xfact - 1, - ATP_FUZZ, 0); + dev->info->fuzz, 0); break; } } @@ -843,10 +846,10 @@ static int atp_probe(struct usb_interface *iface, input_set_abs_params(input_dev, ABS_X, 0, (dev->info->xsensors - 1) * dev->info->xfact - 1, - ATP_FUZZ, 0); + dev->info->fuzz, 0); input_set_abs_params(input_dev, ABS_Y, 0, (dev->info->ysensors - 1) * dev->info->yfact - 1, - ATP_FUZZ, 0); + dev->info->fuzz, 0); input_set_abs_params(input_dev, ABS_PRESSURE, 0, ATP_PRESSURE, 0, 0); set_bit(EV_KEY, input_dev->evbit); -- cgit v1.2.3 From 1730d81460b929b1f217cb7d9cb193a7b0e58dd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lothar=20Wa=C3=9Fmann?= Date: Fri, 28 Mar 2014 09:20:08 -0700 Subject: Input: edt-ft5x06 - several cleanups; no functional change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - remove redundant parens - remove redundant type casts - fix mixed tab/space indentation Signed-off-by: Lothar Waßmann Acked-by: Fugang Duan Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/edt-ft5x06.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index 412a85ec9ba5..155ab3ba84ee 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -173,7 +173,7 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) x = ((buf[0] << 8) | buf[1]) & 0x0fff; y = ((buf[2] << 8) | buf[3]) & 0x0fff; id = (buf[2] >> 4) & 0x0f; - down = (type != TOUCH_EVENT_UP); + down = type != TOUCH_EVENT_UP; input_mt_slot(tsdata->input, id); input_mt_report_slot_state(tsdata->input, MT_TOOL_FINGER, down); @@ -257,7 +257,7 @@ static ssize_t edt_ft5x06_setting_show(struct device *dev, struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client); struct edt_ft5x06_attribute *attr = container_of(dattr, struct edt_ft5x06_attribute, dattr); - u8 *field = (u8 *)((char *)tsdata + attr->field_offset); + u8 *field = (u8 *)tsdata + attr->field_offset; int val; size_t count = 0; int error = 0; @@ -299,7 +299,7 @@ static ssize_t edt_ft5x06_setting_store(struct device *dev, struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client); struct edt_ft5x06_attribute *attr = container_of(dattr, struct edt_ft5x06_attribute, dattr); - u8 *field = (u8 *)((char *)tsdata + attr->field_offset); + u8 *field = (u8 *)tsdata + attr->field_offset; unsigned int val; int error; @@ -479,7 +479,7 @@ static int edt_ft5x06_debugfs_mode_set(void *data, u64 mode) if (mode != tsdata->factory_mode) { retval = mode ? edt_ft5x06_factory_mode(tsdata) : - edt_ft5x06_work_mode(tsdata); + edt_ft5x06_work_mode(tsdata); } mutex_unlock(&tsdata->mutex); @@ -568,7 +568,6 @@ out: return error ?: read; }; - static const struct file_operations debugfs_raw_data_fops = { .open = simple_open, .read = edt_ft5x06_debugfs_raw_data_read, @@ -614,8 +613,6 @@ edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata) #endif /* CONFIG_DEBUGFS */ - - static int edt_ft5x06_ts_reset(struct i2c_client *client, int reset_pin) { @@ -852,8 +849,8 @@ static SIMPLE_DEV_PM_OPS(edt_ft5x06_ts_pm_ops, edt_ft5x06_ts_suspend, edt_ft5x06_ts_resume); static const struct i2c_device_id edt_ft5x06_ts_id[] = { - { "edt-ft5x06", 0 }, - { } + { "edt-ft5x06", 0, }, + { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, edt_ft5x06_ts_id); -- cgit v1.2.3 From dac90dc23211996286c3b934057d0df77cfef0b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lothar=20Wa=C3=9Fmann?= Date: Fri, 28 Mar 2014 09:23:02 -0700 Subject: Input: edt-ft5x06 - add DT support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Lothar Waßmann Signed-off-by: Dmitry Torokhov --- .../bindings/input/touchscreen/edt-ft5x06.txt | 55 ++++++++ drivers/input/touchscreen/edt-ft5x06.c | 145 ++++++++++++++++----- 2 files changed, 169 insertions(+), 31 deletions(-) create mode 100644 Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt diff --git a/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt new file mode 100644 index 000000000000..76db96704a60 --- /dev/null +++ b/Documentation/devicetree/bindings/input/touchscreen/edt-ft5x06.txt @@ -0,0 +1,55 @@ +FocalTech EDT-FT5x06 Polytouch driver +===================================== + +There are 3 variants of the chip for various touch panel sizes +FT5206GE1 2.8" .. 3.8" +FT5306DE4 4.3" .. 7" +FT5406EE8 7" .. 8.9" + +The software interface is identical for all those chips, so that +currently there is no need for the driver to distinguish between the +different chips. Nevertheless distinct compatible strings are used so +that a distinction can be added if necessary without changing the DT +bindings. + + +Required properties: + - compatible: "edt,edt-ft5206" + or: "edt,edt-ft5306" + or: "edt,edt-ft5406" + + - reg: I2C slave address of the chip (0x38) + - interrupt-parent: a phandle pointing to the interrupt controller + serving the interrupt for this chip + - interrupts: interrupt specification for the touchdetect + interrupt + +Optional properties: + - reset-gpios: GPIO specification for the RESET input + - wake-gpios: GPIO specification for the WAKE input + + - pinctrl-names: should be "default" + - pinctrl-0: a phandle pointing to the pin settings for the + control gpios + + - threshold: allows setting the "click"-threshold in the range + from 20 to 80. + + - gain: allows setting the sensitivity in the range from 0 to + 31. Note that lower values indicate higher + sensitivity. + + - offset: allows setting the edge compensation in the range from + 0 to 31. + +Example: + polytouch: edt-ft5x06@38 { + compatible = "edt,edt-ft5406", "edt,edt-ft5x06"; + reg = <0x38>; + pinctrl-names = "default"; + pinctrl-0 = <&edt_ft5x06_pins>; + interrupt-parent = <&gpio2>; + interrupts = <5 0>; + reset-gpios = <&gpio2 6 1>; + wake-gpios = <&gpio4 9 0>; + }; diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index 155ab3ba84ee..9d61f1e36b33 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2012 Simon Budig, + * Lothar Waßmann (DT support) * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -33,6 +34,7 @@ #include #include #include +#include #include #include @@ -65,6 +67,10 @@ struct edt_ft5x06_ts_data { u16 num_x; u16 num_y; + int reset_pin; + int irq_pin; + int wake_pin; + #if defined(CONFIG_DEBUG_FS) struct dentry *debug_dir; u8 *raw_buffer; @@ -614,24 +620,38 @@ edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata) #endif /* CONFIG_DEBUGFS */ static int edt_ft5x06_ts_reset(struct i2c_client *client, - int reset_pin) + struct edt_ft5x06_ts_data *tsdata) { int error; - if (gpio_is_valid(reset_pin)) { + if (gpio_is_valid(tsdata->wake_pin)) { + error = devm_gpio_request_one(&client->dev, + tsdata->wake_pin, GPIOF_OUT_INIT_LOW, + "edt-ft5x06 wake"); + if (error) { + dev_err(&client->dev, + "Failed to request GPIO %d as wake pin, error %d\n", + tsdata->wake_pin, error); + return error; + } + + mdelay(5); + gpio_set_value(tsdata->wake_pin, 1); + } + if (gpio_is_valid(tsdata->reset_pin)) { /* this pulls reset down, enabling the low active reset */ - error = devm_gpio_request_one(&client->dev, reset_pin, - GPIOF_OUT_INIT_LOW, - "edt-ft5x06 reset"); + error = devm_gpio_request_one(&client->dev, + tsdata->reset_pin, GPIOF_OUT_INIT_LOW, + "edt-ft5x06 reset"); if (error) { dev_err(&client->dev, "Failed to request GPIO %d as reset pin, error %d\n", - reset_pin, error); + tsdata->reset_pin, error); return error; } mdelay(50); - gpio_set_value(reset_pin, 1); + gpio_set_value(tsdata->reset_pin, 1); mdelay(100); } @@ -672,6 +692,20 @@ static int edt_ft5x06_ts_identify(struct i2c_client *client, pdata->name <= edt_ft5x06_attr_##name.limit_high) \ edt_ft5x06_register_write(tsdata, reg, pdata->name) +#define EDT_GET_PROP(name, reg) { \ + u32 val; \ + if (of_property_read_u32(np, #name, &val) == 0) \ + edt_ft5x06_register_write(tsdata, reg, val); \ +} + +static void edt_ft5x06_ts_get_dt_defaults(struct device_node *np, + struct edt_ft5x06_ts_data *tsdata) +{ + EDT_GET_PROP(threshold, WORK_REGISTER_THRESHOLD); + EDT_GET_PROP(gain, WORK_REGISTER_GAIN); + EDT_GET_PROP(offset, WORK_REGISTER_OFFSET); +} + static void edt_ft5x06_ts_get_defaults(struct edt_ft5x06_ts_data *tsdata, const struct edt_ft5x06_platform_data *pdata) @@ -699,6 +733,30 @@ edt_ft5x06_ts_get_parameters(struct edt_ft5x06_ts_data *tsdata) tsdata->num_y = edt_ft5x06_register_read(tsdata, WORK_REGISTER_NUM_Y); } +#ifdef CONFIG_OF +static int edt_ft5x06_i2c_ts_probe_dt(struct device *dev, + struct edt_ft5x06_ts_data *tsdata) +{ + struct device_node *np = dev->of_node; + + /* + * irq_pin is not needed for DT setup. + * irq is associated via 'interrupts' property in DT + */ + tsdata->irq_pin = -EINVAL; + tsdata->reset_pin = of_get_named_gpio(np, "reset-gpios", 0); + tsdata->wake_pin = of_get_named_gpio(np, "wake-gpios", 0); + + return 0; +} +#else +static inline int edt_ft5x06_i2c_ts_probe_dt(struct device *dev, + struct edt_ft5x06_ts_data *tsdata) +{ + return -ENODEV; +} +#endif + static int edt_ft5x06_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -711,32 +769,40 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, dev_dbg(&client->dev, "probing for EDT FT5x06 I2C\n"); + tsdata = devm_kzalloc(&client->dev, sizeof(*tsdata), GFP_KERNEL); + if (!tsdata) { + dev_err(&client->dev, "failed to allocate driver data.\n"); + return -ENOMEM; + } + if (!pdata) { - dev_err(&client->dev, "no platform data?\n"); - return -EINVAL; + error = edt_ft5x06_i2c_ts_probe_dt(&client->dev, tsdata); + if (error) { + dev_err(&client->dev, + "DT probe failed and no platform data present\n"); + return error; + } + } else { + tsdata->reset_pin = pdata->reset_pin; + tsdata->irq_pin = pdata->irq_pin; + tsdata->wake_pin = -EINVAL; } - error = edt_ft5x06_ts_reset(client, pdata->reset_pin); + error = edt_ft5x06_ts_reset(client, tsdata); if (error) return error; - if (gpio_is_valid(pdata->irq_pin)) { - error = devm_gpio_request_one(&client->dev, pdata->irq_pin, - GPIOF_IN, "edt-ft5x06 irq"); + if (gpio_is_valid(tsdata->irq_pin)) { + error = devm_gpio_request_one(&client->dev, tsdata->irq_pin, + GPIOF_IN, "edt-ft5x06 irq"); if (error) { dev_err(&client->dev, "Failed to request GPIO %d, error %d\n", - pdata->irq_pin, error); + tsdata->irq_pin, error); return error; } } - tsdata = devm_kzalloc(&client->dev, sizeof(*tsdata), GFP_KERNEL); - if (!tsdata) { - dev_err(&client->dev, "failed to allocate driver data.\n"); - return -ENOMEM; - } - input = devm_input_allocate_device(&client->dev); if (!input) { dev_err(&client->dev, "failed to allocate input device.\n"); @@ -754,7 +820,11 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, return error; } - edt_ft5x06_ts_get_defaults(tsdata, pdata); + if (!pdata) + edt_ft5x06_ts_get_dt_defaults(client->dev.of_node, tsdata); + else + edt_ft5x06_ts_get_defaults(tsdata, pdata); + edt_ft5x06_ts_get_parameters(tsdata); dev_dbg(&client->dev, @@ -784,10 +854,10 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, input_set_drvdata(input, tsdata); i2c_set_clientdata(client, tsdata); - error = devm_request_threaded_irq(&client->dev, client->irq, - NULL, edt_ft5x06_ts_isr, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - client->name, tsdata); + error = devm_request_threaded_irq(&client->dev, client->irq, NULL, + edt_ft5x06_ts_isr, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + client->name, tsdata); if (error) { dev_err(&client->dev, "Unable to request touchscreen IRQ.\n"); return error; @@ -798,19 +868,21 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, return error; error = input_register_device(input); - if (error) { - sysfs_remove_group(&client->dev.kobj, &edt_ft5x06_attr_group); - return error; - } + if (error) + goto err_remove_attrs; edt_ft5x06_ts_prepare_debugfs(tsdata, dev_driver_string(&client->dev)); device_init_wakeup(&client->dev, 1); dev_dbg(&client->dev, - "EDT FT5x06 initialized: IRQ pin %d, Reset pin %d.\n", - pdata->irq_pin, pdata->reset_pin); + "EDT FT5x06 initialized: IRQ %d, WAKE pin %d, Reset pin %d.\n", + client->irq, tsdata->wake_pin, tsdata->reset_pin); return 0; + +err_remove_attrs: + sysfs_remove_group(&client->dev.kobj, &edt_ft5x06_attr_group); + return error; } static int edt_ft5x06_ts_remove(struct i2c_client *client) @@ -854,10 +926,21 @@ static const struct i2c_device_id edt_ft5x06_ts_id[] = { }; MODULE_DEVICE_TABLE(i2c, edt_ft5x06_ts_id); +#ifdef CONFIG_OF +static const struct of_device_id edt_ft5x06_of_match[] = { + { .compatible = "edt,edt-ft5206", }, + { .compatible = "edt,edt-ft5306", }, + { .compatible = "edt,edt-ft5406", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, edt_ft5x06_of_match); +#endif + static struct i2c_driver edt_ft5x06_ts_driver = { .driver = { .owner = THIS_MODULE, .name = "edt_ft5x06", + .of_match_table = of_match_ptr(edt_ft5x06_of_match), .pm = &edt_ft5x06_ts_pm_ops, }, .id_table = edt_ft5x06_ts_id, -- cgit v1.2.3 From c0808467494318029f71185b42b61f1ae153afdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lothar=20Wa=C3=9Fmann?= Date: Fri, 28 Mar 2014 09:27:50 -0700 Subject: Input: edt-ft5x06 - adjust delays to conform datasheet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The FT5x06 datasheet specifies a minimum reset width of 5ms and a delay between deassertion of reset and start of reporting of 300ms. Adjust the delays to conform to the datasheet. With the original delays I sometimes experienced communication timeouts when initializing the controller. Signed-off-by: Lothar Waßmann Acked-by: Fugang Duan Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/edt-ft5x06.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index 9d61f1e36b33..fd18e580870a 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -635,7 +635,7 @@ static int edt_ft5x06_ts_reset(struct i2c_client *client, return error; } - mdelay(5); + msleep(5); gpio_set_value(tsdata->wake_pin, 1); } if (gpio_is_valid(tsdata->reset_pin)) { @@ -650,9 +650,9 @@ static int edt_ft5x06_ts_reset(struct i2c_client *client, return error; } - mdelay(50); + msleep(5); gpio_set_value(tsdata->reset_pin, 1); - mdelay(100); + msleep(300); } return 0; -- cgit v1.2.3 From ee3e946e31e6df0f9845f9b3d527252003603530 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lothar=20Wa=C3=9Fmann?= Date: Fri, 28 Mar 2014 09:28:17 -0700 Subject: Input: edt-ft5x06 - ignore touchdown events MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The chip may report invalid coordinates on touchdown events, so don't report the initial touchdown event. Signed-off-by: Lothar Waßmann Acked-by: Fugang Duan Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/edt-ft5x06.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index fd18e580870a..dd94b9eea6e7 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -176,6 +176,10 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) if (type == TOUCH_EVENT_RESERVED) continue; + /* ignore TOUCH_DOWN events, might have bogus coordinates */ + if (type == TOUCH_EVENT_DOWN) + continue; + x = ((buf[0] << 8) | buf[1]) & 0x0fff; y = ((buf[2] << 8) | buf[3]) & 0x0fff; id = (buf[2] >> 4) & 0x0f; -- cgit v1.2.3 From fd335ab04b3f1b3309dfbfea71a1a79a7bacc4ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lothar=20Wa=C3=9Fmann?= Date: Fri, 28 Mar 2014 09:31:14 -0700 Subject: Input: edt-ft5x06 - add support for M09 firmware version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is a new firmware version for the EDT-FT5x06 chip. Add support for detecting the firmware version and handle the differences appropriately. Signed-off-by: Lothar Waßmann Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/edt-ft5x06.c | 358 +++++++++++++++++++++++++-------- 1 file changed, 276 insertions(+), 82 deletions(-) diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index dd94b9eea6e7..75b666bd2654 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2012 Simon Budig, + * Daniel Wagener (M09 firmware support) * Lothar Waßmann (DT support) * * This software is licensed under the terms of the GNU General Public @@ -47,6 +48,14 @@ #define WORK_REGISTER_NUM_X 0x33 #define WORK_REGISTER_NUM_Y 0x34 +#define M09_REGISTER_THRESHOLD 0x80 +#define M09_REGISTER_GAIN 0x92 +#define M09_REGISTER_OFFSET 0x93 +#define M09_REGISTER_NUM_X 0x94 +#define M09_REGISTER_NUM_Y 0x95 + +#define NO_REGISTER 0xff + #define WORK_REGISTER_OPMODE 0x3c #define FACTORY_REGISTER_OPMODE 0x01 @@ -61,6 +70,20 @@ #define EDT_RAW_DATA_RETRIES 100 #define EDT_RAW_DATA_DELAY 1 /* msec */ +enum edt_ver { + M06, + M09, +}; + +struct edt_reg_addr { + int reg_threshold; + int reg_report_rate; + int reg_gain; + int reg_offset; + int reg_num_x; + int reg_num_y; +}; + struct edt_ft5x06_ts_data { struct i2c_client *client; struct input_dev *input; @@ -85,6 +108,9 @@ struct edt_ft5x06_ts_data { int report_rate; char name[EDT_NAME_LEN]; + + struct edt_reg_addr reg_addr; + enum edt_ver version; }; static int edt_ft5x06_ts_readwrite(struct i2c_client *client, @@ -142,33 +168,58 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) { struct edt_ft5x06_ts_data *tsdata = dev_id; struct device *dev = &tsdata->client->dev; - u8 cmd = 0xf9; - u8 rdbuf[26]; + u8 cmd; + u8 rdbuf[29]; int i, type, x, y, id; + int offset, tplen, datalen; int error; + switch (tsdata->version) { + case M06: + cmd = 0xf9; /* tell the controller to send touch data */ + offset = 5; /* where the actual touch data starts */ + tplen = 4; /* data comes in so called frames */ + datalen = 26; /* how much bytes to listen for */ + break; + + case M09: + cmd = 0x02; + offset = 1; + tplen = 6; + datalen = 29; + break; + + default: + goto out; + } + memset(rdbuf, 0, sizeof(rdbuf)); error = edt_ft5x06_ts_readwrite(tsdata->client, sizeof(cmd), &cmd, - sizeof(rdbuf), rdbuf); + datalen, rdbuf); if (error) { dev_err_ratelimited(dev, "Unable to fetch data, error: %d\n", error); goto out; } - if (rdbuf[0] != 0xaa || rdbuf[1] != 0xaa || rdbuf[2] != 26) { - dev_err_ratelimited(dev, "Unexpected header: %02x%02x%02x!\n", - rdbuf[0], rdbuf[1], rdbuf[2]); - goto out; - } + /* M09 does not send header or CRC */ + if (tsdata->version == M06) { + if (rdbuf[0] != 0xaa || rdbuf[1] != 0xaa || + rdbuf[2] != datalen) { + dev_err_ratelimited(dev, + "Unexpected header: %02x%02x%02x!\n", + rdbuf[0], rdbuf[1], rdbuf[2]); + goto out; + } - if (!edt_ft5x06_ts_check_crc(tsdata, rdbuf, 26)) - goto out; + if (!edt_ft5x06_ts_check_crc(tsdata, rdbuf, datalen)) + goto out; + } for (i = 0; i < MAX_SUPPORT_POINTS; i++) { - u8 *buf = &rdbuf[i * 4 + 5]; + u8 *buf = &rdbuf[i * tplen + offset]; bool down; type = buf[0] >> 6; @@ -176,8 +227,8 @@ static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id) if (type == TOUCH_EVENT_RESERVED) continue; - /* ignore TOUCH_DOWN events, might have bogus coordinates */ - if (type == TOUCH_EVENT_DOWN) + /* M06 sometimes sends bogus coordinates in TOUCH_DOWN */ + if (tsdata->version == M06 && type == TOUCH_EVENT_DOWN) continue; x = ((buf[0] << 8) | buf[1]) & 0x0fff; @@ -207,12 +258,25 @@ static int edt_ft5x06_register_write(struct edt_ft5x06_ts_data *tsdata, { u8 wrbuf[4]; - wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc; - wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f; - wrbuf[2] = value; - wrbuf[3] = wrbuf[0] ^ wrbuf[1] ^ wrbuf[2]; - - return edt_ft5x06_ts_readwrite(tsdata->client, 4, wrbuf, 0, NULL); + switch (tsdata->version) { + case M06: + wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc; + wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f; + wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f; + wrbuf[2] = value; + wrbuf[3] = wrbuf[0] ^ wrbuf[1] ^ wrbuf[2]; + return edt_ft5x06_ts_readwrite(tsdata->client, 4, + wrbuf, 0, NULL); + case M09: + wrbuf[0] = addr; + wrbuf[1] = value; + + return edt_ft5x06_ts_readwrite(tsdata->client, 3, + wrbuf, 0, NULL); + + default: + return -EINVAL; + } } static int edt_ft5x06_register_read(struct edt_ft5x06_ts_data *tsdata, @@ -221,19 +285,35 @@ static int edt_ft5x06_register_read(struct edt_ft5x06_ts_data *tsdata, u8 wrbuf[2], rdbuf[2]; int error; - wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc; - wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f; - wrbuf[1] |= tsdata->factory_mode ? 0x80 : 0x40; + switch (tsdata->version) { + case M06: + wrbuf[0] = tsdata->factory_mode ? 0xf3 : 0xfc; + wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f; + wrbuf[1] |= tsdata->factory_mode ? 0x80 : 0x40; - error = edt_ft5x06_ts_readwrite(tsdata->client, 2, wrbuf, 2, rdbuf); - if (error) + error = edt_ft5x06_ts_readwrite(tsdata->client, + 2, wrbuf, 2, rdbuf); return error; - if ((wrbuf[0] ^ wrbuf[1] ^ rdbuf[0]) != rdbuf[1]) { - dev_err(&tsdata->client->dev, - "crc error: 0x%02x expected, got 0x%02x\n", - wrbuf[0] ^ wrbuf[1] ^ rdbuf[0], rdbuf[1]); - return -EIO; + if ((wrbuf[0] ^ wrbuf[1] ^ rdbuf[0]) != rdbuf[1]) { + dev_err(&tsdata->client->dev, + "crc error: 0x%02x expected, got 0x%02x\n", + wrbuf[0] ^ wrbuf[1] ^ rdbuf[0], + rdbuf[1]); + return -EIO; + } + break; + + case M09: + wrbuf[0] = addr; + error = edt_ft5x06_ts_readwrite(tsdata->client, 1, + wrbuf, 1, rdbuf); + if (error) + return error; + break; + + default: + return -EINVAL; } return rdbuf[0]; @@ -244,19 +324,21 @@ struct edt_ft5x06_attribute { size_t field_offset; u8 limit_low; u8 limit_high; - u8 addr; + u8 addr_m06; + u8 addr_m09; }; -#define EDT_ATTR(_field, _mode, _addr, _limit_low, _limit_high) \ +#define EDT_ATTR(_field, _mode, _addr_m06, _addr_m09, \ + _limit_low, _limit_high) \ struct edt_ft5x06_attribute edt_ft5x06_attr_##_field = { \ .dattr = __ATTR(_field, _mode, \ edt_ft5x06_setting_show, \ edt_ft5x06_setting_store), \ - .field_offset = \ - offsetof(struct edt_ft5x06_ts_data, _field), \ + .field_offset = offsetof(struct edt_ft5x06_ts_data, _field), \ + .addr_m06 = _addr_m06, \ + .addr_m09 = _addr_m09, \ .limit_low = _limit_low, \ .limit_high = _limit_high, \ - .addr = _addr, \ } static ssize_t edt_ft5x06_setting_show(struct device *dev, @@ -271,6 +353,7 @@ static ssize_t edt_ft5x06_setting_show(struct device *dev, int val; size_t count = 0; int error = 0; + u8 addr; mutex_lock(&tsdata->mutex); @@ -279,15 +362,33 @@ static ssize_t edt_ft5x06_setting_show(struct device *dev, goto out; } - val = edt_ft5x06_register_read(tsdata, attr->addr); - if (val < 0) { - error = val; - dev_err(&tsdata->client->dev, - "Failed to fetch attribute %s, error %d\n", - dattr->attr.name, error); + switch (tsdata->version) { + case M06: + addr = attr->addr_m06; + break; + + case M09: + addr = attr->addr_m09; + break; + + default: + error = -ENODEV; goto out; } + if (addr != NO_REGISTER) { + val = edt_ft5x06_register_read(tsdata, addr); + if (val < 0) { + error = val; + dev_err(&tsdata->client->dev, + "Failed to fetch attribute %s, error %d\n", + dattr->attr.name, error); + goto out; + } + } else { + val = *field; + } + if (val != *field) { dev_warn(&tsdata->client->dev, "%s: read (%d) and stored value (%d) differ\n", @@ -312,6 +413,7 @@ static ssize_t edt_ft5x06_setting_store(struct device *dev, u8 *field = (u8 *)tsdata + attr->field_offset; unsigned int val; int error; + u8 addr; mutex_lock(&tsdata->mutex); @@ -329,14 +431,29 @@ static ssize_t edt_ft5x06_setting_store(struct device *dev, goto out; } - error = edt_ft5x06_register_write(tsdata, attr->addr, val); - if (error) { - dev_err(&tsdata->client->dev, - "Failed to update attribute %s, error: %d\n", - dattr->attr.name, error); + switch (tsdata->version) { + case M06: + addr = attr->addr_m06; + break; + + case M09: + addr = attr->addr_m09; + break; + + default: + error = -ENODEV; goto out; } + if (addr != NO_REGISTER) { + error = edt_ft5x06_register_write(tsdata, addr, val); + if (error) { + dev_err(&tsdata->client->dev, + "Failed to update attribute %s, error: %d\n", + dattr->attr.name, error); + goto out; + } + } *field = val; out: @@ -344,12 +461,14 @@ out: return error ?: count; } -static EDT_ATTR(gain, S_IWUSR | S_IRUGO, WORK_REGISTER_GAIN, 0, 31); -static EDT_ATTR(offset, S_IWUSR | S_IRUGO, WORK_REGISTER_OFFSET, 0, 31); -static EDT_ATTR(threshold, S_IWUSR | S_IRUGO, - WORK_REGISTER_THRESHOLD, 20, 80); -static EDT_ATTR(report_rate, S_IWUSR | S_IRUGO, - WORK_REGISTER_REPORT_RATE, 3, 14); +static EDT_ATTR(gain, S_IWUSR | S_IRUGO, WORK_REGISTER_GAIN, + M09_REGISTER_GAIN, 0, 31); +static EDT_ATTR(offset, S_IWUSR | S_IRUGO, WORK_REGISTER_OFFSET, + M09_REGISTER_OFFSET, 0, 31); +static EDT_ATTR(threshold, S_IWUSR | S_IRUGO, WORK_REGISTER_THRESHOLD, + M09_REGISTER_THRESHOLD, 20, 80); +static EDT_ATTR(report_rate, S_IWUSR | S_IRUGO, WORK_REGISTER_REPORT_RATE, + NO_REGISTER, 3, 14); static struct attribute *edt_ft5x06_attrs[] = { &edt_ft5x06_attr_gain.dattr.attr, @@ -384,6 +503,9 @@ static int edt_ft5x06_factory_mode(struct edt_ft5x06_ts_data *tsdata) } /* mode register is 0x3c when in the work mode */ + if (tsdata->version == M09) + goto m09_out; + error = edt_ft5x06_register_write(tsdata, WORK_REGISTER_OPMODE, 0x03); if (error) { dev_err(&client->dev, @@ -416,12 +538,18 @@ err_out: enable_irq(client->irq); return error; + +m09_out: + dev_err(&client->dev, "No factory mode support for M09\n"); + return -EINVAL; + } static int edt_ft5x06_work_mode(struct edt_ft5x06_ts_data *tsdata) { struct i2c_client *client = tsdata->client; int retries = EDT_SWITCH_MODE_RETRIES; + struct edt_reg_addr *reg_addr = &tsdata->reg_addr; int ret; int error; @@ -454,13 +582,14 @@ static int edt_ft5x06_work_mode(struct edt_ft5x06_ts_data *tsdata) tsdata->raw_buffer = NULL; /* restore parameters */ - edt_ft5x06_register_write(tsdata, WORK_REGISTER_THRESHOLD, + edt_ft5x06_register_write(tsdata, reg_addr->reg_threshold, tsdata->threshold); - edt_ft5x06_register_write(tsdata, WORK_REGISTER_GAIN, + edt_ft5x06_register_write(tsdata, reg_addr->reg_gain, tsdata->gain); - edt_ft5x06_register_write(tsdata, WORK_REGISTER_OFFSET, + edt_ft5x06_register_write(tsdata, reg_addr->reg_offset, tsdata->offset); - edt_ft5x06_register_write(tsdata, WORK_REGISTER_REPORT_RATE, + if (reg_addr->reg_report_rate) + edt_ft5x06_register_write(tsdata, reg_addr->reg_report_rate, tsdata->report_rate); enable_irq(client->irq); @@ -663,30 +792,60 @@ static int edt_ft5x06_ts_reset(struct i2c_client *client, } static int edt_ft5x06_ts_identify(struct i2c_client *client, - char *model_name, - char *fw_version) + struct edt_ft5x06_ts_data *tsdata, + char *fw_version) { u8 rdbuf[EDT_NAME_LEN]; char *p; int error; + char *model_name = tsdata->name; + /* see what we find if we assume it is a M06 * + * if we get less than EDT_NAME_LEN, we don't want + * to have garbage in there + */ + memset(rdbuf, 0, sizeof(rdbuf)); error = edt_ft5x06_ts_readwrite(client, 1, "\xbb", EDT_NAME_LEN - 1, rdbuf); if (error) return error; - /* remove last '$' end marker */ - rdbuf[EDT_NAME_LEN - 1] = '\0'; - if (rdbuf[EDT_NAME_LEN - 2] == '$') - rdbuf[EDT_NAME_LEN - 2] = '\0'; + /* if we find something consistent, stay with that assumption + * at least M09 won't send 3 bytes here + */ + if (!(strnicmp(rdbuf + 1, "EP0", 3))) { + tsdata->version = M06; + + /* remove last '$' end marker */ + rdbuf[EDT_NAME_LEN - 1] = '\0'; + if (rdbuf[EDT_NAME_LEN - 2] == '$') + rdbuf[EDT_NAME_LEN - 2] = '\0'; + + /* look for Model/Version separator */ + p = strchr(rdbuf, '*'); + if (p) + *p++ = '\0'; + strlcpy(model_name, rdbuf + 1, EDT_NAME_LEN); + strlcpy(fw_version, p ? p : "", EDT_NAME_LEN); + } else { + /* since there are only two versions around (M06, M09) */ + tsdata->version = M09; + + error = edt_ft5x06_ts_readwrite(client, 1, "\xA6", + 2, rdbuf); + if (error) + return error; - /* look for Model/Version separator */ - p = strchr(rdbuf, '*'); - if (p) - *p++ = '\0'; + strlcpy(fw_version, rdbuf, 2); - strlcpy(model_name, rdbuf + 1, EDT_NAME_LEN); - strlcpy(fw_version, p ? p : "", EDT_NAME_LEN); + error = edt_ft5x06_ts_readwrite(client, 1, "\xA8", + 1, rdbuf); + if (error) + return error; + + snprintf(model_name, EDT_NAME_LEN, "EP0%i%i0M09", + rdbuf[0] >> 4, rdbuf[0] & 0x0F); + } return 0; } @@ -705,36 +864,69 @@ static int edt_ft5x06_ts_identify(struct i2c_client *client, static void edt_ft5x06_ts_get_dt_defaults(struct device_node *np, struct edt_ft5x06_ts_data *tsdata) { - EDT_GET_PROP(threshold, WORK_REGISTER_THRESHOLD); - EDT_GET_PROP(gain, WORK_REGISTER_GAIN); - EDT_GET_PROP(offset, WORK_REGISTER_OFFSET); + struct edt_reg_addr *reg_addr = &tsdata->reg_addr; + + EDT_GET_PROP(threshold, reg_addr->reg_threshold); + EDT_GET_PROP(gain, reg_addr->reg_gain); + EDT_GET_PROP(offset, reg_addr->reg_offset); } static void edt_ft5x06_ts_get_defaults(struct edt_ft5x06_ts_data *tsdata, const struct edt_ft5x06_platform_data *pdata) { + struct edt_reg_addr *reg_addr = &tsdata->reg_addr; + if (!pdata->use_parameters) return; /* pick up defaults from the platform data */ - EDT_ATTR_CHECKSET(threshold, WORK_REGISTER_THRESHOLD); - EDT_ATTR_CHECKSET(gain, WORK_REGISTER_GAIN); - EDT_ATTR_CHECKSET(offset, WORK_REGISTER_OFFSET); - EDT_ATTR_CHECKSET(report_rate, WORK_REGISTER_REPORT_RATE); + EDT_ATTR_CHECKSET(threshold, reg_addr->reg_threshold); + EDT_ATTR_CHECKSET(gain, reg_addr->reg_gain); + EDT_ATTR_CHECKSET(offset, reg_addr->reg_offset); + if (reg_addr->reg_report_rate != NO_REGISTER) + EDT_ATTR_CHECKSET(report_rate, reg_addr->reg_report_rate); } static void edt_ft5x06_ts_get_parameters(struct edt_ft5x06_ts_data *tsdata) { + struct edt_reg_addr *reg_addr = &tsdata->reg_addr; + tsdata->threshold = edt_ft5x06_register_read(tsdata, - WORK_REGISTER_THRESHOLD); - tsdata->gain = edt_ft5x06_register_read(tsdata, WORK_REGISTER_GAIN); - tsdata->offset = edt_ft5x06_register_read(tsdata, WORK_REGISTER_OFFSET); - tsdata->report_rate = edt_ft5x06_register_read(tsdata, - WORK_REGISTER_REPORT_RATE); - tsdata->num_x = edt_ft5x06_register_read(tsdata, WORK_REGISTER_NUM_X); - tsdata->num_y = edt_ft5x06_register_read(tsdata, WORK_REGISTER_NUM_Y); + reg_addr->reg_threshold); + tsdata->gain = edt_ft5x06_register_read(tsdata, reg_addr->reg_gain); + tsdata->offset = edt_ft5x06_register_read(tsdata, reg_addr->reg_offset); + if (reg_addr->reg_report_rate != NO_REGISTER) + tsdata->report_rate = edt_ft5x06_register_read(tsdata, + reg_addr->reg_report_rate); + tsdata->num_x = edt_ft5x06_register_read(tsdata, reg_addr->reg_num_x); + tsdata->num_y = edt_ft5x06_register_read(tsdata, reg_addr->reg_num_y); +} + +static void +edt_ft5x06_ts_set_regs(struct edt_ft5x06_ts_data *tsdata) +{ + struct edt_reg_addr *reg_addr = &tsdata->reg_addr; + + switch (tsdata->version) { + case M06: + reg_addr->reg_threshold = WORK_REGISTER_THRESHOLD; + reg_addr->reg_report_rate = WORK_REGISTER_REPORT_RATE; + reg_addr->reg_gain = WORK_REGISTER_GAIN; + reg_addr->reg_offset = WORK_REGISTER_OFFSET; + reg_addr->reg_num_x = WORK_REGISTER_NUM_X; + reg_addr->reg_num_y = WORK_REGISTER_NUM_Y; + break; + + case M09: + reg_addr->reg_threshold = M09_REGISTER_THRESHOLD; + reg_addr->reg_gain = M09_REGISTER_GAIN; + reg_addr->reg_offset = M09_REGISTER_OFFSET; + reg_addr->reg_num_x = M09_REGISTER_NUM_X; + reg_addr->reg_num_y = M09_REGISTER_NUM_Y; + break; + } } #ifdef CONFIG_OF @@ -818,12 +1010,14 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client, tsdata->input = input; tsdata->factory_mode = false; - error = edt_ft5x06_ts_identify(client, tsdata->name, fw_version); + error = edt_ft5x06_ts_identify(client, tsdata, fw_version); if (error) { dev_err(&client->dev, "touchscreen probe failed\n"); return error; } + edt_ft5x06_ts_set_regs(tsdata); + if (!pdata) edt_ft5x06_ts_get_dt_defaults(client->dev.of_node, tsdata); else -- cgit v1.2.3 From e70f18e1c7868bebad16b21b4821b2adb8c1abf8 Mon Sep 17 00:00:00 2001 From: Alexander Shiyan Date: Fri, 28 Mar 2014 09:37:14 -0700 Subject: Input: add new driver for ARM CLPS711X keypad This patch adds a new driver for keypad for Cirrus Logic CLPS711X CPUs. Target CPU contain keyboard interface which can scan 8 column lines, so we can read row GPIOs to read status and determine asserted state. Signed-off-by: Alexander Shiyan Signed-off-by: Dmitry Torokhov --- .../devicetree/bindings/input/clps711x-keypad.txt | 27 +++ drivers/input/keyboard/Kconfig | 12 ++ drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/clps711x-keypad.c | 207 +++++++++++++++++++++ 4 files changed, 247 insertions(+) create mode 100644 Documentation/devicetree/bindings/input/clps711x-keypad.txt create mode 100644 drivers/input/keyboard/clps711x-keypad.c diff --git a/Documentation/devicetree/bindings/input/clps711x-keypad.txt b/Documentation/devicetree/bindings/input/clps711x-keypad.txt new file mode 100644 index 000000000000..e68d2bbc6c07 --- /dev/null +++ b/Documentation/devicetree/bindings/input/clps711x-keypad.txt @@ -0,0 +1,27 @@ +* Cirrus Logic CLPS711X matrix keypad device tree bindings + +Required Properties: +- compatible: Shall contain "cirrus,clps711x-keypad". +- row-gpios: List of GPIOs used as row lines. +- poll-interval: Poll interval time in milliseconds. +- linux,keymap: The definition can be found at + bindings/input/matrix-keymap.txt. + +Optional Properties: +- autorepeat: Enable autorepeat feature. + +Example: + keypad { + compatible = "cirrus,ep7312-keypad", "cirrus,clps711x-keypad"; + autorepeat; + poll-interval = <120>; + row-gpios = <&porta 0 0>, + <&porta 1 0>; + + linux,keymap = < + MATRIX_KEY(0, 0, KEY_UP) + MATRIX_KEY(0, 1, KEY_DOWN) + MATRIX_KEY(1, 0, KEY_LEFT) + MATRIX_KEY(1, 1, KEY_RIGHT) + >; + }; diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 935dcaf16646..76842d7dc2e3 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -151,6 +151,18 @@ config KEYBOARD_BFIN To compile this driver as a module, choose M here: the module will be called bf54x-keys. +config KEYBOARD_CLPS711X + tristate "CLPS711X Keypad support" + depends on OF_GPIO && (ARCH_CLPS711X || COMPILE_TEST) + select INPUT_MATRIXKMAP + select INPUT_POLLDEV + help + Say Y here to enable the matrix keypad on the Cirrus Logic + CLPS711X CPUs. + + To compile this driver as a module, choose M here: the + module will be called clps711x-keypad. + config KEYBOARD_LKKBD tristate "DECstation/VAXstation LK201/LK401 keyboard" select SERIO diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 81014d94a0c1..11cff7b84b47 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o +obj-$(CONFIG_KEYBOARD_CLPS711X) += clps711x-keypad.o obj-$(CONFIG_KEYBOARD_CROS_EC) += cros_ec_keyb.o obj-$(CONFIG_KEYBOARD_DAVINCI) += davinci_keyscan.o obj-$(CONFIG_KEYBOARD_EP93XX) += ep93xx_keypad.o diff --git a/drivers/input/keyboard/clps711x-keypad.c b/drivers/input/keyboard/clps711x-keypad.c new file mode 100644 index 000000000000..3955aecee44b --- /dev/null +++ b/drivers/input/keyboard/clps711x-keypad.c @@ -0,0 +1,207 @@ +/* + * Cirrus Logic CLPS711X Keypad driver + * + * Copyright (C) 2014 Alexander Shiyan + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CLPS711X_KEYPAD_COL_COUNT 8 + +struct clps711x_gpio_data { + struct gpio_desc *desc; + DECLARE_BITMAP(last_state, CLPS711X_KEYPAD_COL_COUNT); +}; + +struct clps711x_keypad_data { + struct regmap *syscon; + int row_count; + unsigned int row_shift; + struct clps711x_gpio_data *gpio_data; +}; + +static void clps711x_keypad_poll(struct input_polled_dev *dev) +{ + const unsigned short *keycodes = dev->input->keycode; + struct clps711x_keypad_data *priv = dev->private; + bool sync = false; + int col, row; + + for (col = 0; col < CLPS711X_KEYPAD_COL_COUNT; col++) { + /* Assert column */ + regmap_update_bits(priv->syscon, SYSCON_OFFSET, + SYSCON1_KBDSCAN_MASK, + SYSCON1_KBDSCAN(8 + col)); + + /* Scan rows */ + for (row = 0; row < priv->row_count; row++) { + struct clps711x_gpio_data *data = &priv->gpio_data[row]; + bool state, state1; + + /* Read twice for protection against fluctuations */ + do { + state = gpiod_get_value_cansleep(data->desc); + cond_resched(); + state1 = gpiod_get_value_cansleep(data->desc); + } while (state != state1); + + if (test_bit(col, data->last_state) != state) { + int code = MATRIX_SCAN_CODE(row, col, + priv->row_shift); + + if (state) { + set_bit(col, data->last_state); + input_event(dev->input, EV_MSC, + MSC_SCAN, code); + } else { + clear_bit(col, data->last_state); + } + + if (keycodes[code]) + input_report_key(dev->input, + keycodes[code], state); + sync = true; + } + } + + /* Set all columns to low */ + regmap_update_bits(priv->syscon, SYSCON_OFFSET, + SYSCON1_KBDSCAN_MASK, SYSCON1_KBDSCAN(1)); + } + + if (sync) + input_sync(dev->input); +} + +static int clps711x_keypad_probe(struct platform_device *pdev) +{ + struct clps711x_keypad_data *priv; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct input_polled_dev *poll_dev; + u32 poll_interval; + int i, err; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->syscon = + syscon_regmap_lookup_by_compatible("cirrus,clps711x-syscon1"); + if (IS_ERR(priv->syscon)) + return PTR_ERR(priv->syscon); + + priv->row_count = of_gpio_named_count(np, "row-gpios"); + if (priv->row_count < 1) + return -EINVAL; + + priv->gpio_data = devm_kzalloc(dev, + sizeof(*priv->gpio_data) * priv->row_count, + GFP_KERNEL); + if (!priv->gpio_data) + return -ENOMEM; + + priv->row_shift = get_count_order(CLPS711X_KEYPAD_COL_COUNT); + + for (i = 0; i < priv->row_count; i++) { + struct clps711x_gpio_data *data = &priv->gpio_data[i]; + + data->desc = devm_gpiod_get_index(dev, "row", i); + if (!data->desc) + return -EINVAL; + + if (IS_ERR(data->desc)) + return PTR_ERR(data->desc); + + gpiod_direction_input(data->desc); + } + + err = of_property_read_u32(np, "poll-interval", &poll_interval); + if (err) + return err; + + poll_dev = input_allocate_polled_device(); + if (!poll_dev) + return -ENOMEM; + + poll_dev->private = priv; + poll_dev->poll = clps711x_keypad_poll; + poll_dev->poll_interval = poll_interval; + poll_dev->input->name = pdev->name; + poll_dev->input->dev.parent = dev; + poll_dev->input->id.bustype = BUS_HOST; + poll_dev->input->id.vendor = 0x0001; + poll_dev->input->id.product = 0x0001; + poll_dev->input->id.version = 0x0100; + + err = matrix_keypad_build_keymap(NULL, NULL, priv->row_count, + CLPS711X_KEYPAD_COL_COUNT, + NULL, poll_dev->input); + if (err) + goto out_err; + + input_set_capability(poll_dev->input, EV_MSC, MSC_SCAN); + if (of_property_read_bool(np, "autorepeat")) + __set_bit(EV_REP, poll_dev->input->evbit); + + platform_set_drvdata(pdev, poll_dev); + + /* Set all columns to low */ + regmap_update_bits(priv->syscon, SYSCON_OFFSET, SYSCON1_KBDSCAN_MASK, + SYSCON1_KBDSCAN(1)); + + err = input_register_polled_device(poll_dev); + if (err) + goto out_err; + + return 0; + +out_err: + input_free_polled_device(poll_dev); + return err; +} + +static int clps711x_keypad_remove(struct platform_device *pdev) +{ + struct input_polled_dev *poll_dev = platform_get_drvdata(pdev); + + input_unregister_polled_device(poll_dev); + input_free_polled_device(poll_dev); + + return 0; +} + +static struct of_device_id clps711x_keypad_of_match[] = { + { .compatible = "cirrus,clps711x-keypad", }, + { } +}; +MODULE_DEVICE_TABLE(of, clps711x_keypad_of_match); + +static struct platform_driver clps711x_keypad_driver = { + .driver = { + .name = "clps711x-keypad", + .owner = THIS_MODULE, + .of_match_table = clps711x_keypad_of_match, + }, + .probe = clps711x_keypad_probe, + .remove = clps711x_keypad_remove, +}; +module_platform_driver(clps711x_keypad_driver); + +MODULE_AUTHOR("Alexander Shiyan "); +MODULE_DESCRIPTION("Cirrus Logic CLPS711X Keypad driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From d3e6a67c7e502e8d21624c453f1d1f697e674548 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Sat, 29 Mar 2014 12:32:06 -0700 Subject: Input: pmic8xxx-keypad - fix build by removing gpio configuration The gpio configuration in this driver doesn't work because the gpio.h include doesn't exist. Remove the configuration as it isn't strictly necessary, allowing us to actually compile this driver. If it's needed in the future, it should be done via a pinctrl driver. Signed-off-by: Stephen Boyd Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/pmic8xxx-keypad.c | 58 -------------------------------- 1 file changed, 58 deletions(-) diff --git a/drivers/input/keyboard/pmic8xxx-keypad.c b/drivers/input/keyboard/pmic8xxx-keypad.c index 2c9f19ac35ea..c4aa882c8fb9 100644 --- a/drivers/input/keyboard/pmic8xxx-keypad.c +++ b/drivers/input/keyboard/pmic8xxx-keypad.c @@ -21,7 +21,6 @@ #include #include -#include #include #define PM8XXX_MAX_ROWS 18 @@ -447,27 +446,6 @@ static int pmic8xxx_kpd_init(struct pmic8xxx_kp *kp) } -static int pmic8xxx_kp_config_gpio(int gpio_start, int num_gpios, - struct pmic8xxx_kp *kp, struct pm_gpio *gpio_config) -{ - int rc, i; - - if (gpio_start < 0 || num_gpios < 0) - return -EINVAL; - - for (i = 0; i < num_gpios; i++) { - rc = pm8xxx_gpio_config(gpio_start + i, gpio_config); - if (rc) { - dev_err(kp->dev, "%s: FAIL pm8xxx_gpio_config():" - "for PM GPIO [%d] rc=%d.\n", - __func__, gpio_start + i, rc); - return rc; - } - } - - return 0; -} - static int pmic8xxx_kp_enable(struct pmic8xxx_kp *kp) { int rc; @@ -527,27 +505,6 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev) int rc; u8 ctrl_val; - struct pm_gpio kypd_drv = { - .direction = PM_GPIO_DIR_OUT, - .output_buffer = PM_GPIO_OUT_BUF_OPEN_DRAIN, - .output_value = 0, - .pull = PM_GPIO_PULL_NO, - .vin_sel = PM_GPIO_VIN_S3, - .out_strength = PM_GPIO_STRENGTH_LOW, - .function = PM_GPIO_FUNC_1, - .inv_int_pol = 1, - }; - - struct pm_gpio kypd_sns = { - .direction = PM_GPIO_DIR_IN, - .pull = PM_GPIO_PULL_UP_31P5, - .vin_sel = PM_GPIO_VIN_S3, - .out_strength = PM_GPIO_STRENGTH_NO, - .function = PM_GPIO_FUNC_NORMAL, - .inv_int_pol = 1, - }; - - if (!pdata || !pdata->num_cols || !pdata->num_rows || pdata->num_cols > PM8XXX_MAX_COLS || pdata->num_rows > PM8XXX_MAX_ROWS || @@ -653,20 +610,6 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev) goto err_get_irq; } - rc = pmic8xxx_kp_config_gpio(pdata->cols_gpio_start, - pdata->num_cols, kp, &kypd_sns); - if (rc < 0) { - dev_err(&pdev->dev, "unable to configure keypad sense lines\n"); - goto err_gpio_config; - } - - rc = pmic8xxx_kp_config_gpio(pdata->rows_gpio_start, - pdata->num_rows, kp, &kypd_drv); - if (rc < 0) { - dev_err(&pdev->dev, "unable to configure keypad drive lines\n"); - goto err_gpio_config; - } - rc = request_any_context_irq(kp->key_sense_irq, pmic8xxx_kp_irq, IRQF_TRIGGER_RISING, "pmic-keypad", kp); if (rc < 0) { @@ -703,7 +646,6 @@ err_pmic_reg_read: free_irq(kp->key_stuck_irq, kp); err_req_stuck_irq: free_irq(kp->key_sense_irq, kp); -err_gpio_config: err_get_irq: input_free_device(kp->input); err_alloc_device: -- cgit v1.2.3 From c7f6ee264b511d8a35063e9821cf36ad18e4e4fd Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Sat, 29 Mar 2014 12:32:43 -0700 Subject: Input: pmic8xxx-keypad - migrate to devm_* APIs Simplify the error paths and reduce the lines of code in this driver by using the devm_* APIs. Signed-off-by: Stephen Boyd Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/pmic8xxx-keypad.c | 61 +++++++++----------------------- 1 file changed, 17 insertions(+), 44 deletions(-) diff --git a/drivers/input/keyboard/pmic8xxx-keypad.c b/drivers/input/keyboard/pmic8xxx-keypad.c index c4aa882c8fb9..bec53ebde7b2 100644 --- a/drivers/input/keyboard/pmic8xxx-keypad.c +++ b/drivers/input/keyboard/pmic8xxx-keypad.c @@ -543,7 +543,7 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev) return -EINVAL; } - kp = kzalloc(sizeof(*kp), GFP_KERNEL); + kp = devm_kzalloc(&pdev->dev, sizeof(*kp), GFP_KERNEL); if (!kp) return -ENOMEM; @@ -552,32 +552,27 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev) kp->pdata = pdata; kp->dev = &pdev->dev; - kp->input = input_allocate_device(); + kp->input = devm_input_allocate_device(&pdev->dev); if (!kp->input) { dev_err(&pdev->dev, "unable to allocate input device\n"); - rc = -ENOMEM; - goto err_alloc_device; + return -ENOMEM; } kp->key_sense_irq = platform_get_irq(pdev, 0); if (kp->key_sense_irq < 0) { dev_err(&pdev->dev, "unable to get keypad sense irq\n"); - rc = -ENXIO; - goto err_get_irq; + return kp->key_sense_irq; } kp->key_stuck_irq = platform_get_irq(pdev, 1); if (kp->key_stuck_irq < 0) { dev_err(&pdev->dev, "unable to get keypad stuck irq\n"); - rc = -ENXIO; - goto err_get_irq; + return kp->key_stuck_irq; } kp->input->name = pdata->input_name ? : "PMIC8XXX keypad"; kp->input->phys = pdata->input_phys_device ? : "pmic8xxx_keypad/input0"; - kp->input->dev.parent = &pdev->dev; - kp->input->id.bustype = BUS_I2C; kp->input->id.version = 0x0001; kp->input->id.product = 0x0001; @@ -591,7 +586,7 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev) kp->keycodes, kp->input); if (rc) { dev_err(&pdev->dev, "failed to build keymap\n"); - goto err_get_irq; + return rc; } if (pdata->rep) @@ -607,27 +602,29 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev) rc = pmic8xxx_kpd_init(kp); if (rc < 0) { dev_err(&pdev->dev, "unable to initialize keypad controller\n"); - goto err_get_irq; + return rc; } - rc = request_any_context_irq(kp->key_sense_irq, pmic8xxx_kp_irq, - IRQF_TRIGGER_RISING, "pmic-keypad", kp); + rc = devm_request_any_context_irq(&pdev->dev, kp->key_sense_irq, + pmic8xxx_kp_irq, IRQF_TRIGGER_RISING, "pmic-keypad", + kp); if (rc < 0) { dev_err(&pdev->dev, "failed to request keypad sense irq\n"); - goto err_get_irq; + return rc; } - rc = request_any_context_irq(kp->key_stuck_irq, pmic8xxx_kp_stuck_irq, - IRQF_TRIGGER_RISING, "pmic-keypad-stuck", kp); + rc = devm_request_any_context_irq(&pdev->dev, kp->key_stuck_irq, + pmic8xxx_kp_stuck_irq, IRQF_TRIGGER_RISING, + "pmic-keypad-stuck", kp); if (rc < 0) { dev_err(&pdev->dev, "failed to request keypad stuck irq\n"); - goto err_req_stuck_irq; + return rc; } rc = pmic8xxx_kp_read_u8(kp, &ctrl_val, KEYP_CTRL); if (rc < 0) { dev_err(&pdev->dev, "failed to read KEYP_CTRL register\n"); - goto err_pmic_reg_read; + return rc; } kp->ctrl_reg = ctrl_val; @@ -635,34 +632,11 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev) rc = input_register_device(kp->input); if (rc < 0) { dev_err(&pdev->dev, "unable to register keypad input device\n"); - goto err_pmic_reg_read; + return rc; } device_init_wakeup(&pdev->dev, pdata->wakeup); - return 0; - -err_pmic_reg_read: - free_irq(kp->key_stuck_irq, kp); -err_req_stuck_irq: - free_irq(kp->key_sense_irq, kp); -err_get_irq: - input_free_device(kp->input); -err_alloc_device: - kfree(kp); - return rc; -} - -static int pmic8xxx_kp_remove(struct platform_device *pdev) -{ - struct pmic8xxx_kp *kp = platform_get_drvdata(pdev); - - device_init_wakeup(&pdev->dev, 0); - free_irq(kp->key_stuck_irq, kp); - free_irq(kp->key_sense_irq, kp); - input_unregister_device(kp->input); - kfree(kp); - return 0; } @@ -713,7 +687,6 @@ static SIMPLE_DEV_PM_OPS(pm8xxx_kp_pm_ops, static struct platform_driver pmic8xxx_kp_driver = { .probe = pmic8xxx_kp_probe, - .remove = pmic8xxx_kp_remove, .driver = { .name = PM8XXX_KEYPAD_DEV_NAME, .owner = THIS_MODULE, -- cgit v1.2.3 From a5dde0c72ccbb0f66b3491ee83f4c579aea0651d Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Sat, 29 Mar 2014 12:34:14 -0700 Subject: Input: pmic8xxx-keypad - migrate to regmap APIs Use the regmap APIs for this driver instead of custom pm8xxx APIs. This breaks this driver's dependency on the pm8xxx APIs and allows us to easily port it to other bus protocols in the future. Signed-off-by: Stephen Boyd Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/pmic8xxx-keypad.c | 81 ++++++++++++-------------------- 1 file changed, 29 insertions(+), 52 deletions(-) diff --git a/drivers/input/keyboard/pmic8xxx-keypad.c b/drivers/input/keyboard/pmic8xxx-keypad.c index bec53ebde7b2..0efd11e16b7e 100644 --- a/drivers/input/keyboard/pmic8xxx-keypad.c +++ b/drivers/input/keyboard/pmic8xxx-keypad.c @@ -19,8 +19,8 @@ #include #include #include +#include -#include #include #define PM8XXX_MAX_ROWS 18 @@ -86,6 +86,7 @@ * struct pmic8xxx_kp - internal keypad data structure * @pdata - keypad platform data pointer * @input - input device pointer for keypad + * @regmap - regmap handle * @key_sense_irq - key press/release irq number * @key_stuck_irq - key stuck notification irq number * @keycodes - array to hold the key codes @@ -97,6 +98,7 @@ struct pmic8xxx_kp { const struct pm8xxx_keypad_platform_data *pdata; struct input_dev *input; + struct regmap *regmap; int key_sense_irq; int key_stuck_irq; @@ -109,33 +111,6 @@ struct pmic8xxx_kp { u8 ctrl_reg; }; -static int pmic8xxx_kp_write_u8(struct pmic8xxx_kp *kp, - u8 data, u16 reg) -{ - int rc; - - rc = pm8xxx_writeb(kp->dev->parent, reg, data); - return rc; -} - -static int pmic8xxx_kp_read(struct pmic8xxx_kp *kp, - u8 *data, u16 reg, unsigned num_bytes) -{ - int rc; - - rc = pm8xxx_read_buf(kp->dev->parent, reg, data, num_bytes); - return rc; -} - -static int pmic8xxx_kp_read_u8(struct pmic8xxx_kp *kp, - u8 *data, u16 reg) -{ - int rc; - - rc = pmic8xxx_kp_read(kp, data, reg, 1); - return rc; -} - static u8 pmic8xxx_col_state(struct pmic8xxx_kp *kp, u8 col) { /* all keys pressed on that particular row? */ @@ -160,9 +135,9 @@ static u8 pmic8xxx_col_state(struct pmic8xxx_kp *kp, u8 col) static int pmic8xxx_chk_sync_read(struct pmic8xxx_kp *kp) { int rc; - u8 scan_val; + unsigned int scan_val; - rc = pmic8xxx_kp_read_u8(kp, &scan_val, KEYP_SCAN); + rc = regmap_read(kp->regmap, KEYP_SCAN, &scan_val); if (rc < 0) { dev_err(kp->dev, "Error reading KEYP_SCAN reg, rc=%d\n", rc); return rc; @@ -170,7 +145,7 @@ static int pmic8xxx_chk_sync_read(struct pmic8xxx_kp *kp) scan_val |= 0x1; - rc = pmic8xxx_kp_write_u8(kp, scan_val, KEYP_SCAN); + rc = regmap_write(kp->regmap, KEYP_SCAN, scan_val); if (rc < 0) { dev_err(kp->dev, "Error writing KEYP_SCAN reg, rc=%d\n", rc); return rc; @@ -186,26 +161,24 @@ static int pmic8xxx_kp_read_data(struct pmic8xxx_kp *kp, u16 *state, u16 data_reg, int read_rows) { int rc, row; - u8 new_data[PM8XXX_MAX_ROWS]; + unsigned int val; - rc = pmic8xxx_kp_read(kp, new_data, data_reg, read_rows); - if (rc) - return rc; - - for (row = 0; row < kp->pdata->num_rows; row++) { - dev_dbg(kp->dev, "new_data[%d] = %d\n", row, - new_data[row]); - state[row] = pmic8xxx_col_state(kp, new_data[row]); + for (row = 0; row < read_rows; row++) { + rc = regmap_read(kp->regmap, data_reg, &val); + if (rc) + return rc; + dev_dbg(kp->dev, "%d = %d\n", row, val); + state[row] = pmic8xxx_col_state(kp, val); } - return rc; + return 0; } static int pmic8xxx_kp_read_matrix(struct pmic8xxx_kp *kp, u16 *new_state, u16 *old_state) { int rc, read_rows; - u8 scan_val; + unsigned int scan_val; if (kp->pdata->num_rows < PM8XXX_MIN_ROWS) read_rows = PM8XXX_MIN_ROWS; @@ -235,14 +208,14 @@ static int pmic8xxx_kp_read_matrix(struct pmic8xxx_kp *kp, u16 *new_state, /* 4 * 32KHz clocks */ udelay((4 * DIV_ROUND_UP(USEC_PER_SEC, KEYP_CLOCK_FREQ)) + 1); - rc = pmic8xxx_kp_read_u8(kp, &scan_val, KEYP_SCAN); + rc = regmap_read(kp->regmap, KEYP_SCAN, &scan_val); if (rc < 0) { dev_err(kp->dev, "Error reading KEYP_SCAN reg, rc=%d\n", rc); return rc; } scan_val &= 0xFE; - rc = pmic8xxx_kp_write_u8(kp, scan_val, KEYP_SCAN); + rc = regmap_write(kp->regmap, KEYP_SCAN, scan_val); if (rc < 0) dev_err(kp->dev, "Error writing KEYP_SCAN reg, rc=%d\n", rc); @@ -378,10 +351,10 @@ static irqreturn_t pmic8xxx_kp_stuck_irq(int irq, void *data) static irqreturn_t pmic8xxx_kp_irq(int irq, void *data) { struct pmic8xxx_kp *kp = data; - u8 ctrl_val, events; + unsigned int ctrl_val, events; int rc; - rc = pmic8xxx_kp_read(kp, &ctrl_val, KEYP_CTRL, 1); + rc = regmap_read(kp->regmap, KEYP_CTRL, &ctrl_val); if (rc < 0) { dev_err(kp->dev, "failed to read keyp_ctrl register\n"); return IRQ_HANDLED; @@ -420,7 +393,7 @@ static int pmic8xxx_kpd_init(struct pmic8xxx_kp *kp) ctrl_val |= (bits << KEYP_CTRL_SCAN_ROWS_SHIFT); - rc = pmic8xxx_kp_write_u8(kp, ctrl_val, KEYP_CTRL); + rc = regmap_write(kp->regmap, KEYP_CTRL, ctrl_val); if (rc < 0) { dev_err(kp->dev, "Error writing KEYP_CTRL reg, rc=%d\n", rc); return rc; @@ -438,7 +411,7 @@ static int pmic8xxx_kpd_init(struct pmic8xxx_kp *kp) scan_val |= (cycles << KEYP_SCAN_ROW_HOLD_SHIFT); - rc = pmic8xxx_kp_write_u8(kp, scan_val, KEYP_SCAN); + rc = regmap_write(kp->regmap, KEYP_SCAN, scan_val); if (rc) dev_err(kp->dev, "Error writing KEYP_SCAN reg, rc=%d\n", rc); @@ -452,7 +425,7 @@ static int pmic8xxx_kp_enable(struct pmic8xxx_kp *kp) kp->ctrl_reg |= KEYP_CTRL_KEYP_EN; - rc = pmic8xxx_kp_write_u8(kp, kp->ctrl_reg, KEYP_CTRL); + rc = regmap_write(kp->regmap, KEYP_CTRL, kp->ctrl_reg); if (rc < 0) dev_err(kp->dev, "Error writing KEYP_CTRL reg, rc=%d\n", rc); @@ -465,7 +438,7 @@ static int pmic8xxx_kp_disable(struct pmic8xxx_kp *kp) kp->ctrl_reg &= ~KEYP_CTRL_KEYP_EN; - rc = pmic8xxx_kp_write_u8(kp, kp->ctrl_reg, KEYP_CTRL); + rc = regmap_write(kp->regmap, KEYP_CTRL, kp->ctrl_reg); if (rc < 0) return rc; @@ -503,7 +476,7 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev) const struct matrix_keymap_data *keymap_data; struct pmic8xxx_kp *kp; int rc; - u8 ctrl_val; + unsigned int ctrl_val; if (!pdata || !pdata->num_cols || !pdata->num_rows || pdata->num_cols > PM8XXX_MAX_COLS || @@ -547,6 +520,10 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev) if (!kp) return -ENOMEM; + kp->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!kp->regmap) + return -ENODEV; + platform_set_drvdata(pdev, kp); kp->pdata = pdata; @@ -621,7 +598,7 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev) return rc; } - rc = pmic8xxx_kp_read_u8(kp, &ctrl_val, KEYP_CTRL); + rc = regmap_read(kp->regmap, KEYP_CTRL, &ctrl_val); if (rc < 0) { dev_err(&pdev->dev, "failed to read KEYP_CTRL register\n"); return rc; -- cgit v1.2.3 From 86ea5e6b793d45fa7d2aa504ac3aefc813f0fd55 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Sat, 29 Mar 2014 12:35:35 -0700 Subject: Input: pmic8xxx-keypad - migrate to DT The driver is only supported on DT enabled platforms. Convert the driver to DT so that it can probe properly. Signed-off-by: Stephen Boyd Signed-off-by: Dmitry Torokhov --- .../bindings/input/qcom,pm8xxx-keypad.txt | 89 ++++++++++++ drivers/input/keyboard/pmic8xxx-keypad.c | 150 ++++++++++++--------- include/linux/input/pmic8xxx-keypad.h | 52 ------- 3 files changed, 175 insertions(+), 116 deletions(-) create mode 100644 Documentation/devicetree/bindings/input/qcom,pm8xxx-keypad.txt delete mode 100644 include/linux/input/pmic8xxx-keypad.h diff --git a/Documentation/devicetree/bindings/input/qcom,pm8xxx-keypad.txt b/Documentation/devicetree/bindings/input/qcom,pm8xxx-keypad.txt new file mode 100644 index 000000000000..7d8cb92831d7 --- /dev/null +++ b/Documentation/devicetree/bindings/input/qcom,pm8xxx-keypad.txt @@ -0,0 +1,89 @@ +Qualcomm PM8xxx PMIC Keypad + +PROPERTIES + +- compatible: + Usage: required + Value type: + Definition: must be one of: + "qcom,pm8058-keypad" + "qcom,pm8921-keypad" + +- reg: + Usage: required + Value type: + Definition: address of keypad control register + +- interrupts: + Usage: required + Value type: + Definition: the first interrupt specifies the key sense interrupt + and the second interrupt specifies the key stuck interrupt. + The format of the specifier is defined by the binding + document describing the node's interrupt parent. + +- linux,keymap: + Usage: required + Value type: + Definition: the linux keymap. More information can be found in + input/matrix-keymap.txt. + +- linux,keypad-no-autorepeat: + Usage: optional + Value type: + Definition: don't enable autorepeat feature. + +- linux,keypad-wakeup: + Usage: optional + Value type: + Definition: use any event on keypad as wakeup event. + +- keypad,num-rows: + Usage: required + Value type: + Definition: number of rows in the keymap. More information can be found + in input/matrix-keymap.txt. + +- keypad,num-columns: + Usage: required + Value type: + Definition: number of columns in the keymap. More information can be + found in input/matrix-keymap.txt. + +- debounce: + Usage: optional + Value type: + Definition: time in microseconds that key must be pressed or release + for key sense interrupt to trigger. + +- scan-delay: + Usage: optional + Value type: + Definition: time in microseconds to pause between successive scans + of the matrix array. + +- row-hold: + Usage: optional + Value type: + Definition: time in nanoseconds to pause between scans of each row in + the matrix array. + +EXAMPLE + + keypad@148 { + compatible = "qcom,pm8921-keypad"; + reg = <0x148>; + interrupt-parent = <&pmicintc>; + interrupts = <74 1>, <75 1>; + linux,keymap = < + MATRIX_KEY(0, 0, KEY_VOLUMEUP) + MATRIX_KEY(0, 1, KEY_VOLUMEDOWN) + MATRIX_KEY(0, 2, KEY_CAMERA_FOCUS) + MATRIX_KEY(0, 3, KEY_CAMERA) + >; + keypad,num-rows = <1>; + keypad,num-columns = <5>; + debounce = <15>; + scan-delay = <32>; + row-hold = <91500>; + }; diff --git a/drivers/input/keyboard/pmic8xxx-keypad.c b/drivers/input/keyboard/pmic8xxx-keypad.c index 0efd11e16b7e..80c6b0ef3fc8 100644 --- a/drivers/input/keyboard/pmic8xxx-keypad.c +++ b/drivers/input/keyboard/pmic8xxx-keypad.c @@ -20,8 +20,8 @@ #include #include #include - -#include +#include +#include #define PM8XXX_MAX_ROWS 18 #define PM8XXX_MAX_COLS 8 @@ -84,7 +84,8 @@ /** * struct pmic8xxx_kp - internal keypad data structure - * @pdata - keypad platform data pointer + * @num_cols - number of columns of keypad + * @num_rows - number of row of keypad * @input - input device pointer for keypad * @regmap - regmap handle * @key_sense_irq - key press/release irq number @@ -96,7 +97,8 @@ * @ctrl_reg - control register value */ struct pmic8xxx_kp { - const struct pm8xxx_keypad_platform_data *pdata; + unsigned int num_rows; + unsigned int num_cols; struct input_dev *input; struct regmap *regmap; int key_sense_irq; @@ -115,9 +117,9 @@ static u8 pmic8xxx_col_state(struct pmic8xxx_kp *kp, u8 col) { /* all keys pressed on that particular row? */ if (col == 0x00) - return 1 << kp->pdata->num_cols; + return 1 << kp->num_cols; else - return col & ((1 << kp->pdata->num_cols) - 1); + return col & ((1 << kp->num_cols) - 1); } /* @@ -180,10 +182,10 @@ static int pmic8xxx_kp_read_matrix(struct pmic8xxx_kp *kp, u16 *new_state, int rc, read_rows; unsigned int scan_val; - if (kp->pdata->num_rows < PM8XXX_MIN_ROWS) + if (kp->num_rows < PM8XXX_MIN_ROWS) read_rows = PM8XXX_MIN_ROWS; else - read_rows = kp->pdata->num_rows; + read_rows = kp->num_rows; pmic8xxx_chk_sync_read(kp); @@ -227,13 +229,13 @@ static void __pmic8xxx_kp_scan_matrix(struct pmic8xxx_kp *kp, u16 *new_state, { int row, col, code; - for (row = 0; row < kp->pdata->num_rows; row++) { + for (row = 0; row < kp->num_rows; row++) { int bits_changed = new_state[row] ^ old_state[row]; if (!bits_changed) continue; - for (col = 0; col < kp->pdata->num_cols; col++) { + for (col = 0; col < kp->num_cols; col++) { if (!(bits_changed & (1 << col))) continue; @@ -259,9 +261,9 @@ static bool pmic8xxx_detect_ghost_keys(struct pmic8xxx_kp *kp, u16 *new_state) u16 check, row_state; check = 0; - for (row = 0; row < kp->pdata->num_rows; row++) { + for (row = 0; row < kp->num_rows; row++) { row_state = (~new_state[row]) & - ((1 << kp->pdata->num_cols) - 1); + ((1 << kp->num_cols) - 1); if (hweight16(row_state) > 1) { if (found_first == -1) @@ -369,8 +371,13 @@ static irqreturn_t pmic8xxx_kp_irq(int irq, void *data) return IRQ_HANDLED; } -static int pmic8xxx_kpd_init(struct pmic8xxx_kp *kp) +static int pmic8xxx_kpd_init(struct pmic8xxx_kp *kp, + struct platform_device *pdev) { + const struct device_node *of_node = pdev->dev.of_node; + unsigned int scan_delay_ms; + unsigned int row_hold_ns; + unsigned int debounce_ms; int bits, rc, cycles; u8 scan_val = 0, ctrl_val = 0; static const u8 row_bits[] = { @@ -378,18 +385,18 @@ static int pmic8xxx_kpd_init(struct pmic8xxx_kp *kp) }; /* Find column bits */ - if (kp->pdata->num_cols < KEYP_CTRL_SCAN_COLS_MIN) + if (kp->num_cols < KEYP_CTRL_SCAN_COLS_MIN) bits = 0; else - bits = kp->pdata->num_cols - KEYP_CTRL_SCAN_COLS_MIN; + bits = kp->num_cols - KEYP_CTRL_SCAN_COLS_MIN; ctrl_val = (bits & KEYP_CTRL_SCAN_COLS_BITS) << KEYP_CTRL_SCAN_COLS_SHIFT; /* Find row bits */ - if (kp->pdata->num_rows < KEYP_CTRL_SCAN_ROWS_MIN) + if (kp->num_rows < KEYP_CTRL_SCAN_ROWS_MIN) bits = 0; else - bits = row_bits[kp->pdata->num_rows - KEYP_CTRL_SCAN_ROWS_MIN]; + bits = row_bits[kp->num_rows - KEYP_CTRL_SCAN_ROWS_MIN]; ctrl_val |= (bits << KEYP_CTRL_SCAN_ROWS_SHIFT); @@ -399,15 +406,44 @@ static int pmic8xxx_kpd_init(struct pmic8xxx_kp *kp) return rc; } - bits = (kp->pdata->debounce_ms / 5) - 1; + if (of_property_read_u32(of_node, "scan-delay", &scan_delay_ms)) + scan_delay_ms = MIN_SCAN_DELAY; + + if (scan_delay_ms > MAX_SCAN_DELAY || scan_delay_ms < MIN_SCAN_DELAY || + !is_power_of_2(scan_delay_ms)) { + dev_err(&pdev->dev, "invalid keypad scan time supplied\n"); + return -EINVAL; + } + + if (of_property_read_u32(of_node, "row-hold", &row_hold_ns)) + row_hold_ns = MIN_ROW_HOLD_DELAY; + + if (row_hold_ns > MAX_ROW_HOLD_DELAY || + row_hold_ns < MIN_ROW_HOLD_DELAY || + ((row_hold_ns % MIN_ROW_HOLD_DELAY) != 0)) { + dev_err(&pdev->dev, "invalid keypad row hold time supplied\n"); + return -EINVAL; + } + + if (of_property_read_u32(of_node, "debounce", &debounce_ms)) + debounce_ms = MIN_DEBOUNCE_TIME; + + if (((debounce_ms % 5) != 0) || + debounce_ms > MAX_DEBOUNCE_TIME || + debounce_ms < MIN_DEBOUNCE_TIME) { + dev_err(&pdev->dev, "invalid debounce time supplied\n"); + return -EINVAL; + } + + bits = (debounce_ms / 5) - 1; scan_val |= (bits << KEYP_SCAN_DBOUNCE_SHIFT); - bits = fls(kp->pdata->scan_delay_ms) - 1; + bits = fls(scan_delay_ms) - 1; scan_val |= (bits << KEYP_SCAN_PAUSE_SHIFT); /* Row hold time is a multiple of 32KHz cycles. */ - cycles = (kp->pdata->row_hold_ns * KEYP_CLOCK_FREQ) / NSEC_PER_SEC; + cycles = (row_hold_ns * KEYP_CLOCK_FREQ) / NSEC_PER_SEC; scan_val |= (cycles << KEYP_SCAN_ROW_HOLD_SHIFT); @@ -471,50 +507,27 @@ static void pmic8xxx_kp_close(struct input_dev *dev) */ static int pmic8xxx_kp_probe(struct platform_device *pdev) { - const struct pm8xxx_keypad_platform_data *pdata = - dev_get_platdata(&pdev->dev); - const struct matrix_keymap_data *keymap_data; + unsigned int rows, cols; + bool repeat; + bool wakeup; struct pmic8xxx_kp *kp; int rc; unsigned int ctrl_val; - if (!pdata || !pdata->num_cols || !pdata->num_rows || - pdata->num_cols > PM8XXX_MAX_COLS || - pdata->num_rows > PM8XXX_MAX_ROWS || - pdata->num_cols < PM8XXX_MIN_COLS) { - dev_err(&pdev->dev, "invalid platform data\n"); - return -EINVAL; - } - - if (!pdata->scan_delay_ms || - pdata->scan_delay_ms > MAX_SCAN_DELAY || - pdata->scan_delay_ms < MIN_SCAN_DELAY || - !is_power_of_2(pdata->scan_delay_ms)) { - dev_err(&pdev->dev, "invalid keypad scan time supplied\n"); - return -EINVAL; - } - - if (!pdata->row_hold_ns || - pdata->row_hold_ns > MAX_ROW_HOLD_DELAY || - pdata->row_hold_ns < MIN_ROW_HOLD_DELAY || - ((pdata->row_hold_ns % MIN_ROW_HOLD_DELAY) != 0)) { - dev_err(&pdev->dev, "invalid keypad row hold time supplied\n"); - return -EINVAL; - } + rc = matrix_keypad_parse_of_params(&pdev->dev, &rows, &cols); + if (rc) + return rc; - if (!pdata->debounce_ms || - ((pdata->debounce_ms % 5) != 0) || - pdata->debounce_ms > MAX_DEBOUNCE_TIME || - pdata->debounce_ms < MIN_DEBOUNCE_TIME) { - dev_err(&pdev->dev, "invalid debounce time supplied\n"); + if (cols > PM8XXX_MAX_COLS || rows > PM8XXX_MAX_ROWS || + cols < PM8XXX_MIN_COLS) { + dev_err(&pdev->dev, "invalid platform data\n"); return -EINVAL; } - keymap_data = pdata->keymap_data; - if (!keymap_data) { - dev_err(&pdev->dev, "no keymap data supplied\n"); - return -EINVAL; - } + repeat = !of_property_read_bool(pdev->dev.of_node, + "linux,input-no-autorepeat"); + wakeup = of_property_read_bool(pdev->dev.of_node, + "linux,keypad-wakeup"); kp = devm_kzalloc(&pdev->dev, sizeof(*kp), GFP_KERNEL); if (!kp) @@ -526,7 +539,8 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev) platform_set_drvdata(pdev, kp); - kp->pdata = pdata; + kp->num_rows = rows; + kp->num_cols = cols; kp->dev = &pdev->dev; kp->input = devm_input_allocate_device(&pdev->dev); @@ -547,8 +561,8 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev) return kp->key_stuck_irq; } - kp->input->name = pdata->input_name ? : "PMIC8XXX keypad"; - kp->input->phys = pdata->input_phys_device ? : "pmic8xxx_keypad/input0"; + kp->input->name = "PMIC8XXX keypad"; + kp->input->phys = "pmic8xxx_keypad/input0"; kp->input->id.bustype = BUS_I2C; kp->input->id.version = 0x0001; @@ -558,7 +572,7 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev) kp->input->open = pmic8xxx_kp_open; kp->input->close = pmic8xxx_kp_close; - rc = matrix_keypad_build_keymap(keymap_data, NULL, + rc = matrix_keypad_build_keymap(NULL, NULL, PM8XXX_MAX_ROWS, PM8XXX_MAX_COLS, kp->keycodes, kp->input); if (rc) { @@ -566,7 +580,7 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev) return rc; } - if (pdata->rep) + if (repeat) __set_bit(EV_REP, kp->input->evbit); input_set_capability(kp->input, EV_MSC, MSC_SCAN); @@ -576,7 +590,7 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev) memset(kp->keystate, 0xff, sizeof(kp->keystate)); memset(kp->stuckstate, 0xff, sizeof(kp->stuckstate)); - rc = pmic8xxx_kpd_init(kp); + rc = pmic8xxx_kpd_init(kp, pdev); if (rc < 0) { dev_err(&pdev->dev, "unable to initialize keypad controller\n"); return rc; @@ -612,7 +626,7 @@ static int pmic8xxx_kp_probe(struct platform_device *pdev) return rc; } - device_init_wakeup(&pdev->dev, pdata->wakeup); + device_init_wakeup(&pdev->dev, wakeup); return 0; } @@ -662,12 +676,20 @@ static int pmic8xxx_kp_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(pm8xxx_kp_pm_ops, pmic8xxx_kp_suspend, pmic8xxx_kp_resume); +static const struct of_device_id pm8xxx_match_table[] = { + { .compatible = "qcom,pm8058-keypad" }, + { .compatible = "qcom,pm8921-keypad" }, + { } +}; +MODULE_DEVICE_TABLE(of, pm8xxx_match_table); + static struct platform_driver pmic8xxx_kp_driver = { .probe = pmic8xxx_kp_probe, .driver = { - .name = PM8XXX_KEYPAD_DEV_NAME, + .name = "pm8xxx-keypad", .owner = THIS_MODULE, .pm = &pm8xxx_kp_pm_ops, + .of_match_table = pm8xxx_match_table, }, }; module_platform_driver(pmic8xxx_kp_driver); diff --git a/include/linux/input/pmic8xxx-keypad.h b/include/linux/input/pmic8xxx-keypad.h deleted file mode 100644 index 5f1e2f9ad959..000000000000 --- a/include/linux/input/pmic8xxx-keypad.h +++ /dev/null @@ -1,52 +0,0 @@ -/* Copyright (c) 2011, Code Aurora Forum. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef __PMIC8XXX_KEYPAD_H__ -#define __PMIC8XXX_KEYPAD_H__ - -#include - -#define PM8XXX_KEYPAD_DEV_NAME "pm8xxx-keypad" - -/** - * struct pm8xxx_keypad_platform_data - platform data for keypad - * @keymap_data - matrix keymap data - * @input_name - input device name - * @input_phys_device - input device name - * @num_cols - number of columns of keypad - * @num_rows - number of row of keypad - * @debounce_ms - debounce period in milliseconds - * @scan_delay_ms - scan delay in milliseconds - * @row_hold_ns - row hold period in nanoseconds - * @wakeup - configure keypad as wakeup - * @rep - enable or disable key repeat bit - */ -struct pm8xxx_keypad_platform_data { - const struct matrix_keymap_data *keymap_data; - - const char *input_name; - const char *input_phys_device; - - unsigned int num_cols; - unsigned int num_rows; - unsigned int rows_gpio_start; - unsigned int cols_gpio_start; - - unsigned int debounce_ms; - unsigned int scan_delay_ms; - unsigned int row_hold_ns; - - bool wakeup; - bool rep; -}; - -#endif /*__PMIC8XXX_KEYPAD_H__ */ -- cgit v1.2.3 From 57918dfadf717acf7d0488d5970c56a282d0aad1 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Sat, 29 Mar 2014 12:39:38 -0700 Subject: Input: pmic8xxx-pwrkey - migrate to DT The driver is only supported on DT enabled platforms. Convert the driver to DT so that it can probe properly. Signed-off-by: Stephen Boyd Signed-off-by: Dmitry Torokhov --- .../bindings/input/qcom,pm8xxx-pwrkey.txt | 46 ++++++++++++++++++++++ drivers/input/misc/pmic8xxx-pwrkey.c | 33 ++++++++++------ include/linux/input/pmic8xxx-pwrkey.h | 31 --------------- 3 files changed, 66 insertions(+), 44 deletions(-) create mode 100644 Documentation/devicetree/bindings/input/qcom,pm8xxx-pwrkey.txt delete mode 100644 include/linux/input/pmic8xxx-pwrkey.h diff --git a/Documentation/devicetree/bindings/input/qcom,pm8xxx-pwrkey.txt b/Documentation/devicetree/bindings/input/qcom,pm8xxx-pwrkey.txt new file mode 100644 index 000000000000..588536cc96ed --- /dev/null +++ b/Documentation/devicetree/bindings/input/qcom,pm8xxx-pwrkey.txt @@ -0,0 +1,46 @@ +Qualcomm PM8xxx PMIC Power Key + +PROPERTIES + +- compatible: + Usage: required + Value type: + Definition: must be one of: + "qcom,pm8058-pwrkey" + "qcom,pm8921-pwrkey" + +- reg: + Usage: required + Value type: + Definition: address of power key control register + +- interrupts: + Usage: required + Value type: + Definition: the first interrupt specifies the key release interrupt + and the second interrupt specifies the key press interrupt. + The format of the specifier is defined by the binding + document describing the node's interrupt parent. + +- debounce: + Usage: optional + Value type: + Definition: time in microseconds that key must be pressed or release + for state change interrupt to trigger. + +- pull-up: + Usage: optional + Value type: + Definition: presence of this property indicates that the KPDPWR_N pin + should be configured for pull up. + +EXAMPLE + + pwrkey@1c { + compatible = "qcom,pm8921-pwrkey"; + reg = <0x1c>; + interrupt-parent = <&pmicintc>; + interrupts = <50 1>, <51 1>; + debounce = <15625>; + pull-up; + }; diff --git a/drivers/input/misc/pmic8xxx-pwrkey.c b/drivers/input/misc/pmic8xxx-pwrkey.c index 0e1a05f95858..1cb8fda7a166 100644 --- a/drivers/input/misc/pmic8xxx-pwrkey.c +++ b/drivers/input/misc/pmic8xxx-pwrkey.c @@ -19,8 +19,7 @@ #include #include #include - -#include +#include #define PON_CNTL_1 0x1C #define PON_CNTL_PULL_UP BIT(7) @@ -89,15 +88,15 @@ static int pmic8xxx_pwrkey_probe(struct platform_device *pdev) unsigned int pon_cntl; struct regmap *regmap; struct pmic8xxx_pwrkey *pwrkey; - const struct pm8xxx_pwrkey_platform_data *pdata = - dev_get_platdata(&pdev->dev); + u32 kpd_delay; + bool pull_up; - if (!pdata) { - dev_err(&pdev->dev, "power key platform data not supplied\n"); - return -EINVAL; - } + if (of_property_read_u32(pdev->dev.of_node, "debounce", &kpd_delay)) + kpd_delay = 0; - if (pdata->kpd_trigger_delay_us > 62500) { + pull_up = of_property_read_bool(pdev->dev.of_node, "pull-up"); + + if (kpd_delay > 62500) { dev_err(&pdev->dev, "invalid power key trigger delay\n"); return -EINVAL; } @@ -125,7 +124,7 @@ static int pmic8xxx_pwrkey_probe(struct platform_device *pdev) pwr->name = "pmic8xxx_pwrkey"; pwr->phys = "pmic8xxx_pwrkey/input0"; - delay = (pdata->kpd_trigger_delay_us << 10) / USEC_PER_SEC; + delay = (kpd_delay << 10) / USEC_PER_SEC; delay = 1 + ilog2(delay); err = regmap_read(regmap, PON_CNTL_1, &pon_cntl); @@ -136,7 +135,7 @@ static int pmic8xxx_pwrkey_probe(struct platform_device *pdev) pon_cntl &= ~PON_CNTL_TRIG_DELAY_MASK; pon_cntl |= (delay & PON_CNTL_TRIG_DELAY_MASK); - if (pdata->pull_up) + if (pull_up) pon_cntl |= PON_CNTL_PULL_UP; else pon_cntl &= ~PON_CNTL_PULL_UP; @@ -172,7 +171,7 @@ static int pmic8xxx_pwrkey_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, pwrkey); - device_init_wakeup(&pdev->dev, pdata->wakeup); + device_init_wakeup(&pdev->dev, 1); return 0; } @@ -184,13 +183,21 @@ static int pmic8xxx_pwrkey_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id pm8xxx_pwr_key_id_table[] = { + { .compatible = "qcom,pm8058-pwrkey" }, + { .compatible = "qcom,pm8921-pwrkey" }, + { } +}; +MODULE_DEVICE_TABLE(of, pm8xxx_pwr_key_id_table); + static struct platform_driver pmic8xxx_pwrkey_driver = { .probe = pmic8xxx_pwrkey_probe, .remove = pmic8xxx_pwrkey_remove, .driver = { - .name = PM8XXX_PWRKEY_DEV_NAME, + .name = "pm8xxx-pwrkey", .owner = THIS_MODULE, .pm = &pm8xxx_pwr_key_pm_ops, + .of_match_table = pm8xxx_pwr_key_id_table, }, }; module_platform_driver(pmic8xxx_pwrkey_driver); diff --git a/include/linux/input/pmic8xxx-pwrkey.h b/include/linux/input/pmic8xxx-pwrkey.h deleted file mode 100644 index 6d2974e57109..000000000000 --- a/include/linux/input/pmic8xxx-pwrkey.h +++ /dev/null @@ -1,31 +0,0 @@ -/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef __PMIC8XXX_PWRKEY_H__ -#define __PMIC8XXX_PWRKEY_H__ - -#define PM8XXX_PWRKEY_DEV_NAME "pm8xxx-pwrkey" - -/** - * struct pm8xxx_pwrkey_platform_data - platform data for pwrkey driver - * @pull up: power on register control for pull up/down configuration - * @kpd_trigger_delay_us: time delay for power key state change interrupt - * trigger. - * @wakeup: configure power key as wakeup source - */ -struct pm8xxx_pwrkey_platform_data { - bool pull_up; - u32 kpd_trigger_delay_us; - u32 wakeup; -}; - -#endif /* __PMIC8XXX_PWRKEY_H__ */ -- cgit v1.2.3 From 877e1f1529a5c4fcc8460c1317c753ff8a6874c5 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Sat, 29 Mar 2014 12:44:14 -0700 Subject: Input: pm8xxx-vibrator - add DT match table The driver is only supported on DT enabled platforms. Convert the driver to DT so that it can probe properly. Signed-off-by: Stephen Boyd Signed-off-by: Dmitry Torokhov --- .../devicetree/bindings/input/qcom,pm8xxx-vib.txt | 22 ++++++++++++++++++++++ drivers/input/misc/pm8xxx-vibrator.c | 9 ++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/input/qcom,pm8xxx-vib.txt diff --git a/Documentation/devicetree/bindings/input/qcom,pm8xxx-vib.txt b/Documentation/devicetree/bindings/input/qcom,pm8xxx-vib.txt new file mode 100644 index 000000000000..4ed467b1e402 --- /dev/null +++ b/Documentation/devicetree/bindings/input/qcom,pm8xxx-vib.txt @@ -0,0 +1,22 @@ +Qualcomm PM8xxx PMIC Vibrator + +PROPERTIES + +- compatible: + Usage: required + Value type: + Definition: must be one of: + "qcom,pm8058-vib" + "qcom,pm8921-vib" + +- reg: + Usage: required + Value type: + Definition: address of vibration control register + +EXAMPLE + + vibrator@4a { + compatible = "qcom,pm8058-vib"; + reg = <0x4a>; + }; diff --git a/drivers/input/misc/pm8xxx-vibrator.c b/drivers/input/misc/pm8xxx-vibrator.c index b88b7cbf93e2..6a915ba31bba 100644 --- a/drivers/input/misc/pm8xxx-vibrator.c +++ b/drivers/input/misc/pm8xxx-vibrator.c @@ -142,7 +142,6 @@ static int pm8xxx_vib_play_effect(struct input_dev *dev, void *data, } static int pm8xxx_vib_probe(struct platform_device *pdev) - { struct pm8xxx_vib *vib; struct input_dev *input_dev; @@ -214,12 +213,20 @@ static int pm8xxx_vib_suspend(struct device *dev) static SIMPLE_DEV_PM_OPS(pm8xxx_vib_pm_ops, pm8xxx_vib_suspend, NULL); +static const struct of_device_id pm8xxx_vib_id_table[] = { + { .compatible = "qcom,pm8058-vib" }, + { .compatible = "qcom,pm8921-vib" }, + { } +}; +MODULE_DEVICE_TABLE(of, pm8xxx_vib_id_table); + static struct platform_driver pm8xxx_vib_driver = { .probe = pm8xxx_vib_probe, .driver = { .name = "pm8xxx-vib", .owner = THIS_MODULE, .pm = &pm8xxx_vib_pm_ops, + .of_match_table = pm8xxx_vib_id_table, }, }; module_platform_driver(pm8xxx_vib_driver); -- cgit v1.2.3 From 61cd4822dd810e1a3c28eab1af6005728906c0e4 Mon Sep 17 00:00:00 2001 From: Lejun Zhu Date: Sun, 30 Mar 2014 23:12:00 -0700 Subject: Input: add driver for SOC button array This patch adds support for the GPIO buttons on some Intel Bay Trail tablets originally running Windows 8. The ACPI description of these buttons follows "Windows ACPI Design Guide for SoC Platforms". Signed-off-by: Lejun Zhu Signed-off-by: Dmitry Torokhov --- drivers/input/misc/Kconfig | 10 ++ drivers/input/misc/Makefile | 1 + drivers/input/misc/soc_button_array.c | 218 ++++++++++++++++++++++++++++++++++ 3 files changed, 229 insertions(+) create mode 100644 drivers/input/misc/soc_button_array.c diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 7674f05d1fb3..f772981bdcdb 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -666,4 +666,14 @@ config INPUT_IDEAPAD_SLIDEBAR To compile this driver as a module, choose M here: the module will be called ideapad_slidebar. +config INPUT_SOC_BUTTON_ARRAY + tristate "Windows-compatible SoC Button Array" + depends on KEYBOARD_GPIO + help + Say Y here if you have a SoC-based tablet that originally + runs Windows 8. + + To compile this driver as a module, choose M here: the + module will be called soc_button_array. + endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index cda71fc52fb3..4955ad322a01 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -53,6 +53,7 @@ obj-$(CONFIG_INPUT_RETU_PWRBUTTON) += retu-pwrbutton.o obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o obj-$(CONFIG_INPUT_SIRFSOC_ONKEY) += sirfsoc-onkey.o +obj-$(CONFIG_INPUT_SOC_BUTTON_ARRAY) += soc_button_array.o obj-$(CONFIG_INPUT_SPARCSPKR) += sparcspkr.o obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o obj-$(CONFIG_INPUT_TWL4030_VIBRA) += twl4030-vibra.o diff --git a/drivers/input/misc/soc_button_array.c b/drivers/input/misc/soc_button_array.c new file mode 100644 index 000000000000..08ead2aaede5 --- /dev/null +++ b/drivers/input/misc/soc_button_array.c @@ -0,0 +1,218 @@ +/* + * Supports for the button array on SoC tablets originally running + * Windows 8. + * + * (C) Copyright 2014 Intel 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 + * of the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Definition of buttons on the tablet. The ACPI index of each button + * is defined in section 2.8.7.2 of "Windows ACPI Design Guide for SoC + * Platforms" + */ +#define MAX_NBUTTONS 5 + +struct soc_button_info { + const char *name; + int acpi_index; + unsigned int event_type; + unsigned int event_code; + bool autorepeat; + bool wakeup; +}; + +/* + * Some of the buttons like volume up/down are auto repeat, while others + * are not. To support both, we register two platform devices, and put + * buttons into them based on whether the key should be auto repeat. + */ +#define BUTTON_TYPES 2 + +struct soc_button_data { + struct platform_device *children[BUTTON_TYPES]; +}; + +/* + * Get the Nth GPIO number from the ACPI object. + */ +static int soc_button_lookup_gpio(struct device *dev, int acpi_index) +{ + struct gpio_desc *desc; + int gpio; + + desc = gpiod_get_index(dev, KBUILD_MODNAME, acpi_index); + if (IS_ERR(desc)) + return PTR_ERR(desc); + + gpio = desc_to_gpio(desc); + + gpiod_put(desc); + + return gpio; +} + +static struct platform_device * +soc_button_device_create(struct pnp_dev *pdev, + const struct soc_button_info *button_info, + bool autorepeat) +{ + const struct soc_button_info *info; + struct platform_device *pd; + struct gpio_keys_button *gpio_keys; + struct gpio_keys_platform_data *gpio_keys_pdata; + int n_buttons = 0; + int gpio; + int error; + + gpio_keys_pdata = devm_kzalloc(&pdev->dev, + sizeof(*gpio_keys_pdata) + + sizeof(*gpio_keys) * MAX_NBUTTONS, + GFP_KERNEL); + gpio_keys = (void *)(gpio_keys_pdata + 1); + + for (info = button_info; info->name; info++) { + if (info->autorepeat != autorepeat) + continue; + + gpio = soc_button_lookup_gpio(&pdev->dev, info->acpi_index); + if (gpio < 0) + continue; + + gpio_keys[n_buttons].type = info->event_type; + gpio_keys[n_buttons].code = info->event_code; + gpio_keys[n_buttons].gpio = gpio; + gpio_keys[n_buttons].active_low = 1; + gpio_keys[n_buttons].desc = info->name; + gpio_keys[n_buttons].wakeup = info->wakeup; + n_buttons++; + } + + if (n_buttons == 0) { + error = -ENODEV; + goto err_free_mem; + } + + gpio_keys_pdata->buttons = gpio_keys; + gpio_keys_pdata->nbuttons = n_buttons; + gpio_keys_pdata->rep = autorepeat; + + pd = platform_device_alloc("gpio-keys", PLATFORM_DEVID_AUTO); + if (!pd) { + error = -ENOMEM; + goto err_free_mem; + } + + error = platform_device_add_data(pd, gpio_keys_pdata, + sizeof(*gpio_keys_pdata)); + if (error) + goto err_free_pdev; + + error = platform_device_add(pd); + if (error) + goto err_free_pdev; + + return pd; + +err_free_pdev: + platform_device_put(pd); +err_free_mem: + devm_kfree(&pdev->dev, gpio_keys_pdata); + return ERR_PTR(error); +} + +static void soc_button_remove(struct pnp_dev *pdev) +{ + struct soc_button_data *priv = pnp_get_drvdata(pdev); + int i; + + for (i = 0; i < BUTTON_TYPES; i++) + if (priv->children[i]) + platform_device_unregister(priv->children[i]); +} + +static int soc_button_pnp_probe(struct pnp_dev *pdev, + const struct pnp_device_id *id) +{ + const struct soc_button_info *button_info = (void *)id->driver_data; + struct soc_button_data *priv; + struct platform_device *pd; + int i; + int error; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + pnp_set_drvdata(pdev, priv); + + for (i = 0; i < BUTTON_TYPES; i++) { + pd = soc_button_device_create(pdev, button_info, i == 0); + if (IS_ERR(pd)) { + error = PTR_ERR(pd); + if (error != -ENODEV) { + soc_button_remove(pdev); + return error; + } + } + + priv->children[i] = pd; + } + + if (!priv->children[0] && !priv->children[1]) + return -ENODEV; + + return 0; +} + +static struct soc_button_info soc_button_PNP0C40[] = { + { "power", 0, EV_KEY, KEY_POWER, false, true }, + { "home", 1, EV_KEY, KEY_HOME, false, true }, + { "volume_up", 2, EV_KEY, KEY_VOLUMEUP, true, false }, + { "volume_down", 3, EV_KEY, KEY_VOLUMEDOWN, true, false }, + { "rotation_lock", 4, EV_SW, SW_ROTATE_LOCK, false, false }, + { } +}; + +static const struct pnp_device_id soc_button_pnp_match[] = { + { .id = "PNP0C40", .driver_data = (long)soc_button_PNP0C40 }, + { .id = "" } +}; +MODULE_DEVICE_TABLE(pnp, soc_button_pnp_match); + +static struct pnp_driver soc_button_pnp_driver = { + .name = KBUILD_MODNAME, + .id_table = soc_button_pnp_match, + .probe = soc_button_pnp_probe, + .remove = soc_button_remove, +}; + +static int __init soc_button_init(void) +{ + return pnp_register_driver(&soc_button_pnp_driver); +} + +static void __exit soc_button_exit(void) +{ + pnp_unregister_driver(&soc_button_pnp_driver); +} + +module_init(soc_button_init); +module_exit(soc_button_exit); + +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 739204bc957750ca47254114b8ee49b00edfc18d Mon Sep 17 00:00:00 2001 From: Clinton Sprain Date: Sun, 30 Mar 2014 23:21:21 -0700 Subject: Input: appletouch - implement sensor data smoothing Use smoothed version of sensor array data to calculate movement and add weight to prior values when calculating average. This gives more granular and more predictable movement. Signed-off-by: Clinton Sprain Reviewed-by: Henrik Rydberg Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/appletouch.c | 103 ++++++++++++++++++++++++++++++--------- 1 file changed, 79 insertions(+), 24 deletions(-) diff --git a/drivers/input/mouse/appletouch.c b/drivers/input/mouse/appletouch.c index 2745832f74b6..2dbf7a0f0537 100644 --- a/drivers/input/mouse/appletouch.c +++ b/drivers/input/mouse/appletouch.c @@ -161,6 +161,12 @@ MODULE_DEVICE_TABLE(usb, atp_table); #define ATP_XSENSORS 26 #define ATP_YSENSORS 16 +/* + * The largest possible bank of sensors with additional buffer of 4 extra values + * on either side, for an array of smoothed sensor values. + */ +#define ATP_SMOOTHSIZE 34 + /* maximum pressure this driver will report */ #define ATP_PRESSURE 300 @@ -168,7 +174,13 @@ MODULE_DEVICE_TABLE(usb, atp_table); * Threshold for the touchpad sensors. Any change less than ATP_THRESHOLD is * ignored. */ -#define ATP_THRESHOLD 5 +#define ATP_THRESHOLD 5 + +/* + * How far we'll bitshift our sensor values before averaging them. Mitigates + * rounding errors. + */ +#define ATP_SCALE 12 /* Geyser initialization constants */ #define ATP_GEYSER_MODE_READ_REQUEST_ID 1 @@ -211,6 +223,8 @@ struct atp { signed char xy_cur[ATP_XSENSORS + ATP_YSENSORS]; signed char xy_old[ATP_XSENSORS + ATP_YSENSORS]; int xy_acc[ATP_XSENSORS + ATP_YSENSORS]; + int smooth[ATP_SMOOTHSIZE]; + int smooth_tmp[ATP_SMOOTHSIZE]; int idlecount; /* number of empty packets */ struct work_struct work; }; @@ -329,10 +343,17 @@ static void atp_reinit(struct work_struct *work) retval); } -static int atp_calculate_abs(int *xy_sensors, int nb_sensors, int fact, - int *z, int *fingers) +static int atp_calculate_abs(struct atp *dev, int offset, int nb_sensors, + int fact, int *z, int *fingers) { - int i; + int i, pass; + + /* + * Use offset to point xy_sensors at the first value in dev->xy_acc + * for whichever dimension we're looking at this particular go-round. + */ + int *xy_sensors = dev->xy_acc + offset; + /* values to calculate mean */ int pcum = 0, psum = 0; int is_increasing = 0; @@ -344,9 +365,6 @@ static int atp_calculate_abs(int *xy_sensors, int nb_sensors, int fact, if (is_increasing) is_increasing = 0; - continue; - } - /* * Makes the finger detection more versatile. For example, * two fingers with no gap will be detected. Also, my @@ -361,27 +379,63 @@ static int atp_calculate_abs(int *xy_sensors, int nb_sensors, int fact, * * - Jason Parekh */ - if (i < 1 || + + } else if (i < 1 || (!is_increasing && xy_sensors[i - 1] < xy_sensors[i])) { (*fingers)++; is_increasing = 1; } else if (i > 0 && (xy_sensors[i - 1] - xy_sensors[i] > threshold)) { is_increasing = 0; } + } + + if (*fingers < 1) /* No need to continue if no fingers are found. */ + return 0; + + /* + * Use a smoothed version of sensor data for movement calculations, to + * combat noise without needing to rely so heavily on a threshold. + * This improves tracking. + * + * The smoothed array is bigger than the original so that the smoothing + * doesn't result in edge values being truncated. + */ + + memset(dev->smooth, 0, 4 * sizeof(dev->smooth[0])); + /* Pull base values, scaled up to help avoid truncation errors. */ + for (i = 0; i < nb_sensors; i++) + dev->smooth[i + 4] = xy_sensors[i] << ATP_SCALE; + memset(&dev->smooth[nb_sensors + 4], 0, 4 * sizeof(dev->smooth[0])); + for (pass = 0; pass < 4; pass++) { + /* Handle edge. */ + dev->smooth_tmp[0] = (dev->smooth[0] + dev->smooth[1]) / 2; + + /* Average values with neighbors. */ + for (i = 1; i < nb_sensors + 7; i++) + dev->smooth_tmp[i] = (dev->smooth[i - 1] + + dev->smooth[i] * 2 + + dev->smooth[i + 1]) / 4; + + /* Handle other edge. */ + dev->smooth_tmp[i] = (dev->smooth[i - 1] + dev->smooth[i]) / 2; + + memcpy(dev->smooth, dev->smooth_tmp, sizeof(dev->smooth)); + } + + for (i = 0; i < nb_sensors + 8; i++) { /* - * Subtracts threshold so a high sensor that just passes the - * threshold won't skew the calculated absolute coordinate. - * Fixes an issue where slowly moving the mouse would - * occasionally jump a number of pixels (slowly moving the - * finger makes this issue most apparent.) + * Skip values if they're small enough to be truncated to 0 + * by scale. Mostly noise. */ - pcum += (xy_sensors[i] - threshold) * i; - psum += (xy_sensors[i] - threshold); + if ((dev->smooth[i] >> ATP_SCALE) > 0) { + pcum += dev->smooth[i] * i; + psum += dev->smooth[i]; + } } if (psum > 0) { - *z = psum; + *z = psum >> ATP_SCALE; /* Scale down pressure output. */ return pcum * fact / psum; } @@ -551,16 +605,16 @@ static void atp_complete_geyser_1_2(struct urb *urb) dbg_dump("accumulator", dev->xy_acc); - x = atp_calculate_abs(dev->xy_acc, ATP_XSENSORS, + x = atp_calculate_abs(dev, 0, ATP_XSENSORS, dev->info->xfact, &x_z, &x_f); - y = atp_calculate_abs(dev->xy_acc + ATP_XSENSORS, ATP_YSENSORS, + y = atp_calculate_abs(dev, ATP_XSENSORS, ATP_YSENSORS, dev->info->yfact, &y_z, &y_f); key = dev->data[dev->info->datalen - 1] & ATP_STATUS_BUTTON; if (x && y) { if (dev->x_old != -1) { - x = (dev->x_old * 3 + x) >> 2; - y = (dev->y_old * 3 + y) >> 2; + x = (dev->x_old * 7 + x) >> 3; + y = (dev->y_old * 7 + y) >> 3; dev->x_old = x; dev->y_old = y; @@ -663,16 +717,17 @@ static void atp_complete_geyser_3_4(struct urb *urb) dbg_dump("accumulator", dev->xy_acc); - x = atp_calculate_abs(dev->xy_acc, ATP_XSENSORS, + x = atp_calculate_abs(dev, 0, ATP_XSENSORS, dev->info->xfact, &x_z, &x_f); - y = atp_calculate_abs(dev->xy_acc + ATP_XSENSORS, ATP_YSENSORS, + y = atp_calculate_abs(dev, ATP_XSENSORS, ATP_YSENSORS, dev->info->yfact, &y_z, &y_f); + key = dev->data[dev->info->datalen - 1] & ATP_STATUS_BUTTON; if (x && y) { if (dev->x_old != -1) { - x = (dev->x_old * 3 + x) >> 2; - y = (dev->y_old * 3 + y) >> 2; + x = (dev->x_old * 7 + x) >> 3; + y = (dev->y_old * 7 + y) >> 3; dev->x_old = x; dev->y_old = y; -- cgit v1.2.3 From 7bbdba56ba9e01e610f7455c0375f9bbef84f3b1 Mon Sep 17 00:00:00 2001 From: Clinton Sprain Date: Sun, 30 Mar 2014 23:37:05 -0700 Subject: Input: appletouch - fix jumps when additional fingers are detected Addresses issues related to when a second finger enters or leaves the field, causing the cursor to jump or the page to scroll unexpectedly; now, we discard any movement change that happens at the exact moment we detect a change in the number of fingers touching the trackpad. This doesn't completely resolve the issue but does greatly mitigate it. Signed-off-by: Clinton Sprain Reviewed-by: Henrik Rydberg Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/appletouch.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/drivers/input/mouse/appletouch.c b/drivers/input/mouse/appletouch.c index 2dbf7a0f0537..ef234c9b2f2f 100644 --- a/drivers/input/mouse/appletouch.c +++ b/drivers/input/mouse/appletouch.c @@ -218,6 +218,7 @@ struct atp { bool valid; /* are the samples valid? */ bool size_detect_done; bool overflow_warned; + int fingers_old; /* last reported finger count */ int x_old; /* last reported x/y, */ int y_old; /* used for smoothing */ signed char xy_cur[ATP_XSENSORS + ATP_YSENSORS]; @@ -528,7 +529,7 @@ static void atp_complete_geyser_1_2(struct urb *urb) { int x, y, x_z, y_z, x_f, y_f; int retval, i, j; - int key; + int key, fingers; struct atp *dev = urb->context; int status = atp_status_check(urb); @@ -611,7 +612,9 @@ static void atp_complete_geyser_1_2(struct urb *urb) dev->info->yfact, &y_z, &y_f); key = dev->data[dev->info->datalen - 1] & ATP_STATUS_BUTTON; - if (x && y) { + fingers = max(x_f, y_f); + + if (x && y && fingers == dev->fingers_old) { if (dev->x_old != -1) { x = (dev->x_old * 7 + x) >> 3; y = (dev->y_old * 7 + y) >> 3; @@ -628,7 +631,7 @@ static void atp_complete_geyser_1_2(struct urb *urb) input_report_abs(dev->input, ABS_Y, y); input_report_abs(dev->input, ABS_PRESSURE, min(ATP_PRESSURE, x_z + y_z)); - atp_report_fingers(dev->input, max(x_f, y_f)); + atp_report_fingers(dev->input, fingers); } dev->x_old = x; dev->y_old = y; @@ -636,6 +639,7 @@ static void atp_complete_geyser_1_2(struct urb *urb) } else if (!x && !y) { dev->x_old = dev->y_old = -1; + dev->fingers_old = 0; input_report_key(dev->input, BTN_TOUCH, 0); input_report_abs(dev->input, ABS_PRESSURE, 0); atp_report_fingers(dev->input, 0); @@ -644,6 +648,10 @@ static void atp_complete_geyser_1_2(struct urb *urb) memset(dev->xy_acc, 0, sizeof(dev->xy_acc)); } + if (fingers != dev->fingers_old) + dev->x_old = dev->y_old = -1; + dev->fingers_old = fingers; + input_report_key(dev->input, BTN_LEFT, key); input_sync(dev->input); @@ -661,7 +669,7 @@ static void atp_complete_geyser_3_4(struct urb *urb) { int x, y, x_z, y_z, x_f, y_f; int retval, i, j; - int key; + int key, fingers; struct atp *dev = urb->context; int status = atp_status_check(urb); @@ -724,7 +732,9 @@ static void atp_complete_geyser_3_4(struct urb *urb) key = dev->data[dev->info->datalen - 1] & ATP_STATUS_BUTTON; - if (x && y) { + fingers = max(x_f, y_f); + + if (x && y && fingers == dev->fingers_old) { if (dev->x_old != -1) { x = (dev->x_old * 7 + x) >> 3; y = (dev->y_old * 7 + y) >> 3; @@ -741,7 +751,7 @@ static void atp_complete_geyser_3_4(struct urb *urb) input_report_abs(dev->input, ABS_Y, y); input_report_abs(dev->input, ABS_PRESSURE, min(ATP_PRESSURE, x_z + y_z)); - atp_report_fingers(dev->input, max(x_f, y_f)); + atp_report_fingers(dev->input, fingers); } dev->x_old = x; dev->y_old = y; @@ -749,6 +759,7 @@ static void atp_complete_geyser_3_4(struct urb *urb) } else if (!x && !y) { dev->x_old = dev->y_old = -1; + dev->fingers_old = 0; input_report_key(dev->input, BTN_TOUCH, 0); input_report_abs(dev->input, ABS_PRESSURE, 0); atp_report_fingers(dev->input, 0); @@ -757,6 +768,10 @@ static void atp_complete_geyser_3_4(struct urb *urb) memset(dev->xy_acc, 0, sizeof(dev->xy_acc)); } + if (fingers != dev->fingers_old) + dev->x_old = dev->y_old = -1; + dev->fingers_old = fingers; + input_report_key(dev->input, BTN_LEFT, key); input_sync(dev->input); -- cgit v1.2.3 From e2c3ecf0ea8e87c5209371af7da107ebc47a5639 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 3 Apr 2014 09:17:05 -0700 Subject: Input: edt-ft5x06 - add a missing condition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The if condition was accidentally deleted here so we return every time instead of returning on error. Fixes: fd335ab04b3f ('Input: edt-ft5x06 - add support for M09 firmware version') Signed-off-by: Dan Carpenter Reviewed-by: Jingoo Han Acked-By: Lothar Waßmann Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/edt-ft5x06.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c index 75b666bd2654..f8815bebc9ef 100644 --- a/drivers/input/touchscreen/edt-ft5x06.c +++ b/drivers/input/touchscreen/edt-ft5x06.c @@ -291,9 +291,10 @@ static int edt_ft5x06_register_read(struct edt_ft5x06_ts_data *tsdata, wrbuf[1] = tsdata->factory_mode ? addr & 0x7f : addr & 0x3f; wrbuf[1] |= tsdata->factory_mode ? 0x80 : 0x40; - error = edt_ft5x06_ts_readwrite(tsdata->client, - 2, wrbuf, 2, rdbuf); - return error; + error = edt_ft5x06_ts_readwrite(tsdata->client, 2, wrbuf, 2, + rdbuf); + if (error) + return error; if ((wrbuf[0] ^ wrbuf[1] ^ rdbuf[0]) != rdbuf[1]) { dev_err(&tsdata->client->dev, -- cgit v1.2.3