dect
/
linux-2.6
Archived
13
0
Fork 0

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input

Pull input layer updates from Dmitry Torokhov:
 "First set of updates for the input subsystem.  You will get a new
  touchscreen driver (Melfas mms114), a new keypad driver for LPC32xx
  SoC, large update to Atmel mXT touchscreen driver, a lot of drivers
  acquired device tree support and a slew of other fixes."

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input: (57 commits)
  Input: add MELFAS mms114 touchscreen driver
  Input: add support for key scan interface of the LPC32xx SoC
  Input: omap4-keypad - add device tree support
  Input: hanwang - add support for Art Master II tablet
  Input: spear_keyboard - reconfigure operating frequency on suspend
  Input: spear_keyboard - fix clock handling during suspend/resume
  Input: ff-memless - fix a couple min_t() casts
  Input: synaptics - print firmware ID and board number at init
  Input: spear_keyboard - generalize keyboard frequency configuration
  Input: spear_keyboard - rename bit definitions to reflect register
  Input: spear_keyboard - use correct io accessors
  Input: spear-keyboard - fix disable device_init_wakeup in remove
  Input: wacom_i2c - fix compiler warning
  Input: imx_keypad - check error returned by clk_prepare_enable()
  Input: imx_keypad - adapt the new kpp clock name
  Input: imx_keypad - use clk_prepare_enable/clk_disable_unprepare()
  Input: ad7879 - add option to correct xy axis
  Input: synaptics_usb - Remove TrackPoint name trailing whitespace
  Revert "Input: atmel_mxt_ts - warn if sysfs could not be created"
  Input: MT - Include win8 support
  ...
This commit is contained in:
Linus Torvalds 2012-07-26 12:59:53 -07:00
commit 945c40c6b0
33 changed files with 1841 additions and 504 deletions

View File

@ -0,0 +1,28 @@
NXP LPC32xx Key Scan Interface
Required Properties:
- compatible: Should be "nxp,lpc3220-key"
- reg: Physical base address of the controller and length of memory mapped
region.
- interrupts: The interrupt number to the cpu.
- keypad,num-rows: Number of rows and columns, e.g. 1: 1x1, 6: 6x6
- keypad,num-columns: Must be equal to keypad,num-rows since LPC32xx only
supports square matrices
- nxp,debounce-delay-ms: Debounce delay in ms
- nxp,scan-delay-ms: Repeated scan period in ms
- linux,keymap: the key-code to be reported when the key is pressed
and released, see also
Documentation/devicetree/bindings/input/matrix-keymap.txt
Example:
key@40050000 {
compatible = "nxp,lpc3220-key";
reg = <0x40050000 0x1000>;
interrupts = <54 0>;
keypad,num-rows = <1>;
keypad,num-columns = <1>;
nxp,debounce-delay-ms = <3>;
nxp,scan-delay-ms = <34>;
linux,keymap = <0x00000002>;
};

View File

@ -0,0 +1,31 @@
* TI's Keypad Controller device tree bindings
TI's Keypad controller is used to interface a SoC with a matrix-type
keypad device. The keypad controller supports multiple row and column lines.
A key can be placed at each intersection of a unique row and a unique column.
The keypad controller can sense a key-press and key-release and report the
event using a interrupt to the cpu.
Required SoC Specific Properties:
- compatible: should be one of the following
- "ti,omap4-keypad": For controllers compatible with omap4 keypad
controller.
Required Board Specific Properties, in addition to those specified by
the shared matrix-keyboard bindings:
- keypad,num-rows: Number of row lines connected to the keypad
controller.
- keypad,num-columns: Number of column lines connected to the
keypad controller.
Optional Properties specific to linux:
- linux,keypad-no-autorepeat: do no enable autorepeat feature.
Example:
keypad@4ae1c000{
compatible = "ti,omap4-keypad";
keypad,num-rows = <2>;
keypad,num-columns = <8>;
linux,keypad-no-autorepeat;
};

View File

@ -1,37 +0,0 @@
Vibra driver for the twl6040 family
The vibra driver is a child of the twl6040 MFD dirver.
Documentation/devicetree/bindings/mfd/twl6040.txt
Required properties:
- compatible : Must be "ti,twl6040-vibra";
- interrupts: 4, Vibra overcurrent interrupt
- vddvibl-supply: Regulator supplying the left vibra motor
- vddvibr-supply: Regulator supplying the right vibra motor
- vibldrv_res: Board specific left driver resistance
- vibrdrv_res: Board specific right driver resistance
- viblmotor_res: Board specific left motor resistance
- vibrmotor_res: Board specific right motor resistance
Optional properties:
- vddvibl_uV: If the vddvibl default voltage need to be changed
- vddvibr_uV: If the vddvibr default voltage need to be changed
Example:
/*
* 8-channel high quality low-power audio codec
* http://www.ti.com/lit/ds/symlink/twl6040.pdf
*/
twl6040: twl6040@4b {
...
twl6040_vibra: twl6040@1 {
compatible = "ti,twl6040-vibra";
interrupts = <4>;
vddvibl-supply = <&vbat>;
vddvibr-supply = <&vbat>;
vibldrv_res = <8>;
vibrdrv_res = <3>;
viblmotor_res = <10>;
vibrmotor_res = <10>;
};
};

View File

@ -162,26 +162,48 @@ are divided into categories, to allow for partial implementation. The
minimum set consists of ABS_MT_POSITION_X and ABS_MT_POSITION_Y, which
allows for multiple contacts to be tracked. If the device supports it, the
ABS_MT_TOUCH_MAJOR and ABS_MT_WIDTH_MAJOR may be used to provide the size
of the contact area and approaching contact, respectively.
of the contact area and approaching tool, respectively.
The TOUCH and WIDTH parameters have a geometrical interpretation; imagine
looking through a window at someone gently holding a finger against the
glass. You will see two regions, one inner region consisting of the part
of the finger actually touching the glass, and one outer region formed by
the perimeter of the finger. The diameter of the inner region is the
ABS_MT_TOUCH_MAJOR, the diameter of the outer region is
ABS_MT_WIDTH_MAJOR. Now imagine the person pressing the finger harder
against the glass. The inner region will increase, and in general, the
ratio ABS_MT_TOUCH_MAJOR / ABS_MT_WIDTH_MAJOR, which is always smaller than
unity, is related to the contact pressure. For pressure-based devices,
the perimeter of the finger. The center of the touching region (a) is
ABS_MT_POSITION_X/Y and the center of the approaching finger (b) is
ABS_MT_TOOL_X/Y. The touch diameter is ABS_MT_TOUCH_MAJOR and the finger
diameter is ABS_MT_WIDTH_MAJOR. Now imagine the person pressing the finger
harder against the glass. The touch region will increase, and in general,
the ratio ABS_MT_TOUCH_MAJOR / ABS_MT_WIDTH_MAJOR, which is always smaller
than unity, is related to the contact pressure. For pressure-based devices,
ABS_MT_PRESSURE may be used to provide the pressure on the contact area
instead. Devices capable of contact hovering can use ABS_MT_DISTANCE to
indicate the distance between the contact and the surface.
In addition to the MAJOR parameters, the oval shape of the contact can be
described by adding the MINOR parameters, such that MAJOR and MINOR are the
major and minor axis of an ellipse. Finally, the orientation of the oval
shape can be describe with the ORIENTATION parameter.
Linux MT Win8
__________ _______________________
/ \ | |
/ \ | |
/ ____ \ | |
/ / \ \ | |
\ \ a \ \ | a |
\ \____/ \ | |
\ \ | |
\ b \ | b |
\ \ | |
\ \ | |
\ \ | |
\ / | |
\ / | |
\ / | |
\__________/ |_______________________|
In addition to the MAJOR parameters, the oval shape of the touch and finger
regions can be described by adding the MINOR parameters, such that MAJOR
and MINOR are the major and minor axis of an ellipse. The orientation of
the touch ellipse can be described with the ORIENTATION parameter, and the
direction of the finger ellipse is given by the vector (a - b).
For type A devices, further specification of the touch shape is possible
via ABS_MT_BLOB_ID.
@ -224,7 +246,7 @@ tool. Omit if circular [4].
The above four values can be used to derive additional information about
the contact. The ratio ABS_MT_TOUCH_MAJOR / ABS_MT_WIDTH_MAJOR approximates
the notion of pressure. The fingers of the hand and the palm all have
different characteristic widths [1].
different characteristic widths.
ABS_MT_PRESSURE
@ -240,17 +262,24 @@ the contact is hovering above the surface.
ABS_MT_ORIENTATION
The orientation of the ellipse. The value should describe a signed quarter
of a revolution clockwise around the touch center. The signed value range
is arbitrary, but zero should be returned for a finger aligned along the Y
axis of the surface, a negative value when finger is turned to the left, and
a positive value when finger turned to the right. When completely aligned with
the X axis, the range max should be returned. Orientation can be omitted
if the touching object is circular, or if the information is not available
in the kernel driver. Partial orientation support is possible if the device
can distinguish between the two axis, but not (uniquely) any values in
between. In such cases, the range of ABS_MT_ORIENTATION should be [0, 1]
[4].
The orientation of the touching ellipse. The value should describe a signed
quarter of a revolution clockwise around the touch center. The signed value
range is arbitrary, but zero should be returned for an ellipse aligned with
the Y axis of the surface, a negative value when the ellipse is turned to
the left, and a positive value when the ellipse is turned to the
right. When completely aligned with the X axis, the range max should be
returned.
Touch ellipsis are symmetrical by default. For devices capable of true 360
degree orientation, the reported orientation must exceed the range max to
indicate more than a quarter of a revolution. For an upside-down finger,
range max * 2 should be returned.
Orientation can be omitted if the touch area is circular, or if the
information is not available in the kernel driver. Partial orientation
support is possible if the device can distinguish between the two axis, but
not (uniquely) any values in between. In such cases, the range of
ABS_MT_ORIENTATION should be [0, 1] [4].
ABS_MT_POSITION_X
@ -260,6 +289,23 @@ ABS_MT_POSITION_Y
The surface Y coordinate of the center of the touching ellipse.
ABS_MT_TOOL_X
The surface X coordinate of the center of the approaching tool. Omit if
the device cannot distinguish between the intended touch point and the
tool itself.
ABS_MT_TOOL_Y
The surface Y coordinate of the center of the approaching tool. Omit if the
device cannot distinguish between the intended touch point and the tool
itself.
The four position values can be used to separate the position of the touch
from the position of the tool. If both positions are present, the major
tool axis points towards the touch point [1]. Otherwise, the tool axes are
aligned with the touch axes.
ABS_MT_TOOL_TYPE
The type of approaching tool. A lot of kernel drivers cannot distinguish
@ -305,6 +351,28 @@ The range of ABS_MT_ORIENTATION should be set to [0, 1], to indicate that
the device can distinguish between a finger along the Y axis (0) and a
finger along the X axis (1).
For win8 devices with both T and C coordinates, the position mapping is
ABS_MT_POSITION_X := T_X
ABS_MT_POSITION_Y := T_Y
ABS_MT_TOOL_X := C_X
ABS_MT_TOOL_X := C_Y
Unfortunately, there is not enough information to specify both the touching
ellipse and the tool ellipse, so one has to resort to approximations. One
simple scheme, which is compatible with earlier usage, is:
ABS_MT_TOUCH_MAJOR := min(X, Y)
ABS_MT_TOUCH_MINOR := <not used>
ABS_MT_ORIENTATION := <not used>
ABS_MT_WIDTH_MAJOR := min(X, Y) + distance(T, C)
ABS_MT_WIDTH_MINOR := min(X, Y)
Rationale: We have no information about the orientation of the touching
ellipse, so approximate it with an inscribed circle instead. The tool
ellipse should align with the the vector (T - C), so the diameter must
increase with distance(T, C). Finally, assume that the touch diameter is
equal to the tool thickness, and we arrive at the formulas above.
Finger Tracking
---------------
@ -338,9 +406,7 @@ subsequent events of the same type refer to different fingers.
For example usage of the type A protocol, see the bcm5974 driver. For
example usage of the type B protocol, see the hid-egalax driver.
[1] With the extension ABS_MT_APPROACH_X and ABS_MT_APPROACH_Y, the
difference between the contact position and the approaching tool position
could be used to derive tilt.
[1] Also, the difference (TOOL_X - POSITION_X) can be used to model tilt.
[2] The list can of course be extended.
[3] The mtdev project: http://bitmath.org/code/mtdev/.
[4] See the section on event computation.

View File

@ -149,6 +149,7 @@ int _name[] = { \
* keymap: pointer to keymap data (table and size)
* rep: enables key autorepeat
* mode: choose keyboard support(9x9, 6x6, 2x2)
* suspended_rate: rate at which keyboard would operate in suspended mode
*
* This structure is supposed to be used by platform code to supply
* keymaps to drivers that implement keyboards.
@ -157,6 +158,7 @@ struct kbd_platform_data {
const struct matrix_keymap_data *keymap;
bool rep;
unsigned int mode;
unsigned int suspended_rate;
};
#endif /* __PLAT_KEYBOARD_H */

View File

@ -176,7 +176,7 @@ static int apply_envelope(struct ml_effect_state *state, int value,
value, envelope->attack_level);
time_from_level = jiffies_to_msecs(now - state->play_at);
time_of_envelope = envelope->attack_length;
envelope_level = min_t(__s16, envelope->attack_level, 0x7fff);
envelope_level = min_t(u16, envelope->attack_level, 0x7fff);
} else if (envelope->fade_length && effect->replay.length &&
time_after(now,
@ -184,7 +184,7 @@ static int apply_envelope(struct ml_effect_state *state, int value,
time_before(now, state->stop_at)) {
time_from_level = jiffies_to_msecs(state->stop_at - now);
time_of_envelope = envelope->fade_length;
envelope_level = min_t(__s16, envelope->fade_level, 0x7fff);
envelope_level = min_t(u16, envelope->fade_level, 0x7fff);
} else
return value;

View File

@ -135,7 +135,7 @@ EXPORT_SYMBOL(input_mt_report_finger_count);
*/
void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count)
{
struct input_mt_slot *oldest = 0;
struct input_mt_slot *oldest = NULL;
int oldid = dev->trkid;
int count = 0;
int i;

View File

@ -332,6 +332,16 @@ config KEYBOARD_LOCOMO
To compile this driver as a module, choose M here: the
module will be called locomokbd.
config KEYBOARD_LPC32XX
tristate "LPC32XX matrix key scanner support"
depends on ARCH_LPC32XX && OF
help
Say Y here if you want to use NXP LPC32XX SoC key scanner interface,
connected to a key matrix.
To compile this driver as a module, choose M here: the
module will be called lpc32xx-keys.
config KEYBOARD_MAPLE
tristate "Maple bus keyboard"
depends on SH_DREAMCAST && MAPLE

View File

@ -26,6 +26,7 @@ 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_LPC32XX) += lpc32xx-keys.o
obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o
obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o
obj-$(CONFIG_KEYBOARD_MAX7359) += max7359_keypad.o

View File

@ -559,7 +559,6 @@ static int gpio_keys_get_devtree_pdata(struct device *dev,
pdata->rep = !!of_get_property(node, "autorepeat", NULL);
/* First count the subnodes */
pdata->nbuttons = 0;
pp = NULL;
while ((pp = of_get_next_child(node, pp)))
pdata->nbuttons++;

View File

@ -378,20 +378,24 @@ static void imx_keypad_close(struct input_dev *dev)
imx_keypad_inhibit(keypad);
/* Disable clock unit */
clk_disable(keypad->clk);
clk_disable_unprepare(keypad->clk);
}
static int imx_keypad_open(struct input_dev *dev)
{
struct imx_keypad *keypad = input_get_drvdata(dev);
int error;
dev_dbg(&dev->dev, ">%s\n", __func__);
/* Enable the kpp clock */
error = clk_prepare_enable(keypad->clk);
if (error)
return error;
/* We became active from now */
keypad->enabled = true;
/* Enable the kpp clock */
clk_enable(keypad->clk);
imx_keypad_config(keypad);
/* Sanity control, not all the rows must be actived now. */
@ -467,7 +471,7 @@ static int __devinit imx_keypad_probe(struct platform_device *pdev)
goto failed_free_priv;
}
keypad->clk = clk_get(&pdev->dev, "kpp");
keypad->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(keypad->clk)) {
dev_err(&pdev->dev, "failed to get keypad clock\n");
error = PTR_ERR(keypad->clk);
@ -581,7 +585,7 @@ static int imx_kbd_suspend(struct device *dev)
mutex_lock(&input_dev->mutex);
if (input_dev->users)
clk_disable(kbd->clk);
clk_disable_unprepare(kbd->clk);
mutex_unlock(&input_dev->mutex);
@ -596,18 +600,23 @@ static int imx_kbd_resume(struct device *dev)
struct platform_device *pdev = to_platform_device(dev);
struct imx_keypad *kbd = platform_get_drvdata(pdev);
struct input_dev *input_dev = kbd->input_dev;
int ret = 0;
if (device_may_wakeup(&pdev->dev))
disable_irq_wake(kbd->irq);
mutex_lock(&input_dev->mutex);
if (input_dev->users)
clk_enable(kbd->clk);
if (input_dev->users) {
ret = clk_prepare_enable(kbd->clk);
if (ret)
goto err_clk;
}
err_clk:
mutex_unlock(&input_dev->mutex);
return 0;
return ret;
}
#endif

View File

@ -0,0 +1,394 @@
/*
* NXP LPC32xx SoC Key Scan Interface
*
* Authors:
* Kevin Wells <kevin.wells@nxp.com>
* Roland Stigge <stigge@antcom.de>
*
* Copyright (C) 2010 NXP Semiconductors
* Copyright (C) 2012 Roland Stigge
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* This controller supports square key matrices from 1x1 up to 8x8
*/
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/irq.h>
#include <linux/pm.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/input/matrix_keypad.h>
#define DRV_NAME "lpc32xx_keys"
/*
* Key scanner register offsets
*/
#define LPC32XX_KS_DEB(x) ((x) + 0x00)
#define LPC32XX_KS_STATE_COND(x) ((x) + 0x04)
#define LPC32XX_KS_IRQ(x) ((x) + 0x08)
#define LPC32XX_KS_SCAN_CTL(x) ((x) + 0x0C)
#define LPC32XX_KS_FAST_TST(x) ((x) + 0x10)
#define LPC32XX_KS_MATRIX_DIM(x) ((x) + 0x14) /* 1..8 */
#define LPC32XX_KS_DATA(x, y) ((x) + 0x40 + ((y) << 2))
#define LPC32XX_KSCAN_DEB_NUM_DEB_PASS(n) ((n) & 0xFF)
#define LPC32XX_KSCAN_SCOND_IN_IDLE 0x0
#define LPC32XX_KSCAN_SCOND_IN_SCANONCE 0x1
#define LPC32XX_KSCAN_SCOND_IN_IRQGEN 0x2
#define LPC32XX_KSCAN_SCOND_IN_SCAN_MATRIX 0x3
#define LPC32XX_KSCAN_IRQ_PENDING_CLR 0x1
#define LPC32XX_KSCAN_SCTRL_SCAN_DELAY(n) ((n) & 0xFF)
#define LPC32XX_KSCAN_FTST_FORCESCANONCE 0x1
#define LPC32XX_KSCAN_FTST_USE32K_CLK 0x2
#define LPC32XX_KSCAN_MSEL_SELECT(n) ((n) & 0xF)
struct lpc32xx_kscan_drv {
struct input_dev *input;
struct clk *clk;
struct resource *iores;
void __iomem *kscan_base;
unsigned int irq;
u32 matrix_sz; /* Size of matrix in XxY, ie. 3 = 3x3 */
u32 deb_clks; /* Debounce clocks (based on 32KHz clock) */
u32 scan_delay; /* Scan delay (based on 32KHz clock) */
unsigned short *keymap; /* Pointer to key map for the scan matrix */
unsigned int row_shift;
u8 lastkeystates[8];
};
static void lpc32xx_mod_states(struct lpc32xx_kscan_drv *kscandat, int col)
{
struct input_dev *input = kscandat->input;
unsigned row, changed, scancode, keycode;
u8 key;
key = readl(LPC32XX_KS_DATA(kscandat->kscan_base, col));
changed = key ^ kscandat->lastkeystates[col];
kscandat->lastkeystates[col] = key;
for (row = 0; changed; row++, changed >>= 1) {
if (changed & 1) {
/* Key state changed, signal an event */
scancode = MATRIX_SCAN_CODE(row, col,
kscandat->row_shift);
keycode = kscandat->keymap[scancode];
input_event(input, EV_MSC, MSC_SCAN, scancode);
input_report_key(input, keycode, key & (1 << row));
}
}
}
static irqreturn_t lpc32xx_kscan_irq(int irq, void *dev_id)
{
struct lpc32xx_kscan_drv *kscandat = dev_id;
int i;
for (i = 0; i < kscandat->matrix_sz; i++)
lpc32xx_mod_states(kscandat, i);
writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base));
input_sync(kscandat->input);
return IRQ_HANDLED;
}
static int lpc32xx_kscan_open(struct input_dev *dev)
{
struct lpc32xx_kscan_drv *kscandat = input_get_drvdata(dev);
int error;
error = clk_prepare_enable(kscandat->clk);
if (error)
return error;
writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base));
return 0;
}
static void lpc32xx_kscan_close(struct input_dev *dev)
{
struct lpc32xx_kscan_drv *kscandat = input_get_drvdata(dev);
writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base));
clk_disable_unprepare(kscandat->clk);
}
static int __devinit lpc32xx_parse_dt(struct device *dev,
struct lpc32xx_kscan_drv *kscandat)
{
struct device_node *np = dev->of_node;
u32 rows = 0, columns = 0;
of_property_read_u32(np, "keypad,num-rows", &rows);
of_property_read_u32(np, "keypad,num-columns", &columns);
if (!rows || rows != columns) {
dev_err(dev,
"rows and columns must be specified and be equal!\n");
return -EINVAL;
}
kscandat->matrix_sz = rows;
kscandat->row_shift = get_count_order(columns);
of_property_read_u32(np, "nxp,debounce-delay-ms", &kscandat->deb_clks);
of_property_read_u32(np, "nxp,scan-delay-ms", &kscandat->scan_delay);
if (!kscandat->deb_clks || !kscandat->scan_delay) {
dev_err(dev, "debounce or scan delay not specified\n");
return -EINVAL;
}
return 0;
}
static int __devinit lpc32xx_kscan_probe(struct platform_device *pdev)
{
struct lpc32xx_kscan_drv *kscandat;
struct input_dev *input;
struct resource *res;
size_t keymap_size;
int error;
int irq;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "failed to get platform I/O memory\n");
return -EINVAL;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0 || irq >= NR_IRQS) {
dev_err(&pdev->dev, "failed to get platform irq\n");
return -EINVAL;
}
kscandat = kzalloc(sizeof(struct lpc32xx_kscan_drv), GFP_KERNEL);
if (!kscandat) {
dev_err(&pdev->dev, "failed to allocate memory\n");
return -ENOMEM;
}
error = lpc32xx_parse_dt(&pdev->dev, kscandat);
if (error) {
dev_err(&pdev->dev, "failed to parse device tree\n");
goto err_free_mem;
}
keymap_size = sizeof(kscandat->keymap[0]) *
(kscandat->matrix_sz << kscandat->row_shift);
kscandat->keymap = kzalloc(keymap_size, GFP_KERNEL);
if (!kscandat->keymap) {
dev_err(&pdev->dev, "could not allocate memory for keymap\n");
error = -ENOMEM;
goto err_free_mem;
}
kscandat->input = input = input_allocate_device();
if (!input) {
dev_err(&pdev->dev, "failed to allocate input device\n");
error = -ENOMEM;
goto err_free_keymap;
}
/* Setup key input */
input->name = pdev->name;
input->phys = "lpc32xx/input0";
input->id.vendor = 0x0001;
input->id.product = 0x0001;
input->id.version = 0x0100;
input->open = lpc32xx_kscan_open;
input->close = lpc32xx_kscan_close;
input->dev.parent = &pdev->dev;
input_set_capability(input, EV_MSC, MSC_SCAN);
error = matrix_keypad_build_keymap(NULL, NULL,
kscandat->matrix_sz,
kscandat->matrix_sz,
kscandat->keymap, kscandat->input);
if (error) {
dev_err(&pdev->dev, "failed to build keymap\n");
goto err_free_input;
}
input_set_drvdata(kscandat->input, kscandat);
kscandat->iores = request_mem_region(res->start, resource_size(res),
pdev->name);
if (!kscandat->iores) {
dev_err(&pdev->dev, "failed to request I/O memory\n");
error = -EBUSY;
goto err_free_input;
}
kscandat->kscan_base = ioremap(kscandat->iores->start,
resource_size(kscandat->iores));
if (!kscandat->kscan_base) {
dev_err(&pdev->dev, "failed to remap I/O memory\n");
error = -EBUSY;
goto err_release_memregion;
}
/* Get the key scanner clock */
kscandat->clk = clk_get(&pdev->dev, NULL);
if (IS_ERR(kscandat->clk)) {
dev_err(&pdev->dev, "failed to get clock\n");
error = PTR_ERR(kscandat->clk);
goto err_unmap;
}
/* Configure the key scanner */
error = clk_prepare_enable(kscandat->clk);
if (error)
goto err_clk_put;
writel(kscandat->deb_clks, LPC32XX_KS_DEB(kscandat->kscan_base));
writel(kscandat->scan_delay, LPC32XX_KS_SCAN_CTL(kscandat->kscan_base));
writel(LPC32XX_KSCAN_FTST_USE32K_CLK,
LPC32XX_KS_FAST_TST(kscandat->kscan_base));
writel(kscandat->matrix_sz,
LPC32XX_KS_MATRIX_DIM(kscandat->kscan_base));
writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base));
clk_disable_unprepare(kscandat->clk);
error = request_irq(irq, lpc32xx_kscan_irq, 0, pdev->name, kscandat);
if (error) {
dev_err(&pdev->dev, "failed to request irq\n");
goto err_clk_put;
}
error = input_register_device(kscandat->input);
if (error) {
dev_err(&pdev->dev, "failed to register input device\n");
goto err_free_irq;
}
platform_set_drvdata(pdev, kscandat);
return 0;
err_free_irq:
free_irq(irq, kscandat);
err_clk_put:
clk_put(kscandat->clk);
err_unmap:
iounmap(kscandat->kscan_base);
err_release_memregion:
release_mem_region(kscandat->iores->start,
resource_size(kscandat->iores));
err_free_input:
input_free_device(kscandat->input);
err_free_keymap:
kfree(kscandat->keymap);
err_free_mem:
kfree(kscandat);
return error;
}
static int __devexit lpc32xx_kscan_remove(struct platform_device *pdev)
{
struct lpc32xx_kscan_drv *kscandat = platform_get_drvdata(pdev);
free_irq(platform_get_irq(pdev, 0), kscandat);
clk_put(kscandat->clk);
iounmap(kscandat->kscan_base);
release_mem_region(kscandat->iores->start,
resource_size(kscandat->iores));
input_unregister_device(kscandat->input);
kfree(kscandat->keymap);
kfree(kscandat);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int lpc32xx_kscan_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct lpc32xx_kscan_drv *kscandat = platform_get_drvdata(pdev);
struct input_dev *input = kscandat->input;
mutex_lock(&input->mutex);
if (input->users) {
/* Clear IRQ and disable clock */
writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base));
clk_disable_unprepare(kscandat->clk);
}
mutex_unlock(&input->mutex);
return 0;
}
static int lpc32xx_kscan_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct lpc32xx_kscan_drv *kscandat = platform_get_drvdata(pdev);
struct input_dev *input = kscandat->input;
int retval = 0;
mutex_lock(&input->mutex);
if (input->users) {
/* Enable clock and clear IRQ */
retval = clk_prepare_enable(kscandat->clk);
if (retval == 0)
writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base));
}
mutex_unlock(&input->mutex);
return retval;
}
#endif
static SIMPLE_DEV_PM_OPS(lpc32xx_kscan_pm_ops, lpc32xx_kscan_suspend,
lpc32xx_kscan_resume);
static const struct of_device_id lpc32xx_kscan_match[] = {
{ .compatible = "nxp,lpc3220-key" },
{},
};
MODULE_DEVICE_TABLE(of, lpc32xx_kscan_match);
static struct platform_driver lpc32xx_kscan_driver = {
.probe = lpc32xx_kscan_probe,
.remove = __devexit_p(lpc32xx_kscan_remove),
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
.pm = &lpc32xx_kscan_pm_ops,
.of_match_table = of_match_ptr(lpc32xx_kscan_match),
}
};
module_platform_driver(lpc32xx_kscan_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Kevin Wells <kevin.wells@nxp.com>");
MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>");
MODULE_DESCRIPTION("Key scanner driver for LPC32XX devices");

View File

@ -49,6 +49,7 @@
#define SKE_ASR3 0x2C
#define SKE_NUM_ASRX_REGISTERS (4)
#define KEY_PRESSED_DELAY 10
/**
* struct ske_keypad - data structure used by keypad driver
@ -92,7 +93,7 @@ static void ske_keypad_set_bits(struct ske_keypad *keypad, u16 addr,
static int __init ske_keypad_chip_init(struct ske_keypad *keypad)
{
u32 value;
int timeout = 50;
int timeout = keypad->board->debounce_ms;
/* check SKE_RIS to be 0 */
while ((readl(keypad->reg_base + SKE_RIS) != 0x00000000) && timeout--)
@ -135,12 +136,37 @@ static int __init ske_keypad_chip_init(struct ske_keypad *keypad)
return 0;
}
static void ske_keypad_report(struct ske_keypad *keypad, u8 status, int col)
{
int row = 0, code, pos;
struct input_dev *input = keypad->input;
u32 ske_ris;
int key_pressed;
int num_of_rows;
/* find out the row */
num_of_rows = hweight8(status);
do {
pos = __ffs(status);
row = pos;
status &= ~(1 << pos);
code = MATRIX_SCAN_CODE(row, col, SKE_KEYPAD_ROW_SHIFT);
ske_ris = readl(keypad->reg_base + SKE_RIS);
key_pressed = ske_ris & SKE_KPRISA;
input_event(input, EV_MSC, MSC_SCAN, code);
input_report_key(input, keypad->keymap[code], key_pressed);
input_sync(input);
num_of_rows--;
} while (num_of_rows);
}
static void ske_keypad_read_data(struct ske_keypad *keypad)
{
struct input_dev *input = keypad->input;
u16 status;
int col = 0, row = 0, code;
int ske_asr, ske_ris, key_pressed, i;
u8 status;
int col = 0;
int ske_asr, i;
/*
* Read the auto scan registers
@ -154,44 +180,38 @@ static void ske_keypad_read_data(struct ske_keypad *keypad)
if (!ske_asr)
continue;
/* now that ASRx is zero, find out the column x and row y*/
if (ske_asr & 0xff) {
/* now that ASRx is zero, find out the coloumn x and row y */
status = ske_asr & 0xff;
if (status) {
col = i * 2;
status = ske_asr & 0xff;
} else {
col = (i * 2) + 1;
status = (ske_asr & 0xff00) >> 8;
ske_keypad_report(keypad, status, col);
}
status = (ske_asr & 0xff00) >> 8;
if (status) {
col = (i * 2) + 1;
ske_keypad_report(keypad, status, col);
}
/* find out the row */
row = __ffs(status);
code = MATRIX_SCAN_CODE(row, col, SKE_KEYPAD_ROW_SHIFT);
ske_ris = readl(keypad->reg_base + SKE_RIS);
key_pressed = ske_ris & SKE_KPRISA;
input_event(input, EV_MSC, MSC_SCAN, code);
input_report_key(input, keypad->keymap[code], key_pressed);
input_sync(input);
}
}
static irqreturn_t ske_keypad_irq(int irq, void *dev_id)
{
struct ske_keypad *keypad = dev_id;
int retries = 20;
int timeout = keypad->board->debounce_ms;
/* disable auto scan interrupt; mask the interrupt generated */
ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0);
ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA);
while ((readl(keypad->reg_base + SKE_CR) & SKE_KPASON) && --retries)
msleep(5);
while ((readl(keypad->reg_base + SKE_CR) & SKE_KPASON) && --timeout)
cpu_relax();
if (retries) {
/* SKEx registers are stable and can be read */
ske_keypad_read_data(keypad);
}
/* SKEx registers are stable and can be read */
ske_keypad_read_data(keypad);
/* wait until raw interrupt is clear */
while ((readl(keypad->reg_base + SKE_RIS)) && --timeout)
msleep(KEY_PRESSED_DELAY);
/* enable auto scan interrupts */
ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA);

