From 7488b1b94b46f262a60a94d9468d324201574cf8 Mon Sep 17 00:00:00 2001 From: Vipul Kumar Samar Date: Sun, 16 Dec 2012 23:10:56 -0800 Subject: [PATCH 01/55] Input: stmpe-ts - report BTN_TOUCH event stmpe touchscreen supports BTN_TOUCH event but doesn't report it. Add BTN_TOUCH reporting capability to the stmpe touchscreen driver. Signed-off-by: Vipul Kumar Samar Reviewed-by: Viresh Kumar Acked-by: Lee Jones Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/stmpe-ts.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/input/touchscreen/stmpe-ts.c b/drivers/input/touchscreen/stmpe-ts.c index 84d884b4ec3e..59e81b00f244 100644 --- a/drivers/input/touchscreen/stmpe-ts.c +++ b/drivers/input/touchscreen/stmpe-ts.c @@ -120,6 +120,7 @@ static void stmpe_work(struct work_struct *work) __stmpe_reset_fifo(ts->stmpe); input_report_abs(ts->idev, ABS_PRESSURE, 0); + input_report_key(ts->idev, BTN_TOUCH, 0); input_sync(ts->idev); } @@ -153,6 +154,7 @@ static irqreturn_t stmpe_ts_handler(int irq, void *data) input_report_abs(ts->idev, ABS_X, x); input_report_abs(ts->idev, ABS_Y, y); input_report_abs(ts->idev, ABS_PRESSURE, z); + input_report_key(ts->idev, BTN_TOUCH, 1); input_sync(ts->idev); /* flush the FIFO after we have read out our values. */ From e7cd0aeb323441228ebbffb0c5a78ea1132663fd Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 24 Dec 2012 09:44:43 -0800 Subject: [PATCH 02/55] Input: wm831x-ts - convert to devm_input_allocate_device() Signed-off-by: Mark Brown Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/wm831x-ts.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/input/touchscreen/wm831x-ts.c b/drivers/input/touchscreen/wm831x-ts.c index f88fab56178c..6be2eb6a153a 100644 --- a/drivers/input/touchscreen/wm831x-ts.c +++ b/drivers/input/touchscreen/wm831x-ts.c @@ -247,7 +247,7 @@ static int wm831x_ts_probe(struct platform_device *pdev) wm831x_ts = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_ts), GFP_KERNEL); - input_dev = input_allocate_device(); + input_dev = devm_input_allocate_device(&pdev->dev); if (!wm831x_ts || !input_dev) { error = -ENOMEM; goto err_alloc; @@ -376,7 +376,6 @@ static int wm831x_ts_probe(struct platform_device *pdev) err_data_irq: free_irq(wm831x_ts->data_irq, wm831x_ts); err_alloc: - input_free_device(input_dev); return error; } @@ -387,7 +386,6 @@ static int wm831x_ts_remove(struct platform_device *pdev) free_irq(wm831x_ts->pd_irq, wm831x_ts); free_irq(wm831x_ts->data_irq, wm831x_ts); - input_unregister_device(wm831x_ts->input_dev); return 0; } From 5d3caa442e0858fa07b9a6a68d05e1ba2cf246a4 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 24 Dec 2012 09:45:22 -0800 Subject: [PATCH 03/55] Input: wm831x-on - convert to devm_input_allocate_device() Signed-off-by: Mark Brown Signed-off-by: Dmitry Torokhov --- drivers/input/misc/wm831x-on.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/input/misc/wm831x-on.c b/drivers/input/misc/wm831x-on.c index 558767d8ebf4..caa2c4068f09 100644 --- a/drivers/input/misc/wm831x-on.c +++ b/drivers/input/misc/wm831x-on.c @@ -86,7 +86,7 @@ static int wm831x_on_probe(struct platform_device *pdev) wm831x_on->wm831x = wm831x; INIT_DELAYED_WORK(&wm831x_on->work, wm831x_poll_on); - wm831x_on->dev = input_allocate_device(); + wm831x_on->dev = devm_input_allocate_device(&pdev->dev); if (!wm831x_on->dev) { dev_err(&pdev->dev, "Can't allocate input dev\n"); ret = -ENOMEM; @@ -119,7 +119,6 @@ static int wm831x_on_probe(struct platform_device *pdev) err_irq: free_irq(irq, wm831x_on); err_input_dev: - input_free_device(wm831x_on->dev); err: return ret; } @@ -131,7 +130,6 @@ static int wm831x_on_remove(struct platform_device *pdev) free_irq(irq, wm831x_on); cancel_delayed_work_sync(&wm831x_on->work); - input_unregister_device(wm831x_on->dev); return 0; } From ad4e58b0478ba8d34a3ddf695b3ceabe9510a839 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 24 Dec 2012 09:48:55 -0800 Subject: [PATCH 04/55] Input: adxl34x - consistently use read/write encapsulation Don't open code the AC_READ and AC_WRITE macros. Signed-off-by: Wolfram Sang Acked-by: Michael Hennerich Signed-off-by: Dmitry Torokhov --- drivers/input/misc/adxl34x.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/input/misc/adxl34x.c b/drivers/input/misc/adxl34x.c index 1cf72fe513e6..b18d3e2a6ea5 100644 --- a/drivers/input/misc/adxl34x.c +++ b/drivers/input/misc/adxl34x.c @@ -732,7 +732,7 @@ struct adxl34x *adxl34x_probe(struct device *dev, int irq, mutex_init(&ac->mutex); input_dev->name = "ADXL34x accelerometer"; - revid = ac->bops->read(dev, DEVID); + revid = AC_READ(ac, DEVID); switch (revid) { case ID_ADXL345: @@ -809,7 +809,7 @@ struct adxl34x *adxl34x_probe(struct device *dev, int irq, if (FIFO_MODE(pdata->fifo_mode) == FIFO_BYPASS) ac->fifo_delay = false; - ac->bops->write(dev, POWER_CTL, 0); + AC_WRITE(ac, POWER_CTL, 0); err = request_threaded_irq(ac->irq, NULL, adxl34x_irq, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, From 1fae0315e636dd9b134d2c51b04cec912f02a6c9 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 24 Dec 2012 09:49:36 -0800 Subject: [PATCH 05/55] Input: adxl34x - don't set THRESH_TAP twice The datasheet doesn't say anything about writing twice, so this was probably overlooked. Signed-off-by: Wolfram Sang Acked-by: Michael Hennerich Signed-off-by: Dmitry Torokhov --- drivers/input/misc/adxl34x.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/input/misc/adxl34x.c b/drivers/input/misc/adxl34x.c index b18d3e2a6ea5..6be8687de056 100644 --- a/drivers/input/misc/adxl34x.c +++ b/drivers/input/misc/adxl34x.c @@ -827,7 +827,6 @@ struct adxl34x *adxl34x_probe(struct device *dev, int irq, if (err) goto err_remove_attr; - AC_WRITE(ac, THRESH_TAP, pdata->tap_threshold); AC_WRITE(ac, OFSX, pdata->x_axis_offset); ac->hwcal.x = pdata->x_axis_offset; AC_WRITE(ac, OFSY, pdata->y_axis_offset); From 176eb8dc91bd91e7b1beb1761ee29f016e8c09e8 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 24 Dec 2012 09:49:55 -0800 Subject: [PATCH 06/55] Input: adxl34x - make platform_data include self contained Since it suggests to use defines from input.h (ABS_X, ...), also include the file to make them available. Signed-off-by: Wolfram Sang Acked-by: Michael Hennerich Signed-off-by: Dmitry Torokhov --- include/linux/input/adxl34x.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/input/adxl34x.h b/include/linux/input/adxl34x.h index 57e01a7cb006..010d98175efa 100644 --- a/include/linux/input/adxl34x.h +++ b/include/linux/input/adxl34x.h @@ -13,6 +13,8 @@ #ifndef __LINUX_INPUT_ADXL34X_H__ #define __LINUX_INPUT_ADXL34X_H__ +#include + struct adxl34x_platform_data { /* From 03c86ee1548a6ffecc65cf83b0a2d2774fed2f1d Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Mon, 24 Dec 2012 09:50:13 -0800 Subject: [PATCH 07/55] Input: adxl34x - default platform_data should not use defines from driver Only use the defines which are defined in the platform_data include file. Signed-off-by: Wolfram Sang Acked-by: Michael Hennerich Signed-off-by: Dmitry Torokhov --- drivers/input/misc/adxl34x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/misc/adxl34x.c b/drivers/input/misc/adxl34x.c index 6be8687de056..0735de3a6468 100644 --- a/drivers/input/misc/adxl34x.c +++ b/drivers/input/misc/adxl34x.c @@ -232,7 +232,7 @@ static const struct adxl34x_platform_data adxl34x_default_init = { .ev_code_tap = {BTN_TOUCH, BTN_TOUCH, BTN_TOUCH}, /* EV_KEY {x,y,z} */ .power_mode = ADXL_AUTO_SLEEP | ADXL_LINK, - .fifo_mode = FIFO_STREAM, + .fifo_mode = ADXL_FIFO_STREAM, .watermark = 0, }; From 0e14235e6cfbc9b7a546d38b51c4e7ffdab41045 Mon Sep 17 00:00:00 2001 From: Liu Ying Date: Thu, 3 Jan 2013 12:25:46 -0800 Subject: [PATCH 08/55] Input: imx_keypad - add device tree support This patch adds device tree support for imx keypad driver. Signed-off-by: Liu Ying Signed-off-by: Dmitry Torokhov --- .../devicetree/bindings/input/imx-keypad.txt | 53 +++++++++++++++++++ drivers/input/keyboard/imx_keypad.c | 43 ++++++++------- 2 files changed, 78 insertions(+), 18 deletions(-) create mode 100644 Documentation/devicetree/bindings/input/imx-keypad.txt diff --git a/Documentation/devicetree/bindings/input/imx-keypad.txt b/Documentation/devicetree/bindings/input/imx-keypad.txt new file mode 100644 index 000000000000..2ebaf7d26843 --- /dev/null +++ b/Documentation/devicetree/bindings/input/imx-keypad.txt @@ -0,0 +1,53 @@ +* Freescale i.MX Keypad Port(KPP) device tree bindings + +The KPP is designed to interface with a keypad matrix with 2-point contact +or 3-point contact keys. The KPP is designed to simplify the software task +of scanning a keypad matrix. The KPP is capable of detecting, debouncing, +and decoding one or multiple keys pressed simultaneously on a keypad. + +Required SoC Specific Properties: +- compatible: Should be "fsl,-kpp". + +- reg: Physical base address of the KPP and length of memory mapped + region. + +- interrupts: The KPP interrupt number to the CPU(s). + +- clocks: The clock provided by the SoC to the KPP. Some SoCs use dummy +clock(The clock for the KPP is provided by the SoCs automatically). + +Required Board Specific Properties: +- pinctrl-names: The definition can be found at +pinctrl/pinctrl-bindings.txt. + +- pinctrl-0: The definition can be found at +pinctrl/pinctrl-bindings.txt. + +- linux,keymap: The definition can be found at +bindings/input/matrix-keymap.txt. + +Example: +kpp: kpp@73f94000 { + compatible = "fsl,imx51-kpp", "fsl,imx21-kpp"; + reg = <0x73f94000 0x4000>; + interrupts = <60>; + clocks = <&clks 0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_kpp_1>; + linux,keymap = <0x00000067 /* KEY_UP */ + 0x0001006c /* KEY_DOWN */ + 0x00020072 /* KEY_VOLUMEDOWN */ + 0x00030066 /* KEY_HOME */ + 0x0100006a /* KEY_RIGHT */ + 0x01010069 /* KEY_LEFT */ + 0x0102001c /* KEY_ENTER */ + 0x01030073 /* KEY_VOLUMEUP */ + 0x02000040 /* KEY_F6 */ + 0x02010042 /* KEY_F8 */ + 0x02020043 /* KEY_F9 */ + 0x02030044 /* KEY_F10 */ + 0x0300003b /* KEY_F1 */ + 0x0301003c /* KEY_F2 */ + 0x0302003d /* KEY_F3 */ + 0x03030074>; /* KEY_POWER */ +}; diff --git a/drivers/input/keyboard/imx_keypad.c b/drivers/input/keyboard/imx_keypad.c index 6d150e3e1f55..98f9113251d2 100644 --- a/drivers/input/keyboard/imx_keypad.c +++ b/drivers/input/keyboard/imx_keypad.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -414,15 +415,23 @@ static int imx_keypad_open(struct input_dev *dev) return -EIO; } +#ifdef CONFIG_OF +static struct of_device_id imx_keypad_of_match[] = { + { .compatible = "fsl,imx21-kpp", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, imx_keypad_of_match); +#endif + static int imx_keypad_probe(struct platform_device *pdev) { const struct matrix_keymap_data *keymap_data = pdev->dev.platform_data; struct imx_keypad *keypad; struct input_dev *input_dev; struct resource *res; - int irq, error, i; + int irq, error, i, row, col; - if (keymap_data == NULL) { + if (!keymap_data && !pdev->dev.of_node) { dev_err(&pdev->dev, "no keymap defined\n"); return -EINVAL; } @@ -480,22 +489,6 @@ static int imx_keypad_probe(struct platform_device *pdev) goto failed_unmap; } - /* Search for rows and cols enabled */ - for (i = 0; i < keymap_data->keymap_size; i++) { - keypad->rows_en_mask |= 1 << KEY_ROW(keymap_data->keymap[i]); - keypad->cols_en_mask |= 1 << KEY_COL(keymap_data->keymap[i]); - } - - if (keypad->rows_en_mask > ((1 << MAX_MATRIX_KEY_ROWS) - 1) || - keypad->cols_en_mask > ((1 << MAX_MATRIX_KEY_COLS) - 1)) { - dev_err(&pdev->dev, - "invalid key data (too many rows or colums)\n"); - error = -EINVAL; - goto failed_clock_put; - } - dev_dbg(&pdev->dev, "enabled rows mask: %x\n", keypad->rows_en_mask); - dev_dbg(&pdev->dev, "enabled cols mask: %x\n", keypad->cols_en_mask); - /* Init the Input device */ input_dev->name = pdev->name; input_dev->id.bustype = BUS_HOST; @@ -512,6 +505,19 @@ static int imx_keypad_probe(struct platform_device *pdev) goto failed_clock_put; } + /* Search for rows and cols enabled */ + for (row = 0; row < MAX_MATRIX_KEY_ROWS; row++) { + for (col = 0; col < MAX_MATRIX_KEY_COLS; col++) { + i = MATRIX_SCAN_CODE(row, col, MATRIX_ROW_SHIFT); + if (keypad->keycodes[i] != KEY_RESERVED) { + keypad->rows_en_mask |= 1 << row; + keypad->cols_en_mask |= 1 << col; + } + } + } + dev_dbg(&pdev->dev, "enabled rows mask: %x\n", keypad->rows_en_mask); + dev_dbg(&pdev->dev, "enabled cols mask: %x\n", keypad->cols_en_mask); + __set_bit(EV_REP, input_dev->evbit); input_set_capability(input_dev, EV_MSC, MSC_SCAN); input_set_drvdata(input_dev, keypad); @@ -631,6 +637,7 @@ static struct platform_driver imx_keypad_driver = { .name = "imx-keypad", .owner = THIS_MODULE, .pm = &imx_kbd_pm_ops, + .of_match_table = of_match_ptr(imx_keypad_of_match), }, .probe = imx_keypad_probe, .remove = imx_keypad_remove, From 0e47e3dccfcfaa262d3162ab353474d91d792000 Mon Sep 17 00:00:00 2001 From: Javier Martin Date: Fri, 4 Jan 2013 16:36:46 -0800 Subject: [PATCH 09/55] Input: qt2160 - add support for LEDs Outputs x8..x0 of the qt2160 can have leds attached to it. This patch handles those outputs using the generic LED framework. The PWM controls available in the chip are used to achieve different levels of brightness. Signed-off-by: Javier Martin Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/qt2160.c | 141 +++++++++++++++++++++++++++++++- 1 file changed, 139 insertions(+), 2 deletions(-) diff --git a/drivers/input/keyboard/qt2160.c b/drivers/input/keyboard/qt2160.c index 3dc2b0f27b0c..1c0ddad0a1cc 100644 --- a/drivers/input/keyboard/qt2160.c +++ b/drivers/input/keyboard/qt2160.c @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -39,6 +40,11 @@ #define QT2160_CMD_GPIOS 6 #define QT2160_CMD_SUBVER 7 #define QT2160_CMD_CALIBRATE 10 +#define QT2160_CMD_DRIVE_X 70 +#define QT2160_CMD_PWMEN_X 74 +#define QT2160_CMD_PWM_DUTY 76 + +#define QT2160_NUM_LEDS_X 8 #define QT2160_CYCLE_INTERVAL (2*HZ) @@ -49,6 +55,17 @@ static unsigned char qt2160_key2code[] = { KEY_C, KEY_D, KEY_E, KEY_F, }; +#ifdef CONFIG_LEDS_CLASS +struct qt2160_led { + struct qt2160_data *qt2160; + struct led_classdev cdev; + struct work_struct work; + char name[32]; + int id; + enum led_brightness new_brightness; +}; +#endif + struct qt2160_data { struct i2c_client *client; struct input_dev *input; @@ -56,8 +73,61 @@ struct qt2160_data { spinlock_t lock; /* Protects canceling/rescheduling of dwork */ unsigned short keycodes[ARRAY_SIZE(qt2160_key2code)]; u16 key_matrix; +#ifdef CONFIG_LEDS_CLASS + struct qt2160_led leds[QT2160_NUM_LEDS_X]; + struct mutex led_lock; +#endif }; +static int qt2160_read(struct i2c_client *client, u8 reg); +static int qt2160_write(struct i2c_client *client, u8 reg, u8 data); + +#ifdef CONFIG_LEDS_CLASS + +static void qt2160_led_work(struct work_struct *work) +{ + struct qt2160_led *led = container_of(work, struct qt2160_led, work); + struct qt2160_data *qt2160 = led->qt2160; + struct i2c_client *client = qt2160->client; + int value = led->new_brightness; + u32 drive, pwmen; + + mutex_lock(&qt2160->led_lock); + + drive = qt2160_read(client, QT2160_CMD_DRIVE_X); + pwmen = qt2160_read(client, QT2160_CMD_PWMEN_X); + if (value != LED_OFF) { + drive |= (1 << led->id); + pwmen |= (1 << led->id); + + } else { + drive &= ~(1 << led->id); + pwmen &= ~(1 << led->id); + } + qt2160_write(client, QT2160_CMD_DRIVE_X, drive); + qt2160_write(client, QT2160_CMD_PWMEN_X, pwmen); + + /* + * Changing this register will change the brightness + * of every LED in the qt2160. It's a HW limitation. + */ + if (value != LED_OFF) + qt2160_write(client, QT2160_CMD_PWM_DUTY, value); + + mutex_unlock(&qt2160->led_lock); +} + +static void qt2160_led_set(struct led_classdev *cdev, + enum led_brightness value) +{ + struct qt2160_led *led = container_of(cdev, struct qt2160_led, cdev); + + led->new_brightness = value; + schedule_work(&led->work); +} + +#endif /* CONFIG_LEDS_CLASS */ + static int qt2160_read_block(struct i2c_client *client, u8 inireg, u8 *buffer, unsigned int count) { @@ -216,6 +286,63 @@ static int qt2160_write(struct i2c_client *client, u8 reg, u8 data) return ret; } +#ifdef CONFIG_LEDS_CLASS + +static int qt2160_register_leds(struct qt2160_data *qt2160) +{ + struct i2c_client *client = qt2160->client; + int ret; + int i; + + mutex_init(&qt2160->led_lock); + + for (i = 0; i < QT2160_NUM_LEDS_X; i++) { + struct qt2160_led *led = &qt2160->leds[i]; + + snprintf(led->name, sizeof(led->name), "qt2160:x%d", i); + led->cdev.name = led->name; + led->cdev.brightness_set = qt2160_led_set; + led->cdev.brightness = LED_OFF; + led->id = i; + led->qt2160 = qt2160; + + INIT_WORK(&led->work, qt2160_led_work); + + ret = led_classdev_register(&client->dev, &led->cdev); + if (ret < 0) + return ret; + } + + /* Tur off LEDs */ + qt2160_write(client, QT2160_CMD_DRIVE_X, 0); + qt2160_write(client, QT2160_CMD_PWMEN_X, 0); + qt2160_write(client, QT2160_CMD_PWM_DUTY, 0); + + return 0; +} + +static void qt2160_unregister_leds(struct qt2160_data *qt2160) +{ + int i; + + for (i = 0; i < QT2160_NUM_LEDS_X; i++) { + led_classdev_unregister(&qt2160->leds[i].cdev); + cancel_work_sync(&qt2160->leds[i].work); + } +} + +#else + +static inline int qt2160_register_leds(struct qt2160_data *qt2160) +{ + return 0; +} + +static inline void qt2160_unregister_leds(struct qt2160_data *qt2160) +{ +} + +#endif static bool qt2160_identify(struct i2c_client *client) { @@ -249,7 +376,7 @@ static bool qt2160_identify(struct i2c_client *client) } static int qt2160_probe(struct i2c_client *client, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { struct qt2160_data *qt2160; struct input_dev *input; @@ -314,11 +441,17 @@ static int qt2160_probe(struct i2c_client *client, } } + error = qt2160_register_leds(qt2160); + if (error) { + dev_err(&client->dev, "Failed to register leds\n"); + goto err_free_irq; + } + error = input_register_device(qt2160->input); if (error) { dev_err(&client->dev, "Failed to register input device\n"); - goto err_free_irq; + goto err_unregister_leds; } i2c_set_clientdata(client, qt2160); @@ -326,6 +459,8 @@ static int qt2160_probe(struct i2c_client *client, return 0; +err_unregister_leds: + qt2160_unregister_leds(qt2160); err_free_irq: if (client->irq) free_irq(client->irq, qt2160); @@ -339,6 +474,8 @@ static int qt2160_remove(struct i2c_client *client) { struct qt2160_data *qt2160 = i2c_get_clientdata(client); + qt2160_unregister_leds(qt2160); + /* Release IRQ so no queue will be scheduled */ if (client->irq) free_irq(client->irq, qt2160); From 8c0e0a4fa88f53d80ae8c410a9e2e364a96941a4 Mon Sep 17 00:00:00 2001 From: Ping Cheng Date: Fri, 4 Jan 2013 23:53:52 -0800 Subject: [PATCH 10/55] Input: wacom - prepare for syncing with input-mt changes Henrik added new MT routines in release 3.7. This patch is to prepare for the use of new MT routines. In the newly added wacom_abs_set_axis() function, the first if-statement assigns ABS_X/Y for number of contacts less or equal to 2. So, it is single and 2 finger touch devices. Two finger touch devices are processed here since they will not use the updated input_mt_init_slots(), which does not offer benefit to those devices. input_mt_init_slots() will take care of ABS_X/Y assignment when flags are not zero. All touch devices with more than two contacts will use input_mt_init_slots() with non-zero flags. The second if-statement is for all MT devices, which include two finger touch devices. Signed-off-by: Ping Cheng Reviewed-by: Henrik Rydberg Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/wacom_wac.c | 89 +++++++++++++++++--------------- 1 file changed, 47 insertions(+), 42 deletions(-) diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index c0f3e9104832..097b559ea838 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -1444,6 +1444,51 @@ static unsigned int wacom_calculate_touch_res(unsigned int logical_max, return (logical_max * 100) / physical_max; } +static void wacom_abs_set_axis(struct input_dev *input_dev, + struct wacom_wac *wacom_wac) +{ + struct wacom_features *features = &wacom_wac->features; + + if (features->device_type == BTN_TOOL_PEN) { + input_set_abs_params(input_dev, ABS_X, 0, features->x_max, + features->x_fuzz, 0); + input_set_abs_params(input_dev, ABS_Y, 0, features->y_max, + features->y_fuzz, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, + features->pressure_max, features->pressure_fuzz, 0); + + /* penabled devices have fixed resolution for each model */ + input_abs_set_res(input_dev, ABS_X, features->x_resolution); + input_abs_set_res(input_dev, ABS_Y, features->y_resolution); + } else { + if (features->touch_max <= 2) { + input_set_abs_params(input_dev, ABS_X, 0, + features->x_max, features->x_fuzz, 0); + input_set_abs_params(input_dev, ABS_Y, 0, + features->y_max, features->y_fuzz, 0); + input_abs_set_res(input_dev, ABS_X, + wacom_calculate_touch_res(features->x_max, + features->x_phy)); + input_abs_set_res(input_dev, ABS_Y, + wacom_calculate_touch_res(features->y_max, + features->y_phy)); + } + + if (features->touch_max > 1) { + input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, + features->x_max, features->x_fuzz, 0); + input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, + features->y_max, features->y_fuzz, 0); + input_abs_set_res(input_dev, ABS_MT_POSITION_X, + wacom_calculate_touch_res(features->x_max, + features->x_phy)); + input_abs_set_res(input_dev, ABS_MT_POSITION_Y, + wacom_calculate_touch_res(features->y_max, + features->y_phy)); + } + } +} + int wacom_setup_input_capabilities(struct input_dev *input_dev, struct wacom_wac *wacom_wac) { @@ -1453,30 +1498,10 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev, input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); __set_bit(BTN_TOUCH, input_dev->keybit); - - input_set_abs_params(input_dev, ABS_X, 0, features->x_max, - features->x_fuzz, 0); - input_set_abs_params(input_dev, ABS_Y, 0, features->y_max, - features->y_fuzz, 0); - - if (features->device_type == BTN_TOOL_PEN) { - input_set_abs_params(input_dev, ABS_PRESSURE, 0, features->pressure_max, - features->pressure_fuzz, 0); - - /* penabled devices have fixed resolution for each model */ - input_abs_set_res(input_dev, ABS_X, features->x_resolution); - input_abs_set_res(input_dev, ABS_Y, features->y_resolution); - } else { - input_abs_set_res(input_dev, ABS_X, - wacom_calculate_touch_res(features->x_max, - features->x_phy)); - input_abs_set_res(input_dev, ABS_Y, - wacom_calculate_touch_res(features->y_max, - features->y_phy)); - } - __set_bit(ABS_MISC, input_dev->absbit); + wacom_abs_set_axis(input_dev, wacom_wac); + switch (wacom_wac->features.type) { case WACOM_MO: input_set_abs_params(input_dev, ABS_WHEEL, 0, 71, 0, 0); @@ -1625,13 +1650,6 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev, 0, features->x_max, 0, 0); input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR, 0, features->y_max, 0, 0); - - input_set_abs_params(input_dev, ABS_MT_POSITION_X, - 0, features->x_max, - features->x_fuzz, 0); - input_set_abs_params(input_dev, ABS_MT_POSITION_Y, - 0, features->y_max, - features->y_fuzz, 0); } break; @@ -1677,12 +1695,6 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev, case TABLETPC2FG: if (features->device_type == BTN_TOOL_FINGER) { input_mt_init_slots(input_dev, features->touch_max, 0); - input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE, - 0, MT_TOOL_MAX, 0, 0); - input_set_abs_params(input_dev, ABS_MT_POSITION_X, - 0, features->x_max, 0, 0); - input_set_abs_params(input_dev, ABS_MT_POSITION_Y, - 0, features->y_max, 0, 0); } /* fall through */ @@ -1747,13 +1759,6 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev, ABS_MT_TOUCH_MINOR, 0, features->y_max, 0, 0); } - - input_set_abs_params(input_dev, ABS_MT_POSITION_X, - 0, features->x_max, - features->x_fuzz, 0); - input_set_abs_params(input_dev, ABS_MT_POSITION_Y, - 0, features->y_max, - features->y_fuzz, 0); } else if (features->device_type == BTN_TOOL_PEN) { __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); __set_bit(BTN_TOOL_PEN, input_dev->keybit); From 02295e68cf6ae81e94fbcfa61afde299ded593b1 Mon Sep 17 00:00:00 2001 From: Ping Cheng Date: Fri, 4 Jan 2013 23:56:00 -0800 Subject: [PATCH 11/55] Input: wacom - use new input-mt routines This patch brings wacom driver in-sync with input-mt changes made in release 3.7. Signed-off-by: Ping Cheng Reviewed-by: Henrik Rydberg Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/wacom_wac.c | 82 +++++++++----------------------- drivers/input/tablet/wacom_wac.h | 1 - 2 files changed, 22 insertions(+), 61 deletions(-) diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index 097b559ea838..a318bf4ada05 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -785,25 +785,6 @@ static int wacom_intuos_irq(struct wacom_wac *wacom) return 1; } -static int find_slot_from_contactid(struct wacom_wac *wacom, int contactid) -{ - int touch_max = wacom->features.touch_max; - int i; - - if (!wacom->slots) - return -1; - - for (i = 0; i < touch_max; ++i) { - if (wacom->slots[i] == contactid) - return i; - } - for (i = 0; i < touch_max; ++i) { - if (wacom->slots[i] == -1) - return i; - } - return -1; -} - static int int_dist(int x1, int y1, int x2, int y2) { int x = x2 - x1; @@ -833,8 +814,7 @@ static int wacom_24hdt_irq(struct wacom_wac *wacom) for (i = 0; i < contacts_to_send; i++) { int offset = (WACOM_BYTES_PER_24HDT_PACKET * i) + 1; bool touch = data[offset] & 0x1 && !wacom->shared->stylus_in_proximity; - int id = data[offset + 1]; - int slot = find_slot_from_contactid(wacom, id); + int slot = input_mt_get_slot_by_key(input, data[offset + 1]); if (slot < 0) continue; @@ -856,9 +836,7 @@ static int wacom_24hdt_irq(struct wacom_wac *wacom) input_report_abs(input, ABS_MT_WIDTH_MINOR, min(w, h)); input_report_abs(input, ABS_MT_ORIENTATION, w > h); } - wacom->slots[slot] = touch ? id : -1; } - input_mt_report_pointer_emulation(input, true); wacom->num_contacts_left -= contacts_to_send; @@ -895,7 +873,7 @@ static int wacom_mt_touch(struct wacom_wac *wacom) int offset = (WACOM_BYTES_PER_MT_PACKET + x_offset) * i + 3; bool touch = data[offset] & 0x1; int id = le16_to_cpup((__le16 *)&data[offset + 1]); - int slot = find_slot_from_contactid(wacom, id); + int slot = input_mt_get_slot_by_key(input, id); if (slot < 0) continue; @@ -908,9 +886,7 @@ static int wacom_mt_touch(struct wacom_wac *wacom) input_report_abs(input, ABS_MT_POSITION_X, x); input_report_abs(input, ABS_MT_POSITION_Y, y); } - wacom->slots[slot] = touch ? id : -1; } - input_mt_report_pointer_emulation(input, true); wacom->num_contacts_left -= contacts_to_send; @@ -942,12 +918,11 @@ static int wacom_tpc_mt_touch(struct wacom_wac *wacom) contact_with_no_pen_down_count++; } } + input_mt_report_pointer_emulation(input, true); /* keep touch state for pen event */ wacom->shared->touch_down = (contact_with_no_pen_down_count > 0); - input_mt_report_pointer_emulation(input, true); - return 1; } @@ -1104,12 +1079,15 @@ static int wacom_bpt_touch(struct wacom_wac *wacom) static void wacom_bpt3_touch_msg(struct wacom_wac *wacom, unsigned char *data) { struct input_dev *input = wacom->input; - int slot_id = data[0] - 2; /* data[0] is between 2 and 17 */ bool touch = data[1] & 0x80; + int slot = input_mt_get_slot_by_key(input, data[0]); + + if (slot < 0) + return; touch = touch && !wacom->shared->stylus_in_proximity; - input_mt_slot(input, slot_id); + input_mt_slot(input, slot); input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); if (touch) { @@ -1162,7 +1140,6 @@ static int wacom_bpt3_touch(struct wacom_wac *wacom) wacom_bpt3_button_msg(wacom, data + offset); } - input_mt_report_pointer_emulation(input, true); input_sync(input); @@ -1639,17 +1616,11 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev, } else if (features->device_type == BTN_TOOL_FINGER) { __clear_bit(ABS_MISC, input_dev->absbit); - __set_bit(BTN_TOOL_FINGER, input_dev->keybit); - __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit); - __set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit); - __set_bit(BTN_TOOL_QUADTAP, input_dev->keybit); - - input_mt_init_slots(input_dev, features->touch_max, 0); - input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, features->x_max, 0, 0); input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR, 0, features->y_max, 0, 0); + input_mt_init_slots(input_dev, features->touch_max, INPUT_MT_POINTER); } break; @@ -1680,21 +1651,14 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev, case MTSCREEN: case MTTPC: - if (features->device_type == BTN_TOOL_FINGER) { - wacom_wac->slots = kmalloc(features->touch_max * - sizeof(int), - GFP_KERNEL); - if (!wacom_wac->slots) - return -ENOMEM; - - for (i = 0; i < features->touch_max; i++) - wacom_wac->slots[i] = -1; - } - /* fall through */ - case TABLETPC2FG: if (features->device_type == BTN_TOOL_FINGER) { - input_mt_init_slots(input_dev, features->touch_max, 0); + unsigned int flags = INPUT_MT_DIRECT; + + if (wacom_wac->features.type == TABLETPC2FG) + flags = 0; + + input_mt_init_slots(input_dev, features->touch_max, flags); } /* fall through */ @@ -1737,28 +1701,26 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev, __set_bit(INPUT_PROP_POINTER, input_dev->propbit); if (features->device_type == BTN_TOOL_FINGER) { + unsigned int flags = INPUT_MT_POINTER; + __set_bit(BTN_LEFT, input_dev->keybit); __set_bit(BTN_FORWARD, input_dev->keybit); __set_bit(BTN_BACK, input_dev->keybit); __set_bit(BTN_RIGHT, input_dev->keybit); - __set_bit(BTN_TOOL_FINGER, input_dev->keybit); - __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit); - input_mt_init_slots(input_dev, features->touch_max, 0); - if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) { - __set_bit(BTN_TOOL_TRIPLETAP, - input_dev->keybit); - __set_bit(BTN_TOOL_QUADTAP, - input_dev->keybit); - input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, features->x_max, 0, 0); input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR, 0, features->y_max, 0, 0); + } else { + __set_bit(BTN_TOOL_FINGER, input_dev->keybit); + __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit); + flags = 0; } + input_mt_init_slots(input_dev, features->touch_max, flags); } else if (features->device_type == BTN_TOOL_PEN) { __set_bit(BTN_TOOL_RUBBER, input_dev->keybit); __set_bit(BTN_TOOL_PEN, input_dev->keybit); diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h index 9396d7769f86..06410fe6425f 100644 --- a/drivers/input/tablet/wacom_wac.h +++ b/drivers/input/tablet/wacom_wac.h @@ -135,7 +135,6 @@ struct wacom_wac { int pid; int battery_capacity; int num_contacts_left; - int *slots; }; #endif From 80524f083e2c3e70057f5bb476db92baa14cb22b Mon Sep 17 00:00:00 2001 From: Kamal Mostafa Date: Tue, 20 Nov 2012 23:04:35 -0800 Subject: [PATCH 12/55] Input: increase struct ps2dev cmdbuf[] to 8 bytes Cypress PS/2 Trackpad (drivers/input/mouse/cypress_ps2.c) needs this larger cmdbuf[] to handle 8-byte packet responses. Signed-off-by: Kamal Mostafa Signed-off-by: Dmitry Torokhov --- include/linux/libps2.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/libps2.h b/include/linux/libps2.h index 79603a6c356f..4ad06e824f76 100644 --- a/include/linux/libps2.h +++ b/include/linux/libps2.h @@ -36,7 +36,7 @@ struct ps2dev { wait_queue_head_t wait; unsigned long flags; - unsigned char cmdbuf[6]; + unsigned char cmdbuf[8]; unsigned char cmdcnt; unsigned char nak; }; From 0799a924bc93ba46a23e8e7e6b1431ab585fd2ea Mon Sep 17 00:00:00 2001 From: Dudley Du Date: Sat, 5 Jan 2013 00:14:22 -0800 Subject: [PATCH 13/55] Input: add support for Cypress PS/2 Trackpads This driver, submitted on behalf of Cypress Semiconductor Corporation and additional contributors, provides support for the Cypress PS/2 Trackpad. Original code contributed by Dudley Du (Cypress Semiconductor Corporation), modified by Kamal Mostafa and Kyle Fazzari. BugLink: http://launchpad.net/bugs/978807 Signed-off-by: Dudley Du Signed-off-by: Kamal Mostafa Signed-off-by: Kyle Fazzari Signed-off-by: Mario Limonciello Signed-off-by: Tim Gardner Acked-by: Herton Krzesinski Reviewed-by: Henrik Rydberg Reviewed-by: Dudley Du Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/Kconfig | 10 + drivers/input/mouse/Makefile | 1 + drivers/input/mouse/cypress_ps2.c | 725 +++++++++++++++++++++++++++++ drivers/input/mouse/cypress_ps2.h | 191 ++++++++ drivers/input/mouse/psmouse-base.c | 32 ++ drivers/input/mouse/psmouse.h | 1 + 6 files changed, 960 insertions(+) create mode 100644 drivers/input/mouse/cypress_ps2.c create mode 100644 drivers/input/mouse/cypress_ps2.h diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index cd6268cf7cd5..88954ddae6d9 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -68,6 +68,16 @@ config MOUSE_PS2_SYNAPTICS If unsure, say Y. +config MOUSE_PS2_CYPRESS + bool "Cypress PS/2 mouse protocol extension" if EXPERT + default y + depends on MOUSE_PS2 + help + Say Y here if you have a Cypress PS/2 Trackpad connected to + your system. + + If unsure, say Y. + config MOUSE_PS2_LIFEBOOK bool "Fujitsu Lifebook PS/2 mouse protocol extension" if EXPERT default y diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile index 46ba7556fd4f..323e352189ba 100644 --- a/drivers/input/mouse/Makefile +++ b/drivers/input/mouse/Makefile @@ -32,3 +32,4 @@ psmouse-$(CONFIG_MOUSE_PS2_LIFEBOOK) += lifebook.o psmouse-$(CONFIG_MOUSE_PS2_SENTELIC) += sentelic.o psmouse-$(CONFIG_MOUSE_PS2_TRACKPOINT) += trackpoint.o psmouse-$(CONFIG_MOUSE_PS2_TOUCHKIT) += touchkit_ps2.o +psmouse-$(CONFIG_MOUSE_PS2_CYPRESS) += cypress_ps2.o diff --git a/drivers/input/mouse/cypress_ps2.c b/drivers/input/mouse/cypress_ps2.c new file mode 100644 index 000000000000..1673dc6c8092 --- /dev/null +++ b/drivers/input/mouse/cypress_ps2.c @@ -0,0 +1,725 @@ +/* + * Cypress Trackpad PS/2 mouse driver + * + * Copyright (c) 2012 Cypress Semiconductor Corporation. + * + * Author: + * Dudley Du + * + * Additional contributors include: + * Kamal Mostafa + * Kyle Fazzari + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cypress_ps2.h" + +#undef CYTP_DEBUG_VERBOSE /* define this and DEBUG for more verbose dump */ + +static void cypress_set_packet_size(struct psmouse *psmouse, unsigned int n) +{ + struct cytp_data *cytp = psmouse->private; + cytp->pkt_size = n; +} + +static const unsigned char cytp_rate[] = {10, 20, 40, 60, 100, 200}; +static const unsigned char cytp_resolution[] = {0x00, 0x01, 0x02, 0x03}; + +static int cypress_ps2_sendbyte(struct psmouse *psmouse, int value) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + + if (ps2_sendbyte(ps2dev, value & 0xff, CYTP_CMD_TIMEOUT) < 0) { + psmouse_dbg(psmouse, + "sending command 0x%02x failed, resp 0x%02x\n", + value & 0xff, ps2dev->nak); + if (ps2dev->nak == CYTP_PS2_RETRY) + return CYTP_PS2_RETRY; + else + return CYTP_PS2_ERROR; + } + +#ifdef CYTP_DEBUG_VERBOSE + psmouse_dbg(psmouse, "sending command 0x%02x succeeded, resp 0xfa\n", + value & 0xff); +#endif + + return 0; +} + +static int cypress_ps2_ext_cmd(struct psmouse *psmouse, unsigned short cmd, + unsigned char data) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + int tries = CYTP_PS2_CMD_TRIES; + int rc; + + ps2_begin_command(ps2dev); + + do { + /* + * Send extension command byte (0xE8 or 0xF3). + * If sending the command fails, send recovery command + * to make the device return to the ready state. + */ + rc = cypress_ps2_sendbyte(psmouse, cmd & 0xff); + if (rc == CYTP_PS2_RETRY) { + rc = cypress_ps2_sendbyte(psmouse, 0x00); + if (rc == CYTP_PS2_RETRY) + rc = cypress_ps2_sendbyte(psmouse, 0x0a); + } + if (rc == CYTP_PS2_ERROR) + continue; + + rc = cypress_ps2_sendbyte(psmouse, data); + if (rc == CYTP_PS2_RETRY) + rc = cypress_ps2_sendbyte(psmouse, data); + if (rc == CYTP_PS2_ERROR) + continue; + else + break; + } while (--tries > 0); + + ps2_end_command(ps2dev); + + return rc; +} + +static int cypress_ps2_read_cmd_status(struct psmouse *psmouse, + unsigned char cmd, + unsigned char *param) +{ + int rc; + struct ps2dev *ps2dev = &psmouse->ps2dev; + enum psmouse_state old_state; + int pktsize; + + ps2_begin_command(&psmouse->ps2dev); + + old_state = psmouse->state; + psmouse->state = PSMOUSE_CMD_MODE; + psmouse->pktcnt = 0; + + pktsize = (cmd == CYTP_CMD_READ_TP_METRICS) ? 8 : 3; + memset(param, 0, pktsize); + + rc = cypress_ps2_sendbyte(psmouse, 0xe9); + if (rc < 0) + goto out; + + wait_event_timeout(ps2dev->wait, + (psmouse->pktcnt >= pktsize), + msecs_to_jiffies(CYTP_CMD_TIMEOUT)); + + memcpy(param, psmouse->packet, pktsize); + + psmouse_dbg(psmouse, "Command 0x%02x response data (0x): %*ph\n", + cmd, pktsize, param); + +out: + psmouse->state = old_state; + psmouse->pktcnt = 0; + + ps2_end_command(&psmouse->ps2dev); + + return rc; +} + +static bool cypress_verify_cmd_state(struct psmouse *psmouse, + unsigned char cmd, unsigned char *param) +{ + bool rate_match = false; + bool resolution_match = false; + int i; + + /* callers will do further checking. */ + if (cmd == CYTP_CMD_READ_CYPRESS_ID || + cmd == CYTP_CMD_STANDARD_MODE || + cmd == CYTP_CMD_READ_TP_METRICS) + return true; + + if ((~param[0] & DFLT_RESP_BITS_VALID) == DFLT_RESP_BITS_VALID && + (param[0] & DFLT_RESP_BIT_MODE) == DFLT_RESP_STREAM_MODE) { + for (i = 0; i < sizeof(cytp_resolution); i++) + if (cytp_resolution[i] == param[1]) + resolution_match = true; + + for (i = 0; i < sizeof(cytp_rate); i++) + if (cytp_rate[i] == param[2]) + rate_match = true; + + if (resolution_match && rate_match) + return true; + } + + psmouse_dbg(psmouse, "verify cmd state failed.\n"); + return false; +} + +static int cypress_send_ext_cmd(struct psmouse *psmouse, unsigned char cmd, + unsigned char *param) +{ + int tries = CYTP_PS2_CMD_TRIES; + int rc; + + psmouse_dbg(psmouse, "send extension cmd 0x%02x, [%d %d %d %d]\n", + cmd, DECODE_CMD_AA(cmd), DECODE_CMD_BB(cmd), + DECODE_CMD_CC(cmd), DECODE_CMD_DD(cmd)); + + do { + cypress_ps2_ext_cmd(psmouse, + PSMOUSE_CMD_SETRES, DECODE_CMD_DD(cmd)); + cypress_ps2_ext_cmd(psmouse, + PSMOUSE_CMD_SETRES, DECODE_CMD_CC(cmd)); + cypress_ps2_ext_cmd(psmouse, + PSMOUSE_CMD_SETRES, DECODE_CMD_BB(cmd)); + cypress_ps2_ext_cmd(psmouse, + PSMOUSE_CMD_SETRES, DECODE_CMD_AA(cmd)); + + rc = cypress_ps2_read_cmd_status(psmouse, cmd, param); + if (rc) + continue; + + if (cypress_verify_cmd_state(psmouse, cmd, param)) + return 0; + + } while (--tries > 0); + + return -EIO; +} + +int cypress_detect(struct psmouse *psmouse, bool set_properties) +{ + unsigned char param[3]; + + if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_CYPRESS_ID, param)) + return -ENODEV; + + /* Check for Cypress Trackpad signature bytes: 0x33 0xCC */ + if (param[0] != 0x33 || param[1] != 0xCC) + return -ENODEV; + + if (set_properties) { + psmouse->vendor = "Cypress"; + psmouse->name = "Trackpad"; + } + + return 0; +} + +static int cypress_read_fw_version(struct psmouse *psmouse) +{ + struct cytp_data *cytp = psmouse->private; + unsigned char param[3]; + + if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_CYPRESS_ID, param)) + return -ENODEV; + + /* Check for Cypress Trackpad signature bytes: 0x33 0xCC */ + if (param[0] != 0x33 || param[1] != 0xCC) + return -ENODEV; + + cytp->fw_version = param[2] & FW_VERSION_MASX; + cytp->tp_metrics_supported = (param[2] & TP_METRICS_MASK) ? 1 : 0; + + psmouse_dbg(psmouse, "cytp->fw_version = %d\n", cytp->fw_version); + psmouse_dbg(psmouse, "cytp->tp_metrics_supported = %d\n", + cytp->tp_metrics_supported); + + return 0; +} + +static int cypress_read_tp_metrics(struct psmouse *psmouse) +{ + struct cytp_data *cytp = psmouse->private; + unsigned char param[8]; + + /* set default values for tp metrics. */ + cytp->tp_width = CYTP_DEFAULT_WIDTH; + cytp->tp_high = CYTP_DEFAULT_HIGH; + cytp->tp_max_abs_x = CYTP_ABS_MAX_X; + cytp->tp_max_abs_y = CYTP_ABS_MAX_Y; + cytp->tp_min_pressure = CYTP_MIN_PRESSURE; + cytp->tp_max_pressure = CYTP_MAX_PRESSURE; + cytp->tp_res_x = cytp->tp_max_abs_x / cytp->tp_width; + cytp->tp_res_y = cytp->tp_max_abs_y / cytp->tp_high; + + memset(param, 0, sizeof(param)); + if (cypress_send_ext_cmd(psmouse, CYTP_CMD_READ_TP_METRICS, param) == 0) { + /* Update trackpad parameters. */ + cytp->tp_max_abs_x = (param[1] << 8) | param[0]; + cytp->tp_max_abs_y = (param[3] << 8) | param[2]; + cytp->tp_min_pressure = param[4]; + cytp->tp_max_pressure = param[5]; + } + + if (!cytp->tp_max_pressure || + cytp->tp_max_pressure < cytp->tp_min_pressure || + !cytp->tp_width || !cytp->tp_high || + !cytp->tp_max_abs_x || + cytp->tp_max_abs_x < cytp->tp_width || + !cytp->tp_max_abs_y || + cytp->tp_max_abs_y < cytp->tp_high) + return -EINVAL; + + cytp->tp_res_x = cytp->tp_max_abs_x / cytp->tp_width; + cytp->tp_res_y = cytp->tp_max_abs_y / cytp->tp_high; + +#ifdef CYTP_DEBUG_VERBOSE + psmouse_dbg(psmouse, "Dump trackpad hardware configuration as below:\n"); + psmouse_dbg(psmouse, "cytp->tp_width = %d\n", cytp->tp_width); + psmouse_dbg(psmouse, "cytp->tp_high = %d\n", cytp->tp_high); + psmouse_dbg(psmouse, "cytp->tp_max_abs_x = %d\n", cytp->tp_max_abs_x); + psmouse_dbg(psmouse, "cytp->tp_max_abs_y = %d\n", cytp->tp_max_abs_y); + psmouse_dbg(psmouse, "cytp->tp_min_pressure = %d\n", cytp->tp_min_pressure); + psmouse_dbg(psmouse, "cytp->tp_max_pressure = %d\n", cytp->tp_max_pressure); + psmouse_dbg(psmouse, "cytp->tp_res_x = %d\n", cytp->tp_res_x); + psmouse_dbg(psmouse, "cytp->tp_res_y = %d\n", cytp->tp_res_y); + + psmouse_dbg(psmouse, "tp_type_APA = %d\n", + (param[6] & TP_METRICS_BIT_APA) ? 1 : 0); + psmouse_dbg(psmouse, "tp_type_MTG = %d\n", + (param[6] & TP_METRICS_BIT_MTG) ? 1 : 0); + psmouse_dbg(psmouse, "tp_palm = %d\n", + (param[6] & TP_METRICS_BIT_PALM) ? 1 : 0); + psmouse_dbg(psmouse, "tp_stubborn = %d\n", + (param[6] & TP_METRICS_BIT_STUBBORN) ? 1 : 0); + psmouse_dbg(psmouse, "tp_1f_jitter = %d\n", + (param[6] & TP_METRICS_BIT_1F_JITTER) >> 2); + psmouse_dbg(psmouse, "tp_2f_jitter = %d\n", + (param[6] & TP_METRICS_BIT_2F_JITTER) >> 4); + psmouse_dbg(psmouse, "tp_1f_spike = %d\n", + param[7] & TP_METRICS_BIT_1F_SPIKE); + psmouse_dbg(psmouse, "tp_2f_spike = %d\n", + (param[7] & TP_METRICS_BIT_2F_SPIKE) >> 2); + psmouse_dbg(psmouse, "tp_abs_packet_format_set = %d\n", + (param[7] & TP_METRICS_BIT_ABS_PKT_FORMAT_SET) >> 4); +#endif + + return 0; +} + +static int cypress_query_hardware(struct psmouse *psmouse) +{ + struct cytp_data *cytp = psmouse->private; + int ret; + + ret = cypress_read_fw_version(psmouse); + if (ret) + return ret; + + if (cytp->tp_metrics_supported) { + ret = cypress_read_tp_metrics(psmouse); + if (ret) + return ret; + } + + return 0; +} + +static int cypress_set_absolute_mode(struct psmouse *psmouse) +{ + struct cytp_data *cytp = psmouse->private; + unsigned char param[3]; + + if (cypress_send_ext_cmd(psmouse, CYTP_CMD_ABS_WITH_PRESSURE_MODE, param) < 0) + return -1; + + cytp->mode = (cytp->mode & ~CYTP_BIT_ABS_REL_MASK) + | CYTP_BIT_ABS_PRESSURE; + cypress_set_packet_size(psmouse, 5); + + return 0; +} + +/* + * Reset trackpad device. + * This is also the default mode when trackpad powered on. + */ +static void cypress_reset(struct psmouse *psmouse) +{ + struct cytp_data *cytp = psmouse->private; + + cytp->mode = 0; + + psmouse_reset(psmouse); +} + +static int cypress_set_input_params(struct input_dev *input, + struct cytp_data *cytp) +{ + int ret; + + if (!cytp->tp_res_x || !cytp->tp_res_y) + return -EINVAL; + + __set_bit(EV_ABS, input->evbit); + input_set_abs_params(input, ABS_X, 0, cytp->tp_max_abs_x, 0, 0); + input_set_abs_params(input, ABS_Y, 0, cytp->tp_max_abs_y, 0, 0); + input_set_abs_params(input, ABS_PRESSURE, + cytp->tp_min_pressure, cytp->tp_max_pressure, 0, 0); + input_set_abs_params(input, ABS_TOOL_WIDTH, 0, 255, 0, 0); + + /* finger position */ + input_set_abs_params(input, ABS_MT_POSITION_X, 0, cytp->tp_max_abs_x, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, cytp->tp_max_abs_y, 0, 0); + input_set_abs_params(input, ABS_MT_PRESSURE, 0, 255, 0, 0); + + ret = input_mt_init_slots(input, CYTP_MAX_MT_SLOTS, + INPUT_MT_DROP_UNUSED|INPUT_MT_TRACK); + if (ret < 0) + return ret; + + __set_bit(INPUT_PROP_SEMI_MT, input->propbit); + + input_abs_set_res(input, ABS_X, cytp->tp_res_x); + input_abs_set_res(input, ABS_Y, cytp->tp_res_y); + + input_abs_set_res(input, ABS_MT_POSITION_X, cytp->tp_res_x); + input_abs_set_res(input, ABS_MT_POSITION_Y, cytp->tp_res_y); + + __set_bit(BTN_TOUCH, input->keybit); + __set_bit(BTN_TOOL_FINGER, input->keybit); + __set_bit(BTN_TOOL_DOUBLETAP, input->keybit); + __set_bit(BTN_TOOL_TRIPLETAP, input->keybit); + __set_bit(BTN_TOOL_QUADTAP, input->keybit); + __set_bit(BTN_TOOL_QUINTTAP, input->keybit); + + __clear_bit(EV_REL, input->evbit); + __clear_bit(REL_X, input->relbit); + __clear_bit(REL_Y, input->relbit); + + __set_bit(INPUT_PROP_BUTTONPAD, input->propbit); + __set_bit(EV_KEY, input->evbit); + __set_bit(BTN_LEFT, input->keybit); + __set_bit(BTN_RIGHT, input->keybit); + __set_bit(BTN_MIDDLE, input->keybit); + + input_set_drvdata(input, cytp); + + return 0; +} + +static int cypress_get_finger_count(unsigned char header_byte) +{ + unsigned char bits6_7; + int finger_count; + + bits6_7 = header_byte >> 6; + finger_count = bits6_7 & 0x03; + + if (finger_count == 1) + return 1; + + if (header_byte & ABS_HSCROLL_BIT) { + /* HSCROLL gets added on to 0 finger count. */ + switch (finger_count) { + case 0: return 4; + case 2: return 5; + default: + /* Invalid contact (e.g. palm). Ignore it. */ + return -1; + } + } + + return finger_count; +} + + +static int cypress_parse_packet(struct psmouse *psmouse, + struct cytp_data *cytp, struct cytp_report_data *report_data) +{ + unsigned char *packet = psmouse->packet; + unsigned char header_byte = packet[0]; + int contact_cnt; + + memset(report_data, 0, sizeof(struct cytp_report_data)); + + contact_cnt = cypress_get_finger_count(header_byte); + + if (contact_cnt < 0) /* e.g. palm detect */ + return -EINVAL; + + report_data->contact_cnt = contact_cnt; + + report_data->tap = (header_byte & ABS_MULTIFINGER_TAP) ? 1 : 0; + + if (report_data->contact_cnt == 1) { + report_data->contacts[0].x = + ((packet[1] & 0x70) << 4) | packet[2]; + report_data->contacts[0].y = + ((packet[1] & 0x07) << 8) | packet[3]; + if (cytp->mode & CYTP_BIT_ABS_PRESSURE) + report_data->contacts[0].z = packet[4]; + + } else if (report_data->contact_cnt >= 2) { + report_data->contacts[0].x = + ((packet[1] & 0x70) << 4) | packet[2]; + report_data->contacts[0].y = + ((packet[1] & 0x07) << 8) | packet[3]; + if (cytp->mode & CYTP_BIT_ABS_PRESSURE) + report_data->contacts[0].z = packet[4]; + + report_data->contacts[1].x = + ((packet[5] & 0xf0) << 4) | packet[6]; + report_data->contacts[1].y = + ((packet[5] & 0x0f) << 8) | packet[7]; + if (cytp->mode & CYTP_BIT_ABS_PRESSURE) + report_data->contacts[1].z = report_data->contacts[0].z; + } + + report_data->left = (header_byte & BTN_LEFT_BIT) ? 1 : 0; + report_data->right = (header_byte & BTN_RIGHT_BIT) ? 1 : 0; + + /* + * This is only true if one of the mouse buttons were tapped. Make + * sure it doesn't turn into a click. The regular tap-to-click + * functionality will handle that on its own. If we don't do this, + * disabling tap-to-click won't affect the mouse button zones. + */ + if (report_data->tap) + report_data->left = 0; + +#ifdef CYTP_DEBUG_VERBOSE + { + int i; + int n = report_data->contact_cnt; + psmouse_dbg(psmouse, "Dump parsed report data as below:\n"); + psmouse_dbg(psmouse, "contact_cnt = %d\n", + report_data->contact_cnt); + if (n > CYTP_MAX_MT_SLOTS) + n = CYTP_MAX_MT_SLOTS; + for (i = 0; i < n; i++) + psmouse_dbg(psmouse, "contacts[%d] = {%d, %d, %d}\n", i, + report_data->contacts[i].x, + report_data->contacts[i].y, + report_data->contacts[i].z); + psmouse_dbg(psmouse, "left = %d\n", report_data->left); + psmouse_dbg(psmouse, "right = %d\n", report_data->right); + psmouse_dbg(psmouse, "middle = %d\n", report_data->middle); + } +#endif + + return 0; +} + +static void cypress_process_packet(struct psmouse *psmouse, bool zero_pkt) +{ + int i; + struct input_dev *input = psmouse->dev; + struct cytp_data *cytp = psmouse->private; + struct cytp_report_data report_data; + struct cytp_contact *contact; + struct input_mt_pos pos[CYTP_MAX_MT_SLOTS]; + int slots[CYTP_MAX_MT_SLOTS]; + int n; + + if (cypress_parse_packet(psmouse, cytp, &report_data)) + return; + + n = report_data.contact_cnt; + + if (n > CYTP_MAX_MT_SLOTS) + n = CYTP_MAX_MT_SLOTS; + + for (i = 0; i < n; i++) { + contact = &report_data.contacts[i]; + pos[i].x = contact->x; + pos[i].y = contact->y; + } + + input_mt_assign_slots(input, slots, pos, n); + + for (i = 0; i < n; i++) { + contact = &report_data.contacts[i]; + input_mt_slot(input, slots[i]); + input_mt_report_slot_state(input, MT_TOOL_FINGER, true); + input_report_abs(input, ABS_MT_POSITION_X, contact->x); + input_report_abs(input, ABS_MT_POSITION_Y, contact->y); + input_report_abs(input, ABS_MT_PRESSURE, contact->z); + } + + input_mt_sync_frame(input); + + input_mt_report_finger_count(input, report_data.contact_cnt); + + input_report_key(input, BTN_LEFT, report_data.left); + input_report_key(input, BTN_RIGHT, report_data.right); + input_report_key(input, BTN_MIDDLE, report_data.middle); + + input_sync(input); +} + +static psmouse_ret_t cypress_validate_byte(struct psmouse *psmouse) +{ + int contact_cnt; + int index = psmouse->pktcnt - 1; + unsigned char *packet = psmouse->packet; + struct cytp_data *cytp = psmouse->private; + + if (index < 0 || index > cytp->pkt_size) + return PSMOUSE_BAD_DATA; + + if (index == 0 && (packet[0] & 0xfc) == 0) { + /* call packet process for reporting finger leave. */ + cypress_process_packet(psmouse, 1); + return PSMOUSE_FULL_PACKET; + } + + /* + * Perform validation (and adjust packet size) based only on the + * first byte; allow all further bytes through. + */ + if (index != 0) + return PSMOUSE_GOOD_DATA; + + /* + * If absolute/relative mode bit has not been set yet, just pass + * the byte through. + */ + if ((cytp->mode & CYTP_BIT_ABS_REL_MASK) == 0) + return PSMOUSE_GOOD_DATA; + + if ((packet[0] & 0x08) == 0x08) + return PSMOUSE_BAD_DATA; + + contact_cnt = cypress_get_finger_count(packet[0]); + + if (contact_cnt < 0) + return PSMOUSE_BAD_DATA; + + if (cytp->mode & CYTP_BIT_ABS_NO_PRESSURE) + cypress_set_packet_size(psmouse, contact_cnt == 2 ? 7 : 4); + else + cypress_set_packet_size(psmouse, contact_cnt == 2 ? 8 : 5); + + return PSMOUSE_GOOD_DATA; +} + +static psmouse_ret_t cypress_protocol_handler(struct psmouse *psmouse) +{ + struct cytp_data *cytp = psmouse->private; + + if (psmouse->pktcnt >= cytp->pkt_size) { + cypress_process_packet(psmouse, 0); + return PSMOUSE_FULL_PACKET; + } + + return cypress_validate_byte(psmouse); +} + +static void cypress_set_rate(struct psmouse *psmouse, unsigned int rate) +{ + struct cytp_data *cytp = psmouse->private; + + if (rate >= 80) { + psmouse->rate = 80; + cytp->mode |= CYTP_BIT_HIGH_RATE; + } else { + psmouse->rate = 40; + cytp->mode &= ~CYTP_BIT_HIGH_RATE; + } + + ps2_command(&psmouse->ps2dev, (unsigned char *)&psmouse->rate, + PSMOUSE_CMD_SETRATE); +} + +static void cypress_disconnect(struct psmouse *psmouse) +{ + cypress_reset(psmouse); + kfree(psmouse->private); + psmouse->private = NULL; +} + +static int cypress_reconnect(struct psmouse *psmouse) +{ + int tries = CYTP_PS2_CMD_TRIES; + int rc; + + do { + cypress_reset(psmouse); + rc = cypress_detect(psmouse, false); + } while (rc && (--tries > 0)); + + if (rc) { + psmouse_err(psmouse, "Reconnect: unable to detect trackpad.\n"); + return -1; + } + + if (cypress_set_absolute_mode(psmouse)) { + psmouse_err(psmouse, "Reconnect: Unable to initialize Cypress absolute mode.\n"); + return -1; + } + + return 0; +} + +int cypress_init(struct psmouse *psmouse) +{ + struct cytp_data *cytp; + + cytp = (struct cytp_data *)kzalloc(sizeof(struct cytp_data), GFP_KERNEL); + psmouse->private = (void *)cytp; + if (cytp == NULL) + return -ENOMEM; + + cypress_reset(psmouse); + + psmouse->pktsize = 8; + + if (cypress_query_hardware(psmouse)) { + psmouse_err(psmouse, "Unable to query Trackpad hardware.\n"); + goto err_exit; + } + + if (cypress_set_absolute_mode(psmouse)) { + psmouse_err(psmouse, "init: Unable to initialize Cypress absolute mode.\n"); + goto err_exit; + } + + if (cypress_set_input_params(psmouse->dev, cytp) < 0) { + psmouse_err(psmouse, "init: Unable to set input params.\n"); + goto err_exit; + } + + psmouse->model = 1; + psmouse->protocol_handler = cypress_protocol_handler; + psmouse->set_rate = cypress_set_rate; + psmouse->disconnect = cypress_disconnect; + psmouse->reconnect = cypress_reconnect; + psmouse->cleanup = cypress_reset; + psmouse->resync_time = 0; + + return 0; + +err_exit: + /* + * Reset Cypress Trackpad as a standard mouse. Then + * let psmouse driver commmunicating with it as default PS2 mouse. + */ + cypress_reset(psmouse); + + psmouse->private = NULL; + kfree(cytp); + + return -1; +} + +bool cypress_supported(void) +{ + return true; +} diff --git a/drivers/input/mouse/cypress_ps2.h b/drivers/input/mouse/cypress_ps2.h new file mode 100644 index 000000000000..4720f21d2d70 --- /dev/null +++ b/drivers/input/mouse/cypress_ps2.h @@ -0,0 +1,191 @@ +#ifndef _CYPRESS_PS2_H +#define _CYPRESS_PS2_H + +#include "psmouse.h" + +#define CMD_BITS_MASK 0x03 +#define COMPOSIT(x, s) (((x) & CMD_BITS_MASK) << (s)) + +#define ENCODE_CMD(aa, bb, cc, dd) \ + (COMPOSIT((aa), 6) | COMPOSIT((bb), 4) | COMPOSIT((cc), 2) | COMPOSIT((dd), 0)) +#define CYTP_CMD_ABS_NO_PRESSURE_MODE ENCODE_CMD(0, 1, 0, 0) +#define CYTP_CMD_ABS_WITH_PRESSURE_MODE ENCODE_CMD(0, 1, 0, 1) +#define CYTP_CMD_SMBUS_MODE ENCODE_CMD(0, 1, 1, 0) +#define CYTP_CMD_STANDARD_MODE ENCODE_CMD(0, 2, 0, 0) /* not implemented yet. */ +#define CYTP_CMD_CYPRESS_REL_MODE ENCODE_CMD(1, 1, 1, 1) /* not implemented yet. */ +#define CYTP_CMD_READ_CYPRESS_ID ENCODE_CMD(0, 0, 0, 0) +#define CYTP_CMD_READ_TP_METRICS ENCODE_CMD(0, 0, 0, 1) +#define CYTP_CMD_SET_HSCROLL_WIDTH(w) ENCODE_CMD(1, 1, 0, (w)) +#define CYTP_CMD_SET_HSCROLL_MASK ENCODE_CMD(1, 1, 0, 0) +#define CYTP_CMD_SET_VSCROLL_WIDTH(w) ENCODE_CMD(1, 2, 0, (w)) +#define CYTP_CMD_SET_VSCROLL_MASK ENCODE_CMD(1, 2, 0, 0) +#define CYTP_CMD_SET_PALM_GEOMETRY(e) ENCODE_CMD(1, 2, 1, (e)) +#define CYTP_CMD_PALM_GEMMETRY_MASK ENCODE_CMD(1, 2, 1, 0) +#define CYTP_CMD_SET_PALM_SENSITIVITY(s) ENCODE_CMD(1, 2, 2, (s)) +#define CYTP_CMD_PALM_SENSITIVITY_MASK ENCODE_CMD(1, 2, 2, 0) +#define CYTP_CMD_SET_MOUSE_SENSITIVITY(s) ENCODE_CMD(1, 3, ((s) >> 2), (s)) +#define CYTP_CMD_MOUSE_SENSITIVITY_MASK ENCODE_CMD(1, 3, 0, 0) +#define CYTP_CMD_REQUEST_BASELINE_STATUS ENCODE_CMD(2, 0, 0, 1) +#define CYTP_CMD_REQUEST_RECALIBRATION ENCODE_CMD(2, 0, 0, 3) + +#define DECODE_CMD_AA(x) (((x) >> 6) & CMD_BITS_MASK) +#define DECODE_CMD_BB(x) (((x) >> 4) & CMD_BITS_MASK) +#define DECODE_CMD_CC(x) (((x) >> 2) & CMD_BITS_MASK) +#define DECODE_CMD_DD(x) ((x) & CMD_BITS_MASK) + +/* Cypress trackpad working mode. */ +#define CYTP_BIT_ABS_PRESSURE (1 << 3) +#define CYTP_BIT_ABS_NO_PRESSURE (1 << 2) +#define CYTP_BIT_CYPRESS_REL (1 << 1) +#define CYTP_BIT_STANDARD_REL (1 << 0) +#define CYTP_BIT_REL_MASK (CYTP_BIT_CYPRESS_REL | CYTP_BIT_STANDARD_REL) +#define CYTP_BIT_ABS_MASK (CYTP_BIT_ABS_PRESSURE | CYTP_BIT_ABS_NO_PRESSURE) +#define CYTP_BIT_ABS_REL_MASK (CYTP_BIT_ABS_MASK | CYTP_BIT_REL_MASK) + +#define CYTP_BIT_HIGH_RATE (1 << 4) +/* + * report mode bit is set, firmware working in Remote Mode. + * report mode bit is cleared, firmware working in Stream Mode. + */ +#define CYTP_BIT_REPORT_MODE (1 << 5) + +/* scrolling width values for set HSCROLL and VSCROLL width command. */ +#define SCROLL_WIDTH_NARROW 1 +#define SCROLL_WIDTH_NORMAL 2 +#define SCROLL_WIDTH_WIDE 3 + +#define PALM_GEOMETRY_ENABLE 1 +#define PALM_GEOMETRY_DISABLE 0 + +#define TP_METRICS_MASK 0x80 +#define FW_VERSION_MASX 0x7f +#define FW_VER_HIGH_MASK 0x70 +#define FW_VER_LOW_MASK 0x0f + +/* Times to retry a ps2_command and millisecond delay between tries. */ +#define CYTP_PS2_CMD_TRIES 3 +#define CYTP_PS2_CMD_DELAY 500 + +/* time out for PS/2 command only in milliseconds. */ +#define CYTP_CMD_TIMEOUT 200 +#define CYTP_DATA_TIMEOUT 30 + +#define CYTP_EXT_CMD 0xe8 +#define CYTP_PS2_RETRY 0xfe +#define CYTP_PS2_ERROR 0xfc + +#define CYTP_RESP_RETRY 0x01 +#define CYTP_RESP_ERROR 0xfe + + +#define CYTP_105001_WIDTH 97 /* Dell XPS 13 */ +#define CYTP_105001_HIGH 59 +#define CYTP_DEFAULT_WIDTH (CYTP_105001_WIDTH) +#define CYTP_DEFAULT_HIGH (CYTP_105001_HIGH) + +#define CYTP_ABS_MAX_X 1600 +#define CYTP_ABS_MAX_Y 900 +#define CYTP_MAX_PRESSURE 255 +#define CYTP_MIN_PRESSURE 0 + +/* header byte bits of relative package. */ +#define BTN_LEFT_BIT 0x01 +#define BTN_RIGHT_BIT 0x02 +#define BTN_MIDDLE_BIT 0x04 +#define REL_X_SIGN_BIT 0x10 +#define REL_Y_SIGN_BIT 0x20 + +/* header byte bits of absolute package. */ +#define ABS_VSCROLL_BIT 0x10 +#define ABS_HSCROLL_BIT 0x20 +#define ABS_MULTIFINGER_TAP 0x04 +#define ABS_EDGE_MOTION_MASK 0x80 + +#define DFLT_RESP_BITS_VALID 0x88 /* SMBus bit should not be set. */ +#define DFLT_RESP_SMBUS_BIT 0x80 +#define DFLT_SMBUS_MODE 0x80 +#define DFLT_PS2_MODE 0x00 +#define DFLT_RESP_BIT_MODE 0x40 +#define DFLT_RESP_REMOTE_MODE 0x40 +#define DFLT_RESP_STREAM_MODE 0x00 +#define DFLT_RESP_BIT_REPORTING 0x20 +#define DFLT_RESP_BIT_SCALING 0x10 + +#define TP_METRICS_BIT_PALM 0x80 +#define TP_METRICS_BIT_STUBBORN 0x40 +#define TP_METRICS_BIT_2F_JITTER 0x30 +#define TP_METRICS_BIT_1F_JITTER 0x0c +#define TP_METRICS_BIT_APA 0x02 +#define TP_METRICS_BIT_MTG 0x01 +#define TP_METRICS_BIT_ABS_PKT_FORMAT_SET 0xf0 +#define TP_METRICS_BIT_2F_SPIKE 0x0c +#define TP_METRICS_BIT_1F_SPIKE 0x03 + +/* bits of first byte response of E9h-Status Request command. */ +#define RESP_BTN_RIGHT_BIT 0x01 +#define RESP_BTN_MIDDLE_BIT 0x02 +#define RESP_BTN_LEFT_BIT 0x04 +#define RESP_SCALING_BIT 0x10 +#define RESP_ENABLE_BIT 0x20 +#define RESP_REMOTE_BIT 0x40 +#define RESP_SMBUS_BIT 0x80 + +#define CYTP_MAX_MT_SLOTS 2 + +struct cytp_contact { + int x; + int y; + int z; /* also named as touch pressure. */ +}; + +/* The structure of Cypress Trackpad event data. */ +struct cytp_report_data { + int contact_cnt; + struct cytp_contact contacts[CYTP_MAX_MT_SLOTS]; + unsigned int left:1; + unsigned int right:1; + unsigned int middle:1; + unsigned int tap:1; /* multi-finger tap detected. */ +}; + +/* The structure of Cypress Trackpad device private data. */ +struct cytp_data { + int fw_version; + + int pkt_size; + int mode; + + int tp_min_pressure; + int tp_max_pressure; + int tp_width; /* X direction physical size in mm. */ + int tp_high; /* Y direction physical size in mm. */ + int tp_max_abs_x; /* Max X absolute units that can be reported. */ + int tp_max_abs_y; /* Max Y absolute units that can be reported. */ + + int tp_res_x; /* X resolution in units/mm. */ + int tp_res_y; /* Y resolution in units/mm. */ + + int tp_metrics_supported; +}; + + +#ifdef CONFIG_MOUSE_PS2_CYPRESS +int cypress_detect(struct psmouse *psmouse, bool set_properties); +int cypress_init(struct psmouse *psmouse); +bool cypress_supported(void); +#else +inline int cypress_detect(struct psmouse *psmouse, bool set_properties) +{ + return -ENOSYS; +} +inline int cypress_init(struct psmouse *psmouse) +{ + return -ENOSYS; +} +inline bool cypress_supported(void) +{ + return 0; +} +#endif /* CONFIG_MOUSE_PS2_CYPRESS */ + +#endif /* _CYPRESS_PS2_H */ diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 22fe2547e169..cff065f6261c 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -34,6 +34,7 @@ #include "touchkit_ps2.h" #include "elantech.h" #include "sentelic.h" +#include "cypress_ps2.h" #define DRIVER_DESC "PS/2 mouse driver" @@ -758,6 +759,28 @@ static int psmouse_extensions(struct psmouse *psmouse, synaptics_reset(psmouse); } +/* + * Try Cypress Trackpad. + * Must try it before Finger Sensing Pad because Finger Sensing Pad probe + * upsets some modules of Cypress Trackpads. + */ + if (max_proto > PSMOUSE_IMEX && + cypress_detect(psmouse, set_properties) == 0) { + if (cypress_supported()) { + if (cypress_init(psmouse) == 0) + return PSMOUSE_CYPRESS; + + /* + * Finger Sensing Pad probe upsets some modules of + * Cypress Trackpad, must avoid Finger Sensing Pad + * probe if Cypress Trackpad device detected. + */ + return PSMOUSE_PS2; + } + + max_proto = PSMOUSE_IMEX; + } + /* * Try ALPS TouchPad */ @@ -896,6 +919,15 @@ static const struct psmouse_protocol psmouse_protocols[] = { .alias = "thinkps", .detect = thinking_detect, }, +#ifdef CONFIG_MOUSE_PS2_CYPRESS + { + .type = PSMOUSE_CYPRESS, + .name = "CyPS/2", + .alias = "cypress", + .detect = cypress_detect, + .init = cypress_init, + }, +#endif { .type = PSMOUSE_GENPS, .name = "GenPS/2", diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index fe1df231ba4c..2f0b39d59a9b 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -95,6 +95,7 @@ enum psmouse_type { PSMOUSE_ELANTECH, PSMOUSE_FSP, PSMOUSE_SYNAPTICS_RELATIVE, + PSMOUSE_CYPRESS, PSMOUSE_AUTO /* This one should always be last */ }; From 154b7a489a5b1d808323b933b04864958c2f1056 Mon Sep 17 00:00:00 2001 From: Mathieu Poirier Date: Sun, 6 Jan 2013 23:23:33 -0800 Subject: [PATCH 14/55] Input: sysrq - allow specifying alternate reset sequence MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds keyreset functionality to the sysrq driver. It allows certain button/key combinations to be used in order to trigger emergency reboots. Redefining the '__weak platform_sysrq_reset_seq' variable is required to trigger the feature. Alternatively keys can be passed to the driver via a module parameter. This functionality comes from the keyreset driver submitted by Arve Hjønnevåg in the Android kernel. Signed-off-by: Mathieu Poirier Signed-off-by: Dmitry Torokhov --- drivers/tty/sysrq.c | 276 ++++++++++++++++++++++++++++++++------------ 1 file changed, 202 insertions(+), 74 deletions(-) diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c index 16ee6cee07da..77fcad4371ce 100644 --- a/drivers/tty/sysrq.c +++ b/drivers/tty/sysrq.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -576,8 +577,71 @@ struct sysrq_state { bool active; bool need_reinject; bool reinjecting; + + /* reset sequence handling */ + bool reset_canceled; + unsigned long reset_keybit[BITS_TO_LONGS(KEY_CNT)]; + int reset_seq_len; + int reset_seq_cnt; + int reset_seq_version; }; +#define SYSRQ_KEY_RESET_MAX 20 /* Should be plenty */ +static unsigned short sysrq_reset_seq[SYSRQ_KEY_RESET_MAX]; +static unsigned int sysrq_reset_seq_len; +static unsigned int sysrq_reset_seq_version = 1; + +static void sysrq_parse_reset_sequence(struct sysrq_state *state) +{ + int i; + unsigned short key; + + state->reset_seq_cnt = 0; + + for (i = 0; i < sysrq_reset_seq_len; i++) { + key = sysrq_reset_seq[i]; + + if (key == KEY_RESERVED || key > KEY_MAX) + break; + + __set_bit(key, state->reset_keybit); + state->reset_seq_len++; + + if (test_bit(key, state->key_down)) + state->reset_seq_cnt++; + } + + /* Disable reset until old keys are not released */ + state->reset_canceled = state->reset_seq_cnt != 0; + + state->reset_seq_version = sysrq_reset_seq_version; +} + +static bool sysrq_detect_reset_sequence(struct sysrq_state *state, + unsigned int code, int value) +{ + if (!test_bit(code, state->reset_keybit)) { + /* + * Pressing any key _not_ in reset sequence cancels + * the reset sequence. + */ + if (value && state->reset_seq_cnt) + state->reset_canceled = true; + } else if (value == 0) { + /* key release */ + if (--state->reset_seq_cnt == 0) + state->reset_canceled = false; + } else if (value == 1) { + /* key press, not autorepeat */ + if (++state->reset_seq_cnt == state->reset_seq_len && + !state->reset_canceled) { + return true; + } + } + + return false; +} + static void sysrq_reinject_alt_sysrq(struct work_struct *work) { struct sysrq_state *sysrq = @@ -604,11 +668,104 @@ static void sysrq_reinject_alt_sysrq(struct work_struct *work) } } +static bool sysrq_handle_keypress(struct sysrq_state *sysrq, + unsigned int code, int value) +{ + bool was_active = sysrq->active; + bool suppress; + + switch (code) { + + case KEY_LEFTALT: + case KEY_RIGHTALT: + if (!value) { + /* One of ALTs is being released */ + if (sysrq->active && code == sysrq->alt_use) + sysrq->active = false; + + sysrq->alt = KEY_RESERVED; + + } else if (value != 2) { + sysrq->alt = code; + sysrq->need_reinject = false; + } + break; + + case KEY_SYSRQ: + if (value == 1 && sysrq->alt != KEY_RESERVED) { + sysrq->active = true; + sysrq->alt_use = sysrq->alt; + /* + * If nothing else will be pressed we'll need + * to re-inject Alt-SysRq keysroke. + */ + sysrq->need_reinject = true; + } + + /* + * Pretend that sysrq was never pressed at all. This + * is needed to properly handle KGDB which will try + * to release all keys after exiting debugger. If we + * do not clear key bit it KGDB will end up sending + * release events for Alt and SysRq, potentially + * triggering print screen function. + */ + if (sysrq->active) + clear_bit(KEY_SYSRQ, sysrq->handle.dev->key); + + break; + + default: + if (sysrq->active && value && value != 2) { + sysrq->need_reinject = false; + __handle_sysrq(sysrq_xlate[code], true); + } + break; + } + + suppress = sysrq->active; + + if (!sysrq->active) { + + /* + * See if reset sequence has changed since the last time. + */ + if (sysrq->reset_seq_version != sysrq_reset_seq_version) + sysrq_parse_reset_sequence(sysrq); + + /* + * If we are not suppressing key presses keep track of + * keyboard state so we can release keys that have been + * pressed before entering SysRq mode. + */ + if (value) + set_bit(code, sysrq->key_down); + else + clear_bit(code, sysrq->key_down); + + if (was_active) + schedule_work(&sysrq->reinject_work); + + if (sysrq_detect_reset_sequence(sysrq, code, value)) { + /* Force emergency reboot */ + __handle_sysrq(sysrq_xlate[KEY_B], false); + } + + } else if (value == 0 && test_and_clear_bit(code, sysrq->key_down)) { + /* + * Pass on release events for keys that was pressed before + * entering SysRq mode. + */ + suppress = false; + } + + return suppress; +} + static bool sysrq_filter(struct input_handle *handle, unsigned int type, unsigned int code, int value) { struct sysrq_state *sysrq = handle->private; - bool was_active = sysrq->active; bool suppress; /* @@ -625,79 +782,7 @@ static bool sysrq_filter(struct input_handle *handle, break; case EV_KEY: - switch (code) { - - case KEY_LEFTALT: - case KEY_RIGHTALT: - if (!value) { - /* One of ALTs is being released */ - if (sysrq->active && code == sysrq->alt_use) - sysrq->active = false; - - sysrq->alt = KEY_RESERVED; - - } else if (value != 2) { - sysrq->alt = code; - sysrq->need_reinject = false; - } - break; - - case KEY_SYSRQ: - if (value == 1 && sysrq->alt != KEY_RESERVED) { - sysrq->active = true; - sysrq->alt_use = sysrq->alt; - /* - * If nothing else will be pressed we'll need - * to re-inject Alt-SysRq keysroke. - */ - sysrq->need_reinject = true; - } - - /* - * Pretend that sysrq was never pressed at all. This - * is needed to properly handle KGDB which will try - * to release all keys after exiting debugger. If we - * do not clear key bit it KGDB will end up sending - * release events for Alt and SysRq, potentially - * triggering print screen function. - */ - if (sysrq->active) - clear_bit(KEY_SYSRQ, handle->dev->key); - - break; - - default: - if (sysrq->active && value && value != 2) { - sysrq->need_reinject = false; - __handle_sysrq(sysrq_xlate[code], true); - } - break; - } - - suppress = sysrq->active; - - if (!sysrq->active) { - /* - * If we are not suppressing key presses keep track of - * keyboard state so we can release keys that have been - * pressed before entering SysRq mode. - */ - if (value) - set_bit(code, sysrq->key_down); - else - clear_bit(code, sysrq->key_down); - - if (was_active) - schedule_work(&sysrq->reinject_work); - - } else if (value == 0 && - test_and_clear_bit(code, sysrq->key_down)) { - /* - * Pass on release events for keys that was pressed before - * entering SysRq mode. - */ - suppress = false; - } + suppress = sysrq_handle_keypress(sysrq, code, value); break; default: @@ -785,7 +870,20 @@ static bool sysrq_handler_registered; static inline void sysrq_register_handler(void) { + extern unsigned short platform_sysrq_reset_seq[] __weak; + unsigned short key; int error; + int i; + + if (platform_sysrq_reset_seq) { + for (i = 0; i < ARRAY_SIZE(sysrq_reset_seq); i++) { + key = platform_sysrq_reset_seq[i]; + if (key == KEY_RESERVED || key > KEY_MAX) + break; + + sysrq_reset_seq[sysrq_reset_seq_len++] = key; + } + } error = input_register_handler(&sysrq_handler); if (error) @@ -802,6 +900,36 @@ static inline void sysrq_unregister_handler(void) } } +static int sysrq_reset_seq_param_set(const char *buffer, + const struct kernel_param *kp) +{ + unsigned long val; + int error; + + error = strict_strtoul(buffer, 0, &val); + if (error < 0) + return error; + + if (val > KEY_MAX) + return -EINVAL; + + *((unsigned short *)kp->arg) = val; + sysrq_reset_seq_version++; + + return 0; +} + +static struct kernel_param_ops param_ops_sysrq_reset_seq = { + .get = param_get_ushort, + .set = sysrq_reset_seq_param_set, +}; + +#define param_check_sysrq_reset_seq(name, p) \ + __param_check(name, p, unsigned short) + +module_param_array_named(reset_seq, sysrq_reset_seq, sysrq_reset_seq, + &sysrq_reset_seq_len, 0644); + #else static inline void sysrq_register_handler(void) From 1c407a1b24debecccc42e5a2ae167f058dbb6fba Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Sun, 6 Jan 2013 18:30:21 -0800 Subject: [PATCH 15/55] Input: tegra-kbc - fix build warning Fix the following build warning when building driver with CONFIG_PM_SLEEP not selected. tegra-kbc.c:360:13: warning: 'tegra_kbc_set_keypress_interrupt' defined but not used [-Wunused-function] Signed-off-by: Laxman Dewangan Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/tegra-kbc.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c index c76f96872d31..f1d3ba0dafb2 100644 --- a/drivers/input/keyboard/tegra-kbc.c +++ b/drivers/input/keyboard/tegra-kbc.c @@ -357,18 +357,6 @@ static void tegra_kbc_set_fifo_interrupt(struct tegra_kbc *kbc, bool enable) writel(val, kbc->mmio + KBC_CONTROL_0); } -static void tegra_kbc_set_keypress_interrupt(struct tegra_kbc *kbc, bool enable) -{ - u32 val; - - val = readl(kbc->mmio + KBC_CONTROL_0); - if (enable) - val |= KBC_CONTROL_KEYPRESS_INT_EN; - else - val &= ~KBC_CONTROL_KEYPRESS_INT_EN; - writel(val, kbc->mmio + KBC_CONTROL_0); -} - static void tegra_kbc_keypress_timer(unsigned long data) { struct tegra_kbc *kbc = (struct tegra_kbc *)data; @@ -866,6 +854,18 @@ static int tegra_kbc_remove(struct platform_device *pdev) } #ifdef CONFIG_PM_SLEEP +static void tegra_kbc_set_keypress_interrupt(struct tegra_kbc *kbc, bool enable) +{ + u32 val; + + val = readl(kbc->mmio + KBC_CONTROL_0); + if (enable) + val |= KBC_CONTROL_KEYPRESS_INT_EN; + else + val &= ~KBC_CONTROL_KEYPRESS_INT_EN; + writel(val, kbc->mmio + KBC_CONTROL_0); +} + static int tegra_kbc_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); From 00eb81e56a38ffb7839e0e6262e26c20c617d2ab Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Sun, 6 Jan 2013 18:31:20 -0800 Subject: [PATCH 16/55] Input: tegra-kbc - use devm_* for resource allocation Use devm_* for memory, clock, irq, input device allocation. This reduces code for freeing these resources. Signed-off-by: Laxman Dewangan Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/tegra-kbc.c | 105 ++++++++--------------------- 1 file changed, 27 insertions(+), 78 deletions(-) diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c index f1d3ba0dafb2..f79993679070 100644 --- a/drivers/input/keyboard/tegra-kbc.c +++ b/drivers/input/keyboard/tegra-kbc.c @@ -618,7 +618,7 @@ static struct tegra_kbc_platform_data *tegra_kbc_dt_parse_pdata( if (!np) return NULL; - pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return NULL; @@ -700,33 +700,36 @@ static int tegra_kbc_probe(struct platform_device *pdev) if (!pdata) pdata = tegra_kbc_dt_parse_pdata(pdev); - if (!pdata) + if (!pdata) { + dev_err(&pdev->dev, "Platform data missing\n"); return -EINVAL; - - if (!tegra_kbc_check_pin_cfg(pdata, &pdev->dev, &num_rows)) { - err = -EINVAL; - goto err_free_pdata; } + if (!tegra_kbc_check_pin_cfg(pdata, &pdev->dev, &num_rows)) + return -EINVAL; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(&pdev->dev, "failed to get I/O memory\n"); - err = -ENXIO; - goto err_free_pdata; + return -ENXIO; } irq = platform_get_irq(pdev, 0); if (irq < 0) { dev_err(&pdev->dev, "failed to get keyboard IRQ\n"); - err = -ENXIO; - goto err_free_pdata; + return -ENXIO; } - kbc = kzalloc(sizeof(*kbc), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!kbc || !input_dev) { - err = -ENOMEM; - goto err_free_mem; + kbc = devm_kzalloc(&pdev->dev, sizeof(*kbc), GFP_KERNEL); + if (!kbc) { + dev_err(&pdev->dev, "failed to alloc memory for kbc\n"); + return -ENOMEM; + } + + input_dev = devm_input_allocate_device(&pdev->dev); + if (!input_dev) { + dev_err(&pdev->dev, "failed to allocate input device\n"); + return -ENOMEM; } kbc->pdata = pdata; @@ -735,25 +738,16 @@ static int tegra_kbc_probe(struct platform_device *pdev) spin_lock_init(&kbc->lock); setup_timer(&kbc->timer, tegra_kbc_keypress_timer, (unsigned long)kbc); - res = request_mem_region(res->start, resource_size(res), pdev->name); - if (!res) { - dev_err(&pdev->dev, "failed to request I/O memory\n"); - err = -EBUSY; - goto err_free_mem; - } - - kbc->mmio = ioremap(res->start, resource_size(res)); + kbc->mmio = devm_request_and_ioremap(&pdev->dev, res); if (!kbc->mmio) { - dev_err(&pdev->dev, "failed to remap I/O memory\n"); - err = -ENXIO; - goto err_free_mem_region; + dev_err(&pdev->dev, "Cannot request memregion/iomap address\n"); + return -EBUSY; } - kbc->clk = clk_get(&pdev->dev, NULL); + kbc->clk = devm_clk_get(&pdev->dev, NULL); if (IS_ERR(kbc->clk)) { dev_err(&pdev->dev, "failed to get keyboard clock\n"); - err = PTR_ERR(kbc->clk); - goto err_iounmap; + return PTR_ERR(kbc->clk); } /* @@ -780,7 +774,7 @@ static int tegra_kbc_probe(struct platform_device *pdev) err = tegra_kbd_setup_keymap(kbc); if (err) { dev_err(&pdev->dev, "failed to setup keymap\n"); - goto err_put_clk; + return err; } __set_bit(EV_REP, input_dev->evbit); @@ -788,11 +782,11 @@ static int tegra_kbc_probe(struct platform_device *pdev) input_set_drvdata(input_dev, kbc); - err = request_irq(kbc->irq, tegra_kbc_isr, + err = devm_request_irq(&pdev->dev, kbc->irq, tegra_kbc_isr, IRQF_NO_SUSPEND | IRQF_TRIGGER_HIGH, pdev->name, kbc); if (err) { dev_err(&pdev->dev, "failed to request keyboard IRQ\n"); - goto err_put_clk; + return err; } disable_irq(kbc->irq); @@ -800,56 +794,12 @@ static int tegra_kbc_probe(struct platform_device *pdev) err = input_register_device(kbc->idev); if (err) { dev_err(&pdev->dev, "failed to register input device\n"); - goto err_free_irq; + return err; } platform_set_drvdata(pdev, kbc); device_init_wakeup(&pdev->dev, pdata->wakeup); - return 0; - -err_free_irq: - free_irq(kbc->irq, pdev); -err_put_clk: - clk_put(kbc->clk); -err_iounmap: - iounmap(kbc->mmio); -err_free_mem_region: - release_mem_region(res->start, resource_size(res)); -err_free_mem: - input_free_device(input_dev); - kfree(kbc); -err_free_pdata: - if (!pdev->dev.platform_data) - kfree(pdata); - - return err; -} - -static int tegra_kbc_remove(struct platform_device *pdev) -{ - struct tegra_kbc *kbc = platform_get_drvdata(pdev); - struct resource *res; - - platform_set_drvdata(pdev, NULL); - - free_irq(kbc->irq, pdev); - clk_put(kbc->clk); - - input_unregister_device(kbc->idev); - iounmap(kbc->mmio); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, resource_size(res)); - - /* - * If we do not have platform data attached to the device we - * allocated it ourselves and thus need to free it. - */ - if (!pdev->dev.platform_data) - kfree(kbc->pdata); - - kfree(kbc); - return 0; } @@ -954,7 +904,6 @@ MODULE_DEVICE_TABLE(of, tegra_kbc_of_match); static struct platform_driver tegra_kbc_driver = { .probe = tegra_kbc_probe, - .remove = tegra_kbc_remove, .driver = { .name = "tegra-kbc", .owner = THIS_MODULE, From 88390243a9937f9831aeba5113a8b59bb1d4411a Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Sun, 6 Jan 2013 18:32:22 -0800 Subject: [PATCH 17/55] Input: tegra-kbc - add support for rows/columns configuration from dt The NVIDIA's Tegra KBC has maximum 24 pins to make matrix keypad. Any pin can be configured as row or column. The maximum column pin can be 8 and maximum row pin can be 16. Remove the assumption that all first 16 pins will be used as row and remaining as columns and Add the property for configuring pins to either row or column from DT. Update the devicetree binding document accordingly. Signed-off-by: Laxman Dewangan Signed-off-by: Dmitry Torokhov --- .../bindings/input/nvidia,tegra20-kbc.txt | 22 +++++ drivers/input/keyboard/tegra-kbc.c | 80 ++++++++++++++----- 2 files changed, 84 insertions(+), 18 deletions(-) diff --git a/Documentation/devicetree/bindings/input/nvidia,tegra20-kbc.txt b/Documentation/devicetree/bindings/input/nvidia,tegra20-kbc.txt index 72683be6de35..2995fae7ee47 100644 --- a/Documentation/devicetree/bindings/input/nvidia,tegra20-kbc.txt +++ b/Documentation/devicetree/bindings/input/nvidia,tegra20-kbc.txt @@ -1,7 +1,18 @@ * Tegra keyboard controller +The key controller has maximum 24 pins to make matrix keypad. Any pin +can be configured as row or column. The maximum column pin can be 8 +and maximum row pins can be 16 for Tegra20/Tegra30. Required properties: - compatible: "nvidia,tegra20-kbc" +- reg: Register base address of KBC. +- interrupts: Interrupt number for the KBC. +- nvidia,kbc-row-pins: The KBC pins which are configured as row. This is an + array of pin numbers which is used as rows. +- nvidia,kbc-col-pins: The KBC pins which are configured as column. This is an + array of pin numbers which is used as column. +- linux,keymap: The keymap for keys as described in the binding document + devicetree/bindings/input/matrix-keymap.txt. Optional properties, in addition to those specified by the shared matrix-keyboard bindings: @@ -19,5 +30,16 @@ Example: keyboard: keyboard { compatible = "nvidia,tegra20-kbc"; reg = <0x7000e200 0x100>; + interrupts = <0 85 0x04>; nvidia,ghost-filter; + nvidia,debounce-delay-ms = <640>; + nvidia,kbc-row-pins = <0 1 2>; /* pin 0, 1, 2 as rows */ + nvidia,kbc-col-pins = <11 12 13>; /* pin 11, 12, 13 as columns */ + linux,keymap = <0x00000074 + 0x00010067 + 0x00020066 + 0x01010068 + 0x02000069 + 0x02010070 + 0x02020071>; }; diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c index f79993679070..39ef663bb38f 100644 --- a/drivers/input/keyboard/tegra-kbc.c +++ b/drivers/input/keyboard/tegra-kbc.c @@ -614,13 +614,21 @@ static struct tegra_kbc_platform_data *tegra_kbc_dt_parse_pdata( struct device_node *np = pdev->dev.of_node; u32 prop; int i; + u32 num_rows = 0; + u32 num_cols = 0; + u32 cols_cfg[KBC_MAX_GPIO]; + u32 rows_cfg[KBC_MAX_GPIO]; + int proplen; + int ret; - if (!np) - return NULL; + if (!np) { + dev_err(&pdev->dev, "device tree data is missing\n"); + return ERR_PTR(-ENOENT); + } pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) - return NULL; + return ERR_PTR(-ENOMEM); if (!of_property_read_u32(np, "nvidia,debounce-delay-ms", &prop)) pdata->debounce_cnt = prop; @@ -634,18 +642,55 @@ static struct tegra_kbc_platform_data *tegra_kbc_dt_parse_pdata( if (of_find_property(np, "nvidia,wakeup-source", NULL)) pdata->wakeup = true; - /* - * All currently known keymaps with device tree support use the same - * pin_cfg, so set it up here. - */ - for (i = 0; i < KBC_MAX_ROW; i++) { - pdata->pin_cfg[i].num = i; - pdata->pin_cfg[i].type = PIN_CFG_ROW; + if (!of_get_property(np, "nvidia,kbc-row-pins", &proplen)) { + dev_err(&pdev->dev, "property nvidia,kbc-row-pins not found\n"); + return ERR_PTR(-ENOENT); + } + num_rows = proplen / sizeof(u32); + + if (!of_get_property(np, "nvidia,kbc-col-pins", &proplen)) { + dev_err(&pdev->dev, "property nvidia,kbc-col-pins not found\n"); + return ERR_PTR(-ENOENT); + } + num_cols = proplen / sizeof(u32); + + if (!of_get_property(np, "linux,keymap", &proplen)) { + dev_err(&pdev->dev, "property linux,keymap not found\n"); + return ERR_PTR(-ENOENT); } - for (i = 0; i < KBC_MAX_COL; i++) { - pdata->pin_cfg[KBC_MAX_ROW + i].num = i; - pdata->pin_cfg[KBC_MAX_ROW + i].type = PIN_CFG_COL; + if (!num_rows || !num_cols || ((num_rows + num_cols) > KBC_MAX_GPIO)) { + dev_err(&pdev->dev, + "keypad rows/columns not porperly specified\n"); + return ERR_PTR(-EINVAL); + } + + /* Set all pins as non-configured */ + for (i = 0; i < KBC_MAX_GPIO; i++) + pdata->pin_cfg[i].type = PIN_CFG_IGNORE; + + ret = of_property_read_u32_array(np, "nvidia,kbc-row-pins", + rows_cfg, num_rows); + if (ret < 0) { + dev_err(&pdev->dev, "Rows configurations are not proper\n"); + return ERR_PTR(-EINVAL); + } + + ret = of_property_read_u32_array(np, "nvidia,kbc-col-pins", + cols_cfg, num_cols); + if (ret < 0) { + dev_err(&pdev->dev, "Cols configurations are not proper\n"); + return ERR_PTR(-EINVAL); + } + + for (i = 0; i < num_rows; i++) { + pdata->pin_cfg[rows_cfg[i]].type = PIN_CFG_ROW; + pdata->pin_cfg[rows_cfg[i]].num = i; + } + + for (i = 0; i < num_cols; i++) { + pdata->pin_cfg[cols_cfg[i]].type = PIN_CFG_COL; + pdata->pin_cfg[cols_cfg[i]].num = i; } return pdata; @@ -654,7 +699,8 @@ static struct tegra_kbc_platform_data *tegra_kbc_dt_parse_pdata( static inline struct tegra_kbc_platform_data *tegra_kbc_dt_parse_pdata( struct platform_device *pdev) { - return NULL; + dev_err(&pdev->dev, "platform data is missing\n"); + return ERR_PTR(-EINVAL); } #endif @@ -700,10 +746,8 @@ static int tegra_kbc_probe(struct platform_device *pdev) if (!pdata) pdata = tegra_kbc_dt_parse_pdata(pdev); - if (!pdata) { - dev_err(&pdev->dev, "Platform data missing\n"); - return -EINVAL; - } + if (IS_ERR(pdata)) + return PTR_ERR(pdata); if (!tegra_kbc_check_pin_cfg(pdata, &pdev->dev, &num_rows)) return -EINVAL; From 914e597682375ad732d2fedecd855ac637ed7ef7 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Sun, 6 Jan 2013 18:34:48 -0800 Subject: [PATCH 18/55] Input: tegra-kbc - remove default keymap Tegra KBC driver have the default key mapping for 16x8 configuration. The key mapping can be provided through platform data or through DT and the mapping varies from platform to platform, hence this default mapping is not so useful. Remove the default mapping to reduce the code lines of the driver. Signed-off-by: Laxman Dewangan Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/tegra-kbc.c | 176 ++--------------------------- 1 file changed, 7 insertions(+), 169 deletions(-) diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c index 39ef663bb38f..4526bdd36022 100644 --- a/drivers/input/keyboard/tegra-kbc.c +++ b/drivers/input/keyboard/tegra-kbc.c @@ -87,147 +87,6 @@ struct tegra_kbc { struct clk *clk; }; -static const u32 tegra_kbc_default_keymap[] = { - KEY(0, 2, KEY_W), - KEY(0, 3, KEY_S), - KEY(0, 4, KEY_A), - KEY(0, 5, KEY_Z), - KEY(0, 7, KEY_FN), - - KEY(1, 7, KEY_LEFTMETA), - - KEY(2, 6, KEY_RIGHTALT), - KEY(2, 7, KEY_LEFTALT), - - KEY(3, 0, KEY_5), - KEY(3, 1, KEY_4), - KEY(3, 2, KEY_R), - KEY(3, 3, KEY_E), - KEY(3, 4, KEY_F), - KEY(3, 5, KEY_D), - KEY(3, 6, KEY_X), - - KEY(4, 0, KEY_7), - KEY(4, 1, KEY_6), - KEY(4, 2, KEY_T), - KEY(4, 3, KEY_H), - KEY(4, 4, KEY_G), - KEY(4, 5, KEY_V), - KEY(4, 6, KEY_C), - KEY(4, 7, KEY_SPACE), - - KEY(5, 0, KEY_9), - KEY(5, 1, KEY_8), - KEY(5, 2, KEY_U), - KEY(5, 3, KEY_Y), - KEY(5, 4, KEY_J), - KEY(5, 5, KEY_N), - KEY(5, 6, KEY_B), - KEY(5, 7, KEY_BACKSLASH), - - KEY(6, 0, KEY_MINUS), - KEY(6, 1, KEY_0), - KEY(6, 2, KEY_O), - KEY(6, 3, KEY_I), - KEY(6, 4, KEY_L), - KEY(6, 5, KEY_K), - KEY(6, 6, KEY_COMMA), - KEY(6, 7, KEY_M), - - KEY(7, 1, KEY_EQUAL), - KEY(7, 2, KEY_RIGHTBRACE), - KEY(7, 3, KEY_ENTER), - KEY(7, 7, KEY_MENU), - - KEY(8, 4, KEY_RIGHTSHIFT), - KEY(8, 5, KEY_LEFTSHIFT), - - KEY(9, 5, KEY_RIGHTCTRL), - KEY(9, 7, KEY_LEFTCTRL), - - KEY(11, 0, KEY_LEFTBRACE), - KEY(11, 1, KEY_P), - KEY(11, 2, KEY_APOSTROPHE), - KEY(11, 3, KEY_SEMICOLON), - KEY(11, 4, KEY_SLASH), - KEY(11, 5, KEY_DOT), - - KEY(12, 0, KEY_F10), - KEY(12, 1, KEY_F9), - KEY(12, 2, KEY_BACKSPACE), - KEY(12, 3, KEY_3), - KEY(12, 4, KEY_2), - KEY(12, 5, KEY_UP), - KEY(12, 6, KEY_PRINT), - KEY(12, 7, KEY_PAUSE), - - KEY(13, 0, KEY_INSERT), - KEY(13, 1, KEY_DELETE), - KEY(13, 3, KEY_PAGEUP), - KEY(13, 4, KEY_PAGEDOWN), - KEY(13, 5, KEY_RIGHT), - KEY(13, 6, KEY_DOWN), - KEY(13, 7, KEY_LEFT), - - KEY(14, 0, KEY_F11), - KEY(14, 1, KEY_F12), - KEY(14, 2, KEY_F8), - KEY(14, 3, KEY_Q), - KEY(14, 4, KEY_F4), - KEY(14, 5, KEY_F3), - KEY(14, 6, KEY_1), - KEY(14, 7, KEY_F7), - - KEY(15, 0, KEY_ESC), - KEY(15, 1, KEY_GRAVE), - KEY(15, 2, KEY_F5), - KEY(15, 3, KEY_TAB), - KEY(15, 4, KEY_F1), - KEY(15, 5, KEY_F2), - KEY(15, 6, KEY_CAPSLOCK), - KEY(15, 7, KEY_F6), - - /* Software Handled Function Keys */ - KEY(20, 0, KEY_KP7), - - KEY(21, 0, KEY_KP9), - KEY(21, 1, KEY_KP8), - KEY(21, 2, KEY_KP4), - KEY(21, 4, KEY_KP1), - - KEY(22, 1, KEY_KPSLASH), - KEY(22, 2, KEY_KP6), - KEY(22, 3, KEY_KP5), - KEY(22, 4, KEY_KP3), - KEY(22, 5, KEY_KP2), - KEY(22, 7, KEY_KP0), - - KEY(27, 1, KEY_KPASTERISK), - KEY(27, 3, KEY_KPMINUS), - KEY(27, 4, KEY_KPPLUS), - KEY(27, 5, KEY_KPDOT), - - KEY(28, 5, KEY_VOLUMEUP), - - KEY(29, 3, KEY_HOME), - KEY(29, 4, KEY_END), - KEY(29, 5, KEY_BRIGHTNESSDOWN), - KEY(29, 6, KEY_VOLUMEDOWN), - KEY(29, 7, KEY_BRIGHTNESSUP), - - KEY(30, 0, KEY_NUMLOCK), - KEY(30, 1, KEY_SCROLLLOCK), - KEY(30, 2, KEY_MUTE), - - KEY(31, 4, KEY_HELP), -}; - -static const -struct matrix_keymap_data tegra_kbc_default_keymap_data = { - .keymap = tegra_kbc_default_keymap, - .keymap_size = ARRAY_SIZE(tegra_kbc_default_keymap), -}; - static void tegra_kbc_report_released_keys(struct input_dev *input, unsigned short old_keycodes[], unsigned int old_num_keys, @@ -704,33 +563,6 @@ static inline struct tegra_kbc_platform_data *tegra_kbc_dt_parse_pdata( } #endif -static int tegra_kbd_setup_keymap(struct tegra_kbc *kbc) -{ - const struct tegra_kbc_platform_data *pdata = kbc->pdata; - const struct matrix_keymap_data *keymap_data = pdata->keymap_data; - unsigned int keymap_rows = KBC_MAX_KEY; - int retval; - - if (keymap_data && pdata->use_fn_map) - keymap_rows *= 2; - - retval = matrix_keypad_build_keymap(keymap_data, NULL, - keymap_rows, KBC_MAX_COL, - kbc->keycode, kbc->idev); - if (retval == -ENOSYS || retval == -ENOENT) { - /* - * If there is no OF support in kernel or keymap - * property is missing, use default keymap. - */ - retval = matrix_keypad_build_keymap( - &tegra_kbc_default_keymap_data, NULL, - keymap_rows, KBC_MAX_COL, - kbc->keycode, kbc->idev); - } - - return retval; -} - static int tegra_kbc_probe(struct platform_device *pdev) { const struct tegra_kbc_platform_data *pdata = pdev->dev.platform_data; @@ -742,6 +574,7 @@ static int tegra_kbc_probe(struct platform_device *pdev) int num_rows = 0; unsigned int debounce_cnt; unsigned int scan_time_rows; + unsigned int keymap_rows = KBC_MAX_KEY; if (!pdata) pdata = tegra_kbc_dt_parse_pdata(pdev); @@ -815,7 +648,12 @@ static int tegra_kbc_probe(struct platform_device *pdev) input_dev->open = tegra_kbc_open; input_dev->close = tegra_kbc_close; - err = tegra_kbd_setup_keymap(kbc); + if (pdata->keymap_data && pdata->use_fn_map) + keymap_rows *= 2; + + err = matrix_keypad_build_keymap(pdata->keymap_data, NULL, + keymap_rows, KBC_MAX_COL, + kbc->keycode, input_dev); if (err) { dev_err(&pdev->dev, "failed to setup keymap\n"); return err; From 8a0b6c5d14bbaf62a4f19131ac16ac81ed909fda Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 9 Jan 2013 09:03:27 -0800 Subject: [PATCH 19/55] Input: mms114 - switch to using managed resources devm_* APIs are device managed and make the exit and clean up code simpler. Signed-off-by: Sachin Kamat Acked-by: Laxman Dewangan Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/mms114.c | 54 +++++++++--------------------- 1 file changed, 16 insertions(+), 38 deletions(-) diff --git a/drivers/input/touchscreen/mms114.c b/drivers/input/touchscreen/mms114.c index 98841d8aa635..4a29ddf6bf1e 100644 --- a/drivers/input/touchscreen/mms114.c +++ b/drivers/input/touchscreen/mms114.c @@ -429,12 +429,12 @@ static int mms114_probe(struct i2c_client *client, return -ENODEV; } - data = kzalloc(sizeof(struct mms114_data), GFP_KERNEL); - input_dev = input_allocate_device(); + data = devm_kzalloc(&client->dev, sizeof(struct mms114_data), + GFP_KERNEL); + input_dev = devm_input_allocate_device(&client->dev); if (!data || !input_dev) { dev_err(&client->dev, "Failed to allocate memory\n"); - error = -ENOMEM; - goto err_free_mem; + return -ENOMEM; } data->client = client; @@ -466,57 +466,36 @@ static int mms114_probe(struct i2c_client *client, input_set_drvdata(input_dev, data); i2c_set_clientdata(client, data); - data->core_reg = regulator_get(&client->dev, "avdd"); + data->core_reg = devm_regulator_get(&client->dev, "avdd"); if (IS_ERR(data->core_reg)) { error = PTR_ERR(data->core_reg); dev_err(&client->dev, "Unable to get the Core regulator (%d)\n", error); - goto err_free_mem; + return error; } - data->io_reg = regulator_get(&client->dev, "vdd"); + data->io_reg = devm_regulator_get(&client->dev, "vdd"); if (IS_ERR(data->io_reg)) { error = PTR_ERR(data->io_reg); dev_err(&client->dev, "Unable to get the IO regulator (%d)\n", error); - goto err_core_reg; + return error; } - error = request_threaded_irq(client->irq, NULL, mms114_interrupt, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "mms114", data); + error = devm_request_threaded_irq(&client->dev, client->irq, NULL, + mms114_interrupt, IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + dev_name(&client->dev), data); if (error) { dev_err(&client->dev, "Failed to register interrupt\n"); - goto err_io_reg; + return error; } disable_irq(client->irq); error = input_register_device(data->input_dev); - if (error) - goto err_free_irq; - - return 0; - -err_free_irq: - free_irq(client->irq, data); -err_io_reg: - regulator_put(data->io_reg); -err_core_reg: - regulator_put(data->core_reg); -err_free_mem: - input_free_device(input_dev); - kfree(data); - return error; -} - -static int mms114_remove(struct i2c_client *client) -{ - struct mms114_data *data = i2c_get_clientdata(client); - - free_irq(client->irq, data); - regulator_put(data->io_reg); - regulator_put(data->core_reg); - input_unregister_device(data->input_dev); - kfree(data); + if (error) { + dev_err(&client->dev, "Failed to register input device\n"); + return error; + } return 0; } @@ -590,7 +569,6 @@ static struct i2c_driver mms114_driver = { .of_match_table = of_match_ptr(mms114_dt_match), }, .probe = mms114_probe, - .remove = mms114_remove, .id_table = mms114_id, }; From 236d6a77eeac702abb8e908a6ec11a37507c5071 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Mon, 31 Dec 2012 12:42:51 -0800 Subject: [PATCH 20/55] Input: atkbd - fix a typo in a message Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/atkbd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index add5ffd9fe26..33d0fcde441c 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -726,7 +726,7 @@ static int atkbd_probe(struct atkbd *atkbd) if (atkbd->id == 0xaca1 && atkbd->translated) { dev_err(&ps2dev->serio->dev, - "NCD terminal keyboards are only supported on non-translating controlelrs. " + "NCD terminal keyboards are only supported on non-translating controllers. " "Use i8042.direct=1 to disable translation.\n"); return -1; } From d7e34d12ba877fa77080257d2621c29769333c40 Mon Sep 17 00:00:00 2001 From: Benson Leung Date: Wed, 9 Jan 2013 16:25:11 -0800 Subject: [PATCH 21/55] Input: add driver for Cypress APA I2C Trackpad This patch introduces a driver for Cypress All Points Addressable I2C Trackpad, including the ones in 2012 Samsung Chromebooks. This device is compatible with MT protocol type B, providing identifiable contacts. Signed-off-by: Dudley Du Signed-off-by: Daniel Kurtz Signed-off-by: Benson Leung Reviewed-by: Henrik Rydberg Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/Kconfig | 12 + drivers/input/mouse/Makefile | 1 + drivers/input/mouse/cyapa.c | 804 +++++++++++++++++++++++++++++++++++ 3 files changed, 817 insertions(+) create mode 100644 drivers/input/mouse/cyapa.c diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index 88954ddae6d9..802bd6a72d73 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -203,6 +203,18 @@ config MOUSE_BCM5974 To compile this driver as a module, choose M here: the module will be called bcm5974. +config MOUSE_CYAPA + tristate "Cypress APA I2C Trackpad support" + depends on I2C + help + This driver adds support for Cypress All Points Addressable (APA) + I2C Trackpads, including the ones used in 2012 Samsung Chromebooks. + + Say Y here if you have a Cypress APA I2C Trackpad. + + To compile this driver as a module, choose M here: the module will be + called cyapa. + config MOUSE_INPORT tristate "InPort/MS/ATIXL busmouse" depends on ISA diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile index 323e352189ba..c25efdb3f288 100644 --- a/drivers/input/mouse/Makefile +++ b/drivers/input/mouse/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_MOUSE_AMIGA) += amimouse.o obj-$(CONFIG_MOUSE_APPLETOUCH) += appletouch.o obj-$(CONFIG_MOUSE_ATARI) += atarimouse.o obj-$(CONFIG_MOUSE_BCM5974) += bcm5974.o +obj-$(CONFIG_MOUSE_CYAPA) += cyapa.o obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o obj-$(CONFIG_MOUSE_INPORT) += inport.o obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o diff --git a/drivers/input/mouse/cyapa.c b/drivers/input/mouse/cyapa.c new file mode 100644 index 000000000000..1a14688329be --- /dev/null +++ b/drivers/input/mouse/cyapa.c @@ -0,0 +1,804 @@ +/* + * Cypress APA trackpad with I2C interface + * + * Author: Dudley Du + * Further cleanup and restructuring by: + * Daniel Kurtz + * Benson Leung + * + * Copyright (C) 2011-2012 Cypress Semiconductor, Inc. + * Copyright (C) 2011-2012 Google, Inc. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* APA trackpad firmware generation */ +#define CYAPA_GEN3 0x03 /* support MT-protocol B with tracking ID. */ + +#define CYAPA_NAME "Cypress APA Trackpad (cyapa)" + +/* commands for read/write registers of Cypress trackpad */ +#define CYAPA_CMD_SOFT_RESET 0x00 +#define CYAPA_CMD_POWER_MODE 0x01 +#define CYAPA_CMD_DEV_STATUS 0x02 +#define CYAPA_CMD_GROUP_DATA 0x03 +#define CYAPA_CMD_GROUP_CMD 0x04 +#define CYAPA_CMD_GROUP_QUERY 0x05 +#define CYAPA_CMD_BL_STATUS 0x06 +#define CYAPA_CMD_BL_HEAD 0x07 +#define CYAPA_CMD_BL_CMD 0x08 +#define CYAPA_CMD_BL_DATA 0x09 +#define CYAPA_CMD_BL_ALL 0x0a +#define CYAPA_CMD_BLK_PRODUCT_ID 0x0b +#define CYAPA_CMD_BLK_HEAD 0x0c + +/* report data start reg offset address. */ +#define DATA_REG_START_OFFSET 0x0000 + +#define BL_HEAD_OFFSET 0x00 +#define BL_DATA_OFFSET 0x10 + +/* + * Operational Device Status Register + * + * bit 7: Valid interrupt source + * bit 6 - 4: Reserved + * bit 3 - 2: Power status + * bit 1 - 0: Device status + */ +#define REG_OP_STATUS 0x00 +#define OP_STATUS_SRC 0x80 +#define OP_STATUS_POWER 0x0c +#define OP_STATUS_DEV 0x03 +#define OP_STATUS_MASK (OP_STATUS_SRC | OP_STATUS_POWER | OP_STATUS_DEV) + +/* + * Operational Finger Count/Button Flags Register + * + * bit 7 - 4: Number of touched finger + * bit 3: Valid data + * bit 2: Middle Physical Button + * bit 1: Right Physical Button + * bit 0: Left physical Button + */ +#define REG_OP_DATA1 0x01 +#define OP_DATA_VALID 0x08 +#define OP_DATA_MIDDLE_BTN 0x04 +#define OP_DATA_RIGHT_BTN 0x02 +#define OP_DATA_LEFT_BTN 0x01 +#define OP_DATA_BTN_MASK (OP_DATA_MIDDLE_BTN | OP_DATA_RIGHT_BTN | \ + OP_DATA_LEFT_BTN) + +/* + * Bootloader Status Register + * + * bit 7: Busy + * bit 6 - 5: Reserved + * bit 4: Bootloader running + * bit 3 - 1: Reserved + * bit 0: Checksum valid + */ +#define REG_BL_STATUS 0x01 +#define BL_STATUS_BUSY 0x80 +#define BL_STATUS_RUNNING 0x10 +#define BL_STATUS_DATA_VALID 0x08 +#define BL_STATUS_CSUM_VALID 0x01 + +/* + * Bootloader Error Register + * + * bit 7: Invalid + * bit 6: Invalid security key + * bit 5: Bootloading + * bit 4: Command checksum + * bit 3: Flash protection error + * bit 2: Flash checksum error + * bit 1 - 0: Reserved + */ +#define REG_BL_ERROR 0x02 +#define BL_ERROR_INVALID 0x80 +#define BL_ERROR_INVALID_KEY 0x40 +#define BL_ERROR_BOOTLOADING 0x20 +#define BL_ERROR_CMD_CSUM 0x10 +#define BL_ERROR_FLASH_PROT 0x08 +#define BL_ERROR_FLASH_CSUM 0x04 + +#define BL_STATUS_SIZE 3 /* length of bootloader status registers */ +#define BLK_HEAD_BYTES 32 + +#define PRODUCT_ID_SIZE 16 +#define QUERY_DATA_SIZE 27 +#define REG_PROTOCOL_GEN_QUERY_OFFSET 20 + +#define REG_OFFSET_DATA_BASE 0x0000 +#define REG_OFFSET_COMMAND_BASE 0x0028 +#define REG_OFFSET_QUERY_BASE 0x002a + +#define CAPABILITY_LEFT_BTN_MASK (0x01 << 3) +#define CAPABILITY_RIGHT_BTN_MASK (0x01 << 4) +#define CAPABILITY_MIDDLE_BTN_MASK (0x01 << 5) +#define CAPABILITY_BTN_MASK (CAPABILITY_LEFT_BTN_MASK | \ + CAPABILITY_RIGHT_BTN_MASK | \ + CAPABILITY_MIDDLE_BTN_MASK) + +#define CYAPA_OFFSET_SOFT_RESET REG_OFFSET_COMMAND_BASE + +#define REG_OFFSET_POWER_MODE (REG_OFFSET_COMMAND_BASE + 1) + +#define PWR_MODE_MASK 0xfc +#define PWR_MODE_FULL_ACTIVE (0x3f << 2) +#define PWR_MODE_IDLE (0x05 << 2) /* default sleep time is 50 ms. */ +#define PWR_MODE_OFF (0x00 << 2) + +#define PWR_STATUS_MASK 0x0c +#define PWR_STATUS_ACTIVE (0x03 << 2) +#define PWR_STATUS_IDLE (0x02 << 2) +#define PWR_STATUS_OFF (0x00 << 2) + +/* + * CYAPA trackpad device states. + * Used in register 0x00, bit1-0, DeviceStatus field. + * Other values indicate device is in an abnormal state and must be reset. + */ +#define CYAPA_DEV_NORMAL 0x03 +#define CYAPA_DEV_BUSY 0x01 + +enum cyapa_state { + CYAPA_STATE_OP, + CYAPA_STATE_BL_IDLE, + CYAPA_STATE_BL_ACTIVE, + CYAPA_STATE_BL_BUSY, + CYAPA_STATE_NO_DEVICE, +}; + + +struct cyapa_touch { + /* + * high bits or x/y position value + * bit 7 - 4: high 4 bits of x position value + * bit 3 - 0: high 4 bits of y position value + */ + u8 xy_hi; + u8 x_lo; /* low 8 bits of x position value. */ + u8 y_lo; /* low 8 bits of y position value. */ + u8 pressure; + /* id range is 1 - 15. It is incremented with every new touch. */ + u8 id; +} __packed; + +/* The touch.id is used as the MT slot id, thus max MT slot is 15 */ +#define CYAPA_MAX_MT_SLOTS 15 + +struct cyapa_reg_data { + /* + * bit 0 - 1: device status + * bit 3 - 2: power mode + * bit 6 - 4: reserved + * bit 7: interrupt valid bit + */ + u8 device_status; + /* + * bit 7 - 4: number of fingers currently touching pad + * bit 3: valid data check bit + * bit 2: middle mechanism button state if exists + * bit 1: right mechanism button state if exists + * bit 0: left mechanism button state if exists + */ + u8 finger_btn; + /* CYAPA reports up to 5 touches per packet. */ + struct cyapa_touch touches[5]; +} __packed; + +/* The main device structure */ +struct cyapa { + enum cyapa_state state; + + struct i2c_client *client; + struct input_dev *input; + char phys[32]; /* device physical location */ + int irq; + bool irq_wake; /* irq wake is enabled */ + + /* read from query data region. */ + char product_id[16]; + u8 btn_capability; + u8 gen; + int max_abs_x; + int max_abs_y; + int physical_size_x; + int physical_size_y; +}; + +static const u8 bl_deactivate[] = { 0x00, 0xff, 0x3b, 0x00, 0x01, 0x02, 0x03, + 0x04, 0x05, 0x06, 0x07 }; +static const u8 bl_exit[] = { 0x00, 0xff, 0xa5, 0x00, 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07 }; + +struct cyapa_cmd_len { + u8 cmd; + u8 len; +}; + +static const struct cyapa_cmd_len cyapa_i2c_cmds[] = { + { CYAPA_OFFSET_SOFT_RESET, 1 }, + { REG_OFFSET_COMMAND_BASE + 1, 1 }, + { REG_OFFSET_DATA_BASE, 1 }, + { REG_OFFSET_DATA_BASE, sizeof(struct cyapa_reg_data) }, + { REG_OFFSET_COMMAND_BASE, 0 }, + { REG_OFFSET_QUERY_BASE, QUERY_DATA_SIZE }, + { BL_HEAD_OFFSET, 3 }, + { BL_HEAD_OFFSET, 16 }, + { BL_HEAD_OFFSET, 16 }, + { BL_DATA_OFFSET, 16 }, + { BL_HEAD_OFFSET, 32 }, + { REG_OFFSET_QUERY_BASE, PRODUCT_ID_SIZE }, + { REG_OFFSET_DATA_BASE, 32 } +}; + +static ssize_t cyapa_i2c_reg_read_block(struct cyapa *cyapa, u8 reg, size_t len, + u8 *values) +{ + return i2c_smbus_read_i2c_block_data(cyapa->client, reg, len, values); +} + +static ssize_t cyapa_i2c_reg_write_block(struct cyapa *cyapa, u8 reg, + size_t len, const u8 *values) +{ + return i2c_smbus_write_i2c_block_data(cyapa->client, reg, len, values); +} + +static s32 cyapa_read_byte(struct cyapa *cyapa, u8 cmd_idx) +{ + u8 cmd = cyapa_i2c_cmds[cmd_idx].cmd; + + return i2c_smbus_read_byte_data(cyapa->client, cmd); +} + +static s32 cyapa_write_byte(struct cyapa *cyapa, u8 cmd_idx, u8 value) +{ + u8 cmd = cyapa_i2c_cmds[cmd_idx].cmd; + + return i2c_smbus_write_byte_data(cyapa->client, cmd, value); +} + +static ssize_t cyapa_read_block(struct cyapa *cyapa, u8 cmd_idx, u8 *values) +{ + u8 cmd = cyapa_i2c_cmds[cmd_idx].cmd; + size_t len = cyapa_i2c_cmds[cmd_idx].len; + + return cyapa_i2c_reg_read_block(cyapa, cmd, len, values); +} + +/* + * Query device for its current operating state. + * + */ +static int cyapa_get_state(struct cyapa *cyapa) +{ + int ret; + u8 status[BL_STATUS_SIZE]; + + cyapa->state = CYAPA_STATE_NO_DEVICE; + + /* + * Get trackpad status by reading 3 registers starting from 0. + * If the device is in the bootloader, this will be BL_HEAD. + * If the device is in operation mode, this will be the DATA regs. + * + */ + ret = cyapa_i2c_reg_read_block(cyapa, BL_HEAD_OFFSET, BL_STATUS_SIZE, + status); + if (ret != BL_STATUS_SIZE) + goto error; + + if ((status[REG_OP_STATUS] & OP_STATUS_SRC) == OP_STATUS_SRC) { + switch (status[REG_OP_STATUS] & OP_STATUS_DEV) { + case CYAPA_DEV_NORMAL: + case CYAPA_DEV_BUSY: + cyapa->state = CYAPA_STATE_OP; + break; + default: + ret = -EAGAIN; + goto error; + } + } else { + if (status[REG_BL_STATUS] & BL_STATUS_BUSY) + cyapa->state = CYAPA_STATE_BL_BUSY; + else if (status[REG_BL_ERROR] & BL_ERROR_BOOTLOADING) + cyapa->state = CYAPA_STATE_BL_ACTIVE; + else + cyapa->state = CYAPA_STATE_BL_IDLE; + } + + return 0; +error: + return (ret < 0) ? ret : -EAGAIN; +} + +/* + * Poll device for its status in a loop, waiting up to timeout for a response. + * + * When the device switches state, it usually takes ~300 ms. + * However, when running a new firmware image, the device must calibrate its + * sensors, which can take as long as 2 seconds. + * + * Note: The timeout has granularity of the polling rate, which is 100 ms. + * + * Returns: + * 0 when the device eventually responds with a valid non-busy state. + * -ETIMEDOUT if device never responds (too many -EAGAIN) + * < 0 other errors + */ +static int cyapa_poll_state(struct cyapa *cyapa, unsigned int timeout) +{ + int ret; + int tries = timeout / 100; + + ret = cyapa_get_state(cyapa); + while ((ret || cyapa->state >= CYAPA_STATE_BL_BUSY) && tries--) { + msleep(100); + ret = cyapa_get_state(cyapa); + } + return (ret == -EAGAIN || ret == -ETIMEDOUT) ? -ETIMEDOUT : ret; +} + +static int cyapa_bl_deactivate(struct cyapa *cyapa) +{ + int ret; + + ret = cyapa_i2c_reg_write_block(cyapa, 0, sizeof(bl_deactivate), + bl_deactivate); + if (ret < 0) + return ret; + + /* wait for bootloader to switch to idle state; should take < 100ms */ + msleep(100); + ret = cyapa_poll_state(cyapa, 500); + if (ret < 0) + return ret; + if (cyapa->state != CYAPA_STATE_BL_IDLE) + return -EAGAIN; + return 0; +} + +/* + * Exit bootloader + * + * Send bl_exit command, then wait 50 - 100 ms to let device transition to + * operational mode. If this is the first time the device's firmware is + * running, it can take up to 2 seconds to calibrate its sensors. So, poll + * the device's new state for up to 2 seconds. + * + * Returns: + * -EIO failure while reading from device + * -EAGAIN device is stuck in bootloader, b/c it has invalid firmware + * 0 device is supported and in operational mode + */ +static int cyapa_bl_exit(struct cyapa *cyapa) +{ + int ret; + + ret = cyapa_i2c_reg_write_block(cyapa, 0, sizeof(bl_exit), bl_exit); + if (ret < 0) + return ret; + + /* + * Wait for bootloader to exit, and operation mode to start. + * Normally, this takes at least 50 ms. + */ + usleep_range(50000, 100000); + /* + * In addition, when a device boots for the first time after being + * updated to new firmware, it must first calibrate its sensors, which + * can take up to an additional 2 seconds. + */ + ret = cyapa_poll_state(cyapa, 2000); + if (ret < 0) + return ret; + if (cyapa->state != CYAPA_STATE_OP) + return -EAGAIN; + + return 0; +} + +/* + * Set device power mode + * + */ +static int cyapa_set_power_mode(struct cyapa *cyapa, u8 power_mode) +{ + struct device *dev = &cyapa->client->dev; + int ret; + u8 power; + + if (cyapa->state != CYAPA_STATE_OP) + return 0; + + ret = cyapa_read_byte(cyapa, CYAPA_CMD_POWER_MODE); + if (ret < 0) + return ret; + + power = ret & ~PWR_MODE_MASK; + power |= power_mode & PWR_MODE_MASK; + ret = cyapa_write_byte(cyapa, CYAPA_CMD_POWER_MODE, power); + if (ret < 0) + dev_err(dev, "failed to set power_mode 0x%02x err = %d\n", + power_mode, ret); + return ret; +} + +static int cyapa_get_query_data(struct cyapa *cyapa) +{ + u8 query_data[QUERY_DATA_SIZE]; + int ret; + + if (cyapa->state != CYAPA_STATE_OP) + return -EBUSY; + + ret = cyapa_read_block(cyapa, CYAPA_CMD_GROUP_QUERY, query_data); + if (ret < 0) + return ret; + if (ret != QUERY_DATA_SIZE) + return -EIO; + + memcpy(&cyapa->product_id[0], &query_data[0], 5); + cyapa->product_id[5] = '-'; + memcpy(&cyapa->product_id[6], &query_data[5], 6); + cyapa->product_id[12] = '-'; + memcpy(&cyapa->product_id[13], &query_data[11], 2); + cyapa->product_id[15] = '\0'; + + cyapa->btn_capability = query_data[19] & CAPABILITY_BTN_MASK; + + cyapa->gen = query_data[20] & 0x0f; + + cyapa->max_abs_x = ((query_data[21] & 0xf0) << 4) | query_data[22]; + cyapa->max_abs_y = ((query_data[21] & 0x0f) << 8) | query_data[23]; + + cyapa->physical_size_x = + ((query_data[24] & 0xf0) << 4) | query_data[25]; + cyapa->physical_size_y = + ((query_data[24] & 0x0f) << 8) | query_data[26]; + + return 0; +} + +/* + * Check if device is operational. + * + * An operational device is responding, has exited bootloader, and has + * firmware supported by this driver. + * + * Returns: + * -EBUSY no device or in bootloader + * -EIO failure while reading from device + * -EAGAIN device is still in bootloader + * if ->state = CYAPA_STATE_BL_IDLE, device has invalid firmware + * -EINVAL device is in operational mode, but not supported by this driver + * 0 device is supported + */ +static int cyapa_check_is_operational(struct cyapa *cyapa) +{ + struct device *dev = &cyapa->client->dev; + static const char unique_str[] = "CYTRA"; + int ret; + + ret = cyapa_poll_state(cyapa, 2000); + if (ret < 0) + return ret; + switch (cyapa->state) { + case CYAPA_STATE_BL_ACTIVE: + ret = cyapa_bl_deactivate(cyapa); + if (ret) + return ret; + + /* Fallthrough state */ + case CYAPA_STATE_BL_IDLE: + ret = cyapa_bl_exit(cyapa); + if (ret) + return ret; + + /* Fallthrough state */ + case CYAPA_STATE_OP: + ret = cyapa_get_query_data(cyapa); + if (ret < 0) + return ret; + + /* only support firmware protocol gen3 */ + if (cyapa->gen != CYAPA_GEN3) { + dev_err(dev, "unsupported protocol version (%d)", + cyapa->gen); + return -EINVAL; + } + + /* only support product ID starting with CYTRA */ + if (memcmp(cyapa->product_id, unique_str, + sizeof(unique_str) - 1) != 0) { + dev_err(dev, "unsupported product ID (%s)\n", + cyapa->product_id); + return -EINVAL; + } + return 0; + + default: + return -EIO; + } + return 0; +} + +static irqreturn_t cyapa_irq(int irq, void *dev_id) +{ + struct cyapa *cyapa = dev_id; + struct device *dev = &cyapa->client->dev; + struct input_dev *input = cyapa->input; + struct cyapa_reg_data data; + int i; + int ret; + int num_fingers; + + if (device_may_wakeup(dev)) + pm_wakeup_event(dev, 0); + + ret = cyapa_read_block(cyapa, CYAPA_CMD_GROUP_DATA, (u8 *)&data); + if (ret != sizeof(data)) + goto out; + + if ((data.device_status & OP_STATUS_SRC) != OP_STATUS_SRC || + (data.device_status & OP_STATUS_DEV) != CYAPA_DEV_NORMAL || + (data.finger_btn & OP_DATA_VALID) != OP_DATA_VALID) { + goto out; + } + + num_fingers = (data.finger_btn >> 4) & 0x0f; + for (i = 0; i < num_fingers; i++) { + const struct cyapa_touch *touch = &data.touches[i]; + /* Note: touch->id range is 1 to 15; slots are 0 to 14. */ + int slot = touch->id - 1; + + input_mt_slot(input, slot); + input_mt_report_slot_state(input, MT_TOOL_FINGER, true); + input_report_abs(input, ABS_MT_POSITION_X, + ((touch->xy_hi & 0xf0) << 4) | touch->x_lo); + input_report_abs(input, ABS_MT_POSITION_Y, + ((touch->xy_hi & 0x0f) << 8) | touch->y_lo); + input_report_abs(input, ABS_MT_PRESSURE, touch->pressure); + } + + input_mt_sync_frame(input); + + if (cyapa->btn_capability & CAPABILITY_LEFT_BTN_MASK) + input_report_key(input, BTN_LEFT, + data.finger_btn & OP_DATA_LEFT_BTN); + + if (cyapa->btn_capability & CAPABILITY_MIDDLE_BTN_MASK) + input_report_key(input, BTN_MIDDLE, + data.finger_btn & OP_DATA_MIDDLE_BTN); + + if (cyapa->btn_capability & CAPABILITY_RIGHT_BTN_MASK) + input_report_key(input, BTN_RIGHT, + data.finger_btn & OP_DATA_RIGHT_BTN); + + input_sync(input); + +out: + return IRQ_HANDLED; +} + +static int cyapa_create_input_dev(struct cyapa *cyapa) +{ + struct device *dev = &cyapa->client->dev; + int ret; + struct input_dev *input; + + if (!cyapa->physical_size_x || !cyapa->physical_size_y) + return -EINVAL; + + input = cyapa->input = input_allocate_device(); + if (!input) { + dev_err(dev, "allocate memory for input device failed\n"); + return -ENOMEM; + } + + input->name = CYAPA_NAME; + input->phys = cyapa->phys; + input->id.bustype = BUS_I2C; + input->id.version = 1; + input->id.product = 0; /* means any product in eventcomm. */ + input->dev.parent = &cyapa->client->dev; + + input_set_drvdata(input, cyapa); + + __set_bit(EV_ABS, input->evbit); + + /* finger position */ + input_set_abs_params(input, ABS_MT_POSITION_X, 0, cyapa->max_abs_x, 0, + 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, cyapa->max_abs_y, 0, + 0); + input_set_abs_params(input, ABS_MT_PRESSURE, 0, 255, 0, 0); + + input_abs_set_res(input, ABS_MT_POSITION_X, + cyapa->max_abs_x / cyapa->physical_size_x); + input_abs_set_res(input, ABS_MT_POSITION_Y, + cyapa->max_abs_y / cyapa->physical_size_y); + + if (cyapa->btn_capability & CAPABILITY_LEFT_BTN_MASK) + __set_bit(BTN_LEFT, input->keybit); + if (cyapa->btn_capability & CAPABILITY_MIDDLE_BTN_MASK) + __set_bit(BTN_MIDDLE, input->keybit); + if (cyapa->btn_capability & CAPABILITY_RIGHT_BTN_MASK) + __set_bit(BTN_RIGHT, input->keybit); + + if (cyapa->btn_capability == CAPABILITY_LEFT_BTN_MASK) + __set_bit(INPUT_PROP_BUTTONPAD, input->propbit); + + /* handle pointer emulation and unused slots in core */ + ret = input_mt_init_slots(input, CYAPA_MAX_MT_SLOTS, + INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED); + if (ret) { + dev_err(dev, "allocate memory for MT slots failed, %d\n", ret); + goto err_free_device; + } + + /* Register the device in input subsystem */ + ret = input_register_device(input); + if (ret) { + dev_err(dev, "input device register failed, %d\n", ret); + goto err_free_device; + } + return 0; + +err_free_device: + input_free_device(input); + cyapa->input = NULL; + return ret; +} + +static int cyapa_probe(struct i2c_client *client, + const struct i2c_device_id *dev_id) +{ + int ret; + struct cyapa *cyapa; + struct device *dev = &client->dev; + + cyapa = kzalloc(sizeof(struct cyapa), GFP_KERNEL); + if (!cyapa) { + dev_err(dev, "allocate memory for cyapa failed\n"); + return -ENOMEM; + } + + cyapa->gen = CYAPA_GEN3; + cyapa->client = client; + i2c_set_clientdata(client, cyapa); + sprintf(cyapa->phys, "i2c-%d-%04x/input0", client->adapter->nr, + client->addr); + + cyapa->state = CYAPA_STATE_NO_DEVICE; + ret = cyapa_check_is_operational(cyapa); + if (ret) { + dev_err(dev, "device not operational, %d\n", ret); + goto err_mem_free; + } + + ret = cyapa_create_input_dev(cyapa); + if (ret) { + dev_err(dev, "create input_dev instance failed, %d\n", ret); + goto err_mem_free; + } + + ret = cyapa_set_power_mode(cyapa, PWR_MODE_FULL_ACTIVE); + if (ret) { + dev_err(dev, "set active power failed, %d\n", ret); + goto err_unregister_device; + } + + cyapa->irq = client->irq; + ret = request_threaded_irq(cyapa->irq, + NULL, + cyapa_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "cyapa", + cyapa); + if (ret) { + dev_err(dev, "IRQ request failed: %d\n, ", ret); + goto err_unregister_device; + } + + return 0; + +err_unregister_device: + input_unregister_device(cyapa->input); +err_mem_free: + kfree(cyapa); + + return ret; +} + +static int cyapa_remove(struct i2c_client *client) +{ + struct cyapa *cyapa = i2c_get_clientdata(client); + + free_irq(cyapa->irq, cyapa); + input_unregister_device(cyapa->input); + cyapa_set_power_mode(cyapa, PWR_MODE_OFF); + kfree(cyapa); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int cyapa_suspend(struct device *dev) +{ + int ret; + u8 power_mode; + struct cyapa *cyapa = dev_get_drvdata(dev); + + disable_irq(cyapa->irq); + + /* + * Set trackpad device to idle mode if wakeup is allowed, + * otherwise turn off. + */ + power_mode = device_may_wakeup(dev) ? PWR_MODE_IDLE + : PWR_MODE_OFF; + ret = cyapa_set_power_mode(cyapa, power_mode); + if (ret < 0) + dev_err(dev, "set power mode failed, %d\n", ret); + + if (device_may_wakeup(dev)) + cyapa->irq_wake = (enable_irq_wake(cyapa->irq) == 0); + return 0; +} + +static int cyapa_resume(struct device *dev) +{ + int ret; + struct cyapa *cyapa = dev_get_drvdata(dev); + + if (device_may_wakeup(dev) && cyapa->irq_wake) + disable_irq_wake(cyapa->irq); + + ret = cyapa_set_power_mode(cyapa, PWR_MODE_FULL_ACTIVE); + if (ret) + dev_warn(dev, "resume active power failed, %d\n", ret); + + enable_irq(cyapa->irq); + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static SIMPLE_DEV_PM_OPS(cyapa_pm_ops, cyapa_suspend, cyapa_resume); + +static const struct i2c_device_id cyapa_id_table[] = { + { "cyapa", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, cyapa_id_table); + +static struct i2c_driver cyapa_driver = { + .driver = { + .name = "cyapa", + .owner = THIS_MODULE, + .pm = &cyapa_pm_ops, + }, + + .probe = cyapa_probe, + .remove = cyapa_remove, + .id_table = cyapa_id_table, +}; + +module_i2c_driver(cyapa_driver); + +MODULE_DESCRIPTION("Cypress APA I2C Trackpad Driver"); +MODULE_AUTHOR("Dudley Du "); +MODULE_LICENSE("GPL"); From c3ead16e7094076607d7c7c47aee280209d829f7 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 11 Jan 2013 09:05:42 -0800 Subject: [PATCH 22/55] Input: twl4030-vibra - switch to using managed resources This simplifies error handling and eliminates the need for implementing remove() method. Signed-off-by: Peter Ujfalusi Signed-off-by: Dmitry Torokhov --- drivers/input/misc/twl4030-vibra.c | 26 ++++---------------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/drivers/input/misc/twl4030-vibra.c b/drivers/input/misc/twl4030-vibra.c index 78eb6b30580a..e2e2a261e02a 100644 --- a/drivers/input/misc/twl4030-vibra.c +++ b/drivers/input/misc/twl4030-vibra.c @@ -219,7 +219,7 @@ static int twl4030_vibra_probe(struct platform_device *pdev) return -EINVAL; } - info = kzalloc(sizeof(*info), GFP_KERNEL); + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM; @@ -227,11 +227,10 @@ static int twl4030_vibra_probe(struct platform_device *pdev) info->coexist = twl4030_vibra_check_coexist(pdata, twl4030_core_node); INIT_WORK(&info->play_work, vibra_play_work); - info->input_dev = input_allocate_device(); + info->input_dev = devm_input_allocate_device(&pdev->dev); if (info->input_dev == NULL) { dev_err(&pdev->dev, "couldn't allocate input device\n"); - ret = -ENOMEM; - goto err_kzalloc; + return -ENOMEM; } input_set_drvdata(info->input_dev, info); @@ -246,7 +245,7 @@ static int twl4030_vibra_probe(struct platform_device *pdev) ret = input_ff_create_memless(info->input_dev, NULL, vibra_play); if (ret < 0) { dev_dbg(&pdev->dev, "couldn't register vibrator to FF\n"); - goto err_ialloc; + return ret; } ret = input_register_device(info->input_dev); @@ -262,28 +261,11 @@ static int twl4030_vibra_probe(struct platform_device *pdev) err_iff: input_ff_destroy(info->input_dev); -err_ialloc: - input_free_device(info->input_dev); -err_kzalloc: - kfree(info); return ret; } -static int twl4030_vibra_remove(struct platform_device *pdev) -{ - struct vibra_info *info = platform_get_drvdata(pdev); - - /* this also free ff-memless and calls close if needed */ - input_unregister_device(info->input_dev); - kfree(info); - platform_set_drvdata(pdev, NULL); - - return 0; -} - static struct platform_driver twl4030_vibra_driver = { .probe = twl4030_vibra_probe, - .remove = twl4030_vibra_remove, .driver = { .name = "twl4030-vibra", .owner = THIS_MODULE, From 4a31ba37b7ed39c9c7178da6e6df7bce63ffc56f Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 11 Jan 2013 09:05:47 -0800 Subject: [PATCH 23/55] Input: twl4030-vibra - Use system workqueue It is time to switch to system wq instead creating a queue for the driver. Signed-off-by: Peter Ujfalusi Signed-off-by: Dmitry Torokhov --- drivers/input/misc/twl4030-vibra.c | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/drivers/input/misc/twl4030-vibra.c b/drivers/input/misc/twl4030-vibra.c index e2e2a261e02a..68a5f33152a8 100644 --- a/drivers/input/misc/twl4030-vibra.c +++ b/drivers/input/misc/twl4030-vibra.c @@ -43,7 +43,6 @@ struct vibra_info { struct device *dev; struct input_dev *input_dev; - struct workqueue_struct *workqueue; struct work_struct play_work; bool enabled; @@ -143,19 +142,7 @@ static int vibra_play(struct input_dev *input, void *data, if (!info->speed) info->speed = effect->u.rumble.weak_magnitude >> 9; info->direction = effect->direction < EFFECT_DIR_180_DEG ? 0 : 1; - queue_work(info->workqueue, &info->play_work); - return 0; -} - -static int twl4030_vibra_open(struct input_dev *input) -{ - struct vibra_info *info = input_get_drvdata(input); - - info->workqueue = create_singlethread_workqueue("vibra"); - if (info->workqueue == NULL) { - dev_err(&input->dev, "couldn't create workqueue\n"); - return -ENOMEM; - } + schedule_work(&info->play_work); return 0; } @@ -164,9 +151,6 @@ static void twl4030_vibra_close(struct input_dev *input) struct vibra_info *info = input_get_drvdata(input); cancel_work_sync(&info->play_work); - INIT_WORK(&info->play_work, vibra_play_work); /* cleanup */ - destroy_workqueue(info->workqueue); - info->workqueue = NULL; if (info->enabled) vibra_disable(info); @@ -238,7 +222,6 @@ static int twl4030_vibra_probe(struct platform_device *pdev) info->input_dev->name = "twl4030:vibrator"; info->input_dev->id.version = 1; info->input_dev->dev.parent = pdev->dev.parent; - info->input_dev->open = twl4030_vibra_open; info->input_dev->close = twl4030_vibra_close; __set_bit(FF_RUMBLE, info->input_dev->ffbit); From be2d7e4233a4fe439125b825470020cef83407d5 Mon Sep 17 00:00:00 2001 From: Shawn Nematbakhsh Date: Mon, 31 Dec 2012 12:53:35 -0800 Subject: [PATCH 24/55] Input: atkbd - fix multi-byte scancode handling on reconnect On resume from suspend there is a possibility for multi-byte scancodes to be handled incorrectly. atkbd_reconnect disables the processing of scancodes in software by calling atkbd_disable, but the keyboard may still be active because no disconnect command was sent. Later, software handling is re-enabled. If a multi-byte scancode sent from the keyboard straddles the re-enable, only the latter byte(s) will be handled. In practice, this leads to cases where multi-byte break codes (ex. "e0 4d" - break code for right-arrow) are misread as make codes ("4d" - make code for numeric 6), leading to one or more unwanted, untyped characters being interpreted. The solution implemented here involves sending command f5 (reset disable) to the keyboard prior to disabling software handling of codes. Later, the command to re-enable the keyboard is sent only after we are prepared to handle scancodes. Signed-off-by: Shawn Nematbakhsh Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/atkbd.c | 72 ++++++++++++++++++++++++---------- 1 file changed, 51 insertions(+), 21 deletions(-) diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c index 33d0fcde441c..2626773ff29b 100644 --- a/drivers/input/keyboard/atkbd.c +++ b/drivers/input/keyboard/atkbd.c @@ -676,6 +676,39 @@ static inline void atkbd_disable(struct atkbd *atkbd) serio_continue_rx(atkbd->ps2dev.serio); } +static int atkbd_activate(struct atkbd *atkbd) +{ + struct ps2dev *ps2dev = &atkbd->ps2dev; + +/* + * Enable the keyboard to receive keystrokes. + */ + + if (ps2_command(ps2dev, NULL, ATKBD_CMD_ENABLE)) { + dev_err(&ps2dev->serio->dev, + "Failed to enable keyboard on %s\n", + ps2dev->serio->phys); + return -1; + } + + return 0; +} + +/* + * atkbd_deactivate() resets and disables the keyboard from sending + * keystrokes. + */ + +static void atkbd_deactivate(struct atkbd *atkbd) +{ + struct ps2dev *ps2dev = &atkbd->ps2dev; + + if (ps2_command(ps2dev, NULL, ATKBD_CMD_RESET_DIS)) + dev_err(&ps2dev->serio->dev, + "Failed to deactivate keyboard on %s\n", + ps2dev->serio->phys); +} + /* * atkbd_probe() probes for an AT keyboard on a serio port. */ @@ -731,6 +764,12 @@ static int atkbd_probe(struct atkbd *atkbd) return -1; } +/* + * Make sure nothing is coming from the keyboard and disturbs our + * internal state. + */ + atkbd_deactivate(atkbd); + return 0; } @@ -825,24 +864,6 @@ static int atkbd_reset_state(struct atkbd *atkbd) return 0; } -static int atkbd_activate(struct atkbd *atkbd) -{ - struct ps2dev *ps2dev = &atkbd->ps2dev; - -/* - * Enable the keyboard to receive keystrokes. - */ - - if (ps2_command(ps2dev, NULL, ATKBD_CMD_ENABLE)) { - dev_err(&ps2dev->serio->dev, - "Failed to enable keyboard on %s\n", - ps2dev->serio->phys); - return -1; - } - - return 0; -} - /* * atkbd_cleanup() restores the keyboard state so that BIOS is happy after a * reboot. @@ -1150,7 +1171,6 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv) atkbd->set = atkbd_select_set(atkbd, atkbd_set, atkbd_extra); atkbd_reset_state(atkbd); - atkbd_activate(atkbd); } else { atkbd->set = 2; @@ -1165,6 +1185,8 @@ static int atkbd_connect(struct serio *serio, struct serio_driver *drv) goto fail3; atkbd_enable(atkbd); + if (serio->write) + atkbd_activate(atkbd); err = input_register_device(atkbd->dev); if (err) @@ -1208,8 +1230,6 @@ static int atkbd_reconnect(struct serio *serio) if (atkbd->set != atkbd_select_set(atkbd, atkbd->set, atkbd->extra)) goto out; - atkbd_activate(atkbd); - /* * Restore LED state and repeat rate. While input core * will do this for us at resume time reconnect may happen @@ -1223,7 +1243,17 @@ static int atkbd_reconnect(struct serio *serio) } + /* + * Reset our state machine in case reconnect happened in the middle + * of multi-byte scancode. + */ + atkbd->xl_bit = 0; + atkbd->emul = 0; + atkbd_enable(atkbd); + if (atkbd->write) + atkbd_activate(atkbd); + retval = 0; out: From 1932c8a0035231ead2130201a69eca7f5d8e2462 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 14 Dec 2012 23:13:24 -0800 Subject: [PATCH 25/55] Input: walkera0701 - set up input device's parent This will place the joystick's input device into propoer place in sysfs hierarchy as long as th port has device assigned to it (i.e. it is not legacy port). Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/walkera0701.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/input/joystick/walkera0701.c b/drivers/input/joystick/walkera0701.c index f8f892b076e8..38386ce62a47 100644 --- a/drivers/input/joystick/walkera0701.c +++ b/drivers/input/joystick/walkera0701.c @@ -241,6 +241,7 @@ static int walkera0701_connect(struct walkera_dev *w, int parport) w->input_dev->id.vendor = 0x0001; w->input_dev->id.product = 0x0001; w->input_dev->id.version = 0x0100; + w->input_dev->dev.parent = w->parport->dev; w->input_dev->open = walkera0701_open; w->input_dev->close = walkera0701_close; From e0dba55eae4557461aba53ec38ad82372fff86d6 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 14 Dec 2012 23:18:21 -0800 Subject: [PATCH 26/55] Input: walkera0701 - switch to using pr_xxx() for messages Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/walkera0701.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/drivers/input/joystick/walkera0701.c b/drivers/input/joystick/walkera0701.c index 38386ce62a47..da657e63a223 100644 --- a/drivers/input/joystick/walkera0701.c +++ b/drivers/input/joystick/walkera0701.c @@ -12,7 +12,7 @@ * the Free Software Foundation. */ -/* #define WK0701_DEBUG */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define RESERVE 20000 #define SYNC_PULSE 1306000 @@ -67,6 +67,7 @@ static inline void walkera0701_parse_frame(struct walkera_dev *w) { int i; int val1, val2, val3, val4, val5, val6, val7, val8; + int magic, magic_bit; int crc1, crc2; for (crc1 = crc2 = i = 0; i < 10; i++) { @@ -102,17 +103,12 @@ static inline void walkera0701_parse_frame(struct walkera_dev *w) val8 = (w->buf[18] & 1) << 8 | (w->buf[19] << 4) | w->buf[20]; val8 *= (w->buf[18] & 2) - 1; /*sign */ -#ifdef WK0701_DEBUG - { - int magic, magic_bit; - magic = (w->buf[21] << 4) | w->buf[22]; - magic_bit = (w->buf[24] & 8) >> 3; - printk(KERN_DEBUG - "walkera0701: %4d %4d %4d %4d %4d %4d %4d %4d (magic %2x %d)\n", - val1, val2, val3, val4, val5, val6, val7, val8, magic, - magic_bit); - } -#endif + magic = (w->buf[21] << 4) | w->buf[22]; + magic_bit = (w->buf[24] & 8) >> 3; + pr_debug("%4d %4d %4d %4d %4d %4d %4d %4d (magic %2x %d)\n", + val1, val2, val3, val4, val5, val6, val7, val8, + magic, magic_bit); + input_report_abs(w->input_dev, ABS_X, val2); input_report_abs(w->input_dev, ABS_Y, val1); input_report_abs(w->input_dev, ABS_Z, val6); @@ -208,7 +204,8 @@ static int walkera0701_connect(struct walkera_dev *w, int parport) return -ENODEV; if (w->parport->irq == -1) { - printk(KERN_ERR "walkera0701: parport without interrupt\n"); + pr_err("parport %d does not have interrupt assigned\n", + parport); goto init_err; } From ea05ae071ed0f360c6002d7b5107e0dad724d511 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 16 Dec 2012 22:38:45 -0800 Subject: [PATCH 27/55] Input: walkera0701 - use proper error codes We have been using -EBUSY where we should have used -EIO or -ENOMEM, so let's fix that and also add some diagnostic messages. Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/walkera0701.c | 56 ++++++++++++++++++---------- 1 file changed, 36 insertions(+), 20 deletions(-) diff --git a/drivers/input/joystick/walkera0701.c b/drivers/input/joystick/walkera0701.c index da657e63a223..b86134c0b299 100644 --- a/drivers/input/joystick/walkera0701.c +++ b/drivers/input/joystick/walkera0701.c @@ -197,37 +197,51 @@ static void walkera0701_close(struct input_dev *dev) static int walkera0701_connect(struct walkera_dev *w, int parport) { - int err = -ENODEV; + int error; w->parport = parport_find_number(parport); - if (w->parport == NULL) + if (!w->parport) { + pr_err("parport %d does not exist\n", parport); return -ENODEV; + } if (w->parport->irq == -1) { pr_err("parport %d does not have interrupt assigned\n", parport); - goto init_err; + error = -EINVAL; + goto err_put_parport; } - err = -EBUSY; w->pardevice = parport_register_device(w->parport, "walkera0701", NULL, NULL, walkera0701_irq_handler, PARPORT_DEV_EXCL, w); - if (!w->pardevice) - goto init_err; + if (!w->pardevice) { + pr_err("failed to register parport device\n"); + error = -EIO; + goto err_put_parport; + } - if (parport_negotiate(w->pardevice->port, IEEE1284_MODE_COMPAT)) - goto init_err1; + if (parport_negotiate(w->pardevice->port, IEEE1284_MODE_COMPAT)) { + pr_err("failed to negotiate parport mode\n"); + error = -EIO; + goto err_unregister_device; + } - if (parport_claim(w->pardevice)) - goto init_err1; + if (parport_claim(w->pardevice)) { + pr_err("failed to claim parport\n"); + error = -EBUSY; + goto err_unregister_device; + } hrtimer_init(&w->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); w->timer.function = timer_handler; w->input_dev = input_allocate_device(); - if (!w->input_dev) - goto init_err2; + if (!w->input_dev) { + pr_err("failed to allocate input device\n"); + error = -ENOMEM; + goto err_release_parport; + } input_set_drvdata(w->input_dev, w); w->input_dev->name = "Walkera WK-0701 TX"; @@ -252,21 +266,23 @@ static int walkera0701_connect(struct walkera_dev *w, int parport) input_set_abs_params(w->input_dev, ABS_RUDDER, -512, 512, 0, 0); input_set_abs_params(w->input_dev, ABS_MISC, -512, 512, 0, 0); - err = input_register_device(w->input_dev); - if (err) - goto init_err3; + error = input_register_device(w->input_dev); + if (error) { + pr_err("failed to register input device\n"); + goto err_free_input_dev; + } return 0; - init_err3: +err_free_input_dev: input_free_device(w->input_dev); - init_err2: +err_release_parport: parport_release(w->pardevice); - init_err1: +err_unregister_device: parport_unregister_device(w->pardevice); - init_err: +err_put_parport: parport_put_port(w->parport); - return err; + return error; } static void walkera0701_disconnect(struct walkera_dev *w) From cb696e7cf261e2af323d49db8839dd1cc34709dd Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sun, 16 Dec 2012 22:59:12 -0800 Subject: [PATCH 28/55] Input: walkera0701 - claim parport when opening the device Postpone claiming the port until the device is opened, instead of doing that when the driver is loaded. Signed-off-by: Dmitry Torokhov --- drivers/input/joystick/walkera0701.c | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/drivers/input/joystick/walkera0701.c b/drivers/input/joystick/walkera0701.c index b86134c0b299..b76ac580703c 100644 --- a/drivers/input/joystick/walkera0701.c +++ b/drivers/input/joystick/walkera0701.c @@ -183,6 +183,9 @@ static int walkera0701_open(struct input_dev *dev) { struct walkera_dev *w = input_get_drvdata(dev); + if (parport_claim(w->pardevice)) + return -EBUSY; + parport_enable_irq(w->parport); return 0; } @@ -193,6 +196,8 @@ static void walkera0701_close(struct input_dev *dev) parport_disable_irq(w->parport); hrtimer_cancel(&w->timer); + + parport_release(w->pardevice); } static int walkera0701_connect(struct walkera_dev *w, int parport) @@ -227,12 +232,6 @@ static int walkera0701_connect(struct walkera_dev *w, int parport) goto err_unregister_device; } - if (parport_claim(w->pardevice)) { - pr_err("failed to claim parport\n"); - error = -EBUSY; - goto err_unregister_device; - } - hrtimer_init(&w->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); w->timer.function = timer_handler; @@ -240,7 +239,7 @@ static int walkera0701_connect(struct walkera_dev *w, int parport) if (!w->input_dev) { pr_err("failed to allocate input device\n"); error = -ENOMEM; - goto err_release_parport; + goto err_unregister_device; } input_set_drvdata(w->input_dev, w); @@ -276,8 +275,6 @@ static int walkera0701_connect(struct walkera_dev *w, int parport) err_free_input_dev: input_free_device(w->input_dev); -err_release_parport: - parport_release(w->pardevice); err_unregister_device: parport_unregister_device(w->pardevice); err_put_parport: @@ -288,7 +285,6 @@ static int walkera0701_connect(struct walkera_dev *w, int parport) static void walkera0701_disconnect(struct walkera_dev *w) { input_unregister_device(w->input_dev); - parport_release(w->pardevice); parport_unregister_device(w->pardevice); parport_put_port(w->parport); } From 6f2ac009f29bcbd468a7a2017912dd090abd1348 Mon Sep 17 00:00:00 2001 From: Brian Swetland Date: Wed, 23 Jan 2013 19:45:00 -0800 Subject: [PATCH 29/55] Input: goldfish - virtual input event driver This device is a direct pipe from "hardware" to the input event subsystem, allowing us to avoid having to route "keypad" style events through an AT keyboard driver (gross!). As with the other submissions this driver is cross architecture. Signed-off-by: Mike A. Chan [Tided up to work on x86] Signed-off-by: Sheng Yang Signed-off-by: Yunhong Jiang Signed-off-by: Xiaohui Xin Signed-off-by: Jun Nakajima Signed-off-by: Bruce Beare [Ported to 3.4] Signed-off-by: Tom Keel [Cleaned up for 3.7 and submission] Signed-off-by: Alan Cox Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/Kconfig | 10 ++ drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/goldfish_events.c | 194 +++++++++++++++++++++++ 3 files changed, 205 insertions(+) create mode 100644 drivers/input/keyboard/goldfish_events.c diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 48309641b1b2..95b6d0335e8f 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -479,6 +479,16 @@ config KEYBOARD_SAMSUNG To compile this driver as a module, choose M here: the module will be called samsung-keypad. +config KEYBOARD_GOLDFISH_EVENTS + depends on GOLDFISH + tristate "Generic Input Event device for Goldfish" + help + Say Y here to get an input event device for the Goldfish virtual + device emulator. + + To compile this driver as a module, choose M here: the + module will be called goldfish-events. + config KEYBOARD_STOWAWAY tristate "Stowaway keyboard" select SERIO diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 44e76002f54b..49b16453d00e 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o obj-$(CONFIG_KEYBOARD_DAVINCI) += davinci_keyscan.o obj-$(CONFIG_KEYBOARD_EP93XX) += ep93xx_keypad.o +obj-$(CONFIG_KEYBOARD_GOLDFISH_EVENTS) += goldfish_events.o obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o obj-$(CONFIG_KEYBOARD_GPIO_POLLED) += gpio_keys_polled.o obj-$(CONFIG_KEYBOARD_TCA6416) += tca6416-keypad.o diff --git a/drivers/input/keyboard/goldfish_events.c b/drivers/input/keyboard/goldfish_events.c new file mode 100644 index 000000000000..9f60a2ec88db --- /dev/null +++ b/drivers/input/keyboard/goldfish_events.c @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2007 Google, Inc. + * Copyright (C) 2012 Intel, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum { + REG_READ = 0x00, + REG_SET_PAGE = 0x00, + REG_LEN = 0x04, + REG_DATA = 0x08, + + PAGE_NAME = 0x00000, + PAGE_EVBITS = 0x10000, + PAGE_ABSDATA = 0x20000 | EV_ABS, +}; + +struct event_dev { + struct input_dev *input; + int irq; + void __iomem *addr; + char name[0]; +}; + +static irqreturn_t events_interrupt(int irq, void *dev_id) +{ + struct event_dev *edev = dev_id; + unsigned type, code, value; + + type = __raw_readl(edev->addr + REG_READ); + code = __raw_readl(edev->addr + REG_READ); + value = __raw_readl(edev->addr + REG_READ); + + input_event(edev->input, type, code, value); + input_sync(edev->input); + return IRQ_HANDLED; +} + +static void events_import_bits(struct event_dev *edev, + unsigned long bits[], unsigned type, size_t count) +{ + void __iomem *addr = edev->addr; + int i, j; + size_t size; + uint8_t val; + + __raw_writel(PAGE_EVBITS | type, addr + REG_SET_PAGE); + + size = __raw_readl(addr + REG_LEN) * 8; + if (size < count) + count = size; + + addr += REG_DATA; + for (i = 0; i < count; i += 8) { + val = __raw_readb(addr++); + for (j = 0; j < 8; j++) + if (val & 1 << j) + set_bit(i + j, bits); + } +} + +static void events_import_abs_params(struct event_dev *edev) +{ + struct input_dev *input_dev = edev->input; + void __iomem *addr = edev->addr; + u32 val[4]; + int count; + int i, j; + + __raw_writel(PAGE_ABSDATA, addr + REG_SET_PAGE); + + count = __raw_readl(addr + REG_LEN) / sizeof(val); + if (count > ABS_MAX) + count = ABS_MAX; + + for (i = 0; i < count; i++) { + if (!test_bit(i, input_dev->absbit)) + continue; + + for (j = 0; j < ARRAY_SIZE(val); j++) { + int offset = (i * ARRAY_SIZE(val) + j) * sizeof(u32); + val[j] = __raw_readl(edev->addr + REG_DATA + offset); + } + + input_set_abs_params(input_dev, i, + val[0], val[1], val[2], val[3]); + } +} + +static int events_probe(struct platform_device *pdev) +{ + struct input_dev *input_dev; + struct event_dev *edev; + struct resource *res; + unsigned keymapnamelen; + void __iomem *addr; + int irq; + int i; + int error; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return -EINVAL; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + + addr = devm_ioremap(&pdev->dev, res->start, 4096); + if (!addr) + return -ENOMEM; + + __raw_writel(PAGE_NAME, addr + REG_SET_PAGE); + keymapnamelen = __raw_readl(addr + REG_LEN); + + edev = devm_kzalloc(&pdev->dev, + sizeof(struct event_dev) + keymapnamelen + 1, + GFP_KERNEL); + if (!edev) + return -ENOMEM; + + input_dev = devm_input_allocate_device(&pdev->dev); + if (!input_dev) + return -ENOMEM; + + edev->input = input_dev; + edev->addr = addr; + edev->irq = irq; + + for (i = 0; i < keymapnamelen; i++) + edev->name[i] = __raw_readb(edev->addr + REG_DATA + i); + + pr_debug("events_probe() keymap=%s\n", edev->name); + + input_dev->name = edev->name; + input_dev->id.bustype = BUS_HOST; + + events_import_bits(edev, input_dev->evbit, EV_SYN, EV_MAX); + events_import_bits(edev, input_dev->keybit, EV_KEY, KEY_MAX); + events_import_bits(edev, input_dev->relbit, EV_REL, REL_MAX); + events_import_bits(edev, input_dev->absbit, EV_ABS, ABS_MAX); + events_import_bits(edev, input_dev->mscbit, EV_MSC, MSC_MAX); + events_import_bits(edev, input_dev->ledbit, EV_LED, LED_MAX); + events_import_bits(edev, input_dev->sndbit, EV_SND, SND_MAX); + events_import_bits(edev, input_dev->ffbit, EV_FF, FF_MAX); + events_import_bits(edev, input_dev->swbit, EV_SW, SW_MAX); + + events_import_abs_params(edev); + + error = devm_request_irq(&pdev->dev, edev->irq, events_interrupt, 0, + "goldfish-events-keypad", edev); + if (error) + return error; + + error = input_register_device(input_dev); + if (error) + return error; + + return 0; +} + +static struct platform_driver events_driver = { + .probe = events_probe, + .driver = { + .owner = THIS_MODULE, + .name = "goldfish_events", + }, +}; + +module_platform_driver(events_driver); + +MODULE_AUTHOR("Brian Swetland"); +MODULE_DESCRIPTION("Goldfish Event Device"); +MODULE_LICENSE("GPL"); From b2ebcc1be96ee6dd5481697b421447e5a3f61d76 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 25 Jan 2013 00:03:50 -0800 Subject: [PATCH 30/55] Input: twl6040-vibra - code cleanup in probe with devm_* conversion Convert the probe to use devm_*. At the same time reorder the calls so we will register the input device as the last step when the driver is loaded. Signed-off-by: Peter Ujfalusi Signed-off-by: Dmitry Torokhov --- drivers/input/misc/twl6040-vibra.c | 125 +++++++++++++---------------- 1 file changed, 58 insertions(+), 67 deletions(-) diff --git a/drivers/input/misc/twl6040-vibra.c b/drivers/input/misc/twl6040-vibra.c index 71a28ee699f3..d1e31b8eb121 100644 --- a/drivers/input/misc/twl6040-vibra.c +++ b/drivers/input/misc/twl6040-vibra.c @@ -275,7 +275,7 @@ static int twl6040_vibra_probe(struct platform_device *pdev) return -EINVAL; } - info = kzalloc(sizeof(*info), GFP_KERNEL); + info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL); if (!info) { dev_err(&pdev->dev, "couldn't allocate memory\n"); return -ENOMEM; @@ -309,24 +309,71 @@ static int twl6040_vibra_probe(struct platform_device *pdev) if ((!info->vibldrv_res && !info->viblmotor_res) || (!info->vibrdrv_res && !info->vibrmotor_res)) { dev_err(info->dev, "invalid vibra driver/motor resistance\n"); - ret = -EINVAL; - goto err_kzalloc; + return -EINVAL; } info->irq = platform_get_irq(pdev, 0); if (info->irq < 0) { dev_err(info->dev, "invalid irq\n"); - ret = -EINVAL; - goto err_kzalloc; + return -EINVAL; } mutex_init(&info->mutex); + ret = devm_request_threaded_irq(&pdev->dev, info->irq, NULL, + twl6040_vib_irq_handler, 0, + "twl6040_irq_vib", info); + if (ret) { + dev_err(info->dev, "VIB IRQ request failed: %d\n", ret); + return ret; + } + + info->supplies[0].supply = "vddvibl"; + info->supplies[1].supply = "vddvibr"; + /* + * When booted with Device tree the regulators are attached to the + * parent device (twl6040 MFD core) + */ + ret = regulator_bulk_get(pdata ? info->dev : twl6040_core_dev, + ARRAY_SIZE(info->supplies), info->supplies); + if (ret) { + dev_err(info->dev, "couldn't get regulators %d\n", ret); + return ret; + } + + if (vddvibl_uV) { + ret = regulator_set_voltage(info->supplies[0].consumer, + vddvibl_uV, vddvibl_uV); + if (ret) { + dev_err(info->dev, "failed to set VDDVIBL volt %d\n", + ret); + goto err_regulator; + } + } + + if (vddvibr_uV) { + ret = regulator_set_voltage(info->supplies[1].consumer, + vddvibr_uV, vddvibr_uV); + if (ret) { + dev_err(info->dev, "failed to set VDDVIBR volt %d\n", + ret); + goto err_regulator; + } + } + + info->workqueue = alloc_workqueue("twl6040-vibra", 0, 0); + if (info->workqueue == NULL) { + dev_err(info->dev, "couldn't create workqueue\n"); + ret = -ENOMEM; + goto err_regulator; + } + INIT_WORK(&info->play_work, vibra_play_work); + info->input_dev = input_allocate_device(); if (info->input_dev == NULL) { dev_err(info->dev, "couldn't allocate input device\n"); ret = -ENOMEM; - goto err_kzalloc; + goto err_wq; } input_set_drvdata(info->input_dev, info); @@ -351,70 +398,16 @@ static int twl6040_vibra_probe(struct platform_device *pdev) platform_set_drvdata(pdev, info); - ret = request_threaded_irq(info->irq, NULL, twl6040_vib_irq_handler, 0, - "twl6040_irq_vib", info); - if (ret) { - dev_err(info->dev, "VIB IRQ request failed: %d\n", ret); - goto err_irq; - } - - info->supplies[0].supply = "vddvibl"; - info->supplies[1].supply = "vddvibr"; - /* - * When booted with Device tree the regulators are attached to the - * parent device (twl6040 MFD core) - */ - ret = regulator_bulk_get(pdata ? info->dev : twl6040_core_dev, - ARRAY_SIZE(info->supplies), info->supplies); - if (ret) { - dev_err(info->dev, "couldn't get regulators %d\n", ret); - goto err_regulator; - } - - if (vddvibl_uV) { - ret = regulator_set_voltage(info->supplies[0].consumer, - vddvibl_uV, vddvibl_uV); - if (ret) { - dev_err(info->dev, "failed to set VDDVIBL volt %d\n", - ret); - goto err_voltage; - } - } - - if (vddvibr_uV) { - ret = regulator_set_voltage(info->supplies[1].consumer, - vddvibr_uV, vddvibr_uV); - if (ret) { - dev_err(info->dev, "failed to set VDDVIBR volt %d\n", - ret); - goto err_voltage; - } - } - - info->workqueue = alloc_workqueue("twl6040-vibra", 0, 0); - if (info->workqueue == NULL) { - dev_err(info->dev, "couldn't create workqueue\n"); - ret = -ENOMEM; - goto err_voltage; - } - INIT_WORK(&info->play_work, vibra_play_work); - return 0; -err_voltage: - regulator_bulk_free(ARRAY_SIZE(info->supplies), info->supplies); -err_regulator: - free_irq(info->irq, info); -err_irq: - input_unregister_device(info->input_dev); - info->input_dev = NULL; err_iff: - if (info->input_dev) - input_ff_destroy(info->input_dev); + input_ff_destroy(info->input_dev); err_ialloc: input_free_device(info->input_dev); -err_kzalloc: - kfree(info); +err_wq: + destroy_workqueue(info->workqueue); +err_regulator: + regulator_bulk_free(ARRAY_SIZE(info->supplies), info->supplies); return ret; } @@ -423,10 +416,8 @@ static int twl6040_vibra_remove(struct platform_device *pdev) struct vibra_info *info = platform_get_drvdata(pdev); input_unregister_device(info->input_dev); - free_irq(info->irq, info); regulator_bulk_free(ARRAY_SIZE(info->supplies), info->supplies); destroy_workqueue(info->workqueue); - kfree(info); return 0; } From 21fb9f0d5e91152066c9be801b44a7c592fd3fa2 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Fri, 25 Jan 2013 00:03:54 -0800 Subject: [PATCH 31/55] Input: twl6040-vibra - use system workqueue It is time to switch to system wq instead creating a queue for the driver. Signed-off-by: Peter Ujfalusi Signed-off-by: Dmitry Torokhov --- drivers/input/misc/twl6040-vibra.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/drivers/input/misc/twl6040-vibra.c b/drivers/input/misc/twl6040-vibra.c index d1e31b8eb121..0c2dfc8e9691 100644 --- a/drivers/input/misc/twl6040-vibra.c +++ b/drivers/input/misc/twl6040-vibra.c @@ -361,19 +361,13 @@ static int twl6040_vibra_probe(struct platform_device *pdev) } } - info->workqueue = alloc_workqueue("twl6040-vibra", 0, 0); - if (info->workqueue == NULL) { - dev_err(info->dev, "couldn't create workqueue\n"); - ret = -ENOMEM; - goto err_regulator; - } INIT_WORK(&info->play_work, vibra_play_work); info->input_dev = input_allocate_device(); if (info->input_dev == NULL) { dev_err(info->dev, "couldn't allocate input device\n"); ret = -ENOMEM; - goto err_wq; + goto err_regulator; } input_set_drvdata(info->input_dev, info); @@ -404,8 +398,6 @@ static int twl6040_vibra_probe(struct platform_device *pdev) input_ff_destroy(info->input_dev); err_ialloc: input_free_device(info->input_dev); -err_wq: - destroy_workqueue(info->workqueue); err_regulator: regulator_bulk_free(ARRAY_SIZE(info->supplies), info->supplies); return ret; @@ -417,7 +409,6 @@ static int twl6040_vibra_remove(struct platform_device *pdev) input_unregister_device(info->input_dev); regulator_bulk_free(ARRAY_SIZE(info->supplies), info->supplies); - destroy_workqueue(info->workqueue); return 0; } From d722260d233949ec531c684248a88cb163608851 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 13 Feb 2013 13:29:01 -0800 Subject: [PATCH 32/55] Input: add couple of missing GENERIC_HARDIRQS dependencies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When removing the !S390 dependency from drivers/input/Kconfig a couple of drivers don't compile because they have a dependency on GENERIC_HARDIRQS. So add the missing dependencies. Fixes e.g. this one: drivers/input/keyboard/lm8323.c: In function ‘lm8323_suspend’: drivers/input/keyboard/lm8323.c:801:2: error: implicit declaration of function ‘irq_set_irq_wake’ [-Werror=implicit-function-declaration] Signed-off-by: Heiko Carstens Signed-off-by: Dmitry Torokhov --- drivers/input/Kconfig | 2 +- drivers/input/keyboard/Kconfig | 4 ++-- drivers/input/serio/Kconfig | 1 + drivers/input/touchscreen/Kconfig | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 55f7e57d4e42..38b523a1ece0 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -3,7 +3,7 @@ # menu "Input device support" - depends on !S390 && !UML + depends on !UML config INPUT tristate "Generic input layer (needed for keyboard, mouse, ...)" if EXPERT diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 95b6d0335e8f..72377737eec8 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -224,7 +224,7 @@ config KEYBOARD_TCA6416 config KEYBOARD_TCA8418 tristate "TCA8418 Keypad Support" - depends on I2C + depends on I2C && GENERIC_HARDIRQS select INPUT_MATRIXKMAP help This driver implements basic keypad functionality @@ -303,7 +303,7 @@ config KEYBOARD_HP7XX config KEYBOARD_LM8323 tristate "LM8323 keypad chip" - depends on I2C + depends on I2C && GENERIC_HARDIRQS depends on LEDS_CLASS help If you say yes here you get support for the National Semiconductor diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig index 4a4e182c33e7..560c243bfcaf 100644 --- a/drivers/input/serio/Kconfig +++ b/drivers/input/serio/Kconfig @@ -236,6 +236,7 @@ config SERIO_PS2MULT config SERIO_ARC_PS2 tristate "ARC PS/2 support" + depends on GENERIC_HARDIRQS help Say Y here if you have an ARC FPGA platform with a PS/2 controller in it. diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index b93b5988ace8..9a647ee1136a 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -359,7 +359,7 @@ config TOUCHSCREEN_MCS5000 config TOUCHSCREEN_MMS114 tristate "MELFAS MMS114 touchscreen" - depends on I2C + depends on I2C && GENERIC_HARDIRQS help Say Y here if you have the MELFAS MMS114 touchscreen controller chip in your system. From 48064bdcd6d1c27ad0a35103eb8cee2d087940d9 Mon Sep 17 00:00:00 2001 From: Daniel Kurtz Date: Wed, 13 Feb 2013 13:53:07 -0800 Subject: [PATCH 33/55] Input: synaptics - fix 1->3 contact transition reporting Investigating the following gesture highlighted two slight implementation errors with choosing which slots to report in which slot when multiple contacts are present: Action SGM AGM (MTB slot:Contact) 1. Touch contact 0 (0:0) 2. Touch contact 1 (0:0, 1:1) 3. Lift contact 0 (1:1) 4. Touch contacts 2,3 (0:2, 1:3) In step 4, slot 1 was not being cleared first, which means the same tracking ID was being used for reporting both the old contact 1 and the new contact 3. This could result in "drumroll", where the old contact 1 would appear to suddenly jump to new finger 3 position. Similarly, if contacts 2 & 3 are not detected at the same sample, step 4 is split into two: Action SGM AGM (MTB slot:contact) 1. Touch contact 0 (0:0) 2. Touch contact 1 (0:0, 1:1) 3. Lift contact 0 (1:1) 4. Touch contact 2 (0:2, 1:1) 5. Touch contact 3 (0:2, 1:3) In this case, there was also a bug. In step 4, when contact 1 moves from SGM to AGM and contact 2 is first reported in SGM, slot 0 was actually empty. So slot 0 can be used to report the new SGM (contact 0), immediately. Since it was empty, contact 2 in slot 0 will get a new tracking ID. Signed-off-by: Daniel Kurtz Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/synaptics.c | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index 12d12ca3fee0..c3d1c86ac591 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -722,11 +722,13 @@ static void synaptics_report_mt_data(struct psmouse *psmouse, default: /* * If the finger slot contained in SGM is valid, and either - * hasn't changed, or is new, then report SGM in MTB slot 0. + * hasn't changed, or is new, or the old SGM has now moved to + * AGM, then report SGM in MTB slot 0. * Otherwise, empty MTB slot 0. */ if (mt_state->sgm != -1 && - (mt_state->sgm == old->sgm || old->sgm == -1)) + (mt_state->sgm == old->sgm || + old->sgm == -1 || mt_state->agm == old->sgm)) synaptics_report_slot(dev, 0, sgm); else synaptics_report_slot(dev, 0, NULL); @@ -735,9 +737,31 @@ static void synaptics_report_mt_data(struct psmouse *psmouse, * If the finger slot contained in AGM is valid, and either * hasn't changed, or is new, then report AGM in MTB slot 1. * Otherwise, empty MTB slot 1. + * + * However, in the case where the AGM is new, make sure that + * that it is either the same as the old SGM, or there was no + * SGM. + * + * Otherwise, if the SGM was just 1, and the new AGM is 2, then + * the new AGM will keep the old SGM's tracking ID, which can + * cause apparent drumroll. This happens if in the following + * valid finger sequence: + * + * Action SGM AGM (MTB slot:Contact) + * 1. Touch contact 0 (0:0) + * 2. Touch contact 1 (0:0, 1:1) + * 3. Lift contact 0 (1:1) + * 4. Touch contacts 2,3 (0:2, 1:3) + * + * In step 4, contact 3, in AGM must not be given the same + * tracking ID as contact 1 had in step 3. To avoid this, + * the first agm with contact 3 is dropped and slot 1 is + * invalidated (tracking ID = -1). */ if (mt_state->agm != -1 && - (mt_state->agm == old->agm || old->agm == -1)) + (mt_state->agm == old->agm || + (old->agm == -1 && + (old->sgm == -1 || mt_state->agm == old->sgm)))) synaptics_report_slot(dev, 1, agm); else synaptics_report_slot(dev, 1, NULL); From 6ddaf744c9f6dd08aaec05bc9536173317b913a3 Mon Sep 17 00:00:00 2001 From: Benson Leung Date: Wed, 13 Feb 2013 13:56:03 -0800 Subject: [PATCH 34/55] Input: cyapa - add support for smbus protocol This patch adds support for the Cypress APA Smbus Trackpad type, which uses a modified register map that fits within the limitations of the smbus protocol. Devices that use this protocol include: CYTRA-116001-00 - Samsung Series 5 550 Chromebook trackpad CYTRA-103002-00 - Acer C7 Chromebook trackpad CYTRA-101003-00 - HP Pavilion 14 Chromebook trackpad Signed-off-by: Dudley Du Signed-off-by: Benson Leung Reviewed-by: Daniel Kurtz Reviewed-by: Henrik Rydberg Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/cyapa.c | 179 +++++++++++++++++++++++++++++++++++- 1 file changed, 174 insertions(+), 5 deletions(-) diff --git a/drivers/input/mouse/cyapa.c b/drivers/input/mouse/cyapa.c index 1a14688329be..b409c3d7d4fb 100644 --- a/drivers/input/mouse/cyapa.c +++ b/drivers/input/mouse/cyapa.c @@ -208,6 +208,7 @@ struct cyapa { char phys[32]; /* device physical location */ int irq; bool irq_wake; /* irq wake is enabled */ + bool smbus; /* read from query data region. */ char product_id[16]; @@ -229,6 +230,60 @@ struct cyapa_cmd_len { u8 len; }; +#define CYAPA_ADAPTER_FUNC_NONE 0 +#define CYAPA_ADAPTER_FUNC_I2C 1 +#define CYAPA_ADAPTER_FUNC_SMBUS 2 +#define CYAPA_ADAPTER_FUNC_BOTH 3 + +/* + * macros for SMBus communication + */ +#define SMBUS_READ 0x01 +#define SMBUS_WRITE 0x00 +#define SMBUS_ENCODE_IDX(cmd, idx) ((cmd) | (((idx) & 0x03) << 1)) +#define SMBUS_ENCODE_RW(cmd, rw) ((cmd) | ((rw) & 0x01)) +#define SMBUS_BYTE_BLOCK_CMD_MASK 0x80 +#define SMBUS_GROUP_BLOCK_CMD_MASK 0x40 + + /* for byte read/write command */ +#define CMD_RESET 0 +#define CMD_POWER_MODE 1 +#define CMD_DEV_STATUS 2 +#define SMBUS_BYTE_CMD(cmd) (((cmd) & 0x3f) << 1) +#define CYAPA_SMBUS_RESET SMBUS_BYTE_CMD(CMD_RESET) +#define CYAPA_SMBUS_POWER_MODE SMBUS_BYTE_CMD(CMD_POWER_MODE) +#define CYAPA_SMBUS_DEV_STATUS SMBUS_BYTE_CMD(CMD_DEV_STATUS) + + /* for group registers read/write command */ +#define REG_GROUP_DATA 0 +#define REG_GROUP_CMD 2 +#define REG_GROUP_QUERY 3 +#define SMBUS_GROUP_CMD(grp) (0x80 | (((grp) & 0x07) << 3)) +#define CYAPA_SMBUS_GROUP_DATA SMBUS_GROUP_CMD(REG_GROUP_DATA) +#define CYAPA_SMBUS_GROUP_CMD SMBUS_GROUP_CMD(REG_GROUP_CMD) +#define CYAPA_SMBUS_GROUP_QUERY SMBUS_GROUP_CMD(REG_GROUP_QUERY) + + /* for register block read/write command */ +#define CMD_BL_STATUS 0 +#define CMD_BL_HEAD 1 +#define CMD_BL_CMD 2 +#define CMD_BL_DATA 3 +#define CMD_BL_ALL 4 +#define CMD_BLK_PRODUCT_ID 5 +#define CMD_BLK_HEAD 6 +#define SMBUS_BLOCK_CMD(cmd) (0xc0 | (((cmd) & 0x1f) << 1)) + +/* register block read/write command in bootloader mode */ +#define CYAPA_SMBUS_BL_STATUS SMBUS_BLOCK_CMD(CMD_BL_STATUS) +#define CYAPA_SMBUS_BL_HEAD SMBUS_BLOCK_CMD(CMD_BL_HEAD) +#define CYAPA_SMBUS_BL_CMD SMBUS_BLOCK_CMD(CMD_BL_CMD) +#define CYAPA_SMBUS_BL_DATA SMBUS_BLOCK_CMD(CMD_BL_DATA) +#define CYAPA_SMBUS_BL_ALL SMBUS_BLOCK_CMD(CMD_BL_ALL) + +/* register block read/write command in operational mode */ +#define CYAPA_SMBUS_BLK_PRODUCT_ID SMBUS_BLOCK_CMD(CMD_BLK_PRODUCT_ID) +#define CYAPA_SMBUS_BLK_HEAD SMBUS_BLOCK_CMD(CMD_BLK_HEAD) + static const struct cyapa_cmd_len cyapa_i2c_cmds[] = { { CYAPA_OFFSET_SOFT_RESET, 1 }, { REG_OFFSET_COMMAND_BASE + 1, 1 }, @@ -245,6 +300,22 @@ static const struct cyapa_cmd_len cyapa_i2c_cmds[] = { { REG_OFFSET_DATA_BASE, 32 } }; +static const struct cyapa_cmd_len cyapa_smbus_cmds[] = { + { CYAPA_SMBUS_RESET, 1 }, + { CYAPA_SMBUS_POWER_MODE, 1 }, + { CYAPA_SMBUS_DEV_STATUS, 1 }, + { CYAPA_SMBUS_GROUP_DATA, sizeof(struct cyapa_reg_data) }, + { CYAPA_SMBUS_GROUP_CMD, 2 }, + { CYAPA_SMBUS_GROUP_QUERY, QUERY_DATA_SIZE }, + { CYAPA_SMBUS_BL_STATUS, 3 }, + { CYAPA_SMBUS_BL_HEAD, 16 }, + { CYAPA_SMBUS_BL_CMD, 16 }, + { CYAPA_SMBUS_BL_DATA, 16 }, + { CYAPA_SMBUS_BL_ALL, 32 }, + { CYAPA_SMBUS_BLK_PRODUCT_ID, PRODUCT_ID_SIZE }, + { CYAPA_SMBUS_BLK_HEAD, 16 }, +}; + static ssize_t cyapa_i2c_reg_read_block(struct cyapa *cyapa, u8 reg, size_t len, u8 *values) { @@ -257,26 +328,92 @@ static ssize_t cyapa_i2c_reg_write_block(struct cyapa *cyapa, u8 reg, return i2c_smbus_write_i2c_block_data(cyapa->client, reg, len, values); } +/* + * cyapa_smbus_read_block - perform smbus block read command + * @cyapa - private data structure of the driver + * @cmd - the properly encoded smbus command + * @len - expected length of smbus command result + * @values - buffer to store smbus command result + * + * Returns negative errno, else the number of bytes written. + * + * Note: + * In trackpad device, the memory block allocated for I2C register map + * is 256 bytes, so the max read block for I2C bus is 256 bytes. + */ +static ssize_t cyapa_smbus_read_block(struct cyapa *cyapa, u8 cmd, size_t len, + u8 *values) +{ + ssize_t ret; + u8 index; + u8 smbus_cmd; + u8 *buf; + struct i2c_client *client = cyapa->client; + + if (!(SMBUS_BYTE_BLOCK_CMD_MASK & cmd)) + return -EINVAL; + + if (SMBUS_GROUP_BLOCK_CMD_MASK & cmd) { + /* read specific block registers command. */ + smbus_cmd = SMBUS_ENCODE_RW(cmd, SMBUS_READ); + ret = i2c_smbus_read_block_data(client, smbus_cmd, values); + goto out; + } + + ret = 0; + for (index = 0; index * I2C_SMBUS_BLOCK_MAX < len; index++) { + smbus_cmd = SMBUS_ENCODE_IDX(cmd, index); + smbus_cmd = SMBUS_ENCODE_RW(smbus_cmd, SMBUS_READ); + buf = values + I2C_SMBUS_BLOCK_MAX * index; + ret = i2c_smbus_read_block_data(client, smbus_cmd, buf); + if (ret < 0) + goto out; + } + +out: + return ret > 0 ? len : ret; +} + static s32 cyapa_read_byte(struct cyapa *cyapa, u8 cmd_idx) { - u8 cmd = cyapa_i2c_cmds[cmd_idx].cmd; + u8 cmd; + if (cyapa->smbus) { + cmd = cyapa_smbus_cmds[cmd_idx].cmd; + cmd = SMBUS_ENCODE_RW(cmd, SMBUS_READ); + } else { + cmd = cyapa_i2c_cmds[cmd_idx].cmd; + } return i2c_smbus_read_byte_data(cyapa->client, cmd); } static s32 cyapa_write_byte(struct cyapa *cyapa, u8 cmd_idx, u8 value) { - u8 cmd = cyapa_i2c_cmds[cmd_idx].cmd; + u8 cmd; + if (cyapa->smbus) { + cmd = cyapa_smbus_cmds[cmd_idx].cmd; + cmd = SMBUS_ENCODE_RW(cmd, SMBUS_WRITE); + } else { + cmd = cyapa_i2c_cmds[cmd_idx].cmd; + } return i2c_smbus_write_byte_data(cyapa->client, cmd, value); } static ssize_t cyapa_read_block(struct cyapa *cyapa, u8 cmd_idx, u8 *values) { - u8 cmd = cyapa_i2c_cmds[cmd_idx].cmd; - size_t len = cyapa_i2c_cmds[cmd_idx].len; + u8 cmd; + size_t len; - return cyapa_i2c_reg_read_block(cyapa, cmd, len, values); + if (cyapa->smbus) { + cmd = cyapa_smbus_cmds[cmd_idx].cmd; + len = cyapa_smbus_cmds[cmd_idx].len; + return cyapa_smbus_read_block(cyapa, cmd, len, values); + } else { + cmd = cyapa_i2c_cmds[cmd_idx].cmd; + len = cyapa_i2c_cmds[cmd_idx].len; + return cyapa_i2c_reg_read_block(cyapa, cmd, len, values); + } } /* @@ -298,6 +435,15 @@ static int cyapa_get_state(struct cyapa *cyapa) */ ret = cyapa_i2c_reg_read_block(cyapa, BL_HEAD_OFFSET, BL_STATUS_SIZE, status); + + /* + * On smbus systems in OP mode, the i2c_reg_read will fail with + * -ETIMEDOUT. In this case, try again using the smbus equivalent + * command. This should return a BL_HEAD indicating CYAPA_STATE_OP. + */ + if (cyapa->smbus && (ret == -ETIMEDOUT || ret == -ENXIO)) + ret = cyapa_read_block(cyapa, CYAPA_CMD_BL_STATUS, status); + if (ret != BL_STATUS_SIZE) goto error; @@ -594,6 +740,19 @@ static irqreturn_t cyapa_irq(int irq, void *dev_id) return IRQ_HANDLED; } +static u8 cyapa_check_adapter_functionality(struct i2c_client *client) +{ + u8 ret = CYAPA_ADAPTER_FUNC_NONE; + + if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + ret |= CYAPA_ADAPTER_FUNC_I2C; + if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK)) + ret |= CYAPA_ADAPTER_FUNC_SMBUS; + return ret; +} + static int cyapa_create_input_dev(struct cyapa *cyapa) { struct device *dev = &cyapa->client->dev; @@ -668,9 +827,16 @@ static int cyapa_probe(struct i2c_client *client, const struct i2c_device_id *dev_id) { int ret; + u8 adapter_func; struct cyapa *cyapa; struct device *dev = &client->dev; + adapter_func = cyapa_check_adapter_functionality(client); + if (adapter_func == CYAPA_ADAPTER_FUNC_NONE) { + dev_err(dev, "not a supported I2C/SMBus adapter\n"); + return -EIO; + } + cyapa = kzalloc(sizeof(struct cyapa), GFP_KERNEL); if (!cyapa) { dev_err(dev, "allocate memory for cyapa failed\n"); @@ -683,6 +849,9 @@ static int cyapa_probe(struct i2c_client *client, sprintf(cyapa->phys, "i2c-%d-%04x/input0", client->adapter->nr, client->addr); + /* i2c isn't supported, use smbus */ + if (adapter_func == CYAPA_ADAPTER_FUNC_SMBUS) + cyapa->smbus = true; cyapa->state = CYAPA_STATE_NO_DEVICE; ret = cyapa_check_is_operational(cyapa); if (ret) { From a112e9fdda1e4a412628650f0988c46daaee9028 Mon Sep 17 00:00:00 2001 From: Ping Cheng Date: Wed, 13 Feb 2013 20:20:01 -0800 Subject: [PATCH 35/55] Input: wacom - add support for DTH-2242 It is a pen with 10 finger touch device. Signed-off-by: Ping Cheng Signed-off-by: Dmitry Torokhov --- drivers/input/tablet/wacom_wac.c | 25 ++++++++++++++++++++++++- drivers/input/tablet/wacom_wac.h | 1 + 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c index a318bf4ada05..ad11ec543623 100644 --- a/drivers/input/tablet/wacom_wac.c +++ b/drivers/input/tablet/wacom_wac.c @@ -359,6 +359,7 @@ static int wacom_intuos_inout(struct wacom_wac *wacom) case 0x802: /* Intuos4 General Pen */ case 0x804: /* Intuos4 Marker Pen */ case 0x40802: /* Intuos4 Classic Pen */ + case 0x18803: /* DTH2242 Grip Pen */ case 0x022: wacom->tool[idx] = BTN_TOOL_PEN; break; @@ -538,6 +539,13 @@ static int wacom_intuos_irq(struct wacom_wac *wacom) input_report_key(input, wacom->tool[1], 0); input_report_abs(input, ABS_MISC, 0); } + } else if (features->type == DTK) { + input_report_key(input, BTN_0, (data[6] & 0x01)); + input_report_key(input, BTN_1, (data[6] & 0x02)); + input_report_key(input, BTN_2, (data[6] & 0x04)); + input_report_key(input, BTN_3, (data[6] & 0x08)); + input_report_key(input, BTN_4, (data[6] & 0x10)); + input_report_key(input, BTN_5, (data[6] & 0x20)); } else if (features->type == WACOM_24HD) { input_report_key(input, BTN_0, (data[6] & 0x01)); input_report_key(input, BTN_1, (data[6] & 0x02)); @@ -1296,6 +1304,7 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len) case WACOM_21UX2: case WACOM_22HD: case WACOM_24HD: + case DTK: sync = wacom_intuos_irq(wacom_wac); break; @@ -1515,12 +1524,17 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev, __set_bit(BTN_Y, input_dev->keybit); __set_bit(BTN_Z, input_dev->keybit); - for (i = 0; i < 10; i++) + for (i = 6; i < 10; i++) __set_bit(BTN_0 + i, input_dev->keybit); __set_bit(KEY_PROG1, input_dev->keybit); __set_bit(KEY_PROG2, input_dev->keybit); __set_bit(KEY_PROG3, input_dev->keybit); + /* fall through */ + + case DTK: + for (i = 0; i < 6; i++) + __set_bit(BTN_0 + i, input_dev->keybit); input_set_abs_params(input_dev, ABS_Z, -900, 899, 0, 0); input_set_abs_params(input_dev, ABS_THROTTLE, 0, 71, 0, 0); @@ -1945,6 +1959,13 @@ static const struct wacom_features wacom_features_0xCE = static const struct wacom_features wacom_features_0xF0 = { "Wacom DTU1631", WACOM_PKGLEN_GRAPHIRE, 34623, 19553, 511, 0, DTU, WACOM_INTUOS_RES, WACOM_INTUOS_RES }; +static const struct wacom_features wacom_features_0x59 = /* Pen */ + { "Wacom DTH2242", WACOM_PKGLEN_INTUOS, 95840, 54260, 2047, + 63, DTK, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES, + .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x5D }; +static const struct wacom_features wacom_features_0x5D = /* Touch */ + { "Wacom DTH2242", .type = WACOM_24HDT, + .oVid = USB_VENDOR_ID_WACOM, .oPid = 0x59, .touch_max = 10 }; static const struct wacom_features wacom_features_0xCC = { "Wacom Cintiq 21UX2", WACOM_PKGLEN_INTUOS, 87200, 65600, 2047, 63, WACOM_21UX2, WACOM_INTUOS3_RES, WACOM_INTUOS3_RES }; @@ -2118,6 +2139,8 @@ const struct usb_device_id wacom_ids[] = { { USB_DEVICE_WACOM(0x43) }, { USB_DEVICE_WACOM(0x44) }, { USB_DEVICE_WACOM(0x45) }, + { USB_DEVICE_WACOM(0x59) }, + { USB_DEVICE_WACOM(0x5D) }, { USB_DEVICE_WACOM(0xB0) }, { USB_DEVICE_WACOM(0xB1) }, { USB_DEVICE_WACOM(0xB2) }, diff --git a/drivers/input/tablet/wacom_wac.h b/drivers/input/tablet/wacom_wac.h index 06410fe6425f..5f9a7721e16c 100644 --- a/drivers/input/tablet/wacom_wac.h +++ b/drivers/input/tablet/wacom_wac.h @@ -78,6 +78,7 @@ enum { INTUOS5L, WACOM_21UX2, WACOM_22HD, + DTK, WACOM_24HD, CINTIQ, WACOM_BEE, From 88a803482c4165642409bab77424a27939d51500 Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Wed, 13 Feb 2013 20:55:19 -0800 Subject: [PATCH 36/55] Input: ALPS - document the alps.h data structures Add kernel-doc markup. Signed-off-by: Kevin Cernekee Tested-by: Dave Turvene Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.h | 74 +++++++++++++++++++++++++++++++------- 1 file changed, 61 insertions(+), 13 deletions(-) diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h index ae1ac354c778..67be4e5fa1cd 100644 --- a/drivers/input/mouse/alps.h +++ b/drivers/input/mouse/alps.h @@ -17,30 +17,78 @@ #define ALPS_PROTO_V3 2 #define ALPS_PROTO_V4 3 +/** + * struct alps_model_info - touchpad ID table + * @signature: E7 response string to match. + * @command_mode_resp: For V3/V4 touchpads, the final byte of the EC response + * (aka command mode response) identifies the firmware minor version. This + * can be used to distinguish different hardware models which are not + * uniquely identifiable through their E7 responses. + * @proto_version: Indicates V1/V2/V3/... + * @byte0: Helps figure out whether a position report packet matches the + * known format for this model. The first byte of the report, ANDed with + * mask0, should match byte0. + * @mask0: The mask used to check the first byte of the report. + * @flags: Additional device capabilities (passthrough port, trackstick, etc.). + * + * Many (but not all) ALPS touchpads can be identified by looking at the + * values returned in the "E7 report" and/or the "EC report." This table + * lists a number of such touchpads. + */ struct alps_model_info { - unsigned char signature[3]; - unsigned char command_mode_resp; /* v3/v4 only */ + unsigned char signature[3]; + unsigned char command_mode_resp; unsigned char proto_version; - unsigned char byte0, mask0; - unsigned char flags; + unsigned char byte0, mask0; + unsigned char flags; }; +/** + * struct alps_nibble_commands - encodings for register accesses + * @command: PS/2 command used for the nibble + * @data: Data supplied as an argument to the PS/2 command, if applicable + * + * The ALPS protocol uses magic sequences to transmit binary data to the + * touchpad, as it is generally not OK to send arbitrary bytes out the + * PS/2 port. Each of the sequences in this table sends one nibble of the + * register address or (write) data. Different versions of the ALPS protocol + * use slightly different encodings. + */ struct alps_nibble_commands { int command; unsigned char data; }; +/** + * struct alps_data - private data structure for the ALPS driver + * @dev2: "Relative" device used to report trackstick or mouse activity. + * @phys: Physical path for the relative device. + * @i: Information on the detected touchpad model. + * @nibble_commands: Command mapping used for touchpad register accesses. + * @addr_command: Command used to tell the touchpad that a register address + * follows. + * @prev_fin: Finger bit from previous packet. + * @multi_packet: Multi-packet data in progress. + * @multi_data: Saved multi-packet data. + * @x1: First X coordinate from last MT report. + * @x2: Second X coordinate from last MT report. + * @y1: First Y coordinate from last MT report. + * @y2: Second Y coordinate from last MT report. + * @fingers: Number of fingers from last MT report. + * @quirks: Bitmap of ALPS_QUIRK_*. + * @timer: Timer for flushing out the final report packet in the stream. + */ struct alps_data { - struct input_dev *dev2; /* Relative device */ - char phys[32]; /* Phys */ - const struct alps_model_info *i;/* Info */ + struct input_dev *dev2; + char phys[32]; + const struct alps_model_info *i; const struct alps_nibble_commands *nibble_commands; - int addr_command; /* Command to set register address */ - int prev_fin; /* Finger bit from previous packet */ - int multi_packet; /* Multi-packet data in progress */ - unsigned char multi_data[6]; /* Saved multi-packet data */ - int x1, x2, y1, y2; /* Coordinates from last MT report */ - int fingers; /* Number of fingers from MT report */ + int addr_command; + int prev_fin; + int multi_packet; + unsigned char multi_data[6]; + int x1, x2, y1, y2; + int fingers; u8 quirks; struct timer_list timer; }; From 99df65e705503a97a5fceb30317086a091b4052d Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Wed, 13 Feb 2013 20:56:33 -0800 Subject: [PATCH 37/55] Input: ALPS - copy "model" info into alps_data struct Not every type of ALPS touchpad is well-suited to table-based detection. Start moving the various alps_model_data attributes into the alps_data struct so that we don't need a unique table entry for every possible permutation of protocol version, flags, byte0/mask0, etc. Signed-off-by: Kevin Cernekee Tested-by: Dave Turvene Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 63 +++++++++++++++++++------------------- drivers/input/mouse/alps.h | 14 +++++++-- 2 files changed, 43 insertions(+), 34 deletions(-) diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index e229fa3cad96..33ee6e0b9752 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -122,10 +122,10 @@ static const struct alps_model_info alps_model_data[] = { /* Packet formats are described in Documentation/input/alps.txt */ -static bool alps_is_valid_first_byte(const struct alps_model_info *model, +static bool alps_is_valid_first_byte(struct alps_data *priv, unsigned char data) { - return (data & model->mask0) == model->byte0; + return (data & priv->mask0) == priv->byte0; } static void alps_report_buttons(struct psmouse *psmouse, @@ -158,14 +158,13 @@ static void alps_report_buttons(struct psmouse *psmouse, static void alps_process_packet_v1_v2(struct psmouse *psmouse) { struct alps_data *priv = psmouse->private; - const struct alps_model_info *model = priv->i; unsigned char *packet = psmouse->packet; struct input_dev *dev = psmouse->dev; struct input_dev *dev2 = priv->dev2; int x, y, z, ges, fin, left, right, middle; int back = 0, forward = 0; - if (model->proto_version == ALPS_PROTO_V1) { + if (priv->proto_version == ALPS_PROTO_V1) { left = packet[2] & 0x10; right = packet[2] & 0x08; middle = 0; @@ -181,12 +180,12 @@ static void alps_process_packet_v1_v2(struct psmouse *psmouse) z = packet[5]; } - if (model->flags & ALPS_FW_BK_1) { + if (priv->flags & ALPS_FW_BK_1) { back = packet[0] & 0x10; forward = packet[2] & 4; } - if (model->flags & ALPS_FW_BK_2) { + if (priv->flags & ALPS_FW_BK_2) { back = packet[3] & 4; forward = packet[2] & 4; if ((middle = forward && back)) @@ -196,7 +195,7 @@ static void alps_process_packet_v1_v2(struct psmouse *psmouse) ges = packet[2] & 1; fin = packet[2] & 2; - if ((model->flags & ALPS_DUALPOINT) && z == 127) { + if ((priv->flags & ALPS_DUALPOINT) && z == 127) { input_report_rel(dev2, REL_X, (x > 383 ? (x - 768) : x)); input_report_rel(dev2, REL_Y, -(y > 255 ? (y - 512) : y)); @@ -239,15 +238,15 @@ static void alps_process_packet_v1_v2(struct psmouse *psmouse) input_report_abs(dev, ABS_PRESSURE, z); input_report_key(dev, BTN_TOOL_FINGER, z > 0); - if (model->flags & ALPS_WHEEL) + if (priv->flags & ALPS_WHEEL) input_report_rel(dev, REL_WHEEL, ((packet[2] << 1) & 0x08) - ((packet[0] >> 4) & 0x07)); - if (model->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) { + if (priv->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) { input_report_key(dev, BTN_FORWARD, forward); input_report_key(dev, BTN_BACK, back); } - if (model->flags & ALPS_FOUR_BUTTONS) { + if (priv->flags & ALPS_FOUR_BUTTONS) { input_report_key(dev, BTN_0, packet[2] & 4); input_report_key(dev, BTN_1, packet[0] & 0x10); input_report_key(dev, BTN_2, packet[3] & 4); @@ -699,9 +698,8 @@ static void alps_process_packet_v4(struct psmouse *psmouse) static void alps_process_packet(struct psmouse *psmouse) { struct alps_data *priv = psmouse->private; - const struct alps_model_info *model = priv->i; - switch (model->proto_version) { + switch (priv->proto_version) { case ALPS_PROTO_V1: case ALPS_PROTO_V2: alps_process_packet_v1_v2(psmouse); @@ -765,7 +763,7 @@ static psmouse_ret_t alps_handle_interleaved_ps2(struct psmouse *psmouse) if (((psmouse->packet[3] | psmouse->packet[4] | psmouse->packet[5]) & 0x80) || - (!alps_is_valid_first_byte(priv->i, psmouse->packet[6]))) { + (!alps_is_valid_first_byte(priv, psmouse->packet[6]))) { psmouse_dbg(psmouse, "refusing packet %4ph (suspected interleaved ps/2)\n", psmouse->packet + 3); @@ -844,7 +842,6 @@ static void alps_flush_packet(unsigned long data) static psmouse_ret_t alps_process_byte(struct psmouse *psmouse) { struct alps_data *priv = psmouse->private; - const struct alps_model_info *model = priv->i; if ((psmouse->packet[0] & 0xc8) == 0x08) { /* PS/2 packet */ if (psmouse->pktcnt == 3) { @@ -857,15 +854,15 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse) /* Check for PS/2 packet stuffed in the middle of ALPS packet. */ - if ((model->flags & ALPS_PS2_INTERLEAVED) && + if ((priv->flags & ALPS_PS2_INTERLEAVED) && psmouse->pktcnt >= 4 && (psmouse->packet[3] & 0x0f) == 0x0f) { return alps_handle_interleaved_ps2(psmouse); } - if (!alps_is_valid_first_byte(model, psmouse->packet[0])) { + if (!alps_is_valid_first_byte(priv, psmouse->packet[0])) { psmouse_dbg(psmouse, "refusing packet[0] = %x (mask0 = %x, byte0 = %x)\n", - psmouse->packet[0], model->mask0, model->byte0); + psmouse->packet[0], priv->mask0, priv->byte0); return PSMOUSE_BAD_DATA; } @@ -1190,16 +1187,16 @@ static int alps_poll(struct psmouse *psmouse) unsigned char buf[sizeof(psmouse->packet)]; bool poll_failed; - if (priv->i->flags & ALPS_PASS) + if (priv->flags & ALPS_PASS) alps_passthrough_mode_v2(psmouse, true); poll_failed = ps2_command(&psmouse->ps2dev, buf, PSMOUSE_CMD_POLL | (psmouse->pktsize << 8)) < 0; - if (priv->i->flags & ALPS_PASS) + if (priv->flags & ALPS_PASS) alps_passthrough_mode_v2(psmouse, false); - if (poll_failed || (buf[0] & priv->i->mask0) != priv->i->byte0) + if (poll_failed || (buf[0] & priv->mask0) != priv->byte0) return -1; if ((psmouse->badbyte & 0xc8) == 0x08) { @@ -1217,9 +1214,8 @@ static int alps_poll(struct psmouse *psmouse) static int alps_hw_init_v1_v2(struct psmouse *psmouse) { struct alps_data *priv = psmouse->private; - const struct alps_model_info *model = priv->i; - if ((model->flags & ALPS_PASS) && + if ((priv->flags & ALPS_PASS) && alps_passthrough_mode_v2(psmouse, true)) { return -1; } @@ -1234,7 +1230,7 @@ static int alps_hw_init_v1_v2(struct psmouse *psmouse) return -1; } - if ((model->flags & ALPS_PASS) && + if ((priv->flags & ALPS_PASS) && alps_passthrough_mode_v2(psmouse, false)) { return -1; } @@ -1520,10 +1516,9 @@ static int alps_hw_init_v4(struct psmouse *psmouse) static int alps_hw_init(struct psmouse *psmouse) { struct alps_data *priv = psmouse->private; - const struct alps_model_info *model = priv->i; int ret = -1; - switch (model->proto_version) { + switch (priv->proto_version) { case ALPS_PROTO_V1: case ALPS_PROTO_V2: ret = alps_hw_init_v1_v2(psmouse); @@ -1585,7 +1580,10 @@ int alps_init(struct psmouse *psmouse) if (!model) goto init_fail; - priv->i = model; + priv->proto_version = model->proto_version; + priv->byte0 = model->byte0; + priv->mask0 = model->mask0; + priv->flags = model->flags; if (alps_hw_init(psmouse)) goto init_fail; @@ -1609,7 +1607,7 @@ int alps_init(struct psmouse *psmouse) dev1->evbit[BIT_WORD(EV_ABS)] |= BIT_MASK(EV_ABS); - switch (model->proto_version) { + switch (priv->proto_version) { case ALPS_PROTO_V1: case ALPS_PROTO_V2: input_set_abs_params(dev1, ABS_X, 0, 1023, 0, 0); @@ -1633,17 +1631,17 @@ int alps_init(struct psmouse *psmouse) input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0); - if (model->flags & ALPS_WHEEL) { + if (priv->flags & ALPS_WHEEL) { dev1->evbit[BIT_WORD(EV_REL)] |= BIT_MASK(EV_REL); dev1->relbit[BIT_WORD(REL_WHEEL)] |= BIT_MASK(REL_WHEEL); } - if (model->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) { + if (priv->flags & (ALPS_FW_BK_1 | ALPS_FW_BK_2)) { dev1->keybit[BIT_WORD(BTN_FORWARD)] |= BIT_MASK(BTN_FORWARD); dev1->keybit[BIT_WORD(BTN_BACK)] |= BIT_MASK(BTN_BACK); } - if (model->flags & ALPS_FOUR_BUTTONS) { + if (priv->flags & ALPS_FOUR_BUTTONS) { dev1->keybit[BIT_WORD(BTN_0)] |= BIT_MASK(BTN_0); dev1->keybit[BIT_WORD(BTN_1)] |= BIT_MASK(BTN_1); dev1->keybit[BIT_WORD(BTN_2)] |= BIT_MASK(BTN_2); @@ -1654,7 +1652,8 @@ int alps_init(struct psmouse *psmouse) snprintf(priv->phys, sizeof(priv->phys), "%s/input1", psmouse->ps2dev.serio->phys); dev2->phys = priv->phys; - dev2->name = (model->flags & ALPS_DUALPOINT) ? "DualPoint Stick" : "PS/2 Mouse"; + dev2->name = (priv->flags & ALPS_DUALPOINT) ? + "DualPoint Stick" : "PS/2 Mouse"; dev2->id.bustype = BUS_I8042; dev2->id.vendor = 0x0002; dev2->id.product = PSMOUSE_ALPS; @@ -1673,7 +1672,7 @@ int alps_init(struct psmouse *psmouse) psmouse->poll = alps_poll; psmouse->disconnect = alps_disconnect; psmouse->reconnect = alps_reconnect; - psmouse->pktsize = model->proto_version == ALPS_PROTO_V4 ? 8 : 6; + psmouse->pktsize = priv->proto_version == ALPS_PROTO_V4 ? 8 : 6; /* We are having trouble resyncing ALPS touchpads so disable it for now */ psmouse->resync_time = 0; diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h index 67be4e5fa1cd..efd0eea9f001 100644 --- a/drivers/input/mouse/alps.h +++ b/drivers/input/mouse/alps.h @@ -63,10 +63,15 @@ struct alps_nibble_commands { * struct alps_data - private data structure for the ALPS driver * @dev2: "Relative" device used to report trackstick or mouse activity. * @phys: Physical path for the relative device. - * @i: Information on the detected touchpad model. * @nibble_commands: Command mapping used for touchpad register accesses. * @addr_command: Command used to tell the touchpad that a register address * follows. + * @proto_version: Indicates V1/V2/V3/... + * @byte0: Helps figure out whether a position report packet matches the + * known format for this model. The first byte of the report, ANDed with + * mask0, should match byte0. + * @mask0: The mask used to check the first byte of the report. + * @flags: Additional device capabilities (passthrough port, trackstick, etc.). * @prev_fin: Finger bit from previous packet. * @multi_packet: Multi-packet data in progress. * @multi_data: Saved multi-packet data. @@ -81,9 +86,14 @@ struct alps_nibble_commands { struct alps_data { struct input_dev *dev2; char phys[32]; - const struct alps_model_info *i; + + /* these are autodetected when the device is identified */ const struct alps_nibble_commands *nibble_commands; int addr_command; + unsigned char proto_version; + unsigned char byte0, mask0; + unsigned char flags; + int prev_fin; int multi_packet; unsigned char multi_data[6]; From 2e992cc030d8e4e42632bcc9e16605e629886778 Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Wed, 13 Feb 2013 20:57:04 -0800 Subject: [PATCH 38/55] Input: ALPS - move alps_get_model() down below hw_init code This will minimize the number of forward declarations needed when alps_get_model() starts assigning function pointers. Signed-off-by: Kevin Cernekee Tested-by: Dave Turvene Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 186 ++++++++++++++++++------------------- 1 file changed, 93 insertions(+), 93 deletions(-) diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 33ee6e0b9752..c473549c33bd 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -998,99 +998,6 @@ static inline int alps_exit_command_mode(struct psmouse *psmouse) return 0; } -static const struct alps_model_info *alps_get_model(struct psmouse *psmouse, int *version) -{ - struct ps2dev *ps2dev = &psmouse->ps2dev; - static const unsigned char rates[] = { 0, 10, 20, 40, 60, 80, 100, 200 }; - unsigned char param[4]; - const struct alps_model_info *model = NULL; - int i; - - /* - * First try "E6 report". - * ALPS should return 0,0,10 or 0,0,100 if no buttons are pressed. - * The bits 0-2 of the first byte will be 1s if some buttons are - * pressed. - */ - param[0] = 0; - if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) - return NULL; - - param[0] = param[1] = param[2] = 0xff; - if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) - return NULL; - - psmouse_dbg(psmouse, "E6 report: %2.2x %2.2x %2.2x", - param[0], param[1], param[2]); - - if ((param[0] & 0xf8) != 0 || param[1] != 0 || - (param[2] != 10 && param[2] != 100)) - return NULL; - - /* - * Now try "E7 report". Allowed responses are in - * alps_model_data[].signature - */ - param[0] = 0; - if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21)) - return NULL; - - param[0] = param[1] = param[2] = 0xff; - if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) - return NULL; - - psmouse_dbg(psmouse, "E7 report: %2.2x %2.2x %2.2x", - param[0], param[1], param[2]); - - if (version) { - for (i = 0; i < ARRAY_SIZE(rates) && param[2] != rates[i]; i++) - /* empty */; - *version = (param[0] << 8) | (param[1] << 4) | i; - } - - for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) { - if (!memcmp(param, alps_model_data[i].signature, - sizeof(alps_model_data[i].signature))) { - model = alps_model_data + i; - break; - } - } - - if (model && model->proto_version > ALPS_PROTO_V2) { - /* - * Need to check command mode response to identify - * model - */ - model = NULL; - if (alps_enter_command_mode(psmouse, param)) { - psmouse_warn(psmouse, - "touchpad failed to enter command mode\n"); - } else { - for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) { - if (alps_model_data[i].proto_version > ALPS_PROTO_V2 && - alps_model_data[i].command_mode_resp == param[0]) { - model = alps_model_data + i; - break; - } - } - alps_exit_command_mode(psmouse); - - if (!model) - psmouse_dbg(psmouse, - "Unknown command mode response %2.2x\n", - param[0]); - } - } - - return model; -} - /* * For DualPoint devices select the device that should respond to * subsequent commands. It looks like glidepad is behind stickpointer, @@ -1534,6 +1441,99 @@ static int alps_hw_init(struct psmouse *psmouse) return ret; } +static const struct alps_model_info *alps_get_model(struct psmouse *psmouse, int *version) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + static const unsigned char rates[] = { 0, 10, 20, 40, 60, 80, 100, 200 }; + unsigned char param[4]; + const struct alps_model_info *model = NULL; + int i; + + /* + * First try "E6 report". + * ALPS should return 0,0,10 or 0,0,100 if no buttons are pressed. + * The bits 0-2 of the first byte will be 1s if some buttons are + * pressed. + */ + param[0] = 0; + if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) + return NULL; + + param[0] = param[1] = param[2] = 0xff; + if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) + return NULL; + + psmouse_dbg(psmouse, "E6 report: %2.2x %2.2x %2.2x", + param[0], param[1], param[2]); + + if ((param[0] & 0xf8) != 0 || param[1] != 0 || + (param[2] != 10 && param[2] != 100)) + return NULL; + + /* + * Now try "E7 report". Allowed responses are in + * alps_model_data[].signature + */ + param[0] = 0; + if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21)) + return NULL; + + param[0] = param[1] = param[2] = 0xff; + if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) + return NULL; + + psmouse_dbg(psmouse, "E7 report: %2.2x %2.2x %2.2x", + param[0], param[1], param[2]); + + if (version) { + for (i = 0; i < ARRAY_SIZE(rates) && param[2] != rates[i]; i++) + /* empty */; + *version = (param[0] << 8) | (param[1] << 4) | i; + } + + for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) { + if (!memcmp(param, alps_model_data[i].signature, + sizeof(alps_model_data[i].signature))) { + model = alps_model_data + i; + break; + } + } + + if (model && model->proto_version > ALPS_PROTO_V2) { + /* + * Need to check command mode response to identify + * model + */ + model = NULL; + if (alps_enter_command_mode(psmouse, param)) { + psmouse_warn(psmouse, + "touchpad failed to enter command mode\n"); + } else { + for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) { + if (alps_model_data[i].proto_version > ALPS_PROTO_V2 && + alps_model_data[i].command_mode_resp == param[0]) { + model = alps_model_data + i; + break; + } + } + alps_exit_command_mode(psmouse); + + if (!model) + psmouse_dbg(psmouse, + "Unknown command mode response %2.2x\n", + param[0]); + } + } + + return model; +} + static int alps_reconnect(struct psmouse *psmouse) { const struct alps_model_info *model; From 24ba9707829818daab711eed7e41313d5e56d0b4 Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Wed, 13 Feb 2013 22:19:01 -0800 Subject: [PATCH 39/55] Input: ALPS - introduce helper function for repeated commands Several ALPS driver init sequences repeat a command three times, then issue PSMOUSE_CMD_GETINFO to read the result. Move this into a helper function to simplify the code. Signed-off-by: Kevin Cernekee Tested-by: Dave Turvene Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 71 ++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 41 deletions(-) diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index c473549c33bd..1ca854b0b012 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -964,24 +964,42 @@ static int alps_command_mode_write_reg(struct psmouse *psmouse, int addr, return __alps_command_mode_write_reg(psmouse, value); } +static int alps_rpt_cmd(struct psmouse *psmouse, int init_command, + int repeated_command, unsigned char *param) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + + param[0] = 0; + if (init_command && ps2_command(ps2dev, param, init_command)) + return -EIO; + + if (ps2_command(ps2dev, NULL, repeated_command) || + ps2_command(ps2dev, NULL, repeated_command) || + ps2_command(ps2dev, NULL, repeated_command)) + return -EIO; + + param[0] = param[1] = param[2] = 0xff; + if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) + return -EIO; + + psmouse_dbg(psmouse, "%2.2X report: %2.2x %2.2x %2.2x\n", + repeated_command, param[0], param[1], param[2]); + return 0; +} + static int alps_enter_command_mode(struct psmouse *psmouse, unsigned char *resp) { unsigned char param[4]; - struct ps2dev *ps2dev = &psmouse->ps2dev; - if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_WRAP) || - ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) { + if (alps_rpt_cmd(psmouse, 0, PSMOUSE_CMD_RESET_WRAP, param)) { psmouse_err(psmouse, "failed to enter command mode\n"); return -1; } if (param[0] != 0x88 && param[1] != 0x07) { psmouse_dbg(psmouse, - "unknown response while entering command mode: %2.2x %2.2x %2.2x\n", - param[0], param[1], param[2]); + "unknown response while entering command mode\n"); return -1; } @@ -1041,18 +1059,10 @@ static int alps_absolute_mode_v1_v2(struct psmouse *psmouse) static int alps_get_status(struct psmouse *psmouse, char *param) { - struct ps2dev *ps2dev = &psmouse->ps2dev; - /* Get status: 0xF5 0xF5 0xF5 0xE9 */ - if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_DISABLE) || - ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) + if (alps_rpt_cmd(psmouse, 0, PSMOUSE_CMD_DISABLE, param)) return -1; - psmouse_dbg(psmouse, "Status: %2.2x %2.2x %2.2x", - param[0], param[1], param[2]); - return 0; } @@ -1443,7 +1453,6 @@ static int alps_hw_init(struct psmouse *psmouse) static const struct alps_model_info *alps_get_model(struct psmouse *psmouse, int *version) { - struct ps2dev *ps2dev = &psmouse->ps2dev; static const unsigned char rates[] = { 0, 10, 20, 40, 60, 80, 100, 200 }; unsigned char param[4]; const struct alps_model_info *model = NULL; @@ -1455,20 +1464,10 @@ static const struct alps_model_info *alps_get_model(struct psmouse *psmouse, int * The bits 0-2 of the first byte will be 1s if some buttons are * pressed. */ - param[0] = 0; - if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11)) + if (alps_rpt_cmd(psmouse, PSMOUSE_CMD_SETRES, PSMOUSE_CMD_SETSCALE11, + param)) return NULL; - param[0] = param[1] = param[2] = 0xff; - if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) - return NULL; - - psmouse_dbg(psmouse, "E6 report: %2.2x %2.2x %2.2x", - param[0], param[1], param[2]); - if ((param[0] & 0xf8) != 0 || param[1] != 0 || (param[2] != 10 && param[2] != 100)) return NULL; @@ -1477,20 +1476,10 @@ static const struct alps_model_info *alps_get_model(struct psmouse *psmouse, int * Now try "E7 report". Allowed responses are in * alps_model_data[].signature */ - param[0] = 0; - if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21)) + if (alps_rpt_cmd(psmouse, PSMOUSE_CMD_SETRES, PSMOUSE_CMD_SETSCALE21, + param)) return NULL; - param[0] = param[1] = param[2] = 0xff; - if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) - return NULL; - - psmouse_dbg(psmouse, "E7 report: %2.2x %2.2x %2.2x", - param[0], param[1], param[2]); - if (version) { for (i = 0; i < ARRAY_SIZE(rates) && param[2] != rates[i]; i++) /* empty */; From b5d6b851eab7f346ea5e69f46247b363b32b3670 Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Wed, 13 Feb 2013 22:19:59 -0800 Subject: [PATCH 40/55] Input: ALPS - rework detection sequence If the E6 report test passes, get the E7 and EC reports right away and then try to match an entry in the table. Pass in the alps_data struct, so that the detection code will be able to set operating parameters based on information found during detection. Change the version (psmouse->model) to report the protocol version only, in preparation for supporting models that do not show up in the ID table. Signed-off-by: Kevin Cernekee Tested-by: Dave Turvene Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 124 ++++++++++++++++--------------------- drivers/input/mouse/alps.h | 8 +-- 2 files changed, 56 insertions(+), 76 deletions(-) diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 1ca854b0b012..e6a27a5cbb1d 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -1451,86 +1451,76 @@ static int alps_hw_init(struct psmouse *psmouse) return ret; } -static const struct alps_model_info *alps_get_model(struct psmouse *psmouse, int *version) +static int alps_match_table(struct psmouse *psmouse, struct alps_data *priv, + unsigned char *e7, unsigned char *ec) { - static const unsigned char rates[] = { 0, 10, 20, 40, 60, 80, 100, 200 }; - unsigned char param[4]; - const struct alps_model_info *model = NULL; + const struct alps_model_info *model; int i; + for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) { + model = &alps_model_data[i]; + + if (!memcmp(e7, model->signature, sizeof(model->signature)) && + (!model->command_mode_resp || + model->command_mode_resp == ec[2])) { + + priv->proto_version = model->proto_version; + priv->flags = model->flags; + priv->byte0 = model->byte0; + priv->mask0 = model->mask0; + + return 0; + } + } + + return -EINVAL; +} + +static int alps_identify(struct psmouse *psmouse, struct alps_data *priv) +{ + unsigned char e6[4], e7[4], ec[4]; + /* * First try "E6 report". * ALPS should return 0,0,10 or 0,0,100 if no buttons are pressed. * The bits 0-2 of the first byte will be 1s if some buttons are * pressed. */ - if (alps_rpt_cmd(psmouse, PSMOUSE_CMD_SETRES, PSMOUSE_CMD_SETSCALE11, - param)) - return NULL; + if (alps_rpt_cmd(psmouse, PSMOUSE_CMD_SETRES, + PSMOUSE_CMD_SETSCALE11, e6)) + return -EIO; - if ((param[0] & 0xf8) != 0 || param[1] != 0 || - (param[2] != 10 && param[2] != 100)) - return NULL; + if ((e6[0] & 0xf8) != 0 || e6[1] != 0 || (e6[2] != 10 && e6[2] != 100)) + return -EINVAL; /* - * Now try "E7 report". Allowed responses are in - * alps_model_data[].signature + * Now get the "E7" and "EC" reports. These will uniquely identify + * most ALPS touchpads. */ - if (alps_rpt_cmd(psmouse, PSMOUSE_CMD_SETRES, PSMOUSE_CMD_SETSCALE21, - param)) - return NULL; + if (alps_rpt_cmd(psmouse, PSMOUSE_CMD_SETRES, + PSMOUSE_CMD_SETSCALE21, e7) || + alps_rpt_cmd(psmouse, PSMOUSE_CMD_SETRES, + PSMOUSE_CMD_RESET_WRAP, ec) || + alps_exit_command_mode(psmouse)) + return -EIO; - if (version) { - for (i = 0; i < ARRAY_SIZE(rates) && param[2] != rates[i]; i++) - /* empty */; - *version = (param[0] << 8) | (param[1] << 4) | i; - } + if (alps_match_table(psmouse, priv, e7, ec) == 0) + return 0; - for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) { - if (!memcmp(param, alps_model_data[i].signature, - sizeof(alps_model_data[i].signature))) { - model = alps_model_data + i; - break; - } - } + psmouse_info(psmouse, + "Unknown ALPS touchpad: E7=%2.2x %2.2x %2.2x, EC=%2.2x %2.2x %2.2x\n", + e7[0], e7[1], e7[2], ec[0], ec[1], ec[2]); - if (model && model->proto_version > ALPS_PROTO_V2) { - /* - * Need to check command mode response to identify - * model - */ - model = NULL; - if (alps_enter_command_mode(psmouse, param)) { - psmouse_warn(psmouse, - "touchpad failed to enter command mode\n"); - } else { - for (i = 0; i < ARRAY_SIZE(alps_model_data); i++) { - if (alps_model_data[i].proto_version > ALPS_PROTO_V2 && - alps_model_data[i].command_mode_resp == param[0]) { - model = alps_model_data + i; - break; - } - } - alps_exit_command_mode(psmouse); - - if (!model) - psmouse_dbg(psmouse, - "Unknown command mode response %2.2x\n", - param[0]); - } - } - - return model; + return -EINVAL; } static int alps_reconnect(struct psmouse *psmouse) { - const struct alps_model_info *model; + struct alps_data *priv = psmouse->private; psmouse_reset(psmouse); - model = alps_get_model(psmouse, NULL); - if (!model) + if (alps_identify(psmouse, priv) < 0) return -1; return alps_hw_init(psmouse); @@ -1549,9 +1539,7 @@ static void alps_disconnect(struct psmouse *psmouse) int alps_init(struct psmouse *psmouse) { struct alps_data *priv; - const struct alps_model_info *model; struct input_dev *dev1 = psmouse->dev, *dev2; - int version; priv = kzalloc(sizeof(struct alps_data), GFP_KERNEL); dev2 = input_allocate_device(); @@ -1565,15 +1553,9 @@ int alps_init(struct psmouse *psmouse) psmouse_reset(psmouse); - model = alps_get_model(psmouse, &version); - if (!model) + if (alps_identify(psmouse, priv) < 0) goto init_fail; - priv->proto_version = model->proto_version; - priv->byte0 = model->byte0; - priv->mask0 = model->mask0; - priv->flags = model->flags; - if (alps_hw_init(psmouse)) goto init_fail; @@ -1678,18 +1660,16 @@ int alps_init(struct psmouse *psmouse) int alps_detect(struct psmouse *psmouse, bool set_properties) { - int version; - const struct alps_model_info *model; + struct alps_data dummy; - model = alps_get_model(psmouse, &version); - if (!model) + if (alps_identify(psmouse, &dummy) < 0) return -1; if (set_properties) { psmouse->vendor = "ALPS"; - psmouse->name = model->flags & ALPS_DUALPOINT ? + psmouse->name = dummy.flags & ALPS_DUALPOINT ? "DualPoint TouchPad" : "GlidePoint"; - psmouse->model = version; + psmouse->model = dummy.proto_version << 8; } return 0; } diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h index efd0eea9f001..a81b31830454 100644 --- a/drivers/input/mouse/alps.h +++ b/drivers/input/mouse/alps.h @@ -12,10 +12,10 @@ #ifndef _ALPS_H #define _ALPS_H -#define ALPS_PROTO_V1 0 -#define ALPS_PROTO_V2 1 -#define ALPS_PROTO_V3 2 -#define ALPS_PROTO_V4 3 +#define ALPS_PROTO_V1 1 +#define ALPS_PROTO_V2 2 +#define ALPS_PROTO_V3 3 +#define ALPS_PROTO_V4 4 /** * struct alps_model_info - touchpad ID table From 24af5cb95f1b93c6c72a73113494ace4bcbee5a2 Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Wed, 13 Feb 2013 22:22:08 -0800 Subject: [PATCH 41/55] Input: ALPS - use function pointers for different protocol handlers In anticipation of adding more ALPS protocols and more per-device quirks, use function pointers instead of switch statements to call functions that differ from one device to the next. Signed-off-by: Kevin Cernekee Tested-by: Dave Turvene Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 101 +++++++++++++++++-------------------- drivers/input/mouse/alps.h | 7 +++ 2 files changed, 54 insertions(+), 54 deletions(-) diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index e6a27a5cbb1d..fe45687cf65a 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -114,6 +114,11 @@ static const struct alps_model_info alps_model_data[] = { { { 0x73, 0x02, 0x64 }, 0x8a, ALPS_PROTO_V4, 0x8f, 0x8f, 0 }, }; +static void alps_set_abs_params_st(struct alps_data *priv, + struct input_dev *dev1); +static void alps_set_abs_params_mt(struct alps_data *priv, + struct input_dev *dev1); + /* * XXX - this entry is suspicious. First byte has zero lower nibble, * which is what a normal mouse would report. Also, the value 0x0e @@ -695,24 +700,6 @@ static void alps_process_packet_v4(struct psmouse *psmouse) input_sync(dev); } -static void alps_process_packet(struct psmouse *psmouse) -{ - struct alps_data *priv = psmouse->private; - - switch (priv->proto_version) { - case ALPS_PROTO_V1: - case ALPS_PROTO_V2: - alps_process_packet_v1_v2(psmouse); - break; - case ALPS_PROTO_V3: - alps_process_packet_v3(psmouse); - break; - case ALPS_PROTO_V4: - alps_process_packet_v4(psmouse); - break; - } -} - static void alps_report_bare_ps2_packet(struct psmouse *psmouse, unsigned char packet[], bool report_buttons) @@ -770,7 +757,7 @@ static psmouse_ret_t alps_handle_interleaved_ps2(struct psmouse *psmouse) return PSMOUSE_BAD_DATA; } - alps_process_packet(psmouse); + priv->process_packet(psmouse); /* Continue with the next packet */ psmouse->packet[0] = psmouse->packet[6]; @@ -814,6 +801,7 @@ static psmouse_ret_t alps_handle_interleaved_ps2(struct psmouse *psmouse) static void alps_flush_packet(unsigned long data) { struct psmouse *psmouse = (struct psmouse *)data; + struct alps_data *priv = psmouse->private; serio_pause_rx(psmouse->ps2dev.serio); @@ -831,7 +819,7 @@ static void alps_flush_packet(unsigned long data) "refusing packet %3ph (suspected interleaved ps/2)\n", psmouse->packet + 3); } else { - alps_process_packet(psmouse); + priv->process_packet(psmouse); } psmouse->pktcnt = 0; } @@ -876,7 +864,7 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse) } if (psmouse->pktcnt == psmouse->pktsize) { - alps_process_packet(psmouse); + priv->process_packet(psmouse); return PSMOUSE_FULL_PACKET; } @@ -1430,25 +1418,26 @@ static int alps_hw_init_v4(struct psmouse *psmouse) return -1; } -static int alps_hw_init(struct psmouse *psmouse) +static void alps_set_defaults(struct alps_data *priv) { - struct alps_data *priv = psmouse->private; - int ret = -1; - switch (priv->proto_version) { case ALPS_PROTO_V1: case ALPS_PROTO_V2: - ret = alps_hw_init_v1_v2(psmouse); + priv->hw_init = alps_hw_init_v1_v2; + priv->process_packet = alps_process_packet_v1_v2; + priv->set_abs_params = alps_set_abs_params_st; break; case ALPS_PROTO_V3: - ret = alps_hw_init_v3(psmouse); + priv->hw_init = alps_hw_init_v3; + priv->process_packet = alps_process_packet_v3; + priv->set_abs_params = alps_set_abs_params_mt; break; case ALPS_PROTO_V4: - ret = alps_hw_init_v4(psmouse); + priv->hw_init = alps_hw_init_v4; + priv->process_packet = alps_process_packet_v4; + priv->set_abs_params = alps_set_abs_params_mt; break; } - - return ret; } static int alps_match_table(struct psmouse *psmouse, struct alps_data *priv, @@ -1465,6 +1454,8 @@ static int alps_match_table(struct psmouse *psmouse, struct alps_data *priv, model->command_mode_resp == ec[2])) { priv->proto_version = model->proto_version; + alps_set_defaults(priv); + priv->flags = model->flags; priv->byte0 = model->byte0; priv->mask0 = model->mask0; @@ -1523,7 +1514,7 @@ static int alps_reconnect(struct psmouse *psmouse) if (alps_identify(psmouse, priv) < 0) return -1; - return alps_hw_init(psmouse); + return priv->hw_init(psmouse); } static void alps_disconnect(struct psmouse *psmouse) @@ -1536,6 +1527,29 @@ static void alps_disconnect(struct psmouse *psmouse) kfree(priv); } +static void alps_set_abs_params_st(struct alps_data *priv, + struct input_dev *dev1) +{ + input_set_abs_params(dev1, ABS_X, 0, 1023, 0, 0); + input_set_abs_params(dev1, ABS_Y, 0, 767, 0, 0); +} + +static void alps_set_abs_params_mt(struct alps_data *priv, + struct input_dev *dev1) +{ + set_bit(INPUT_PROP_SEMI_MT, dev1->propbit); + input_mt_init_slots(dev1, 2, 0); + input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, ALPS_V3_X_MAX, 0, 0); + input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, ALPS_V3_Y_MAX, 0, 0); + + set_bit(BTN_TOOL_DOUBLETAP, dev1->keybit); + set_bit(BTN_TOOL_TRIPLETAP, dev1->keybit); + set_bit(BTN_TOOL_QUADTAP, dev1->keybit); + + input_set_abs_params(dev1, ABS_X, 0, ALPS_V3_X_MAX, 0, 0); + input_set_abs_params(dev1, ABS_Y, 0, ALPS_V3_Y_MAX, 0, 0); +} + int alps_init(struct psmouse *psmouse) { struct alps_data *priv; @@ -1556,7 +1570,7 @@ int alps_init(struct psmouse *psmouse) if (alps_identify(psmouse, priv) < 0) goto init_fail; - if (alps_hw_init(psmouse)) + if (priv->hw_init(psmouse)) goto init_fail; /* @@ -1578,28 +1592,7 @@ int alps_init(struct psmouse *psmouse) dev1->evbit[BIT_WORD(EV_ABS)] |= BIT_MASK(EV_ABS); - switch (priv->proto_version) { - case ALPS_PROTO_V1: - case ALPS_PROTO_V2: - input_set_abs_params(dev1, ABS_X, 0, 1023, 0, 0); - input_set_abs_params(dev1, ABS_Y, 0, 767, 0, 0); - break; - case ALPS_PROTO_V3: - case ALPS_PROTO_V4: - set_bit(INPUT_PROP_SEMI_MT, dev1->propbit); - input_mt_init_slots(dev1, 2, 0); - input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, ALPS_V3_X_MAX, 0, 0); - input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, ALPS_V3_Y_MAX, 0, 0); - - set_bit(BTN_TOOL_DOUBLETAP, dev1->keybit); - set_bit(BTN_TOOL_TRIPLETAP, dev1->keybit); - set_bit(BTN_TOOL_QUADTAP, dev1->keybit); - - input_set_abs_params(dev1, ABS_X, 0, ALPS_V3_X_MAX, 0, 0); - input_set_abs_params(dev1, ABS_Y, 0, ALPS_V3_Y_MAX, 0, 0); - break; - } - + priv->set_abs_params(priv, dev1); input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0); if (priv->flags & ALPS_WHEEL) { diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h index a81b31830454..0934f8bf7d76 100644 --- a/drivers/input/mouse/alps.h +++ b/drivers/input/mouse/alps.h @@ -72,6 +72,9 @@ struct alps_nibble_commands { * mask0, should match byte0. * @mask0: The mask used to check the first byte of the report. * @flags: Additional device capabilities (passthrough port, trackstick, etc.). + * @hw_init: Protocol-specific hardware init function. + * @process_packet: Protocol-specific function to process a report packet. + * @set_abs_params: Protocol-specific function to configure the input_dev. * @prev_fin: Finger bit from previous packet. * @multi_packet: Multi-packet data in progress. * @multi_data: Saved multi-packet data. @@ -94,6 +97,10 @@ struct alps_data { unsigned char byte0, mask0; unsigned char flags; + int (*hw_init)(struct psmouse *psmouse); + void (*process_packet)(struct psmouse *psmouse); + void (*set_abs_params)(struct alps_data *priv, struct input_dev *dev1); + int prev_fin; int multi_packet; unsigned char multi_data[6]; From 50e8b2162feb851f5af1372ba996640ad8d66fc9 Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Wed, 13 Feb 2013 22:23:04 -0800 Subject: [PATCH 42/55] Input: ALPS - move {addr,nibble}_command settings into alps_set_defaults() This allows alps_identify() to override these settings based on the device characteristics, if it is ever necessary. Signed-off-by: Kevin Cernekee Tested-by: Dave Turvene Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index fe45687cf65a..2221a00b25d3 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -1190,14 +1190,10 @@ static int alps_absolute_mode_v3(struct psmouse *psmouse) static int alps_hw_init_v3(struct psmouse *psmouse) { - struct alps_data *priv = psmouse->private; struct ps2dev *ps2dev = &psmouse->ps2dev; int reg_val; unsigned char param[4]; - priv->nibble_commands = alps_v3_nibble_commands; - priv->addr_command = PSMOUSE_CMD_RESET_WRAP; - if (alps_enter_command_mode(psmouse, NULL)) goto error; @@ -1343,13 +1339,9 @@ static int alps_absolute_mode_v4(struct psmouse *psmouse) static int alps_hw_init_v4(struct psmouse *psmouse) { - struct alps_data *priv = psmouse->private; struct ps2dev *ps2dev = &psmouse->ps2dev; unsigned char param[4]; - priv->nibble_commands = alps_v4_nibble_commands; - priv->addr_command = PSMOUSE_CMD_DISABLE; - if (alps_enter_command_mode(psmouse, NULL)) goto error; @@ -1431,11 +1423,15 @@ static void alps_set_defaults(struct alps_data *priv) priv->hw_init = alps_hw_init_v3; priv->process_packet = alps_process_packet_v3; priv->set_abs_params = alps_set_abs_params_mt; + priv->nibble_commands = alps_v3_nibble_commands; + priv->addr_command = PSMOUSE_CMD_RESET_WRAP; break; case ALPS_PROTO_V4: priv->hw_init = alps_hw_init_v4; priv->process_packet = alps_process_packet_v4; priv->set_abs_params = alps_set_abs_params_mt; + priv->nibble_commands = alps_v4_nibble_commands; + priv->addr_command = PSMOUSE_CMD_DISABLE; break; } } From f673ceb11b880e8535476326612e54c3e0cace5b Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Wed, 13 Feb 2013 22:23:34 -0800 Subject: [PATCH 43/55] Input: ALPS - rework detection of Pinnacle AGx touchpads The official ALPS driver uses the EC report, not the E7 report, to detect these devices. Also, they check for a range of values; the original table-based code only checked for two specific ones. Signed-off-by: Kevin Cernekee Tested-by: Dave Turvene Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 2221a00b25d3..eafeae279356 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -109,8 +109,6 @@ static const struct alps_model_info alps_model_data[] = { { { 0x73, 0x02, 0x50 }, 0x00, ALPS_PROTO_V2, 0xcf, 0xcf, ALPS_FOUR_BUTTONS }, /* Dell Vostro 1400 */ { { 0x52, 0x01, 0x14 }, 0x00, ALPS_PROTO_V2, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT | ALPS_PS2_INTERLEAVED }, /* Toshiba Tecra A11-11L */ - { { 0x73, 0x02, 0x64 }, 0x9b, ALPS_PROTO_V3, 0x8f, 0x8f, ALPS_DUALPOINT }, - { { 0x73, 0x02, 0x64 }, 0x9d, ALPS_PROTO_V3, 0x8f, 0x8f, ALPS_DUALPOINT }, { { 0x73, 0x02, 0x64 }, 0x8a, ALPS_PROTO_V4, 0x8f, 0x8f, 0 }, }; @@ -1412,6 +1410,10 @@ static int alps_hw_init_v4(struct psmouse *psmouse) static void alps_set_defaults(struct alps_data *priv) { + priv->byte0 = 0x8f; + priv->mask0 = 0x8f; + priv->flags = ALPS_DUALPOINT; + switch (priv->proto_version) { case ALPS_PROTO_V1: case ALPS_PROTO_V2: @@ -1491,8 +1493,15 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv) alps_exit_command_mode(psmouse)) return -EIO; - if (alps_match_table(psmouse, priv, e7, ec) == 0) + if (alps_match_table(psmouse, priv, e7, ec) == 0) { return 0; + } else if (ec[0] == 0x88 && ec[1] == 0x07 && + ec[2] >= 0x90 && ec[2] <= 0x9d) { + priv->proto_version = ALPS_PROTO_V3; + alps_set_defaults(priv); + + return 0; + } psmouse_info(psmouse, "Unknown ALPS touchpad: E7=%2.2x %2.2x %2.2x, EC=%2.2x %2.2x %2.2x\n", From 56fd340ebb533cb45c5eaf350cf889c43c5911e6 Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Wed, 13 Feb 2013 22:24:22 -0800 Subject: [PATCH 44/55] Input: ALPS - fix command mode check Pinnacle class devices should return "88 07 xx" or "88 08 xx" when entering command mode. If either the first byte or the second byte is invalid, return an error. Signed-off-by: Kevin Cernekee Tested-by: Dave Turvene Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index eafeae279356..bfc193806fc1 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -983,7 +983,7 @@ static int alps_enter_command_mode(struct psmouse *psmouse, return -1; } - if (param[0] != 0x88 && param[1] != 0x07) { + if (param[0] != 0x88 || (param[1] != 0x07 && param[1] != 0x08)) { psmouse_dbg(psmouse, "unknown response while entering command mode\n"); return -1; From 7a9f73e75cd0dcd320166aa340141893bd1848d1 Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Wed, 13 Feb 2013 22:24:55 -0800 Subject: [PATCH 45/55] Input: ALPS - move pixel and bitmap info into alps_data struct Newer touchpads use different constants, so make them runtime- configurable. Signed-off-by: Kevin Cernekee Tested-by: Dave Turvene Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 47 ++++++++++++++++++++------------------ drivers/input/mouse/alps.h | 8 +++++++ 2 files changed, 33 insertions(+), 22 deletions(-) diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index bfc193806fc1..2cd8be774f91 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -27,12 +27,6 @@ /* * Definitions for ALPS version 3 and 4 command mode protocol */ -#define ALPS_V3_X_MAX 2000 -#define ALPS_V3_Y_MAX 1400 - -#define ALPS_BITMAP_X_BITS 15 -#define ALPS_BITMAP_Y_BITS 11 - #define ALPS_CMD_NIBBLE_10 0x01f2 static const struct alps_nibble_commands alps_v3_nibble_commands[] = { @@ -269,7 +263,8 @@ static void alps_process_packet_v1_v2(struct psmouse *psmouse) * These points are returned in x1, y1, x2, and y2 when the return value * is greater than 0. */ -static int alps_process_bitmap(unsigned int x_map, unsigned int y_map, +static int alps_process_bitmap(struct alps_data *priv, + unsigned int x_map, unsigned int y_map, int *x1, int *y1, int *x2, int *y2) { struct alps_bitmap_point { @@ -311,7 +306,7 @@ static int alps_process_bitmap(unsigned int x_map, unsigned int y_map, * y bitmap is reversed for what we need (lower positions are in * higher bits), so we process from the top end. */ - y_map = y_map << (sizeof(y_map) * BITS_PER_BYTE - ALPS_BITMAP_Y_BITS); + y_map = y_map << (sizeof(y_map) * BITS_PER_BYTE - priv->y_bits); prev_bit = 0; point = &y_low; for (i = 0; y_map != 0; i++, y_map <<= 1) { @@ -357,16 +352,18 @@ static int alps_process_bitmap(unsigned int x_map, unsigned int y_map, } } - *x1 = (ALPS_V3_X_MAX * (2 * x_low.start_bit + x_low.num_bits - 1)) / - (2 * (ALPS_BITMAP_X_BITS - 1)); - *y1 = (ALPS_V3_Y_MAX * (2 * y_low.start_bit + y_low.num_bits - 1)) / - (2 * (ALPS_BITMAP_Y_BITS - 1)); + *x1 = (priv->x_max * (2 * x_low.start_bit + x_low.num_bits - 1)) / + (2 * (priv->x_bits - 1)); + *y1 = (priv->y_max * (2 * y_low.start_bit + y_low.num_bits - 1)) / + (2 * (priv->y_bits - 1)); if (fingers > 1) { - *x2 = (ALPS_V3_X_MAX * (2 * x_high.start_bit + x_high.num_bits - 1)) / - (2 * (ALPS_BITMAP_X_BITS - 1)); - *y2 = (ALPS_V3_Y_MAX * (2 * y_high.start_bit + y_high.num_bits - 1)) / - (2 * (ALPS_BITMAP_Y_BITS - 1)); + *x2 = (priv->x_max * + (2 * x_high.start_bit + x_high.num_bits - 1)) / + (2 * (priv->x_bits - 1)); + *y2 = (priv->y_max * + (2 * y_high.start_bit + y_high.num_bits - 1)) / + (2 * (priv->y_bits - 1)); } return fingers; @@ -484,7 +481,8 @@ static void alps_process_touchpad_packet_v3(struct psmouse *psmouse) ((packet[2] & 0x7f) << 1) | (packet[4] & 0x01); - bmap_fingers = alps_process_bitmap(x_bitmap, y_bitmap, + bmap_fingers = alps_process_bitmap(priv, + x_bitmap, y_bitmap, &x1, &y1, &x2, &y2); /* @@ -641,7 +639,7 @@ static void alps_process_packet_v4(struct psmouse *psmouse) ((priv->multi_data[3] & 0x1f) << 5) | (priv->multi_data[1] & 0x1f); - fingers = alps_process_bitmap(x_bitmap, y_bitmap, + fingers = alps_process_bitmap(priv, x_bitmap, y_bitmap, &x1, &y1, &x2, &y2); /* Store MT data.*/ @@ -1414,6 +1412,11 @@ static void alps_set_defaults(struct alps_data *priv) priv->mask0 = 0x8f; priv->flags = ALPS_DUALPOINT; + priv->x_max = 2000; + priv->y_max = 1400; + priv->x_bits = 15; + priv->y_bits = 11; + switch (priv->proto_version) { case ALPS_PROTO_V1: case ALPS_PROTO_V2: @@ -1544,15 +1547,15 @@ static void alps_set_abs_params_mt(struct alps_data *priv, { set_bit(INPUT_PROP_SEMI_MT, dev1->propbit); input_mt_init_slots(dev1, 2, 0); - input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, ALPS_V3_X_MAX, 0, 0); - input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, ALPS_V3_Y_MAX, 0, 0); + input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, priv->x_max, 0, 0); + input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, priv->y_max, 0, 0); set_bit(BTN_TOOL_DOUBLETAP, dev1->keybit); set_bit(BTN_TOOL_TRIPLETAP, dev1->keybit); set_bit(BTN_TOOL_QUADTAP, dev1->keybit); - input_set_abs_params(dev1, ABS_X, 0, ALPS_V3_X_MAX, 0, 0); - input_set_abs_params(dev1, ABS_Y, 0, ALPS_V3_Y_MAX, 0, 0); + input_set_abs_params(dev1, ABS_X, 0, priv->x_max, 0, 0); + input_set_abs_params(dev1, ABS_Y, 0, priv->y_max, 0, 0); } int alps_init(struct psmouse *psmouse) diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h index 0934f8bf7d76..5e638be33dc9 100644 --- a/drivers/input/mouse/alps.h +++ b/drivers/input/mouse/alps.h @@ -72,6 +72,10 @@ struct alps_nibble_commands { * mask0, should match byte0. * @mask0: The mask used to check the first byte of the report. * @flags: Additional device capabilities (passthrough port, trackstick, etc.). + * @x_max: Largest possible X position value. + * @y_max: Largest possible Y position value. + * @x_bits: Number of X bits in the MT bitmap. + * @y_bits: Number of Y bits in the MT bitmap. * @hw_init: Protocol-specific hardware init function. * @process_packet: Protocol-specific function to process a report packet. * @set_abs_params: Protocol-specific function to configure the input_dev. @@ -96,6 +100,10 @@ struct alps_data { unsigned char proto_version; unsigned char byte0, mask0; unsigned char flags; + int x_max; + int y_max; + int x_bits; + int y_bits; int (*hw_init)(struct psmouse *psmouse); void (*process_packet)(struct psmouse *psmouse); From f85e5001cce0298a44e35e2f8acbf9abeadb6c03 Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Wed, 13 Feb 2013 22:26:11 -0800 Subject: [PATCH 46/55] Input: ALPS - make the V3 packet field decoder "pluggable" A number of different ALPS touchpad protocols can reuse alps_process_touchpad_packet_v3() with small tweaks to the bitfield decoding. Create a new priv->decode_fields() callback that handles the per-model differences. Signed-off-by: Kevin Cernekee Tested-by: Dave Turvene Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 101 +++++++++++++++++++++---------------- drivers/input/mouse/alps.h | 38 ++++++++++++++ 2 files changed, 95 insertions(+), 44 deletions(-) diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 2cd8be774f91..270b7deefe5b 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -447,17 +447,49 @@ static void alps_process_trackstick_packet_v3(struct psmouse *psmouse) return; } +static void alps_decode_buttons_v3(struct alps_fields *f, unsigned char *p) +{ + f->left = !!(p[3] & 0x01); + f->right = !!(p[3] & 0x02); + f->middle = !!(p[3] & 0x04); + + f->ts_left = !!(p[3] & 0x10); + f->ts_right = !!(p[3] & 0x20); + f->ts_middle = !!(p[3] & 0x40); +} + +static void alps_decode_pinnacle(struct alps_fields *f, unsigned char *p) +{ + f->first_mp = !!(p[4] & 0x40); + f->is_mp = !!(p[0] & 0x40); + + f->fingers = (p[5] & 0x3) + 1; + f->x_map = ((p[4] & 0x7e) << 8) | + ((p[1] & 0x7f) << 2) | + ((p[0] & 0x30) >> 4); + f->y_map = ((p[3] & 0x70) << 4) | + ((p[2] & 0x7f) << 1) | + (p[4] & 0x01); + + f->x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) | + ((p[0] & 0x30) >> 4); + f->y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f); + f->z = p[5] & 0x7f; + + alps_decode_buttons_v3(f, p); +} + static void alps_process_touchpad_packet_v3(struct psmouse *psmouse) { struct alps_data *priv = psmouse->private; unsigned char *packet = psmouse->packet; struct input_dev *dev = psmouse->dev; struct input_dev *dev2 = priv->dev2; - int x, y, z; - int left, right, middle; int x1 = 0, y1 = 0, x2 = 0, y2 = 0; int fingers = 0, bmap_fingers; - unsigned int x_bitmap, y_bitmap; + struct alps_fields f; + + priv->decode_fields(&f, packet); /* * There's no single feature of touchpad position and bitmap packets @@ -472,17 +504,10 @@ static void alps_process_touchpad_packet_v3(struct psmouse *psmouse) * packet. Check for this, and when it happens process the * position packet as usual. */ - if (packet[0] & 0x40) { - fingers = (packet[5] & 0x3) + 1; - x_bitmap = ((packet[4] & 0x7e) << 8) | - ((packet[1] & 0x7f) << 2) | - ((packet[0] & 0x30) >> 4); - y_bitmap = ((packet[3] & 0x70) << 4) | - ((packet[2] & 0x7f) << 1) | - (packet[4] & 0x01); - + if (f.is_mp) { + fingers = f.fingers; bmap_fingers = alps_process_bitmap(priv, - x_bitmap, y_bitmap, + f.x_map, f.y_map, &x1, &y1, &x2, &y2); /* @@ -493,7 +518,7 @@ static void alps_process_touchpad_packet_v3(struct psmouse *psmouse) fingers = bmap_fingers; /* Now process position packet */ - packet = priv->multi_data; + priv->decode_fields(&f, priv->multi_data); } else { priv->multi_packet = 0; } @@ -507,10 +532,10 @@ static void alps_process_touchpad_packet_v3(struct psmouse *psmouse) * out misidentified bitmap packets, we reject anything with this * bit set. */ - if (packet[0] & 0x40) + if (f.is_mp) return; - if (!priv->multi_packet && (packet[4] & 0x40)) { + if (!priv->multi_packet && f.first_mp) { priv->multi_packet = 1; memcpy(priv->multi_data, packet, sizeof(priv->multi_data)); return; @@ -518,22 +543,13 @@ static void alps_process_touchpad_packet_v3(struct psmouse *psmouse) priv->multi_packet = 0; - left = packet[3] & 0x01; - right = packet[3] & 0x02; - middle = packet[3] & 0x04; - - x = ((packet[1] & 0x7f) << 4) | ((packet[4] & 0x30) >> 2) | - ((packet[0] & 0x30) >> 4); - y = ((packet[2] & 0x7f) << 4) | (packet[4] & 0x0f); - z = packet[5] & 0x7f; - /* * Sometimes the hardware sends a single packet with z = 0 * in the middle of a stream. Real releases generate packets * with x, y, and z all zero, so these seem to be flukes. * Ignore them. */ - if (x && y && !z) + if (f.x && f.y && !f.z) return; /* @@ -541,12 +557,12 @@ static void alps_process_touchpad_packet_v3(struct psmouse *psmouse) * to rely on ST data. */ if (!fingers) { - x1 = x; - y1 = y; - fingers = z > 0 ? 1 : 0; + x1 = f.x; + y1 = f.y; + fingers = f.z > 0 ? 1 : 0; } - if (z >= 64) + if (f.z >= 64) input_report_key(dev, BTN_TOUCH, 1); else input_report_key(dev, BTN_TOUCH, 0); @@ -555,26 +571,22 @@ static void alps_process_touchpad_packet_v3(struct psmouse *psmouse) input_mt_report_finger_count(dev, fingers); - input_report_key(dev, BTN_LEFT, left); - input_report_key(dev, BTN_RIGHT, right); - input_report_key(dev, BTN_MIDDLE, middle); + input_report_key(dev, BTN_LEFT, f.left); + input_report_key(dev, BTN_RIGHT, f.right); + input_report_key(dev, BTN_MIDDLE, f.middle); - if (z > 0) { - input_report_abs(dev, ABS_X, x); - input_report_abs(dev, ABS_Y, y); + if (f.z > 0) { + input_report_abs(dev, ABS_X, f.x); + input_report_abs(dev, ABS_Y, f.y); } - input_report_abs(dev, ABS_PRESSURE, z); + input_report_abs(dev, ABS_PRESSURE, f.z); input_sync(dev); if (!(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS)) { - left = packet[3] & 0x10; - right = packet[3] & 0x20; - middle = packet[3] & 0x40; - - input_report_key(dev2, BTN_LEFT, left); - input_report_key(dev2, BTN_RIGHT, right); - input_report_key(dev2, BTN_MIDDLE, middle); + input_report_key(dev2, BTN_LEFT, f.ts_left); + input_report_key(dev2, BTN_RIGHT, f.ts_right); + input_report_key(dev2, BTN_MIDDLE, f.ts_middle); input_sync(dev2); } } @@ -1428,6 +1440,7 @@ static void alps_set_defaults(struct alps_data *priv) priv->hw_init = alps_hw_init_v3; priv->process_packet = alps_process_packet_v3; priv->set_abs_params = alps_set_abs_params_mt; + priv->decode_fields = alps_decode_pinnacle; priv->nibble_commands = alps_v3_nibble_commands; priv->addr_command = PSMOUSE_CMD_RESET_WRAP; break; diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h index 5e638be33dc9..970480551b6e 100644 --- a/drivers/input/mouse/alps.h +++ b/drivers/input/mouse/alps.h @@ -59,6 +59,42 @@ struct alps_nibble_commands { unsigned char data; }; +/** + * struct alps_fields - decoded version of the report packet + * @x_map: Bitmap of active X positions for MT. + * @y_map: Bitmap of active Y positions for MT. + * @fingers: Number of fingers for MT. + * @x: X position for ST. + * @y: Y position for ST. + * @z: Z position for ST. + * @first_mp: Packet is the first of a multi-packet report. + * @is_mp: Packet is part of a multi-packet report. + * @left: Left touchpad button is active. + * @right: Right touchpad button is active. + * @middle: Middle touchpad button is active. + * @ts_left: Left trackstick button is active. + * @ts_right: Right trackstick button is active. + * @ts_middle: Middle trackstick button is active. + */ +struct alps_fields { + unsigned int x_map; + unsigned int y_map; + unsigned int fingers; + unsigned int x; + unsigned int y; + unsigned int z; + unsigned int first_mp:1; + unsigned int is_mp:1; + + unsigned int left:1; + unsigned int right:1; + unsigned int middle:1; + + unsigned int ts_left:1; + unsigned int ts_right:1; + unsigned int ts_middle:1; +}; + /** * struct alps_data - private data structure for the ALPS driver * @dev2: "Relative" device used to report trackstick or mouse activity. @@ -78,6 +114,7 @@ struct alps_nibble_commands { * @y_bits: Number of Y bits in the MT bitmap. * @hw_init: Protocol-specific hardware init function. * @process_packet: Protocol-specific function to process a report packet. + * @decode_fields: Protocol-specific function to read packet bitfields. * @set_abs_params: Protocol-specific function to configure the input_dev. * @prev_fin: Finger bit from previous packet. * @multi_packet: Multi-packet data in progress. @@ -107,6 +144,7 @@ struct alps_data { int (*hw_init)(struct psmouse *psmouse); void (*process_packet)(struct psmouse *psmouse); + void (*decode_fields)(struct alps_fields *f, unsigned char *p); void (*set_abs_params)(struct alps_data *priv, struct input_dev *dev1); int prev_fin; From 1302bac33d9e88cd43e482191a806998f3ed43cc Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Wed, 13 Feb 2013 22:27:08 -0800 Subject: [PATCH 47/55] Input: ALPS - add support for "Rushmore" touchpads Rushmore touchpads are found on Dell E6230/E6430/E6530. They use the V3 protocol with slightly tweaked init sequences and report formats. The E7 report is 73 03 0a, and the EC report is 88 08 1d Credits: Emmanuel Thome reported the MT bitmap changes. Signed-off-by: Kevin Cernekee Tested-by: Dave Turvene Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 52 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index 270b7deefe5b..bf2fa51853e3 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -479,6 +479,14 @@ static void alps_decode_pinnacle(struct alps_fields *f, unsigned char *p) alps_decode_buttons_v3(f, p); } +static void alps_decode_rushmore(struct alps_fields *f, unsigned char *p) +{ + alps_decode_pinnacle(f, p); + + f->x_map |= (p[5] & 0x10) << 11; + f->y_map |= (p[5] & 0x20) << 6; +} + static void alps_process_touchpad_packet_v3(struct psmouse *psmouse) { struct alps_data *priv = psmouse->private; @@ -1329,6 +1337,40 @@ static int alps_hw_init_v3(struct psmouse *psmouse) return -1; } +static int alps_hw_init_rushmore_v3(struct psmouse *psmouse) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + int reg_val, ret = -1; + + if (alps_enter_command_mode(psmouse, NULL) || + alps_command_mode_read_reg(psmouse, 0xc2d9) == -1 || + alps_command_mode_write_reg(psmouse, 0xc2cb, 0x00)) + goto error; + + reg_val = alps_command_mode_read_reg(psmouse, 0xc2c6); + if (reg_val == -1) + goto error; + if (__alps_command_mode_write_reg(psmouse, reg_val & 0xfd)) + goto error; + + if (alps_command_mode_write_reg(psmouse, 0xc2c9, 0x64)) + goto error; + + /* enter absolute mode */ + reg_val = alps_command_mode_read_reg(psmouse, 0xc2c4); + if (reg_val == -1) + goto error; + if (__alps_command_mode_write_reg(psmouse, reg_val | 0x02)) + goto error; + + alps_exit_command_mode(psmouse); + return ps2_command(ps2dev, NULL, PSMOUSE_CMD_ENABLE); + +error: + alps_exit_command_mode(psmouse); + return ret; +} + /* Must be in command mode when calling this function */ static int alps_absolute_mode_v4(struct psmouse *psmouse) { @@ -1510,6 +1552,16 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv) return -EIO; if (alps_match_table(psmouse, priv, e7, ec) == 0) { + return 0; + } else if (ec[0] == 0x88 && ec[1] == 0x08) { + priv->proto_version = ALPS_PROTO_V3; + alps_set_defaults(priv); + + priv->hw_init = alps_hw_init_rushmore_v3; + priv->decode_fields = alps_decode_rushmore; + priv->x_bits = 16; + priv->y_bits = 12; + return 0; } else if (ec[0] == 0x88 && ec[1] == 0x07 && ec[2] >= 0x90 && ec[2] <= 0x9d) { From cd401204873101245287afc07271b39c79194d9c Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Wed, 13 Feb 2013 22:28:07 -0800 Subject: [PATCH 48/55] Input: ALPS - enable trackstick on Rushmore touchpads Separate out the common trackstick probe/setup sequences, then call them from each of the v3 init functions. Credits: Emmanual Thome furnished the information on the trackstick init and how it affected the report format. Signed-off-by: Kevin Cernekee Tested-by: Dave Turvene Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/alps.c | 197 +++++++++++++++++++++++-------------- 1 file changed, 121 insertions(+), 76 deletions(-) diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c index bf2fa51853e3..7b99fc7c9438 100644 --- a/drivers/input/mouse/alps.c +++ b/drivers/input/mouse/alps.c @@ -29,6 +29,9 @@ */ #define ALPS_CMD_NIBBLE_10 0x01f2 +#define ALPS_REG_BASE_RUSHMORE 0xc2c0 +#define ALPS_REG_BASE_PINNACLE 0x0000 + static const struct alps_nibble_commands alps_v3_nibble_commands[] = { { PSMOUSE_CMD_SETPOLL, 0x00 }, /* 0 */ { PSMOUSE_CMD_RESET_DIS, 0x00 }, /* 1 */ @@ -1166,26 +1169,31 @@ static int alps_hw_init_v1_v2(struct psmouse *psmouse) } /* - * Enable or disable passthrough mode to the trackstick. Must be in - * command mode when calling this function. + * Enable or disable passthrough mode to the trackstick. */ -static int alps_passthrough_mode_v3(struct psmouse *psmouse, bool enable) +static int alps_passthrough_mode_v3(struct psmouse *psmouse, + int reg_base, bool enable) { - int reg_val; + int reg_val, ret = -1; - reg_val = alps_command_mode_read_reg(psmouse, 0x0008); - if (reg_val == -1) + if (alps_enter_command_mode(psmouse, NULL)) return -1; + reg_val = alps_command_mode_read_reg(psmouse, reg_base + 0x0008); + if (reg_val == -1) + goto error; + if (enable) reg_val |= 0x01; else reg_val &= ~0x01; - if (__alps_command_mode_write_reg(psmouse, reg_val)) - return -1; + ret = __alps_command_mode_write_reg(psmouse, reg_val); - return 0; +error: + if (alps_exit_command_mode(psmouse)) + ret = -1; + return ret; } /* Must be in command mode when calling this function */ @@ -1204,69 +1212,102 @@ static int alps_absolute_mode_v3(struct psmouse *psmouse) return 0; } +static int alps_probe_trackstick_v3(struct psmouse *psmouse, int reg_base) +{ + int ret = -EIO, reg_val; + + if (alps_enter_command_mode(psmouse, NULL)) + goto error; + + reg_val = alps_command_mode_read_reg(psmouse, reg_base + 0x08); + if (reg_val == -1) + goto error; + + /* bit 7: trackstick is present */ + ret = reg_val & 0x80 ? 0 : -ENODEV; + +error: + alps_exit_command_mode(psmouse); + return ret; +} + +static int alps_setup_trackstick_v3(struct psmouse *psmouse, int reg_base) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + int ret = 0; + unsigned char param[4]; + + if (alps_passthrough_mode_v3(psmouse, reg_base, true)) + return -EIO; + + /* + * E7 report for the trackstick + * + * There have been reports of failures to seem to trace back + * to the above trackstick check failing. When these occur + * this E7 report fails, so when that happens we continue + * with the assumption that there isn't a trackstick after + * all. + */ + if (alps_rpt_cmd(psmouse, 0, PSMOUSE_CMD_SETSCALE21, param)) { + psmouse_warn(psmouse, "trackstick E7 report failed\n"); + ret = -ENODEV; + } else { + psmouse_dbg(psmouse, + "trackstick E7 report: %2.2x %2.2x %2.2x\n", + param[0], param[1], param[2]); + + /* + * Not sure what this does, but it is absolutely + * essential. Without it, the touchpad does not + * work at all and the trackstick just emits normal + * PS/2 packets. + */ + if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || + ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || + alps_command_mode_send_nibble(psmouse, 0x9) || + alps_command_mode_send_nibble(psmouse, 0x4)) { + psmouse_err(psmouse, + "Error sending magic E6 sequence\n"); + ret = -EIO; + goto error; + } + + /* + * This ensures the trackstick packets are in the format + * supported by this driver. If bit 1 isn't set the packet + * format is different. + */ + if (alps_enter_command_mode(psmouse, NULL) || + alps_command_mode_write_reg(psmouse, + reg_base + 0x08, 0x82) || + alps_exit_command_mode(psmouse)) + ret = -EIO; + } + +error: + if (alps_passthrough_mode_v3(psmouse, reg_base, false)) + ret = -EIO; + + return ret; +} + static int alps_hw_init_v3(struct psmouse *psmouse) { struct ps2dev *ps2dev = &psmouse->ps2dev; int reg_val; unsigned char param[4]; - if (alps_enter_command_mode(psmouse, NULL)) + reg_val = alps_probe_trackstick_v3(psmouse, ALPS_REG_BASE_PINNACLE); + if (reg_val == -EIO) + goto error; + if (reg_val == 0 && + alps_setup_trackstick_v3(psmouse, ALPS_REG_BASE_PINNACLE) == -EIO) goto error; - /* Check for trackstick */ - reg_val = alps_command_mode_read_reg(psmouse, 0x0008); - if (reg_val == -1) - goto error; - if (reg_val & 0x80) { - if (alps_passthrough_mode_v3(psmouse, true)) - goto error; - if (alps_exit_command_mode(psmouse)) - goto error; - - /* - * E7 report for the trackstick - * - * There have been reports of failures to seem to trace back - * to the above trackstick check failing. When these occur - * this E7 report fails, so when that happens we continue - * with the assumption that there isn't a trackstick after - * all. - */ - param[0] = 0x64; - if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE21) || - ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) { - psmouse_warn(psmouse, "trackstick E7 report failed\n"); - } else { - psmouse_dbg(psmouse, - "trackstick E7 report: %2.2x %2.2x %2.2x\n", - param[0], param[1], param[2]); - - /* - * Not sure what this does, but it is absolutely - * essential. Without it, the touchpad does not - * work at all and the trackstick just emits normal - * PS/2 packets. - */ - if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || - ps2_command(ps2dev, NULL, PSMOUSE_CMD_SETSCALE11) || - alps_command_mode_send_nibble(psmouse, 0x9) || - alps_command_mode_send_nibble(psmouse, 0x4)) { - psmouse_err(psmouse, - "Error sending magic E6 sequence\n"); - goto error_passthrough; - } - } - - if (alps_enter_command_mode(psmouse, NULL)) - goto error_passthrough; - if (alps_passthrough_mode_v3(psmouse, false)) - goto error; - } - - if (alps_absolute_mode_v3(psmouse)) { + if (alps_enter_command_mode(psmouse, NULL) || + alps_absolute_mode_v3(psmouse)) { psmouse_err(psmouse, "Failed to enter absolute mode\n"); goto error; } @@ -1303,14 +1344,6 @@ static int alps_hw_init_v3(struct psmouse *psmouse) if (alps_command_mode_write_reg(psmouse, 0x0162, 0x04)) goto error; - /* - * This ensures the trackstick packets are in the format - * supported by this driver. If bit 1 isn't set the packet - * format is different. - */ - if (alps_command_mode_write_reg(psmouse, 0x0008, 0x82)) - goto error; - alps_exit_command_mode(psmouse); /* Set rate and enable data reporting */ @@ -1323,10 +1356,6 @@ static int alps_hw_init_v3(struct psmouse *psmouse) return 0; -error_passthrough: - /* Something failed while in passthrough mode, so try to get out */ - if (!alps_enter_command_mode(psmouse, NULL)) - alps_passthrough_mode_v3(psmouse, false); error: /* * Leaving the touchpad in command mode will essentially render @@ -1339,9 +1368,19 @@ static int alps_hw_init_v3(struct psmouse *psmouse) static int alps_hw_init_rushmore_v3(struct psmouse *psmouse) { + struct alps_data *priv = psmouse->private; struct ps2dev *ps2dev = &psmouse->ps2dev; int reg_val, ret = -1; + if (priv->flags & ALPS_DUALPOINT) { + reg_val = alps_setup_trackstick_v3(psmouse, + ALPS_REG_BASE_RUSHMORE); + if (reg_val == -EIO) + goto error; + if (reg_val == -ENODEV) + priv->flags &= ~ALPS_DUALPOINT; + } + if (alps_enter_command_mode(psmouse, NULL) || alps_command_mode_read_reg(psmouse, 0xc2d9) == -1 || alps_command_mode_write_reg(psmouse, 0xc2cb, 0x00)) @@ -1562,6 +1601,12 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv) priv->x_bits = 16; priv->y_bits = 12; + /* hack to make addr_command, nibble_command available */ + psmouse->private = priv; + + if (alps_probe_trackstick_v3(psmouse, ALPS_REG_BASE_RUSHMORE)) + priv->flags &= ~ALPS_DUALPOINT; + return 0; } else if (ec[0] == 0x88 && ec[1] == 0x07 && ec[2] >= 0x90 && ec[2] <= 0x9d) { From 79f34d19d7d1ece9918afe079529e61f978eeebe Mon Sep 17 00:00:00 2001 From: Michael Trimarchi Date: Fri, 15 Feb 2013 14:42:51 -0800 Subject: [PATCH 49/55] Input: bma150 - fix checking pm_runtime_get_sync() return value When PM_RUNTIME is not defined, pm_runtime_get_sync() returns 1, see include/linux/pm_runtime.c::__pm_runtime_resume(), and the check of the return value was overlooking this, in this case bma150_open() would return 1 which is not expected by upper layers. Maybe the check for != -ENOSYS (Function not implemented) was meant to cover this, but pm_runtime_get_sync() does not return this value. For now fix the issue locally by checking explicitly for negative return values. Signed-off-by: Michael Trimarchi Signed-off-by: Antonio Ospite Signed-off-by: Dmitry Torokhov --- drivers/input/misc/bma150.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/misc/bma150.c b/drivers/input/misc/bma150.c index 08ffcabd7220..e5d18946bb2e 100644 --- a/drivers/input/misc/bma150.c +++ b/drivers/input/misc/bma150.c @@ -372,7 +372,7 @@ static int bma150_open(struct bma150_data *bma150) int error; error = pm_runtime_get_sync(&bma150->client->dev); - if (error && error != -ENOSYS) + if (error < 0 && error != -ENOSYS) return error; /* From 0db3863add5cb249520b31a4ad36f275a30e7ba6 Mon Sep 17 00:00:00 2001 From: Michael Trimarchi Date: Fri, 15 Feb 2013 14:43:38 -0800 Subject: [PATCH 50/55] Input: bma150 - make some defines public and fix some comments Make the constants referring to range and bandwidth public so they can be used when initializing the platform data fields in the platform code. Fix also some comments regarding the unit of measurement to use for the range and bandwidth fields, the values are not actually expected to be in G or HZ, the code in bma150.c just uses the BMA150_RANGE_xxx and BMA150_BW_xxx constants like they are with no translation from actual values in G or HZ. Signed-off-by: Michael Trimarchi Signed-off-by: Antonio Ospite Signed-off-by: Dmitry Torokhov --- drivers/input/misc/bma150.c | 12 ------------ include/linux/bma150.h | 16 ++++++++++++++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/input/misc/bma150.c b/drivers/input/misc/bma150.c index e5d18946bb2e..865c2f9d25b9 100644 --- a/drivers/input/misc/bma150.c +++ b/drivers/input/misc/bma150.c @@ -46,18 +46,6 @@ #define BMA150_POLL_MAX 200 #define BMA150_POLL_MIN 0 -#define BMA150_BW_25HZ 0 -#define BMA150_BW_50HZ 1 -#define BMA150_BW_100HZ 2 -#define BMA150_BW_190HZ 3 -#define BMA150_BW_375HZ 4 -#define BMA150_BW_750HZ 5 -#define BMA150_BW_1500HZ 6 - -#define BMA150_RANGE_2G 0 -#define BMA150_RANGE_4G 1 -#define BMA150_RANGE_8G 2 - #define BMA150_MODE_NORMAL 0 #define BMA150_MODE_SLEEP 2 #define BMA150_MODE_WAKE_UP 3 diff --git a/include/linux/bma150.h b/include/linux/bma150.h index 7911fda23bb4..97ade7cdc870 100644 --- a/include/linux/bma150.h +++ b/include/linux/bma150.h @@ -22,6 +22,18 @@ #define BMA150_DRIVER "bma150" +#define BMA150_RANGE_2G 0 +#define BMA150_RANGE_4G 1 +#define BMA150_RANGE_8G 2 + +#define BMA150_BW_25HZ 0 +#define BMA150_BW_50HZ 1 +#define BMA150_BW_100HZ 2 +#define BMA150_BW_190HZ 3 +#define BMA150_BW_375HZ 4 +#define BMA150_BW_750HZ 5 +#define BMA150_BW_1500HZ 6 + struct bma150_cfg { bool any_motion_int; /* Set to enable any-motion interrupt */ bool hg_int; /* Set to enable high-G interrupt */ @@ -34,8 +46,8 @@ struct bma150_cfg { unsigned char lg_hyst; /* Low-G hysterisis */ unsigned char lg_dur; /* Low-G duration */ unsigned char lg_thres; /* Low-G threshold */ - unsigned char range; /* BMA0150_RANGE_xxx (in G) */ - unsigned char bandwidth; /* BMA0150_BW_xxx (in Hz) */ + unsigned char range; /* one of BMA0150_RANGE_xxx */ + unsigned char bandwidth; /* one of BMA0150_BW_xxx */ }; struct bma150_platform_data { From 352ce2b0575f195c8e6452a9802e6c186b39409a Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Fri, 15 Feb 2013 17:03:57 -0800 Subject: [PATCH 51/55] Input: MT - do not apply filtering on emulated events The pointer emulation events are derived from contact values that have already been filtered, so send the emulated events as is. Reported-by: Daniel Kurtz Signed-off-by: Henrik Rydberg Signed-off-by: Dmitry Torokhov --- drivers/input/input-mt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c index c2f436ce7f5b..53eaf89f2db5 100644 --- a/drivers/input/input-mt.c +++ b/drivers/input/input-mt.c @@ -18,6 +18,7 @@ static void copy_abs(struct input_dev *dev, unsigned int dst, unsigned int src) { if (dev->absinfo && test_bit(src, dev->absbit)) { dev->absinfo[dst] = dev->absinfo[src]; + dev->absinfo[dst].fuzz = 0; dev->absbit[BIT_WORD(dst)] |= BIT_MASK(dst); } } From 0b85bf78d4aedfdd4bcb1b848045111a5b840310 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Fri, 15 Feb 2013 17:04:03 -0800 Subject: [PATCH 52/55] Input: synaptics - initialize pointer emulation usage To properly setup event parameters for emulated events, pass the appropriate flag to the slot initialization function. Also, all MT-related events should be setup before initialization. Incidentally, this solves the issue of doubly filtered pointer events. Reported-by: Daniel Kurtz Signed-off-by: Henrik Rydberg Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/synaptics.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index c3d1c86ac591..2f78538e09d0 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -1271,11 +1271,11 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv) input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0); if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) { - input_mt_init_slots(dev, 2, 0); set_abs_position_params(dev, priv, ABS_MT_POSITION_X, ABS_MT_POSITION_Y); /* Image sensors can report per-contact pressure */ input_set_abs_params(dev, ABS_MT_PRESSURE, 0, 255, 0, 0); + input_mt_init_slots(dev, 2, INPUT_MT_POINTER); /* Image sensors can signal 4 and 5 finger clicks */ __set_bit(BTN_TOOL_QUADTAP, dev->keybit); From 9eee07d39fa606a191ae65d3c0e12771a80e70ca Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Fri, 15 Feb 2013 17:04:12 -0800 Subject: [PATCH 53/55] Input: tegra-kbc - require CONFIG_OF, remove platform data Tegra only supports, and always enables, device tree. Remove all ifdefs and runtime checks for DT support from the driver. Platform data is therefore no longer required. Delete the header that defines it, and rework the driver to parse the device tree directly into struct tegra_kbc. Signed-off-by: Stephen Warren Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/Kconfig | 2 +- drivers/input/keyboard/tegra-kbc.c | 195 ++++++++++++++--------------- include/linux/input/tegra_kbc.h | 62 --------- 3 files changed, 93 insertions(+), 166 deletions(-) delete mode 100644 include/linux/input/tegra_kbc.h diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 72377737eec8..992137cf3a64 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -420,7 +420,7 @@ config KEYBOARD_NOMADIK config KEYBOARD_TEGRA tristate "NVIDIA Tegra internal matrix keyboard controller support" - depends on ARCH_TEGRA + depends on ARCH_TEGRA && OF select INPUT_MATRIXKMAP help Say Y here if you want to use a matrix keyboard connected directly diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c index 4526bdd36022..d89e7d392d1e 100644 --- a/drivers/input/keyboard/tegra-kbc.c +++ b/drivers/input/keyboard/tegra-kbc.c @@ -29,9 +29,16 @@ #include #include #include -#include +#include #include +#define KBC_MAX_GPIO 24 +#define KBC_MAX_KPENT 8 + +#define KBC_MAX_ROW 16 +#define KBC_MAX_COL 8 +#define KBC_MAX_KEY (KBC_MAX_ROW * KBC_MAX_COL) + #define KBC_MAX_DEBOUNCE_CNT 0x3ffu /* KBC row scan time and delay for beginning the row scan. */ @@ -67,10 +74,27 @@ #define KBC_ROW_SHIFT 3 +enum tegra_pin_type { + PIN_CFG_IGNORE, + PIN_CFG_COL, + PIN_CFG_ROW, +}; + +struct tegra_kbc_pin_cfg { + enum tegra_pin_type type; + unsigned char num; +}; + struct tegra_kbc { + struct device *dev; + unsigned int debounce_cnt; + unsigned int repeat_cnt; + struct tegra_kbc_pin_cfg pin_cfg[KBC_MAX_GPIO]; + const struct matrix_keymap_data *keymap_data; + bool wakeup; void __iomem *mmio; struct input_dev *idev; - unsigned int irq; + int irq; spinlock_t lock; unsigned int repoll_dly; unsigned long cp_dly_jiffies; @@ -78,7 +102,6 @@ struct tegra_kbc { bool use_fn_map; bool use_ghost_filter; bool keypress_caused_wake; - const struct tegra_kbc_platform_data *pdata; unsigned short keycode[KBC_MAX_KEY * 2]; unsigned short current_keys[KBC_MAX_KPENT]; unsigned int num_pressed_keys; @@ -286,12 +309,11 @@ static irqreturn_t tegra_kbc_isr(int irq, void *args) static void tegra_kbc_setup_wakekeys(struct tegra_kbc *kbc, bool filter) { - const struct tegra_kbc_platform_data *pdata = kbc->pdata; int i; unsigned int rst_val; /* Either mask all keys or none. */ - rst_val = (filter && !pdata->wakeup) ? ~0 : 0; + rst_val = (filter && !kbc->wakeup) ? ~0 : 0; for (i = 0; i < KBC_MAX_ROW; i++) writel(rst_val, kbc->mmio + KBC_ROW0_MASK_0 + i * 4); @@ -299,7 +321,6 @@ static void tegra_kbc_setup_wakekeys(struct tegra_kbc *kbc, bool filter) static void tegra_kbc_config_pins(struct tegra_kbc *kbc) { - const struct tegra_kbc_platform_data *pdata = kbc->pdata; int i; for (i = 0; i < KBC_MAX_GPIO; i++) { @@ -315,13 +336,13 @@ static void tegra_kbc_config_pins(struct tegra_kbc *kbc) row_cfg &= ~r_mask; col_cfg &= ~c_mask; - switch (pdata->pin_cfg[i].type) { + switch (kbc->pin_cfg[i].type) { case PIN_CFG_ROW: - row_cfg |= ((pdata->pin_cfg[i].num << 1) | 1) << r_shft; + row_cfg |= ((kbc->pin_cfg[i].num << 1) | 1) << r_shft; break; case PIN_CFG_COL: - col_cfg |= ((pdata->pin_cfg[i].num << 1) | 1) << c_shft; + col_cfg |= ((kbc->pin_cfg[i].num << 1) | 1) << c_shft; break; case PIN_CFG_IGNORE: @@ -335,7 +356,6 @@ static void tegra_kbc_config_pins(struct tegra_kbc *kbc) static int tegra_kbc_start(struct tegra_kbc *kbc) { - const struct tegra_kbc_platform_data *pdata = kbc->pdata; unsigned int debounce_cnt; u32 val = 0; @@ -350,10 +370,10 @@ static int tegra_kbc_start(struct tegra_kbc *kbc) tegra_kbc_config_pins(kbc); tegra_kbc_setup_wakekeys(kbc, false); - writel(pdata->repeat_cnt, kbc->mmio + KBC_RPT_DLY_0); + writel(kbc->repeat_cnt, kbc->mmio + KBC_RPT_DLY_0); /* Keyboard debounce count is maximum of 12 bits. */ - debounce_cnt = min(pdata->debounce_cnt, KBC_MAX_DEBOUNCE_CNT); + debounce_cnt = min(kbc->debounce_cnt, KBC_MAX_DEBOUNCE_CNT); val = KBC_DEBOUNCE_CNT_SHIFT(debounce_cnt); val |= KBC_FIFO_TH_CNT_SHIFT(1); /* set fifo interrupt threshold to 1 */ val |= KBC_CONTROL_FIFO_CNT_INT_EN; /* interrupt on FIFO threshold */ @@ -420,21 +440,20 @@ static void tegra_kbc_close(struct input_dev *dev) return tegra_kbc_stop(kbc); } -static bool -tegra_kbc_check_pin_cfg(const struct tegra_kbc_platform_data *pdata, - struct device *dev, unsigned int *num_rows) +static bool tegra_kbc_check_pin_cfg(const struct tegra_kbc *kbc, + unsigned int *num_rows) { int i; *num_rows = 0; for (i = 0; i < KBC_MAX_GPIO; i++) { - const struct tegra_kbc_pin_cfg *pin_cfg = &pdata->pin_cfg[i]; + const struct tegra_kbc_pin_cfg *pin_cfg = &kbc->pin_cfg[i]; switch (pin_cfg->type) { case PIN_CFG_ROW: if (pin_cfg->num >= KBC_MAX_ROW) { - dev_err(dev, + dev_err(kbc->dev, "pin_cfg[%d]: invalid row number %d\n", i, pin_cfg->num); return false; @@ -444,7 +463,7 @@ tegra_kbc_check_pin_cfg(const struct tegra_kbc_platform_data *pdata, case PIN_CFG_COL: if (pin_cfg->num >= KBC_MAX_COL) { - dev_err(dev, + dev_err(kbc->dev, "pin_cfg[%d]: invalid column number %d\n", i, pin_cfg->num); return false; @@ -455,7 +474,7 @@ tegra_kbc_check_pin_cfg(const struct tegra_kbc_platform_data *pdata, break; default: - dev_err(dev, + dev_err(kbc->dev, "pin_cfg[%d]: invalid entry type %d\n", pin_cfg->type, pin_cfg->num); return false; @@ -465,12 +484,9 @@ tegra_kbc_check_pin_cfg(const struct tegra_kbc_platform_data *pdata, return true; } -#ifdef CONFIG_OF -static struct tegra_kbc_platform_data *tegra_kbc_dt_parse_pdata( - struct platform_device *pdev) +static int tegra_kbc_parse_dt(struct tegra_kbc *kbc) { - struct tegra_kbc_platform_data *pdata; - struct device_node *np = pdev->dev.of_node; + struct device_node *np = kbc->dev->of_node; u32 prop; int i; u32 num_rows = 0; @@ -480,109 +496,96 @@ static struct tegra_kbc_platform_data *tegra_kbc_dt_parse_pdata( int proplen; int ret; - if (!np) { - dev_err(&pdev->dev, "device tree data is missing\n"); - return ERR_PTR(-ENOENT); - } - - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return ERR_PTR(-ENOMEM); - if (!of_property_read_u32(np, "nvidia,debounce-delay-ms", &prop)) - pdata->debounce_cnt = prop; + kbc->debounce_cnt = prop; if (!of_property_read_u32(np, "nvidia,repeat-delay-ms", &prop)) - pdata->repeat_cnt = prop; + kbc->repeat_cnt = prop; if (of_find_property(np, "nvidia,needs-ghost-filter", NULL)) - pdata->use_ghost_filter = true; + kbc->use_ghost_filter = true; if (of_find_property(np, "nvidia,wakeup-source", NULL)) - pdata->wakeup = true; + kbc->wakeup = true; if (!of_get_property(np, "nvidia,kbc-row-pins", &proplen)) { - dev_err(&pdev->dev, "property nvidia,kbc-row-pins not found\n"); - return ERR_PTR(-ENOENT); + dev_err(kbc->dev, "property nvidia,kbc-row-pins not found\n"); + return -ENOENT; } num_rows = proplen / sizeof(u32); if (!of_get_property(np, "nvidia,kbc-col-pins", &proplen)) { - dev_err(&pdev->dev, "property nvidia,kbc-col-pins not found\n"); - return ERR_PTR(-ENOENT); + dev_err(kbc->dev, "property nvidia,kbc-col-pins not found\n"); + return -ENOENT; } num_cols = proplen / sizeof(u32); if (!of_get_property(np, "linux,keymap", &proplen)) { - dev_err(&pdev->dev, "property linux,keymap not found\n"); - return ERR_PTR(-ENOENT); + dev_err(kbc->dev, "property linux,keymap not found\n"); + return -ENOENT; } if (!num_rows || !num_cols || ((num_rows + num_cols) > KBC_MAX_GPIO)) { - dev_err(&pdev->dev, + dev_err(kbc->dev, "keypad rows/columns not porperly specified\n"); - return ERR_PTR(-EINVAL); + return -EINVAL; } /* Set all pins as non-configured */ for (i = 0; i < KBC_MAX_GPIO; i++) - pdata->pin_cfg[i].type = PIN_CFG_IGNORE; + kbc->pin_cfg[i].type = PIN_CFG_IGNORE; ret = of_property_read_u32_array(np, "nvidia,kbc-row-pins", rows_cfg, num_rows); if (ret < 0) { - dev_err(&pdev->dev, "Rows configurations are not proper\n"); - return ERR_PTR(-EINVAL); + dev_err(kbc->dev, "Rows configurations are not proper\n"); + return -EINVAL; } ret = of_property_read_u32_array(np, "nvidia,kbc-col-pins", cols_cfg, num_cols); if (ret < 0) { - dev_err(&pdev->dev, "Cols configurations are not proper\n"); - return ERR_PTR(-EINVAL); + dev_err(kbc->dev, "Cols configurations are not proper\n"); + return -EINVAL; } for (i = 0; i < num_rows; i++) { - pdata->pin_cfg[rows_cfg[i]].type = PIN_CFG_ROW; - pdata->pin_cfg[rows_cfg[i]].num = i; + kbc->pin_cfg[rows_cfg[i]].type = PIN_CFG_ROW; + kbc->pin_cfg[rows_cfg[i]].num = i; } for (i = 0; i < num_cols; i++) { - pdata->pin_cfg[cols_cfg[i]].type = PIN_CFG_COL; - pdata->pin_cfg[cols_cfg[i]].num = i; + kbc->pin_cfg[cols_cfg[i]].type = PIN_CFG_COL; + kbc->pin_cfg[cols_cfg[i]].num = i; } - return pdata; + return 0; } -#else -static inline struct tegra_kbc_platform_data *tegra_kbc_dt_parse_pdata( - struct platform_device *pdev) -{ - dev_err(&pdev->dev, "platform data is missing\n"); - return ERR_PTR(-EINVAL); -} -#endif static int tegra_kbc_probe(struct platform_device *pdev) { - const struct tegra_kbc_platform_data *pdata = pdev->dev.platform_data; struct tegra_kbc *kbc; - struct input_dev *input_dev; struct resource *res; - int irq; int err; int num_rows = 0; unsigned int debounce_cnt; unsigned int scan_time_rows; unsigned int keymap_rows = KBC_MAX_KEY; - if (!pdata) - pdata = tegra_kbc_dt_parse_pdata(pdev); + kbc = devm_kzalloc(&pdev->dev, sizeof(*kbc), GFP_KERNEL); + if (!kbc) { + dev_err(&pdev->dev, "failed to alloc memory for kbc\n"); + return -ENOMEM; + } - if (IS_ERR(pdata)) - return PTR_ERR(pdata); + kbc->dev = &pdev->dev; + spin_lock_init(&kbc->lock); - if (!tegra_kbc_check_pin_cfg(pdata, &pdev->dev, &num_rows)) + err = tegra_kbc_parse_dt(kbc); + if (err) + return err; + + if (!tegra_kbc_check_pin_cfg(kbc, &num_rows)) return -EINVAL; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -591,28 +594,18 @@ static int tegra_kbc_probe(struct platform_device *pdev) return -ENXIO; } - irq = platform_get_irq(pdev, 0); - if (irq < 0) { + kbc->irq = platform_get_irq(pdev, 0); + if (kbc->irq < 0) { dev_err(&pdev->dev, "failed to get keyboard IRQ\n"); return -ENXIO; } - kbc = devm_kzalloc(&pdev->dev, sizeof(*kbc), GFP_KERNEL); - if (!kbc) { - dev_err(&pdev->dev, "failed to alloc memory for kbc\n"); - return -ENOMEM; - } - - input_dev = devm_input_allocate_device(&pdev->dev); - if (!input_dev) { + kbc->idev = devm_input_allocate_device(&pdev->dev); + if (!kbc->idev) { dev_err(&pdev->dev, "failed to allocate input device\n"); return -ENOMEM; } - kbc->pdata = pdata; - kbc->idev = input_dev; - kbc->irq = irq; - spin_lock_init(&kbc->lock); setup_timer(&kbc->timer, tegra_kbc_keypress_timer, (unsigned long)kbc); kbc->mmio = devm_request_and_ioremap(&pdev->dev, res); @@ -633,36 +626,32 @@ static int tegra_kbc_probe(struct platform_device *pdev) * the rows. There is an additional delay before the row scanning * starts. The repoll delay is computed in milliseconds. */ - debounce_cnt = min(pdata->debounce_cnt, KBC_MAX_DEBOUNCE_CNT); + debounce_cnt = min(kbc->debounce_cnt, KBC_MAX_DEBOUNCE_CNT); scan_time_rows = (KBC_ROW_SCAN_TIME + debounce_cnt) * num_rows; - kbc->repoll_dly = KBC_ROW_SCAN_DLY + scan_time_rows + pdata->repeat_cnt; + kbc->repoll_dly = KBC_ROW_SCAN_DLY + scan_time_rows + kbc->repeat_cnt; kbc->repoll_dly = DIV_ROUND_UP(kbc->repoll_dly, KBC_CYCLE_MS); - kbc->wakeup_key = pdata->wakeup_key; - kbc->use_fn_map = pdata->use_fn_map; - kbc->use_ghost_filter = pdata->use_ghost_filter; + kbc->idev->name = pdev->name; + kbc->idev->id.bustype = BUS_HOST; + kbc->idev->dev.parent = &pdev->dev; + kbc->idev->open = tegra_kbc_open; + kbc->idev->close = tegra_kbc_close; - input_dev->name = pdev->name; - input_dev->id.bustype = BUS_HOST; - input_dev->dev.parent = &pdev->dev; - input_dev->open = tegra_kbc_open; - input_dev->close = tegra_kbc_close; - - if (pdata->keymap_data && pdata->use_fn_map) + if (kbc->keymap_data && kbc->use_fn_map) keymap_rows *= 2; - err = matrix_keypad_build_keymap(pdata->keymap_data, NULL, + err = matrix_keypad_build_keymap(kbc->keymap_data, NULL, keymap_rows, KBC_MAX_COL, - kbc->keycode, input_dev); + kbc->keycode, kbc->idev); if (err) { dev_err(&pdev->dev, "failed to setup keymap\n"); return err; } - __set_bit(EV_REP, input_dev->evbit); - input_set_capability(input_dev, EV_MSC, MSC_SCAN); + __set_bit(EV_REP, kbc->idev->evbit); + input_set_capability(kbc->idev, EV_MSC, MSC_SCAN); - input_set_drvdata(input_dev, kbc); + input_set_drvdata(kbc->idev, kbc); err = devm_request_irq(&pdev->dev, kbc->irq, tegra_kbc_isr, IRQF_NO_SUSPEND | IRQF_TRIGGER_HIGH, pdev->name, kbc); @@ -680,7 +669,7 @@ static int tegra_kbc_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, kbc); - device_init_wakeup(&pdev->dev, pdata->wakeup); + device_init_wakeup(&pdev->dev, kbc->wakeup); return 0; } diff --git a/include/linux/input/tegra_kbc.h b/include/linux/input/tegra_kbc.h deleted file mode 100644 index a13025612939..000000000000 --- a/include/linux/input/tegra_kbc.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Platform definitions for tegra-kbc keyboard input driver - * - * Copyright (c) 2010-2011, NVIDIA 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; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#ifndef ASMARM_ARCH_TEGRA_KBC_H -#define ASMARM_ARCH_TEGRA_KBC_H - -#include -#include - -#define KBC_MAX_GPIO 24 -#define KBC_MAX_KPENT 8 - -#define KBC_MAX_ROW 16 -#define KBC_MAX_COL 8 -#define KBC_MAX_KEY (KBC_MAX_ROW * KBC_MAX_COL) - -enum tegra_pin_type { - PIN_CFG_IGNORE, - PIN_CFG_COL, - PIN_CFG_ROW, -}; - -struct tegra_kbc_pin_cfg { - enum tegra_pin_type type; - unsigned char num; -}; - -struct tegra_kbc_wake_key { - u8 row:4; - u8 col:4; -}; - -struct tegra_kbc_platform_data { - unsigned int debounce_cnt; - unsigned int repeat_cnt; - - struct tegra_kbc_pin_cfg pin_cfg[KBC_MAX_GPIO]; - const struct matrix_keymap_data *keymap_data; - - u32 wakeup_key; - bool wakeup; - bool use_fn_map; - bool use_ghost_filter; -}; -#endif From 938789fea130c451fde618ac43955beae6b90177 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Sat, 16 Feb 2013 22:01:44 -0800 Subject: [PATCH 54/55] Input: tsc2005 - add MODULE_ALIAS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This enables autoloading of tsc2005 driver when is compiled as a module. Signed-off-by: Pali Rohár Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/tsc2005.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/input/touchscreen/tsc2005.c b/drivers/input/touchscreen/tsc2005.c index 9c0cdc7ea449..7213e8b07e79 100644 --- a/drivers/input/touchscreen/tsc2005.c +++ b/drivers/input/touchscreen/tsc2005.c @@ -753,3 +753,4 @@ module_spi_driver(tsc2005_driver); MODULE_AUTHOR("Lauri Leukkunen "); MODULE_DESCRIPTION("TSC2005 Touchscreen Driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:tsc2005"); From 005a69d632cd8694061c2dd27492fe874780b5ee Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Sat, 16 Feb 2013 22:07:41 -0800 Subject: [PATCH 55/55] Input: cyttsp-spi - remove duplicate MODULE_ALIAS() Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/cyttsp_spi.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/input/touchscreen/cyttsp_spi.c b/drivers/input/touchscreen/cyttsp_spi.c index 638e20310f12..861b7f77605b 100644 --- a/drivers/input/touchscreen/cyttsp_spi.c +++ b/drivers/input/touchscreen/cyttsp_spi.c @@ -193,7 +193,6 @@ static struct spi_driver cyttsp_spi_driver = { module_spi_driver(cyttsp_spi_driver); -MODULE_ALIAS("spi:cyttsp"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard Product (TTSP) SPI driver"); MODULE_AUTHOR("Cypress");