mirror of https://gitee.com/openkylin/linux.git
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:
commit
945c40c6b0
|
@ -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>;
|
||||
};
|
|
@ -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;
|
||||
};
|
|
@ -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>;
|
||||
};
|
||||
};
|
|
@ -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.
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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++;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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");
|
|
@ -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);
|
||||
|
|
|
@ -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 @@ static int __devinit omap4_keypad_probe(struct platform_device *pdev)
|
|||
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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) },
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
|
@ -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)) {
|
||||
|
|
|
@ -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 */
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue