diff options
Diffstat (limited to 'drivers')
30 files changed, 1805 insertions, 84 deletions
diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c index 8bf61d295ffd..1086800693b2 100644 --- a/drivers/hid/hid-debug.c +++ b/drivers/hid/hid-debug.c @@ -814,7 +814,7 @@ static const char *keys[KEY_MAX + 1] = { [KEY_DELETEFILE] = "DeleteFile", [KEY_XFER] = "X-fer", [KEY_PROG1] = "Prog1", [KEY_PROG2] = "Prog2", [KEY_WWW] = "WWW", [KEY_MSDOS] = "MSDOS", - [KEY_COFFEE] = "Coffee", [KEY_DIRECTION] = "Direction", + [KEY_COFFEE] = "Coffee", [KEY_ROTATE_DISPLAY] = "RotateDisplay", [KEY_CYCLEWINDOWS] = "CycleWindows", [KEY_MAIL] = "Mail", [KEY_BOOKMARKS] = "Bookmarks", [KEY_COMPUTER] = "Computer", [KEY_BACK] = "Back", [KEY_FORWARD] = "Forward", diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index a89ba7cb96f1..106fbac7f8c5 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -588,6 +588,16 @@ config KEYBOARD_DAVINCI To compile this driver as a module, choose M here: the module will be called davinci_keyscan. +config KEYBOARD_IPAQ_MICRO + tristate "Buttons on Micro SoC (iPaq h3100,h3600,h3700)" + depends on MFD_IPAQ_MICRO + help + Say Y to enable support for the buttons attached to + Micro peripheral controller on iPAQ h3100/h3600/h3700 + + To compile this driver as a module, choose M here: the + module will be called ipaq-micro-keys. + config KEYBOARD_OMAP tristate "TI OMAP keypad support" depends on ARCH_OMAP1 @@ -686,4 +696,15 @@ config KEYBOARD_CAP11XX To compile this driver as a module, choose M here: the module will be called cap11xx. +config KEYBOARD_BCM + tristate "Broadcom keypad driver" + depends on OF && HAVE_CLK + select INPUT_MATRIXKMAP + default ARCH_BCM_CYGNUS + help + Say Y here if you want to use Broadcom keypad. + + To compile this driver as a module, choose M here: the + module will be called bcm-keypad. + endif diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 470767884bd8..df28d5553c05 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_KEYBOARD_ADP5589) += adp5589-keys.o obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o +obj-$(CONFIG_KEYBOARD_BCM) += bcm-keypad.o obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o obj-$(CONFIG_KEYBOARD_CAP11XX) += cap11xx.o obj-$(CONFIG_KEYBOARD_CLPS711X) += clps711x-keypad.o @@ -23,6 +24,7 @@ obj-$(CONFIG_KEYBOARD_TCA6416) += tca6416-keypad.o obj-$(CONFIG_KEYBOARD_TCA8418) += tca8418_keypad.o obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o +obj-$(CONFIG_KEYBOARD_IPAQ_MICRO) += ipaq-micro-keys.o obj-$(CONFIG_KEYBOARD_IMX) += imx_keypad.o obj-$(CONFIG_KEYBOARD_HP6XX) += jornada680_kbd.o obj-$(CONFIG_KEYBOARD_HP7XX) += jornada720_kbd.o diff --git a/drivers/input/keyboard/bcm-keypad.c b/drivers/input/keyboard/bcm-keypad.c new file mode 100644 index 000000000000..86a8b723ae15 --- /dev/null +++ b/drivers/input/keyboard/bcm-keypad.c @@ -0,0 +1,458 @@ +/* + * Copyright (C) 2014 Broadcom Corporation + * + * 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 version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/bitops.h> +#include <linux/clk.h> +#include <linux/gfp.h> +#include <linux/io.h> +#include <linux/input.h> +#include <linux/input/matrix_keypad.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/stddef.h> +#include <linux/types.h> + +#define DEFAULT_CLK_HZ 31250 +#define MAX_ROWS 8 +#define MAX_COLS 8 + +/* Register/field definitions */ +#define KPCR_OFFSET 0x00000080 +#define KPCR_MODE 0x00000002 +#define KPCR_MODE_SHIFT 1 +#define KPCR_MODE_MASK 1 +#define KPCR_ENABLE 0x00000001 +#define KPCR_STATUSFILTERENABLE 0x00008000 +#define KPCR_STATUSFILTERTYPE_SHIFT 12 +#define KPCR_COLFILTERENABLE 0x00000800 +#define KPCR_COLFILTERTYPE_SHIFT 8 +#define KPCR_ROWWIDTH_SHIFT 20 +#define KPCR_COLUMNWIDTH_SHIFT 16 + +#define KPIOR_OFFSET 0x00000084 +#define KPIOR_ROWOCONTRL_SHIFT 24 +#define KPIOR_ROWOCONTRL_MASK 0xFF000000 +#define KPIOR_COLUMNOCONTRL_SHIFT 16 +#define KPIOR_COLUMNOCONTRL_MASK 0x00FF0000 +#define KPIOR_COLUMN_IO_DATA_SHIFT 0 + +#define KPEMR0_OFFSET 0x00000090 +#define KPEMR1_OFFSET 0x00000094 +#define KPEMR2_OFFSET 0x00000098 +#define KPEMR3_OFFSET 0x0000009C +#define KPEMR_EDGETYPE_BOTH 3 + +#define KPSSR0_OFFSET 0x000000A0 +#define KPSSR1_OFFSET 0x000000A4 +#define KPSSRN_OFFSET(reg_n) (KPSSR0_OFFSET + 4 * (reg_n)) +#define KPIMR0_OFFSET 0x000000B0 +#define KPIMR1_OFFSET 0x000000B4 +#define KPICR0_OFFSET 0x000000B8 +#define KPICR1_OFFSET 0x000000BC +#define KPICRN_OFFSET(reg_n) (KPICR0_OFFSET + 4 * (reg_n)) +#define KPISR0_OFFSET 0x000000C0 +#define KPISR1_OFFSET 0x000000C4 + +#define KPCR_STATUSFILTERTYPE_MAX 7 +#define KPCR_COLFILTERTYPE_MAX 7 + +/* Macros to determine the row/column from a bit that is set in SSR0/1. */ +#define BIT_TO_ROW_SSRN(bit_nr, reg_n) (((bit_nr) >> 3) + 4 * (reg_n)) +#define BIT_TO_COL(bit_nr) ((bit_nr) % 8) + +/* Structure representing various run-time entities */ +struct bcm_kp { + void __iomem *base; + int irq; + struct clk *clk; + struct input_dev *input_dev; + unsigned long last_state[2]; + unsigned int n_rows; + unsigned int n_cols; + u32 kpcr; + u32 kpior; + u32 kpemr; + u32 imr0_val; + u32 imr1_val; +}; + +/* + * Returns the keycode from the input device keymap given the row and + * column. + */ +static int bcm_kp_get_keycode(struct bcm_kp *kp, int row, int col) +{ + unsigned int row_shift = get_count_order(kp->n_cols); + unsigned short *keymap = kp->input_dev->keycode; + + return keymap[MATRIX_SCAN_CODE(row, col, row_shift)]; +} + +static void bcm_kp_report_keys(struct bcm_kp *kp, int reg_num, int pull_mode) +{ + unsigned long state, change; + int bit_nr; + int key_press; + int row, col; + unsigned int keycode; + + /* Clear interrupts */ + writel(0xFFFFFFFF, kp->base + KPICRN_OFFSET(reg_num)); + + state = readl(kp->base + KPSSRN_OFFSET(reg_num)); + change = kp->last_state[reg_num] ^ state; + kp->last_state[reg_num] = state; + + for_each_set_bit(bit_nr, &change, BITS_PER_LONG) { + key_press = state & BIT(bit_nr); + /* The meaning of SSR register depends on pull mode. */ + key_press = pull_mode ? !key_press : key_press; + row = BIT_TO_ROW_SSRN(bit_nr, reg_num); + col = BIT_TO_COL(bit_nr); + keycode = bcm_kp_get_keycode(kp, row, col); + input_report_key(kp->input_dev, keycode, key_press); + } +} + +static irqreturn_t bcm_kp_isr_thread(int irq, void *dev_id) +{ + struct bcm_kp *kp = dev_id; + int pull_mode = (kp->kpcr >> KPCR_MODE_SHIFT) & KPCR_MODE_MASK; + int reg_num; + + for (reg_num = 0; reg_num <= 1; reg_num++) + bcm_kp_report_keys(kp, reg_num, pull_mode); + + input_sync(kp->input_dev); + + return IRQ_HANDLED; +} + +static int bcm_kp_start(struct bcm_kp *kp) +{ + int error; + + if (kp->clk) { + error = clk_prepare_enable(kp->clk); + if (error) + return error; + } + + writel(kp->kpior, kp->base + KPIOR_OFFSET); + + writel(kp->imr0_val, kp->base + KPIMR0_OFFSET); + writel(kp->imr1_val, kp->base + KPIMR1_OFFSET); + + writel(kp->kpemr, kp->base + KPEMR0_OFFSET); + writel(kp->kpemr, kp->base + KPEMR1_OFFSET); + writel(kp->kpemr, kp->base + KPEMR2_OFFSET); + writel(kp->kpemr, kp->base + KPEMR3_OFFSET); + + writel(0xFFFFFFFF, kp->base + KPICR0_OFFSET); + writel(0xFFFFFFFF, kp->base + KPICR1_OFFSET); + + kp->last_state[0] = readl(kp->base + KPSSR0_OFFSET); + kp->last_state[0] = readl(kp->base + KPSSR1_OFFSET); + + writel(kp->kpcr | KPCR_ENABLE, kp->base + KPCR_OFFSET); + + return 0; +} + +static void bcm_kp_stop(const struct bcm_kp *kp) +{ + u32 val; + + val = readl(kp->base + KPCR_OFFSET); + val &= ~KPCR_ENABLE; + writel(0, kp->base + KPCR_OFFSET); + writel(0, kp->base + KPIMR0_OFFSET); + writel(0, kp->base + KPIMR1_OFFSET); + writel(0xFFFFFFFF, kp->base + KPICR0_OFFSET); + writel(0xFFFFFFFF, kp->base + KPICR1_OFFSET); + + if (kp->clk) + clk_disable_unprepare(kp->clk); +} + +static int bcm_kp_open(struct input_dev *dev) +{ + struct bcm_kp *kp = input_get_drvdata(dev); + + return bcm_kp_start(kp); +} + +static void bcm_kp_close(struct input_dev *dev) +{ + struct bcm_kp *kp = input_get_drvdata(dev); + + bcm_kp_stop(kp); +} + +static int bcm_kp_matrix_key_parse_dt(struct bcm_kp *kp) +{ + struct device *dev = kp->input_dev->dev.parent; + struct device_node *np = dev->of_node; + int error; + unsigned int dt_val; + unsigned int i; + unsigned int num_rows, col_mask, rows_set; + + /* Initialize the KPCR Keypad Configuration Register */ + kp->kpcr = KPCR_STATUSFILTERENABLE | KPCR_COLFILTERENABLE; + + error = matrix_keypad_parse_of_params(dev, &kp->n_rows, &kp->n_cols); + if (error) { + dev_err(dev, "failed to parse kp params\n"); + return error; + } + + /* Set row width for the ASIC block. */ + kp->kpcr |= (kp->n_rows - 1) << KPCR_ROWWIDTH_SHIFT; + + /* Set column width for the ASIC block. */ + kp->kpcr |= (kp->n_cols - 1) << KPCR_COLUMNWIDTH_SHIFT; + + /* Configure the IMR registers */ + + /* + * IMR registers contain interrupt enable bits for 8x8 matrix + * IMR0 register format: <row3> <row2> <row1> <row0> + * IMR1 register format: <row7> <row6> <row5> <row4> + */ + col_mask = (1 << (kp->n_cols)) - 1; + num_rows = kp->n_rows; + + /* Set column bits in rows 0 to 3 in IMR0 */ + kp->imr0_val = col_mask; + + rows_set = 1; + while (--num_rows && rows_set++ < 4) + kp->imr0_val |= kp->imr0_val << MAX_COLS; + + /* Set column bits in rows 4 to 7 in IMR1 */ + kp->imr1_val = 0; + if (num_rows) { + kp->imr1_val = col_mask; + while (--num_rows) + kp->imr1_val |= kp->imr1_val << MAX_COLS; + } + + /* Initialize the KPEMR Keypress Edge Mode Registers */ + /* Trigger on both edges */ + kp->kpemr = 0; + for (i = 0; i <= 30; i += 2) + kp->kpemr |= (KPEMR_EDGETYPE_BOTH << i); + + /* + * Obtain the Status filter debounce value and verify against the + * possible values specified in the DT binding. + */ + of_property_read_u32(np, "status-debounce-filter-period", &dt_val); + + if (dt_val > KPCR_STATUSFILTERTYPE_MAX) { + dev_err(dev, "Invalid Status filter debounce value %d\n", + dt_val); + return -EINVAL; + } + + kp->kpcr |= dt_val << KPCR_STATUSFILTERTYPE_SHIFT; + + /* + * Obtain the Column filter debounce value and verify against the + * possible values specified in the DT binding. + */ + of_property_read_u32(np, "col-debounce-filter-period", &dt_val); + + if (dt_val > KPCR_COLFILTERTYPE_MAX) { + dev_err(dev, "Invalid Column filter debounce value %d\n", + dt_val); + return -EINVAL; + } + + kp->kpcr |= dt_val << KPCR_COLFILTERTYPE_SHIFT; + + /* + * Determine between the row and column, + * which should be configured as output. + */ + if (of_property_read_bool(np, "row-output-enabled")) { + /* + * Set RowOContrl or ColumnOContrl in KPIOR + * to the number of pins to drive as outputs + */ + kp->kpior = ((1 << kp->n_rows) - 1) << + KPIOR_ROWOCONTRL_SHIFT; + } else { + kp->kpior = ((1 << kp->n_cols) - 1) << + KPIOR_COLUMNOCONTRL_SHIFT; + } + + /* + * Determine if the scan pull up needs to be enabled + */ + if (of_property_read_bool(np, "pull-up-enabled")) + kp->kpcr |= KPCR_MODE; + + dev_dbg(dev, "n_rows=%d n_col=%d kpcr=%x kpior=%x kpemr=%x\n", + kp->n_rows, kp->n_cols, + kp->kpcr, kp->kpior, kp->kpemr); + + return 0; +} + + +static int bcm_kp_probe(struct platform_device *pdev) +{ + struct bcm_kp *kp; + struct input_dev *input_dev; + struct resource *res; + int error; + + kp = devm_kzalloc(&pdev->dev, sizeof(*kp), GFP_KERNEL); + if (!kp) + return -ENOMEM; + + input_dev = devm_input_allocate_device(&pdev->dev); + if (!input_dev) { + dev_err(&pdev->dev, "failed to allocate the input device\n"); + return -ENOMEM; + } + + __set_bit(EV_KEY, input_dev->evbit); + + /* Enable auto repeat feature of Linux input subsystem */ + if (of_property_read_bool(pdev->dev.of_node, "autorepeat")) + __set_bit(EV_REP, input_dev->evbit); + + input_dev->name = pdev->name; + input_dev->phys = "keypad/input0"; + input_dev->dev.parent = &pdev->dev; + input_dev->open = bcm_kp_open; + input_dev->close = bcm_kp_close; + + input_dev->id.bustype = BUS_HOST; + input_dev->id.vendor = 0x0001; + input_dev->id.product = 0x0001; + input_dev->id.version = 0x0100; + + input_set_drvdata(input_dev, kp); + + kp->input_dev = input_dev; + + platform_set_drvdata(pdev, kp); + + error = bcm_kp_matrix_key_parse_dt(kp); + if (error) + return error; + + error = matrix_keypad_build_keymap(NULL, NULL, + kp->n_rows, kp->n_cols, + NULL, input_dev); + if (error) { + dev_err(&pdev->dev, "failed to build keymap\n"); + return error; + } + + /* Get the KEYPAD base address */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Missing keypad base address resource\n"); + return -ENODEV; + } + + kp->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(kp->base)) + return PTR_ERR(kp->base); + + /* Enable clock */ + kp->clk = devm_clk_get(&pdev->dev, "peri_clk"); + if (IS_ERR(kp->clk)) { + error = PTR_ERR(kp->clk); + if (error != -ENOENT) { + if (error != -EPROBE_DEFER) + dev_err(&pdev->dev, "Failed to get clock\n"); + return error; + } + dev_dbg(&pdev->dev, + "No clock specified. Assuming it's enabled\n"); + kp->clk = NULL; + } else { + unsigned int desired_rate; + long actual_rate; + + error = of_property_read_u32(pdev->dev.of_node, + "clock-frequency", &desired_rate); + if (error < 0) + desired_rate = DEFAULT_CLK_HZ; + + actual_rate = clk_round_rate(kp->clk, desired_rate); + if (actual_rate <= 0) + return -EINVAL; + + error = clk_set_rate(kp->clk, actual_rate); + if (error) + return error; + + error = clk_prepare_enable(kp->clk); + if (error) + return error; + } + + /* Put the kp into a known sane state */ + bcm_kp_stop(kp); + + kp->irq = platform_get_irq(pdev, 0); + if (kp->irq < 0) { + dev_err(&pdev->dev, "no IRQ specified\n"); + return -EINVAL; + } + + error = devm_request_threaded_irq(&pdev->dev, kp->irq, + NULL, bcm_kp_isr_thread, + IRQF_ONESHOT, pdev->name, kp); + if (error) { + dev_err(&pdev->dev, "failed to request IRQ\n"); + return error; + } + + error = input_register_device(input_dev); + if (error) { + dev_err(&pdev->dev, "failed to register input device\n"); + return error; + } + + return 0; +} + +static const struct of_device_id bcm_kp_of_match[] = { + { .compatible = "brcm,bcm-keypad" }, + { }, +}; +MODULE_DEVICE_TABLE(of, bcm_kp_of_match); + +static struct platform_driver bcm_kp_device_driver = { + .probe = bcm_kp_probe, + .driver = { + .name = "bcm-keypad", + .of_match_table = of_match_ptr(bcm_kp_of_match), + } +}; + +module_platform_driver(bcm_kp_device_driver); + +MODULE_AUTHOR("Broadcom Corporation"); +MODULE_DESCRIPTION("BCM Keypad Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/keyboard/ipaq-micro-keys.c b/drivers/input/keyboard/ipaq-micro-keys.c new file mode 100644 index 000000000000..602900d1f937 --- /dev/null +++ b/drivers/input/keyboard/ipaq-micro-keys.c @@ -0,0 +1,168 @@ +/* + * 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. + * + * h3600 atmel micro companion support, key subdevice + * based on previous kernel 2.4 version + * Author : Alessandro Gardich <gremlin@gremlin.it> + * Author : Linus Walleij <linus.walleij@linaro.org> + * + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/fs.h> +#include <linux/interrupt.h> +#include <linux/sched.h> +#include <linux/pm.h> +#include <linux/sysctl.h> +#include <linux/proc_fs.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/input.h> +#include <linux/platform_device.h> +#include <linux/mfd/ipaq-micro.h> + +struct ipaq_micro_keys { + struct ipaq_micro *micro; + struct input_dev *input; + u16 *codes; +}; + +static const u16 micro_keycodes[] = { + KEY_RECORD, /* 1: Record button */ + KEY_CALENDAR, /* 2: Calendar */ + KEY_ADDRESSBOOK, /* 3: Contacts (looks like Outlook) */ + KEY_MAIL, /* 4: Envelope (Q on older iPAQs) */ + KEY_HOMEPAGE, /* 5: Start (looks like swoopy arrow) */ + KEY_UP, /* 6: Up */ + KEY_RIGHT, /* 7: Right */ + KEY_LEFT, /* 8: Left */ + KEY_DOWN, /* 9: Down */ +}; + +static void micro_key_receive(void *data, int len, unsigned char *msg) +{ + struct ipaq_micro_keys *keys = data; + int key, down; + + down = 0x80 & msg[0]; + key = 0x7f & msg[0]; + + if (key < ARRAY_SIZE(micro_keycodes)) { + input_report_key(keys->input, keys->codes[key], down); + input_sync(keys->input); + } +} + +static void micro_key_start(struct ipaq_micro_keys *keys) +{ + spin_lock(&keys->micro->lock); + keys->micro->key = micro_key_receive; + keys->micro->key_data = keys; + spin_unlock(&keys->micro->lock); +} + +static void micro_key_stop(struct ipaq_micro_keys *keys) +{ + spin_lock(&keys->micro->lock); + keys->micro->key = NULL; + keys->micro->key_data = NULL; + spin_unlock(&keys->micro->lock); +} + +static int micro_key_open(struct input_dev *input) +{ + struct ipaq_micro_keys *keys = input_get_drvdata(input); + + micro_key_start(keys); + + return 0; +} + +static void micro_key_close(struct input_dev *input) +{ + struct ipaq_micro_keys *keys = input_get_drvdata(input); + + micro_key_stop(keys); +} + +static int micro_key_probe(struct platform_device *pdev) +{ + struct ipaq_micro_keys *keys; + int error; + int i; + + keys = devm_kzalloc(&pdev->dev, sizeof(*keys), GFP_KERNEL); + if (!keys) + return -ENOMEM; + + keys->micro = dev_get_drvdata(pdev->dev.parent); + + keys->input = devm_input_allocate_device(&pdev->dev); + if (!keys->input) + return -ENOMEM; + + keys->input->keycodesize = sizeof(micro_keycodes[0]); + keys->input->keycodemax = ARRAY_SIZE(micro_keycodes); + keys->codes = devm_kmemdup(&pdev->dev, micro_keycodes, + keys->input->keycodesize * keys->input->keycodemax, + GFP_KERNEL); + keys->input->keycode = keys->codes; + + __set_bit(EV_KEY, keys->input->evbit); + for (i = 0; i < ARRAY_SIZE(micro_keycodes); i++) + __set_bit(micro_keycodes[i], keys->input->keybit); + + keys->input->name = "h3600 micro keys"; + keys->input->open = micro_key_open; + keys->input->close = micro_key_close; + input_set_drvdata(keys->input, keys); + + error = input_register_device(keys->input); + if (error) + return error; + + platform_set_drvdata(pdev, keys); + return 0; +} + +static int __maybe_unused micro_key_suspend(struct device *dev) +{ + struct ipaq_micro_keys *keys = dev_get_drvdata(dev); + + micro_key_stop(keys); + + return 0; +} + +static int __maybe_unused micro_key_resume(struct device *dev) +{ + struct ipaq_micro_keys *keys = dev_get_drvdata(dev); + struct input_dev *input = keys->input; + + mutex_lock(&input->mutex); + + if (input->users) + micro_key_start(keys); + + mutex_unlock(&input->mutex); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(micro_key_dev_pm_ops, + micro_key_suspend, micro_key_resume); + +static struct platform_driver micro_key_device_driver = { + .driver = { + .name = "ipaq-micro-keys", + .pm = µ_key_dev_pm_ops, + }, + .probe = micro_key_probe, +}; +module_platform_driver(micro_key_device_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("driver for iPAQ Atmel micro keys"); +MODULE_ALIAS("platform:ipaq-micro-keys"); diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 6deb8dae3205..4436ab1b9735 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -115,6 +115,18 @@ config INPUT_PCSPKR To compile this driver as a module, choose M here: the module will be called pcspkr. +config INPUT_PM8941_PWRKEY + tristate "Qualcomm PM8941 power key support" + depends on MFD_SPMI_PMIC + help + Say Y here if you want support for the power key usually found + on boards using a Qualcomm PM8941 compatible PMIC. + + If unsure, say Y. + + To compile this driver as a module, choose M here: the module + will be called pm8941-pwrkey. + config INPUT_PM8XXX_VIBRATOR tristate "Qualcomm PM8XXX vibrator support" depends on MFD_PM8XXX @@ -165,6 +177,18 @@ config INPUT_MAX77693_HAPTIC To compile this driver as module, choose M here: the module will be called max77693-haptic. +config INPUT_MAX77843_HAPTIC + tristate "MAXIM MAX77843 haptic controller support" + depends on MFD_MAX77843 && REGULATOR + select INPUT_FF_MEMLESS + help + This option enables support for the haptic controller on + MAXIM MAX77843 chip. The driver supports ff-memless interface + from input framework. + + To compile this driver as module, choose M here: the + module will be called max77843-haptic. + config INPUT_MAX8925_ONKEY tristate "MAX8925 ONKEY support" depends on MFD_MAX8925 diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 403a1a54a76c..78ba4c1b8532 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o obj-$(CONFIG_INPUT_MAX77693_HAPTIC) += max77693-haptic.o +obj-$(CONFIG_INPUT_MAX77843_HAPTIC) += max77843-haptic.o obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o obj-$(CONFIG_INPUT_MAX8997_HAPTIC) += max8997_haptic.o obj-$(CONFIG_INPUT_MC13783_PWRBUTTON) += mc13783-pwrbutton.o @@ -49,6 +50,7 @@ obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o obj-$(CONFIG_INPUT_PCF8574) += pcf8574_keypad.o obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o +obj-$(CONFIG_INPUT_PM8941_PWRKEY) += pm8941-pwrkey.o obj-$(CONFIG_INPUT_PM8XXX_VIBRATOR) += pm8xxx-vibrator.o obj-$(CONFIG_INPUT_PMIC8XXX_PWRKEY) += pmic8xxx-pwrkey.o obj-$(CONFIG_INPUT_POWERMATE) += powermate.o diff --git a/drivers/input/misc/max77843-haptic.c b/drivers/input/misc/max77843-haptic.c new file mode 100644 index 000000000000..dccbb465a055 --- /dev/null +++ b/drivers/input/misc/max77843-haptic.c @@ -0,0 +1,358 @@ +/* + * MAXIM MAX77693 Haptic device driver + * + * Copyright (C) 2015 Samsung Electronics + * Author: Jaewon Kim <jaewon02.kim@samsung.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. + */ + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/mfd/max77843-private.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/pwm.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <linux/workqueue.h> + +#define MAX_MAGNITUDE_SHIFT 16 + +enum max77843_haptic_motor_type { + MAX77843_HAPTIC_ERM = 0, + MAX77843_HAPTIC_LRA, +}; + +enum max77843_haptic_pwm_divisor { + MAX77843_HAPTIC_PWM_DIVISOR_32 = 0, + MAX77843_HAPTIC_PWM_DIVISOR_64, + MAX77843_HAPTIC_PWM_DIVISOR_128, + MAX77843_HAPTIC_PWM_DIVISOR_256, +}; + +struct max77843_haptic { + struct regmap *regmap_haptic; + struct device *dev; + struct input_dev *input_dev; + struct pwm_device *pwm_dev; + struct regulator *motor_reg; + struct work_struct work; + struct mutex mutex; + + unsigned int magnitude; + unsigned int pwm_duty; + + bool active; + bool suspended; + + enum max77843_haptic_motor_type type; + enum max77843_haptic_pwm_divisor pwm_divisor; +}; + +static int max77843_haptic_set_duty_cycle(struct max77843_haptic *haptic) +{ + int delta = (haptic->pwm_dev->period + haptic->pwm_duty) / 2; + int error; + + error = pwm_config(haptic->pwm_dev, delta, haptic->pwm_dev->period); + if (error) { + dev_err(haptic->dev, "failed to configure pwm: %d\n", error); + return error; + } + + return 0; +} + +static int max77843_haptic_bias(struct max77843_haptic *haptic, bool on) +{ + int error; + + error = regmap_update_bits(haptic->regmap_haptic, + MAX77843_SYS_REG_MAINCTRL1, + MAX77843_MAINCTRL1_BIASEN_MASK, + on << MAINCTRL1_BIASEN_SHIFT); + if (error) { + dev_err(haptic->dev, "failed to %s bias: %d\n", + on ? "enable" : "disable", error); + return error; + } + + return 0; +} + +static int max77843_haptic_config(struct max77843_haptic *haptic, bool enable) +{ + unsigned int value; + int error; + + value = (haptic->type << MCONFIG_MODE_SHIFT) | + (enable << MCONFIG_MEN_SHIFT) | + (haptic->pwm_divisor << MCONFIG_PDIV_SHIFT); + + error = regmap_write(haptic->regmap_haptic, + MAX77843_HAP_REG_MCONFIG, value); + if (error) { + dev_err(haptic->dev, + "failed to update haptic config: %d\n", error); + return error; + } + + return 0; +} + +static int max77843_haptic_enable(struct max77843_haptic *haptic) +{ + int error; + + if (haptic->active) + return 0; + + error = pwm_enable(haptic->pwm_dev); + if (error) { + dev_err(haptic->dev, + "failed to enable pwm device: %d\n", error); + return error; + } + + error = max77843_haptic_config(haptic, true); + if (error) + goto err_config; + + haptic->active = true; + + return 0; + +err_config: + pwm_disable(haptic->pwm_dev); + + return error; +} + +static int max77843_haptic_disable(struct max77843_haptic *haptic) +{ + int error; + + if (!haptic->active) + return 0; + + error = max77843_haptic_config(haptic, false); + if (error) + return error; + + pwm_disable(haptic->pwm_dev); + + haptic->active = false; + + return 0; +} + +static void max77843_haptic_play_work(struct work_struct *work) +{ + struct max77843_haptic *haptic = + container_of(work, struct max77843_haptic, work); + int error; + + mutex_lock(&haptic->mutex); + + if (haptic->suspended) + goto out_unlock; + + if (haptic->magnitude) { + error = max77843_haptic_set_duty_cycle(haptic); + if (error) { + dev_err(haptic->dev, + "failed to set duty cycle: %d\n", error); + goto out_unlock; + } + + error = max77843_haptic_enable(haptic); + if (error) + dev_err(haptic->dev, + "cannot enable haptic: %d\n", error); + } else { + error = max77843_haptic_disable(haptic); + if (error) + dev_err(haptic->dev, + "cannot disable haptic: %d\n", error); + } + +out_unlock: + mutex_unlock(&haptic->mutex); +} + +static int max77843_haptic_play_effect(struct input_dev *dev, void *data, + struct ff_effect *effect) +{ + struct max77843_haptic *haptic = input_get_drvdata(dev); + u64 period_mag_multi; + + haptic->magnitude = effect->u.rumble.strong_magnitude; + if (!haptic->magnitude) + haptic->magnitude = effect->u.rumble.weak_magnitude; + + period_mag_multi = (u64)haptic->pwm_dev->period * haptic->magnitude; + haptic->pwm_duty = (unsigned int)(period_mag_multi >> + MAX_MAGNITUDE_SHIFT); + + schedule_work(&haptic->work); + + return 0; +} + +static int max77843_haptic_open(struct input_dev *dev) +{ + struct max77843_haptic *haptic = input_get_drvdata(dev); + int error; + + error = max77843_haptic_bias(haptic, true); + if (error) + return error; + + error = regulator_enable(haptic->motor_reg); + if (error) { + dev_err(haptic->dev, + "failed to enable regulator: %d\n", error); + return error; + } + + return 0; +} + +static void max77843_haptic_close(struct input_dev *dev) +{ + struct max77843_haptic *haptic = input_get_drvdata(dev); + int error; + + cancel_work_sync(&haptic->work); + max77843_haptic_disable(haptic); + + error = regulator_disable(haptic->motor_reg); + if (error) + dev_err(haptic->dev, + "failed to disable regulator: %d\n", error); + + max77843_haptic_bias(haptic, false); +} + +static int max77843_haptic_probe(struct platform_device *pdev) +{ + struct max77843 *max77843 = dev_get_drvdata(pdev->dev.parent); + struct max77843_haptic *haptic; + int error; + + haptic = devm_kzalloc(&pdev->dev, sizeof(*haptic), GFP_KERNEL); + if (!haptic) + return -ENOMEM; + + haptic->regmap_haptic = max77843->regmap; + haptic->dev = &pdev->dev; + haptic->type = MAX77843_HAPTIC_LRA; + haptic->pwm_divisor = MAX77843_HAPTIC_PWM_DIVISOR_128; + + INIT_WORK(&haptic->work, max77843_haptic_play_work); + mutex_init(&haptic->mutex); + + haptic->pwm_dev = devm_pwm_get(&pdev->dev, NULL); + if (IS_ERR(haptic->pwm_dev)) { + dev_err(&pdev->dev, "failed to get pwm device\n"); + return PTR_ERR(haptic->pwm_dev); + } + + haptic->motor_reg = devm_regulator_get_exclusive(&pdev->dev, "haptic"); + if (IS_ERR(haptic->motor_reg)) { + dev_err(&pdev->dev, "failed to get regulator\n"); + return PTR_ERR(haptic->motor_reg); + } + + haptic->input_dev = devm_input_allocate_device(&pdev->dev); + if (!haptic->input_dev) { + dev_err(&pdev->dev, "failed to allocate input device\n"); + return -ENOMEM; + } + + haptic->input_dev->name = "max77843-haptic"; + haptic->input_dev->id.version = 1; + haptic->input_dev->dev.parent = &pdev->dev; + haptic->input_dev->open = max77843_haptic_open; + haptic->input_dev->close = max77843_haptic_close; + input_set_drvdata(haptic->input_dev, haptic); + input_set_capability(haptic->input_dev, EV_FF, FF_RUMBLE); + + error = input_ff_create_memless(haptic->input_dev, NULL, + max77843_haptic_play_effect); + if (error) { + dev_err(&pdev->dev, "failed to create force-feedback\n"); + return error; + } + + error = input_register_device(haptic->input_dev); + if (error) { + dev_err(&pdev->dev, "failed to register input device\n"); + return error; + } + + platform_set_drvdata(pdev, haptic); + + return 0; +} + +static int __maybe_unused max77843_haptic_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct max77843_haptic *haptic = platform_get_drvdata(pdev); + int error; + + error = mutex_lock_interruptible(&haptic->mutex); + if (error) + return error; + + max77843_haptic_disable(haptic); + + haptic->suspended = true; + + mutex_unlock(&haptic->mutex); + + return 0; +} + +static int __maybe_unused max77843_haptic_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct max77843_haptic *haptic = platform_get_drvdata(pdev); + unsigned int magnitude; + + mutex_lock(&haptic->mutex); + + haptic->suspended = false; + + magnitude = ACCESS_ONCE(haptic->magnitude); + if (magnitude) + max77843_haptic_enable(haptic); + + mutex_unlock(&haptic->mutex); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(max77843_haptic_pm_ops, + max77843_haptic_suspend, max77843_haptic_resume); + +static struct platform_driver max77843_haptic_driver = { + .driver = { + .name = "max77843-haptic", + .pm = &max77843_haptic_pm_ops, + }, + .probe = max77843_haptic_probe, +}; +module_platform_driver(max77843_haptic_driver); + +MODULE_AUTHOR("Jaewon Kim <jaewon02.kim@samsung.com>"); +MODULE_DESCRIPTION("MAXIM MAX77843 Haptic driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/misc/palmas-pwrbutton.c b/drivers/input/misc/palmas-pwrbutton.c index 1f9b5ee92746..1e1baed63929 100644 --- a/drivers/input/misc/palmas-pwrbutton.c +++ b/drivers/input/misc/palmas-pwrbutton.c @@ -304,7 +304,7 @@ static SIMPLE_DEV_PM_OPS(palmas_pwron_pm, palmas_pwron_suspend, palmas_pwron_resume); #ifdef CONFIG_OF -static struct of_device_id of_palmas_pwr_match[] = { +static const struct of_device_id of_palmas_pwr_match[] = { { .compatible = "ti,palmas-pwrbutton" }, { }, }; diff --git a/drivers/input/misc/pm8941-pwrkey.c b/drivers/input/misc/pm8941-pwrkey.c new file mode 100644 index 000000000000..867db8a91372 --- /dev/null +++ b/drivers/input/misc/pm8941-pwrkey.c @@ -0,0 +1,293 @@ +/* + * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. + * Copyright (c) 2014, Sony Mobile Communications Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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/delay.h> +#include <linux/errno.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/log2.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/reboot.h> +#include <linux/regmap.h> + +#define PON_REV2 0x01 + +#define PON_RT_STS 0x10 +#define PON_KPDPWR_N_SET BIT(0) + +#define PON_PS_HOLD_RST_CTL 0x5a +#define PON_PS_HOLD_RST_CTL2 0x5b +#define PON_PS_HOLD_ENABLE BIT(7) +#define PON_PS_HOLD_TYPE_MASK 0x0f +#define PON_PS_HOLD_TYPE_SHUTDOWN 4 +#define PON_PS_HOLD_TYPE_HARD_RESET 7 + +#define PON_PULL_CTL 0x70 +#define PON_KPDPWR_PULL_UP BIT(1) + +#define PON_DBC_CTL 0x71 +#define PON_DBC_DELAY_MASK 0x7 + + +struct pm8941_pwrkey { + struct device *dev; + int irq; + u32 baseaddr; + struct regmap *regmap; + struct input_dev *input; + + unsigned int revision; + struct notifier_block reboot_notifier; +}; + +static int pm8941_reboot_notify(struct notifier_block *nb, + unsigned long code, void *unused) +{ + struct pm8941_pwrkey *pwrkey = container_of(nb, struct pm8941_pwrkey, + reboot_notifier); + unsigned int enable_reg; + unsigned int reset_type; + int error; + + /* PMICs with revision 0 have the enable bit in same register as ctrl */ + if (pwrkey->revision == 0) + enable_reg = PON_PS_HOLD_RST_CTL; + else + enable_reg = PON_PS_HOLD_RST_CTL2; + + error = regmap_update_bits(pwrkey->regmap, + pwrkey->baseaddr + enable_reg, + PON_PS_HOLD_ENABLE, + 0); + if (error) + dev_err(pwrkey->dev, + "unable to clear ps hold reset enable: %d\n", + error); + + /* + * Updates of PON_PS_HOLD_ENABLE requires 3 sleep cycles between + * writes. + */ + usleep_range(100, 1000); + + switch (code) { + case SYS_HALT: + case SYS_POWER_OFF: + reset_type = PON_PS_HOLD_TYPE_SHUTDOWN; + break; + case SYS_RESTART: + default: + reset_type = PON_PS_HOLD_TYPE_HARD_RESET; + break; + }; + + error = regmap_update_bits(pwrkey->regmap, + pwrkey->baseaddr + PON_PS_HOLD_RST_CTL, + PON_PS_HOLD_TYPE_MASK, + reset_type); + if (error) + dev_err(pwrkey->dev, "unable to set ps hold reset type: %d\n", + error); + + error = regmap_update_bits(pwrkey->regmap, + pwrkey->baseaddr + enable_reg, + PON_PS_HOLD_ENABLE, + PON_PS_HOLD_ENABLE); + if (error) + dev_err(pwrkey->dev, "unable to re-set enable: %d\n", error); + + return NOTIFY_DONE; +} + +static irqreturn_t pm8941_pwrkey_irq(int irq, void *_data) +{ + struct pm8941_pwrkey *pwrkey = _data; + unsigned int sts; + int error; + + error = regmap_read(pwrkey->regmap, + pwrkey->baseaddr + PON_RT_STS, &sts); + if (error) + return IRQ_HANDLED; + + input_report_key(pwrkey->input, KEY_POWER, !!(sts & PON_KPDPWR_N_SET)); + input_sync(pwrkey->input); + + return IRQ_HANDLED; +} + +static int __maybe_unused pm8941_pwrkey_suspend(struct device *dev) +{ + struct pm8941_pwrkey *pwrkey = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + enable_irq_wake(pwrkey->irq); + + return 0; +} + +static int __maybe_unused pm8941_pwrkey_resume(struct device *dev) +{ + struct pm8941_pwrkey *pwrkey = dev_get_drvdata(dev); + + if (device_may_wakeup(dev)) + disable_irq_wake(pwrkey->irq); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(pm8941_pwr_key_pm_ops, + pm8941_pwrkey_suspend, pm8941_pwrkey_resume); + +static int pm8941_pwrkey_probe(struct platform_device *pdev) +{ + struct pm8941_pwrkey *pwrkey; + bool pull_up; + u32 req_delay; + int error; + + if (of_property_read_u32(pdev->dev.of_node, "debounce", &req_delay)) + req_delay = 15625; + + if (req_delay > 2000000 || req_delay == 0) { + dev_err(&pdev->dev, "invalid debounce time: %u\n", req_delay); + return -EINVAL; + } + + pull_up = of_property_read_bool(pdev->dev.of_node, "bias-pull-up"); + + pwrkey = devm_kzalloc(&pdev->dev, sizeof(*pwrkey), GFP_KERNEL); + if (!pwrkey) + return -ENOMEM; + + pwrkey->dev = &pdev->dev; + + pwrkey->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!pwrkey->regmap) { + dev_err(&pdev->dev, "failed to locate regmap\n"); + return -ENODEV; + } + + pwrkey->irq = platform_get_irq(pdev, 0); + if (pwrkey->irq < 0) { + dev_err(&pdev->dev, "failed to get irq\n"); + return pwrkey->irq; + } + + error = of_property_read_u32(pdev->dev.of_node, "reg", + &pwrkey->baseaddr); + if (error) + return error; + + error = regmap_read(pwrkey->regmap, pwrkey->baseaddr + PON_REV2, + &pwrkey->revision); + if (error) { + dev_err(&pdev->dev, "failed to set debounce: %d\n", error); + return error; + } + + pwrkey->input = devm_input_allocate_device(&pdev->dev); + if (!pwrkey->input) { + dev_dbg(&pdev->dev, "unable to allocate input device\n"); + return -ENOMEM; + } + + input_set_capability(pwrkey->input, EV_KEY, KEY_POWER); + + pwrkey->input->name = "pm8941_pwrkey"; + pwrkey->input->phys = "pm8941_pwrkey/input0"; + + req_delay = (req_delay << 6) / USEC_PER_SEC; + req_delay = ilog2(req_delay); + + error = regmap_update_bits(pwrkey->regmap, + pwrkey->baseaddr + PON_DBC_CTL, + PON_DBC_DELAY_MASK, + req_delay); + if (error) { + dev_err(&pdev->dev, "failed to set debounce: %d\n", error); + return error; + } + + error = regmap_update_bits(pwrkey->regmap, + pwrkey->baseaddr + PON_PULL_CTL, + PON_KPDPWR_PULL_UP, + pull_up ? PON_KPDPWR_PULL_UP : 0); + if (error) { + dev_err(&pdev->dev, "failed to set pull: %d\n", error); + return error; + } + + error = devm_request_threaded_irq(&pdev->dev, pwrkey->irq, + NULL, pm8941_pwrkey_irq, + IRQF_ONESHOT, + "pm8941_pwrkey", pwrkey); + if (error) { + dev_err(&pdev->dev, "failed requesting IRQ: %d\n", error); + return error; + } + + error = input_register_device(pwrkey->input); + if (error) { + dev_err(&pdev->dev, "failed to register input device: %d\n", + error); + return error; + } + + pwrkey->reboot_notifier.notifier_call = pm8941_reboot_notify, + error = register_reboot_notifier(&pwrkey->reboot_notifier); + if (error) { + dev_err(&pdev->dev, "failed to register reboot notifier: %d\n", + error); + return error; + } + + platform_set_drvdata(pdev, pwrkey); + device_init_wakeup(&pdev->dev, 1); + + return 0; +} + +static int pm8941_pwrkey_remove(struct platform_device *pdev) +{ + struct pm8941_pwrkey *pwrkey = platform_get_drvdata(pdev); + + device_init_wakeup(&pdev->dev, 0); + unregister_reboot_notifier(&pwrkey->reboot_notifier); + + return 0; +} + +static const struct of_device_id pm8941_pwr_key_id_table[] = { + { .compatible = "qcom,pm8941-pwrkey" }, + { } +}; +MODULE_DEVICE_TABLE(of, pm8941_pwr_key_id_table); + +static struct platform_driver pm8941_pwrkey_driver = { + .probe = pm8941_pwrkey_probe, + .remove = pm8941_pwrkey_remove, + .driver = { + .name = "pm8941-pwrkey", + .pm = &pm8941_pwr_key_pm_ops, + .of_match_table = of_match_ptr(pm8941_pwr_key_id_table), + }, +}; +module_platform_driver(pm8941_pwrkey_driver); + +MODULE_DESCRIPTION("PM8941 Power Key driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/input/misc/pwm-beeper.c b/drivers/input/misc/pwm-beeper.c index a28ee70ff158..e82edf810d1f 100644 --- a/drivers/input/misc/pwm-beeper.c +++ b/drivers/input/misc/pwm-beeper.c @@ -50,7 +50,6 @@ static int pwm_beeper_event(struct input_dev *input, } if (value == 0) { - pwm_config(beeper->pwm, 0, 0); pwm_disable(beeper->pwm); } else { period = HZ_TO_NANOSECONDS(value); @@ -169,12 +168,6 @@ static int __maybe_unused pwm_beeper_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(pwm_beeper_pm_ops, pwm_beeper_suspend, pwm_beeper_resume); -#ifdef CONFIG_PM_SLEEP -#define PWM_BEEPER_PM_OPS (&pwm_beeper_pm_ops) -#else -#define PWM_BEEPER_PM_OPS NULL -#endif - #ifdef CONFIG_OF static const struct of_device_id pwm_beeper_match[] = { { .compatible = "pwm-beeper", }, @@ -187,7 +180,7 @@ static struct platform_driver pwm_beeper_driver = { .remove = pwm_beeper_remove, .driver = { .name = "pwm-beeper", - .pm = PWM_BEEPER_PM_OPS, + .pm = &pwm_beeper_pm_ops, .of_match_table = of_match_ptr(pwm_beeper_match), }, }; diff --git a/drivers/input/misc/regulator-haptic.c b/drivers/input/misc/regulator-haptic.c index 132eb914ea3e..6bf3f1082f71 100644 --- a/drivers/input/misc/regulator-haptic.c +++ b/drivers/input/misc/regulator-haptic.c @@ -245,7 +245,7 @@ static int __maybe_unused regulator_haptic_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(regulator_haptic_pm_ops, regulator_haptic_suspend, regulator_haptic_resume); -static struct of_device_id regulator_haptic_dt_match[] = { +static const struct of_device_id regulator_haptic_dt_match[] = { { .compatible = "regulator-haptic" }, { /* sentinel */ }, }; diff --git a/drivers/input/misc/tps65218-pwrbutton.c b/drivers/input/misc/tps65218-pwrbutton.c index 54508dec4eb3..a39b62651a4b 100644 --- a/drivers/input/misc/tps65218-pwrbutton.c +++ b/drivers/input/misc/tps65218-pwrbutton.c @@ -106,7 +106,7 @@ static int tps65218_pwron_probe(struct platform_device *pdev) return 0; } -static struct of_device_id of_tps65218_pwr_match[] = { +static const struct of_device_id of_tps65218_pwr_match[] = { { .compatible = "ti,tps65218-pwrbutton" }, { }, }; diff --git a/drivers/input/mouse/elan_i2c.h b/drivers/input/mouse/elan_i2c.h index e100c1b31597..9b2dc015f20c 100644 --- a/drivers/input/mouse/elan_i2c.h +++ b/drivers/input/mouse/elan_i2c.h @@ -17,7 +17,7 @@ */ #ifndef _ELAN_I2C_H -#define _ELAN_i2C_H +#define _ELAN_I2C_H #include <linux/types.h> diff --git a/drivers/input/mouse/elan_i2c_core.c b/drivers/input/mouse/elan_i2c_core.c index 7ce8bfe22d7e..375d98f47483 100644 --- a/drivers/input/mouse/elan_i2c_core.c +++ b/drivers/input/mouse/elan_i2c_core.c @@ -99,7 +99,7 @@ static int elan_enable_power(struct elan_tp_data *data) error = regulator_enable(data->vcc); if (error) { dev_err(&data->client->dev, - "Failed to enable regulator: %d\n", error); + "failed to enable regulator: %d\n", error); return error; } @@ -111,6 +111,7 @@ static int elan_enable_power(struct elan_tp_data *data) msleep(30); } while (--repeat > 0); + dev_err(&data->client->dev, "failed to enable power: %d\n", error); return error; } @@ -125,7 +126,7 @@ static int elan_disable_power(struct elan_tp_data *data) error = regulator_disable(data->vcc); if (error) { dev_err(&data->client->dev, - "Failed to disable regulator: %d\n", + "failed to disable regulator: %d\n", error); /* Attempt to power the chip back up */ data->ops->power_control(data->client, true); @@ -138,6 +139,7 @@ static int elan_disable_power(struct elan_tp_data *data) msleep(30); } while (--repeat > 0); + dev_err(&data->client->dev, "failed to disable power: %d\n", error); return error; } @@ -196,7 +198,6 @@ static int elan_initialize(struct elan_tp_data *data) if (!error) return 0; - repeat--; msleep(30); } while (--repeat > 0); @@ -1084,16 +1085,18 @@ static int __maybe_unused elan_resume(struct device *dev) } error = elan_enable_power(data); - if (error) + if (error) { dev_err(dev, "power up when resuming failed: %d\n", error); + goto err; + } error = elan_initialize(data); if (error) dev_err(dev, "initialize when resuming failed: %d\n", error); +err: enable_irq(data->client->irq); - - return 0; + return error; } static SIMPLE_DEV_PM_OPS(elan_pm_ops, elan_suspend, elan_resume); diff --git a/drivers/input/mouse/elan_i2c_i2c.c b/drivers/input/mouse/elan_i2c_i2c.c index 029941f861af..6cf0def6d35e 100644 --- a/drivers/input/mouse/elan_i2c_i2c.c +++ b/drivers/input/mouse/elan_i2c_i2c.c @@ -117,7 +117,15 @@ static int elan_i2c_write_cmd(struct i2c_client *client, u16 reg, u16 cmd) int ret; ret = i2c_transfer(client->adapter, &msg, 1); - return ret == 1 ? 0 : (ret < 0 ? ret : -EIO); + if (ret != 1) { + if (ret >= 0) + ret = -EIO; + dev_err(&client->dev, "writing cmd (0x%04x) failed: %d\n", + reg, ret); + return ret; + } + + return 0; } static int elan_i2c_initialize(struct i2c_client *client) diff --git a/drivers/input/mouse/lifebook.c b/drivers/input/mouse/lifebook.c index 23222dd5a66f..e5ed216824e9 100644 --- a/drivers/input/mouse/lifebook.c +++ b/drivers/input/mouse/lifebook.c @@ -256,8 +256,8 @@ static void lifebook_disconnect(struct psmouse *psmouse) int lifebook_detect(struct psmouse *psmouse, bool set_properties) { - if (!lifebook_present) - return -1; + if (!lifebook_present) + return -1; if (desired_serio_phys && strcmp(psmouse->ps2dev.serio->phys, desired_serio_phys)) @@ -268,7 +268,7 @@ int lifebook_detect(struct psmouse *psmouse, bool set_properties) psmouse->name = "Lifebook TouchScreen"; } - return 0; + return 0; } static int lifebook_create_relative_device(struct psmouse *psmouse) diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index 8bc61237bc1b..27057df7ba74 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -474,19 +474,45 @@ static int psmouse_poll(struct psmouse *psmouse) PSMOUSE_CMD_POLL | (psmouse->pktsize << 8)); } +static bool psmouse_check_pnp_id(const char *id, const char * const ids[]) +{ + int i; + + for (i = 0; ids[i]; i++) + if (!strcasecmp(id, ids[i])) + return true; + + return false; +} + /* * psmouse_matches_pnp_id - check if psmouse matches one of the passed in ids. */ bool psmouse_matches_pnp_id(struct psmouse *psmouse, const char * const ids[]) { - int i; - - if (!strncmp(psmouse->ps2dev.serio->firmware_id, "PNP:", 4)) - for (i = 0; ids[i]; i++) - if (strstr(psmouse->ps2dev.serio->firmware_id, ids[i])) - return true; + struct serio *serio = psmouse->ps2dev.serio; + char *p, *fw_id_copy, *save_ptr; + bool found = false; + + if (strncmp(serio->firmware_id, "PNP: ", 5)) + return false; + + fw_id_copy = kstrndup(&serio->firmware_id[5], + sizeof(serio->firmware_id) - 5, + GFP_KERNEL); + if (!fw_id_copy) + return false; + + save_ptr = fw_id_copy; + while ((p = strsep(&fw_id_copy, " ")) != NULL) { + if (psmouse_check_pnp_id(p, ids)) { + found = true; + break; + } + } - return false; + kfree(save_ptr); + return found; } /* diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c index dda605836546..d73a94270211 100644 --- a/drivers/input/mouse/synaptics.c +++ b/drivers/input/mouse/synaptics.c @@ -198,6 +198,13 @@ static const char * const topbuttonpad_pnp_ids[] = { NULL }; +/* This list has been kindly provided by Synaptics. */ +static const char * const forcepad_pnp_ids[] = { + "SYN300D", + "SYN3014", + NULL +}; + /***************************************************************************** * Synaptics communications functions ****************************************************************************/ @@ -682,8 +689,6 @@ static void synaptics_parse_ext_buttons(const unsigned char buf[], hw->ext_buttons |= (buf[5] & ext_mask) << ext_bits; } -static bool is_forcepad; - static int synaptics_parse_hw_state(const unsigned char buf[], struct synaptics_data *priv, struct synaptics_hw_state *hw) @@ -713,7 +718,7 @@ static int synaptics_parse_hw_state(const unsigned char buf[], hw->left = (buf[0] & 0x01) ? 1 : 0; hw->right = (buf[0] & 0x02) ? 1 : 0; - if (is_forcepad) { + if (priv->is_forcepad) { /* * ForcePads, like Clickpads, use middle button * bits to report primary button clicks. @@ -1413,29 +1418,11 @@ static const struct dmi_system_id __initconst cr48_dmi_table[] = { { } }; -static const struct dmi_system_id forcepad_dmi_table[] __initconst = { -#if defined(CONFIG_DMI) && defined(CONFIG_X86) - { - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), - DMI_MATCH(DMI_PRODUCT_NAME, "HP EliteBook Folio 1040 G1"), - }, - }, -#endif - { } -}; - void __init synaptics_module_init(void) { impaired_toshiba_kbc = dmi_check_system(toshiba_dmi_table); broken_olpc_ec = dmi_check_system(olpc_dmi_table); cr48_profile_sensor = dmi_check_system(cr48_dmi_table); - - /* - * Unfortunately ForcePad capability is not exported over PS/2, - * so we have to resort to checking DMI. - */ - is_forcepad = dmi_check_system(forcepad_dmi_table); } static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode) @@ -1470,6 +1457,12 @@ static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode) if (SYN_ID_DISGEST_SUPPORTED(priv->identity)) priv->disable_gesture = true; + /* + * Unfortunately ForcePad capability is not exported over PS/2, + * so we have to resort to checking PNP IDs. + */ + priv->is_forcepad = psmouse_matches_pnp_id(psmouse, forcepad_pnp_ids); + if (synaptics_set_mode(psmouse)) { psmouse_err(psmouse, "Unable to initialize device.\n"); goto init_fail; diff --git a/drivers/input/mouse/synaptics.h b/drivers/input/mouse/synaptics.h index ee4bd0d12b26..56faa7ec4434 100644 --- a/drivers/input/mouse/synaptics.h +++ b/drivers/input/mouse/synaptics.h @@ -196,6 +196,7 @@ struct synaptics_data { unsigned long press_start; bool press; bool report_press; + bool is_forcepad; }; void synaptics_module_init(void); diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c index 986a71c614b0..cb5ece77fd7d 100644 --- a/drivers/input/serio/i8042.c +++ b/drivers/input/serio/i8042.c @@ -1162,13 +1162,32 @@ static int i8042_controller_resume(bool force_reset) static int i8042_pm_suspend(struct device *dev) { + int i; + i8042_controller_reset(true); + /* Set up serio interrupts for system wakeup. */ + for (i = 0; i < I8042_NUM_PORTS; i++) { + struct serio *serio = i8042_ports[i].serio; + + if (serio && device_may_wakeup(&serio->dev)) + enable_irq_wake(i8042_ports[i].irq); + } + return 0; } static int i8042_pm_resume(struct device *dev) { + int i; + + for (i = 0; i < I8042_NUM_PORTS; i++) { + struct serio *serio = i8042_ports[i].serio; + + if (serio && device_may_wakeup(&serio->dev)) + disable_irq_wake(i8042_ports[i].irq); + } + /* * On resume from S2R we always try to reset the controller * to bring it in a sane state. (In case of S2D we expect @@ -1300,13 +1319,16 @@ static void __init i8042_register_ports(void) int i; for (i = 0; i < I8042_NUM_PORTS; i++) { - if (i8042_ports[i].serio) { + struct serio *serio = i8042_ports[i].serio; + + if (serio) { printk(KERN_INFO "serio: %s at %#lx,%#lx irq %d\n", - i8042_ports[i].serio->name, + serio->name, (unsigned long) I8042_DATA_REG, (unsigned long) I8042_COMMAND_REG, i8042_ports[i].irq); - serio_register_port(i8042_ports[i].serio); + serio_register_port(serio); + device_set_wakeup_capable(&serio->dev, true); } } } diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 6261fd6d7c3c..1130c4059104 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -297,11 +297,12 @@ config TOUCHSCREEN_FUJITSU config TOUCHSCREEN_GOODIX tristate "Goodix I2C touchscreen" - depends on I2C && ACPI + depends on I2C help Say Y here if you have the Goodix touchscreen (such as one installed in Onda v975w tablets) connected to your - system. + system. It also supports 5-finger chip models, which can be + found on ARM tablets, like Wexler TAB7200 and MSI Primo73. If unsure, say N. @@ -962,6 +963,17 @@ config TOUCHSCREEN_SUR40 To compile this driver as a module, choose M here: the module will be called sur40. +config TOUCHSCREEN_SX8654 + tristate "Semtech SX8654 touchscreen" + depends on I2C + help + Say Y here if you have a Semtech SX8654 touchscreen controller. + + If unsure, say N + + To compile this driver as a module, choose M here: the + module will be called sx8654. + config TOUCHSCREEN_TPS6507X tristate "TPS6507x based touchscreens" depends on I2C diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 0242fea2102a..a06a752966fe 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -79,5 +79,6 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_ATMEL) += atmel-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o +obj-$(CONFIG_TOUCHSCREEN_SX8654) += sx8654.o obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o obj-$(CONFIG_TOUCHSCREEN_ZFORCE) += zforce_ts.o diff --git a/drivers/input/touchscreen/ar1021_i2c.c b/drivers/input/touchscreen/ar1021_i2c.c index ba30578e296e..f0b954d46a25 100644 --- a/drivers/input/touchscreen/ar1021_i2c.c +++ b/drivers/input/touchscreen/ar1021_i2c.c @@ -157,7 +157,7 @@ static const struct i2c_device_id ar1021_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, ar1021_i2c_id); -static struct of_device_id ar1021_i2c_of_match[] = { +static const struct of_device_id ar1021_i2c_of_match[] = { { .compatible = "microchip,ar1021-i2c", }, { } }; diff --git a/drivers/input/touchscreen/elants_i2c.c b/drivers/input/touchscreen/elants_i2c.c index 926c58e540c0..43b3c9c2d788 100644 --- a/drivers/input/touchscreen/elants_i2c.c +++ b/drivers/input/touchscreen/elants_i2c.c @@ -98,7 +98,6 @@ #define MAX_FW_UPDATE_RETRIES 30 #define ELAN_FW_PAGESIZE 132 -#define ELAN_FW_FILENAME "elants_i2c.bin" /* calibration timeout definition */ #define ELAN_CALI_TIMEOUT_MSEC 10000 @@ -697,12 +696,19 @@ static int elants_i2c_fw_update(struct elants_data *ts) { struct i2c_client *client = ts->client; const struct firmware *fw; + char *fw_name; int error; - error = request_firmware(&fw, ELAN_FW_FILENAME, &client->dev); + fw_name = kasprintf(GFP_KERNEL, "elants_i2c_%4x.bin", ts->hw_version); + if (!fw_name) + return -ENOMEM; + + dev_info(&client->dev, "requesting fw name = %s\n", fw_name); + error = request_firmware(&fw, fw_name, &client->dev); + kfree(fw_name); if (error) { - dev_err(&client->dev, "failed to request firmware %s: %d\n", - ELAN_FW_FILENAME, error); + dev_err(&client->dev, "failed to request firmware: %d\n", + error); return error; } diff --git a/drivers/input/touchscreen/goodix.c b/drivers/input/touchscreen/goodix.c index ca196689f025..3af16984d57c 100644 --- a/drivers/input/touchscreen/goodix.c +++ b/drivers/input/touchscreen/goodix.c @@ -23,6 +23,8 @@ #include <linux/irq.h> #include <linux/interrupt.h> #include <linux/slab.h> +#include <linux/acpi.h> +#include <linux/of.h> #include <asm/unaligned.h> struct goodix_ts_data { @@ -48,6 +50,7 @@ struct goodix_ts_data { #define GOODIX_REG_VERSION 0x8140 #define RESOLUTION_LOC 1 +#define MAX_CONTACTS_LOC 5 #define TRIGGER_LOC 6 static const unsigned long goodix_irq_flags[] = { @@ -99,7 +102,7 @@ static int goodix_ts_read_input_report(struct goodix_ts_data *ts, u8 *data) } touch_num = data[0] & 0x0f; - if (touch_num > GOODIX_MAX_CONTACTS) + if (touch_num > ts->max_touch_num) return -EPROTO; if (touch_num > 1) { @@ -141,7 +144,7 @@ static void goodix_ts_report_touch(struct goodix_ts_data *ts, u8 *coor_data) */ static void goodix_process_events(struct goodix_ts_data *ts) { - u8 point_data[1 + GOODIX_CONTACT_SIZE * GOODIX_MAX_CONTACTS]; + u8 point_data[1 + GOODIX_CONTACT_SIZE * ts->max_touch_num]; int touch_num; int i; @@ -202,21 +205,23 @@ static void goodix_read_config(struct goodix_ts_data *ts) ts->abs_x_max = GOODIX_MAX_WIDTH; ts->abs_y_max = GOODIX_MAX_HEIGHT; ts->int_trigger_type = GOODIX_INT_TRIGGER; + ts->max_touch_num = GOODIX_MAX_CONTACTS; return; } ts->abs_x_max = get_unaligned_le16(&config[RESOLUTION_LOC]); ts->abs_y_max = get_unaligned_le16(&config[RESOLUTION_LOC + 2]); - ts->int_trigger_type = (config[TRIGGER_LOC]) & 0x03; - if (!ts->abs_x_max || !ts->abs_y_max) { + ts->int_trigger_type = config[TRIGGER_LOC] & 0x03; + ts->max_touch_num = config[MAX_CONTACTS_LOC] & 0x0f; + if (!ts->abs_x_max || !ts->abs_y_max || !ts->max_touch_num) { dev_err(&ts->client->dev, "Invalid config, using defaults\n"); ts->abs_x_max = GOODIX_MAX_WIDTH; ts->abs_y_max = GOODIX_MAX_HEIGHT; + ts->max_touch_num = GOODIX_MAX_CONTACTS; } } - /** * goodix_read_version - Read goodix touchscreen version * @@ -295,7 +300,7 @@ static int goodix_request_input_dev(struct goodix_ts_data *ts) input_set_abs_params(ts->input_dev, ABS_MT_WIDTH_MAJOR, 0, 255, 0, 0); input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); - input_mt_init_slots(ts->input_dev, GOODIX_MAX_CONTACTS, + input_mt_init_slots(ts->input_dev, ts->max_touch_num, INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED); ts->input_dev->name = "Goodix Capacitive TouchScreen"; @@ -372,11 +377,27 @@ static const struct i2c_device_id goodix_ts_id[] = { { } }; +#ifdef CONFIG_ACPI static const struct acpi_device_id goodix_acpi_match[] = { { "GDIX1001", 0 }, { } }; MODULE_DEVICE_TABLE(acpi, goodix_acpi_match); +#endif + +#ifdef CONFIG_OF +static const struct of_device_id goodix_of_match[] = { + { .compatible = "goodix,gt911" }, + { .compatible = "goodix,gt9110" }, + { .compatible = "goodix,gt912" }, + { .compatible = "goodix,gt927" }, + { .compatible = "goodix,gt9271" }, + { .compatible = "goodix,gt928" }, + { .compatible = "goodix,gt967" }, + { } +}; +MODULE_DEVICE_TABLE(of, goodix_of_match); +#endif static struct i2c_driver goodix_ts_driver = { .probe = goodix_ts_probe, @@ -384,7 +405,8 @@ static struct i2c_driver goodix_ts_driver = { .driver = { .name = "Goodix-TS", .owner = THIS_MODULE, - .acpi_match_table = goodix_acpi_match, + .acpi_match_table = ACPI_PTR(goodix_acpi_match), + .of_match_table = of_match_ptr(goodix_of_match), }, }; module_i2c_driver(goodix_ts_driver); diff --git a/drivers/input/touchscreen/sun4i-ts.c b/drivers/input/touchscreen/sun4i-ts.c index b93a28b955fd..c0116994067d 100644 --- a/drivers/input/touchscreen/sun4i-ts.c +++ b/drivers/input/touchscreen/sun4i-ts.c @@ -30,6 +30,10 @@ * These kinds of heuristics are just asking for trouble (and don't belong * in the kernel). So this driver offers straight forward, reliable single * touch functionality only. + * + * s.a. A20 User Manual "1.15 TP" (Documentation/arm/sunxi/README) + * (looks like the description in the A20 User Manual v1.3 is better + * than the one in the A10 User Manual v.1.5) */ #include <linux/err.h> @@ -193,7 +197,7 @@ static int sun4i_get_temp(const struct sun4i_ts_data *ts, long *temp) if (ts->temp_data == -1) return -EAGAIN; - *temp = (ts->temp_data - ts->temp_offset) * ts->temp_step; + *temp = ts->temp_data * ts->temp_step - ts->temp_offset; return 0; } @@ -246,6 +250,8 @@ static int sun4i_ts_probe(struct platform_device *pdev) int error; u32 reg; bool ts_attached; + u32 tp_sensitive_adjust = 15; + u32 filter_type = 1; ts = devm_kzalloc(dev, sizeof(struct sun4i_ts_data), GFP_KERNEL); if (!ts) @@ -255,22 +261,31 @@ static int sun4i_ts_probe(struct platform_device *pdev) ts->ignore_fifo_data = true; ts->temp_data = -1; if (of_device_is_compatible(np, "allwinner,sun6i-a31-ts")) { - /* Allwinner SDK has temperature = -271 + (value / 6) (C) */ - ts->temp_offset = 1626; + /* Allwinner SDK has temperature (C) = (value / 6) - 271 */ + ts->temp_offset = 271000; ts->temp_step = 167; + } else if (of_device_is_compatible(np, "allwinner,sun4i-a10-ts")) { + /* + * The A10 temperature sensor has quite a wide spread, these + * parameters are based on the averaging of the calibration + * results of 4 completely different boards, with a spread of + * temp_step from 0.096 - 0.170 and temp_offset from 176 - 331. + */ + ts->temp_offset = 257000; + ts->temp_step = 133; } else { /* * The user manuals do not contain the formula for calculating * the temperature. The formula used here is from the AXP209, * which is designed by X-Powers, an affiliate of Allwinner: * - * temperature = -144.7 + (value * 0.1) + * temperature (C) = (value * 0.1) - 144.7 * * Allwinner does not have any documentation whatsoever for * this hardware. Moreover, it is claimed that the sensor * is inaccurate and cannot work properly. */ - ts->temp_offset = 1447; + ts->temp_offset = 144700; ts->temp_step = 100; } @@ -313,14 +328,20 @@ static int sun4i_ts_probe(struct platform_device *pdev) ts->base + TP_CTRL0); /* - * sensitive_adjust = 15 : max, which is not all that sensitive, + * tp_sensitive_adjust is an optional property * tp_mode = 0 : only x and y coordinates, as we don't use dual touch */ - writel(TP_SENSITIVE_ADJUST(15) | TP_MODE_SELECT(0), + of_property_read_u32(np, "allwinner,tp-sensitive-adjust", + &tp_sensitive_adjust); + writel(TP_SENSITIVE_ADJUST(tp_sensitive_adjust) | TP_MODE_SELECT(0), ts->base + TP_CTRL2); - /* Enable median filter, type 1 : 5/3 */ - writel(FILTER_EN(1) | FILTER_TYPE(1), ts->base + TP_CTRL3); + /* + * Enable median and averaging filter, optional property for + * filter type. + */ + of_property_read_u32(np, "allwinner,filter-type", &filter_type); + writel(FILTER_EN(1) | FILTER_TYPE(filter_type), ts->base + TP_CTRL3); /* Enable temperature measurement, period 1953 (2 seconds) */ writel(TEMP_ENABLE(1) | TEMP_PERIOD(1953), ts->base + TP_TPR); @@ -330,10 +351,10 @@ static int sun4i_ts_probe(struct platform_device *pdev) * finally enable tp mode. */ reg = STYLUS_UP_DEBOUN(5) | STYLUS_UP_DEBOUN_EN(1); - if (of_device_is_compatible(np, "allwinner,sun4i-a10-ts")) - reg |= TP_MODE_EN(1); - else + if (of_device_is_compatible(np, "allwinner,sun6i-a31-ts")) reg |= SUN6I_TP_MODE_EN(1); + else + reg |= TP_MODE_EN(1); writel(reg, ts->base + TP_CTRL1); /* @@ -383,6 +404,7 @@ static int sun4i_ts_remove(struct platform_device *pdev) static const struct of_device_id sun4i_ts_of_match[] = { { .compatible = "allwinner,sun4i-a10-ts", }, + { .compatible = "allwinner,sun5i-a13-ts", }, { .compatible = "allwinner,sun6i-a31-ts", }, { /* sentinel */ } }; diff --git a/drivers/input/touchscreen/sx8654.c b/drivers/input/touchscreen/sx8654.c new file mode 100644 index 000000000000..aecb9ad2e701 --- /dev/null +++ b/drivers/input/touchscreen/sx8654.c @@ -0,0 +1,286 @@ +/* + * Driver for Semtech SX8654 I2C touchscreen controller. + * + * Copyright (c) 2015 Armadeus Systems + * Sébastien Szymanski <sebastien.szymanski@armadeus.com> + * + * Using code from: + * - sx865x.c + * Copyright (c) 2013 U-MoBo Srl + * Pierluigi Passaro <p.passaro@u-mobo.com> + * - sx8650.c + * Copyright (c) 2009 Wayne Roberts + * - tsc2007.c + * Copyright (c) 2008 Kwangwoo Lee + * - ads7846.c + * Copyright (c) 2005 David Brownell + * Copyright (c) 2006 Nokia Corporation + * - corgi_ts.c + * Copyright (C) 2004-2005 Richard Purdie + * - omap_ts.[hc], ads7846.h, ts_osk.c + * Copyright (C) 2002 MontaVista Software + * Copyright (C) 2004 Texas Instruments + * Copyright (C) 2005 Dirk Behme + * + * 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/module.h> +#include <linux/of.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/irq.h> + +/* register addresses */ +#define I2C_REG_TOUCH0 0x00 +#define I2C_REG_TOUCH1 0x01 +#define I2C_REG_CHANMASK 0x04 +#define I2C_REG_IRQMASK 0x22 +#define I2C_REG_IRQSRC 0x23 +#define I2C_REG_SOFTRESET 0x3f + +/* commands */ +#define CMD_READ_REGISTER 0x40 +#define CMD_MANUAL 0xc0 +#define CMD_PENTRG 0xe0 + +/* value for I2C_REG_SOFTRESET */ +#define SOFTRESET_VALUE 0xde + +/* bits for I2C_REG_IRQSRC */ +#define IRQ_PENTOUCH_TOUCHCONVDONE 0x08 +#define IRQ_PENRELEASE 0x04 + +/* bits for RegTouch1 */ +#define CONDIRQ 0x20 +#define FILT_7SA 0x03 + +/* bits for I2C_REG_CHANMASK */ +#define CONV_X 0x80 +#define CONV_Y 0x40 + +/* coordinates rate: higher nibble of CTRL0 register */ +#define RATE_MANUAL 0x00 +#define RATE_5000CPS 0xf0 + +/* power delay: lower nibble of CTRL0 register */ +#define POWDLY_1_1MS 0x0b + +#define MAX_12BIT ((1 << 12) - 1) + +struct sx8654 { + struct input_dev *input; + struct i2c_client *client; +}; + +static irqreturn_t sx8654_irq(int irq, void *handle) +{ + struct sx8654 *sx8654 = handle; + int irqsrc; + u8 data[4]; + unsigned int x, y; + int retval; + + irqsrc = i2c_smbus_read_byte_data(sx8654->client, + CMD_READ_REGISTER | I2C_REG_IRQSRC); + dev_dbg(&sx8654->client->dev, "irqsrc = 0x%x", irqsrc); + + if (irqsrc < 0) + goto out; + + if (irqsrc & IRQ_PENRELEASE) { + dev_dbg(&sx8654->client->dev, "pen release interrupt"); + + input_report_key(sx8654->input, BTN_TOUCH, 0); + input_sync(sx8654->input); + } + + if (irqsrc & IRQ_PENTOUCH_TOUCHCONVDONE) { + dev_dbg(&sx8654->client->dev, "pen touch interrupt"); + + retval = i2c_master_recv(sx8654->client, data, sizeof(data)); + if (retval != sizeof(data)) + goto out; + + /* invalid data */ + if (unlikely(data[0] & 0x80 || data[2] & 0x80)) + goto out; + + x = ((data[0] & 0xf) << 8) | (data[1]); + y = ((data[2] & 0xf) << 8) | (data[3]); + + input_report_abs(sx8654->input, ABS_X, x); + input_report_abs(sx8654->input, ABS_Y, y); + input_report_key(sx8654->input, BTN_TOUCH, 1); + input_sync(sx8654->input); + + dev_dbg(&sx8654->client->dev, "point(%4d,%4d)\n", x, y); + } + +out: + return IRQ_HANDLED; +} + +static int sx8654_open(struct input_dev *dev) +{ + struct sx8654 *sx8654 = input_get_drvdata(dev); + struct i2c_client *client = sx8654->client; + int error; + + /* enable pen trigger mode */ + error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH0, + RATE_5000CPS | POWDLY_1_1MS); + if (error) { + dev_err(&client->dev, "writing to I2C_REG_TOUCH0 failed"); + return error; + } + + error = i2c_smbus_write_byte(client, CMD_PENTRG); + if (error) { + dev_err(&client->dev, "writing command CMD_PENTRG failed"); + return error; + } + + enable_irq(client->irq); + + return 0; +} + +static void sx8654_close(struct input_dev *dev) +{ + struct sx8654 *sx8654 = input_get_drvdata(dev); + struct i2c_client *client = sx8654->client; + int error; + + disable_irq(client->irq); + + /* enable manual mode mode */ + error = i2c_smbus_write_byte(client, CMD_MANUAL); + if (error) { + dev_err(&client->dev, "writing command CMD_MANUAL failed"); + return; + } + + error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH0, 0); + if (error) { + dev_err(&client->dev, "writing to I2C_REG_TOUCH0 failed"); + return; + } +} + +static int sx8654_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct sx8654 *sx8654; + struct input_dev *input; + int error; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA)) + return -ENXIO; + + sx8654 = devm_kzalloc(&client->dev, sizeof(*sx8654), GFP_KERNEL); + if (!sx8654) + return -ENOMEM; + + input = devm_input_allocate_device(&client->dev); + if (!sx8654) + return -ENOMEM; + + input->name = "SX8654 I2C Touchscreen"; + input->id.bustype = BUS_I2C; + input->dev.parent = &client->dev; + input->open = sx8654_open; + input->close = sx8654_close; + + __set_bit(INPUT_PROP_DIRECT, input->propbit); + input_set_capability(input, EV_KEY, BTN_TOUCH); + input_set_abs_params(input, ABS_X, 0, MAX_12BIT, 0, 0); + input_set_abs_params(input, ABS_Y, 0, MAX_12BIT, 0, 0); + + sx8654->client = client; + sx8654->input = input; + + input_set_drvdata(sx8654->input, sx8654); + + error = i2c_smbus_write_byte_data(client, I2C_REG_SOFTRESET, + SOFTRESET_VALUE); + if (error) { + dev_err(&client->dev, "writing softreset value failed"); + return error; + } + + error = i2c_smbus_write_byte_data(client, I2C_REG_CHANMASK, + CONV_X | CONV_Y); + if (error) { + dev_err(&client->dev, "writing to I2C_REG_CHANMASK failed"); + return error; + } + + error = i2c_smbus_write_byte_data(client, I2C_REG_IRQMASK, + IRQ_PENTOUCH_TOUCHCONVDONE | + IRQ_PENRELEASE); + if (error) { + dev_err(&client->dev, "writing to I2C_REG_IRQMASK failed"); + return error; + } + + error = i2c_smbus_write_byte_data(client, I2C_REG_TOUCH1, + CONDIRQ | FILT_7SA); + if (error) { + dev_err(&client->dev, "writing to I2C_REG_TOUCH1 failed"); + return error; + } + + error = devm_request_threaded_irq(&client->dev, client->irq, + NULL, sx8654_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + client->name, sx8654); + if (error) { + dev_err(&client->dev, + "Failed to enable IRQ %d, error: %d\n", + client->irq, error); + return error; + } + + /* Disable the IRQ, we'll enable it in sx8654_open() */ + disable_irq(client->irq); + + error = input_register_device(sx8654->input); + if (error) + return error; + + i2c_set_clientdata(client, sx8654); + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id sx8654_of_match[] = { + { .compatible = "semtech,sx8654", }, + { }, +}; +MODULE_DEVICE_TABLE(of, sx8654_of_match); +#endif + +static const struct i2c_device_id sx8654_id_table[] = { + { "semtech_sx8654", 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, sx8654_id_table); + +static struct i2c_driver sx8654_driver = { + .driver = { + .name = "sx8654", + .of_match_table = of_match_ptr(sx8654_of_match), + }, + .id_table = sx8654_id_table, + .probe = sx8654_probe, +}; +module_i2c_driver(sx8654_driver); + +MODULE_AUTHOR("Sébastien Szymanski <sebastien.szymanski@armadeus.com>"); +MODULE_DESCRIPTION("Semtech SX8654 I2C Touchscreen Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/x86/fujitsu-tablet.c b/drivers/platform/x86/fujitsu-tablet.c index 53bdbb01bd3f..baea077a02cc 100644 --- a/drivers/platform/x86/fujitsu-tablet.c +++ b/drivers/platform/x86/fujitsu-tablet.c @@ -59,7 +59,7 @@ static unsigned short keymap_Lifebook_Tseries[KEYMAP_LEN] __initdata = { KEY_RESERVED, KEY_SCROLLDOWN, KEY_SCROLLUP, - KEY_DIRECTION, + KEY_ROTATE_DISPLAY, KEY_LEFTCTRL, KEY_BRIGHTNESSUP, KEY_BRIGHTNESSDOWN, @@ -116,7 +116,7 @@ static unsigned short keymap_Lifebook_U810[KEYMAP_LEN] __initdata = { KEY_RESERVED, KEY_PROG1, KEY_PROG2, - KEY_DIRECTION, + KEY_ROTATE_DISPLAY, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, @@ -153,7 +153,7 @@ static unsigned short keymap_Stylistic_ST5xxx[KEYMAP_LEN] __initdata = { KEY_RESERVED, KEY_RESERVED, KEY_MAIL, - KEY_DIRECTION, + KEY_ROTATE_DISPLAY, KEY_ESC, KEY_ENTER, KEY_BRIGHTNESSUP, diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index 0ab2b377a778..06697315a088 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -144,7 +144,7 @@ static const struct key_entry hp_wmi_keymap[] = { { KE_KEY, 0x20e8, { KEY_MEDIA } }, { KE_KEY, 0x2142, { KEY_MEDIA } }, { KE_KEY, 0x213b, { KEY_INFO } }, - { KE_KEY, 0x2169, { KEY_DIRECTION } }, + { KE_KEY, 0x2169, { KEY_ROTATE_DISPLAY } }, { KE_KEY, 0x216a, { KEY_SETUP } }, { KE_KEY, 0x231b, { KEY_HELP } }, { KE_END, 0 } |