mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-28 18:51:56 -04:00
Merge branch 'next' into for-linus
Prepare first round of input updates for 4.5 merge window.
This commit is contained in:
commit
009f773836
10081 changed files with 483936 additions and 241919 deletions
|
|
@ -150,7 +150,7 @@ static void walkera0701_irq_handler(void *handler_data)
|
|||
if (w->counter == 24) { /* full frame */
|
||||
walkera0701_parse_frame(w);
|
||||
w->counter = NO_SYNC;
|
||||
if (abs64(pulse_time - SYNC_PULSE) < RESERVE) /* new frame sync */
|
||||
if (abs(pulse_time - SYNC_PULSE) < RESERVE) /* new frame sync */
|
||||
w->counter = 0;
|
||||
} else {
|
||||
if ((pulse_time > (ANALOG_MIN_PULSE - RESERVE)
|
||||
|
|
@ -161,7 +161,7 @@ static void walkera0701_irq_handler(void *handler_data)
|
|||
} else
|
||||
w->counter = NO_SYNC;
|
||||
}
|
||||
} else if (abs64(pulse_time - SYNC_PULSE - BIN0_PULSE) <
|
||||
} else if (abs(pulse_time - SYNC_PULSE - BIN0_PULSE) <
|
||||
RESERVE + BIN1_PULSE - BIN0_PULSE) /* frame sync .. */
|
||||
w->counter = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -104,7 +104,6 @@ static int ad714x_spi_probe(struct spi_device *spi)
|
|||
static struct spi_driver ad714x_spi_driver = {
|
||||
.driver = {
|
||||
.name = "ad714x_captouch",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &ad714x_spi_pm,
|
||||
},
|
||||
.probe = ad714x_spi_probe,
|
||||
|
|
|
|||
|
|
@ -120,7 +120,6 @@ static SIMPLE_DEV_PM_OPS(adxl34x_spi_pm, adxl34x_spi_suspend,
|
|||
static struct spi_driver adxl34x_driver = {
|
||||
.driver = {
|
||||
.name = "adxl34x",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &adxl34x_spi_pm,
|
||||
},
|
||||
.probe = adxl34x_spi_probe,
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -24,6 +24,7 @@
|
|||
#include <linux/platform_device.h>
|
||||
#include <linux/i8042.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/suspend.h>
|
||||
|
||||
#include <asm/io.h>
|
||||
|
||||
|
|
@ -1170,7 +1171,8 @@ static int i8042_pm_suspend(struct device *dev)
|
|||
{
|
||||
int i;
|
||||
|
||||
i8042_controller_reset(true);
|
||||
if (pm_suspend_via_firmware())
|
||||
i8042_controller_reset(true);
|
||||
|
||||
/* Set up serio interrupts for system wakeup. */
|
||||
for (i = 0; i < I8042_NUM_PORTS; i++) {
|
||||
|
|
@ -1183,8 +1185,17 @@ static int i8042_pm_suspend(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int i8042_pm_resume_noirq(struct device *dev)
|
||||
{
|
||||
if (!pm_resume_via_firmware())
|
||||
i8042_interrupt(0, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i8042_pm_resume(struct device *dev)
|
||||
{
|
||||
bool force_reset;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < I8042_NUM_PORTS; i++) {
|
||||
|
|
@ -1195,11 +1206,21 @@ static int i8042_pm_resume(struct device *dev)
|
|||
}
|
||||
|
||||
/*
|
||||
* On resume from S2R we always try to reset the controller
|
||||
* to bring it in a sane state. (In case of S2D we expect
|
||||
* BIOS to reset the controller for us.)
|
||||
* If platform firmware was not going to be involved in suspend, we did
|
||||
* not restore the controller state to whatever it had been at boot
|
||||
* time, so we do not need to do anything.
|
||||
*/
|
||||
return i8042_controller_resume(true);
|
||||
if (!pm_suspend_via_firmware())
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* We only need to reset the controller if we are resuming after handing
|
||||
* off control to the platform firmware, otherwise we can simply restore
|
||||
* the mode.
|
||||
*/
|
||||
force_reset = pm_resume_via_firmware();
|
||||
|
||||
return i8042_controller_resume(force_reset);
|
||||
}
|
||||
|
||||
static int i8042_pm_thaw(struct device *dev)
|
||||
|
|
@ -1223,6 +1244,7 @@ static int i8042_pm_restore(struct device *dev)
|
|||
|
||||
static const struct dev_pm_ops i8042_pm_ops = {
|
||||
.suspend = i8042_pm_suspend,
|
||||
.resume_noirq = i8042_pm_resume_noirq,
|
||||
.resume = i8042_pm_resume,
|
||||
.thaw = i8042_pm_thaw,
|
||||
.poweroff = i8042_pm_reset,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -843,7 +843,6 @@ static SIMPLE_DEV_PM_OPS(ad7877_pm, ad7877_suspend, ad7877_resume);
|
|||
static struct spi_driver ad7877_driver = {
|
||||
.driver = {
|
||||
.name = "ad7877",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &ad7877_pm,
|
||||
},
|
||||
.probe = ad7877_probe,
|
||||
|
|
|
|||
|
|
@ -149,7 +149,6 @@ static int ad7879_spi_remove(struct spi_device *spi)
|
|||
static struct spi_driver ad7879_spi_driver = {
|
||||
.driver = {
|
||||
.name = "ad7879",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &ad7879_pm_ops,
|
||||
},
|
||||
.probe = ad7879_spi_probe,
|
||||
|
|
|
|||
|
|
@ -1498,7 +1498,6 @@ static int ads7846_remove(struct spi_device *spi)
|
|||
static struct spi_driver ads7846_driver = {
|
||||
.driver = {
|
||||
.name = "ads7846",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &ads7846_pm,
|
||||
.of_match_table = of_match_ptr(ads7846_dt_ids),
|
||||
},
|
||||
|
|
|
|||
|
|
@ -185,7 +185,6 @@ static int cyttsp4_spi_remove(struct spi_device *spi)
|
|||
static struct spi_driver cyttsp4_spi_driver = {
|
||||
.driver = {
|
||||
.name = CYTTSP4_SPI_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &cyttsp4_pm_ops,
|
||||
},
|
||||
.probe = cyttsp4_spi_probe,
|
||||
|
|
|
|||
|
|
@ -182,7 +182,6 @@ static int cyttsp_spi_remove(struct spi_device *spi)
|
|||
static struct spi_driver cyttsp_spi_driver = {
|
||||
.driver = {
|
||||
.name = CY_SPI_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &cyttsp_pm_ops,
|
||||
},
|
||||
.probe = cyttsp_spi_probe,
|
||||
|
|
|
|||
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 <[email protected]>");
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@
|
|||
#include <media/v4l2-device.h>
|
||||
#include <media/v4l2-dev.h>
|
||||
#include <media/v4l2-ioctl.h>
|
||||
#include <media/videobuf2-v4l2.h>
|
||||
#include <media/videobuf2-dma-sg.h>
|
||||
|
||||
/* read 512 bytes from endpoint 0x86 -> get header + blobs */
|
||||
|
|
@ -163,7 +164,7 @@ struct sur40_state {
|
|||
};
|
||||
|
||||
struct sur40_buffer {
|
||||
struct vb2_buffer vb;
|
||||
struct vb2_v4l2_buffer vb;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
|
|
@ -420,7 +421,7 @@ static void sur40_process_video(struct sur40_state *sur40)
|
|||
|
||||
dev_dbg(sur40->dev, "header acquired\n");
|
||||
|
||||
sgt = vb2_dma_sg_plane_desc(&new_buf->vb, 0);
|
||||
sgt = vb2_dma_sg_plane_desc(&new_buf->vb.vb2_buf, 0);
|
||||
|
||||
result = usb_sg_init(&sgr, sur40->usbdev,
|
||||
usb_rcvbulkpipe(sur40->usbdev, VIDEO_ENDPOINT), 0,
|
||||
|
|
@ -443,15 +444,15 @@ static void sur40_process_video(struct sur40_state *sur40)
|
|||
goto err_poll;
|
||||
|
||||
/* mark as finished */
|
||||
v4l2_get_timestamp(&new_buf->vb.v4l2_buf.timestamp);
|
||||
new_buf->vb.v4l2_buf.sequence = sur40->sequence++;
|
||||
new_buf->vb.v4l2_buf.field = V4L2_FIELD_NONE;
|
||||
vb2_buffer_done(&new_buf->vb, VB2_BUF_STATE_DONE);
|
||||
v4l2_get_timestamp(&new_buf->vb.timestamp);
|
||||
new_buf->vb.sequence = sur40->sequence++;
|
||||
new_buf->vb.field = V4L2_FIELD_NONE;
|
||||
vb2_buffer_done(&new_buf->vb.vb2_buf, VB2_BUF_STATE_DONE);
|
||||
dev_dbg(sur40->dev, "buffer marked done\n");
|
||||
return;
|
||||
|
||||
err_poll:
|
||||
vb2_buffer_done(&new_buf->vb, VB2_BUF_STATE_ERROR);
|
||||
vb2_buffer_done(&new_buf->vb.vb2_buf, VB2_BUF_STATE_ERROR);
|
||||
}
|
||||
|
||||
/* Initialize input device parameters. */
|
||||
|
|
@ -643,10 +644,11 @@ static void sur40_disconnect(struct usb_interface *interface)
|
|||
* minimum number: many DMA engines need a minimum of 2 buffers in the
|
||||
* queue and you need to have another available for userspace processing.
|
||||
*/
|
||||
static int sur40_queue_setup(struct vb2_queue *q, const struct v4l2_format *fmt,
|
||||
static int sur40_queue_setup(struct vb2_queue *q, const void *parg,
|
||||
unsigned int *nbuffers, unsigned int *nplanes,
|
||||
unsigned int sizes[], void *alloc_ctxs[])
|
||||
{
|
||||
const struct v4l2_format *fmt = parg;
|
||||
struct sur40_state *sur40 = vb2_get_drv_priv(q);
|
||||
|
||||
if (q->num_buffers + *nbuffers < 3)
|
||||
|
|
@ -701,7 +703,7 @@ static void return_all_buffers(struct sur40_state *sur40,
|
|||
|
||||
spin_lock(&sur40->qlock);
|
||||
list_for_each_entry_safe(buf, node, &sur40->buf_list, list) {
|
||||
vb2_buffer_done(&buf->vb, state);
|
||||
vb2_buffer_done(&buf->vb.vb2_buf, state);
|
||||
list_del(&buf->list);
|
||||
}
|
||||
spin_unlock(&sur40->qlock);
|
||||
|
|
|
|||
|
|
@ -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 <[email protected]>");
|
||||
MODULE_DESCRIPTION("TS-4800 Touchscreen Driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_ALIAS("platform:ts4800_ts");
|
||||
|
|
@ -75,7 +75,6 @@ static int tsc2005_remove(struct spi_device *spi)
|
|||
static struct spi_driver tsc2005_driver = {
|
||||
.driver = {
|
||||
.name = "tsc2005",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &tsc200x_pm_ops,
|
||||
},
|
||||
.probe = tsc2005_probe,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue