mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-26 06:33:19 -04:00
Merge branch 'next' into for-linus
Prepare first round of input updates for 3.17.
This commit is contained in:
commit
5e2aa2ed08
9844 changed files with 502512 additions and 252808 deletions
|
|
@ -665,4 +665,14 @@ config KEYBOARD_CROS_EC
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called cros_ec_keyb.
|
||||
|
||||
config KEYBOARD_CAP1106
|
||||
tristate "Microchip CAP1106 touch sensor"
|
||||
depends on OF && I2C
|
||||
select REGMAP_I2C
|
||||
help
|
||||
Say Y here to enable the CAP1106 touch sensor driver.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called cap1106.
|
||||
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o
|
|||
obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o
|
||||
obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o
|
||||
obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o
|
||||
obj-$(CONFIG_KEYBOARD_CAP1106) += cap1106.o
|
||||
obj-$(CONFIG_KEYBOARD_CLPS711X) += clps711x-keypad.o
|
||||
obj-$(CONFIG_KEYBOARD_CROS_EC) += cros_ec_keyb.o
|
||||
obj-$(CONFIG_KEYBOARD_DAVINCI) += davinci_keyscan.o
|
||||
|
|
|
|||
335
drivers/input/keyboard/cap1106.c
Normal file
335
drivers/input/keyboard/cap1106.c
Normal file
|
|
@ -0,0 +1,335 @@
|
|||
/*
|
||||
* Input driver for Microchip CAP1106, 6 channel capacitive touch sensor
|
||||
*
|
||||
* http://www.microchip.com/wwwproducts/Devices.aspx?product=CAP1106
|
||||
*
|
||||
* (c) 2014 Daniel Mack <linux@zonque.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
|
||||
#define CAP1106_REG_MAIN_CONTROL 0x00
|
||||
#define CAP1106_REG_MAIN_CONTROL_GAIN_SHIFT (6)
|
||||
#define CAP1106_REG_MAIN_CONTROL_GAIN_MASK (0xc0)
|
||||
#define CAP1106_REG_MAIN_CONTROL_DLSEEP BIT(4)
|
||||
#define CAP1106_REG_GENERAL_STATUS 0x02
|
||||
#define CAP1106_REG_SENSOR_INPUT 0x03
|
||||
#define CAP1106_REG_NOISE_FLAG_STATUS 0x0a
|
||||
#define CAP1106_REG_SENOR_DELTA(X) (0x10 + (X))
|
||||
#define CAP1106_REG_SENSITIVITY_CONTROL 0x1f
|
||||
#define CAP1106_REG_CONFIG 0x20
|
||||
#define CAP1106_REG_SENSOR_ENABLE 0x21
|
||||
#define CAP1106_REG_SENSOR_CONFIG 0x22
|
||||
#define CAP1106_REG_SENSOR_CONFIG2 0x23
|
||||
#define CAP1106_REG_SAMPLING_CONFIG 0x24
|
||||
#define CAP1106_REG_CALIBRATION 0x25
|
||||
#define CAP1106_REG_INT_ENABLE 0x26
|
||||
#define CAP1106_REG_REPEAT_RATE 0x28
|
||||
#define CAP1106_REG_MT_CONFIG 0x2a
|
||||
#define CAP1106_REG_MT_PATTERN_CONFIG 0x2b
|
||||
#define CAP1106_REG_MT_PATTERN 0x2d
|
||||
#define CAP1106_REG_RECALIB_CONFIG 0x2f
|
||||
#define CAP1106_REG_SENSOR_THRESH(X) (0x30 + (X))
|
||||
#define CAP1106_REG_SENSOR_NOISE_THRESH 0x38
|
||||
#define CAP1106_REG_STANDBY_CHANNEL 0x40
|
||||
#define CAP1106_REG_STANDBY_CONFIG 0x41
|
||||
#define CAP1106_REG_STANDBY_SENSITIVITY 0x42
|
||||
#define CAP1106_REG_STANDBY_THRESH 0x43
|
||||
#define CAP1106_REG_CONFIG2 0x44
|
||||
#define CAP1106_REG_SENSOR_BASE_CNT(X) (0x50 + (X))
|
||||
#define CAP1106_REG_SENSOR_CALIB (0xb1 + (X))
|
||||
#define CAP1106_REG_SENSOR_CALIB_LSB1 0xb9
|
||||
#define CAP1106_REG_SENSOR_CALIB_LSB2 0xba
|
||||
#define CAP1106_REG_PRODUCT_ID 0xfd
|
||||
#define CAP1106_REG_MANUFACTURER_ID 0xfe
|
||||
#define CAP1106_REG_REVISION 0xff
|
||||
|
||||
#define CAP1106_NUM_CHN 6
|
||||
#define CAP1106_PRODUCT_ID 0x55
|
||||
#define CAP1106_MANUFACTURER_ID 0x5d
|
||||
|
||||
struct cap1106_priv {
|
||||
struct regmap *regmap;
|
||||
struct input_dev *idev;
|
||||
|
||||
/* config */
|
||||
unsigned int keycodes[CAP1106_NUM_CHN];
|
||||
};
|
||||
|
||||
static const struct reg_default cap1106_reg_defaults[] = {
|
||||
{ CAP1106_REG_MAIN_CONTROL, 0x00 },
|
||||
{ CAP1106_REG_GENERAL_STATUS, 0x00 },
|
||||
{ CAP1106_REG_SENSOR_INPUT, 0x00 },
|
||||
{ CAP1106_REG_NOISE_FLAG_STATUS, 0x00 },
|
||||
{ CAP1106_REG_SENSITIVITY_CONTROL, 0x2f },
|
||||
{ CAP1106_REG_CONFIG, 0x20 },
|
||||
{ CAP1106_REG_SENSOR_ENABLE, 0x3f },
|
||||
{ CAP1106_REG_SENSOR_CONFIG, 0xa4 },
|
||||
{ CAP1106_REG_SENSOR_CONFIG2, 0x07 },
|
||||
{ CAP1106_REG_SAMPLING_CONFIG, 0x39 },
|
||||
{ CAP1106_REG_CALIBRATION, 0x00 },
|
||||
{ CAP1106_REG_INT_ENABLE, 0x3f },
|
||||
{ CAP1106_REG_REPEAT_RATE, 0x3f },
|
||||
{ CAP1106_REG_MT_CONFIG, 0x80 },
|
||||
{ CAP1106_REG_MT_PATTERN_CONFIG, 0x00 },
|
||||
{ CAP1106_REG_MT_PATTERN, 0x3f },
|
||||
{ CAP1106_REG_RECALIB_CONFIG, 0x8a },
|
||||
{ CAP1106_REG_SENSOR_THRESH(0), 0x40 },
|
||||
{ CAP1106_REG_SENSOR_THRESH(1), 0x40 },
|
||||
{ CAP1106_REG_SENSOR_THRESH(2), 0x40 },
|
||||
{ CAP1106_REG_SENSOR_THRESH(3), 0x40 },
|
||||
{ CAP1106_REG_SENSOR_THRESH(4), 0x40 },
|
||||
{ CAP1106_REG_SENSOR_THRESH(5), 0x40 },
|
||||
{ CAP1106_REG_SENSOR_NOISE_THRESH, 0x01 },
|
||||
{ CAP1106_REG_STANDBY_CHANNEL, 0x00 },
|
||||
{ CAP1106_REG_STANDBY_CONFIG, 0x39 },
|
||||
{ CAP1106_REG_STANDBY_SENSITIVITY, 0x02 },
|
||||
{ CAP1106_REG_STANDBY_THRESH, 0x40 },
|
||||
{ CAP1106_REG_CONFIG2, 0x40 },
|
||||
{ CAP1106_REG_SENSOR_CALIB_LSB1, 0x00 },
|
||||
{ CAP1106_REG_SENSOR_CALIB_LSB2, 0x00 },
|
||||
};
|
||||
|
||||
static bool cap1106_volatile_reg(struct device *dev, unsigned int reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case CAP1106_REG_MAIN_CONTROL:
|
||||
case CAP1106_REG_SENSOR_INPUT:
|
||||
case CAP1106_REG_SENOR_DELTA(0):
|
||||
case CAP1106_REG_SENOR_DELTA(1):
|
||||
case CAP1106_REG_SENOR_DELTA(2):
|
||||
case CAP1106_REG_SENOR_DELTA(3):
|
||||
case CAP1106_REG_SENOR_DELTA(4):
|
||||
case CAP1106_REG_SENOR_DELTA(5):
|
||||
case CAP1106_REG_PRODUCT_ID:
|
||||
case CAP1106_REG_MANUFACTURER_ID:
|
||||
case CAP1106_REG_REVISION:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static const struct regmap_config cap1106_regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
|
||||
.max_register = CAP1106_REG_REVISION,
|
||||
.reg_defaults = cap1106_reg_defaults,
|
||||
|
||||
.num_reg_defaults = ARRAY_SIZE(cap1106_reg_defaults),
|
||||
.cache_type = REGCACHE_RBTREE,
|
||||
.volatile_reg = cap1106_volatile_reg,
|
||||
};
|
||||
|
||||
static irqreturn_t cap1106_thread_func(int irq_num, void *data)
|
||||
{
|
||||
struct cap1106_priv *priv = data;
|
||||
unsigned int status;
|
||||
int ret, i;
|
||||
|
||||
/*
|
||||
* Deassert interrupt. This needs to be done before reading the status
|
||||
* registers, which will not carry valid values otherwise.
|
||||
*/
|
||||
ret = regmap_update_bits(priv->regmap, CAP1106_REG_MAIN_CONTROL, 1, 0);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = regmap_read(priv->regmap, CAP1106_REG_SENSOR_INPUT, &status);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
for (i = 0; i < CAP1106_NUM_CHN; i++)
|
||||
input_report_key(priv->idev, priv->keycodes[i],
|
||||
status & (1 << i));
|
||||
|
||||
input_sync(priv->idev);
|
||||
|
||||
out:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int cap1106_set_sleep(struct cap1106_priv *priv, bool sleep)
|
||||
{
|
||||
return regmap_update_bits(priv->regmap, CAP1106_REG_MAIN_CONTROL,
|
||||
CAP1106_REG_MAIN_CONTROL_DLSEEP,
|
||||
sleep ? CAP1106_REG_MAIN_CONTROL_DLSEEP : 0);
|
||||
}
|
||||
|
||||
static int cap1106_input_open(struct input_dev *idev)
|
||||
{
|
||||
struct cap1106_priv *priv = input_get_drvdata(idev);
|
||||
|
||||
return cap1106_set_sleep(priv, false);
|
||||
}
|
||||
|
||||
static void cap1106_input_close(struct input_dev *idev)
|
||||
{
|
||||
struct cap1106_priv *priv = input_get_drvdata(idev);
|
||||
|
||||
cap1106_set_sleep(priv, true);
|
||||
}
|
||||
|
||||
static int cap1106_i2c_probe(struct i2c_client *i2c_client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct device *dev = &i2c_client->dev;
|
||||
struct cap1106_priv *priv;
|
||||
struct device_node *node;
|
||||
int i, error, irq, gain = 0;
|
||||
unsigned int val, rev;
|
||||
u32 gain32, keycodes[CAP1106_NUM_CHN];
|
||||
|
||||
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->regmap = devm_regmap_init_i2c(i2c_client, &cap1106_regmap_config);
|
||||
if (IS_ERR(priv->regmap))
|
||||
return PTR_ERR(priv->regmap);
|
||||
|
||||
error = regmap_read(priv->regmap, CAP1106_REG_PRODUCT_ID, &val);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (val != CAP1106_PRODUCT_ID) {
|
||||
dev_err(dev, "Product ID: Got 0x%02x, expected 0x%02x\n",
|
||||
val, CAP1106_PRODUCT_ID);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
error = regmap_read(priv->regmap, CAP1106_REG_MANUFACTURER_ID, &val);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (val != CAP1106_MANUFACTURER_ID) {
|
||||
dev_err(dev, "Manufacturer ID: Got 0x%02x, expected 0x%02x\n",
|
||||
val, CAP1106_MANUFACTURER_ID);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
error = regmap_read(priv->regmap, CAP1106_REG_REVISION, &rev);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
dev_info(dev, "CAP1106 detected, revision 0x%02x\n", rev);
|
||||
i2c_set_clientdata(i2c_client, priv);
|
||||
node = dev->of_node;
|
||||
|
||||
if (!of_property_read_u32(node, "microchip,sensor-gain", &gain32)) {
|
||||
if (is_power_of_2(gain32) && gain32 <= 8)
|
||||
gain = ilog2(gain32);
|
||||
else
|
||||
dev_err(dev, "Invalid sensor-gain value %d\n", gain32);
|
||||
}
|
||||
|
||||
BUILD_BUG_ON(ARRAY_SIZE(keycodes) != ARRAY_SIZE(priv->keycodes));
|
||||
|
||||
/* Provide some useful defaults */
|
||||
for (i = 0; i < ARRAY_SIZE(keycodes); i++)
|
||||
keycodes[i] = KEY_A + i;
|
||||
|
||||
of_property_read_u32_array(node, "linux,keycodes",
|
||||
keycodes, ARRAY_SIZE(keycodes));
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(keycodes); i++)
|
||||
priv->keycodes[i] = keycodes[i];
|
||||
|
||||
error = regmap_update_bits(priv->regmap, CAP1106_REG_MAIN_CONTROL,
|
||||
CAP1106_REG_MAIN_CONTROL_GAIN_MASK,
|
||||
gain << CAP1106_REG_MAIN_CONTROL_GAIN_SHIFT);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
/* Disable autorepeat. The Linux input system has its own handling. */
|
||||
error = regmap_write(priv->regmap, CAP1106_REG_REPEAT_RATE, 0);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
priv->idev = devm_input_allocate_device(dev);
|
||||
if (!priv->idev)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->idev->name = "CAP1106 capacitive touch sensor";
|
||||
priv->idev->id.bustype = BUS_I2C;
|
||||
priv->idev->evbit[0] = BIT_MASK(EV_KEY);
|
||||
|
||||
if (of_property_read_bool(node, "autorepeat"))
|
||||
__set_bit(EV_REP, priv->idev->evbit);
|
||||
|
||||
for (i = 0; i < CAP1106_NUM_CHN; i++)
|
||||
__set_bit(priv->keycodes[i], priv->idev->keybit);
|
||||
|
||||
priv->idev->id.vendor = CAP1106_MANUFACTURER_ID;
|
||||
priv->idev->id.product = CAP1106_PRODUCT_ID;
|
||||
priv->idev->id.version = rev;
|
||||
|
||||
priv->idev->open = cap1106_input_open;
|
||||
priv->idev->close = cap1106_input_close;
|
||||
|
||||
input_set_drvdata(priv->idev, priv);
|
||||
|
||||
/*
|
||||
* Put the device in deep sleep mode for now.
|
||||
* ->open() will bring it back once the it is actually needed.
|
||||
*/
|
||||
cap1106_set_sleep(priv, true);
|
||||
|
||||
error = input_register_device(priv->idev);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
irq = irq_of_parse_and_map(node, 0);
|
||||
if (!irq) {
|
||||
dev_err(dev, "Unable to parse or map IRQ\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
error = devm_request_threaded_irq(dev, irq, NULL, cap1106_thread_func,
|
||||
IRQF_ONESHOT, dev_name(dev), priv);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id cap1106_dt_ids[] = {
|
||||
{ .compatible = "microchip,cap1106", },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, cap1106_dt_ids);
|
||||
|
||||
static const struct i2c_device_id cap1106_i2c_ids[] = {
|
||||
{ "cap1106", 0 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, cap1106_i2c_ids);
|
||||
|
||||
static struct i2c_driver cap1106_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "cap1106",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = cap1106_dt_ids,
|
||||
},
|
||||
.id_table = cap1106_i2c_ids,
|
||||
.probe = cap1106_i2c_probe,
|
||||
};
|
||||
|
||||
module_i2c_driver(cap1106_i2c_driver);
|
||||
|
||||
MODULE_ALIAS("platform:cap1106");
|
||||
MODULE_DESCRIPTION("Microchip CAP1106 driver");
|
||||
MODULE_AUTHOR("Daniel Mack <linux@zonque.org>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
@ -531,8 +531,7 @@ static int imx_keypad_probe(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int imx_kbd_suspend(struct device *dev)
|
||||
static int __maybe_unused imx_kbd_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct imx_keypad *kbd = platform_get_drvdata(pdev);
|
||||
|
|
@ -552,7 +551,7 @@ static int imx_kbd_suspend(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int imx_kbd_resume(struct device *dev)
|
||||
static int __maybe_unused imx_kbd_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct imx_keypad *kbd = platform_get_drvdata(pdev);
|
||||
|
|
@ -575,7 +574,6 @@ err_clk:
|
|||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(imx_kbd_pm_ops, imx_kbd_suspend, imx_kbd_resume);
|
||||
|
||||
|
|
|
|||
|
|
@ -203,12 +203,17 @@ static int max7359_probe(struct i2c_client *client,
|
|||
|
||||
dev_dbg(&client->dev, "keys FIFO is 0x%02x\n", ret);
|
||||
|
||||
keypad = kzalloc(sizeof(struct max7359_keypad), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!keypad || !input_dev) {
|
||||
keypad = devm_kzalloc(&client->dev, sizeof(struct max7359_keypad),
|
||||
GFP_KERNEL);
|
||||
if (!keypad) {
|
||||
dev_err(&client->dev, "failed to allocate memory\n");
|
||||
error = -ENOMEM;
|
||||
goto failed_free_mem;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
input_dev = devm_input_allocate_device(&client->dev);
|
||||
if (!input_dev) {
|
||||
dev_err(&client->dev, "failed to allocate input device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
keypad->client = client;
|
||||
|
|
@ -230,19 +235,20 @@ static int max7359_probe(struct i2c_client *client,
|
|||
|
||||
max7359_build_keycode(keypad, keymap_data);
|
||||
|
||||
error = request_threaded_irq(client->irq, NULL, max7359_interrupt,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
client->name, keypad);
|
||||
error = devm_request_threaded_irq(&client->dev, client->irq, NULL,
|
||||
max7359_interrupt,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
client->name, keypad);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "failed to register interrupt\n");
|
||||
goto failed_free_mem;
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Register the input device */
|
||||
error = input_register_device(input_dev);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "failed to register input device\n");
|
||||
goto failed_free_irq;
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Initialize MAX7359 */
|
||||
|
|
@ -251,24 +257,6 @@ static int max7359_probe(struct i2c_client *client,
|
|||
i2c_set_clientdata(client, keypad);
|
||||
device_init_wakeup(&client->dev, 1);
|
||||
|
||||
return 0;
|
||||
|
||||
failed_free_irq:
|
||||
free_irq(client->irq, keypad);
|
||||
failed_free_mem:
|
||||
input_free_device(input_dev);
|
||||
kfree(keypad);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int max7359_remove(struct i2c_client *client)
|
||||
{
|
||||
struct max7359_keypad *keypad = i2c_get_clientdata(client);
|
||||
|
||||
free_irq(client->irq, keypad);
|
||||
input_unregister_device(keypad->input_dev);
|
||||
kfree(keypad);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -313,7 +301,6 @@ static struct i2c_driver max7359_i2c_driver = {
|
|||
.pm = &max7359_pm,
|
||||
},
|
||||
.probe = max7359_probe,
|
||||
.remove = max7359_remove,
|
||||
.id_table = max7359_ids,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -392,7 +392,6 @@ static void keyspan_irq_recv(struct urb *urb)
|
|||
|
||||
default:
|
||||
goto resubmit;
|
||||
break;
|
||||
}
|
||||
|
||||
if (debug)
|
||||
|
|
|
|||
|
|
@ -83,6 +83,9 @@ soc_button_device_create(struct pnp_dev *pdev,
|
|||
sizeof(*gpio_keys_pdata) +
|
||||
sizeof(*gpio_keys) * MAX_NBUTTONS,
|
||||
GFP_KERNEL);
|
||||
if (!gpio_keys_pdata)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
gpio_keys = (void *)(gpio_keys_pdata + 1);
|
||||
|
||||
for (info = button_info; info->name; info++) {
|
||||
|
|
|
|||
|
|
@ -311,7 +311,14 @@ static int uinput_open(struct inode *inode, struct file *file)
|
|||
static int uinput_validate_absbits(struct input_dev *dev)
|
||||
{
|
||||
unsigned int cnt;
|
||||
int retval = 0;
|
||||
int nslot;
|
||||
|
||||
if (!test_bit(EV_ABS, dev->evbit))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Check if absmin/absmax/absfuzz/absflat are sane.
|
||||
*/
|
||||
|
||||
for (cnt = 0; cnt < ABS_CNT; cnt++) {
|
||||
int min, max;
|
||||
|
|
@ -327,8 +334,7 @@ static int uinput_validate_absbits(struct input_dev *dev)
|
|||
UINPUT_NAME, cnt,
|
||||
input_abs_get_min(dev, cnt),
|
||||
input_abs_get_max(dev, cnt));
|
||||
retval = -EINVAL;
|
||||
break;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (input_abs_get_flat(dev, cnt) >
|
||||
|
|
@ -340,11 +346,18 @@ static int uinput_validate_absbits(struct input_dev *dev)
|
|||
input_abs_get_flat(dev, cnt),
|
||||
input_abs_get_min(dev, cnt),
|
||||
input_abs_get_max(dev, cnt));
|
||||
retval = -EINVAL;
|
||||
break;
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
|
||||
if (test_bit(ABS_MT_SLOT, dev->absbit)) {
|
||||
nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1;
|
||||
input_mt_init_slots(dev, nslot, 0);
|
||||
} else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) {
|
||||
input_set_events_per_packet(dev, 60);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uinput_allocate_device(struct uinput_device *udev)
|
||||
|
|
@ -410,19 +423,9 @@ static int uinput_setup_device(struct uinput_device *udev,
|
|||
input_abs_set_flat(dev, i, user_dev->absflat[i]);
|
||||
}
|
||||
|
||||
/* check if absmin/absmax/absfuzz/absflat are filled as
|
||||
* told in Documentation/input/input-programming.txt */
|
||||
if (test_bit(EV_ABS, dev->evbit)) {
|
||||
retval = uinput_validate_absbits(dev);
|
||||
if (retval < 0)
|
||||
goto exit;
|
||||
if (test_bit(ABS_MT_SLOT, dev->absbit)) {
|
||||
int nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1;
|
||||
input_mt_init_slots(dev, nslot, 0);
|
||||
} else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) {
|
||||
input_set_events_per_packet(dev, 60);
|
||||
}
|
||||
}
|
||||
retval = uinput_validate_absbits(dev);
|
||||
if (retval < 0)
|
||||
goto exit;
|
||||
|
||||
udev->state = UIST_SETUP_COMPLETE;
|
||||
retval = count;
|
||||
|
|
@ -720,6 +723,12 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
|
|||
}
|
||||
|
||||
switch (cmd) {
|
||||
case UI_GET_VERSION:
|
||||
if (put_user(UINPUT_VERSION,
|
||||
(unsigned int __user *)p))
|
||||
retval = -EFAULT;
|
||||
goto out;
|
||||
|
||||
case UI_DEV_CREATE:
|
||||
retval = uinput_create_device(udev);
|
||||
goto out;
|
||||
|
|
|
|||
|
|
@ -99,6 +99,8 @@ static const struct alps_nibble_commands alps_v6_nibble_commands[] = {
|
|||
#define ALPS_FOUR_BUTTONS 0x40 /* 4 direction button present */
|
||||
#define ALPS_PS2_INTERLEAVED 0x80 /* 3-byte PS/2 packet interleaved with
|
||||
6-byte ALPS packet */
|
||||
#define ALPS_IS_RUSHMORE 0x100 /* device is a rushmore */
|
||||
#define ALPS_BUTTONPAD 0x200 /* device is a clickpad */
|
||||
|
||||
static const struct alps_model_info alps_model_data[] = {
|
||||
{ { 0x32, 0x02, 0x14 }, 0x00, ALPS_PROTO_V2, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT }, /* Toshiba Salellite Pro M10 */
|
||||
|
|
@ -281,11 +283,10 @@ static void alps_process_packet_v1_v2(struct psmouse *psmouse)
|
|||
*
|
||||
* The bitmaps don't have enough data to track fingers, so this function
|
||||
* only generates points representing a bounding box of at most two contacts.
|
||||
* These two points are returned in x1, y1, x2, and y2.
|
||||
* These two points are returned in fields->mt.
|
||||
*/
|
||||
static void alps_process_bitmap_dolphin(struct alps_data *priv,
|
||||
struct alps_fields *fields,
|
||||
int *x1, int *y1, int *x2, int *y2)
|
||||
struct alps_fields *fields)
|
||||
{
|
||||
int box_middle_x, box_middle_y;
|
||||
unsigned int x_map, y_map;
|
||||
|
|
@ -308,8 +309,6 @@ static void alps_process_bitmap_dolphin(struct alps_data *priv,
|
|||
if (x_msb > priv->x_bits || y_msb > priv->y_bits)
|
||||
return;
|
||||
|
||||
*x1 = *y1 = *x2 = *y2 = 0;
|
||||
|
||||
if (fields->fingers > 1) {
|
||||
start_bit = priv->x_bits - x_msb;
|
||||
end_bit = priv->x_bits - x_lsb;
|
||||
|
|
@ -320,10 +319,35 @@ static void alps_process_bitmap_dolphin(struct alps_data *priv,
|
|||
end_bit = y_msb - 1;
|
||||
box_middle_y = (priv->y_max * (start_bit + end_bit)) /
|
||||
(2 * (priv->y_bits - 1));
|
||||
*x1 = fields->x;
|
||||
*y1 = fields->y;
|
||||
*x2 = 2 * box_middle_x - *x1;
|
||||
*y2 = 2 * box_middle_y - *y1;
|
||||
fields->mt[0] = fields->st;
|
||||
fields->mt[1].x = 2 * box_middle_x - fields->mt[0].x;
|
||||
fields->mt[1].y = 2 * box_middle_y - fields->mt[0].y;
|
||||
}
|
||||
}
|
||||
|
||||
static void alps_get_bitmap_points(unsigned int map,
|
||||
struct alps_bitmap_point *low,
|
||||
struct alps_bitmap_point *high,
|
||||
int *fingers)
|
||||
{
|
||||
struct alps_bitmap_point *point;
|
||||
int i, bit, prev_bit = 0;
|
||||
|
||||
point = low;
|
||||
for (i = 0; map != 0; i++, map >>= 1) {
|
||||
bit = map & 1;
|
||||
if (bit) {
|
||||
if (!prev_bit) {
|
||||
point->start_bit = i;
|
||||
point->num_bits = 0;
|
||||
(*fingers)++;
|
||||
}
|
||||
point->num_bits++;
|
||||
} else {
|
||||
if (prev_bit)
|
||||
point = high;
|
||||
}
|
||||
prev_bit = bit;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -334,71 +358,21 @@ static void alps_process_bitmap_dolphin(struct alps_data *priv,
|
|||
*
|
||||
* The bitmaps don't have enough data to track fingers, so this function
|
||||
* only generates points representing a bounding box of all contacts.
|
||||
* These points are returned in x1, y1, x2, and y2 when the return value
|
||||
* These points are returned in fields->mt when the return value
|
||||
* is greater than 0.
|
||||
*/
|
||||
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_fields *fields)
|
||||
{
|
||||
struct alps_bitmap_point {
|
||||
int start_bit;
|
||||
int num_bits;
|
||||
};
|
||||
|
||||
int fingers_x = 0, fingers_y = 0, fingers;
|
||||
int i, bit, prev_bit;
|
||||
int i, fingers_x = 0, fingers_y = 0, fingers;
|
||||
struct alps_bitmap_point x_low = {0,}, x_high = {0,};
|
||||
struct alps_bitmap_point y_low = {0,}, y_high = {0,};
|
||||
struct alps_bitmap_point *point;
|
||||
|
||||
if (!x_map || !y_map)
|
||||
if (!fields->x_map || !fields->y_map)
|
||||
return 0;
|
||||
|
||||
*x1 = *y1 = *x2 = *y2 = 0;
|
||||
|
||||
prev_bit = 0;
|
||||
point = &x_low;
|
||||
for (i = 0; x_map != 0; i++, x_map >>= 1) {
|
||||
bit = x_map & 1;
|
||||
if (bit) {
|
||||
if (!prev_bit) {
|
||||
point->start_bit = i;
|
||||
fingers_x++;
|
||||
}
|
||||
point->num_bits++;
|
||||
} else {
|
||||
if (prev_bit)
|
||||
point = &x_high;
|
||||
else
|
||||
point->num_bits = 0;
|
||||
}
|
||||
prev_bit = bit;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 - priv->y_bits);
|
||||
prev_bit = 0;
|
||||
point = &y_low;
|
||||
for (i = 0; y_map != 0; i++, y_map <<= 1) {
|
||||
bit = y_map & (1 << (sizeof(y_map) * BITS_PER_BYTE - 1));
|
||||
if (bit) {
|
||||
if (!prev_bit) {
|
||||
point->start_bit = i;
|
||||
fingers_y++;
|
||||
}
|
||||
point->num_bits++;
|
||||
} else {
|
||||
if (prev_bit)
|
||||
point = &y_high;
|
||||
else
|
||||
point->num_bits = 0;
|
||||
}
|
||||
prev_bit = bit;
|
||||
}
|
||||
alps_get_bitmap_points(fields->x_map, &x_low, &x_high, &fingers_x);
|
||||
alps_get_bitmap_points(fields->y_map, &y_low, &y_high, &fingers_y);
|
||||
|
||||
/*
|
||||
* Fingers can overlap, so we use the maximum count of fingers
|
||||
|
|
@ -407,58 +381,91 @@ static int alps_process_bitmap(struct alps_data *priv,
|
|||
fingers = max(fingers_x, fingers_y);
|
||||
|
||||
/*
|
||||
* If total fingers is > 1 but either axis reports only a single
|
||||
* contact, we have overlapping or adjacent fingers. For the
|
||||
* purposes of creating a bounding box, divide the single contact
|
||||
* (roughly) equally between the two points.
|
||||
* If an axis reports only a single contact, we have overlapping or
|
||||
* adjacent fingers. Divide the single contact between the two points.
|
||||
*/
|
||||
if (fingers > 1) {
|
||||
if (fingers_x == 1) {
|
||||
i = x_low.num_bits / 2;
|
||||
x_low.num_bits = x_low.num_bits - i;
|
||||
x_high.start_bit = x_low.start_bit + i;
|
||||
x_high.num_bits = max(i, 1);
|
||||
} else if (fingers_y == 1) {
|
||||
i = y_low.num_bits / 2;
|
||||
y_low.num_bits = y_low.num_bits - i;
|
||||
y_high.start_bit = y_low.start_bit + i;
|
||||
y_high.num_bits = max(i, 1);
|
||||
}
|
||||
if (fingers_x == 1) {
|
||||
i = (x_low.num_bits - 1) / 2;
|
||||
x_low.num_bits = x_low.num_bits - i;
|
||||
x_high.start_bit = x_low.start_bit + i;
|
||||
x_high.num_bits = max(i, 1);
|
||||
}
|
||||
if (fingers_y == 1) {
|
||||
i = (y_low.num_bits - 1) / 2;
|
||||
y_low.num_bits = y_low.num_bits - i;
|
||||
y_high.start_bit = y_low.start_bit + i;
|
||||
y_high.num_bits = max(i, 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));
|
||||
fields->mt[0].x =
|
||||
(priv->x_max * (2 * x_low.start_bit + x_low.num_bits - 1)) /
|
||||
(2 * (priv->x_bits - 1));
|
||||
fields->mt[0].y =
|
||||
(priv->y_max * (2 * y_low.start_bit + y_low.num_bits - 1)) /
|
||||
(2 * (priv->y_bits - 1));
|
||||
|
||||
if (fingers > 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));
|
||||
fields->mt[1].x =
|
||||
(priv->x_max * (2 * x_high.start_bit + x_high.num_bits - 1)) /
|
||||
(2 * (priv->x_bits - 1));
|
||||
fields->mt[1].y =
|
||||
(priv->y_max * (2 * y_high.start_bit + y_high.num_bits - 1)) /
|
||||
(2 * (priv->y_bits - 1));
|
||||
|
||||
/* y-bitmap order is reversed, except on rushmore */
|
||||
if (!(priv->flags & ALPS_IS_RUSHMORE)) {
|
||||
fields->mt[0].y = priv->y_max - fields->mt[0].y;
|
||||
fields->mt[1].y = priv->y_max - fields->mt[1].y;
|
||||
}
|
||||
|
||||
return fingers;
|
||||
}
|
||||
|
||||
static void alps_set_slot(struct input_dev *dev, int slot, bool active,
|
||||
int x, int y)
|
||||
static void alps_set_slot(struct input_dev *dev, int slot, int x, int y)
|
||||
{
|
||||
input_mt_slot(dev, slot);
|
||||
input_mt_report_slot_state(dev, MT_TOOL_FINGER, active);
|
||||
if (active) {
|
||||
input_report_abs(dev, ABS_MT_POSITION_X, x);
|
||||
input_report_abs(dev, ABS_MT_POSITION_Y, y);
|
||||
}
|
||||
input_mt_report_slot_state(dev, MT_TOOL_FINGER, true);
|
||||
input_report_abs(dev, ABS_MT_POSITION_X, x);
|
||||
input_report_abs(dev, ABS_MT_POSITION_Y, y);
|
||||
}
|
||||
|
||||
static void alps_report_semi_mt_data(struct input_dev *dev, int num_fingers,
|
||||
int x1, int y1, int x2, int y2)
|
||||
static void alps_report_mt_data(struct psmouse *psmouse, int n)
|
||||
{
|
||||
alps_set_slot(dev, 0, num_fingers != 0, x1, y1);
|
||||
alps_set_slot(dev, 1, num_fingers == 2, x2, y2);
|
||||
struct alps_data *priv = psmouse->private;
|
||||
struct input_dev *dev = psmouse->dev;
|
||||
struct alps_fields *f = &priv->f;
|
||||
int i, slot[MAX_TOUCHES];
|
||||
|
||||
input_mt_assign_slots(dev, slot, f->mt, n);
|
||||
for (i = 0; i < n; i++)
|
||||
alps_set_slot(dev, slot[i], f->mt[i].x, f->mt[i].y);
|
||||
|
||||
input_mt_sync_frame(dev);
|
||||
}
|
||||
|
||||
static void alps_report_semi_mt_data(struct psmouse *psmouse, int fingers)
|
||||
{
|
||||
struct alps_data *priv = psmouse->private;
|
||||
struct input_dev *dev = psmouse->dev;
|
||||
struct alps_fields *f = &priv->f;
|
||||
|
||||
/* Use st data when we don't have mt data */
|
||||
if (fingers < 2) {
|
||||
f->mt[0].x = f->st.x;
|
||||
f->mt[0].y = f->st.y;
|
||||
fingers = f->pressure > 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
alps_report_mt_data(psmouse, (fingers <= 2) ? fingers : 2);
|
||||
|
||||
input_mt_report_finger_count(dev, fingers);
|
||||
|
||||
input_report_key(dev, BTN_LEFT, f->left);
|
||||
input_report_key(dev, BTN_RIGHT, f->right);
|
||||
input_report_key(dev, BTN_MIDDLE, f->middle);
|
||||
|
||||
input_report_abs(dev, ABS_PRESSURE, f->pressure);
|
||||
|
||||
input_sync(dev);
|
||||
}
|
||||
|
||||
static void alps_process_trackstick_packet_v3(struct psmouse *psmouse)
|
||||
|
|
@ -532,7 +539,7 @@ static void alps_decode_buttons_v3(struct alps_fields *f, unsigned char *p)
|
|||
f->ts_middle = !!(p[3] & 0x40);
|
||||
}
|
||||
|
||||
static void alps_decode_pinnacle(struct alps_fields *f, unsigned char *p,
|
||||
static int alps_decode_pinnacle(struct alps_fields *f, unsigned char *p,
|
||||
struct psmouse *psmouse)
|
||||
{
|
||||
f->first_mp = !!(p[4] & 0x40);
|
||||
|
|
@ -546,24 +553,31 @@ static void alps_decode_pinnacle(struct alps_fields *f, unsigned char *p,
|
|||
((p[2] & 0x7f) << 1) |
|
||||
(p[4] & 0x01);
|
||||
|
||||
f->x = ((p[1] & 0x7f) << 4) | ((p[4] & 0x30) >> 2) |
|
||||
f->st.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;
|
||||
f->st.y = ((p[2] & 0x7f) << 4) | (p[4] & 0x0f);
|
||||
f->pressure = p[5] & 0x7f;
|
||||
|
||||
alps_decode_buttons_v3(f, p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void alps_decode_rushmore(struct alps_fields *f, unsigned char *p,
|
||||
static int alps_decode_rushmore(struct alps_fields *f, unsigned char *p,
|
||||
struct psmouse *psmouse)
|
||||
{
|
||||
alps_decode_pinnacle(f, p, psmouse);
|
||||
|
||||
/* Rushmore's packet decode has a bit difference with Pinnacle's */
|
||||
f->is_mp = !!(p[5] & 0x40);
|
||||
f->fingers = max((p[5] & 0x3), ((p[5] >> 2) & 0x3)) + 1;
|
||||
f->x_map |= (p[5] & 0x10) << 11;
|
||||
f->y_map |= (p[5] & 0x20) << 6;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void alps_decode_dolphin(struct alps_fields *f, unsigned char *p,
|
||||
static int alps_decode_dolphin(struct alps_fields *f, unsigned char *p,
|
||||
struct psmouse *psmouse)
|
||||
{
|
||||
u64 palm_data = 0;
|
||||
|
|
@ -573,9 +587,9 @@ static void alps_decode_dolphin(struct alps_fields *f, unsigned char *p,
|
|||
f->is_mp = !!(p[0] & 0x20);
|
||||
|
||||
if (!f->is_mp) {
|
||||
f->x = ((p[1] & 0x7f) | ((p[4] & 0x0f) << 7));
|
||||
f->y = ((p[2] & 0x7f) | ((p[4] & 0xf0) << 3));
|
||||
f->z = (p[0] & 4) ? 0 : p[5] & 0x7f;
|
||||
f->st.x = ((p[1] & 0x7f) | ((p[4] & 0x0f) << 7));
|
||||
f->st.y = ((p[2] & 0x7f) | ((p[4] & 0xf0) << 3));
|
||||
f->pressure = (p[0] & 4) ? 0 : p[5] & 0x7f;
|
||||
alps_decode_buttons_v3(f, p);
|
||||
} else {
|
||||
f->fingers = ((p[0] & 0x6) >> 1 |
|
||||
|
|
@ -596,19 +610,21 @@ static void alps_decode_dolphin(struct alps_fields *f, unsigned char *p,
|
|||
f->x_map = (palm_data >> priv->y_bits) &
|
||||
(BIT(priv->x_bits) - 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void alps_process_touchpad_packet_v3_v5(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 x1 = 0, y1 = 0, x2 = 0, y2 = 0;
|
||||
int fingers = 0, bmap_fn;
|
||||
struct alps_fields f = {0};
|
||||
struct alps_fields *f = &priv->f;
|
||||
int fingers = 0;
|
||||
|
||||
priv->decode_fields(&f, packet, psmouse);
|
||||
memset(f, 0, sizeof(*f));
|
||||
|
||||
priv->decode_fields(f, packet, psmouse);
|
||||
|
||||
/*
|
||||
* There's no single feature of touchpad position and bitmap packets
|
||||
|
|
@ -623,22 +639,14 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse)
|
|||
* packet. Check for this, and when it happens process the
|
||||
* position packet as usual.
|
||||
*/
|
||||
if (f.is_mp) {
|
||||
fingers = f.fingers;
|
||||
if (f->is_mp) {
|
||||
fingers = f->fingers;
|
||||
if (priv->proto_version == ALPS_PROTO_V3) {
|
||||
bmap_fn = alps_process_bitmap(priv, f.x_map,
|
||||
f.y_map, &x1, &y1,
|
||||
&x2, &y2);
|
||||
|
||||
/*
|
||||
* We shouldn't report more than one finger if
|
||||
* we don't have two coordinates.
|
||||
*/
|
||||
if (fingers > 1 && bmap_fn < 2)
|
||||
fingers = bmap_fn;
|
||||
if (alps_process_bitmap(priv, f) == 0)
|
||||
fingers = 0; /* Use st data */
|
||||
|
||||
/* Now process position packet */
|
||||
priv->decode_fields(&f, priv->multi_data,
|
||||
priv->decode_fields(f, priv->multi_data,
|
||||
psmouse);
|
||||
} else {
|
||||
/*
|
||||
|
|
@ -647,15 +655,14 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse)
|
|||
* calculate Pt2, so we need to do position
|
||||
* packet decode first.
|
||||
*/
|
||||
priv->decode_fields(&f, priv->multi_data,
|
||||
priv->decode_fields(f, priv->multi_data,
|
||||
psmouse);
|
||||
|
||||
/*
|
||||
* Since Dolphin's finger number is reliable,
|
||||
* there is no need to compare with bmap_fn.
|
||||
*/
|
||||
alps_process_bitmap_dolphin(priv, &f, &x1, &y1,
|
||||
&x2, &y2);
|
||||
alps_process_bitmap_dolphin(priv, f);
|
||||
}
|
||||
} else {
|
||||
priv->multi_packet = 0;
|
||||
|
|
@ -670,10 +677,10 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse)
|
|||
* out misidentified bitmap packets, we reject anything with this
|
||||
* bit set.
|
||||
*/
|
||||
if (f.is_mp)
|
||||
if (f->is_mp)
|
||||
return;
|
||||
|
||||
if (!priv->multi_packet && f.first_mp) {
|
||||
if (!priv->multi_packet && f->first_mp) {
|
||||
priv->multi_packet = 1;
|
||||
memcpy(priv->multi_data, packet, sizeof(priv->multi_data));
|
||||
return;
|
||||
|
|
@ -687,44 +694,15 @@ static void alps_process_touchpad_packet_v3_v5(struct psmouse *psmouse)
|
|||
* with x, y, and z all zero, so these seem to be flukes.
|
||||
* Ignore them.
|
||||
*/
|
||||
if (f.x && f.y && !f.z)
|
||||
if (f->st.x && f->st.y && !f->pressure)
|
||||
return;
|
||||
|
||||
/*
|
||||
* If we don't have MT data or the bitmaps were empty, we have
|
||||
* to rely on ST data.
|
||||
*/
|
||||
if (!fingers) {
|
||||
x1 = f.x;
|
||||
y1 = f.y;
|
||||
fingers = f.z > 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
if (f.z >= 64)
|
||||
input_report_key(dev, BTN_TOUCH, 1);
|
||||
else
|
||||
input_report_key(dev, BTN_TOUCH, 0);
|
||||
|
||||
alps_report_semi_mt_data(dev, fingers, x1, y1, x2, y2);
|
||||
|
||||
input_mt_report_finger_count(dev, fingers);
|
||||
|
||||
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 (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, f.z);
|
||||
|
||||
input_sync(dev);
|
||||
alps_report_semi_mt_data(psmouse, fingers);
|
||||
|
||||
if (!(priv->quirks & ALPS_QUIRK_TRACKSTICK_BUTTONS)) {
|
||||
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_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);
|
||||
}
|
||||
}
|
||||
|
|
@ -823,13 +801,8 @@ static void alps_process_packet_v4(struct psmouse *psmouse)
|
|||
{
|
||||
struct alps_data *priv = psmouse->private;
|
||||
unsigned char *packet = psmouse->packet;
|
||||
struct input_dev *dev = psmouse->dev;
|
||||
struct alps_fields *f = &priv->f;
|
||||
int offset;
|
||||
int x, y, z;
|
||||
int left, right;
|
||||
int x1, y1, x2, y2;
|
||||
int fingers = 0;
|
||||
unsigned int x_bitmap, y_bitmap;
|
||||
|
||||
/*
|
||||
* v4 has a 6-byte encoding for bitmap data, but this data is
|
||||
|
|
@ -851,71 +824,207 @@ static void alps_process_packet_v4(struct psmouse *psmouse)
|
|||
if (++priv->multi_packet > 2) {
|
||||
priv->multi_packet = 0;
|
||||
|
||||
x_bitmap = ((priv->multi_data[2] & 0x1f) << 10) |
|
||||
f->x_map = ((priv->multi_data[2] & 0x1f) << 10) |
|
||||
((priv->multi_data[3] & 0x60) << 3) |
|
||||
((priv->multi_data[0] & 0x3f) << 2) |
|
||||
((priv->multi_data[1] & 0x60) >> 5);
|
||||
y_bitmap = ((priv->multi_data[5] & 0x01) << 10) |
|
||||
f->y_map = ((priv->multi_data[5] & 0x01) << 10) |
|
||||
((priv->multi_data[3] & 0x1f) << 5) |
|
||||
(priv->multi_data[1] & 0x1f);
|
||||
|
||||
fingers = alps_process_bitmap(priv, x_bitmap, y_bitmap,
|
||||
&x1, &y1, &x2, &y2);
|
||||
|
||||
/* Store MT data.*/
|
||||
priv->fingers = fingers;
|
||||
priv->x1 = x1;
|
||||
priv->x2 = x2;
|
||||
priv->y1 = y1;
|
||||
priv->y2 = y2;
|
||||
f->fingers = alps_process_bitmap(priv, f);
|
||||
}
|
||||
|
||||
left = packet[4] & 0x01;
|
||||
right = packet[4] & 0x02;
|
||||
f->left = packet[4] & 0x01;
|
||||
f->right = packet[4] & 0x02;
|
||||
|
||||
x = ((packet[1] & 0x7f) << 4) | ((packet[3] & 0x30) >> 2) |
|
||||
((packet[0] & 0x30) >> 4);
|
||||
y = ((packet[2] & 0x7f) << 4) | (packet[3] & 0x0f);
|
||||
z = packet[5] & 0x7f;
|
||||
f->st.x = ((packet[1] & 0x7f) << 4) | ((packet[3] & 0x30) >> 2) |
|
||||
((packet[0] & 0x30) >> 4);
|
||||
f->st.y = ((packet[2] & 0x7f) << 4) | (packet[3] & 0x0f);
|
||||
f->pressure = packet[5] & 0x7f;
|
||||
|
||||
alps_report_semi_mt_data(psmouse, f->fingers);
|
||||
}
|
||||
|
||||
static bool alps_is_valid_package_v7(struct psmouse *psmouse)
|
||||
{
|
||||
switch (psmouse->pktcnt) {
|
||||
case 3:
|
||||
return (psmouse->packet[2] & 0x40) == 0x40;
|
||||
case 4:
|
||||
return (psmouse->packet[3] & 0x48) == 0x48;
|
||||
case 6:
|
||||
return (psmouse->packet[5] & 0x40) == 0x00;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static unsigned char alps_get_packet_id_v7(char *byte)
|
||||
{
|
||||
unsigned char packet_id;
|
||||
|
||||
if (byte[4] & 0x40)
|
||||
packet_id = V7_PACKET_ID_TWO;
|
||||
else if (byte[4] & 0x01)
|
||||
packet_id = V7_PACKET_ID_MULTI;
|
||||
else if ((byte[0] & 0x10) && !(byte[4] & 0x43))
|
||||
packet_id = V7_PACKET_ID_NEW;
|
||||
else if (byte[1] == 0x00 && byte[4] == 0x00)
|
||||
packet_id = V7_PACKET_ID_IDLE;
|
||||
else
|
||||
packet_id = V7_PACKET_ID_UNKNOWN;
|
||||
|
||||
return packet_id;
|
||||
}
|
||||
|
||||
static void alps_get_finger_coordinate_v7(struct input_mt_pos *mt,
|
||||
unsigned char *pkt,
|
||||
unsigned char pkt_id)
|
||||
{
|
||||
mt[0].x = ((pkt[2] & 0x80) << 4);
|
||||
mt[0].x |= ((pkt[2] & 0x3F) << 5);
|
||||
mt[0].x |= ((pkt[3] & 0x30) >> 1);
|
||||
mt[0].x |= (pkt[3] & 0x07);
|
||||
mt[0].y = (pkt[1] << 3) | (pkt[0] & 0x07);
|
||||
|
||||
mt[1].x = ((pkt[3] & 0x80) << 4);
|
||||
mt[1].x |= ((pkt[4] & 0x80) << 3);
|
||||
mt[1].x |= ((pkt[4] & 0x3F) << 4);
|
||||
mt[1].y = ((pkt[5] & 0x80) << 3);
|
||||
mt[1].y |= ((pkt[5] & 0x3F) << 4);
|
||||
|
||||
switch (pkt_id) {
|
||||
case V7_PACKET_ID_TWO:
|
||||
mt[1].x &= ~0x000F;
|
||||
mt[1].y |= 0x000F;
|
||||
break;
|
||||
|
||||
case V7_PACKET_ID_MULTI:
|
||||
mt[1].x &= ~0x003F;
|
||||
mt[1].y &= ~0x0020;
|
||||
mt[1].y |= ((pkt[4] & 0x02) << 4);
|
||||
mt[1].y |= 0x001F;
|
||||
break;
|
||||
|
||||
case V7_PACKET_ID_NEW:
|
||||
mt[1].x &= ~0x003F;
|
||||
mt[1].x |= (pkt[0] & 0x20);
|
||||
mt[1].y |= 0x000F;
|
||||
break;
|
||||
}
|
||||
|
||||
mt[0].y = 0x7FF - mt[0].y;
|
||||
mt[1].y = 0x7FF - mt[1].y;
|
||||
}
|
||||
|
||||
static int alps_get_mt_count(struct input_mt_pos *mt)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_TOUCHES && mt[i].x != 0 && mt[i].y != 0; i++)
|
||||
/* empty */;
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static int alps_decode_packet_v7(struct alps_fields *f,
|
||||
unsigned char *p,
|
||||
struct psmouse *psmouse)
|
||||
{
|
||||
unsigned char pkt_id;
|
||||
|
||||
pkt_id = alps_get_packet_id_v7(p);
|
||||
if (pkt_id == V7_PACKET_ID_IDLE)
|
||||
return 0;
|
||||
if (pkt_id == V7_PACKET_ID_UNKNOWN)
|
||||
return -1;
|
||||
|
||||
alps_get_finger_coordinate_v7(f->mt, p, pkt_id);
|
||||
|
||||
if (pkt_id == V7_PACKET_ID_TWO || pkt_id == V7_PACKET_ID_MULTI) {
|
||||
f->left = (p[0] & 0x80) >> 7;
|
||||
f->right = (p[0] & 0x20) >> 5;
|
||||
f->middle = (p[0] & 0x10) >> 4;
|
||||
}
|
||||
|
||||
if (pkt_id == V7_PACKET_ID_TWO)
|
||||
f->fingers = alps_get_mt_count(f->mt);
|
||||
else if (pkt_id == V7_PACKET_ID_MULTI)
|
||||
f->fingers = 3 + (p[5] & 0x03);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void alps_process_trackstick_packet_v7(struct psmouse *psmouse)
|
||||
{
|
||||
struct alps_data *priv = psmouse->private;
|
||||
unsigned char *packet = psmouse->packet;
|
||||
struct input_dev *dev2 = priv->dev2;
|
||||
int x, y, z, left, right, middle;
|
||||
|
||||
/*
|
||||
* If there were no contacts in the bitmap, use ST
|
||||
* points in MT reports.
|
||||
* If there were two contacts or more, report MT data.
|
||||
* b7 b6 b5 b4 b3 b2 b1 b0
|
||||
* Byte0 0 1 0 0 1 0 0 0
|
||||
* Byte1 1 1 * * 1 M R L
|
||||
* Byte2 X7 1 X5 X4 X3 X2 X1 X0
|
||||
* Byte3 Z6 1 Y6 X6 1 Y2 Y1 Y0
|
||||
* Byte4 Y7 0 Y5 Y4 Y3 1 1 0
|
||||
* Byte5 T&P 0 Z5 Z4 Z3 Z2 Z1 Z0
|
||||
* M / R / L: Middle / Right / Left button
|
||||
*/
|
||||
if (priv->fingers < 2) {
|
||||
x1 = x;
|
||||
y1 = y;
|
||||
fingers = z > 0 ? 1 : 0;
|
||||
} else {
|
||||
fingers = priv->fingers;
|
||||
x1 = priv->x1;
|
||||
x2 = priv->x2;
|
||||
y1 = priv->y1;
|
||||
y2 = priv->y2;
|
||||
}
|
||||
|
||||
if (z >= 64)
|
||||
input_report_key(dev, BTN_TOUCH, 1);
|
||||
else
|
||||
input_report_key(dev, BTN_TOUCH, 0);
|
||||
x = ((packet[2] & 0xbf)) | ((packet[3] & 0x10) << 2);
|
||||
y = (packet[3] & 0x07) | (packet[4] & 0xb8) |
|
||||
((packet[3] & 0x20) << 1);
|
||||
z = (packet[5] & 0x3f) | ((packet[3] & 0x80) >> 1);
|
||||
|
||||
alps_report_semi_mt_data(dev, fingers, x1, y1, x2, y2);
|
||||
left = (packet[1] & 0x01);
|
||||
right = (packet[1] & 0x02) >> 1;
|
||||
middle = (packet[1] & 0x04) >> 2;
|
||||
|
||||
input_mt_report_finger_count(dev, fingers);
|
||||
/* Divide 2 since trackpoint's speed is too fast */
|
||||
input_report_rel(dev2, REL_X, (char)x / 2);
|
||||
input_report_rel(dev2, REL_Y, -((char)y / 2));
|
||||
|
||||
input_report_key(dev, BTN_LEFT, left);
|
||||
input_report_key(dev, BTN_RIGHT, right);
|
||||
input_report_key(dev2, BTN_LEFT, left);
|
||||
input_report_key(dev2, BTN_RIGHT, right);
|
||||
input_report_key(dev2, BTN_MIDDLE, middle);
|
||||
|
||||
if (z > 0) {
|
||||
input_report_abs(dev, ABS_X, x);
|
||||
input_report_abs(dev, ABS_Y, y);
|
||||
}
|
||||
input_report_abs(dev, ABS_PRESSURE, z);
|
||||
input_sync(dev2);
|
||||
}
|
||||
|
||||
static void alps_process_touchpad_packet_v7(struct psmouse *psmouse)
|
||||
{
|
||||
struct alps_data *priv = psmouse->private;
|
||||
struct input_dev *dev = psmouse->dev;
|
||||
struct alps_fields *f = &priv->f;
|
||||
|
||||
memset(f, 0, sizeof(*f));
|
||||
|
||||
if (priv->decode_fields(f, psmouse->packet, psmouse))
|
||||
return;
|
||||
|
||||
alps_report_mt_data(psmouse, alps_get_mt_count(f->mt));
|
||||
|
||||
input_mt_report_finger_count(dev, f->fingers);
|
||||
|
||||
input_report_key(dev, BTN_LEFT, f->left);
|
||||
input_report_key(dev, BTN_RIGHT, f->right);
|
||||
input_report_key(dev, BTN_MIDDLE, f->middle);
|
||||
|
||||
input_sync(dev);
|
||||
}
|
||||
|
||||
static void alps_process_packet_v7(struct psmouse *psmouse)
|
||||
{
|
||||
unsigned char *packet = psmouse->packet;
|
||||
|
||||
if (packet[0] == 0x48 && (packet[4] & 0x47) == 0x06)
|
||||
alps_process_trackstick_packet_v7(psmouse);
|
||||
else
|
||||
alps_process_touchpad_packet_v7(psmouse);
|
||||
}
|
||||
|
||||
static void alps_report_bare_ps2_packet(struct psmouse *psmouse,
|
||||
unsigned char packet[],
|
||||
bool report_buttons)
|
||||
|
|
@ -1080,6 +1189,14 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse)
|
|||
return PSMOUSE_BAD_DATA;
|
||||
}
|
||||
|
||||
if (priv->proto_version == ALPS_PROTO_V7 &&
|
||||
!alps_is_valid_package_v7(psmouse)) {
|
||||
psmouse_dbg(psmouse, "refusing packet[%i] = %x\n",
|
||||
psmouse->pktcnt - 1,
|
||||
psmouse->packet[psmouse->pktcnt - 1]);
|
||||
return PSMOUSE_BAD_DATA;
|
||||
}
|
||||
|
||||
if (psmouse->pktcnt == psmouse->pktsize) {
|
||||
priv->process_packet(psmouse);
|
||||
return PSMOUSE_FULL_PACKET;
|
||||
|
|
@ -1192,6 +1309,22 @@ static int alps_rpt_cmd(struct psmouse *psmouse, int init_command,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static bool alps_check_valid_firmware_id(unsigned char id[])
|
||||
{
|
||||
if (id[0] == 0x73)
|
||||
return true;
|
||||
|
||||
if (id[0] == 0x88 &&
|
||||
(id[1] == 0x07 ||
|
||||
id[1] == 0x08 ||
|
||||
(id[1] & 0xf0) == 0xb0 ||
|
||||
(id[1] & 0xf0) == 0xc0)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int alps_enter_command_mode(struct psmouse *psmouse)
|
||||
{
|
||||
unsigned char param[4];
|
||||
|
|
@ -1201,8 +1334,7 @@ static int alps_enter_command_mode(struct psmouse *psmouse)
|
|||
return -1;
|
||||
}
|
||||
|
||||
if ((param[0] != 0x88 || (param[1] != 0x07 && param[1] != 0x08)) &&
|
||||
param[0] != 0x73) {
|
||||
if (!alps_check_valid_firmware_id(param)) {
|
||||
psmouse_dbg(psmouse,
|
||||
"unknown response while entering command mode\n");
|
||||
return -1;
|
||||
|
|
@ -1660,6 +1792,45 @@ error:
|
|||
return -1;
|
||||
}
|
||||
|
||||
static int alps_get_v3_v7_resolution(struct psmouse *psmouse, int reg_pitch)
|
||||
{
|
||||
int reg, x_pitch, y_pitch, x_electrode, y_electrode, x_phys, y_phys;
|
||||
struct alps_data *priv = psmouse->private;
|
||||
|
||||
reg = alps_command_mode_read_reg(psmouse, reg_pitch);
|
||||
if (reg < 0)
|
||||
return reg;
|
||||
|
||||
x_pitch = (char)(reg << 4) >> 4; /* sign extend lower 4 bits */
|
||||
x_pitch = 50 + 2 * x_pitch; /* In 0.1 mm units */
|
||||
|
||||
y_pitch = (char)reg >> 4; /* sign extend upper 4 bits */
|
||||
y_pitch = 36 + 2 * y_pitch; /* In 0.1 mm units */
|
||||
|
||||
reg = alps_command_mode_read_reg(psmouse, reg_pitch + 1);
|
||||
if (reg < 0)
|
||||
return reg;
|
||||
|
||||
x_electrode = (char)(reg << 4) >> 4; /* sign extend lower 4 bits */
|
||||
x_electrode = 17 + x_electrode;
|
||||
|
||||
y_electrode = (char)reg >> 4; /* sign extend upper 4 bits */
|
||||
y_electrode = 13 + y_electrode;
|
||||
|
||||
x_phys = x_pitch * (x_electrode - 1); /* In 0.1 mm units */
|
||||
y_phys = y_pitch * (y_electrode - 1); /* In 0.1 mm units */
|
||||
|
||||
priv->x_res = priv->x_max * 10 / x_phys; /* units / mm */
|
||||
priv->y_res = priv->y_max * 10 / y_phys; /* units / mm */
|
||||
|
||||
psmouse_dbg(psmouse,
|
||||
"pitch %dx%d num-electrodes %dx%d physical size %dx%d mm res %dx%d\n",
|
||||
x_pitch, y_pitch, x_electrode, y_electrode,
|
||||
x_phys / 10, y_phys / 10, priv->x_res, priv->y_res);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alps_hw_init_rushmore_v3(struct psmouse *psmouse)
|
||||
{
|
||||
struct alps_data *priv = psmouse->private;
|
||||
|
|
@ -1680,6 +1851,9 @@ static int alps_hw_init_rushmore_v3(struct psmouse *psmouse)
|
|||
alps_command_mode_write_reg(psmouse, 0xc2cb, 0x00))
|
||||
goto error;
|
||||
|
||||
if (alps_get_v3_v7_resolution(psmouse, 0xc2da))
|
||||
goto error;
|
||||
|
||||
reg_val = alps_command_mode_read_reg(psmouse, 0xc2c6);
|
||||
if (reg_val == -1)
|
||||
goto error;
|
||||
|
|
@ -1856,6 +2030,35 @@ static int alps_hw_init_dolphin_v1(struct psmouse *psmouse)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int alps_hw_init_v7(struct psmouse *psmouse)
|
||||
{
|
||||
struct ps2dev *ps2dev = &psmouse->ps2dev;
|
||||
int reg_val, ret = -1;
|
||||
|
||||
if (alps_enter_command_mode(psmouse) ||
|
||||
alps_command_mode_read_reg(psmouse, 0xc2d9) == -1)
|
||||
goto error;
|
||||
|
||||
if (alps_get_v3_v7_resolution(psmouse, 0xc397))
|
||||
goto error;
|
||||
|
||||
if (alps_command_mode_write_reg(psmouse, 0xc2c9, 0x64))
|
||||
goto error;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static void alps_set_defaults(struct alps_data *priv)
|
||||
{
|
||||
priv->byte0 = 0x8f;
|
||||
|
|
@ -1914,6 +2117,21 @@ static void alps_set_defaults(struct alps_data *priv)
|
|||
priv->x_max = 2047;
|
||||
priv->y_max = 1535;
|
||||
break;
|
||||
case ALPS_PROTO_V7:
|
||||
priv->hw_init = alps_hw_init_v7;
|
||||
priv->process_packet = alps_process_packet_v7;
|
||||
priv->decode_fields = alps_decode_packet_v7;
|
||||
priv->set_abs_params = alps_set_abs_params_mt;
|
||||
priv->nibble_commands = alps_v3_nibble_commands;
|
||||
priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
|
||||
priv->x_max = 0xfff;
|
||||
priv->y_max = 0x7ff;
|
||||
priv->byte0 = 0x48;
|
||||
priv->mask0 = 0x48;
|
||||
|
||||
if (priv->fw_ver[1] != 0xba)
|
||||
priv->flags |= ALPS_BUTTONPAD;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1972,6 +2190,9 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv)
|
|||
alps_exit_command_mode(psmouse))
|
||||
return -EIO;
|
||||
|
||||
/* Save the Firmware version */
|
||||
memcpy(priv->fw_ver, ec, 3);
|
||||
|
||||
if (alps_match_table(psmouse, priv, e7, ec) == 0) {
|
||||
return 0;
|
||||
} else if (e7[0] == 0x73 && e7[1] == 0x03 && e7[2] == 0x50 &&
|
||||
|
|
@ -1982,6 +2203,12 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv)
|
|||
return -EIO;
|
||||
else
|
||||
return 0;
|
||||
} else if (ec[0] == 0x88 &&
|
||||
((ec[1] & 0xf0) == 0xb0 || (ec[1] & 0xf0) == 0xc0)) {
|
||||
priv->proto_version = ALPS_PROTO_V7;
|
||||
alps_set_defaults(priv);
|
||||
|
||||
return 0;
|
||||
} else if (ec[0] == 0x88 && ec[1] == 0x08) {
|
||||
priv->proto_version = ALPS_PROTO_V3;
|
||||
alps_set_defaults(priv);
|
||||
|
|
@ -1990,6 +2217,7 @@ static int alps_identify(struct psmouse *psmouse, struct alps_data *priv)
|
|||
priv->decode_fields = alps_decode_rushmore;
|
||||
priv->x_bits = 16;
|
||||
priv->y_bits = 12;
|
||||
priv->flags |= ALPS_IS_RUSHMORE;
|
||||
|
||||
/* hack to make addr_command, nibble_command available */
|
||||
psmouse->private = priv;
|
||||
|
|
@ -2044,17 +2272,21 @@ static void alps_set_abs_params_st(struct alps_data *priv,
|
|||
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, 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);
|
||||
input_abs_set_res(dev1, ABS_MT_POSITION_X, priv->x_res);
|
||||
input_abs_set_res(dev1, ABS_MT_POSITION_Y, priv->y_res);
|
||||
|
||||
input_mt_init_slots(dev1, MAX_TOUCHES, INPUT_MT_POINTER |
|
||||
INPUT_MT_DROP_UNUSED | INPUT_MT_TRACK | INPUT_MT_SEMI_MT);
|
||||
|
||||
set_bit(BTN_TOOL_TRIPLETAP, dev1->keybit);
|
||||
set_bit(BTN_TOOL_QUADTAP, dev1->keybit);
|
||||
|
||||
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);
|
||||
/* V7 is real multi-touch */
|
||||
if (priv->proto_version == ALPS_PROTO_V7)
|
||||
clear_bit(INPUT_PROP_SEMI_MT, dev1->propbit);
|
||||
}
|
||||
|
||||
int alps_init(struct psmouse *psmouse)
|
||||
|
|
@ -2100,7 +2332,9 @@ int alps_init(struct psmouse *psmouse)
|
|||
dev1->evbit[BIT_WORD(EV_ABS)] |= BIT_MASK(EV_ABS);
|
||||
|
||||
priv->set_abs_params(priv, dev1);
|
||||
input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0);
|
||||
/* No pressure on V7 */
|
||||
if (priv->proto_version != ALPS_PROTO_V7)
|
||||
input_set_abs_params(dev1, ABS_PRESSURE, 0, 127, 0, 0);
|
||||
|
||||
if (priv->flags & ALPS_WHEEL) {
|
||||
dev1->evbit[BIT_WORD(EV_REL)] |= BIT_MASK(EV_REL);
|
||||
|
|
@ -2117,6 +2351,9 @@ int alps_init(struct psmouse *psmouse)
|
|||
dev1->keybit[BIT_WORD(BTN_1)] |= BIT_MASK(BTN_1);
|
||||
dev1->keybit[BIT_WORD(BTN_2)] |= BIT_MASK(BTN_2);
|
||||
dev1->keybit[BIT_WORD(BTN_3)] |= BIT_MASK(BTN_3);
|
||||
} else if (priv->flags & ALPS_BUTTONPAD) {
|
||||
set_bit(INPUT_PROP_BUTTONPAD, dev1->propbit);
|
||||
clear_bit(BTN_RIGHT, dev1->keybit);
|
||||
} else {
|
||||
dev1->keybit[BIT_WORD(BTN_MIDDLE)] |= BIT_MASK(BTN_MIDDLE);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,17 +12,39 @@
|
|||
#ifndef _ALPS_H
|
||||
#define _ALPS_H
|
||||
|
||||
#include <linux/input/mt.h>
|
||||
|
||||
#define ALPS_PROTO_V1 1
|
||||
#define ALPS_PROTO_V2 2
|
||||
#define ALPS_PROTO_V3 3
|
||||
#define ALPS_PROTO_V4 4
|
||||
#define ALPS_PROTO_V5 5
|
||||
#define ALPS_PROTO_V6 6
|
||||
#define ALPS_PROTO_V7 7 /* t3btl t4s */
|
||||
|
||||
#define MAX_TOUCHES 2
|
||||
|
||||
#define DOLPHIN_COUNT_PER_ELECTRODE 64
|
||||
#define DOLPHIN_PROFILE_XOFFSET 8 /* x-electrode offset */
|
||||
#define DOLPHIN_PROFILE_YOFFSET 1 /* y-electrode offset */
|
||||
|
||||
/*
|
||||
* enum V7_PACKET_ID - defines the packet type for V7
|
||||
* V7_PACKET_ID_IDLE: There's no finger and no button activity.
|
||||
* V7_PACKET_ID_TWO: There's one or two non-resting fingers on touchpad
|
||||
* or there's button activities.
|
||||
* V7_PACKET_ID_MULTI: There are at least three non-resting fingers.
|
||||
* V7_PACKET_ID_NEW: The finger position in slot is not continues from
|
||||
* previous packet.
|
||||
*/
|
||||
enum V7_PACKET_ID {
|
||||
V7_PACKET_ID_IDLE,
|
||||
V7_PACKET_ID_TWO,
|
||||
V7_PACKET_ID_MULTI,
|
||||
V7_PACKET_ID_NEW,
|
||||
V7_PACKET_ID_UNKNOWN,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct alps_model_info - touchpad ID table
|
||||
* @signature: E7 response string to match.
|
||||
|
|
@ -46,7 +68,7 @@ struct alps_model_info {
|
|||
unsigned char command_mode_resp;
|
||||
unsigned char proto_version;
|
||||
unsigned char byte0, mask0;
|
||||
unsigned char flags;
|
||||
int flags;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
@ -65,14 +87,19 @@ struct alps_nibble_commands {
|
|||
unsigned char data;
|
||||
};
|
||||
|
||||
struct alps_bitmap_point {
|
||||
int start_bit;
|
||||
int num_bits;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @pressure: Pressure.
|
||||
* @st: position for ST.
|
||||
* @mt: position for MT.
|
||||
* @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.
|
||||
|
|
@ -86,9 +113,11 @@ struct alps_fields {
|
|||
unsigned int x_map;
|
||||
unsigned int y_map;
|
||||
unsigned int fingers;
|
||||
unsigned int x;
|
||||
unsigned int y;
|
||||
unsigned int z;
|
||||
|
||||
int pressure;
|
||||
struct input_mt_pos st;
|
||||
struct input_mt_pos mt[MAX_TOUCHES];
|
||||
|
||||
unsigned int first_mp:1;
|
||||
unsigned int is_mp:1;
|
||||
|
||||
|
|
@ -113,6 +142,7 @@ struct alps_fields {
|
|||
* 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.
|
||||
* @fw_ver: cached copy of firmware version (EC report)
|
||||
* @flags: Additional device capabilities (passthrough port, trackstick, etc.).
|
||||
* @x_max: Largest possible X position value.
|
||||
* @y_max: Largest possible Y position value.
|
||||
|
|
@ -125,11 +155,7 @@ struct alps_fields {
|
|||
* @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.
|
||||
* @f: Decoded packet data fields.
|
||||
* @quirks: Bitmap of ALPS_QUIRK_*.
|
||||
* @timer: Timer for flushing out the final report packet in the stream.
|
||||
*/
|
||||
|
|
@ -142,23 +168,25 @@ struct alps_data {
|
|||
int addr_command;
|
||||
unsigned char proto_version;
|
||||
unsigned char byte0, mask0;
|
||||
unsigned char flags;
|
||||
unsigned char fw_ver[3];
|
||||
int flags;
|
||||
int x_max;
|
||||
int y_max;
|
||||
int x_bits;
|
||||
int y_bits;
|
||||
unsigned int x_res;
|
||||
unsigned int y_res;
|
||||
|
||||
int (*hw_init)(struct psmouse *psmouse);
|
||||
void (*process_packet)(struct psmouse *psmouse);
|
||||
void (*decode_fields)(struct alps_fields *f, unsigned char *p,
|
||||
int (*decode_fields)(struct alps_fields *f, unsigned char *p,
|
||||
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];
|
||||
int x1, x2, y1, y2;
|
||||
int fingers;
|
||||
struct alps_fields f;
|
||||
u8 quirks;
|
||||
struct timer_list timer;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -170,6 +170,15 @@ static void hv_kbd_on_receive(struct hv_device *hv_dev,
|
|||
serio_interrupt(kbd_dev->hv_serio, scan_code, 0);
|
||||
}
|
||||
spin_unlock_irqrestore(&kbd_dev->lock, flags);
|
||||
|
||||
/*
|
||||
* Only trigger a wakeup on key down, otherwise
|
||||
* "echo freeze > /sys/power/state" can't really enter the
|
||||
* state because the Enter-UP can trigger a wakeup at once.
|
||||
*/
|
||||
if (!(info & IS_BREAK))
|
||||
pm_wakeup_event(&hv_dev->device, 0);
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -376,6 +385,9 @@ static int hv_kbd_probe(struct hv_device *hv_dev,
|
|||
goto err_close_vmbus;
|
||||
|
||||
serio_register_port(kbd_dev->hv_serio);
|
||||
|
||||
device_init_wakeup(&hv_dev->device, true);
|
||||
|
||||
return 0;
|
||||
|
||||
err_close_vmbus:
|
||||
|
|
@ -390,6 +402,7 @@ static int hv_kbd_remove(struct hv_device *hv_dev)
|
|||
{
|
||||
struct hv_kbd_dev *kbd_dev = hv_get_drvdata(hv_dev);
|
||||
|
||||
device_init_wakeup(&hv_dev->device, false);
|
||||
serio_unregister_port(kbd_dev->hv_serio);
|
||||
vmbus_close(hv_dev->channel);
|
||||
kfree(kbd_dev);
|
||||
|
|
|
|||
|
|
@ -73,20 +73,14 @@ config TABLET_USB_KBTAB
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called kbtab.
|
||||
|
||||
config TABLET_USB_WACOM
|
||||
tristate "Wacom Intuos/Graphire tablet support (USB)"
|
||||
depends on USB_ARCH_HAS_HCD
|
||||
select POWER_SUPPLY
|
||||
select USB
|
||||
select NEW_LEDS
|
||||
select LEDS_CLASS
|
||||
config TABLET_SERIAL_WACOM4
|
||||
tristate "Wacom protocol 4 serial tablet support"
|
||||
select SERIO
|
||||
help
|
||||
Say Y here if you want to use the USB version of the Wacom Intuos
|
||||
or Graphire tablet. Make sure to say Y to "Mouse support"
|
||||
(CONFIG_INPUT_MOUSEDEV) and/or "Event interface support"
|
||||
(CONFIG_INPUT_EVDEV) as well.
|
||||
Say Y here if you want to use Wacom protocol 4 serial tablets.
|
||||
E.g. serial versions of the Cintiq, Graphire or Penpartner.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called wacom.
|
||||
module will be called wacom_serial4.
|
||||
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -2,12 +2,10 @@
|
|||
# Makefile for the tablet drivers
|
||||
#
|
||||
|
||||
# Multipart objects.
|
||||
wacom-objs := wacom_wac.o wacom_sys.o
|
||||
|
||||
obj-$(CONFIG_TABLET_USB_ACECAD) += acecad.o
|
||||
obj-$(CONFIG_TABLET_USB_AIPTEK) += aiptek.o
|
||||
obj-$(CONFIG_TABLET_USB_GTCO) += gtco.o
|
||||
obj-$(CONFIG_TABLET_USB_HANWANG) += hanwang.o
|
||||
obj-$(CONFIG_TABLET_USB_KBTAB) += kbtab.o
|
||||
obj-$(CONFIG_TABLET_USB_WACOM) += wacom.o
|
||||
obj-$(CONFIG_TABLET_SERIAL_WACOM4) += wacom_serial4.o
|
||||
|
|
|
|||
|
|
@ -1,139 +0,0 @@
|
|||
/*
|
||||
* drivers/input/tablet/wacom.h
|
||||
*
|
||||
* USB Wacom tablet support
|
||||
*
|
||||
* Copyright (c) 2000-2004 Vojtech Pavlik <vojtech@ucw.cz>
|
||||
* Copyright (c) 2000 Andreas Bach Aaen <abach@stofanet.dk>
|
||||
* Copyright (c) 2000 Clifford Wolf <clifford@clifford.at>
|
||||
* Copyright (c) 2000 Sam Mosel <sam.mosel@computer.org>
|
||||
* Copyright (c) 2000 James E. Blair <corvus@gnu.org>
|
||||
* Copyright (c) 2000 Daniel Egger <egger@suse.de>
|
||||
* Copyright (c) 2001 Frederic Lepied <flepied@mandrakesoft.com>
|
||||
* Copyright (c) 2004 Panagiotis Issaris <panagiotis.issaris@mech.kuleuven.ac.be>
|
||||
* Copyright (c) 2002-2011 Ping Cheng <pingc@wacom.com>
|
||||
*
|
||||
* ChangeLog:
|
||||
* v0.1 (vp) - Initial release
|
||||
* v0.2 (aba) - Support for all buttons / combinations
|
||||
* v0.3 (vp) - Support for Intuos added
|
||||
* v0.4 (sm) - Support for more Intuos models, menustrip
|
||||
* relative mode, proximity.
|
||||
* v0.5 (vp) - Big cleanup, nifty features removed,
|
||||
* they belong in userspace
|
||||
* v1.8 (vp) - Submit URB only when operating, moved to CVS,
|
||||
* use input_report_key instead of report_btn and
|
||||
* other cleanups
|
||||
* v1.11 (vp) - Add URB ->dev setting for new kernels
|
||||
* v1.11 (jb) - Add support for the 4D Mouse & Lens
|
||||
* v1.12 (de) - Add support for two more inking pen IDs
|
||||
* v1.14 (vp) - Use new USB device id probing scheme.
|
||||
* Fix Wacom Graphire mouse wheel
|
||||
* v1.18 (vp) - Fix mouse wheel direction
|
||||
* Make mouse relative
|
||||
* v1.20 (fl) - Report tool id for Intuos devices
|
||||
* - Multi tools support
|
||||
* - Corrected Intuos protocol decoding (airbrush, 4D mouse, lens cursor...)
|
||||
* - Add PL models support
|
||||
* - Fix Wacom Graphire mouse wheel again
|
||||
* v1.21 (vp) - Removed protocol descriptions
|
||||
* - Added MISC_SERIAL for tool serial numbers
|
||||
* (gb) - Identify version on module load.
|
||||
* v1.21.1 (fl) - added Graphire2 support
|
||||
* v1.21.2 (fl) - added Intuos2 support
|
||||
* - added all the PL ids
|
||||
* v1.21.3 (fl) - added another eraser id from Neil Okamoto
|
||||
* - added smooth filter for Graphire from Peri Hankey
|
||||
* - added PenPartner support from Olaf van Es
|
||||
* - new tool ids from Ole Martin Bjoerndalen
|
||||
* v1.29 (pc) - Add support for more tablets
|
||||
* - Fix pressure reporting
|
||||
* v1.30 (vp) - Merge 2.4 and 2.5 drivers
|
||||
* - Since 2.5 now has input_sync(), remove MSC_SERIAL abuse
|
||||
* - Cleanups here and there
|
||||
* v1.30.1 (pi) - Added Graphire3 support
|
||||
* v1.40 (pc) - Add support for several new devices, fix eraser reporting, ...
|
||||
* v1.43 (pc) - Added support for Cintiq 21UX
|
||||
* - Fixed a Graphire bug
|
||||
* - Merged wacom_intuos3_irq into wacom_intuos_irq
|
||||
* v1.44 (pc) - Added support for Graphire4, Cintiq 710, Intuos3 6x11, etc.
|
||||
* - Report Device IDs
|
||||
* v1.45 (pc) - Added support for DTF 521, Intuos3 12x12 and 12x19
|
||||
* - Minor data report fix
|
||||
* v1.46 (pc) - Split wacom.c into wacom_sys.c and wacom_wac.c,
|
||||
* - where wacom_sys.c deals with system specific code,
|
||||
* - and wacom_wac.c deals with Wacom specific code
|
||||
* - Support Intuos3 4x6
|
||||
* v1.47 (pc) - Added support for Bamboo
|
||||
* v1.48 (pc) - Added support for Bamboo1, BambooFun, and Cintiq 12WX
|
||||
* v1.49 (pc) - Added support for USB Tablet PC (0x90, 0x93, and 0x9A)
|
||||
* v1.50 (pc) - Fixed a TabletPC touch bug in 2.6.28
|
||||
* v1.51 (pc) - Added support for Intuos4
|
||||
* v1.52 (pc) - Query Wacom data upon system resume
|
||||
* - add defines for features->type
|
||||
* - add new devices (0x9F, 0xE2, and 0XE3)
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef WACOM_H
|
||||
#define WACOM_H
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/usb/input.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
/*
|
||||
* Version Information
|
||||
*/
|
||||
#define DRIVER_VERSION "v1.53"
|
||||
#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"
|
||||
#define DRIVER_DESC "USB Wacom tablet driver"
|
||||
#define DRIVER_LICENSE "GPL"
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE(DRIVER_LICENSE);
|
||||
|
||||
#define USB_VENDOR_ID_WACOM 0x056a
|
||||
#define USB_VENDOR_ID_LENOVO 0x17ef
|
||||
|
||||
struct wacom {
|
||||
dma_addr_t data_dma;
|
||||
struct usb_device *usbdev;
|
||||
struct usb_interface *intf;
|
||||
struct urb *irq;
|
||||
struct wacom_wac wacom_wac;
|
||||
struct mutex lock;
|
||||
struct work_struct work;
|
||||
bool open;
|
||||
char phys[32];
|
||||
struct wacom_led {
|
||||
u8 select[2]; /* status led selector (0..3) */
|
||||
u8 llv; /* status led brightness no button (1..127) */
|
||||
u8 hlv; /* status led brightness button pressed (1..127) */
|
||||
u8 img_lum; /* OLED matrix display brightness */
|
||||
} led;
|
||||
struct power_supply battery;
|
||||
};
|
||||
|
||||
static inline void wacom_schedule_work(struct wacom_wac *wacom_wac)
|
||||
{
|
||||
struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac);
|
||||
schedule_work(&wacom->work);
|
||||
}
|
||||
|
||||
extern const struct usb_device_id wacom_ids[];
|
||||
|
||||
void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len);
|
||||
void wacom_setup_device_quirks(struct wacom_features *features);
|
||||
int wacom_setup_input_capabilities(struct input_dev *input_dev,
|
||||
struct wacom_wac *wacom_wac);
|
||||
#endif
|
||||
620
drivers/input/tablet/wacom_serial4.c
Normal file
620
drivers/input/tablet/wacom_serial4.c
Normal file
|
|
@ -0,0 +1,620 @@
|
|||
/*
|
||||
* Wacom protocol 4 serial tablet driver
|
||||
*
|
||||
* Copyright 2014 Hans de Goede <hdegoede@redhat.com>
|
||||
* Copyright 2011-2012 Julian Squires <julian@cipht.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version of 2 of the License, or (at your
|
||||
* option) any later version. See the file COPYING in the main directory of
|
||||
* this archive for more details.
|
||||
*
|
||||
* Many thanks to Bill Seremetis, without whom PenPartner support
|
||||
* would not have been possible. Thanks to Patrick Mahoney.
|
||||
*
|
||||
* This driver was developed with reference to much code written by others,
|
||||
* particularly:
|
||||
* - elo, gunze drivers by Vojtech Pavlik <vojtech@ucw.cz>;
|
||||
* - wacom_w8001 driver by Jaya Kumar <jayakumar.lkml@gmail.com>;
|
||||
* - the USB wacom input driver, credited to many people
|
||||
* (see drivers/input/tablet/wacom.h);
|
||||
* - new and old versions of linuxwacom / xf86-input-wacom credited to
|
||||
* Frederic Lepied, France. <Lepied@XFree86.org> and
|
||||
* Ping Cheng, Wacom. <pingc@wacom.com>;
|
||||
* - and xf86wacom.c (a presumably ancient version of the linuxwacom code),
|
||||
* by Frederic Lepied and Raph Levien <raph@gtk.org>.
|
||||
*
|
||||
* To do:
|
||||
* - support pad buttons; (requires access to a model with pad buttons)
|
||||
* - support (protocol 4-style) tilt (requires access to a > 1.4 rom model)
|
||||
*/
|
||||
|
||||
/*
|
||||
* Wacom serial protocol 4 documentation taken from linuxwacom-0.9.9 code,
|
||||
* protocol 4 uses 7 or 9 byte of data in the following format:
|
||||
*
|
||||
* Byte 1
|
||||
* bit 7 Sync bit always 1
|
||||
* bit 6 Pointing device detected
|
||||
* bit 5 Cursor = 0 / Stylus = 1
|
||||
* bit 4 Reserved
|
||||
* bit 3 1 if a button on the pointing device has been pressed
|
||||
* bit 2 P0 (optional)
|
||||
* bit 1 X15
|
||||
* bit 0 X14
|
||||
*
|
||||
* Byte 2
|
||||
* bit 7 Always 0
|
||||
* bits 6-0 = X13 - X7
|
||||
*
|
||||
* Byte 3
|
||||
* bit 7 Always 0
|
||||
* bits 6-0 = X6 - X0
|
||||
*
|
||||
* Byte 4
|
||||
* bit 7 Always 0
|
||||
* bit 6 B3
|
||||
* bit 5 B2
|
||||
* bit 4 B1
|
||||
* bit 3 B0
|
||||
* bit 2 P1 (optional)
|
||||
* bit 1 Y15
|
||||
* bit 0 Y14
|
||||
*
|
||||
* Byte 5
|
||||
* bit 7 Always 0
|
||||
* bits 6-0 = Y13 - Y7
|
||||
*
|
||||
* Byte 6
|
||||
* bit 7 Always 0
|
||||
* bits 6-0 = Y6 - Y0
|
||||
*
|
||||
* Byte 7
|
||||
* bit 7 Always 0
|
||||
* bit 6 Sign of pressure data; or wheel-rel for cursor tool
|
||||
* bit 5 P7; or REL1 for cursor tool
|
||||
* bit 4 P6; or REL0 for cursor tool
|
||||
* bit 3 P5
|
||||
* bit 2 P4
|
||||
* bit 1 P3
|
||||
* bit 0 P2
|
||||
*
|
||||
* byte 8 and 9 are optional and present only
|
||||
* in tilt mode.
|
||||
*
|
||||
* Byte 8
|
||||
* bit 7 Always 0
|
||||
* bit 6 Sign of tilt X
|
||||
* bit 5 Xt6
|
||||
* bit 4 Xt5
|
||||
* bit 3 Xt4
|
||||
* bit 2 Xt3
|
||||
* bit 1 Xt2
|
||||
* bit 0 Xt1
|
||||
*
|
||||
* Byte 9
|
||||
* bit 7 Always 0
|
||||
* bit 6 Sign of tilt Y
|
||||
* bit 5 Yt6
|
||||
* bit 4 Yt5
|
||||
* bit 3 Yt4
|
||||
* bit 2 Yt3
|
||||
* bit 1 Yt2
|
||||
* bit 0 Yt1
|
||||
*/
|
||||
|
||||
#include <linux/completion.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/serio.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
MODULE_AUTHOR("Julian Squires <julian@cipht.net>, Hans de Goede <hdegoede@redhat.com>");
|
||||
MODULE_DESCRIPTION("Wacom protocol 4 serial tablet driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define REQUEST_MODEL_AND_ROM_VERSION "~#"
|
||||
#define REQUEST_MAX_COORDINATES "~C\r"
|
||||
#define REQUEST_CONFIGURATION_STRING "~R\r"
|
||||
#define REQUEST_RESET_TO_PROTOCOL_IV "\r#"
|
||||
/*
|
||||
* Note: sending "\r$\r" causes at least the Digitizer II to send
|
||||
* packets in ASCII instead of binary. "\r#" seems to undo that.
|
||||
*/
|
||||
|
||||
#define COMMAND_START_SENDING_PACKETS "ST\r"
|
||||
#define COMMAND_STOP_SENDING_PACKETS "SP\r"
|
||||
#define COMMAND_MULTI_MODE_INPUT "MU1\r"
|
||||
#define COMMAND_ORIGIN_IN_UPPER_LEFT "OC1\r"
|
||||
#define COMMAND_ENABLE_ALL_MACRO_BUTTONS "~M0\r"
|
||||
#define COMMAND_DISABLE_GROUP_1_MACRO_BUTTONS "~M1\r"
|
||||
#define COMMAND_TRANSMIT_AT_MAX_RATE "IT0\r"
|
||||
#define COMMAND_DISABLE_INCREMENTAL_MODE "IN0\r"
|
||||
#define COMMAND_ENABLE_CONTINUOUS_MODE "SR\r"
|
||||
#define COMMAND_ENABLE_PRESSURE_MODE "PH1\r"
|
||||
#define COMMAND_Z_FILTER "ZF1\r"
|
||||
|
||||
/* Note that this is a protocol 4 packet without tilt information. */
|
||||
#define PACKET_LENGTH 7
|
||||
#define DATA_SIZE 32
|
||||
|
||||
/* flags */
|
||||
#define F_COVERS_SCREEN 0x01
|
||||
#define F_HAS_STYLUS2 0x02
|
||||
#define F_HAS_SCROLLWHEEL 0x04
|
||||
|
||||
/* device IDs */
|
||||
#define STYLUS_DEVICE_ID 0x02
|
||||
#define CURSOR_DEVICE_ID 0x06
|
||||
#define ERASER_DEVICE_ID 0x0A
|
||||
|
||||
enum { STYLUS = 1, ERASER, CURSOR };
|
||||
|
||||
static const struct {
|
||||
int device_id;
|
||||
int input_id;
|
||||
} tools[] = {
|
||||
{ 0, 0 },
|
||||
{ STYLUS_DEVICE_ID, BTN_TOOL_PEN },
|
||||
{ ERASER_DEVICE_ID, BTN_TOOL_RUBBER },
|
||||
{ CURSOR_DEVICE_ID, BTN_TOOL_MOUSE },
|
||||
};
|
||||
|
||||
struct wacom {
|
||||
struct input_dev *dev;
|
||||
struct completion cmd_done;
|
||||
int result;
|
||||
u8 expect;
|
||||
u8 eraser_mask;
|
||||
unsigned int extra_z_bits;
|
||||
unsigned int flags;
|
||||
unsigned int res_x, res_y;
|
||||
unsigned int max_x, max_y;
|
||||
unsigned int tool;
|
||||
unsigned int idx;
|
||||
u8 data[DATA_SIZE];
|
||||
char phys[32];
|
||||
};
|
||||
|
||||
enum {
|
||||
MODEL_CINTIQ = 0x504C, /* PL */
|
||||
MODEL_CINTIQ2 = 0x4454, /* DT */
|
||||
MODEL_DIGITIZER_II = 0x5544, /* UD */
|
||||
MODEL_GRAPHIRE = 0x4554, /* ET */
|
||||
MODEL_PENPARTNER = 0x4354, /* CT */
|
||||
};
|
||||
|
||||
static void wacom_handle_model_response(struct wacom *wacom)
|
||||
{
|
||||
int major_v, minor_v, r = 0;
|
||||
char *p;
|
||||
|
||||
p = strrchr(wacom->data, 'V');
|
||||
if (p)
|
||||
r = sscanf(p + 1, "%u.%u", &major_v, &minor_v);
|
||||
if (r != 2)
|
||||
major_v = minor_v = 0;
|
||||
|
||||
switch (wacom->data[2] << 8 | wacom->data[3]) {
|
||||
case MODEL_CINTIQ: /* UNTESTED */
|
||||
case MODEL_CINTIQ2:
|
||||
if ((wacom->data[2] << 8 | wacom->data[3]) == MODEL_CINTIQ) {
|
||||
wacom->dev->name = "Wacom Cintiq";
|
||||
wacom->dev->id.version = MODEL_CINTIQ;
|
||||
} else {
|
||||
wacom->dev->name = "Wacom Cintiq II";
|
||||
wacom->dev->id.version = MODEL_CINTIQ2;
|
||||
}
|
||||
wacom->res_x = 508;
|
||||
wacom->res_y = 508;
|
||||
|
||||
switch (wacom->data[5] << 8 | wacom->data[6]) {
|
||||
case 0x3731: /* PL-710 */
|
||||
wacom->res_x = 2540;
|
||||
wacom->res_y = 2540;
|
||||
/* fall through */
|
||||
case 0x3535: /* PL-550 */
|
||||
case 0x3830: /* PL-800 */
|
||||
wacom->extra_z_bits = 2;
|
||||
}
|
||||
|
||||
wacom->flags = F_COVERS_SCREEN;
|
||||
break;
|
||||
|
||||
case MODEL_PENPARTNER:
|
||||
wacom->dev->name = "Wacom Penpartner";
|
||||
wacom->dev->id.version = MODEL_PENPARTNER;
|
||||
wacom->res_x = 1000;
|
||||
wacom->res_y = 1000;
|
||||
break;
|
||||
|
||||
case MODEL_GRAPHIRE:
|
||||
wacom->dev->name = "Wacom Graphire";
|
||||
wacom->dev->id.version = MODEL_GRAPHIRE;
|
||||
wacom->res_x = 1016;
|
||||
wacom->res_y = 1016;
|
||||
wacom->max_x = 5103;
|
||||
wacom->max_y = 3711;
|
||||
wacom->extra_z_bits = 2;
|
||||
wacom->eraser_mask = 0x08;
|
||||
wacom->flags = F_HAS_STYLUS2 | F_HAS_SCROLLWHEEL;
|
||||
break;
|
||||
|
||||
case MODEL_DIGITIZER_II:
|
||||
wacom->dev->name = "Wacom Digitizer II";
|
||||
wacom->dev->id.version = MODEL_DIGITIZER_II;
|
||||
if (major_v == 1 && minor_v <= 2)
|
||||
wacom->extra_z_bits = 0; /* UNTESTED */
|
||||
break;
|
||||
|
||||
default:
|
||||
dev_err(&wacom->dev->dev, "Unsupported Wacom model %s\n",
|
||||
wacom->data);
|
||||
wacom->result = -ENODEV;
|
||||
return;
|
||||
}
|
||||
|
||||
dev_info(&wacom->dev->dev, "%s tablet, version %u.%u\n",
|
||||
wacom->dev->name, major_v, minor_v);
|
||||
}
|
||||
|
||||
static void wacom_handle_configuration_response(struct wacom *wacom)
|
||||
{
|
||||
int r, skip;
|
||||
|
||||
dev_dbg(&wacom->dev->dev, "Configuration string: %s\n", wacom->data);
|
||||
r = sscanf(wacom->data, "~R%x,%u,%u,%u,%u", &skip, &skip, &skip,
|
||||
&wacom->res_x, &wacom->res_y);
|
||||
if (r != 5)
|
||||
dev_warn(&wacom->dev->dev, "could not get resolution\n");
|
||||
}
|
||||
|
||||
static void wacom_handle_coordinates_response(struct wacom *wacom)
|
||||
{
|
||||
int r;
|
||||
|
||||
dev_dbg(&wacom->dev->dev, "Coordinates string: %s\n", wacom->data);
|
||||
r = sscanf(wacom->data, "~C%u,%u", &wacom->max_x, &wacom->max_y);
|
||||
if (r != 2)
|
||||
dev_warn(&wacom->dev->dev, "could not get max coordinates\n");
|
||||
}
|
||||
|
||||
static void wacom_handle_response(struct wacom *wacom)
|
||||
{
|
||||
if (wacom->data[0] != '~' || wacom->data[1] != wacom->expect) {
|
||||
dev_err(&wacom->dev->dev,
|
||||
"Wacom got an unexpected response: %s\n", wacom->data);
|
||||
wacom->result = -EIO;
|
||||
} else {
|
||||
wacom->result = 0;
|
||||
|
||||
switch (wacom->data[1]) {
|
||||
case '#':
|
||||
wacom_handle_model_response(wacom);
|
||||
break;
|
||||
case 'R':
|
||||
wacom_handle_configuration_response(wacom);
|
||||
break;
|
||||
case 'C':
|
||||
wacom_handle_coordinates_response(wacom);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
complete(&wacom->cmd_done);
|
||||
}
|
||||
|
||||
static void wacom_handle_packet(struct wacom *wacom)
|
||||
{
|
||||
u8 in_proximity_p, stylus_p, button;
|
||||
unsigned int tool;
|
||||
int x, y, z;
|
||||
|
||||
in_proximity_p = wacom->data[0] & 0x40;
|
||||
stylus_p = wacom->data[0] & 0x20;
|
||||
button = (wacom->data[3] & 0x78) >> 3;
|
||||
x = (wacom->data[0] & 3) << 14 | wacom->data[1]<<7 | wacom->data[2];
|
||||
y = (wacom->data[3] & 3) << 14 | wacom->data[4]<<7 | wacom->data[5];
|
||||
|
||||
if (in_proximity_p && stylus_p) {
|
||||
z = wacom->data[6] & 0x7f;
|
||||
if (wacom->extra_z_bits >= 1)
|
||||
z = z << 1 | (wacom->data[3] & 0x4) >> 2;
|
||||
if (wacom->extra_z_bits > 1)
|
||||
z = z << 1 | (wacom->data[0] & 0x4) >> 2;
|
||||
z = z ^ (0x40 << wacom->extra_z_bits);
|
||||
} else {
|
||||
z = -1;
|
||||
}
|
||||
|
||||
if (stylus_p)
|
||||
tool = (button & wacom->eraser_mask) ? ERASER : STYLUS;
|
||||
else
|
||||
tool = CURSOR;
|
||||
|
||||
if (tool != wacom->tool && wacom->tool != 0) {
|
||||
input_report_key(wacom->dev, tools[wacom->tool].input_id, 0);
|
||||
input_sync(wacom->dev);
|
||||
}
|
||||
wacom->tool = tool;
|
||||
|
||||
input_report_key(wacom->dev, tools[tool].input_id, in_proximity_p);
|
||||
input_report_abs(wacom->dev, ABS_MISC,
|
||||
in_proximity_p ? tools[tool].device_id : 0);
|
||||
input_report_abs(wacom->dev, ABS_X, x);
|
||||
input_report_abs(wacom->dev, ABS_Y, y);
|
||||
input_report_abs(wacom->dev, ABS_PRESSURE, z);
|
||||
if (stylus_p) {
|
||||
input_report_key(wacom->dev, BTN_TOUCH, button & 1);
|
||||
input_report_key(wacom->dev, BTN_STYLUS, button & 2);
|
||||
input_report_key(wacom->dev, BTN_STYLUS2, button & 4);
|
||||
} else {
|
||||
input_report_key(wacom->dev, BTN_LEFT, button & 1);
|
||||
input_report_key(wacom->dev, BTN_RIGHT, button & 2);
|
||||
input_report_key(wacom->dev, BTN_MIDDLE, button & 4);
|
||||
/* handle relative wheel for non-stylus device */
|
||||
z = (wacom->data[6] & 0x30) >> 4;
|
||||
if (wacom->data[6] & 0x40)
|
||||
z = -z;
|
||||
input_report_rel(wacom->dev, REL_WHEEL, z);
|
||||
}
|
||||
input_sync(wacom->dev);
|
||||
}
|
||||
|
||||
static void wacom_clear_data_buf(struct wacom *wacom)
|
||||
{
|
||||
memset(wacom->data, 0, DATA_SIZE);
|
||||
wacom->idx = 0;
|
||||
}
|
||||
|
||||
static irqreturn_t wacom_interrupt(struct serio *serio, unsigned char data,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct wacom *wacom = serio_get_drvdata(serio);
|
||||
|
||||
if (data & 0x80)
|
||||
wacom->idx = 0;
|
||||
|
||||
/*
|
||||
* We're either expecting a carriage return-terminated ASCII
|
||||
* response string, or a seven-byte packet with the MSB set on
|
||||
* the first byte.
|
||||
*
|
||||
* Note however that some tablets (the PenPartner, for
|
||||
* example) don't send a carriage return at the end of a
|
||||
* command. We handle these by waiting for timeout.
|
||||
*/
|
||||
if (data == '\r' && !(wacom->data[0] & 0x80)) {
|
||||
wacom_handle_response(wacom);
|
||||
wacom_clear_data_buf(wacom);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/* Leave place for 0 termination */
|
||||
if (wacom->idx > (DATA_SIZE - 2)) {
|
||||
dev_dbg(&wacom->dev->dev,
|
||||
"throwing away %d bytes of garbage\n", wacom->idx);
|
||||
wacom_clear_data_buf(wacom);
|
||||
}
|
||||
wacom->data[wacom->idx++] = data;
|
||||
|
||||
if (wacom->idx == PACKET_LENGTH && (wacom->data[0] & 0x80)) {
|
||||
wacom_handle_packet(wacom);
|
||||
wacom_clear_data_buf(wacom);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void wacom_disconnect(struct serio *serio)
|
||||
{
|
||||
struct wacom *wacom = serio_get_drvdata(serio);
|
||||
|
||||
serio_close(serio);
|
||||
serio_set_drvdata(serio, NULL);
|
||||
input_unregister_device(wacom->dev);
|
||||
kfree(wacom);
|
||||
}
|
||||
|
||||
static int wacom_send(struct serio *serio, const u8 *command)
|
||||
{
|
||||
int err = 0;
|
||||
|
||||
for (; !err && *command; command++)
|
||||
err = serio_write(serio, *command);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int wacom_send_setup_string(struct wacom *wacom, struct serio *serio)
|
||||
{
|
||||
const u8 *cmd;
|
||||
|
||||
switch (wacom->dev->id.version) {
|
||||
case MODEL_CINTIQ: /* UNTESTED */
|
||||
cmd = COMMAND_ORIGIN_IN_UPPER_LEFT
|
||||
COMMAND_TRANSMIT_AT_MAX_RATE
|
||||
COMMAND_ENABLE_CONTINUOUS_MODE
|
||||
COMMAND_START_SENDING_PACKETS;
|
||||
break;
|
||||
|
||||
case MODEL_PENPARTNER:
|
||||
cmd = COMMAND_ENABLE_PRESSURE_MODE
|
||||
COMMAND_START_SENDING_PACKETS;
|
||||
break;
|
||||
|
||||
default:
|
||||
cmd = COMMAND_MULTI_MODE_INPUT
|
||||
COMMAND_ORIGIN_IN_UPPER_LEFT
|
||||
COMMAND_ENABLE_ALL_MACRO_BUTTONS
|
||||
COMMAND_DISABLE_GROUP_1_MACRO_BUTTONS
|
||||
COMMAND_TRANSMIT_AT_MAX_RATE
|
||||
COMMAND_DISABLE_INCREMENTAL_MODE
|
||||
COMMAND_ENABLE_CONTINUOUS_MODE
|
||||
COMMAND_Z_FILTER
|
||||
COMMAND_START_SENDING_PACKETS;
|
||||
break;
|
||||
}
|
||||
|
||||
return wacom_send(serio, cmd);
|
||||
}
|
||||
|
||||
static int wacom_send_and_wait(struct wacom *wacom, struct serio *serio,
|
||||
const u8 *cmd, const char *desc)
|
||||
{
|
||||
int err;
|
||||
unsigned long u;
|
||||
|
||||
wacom->expect = cmd[1];
|
||||
init_completion(&wacom->cmd_done);
|
||||
|
||||
err = wacom_send(serio, cmd);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
u = wait_for_completion_timeout(&wacom->cmd_done, HZ);
|
||||
if (u == 0) {
|
||||
/* Timeout, process what we've received. */
|
||||
wacom_handle_response(wacom);
|
||||
}
|
||||
|
||||
wacom->expect = 0;
|
||||
return wacom->result;
|
||||
}
|
||||
|
||||
static int wacom_setup(struct wacom *wacom, struct serio *serio)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* Note that setting the link speed is the job of inputattach.
|
||||
* We assume that reset negotiation has already happened,
|
||||
* here. */
|
||||
err = wacom_send_and_wait(wacom, serio, REQUEST_MODEL_AND_ROM_VERSION,
|
||||
"model and version");
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!(wacom->res_x && wacom->res_y)) {
|
||||
err = wacom_send_and_wait(wacom, serio,
|
||||
REQUEST_CONFIGURATION_STRING,
|
||||
"configuration string");
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!(wacom->max_x && wacom->max_y)) {
|
||||
err = wacom_send_and_wait(wacom, serio,
|
||||
REQUEST_MAX_COORDINATES,
|
||||
"coordinates string");
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
return wacom_send_setup_string(wacom, serio);
|
||||
}
|
||||
|
||||
static int wacom_connect(struct serio *serio, struct serio_driver *drv)
|
||||
{
|
||||
struct wacom *wacom;
|
||||
struct input_dev *input_dev;
|
||||
int err = -ENOMEM;
|
||||
|
||||
wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!wacom || !input_dev)
|
||||
goto free_device;
|
||||
|
||||
wacom->dev = input_dev;
|
||||
wacom->extra_z_bits = 1;
|
||||
wacom->eraser_mask = 0x04;
|
||||
wacom->tool = wacom->idx = 0;
|
||||
snprintf(wacom->phys, sizeof(wacom->phys), "%s/input0", serio->phys);
|
||||
input_dev->phys = wacom->phys;
|
||||
input_dev->id.bustype = BUS_RS232;
|
||||
input_dev->id.vendor = SERIO_WACOM_IV;
|
||||
input_dev->id.product = serio->id.extra;
|
||||
input_dev->dev.parent = &serio->dev;
|
||||
|
||||
input_dev->evbit[0] =
|
||||
BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS) | BIT_MASK(EV_REL);
|
||||
set_bit(ABS_MISC, input_dev->absbit);
|
||||
set_bit(BTN_TOOL_PEN, input_dev->keybit);
|
||||
set_bit(BTN_TOOL_RUBBER, input_dev->keybit);
|
||||
set_bit(BTN_TOOL_MOUSE, input_dev->keybit);
|
||||
set_bit(BTN_TOUCH, input_dev->keybit);
|
||||
set_bit(BTN_STYLUS, input_dev->keybit);
|
||||
set_bit(BTN_LEFT, input_dev->keybit);
|
||||
set_bit(BTN_RIGHT, input_dev->keybit);
|
||||
set_bit(BTN_MIDDLE, input_dev->keybit);
|
||||
|
||||
serio_set_drvdata(serio, wacom);
|
||||
|
||||
err = serio_open(serio, drv);
|
||||
if (err)
|
||||
goto free_device;
|
||||
|
||||
err = wacom_setup(wacom, serio);
|
||||
if (err)
|
||||
goto close_serio;
|
||||
|
||||
set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
|
||||
if (!(wacom->flags & F_COVERS_SCREEN))
|
||||
__set_bit(INPUT_PROP_POINTER, input_dev->propbit);
|
||||
|
||||
if (wacom->flags & F_HAS_STYLUS2)
|
||||
__set_bit(BTN_STYLUS2, input_dev->keybit);
|
||||
|
||||
if (wacom->flags & F_HAS_SCROLLWHEEL)
|
||||
__set_bit(REL_WHEEL, input_dev->relbit);
|
||||
|
||||
input_abs_set_res(wacom->dev, ABS_X, wacom->res_x);
|
||||
input_abs_set_res(wacom->dev, ABS_Y, wacom->res_y);
|
||||
input_set_abs_params(wacom->dev, ABS_X, 0, wacom->max_x, 0, 0);
|
||||
input_set_abs_params(wacom->dev, ABS_Y, 0, wacom->max_y, 0, 0);
|
||||
input_set_abs_params(wacom->dev, ABS_PRESSURE, -1,
|
||||
(1 << (7 + wacom->extra_z_bits)) - 1, 0, 0);
|
||||
|
||||
err = input_register_device(wacom->dev);
|
||||
if (err)
|
||||
goto close_serio;
|
||||
|
||||
return 0;
|
||||
|
||||
close_serio:
|
||||
serio_close(serio);
|
||||
free_device:
|
||||
serio_set_drvdata(serio, NULL);
|
||||
input_free_device(input_dev);
|
||||
kfree(wacom);
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct serio_device_id wacom_serio_ids[] = {
|
||||
{
|
||||
.type = SERIO_RS232,
|
||||
.proto = SERIO_WACOM_IV,
|
||||
.id = SERIO_ANY,
|
||||
.extra = SERIO_ANY,
|
||||
},
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(serio, wacom_serio_ids);
|
||||
|
||||
static struct serio_driver wacom_drv = {
|
||||
.driver = {
|
||||
.name = "wacom_serial4",
|
||||
},
|
||||
.description = "Wacom protocol 4 serial tablet driver",
|
||||
.id_table = wacom_serio_ids,
|
||||
.interrupt = wacom_interrupt,
|
||||
.connect = wacom_connect,
|
||||
.disconnect = wacom_disconnect,
|
||||
};
|
||||
|
||||
module_serio_driver(wacom_drv);
|
||||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,165 +0,0 @@
|
|||
/*
|
||||
* drivers/input/tablet/wacom_wac.h
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef WACOM_WAC_H
|
||||
#define WACOM_WAC_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
/* maximum packet length for USB devices */
|
||||
#define WACOM_PKGLEN_MAX 68
|
||||
|
||||
#define WACOM_NAME_MAX 64
|
||||
|
||||
/* packet length for individual models */
|
||||
#define WACOM_PKGLEN_PENPRTN 7
|
||||
#define WACOM_PKGLEN_GRAPHIRE 8
|
||||
#define WACOM_PKGLEN_BBFUN 9
|
||||
#define WACOM_PKGLEN_INTUOS 10
|
||||
#define WACOM_PKGLEN_TPC1FG 5
|
||||
#define WACOM_PKGLEN_TPC1FG_B 10
|
||||
#define WACOM_PKGLEN_TPC2FG 14
|
||||
#define WACOM_PKGLEN_BBTOUCH 20
|
||||
#define WACOM_PKGLEN_BBTOUCH3 64
|
||||
#define WACOM_PKGLEN_BBPEN 10
|
||||
#define WACOM_PKGLEN_WIRELESS 32
|
||||
#define WACOM_PKGLEN_MTOUCH 62
|
||||
#define WACOM_PKGLEN_MTTPC 40
|
||||
#define WACOM_PKGLEN_DTUS 68
|
||||
#define WACOM_PKGLEN_PENABLED 8
|
||||
|
||||
/* wacom data size per MT contact */
|
||||
#define WACOM_BYTES_PER_MT_PACKET 11
|
||||
#define WACOM_BYTES_PER_24HDT_PACKET 14
|
||||
|
||||
/* device IDs */
|
||||
#define STYLUS_DEVICE_ID 0x02
|
||||
#define TOUCH_DEVICE_ID 0x03
|
||||
#define CURSOR_DEVICE_ID 0x06
|
||||
#define ERASER_DEVICE_ID 0x0A
|
||||
#define PAD_DEVICE_ID 0x0F
|
||||
|
||||
/* wacom data packet report IDs */
|
||||
#define WACOM_REPORT_PENABLED 2
|
||||
#define WACOM_REPORT_INTUOSREAD 5
|
||||
#define WACOM_REPORT_INTUOSWRITE 6
|
||||
#define WACOM_REPORT_INTUOSPAD 12
|
||||
#define WACOM_REPORT_INTUOS5PAD 3
|
||||
#define WACOM_REPORT_DTUSPAD 21
|
||||
#define WACOM_REPORT_TPC1FG 6
|
||||
#define WACOM_REPORT_TPC2FG 13
|
||||
#define WACOM_REPORT_TPCMT 13
|
||||
#define WACOM_REPORT_TPCMT2 3
|
||||
#define WACOM_REPORT_TPCHID 15
|
||||
#define WACOM_REPORT_TPCST 16
|
||||
#define WACOM_REPORT_DTUS 17
|
||||
#define WACOM_REPORT_TPC1FGE 18
|
||||
#define WACOM_REPORT_24HDT 1
|
||||
#define WACOM_REPORT_WL 128
|
||||
#define WACOM_REPORT_USB 192
|
||||
|
||||
/* device quirks */
|
||||
#define WACOM_QUIRK_MULTI_INPUT 0x0001
|
||||
#define WACOM_QUIRK_BBTOUCH_LOWRES 0x0002
|
||||
#define WACOM_QUIRK_NO_INPUT 0x0004
|
||||
#define WACOM_QUIRK_MONITOR 0x0008
|
||||
|
||||
enum {
|
||||
PENPARTNER = 0,
|
||||
GRAPHIRE,
|
||||
WACOM_G4,
|
||||
PTU,
|
||||
PL,
|
||||
DTU,
|
||||
DTUS,
|
||||
INTUOS,
|
||||
INTUOS3S,
|
||||
INTUOS3,
|
||||
INTUOS3L,
|
||||
INTUOS4S,
|
||||
INTUOS4,
|
||||
INTUOS4L,
|
||||
INTUOS5S,
|
||||
INTUOS5,
|
||||
INTUOS5L,
|
||||
INTUOSPS,
|
||||
INTUOSPM,
|
||||
INTUOSPL,
|
||||
INTUOSHT,
|
||||
WACOM_21UX2,
|
||||
WACOM_22HD,
|
||||
DTK,
|
||||
WACOM_24HD,
|
||||
CINTIQ_HYBRID,
|
||||
CINTIQ,
|
||||
WACOM_BEE,
|
||||
WACOM_13HD,
|
||||
WACOM_MO,
|
||||
WIRELESS,
|
||||
BAMBOO_PT,
|
||||
WACOM_24HDT,
|
||||
TABLETPC, /* add new TPC below */
|
||||
TABLETPCE,
|
||||
TABLETPC2FG,
|
||||
MTSCREEN,
|
||||
MTTPC,
|
||||
MTTPC_B,
|
||||
MAX_TYPE
|
||||
};
|
||||
|
||||
struct wacom_features {
|
||||
const char *name;
|
||||
int pktlen;
|
||||
int x_max;
|
||||
int y_max;
|
||||
int pressure_max;
|
||||
int distance_max;
|
||||
int type;
|
||||
int x_resolution;
|
||||
int y_resolution;
|
||||
int x_min;
|
||||
int y_min;
|
||||
int device_type;
|
||||
int x_phy;
|
||||
int y_phy;
|
||||
unsigned char unit;
|
||||
unsigned char unitExpo;
|
||||
int x_fuzz;
|
||||
int y_fuzz;
|
||||
int pressure_fuzz;
|
||||
int distance_fuzz;
|
||||
unsigned quirks;
|
||||
unsigned touch_max;
|
||||
int oVid;
|
||||
int oPid;
|
||||
};
|
||||
|
||||
struct wacom_shared {
|
||||
bool stylus_in_proximity;
|
||||
bool touch_down;
|
||||
/* for wireless device to access USB interfaces */
|
||||
unsigned touch_max;
|
||||
int type;
|
||||
struct input_dev *touch_input;
|
||||
};
|
||||
|
||||
struct wacom_wac {
|
||||
char name[WACOM_NAME_MAX];
|
||||
unsigned char *data;
|
||||
int tool[2];
|
||||
int id[2];
|
||||
__u32 serial[2];
|
||||
struct wacom_features features;
|
||||
struct wacom_shared *shared;
|
||||
struct input_dev *input;
|
||||
int pid;
|
||||
int battery_capacity;
|
||||
int num_contacts_left;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -471,6 +471,18 @@ config TOUCHSCREEN_HP7XX
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called jornada720_ts.
|
||||
|
||||
config TOUCHSCREEN_IPAQ_MICRO
|
||||
tristate "HP iPAQ Atmel Micro ASIC touchscreen"
|
||||
depends on MFD_IPAQ_MICRO
|
||||
help
|
||||
Say Y here to enable support for the touchscreen attached to
|
||||
the Atmel Micro peripheral controller on iPAQ h3100/h3600/h3700
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ipaq-micro-ts.
|
||||
|
||||
config TOUCHSCREEN_HTCPEN
|
||||
tristate "HTC Shift X9500 touchscreen"
|
||||
depends on ISA
|
||||
|
|
@ -555,18 +567,6 @@ config TOUCHSCREEN_TI_AM335X_TSC
|
|||
To compile this driver as a module, choose M here: the
|
||||
module will be called ti_am335x_tsc.
|
||||
|
||||
config TOUCHSCREEN_ATMEL_TSADCC
|
||||
tristate "Atmel Touchscreen Interface"
|
||||
depends on ARCH_AT91
|
||||
help
|
||||
Say Y here if you have a 4-wire touchscreen connected to the
|
||||
ADC Controller on your Atmel SoC.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called atmel_tsadcc.
|
||||
|
||||
config TOUCHSCREEN_UCB1400
|
||||
tristate "Philips UCB1400 touchscreen"
|
||||
depends on AC97_BUS
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ obj-$(CONFIG_TOUCHSCREEN_AD7879_I2C) += ad7879-i2c.o
|
|||
obj-$(CONFIG_TOUCHSCREEN_AD7879_SPI) += ad7879-spi.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_ATMEL_MXT) += atmel_mxt_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_ATMEL_TSADCC) += atmel_tsadcc.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_AUO_PIXCIR) += auo-pixcir-ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_BU21013) += bu21013_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_CY8CTMG110) += cy8ctmg110_ts.o
|
||||
|
|
@ -47,6 +46,7 @@ obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o
|
|||
obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_HP7XX) += jornada720_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_IPAQ_MICRO) += ipaq-micro-ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_HTCPEN) += htcpen.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE) += usbtouchscreen.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_PCAP) += pcap_ts.o
|
||||
|
|
|
|||
|
|
@ -1302,8 +1302,10 @@ static int ads7846_probe(struct spi_device *spi)
|
|||
pdata = dev_get_platdata(&spi->dev);
|
||||
if (!pdata) {
|
||||
pdata = ads7846_probe_dt(&spi->dev);
|
||||
if (IS_ERR(pdata))
|
||||
return PTR_ERR(pdata);
|
||||
if (IS_ERR(pdata)) {
|
||||
err = PTR_ERR(pdata);
|
||||
goto err_free_mem;
|
||||
}
|
||||
}
|
||||
|
||||
ts->model = pdata->model ? : 7846;
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,358 +0,0 @@
|
|||
/*
|
||||
* Atmel Touch Screen Driver
|
||||
*
|
||||
* Copyright (c) 2008 ATMEL
|
||||
* Copyright (c) 2008 Dan Liang
|
||||
* Copyright (c) 2008 TimeSys Corporation
|
||||
* Copyright (c) 2008 Justin Waters
|
||||
*
|
||||
* Based on touchscreen code from Atmel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/err.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/platform_data/atmel.h>
|
||||
#include <mach/cpu.h>
|
||||
|
||||
/* Register definitions based on AT91SAM9RL64 preliminary draft datasheet */
|
||||
|
||||
#define ATMEL_TSADCC_CR 0x00 /* Control register */
|
||||
#define ATMEL_TSADCC_SWRST (1 << 0) /* Software Reset*/
|
||||
#define ATMEL_TSADCC_START (1 << 1) /* Start conversion */
|
||||
|
||||
#define ATMEL_TSADCC_MR 0x04 /* Mode register */
|
||||
#define ATMEL_TSADCC_TSAMOD (3 << 0) /* ADC mode */
|
||||
#define ATMEL_TSADCC_TSAMOD_ADC_ONLY_MODE (0x0) /* ADC Mode */
|
||||
#define ATMEL_TSADCC_TSAMOD_TS_ONLY_MODE (0x1) /* Touch Screen Only Mode */
|
||||
#define ATMEL_TSADCC_LOWRES (1 << 4) /* Resolution selection */
|
||||
#define ATMEL_TSADCC_SLEEP (1 << 5) /* Sleep mode */
|
||||
#define ATMEL_TSADCC_PENDET (1 << 6) /* Pen Detect selection */
|
||||
#define ATMEL_TSADCC_PRES (1 << 7) /* Pressure Measurement Selection */
|
||||
#define ATMEL_TSADCC_PRESCAL (0x3f << 8) /* Prescalar Rate Selection */
|
||||
#define ATMEL_TSADCC_EPRESCAL (0xff << 8) /* Prescalar Rate Selection (Extended) */
|
||||
#define ATMEL_TSADCC_STARTUP (0x7f << 16) /* Start Up time */
|
||||
#define ATMEL_TSADCC_SHTIM (0xf << 24) /* Sample & Hold time */
|
||||
#define ATMEL_TSADCC_PENDBC (0xf << 28) /* Pen Detect debouncing time */
|
||||
|
||||
#define ATMEL_TSADCC_TRGR 0x08 /* Trigger register */
|
||||
#define ATMEL_TSADCC_TRGMOD (7 << 0) /* Trigger mode */
|
||||
#define ATMEL_TSADCC_TRGMOD_NONE (0 << 0)
|
||||
#define ATMEL_TSADCC_TRGMOD_EXT_RISING (1 << 0)
|
||||
#define ATMEL_TSADCC_TRGMOD_EXT_FALLING (2 << 0)
|
||||
#define ATMEL_TSADCC_TRGMOD_EXT_ANY (3 << 0)
|
||||
#define ATMEL_TSADCC_TRGMOD_PENDET (4 << 0)
|
||||
#define ATMEL_TSADCC_TRGMOD_PERIOD (5 << 0)
|
||||
#define ATMEL_TSADCC_TRGMOD_CONTINUOUS (6 << 0)
|
||||
#define ATMEL_TSADCC_TRGPER (0xffff << 16) /* Trigger period */
|
||||
|
||||
#define ATMEL_TSADCC_TSR 0x0C /* Touch Screen register */
|
||||
#define ATMEL_TSADCC_TSFREQ (0xf << 0) /* TS Frequency in Interleaved mode */
|
||||
#define ATMEL_TSADCC_TSSHTIM (0xf << 24) /* Sample & Hold time */
|
||||
|
||||
#define ATMEL_TSADCC_CHER 0x10 /* Channel Enable register */
|
||||
#define ATMEL_TSADCC_CHDR 0x14 /* Channel Disable register */
|
||||
#define ATMEL_TSADCC_CHSR 0x18 /* Channel Status register */
|
||||
#define ATMEL_TSADCC_CH(n) (1 << (n)) /* Channel number */
|
||||
|
||||
#define ATMEL_TSADCC_SR 0x1C /* Status register */
|
||||
#define ATMEL_TSADCC_EOC(n) (1 << ((n)+0)) /* End of conversion for channel N */
|
||||
#define ATMEL_TSADCC_OVRE(n) (1 << ((n)+8)) /* Overrun error for channel N */
|
||||
#define ATMEL_TSADCC_DRDY (1 << 16) /* Data Ready */
|
||||
#define ATMEL_TSADCC_GOVRE (1 << 17) /* General Overrun Error */
|
||||
#define ATMEL_TSADCC_ENDRX (1 << 18) /* End of RX Buffer */
|
||||
#define ATMEL_TSADCC_RXBUFF (1 << 19) /* TX Buffer full */
|
||||
#define ATMEL_TSADCC_PENCNT (1 << 20) /* Pen contact */
|
||||
#define ATMEL_TSADCC_NOCNT (1 << 21) /* No contact */
|
||||
|
||||
#define ATMEL_TSADCC_LCDR 0x20 /* Last Converted Data register */
|
||||
#define ATMEL_TSADCC_DATA (0x3ff << 0) /* Channel data */
|
||||
|
||||
#define ATMEL_TSADCC_IER 0x24 /* Interrupt Enable register */
|
||||
#define ATMEL_TSADCC_IDR 0x28 /* Interrupt Disable register */
|
||||
#define ATMEL_TSADCC_IMR 0x2C /* Interrupt Mask register */
|
||||
#define ATMEL_TSADCC_CDR0 0x30 /* Channel Data 0 */
|
||||
#define ATMEL_TSADCC_CDR1 0x34 /* Channel Data 1 */
|
||||
#define ATMEL_TSADCC_CDR2 0x38 /* Channel Data 2 */
|
||||
#define ATMEL_TSADCC_CDR3 0x3C /* Channel Data 3 */
|
||||
#define ATMEL_TSADCC_CDR4 0x40 /* Channel Data 4 */
|
||||
#define ATMEL_TSADCC_CDR5 0x44 /* Channel Data 5 */
|
||||
|
||||
#define ATMEL_TSADCC_XPOS 0x50
|
||||
#define ATMEL_TSADCC_Z1DAT 0x54
|
||||
#define ATMEL_TSADCC_Z2DAT 0x58
|
||||
|
||||
#define PRESCALER_VAL(x) ((x) >> 8)
|
||||
|
||||
#define ADC_DEFAULT_CLOCK 100000
|
||||
|
||||
struct atmel_tsadcc {
|
||||
struct input_dev *input;
|
||||
char phys[32];
|
||||
struct clk *clk;
|
||||
int irq;
|
||||
unsigned int prev_absx;
|
||||
unsigned int prev_absy;
|
||||
unsigned char bufferedmeasure;
|
||||
};
|
||||
|
||||
static void __iomem *tsc_base;
|
||||
|
||||
#define atmel_tsadcc_read(reg) __raw_readl(tsc_base + (reg))
|
||||
#define atmel_tsadcc_write(reg, val) __raw_writel((val), tsc_base + (reg))
|
||||
|
||||
static irqreturn_t atmel_tsadcc_interrupt(int irq, void *dev)
|
||||
{
|
||||
struct atmel_tsadcc *ts_dev = (struct atmel_tsadcc *)dev;
|
||||
struct input_dev *input_dev = ts_dev->input;
|
||||
|
||||
unsigned int status;
|
||||
unsigned int reg;
|
||||
|
||||
status = atmel_tsadcc_read(ATMEL_TSADCC_SR);
|
||||
status &= atmel_tsadcc_read(ATMEL_TSADCC_IMR);
|
||||
|
||||
if (status & ATMEL_TSADCC_NOCNT) {
|
||||
/* Contact lost */
|
||||
reg = atmel_tsadcc_read(ATMEL_TSADCC_MR) | ATMEL_TSADCC_PENDBC;
|
||||
|
||||
atmel_tsadcc_write(ATMEL_TSADCC_MR, reg);
|
||||
atmel_tsadcc_write(ATMEL_TSADCC_TRGR, ATMEL_TSADCC_TRGMOD_NONE);
|
||||
atmel_tsadcc_write(ATMEL_TSADCC_IDR,
|
||||
ATMEL_TSADCC_EOC(3) | ATMEL_TSADCC_NOCNT);
|
||||
atmel_tsadcc_write(ATMEL_TSADCC_IER, ATMEL_TSADCC_PENCNT);
|
||||
|
||||
input_report_key(input_dev, BTN_TOUCH, 0);
|
||||
ts_dev->bufferedmeasure = 0;
|
||||
input_sync(input_dev);
|
||||
|
||||
} else if (status & ATMEL_TSADCC_PENCNT) {
|
||||
/* Pen detected */
|
||||
reg = atmel_tsadcc_read(ATMEL_TSADCC_MR);
|
||||
reg &= ~ATMEL_TSADCC_PENDBC;
|
||||
|
||||
atmel_tsadcc_write(ATMEL_TSADCC_IDR, ATMEL_TSADCC_PENCNT);
|
||||
atmel_tsadcc_write(ATMEL_TSADCC_MR, reg);
|
||||
atmel_tsadcc_write(ATMEL_TSADCC_IER,
|
||||
ATMEL_TSADCC_EOC(3) | ATMEL_TSADCC_NOCNT);
|
||||
atmel_tsadcc_write(ATMEL_TSADCC_TRGR,
|
||||
ATMEL_TSADCC_TRGMOD_PERIOD | (0x0FFF << 16));
|
||||
|
||||
} else if (status & ATMEL_TSADCC_EOC(3)) {
|
||||
/* Conversion finished */
|
||||
|
||||
if (ts_dev->bufferedmeasure) {
|
||||
/* Last measurement is always discarded, since it can
|
||||
* be erroneous.
|
||||
* Always report previous measurement */
|
||||
input_report_abs(input_dev, ABS_X, ts_dev->prev_absx);
|
||||
input_report_abs(input_dev, ABS_Y, ts_dev->prev_absy);
|
||||
input_report_key(input_dev, BTN_TOUCH, 1);
|
||||
input_sync(input_dev);
|
||||
} else
|
||||
ts_dev->bufferedmeasure = 1;
|
||||
|
||||
/* Now make new measurement */
|
||||
ts_dev->prev_absx = atmel_tsadcc_read(ATMEL_TSADCC_CDR3) << 10;
|
||||
ts_dev->prev_absx /= atmel_tsadcc_read(ATMEL_TSADCC_CDR2);
|
||||
|
||||
ts_dev->prev_absy = atmel_tsadcc_read(ATMEL_TSADCC_CDR1) << 10;
|
||||
ts_dev->prev_absy /= atmel_tsadcc_read(ATMEL_TSADCC_CDR0);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* The functions for inserting/removing us as a module.
|
||||
*/
|
||||
|
||||
static int atmel_tsadcc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct atmel_tsadcc *ts_dev;
|
||||
struct input_dev *input_dev;
|
||||
struct resource *res;
|
||||
struct at91_tsadcc_data *pdata = dev_get_platdata(&pdev->dev);
|
||||
int err;
|
||||
unsigned int prsc;
|
||||
unsigned int reg;
|
||||
|
||||
if (!pdata)
|
||||
return -EINVAL;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(&pdev->dev, "no mmio resource defined.\n");
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
/* Allocate memory for device */
|
||||
ts_dev = kzalloc(sizeof(struct atmel_tsadcc), GFP_KERNEL);
|
||||
if (!ts_dev) {
|
||||
dev_err(&pdev->dev, "failed to allocate memory.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
platform_set_drvdata(pdev, ts_dev);
|
||||
|
||||
input_dev = input_allocate_device();
|
||||
if (!input_dev) {
|
||||
dev_err(&pdev->dev, "failed to allocate input device.\n");
|
||||
err = -EBUSY;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
ts_dev->irq = platform_get_irq(pdev, 0);
|
||||
if (ts_dev->irq < 0) {
|
||||
dev_err(&pdev->dev, "no irq ID is designated.\n");
|
||||
err = -ENODEV;
|
||||
goto err_free_dev;
|
||||
}
|
||||
|
||||
if (!request_mem_region(res->start, resource_size(res),
|
||||
"atmel tsadcc regs")) {
|
||||
dev_err(&pdev->dev, "resources is unavailable.\n");
|
||||
err = -EBUSY;
|
||||
goto err_free_dev;
|
||||
}
|
||||
|
||||
tsc_base = ioremap(res->start, resource_size(res));
|
||||
if (!tsc_base) {
|
||||
dev_err(&pdev->dev, "failed to map registers.\n");
|
||||
err = -ENOMEM;
|
||||
goto err_release_mem;
|
||||
}
|
||||
|
||||
err = request_irq(ts_dev->irq, atmel_tsadcc_interrupt, 0,
|
||||
pdev->dev.driver->name, ts_dev);
|
||||
if (err) {
|
||||
dev_err(&pdev->dev, "failed to allocate irq.\n");
|
||||
goto err_unmap_regs;
|
||||
}
|
||||
|
||||
ts_dev->clk = clk_get(&pdev->dev, "tsc_clk");
|
||||
if (IS_ERR(ts_dev->clk)) {
|
||||
dev_err(&pdev->dev, "failed to get ts_clk\n");
|
||||
err = PTR_ERR(ts_dev->clk);
|
||||
goto err_free_irq;
|
||||
}
|
||||
|
||||
ts_dev->input = input_dev;
|
||||
ts_dev->bufferedmeasure = 0;
|
||||
|
||||
snprintf(ts_dev->phys, sizeof(ts_dev->phys),
|
||||
"%s/input0", dev_name(&pdev->dev));
|
||||
|
||||
input_dev->name = "atmel touch screen controller";
|
||||
input_dev->phys = ts_dev->phys;
|
||||
input_dev->dev.parent = &pdev->dev;
|
||||
|
||||
__set_bit(EV_ABS, input_dev->evbit);
|
||||
input_set_abs_params(input_dev, ABS_X, 0, 0x3FF, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y, 0, 0x3FF, 0, 0);
|
||||
|
||||
input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
|
||||
|
||||
/* clk_enable() always returns 0, no need to check it */
|
||||
clk_enable(ts_dev->clk);
|
||||
|
||||
prsc = clk_get_rate(ts_dev->clk);
|
||||
dev_info(&pdev->dev, "Master clock is set at: %d Hz\n", prsc);
|
||||
|
||||
if (!pdata->adc_clock)
|
||||
pdata->adc_clock = ADC_DEFAULT_CLOCK;
|
||||
|
||||
prsc = (prsc / (2 * pdata->adc_clock)) - 1;
|
||||
|
||||
/* saturate if this value is too high */
|
||||
if (cpu_is_at91sam9rl()) {
|
||||
if (prsc > PRESCALER_VAL(ATMEL_TSADCC_PRESCAL))
|
||||
prsc = PRESCALER_VAL(ATMEL_TSADCC_PRESCAL);
|
||||
} else {
|
||||
if (prsc > PRESCALER_VAL(ATMEL_TSADCC_EPRESCAL))
|
||||
prsc = PRESCALER_VAL(ATMEL_TSADCC_EPRESCAL);
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "Prescaler is set at: %d\n", prsc);
|
||||
|
||||
reg = ATMEL_TSADCC_TSAMOD_TS_ONLY_MODE |
|
||||
((0x00 << 5) & ATMEL_TSADCC_SLEEP) | /* Normal Mode */
|
||||
((0x01 << 6) & ATMEL_TSADCC_PENDET) | /* Enable Pen Detect */
|
||||
(prsc << 8) |
|
||||
((0x26 << 16) & ATMEL_TSADCC_STARTUP) |
|
||||
((pdata->pendet_debounce << 28) & ATMEL_TSADCC_PENDBC);
|
||||
|
||||
atmel_tsadcc_write(ATMEL_TSADCC_CR, ATMEL_TSADCC_SWRST);
|
||||
atmel_tsadcc_write(ATMEL_TSADCC_MR, reg);
|
||||
atmel_tsadcc_write(ATMEL_TSADCC_TRGR, ATMEL_TSADCC_TRGMOD_NONE);
|
||||
atmel_tsadcc_write(ATMEL_TSADCC_TSR,
|
||||
(pdata->ts_sample_hold_time << 24) & ATMEL_TSADCC_TSSHTIM);
|
||||
|
||||
atmel_tsadcc_read(ATMEL_TSADCC_SR);
|
||||
atmel_tsadcc_write(ATMEL_TSADCC_IER, ATMEL_TSADCC_PENCNT);
|
||||
|
||||
/* All went ok, so register to the input system */
|
||||
err = input_register_device(input_dev);
|
||||
if (err)
|
||||
goto err_fail;
|
||||
|
||||
return 0;
|
||||
|
||||
err_fail:
|
||||
clk_disable(ts_dev->clk);
|
||||
clk_put(ts_dev->clk);
|
||||
err_free_irq:
|
||||
free_irq(ts_dev->irq, ts_dev);
|
||||
err_unmap_regs:
|
||||
iounmap(tsc_base);
|
||||
err_release_mem:
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
err_free_dev:
|
||||
input_free_device(input_dev);
|
||||
err_free_mem:
|
||||
kfree(ts_dev);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int atmel_tsadcc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct atmel_tsadcc *ts_dev = platform_get_drvdata(pdev);
|
||||
struct resource *res;
|
||||
|
||||
free_irq(ts_dev->irq, ts_dev);
|
||||
|
||||
input_unregister_device(ts_dev->input);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
iounmap(tsc_base);
|
||||
release_mem_region(res->start, resource_size(res));
|
||||
|
||||
clk_disable(ts_dev->clk);
|
||||
clk_put(ts_dev->clk);
|
||||
|
||||
kfree(ts_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver atmel_tsadcc_driver = {
|
||||
.probe = atmel_tsadcc_probe,
|
||||
.remove = atmel_tsadcc_remove,
|
||||
.driver = {
|
||||
.name = "atmel_tsadcc",
|
||||
},
|
||||
};
|
||||
module_platform_driver(atmel_tsadcc_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("Atmel TouchScreen Driver");
|
||||
MODULE_AUTHOR("Dan Liang <dan.liang@atmel.com>");
|
||||
|
||||
|
|
@ -733,8 +733,7 @@ edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata,
|
|||
static void
|
||||
edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata)
|
||||
{
|
||||
if (tsdata->debug_dir)
|
||||
debugfs_remove_recursive(tsdata->debug_dir);
|
||||
debugfs_remove_recursive(tsdata->debug_dir);
|
||||
kfree(tsdata->raw_buffer);
|
||||
}
|
||||
|
||||
|
|
|
|||
166
drivers/input/touchscreen/ipaq-micro-ts.c
Normal file
166
drivers/input/touchscreen/ipaq-micro-ts.c
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
* h3600 atmel micro companion support, touchscreen subdevice
|
||||
* Author : Alessandro Gardich <gremlin@gremlin.it>
|
||||
* Author : Dmitry Artamonow <mad_soft@inbox.ru>
|
||||
* Author : Linus Walleij <linus.walleij@linaro.org>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mfd/ipaq-micro.h>
|
||||
|
||||
struct touchscreen_data {
|
||||
struct input_dev *input;
|
||||
struct ipaq_micro *micro;
|
||||
};
|
||||
|
||||
static void micro_ts_receive(void *data, int len, unsigned char *msg)
|
||||
{
|
||||
struct touchscreen_data *ts = data;
|
||||
|
||||
if (len == 4) {
|
||||
input_report_abs(ts->input, ABS_X,
|
||||
be16_to_cpup((__be16 *) &msg[2]));
|
||||
input_report_abs(ts->input, ABS_Y,
|
||||
be16_to_cpup((__be16 *) &msg[0]));
|
||||
input_report_key(ts->input, BTN_TOUCH, 1);
|
||||
input_sync(ts->input);
|
||||
} else if (len == 0) {
|
||||
input_report_abs(ts->input, ABS_X, 0);
|
||||
input_report_abs(ts->input, ABS_Y, 0);
|
||||
input_report_key(ts->input, BTN_TOUCH, 0);
|
||||
input_sync(ts->input);
|
||||
}
|
||||
}
|
||||
|
||||
static void micro_ts_toggle_receive(struct touchscreen_data *ts, bool enable)
|
||||
{
|
||||
struct ipaq_micro *micro = ts->micro;
|
||||
|
||||
spin_lock_irq(µ->lock);
|
||||
|
||||
if (enable) {
|
||||
micro->ts = micro_ts_receive;
|
||||
micro->ts_data = ts;
|
||||
} else {
|
||||
micro->ts = NULL;
|
||||
micro->ts_data = NULL;
|
||||
}
|
||||
|
||||
spin_unlock_irq(&ts->micro->lock);
|
||||
}
|
||||
|
||||
static int micro_ts_open(struct input_dev *input)
|
||||
{
|
||||
struct touchscreen_data *ts = input_get_drvdata(input);
|
||||
|
||||
micro_ts_toggle_receive(ts, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void micro_ts_close(struct input_dev *input)
|
||||
{
|
||||
struct touchscreen_data *ts = input_get_drvdata(input);
|
||||
|
||||
micro_ts_toggle_receive(ts, false);
|
||||
}
|
||||
|
||||
static int micro_ts_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ipaq_micro *micro = dev_get_drvdata(pdev->dev.parent);
|
||||
struct touchscreen_data *ts;
|
||||
int error;
|
||||
|
||||
ts = devm_kzalloc(&pdev->dev, sizeof(*ts), GFP_KERNEL);
|
||||
if (!ts)
|
||||
return -ENOMEM;
|
||||
|
||||
ts->micro = micro;
|
||||
|
||||
ts->input = devm_input_allocate_device(&pdev->dev);
|
||||
if (!ts->input) {
|
||||
dev_err(&pdev->dev, "failed to allocate input device\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ts->input->name = "ipaq micro ts";
|
||||
ts->input->open = micro_ts_open;
|
||||
ts->input->close = micro_ts_close;
|
||||
|
||||
input_set_drvdata(ts->input, ts);
|
||||
|
||||
input_set_capability(ts->input, EV_KEY, BTN_TOUCH);
|
||||
input_set_capability(ts->input, EV_ABS, ABS_X);
|
||||
input_set_capability(ts->input, EV_ABS, ABS_Y);
|
||||
input_set_abs_params(ts->input, ABS_X, 0, 1023, 0, 0);
|
||||
input_set_abs_params(ts->input, ABS_Y, 0, 1023, 0, 0);
|
||||
|
||||
error = input_register_device(ts->input);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev, "error registering touch input\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, ts);
|
||||
|
||||
dev_info(&pdev->dev, "iPAQ micro touchscreen\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int micro_ts_suspend(struct device *dev)
|
||||
{
|
||||
struct touchscreen_data *ts = dev_get_drvdata(dev);
|
||||
|
||||
micro_ts_toggle_receive(ts, false);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int micro_ts_resume(struct device *dev)
|
||||
{
|
||||
struct touchscreen_data *ts = dev_get_drvdata(dev);
|
||||
struct input_dev *input = ts->input;
|
||||
|
||||
mutex_lock(&input->mutex);
|
||||
|
||||
if (input->users)
|
||||
micro_ts_toggle_receive(ts, true);
|
||||
|
||||
mutex_unlock(&input->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops micro_ts_dev_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(micro_ts_suspend, micro_ts_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver micro_ts_device_driver = {
|
||||
.driver = {
|
||||
.name = "ipaq-micro-ts",
|
||||
.pm = µ_ts_dev_pm_ops,
|
||||
},
|
||||
.probe = micro_ts_probe,
|
||||
};
|
||||
module_platform_driver(micro_ts_device_driver);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("driver for iPAQ Atmel micro touchscreen");
|
||||
MODULE_ALIAS("platform:ipaq-micro-ts");
|
||||
|
|
@ -36,22 +36,21 @@ struct jornada_ts {
|
|||
|
||||
static void jornada720_ts_collect_data(struct jornada_ts *jornada_ts)
|
||||
{
|
||||
/* 3 low word X samples */
|
||||
jornada_ts->x_data[0] = jornada_ssp_byte(TXDUMMY);
|
||||
jornada_ts->x_data[1] = jornada_ssp_byte(TXDUMMY);
|
||||
jornada_ts->x_data[2] = jornada_ssp_byte(TXDUMMY);
|
||||
|
||||
/* 3 low word X samples */
|
||||
jornada_ts->x_data[0] = jornada_ssp_byte(TXDUMMY);
|
||||
jornada_ts->x_data[1] = jornada_ssp_byte(TXDUMMY);
|
||||
jornada_ts->x_data[2] = jornada_ssp_byte(TXDUMMY);
|
||||
/* 3 low word Y samples */
|
||||
jornada_ts->y_data[0] = jornada_ssp_byte(TXDUMMY);
|
||||
jornada_ts->y_data[1] = jornada_ssp_byte(TXDUMMY);
|
||||
jornada_ts->y_data[2] = jornada_ssp_byte(TXDUMMY);
|
||||
|
||||
/* 3 low word Y samples */
|
||||
jornada_ts->y_data[0] = jornada_ssp_byte(TXDUMMY);
|
||||
jornada_ts->y_data[1] = jornada_ssp_byte(TXDUMMY);
|
||||
jornada_ts->y_data[2] = jornada_ssp_byte(TXDUMMY);
|
||||
/* combined x samples bits */
|
||||
jornada_ts->x_data[3] = jornada_ssp_byte(TXDUMMY);
|
||||
|
||||
/* combined x samples bits */
|
||||
jornada_ts->x_data[3] = jornada_ssp_byte(TXDUMMY);
|
||||
|
||||
/* combined y samples bits */
|
||||
jornada_ts->y_data[3] = jornada_ssp_byte(TXDUMMY);
|
||||
/* combined y samples bits */
|
||||
jornada_ts->y_data[3] = jornada_ssp_byte(TXDUMMY);
|
||||
}
|
||||
|
||||
static int jornada720_ts_average(int coords[4])
|
||||
|
|
@ -104,13 +103,13 @@ static int jornada720_ts_probe(struct platform_device *pdev)
|
|||
struct input_dev *input_dev;
|
||||
int error;
|
||||
|
||||
jornada_ts = kzalloc(sizeof(struct jornada_ts), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
jornada_ts = devm_kzalloc(&pdev->dev, sizeof(*jornada_ts), GFP_KERNEL);
|
||||
if (!jornada_ts)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!jornada_ts || !input_dev) {
|
||||
error = -ENOMEM;
|
||||
goto fail1;
|
||||
}
|
||||
input_dev = devm_input_allocate_device(&pdev->dev);
|
||||
if (!input_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, jornada_ts);
|
||||
|
||||
|
|
@ -126,36 +125,18 @@ static int jornada720_ts_probe(struct platform_device *pdev)
|
|||
input_set_abs_params(input_dev, ABS_X, 270, 3900, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y, 180, 3700, 0, 0);
|
||||
|
||||
error = request_irq(IRQ_GPIO9,
|
||||
jornada720_ts_interrupt,
|
||||
IRQF_TRIGGER_RISING,
|
||||
"HP7XX Touchscreen driver", pdev);
|
||||
error = devm_request_irq(&pdev->dev, IRQ_GPIO9,
|
||||
jornada720_ts_interrupt,
|
||||
IRQF_TRIGGER_RISING,
|
||||
"HP7XX Touchscreen driver", pdev);
|
||||
if (error) {
|
||||
printk(KERN_INFO "HP7XX TS : Unable to acquire irq!\n");
|
||||
goto fail1;
|
||||
dev_err(&pdev->dev, "HP7XX TS : Unable to acquire irq!\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
error = input_register_device(jornada_ts->dev);
|
||||
if (error)
|
||||
goto fail2;
|
||||
|
||||
return 0;
|
||||
|
||||
fail2:
|
||||
free_irq(IRQ_GPIO9, pdev);
|
||||
fail1:
|
||||
input_free_device(input_dev);
|
||||
kfree(jornada_ts);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int jornada720_ts_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct jornada_ts *jornada_ts = platform_get_drvdata(pdev);
|
||||
|
||||
free_irq(IRQ_GPIO9, pdev);
|
||||
input_unregister_device(jornada_ts->dev);
|
||||
kfree(jornada_ts);
|
||||
return error;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -165,7 +146,6 @@ MODULE_ALIAS("platform:jornada_ts");
|
|||
|
||||
static struct platform_driver jornada720_ts_driver = {
|
||||
.probe = jornada720_ts_probe,
|
||||
.remove = jornada720_ts_remove,
|
||||
.driver = {
|
||||
.name = "jornada_ts",
|
||||
.owner = THIS_MODULE,
|
||||
|
|
|
|||
|
|
@ -248,8 +248,7 @@ static int mcs5000_ts_probe(struct i2c_client *client,
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int mcs5000_ts_suspend(struct device *dev)
|
||||
static int __maybe_unused mcs5000_ts_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
|
||||
|
|
@ -259,7 +258,7 @@ static int mcs5000_ts_suspend(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int mcs5000_ts_resume(struct device *dev)
|
||||
static int __maybe_unused mcs5000_ts_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct mcs5000_ts_data *data = i2c_get_clientdata(client);
|
||||
|
|
@ -269,7 +268,6 @@ static int mcs5000_ts_resume(struct device *dev)
|
|||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(mcs5000_ts_pm, mcs5000_ts_suspend, mcs5000_ts_resume);
|
||||
|
||||
|
|
|
|||
|
|
@ -23,22 +23,51 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/input/pixcir_ts.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#define PIXCIR_MAX_SLOTS 5 /* Max fingers supported by driver */
|
||||
|
||||
struct pixcir_i2c_ts_data {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input;
|
||||
const struct pixcir_ts_platform_data *chip;
|
||||
const struct pixcir_ts_platform_data *pdata;
|
||||
bool running;
|
||||
int max_fingers; /* Max fingers supported in this instance */
|
||||
};
|
||||
|
||||
static void pixcir_ts_poscheck(struct pixcir_i2c_ts_data *data)
|
||||
struct pixcir_touch {
|
||||
int x;
|
||||
int y;
|
||||
int id;
|
||||
};
|
||||
|
||||
struct pixcir_report_data {
|
||||
int num_touches;
|
||||
struct pixcir_touch touches[PIXCIR_MAX_SLOTS];
|
||||
};
|
||||
|
||||
static void pixcir_ts_parse(struct pixcir_i2c_ts_data *tsdata,
|
||||
struct pixcir_report_data *report)
|
||||
{
|
||||
struct pixcir_i2c_ts_data *tsdata = data;
|
||||
u8 rdbuf[10], wrbuf[1] = { 0 };
|
||||
u8 rdbuf[2 + PIXCIR_MAX_SLOTS * 5];
|
||||
u8 wrbuf[1] = { 0 };
|
||||
u8 *bufptr;
|
||||
u8 touch;
|
||||
int ret;
|
||||
int ret, i;
|
||||
int readsize;
|
||||
const struct pixcir_i2c_chip_data *chip = &tsdata->pdata->chip;
|
||||
|
||||
memset(report, 0, sizeof(struct pixcir_report_data));
|
||||
|
||||
i = chip->has_hw_ids ? 1 : 0;
|
||||
readsize = 2 + tsdata->max_fingers * (4 + i);
|
||||
if (readsize > sizeof(rdbuf))
|
||||
readsize = sizeof(rdbuf);
|
||||
|
||||
ret = i2c_master_send(tsdata->client, wrbuf, sizeof(wrbuf));
|
||||
if (ret != sizeof(wrbuf)) {
|
||||
|
|
@ -48,7 +77,7 @@ static void pixcir_ts_poscheck(struct pixcir_i2c_ts_data *data)
|
|||
return;
|
||||
}
|
||||
|
||||
ret = i2c_master_recv(tsdata->client, rdbuf, sizeof(rdbuf));
|
||||
ret = i2c_master_recv(tsdata->client, rdbuf, readsize);
|
||||
if (ret != sizeof(rdbuf)) {
|
||||
dev_err(&tsdata->client->dev,
|
||||
"%s: i2c_master_recv failed(), ret=%d\n",
|
||||
|
|
@ -56,45 +85,103 @@ static void pixcir_ts_poscheck(struct pixcir_i2c_ts_data *data)
|
|||
return;
|
||||
}
|
||||
|
||||
touch = rdbuf[0];
|
||||
if (touch) {
|
||||
u16 posx1 = (rdbuf[3] << 8) | rdbuf[2];
|
||||
u16 posy1 = (rdbuf[5] << 8) | rdbuf[4];
|
||||
u16 posx2 = (rdbuf[7] << 8) | rdbuf[6];
|
||||
u16 posy2 = (rdbuf[9] << 8) | rdbuf[8];
|
||||
touch = rdbuf[0] & 0x7;
|
||||
if (touch > tsdata->max_fingers)
|
||||
touch = tsdata->max_fingers;
|
||||
|
||||
input_report_key(tsdata->input, BTN_TOUCH, 1);
|
||||
input_report_abs(tsdata->input, ABS_X, posx1);
|
||||
input_report_abs(tsdata->input, ABS_Y, posy1);
|
||||
report->num_touches = touch;
|
||||
bufptr = &rdbuf[2];
|
||||
|
||||
input_report_abs(tsdata->input, ABS_MT_POSITION_X, posx1);
|
||||
input_report_abs(tsdata->input, ABS_MT_POSITION_Y, posy1);
|
||||
input_mt_sync(tsdata->input);
|
||||
for (i = 0; i < touch; i++) {
|
||||
report->touches[i].x = (bufptr[1] << 8) | bufptr[0];
|
||||
report->touches[i].y = (bufptr[3] << 8) | bufptr[2];
|
||||
|
||||
if (touch == 2) {
|
||||
input_report_abs(tsdata->input,
|
||||
ABS_MT_POSITION_X, posx2);
|
||||
input_report_abs(tsdata->input,
|
||||
ABS_MT_POSITION_Y, posy2);
|
||||
input_mt_sync(tsdata->input);
|
||||
if (chip->has_hw_ids) {
|
||||
report->touches[i].id = bufptr[4];
|
||||
bufptr = bufptr + 5;
|
||||
} else {
|
||||
bufptr = bufptr + 4;
|
||||
}
|
||||
} else {
|
||||
input_report_key(tsdata->input, BTN_TOUCH, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void pixcir_ts_report(struct pixcir_i2c_ts_data *ts,
|
||||
struct pixcir_report_data *report)
|
||||
{
|
||||
struct input_mt_pos pos[PIXCIR_MAX_SLOTS];
|
||||
int slots[PIXCIR_MAX_SLOTS];
|
||||
struct pixcir_touch *touch;
|
||||
int n, i, slot;
|
||||
struct device *dev = &ts->client->dev;
|
||||
const struct pixcir_i2c_chip_data *chip = &ts->pdata->chip;
|
||||
|
||||
n = report->num_touches;
|
||||
if (n > PIXCIR_MAX_SLOTS)
|
||||
n = PIXCIR_MAX_SLOTS;
|
||||
|
||||
if (!chip->has_hw_ids) {
|
||||
for (i = 0; i < n; i++) {
|
||||
touch = &report->touches[i];
|
||||
pos[i].x = touch->x;
|
||||
pos[i].y = touch->y;
|
||||
}
|
||||
|
||||
input_mt_assign_slots(ts->input, slots, pos, n);
|
||||
}
|
||||
|
||||
input_sync(tsdata->input);
|
||||
for (i = 0; i < n; i++) {
|
||||
touch = &report->touches[i];
|
||||
|
||||
if (chip->has_hw_ids) {
|
||||
slot = input_mt_get_slot_by_key(ts->input, touch->id);
|
||||
if (slot < 0) {
|
||||
dev_dbg(dev, "no free slot for id 0x%x\n",
|
||||
touch->id);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
slot = slots[i];
|
||||
}
|
||||
|
||||
input_mt_slot(ts->input, slot);
|
||||
input_mt_report_slot_state(ts->input,
|
||||
MT_TOOL_FINGER, true);
|
||||
|
||||
input_event(ts->input, EV_ABS, ABS_MT_POSITION_X, touch->x);
|
||||
input_event(ts->input, EV_ABS, ABS_MT_POSITION_Y, touch->y);
|
||||
|
||||
dev_dbg(dev, "%d: slot %d, x %d, y %d\n",
|
||||
i, slot, touch->x, touch->y);
|
||||
}
|
||||
|
||||
input_mt_sync_frame(ts->input);
|
||||
input_sync(ts->input);
|
||||
}
|
||||
|
||||
static irqreturn_t pixcir_ts_isr(int irq, void *dev_id)
|
||||
{
|
||||
struct pixcir_i2c_ts_data *tsdata = dev_id;
|
||||
const struct pixcir_ts_platform_data *pdata = tsdata->chip;
|
||||
const struct pixcir_ts_platform_data *pdata = tsdata->pdata;
|
||||
struct pixcir_report_data report;
|
||||
|
||||
while (tsdata->running) {
|
||||
pixcir_ts_poscheck(tsdata);
|
||||
/* parse packet */
|
||||
pixcir_ts_parse(tsdata, &report);
|
||||
|
||||
if (gpio_get_value(pdata->gpio_attb))
|
||||
/* report it */
|
||||
pixcir_ts_report(tsdata, &report);
|
||||
|
||||
if (gpio_get_value(pdata->gpio_attb)) {
|
||||
if (report.num_touches) {
|
||||
/*
|
||||
* Last report with no finger up?
|
||||
* Do it now then.
|
||||
*/
|
||||
input_mt_sync_frame(tsdata->input);
|
||||
input_sync(tsdata->input);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
msleep(20);
|
||||
}
|
||||
|
|
@ -323,16 +410,69 @@ unlock:
|
|||
static SIMPLE_DEV_PM_OPS(pixcir_dev_pm_ops,
|
||||
pixcir_i2c_ts_suspend, pixcir_i2c_ts_resume);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id pixcir_of_match[];
|
||||
|
||||
static struct pixcir_ts_platform_data *pixcir_parse_dt(struct device *dev)
|
||||
{
|
||||
struct pixcir_ts_platform_data *pdata;
|
||||
struct device_node *np = dev->of_node;
|
||||
const struct of_device_id *match;
|
||||
|
||||
match = of_match_device(of_match_ptr(pixcir_of_match), dev);
|
||||
if (!match)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
|
||||
if (!pdata)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
pdata->chip = *(const struct pixcir_i2c_chip_data *)match->data;
|
||||
|
||||
pdata->gpio_attb = of_get_named_gpio(np, "attb-gpio", 0);
|
||||
/* gpio_attb validity is checked in probe */
|
||||
|
||||
if (of_property_read_u32(np, "touchscreen-size-x", &pdata->x_max)) {
|
||||
dev_err(dev, "Failed to get touchscreen-size-x property\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
pdata->x_max -= 1;
|
||||
|
||||
if (of_property_read_u32(np, "touchscreen-size-y", &pdata->y_max)) {
|
||||
dev_err(dev, "Failed to get touchscreen-size-y property\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
pdata->y_max -= 1;
|
||||
|
||||
dev_dbg(dev, "%s: x %d, y %d, gpio %d\n", __func__,
|
||||
pdata->x_max + 1, pdata->y_max + 1, pdata->gpio_attb);
|
||||
|
||||
return pdata;
|
||||
}
|
||||
#else
|
||||
static struct pixcir_ts_platform_data *pixcir_parse_dt(struct device *dev)
|
||||
{
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int pixcir_i2c_ts_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
const struct pixcir_ts_platform_data *pdata =
|
||||
dev_get_platdata(&client->dev);
|
||||
struct device *dev = &client->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct pixcir_i2c_ts_data *tsdata;
|
||||
struct input_dev *input;
|
||||
int error;
|
||||
|
||||
if (np && !pdata) {
|
||||
pdata = pixcir_parse_dt(dev);
|
||||
if (IS_ERR(pdata))
|
||||
return PTR_ERR(pdata);
|
||||
}
|
||||
|
||||
if (!pdata) {
|
||||
dev_err(&client->dev, "platform data not defined\n");
|
||||
return -EINVAL;
|
||||
|
|
@ -343,6 +483,11 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!pdata->chip.max_fingers) {
|
||||
dev_err(dev, "Invalid max_fingers in pdata\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
tsdata = devm_kzalloc(dev, sizeof(*tsdata), GFP_KERNEL);
|
||||
if (!tsdata)
|
||||
return -ENOMEM;
|
||||
|
|
@ -355,7 +500,7 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client,
|
|||
|
||||
tsdata->client = client;
|
||||
tsdata->input = input;
|
||||
tsdata->chip = pdata;
|
||||
tsdata->pdata = pdata;
|
||||
|
||||
input->name = client->name;
|
||||
input->id.bustype = BUS_I2C;
|
||||
|
|
@ -371,6 +516,20 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client,
|
|||
input_set_abs_params(input, ABS_MT_POSITION_X, 0, pdata->x_max, 0, 0);
|
||||
input_set_abs_params(input, ABS_MT_POSITION_Y, 0, pdata->y_max, 0, 0);
|
||||
|
||||
tsdata->max_fingers = tsdata->pdata->chip.max_fingers;
|
||||
if (tsdata->max_fingers > PIXCIR_MAX_SLOTS) {
|
||||
tsdata->max_fingers = PIXCIR_MAX_SLOTS;
|
||||
dev_info(dev, "Limiting maximum fingers to %d\n",
|
||||
tsdata->max_fingers);
|
||||
}
|
||||
|
||||
error = input_mt_init_slots(input, tsdata->max_fingers,
|
||||
INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
|
||||
if (error) {
|
||||
dev_err(dev, "Error initializing Multi-Touch slots\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
input_set_drvdata(input, tsdata);
|
||||
|
||||
error = devm_gpio_request_one(dev, pdata->gpio_attb,
|
||||
|
|
@ -419,15 +578,36 @@ static int pixcir_i2c_ts_remove(struct i2c_client *client)
|
|||
|
||||
static const struct i2c_device_id pixcir_i2c_ts_id[] = {
|
||||
{ "pixcir_ts", 0 },
|
||||
{ "pixcir_tangoc", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, pixcir_i2c_ts_id);
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct pixcir_i2c_chip_data pixcir_ts_data = {
|
||||
.max_fingers = 2,
|
||||
/* no hw id support */
|
||||
};
|
||||
|
||||
static const struct pixcir_i2c_chip_data pixcir_tangoc_data = {
|
||||
.max_fingers = 5,
|
||||
.has_hw_ids = true,
|
||||
};
|
||||
|
||||
static const struct of_device_id pixcir_of_match[] = {
|
||||
{ .compatible = "pixcir,pixcir_ts", .data = &pixcir_ts_data },
|
||||
{ .compatible = "pixcir,pixcir_tangoc", .data = &pixcir_tangoc_data },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, pixcir_of_match);
|
||||
#endif
|
||||
|
||||
static struct i2c_driver pixcir_i2c_ts_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "pixcir_ts",
|
||||
.pm = &pixcir_dev_pm_ops,
|
||||
.of_match_table = of_match_ptr(pixcir_of_match),
|
||||
},
|
||||
.probe = pixcir_i2c_ts_probe,
|
||||
.remove = pixcir_i2c_ts_remove,
|
||||
|
|
|
|||
|
|
@ -264,7 +264,7 @@ static int s3c2410ts_probe(struct platform_device *pdev)
|
|||
return -ENOENT;
|
||||
}
|
||||
|
||||
clk_enable(ts.clock);
|
||||
clk_prepare_enable(ts.clock);
|
||||
dev_dbg(dev, "got and enabled clocks\n");
|
||||
|
||||
ts.irq_tc = ret = platform_get_irq(pdev, 0);
|
||||
|
|
@ -369,7 +369,7 @@ static int s3c2410ts_remove(struct platform_device *pdev)
|
|||
free_irq(ts.irq_tc, ts.input);
|
||||
del_timer_sync(&touch_timer);
|
||||
|
||||
clk_disable(ts.clock);
|
||||
clk_disable_unprepare(ts.clock);
|
||||
clk_put(ts.clock);
|
||||
|
||||
input_unregister_device(ts.input);
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@
|
|||
#include <linux/sysfs.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/platform_data/zforce_ts.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
|
|
@ -117,6 +119,8 @@ struct zforce_ts {
|
|||
const struct zforce_ts_platdata *pdata;
|
||||
char phys[32];
|
||||
|
||||
struct regulator *reg_vdd;
|
||||
|
||||
bool suspending;
|
||||
bool suspended;
|
||||
bool boot_complete;
|
||||
|
|
@ -690,6 +694,11 @@ static void zforce_reset(void *data)
|
|||
struct zforce_ts *ts = data;
|
||||
|
||||
gpio_set_value(ts->pdata->gpio_rst, 0);
|
||||
|
||||
udelay(10);
|
||||
|
||||
if (!IS_ERR(ts->reg_vdd))
|
||||
regulator_disable(ts->reg_vdd);
|
||||
}
|
||||
|
||||
static struct zforce_ts_platdata *zforce_parse_dt(struct device *dev)
|
||||
|
|
@ -765,10 +774,32 @@ static int zforce_probe(struct i2c_client *client,
|
|||
return ret;
|
||||
}
|
||||
|
||||
ts->reg_vdd = devm_regulator_get_optional(&client->dev, "vdd");
|
||||
if (IS_ERR(ts->reg_vdd)) {
|
||||
ret = PTR_ERR(ts->reg_vdd);
|
||||
if (ret == -EPROBE_DEFER)
|
||||
return ret;
|
||||
} else {
|
||||
ret = regulator_enable(ts->reg_vdd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* according to datasheet add 100us grace time after regular
|
||||
* regulator enable delay.
|
||||
*/
|
||||
udelay(100);
|
||||
}
|
||||
|
||||
ret = devm_add_action(&client->dev, zforce_reset, ts);
|
||||
if (ret) {
|
||||
dev_err(&client->dev, "failed to register reset action, %d\n",
|
||||
ret);
|
||||
|
||||
/* hereafter the regulator will be disabled by the action */
|
||||
if (!IS_ERR(ts->reg_vdd))
|
||||
regulator_disable(ts->reg_vdd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue