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,8 +385,71 @@ static int uinput_allocate_device(struct uinput_device *udev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int uinput_setup_device(struct uinput_device *udev,
|
||||
const char __user *buffer, size_t count)
|
||||
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;
|
||||
struct input_dev *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,8 +2628,8 @@ static int alps_set_protocol(struct psmouse *psmouse,
|
|||
priv->x_bits = 16;
|
||||
priv->y_bits = 12;
|
||||
|
||||
if (alps_probe_trackstick_v3(psmouse,
|
||||
ALPS_REG_BASE_RUSHMORE) < 0)
|
||||
if (alps_probe_trackstick_v3_v7(psmouse,
|
||||
ALPS_REG_BASE_RUSHMORE) < 0)
|
||||
priv->flags &= ~ALPS_DUALPOINT;
|
||||
|
||||
break;
|
||||
|
@ -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;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "request IRQ failed: %d\n", error);
|
||||
return error;
|
||||
error = request_firmware_nowait(THIS_MODULE, true, ts->cfg_name,
|
||||
&client->dev, GFP_KERNEL, ts,
|
||||
goodix_config_cb);
|
||||
if (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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
w8001_command(w8001, W8001_CMD_STOP, false);
|
||||
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,105 +404,145 @@ 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) {
|
||||
__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);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
parse_pen_data(w8001->response, &coord);
|
||||
w8001->max_pen_x = coord.x;
|
||||
w8001->max_pen_y = coord.y;
|
||||
__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);
|
||||
|
||||
input_set_abs_params(dev, ABS_X, 0, coord.x, 0, 0);
|
||||
input_set_abs_params(dev, ABS_Y, 0, coord.y, 0, 0);
|
||||
input_abs_set_res(dev, ABS_X, W8001_PEN_RESOLUTION);
|
||||
input_abs_set_res(dev, ABS_Y, W8001_PEN_RESOLUTION);
|
||||
input_set_abs_params(dev, ABS_PRESSURE, 0, coord.pen_pressure, 0, 0);
|
||||
if (coord.tilt_x && coord.tilt_y) {
|
||||
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));
|
||||
parse_pen_data(w8001->response, &coord);
|
||||
w8001->max_pen_x = coord.x;
|
||||
w8001->max_pen_y = coord.y;
|
||||
|
||||
input_set_abs_params(dev, ABS_X, 0, coord.x, 0, 0);
|
||||
input_set_abs_params(dev, ABS_Y, 0, coord.y, 0, 0);
|
||||
input_abs_set_res(dev, ABS_X, W8001_PEN_RESOLUTION);
|
||||
input_abs_set_res(dev, ABS_Y, W8001_PEN_RESOLUTION);
|
||||
input_set_abs_params(dev, ABS_PRESSURE, 0, coord.pen_pressure, 0, 0);
|
||||
if (coord.tilt_x && coord.tilt_y) {
|
||||
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(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]) {
|
||||
__set_bit(BTN_TOUCH, dev->keybit);
|
||||
__set_bit(BTN_TOOL_FINGER, dev->keybit);
|
||||
if (!w8001->response[1])
|
||||
return -ENXIO;
|
||||
|
||||
parse_touchquery(w8001->response, &touch);
|
||||
w8001->max_touch_x = touch.x;
|
||||
w8001->max_touch_y = touch.y;
|
||||
__set_bit(EV_KEY, dev->evbit);
|
||||
__set_bit(EV_ABS, dev->evbit);
|
||||
__set_bit(BTN_TOUCH, dev->keybit);
|
||||
__set_bit(INPUT_PROP_DIRECT, dev->propbit);
|
||||
|
||||
if (w8001->max_pen_x && w8001->max_pen_y) {
|
||||
/* if pen is supported scale to pen maximum */
|
||||
touch.x = w8001->max_pen_x;
|
||||
touch.y = w8001->max_pen_y;
|
||||
touch.panel_res = W8001_PEN_RESOLUTION;
|
||||
}
|
||||
parse_touchquery(w8001->response, &touch);
|
||||
w8001->max_touch_x = touch.x;
|
||||
w8001->max_touch_y = touch.y;
|
||||
|
||||
input_set_abs_params(dev, ABS_X, 0, touch.x, 0, 0);
|
||||
input_set_abs_params(dev, ABS_Y, 0, touch.y, 0, 0);
|
||||
input_abs_set_res(dev, ABS_X, touch.panel_res);
|
||||
input_abs_set_res(dev, ABS_Y, touch.panel_res);
|
||||
|
||||
switch (touch.sensor_id) {
|
||||
case 0:
|
||||
case 2:
|
||||
w8001->pktlen = W8001_PKTLEN_TOUCH93;
|
||||
w8001->id = 0x93;
|
||||
strlcat(w8001->name, " 1FG", sizeof(w8001->name));
|
||||
break;
|
||||
|
||||
case 1:
|
||||
case 3:
|
||||
case 4:
|
||||
w8001->pktlen = W8001_PKTLEN_TOUCH9A;
|
||||
strlcat(w8001->name, " 1FG", sizeof(w8001->name));
|
||||
w8001->id = 0x9a;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
w8001->pktlen = W8001_PKTLEN_TOUCH2FG;
|
||||
|
||||
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));
|
||||
if (w8001->max_pen_x && w8001->max_pen_y)
|
||||
w8001->id = 0xE3;
|
||||
else
|
||||
w8001->id = 0xE2;
|
||||
break;
|
||||
}
|
||||
if (w8001->max_pen_x && w8001->max_pen_y) {
|
||||
/* if pen is supported scale to pen maximum */
|
||||
touch.x = w8001->max_pen_x;
|
||||
touch.y = w8001->max_pen_y;
|
||||
touch.panel_res = W8001_PEN_RESOLUTION;
|
||||
}
|
||||
|
||||
strlcat(w8001->name, " Touchscreen", sizeof(w8001->name));
|
||||
input_set_abs_params(dev, ABS_X, 0, touch.x, 0, 0);
|
||||
input_set_abs_params(dev, ABS_Y, 0, touch.y, 0, 0);
|
||||
input_abs_set_res(dev, ABS_X, touch.panel_res);
|
||||
input_abs_set_res(dev, ABS_Y, touch.panel_res);
|
||||
|
||||
switch (touch.sensor_id) {
|
||||
case 0:
|
||||
case 2:
|
||||
w8001->pktlen = W8001_PKTLEN_TOUCH93;
|
||||
w8001->id = 0x93;
|
||||
strlcat(basename, " 1FG", basename_sz);
|
||||
break;
|
||||
|
||||
case 1:
|
||||
case 3:
|
||||
case 4:
|
||||
w8001->pktlen = W8001_PKTLEN_TOUCH9A;
|
||||
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);
|
||||
|
||||
strlcat(basename, " 2FG", basename_sz);
|
||||
if (w8001->max_pen_x && w8001->max_pen_y)
|
||||
w8001->id = 0xE3;
|
||||
else
|
||||
w8001->id = 0xE2;
|
||||
break;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
input_set_drvdata(input_dev, w8001);
|
||||
|
||||
err = input_register_device(w8001->dev);
|
||||
if (err)
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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