View File

@ -27,6 +27,7 @@
#include <linux/platform_device.h>
#include <linux/errno.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/input.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
@ -84,8 +85,9 @@ struct omap4_keypad {
u32 reg_offset;
u32 irqreg_offset;
unsigned int row_shift;
bool no_autorepeat;
unsigned char key_state[8];
unsigned short keymap[];
unsigned short *keymap;
};
static int kbd_readl(struct omap4_keypad *keypad_data, u32 offset)
@ -208,25 +210,51 @@ static void omap4_keypad_close(struct input_dev *input)
pm_runtime_put_sync(input->dev.parent);
}
#ifdef CONFIG_OF
static int __devinit omap4_keypad_parse_dt(struct device *dev,
struct omap4_keypad *keypad_data)
{
struct device_node *np = dev->of_node;
if (!np) {
dev_err(dev, "missing DT data");
return -EINVAL;
}
of_property_read_u32(np, "keypad,num-rows", &keypad_data->rows);
of_property_read_u32(np, "keypad,num-columns", &keypad_data->cols);
if (!keypad_data->rows || !keypad_data->cols) {
dev_err(dev, "number of keypad rows/columns not specified\n");
return -EINVAL;
}
if (of_get_property(np, "linux,input-no-autorepeat", NULL))
keypad_data->no_autorepeat = true;
return 0;
}
#else
static inline int omap4_keypad_parse_dt(struct device *dev,
struct omap4_keypad *keypad_data)
{
return -ENOSYS;
}
#endif
static int __devinit omap4_keypad_probe(struct platform_device *pdev)
{
const struct omap4_keypad_platform_data *pdata;
const struct omap4_keypad_platform_data *pdata =
dev_get_platdata(&pdev->dev);
const struct matrix_keymap_data *keymap_data =
pdata ? pdata->keymap_data : NULL;
struct omap4_keypad *keypad_data;
struct input_dev *input_dev;
struct resource *res;
resource_size_t size;
unsigned int row_shift, max_keys;
unsigned int max_keys;
int rev;
int irq;
int error;
/* platform data */
pdata = pdev->dev.platform_data;
if (!pdata) {
dev_err(&pdev->dev, "no platform data defined\n");
return -EINVAL;
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev, "no base address specified\n");
@ -239,25 +267,24 @@ static int __devinit omap4_keypad_probe(struct platform_device *pdev)
return -EINVAL;
}
if (!pdata->keymap_data) {
dev_err(&pdev->dev, "no keymap data defined\n");
return -EINVAL;
}
row_shift = get_count_order(pdata->cols);
max_keys = pdata->rows << row_shift;
keypad_data = kzalloc(sizeof(struct omap4_keypad) +
max_keys * sizeof(keypad_data->keymap[0]),
GFP_KERNEL);
keypad_data = kzalloc(sizeof(struct omap4_keypad), GFP_KERNEL);
if (!keypad_data) {
dev_err(&pdev->dev, "keypad_data memory allocation failed\n");
return -ENOMEM;
}
size = resource_size(res);
keypad_data->irq = irq;
res = request_mem_region(res->start, size, pdev->name);
if (pdata) {
keypad_data->rows = pdata->rows;
keypad_data->cols = pdata->cols;
} else {
error = omap4_keypad_parse_dt(&pdev->dev, keypad_data);
if (error)
return error;
}
res = request_mem_region(res->start, resource_size(res), pdev->name);
if (!res) {
dev_err(&pdev->dev, "can't request mem region\n");
error = -EBUSY;
@ -271,15 +298,11 @@ static int __devinit omap4_keypad_probe(struct platform_device *pdev)
goto err_release_mem;
}
keypad_data->irq = irq;
keypad_data->row_shift = row_shift;
keypad_data->rows = pdata->rows;
keypad_data->cols = pdata->cols;
/*
* Enable clocks for the keypad module so that we can read
* revision register.
*/
* Enable clocks for the keypad module so that we can read
* revision register.
*/
pm_runtime_enable(&pdev->dev);
error = pm_runtime_get_sync(&pdev->dev);
if (error) {
@ -322,18 +345,29 @@ static int __devinit omap4_keypad_probe(struct platform_device *pdev)
input_dev->open = omap4_keypad_open;
input_dev->close = omap4_keypad_close;
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");
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
if (!keypad_data->no_autorepeat)
__set_bit(EV_REP, input_dev->evbit);
input_set_drvdata(input_dev, keypad_data);
keypad_data->row_shift = get_count_order(keypad_data->cols);
max_keys = keypad_data->rows << keypad_data->row_shift;
keypad_data->keymap = kzalloc(max_keys * sizeof(keypad_data->keymap[0]),
GFP_KERNEL);
if (!keypad_data->keymap) {
dev_err(&pdev->dev, "Not enough memory for keymap\n");
error = -ENOMEM;
goto err_free_input;
}
__set_bit(EV_REP, input_dev->evbit);
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
input_set_drvdata(input_dev, keypad_data);
error = matrix_keypad_build_keymap(keymap_data, NULL,
keypad_data->rows, keypad_data->cols,
keypad_data->keymap, input_dev);
if (error) {
dev_err(&pdev->dev, "failed to build keymap\n");
goto err_free_keymap;
}
error = request_irq(keypad_data->irq, omap4_keypad_interrupt,
IRQF_TRIGGER_RISING,
@ -357,6 +391,8 @@ static int __devinit omap4_keypad_probe(struct platform_device *pdev)
err_pm_disable:
pm_runtime_disable(&pdev->dev);
free_irq(keypad_data->irq, keypad_data);
err_free_keymap:
kfree(keypad_data->keymap);
err_free_input:
input_free_device(input_dev);
err_pm_put_sync:
@ -364,7 +400,7 @@ err_pm_put_sync:
err_unmap:
iounmap(keypad_data->base);
err_release_mem:
release_mem_region(res->start, size);
release_mem_region(res->start, resource_size(res));
err_free_keypad:
kfree(keypad_data);
return error;
@ -386,18 +422,29 @@ static int __devexit omap4_keypad_remove(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(res->start, resource_size(res));
kfree(keypad_data->keymap);
kfree(keypad_data);
platform_set_drvdata(pdev, NULL);
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id omap_keypad_dt_match[] = {
{ .compatible = "ti,omap4-keypad" },
{},
};
MODULE_DEVICE_TABLE(of, omap_keypad_dt_match);
#endif
static struct platform_driver omap4_keypad_driver = {
.probe = omap4_keypad_probe,
.remove = __devexit_p(omap4_keypad_remove),
.driver = {
.name = "omap4-keypad",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(omap_keypad_dt_match),
},
};
module_platform_driver(omap4_keypad_driver);

View File

@ -27,33 +27,31 @@
#include <plat/keyboard.h>
/* Keyboard Registers */
#define MODE_REG 0x00 /* 16 bit reg */
#define STATUS_REG 0x0C /* 2 bit reg */
#define DATA_REG 0x10 /* 8 bit reg */
#define MODE_CTL_REG 0x00
#define STATUS_REG 0x0C
#define DATA_REG 0x10
#define INTR_MASK 0x54
/* Register Values */
/*
* pclk freq mask = (APB FEQ -1)= 82 MHZ.Programme bit 15-9 in mode
* control register as 1010010(82MHZ)
*/
#define PCLK_FREQ_MSK 0xA400 /* 82 MHz */
#define START_SCAN 0x0100
#define SCAN_RATE_10 0x0000
#define SCAN_RATE_20 0x0004
#define SCAN_RATE_40 0x0008
#define SCAN_RATE_80 0x000C
#define MODE_KEYBOARD 0x0002
#define DATA_AVAIL 0x2
#define KEY_MASK 0xFF000000
#define KEY_VALUE 0x00FFFFFF
#define ROW_MASK 0xF0
#define COLUMN_MASK 0x0F
#define NUM_ROWS 16
#define NUM_COLS 16
#define MODE_CTL_PCLK_FREQ_SHIFT 9
#define MODE_CTL_PCLK_FREQ_MSK 0x7F
#define KEY_MATRIX_SHIFT 6
#define MODE_CTL_KEYBOARD (0x2 << 0)
#define MODE_CTL_SCAN_RATE_10 (0x0 << 2)
#define MODE_CTL_SCAN_RATE_20 (0x1 << 2)
#define MODE_CTL_SCAN_RATE_40 (0x2 << 2)
#define MODE_CTL_SCAN_RATE_80 (0x3 << 2)
#define MODE_CTL_KEYNUM_SHIFT 6
#define MODE_CTL_START_SCAN (0x1 << 8)
#define STATUS_DATA_AVAIL (0x1 << 1)
#define DATA_ROW_MASK 0xF0
#define DATA_COLUMN_MASK 0x0F
#define ROW_SHIFT 4
struct spear_kbd {
struct input_dev *input;
@ -65,6 +63,8 @@ struct spear_kbd {
unsigned short last_key;
unsigned short keycodes[NUM_ROWS * NUM_COLS];
bool rep;
unsigned int suspended_rate;
u32 mode_ctl_reg;
};
static irqreturn_t spear_kbd_interrupt(int irq, void *dev_id)
@ -72,10 +72,10 @@ static irqreturn_t spear_kbd_interrupt(int irq, void *dev_id)
struct spear_kbd *kbd = dev_id;
struct input_dev *input = kbd->input;
unsigned int key;
u8 sts, val;
u32 sts, val;
sts = readb(kbd->io_base + STATUS_REG);
if (!(sts & DATA_AVAIL))
sts = readl_relaxed(kbd->io_base + STATUS_REG);
if (!(sts & STATUS_DATA_AVAIL))
return IRQ_NONE;
if (kbd->last_key != KEY_RESERVED) {
@ -84,7 +84,8 @@ static irqreturn_t spear_kbd_interrupt(int irq, void *dev_id)
}
/* following reads active (row, col) pair */
val = readb(kbd->io_base + DATA_REG);
val = readl_relaxed(kbd->io_base + DATA_REG) &
(DATA_ROW_MASK | DATA_COLUMN_MASK);
key = kbd->keycodes[val];
input_event(input, EV_MSC, MSC_SCAN, val);
@ -94,7 +95,7 @@ static irqreturn_t spear_kbd_interrupt(int irq, void *dev_id)
kbd->last_key = key;
/* clear interrupt */
writeb(0, kbd->io_base + STATUS_REG);
writel_relaxed(0, kbd->io_base + STATUS_REG);
return IRQ_HANDLED;
}
@ -103,7 +104,7 @@ static int spear_kbd_open(struct input_dev *dev)
{
struct spear_kbd *kbd = input_get_drvdata(dev);
int error;
u16 val;
u32 val;
kbd->last_key = KEY_RESERVED;
@ -111,16 +112,20 @@ static int spear_kbd_open(struct input_dev *dev)
if (error)
return error;
/* keyboard rate to be programmed is input clock (in MHz) - 1 */
val = clk_get_rate(kbd->clk) / 1000000 - 1;
val = (val & MODE_CTL_PCLK_FREQ_MSK) << MODE_CTL_PCLK_FREQ_SHIFT;
/* program keyboard */
val = SCAN_RATE_80 | MODE_KEYBOARD | PCLK_FREQ_MSK |
(kbd->mode << KEY_MATRIX_SHIFT);
writew(val, kbd->io_base + MODE_REG);
writeb(1, kbd->io_base + STATUS_REG);
val = MODE_CTL_SCAN_RATE_80 | MODE_CTL_KEYBOARD | val |
(kbd->mode << MODE_CTL_KEYNUM_SHIFT);
writel_relaxed(val, kbd->io_base + MODE_CTL_REG);
writel_relaxed(1, kbd->io_base + STATUS_REG);
/* start key scan */
val = readw(kbd->io_base + MODE_REG);
val |= START_SCAN;
writew(val, kbd->io_base + MODE_REG);
val = readl_relaxed(kbd->io_base + MODE_CTL_REG);
val |= MODE_CTL_START_SCAN;
writel_relaxed(val, kbd->io_base + MODE_CTL_REG);
return 0;
}
@ -128,12 +133,12 @@ static int spear_kbd_open(struct input_dev *dev)
static void spear_kbd_close(struct input_dev *dev)
{
struct spear_kbd *kbd = input_get_drvdata(dev);
u16 val;
u32 val;
/* stop key scan */
val = readw(kbd->io_base + MODE_REG);
val &= ~START_SCAN;
writew(val, kbd->io_base + MODE_REG);
val = readl_relaxed(kbd->io_base + MODE_CTL_REG);
val &= ~MODE_CTL_START_SCAN;
writel_relaxed(val, kbd->io_base + MODE_CTL_REG);
clk_disable(kbd->clk);
@ -146,7 +151,7 @@ static int __devinit spear_kbd_parse_dt(struct platform_device *pdev,
{
struct device_node *np = pdev->dev.of_node;
int error;
u32 val;
u32 val, suspended_rate;
if (!np) {
dev_err(&pdev->dev, "Missing DT data\n");
@ -156,6 +161,9 @@ static int __devinit spear_kbd_parse_dt(struct platform_device *pdev,
if (of_property_read_bool(np, "autorepeat"))
kbd->rep = true;
if (of_property_read_u32(np, "suspended_rate", &suspended_rate))
kbd->suspended_rate = suspended_rate;
error = of_property_read_u32(np, "st,mode", &val);
if (error) {
dev_err(&pdev->dev, "DT: Invalid or missing mode\n");
@ -213,6 +221,7 @@ static int __devinit spear_kbd_probe(struct platform_device *pdev)
} else {
kbd->mode = pdata->mode;
kbd->rep = pdata->rep;
kbd->suspended_rate = pdata->suspended_rate;
}
kbd->res = request_mem_region(res->start, resource_size(res),
@ -302,7 +311,7 @@ static int __devexit spear_kbd_remove(struct platform_device *pdev)
release_mem_region(kbd->res->start, resource_size(kbd->res));
kfree(kbd);
device_init_wakeup(&pdev->dev, 1);
device_init_wakeup(&pdev->dev, 0);
platform_set_drvdata(pdev, NULL);
return 0;
@ -314,15 +323,48 @@ static int spear_kbd_suspend(struct device *dev)
struct platform_device *pdev = to_platform_device(dev);
struct spear_kbd *kbd = platform_get_drvdata(pdev);
struct input_dev *input_dev = kbd->input;
unsigned int rate = 0, mode_ctl_reg, val;
mutex_lock(&input_dev->mutex);
if (input_dev->users)
clk_enable(kbd->clk);
/* explicitly enable clock as we may program device */
clk_enable(kbd->clk);
if (device_may_wakeup(&pdev->dev))
mode_ctl_reg = readl_relaxed(kbd->io_base + MODE_CTL_REG);
if (device_may_wakeup(&pdev->dev)) {
enable_irq_wake(kbd->irq);
/*
* reprogram the keyboard operating frequency as on some
* platform it may change during system suspended
*/
if (kbd->suspended_rate)
rate = kbd->suspended_rate / 1000000 - 1;
else
rate = clk_get_rate(kbd->clk) / 1000000 - 1;
val = mode_ctl_reg &
~(MODE_CTL_PCLK_FREQ_MSK << MODE_CTL_PCLK_FREQ_SHIFT);
val |= (rate & MODE_CTL_PCLK_FREQ_MSK)
<< MODE_CTL_PCLK_FREQ_SHIFT;
writel_relaxed(val, kbd->io_base + MODE_CTL_REG);
} else {
if (input_dev->users) {
writel_relaxed(mode_ctl_reg & ~MODE_CTL_START_SCAN,
kbd->io_base + MODE_CTL_REG);
clk_disable(kbd->clk);
}
}
/* store current configuration */
if (input_dev->users)
kbd->mode_ctl_reg = mode_ctl_reg;
/* restore previous clk state */
clk_disable(kbd->clk);
mutex_unlock(&input_dev->mutex);
return 0;
@ -336,11 +378,16 @@ static int spear_kbd_resume(struct device *dev)
mutex_lock(&input_dev->mutex);
if (device_may_wakeup(&pdev->dev))
if (device_may_wakeup(&pdev->dev)) {
disable_irq_wake(kbd->irq);
} else {
if (input_dev->users)
clk_enable(kbd->clk);
}
/* restore current configuration */
if (input_dev->users)
clk_enable(kbd->clk);
writel_relaxed(kbd->mode_ctl_reg, kbd->io_base + MODE_CTL_REG);
mutex_unlock(&input_dev->mutex);

View File

@ -13,6 +13,7 @@
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/mfd/abx500/ab8500.h>
#include <linux/of.h>
#include <linux/slab.h>
/**
@ -131,10 +132,18 @@ static int __devexit ab8500_ponkey_remove(struct platform_device *pdev)
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id ab8500_ponkey_match[] = {
{ .compatible = "stericsson,ab8500-ponkey", },
{}
};
#endif
static struct platform_driver ab8500_ponkey_driver = {
.driver = {
.name = "ab8500-poweron-key",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(ab8500_ponkey_match),
},
.probe = ab8500_ponkey_probe,
.remove = __devexit_p(ab8500_ponkey_remove),

View File

@ -251,7 +251,6 @@ static int twl6040_vibra_suspend(struct device *dev)
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(twl6040_vibra_pm_ops, twl6040_vibra_suspend, NULL);
@ -259,13 +258,19 @@ static SIMPLE_DEV_PM_OPS(twl6040_vibra_pm_ops, twl6040_vibra_suspend, NULL);
static int __devinit twl6040_vibra_probe(struct platform_device *pdev)
{
struct twl6040_vibra_data *pdata = pdev->dev.platform_data;
struct device_node *node = pdev->dev.of_node;
struct device *twl6040_core_dev = pdev->dev.parent;
struct device_node *twl6040_core_node = NULL;
struct vibra_info *info;
int vddvibl_uV = 0;
int vddvibr_uV = 0;
int ret;
if (!pdata && !node) {
#ifdef CONFIG_OF
twl6040_core_node = of_find_node_by_name(twl6040_core_dev->of_node,
"vibra");
#endif
if (!pdata && !twl6040_core_node) {
dev_err(&pdev->dev, "platform_data not available\n");
return -EINVAL;
}
@ -287,14 +292,18 @@ static int __devinit twl6040_vibra_probe(struct platform_device *pdev)
vddvibl_uV = pdata->vddvibl_uV;
vddvibr_uV = pdata->vddvibr_uV;
} else {
of_property_read_u32(node, "vibldrv_res", &info->vibldrv_res);
of_property_read_u32(node, "vibrdrv_res", &info->vibrdrv_res);
of_property_read_u32(node, "viblmotor_res",
of_property_read_u32(twl6040_core_node, "ti,vibldrv-res",
&info->vibldrv_res);
of_property_read_u32(twl6040_core_node, "ti,vibrdrv-res",
&info->vibrdrv_res);
of_property_read_u32(twl6040_core_node, "ti,viblmotor-res",
&info->viblmotor_res);
of_property_read_u32(node, "vibrmotor_res",
of_property_read_u32(twl6040_core_node, "ti,vibrmotor-res",
&info->vibrmotor_res);
of_property_read_u32(node, "vddvibl_uV", &vddvibl_uV);
of_property_read_u32(node, "vddvibr_uV", &vddvibr_uV);
of_property_read_u32(twl6040_core_node, "ti,vddvibl-uV",
&vddvibl_uV);
of_property_read_u32(twl6040_core_node, "ti,vddvibr-uV",
&vddvibr_uV);
}
if ((!info->vibldrv_res && !info->viblmotor_res) ||
@ -351,8 +360,12 @@ static int __devinit twl6040_vibra_probe(struct platform_device *pdev)
info->supplies[0].supply = "vddvibl";
info->supplies[1].supply = "vddvibr";
ret = regulator_bulk_get(info->dev, ARRAY_SIZE(info->supplies),
info->supplies);
/*
* When booted with Device tree the regulators are attached to the
* parent device (twl6040 MFD core)
*/
ret = regulator_bulk_get(pdata ? info->dev : twl6040_core_dev,
ARRAY_SIZE(info->supplies), info->supplies);
if (ret) {
dev_err(info->dev, "couldn't get regulators %d\n", ret);
goto err_regulator;
@ -418,12 +431,6 @@ static int __devexit twl6040_vibra_remove(struct platform_device *pdev)
return 0;
}
static const struct of_device_id twl6040_vibra_of_match[] = {
{.compatible = "ti,twl6040-vibra", },
{ },
};
MODULE_DEVICE_TABLE(of, twl6040_vibra_of_match);
static struct platform_driver twl6040_vibra_driver = {
.probe = twl6040_vibra_probe,
.remove = __devexit_p(twl6040_vibra_remove),
@ -431,7 +438,6 @@ static struct platform_driver twl6040_vibra_driver = {
.name = "twl6040-vibra",
.owner = THIS_MODULE,
.pm = &twl6040_vibra_pm_ops,
.of_match_table = twl6040_vibra_of_match,
},
};
module_platform_driver(twl6040_vibra_driver);

View File

@ -138,6 +138,35 @@ static int synaptics_model_id(struct psmouse *psmouse)
return 0;
}
/*
* Read the board id from the touchpad
* The board id is encoded in the "QUERY MODES" response
*/
static int synaptics_board_id(struct psmouse *psmouse)
{
struct synaptics_data *priv = psmouse->private;
unsigned char bid[3];
if (synaptics_send_cmd(psmouse, SYN_QUE_MODES, bid))
return -1;
priv->board_id = ((bid[0] & 0xfc) << 6) | bid[1];
return 0;
}
/*
* Read the firmware id from the touchpad
*/
static int synaptics_firmware_id(struct psmouse *psmouse)
{
struct synaptics_data *priv = psmouse->private;
unsigned char fwid[3];
if (synaptics_send_cmd(psmouse, SYN_QUE_FIRMWARE_ID, fwid))
return -1;
priv->firmware_id = (fwid[0] << 16) | (fwid[1] << 8) | fwid[2];
return 0;
}
/*
* Read the capability-bits from the touchpad
* see also the SYN_CAP_* macros
@ -261,6 +290,10 @@ static int synaptics_query_hardware(struct psmouse *psmouse)
return -1;
if (synaptics_model_id(psmouse))
return -1;
if (synaptics_firmware_id(psmouse))
return -1;
if (synaptics_board_id(psmouse))
return -1;
if (synaptics_capability(psmouse))
return -1;
if (synaptics_resolution(psmouse))
@ -1435,11 +1468,12 @@ static int __synaptics_init(struct psmouse *psmouse, bool absolute_mode)
priv->pkt_type = SYN_MODEL_NEWABS(priv->model_id) ? SYN_NEWABS : SYN_OLDABS;
psmouse_info(psmouse,
"Touchpad model: %ld, fw: %ld.%ld, id: %#lx, caps: %#lx/%#lx/%#lx\n",
"Touchpad model: %ld, fw: %ld.%ld, id: %#lx, caps: %#lx/%#lx/%#lx, board id: %lu, fw id: %lu\n",
SYN_ID_MODEL(priv->identity),
SYN_ID_MAJOR(priv->identity), SYN_ID_MINOR(priv->identity),
priv->model_id,
priv->capabilities, priv->ext_cap, priv->ext_cap_0c);
priv->capabilities, priv->ext_cap, priv->ext_cap_0c,
priv->board_id, priv->firmware_id);
set_input_params(psmouse->dev, priv);

View File

@ -18,6 +18,7 @@
#define SYN_QUE_SERIAL_NUMBER_SUFFIX 0x07
#define SYN_QUE_RESOLUTION 0x08
#define SYN_QUE_EXT_CAPAB 0x09
#define SYN_QUE_FIRMWARE_ID 0x0a
#define SYN_QUE_EXT_CAPAB_0C 0x0c
#define SYN_QUE_EXT_MAX_COORDS 0x0d
#define SYN_QUE_EXT_MIN_COORDS 0x0f
@ -148,6 +149,8 @@ struct synaptics_hw_state {
struct synaptics_data {
/* Data read from the touchpad */
unsigned long int model_id; /* Model-ID */
unsigned long int firmware_id; /* Firmware-ID */
unsigned long int board_id; /* Board-ID */
unsigned long int capabilities; /* Capabilities */
unsigned long int ext_cap; /* Extended Capabilities */
unsigned long int ext_cap_0c; /* Ext Caps from 0x0c query */

View File

@ -364,7 +364,7 @@ static int synusb_probe(struct usb_interface *intf,
le16_to_cpu(udev->descriptor.idProduct));
if (synusb->flags & SYNUSB_STICK)
strlcat(synusb->name, " (Stick) ", sizeof(synusb->name));
strlcat(synusb->name, " (Stick)", sizeof(synusb->name));
usb_make_path(udev, synusb->phys, sizeof(synusb->phys));
strlcat(synusb->phys, "/input0", sizeof(synusb->phys));

View File

@ -63,6 +63,7 @@ MODULE_LICENSE(DRIVER_LICENSE);
enum hanwang_tablet_type {
HANWANG_ART_MASTER_III,
HANWANG_ART_MASTER_HD,
HANWANG_ART_MASTER_II,
};
struct hanwang {
@ -99,6 +100,8 @@ static const struct hanwang_features features_array[] = {
ART_MASTER_PKGLEN_MAX, 0x7f00, 0x4f60, 0x3f, 0x7f, 2048 },
{ 0x8401, "Hanwang Art Master HD 5012", HANWANG_ART_MASTER_HD,
ART_MASTER_PKGLEN_MAX, 0x678e, 0x4150, 0x3f, 0x7f, 1024 },
{ 0x8503, "Hanwang Art Master II", HANWANG_ART_MASTER_II,
ART_MASTER_PKGLEN_MAX, 0x27de, 0x1cfe, 0x3f, 0x7f, 1024 },
};
static const int hw_eventtypes[] = {
@ -127,14 +130,30 @@ static void hanwang_parse_packet(struct hanwang *hanwang)
struct usb_device *dev = hanwang->usbdev;
enum hanwang_tablet_type type = hanwang->features->type;
int i;
u16 x, y, p;
u16 p;
if (type == HANWANG_ART_MASTER_II) {
hanwang->current_tool = BTN_TOOL_PEN;
hanwang->current_id = STYLUS_DEVICE_ID;
}
switch (data[0]) {
case 0x02: /* data packet */
switch (data[1]) {
case 0x80: /* tool prox out */
hanwang->current_id = 0;
input_report_key(input_dev, hanwang->current_tool, 0);
if (type != HANWANG_ART_MASTER_II) {
hanwang->current_id = 0;
input_report_key(input_dev,
hanwang->current_tool, 0);
}
break;
case 0x00: /* artmaster ii pen leave */
if (type == HANWANG_ART_MASTER_II) {
hanwang->current_id = 0;
input_report_key(input_dev,
hanwang->current_tool, 0);
}
break;
case 0xc2: /* first time tool prox in */
@ -154,15 +173,12 @@ static void hanwang_parse_packet(struct hanwang *hanwang)
default:
hanwang->current_id = 0;
dev_dbg(&dev->dev,
"unknown tablet tool %02x ", data[0]);
"unknown tablet tool %02x\n", data[0]);
break;
}
break;
default: /* tool data packet */
x = (data[2] << 8) | data[3];
y = (data[4] << 8) | data[5];
switch (type) {
case HANWANG_ART_MASTER_III:
p = (data[6] << 3) |
@ -171,6 +187,7 @@ static void hanwang_parse_packet(struct hanwang *hanwang)
break;
case HANWANG_ART_MASTER_HD:
case HANWANG_ART_MASTER_II:
p = (data[7] >> 6) | (data[6] << 2);
break;
@ -180,17 +197,23 @@ static void hanwang_parse_packet(struct hanwang *hanwang)
}
input_report_abs(input_dev, ABS_X,
le16_to_cpup((__le16 *)&x));
be16_to_cpup((__be16 *)&data[2]));
input_report_abs(input_dev, ABS_Y,
le16_to_cpup((__le16 *)&y));
input_report_abs(input_dev, ABS_PRESSURE,
le16_to_cpup((__le16 *)&p));
be16_to_cpup((__be16 *)&data[4]));
input_report_abs(input_dev, ABS_PRESSURE, p);
input_report_abs(input_dev, ABS_TILT_X, data[7] & 0x3f);
input_report_abs(input_dev, ABS_TILT_Y, data[8] & 0x7f);
input_report_key(input_dev, BTN_STYLUS, data[1] & 0x02);
input_report_key(input_dev, BTN_STYLUS2, data[1] & 0x04);
if (type != HANWANG_ART_MASTER_II)
input_report_key(input_dev, BTN_STYLUS2,
data[1] & 0x04);
else
input_report_key(input_dev, BTN_TOOL_PEN, 1);
break;
}
input_report_abs(input_dev, ABS_MISC, hanwang->current_id);
input_event(input_dev, EV_MSC, MSC_SERIAL,
hanwang->features->pid);
@ -202,8 +225,8 @@ static void hanwang_parse_packet(struct hanwang *hanwang)
switch (type) {
case HANWANG_ART_MASTER_III:
input_report_key(input_dev, BTN_TOOL_FINGER, data[1] ||
data[2] || data[3]);
input_report_key(input_dev, BTN_TOOL_FINGER,
data[1] || data[2] || data[3]);
input_report_abs(input_dev, ABS_WHEEL, data[1]);
input_report_key(input_dev, BTN_0, data[2]);
for (i = 0; i < 8; i++)
@ -227,6 +250,10 @@ static void hanwang_parse_packet(struct hanwang *hanwang)
BTN_5 + i, data[6] & (1 << i));
}
break;
case HANWANG_ART_MASTER_II:
dev_dbg(&dev->dev, "error packet %02x\n", data[0]);
return;
}
input_report_abs(input_dev, ABS_MISC, hanwang->current_id);
@ -234,7 +261,7 @@ static void hanwang_parse_packet(struct hanwang *hanwang)
break;
default:
dev_dbg(&dev->dev, "error packet %02x ", data[0]);
dev_dbg(&dev->dev, "error packet %02x\n", data[0]);
break;
}

View File

@ -445,8 +445,7 @@ static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_feat
/* ask to report Wacom data */
if (features->device_type == BTN_TOOL_FINGER) {
/* if it is an MT Tablet PC touch */
if (features->type == TABLETPC2FG ||
features->type == MTSCREEN) {
if (features->type > TABLETPC) {
do {
rep_data[0] = 3;
rep_data[1] = 4;
@ -465,7 +464,7 @@ static int wacom_query_tablet_data(struct usb_interface *intf, struct wacom_feat
} while ((error < 0 || rep_data[1] != 4) &&
limit++ < WAC_MSG_RETRIES);
}
} else if (features->type != TABLETPC &&
} else if (features->type <= BAMBOO_PT &&
features->type != WIRELESS &&
features->device_type == BTN_TOOL_PEN) {
do {
@ -509,16 +508,13 @@ static int wacom_retrieve_hid_descriptor(struct usb_interface *intf,
if (intf->cur_altsetting->desc.bInterfaceNumber == 0) {
features->device_type = 0;
} else if (intf->cur_altsetting->desc.bInterfaceNumber == 2) {
features->device_type = BTN_TOOL_DOUBLETAP;
features->device_type = BTN_TOOL_FINGER;
features->pktlen = WACOM_PKGLEN_BBTOUCH3;
}
}
/* only devices that support touch need to retrieve the info */
if (features->type != TABLETPC &&
features->type != TABLETPC2FG &&
features->type != BAMBOO_PT &&
features->type != MTSCREEN) {
if (features->type < BAMBOO_PT) {
goto out;
}
@ -860,6 +856,7 @@ static int wacom_initialize_leds(struct wacom *wacom)
/* Initialize default values */
switch (wacom->wacom_wac.features.type) {
case INTUOS4S:
case INTUOS4:
case INTUOS4L:
wacom->led.select[0] = 0;
@ -913,6 +910,7 @@ static int wacom_initialize_leds(struct wacom *wacom)
static void wacom_destroy_leds(struct wacom *wacom)
{
switch (wacom->wacom_wac.features.type) {
case INTUOS4S:
case INTUOS4:
case INTUOS4L:
sysfs_remove_group(&wacom->intf->dev.kobj,
@ -972,6 +970,10 @@ static int wacom_initialize_battery(struct wacom *wacom)
error = power_supply_register(&wacom->usbdev->dev,
&wacom->battery);
if (!error)
power_supply_powers(&wacom->battery,
&wacom->usbdev->dev);
}
return error;
@ -979,8 +981,11 @@ static int wacom_initialize_battery(struct wacom *wacom)
static void wacom_destroy_battery(struct wacom *wacom)
{
if (wacom->wacom_wac.features.quirks & WACOM_QUIRK_MONITOR)
if (wacom->wacom_wac.features.quirks & WACOM_QUIRK_MONITOR &&
wacom->battery.dev) {
power_supply_unregister(&wacom->battery);
wacom->battery.dev = NULL;
}
}
static int wacom_register_input(struct wacom *wacom)
@ -1027,23 +1032,30 @@ static void wacom_wireless_work(struct work_struct *work)
struct wacom *wacom = container_of(work, struct wacom, work);
struct usb_device *usbdev = wacom->usbdev;
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
struct wacom *wacom1, *wacom2;
struct wacom_wac *wacom_wac1, *wacom_wac2;
int error;
/*
* Regardless if this is a disconnect or a new tablet,
* remove any existing input devices.
* remove any existing input and battery devices.
*/
wacom_destroy_battery(wacom);
/* Stylus interface */
wacom = usb_get_intfdata(usbdev->config->interface[1]);
if (wacom->wacom_wac.input)
input_unregister_device(wacom->wacom_wac.input);
wacom->wacom_wac.input = NULL;
wacom1 = usb_get_intfdata(usbdev->config->interface[1]);
wacom_wac1 = &(wacom1->wacom_wac);
if (wacom_wac1->input)
input_unregister_device(wacom_wac1->input);
wacom_wac1->input = NULL;
/* Touch interface */
wacom = usb_get_intfdata(usbdev->config->interface[2]);
if (wacom->wacom_wac.input)
input_unregister_device(wacom->wacom_wac.input);
wacom->wacom_wac.input = NULL;
wacom2 = usb_get_intfdata(usbdev->config->interface[2]);
wacom_wac2 = &(wacom2->wacom_wac);
if (wacom_wac2->input)
input_unregister_device(wacom_wac2->input);
wacom_wac2->input = NULL;
if (wacom_wac->pid == 0) {
dev_info(&wacom->intf->dev, "wireless tablet disconnected\n");
@ -1068,24 +1080,39 @@ static void wacom_wireless_work(struct work_struct *work)
}
/* Stylus interface */
wacom = usb_get_intfdata(usbdev->config->interface[1]);
wacom_wac = &wacom->wacom_wac;
wacom_wac->features =
wacom_wac1->features =
*((struct wacom_features *)id->driver_info);
wacom_wac->features.device_type = BTN_TOOL_PEN;
wacom_register_input(wacom);
wacom_wac1->features.device_type = BTN_TOOL_PEN;
error = wacom_register_input(wacom1);
if (error)
goto fail1;
/* Touch interface */
wacom = usb_get_intfdata(usbdev->config->interface[2]);
wacom_wac = &wacom->wacom_wac;
wacom_wac->features =
wacom_wac2->features =
*((struct wacom_features *)id->driver_info);
wacom_wac->features.pktlen = WACOM_PKGLEN_BBTOUCH3;
wacom_wac->features.device_type = BTN_TOOL_FINGER;
wacom_set_phy_from_res(&wacom_wac->features);
wacom_wac->features.x_max = wacom_wac->features.y_max = 4096;
wacom_register_input(wacom);
wacom_wac2->features.pktlen = WACOM_PKGLEN_BBTOUCH3;
wacom_wac2->features.device_type = BTN_TOOL_FINGER;
wacom_set_phy_from_res(&wacom_wac2->features);
wacom_wac2->features.x_max = wacom_wac2->features.y_max = 4096;
error = wacom_register_input(wacom2);
if (error)
goto fail2;
error = wacom_initialize_battery(wacom);
if (error)
goto fail3;
}
return;
fail3:
input_unregister_device(wacom_wac2->input);
wacom_wac2->input = NULL;
fail2:
input_unregister_device(wacom_wac1->input);
wacom_wac1->input = NULL;
fail1:
return;
}
static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id)
@ -1149,10 +1176,7 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
features->device_type = BTN_TOOL_FINGER;
features->pktlen = WACOM_PKGLEN_BBTOUCH3;
features->x_phy =
(features->x_max * 100) / features->x_resolution;
features->y_phy =
(features->y_max * 100) / features->y_resolution;
wacom_set_phy_from_res(features);
features->x_max = 4096;
features->y_max = 4096;
@ -1188,14 +1212,10 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
if (error)
goto fail4;
error = wacom_initialize_battery(wacom);
if (error)
goto fail5;
if (!(features->quirks & WACOM_QUIRK_NO_INPUT)) {
error = wacom_register_input(wacom);
if (error)
goto fail6;
goto fail5;
}
/* Note that if query fails it is not a hard failure */
@ -1210,7 +1230,6 @@ static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *i
return 0;
fail6: wacom_destroy_battery(wacom);
fail5: wacom_destroy_leds(wacom);
fail4: wacom_remove_shared_data(wacom_wac);
fail3: usb_free_urb(wacom->irq);

View File

@ -248,7 +248,7 @@ static int wacom_graphire_irq(struct wacom_wac *wacom)
input_report_abs(input, ABS_X, le16_to_cpup((__le16 *)&data[2]));
input_report_abs(input, ABS_Y, le16_to_cpup((__le16 *)&data[4]));
if (wacom->tool[0] != BTN_TOOL_MOUSE) {
input_report_abs(input, ABS_PRESSURE, data[6] | ((data[7] & 0x01) << 8));
input_report_abs(input, ABS_PRESSURE, data[6] | ((data[7] & 0x03) << 8));
input_report_key(input, BTN_TOUCH, data[1] & 0x01);
input_report_key(input, BTN_STYLUS, data[1] & 0x02);
input_report_key(input, BTN_STYLUS2, data[1] & 0x04);
@ -888,7 +888,7 @@ static int wacom_tpc_single_touch(struct wacom_wac *wacom, size_t len)
prox = data[0] & 0x01;
x = get_unaligned_le16(&data[1]);
y = get_unaligned_le16(&data[3]);
} else { /* with capacity */
} else {
prox = data[1] & 0x01;
x = le16_to_cpup((__le16 *)&data[2]);
y = le16_to_cpup((__le16 *)&data[4]);
@ -961,6 +961,7 @@ static int wacom_tpc_irq(struct wacom_wac *wacom, size_t len)
case WACOM_REPORT_TPC1FG:
case WACOM_REPORT_TPCHID:
case WACOM_REPORT_TPCST:
case WACOM_REPORT_TPC1FGE:
return wacom_tpc_single_touch(wacom, len);
case WACOM_REPORT_TPCMT:
@ -1244,6 +1245,7 @@ void wacom_wac_irq(struct wacom_wac *wacom_wac, size_t len)
break;
case TABLETPC:
case TABLETPCE:
case TABLETPC2FG:
case MTSCREEN:
sync = wacom_tpc_irq(wacom_wac, len);
@ -1317,10 +1319,8 @@ void wacom_setup_device_quirks(struct wacom_features *features)
}
/* these device have multiple inputs */
if (features->type == TABLETPC || features->type == TABLETPC2FG ||
features->type == BAMBOO_PT || features->type == WIRELESS ||
(features->type >= INTUOS5S && features->type <= INTUOS5L) ||
features->type == MTSCREEN)
if (features->type >= WIRELESS ||
(features->type >= INTUOS5S && features->type <= INTUOS5L))
features->quirks |= WACOM_QUIRK_MULTI_INPUT;
/* quirk for bamboo touch with 2 low res touches */
@ -1547,10 +1547,8 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
__set_bit(INPUT_PROP_POINTER, input_dev->propbit);
break;
case TABLETPC2FG:
case MTSCREEN:
if (features->device_type == BTN_TOOL_FINGER) {
wacom_wac->slots = kmalloc(features->touch_max *
sizeof(int),
GFP_KERNEL);
@ -1559,7 +1557,11 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
for (i = 0; i < features->touch_max; i++)
wacom_wac->slots[i] = -1;
}
/* fall through */
case TABLETPC2FG:
if (features->device_type == BTN_TOOL_FINGER) {
input_mt_init_slots(input_dev, features->touch_max);
input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE,
0, MT_TOOL_MAX, 0, 0);
@ -1571,6 +1573,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
/* fall through */
case TABLETPC:
case TABLETPCE:
__clear_bit(ABS_MISC, input_dev->absbit);
__set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
@ -1888,6 +1891,12 @@ static const struct wacom_features wacom_features_0xE6 =
static const struct wacom_features wacom_features_0xEC =
{ "Wacom ISDv4 EC", WACOM_PKGLEN_GRAPHIRE, 25710, 14500, 255,
0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
static const struct wacom_features wacom_features_0xED =
{ "Wacom ISDv4 ED", WACOM_PKGLEN_GRAPHIRE, 26202, 16325, 255,
0, TABLETPCE, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
static const struct wacom_features wacom_features_0xEF =
{ "Wacom ISDv4 EF", WACOM_PKGLEN_GRAPHIRE, 26202, 16325, 255,
0, TABLETPC, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
static const struct wacom_features wacom_features_0x47 =
{ "Wacom Intuos2 6x8", WACOM_PKGLEN_INTUOS, 20320, 16240, 1023,
31, INTUOS, WACOM_INTUOS_RES, WACOM_INTUOS_RES };
@ -2062,6 +2071,8 @@ const struct usb_device_id wacom_ids[] = {
{ USB_DEVICE_WACOM(0xE5) },
{ USB_DEVICE_WACOM(0xE6) },
{ USB_DEVICE_WACOM(0xEC) },
{ USB_DEVICE_WACOM(0xED) },
{ USB_DEVICE_WACOM(0xEF) },
{ USB_DEVICE_WACOM(0x47) },
{ USB_DEVICE_WACOM(0xF4) },
{ USB_DEVICE_LENOVO(0x6004) },

View File

@ -48,6 +48,7 @@
#define WACOM_REPORT_TPCMT 13
#define WACOM_REPORT_TPCHID 15
#define WACOM_REPORT_TPCST 16
#define WACOM_REPORT_TPC1FGE 18
/* device quirks */
#define WACOM_QUIRK_MULTI_INPUT 0x0001
@ -62,8 +63,6 @@ enum {
PTU,
PL,
DTU,
BAMBOO_PT,
WIRELESS,
INTUOS,
INTUOS3S,
INTUOS3,
@ -79,7 +78,10 @@ enum {
CINTIQ,
WACOM_BEE,
WACOM_MO,
TABLETPC,
WIRELESS,
BAMBOO_PT,
TABLETPC, /* add new TPC below */
TABLETPCE,
TABLETPC2FG,
MTSCREEN,
MAX_TYPE

View File

@ -369,6 +369,18 @@ config TOUCHSCREEN_MCS5000
To compile this driver as a module, choose M here: the
module will be called mcs5000_ts.
config TOUCHSCREEN_MMS114
tristate "MELFAS MMS114 touchscreen"
depends on I2C
help
Say Y here if you have the MELFAS MMS114 touchscreen controller
chip in your system.
If unsure, say N.
To compile this driver as a module, choose M here: the
module will be called mms114.
config TOUCHSCREEN_MTOUCH
tristate "MicroTouch serial touchscreens"
select SERIO

View File

@ -38,6 +38,7 @@ obj-$(CONFIG_TOUCHSCREEN_MAX11801) += max11801_ts.o
obj-$(CONFIG_TOUCHSCREEN_MC13783) += mc13783_ts.o
obj-$(CONFIG_TOUCHSCREEN_MCS5000) += mcs5000_ts.o
obj-$(CONFIG_TOUCHSCREEN_MIGOR) += migor_ts.o
obj-$(CONFIG_TOUCHSCREEN_MMS114) += mms114.o
obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o
obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o
obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o

View File

@ -118,6 +118,7 @@ struct ad7879 {
unsigned int irq;
bool disabled; /* P: input->mutex */
bool suspended; /* P: input->mutex */
bool swap_xy;
u16 conversion_data[AD7879_NR_SENSE];
char phys[32];
u8 first_conversion_delay;
@ -161,6 +162,9 @@ static int ad7879_report(struct ad7879 *ts)
z1 = ts->conversion_data[AD7879_SEQ_Z1] & MAX_12BIT;
z2 = ts->conversion_data[AD7879_SEQ_Z2] & MAX_12BIT;
if (ts->swap_xy)
swap(x, y);
/*
* The samples processed here are already preprocessed by the AD7879.
* The preprocessing function consists of a median and an averaging
@ -520,6 +524,7 @@ struct ad7879 *ad7879_probe(struct device *dev, u8 devid, unsigned int irq,
ts->dev = dev;
ts->input = input_dev;
ts->irq = irq;
ts->swap_xy = pdata->swap_xy;
setup_timer(&ts->timer, ad7879_timer, (unsigned long) ts);

View File

@ -36,6 +36,7 @@
#define MXT_FW_NAME "maxtouch.fw"
/* Registers */
#define MXT_INFO 0x00
#define MXT_FAMILY_ID 0x00
#define MXT_VARIANT_ID 0x01
#define MXT_VERSION 0x02
@ -194,6 +195,7 @@
#define MXT_BOOT_STATUS_MASK 0x3f
/* Touch status */
#define MXT_UNGRIP (1 << 0)
#define MXT_SUPPRESS (1 << 1)
#define MXT_AMP (1 << 2)
#define MXT_VECTOR (1 << 3)
@ -210,8 +212,6 @@
/* Touchscreen absolute values */
#define MXT_MAX_AREA 0xff
#define MXT_MAX_FINGER 10
struct mxt_info {
u8 family_id;
u8 variant_id;
@ -225,44 +225,37 @@ struct mxt_info {
struct mxt_object {
u8 type;
u16 start_address;
u8 size;
u8 instances;
u8 size; /* Size of each instance - 1 */
u8 instances; /* Number of instances - 1 */
u8 num_report_ids;
/* to map object and message */
u8 max_reportid;
};
} __packed;
struct mxt_message {
u8 reportid;
u8 message[7];
};
struct mxt_finger {
int status;
int x;
int y;
int area;
int pressure;
};
/* Each client has this additional data */
struct mxt_data {
struct i2c_client *client;
struct input_dev *input_dev;
char phys[64]; /* device physical location */
const struct mxt_platform_data *pdata;
struct mxt_object *object_table;
struct mxt_info info;
struct mxt_finger finger[MXT_MAX_FINGER];
unsigned int irq;
unsigned int max_x;
unsigned int max_y;
/* Cached parameters from object table */
u8 T6_reportid;
u8 T9_reportid_min;
u8 T9_reportid_max;
};
static bool mxt_object_readable(unsigned int type)
{
switch (type) {
case MXT_GEN_MESSAGE_T5:
case MXT_GEN_COMMAND_T6:
case MXT_GEN_POWER_T7:
case MXT_GEN_ACQUIRE_T8:
@ -396,6 +389,7 @@ static int __mxt_read_reg(struct i2c_client *client,
{
struct i2c_msg xfer[2];
u8 buf[2];
int ret;
buf[0] = reg & 0xff;
buf[1] = (reg >> 8) & 0xff;
@ -412,12 +406,17 @@ static int __mxt_read_reg(struct i2c_client *client,
xfer[1].len = len;
xfer[1].buf = val;
if (i2c_transfer(client->adapter, xfer, 2) != 2) {
dev_err(&client->dev, "%s: i2c transfer failed\n", __func__);
return -EIO;
ret = i2c_transfer(client->adapter, xfer, 2);
if (ret == 2) {
ret = 0;
} else {
if (ret >= 0)
ret = -EIO;
dev_err(&client->dev, "%s: i2c transfer failed (%d)\n",
__func__, ret);
}
return 0;
return ret;
}
static int mxt_read_reg(struct i2c_client *client, u16 reg, u8 *val)
@ -425,27 +424,39 @@ static int mxt_read_reg(struct i2c_client *client, u16 reg, u8 *val)
return __mxt_read_reg(client, reg, 1, val);
}
static int mxt_write_reg(struct i2c_client *client, u16 reg, u8 val)
static int __mxt_write_reg(struct i2c_client *client, u16 reg, u16 len,
const void *val)
{
u8 buf[3];
u8 *buf;
size_t count;
int ret;
count = len + 2;
buf = kmalloc(count, GFP_KERNEL);
if (!buf)
return -ENOMEM;
buf[0] = reg & 0xff;
buf[1] = (reg >> 8) & 0xff;
buf[2] = val;
memcpy(&buf[2], val, len);
if (i2c_master_send(client, buf, 3) != 3) {
dev_err(&client->dev, "%s: i2c send failed\n", __func__);
return -EIO;
ret = i2c_master_send(client, buf, count);
if (ret == count) {
ret = 0;
} else {
if (ret >= 0)
ret = -EIO;
dev_err(&client->dev, "%s: i2c send failed (%d)\n",
__func__, ret);
}
return 0;
kfree(buf);
return ret;
}
static int mxt_read_object_table(struct i2c_client *client,
u16 reg, u8 *object_buf)
static int mxt_write_reg(struct i2c_client *client, u16 reg, u8 val)
{
return __mxt_read_reg(client, reg, MXT_OBJECT_SIZE,
object_buf);
return __mxt_write_reg(client, reg, 1, &val);
}
static struct mxt_object *
@ -479,20 +490,6 @@ static int mxt_read_message(struct mxt_data *data,
sizeof(struct mxt_message), message);
}
static int mxt_read_object(struct mxt_data *data,
u8 type, u8 offset, u8 *val)
{
struct mxt_object *object;
u16 reg;
object = mxt_get_object(data, type);
if (!object)
return -EINVAL;
reg = object->start_address;
return __mxt_read_reg(data->client, reg + offset, 1, val);
}
static int mxt_write_object(struct mxt_data *data,
u8 type, u8 offset, u8 val)
{
@ -507,75 +504,17 @@ static int mxt_write_object(struct mxt_data *data,
return mxt_write_reg(data->client, reg + offset, val);
}
static void mxt_input_report(struct mxt_data *data, int single_id)
{
struct mxt_finger *finger = data->finger;
struct input_dev *input_dev = data->input_dev;
int status = finger[single_id].status;
int finger_num = 0;
int id;
for (id = 0; id < MXT_MAX_FINGER; id++) {
if (!finger[id].status)
continue;
input_mt_slot(input_dev, id);
input_mt_report_slot_state(input_dev, MT_TOOL_FINGER,
finger[id].status != MXT_RELEASE);
if (finger[id].status != MXT_RELEASE) {
finger_num++;
input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR,
finger[id].area);
input_report_abs(input_dev, ABS_MT_POSITION_X,
finger[id].x);
input_report_abs(input_dev, ABS_MT_POSITION_Y,
finger[id].y);
input_report_abs(input_dev, ABS_MT_PRESSURE,
finger[id].pressure);
} else {
finger[id].status = 0;
}
}
input_report_key(input_dev, BTN_TOUCH, finger_num > 0);
if (status != MXT_RELEASE) {
input_report_abs(input_dev, ABS_X, finger[single_id].x);
input_report_abs(input_dev, ABS_Y, finger[single_id].y);
input_report_abs(input_dev,
ABS_PRESSURE, finger[single_id].pressure);
}
input_sync(input_dev);
}
static void mxt_input_touchevent(struct mxt_data *data,
struct mxt_message *message, int id)
{
struct mxt_finger *finger = data->finger;
struct device *dev = &data->client->dev;
u8 status = message->message[0];
struct input_dev *input_dev = data->input_dev;
int x;
int y;
int area;
int pressure;
/* Check the touch is present on the screen */
if (!(status & MXT_DETECT)) {
if (status & MXT_RELEASE) {
dev_dbg(dev, "[%d] released\n", id);
finger[id].status = MXT_RELEASE;
mxt_input_report(data, id);
}
return;
}
/* Check only AMP detection */
if (!(status & (MXT_PRESS | MXT_MOVE)))
return;
x = (message->message[1] << 4) | ((message->message[3] >> 4) & 0xf);
y = (message->message[2] << 4) | ((message->message[3] & 0xf));
if (data->max_x < 1024)
@ -586,30 +525,50 @@ static void mxt_input_touchevent(struct mxt_data *data,
area = message->message[4];
pressure = message->message[5];
dev_dbg(dev, "[%d] %s x: %d, y: %d, area: %d\n", id,
status & MXT_MOVE ? "moved" : "pressed",
x, y, area);
dev_dbg(dev,
"[%u] %c%c%c%c%c%c%c%c x: %5u y: %5u area: %3u amp: %3u\n",
id,
(status & MXT_DETECT) ? 'D' : '.',
(status & MXT_PRESS) ? 'P' : '.',
(status & MXT_RELEASE) ? 'R' : '.',
(status & MXT_MOVE) ? 'M' : '.',
(status & MXT_VECTOR) ? 'V' : '.',
(status & MXT_AMP) ? 'A' : '.',
(status & MXT_SUPPRESS) ? 'S' : '.',
(status & MXT_UNGRIP) ? 'U' : '.',
x, y, area, pressure);
finger[id].status = status & MXT_MOVE ?
MXT_MOVE : MXT_PRESS;
finger[id].x = x;
finger[id].y = y;
finger[id].area = area;
finger[id].pressure = pressure;
input_mt_slot(input_dev, id);
input_mt_report_slot_state(input_dev, MT_TOOL_FINGER,
status & MXT_DETECT);
mxt_input_report(data, id);
if (status & MXT_DETECT) {
input_report_abs(input_dev, ABS_MT_POSITION_X, x);
input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
input_report_abs(input_dev, ABS_MT_PRESSURE, pressure);
input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, area);
}
}
static unsigned mxt_extract_T6_csum(const u8 *csum)
{
return csum[0] | (csum[1] << 8) | (csum[2] << 16);
}
static bool mxt_is_T9_message(struct mxt_data *data, struct mxt_message *msg)
{
u8 id = msg->reportid;
return (id >= data->T9_reportid_min && id <= data->T9_reportid_max);
}
static irqreturn_t mxt_interrupt(int irq, void *dev_id)
{
struct mxt_data *data = dev_id;
struct mxt_message message;
struct mxt_object *object;
const u8 *payload = &message.message[0];
struct device *dev = &data->client->dev;
int id;
u8 reportid;
u8 max_reportid;
u8 min_reportid;
bool update_input = false;
do {
if (mxt_read_message(data, &message)) {
@ -619,21 +578,25 @@ static irqreturn_t mxt_interrupt(int irq, void *dev_id)
reportid = message.reportid;
/* whether reportid is thing of MXT_TOUCH_MULTI_T9 */
object = mxt_get_object(data, MXT_TOUCH_MULTI_T9);
if (!object)
goto end;
max_reportid = object->max_reportid;
min_reportid = max_reportid - object->num_report_ids + 1;
id = reportid - min_reportid;
if (reportid >= min_reportid && reportid <= max_reportid)
if (reportid == data->T6_reportid) {
u8 status = payload[0];
unsigned csum = mxt_extract_T6_csum(&payload[1]);
dev_dbg(dev, "Status: %02x Config Checksum: %06x\n",
status, csum);
} else if (mxt_is_T9_message(data, &message)) {
int id = reportid - data->T9_reportid_min;
mxt_input_touchevent(data, &message, id);
else
update_input = true;
} else {
mxt_dump_message(dev, &message);
}
} while (reportid != 0xff);
if (update_input) {
input_mt_report_pointer_emulation(data->input_dev, false);
input_sync(data->input_dev);
}
end:
return IRQ_HANDLED;
}
@ -644,7 +607,8 @@ static int mxt_check_reg_init(struct mxt_data *data)
struct mxt_object *object;
struct device *dev = &data->client->dev;
int index = 0;
int i, j, config_offset;
int i, size;
int ret;
if (!pdata->config) {
dev_dbg(dev, "No cfg data defined, skipping reg init\n");
@ -657,18 +621,17 @@ static int mxt_check_reg_init(struct mxt_data *data)
if (!mxt_object_writable(object->type))
continue;
for (j = 0;
j < (object->size + 1) * (object->instances + 1);
j++) {
config_offset = index + j;
if (config_offset > pdata->config_length) {
dev_err(dev, "Not enough config data!\n");
return -EINVAL;
}
mxt_write_object(data, object->type, j,
pdata->config[config_offset]);
size = (object->size + 1) * (object->instances + 1);
if (index + size > pdata->config_length) {
dev_err(dev, "Not enough config data!\n");
return -EINVAL;
}
index += (object->size + 1) * (object->instances + 1);
ret = __mxt_write_reg(data->client, object->start_address,
size, &pdata->config[index]);
if (ret)
return ret;
index += size;
}
return 0;
@ -749,68 +712,76 @@ static int mxt_get_info(struct mxt_data *data)
struct i2c_client *client = data->client;
struct mxt_info *info = &data->info;
int error;
u8 val;
error = mxt_read_reg(client, MXT_FAMILY_ID, &val);
/* Read 7-byte info block starting at address 0 */
error = __mxt_read_reg(client, MXT_INFO, sizeof(*info), info);
if (error)
return error;
info->family_id = val;
error = mxt_read_reg(client, MXT_VARIANT_ID, &val);
if (error)
return error;
info->variant_id = val;
error = mxt_read_reg(client, MXT_VERSION, &val);
if (error)
return error;
info->version = val;
error = mxt_read_reg(client, MXT_BUILD, &val);
if (error)
return error;
info->build = val;
error = mxt_read_reg(client, MXT_OBJECT_NUM, &val);
if (error)
return error;
info->object_num = val;
return 0;
}
static int mxt_get_object_table(struct mxt_data *data)
{
struct i2c_client *client = data->client;
size_t table_size;
int error;
int i;
u16 reg;
u8 reportid = 0;
u8 buf[MXT_OBJECT_SIZE];
u8 reportid;
table_size = data->info.object_num * sizeof(struct mxt_object);
error = __mxt_read_reg(client, MXT_OBJECT_START, table_size,
data->object_table);
if (error)
return error;
/* Valid Report IDs start counting from 1 */
reportid = 1;
for (i = 0; i < data->info.object_num; i++) {
struct mxt_object *object = data->object_table + i;
u8 min_id, max_id;
reg = MXT_OBJECT_START + MXT_OBJECT_SIZE * i;
error = mxt_read_object_table(data->client, reg, buf);
if (error)
return error;
object->type = buf[0];
object->start_address = (buf[2] << 8) | buf[1];
object->size = buf[3];
object->instances = buf[4];
object->num_report_ids = buf[5];
le16_to_cpus(&object->start_address);
if (object->num_report_ids) {
min_id = reportid;
reportid += object->num_report_ids *
(object->instances + 1);
object->max_reportid = reportid;
max_id = reportid - 1;
} else {
min_id = 0;
max_id = 0;
}
dev_dbg(&data->client->dev,
"Type %2d Start %3d Size %3d Instances %2d ReportIDs %3u : %3u\n",
object->type, object->start_address, object->size + 1,
object->instances + 1, min_id, max_id);
switch (object->type) {
case MXT_GEN_COMMAND_T6:
data->T6_reportid = min_id;
break;
case MXT_TOUCH_MULTI_T9:
data->T9_reportid_min = min_id;
data->T9_reportid_max = max_id;
break;
}
}
return 0;
}
static void mxt_free_object_table(struct mxt_data *data)
{
kfree(data->object_table);
data->object_table = NULL;
data->T6_reportid = 0;
data->T9_reportid_min = 0;
data->T9_reportid_max = 0;
}
static int mxt_initialize(struct mxt_data *data)
{
struct i2c_client *client = data->client;
@ -833,12 +804,12 @@ static int mxt_initialize(struct mxt_data *data)
/* Get object table information */
error = mxt_get_object_table(data);
if (error)
return error;
goto err_free_object_table;
/* Check register init values */
error = mxt_check_reg_init(data);
if (error)
return error;
goto err_free_object_table;
mxt_handle_pdata(data);
@ -856,25 +827,29 @@ static int mxt_initialize(struct mxt_data *data)
/* Update matrix size at info struct */
error = mxt_read_reg(client, MXT_MATRIX_X_SIZE, &val);
if (error)
return error;
goto err_free_object_table;
info->matrix_xsize = val;
error = mxt_read_reg(client, MXT_MATRIX_Y_SIZE, &val);
if (error)
return error;
goto err_free_object_table;
info->matrix_ysize = val;
dev_info(&client->dev,
"Family ID: %d Variant ID: %d Version: %d Build: %d\n",
info->family_id, info->variant_id, info->version,
info->build);
"Family ID: %u Variant ID: %u Major.Minor.Build: %u.%u.%02X\n",
info->family_id, info->variant_id, info->version >> 4,
info->version & 0xf, info->build);
dev_info(&client->dev,
"Matrix X Size: %d Matrix Y Size: %d Object Num: %d\n",
"Matrix X Size: %u Matrix Y Size: %u Object Num: %u\n",
info->matrix_xsize, info->matrix_ysize,
info->object_num);
return 0;
err_free_object_table:
mxt_free_object_table(data);
return error;
}
static void mxt_calc_resolution(struct mxt_data *data)
@ -891,6 +866,44 @@ static void mxt_calc_resolution(struct mxt_data *data)
}
}
/* Firmware Version is returned as Major.Minor.Build */
static ssize_t mxt_fw_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mxt_data *data = dev_get_drvdata(dev);
struct mxt_info *info = &data->info;
return scnprintf(buf, PAGE_SIZE, "%u.%u.%02X\n",
info->version >> 4, info->version & 0xf, info->build);
}
/* Hardware Version is returned as FamilyID.VariantID */
static ssize_t mxt_hw_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct mxt_data *data = dev_get_drvdata(dev);
struct mxt_info *info = &data->info;
return scnprintf(buf, PAGE_SIZE, "%u.%u\n",
info->family_id, info->variant_id);
}
static ssize_t mxt_show_instance(char *buf, int count,
struct mxt_object *object, int instance,
const u8 *val)
{
int i;
if (object->instances > 0)
count += scnprintf(buf + count, PAGE_SIZE - count,
"Instance %u\n", instance);
for (i = 0; i < object->size + 1; i++)
count += scnprintf(buf + count, PAGE_SIZE - count,
"\t[%2u]: %02x (%d)\n", i, val[i], val[i]);
count += scnprintf(buf + count, PAGE_SIZE - count, "\n");
return count;
}
static ssize_t mxt_object_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@ -899,43 +912,38 @@ static ssize_t mxt_object_show(struct device *dev,
int count = 0;
int i, j;
int error;
u8 val;
u8 *obuf;
/* Pre-allocate buffer large enough to hold max sized object. */
obuf = kmalloc(256, GFP_KERNEL);
if (!obuf)
return -ENOMEM;
error = 0;
for (i = 0; i < data->info.object_num; i++) {
object = data->object_table + i;
count += snprintf(buf + count, PAGE_SIZE - count,
"Object[%d] (Type %d)\n",
i + 1, object->type);
if (count >= PAGE_SIZE)
return PAGE_SIZE - 1;
if (!mxt_object_readable(object->type)) {
count += snprintf(buf + count, PAGE_SIZE - count,
"\n");
if (count >= PAGE_SIZE)
return PAGE_SIZE - 1;
if (!mxt_object_readable(object->type))
continue;
}
for (j = 0; j < object->size + 1; j++) {
error = mxt_read_object(data,
object->type, j, &val);
count += scnprintf(buf + count, PAGE_SIZE - count,
"T%u:\n", object->type);
for (j = 0; j < object->instances + 1; j++) {
u16 size = object->size + 1;
u16 addr = object->start_address + j * size;
error = __mxt_read_reg(data->client, addr, size, obuf);
if (error)
return error;
goto done;
count += snprintf(buf + count, PAGE_SIZE - count,
"\t[%2d]: %02x (%d)\n", j, val, val);
if (count >= PAGE_SIZE)
return PAGE_SIZE - 1;
count = mxt_show_instance(buf, count, object, j, obuf);
}
count += snprintf(buf + count, PAGE_SIZE - count, "\n");
if (count >= PAGE_SIZE)
return PAGE_SIZE - 1;
}
return count;
done:
kfree(obuf);
return error ?: count;
}
static int mxt_load_fw(struct device *dev, const char *fn)
@ -1028,8 +1036,7 @@ static ssize_t mxt_update_fw_store(struct device *dev,
/* Wait for reset */
msleep(MXT_FWRESET_TIME);
kfree(data->object_table);
data->object_table = NULL;
mxt_free_object_table(data);
mxt_initialize(data);
}
@ -1043,10 +1050,14 @@ static ssize_t mxt_update_fw_store(struct device *dev,
return count;
}
static DEVICE_ATTR(fw_version, S_IRUGO, mxt_fw_version_show, NULL);
static DEVICE_ATTR(hw_version, S_IRUGO, mxt_hw_version_show, NULL);
static DEVICE_ATTR(object, S_IRUGO, mxt_object_show, NULL);
static DEVICE_ATTR(update_fw, S_IWUSR, NULL, mxt_update_fw_store);
static struct attribute *mxt_attrs[] = {
&dev_attr_fw_version.attr,
&dev_attr_hw_version.attr,
&dev_attr_object.attr,
&dev_attr_update_fw.attr,
NULL
@ -1093,6 +1104,7 @@ static int __devinit mxt_probe(struct i2c_client *client,
struct mxt_data *data;
struct input_dev *input_dev;
int error;
unsigned int num_mt_slots;
if (!pdata)
return -EINVAL;
@ -1106,6 +1118,10 @@ static int __devinit mxt_probe(struct i2c_client *client,
}
input_dev->name = "Atmel maXTouch Touchscreen";
snprintf(data->phys, sizeof(data->phys), "i2c-%u-%04x/input0",
client->adapter->nr, client->addr);
input_dev->phys = data->phys;
input_dev->id.bustype = BUS_I2C;
input_dev->dev.parent = &client->dev;
input_dev->open = mxt_input_open;
@ -1118,6 +1134,10 @@ static int __devinit mxt_probe(struct i2c_client *client,
mxt_calc_resolution(data);
error = mxt_initialize(data);
if (error)
goto err_free_mem;
__set_bit(EV_ABS, input_dev->evbit);
__set_bit(EV_KEY, input_dev->evbit);
__set_bit(BTN_TOUCH, input_dev->keybit);
@ -1131,7 +1151,10 @@ static int __devinit mxt_probe(struct i2c_client *client,
0, 255, 0, 0);
/* For multi touch */
input_mt_init_slots(input_dev, MXT_MAX_FINGER);
num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1;
error = input_mt_init_slots(input_dev, num_mt_slots);
if (error)
goto err_free_object;
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
0, MXT_MAX_AREA, 0, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_X,
@ -1144,13 +1167,9 @@ static int __devinit mxt_probe(struct i2c_client *client,
input_set_drvdata(input_dev, data);
i2c_set_clientdata(client, data);
error = mxt_initialize(data);
if (error)
goto err_free_object;
error = request_threaded_irq(client->irq, NULL, mxt_interrupt,
pdata->irqflags | IRQF_ONESHOT,
client->dev.driver->name, data);
client->name, data);
if (error) {
dev_err(&client->dev, "Failed to register interrupt\n");
goto err_free_object;

View File

@ -0,0 +1,544 @@
/*
* Copyright (C) 2012 Samsung Electronics Co.Ltd
* Author: Joonyoung Shim <jy0922.shim@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/i2c/mms114.h>
#include <linux/input/mt.h>
#include <linux/interrupt.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
/* Write only registers */
#define MMS114_MODE_CONTROL 0x01
#define MMS114_OPERATION_MODE_MASK 0xE
#define MMS114_ACTIVE (1 << 1)
#define MMS114_XY_RESOLUTION_H 0x02
#define MMS114_X_RESOLUTION 0x03
#define MMS114_Y_RESOLUTION 0x04
#define MMS114_CONTACT_THRESHOLD 0x05
#define MMS114_MOVING_THRESHOLD 0x06
/* Read only registers */
#define MMS114_PACKET_SIZE 0x0F
#define MMS114_INFOMATION 0x10
#define MMS114_TSP_REV 0xF0
/* Minimum delay time is 50us between stop and start signal of i2c */
#define MMS114_I2C_DELAY 50
/* 200ms needs after power on */
#define MMS114_POWERON_DELAY 200
/* Touchscreen absolute values */
#define MMS114_MAX_AREA 0xff
#define MMS114_MAX_TOUCH 10
#define MMS114_PACKET_NUM 8
/* Touch type */
#define MMS114_TYPE_NONE 0
#define MMS114_TYPE_TOUCHSCREEN 1
#define MMS114_TYPE_TOUCHKEY 2
struct mms114_data {
struct i2c_client *client;
struct input_dev *input_dev;
struct regulator *core_reg;
struct regulator *io_reg;
const struct mms114_platform_data *pdata;
/* Use cache data for mode control register(write only) */
u8 cache_mode_control;
};
struct mms114_touch {
u8 id:4, reserved_bit4:1, type:2, pressed:1;
u8 x_hi:4, y_hi:4;
u8 x_lo;
u8 y_lo;
u8 width;
u8 strength;
u8 reserved[2];
} __packed;
static int __mms114_read_reg(struct mms114_data *data, unsigned int reg,
unsigned int len, u8 *val)
{
struct i2c_client *client = data->client;
struct i2c_msg xfer[2];
u8 buf = reg & 0xff;
int error;
if (reg <= MMS114_MODE_CONTROL && reg + len > MMS114_MODE_CONTROL)
BUG();
/* Write register: use repeated start */
xfer[0].addr = client->addr;
xfer[0].flags = I2C_M_TEN | I2C_M_NOSTART;
xfer[0].len = 1;
xfer[0].buf = &buf;
/* Read data */
xfer[1].addr = client->addr;
xfer[1].flags = I2C_M_RD;
xfer[1].len = len;
xfer[1].buf = val;
error = i2c_transfer(client->adapter, xfer, 2);
if (error != 2) {
dev_err(&client->dev,
"%s: i2c transfer failed (%d)\n", __func__, error);
return error < 0 ? error : -EIO;
}
udelay(MMS114_I2C_DELAY);
return 0;
}
static int mms114_read_reg(struct mms114_data *data, unsigned int reg)
{
u8 val;
int error;
if (reg == MMS114_MODE_CONTROL)
return data->cache_mode_control;
error = __mms114_read_reg(data, reg, 1, &val);
return error < 0 ? error : val;
}
static int mms114_write_reg(struct mms114_data *data, unsigned int reg,
unsigned int val)
{
struct i2c_client *client = data->client;
u8 buf[2];
int error;
buf[0] = reg & 0xff;
buf[1] = val & 0xff;
error = i2c_master_send(client, buf, 2);
if (error != 2) {
dev_err(&client->dev,
"%s: i2c send failed (%d)\n", __func__, error);
return error < 0 ? error : -EIO;
}
udelay(MMS114_I2C_DELAY);
if (reg == MMS114_MODE_CONTROL)
data->cache_mode_control = val;
return 0;
}
static void mms114_process_mt(struct mms114_data *data, struct mms114_touch *touch)
{
const struct mms114_platform_data *pdata = data->pdata;
struct i2c_client *client = data->client;
struct input_dev *input_dev = data->input_dev;
unsigned int id;
unsigned int x;
unsigned int y;
if (touch->id > MMS114_MAX_TOUCH) {
dev_err(&client->dev, "Wrong touch id (%d)\n", touch->id);
return;
}
if (touch->type != MMS114_TYPE_TOUCHSCREEN) {
dev_err(&client->dev, "Wrong touch type (%d)\n", touch->type);
return;
}
id = touch->id - 1;
x = touch->x_lo | touch->x_hi << 8;
y = touch->y_lo | touch->y_hi << 8;
if (x > pdata->x_size || y > pdata->y_size) {
dev_dbg(&client->dev,
"Wrong touch coordinates (%d, %d)\n", x, y);
return;
}
if (pdata->x_invert)
x = pdata->x_size - x;
if (pdata->y_invert)
y = pdata->y_size - y;
dev_dbg(&client->dev,
"id: %d, type: %d, pressed: %d, x: %d, y: %d, width: %d, strength: %d\n",
id, touch->type, touch->pressed,
x, y, touch->width, touch->strength);
input_mt_slot(input_dev, id);
input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, touch->pressed);
if (touch->pressed) {
input_report_abs(input_dev, ABS_MT_TOUCH_MAJOR, touch->width);
input_report_abs(input_dev, ABS_MT_POSITION_X, x);
input_report_abs(input_dev, ABS_MT_POSITION_Y, y);
input_report_abs(input_dev, ABS_MT_PRESSURE, touch->strength);
}
}
static irqreturn_t mms114_interrupt(int irq, void *dev_id)
{
struct mms114_data *data = dev_id;
struct input_dev *input_dev = data->input_dev;
struct mms114_touch touch[MMS114_MAX_TOUCH];
int packet_size;
int touch_size;
int index;
int error;
mutex_lock(&input_dev->mutex);
if (!input_dev->users) {
mutex_unlock(&input_dev->mutex);
goto out;
}
mutex_unlock(&input_dev->mutex);
packet_size = mms114_read_reg(data, MMS114_PACKET_SIZE);
if (packet_size <= 0)
goto out;
touch_size = packet_size / MMS114_PACKET_NUM;
error = __mms114_read_reg(data, MMS114_INFOMATION, packet_size,
(u8 *)touch);
if (error < 0)
goto out;
for (index = 0; index < touch_size; index++)
mms114_process_mt(data, touch + index);
input_mt_report_pointer_emulation(data->input_dev, true);
input_sync(data->input_dev);
out:
return IRQ_HANDLED;
}
static int mms114_set_active(struct mms114_data *data, bool active)
{
int val;
val = mms114_read_reg(data, MMS114_MODE_CONTROL);
if (val < 0)
return val;
val &= ~MMS114_OPERATION_MODE_MASK;
/* If active is false, sleep mode */
if (active)
val |= MMS114_ACTIVE;
return mms114_write_reg(data, MMS114_MODE_CONTROL, val);
}
static int mms114_get_version(struct mms114_data *data)
{
struct device *dev = &data->client->dev;
u8 buf[6];
int error;
error = __mms114_read_reg(data, MMS114_TSP_REV, 6, buf);
if (error < 0)
return error;
dev_info(dev, "TSP Rev: 0x%x, HW Rev: 0x%x, Firmware Ver: 0x%x\n",
buf[0], buf[1], buf[3]);
return 0;
}
static int mms114_setup_regs(struct mms114_data *data)
{
const struct mms114_platform_data *pdata = data->pdata;
int val;
int error;
error = mms114_get_version(data);
if (error < 0)
return error;
error = mms114_set_active(data, true);
if (error < 0)
return error;
val = (pdata->x_size >> 8) & 0xf;
val |= ((pdata->y_size >> 8) & 0xf) << 4;
error = mms114_write_reg(data, MMS114_XY_RESOLUTION_H, val);
if (error < 0)
return error;
val = pdata->x_size & 0xff;
error = mms114_write_reg(data, MMS114_X_RESOLUTION, val);
if (error < 0)
return error;
val = pdata->y_size & 0xff;
error = mms114_write_reg(data, MMS114_Y_RESOLUTION, val);
if (error < 0)
return error;
if (pdata->contact_threshold) {
error = mms114_write_reg(data, MMS114_CONTACT_THRESHOLD,
pdata->contact_threshold);
if (error < 0)
return error;
}
if (pdata->moving_threshold) {
error = mms114_write_reg(data, MMS114_MOVING_THRESHOLD,
pdata->moving_threshold);
if (error < 0)
return error;
}
return 0;
}
static int mms114_start(struct mms114_data *data)
{
struct i2c_client *client = data->client;
int error;
if (data->core_reg)
regulator_enable(data->core_reg);
if (data->io_reg)
regulator_enable(data->io_reg);
mdelay(MMS114_POWERON_DELAY);
error = mms114_setup_regs(data);
if (error < 0)
return error;
if (data->pdata->cfg_pin)
data->pdata->cfg_pin(true);
enable_irq(client->irq);
return 0;
}
static void mms114_stop(struct mms114_data *data)
{
struct i2c_client *client = data->client;
disable_irq(client->irq);
if (data->pdata->cfg_pin)
data->pdata->cfg_pin(false);
if (data->io_reg)
regulator_disable(data->io_reg);
if (data->core_reg)
regulator_disable(data->core_reg);
}
static int mms114_input_open(struct input_dev *dev)
{
struct mms114_data *data = input_get_drvdata(dev);
return mms114_start(data);
}
static void mms114_input_close(struct input_dev *dev)
{
struct mms114_data *data = input_get_drvdata(dev);
mms114_stop(data);
}
static int __devinit mms114_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct mms114_data *data;
struct input_dev *input_dev;
int error;
if (!client->dev.platform_data) {
dev_err(&client->dev, "Need platform data\n");
return -EINVAL;
}
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_PROTOCOL_MANGLING)) {
dev_err(&client->dev,
"Need i2c bus that supports protocol mangling\n");
return -ENODEV;
}
data = kzalloc(sizeof(struct mms114_data), GFP_KERNEL);
input_dev = input_allocate_device();
if (!data || !input_dev) {
dev_err(&client->dev, "Failed to allocate memory\n");
error = -ENOMEM;
goto err_free_mem;
}
data->client = client;
data->input_dev = input_dev;
data->pdata = client->dev.platform_data;
input_dev->name = "MELPAS MMS114 Touchscreen";
input_dev->id.bustype = BUS_I2C;
input_dev->dev.parent = &client->dev;
input_dev->open = mms114_input_open;
input_dev->close = mms114_input_close;
__set_bit(EV_ABS, input_dev->evbit);
__set_bit(EV_KEY, input_dev->evbit);
__set_bit(BTN_TOUCH, input_dev->keybit);
input_set_abs_params(input_dev, ABS_X, 0, data->pdata->x_size, 0, 0);
input_set_abs_params(input_dev, ABS_Y, 0, data->pdata->y_size, 0, 0);
/* For multi touch */
input_mt_init_slots(input_dev, MMS114_MAX_TOUCH);
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
0, MMS114_MAX_AREA, 0, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_X,
0, data->pdata->x_size, 0, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
0, data->pdata->y_size, 0, 0);
input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, 255, 0, 0);
input_set_drvdata(input_dev, data);
i2c_set_clientdata(client, data);
data->core_reg = regulator_get(&client->dev, "avdd");
if (IS_ERR(data->core_reg)) {
error = PTR_ERR(data->core_reg);
dev_err(&client->dev,
"Unable to get the Core regulator (%d)\n", error);
goto err_free_mem;
}
data->io_reg = regulator_get(&client->dev, "vdd");
if (IS_ERR(data->io_reg)) {
error = PTR_ERR(data->io_reg);
dev_err(&client->dev,
"Unable to get the IO regulator (%d)\n", error);
goto err_core_reg;
}
error = request_threaded_irq(client->irq, NULL, mms114_interrupt,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "mms114", data);
if (error) {
dev_err(&client->dev, "Failed to register interrupt\n");
goto err_io_reg;
}
disable_irq(client->irq);
error = input_register_device(data->input_dev);
if (error)
goto err_free_irq;
return 0;
err_free_irq:
free_irq(client->irq, data);
err_io_reg:
regulator_put(data->io_reg);
err_core_reg:
regulator_put(data->core_reg);
err_free_mem:
input_free_device(input_dev);
kfree(data);
return error;
}
static int __devexit mms114_remove(struct i2c_client *client)
{
struct mms114_data *data = i2c_get_clientdata(client);
free_irq(client->irq, data);
regulator_put(data->io_reg);
regulator_put(data->core_reg);
input_unregister_device(data->input_dev);
kfree(data);
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int mms114_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct mms114_data *data = i2c_get_clientdata(client);
struct input_dev *input_dev = data->input_dev;
int id;
/* Release all touch */
for (id = 0; id < MMS114_MAX_TOUCH; id++) {
input_mt_slot(input_dev, id);
input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false);
}
input_mt_report_pointer_emulation(input_dev, true);
input_sync(input_dev);
mutex_lock(&input_dev->mutex);
if (input_dev->users)
mms114_stop(data);
mutex_unlock(&input_dev->mutex);
return 0;
}
static int mms114_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct mms114_data *data = i2c_get_clientdata(client);
struct input_dev *input_dev = data->input_dev;
int error;
mutex_lock(&input_dev->mutex);
if (input_dev->users) {
error = mms114_start(data);
if (error < 0) {
mutex_unlock(&input_dev->mutex);
return error;
}
}
mutex_unlock(&input_dev->mutex);
return 0;
}
#endif
static SIMPLE_DEV_PM_OPS(mms114_pm_ops, mms114_suspend, mms114_resume);
static const struct i2c_device_id mms114_id[] = {
{ "mms114", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, mms114_id);
static struct i2c_driver mms114_driver = {
.driver = {
.name = "mms114",
.owner = THIS_MODULE,
.pm = &mms114_pm_ops,
},
.probe = mms114_probe,
.remove = __devexit_p(mms114_remove),
.id_table = mms114_id,
};
module_i2c_driver(mms114_driver);
/* Module information */
MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
MODULE_DESCRIPTION("MELFAS mms114 Touchscreen driver");
MODULE_LICENSE("GPL");

View File

@ -149,7 +149,7 @@ static int __devinit wacom_i2c_probe(struct i2c_client *client,
{
struct wacom_i2c *wac_i2c;
struct input_dev *input;
struct wacom_features features;
struct wacom_features features = { 0 };
int error;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {

View File

@ -0,0 +1,24 @@
/*
* Copyright (C) 2012 Samsung Electronics Co.Ltd
* Author: Joonyoung Shim <jy0922.shim@samsung.com>
*
* 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 Foundationr
*/
#ifndef __LINUX_MMS114_H
#define __LINUX_MMS114_H
struct mms114_platform_data {
unsigned int x_size;
unsigned int y_size;
unsigned int contact_threshold;
unsigned int moving_threshold;
bool x_invert;
bool y_invert;
void (*cfg_pin)(bool);
};
#endif /* __LINUX_MMS114_H */

View File

@ -807,18 +807,20 @@ struct input_keymap_entry {
#define ABS_MT_WIDTH_MAJOR 0x32 /* Major axis of approaching ellipse */
#define ABS_MT_WIDTH_MINOR 0x33 /* Minor axis (omit if circular) */
#define ABS_MT_ORIENTATION 0x34 /* Ellipse orientation */
#define ABS_MT_POSITION_X 0x35 /* Center X ellipse position */
#define ABS_MT_POSITION_Y 0x36 /* Center Y ellipse position */
#define ABS_MT_POSITION_X 0x35 /* Center X touch position */
#define ABS_MT_POSITION_Y 0x36 /* Center Y touch position */
#define ABS_MT_TOOL_TYPE 0x37 /* Type of touching device */
#define ABS_MT_BLOB_ID 0x38 /* Group a set of packets as a blob */
#define ABS_MT_TRACKING_ID 0x39 /* Unique ID of initiated contact */
#define ABS_MT_PRESSURE 0x3a /* Pressure on contact area */
#define ABS_MT_DISTANCE 0x3b /* Contact hover distance */
#define ABS_MT_TOOL_X 0x3c /* Center X tool position */
#define ABS_MT_TOOL_Y 0x3d /* Center Y tool position */
#ifdef __KERNEL__
/* Implementation details, userspace should not care about these */
#define ABS_MT_FIRST ABS_MT_TOUCH_MAJOR
#define ABS_MT_LAST ABS_MT_DISTANCE
#define ABS_MT_LAST ABS_MT_TOOL_Y
#endif
#define ABS_MAX 0x3f

View File

@ -12,6 +12,8 @@ struct ad7879_platform_data {
u16 y_min, y_max;
u16 pressure_min, pressure_max;
bool swap_xy; /* swap x and y axes */
/* [0..255] 0=OFF Starts at 1=550us and goes
* all the way to 9.440ms in steps of 35us.
*/