diff options
Diffstat (limited to 'drivers/input')
57 files changed, 9527 insertions, 554 deletions
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index a35532ec0..6261874c0 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -201,6 +201,8 @@ source "drivers/input/touchscreen/Kconfig" source "drivers/input/misc/Kconfig" +source "drivers/input/rmi4/Kconfig" + endif menu "Hardware I/O ports" diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 0c9302ca9..595820bba 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -26,3 +26,5 @@ obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/ obj-$(CONFIG_INPUT_MISC) += misc/ obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o + +obj-$(CONFIG_RMI4_CORE) += rmi4/ diff --git a/drivers/input/input-compat.c b/drivers/input/input-compat.c index 64ca7113f..d84d20b9c 100644 --- a/drivers/input/input-compat.c +++ b/drivers/input/input-compat.c @@ -17,7 +17,7 @@ int input_event_from_user(const char __user *buffer, struct input_event *event) { - if (INPUT_COMPAT_TEST && !COMPAT_USE_64BIT_TIME) { + if (in_compat_syscall() && !COMPAT_USE_64BIT_TIME) { struct input_event_compat compat_event; if (copy_from_user(&compat_event, buffer, @@ -41,7 +41,7 @@ int input_event_from_user(const char __user *buffer, int input_event_to_user(char __user *buffer, const struct input_event *event) { - if (INPUT_COMPAT_TEST && !COMPAT_USE_64BIT_TIME) { + if (in_compat_syscall() && !COMPAT_USE_64BIT_TIME) { struct input_event_compat compat_event; compat_event.time.tv_sec = event->time.tv_sec; @@ -65,7 +65,7 @@ int input_event_to_user(char __user *buffer, int input_ff_effect_from_user(const char __user *buffer, size_t size, struct ff_effect *effect) { - if (INPUT_COMPAT_TEST) { + if (in_compat_syscall()) { struct ff_effect_compat *compat_effect; if (size != sizeof(struct ff_effect_compat)) diff --git a/drivers/input/input-compat.h b/drivers/input/input-compat.h index 148f66fe3..1563160a7 100644 --- a/drivers/input/input-compat.h +++ b/drivers/input/input-compat.h @@ -17,18 +17,6 @@ #ifdef CONFIG_COMPAT -/* Note to the author of this code: did it ever occur to - you why the ifdefs are needed? Think about it again. -AK */ -#if defined(CONFIG_X86_64) || defined(CONFIG_TILE) -# define INPUT_COMPAT_TEST is_compat_task() -#elif defined(CONFIG_S390) -# define INPUT_COMPAT_TEST test_thread_flag(TIF_31BIT) -#elif defined(CONFIG_MIPS) -# define INPUT_COMPAT_TEST test_thread_flag(TIF_32BIT_ADDR) -#else -# define INPUT_COMPAT_TEST test_thread_flag(TIF_32BIT) -#endif - struct input_event_compat { struct compat_timeval time; __u16 type; @@ -67,7 +55,7 @@ struct ff_effect_compat { static inline size_t input_event_size(void) { - return (INPUT_COMPAT_TEST && !COMPAT_USE_64BIT_TIME) ? + return (in_compat_syscall() && !COMPAT_USE_64BIT_TIME) ? sizeof(struct input_event_compat) : sizeof(struct input_event); } diff --git a/drivers/input/input.c b/drivers/input/input.c index 880605959..b87ffbd45 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -1015,7 +1015,7 @@ static int input_bits_to_string(char *buf, int buf_size, { int len = 0; - if (INPUT_COMPAT_TEST) { + if (in_compat_syscall()) { u32 dword = bits >> 32; if (dword || !skip_empty) len += snprintf(buf, buf_size, "%x ", dword); diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c index e8a84d12b..ca62a6e11 100644 --- a/drivers/input/joystick/xpad.c +++ b/drivers/input/joystick/xpad.c @@ -153,6 +153,7 @@ static const struct xpad_device { { 0x0738, 0x4728, "Mad Catz Street Fighter IV FightPad", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, { 0x0738, 0x4738, "Mad Catz Wired Xbox 360 Controller (SFIV)", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOX360 }, { 0x0738, 0x4740, "Mad Catz Beat Pad", 0, XTYPE_XBOX360 }, + { 0x0738, 0x4a01, "Mad Catz FightStick TE 2", MAP_TRIGGERS_TO_BUTTONS, XTYPE_XBOXONE }, { 0x0738, 0x6040, "Mad Catz Beat Pad Pro", MAP_DPAD_TO_BUTTONS, XTYPE_XBOX }, { 0x0738, 0xb726, "Mad Catz Xbox controller - MW2", 0, XTYPE_XBOX360 }, { 0x0738, 0xbeef, "Mad Catz JOYTECH NEO SE Advanced GamePad", XTYPE_XBOX360 }, @@ -304,6 +305,7 @@ static struct usb_device_id xpad_table[] = { XPAD_XBOX360_VENDOR(0x046d), /* Logitech X-Box 360 style controllers */ XPAD_XBOX360_VENDOR(0x0738), /* Mad Catz X-Box 360 controllers */ { USB_DEVICE(0x0738, 0x4540) }, /* Mad Catz Beat Pad */ + XPAD_XBOXONE_VENDOR(0x0738), /* Mad Catz FightStick TE 2 */ XPAD_XBOX360_VENDOR(0x0e6f), /* 0x0e6f X-Box 360 controllers */ XPAD_XBOX360_VENDOR(0x12ab), /* X-Box 360 dance pads */ XPAD_XBOX360_VENDOR(0x1430), /* RedOctane X-Box 360 controllers */ @@ -455,6 +457,10 @@ static void xpad_process_packet(struct usb_xpad *xpad, u16 cmd, unsigned char *d static void xpad360_process_packet(struct usb_xpad *xpad, struct input_dev *dev, u16 cmd, unsigned char *data) { + /* valid pad data */ + if (data[0] != 0x00) + return; + /* digital pad */ if (xpad->mapping & MAP_DPAD_TO_BUTTONS) { /* dpad as buttons (left, right, up, down) */ @@ -754,6 +760,7 @@ static bool xpad_prepare_next_out_packet(struct usb_xpad *xpad) if (packet) { memcpy(xpad->odata, packet->data, packet->len); xpad->irq_out->transfer_buffer_length = packet->len; + packet->pending = false; return true; } @@ -795,7 +802,6 @@ static void xpad_irq_out(struct urb *urb) switch (status) { case 0: /* success */ - xpad->out_packets[xpad->last_out_packet].pending = false; xpad->irq_out_active = xpad_prepare_next_out_packet(xpad); break; diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index ddd8148d5..509608c95 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -560,7 +560,7 @@ config KEYBOARD_SUNKBD config KEYBOARD_SH_KEYSC tristate "SuperH KEYSC keypad support" - depends on SUPERH || ARCH_SHMOBILE || COMPILE_TEST + depends on ARCH_SHMOBILE || COMPILE_TEST help Say Y here if you want to use a keypad attached to the KEYSC block on SuperH processors such as sh7722 and sh7343. diff --git a/drivers/input/keyboard/goldfish_events.c b/drivers/input/keyboard/goldfish_events.c index 907e4e278..f6e643b58 100644 --- a/drivers/input/keyboard/goldfish_events.c +++ b/drivers/input/keyboard/goldfish_events.c @@ -22,6 +22,7 @@ #include <linux/slab.h> #include <linux/irq.h> #include <linux/io.h> +#include <linux/acpi.h> enum { REG_READ = 0x00, @@ -178,10 +179,26 @@ static int events_probe(struct platform_device *pdev) return 0; } +static const struct of_device_id goldfish_events_of_match[] = { + { .compatible = "google,goldfish-events-keypad", }, + {}, +}; +MODULE_DEVICE_TABLE(of, goldfish_events_of_match); + +#ifdef CONFIG_ACPI +static const struct acpi_device_id goldfish_events_acpi_match[] = { + { "GFSH0002", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(acpi, goldfish_events_acpi_match); +#endif + static struct platform_driver events_driver = { .probe = events_probe, .driver = { .name = "goldfish_events", + .of_match_table = goldfish_events_of_match, + .acpi_match_table = ACPI_PTR(goldfish_events_acpi_match), }, }; diff --git a/drivers/input/keyboard/snvs_pwrkey.c b/drivers/input/keyboard/snvs_pwrkey.c index 9adf13a58..24a9f5990 100644 --- a/drivers/input/keyboard/snvs_pwrkey.c +++ b/drivers/input/keyboard/snvs_pwrkey.c @@ -111,9 +111,9 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev) return -ENOMEM; pdata->snvs = syscon_regmap_lookup_by_phandle(np, "regmap"); - if (!pdata->snvs) { + if (IS_ERR(pdata->snvs)) { dev_err(&pdev->dev, "Can't get snvs syscon\n"); - return -ENODEV; + return PTR_ERR(pdata->snvs); } if (of_property_read_u32(np, "linux,keycode", &pdata->keycode)) { @@ -180,7 +180,7 @@ static int imx_snvs_pwrkey_probe(struct platform_device *pdev) return 0; } -static int imx_snvs_pwrkey_suspend(struct device *dev) +static int __maybe_unused imx_snvs_pwrkey_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev); @@ -191,7 +191,7 @@ static int imx_snvs_pwrkey_suspend(struct device *dev) return 0; } -static int imx_snvs_pwrkey_resume(struct device *dev) +static int __maybe_unused imx_snvs_pwrkey_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct pwrkey_drv_data *pdata = platform_get_drvdata(pdev); diff --git a/drivers/input/keyboard/spear-keyboard.c b/drivers/input/keyboard/spear-keyboard.c index 623d45176..8083eaa05 100644 --- a/drivers/input/keyboard/spear-keyboard.c +++ b/drivers/input/keyboard/spear-keyboard.c @@ -288,8 +288,7 @@ static int spear_kbd_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM -static int spear_kbd_suspend(struct device *dev) +static int __maybe_unused spear_kbd_suspend(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct spear_kbd *kbd = platform_get_drvdata(pdev); @@ -342,7 +341,7 @@ static int spear_kbd_suspend(struct device *dev) return 0; } -static int spear_kbd_resume(struct device *dev) +static int __maybe_unused spear_kbd_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct spear_kbd *kbd = platform_get_drvdata(pdev); @@ -368,7 +367,6 @@ static int spear_kbd_resume(struct device *dev) return 0; } -#endif static SIMPLE_DEV_PM_OPS(spear_kbd_pm_ops, spear_kbd_suspend, spear_kbd_resume); diff --git a/drivers/input/misc/arizona-haptics.c b/drivers/input/misc/arizona-haptics.c index d5994a745..982936334 100644 --- a/drivers/input/misc/arizona-haptics.c +++ b/drivers/input/misc/arizona-haptics.c @@ -178,7 +178,6 @@ static int arizona_haptics_probe(struct platform_device *pdev) input_set_drvdata(haptics->input_dev, haptics); haptics->input_dev->name = "arizona:haptics"; - haptics->input_dev->dev.parent = pdev->dev.parent; haptics->input_dev->close = arizona_haptics_close; __set_bit(FF_RUMBLE, haptics->input_dev->ffbit); diff --git a/drivers/input/misc/max8997_haptic.c b/drivers/input/misc/max8997_haptic.c index a806ba381..8d6326d7e 100644 --- a/drivers/input/misc/max8997_haptic.c +++ b/drivers/input/misc/max8997_haptic.c @@ -255,12 +255,14 @@ static int max8997_haptic_probe(struct platform_device *pdev) struct max8997_dev *iodev = dev_get_drvdata(pdev->dev.parent); const struct max8997_platform_data *pdata = dev_get_platdata(iodev->dev); - const struct max8997_haptic_platform_data *haptic_pdata = - pdata->haptic_pdata; + const struct max8997_haptic_platform_data *haptic_pdata = NULL; struct max8997_haptic *chip; struct input_dev *input_dev; int error; + if (pdata) + haptic_pdata = pdata->haptic_pdata; + if (!haptic_pdata) { dev_err(&pdev->dev, "no haptic platform data\n"); return -EINVAL; diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c index f2261ab54..18663d4ed 100644 --- a/drivers/input/misc/pwm-beeper.c +++ b/drivers/input/misc/pwm-beeper.c @@ -20,21 +20,40 @@ #include <linux/platform_device.h> #include <linux/pwm.h> #include <linux/slab.h> +#include <linux/workqueue.h> struct pwm_beeper { struct input_dev *input; struct pwm_device *pwm; + struct work_struct work; unsigned long period; }; #define HZ_TO_NANOSECONDS(x) (1000000000UL/(x)) +static void __pwm_beeper_set(struct pwm_beeper *beeper) +{ + unsigned long period = beeper->period; + + if (period) { + pwm_config(beeper->pwm, period / 2, period); + pwm_enable(beeper->pwm); + } else + pwm_disable(beeper->pwm); +} + +static void pwm_beeper_work(struct work_struct *work) +{ + struct pwm_beeper *beeper = + container_of(work, struct pwm_beeper, work); + + __pwm_beeper_set(beeper); +} + static int pwm_beeper_event(struct input_dev *input, unsigned int type, unsigned int code, int value) { - int ret = 0; struct pwm_beeper *beeper = input_get_drvdata(input); - unsigned long period; if (type != EV_SND || value < 0) return -EINVAL; @@ -49,22 +68,31 @@ static int pwm_beeper_event(struct input_dev *input, return -EINVAL; } - if (value == 0) { - pwm_disable(beeper->pwm); - } else { - period = HZ_TO_NANOSECONDS(value); - ret = pwm_config(beeper->pwm, period / 2, period); - if (ret) - return ret; - ret = pwm_enable(beeper->pwm); - if (ret) - return ret; - beeper->period = period; - } + if (value == 0) + beeper->period = 0; + else + beeper->period = HZ_TO_NANOSECONDS(value); + + schedule_work(&beeper->work); return 0; } +static void pwm_beeper_stop(struct pwm_beeper *beeper) +{ + cancel_work_sync(&beeper->work); + + if (beeper->period) + pwm_disable(beeper->pwm); +} + +static void pwm_beeper_close(struct input_dev *input) +{ + struct pwm_beeper *beeper = input_get_drvdata(input); + + pwm_beeper_stop(beeper); +} + static int pwm_beeper_probe(struct platform_device *pdev) { unsigned long pwm_id = (unsigned long)dev_get_platdata(&pdev->dev); @@ -87,6 +115,8 @@ static int pwm_beeper_probe(struct platform_device *pdev) goto err_free; } + INIT_WORK(&beeper->work, pwm_beeper_work); + beeper->input = input_allocate_device(); if (!beeper->input) { dev_err(&pdev->dev, "Failed to allocate input device\n"); @@ -106,6 +136,7 @@ static int pwm_beeper_probe(struct platform_device *pdev) beeper->input->sndbit[0] = BIT(SND_TONE) | BIT(SND_BELL); beeper->input->event = pwm_beeper_event; + beeper->input->close = pwm_beeper_close; input_set_drvdata(beeper->input, beeper); @@ -135,7 +166,6 @@ static int pwm_beeper_remove(struct platform_device *pdev) input_unregister_device(beeper->input); - pwm_disable(beeper->pwm); pwm_free(beeper->pwm); kfree(beeper); @@ -147,8 +177,7 @@ static int __maybe_unused pwm_beeper_suspend(struct device *dev) { struct pwm_beeper *beeper = dev_get_drvdata(dev); - if (beeper->period) - pwm_disable(beeper->pwm); + pwm_beeper_stop(beeper); return 0; } @@ -157,10 +186,8 @@ static int __maybe_unused pwm_beeper_resume(struct device *dev) { struct pwm_beeper *beeper = dev_get_drvdata(dev); - if (beeper->period) { - pwm_config(beeper->pwm, beeper->period / 2, beeper->period); - pwm_enable(beeper->pwm); - } + if (beeper->period) + __pwm_beeper_set(beeper); return 0; } diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c index 8aee71986..96c486de4 100644 --- a/drivers/input/misc/rotary_encoder.c +++ b/drivers/input/misc/rotary_encoder.c @@ -20,70 +20,78 @@ #include <linux/input.h> #include <linux/device.h> #include <linux/platform_device.h> -#include <linux/gpio.h> -#include <linux/rotary_encoder.h> +#include <linux/gpio/consumer.h> #include <linux/slab.h> #include <linux/of.h> -#include <linux/of_platform.h> -#include <linux/of_gpio.h> #include <linux/pm.h> +#include <linux/property.h> #define DRV_NAME "rotary-encoder" struct rotary_encoder { struct input_dev *input; - const struct rotary_encoder_platform_data *pdata; - unsigned int axis; + struct mutex access_mutex; + + u32 steps; + u32 axis; + bool relative_axis; + bool rollover; + unsigned int pos; - unsigned int irq_a; - unsigned int irq_b; + struct gpio_descs *gpios; + + unsigned int *irq; bool armed; - unsigned char dir; /* 0 - clockwise, 1 - CCW */ + signed char dir; /* 1 - clockwise, -1 - CCW */ - char last_stable; + unsigned last_stable; }; -static int rotary_encoder_get_state(const struct rotary_encoder_platform_data *pdata) +static unsigned rotary_encoder_get_state(struct rotary_encoder *encoder) { - int a = !!gpio_get_value(pdata->gpio_a); - int b = !!gpio_get_value(pdata->gpio_b); + int i; + unsigned ret = 0; - a ^= pdata->inverted_a; - b ^= pdata->inverted_b; + for (i = 0; i < encoder->gpios->ndescs; ++i) { + int val = gpiod_get_value_cansleep(encoder->gpios->desc[i]); + /* convert from gray encoding to normal */ + if (ret & 1) + val = !val; - return ((a << 1) | b); + ret = ret << 1 | val; + } + + return ret & 3; } static void rotary_encoder_report_event(struct rotary_encoder *encoder) { - const struct rotary_encoder_platform_data *pdata = encoder->pdata; - - if (pdata->relative_axis) { + if (encoder->relative_axis) { input_report_rel(encoder->input, - pdata->axis, encoder->dir ? -1 : 1); + encoder->axis, encoder->dir); } else { unsigned int pos = encoder->pos; - if (encoder->dir) { + if (encoder->dir < 0) { /* turning counter-clockwise */ - if (pdata->rollover) - pos += pdata->steps; + if (encoder->rollover) + pos += encoder->steps; if (pos) pos--; } else { /* turning clockwise */ - if (pdata->rollover || pos < pdata->steps) + if (encoder->rollover || pos < encoder->steps) pos++; } - if (pdata->rollover) - pos %= pdata->steps; + if (encoder->rollover) + pos %= encoder->steps; encoder->pos = pos; - input_report_abs(encoder->input, pdata->axis, encoder->pos); + input_report_abs(encoder->input, encoder->axis, encoder->pos); } input_sync(encoder->input); @@ -92,9 +100,11 @@ static void rotary_encoder_report_event(struct rotary_encoder *encoder) static irqreturn_t rotary_encoder_irq(int irq, void *dev_id) { struct rotary_encoder *encoder = dev_id; - int state; + unsigned state; - state = rotary_encoder_get_state(encoder->pdata); + mutex_lock(&encoder->access_mutex); + + state = rotary_encoder_get_state(encoder); switch (state) { case 0x0: @@ -105,334 +115,227 @@ static irqreturn_t rotary_encoder_irq(int irq, void *dev_id) break; case 0x1: - case 0x2: + case 0x3: if (encoder->armed) - encoder->dir = state - 1; + encoder->dir = 2 - state; break; - case 0x3: + case 0x2: encoder->armed = true; break; } + mutex_unlock(&encoder->access_mutex); + return IRQ_HANDLED; } static irqreturn_t rotary_encoder_half_period_irq(int irq, void *dev_id) { struct rotary_encoder *encoder = dev_id; - int state; + unsigned int state; - state = rotary_encoder_get_state(encoder->pdata); + mutex_lock(&encoder->access_mutex); - switch (state) { - case 0x00: - case 0x03: + state = rotary_encoder_get_state(encoder); + + if (state & 1) { + encoder->dir = ((encoder->last_stable - state + 1) % 4) - 1; + } else { if (state != encoder->last_stable) { rotary_encoder_report_event(encoder); encoder->last_stable = state; } - break; - - case 0x01: - case 0x02: - encoder->dir = (encoder->last_stable + state) & 0x01; - break; } + mutex_unlock(&encoder->access_mutex); + return IRQ_HANDLED; } static irqreturn_t rotary_encoder_quarter_period_irq(int irq, void *dev_id) { struct rotary_encoder *encoder = dev_id; - unsigned char sum; - int state; - - state = rotary_encoder_get_state(encoder->pdata); - - /* - * We encode the previous and the current state using a byte. - * The previous state in the MSB nibble, the current state in the LSB - * nibble. Then use a table to decide the direction of the turn. - */ - sum = (encoder->last_stable << 4) + state; - switch (sum) { - case 0x31: - case 0x10: - case 0x02: - case 0x23: - encoder->dir = 0; /* clockwise */ - break; + unsigned int state; - case 0x13: - case 0x01: - case 0x20: - case 0x32: - encoder->dir = 1; /* counter-clockwise */ - break; + mutex_lock(&encoder->access_mutex); - default: - /* - * Ignore all other values. This covers the case when the - * state didn't change (a spurious interrupt) and the - * cases where the state changed by two steps, making it - * impossible to tell the direction. - * - * In either case, don't report any event and save the - * state for later. - */ + state = rotary_encoder_get_state(encoder); + + if ((encoder->last_stable + 1) % 4 == state) + encoder->dir = 1; + else if (encoder->last_stable == (state + 1) % 4) + encoder->dir = -1; + else goto out; - } rotary_encoder_report_event(encoder); out: encoder->last_stable = state; + mutex_unlock(&encoder->access_mutex); + return IRQ_HANDLED; } -#ifdef CONFIG_OF -static const struct of_device_id rotary_encoder_of_match[] = { - { .compatible = "rotary-encoder", }, - { }, -}; -MODULE_DEVICE_TABLE(of, rotary_encoder_of_match); - -static struct rotary_encoder_platform_data *rotary_encoder_parse_dt(struct device *dev) +static int rotary_encoder_probe(struct platform_device *pdev) { - const struct of_device_id *of_id = - of_match_device(rotary_encoder_of_match, dev); - struct device_node *np = dev->of_node; - struct rotary_encoder_platform_data *pdata; - enum of_gpio_flags flags; - int error; - - if (!of_id || !np) - return NULL; - - pdata = kzalloc(sizeof(struct rotary_encoder_platform_data), - GFP_KERNEL); - if (!pdata) - return ERR_PTR(-ENOMEM); - - of_property_read_u32(np, "rotary-encoder,steps", &pdata->steps); - of_property_read_u32(np, "linux,axis", &pdata->axis); + struct device *dev = &pdev->dev; + struct rotary_encoder *encoder; + struct input_dev *input; + irq_handler_t handler; + u32 steps_per_period; + unsigned int i; + int err; - pdata->gpio_a = of_get_gpio_flags(np, 0, &flags); - pdata->inverted_a = flags & OF_GPIO_ACTIVE_LOW; + encoder = devm_kzalloc(dev, sizeof(struct rotary_encoder), GFP_KERNEL); + if (!encoder) + return -ENOMEM; - pdata->gpio_b = of_get_gpio_flags(np, 1, &flags); - pdata->inverted_b = flags & OF_GPIO_ACTIVE_LOW; + mutex_init(&encoder->access_mutex); - pdata->relative_axis = - of_property_read_bool(np, "rotary-encoder,relative-axis"); - pdata->rollover = of_property_read_bool(np, "rotary-encoder,rollover"); + device_property_read_u32(dev, "rotary-encoder,steps", &encoder->steps); - error = of_property_read_u32(np, "rotary-encoder,steps-per-period", - &pdata->steps_per_period); - if (error) { + err = device_property_read_u32(dev, "rotary-encoder,steps-per-period", + &steps_per_period); + if (err) { /* - * The 'half-period' property has been deprecated, you must use - * 'steps-per-period' and set an appropriate value, but we still - * need to parse it to maintain compatibility. + * The 'half-period' property has been deprecated, you must + * use 'steps-per-period' and set an appropriate value, but + * we still need to parse it to maintain compatibility. If + * neither property is present we fall back to the one step + * per period behavior. */ - if (of_property_read_bool(np, "rotary-encoder,half-period")) { - pdata->steps_per_period = 2; - } else { - /* Fallback to one step per period behavior */ - pdata->steps_per_period = 1; - } + steps_per_period = device_property_read_bool(dev, + "rotary-encoder,half-period") ? 2 : 1; } - pdata->wakeup_source = of_property_read_bool(np, "wakeup-source"); + encoder->rollover = + device_property_read_bool(dev, "rotary-encoder,rollover"); - return pdata; -} -#else -static inline struct rotary_encoder_platform_data * -rotary_encoder_parse_dt(struct device *dev) -{ - return NULL; -} -#endif - -static int rotary_encoder_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - const struct rotary_encoder_platform_data *pdata = dev_get_platdata(dev); - struct rotary_encoder *encoder; - struct input_dev *input; - irq_handler_t handler; - int err; - - if (!pdata) { - pdata = rotary_encoder_parse_dt(dev); - if (IS_ERR(pdata)) - return PTR_ERR(pdata); + device_property_read_u32(dev, "linux,axis", &encoder->axis); + encoder->relative_axis = + device_property_read_bool(dev, "rotary-encoder,relative-axis"); - if (!pdata) { - dev_err(dev, "missing platform data\n"); - return -EINVAL; - } + encoder->gpios = devm_gpiod_get_array(dev, NULL, GPIOD_IN); + if (IS_ERR(encoder->gpios)) { + dev_err(dev, "unable to get gpios\n"); + return PTR_ERR(encoder->gpios); } - - encoder = kzalloc(sizeof(struct rotary_encoder), GFP_KERNEL); - input = input_allocate_device(); - if (!encoder || !input) { - err = -ENOMEM; - goto exit_free_mem; + if (encoder->gpios->ndescs < 2) { + dev_err(dev, "not enough gpios found\n"); + return -EINVAL; } + input = devm_input_allocate_device(dev); + if (!input) + return -ENOMEM; + encoder->input = input; - encoder->pdata = pdata; input->name = pdev->name; input->id.bustype = BUS_HOST; input->dev.parent = dev; - if (pdata->relative_axis) { - input->evbit[0] = BIT_MASK(EV_REL); - input->relbit[0] = BIT_MASK(pdata->axis); - } else { - input->evbit[0] = BIT_MASK(EV_ABS); - input_set_abs_params(encoder->input, - pdata->axis, 0, pdata->steps, 0, 1); - } - - /* request the GPIOs */ - err = gpio_request_one(pdata->gpio_a, GPIOF_IN, dev_name(dev)); - if (err) { - dev_err(dev, "unable to request GPIO %d\n", pdata->gpio_a); - goto exit_free_mem; - } - - err = gpio_request_one(pdata->gpio_b, GPIOF_IN, dev_name(dev)); - if (err) { - dev_err(dev, "unable to request GPIO %d\n", pdata->gpio_b); - goto exit_free_gpio_a; - } - - encoder->irq_a = gpio_to_irq(pdata->gpio_a); - encoder->irq_b = gpio_to_irq(pdata->gpio_b); + if (encoder->relative_axis) + input_set_capability(input, EV_REL, encoder->axis); + else + input_set_abs_params(input, + encoder->axis, 0, encoder->steps, 0, 1); - switch (pdata->steps_per_period) { + switch (steps_per_period >> (encoder->gpios->ndescs - 2)) { case 4: handler = &rotary_encoder_quarter_period_irq; - encoder->last_stable = rotary_encoder_get_state(pdata); + encoder->last_stable = rotary_encoder_get_state(encoder); break; case 2: handler = &rotary_encoder_half_period_irq; - encoder->last_stable = rotary_encoder_get_state(pdata); + encoder->last_stable = rotary_encoder_get_state(encoder); break; case 1: handler = &rotary_encoder_irq; break; default: dev_err(dev, "'%d' is not a valid steps-per-period value\n", - pdata->steps_per_period); - err = -EINVAL; - goto exit_free_gpio_b; + steps_per_period); + return -EINVAL; } - err = request_irq(encoder->irq_a, handler, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - DRV_NAME, encoder); - if (err) { - dev_err(dev, "unable to request IRQ %d\n", encoder->irq_a); - goto exit_free_gpio_b; - } - - err = request_irq(encoder->irq_b, handler, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - DRV_NAME, encoder); - if (err) { - dev_err(dev, "unable to request IRQ %d\n", encoder->irq_b); - goto exit_free_irq_a; + encoder->irq = + devm_kzalloc(dev, + sizeof(*encoder->irq) * encoder->gpios->ndescs, + GFP_KERNEL); + if (!encoder->irq) + return -ENOMEM; + + for (i = 0; i < encoder->gpios->ndescs; ++i) { + encoder->irq[i] = gpiod_to_irq(encoder->gpios->desc[i]); + + err = devm_request_threaded_irq(dev, encoder->irq[i], + NULL, handler, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + DRV_NAME, encoder); + if (err) { + dev_err(dev, "unable to request IRQ %d (gpio#%d)\n", + encoder->irq[i], i); + return err; + } } err = input_register_device(input); if (err) { dev_err(dev, "failed to register input device\n"); - goto exit_free_irq_b; + return err; } - device_init_wakeup(&pdev->dev, pdata->wakeup_source); + device_init_wakeup(dev, + device_property_read_bool(dev, "wakeup-source")); platform_set_drvdata(pdev, encoder); return 0; - -exit_free_irq_b: - free_irq(encoder->irq_b, encoder); -exit_free_irq_a: - free_irq(encoder->irq_a, encoder); -exit_free_gpio_b: - gpio_free(pdata->gpio_b); -exit_free_gpio_a: - gpio_free(pdata->gpio_a); -exit_free_mem: - input_free_device(input); - kfree(encoder); - if (!dev_get_platdata(&pdev->dev)) - kfree(pdata); - - return err; } -static int rotary_encoder_remove(struct platform_device *pdev) -{ - struct rotary_encoder *encoder = platform_get_drvdata(pdev); - const struct rotary_encoder_platform_data *pdata = encoder->pdata; - - device_init_wakeup(&pdev->dev, false); - - free_irq(encoder->irq_a, encoder); - free_irq(encoder->irq_b, encoder); - gpio_free(pdata->gpio_a); - gpio_free(pdata->gpio_b); - - input_unregister_device(encoder->input); - kfree(encoder); - - if (!dev_get_platdata(&pdev->dev)) - kfree(pdata); - - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int rotary_encoder_suspend(struct device *dev) +static int __maybe_unused rotary_encoder_suspend(struct device *dev) { struct rotary_encoder *encoder = dev_get_drvdata(dev); + unsigned int i; if (device_may_wakeup(dev)) { - enable_irq_wake(encoder->irq_a); - enable_irq_wake(encoder->irq_b); + for (i = 0; i < encoder->gpios->ndescs; ++i) + enable_irq_wake(encoder->irq[i]); } return 0; } -static int rotary_encoder_resume(struct device *dev) +static int __maybe_unused rotary_encoder_resume(struct device *dev) { struct rotary_encoder *encoder = dev_get_drvdata(dev); + unsigned int i; if (device_may_wakeup(dev)) { - disable_irq_wake(encoder->irq_a); - disable_irq_wake(encoder->irq_b); + for (i = 0; i < encoder->gpios->ndescs; ++i) + disable_irq_wake(encoder->irq[i]); } return 0; } -#endif static SIMPLE_DEV_PM_OPS(rotary_encoder_pm_ops, - rotary_encoder_suspend, rotary_encoder_resume); + rotary_encoder_suspend, rotary_encoder_resume); + +#ifdef CONFIG_OF +static const struct of_device_id rotary_encoder_of_match[] = { + { .compatible = "rotary-encoder", }, + { }, +}; +MODULE_DEVICE_TABLE(of, rotary_encoder_of_match); +#endif static struct platform_driver rotary_encoder_driver = { .probe = rotary_encoder_probe, - .remove = rotary_encoder_remove, .driver = { .name = DRV_NAME, .pm = &rotary_encoder_pm_ops, diff --git a/drivers/input/misc/twl4030-vibra.c b/drivers/input/misc/twl4030-vibra.c index 10c4e3d46..caa5a62c4 100644 --- a/drivers/input/misc/twl4030-vibra.c +++ b/drivers/input/misc/twl4030-vibra.c @@ -222,7 +222,6 @@ static int twl4030_vibra_probe(struct platform_device *pdev) info->input_dev->name = "twl4030:vibrator"; info->input_dev->id.version = 1; - info->input_dev->dev.parent = pdev->dev.parent; info->input_dev->close = twl4030_vibra_close; __set_bit(FF_RUMBLE, info->input_dev->ffbit); diff --git a/drivers/input/misc/twl6040-vibra.c b/drivers/input/misc/twl6040-vibra.c index ea63fad48..42de34b92 100644 --- a/drivers/input/misc/twl6040-vibra.c +++ b/drivers/input/misc/twl6040-vibra.c @@ -45,7 +45,6 @@ struct vibra_info { struct device *dev; struct input_dev *input_dev; - struct workqueue_struct *workqueue; struct work_struct play_work; struct mutex mutex; int irq; @@ -182,6 +181,14 @@ static void vibra_play_work(struct work_struct *work) { struct vibra_info *info = container_of(work, struct vibra_info, play_work); + int ret; + + /* Do not allow effect, while the routing is set to use audio */ + ret = twl6040_get_vibralr_status(info->twl6040); + if (ret & TWL6040_VIBSEL) { + dev_info(info->dev, "Vibra is configured for audio\n"); + return; + } mutex_lock(&info->mutex); @@ -200,24 +207,12 @@ static int vibra_play(struct input_dev *input, void *data, struct ff_effect *effect) { struct vibra_info *info = input_get_drvdata(input); - int ret; - - /* Do not allow effect, while the routing is set to use audio */ - ret = twl6040_get_vibralr_status(info->twl6040); - if (ret & TWL6040_VIBSEL) { - dev_info(&input->dev, "Vibra is configured for audio\n"); - return -EBUSY; - } info->weak_speed = effect->u.rumble.weak_magnitude; info->strong_speed = effect->u.rumble.strong_magnitude; info->direction = effect->direction < EFFECT_DIR_180_DEG ? 1 : -1; - ret = queue_work(info->workqueue, &info->play_work); - if (!ret) { - dev_info(&input->dev, "work is already on queue\n"); - return ret; - } + schedule_work(&info->play_work); return 0; } @@ -262,6 +257,7 @@ static int twl6040_vibra_probe(struct platform_device *pdev) int vddvibr_uV = 0; int error; + of_node_get(twl6040_core_dev->of_node); twl6040_core_node = of_find_node_by_name(twl6040_core_dev->of_node, "vibra"); if (!twl6040_core_node) { @@ -362,7 +358,6 @@ static int twl6040_vibra_probe(struct platform_device *pdev) info->input_dev->name = "twl6040:vibrator"; info->input_dev->id.version = 1; - info->input_dev->dev.parent = pdev->dev.parent; info->input_dev->close = twl6040_vibra_close; __set_bit(FF_RUMBLE, info->input_dev->ffbit); diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c index 4eb9e4d94..65ebbd111 100644 --- a/drivers/input/misc/uinput.c +++ b/drivers/input/misc/uinput.c @@ -664,7 +664,7 @@ struct uinput_ff_upload_compat { static int uinput_ff_upload_to_user(char __user *buffer, const struct uinput_ff_upload *ff_up) { - if (INPUT_COMPAT_TEST) { + if (in_compat_syscall()) { struct uinput_ff_upload_compat ff_up_compat; ff_up_compat.request_id = ff_up->request_id; @@ -695,7 +695,7 @@ static int uinput_ff_upload_to_user(char __user *buffer, static int uinput_ff_upload_from_user(const char __user *buffer, struct uinput_ff_upload *ff_up) { - if (INPUT_COMPAT_TEST) { + if (in_compat_syscall()) { struct uinput_ff_upload_compat ff_up_compat; if (copy_from_user(&ff_up_compat, buffer, @@ -981,9 +981,15 @@ static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg) } #ifdef CONFIG_COMPAT + +#define UI_SET_PHYS_COMPAT _IOW(UINPUT_IOCTL_BASE, 108, compat_uptr_t) + static long uinput_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { + if (cmd == UI_SET_PHYS_COMPAT) + cmd = UI_SET_PHYS; + return uinput_ioctl_handler(file, cmd, arg, compat_ptr(arg)); } #endif diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index 17f97e5e1..096abb4ad 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -48,6 +48,16 @@ config MOUSE_PS2_ALPS If unsure, say Y. +config MOUSE_PS2_BYD + bool "BYD PS/2 mouse protocol extension" if EXPERT + default y + depends on MOUSE_PS2 + help + Say Y here if you have a BYD PS/2 touchpad connected to + your system. + + If unsure, say Y. + config MOUSE_PS2_LOGIPS2PP bool "Logitech PS/2++ mouse protocol extension" if EXPERT default y diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile index ee6a6e956..6168b1349 100644 --- a/drivers/input/mouse/Makefile +++ b/drivers/input/mouse/Makefile @@ -28,6 +28,7 @@ cyapatp-objs := cyapa.o cyapa_gen3.o cyapa_gen5.o cyapa_gen6.o psmouse-objs := psmouse-base.o synaptics.o focaltech.o psmouse-$(CONFIG_MOUSE_PS2_ALPS) += alps.o +psmouse-$(CONFIG_MOUSE_PS2_BYD) += byd.o psmouse-$(CONFIG_MOUSE_PS2_ELANTECH) += elantech.o psmouse-$(CONFIG_MOUSE_PS2_OLPC) += hgpk.o psmouse-$(CONFIG_MOUSE_PS2_LOGIPS2PP) += logips2pp.o diff --git a/drivers/input/mouse/byd.c b/drivers/input/mouse/byd.c new file mode 100644 index 000000000..e583f8b50 --- /dev/null +++ b/drivers/input/mouse/byd.c @@ -0,0 +1,512 @@ +/* + * BYD TouchPad PS/2 mouse driver + * + * Copyright (C) 2015 Chris Diamand <chris@diamand.org> + * Copyright (C) 2015 Richard Pospesel + * Copyright (C) 2015 Tai Chi Minh Ralph Eastwood + * Copyright (C) 2015 Martin Wimpress + * Copyright (C) 2015 Jay Kuri + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include <linux/delay.h> +#include <linux/input.h> +#include <linux/libps2.h> +#include <linux/serio.h> +#include <linux/slab.h> + +#include "psmouse.h" +#include "byd.h" + +/* PS2 Bits */ +#define PS2_Y_OVERFLOW BIT_MASK(7) +#define PS2_X_OVERFLOW BIT_MASK(6) +#define PS2_Y_SIGN BIT_MASK(5) +#define PS2_X_SIGN BIT_MASK(4) +#define PS2_ALWAYS_1 BIT_MASK(3) +#define PS2_MIDDLE BIT_MASK(2) +#define PS2_RIGHT BIT_MASK(1) +#define PS2_LEFT BIT_MASK(0) + +/* + * BYD pad constants + */ + +/* + * True device resolution is unknown, however experiments show the + * resolution is about 111 units/mm. + * Absolute coordinate packets are in the range 0-255 for both X and Y + * we pick ABS_X/ABS_Y dimensions which are multiples of 256 and in + * the right ballpark given the touchpad's physical dimensions and estimate + * resolution per spec sheet, device active area dimensions are + * 101.6 x 60.1 mm. + */ +#define BYD_PAD_WIDTH 11264 +#define BYD_PAD_HEIGHT 6656 +#define BYD_PAD_RESOLUTION 111 + +/* + * Given the above dimensions, relative packets velocity is in multiples of + * 1 unit / 11 milliseconds. We use this dt to estimate distance traveled + */ +#define BYD_DT 11 +/* Time in jiffies used to timeout various touch events (64 ms) */ +#define BYD_TOUCH_TIMEOUT msecs_to_jiffies(64) + +/* BYD commands reverse engineered from windows driver */ + +/* + * Swipe gesture from off-pad to on-pad + * 0 : disable + * 1 : enable + */ +#define BYD_CMD_SET_OFFSCREEN_SWIPE 0x10cc +/* + * Tap and drag delay time + * 0 : disable + * 1 - 8 : least to most delay + */ +#define BYD_CMD_SET_TAP_DRAG_DELAY_TIME 0x10cf +/* + * Physical buttons function mapping + * 0 : enable + * 4 : normal + * 5 : left button custom command + * 6 : right button custom command + * 8 : disable + */ +#define BYD_CMD_SET_PHYSICAL_BUTTONS 0x10d0 +/* + * Absolute mode (1 byte X/Y resolution) + * 0 : disable + * 2 : enable + */ +#define BYD_CMD_SET_ABSOLUTE_MODE 0x10d1 +/* + * Two finger scrolling + * 1 : vertical + * 2 : horizontal + * 3 : vertical + horizontal + * 4 : disable + */ +#define BYD_CMD_SET_TWO_FINGER_SCROLL 0x10d2 +/* + * Handedness + * 1 : right handed + * 2 : left handed + */ +#define BYD_CMD_SET_HANDEDNESS 0x10d3 +/* + * Tap to click + * 1 : enable + * 2 : disable + */ +#define BYD_CMD_SET_TAP 0x10d4 +/* + * Tap and drag + * 1 : tap and hold to drag + * 2 : tap and hold to drag + lock + * 3 : disable + */ +#define BYD_CMD_SET_TAP_DRAG 0x10d5 +/* + * Touch sensitivity + * 1 - 7 : least to most sensitive + */ +#define BYD_CMD_SET_TOUCH_SENSITIVITY 0x10d6 +/* + * One finger scrolling + * 1 : vertical + * 2 : horizontal + * 3 : vertical + horizontal + * 4 : disable + */ +#define BYD_CMD_SET_ONE_FINGER_SCROLL 0x10d7 +/* + * One finger scrolling function + * 1 : free scrolling + * 2 : edge motion + * 3 : free scrolling + edge motion + * 4 : disable + */ +#define BYD_CMD_SET_ONE_FINGER_SCROLL_FUNC 0x10d8 +/* + * Sliding speed + * 1 - 5 : slowest to fastest + */ +#define BYD_CMD_SET_SLIDING_SPEED 0x10da +/* + * Edge motion + * 1 : disable + * 2 : enable when dragging + * 3 : enable when dragging and pointing + */ +#define BYD_CMD_SET_EDGE_MOTION 0x10db +/* + * Left edge region size + * 0 - 7 : smallest to largest width + */ +#define BYD_CMD_SET_LEFT_EDGE_REGION 0x10dc +/* + * Top edge region size + * 0 - 9 : smallest to largest height + */ +#define BYD_CMD_SET_TOP_EDGE_REGION 0x10dd +/* + * Disregard palm press as clicks + * 1 - 6 : smallest to largest + */ +#define BYD_CMD_SET_PALM_CHECK 0x10de +/* + * Right edge region size + * 0 - 7 : smallest to largest width + */ +#define BYD_CMD_SET_RIGHT_EDGE_REGION 0x10df +/* + * Bottom edge region size + * 0 - 9 : smallest to largest height + */ +#define BYD_CMD_SET_BOTTOM_EDGE_REGION 0x10e1 +/* + * Multitouch gestures + * 1 : enable + * 2 : disable + */ +#define BYD_CMD_SET_MULTITOUCH 0x10e3 +/* + * Edge motion speed + * 0 : control with finger pressure + * 1 - 9 : slowest to fastest + */ +#define BYD_CMD_SET_EDGE_MOTION_SPEED 0x10e4 +/* + * Two finger scolling function + * 0 : free scrolling + * 1 : free scrolling (with momentum) + * 2 : edge motion + * 3 : free scrolling (with momentum) + edge motion + * 4 : disable + */ +#define BYD_CMD_SET_TWO_FINGER_SCROLL_FUNC 0x10e5 + +/* + * The touchpad generates a mixture of absolute and relative packets, indicated + * by the the last byte of each packet being set to one of the following: + */ +#define BYD_PACKET_ABSOLUTE 0xf8 +#define BYD_PACKET_RELATIVE 0x00 +/* Multitouch gesture packets */ +#define BYD_PACKET_PINCH_IN 0xd8 +#define BYD_PACKET_PINCH_OUT 0x28 +#define BYD_PACKET_ROTATE_CLOCKWISE 0x29 +#define BYD_PACKET_ROTATE_ANTICLOCKWISE 0xd7 +#define BYD_PACKET_TWO_FINGER_SCROLL_RIGHT 0x2a +#define BYD_PACKET_TWO_FINGER_SCROLL_DOWN 0x2b +#define BYD_PACKET_TWO_FINGER_SCROLL_UP 0xd5 +#define BYD_PACKET_TWO_FINGER_SCROLL_LEFT 0xd6 +#define BYD_PACKET_THREE_FINGER_SWIPE_RIGHT 0x2c +#define BYD_PACKET_THREE_FINGER_SWIPE_DOWN 0x2d +#define BYD_PACKET_THREE_FINGER_SWIPE_UP 0xd3 +#define BYD_PACKET_THREE_FINGER_SWIPE_LEFT 0xd4 +#define BYD_PACKET_FOUR_FINGER_DOWN 0x33 +#define BYD_PACKET_FOUR_FINGER_UP 0xcd +#define BYD_PACKET_REGION_SCROLL_RIGHT 0x35 +#define BYD_PACKET_REGION_SCROLL_DOWN 0x36 +#define BYD_PACKET_REGION_SCROLL_UP 0xca +#define BYD_PACKET_REGION_SCROLL_LEFT 0xcb +#define BYD_PACKET_RIGHT_CORNER_CLICK 0xd2 +#define BYD_PACKET_LEFT_CORNER_CLICK 0x2e +#define BYD_PACKET_LEFT_AND_RIGHT_CORNER_CLICK 0x2f +#define BYD_PACKET_ONTO_PAD_SWIPE_RIGHT 0x37 +#define BYD_PACKET_ONTO_PAD_SWIPE_DOWN 0x30 +#define BYD_PACKET_ONTO_PAD_SWIPE_UP 0xd0 +#define BYD_PACKET_ONTO_PAD_SWIPE_LEFT 0xc9 + +struct byd_data { + struct timer_list timer; + s32 abs_x; + s32 abs_y; + typeof(jiffies) last_touch_time; + bool btn_left; + bool btn_right; + bool touch; +}; + +static void byd_report_input(struct psmouse *psmouse) +{ + struct byd_data *priv = psmouse->private; + struct input_dev *dev = psmouse->dev; + + input_report_key(dev, BTN_TOUCH, priv->touch); + input_report_key(dev, BTN_TOOL_FINGER, priv->touch); + + input_report_abs(dev, ABS_X, priv->abs_x); + input_report_abs(dev, ABS_Y, priv->abs_y); + input_report_key(dev, BTN_LEFT, priv->btn_left); + input_report_key(dev, BTN_RIGHT, priv->btn_right); + + input_sync(dev); +} + +static void byd_clear_touch(unsigned long data) +{ + struct psmouse *psmouse = (struct psmouse *)data; + struct byd_data *priv = psmouse->private; + + serio_pause_rx(psmouse->ps2dev.serio); + priv->touch = false; + + byd_report_input(psmouse); + + serio_continue_rx(psmouse->ps2dev.serio); + + /* + * Move cursor back to center of pad when we lose touch - this + * specifically improves user experience when moving cursor with one + * finger, and pressing a button with another. + */ + priv->abs_x = BYD_PAD_WIDTH / 2; + priv->abs_y = BYD_PAD_HEIGHT / 2; +} + +static psmouse_ret_t byd_process_byte(struct psmouse *psmouse) +{ + struct byd_data *priv = psmouse->private; + u8 *pkt = psmouse->packet; + + if (psmouse->pktcnt > 0 && !(pkt[0] & PS2_ALWAYS_1)) { + psmouse_warn(psmouse, "Always_1 bit not 1. pkt[0] = %02x\n", + pkt[0]); + return PSMOUSE_BAD_DATA; + } + + if (psmouse->pktcnt < psmouse->pktsize) + return PSMOUSE_GOOD_DATA; + + /* Otherwise, a full packet has been received */ + switch (pkt[3]) { + case BYD_PACKET_ABSOLUTE: + /* Only use absolute packets for the start of movement. */ + if (!priv->touch) { + /* needed to detect tap */ + typeof(jiffies) tap_time = + priv->last_touch_time + BYD_TOUCH_TIMEOUT; + priv->touch = time_after(jiffies, tap_time); + + /* init abs position */ + priv->abs_x = pkt[1] * (BYD_PAD_WIDTH / 256); + priv->abs_y = (255 - pkt[2]) * (BYD_PAD_HEIGHT / 256); + } + break; + case BYD_PACKET_RELATIVE: { + /* Standard packet */ + /* Sign-extend if a sign bit is set. */ + u32 signx = pkt[0] & PS2_X_SIGN ? ~0xFF : 0; + u32 signy = pkt[0] & PS2_Y_SIGN ? ~0xFF : 0; + s32 dx = signx | (int) pkt[1]; + s32 dy = signy | (int) pkt[2]; + + /* Update position based on velocity */ + priv->abs_x += dx * BYD_DT; + priv->abs_y -= dy * BYD_DT; + + priv->touch = true; + break; + } + default: + psmouse_warn(psmouse, + "Unrecognized Z: pkt = %02x %02x %02x %02x\n", + psmouse->packet[0], psmouse->packet[1], + psmouse->packet[2], psmouse->packet[3]); + return PSMOUSE_BAD_DATA; + } + + priv->btn_left = pkt[0] & PS2_LEFT; + priv->btn_right = pkt[0] & PS2_RIGHT; + + byd_report_input(psmouse); + + /* Reset time since last touch. */ + if (priv->touch) { + priv->last_touch_time = jiffies; + mod_timer(&priv->timer, jiffies + BYD_TOUCH_TIMEOUT); + } + + return PSMOUSE_FULL_PACKET; +} + +static int byd_reset_touchpad(struct psmouse *psmouse) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + u8 param[4]; + size_t i; + + const struct { + u16 command; + u8 arg; + } seq[] = { + /* + * Intellimouse initialization sequence, to get 4-byte instead + * of 3-byte packets. + */ + { PSMOUSE_CMD_SETRATE, 0xC8 }, + { PSMOUSE_CMD_SETRATE, 0x64 }, + { PSMOUSE_CMD_SETRATE, 0x50 }, + { PSMOUSE_CMD_GETID, 0 }, + { PSMOUSE_CMD_ENABLE, 0 }, + /* + * BYD-specific initialization, which enables absolute mode and + * (if desired), the touchpad's built-in gesture detection. + */ + { 0x10E2, 0x00 }, + { 0x10E0, 0x02 }, + /* The touchpad should reply with 4 seemingly-random bytes */ + { 0x14E0, 0x01 }, + /* Pairs of parameters and values. */ + { BYD_CMD_SET_HANDEDNESS, 0x01 }, + { BYD_CMD_SET_PHYSICAL_BUTTONS, 0x04 }, + { BYD_CMD_SET_TAP, 0x02 }, + { BYD_CMD_SET_ONE_FINGER_SCROLL, 0x04 }, + { BYD_CMD_SET_ONE_FINGER_SCROLL_FUNC, 0x04 }, + { BYD_CMD_SET_EDGE_MOTION, 0x01 }, + { BYD_CMD_SET_PALM_CHECK, 0x00 }, + { BYD_CMD_SET_MULTITOUCH, 0x02 }, + { BYD_CMD_SET_TWO_FINGER_SCROLL, 0x04 }, + { BYD_CMD_SET_TWO_FINGER_SCROLL_FUNC, 0x04 }, + { BYD_CMD_SET_LEFT_EDGE_REGION, 0x00 }, + { BYD_CMD_SET_TOP_EDGE_REGION, 0x00 }, + { BYD_CMD_SET_RIGHT_EDGE_REGION, 0x00 }, + { BYD_CMD_SET_BOTTOM_EDGE_REGION, 0x00 }, + { BYD_CMD_SET_ABSOLUTE_MODE, 0x02 }, + /* Finalize initialization. */ + { 0x10E0, 0x00 }, + { 0x10E2, 0x01 }, + }; + + for (i = 0; i < ARRAY_SIZE(seq); ++i) { + memset(param, 0, sizeof(param)); + param[0] = seq[i].arg; + if (ps2_command(ps2dev, param, seq[i].command)) + return -EIO; + } + + psmouse_set_state(psmouse, PSMOUSE_ACTIVATED); + return 0; +} + +static int byd_reconnect(struct psmouse *psmouse) +{ + int retry = 0, error = 0; + + psmouse_dbg(psmouse, "Reconnect\n"); + do { + psmouse_reset(psmouse); + if (retry) + ssleep(1); + error = byd_detect(psmouse, 0); + } while (error && ++retry < 3); + + if (error) + return error; + + psmouse_dbg(psmouse, "Reconnected after %d attempts\n", retry); + + error = byd_reset_touchpad(psmouse); + if (error) { + psmouse_err(psmouse, "Unable to initialize device\n"); + return error; + } + + return 0; +} + +static void byd_disconnect(struct psmouse *psmouse) +{ + struct byd_data *priv = psmouse->private; + + if (priv) { + del_timer(&priv->timer); + kfree(psmouse->private); + psmouse->private = NULL; + } +} + +int byd_detect(struct psmouse *psmouse, bool set_properties) +{ + struct ps2dev *ps2dev = &psmouse->ps2dev; + u8 param[4] = {0x03, 0x00, 0x00, 0x00}; + + if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES)) + return -1; + if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES)) + return -1; + if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES)) + return -1; + if (ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES)) + return -1; + if (ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO)) + return -1; + + if (param[1] != 0x03 || param[2] != 0x64) + return -ENODEV; + + psmouse_dbg(psmouse, "BYD touchpad detected\n"); + + if (set_properties) { + psmouse->vendor = "BYD"; + psmouse->name = "TouchPad"; + } + + return 0; +} + +int byd_init(struct psmouse *psmouse) +{ + struct input_dev *dev = psmouse->dev; + struct byd_data *priv; + + if (psmouse_reset(psmouse)) + return -EIO; + + if (byd_reset_touchpad(psmouse)) + return -EIO; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + memset(priv, 0, sizeof(*priv)); + setup_timer(&priv->timer, byd_clear_touch, (unsigned long) psmouse); + + psmouse->private = priv; + psmouse->disconnect = byd_disconnect; + psmouse->reconnect = byd_reconnect; + psmouse->protocol_handler = byd_process_byte; + psmouse->pktsize = 4; + psmouse->resync_time = 0; + + __set_bit(INPUT_PROP_POINTER, dev->propbit); + /* Touchpad */ + __set_bit(BTN_TOUCH, dev->keybit); + __set_bit(BTN_TOOL_FINGER, dev->keybit); + /* Buttons */ + __set_bit(BTN_LEFT, dev->keybit); + __set_bit(BTN_RIGHT, dev->keybit); + __clear_bit(BTN_MIDDLE, dev->keybit); + + /* Absolute position */ + __set_bit(EV_ABS, dev->evbit); + input_set_abs_params(dev, ABS_X, 0, BYD_PAD_WIDTH, 0, 0); + input_set_abs_params(dev, ABS_Y, 0, BYD_PAD_HEIGHT, 0, 0); + input_abs_set_res(dev, ABS_X, BYD_PAD_RESOLUTION); + input_abs_set_res(dev, ABS_Y, BYD_PAD_RESOLUTION); + /* No relative support */ + __clear_bit(EV_REL, dev->evbit); + __clear_bit(REL_X, dev->relbit); + __clear_bit(REL_Y, dev->relbit); + + return 0; +} diff --git a/drivers/input/mouse/byd.h b/drivers/input/mouse/byd.h new file mode 100644 index 000000000..d6c120cf3 --- /dev/null +++ b/drivers/input/mouse/byd.h @@ -0,0 +1,18 @@ +#ifndef _BYD_H +#define _BYD_H + +#ifdef CONFIG_MOUSE_PS2_BYD +int byd_detect(struct psmouse *psmouse, bool set_properties); +int byd_init(struct psmouse *psmouse); +#else +static inline int byd_detect(struct psmouse *psmouse, bool set_properties) +{ + return -ENOSYS; +} +static inline int byd_init(struct psmouse *psmouse) +{ + return -ENOSYS; +} +#endif /* CONFIG_MOUSE_PS2_BYD */ + +#endif /* _BYD_H */ diff --git a/drivers/input/mouse/cyapa.c b/drivers/input/mouse/cyapa.c index e054261f3..117eec126 100644 --- a/drivers/input/mouse/cyapa.c +++ b/drivers/input/mouse/cyapa.c @@ -383,7 +383,7 @@ static int cyapa_open(struct input_dev *input) * when in operational mode. */ error = cyapa->ops->set_power_mode(cyapa, - PWR_MODE_FULL_ACTIVE, 0, false); + PWR_MODE_FULL_ACTIVE, 0, CYAPA_PM_ACTIVE); if (error) { dev_warn(dev, "set active power failed: %d\n", error); goto out; @@ -424,7 +424,8 @@ static void cyapa_close(struct input_dev *input) pm_runtime_set_suspended(dev); if (cyapa->operational) - cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0, false); + cyapa->ops->set_power_mode(cyapa, + PWR_MODE_OFF, 0, CYAPA_PM_DEACTIVE); mutex_unlock(&cyapa->state_sync_lock); } @@ -534,7 +535,7 @@ static void cyapa_enable_irq_for_cmd(struct cyapa *cyapa) */ if (!input || cyapa->operational) cyapa->ops->set_power_mode(cyapa, - PWR_MODE_FULL_ACTIVE, 0, false); + PWR_MODE_FULL_ACTIVE, 0, CYAPA_PM_ACTIVE); /* Gen3 always using polling mode for command. */ if (cyapa->gen >= CYAPA_GEN5) enable_irq(cyapa->client->irq); @@ -550,7 +551,7 @@ static void cyapa_disable_irq_for_cmd(struct cyapa *cyapa) disable_irq(cyapa->client->irq); if (!input || cyapa->operational) cyapa->ops->set_power_mode(cyapa, - PWR_MODE_OFF, 0, false); + PWR_MODE_OFF, 0, CYAPA_PM_ACTIVE); } } @@ -617,7 +618,8 @@ static int cyapa_initialize(struct cyapa *cyapa) /* Power down the device until we need it. */ if (cyapa->operational) - cyapa->ops->set_power_mode(cyapa, PWR_MODE_OFF, 0, false); + cyapa->ops->set_power_mode(cyapa, + PWR_MODE_OFF, 0, CYAPA_PM_ACTIVE); return 0; } @@ -634,7 +636,7 @@ static int cyapa_reinitialize(struct cyapa *cyapa) /* Avoid command failures when TP was in OFF state. */ if (cyapa->operational) cyapa->ops->set_power_mode(cyapa, - PWR_MODE_FULL_ACTIVE, 0, false); + PWR_MODE_FULL_ACTIVE, 0, CYAPA_PM_ACTIVE); error = cyapa_detect(cyapa); if (error) @@ -654,7 +656,7 @@ out: /* Reset to power OFF state to save power when no user open. */ if (cyapa->operational) cyapa->ops->set_power_mode(cyapa, - PWR_MODE_OFF, 0, false); + PWR_MODE_OFF, 0, CYAPA_PM_DEACTIVE); } else if (!error && cyapa->operational) { /* * Make sure only enable runtime PM when device is @@ -1392,7 +1394,7 @@ static int __maybe_unused cyapa_suspend(struct device *dev) power_mode = device_may_wakeup(dev) ? cyapa->suspend_power_mode : PWR_MODE_OFF; error = cyapa->ops->set_power_mode(cyapa, power_mode, - cyapa->suspend_sleep_time, true); + cyapa->suspend_sleep_time, CYAPA_PM_SUSPEND); if (error) dev_err(dev, "suspend set power mode failed: %d\n", error); @@ -1447,7 +1449,7 @@ static int __maybe_unused cyapa_runtime_suspend(struct device *dev) error = cyapa->ops->set_power_mode(cyapa, cyapa->runtime_suspend_power_mode, cyapa->runtime_suspend_sleep_time, - false); + CYAPA_PM_RUNTIME_SUSPEND); if (error) dev_warn(dev, "runtime suspend failed: %d\n", error); @@ -1460,7 +1462,7 @@ static int __maybe_unused cyapa_runtime_resume(struct device *dev) int error; error = cyapa->ops->set_power_mode(cyapa, - PWR_MODE_FULL_ACTIVE, 0, false); + PWR_MODE_FULL_ACTIVE, 0, CYAPA_PM_RUNTIME_RESUME); if (error) dev_warn(dev, "runtime resume failed: %d\n", error); diff --git a/drivers/input/mouse/cyapa.h b/drivers/input/mouse/cyapa.h index b812bba8c..ce951fe45 100644 --- a/drivers/input/mouse/cyapa.h +++ b/drivers/input/mouse/cyapa.h @@ -250,6 +250,15 @@ struct cyapa; typedef bool (*cb_sort)(struct cyapa *, u8 *, int); +enum cyapa_pm_stage { + CYAPA_PM_DEACTIVE, + CYAPA_PM_ACTIVE, + CYAPA_PM_SUSPEND, + CYAPA_PM_RESUME, + CYAPA_PM_RUNTIME_SUSPEND, + CYAPA_PM_RUNTIME_RESUME, +}; + struct cyapa_dev_ops { int (*check_fw)(struct cyapa *, const struct firmware *); int (*bl_enter)(struct cyapa *); @@ -273,7 +282,7 @@ struct cyapa_dev_ops { int (*sort_empty_output_data)(struct cyapa *, u8 *, int *, cb_sort); - int (*set_power_mode)(struct cyapa *, u8, u16, bool); + int (*set_power_mode)(struct cyapa *, u8, u16, enum cyapa_pm_stage); int (*set_proximity)(struct cyapa *, bool); }; @@ -289,6 +298,9 @@ struct cyapa_pip_cmd_states { u8 *resp_data; int *resp_len; + enum cyapa_pm_stage pm_stage; + struct mutex pm_stage_lock; + u8 irq_cmd_buf[CYAPA_REG_MAP_SIZE]; u8 empty_buf[CYAPA_REG_MAP_SIZE]; }; diff --git a/drivers/input/mouse/cyapa_gen3.c b/drivers/input/mouse/cyapa_gen3.c index 1a9d12ae7..f9600753e 100644 --- a/drivers/input/mouse/cyapa_gen3.c +++ b/drivers/input/mouse/cyapa_gen3.c @@ -269,6 +269,7 @@ static const struct cyapa_cmd_len cyapa_smbus_cmds[] = { { CYAPA_SMBUS_MIN_BASELINE, 1 }, /* CYAPA_CMD_MIN_BASELINE */ }; +static int cyapa_gen3_try_poll_handler(struct cyapa *cyapa); /* * cyapa_smbus_read_block - perform smbus block read command @@ -950,12 +951,14 @@ static u16 cyapa_get_wait_time_for_pwr_cmd(u8 pwr_mode) * Device power mode can only be set when device is in operational mode. */ static int cyapa_gen3_set_power_mode(struct cyapa *cyapa, u8 power_mode, - u16 always_unused, bool is_suspend_unused) + u16 always_unused, enum cyapa_pm_stage pm_stage) { - int ret; + struct input_dev *input = cyapa->input; u8 power; int tries; - u16 sleep_time; + int sleep_time; + int interval; + int ret; if (cyapa->state != CYAPA_STATE_OP) return 0; @@ -977,7 +980,7 @@ static int cyapa_gen3_set_power_mode(struct cyapa *cyapa, u8 power_mode, if ((ret & PWR_MODE_MASK) == power_mode) return 0; - sleep_time = cyapa_get_wait_time_for_pwr_cmd(ret & PWR_MODE_MASK); + sleep_time = (int)cyapa_get_wait_time_for_pwr_cmd(ret & PWR_MODE_MASK); power = ret; power &= ~PWR_MODE_MASK; power |= power_mode & PWR_MODE_MASK; @@ -995,7 +998,23 @@ static int cyapa_gen3_set_power_mode(struct cyapa *cyapa, u8 power_mode, * doing so before issuing the next command may result in errors * depending on the command's content. */ - msleep(sleep_time); + if (cyapa->operational && input && input->users && + (pm_stage == CYAPA_PM_RUNTIME_SUSPEND || + pm_stage == CYAPA_PM_RUNTIME_RESUME)) { + /* Try to polling in 120Hz, read may fail, just ignore it. */ + interval = 1000 / 120; + while (sleep_time > 0) { + if (sleep_time > interval) + msleep(interval); + else + msleep(sleep_time); + sleep_time -= interval; + cyapa_gen3_try_poll_handler(cyapa); + } + } else { + msleep(sleep_time); + } + return ret; } @@ -1112,7 +1131,7 @@ static int cyapa_gen3_do_operational_check(struct cyapa *cyapa) * may cause problems, so we set the power mode first here. */ error = cyapa_gen3_set_power_mode(cyapa, - PWR_MODE_FULL_ACTIVE, 0, false); + PWR_MODE_FULL_ACTIVE, 0, CYAPA_PM_ACTIVE); if (error) dev_err(dev, "%s: set full power mode failed: %d\n", __func__, error); @@ -1168,32 +1187,16 @@ static bool cyapa_gen3_irq_cmd_handler(struct cyapa *cyapa) return false; } -static int cyapa_gen3_irq_handler(struct cyapa *cyapa) +static int cyapa_gen3_event_process(struct cyapa *cyapa, + struct cyapa_reg_data *data) { struct input_dev *input = cyapa->input; - struct device *dev = &cyapa->client->dev; - struct cyapa_reg_data data; int num_fingers; - int ret; int i; - ret = cyapa_read_block(cyapa, CYAPA_CMD_GROUP_DATA, (u8 *)&data); - if (ret != sizeof(data)) { - dev_err(dev, "failed to read report data, (%d)\n", ret); - return -EINVAL; - } - - if ((data.device_status & OP_STATUS_SRC) != OP_STATUS_SRC || - (data.device_status & OP_STATUS_DEV) != CYAPA_DEV_NORMAL || - (data.finger_btn & OP_DATA_VALID) != OP_DATA_VALID) { - dev_err(dev, "invalid device state bytes, %02x %02x\n", - data.device_status, data.finger_btn); - return -EINVAL; - } - - num_fingers = (data.finger_btn >> 4) & 0x0f; + num_fingers = (data->finger_btn >> 4) & 0x0f; for (i = 0; i < num_fingers; i++) { - const struct cyapa_touch *touch = &data.touches[i]; + const struct cyapa_touch *touch = &data->touches[i]; /* Note: touch->id range is 1 to 15; slots are 0 to 14. */ int slot = touch->id - 1; @@ -1210,18 +1213,65 @@ static int cyapa_gen3_irq_handler(struct cyapa *cyapa) if (cyapa->btn_capability & CAPABILITY_LEFT_BTN_MASK) input_report_key(input, BTN_LEFT, - !!(data.finger_btn & OP_DATA_LEFT_BTN)); + !!(data->finger_btn & OP_DATA_LEFT_BTN)); if (cyapa->btn_capability & CAPABILITY_MIDDLE_BTN_MASK) input_report_key(input, BTN_MIDDLE, - !!(data.finger_btn & OP_DATA_MIDDLE_BTN)); + !!(data->finger_btn & OP_DATA_MIDDLE_BTN)); if (cyapa->btn_capability & CAPABILITY_RIGHT_BTN_MASK) input_report_key(input, BTN_RIGHT, - !!(data.finger_btn & OP_DATA_RIGHT_BTN)); + !!(data->finger_btn & OP_DATA_RIGHT_BTN)); input_sync(input); return 0; } +static int cyapa_gen3_irq_handler(struct cyapa *cyapa) +{ + struct device *dev = &cyapa->client->dev; + struct cyapa_reg_data data; + int ret; + + ret = cyapa_read_block(cyapa, CYAPA_CMD_GROUP_DATA, (u8 *)&data); + if (ret != sizeof(data)) { + dev_err(dev, "failed to read report data, (%d)\n", ret); + return -EINVAL; + } + + if ((data.device_status & OP_STATUS_SRC) != OP_STATUS_SRC || + (data.device_status & OP_STATUS_DEV) != CYAPA_DEV_NORMAL || + (data.finger_btn & OP_DATA_VALID) != OP_DATA_VALID) { + dev_err(dev, "invalid device state bytes: %02x %02x\n", + data.device_status, data.finger_btn); + return -EINVAL; + } + + return cyapa_gen3_event_process(cyapa, &data); +} + +/* + * This function will be called in the cyapa_gen3_set_power_mode function, + * and it's known that it may failed in some situation after the set power + * mode command was sent. So this function is aimed to avoid the knwon + * and unwanted output I2C and data parse error messages. + */ +static int cyapa_gen3_try_poll_handler(struct cyapa *cyapa) +{ + struct cyapa_reg_data data; + int ret; + + ret = cyapa_read_block(cyapa, CYAPA_CMD_GROUP_DATA, (u8 *)&data); + if (ret != sizeof(data)) + return -EINVAL; + + if ((data.device_status & OP_STATUS_SRC) != OP_STATUS_SRC || + (data.device_status & OP_STATUS_DEV) != CYAPA_DEV_NORMAL || + (data.finger_btn & OP_DATA_VALID) != OP_DATA_VALID) + return -EINVAL; + + return cyapa_gen3_event_process(cyapa, &data); + +} + static int cyapa_gen3_initialize(struct cyapa *cyapa) { return 0; } static int cyapa_gen3_bl_initiate(struct cyapa *cyapa, const struct firmware *fw) { return 0; } diff --git a/drivers/input/mouse/cyapa_gen5.c b/drivers/input/mouse/cyapa_gen5.c index 118ba9771..5775d40b3 100644 --- a/drivers/input/mouse/cyapa_gen5.c +++ b/drivers/input/mouse/cyapa_gen5.c @@ -342,6 +342,9 @@ u8 pip_bl_read_app_info[] = { 0x04, 0x00, 0x0b, 0x00, 0x40, 0x00, static u8 cyapa_pip_bl_cmd_key[] = { 0xa5, 0x01, 0x02, 0x03, 0xff, 0xfe, 0xfd, 0x5a }; +static int cyapa_pip_event_process(struct cyapa *cyapa, + struct cyapa_pip_report_data *report_data); + int cyapa_pip_cmd_state_initialize(struct cyapa *cyapa) { struct cyapa_pip_cmd_states *pip = &cyapa->cmd_states.pip; @@ -350,6 +353,9 @@ int cyapa_pip_cmd_state_initialize(struct cyapa *cyapa) atomic_set(&pip->cmd_issued, 0); mutex_init(&pip->cmd_lock); + mutex_init(&pip->pm_stage_lock); + pip->pm_stage = CYAPA_PM_DEACTIVE; + pip->resp_sort_func = NULL; pip->in_progress_cmd = PIP_INVALID_CMD; pip->resp_data = NULL; @@ -397,6 +403,38 @@ ssize_t cyapa_i2c_pip_write(struct cyapa *cyapa, u8 *buf, size_t size) return 0; } +static void cyapa_set_pip_pm_state(struct cyapa *cyapa, + enum cyapa_pm_stage pm_stage) +{ + struct cyapa_pip_cmd_states *pip = &cyapa->cmd_states.pip; + + mutex_lock(&pip->pm_stage_lock); + pip->pm_stage = pm_stage; + mutex_unlock(&pip->pm_stage_lock); +} + +static void cyapa_reset_pip_pm_state(struct cyapa *cyapa) +{ + struct cyapa_pip_cmd_states *pip = &cyapa->cmd_states.pip; + + /* Indicates the pip->pm_stage is not valid. */ + mutex_lock(&pip->pm_stage_lock); + pip->pm_stage = CYAPA_PM_DEACTIVE; + mutex_unlock(&pip->pm_stage_lock); +} + +static enum cyapa_pm_stage cyapa_get_pip_pm_state(struct cyapa *cyapa) +{ + struct cyapa_pip_cmd_states *pip = &cyapa->cmd_states.pip; + enum cyapa_pm_stage pm_stage; + + mutex_lock(&pip->pm_stage_lock); + pm_stage = pip->pm_stage; + mutex_unlock(&pip->pm_stage_lock); + + return pm_stage; +} + /** * This function is aimed to dump all not read data in Gen5 trackpad * before send any command, otherwise, the interrupt line will be blocked. @@ -404,7 +442,9 @@ ssize_t cyapa_i2c_pip_write(struct cyapa *cyapa, u8 *buf, size_t size) int cyapa_empty_pip_output_data(struct cyapa *cyapa, u8 *buf, int *len, cb_sort func) { + struct input_dev *input = cyapa->input; struct cyapa_pip_cmd_states *pip = &cyapa->cmd_states.pip; + enum cyapa_pm_stage pm_stage = cyapa_get_pip_pm_state(cyapa); int length; int report_count; int empty_count; @@ -478,6 +518,12 @@ int cyapa_empty_pip_output_data(struct cyapa *cyapa, *len = length; /* Response found, success. */ return 0; + } else if (cyapa->operational && input && input->users && + (pm_stage == CYAPA_PM_RUNTIME_RESUME || + pm_stage == CYAPA_PM_RUNTIME_SUSPEND)) { + /* Parse the data and report it if it's valid. */ + cyapa_pip_event_process(cyapa, + (struct cyapa_pip_report_data *)pip->empty_buf); } error = -EINVAL; @@ -1566,15 +1612,17 @@ int cyapa_pip_deep_sleep(struct cyapa *cyapa, u8 state) } static int cyapa_gen5_set_power_mode(struct cyapa *cyapa, - u8 power_mode, u16 sleep_time, bool is_suspend) + u8 power_mode, u16 sleep_time, enum cyapa_pm_stage pm_stage) { struct device *dev = &cyapa->client->dev; u8 power_state; - int error; + int error = 0; if (cyapa->state != CYAPA_STATE_GEN5_APP) return 0; + cyapa_set_pip_pm_state(cyapa, pm_stage); + if (PIP_DEV_GET_PWR_STATE(cyapa) == UNINIT_PWR_MODE) { /* * Assume TP in deep sleep mode when driver is loaded, @@ -1597,7 +1645,7 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa, power_mode == PWR_MODE_BTN_ONLY || PIP_DEV_GET_SLEEP_TIME(cyapa) == sleep_time) { /* Has in correct power mode state, early return. */ - return 0; + goto out; } } @@ -1605,11 +1653,11 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa, error = cyapa_pip_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_OFF); if (error) { dev_err(dev, "enter deep sleep fail: %d\n", error); - return error; + goto out; } PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_OFF); - return 0; + goto out; } /* @@ -1621,7 +1669,7 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa, error = cyapa_pip_deep_sleep(cyapa, PIP_DEEP_SLEEP_STATE_ON); if (error) { dev_err(dev, "deep sleep wake fail: %d\n", error); - return error; + goto out; } } @@ -1630,7 +1678,7 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa, GEN5_POWER_STATE_ACTIVE); if (error) { dev_err(dev, "change to active fail: %d\n", error); - return error; + goto out; } PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_FULL_ACTIVE); @@ -1639,7 +1687,7 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa, GEN5_POWER_STATE_BTN_ONLY); if (error) { dev_err(dev, "fail to button only mode: %d\n", error); - return error; + goto out; } PIP_DEV_SET_PWR_STATE(cyapa, PWR_MODE_BTN_ONLY); @@ -1664,7 +1712,7 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa, if (error) { dev_err(dev, "set power state to 0x%02x failed: %d\n", power_state, error); - return error; + goto out; } /* @@ -1677,14 +1725,16 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa, * is suspending which may cause interrupt line unable to be * asserted again. */ - if (is_suspend) + if (pm_stage == CYAPA_PM_SUSPEND) cyapa_gen5_disable_pip_report(cyapa); PIP_DEV_SET_PWR_STATE(cyapa, cyapa_sleep_time_to_pwr_cmd(sleep_time)); } - return 0; +out: + cyapa_reset_pip_pm_state(cyapa); + return error; } int cyapa_pip_resume_scanning(struct cyapa *cyapa) @@ -2513,7 +2563,7 @@ static int cyapa_gen5_do_operational_check(struct cyapa *cyapa) * the device state is required. */ error = cyapa_gen5_set_power_mode(cyapa, - PWR_MODE_FULL_ACTIVE, 0, false); + PWR_MODE_FULL_ACTIVE, 0, CYAPA_PM_ACTIVE); if (error) dev_warn(dev, "%s: failed to set power active mode.\n", __func__); @@ -2715,7 +2765,6 @@ int cyapa_pip_irq_handler(struct cyapa *cyapa) struct device *dev = &cyapa->client->dev; struct cyapa_pip_report_data report_data; unsigned int report_len; - u8 report_id; int ret; if (!cyapa_is_pip_app_mode(cyapa)) { @@ -2752,7 +2801,23 @@ int cyapa_pip_irq_handler(struct cyapa *cyapa) return -EINVAL; } - report_id = report_data.report_head[PIP_RESP_REPORT_ID_OFFSET]; + return cyapa_pip_event_process(cyapa, &report_data); +} + +static int cyapa_pip_event_process(struct cyapa *cyapa, + struct cyapa_pip_report_data *report_data) +{ + struct device *dev = &cyapa->client->dev; + unsigned int report_len; + u8 report_id; + + report_len = get_unaligned_le16( + &report_data->report_head[PIP_RESP_LENGTH_OFFSET]); + /* Idle, no data for report. */ + if (report_len == PIP_RESP_LENGTH_SIZE) + return 0; + + report_id = report_data->report_head[PIP_RESP_REPORT_ID_OFFSET]; if (report_id == PIP_WAKEUP_EVENT_REPORT_ID && report_len == PIP_WAKEUP_EVENT_SIZE) { /* @@ -2805,11 +2870,11 @@ int cyapa_pip_irq_handler(struct cyapa *cyapa) } if (report_id == PIP_TOUCH_REPORT_ID) - cyapa_pip_report_touches(cyapa, &report_data); + cyapa_pip_report_touches(cyapa, report_data); else if (report_id == PIP_PROXIMITY_REPORT_ID) - cyapa_pip_report_proximity(cyapa, &report_data); + cyapa_pip_report_proximity(cyapa, report_data); else - cyapa_pip_report_buttons(cyapa, &report_data); + cyapa_pip_report_buttons(cyapa, report_data); return 0; } diff --git a/drivers/input/mouse/cyapa_gen6.c b/drivers/input/mouse/cyapa_gen6.c index e4eb048d1..016397850 100644 --- a/drivers/input/mouse/cyapa_gen6.c +++ b/drivers/input/mouse/cyapa_gen6.c @@ -425,7 +425,7 @@ static int cyapa_gen6_deep_sleep(struct cyapa *cyapa, u8 state) } static int cyapa_gen6_set_power_mode(struct cyapa *cyapa, - u8 power_mode, u16 sleep_time, bool is_suspend) + u8 power_mode, u16 sleep_time, enum cyapa_pm_stage pm_stage) { struct device *dev = &cyapa->client->dev; struct gen6_interval_setting *interval_setting = @@ -689,7 +689,7 @@ static int cyapa_gen6_operational_check(struct cyapa *cyapa) * the device state is required. */ error = cyapa_gen6_set_power_mode(cyapa, - PWR_MODE_FULL_ACTIVE, 0, false); + PWR_MODE_FULL_ACTIVE, 0, CYAPA_PM_ACTIVE); if (error) dev_warn(dev, "%s: failed to set power active mode.\n", __func__); diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index b9e4ee34c..5784e2054 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -37,6 +37,7 @@ #include "cypress_ps2.h" #include "focaltech.h" #include "vmmouse.h" +#include "byd.h" #define DRIVER_DESC "PS/2 mouse driver" @@ -842,6 +843,15 @@ static const struct psmouse_protocol psmouse_protocols[] = { .init = vmmouse_init, }, #endif +#ifdef CONFIG_MOUSE_PS2_BYD + { + .type = PSMOUSE_BYD, + .name = "BYDPS/2", + .alias = "byd", + .detect = byd_detect, + .init = byd_init, + }, +#endif { .type = PSMOUSE_AUTO, .name = "auto", @@ -1105,6 +1115,10 @@ static int psmouse_extensions(struct psmouse *psmouse, if (psmouse_try_protocol(psmouse, PSMOUSE_TOUCHKIT_PS2, &max_proto, set_properties, true)) return PSMOUSE_TOUCHKIT_PS2; + + if (psmouse_try_protocol(psmouse, PSMOUSE_BYD, + &max_proto, set_properties, true)) + return PSMOUSE_BYD; } /* diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index ad5a5a1ea..e0ca6cda3 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -104,6 +104,7 @@ enum psmouse_type { PSMOUSE_CYPRESS, PSMOUSE_FOCALTECH, PSMOUSE_VMMOUSE, + PSMOUSE_BYD, PSMOUSE_AUTO /* This one should always be last */ }; diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig new file mode 100644 index 000000000..f73df2495 --- /dev/null +++ b/drivers/input/rmi4/Kconfig @@ -0,0 +1,63 @@ +# +# RMI4 configuration +# +config RMI4_CORE + tristate "Synaptics RMI4 bus support" + help + Say Y here if you want to support the Synaptics RMI4 bus. This is + required for all RMI4 device support. + + If unsure, say Y. + +config RMI4_I2C + tristate "RMI4 I2C Support" + depends on RMI4_CORE && I2C + help + Say Y here if you want to support RMI4 devices connected to an I2C + bus. + + If unsure, say Y. + +config RMI4_SPI + tristate "RMI4 SPI Support" + depends on RMI4_CORE && SPI + help + Say Y here if you want to support RMI4 devices connected to a SPI + bus. + + If unsure, say N. + +config RMI4_2D_SENSOR + bool + depends on RMI4_CORE + +config RMI4_F11 + bool "RMI4 Function 11 (2D pointing)" + select RMI4_2D_SENSOR + depends on RMI4_CORE + help + Say Y here if you want to add support for RMI4 function 11. + + Function 11 provides 2D multifinger pointing for touchscreens and + touchpads. For sensors that support relative pointing, F11 also + provides mouse input. + +config RMI4_F12 + bool "RMI4 Function 12 (2D pointing)" + select RMI4_2D_SENSOR + depends on RMI4_CORE + help + Say Y here if you want to add support for RMI4 function 12. + + Function 12 provides 2D multifinger pointing for touchscreens and + touchpads. For sensors that support relative pointing, F12 also + provides mouse input. + +config RMI4_F30 + bool "RMI4 Function 30 (GPIO LED)" + depends on RMI4_CORE + help + Say Y here if you want to add support for RMI4 function 30. + + Function 30 provides GPIO and LED support for RMI4 devices. This + includes support for buttons on TouchPads and ClickPads. diff --git a/drivers/input/rmi4/Makefile b/drivers/input/rmi4/Makefile new file mode 100644 index 000000000..95c00a783 --- /dev/null +++ b/drivers/input/rmi4/Makefile @@ -0,0 +1,13 @@ +obj-$(CONFIG_RMI4_CORE) += rmi_core.o +rmi_core-y := rmi_bus.o rmi_driver.o rmi_f01.o + +rmi_core-$(CONFIG_RMI4_2D_SENSOR) += rmi_2d_sensor.o + +# Function drivers +rmi_core-$(CONFIG_RMI4_F11) += rmi_f11.o +rmi_core-$(CONFIG_RMI4_F12) += rmi_f12.o +rmi_core-$(CONFIG_RMI4_F30) += rmi_f30.o + +# Transports +obj-$(CONFIG_RMI4_I2C) += rmi_i2c.o +obj-$(CONFIG_RMI4_SPI) += rmi_spi.o diff --git a/drivers/input/rmi4/rmi_2d_sensor.c b/drivers/input/rmi4/rmi_2d_sensor.c new file mode 100644 index 000000000..e97bd7fab --- /dev/null +++ b/drivers/input/rmi4/rmi_2d_sensor.c @@ -0,0 +1,329 @@ +/* + * Copyright (c) 2011-2016 Synaptics Incorporated + * Copyright (c) 2011 Unixphere + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/of.h> +#include <linux/input.h> +#include <linux/input/mt.h> +#include <linux/rmi.h> +#include "rmi_driver.h" +#include "rmi_2d_sensor.h" + +#define RMI_2D_REL_POS_MIN -128 +#define RMI_2D_REL_POS_MAX 127 + +/* maximum ABS_MT_POSITION displacement (in mm) */ +#define DMAX 10 + +void rmi_2d_sensor_abs_process(struct rmi_2d_sensor *sensor, + struct rmi_2d_sensor_abs_object *obj, + int slot) +{ + struct rmi_2d_axis_alignment *axis_align = &sensor->axis_align; + + /* we keep the previous values if the finger is released */ + if (obj->type == RMI_2D_OBJECT_NONE) + return; + + if (axis_align->swap_axes) + swap(obj->x, obj->y); + + if (axis_align->flip_x) + obj->x = sensor->max_x - obj->x; + + if (axis_align->flip_y) + obj->y = sensor->max_y - obj->y; + + /* + * Here checking if X offset or y offset are specified is + * redundant. We just add the offsets or clip the values. + * + * Note: offsets need to be applied before clipping occurs, + * or we could get funny values that are outside of + * clipping boundaries. + */ + obj->x += axis_align->offset_x; + obj->y += axis_align->offset_y; + + obj->x = max(axis_align->clip_x_low, obj->x); + obj->y = max(axis_align->clip_y_low, obj->y); + + if (axis_align->clip_x_high) + obj->x = min(sensor->max_x, obj->x); + + if (axis_align->clip_y_high) + obj->y = min(sensor->max_y, obj->y); + + sensor->tracking_pos[slot].x = obj->x; + sensor->tracking_pos[slot].y = obj->y; +} +EXPORT_SYMBOL_GPL(rmi_2d_sensor_abs_process); + +void rmi_2d_sensor_abs_report(struct rmi_2d_sensor *sensor, + struct rmi_2d_sensor_abs_object *obj, + int slot) +{ + struct rmi_2d_axis_alignment *axis_align = &sensor->axis_align; + struct input_dev *input = sensor->input; + int wide, major, minor; + + if (sensor->kernel_tracking) + input_mt_slot(input, sensor->tracking_slots[slot]); + else + input_mt_slot(input, slot); + + input_mt_report_slot_state(input, obj->mt_tool, + obj->type != RMI_2D_OBJECT_NONE); + + if (obj->type != RMI_2D_OBJECT_NONE) { + obj->x = sensor->tracking_pos[slot].x; + obj->y = sensor->tracking_pos[slot].y; + + if (axis_align->swap_axes) + swap(obj->wx, obj->wy); + + wide = (obj->wx > obj->wy); + major = max(obj->wx, obj->wy); + minor = min(obj->wx, obj->wy); + + if (obj->type == RMI_2D_OBJECT_STYLUS) { + major = max(1, major); + minor = max(1, minor); + } + + input_event(sensor->input, EV_ABS, ABS_MT_POSITION_X, obj->x); + input_event(sensor->input, EV_ABS, ABS_MT_POSITION_Y, obj->y); + input_event(sensor->input, EV_ABS, ABS_MT_ORIENTATION, wide); + input_event(sensor->input, EV_ABS, ABS_MT_PRESSURE, obj->z); + input_event(sensor->input, EV_ABS, ABS_MT_TOUCH_MAJOR, major); + input_event(sensor->input, EV_ABS, ABS_MT_TOUCH_MINOR, minor); + + rmi_dbg(RMI_DEBUG_2D_SENSOR, &sensor->input->dev, + "%s: obj[%d]: type: 0x%02x X: %d Y: %d Z: %d WX: %d WY: %d\n", + __func__, slot, obj->type, obj->x, obj->y, obj->z, + obj->wx, obj->wy); + } +} +EXPORT_SYMBOL_GPL(rmi_2d_sensor_abs_report); + +void rmi_2d_sensor_rel_report(struct rmi_2d_sensor *sensor, int x, int y) +{ + struct rmi_2d_axis_alignment *axis_align = &sensor->axis_align; + + x = min(RMI_2D_REL_POS_MAX, max(RMI_2D_REL_POS_MIN, (int)x)); + y = min(RMI_2D_REL_POS_MAX, max(RMI_2D_REL_POS_MIN, (int)y)); + + if (axis_align->swap_axes) + swap(x, y); + + if (axis_align->flip_x) + x = min(RMI_2D_REL_POS_MAX, -x); + + if (axis_align->flip_y) + y = min(RMI_2D_REL_POS_MAX, -y); + + if (x || y) { + input_report_rel(sensor->input, REL_X, x); + input_report_rel(sensor->input, REL_Y, y); + } +} +EXPORT_SYMBOL_GPL(rmi_2d_sensor_rel_report); + +static void rmi_2d_sensor_set_input_params(struct rmi_2d_sensor *sensor) +{ + struct input_dev *input = sensor->input; + int res_x; + int res_y; + int input_flags = 0; + + if (sensor->report_abs) { + if (sensor->axis_align.swap_axes) + swap(sensor->max_x, sensor->max_y); + + sensor->min_x = sensor->axis_align.clip_x_low; + if (sensor->axis_align.clip_x_high) + sensor->max_x = min(sensor->max_x, + sensor->axis_align.clip_x_high); + + sensor->min_y = sensor->axis_align.clip_y_low; + if (sensor->axis_align.clip_y_high) + sensor->max_y = min(sensor->max_y, + sensor->axis_align.clip_y_high); + + set_bit(EV_ABS, input->evbit); + input_set_abs_params(input, ABS_MT_POSITION_X, 0, sensor->max_x, + 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, sensor->max_y, + 0, 0); + + if (sensor->x_mm && sensor->y_mm) { + res_x = (sensor->max_x - sensor->min_x) / sensor->x_mm; + res_y = (sensor->max_y - sensor->min_y) / sensor->y_mm; + + input_abs_set_res(input, ABS_X, res_x); + input_abs_set_res(input, ABS_Y, res_y); + + input_abs_set_res(input, ABS_MT_POSITION_X, res_x); + input_abs_set_res(input, ABS_MT_POSITION_Y, res_y); + + if (!sensor->dmax) + sensor->dmax = DMAX * res_x; + } + + input_set_abs_params(input, ABS_MT_PRESSURE, 0, 0xff, 0, 0); + input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 0x0f, 0, 0); + input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 0x0f, 0, 0); + input_set_abs_params(input, ABS_MT_ORIENTATION, 0, 1, 0, 0); + + if (sensor->sensor_type == rmi_sensor_touchpad) + input_flags = INPUT_MT_POINTER; + else + input_flags = INPUT_MT_DIRECT; + + if (sensor->kernel_tracking) + input_flags |= INPUT_MT_TRACK; + + input_mt_init_slots(input, sensor->nbr_fingers, input_flags); + } + + if (sensor->report_rel) { + set_bit(EV_REL, input->evbit); + set_bit(REL_X, input->relbit); + set_bit(REL_Y, input->relbit); + } + + if (sensor->topbuttonpad) + set_bit(INPUT_PROP_TOPBUTTONPAD, input->propbit); +} +EXPORT_SYMBOL_GPL(rmi_2d_sensor_set_input_params); + +int rmi_2d_sensor_configure_input(struct rmi_function *fn, + struct rmi_2d_sensor *sensor) +{ + struct rmi_device *rmi_dev = fn->rmi_dev; + struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev); + + if (!drv_data->input) + return -ENODEV; + + sensor->input = drv_data->input; + rmi_2d_sensor_set_input_params(sensor); + + return 0; +} +EXPORT_SYMBOL_GPL(rmi_2d_sensor_configure_input); + +#ifdef CONFIG_OF +int rmi_2d_sensor_of_probe(struct device *dev, + struct rmi_2d_sensor_platform_data *pdata) +{ + int retval; + u32 val; + + pdata->axis_align.swap_axes = of_property_read_bool(dev->of_node, + "touchscreen-swapped-x-y"); + + pdata->axis_align.flip_x = of_property_read_bool(dev->of_node, + "touchscreen-inverted-x"); + + pdata->axis_align.flip_y = of_property_read_bool(dev->of_node, + "touchscreen-inverted-y"); + + retval = rmi_of_property_read_u32(dev, &val, "syna,clip-x-low", 1); + if (retval) + return retval; + + pdata->axis_align.clip_x_low = val; + + retval = rmi_of_property_read_u32(dev, &val, "syna,clip-y-low", 1); + if (retval) + return retval; + + pdata->axis_align.clip_y_low = val; + + retval = rmi_of_property_read_u32(dev, &val, "syna,clip-x-high", 1); + if (retval) + return retval; + + pdata->axis_align.clip_x_high = val; + + retval = rmi_of_property_read_u32(dev, &val, "syna,clip-y-high", 1); + if (retval) + return retval; + + pdata->axis_align.clip_y_high = val; + + retval = rmi_of_property_read_u32(dev, &val, "syna,offset-x", 1); + if (retval) + return retval; + + pdata->axis_align.offset_x = val; + + retval = rmi_of_property_read_u32(dev, &val, "syna,offset-y", 1); + if (retval) + return retval; + + pdata->axis_align.offset_y = val; + + retval = rmi_of_property_read_u32(dev, &val, "syna,delta-x-threshold", + 1); + if (retval) + return retval; + + pdata->axis_align.delta_x_threshold = val; + + retval = rmi_of_property_read_u32(dev, &val, "syna,delta-y-threshold", + 1); + if (retval) + return retval; + + pdata->axis_align.delta_y_threshold = val; + + retval = rmi_of_property_read_u32(dev, (u32 *)&pdata->sensor_type, + "syna,sensor-type", 1); + if (retval) + return retval; + + retval = rmi_of_property_read_u32(dev, &val, "touchscreen-x-mm", 1); + if (retval) + return retval; + + pdata->x_mm = val; + + retval = rmi_of_property_read_u32(dev, &val, "touchscreen-y-mm", 1); + if (retval) + return retval; + + pdata->y_mm = val; + + retval = rmi_of_property_read_u32(dev, &val, + "syna,disable-report-mask", 1); + if (retval) + return retval; + + pdata->disable_report_mask = val; + + retval = rmi_of_property_read_u32(dev, &val, "syna,rezero-wait-ms", + 1); + if (retval) + return retval; + + pdata->rezero_wait = val; + + return 0; +} +#else +inline int rmi_2d_sensor_of_probe(struct device *dev, + struct rmi_2d_sensor_platform_data *pdata) +{ + return -ENODEV; +} +#endif +EXPORT_SYMBOL_GPL(rmi_2d_sensor_of_probe); diff --git a/drivers/input/rmi4/rmi_2d_sensor.h b/drivers/input/rmi4/rmi_2d_sensor.h new file mode 100644 index 000000000..77fcdfef0 --- /dev/null +++ b/drivers/input/rmi4/rmi_2d_sensor.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2011-2016 Synaptics Incorporated + * Copyright (c) 2011 Unixphere + * + * 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. + */ + +#ifndef _RMI_2D_SENSOR_H +#define _RMI_2D_SENSOR_H + +enum rmi_2d_sensor_object_type { + RMI_2D_OBJECT_NONE, + RMI_2D_OBJECT_FINGER, + RMI_2D_OBJECT_STYLUS, + RMI_2D_OBJECT_PALM, + RMI_2D_OBJECT_UNCLASSIFIED, +}; + +struct rmi_2d_sensor_abs_object { + enum rmi_2d_sensor_object_type type; + int mt_tool; + u16 x; + u16 y; + u8 z; + u8 wx; + u8 wy; +}; + +/** + * @axis_align - controls parameters that are useful in system prototyping + * and bring up. + * @max_x - The maximum X coordinate that will be reported by this sensor. + * @max_y - The maximum Y coordinate that will be reported by this sensor. + * @nbr_fingers - How many fingers can this sensor report? + * @data_pkt - buffer for data reported by this sensor. + * @pkt_size - number of bytes in that buffer. + * @attn_size - Size of the HID attention report (only contains abs data). + * position when two fingers are on the device. When this is true, we + * assume we have one of those sensors and report events appropriately. + * @sensor_type - indicates whether we're touchscreen or touchpad. + * @input - input device for absolute pointing stream + * @input_phys - buffer for the absolute phys name for this sensor. + */ +struct rmi_2d_sensor { + struct rmi_2d_axis_alignment axis_align; + struct input_mt_pos *tracking_pos; + int *tracking_slots; + bool kernel_tracking; + struct rmi_2d_sensor_abs_object *objs; + int dmax; + u16 min_x; + u16 max_x; + u16 min_y; + u16 max_y; + u8 nbr_fingers; + u8 *data_pkt; + int pkt_size; + int attn_size; + bool topbuttonpad; + enum rmi_sensor_type sensor_type; + struct input_dev *input; + struct rmi_function *fn; + char input_phys[32]; + u8 report_abs; + u8 report_rel; + u8 x_mm; + u8 y_mm; +}; + +int rmi_2d_sensor_of_probe(struct device *dev, + struct rmi_2d_sensor_platform_data *pdata); + +void rmi_2d_sensor_abs_process(struct rmi_2d_sensor *sensor, + struct rmi_2d_sensor_abs_object *obj, + int slot); + +void rmi_2d_sensor_abs_report(struct rmi_2d_sensor *sensor, + struct rmi_2d_sensor_abs_object *obj, + int slot); + +void rmi_2d_sensor_rel_report(struct rmi_2d_sensor *sensor, int x, int y); + +int rmi_2d_sensor_configure_input(struct rmi_function *fn, + struct rmi_2d_sensor *sensor); +#endif /* _RMI_2D_SENSOR_H */ diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c new file mode 100644 index 000000000..b368b0515 --- /dev/null +++ b/drivers/input/rmi4/rmi_bus.c @@ -0,0 +1,419 @@ +/* + * Copyright (c) 2011-2016 Synaptics Incorporated + * Copyright (c) 2011 Unixphere + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/kconfig.h> +#include <linux/list.h> +#include <linux/pm.h> +#include <linux/rmi.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/of.h> +#include "rmi_bus.h" +#include "rmi_driver.h" + +static int debug_flags; +module_param(debug_flags, int, 0644); +MODULE_PARM_DESC(debug_flags, "control debugging information"); + +void rmi_dbg(int flags, struct device *dev, const char *fmt, ...) +{ + struct va_format vaf; + va_list args; + + if (flags & debug_flags) { + va_start(args, fmt); + + vaf.fmt = fmt; + vaf.va = &args; + + dev_printk(KERN_DEBUG, dev, "%pV", &vaf); + + va_end(args); + } +} +EXPORT_SYMBOL_GPL(rmi_dbg); + +/* + * RMI Physical devices + * + * Physical RMI device consists of several functions serving particular + * purpose. For example F11 is a 2D touch sensor while F01 is a generic + * function present in every RMI device. + */ + +static void rmi_release_device(struct device *dev) +{ + struct rmi_device *rmi_dev = to_rmi_device(dev); + + kfree(rmi_dev); +} + +static struct device_type rmi_device_type = { + .name = "rmi4_sensor", + .release = rmi_release_device, +}; + +bool rmi_is_physical_device(struct device *dev) +{ + return dev->type == &rmi_device_type; +} + +/** + * rmi_register_transport_device - register a transport device connection + * on the RMI bus. Transport drivers provide communication from the devices + * on a bus (such as SPI, I2C, and so on) to the RMI4 sensor. + * + * @xport: the transport device to register + */ +int rmi_register_transport_device(struct rmi_transport_dev *xport) +{ + static atomic_t transport_device_count = ATOMIC_INIT(0); + struct rmi_device *rmi_dev; + int error; + + rmi_dev = kzalloc(sizeof(struct rmi_device), GFP_KERNEL); + if (!rmi_dev) + return -ENOMEM; + + device_initialize(&rmi_dev->dev); + + rmi_dev->xport = xport; + rmi_dev->number = atomic_inc_return(&transport_device_count) - 1; + + dev_set_name(&rmi_dev->dev, "rmi4-%02d", rmi_dev->number); + + rmi_dev->dev.bus = &rmi_bus_type; + rmi_dev->dev.type = &rmi_device_type; + + xport->rmi_dev = rmi_dev; + + error = device_add(&rmi_dev->dev); + if (error) + goto err_put_device; + + rmi_dbg(RMI_DEBUG_CORE, xport->dev, + "%s: Registered %s as %s.\n", __func__, + dev_name(rmi_dev->xport->dev), dev_name(&rmi_dev->dev)); + + return 0; + +err_put_device: + put_device(&rmi_dev->dev); + return error; +} +EXPORT_SYMBOL_GPL(rmi_register_transport_device); + +/** + * rmi_unregister_transport_device - unregister a transport device connection + * @xport: the transport driver to unregister + * + */ +void rmi_unregister_transport_device(struct rmi_transport_dev *xport) +{ + struct rmi_device *rmi_dev = xport->rmi_dev; + + device_del(&rmi_dev->dev); + put_device(&rmi_dev->dev); +} +EXPORT_SYMBOL(rmi_unregister_transport_device); + + +/* Function specific stuff */ + +static void rmi_release_function(struct device *dev) +{ + struct rmi_function *fn = to_rmi_function(dev); + + kfree(fn); +} + +static struct device_type rmi_function_type = { + .name = "rmi4_function", + .release = rmi_release_function, +}; + +bool rmi_is_function_device(struct device *dev) +{ + return dev->type == &rmi_function_type; +} + +static int rmi_function_match(struct device *dev, struct device_driver *drv) +{ + struct rmi_function_handler *handler = to_rmi_function_handler(drv); + struct rmi_function *fn = to_rmi_function(dev); + + return fn->fd.function_number == handler->func; +} + +#ifdef CONFIG_OF +static void rmi_function_of_probe(struct rmi_function *fn) +{ + char of_name[9]; + + snprintf(of_name, sizeof(of_name), "rmi4-f%02x", + fn->fd.function_number); + fn->dev.of_node = of_find_node_by_name( + fn->rmi_dev->xport->dev->of_node, of_name); +} +#else +static inline void rmi_function_of_probe(struct rmi_function *fn) +{} +#endif + +static int rmi_function_probe(struct device *dev) +{ + struct rmi_function *fn = to_rmi_function(dev); + struct rmi_function_handler *handler = + to_rmi_function_handler(dev->driver); + int error; + + rmi_function_of_probe(fn); + + if (handler->probe) { + error = handler->probe(fn); + return error; + } + + return 0; +} + +static int rmi_function_remove(struct device *dev) +{ + struct rmi_function *fn = to_rmi_function(dev); + struct rmi_function_handler *handler = + to_rmi_function_handler(dev->driver); + + if (handler->remove) + handler->remove(fn); + + return 0; +} + +int rmi_register_function(struct rmi_function *fn) +{ + struct rmi_device *rmi_dev = fn->rmi_dev; + int error; + + device_initialize(&fn->dev); + + dev_set_name(&fn->dev, "%s.fn%02x", + dev_name(&rmi_dev->dev), fn->fd.function_number); + + fn->dev.parent = &rmi_dev->dev; + fn->dev.type = &rmi_function_type; + fn->dev.bus = &rmi_bus_type; + + error = device_add(&fn->dev); + if (error) { + dev_err(&rmi_dev->dev, + "Failed device_register function device %s\n", + dev_name(&fn->dev)); + goto err_put_device; + } + + rmi_dbg(RMI_DEBUG_CORE, &rmi_dev->dev, "Registered F%02X.\n", + fn->fd.function_number); + + return 0; + +err_put_device: + put_device(&fn->dev); + return error; +} + +void rmi_unregister_function(struct rmi_function *fn) +{ + device_del(&fn->dev); + + if (fn->dev.of_node) + of_node_put(fn->dev.of_node); + + put_device(&fn->dev); +} + +/** + * rmi_register_function_handler - register a handler for an RMI function + * @handler: RMI handler that should be registered. + * @module: pointer to module that implements the handler + * @mod_name: name of the module implementing the handler + * + * This function performs additional setup of RMI function handler and + * registers it with the RMI core so that it can be bound to + * RMI function devices. + */ +int __rmi_register_function_handler(struct rmi_function_handler *handler, + struct module *owner, + const char *mod_name) +{ + struct device_driver *driver = &handler->driver; + int error; + + driver->bus = &rmi_bus_type; + driver->owner = owner; + driver->mod_name = mod_name; + driver->probe = rmi_function_probe; + driver->remove = rmi_function_remove; + + error = driver_register(&handler->driver); + if (error) { + pr_err("driver_register() failed for %s, error: %d\n", + handler->driver.name, error); + return error; + } + + return 0; +} +EXPORT_SYMBOL_GPL(__rmi_register_function_handler); + +/** + * rmi_unregister_function_handler - unregister given RMI function handler + * @handler: RMI handler that should be unregistered. + * + * This function unregisters given function handler from RMI core which + * causes it to be unbound from the function devices. + */ +void rmi_unregister_function_handler(struct rmi_function_handler *handler) +{ + driver_unregister(&handler->driver); +} +EXPORT_SYMBOL_GPL(rmi_unregister_function_handler); + +/* Bus specific stuff */ + +static int rmi_bus_match(struct device *dev, struct device_driver *drv) +{ + bool physical = rmi_is_physical_device(dev); + + /* First see if types are not compatible */ + if (physical != rmi_is_physical_driver(drv)) + return 0; + + return physical || rmi_function_match(dev, drv); +} + +struct bus_type rmi_bus_type = { + .match = rmi_bus_match, + .name = "rmi4", +}; + +static struct rmi_function_handler *fn_handlers[] = { + &rmi_f01_handler, +#ifdef CONFIG_RMI4_F11 + &rmi_f11_handler, +#endif +#ifdef CONFIG_RMI4_F12 + &rmi_f12_handler, +#endif +#ifdef CONFIG_RMI4_F30 + &rmi_f30_handler, +#endif +}; + +static void __rmi_unregister_function_handlers(int start_idx) +{ + int i; + + for (i = start_idx; i >= 0; i--) + rmi_unregister_function_handler(fn_handlers[i]); +} + +static void rmi_unregister_function_handlers(void) +{ + __rmi_unregister_function_handlers(ARRAY_SIZE(fn_handlers) - 1); +} + +static int rmi_register_function_handlers(void) +{ + int ret; + int i; + + for (i = 0; i < ARRAY_SIZE(fn_handlers); i++) { + ret = rmi_register_function_handler(fn_handlers[i]); + if (ret) { + pr_err("%s: error registering the RMI F%02x handler: %d\n", + __func__, fn_handlers[i]->func, ret); + goto err_unregister_function_handlers; + } + } + + return 0; + +err_unregister_function_handlers: + __rmi_unregister_function_handlers(i - 1); + return ret; +} + +int rmi_of_property_read_u32(struct device *dev, u32 *result, + const char *prop, bool optional) +{ + int retval; + u32 val = 0; + + retval = of_property_read_u32(dev->of_node, prop, &val); + if (retval && (!optional && retval == -EINVAL)) { + dev_err(dev, "Failed to get %s value: %d\n", + prop, retval); + return retval; + } + *result = val; + + return 0; +} +EXPORT_SYMBOL_GPL(rmi_of_property_read_u32); + +static int __init rmi_bus_init(void) +{ + int error; + + error = bus_register(&rmi_bus_type); + if (error) { + pr_err("%s: error registering the RMI bus: %d\n", + __func__, error); + return error; + } + + error = rmi_register_function_handlers(); + if (error) + goto err_unregister_bus; + + error = rmi_register_physical_driver(); + if (error) { + pr_err("%s: error registering the RMI physical driver: %d\n", + __func__, error); + goto err_unregister_bus; + } + + return 0; + +err_unregister_bus: + bus_unregister(&rmi_bus_type); + return error; +} +module_init(rmi_bus_init); + +static void __exit rmi_bus_exit(void) +{ + /* + * We should only ever get here if all drivers are unloaded, so + * all we have to do at this point is unregister ourselves. + */ + + rmi_unregister_physical_driver(); + rmi_unregister_function_handlers(); + bus_unregister(&rmi_bus_type); +} +module_exit(rmi_bus_exit); + +MODULE_AUTHOR("Christopher Heiny <cheiny@synaptics.com"); +MODULE_AUTHOR("Andrew Duggan <aduggan@synaptics.com"); +MODULE_DESCRIPTION("RMI bus"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(RMI_DRIVER_VERSION); diff --git a/drivers/input/rmi4/rmi_bus.h b/drivers/input/rmi4/rmi_bus.h new file mode 100644 index 000000000..899579830 --- /dev/null +++ b/drivers/input/rmi4/rmi_bus.h @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2011-2016 Synaptics Incorporated + * Copyright (c) 2011 Unixphere + * + * 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. + */ + +#ifndef _RMI_BUS_H +#define _RMI_BUS_H + +#include <linux/rmi.h> + +struct rmi_device; + +/** + * struct rmi_function - represents the implementation of an RMI4 + * function for a particular device (basically, a driver for that RMI4 function) + * + * @fd: The function descriptor of the RMI function + * @rmi_dev: Pointer to the RMI device associated with this function container + * @dev: The device associated with this particular function. + * + * @num_of_irqs: The number of irqs needed by this function + * @irq_pos: The position in the irq bitfield this function holds + * @irq_mask: For convenience, can be used to mask IRQ bits off during ATTN + * interrupt handling. + * + * @node: entry in device's list of functions + */ +struct rmi_function { + struct rmi_function_descriptor fd; + struct rmi_device *rmi_dev; + struct device dev; + struct list_head node; + + unsigned int num_of_irqs; + unsigned int irq_pos; + unsigned long irq_mask[]; +}; + +#define to_rmi_function(d) container_of(d, struct rmi_function, dev) + +bool rmi_is_function_device(struct device *dev); + +int __must_check rmi_register_function(struct rmi_function *); +void rmi_unregister_function(struct rmi_function *); + +/** + * struct rmi_function_handler - driver routines for a particular RMI function. + * + * @func: The RMI function number + * @reset: Called when a reset of the touch sensor is detected. The routine + * should perform any out-of-the-ordinary reset handling that might be + * necessary. Restoring of touch sensor configuration registers should be + * handled in the config() callback, below. + * @config: Called when the function container is first initialized, and + * after a reset is detected. This routine should write any necessary + * configuration settings to the device. + * @attention: Called when the IRQ(s) for the function are set by the touch + * sensor. + * @suspend: Should perform any required operations to suspend the particular + * function. + * @resume: Should perform any required operations to resume the particular + * function. + * + * All callbacks are expected to return 0 on success, error code on failure. + */ +struct rmi_function_handler { + struct device_driver driver; + + u8 func; + + int (*probe)(struct rmi_function *fn); + void (*remove)(struct rmi_function *fn); + int (*config)(struct rmi_function *fn); + int (*reset)(struct rmi_function *fn); + int (*attention)(struct rmi_function *fn, unsigned long *irq_bits); + int (*suspend)(struct rmi_function *fn); + int (*resume)(struct rmi_function *fn); +}; + +#define to_rmi_function_handler(d) \ + container_of(d, struct rmi_function_handler, driver) + +int __must_check __rmi_register_function_handler(struct rmi_function_handler *, + struct module *, const char *); +#define rmi_register_function_handler(handler) \ + __rmi_register_function_handler(handler, THIS_MODULE, KBUILD_MODNAME) + +void rmi_unregister_function_handler(struct rmi_function_handler *); + +#define to_rmi_driver(d) \ + container_of(d, struct rmi_driver, driver) + +#define to_rmi_device(d) container_of(d, struct rmi_device, dev) + +static inline struct rmi_device_platform_data * +rmi_get_platform_data(struct rmi_device *d) +{ + return &d->xport->pdata; +} + +bool rmi_is_physical_device(struct device *dev); + +/** + * rmi_read - read a single byte + * @d: Pointer to an RMI device + * @addr: The address to read from + * @buf: The read buffer + * + * Reads a single byte of data using the underlying transport protocol + * into memory pointed by @buf. It returns 0 on success or a negative + * error code. + */ +static inline int rmi_read(struct rmi_device *d, u16 addr, u8 *buf) +{ + return d->xport->ops->read_block(d->xport, addr, buf, 1); +} + +/** + * rmi_read_block - read a block of bytes + * @d: Pointer to an RMI device + * @addr: The start address to read from + * @buf: The read buffer + * @len: Length of the read buffer + * + * Reads a block of byte data using the underlying transport protocol + * into memory pointed by @buf. It returns 0 on success or a negative + * error code. + */ +static inline int rmi_read_block(struct rmi_device *d, u16 addr, + void *buf, size_t len) +{ + return d->xport->ops->read_block(d->xport, addr, buf, len); +} + +/** + * rmi_write - write a single byte + * @d: Pointer to an RMI device + * @addr: The address to write to + * @data: The data to write + * + * Writes a single byte using the underlying transport protocol. It + * returns zero on success or a negative error code. + */ +static inline int rmi_write(struct rmi_device *d, u16 addr, u8 data) +{ + return d->xport->ops->write_block(d->xport, addr, &data, 1); +} + +/** + * rmi_write_block - write a block of bytes + * @d: Pointer to an RMI device + * @addr: The start address to write to + * @buf: The write buffer + * @len: Length of the write buffer + * + * Writes a block of byte data from buf using the underlaying transport + * protocol. It returns the amount of bytes written or a negative error code. + */ +static inline int rmi_write_block(struct rmi_device *d, u16 addr, + const void *buf, size_t len) +{ + return d->xport->ops->write_block(d->xport, addr, buf, len); +} + +int rmi_for_each_dev(void *data, int (*func)(struct device *dev, void *data)); + +extern struct bus_type rmi_bus_type; + +int rmi_of_property_read_u32(struct device *dev, u32 *result, + const char *prop, bool optional); + +#define RMI_DEBUG_CORE BIT(0) +#define RMI_DEBUG_XPORT BIT(1) +#define RMI_DEBUG_FN BIT(2) +#define RMI_DEBUG_2D_SENSOR BIT(3) + +void rmi_dbg(int flags, struct device *dev, const char *fmt, ...); +#endif diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c new file mode 100644 index 000000000..faa295ec4 --- /dev/null +++ b/drivers/input/rmi4/rmi_driver.c @@ -0,0 +1,1054 @@ +/* + * Copyright (c) 2011-2016 Synaptics Incorporated + * Copyright (c) 2011 Unixphere + * + * This driver provides the core support for a single RMI4-based device. + * + * The RMI4 specification can be found here (URL split for line length): + * + * http://www.synaptics.com/sites/default/files/ + * 511-000136-01-Rev-E-RMI4-Interfacing-Guide.pdf + * + * 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/bitmap.h> +#include <linux/delay.h> +#include <linux/fs.h> +#include <linux/kconfig.h> +#include <linux/pm.h> +#include <linux/slab.h> +#include <linux/of.h> +#include <uapi/linux/input.h> +#include <linux/rmi.h> +#include "rmi_bus.h" +#include "rmi_driver.h" + +#define HAS_NONSTANDARD_PDT_MASK 0x40 +#define RMI4_MAX_PAGE 0xff +#define RMI4_PAGE_SIZE 0x100 +#define RMI4_PAGE_MASK 0xFF00 + +#define RMI_DEVICE_RESET_CMD 0x01 +#define DEFAULT_RESET_DELAY_MS 100 + +static void rmi_free_function_list(struct rmi_device *rmi_dev) +{ + struct rmi_function *fn, *tmp; + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); + + data->f01_container = NULL; + + /* Doing it in the reverse order so F01 will be removed last */ + list_for_each_entry_safe_reverse(fn, tmp, + &data->function_list, node) { + list_del(&fn->node); + rmi_unregister_function(fn); + } +} + +static int reset_one_function(struct rmi_function *fn) +{ + struct rmi_function_handler *fh; + int retval = 0; + + if (!fn || !fn->dev.driver) + return 0; + + fh = to_rmi_function_handler(fn->dev.driver); + if (fh->reset) { + retval = fh->reset(fn); + if (retval < 0) + dev_err(&fn->dev, "Reset failed with code %d.\n", + retval); + } + + return retval; +} + +static int configure_one_function(struct rmi_function *fn) +{ + struct rmi_function_handler *fh; + int retval = 0; + + if (!fn || !fn->dev.driver) + return 0; + + fh = to_rmi_function_handler(fn->dev.driver); + if (fh->config) { + retval = fh->config(fn); + if (retval < 0) + dev_err(&fn->dev, "Config failed with code %d.\n", + retval); + } + + return retval; +} + +static int rmi_driver_process_reset_requests(struct rmi_device *rmi_dev) +{ + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); + struct rmi_function *entry; + int retval; + + list_for_each_entry(entry, &data->function_list, node) { + retval = reset_one_function(entry); + if (retval < 0) + return retval; + } + + return 0; +} + +static int rmi_driver_process_config_requests(struct rmi_device *rmi_dev) +{ + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); + struct rmi_function *entry; + int retval; + + list_for_each_entry(entry, &data->function_list, node) { + retval = configure_one_function(entry); + if (retval < 0) + return retval; + } + + return 0; +} + +static void process_one_interrupt(struct rmi_driver_data *data, + struct rmi_function *fn) +{ + struct rmi_function_handler *fh; + + if (!fn || !fn->dev.driver) + return; + + fh = to_rmi_function_handler(fn->dev.driver); + if (fh->attention) { + bitmap_and(data->fn_irq_bits, data->irq_status, fn->irq_mask, + data->irq_count); + if (!bitmap_empty(data->fn_irq_bits, data->irq_count)) + fh->attention(fn, data->fn_irq_bits); + } +} + +int rmi_process_interrupt_requests(struct rmi_device *rmi_dev) +{ + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); + struct device *dev = &rmi_dev->dev; + struct rmi_function *entry; + int error; + + if (!data) + return 0; + + if (!rmi_dev->xport->attn_data) { + error = rmi_read_block(rmi_dev, + data->f01_container->fd.data_base_addr + 1, + data->irq_status, data->num_of_irq_regs); + if (error < 0) { + dev_err(dev, "Failed to read irqs, code=%d\n", error); + return error; + } + } + + mutex_lock(&data->irq_mutex); + bitmap_and(data->irq_status, data->irq_status, data->current_irq_mask, + data->irq_count); + /* + * At this point, irq_status has all bits that are set in the + * interrupt status register and are enabled. + */ + mutex_unlock(&data->irq_mutex); + + /* + * It would be nice to be able to use irq_chip to handle these + * nested IRQs. Unfortunately, most of the current customers for + * this driver are using older kernels (3.0.x) that don't support + * the features required for that. Once they've shifted to more + * recent kernels (say, 3.3 and higher), this should be switched to + * use irq_chip. + */ + list_for_each_entry(entry, &data->function_list, node) + process_one_interrupt(data, entry); + + if (data->input) + input_sync(data->input); + + return 0; +} +EXPORT_SYMBOL_GPL(rmi_process_interrupt_requests); + +static int suspend_one_function(struct rmi_function *fn) +{ + struct rmi_function_handler *fh; + int retval = 0; + + if (!fn || !fn->dev.driver) + return 0; + + fh = to_rmi_function_handler(fn->dev.driver); + if (fh->suspend) { + retval = fh->suspend(fn); + if (retval < 0) + dev_err(&fn->dev, "Suspend failed with code %d.\n", + retval); + } + + return retval; +} + +static int rmi_suspend_functions(struct rmi_device *rmi_dev) +{ + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); + struct rmi_function *entry; + int retval; + + list_for_each_entry(entry, &data->function_list, node) { + retval = suspend_one_function(entry); + if (retval < 0) + return retval; + } + + return 0; +} + +static int resume_one_function(struct rmi_function *fn) +{ + struct rmi_function_handler *fh; + int retval = 0; + + if (!fn || !fn->dev.driver) + return 0; + + fh = to_rmi_function_handler(fn->dev.driver); + if (fh->resume) { + retval = fh->resume(fn); + if (retval < 0) + dev_err(&fn->dev, "Resume failed with code %d.\n", + retval); + } + + return retval; +} + +static int rmi_resume_functions(struct rmi_device *rmi_dev) +{ + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); + struct rmi_function *entry; + int retval; + + list_for_each_entry(entry, &data->function_list, node) { + retval = resume_one_function(entry); + if (retval < 0) + return retval; + } + + return 0; +} + +static int enable_sensor(struct rmi_device *rmi_dev) +{ + int retval = 0; + + retval = rmi_driver_process_config_requests(rmi_dev); + if (retval < 0) + return retval; + + return rmi_process_interrupt_requests(rmi_dev); +} + +/** + * rmi_driver_set_input_params - set input device id and other data. + * + * @rmi_dev: Pointer to an RMI device + * @input: Pointer to input device + * + */ +static int rmi_driver_set_input_params(struct rmi_device *rmi_dev, + struct input_dev *input) +{ + input->name = SYNAPTICS_INPUT_DEVICE_NAME; + input->id.vendor = SYNAPTICS_VENDOR_ID; + input->id.bustype = BUS_RMI; + return 0; +} + +static void rmi_driver_set_input_name(struct rmi_device *rmi_dev, + struct input_dev *input) +{ + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); + char *device_name = rmi_f01_get_product_ID(data->f01_container); + char *name; + + name = devm_kasprintf(&rmi_dev->dev, GFP_KERNEL, + "Synaptics %s", device_name); + if (!name) + return; + + input->name = name; +} + +static int rmi_driver_set_irq_bits(struct rmi_device *rmi_dev, + unsigned long *mask) +{ + int error = 0; + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); + struct device *dev = &rmi_dev->dev; + + mutex_lock(&data->irq_mutex); + bitmap_or(data->new_irq_mask, + data->current_irq_mask, mask, data->irq_count); + + error = rmi_write_block(rmi_dev, + data->f01_container->fd.control_base_addr + 1, + data->new_irq_mask, data->num_of_irq_regs); + if (error < 0) { + dev_err(dev, "%s: Failed to change enabled interrupts!", + __func__); + goto error_unlock; + } + bitmap_copy(data->current_irq_mask, data->new_irq_mask, + data->num_of_irq_regs); + +error_unlock: + mutex_unlock(&data->irq_mutex); + return error; +} + +static int rmi_driver_clear_irq_bits(struct rmi_device *rmi_dev, + unsigned long *mask) +{ + int error = 0; + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); + struct device *dev = &rmi_dev->dev; + + mutex_lock(&data->irq_mutex); + bitmap_andnot(data->new_irq_mask, + data->current_irq_mask, mask, data->irq_count); + + error = rmi_write_block(rmi_dev, + data->f01_container->fd.control_base_addr + 1, + data->new_irq_mask, data->num_of_irq_regs); + if (error < 0) { + dev_err(dev, "%s: Failed to change enabled interrupts!", + __func__); + goto error_unlock; + } + bitmap_copy(data->current_irq_mask, data->new_irq_mask, + data->num_of_irq_regs); + +error_unlock: + mutex_unlock(&data->irq_mutex); + return error; +} + +static int rmi_driver_reset_handler(struct rmi_device *rmi_dev) +{ + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); + int error; + + /* + * Can get called before the driver is fully ready to deal with + * this situation. + */ + if (!data || !data->f01_container) { + dev_warn(&rmi_dev->dev, + "Not ready to handle reset yet!\n"); + return 0; + } + + error = rmi_read_block(rmi_dev, + data->f01_container->fd.control_base_addr + 1, + data->current_irq_mask, data->num_of_irq_regs); + if (error < 0) { + dev_err(&rmi_dev->dev, "%s: Failed to read current IRQ mask.\n", + __func__); + return error; + } + + error = rmi_driver_process_reset_requests(rmi_dev); + if (error < 0) + return error; + + error = rmi_driver_process_config_requests(rmi_dev); + if (error < 0) + return error; + + return 0; +} + +int rmi_read_pdt_entry(struct rmi_device *rmi_dev, struct pdt_entry *entry, + u16 pdt_address) +{ + u8 buf[RMI_PDT_ENTRY_SIZE]; + int error; + + error = rmi_read_block(rmi_dev, pdt_address, buf, RMI_PDT_ENTRY_SIZE); + if (error) { + dev_err(&rmi_dev->dev, "Read PDT entry at %#06x failed, code: %d.\n", + pdt_address, error); + return error; + } + + entry->page_start = pdt_address & RMI4_PAGE_MASK; + entry->query_base_addr = buf[0]; + entry->command_base_addr = buf[1]; + entry->control_base_addr = buf[2]; + entry->data_base_addr = buf[3]; + entry->interrupt_source_count = buf[4] & RMI_PDT_INT_SOURCE_COUNT_MASK; + entry->function_version = (buf[4] & RMI_PDT_FUNCTION_VERSION_MASK) >> 5; + entry->function_number = buf[5]; + + return 0; +} +EXPORT_SYMBOL_GPL(rmi_read_pdt_entry); + +static void rmi_driver_copy_pdt_to_fd(const struct pdt_entry *pdt, + struct rmi_function_descriptor *fd) +{ + fd->query_base_addr = pdt->query_base_addr + pdt->page_start; + fd->command_base_addr = pdt->command_base_addr + pdt->page_start; + fd->control_base_addr = pdt->control_base_addr + pdt->page_start; + fd->data_base_addr = pdt->data_base_addr + pdt->page_start; + fd->function_number = pdt->function_number; + fd->interrupt_source_count = pdt->interrupt_source_count; + fd->function_version = pdt->function_version; +} + +#define RMI_SCAN_CONTINUE 0 +#define RMI_SCAN_DONE 1 + +static int rmi_scan_pdt_page(struct rmi_device *rmi_dev, + int page, + void *ctx, + int (*callback)(struct rmi_device *rmi_dev, + void *ctx, + const struct pdt_entry *entry)) +{ + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); + struct pdt_entry pdt_entry; + u16 page_start = RMI4_PAGE_SIZE * page; + u16 pdt_start = page_start + PDT_START_SCAN_LOCATION; + u16 pdt_end = page_start + PDT_END_SCAN_LOCATION; + u16 addr; + int error; + int retval; + + for (addr = pdt_start; addr >= pdt_end; addr -= RMI_PDT_ENTRY_SIZE) { + error = rmi_read_pdt_entry(rmi_dev, &pdt_entry, addr); + if (error) + return error; + + if (RMI4_END_OF_PDT(pdt_entry.function_number)) + break; + + retval = callback(rmi_dev, ctx, &pdt_entry); + if (retval != RMI_SCAN_CONTINUE) + return retval; + } + + return (data->f01_bootloader_mode || addr == pdt_start) ? + RMI_SCAN_DONE : RMI_SCAN_CONTINUE; +} + +static int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx, + int (*callback)(struct rmi_device *rmi_dev, + void *ctx, + const struct pdt_entry *entry)) +{ + int page; + int retval = RMI_SCAN_DONE; + + for (page = 0; page <= RMI4_MAX_PAGE; page++) { + retval = rmi_scan_pdt_page(rmi_dev, page, ctx, callback); + if (retval != RMI_SCAN_CONTINUE) + break; + } + + return retval < 0 ? retval : 0; +} + +int rmi_read_register_desc(struct rmi_device *d, u16 addr, + struct rmi_register_descriptor *rdesc) +{ + int ret; + u8 size_presence_reg; + u8 buf[35]; + int presense_offset = 1; + u8 *struct_buf; + int reg; + int offset = 0; + int map_offset = 0; + int i; + int b; + + /* + * The first register of the register descriptor is the size of + * the register descriptor's presense register. + */ + ret = rmi_read(d, addr, &size_presence_reg); + if (ret) + return ret; + ++addr; + + if (size_presence_reg < 0 || size_presence_reg > 35) + return -EIO; + + memset(buf, 0, sizeof(buf)); + + /* + * The presence register contains the size of the register structure + * and a bitmap which identified which packet registers are present + * for this particular register type (ie query, control, or data). + */ + ret = rmi_read_block(d, addr, buf, size_presence_reg); + if (ret) + return ret; + ++addr; + + if (buf[0] == 0) { + presense_offset = 3; + rdesc->struct_size = buf[1] | (buf[2] << 8); + } else { + rdesc->struct_size = buf[0]; + } + + for (i = presense_offset; i < size_presence_reg; i++) { + for (b = 0; b < 8; b++) { + if (buf[i] & (0x1 << b)) + bitmap_set(rdesc->presense_map, map_offset, 1); + ++map_offset; + } + } + + rdesc->num_registers = bitmap_weight(rdesc->presense_map, + RMI_REG_DESC_PRESENSE_BITS); + + rdesc->registers = devm_kzalloc(&d->dev, rdesc->num_registers * + sizeof(struct rmi_register_desc_item), + GFP_KERNEL); + if (!rdesc->registers) + return -ENOMEM; + + /* + * Allocate a temporary buffer to hold the register structure. + * I'm not using devm_kzalloc here since it will not be retained + * after exiting this function + */ + struct_buf = kzalloc(rdesc->struct_size, GFP_KERNEL); + if (!struct_buf) + return -ENOMEM; + + /* + * The register structure contains information about every packet + * register of this type. This includes the size of the packet + * register and a bitmap of all subpackets contained in the packet + * register. + */ + ret = rmi_read_block(d, addr, struct_buf, rdesc->struct_size); + if (ret) + goto free_struct_buff; + + reg = find_first_bit(rdesc->presense_map, RMI_REG_DESC_PRESENSE_BITS); + map_offset = 0; + for (i = 0; i < rdesc->num_registers; i++) { + struct rmi_register_desc_item *item = &rdesc->registers[i]; + int reg_size = struct_buf[offset]; + + ++offset; + if (reg_size == 0) { + reg_size = struct_buf[offset] | + (struct_buf[offset + 1] << 8); + offset += 2; + } + + if (reg_size == 0) { + reg_size = struct_buf[offset] | + (struct_buf[offset + 1] << 8) | + (struct_buf[offset + 2] << 16) | + (struct_buf[offset + 3] << 24); + offset += 4; + } + + item->reg = reg; + item->reg_size = reg_size; + + do { + for (b = 0; b < 7; b++) { + if (struct_buf[offset] & (0x1 << b)) + bitmap_set(item->subpacket_map, + map_offset, 1); + ++map_offset; + } + } while (struct_buf[offset++] & 0x80); + + item->num_subpackets = bitmap_weight(item->subpacket_map, + RMI_REG_DESC_SUBPACKET_BITS); + + rmi_dbg(RMI_DEBUG_CORE, &d->dev, + "%s: reg: %d reg size: %ld subpackets: %d\n", __func__, + item->reg, item->reg_size, item->num_subpackets); + + reg = find_next_bit(rdesc->presense_map, + RMI_REG_DESC_PRESENSE_BITS, reg + 1); + } + +free_struct_buff: + kfree(struct_buf); + return ret; +} +EXPORT_SYMBOL_GPL(rmi_read_register_desc); + +const struct rmi_register_desc_item *rmi_get_register_desc_item( + struct rmi_register_descriptor *rdesc, u16 reg) +{ + const struct rmi_register_desc_item *item; + int i; + + for (i = 0; i < rdesc->num_registers; i++) { + item = &rdesc->registers[i]; + if (item->reg == reg) + return item; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(rmi_get_register_desc_item); + +size_t rmi_register_desc_calc_size(struct rmi_register_descriptor *rdesc) +{ + const struct rmi_register_desc_item *item; + int i; + size_t size = 0; + + for (i = 0; i < rdesc->num_registers; i++) { + item = &rdesc->registers[i]; + size += item->reg_size; + } + return size; +} +EXPORT_SYMBOL_GPL(rmi_register_desc_calc_size); + +/* Compute the register offset relative to the base address */ +int rmi_register_desc_calc_reg_offset( + struct rmi_register_descriptor *rdesc, u16 reg) +{ + const struct rmi_register_desc_item *item; + int offset = 0; + int i; + + for (i = 0; i < rdesc->num_registers; i++) { + item = &rdesc->registers[i]; + if (item->reg == reg) + return offset; + ++offset; + } + return -1; +} +EXPORT_SYMBOL_GPL(rmi_register_desc_calc_reg_offset); + +bool rmi_register_desc_has_subpacket(const struct rmi_register_desc_item *item, + u8 subpacket) +{ + return find_next_bit(item->subpacket_map, RMI_REG_DESC_PRESENSE_BITS, + subpacket) == subpacket; +} + +/* Indicates that flash programming is enabled (bootloader mode). */ +#define RMI_F01_STATUS_BOOTLOADER(status) (!!((status) & 0x40)) + +/* + * Given the PDT entry for F01, read the device status register to determine + * if we're stuck in bootloader mode or not. + * + */ +static int rmi_check_bootloader_mode(struct rmi_device *rmi_dev, + const struct pdt_entry *pdt) +{ + int error; + u8 device_status; + + error = rmi_read(rmi_dev, pdt->data_base_addr + pdt->page_start, + &device_status); + if (error) { + dev_err(&rmi_dev->dev, + "Failed to read device status: %d.\n", error); + return error; + } + + return RMI_F01_STATUS_BOOTLOADER(device_status); +} + +static int rmi_count_irqs(struct rmi_device *rmi_dev, + void *ctx, const struct pdt_entry *pdt) +{ + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); + int *irq_count = ctx; + + *irq_count += pdt->interrupt_source_count; + if (pdt->function_number == 0x01) { + data->f01_bootloader_mode = + rmi_check_bootloader_mode(rmi_dev, pdt); + if (data->f01_bootloader_mode) + dev_warn(&rmi_dev->dev, + "WARNING: RMI4 device is in bootloader mode!\n"); + } + + return RMI_SCAN_CONTINUE; +} + +static int rmi_initial_reset(struct rmi_device *rmi_dev, + void *ctx, const struct pdt_entry *pdt) +{ + int error; + + if (pdt->function_number == 0x01) { + u16 cmd_addr = pdt->page_start + pdt->command_base_addr; + u8 cmd_buf = RMI_DEVICE_RESET_CMD; + const struct rmi_device_platform_data *pdata = + rmi_get_platform_data(rmi_dev); + + if (rmi_dev->xport->ops->reset) { + error = rmi_dev->xport->ops->reset(rmi_dev->xport, + cmd_addr); + if (error) + return error; + + return RMI_SCAN_DONE; + } + + error = rmi_write_block(rmi_dev, cmd_addr, &cmd_buf, 1); + if (error) { + dev_err(&rmi_dev->dev, + "Initial reset failed. Code = %d.\n", error); + return error; + } + + mdelay(pdata->reset_delay_ms ?: DEFAULT_RESET_DELAY_MS); + + return RMI_SCAN_DONE; + } + + /* F01 should always be on page 0. If we don't find it there, fail. */ + return pdt->page_start == 0 ? RMI_SCAN_CONTINUE : -ENODEV; +} + +static int rmi_create_function(struct rmi_device *rmi_dev, + void *ctx, const struct pdt_entry *pdt) +{ + struct device *dev = &rmi_dev->dev; + struct rmi_driver_data *data = dev_get_drvdata(&rmi_dev->dev); + int *current_irq_count = ctx; + struct rmi_function *fn; + int i; + int error; + + rmi_dbg(RMI_DEBUG_CORE, dev, "Initializing F%02X.\n", + pdt->function_number); + + fn = kzalloc(sizeof(struct rmi_function) + + BITS_TO_LONGS(data->irq_count) * sizeof(unsigned long), + GFP_KERNEL); + if (!fn) { + dev_err(dev, "Failed to allocate memory for F%02X\n", + pdt->function_number); + return -ENOMEM; + } + + INIT_LIST_HEAD(&fn->node); + rmi_driver_copy_pdt_to_fd(pdt, &fn->fd); + + fn->rmi_dev = rmi_dev; + + fn->num_of_irqs = pdt->interrupt_source_count; + fn->irq_pos = *current_irq_count; + *current_irq_count += fn->num_of_irqs; + + for (i = 0; i < fn->num_of_irqs; i++) + set_bit(fn->irq_pos + i, fn->irq_mask); + + error = rmi_register_function(fn); + if (error) + goto err_put_fn; + + if (pdt->function_number == 0x01) + data->f01_container = fn; + + list_add_tail(&fn->node, &data->function_list); + + return RMI_SCAN_CONTINUE; + +err_put_fn: + put_device(&fn->dev); + return error; +} + +int rmi_driver_suspend(struct rmi_device *rmi_dev) +{ + int retval = 0; + + retval = rmi_suspend_functions(rmi_dev); + if (retval) + dev_warn(&rmi_dev->dev, "Failed to suspend functions: %d\n", + retval); + + return retval; +} +EXPORT_SYMBOL_GPL(rmi_driver_suspend); + +int rmi_driver_resume(struct rmi_device *rmi_dev) +{ + int retval; + + retval = rmi_resume_functions(rmi_dev); + if (retval) + dev_warn(&rmi_dev->dev, "Failed to suspend functions: %d\n", + retval); + + return retval; +} +EXPORT_SYMBOL_GPL(rmi_driver_resume); + +static int rmi_driver_remove(struct device *dev) +{ + struct rmi_device *rmi_dev = to_rmi_device(dev); + + rmi_free_function_list(rmi_dev); + + return 0; +} + +#ifdef CONFIG_OF +static int rmi_driver_of_probe(struct device *dev, + struct rmi_device_platform_data *pdata) +{ + int retval; + + retval = rmi_of_property_read_u32(dev, &pdata->reset_delay_ms, + "syna,reset-delay-ms", 1); + if (retval) + return retval; + + return 0; +} +#else +static inline int rmi_driver_of_probe(struct device *dev, + struct rmi_device_platform_data *pdata) +{ + return -ENODEV; +} +#endif + +static int rmi_driver_probe(struct device *dev) +{ + struct rmi_driver *rmi_driver; + struct rmi_driver_data *data; + struct rmi_device_platform_data *pdata; + struct rmi_device *rmi_dev; + size_t size; + void *irq_memory; + int irq_count; + int retval; + + rmi_dbg(RMI_DEBUG_CORE, dev, "%s: Starting probe.\n", + __func__); + + if (!rmi_is_physical_device(dev)) { + rmi_dbg(RMI_DEBUG_CORE, dev, "Not a physical device.\n"); + return -ENODEV; + } + + rmi_dev = to_rmi_device(dev); + rmi_driver = to_rmi_driver(dev->driver); + rmi_dev->driver = rmi_driver; + + pdata = rmi_get_platform_data(rmi_dev); + + if (rmi_dev->xport->dev->of_node) { + retval = rmi_driver_of_probe(rmi_dev->xport->dev, pdata); + if (retval) + return retval; + } + + data = devm_kzalloc(dev, sizeof(struct rmi_driver_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + INIT_LIST_HEAD(&data->function_list); + data->rmi_dev = rmi_dev; + dev_set_drvdata(&rmi_dev->dev, data); + + /* + * Right before a warm boot, the sensor might be in some unusual state, + * such as F54 diagnostics, or F34 bootloader mode after a firmware + * or configuration update. In order to clear the sensor to a known + * state and/or apply any updates, we issue a initial reset to clear any + * previous settings and force it into normal operation. + * + * We have to do this before actually building the PDT because + * the reflash updates (if any) might cause various registers to move + * around. + * + * For a number of reasons, this initial reset may fail to return + * within the specified time, but we'll still be able to bring up the + * driver normally after that failure. This occurs most commonly in + * a cold boot situation (where then firmware takes longer to come up + * than from a warm boot) and the reset_delay_ms in the platform data + * has been set too short to accommodate that. Since the sensor will + * eventually come up and be usable, we don't want to just fail here + * and leave the customer's device unusable. So we warn them, and + * continue processing. + */ + retval = rmi_scan_pdt(rmi_dev, NULL, rmi_initial_reset); + if (retval < 0) + dev_warn(dev, "RMI initial reset failed! Continuing in spite of this.\n"); + + retval = rmi_read(rmi_dev, PDT_PROPERTIES_LOCATION, &data->pdt_props); + if (retval < 0) { + /* + * we'll print out a warning and continue since + * failure to get the PDT properties is not a cause to fail + */ + dev_warn(dev, "Could not read PDT properties from %#06x (code %d). Assuming 0x00.\n", + PDT_PROPERTIES_LOCATION, retval); + } + + /* + * We need to count the IRQs and allocate their storage before scanning + * the PDT and creating the function entries, because adding a new + * function can trigger events that result in the IRQ related storage + * being accessed. + */ + rmi_dbg(RMI_DEBUG_CORE, dev, "Counting IRQs.\n"); + irq_count = 0; + retval = rmi_scan_pdt(rmi_dev, &irq_count, rmi_count_irqs); + if (retval < 0) { + dev_err(dev, "IRQ counting failed with code %d.\n", retval); + goto err; + } + data->irq_count = irq_count; + data->num_of_irq_regs = (data->irq_count + 7) / 8; + + mutex_init(&data->irq_mutex); + + size = BITS_TO_LONGS(data->irq_count) * sizeof(unsigned long); + irq_memory = devm_kzalloc(dev, size * 4, GFP_KERNEL); + if (!irq_memory) { + dev_err(dev, "Failed to allocate memory for irq masks.\n"); + goto err; + } + + data->irq_status = irq_memory + size * 0; + data->fn_irq_bits = irq_memory + size * 1; + data->current_irq_mask = irq_memory + size * 2; + data->new_irq_mask = irq_memory + size * 3; + + if (rmi_dev->xport->input) { + /* + * The transport driver already has an input device. + * In some cases it is preferable to reuse the transport + * devices input device instead of creating a new one here. + * One example is some HID touchpads report "pass-through" + * button events are not reported by rmi registers. + */ + data->input = rmi_dev->xport->input; + } else { + data->input = devm_input_allocate_device(dev); + if (!data->input) { + dev_err(dev, "%s: Failed to allocate input device.\n", + __func__); + retval = -ENOMEM; + goto err_destroy_functions; + } + rmi_driver_set_input_params(rmi_dev, data->input); + data->input->phys = devm_kasprintf(dev, GFP_KERNEL, + "%s/input0", dev_name(dev)); + } + + irq_count = 0; + rmi_dbg(RMI_DEBUG_CORE, dev, "Creating functions."); + retval = rmi_scan_pdt(rmi_dev, &irq_count, rmi_create_function); + if (retval < 0) { + dev_err(dev, "Function creation failed with code %d.\n", + retval); + goto err_destroy_functions; + } + + if (!data->f01_container) { + dev_err(dev, "Missing F01 container!\n"); + retval = -EINVAL; + goto err_destroy_functions; + } + + retval = rmi_read_block(rmi_dev, + data->f01_container->fd.control_base_addr + 1, + data->current_irq_mask, data->num_of_irq_regs); + if (retval < 0) { + dev_err(dev, "%s: Failed to read current IRQ mask.\n", + __func__); + goto err_destroy_functions; + } + + if (data->input) { + rmi_driver_set_input_name(rmi_dev, data->input); + if (!rmi_dev->xport->input) { + if (input_register_device(data->input)) { + dev_err(dev, "%s: Failed to register input device.\n", + __func__); + goto err_destroy_functions; + } + } + } + + if (data->f01_container->dev.driver) + /* Driver already bound, so enable ATTN now. */ + return enable_sensor(rmi_dev); + + return 0; + +err_destroy_functions: + rmi_free_function_list(rmi_dev); +err: + return retval < 0 ? retval : 0; +} + +static struct rmi_driver rmi_physical_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "rmi4_physical", + .bus = &rmi_bus_type, + .probe = rmi_driver_probe, + .remove = rmi_driver_remove, + }, + .reset_handler = rmi_driver_reset_handler, + .clear_irq_bits = rmi_driver_clear_irq_bits, + .set_irq_bits = rmi_driver_set_irq_bits, + .set_input_params = rmi_driver_set_input_params, +}; + +bool rmi_is_physical_driver(struct device_driver *drv) +{ + return drv == &rmi_physical_driver.driver; +} + +int __init rmi_register_physical_driver(void) +{ + int error; + + error = driver_register(&rmi_physical_driver.driver); + if (error) { + pr_err("%s: driver register failed, code=%d.\n", __func__, + error); + return error; + } + + return 0; +} + +void __exit rmi_unregister_physical_driver(void) +{ + driver_unregister(&rmi_physical_driver.driver); +} diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h new file mode 100644 index 000000000..6e140fa3c --- /dev/null +++ b/drivers/input/rmi4/rmi_driver.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2011-2016 Synaptics Incorporated + * Copyright (c) 2011 Unixphere + * + * 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. + */ + +#ifndef _RMI_DRIVER_H +#define _RMI_DRIVER_H + +#include <linux/ctype.h> +#include <linux/hrtimer.h> +#include <linux/ktime.h> +#include <linux/input.h> +#include "rmi_bus.h" + +#define RMI_DRIVER_VERSION "2.0" + +#define SYNAPTICS_INPUT_DEVICE_NAME "Synaptics RMI4 Touch Sensor" +#define SYNAPTICS_VENDOR_ID 0x06cb + +#define GROUP(_attrs) { \ + .attrs = _attrs, \ +} + +#define PDT_PROPERTIES_LOCATION 0x00EF +#define BSR_LOCATION 0x00FE + +#define RMI_PDT_PROPS_HAS_BSR 0x02 + +#define NAME_BUFFER_SIZE 256 + +#define RMI_PDT_ENTRY_SIZE 6 +#define RMI_PDT_FUNCTION_VERSION_MASK 0x60 +#define RMI_PDT_INT_SOURCE_COUNT_MASK 0x07 + +#define PDT_START_SCAN_LOCATION 0x00e9 +#define PDT_END_SCAN_LOCATION 0x0005 +#define RMI4_END_OF_PDT(id) ((id) == 0x00 || (id) == 0xff) + +struct pdt_entry { + u16 page_start; + u8 query_base_addr; + u8 command_base_addr; + u8 control_base_addr; + u8 data_base_addr; + u8 interrupt_source_count; + u8 function_version; + u8 function_number; +}; + +int rmi_read_pdt_entry(struct rmi_device *rmi_dev, struct pdt_entry *entry, + u16 pdt_address); + +#define RMI_REG_DESC_PRESENSE_BITS (32 * BITS_PER_BYTE) +#define RMI_REG_DESC_SUBPACKET_BITS (37 * BITS_PER_BYTE) + +/* describes a single packet register */ +struct rmi_register_desc_item { + u16 reg; + unsigned long reg_size; + u8 num_subpackets; + unsigned long subpacket_map[BITS_TO_LONGS( + RMI_REG_DESC_SUBPACKET_BITS)]; +}; + +/* + * describes the packet registers for a particular type + * (ie query, control, data) + */ +struct rmi_register_descriptor { + unsigned long struct_size; + unsigned long presense_map[BITS_TO_LONGS(RMI_REG_DESC_PRESENSE_BITS)]; + u8 num_registers; + struct rmi_register_desc_item *registers; +}; + +int rmi_read_register_desc(struct rmi_device *d, u16 addr, + struct rmi_register_descriptor *rdesc); +const struct rmi_register_desc_item *rmi_get_register_desc_item( + struct rmi_register_descriptor *rdesc, u16 reg); + +/* + * Calculate the total size of all of the registers described in the + * descriptor. + */ +size_t rmi_register_desc_calc_size(struct rmi_register_descriptor *rdesc); +int rmi_register_desc_calc_reg_offset( + struct rmi_register_descriptor *rdesc, u16 reg); +bool rmi_register_desc_has_subpacket(const struct rmi_register_desc_item *item, + u8 subpacket); + +bool rmi_is_physical_driver(struct device_driver *); +int rmi_register_physical_driver(void); +void rmi_unregister_physical_driver(void); + +char *rmi_f01_get_product_ID(struct rmi_function *fn); + +extern struct rmi_function_handler rmi_f01_handler; +extern struct rmi_function_handler rmi_f11_handler; +extern struct rmi_function_handler rmi_f12_handler; +extern struct rmi_function_handler rmi_f30_handler; +#endif diff --git a/drivers/input/rmi4/rmi_f01.c b/drivers/input/rmi4/rmi_f01.c new file mode 100644 index 000000000..eb362bc71 --- /dev/null +++ b/drivers/input/rmi4/rmi_f01.c @@ -0,0 +1,624 @@ +/* + * Copyright (c) 2011-2016 Synaptics Incorporated + * Copyright (c) 2011 Unixphere + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/kconfig.h> +#include <linux/rmi.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include <linux/of.h> +#include "rmi_driver.h" + +#define RMI_PRODUCT_ID_LENGTH 10 +#define RMI_PRODUCT_INFO_LENGTH 2 + +#define RMI_DATE_CODE_LENGTH 3 + +#define PRODUCT_ID_OFFSET 0x10 +#define PRODUCT_INFO_OFFSET 0x1E + + +/* Force a firmware reset of the sensor */ +#define RMI_F01_CMD_DEVICE_RESET 1 + +/* Various F01_RMI_QueryX bits */ + +#define RMI_F01_QRY1_CUSTOM_MAP BIT(0) +#define RMI_F01_QRY1_NON_COMPLIANT BIT(1) +#define RMI_F01_QRY1_HAS_LTS BIT(2) +#define RMI_F01_QRY1_HAS_SENSOR_ID BIT(3) +#define RMI_F01_QRY1_HAS_CHARGER_INP BIT(4) +#define RMI_F01_QRY1_HAS_ADJ_DOZE BIT(5) +#define RMI_F01_QRY1_HAS_ADJ_DOZE_HOFF BIT(6) +#define RMI_F01_QRY1_HAS_QUERY42 BIT(7) + +#define RMI_F01_QRY5_YEAR_MASK 0x1f +#define RMI_F01_QRY6_MONTH_MASK 0x0f +#define RMI_F01_QRY7_DAY_MASK 0x1f + +#define RMI_F01_QRY2_PRODINFO_MASK 0x7f + +#define RMI_F01_BASIC_QUERY_LEN 21 /* From Query 00 through 20 */ + +struct f01_basic_properties { + u8 manufacturer_id; + bool has_lts; + bool has_adjustable_doze; + bool has_adjustable_doze_holdoff; + char dom[11]; /* YYYY/MM/DD + '\0' */ + u8 product_id[RMI_PRODUCT_ID_LENGTH + 1]; + u16 productinfo; + u32 firmware_id; +}; + +/* F01 device status bits */ + +/* Most recent device status event */ +#define RMI_F01_STATUS_CODE(status) ((status) & 0x0f) +/* The device has lost its configuration for some reason. */ +#define RMI_F01_STATUS_UNCONFIGURED(status) (!!((status) & 0x80)) + +/* Control register bits */ + +/* + * Sleep mode controls power management on the device and affects all + * functions of the device. + */ +#define RMI_F01_CTRL0_SLEEP_MODE_MASK 0x03 + +#define RMI_SLEEP_MODE_NORMAL 0x00 +#define RMI_SLEEP_MODE_SENSOR_SLEEP 0x01 +#define RMI_SLEEP_MODE_RESERVED0 0x02 +#define RMI_SLEEP_MODE_RESERVED1 0x03 + +/* + * This bit disables whatever sleep mode may be selected by the sleep_mode + * field and forces the device to run at full power without sleeping. + */ +#define RMI_F01_CRTL0_NOSLEEP_BIT BIT(2) + +/* + * When this bit is set, the touch controller employs a noise-filtering + * algorithm designed for use with a connected battery charger. + */ +#define RMI_F01_CRTL0_CHARGER_BIT BIT(5) + +/* + * Sets the report rate for the device. The effect of this setting is + * highly product dependent. Check the spec sheet for your particular + * touch sensor. + */ +#define RMI_F01_CRTL0_REPORTRATE_BIT BIT(6) + +/* + * Written by the host as an indicator that the device has been + * successfully configured. + */ +#define RMI_F01_CRTL0_CONFIGURED_BIT BIT(7) + +/** + * @ctrl0 - see the bit definitions above. + * @doze_interval - controls the interval between checks for finger presence + * when the touch sensor is in doze mode, in units of 10ms. + * @wakeup_threshold - controls the capacitance threshold at which the touch + * sensor will decide to wake up from that low power state. + * @doze_holdoff - controls how long the touch sensor waits after the last + * finger lifts before entering the doze state, in units of 100ms. + */ +struct f01_device_control { + u8 ctrl0; + u8 doze_interval; + u8 wakeup_threshold; + u8 doze_holdoff; +}; + +struct f01_data { + struct f01_basic_properties properties; + struct f01_device_control device_control; + + u16 doze_interval_addr; + u16 wakeup_threshold_addr; + u16 doze_holdoff_addr; + + bool suspended; + bool old_nosleep; + + unsigned int num_of_irq_regs; +}; + +static int rmi_f01_read_properties(struct rmi_device *rmi_dev, + u16 query_base_addr, + struct f01_basic_properties *props) +{ + u8 queries[RMI_F01_BASIC_QUERY_LEN]; + int ret; + int query_offset = query_base_addr; + bool has_ds4_queries = false; + bool has_query42 = false; + bool has_sensor_id = false; + bool has_package_id_query = false; + bool has_build_id_query = false; + u16 prod_info_addr; + u8 ds4_query_len; + + ret = rmi_read_block(rmi_dev, query_offset, + queries, RMI_F01_BASIC_QUERY_LEN); + if (ret) { + dev_err(&rmi_dev->dev, + "Failed to read device query registers: %d\n", ret); + return ret; + } + + prod_info_addr = query_offset + 17; + query_offset += RMI_F01_BASIC_QUERY_LEN; + + /* Now parse what we got */ + props->manufacturer_id = queries[0]; + + props->has_lts = queries[1] & RMI_F01_QRY1_HAS_LTS; + props->has_adjustable_doze = + queries[1] & RMI_F01_QRY1_HAS_ADJ_DOZE; + props->has_adjustable_doze_holdoff = + queries[1] & RMI_F01_QRY1_HAS_ADJ_DOZE_HOFF; + has_query42 = queries[1] & RMI_F01_QRY1_HAS_QUERY42; + has_sensor_id = queries[1] & RMI_F01_QRY1_HAS_SENSOR_ID; + + snprintf(props->dom, sizeof(props->dom), "20%02d/%02d/%02d", + queries[5] & RMI_F01_QRY5_YEAR_MASK, + queries[6] & RMI_F01_QRY6_MONTH_MASK, + queries[7] & RMI_F01_QRY7_DAY_MASK); + + memcpy(props->product_id, &queries[11], + RMI_PRODUCT_ID_LENGTH); + props->product_id[RMI_PRODUCT_ID_LENGTH] = '\0'; + + props->productinfo = + ((queries[2] & RMI_F01_QRY2_PRODINFO_MASK) << 7) | + (queries[3] & RMI_F01_QRY2_PRODINFO_MASK); + + if (has_sensor_id) + query_offset++; + + if (has_query42) { + ret = rmi_read(rmi_dev, query_offset, queries); + if (ret) { + dev_err(&rmi_dev->dev, + "Failed to read query 42 register: %d\n", ret); + return ret; + } + + has_ds4_queries = !!(queries[0] & BIT(0)); + query_offset++; + } + + if (has_ds4_queries) { + ret = rmi_read(rmi_dev, query_offset, &ds4_query_len); + if (ret) { + dev_err(&rmi_dev->dev, + "Failed to read DS4 queries length: %d\n", ret); + return ret; + } + query_offset++; + + if (ds4_query_len > 0) { + ret = rmi_read(rmi_dev, query_offset, queries); + if (ret) { + dev_err(&rmi_dev->dev, + "Failed to read DS4 queries: %d\n", + ret); + return ret; + } + + has_package_id_query = !!(queries[0] & BIT(0)); + has_build_id_query = !!(queries[0] & BIT(1)); + } + + if (has_package_id_query) + prod_info_addr++; + + if (has_build_id_query) { + ret = rmi_read_block(rmi_dev, prod_info_addr, queries, + 3); + if (ret) { + dev_err(&rmi_dev->dev, + "Failed to read product info: %d\n", + ret); + return ret; + } + + props->firmware_id = queries[1] << 8 | queries[0]; + props->firmware_id += queries[2] * 65536; + } + } + + return 0; +} + +char *rmi_f01_get_product_ID(struct rmi_function *fn) +{ + struct f01_data *f01 = dev_get_drvdata(&fn->dev); + + return f01->properties.product_id; +} + +#ifdef CONFIG_OF +static int rmi_f01_of_probe(struct device *dev, + struct rmi_device_platform_data *pdata) +{ + int retval; + u32 val; + + retval = rmi_of_property_read_u32(dev, + (u32 *)&pdata->power_management.nosleep, + "syna,nosleep-mode", 1); + if (retval) + return retval; + + retval = rmi_of_property_read_u32(dev, &val, + "syna,wakeup-threshold", 1); + if (retval) + return retval; + + pdata->power_management.wakeup_threshold = val; + + retval = rmi_of_property_read_u32(dev, &val, + "syna,doze-holdoff-ms", 1); + if (retval) + return retval; + + pdata->power_management.doze_holdoff = val * 100; + + retval = rmi_of_property_read_u32(dev, &val, + "syna,doze-interval-ms", 1); + if (retval) + return retval; + + pdata->power_management.doze_interval = val / 10; + + return 0; +} +#else +static inline int rmi_f01_of_probe(struct device *dev, + struct rmi_device_platform_data *pdata) +{ + return -ENODEV; +} +#endif + +static int rmi_f01_probe(struct rmi_function *fn) +{ + struct rmi_device *rmi_dev = fn->rmi_dev; + struct rmi_driver_data *driver_data = dev_get_drvdata(&rmi_dev->dev); + struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev); + struct f01_data *f01; + int error; + u16 ctrl_base_addr = fn->fd.control_base_addr; + u8 device_status; + u8 temp; + + if (fn->dev.of_node) { + error = rmi_f01_of_probe(&fn->dev, pdata); + if (error) + return error; + } + + f01 = devm_kzalloc(&fn->dev, sizeof(struct f01_data), GFP_KERNEL); + if (!f01) + return -ENOMEM; + + f01->num_of_irq_regs = driver_data->num_of_irq_regs; + + /* + * Set the configured bit and (optionally) other important stuff + * in the device control register. + */ + + error = rmi_read(rmi_dev, fn->fd.control_base_addr, + &f01->device_control.ctrl0); + if (error) { + dev_err(&fn->dev, "Failed to read F01 control: %d\n", error); + return error; + } + + switch (pdata->power_management.nosleep) { + case RMI_F01_NOSLEEP_DEFAULT: + break; + case RMI_F01_NOSLEEP_OFF: + f01->device_control.ctrl0 &= ~RMI_F01_CRTL0_NOSLEEP_BIT; + break; + case RMI_F01_NOSLEEP_ON: + f01->device_control.ctrl0 |= RMI_F01_CRTL0_NOSLEEP_BIT; + break; + } + + /* + * Sleep mode might be set as a hangover from a system crash or + * reboot without power cycle. If so, clear it so the sensor + * is certain to function. + */ + if ((f01->device_control.ctrl0 & RMI_F01_CTRL0_SLEEP_MODE_MASK) != + RMI_SLEEP_MODE_NORMAL) { + dev_warn(&fn->dev, + "WARNING: Non-zero sleep mode found. Clearing...\n"); + f01->device_control.ctrl0 &= ~RMI_F01_CTRL0_SLEEP_MODE_MASK; + } + + f01->device_control.ctrl0 |= RMI_F01_CRTL0_CONFIGURED_BIT; + + error = rmi_write(rmi_dev, fn->fd.control_base_addr, + f01->device_control.ctrl0); + if (error) { + dev_err(&fn->dev, "Failed to write F01 control: %d\n", error); + return error; + } + + /* Dummy read in order to clear irqs */ + error = rmi_read(rmi_dev, fn->fd.data_base_addr + 1, &temp); + if (error < 0) { + dev_err(&fn->dev, "Failed to read Interrupt Status.\n"); + return error; + } + + error = rmi_f01_read_properties(rmi_dev, fn->fd.query_base_addr, + &f01->properties); + if (error < 0) { + dev_err(&fn->dev, "Failed to read F01 properties.\n"); + return error; + } + + dev_info(&fn->dev, "found RMI device, manufacturer: %s, product: %s, fw id: %d\n", + f01->properties.manufacturer_id == 1 ? "Synaptics" : "unknown", + f01->properties.product_id, f01->properties.firmware_id); + + /* Advance to interrupt control registers, then skip over them. */ + ctrl_base_addr++; + ctrl_base_addr += f01->num_of_irq_regs; + + /* read control register */ + if (f01->properties.has_adjustable_doze) { + f01->doze_interval_addr = ctrl_base_addr; + ctrl_base_addr++; + + if (pdata->power_management.doze_interval) { + f01->device_control.doze_interval = + pdata->power_management.doze_interval; + error = rmi_write(rmi_dev, f01->doze_interval_addr, + f01->device_control.doze_interval); + if (error) { + dev_err(&fn->dev, + "Failed to configure F01 doze interval register: %d\n", + error); + return error; + } + } else { + error = rmi_read(rmi_dev, f01->doze_interval_addr, + &f01->device_control.doze_interval); + if (error) { + dev_err(&fn->dev, + "Failed to read F01 doze interval register: %d\n", + error); + return error; + } + } + + f01->wakeup_threshold_addr = ctrl_base_addr; + ctrl_base_addr++; + + if (pdata->power_management.wakeup_threshold) { + f01->device_control.wakeup_threshold = + pdata->power_management.wakeup_threshold; + error = rmi_write(rmi_dev, f01->wakeup_threshold_addr, + f01->device_control.wakeup_threshold); + if (error) { + dev_err(&fn->dev, + "Failed to configure F01 wakeup threshold register: %d\n", + error); + return error; + } + } else { + error = rmi_read(rmi_dev, f01->wakeup_threshold_addr, + &f01->device_control.wakeup_threshold); + if (error < 0) { + dev_err(&fn->dev, + "Failed to read F01 wakeup threshold register: %d\n", + error); + return error; + } + } + } + + if (f01->properties.has_lts) + ctrl_base_addr++; + + if (f01->properties.has_adjustable_doze_holdoff) { + f01->doze_holdoff_addr = ctrl_base_addr; + ctrl_base_addr++; + + if (pdata->power_management.doze_holdoff) { + f01->device_control.doze_holdoff = + pdata->power_management.doze_holdoff; + error = rmi_write(rmi_dev, f01->doze_holdoff_addr, + f01->device_control.doze_holdoff); + if (error) { + dev_err(&fn->dev, + "Failed to configure F01 doze holdoff register: %d\n", + error); + return error; + } + } else { + error = rmi_read(rmi_dev, f01->doze_holdoff_addr, + &f01->device_control.doze_holdoff); + if (error) { + dev_err(&fn->dev, + "Failed to read F01 doze holdoff register: %d\n", + error); + return error; + } + } + } + + error = rmi_read(rmi_dev, fn->fd.data_base_addr, &device_status); + if (error < 0) { + dev_err(&fn->dev, + "Failed to read device status: %d\n", error); + return error; + } + + if (RMI_F01_STATUS_UNCONFIGURED(device_status)) { + dev_err(&fn->dev, + "Device was reset during configuration process, status: %#02x!\n", + RMI_F01_STATUS_CODE(device_status)); + return -EINVAL; + } + + dev_set_drvdata(&fn->dev, f01); + + return 0; +} + +static int rmi_f01_config(struct rmi_function *fn) +{ + struct f01_data *f01 = dev_get_drvdata(&fn->dev); + int error; + + error = rmi_write(fn->rmi_dev, fn->fd.control_base_addr, + f01->device_control.ctrl0); + if (error) { + dev_err(&fn->dev, + "Failed to write device_control register: %d\n", error); + return error; + } + + if (f01->properties.has_adjustable_doze) { + error = rmi_write(fn->rmi_dev, f01->doze_interval_addr, + f01->device_control.doze_interval); + if (error) { + dev_err(&fn->dev, + "Failed to write doze interval: %d\n", error); + return error; + } + + error = rmi_write_block(fn->rmi_dev, + f01->wakeup_threshold_addr, + &f01->device_control.wakeup_threshold, + sizeof(u8)); + if (error) { + dev_err(&fn->dev, + "Failed to write wakeup threshold: %d\n", + error); + return error; + } + } + + if (f01->properties.has_adjustable_doze_holdoff) { + error = rmi_write(fn->rmi_dev, f01->doze_holdoff_addr, + f01->device_control.doze_holdoff); + if (error) { + dev_err(&fn->dev, + "Failed to write doze holdoff: %d\n", error); + return error; + } + } + + return 0; +} + +static int rmi_f01_suspend(struct rmi_function *fn) +{ + struct f01_data *f01 = dev_get_drvdata(&fn->dev); + int error; + + f01->old_nosleep = + f01->device_control.ctrl0 & RMI_F01_CRTL0_NOSLEEP_BIT; + f01->device_control.ctrl0 &= ~RMI_F01_CRTL0_NOSLEEP_BIT; + + f01->device_control.ctrl0 &= ~RMI_F01_CTRL0_SLEEP_MODE_MASK; + if (device_may_wakeup(fn->rmi_dev->xport->dev)) + f01->device_control.ctrl0 |= RMI_SLEEP_MODE_RESERVED1; + else + f01->device_control.ctrl0 |= RMI_SLEEP_MODE_SENSOR_SLEEP; + + error = rmi_write(fn->rmi_dev, fn->fd.control_base_addr, + f01->device_control.ctrl0); + if (error) { + dev_err(&fn->dev, "Failed to write sleep mode: %d.\n", error); + if (f01->old_nosleep) + f01->device_control.ctrl0 |= RMI_F01_CRTL0_NOSLEEP_BIT; + f01->device_control.ctrl0 &= ~RMI_F01_CTRL0_SLEEP_MODE_MASK; + f01->device_control.ctrl0 |= RMI_SLEEP_MODE_NORMAL; + return error; + } + + return 0; +} + +static int rmi_f01_resume(struct rmi_function *fn) +{ + struct f01_data *f01 = dev_get_drvdata(&fn->dev); + int error; + + if (f01->old_nosleep) + f01->device_control.ctrl0 |= RMI_F01_CRTL0_NOSLEEP_BIT; + + f01->device_control.ctrl0 &= ~RMI_F01_CTRL0_SLEEP_MODE_MASK; + f01->device_control.ctrl0 |= RMI_SLEEP_MODE_NORMAL; + + error = rmi_write(fn->rmi_dev, fn->fd.control_base_addr, + f01->device_control.ctrl0); + if (error) { + dev_err(&fn->dev, + "Failed to restore normal operation: %d.\n", error); + return error; + } + + return 0; +} + +static int rmi_f01_attention(struct rmi_function *fn, + unsigned long *irq_bits) +{ + struct rmi_device *rmi_dev = fn->rmi_dev; + int error; + u8 device_status; + + error = rmi_read(rmi_dev, fn->fd.data_base_addr, &device_status); + if (error) { + dev_err(&fn->dev, + "Failed to read device status: %d.\n", error); + return error; + } + + if (RMI_F01_STATUS_UNCONFIGURED(device_status)) { + dev_warn(&fn->dev, "Device reset detected.\n"); + error = rmi_dev->driver->reset_handler(rmi_dev); + if (error) { + dev_err(&fn->dev, "Device reset failed: %d\n", error); + return error; + } + } + + return 0; +} + +struct rmi_function_handler rmi_f01_handler = { + .driver = { + .name = "rmi4_f01", + /* + * Do not allow user unbinding F01 as it is critical + * function. + */ + .suppress_bind_attrs = true, + }, + .func = 0x01, + .probe = rmi_f01_probe, + .config = rmi_f01_config, + .attention = rmi_f01_attention, + .suspend = rmi_f01_suspend, + .resume = rmi_f01_resume, +}; diff --git a/drivers/input/rmi4/rmi_f11.c b/drivers/input/rmi4/rmi_f11.c new file mode 100644 index 000000000..ec8a10d53 --- /dev/null +++ b/drivers/input/rmi4/rmi_f11.c @@ -0,0 +1,1317 @@ +/* + * Copyright (c) 2011-2015 Synaptics Incorporated + * Copyright (c) 2011 Unixphere + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/input.h> +#include <linux/input/mt.h> +#include <linux/kconfig.h> +#include <linux/rmi.h> +#include <linux/slab.h> +#include <linux/of.h> +#include "rmi_driver.h" +#include "rmi_2d_sensor.h" + +#define F11_MAX_NUM_OF_FINGERS 10 +#define F11_MAX_NUM_OF_TOUCH_SHAPES 16 + +#define FINGER_STATE_MASK 0x03 + +#define F11_CTRL_SENSOR_MAX_X_POS_OFFSET 6 +#define F11_CTRL_SENSOR_MAX_Y_POS_OFFSET 8 + +#define DEFAULT_XY_MAX 9999 +#define DEFAULT_MAX_ABS_MT_PRESSURE 255 +#define DEFAULT_MAX_ABS_MT_TOUCH 15 +#define DEFAULT_MAX_ABS_MT_ORIENTATION 1 +#define DEFAULT_MIN_ABS_MT_TRACKING_ID 1 +#define DEFAULT_MAX_ABS_MT_TRACKING_ID 10 + +/** A note about RMI4 F11 register structure. + * + * The properties for + * a given sensor are described by its query registers. The number of query + * registers and the layout of their contents are described by the F11 device + * queries as well as the sensor query information. + * + * Similarly, each sensor has control registers that govern its behavior. The + * size and layout of the control registers for a given sensor can be determined + * by parsing that sensors query registers. + * + * And in a likewise fashion, each sensor has data registers where it reports + * its touch data and other interesting stuff. The size and layout of a + * sensors data registers must be determined by parsing its query registers. + * + * The short story is that we need to read and parse a lot of query + * registers in order to determine the attributes of a sensor. Then + * we need to use that data to compute the size of the control and data + * registers for sensor. + * + * The end result is that we have a number of structs that aren't used to + * directly generate the input events, but their size, location and contents + * are critical to determining where the data we are interested in lives. + * + * At this time, the driver does not yet comprehend all possible F11 + * configuration options, but it should be sufficient to cover 99% of RMI4 F11 + * devices currently in the field. + */ + +/* maximum ABS_MT_POSITION displacement (in mm) */ +#define DMAX 10 + +/** + * @rezero - writing this to the F11 command register will cause the sensor to + * calibrate to the current capacitive state. + */ +#define RMI_F11_REZERO 0x01 + +#define RMI_F11_HAS_QUERY9 (1 << 3) +#define RMI_F11_HAS_QUERY11 (1 << 4) +#define RMI_F11_HAS_QUERY12 (1 << 5) +#define RMI_F11_HAS_QUERY27 (1 << 6) +#define RMI_F11_HAS_QUERY28 (1 << 7) + +/** Defs for Query 1 */ + +#define RMI_F11_NR_FINGERS_MASK 0x07 +#define RMI_F11_HAS_REL (1 << 3) +#define RMI_F11_HAS_ABS (1 << 4) +#define RMI_F11_HAS_GESTURES (1 << 5) +#define RMI_F11_HAS_SENSITIVITY_ADJ (1 << 6) +#define RMI_F11_CONFIGURABLE (1 << 7) + +/** Defs for Query 2, 3, and 4. */ +#define RMI_F11_NR_ELECTRODES_MASK 0x7F + +/** Defs for Query 5 */ + +#define RMI_F11_ABS_DATA_SIZE_MASK 0x03 +#define RMI_F11_HAS_ANCHORED_FINGER (1 << 2) +#define RMI_F11_HAS_ADJ_HYST (1 << 3) +#define RMI_F11_HAS_DRIBBLE (1 << 4) +#define RMI_F11_HAS_BENDING_CORRECTION (1 << 5) +#define RMI_F11_HAS_LARGE_OBJECT_SUPPRESSION (1 << 6) +#define RMI_F11_HAS_JITTER_FILTER (1 << 7) + +/** Defs for Query 7 */ +#define RMI_F11_HAS_SINGLE_TAP (1 << 0) +#define RMI_F11_HAS_TAP_AND_HOLD (1 << 1) +#define RMI_F11_HAS_DOUBLE_TAP (1 << 2) +#define RMI_F11_HAS_EARLY_TAP (1 << 3) +#define RMI_F11_HAS_FLICK (1 << 4) +#define RMI_F11_HAS_PRESS (1 << 5) +#define RMI_F11_HAS_PINCH (1 << 6) +#define RMI_F11_HAS_CHIRAL (1 << 7) + +/** Defs for Query 8 */ +#define RMI_F11_HAS_PALM_DET (1 << 0) +#define RMI_F11_HAS_ROTATE (1 << 1) +#define RMI_F11_HAS_TOUCH_SHAPES (1 << 2) +#define RMI_F11_HAS_SCROLL_ZONES (1 << 3) +#define RMI_F11_HAS_INDIVIDUAL_SCROLL_ZONES (1 << 4) +#define RMI_F11_HAS_MF_SCROLL (1 << 5) +#define RMI_F11_HAS_MF_EDGE_MOTION (1 << 6) +#define RMI_F11_HAS_MF_SCROLL_INERTIA (1 << 7) + +/** Defs for Query 9. */ +#define RMI_F11_HAS_PEN (1 << 0) +#define RMI_F11_HAS_PROXIMITY (1 << 1) +#define RMI_F11_HAS_PALM_DET_SENSITIVITY (1 << 2) +#define RMI_F11_HAS_SUPPRESS_ON_PALM_DETECT (1 << 3) +#define RMI_F11_HAS_TWO_PEN_THRESHOLDS (1 << 4) +#define RMI_F11_HAS_CONTACT_GEOMETRY (1 << 5) +#define RMI_F11_HAS_PEN_HOVER_DISCRIMINATION (1 << 6) +#define RMI_F11_HAS_PEN_FILTERS (1 << 7) + +/** Defs for Query 10. */ +#define RMI_F11_NR_TOUCH_SHAPES_MASK 0x1F + +/** Defs for Query 11 */ + +#define RMI_F11_HAS_Z_TUNING (1 << 0) +#define RMI_F11_HAS_ALGORITHM_SELECTION (1 << 1) +#define RMI_F11_HAS_W_TUNING (1 << 2) +#define RMI_F11_HAS_PITCH_INFO (1 << 3) +#define RMI_F11_HAS_FINGER_SIZE (1 << 4) +#define RMI_F11_HAS_SEGMENTATION_AGGRESSIVENESS (1 << 5) +#define RMI_F11_HAS_XY_CLIP (1 << 6) +#define RMI_F11_HAS_DRUMMING_FILTER (1 << 7) + +/** Defs for Query 12. */ + +#define RMI_F11_HAS_GAPLESS_FINGER (1 << 0) +#define RMI_F11_HAS_GAPLESS_FINGER_TUNING (1 << 1) +#define RMI_F11_HAS_8BIT_W (1 << 2) +#define RMI_F11_HAS_ADJUSTABLE_MAPPING (1 << 3) +#define RMI_F11_HAS_INFO2 (1 << 4) +#define RMI_F11_HAS_PHYSICAL_PROPS (1 << 5) +#define RMI_F11_HAS_FINGER_LIMIT (1 << 6) +#define RMI_F11_HAS_LINEAR_COEFF (1 << 7) + +/** Defs for Query 13. */ + +#define RMI_F11_JITTER_WINDOW_MASK 0x1F +#define RMI_F11_JITTER_FILTER_MASK 0x60 +#define RMI_F11_JITTER_FILTER_SHIFT 5 + +/** Defs for Query 14. */ +#define RMI_F11_LIGHT_CONTROL_MASK 0x03 +#define RMI_F11_IS_CLEAR (1 << 2) +#define RMI_F11_CLICKPAD_PROPS_MASK 0x18 +#define RMI_F11_CLICKPAD_PROPS_SHIFT 3 +#define RMI_F11_MOUSE_BUTTONS_MASK 0x60 +#define RMI_F11_MOUSE_BUTTONS_SHIFT 5 +#define RMI_F11_HAS_ADVANCED_GESTURES (1 << 7) + +#define RMI_F11_QUERY_SIZE 4 +#define RMI_F11_QUERY_GESTURE_SIZE 2 + +#define F11_LIGHT_CTL_NONE 0x00 +#define F11_LUXPAD 0x01 +#define F11_DUAL_MODE 0x02 + +#define F11_NOT_CLICKPAD 0x00 +#define F11_HINGED_CLICKPAD 0x01 +#define F11_UNIFORM_CLICKPAD 0x02 + +/** + * Query registers 1 through 4 are always present. + * + * @nr_fingers - describes the maximum number of fingers the 2-D sensor + * supports. + * @has_rel - the sensor supports relative motion reporting. + * @has_abs - the sensor supports absolute poition reporting. + * @has_gestures - the sensor supports gesture reporting. + * @has_sensitivity_adjust - the sensor supports a global sensitivity + * adjustment. + * @configurable - the sensor supports various configuration options. + * @num_of_x_electrodes - the maximum number of electrodes the 2-D sensor + * supports on the X axis. + * @num_of_y_electrodes - the maximum number of electrodes the 2-D sensor + * supports on the Y axis. + * @max_electrodes - the total number of X and Y electrodes that may be + * configured. + * + * Query 5 is present if the has_abs bit is set. + * + * @abs_data_size - describes the format of data reported by the absolute + * data source. Only one format (the kind used here) is supported at this + * time. + * @has_anchored_finger - then the sensor supports the high-precision second + * finger tracking provided by the manual tracking and motion sensitivity + * options. + * @has_adjust_hyst - the difference between the finger release threshold and + * the touch threshold. + * @has_dribble - the sensor supports the generation of dribble interrupts, + * which may be enabled or disabled with the dribble control bit. + * @has_bending_correction - Bending related data registers 28 and 36, and + * control register 52..57 are present. + * @has_large_object_suppression - control register 58 and data register 28 + * exist. + * @has_jitter_filter - query 13 and control 73..76 exist. + * + * Gesture information queries 7 and 8 are present if has_gestures bit is set. + * + * @has_single_tap - a basic single-tap gesture is supported. + * @has_tap_n_hold - tap-and-hold gesture is supported. + * @has_double_tap - double-tap gesture is supported. + * @has_early_tap - early tap is supported and reported as soon as the finger + * lifts for any tap event that could be interpreted as either a single tap + * or as the first tap of a double-tap or tap-and-hold gesture. + * @has_flick - flick detection is supported. + * @has_press - press gesture reporting is supported. + * @has_pinch - pinch gesture detection is supported. + * @has_palm_det - the 2-D sensor notifies the host whenever a large conductive + * object such as a palm or a cheek touches the 2-D sensor. + * @has_rotate - rotation gesture detection is supported. + * @has_touch_shapes - TouchShapes are supported. A TouchShape is a fixed + * rectangular area on the sensor that behaves like a capacitive button. + * @has_scroll_zones - scrolling areas near the sensor edges are supported. + * @has_individual_scroll_zones - if 1, then 4 scroll zones are supported; + * if 0, then only two are supported. + * @has_mf_scroll - the multifinger_scrolling bit will be set when + * more than one finger is involved in a scrolling action. + * + * Convenience for checking bytes in the gesture info registers. This is done + * often enough that we put it here to declutter the conditionals + * + * @query7_nonzero - true if none of the query 7 bits are set + * @query8_nonzero - true if none of the query 8 bits are set + * + * Query 9 is present if the has_query9 is set. + * + * @has_pen - detection of a stylus is supported and registers F11_2D_Ctrl20 + * and F11_2D_Ctrl21 exist. + * @has_proximity - detection of fingers near the sensor is supported and + * registers F11_2D_Ctrl22 through F11_2D_Ctrl26 exist. + * @has_palm_det_sensitivity - the sensor supports the palm detect sensitivity + * feature and register F11_2D_Ctrl27 exists. + * @has_two_pen_thresholds - is has_pen is also set, then F11_2D_Ctrl35 exists. + * @has_contact_geometry - the sensor supports the use of contact geometry to + * map absolute X and Y target positions and registers F11_2D_Data18 + * through F11_2D_Data27 exist. + * + * Touch shape info (query 10) is present if has_touch_shapes is set. + * + * @nr_touch_shapes - the total number of touch shapes supported. + * + * Query 11 is present if the has_query11 bit is set in query 0. + * + * @has_z_tuning - if set, the sensor supports Z tuning and registers + * F11_2D_Ctrl29 through F11_2D_Ctrl33 exist. + * @has_algorithm_selection - controls choice of noise suppression algorithm + * @has_w_tuning - the sensor supports Wx and Wy scaling and registers + * F11_2D_Ctrl36 through F11_2D_Ctrl39 exist. + * @has_pitch_info - the X and Y pitches of the sensor electrodes can be + * configured and registers F11_2D_Ctrl40 and F11_2D_Ctrl41 exist. + * @has_finger_size - the default finger width settings for the + * sensor can be configured and registers F11_2D_Ctrl42 through F11_2D_Ctrl44 + * exist. + * @has_segmentation_aggressiveness - the sensor’s ability to distinguish + * multiple objects close together can be configured and register F11_2D_Ctrl45 + * exists. + * @has_XY_clip - the inactive outside borders of the sensor can be + * configured and registers F11_2D_Ctrl46 through F11_2D_Ctrl49 exist. + * @has_drumming_filter - the sensor can be configured to distinguish + * between a fast flick and a quick drumming movement and registers + * F11_2D_Ctrl50 and F11_2D_Ctrl51 exist. + * + * Query 12 is present if hasQuery12 bit is set. + * + * @has_gapless_finger - control registers relating to gapless finger are + * present. + * @has_gapless_finger_tuning - additional control and data registers relating + * to gapless finger are present. + * @has_8bit_w - larger W value reporting is supported. + * @has_adjustable_mapping - TBD + * @has_info2 - the general info query14 is present + * @has_physical_props - additional queries describing the physical properties + * of the sensor are present. + * @has_finger_limit - indicates that F11 Ctrl 80 exists. + * @has_linear_coeff - indicates that F11 Ctrl 81 exists. + * + * Query 13 is present if Query 5's has_jitter_filter bit is set. + * @jitter_window_size - used by Design Studio 4. + * @jitter_filter_type - used by Design Studio 4. + * + * Query 14 is present if query 12's has_general_info2 flag is set. + * + * @light_control - Indicates what light/led control features are present, if + * any. + * @is_clear - if set, this is a clear sensor (indicating direct pointing + * application), otherwise it's opaque (indicating indirect pointing). + * @clickpad_props - specifies if this is a clickpad, and if so what sort of + * mechanism it uses + * @mouse_buttons - specifies the number of mouse buttons present (if any). + * @has_advanced_gestures - advanced driver gestures are supported. + */ +struct f11_2d_sensor_queries { + /* query1 */ + u8 nr_fingers; + bool has_rel; + bool has_abs; + bool has_gestures; + bool has_sensitivity_adjust; + bool configurable; + + /* query2 */ + u8 nr_x_electrodes; + + /* query3 */ + u8 nr_y_electrodes; + + /* query4 */ + u8 max_electrodes; + + /* query5 */ + u8 abs_data_size; + bool has_anchored_finger; + bool has_adj_hyst; + bool has_dribble; + bool has_bending_correction; + bool has_large_object_suppression; + bool has_jitter_filter; + + u8 f11_2d_query6; + + /* query 7 */ + bool has_single_tap; + bool has_tap_n_hold; + bool has_double_tap; + bool has_early_tap; + bool has_flick; + bool has_press; + bool has_pinch; + bool has_chiral; + + bool query7_nonzero; + + /* query 8 */ + bool has_palm_det; + bool has_rotate; + bool has_touch_shapes; + bool has_scroll_zones; + bool has_individual_scroll_zones; + bool has_mf_scroll; + bool has_mf_edge_motion; + bool has_mf_scroll_inertia; + + bool query8_nonzero; + + /* Query 9 */ + bool has_pen; + bool has_proximity; + bool has_palm_det_sensitivity; + bool has_suppress_on_palm_detect; + bool has_two_pen_thresholds; + bool has_contact_geometry; + bool has_pen_hover_discrimination; + bool has_pen_filters; + + /* Query 10 */ + u8 nr_touch_shapes; + + /* Query 11. */ + bool has_z_tuning; + bool has_algorithm_selection; + bool has_w_tuning; + bool has_pitch_info; + bool has_finger_size; + bool has_segmentation_aggressiveness; + bool has_XY_clip; + bool has_drumming_filter; + + /* Query 12 */ + bool has_gapless_finger; + bool has_gapless_finger_tuning; + bool has_8bit_w; + bool has_adjustable_mapping; + bool has_info2; + bool has_physical_props; + bool has_finger_limit; + bool has_linear_coeff_2; + + /* Query 13 */ + u8 jitter_window_size; + u8 jitter_filter_type; + + /* Query 14 */ + u8 light_control; + bool is_clear; + u8 clickpad_props; + u8 mouse_buttons; + bool has_advanced_gestures; + + /* Query 15 - 18 */ + u16 x_sensor_size_mm; + u16 y_sensor_size_mm; +}; + +/* Defs for Ctrl0. */ +#define RMI_F11_REPORT_MODE_MASK 0x07 +#define RMI_F11_ABS_POS_FILT (1 << 3) +#define RMI_F11_REL_POS_FILT (1 << 4) +#define RMI_F11_REL_BALLISTICS (1 << 5) +#define RMI_F11_DRIBBLE (1 << 6) +#define RMI_F11_REPORT_BEYOND_CLIP (1 << 7) + +/* Defs for Ctrl1. */ +#define RMI_F11_PALM_DETECT_THRESH_MASK 0x0F +#define RMI_F11_MOTION_SENSITIVITY_MASK 0x30 +#define RMI_F11_MANUAL_TRACKING (1 << 6) +#define RMI_F11_MANUAL_TRACKED_FINGER (1 << 7) + +#define RMI_F11_DELTA_X_THRESHOLD 2 +#define RMI_F11_DELTA_Y_THRESHOLD 3 + +#define RMI_F11_CTRL_REG_COUNT 12 + +struct f11_2d_ctrl { + u8 ctrl0_11[RMI_F11_CTRL_REG_COUNT]; + u16 ctrl0_11_address; +}; + +#define RMI_F11_ABS_BYTES 5 +#define RMI_F11_REL_BYTES 2 + +/* Defs for Data 8 */ + +#define RMI_F11_SINGLE_TAP (1 << 0) +#define RMI_F11_TAP_AND_HOLD (1 << 1) +#define RMI_F11_DOUBLE_TAP (1 << 2) +#define RMI_F11_EARLY_TAP (1 << 3) +#define RMI_F11_FLICK (1 << 4) +#define RMI_F11_PRESS (1 << 5) +#define RMI_F11_PINCH (1 << 6) + +/* Defs for Data 9 */ + +#define RMI_F11_PALM_DETECT (1 << 0) +#define RMI_F11_ROTATE (1 << 1) +#define RMI_F11_SHAPE (1 << 2) +#define RMI_F11_SCROLLZONE (1 << 3) +#define RMI_F11_GESTURE_FINGER_COUNT_MASK 0x70 + +/** Handy pointers into our data buffer. + * + * @f_state - start of finger state registers. + * @abs_pos - start of absolute position registers (if present). + * @rel_pos - start of relative data registers (if present). + * @gest_1 - gesture flags (if present). + * @gest_2 - gesture flags & finger count (if present). + * @pinch - pinch motion register (if present). + * @flick - flick distance X & Y, flick time (if present). + * @rotate - rotate motion and finger separation. + * @multi_scroll - chiral deltas for X and Y (if present). + * @scroll_zones - scroll deltas for 4 regions (if present). + */ +struct f11_2d_data { + u8 *f_state; + u8 *abs_pos; + s8 *rel_pos; + u8 *gest_1; + u8 *gest_2; + s8 *pinch; + u8 *flick; + u8 *rotate; + u8 *shapes; + s8 *multi_scroll; + s8 *scroll_zones; +}; + +/** Data pertaining to F11 in general. For per-sensor data, see struct + * f11_2d_sensor. + * + * @dev_query - F11 device specific query registers. + * @dev_controls - F11 device specific control registers. + * @dev_controls_mutex - lock for the control registers. + * @rezero_wait_ms - if nonzero, upon resume we will wait this many + * milliseconds before rezeroing the sensor(s). This is useful in systems with + * poor electrical behavior on resume, where the initial calibration of the + * sensor(s) coming out of sleep state may be bogus. + * @sensors - per sensor data structures. + */ +struct f11_data { + bool has_query9; + bool has_query11; + bool has_query12; + bool has_query27; + bool has_query28; + bool has_acm; + struct f11_2d_ctrl dev_controls; + struct mutex dev_controls_mutex; + u16 rezero_wait_ms; + struct rmi_2d_sensor sensor; + struct f11_2d_sensor_queries sens_query; + struct f11_2d_data data; + struct rmi_2d_sensor_platform_data sensor_pdata; + unsigned long *abs_mask; + unsigned long *rel_mask; + unsigned long *result_bits; +}; + +enum f11_finger_state { + F11_NO_FINGER = 0x00, + F11_PRESENT = 0x01, + F11_INACCURATE = 0x02, + F11_RESERVED = 0x03 +}; + +static void rmi_f11_rel_pos_report(struct f11_data *f11, u8 n_finger) +{ + struct rmi_2d_sensor *sensor = &f11->sensor; + struct f11_2d_data *data = &f11->data; + s8 x, y; + + x = data->rel_pos[n_finger * 2]; + y = data->rel_pos[n_finger * 2 + 1]; + + rmi_2d_sensor_rel_report(sensor, x, y); +} + +static void rmi_f11_abs_pos_process(struct f11_data *f11, + struct rmi_2d_sensor *sensor, + struct rmi_2d_sensor_abs_object *obj, + enum f11_finger_state finger_state, + u8 n_finger) +{ + struct f11_2d_data *data = &f11->data; + u8 *pos_data = &data->abs_pos[n_finger * RMI_F11_ABS_BYTES]; + int tool_type = MT_TOOL_FINGER; + + switch (finger_state) { + case F11_PRESENT: + obj->type = RMI_2D_OBJECT_FINGER; + break; + default: + obj->type = RMI_2D_OBJECT_NONE; + } + + obj->mt_tool = tool_type; + obj->x = (pos_data[0] << 4) | (pos_data[2] & 0x0F); + obj->y = (pos_data[1] << 4) | (pos_data[2] >> 4); + obj->z = pos_data[4]; + obj->wx = pos_data[3] & 0x0f; + obj->wy = pos_data[3] >> 4; + + rmi_2d_sensor_abs_process(sensor, obj, n_finger); +} + +static inline u8 rmi_f11_parse_finger_state(const u8 *f_state, u8 n_finger) +{ + return (f_state[n_finger / 4] >> (2 * (n_finger % 4))) & + FINGER_STATE_MASK; +} + +static void rmi_f11_finger_handler(struct f11_data *f11, + struct rmi_2d_sensor *sensor, + unsigned long *irq_bits, int num_irq_regs) +{ + const u8 *f_state = f11->data.f_state; + u8 finger_state; + u8 i; + + int abs_bits = bitmap_and(f11->result_bits, irq_bits, f11->abs_mask, + num_irq_regs * 8); + int rel_bits = bitmap_and(f11->result_bits, irq_bits, f11->rel_mask, + num_irq_regs * 8); + + for (i = 0; i < sensor->nbr_fingers; i++) { + /* Possible of having 4 fingers per f_statet register */ + finger_state = rmi_f11_parse_finger_state(f_state, i); + if (finger_state == F11_RESERVED) { + pr_err("Invalid finger state[%d]: 0x%02x", i, + finger_state); + continue; + } + + if (abs_bits) + rmi_f11_abs_pos_process(f11, sensor, &sensor->objs[i], + finger_state, i); + + if (rel_bits) + rmi_f11_rel_pos_report(f11, i); + } + + if (abs_bits) { + /* + * the absolute part is made in 2 parts to allow the kernel + * tracking to take place. + */ + if (sensor->kernel_tracking) + input_mt_assign_slots(sensor->input, + sensor->tracking_slots, + sensor->tracking_pos, + sensor->nbr_fingers, + sensor->dmax); + + for (i = 0; i < sensor->nbr_fingers; i++) { + finger_state = rmi_f11_parse_finger_state(f_state, i); + if (finger_state == F11_RESERVED) + /* no need to send twice the error */ + continue; + + rmi_2d_sensor_abs_report(sensor, &sensor->objs[i], i); + } + + input_mt_sync_frame(sensor->input); + } +} + +static int f11_2d_construct_data(struct f11_data *f11) +{ + struct rmi_2d_sensor *sensor = &f11->sensor; + struct f11_2d_sensor_queries *query = &f11->sens_query; + struct f11_2d_data *data = &f11->data; + int i; + + sensor->nbr_fingers = (query->nr_fingers == 5 ? 10 : + query->nr_fingers + 1); + + sensor->pkt_size = DIV_ROUND_UP(sensor->nbr_fingers, 4); + + if (query->has_abs) { + sensor->pkt_size += (sensor->nbr_fingers * 5); + sensor->attn_size = sensor->pkt_size; + } + + if (query->has_rel) + sensor->pkt_size += (sensor->nbr_fingers * 2); + + /* Check if F11_2D_Query7 is non-zero */ + if (query->query7_nonzero) + sensor->pkt_size += sizeof(u8); + + /* Check if F11_2D_Query7 or F11_2D_Query8 is non-zero */ + if (query->query7_nonzero || query->query8_nonzero) + sensor->pkt_size += sizeof(u8); + + if (query->has_pinch || query->has_flick || query->has_rotate) { + sensor->pkt_size += 3; + if (!query->has_flick) + sensor->pkt_size--; + if (!query->has_rotate) + sensor->pkt_size--; + } + + if (query->has_touch_shapes) + sensor->pkt_size += + DIV_ROUND_UP(query->nr_touch_shapes + 1, 8); + + sensor->data_pkt = devm_kzalloc(&sensor->fn->dev, sensor->pkt_size, + GFP_KERNEL); + if (!sensor->data_pkt) + return -ENOMEM; + + data->f_state = sensor->data_pkt; + i = DIV_ROUND_UP(sensor->nbr_fingers, 4); + + if (query->has_abs) { + data->abs_pos = &sensor->data_pkt[i]; + i += (sensor->nbr_fingers * RMI_F11_ABS_BYTES); + } + + if (query->has_rel) { + data->rel_pos = &sensor->data_pkt[i]; + i += (sensor->nbr_fingers * RMI_F11_REL_BYTES); + } + + if (query->query7_nonzero) { + data->gest_1 = &sensor->data_pkt[i]; + i++; + } + + if (query->query7_nonzero || query->query8_nonzero) { + data->gest_2 = &sensor->data_pkt[i]; + i++; + } + + if (query->has_pinch) { + data->pinch = &sensor->data_pkt[i]; + i++; + } + + if (query->has_flick) { + if (query->has_pinch) { + data->flick = data->pinch; + i += 2; + } else { + data->flick = &sensor->data_pkt[i]; + i += 3; + } + } + + if (query->has_rotate) { + if (query->has_flick) { + data->rotate = data->flick + 1; + } else { + data->rotate = &sensor->data_pkt[i]; + i += 2; + } + } + + if (query->has_touch_shapes) + data->shapes = &sensor->data_pkt[i]; + + return 0; +} + +static int f11_read_control_regs(struct rmi_function *fn, + struct f11_2d_ctrl *ctrl, u16 ctrl_base_addr) { + struct rmi_device *rmi_dev = fn->rmi_dev; + int error = 0; + + ctrl->ctrl0_11_address = ctrl_base_addr; + error = rmi_read_block(rmi_dev, ctrl_base_addr, ctrl->ctrl0_11, + RMI_F11_CTRL_REG_COUNT); + if (error < 0) { + dev_err(&fn->dev, "Failed to read ctrl0, code: %d.\n", error); + return error; + } + + return 0; +} + +static int f11_write_control_regs(struct rmi_function *fn, + struct f11_2d_sensor_queries *query, + struct f11_2d_ctrl *ctrl, + u16 ctrl_base_addr) +{ + struct rmi_device *rmi_dev = fn->rmi_dev; + int error; + + error = rmi_write_block(rmi_dev, ctrl_base_addr, ctrl->ctrl0_11, + RMI_F11_CTRL_REG_COUNT); + if (error < 0) + return error; + + return 0; +} + +static int rmi_f11_get_query_parameters(struct rmi_device *rmi_dev, + struct f11_data *f11, + struct f11_2d_sensor_queries *sensor_query, + u16 query_base_addr) +{ + int query_size; + int rc; + u8 query_buf[RMI_F11_QUERY_SIZE]; + bool has_query36 = false; + + rc = rmi_read_block(rmi_dev, query_base_addr, query_buf, + RMI_F11_QUERY_SIZE); + if (rc < 0) + return rc; + + sensor_query->nr_fingers = query_buf[0] & RMI_F11_NR_FINGERS_MASK; + sensor_query->has_rel = !!(query_buf[0] & RMI_F11_HAS_REL); + sensor_query->has_abs = !!(query_buf[0] & RMI_F11_HAS_ABS); + sensor_query->has_gestures = !!(query_buf[0] & RMI_F11_HAS_GESTURES); + sensor_query->has_sensitivity_adjust = + !!(query_buf[0] & RMI_F11_HAS_SENSITIVITY_ADJ); + sensor_query->configurable = !!(query_buf[0] & RMI_F11_CONFIGURABLE); + + sensor_query->nr_x_electrodes = + query_buf[1] & RMI_F11_NR_ELECTRODES_MASK; + sensor_query->nr_y_electrodes = + query_buf[2] & RMI_F11_NR_ELECTRODES_MASK; + sensor_query->max_electrodes = + query_buf[3] & RMI_F11_NR_ELECTRODES_MASK; + + query_size = RMI_F11_QUERY_SIZE; + + if (sensor_query->has_abs) { + rc = rmi_read(rmi_dev, query_base_addr + query_size, query_buf); + if (rc < 0) + return rc; + + sensor_query->abs_data_size = + query_buf[0] & RMI_F11_ABS_DATA_SIZE_MASK; + sensor_query->has_anchored_finger = + !!(query_buf[0] & RMI_F11_HAS_ANCHORED_FINGER); + sensor_query->has_adj_hyst = + !!(query_buf[0] & RMI_F11_HAS_ADJ_HYST); + sensor_query->has_dribble = + !!(query_buf[0] & RMI_F11_HAS_DRIBBLE); + sensor_query->has_bending_correction = + !!(query_buf[0] & RMI_F11_HAS_BENDING_CORRECTION); + sensor_query->has_large_object_suppression = + !!(query_buf[0] & RMI_F11_HAS_LARGE_OBJECT_SUPPRESSION); + sensor_query->has_jitter_filter = + !!(query_buf[0] & RMI_F11_HAS_JITTER_FILTER); + query_size++; + } + + if (sensor_query->has_rel) { + rc = rmi_read(rmi_dev, query_base_addr + query_size, + &sensor_query->f11_2d_query6); + if (rc < 0) + return rc; + query_size++; + } + + if (sensor_query->has_gestures) { + rc = rmi_read_block(rmi_dev, query_base_addr + query_size, + query_buf, RMI_F11_QUERY_GESTURE_SIZE); + if (rc < 0) + return rc; + + sensor_query->has_single_tap = + !!(query_buf[0] & RMI_F11_HAS_SINGLE_TAP); + sensor_query->has_tap_n_hold = + !!(query_buf[0] & RMI_F11_HAS_TAP_AND_HOLD); + sensor_query->has_double_tap = + !!(query_buf[0] & RMI_F11_HAS_DOUBLE_TAP); + sensor_query->has_early_tap = + !!(query_buf[0] & RMI_F11_HAS_EARLY_TAP); + sensor_query->has_flick = + !!(query_buf[0] & RMI_F11_HAS_FLICK); + sensor_query->has_press = + !!(query_buf[0] & RMI_F11_HAS_PRESS); + sensor_query->has_pinch = + !!(query_buf[0] & RMI_F11_HAS_PINCH); + sensor_query->has_chiral = + !!(query_buf[0] & RMI_F11_HAS_CHIRAL); + + /* query 8 */ + sensor_query->has_palm_det = + !!(query_buf[1] & RMI_F11_HAS_PALM_DET); + sensor_query->has_rotate = + !!(query_buf[1] & RMI_F11_HAS_ROTATE); + sensor_query->has_touch_shapes = + !!(query_buf[1] & RMI_F11_HAS_TOUCH_SHAPES); + sensor_query->has_scroll_zones = + !!(query_buf[1] & RMI_F11_HAS_SCROLL_ZONES); + sensor_query->has_individual_scroll_zones = + !!(query_buf[1] & RMI_F11_HAS_INDIVIDUAL_SCROLL_ZONES); + sensor_query->has_mf_scroll = + !!(query_buf[1] & RMI_F11_HAS_MF_SCROLL); + sensor_query->has_mf_edge_motion = + !!(query_buf[1] & RMI_F11_HAS_MF_EDGE_MOTION); + sensor_query->has_mf_scroll_inertia = + !!(query_buf[1] & RMI_F11_HAS_MF_SCROLL_INERTIA); + + sensor_query->query7_nonzero = !!(query_buf[0]); + sensor_query->query8_nonzero = !!(query_buf[1]); + + query_size += 2; + } + + if (f11->has_query9) { + rc = rmi_read(rmi_dev, query_base_addr + query_size, query_buf); + if (rc < 0) + return rc; + + sensor_query->has_pen = + !!(query_buf[0] & RMI_F11_HAS_PEN); + sensor_query->has_proximity = + !!(query_buf[0] & RMI_F11_HAS_PROXIMITY); + sensor_query->has_palm_det_sensitivity = + !!(query_buf[0] & RMI_F11_HAS_PALM_DET_SENSITIVITY); + sensor_query->has_suppress_on_palm_detect = + !!(query_buf[0] & RMI_F11_HAS_SUPPRESS_ON_PALM_DETECT); + sensor_query->has_two_pen_thresholds = + !!(query_buf[0] & RMI_F11_HAS_TWO_PEN_THRESHOLDS); + sensor_query->has_contact_geometry = + !!(query_buf[0] & RMI_F11_HAS_CONTACT_GEOMETRY); + sensor_query->has_pen_hover_discrimination = + !!(query_buf[0] & RMI_F11_HAS_PEN_HOVER_DISCRIMINATION); + sensor_query->has_pen_filters = + !!(query_buf[0] & RMI_F11_HAS_PEN_FILTERS); + + query_size++; + } + + if (sensor_query->has_touch_shapes) { + rc = rmi_read(rmi_dev, query_base_addr + query_size, query_buf); + if (rc < 0) + return rc; + + sensor_query->nr_touch_shapes = query_buf[0] & + RMI_F11_NR_TOUCH_SHAPES_MASK; + + query_size++; + } + + if (f11->has_query11) { + rc = rmi_read(rmi_dev, query_base_addr + query_size, query_buf); + if (rc < 0) + return rc; + + sensor_query->has_z_tuning = + !!(query_buf[0] & RMI_F11_HAS_Z_TUNING); + sensor_query->has_algorithm_selection = + !!(query_buf[0] & RMI_F11_HAS_ALGORITHM_SELECTION); + sensor_query->has_w_tuning = + !!(query_buf[0] & RMI_F11_HAS_W_TUNING); + sensor_query->has_pitch_info = + !!(query_buf[0] & RMI_F11_HAS_PITCH_INFO); + sensor_query->has_finger_size = + !!(query_buf[0] & RMI_F11_HAS_FINGER_SIZE); + sensor_query->has_segmentation_aggressiveness = + !!(query_buf[0] & + RMI_F11_HAS_SEGMENTATION_AGGRESSIVENESS); + sensor_query->has_XY_clip = + !!(query_buf[0] & RMI_F11_HAS_XY_CLIP); + sensor_query->has_drumming_filter = + !!(query_buf[0] & RMI_F11_HAS_DRUMMING_FILTER); + + query_size++; + } + + if (f11->has_query12) { + rc = rmi_read(rmi_dev, query_base_addr + query_size, query_buf); + if (rc < 0) + return rc; + + sensor_query->has_gapless_finger = + !!(query_buf[0] & RMI_F11_HAS_GAPLESS_FINGER); + sensor_query->has_gapless_finger_tuning = + !!(query_buf[0] & RMI_F11_HAS_GAPLESS_FINGER_TUNING); + sensor_query->has_8bit_w = + !!(query_buf[0] & RMI_F11_HAS_8BIT_W); + sensor_query->has_adjustable_mapping = + !!(query_buf[0] & RMI_F11_HAS_ADJUSTABLE_MAPPING); + sensor_query->has_info2 = + !!(query_buf[0] & RMI_F11_HAS_INFO2); + sensor_query->has_physical_props = + !!(query_buf[0] & RMI_F11_HAS_PHYSICAL_PROPS); + sensor_query->has_finger_limit = + !!(query_buf[0] & RMI_F11_HAS_FINGER_LIMIT); + sensor_query->has_linear_coeff_2 = + !!(query_buf[0] & RMI_F11_HAS_LINEAR_COEFF); + + query_size++; + } + + if (sensor_query->has_jitter_filter) { + rc = rmi_read(rmi_dev, query_base_addr + query_size, query_buf); + if (rc < 0) + return rc; + + sensor_query->jitter_window_size = query_buf[0] & + RMI_F11_JITTER_WINDOW_MASK; + sensor_query->jitter_filter_type = (query_buf[0] & + RMI_F11_JITTER_FILTER_MASK) >> + RMI_F11_JITTER_FILTER_SHIFT; + + query_size++; + } + + if (sensor_query->has_info2) { + rc = rmi_read(rmi_dev, query_base_addr + query_size, query_buf); + if (rc < 0) + return rc; + + sensor_query->light_control = + query_buf[0] & RMI_F11_LIGHT_CONTROL_MASK; + sensor_query->is_clear = + !!(query_buf[0] & RMI_F11_IS_CLEAR); + sensor_query->clickpad_props = + (query_buf[0] & RMI_F11_CLICKPAD_PROPS_MASK) >> + RMI_F11_CLICKPAD_PROPS_SHIFT; + sensor_query->mouse_buttons = + (query_buf[0] & RMI_F11_MOUSE_BUTTONS_MASK) >> + RMI_F11_MOUSE_BUTTONS_SHIFT; + sensor_query->has_advanced_gestures = + !!(query_buf[0] & RMI_F11_HAS_ADVANCED_GESTURES); + + query_size++; + } + + if (sensor_query->has_physical_props) { + rc = rmi_read_block(rmi_dev, query_base_addr + + query_size, query_buf, 4); + if (rc < 0) + return rc; + + sensor_query->x_sensor_size_mm = + (query_buf[0] | (query_buf[1] << 8)) / 10; + sensor_query->y_sensor_size_mm = + (query_buf[2] | (query_buf[3] << 8)) / 10; + + /* + * query 15 - 18 contain the size of the sensor + * and query 19 - 26 contain bezel dimensions + */ + query_size += 12; + } + + if (f11->has_query27) + ++query_size; + + if (f11->has_query28) { + rc = rmi_read(rmi_dev, query_base_addr + query_size, + query_buf); + if (rc < 0) + return rc; + + has_query36 = !!(query_buf[0] & BIT(6)); + } + + if (has_query36) { + query_size += 2; + rc = rmi_read(rmi_dev, query_base_addr + query_size, + query_buf); + if (rc < 0) + return rc; + + if (!!(query_buf[0] & BIT(5))) + f11->has_acm = true; + } + + return query_size; +} + +static int rmi_f11_initialize(struct rmi_function *fn) +{ + struct rmi_device *rmi_dev = fn->rmi_dev; + struct f11_data *f11; + struct f11_2d_ctrl *ctrl; + u8 query_offset; + u16 query_base_addr; + u16 control_base_addr; + u16 max_x_pos, max_y_pos; + int rc; + const struct rmi_device_platform_data *pdata = + rmi_get_platform_data(rmi_dev); + struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev); + struct rmi_2d_sensor *sensor; + u8 buf; + int mask_size; + + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "Initializing F11 values.\n"); + + mask_size = BITS_TO_LONGS(drvdata->irq_count) * sizeof(unsigned long); + + /* + ** init instance data, fill in values and create any sysfs files + */ + f11 = devm_kzalloc(&fn->dev, sizeof(struct f11_data) + mask_size * 3, + GFP_KERNEL); + if (!f11) + return -ENOMEM; + + if (fn->dev.of_node) { + rc = rmi_2d_sensor_of_probe(&fn->dev, &f11->sensor_pdata); + if (rc) + return rc; + } else if (pdata->sensor_pdata) { + f11->sensor_pdata = *pdata->sensor_pdata; + } + + f11->rezero_wait_ms = f11->sensor_pdata.rezero_wait; + + f11->abs_mask = (unsigned long *)((char *)f11 + + sizeof(struct f11_data)); + f11->rel_mask = (unsigned long *)((char *)f11 + + sizeof(struct f11_data) + mask_size); + f11->result_bits = (unsigned long *)((char *)f11 + + sizeof(struct f11_data) + mask_size * 2); + + set_bit(fn->irq_pos, f11->abs_mask); + set_bit(fn->irq_pos + 1, f11->rel_mask); + + query_base_addr = fn->fd.query_base_addr; + control_base_addr = fn->fd.control_base_addr; + + rc = rmi_read(rmi_dev, query_base_addr, &buf); + if (rc < 0) + return rc; + + f11->has_query9 = !!(buf & RMI_F11_HAS_QUERY9); + f11->has_query11 = !!(buf & RMI_F11_HAS_QUERY11); + f11->has_query12 = !!(buf & RMI_F11_HAS_QUERY12); + f11->has_query27 = !!(buf & RMI_F11_HAS_QUERY27); + f11->has_query28 = !!(buf & RMI_F11_HAS_QUERY28); + + query_offset = (query_base_addr + 1); + sensor = &f11->sensor; + sensor->fn = fn; + + rc = rmi_f11_get_query_parameters(rmi_dev, f11, + &f11->sens_query, query_offset); + if (rc < 0) + return rc; + query_offset += rc; + + rc = f11_read_control_regs(fn, &f11->dev_controls, + control_base_addr); + if (rc < 0) { + dev_err(&fn->dev, + "Failed to read F11 control params.\n"); + return rc; + } + + if (f11->sens_query.has_info2) { + if (f11->sens_query.is_clear) + f11->sensor.sensor_type = rmi_sensor_touchscreen; + else + f11->sensor.sensor_type = rmi_sensor_touchpad; + } + + sensor->report_abs = f11->sens_query.has_abs; + + sensor->axis_align = + f11->sensor_pdata.axis_align; + + sensor->topbuttonpad = f11->sensor_pdata.topbuttonpad; + sensor->kernel_tracking = f11->sensor_pdata.kernel_tracking; + sensor->dmax = f11->sensor_pdata.dmax; + + if (f11->sens_query.has_physical_props) { + sensor->x_mm = f11->sens_query.x_sensor_size_mm; + sensor->y_mm = f11->sens_query.y_sensor_size_mm; + } else { + sensor->x_mm = f11->sensor_pdata.x_mm; + sensor->y_mm = f11->sensor_pdata.y_mm; + } + + if (sensor->sensor_type == rmi_sensor_default) + sensor->sensor_type = + f11->sensor_pdata.sensor_type; + + sensor->report_abs = sensor->report_abs + && !(f11->sensor_pdata.disable_report_mask + & RMI_F11_DISABLE_ABS_REPORT); + + if (!sensor->report_abs) + /* + * If device doesn't have abs or if it has been disables + * fallback to reporting rel data. + */ + sensor->report_rel = f11->sens_query.has_rel; + + rc = rmi_read_block(rmi_dev, + control_base_addr + F11_CTRL_SENSOR_MAX_X_POS_OFFSET, + (u8 *)&max_x_pos, sizeof(max_x_pos)); + if (rc < 0) + return rc; + + rc = rmi_read_block(rmi_dev, + control_base_addr + F11_CTRL_SENSOR_MAX_Y_POS_OFFSET, + (u8 *)&max_y_pos, sizeof(max_y_pos)); + if (rc < 0) + return rc; + + sensor->max_x = max_x_pos; + sensor->max_y = max_y_pos; + + rc = f11_2d_construct_data(f11); + if (rc < 0) + return rc; + + if (f11->has_acm) + f11->sensor.attn_size += f11->sensor.nbr_fingers * 2; + + /* allocate the in-kernel tracking buffers */ + sensor->tracking_pos = devm_kzalloc(&fn->dev, + sizeof(struct input_mt_pos) * sensor->nbr_fingers, + GFP_KERNEL); + sensor->tracking_slots = devm_kzalloc(&fn->dev, + sizeof(int) * sensor->nbr_fingers, GFP_KERNEL); + sensor->objs = devm_kzalloc(&fn->dev, + sizeof(struct rmi_2d_sensor_abs_object) + * sensor->nbr_fingers, GFP_KERNEL); + if (!sensor->tracking_pos || !sensor->tracking_slots || !sensor->objs) + return -ENOMEM; + + ctrl = &f11->dev_controls; + if (sensor->axis_align.delta_x_threshold) + ctrl->ctrl0_11[RMI_F11_DELTA_X_THRESHOLD] = + sensor->axis_align.delta_x_threshold; + + if (sensor->axis_align.delta_y_threshold) + ctrl->ctrl0_11[RMI_F11_DELTA_Y_THRESHOLD] = + sensor->axis_align.delta_y_threshold; + + if (f11->sens_query.has_dribble) + ctrl->ctrl0_11[0] = ctrl->ctrl0_11[0] & ~BIT(6); + + if (f11->sens_query.has_palm_det) + ctrl->ctrl0_11[11] = ctrl->ctrl0_11[11] & ~BIT(0); + + rc = f11_write_control_regs(fn, &f11->sens_query, + &f11->dev_controls, fn->fd.query_base_addr); + if (rc) + dev_warn(&fn->dev, "Failed to write control registers\n"); + + mutex_init(&f11->dev_controls_mutex); + + dev_set_drvdata(&fn->dev, f11); + + return 0; +} + +static int rmi_f11_config(struct rmi_function *fn) +{ + struct f11_data *f11 = dev_get_drvdata(&fn->dev); + struct rmi_driver *drv = fn->rmi_dev->driver; + struct rmi_2d_sensor *sensor = &f11->sensor; + int rc; + + if (!sensor->report_abs) + drv->clear_irq_bits(fn->rmi_dev, f11->abs_mask); + else + drv->set_irq_bits(fn->rmi_dev, f11->abs_mask); + + if (!sensor->report_rel) + drv->clear_irq_bits(fn->rmi_dev, f11->rel_mask); + else + drv->set_irq_bits(fn->rmi_dev, f11->rel_mask); + + rc = f11_write_control_regs(fn, &f11->sens_query, + &f11->dev_controls, fn->fd.query_base_addr); + if (rc < 0) + return rc; + + return 0; +} + +static int rmi_f11_attention(struct rmi_function *fn, unsigned long *irq_bits) +{ + struct rmi_device *rmi_dev = fn->rmi_dev; + struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev); + struct f11_data *f11 = dev_get_drvdata(&fn->dev); + u16 data_base_addr = fn->fd.data_base_addr; + u16 data_base_addr_offset = 0; + int error; + + if (rmi_dev->xport->attn_data) { + memcpy(f11->sensor.data_pkt, rmi_dev->xport->attn_data, + f11->sensor.attn_size); + rmi_dev->xport->attn_data += f11->sensor.attn_size; + rmi_dev->xport->attn_size -= f11->sensor.attn_size; + } else { + error = rmi_read_block(rmi_dev, + data_base_addr + data_base_addr_offset, + f11->sensor.data_pkt, + f11->sensor.pkt_size); + if (error < 0) + return error; + } + + rmi_f11_finger_handler(f11, &f11->sensor, irq_bits, + drvdata->num_of_irq_regs); + data_base_addr_offset += f11->sensor.pkt_size; + + return 0; +} + +static int rmi_f11_resume(struct rmi_function *fn) +{ + struct f11_data *f11 = dev_get_drvdata(&fn->dev); + int error; + + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "Resuming...\n"); + if (!f11->rezero_wait_ms) + return 0; + + mdelay(f11->rezero_wait_ms); + + error = rmi_write(fn->rmi_dev, fn->fd.command_base_addr, + RMI_F11_REZERO); + if (error) { + dev_err(&fn->dev, + "%s: failed to issue rezero command, error = %d.", + __func__, error); + return error; + } + + return 0; +} + +static int rmi_f11_probe(struct rmi_function *fn) +{ + int error; + struct f11_data *f11; + + error = rmi_f11_initialize(fn); + if (error) + return error; + + f11 = dev_get_drvdata(&fn->dev); + error = rmi_2d_sensor_configure_input(fn, &f11->sensor); + if (error) + return error; + + return 0; +} + +struct rmi_function_handler rmi_f11_handler = { + .driver = { + .name = "rmi4_f11", + }, + .func = 0x11, + .probe = rmi_f11_probe, + .config = rmi_f11_config, + .attention = rmi_f11_attention, + .resume = rmi_f11_resume, +}; diff --git a/drivers/input/rmi4/rmi_f12.c b/drivers/input/rmi4/rmi_f12.c new file mode 100644 index 000000000..8dd3fb5e1 --- /dev/null +++ b/drivers/input/rmi4/rmi_f12.c @@ -0,0 +1,457 @@ +/* + * Copyright (c) 2012-2016 Synaptics Incorporated + * + * 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/input.h> +#include <linux/input/mt.h> +#include <linux/rmi.h> +#include "rmi_driver.h" +#include "rmi_2d_sensor.h" + +enum rmi_f12_object_type { + RMI_F12_OBJECT_NONE = 0x00, + RMI_F12_OBJECT_FINGER = 0x01, + RMI_F12_OBJECT_STYLUS = 0x02, + RMI_F12_OBJECT_PALM = 0x03, + RMI_F12_OBJECT_UNCLASSIFIED = 0x04, + RMI_F12_OBJECT_GLOVED_FINGER = 0x06, + RMI_F12_OBJECT_NARROW_OBJECT = 0x07, + RMI_F12_OBJECT_HAND_EDGE = 0x08, + RMI_F12_OBJECT_COVER = 0x0A, + RMI_F12_OBJECT_STYLUS_2 = 0x0B, + RMI_F12_OBJECT_ERASER = 0x0C, + RMI_F12_OBJECT_SMALL_OBJECT = 0x0D, +}; + +struct f12_data { + struct rmi_function *fn; + struct rmi_2d_sensor sensor; + struct rmi_2d_sensor_platform_data sensor_pdata; + + u16 data_addr; + + struct rmi_register_descriptor query_reg_desc; + struct rmi_register_descriptor control_reg_desc; + struct rmi_register_descriptor data_reg_desc; + + /* F12 Data1 describes sensed objects */ + const struct rmi_register_desc_item *data1; + u16 data1_offset; + + /* F12 Data5 describes finger ACM */ + const struct rmi_register_desc_item *data5; + u16 data5_offset; + + /* F12 Data5 describes Pen */ + const struct rmi_register_desc_item *data6; + u16 data6_offset; + + + /* F12 Data9 reports relative data */ + const struct rmi_register_desc_item *data9; + u16 data9_offset; + + const struct rmi_register_desc_item *data15; + u16 data15_offset; +}; + +static int rmi_f12_read_sensor_tuning(struct f12_data *f12) +{ + const struct rmi_register_desc_item *item; + struct rmi_2d_sensor *sensor = &f12->sensor; + struct rmi_function *fn = sensor->fn; + struct rmi_device *rmi_dev = fn->rmi_dev; + int ret; + int offset; + u8 buf[14]; + int pitch_x = 0; + int pitch_y = 0; + int clip_x_low = 0; + int clip_x_high = 0; + int clip_y_low = 0; + int clip_y_high = 0; + int rx_receivers = 0; + int tx_receivers = 0; + int sensor_flags = 0; + + item = rmi_get_register_desc_item(&f12->control_reg_desc, 8); + if (!item) { + dev_err(&fn->dev, + "F12 does not have the sensor tuning control register\n"); + return -ENODEV; + } + + offset = rmi_register_desc_calc_reg_offset(&f12->control_reg_desc, 8); + + if (item->reg_size > 14) { + dev_err(&fn->dev, "F12 control8 should be 14 bytes, not: %ld\n", + item->reg_size); + return -ENODEV; + } + + ret = rmi_read_block(rmi_dev, fn->fd.control_base_addr + offset, buf, + item->reg_size); + if (ret) + return ret; + + offset = 0; + if (rmi_register_desc_has_subpacket(item, 0)) { + sensor->max_x = (buf[offset + 1] << 8) | buf[offset]; + sensor->max_y = (buf[offset + 3] << 8) | buf[offset + 2]; + offset += 4; + } + + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: max_x: %d max_y: %d\n", __func__, + sensor->max_x, sensor->max_y); + + if (rmi_register_desc_has_subpacket(item, 1)) { + pitch_x = (buf[offset + 1] << 8) | buf[offset]; + pitch_y = (buf[offset + 3] << 8) | buf[offset + 2]; + offset += 4; + } + + if (rmi_register_desc_has_subpacket(item, 2)) { + sensor->axis_align.clip_x_low = buf[offset]; + sensor->axis_align.clip_x_high = sensor->max_x + - buf[offset + 1]; + sensor->axis_align.clip_y_low = buf[offset + 2]; + sensor->axis_align.clip_y_high = sensor->max_y + - buf[offset + 3]; + offset += 4; + } + + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: x low: %d x high: %d y low: %d y high: %d\n", + __func__, clip_x_low, clip_x_high, clip_y_low, clip_y_high); + + if (rmi_register_desc_has_subpacket(item, 3)) { + rx_receivers = buf[offset]; + tx_receivers = buf[offset + 1]; + offset += 2; + } + + if (rmi_register_desc_has_subpacket(item, 4)) { + sensor_flags = buf[offset]; + offset += 1; + } + + sensor->x_mm = (pitch_x * rx_receivers) >> 12; + sensor->y_mm = (pitch_y * tx_receivers) >> 12; + + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: x_mm: %d y_mm: %d\n", __func__, + sensor->x_mm, sensor->y_mm); + + return 0; +} + +static void rmi_f12_process_objects(struct f12_data *f12, u8 *data1) +{ + int i; + struct rmi_2d_sensor *sensor = &f12->sensor; + + for (i = 0; i < f12->data1->num_subpackets; i++) { + struct rmi_2d_sensor_abs_object *obj = &sensor->objs[i]; + + obj->type = RMI_2D_OBJECT_NONE; + obj->mt_tool = MT_TOOL_FINGER; + + switch (data1[0]) { + case RMI_F12_OBJECT_FINGER: + obj->type = RMI_2D_OBJECT_FINGER; + break; + case RMI_F12_OBJECT_STYLUS: + obj->type = RMI_2D_OBJECT_STYLUS; + obj->mt_tool = MT_TOOL_PEN; + break; + case RMI_F12_OBJECT_PALM: + obj->type = RMI_2D_OBJECT_PALM; + obj->mt_tool = MT_TOOL_PALM; + break; + case RMI_F12_OBJECT_UNCLASSIFIED: + obj->type = RMI_2D_OBJECT_UNCLASSIFIED; + break; + } + + obj->x = (data1[2] << 8) | data1[1]; + obj->y = (data1[4] << 8) | data1[3]; + obj->z = data1[5]; + obj->wx = data1[6]; + obj->wy = data1[7]; + + rmi_2d_sensor_abs_process(sensor, obj, i); + + data1 += 8; + } + + if (sensor->kernel_tracking) + input_mt_assign_slots(sensor->input, + sensor->tracking_slots, + sensor->tracking_pos, + sensor->nbr_fingers, + sensor->dmax); + + for (i = 0; i < sensor->nbr_fingers; i++) + rmi_2d_sensor_abs_report(sensor, &sensor->objs[i], i); +} + +static int rmi_f12_attention(struct rmi_function *fn, + unsigned long *irq_nr_regs) +{ + int retval; + struct rmi_device *rmi_dev = fn->rmi_dev; + struct f12_data *f12 = dev_get_drvdata(&fn->dev); + struct rmi_2d_sensor *sensor = &f12->sensor; + + if (rmi_dev->xport->attn_data) { + memcpy(sensor->data_pkt, rmi_dev->xport->attn_data, + sensor->attn_size); + rmi_dev->xport->attn_data += sensor->attn_size; + rmi_dev->xport->attn_size -= sensor->attn_size; + } else { + retval = rmi_read_block(rmi_dev, f12->data_addr, + sensor->data_pkt, sensor->pkt_size); + if (retval < 0) { + dev_err(&fn->dev, "Failed to read object data. Code: %d.\n", + retval); + return retval; + } + } + + if (f12->data1) + rmi_f12_process_objects(f12, + &sensor->data_pkt[f12->data1_offset]); + + input_mt_sync_frame(sensor->input); + + return 0; +} + +static int rmi_f12_config(struct rmi_function *fn) +{ + struct rmi_driver *drv = fn->rmi_dev->driver; + + drv->set_irq_bits(fn->rmi_dev, fn->irq_mask); + + return 0; +} + +static int rmi_f12_probe(struct rmi_function *fn) +{ + struct f12_data *f12; + int ret; + struct rmi_device *rmi_dev = fn->rmi_dev; + char buf; + u16 query_addr = fn->fd.query_base_addr; + const struct rmi_register_desc_item *item; + struct rmi_2d_sensor *sensor; + struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev); + struct rmi_transport_dev *xport = rmi_dev->xport; + u16 data_offset = 0; + + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s\n", __func__); + + ret = rmi_read(fn->rmi_dev, query_addr, &buf); + if (ret < 0) { + dev_err(&fn->dev, "Failed to read general info register: %d\n", + ret); + return -ENODEV; + } + ++query_addr; + + if (!(buf & 0x1)) { + dev_err(&fn->dev, + "Behavior of F12 without register descriptors is undefined.\n"); + return -ENODEV; + } + + f12 = devm_kzalloc(&fn->dev, sizeof(struct f12_data), GFP_KERNEL); + if (!f12) + return -ENOMEM; + + if (fn->dev.of_node) { + ret = rmi_2d_sensor_of_probe(&fn->dev, &f12->sensor_pdata); + if (ret) + return ret; + } else if (pdata->sensor_pdata) { + f12->sensor_pdata = *pdata->sensor_pdata; + } + + ret = rmi_read_register_desc(rmi_dev, query_addr, + &f12->query_reg_desc); + if (ret) { + dev_err(&fn->dev, + "Failed to read the Query Register Descriptor: %d\n", + ret); + return ret; + } + query_addr += 3; + + ret = rmi_read_register_desc(rmi_dev, query_addr, + &f12->control_reg_desc); + if (ret) { + dev_err(&fn->dev, + "Failed to read the Control Register Descriptor: %d\n", + ret); + return ret; + } + query_addr += 3; + + ret = rmi_read_register_desc(rmi_dev, query_addr, + &f12->data_reg_desc); + if (ret) { + dev_err(&fn->dev, + "Failed to read the Data Register Descriptor: %d\n", + ret); + return ret; + } + query_addr += 3; + + sensor = &f12->sensor; + sensor->fn = fn; + f12->data_addr = fn->fd.data_base_addr; + sensor->pkt_size = rmi_register_desc_calc_size(&f12->data_reg_desc); + + sensor->axis_align = + f12->sensor_pdata.axis_align; + + sensor->x_mm = f12->sensor_pdata.x_mm; + sensor->y_mm = f12->sensor_pdata.y_mm; + + if (sensor->sensor_type == rmi_sensor_default) + sensor->sensor_type = + f12->sensor_pdata.sensor_type; + + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: data packet size: %d\n", __func__, + sensor->pkt_size); + sensor->data_pkt = devm_kzalloc(&fn->dev, sensor->pkt_size, GFP_KERNEL); + if (!sensor->data_pkt) + return -ENOMEM; + + dev_set_drvdata(&fn->dev, f12); + + ret = rmi_f12_read_sensor_tuning(f12); + if (ret) + return ret; + + /* + * Figure out what data is contained in the data registers. HID devices + * may have registers defined, but their data is not reported in the + * HID attention report. Registers which are not reported in the HID + * attention report check to see if the device is receiving data from + * HID attention reports. + */ + item = rmi_get_register_desc_item(&f12->data_reg_desc, 0); + if (item && !xport->attn_data) + data_offset += item->reg_size; + + item = rmi_get_register_desc_item(&f12->data_reg_desc, 1); + if (item) { + f12->data1 = item; + f12->data1_offset = data_offset; + data_offset += item->reg_size; + sensor->nbr_fingers = item->num_subpackets; + sensor->report_abs = 1; + sensor->attn_size += item->reg_size; + } + + item = rmi_get_register_desc_item(&f12->data_reg_desc, 2); + if (item && !xport->attn_data) + data_offset += item->reg_size; + + item = rmi_get_register_desc_item(&f12->data_reg_desc, 3); + if (item && !xport->attn_data) + data_offset += item->reg_size; + + item = rmi_get_register_desc_item(&f12->data_reg_desc, 4); + if (item && !xport->attn_data) + data_offset += item->reg_size; + + item = rmi_get_register_desc_item(&f12->data_reg_desc, 5); + if (item) { + f12->data5 = item; + f12->data5_offset = data_offset; + data_offset += item->reg_size; + sensor->attn_size += item->reg_size; + } + + item = rmi_get_register_desc_item(&f12->data_reg_desc, 6); + if (item && !xport->attn_data) { + f12->data6 = item; + f12->data6_offset = data_offset; + data_offset += item->reg_size; + } + + item = rmi_get_register_desc_item(&f12->data_reg_desc, 7); + if (item && !xport->attn_data) + data_offset += item->reg_size; + + item = rmi_get_register_desc_item(&f12->data_reg_desc, 8); + if (item && !xport->attn_data) + data_offset += item->reg_size; + + item = rmi_get_register_desc_item(&f12->data_reg_desc, 9); + if (item && !xport->attn_data) { + f12->data9 = item; + f12->data9_offset = data_offset; + data_offset += item->reg_size; + if (!sensor->report_abs) + sensor->report_rel = 1; + } + + item = rmi_get_register_desc_item(&f12->data_reg_desc, 10); + if (item && !xport->attn_data) + data_offset += item->reg_size; + + item = rmi_get_register_desc_item(&f12->data_reg_desc, 11); + if (item && !xport->attn_data) + data_offset += item->reg_size; + + item = rmi_get_register_desc_item(&f12->data_reg_desc, 12); + if (item && !xport->attn_data) + data_offset += item->reg_size; + + item = rmi_get_register_desc_item(&f12->data_reg_desc, 13); + if (item && !xport->attn_data) + data_offset += item->reg_size; + + item = rmi_get_register_desc_item(&f12->data_reg_desc, 14); + if (item && !xport->attn_data) + data_offset += item->reg_size; + + item = rmi_get_register_desc_item(&f12->data_reg_desc, 15); + if (item && !xport->attn_data) { + f12->data15 = item; + f12->data15_offset = data_offset; + data_offset += item->reg_size; + } + + /* allocate the in-kernel tracking buffers */ + sensor->tracking_pos = devm_kzalloc(&fn->dev, + sizeof(struct input_mt_pos) * sensor->nbr_fingers, + GFP_KERNEL); + sensor->tracking_slots = devm_kzalloc(&fn->dev, + sizeof(int) * sensor->nbr_fingers, GFP_KERNEL); + sensor->objs = devm_kzalloc(&fn->dev, + sizeof(struct rmi_2d_sensor_abs_object) + * sensor->nbr_fingers, GFP_KERNEL); + if (!sensor->tracking_pos || !sensor->tracking_slots || !sensor->objs) + return -ENOMEM; + + ret = rmi_2d_sensor_configure_input(fn, sensor); + if (ret) + return ret; + + return 0; +} + +struct rmi_function_handler rmi_f12_handler = { + .driver = { + .name = "rmi4_f12", + }, + .func = 0x12, + .probe = rmi_f12_probe, + .config = rmi_f12_config, + .attention = rmi_f12_attention, +}; diff --git a/drivers/input/rmi4/rmi_f30.c b/drivers/input/rmi4/rmi_f30.c new file mode 100644 index 000000000..760aff1bc --- /dev/null +++ b/drivers/input/rmi4/rmi_f30.c @@ -0,0 +1,407 @@ +/* + * Copyright (c) 2012-2016 Synaptics Incorporated + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/rmi.h> +#include <linux/input.h> +#include <linux/slab.h> +#include "rmi_driver.h" + +#define RMI_F30_QUERY_SIZE 2 + +/* Defs for Query 0 */ +#define RMI_F30_EXTENDED_PATTERNS 0x01 +#define RMI_F30_HAS_MAPPABLE_BUTTONS (1 << 1) +#define RMI_F30_HAS_LED (1 << 2) +#define RMI_F30_HAS_GPIO (1 << 3) +#define RMI_F30_HAS_HAPTIC (1 << 4) +#define RMI_F30_HAS_GPIO_DRV_CTL (1 << 5) +#define RMI_F30_HAS_MECH_MOUSE_BTNS (1 << 6) + +/* Defs for Query 1 */ +#define RMI_F30_GPIO_LED_COUNT 0x1F + +/* Defs for Control Registers */ +#define RMI_F30_CTRL_1_GPIO_DEBOUNCE 0x01 +#define RMI_F30_CTRL_1_HALT (1 << 4) +#define RMI_F30_CTRL_1_HALTED (1 << 5) +#define RMI_F30_CTRL_10_NUM_MECH_MOUSE_BTNS 0x03 + +struct rmi_f30_ctrl_data { + int address; + int length; + u8 *regs; +}; + +#define RMI_F30_CTRL_MAX_REGS 32 +#define RMI_F30_CTRL_MAX_BYTES ((RMI_F30_CTRL_MAX_REGS + 7) >> 3) +#define RMI_F30_CTRL_MAX_REG_BLOCKS 11 + +#define RMI_F30_CTRL_REGS_MAX_SIZE (RMI_F30_CTRL_MAX_BYTES \ + + 1 \ + + RMI_F30_CTRL_MAX_BYTES \ + + RMI_F30_CTRL_MAX_BYTES \ + + RMI_F30_CTRL_MAX_BYTES \ + + 6 \ + + RMI_F30_CTRL_MAX_REGS \ + + RMI_F30_CTRL_MAX_REGS \ + + RMI_F30_CTRL_MAX_BYTES \ + + 1 \ + + 1) + +struct f30_data { + /* Query Data */ + bool has_extended_pattern; + bool has_mappable_buttons; + bool has_led; + bool has_gpio; + bool has_haptic; + bool has_gpio_driver_control; + bool has_mech_mouse_btns; + u8 gpioled_count; + + u8 register_count; + + /* Control Register Data */ + struct rmi_f30_ctrl_data ctrl[RMI_F30_CTRL_MAX_REG_BLOCKS]; + u8 ctrl_regs[RMI_F30_CTRL_REGS_MAX_SIZE]; + u32 ctrl_regs_size; + + u8 data_regs[RMI_F30_CTRL_MAX_BYTES]; + u16 *gpioled_key_map; + + struct input_dev *input; +}; + +static int rmi_f30_read_control_parameters(struct rmi_function *fn, + struct f30_data *f30) +{ + struct rmi_device *rmi_dev = fn->rmi_dev; + int error = 0; + + error = rmi_read_block(rmi_dev, fn->fd.control_base_addr, + f30->ctrl_regs, f30->ctrl_regs_size); + if (error) { + dev_err(&rmi_dev->dev, "%s : Could not read control registers at 0x%x error (%d)\n", + __func__, fn->fd.control_base_addr, error); + return error; + } + + return 0; +} + +static int rmi_f30_attention(struct rmi_function *fn, unsigned long *irq_bits) +{ + struct f30_data *f30 = dev_get_drvdata(&fn->dev); + struct rmi_device *rmi_dev = fn->rmi_dev; + int retval; + int gpiled = 0; + int value = 0; + int i; + int reg_num; + + if (!f30->input) + return 0; + + /* Read the gpi led data. */ + if (rmi_dev->xport->attn_data) { + memcpy(f30->data_regs, rmi_dev->xport->attn_data, + f30->register_count); + rmi_dev->xport->attn_data += f30->register_count; + rmi_dev->xport->attn_size -= f30->register_count; + } else { + retval = rmi_read_block(rmi_dev, fn->fd.data_base_addr, + f30->data_regs, f30->register_count); + + if (retval) { + dev_err(&fn->dev, "%s: Failed to read F30 data registers.\n", + __func__); + return retval; + } + } + + for (reg_num = 0; reg_num < f30->register_count; ++reg_num) { + for (i = 0; gpiled < f30->gpioled_count && i < 8; ++i, + ++gpiled) { + if (f30->gpioled_key_map[gpiled] != 0) { + /* buttons have pull up resistors */ + value = (((f30->data_regs[reg_num] >> i) & 0x01) + == 0); + + rmi_dbg(RMI_DEBUG_FN, &fn->dev, + "%s: call input report key (0x%04x) value (0x%02x)", + __func__, + f30->gpioled_key_map[gpiled], value); + input_report_key(f30->input, + f30->gpioled_key_map[gpiled], + value); + } + + } + } + + return 0; +} + +static int rmi_f30_register_device(struct rmi_function *fn) +{ + int i; + struct rmi_device *rmi_dev = fn->rmi_dev; + struct rmi_driver_data *drv_data = dev_get_drvdata(&rmi_dev->dev); + struct f30_data *f30 = dev_get_drvdata(&fn->dev); + struct input_dev *input_dev; + int button_count = 0; + + input_dev = drv_data->input; + if (!input_dev) { + dev_info(&fn->dev, "F30: no input device found, ignoring.\n"); + return -EINVAL; + } + + f30->input = input_dev; + + set_bit(EV_KEY, input_dev->evbit); + + input_dev->keycode = f30->gpioled_key_map; + input_dev->keycodesize = sizeof(u16); + input_dev->keycodemax = f30->gpioled_count; + + for (i = 0; i < f30->gpioled_count; i++) { + if (f30->gpioled_key_map[i] != 0) { + input_set_capability(input_dev, EV_KEY, + f30->gpioled_key_map[i]); + button_count++; + } + } + + if (button_count == 1) + __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit); + return 0; +} + +static int rmi_f30_config(struct rmi_function *fn) +{ + struct f30_data *f30 = dev_get_drvdata(&fn->dev); + struct rmi_driver *drv = fn->rmi_dev->driver; + const struct rmi_device_platform_data *pdata = + rmi_get_platform_data(fn->rmi_dev); + int error; + + if (pdata->f30_data && pdata->f30_data->disable) { + drv->clear_irq_bits(fn->rmi_dev, fn->irq_mask); + } else { + /* Write Control Register values back to device */ + error = rmi_write_block(fn->rmi_dev, fn->fd.control_base_addr, + f30->ctrl_regs, f30->ctrl_regs_size); + if (error) { + dev_err(&fn->rmi_dev->dev, + "%s : Could not write control registers at 0x%x error (%d)\n", + __func__, fn->fd.control_base_addr, error); + return error; + } + + drv->set_irq_bits(fn->rmi_dev, fn->irq_mask); + } + return 0; +} + +static inline void rmi_f30_set_ctrl_data(struct rmi_f30_ctrl_data *ctrl, + int *ctrl_addr, int len, u8 **reg) +{ + ctrl->address = *ctrl_addr; + ctrl->length = len; + ctrl->regs = *reg; + *ctrl_addr += len; + *reg += len; +} + +static inline bool rmi_f30_is_valid_button(int button, + struct rmi_f30_ctrl_data *ctrl) +{ + int byte_position = button >> 3; + int bit_position = button & 0x07; + + /* + * ctrl2 -> dir == 0 -> input mode + * ctrl3 -> data == 1 -> actual button + */ + return !(ctrl[2].regs[byte_position] & BIT(bit_position)) && + (ctrl[3].regs[byte_position] & BIT(bit_position)); +} + +static inline int rmi_f30_initialize(struct rmi_function *fn) +{ + struct f30_data *f30; + struct rmi_device *rmi_dev = fn->rmi_dev; + const struct rmi_device_platform_data *pdata; + int retval = 0; + int control_address; + int i; + int button; + u8 buf[RMI_F30_QUERY_SIZE]; + u8 *ctrl_reg; + u8 *map_memory; + + f30 = devm_kzalloc(&fn->dev, sizeof(struct f30_data), + GFP_KERNEL); + if (!f30) + return -ENOMEM; + + dev_set_drvdata(&fn->dev, f30); + + retval = rmi_read_block(fn->rmi_dev, fn->fd.query_base_addr, buf, + RMI_F30_QUERY_SIZE); + + if (retval) { + dev_err(&fn->dev, "Failed to read query register.\n"); + return retval; + } + + f30->has_extended_pattern = buf[0] & RMI_F30_EXTENDED_PATTERNS; + f30->has_mappable_buttons = buf[0] & RMI_F30_HAS_MAPPABLE_BUTTONS; + f30->has_led = buf[0] & RMI_F30_HAS_LED; + f30->has_gpio = buf[0] & RMI_F30_HAS_GPIO; + f30->has_haptic = buf[0] & RMI_F30_HAS_HAPTIC; + f30->has_gpio_driver_control = buf[0] & RMI_F30_HAS_GPIO_DRV_CTL; + f30->has_mech_mouse_btns = buf[0] & RMI_F30_HAS_MECH_MOUSE_BTNS; + f30->gpioled_count = buf[1] & RMI_F30_GPIO_LED_COUNT; + + f30->register_count = (f30->gpioled_count + 7) >> 3; + + control_address = fn->fd.control_base_addr; + ctrl_reg = f30->ctrl_regs; + + if (f30->has_gpio && f30->has_led) + rmi_f30_set_ctrl_data(&f30->ctrl[0], &control_address, + f30->register_count, &ctrl_reg); + + rmi_f30_set_ctrl_data(&f30->ctrl[1], &control_address, sizeof(u8), + &ctrl_reg); + + if (f30->has_gpio) { + rmi_f30_set_ctrl_data(&f30->ctrl[2], &control_address, + f30->register_count, &ctrl_reg); + + rmi_f30_set_ctrl_data(&f30->ctrl[3], &control_address, + f30->register_count, &ctrl_reg); + } + + if (f30->has_led) { + int ctrl5_len; + + rmi_f30_set_ctrl_data(&f30->ctrl[4], &control_address, + f30->register_count, &ctrl_reg); + + if (f30->has_extended_pattern) + ctrl5_len = 6; + else + ctrl5_len = 2; + + rmi_f30_set_ctrl_data(&f30->ctrl[5], &control_address, + ctrl5_len, &ctrl_reg); + } + + if (f30->has_led || f30->has_gpio_driver_control) { + /* control 6 uses a byte per gpio/led */ + rmi_f30_set_ctrl_data(&f30->ctrl[6], &control_address, + f30->gpioled_count, &ctrl_reg); + } + + if (f30->has_mappable_buttons) { + /* control 7 uses a byte per gpio/led */ + rmi_f30_set_ctrl_data(&f30->ctrl[7], &control_address, + f30->gpioled_count, &ctrl_reg); + } + + if (f30->has_haptic) { + rmi_f30_set_ctrl_data(&f30->ctrl[8], &control_address, + f30->register_count, &ctrl_reg); + + rmi_f30_set_ctrl_data(&f30->ctrl[9], &control_address, + sizeof(u8), &ctrl_reg); + } + + if (f30->has_mech_mouse_btns) + rmi_f30_set_ctrl_data(&f30->ctrl[10], &control_address, + sizeof(u8), &ctrl_reg); + + f30->ctrl_regs_size = ctrl_reg - f30->ctrl_regs + ?: RMI_F30_CTRL_REGS_MAX_SIZE; + + retval = rmi_f30_read_control_parameters(fn, f30); + if (retval < 0) { + dev_err(&fn->dev, + "Failed to initialize F19 control params.\n"); + return retval; + } + + map_memory = devm_kzalloc(&fn->dev, + (f30->gpioled_count * (sizeof(u16))), + GFP_KERNEL); + if (!map_memory) { + dev_err(&fn->dev, "Failed to allocate gpioled map memory.\n"); + return -ENOMEM; + } + + f30->gpioled_key_map = (u16 *)map_memory; + + pdata = rmi_get_platform_data(rmi_dev); + if (pdata && f30->has_gpio) { + button = BTN_LEFT; + for (i = 0; i < f30->gpioled_count; i++) { + if (rmi_f30_is_valid_button(i, f30->ctrl)) { + f30->gpioled_key_map[i] = button++; + + /* + * buttonpad might be given by + * f30->has_mech_mouse_btns, but I am + * not sure, so use only the pdata info + */ + if (pdata->f30_data && + pdata->f30_data->buttonpad) + break; + } + } + } + + return 0; +} + +static int rmi_f30_probe(struct rmi_function *fn) +{ + int rc; + const struct rmi_device_platform_data *pdata = + rmi_get_platform_data(fn->rmi_dev); + + if (pdata->f30_data && pdata->f30_data->disable) + return 0; + + rc = rmi_f30_initialize(fn); + if (rc < 0) + goto error_exit; + + rc = rmi_f30_register_device(fn); + if (rc < 0) + goto error_exit; + + return 0; + +error_exit: + return rc; + +} + +struct rmi_function_handler rmi_f30_handler = { + .driver = { + .name = "rmi4_f30", + }, + .func = 0x30, + .probe = rmi_f30_probe, + .config = rmi_f30_config, + .attention = rmi_f30_attention, +}; diff --git a/drivers/input/rmi4/rmi_i2c.c b/drivers/input/rmi4/rmi_i2c.c new file mode 100644 index 000000000..a96a326b5 --- /dev/null +++ b/drivers/input/rmi4/rmi_i2c.c @@ -0,0 +1,397 @@ +/* + * Copyright (c) 2011-2016 Synaptics Incorporated + * Copyright (c) 2011 Unixphere + * + * 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/i2c.h> +#include <linux/rmi.h> +#include <linux/irq.h> +#include <linux/of.h> +#include "rmi_driver.h" + +#define BUFFER_SIZE_INCREMENT 32 + +/** + * struct rmi_i2c_xport - stores information for i2c communication + * + * @xport: The transport interface structure + * + * @page_mutex: Locks current page to avoid changing pages in unexpected ways. + * @page: Keeps track of the current virtual page + * + * @tx_buf: Buffer used for transmitting data to the sensor over i2c. + * @tx_buf_size: Size of the buffer + */ +struct rmi_i2c_xport { + struct rmi_transport_dev xport; + struct i2c_client *client; + + struct mutex page_mutex; + int page; + + int irq; + + u8 *tx_buf; + size_t tx_buf_size; +}; + +#define RMI_PAGE_SELECT_REGISTER 0xff +#define RMI_I2C_PAGE(addr) (((addr) >> 8) & 0xff) + +/* + * rmi_set_page - Set RMI page + * @xport: The pointer to the rmi_transport_dev struct + * @page: The new page address. + * + * RMI devices have 16-bit addressing, but some of the transport + * implementations (like SMBus) only have 8-bit addressing. So RMI implements + * a page address at 0xff of every page so we can reliable page addresses + * every 256 registers. + * + * The page_mutex lock must be held when this function is entered. + * + * Returns zero on success, non-zero on failure. + */ +static int rmi_set_page(struct rmi_i2c_xport *rmi_i2c, u8 page) +{ + struct i2c_client *client = rmi_i2c->client; + u8 txbuf[2] = {RMI_PAGE_SELECT_REGISTER, page}; + int retval; + + retval = i2c_master_send(client, txbuf, sizeof(txbuf)); + if (retval != sizeof(txbuf)) { + dev_err(&client->dev, + "%s: set page failed: %d.", __func__, retval); + return (retval < 0) ? retval : -EIO; + } + + rmi_i2c->page = page; + return 0; +} + +static int rmi_i2c_write_block(struct rmi_transport_dev *xport, u16 addr, + const void *buf, size_t len) +{ + struct rmi_i2c_xport *rmi_i2c = + container_of(xport, struct rmi_i2c_xport, xport); + struct i2c_client *client = rmi_i2c->client; + size_t tx_size = len + 1; + int retval; + + mutex_lock(&rmi_i2c->page_mutex); + + if (!rmi_i2c->tx_buf || rmi_i2c->tx_buf_size < tx_size) { + if (rmi_i2c->tx_buf) + devm_kfree(&client->dev, rmi_i2c->tx_buf); + rmi_i2c->tx_buf_size = tx_size + BUFFER_SIZE_INCREMENT; + rmi_i2c->tx_buf = devm_kzalloc(&client->dev, + rmi_i2c->tx_buf_size, + GFP_KERNEL); + if (!rmi_i2c->tx_buf) { + rmi_i2c->tx_buf_size = 0; + retval = -ENOMEM; + goto exit; + } + } + + rmi_i2c->tx_buf[0] = addr & 0xff; + memcpy(rmi_i2c->tx_buf + 1, buf, len); + + if (RMI_I2C_PAGE(addr) != rmi_i2c->page) { + retval = rmi_set_page(rmi_i2c, RMI_I2C_PAGE(addr)); + if (retval) + goto exit; + } + + retval = i2c_master_send(client, rmi_i2c->tx_buf, tx_size); + if (retval == tx_size) + retval = 0; + else if (retval >= 0) + retval = -EIO; + +exit: + rmi_dbg(RMI_DEBUG_XPORT, &client->dev, + "write %zd bytes at %#06x: %d (%*ph)\n", + len, addr, retval, (int)len, buf); + + mutex_unlock(&rmi_i2c->page_mutex); + return retval; +} + +static int rmi_i2c_read_block(struct rmi_transport_dev *xport, u16 addr, + void *buf, size_t len) +{ + struct rmi_i2c_xport *rmi_i2c = + container_of(xport, struct rmi_i2c_xport, xport); + struct i2c_client *client = rmi_i2c->client; + u8 addr_offset = addr & 0xff; + int retval; + struct i2c_msg msgs[] = { + { + .addr = client->addr, + .len = sizeof(addr_offset), + .buf = &addr_offset, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = len, + .buf = buf, + }, + }; + + mutex_lock(&rmi_i2c->page_mutex); + + if (RMI_I2C_PAGE(addr) != rmi_i2c->page) { + retval = rmi_set_page(rmi_i2c, RMI_I2C_PAGE(addr)); + if (retval) + goto exit; + } + + retval = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)); + if (retval == ARRAY_SIZE(msgs)) + retval = 0; /* success */ + else if (retval >= 0) + retval = -EIO; + +exit: + rmi_dbg(RMI_DEBUG_XPORT, &client->dev, + "read %zd bytes at %#06x: %d (%*ph)\n", + len, addr, retval, (int)len, buf); + + mutex_unlock(&rmi_i2c->page_mutex); + return retval; +} + +static const struct rmi_transport_ops rmi_i2c_ops = { + .write_block = rmi_i2c_write_block, + .read_block = rmi_i2c_read_block, +}; + +static irqreturn_t rmi_i2c_irq(int irq, void *dev_id) +{ + struct rmi_i2c_xport *rmi_i2c = dev_id; + struct rmi_device *rmi_dev = rmi_i2c->xport.rmi_dev; + int ret; + + ret = rmi_process_interrupt_requests(rmi_dev); + if (ret) + rmi_dbg(RMI_DEBUG_XPORT, &rmi_dev->dev, + "Failed to process interrupt request: %d\n", ret); + + return IRQ_HANDLED; +} + +static int rmi_i2c_init_irq(struct i2c_client *client) +{ + struct rmi_i2c_xport *rmi_i2c = i2c_get_clientdata(client); + int irq_flags = irqd_get_trigger_type(irq_get_irq_data(rmi_i2c->irq)); + int ret; + + if (!irq_flags) + irq_flags = IRQF_TRIGGER_LOW; + + ret = devm_request_threaded_irq(&client->dev, rmi_i2c->irq, NULL, + rmi_i2c_irq, irq_flags | IRQF_ONESHOT, client->name, + rmi_i2c); + if (ret < 0) { + dev_warn(&client->dev, "Failed to register interrupt %d\n", + rmi_i2c->irq); + + return ret; + } + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id rmi_i2c_of_match[] = { + { .compatible = "syna,rmi4-i2c" }, + {}, +}; +MODULE_DEVICE_TABLE(of, rmi_i2c_of_match); +#endif + +static int rmi_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct rmi_device_platform_data *pdata; + struct rmi_device_platform_data *client_pdata = + dev_get_platdata(&client->dev); + struct rmi_i2c_xport *rmi_i2c; + int retval; + + rmi_i2c = devm_kzalloc(&client->dev, sizeof(struct rmi_i2c_xport), + GFP_KERNEL); + if (!rmi_i2c) + return -ENOMEM; + + pdata = &rmi_i2c->xport.pdata; + + if (!client->dev.of_node && client_pdata) + *pdata = *client_pdata; + + if (client->irq > 0) + rmi_i2c->irq = client->irq; + + rmi_dbg(RMI_DEBUG_XPORT, &client->dev, "Probing %s.\n", + dev_name(&client->dev)); + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, + "adapter does not support required functionality.\n"); + return -ENODEV; + } + + rmi_i2c->client = client; + mutex_init(&rmi_i2c->page_mutex); + + rmi_i2c->xport.dev = &client->dev; + rmi_i2c->xport.proto_name = "i2c"; + rmi_i2c->xport.ops = &rmi_i2c_ops; + + i2c_set_clientdata(client, rmi_i2c); + + /* + * Setting the page to zero will (a) make sure the PSR is in a + * known state, and (b) make sure we can talk to the device. + */ + retval = rmi_set_page(rmi_i2c, 0); + if (retval) { + dev_err(&client->dev, "Failed to set page select to 0.\n"); + return retval; + } + + retval = rmi_register_transport_device(&rmi_i2c->xport); + if (retval) { + dev_err(&client->dev, "Failed to register transport driver at 0x%.2X.\n", + client->addr); + return retval; + } + + retval = rmi_i2c_init_irq(client); + if (retval < 0) + return retval; + + dev_info(&client->dev, "registered rmi i2c driver at %#04x.\n", + client->addr); + return 0; +} + +static int rmi_i2c_remove(struct i2c_client *client) +{ + struct rmi_i2c_xport *rmi_i2c = i2c_get_clientdata(client); + + rmi_unregister_transport_device(&rmi_i2c->xport); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int rmi_i2c_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rmi_i2c_xport *rmi_i2c = i2c_get_clientdata(client); + int ret; + + ret = rmi_driver_suspend(rmi_i2c->xport.rmi_dev); + if (ret) + dev_warn(dev, "Failed to resume device: %d\n", ret); + + disable_irq(rmi_i2c->irq); + if (device_may_wakeup(&client->dev)) { + ret = enable_irq_wake(rmi_i2c->irq); + if (!ret) + dev_warn(dev, "Failed to enable irq for wake: %d\n", + ret); + } + return ret; +} + +static int rmi_i2c_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rmi_i2c_xport *rmi_i2c = i2c_get_clientdata(client); + int ret; + + enable_irq(rmi_i2c->irq); + if (device_may_wakeup(&client->dev)) { + ret = disable_irq_wake(rmi_i2c->irq); + if (!ret) + dev_warn(dev, "Failed to disable irq for wake: %d\n", + ret); + } + + ret = rmi_driver_resume(rmi_i2c->xport.rmi_dev); + if (ret) + dev_warn(dev, "Failed to resume device: %d\n", ret); + + return ret; +} +#endif + +#ifdef CONFIG_PM +static int rmi_i2c_runtime_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rmi_i2c_xport *rmi_i2c = i2c_get_clientdata(client); + int ret; + + ret = rmi_driver_suspend(rmi_i2c->xport.rmi_dev); + if (ret) + dev_warn(dev, "Failed to resume device: %d\n", ret); + + disable_irq(rmi_i2c->irq); + + return 0; +} + +static int rmi_i2c_runtime_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rmi_i2c_xport *rmi_i2c = i2c_get_clientdata(client); + int ret; + + enable_irq(rmi_i2c->irq); + + ret = rmi_driver_resume(rmi_i2c->xport.rmi_dev); + if (ret) + dev_warn(dev, "Failed to resume device: %d\n", ret); + + return 0; +} +#endif + +static const struct dev_pm_ops rmi_i2c_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rmi_i2c_suspend, rmi_i2c_resume) + SET_RUNTIME_PM_OPS(rmi_i2c_runtime_suspend, rmi_i2c_runtime_resume, + NULL) +}; + +static const struct i2c_device_id rmi_id[] = { + { "rmi4_i2c", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rmi_id); + +static struct i2c_driver rmi_i2c_driver = { + .driver = { + .name = "rmi4_i2c", + .pm = &rmi_i2c_pm, + .of_match_table = of_match_ptr(rmi_i2c_of_match), + }, + .id_table = rmi_id, + .probe = rmi_i2c_probe, + .remove = rmi_i2c_remove, +}; + +module_i2c_driver(rmi_i2c_driver); + +MODULE_AUTHOR("Christopher Heiny <cheiny@synaptics.com>"); +MODULE_AUTHOR("Andrew Duggan <aduggan@synaptics.com>"); +MODULE_DESCRIPTION("RMI I2C driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(RMI_DRIVER_VERSION); diff --git a/drivers/input/rmi4/rmi_spi.c b/drivers/input/rmi4/rmi_spi.c new file mode 100644 index 000000000..55bd1b349 --- /dev/null +++ b/drivers/input/rmi4/rmi_spi.c @@ -0,0 +1,589 @@ +/* + * Copyright (c) 2011-2016 Synaptics Incorporated + * Copyright (c) 2011 Unixphere + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/rmi.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> +#include <linux/irq.h> +#include <linux/of.h> +#include "rmi_driver.h" + +#define RMI_SPI_DEFAULT_XFER_BUF_SIZE 64 + +#define RMI_PAGE_SELECT_REGISTER 0x00FF +#define RMI_SPI_PAGE(addr) (((addr) >> 8) & 0x80) +#define RMI_SPI_XFER_SIZE_LIMIT 255 + +#define BUFFER_SIZE_INCREMENT 32 + +enum rmi_spi_op { + RMI_SPI_WRITE = 0, + RMI_SPI_READ, + RMI_SPI_V2_READ_UNIFIED, + RMI_SPI_V2_READ_SPLIT, + RMI_SPI_V2_WRITE, +}; + +struct rmi_spi_cmd { + enum rmi_spi_op op; + u16 addr; +}; + +struct rmi_spi_xport { + struct rmi_transport_dev xport; + struct spi_device *spi; + + struct mutex page_mutex; + int page; + + int irq; + + u8 *rx_buf; + u8 *tx_buf; + int xfer_buf_size; + + struct spi_transfer *rx_xfers; + struct spi_transfer *tx_xfers; + int rx_xfer_count; + int tx_xfer_count; +}; + +static int rmi_spi_manage_pools(struct rmi_spi_xport *rmi_spi, int len) +{ + struct spi_device *spi = rmi_spi->spi; + int buf_size = rmi_spi->xfer_buf_size + ? rmi_spi->xfer_buf_size : RMI_SPI_DEFAULT_XFER_BUF_SIZE; + struct spi_transfer *xfer_buf; + void *buf; + void *tmp; + + while (buf_size < len) + buf_size *= 2; + + if (buf_size > RMI_SPI_XFER_SIZE_LIMIT) + buf_size = RMI_SPI_XFER_SIZE_LIMIT; + + tmp = rmi_spi->rx_buf; + buf = devm_kzalloc(&spi->dev, buf_size * 2, + GFP_KERNEL | GFP_DMA); + if (!buf) + return -ENOMEM; + + rmi_spi->rx_buf = buf; + rmi_spi->tx_buf = &rmi_spi->rx_buf[buf_size]; + rmi_spi->xfer_buf_size = buf_size; + + if (tmp) + devm_kfree(&spi->dev, tmp); + + if (rmi_spi->xport.pdata.spi_data.read_delay_us) + rmi_spi->rx_xfer_count = buf_size; + else + rmi_spi->rx_xfer_count = 1; + + if (rmi_spi->xport.pdata.spi_data.write_delay_us) + rmi_spi->tx_xfer_count = buf_size; + else + rmi_spi->tx_xfer_count = 1; + + /* + * Allocate a pool of spi_transfer buffers for devices which need + * per byte delays. + */ + tmp = rmi_spi->rx_xfers; + xfer_buf = devm_kzalloc(&spi->dev, + (rmi_spi->rx_xfer_count + rmi_spi->tx_xfer_count) + * sizeof(struct spi_transfer), GFP_KERNEL); + if (!xfer_buf) + return -ENOMEM; + + rmi_spi->rx_xfers = xfer_buf; + rmi_spi->tx_xfers = &xfer_buf[rmi_spi->rx_xfer_count]; + + if (tmp) + devm_kfree(&spi->dev, tmp); + + return 0; +} + +static int rmi_spi_xfer(struct rmi_spi_xport *rmi_spi, + const struct rmi_spi_cmd *cmd, const u8 *tx_buf, + int tx_len, u8 *rx_buf, int rx_len) +{ + struct spi_device *spi = rmi_spi->spi; + struct rmi_device_platform_data_spi *spi_data = + &rmi_spi->xport.pdata.spi_data; + struct spi_message msg; + struct spi_transfer *xfer; + int ret = 0; + int len; + int cmd_len = 0; + int total_tx_len; + int i; + u16 addr = cmd->addr; + + spi_message_init(&msg); + + switch (cmd->op) { + case RMI_SPI_WRITE: + case RMI_SPI_READ: + cmd_len += 2; + break; + case RMI_SPI_V2_READ_UNIFIED: + case RMI_SPI_V2_READ_SPLIT: + case RMI_SPI_V2_WRITE: + cmd_len += 4; + break; + } + + total_tx_len = cmd_len + tx_len; + len = max(total_tx_len, rx_len); + + if (len > RMI_SPI_XFER_SIZE_LIMIT) + return -EINVAL; + + if (rmi_spi->xfer_buf_size < len) + rmi_spi_manage_pools(rmi_spi, len); + + if (addr == 0) + /* + * SPI needs an address. Use 0x7FF if we want to keep + * reading from the last position of the register pointer. + */ + addr = 0x7FF; + + switch (cmd->op) { + case RMI_SPI_WRITE: + rmi_spi->tx_buf[0] = (addr >> 8); + rmi_spi->tx_buf[1] = addr & 0xFF; + break; + case RMI_SPI_READ: + rmi_spi->tx_buf[0] = (addr >> 8) | 0x80; + rmi_spi->tx_buf[1] = addr & 0xFF; + break; + case RMI_SPI_V2_READ_UNIFIED: + break; + case RMI_SPI_V2_READ_SPLIT: + break; + case RMI_SPI_V2_WRITE: + rmi_spi->tx_buf[0] = 0x40; + rmi_spi->tx_buf[1] = (addr >> 8) & 0xFF; + rmi_spi->tx_buf[2] = addr & 0xFF; + rmi_spi->tx_buf[3] = tx_len; + break; + } + + if (tx_buf) + memcpy(&rmi_spi->tx_buf[cmd_len], tx_buf, tx_len); + + if (rmi_spi->tx_xfer_count > 1) { + for (i = 0; i < total_tx_len; i++) { + xfer = &rmi_spi->tx_xfers[i]; + memset(xfer, 0, sizeof(struct spi_transfer)); + xfer->tx_buf = &rmi_spi->tx_buf[i]; + xfer->len = 1; + xfer->delay_usecs = spi_data->write_delay_us; + spi_message_add_tail(xfer, &msg); + } + } else { + xfer = rmi_spi->tx_xfers; + memset(xfer, 0, sizeof(struct spi_transfer)); + xfer->tx_buf = rmi_spi->tx_buf; + xfer->len = total_tx_len; + spi_message_add_tail(xfer, &msg); + } + + rmi_dbg(RMI_DEBUG_XPORT, &spi->dev, "%s: cmd: %s tx_buf len: %d tx_buf: %*ph\n", + __func__, cmd->op == RMI_SPI_WRITE ? "WRITE" : "READ", + total_tx_len, total_tx_len, rmi_spi->tx_buf); + + if (rx_buf) { + if (rmi_spi->rx_xfer_count > 1) { + for (i = 0; i < rx_len; i++) { + xfer = &rmi_spi->rx_xfers[i]; + memset(xfer, 0, sizeof(struct spi_transfer)); + xfer->rx_buf = &rmi_spi->rx_buf[i]; + xfer->len = 1; + xfer->delay_usecs = spi_data->read_delay_us; + spi_message_add_tail(xfer, &msg); + } + } else { + xfer = rmi_spi->rx_xfers; + memset(xfer, 0, sizeof(struct spi_transfer)); + xfer->rx_buf = rmi_spi->rx_buf; + xfer->len = rx_len; + spi_message_add_tail(xfer, &msg); + } + } + + ret = spi_sync(spi, &msg); + if (ret < 0) { + dev_err(&spi->dev, "spi xfer failed: %d\n", ret); + return ret; + } + + if (rx_buf) { + memcpy(rx_buf, rmi_spi->rx_buf, rx_len); + rmi_dbg(RMI_DEBUG_XPORT, &spi->dev, "%s: (%d) %*ph\n", + __func__, rx_len, rx_len, rx_buf); + } + + return 0; +} + +/* + * rmi_set_page - Set RMI page + * @xport: The pointer to the rmi_transport_dev struct + * @page: The new page address. + * + * RMI devices have 16-bit addressing, but some of the transport + * implementations (like SMBus) only have 8-bit addressing. So RMI implements + * a page address at 0xff of every page so we can reliable page addresses + * every 256 registers. + * + * The page_mutex lock must be held when this function is entered. + * + * Returns zero on success, non-zero on failure. + */ +static int rmi_set_page(struct rmi_spi_xport *rmi_spi, u8 page) +{ + struct rmi_spi_cmd cmd; + int ret; + + cmd.op = RMI_SPI_WRITE; + cmd.addr = RMI_PAGE_SELECT_REGISTER; + + ret = rmi_spi_xfer(rmi_spi, &cmd, &page, 1, NULL, 0); + + if (ret) + rmi_spi->page = page; + + return ret; +} + +static int rmi_spi_write_block(struct rmi_transport_dev *xport, u16 addr, + const void *buf, size_t len) +{ + struct rmi_spi_xport *rmi_spi = + container_of(xport, struct rmi_spi_xport, xport); + struct rmi_spi_cmd cmd; + int ret; + + mutex_lock(&rmi_spi->page_mutex); + + if (RMI_SPI_PAGE(addr) != rmi_spi->page) { + ret = rmi_set_page(rmi_spi, RMI_SPI_PAGE(addr)); + if (ret) + goto exit; + } + + cmd.op = RMI_SPI_WRITE; + cmd.addr = addr; + + ret = rmi_spi_xfer(rmi_spi, &cmd, buf, len, NULL, 0); + +exit: + mutex_unlock(&rmi_spi->page_mutex); + return ret; +} + +static int rmi_spi_read_block(struct rmi_transport_dev *xport, u16 addr, + void *buf, size_t len) +{ + struct rmi_spi_xport *rmi_spi = + container_of(xport, struct rmi_spi_xport, xport); + struct rmi_spi_cmd cmd; + int ret; + + mutex_lock(&rmi_spi->page_mutex); + + if (RMI_SPI_PAGE(addr) != rmi_spi->page) { + ret = rmi_set_page(rmi_spi, RMI_SPI_PAGE(addr)); + if (ret) + goto exit; + } + + cmd.op = RMI_SPI_READ; + cmd.addr = addr; + + ret = rmi_spi_xfer(rmi_spi, &cmd, NULL, 0, buf, len); + +exit: + mutex_unlock(&rmi_spi->page_mutex); + return ret; +} + +static const struct rmi_transport_ops rmi_spi_ops = { + .write_block = rmi_spi_write_block, + .read_block = rmi_spi_read_block, +}; + +static irqreturn_t rmi_spi_irq(int irq, void *dev_id) +{ + struct rmi_spi_xport *rmi_spi = dev_id; + struct rmi_device *rmi_dev = rmi_spi->xport.rmi_dev; + int ret; + + ret = rmi_process_interrupt_requests(rmi_dev); + if (ret) + rmi_dbg(RMI_DEBUG_XPORT, &rmi_dev->dev, + "Failed to process interrupt request: %d\n", ret); + + return IRQ_HANDLED; +} + +static int rmi_spi_init_irq(struct spi_device *spi) +{ + struct rmi_spi_xport *rmi_spi = spi_get_drvdata(spi); + int irq_flags = irqd_get_trigger_type(irq_get_irq_data(rmi_spi->irq)); + int ret; + + if (!irq_flags) + irq_flags = IRQF_TRIGGER_LOW; + + ret = devm_request_threaded_irq(&spi->dev, rmi_spi->irq, NULL, + rmi_spi_irq, irq_flags | IRQF_ONESHOT, + dev_name(&spi->dev), rmi_spi); + if (ret < 0) { + dev_warn(&spi->dev, "Failed to register interrupt %d\n", + rmi_spi->irq); + return ret; + } + + return 0; +} + +#ifdef CONFIG_OF +static int rmi_spi_of_probe(struct spi_device *spi, + struct rmi_device_platform_data *pdata) +{ + struct device *dev = &spi->dev; + int retval; + + retval = rmi_of_property_read_u32(dev, + &pdata->spi_data.read_delay_us, + "spi-rx-delay-us", 1); + if (retval) + return retval; + + retval = rmi_of_property_read_u32(dev, + &pdata->spi_data.write_delay_us, + "spi-tx-delay-us", 1); + if (retval) + return retval; + + return 0; +} + +static const struct of_device_id rmi_spi_of_match[] = { + { .compatible = "syna,rmi4-spi" }, + {}, +}; +MODULE_DEVICE_TABLE(of, rmi_spi_of_match); +#else +static inline int rmi_spi_of_probe(struct spi_device *spi, + struct rmi_device_platform_data *pdata) +{ + return -ENODEV; +} +#endif + +static int rmi_spi_probe(struct spi_device *spi) +{ + struct rmi_spi_xport *rmi_spi; + struct rmi_device_platform_data *pdata; + struct rmi_device_platform_data *spi_pdata = spi->dev.platform_data; + int retval; + + if (spi->master->flags & SPI_MASTER_HALF_DUPLEX) + return -EINVAL; + + rmi_spi = devm_kzalloc(&spi->dev, sizeof(struct rmi_spi_xport), + GFP_KERNEL); + if (!rmi_spi) + return -ENOMEM; + + pdata = &rmi_spi->xport.pdata; + + if (spi->dev.of_node) { + retval = rmi_spi_of_probe(spi, pdata); + if (retval) + return retval; + } else if (spi_pdata) { + *pdata = *spi_pdata; + } + + if (pdata->spi_data.bits_per_word) + spi->bits_per_word = pdata->spi_data.bits_per_word; + + if (pdata->spi_data.mode) + spi->mode = pdata->spi_data.mode; + + retval = spi_setup(spi); + if (retval < 0) { + dev_err(&spi->dev, "spi_setup failed!\n"); + return retval; + } + + if (spi->irq > 0) + rmi_spi->irq = spi->irq; + + rmi_spi->spi = spi; + mutex_init(&rmi_spi->page_mutex); + + rmi_spi->xport.dev = &spi->dev; + rmi_spi->xport.proto_name = "spi"; + rmi_spi->xport.ops = &rmi_spi_ops; + + spi_set_drvdata(spi, rmi_spi); + + retval = rmi_spi_manage_pools(rmi_spi, RMI_SPI_DEFAULT_XFER_BUF_SIZE); + if (retval) + return retval; + + /* + * Setting the page to zero will (a) make sure the PSR is in a + * known state, and (b) make sure we can talk to the device. + */ + retval = rmi_set_page(rmi_spi, 0); + if (retval) { + dev_err(&spi->dev, "Failed to set page select to 0.\n"); + return retval; + } + + retval = rmi_register_transport_device(&rmi_spi->xport); + if (retval) { + dev_err(&spi->dev, "failed to register transport.\n"); + return retval; + } + + retval = rmi_spi_init_irq(spi); + if (retval < 0) + return retval; + + dev_info(&spi->dev, "registered RMI SPI driver\n"); + return 0; +} + +static int rmi_spi_remove(struct spi_device *spi) +{ + struct rmi_spi_xport *rmi_spi = spi_get_drvdata(spi); + + rmi_unregister_transport_device(&rmi_spi->xport); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int rmi_spi_suspend(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct rmi_spi_xport *rmi_spi = spi_get_drvdata(spi); + int ret; + + ret = rmi_driver_suspend(rmi_spi->xport.rmi_dev); + if (ret) + dev_warn(dev, "Failed to resume device: %d\n", ret); + + disable_irq(rmi_spi->irq); + if (device_may_wakeup(&spi->dev)) { + ret = enable_irq_wake(rmi_spi->irq); + if (!ret) + dev_warn(dev, "Failed to enable irq for wake: %d\n", + ret); + } + return ret; +} + +static int rmi_spi_resume(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct rmi_spi_xport *rmi_spi = spi_get_drvdata(spi); + int ret; + + enable_irq(rmi_spi->irq); + if (device_may_wakeup(&spi->dev)) { + ret = disable_irq_wake(rmi_spi->irq); + if (!ret) + dev_warn(dev, "Failed to disable irq for wake: %d\n", + ret); + } + + ret = rmi_driver_resume(rmi_spi->xport.rmi_dev); + if (ret) + dev_warn(dev, "Failed to resume device: %d\n", ret); + + return ret; +} +#endif + +#ifdef CONFIG_PM +static int rmi_spi_runtime_suspend(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct rmi_spi_xport *rmi_spi = spi_get_drvdata(spi); + int ret; + + ret = rmi_driver_suspend(rmi_spi->xport.rmi_dev); + if (ret) + dev_warn(dev, "Failed to resume device: %d\n", ret); + + disable_irq(rmi_spi->irq); + + return 0; +} + +static int rmi_spi_runtime_resume(struct device *dev) +{ + struct spi_device *spi = to_spi_device(dev); + struct rmi_spi_xport *rmi_spi = spi_get_drvdata(spi); + int ret; + + enable_irq(rmi_spi->irq); + + ret = rmi_driver_resume(rmi_spi->xport.rmi_dev); + if (ret) + dev_warn(dev, "Failed to resume device: %d\n", ret); + + return 0; +} +#endif + +static const struct dev_pm_ops rmi_spi_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rmi_spi_suspend, rmi_spi_resume) + SET_RUNTIME_PM_OPS(rmi_spi_runtime_suspend, rmi_spi_runtime_resume, + NULL) +}; + +static const struct spi_device_id rmi_id[] = { + { "rmi4_spi", 0 }, + { } +}; +MODULE_DEVICE_TABLE(spi, rmi_id); + +static struct spi_driver rmi_spi_driver = { + .driver = { + .name = "rmi4_spi", + .pm = &rmi_spi_pm, + .of_match_table = of_match_ptr(rmi_spi_of_match), + }, + .id_table = rmi_id, + .probe = rmi_spi_probe, + .remove = rmi_spi_remove, +}; + +module_spi_driver(rmi_spi_driver); + +MODULE_AUTHOR("Christopher Heiny <cheiny@synaptics.com>"); +MODULE_AUTHOR("Andrew Duggan <aduggan@synaptics.com>"); +MODULE_DESCRIPTION("RMI SPI driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(RMI_DRIVER_VERSION); diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 66c62641b..8ecdc38fd 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -334,7 +334,7 @@ config TOUCHSCREEN_FUJITSU config TOUCHSCREEN_GOODIX tristate "Goodix I2C touchscreen" depends on I2C - depends on GPIOLIB + depends on GPIOLIB || COMPILE_TEST help Say Y here if you have the Goodix touchscreen (such as one installed in Onda v975w tablets) connected to your @@ -491,6 +491,17 @@ config TOUCHSCREEN_MMS114 To compile this driver as a module, choose M here: the module will be called mms114. +config TOUCHSCREEN_MELFAS_MIP4 + tristate "MELFAS MIP4 Touchscreen" + depends on I2C + help + Say Y here if you have a MELFAS MIP4 Touchscreen device. + + If unsure, say N. + + To compile this driver as a module, choose M here: + the module will be called melfas_mip4. + config TOUCHSCREEN_MTOUCH tristate "MicroTouch serial touchscreens" select SERIO @@ -822,6 +833,15 @@ config TOUCHSCREEN_USB_COMPOSITE To compile this driver as a module, choose M here: the module will be called usbtouchscreen. +config TOUCHSCREEN_MX25 + tristate "Freescale i.MX25 touchscreen input driver" + depends on MFD_MX25_TSADC + help + Enable support for touchscreen connected to your i.MX25. + + To compile this driver as a module, choose M here: the + module will be called fsl-imx25-tcq. + config TOUCHSCREEN_MC13783 tristate "Freescale MC13783 touchscreen input driver" depends on MFD_MC13XXX @@ -941,6 +961,7 @@ config TOUCHSCREEN_TOUCHIT213 config TOUCHSCREEN_TS4800 tristate "TS-4800 touchscreen" depends on HAS_IOMEM && OF + depends on SOC_IMX51 || COMPILE_TEST select MFD_SYSCON select INPUT_POLLDEV help @@ -1112,7 +1133,8 @@ config TOUCHSCREEN_ZFORCE config TOUCHSCREEN_COLIBRI_VF50 tristate "Toradex Colibri on board touchscreen driver" - depends on GPIOLIB && IIO && VF610_ADC + depends on IIO && VF610_ADC + depends on GPIOLIB || COMPILE_TEST help Say Y here if you have a Colibri VF50 and plan to use the on-board provided 4-wire touchscreen driver. diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 968ff12e3..f42975e71 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -46,8 +46,10 @@ obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o obj-$(CONFIG_TOUCHSCREEN_IPROC) += bcm_iproc_tsc.o obj-$(CONFIG_TOUCHSCREEN_LPC32XX) += lpc32xx_ts.o obj-$(CONFIG_TOUCHSCREEN_MAX11801) += max11801_ts.o +obj-$(CONFIG_TOUCHSCREEN_MX25) += fsl-imx25-tcq.o obj-$(CONFIG_TOUCHSCREEN_MC13783) += mc13783_ts.o obj-$(CONFIG_TOUCHSCREEN_MCS5000) += mcs5000_ts.o +obj-$(CONFIG_TOUCHSCREEN_MELFAS_MIP4) += melfas_mip4.o obj-$(CONFIG_TOUCHSCREEN_MIGOR) += migor_ts.o obj-$(CONFIG_TOUCHSCREEN_MMS114) += mms114.o obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o diff --git a/drivers/input/touchscreen/ad7879-i2c.c b/drivers/input/touchscreen/ad7879-i2c.c index d66962c5b..58f72e024 100644 --- a/drivers/input/touchscreen/ad7879-i2c.c +++ b/drivers/input/touchscreen/ad7879-i2c.c @@ -10,6 +10,7 @@ #include <linux/i2c.h> #include <linux/module.h> #include <linux/types.h> +#include <linux/of.h> #include <linux/pm.h> #include "ad7879.h" @@ -91,10 +92,19 @@ static const struct i2c_device_id ad7879_id[] = { }; MODULE_DEVICE_TABLE(i2c, ad7879_id); +#ifdef CONFIG_OF +static const struct of_device_id ad7879_i2c_dt_ids[] = { + { .compatible = "adi,ad7879-1", }, + { } +}; +MODULE_DEVICE_TABLE(of, ad7879_i2c_dt_ids); +#endif + static struct i2c_driver ad7879_i2c_driver = { .driver = { .name = "ad7879", .pm = &ad7879_pm_ops, + .of_match_table = of_match_ptr(ad7879_i2c_dt_ids), }, .probe = ad7879_i2c_probe, .remove = ad7879_i2c_remove, diff --git a/drivers/input/touchscreen/ad7879-spi.c b/drivers/input/touchscreen/ad7879-spi.c index 48033c268..d42b6b9af 100644 --- a/drivers/input/touchscreen/ad7879-spi.c +++ b/drivers/input/touchscreen/ad7879-spi.c @@ -10,6 +10,7 @@ #include <linux/pm.h> #include <linux/spi/spi.h> #include <linux/module.h> +#include <linux/of.h> #include "ad7879.h" @@ -146,10 +147,19 @@ static int ad7879_spi_remove(struct spi_device *spi) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id ad7879_spi_dt_ids[] = { + { .compatible = "adi,ad7879", }, + { } +}; +MODULE_DEVICE_TABLE(of, ad7879_spi_dt_ids); +#endif + static struct spi_driver ad7879_spi_driver = { .driver = { .name = "ad7879", .pm = &ad7879_pm_ops, + .of_match_table = of_match_ptr(ad7879_spi_dt_ids), }, .probe = ad7879_spi_probe, .remove = ad7879_spi_remove, diff --git a/drivers/input/touchscreen/ad7879.c b/drivers/input/touchscreen/ad7879.c index 16b5cc219..69d299d5d 100644 --- a/drivers/input/touchscreen/ad7879.c +++ b/drivers/input/touchscreen/ad7879.c @@ -31,7 +31,8 @@ #include <linux/i2c.h> #include <linux/gpio.h> -#include <linux/spi/ad7879.h> +#include <linux/input/touchscreen.h> +#include <linux/platform_data/ad7879.h> #include <linux/module.h> #include "ad7879.h" @@ -94,8 +95,8 @@ #define AD7879_TEMP_BIT (1<<1) enum { - AD7879_SEQ_XPOS = 0, - AD7879_SEQ_YPOS = 1, + AD7879_SEQ_YPOS = 0, + AD7879_SEQ_XPOS = 1, AD7879_SEQ_Z1 = 2, AD7879_SEQ_Z2 = 3, AD7879_NR_SENSE = 4, @@ -126,7 +127,6 @@ struct ad7879 { u8 pen_down_acc_interval; u8 median; u16 x_plate_ohms; - u16 pressure_max; u16 cmd_crtl1; u16 cmd_crtl2; u16 cmd_crtl3; @@ -170,10 +170,10 @@ static int ad7879_report(struct ad7879 *ts) * filter. The combination of these two techniques provides a robust * solution, discarding the spurious noise in the signal and keeping * only the data of interest. The size of both filters is - * programmable. (dev.platform_data, see linux/spi/ad7879.h) Other - * user-programmable conversion controls include variable acquisition - * time, and first conversion delay. Up to 16 averages can be taken - * per conversion. + * programmable. (dev.platform_data, see linux/platform_data/ad7879.h) + * Other user-programmable conversion controls include variable + * acquisition time, and first conversion delay. Up to 16 averages can + * be taken per conversion. */ if (likely(x && z1)) { @@ -186,7 +186,7 @@ static int ad7879_report(struct ad7879 *ts) * Sample found inconsistent, pressure is beyond * the maximum. Don't report it to user space. */ - if (Rt > ts->pressure_max) + if (Rt > input_abs_get_max(input_dev, ABS_PRESSURE)) return -EINVAL; /* @@ -469,7 +469,7 @@ static void ad7879_gpio_remove(struct ad7879 *ts) { const struct ad7879_platform_data *pdata = dev_get_platdata(ts->dev); - if (pdata->gpio_export) + if (pdata && pdata->gpio_export) gpiochip_remove(&ts->gc); } @@ -485,6 +485,32 @@ static inline void ad7879_gpio_remove(struct ad7879 *ts) } #endif +static int ad7879_parse_dt(struct device *dev, struct ad7879 *ts) +{ + int err; + u32 tmp; + + err = device_property_read_u32(dev, "adi,resistance-plate-x", &tmp); + if (err) { + dev_err(dev, "failed to get resistance-plate-x property\n"); + return err; + } + ts->x_plate_ohms = (u16)tmp; + + device_property_read_u8(dev, "adi,first-conversion-delay", + &ts->first_conversion_delay); + device_property_read_u8(dev, "adi,acquisition-time", + &ts->acquisition_time); + device_property_read_u8(dev, "adi,median-filter-size", &ts->median); + device_property_read_u8(dev, "adi,averaging", &ts->averaging); + device_property_read_u8(dev, "adi,conversion-interval", + &ts->pen_down_acc_interval); + + ts->swap_xy = device_property_read_bool(dev, "touchscreen-swapped-x-y"); + + return 0; +} + struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq, const struct ad7879_bus_ops *bops) { @@ -495,41 +521,44 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq, u16 revid; if (!irq) { - dev_err(dev, "no IRQ?\n"); - err = -EINVAL; - goto err_out; + dev_err(dev, "No IRQ specified\n"); + return ERR_PTR(-EINVAL); } - if (!pdata) { - dev_err(dev, "no platform data?\n"); - err = -EINVAL; - goto err_out; + ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL); + if (!ts) + return ERR_PTR(-ENOMEM); + + if (pdata) { + /* Platform data use swapped axis (backward compatibility) */ + ts->swap_xy = !pdata->swap_xy; + + ts->x_plate_ohms = pdata->x_plate_ohms ? : 400; + + ts->first_conversion_delay = pdata->first_conversion_delay; + ts->acquisition_time = pdata->acquisition_time; + ts->averaging = pdata->averaging; + ts->pen_down_acc_interval = pdata->pen_down_acc_interval; + ts->median = pdata->median; + } else if (dev->of_node) { + ad7879_parse_dt(dev, ts); + } else { + dev_err(dev, "No platform data\n"); + return ERR_PTR(-EINVAL); } - ts = kzalloc(sizeof(*ts), GFP_KERNEL); - input_dev = input_allocate_device(); - if (!ts || !input_dev) { - err = -ENOMEM; - goto err_free_mem; + input_dev = devm_input_allocate_device(dev); + if (!input_dev) { + dev_err(dev, "Failed to allocate input device\n"); + return ERR_PTR(-ENOMEM); } ts->bops = bops; ts->dev = dev; ts->input = input_dev; ts->irq = irq; - ts->swap_xy = pdata->swap_xy; setup_timer(&ts->timer, ad7879_timer, (unsigned long) ts); - - ts->x_plate_ohms = pdata->x_plate_ohms ? : 400; - ts->pressure_max = pdata->pressure_max ? : ~0; - - ts->first_conversion_delay = pdata->first_conversion_delay; - ts->acquisition_time = pdata->acquisition_time; - ts->averaging = pdata->averaging; - ts->pen_down_acc_interval = pdata->pen_down_acc_interval; - ts->median = pdata->median; - snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev)); input_dev->name = "AD7879 Touchscreen"; @@ -550,21 +579,33 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq, __set_bit(EV_KEY, input_dev->evbit); __set_bit(BTN_TOUCH, input_dev->keybit); - input_set_abs_params(input_dev, ABS_X, - pdata->x_min ? : 0, - pdata->x_max ? : MAX_12BIT, - 0, 0); - input_set_abs_params(input_dev, ABS_Y, - pdata->y_min ? : 0, - pdata->y_max ? : MAX_12BIT, - 0, 0); - input_set_abs_params(input_dev, ABS_PRESSURE, - pdata->pressure_min, pdata->pressure_max, 0, 0); + if (pdata) { + input_set_abs_params(input_dev, ABS_X, + pdata->x_min ? : 0, + pdata->x_max ? : MAX_12BIT, + 0, 0); + input_set_abs_params(input_dev, ABS_Y, + pdata->y_min ? : 0, + pdata->y_max ? : MAX_12BIT, + 0, 0); + input_set_abs_params(input_dev, ABS_PRESSURE, + pdata->pressure_min, + pdata->pressure_max ? : ~0, + 0, 0); + } else { + input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0); + input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0); + touchscreen_parse_properties(input_dev, false); + if (!input_abs_get_max(input_dev, ABS_PRESSURE)) { + dev_err(dev, "Touchscreen pressure is not specified\n"); + return ERR_PTR(-EINVAL); + } + } err = ad7879_write(ts, AD7879_REG_CTRL2, AD7879_RESET); if (err < 0) { dev_err(dev, "Failed to write %s\n", input_dev->name); - goto err_free_mem; + return ERR_PTR(err); } revid = ad7879_read(ts, AD7879_REG_REVID); @@ -573,8 +614,7 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq, if (input_dev->id.product != devid) { dev_err(dev, "Failed to probe %s (%x vs %x)\n", input_dev->name, devid, revid); - err = -ENODEV; - goto err_free_mem; + return ERR_PTR(-ENODEV); } ts->cmd_crtl3 = AD7879_YPLUS_BIT | @@ -594,23 +634,25 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq, AD7879_ACQ(ts->acquisition_time) | AD7879_TMR(ts->pen_down_acc_interval); - err = request_threaded_irq(ts->irq, NULL, ad7879_irq, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - dev_name(dev), ts); + err = devm_request_threaded_irq(dev, ts->irq, NULL, ad7879_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + dev_name(dev), ts); if (err) { - dev_err(dev, "irq %d busy?\n", ts->irq); - goto err_free_mem; + dev_err(dev, "Failed to request IRQ: %d\n", err); + return ERR_PTR(err); } __ad7879_disable(ts); err = sysfs_create_group(&dev->kobj, &ad7879_attr_group); if (err) - goto err_free_irq; + goto err_out; - err = ad7879_gpio_add(ts, pdata); - if (err) - goto err_remove_attr; + if (pdata) { + err = ad7879_gpio_add(ts, pdata); + if (err) + goto err_remove_attr; + } err = input_register_device(input_dev); if (err) @@ -622,11 +664,6 @@ err_remove_gpio: ad7879_gpio_remove(ts); err_remove_attr: sysfs_remove_group(&dev->kobj, &ad7879_attr_group); -err_free_irq: - free_irq(ts->irq, ts); -err_free_mem: - input_free_device(input_dev); - kfree(ts); err_out: return ERR_PTR(err); } @@ -636,9 +673,6 @@ void ad7879_remove(struct ad7879 *ts) { ad7879_gpio_remove(ts); sysfs_remove_group(&ts->dev->kobj, &ad7879_attr_group); - free_irq(ts->irq, ts); - input_unregister_device(ts->input); - kfree(ts); } EXPORT_SYMBOL(ad7879_remove); diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c index e0c714f4b..13bda5786 100644 --- a/drivers/input/touchscreen/atmel_mxt_ts.c +++ b/drivers/input/touchscreen/atmel_mxt_ts.c @@ -1093,6 +1093,19 @@ static int mxt_t6_command(struct mxt_data *data, u16 cmd_offset, return 0; } +static int mxt_acquire_irq(struct mxt_data *data) +{ + int error; + + enable_irq(data->irq); + + error = mxt_process_messages_until_invalid(data); + if (error) + return error; + + return 0; +} + static int mxt_soft_reset(struct mxt_data *data) { struct device *dev = &data->client->dev; @@ -1111,7 +1124,7 @@ static int mxt_soft_reset(struct mxt_data *data) /* Ignore CHG line for 100ms after reset */ msleep(100); - enable_irq(data->irq); + mxt_acquire_irq(data); ret = mxt_wait_for_completion(data, &data->reset_completion, MXT_RESET_TIMEOUT); @@ -1466,19 +1479,6 @@ release_mem: return ret; } -static int mxt_acquire_irq(struct mxt_data *data) -{ - int error; - - enable_irq(data->irq); - - error = mxt_process_messages_until_invalid(data); - if (error) - return error; - - return 0; -} - static int mxt_get_info(struct mxt_data *data) { struct i2c_client *client = data->client; diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c index 5b74e8b84..91cda8f81 100644 --- a/drivers/input/touchscreen/cyttsp_core.c +++ b/drivers/input/touchscreen/cyttsp_core.c @@ -30,9 +30,12 @@ #include <linux/delay.h> #include <linux/input.h> #include <linux/input/mt.h> +#include <linux/input/touchscreen.h> #include <linux/gpio.h> #include <linux/interrupt.h> #include <linux/slab.h> +#include <linux/property.h> +#include <linux/gpio/consumer.h> #include "cyttsp_core.h" @@ -57,6 +60,7 @@ #define CY_DELAY_DFLT 20 /* ms */ #define CY_DELAY_MAX 500 #define CY_ACT_DIST_DFLT 0xF8 +#define CY_ACT_DIST_MASK 0x0F #define CY_HNDSHK_BIT 0x80 /* device mode bits */ #define CY_OPERATE_MODE 0x00 @@ -120,7 +124,7 @@ static int ttsp_send_command(struct cyttsp *ts, u8 cmd) static int cyttsp_handshake(struct cyttsp *ts) { - if (ts->pdata->use_hndshk) + if (ts->use_hndshk) return ttsp_send_command(ts, ts->xy_data.hst_mode ^ CY_HNDSHK_BIT); @@ -142,9 +146,9 @@ static int cyttsp_exit_bl_mode(struct cyttsp *ts) u8 bl_cmd[sizeof(bl_command)]; memcpy(bl_cmd, bl_command, sizeof(bl_command)); - if (ts->pdata->bl_keys) + if (ts->bl_keys) memcpy(&bl_cmd[sizeof(bl_command) - CY_NUM_BL_KEYS], - ts->pdata->bl_keys, CY_NUM_BL_KEYS); + ts->bl_keys, CY_NUM_BL_KEYS); error = ttsp_write_block_data(ts, CY_REG_BASE, sizeof(bl_cmd), bl_cmd); @@ -217,14 +221,14 @@ static int cyttsp_set_sysinfo_regs(struct cyttsp *ts) { int retval = 0; - if (ts->pdata->act_intrvl != CY_ACT_INTRVL_DFLT || - ts->pdata->tch_tmout != CY_TCH_TMOUT_DFLT || - ts->pdata->lp_intrvl != CY_LP_INTRVL_DFLT) { + if (ts->act_intrvl != CY_ACT_INTRVL_DFLT || + ts->tch_tmout != CY_TCH_TMOUT_DFLT || + ts->lp_intrvl != CY_LP_INTRVL_DFLT) { u8 intrvl_ray[] = { - ts->pdata->act_intrvl, - ts->pdata->tch_tmout, - ts->pdata->lp_intrvl + ts->act_intrvl, + ts->tch_tmout, + ts->lp_intrvl }; /* set intrvl registers */ @@ -236,6 +240,16 @@ static int cyttsp_set_sysinfo_regs(struct cyttsp *ts) return retval; } +static void cyttsp_hard_reset(struct cyttsp *ts) +{ + if (ts->reset_gpio) { + gpiod_set_value_cansleep(ts->reset_gpio, 1); + msleep(CY_DELAY_DFLT); + gpiod_set_value_cansleep(ts->reset_gpio, 0); + msleep(CY_DELAY_DFLT); + } +} + static int cyttsp_soft_reset(struct cyttsp *ts) { unsigned long timeout; @@ -263,7 +277,7 @@ out: static int cyttsp_act_dist_setup(struct cyttsp *ts) { - u8 act_dist_setup = ts->pdata->act_dist; + u8 act_dist_setup = ts->act_dist; /* Init gesture; active distance setup */ return ttsp_write_block_data(ts, CY_REG_ACT_DIST, @@ -528,45 +542,110 @@ static void cyttsp_close(struct input_dev *dev) cyttsp_disable(ts); } +static int cyttsp_parse_properties(struct cyttsp *ts) +{ + struct device *dev = ts->dev; + u32 dt_value; + int ret; + + ts->bl_keys = devm_kzalloc(dev, CY_NUM_BL_KEYS, GFP_KERNEL); + if (!ts->bl_keys) + return -ENOMEM; + + /* Set some default values */ + ts->use_hndshk = false; + ts->act_dist = CY_ACT_DIST_DFLT; + ts->act_intrvl = CY_ACT_INTRVL_DFLT; + ts->tch_tmout = CY_TCH_TMOUT_DFLT; + ts->lp_intrvl = CY_LP_INTRVL_DFLT; + + ret = device_property_read_u8_array(dev, "bootloader-key", + ts->bl_keys, CY_NUM_BL_KEYS); + if (ret) { + dev_err(dev, + "bootloader-key property could not be retrieved\n"); + return ret; + } + + ts->use_hndshk = device_property_present(dev, "use-handshake"); + + if (!device_property_read_u32(dev, "active-distance", &dt_value)) { + if (dt_value > 15) { + dev_err(dev, "active-distance (%u) must be [0-15]\n", + dt_value); + return -EINVAL; + } + ts->act_dist &= ~CY_ACT_DIST_MASK; + ts->act_dist |= dt_value; + } + + if (!device_property_read_u32(dev, "active-interval-ms", &dt_value)) { + if (dt_value > 255) { + dev_err(dev, "active-interval-ms (%u) must be [0-255]\n", + dt_value); + return -EINVAL; + } + ts->act_intrvl = dt_value; + } + + if (!device_property_read_u32(dev, "lowpower-interval-ms", &dt_value)) { + if (dt_value > 2550) { + dev_err(dev, "lowpower-interval-ms (%u) must be [0-2550]\n", + dt_value); + return -EINVAL; + } + /* Register value is expressed in 0.01s / bit */ + ts->lp_intrvl = dt_value / 10; + } + + if (!device_property_read_u32(dev, "touch-timeout-ms", &dt_value)) { + if (dt_value > 2550) { + dev_err(dev, "touch-timeout-ms (%u) must be [0-2550]\n", + dt_value); + return -EINVAL; + } + /* Register value is expressed in 0.01s / bit */ + ts->tch_tmout = dt_value / 10; + } + + return 0; +} + struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops, struct device *dev, int irq, size_t xfer_buf_size) { - const struct cyttsp_platform_data *pdata = dev_get_platdata(dev); struct cyttsp *ts; struct input_dev *input_dev; int error; - if (!pdata || !pdata->name || irq <= 0) { - error = -EINVAL; - goto err_out; - } + ts = devm_kzalloc(dev, sizeof(*ts) + xfer_buf_size, GFP_KERNEL); + if (!ts) + return ERR_PTR(-ENOMEM); - ts = kzalloc(sizeof(*ts) + xfer_buf_size, GFP_KERNEL); - input_dev = input_allocate_device(); - if (!ts || !input_dev) { - error = -ENOMEM; - goto err_free_mem; - } + input_dev = devm_input_allocate_device(dev); + if (!input_dev) + return ERR_PTR(-ENOMEM); ts->dev = dev; ts->input = input_dev; - ts->pdata = dev_get_platdata(dev); ts->bus_ops = bus_ops; ts->irq = irq; + ts->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(ts->reset_gpio)) { + error = PTR_ERR(ts->reset_gpio); + dev_err(dev, "Failed to request reset gpio, error %d\n", error); + return ERR_PTR(error); + } + + error = cyttsp_parse_properties(ts); + if (error) + return ERR_PTR(error); + init_completion(&ts->bl_ready); snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev)); - if (pdata->init) { - error = pdata->init(); - if (error) { - dev_err(ts->dev, "platform init failed, err: %d\n", - error); - goto err_free_mem; - } - } - - input_dev->name = pdata->name; + input_dev->name = "Cypress TTSP TouchScreen"; input_dev->phys = ts->phys; input_dev->id.bustype = bus_ops->bustype; input_dev->dev.parent = ts->dev; @@ -576,63 +655,44 @@ struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops, input_set_drvdata(input_dev, ts); - __set_bit(EV_ABS, input_dev->evbit); - input_set_abs_params(input_dev, ABS_MT_POSITION_X, - 0, pdata->maxx, 0, 0); - input_set_abs_params(input_dev, ABS_MT_POSITION_Y, - 0, pdata->maxy, 0, 0); - input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, - 0, CY_MAXZ, 0, 0); + input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_X); + input_set_capability(input_dev, EV_ABS, ABS_MT_POSITION_Y); + touchscreen_parse_properties(input_dev, true); - input_mt_init_slots(input_dev, CY_MAX_ID, 0); + error = input_mt_init_slots(input_dev, CY_MAX_ID, 0); + if (error) { + dev_err(dev, "Unable to init MT slots.\n"); + return ERR_PTR(error); + } - error = request_threaded_irq(ts->irq, NULL, cyttsp_irq, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - pdata->name, ts); + error = devm_request_threaded_irq(dev, ts->irq, NULL, cyttsp_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "cyttsp", ts); if (error) { dev_err(ts->dev, "failed to request IRQ %d, err: %d\n", ts->irq, error); - goto err_platform_exit; + return ERR_PTR(error); } disable_irq(ts->irq); + cyttsp_hard_reset(ts); + error = cyttsp_power_on(ts); if (error) - goto err_free_irq; + return ERR_PTR(error); error = input_register_device(input_dev); if (error) { dev_err(ts->dev, "failed to register input device: %d\n", error); - goto err_free_irq; + return ERR_PTR(error); } return ts; - -err_free_irq: - free_irq(ts->irq, ts); -err_platform_exit: - if (pdata->exit) - pdata->exit(); -err_free_mem: - input_free_device(input_dev); - kfree(ts); -err_out: - return ERR_PTR(error); } EXPORT_SYMBOL_GPL(cyttsp_probe); -void cyttsp_remove(struct cyttsp *ts) -{ - free_irq(ts->irq, ts); - input_unregister_device(ts->input); - if (ts->pdata->exit) - ts->pdata->exit(); - kfree(ts); -} -EXPORT_SYMBOL_GPL(cyttsp_remove); - MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Cypress TrueTouch(R) Standard touchscreen driver core"); MODULE_AUTHOR("Cypress"); diff --git a/drivers/input/touchscreen/cyttsp_core.h b/drivers/input/touchscreen/cyttsp_core.h index 07074110a..7835e2bac 100644 --- a/drivers/input/touchscreen/cyttsp_core.h +++ b/drivers/input/touchscreen/cyttsp_core.h @@ -129,7 +129,6 @@ struct cyttsp { int irq; struct input_dev *input; char phys[32]; - const struct cyttsp_platform_data *pdata; const struct cyttsp_bus_ops *bus_ops; struct cyttsp_bootloader_data bl_data; struct cyttsp_sysinfo_data sysinfo_data; @@ -138,12 +137,19 @@ struct cyttsp { enum cyttsp_state state; bool suspended; + struct gpio_desc *reset_gpio; + bool use_hndshk; + u8 act_dist; + u8 act_intrvl; + u8 tch_tmout; + u8 lp_intrvl; + u8 *bl_keys; + u8 xfer_buf[] ____cacheline_aligned; }; struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops, struct device *dev, int irq, size_t xfer_buf_size); -void cyttsp_remove(struct cyttsp *ts); int cyttsp_i2c_write_block_data(struct device *dev, u8 *xfer_buf, u16 addr, u8 length, const void *values); diff --git a/drivers/input/touchscreen/cyttsp_i2c.c b/drivers/input/touchscreen/cyttsp_i2c.c index eee51b3f2..1edfdba96 100644 --- a/drivers/input/touchscreen/cyttsp_i2c.c +++ b/drivers/input/touchscreen/cyttsp_i2c.c @@ -56,15 +56,6 @@ static int cyttsp_i2c_probe(struct i2c_client *client, return 0; } -static int cyttsp_i2c_remove(struct i2c_client *client) -{ - struct cyttsp *ts = i2c_get_clientdata(client); - - cyttsp_remove(ts); - - return 0; -} - static const struct i2c_device_id cyttsp_i2c_id[] = { { CY_I2C_NAME, 0 }, { } @@ -77,7 +68,6 @@ static struct i2c_driver cyttsp_i2c_driver = { .pm = &cyttsp_pm_ops, }, .probe = cyttsp_i2c_probe, - .remove = cyttsp_i2c_remove, .id_table = cyttsp_i2c_id, }; diff --git a/drivers/input/touchscreen/cyttsp_spi.c b/drivers/input/touchscreen/cyttsp_spi.c index bbeeb2488..3c9d18b1b 100644 --- a/drivers/input/touchscreen/cyttsp_spi.c +++ b/drivers/input/touchscreen/cyttsp_spi.c @@ -170,22 +170,12 @@ static int cyttsp_spi_probe(struct spi_device *spi) return 0; } -static int cyttsp_spi_remove(struct spi_device *spi) -{ - struct cyttsp *ts = spi_get_drvdata(spi); - - cyttsp_remove(ts); - - return 0; -} - static struct spi_driver cyttsp_spi_driver = { .driver = { .name = CY_SPI_NAME, .pm = &cyttsp_pm_ops, }, .probe = cyttsp_spi_probe, - .remove = cyttsp_spi_remove, }; module_spi_driver(cyttsp_spi_driver); diff --git a/drivers/input/touchscreen/fsl-imx25-tcq.c b/drivers/input/touchscreen/fsl-imx25-tcq.c new file mode 100644 index 000000000..fe9877a6a --- /dev/null +++ b/drivers/input/touchscreen/fsl-imx25-tcq.c @@ -0,0 +1,596 @@ +/* + * Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de> + * + * 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. + * + * Based on driver from 2011: + * Juergen Beisert, Pengutronix <kernel@pengutronix.de> + * + * This is the driver for the imx25 TCQ (Touchscreen Conversion Queue) + * connected to the imx25 ADC. + */ + +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/mfd/imx25-tsadc.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +static const char mx25_tcq_name[] = "mx25-tcq"; + +enum mx25_tcq_mode { + MX25_TS_4WIRE, +}; + +struct mx25_tcq_priv { + struct regmap *regs; + struct regmap *core_regs; + struct input_dev *idev; + enum mx25_tcq_mode mode; + unsigned int pen_threshold; + unsigned int sample_count; + unsigned int expected_samples; + unsigned int pen_debounce; + unsigned int settling_time; + struct clk *clk; + int irq; + struct device *dev; +}; + +static struct regmap_config mx25_tcq_regconfig = { + .fast_io = true, + .max_register = 0x5c, + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, +}; + +static const struct of_device_id mx25_tcq_ids[] = { + { .compatible = "fsl,imx25-tcq", }, + { /* Sentinel */ } +}; + +#define TSC_4WIRE_PRE_INDEX 0 +#define TSC_4WIRE_X_INDEX 1 +#define TSC_4WIRE_Y_INDEX 2 +#define TSC_4WIRE_POST_INDEX 3 +#define TSC_4WIRE_LEAVE 4 + +#define MX25_TSC_DEF_THRESHOLD 80 +#define TSC_MAX_SAMPLES 16 + +#define MX25_TSC_REPEAT_WAIT 14 + +enum mx25_adc_configurations { + MX25_CFG_PRECHARGE = 0, + MX25_CFG_TOUCH_DETECT, + MX25_CFG_X_MEASUREMENT, + MX25_CFG_Y_MEASUREMENT, +}; + +#define MX25_PRECHARGE_VALUE (\ + MX25_ADCQ_CFG_YPLL_OFF | \ + MX25_ADCQ_CFG_XNUR_OFF | \ + MX25_ADCQ_CFG_XPUL_HIGH | \ + MX25_ADCQ_CFG_REFP_INT | \ + MX25_ADCQ_CFG_IN_XP | \ + MX25_ADCQ_CFG_REFN_NGND2 | \ + MX25_ADCQ_CFG_IGS) + +#define MX25_TOUCH_DETECT_VALUE (\ + MX25_ADCQ_CFG_YNLR | \ + MX25_ADCQ_CFG_YPLL_OFF | \ + MX25_ADCQ_CFG_XNUR_OFF | \ + MX25_ADCQ_CFG_XPUL_OFF | \ + MX25_ADCQ_CFG_REFP_INT | \ + MX25_ADCQ_CFG_IN_XP | \ + MX25_ADCQ_CFG_REFN_NGND2 | \ + MX25_ADCQ_CFG_PENIACK) + +static void imx25_setup_queue_cfgs(struct mx25_tcq_priv *priv, + unsigned int settling_cnt) +{ + u32 precharge_cfg = + MX25_PRECHARGE_VALUE | + MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt); + u32 touch_detect_cfg = + MX25_TOUCH_DETECT_VALUE | + MX25_ADCQ_CFG_NOS(1) | + MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt); + + regmap_write(priv->core_regs, MX25_TSC_TICR, precharge_cfg); + + /* PRECHARGE */ + regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_PRECHARGE), + precharge_cfg); + + /* TOUCH_DETECT */ + regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_TOUCH_DETECT), + touch_detect_cfg); + + /* X Measurement */ + regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_X_MEASUREMENT), + MX25_ADCQ_CFG_YPLL_OFF | + MX25_ADCQ_CFG_XNUR_LOW | + MX25_ADCQ_CFG_XPUL_HIGH | + MX25_ADCQ_CFG_REFP_XP | + MX25_ADCQ_CFG_IN_YP | + MX25_ADCQ_CFG_REFN_XN | + MX25_ADCQ_CFG_NOS(priv->sample_count) | + MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt)); + + /* Y Measurement */ + regmap_write(priv->regs, MX25_ADCQ_CFG(MX25_CFG_Y_MEASUREMENT), + MX25_ADCQ_CFG_YNLR | + MX25_ADCQ_CFG_YPLL_HIGH | + MX25_ADCQ_CFG_XNUR_OFF | + MX25_ADCQ_CFG_XPUL_OFF | + MX25_ADCQ_CFG_REFP_YP | + MX25_ADCQ_CFG_IN_XP | + MX25_ADCQ_CFG_REFN_YN | + MX25_ADCQ_CFG_NOS(priv->sample_count) | + MX25_ADCQ_CFG_SETTLING_TIME(settling_cnt)); + + /* Enable the touch detection right now */ + regmap_write(priv->core_regs, MX25_TSC_TICR, touch_detect_cfg | + MX25_ADCQ_CFG_IGS); +} + +static int imx25_setup_queue_4wire(struct mx25_tcq_priv *priv, + unsigned settling_cnt, int *items) +{ + imx25_setup_queue_cfgs(priv, settling_cnt); + + /* Setup the conversion queue */ + regmap_write(priv->regs, MX25_ADCQ_ITEM_7_0, + MX25_ADCQ_ITEM(0, MX25_CFG_PRECHARGE) | + MX25_ADCQ_ITEM(1, MX25_CFG_TOUCH_DETECT) | + MX25_ADCQ_ITEM(2, MX25_CFG_X_MEASUREMENT) | + MX25_ADCQ_ITEM(3, MX25_CFG_Y_MEASUREMENT) | + MX25_ADCQ_ITEM(4, MX25_CFG_PRECHARGE) | + MX25_ADCQ_ITEM(5, MX25_CFG_TOUCH_DETECT)); + + /* + * We measure X/Y with 'sample_count' number of samples and execute a + * touch detection twice, with 1 sample each + */ + priv->expected_samples = priv->sample_count * 2 + 2; + *items = 6; + + return 0; +} + +static void mx25_tcq_disable_touch_irq(struct mx25_tcq_priv *priv) +{ + regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_PDMSK, + MX25_ADCQ_CR_PDMSK); +} + +static void mx25_tcq_enable_touch_irq(struct mx25_tcq_priv *priv) +{ + regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_PDMSK, 0); +} + +static void mx25_tcq_disable_fifo_irq(struct mx25_tcq_priv *priv) +{ + regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_FDRY_IRQ, + MX25_ADCQ_MR_FDRY_IRQ); +} + +static void mx25_tcq_enable_fifo_irq(struct mx25_tcq_priv *priv) +{ + regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_FDRY_IRQ, 0); +} + +static void mx25_tcq_force_queue_start(struct mx25_tcq_priv *priv) +{ + regmap_update_bits(priv->regs, MX25_ADCQ_CR, + MX25_ADCQ_CR_FQS, + MX25_ADCQ_CR_FQS); +} + +static void mx25_tcq_force_queue_stop(struct mx25_tcq_priv *priv) +{ + regmap_update_bits(priv->regs, MX25_ADCQ_CR, + MX25_ADCQ_CR_FQS, 0); +} + +static void mx25_tcq_fifo_reset(struct mx25_tcq_priv *priv) +{ + u32 tcqcr; + + regmap_read(priv->regs, MX25_ADCQ_CR, &tcqcr); + regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FRST, + MX25_ADCQ_CR_FRST); + regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FRST, 0); + regmap_write(priv->regs, MX25_ADCQ_CR, tcqcr); +} + +static void mx25_tcq_re_enable_touch_detection(struct mx25_tcq_priv *priv) +{ + /* stop the queue from looping */ + mx25_tcq_force_queue_stop(priv); + + /* for a clean touch detection, preload the X plane */ + regmap_write(priv->core_regs, MX25_TSC_TICR, MX25_PRECHARGE_VALUE); + + /* waste some time now to pre-load the X plate to high voltage */ + mx25_tcq_fifo_reset(priv); + + /* re-enable the detection right now */ + regmap_write(priv->core_regs, MX25_TSC_TICR, + MX25_TOUCH_DETECT_VALUE | MX25_ADCQ_CFG_IGS); + + regmap_update_bits(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_PD, + MX25_ADCQ_SR_PD); + + /* enable the pen down event to be a source for the interrupt */ + regmap_update_bits(priv->regs, MX25_ADCQ_MR, MX25_ADCQ_MR_PD_IRQ, 0); + + /* lets fire the next IRQ if someone touches the touchscreen */ + mx25_tcq_enable_touch_irq(priv); +} + +static void mx25_tcq_create_event_for_4wire(struct mx25_tcq_priv *priv, + u32 *sample_buf, + unsigned int samples) +{ + unsigned int x_pos = 0; + unsigned int y_pos = 0; + unsigned int touch_pre = 0; + unsigned int touch_post = 0; + unsigned int i; + + for (i = 0; i < samples; i++) { + unsigned int index = MX25_ADCQ_FIFO_ID(sample_buf[i]); + unsigned int val = MX25_ADCQ_FIFO_DATA(sample_buf[i]); + + switch (index) { + case 1: + touch_pre = val; + break; + case 2: + x_pos = val; + break; + case 3: + y_pos = val; + break; + case 5: + touch_post = val; + break; + default: + dev_dbg(priv->dev, "Dropped samples because of invalid index %d\n", + index); + return; + } + } + + if (samples != 0) { + /* + * only if both touch measures are below a threshold, + * the position is valid + */ + if (touch_pre < priv->pen_threshold && + touch_post < priv->pen_threshold) { + /* valid samples, generate a report */ + x_pos /= priv->sample_count; + y_pos /= priv->sample_count; + input_report_abs(priv->idev, ABS_X, x_pos); + input_report_abs(priv->idev, ABS_Y, y_pos); + input_report_key(priv->idev, BTN_TOUCH, 1); + input_sync(priv->idev); + + /* get next sample */ + mx25_tcq_enable_fifo_irq(priv); + } else if (touch_pre >= priv->pen_threshold && + touch_post >= priv->pen_threshold) { + /* + * if both samples are invalid, + * generate a release report + */ + input_report_key(priv->idev, BTN_TOUCH, 0); + input_sync(priv->idev); + mx25_tcq_re_enable_touch_detection(priv); + } else { + /* + * if only one of both touch measurements are + * below the threshold, still some bouncing + * happens. Take additional samples in this + * case to be sure + */ + mx25_tcq_enable_fifo_irq(priv); + } + } +} + +static irqreturn_t mx25_tcq_irq_thread(int irq, void *dev_id) +{ + struct mx25_tcq_priv *priv = dev_id; + u32 sample_buf[TSC_MAX_SAMPLES]; + unsigned int samples; + u32 stats; + unsigned int i; + + /* + * Check how many samples are available. We always have to read exactly + * sample_count samples from the fifo, or a multiple of sample_count. + * Otherwise we mixup samples into different touch events. + */ + regmap_read(priv->regs, MX25_ADCQ_SR, &stats); + samples = MX25_ADCQ_SR_FDN(stats); + samples -= samples % priv->sample_count; + + if (!samples) + return IRQ_HANDLED; + + for (i = 0; i != samples; ++i) + regmap_read(priv->regs, MX25_ADCQ_FIFO, &sample_buf[i]); + + mx25_tcq_create_event_for_4wire(priv, sample_buf, samples); + + return IRQ_HANDLED; +} + +static irqreturn_t mx25_tcq_irq(int irq, void *dev_id) +{ + struct mx25_tcq_priv *priv = dev_id; + u32 stat; + int ret = IRQ_HANDLED; + + regmap_read(priv->regs, MX25_ADCQ_SR, &stat); + + if (stat & (MX25_ADCQ_SR_FRR | MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR)) + mx25_tcq_re_enable_touch_detection(priv); + + if (stat & MX25_ADCQ_SR_PD) { + mx25_tcq_disable_touch_irq(priv); + mx25_tcq_force_queue_start(priv); + mx25_tcq_enable_fifo_irq(priv); + } + + if (stat & MX25_ADCQ_SR_FDRY) { + mx25_tcq_disable_fifo_irq(priv); + ret = IRQ_WAKE_THREAD; + } + + regmap_update_bits(priv->regs, MX25_ADCQ_SR, MX25_ADCQ_SR_FRR | + MX25_ADCQ_SR_FUR | MX25_ADCQ_SR_FOR | + MX25_ADCQ_SR_PD, + MX25_ADCQ_SR_FRR | MX25_ADCQ_SR_FUR | + MX25_ADCQ_SR_FOR | MX25_ADCQ_SR_PD); + + return ret; +} + +/* configure the state machine for a 4-wire touchscreen */ +static int mx25_tcq_init(struct mx25_tcq_priv *priv) +{ + u32 tgcr; + unsigned int ipg_div; + unsigned int adc_period; + unsigned int debounce_cnt; + unsigned int settling_cnt; + int itemct; + int error; + + regmap_read(priv->core_regs, MX25_TSC_TGCR, &tgcr); + ipg_div = max_t(unsigned int, 4, MX25_TGCR_GET_ADCCLK(tgcr)); + adc_period = USEC_PER_SEC * ipg_div * 2 + 2; + adc_period /= clk_get_rate(priv->clk) / 1000 + 1; + debounce_cnt = DIV_ROUND_UP(priv->pen_debounce, adc_period * 8) - 1; + settling_cnt = DIV_ROUND_UP(priv->settling_time, adc_period * 8) - 1; + + /* Reset */ + regmap_write(priv->regs, MX25_ADCQ_CR, + MX25_ADCQ_CR_QRST | MX25_ADCQ_CR_FRST); + regmap_update_bits(priv->regs, MX25_ADCQ_CR, + MX25_ADCQ_CR_QRST | MX25_ADCQ_CR_FRST, 0); + + /* up to 128 * 8 ADC clocks are possible */ + if (debounce_cnt > 127) + debounce_cnt = 127; + + /* up to 255 * 8 ADC clocks are possible */ + if (settling_cnt > 255) + settling_cnt = 255; + + error = imx25_setup_queue_4wire(priv, settling_cnt, &itemct); + if (error) + return error; + + regmap_update_bits(priv->regs, MX25_ADCQ_CR, + MX25_ADCQ_CR_LITEMID_MASK | MX25_ADCQ_CR_WMRK_MASK, + MX25_ADCQ_CR_LITEMID(itemct - 1) | + MX25_ADCQ_CR_WMRK(priv->expected_samples - 1)); + + /* setup debounce count */ + regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, + MX25_TGCR_PDBTIME_MASK, + MX25_TGCR_PDBTIME(debounce_cnt)); + + /* enable debounce */ + regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, MX25_TGCR_PDBEN, + MX25_TGCR_PDBEN); + regmap_update_bits(priv->core_regs, MX25_TSC_TGCR, MX25_TGCR_PDEN, + MX25_TGCR_PDEN); + + /* enable the engine on demand */ + regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_QSM_MASK, + MX25_ADCQ_CR_QSM_FQS); + + /* Enable repeat and repeat wait */ + regmap_update_bits(priv->regs, MX25_ADCQ_CR, + MX25_ADCQ_CR_RPT | MX25_ADCQ_CR_RWAIT_MASK, + MX25_ADCQ_CR_RPT | + MX25_ADCQ_CR_RWAIT(MX25_TSC_REPEAT_WAIT)); + + return 0; +} + +static int mx25_tcq_parse_dt(struct platform_device *pdev, + struct mx25_tcq_priv *priv) +{ + struct device_node *np = pdev->dev.of_node; + u32 wires; + int error; + + /* Setup defaults */ + priv->pen_threshold = 500; + priv->sample_count = 3; + priv->pen_debounce = 1000000; + priv->settling_time = 250000; + + error = of_property_read_u32(np, "fsl,wires", &wires); + if (error) { + dev_err(&pdev->dev, "Failed to find fsl,wires properties\n"); + return error; + } + + if (wires == 4) { + priv->mode = MX25_TS_4WIRE; + } else { + dev_err(&pdev->dev, "%u-wire mode not supported\n", wires); + return -EINVAL; + } + + /* These are optional, we don't care about the return values */ + of_property_read_u32(np, "fsl,pen-threshold", &priv->pen_threshold); + of_property_read_u32(np, "fsl,settling-time-ns", &priv->settling_time); + of_property_read_u32(np, "fsl,pen-debounce-ns", &priv->pen_debounce); + + return 0; +} + +static int mx25_tcq_open(struct input_dev *idev) +{ + struct device *dev = &idev->dev; + struct mx25_tcq_priv *priv = dev_get_drvdata(dev); + int error; + + error = clk_prepare_enable(priv->clk); + if (error) { + dev_err(dev, "Failed to enable ipg clock\n"); + return error; + } + + error = mx25_tcq_init(priv); + if (error) { + dev_err(dev, "Failed to init tcq\n"); + clk_disable_unprepare(priv->clk); + return error; + } + + mx25_tcq_re_enable_touch_detection(priv); + + return 0; +} + +static void mx25_tcq_close(struct input_dev *idev) +{ + struct mx25_tcq_priv *priv = input_get_drvdata(idev); + + mx25_tcq_force_queue_stop(priv); + mx25_tcq_disable_touch_irq(priv); + mx25_tcq_disable_fifo_irq(priv); + clk_disable_unprepare(priv->clk); +} + +static int mx25_tcq_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct input_dev *idev; + struct mx25_tcq_priv *priv; + struct mx25_tsadc *tsadc = dev_get_drvdata(pdev->dev.parent); + struct resource *res; + void __iomem *mem; + int error; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + priv->dev = dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mem = devm_ioremap_resource(dev, res); + if (IS_ERR(mem)) + return PTR_ERR(mem); + + error = mx25_tcq_parse_dt(pdev, priv); + if (error) + return error; + + priv->regs = devm_regmap_init_mmio(dev, mem, &mx25_tcq_regconfig); + if (IS_ERR(priv->regs)) { + dev_err(dev, "Failed to initialize regmap\n"); + return PTR_ERR(priv->regs); + } + + priv->irq = platform_get_irq(pdev, 0); + if (priv->irq <= 0) { + dev_err(dev, "Failed to get IRQ\n"); + return priv->irq; + } + + idev = devm_input_allocate_device(dev); + if (!idev) { + dev_err(dev, "Failed to allocate input device\n"); + return -ENOMEM; + } + + idev->name = mx25_tcq_name; + input_set_capability(idev, EV_KEY, BTN_TOUCH); + input_set_abs_params(idev, ABS_X, 0, 0xfff, 0, 0); + input_set_abs_params(idev, ABS_Y, 0, 0xfff, 0, 0); + + idev->id.bustype = BUS_HOST; + idev->open = mx25_tcq_open; + idev->close = mx25_tcq_close; + + priv->idev = idev; + input_set_drvdata(idev, priv); + + priv->core_regs = tsadc->regs; + if (!priv->core_regs) + return -EINVAL; + + priv->clk = tsadc->clk; + if (!priv->clk) + return -EINVAL; + + platform_set_drvdata(pdev, priv); + + error = devm_request_threaded_irq(dev, priv->irq, mx25_tcq_irq, + mx25_tcq_irq_thread, 0, pdev->name, + priv); + if (error) { + dev_err(dev, "Failed requesting IRQ\n"); + return error; + } + + error = input_register_device(idev); + if (error) { + dev_err(dev, "Failed to register input device\n"); + return error; + } + + return 0; +} + +static struct platform_driver mx25_tcq_driver = { + .driver = { + .name = "mx25-tcq", + .of_match_table = mx25_tcq_ids, + }, + .probe = mx25_tcq_probe, +}; +module_platform_driver(mx25_tcq_driver); + +MODULE_DESCRIPTION("TS input driver for Freescale mx25"); +MODULE_AUTHOR("Markus Pargmann <mpa@pengutronix.de>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/touchscreen/melfas_mip4.c b/drivers/input/touchscreen/melfas_mip4.c new file mode 100644 index 000000000..d4c0a0475 --- /dev/null +++ b/drivers/input/touchscreen/melfas_mip4.c @@ -0,0 +1,1543 @@ +/* + * MELFAS MIP4 Touchscreen + * + * Copyright (C) 2016 MELFAS Inc. + * + * Author : Sangwon Jee <jeesw@melfas.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/acpi.h> +#include <linux/delay.h> +#include <linux/firmware.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/input.h> +#include <linux/input/mt.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/slab.h> +#include <asm/unaligned.h> + +#define MIP4_DEVICE_NAME "mip4_ts" + +/***************************************************************** + * Protocol + * Version : MIP 4.0 Rev 4.6 + *****************************************************************/ + +/* Address */ +#define MIP4_R0_BOOT 0x00 +#define MIP4_R1_BOOT_MODE 0x01 +#define MIP4_R1_BOOT_BUF_ADDR 0x10 +#define MIP4_R1_BOOT_STATUS 0x20 +#define MIP4_R1_BOOT_CMD 0x30 +#define MIP4_R1_BOOT_TARGET_ADDR 0x40 +#define MIP4_R1_BOOT_SIZE 0x44 + +#define MIP4_R0_INFO 0x01 +#define MIP4_R1_INFO_PRODUCT_NAME 0x00 +#define MIP4_R1_INFO_RESOLUTION_X 0x10 +#define MIP4_R1_INFO_RESOLUTION_Y 0x12 +#define MIP4_R1_INFO_NODE_NUM_X 0x14 +#define MIP4_R1_INFO_NODE_NUM_Y 0x15 +#define MIP4_R1_INFO_KEY_NUM 0x16 +#define MIP4_R1_INFO_PRESSURE_NUM 0x17 +#define MIP4_R1_INFO_LENGTH_X 0x18 +#define MIP4_R1_INFO_LENGTH_Y 0x1A +#define MIP4_R1_INFO_PPM_X 0x1C +#define MIP4_R1_INFO_PPM_Y 0x1D +#define MIP4_R1_INFO_VERSION_BOOT 0x20 +#define MIP4_R1_INFO_VERSION_CORE 0x22 +#define MIP4_R1_INFO_VERSION_APP 0x24 +#define MIP4_R1_INFO_VERSION_PARAM 0x26 +#define MIP4_R1_INFO_SECT_BOOT_START 0x30 +#define MIP4_R1_INFO_SECT_BOOT_END 0x31 +#define MIP4_R1_INFO_SECT_CORE_START 0x32 +#define MIP4_R1_INFO_SECT_CORE_END 0x33 +#define MIP4_R1_INFO_SECT_APP_START 0x34 +#define MIP4_R1_INFO_SECT_APP_END 0x35 +#define MIP4_R1_INFO_SECT_PARAM_START 0x36 +#define MIP4_R1_INFO_SECT_PARAM_END 0x37 +#define MIP4_R1_INFO_BUILD_DATE 0x40 +#define MIP4_R1_INFO_BUILD_TIME 0x44 +#define MIP4_R1_INFO_CHECKSUM_PRECALC 0x48 +#define MIP4_R1_INFO_CHECKSUM_REALTIME 0x4A +#define MIP4_R1_INFO_PROTOCOL_NAME 0x50 +#define MIP4_R1_INFO_PROTOCOL_VERSION 0x58 +#define MIP4_R1_INFO_IC_ID 0x70 +#define MIP4_R1_INFO_IC_NAME 0x71 +#define MIP4_R1_INFO_IC_VENDOR_ID 0x75 +#define MIP4_R1_INFO_IC_HW_CATEGORY 0x77 +#define MIP4_R1_INFO_CONTACT_THD_SCR 0x78 +#define MIP4_R1_INFO_CONTACT_THD_KEY 0x7A + +#define MIP4_R0_EVENT 0x02 +#define MIP4_R1_EVENT_SUPPORTED_FUNC 0x00 +#define MIP4_R1_EVENT_FORMAT 0x04 +#define MIP4_R1_EVENT_SIZE 0x06 +#define MIP4_R1_EVENT_PACKET_INFO 0x10 +#define MIP4_R1_EVENT_PACKET_DATA 0x11 + +#define MIP4_R0_CTRL 0x06 +#define MIP4_R1_CTRL_READY_STATUS 0x00 +#define MIP4_R1_CTRL_EVENT_READY 0x01 +#define MIP4_R1_CTRL_MODE 0x10 +#define MIP4_R1_CTRL_EVENT_TRIGGER_TYPE 0x11 +#define MIP4_R1_CTRL_RECALIBRATE 0x12 +#define MIP4_R1_CTRL_POWER_STATE 0x13 +#define MIP4_R1_CTRL_GESTURE_TYPE 0x14 +#define MIP4_R1_CTRL_DISABLE_ESD_ALERT 0x18 +#define MIP4_R1_CTRL_CHARGER_MODE 0x19 +#define MIP4_R1_CTRL_HIGH_SENS_MODE 0x1A +#define MIP4_R1_CTRL_WINDOW_MODE 0x1B +#define MIP4_R1_CTRL_PALM_REJECTION 0x1C +#define MIP4_R1_CTRL_EDGE_CORRECTION 0x1D +#define MIP4_R1_CTRL_ENTER_GLOVE_MODE 0x1E +#define MIP4_R1_CTRL_I2C_ON_LPM 0x1F +#define MIP4_R1_CTRL_GESTURE_DEBUG 0x20 +#define MIP4_R1_CTRL_PALM_EVENT 0x22 +#define MIP4_R1_CTRL_PROXIMITY_SENSING 0x23 + +/* Value */ +#define MIP4_BOOT_MODE_BOOT 0x01 +#define MIP4_BOOT_MODE_APP 0x02 + +#define MIP4_BOOT_STATUS_BUSY 0x05 +#define MIP4_BOOT_STATUS_ERROR 0x0E +#define MIP4_BOOT_STATUS_DONE 0xA0 + +#define MIP4_BOOT_CMD_MASS_ERASE 0x15 +#define MIP4_BOOT_CMD_PROGRAM 0x54 +#define MIP4_BOOT_CMD_ERASE 0x8F +#define MIP4_BOOT_CMD_WRITE 0xA5 +#define MIP4_BOOT_CMD_READ 0xC2 + +#define MIP4_EVENT_INPUT_TYPE_KEY 0 +#define MIP4_EVENT_INPUT_TYPE_SCREEN 1 +#define MIP4_EVENT_INPUT_TYPE_PROXIMITY 2 + +#define I2C_RETRY_COUNT 3 /* 2~ */ + +#define MIP4_BUF_SIZE 128 +#define MIP4_MAX_FINGERS 10 +#define MIP4_MAX_KEYS 4 + +#define MIP4_TOUCH_MAJOR_MIN 0 +#define MIP4_TOUCH_MAJOR_MAX 255 +#define MIP4_TOUCH_MINOR_MIN 0 +#define MIP4_TOUCH_MINOR_MAX 255 +#define MIP4_PRESSURE_MIN 0 +#define MIP4_PRESSURE_MAX 255 + +#define MIP4_FW_NAME "/*(DEBLOBBED)*/" +#define MIP4_FW_UPDATE_DEBUG 0 /* 0 (default) or 1 */ + +struct mip4_fw_version { + u16 boot; + u16 core; + u16 app; + u16 param; +}; + +struct mip4_ts { + struct i2c_client *client; + struct input_dev *input; + struct gpio_desc *gpio_ce; + + char phys[32]; + char product_name[16]; + + unsigned int max_x; + unsigned int max_y; + u8 node_x; + u8 node_y; + u8 node_key; + unsigned int ppm_x; + unsigned int ppm_y; + + struct mip4_fw_version fw_version; + + unsigned int event_size; + unsigned int event_format; + + unsigned int key_num; + unsigned short key_code[MIP4_MAX_KEYS]; + + bool wake_irq_enabled; + + u8 buf[MIP4_BUF_SIZE]; +}; + +static int mip4_i2c_xfer(struct mip4_ts *ts, + char *write_buf, unsigned int write_len, + char *read_buf, unsigned int read_len) +{ + struct i2c_msg msg[] = { + { + .addr = ts->client->addr, + .flags = 0, + .buf = write_buf, + .len = write_len, + }, { + .addr = ts->client->addr, + .flags = I2C_M_RD, + .buf = read_buf, + .len = read_len, + }, + }; + int retry = I2C_RETRY_COUNT; + int res; + int error; + + do { + res = i2c_transfer(ts->client->adapter, msg, ARRAY_SIZE(msg)); + if (res == ARRAY_SIZE(msg)) + return 0; + + error = res < 0 ? res : -EIO; + dev_err(&ts->client->dev, + "%s - i2c_transfer failed: %d (%d)\n", + __func__, error, res); + } while (--retry); + + return error; +} + +static void mip4_parse_fw_version(const u8 *buf, struct mip4_fw_version *v) +{ + v->boot = get_unaligned_le16(buf + 0); + v->core = get_unaligned_le16(buf + 2); + v->app = get_unaligned_le16(buf + 4); + v->param = get_unaligned_le16(buf + 6); +} + +/* + * Read chip firmware version + */ +static int mip4_get_fw_version(struct mip4_ts *ts) +{ + u8 cmd[] = { MIP4_R0_INFO, MIP4_R1_INFO_VERSION_BOOT }; + u8 buf[sizeof(ts->fw_version)]; + int error; + + error = mip4_i2c_xfer(ts, cmd, sizeof(cmd), buf, sizeof(buf)); + if (error) { + memset(&ts->fw_version, 0xff, sizeof(ts->fw_version)); + return error; + } + + mip4_parse_fw_version(buf, &ts->fw_version); + + return 0; +} + +/* + * Fetch device characteristics + */ +static int mip4_query_device(struct mip4_ts *ts) +{ + int error; + u8 cmd[2]; + u8 buf[14]; + + /* Product name */ + cmd[0] = MIP4_R0_INFO; + cmd[1] = MIP4_R1_INFO_PRODUCT_NAME; + error = mip4_i2c_xfer(ts, cmd, sizeof(cmd), + ts->product_name, sizeof(ts->product_name)); + if (error) + dev_warn(&ts->client->dev, + "Failed to retrieve product name: %d\n", error); + else + dev_dbg(&ts->client->dev, "product name: %.*s\n", + (int)sizeof(ts->product_name), ts->product_name); + + /* Firmware version */ + error = mip4_get_fw_version(ts); + if (error) + dev_warn(&ts->client->dev, + "Failed to retrieve FW version: %d\n", error); + else + dev_dbg(&ts->client->dev, "F/W Version: %04X %04X %04X %04X\n", + ts->fw_version.boot, ts->fw_version.core, + ts->fw_version.app, ts->fw_version.param); + + /* Resolution */ + cmd[0] = MIP4_R0_INFO; + cmd[1] = MIP4_R1_INFO_RESOLUTION_X; + error = mip4_i2c_xfer(ts, cmd, sizeof(cmd), buf, 14); + if (error) { + dev_warn(&ts->client->dev, + "Failed to retrieve touchscreen parameters: %d\n", + error); + } else { + ts->max_x = get_unaligned_le16(&buf[0]); + ts->max_y = get_unaligned_le16(&buf[2]); + dev_dbg(&ts->client->dev, "max_x: %d, max_y: %d\n", + ts->max_x, ts->max_y); + + ts->node_x = buf[4]; + ts->node_y = buf[5]; + ts->node_key = buf[6]; + dev_dbg(&ts->client->dev, + "node_x: %d, node_y: %d, node_key: %d\n", + ts->node_x, ts->node_y, ts->node_key); + + ts->ppm_x = buf[12]; + ts->ppm_y = buf[13]; + dev_dbg(&ts->client->dev, "ppm_x: %d, ppm_y: %d\n", + ts->ppm_x, ts->ppm_y); + + /* Key ts */ + if (ts->node_key > 0) + ts->key_num = ts->node_key; + } + + /* Protocol */ + cmd[0] = MIP4_R0_EVENT; + cmd[1] = MIP4_R1_EVENT_SUPPORTED_FUNC; + error = mip4_i2c_xfer(ts, cmd, sizeof(cmd), buf, 7); + if (error) { + dev_warn(&ts->client->dev, + "Failed to retrieve device type: %d\n", error); + ts->event_format = 0xff; + } else { + ts->event_format = get_unaligned_le16(&buf[4]); + ts->event_size = buf[6]; + dev_dbg(&ts->client->dev, "event_format: %d, event_size: %d\n", + ts->event_format, ts->event_size); + + if (ts->event_format == 2 || ts->event_format > 3) + dev_warn(&ts->client->dev, + "Unknown event format %d\n", ts->event_format); + } + + return 0; +} + +static int mip4_power_on(struct mip4_ts *ts) +{ + if (ts->gpio_ce) { + gpiod_set_value_cansleep(ts->gpio_ce, 1); + + /* Booting delay : 200~300ms */ + usleep_range(200 * 1000, 300 * 1000); + } + + return 0; +} + +static void mip4_power_off(struct mip4_ts *ts) +{ + if (ts->gpio_ce) + gpiod_set_value_cansleep(ts->gpio_ce, 0); +} + +/* + * Clear touch input event status + */ +static void mip4_clear_input(struct mip4_ts *ts) +{ + int i; + + /* Screen */ + for (i = 0; i < MIP4_MAX_FINGERS; i++) { + input_mt_slot(ts->input, i); + input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, 0); + } + + /* Keys */ + for (i = 0; i < ts->key_num; i++) + input_report_key(ts->input, ts->key_code[i], 0); + + input_sync(ts->input); +} + +static int mip4_enable(struct mip4_ts *ts) +{ + int error; + + error = mip4_power_on(ts); + if (error) + return error; + + enable_irq(ts->client->irq); + + return 0; +} + +static void mip4_disable(struct mip4_ts *ts) +{ + disable_irq(ts->client->irq); + + mip4_power_off(ts); + + mip4_clear_input(ts); +} + +/***************************************************************** + * Input handling + *****************************************************************/ + +static void mip4_report_keys(struct mip4_ts *ts, u8 *packet) +{ + u8 key; + bool down; + + switch (ts->event_format) { + case 0: + case 1: + key = packet[0] & 0x0F; + down = packet[0] & 0x80; + break; + + case 3: + default: + key = packet[0] & 0x0F; + down = packet[1] & 0x01; + break; + } + + /* Report key event */ + if (key >= 1 && key <= ts->key_num) { + unsigned short keycode = ts->key_code[key - 1]; + + dev_dbg(&ts->client->dev, + "Key - ID: %d, keycode: %d, state: %d\n", + key, keycode, down); + + input_event(ts->input, EV_MSC, MSC_SCAN, keycode); + input_report_key(ts->input, keycode, down); + + } else { + dev_err(&ts->client->dev, "Unknown key: %d\n", key); + } +} + +static void mip4_report_touch(struct mip4_ts *ts, u8 *packet) +{ + int id; + bool hover; + bool palm; + bool state; + u16 x, y; + u8 pressure_stage = 0; + u8 pressure; + u8 size; + u8 touch_major; + u8 touch_minor; + + switch (ts->event_format) { + case 0: + case 1: + /* Touch only */ + state = packet[0] & BIT(7); + hover = packet[0] & BIT(5); + palm = packet[0] & BIT(4); + id = (packet[0] & 0x0F) - 1; + x = ((packet[1] & 0x0F) << 8) | packet[2]; + y = (((packet[1] >> 4) & 0x0F) << 8) | + packet[3]; + pressure = packet[4]; + size = packet[5]; + if (ts->event_format == 0) { + touch_major = packet[5]; + touch_minor = packet[5]; + } else { + touch_major = packet[6]; + touch_minor = packet[7]; + } + break; + + case 3: + default: + /* Touch + Force(Pressure) */ + id = (packet[0] & 0x0F) - 1; + hover = packet[1] & BIT(2); + palm = packet[1] & BIT(1); + state = packet[1] & BIT(0); + x = ((packet[2] & 0x0F) << 8) | packet[3]; + y = (((packet[2] >> 4) & 0x0F) << 8) | + packet[4]; + size = packet[6]; + pressure_stage = (packet[7] & 0xF0) >> 4; + pressure = ((packet[7] & 0x0F) << 8) | + packet[8]; + touch_major = packet[9]; + touch_minor = packet[10]; + break; + } + + dev_dbg(&ts->client->dev, + "Screen - Slot: %d State: %d X: %04d Y: %04d Z: %d\n", + id, state, x, y, pressure); + + if (unlikely(id < 0 || id >= MIP4_MAX_FINGERS)) { + dev_err(&ts->client->dev, "Screen - invalid slot ID: %d\n", id); + } else if (state) { + /* Press or Move event */ + input_mt_slot(ts->input, id); + input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, true); + input_report_abs(ts->input, ABS_MT_POSITION_X, x); + input_report_abs(ts->input, ABS_MT_POSITION_Y, y); + input_report_abs(ts->input, ABS_MT_PRESSURE, pressure); + input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, touch_major); + input_report_abs(ts->input, ABS_MT_TOUCH_MINOR, touch_minor); + } else { + /* Release event */ + input_mt_slot(ts->input, id); + input_mt_report_slot_state(ts->input, MT_TOOL_FINGER, 0); + } + + input_mt_sync_frame(ts->input); +} + +static int mip4_handle_packet(struct mip4_ts *ts, u8 *packet) +{ + u8 type; + + switch (ts->event_format) { + case 0: + case 1: + type = (packet[0] & 0x40) >> 6; + break; + + case 3: + type = (packet[0] & 0xF0) >> 4; + break; + + default: + /* Should not happen unless we have corrupted firmware */ + return -EINVAL; + } + + dev_dbg(&ts->client->dev, "Type: %d\n", type); + + /* Report input event */ + switch (type) { + case MIP4_EVENT_INPUT_TYPE_KEY: + mip4_report_keys(ts, packet); + break; + + case MIP4_EVENT_INPUT_TYPE_SCREEN: + mip4_report_touch(ts, packet); + break; + + default: + dev_err(&ts->client->dev, "Unknown event type: %d\n", type); + break; + } + + return 0; +} + +static irqreturn_t mip4_interrupt(int irq, void *dev_id) +{ + struct mip4_ts *ts = dev_id; + struct i2c_client *client = ts->client; + unsigned int i; + int error; + u8 cmd[2]; + u8 size; + bool alert; + + /* Read packet info */ + cmd[0] = MIP4_R0_EVENT; + cmd[1] = MIP4_R1_EVENT_PACKET_INFO; + error = mip4_i2c_xfer(ts, cmd, sizeof(cmd), ts->buf, 1); + if (error) { + dev_err(&client->dev, + "Failed to read packet info: %d\n", error); + goto out; + } + + size = ts->buf[0] & 0x7F; + alert = ts->buf[0] & BIT(7); + dev_dbg(&client->dev, "packet size: %d, alert: %d\n", size, alert); + + /* Check size */ + if (!size) { + dev_err(&client->dev, "Empty packet\n"); + goto out; + } + + /* Read packet data */ + cmd[0] = MIP4_R0_EVENT; + cmd[1] = MIP4_R1_EVENT_PACKET_DATA; + error = mip4_i2c_xfer(ts, cmd, sizeof(cmd), ts->buf, size); + if (error) { + dev_err(&client->dev, + "Failed to read packet data: %d\n", error); + goto out; + } + + if (alert) { + dev_dbg(&client->dev, "Alert: %d\n", ts->buf[0]); + } else { + for (i = 0; i < size; i += ts->event_size) { + error = mip4_handle_packet(ts, &ts->buf[i]); + if (error) + break; + } + + input_sync(ts->input); + } + +out: + return IRQ_HANDLED; +} + +static int mip4_input_open(struct input_dev *dev) +{ + struct mip4_ts *ts = input_get_drvdata(dev); + + return mip4_enable(ts); +} + +static void mip4_input_close(struct input_dev *dev) +{ + struct mip4_ts *ts = input_get_drvdata(dev); + + mip4_disable(ts); +} + +/***************************************************************** + * Firmware update + *****************************************************************/ + +/* Firmware Info */ +#define MIP4_BL_PAGE_SIZE 512 /* 512 */ +#define MIP4_BL_PACKET_SIZE 512 /* 512, 256, 128, 64, ... */ + +/* + * Firmware binary tail info + */ + +struct mip4_bin_tail { + u8 tail_mark[4]; + u8 chip_name[4]; + + __le32 bin_start_addr; + __le32 bin_length; + + __le16 ver_boot; + __le16 ver_core; + __le16 ver_app; + __le16 ver_param; + + u8 boot_start; + u8 boot_end; + u8 core_start; + u8 core_end; + u8 app_start; + u8 app_end; + u8 param_start; + u8 param_end; + + u8 checksum_type; + u8 hw_category; + + __le16 param_id; + __le32 param_length; + __le32 build_date; + __le32 build_time; + + __le32 reserved1; + __le32 reserved2; + __le16 reserved3; + __le16 tail_size; + __le32 crc; +} __packed; + +#define MIP4_BIN_TAIL_MARK "MBT\001" +#define MIP4_BIN_TAIL_SIZE (sizeof(struct mip4_bin_tail)) + +/* +* Bootloader - Read status +*/ +static int mip4_bl_read_status(struct mip4_ts *ts) +{ + u8 cmd[] = { MIP4_R0_BOOT, MIP4_R1_BOOT_STATUS }; + u8 result; + struct i2c_msg msg[] = { + { + .addr = ts->client->addr, + .flags = 0, + .buf = cmd, + .len = sizeof(cmd), + }, { + .addr = ts->client->addr, + .flags = I2C_M_RD, + .buf = &result, + .len = sizeof(result), + }, + }; + int ret; + int error; + int retry = 1000; + + do { + ret = i2c_transfer(ts->client->adapter, msg, ARRAY_SIZE(msg)); + if (ret != ARRAY_SIZE(msg)) { + error = ret < 0 ? ret : -EIO; + dev_err(&ts->client->dev, + "Failed to read bootloader status: %d\n", + error); + return error; + } + + switch (result) { + case MIP4_BOOT_STATUS_DONE: + dev_dbg(&ts->client->dev, "%s - done\n", __func__); + return 0; + + case MIP4_BOOT_STATUS_ERROR: + dev_err(&ts->client->dev, "Bootloader failure\n"); + return -EIO; + + case MIP4_BOOT_STATUS_BUSY: + dev_dbg(&ts->client->dev, "%s - Busy\n", __func__); + error = -EBUSY; + break; + + default: + dev_err(&ts->client->dev, + "Unexpected bootloader status: %#02x\n", + result); + error = -EINVAL; + break; + } + + usleep_range(1000, 2000); + } while (--retry); + + return error; +} + +/* +* Bootloader - Change mode +*/ +static int mip4_bl_change_mode(struct mip4_ts *ts, u8 mode) +{ + u8 mode_chg_cmd[] = { MIP4_R0_BOOT, MIP4_R1_BOOT_MODE, mode }; + u8 mode_read_cmd[] = { MIP4_R0_BOOT, MIP4_R1_BOOT_MODE }; + u8 result; + struct i2c_msg msg[] = { + { + .addr = ts->client->addr, + .flags = 0, + .buf = mode_read_cmd, + .len = sizeof(mode_read_cmd), + }, { + .addr = ts->client->addr, + .flags = I2C_M_RD, + .buf = &result, + .len = sizeof(result), + }, + }; + int retry = 10; + int ret; + int error; + + do { + /* Send mode change command */ + ret = i2c_master_send(ts->client, + mode_chg_cmd, sizeof(mode_chg_cmd)); + if (ret != sizeof(mode_chg_cmd)) { + error = ret < 0 ? ret : -EIO; + dev_err(&ts->client->dev, + "Failed to send %d mode change: %d (%d)\n", + mode, error, ret); + return error; + } + + dev_dbg(&ts->client->dev, + "Sent mode change request (mode: %d)\n", mode); + + /* Wait */ + msleep(1000); + + /* Verify target mode */ + ret = i2c_transfer(ts->client->adapter, msg, ARRAY_SIZE(msg)); + if (ret != ARRAY_SIZE(msg)) { + error = ret < 0 ? ret : -EIO; + dev_err(&ts->client->dev, + "Failed to read device mode: %d\n", error); + return error; + } + + dev_dbg(&ts->client->dev, + "Current device mode: %d, want: %d\n", result, mode); + + if (result == mode) + return 0; + + } while (--retry); + + return -EIO; +} + +/* + * Bootloader - Start bootloader mode + */ +static int mip4_bl_enter(struct mip4_ts *ts) +{ + return mip4_bl_change_mode(ts, MIP4_BOOT_MODE_BOOT); +} + +/* + * Bootloader - Exit bootloader mode + */ +static int mip4_bl_exit(struct mip4_ts *ts) +{ + return mip4_bl_change_mode(ts, MIP4_BOOT_MODE_APP); +} + +static int mip4_bl_get_address(struct mip4_ts *ts, u16 *buf_addr) +{ + u8 cmd[] = { MIP4_R0_BOOT, MIP4_R1_BOOT_BUF_ADDR }; + u8 result[sizeof(u16)]; + struct i2c_msg msg[] = { + { + .addr = ts->client->addr, + .flags = 0, + .buf = cmd, + .len = sizeof(cmd), + }, { + .addr = ts->client->addr, + .flags = I2C_M_RD, + .buf = result, + .len = sizeof(result), + }, + }; + int ret; + int error; + + ret = i2c_transfer(ts->client->adapter, msg, ARRAY_SIZE(msg)); + if (ret != ARRAY_SIZE(msg)) { + error = ret < 0 ? ret : -EIO; + dev_err(&ts->client->dev, + "Failed to retrieve bootloader buffer address: %d\n", + error); + return error; + } + + *buf_addr = get_unaligned_le16(result); + dev_dbg(&ts->client->dev, + "Bootloader buffer address %#04x\n", *buf_addr); + + return 0; +} + +static int mip4_bl_program_page(struct mip4_ts *ts, int offset, + const u8 *data, int length, u16 buf_addr) +{ + u8 cmd[6]; + u8 *data_buf; + u16 buf_offset; + int ret; + int error; + + dev_dbg(&ts->client->dev, "Writing page @%#06x (%d)\n", + offset, length); + + if (length > MIP4_BL_PAGE_SIZE || length % MIP4_BL_PACKET_SIZE) { + dev_err(&ts->client->dev, + "Invalid page length: %d\n", length); + return -EINVAL; + } + + data_buf = kmalloc(2 + MIP4_BL_PACKET_SIZE, GFP_KERNEL); + if (!data_buf) + return -ENOMEM; + + /* Addr */ + cmd[0] = MIP4_R0_BOOT; + cmd[1] = MIP4_R1_BOOT_TARGET_ADDR; + put_unaligned_le32(offset, &cmd[2]); + ret = i2c_master_send(ts->client, cmd, 6); + if (ret != 6) { + error = ret < 0 ? ret : -EIO; + dev_err(&ts->client->dev, + "Failed to send write page address: %d\n", error); + goto out; + } + + /* Size */ + cmd[0] = MIP4_R0_BOOT; + cmd[1] = MIP4_R1_BOOT_SIZE; + put_unaligned_le32(length, &cmd[2]); + ret = i2c_master_send(ts->client, cmd, 6); + if (ret != 6) { + error = ret < 0 ? ret : -EIO; + dev_err(&ts->client->dev, + "Failed to send write page size: %d\n", error); + goto out; + } + + /* Data */ + for (buf_offset = 0; + buf_offset < length; + buf_offset += MIP4_BL_PACKET_SIZE) { + dev_dbg(&ts->client->dev, + "writing chunk at %#04x (size %d)\n", + buf_offset, MIP4_BL_PACKET_SIZE); + put_unaligned_be16(buf_addr + buf_offset, data_buf); + memcpy(&data_buf[2], &data[buf_offset], MIP4_BL_PACKET_SIZE); + ret = i2c_master_send(ts->client, + data_buf, 2 + MIP4_BL_PACKET_SIZE); + if (ret != 2 + MIP4_BL_PACKET_SIZE) { + error = ret < 0 ? ret : -EIO; + dev_err(&ts->client->dev, + "Failed to read chunk at %#04x (size %d): %d\n", + buf_offset, MIP4_BL_PACKET_SIZE, error); + goto out; + } + } + + /* Command */ + cmd[0] = MIP4_R0_BOOT; + cmd[1] = MIP4_R1_BOOT_CMD; + cmd[2] = MIP4_BOOT_CMD_PROGRAM; + ret = i2c_master_send(ts->client, cmd, 3); + if (ret != 3) { + error = ret < 0 ? ret : -EIO; + dev_err(&ts->client->dev, + "Failed to send 'write' command: %d\n", error); + goto out; + } + + /* Status */ + error = mip4_bl_read_status(ts); + +out: + kfree(data_buf); + return error ? error : 0; +} + +static int mip4_bl_verify_page(struct mip4_ts *ts, int offset, + const u8 *data, int length, int buf_addr) +{ + u8 cmd[8]; + u8 *read_buf; + int buf_offset; + struct i2c_msg msg[] = { + { + .addr = ts->client->addr, + .flags = 0, + .buf = cmd, + .len = 2, + }, { + .addr = ts->client->addr, + .flags = I2C_M_RD, + .len = MIP4_BL_PACKET_SIZE, + }, + }; + int ret; + int error; + + dev_dbg(&ts->client->dev, "Validating page @%#06x (%d)\n", + offset, length); + + /* Addr */ + cmd[0] = MIP4_R0_BOOT; + cmd[1] = MIP4_R1_BOOT_TARGET_ADDR; + put_unaligned_le32(offset, &cmd[2]); + ret = i2c_master_send(ts->client, cmd, 6); + if (ret != 6) { + error = ret < 0 ? ret : -EIO; + dev_err(&ts->client->dev, + "Failed to send read page address: %d\n", error); + return error; + } + + /* Size */ + cmd[0] = MIP4_R0_BOOT; + cmd[1] = MIP4_R1_BOOT_SIZE; + put_unaligned_le32(length, &cmd[2]); + ret = i2c_master_send(ts->client, cmd, 6); + if (ret != 6) { + error = ret < 0 ? ret : -EIO; + dev_err(&ts->client->dev, + "Failed to send read page size: %d\n", error); + return error; + } + + /* Command */ + cmd[0] = MIP4_R0_BOOT; + cmd[1] = MIP4_R1_BOOT_CMD; + cmd[2] = MIP4_BOOT_CMD_READ; + ret = i2c_master_send(ts->client, cmd, 3); + if (ret != 3) { + error = ret < 0 ? ret : -EIO; + dev_err(&ts->client->dev, + "Failed to send 'read' command: %d\n", error); + return error; + } + + /* Status */ + error = mip4_bl_read_status(ts); + if (error) + return error; + + /* Read */ + msg[1].buf = read_buf = kmalloc(MIP4_BL_PACKET_SIZE, GFP_KERNEL); + if (!read_buf) + return -ENOMEM; + + for (buf_offset = 0; + buf_offset < length; + buf_offset += MIP4_BL_PACKET_SIZE) { + dev_dbg(&ts->client->dev, + "reading chunk at %#04x (size %d)\n", + buf_offset, MIP4_BL_PACKET_SIZE); + put_unaligned_be16(buf_addr + buf_offset, cmd); + ret = i2c_transfer(ts->client->adapter, msg, ARRAY_SIZE(msg)); + if (ret != ARRAY_SIZE(msg)) { + error = ret < 0 ? ret : -EIO; + dev_err(&ts->client->dev, + "Failed to read chunk at %#04x (size %d): %d\n", + buf_offset, MIP4_BL_PACKET_SIZE, error); + break; + } + + if (memcmp(&data[buf_offset], read_buf, MIP4_BL_PACKET_SIZE)) { + dev_err(&ts->client->dev, + "Failed to validate chunk at %#04x (size %d)\n", + buf_offset, MIP4_BL_PACKET_SIZE); +#if MIP4_FW_UPDATE_DEBUG + print_hex_dump(KERN_DEBUG, + MIP4_DEVICE_NAME " F/W File: ", + DUMP_PREFIX_OFFSET, 16, 1, + data + offset, MIP4_BL_PACKET_SIZE, + false); + print_hex_dump(KERN_DEBUG, + MIP4_DEVICE_NAME " F/W Chip: ", + DUMP_PREFIX_OFFSET, 16, 1, + read_buf, MIP4_BL_PAGE_SIZE, false); +#endif + error = -EINVAL; + break; + } + } + + kfree(read_buf); + return error ? error : 0; +} + +/* + * Flash chip firmware + */ +static int mip4_flash_fw(struct mip4_ts *ts, + const u8 *fw_data, u32 fw_size, u32 fw_offset) +{ + struct i2c_client *client = ts->client; + int offset; + u16 buf_addr; + int error, error2; + + /* Enter bootloader mode */ + dev_dbg(&client->dev, "Entering bootloader mode\n"); + + error = mip4_bl_enter(ts); + if (error) { + dev_err(&client->dev, + "Failed to enter bootloader mode: %d\n", + error); + return error; + } + + /* Read info */ + error = mip4_bl_get_address(ts, &buf_addr); + if (error) + goto exit_bl; + + /* Program & Verify */ + dev_dbg(&client->dev, + "Program & Verify, page size: %d, packet size: %d\n", + MIP4_BL_PAGE_SIZE, MIP4_BL_PACKET_SIZE); + + for (offset = fw_offset; + offset < fw_offset + fw_size; + offset += MIP4_BL_PAGE_SIZE) { + /* Program */ + error = mip4_bl_program_page(ts, offset, fw_data + offset, + MIP4_BL_PAGE_SIZE, buf_addr); + if (error) + break; + + /* Verify */ + error = mip4_bl_verify_page(ts, offset, fw_data + offset, + MIP4_BL_PAGE_SIZE, buf_addr); + if (error) + break; + } + +exit_bl: + /* Exit bootloader mode */ + dev_dbg(&client->dev, "Exiting bootloader mode\n"); + + error2 = mip4_bl_exit(ts); + if (error2) { + dev_err(&client->dev, + "Failed to exit bootloader mode: %d\n", error2); + if (!error) + error = error2; + } + + /* Reset chip */ + mip4_power_off(ts); + mip4_power_on(ts); + + mip4_query_device(ts); + + /* Refresh device parameters */ + input_set_abs_params(ts->input, ABS_MT_POSITION_X, 0, ts->max_x, 0, 0); + input_set_abs_params(ts->input, ABS_MT_POSITION_Y, 0, ts->max_y, 0, 0); + input_set_abs_params(ts->input, ABS_X, 0, ts->max_x, 0, 0); + input_set_abs_params(ts->input, ABS_Y, 0, ts->max_y, 0, 0); + input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->ppm_x); + input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->ppm_y); + input_abs_set_res(ts->input, ABS_X, ts->ppm_x); + input_abs_set_res(ts->input, ABS_Y, ts->ppm_y); + + return error ? error : 0; +} + +static int mip4_parse_firmware(struct mip4_ts *ts, const struct firmware *fw, + u32 *fw_offset_start, u32 *fw_size, + const struct mip4_bin_tail **pfw_info) +{ + const struct mip4_bin_tail *fw_info; + struct mip4_fw_version fw_version; + u16 tail_size; + + if (fw->size < MIP4_BIN_TAIL_SIZE) { + dev_err(&ts->client->dev, + "Invalid firmware, size mismatch (tail %zd vs %zd)\n", + MIP4_BIN_TAIL_SIZE, fw->size); + return -EINVAL; + } + + fw_info = (const void *)&fw->data[fw->size - MIP4_BIN_TAIL_SIZE]; + +#if MIP4_FW_UPDATE_DEBUG + print_hex_dump(KERN_ERR, MIP4_DEVICE_NAME " Bin Info: ", + DUMP_PREFIX_OFFSET, 16, 1, *fw_info, tail_size, false); +#endif + + tail_size = get_unaligned_le16(&fw_info->tail_size); + if (tail_size != MIP4_BIN_TAIL_SIZE) { + dev_err(&ts->client->dev, + "wrong tail size: %d (expected %zd)\n", + tail_size, MIP4_BIN_TAIL_SIZE); + return -EINVAL; + } + + /* Check bin format */ + if (memcmp(fw_info->tail_mark, MIP4_BIN_TAIL_MARK, + sizeof(fw_info->tail_mark))) { + dev_err(&ts->client->dev, + "unable to locate tail marker (%*ph vs %*ph)\n", + (int)sizeof(fw_info->tail_mark), fw_info->tail_mark, + (int)sizeof(fw_info->tail_mark), MIP4_BIN_TAIL_MARK); + return -EINVAL; + } + + *fw_offset_start = get_unaligned_le32(&fw_info->bin_start_addr); + *fw_size = get_unaligned_le32(&fw_info->bin_length); + + dev_dbg(&ts->client->dev, + "F/W Data offset: %#08x, size: %d\n", + *fw_offset_start, *fw_size); + + if (*fw_size % MIP4_BL_PAGE_SIZE) { + dev_err(&ts->client->dev, + "encoded fw length %d is not multiple of pages (%d)\n", + *fw_size, MIP4_BL_PAGE_SIZE); + return -EINVAL; + } + + if (fw->size != *fw_offset_start + *fw_size) { + dev_err(&ts->client->dev, + "Wrong firmware size, expected %d bytes, got %zd\n", + *fw_offset_start + *fw_size, fw->size); + return -EINVAL; + } + + mip4_parse_fw_version((const u8 *)&fw_info->ver_boot, &fw_version); + + dev_dbg(&ts->client->dev, + "F/W file version %04X %04X %04X %04X\n", + fw_version.boot, fw_version.core, + fw_version.app, fw_version.param); + + dev_dbg(&ts->client->dev, "F/W chip version: %04X %04X %04X %04X\n", + ts->fw_version.boot, ts->fw_version.core, + ts->fw_version.app, ts->fw_version.param); + + /* Check F/W type */ + if (fw_version.boot != 0xEEEE && fw_version.boot != 0xFFFF && + fw_version.core == 0xEEEE && + fw_version.app == 0xEEEE && + fw_version.param == 0xEEEE) { + dev_dbg(&ts->client->dev, "F/W type: Bootloader\n"); + } else if (fw_version.boot == 0xEEEE && + fw_version.core != 0xEEEE && fw_version.core != 0xFFFF && + fw_version.app != 0xEEEE && fw_version.app != 0xFFFF && + fw_version.param != 0xEEEE && fw_version.param != 0xFFFF) { + dev_dbg(&ts->client->dev, "F/W type: Main\n"); + } else { + dev_err(&ts->client->dev, "Wrong firmware type\n"); + return -EINVAL; + } + + return 0; +} + +static int mip4_execute_fw_update(struct mip4_ts *ts, const struct firmware *fw) +{ + const struct mip4_bin_tail *fw_info; + u32 fw_start_offset; + u32 fw_size; + int retires = 3; + int error; + + error = mip4_parse_firmware(ts, fw, + &fw_start_offset, &fw_size, &fw_info); + if (error) + return error; + + if (ts->input->users) { + disable_irq(ts->client->irq); + } else { + error = mip4_power_on(ts); + if (error) + return error; + } + + /* Update firmware */ + do { + error = mip4_flash_fw(ts, fw->data, fw_size, fw_start_offset); + if (!error) + break; + } while (--retires); + + if (error) + dev_err(&ts->client->dev, + "Failed to flash firmware: %d\n", error); + + /* Enable IRQ */ + if (ts->input->users) + enable_irq(ts->client->irq); + else + mip4_power_off(ts); + + return error ? error : 0; +} + +static ssize_t mip4_sysfs_fw_update(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mip4_ts *ts = i2c_get_clientdata(client); + const struct firmware *fw; + int error; + + error = reject_firmware(&fw, MIP4_FW_NAME, dev); + if (error) { + dev_err(&ts->client->dev, + "Failed to retrieve firmware %s: %d\n", + MIP4_FW_NAME, error); + return error; + } + + /* + * Take input mutex to prevent racing with itself and also with + * userspace opening and closing the device and also suspend/resume + * transitions. + */ + mutex_lock(&ts->input->mutex); + + error = mip4_execute_fw_update(ts, fw); + + mutex_unlock(&ts->input->mutex); + + release_firmware(fw); + + if (error) { + dev_err(&ts->client->dev, + "Firmware update failed: %d\n", error); + return error; + } + + return count; +} + +static DEVICE_ATTR(update_fw, S_IWUSR, NULL, mip4_sysfs_fw_update); + +static ssize_t mip4_sysfs_read_fw_version(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mip4_ts *ts = i2c_get_clientdata(client); + size_t count; + + /* Take lock to prevent racing with firmware update */ + mutex_lock(&ts->input->mutex); + + count = snprintf(buf, PAGE_SIZE, "%04X %04X %04X %04X\n", + ts->fw_version.boot, ts->fw_version.core, + ts->fw_version.app, ts->fw_version.param); + + mutex_unlock(&ts->input->mutex); + + return count; +} + +static DEVICE_ATTR(fw_version, S_IRUGO, mip4_sysfs_read_fw_version, NULL); + +static ssize_t mip4_sysfs_read_hw_version(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mip4_ts *ts = i2c_get_clientdata(client); + size_t count; + + /* Take lock to prevent racing with firmware update */ + mutex_lock(&ts->input->mutex); + + /* + * product_name shows the name or version of the hardware + * paired with current firmware in the chip. + */ + count = snprintf(buf, PAGE_SIZE, "%.*s\n", + (int)sizeof(ts->product_name), ts->product_name); + + mutex_unlock(&ts->input->mutex); + + return count; +} + +static DEVICE_ATTR(hw_version, S_IRUGO, mip4_sysfs_read_hw_version, NULL); + +static struct attribute *mip4_attrs[] = { + &dev_attr_fw_version.attr, + &dev_attr_hw_version.attr, + &dev_attr_update_fw.attr, + NULL, +}; + +static const struct attribute_group mip4_attr_group = { + .attrs = mip4_attrs, +}; + +static void mip4_sysfs_remove(void *_data) +{ + struct mip4_ts *ts = _data; + + sysfs_remove_group(&ts->client->dev.kobj, &mip4_attr_group); +} + +static int mip4_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct mip4_ts *ts; + struct input_dev *input; + int error; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "Not supported I2C adapter\n"); + return -ENXIO; + } + + ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL); + if (!ts) + return -ENOMEM; + + input = devm_input_allocate_device(&client->dev); + if (!input) + return -ENOMEM; + + ts->client = client; + ts->input = input; + + snprintf(ts->phys, sizeof(ts->phys), + "%s/input0", dev_name(&client->dev)); + + ts->gpio_ce = devm_gpiod_get_optional(&client->dev, + "ce", GPIOD_OUT_LOW); + if (IS_ERR(ts->gpio_ce)) { + error = PTR_ERR(ts->gpio_ce); + if (error != EPROBE_DEFER) + dev_err(&client->dev, + "Failed to get gpio: %d\n", error); + return error; + } + + error = mip4_power_on(ts); + if (error) + return error; + error = mip4_query_device(ts); + mip4_power_off(ts); + if (error) + return error; + + input->name = "MELFAS MIP4 Touchscreen"; + input->phys = ts->phys; + + input->id.bustype = BUS_I2C; + input->id.vendor = 0x13c5; + + input->open = mip4_input_open; + input->close = mip4_input_close; + + input_set_drvdata(input, ts); + + input->keycode = ts->key_code; + input->keycodesize = sizeof(*ts->key_code); + input->keycodemax = ts->key_num; + + input_set_abs_params(input, ABS_MT_POSITION_X, 0, ts->max_x, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ts->max_y, 0, 0); + input_set_abs_params(input, ABS_MT_PRESSURE, + MIP4_PRESSURE_MIN, MIP4_PRESSURE_MAX, 0, 0); + input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, + MIP4_TOUCH_MAJOR_MIN, MIP4_TOUCH_MAJOR_MAX, 0, 0); + input_set_abs_params(input, ABS_MT_TOUCH_MINOR, + MIP4_TOUCH_MINOR_MIN, MIP4_TOUCH_MINOR_MAX, 0, 0); + input_abs_set_res(ts->input, ABS_MT_POSITION_X, ts->ppm_x); + input_abs_set_res(ts->input, ABS_MT_POSITION_Y, ts->ppm_y); + + error = input_mt_init_slots(input, MIP4_MAX_FINGERS, INPUT_MT_DIRECT); + if (error) + return error; + + i2c_set_clientdata(client, ts); + + error = devm_request_threaded_irq(&client->dev, client->irq, + NULL, mip4_interrupt, + IRQF_ONESHOT, MIP4_DEVICE_NAME, ts); + if (error) { + dev_err(&client->dev, + "Failed to request interrupt %d: %d\n", + client->irq, error); + return error; + } + + disable_irq(client->irq); + + error = input_register_device(input); + if (error) { + dev_err(&client->dev, + "Failed to register input device: %d\n", error); + return error; + } + + error = sysfs_create_group(&client->dev.kobj, &mip4_attr_group); + if (error) { + dev_err(&client->dev, + "Failed to create sysfs attribute group: %d\n", error); + return error; + } + + error = devm_add_action(&client->dev, mip4_sysfs_remove, ts); + if (error) { + mip4_sysfs_remove(ts); + dev_err(&client->dev, + "Failed to install sysfs remoce action: %d\n", error); + return error; + } + + return 0; +} + +static int __maybe_unused mip4_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mip4_ts *ts = i2c_get_clientdata(client); + struct input_dev *input = ts->input; + + mutex_lock(&input->mutex); + + if (device_may_wakeup(dev)) + ts->wake_irq_enabled = enable_irq_wake(client->irq) == 0; + else if (input->users) + mip4_disable(ts); + + mutex_unlock(&input->mutex); + + return 0; +} + +static int __maybe_unused mip4_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct mip4_ts *ts = i2c_get_clientdata(client); + struct input_dev *input = ts->input; + + mutex_lock(&input->mutex); + + if (ts->wake_irq_enabled) + disable_irq_wake(client->irq); + else if (input->users) + mip4_enable(ts); + + mutex_unlock(&input->mutex); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(mip4_pm_ops, mip4_suspend, mip4_resume); + +#ifdef CONFIG_OF +static const struct of_device_id mip4_of_match[] = { + { .compatible = "melfas,"MIP4_DEVICE_NAME, }, + { }, +}; +MODULE_DEVICE_TABLE(of, mip4_of_match); +#endif + +#ifdef CONFIG_ACPI +static const struct acpi_device_id mip4_acpi_match[] = { + { "MLFS0000", 0}, + { }, +}; +MODULE_DEVICE_TABLE(acpi, mip4_acpi_match); +#endif + +static const struct i2c_device_id mip4_i2c_ids[] = { + { MIP4_DEVICE_NAME, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, mip4_i2c_ids); + +static struct i2c_driver mip4_driver = { + .id_table = mip4_i2c_ids, + .probe = mip4_probe, + .driver = { + .name = MIP4_DEVICE_NAME, + .of_match_table = of_match_ptr(mip4_of_match), + .acpi_match_table = ACPI_PTR(mip4_acpi_match), + .pm = &mip4_pm_ops, + }, +}; +module_i2c_driver(mip4_driver); + +MODULE_DESCRIPTION("MELFAS MIP4 Touchscreen"); +MODULE_VERSION("2016.03.12"); +MODULE_AUTHOR("Sangwon Jee <jeesw@melfas.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/stmpe-ts.c b/drivers/input/touchscreen/stmpe-ts.c index e414d43e5..2a78e27b4 100644 --- a/drivers/input/touchscreen/stmpe-ts.c +++ b/drivers/input/touchscreen/stmpe-ts.c @@ -63,6 +63,37 @@ #define STMPE_TS_NAME "stmpe-ts" #define XY_MASK 0xfff +/** + * struct stmpe_touch - stmpe811 touch screen controller state + * @stmpe: pointer back to STMPE MFD container + * @idev: registered input device + * @work: a work item used to scan the device + * @dev: a pointer back to the MFD cell struct device* + * @sample_time: ADC converstion time in number of clock. + * (0 -> 36 clocks, 1 -> 44 clocks, 2 -> 56 clocks, 3 -> 64 clocks, + * 4 -> 80 clocks, 5 -> 96 clocks, 6 -> 144 clocks), + * recommended is 4. + * @mod_12b: ADC Bit mode (0 -> 10bit ADC, 1 -> 12bit ADC) + * @ref_sel: ADC reference source + * (0 -> internal reference, 1 -> external reference) + * @adc_freq: ADC Clock speed + * (0 -> 1.625 MHz, 1 -> 3.25 MHz, 2 || 3 -> 6.5 MHz) + * @ave_ctrl: Sample average control + * (0 -> 1 sample, 1 -> 2 samples, 2 -> 4 samples, 3 -> 8 samples) + * @touch_det_delay: Touch detect interrupt delay + * (0 -> 10 us, 1 -> 50 us, 2 -> 100 us, 3 -> 500 us, + * 4-> 1 ms, 5 -> 5 ms, 6 -> 10 ms, 7 -> 50 ms) + * recommended is 3 + * @settling: Panel driver settling time + * (0 -> 10 us, 1 -> 100 us, 2 -> 500 us, 3 -> 1 ms, + * 4 -> 5 ms, 5 -> 10 ms, 6 for 50 ms, 7 -> 100 ms) + * recommended is 2 + * @fraction_z: Length of the fractional part in z + * (fraction_z ([0..7]) = Count of the fractional part) + * recommended is 7 + * @i_drive: current limit value of the touchscreen drivers + * (0 -> 20 mA typical 35 mA max, 1 -> 50 mA typical 80 mA max) + */ struct stmpe_touch { struct stmpe *stmpe; struct input_dev *idev; diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c index b6c4d03de..880c40b23 100644 --- a/drivers/input/touchscreen/sur40.c +++ b/drivers/input/touchscreen/sur40.c @@ -197,28 +197,34 @@ static int sur40_command(struct sur40_state *dev, static int sur40_init(struct sur40_state *dev) { int result; - u8 buffer[24]; + u8 *buffer; + + buffer = kmalloc(24, GFP_KERNEL); + if (!buffer) { + result = -ENOMEM; + goto error; + } /* stupidly replay the original MS driver init sequence */ result = sur40_command(dev, SUR40_GET_VERSION, 0x00, buffer, 12); if (result < 0) - return result; + goto error; result = sur40_command(dev, SUR40_GET_VERSION, 0x01, buffer, 12); if (result < 0) - return result; + goto error; result = sur40_command(dev, SUR40_GET_VERSION, 0x02, buffer, 12); if (result < 0) - return result; + goto error; result = sur40_command(dev, SUR40_UNKNOWN2, 0x00, buffer, 24); if (result < 0) - return result; + goto error; result = sur40_command(dev, SUR40_UNKNOWN1, 0x00, buffer, 5); if (result < 0) - return result; + goto error; result = sur40_command(dev, SUR40_GET_VERSION, 0x03, buffer, 12); @@ -226,7 +232,8 @@ static int sur40_init(struct sur40_state *dev) * Discard the result buffer - no known data inside except * some version strings, maybe extract these sometime... */ - +error: + kfree(buffer); return result; } diff --git a/drivers/input/touchscreen/wdt87xx_i2c.c b/drivers/input/touchscreen/wdt87xx_i2c.c index 61353038c..c03f4104a 100644 --- a/drivers/input/touchscreen/wdt87xx_i2c.c +++ b/drivers/input/touchscreen/wdt87xx_i2c.c @@ -848,7 +848,7 @@ static int wdt87xx_do_update_firmware(struct i2c_client *client, error = wdt87xx_get_sysparam(client, &wdt->param); if (error) dev_err(&client->dev, - "failed to refresh system paramaters: %d\n", error); + "failed to refresh system parameters: %d\n", error); out: enable_irq(client->irq); mutex_unlock(&wdt->fw_mutex); |