From 4065d1e7b2164cff4af57b58fac887df2fe75d2a Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Tue, 31 Jan 2012 00:18:00 -0800 Subject: Input: add Cypress TTSP capacitive multi-touch screen support Cypress TrueTouch(tm) Standard Product controllers are found in a wide range of embedded devices. This driver add support for a variety of TTSP controllers. Since the hardware is capable of tracking identifiable contacts, multi-touch protocol type B (stateful) is used to report contact information. The driver is composed of a core driver that process the data sent by the contacts and a set of bus specific interface modules. This patch adds the base core TTSP driver. Signed-off-by: Javier Martinez Canillas Reviewed-by: Henrik Rydberg Signed-off-by: Dmitry Torokhov --- include/linux/input/cyttsp.h | 58 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 include/linux/input/cyttsp.h (limited to 'include/linux/input') diff --git a/include/linux/input/cyttsp.h b/include/linux/input/cyttsp.h new file mode 100644 index 000000000000..5af7c66f1fca --- /dev/null +++ b/include/linux/input/cyttsp.h @@ -0,0 +1,58 @@ +/* + * Header file for: + * Cypress TrueTouch(TM) Standard Product (TTSP) touchscreen drivers. + * For use with Cypress Txx3xx parts. + * Supported parts include: + * CY8CTST341 + * CY8CTMA340 + * + * Copyright (C) 2009, 2010, 2011 Cypress Semiconductor, Inc. + * Copyright (C) 2012 Javier Martinez Canillas + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contact Cypress Semiconductor at www.cypress.com (kev@cypress.com) + * + */ +#ifndef _CYTTSP_H_ +#define _CYTTSP_H_ + +#define CY_SPI_NAME "cyttsp-spi" +#define CY_I2C_NAME "cyttsp-i2c" +/* Active Power state scanning/processing refresh interval */ +#define CY_ACT_INTRVL_DFLT 0x00 /* ms */ +/* touch timeout for the Active power */ +#define CY_TCH_TMOUT_DFLT 0xFF /* ms */ +/* Low Power state scanning/processing refresh interval */ +#define CY_LP_INTRVL_DFLT 0x0A /* ms */ +/* Active distance in pixels for a gesture to be reported */ +#define CY_ACT_DIST_DFLT 0xF8 /* pixels */ + +struct cyttsp_platform_data { + u32 maxx; + u32 maxy; + bool use_hndshk; + u8 act_dist; /* Active distance */ + u8 act_intrvl; /* Active refresh interval; ms */ + u8 tch_tmout; /* Active touch timeout; ms */ + u8 lp_intrvl; /* Low power refresh interval; ms */ + int (*init)(void); + void (*exit)(void); + char *name; + s16 irq_gpio; + u8 *bl_keys; +}; + +#endif /* _CYTTSP_H_ */ -- cgit v1.2.3 From b89529a10c954f14191367355da2a6053c49abb9 Mon Sep 17 00:00:00 2001 From: Henrik Rydberg Date: Thu, 12 Jan 2012 19:40:34 +0100 Subject: Input: Use accessor for MT values The current MT accessor function does not distinguish between the MT values and the slot specification event. Add an accessor function for the values only, and use it where appropriate. Signed-off-by: Henrik Rydberg --- drivers/input/input.c | 2 +- include/linux/input/mt.h | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) (limited to 'include/linux/input') diff --git a/drivers/input/input.c b/drivers/input/input.c index 1f78c957a75a..8921c6180c51 100644 --- a/drivers/input/input.c +++ b/drivers/input/input.c @@ -180,7 +180,7 @@ static int input_handle_abs_event(struct input_dev *dev, return INPUT_IGNORE_EVENT; } - is_mt_event = code >= ABS_MT_FIRST && code <= ABS_MT_LAST; + is_mt_event = input_is_mt_value(code); if (!is_mt_event) { pold = &dev->absinfo[code].value; diff --git a/include/linux/input/mt.h b/include/linux/input/mt.h index 318bb82325a6..f86737586e19 100644 --- a/include/linux/input/mt.h +++ b/include/linux/input/mt.h @@ -48,10 +48,14 @@ static inline void input_mt_slot(struct input_dev *dev, int slot) input_event(dev, EV_ABS, ABS_MT_SLOT, slot); } +static inline bool input_is_mt_value(int axis) +{ + return axis >= ABS_MT_FIRST && axis <= ABS_MT_LAST; +} + static inline bool input_is_mt_axis(int axis) { - return axis == ABS_MT_SLOT || - (axis >= ABS_MT_FIRST && axis <= ABS_MT_LAST); + return axis == ABS_MT_SLOT || input_is_mt_value(axis); } void input_mt_report_slot_state(struct input_dev *dev, -- cgit v1.2.3 From 1b8be32e6914ed862a5ce460c0a0b418ba85d2b7 Mon Sep 17 00:00:00 2001 From: Rachna Patil Date: Sun, 4 Mar 2012 08:11:57 -0800 Subject: Input: add support for TI Touchscreen controller This patch adds support for TI's touchscreen controller for a 4/5/8 wire resistive panel that is directly fed to the ADC. This touchscreen controller will be part of AM335x TI SoC. The TRM can be found at: http://www.ti.com/lit/ug/spruh73a/spruh73a.pdf Signed-off-by: Patil, Rachna Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 12 + drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/ti_tscadc.c | 486 ++++++++++++++++++++++++++++++++++ include/linux/input/ti_tscadc.h | 17 ++ 4 files changed, 516 insertions(+) create mode 100644 drivers/input/touchscreen/ti_tscadc.c create mode 100644 include/linux/input/ti_tscadc.h (limited to 'include/linux/input') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 2b21a7066d36..5df719b591c3 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -460,6 +460,18 @@ config TOUCHSCREEN_TOUCHWIN To compile this driver as a module, choose M here: the module will be called touchwin. +config TOUCHSCREEN_TI_TSCADC + tristate "TI Touchscreen Interface" + depends on ARCH_OMAP2PLUS + help + Say Y here if you have 4/5/8 wire touchscreen controller + to be connected to the ADC controller on your TI AM335x SoC. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called ti_tscadc. + config TOUCHSCREEN_ATMEL_TSADCC tristate "Atmel Touchscreen Interface" depends on ARCH_AT91SAM9RL || ARCH_AT91SAM9G45 diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index d9acbdcda3d9..e748fb837759 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_TOUCHSCREEN_PIXCIR) += pixcir_i2c_ts.o obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o obj-$(CONFIG_TOUCHSCREEN_ST1232) += st1232.o obj-$(CONFIG_TOUCHSCREEN_STMPE) += stmpe-ts.o +obj-$(CONFIG_TOUCHSCREEN_TI_TSCADC) += ti_tscadc.o obj-$(CONFIG_TOUCHSCREEN_TNETV107X) += tnetv107x-ts.o obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o diff --git a/drivers/input/touchscreen/ti_tscadc.c b/drivers/input/touchscreen/ti_tscadc.c new file mode 100644 index 000000000000..d229c741d544 --- /dev/null +++ b/drivers/input/touchscreen/ti_tscadc.c @@ -0,0 +1,486 @@ +/* + * TI Touch Screen driver + * + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.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 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define REG_IRQEOI 0x020 +#define REG_RAWIRQSTATUS 0x024 +#define REG_IRQSTATUS 0x028 +#define REG_IRQENABLE 0x02C +#define REG_IRQWAKEUP 0x034 +#define REG_CTRL 0x040 +#define REG_ADCFSM 0x044 +#define REG_CLKDIV 0x04C +#define REG_SE 0x054 +#define REG_IDLECONFIG 0x058 +#define REG_CHARGECONFIG 0x05C +#define REG_CHARGEDELAY 0x060 +#define REG_STEPCONFIG(n) (0x64 + ((n - 1) * 8)) +#define REG_STEPDELAY(n) (0x68 + ((n - 1) * 8)) +#define REG_STEPCONFIG13 0x0C4 +#define REG_STEPDELAY13 0x0C8 +#define REG_STEPCONFIG14 0x0CC +#define REG_STEPDELAY14 0x0D0 +#define REG_FIFO0CNT 0xE4 +#define REG_FIFO1THR 0xF4 +#define REG_FIFO0 0x100 +#define REG_FIFO1 0x200 + +/* Register Bitfields */ +#define IRQWKUP_ENB BIT(0) +#define STPENB_STEPENB 0x7FFF +#define IRQENB_FIFO1THRES BIT(5) +#define IRQENB_PENUP BIT(9) +#define STEPCONFIG_MODE_HWSYNC 0x2 +#define STEPCONFIG_SAMPLES_AVG (1 << 4) +#define STEPCONFIG_XPP (1 << 5) +#define STEPCONFIG_XNN (1 << 6) +#define STEPCONFIG_YPP (1 << 7) +#define STEPCONFIG_YNN (1 << 8) +#define STEPCONFIG_XNP (1 << 9) +#define STEPCONFIG_YPN (1 << 10) +#define STEPCONFIG_INM (1 << 18) +#define STEPCONFIG_INP (1 << 20) +#define STEPCONFIG_INP_5 (1 << 21) +#define STEPCONFIG_FIFO1 (1 << 26) +#define STEPCONFIG_OPENDLY 0xff +#define STEPCONFIG_Z1 (3 << 19) +#define STEPIDLE_INP (1 << 22) +#define STEPCHARGE_RFP (1 << 12) +#define STEPCHARGE_INM (1 << 15) +#define STEPCHARGE_INP (1 << 19) +#define STEPCHARGE_RFM (1 << 23) +#define STEPCHARGE_DELAY 0x1 +#define CNTRLREG_TSCSSENB (1 << 0) +#define CNTRLREG_STEPID (1 << 1) +#define CNTRLREG_STEPCONFIGWRT (1 << 2) +#define CNTRLREG_4WIRE (1 << 5) +#define CNTRLREG_5WIRE (1 << 6) +#define CNTRLREG_8WIRE (3 << 5) +#define CNTRLREG_TSCENB (1 << 7) +#define ADCFSM_STEPID 0x10 + +#define SEQ_SETTLE 275 +#define ADC_CLK 3000000 +#define MAX_12BIT ((1 << 12) - 1) +#define TSCADC_DELTA_X 15 +#define TSCADC_DELTA_Y 15 + +struct tscadc { + struct input_dev *input; + struct clk *tsc_ick; + void __iomem *tsc_base; + unsigned int irq; + unsigned int wires; + unsigned int x_plate_resistance; + bool pen_down; +}; + +static unsigned int tscadc_readl(struct tscadc *ts, unsigned int reg) +{ + return readl(ts->tsc_base + reg); +} + +static void tscadc_writel(struct tscadc *tsc, unsigned int reg, + unsigned int val) +{ + writel(val, tsc->tsc_base + reg); +} + +static void tscadc_step_config(struct tscadc *ts_dev) +{ + unsigned int config; + int i; + + /* Configure the Step registers */ + + config = STEPCONFIG_MODE_HWSYNC | + STEPCONFIG_SAMPLES_AVG | STEPCONFIG_XPP; + switch (ts_dev->wires) { + case 4: + config |= STEPCONFIG_INP | STEPCONFIG_XNN; + break; + case 5: + config |= STEPCONFIG_YNN | + STEPCONFIG_INP_5 | STEPCONFIG_XNN | + STEPCONFIG_YPP; + break; + case 8: + config |= STEPCONFIG_INP | STEPCONFIG_XNN; + break; + } + + for (i = 1; i < 7; i++) { + tscadc_writel(ts_dev, REG_STEPCONFIG(i), config); + tscadc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY); + } + + config = 0; + config = STEPCONFIG_MODE_HWSYNC | + STEPCONFIG_SAMPLES_AVG | STEPCONFIG_YNN | + STEPCONFIG_INM | STEPCONFIG_FIFO1; + switch (ts_dev->wires) { + case 4: + config |= STEPCONFIG_YPP; + break; + case 5: + config |= STEPCONFIG_XPP | STEPCONFIG_INP_5 | + STEPCONFIG_XNP | STEPCONFIG_YPN; + break; + case 8: + config |= STEPCONFIG_YPP; + break; + } + + for (i = 7; i < 13; i++) { + tscadc_writel(ts_dev, REG_STEPCONFIG(i), config); + tscadc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY); + } + + config = 0; + /* Charge step configuration */ + config = STEPCONFIG_XPP | STEPCONFIG_YNN | + STEPCHARGE_RFP | STEPCHARGE_RFM | + STEPCHARGE_INM | STEPCHARGE_INP; + + tscadc_writel(ts_dev, REG_CHARGECONFIG, config); + tscadc_writel(ts_dev, REG_CHARGEDELAY, STEPCHARGE_DELAY); + + config = 0; + /* Configure to calculate pressure */ + config = STEPCONFIG_MODE_HWSYNC | + STEPCONFIG_SAMPLES_AVG | STEPCONFIG_YPP | + STEPCONFIG_XNN | STEPCONFIG_INM; + tscadc_writel(ts_dev, REG_STEPCONFIG13, config); + tscadc_writel(ts_dev, REG_STEPDELAY13, STEPCONFIG_OPENDLY); + + config |= STEPCONFIG_Z1 | STEPCONFIG_FIFO1; + tscadc_writel(ts_dev, REG_STEPCONFIG14, config); + tscadc_writel(ts_dev, REG_STEPDELAY14, STEPCONFIG_OPENDLY); + + tscadc_writel(ts_dev, REG_SE, STPENB_STEPENB); +} + +static void tscadc_idle_config(struct tscadc *ts_config) +{ + unsigned int idleconfig; + + idleconfig = STEPCONFIG_YNN | + STEPCONFIG_INM | + STEPCONFIG_YPN | STEPIDLE_INP; + tscadc_writel(ts_config, REG_IDLECONFIG, idleconfig); +} + +static void tscadc_read_coordinates(struct tscadc *ts_dev, + unsigned int *x, unsigned int *y) +{ + unsigned int fifocount = tscadc_readl(ts_dev, REG_FIFO0CNT); + unsigned int prev_val_x = ~0, prev_val_y = ~0; + unsigned int prev_diff_x = ~0, prev_diff_y = ~0; + unsigned int read, diff; + unsigned int i; + + /* + * Delta filter is used to remove large variations in sampled + * values from ADC. The filter tries to predict where the next + * coordinate could be. This is done by taking a previous + * coordinate and subtracting it form current one. Further the + * algorithm compares the difference with that of a present value, + * if true the value is reported to the sub system. + */ + for (i = 0; i < fifocount - 1; i++) { + read = tscadc_readl(ts_dev, REG_FIFO0) & 0xfff; + diff = abs(read - prev_val_x); + if (diff < prev_diff_x) { + prev_diff_x = diff; + *x = read; + } + prev_val_x = read; + + read = tscadc_readl(ts_dev, REG_FIFO1) & 0xfff; + diff = abs(read - prev_val_y); + if (diff < prev_diff_y) { + prev_diff_y = diff; + *y = read; + } + prev_val_y = read; + } +} + +static irqreturn_t tscadc_irq(int irq, void *dev) +{ + struct tscadc *ts_dev = dev; + struct input_dev *input_dev = ts_dev->input; + unsigned int status, irqclr = 0; + unsigned int x = 0, y = 0; + unsigned int z1, z2, z; + unsigned int fsm; + + status = tscadc_readl(ts_dev, REG_IRQSTATUS); + if (status & IRQENB_FIFO1THRES) { + tscadc_read_coordinates(ts_dev, &x, &y); + + z1 = tscadc_readl(ts_dev, REG_FIFO0) & 0xfff; + z2 = tscadc_readl(ts_dev, REG_FIFO1) & 0xfff; + + if (ts_dev->pen_down && z1 != 0 && z2 != 0) { + /* + * Calculate pressure using formula + * Resistance(touch) = x plate resistance * + * x postion/4096 * ((z2 / z1) - 1) + */ + z = z2 - z1; + z *= x; + z *= ts_dev->x_plate_resistance; + z /= z1; + z = (z + 2047) >> 12; + + if (z <= MAX_12BIT) { + input_report_abs(input_dev, ABS_X, x); + input_report_abs(input_dev, ABS_Y, y); + input_report_abs(input_dev, ABS_PRESSURE, z); + input_report_key(input_dev, BTN_TOUCH, 1); + input_sync(input_dev); + } + } + irqclr |= IRQENB_FIFO1THRES; + } + + /* + * Time for sequencer to settle, to read + * correct state of the sequencer. + */ + udelay(SEQ_SETTLE); + + status = tscadc_readl(ts_dev, REG_RAWIRQSTATUS); + if (status & IRQENB_PENUP) { + /* Pen up event */ + fsm = tscadc_readl(ts_dev, REG_ADCFSM); + if (fsm == ADCFSM_STEPID) { + ts_dev->pen_down = false; + input_report_key(input_dev, BTN_TOUCH, 0); + input_report_abs(input_dev, ABS_PRESSURE, 0); + input_sync(input_dev); + } else { + ts_dev->pen_down = true; + } + irqclr |= IRQENB_PENUP; + } + + tscadc_writel(ts_dev, REG_IRQSTATUS, irqclr); + /* check pending interrupts */ + tscadc_writel(ts_dev, REG_IRQEOI, 0x0); + + tscadc_writel(ts_dev, REG_SE, STPENB_STEPENB); + return IRQ_HANDLED; +} + +/* + * The functions for inserting/removing driver as a module. + */ + +static int __devinit tscadc_probe(struct platform_device *pdev) +{ + const struct tsc_data *pdata = pdev->dev.platform_data; + struct resource *res; + struct tscadc *ts_dev; + struct input_dev *input_dev; + struct clk *clk; + int err; + int clk_value, ctrl, irq; + + if (!pdata) { + dev_err(&pdev->dev, "missing platform data.\n"); + return -EINVAL; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "no memory resource defined.\n"); + return -EINVAL; + } + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "no irq ID is specified.\n"); + return -EINVAL; + } + + /* Allocate memory for device */ + ts_dev = kzalloc(sizeof(struct tscadc), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ts_dev || !input_dev) { + dev_err(&pdev->dev, "failed to allocate memory.\n"); + err = -ENOMEM; + goto err_free_mem; + } + + ts_dev->input = input_dev; + ts_dev->irq = irq; + ts_dev->wires = pdata->wires; + ts_dev->x_plate_resistance = pdata->x_plate_resistance; + + res = request_mem_region(res->start, resource_size(res), pdev->name); + if (!res) { + dev_err(&pdev->dev, "failed to reserve registers.\n"); + err = -EBUSY; + goto err_free_mem; + } + + ts_dev->tsc_base = ioremap(res->start, resource_size(res)); + if (!ts_dev->tsc_base) { + dev_err(&pdev->dev, "failed to map registers.\n"); + err = -ENOMEM; + goto err_release_mem_region; + } + + err = request_irq(ts_dev->irq, tscadc_irq, + 0, pdev->dev.driver->name, ts_dev); + if (err) { + dev_err(&pdev->dev, "failed to allocate irq.\n"); + goto err_unmap_regs; + } + + ts_dev->tsc_ick = clk_get(&pdev->dev, "adc_tsc_ick"); + if (IS_ERR(ts_dev->tsc_ick)) { + dev_err(&pdev->dev, "failed to get TSC ick\n"); + goto err_free_irq; + } + clk_enable(ts_dev->tsc_ick); + + clk = clk_get(&pdev->dev, "adc_tsc_fck"); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "failed to get TSC fck\n"); + err = PTR_ERR(clk); + goto err_disable_clk; + } + + clk_value = clk_get_rate(clk) / ADC_CLK; + clk_put(clk); + + if (clk_value < 7) { + dev_err(&pdev->dev, "clock input less than min clock requirement\n"); + goto err_disable_clk; + } + /* CLKDIV needs to be configured to the value minus 1 */ + tscadc_writel(ts_dev, REG_CLKDIV, clk_value - 1); + + /* Enable wake-up of the SoC using touchscreen */ + tscadc_writel(ts_dev, REG_IRQWAKEUP, IRQWKUP_ENB); + + ctrl = CNTRLREG_STEPCONFIGWRT | + CNTRLREG_TSCENB | + CNTRLREG_STEPID; + switch (ts_dev->wires) { + case 4: + ctrl |= CNTRLREG_4WIRE; + break; + case 5: + ctrl |= CNTRLREG_5WIRE; + break; + case 8: + ctrl |= CNTRLREG_8WIRE; + break; + } + tscadc_writel(ts_dev, REG_CTRL, ctrl); + + tscadc_idle_config(ts_dev); + tscadc_writel(ts_dev, REG_IRQENABLE, IRQENB_FIFO1THRES); + tscadc_step_config(ts_dev); + tscadc_writel(ts_dev, REG_FIFO1THR, 6); + + ctrl |= CNTRLREG_TSCSSENB; + tscadc_writel(ts_dev, REG_CTRL, ctrl); + + input_dev->name = "ti-tsc-adc"; + input_dev->dev.parent = &pdev->dev; + + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); + input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); + + 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); + input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0); + + /* register to the input system */ + err = input_register_device(input_dev); + if (err) + goto err_disable_clk; + + platform_set_drvdata(pdev, ts_dev); + return 0; + +err_disable_clk: + clk_disable(ts_dev->tsc_ick); + clk_put(ts_dev->tsc_ick); +err_free_irq: + free_irq(ts_dev->irq, ts_dev); +err_unmap_regs: + iounmap(ts_dev->tsc_base); +err_release_mem_region: + release_mem_region(res->start, resource_size(res)); +err_free_mem: + input_free_device(input_dev); + kfree(ts_dev); + return err; +} + +static int __devexit tscadc_remove(struct platform_device *pdev) +{ + struct tscadc *ts_dev = platform_get_drvdata(pdev); + struct resource *res; + + free_irq(ts_dev->irq, ts_dev); + + input_unregister_device(ts_dev->input); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + iounmap(ts_dev->tsc_base); + release_mem_region(res->start, resource_size(res)); + + clk_disable(ts_dev->tsc_ick); + clk_put(ts_dev->tsc_ick); + + kfree(ts_dev); + + platform_set_drvdata(pdev, NULL); + return 0; +} + +static struct platform_driver ti_tsc_driver = { + .probe = tscadc_probe, + .remove = __devexit_p(tscadc_remove), + .driver = { + .name = "tsc", + .owner = THIS_MODULE, + }, +}; +module_platform_driver(ti_tsc_driver); + +MODULE_DESCRIPTION("TI touchscreen controller driver"); +MODULE_AUTHOR("Rachna Patil "); +MODULE_LICENSE("GPL"); diff --git a/include/linux/input/ti_tscadc.h b/include/linux/input/ti_tscadc.h new file mode 100644 index 000000000000..b10a527a92a4 --- /dev/null +++ b/include/linux/input/ti_tscadc.h @@ -0,0 +1,17 @@ +#ifndef __LINUX_TI_TSCADC_H +#define __LINUX_TI_TSCADC_H + +/** + * struct tsc_data Touchscreen wire configuration + * @wires: Wires refer to application modes + * i.e. 4/5/8 wire touchscreen support + * on the platform. + * @x_plate_resistance: X plate resistance. + */ + +struct tsc_data { + int wires; + int x_plate_resistance; +}; + +#endif -- cgit v1.2.3 From 2cd36877ad1c61429e00c099b6903ebcd936ca00 Mon Sep 17 00:00:00 2001 From: Olof Johansson Date: Tue, 13 Mar 2012 21:35:51 -0700 Subject: Input: of_keymap - add device tree bindings for simple key matrices This adds a simple device tree binding for simple key matrix data and a helper to fill in the platform data. Signed-off-by: Olof Johansson Acked-by: Stephen Warren Signed-off-by: Dmitry Torokhov --- .../devicetree/bindings/input/matrix-keymap.txt | 19 +++++ drivers/input/Kconfig | 4 + drivers/input/Makefile | 1 + drivers/input/keyboard/Kconfig | 1 + drivers/input/of_keymap.c | 87 ++++++++++++++++++++++ include/linux/input/matrix_keypad.h | 19 +++++ 6 files changed, 131 insertions(+) create mode 100644 Documentation/devicetree/bindings/input/matrix-keymap.txt create mode 100644 drivers/input/of_keymap.c (limited to 'include/linux/input') diff --git a/Documentation/devicetree/bindings/input/matrix-keymap.txt b/Documentation/devicetree/bindings/input/matrix-keymap.txt new file mode 100644 index 000000000000..3cd8b98ccd2d --- /dev/null +++ b/Documentation/devicetree/bindings/input/matrix-keymap.txt @@ -0,0 +1,19 @@ +A simple common binding for matrix-connected key boards. Currently targeted at +defining the keys in the scope of linux key codes since that is a stable and +standardized interface at this time. + +Required properties: +- linux,keymap: an array of packed 1-cell entries containing the equivalent + of row, column and linux key-code. The 32-bit big endian cell is packed + as: + row << 24 | column << 16 | key-code + +Optional properties: +Some users of this binding might choose to specify secondary keymaps for +cases where there is a modifier key such as a Fn key. Proposed names +for said properties are "linux,fn-keymap" or with another descriptive +word for the modifier other from "Fn". + +Example: + linux,keymap = < 0x00030012 + 0x0102003a >; diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 001b147c7f95..332597980817 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -25,6 +25,10 @@ config INPUT if INPUT +config INPUT_OF_MATRIX_KEYMAP + depends on USE_OF + bool + config INPUT_FF_MEMLESS tristate "Support for memoryless force-feedback devices" help diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 0c789490e0b3..b173a13a73ca 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -24,3 +24,4 @@ obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/ obj-$(CONFIG_INPUT_MISC) += misc/ obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o +obj-$(CONFIG_INPUT_OF_MATRIX_KEYMAP) += of_keymap.o diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index cdc385b2cf7d..3371954c979b 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -394,6 +394,7 @@ config KEYBOARD_NOMADIK config KEYBOARD_TEGRA tristate "NVIDIA Tegra internal matrix keyboard controller support" depends on ARCH_TEGRA + select INPUT_OF_MATRIX_KEYMAP if USE_OF help Say Y here if you want to use a matrix keyboard connected directly to the internal keyboard controller on Tegra SoCs. diff --git a/drivers/input/of_keymap.c b/drivers/input/of_keymap.c new file mode 100644 index 000000000000..061493d57682 --- /dev/null +++ b/drivers/input/of_keymap.c @@ -0,0 +1,87 @@ +/* + * Helpers for open firmware matrix keyboard bindings + * + * Copyright (C) 2012 Google, Inc + * + * Author: + * Olof Johansson + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +struct matrix_keymap_data * +matrix_keyboard_of_fill_keymap(struct device_node *np, + const char *propname) +{ + struct matrix_keymap_data *kd; + u32 *keymap; + int proplen, i; + const __be32 *prop; + + if (!np) + return NULL; + + if (!propname) + propname = "linux,keymap"; + + prop = of_get_property(np, propname, &proplen); + if (!prop) + return NULL; + + if (proplen % sizeof(u32)) { + pr_warn("Malformed keymap property %s in %s\n", + propname, np->full_name); + return NULL; + } + + kd = kzalloc(sizeof(*kd), GFP_KERNEL); + if (!kd) + return NULL; + + kd->keymap = keymap = kzalloc(proplen, GFP_KERNEL); + if (!kd->keymap) { + kfree(kd); + return NULL; + } + + kd->keymap_size = proplen / sizeof(u32); + + for (i = 0; i < kd->keymap_size; i++) { + u32 tmp = be32_to_cpup(prop + i); + int key_code, row, col; + + row = (tmp >> 24) & 0xff; + col = (tmp >> 16) & 0xff; + key_code = tmp & 0xffff; + keymap[i] = KEY(row, col, key_code); + } + + return kd; +} +EXPORT_SYMBOL_GPL(matrix_keyboard_of_fill_keymap); + +void matrix_keyboard_of_free_keymap(const struct matrix_keymap_data *kd) +{ + if (kd) { + kfree(kd->keymap); + kfree(kd); + } +} +EXPORT_SYMBOL_GPL(matrix_keyboard_of_free_keymap); diff --git a/include/linux/input/matrix_keypad.h b/include/linux/input/matrix_keypad.h index fe7c4b9ae270..6c07ced0af81 100644 --- a/include/linux/input/matrix_keypad.h +++ b/include/linux/input/matrix_keypad.h @@ -3,6 +3,7 @@ #include #include +#include #define MATRIX_MAX_ROWS 32 #define MATRIX_MAX_COLS 32 @@ -106,4 +107,22 @@ matrix_keypad_build_keymap(const struct matrix_keymap_data *keymap_data, __clear_bit(KEY_RESERVED, keybit); } +#ifdef CONFIG_INPUT_OF_MATRIX_KEYMAP +struct matrix_keymap_data * +matrix_keyboard_of_fill_keymap(struct device_node *np, const char *propname); + +void matrix_keyboard_of_free_keymap(const struct matrix_keymap_data *kd); +#else +static inline struct matrix_keymap_data * +matrix_keyboard_of_fill_keymap(struct device_node *np, const char *propname) +{ + return NULL; +} + +static inline void +matrix_keyboard_of_free_keymap(const struct matrix_keymap_data *kd) +{ +} +#endif + #endif /* _MATRIX_KEYPAD_H */ -- cgit v1.2.3 From 043916608c4b309e98a1650520ed4e88ec5e9123 Mon Sep 17 00:00:00 2001 From: Christopher Hudson Date: Fri, 16 Mar 2012 22:47:47 -0700 Subject: Input: kxtj9 - who_am_i check value and initial data rate fixes Several fixes based on customer feedback: * WHO_AM_I value has changed since preliminary parts used for initial testing; * Output of le16_to_cpu must be saved to memory before shifting to preserve sign; * Initial data rate was not extracted from data control register init. This was causing the initial data rate to be set to maximum until it was changed. To fix this problem, it made more sense to specify initial data rate and extract the register mask from that. Signed-off-by: Chris Hudson Signed-off-by: Dmitry Torokhov --- drivers/input/misc/kxtj9.c | 22 +++++++++++++++++----- include/linux/input/kxtj9.h | 11 +---------- 2 files changed, 18 insertions(+), 15 deletions(-) (limited to 'include/linux/input') diff --git a/drivers/input/misc/kxtj9.c b/drivers/input/misc/kxtj9.c index 783597a9a64a..21edaba489be 100644 --- a/drivers/input/misc/kxtj9.c +++ b/drivers/input/misc/kxtj9.c @@ -41,6 +41,14 @@ #define PC1_ON (1 << 7) /* Data ready funtion enable bit: set during probe if using irq mode */ #define DRDYE (1 << 5) +/* DATA CONTROL REGISTER BITS */ +#define ODR12_5F 0 +#define ODR25F 1 +#define ODR50F 2 +#define ODR100F 3 +#define ODR200F 4 +#define ODR400F 5 +#define ODR800F 6 /* INTERRUPT CONTROL REGISTER 1 BITS */ /* Set these during probe if using irq mode */ #define KXTJ9_IEL (1 << 3) @@ -116,9 +124,13 @@ static void kxtj9_report_acceleration_data(struct kxtj9_data *tj9) if (err < 0) dev_err(&tj9->client->dev, "accelerometer data read failed\n"); - x = le16_to_cpu(acc_data[tj9->pdata.axis_map_x]) >> tj9->shift; - y = le16_to_cpu(acc_data[tj9->pdata.axis_map_y]) >> tj9->shift; - z = le16_to_cpu(acc_data[tj9->pdata.axis_map_z]) >> tj9->shift; + x = le16_to_cpu(acc_data[tj9->pdata.axis_map_x]); + y = le16_to_cpu(acc_data[tj9->pdata.axis_map_y]); + z = le16_to_cpu(acc_data[tj9->pdata.axis_map_z]); + + x >>= tj9->shift; + y >>= tj9->shift; + z >>= tj9->shift; input_report_abs(tj9->input_dev, ABS_X, tj9->pdata.negate_x ? -x : x); input_report_abs(tj9->input_dev, ABS_Y, tj9->pdata.negate_y ? -y : y); @@ -487,7 +499,7 @@ static int __devinit kxtj9_verify(struct kxtj9_data *tj9) goto out; } - retval = retval != 0x06 ? -EIO : 0; + retval = (retval != 0x07 && retval != 0x08) ? -EIO : 0; out: kxtj9_device_power_off(tj9); @@ -537,7 +549,7 @@ static int __devinit kxtj9_probe(struct i2c_client *client, i2c_set_clientdata(client, tj9); tj9->ctrl_reg1 = tj9->pdata.res_12bit | tj9->pdata.g_range; - tj9->data_ctrl = tj9->pdata.data_odr_init; + tj9->last_poll_interval = tj9->pdata.init_interval; if (client->irq) { /* If in irq mode, populate INT_CTRL_REG1 and enable DRDY. */ diff --git a/include/linux/input/kxtj9.h b/include/linux/input/kxtj9.h index f6bac89537b8..d415579b56fe 100644 --- a/include/linux/input/kxtj9.h +++ b/include/linux/input/kxtj9.h @@ -24,6 +24,7 @@ struct kxtj9_platform_data { unsigned int min_interval; /* minimum poll interval (in milli-seconds) */ + unsigned int init_interval; /* initial poll interval (in milli-seconds) */ /* * By default, x is axis 0, y is axis 1, z is axis 2; these can be @@ -52,16 +53,6 @@ struct kxtj9_platform_data { #define KXTJ9_G_8G (1 << 4) u8 g_range; - /* DATA_CTRL_REG: controls the output data rate of the part */ - #define ODR12_5F 0 - #define ODR25F 1 - #define ODR50F 2 - #define ODR100F 3 - #define ODR200F 4 - #define ODR400F 5 - #define ODR800F 6 - u8 data_odr_init; - int (*init)(void); void (*exit)(void); int (*power_on)(void); -- cgit v1.2.3 From 5c6a7a62c130afef3d61c1dee153012231ff5cd9 Mon Sep 17 00:00:00 2001 From: Olivier Sobrie Date: Fri, 16 Mar 2012 23:57:09 -0700 Subject: Input: ili210x - add support for Ilitek ILI210x based touchscreens The driver supports chipsets ILI2102, ILI2102s, ILI2103, ILI2103s and ILI2105. Such kind of controllers can be found in Amazon Kindle Fire devices. Reviewed-by: Jan Paesmans Reviewed-by: Henrik Rydberg Signed-off-by: Olivier Sobrie Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/Kconfig | 15 ++ drivers/input/touchscreen/Makefile | 1 + drivers/input/touchscreen/ili210x.c | 360 ++++++++++++++++++++++++++++++++++++ include/linux/input/ili210x.h | 10 + 4 files changed, 386 insertions(+) create mode 100644 drivers/input/touchscreen/ili210x.c create mode 100644 include/linux/input/ili210x.h (limited to 'include/linux/input') diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index fc087b3c95cf..97b31a0e0525 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -243,6 +243,21 @@ config TOUCHSCREEN_FUJITSU To compile this driver as a module, choose M here: the module will be called fujitsu-ts. +config TOUCHSCREEN_ILI210X + tristate "Ilitek ILI210X based touchscreen" + depends on I2C + help + Say Y here if you have a ILI210X based touchscreen + controller. This driver supports models ILI2102, + ILI2102s, ILI2103, ILI2103s and ILI2105. + Such kind of chipsets can be found in Amazon Kindle Fire + touchscreens. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called ili210x. + config TOUCHSCREEN_S3C2410 tristate "Samsung S3C2410/generic touchscreen input driver" depends on ARCH_S3C2410 || SAMSUNG_DEV_TS diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index e748fb837759..3d5cf8cbf89c 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_TOUCHSCREEN_EETI) += eeti_ts.o obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o obj-$(CONFIG_TOUCHSCREEN_FUJITSU) += fujitsu_ts.o +obj-$(CONFIG_TOUCHSCREEN_ILI210X) += ili210x.o obj-$(CONFIG_TOUCHSCREEN_INEXIO) += inexio.o obj-$(CONFIG_TOUCHSCREEN_INTEL_MID) += intel-mid-touch.o obj-$(CONFIG_TOUCHSCREEN_LPC32XX) += lpc32xx_ts.o diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c new file mode 100644 index 000000000000..c0044175a921 --- /dev/null +++ b/drivers/input/touchscreen/ili210x.c @@ -0,0 +1,360 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_TOUCHES 2 +#define DEFAULT_POLL_PERIOD 20 + +/* Touchscreen commands */ +#define REG_TOUCHDATA 0x10 +#define REG_PANEL_INFO 0x20 +#define REG_FIRMWARE_VERSION 0x40 +#define REG_CALIBRATE 0xcc + +struct finger { + u8 x_low; + u8 x_high; + u8 y_low; + u8 y_high; +} __packed; + +struct touchdata { + u8 status; + struct finger finger[MAX_TOUCHES]; +} __packed; + +struct panel_info { + struct finger finger_max; + u8 xchannel_num; + u8 ychannel_num; +} __packed; + +struct firmware_version { + u8 id; + u8 major; + u8 minor; +} __packed; + +struct ili210x { + struct i2c_client *client; + struct input_dev *input; + bool (*get_pendown_state)(void); + unsigned int poll_period; + struct delayed_work dwork; +}; + +static int ili210x_read_reg(struct i2c_client *client, u8 reg, void *buf, + size_t len) +{ + struct i2c_msg msg[2] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = ®, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = len, + .buf = buf, + } + }; + + if (i2c_transfer(client->adapter, msg, 2) != 2) { + dev_err(&client->dev, "i2c transfer failed\n"); + return -EIO; + } + + return 0; +} + +static void ili210x_report_events(struct input_dev *input, + const struct touchdata *touchdata) +{ + int i; + bool touch; + unsigned int x, y; + const struct finger *finger; + + for (i = 0; i < MAX_TOUCHES; i++) { + input_mt_slot(input, i); + + finger = &touchdata->finger[i]; + + touch = touchdata->status & (1 << i); + input_mt_report_slot_state(input, MT_TOOL_FINGER, touch); + if (touch) { + x = finger->x_low | (finger->x_high << 8); + y = finger->y_low | (finger->y_high << 8); + + input_report_abs(input, ABS_MT_POSITION_X, x); + input_report_abs(input, ABS_MT_POSITION_Y, y); + } + } + + input_mt_report_pointer_emulation(input, false); + input_sync(input); +} + +static bool get_pendown_state(const struct ili210x *priv) +{ + bool state = false; + + if (priv->get_pendown_state) + state = priv->get_pendown_state(); + + return state; +} + +static void ili210x_work(struct work_struct *work) +{ + struct ili210x *priv = container_of(work, struct ili210x, + dwork.work); + struct i2c_client *client = priv->client; + struct touchdata touchdata; + int error; + + error = ili210x_read_reg(client, REG_TOUCHDATA, + &touchdata, sizeof(touchdata)); + if (error) { + dev_err(&client->dev, + "Unable to get touchdata, err = %d\n", error); + return; + } + + ili210x_report_events(priv->input, &touchdata); + + if ((touchdata.status & 0xf3) || get_pendown_state(priv)) + schedule_delayed_work(&priv->dwork, + msecs_to_jiffies(priv->poll_period)); +} + +static irqreturn_t ili210x_irq(int irq, void *irq_data) +{ + struct ili210x *priv = irq_data; + + schedule_delayed_work(&priv->dwork, 0); + + return IRQ_HANDLED; +} + +static ssize_t ili210x_calibrate(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ili210x *priv = i2c_get_clientdata(client); + unsigned long calibrate; + int rc; + u8 cmd = REG_CALIBRATE; + + if (kstrtoul(buf, 10, &calibrate)) + return -EINVAL; + + if (calibrate > 1) + return -EINVAL; + + if (calibrate) { + rc = i2c_master_send(priv->client, &cmd, sizeof(cmd)); + if (rc != sizeof(cmd)) + return -EIO; + } + + return count; +} +static DEVICE_ATTR(calibrate, 0644, NULL, ili210x_calibrate); + +static struct attribute *ili210x_attributes[] = { + &dev_attr_calibrate.attr, + NULL, +}; + +static const struct attribute_group ili210x_attr_group = { + .attrs = ili210x_attributes, +}; + +static int __devinit ili210x_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + const struct ili210x_platform_data *pdata = dev->platform_data; + struct ili210x *priv; + struct input_dev *input; + struct panel_info panel; + struct firmware_version firmware; + int xmax, ymax; + int error; + + dev_dbg(dev, "Probing for ILI210X I2C Touschreen driver"); + + if (!pdata) { + dev_err(dev, "No platform data!\n"); + return -EINVAL; + } + + if (client->irq <= 0) { + dev_err(dev, "No IRQ!\n"); + return -EINVAL; + } + + /* Get firmware version */ + error = ili210x_read_reg(client, REG_FIRMWARE_VERSION, + &firmware, sizeof(firmware)); + if (error) { + dev_err(dev, "Failed to get firmware version, err: %d\n", + error); + return error; + } + + /* get panel info */ + error = ili210x_read_reg(client, REG_PANEL_INFO, &panel, sizeof(panel)); + if (error) { + dev_err(dev, "Failed to get panel informations, err: %d\n", + error); + return error; + } + + xmax = panel.finger_max.x_low | (panel.finger_max.x_high << 8); + ymax = panel.finger_max.y_low | (panel.finger_max.y_high << 8); + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + input = input_allocate_device(); + if (!priv || !input) { + error = -ENOMEM; + goto err_free_mem; + } + + priv->client = client; + priv->input = input; + priv->get_pendown_state = pdata->get_pendown_state; + priv->poll_period = pdata->poll_period ? : DEFAULT_POLL_PERIOD; + INIT_DELAYED_WORK(&priv->dwork, ili210x_work); + + /* Setup input device */ + input->name = "ILI210x Touchscreen"; + input->id.bustype = BUS_I2C; + input->dev.parent = dev; + + __set_bit(EV_SYN, input->evbit); + __set_bit(EV_KEY, input->evbit); + __set_bit(EV_ABS, input->evbit); + __set_bit(BTN_TOUCH, input->keybit); + + /* Single touch */ + input_set_abs_params(input, ABS_X, 0, xmax, 0, 0); + input_set_abs_params(input, ABS_Y, 0, ymax, 0, 0); + + /* Multi touch */ + input_mt_init_slots(input, MAX_TOUCHES); + input_set_abs_params(input, ABS_MT_POSITION_X, 0, xmax, 0, 0); + input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ymax, 0, 0); + + input_set_drvdata(input, priv); + i2c_set_clientdata(client, priv); + + error = request_irq(client->irq, ili210x_irq, pdata->irq_flags, + client->name, priv); + if (error) { + dev_err(dev, "Unable to request touchscreen IRQ, err: %d\n", + error); + goto err_free_mem; + } + + error = sysfs_create_group(&dev->kobj, &ili210x_attr_group); + if (error) { + dev_err(dev, "Unable to create sysfs attributes, err: %d\n", + error); + goto err_free_irq; + } + + error = input_register_device(priv->input); + if (error) { + dev_err(dev, "Cannot regiser input device, err: %d\n", error); + goto err_remove_sysfs; + } + + device_init_wakeup(&client->dev, 1); + + dev_dbg(dev, + "ILI210x initialized (IRQ: %d), firmware version %d.%d.%d", + client->irq, firmware.id, firmware.major, firmware.minor); + + return 0; + +err_remove_sysfs: + sysfs_remove_group(&dev->kobj, &ili210x_attr_group); +err_free_irq: + free_irq(client->irq, priv); +err_free_mem: + input_free_device(input); + kfree(priv); + return error; +} + +static int __devexit ili210x_i2c_remove(struct i2c_client *client) +{ + struct ili210x *priv = i2c_get_clientdata(client); + + sysfs_remove_group(&client->dev.kobj, &ili210x_attr_group); + free_irq(priv->client->irq, priv); + cancel_delayed_work_sync(&priv->dwork); + input_unregister_device(priv->input); + kfree(priv); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int ili210x_i2c_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (device_may_wakeup(&client->dev)) + enable_irq_wake(client->irq); + + return 0; +} + +static int ili210x_i2c_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + + if (device_may_wakeup(&client->dev)) + disable_irq_wake(client->irq); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(ili210x_i2c_pm, + ili210x_i2c_suspend, ili210x_i2c_resume); + +static const struct i2c_device_id ili210x_i2c_id[] = { + { "ili210x", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ili210x_i2c_id); + +static struct i2c_driver ili210x_ts_driver = { + .driver = { + .name = "ili210x_i2c", + .owner = THIS_MODULE, + .pm = &ili210x_i2c_pm, + }, + .id_table = ili210x_i2c_id, + .probe = ili210x_i2c_probe, + .remove = __devexit_p(ili210x_i2c_remove), +}; + +module_i2c_driver(ili210x_ts_driver); + +MODULE_AUTHOR("Olivier Sobrie "); +MODULE_DESCRIPTION("ILI210X I2C Touchscreen Driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/input/ili210x.h b/include/linux/input/ili210x.h new file mode 100644 index 000000000000..a5471245a13c --- /dev/null +++ b/include/linux/input/ili210x.h @@ -0,0 +1,10 @@ +#ifndef _ILI210X_H +#define _ILI210X_H + +struct ili210x_platform_data { + unsigned long irq_flags; + unsigned int poll_period; + bool (*get_pendown_state)(void); +}; + +#endif -- cgit v1.2.3 From 0bf25a45386f284d591530ef174eaa9e44d84956 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 3 Apr 2012 13:39:44 -0700 Subject: Input: add support for LM8333 keypads This driver adds support for the keypad part of the LM8333 and is prepared for possible GPIO/PWM drivers. Note that this is not a MFD because you cannot disable the keypad functionality which, thus, has to be handled by the core anyhow. Signed-off-by: Wolfram Sang Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/Kconfig | 10 ++ drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/lm8333.c | 236 ++++++++++++++++++++++++++++++++++++++++ include/linux/input/lm8333.h | 24 ++++ 4 files changed, 271 insertions(+) create mode 100644 drivers/input/keyboard/lm8333.c create mode 100644 include/linux/input/lm8333.h (limited to 'include/linux/input') diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index f354813a13e8..7eaf93fe5128 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -309,6 +309,16 @@ config KEYBOARD_LM8323 To compile this driver as a module, choose M here: the module will be called lm8323. +config KEYBOARD_LM8333 + tristate "LM8333 keypad chip" + depends on I2C + help + If you say yes here you get support for the National Semiconductor + LM8333 keypad controller. + + To compile this driver as a module, choose M here: the + module will be called lm8333. + config KEYBOARD_LOCOMO tristate "LoCoMo Keyboard Support" depends on SHARP_LOCOMO diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index df7061f12918..b03b02456a82 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_KEYBOARD_HP6XX) += jornada680_kbd.o obj-$(CONFIG_KEYBOARD_HP7XX) += jornada720_kbd.o obj-$(CONFIG_KEYBOARD_LKKBD) += lkkbd.o obj-$(CONFIG_KEYBOARD_LM8323) += lm8323.o +obj-$(CONFIG_KEYBOARD_LM8333) += lm8333.o obj-$(CONFIG_KEYBOARD_LOCOMO) += locomokbd.o obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o diff --git a/drivers/input/keyboard/lm8333.c b/drivers/input/keyboard/lm8333.c new file mode 100644 index 000000000000..9a8c4a6cf5c6 --- /dev/null +++ b/drivers/input/keyboard/lm8333.c @@ -0,0 +1,236 @@ +/* + * LM8333 keypad driver + * Copyright (C) 2012 Wolfram Sang, Pengutronix + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define LM8333_FIFO_READ 0x20 +#define LM8333_DEBOUNCE 0x22 +#define LM8333_READ_INT 0xD0 +#define LM8333_ACTIVE 0xE4 +#define LM8333_READ_ERROR 0xF0 + +#define LM8333_KEYPAD_IRQ (1 << 0) +#define LM8333_ERROR_IRQ (1 << 3) + +#define LM8333_ERROR_KEYOVR 0x04 +#define LM8333_ERROR_FIFOOVR 0x40 + +#define LM8333_FIFO_TRANSFER_SIZE 16 + +#define LM8333_ROW_SHIFT 4 +#define LM8333_NUM_ROWS 8 + + +struct lm8333 { + struct i2c_client *client; + struct input_dev *input; + unsigned short keycodes[LM8333_NUM_ROWS << LM8333_ROW_SHIFT]; +}; + +/* The accessors try twice because the first access may be needed for wakeup */ +#define LM8333_READ_RETRIES 2 + +int lm8333_read8(struct lm8333 *lm8333, u8 cmd) +{ + int retries = 0, ret; + + do { + ret = i2c_smbus_read_byte_data(lm8333->client, cmd); + } while (ret < 0 && retries++ < LM8333_READ_RETRIES); + + return ret; +} + +int lm8333_write8(struct lm8333 *lm8333, u8 cmd, u8 val) +{ + int retries = 0, ret; + + do { + ret = i2c_smbus_write_byte_data(lm8333->client, cmd, val); + } while (ret < 0 && retries++ < LM8333_READ_RETRIES); + + return ret; +} + +int lm8333_read_block(struct lm8333 *lm8333, u8 cmd, u8 len, u8 *buf) +{ + int retries = 0, ret; + + do { + ret = i2c_smbus_read_i2c_block_data(lm8333->client, + cmd, len, buf); + } while (ret < 0 && retries++ < LM8333_READ_RETRIES); + + return ret; +} + +static void lm8333_key_handler(struct lm8333 *lm8333) +{ + struct input_dev *input = lm8333->input; + u8 keys[LM8333_FIFO_TRANSFER_SIZE]; + u8 code, pressed; + int i, ret; + + ret = lm8333_read_block(lm8333, LM8333_FIFO_READ, + LM8333_FIFO_TRANSFER_SIZE, keys); + if (ret != LM8333_FIFO_TRANSFER_SIZE) { + dev_err(&lm8333->client->dev, + "Error %d while reading FIFO\n", ret); + return; + } + + for (i = 0; keys[i] && i < LM8333_FIFO_TRANSFER_SIZE; i++) { + pressed = keys[i] & 0x80; + code = keys[i] & 0x7f; + + input_event(input, EV_MSC, MSC_SCAN, code); + input_report_key(input, lm8333->keycodes[code], pressed); + } + + input_sync(input); +} + +static irqreturn_t lm8333_irq_thread(int irq, void *data) +{ + struct lm8333 *lm8333 = data; + u8 status = lm8333_read8(lm8333, LM8333_READ_INT); + + if (!status) + return IRQ_NONE; + + if (status & LM8333_ERROR_IRQ) { + u8 err = lm8333_read8(lm8333, LM8333_READ_ERROR); + + if (err & (LM8333_ERROR_KEYOVR | LM8333_ERROR_FIFOOVR)) { + u8 dummy[LM8333_FIFO_TRANSFER_SIZE]; + + lm8333_read_block(lm8333, LM8333_FIFO_READ, + LM8333_FIFO_TRANSFER_SIZE, dummy); + } + dev_err(&lm8333->client->dev, "Got error %02x\n", err); + } + + if (status & LM8333_KEYPAD_IRQ) + lm8333_key_handler(lm8333); + + return IRQ_HANDLED; +} + +static int __devinit lm8333_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + const struct lm8333_platform_data *pdata = client->dev.platform_data; + struct lm8333 *lm8333; + struct input_dev *input; + int err, active_time; + + if (!pdata) + return -EINVAL; + + active_time = pdata->active_time ?: 500; + if (active_time / 3 <= pdata->debounce_time / 3) { + dev_err(&client->dev, "Active time not big enough!\n"); + return -EINVAL; + } + + lm8333 = kzalloc(sizeof(*lm8333), GFP_KERNEL); + input = input_allocate_device(); + if (!lm8333 || !input) { + err = -ENOMEM; + goto free_mem; + } + + lm8333->client = client; + lm8333->input = input; + + input->name = client->name; + input->dev.parent = &client->dev; + input->id.bustype = BUS_I2C; + + input->keycode = lm8333->keycodes; + input->keycodesize = sizeof(lm8333->keycodes[0]); + input->keycodemax = ARRAY_SIZE(lm8333->keycodes); + input->evbit[0] = BIT_MASK(EV_KEY); + input_set_capability(input, EV_MSC, MSC_SCAN); + + matrix_keypad_build_keymap(pdata->matrix_data, LM8333_ROW_SHIFT, + input->keycode, input->keybit); + + if (pdata->debounce_time) { + err = lm8333_write8(lm8333, LM8333_DEBOUNCE, + pdata->debounce_time / 3); + if (err) + dev_warn(&client->dev, "Unable to set debounce time\n"); + } + + if (pdata->active_time) { + err = lm8333_write8(lm8333, LM8333_ACTIVE, + pdata->active_time / 3); + if (err) + dev_warn(&client->dev, "Unable to set active time\n"); + } + + err = request_threaded_irq(client->irq, NULL, lm8333_irq_thread, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "lm8333", lm8333); + if (err) + goto free_mem; + + err = input_register_device(input); + if (err) + goto free_irq; + + i2c_set_clientdata(client, lm8333); + return 0; + + free_irq: + free_irq(client->irq, lm8333); + free_mem: + input_free_device(input); + kfree(lm8333); + return err; +} + +static int __devexit lm8333_remove(struct i2c_client *client) +{ + struct lm8333 *lm8333 = i2c_get_clientdata(client); + + free_irq(client->irq, lm8333); + input_unregister_device(lm8333->input); + kfree(lm8333); + + return 0; +} + +static const struct i2c_device_id lm8333_id[] = { + { "lm8333", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, lm8333_id); + +static struct i2c_driver lm8333_driver = { + .driver = { + .name = "lm8333", + .owner = THIS_MODULE, + }, + .probe = lm8333_probe, + .remove = __devexit_p(lm8333_remove), + .id_table = lm8333_id, +}; +module_i2c_driver(lm8333_driver); + +MODULE_AUTHOR("Wolfram Sang "); +MODULE_DESCRIPTION("LM8333 keyboard driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/input/lm8333.h b/include/linux/input/lm8333.h new file mode 100644 index 000000000000..79f918c6e8c5 --- /dev/null +++ b/include/linux/input/lm8333.h @@ -0,0 +1,24 @@ +/* + * public include for LM8333 keypad driver - same license as driver + * Copyright (C) 2012 Wolfram Sang, Pengutronix + */ + +#ifndef _LM8333_H +#define _LM8333_H + +struct lm8333; + +struct lm8333_platform_data { + /* Keymap data */ + const struct matrix_keymap_data *matrix_data; + /* Active timeout before enter HALT mode in microseconds */ + unsigned active_time; + /* Debounce interval in microseconds */ + unsigned debounce_time; +}; + +extern int lm8333_read8(struct lm8333 *lm8333, u8 cmd); +extern int lm8333_write8(struct lm8333 *lm8333, u8 cmd, u8 val); +extern int lm8333_read_block(struct lm8333 *lm8333, u8 cmd, u8 len, u8 *buf); + +#endif /* _LM8333_H */ -- cgit v1.2.3 From ae99ea562be0a788ccb583aff21f3d2147de531f Mon Sep 17 00:00:00 2001 From: Paul Parsons Date: Thu, 10 May 2012 22:11:51 -0700 Subject: Input: Add Synaptics NavPoint (PXA27x SSP/SPI) driver This driver adds support for the Synaptics NavPoint touchpad connected to a PXA27x SSP port in SPI slave mode. The device emulates a mouse; a tap or tap-and-a-half drag gesture emulates the left mouse button. For example, use the xf86-input-evdev driver for an X pointing device. Signed-off-by: Paul Parsons Tested-by: Philipp Zabel Signed-off-by: Dmitry Torokhov --- drivers/input/mouse/Kconfig | 12 ++ drivers/input/mouse/Makefile | 1 + drivers/input/mouse/navpoint.c | 369 +++++++++++++++++++++++++++++++++++++++++ include/linux/input/navpoint.h | 12 ++ 4 files changed, 394 insertions(+) create mode 100644 drivers/input/mouse/navpoint.c create mode 100644 include/linux/input/navpoint.h (limited to 'include/linux/input') diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig index 9b8db821d5f0..cd6268cf7cd5 100644 --- a/drivers/input/mouse/Kconfig +++ b/drivers/input/mouse/Kconfig @@ -339,4 +339,16 @@ config MOUSE_SYNAPTICS_USB To compile this driver as a module, choose M here: the module will be called synaptics_usb. +config MOUSE_NAVPOINT_PXA27x + tristate "Synaptics NavPoint (PXA27x SSP/SPI)" + depends on PXA27x && PXA_SSP + help + This driver adds support for the Synaptics NavPoint touchpad connected + to a PXA27x SSP port in SPI slave mode. The device emulates a mouse; + a tap or tap-and-a-half drag gesture emulates the left mouse button. + For example, use the xf86-input-evdev driver for an X pointing device. + + To compile this driver as a module, choose M here: the + module will be called navpoint. + endif diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile index 4718effeb8d9..46ba7556fd4f 100644 --- a/drivers/input/mouse/Makefile +++ b/drivers/input/mouse/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o obj-$(CONFIG_MOUSE_INPORT) += inport.o obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o obj-$(CONFIG_MOUSE_MAPLE) += maplemouse.o +obj-$(CONFIG_MOUSE_NAVPOINT_PXA27x) += navpoint.o obj-$(CONFIG_MOUSE_PC110PAD) += pc110pad.o obj-$(CONFIG_MOUSE_PS2) += psmouse.o obj-$(CONFIG_MOUSE_PXA930_TRKBALL) += pxa930_trkball.o diff --git a/drivers/input/mouse/navpoint.c b/drivers/input/mouse/navpoint.c new file mode 100644 index 000000000000..c29ae7654d5e --- /dev/null +++ b/drivers/input/mouse/navpoint.c @@ -0,0 +1,369 @@ +/* + * Synaptics NavPoint (PXA27x SSP/SPI) driver. + * + * Copyright (C) 2012 Paul Parsons + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Synaptics Modular Embedded Protocol: Module Packet Format. + * Module header byte 2:0 = Length (# bytes that follow) + * Module header byte 4:3 = Control + * Module header byte 7:5 = Module Address + */ +#define HEADER_LENGTH(byte) ((byte) & 0x07) +#define HEADER_CONTROL(byte) (((byte) >> 3) & 0x03) +#define HEADER_ADDRESS(byte) ((byte) >> 5) + +struct navpoint { + struct ssp_device *ssp; + struct input_dev *input; + struct device *dev; + int gpio; + int index; + u8 data[1 + HEADER_LENGTH(0xff)]; +}; + +/* + * Initialization values for SSCR0_x, SSCR1_x, SSSR_x. + */ +static const u32 sscr0 = 0 + | SSCR0_TUM /* TIM = 1; No TUR interrupts */ + | SSCR0_RIM /* RIM = 1; No ROR interrupts */ + | SSCR0_SSE /* SSE = 1; SSP enabled */ + | SSCR0_Motorola /* FRF = 0; Motorola SPI */ + | SSCR0_DataSize(16) /* DSS = 15; Data size = 16-bit */ + ; +static const u32 sscr1 = 0 + | SSCR1_SCFR /* SCFR = 1; SSPSCLK only during transfers */ + | SSCR1_SCLKDIR /* SCLKDIR = 1; Slave mode */ + | SSCR1_SFRMDIR /* SFRMDIR = 1; Slave mode */ + | SSCR1_RWOT /* RWOT = 1; Receive without transmit mode */ + | SSCR1_RxTresh(1) /* RFT = 0; Receive FIFO threshold = 1 */ + | SSCR1_SPH /* SPH = 1; SSPSCLK inactive 0.5 + 1 cycles */ + | SSCR1_RIE /* RIE = 1; Receive FIFO interrupt enabled */ + ; +static const u32 sssr = 0 + | SSSR_BCE /* BCE = 1; Clear BCE */ + | SSSR_TUR /* TUR = 1; Clear TUR */ + | SSSR_EOC /* EOC = 1; Clear EOC */ + | SSSR_TINT /* TINT = 1; Clear TINT */ + | SSSR_PINT /* PINT = 1; Clear PINT */ + | SSSR_ROR /* ROR = 1; Clear ROR */ + ; + +/* + * MEP Query $22: Touchpad Coordinate Range Query is not supported by + * the NavPoint module, so sampled values provide the default limits. + */ +#define NAVPOINT_X_MIN 1278 +#define NAVPOINT_X_MAX 5340 +#define NAVPOINT_Y_MIN 1572 +#define NAVPOINT_Y_MAX 4396 +#define NAVPOINT_PRESSURE_MIN 0 +#define NAVPOINT_PRESSURE_MAX 255 + +static void navpoint_packet(struct navpoint *navpoint) +{ + int finger; + int gesture; + int x, y, z; + + switch (navpoint->data[0]) { + case 0xff: /* Garbage (packet?) between reset and Hello packet */ + case 0x00: /* Module 0, NULL packet */ + break; + + case 0x0e: /* Module 0, Absolute packet */ + finger = (navpoint->data[1] & 0x01); + gesture = (navpoint->data[1] & 0x02); + x = ((navpoint->data[2] & 0x1f) << 8) | navpoint->data[3]; + y = ((navpoint->data[4] & 0x1f) << 8) | navpoint->data[5]; + z = navpoint->data[6]; + input_report_key(navpoint->input, BTN_TOUCH, finger); + input_report_abs(navpoint->input, ABS_X, x); + input_report_abs(navpoint->input, ABS_Y, y); + input_report_abs(navpoint->input, ABS_PRESSURE, z); + input_report_key(navpoint->input, BTN_TOOL_FINGER, finger); + input_report_key(navpoint->input, BTN_LEFT, gesture); + input_sync(navpoint->input); + break; + + case 0x19: /* Module 0, Hello packet */ + if ((navpoint->data[1] & 0xf0) == 0x10) + break; + /* FALLTHROUGH */ + default: + dev_warn(navpoint->dev, + "spurious packet: data=0x%02x,0x%02x,...\n", + navpoint->data[0], navpoint->data[1]); + break; + } +} + +static irqreturn_t navpoint_irq(int irq, void *dev_id) +{ + struct navpoint *navpoint = dev_id; + struct ssp_device *ssp = navpoint->ssp; + irqreturn_t ret = IRQ_NONE; + u32 status; + + status = pxa_ssp_read_reg(ssp, SSSR); + if (status & sssr) { + dev_warn(navpoint->dev, + "unexpected interrupt: status=0x%08x\n", status); + pxa_ssp_write_reg(ssp, SSSR, (status & sssr)); + ret = IRQ_HANDLED; + } + + while (status & SSSR_RNE) { + u32 data; + + data = pxa_ssp_read_reg(ssp, SSDR); + navpoint->data[navpoint->index + 0] = (data >> 8); + navpoint->data[navpoint->index + 1] = data; + navpoint->index += 2; + if (HEADER_LENGTH(navpoint->data[0]) < navpoint->index) { + navpoint_packet(navpoint); + navpoint->index = 0; + } + status = pxa_ssp_read_reg(ssp, SSSR); + ret = IRQ_HANDLED; + } + + return ret; +} + +static void navpoint_up(struct navpoint *navpoint) +{ + struct ssp_device *ssp = navpoint->ssp; + int timeout; + + clk_prepare_enable(ssp->clk); + + pxa_ssp_write_reg(ssp, SSCR1, sscr1); + pxa_ssp_write_reg(ssp, SSSR, sssr); + pxa_ssp_write_reg(ssp, SSTO, 0); + pxa_ssp_write_reg(ssp, SSCR0, sscr0); /* SSCR0_SSE written last */ + + /* Wait until SSP port is ready for slave clock operations */ + for (timeout = 100; timeout != 0; --timeout) { + if (!(pxa_ssp_read_reg(ssp, SSSR) & SSSR_CSS)) + break; + msleep(1); + } + + if (timeout == 0) + dev_err(navpoint->dev, + "timeout waiting for SSSR[CSS] to clear\n"); + + if (gpio_is_valid(navpoint->gpio)) + gpio_set_value(navpoint->gpio, 1); +} + +static void navpoint_down(struct navpoint *navpoint) +{ + struct ssp_device *ssp = navpoint->ssp; + + if (gpio_is_valid(navpoint->gpio)) + gpio_set_value(navpoint->gpio, 0); + + pxa_ssp_write_reg(ssp, SSCR0, 0); + + clk_disable_unprepare(ssp->clk); +} + +static int navpoint_open(struct input_dev *input) +{ + struct navpoint *navpoint = input_get_drvdata(input); + + navpoint_up(navpoint); + + return 0; +} + +static void navpoint_close(struct input_dev *input) +{ + struct navpoint *navpoint = input_get_drvdata(input); + + navpoint_down(navpoint); +} + +static int __devinit navpoint_probe(struct platform_device *pdev) +{ + const struct navpoint_platform_data *pdata = + dev_get_platdata(&pdev->dev); + struct ssp_device *ssp; + struct input_dev *input; + struct navpoint *navpoint; + int error; + + if (!pdata) { + dev_err(&pdev->dev, "no platform data\n"); + return -EINVAL; + } + + if (gpio_is_valid(pdata->gpio)) { + error = gpio_request_one(pdata->gpio, GPIOF_OUT_INIT_LOW, + "SYNAPTICS_ON"); + if (error) + return error; + } + + ssp = pxa_ssp_request(pdata->port, pdev->name); + if (!ssp) { + error = -ENODEV; + goto err_free_gpio; + } + + /* HaRET does not disable devices before jumping into Linux */ + if (pxa_ssp_read_reg(ssp, SSCR0) & SSCR0_SSE) { + pxa_ssp_write_reg(ssp, SSCR0, 0); + dev_warn(&pdev->dev, "ssp%d already enabled\n", pdata->port); + } + + navpoint = kzalloc(sizeof(*navpoint), GFP_KERNEL); + input = input_allocate_device(); + if (!navpoint || !input) { + error = -ENOMEM; + goto err_free_mem; + } + + navpoint->ssp = ssp; + navpoint->input = input; + navpoint->dev = &pdev->dev; + navpoint->gpio = pdata->gpio; + + input->name = pdev->name; + input->dev.parent = &pdev->dev; + + __set_bit(EV_KEY, input->evbit); + __set_bit(EV_ABS, input->evbit); + __set_bit(BTN_LEFT, input->keybit); + __set_bit(BTN_TOUCH, input->keybit); + __set_bit(BTN_TOOL_FINGER, input->keybit); + + input_set_abs_params(input, ABS_X, + NAVPOINT_X_MIN, NAVPOINT_X_MAX, 0, 0); + input_set_abs_params(input, ABS_Y, + NAVPOINT_Y_MIN, NAVPOINT_Y_MAX, 0, 0); + input_set_abs_params(input, ABS_PRESSURE, + NAVPOINT_PRESSURE_MIN, NAVPOINT_PRESSURE_MAX, + 0, 0); + + input->open = navpoint_open; + input->close = navpoint_close; + + input_set_drvdata(input, navpoint); + + error = request_irq(ssp->irq, navpoint_irq, 0, pdev->name, navpoint); + if (error) + goto err_free_mem; + + error = input_register_device(input); + if (error) + goto err_free_irq; + + platform_set_drvdata(pdev, navpoint); + dev_dbg(&pdev->dev, "ssp%d, irq %d\n", pdata->port, ssp->irq); + + return 0; + +err_free_irq: + free_irq(ssp->irq, &pdev->dev); +err_free_mem: + input_free_device(input); + kfree(navpoint); + pxa_ssp_free(ssp); +err_free_gpio: + if (gpio_is_valid(pdata->gpio)) + gpio_free(pdata->gpio); + + return error; +} + +static int __devexit navpoint_remove(struct platform_device *pdev) +{ + const struct navpoint_platform_data *pdata = + dev_get_platdata(&pdev->dev); + struct navpoint *navpoint = platform_get_drvdata(pdev); + struct ssp_device *ssp = navpoint->ssp; + + free_irq(ssp->irq, navpoint); + + input_unregister_device(navpoint->input); + kfree(navpoint); + + pxa_ssp_free(ssp); + + if (gpio_is_valid(pdata->gpio)) + gpio_free(pdata->gpio); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int navpoint_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct navpoint *navpoint = platform_get_drvdata(pdev); + struct input_dev *input = navpoint->input; + + mutex_lock(&input->mutex); + if (input->users) + navpoint_down(navpoint); + mutex_unlock(&input->mutex); + + return 0; +} + +static int navpoint_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct navpoint *navpoint = platform_get_drvdata(pdev); + struct input_dev *input = navpoint->input; + + mutex_lock(&input->mutex); + if (input->users) + navpoint_up(navpoint); + mutex_unlock(&input->mutex); + + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(navpoint_pm_ops, navpoint_suspend, navpoint_resume); + +static struct platform_driver navpoint_driver = { + .probe = navpoint_probe, + .remove = __devexit_p(navpoint_remove), + .driver = { + .name = "navpoint", + .owner = THIS_MODULE, + .pm = &navpoint_pm_ops, + }, +}; + +module_platform_driver(navpoint_driver); + +MODULE_AUTHOR("Paul Parsons "); +MODULE_DESCRIPTION("Synaptics NavPoint (PXA27x SSP/SPI) driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:navpoint"); diff --git a/include/linux/input/navpoint.h b/include/linux/input/navpoint.h new file mode 100644 index 000000000000..45050eb34de3 --- /dev/null +++ b/include/linux/input/navpoint.h @@ -0,0 +1,12 @@ +/* + * Copyright (C) 2012 Paul Parsons + * + * 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. + */ + +struct navpoint_platform_data { + int port; /* PXA SSP port for pxa_ssp_request() */ + int gpio; /* GPIO for power on/off */ +}; -- cgit v1.2.3 From 1932811f426fee71b7ece67e70aeba7e1b0ebb6d Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 10 May 2012 22:37:08 -0700 Subject: Input: matrix-keymap - uninline and prepare for device tree support Change matrix-keymap helper to be out-of-line, like sparse keymap, allow the helper perform basic keymap validation and return errors, and prepare for device tree support. Signed-off-by: Dmitry Torokhov --- drivers/input/Kconfig | 17 +++- drivers/input/Makefile | 2 +- drivers/input/keyboard/Kconfig | 18 +++- drivers/input/keyboard/ep93xx_keypad.c | 18 ++-- drivers/input/keyboard/imx_keypad.c | 17 ++-- drivers/input/keyboard/lm8333.c | 15 ++- drivers/input/keyboard/matrix_keypad.c | 17 ++-- drivers/input/keyboard/nomadik-ske-keypad.c | 20 ++-- drivers/input/keyboard/omap-keypad.c | 20 ++-- drivers/input/keyboard/omap4-keypad.c | 15 ++- drivers/input/keyboard/pmic8xxx-keypad.c | 20 ++-- drivers/input/keyboard/samsung-keypad.c | 20 ++-- drivers/input/keyboard/spear-keyboard.c | 21 ++-- drivers/input/keyboard/stmpe-keypad.c | 16 ++-- drivers/input/keyboard/tc3589x-keypad.c | 19 ++-- drivers/input/keyboard/tca8418_keypad.c | 15 ++- drivers/input/keyboard/tegra-kbc.c | 34 ++++--- drivers/input/keyboard/tnetv107x-keypad.c | 21 ++-- drivers/input/keyboard/twl4030_keypad.c | 25 +++-- drivers/input/keyboard/w90p910_keypad.c | 27 +++--- drivers/input/matrix-keymap.c | 142 ++++++++++++++++++++++++++++ drivers/input/of_keymap.c | 87 ----------------- include/linux/input/matrix_keypad.h | 38 ++------ 23 files changed, 350 insertions(+), 294 deletions(-) create mode 100644 drivers/input/matrix-keymap.c delete mode 100644 drivers/input/of_keymap.c (limited to 'include/linux/input') diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 332597980817..55f7e57d4e42 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -25,10 +25,6 @@ config INPUT if INPUT -config INPUT_OF_MATRIX_KEYMAP - depends on USE_OF - bool - config INPUT_FF_MEMLESS tristate "Support for memoryless force-feedback devices" help @@ -68,6 +64,19 @@ config INPUT_SPARSEKMAP To compile this driver as a module, choose M here: the module will be called sparse-keymap. +config INPUT_MATRIXKMAP + tristate "Matrix keymap support library" + help + Say Y here if you are using a driver for an input + device that uses matrix keymap. This option is only + useful for out-of-tree drivers since in-tree drivers + select it automatically. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called matrix-keymap. + comment "Userland interfaces" config INPUT_MOUSEDEV diff --git a/drivers/input/Makefile b/drivers/input/Makefile index b173a13a73ca..5ca3f631497f 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -10,6 +10,7 @@ input-core-y := input.o input-compat.o input-mt.o ff-core.o obj-$(CONFIG_INPUT_FF_MEMLESS) += ff-memless.o obj-$(CONFIG_INPUT_POLLDEV) += input-polldev.o obj-$(CONFIG_INPUT_SPARSEKMAP) += sparse-keymap.o +obj-$(CONFIG_INPUT_MATRIXKMAP) += matrix-keymap.o obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o obj-$(CONFIG_INPUT_JOYDEV) += joydev.o @@ -24,4 +25,3 @@ obj-$(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/ obj-$(CONFIG_INPUT_MISC) += misc/ obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o -obj-$(CONFIG_INPUT_OF_MATRIX_KEYMAP) += of_keymap.o diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index ea4bbaae9e02..c0e11ecc646f 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -166,6 +166,7 @@ config KEYBOARD_LKKBD config KEYBOARD_EP93XX tristate "EP93xx Matrix Keypad support" depends on ARCH_EP93XX + select INPUT_MATRIXKMAP help Say Y here to enable the matrix keypad on the Cirrus EP93XX. @@ -224,6 +225,7 @@ config KEYBOARD_TCA6416 config KEYBOARD_TCA8418 tristate "TCA8418 Keypad Support" depends on I2C + select INPUT_MATRIXKMAP help This driver implements basic keypad functionality for keys connected through TCA8418 keypad decoder. @@ -240,6 +242,7 @@ config KEYBOARD_TCA8418 config KEYBOARD_MATRIX tristate "GPIO driven matrix keypad support" depends on GENERIC_GPIO + select INPUT_MATRIXKMAP help Enable support for GPIO driven matrix keypad. @@ -312,6 +315,7 @@ config KEYBOARD_LM8323 config KEYBOARD_LM8333 tristate "LM8333 keypad chip" depends on I2C + select INPUT_MATRIXKMAP help If you say yes here you get support for the National Semiconductor LM8333 keypad controller. @@ -376,6 +380,7 @@ config KEYBOARD_MPR121 config KEYBOARD_IMX tristate "IMX keypad support" depends on ARCH_MXC + select INPUT_MATRIXKMAP help Enable support for IMX keypad port. @@ -394,6 +399,7 @@ config KEYBOARD_NEWTON config KEYBOARD_NOMADIK tristate "ST-Ericsson Nomadik SKE keyboard" depends on PLAT_NOMADIK + select INPUT_MATRIXKMAP help Say Y here if you want to use a keypad provided on the SKE controller used on the Ux500 and Nomadik platforms @@ -404,7 +410,7 @@ config KEYBOARD_NOMADIK config KEYBOARD_TEGRA tristate "NVIDIA Tegra internal matrix keyboard controller support" depends on ARCH_TEGRA - select INPUT_OF_MATRIX_KEYMAP if USE_OF + select INPUT_MATRIXKMAP help Say Y here if you want to use a matrix keyboard connected directly to the internal keyboard controller on Tegra SoCs. @@ -442,6 +448,7 @@ config KEYBOARD_PXA930_ROTARY config KEYBOARD_PMIC8XXX tristate "Qualcomm PMIC8XXX keypad support" depends on MFD_PM8XXX + select INPUT_MATRIXKMAP help Say Y here if you want to enable the driver for the PMIC8XXX keypad provided as a reference design from Qualcomm. This is intended @@ -453,6 +460,7 @@ config KEYBOARD_PMIC8XXX config KEYBOARD_SAMSUNG tristate "Samsung keypad support" depends on HAVE_CLK + select INPUT_MATRIXKMAP help Say Y here if you want to use the keypad on your Samsung mobile device. @@ -495,6 +503,7 @@ config KEYBOARD_SH_KEYSC config KEYBOARD_STMPE tristate "STMPE keypad support" depends on MFD_STMPE + select INPUT_MATRIXKMAP help Say Y here if you want to use the keypad controller on STMPE I/O expanders. @@ -515,6 +524,7 @@ config KEYBOARD_DAVINCI config KEYBOARD_OMAP tristate "TI OMAP keypad support" depends on (ARCH_OMAP1 || ARCH_OMAP2) + select INPUT_MATRIXKMAP help Say Y here if you want to use the OMAP keypad. @@ -523,6 +533,7 @@ config KEYBOARD_OMAP config KEYBOARD_OMAP4 tristate "TI OMAP4+ keypad support" + select INPUT_MATRIXKMAP help Say Y here if you want to use the OMAP4+ keypad. @@ -532,6 +543,7 @@ config KEYBOARD_OMAP4 config KEYBOARD_SPEAR tristate "ST SPEAR keyboard support" depends on PLAT_SPEAR + select INPUT_MATRIXKMAP help Say Y here if you want to use the SPEAR keyboard. @@ -541,6 +553,7 @@ config KEYBOARD_SPEAR config KEYBOARD_TC3589X tristate "TC3589X Keypad support" depends on MFD_TC3589X + select INPUT_MATRIXKMAP help Say Y here if you want to use the keypad controller on TC35892/3 I/O expander. @@ -551,6 +564,7 @@ config KEYBOARD_TC3589X config KEYBOARD_TNETV107X tristate "TI TNETV107X keypad support" depends on ARCH_DAVINCI_TNETV107X + select INPUT_MATRIXKMAP help Say Y here if you want to use the TNETV107X keypad. @@ -560,6 +574,7 @@ config KEYBOARD_TNETV107X config KEYBOARD_TWL4030 tristate "TI TWL4030/TWL5030/TPS659x0 keypad support" depends on TWL4030_CORE + select INPUT_MATRIXKMAP help Say Y here if your board use the keypad controller on TWL4030 family chips. It's safe to say enable this @@ -583,6 +598,7 @@ config KEYBOARD_XTKBD config KEYBOARD_W90P910 tristate "W90P910 Matrix Keypad support" depends on ARCH_W90X900 + select INPUT_MATRIXKMAP help Say Y here to enable the matrix keypad on evaluation board based on W90P910. diff --git a/drivers/input/keyboard/ep93xx_keypad.c b/drivers/input/keyboard/ep93xx_keypad.c index df194bdaab50..c46fc8185469 100644 --- a/drivers/input/keyboard/ep93xx_keypad.c +++ b/drivers/input/keyboard/ep93xx_keypad.c @@ -303,19 +303,16 @@ static int __devinit ep93xx_keypad_probe(struct platform_device *pdev) input_dev->open = ep93xx_keypad_open; input_dev->close = ep93xx_keypad_close; input_dev->dev.parent = &pdev->dev; - input_dev->keycode = keypad->keycodes; - input_dev->keycodesize = sizeof(keypad->keycodes[0]); - input_dev->keycodemax = ARRAY_SIZE(keypad->keycodes); - input_set_drvdata(input_dev, keypad); + err = matrix_keypad_build_keymap(keymap_data, NULL, + EP93XX_MATRIX_ROWS, EP93XX_MATRIX_COLS, + keypad->keycodes, input_dev); + if (err) + goto failed_free_dev; - input_dev->evbit[0] = BIT_MASK(EV_KEY); if (keypad->pdata->flags & EP93XX_KEYPAD_AUTOREPEAT) - input_dev->evbit[0] |= BIT_MASK(EV_REP); - - matrix_keypad_build_keymap(keymap_data, 3, - input_dev->keycode, input_dev->keybit); - platform_set_drvdata(pdev, keypad); + __set_bit(EV_REP, input_dev->evbit); + input_set_drvdata(input_dev, keypad); err = request_irq(keypad->irq, ep93xx_keypad_irq_handler, 0, pdev->name, keypad); @@ -326,6 +323,7 @@ static int __devinit ep93xx_keypad_probe(struct platform_device *pdev) if (err) goto failed_free_irq; + platform_set_drvdata(pdev, keypad); device_init_wakeup(&pdev->dev, 1); return 0; diff --git a/drivers/input/keyboard/imx_keypad.c b/drivers/input/keyboard/imx_keypad.c index fb87b3bcadb9..6ee7421e2321 100644 --- a/drivers/input/keyboard/imx_keypad.c +++ b/drivers/input/keyboard/imx_keypad.c @@ -481,7 +481,7 @@ static int __devinit imx_keypad_probe(struct platform_device *pdev) } if (keypad->rows_en_mask > ((1 << MAX_MATRIX_KEY_ROWS) - 1) || - keypad->cols_en_mask > ((1 << MAX_MATRIX_KEY_COLS) - 1)) { + keypad->cols_en_mask > ((1 << MAX_MATRIX_KEY_COLS) - 1)) { dev_err(&pdev->dev, "invalid key data (too many rows or colums)\n"); error = -EINVAL; @@ -496,14 +496,17 @@ static int __devinit imx_keypad_probe(struct platform_device *pdev) input_dev->dev.parent = &pdev->dev; input_dev->open = imx_keypad_open; input_dev->close = imx_keypad_close; - input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); - input_dev->keycode = keypad->keycodes; - input_dev->keycodesize = sizeof(keypad->keycodes[0]); - input_dev->keycodemax = ARRAY_SIZE(keypad->keycodes); - matrix_keypad_build_keymap(keymap_data, MATRIX_ROW_SHIFT, - keypad->keycodes, input_dev->keybit); + error = matrix_keypad_build_keymap(keymap_data, NULL, + MAX_MATRIX_KEY_ROWS, + MAX_MATRIX_KEY_COLS, + keypad->keycodes, input_dev); + if (error) { + dev_err(&pdev->dev, "failed to build keymap\n"); + goto failed_clock_put; + } + __set_bit(EV_REP, input_dev->evbit); input_set_capability(input_dev, EV_MSC, MSC_SCAN); input_set_drvdata(input_dev, keypad); diff --git a/drivers/input/keyboard/lm8333.c b/drivers/input/keyboard/lm8333.c index 9a8c4a6cf5c6..ca168a6679de 100644 --- a/drivers/input/keyboard/lm8333.c +++ b/drivers/input/keyboard/lm8333.c @@ -29,9 +29,9 @@ #define LM8333_FIFO_TRANSFER_SIZE 16 -#define LM8333_ROW_SHIFT 4 #define LM8333_NUM_ROWS 8 - +#define LM8333_NUM_COLS 16 +#define LM8333_ROW_SHIFT 4 struct lm8333 { struct i2c_client *client; @@ -159,14 +159,13 @@ static int __devinit lm8333_probe(struct i2c_client *client, input->dev.parent = &client->dev; input->id.bustype = BUS_I2C; - input->keycode = lm8333->keycodes; - input->keycodesize = sizeof(lm8333->keycodes[0]); - input->keycodemax = ARRAY_SIZE(lm8333->keycodes); - input->evbit[0] = BIT_MASK(EV_KEY); input_set_capability(input, EV_MSC, MSC_SCAN); - matrix_keypad_build_keymap(pdata->matrix_data, LM8333_ROW_SHIFT, - input->keycode, input->keybit); + err = matrix_keypad_build_keymap(pdata->matrix_data, NULL, + LM8333_NUM_ROWS, LM8333_NUM_COLS, + lm8333->keycodes, input); + if (err) + goto free_mem; if (pdata->debounce_time) { err = lm8333_write8(lm8333, LM8333_DEBOUNCE, diff --git a/drivers/input/keyboard/matrix_keypad.c b/drivers/input/keyboard/matrix_keypad.c index 98ae281bedb0..18b72372028a 100644 --- a/drivers/input/keyboard/matrix_keypad.c +++ b/drivers/input/keyboard/matrix_keypad.c @@ -437,19 +437,18 @@ static int __devinit matrix_keypad_probe(struct platform_device *pdev) input_dev->name = pdev->name; input_dev->id.bustype = BUS_HOST; input_dev->dev.parent = &pdev->dev; - input_dev->evbit[0] = BIT_MASK(EV_KEY); - if (!pdata->no_autorepeat) - input_dev->evbit[0] |= BIT_MASK(EV_REP); input_dev->open = matrix_keypad_start; input_dev->close = matrix_keypad_stop; - input_dev->keycode = keypad->keycodes; - input_dev->keycodesize = sizeof(keypad->keycodes[0]); - input_dev->keycodemax = pdata->num_row_gpios << row_shift; - - matrix_keypad_build_keymap(keymap_data, row_shift, - input_dev->keycode, input_dev->keybit); + err = matrix_keypad_build_keymap(keymap_data, NULL, + pdata->num_row_gpios, + pdata->num_col_gpios, + keypad->keycodes, input_dev); + if (err) + goto err_free_mem; + if (!pdata->no_autorepeat) + __set_bit(EV_REP, input_dev->evbit); input_set_capability(input_dev, EV_MSC, MSC_SCAN); input_set_drvdata(input_dev, keypad); diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c index 101e245944e7..4ea4341a68c5 100644 --- a/drivers/input/keyboard/nomadik-ske-keypad.c +++ b/drivers/input/keyboard/nomadik-ske-keypad.c @@ -39,7 +39,8 @@ #define SKE_KPRISA (0x1 << 2) #define SKE_KEYPAD_ROW_SHIFT 3 -#define SKE_KPD_KEYMAP_SIZE (8 * 8) +#define SKE_KPD_NUM_ROWS 8 +#define SKE_KPD_NUM_COLS 8 /* keypad auto scan registers */ #define SKE_ASR0 0x20 @@ -63,7 +64,7 @@ struct ske_keypad { void __iomem *reg_base; struct input_dev *input; const struct ske_keypad_platform_data *board; - unsigned short keymap[SKE_KPD_KEYMAP_SIZE]; + unsigned short keymap[SKE_KPD_NUM_ROWS * SKE_KPD_NUM_COLS]; struct clk *clk; spinlock_t ske_keypad_lock; }; @@ -261,19 +262,18 @@ static int __init ske_keypad_probe(struct platform_device *pdev) input->name = "ux500-ske-keypad"; input->dev.parent = &pdev->dev; - input->keycode = keypad->keymap; - input->keycodesize = sizeof(keypad->keymap[0]); - input->keycodemax = ARRAY_SIZE(keypad->keymap); + error = matrix_keypad_build_keymap(plat->keymap_data, NULL, + SKE_KPD_NUM_ROWS, SKE_KPD_NUM_COLS, + keypad->keymap, input); + if (error) { + dev_err(&pdev->dev, "Failed to build keymap\n"); + goto err_iounmap; + } input_set_capability(input, EV_MSC, MSC_SCAN); - - __set_bit(EV_KEY, input->evbit); if (!plat->no_autorepeat) __set_bit(EV_REP, input->evbit); - matrix_keypad_build_keymap(plat->keymap_data, SKE_KEYPAD_ROW_SHIFT, - input->keycode, input->keybit); - clk_enable(keypad->clk); /* go through board initialization helpers */ diff --git a/drivers/input/keyboard/omap-keypad.c b/drivers/input/keyboard/omap-keypad.c index 6b630d9d3dff..a0222db4dc86 100644 --- a/drivers/input/keyboard/omap-keypad.c +++ b/drivers/input/keyboard/omap-keypad.c @@ -61,6 +61,7 @@ struct omap_kp { unsigned int cols; unsigned long delay; unsigned int debounce; + unsigned short keymap[]; }; static DECLARE_TASKLET_DISABLED(kp_tasklet, omap_kp_tasklet, 0); @@ -316,13 +317,6 @@ static int __devinit omap_kp_probe(struct platform_device *pdev) if (!cpu_is_omap24xx()) omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); - input_dev->keycode = &omap_kp[1]; - input_dev->keycodesize = sizeof(unsigned short); - input_dev->keycodemax = keycodemax; - - if (pdata->rep) - __set_bit(EV_REP, input_dev->evbit); - if (pdata->delay) omap_kp->delay = pdata->delay; @@ -371,9 +365,6 @@ static int __devinit omap_kp_probe(struct platform_device *pdev) goto err2; /* setup input device */ - __set_bit(EV_KEY, input_dev->evbit); - matrix_keypad_build_keymap(pdata->keymap_data, row_shift, - input_dev->keycode, input_dev->keybit); input_dev->name = "omap-keypad"; input_dev->phys = "omap-keypad/input0"; input_dev->dev.parent = &pdev->dev; @@ -383,6 +374,15 @@ static int __devinit omap_kp_probe(struct platform_device *pdev) input_dev->id.product = 0x0001; input_dev->id.version = 0x0100; + if (pdata->rep) + __set_bit(EV_REP, input_dev->evbit); + + ret = matrix_keypad_build_keymap(pdata->keymap_data, NULL, + pdata->rows, pdata->cols, + omap_kp->keymap, input_dev); + if (ret < 0) + goto err3; + ret = input_register_device(omap_kp->input); if (ret < 0) { printk(KERN_ERR "Unable to register omap-keypad input device\n"); diff --git a/drivers/input/keyboard/omap4-keypad.c b/drivers/input/keyboard/omap4-keypad.c index 28da0e469689..aed5f6999ce2 100644 --- a/drivers/input/keyboard/omap4-keypad.c +++ b/drivers/input/keyboard/omap4-keypad.c @@ -322,20 +322,19 @@ static int __devinit omap4_keypad_probe(struct platform_device *pdev) input_dev->open = omap4_keypad_open; input_dev->close = omap4_keypad_close; - input_dev->keycode = keypad_data->keymap; - input_dev->keycodesize = sizeof(keypad_data->keymap[0]); - input_dev->keycodemax = max_keys; + error = matrix_keypad_build_keymap(pdata->keymap_data, NULL, + pdata->rows, pdata->cols, + keypad_data->keymap, input_dev); + if (error) { + dev_err(&pdev->dev, "failed to build keymap\n"); + goto err_free_input; + } - __set_bit(EV_KEY, input_dev->evbit); __set_bit(EV_REP, input_dev->evbit); - input_set_capability(input_dev, EV_MSC, MSC_SCAN); input_set_drvdata(input_dev, keypad_data); - matrix_keypad_build_keymap(pdata->keymap_data, row_shift, - input_dev->keycode, input_dev->keybit); - error = request_irq(keypad_data->irq, omap4_keypad_interrupt, IRQF_TRIGGER_RISING, "omap4-keypad", keypad_data); diff --git a/drivers/input/keyboard/pmic8xxx-keypad.c b/drivers/input/keyboard/pmic8xxx-keypad.c index 01a1c9f8a383..52c34657d301 100644 --- a/drivers/input/keyboard/pmic8xxx-keypad.c +++ b/drivers/input/keyboard/pmic8xxx-keypad.c @@ -626,21 +626,21 @@ static int __devinit pmic8xxx_kp_probe(struct platform_device *pdev) kp->input->id.product = 0x0001; kp->input->id.vendor = 0x0001; - kp->input->evbit[0] = BIT_MASK(EV_KEY); - - if (pdata->rep) - __set_bit(EV_REP, kp->input->evbit); - - kp->input->keycode = kp->keycodes; - kp->input->keycodemax = PM8XXX_MATRIX_MAX_SIZE; - kp->input->keycodesize = sizeof(kp->keycodes); kp->input->open = pmic8xxx_kp_open; kp->input->close = pmic8xxx_kp_close; - matrix_keypad_build_keymap(keymap_data, PM8XXX_ROW_SHIFT, - kp->input->keycode, kp->input->keybit); + rc = matrix_keypad_build_keymap(keymap_data, NULL, + PM8XXX_MAX_ROWS, PM8XXX_MAX_COLS, + kp->keycodes, kp->input); + if (rc) { + dev_err(&pdev->dev, "failed to build keymap\n"); + goto err_get_irq; + } + if (pdata->rep) + __set_bit(EV_REP, kp->input->evbit); input_set_capability(kp->input, EV_MSC, MSC_SCAN); + input_set_drvdata(kp->input, kp); /* initialize keypad state */ diff --git a/drivers/input/keyboard/samsung-keypad.c b/drivers/input/keyboard/samsung-keypad.c index 2391ae884fee..a061ba603a29 100644 --- a/drivers/input/keyboard/samsung-keypad.c +++ b/drivers/input/keyboard/samsung-keypad.c @@ -454,23 +454,23 @@ static int __devinit samsung_keypad_probe(struct platform_device *pdev) input_dev->name = pdev->name; input_dev->id.bustype = BUS_HOST; input_dev->dev.parent = &pdev->dev; - input_set_drvdata(input_dev, keypad); input_dev->open = samsung_keypad_open; input_dev->close = samsung_keypad_close; - input_dev->evbit[0] = BIT_MASK(EV_KEY); - if (!pdata->no_autorepeat) - input_dev->evbit[0] |= BIT_MASK(EV_REP); + error = matrix_keypad_build_keymap(keymap_data, NULL, + pdata->rows, pdata->cols, + keypad->keycodes, input_dev); + if (error) { + dev_err(&pdev->dev, "failed to build keymap\n"); + goto err_put_clk; + } input_set_capability(input_dev, EV_MSC, MSC_SCAN); + if (!pdata->no_autorepeat) + __set_bit(EV_REP, input_dev->evbit); - input_dev->keycode = keypad->keycodes; - input_dev->keycodesize = sizeof(keypad->keycodes[0]); - input_dev->keycodemax = pdata->rows << row_shift; - - matrix_keypad_build_keymap(keymap_data, row_shift, - input_dev->keycode, input_dev->keybit); + input_set_drvdata(input_dev, keypad); keypad->irq = platform_get_irq(pdev, 0); if (keypad->irq < 0) { diff --git a/drivers/input/keyboard/spear-keyboard.c b/drivers/input/keyboard/spear-keyboard.c index 3b6b528f02fd..e83cab27eb56 100644 --- a/drivers/input/keyboard/spear-keyboard.c +++ b/drivers/input/keyboard/spear-keyboard.c @@ -49,7 +49,9 @@ #define KEY_VALUE 0x00FFFFFF #define ROW_MASK 0xF0 #define COLUMN_MASK 0x0F -#define ROW_SHIFT 4 +#define NUM_ROWS 16 +#define NUM_COLS 16 + #define KEY_MATRIX_SHIFT 6 struct spear_kbd { @@ -60,7 +62,7 @@ struct spear_kbd { unsigned int irq; unsigned int mode; unsigned short last_key; - unsigned short keycodes[256]; + unsigned short keycodes[NUM_ROWS * NUM_COLS]; }; static irqreturn_t spear_kbd_interrupt(int irq, void *dev_id) @@ -212,18 +214,17 @@ static int __devinit spear_kbd_probe(struct platform_device *pdev) input_dev->open = spear_kbd_open; input_dev->close = spear_kbd_close; - __set_bit(EV_KEY, input_dev->evbit); + error = matrix_keypad_build_keymap(keymap, NULL, NUM_ROWS, NUM_COLS, + kbd->keycodes, input_dev); + if (error) { + dev_err(&pdev->dev, "Failed to build keymap\n"); + goto err_put_clk; + } + if (pdata->rep) __set_bit(EV_REP, input_dev->evbit); input_set_capability(input_dev, EV_MSC, MSC_SCAN); - input_dev->keycode = kbd->keycodes; - input_dev->keycodesize = sizeof(kbd->keycodes[0]); - input_dev->keycodemax = ARRAY_SIZE(kbd->keycodes); - - matrix_keypad_build_keymap(keymap, ROW_SHIFT, - input_dev->keycode, input_dev->keybit); - input_set_drvdata(input_dev, kbd); error = request_irq(irq, spear_kbd_interrupt, 0, "keyboard", kbd); diff --git a/drivers/input/keyboard/stmpe-keypad.c b/drivers/input/keyboard/stmpe-keypad.c index 9397cf9c625c..470a8778dec1 100644 --- a/drivers/input/keyboard/stmpe-keypad.c +++ b/drivers/input/keyboard/stmpe-keypad.c @@ -289,19 +289,17 @@ static int __devinit stmpe_keypad_probe(struct platform_device *pdev) input->id.bustype = BUS_I2C; input->dev.parent = &pdev->dev; - input_set_capability(input, EV_MSC, MSC_SCAN); + ret = matrix_keypad_build_keymap(plat->keymap_data, NULL, + STMPE_KEYPAD_MAX_ROWS, + STMPE_KEYPAD_MAX_COLS, + keypad->keymap, input); + if (ret) + goto out_freeinput; - __set_bit(EV_KEY, input->evbit); + input_set_capability(input, EV_MSC, MSC_SCAN); if (!plat->no_autorepeat) __set_bit(EV_REP, input->evbit); - input->keycode = keypad->keymap; - input->keycodesize = sizeof(keypad->keymap[0]); - input->keycodemax = ARRAY_SIZE(keypad->keymap); - - matrix_keypad_build_keymap(plat->keymap_data, STMPE_KEYPAD_ROW_SHIFT, - input->keycode, input->keybit); - for (i = 0; i < plat->keymap_data->keymap_size; i++) { unsigned int key = plat->keymap_data->keymap[i]; diff --git a/drivers/input/keyboard/tc3589x-keypad.c b/drivers/input/keyboard/tc3589x-keypad.c index f4da2a7a6970..7d498e698508 100644 --- a/drivers/input/keyboard/tc3589x-keypad.c +++ b/drivers/input/keyboard/tc3589x-keypad.c @@ -78,7 +78,7 @@ * @input: pointer to input device object * @board: keypad platform device * @krow: number of rows - * @kcol: number of coloumns + * @kcol: number of columns * @keymap: matrix scan code table for keycodes * @keypad_stopped: holds keypad status */ @@ -333,23 +333,22 @@ static int __devinit tc3589x_keypad_probe(struct platform_device *pdev) input->name = pdev->name; input->dev.parent = &pdev->dev; - input->keycode = keypad->keymap; - input->keycodesize = sizeof(keypad->keymap[0]); - input->keycodemax = ARRAY_SIZE(keypad->keymap); - input->open = tc3589x_keypad_open; input->close = tc3589x_keypad_close; - input_set_drvdata(input, keypad); + error = matrix_keypad_build_keymap(plat->keymap_data, NULL, + TC3589x_MAX_KPROW, TC3589x_MAX_KPCOL, + keypad->keymap, input); + if (error) { + dev_err(&pdev->dev, "Failed to build keymap\n"); + goto err_free_mem; + } input_set_capability(input, EV_MSC, MSC_SCAN); - - __set_bit(EV_KEY, input->evbit); if (!plat->no_autorepeat) __set_bit(EV_REP, input->evbit); - matrix_keypad_build_keymap(plat->keymap_data, 0x3, - input->keycode, input->keybit); + input_set_drvdata(input, keypad); error = request_threaded_irq(irq, NULL, tc3589x_keypad_irq, plat->irqtype, diff --git a/drivers/input/keyboard/tca8418_keypad.c b/drivers/input/keyboard/tca8418_keypad.c index 958ec107bfbc..5f87b28b3192 100644 --- a/drivers/input/keyboard/tca8418_keypad.c +++ b/drivers/input/keyboard/tca8418_keypad.c @@ -342,21 +342,20 @@ static int __devinit tca8418_keypad_probe(struct i2c_client *client, input->id.product = 0x001; input->id.version = 0x0001; - input->keycode = keypad_data->keymap; - input->keycodesize = sizeof(keypad_data->keymap[0]); - input->keycodemax = max_keys; + error = matrix_keypad_build_keymap(pdata->keymap_data, NULL, + pdata->rows, pdata->cols, + keypad_data->keymap, input); + if (error) { + dev_dbg(&client->dev, "Failed to build keymap\n"); + goto fail2; + } - __set_bit(EV_KEY, input->evbit); if (pdata->rep) __set_bit(EV_REP, input->evbit); - input_set_capability(input, EV_MSC, MSC_SCAN); input_set_drvdata(input, keypad_data); - matrix_keypad_build_keymap(pdata->keymap_data, row_shift, - input->keycode, input->keybit); - if (pdata->irq_is_gpio) client->irq = gpio_to_irq(client->irq); diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c index fe4ac95ca6c8..6722d376e898 100644 --- a/drivers/input/keyboard/tegra-kbc.c +++ b/drivers/input/keyboard/tegra-kbc.c @@ -686,6 +686,7 @@ static int __devinit tegra_kbc_probe(struct platform_device *pdev) int num_rows = 0; unsigned int debounce_cnt; unsigned int scan_time_rows; + unsigned int keymap_rows; if (!pdata) pdata = tegra_kbc_dt_parse_pdata(pdev); @@ -757,29 +758,34 @@ static int __devinit tegra_kbc_probe(struct platform_device *pdev) kbc->repoll_dly = KBC_ROW_SCAN_DLY + scan_time_rows + pdata->repeat_cnt; kbc->repoll_dly = DIV_ROUND_UP(kbc->repoll_dly, KBC_CYCLE_MS); + kbc->wakeup_key = pdata->wakeup_key; + kbc->use_fn_map = pdata->use_fn_map; + kbc->use_ghost_filter = pdata->use_ghost_filter; + input_dev->name = pdev->name; input_dev->id.bustype = BUS_HOST; input_dev->dev.parent = &pdev->dev; input_dev->open = tegra_kbc_open; input_dev->close = tegra_kbc_close; - input_set_drvdata(input_dev, kbc); - - input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); - input_set_capability(input_dev, EV_MSC, MSC_SCAN); - - input_dev->keycode = kbc->keycode; - input_dev->keycodesize = sizeof(kbc->keycode[0]); - input_dev->keycodemax = KBC_MAX_KEY; + keymap_rows = KBC_MAX_KEY; if (pdata->use_fn_map) - input_dev->keycodemax *= 2; + keymap_rows *= 2; - kbc->use_fn_map = pdata->use_fn_map; - kbc->use_ghost_filter = pdata->use_ghost_filter; keymap_data = pdata->keymap_data ?: &tegra_kbc_default_keymap_data; - matrix_keypad_build_keymap(keymap_data, KBC_ROW_SHIFT, - input_dev->keycode, input_dev->keybit); - kbc->wakeup_key = pdata->wakeup_key; + + err = matrix_keypad_build_keymap(keymap_data, NULL, + keymap_rows, KBC_MAX_COL, + kbc->keycode, input_dev); + if (err) { + dev_err(&pdev->dev, "failed to build keymap\n"); + goto err_put_clk; + } + + __set_bit(EV_REP, input_dev->evbit); + input_set_capability(input_dev, EV_MSC, MSC_SCAN); + + input_set_drvdata(input_dev, kbc); err = request_irq(kbc->irq, tegra_kbc_isr, IRQF_NO_SUSPEND | IRQF_TRIGGER_HIGH, pdev->name, kbc); diff --git a/drivers/input/keyboard/tnetv107x-keypad.c b/drivers/input/keyboard/tnetv107x-keypad.c index fb39c94b6fdd..a4a445fb7020 100644 --- a/drivers/input/keyboard/tnetv107x-keypad.c +++ b/drivers/input/keyboard/tnetv107x-keypad.c @@ -247,15 +247,11 @@ static int __devinit keypad_probe(struct platform_device *pdev) error = -ENOMEM; goto error_input; } - input_set_drvdata(kp->input_dev, kp); kp->input_dev->name = pdev->name; kp->input_dev->dev.parent = &pdev->dev; kp->input_dev->open = keypad_start; kp->input_dev->close = keypad_stop; - kp->input_dev->evbit[0] = BIT_MASK(EV_KEY); - if (!pdata->no_autorepeat) - kp->input_dev->evbit[0] |= BIT_MASK(EV_REP); clk_enable(kp->clk); rev = keypad_read(kp, rev); @@ -264,15 +260,20 @@ static int __devinit keypad_probe(struct platform_device *pdev) kp->input_dev->id.version = ((rev >> 16) & 0xfff); clk_disable(kp->clk); - kp->input_dev->keycode = kp->keycodes; - kp->input_dev->keycodesize = sizeof(kp->keycodes[0]); - kp->input_dev->keycodemax = kp->rows << kp->row_shift; - - matrix_keypad_build_keymap(keymap_data, kp->row_shift, kp->keycodes, - kp->input_dev->keybit); + error = matrix_keypad_build_keymap(keymap_data, NULL, + kp->rows, kp->cols, + kp->keycodes, kp->input_dev); + if (error) { + dev_err(dev, "Failed to build keymap\n"); + goto error_reg; + } + if (!pdata->no_autorepeat) + kp->input_dev->evbit[0] |= BIT_MASK(EV_REP); input_set_capability(kp->input_dev, EV_MSC, MSC_SCAN); + input_set_drvdata(kp->input_dev, kp); + error = input_register_device(kp->input_dev); if (error < 0) { dev_err(dev, "Could not register input device\n"); diff --git a/drivers/input/keyboard/twl4030_keypad.c b/drivers/input/keyboard/twl4030_keypad.c index 67bec14e8b96..a2c6f79aa101 100644 --- a/drivers/input/keyboard/twl4030_keypad.c +++ b/drivers/input/keyboard/twl4030_keypad.c @@ -361,14 +361,6 @@ static int __devinit twl4030_kp_probe(struct platform_device *pdev) kp->irq = platform_get_irq(pdev, 0); /* setup input device */ - __set_bit(EV_KEY, input->evbit); - - /* Enable auto repeat feature of Linux input subsystem */ - if (pdata->rep) - __set_bit(EV_REP, input->evbit); - - input_set_capability(input, EV_MSC, MSC_SCAN); - input->name = "TWL4030 Keypad"; input->phys = "twl4030_keypad/input0"; input->dev.parent = &pdev->dev; @@ -378,12 +370,19 @@ static int __devinit twl4030_kp_probe(struct platform_device *pdev) input->id.product = 0x0001; input->id.version = 0x0003; - input->keycode = kp->keymap; - input->keycodesize = sizeof(kp->keymap[0]); - input->keycodemax = ARRAY_SIZE(kp->keymap); + error = matrix_keypad_build_keymap(keymap_data, NULL, + TWL4030_MAX_ROWS, + 1 << TWL4030_ROW_SHIFT, + kp->keymap, input); + if (error) { + dev_err(kp->dbg_dev, "Failed to build keymap\n"); + goto err1; + } - matrix_keypad_build_keymap(keymap_data, TWL4030_ROW_SHIFT, - input->keycode, input->keybit); + input_set_capability(input, EV_MSC, MSC_SCAN); + /* Enable auto repeat feature of Linux input subsystem */ + if (pdata->rep) + __set_bit(EV_REP, input->evbit); error = input_register_device(input); if (error) { diff --git a/drivers/input/keyboard/w90p910_keypad.c b/drivers/input/keyboard/w90p910_keypad.c index 99bbb7e775ae..085ede4d972d 100644 --- a/drivers/input/keyboard/w90p910_keypad.c +++ b/drivers/input/keyboard/w90p910_keypad.c @@ -42,7 +42,8 @@ #define KGET_RAW(n) (((n) & KEY0R) >> 3) #define KGET_COLUMN(n) ((n) & KEY0C) -#define W90P910_MAX_KEY_NUM (8 * 8) +#define W90P910_NUM_ROWS 8 +#define W90P910_NUM_COLS 8 #define W90P910_ROW_SHIFT 3 struct w90p910_keypad { @@ -51,7 +52,7 @@ struct w90p910_keypad { struct input_dev *input_dev; void __iomem *mmio_base; int irq; - unsigned short keymap[W90P910_MAX_KEY_NUM]; + unsigned short keymap[W90P910_NUM_ROWS * W90P910_NUM_COLS]; }; static void w90p910_keypad_scan_matrix(struct w90p910_keypad *keypad, @@ -190,17 +191,13 @@ static int __devinit w90p910_keypad_probe(struct platform_device *pdev) input_dev->close = w90p910_keypad_close; input_dev->dev.parent = &pdev->dev; - input_dev->keycode = keypad->keymap; - input_dev->keycodesize = sizeof(keypad->keymap[0]); - input_dev->keycodemax = ARRAY_SIZE(keypad->keymap); - - input_set_drvdata(input_dev, keypad); - - input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); - input_set_capability(input_dev, EV_MSC, MSC_SCAN); - - matrix_keypad_build_keymap(keymap_data, W90P910_ROW_SHIFT, - input_dev->keycode, input_dev->keybit); + error = matrix_keypad_build_keymap(keymap_data, NULL, + W90P910_NUM_ROWS, W90P910_NUM_COLS, + keypad->keymap, input_dev); + if (error) { + dev_err(&pdev->dev, "failed to build keymap\n"); + goto failed_put_clk; + } error = request_irq(keypad->irq, w90p910_keypad_irq_handler, 0, pdev->name, keypad); @@ -209,6 +206,10 @@ static int __devinit w90p910_keypad_probe(struct platform_device *pdev) goto failed_put_clk; } + __set_bit(EV_REP, input_dev->evbit); + input_set_capability(input_dev, EV_MSC, MSC_SCAN); + input_set_drvdata(input_dev, keypad); + /* Register the input device */ error = input_register_device(input_dev); if (error) { diff --git a/drivers/input/matrix-keymap.c b/drivers/input/matrix-keymap.c new file mode 100644 index 000000000000..de7992d55da2 --- /dev/null +++ b/drivers/input/matrix-keymap.c @@ -0,0 +1,142 @@ +/* + * Helpers for matrix keyboard bindings + * + * Copyright (C) 2012 Google, Inc + * + * Author: + * Olof Johansson + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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 +#include +#include +#include +#include +#include +#include +#include + + +/** + * matrix_keypad_build_keymap - convert platform keymap into matrix keymap + * @keymap_data: keymap supplied by the platform code + * @keymap_name: name of device tree property containing keymap (if device + * tree support is enabled). + * @rows: number of rows in target keymap array + * @cols: number of cols in target keymap array + * @keymap: expanded version of keymap that is suitable for use by + * matrix keyboard driver + * @input_dev: input devices for which we are setting up the keymap + * + * This function converts platform keymap (encoded with KEY() macro) into + * an array of keycodes that is suitable for using in a standard matrix + * keyboard driver that uses row and col as indices. + */ +int matrix_keypad_build_keymap(const struct matrix_keymap_data *keymap_data, + const char *keymap_name, + unsigned int rows, unsigned int cols, + unsigned short *keymap, + struct input_dev *input_dev) +{ + unsigned int row_shift = get_count_order(cols); + int i; + + input_dev->keycode = keymap; + input_dev->keycodesize = sizeof(*keymap); + input_dev->keycodemax = rows << row_shift; + + __set_bit(EV_KEY, input_dev->evbit); + + for (i = 0; i < keymap_data->keymap_size; i++) { + unsigned int key = keymap_data->keymap[i]; + unsigned int row = KEY_ROW(key); + unsigned int col = KEY_COL(key); + unsigned short code = KEY_VAL(key); + + if (row >= rows || col >= cols) { + dev_err(input_dev->dev.parent, + "%s: invalid keymap entry %d (row: %d, col: %d, rows: %d, cols: %d)\n", + __func__, i, row, col, rows, cols); + return -EINVAL; + } + + keymap[MATRIX_SCAN_CODE(row, col, row_shift)] = code; + __set_bit(code, input_dev->keybit); + } + __clear_bit(KEY_RESERVED, input_dev->keybit); + + return 0; +} +EXPORT_SYMBOL(matrix_keypad_build_keymap); + +#ifdef CONFIG_OF +struct matrix_keymap_data * +matrix_keyboard_of_fill_keymap(struct device_node *np, + const char *propname) +{ + struct matrix_keymap_data *kd; + u32 *keymap; + int proplen, i; + const __be32 *prop; + + if (!np) + return NULL; + + if (!propname) + propname = "linux,keymap"; + + prop = of_get_property(np, propname, &proplen); + if (!prop) + return NULL; + + if (proplen % sizeof(u32)) { + pr_warn("Malformed keymap property %s in %s\n", + propname, np->full_name); + return NULL; + } + + kd = kzalloc(sizeof(*kd), GFP_KERNEL); + if (!kd) + return NULL; + + kd->keymap = keymap = kzalloc(proplen, GFP_KERNEL); + if (!kd->keymap) { + kfree(kd); + return NULL; + } + + kd->keymap_size = proplen / sizeof(u32); + + for (i = 0; i < kd->keymap_size; i++) { + u32 tmp = be32_to_cpup(prop + i); + int key_code, row, col; + + row = (tmp >> 24) & 0xff; + col = (tmp >> 16) & 0xff; + key_code = tmp & 0xffff; + keymap[i] = KEY(row, col, key_code); + } + + return kd; +} +EXPORT_SYMBOL_GPL(matrix_keyboard_of_fill_keymap); + +void matrix_keyboard_of_free_keymap(const struct matrix_keymap_data *kd) +{ + if (kd) { + kfree(kd->keymap); + kfree(kd); + } +} +EXPORT_SYMBOL_GPL(matrix_keyboard_of_free_keymap); +#endif diff --git a/drivers/input/of_keymap.c b/drivers/input/of_keymap.c deleted file mode 100644 index 061493d57682..000000000000 --- a/drivers/input/of_keymap.c +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Helpers for open firmware matrix keyboard bindings - * - * Copyright (C) 2012 Google, Inc - * - * Author: - * Olof Johansson - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * 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 -#include -#include -#include -#include -#include -#include -#include - -struct matrix_keymap_data * -matrix_keyboard_of_fill_keymap(struct device_node *np, - const char *propname) -{ - struct matrix_keymap_data *kd; - u32 *keymap; - int proplen, i; - const __be32 *prop; - - if (!np) - return NULL; - - if (!propname) - propname = "linux,keymap"; - - prop = of_get_property(np, propname, &proplen); - if (!prop) - return NULL; - - if (proplen % sizeof(u32)) { - pr_warn("Malformed keymap property %s in %s\n", - propname, np->full_name); - return NULL; - } - - kd = kzalloc(sizeof(*kd), GFP_KERNEL); - if (!kd) - return NULL; - - kd->keymap = keymap = kzalloc(proplen, GFP_KERNEL); - if (!kd->keymap) { - kfree(kd); - return NULL; - } - - kd->keymap_size = proplen / sizeof(u32); - - for (i = 0; i < kd->keymap_size; i++) { - u32 tmp = be32_to_cpup(prop + i); - int key_code, row, col; - - row = (tmp >> 24) & 0xff; - col = (tmp >> 16) & 0xff; - key_code = tmp & 0xffff; - keymap[i] = KEY(row, col, key_code); - } - - return kd; -} -EXPORT_SYMBOL_GPL(matrix_keyboard_of_fill_keymap); - -void matrix_keyboard_of_free_keymap(const struct matrix_keymap_data *kd) -{ - if (kd) { - kfree(kd->keymap); - kfree(kd); - } -} -EXPORT_SYMBOL_GPL(matrix_keyboard_of_free_keymap); diff --git a/include/linux/input/matrix_keypad.h b/include/linux/input/matrix_keypad.h index 6c07ced0af81..e8fe08ebc245 100644 --- a/include/linux/input/matrix_keypad.h +++ b/include/linux/input/matrix_keypad.h @@ -75,39 +75,13 @@ struct matrix_keypad_platform_data { bool no_autorepeat; }; -/** - * matrix_keypad_build_keymap - convert platform keymap into matrix keymap - * @keymap_data: keymap supplied by the platform code - * @row_shift: number of bits to shift row value by to advance to the next - * line in the keymap - * @keymap: expanded version of keymap that is suitable for use by - * matrix keyboad driver - * @keybit: pointer to bitmap of keys supported by input device - * - * This function converts platform keymap (encoded with KEY() macro) into - * an array of keycodes that is suitable for using in a standard matrix - * keyboard driver that uses row and col as indices. - */ -static inline void -matrix_keypad_build_keymap(const struct matrix_keymap_data *keymap_data, - unsigned int row_shift, - unsigned short *keymap, unsigned long *keybit) -{ - int i; - - for (i = 0; i < keymap_data->keymap_size; i++) { - unsigned int key = keymap_data->keymap[i]; - unsigned int row = KEY_ROW(key); - unsigned int col = KEY_COL(key); - unsigned short code = KEY_VAL(key); - - keymap[MATRIX_SCAN_CODE(row, col, row_shift)] = code; - __set_bit(code, keybit); - } - __clear_bit(KEY_RESERVED, keybit); -} +int matrix_keypad_build_keymap(const struct matrix_keymap_data *keymap_data, + const char *keymap_name, + unsigned int rows, unsigned int cols, + unsigned short *keymap, + struct input_dev *input_dev); -#ifdef CONFIG_INPUT_OF_MATRIX_KEYMAP +#ifdef CONFIG_OF struct matrix_keymap_data * matrix_keyboard_of_fill_keymap(struct device_node *np, const char *propname); -- cgit v1.2.3 From b45c8f35a7fec17bdd159a7815203b6137c00ab9 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Thu, 10 May 2012 22:37:15 -0700 Subject: Input: matrix-keymap - wire up device tree support When platform keymap is not supplied to matrix_keypad_build_keymap() and device tree support is enabled, try locating specified property and load keymap from it. If property name is not defined, try using "linux,keymap". Based on earlier patch by Viresh Kumar Signed-off-by: Dmitry Torokhov --- drivers/input/keyboard/tegra-kbc.c | 56 +++++++----- drivers/input/matrix-keymap.c | 175 ++++++++++++++++++++---------------- include/linux/input/matrix_keypad.h | 18 ---- 3 files changed, 130 insertions(+), 119 deletions(-) (limited to 'include/linux/input') diff --git a/drivers/input/keyboard/tegra-kbc.c b/drivers/input/keyboard/tegra-kbc.c index 6722d376e898..4ffe64d53107 100644 --- a/drivers/input/keyboard/tegra-kbc.c +++ b/drivers/input/keyboard/tegra-kbc.c @@ -619,8 +619,8 @@ tegra_kbc_check_pin_cfg(const struct tegra_kbc_platform_data *pdata, } #ifdef CONFIG_OF -static struct tegra_kbc_platform_data * __devinit -tegra_kbc_dt_parse_pdata(struct platform_device *pdev) +static struct tegra_kbc_platform_data * __devinit tegra_kbc_dt_parse_pdata( + struct platform_device *pdev) { struct tegra_kbc_platform_data *pdata; struct device_node *np = pdev->dev.of_node; @@ -660,10 +660,6 @@ tegra_kbc_dt_parse_pdata(struct platform_device *pdev) pdata->pin_cfg[KBC_MAX_ROW + i].type = PIN_CFG_COL; } - pdata->keymap_data = matrix_keyboard_of_fill_keymap(np, "linux,keymap"); - - /* FIXME: Add handling of linux,fn-keymap here */ - return pdata; } #else @@ -674,10 +670,36 @@ static inline struct tegra_kbc_platform_data *tegra_kbc_dt_parse_pdata( } #endif +static int __devinit tegra_kbd_setup_keymap(struct tegra_kbc *kbc) +{ + const struct tegra_kbc_platform_data *pdata = kbc->pdata; + const struct matrix_keymap_data *keymap_data = pdata->keymap_data; + unsigned int keymap_rows = KBC_MAX_KEY; + int retval; + + if (keymap_data && pdata->use_fn_map) + keymap_rows *= 2; + + retval = matrix_keypad_build_keymap(keymap_data, NULL, + keymap_rows, KBC_MAX_COL, + kbc->keycode, kbc->idev); + if (retval == -ENOSYS || retval == -ENOENT) { + /* + * If there is no OF support in kernel or keymap + * property is missing, use default keymap. + */ + retval = matrix_keypad_build_keymap( + &tegra_kbc_default_keymap_data, NULL, + keymap_rows, KBC_MAX_COL, + kbc->keycode, kbc->idev); + } + + return retval; +} + static int __devinit tegra_kbc_probe(struct platform_device *pdev) { const struct tegra_kbc_platform_data *pdata = pdev->dev.platform_data; - const struct matrix_keymap_data *keymap_data; struct tegra_kbc *kbc; struct input_dev *input_dev; struct resource *res; @@ -686,7 +708,6 @@ static int __devinit tegra_kbc_probe(struct platform_device *pdev) int num_rows = 0; unsigned int debounce_cnt; unsigned int scan_time_rows; - unsigned int keymap_rows; if (!pdata) pdata = tegra_kbc_dt_parse_pdata(pdev); @@ -768,17 +789,9 @@ static int __devinit tegra_kbc_probe(struct platform_device *pdev) input_dev->open = tegra_kbc_open; input_dev->close = tegra_kbc_close; - keymap_rows = KBC_MAX_KEY; - if (pdata->use_fn_map) - keymap_rows *= 2; - - keymap_data = pdata->keymap_data ?: &tegra_kbc_default_keymap_data; - - err = matrix_keypad_build_keymap(keymap_data, NULL, - keymap_rows, KBC_MAX_COL, - kbc->keycode, input_dev); + err = tegra_kbd_setup_keymap(kbc); if (err) { - dev_err(&pdev->dev, "failed to build keymap\n"); + dev_err(&pdev->dev, "failed to setup keymap\n"); goto err_put_clk; } @@ -805,9 +818,6 @@ static int __devinit tegra_kbc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, kbc); device_init_wakeup(&pdev->dev, pdata->wakeup); - if (!pdev->dev.platform_data) - matrix_keyboard_of_free_keymap(pdata->keymap_data); - return 0; err_free_irq: @@ -822,10 +832,8 @@ err_free_mem: input_free_device(input_dev); kfree(kbc); err_free_pdata: - if (!pdev->dev.platform_data) { - matrix_keyboard_of_free_keymap(pdata->keymap_data); + if (!pdev->dev.platform_data) kfree(pdata); - } return err; } diff --git a/drivers/input/matrix-keymap.c b/drivers/input/matrix-keymap.c index de7992d55da2..db92c1ebfc59 100644 --- a/drivers/input/matrix-keymap.c +++ b/drivers/input/matrix-keymap.c @@ -17,15 +17,91 @@ * */ +#include #include #include #include #include #include -#include -#include #include +static bool matrix_keypad_map_key(struct input_dev *input_dev, + unsigned int rows, unsigned int cols, + unsigned int row_shift, unsigned int key) +{ + unsigned char *keymap = input_dev->keycode; + unsigned int row = KEY_ROW(key); + unsigned int col = KEY_COL(key); + unsigned short code = KEY_VAL(key); + + if (row >= rows || col >= cols) { + dev_err(input_dev->dev.parent, + "%s: invalid keymap entry 0x%x (row: %d, col: %d, rows: %d, cols: %d)\n", + __func__, key, row, col, rows, cols); + return false; + } + + keymap[MATRIX_SCAN_CODE(row, col, row_shift)] = code; + __set_bit(code, input_dev->keybit); + + return true; +} + +#ifdef CONFIG_OF +static int matrix_keypad_parse_of_keymap(const char *propname, + unsigned int rows, unsigned int cols, + struct input_dev *input_dev) +{ + struct device *dev = input_dev->dev.parent; + struct device_node *np = dev->of_node; + unsigned int row_shift = get_count_order(cols); + unsigned int max_keys = rows << row_shift; + unsigned int proplen, i, size; + const __be32 *prop; + + if (!np) + return -ENOENT; + + if (!propname) + propname = "linux,keymap"; + + prop = of_get_property(np, propname, &proplen); + if (!prop) { + dev_err(dev, "OF: %s property not defined in %s\n", + propname, np->full_name); + return -ENOENT; + } + + if (proplen % sizeof(u32)) { + dev_err(dev, "OF: Malformed keycode property %s in %s\n", + propname, np->full_name); + return -EINVAL; + } + + size = proplen / sizeof(u32); + if (size > max_keys) { + dev_err(dev, "OF: %s size overflow\n", propname); + return -EINVAL; + } + + for (i = 0; i < size; i++) { + unsigned int key = be32_to_cpup(prop + i); + + if (!matrix_keypad_map_key(input_dev, rows, cols, + row_shift, key)) + return -EINVAL; + } + + return 0; +} +#else +static int matrix_keypad_parse_of_keymap(const char *propname, + unsigned int rows, unsigned int cols, + struct input_dev *input_dev) +{ + return -ENOSYS; +} +#endif /** * matrix_keypad_build_keymap - convert platform keymap into matrix keymap @@ -41,6 +117,13 @@ * This function converts platform keymap (encoded with KEY() macro) into * an array of keycodes that is suitable for using in a standard matrix * keyboard driver that uses row and col as indices. + * + * If @keymap_data is not supplied and device tree support is enabled + * it will attempt load the keymap from property specified by @keymap_name + * argument (or "linux,keymap" if @keymap_name is %NULL). + * + * Callers are expected to set up input_dev->dev.parent before calling this + * function. */ int matrix_keypad_build_keymap(const struct matrix_keymap_data *keymap_data, const char *keymap_name, @@ -50,6 +133,7 @@ int matrix_keypad_build_keymap(const struct matrix_keymap_data *keymap_data, { unsigned int row_shift = get_count_order(cols); int i; + int error; input_dev->keycode = keymap; input_dev->keycodesize = sizeof(*keymap); @@ -57,86 +141,23 @@ int matrix_keypad_build_keymap(const struct matrix_keymap_data *keymap_data, __set_bit(EV_KEY, input_dev->evbit); - for (i = 0; i < keymap_data->keymap_size; i++) { - unsigned int key = keymap_data->keymap[i]; - unsigned int row = KEY_ROW(key); - unsigned int col = KEY_COL(key); - unsigned short code = KEY_VAL(key); + if (keymap_data) { + for (i = 0; i < keymap_data->keymap_size; i++) { + unsigned int key = keymap_data->keymap[i]; - if (row >= rows || col >= cols) { - dev_err(input_dev->dev.parent, - "%s: invalid keymap entry %d (row: %d, col: %d, rows: %d, cols: %d)\n", - __func__, i, row, col, rows, cols); - return -EINVAL; + if (!matrix_keypad_map_key(input_dev, rows, cols, + row_shift, key)) + return -EINVAL; } - - keymap[MATRIX_SCAN_CODE(row, col, row_shift)] = code; - __set_bit(code, input_dev->keybit); + } else { + error = matrix_keypad_parse_of_keymap(keymap_name, rows, cols, + input_dev); + if (error) + return error; } + __clear_bit(KEY_RESERVED, input_dev->keybit); return 0; } EXPORT_SYMBOL(matrix_keypad_build_keymap); - -#ifdef CONFIG_OF -struct matrix_keymap_data * -matrix_keyboard_of_fill_keymap(struct device_node *np, - const char *propname) -{ - struct matrix_keymap_data *kd; - u32 *keymap; - int proplen, i; - const __be32 *prop; - - if (!np) - return NULL; - - if (!propname) - propname = "linux,keymap"; - - prop = of_get_property(np, propname, &proplen); - if (!prop) - return NULL; - - if (proplen % sizeof(u32)) { - pr_warn("Malformed keymap property %s in %s\n", - propname, np->full_name); - return NULL; - } - - kd = kzalloc(sizeof(*kd), GFP_KERNEL); - if (!kd) - return NULL; - - kd->keymap = keymap = kzalloc(proplen, GFP_KERNEL); - if (!kd->keymap) { - kfree(kd); - return NULL; - } - - kd->keymap_size = proplen / sizeof(u32); - - for (i = 0; i < kd->keymap_size; i++) { - u32 tmp = be32_to_cpup(prop + i); - int key_code, row, col; - - row = (tmp >> 24) & 0xff; - col = (tmp >> 16) & 0xff; - key_code = tmp & 0xffff; - keymap[i] = KEY(row, col, key_code); - } - - return kd; -} -EXPORT_SYMBOL_GPL(matrix_keyboard_of_fill_keymap); - -void matrix_keyboard_of_free_keymap(const struct matrix_keymap_data *kd) -{ - if (kd) { - kfree(kd->keymap); - kfree(kd); - } -} -EXPORT_SYMBOL_GPL(matrix_keyboard_of_free_keymap); -#endif diff --git a/include/linux/input/matrix_keypad.h b/include/linux/input/matrix_keypad.h index e8fe08ebc245..5f3aa6b11bfa 100644 --- a/include/linux/input/matrix_keypad.h +++ b/include/linux/input/matrix_keypad.h @@ -81,22 +81,4 @@ int matrix_keypad_build_keymap(const struct matrix_keymap_data *keymap_data, unsigned short *keymap, struct input_dev *input_dev); -#ifdef CONFIG_OF -struct matrix_keymap_data * -matrix_keyboard_of_fill_keymap(struct device_node *np, const char *propname); - -void matrix_keyboard_of_free_keymap(const struct matrix_keymap_data *kd); -#else -static inline struct matrix_keymap_data * -matrix_keyboard_of_fill_keymap(struct device_node *np, const char *propname) -{ - return NULL; -} - -static inline void -matrix_keyboard_of_free_keymap(const struct matrix_keymap_data *kd) -{ -} -#endif - #endif /* _MATRIX_KEYPAD_H */ -- cgit v1.2.3