Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
Pull input updates from Dmitry Torokhov: - new driver for eGalaxTouch serial touchscreen - new driver for TS-4800 touchscreen - an update for Goodix touchscreen driver - PS/2 mouse module was reworked to limit number of protocols we try on pass-through ports to speed up their detection time - wacom_w8001 touchscreen driver now reports pen and touch via separate instances of input devices - other driver changes * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (42 commits) Input: elantech - mark protocols v2 and v3 as semi-mt Input: wacom_w8001 - drop use of ABS_MT_TOOL_TYPE Input: gpio-keys - fix check for disabling unsupported keys Input: omap-keypad - remove dead check Input: ti_am335x_tsc - fix HWPEN interrupt handling Input: omap-keypad - set tasklet data earlier Input: rohm_bu21023 - fix handling of retrying firmware update Input: ALPS - report v3 pinnacle trackstick device only if is present Input: ALPS - detect trackstick presence for v7 protocol Input: pcap_ts - use to_delayed_work Input: bma150 - constify bma150_cfg structure Input: i8042 - add Fujitsu Lifebook U745 to the nomux list Input: egalax_ts_serial - fix potential NULL dereference on error Input: uinput - sanity check on ff_effects_max and EV_FF Input: uinput - rework ABS validation Input: uinput - add new UINPUT_DEV_SETUP and UI_ABS_SETUP ioctl Input: goodix - use "inverted_[xy]" flags instead of "rotated_screen" Input: goodix - add axis swapping and axis inversion support Input: goodix - use goodix_i2c_write_u8 instead of i2c_master_send Input: goodix - add power management support ...
This commit is contained in:
commit
1c5ff2ab7b
@ -13,6 +13,17 @@ Required properties:
|
||||
- interrupt-parent : Interrupt controller to which the chip is connected
|
||||
- interrupts : Interrupt to which the chip is connected
|
||||
|
||||
Optional properties:
|
||||
|
||||
- irq-gpios : GPIO pin used for IRQ. The driver uses the
|
||||
interrupt gpio pin as output to reset the device.
|
||||
- reset-gpios : GPIO pin used for reset
|
||||
|
||||
- touchscreen-inverted-x : X axis is inverted (boolean)
|
||||
- touchscreen-inverted-y : Y axis is inverted (boolean)
|
||||
- touchscreen-swapped-x-y : X and Y axis are swapped (boolean)
|
||||
(swapping is done after inverting the axis)
|
||||
|
||||
Example:
|
||||
|
||||
i2c@00000000 {
|
||||
@ -23,6 +34,9 @@ Example:
|
||||
reg = <0x5d>;
|
||||
interrupt-parent = <&gpio>;
|
||||
interrupts = <0 0>;
|
||||
|
||||
irq-gpios = <&gpio1 0 0>;
|
||||
reset-gpios = <&gpio1 1 0>;
|
||||
};
|
||||
|
||||
/* ... */
|
||||
|
@ -9,7 +9,9 @@ Required properties:
|
||||
- touchscreen-size-y: vertical resolution of touchscreen (in pixels)
|
||||
|
||||
Optional properties:
|
||||
- reset-gpio: GPIO connected to the RESET line of the chip
|
||||
- reset-gpios: GPIO connected to the RESET line of the chip
|
||||
- enable-gpios: GPIO connected to the ENABLE line of the chip
|
||||
- wake-gpios: GPIO connected to the WAKE line of the chip
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -0,0 +1,11 @@
|
||||
* TS-4800 Touchscreen bindings
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "technologic,ts4800-ts"
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
region.
|
||||
- syscon: phandle / integers array that points to the syscon node which
|
||||
describes the FPGA's syscon registers.
|
||||
- phandle to FPGA's syscon
|
||||
- offset to the touchscreen register
|
||||
- offset to the touchscreen enable bit
|
@ -943,3 +943,46 @@ int acpi_gpio_count(struct device *dev, const char *con_id)
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
struct acpi_crs_lookup {
|
||||
struct list_head node;
|
||||
struct acpi_device *adev;
|
||||
const char *con_id;
|
||||
};
|
||||
|
||||
static DEFINE_MUTEX(acpi_crs_lookup_lock);
|
||||
static LIST_HEAD(acpi_crs_lookup_list);
|
||||
|
||||
bool acpi_can_fallback_to_crs(struct acpi_device *adev, const char *con_id)
|
||||
{
|
||||
struct acpi_crs_lookup *l, *lookup = NULL;
|
||||
|
||||
/* Never allow fallback if the device has properties */
|
||||
if (adev->data.properties || adev->driver_gpios)
|
||||
return false;
|
||||
|
||||
mutex_lock(&acpi_crs_lookup_lock);
|
||||
|
||||
list_for_each_entry(l, &acpi_crs_lookup_list, node) {
|
||||
if (l->adev == adev) {
|
||||
lookup = l;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!lookup) {
|
||||
lookup = kmalloc(sizeof(*lookup), GFP_KERNEL);
|
||||
if (lookup) {
|
||||
lookup->adev = adev;
|
||||
lookup->con_id = con_id;
|
||||
list_add_tail(&lookup->node, &acpi_crs_lookup_list);
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&acpi_crs_lookup_lock);
|
||||
|
||||
return lookup &&
|
||||
((!lookup->con_id && !con_id) ||
|
||||
(lookup->con_id && con_id &&
|
||||
strcmp(lookup->con_id, con_id) == 0));
|
||||
}
|
||||
|
@ -1874,6 +1874,9 @@ static struct gpio_desc *acpi_find_gpio(struct device *dev, const char *con_id,
|
||||
|
||||
/* Then from plain _CRS GPIOs */
|
||||
if (IS_ERR(desc)) {
|
||||
if (!acpi_can_fallback_to_crs(adev, con_id))
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
desc = acpi_get_gpiod_by_index(adev, NULL, idx, &info);
|
||||
if (IS_ERR(desc))
|
||||
return desc;
|
||||
|
@ -48,6 +48,8 @@ struct gpio_desc *acpi_node_get_gpiod(struct fwnode_handle *fwnode,
|
||||
struct acpi_gpio_info *info);
|
||||
|
||||
int acpi_gpio_count(struct device *dev, const char *con_id);
|
||||
|
||||
bool acpi_can_fallback_to_crs(struct acpi_device *adev, const char *con_id);
|
||||
#else
|
||||
static inline void acpi_gpiochip_add(struct gpio_chip *chip) { }
|
||||
static inline void acpi_gpiochip_remove(struct gpio_chip *chip) { }
|
||||
@ -74,6 +76,12 @@ static inline int acpi_gpio_count(struct device *dev, const char *con_id)
|
||||
{
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
static inline bool acpi_can_fallback_to_crs(struct acpi_device *adev,
|
||||
const char *con_id)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np,
|
||||
|
@ -96,13 +96,29 @@ struct gpio_keys_drvdata {
|
||||
* Return value of this function can be used to allocate bitmap
|
||||
* large enough to hold all bits for given type.
|
||||
*/
|
||||
static inline int get_n_events_by_type(int type)
|
||||
static int get_n_events_by_type(int type)
|
||||
{
|
||||
BUG_ON(type != EV_SW && type != EV_KEY);
|
||||
|
||||
return (type == EV_KEY) ? KEY_CNT : SW_CNT;
|
||||
}
|
||||
|
||||
/**
|
||||
* get_bm_events_by_type() - returns bitmap of supported events per @type
|
||||
* @input: input device from which bitmap is retrieved
|
||||
* @type: type of button (%EV_KEY, %EV_SW)
|
||||
*
|
||||
* Return value of this function can be used to allocate bitmap
|
||||
* large enough to hold all bits for given type.
|
||||
*/
|
||||
static const unsigned long *get_bm_events_by_type(struct input_dev *dev,
|
||||
int type)
|
||||
{
|
||||
BUG_ON(type != EV_SW && type != EV_KEY);
|
||||
|
||||
return (type == EV_KEY) ? dev->keybit : dev->swbit;
|
||||
}
|
||||
|
||||
/**
|
||||
* gpio_keys_disable_button() - disables given GPIO button
|
||||
* @bdata: button data for button to be disabled
|
||||
@ -213,6 +229,7 @@ static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata,
|
||||
const char *buf, unsigned int type)
|
||||
{
|
||||
int n_events = get_n_events_by_type(type);
|
||||
const unsigned long *bitmap = get_bm_events_by_type(ddata->input, type);
|
||||
unsigned long *bits;
|
||||
ssize_t error;
|
||||
int i;
|
||||
@ -226,6 +243,11 @@ static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata,
|
||||
goto out;
|
||||
|
||||
/* First validate */
|
||||
if (!bitmap_subset(bits, bitmap, n_events)) {
|
||||
error = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < ddata->pdata->nbuttons; i++) {
|
||||
struct gpio_button_data *bdata = &ddata->data[i];
|
||||
|
||||
@ -239,11 +261,6 @@ static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata,
|
||||
}
|
||||
}
|
||||
|
||||
if (i == ddata->pdata->nbuttons) {
|
||||
error = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mutex_lock(&ddata->disable_lock);
|
||||
|
||||
for (i = 0; i < ddata->pdata->nbuttons; i++) {
|
||||
|
@ -155,14 +155,6 @@ static void omap_kp_tasklet(unsigned long data)
|
||||
"pressed" : "released");
|
||||
#else
|
||||
key = keycodes[MATRIX_SCAN_CODE(row, col, row_shift)];
|
||||
if (key < 0) {
|
||||
printk(KERN_WARNING
|
||||
"omap-keypad: Spurious key event %d-%d\n",
|
||||
col, row);
|
||||
/* We scan again after a couple of seconds */
|
||||
spurious = 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(kp_cur_group == (key & GROUP_MASK) ||
|
||||
kp_cur_group == -1))
|
||||
@ -292,8 +284,8 @@ static int omap_kp_probe(struct platform_device *pdev)
|
||||
setup_timer(&omap_kp->timer, omap_kp_timer, (unsigned long)omap_kp);
|
||||
|
||||
/* get the irq and init timer*/
|
||||
tasklet_enable(&kp_tasklet);
|
||||
kp_tasklet.data = (unsigned long) omap_kp;
|
||||
tasklet_enable(&kp_tasklet);
|
||||
|
||||
ret = device_create_file(&pdev->dev, &dev_attr_enable);
|
||||
if (ret < 0)
|
||||
|
@ -147,7 +147,7 @@ struct bma150_data {
|
||||
* are stated and verified by Bosch Sensortec where they are configured
|
||||
* to provide a generic sensitivity performance.
|
||||
*/
|
||||
static struct bma150_cfg default_cfg = {
|
||||
static const struct bma150_cfg default_cfg = {
|
||||
.any_motion_int = 1,
|
||||
.hg_int = 1,
|
||||
.lg_int = 1,
|
||||
|
@ -179,13 +179,13 @@ static irqreturn_t da9063_onkey_irq_handler(int irq, void *data)
|
||||
input_report_key(onkey->input, KEY_POWER, 1);
|
||||
input_sync(onkey->input);
|
||||
schedule_delayed_work(&onkey->work, 0);
|
||||
dev_dbg(onkey->dev, "KEY_POWER pressed.\n");
|
||||
dev_dbg(onkey->dev, "KEY_POWER long press.\n");
|
||||
} else {
|
||||
input_report_key(onkey->input, KEY_SLEEP, 1);
|
||||
input_report_key(onkey->input, KEY_POWER, 1);
|
||||
input_sync(onkey->input);
|
||||
input_report_key(onkey->input, KEY_SLEEP, 0);
|
||||
input_report_key(onkey->input, KEY_POWER, 0);
|
||||
input_sync(onkey->input);
|
||||
dev_dbg(onkey->dev, "KEY_SLEEP pressed.\n");
|
||||
dev_dbg(onkey->dev, "KEY_POWER short press.\n");
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
|
@ -345,23 +345,19 @@ static struct platform_driver grover_beep_driver = {
|
||||
.shutdown = sparcspkr_shutdown,
|
||||
};
|
||||
|
||||
static struct platform_driver * const drivers[] = {
|
||||
&bbc_beep_driver,
|
||||
&grover_beep_driver,
|
||||
};
|
||||
|
||||
static int __init sparcspkr_init(void)
|
||||
{
|
||||
int err = platform_driver_register(&bbc_beep_driver);
|
||||
|
||||
if (!err) {
|
||||
err = platform_driver_register(&grover_beep_driver);
|
||||
if (err)
|
||||
platform_driver_unregister(&bbc_beep_driver);
|
||||
}
|
||||
|
||||
return err;
|
||||
return platform_register_drivers(drivers, ARRAY_SIZE(drivers));
|
||||
}
|
||||
|
||||
static void __exit sparcspkr_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&bbc_beep_driver);
|
||||
platform_driver_unregister(&grover_beep_driver);
|
||||
platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
|
||||
}
|
||||
|
||||
module_init(sparcspkr_init);
|
||||
|
@ -256,13 +256,29 @@ static void uinput_destroy_device(struct uinput_device *udev)
|
||||
static int uinput_create_device(struct uinput_device *udev)
|
||||
{
|
||||
struct input_dev *dev = udev->dev;
|
||||
int error;
|
||||
int error, nslot;
|
||||
|
||||
if (udev->state != UIST_SETUP_COMPLETE) {
|
||||
printk(KERN_DEBUG "%s: write device info first\n", UINPUT_NAME);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (test_bit(ABS_MT_SLOT, dev->absbit)) {
|
||||
nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1;
|
||||
error = input_mt_init_slots(dev, nslot, 0);
|
||||
if (error)
|
||||
goto fail1;
|
||||
} else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) {
|
||||
input_set_events_per_packet(dev, 60);
|
||||
}
|
||||
|
||||
if (test_bit(EV_FF, dev->evbit) && !udev->ff_effects_max) {
|
||||
printk(KERN_DEBUG "%s: ff_effects_max should be non-zero when FF_BIT is set\n",
|
||||
UINPUT_NAME);
|
||||
error = -EINVAL;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
if (udev->ff_effects_max) {
|
||||
error = input_ff_create(dev, udev->ff_effects_max);
|
||||
if (error)
|
||||
@ -308,10 +324,35 @@ static int uinput_open(struct inode *inode, struct file *file)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uinput_validate_absinfo(struct input_dev *dev, unsigned int code,
|
||||
const struct input_absinfo *abs)
|
||||
{
|
||||
int min, max;
|
||||
|
||||
min = abs->minimum;
|
||||
max = abs->maximum;
|
||||
|
||||
if ((min != 0 || max != 0) && max <= min) {
|
||||
printk(KERN_DEBUG
|
||||
"%s: invalid abs[%02x] min:%d max:%d\n",
|
||||
UINPUT_NAME, code, min, max);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (abs->flat > max - min) {
|
||||
printk(KERN_DEBUG
|
||||
"%s: abs_flat #%02x out of range: %d (min:%d/max:%d)\n",
|
||||
UINPUT_NAME, code, abs->flat, min, max);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uinput_validate_absbits(struct input_dev *dev)
|
||||
{
|
||||
unsigned int cnt;
|
||||
int nslot;
|
||||
int error;
|
||||
|
||||
if (!test_bit(EV_ABS, dev->evbit))
|
||||
return 0;
|
||||
@ -321,38 +362,12 @@ static int uinput_validate_absbits(struct input_dev *dev)
|
||||
*/
|
||||
|
||||
for_each_set_bit(cnt, dev->absbit, ABS_CNT) {
|
||||
int min, max;
|
||||
|
||||
min = input_abs_get_min(dev, cnt);
|
||||
max = input_abs_get_max(dev, cnt);
|
||||
|
||||
if ((min != 0 || max != 0) && max <= min) {
|
||||
printk(KERN_DEBUG
|
||||
"%s: invalid abs[%02x] min:%d max:%d\n",
|
||||
UINPUT_NAME, cnt,
|
||||
input_abs_get_min(dev, cnt),
|
||||
input_abs_get_max(dev, cnt));
|
||||
if (!dev->absinfo)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (input_abs_get_flat(dev, cnt) >
|
||||
input_abs_get_max(dev, cnt) - input_abs_get_min(dev, cnt)) {
|
||||
printk(KERN_DEBUG
|
||||
"%s: abs_flat #%02x out of range: %d "
|
||||
"(min:%d/max:%d)\n",
|
||||
UINPUT_NAME, cnt,
|
||||
input_abs_get_flat(dev, cnt),
|
||||
input_abs_get_min(dev, cnt),
|
||||
input_abs_get_max(dev, cnt));
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
error = uinput_validate_absinfo(dev, cnt, &dev->absinfo[cnt]);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -370,7 +385,70 @@ static int uinput_allocate_device(struct uinput_device *udev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uinput_setup_device(struct uinput_device *udev,
|
||||
static int uinput_dev_setup(struct uinput_device *udev,
|
||||
struct uinput_setup __user *arg)
|
||||
{
|
||||
struct uinput_setup setup;
|
||||
struct input_dev *dev;
|
||||
|
||||
if (udev->state == UIST_CREATED)
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_user(&setup, arg, sizeof(setup)))
|
||||
return -EFAULT;
|
||||
|
||||
if (!setup.name[0])
|
||||
return -EINVAL;
|
||||
|
||||
dev = udev->dev;
|
||||
dev->id = setup.id;
|
||||
udev->ff_effects_max = setup.ff_effects_max;
|
||||
|
||||
kfree(dev->name);
|
||||
dev->name = kstrndup(setup.name, UINPUT_MAX_NAME_SIZE, GFP_KERNEL);
|
||||
if (!dev->name)
|
||||
return -ENOMEM;
|
||||
|
||||
udev->state = UIST_SETUP_COMPLETE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int uinput_abs_setup(struct uinput_device *udev,
|
||||
struct uinput_setup __user *arg, size_t size)
|
||||
{
|
||||
struct uinput_abs_setup setup = {};
|
||||
struct input_dev *dev;
|
||||
int error;
|
||||
|
||||
if (size > sizeof(setup))
|
||||
return -E2BIG;
|
||||
|
||||
if (udev->state == UIST_CREATED)
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_user(&setup, arg, size))
|
||||
return -EFAULT;
|
||||
|
||||
if (setup.code > ABS_MAX)
|
||||
return -ERANGE;
|
||||
|
||||
dev = udev->dev;
|
||||
|
||||
error = uinput_validate_absinfo(dev, setup.code, &setup.absinfo);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
input_alloc_absinfo(dev);
|
||||
if (!dev->absinfo)
|
||||
return -ENOMEM;
|
||||
|
||||
set_bit(setup.code, dev->absbit);
|
||||
dev->absinfo[setup.code] = setup.absinfo;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* legacy setup via write() */
|
||||
static int uinput_setup_device_legacy(struct uinput_device *udev,
|
||||
const char __user *buffer, size_t count)
|
||||
{
|
||||
struct uinput_user_dev *user_dev;
|
||||
@ -474,7 +552,7 @@ static ssize_t uinput_write(struct file *file, const char __user *buffer,
|
||||
|
||||
retval = udev->state == UIST_CREATED ?
|
||||
uinput_inject_events(udev, buffer, count) :
|
||||
uinput_setup_device(udev, buffer, count);
|
||||
uinput_setup_device_legacy(udev, buffer, count);
|
||||
|
||||
mutex_unlock(&udev->mutex);
|
||||
|
||||
@ -735,6 +813,12 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
|
||||
uinput_destroy_device(udev);
|
||||
goto out;
|
||||
|
||||
case UI_DEV_SETUP:
|
||||
retval = uinput_dev_setup(udev, p);
|
||||
goto out;
|
||||
|
||||
/* UI_ABS_SETUP is handled in the variable size ioctls */
|
||||
|
||||
case UI_SET_EVBIT:
|
||||
retval = uinput_set_bit(arg, evbit, EV_MAX);
|
||||
goto out;
|
||||
@ -879,6 +963,10 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
|
||||
name = dev_name(&udev->dev->dev);
|
||||
retval = uinput_str_to_user(p, name, size);
|
||||
goto out;
|
||||
|
||||
case UI_ABS_SETUP & ~IOCSIZE_MASK:
|
||||
retval = uinput_abs_setup(udev, p, size);
|
||||
goto out;
|
||||
}
|
||||
|
||||
retval = -EINVAL;
|
||||
|
@ -31,6 +31,7 @@
|
||||
#define ALPS_CMD_NIBBLE_10 0x01f2
|
||||
|
||||
#define ALPS_REG_BASE_RUSHMORE 0xc2c0
|
||||
#define ALPS_REG_BASE_V7 0xc2c0
|
||||
#define ALPS_REG_BASE_PINNACLE 0x0000
|
||||
|
||||
static const struct alps_nibble_commands alps_v3_nibble_commands[] = {
|
||||
@ -2047,7 +2048,7 @@ static int alps_absolute_mode_v3(struct psmouse *psmouse)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int alps_probe_trackstick_v3(struct psmouse *psmouse, int reg_base)
|
||||
static int alps_probe_trackstick_v3_v7(struct psmouse *psmouse, int reg_base)
|
||||
{
|
||||
int ret = -EIO, reg_val;
|
||||
|
||||
@ -2128,15 +2129,12 @@ error:
|
||||
|
||||
static int alps_hw_init_v3(struct psmouse *psmouse)
|
||||
{
|
||||
struct alps_data *priv = psmouse->private;
|
||||
struct ps2dev *ps2dev = &psmouse->ps2dev;
|
||||
int reg_val;
|
||||
unsigned char param[4];
|
||||
|
||||
reg_val = alps_probe_trackstick_v3(psmouse, ALPS_REG_BASE_PINNACLE);
|
||||
if (reg_val == -EIO)
|
||||
goto error;
|
||||
|
||||
if (reg_val == 0 &&
|
||||
if ((priv->flags & ALPS_DUALPOINT) &&
|
||||
alps_setup_trackstick_v3(psmouse, ALPS_REG_BASE_PINNACLE) == -EIO)
|
||||
goto error;
|
||||
|
||||
@ -2613,6 +2611,11 @@ static int alps_set_protocol(struct psmouse *psmouse,
|
||||
priv->decode_fields = alps_decode_pinnacle;
|
||||
priv->nibble_commands = alps_v3_nibble_commands;
|
||||
priv->addr_command = PSMOUSE_CMD_RESET_WRAP;
|
||||
|
||||
if (alps_probe_trackstick_v3_v7(psmouse,
|
||||
ALPS_REG_BASE_PINNACLE) < 0)
|
||||
priv->flags &= ~ALPS_DUALPOINT;
|
||||
|
||||
break;
|
||||
|
||||
case ALPS_PROTO_V3_RUSHMORE:
|
||||
@ -2625,7 +2628,7 @@ static int alps_set_protocol(struct psmouse *psmouse,
|
||||
priv->x_bits = 16;
|
||||
priv->y_bits = 12;
|
||||
|
||||
if (alps_probe_trackstick_v3(psmouse,
|
||||
if (alps_probe_trackstick_v3_v7(psmouse,
|
||||
ALPS_REG_BASE_RUSHMORE) < 0)
|
||||
priv->flags &= ~ALPS_DUALPOINT;
|
||||
|
||||
@ -2676,6 +2679,9 @@ static int alps_set_protocol(struct psmouse *psmouse,
|
||||
if (priv->fw_ver[1] != 0xba)
|
||||
priv->flags |= ALPS_BUTTONPAD;
|
||||
|
||||
if (alps_probe_trackstick_v3_v7(psmouse, ALPS_REG_BASE_V7) < 0)
|
||||
priv->flags &= ~ALPS_DUALPOINT;
|
||||
|
||||
break;
|
||||
|
||||
case ALPS_PROTO_V8:
|
||||
|
@ -1222,7 +1222,7 @@ static int elantech_set_input_params(struct psmouse *psmouse)
|
||||
input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2,
|
||||
ETP_WMAX_V2, 0, 0);
|
||||
}
|
||||
input_mt_init_slots(dev, 2, 0);
|
||||
input_mt_init_slots(dev, 2, INPUT_MT_SEMI_MT);
|
||||
input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0);
|
||||
input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0);
|
||||
break;
|
||||
|
@ -49,12 +49,6 @@ int focaltech_detect(struct psmouse *psmouse, bool set_properties)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void focaltech_reset(struct psmouse *psmouse)
|
||||
{
|
||||
ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
|
||||
psmouse_reset(psmouse);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MOUSE_PS2_FOCALTECH
|
||||
|
||||
/*
|
||||
@ -300,6 +294,12 @@ static int focaltech_switch_protocol(struct psmouse *psmouse)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void focaltech_reset(struct psmouse *psmouse)
|
||||
{
|
||||
ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
|
||||
psmouse_reset(psmouse);
|
||||
}
|
||||
|
||||
static void focaltech_disconnect(struct psmouse *psmouse)
|
||||
{
|
||||
focaltech_reset(psmouse);
|
||||
@ -456,14 +456,4 @@ fail:
|
||||
kfree(priv);
|
||||
return error;
|
||||
}
|
||||
|
||||
#else /* CONFIG_MOUSE_PS2_FOCALTECH */
|
||||
|
||||
int focaltech_init(struct psmouse *psmouse)
|
||||
{
|
||||
focaltech_reset(psmouse);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_MOUSE_PS2_FOCALTECH */
|
||||
|
@ -18,6 +18,14 @@
|
||||
#define _FOCALTECH_H
|
||||
|
||||
int focaltech_detect(struct psmouse *psmouse, bool set_properties);
|
||||
|
||||
#ifdef CONFIG_MOUSE_PS2_FOCALTECH
|
||||
int focaltech_init(struct psmouse *psmouse);
|
||||
#else
|
||||
static inline int focaltech_init(struct psmouse *psmouse)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -325,7 +325,7 @@ static void ps2pp_set_model_properties(struct psmouse *psmouse,
|
||||
* that support it.
|
||||
*/
|
||||
|
||||
int ps2pp_init(struct psmouse *psmouse, bool set_properties)
|
||||
int ps2pp_detect(struct psmouse *psmouse, bool set_properties)
|
||||
{
|
||||
struct ps2dev *ps2dev = &psmouse->ps2dev;
|
||||
unsigned char param[4];
|
||||
|
@ -12,9 +12,9 @@
|
||||
#define _LOGIPS2PP_H
|
||||
|
||||
#ifdef CONFIG_MOUSE_PS2_LOGIPS2PP
|
||||
int ps2pp_init(struct psmouse *psmouse, bool set_properties);
|
||||
int ps2pp_detect(struct psmouse *psmouse, bool set_properties);
|
||||
#else
|
||||
inline int ps2pp_init(struct psmouse *psmouse, bool set_properties)
|
||||
static inline int ps2pp_detect(struct psmouse *psmouse, bool set_properties)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
@ -119,6 +119,7 @@ struct psmouse_protocol {
|
||||
enum psmouse_type type;
|
||||
bool maxproto;
|
||||
bool ignore_parity; /* Protocol should ignore parity errors from KBC */
|
||||
bool try_passthru; /* Try protocol also on passthrough ports */
|
||||
const char *name;
|
||||
const char *alias;
|
||||
int (*detect)(struct psmouse *, bool);
|
||||
@ -129,7 +130,6 @@ struct psmouse_protocol {
|
||||
* psmouse_process_byte() analyzes the PS/2 data stream and reports
|
||||
* relevant events to the input module once full packet has arrived.
|
||||
*/
|
||||
|
||||
psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)
|
||||
{
|
||||
struct input_dev *dev = psmouse->dev;
|
||||
@ -138,22 +138,16 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)
|
||||
if (psmouse->pktcnt < psmouse->pktsize)
|
||||
return PSMOUSE_GOOD_DATA;
|
||||
|
||||
/*
|
||||
* Full packet accumulated, process it
|
||||
*/
|
||||
/* Full packet accumulated, process it */
|
||||
|
||||
/*
|
||||
* Scroll wheel on IntelliMice, scroll buttons on NetMice
|
||||
*/
|
||||
|
||||
if (psmouse->type == PSMOUSE_IMPS || psmouse->type == PSMOUSE_GENPS)
|
||||
switch (psmouse->type) {
|
||||
case PSMOUSE_IMPS:
|
||||
/* IntelliMouse has scroll wheel */
|
||||
input_report_rel(dev, REL_WHEEL, -(signed char) packet[3]);
|
||||
break;
|
||||
|
||||
/*
|
||||
* Scroll wheel and buttons on IntelliMouse Explorer
|
||||
*/
|
||||
|
||||
if (psmouse->type == PSMOUSE_IMEX) {
|
||||
case PSMOUSE_IMEX:
|
||||
/* Scroll wheel and buttons on IntelliMouse Explorer */
|
||||
switch (packet[3] & 0xC0) {
|
||||
case 0x80: /* vertical scroll on IntelliMouse Explorer 4.0 */
|
||||
input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 32) - (int) (packet[3] & 31));
|
||||
@ -168,39 +162,42 @@ psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse)
|
||||
input_report_key(dev, BTN_EXTRA, (packet[3] >> 5) & 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
/*
|
||||
* Extra buttons on Genius NewNet 3D
|
||||
*/
|
||||
case PSMOUSE_GENPS:
|
||||
/* Report scroll buttons on NetMice */
|
||||
input_report_rel(dev, REL_WHEEL, -(signed char) packet[3]);
|
||||
|
||||
if (psmouse->type == PSMOUSE_GENPS) {
|
||||
/* Extra buttons on Genius NewNet 3D */
|
||||
input_report_key(dev, BTN_SIDE, (packet[0] >> 6) & 1);
|
||||
input_report_key(dev, BTN_EXTRA, (packet[0] >> 7) & 1);
|
||||
}
|
||||
break;
|
||||
|
||||
/*
|
||||
* Extra button on ThinkingMouse
|
||||
*/
|
||||
if (psmouse->type == PSMOUSE_THINKPS) {
|
||||
case PSMOUSE_THINKPS:
|
||||
/* Extra button on ThinkingMouse */
|
||||
input_report_key(dev, BTN_EXTRA, (packet[0] >> 3) & 1);
|
||||
/* Without this bit of weirdness moving up gives wildly high Y changes. */
|
||||
packet[1] |= (packet[0] & 0x40) << 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Cortron PS2 Trackball reports SIDE button on the 4th bit of the first
|
||||
* byte.
|
||||
* Without this bit of weirdness moving up gives wildly
|
||||
* high Y changes.
|
||||
*/
|
||||
packet[1] |= (packet[0] & 0x40) << 1;
|
||||
break;
|
||||
|
||||
case PSMOUSE_CORTRON:
|
||||
/*
|
||||
* Cortron PS2 Trackball reports SIDE button in the
|
||||
* 4th bit of the first byte.
|
||||
*/
|
||||
if (psmouse->type == PSMOUSE_CORTRON) {
|
||||
input_report_key(dev, BTN_SIDE, (packet[0] >> 3) & 1);
|
||||
packet[0] |= 0x08;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Generic PS/2 Mouse
|
||||
*/
|
||||
|
||||
/* Generic PS/2 Mouse */
|
||||
input_report_key(dev, BTN_LEFT, packet[0] & 1);
|
||||
input_report_key(dev, BTN_MIDDLE, (packet[0] >> 2) & 1);
|
||||
input_report_key(dev, BTN_RIGHT, (packet[0] >> 1) & 1);
|
||||
@ -222,7 +219,6 @@ void psmouse_queue_work(struct psmouse *psmouse, struct delayed_work *work,
|
||||
/*
|
||||
* __psmouse_set_state() sets new psmouse state and resets all flags.
|
||||
*/
|
||||
|
||||
static inline void __psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state)
|
||||
{
|
||||
psmouse->state = new_state;
|
||||
@ -231,13 +227,11 @@ static inline void __psmouse_set_state(struct psmouse *psmouse, enum psmouse_sta
|
||||
psmouse->last = jiffies;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* psmouse_set_state() sets new psmouse state and resets all flags and
|
||||
* counters while holding serio lock so fighting with interrupt handler
|
||||
* is not a concern.
|
||||
*/
|
||||
|
||||
void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state)
|
||||
{
|
||||
serio_pause_rx(psmouse->ps2dev.serio);
|
||||
@ -249,7 +243,6 @@ void psmouse_set_state(struct psmouse *psmouse, enum psmouse_state new_state)
|
||||
* psmouse_handle_byte() processes one byte of the input data stream
|
||||
* by calling corresponding protocol handler.
|
||||
*/
|
||||
|
||||
static int psmouse_handle_byte(struct psmouse *psmouse)
|
||||
{
|
||||
psmouse_ret_t rc = psmouse->protocol_handler(psmouse);
|
||||
@ -292,7 +285,6 @@ static int psmouse_handle_byte(struct psmouse *psmouse)
|
||||
* psmouse_interrupt() handles incoming characters, either passing them
|
||||
* for normal processing or gathering them as command response.
|
||||
*/
|
||||
|
||||
static irqreturn_t psmouse_interrupt(struct serio *serio,
|
||||
unsigned char data, unsigned int flags)
|
||||
{
|
||||
@ -335,9 +327,8 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
|
||||
}
|
||||
|
||||
psmouse->packet[psmouse->pktcnt++] = data;
|
||||
/*
|
||||
* Check if this is a new device announcement (0xAA 0x00)
|
||||
*/
|
||||
|
||||
/* Check if this is a new device announcement (0xAA 0x00) */
|
||||
if (unlikely(psmouse->packet[0] == PSMOUSE_RET_BAT && psmouse->pktcnt <= 2)) {
|
||||
if (psmouse->pktcnt == 1) {
|
||||
psmouse->last = jiffies;
|
||||
@ -351,9 +342,8 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
|
||||
serio_reconnect(serio);
|
||||
goto out;
|
||||
}
|
||||
/*
|
||||
* Not a new device, try processing first byte normally
|
||||
*/
|
||||
|
||||
/* Not a new device, try processing first byte normally */
|
||||
psmouse->pktcnt = 1;
|
||||
if (psmouse_handle_byte(psmouse))
|
||||
goto out;
|
||||
@ -362,7 +352,8 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
|
||||
}
|
||||
|
||||
/*
|
||||
* See if we need to force resync because mouse was idle for too long
|
||||
* See if we need to force resync because mouse was idle for
|
||||
* too long.
|
||||
*/
|
||||
if (psmouse->state == PSMOUSE_ACTIVATED &&
|
||||
psmouse->pktcnt == 1 && psmouse->resync_time &&
|
||||
@ -380,7 +371,6 @@ static irqreturn_t psmouse_interrupt(struct serio *serio,
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* psmouse_sliced_command() sends an extended PS/2 command to the mouse
|
||||
* using sliced syntax, understood by advanced devices, such as Logitech
|
||||
@ -404,7 +394,6 @@ int psmouse_sliced_command(struct psmouse *psmouse, unsigned char command)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* psmouse_reset() resets the mouse into power-on state.
|
||||
*/
|
||||
@ -424,7 +413,6 @@ int psmouse_reset(struct psmouse *psmouse)
|
||||
/*
|
||||
* Here we set the mouse resolution.
|
||||
*/
|
||||
|
||||
void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution)
|
||||
{
|
||||
static const unsigned char params[] = { 0, 1, 2, 2, 3 };
|
||||
@ -441,7 +429,6 @@ void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution)
|
||||
/*
|
||||
* Here we set the mouse report rate.
|
||||
*/
|
||||
|
||||
static void psmouse_set_rate(struct psmouse *psmouse, unsigned int rate)
|
||||
{
|
||||
static const unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10, 0 };
|
||||
@ -457,7 +444,6 @@ static void psmouse_set_rate(struct psmouse *psmouse, unsigned int rate)
|
||||
/*
|
||||
* Here we set the mouse scaling.
|
||||
*/
|
||||
|
||||
static void psmouse_set_scale(struct psmouse *psmouse, enum psmouse_scale scale)
|
||||
{
|
||||
ps2_command(&psmouse->ps2dev, NULL,
|
||||
@ -468,7 +454,6 @@ static void psmouse_set_scale(struct psmouse *psmouse, enum psmouse_scale scale)
|
||||
/*
|
||||
* psmouse_poll() - default poll handler. Everyone except for ALPS uses it.
|
||||
*/
|
||||
|
||||
static int psmouse_poll(struct psmouse *psmouse)
|
||||
{
|
||||
return ps2_command(&psmouse->ps2dev, psmouse->packet,
|
||||
@ -699,284 +684,6 @@ static int cortron_detect(struct psmouse *psmouse, bool set_properties)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Apply default settings to the psmouse structure. Most of them will
|
||||
* be overridden by individual protocol initialization routines.
|
||||
*/
|
||||
|
||||
static void psmouse_apply_defaults(struct psmouse *psmouse)
|
||||
{
|
||||
struct input_dev *input_dev = psmouse->dev;
|
||||
|
||||
memset(input_dev->evbit, 0, sizeof(input_dev->evbit));
|
||||
memset(input_dev->keybit, 0, sizeof(input_dev->keybit));
|
||||
memset(input_dev->relbit, 0, sizeof(input_dev->relbit));
|
||||
memset(input_dev->absbit, 0, sizeof(input_dev->absbit));
|
||||
memset(input_dev->mscbit, 0, sizeof(input_dev->mscbit));
|
||||
|
||||
__set_bit(EV_KEY, input_dev->evbit);
|
||||
__set_bit(EV_REL, input_dev->evbit);
|
||||
|
||||
__set_bit(BTN_LEFT, input_dev->keybit);
|
||||
__set_bit(BTN_RIGHT, input_dev->keybit);
|
||||
|
||||
__set_bit(REL_X, input_dev->relbit);
|
||||
__set_bit(REL_Y, input_dev->relbit);
|
||||
|
||||
__set_bit(INPUT_PROP_POINTER, input_dev->propbit);
|
||||
|
||||
psmouse->set_rate = psmouse_set_rate;
|
||||
psmouse->set_resolution = psmouse_set_resolution;
|
||||
psmouse->set_scale = psmouse_set_scale;
|
||||
psmouse->poll = psmouse_poll;
|
||||
psmouse->protocol_handler = psmouse_process_byte;
|
||||
psmouse->pktsize = 3;
|
||||
psmouse->reconnect = NULL;
|
||||
psmouse->disconnect = NULL;
|
||||
psmouse->cleanup = NULL;
|
||||
psmouse->pt_activate = NULL;
|
||||
psmouse->pt_deactivate = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Apply default settings to the psmouse structure and call specified
|
||||
* protocol detection or initialization routine.
|
||||
*/
|
||||
static int psmouse_do_detect(int (*detect)(struct psmouse *psmouse,
|
||||
bool set_properties),
|
||||
struct psmouse *psmouse, bool set_properties)
|
||||
{
|
||||
if (set_properties)
|
||||
psmouse_apply_defaults(psmouse);
|
||||
|
||||
return detect(psmouse, set_properties);
|
||||
}
|
||||
|
||||
/*
|
||||
* psmouse_extensions() probes for any extensions to the basic PS/2 protocol
|
||||
* the mouse may have.
|
||||
*/
|
||||
|
||||
static int psmouse_extensions(struct psmouse *psmouse,
|
||||
unsigned int max_proto, bool set_properties)
|
||||
{
|
||||
bool synaptics_hardware = false;
|
||||
|
||||
/* Always check for focaltech, this is safe as it uses pnp-id matching */
|
||||
if (psmouse_do_detect(focaltech_detect, psmouse, set_properties) == 0) {
|
||||
if (max_proto > PSMOUSE_IMEX) {
|
||||
if (!set_properties || focaltech_init(psmouse) == 0) {
|
||||
if (IS_ENABLED(CONFIG_MOUSE_PS2_FOCALTECH))
|
||||
return PSMOUSE_FOCALTECH;
|
||||
/*
|
||||
* Note that we need to also restrict
|
||||
* psmouse_max_proto so that psmouse_initialize()
|
||||
* does not try to reset rate and resolution,
|
||||
* because even that upsets the device.
|
||||
*/
|
||||
psmouse_max_proto = PSMOUSE_PS2;
|
||||
return PSMOUSE_PS2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We always check for lifebook because it does not disturb mouse
|
||||
* (it only checks DMI information).
|
||||
*/
|
||||
if (psmouse_do_detect(lifebook_detect, psmouse, set_properties) == 0) {
|
||||
if (max_proto > PSMOUSE_IMEX) {
|
||||
if (!set_properties || lifebook_init(psmouse) == 0)
|
||||
return PSMOUSE_LIFEBOOK;
|
||||
}
|
||||
}
|
||||
|
||||
if (psmouse_do_detect(vmmouse_detect, psmouse, set_properties) == 0) {
|
||||
if (max_proto > PSMOUSE_IMEX) {
|
||||
if (!set_properties || vmmouse_init(psmouse) == 0)
|
||||
return PSMOUSE_VMMOUSE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Try Kensington ThinkingMouse (we try first, because synaptics probe
|
||||
* upsets the thinkingmouse).
|
||||
*/
|
||||
|
||||
if (max_proto > PSMOUSE_IMEX &&
|
||||
psmouse_do_detect(thinking_detect, psmouse, set_properties) == 0) {
|
||||
return PSMOUSE_THINKPS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try Synaptics TouchPad. Note that probing is done even if Synaptics protocol
|
||||
* support is disabled in config - we need to know if it is synaptics so we
|
||||
* can reset it properly after probing for intellimouse.
|
||||
*/
|
||||
if (max_proto > PSMOUSE_PS2 &&
|
||||
psmouse_do_detect(synaptics_detect, psmouse, set_properties) == 0) {
|
||||
synaptics_hardware = true;
|
||||
|
||||
if (max_proto > PSMOUSE_IMEX) {
|
||||
/*
|
||||
* Try activating protocol, but check if support is enabled first, since
|
||||
* we try detecting Synaptics even when protocol is disabled.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_MOUSE_PS2_SYNAPTICS) &&
|
||||
(!set_properties || synaptics_init(psmouse) == 0)) {
|
||||
return PSMOUSE_SYNAPTICS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Some Synaptics touchpads can emulate extended protocols (like IMPS/2).
|
||||
* Unfortunately Logitech/Genius probes confuse some firmware versions so
|
||||
* we'll have to skip them.
|
||||
*/
|
||||
max_proto = PSMOUSE_IMEX;
|
||||
}
|
||||
/*
|
||||
* Make sure that touchpad is in relative mode, gestures (taps) are enabled
|
||||
*/
|
||||
synaptics_reset(psmouse);
|
||||
}
|
||||
|
||||
/*
|
||||
* Try Cypress Trackpad.
|
||||
* Must try it before Finger Sensing Pad because Finger Sensing Pad probe
|
||||
* upsets some modules of Cypress Trackpads.
|
||||
*/
|
||||
if (max_proto > PSMOUSE_IMEX &&
|
||||
cypress_detect(psmouse, set_properties) == 0) {
|
||||
if (IS_ENABLED(CONFIG_MOUSE_PS2_CYPRESS)) {
|
||||
if (cypress_init(psmouse) == 0)
|
||||
return PSMOUSE_CYPRESS;
|
||||
|
||||
/*
|
||||
* Finger Sensing Pad probe upsets some modules of
|
||||
* Cypress Trackpad, must avoid Finger Sensing Pad
|
||||
* probe if Cypress Trackpad device detected.
|
||||
*/
|
||||
return PSMOUSE_PS2;
|
||||
}
|
||||
|
||||
max_proto = PSMOUSE_IMEX;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try ALPS TouchPad
|
||||
*/
|
||||
if (max_proto > PSMOUSE_IMEX) {
|
||||
ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
|
||||
if (psmouse_do_detect(alps_detect,
|
||||
psmouse, set_properties) == 0) {
|
||||
if (!set_properties || alps_init(psmouse) == 0)
|
||||
return PSMOUSE_ALPS;
|
||||
/*
|
||||
* Init failed, try basic relative protocols
|
||||
*/
|
||||
max_proto = PSMOUSE_IMEX;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Try OLPC HGPK touchpad.
|
||||
*/
|
||||
if (max_proto > PSMOUSE_IMEX &&
|
||||
psmouse_do_detect(hgpk_detect, psmouse, set_properties) == 0) {
|
||||
if (!set_properties || hgpk_init(psmouse) == 0)
|
||||
return PSMOUSE_HGPK;
|
||||
/*
|
||||
* Init failed, try basic relative protocols
|
||||
*/
|
||||
max_proto = PSMOUSE_IMEX;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try Elantech touchpad.
|
||||
*/
|
||||
if (max_proto > PSMOUSE_IMEX &&
|
||||
psmouse_do_detect(elantech_detect, psmouse, set_properties) == 0) {
|
||||
if (!set_properties || elantech_init(psmouse) == 0)
|
||||
return PSMOUSE_ELANTECH;
|
||||
/*
|
||||
* Init failed, try basic relative protocols
|
||||
*/
|
||||
max_proto = PSMOUSE_IMEX;
|
||||
}
|
||||
|
||||
if (max_proto > PSMOUSE_IMEX) {
|
||||
if (psmouse_do_detect(genius_detect,
|
||||
psmouse, set_properties) == 0)
|
||||
return PSMOUSE_GENPS;
|
||||
|
||||
if (psmouse_do_detect(ps2pp_init,
|
||||
psmouse, set_properties) == 0)
|
||||
return PSMOUSE_PS2PP;
|
||||
|
||||
if (psmouse_do_detect(trackpoint_detect,
|
||||
psmouse, set_properties) == 0)
|
||||
return PSMOUSE_TRACKPOINT;
|
||||
|
||||
if (psmouse_do_detect(touchkit_ps2_detect,
|
||||
psmouse, set_properties) == 0)
|
||||
return PSMOUSE_TOUCHKIT_PS2;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try Finger Sensing Pad. We do it here because its probe upsets
|
||||
* Trackpoint devices (causing TP_READ_ID command to time out).
|
||||
*/
|
||||
if (max_proto > PSMOUSE_IMEX) {
|
||||
if (psmouse_do_detect(fsp_detect,
|
||||
psmouse, set_properties) == 0) {
|
||||
if (!set_properties || fsp_init(psmouse) == 0)
|
||||
return PSMOUSE_FSP;
|
||||
/*
|
||||
* Init failed, try basic relative protocols
|
||||
*/
|
||||
max_proto = PSMOUSE_IMEX;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset to defaults in case the device got confused by extended
|
||||
* protocol probes. Note that we follow up with full reset because
|
||||
* some mice put themselves to sleep when they see PSMOUSE_RESET_DIS.
|
||||
*/
|
||||
ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
|
||||
psmouse_reset(psmouse);
|
||||
|
||||
if (max_proto >= PSMOUSE_IMEX &&
|
||||
psmouse_do_detect(im_explorer_detect,
|
||||
psmouse, set_properties) == 0) {
|
||||
return PSMOUSE_IMEX;
|
||||
}
|
||||
|
||||
if (max_proto >= PSMOUSE_IMPS &&
|
||||
psmouse_do_detect(intellimouse_detect,
|
||||
psmouse, set_properties) == 0) {
|
||||
return PSMOUSE_IMPS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Okay, all failed, we have a standard mouse here. The number of the buttons
|
||||
* is still a question, though. We assume 3.
|
||||
*/
|
||||
psmouse_do_detect(ps2bare_detect, psmouse, set_properties);
|
||||
|
||||
if (synaptics_hardware) {
|
||||
/*
|
||||
* We detected Synaptics hardware but it did not respond to IMPS/2 probes.
|
||||
* We need to reset the touchpad because if there is a track point on the
|
||||
* pass through port it could get disabled while probing for protocol
|
||||
* extensions.
|
||||
*/
|
||||
psmouse_reset(psmouse);
|
||||
}
|
||||
|
||||
return PSMOUSE_PS2;
|
||||
}
|
||||
|
||||
static const struct psmouse_protocol psmouse_protocols[] = {
|
||||
{
|
||||
.type = PSMOUSE_PS2,
|
||||
@ -985,13 +692,14 @@ static const struct psmouse_protocol psmouse_protocols[] = {
|
||||
.maxproto = true,
|
||||
.ignore_parity = true,
|
||||
.detect = ps2bare_detect,
|
||||
.try_passthru = true,
|
||||
},
|
||||
#ifdef CONFIG_MOUSE_PS2_LOGIPS2PP
|
||||
{
|
||||
.type = PSMOUSE_PS2PP,
|
||||
.name = "PS2++",
|
||||
.alias = "logitech",
|
||||
.detect = ps2pp_init,
|
||||
.detect = ps2pp_detect,
|
||||
},
|
||||
#endif
|
||||
{
|
||||
@ -1022,6 +730,7 @@ static const struct psmouse_protocol psmouse_protocols[] = {
|
||||
.maxproto = true,
|
||||
.ignore_parity = true,
|
||||
.detect = intellimouse_detect,
|
||||
.try_passthru = true,
|
||||
},
|
||||
{
|
||||
.type = PSMOUSE_IMEX,
|
||||
@ -1030,6 +739,7 @@ static const struct psmouse_protocol psmouse_protocols[] = {
|
||||
.maxproto = true,
|
||||
.ignore_parity = true,
|
||||
.detect = im_explorer_detect,
|
||||
.try_passthru = true,
|
||||
},
|
||||
#ifdef CONFIG_MOUSE_PS2_SYNAPTICS
|
||||
{
|
||||
@ -1061,6 +771,7 @@ static const struct psmouse_protocol psmouse_protocols[] = {
|
||||
.type = PSMOUSE_LIFEBOOK,
|
||||
.name = "LBPS/2",
|
||||
.alias = "lifebook",
|
||||
.detect = lifebook_detect,
|
||||
.init = lifebook_init,
|
||||
},
|
||||
#endif
|
||||
@ -1070,6 +781,7 @@ static const struct psmouse_protocol psmouse_protocols[] = {
|
||||
.name = "TPPS/2",
|
||||
.alias = "trackpoint",
|
||||
.detect = trackpoint_detect,
|
||||
.try_passthru = true,
|
||||
},
|
||||
#endif
|
||||
#ifdef CONFIG_MOUSE_PS2_TOUCHKIT
|
||||
@ -1138,7 +850,7 @@ static const struct psmouse_protocol psmouse_protocols[] = {
|
||||
},
|
||||
};
|
||||
|
||||
static const struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type type)
|
||||
static const struct psmouse_protocol *__psmouse_protocol_by_type(enum psmouse_type type)
|
||||
{
|
||||
int i;
|
||||
|
||||
@ -1146,6 +858,17 @@ static const struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type
|
||||
if (psmouse_protocols[i].type == type)
|
||||
return &psmouse_protocols[i];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type type)
|
||||
{
|
||||
const struct psmouse_protocol *proto;
|
||||
|
||||
proto = __psmouse_protocol_by_type(type);
|
||||
if (proto)
|
||||
return proto;
|
||||
|
||||
WARN_ON(1);
|
||||
return &psmouse_protocols[0];
|
||||
}
|
||||
@ -1166,23 +889,288 @@ static const struct psmouse_protocol *psmouse_protocol_by_name(const char *name,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Apply default settings to the psmouse structure. Most of them will
|
||||
* be overridden by individual protocol initialization routines.
|
||||
*/
|
||||
static void psmouse_apply_defaults(struct psmouse *psmouse)
|
||||
{
|
||||
struct input_dev *input_dev = psmouse->dev;
|
||||
|
||||
memset(input_dev->evbit, 0, sizeof(input_dev->evbit));
|
||||
memset(input_dev->keybit, 0, sizeof(input_dev->keybit));
|
||||
memset(input_dev->relbit, 0, sizeof(input_dev->relbit));
|
||||
memset(input_dev->absbit, 0, sizeof(input_dev->absbit));
|
||||
memset(input_dev->mscbit, 0, sizeof(input_dev->mscbit));
|
||||
|
||||
__set_bit(EV_KEY, input_dev->evbit);
|
||||
__set_bit(EV_REL, input_dev->evbit);
|
||||
|
||||
__set_bit(BTN_LEFT, input_dev->keybit);
|
||||
__set_bit(BTN_RIGHT, input_dev->keybit);
|
||||
|
||||
__set_bit(REL_X, input_dev->relbit);
|
||||
__set_bit(REL_Y, input_dev->relbit);
|
||||
|
||||
__set_bit(INPUT_PROP_POINTER, input_dev->propbit);
|
||||
|
||||
psmouse->set_rate = psmouse_set_rate;
|
||||
psmouse->set_resolution = psmouse_set_resolution;
|
||||
psmouse->set_scale = psmouse_set_scale;
|
||||
psmouse->poll = psmouse_poll;
|
||||
psmouse->protocol_handler = psmouse_process_byte;
|
||||
psmouse->pktsize = 3;
|
||||
psmouse->reconnect = NULL;
|
||||
psmouse->disconnect = NULL;
|
||||
psmouse->cleanup = NULL;
|
||||
psmouse->pt_activate = NULL;
|
||||
psmouse->pt_deactivate = NULL;
|
||||
}
|
||||
|
||||
static bool psmouse_try_protocol(struct psmouse *psmouse,
|
||||
enum psmouse_type type,
|
||||
unsigned int *max_proto,
|
||||
bool set_properties, bool init_allowed)
|
||||
{
|
||||
const struct psmouse_protocol *proto;
|
||||
|
||||
proto = __psmouse_protocol_by_type(type);
|
||||
if (!proto)
|
||||
return false;
|
||||
|
||||
if (psmouse->ps2dev.serio->id.type == SERIO_PS_PSTHRU &&
|
||||
!proto->try_passthru) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (set_properties)
|
||||
psmouse_apply_defaults(psmouse);
|
||||
|
||||
if (proto->detect(psmouse, set_properties) != 0)
|
||||
return false;
|
||||
|
||||
if (set_properties && proto->init && init_allowed) {
|
||||
if (proto->init(psmouse) != 0) {
|
||||
/*
|
||||
* We detected device, but init failed. Adjust
|
||||
* max_proto so we only try standard protocols.
|
||||
*/
|
||||
if (*max_proto > PSMOUSE_IMEX)
|
||||
*max_proto = PSMOUSE_IMEX;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* psmouse_extensions() probes for any extensions to the basic PS/2 protocol
|
||||
* the mouse may have.
|
||||
*/
|
||||
static int psmouse_extensions(struct psmouse *psmouse,
|
||||
unsigned int max_proto, bool set_properties)
|
||||
{
|
||||
bool synaptics_hardware = false;
|
||||
|
||||
/*
|
||||
* Always check for focaltech, this is safe as it uses pnp-id
|
||||
* matching.
|
||||
*/
|
||||
if (psmouse_try_protocol(psmouse, PSMOUSE_FOCALTECH,
|
||||
&max_proto, set_properties, false)) {
|
||||
if (max_proto > PSMOUSE_IMEX &&
|
||||
IS_ENABLED(CONFIG_MOUSE_PS2_FOCALTECH) &&
|
||||
(!set_properties || focaltech_init(psmouse) == 0)) {
|
||||
return PSMOUSE_FOCALTECH;
|
||||
}
|
||||
/*
|
||||
* Restrict psmouse_max_proto so that psmouse_initialize()
|
||||
* does not try to reset rate and resolution, because even
|
||||
* that upsets the device.
|
||||
* This also causes us to basically fall through to basic
|
||||
* protocol detection, where we fully reset the mouse,
|
||||
* and set it up as bare PS/2 protocol device.
|
||||
*/
|
||||
psmouse_max_proto = max_proto = PSMOUSE_PS2;
|
||||
}
|
||||
|
||||
/*
|
||||
* We always check for LifeBook because it does not disturb mouse
|
||||
* (it only checks DMI information).
|
||||
*/
|
||||
if (psmouse_try_protocol(psmouse, PSMOUSE_LIFEBOOK, &max_proto,
|
||||
set_properties, max_proto > PSMOUSE_IMEX))
|
||||
return PSMOUSE_LIFEBOOK;
|
||||
|
||||
if (psmouse_try_protocol(psmouse, PSMOUSE_VMMOUSE, &max_proto,
|
||||
set_properties, max_proto > PSMOUSE_IMEX))
|
||||
return PSMOUSE_VMMOUSE;
|
||||
|
||||
/*
|
||||
* Try Kensington ThinkingMouse (we try first, because Synaptics
|
||||
* probe upsets the ThinkingMouse).
|
||||
*/
|
||||
if (max_proto > PSMOUSE_IMEX &&
|
||||
psmouse_try_protocol(psmouse, PSMOUSE_THINKPS, &max_proto,
|
||||
set_properties, true)) {
|
||||
return PSMOUSE_THINKPS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try Synaptics TouchPad. Note that probing is done even if
|
||||
* Synaptics protocol support is disabled in config - we need to
|
||||
* know if it is Synaptics so we can reset it properly after
|
||||
* probing for IntelliMouse.
|
||||
*/
|
||||
if (max_proto > PSMOUSE_PS2 &&
|
||||
psmouse_try_protocol(psmouse, PSMOUSE_SYNAPTICS, &max_proto,
|
||||
set_properties, false)) {
|
||||
synaptics_hardware = true;
|
||||
|
||||
if (max_proto > PSMOUSE_IMEX) {
|
||||
/*
|
||||
* Try activating protocol, but check if support is
|
||||
* enabled first, since we try detecting Synaptics
|
||||
* even when protocol is disabled.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_MOUSE_PS2_SYNAPTICS) &&
|
||||
(!set_properties || synaptics_init(psmouse) == 0)) {
|
||||
return PSMOUSE_SYNAPTICS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Some Synaptics touchpads can emulate extended
|
||||
* protocols (like IMPS/2). Unfortunately
|
||||
* Logitech/Genius probes confuse some firmware
|
||||
* versions so we'll have to skip them.
|
||||
*/
|
||||
max_proto = PSMOUSE_IMEX;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make sure that touchpad is in relative mode, gestures
|
||||
* (taps) are enabled.
|
||||
*/
|
||||
synaptics_reset(psmouse);
|
||||
}
|
||||
|
||||
/*
|
||||
* Try Cypress Trackpad. We must try it before Finger Sensing Pad
|
||||
* because Finger Sensing Pad probe upsets some modules of Cypress
|
||||
* Trackpads.
|
||||
*/
|
||||
if (max_proto > PSMOUSE_IMEX &&
|
||||
psmouse_try_protocol(psmouse, PSMOUSE_CYPRESS, &max_proto,
|
||||
set_properties, true)) {
|
||||
return PSMOUSE_CYPRESS;
|
||||
}
|
||||
|
||||
/* Try ALPS TouchPad */
|
||||
if (max_proto > PSMOUSE_IMEX) {
|
||||
ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
|
||||
if (psmouse_try_protocol(psmouse, PSMOUSE_ALPS,
|
||||
&max_proto, set_properties, true))
|
||||
return PSMOUSE_ALPS;
|
||||
}
|
||||
|
||||
/* Try OLPC HGPK touchpad */
|
||||
if (max_proto > PSMOUSE_IMEX &&
|
||||
psmouse_try_protocol(psmouse, PSMOUSE_HGPK, &max_proto,
|
||||
set_properties, true)) {
|
||||
return PSMOUSE_HGPK;
|
||||
}
|
||||
|
||||
/* Try Elantech touchpad */
|
||||
if (max_proto > PSMOUSE_IMEX &&
|
||||
psmouse_try_protocol(psmouse, PSMOUSE_ELANTECH,
|
||||
&max_proto, set_properties, true)) {
|
||||
return PSMOUSE_ELANTECH;
|
||||
}
|
||||
|
||||
if (max_proto > PSMOUSE_IMEX) {
|
||||
if (psmouse_try_protocol(psmouse, PSMOUSE_GENPS,
|
||||
&max_proto, set_properties, true))
|
||||
return PSMOUSE_GENPS;
|
||||
|
||||
if (psmouse_try_protocol(psmouse, PSMOUSE_PS2PP,
|
||||
&max_proto, set_properties, true))
|
||||
return PSMOUSE_PS2PP;
|
||||
|
||||
if (psmouse_try_protocol(psmouse, PSMOUSE_TRACKPOINT,
|
||||
&max_proto, set_properties, true))
|
||||
return PSMOUSE_TRACKPOINT;
|
||||
|
||||
if (psmouse_try_protocol(psmouse, PSMOUSE_TOUCHKIT_PS2,
|
||||
&max_proto, set_properties, true))
|
||||
return PSMOUSE_TOUCHKIT_PS2;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try Finger Sensing Pad. We do it here because its probe upsets
|
||||
* Trackpoint devices (causing TP_READ_ID command to time out).
|
||||
*/
|
||||
if (max_proto > PSMOUSE_IMEX &&
|
||||
psmouse_try_protocol(psmouse, PSMOUSE_FSP,
|
||||
&max_proto, set_properties, true)) {
|
||||
return PSMOUSE_FSP;
|
||||
}
|
||||
|
||||
/*
|
||||
* Reset to defaults in case the device got confused by extended
|
||||
* protocol probes. Note that we follow up with full reset because
|
||||
* some mice put themselves to sleep when they see PSMOUSE_RESET_DIS.
|
||||
*/
|
||||
ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_RESET_DIS);
|
||||
psmouse_reset(psmouse);
|
||||
|
||||
if (max_proto >= PSMOUSE_IMEX &&
|
||||
psmouse_try_protocol(psmouse, PSMOUSE_IMEX,
|
||||
&max_proto, set_properties, true)) {
|
||||
return PSMOUSE_IMEX;
|
||||
}
|
||||
|
||||
if (max_proto >= PSMOUSE_IMPS &&
|
||||
psmouse_try_protocol(psmouse, PSMOUSE_IMPS,
|
||||
&max_proto, set_properties, true)) {
|
||||
return PSMOUSE_IMPS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Okay, all failed, we have a standard mouse here. The number of
|
||||
* the buttons is still a question, though. We assume 3.
|
||||
*/
|
||||
psmouse_try_protocol(psmouse, PSMOUSE_PS2,
|
||||
&max_proto, set_properties, true);
|
||||
|
||||
if (synaptics_hardware) {
|
||||
/*
|
||||
* We detected Synaptics hardware but it did not respond to
|
||||
* IMPS/2 probes. We need to reset the touchpad because if
|
||||
* there is a track point on the pass through port it could
|
||||
* get disabled while probing for protocol extensions.
|
||||
*/
|
||||
psmouse_reset(psmouse);
|
||||
}
|
||||
|
||||
return PSMOUSE_PS2;
|
||||
}
|
||||
|
||||
/*
|
||||
* psmouse_probe() probes for a PS/2 mouse.
|
||||
*/
|
||||
|
||||
static int psmouse_probe(struct psmouse *psmouse)
|
||||
{
|
||||
struct ps2dev *ps2dev = &psmouse->ps2dev;
|
||||
unsigned char param[2];
|
||||
|
||||
/*
|
||||
* First, we check if it's a mouse. It should send 0x00 or 0x03
|
||||
* in case of an IntelliMouse in 4-byte mode or 0x04 for IM Explorer.
|
||||
* Sunrex K8561 IR Keyboard/Mouse reports 0xff on second and subsequent
|
||||
* ID queries, probably due to a firmware bug.
|
||||
* First, we check if it's a mouse. It should send 0x00 or 0x03 in
|
||||
* case of an IntelliMouse in 4-byte mode or 0x04 for IM Explorer.
|
||||
* Sunrex K8561 IR Keyboard/Mouse reports 0xff on second and
|
||||
* subsequent ID queries, probably due to a firmware bug.
|
||||
*/
|
||||
|
||||
param[0] = 0xa5;
|
||||
if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETID))
|
||||
return -1;
|
||||
@ -1192,9 +1180,9 @@ static int psmouse_probe(struct psmouse *psmouse)
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* Then we reset and disable the mouse so that it doesn't generate events.
|
||||
* Then we reset and disable the mouse so that it doesn't generate
|
||||
* events.
|
||||
*/
|
||||
|
||||
if (ps2_command(ps2dev, NULL, PSMOUSE_CMD_RESET_DIS))
|
||||
psmouse_warn(psmouse, "Failed to reset mouse on %s\n",
|
||||
ps2dev->serio->phys);
|
||||
@ -1205,13 +1193,11 @@ static int psmouse_probe(struct psmouse *psmouse)
|
||||
/*
|
||||
* psmouse_initialize() initializes the mouse to a sane state.
|
||||
*/
|
||||
|
||||
static void psmouse_initialize(struct psmouse *psmouse)
|
||||
{
|
||||
/*
|
||||
* We set the mouse report rate, resolution and scaling.
|
||||
*/
|
||||
|
||||
if (psmouse_max_proto != PSMOUSE_PS2) {
|
||||
psmouse->set_rate(psmouse, psmouse->rate);
|
||||
psmouse->set_resolution(psmouse, psmouse->resolution);
|
||||
@ -1222,7 +1208,6 @@ static void psmouse_initialize(struct psmouse *psmouse)
|
||||
/*
|
||||
* psmouse_activate() enables the mouse so that we get motion reports from it.
|
||||
*/
|
||||
|
||||
int psmouse_activate(struct psmouse *psmouse)
|
||||
{
|
||||
if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE)) {
|
||||
@ -1236,10 +1221,9 @@ int psmouse_activate(struct psmouse *psmouse)
|
||||
}
|
||||
|
||||
/*
|
||||
* psmouse_deactivate() puts the mouse into poll mode so that we don't get motion
|
||||
* reports from it unless we explicitly request it.
|
||||
* psmouse_deactivate() puts the mouse into poll mode so that we don't get
|
||||
* motion reports from it unless we explicitly request it.
|
||||
*/
|
||||
|
||||
int psmouse_deactivate(struct psmouse *psmouse)
|
||||
{
|
||||
if (ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_DISABLE)) {
|
||||
@ -1252,11 +1236,9 @@ int psmouse_deactivate(struct psmouse *psmouse)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* psmouse_resync() attempts to re-validate current protocol.
|
||||
*/
|
||||
|
||||
static void psmouse_resync(struct work_struct *work)
|
||||
{
|
||||
struct psmouse *parent = NULL, *psmouse =
|
||||
@ -1317,10 +1299,11 @@ static void psmouse_resync(struct work_struct *work)
|
||||
psmouse_set_state(psmouse, PSMOUSE_RESYNCING);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Now try to enable mouse. We try to do that even if poll failed and also
|
||||
* repeat our attempts 5 times, otherwise we may be left out with disabled
|
||||
* mouse.
|
||||
* Now try to enable mouse. We try to do that even if poll failed
|
||||
* and also repeat our attempts 5 times, otherwise we may be left
|
||||
* out with disabled mouse.
|
||||
*/
|
||||
for (i = 0; i < 5; i++) {
|
||||
if (!ps2_command(&psmouse->ps2dev, NULL, PSMOUSE_CMD_ENABLE)) {
|
||||
@ -1353,7 +1336,6 @@ static void psmouse_resync(struct work_struct *work)
|
||||
/*
|
||||
* psmouse_cleanup() resets the mouse into power-on state.
|
||||
*/
|
||||
|
||||
static void psmouse_cleanup(struct serio *serio)
|
||||
{
|
||||
struct psmouse *psmouse = serio_get_drvdata(serio);
|
||||
@ -1402,7 +1384,6 @@ static void psmouse_cleanup(struct serio *serio)
|
||||
/*
|
||||
* psmouse_disconnect() closes and frees.
|
||||
*/
|
||||
|
||||
static void psmouse_disconnect(struct serio *serio)
|
||||
{
|
||||
struct psmouse *psmouse, *parent = NULL;
|
||||
@ -1602,7 +1583,6 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv)
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
static int psmouse_reconnect(struct serio *serio)
|
||||
{
|
||||
struct psmouse *psmouse = serio_get_drvdata(serio);
|
||||
|
@ -257,6 +257,13 @@ static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = {
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S6230"),
|
||||
},
|
||||
},
|
||||
{
|
||||
/* Fujitsu Lifebook U745 */
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
|
||||
DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK U745"),
|
||||
},
|
||||
},
|
||||
{
|
||||
/* Fujitsu T70H */
|
||||
.matches = {
|
||||
|
@ -295,6 +295,16 @@ config TOUCHSCREEN_EGALAX
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called egalax_ts.
|
||||
|
||||
config TOUCHSCREEN_EGALAX_SERIAL
|
||||
tristate "EETI eGalax serial touchscreen"
|
||||
select SERIO
|
||||
help
|
||||
Say Y here to enable support for serial connected EETI
|
||||
eGalax touch panels.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called egalax_ts_serial.
|
||||
|
||||
config TOUCHSCREEN_FT6236
|
||||
tristate "FT6236 I2C touchscreen"
|
||||
depends on I2C
|
||||
@ -324,6 +334,7 @@ config TOUCHSCREEN_FUJITSU
|
||||
config TOUCHSCREEN_GOODIX
|
||||
tristate "Goodix I2C touchscreen"
|
||||
depends on I2C
|
||||
depends on GPIOLIB
|
||||
help
|
||||
Say Y here if you have the Goodix touchscreen (such as one
|
||||
installed in Onda v975w tablets) connected to your
|
||||
@ -927,6 +938,22 @@ config TOUCHSCREEN_TOUCHIT213
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called touchit213.
|
||||
|
||||
config TOUCHSCREEN_TS4800
|
||||
tristate "TS-4800 touchscreen"
|
||||
depends on HAS_IOMEM && OF
|
||||
select MFD_SYSCON
|
||||
select INPUT_POLLDEV
|
||||
help
|
||||
Say Y here if you have a touchscreen on a TS-4800 board.
|
||||
|
||||
On TS-4800, the touchscreen is not handled directly by Linux but by
|
||||
a companion FPGA.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called ts4800_ts.
|
||||
|
||||
config TOUCHSCREEN_TSC_SERIO
|
||||
tristate "TSC-10/25/40 serial touchscreen support"
|
||||
select SERIO
|
||||
|
@ -35,6 +35,7 @@ obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_ELAN) += elants_i2c.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_EGALAX_SERIAL) += egalax_ts_serial.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_FT6236) += ft6236.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_GOODIX) += goodix.o
|
||||
@ -68,6 +69,7 @@ obj-$(CONFIG_TOUCHSCREEN_TI_AM335X_TSC) += ti_am335x_tsc.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_TS4800) += ts4800-ts.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_TSC_SERIO) += tsc40.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_TSC200X_CORE) += tsc200x-core.o
|
||||
obj-$(CONFIG_TOUCHSCREEN_TSC2004) += tsc2004.o
|
||||
|
194
drivers/input/touchscreen/egalax_ts_serial.c
Normal file
194
drivers/input/touchscreen/egalax_ts_serial.c
Normal file
@ -0,0 +1,194 @@
|
||||
/*
|
||||
* EETI Egalax serial touchscreen driver
|
||||
*
|
||||
* Copyright (c) 2015 Zoltán Böszörményi <zboszor@pr.hu>
|
||||
*
|
||||
* based on the
|
||||
*
|
||||
* Hampshire serial touchscreen driver (Copyright (c) 2010 Adam Bennett)
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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/errno.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/serio.h>
|
||||
|
||||
#define DRIVER_DESC "EETI Egalax serial touchscreen driver"
|
||||
|
||||
/*
|
||||
* Definitions & global arrays.
|
||||
*/
|
||||
|
||||
#define EGALAX_FORMAT_MAX_LENGTH 6
|
||||
#define EGALAX_FORMAT_START_BIT BIT(7)
|
||||
#define EGALAX_FORMAT_PRESSURE_BIT BIT(6)
|
||||
#define EGALAX_FORMAT_TOUCH_BIT BIT(0)
|
||||
#define EGALAX_FORMAT_RESOLUTION_MASK 0x06
|
||||
|
||||
#define EGALAX_MIN_XC 0
|
||||
#define EGALAX_MAX_XC 0x4000
|
||||
#define EGALAX_MIN_YC 0
|
||||
#define EGALAX_MAX_YC 0x4000
|
||||
|
||||
/*
|
||||
* Per-touchscreen data.
|
||||
*/
|
||||
struct egalax {
|
||||
struct input_dev *input;
|
||||
struct serio *serio;
|
||||
int idx;
|
||||
u8 data[EGALAX_FORMAT_MAX_LENGTH];
|
||||
char phys[32];
|
||||
};
|
||||
|
||||
static void egalax_process_data(struct egalax *egalax)
|
||||
{
|
||||
struct input_dev *dev = egalax->input;
|
||||
u8 *data = egalax->data;
|
||||
u16 x, y;
|
||||
u8 shift;
|
||||
u8 mask;
|
||||
|
||||
shift = 3 - ((data[0] & EGALAX_FORMAT_RESOLUTION_MASK) >> 1);
|
||||
mask = 0xff >> (shift + 1);
|
||||
|
||||
x = (((u16)(data[1] & mask) << 7) | (data[2] & 0x7f)) << shift;
|
||||
y = (((u16)(data[3] & mask) << 7) | (data[4] & 0x7f)) << shift;
|
||||
|
||||
input_report_key(dev, BTN_TOUCH, data[0] & EGALAX_FORMAT_TOUCH_BIT);
|
||||
input_report_abs(dev, ABS_X, x);
|
||||
input_report_abs(dev, ABS_Y, y);
|
||||
input_sync(dev);
|
||||
}
|
||||
|
||||
static irqreturn_t egalax_interrupt(struct serio *serio,
|
||||
unsigned char data, unsigned int flags)
|
||||
{
|
||||
struct egalax *egalax = serio_get_drvdata(serio);
|
||||
int pkt_len;
|
||||
|
||||
egalax->data[egalax->idx++] = data;
|
||||
|
||||
if (likely(egalax->data[0] & EGALAX_FORMAT_START_BIT)) {
|
||||
pkt_len = egalax->data[0] & EGALAX_FORMAT_PRESSURE_BIT ? 6 : 5;
|
||||
if (pkt_len == egalax->idx) {
|
||||
egalax_process_data(egalax);
|
||||
egalax->idx = 0;
|
||||
}
|
||||
} else {
|
||||
dev_dbg(&serio->dev, "unknown/unsynchronized data: %x\n",
|
||||
egalax->data[0]);
|
||||
egalax->idx = 0;
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* egalax_connect() is the routine that is called when someone adds a
|
||||
* new serio device that supports egalax protocol and registers it as
|
||||
* an input device. This is usually accomplished using inputattach.
|
||||
*/
|
||||
static int egalax_connect(struct serio *serio, struct serio_driver *drv)
|
||||
{
|
||||
struct egalax *egalax;
|
||||
struct input_dev *input_dev;
|
||||
int error;
|
||||
|
||||
egalax = kzalloc(sizeof(struct egalax), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!egalax || !input_dev) {
|
||||
error = -ENOMEM;
|
||||
goto err_free_mem;
|
||||
}
|
||||
|
||||
egalax->serio = serio;
|
||||
egalax->input = input_dev;
|
||||
snprintf(egalax->phys, sizeof(egalax->phys),
|
||||
"%s/input0", serio->phys);
|
||||
|
||||
input_dev->name = "EETI eGalaxTouch Serial TouchScreen";
|
||||
input_dev->phys = egalax->phys;
|
||||
input_dev->id.bustype = BUS_RS232;
|
||||
input_dev->id.vendor = SERIO_EGALAX;
|
||||
input_dev->id.product = 0;
|
||||
input_dev->id.version = 0x0001;
|
||||
input_dev->dev.parent = &serio->dev;
|
||||
|
||||
input_set_capability(input_dev, EV_KEY, BTN_TOUCH);
|
||||
input_set_abs_params(input_dev, ABS_X,
|
||||
EGALAX_MIN_XC, EGALAX_MAX_XC, 0, 0);
|
||||
input_set_abs_params(input_dev, ABS_Y,
|
||||
EGALAX_MIN_YC, EGALAX_MAX_YC, 0, 0);
|
||||
|
||||
serio_set_drvdata(serio, egalax);
|
||||
|
||||
error = serio_open(serio, drv);
|
||||
if (error)
|
||||
goto err_reset_drvdata;
|
||||
|
||||
error = input_register_device(input_dev);
|
||||
if (error)
|
||||
goto err_close_serio;
|
||||
|
||||
return 0;
|
||||
|
||||
err_close_serio:
|
||||
serio_close(serio);
|
||||
err_reset_drvdata:
|
||||
serio_set_drvdata(serio, NULL);
|
||||
err_free_mem:
|
||||
input_free_device(input_dev);
|
||||
kfree(egalax);
|
||||
return error;
|
||||
}
|
||||
|
||||
static void egalax_disconnect(struct serio *serio)
|
||||
{
|
||||
struct egalax *egalax = serio_get_drvdata(serio);
|
||||
|
||||
serio_close(serio);
|
||||
serio_set_drvdata(serio, NULL);
|
||||
input_unregister_device(egalax->input);
|
||||
kfree(egalax);
|
||||
}
|
||||
|
||||
/*
|
||||
* The serio driver structure.
|
||||
*/
|
||||
|
||||
static const struct serio_device_id egalax_serio_ids[] = {
|
||||
{
|
||||
.type = SERIO_RS232,
|
||||
.proto = SERIO_EGALAX,
|
||||
.id = SERIO_ANY,
|
||||
.extra = SERIO_ANY,
|
||||
},
|
||||
{ 0 }
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(serio, egalax_serio_ids);
|
||||
|
||||
static struct serio_driver egalax_drv = {
|
||||
.driver = {
|
||||
.name = "egalax",
|
||||
},
|
||||
.description = DRIVER_DESC,
|
||||
.id_table = egalax_serio_ids,
|
||||
.interrupt = egalax_interrupt,
|
||||
.connect = egalax_connect,
|
||||
.disconnect = egalax_disconnect,
|
||||
};
|
||||
module_serio_driver(egalax_drv);
|
||||
|
||||
MODULE_AUTHOR("Zoltán Böszörményi <zboszor@pr.hu>");
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL v2");
|
@ -2,6 +2,7 @@
|
||||
* Driver for Goodix Touchscreens
|
||||
*
|
||||
* Copyright (c) 2014 Red Hat Inc.
|
||||
* Copyright (c) 2015 K. Merker <merker@debian.org>
|
||||
*
|
||||
* This code is based on gt9xx.c authored by andrew@goodix.com:
|
||||
*
|
||||
@ -16,6 +17,8 @@
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input/mt.h>
|
||||
@ -33,11 +36,24 @@ struct goodix_ts_data {
|
||||
struct input_dev *input_dev;
|
||||
int abs_x_max;
|
||||
int abs_y_max;
|
||||
bool swapped_x_y;
|
||||
bool inverted_x;
|
||||
bool inverted_y;
|
||||
unsigned int max_touch_num;
|
||||
unsigned int int_trigger_type;
|
||||
bool rotated_screen;
|
||||
int cfg_len;
|
||||
struct gpio_desc *gpiod_int;
|
||||
struct gpio_desc *gpiod_rst;
|
||||
u16 id;
|
||||
u16 version;
|
||||
const char *cfg_name;
|
||||
struct completion firmware_loading_complete;
|
||||
unsigned long irq_flags;
|
||||
};
|
||||
|
||||
#define GOODIX_GPIO_INT_NAME "irq"
|
||||
#define GOODIX_GPIO_RST_NAME "reset"
|
||||
|
||||
#define GOODIX_MAX_HEIGHT 4096
|
||||
#define GOODIX_MAX_WIDTH 4096
|
||||
#define GOODIX_INT_TRIGGER 1
|
||||
@ -45,8 +61,13 @@ struct goodix_ts_data {
|
||||
#define GOODIX_MAX_CONTACTS 10
|
||||
|
||||
#define GOODIX_CONFIG_MAX_LENGTH 240
|
||||
#define GOODIX_CONFIG_911_LENGTH 186
|
||||
#define GOODIX_CONFIG_967_LENGTH 228
|
||||
|
||||
/* Register defines */
|
||||
#define GOODIX_REG_COMMAND 0x8040
|
||||
#define GOODIX_CMD_SCREEN_OFF 0x05
|
||||
|
||||
#define GOODIX_READ_COOR_ADDR 0x814E
|
||||
#define GOODIX_REG_CONFIG_DATA 0x8047
|
||||
#define GOODIX_REG_ID 0x8140
|
||||
@ -115,6 +136,63 @@ static int goodix_i2c_read(struct i2c_client *client,
|
||||
return ret < 0 ? ret : (ret != ARRAY_SIZE(msgs) ? -EIO : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* goodix_i2c_write - write data to a register of the i2c slave device.
|
||||
*
|
||||
* @client: i2c device.
|
||||
* @reg: the register to write to.
|
||||
* @buf: raw data buffer to write.
|
||||
* @len: length of the buffer to write
|
||||
*/
|
||||
static int goodix_i2c_write(struct i2c_client *client, u16 reg, const u8 *buf,
|
||||
unsigned len)
|
||||
{
|
||||
u8 *addr_buf;
|
||||
struct i2c_msg msg;
|
||||
int ret;
|
||||
|
||||
addr_buf = kmalloc(len + 2, GFP_KERNEL);
|
||||
if (!addr_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
addr_buf[0] = reg >> 8;
|
||||
addr_buf[1] = reg & 0xFF;
|
||||
memcpy(&addr_buf[2], buf, len);
|
||||
|
||||
msg.flags = 0;
|
||||
msg.addr = client->addr;
|
||||
msg.buf = addr_buf;
|
||||
msg.len = len + 2;
|
||||
|
||||
ret = i2c_transfer(client->adapter, &msg, 1);
|
||||
kfree(addr_buf);
|
||||
return ret < 0 ? ret : (ret != 1 ? -EIO : 0);
|
||||
}
|
||||
|
||||
static int goodix_i2c_write_u8(struct i2c_client *client, u16 reg, u8 value)
|
||||
{
|
||||
return goodix_i2c_write(client, reg, &value, sizeof(value));
|
||||
}
|
||||
|
||||
static int goodix_get_cfg_len(u16 id)
|
||||
{
|
||||
switch (id) {
|
||||
case 911:
|
||||
case 9271:
|
||||
case 9110:
|
||||
case 927:
|
||||
case 928:
|
||||
return GOODIX_CONFIG_911_LENGTH;
|
||||
|
||||
case 912:
|
||||
case 967:
|
||||
return GOODIX_CONFIG_967_LENGTH;
|
||||
|
||||
default:
|
||||
return GOODIX_CONFIG_MAX_LENGTH;
|
||||
}
|
||||
}
|
||||
|
||||
static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data)
|
||||
{
|
||||
int touch_num;
|
||||
@ -155,10 +233,13 @@ static void goodix_ts_report_touch(struct goodix_ts_data *ts, u8 *coor_data)
|
||||
int input_y = get_unaligned_le16(&coor_data[3]);
|
||||
int input_w = get_unaligned_le16(&coor_data[5]);
|
||||
|
||||
if (ts->rotated_screen) {
|
||||
/* Inversions have to happen before axis swapping */
|
||||
if (ts->inverted_x)
|
||||
input_x = ts->abs_x_max - input_x;
|
||||
if (ts->inverted_y)
|
||||
input_y = ts->abs_y_max - input_y;
|
||||
}
|
||||
if (ts->swapped_x_y)
|
||||
swap(input_x, input_y);
|
||||
|
||||
input_mt_slot(ts->input_dev, id);
|
||||
input_mt_report_slot_state(ts->input_dev, MT_TOOL_FINGER, true);
|
||||
@ -202,21 +283,195 @@ static void goodix_process_events(struct goodix_ts_data *ts)
|
||||
*/
|
||||
static irqreturn_t goodix_ts_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
static const u8 end_cmd[] = {
|
||||
GOODIX_READ_COOR_ADDR >> 8,
|
||||
GOODIX_READ_COOR_ADDR & 0xff,
|
||||
0
|
||||
};
|
||||
struct goodix_ts_data *ts = dev_id;
|
||||
|
||||
goodix_process_events(ts);
|
||||
|
||||
if (i2c_master_send(ts->client, end_cmd, sizeof(end_cmd)) < 0)
|
||||
if (goodix_i2c_write_u8(ts->client, GOODIX_READ_COOR_ADDR, 0) < 0)
|
||||
dev_err(&ts->client->dev, "I2C write end_cmd error\n");
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void goodix_free_irq(struct goodix_ts_data *ts)
|
||||
{
|
||||
devm_free_irq(&ts->client->dev, ts->client->irq, ts);
|
||||
}
|
||||
|
||||
static int goodix_request_irq(struct goodix_ts_data *ts)
|
||||
{
|
||||
return devm_request_threaded_irq(&ts->client->dev, ts->client->irq,
|
||||
NULL, goodix_ts_irq_handler,
|
||||
ts->irq_flags, ts->client->name, ts);
|
||||
}
|
||||
|
||||
/**
|
||||
* goodix_check_cfg - Checks if config fw is valid
|
||||
*
|
||||
* @ts: goodix_ts_data pointer
|
||||
* @cfg: firmware config data
|
||||
*/
|
||||
static int goodix_check_cfg(struct goodix_ts_data *ts,
|
||||
const struct firmware *cfg)
|
||||
{
|
||||
int i, raw_cfg_len;
|
||||
u8 check_sum = 0;
|
||||
|
||||
if (cfg->size > GOODIX_CONFIG_MAX_LENGTH) {
|
||||
dev_err(&ts->client->dev,
|
||||
"The length of the config fw is not correct");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
raw_cfg_len = cfg->size - 2;
|
||||
for (i = 0; i < raw_cfg_len; i++)
|
||||
check_sum += cfg->data[i];
|
||||
check_sum = (~check_sum) + 1;
|
||||
if (check_sum != cfg->data[raw_cfg_len]) {
|
||||
dev_err(&ts->client->dev,
|
||||
"The checksum of the config fw is not correct");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (cfg->data[raw_cfg_len + 1] != 1) {
|
||||
dev_err(&ts->client->dev,
|
||||
"Config fw must have Config_Fresh register set");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* goodix_send_cfg - Write fw config to device
|
||||
*
|
||||
* @ts: goodix_ts_data pointer
|
||||
* @cfg: config firmware to write to device
|
||||
*/
|
||||
static int goodix_send_cfg(struct goodix_ts_data *ts,
|
||||
const struct firmware *cfg)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = goodix_check_cfg(ts, cfg);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = goodix_i2c_write(ts->client, GOODIX_REG_CONFIG_DATA, cfg->data,
|
||||
cfg->size);
|
||||
if (error) {
|
||||
dev_err(&ts->client->dev, "Failed to write config data: %d",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
dev_dbg(&ts->client->dev, "Config sent successfully.");
|
||||
|
||||
/* Let the firmware reconfigure itself, so sleep for 10ms */
|
||||
usleep_range(10000, 11000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int goodix_int_sync(struct goodix_ts_data *ts)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = gpiod_direction_output(ts->gpiod_int, 0);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
msleep(50); /* T5: 50ms */
|
||||
|
||||
error = gpiod_direction_input(ts->gpiod_int);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* goodix_reset - Reset device during power on
|
||||
*
|
||||
* @ts: goodix_ts_data pointer
|
||||
*/
|
||||
static int goodix_reset(struct goodix_ts_data *ts)
|
||||
{
|
||||
int error;
|
||||
|
||||
/* begin select I2C slave addr */
|
||||
error = gpiod_direction_output(ts->gpiod_rst, 0);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
msleep(20); /* T2: > 10ms */
|
||||
|
||||
/* HIGH: 0x28/0x29, LOW: 0xBA/0xBB */
|
||||
error = gpiod_direction_output(ts->gpiod_int, ts->client->addr == 0x14);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
usleep_range(100, 2000); /* T3: > 100us */
|
||||
|
||||
error = gpiod_direction_output(ts->gpiod_rst, 1);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
usleep_range(6000, 10000); /* T4: > 5ms */
|
||||
|
||||
/* end select I2C slave addr */
|
||||
error = gpiod_direction_input(ts->gpiod_rst);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = goodix_int_sync(ts);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* goodix_get_gpio_config - Get GPIO config from ACPI/DT
|
||||
*
|
||||
* @ts: goodix_ts_data pointer
|
||||
*/
|
||||
static int goodix_get_gpio_config(struct goodix_ts_data *ts)
|
||||
{
|
||||
int error;
|
||||
struct device *dev;
|
||||
struct gpio_desc *gpiod;
|
||||
|
||||
if (!ts->client)
|
||||
return -EINVAL;
|
||||
dev = &ts->client->dev;
|
||||
|
||||
/* Get the interrupt GPIO pin number */
|
||||
gpiod = devm_gpiod_get_optional(dev, GOODIX_GPIO_INT_NAME, GPIOD_IN);
|
||||
if (IS_ERR(gpiod)) {
|
||||
error = PTR_ERR(gpiod);
|
||||
if (error != -EPROBE_DEFER)
|
||||
dev_dbg(dev, "Failed to get %s GPIO: %d\n",
|
||||
GOODIX_GPIO_INT_NAME, error);
|
||||
return error;
|
||||
}
|
||||
|
||||
ts->gpiod_int = gpiod;
|
||||
|
||||
/* Get the reset line GPIO pin number */
|
||||
gpiod = devm_gpiod_get_optional(dev, GOODIX_GPIO_RST_NAME, GPIOD_IN);
|
||||
if (IS_ERR(gpiod)) {
|
||||
error = PTR_ERR(gpiod);
|
||||
if (error != -EPROBE_DEFER)
|
||||
dev_dbg(dev, "Failed to get %s GPIO: %d\n",
|
||||
GOODIX_GPIO_RST_NAME, error);
|
||||
return error;
|
||||
}
|
||||
|
||||
ts->gpiod_rst = gpiod;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* goodix_read_config - Read the embedded configuration of the panel
|
||||
*
|
||||
@ -230,14 +485,15 @@ static void goodix_read_config(struct goodix_ts_data *ts)
|
||||
int error;
|
||||
|
||||
error = goodix_i2c_read(ts->client, GOODIX_REG_CONFIG_DATA,
|
||||
config,
|
||||
GOODIX_CONFIG_MAX_LENGTH);
|
||||
config, ts->cfg_len);
|
||||
if (error) {
|
||||
dev_warn(&ts->client->dev,
|
||||
"Error reading config (%d), using defaults\n",
|
||||
error);
|
||||
ts->abs_x_max = GOODIX_MAX_WIDTH;
|
||||
ts->abs_y_max = GOODIX_MAX_HEIGHT;
|
||||
if (ts->swapped_x_y)
|
||||
swap(ts->abs_x_max, ts->abs_y_max);
|
||||
ts->int_trigger_type = GOODIX_INT_TRIGGER;
|
||||
ts->max_touch_num = GOODIX_MAX_CONTACTS;
|
||||
return;
|
||||
@ -245,6 +501,8 @@ static void goodix_read_config(struct goodix_ts_data *ts)
|
||||
|
||||
ts->abs_x_max = get_unaligned_le16(&config[RESOLUTION_LOC]);
|
||||
ts->abs_y_max = get_unaligned_le16(&config[RESOLUTION_LOC + 2]);
|
||||
if (ts->swapped_x_y)
|
||||
swap(ts->abs_x_max, ts->abs_y_max);
|
||||
ts->int_trigger_type = config[TRIGGER_LOC] & 0x03;
|
||||
ts->max_touch_num = config[MAX_CONTACTS_LOC] & 0x0f;
|
||||
if (!ts->abs_x_max || !ts->abs_y_max || !ts->max_touch_num) {
|
||||
@ -252,42 +510,45 @@ static void goodix_read_config(struct goodix_ts_data *ts)
|
||||
"Invalid config, using defaults\n");
|
||||
ts->abs_x_max = GOODIX_MAX_WIDTH;
|
||||
ts->abs_y_max = GOODIX_MAX_HEIGHT;
|
||||
if (ts->swapped_x_y)
|
||||
swap(ts->abs_x_max, ts->abs_y_max);
|
||||
ts->max_touch_num = GOODIX_MAX_CONTACTS;
|
||||
}
|
||||
|
||||
ts->rotated_screen = dmi_check_system(rotated_screen);
|
||||
if (ts->rotated_screen)
|
||||
if (dmi_check_system(rotated_screen)) {
|
||||
ts->inverted_x = true;
|
||||
ts->inverted_y = true;
|
||||
dev_dbg(&ts->client->dev,
|
||||
"Applying '180 degrees rotated screen' quirk\n");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* goodix_read_version - Read goodix touchscreen version
|
||||
*
|
||||
* @client: the i2c client
|
||||
* @version: output buffer containing the version on success
|
||||
* @id: output buffer containing the id on success
|
||||
* @ts: our goodix_ts_data pointer
|
||||
*/
|
||||
static int goodix_read_version(struct i2c_client *client, u16 *version, u16 *id)
|
||||
static int goodix_read_version(struct goodix_ts_data *ts)
|
||||
{
|
||||
int error;
|
||||
u8 buf[6];
|
||||
char id_str[5];
|
||||
|
||||
error = goodix_i2c_read(client, GOODIX_REG_ID, buf, sizeof(buf));
|
||||
error = goodix_i2c_read(ts->client, GOODIX_REG_ID, buf, sizeof(buf));
|
||||
if (error) {
|
||||
dev_err(&client->dev, "read version failed: %d\n", error);
|
||||
dev_err(&ts->client->dev, "read version failed: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
memcpy(id_str, buf, 4);
|
||||
id_str[4] = 0;
|
||||
if (kstrtou16(id_str, 10, id))
|
||||
*id = 0x1001;
|
||||
if (kstrtou16(id_str, 10, &ts->id))
|
||||
ts->id = 0x1001;
|
||||
|
||||
*version = get_unaligned_le16(&buf[4]);
|
||||
ts->version = get_unaligned_le16(&buf[4]);
|
||||
|
||||
dev_info(&client->dev, "ID %d, version: %04x\n", *id, *version);
|
||||
dev_info(&ts->client->dev, "ID %d, version: %04x\n", ts->id,
|
||||
ts->version);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -321,13 +582,10 @@ static int goodix_i2c_test(struct i2c_client *client)
|
||||
* goodix_request_input_dev - Allocate, populate and register the input device
|
||||
*
|
||||
* @ts: our goodix_ts_data pointer
|
||||
* @version: device firmware version
|
||||
* @id: device ID
|
||||
*
|
||||
* Must be called during probe
|
||||
*/
|
||||
static int goodix_request_input_dev(struct goodix_ts_data *ts, u16 version,
|
||||
u16 id)
|
||||
static int goodix_request_input_dev(struct goodix_ts_data *ts)
|
||||
{
|
||||
int error;
|
||||
|
||||
@ -351,8 +609,8 @@ static int goodix_request_input_dev(struct goodix_ts_data *ts, u16 version,
|
||||
ts->input_dev->phys = "input/ts";
|
||||
ts->input_dev->id.bustype = BUS_I2C;
|
||||
ts->input_dev->id.vendor = 0x0416;
|
||||
ts->input_dev->id.product = id;
|
||||
ts->input_dev->id.version = version;
|
||||
ts->input_dev->id.product = ts->id;
|
||||
ts->input_dev->id.version = ts->version;
|
||||
|
||||
error = input_register_device(ts->input_dev);
|
||||
if (error) {
|
||||
@ -364,13 +622,75 @@ static int goodix_request_input_dev(struct goodix_ts_data *ts, u16 version,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* goodix_configure_dev - Finish device initialization
|
||||
*
|
||||
* @ts: our goodix_ts_data pointer
|
||||
*
|
||||
* Must be called from probe to finish initialization of the device.
|
||||
* Contains the common initialization code for both devices that
|
||||
* declare gpio pins and devices that do not. It is either called
|
||||
* directly from probe or from request_firmware_wait callback.
|
||||
*/
|
||||
static int goodix_configure_dev(struct goodix_ts_data *ts)
|
||||
{
|
||||
int error;
|
||||
|
||||
ts->swapped_x_y = device_property_read_bool(&ts->client->dev,
|
||||
"touchscreen-swapped-x-y");
|
||||
ts->inverted_x = device_property_read_bool(&ts->client->dev,
|
||||
"touchscreen-inverted-x");
|
||||
ts->inverted_y = device_property_read_bool(&ts->client->dev,
|
||||
"touchscreen-inverted-y");
|
||||
|
||||
goodix_read_config(ts);
|
||||
|
||||
error = goodix_request_input_dev(ts);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
ts->irq_flags = goodix_irq_flags[ts->int_trigger_type] | IRQF_ONESHOT;
|
||||
error = goodix_request_irq(ts);
|
||||
if (error) {
|
||||
dev_err(&ts->client->dev, "request IRQ failed: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* goodix_config_cb - Callback to finish device init
|
||||
*
|
||||
* @ts: our goodix_ts_data pointer
|
||||
*
|
||||
* request_firmware_wait callback that finishes
|
||||
* initialization of the device.
|
||||
*/
|
||||
static void goodix_config_cb(const struct firmware *cfg, void *ctx)
|
||||
{
|
||||
struct goodix_ts_data *ts = ctx;
|
||||
int error;
|
||||
|
||||
if (cfg) {
|
||||
/* send device configuration to the firmware */
|
||||
error = goodix_send_cfg(ts, cfg);
|
||||
if (error)
|
||||
goto err_release_cfg;
|
||||
}
|
||||
|
||||
goodix_configure_dev(ts);
|
||||
|
||||
err_release_cfg:
|
||||
release_firmware(cfg);
|
||||
complete_all(&ts->firmware_loading_complete);
|
||||
}
|
||||
|
||||
static int goodix_ts_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct goodix_ts_data *ts;
|
||||
unsigned long irq_flags;
|
||||
int error;
|
||||
u16 version_info, id_info;
|
||||
|
||||
dev_dbg(&client->dev, "I2C Address: 0x%02x\n", client->addr);
|
||||
|
||||
@ -385,6 +705,20 @@ static int goodix_ts_probe(struct i2c_client *client,
|
||||
|
||||
ts->client = client;
|
||||
i2c_set_clientdata(client, ts);
|
||||
init_completion(&ts->firmware_loading_complete);
|
||||
|
||||
error = goodix_get_gpio_config(ts);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (ts->gpiod_int && ts->gpiod_rst) {
|
||||
/* reset the controller */
|
||||
error = goodix_reset(ts);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "Controller reset failed.\n");
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
error = goodix_i2c_test(client);
|
||||
if (error) {
|
||||
@ -392,30 +726,125 @@ static int goodix_ts_probe(struct i2c_client *client,
|
||||
return error;
|
||||
}
|
||||
|
||||
error = goodix_read_version(client, &version_info, &id_info);
|
||||
error = goodix_read_version(ts);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "Read version failed.\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
goodix_read_config(ts);
|
||||
ts->cfg_len = goodix_get_cfg_len(ts->id);
|
||||
|
||||
error = goodix_request_input_dev(ts, version_info, id_info);
|
||||
if (error)
|
||||
return error;
|
||||
if (ts->gpiod_int && ts->gpiod_rst) {
|
||||
/* update device config */
|
||||
ts->cfg_name = devm_kasprintf(&client->dev, GFP_KERNEL,
|
||||
"goodix_%d_cfg.bin", ts->id);
|
||||
if (!ts->cfg_name)
|
||||
return -ENOMEM;
|
||||
|
||||
irq_flags = goodix_irq_flags[ts->int_trigger_type] | IRQF_ONESHOT;
|
||||
error = devm_request_threaded_irq(&ts->client->dev, client->irq,
|
||||
NULL, goodix_ts_irq_handler,
|
||||
irq_flags, client->name, ts);
|
||||
error = request_firmware_nowait(THIS_MODULE, true, ts->cfg_name,
|
||||
&client->dev, GFP_KERNEL, ts,
|
||||
goodix_config_cb);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "request IRQ failed: %d\n", error);
|
||||
dev_err(&client->dev,
|
||||
"Failed to invoke firmware loader: %d\n",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
} else {
|
||||
error = goodix_configure_dev(ts);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int goodix_ts_remove(struct i2c_client *client)
|
||||
{
|
||||
struct goodix_ts_data *ts = i2c_get_clientdata(client);
|
||||
|
||||
if (ts->gpiod_int && ts->gpiod_rst)
|
||||
wait_for_completion(&ts->firmware_loading_complete);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused goodix_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct goodix_ts_data *ts = i2c_get_clientdata(client);
|
||||
int error;
|
||||
|
||||
/* We need gpio pins to suspend/resume */
|
||||
if (!ts->gpiod_int || !ts->gpiod_rst)
|
||||
return 0;
|
||||
|
||||
wait_for_completion(&ts->firmware_loading_complete);
|
||||
|
||||
/* Free IRQ as IRQ pin is used as output in the suspend sequence */
|
||||
goodix_free_irq(ts);
|
||||
|
||||
/* Output LOW on the INT pin for 5 ms */
|
||||
error = gpiod_direction_output(ts->gpiod_int, 0);
|
||||
if (error) {
|
||||
goodix_request_irq(ts);
|
||||
return error;
|
||||
}
|
||||
|
||||
usleep_range(5000, 6000);
|
||||
|
||||
error = goodix_i2c_write_u8(ts->client, GOODIX_REG_COMMAND,
|
||||
GOODIX_CMD_SCREEN_OFF);
|
||||
if (error) {
|
||||
dev_err(&ts->client->dev, "Screen off command failed\n");
|
||||
gpiod_direction_input(ts->gpiod_int);
|
||||
goodix_request_irq(ts);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
/*
|
||||
* The datasheet specifies that the interval between sending screen-off
|
||||
* command and wake-up should be longer than 58 ms. To avoid waking up
|
||||
* sooner, delay 58ms here.
|
||||
*/
|
||||
msleep(58);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused goodix_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct goodix_ts_data *ts = i2c_get_clientdata(client);
|
||||
int error;
|
||||
|
||||
if (!ts->gpiod_int || !ts->gpiod_rst)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Exit sleep mode by outputting HIGH level to INT pin
|
||||
* for 2ms~5ms.
|
||||
*/
|
||||
error = gpiod_direction_output(ts->gpiod_int, 1);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
usleep_range(2000, 5000);
|
||||
|
||||
error = goodix_int_sync(ts);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = goodix_request_irq(ts);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(goodix_pm_ops, goodix_suspend, goodix_resume);
|
||||
|
||||
static const struct i2c_device_id goodix_ts_id[] = {
|
||||
{ "GDIX1001:00", 0 },
|
||||
{ }
|
||||
@ -446,11 +875,13 @@ MODULE_DEVICE_TABLE(of, goodix_of_match);
|
||||
|
||||
static struct i2c_driver goodix_ts_driver = {
|
||||
.probe = goodix_ts_probe,
|
||||
.remove = goodix_ts_remove,
|
||||
.id_table = goodix_ts_id,
|
||||
.driver = {
|
||||
.name = "Goodix-TS",
|
||||
.acpi_match_table = ACPI_PTR(goodix_acpi_match),
|
||||
.of_match_table = of_match_ptr(goodix_of_match),
|
||||
.pm = &goodix_pm_ops,
|
||||
},
|
||||
};
|
||||
module_i2c_driver(goodix_ts_driver);
|
||||
|
@ -87,7 +87,7 @@ static void pcap_ts_read_xy(void *data, u16 res[2])
|
||||
|
||||
static void pcap_ts_work(struct work_struct *work)
|
||||
{
|
||||
struct delayed_work *dw = container_of(work, struct delayed_work, work);
|
||||
struct delayed_work *dw = to_delayed_work(work);
|
||||
struct pcap_ts *pcap_ts = container_of(dw, struct pcap_ts, work);
|
||||
u8 ch[2];
|
||||
|
||||
|
@ -38,6 +38,8 @@ struct pixcir_i2c_ts_data {
|
||||
struct input_dev *input;
|
||||
struct gpio_desc *gpio_attb;
|
||||
struct gpio_desc *gpio_reset;
|
||||
struct gpio_desc *gpio_enable;
|
||||
struct gpio_desc *gpio_wake;
|
||||
const struct pixcir_i2c_chip_data *chip;
|
||||
int max_fingers; /* Max fingers supported in this instance */
|
||||
bool running;
|
||||
@ -208,6 +210,11 @@ static int pixcir_set_power_mode(struct pixcir_i2c_ts_data *ts,
|
||||
struct device *dev = &ts->client->dev;
|
||||
int ret;
|
||||
|
||||
if (mode == PIXCIR_POWER_ACTIVE || mode == PIXCIR_POWER_IDLE) {
|
||||
if (ts->gpio_wake)
|
||||
gpiod_set_value_cansleep(ts->gpio_wake, 1);
|
||||
}
|
||||
|
||||
ret = i2c_smbus_read_byte_data(ts->client, PIXCIR_REG_POWER_MODE);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "%s: can't read reg 0x%x : %d\n",
|
||||
@ -228,6 +235,11 @@ static int pixcir_set_power_mode(struct pixcir_i2c_ts_data *ts,
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (mode == PIXCIR_POWER_HALT) {
|
||||
if (ts->gpio_wake)
|
||||
gpiod_set_value_cansleep(ts->gpio_wake, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -302,6 +314,11 @@ static int pixcir_start(struct pixcir_i2c_ts_data *ts)
|
||||
struct device *dev = &ts->client->dev;
|
||||
int error;
|
||||
|
||||
if (ts->gpio_enable) {
|
||||
gpiod_set_value_cansleep(ts->gpio_enable, 1);
|
||||
msleep(100);
|
||||
}
|
||||
|
||||
/* LEVEL_TOUCH interrupt with active low polarity */
|
||||
error = pixcir_set_int_mode(ts, PIXCIR_INT_LEVEL_TOUCH, 0);
|
||||
if (error) {
|
||||
@ -343,6 +360,9 @@ static int pixcir_stop(struct pixcir_i2c_ts_data *ts)
|
||||
/* Wait till running ISR is complete */
|
||||
synchronize_irq(ts->client->irq);
|
||||
|
||||
if (ts->gpio_enable)
|
||||
gpiod_set_value_cansleep(ts->gpio_enable, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -534,6 +554,27 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client,
|
||||
return error;
|
||||
}
|
||||
|
||||
tsdata->gpio_wake = devm_gpiod_get_optional(dev, "wake",
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(tsdata->gpio_wake)) {
|
||||
error = PTR_ERR(tsdata->gpio_wake);
|
||||
if (error != -EPROBE_DEFER)
|
||||
dev_err(dev, "Failed to get wake gpio: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
tsdata->gpio_enable = devm_gpiod_get_optional(dev, "enable",
|
||||
GPIOD_OUT_HIGH);
|
||||
if (IS_ERR(tsdata->gpio_enable)) {
|
||||
error = PTR_ERR(tsdata->gpio_enable);
|
||||
if (error != -EPROBE_DEFER)
|
||||
dev_err(dev, "Failed to get enable gpio: %d\n", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
if (tsdata->gpio_enable)
|
||||
msleep(100);
|
||||
|
||||
error = devm_request_threaded_irq(dev, client->irq, NULL, pixcir_ts_isr,
|
||||
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
||||
client->name, tsdata);
|
||||
|
@ -725,7 +725,7 @@ static int rohm_ts_load_firmware(struct i2c_client *client,
|
||||
break;
|
||||
|
||||
error = -EIO;
|
||||
} while (++retry >= FIRMWARE_RETRY_MAX);
|
||||
} while (++retry <= FIRMWARE_RETRY_MAX);
|
||||
|
||||
out:
|
||||
error2 = i2c_smbus_write_byte_data(client, INT_MASK, INT_ALL);
|
||||
|
@ -273,8 +273,6 @@ static irqreturn_t titsc_irq(int irq, void *dev)
|
||||
status = titsc_readl(ts_dev, REG_RAWIRQSTATUS);
|
||||
if (status & IRQENB_HW_PEN) {
|
||||
ts_dev->pen_down = true;
|
||||
titsc_writel(ts_dev, REG_IRQWAKEUP, 0x00);
|
||||
titsc_writel(ts_dev, REG_IRQCLR, IRQENB_HW_PEN);
|
||||
irqclr |= IRQENB_HW_PEN;
|
||||
}
|
||||
|
||||
|
216
drivers/input/touchscreen/ts4800-ts.c
Normal file
216
drivers/input/touchscreen/ts4800-ts.c
Normal file
@ -0,0 +1,216 @@
|
||||
/*
|
||||
* Touchscreen driver for the TS-4800 board
|
||||
*
|
||||
* Copyright (c) 2015 - Savoir-faire Linux
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/input-polldev.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
/* polling interval in ms */
|
||||
#define POLL_INTERVAL 3
|
||||
|
||||
#define DEBOUNCE_COUNT 1
|
||||
|
||||
/* sensor values are 12-bit wide */
|
||||
#define MAX_12BIT ((1 << 12) - 1)
|
||||
|
||||
#define PENDOWN_MASK 0x1
|
||||
|
||||
#define X_OFFSET 0x0
|
||||
#define Y_OFFSET 0x2
|
||||
|
||||
struct ts4800_ts {
|
||||
struct input_polled_dev *poll_dev;
|
||||
struct device *dev;
|
||||
char phys[32];
|
||||
|
||||
void __iomem *base;
|
||||
struct regmap *regmap;
|
||||
unsigned int reg;
|
||||
unsigned int bit;
|
||||
|
||||
bool pendown;
|
||||
int debounce;
|
||||
};
|
||||
|
||||
static void ts4800_ts_open(struct input_polled_dev *dev)
|
||||
{
|
||||
struct ts4800_ts *ts = dev->private;
|
||||
int ret;
|
||||
|
||||
ts->pendown = false;
|
||||
ts->debounce = DEBOUNCE_COUNT;
|
||||
|
||||
ret = regmap_update_bits(ts->regmap, ts->reg, ts->bit, ts->bit);
|
||||
if (ret)
|
||||
dev_warn(ts->dev, "Failed to enable touchscreen\n");
|
||||
}
|
||||
|
||||
static void ts4800_ts_close(struct input_polled_dev *dev)
|
||||
{
|
||||
struct ts4800_ts *ts = dev->private;
|
||||
int ret;
|
||||
|
||||
ret = regmap_update_bits(ts->regmap, ts->reg, ts->bit, 0);
|
||||
if (ret)
|
||||
dev_warn(ts->dev, "Failed to disable touchscreen\n");
|
||||
|
||||
}
|
||||
|
||||
static void ts4800_ts_poll(struct input_polled_dev *dev)
|
||||
{
|
||||
struct input_dev *input_dev = dev->input;
|
||||
struct ts4800_ts *ts = dev->private;
|
||||
u16 last_x = readw(ts->base + X_OFFSET);
|
||||
u16 last_y = readw(ts->base + Y_OFFSET);
|
||||
bool pendown = last_x & PENDOWN_MASK;
|
||||
|
||||
if (pendown) {
|
||||
if (ts->debounce) {
|
||||
ts->debounce--;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ts->pendown) {
|
||||
input_report_key(input_dev, BTN_TOUCH, 1);
|
||||
ts->pendown = true;
|
||||
}
|
||||
|
||||
last_x = ((~last_x) >> 4) & MAX_12BIT;
|
||||
last_y = ((~last_y) >> 4) & MAX_12BIT;
|
||||
|
||||
input_report_abs(input_dev, ABS_X, last_x);
|
||||
input_report_abs(input_dev, ABS_Y, last_y);
|
||||
input_sync(input_dev);
|
||||
} else if (ts->pendown) {
|
||||
ts->pendown = false;
|
||||
ts->debounce = DEBOUNCE_COUNT;
|
||||
input_report_key(input_dev, BTN_TOUCH, 0);
|
||||
input_sync(input_dev);
|
||||
}
|
||||
}
|
||||
|
||||
static int ts4800_parse_dt(struct platform_device *pdev,
|
||||
struct ts4800_ts *ts)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct device_node *syscon_np;
|
||||
u32 reg, bit;
|
||||
int error;
|
||||
|
||||
syscon_np = of_parse_phandle(np, "syscon", 0);
|
||||
if (!syscon_np) {
|
||||
dev_err(dev, "no syscon property\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
error = of_property_read_u32_index(np, "syscon", 1, ®);
|
||||
if (error < 0) {
|
||||
dev_err(dev, "no offset in syscon\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
ts->reg = reg;
|
||||
|
||||
error = of_property_read_u32_index(np, "syscon", 2, &bit);
|
||||
if (error < 0) {
|
||||
dev_err(dev, "no bit in syscon\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
ts->bit = BIT(bit);
|
||||
|
||||
ts->regmap = syscon_node_to_regmap(syscon_np);
|
||||
if (IS_ERR(ts->regmap)) {
|
||||
dev_err(dev, "cannot get parent's regmap\n");
|
||||
return PTR_ERR(ts->regmap);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ts4800_ts_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct input_polled_dev *poll_dev;
|
||||
struct ts4800_ts *ts;
|
||||
struct resource *res;
|
||||
int error;
|
||||
|
||||
ts = devm_kzalloc(&pdev->dev, sizeof(*ts), GFP_KERNEL);
|
||||
if (!ts)
|
||||
return -ENOMEM;
|
||||
|
||||
error = ts4800_parse_dt(pdev, ts);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
ts->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(ts->base))
|
||||
return PTR_ERR(ts->base);
|
||||
|
||||
poll_dev = devm_input_allocate_polled_device(&pdev->dev);
|
||||
if (!poll_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(&pdev->dev));
|
||||
ts->poll_dev = poll_dev;
|
||||
ts->dev = &pdev->dev;
|
||||
|
||||
poll_dev->private = ts;
|
||||
poll_dev->poll_interval = POLL_INTERVAL;
|
||||
poll_dev->open = ts4800_ts_open;
|
||||
poll_dev->close = ts4800_ts_close;
|
||||
poll_dev->poll = ts4800_ts_poll;
|
||||
|
||||
poll_dev->input->name = "TS-4800 Touchscreen";
|
||||
poll_dev->input->phys = ts->phys;
|
||||
|
||||
input_set_capability(poll_dev->input, EV_KEY, BTN_TOUCH);
|
||||
input_set_abs_params(poll_dev->input, ABS_X, 0, MAX_12BIT, 0, 0);
|
||||
input_set_abs_params(poll_dev->input, ABS_Y, 0, MAX_12BIT, 0, 0);
|
||||
|
||||
error = input_register_polled_device(poll_dev);
|
||||
if (error) {
|
||||
dev_err(&pdev->dev,
|
||||
"Unabled to register polled input device (%d)\n",
|
||||
error);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ts4800_ts_of_match[] = {
|
||||
{ .compatible = "technologic,ts4800-ts", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ts4800_ts_of_match);
|
||||
|
||||
static struct platform_driver ts4800_ts_driver = {
|
||||
.driver = {
|
||||
.name = "ts4800-ts",
|
||||
.of_match_table = ts4800_ts_of_match,
|
||||
},
|
||||
.probe = ts4800_ts_probe,
|
||||
};
|
||||
module_platform_driver(ts4800_ts_driver);
|
||||
|
||||
MODULE_AUTHOR("Damien Riegel <damien.riegel@savoirfairelinux.com>");
|
||||
MODULE_DESCRIPTION("TS-4800 Touchscreen Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:ts4800_ts");
|
@ -80,7 +80,8 @@ struct w8001_touch_query {
|
||||
*/
|
||||
|
||||
struct w8001 {
|
||||
struct input_dev *dev;
|
||||
struct input_dev *pen_dev;
|
||||
struct input_dev *touch_dev;
|
||||
struct serio *serio;
|
||||
struct completion cmd_done;
|
||||
int id;
|
||||
@ -95,7 +96,10 @@ struct w8001 {
|
||||
u16 max_touch_y;
|
||||
u16 max_pen_x;
|
||||
u16 max_pen_y;
|
||||
char name[64];
|
||||
char pen_name[64];
|
||||
char touch_name[64];
|
||||
int open_count;
|
||||
struct mutex mutex;
|
||||
};
|
||||
|
||||
static void parse_pen_data(u8 *data, struct w8001_coord *coord)
|
||||
@ -141,7 +145,7 @@ static void scale_touch_coordinates(struct w8001 *w8001,
|
||||
|
||||
static void parse_multi_touch(struct w8001 *w8001)
|
||||
{
|
||||
struct input_dev *dev = w8001->dev;
|
||||
struct input_dev *dev = w8001->touch_dev;
|
||||
unsigned char *data = w8001->data;
|
||||
unsigned int x, y;
|
||||
int i;
|
||||
@ -151,7 +155,6 @@ static void parse_multi_touch(struct w8001 *w8001)
|
||||
bool touch = data[0] & (1 << i);
|
||||
|
||||
input_mt_slot(dev, i);
|
||||
input_mt_report_slot_state(dev, MT_TOOL_FINGER, touch);
|
||||
if (touch) {
|
||||
x = (data[6 * i + 1] << 7) | data[6 * i + 2];
|
||||
y = (data[6 * i + 3] << 7) | data[6 * i + 4];
|
||||
@ -207,7 +210,7 @@ static void parse_touchquery(u8 *data, struct w8001_touch_query *query)
|
||||
|
||||
static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord)
|
||||
{
|
||||
struct input_dev *dev = w8001->dev;
|
||||
struct input_dev *dev = w8001->pen_dev;
|
||||
|
||||
/*
|
||||
* We have 1 bit for proximity (rdy) and 3 bits for tip, side,
|
||||
@ -233,11 +236,6 @@ static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord)
|
||||
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;
|
||||
@ -261,7 +259,7 @@ static void report_pen_events(struct w8001 *w8001, struct w8001_coord *coord)
|
||||
|
||||
static void report_single_touch(struct w8001 *w8001, struct w8001_coord *coord)
|
||||
{
|
||||
struct input_dev *dev = w8001->dev;
|
||||
struct input_dev *dev = w8001->touch_dev;
|
||||
unsigned int x = coord->x;
|
||||
unsigned int y = coord->y;
|
||||
|
||||
@ -271,7 +269,6 @@ static void report_single_touch(struct w8001 *w8001, struct w8001_coord *coord)
|
||||
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);
|
||||
|
||||
@ -369,22 +366,36 @@ static int w8001_command(struct w8001 *w8001, unsigned char command,
|
||||
static int w8001_open(struct input_dev *dev)
|
||||
{
|
||||
struct w8001 *w8001 = input_get_drvdata(dev);
|
||||
int err;
|
||||
|
||||
return w8001_command(w8001, W8001_CMD_START, false);
|
||||
err = mutex_lock_interruptible(&w8001->mutex);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (w8001->open_count++ == 0) {
|
||||
err = w8001_command(w8001, W8001_CMD_START, false);
|
||||
if (err)
|
||||
w8001->open_count--;
|
||||
}
|
||||
|
||||
mutex_unlock(&w8001->mutex);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void w8001_close(struct input_dev *dev)
|
||||
{
|
||||
struct w8001 *w8001 = input_get_drvdata(dev);
|
||||
|
||||
mutex_lock(&w8001->mutex);
|
||||
|
||||
if (--w8001->open_count == 0)
|
||||
w8001_command(w8001, W8001_CMD_STOP, false);
|
||||
|
||||
mutex_unlock(&w8001->mutex);
|
||||
}
|
||||
|
||||
static int w8001_setup(struct w8001 *w8001)
|
||||
static int w8001_detect(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);
|
||||
@ -393,19 +404,29 @@ 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));
|
||||
return 0;
|
||||
}
|
||||
|
||||
__set_bit(INPUT_PROP_DIRECT, dev->propbit);
|
||||
static int w8001_setup_pen(struct w8001 *w8001, char *basename,
|
||||
size_t basename_sz)
|
||||
{
|
||||
struct input_dev *dev = w8001->pen_dev;
|
||||
struct w8001_coord coord;
|
||||
int error;
|
||||
|
||||
/* penabled? */
|
||||
error = w8001_command(w8001, W8001_CMD_QUERY, true);
|
||||
if (!error) {
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
__set_bit(EV_KEY, dev->evbit);
|
||||
__set_bit(EV_ABS, dev->evbit);
|
||||
__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);
|
||||
__set_bit(INPUT_PROP_DIRECT, dev->propbit);
|
||||
|
||||
parse_pen_data(w8001->response, &coord);
|
||||
w8001->max_pen_x = coord.x;
|
||||
@ -420,20 +441,36 @@ 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));
|
||||
strlcat(basename, " Penabled", basename_sz);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int w8001_setup_touch(struct w8001 *w8001, char *basename,
|
||||
size_t basename_sz)
|
||||
{
|
||||
struct input_dev *dev = w8001->touch_dev;
|
||||
struct w8001_touch_query touch;
|
||||
int error;
|
||||
|
||||
|
||||
/* Touch enabled? */
|
||||
error = w8001_command(w8001, W8001_CMD_TOUCHQUERY, true);
|
||||
|
||||
if (error)
|
||||
return error;
|
||||
/*
|
||||
* Some non-touch devices may reply to the touch query. But their
|
||||
* second byte is empty, which indicates touch is not supported.
|
||||
*/
|
||||
if (!error && w8001->response[1]) {
|
||||
if (!w8001->response[1])
|
||||
return -ENXIO;
|
||||
|
||||
__set_bit(EV_KEY, dev->evbit);
|
||||
__set_bit(EV_ABS, dev->evbit);
|
||||
__set_bit(BTN_TOUCH, dev->keybit);
|
||||
__set_bit(BTN_TOOL_FINGER, dev->keybit);
|
||||
__set_bit(INPUT_PROP_DIRECT, dev->propbit);
|
||||
|
||||
parse_touchquery(w8001->response, &touch);
|
||||
w8001->max_touch_x = touch.x;
|
||||
@ -456,42 +493,56 @@ static int w8001_setup(struct w8001 *w8001)
|
||||
case 2:
|
||||
w8001->pktlen = W8001_PKTLEN_TOUCH93;
|
||||
w8001->id = 0x93;
|
||||
strlcat(w8001->name, " 1FG", sizeof(w8001->name));
|
||||
strlcat(basename, " 1FG", basename_sz);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
case 3:
|
||||
case 4:
|
||||
w8001->pktlen = W8001_PKTLEN_TOUCH9A;
|
||||
strlcat(w8001->name, " 1FG", sizeof(w8001->name));
|
||||
strlcat(basename, " 1FG", basename_sz);
|
||||
w8001->id = 0x9a;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
w8001->pktlen = W8001_PKTLEN_TOUCH2FG;
|
||||
|
||||
__set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
|
||||
input_mt_init_slots(dev, 2, 0);
|
||||
input_set_abs_params(dev, ABS_MT_POSITION_X,
|
||||
0, touch.x, 0, 0);
|
||||
input_set_abs_params(dev, ABS_MT_POSITION_Y,
|
||||
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));
|
||||
strlcat(basename, " 2FG", basename_sz);
|
||||
if (w8001->max_pen_x && w8001->max_pen_y)
|
||||
w8001->id = 0xE3;
|
||||
else
|
||||
w8001->id = 0xE2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
strlcat(w8001->name, " Touchscreen", sizeof(w8001->name));
|
||||
strlcat(basename, " Touchscreen", basename_sz);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void w8001_set_devdata(struct input_dev *dev, struct w8001 *w8001,
|
||||
struct serio *serio)
|
||||
{
|
||||
dev->phys = w8001->phys;
|
||||
dev->id.bustype = BUS_RS232;
|
||||
dev->id.product = w8001->id;
|
||||
dev->id.vendor = 0x056a;
|
||||
dev->id.version = 0x0100;
|
||||
dev->open = w8001_open;
|
||||
dev->close = w8001_close;
|
||||
|
||||
dev->dev.parent = &serio->dev;
|
||||
|
||||
input_set_drvdata(dev, w8001);
|
||||
}
|
||||
|
||||
/*
|
||||
* w8001_disconnect() is the opposite of w8001_connect()
|
||||
*/
|
||||
@ -502,7 +553,10 @@ static void w8001_disconnect(struct serio *serio)
|
||||
|
||||
serio_close(serio);
|
||||
|
||||
input_unregister_device(w8001->dev);
|
||||
if (w8001->pen_dev)
|
||||
input_unregister_device(w8001->pen_dev);
|
||||
if (w8001->touch_dev)
|
||||
input_unregister_device(w8001->touch_dev);
|
||||
kfree(w8001);
|
||||
|
||||
serio_set_drvdata(serio, NULL);
|
||||
@ -517,18 +571,23 @@ static void w8001_disconnect(struct serio *serio)
|
||||
static int w8001_connect(struct serio *serio, struct serio_driver *drv)
|
||||
{
|
||||
struct w8001 *w8001;
|
||||
struct input_dev *input_dev;
|
||||
int err;
|
||||
struct input_dev *input_dev_pen;
|
||||
struct input_dev *input_dev_touch;
|
||||
char basename[64];
|
||||
int err, err_pen, err_touch;
|
||||
|
||||
w8001 = kzalloc(sizeof(struct w8001), GFP_KERNEL);
|
||||
input_dev = input_allocate_device();
|
||||
if (!w8001 || !input_dev) {
|
||||
input_dev_pen = input_allocate_device();
|
||||
input_dev_touch = input_allocate_device();
|
||||
if (!w8001 || !input_dev_pen || !input_dev_touch) {
|
||||
err = -ENOMEM;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
w8001->serio = serio;
|
||||
w8001->dev = input_dev;
|
||||
w8001->pen_dev = input_dev_pen;
|
||||
w8001->touch_dev = input_dev_touch;
|
||||
mutex_init(&w8001->mutex);
|
||||
init_completion(&w8001->cmd_done);
|
||||
snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys);
|
||||
|
||||
@ -537,35 +596,67 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv)
|
||||
if (err)
|
||||
goto fail2;
|
||||
|
||||
err = w8001_setup(w8001);
|
||||
err = w8001_detect(w8001);
|
||||
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;
|
||||
/* For backwards-compatibility we compose the basename based on
|
||||
* capabilities and then just append the tool type
|
||||
*/
|
||||
strlcpy(basename, "Wacom Serial", sizeof(basename));
|
||||
|
||||
input_dev->open = w8001_open;
|
||||
input_dev->close = w8001_close;
|
||||
err_pen = w8001_setup_pen(w8001, basename, sizeof(basename));
|
||||
err_touch = w8001_setup_touch(w8001, basename, sizeof(basename));
|
||||
if (err_pen && err_touch) {
|
||||
err = -ENXIO;
|
||||
goto fail3;
|
||||
}
|
||||
|
||||
input_set_drvdata(input_dev, w8001);
|
||||
if (!err_pen) {
|
||||
strlcpy(w8001->pen_name, basename, sizeof(w8001->pen_name));
|
||||
strlcat(w8001->pen_name, " Pen", sizeof(w8001->pen_name));
|
||||
input_dev_pen->name = w8001->pen_name;
|
||||
|
||||
err = input_register_device(w8001->dev);
|
||||
w8001_set_devdata(input_dev_pen, w8001, serio);
|
||||
|
||||
err = input_register_device(w8001->pen_dev);
|
||||
if (err)
|
||||
goto fail3;
|
||||
} else {
|
||||
input_free_device(input_dev_pen);
|
||||
input_dev_pen = NULL;
|
||||
w8001->pen_dev = NULL;
|
||||
}
|
||||
|
||||
if (!err_touch) {
|
||||
strlcpy(w8001->touch_name, basename, sizeof(w8001->touch_name));
|
||||
strlcat(w8001->touch_name, " Finger",
|
||||
sizeof(w8001->touch_name));
|
||||
input_dev_touch->name = w8001->touch_name;
|
||||
|
||||
w8001_set_devdata(input_dev_touch, w8001, serio);
|
||||
|
||||
err = input_register_device(w8001->touch_dev);
|
||||
if (err)
|
||||
goto fail4;
|
||||
} else {
|
||||
input_free_device(input_dev_touch);
|
||||
input_dev_touch = NULL;
|
||||
w8001->touch_dev = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
fail4:
|
||||
if (w8001->pen_dev)
|
||||
input_unregister_device(w8001->pen_dev);
|
||||
fail3:
|
||||
serio_close(serio);
|
||||
fail2:
|
||||
serio_set_drvdata(serio, NULL);
|
||||
fail1:
|
||||
input_free_device(input_dev);
|
||||
input_free_device(input_dev_pen);
|
||||
input_free_device(input_dev_touch);
|
||||
kfree(w8001);
|
||||
return err;
|
||||
}
|
||||
|
@ -20,6 +20,11 @@
|
||||
* Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
|
||||
*
|
||||
* Changes/Revisions:
|
||||
* 0.5 08/13/2015 (David Herrmann <dh.herrmann@gmail.com> &
|
||||
* Benjamin Tissoires <benjamin.tissoires@redhat.com>)
|
||||
* - add UI_DEV_SETUP ioctl
|
||||
* - add UI_ABS_SETUP ioctl
|
||||
* - add UI_GET_VERSION ioctl
|
||||
* 0.4 01/09/2014 (Benjamin Tissoires <benjamin.tissoires@redhat.com>)
|
||||
* - add UI_GET_SYSNAME ioctl
|
||||
* 0.3 24/05/2006 (Anssi Hannula <anssi.hannulagmail.com>)
|
||||
|
@ -77,5 +77,6 @@
|
||||
#define SERIO_PS2MULT 0x3c
|
||||
#define SERIO_TSC40 0x3d
|
||||
#define SERIO_WACOM_IV 0x3e
|
||||
#define SERIO_EGALAX 0x3f
|
||||
|
||||
#endif /* _UAPI_SERIO_H */
|
||||
|
@ -20,6 +20,11 @@
|
||||
* Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
|
||||
*
|
||||
* Changes/Revisions:
|
||||
* 0.5 08/13/2015 (David Herrmann <dh.herrmann@gmail.com> &
|
||||
* Benjamin Tissoires <benjamin.tissoires@redhat.com>)
|
||||
* - add UI_DEV_SETUP ioctl
|
||||
* - add UI_ABS_SETUP ioctl
|
||||
* - add UI_GET_VERSION ioctl
|
||||
* 0.4 01/09/2014 (Benjamin Tissoires <benjamin.tissoires@redhat.com>)
|
||||
* - add UI_GET_SYSNAME ioctl
|
||||
* 0.3 24/05/2006 (Anssi Hannula <anssi.hannulagmail.com>)
|
||||
@ -37,8 +42,8 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/input.h>
|
||||
|
||||
#define UINPUT_VERSION 4
|
||||
|
||||
#define UINPUT_VERSION 5
|
||||
#define UINPUT_MAX_NAME_SIZE 80
|
||||
|
||||
struct uinput_ff_upload {
|
||||
__u32 request_id;
|
||||
@ -58,6 +63,76 @@ struct uinput_ff_erase {
|
||||
#define UI_DEV_CREATE _IO(UINPUT_IOCTL_BASE, 1)
|
||||
#define UI_DEV_DESTROY _IO(UINPUT_IOCTL_BASE, 2)
|
||||
|
||||
struct uinput_setup {
|
||||
struct input_id id;
|
||||
char name[UINPUT_MAX_NAME_SIZE];
|
||||
__u32 ff_effects_max;
|
||||
};
|
||||
|
||||
/**
|
||||
* UI_DEV_SETUP - Set device parameters for setup
|
||||
*
|
||||
* This ioctl sets parameters for the input device to be created. It
|
||||
* supersedes the old "struct uinput_user_dev" method, which wrote this data
|
||||
* via write(). To actually set the absolute axes UI_ABS_SETUP should be
|
||||
* used.
|
||||
*
|
||||
* The ioctl takes a "struct uinput_setup" object as argument. The fields of
|
||||
* this object are as follows:
|
||||
* id: See the description of "struct input_id". This field is
|
||||
* copied unchanged into the new device.
|
||||
* name: This is used unchanged as name for the new device.
|
||||
* ff_effects_max: This limits the maximum numbers of force-feedback effects.
|
||||
* See below for a description of FF with uinput.
|
||||
*
|
||||
* This ioctl can be called multiple times and will overwrite previous values.
|
||||
* If this ioctl fails with -EINVAL, it is recommended to use the old
|
||||
* "uinput_user_dev" method via write() as a fallback, in case you run on an
|
||||
* old kernel that does not support this ioctl.
|
||||
*
|
||||
* This ioctl may fail with -EINVAL if it is not supported or if you passed
|
||||
* incorrect values, -ENOMEM if the kernel runs out of memory or -EFAULT if the
|
||||
* passed uinput_setup object cannot be read/written.
|
||||
* If this call fails, partial data may have already been applied to the
|
||||
* internal device.
|
||||
*/
|
||||
#define UI_DEV_SETUP _IOW(UINPUT_IOCTL_BASE, 3, struct uinput_setup)
|
||||
|
||||
struct uinput_abs_setup {
|
||||
__u16 code; /* axis code */
|
||||
/* __u16 filler; */
|
||||
struct input_absinfo absinfo;
|
||||
};
|
||||
|
||||
/**
|
||||
* UI_ABS_SETUP - Set absolute axis information for the device to setup
|
||||
*
|
||||
* This ioctl sets one absolute axis information for the input device to be
|
||||
* created. It supersedes the old "struct uinput_user_dev" method, which wrote
|
||||
* part of this data and the content of UI_DEV_SETUP via write().
|
||||
*
|
||||
* The ioctl takes a "struct uinput_abs_setup" object as argument. The fields
|
||||
* of this object are as follows:
|
||||
* code: The corresponding input code associated with this axis
|
||||
* (ABS_X, ABS_Y, etc...)
|
||||
* absinfo: See "struct input_absinfo" for a description of this field.
|
||||
* This field is copied unchanged into the kernel for the
|
||||
* specified axis. If the axis is not enabled via
|
||||
* UI_SET_ABSBIT, this ioctl will enable it.
|
||||
*
|
||||
* This ioctl can be called multiple times and will overwrite previous values.
|
||||
* If this ioctl fails with -EINVAL, it is recommended to use the old
|
||||
* "uinput_user_dev" method via write() as a fallback, in case you run on an
|
||||
* old kernel that does not support this ioctl.
|
||||
*
|
||||
* This ioctl may fail with -EINVAL if it is not supported or if you passed
|
||||
* incorrect values, -ENOMEM if the kernel runs out of memory or -EFAULT if the
|
||||
* passed uinput_setup object cannot be read/written.
|
||||
* If this call fails, partial data may have already been applied to the
|
||||
* internal device.
|
||||
*/
|
||||
#define UI_ABS_SETUP _IOW(UINPUT_IOCTL_BASE, 4, struct uinput_abs_setup)
|
||||
|
||||
#define UI_SET_EVBIT _IOW(UINPUT_IOCTL_BASE, 100, int)
|
||||
#define UI_SET_KEYBIT _IOW(UINPUT_IOCTL_BASE, 101, int)
|
||||
#define UI_SET_RELBIT _IOW(UINPUT_IOCTL_BASE, 102, int)
|
||||
@ -144,7 +219,6 @@ struct uinput_ff_erase {
|
||||
#define UI_FF_UPLOAD 1
|
||||
#define UI_FF_ERASE 2
|
||||
|
||||
#define UINPUT_MAX_NAME_SIZE 80
|
||||
struct uinput_user_dev {
|
||||
char name[UINPUT_MAX_NAME_SIZE];
|
||||
struct input_id id;
|
||||
|
Loading…
Reference in New Issue
Block a user