forked from Minki/linux
Merge branch 'next' into for-linus
This commit is contained in:
commit
01c728a246
@ -49,7 +49,9 @@ This information is subject to change.
|
||||
#include <linux/input.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
unsigned long features[1 + FF_MAX/sizeof(unsigned long)];
|
||||
#define BITS_TO_LONGS(x) \
|
||||
(((x) + 8 * sizeof (unsigned long) - 1) / (8 * sizeof (unsigned long)))
|
||||
unsigned long features[BITS_TO_LONGS(FF_CNT)];
|
||||
int ioctl(int file_descriptor, int request, unsigned long *features);
|
||||
|
||||
"request" must be EVIOCGBIT(EV_FF, size of features array in bytes )
|
||||
|
@ -884,6 +884,7 @@ and is between 256 and 4096 characters. It is defined in the file
|
||||
controller
|
||||
i8042.nopnp [HW] Don't use ACPIPnP / PnPBIOS to discover KBD/AUX
|
||||
controllers
|
||||
i8042.notimeout [HW] Ignore timeout condition signalled by conroller
|
||||
i8042.reset [HW] Reset the controller during init and cleanup
|
||||
i8042.unlock [HW] Unlock (ignore) the keylock
|
||||
|
||||
|
@ -255,6 +255,16 @@ config JOYSTICK_AMIGA
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called amijoy.
|
||||
|
||||
config JOYSTICK_AS5011
|
||||
tristate "Austria Microsystem AS5011 joystick"
|
||||
depends on I2C
|
||||
help
|
||||
Say Y here if you have an AS5011 digital joystick connected to your
|
||||
system.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called as5011.
|
||||
|
||||
config JOYSTICK_JOYDUMP
|
||||
tristate "Gameport data dumper"
|
||||
select GAMEPORT
|
||||
|
@ -7,6 +7,7 @@
|
||||
obj-$(CONFIG_JOYSTICK_A3D) += a3d.o
|
||||
obj-$(CONFIG_JOYSTICK_ADI) += adi.o
|
||||
obj-$(CONFIG_JOYSTICK_AMIGA) += amijoy.o
|
||||
obj-$(CONFIG_JOYSTICK_AS5011) += as5011.o
|
||||
obj-$(CONFIG_JOYSTICK_ANALOG) += analog.o
|
||||
obj-$(CONFIG_JOYSTICK_COBRA) += cobra.o
|
||||
obj-$(CONFIG_JOYSTICK_DB9) += db9.o
|
||||
|
367
drivers/input/joystick/as5011.c
Normal file
367
drivers/input/joystick/as5011.c
Normal file
@ -0,0 +1,367 @@
|
||||
/*
|
||||
* Copyright (c) 2010, 2011 Fabien Marteau <fabien.marteau@armadeus.com>
|
||||
* Sponsored by ARMadeus Systems
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* Driver for Austria Microsystems joysticks AS5011
|
||||
*
|
||||
* TODO:
|
||||
* - Power on the chip when open() and power down when close()
|
||||
* - Manage power mode
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/input/as5011.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define DRIVER_DESC "Driver for Austria Microsystems AS5011 joystick"
|
||||
#define MODULE_DEVICE_ALIAS "as5011"
|
||||
|
||||
MODULE_AUTHOR("Fabien Marteau <fabien.marteau@armadeus.com>");
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
/* registers */
|
||||
#define AS5011_CTRL1 0x76
|
||||
#define AS5011_CTRL2 0x75
|
||||
#define AS5011_XP 0x43
|
||||
#define AS5011_XN 0x44
|
||||
#define AS5011_YP 0x53
|
||||
#define AS5011_YN 0x54
|
||||
#define AS5011_X_REG 0x41
|
||||
#define AS5011_Y_REG 0x42
|
||||
#define AS5011_X_RES_INT 0x51
|
||||
#define AS5011_Y_RES_INT 0x52
|
||||
|
||||
/* CTRL1 bits */
|
||||
#define AS5011_CTRL1_LP_PULSED 0x80
|
||||
#define AS5011_CTRL1_LP_ACTIVE 0x40
|
||||
#define AS5011_CTRL1_LP_CONTINUE 0x20
|
||||
#define AS5011_CTRL1_INT_WUP_EN 0x10
|
||||
#define AS5011_CTRL1_INT_ACT_EN 0x08
|
||||
#define AS5011_CTRL1_EXT_CLK_EN 0x04
|
||||
#define AS5011_CTRL1_SOFT_RST 0x02
|
||||
#define AS5011_CTRL1_DATA_VALID 0x01
|
||||
|
||||
/* CTRL2 bits */
|
||||
#define AS5011_CTRL2_EXT_SAMPLE_EN 0x08
|
||||
#define AS5011_CTRL2_RC_BIAS_ON 0x04
|
||||
#define AS5011_CTRL2_INV_SPINNING 0x02
|
||||
|
||||
#define AS5011_MAX_AXIS 80
|
||||
#define AS5011_MIN_AXIS (-80)
|
||||
#define AS5011_FUZZ 8
|
||||
#define AS5011_FLAT 40
|
||||
|
||||
struct as5011_device {
|
||||
struct input_dev *input_dev;
|
||||
struct i2c_client *i2c_client;
|
||||
unsigned int button_gpio;
|
||||
unsigned int button_irq;
|
||||
unsigned int axis_irq;
|
||||
};
|
||||
|
||||
static int as5011_i2c_write(struct i2c_client *client,
|
||||
uint8_t aregaddr,
|
||||
uint8_t avalue)
|
||||
{
|
||||
uint8_t data[2] = { aregaddr, avalue };
|
||||
struct i2c_msg msg = {
|
||||
client->addr, I2C_M_IGNORE_NAK, 2, (uint8_t *)data
|
||||
};
|
||||
int error;
|
||||
|
||||
error = i2c_transfer(client->adapter, &msg, 1);
|
||||
return error < 0 ? error : 0;
|
||||
}
|
||||
|
||||
static int as5011_i2c_read(struct i2c_client *client,
|
||||
uint8_t aregaddr, signed char *value)
|
||||
{
|
||||
uint8_t data[2] = { aregaddr };
|
||||
struct i2c_msg msg_set[2] = {
|
||||
{ client->addr, I2C_M_REV_DIR_ADDR, 1, (uint8_t *)data },
|
||||
{ client->addr, I2C_M_RD | I2C_M_NOSTART, 1, (uint8_t *)data }
|
||||
};
|
||||
int error;
|
||||
|
||||
error = i2c_transfer(client->adapter, msg_set, 2);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
*value = data[0] & 0x80 ? -1 * (1 + ~data[0]) : data[0];
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t as5011_button_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct as5011_device *as5011 = dev_id;
|
||||
int val = gpio_get_value_cansleep(as5011->button_gpio);
|
||||
|
||||
input_report_key(as5011->input_dev, BTN_JOYSTICK, !val);
|
||||
input_sync(as5011->input_dev);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t as5011_axis_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct as5011_device *as5011 = dev_id;
|
||||
int error;
|
||||
signed char x, y;
|
||||
|
||||
error = as5011_i2c_read(as5011->i2c_client, AS5011_X_RES_INT, &x);
|
||||
if (error < 0)
|
||||
goto out;
|
||||
|
||||
error = as5011_i2c_read(as5011->i2c_client, AS5011_Y_RES_INT, &y);
|
||||
if (error < 0)
|
||||
goto out;
|
||||
|
||||
input_report_abs(as5011->input_dev, ABS_X, x);
|
||||
input_report_abs(as5011->input_dev, ABS_Y, y);
|
||||
input_sync(as5011->input_dev);
|
||||
|
||||
out:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int __devinit as5011_configure_chip(struct as5011_device *as5011,
|
||||
const struct as5011_platform_data *plat_dat)
|
||||
{
|
||||
struct i2c_client *client = as5011->i2c_client;
|
||||
int error;
|
||||
signed char value;
|
||||
|
||||
/* chip soft reset */
|
||||
error = as5011_i2c_write(client, AS5011_CTRL1,
|
||||
AS5011_CTRL1_SOFT_RST);
|
||||
if (error < 0) {
|
||||
dev_err(&client->dev, "Soft reset failed\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
mdelay(10);
|
||||
|
||||
error = as5011_i2c_write(client, AS5011_CTRL1,
|
||||
AS5011_CTRL1_LP_PULSED |
|
||||
AS5011_CTRL1_LP_ACTIVE |
|
||||
AS5011_CTRL1_INT_ACT_EN);
|
||||
if (error < 0) {
|
||||
dev_err(&client->dev, "Power config failed\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
error = as5011_i2c_write(client, AS5011_CTRL2,
|
||||
AS5011_CTRL2_INV_SPINNING);
|
||||
if (error < 0) {
|
||||
dev_err(&client->dev, "Can't invert spinning\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
/* write threshold */
|
||||
error = as5011_i2c_write(client, AS5011_XP, plat_dat->xp);
|
||||
if (error < 0) {
|
||||
dev_err(&client->dev, "Can't write threshold\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
error = as5011_i2c_write(client, AS5011_XN, plat_dat->xn);
|
||||
if (error < 0) {
|
||||
dev_err(&client->dev, "Can't write threshold\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
error = as5011_i2c_write(client, AS5011_YP, plat_dat->yp);
|
||||
if (error < 0) {
|
||||
dev_err(&client->dev, "Can't write threshold\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
error = as5011_i2c_write(client, AS5011_YN, plat_dat->yn);
|
||||
if (error < 0) {
|
||||
dev_err(&client->dev, "Can't write threshold\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
/* to free irq gpio in chip */
|
||||
error = as5011_i2c_read(client, AS5011_X_RES_INT, &value);
|
||||
if (error < 0) {
|
||||
dev_err(&client->dev, "Can't read i2c X resolution value\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devinit as5011_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
const struct as5011_platform_data *plat_data;
|
||||
struct as5011_device *as5011;
|
||||
struct input_dev *input_dev;
|
||||
int irq;
|
||||
int error;
|
||||
|
||||
plat_data = client->dev.platform_data;
|
||||
if (!plat_data)
|
||||
return -EINVAL;
|
||||
|
||||
if (!plat_data->axis_irq) {
|
||||
dev_err(&client->dev, "No axis IRQ?\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_PROTOCOL_MANGLING)) {
|
||||
dev_err(&client->dev,
|
||||
"need i2c bus that supports protocol mangling\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
as5011 = kmalloc(sizeof(struct as5011_device), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!as5011 || !input_dev) {
|
||||
dev_err(&client->dev,
|
||||
"Can't allocate memory for device structure\n");
|
||||
error = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
as5011->i2c_client = client;
|
||||
as5011->input_dev = input_dev;
|
||||
as5011->button_gpio = plat_data->button_gpio;
|
||||
as5011->axis_irq = plat_data->axis_irq;
|
||||
|
||||
input_dev->name = "Austria Microsystem as5011 joystick";
|
||||
input_dev->id.bustype = BUS_I2C;
|
||||
input_dev->dev.parent = &client->dev;
|
||||
|
||||
__set_bit(EV_KEY, input_dev->evbit);
|
||||
__set_bit(EV_ABS, input_dev->evbit);
|
||||
__set_bit(BTN_JOYSTICK, input_dev->keybit);
|
||||
|
||||
input_set_abs_params(input_dev, ABS_X,
|
||||
AS5011_MIN_AXIS, AS5011_MAX_AXIS, AS5011_FUZZ, AS5011_FLAT);
|
||||
input_set_abs_params(as5011->input_dev, ABS_Y,
|
||||
AS5011_MIN_AXIS, AS5011_MAX_AXIS, AS5011_FUZZ, AS5011_FLAT);
|
||||
|
||||
error = gpio_request(as5011->button_gpio, "AS5011 button");
|
||||
if (error < 0) {
|
||||
dev_err(&client->dev, "Failed to request button gpio\n");
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
irq = gpio_to_irq(as5011->button_gpio);
|
||||
if (irq < 0) {
|
||||
dev_err(&client->dev,
|
||||
"Failed to get irq number for button gpio\n");
|
||||
goto err_free_button_gpio;
|
||||
}
|
||||
|
||||
as5011->button_irq = irq;
|
||||
|
||||
error = request_threaded_irq(as5011->button_irq,
|
||||
NULL, as5011_button_interrupt,
|
||||
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
|
||||
"as5011_button", as5011);
|
||||
if (error < 0) {
|
||||
dev_err(&client->dev,
|
||||
"Can't allocate button irq %d\n", as5011->button_irq);
|
||||
goto err_free_button_gpio;
|
||||
}
|
||||
|
||||
error = as5011_configure_chip(as5011, plat_data);
|
||||
if (error)
|
||||
goto err_free_button_irq;
|
||||
|
||||
error = request_threaded_irq(as5011->axis_irq, NULL,
|
||||
as5011_axis_interrupt,
|
||||
plat_data->axis_irqflags,
|
||||
"as5011_joystick", as5011);
|
||||
if (error) {
|
||||
dev_err(&client->dev,
|
||||
"Can't allocate axis irq %d\n", plat_data->axis_irq);
|
||||
goto err_free_button_irq;
|
||||
}
|
||||
|
||||
error = input_register_device(as5011->input_dev);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "Failed to register input device\n");
|
||||
goto err_free_axis_irq;
|
||||
}
|
||||
|
||||
i2c_set_clientdata(client, as5011);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free_axis_irq:
|
||||
free_irq(as5011->axis_irq, as5011);
|
||||
err_free_button_irq:
|
||||
free_irq(as5011->button_irq, as5011);
|
||||
err_free_button_gpio:
|
||||
gpio_free(as5011->button_gpio);
|
||||
err_free_mem:
|
||||
input_free_device(input_dev);
|
||||
kfree(as5011);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int __devexit as5011_remove(struct i2c_client *client)
|
||||
{
|
||||
struct as5011_device *as5011 = i2c_get_clientdata(client);
|
||||
|
||||
free_irq(as5011->axis_irq, as5011);
|
||||
free_irq(as5011->button_irq, as5011);
|
||||
gpio_free(as5011->button_gpio);
|
||||
|
||||
input_unregister_device(as5011->input_dev);
|
||||
kfree(as5011);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id as5011_id[] = {
|
||||
{ MODULE_DEVICE_ALIAS, 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, as5011_id);
|
||||
|
||||
static struct i2c_driver as5011_driver = {
|
||||
.driver = {
|
||||
.name = "as5011",
|
||||
},
|
||||
.probe = as5011_probe,
|
||||
.remove = __devexit_p(as5011_remove),
|
||||
.id_table = as5011_id,
|
||||
};
|
||||
|
||||
static int __init as5011_init(void)
|
||||
{
|
||||
return i2c_add_driver(&as5011_driver);
|
||||
}
|
||||
module_init(as5011_init);
|
||||
|
||||
static void __exit as5011_exit(void)
|
||||
{
|
||||
i2c_del_driver(&as5011_driver);
|
||||
}
|
||||
module_exit(as5011_exit);
|
@ -12,18 +12,6 @@ menuconfig INPUT_KEYBOARD
|
||||
|
||||
if INPUT_KEYBOARD
|
||||
|
||||
config KEYBOARD_AAED2000
|
||||
tristate "AAED-2000 keyboard"
|
||||
depends on MACH_AAED2000
|
||||
select INPUT_POLLDEV
|
||||
default y
|
||||
help
|
||||
Say Y here to enable the keyboard on the Agilent AAED-2000
|
||||
development board.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called aaed2000_kbd.
|
||||
|
||||
config KEYBOARD_ADP5520
|
||||
tristate "Keypad Support for ADP5520 PMIC"
|
||||
depends on PMIC_ADP5520
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
# Each configuration option enables a list of files.
|
||||
|
||||
obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o
|
||||
obj-$(CONFIG_KEYBOARD_ADP5520) += adp5520-keys.o
|
||||
obj-$(CONFIG_KEYBOARD_ADP5588) += adp5588-keys.o
|
||||
obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o
|
||||
|
@ -1,186 +0,0 @@
|
||||
/*
|
||||
* Keyboard driver for the AAED-2000 dev board
|
||||
*
|
||||
* Copyright (c) 2006 Nicolas Bellido Y Ortega
|
||||
*
|
||||
* Based on corgikbd.c
|
||||
*
|
||||
* 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/delay.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/input-polldev.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <mach/hardware.h>
|
||||
#include <mach/aaed2000.h>
|
||||
|
||||
#define KB_ROWS 12
|
||||
#define KB_COLS 8
|
||||
#define KB_ROWMASK(r) (1 << (r))
|
||||
#define SCANCODE(r,c) (((c) * KB_ROWS) + (r))
|
||||
#define NR_SCANCODES (KB_COLS * KB_ROWS)
|
||||
|
||||
#define SCAN_INTERVAL (50) /* ms */
|
||||
#define KB_ACTIVATE_DELAY (20) /* us */
|
||||
|
||||
static unsigned char aaedkbd_keycode[NR_SCANCODES] = {
|
||||
KEY_9, KEY_0, KEY_MINUS, KEY_EQUAL, KEY_BACKSPACE, 0, KEY_SPACE, KEY_KP6, 0, KEY_KPDOT, 0, 0,
|
||||
KEY_K, KEY_M, KEY_O, KEY_DOT, KEY_SLASH, 0, KEY_F, 0, 0, 0, KEY_LEFTSHIFT, 0,
|
||||
KEY_I, KEY_P, KEY_LEFTBRACE, KEY_RIGHTBRACE, KEY_BACKSLASH, 0, 0, 0, 0, 0, KEY_RIGHTSHIFT, 0,
|
||||
KEY_8, KEY_L, KEY_SEMICOLON, KEY_APOSTROPHE, KEY_ENTER, 0, 0, 0, 0, 0, 0, 0,
|
||||
KEY_J, KEY_H, KEY_B, KEY_KP8, KEY_KP4, 0, KEY_C, KEY_D, KEY_S, KEY_A, 0, KEY_CAPSLOCK,
|
||||
KEY_Y, KEY_U, KEY_N, KEY_T, 0, 0, KEY_R, KEY_E, KEY_W, KEY_Q, 0, KEY_TAB,
|
||||
KEY_7, KEY_6, KEY_G, 0, KEY_5, 0, KEY_4, KEY_3, KEY_2, KEY_1, 0, KEY_GRAVE,
|
||||
0, 0, KEY_COMMA, 0, KEY_KP2, 0, KEY_V, KEY_LEFTALT, KEY_X, KEY_Z, 0, KEY_LEFTCTRL
|
||||
};
|
||||
|
||||
struct aaedkbd {
|
||||
unsigned char keycode[ARRAY_SIZE(aaedkbd_keycode)];
|
||||
struct input_polled_dev *poll_dev;
|
||||
int kbdscan_state[KB_COLS];
|
||||
int kbdscan_count[KB_COLS];
|
||||
};
|
||||
|
||||
#define KBDSCAN_STABLE_COUNT 2
|
||||
|
||||
static void aaedkbd_report_col(struct aaedkbd *aaedkbd,
|
||||
unsigned int col, unsigned int rowd)
|
||||
{
|
||||
unsigned int scancode, pressed;
|
||||
unsigned int row;
|
||||
|
||||
for (row = 0; row < KB_ROWS; row++) {
|
||||
scancode = SCANCODE(row, col);
|
||||
pressed = rowd & KB_ROWMASK(row);
|
||||
|
||||
input_report_key(aaedkbd->poll_dev->input,
|
||||
aaedkbd->keycode[scancode], pressed);
|
||||
}
|
||||
}
|
||||
|
||||
/* Scan the hardware keyboard and push any changes up through the input layer */
|
||||
static void aaedkbd_poll(struct input_polled_dev *dev)
|
||||
{
|
||||
struct aaedkbd *aaedkbd = dev->private;
|
||||
unsigned int col, rowd;
|
||||
|
||||
col = 0;
|
||||
do {
|
||||
AAEC_GPIO_KSCAN = col + 8;
|
||||
udelay(KB_ACTIVATE_DELAY);
|
||||
rowd = AAED_EXT_GPIO & AAED_EGPIO_KBD_SCAN;
|
||||
|
||||
if (rowd != aaedkbd->kbdscan_state[col]) {
|
||||
aaedkbd->kbdscan_count[col] = 0;
|
||||
aaedkbd->kbdscan_state[col] = rowd;
|
||||
} else if (++aaedkbd->kbdscan_count[col] >= KBDSCAN_STABLE_COUNT) {
|
||||
aaedkbd_report_col(aaedkbd, col, rowd);
|
||||
col++;
|
||||
}
|
||||
} while (col < KB_COLS);
|
||||
|
||||
AAEC_GPIO_KSCAN = 0x07;
|
||||
input_sync(dev->input);
|
||||
}
|
||||
|
||||
static int __devinit aaedkbd_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct aaedkbd *aaedkbd;
|
||||
struct input_polled_dev *poll_dev;
|
||||
struct input_dev *input_dev;
|
||||
int i;
|
||||
int error;
|
||||
|
||||
aaedkbd = kzalloc(sizeof(struct aaedkbd), GFP_KERNEL);
|
||||
poll_dev = input_allocate_polled_device();
|
||||
if (!aaedkbd || !poll_dev) {
|
||||
error = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, aaedkbd);
|
||||
|
||||
aaedkbd->poll_dev = poll_dev;
|
||||
memcpy(aaedkbd->keycode, aaedkbd_keycode, sizeof(aaedkbd->keycode));
|
||||
|
||||
poll_dev->private = aaedkbd;
|
||||
poll_dev->poll = aaedkbd_poll;
|
||||
poll_dev->poll_interval = SCAN_INTERVAL;
|
||||
|
||||
input_dev = poll_dev->input;
|
||||
input_dev->name = "AAED-2000 Keyboard";
|
||||
input_dev->phys = "aaedkbd/input0";
|
||||
input_dev->id.bustype = BUS_HOST;
|
||||
input_dev->id.vendor = 0x0001;
|
||||
input_dev->id.product = 0x0001;
|
||||
input_dev->id.version = 0x0100;
|
||||
input_dev->dev.parent = &pdev->dev;
|
||||
|
||||
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
|
||||
input_dev->keycode = aaedkbd->keycode;
|
||||
input_dev->keycodesize = sizeof(unsigned char);
|
||||
input_dev->keycodemax = ARRAY_SIZE(aaedkbd_keycode);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(aaedkbd_keycode); i++)
|
||||
set_bit(aaedkbd->keycode[i], input_dev->keybit);
|
||||
clear_bit(0, input_dev->keybit);
|
||||
|
||||
error = input_register_polled_device(aaedkbd->poll_dev);
|
||||
if (error)
|
||||
goto fail;
|
||||
|
||||
return 0;
|
||||
|
||||
fail: kfree(aaedkbd);
|
||||
input_free_polled_device(poll_dev);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int __devexit aaedkbd_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct aaedkbd *aaedkbd = platform_get_drvdata(pdev);
|
||||
|
||||
input_unregister_polled_device(aaedkbd->poll_dev);
|
||||
input_free_polled_device(aaedkbd->poll_dev);
|
||||
kfree(aaedkbd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* work with hotplug and coldplug */
|
||||
MODULE_ALIAS("platform:aaed2000-keyboard");
|
||||
|
||||
static struct platform_driver aaedkbd_driver = {
|
||||
.probe = aaedkbd_probe,
|
||||
.remove = __devexit_p(aaedkbd_remove),
|
||||
.driver = {
|
||||
.name = "aaed2000-keyboard",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static int __init aaedkbd_init(void)
|
||||
{
|
||||
return platform_driver_register(&aaedkbd_driver);
|
||||
}
|
||||
|
||||
static void __exit aaedkbd_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&aaedkbd_driver);
|
||||
}
|
||||
|
||||
module_init(aaedkbd_init);
|
||||
module_exit(aaedkbd_exit);
|
||||
|
||||
MODULE_AUTHOR("Nicolas Bellido Y Ortega");
|
||||
MODULE_DESCRIPTION("AAED-2000 Keyboard Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -424,6 +424,13 @@ static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = {
|
||||
DMI_MATCH(DMI_PRODUCT_VERSION, "0100"),
|
||||
},
|
||||
},
|
||||
{
|
||||
/* Dell Vostro V13 */
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V13"),
|
||||
},
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
@ -545,6 +552,17 @@ static const struct dmi_system_id __initconst i8042_dmi_laptop_table[] = {
|
||||
};
|
||||
#endif
|
||||
|
||||
static const struct dmi_system_id __initconst i8042_dmi_notimeout_table[] = {
|
||||
{
|
||||
/* Dell Vostro V13 */
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V13"),
|
||||
},
|
||||
},
|
||||
{ }
|
||||
};
|
||||
|
||||
/*
|
||||
* Some Wistron based laptops need us to explicitly enable the 'Dritek
|
||||
* keyboard extension' to make their extra keys start generating scancodes.
|
||||
@ -896,6 +914,9 @@ static int __init i8042_platform_init(void)
|
||||
if (dmi_check_system(i8042_dmi_nomux_table))
|
||||
i8042_nomux = true;
|
||||
|
||||
if (dmi_check_system(i8042_dmi_notimeout_table))
|
||||
i8042_notimeout = true;
|
||||
|
||||
if (dmi_check_system(i8042_dmi_dritek_table))
|
||||
i8042_dritek = true;
|
||||
#endif /* CONFIG_X86 */
|
||||
|
@ -63,6 +63,10 @@ static bool i8042_noloop;
|
||||
module_param_named(noloop, i8042_noloop, bool, 0);
|
||||
MODULE_PARM_DESC(noloop, "Disable the AUX Loopback command while probing for the AUX port");
|
||||
|
||||
static bool i8042_notimeout;
|
||||
module_param_named(notimeout, i8042_notimeout, bool, 0);
|
||||
MODULE_PARM_DESC(notimeout, "Ignore timeouts signalled by i8042");
|
||||
|
||||
#ifdef CONFIG_X86
|
||||
static bool i8042_dritek;
|
||||
module_param_named(dritek, i8042_dritek, bool, 0);
|
||||
@ -504,7 +508,7 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id)
|
||||
} else {
|
||||
|
||||
dfl = ((str & I8042_STR_PARITY) ? SERIO_PARITY : 0) |
|
||||
((str & I8042_STR_TIMEOUT) ? SERIO_TIMEOUT : 0);
|
||||
((str & I8042_STR_TIMEOUT && !i8042_notimeout) ? SERIO_TIMEOUT : 0);
|
||||
|
||||
port_no = (str & I8042_STR_AUXDATA) ?
|
||||
I8042_AUX_PORT_NO : I8042_KBD_PORT_NO;
|
||||
|
@ -10,14 +10,16 @@
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/pm.h>
|
||||
|
||||
#include "ad7879.h"
|
||||
|
||||
#define AD7879_DEVID 0x79 /* AD7879-1/AD7889-1 */
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int ad7879_i2c_suspend(struct i2c_client *client, pm_message_t message)
|
||||
static int ad7879_i2c_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct ad7879 *ts = i2c_get_clientdata(client);
|
||||
|
||||
ad7879_suspend(ts);
|
||||
@ -25,17 +27,17 @@ static int ad7879_i2c_suspend(struct i2c_client *client, pm_message_t message)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ad7879_i2c_resume(struct i2c_client *client)
|
||||
static int ad7879_i2c_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct ad7879 *ts = i2c_get_clientdata(client);
|
||||
|
||||
ad7879_resume(ts);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
# define ad7879_i2c_suspend NULL
|
||||
# define ad7879_i2c_resume NULL
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(ad7879_i2c_pm, ad7879_i2c_suspend, ad7879_i2c_resume);
|
||||
#endif
|
||||
|
||||
/* All registers are word-sized.
|
||||
@ -117,11 +119,12 @@ static struct i2c_driver ad7879_i2c_driver = {
|
||||
.driver = {
|
||||
.name = "ad7879",
|
||||
.owner = THIS_MODULE,
|
||||
#ifdef CONFIG_PM
|
||||
.pm = &ad7879_i2c_pm,
|
||||
#endif
|
||||
},
|
||||
.probe = ad7879_i2c_probe,
|
||||
.remove = __devexit_p(ad7879_i2c_remove),
|
||||
.suspend = ad7879_i2c_suspend,
|
||||
.resume = ad7879_i2c_resume,
|
||||
.id_table = ad7879_id,
|
||||
};
|
||||
|
||||
|
@ -280,8 +280,9 @@ err_free_mem:
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int cy8ctmg110_suspend(struct i2c_client *client, pm_message_t mesg)
|
||||
static int cy8ctmg110_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct cy8ctmg110 *ts = i2c_get_clientdata(client);
|
||||
|
||||
if (device_may_wakeup(&client->dev))
|
||||
@ -293,8 +294,9 @@ static int cy8ctmg110_suspend(struct i2c_client *client, pm_message_t mesg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cy8ctmg110_resume(struct i2c_client *client)
|
||||
static int cy8ctmg110_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct cy8ctmg110 *ts = i2c_get_clientdata(client);
|
||||
|
||||
if (device_may_wakeup(&client->dev))
|
||||
@ -305,6 +307,8 @@ static int cy8ctmg110_resume(struct i2c_client *client)
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(cy8ctmg110_pm, cy8ctmg110_suspend, cy8ctmg110_resume);
|
||||
#endif
|
||||
|
||||
static int __devexit cy8ctmg110_remove(struct i2c_client *client)
|
||||
@ -335,14 +339,13 @@ static struct i2c_driver cy8ctmg110_driver = {
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = CY8CTMG110_DRIVER_NAME,
|
||||
#ifdef CONFIG_PM
|
||||
.pm = &cy8ctmg110_pm,
|
||||
#endif
|
||||
},
|
||||
.id_table = cy8ctmg110_idtable,
|
||||
.probe = cy8ctmg110_probe,
|
||||
.remove = __devexit_p(cy8ctmg110_remove),
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = cy8ctmg110_suspend,
|
||||
.resume = cy8ctmg110_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int __init cy8ctmg110_init(void)
|
||||
|
@ -261,8 +261,9 @@ static int __devexit eeti_ts_remove(struct i2c_client *client)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int eeti_ts_suspend(struct i2c_client *client, pm_message_t mesg)
|
||||
static int eeti_ts_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct eeti_ts_priv *priv = i2c_get_clientdata(client);
|
||||
struct input_dev *input_dev = priv->input;
|
||||
|
||||
@ -279,8 +280,9 @@ static int eeti_ts_suspend(struct i2c_client *client, pm_message_t mesg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int eeti_ts_resume(struct i2c_client *client)
|
||||
static int eeti_ts_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct eeti_ts_priv *priv = i2c_get_clientdata(client);
|
||||
struct input_dev *input_dev = priv->input;
|
||||
|
||||
@ -296,9 +298,8 @@ static int eeti_ts_resume(struct i2c_client *client)
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define eeti_ts_suspend NULL
|
||||
#define eeti_ts_resume NULL
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(eeti_ts_pm, eeti_ts_suspend, eeti_ts_resume);
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id eeti_ts_id[] = {
|
||||
@ -310,11 +311,12 @@ MODULE_DEVICE_TABLE(i2c, eeti_ts_id);
|
||||
static struct i2c_driver eeti_ts_driver = {
|
||||
.driver = {
|
||||
.name = "eeti_ts",
|
||||
#ifdef CONFIG_PM
|
||||
.pm = &eeti_ts_pm,
|
||||
#endif
|
||||
},
|
||||
.probe = eeti_ts_probe,
|
||||
.remove = __devexit_p(eeti_ts_remove),
|
||||
.suspend = eeti_ts_suspend,
|
||||
.resume = eeti_ts_resume,
|
||||
.id_table = eeti_ts_id,
|
||||
};
|
||||
|
||||
|
@ -261,25 +261,27 @@ static int __devexit mcs5000_ts_remove(struct i2c_client *client)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int mcs5000_ts_suspend(struct i2c_client *client, pm_message_t mesg)
|
||||
static int mcs5000_ts_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
|
||||
/* Touch sleep mode */
|
||||
i2c_smbus_write_byte_data(client, MCS5000_TS_OP_MODE, OP_MODE_SLEEP);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mcs5000_ts_resume(struct i2c_client *client)
|
||||
static int mcs5000_ts_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct mcs5000_ts_data *data = i2c_get_clientdata(client);
|
||||
|
||||
mcs5000_ts_phys_init(data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define mcs5000_ts_suspend NULL
|
||||
#define mcs5000_ts_resume NULL
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(mcs5000_ts_pm, mcs5000_ts_suspend, mcs5000_ts_resume);
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id mcs5000_ts_id[] = {
|
||||
@ -291,10 +293,11 @@ MODULE_DEVICE_TABLE(i2c, mcs5000_ts_id);
|
||||
static struct i2c_driver mcs5000_ts_driver = {
|
||||
.probe = mcs5000_ts_probe,
|
||||
.remove = __devexit_p(mcs5000_ts_remove),
|
||||
.suspend = mcs5000_ts_suspend,
|
||||
.resume = mcs5000_ts_resume,
|
||||
.driver = {
|
||||
.name = "mcs5000_ts",
|
||||
#ifdef CONFIG_PM
|
||||
.pm = &mcs5000_ts_pm,
|
||||
#endif
|
||||
},
|
||||
.id_table = mcs5000_ts_id,
|
||||
};
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/i2c.h>
|
||||
@ -226,8 +227,9 @@ static int migor_ts_remove(struct i2c_client *client)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int migor_ts_suspend(struct i2c_client *client, pm_message_t mesg)
|
||||
static int migor_ts_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct migor_ts_priv *priv = dev_get_drvdata(&client->dev);
|
||||
|
||||
if (device_may_wakeup(&client->dev))
|
||||
@ -236,8 +238,9 @@ static int migor_ts_suspend(struct i2c_client *client, pm_message_t mesg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int migor_ts_resume(struct i2c_client *client)
|
||||
static int migor_ts_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct migor_ts_priv *priv = dev_get_drvdata(&client->dev);
|
||||
|
||||
if (device_may_wakeup(&client->dev))
|
||||
@ -246,6 +249,8 @@ static int migor_ts_resume(struct i2c_client *client)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(migor_ts_pm, migor_ts_suspend, migor_ts_resume);
|
||||
|
||||
static const struct i2c_device_id migor_ts_id[] = {
|
||||
{ "migor_ts", 0 },
|
||||
{ }
|
||||
@ -255,11 +260,10 @@ MODULE_DEVICE_TABLE(i2c, migor_ts);
|
||||
static struct i2c_driver migor_ts_driver = {
|
||||
.driver = {
|
||||
.name = "migor_ts",
|
||||
.pm = &migor_ts_pm,
|
||||
},
|
||||
.probe = migor_ts_probe,
|
||||
.remove = migor_ts_remove,
|
||||
.suspend = migor_ts_suspend,
|
||||
.resume = migor_ts_resume,
|
||||
.id_table = migor_ts_id,
|
||||
};
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2008 Jaya Kumar
|
||||
* Copyright (c) 2010 Red Hat, Inc.
|
||||
* Copyright (c) 2010 - 2011 Ping Cheng, Wacom. <pingc@wacom.com>
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file COPYING in the main directory of this archive for
|
||||
@ -64,11 +65,11 @@ struct w8001_coord {
|
||||
|
||||
/* touch query reply packet */
|
||||
struct w8001_touch_query {
|
||||
u16 x;
|
||||
u16 y;
|
||||
u8 panel_res;
|
||||
u8 capacity_res;
|
||||
u8 sensor_id;
|
||||
u16 x;
|
||||
u16 y;
|
||||
};
|
||||
|
||||
/*
|
||||
@ -87,9 +88,14 @@ struct w8001 {
|
||||
char phys[32];
|
||||
int type;
|
||||
unsigned int pktlen;
|
||||
u16 max_touch_x;
|
||||
u16 max_touch_y;
|
||||
u16 max_pen_x;
|
||||
u16 max_pen_y;
|
||||
char name[64];
|
||||
};
|
||||
|
||||
static void parse_data(u8 *data, struct w8001_coord *coord)
|
||||
static void parse_pen_data(u8 *data, struct w8001_coord *coord)
|
||||
{
|
||||
memset(coord, 0, sizeof(*coord));
|
||||
|
||||
@ -113,11 +119,30 @@ static void parse_data(u8 *data, struct w8001_coord *coord)
|
||||
coord->tilt_y = data[8] & 0x7F;
|
||||
}
|
||||
|
||||
static void parse_touch(struct w8001 *w8001)
|
||||
static void parse_single_touch(u8 *data, struct w8001_coord *coord)
|
||||
{
|
||||
coord->x = (data[1] << 7) | data[2];
|
||||
coord->y = (data[3] << 7) | data[4];
|
||||
coord->tsw = data[0] & 0x01;
|
||||
}
|
||||
|
||||
static void scale_touch_coordinates(struct w8001 *w8001,
|
||||
unsigned int *x, unsigned int *y)
|
||||
{
|
||||
if (w8001->max_pen_x && w8001->max_touch_x)
|
||||
*x = *x * w8001->max_pen_x / w8001->max_touch_x;
|
||||
|
||||
if (w8001->max_pen_y && w8001->max_touch_y)
|
||||
*y = *y * w8001->max_pen_y / w8001->max_touch_y;
|
||||
}
|
||||
|
||||
static void parse_multi_touch(struct w8001 *w8001)
|
||||
{
|
||||
struct input_dev *dev = w8001->dev;
|
||||
unsigned char *data = w8001->data;
|
||||
unsigned int x, y;
|
||||
int i;
|
||||
int count = 0;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
bool touch = data[0] & (1 << i);
|
||||
@ -125,15 +150,29 @@ static void parse_touch(struct w8001 *w8001)
|
||||
input_mt_slot(dev, i);
|
||||
input_mt_report_slot_state(dev, MT_TOOL_FINGER, touch);
|
||||
if (touch) {
|
||||
int x = (data[6 * i + 1] << 7) | (data[6 * i + 2]);
|
||||
int y = (data[6 * i + 3] << 7) | (data[6 * i + 4]);
|
||||
x = (data[6 * i + 1] << 7) | data[6 * i + 2];
|
||||
y = (data[6 * i + 3] << 7) | data[6 * i + 4];
|
||||
/* data[5,6] and [11,12] is finger capacity */
|
||||
|
||||
/* scale to pen maximum */
|
||||
scale_touch_coordinates(w8001, &x, &y);
|
||||
|
||||
input_report_abs(dev, ABS_MT_POSITION_X, x);
|
||||
input_report_abs(dev, ABS_MT_POSITION_Y, y);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
/* emulate single touch events when stylus is out of proximity.
|
||||
* This is to make single touch backward support consistent
|
||||
* across all Wacom single touch devices.
|
||||
*/
|
||||
if (w8001->type != BTN_TOOL_PEN &&
|
||||
w8001->type != BTN_TOOL_RUBBER) {
|
||||
w8001->type = count == 1 ? BTN_TOOL_FINGER : KEY_RESERVED;
|
||||
input_mt_report_pointer_emulation(dev, true);
|
||||
}
|
||||
|
||||
input_sync(dev);
|
||||
}
|
||||
|
||||
@ -152,6 +191,15 @@ static void parse_touchquery(u8 *data, struct w8001_touch_query *query)
|
||||
query->y = data[5] << 9;
|
||||
query->y |= data[6] << 2;
|
||||
query->y |= (data[2] >> 3) & 0x3;
|
||||
|
||||
/* Early days' single-finger touch models need the following defaults */
|
||||
if (!query->x && !query->y) {
|
||||
query->x = 1024;
|
||||
query->y = 1024;
|
||||
if (query->panel_res)
|
||||
query->x = query->y = (1 << query->panel_res);
|
||||
query->panel_res = 10;
|
||||
}
|
||||
}
|
||||
|
||||
static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord)
|
||||
@ -161,16 +209,15 @@ static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord)
|
||||
/*
|
||||
* We have 1 bit for proximity (rdy) and 3 bits for tip, side,
|
||||
* side2/eraser. If rdy && f2 are set, this can be either pen + side2,
|
||||
* or eraser. assume
|
||||
* or eraser. Assume:
|
||||
* - if dev is already in proximity and f2 is toggled → pen + side2
|
||||
* - if dev comes into proximity with f2 set → eraser
|
||||
* If f2 disappears after assuming eraser, fake proximity out for
|
||||
* eraser and in for pen.
|
||||
*/
|
||||
|
||||
if (!w8001->type) {
|
||||
w8001->type = coord->f2 ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
|
||||
} else if (w8001->type == BTN_TOOL_RUBBER) {
|
||||
switch (w8001->type) {
|
||||
case BTN_TOOL_RUBBER:
|
||||
if (!coord->f2) {
|
||||
input_report_abs(dev, ABS_PRESSURE, 0);
|
||||
input_report_key(dev, BTN_TOUCH, 0);
|
||||
@ -180,8 +227,21 @@ static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord)
|
||||
input_sync(dev);
|
||||
w8001->type = BTN_TOOL_PEN;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
|
||||
case BTN_TOOL_FINGER:
|
||||
input_report_key(dev, BTN_TOUCH, 0);
|
||||
input_report_key(dev, BTN_TOOL_FINGER, 0);
|
||||
input_sync(dev);
|
||||
/* fall through */
|
||||
|
||||
case KEY_RESERVED:
|
||||
w8001->type = coord->f2 ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
|
||||
break;
|
||||
|
||||
default:
|
||||
input_report_key(dev, BTN_STYLUS2, coord->f2);
|
||||
break;
|
||||
}
|
||||
|
||||
input_report_abs(dev, ABS_X, coord->x);
|
||||
@ -193,7 +253,26 @@ static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord)
|
||||
input_sync(dev);
|
||||
|
||||
if (!coord->rdy)
|
||||
w8001->type = 0;
|
||||
w8001->type = KEY_RESERVED;
|
||||
}
|
||||
|
||||
static void report_single_touch(struct w8001 *w8001, struct w8001_coord *coord)
|
||||
{
|
||||
struct input_dev *dev = w8001->dev;
|
||||
unsigned int x = coord->x;
|
||||
unsigned int y = coord->y;
|
||||
|
||||
/* scale to pen maximum */
|
||||
scale_touch_coordinates(w8001, &x, &y);
|
||||
|
||||
input_report_abs(dev, ABS_X, x);
|
||||
input_report_abs(dev, ABS_Y, y);
|
||||
input_report_key(dev, BTN_TOUCH, coord->tsw);
|
||||
input_report_key(dev, BTN_TOOL_FINGER, coord->tsw);
|
||||
|
||||
input_sync(dev);
|
||||
|
||||
w8001->type = coord->tsw ? BTN_TOOL_FINGER : KEY_RESERVED;
|
||||
}
|
||||
|
||||
static irqreturn_t w8001_interrupt(struct serio *serio,
|
||||
@ -214,9 +293,18 @@ static irqreturn_t w8001_interrupt(struct serio *serio,
|
||||
|
||||
case W8001_PKTLEN_TOUCH93 - 1:
|
||||
case W8001_PKTLEN_TOUCH9A - 1:
|
||||
/* ignore one-finger touch packet. */
|
||||
if (w8001->pktlen == w8001->idx)
|
||||
tmp = w8001->data[0] & W8001_TOUCH_BYTE;
|
||||
if (tmp != W8001_TOUCH_BYTE)
|
||||
break;
|
||||
|
||||
if (w8001->pktlen == w8001->idx) {
|
||||
w8001->idx = 0;
|
||||
if (w8001->type != BTN_TOOL_PEN &&
|
||||
w8001->type != BTN_TOOL_RUBBER) {
|
||||
parse_single_touch(w8001->data, &coord);
|
||||
report_single_touch(w8001, &coord);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
/* Pen coordinates packet */
|
||||
@ -225,18 +313,18 @@ static irqreturn_t w8001_interrupt(struct serio *serio,
|
||||
if (unlikely(tmp == W8001_TAB_BYTE))
|
||||
break;
|
||||
|
||||
tmp = (w8001->data[0] & W8001_TOUCH_BYTE);
|
||||
tmp = w8001->data[0] & W8001_TOUCH_BYTE;
|
||||
if (tmp == W8001_TOUCH_BYTE)
|
||||
break;
|
||||
|
||||
w8001->idx = 0;
|
||||
parse_data(w8001->data, &coord);
|
||||
parse_pen_data(w8001->data, &coord);
|
||||
report_pen_events(w8001, &coord);
|
||||
break;
|
||||
|
||||
/* control packet */
|
||||
case W8001_PKTLEN_TPCCTL - 1:
|
||||
tmp = (w8001->data[0] & W8001_TOUCH_MASK);
|
||||
tmp = w8001->data[0] & W8001_TOUCH_MASK;
|
||||
if (tmp == W8001_TOUCH_BYTE)
|
||||
break;
|
||||
|
||||
@ -249,7 +337,7 @@ static irqreturn_t w8001_interrupt(struct serio *serio,
|
||||
/* 2 finger touch packet */
|
||||
case W8001_PKTLEN_TOUCH2FG - 1:
|
||||
w8001->idx = 0;
|
||||
parse_touch(w8001);
|
||||
parse_multi_touch(w8001);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -279,6 +367,7 @@ static int w8001_setup(struct w8001 *w8001)
|
||||
{
|
||||
struct input_dev *dev = w8001->dev;
|
||||
struct w8001_coord coord;
|
||||
struct w8001_touch_query touch;
|
||||
int error;
|
||||
|
||||
error = w8001_command(w8001, W8001_CMD_STOP, false);
|
||||
@ -287,14 +376,21 @@ static int w8001_setup(struct w8001 *w8001)
|
||||
|
||||
msleep(250); /* wait 250ms before querying the device */
|
||||
|
||||
dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
|
||||
strlcat(w8001->name, "Wacom Serial", sizeof(w8001->name));
|
||||
|
||||
/* penabled? */
|
||||
error = w8001_command(w8001, W8001_CMD_QUERY, true);
|
||||
if (!error) {
|
||||
__set_bit(BTN_TOUCH, dev->keybit);
|
||||
__set_bit(BTN_TOOL_PEN, dev->keybit);
|
||||
__set_bit(BTN_TOOL_RUBBER, dev->keybit);
|
||||
__set_bit(BTN_STYLUS, dev->keybit);
|
||||
__set_bit(BTN_STYLUS2, dev->keybit);
|
||||
parse_data(w8001->response, &coord);
|
||||
|
||||
parse_pen_data(w8001->response, &coord);
|
||||
w8001->max_pen_x = coord.x;
|
||||
w8001->max_pen_y = coord.y;
|
||||
|
||||
input_set_abs_params(dev, ABS_X, 0, coord.x, 0, 0);
|
||||
input_set_abs_params(dev, ABS_Y, 0, coord.y, 0, 0);
|
||||
@ -303,6 +399,8 @@ static int w8001_setup(struct w8001 *w8001)
|
||||
input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0);
|
||||
input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0);
|
||||
}
|
||||
w8001->id = 0x90;
|
||||
strlcat(w8001->name, " Penabled", sizeof(w8001->name));
|
||||
}
|
||||
|
||||
/* Touch enabled? */
|
||||
@ -313,24 +411,38 @@ static int w8001_setup(struct w8001 *w8001)
|
||||
* second byte is empty, which indicates touch is not supported.
|
||||
*/
|
||||
if (!error && w8001->response[1]) {
|
||||
struct w8001_touch_query touch;
|
||||
__set_bit(BTN_TOUCH, dev->keybit);
|
||||
__set_bit(BTN_TOOL_FINGER, dev->keybit);
|
||||
|
||||
parse_touchquery(w8001->response, &touch);
|
||||
w8001->max_touch_x = touch.x;
|
||||
w8001->max_touch_y = touch.y;
|
||||
|
||||
/* scale to pen maximum */
|
||||
if (w8001->max_pen_x && w8001->max_pen_y) {
|
||||
touch.x = w8001->max_pen_x;
|
||||
touch.y = w8001->max_pen_y;
|
||||
}
|
||||
|
||||
input_set_abs_params(dev, ABS_X, 0, touch.x, 0, 0);
|
||||
input_set_abs_params(dev, ABS_Y, 0, touch.y, 0, 0);
|
||||
__set_bit(BTN_TOOL_FINGER, dev->keybit);
|
||||
|
||||
switch (touch.sensor_id) {
|
||||
case 0:
|
||||
case 2:
|
||||
w8001->pktlen = W8001_PKTLEN_TOUCH93;
|
||||
w8001->id = 0x93;
|
||||
strlcat(w8001->name, " 1FG", sizeof(w8001->name));
|
||||
break;
|
||||
|
||||
case 1:
|
||||
case 3:
|
||||
case 4:
|
||||
w8001->pktlen = W8001_PKTLEN_TOUCH9A;
|
||||
strlcat(w8001->name, " 1FG", sizeof(w8001->name));
|
||||
w8001->id = 0x9a;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
w8001->pktlen = W8001_PKTLEN_TOUCH2FG;
|
||||
|
||||
@ -341,10 +453,18 @@ static int w8001_setup(struct w8001 *w8001)
|
||||
0, touch.y, 0, 0);
|
||||
input_set_abs_params(dev, ABS_MT_TOOL_TYPE,
|
||||
0, MT_TOOL_MAX, 0, 0);
|
||||
|
||||
strlcat(w8001->name, " 2FG", sizeof(w8001->name));
|
||||
if (w8001->max_pen_x && w8001->max_pen_y)
|
||||
w8001->id = 0xE3;
|
||||
else
|
||||
w8001->id = 0xE2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
strlcat(w8001->name, " Touchscreen", sizeof(w8001->name));
|
||||
|
||||
return w8001_command(w8001, W8001_CMD_START, false);
|
||||
}
|
||||
|
||||
@ -384,22 +504,10 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv)
|
||||
}
|
||||
|
||||
w8001->serio = serio;
|
||||
w8001->id = serio->id.id;
|
||||
w8001->dev = input_dev;
|
||||
init_completion(&w8001->cmd_done);
|
||||
snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys);
|
||||
|
||||
input_dev->name = "Wacom W8001 Penabled Serial TouchScreen";
|
||||
input_dev->phys = w8001->phys;
|
||||
input_dev->id.bustype = BUS_RS232;
|
||||
input_dev->id.vendor = SERIO_W8001;
|
||||
input_dev->id.product = w8001->id;
|
||||
input_dev->id.version = 0x0100;
|
||||
input_dev->dev.parent = &serio->dev;
|
||||
|
||||
input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
|
||||
__set_bit(BTN_TOUCH, input_dev->keybit);
|
||||
|
||||
serio_set_drvdata(serio, w8001);
|
||||
err = serio_open(serio, drv);
|
||||
if (err)
|
||||
@ -409,6 +517,14 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv)
|
||||
if (err)
|
||||
goto fail3;
|
||||
|
||||
input_dev->name = w8001->name;
|
||||
input_dev->phys = w8001->phys;
|
||||
input_dev->id.product = w8001->id;
|
||||
input_dev->id.bustype = BUS_RS232;
|
||||
input_dev->id.vendor = 0x056a;
|
||||
input_dev->id.version = 0x0100;
|
||||
input_dev->dev.parent = &serio->dev;
|
||||
|
||||
err = input_register_device(w8001->dev);
|
||||
if (err)
|
||||
goto fail3;
|
||||
|
@ -800,6 +800,7 @@ struct input_keymap_entry {
|
||||
#define SW_CAMERA_LENS_COVER 0x09 /* set = lens covered */
|
||||
#define SW_KEYPAD_SLIDE 0x0a /* set = keypad slide out */
|
||||
#define SW_FRONT_PROXIMITY 0x0b /* set = front proximity sensor active */
|
||||
#define SW_ROTATE_LOCK 0x0c /* set = rotate locked/disabled */
|
||||
#define SW_MAX 0x0f
|
||||
#define SW_CNT (SW_MAX+1)
|
||||
|
||||
|
20
include/linux/input/as5011.h
Normal file
20
include/linux/input/as5011.h
Normal file
@ -0,0 +1,20 @@
|
||||
#ifndef _AS5011_H
|
||||
#define _AS5011_H
|
||||
|
||||
/*
|
||||
* Copyright (c) 2010, 2011 Fabien Marteau <fabien.marteau@armadeus.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
struct as5011_platform_data {
|
||||
unsigned int button_gpio;
|
||||
unsigned int axis_irq; /* irq number */
|
||||
unsigned long axis_irqflags;
|
||||
char xp, xn; /* threshold for x axis */
|
||||
char yp, yn; /* threshold for y axis */
|
||||
};
|
||||
|
||||
#endif /* _AS5011_H */
|
Loading…
Reference in New Issue
Block a user