* Re: [PATCH 01/02] input: serio: use DEVICE_ATTR_RO()
From: Greg Kroah-Hartman @ 2013-10-22 6:12 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: linux-input
In-Reply-To: <20131021162531.GC4575@core.coreip.homeip.net>
On Mon, Oct 21, 2013 at 09:25:32AM -0700, Dmitry Torokhov wrote:
> Hi Greg,
>
> On Mon, Oct 07, 2013 at 06:08:23PM -0700, Greg Kroah-Hartman wrote:
> > From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> >
> > Convert the serio sysfs fiels to use the DEVICE_ATTR_RO() macros to make
> > it easier to audit the correct sysfs file permission usage.
>
> Sorry for the delay. Frankly I am not a fan of DEVICE_ATTR macros as it
> forces particular naming of the attribute structures and methods and
> forces us to lose the module prefix in names. I prefer all functions and
> file-scope variables have module prefix because in the absence of
> language-supported namespaces this is the one thing that insulates
> drivers from changes in shared headers.
No sysfs read/write functions should ever be exported out of the local
file "scope", so why is this an issue? I have run into it being a
problem for some files, where people have device/bus/class sysfs files
all in the same .c file, but that usage is very limited.
I'm trying to eventually convert the whole kernel to use the
DEVICE_ATTR_??() macros to make it so that we can audit the sysfs file
permissions easier, by making them impossible to get wrong. We have had
problems in the past where device files had incorrect permissions and
unprivilidged users could do things they shouldn't have. Using these
macros _should_ prevent almost all of that from even being possible.
Now odds are, I'll never be able to remove DEVICE_ATTR() from the tree,
as there are some strange files that use crazy permissions and we have
to live with that, but for all of the "simple" files, like are done
here, I'd really like to use them, especially as it provides a
kernel-wide uniformity to the naming of attribute function calls, which
is a huge benifit as well.
Note, I did apply this patch to my tree already, and it's queued up for
3.13-rc1. If you really object to it, I'll revert it, and fix up the
02/02 patch in this series to not need it, but I'd really like you to
reconsider your objection.
thanks,
greg k-h
^ permalink raw reply
* Re: [PATCH 1/2] input: touchscreen: fix spelling mistake in TSC/ADC DT binding
From: Sebastian Andrzej Siewior @ 2013-10-22 8:42 UTC (permalink / raw)
To: Felipe Balbi
Cc: dmitry.torokhov, rob.herring, pawel.moll, mark.rutland, swarren,
ijc+devicetree, rob, bcousson, Tony Lindgren, devicetree,
Linux OMAP Mailing List, linux-input
In-Reply-To: <1382386404-6659-2-git-send-email-balbi@ti.com>
On 10/21/2013 10:13 PM, Felipe Balbi wrote:
> diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c
> index e1c5300..b61df9d 100644
> --- a/drivers/input/touchscreen/ti_am335x_tsc.c
> +++ b/drivers/input/touchscreen/ti_am335x_tsc.c
> @@ -348,9 +348,16 @@ static int titsc_parse_dt(struct platform_device *pdev,
> if (err < 0)
> return err;
>
> - err = of_property_read_u32(node, "ti,coordiante-readouts",
> + /*
> + * try with new binding first. If it fails, still try with
> + * bogus, miss-spelled version.
> + */
> + err = of_property_read_u32(node, "ti,coordinate-readouts",
> &ts_dev->coordinate_readouts);
> if (err < 0)
> + err = of_property_read_u32(node, "ti,coordiante-readouts",
> + &ts_dev->coordinate_readouts);
> + if (err < 0)
> return err;
Thanks, very good. Do we keep this fallback for ever or just for a year
or two?
>
> return of_property_read_u32_array(node, "ti,wire-config",
Sebastian
^ permalink raw reply
* Re: [PATCH] typo fixes (coordiante -> coordinate) in am335x
From: Lee Jones @ 2013-10-22 9:05 UTC (permalink / raw)
To: Jan Lübbe, Dmitry Torokhov
Cc: Tony Lindgren, Samuel Ortiz, Zubair Lutfullah, linux-doc,
linux-kernel, linux-omap, linux-arm-kernel, linux-input
In-Reply-To: <1382198544.22609.8.camel@polaris.local>
On Sat, 19 Oct 2013, Jan Lübbe wrote:
> On Wed, 2013-08-21 at 00:58 -0700, Tony Lindgren wrote:
> > * Zubair Lutfullah <zubair.lutfullah@gmail.com> [130715 08:33]:
> > > Did a grep for coordiante and replaced them all
> > > with coordinate.
> > >
> > > This applies to the mfd-next tree.
> >
> > This should be safe to apply via the MFD tree as a non-critical
> > fix assuming the bootloaders are not yet using this:
> >
> > Acked-by: Tony Lindgren <tony@atomide.com>
>
> It seems this didn't get applied. It fixes the touchscreen on a
> BeagleBone black with the 7" LCD and we should avoid having people use
> the wrong binding.
>
> Samuel or Lee: Could you take this patch?
This is the first time this patch has been sent to me.
I need Dmitry's input (no pun intended) on how he's like to deal with
this. At a bare minimum I'd like his Ack.
--
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
^ permalink raw reply
* Re: [PATCHv4][ 1/4] Input: tsc2007: Add device tree support.
From: Lothar Waßmann @ 2013-10-22 9:49 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: Denis Carikli, linux-input, Sascha Hauer, linux-arm-kernel,
Eric Bénard, Rob Herring, Pawel Moll, Mark Rutland,
Stephen Warren, Ian Campbell, devicetree
In-Reply-To: <20131021155927.GB4255@core.coreip.homeip.net>
Hi,
> On Mon, Oct 21, 2013 at 03:54:24PM +0200, Denis Carikli wrote:
> >
> > + if (ts->of)
> > + return tsc2007_get_pendown_state_dt(ts);
> > +
> > if (!ts->get_pendown_state)
> > return true;
>
> Instead of special casing "if (ts->of)" all over the place why don't you
> set up the device structure as:
>
> if (<configuring_tsc2007_form_dt>)
> ts->get_pendown_state = tsc2007_get_pendown_state_dt;
>
> and be done with it?
>
I also thought about that, but the existing function does not have any
parameters, while the DT version of get_pendown_state() requires to get
the GPIO passed to it somehow.
Lothar Waßmann
--
___________________________________________________________
Ka-Ro electronics GmbH | Pascalstraße 22 | D - 52076 Aachen
Phone: +49 2408 1402-0 | Fax: +49 2408 1402-10
Geschäftsführer: Matthias Kaussen
Handelsregistereintrag: Amtsgericht Aachen, HRB 4996
www.karo-electronics.de | info@karo-electronics.de
___________________________________________________________
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: WPC8769L (WEC1020) support in winbond-cir?
From: Juan Jesús García de Soria Lucena @ 2013-10-22 10:57 UTC (permalink / raw)
To: Tom Gundersen
Cc: David Härdeman, Sean Young, linux-input@vger.kernel.org,
linux-media@vger.kernel.org
In-Reply-To: <CAG-2HqVh1hzC0rzft9gD+4zmJpnpH9OKj64vyO4teCrX5wuT+g@mail.gmail.com>
I tried to find the hacked code, but it's not there anymore.
I have the docs, through, and a rough idea of what would have to be
done in order to support WEC1020 in winbond-cir.
I cannot tell when I will be able to put time into this, though :-(
Best regards,
Juan.
2013/10/17 Tom Gundersen <teg@jklm.no>:
>
>
> On Wednesday, October 16, 2013, Juan Jesús García de Soria Lucena
> <skandalfo@gmail.com> wrote:
>> Hi there,
>>
>> 2013/10/15 David Härdeman <david@hardeman.nu>
>>>
>>> IIRC, Juan had a hacked-up version of the winbond-cir driver working on
>>> his hardware back in March (the hardware seems similar enough, basically
>>> the WEC1022 adds some additional Wake-On-IR functionality...I seem to
>>> recall).
>>
>> I did indeed look at this at the time. I still have the hardware, should
>> have the hardware docs somewhere, and can look at where I left that hacked
>> code at the time...
>> I really would have liked to merge in the support for it into winbond-cir,
>> but real life took over.
>>>
>>> But I think Juan is the one to talk to. I don't have the WEC1020
>>> hardware and I don't have his experience of adding support for it...
>>
>> As I said, I still have the hardware around. I might get some time around
>> this weekend to look at it and come back with at least an idea of what is
>> left to be done.
>
> Thanks for taking a look.
>
> Cheers,
>
> Tom
--
:wq
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 1/2] input: touchscreen: fix spelling mistake in TSC/ADC DT binding
From: Felipe Balbi @ 2013-10-22 12:02 UTC (permalink / raw)
To: Sebastian Andrzej Siewior
Cc: Felipe Balbi, dmitry.torokhov, rob.herring, pawel.moll,
mark.rutland, swarren, ijc+devicetree, rob, bcousson,
Tony Lindgren, devicetree, Linux OMAP Mailing List, linux-input
In-Reply-To: <52663A58.9070706@linutronix.de>
[-- Attachment #1: Type: text/plain, Size: 1191 bytes --]
Hi,
On Tue, Oct 22, 2013 at 10:42:00AM +0200, Sebastian Andrzej Siewior wrote:
> On 10/21/2013 10:13 PM, Felipe Balbi wrote:
> > diff --git a/drivers/input/touchscreen/ti_am335x_tsc.c b/drivers/input/touchscreen/ti_am335x_tsc.c
> > index e1c5300..b61df9d 100644
> > --- a/drivers/input/touchscreen/ti_am335x_tsc.c
> > +++ b/drivers/input/touchscreen/ti_am335x_tsc.c
> > @@ -348,9 +348,16 @@ static int titsc_parse_dt(struct platform_device *pdev,
> > if (err < 0)
> > return err;
> >
> > - err = of_property_read_u32(node, "ti,coordiante-readouts",
> > + /*
> > + * try with new binding first. If it fails, still try with
> > + * bogus, miss-spelled version.
> > + */
> > + err = of_property_read_u32(node, "ti,coordinate-readouts",
> > &ts_dev->coordinate_readouts);
> > if (err < 0)
> > + err = of_property_read_u32(node, "ti,coordiante-readouts",
> > + &ts_dev->coordinate_readouts);
> > + if (err < 0)
> > return err;
>
> Thanks, very good. Do we keep this fallback for ever or just for a year
> or two?
That's for DT maintainers to decide but considering DT is an ABI, I
guess we need to keep for 30 years or so :-p
--
balbi
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply
* [PATCHv2 0/3] Add Nokia N900 DT support
From: Sebastian Reichel @ 2013-10-22 12:47 UTC (permalink / raw)
To: Sebastian Reichel, linux-input
Cc: 'Benoît Cousson', Tony Lindgren, Rob Herring,
Pawel Moll, Mark Rutland, Stephen Warren, Ian Campbell,
Rob Landley, Russell King, Dmitry Torokhov, Grant Likely,
devicetree, linux-doc, linux-kernel, linux-arm-kernel, linux-omap,
Sebastian Reichel
Add device tree support for the Nokia N900 keyboard.
Changes since v1:
* create "DTS: ARM: TWL4030: Add keypad node" patch, which
was part of "Input: twl4030-keypad - add device tree support"
before.
* use "IS_ENABLED(CONFIG_OF)" to check if DT is enabled
Currently non-DT boot crashes for me very early, so I was not able
to check this patch using non-DT boot.
-- Sebastian
Sebastian Reichel (3):
Input: twl4030-keypad - add device tree support
DTS: ARM: TWL4030: Add keypad node
ARM: dts: N900: TWL4030 Keypad Matrix definition
.../devicetree/bindings/input/twl4030-keypad.txt | 31 ++++++++
arch/arm/boot/dts/omap3-n900.dts | 55 +++++++++++++
arch/arm/boot/dts/twl4030.dtsi | 7 ++
drivers/input/keyboard/twl4030_keypad.c | 91 ++++++++++++++++++----
4 files changed, 167 insertions(+), 17 deletions(-)
create mode 100644 Documentation/devicetree/bindings/input/twl4030-keypad.txt
--
1.8.4.rc3
^ permalink raw reply
* [PATCHv2 1/3] Input: twl4030-keypad - add device tree support
From: Sebastian Reichel @ 2013-10-22 12:47 UTC (permalink / raw)
To: Sebastian Reichel, linux-input
Cc: 'Benoît Cousson', Tony Lindgren, Rob Herring,
Pawel Moll, Mark Rutland, Stephen Warren, Ian Campbell,
Rob Landley, Russell King, Dmitry Torokhov, Grant Likely,
devicetree, linux-doc, linux-kernel, linux-arm-kernel, linux-omap,
Sebastian Reichel
In-Reply-To: <1382446042-27099-1-git-send-email-sre@debian.org>
Add device tree support for twl4030 keypad driver and update the
Documentation with twl4030 keypad device tree binding information.
Tested on Nokia N900.
Signed-off-by: Sebastian Reichel <sre@debian.org>
---
.../devicetree/bindings/input/twl4030-keypad.txt | 31 ++++++++
drivers/input/keyboard/twl4030_keypad.c | 91 ++++++++++++++++++----
2 files changed, 105 insertions(+), 17 deletions(-)
create mode 100644 Documentation/devicetree/bindings/input/twl4030-keypad.txt
diff --git a/Documentation/devicetree/bindings/input/twl4030-keypad.txt b/Documentation/devicetree/bindings/input/twl4030-keypad.txt
new file mode 100644
index 0000000..2b4bd7a
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/twl4030-keypad.txt
@@ -0,0 +1,31 @@
+* TWL4030's Keypad Controller device tree bindings
+
+TWL4030'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.
+
+This binding is based on the matrix-keymap binding with the following
+changes:
+
+ * keypad,num-rows and keypad,num-columns are required.
+
+Required SoC Specific Properties:
+- compatible: should be one of the following
+ - "ti,twl4030-keypad": For controllers compatible with twl4030 keypad
+ controller.
+- interrupt: should be one of the following
+ - <1>: For controllers compatible with twl4030 keypad controller.
+
+Optional Properties specific to linux:
+- linux,keypad-no-autorepeat: do no enable autorepeat feature.
+
+Example:
+ twl_keypad: keypad {
+ compatible = "ti,twl4030-keypad";
+ interrupts = <1>;
+ keypad,num-rows = <8>;
+ keypad,num-columns = <8>;
+ linux,keypad-no-autorepeat;
+ };
diff --git a/drivers/input/keyboard/twl4030_keypad.c b/drivers/input/keyboard/twl4030_keypad.c
index d2d178c..034c312 100644
--- a/drivers/input/keyboard/twl4030_keypad.c
+++ b/drivers/input/keyboard/twl4030_keypad.c
@@ -33,6 +33,7 @@
#include <linux/platform_device.h>
#include <linux/i2c/twl.h>
#include <linux/slab.h>
+#include <linux/of.h>
/*
* The TWL4030 family chips include a keypad controller that supports
@@ -60,6 +61,7 @@
struct twl4030_keypad {
unsigned short keymap[TWL4030_KEYMAP_SIZE];
u16 kp_state[TWL4030_MAX_ROWS];
+ bool no_autorepeat;
unsigned n_rows;
unsigned n_cols;
unsigned irq;
@@ -324,6 +326,31 @@ static int twl4030_kp_program(struct twl4030_keypad *kp)
return 0;
}
+#if IS_ENABLED(CONFIG_OF)
+static int twl4030_keypad_parse_dt(struct device *dev,
+ struct twl4030_keypad *keypad_data)
+{
+ struct device_node *np = dev->of_node;
+ int err;
+
+ err = matrix_keypad_parse_of_params(dev, &keypad_data->n_rows,
+ &keypad_data->n_cols);
+ if (err)
+ return err;
+
+ if (of_get_property(np, "linux,input-no-autorepeat", NULL))
+ keypad_data->no_autorepeat = true;
+
+ return 0;
+}
+#else
+static inline int twl4030_keypad_parse_dt(struct device *dev,
+ struct twl4030_keypad *keypad_data)
+{
+ return -ENOSYS;
+}
+#endif
+
/*
* Registers keypad device with input subsystem
* and configures TWL4030 keypad registers
@@ -331,20 +358,12 @@ static int twl4030_kp_program(struct twl4030_keypad *kp)
static int twl4030_kp_probe(struct platform_device *pdev)
{
struct twl4030_keypad_data *pdata = pdev->dev.platform_data;
- const struct matrix_keymap_data *keymap_data;
+ const struct matrix_keymap_data *keymap_data = NULL;
struct twl4030_keypad *kp;
struct input_dev *input;
u8 reg;
int error;
- if (!pdata || !pdata->rows || !pdata->cols || !pdata->keymap_data ||
- pdata->rows > TWL4030_MAX_ROWS || pdata->cols > TWL4030_MAX_COLS) {
- dev_err(&pdev->dev, "Invalid platform_data\n");
- return -EINVAL;
- }
-
- keymap_data = pdata->keymap_data;
-
kp = kzalloc(sizeof(*kp), GFP_KERNEL);
input = input_allocate_device();
if (!kp || !input) {
@@ -352,13 +371,9 @@ static int twl4030_kp_probe(struct platform_device *pdev)
goto err1;
}
- /* Get the debug Device */
- kp->dbg_dev = &pdev->dev;
- kp->input = input;
-
- kp->n_rows = pdata->rows;
- kp->n_cols = pdata->cols;
- kp->irq = platform_get_irq(pdev, 0);
+ /* get the debug device */
+ kp->dbg_dev = &pdev->dev;
+ kp->input = input;
/* setup input device */
input->name = "TWL4030 Keypad";
@@ -370,6 +385,36 @@ static int twl4030_kp_probe(struct platform_device *pdev)
input->id.product = 0x0001;
input->id.version = 0x0003;
+ if (pdata) {
+ if (!pdata->rows || !pdata->cols || !pdata->keymap_data) {
+ dev_err(&pdev->dev, "Missing platform_data\n");
+ error = -EINVAL;
+ goto err1;
+ }
+
+ kp->n_rows = pdata->rows;
+ kp->n_cols = pdata->cols;
+ kp->no_autorepeat = !pdata->rep;
+ keymap_data = pdata->keymap_data;
+ } else {
+ error = twl4030_keypad_parse_dt(&pdev->dev, kp);
+ if (error)
+ goto err1;
+ }
+
+ if (kp->n_rows > TWL4030_MAX_ROWS || kp->n_cols > TWL4030_MAX_COLS) {
+ dev_err(&pdev->dev, "Invalid rows/cols amount specified in platform/devicetree data\n");
+ error = -EINVAL;
+ goto err1;
+ }
+
+ kp->irq = platform_get_irq(pdev, 0);
+ if (!kp->irq) {
+ dev_err(&pdev->dev, "no keyboard irq assigned\n");
+ error = -EINVAL;
+ goto err1;
+ }
+
error = matrix_keypad_build_keymap(keymap_data, NULL,
TWL4030_MAX_ROWS,
1 << TWL4030_ROW_SHIFT,
@@ -381,7 +426,7 @@ static int twl4030_kp_probe(struct platform_device *pdev)
input_set_capability(input, EV_MSC, MSC_SCAN);
/* Enable auto repeat feature of Linux input subsystem */
- if (pdata->rep)
+ if (!kp->no_autorepeat)
__set_bit(EV_REP, input->evbit);
error = input_register_device(input);
@@ -443,6 +488,17 @@ static int twl4030_kp_remove(struct platform_device *pdev)
return 0;
}
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id twl4030_keypad_dt_match_table[] = {
+ { .compatible = "ti,twl4030-keypad" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, twl4030_keypad_dt_match_table);
+#define twl4030_keypad_dt_match of_match_ptr(twl4030_keypad_dt_match_table)
+#else
+#define twl4030_keypad_dt_match NULL
+#endif
+
/*
* NOTE: twl4030 are multi-function devices connected via I2C.
* So this device is a child of an I2C parent, thus it needs to
@@ -455,6 +511,7 @@ static struct platform_driver twl4030_kp_driver = {
.driver = {
.name = "twl4030_keypad",
.owner = THIS_MODULE,
+ .of_match_table = twl4030_keypad_dt_match,
},
};
module_platform_driver(twl4030_kp_driver);
--
1.8.4.rc3
^ permalink raw reply related
* [PATCHv2 2/3] DTS: ARM: TWL4030: Add keypad node
From: Sebastian Reichel @ 2013-10-22 12:47 UTC (permalink / raw)
To: Sebastian Reichel, linux-input
Cc: 'Benoît Cousson', Tony Lindgren, Rob Herring,
Pawel Moll, Mark Rutland, Stephen Warren, Ian Campbell,
Rob Landley, Russell King, Dmitry Torokhov, Grant Likely,
devicetree, linux-doc, linux-kernel, linux-arm-kernel, linux-omap,
Sebastian Reichel
In-Reply-To: <1382446042-27099-1-git-send-email-sre@debian.org>
Add keypad node to twl4030, so that board DTS
files can just add the keymap.
Signed-off-by: Sebastian Reichel <sre@debian.org>
---
arch/arm/boot/dts/twl4030.dtsi | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/arch/arm/boot/dts/twl4030.dtsi b/arch/arm/boot/dts/twl4030.dtsi
index ae6a17a..773c94c 100644
--- a/arch/arm/boot/dts/twl4030.dtsi
+++ b/arch/arm/boot/dts/twl4030.dtsi
@@ -97,4 +97,11 @@
compatible = "ti,twl4030-pwmled";
#pwm-cells = <2>;
};
+
+ twl_keypad: keypad {
+ compatible = "ti,twl4030-keypad";
+ interrupts = <1>;
+ keypad,num-rows = <8>;
+ keypad,num-columns = <8>;
+ };
};
--
1.8.4.rc3
^ permalink raw reply related
* [PATCHv2 3/3] ARM: dts: N900: TWL4030 Keypad Matrix definition
From: Sebastian Reichel @ 2013-10-22 12:47 UTC (permalink / raw)
To: Sebastian Reichel, linux-input
Cc: 'Benoît Cousson', Tony Lindgren, Rob Herring,
Pawel Moll, Mark Rutland, Stephen Warren, Ian Campbell,
Rob Landley, Russell King, Dmitry Torokhov, Grant Likely,
devicetree, linux-doc, linux-kernel, linux-arm-kernel, linux-omap,
Sebastian Reichel
In-Reply-To: <1382446042-27099-1-git-send-email-sre@debian.org>
Add Keyboard Matrix information to N900's DTS file.
This patch maps the keys exactly as the original
board code.
Signed-off-by: Sebastian Reichel <sre@debian.org>
---
arch/arm/boot/dts/omap3-n900.dts | 55 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 55 insertions(+)
diff --git a/arch/arm/boot/dts/omap3-n900.dts b/arch/arm/boot/dts/omap3-n900.dts
index c2b92e8..30ac779 100644
--- a/arch/arm/boot/dts/omap3-n900.dts
+++ b/arch/arm/boot/dts/omap3-n900.dts
@@ -109,6 +109,61 @@
#include "twl4030.dtsi"
#include "twl4030_omap3.dtsi"
+&twl_keypad {
+ linux,keymap = < 0x00000010 /* KEY_Q */
+ 0x00010018 /* KEY_O */
+ 0x00020019 /* KEY_P */
+ 0x00030033 /* KEY_COMMA */
+ 0x0004000e /* KEY_BACKSPACE */
+ 0x0006001e /* KEY_A */
+ 0x0007001f /* KEY_S */
+
+ 0x01000011 /* KEY_W */
+ 0x01010020 /* KEY_D */
+ 0x01020021 /* KEY_F */
+ 0x01030022 /* KEY_G */
+ 0x01040023 /* KEY_H */
+ 0x01050024 /* KEY_J */
+ 0x01060025 /* KEY_K */
+ 0x01070026 /* KEY_L */
+
+ 0x02000012 /* KEY_E */
+ 0x02010034 /* KEY_DOT */
+ 0x02020067 /* KEY_UP */
+ 0x0203001c /* KEY_ENTER */
+ 0x0205002c /* KEY_Z */
+ 0x0206002d /* KEY_X */
+ 0x0207002e /* KEY_C */
+ 0x02080043 /* KEY_F9 */
+
+ 0x03000013 /* KEY_R */
+ 0x0301002f /* KEY_V */
+ 0x03020030 /* KEY_B */
+ 0x03030031 /* KEY_N */
+ 0x03040032 /* KEY_M */
+ 0x03050039 /* KEY_SPACE */
+ 0x03060039 /* KEY_SPACE */
+ 0x03070069 /* KEY_LEFT */
+
+ 0x04000014 /* KEY_T */
+ 0x0401006c /* KEY_DOWN */
+ 0x0402006a /* KEY_RIGHT */
+ 0x0404001d /* KEY_LEFTCTRL */
+ 0x04050064 /* KEY_RIGHTALT */
+ 0x0406002a /* KEY_LEFTSHIFT */
+ 0x04080044 /* KEY_F10 */
+
+ 0x05000015 /* KEY_Y */
+ 0x05080057 /* KEY_F11 */
+
+ 0x06000016 /* KEY_U */
+
+ 0x07000017 /* KEY_I */
+ 0x07010041 /* KEY_F7 */
+ 0x07020042 /* KEY_F8 */
+ >;
+};
+
&twl_gpio {
ti,pullups = <0x0>;
ti,pulldowns = <0x03ff3f>; /* BIT(0..5) | BIT(8..17) */
--
1.8.4.rc3
^ permalink raw reply related
* [PATCH 2/4] Input: twl4030-pwrbutton: use dev_err for errors
From: Sebastian Reichel @ 2013-10-22 12:55 UTC (permalink / raw)
To: Sebastian Reichel, linux-input
Cc: 'Benoît Cousson', Tony Lindgren, Rob Herring,
Pawel Moll, Mark Rutland, Stephen Warren, Ian Campbell,
Rob Landley, Russell King, Dmitry Torokhov, Grant Likely,
devicetree, linux-doc, linux-kernel, linux-arm-kernel, linux-omap,
Sebastian Reichel
In-Reply-To: <1382446504-27837-1-git-send-email-sre@debian.org>
Use dev_err() to output errors instead of dev_dbg().
Signed-off-by: Sebastian Reichel <sre@debian.org>
---
drivers/input/misc/twl4030-pwrbutton.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/input/misc/twl4030-pwrbutton.c b/drivers/input/misc/twl4030-pwrbutton.c
index e450947..4e7a810 100644
--- a/drivers/input/misc/twl4030-pwrbutton.c
+++ b/drivers/input/misc/twl4030-pwrbutton.c
@@ -60,7 +60,7 @@ static int twl4030_pwrbutton_probe(struct platform_device *pdev)
pwr = input_allocate_device();
if (!pwr) {
- dev_dbg(&pdev->dev, "Can't allocate power button\n");
+ dev_err(&pdev->dev, "Can't allocate power button\n");
return -ENOMEM;
}
@@ -74,13 +74,13 @@ static int twl4030_pwrbutton_probe(struct platform_device *pdev)
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
"twl4030_pwrbutton", pwr);
if (err < 0) {
- dev_dbg(&pdev->dev, "Can't get IRQ for pwrbutton: %d\n", err);
+ dev_err(&pdev->dev, "Can't get IRQ for pwrbutton: %d\n", err);
goto free_input_dev;
}
err = input_register_device(pwr);
if (err) {
- dev_dbg(&pdev->dev, "Can't register power button: %d\n", err);
+ dev_err(&pdev->dev, "Can't register power button: %d\n", err);
goto free_irq;
}
--
1.8.4.rc3
^ permalink raw reply related
* [PATCH 3/4] Input: twl4030-pwrbutton: simplify driver using devm_*
From: Sebastian Reichel @ 2013-10-22 12:55 UTC (permalink / raw)
To: Sebastian Reichel, linux-input
Cc: Mark Rutland, devicetree, Dmitry Torokhov, Russell King,
Rob Landley, Pawel Moll, Stephen Warren, Tony Lindgren,
Sebastian Reichel, Ian Campbell, linux-doc, linux-kernel,
Rob Herring, 'Benoît Cousson', Grant Likely,
linux-omap, linux-arm-kernel
In-Reply-To: <1382446504-27837-1-git-send-email-sre@debian.org>
Use managed irq resource to simplify the driver.
Signed-off-by: Sebastian Reichel <sre@debian.org>
---
drivers/input/misc/twl4030-pwrbutton.c | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/drivers/input/misc/twl4030-pwrbutton.c b/drivers/input/misc/twl4030-pwrbutton.c
index 4e7a810..4ab9243 100644
--- a/drivers/input/misc/twl4030-pwrbutton.c
+++ b/drivers/input/misc/twl4030-pwrbutton.c
@@ -70,7 +70,7 @@ static int twl4030_pwrbutton_probe(struct platform_device *pdev)
pwr->phys = "twl4030_pwrbutton/input0";
pwr->dev.parent = &pdev->dev;
- err = request_threaded_irq(irq, NULL, powerbutton_irq,
+ err = devm_request_threaded_irq(&pwr->dev, irq, NULL, powerbutton_irq,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
"twl4030_pwrbutton", pwr);
if (err < 0) {
@@ -81,15 +81,13 @@ static int twl4030_pwrbutton_probe(struct platform_device *pdev)
err = input_register_device(pwr);
if (err) {
dev_err(&pdev->dev, "Can't register power button: %d\n", err);
- goto free_irq;
+ goto free_input_dev;
}
platform_set_drvdata(pdev, pwr);
return 0;
-free_irq:
- free_irq(irq, pwr);
free_input_dev:
input_free_device(pwr);
return err;
@@ -98,9 +96,7 @@ free_input_dev:
static int __exit twl4030_pwrbutton_remove(struct platform_device *pdev)
{
struct input_dev *pwr = platform_get_drvdata(pdev);
- int irq = platform_get_irq(pdev, 0);
- free_irq(irq, pwr);
input_unregister_device(pwr);
return 0;
--
1.8.4.rc3
^ permalink raw reply related
* [PATCH 4/4] ARM: dts: twl4030: Add power button support
From: Sebastian Reichel @ 2013-10-22 12:55 UTC (permalink / raw)
To: Sebastian Reichel, linux-input
Cc: Mark Rutland, devicetree, Dmitry Torokhov, Russell King,
Rob Landley, Pawel Moll, Stephen Warren, Tony Lindgren,
Sebastian Reichel, Ian Campbell, linux-doc, linux-kernel,
Rob Herring, 'Benoît Cousson', Grant Likely,
linux-omap, linux-arm-kernel
In-Reply-To: <1382446504-27837-1-git-send-email-sre@debian.org>
Enable support for the power button.
Signed-off-by: Sebastian Reichel <sre@debian.org>
---
arch/arm/boot/dts/twl4030.dtsi | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/arch/arm/boot/dts/twl4030.dtsi b/arch/arm/boot/dts/twl4030.dtsi
index 773c94c..19e3cd0 100644
--- a/arch/arm/boot/dts/twl4030.dtsi
+++ b/arch/arm/boot/dts/twl4030.dtsi
@@ -104,4 +104,9 @@
keypad,num-rows = <8>;
keypad,num-columns = <8>;
};
+
+ twl_pwrbutton: pwrbutton {
+ compatible = "ti,twl4030-pwrbutton";
+ interrupts = <8>;
+ };
};
--
1.8.4.rc3
^ permalink raw reply related
* [PATCH 0/4] Add N900 powerbutton DT support
From: Sebastian Reichel @ 2013-10-22 12:55 UTC (permalink / raw)
To: Sebastian Reichel, linux-input
Cc: 'Benoît Cousson', Tony Lindgren, Rob Herring,
Pawel Moll, Mark Rutland, Stephen Warren, Ian Campbell,
Rob Landley, Russell King, Dmitry Torokhov, Grant Likely,
devicetree, linux-doc, linux-kernel, linux-arm-kernel, linux-omap,
Sebastian Reichel
This patchset adds support for the Nokia N900
powerbutton.
Currently non-DT boot crashes for me very early,
so I was not able to check this patch using non-DT
boot.
It would be nice if somebody can check if the first
patch works using non-DT boot. It should be enough
to test it with any omap3 board using the twl4030
power button.
Sebastian Reichel (4):
Input: twl4030-pwrbutton - add device tree support
Input: twl4030-pwrbutton: use dev_err for errors
Input: twl4030-pwrbutton: simplify driver using devm_*
ARM: dts: twl4030: Add power button support
arch/arm/boot/dts/twl4030.dtsi | 5 +++++
drivers/input/misc/twl4030-pwrbutton.c | 33 ++++++++++++++++++++-------------
2 files changed, 25 insertions(+), 13 deletions(-)
--
1.8.4.rc3
^ permalink raw reply
* [PATCH 1/4] Input: twl4030-pwrbutton - add device tree support
From: Sebastian Reichel @ 2013-10-22 12:55 UTC (permalink / raw)
To: Sebastian Reichel, linux-input
Cc: 'Benoît Cousson', Tony Lindgren, Rob Herring,
Pawel Moll, Mark Rutland, Stephen Warren, Ian Campbell,
Rob Landley, Russell King, Dmitry Torokhov, Grant Likely,
devicetree, linux-doc, linux-kernel, linux-arm-kernel, linux-omap,
Sebastian Reichel
In-Reply-To: <1382446504-27837-1-git-send-email-sre@debian.org>
Add device tree support for twl4030 power button driver.
Signed-off-by: Sebastian Reichel <sre@debian.org>
---
drivers/input/misc/twl4030-pwrbutton.c | 19 +++++++++++++++----
1 file changed, 15 insertions(+), 4 deletions(-)
diff --git a/drivers/input/misc/twl4030-pwrbutton.c b/drivers/input/misc/twl4030-pwrbutton.c
index b9a05fd..e450947 100644
--- a/drivers/input/misc/twl4030-pwrbutton.c
+++ b/drivers/input/misc/twl4030-pwrbutton.c
@@ -52,7 +52,7 @@ static irqreturn_t powerbutton_irq(int irq, void *_pwr)
return IRQ_HANDLED;
}
-static int __init twl4030_pwrbutton_probe(struct platform_device *pdev)
+static int twl4030_pwrbutton_probe(struct platform_device *pdev)
{
struct input_dev *pwr;
int irq = platform_get_irq(pdev, 0);
@@ -106,16 +106,27 @@ static int __exit twl4030_pwrbutton_remove(struct platform_device *pdev)
return 0;
}
+#if IS_ENABLED(CONFIG_OF)
+static const struct of_device_id twl4030_pwrbutton_dt_match_table[] = {
+ { .compatible = "ti,twl4030-pwrbutton" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, twl4030_pwrbutton_dt_match_table);
+#define twl4030_pwrbutton_dt_match of_match_ptr(twl4030_pwrbutton_dt_match_table)
+#else
+#define twl4030_pwrbutton_dt_match NULL
+#endif
+
static struct platform_driver twl4030_pwrbutton_driver = {
+ .probe = twl4030_pwrbutton_probe,
.remove = __exit_p(twl4030_pwrbutton_remove),
.driver = {
.name = "twl4030_pwrbutton",
.owner = THIS_MODULE,
+ .of_match_table = twl4030_pwrbutton_dt_match,
},
};
-
-module_platform_driver_probe(twl4030_pwrbutton_driver,
- twl4030_pwrbutton_probe);
+module_platform_driver(twl4030_pwrbutton_driver);
MODULE_ALIAS("platform:twl4030_pwrbutton");
MODULE_DESCRIPTION("Triton2 Power Button");
--
1.8.4.rc3
^ permalink raw reply related
* Re: [PATCH 1/4] Input: twl4030-pwrbutton - add device tree support
From: Felipe Balbi @ 2013-10-22 13:50 UTC (permalink / raw)
To: Sebastian Reichel
Cc: Sebastian Reichel, linux-input, 'Benoît Cousson',
Tony Lindgren, Rob Herring, Pawel Moll, Mark Rutland,
Stephen Warren, Ian Campbell, Rob Landley, Russell King,
Dmitry Torokhov, Grant Likely, devicetree, linux-doc,
linux-kernel, linux-arm-kernel, linux-omap
In-Reply-To: <1382446504-27837-2-git-send-email-sre@debian.org>
[-- Attachment #1: Type: text/plain, Size: 1485 bytes --]
Hi,
On Tue, Oct 22, 2013 at 02:55:01PM +0200, Sebastian Reichel wrote:
> Add device tree support for twl4030 power button driver.
>
> Signed-off-by: Sebastian Reichel <sre@debian.org>
> ---
> drivers/input/misc/twl4030-pwrbutton.c | 19 +++++++++++++++----
> 1 file changed, 15 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/input/misc/twl4030-pwrbutton.c b/drivers/input/misc/twl4030-pwrbutton.c
> index b9a05fd..e450947 100644
> --- a/drivers/input/misc/twl4030-pwrbutton.c
> +++ b/drivers/input/misc/twl4030-pwrbutton.c
> @@ -52,7 +52,7 @@ static irqreturn_t powerbutton_irq(int irq, void *_pwr)
> return IRQ_HANDLED;
> }
>
> -static int __init twl4030_pwrbutton_probe(struct platform_device *pdev)
> +static int twl4030_pwrbutton_probe(struct platform_device *pdev)
> {
> struct input_dev *pwr;
> int irq = platform_get_irq(pdev, 0);
> @@ -106,16 +106,27 @@ static int __exit twl4030_pwrbutton_remove(struct platform_device *pdev)
> return 0;
> }
>
> +#if IS_ENABLED(CONFIG_OF)
> +static const struct of_device_id twl4030_pwrbutton_dt_match_table[] = {
> + { .compatible = "ti,twl4030-pwrbutton" },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, twl4030_pwrbutton_dt_match_table);
> +#define twl4030_pwrbutton_dt_match of_match_ptr(twl4030_pwrbutton_dt_match_table)
> +#else
> +#define twl4030_pwrbutton_dt_match NULL
> +#endif
you don't ned this trickery.. just always device the match table.
--
balbi
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply
* Re: [PATCH v2] add sur40 driver for Samsung SUR40 (aka MS Surface 2.0/Pixelsense)
From: David Herrmann @ 2013-10-22 15:22 UTC (permalink / raw)
To: Florian Echtler
Cc: Dmitry Torokhov, open list:HID CORE LAYER, Benjamin Tissoires,
Henrik Rydberg
In-Reply-To: <5266164C.3060509@butterbrot.org>
Hi
On Tue, Oct 22, 2013 at 8:08 AM, Florian Echtler <floe@butterbrot.org> wrote:
> Hello Dmitry,
>
> thanks for your quick feedback, a few questions below:
>
> On 21.10.2013 18:20, Dmitry Torokhov wrote:
>> On Sun, Oct 20, 2013 at 06:49:11PM +0200, Florian Echtler wrote:
>>> +/* read 512 bytes from endpoint 0x86 -> get header + blobs */
>>> +struct sur40_header {
>>> +
>>> + uint16_t type; /* always 0x0001 */
>>> + uint16_t count; /* count of blobs (if 0: continue prev. packet) */
>>> +
>>> + uint32_t packet_id;
>>> +
>>> + uint32_t timestamp; /* milliseconds (inc. by 16 or 17 each frame) */
>>> + uint32_t unknown; /* "epoch?" always 02/03 00 00 00 */
>>
>> Proper internal kernel types are u8, u16, u32. For user-facing APIs
>> __u8, __u16, and __u32 should be used. Also, since this is data coming
>> directly off the wire, you should be using __le16, __le32, etc, and then
>> do __leXX_to_cpu() conversion before using it in calculations.
> OK, I'll switch to u32 throughout (also for the float, I'll explain in a
> commment). However, I haven't found a single other touchscreen driver
> which uses __le32, even though they all probably process raw wire data -
> can you suggest an example?
These are probably all broken or the hardware guarantees
cpu-byte-order. Anyway, what you should do is use __le16/32 for your
types which represent data from the device. Then call le16_to_cpu() on
these values to convert it to host byte-order. Something like this:
struct sur40_raw_header {
__le16 type;
__le32 unused;
__le8 count;
} __packed;
struct sur40_header {
u16 type;
u8 count;
};
static void parse_data(const struct sur40_raw_header *h)
{
struct sur40_header d;
d.type = __le16_to_cpu(h->type);
d.count = h->count;
do_something(&d);
}
>>> +/* debug helper macro */
>>> +#define get_dev(x) (&(x->usbdev->dev))
>> Just stick that dev in sur40_state and then use sur40->dev throughout.
> OK.
>
>>> + struct sur40_header *header = &(sur40->bulk_in_buffer->header);
>> No need to have parenthesis around & operator.
>>> + struct sur40_blob *inblob = &(sur40->bulk_in_buffer->blobs[0]);
>> Same here.
> Intention seems clearer to me with parentheses, but if this doesn't
> conform to coding style, I'll fix it.
We never use parentheses for that. You will get used to it ;)
>>> + if (!sur40->bulk_in_buffer) {
>>> + dev_err(&interface->dev, "Unable to allocate input buffer.");
>>> + sur40_delete(sur40);
>> Would prefer standard kernel error unwinding style (gotos to proper
>> unwinding point).
> Something like this example from ucb1400_ts?
>
> error = input_register_device(ucb->ts_idev);
> if (error)
> goto err_free_irq;
>
> return 0;
>
> err_free_irq:
> free_irq(ucb->irq, ucb);
> err_free_devs:
> input_free_device(ucb->ts_idev);
> err:
> return error;
Yepp, exactly.
David
^ permalink raw reply
* Re: [PATCH v2] add sur40 driver for Samsung SUR40 (aka MS Surface 2.0/Pixelsense)
From: Dmitry Torokhov @ 2013-10-22 15:46 UTC (permalink / raw)
To: Florian Echtler; +Cc: linux-input, benjamin.tissoires, rydberg, dh.herrmann
In-Reply-To: <5266164C.3060509@butterbrot.org>
On Tue, Oct 22, 2013 at 08:08:12AM +0200, Florian Echtler wrote:
> Hello Dmitry,
>
> thanks for your quick feedback, a few questions below:
>
> On 21.10.2013 18:20, Dmitry Torokhov wrote:
> > On Sun, Oct 20, 2013 at 06:49:11PM +0200, Florian Echtler wrote:
> >> +/* read 512 bytes from endpoint 0x86 -> get header + blobs */
> >> +struct sur40_header {
> >> +
> >> + uint16_t type; /* always 0x0001 */
> >> + uint16_t count; /* count of blobs (if 0: continue prev. packet) */
> >> +
> >> + uint32_t packet_id;
> >> +
> >> + uint32_t timestamp; /* milliseconds (inc. by 16 or 17 each frame) */
> >> + uint32_t unknown; /* "epoch?" always 02/03 00 00 00 */
> >
> > Proper internal kernel types are u8, u16, u32. For user-facing APIs
> > __u8, __u16, and __u32 should be used. Also, since this is data coming
> > directly off the wire, you should be using __le16, __le32, etc, and then
> > do __leXX_to_cpu() conversion before using it in calculations.
> OK, I'll switch to u32 throughout (also for the float, I'll explain in a
> commment). However, I haven't found a single other touchscreen driver
> which uses __le32, even though they all probably process raw wire data -
> can you suggest an example?
Not necessarily le32, but:
[dtor@dtor-d630 work]$ grep -ri to_cpu drivers/input/touchscreen/
drivers/input/touchscreen/atmel_mxt_ts.c:
le16_to_cpus(&object->start_address);
drivers/input/touchscreen/atmel_mxt_ts.c: le16_to_cpus(range.x);
drivers/input/touchscreen/atmel_mxt_ts.c: le16_to_cpus(range.y);
drivers/input/touchscreen/cyttsp_core.c: input_report_abs(input, ABS_MT_POSITION_X, be16_to_cpu(tch->x));
drivers/input/touchscreen/cyttsp_core.c: input_report_abs(input, ABS_MT_POSITION_Y, be16_to_cpu(tch->y));
drivers/input/touchscreen/ads7846.c: status = be16_to_cpu(req->sample);
drivers/input/touchscreen/ads7846.c: status = be16_to_cpu(*((u16 *)&req->sample[1]));
drivers/input/touchscreen/ads7846.c: return be16_to_cpup((__be16 *)&(((char*)t->rx_buf)[1])) >> 3;
drivers/input/touchscreen/ads7846.c: return be16_to_cpup((__be16 *)t->rx_buf) >> 3;
drivers/input/touchscreen/usbtouchscreen.c: unsigned int data_len = be16_to_cpu(packet->data_len);
drivers/input/touchscreen/usbtouchscreen.c: unsigned int x_len = be16_to_cpu(packet->x_len);
drivers/input/touchscreen/usbtouchscreen.c: unsigned int y_len = be16_to_cpu(packet->y_len);
drivers/input/touchscreen/usbtouchscreen.c: le16_to_cpu(udev->descriptor.idVendor),
drivers/input/touchscreen/usbtouchscreen.c: le16_to_cpu(udev->descriptor.idProduct));
drivers/input/touchscreen/wacom_i2c.c: x = le16_to_cpup((__le16 *)&data[4]);
drivers/input/touchscreen/wacom_i2c.c: y = le16_to_cpup((__le16 *)&data[6]);
drivers/input/touchscreen/wacom_i2c.c: pressure = le16_to_cpup((__le16 *)&data[8]);
>
> >> +/* debug helper macro */
> >> +#define get_dev(x) (&(x->usbdev->dev))
> > Just stick that dev in sur40_state and then use sur40->dev throughout.
> OK.
>
> >> + struct sur40_header *header = &(sur40->bulk_in_buffer->header);
> > No need to have parenthesis around & operator.
> >> + struct sur40_blob *inblob = &(sur40->bulk_in_buffer->blobs[0]);
> > Same here.
> Intention seems clearer to me with parentheses, but if this doesn't
> conform to coding style, I'll fix it.
>
> >> + if (!sur40->bulk_in_buffer) {
> >> + dev_err(&interface->dev, "Unable to allocate input buffer.");
> >> + sur40_delete(sur40);
> > Would prefer standard kernel error unwinding style (gotos to proper
> > unwinding point).
> Something like this example from ucb1400_ts?
>
> error = input_register_device(ucb->ts_idev);
> if (error)
> goto err_free_irq;
>
> return 0;
>
> err_free_irq:
> free_irq(ucb->irq, ucb);
> err_free_devs:
> input_free_device(ucb->ts_idev);
> err:
> return error;
>
Yes, please.
--
Dmitry
^ permalink raw reply
* Re: [PATCH v2] add sur40 driver for Samsung SUR40 (aka MS Surface 2.0/Pixelsense)
From: Dmitry Torokhov @ 2013-10-22 15:47 UTC (permalink / raw)
To: David Herrmann
Cc: Florian Echtler, open list:HID CORE LAYER, Benjamin Tissoires,
Henrik Rydberg
In-Reply-To: <CANq1E4R88OooYjFz0d46uJZgvD=a_qzVVxV74wQzWfaUB7WBEg@mail.gmail.com>
On Tue, Oct 22, 2013 at 05:22:08PM +0200, David Herrmann wrote:
> Hi
>
> On Tue, Oct 22, 2013 at 8:08 AM, Florian Echtler <floe@butterbrot.org> wrote:
> > Hello Dmitry,
> >
> > thanks for your quick feedback, a few questions below:
> >
> > On 21.10.2013 18:20, Dmitry Torokhov wrote:
> >> On Sun, Oct 20, 2013 at 06:49:11PM +0200, Florian Echtler wrote:
> >>> +/* read 512 bytes from endpoint 0x86 -> get header + blobs */
> >>> +struct sur40_header {
> >>> +
> >>> + uint16_t type; /* always 0x0001 */
> >>> + uint16_t count; /* count of blobs (if 0: continue prev. packet) */
> >>> +
> >>> + uint32_t packet_id;
> >>> +
> >>> + uint32_t timestamp; /* milliseconds (inc. by 16 or 17 each frame) */
> >>> + uint32_t unknown; /* "epoch?" always 02/03 00 00 00 */
> >>
> >> Proper internal kernel types are u8, u16, u32. For user-facing APIs
> >> __u8, __u16, and __u32 should be used. Also, since this is data coming
> >> directly off the wire, you should be using __le16, __le32, etc, and then
> >> do __leXX_to_cpu() conversion before using it in calculations.
> > OK, I'll switch to u32 throughout (also for the float, I'll explain in a
> > commment). However, I haven't found a single other touchscreen driver
> > which uses __le32, even though they all probably process raw wire data -
> > can you suggest an example?
>
> These are probably all broken or the hardware guarantees
> cpu-byte-order. Anyway, what you should do is use __le16/32 for your
> types which represent data from the device. Then call le16_to_cpu() on
> these values to convert it to host byte-order. Something like this:
>
> struct sur40_raw_header {
> __le16 type;
> __le32 unused;
> __le8 count;
Not the last one please ;)
Thanks.
--
Dmitry
^ permalink raw reply
* Re: [PATCH 01/02] input: serio: use DEVICE_ATTR_RO()
From: Dmitry Torokhov @ 2013-10-22 15:51 UTC (permalink / raw)
To: Greg Kroah-Hartman; +Cc: linux-input
In-Reply-To: <20131022061219.GA15412@kroah.com>
On Tue, Oct 22, 2013 at 07:12:19AM +0100, Greg Kroah-Hartman wrote:
> On Mon, Oct 21, 2013 at 09:25:32AM -0700, Dmitry Torokhov wrote:
> > Hi Greg,
> >
> > On Mon, Oct 07, 2013 at 06:08:23PM -0700, Greg Kroah-Hartman wrote:
> > > From: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> > >
> > > Convert the serio sysfs fiels to use the DEVICE_ATTR_RO() macros to make
> > > it easier to audit the correct sysfs file permission usage.
> >
> > Sorry for the delay. Frankly I am not a fan of DEVICE_ATTR macros as it
> > forces particular naming of the attribute structures and methods and
> > forces us to lose the module prefix in names. I prefer all functions and
> > file-scope variables have module prefix because in the absence of
> > language-supported namespaces this is the one thing that insulates
> > drivers from changes in shared headers.
>
> No sysfs read/write functions should ever be exported out of the local
> file "scope", so why is this an issue? I have run into it being a
> problem for some files, where people have device/bus/class sysfs files
> all in the same .c file, but that usage is very limited.
It is not about exporting read/write functions, it is about newly
defined data from common headers stomping on the local module
definitions.
>
> I'm trying to eventually convert the whole kernel to use the
> DEVICE_ATTR_??() macros to make it so that we can audit the sysfs file
> permissions easier, by making them impossible to get wrong. We have had
> problems in the past where device files had incorrect permissions and
> unprivilidged users could do things they shouldn't have. Using these
> macros _should_ prevent almost all of that from even being possible.
Sounds more like task for sparse to ensure we have sane permission bits.
>
> Now odds are, I'll never be able to remove DEVICE_ATTR() from the tree,
> as there are some strange files that use crazy permissions and we have
> to live with that, but for all of the "simple" files, like are done
> here, I'd really like to use them, especially as it provides a
> kernel-wide uniformity to the naming of attribute function calls, which
> is a huge benifit as well.
>
> Note, I did apply this patch to my tree already, and it's queued up for
> 3.13-rc1. If you really object to it, I'll revert it, and fix up the
> 02/02 patch in this series to not need it, but I'd really like you to
> reconsider your objection.
Nah, that is fine.
Thanks.
--
Dmitry
^ permalink raw reply
* Re: [PATCHv4][ 1/4] Input: tsc2007: Add device tree support.
From: Dmitry Torokhov @ 2013-10-22 22:35 UTC (permalink / raw)
To: Lothar Waßmann
Cc: Denis Carikli, linux-input, Sascha Hauer, linux-arm-kernel,
Eric Bénard, Rob Herring, Pawel Moll, Mark Rutland,
Stephen Warren, Ian Campbell, devicetree
In-Reply-To: <20131022114947.30dc9c07@ipc1.ka-ro>
On Tue, Oct 22, 2013 at 11:49:47AM +0200, Lothar Waßmann wrote:
> Hi,
>
> > On Mon, Oct 21, 2013 at 03:54:24PM +0200, Denis Carikli wrote:
> > >
> > > + if (ts->of)
> > > + return tsc2007_get_pendown_state_dt(ts);
> > > +
> > > if (!ts->get_pendown_state)
> > > return true;
> >
> > Instead of special casing "if (ts->of)" all over the place why don't you
> > set up the device structure as:
> >
> > if (<configuring_tsc2007_form_dt>)
> > ts->get_pendown_state = tsc2007_get_pendown_state_dt;
> >
> > and be done with it?
> >
> I also thought about that, but the existing function does not have any
> parameters, while the DT version of get_pendown_state() requires to get
> the GPIO passed to it somehow.
You can always have tsc2007_get_pendown_state_platform() wrapping the
call. Or we just go and fix board code.
Thanks.
--
Dmitry
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH] SOUND/SPI: Fix "clk_round_rate" undefined reference error.
From: Dmitry Torokhov @ 2013-10-22 22:53 UTC (permalink / raw)
To: Majunath Goudar; +Cc: linux-arm-kernel, nataraja.km, linux-input, linux-kernel
In-Reply-To: <1381228833-24082-1-git-send-email-csmanjuvijay@gmail.com>
Hi Majunath,
On Tue, Oct 08, 2013 at 04:10:33PM +0530, Majunath Goudar wrote:
> This patch adds a COMMON_CLK dependency to configure the
> SND_AT73C213 for Atmel AT73C213 DAC driver. Without this patch,
> build system can lead to build failure. This was observed during
> randconfig testing, in which SND_AT73C213 was enabled w/o COMMON_CLK
> being enabled. leading to the following error:
>
> sound/built-in.o: In function `snd_at73c213_probe':
> :(.text+0x328c4): undefined reference to `clk_round_rate'
> make: *** [vmlinux] Error 1
>
> Signed-off-by: Manjunath Goudar <csmanjuvijay@gmail.com>
> Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> Cc: linux-input@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org
> ---
> sound/spi/Kconfig | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/sound/spi/Kconfig b/sound/spi/Kconfig
> index e6485be..798d1ee 100644
> --- a/sound/spi/Kconfig
> +++ b/sound/spi/Kconfig
> @@ -11,7 +11,7 @@ if SND_SPI
>
> config SND_AT73C213
> tristate "Atmel AT73C213 DAC driver"
> - depends on ATMEL_SSC
> + depends on ATMEL_SSC && COMMON_CLK
I do not see clk_round_rate() being protected by CONFIG_COMMON_CLK so it
looks to me that ATMEL_SSC is at fault here.
Thanks.
--
Dmitry
^ permalink raw reply
* Re: [PATCH v3 1/1] Input: Improve the performance of alps v5-protocol's touchpad
From: Kevin Cernekee @ 2013-10-23 0:09 UTC (permalink / raw)
To: Yunkang Tang
Cc: Dmitry Torokhov, david turvene, linux-input, Niels de Vos,
yunkang.tang
In-Reply-To: <1382250180-3579-1-git-send-email-yunkang.tang@cn.alps.com>
On Sat, Oct 19, 2013 at 11:23 PM, Yunkang Tang <tommywill2011@gmail.com> wrote:
> /*
> + * Process bitmap data for V5 protocols. Return value is null.
> + *
> + * The bitmaps don't have enough data to track fingers, so this function
> + * only generates points representing a bounding box of at most two contacts.
> + * These two points are returned in x1, y1, x2, and y2.
> + */
> +static void alps_process_bitmap_dolphin(struct alps_data *priv,
> + struct alps_fields *fields,
> + int *x1, int *y1, int *x2, int *y2)
> +{
> + int box_middle_x, box_middle_y;
> + unsigned int x_map, y_map;
> + unsigned char start_bit, end_bit;
> +
> + x_map = fields->x_map;
> + y_map = fields->y_map;
> +
> + if (!x_map || !y_map)
> + return;
> +
> + /* Most-significant bit should never exceed max sensor line number */
> + if (fls(x_map) > priv->x_bits || fls(y_map) > priv->y_bits)
> + return;
fls() does require a little computation, so it might be preferable to
calculate it once and store the result.
> +
> + *x1 = *y1 = *x2 = *y2 = 0;
> +
> + if (fields->fingers > 1) {
> + start_bit = priv->x_bits - fls(x_map);
> + end_bit = priv->x_bits - ffs(x_map);
> + box_middle_x = (priv->x_max * (start_bit + end_bit)) /
> + (2 * (priv->x_bits - 1));
> +
> + start_bit = ffs(y_map) - 1;
> + end_bit = fls(y_map) - 1;
> + box_middle_y = (priv->y_max * (start_bit + end_bit)) /
> + (2 * (priv->y_bits - 1));
The revised logic looks good to me.
> + *x1 = fields->x;
> + *y1 = fields->y;
> + *x2 = 2 * box_middle_x - *x1;
> + *y2 = 2 * box_middle_y - *y1;
> + }
> +}
> +
> +/*
> * Process bitmap data from v3 and v4 protocols. Returns the number of
> * fingers detected. A return value of 0 means at least one of the
> * bitmaps was empty.
> @@ -461,7 +505,8 @@ static void alps_decode_buttons_v3(struct alps_fields *f, unsigned char *p)
> f->ts_middle = !!(p[3] & 0x40);
> }
>
> -static void alps_decode_pinnacle(struct alps_fields *f, unsigned char *p)
> +static void alps_decode_pinnacle(struct alps_fields *f, unsigned char *p,
> + struct psmouse *psmouse)
> {
> f->first_mp = !!(p[4] & 0x40);
> f->is_mp = !!(p[0] & 0x40);
> @@ -482,48 +527,61 @@ static void alps_decode_pinnacle(struct alps_fields *f, unsigned char *p)
> alps_decode_buttons_v3(f, p);
> }
>
> -static void alps_decode_rushmore(struct alps_fields *f, unsigned char *p)
> +static void alps_decode_rushmore(struct alps_fields *f, unsigned char *p,
> + struct psmouse *psmouse)
> {
> - alps_decode_pinnacle(f, p);
> + alps_decode_pinnacle(f, p, psmouse);
>
> f->x_map |= (p[5] & 0x10) << 11;
> f->y_map |= (p[5] & 0x20) << 6;
> }
>
> -static void alps_decode_dolphin(struct alps_fields *f, unsigned char *p)
> +static void alps_decode_dolphin(struct alps_fields *f, unsigned char *p,
> + struct psmouse *psmouse)
> {
> + u64 palm_data = 0;
> + struct alps_data *priv = psmouse->private;
> +
> f->first_mp = !!(p[0] & 0x02);
> f->is_mp = !!(p[0] & 0x20);
>
> - f->fingers = ((p[0] & 0x6) >> 1 |
> + if (!f->is_mp) {
> + f->x = ((p[1] & 0x7f) | ((p[4] & 0x0f) << 7));
> + f->y = ((p[2] & 0x7f) | ((p[4] & 0xf0) << 3));
> + f->z = (p[0] & 4) ? 0 : p[5] & 0x7f;
> + alps_decode_buttons_v3(f, p);
> + } else {
> + f->fingers = ((p[0] & 0x6) >> 1 |
> (p[0] & 0x10) >> 2);
> - f->x_map = ((p[2] & 0x60) >> 5) |
> - ((p[4] & 0x7f) << 2) |
> - ((p[5] & 0x7f) << 9) |
> - ((p[3] & 0x07) << 16) |
> - ((p[3] & 0x70) << 15) |
> - ((p[0] & 0x01) << 22);
> - f->y_map = (p[1] & 0x7f) |
> - ((p[2] & 0x1f) << 7);
> -
> - f->x = ((p[1] & 0x7f) | ((p[4] & 0x0f) << 7));
> - f->y = ((p[2] & 0x7f) | ((p[4] & 0xf0) << 3));
> - f->z = (p[0] & 4) ? 0 : p[5] & 0x7f;
>
> - alps_decode_buttons_v3(f, p);
> + palm_data = (p[1] & 0x7f) |
> + ((p[2] & 0x7f) << 7) |
> + ((p[4] & 0x7f) << 14) |
> + ((p[5] & 0x7f) << 21) |
> + ((p[3] & 0x07) << 28) |
> + (((u64)p[3] & 0x70) << 27) |
> + (((u64)p[0] & 0x01) << 34);
> +
> + /* Y-profile is stored in P(0) to p(n-1), n = y_bits; */
> + f->y_map = palm_data & (BIT(priv->y_bits) - 1);
> +
> + /* X-profile is stored in p(n) to p(n+m-1), m = x_bits; */
> + f->x_map = (palm_data >> priv->y_bits) &
> + (BIT(priv->x_bits) - 1);
> + }
This also looks OK. For a touchpad with x_bits = 23, the computed MT
X/Y maps look equivalent to the old code.
[snip]
> +static int alps_dolphin_get_device_area(struct psmouse *psmouse,
> + struct alps_data *priv)
> +{
> + struct ps2dev *ps2dev = &psmouse->ps2dev;
> + unsigned char param[4] = {0};
> + int num_x_electrode, num_y_electrode;
> +
> + if (alps_enter_command_mode(psmouse))
> + return -1;
> +
> + if (ps2_command(ps2dev, NULL, 0x00EC) ||
> + ps2_command(ps2dev, NULL, 0x00F0) ||
> + ps2_command(ps2dev, NULL, 0x00F0) ||
> + ps2_command(ps2dev, NULL, 0x00F3) ||
> + ps2_command(ps2dev, NULL, 0x000A) ||
> + ps2_command(ps2dev, NULL, 0x00F3) ||
> + ps2_command(ps2dev, NULL, 0x000A))
> + return -1;
The other ps2_command() calls in alps.c tend to use the PSMOUSE_*
constants instead of raw hex values.
(Sorry I didn't catch this the first time around.)
Everything else looks fine. So:
Reviewed-by: Kevin Cernekee <cernekee@gmail.com>
^ permalink raw reply
* [PATCH] Input: add i2c driver for elan i2c touchpad
From: Duson Lin @ 2013-10-23 3:15 UTC (permalink / raw)
To: linux-kernel, linux-input, dmitry.torokhov; +Cc: agnescheng, phoenix, Duson Lin
From: Duson Lin <dusonlin@emc.com.tw>
This driver adds support for elan i2c touchpad found on some laptops.
---
drivers/input/mouse/Kconfig | 10 +
drivers/input/mouse/Makefile | 1 +
drivers/input/mouse/elan_i2c.c | 1439 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 1450 insertions(+)
create mode 100644 drivers/input/mouse/elan_i2c.c
diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
index effa9c5..8ad4b38 100644
--- a/drivers/input/mouse/Kconfig
+++ b/drivers/input/mouse/Kconfig
@@ -215,6 +215,16 @@ config MOUSE_CYAPA
To compile this driver as a module, choose M here: the module will be
called cyapa.
+config MOUSE_ELAN_I2C
+ tristate "ELAN I2C Touchpad support"
+ depends on I2C
+ help
+ This driver adds support for Elan I2C Trackpads.
+ Say y here if you have a ELAN I2C Touchpad.
+
+ To compile this driver as a module, choose M here: the module will be
+ called elan_i2c.
+
config MOUSE_INPORT
tristate "InPort/MS/ATIXL busmouse"
depends on ISA
diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile
index c25efdb..24a12a6 100644
--- a/drivers/input/mouse/Makefile
+++ b/drivers/input/mouse/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_MOUSE_APPLETOUCH) += appletouch.o
obj-$(CONFIG_MOUSE_ATARI) += atarimouse.o
obj-$(CONFIG_MOUSE_BCM5974) += bcm5974.o
obj-$(CONFIG_MOUSE_CYAPA) += cyapa.o
+obj-$(CONFIG_MOUSE_ELAN_I2C) += elan_i2c.o
obj-$(CONFIG_MOUSE_GPIO) += gpio_mouse.o
obj-$(CONFIG_MOUSE_INPORT) += inport.o
obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o
diff --git a/drivers/input/mouse/elan_i2c.c b/drivers/input/mouse/elan_i2c.c
new file mode 100644
index 0000000..bc6e957
--- /dev/null
+++ b/drivers/input/mouse/elan_i2c.c
@@ -0,0 +1,1439 @@
+/*
+ * Elan I2C Touchpad driver
+ *
+ * Copyright (c) 2013 ELAN Microelectronics Corp.
+ *
+ * Author: 林政維 (Duson Lin) <dusonlin@emc.com.tw>
+ * Version: 1.4.5
+ *
+ * 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.
+ *
+ * Trademarks are the property of their respective owners.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/input/mt.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <linux/cdev.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/input.h>
+#include <linux/uaccess.h>
+#include <linux/jiffies.h>
+
+#define DRIVER_NAME "elan_i2c"
+#define ELAN_DRIVER_VERSION "1.4.5"
+#define ETP_PRESSURE_OFFSET 25
+#define ETP_MAX_PRESSURE 255
+#define ETP_FWIDTH_REDUCE 90
+#define ETP_FINGER_WIDTH 15
+
+#define ELAN_ADAPTER_FUNC_NONE 0
+#define ELAN_ADAPTER_FUNC_I2C 1
+
+/* Length of Elan touchpad information */
+#define ETP_INF_LENGTH 2
+#define ETP_MAX_FINGERS 5
+#define ETP_FINGER_DATA_LEN 5
+#define ETP_REPORT_ID 0x5D
+#define ETP_MAX_REPORT_LEN 34
+#define ETP_ENABLE_ABS 0x0001
+#define ETP_ENABLE_CALIBRATE 0x0002
+#define ETP_DISABLE_CALIBRATE 0x0000
+
+/* Elan i2c command */
+#define ETP_I2C_RESET 0x0100
+#define ETP_I2C_WAKE_UP 0x0800
+#define ETP_I2C_SLEEP 0x0801
+#define ETP_I2C_DESC_CMD 0x0001
+#define ETP_I2C_REPORT_DESC_CMD 0x0002
+#define ETP_I2C_STAND_CMD 0x0005
+#define ETP_I2C_UNIQUEID_CMD 0x0101
+#define ETP_I2C_FW_VERSION_CMD 0x0102
+#define ETP_I2C_SM_VERSION_CMD 0x0103
+#define ETP_I2C_XY_TRACENUM_CMD 0x0105
+#define ETP_I2C_MAX_X_AXIS_CMD 0x0106
+#define ETP_I2C_MAX_Y_AXIS_CMD 0x0107
+#define ETP_I2C_RESOLUTION_CMD 0x0108
+#define ETP_I2C_IAP_VERSION_CMD 0x0110
+#define ETP_I2C_SET_CMD 0x0300
+#define ETP_I2C_MAX_BASELINE_CMD 0x0306
+#define ETP_I2C_MIN_BASELINE_CMD 0x0307
+#define ETP_I2C_FW_CHECKSUM_CMD 0x030F
+#define ETP_I2C_IAP_CTRL_CMD 0x0310
+#define ETP_I2C_IAP_CMD 0x0311
+#define ETP_I2C_IAP_RESET_CMD 0x0314
+#define ETP_I2C_IAP_CHECKSUM_CMD 0x0315
+#define ETP_I2C_CALIBRATE_CMD 0x0316
+#define ETP_I2C_REPORT_LEN 34
+#define ETP_I2C_FINGER_DATA_OFFSET 4
+#define ETP_I2C_REPORT_ID_OFFSET 2
+#define ETP_I2C_DESC_LENGTH 30
+#define ETP_I2C_REPORT_DESC_LENGTH 158
+
+/* IAP F/W updater */
+#define ETP_FW_NAME "elan_i2c.bin"
+#define ETP_FW_IAP_REG_L 0x01
+#define ETP_FW_IAP_REG_H 0x06
+#define ETP_IAP_VERSION_ADDR 0x0082
+#define ETP_IAP_START_ADDR 0x0083
+#define ETP_IAP_RESET 0xF0F0
+#define ETP_ENABLE_FWUPDATE 0x1EA5
+#define ETP_FW_IAP_MODE_ON (1<<9)
+#define ETP_FW_IAP_PAGE_ERR (1<<5)
+#define ETP_FW_IAP_INTERFACE_ERR (1<<4)
+#define ETP_FW_PAGE_SIZE 64
+#define ETP_FW_PAGE_COUNT 768
+#define ETP_FW_SIZE (ETP_FW_PAGE_SIZE * ETP_FW_PAGE_COUNT)
+enum {IAP_MODE = 0, MAIN_MODE};
+
+struct dbfs_data {
+ bool bfetch;
+ u8 buffer[ETP_MAX_REPORT_LEN];
+};
+
+/* The main device structure */
+struct elan_tp_data {
+ struct i2c_client *client;
+ struct input_dev *input;
+ unsigned int max_x;
+ unsigned int max_y;
+ unsigned int width_x;
+ unsigned int width_y;
+ unsigned int irq;
+
+ /* fields required for IAP firmware updater */
+ u16 unique_id;
+ u16 fw_version;
+ u16 sm_version;
+ u16 iap_version;
+ bool updated_fw;
+ u16 iap_start_addr;
+ u8 adapter_func;
+
+ /* irq wake is enabled */
+ bool irq_wake;
+ bool enable_detail_info;
+
+ /* fields required for debug fs */
+ struct mutex dbfs_mutex;
+ struct dentry *dbfs_root;
+ struct dbfs_data dbfs_buffer;
+};
+
+u8 val[256];
+
+static int elan_i2c_read_block(struct i2c_client *client,
+ u16 reg, u8 *val, u16 len);
+static int elan_i2c_read_cmd(struct i2c_client *client, u16 reg, u8 *val);
+static int elan_i2c_write_cmd(struct i2c_client *client, u16 reg, u16 cmd);
+static int elan_i2c_reinitialize(struct i2c_client *client);
+static int elan_i2c_enable_calibrate(struct i2c_client *client);
+static int elan_i2c_disable_calibrate(struct i2c_client *client);
+
+/*
+ **************************************************************
+ * debugfs interface
+ **************************************************************
+*/
+static int elan_dbfs_open(struct inode *inode, struct file *file)
+{
+ int retval;
+ struct elan_tp_data *data = inode->i_private;
+
+ if (!data)
+ return -ENODEV;
+
+ retval = mutex_lock_interruptible(&data->dbfs_mutex);
+ if (retval)
+ return retval;
+
+ if (!kobject_get(&data->client->dev.kobj)) {
+ retval = -ENODEV;
+ goto dbfs_out;
+ }
+
+ file->private_data = data;
+dbfs_out:
+ mutex_unlock(&data->dbfs_mutex);
+ return 0;
+}
+
+static int elan_dbfs_release(struct inode *inode, struct file *file)
+{
+ struct elan_tp_data *data = file->private_data;
+ int retval;
+ if (!data)
+ return -ENODEV;
+
+ retval = mutex_lock_interruptible(&data->dbfs_mutex);
+ if (retval)
+ return retval;
+ file->private_data = NULL;
+ kobject_put(&data->client->dev.kobj);
+ mutex_unlock(&data->dbfs_mutex);
+ return 0;
+}
+
+
+static ssize_t elan_dbfs_read(struct file *file,
+ char __user *buffer, size_t count, loff_t *ppos)
+{
+ struct elan_tp_data *data = file->private_data;
+ int retval;
+ if (!data)
+ return -ENODEV;
+
+ retval = mutex_lock_interruptible(&data->dbfs_mutex);
+ if (retval)
+ return -EFAULT;
+ if (data->dbfs_buffer.bfetch == false) {
+ if (!copy_to_user(buffer, data->dbfs_buffer.buffer, count)) {
+ data->dbfs_buffer.bfetch = true;
+ retval = count;
+ } else {
+ retval = -2;
+ }
+ } else {
+ retval = -4;
+ }
+ mutex_unlock(&data->dbfs_mutex);
+ return retval;
+}
+
+static ssize_t elan_dbfs_write(struct file *file,
+ const char __user *buffer, size_t count, loff_t *ppos)
+{
+ struct elan_tp_data *data = file->private_data;
+ int retval;
+ if (!data)
+ return -ENODEV;
+
+ retval = mutex_lock_interruptible(&data->dbfs_mutex);
+ if (retval)
+ return -EFAULT;
+ retval = count;
+ mutex_unlock(&data->dbfs_mutex);
+ return retval;
+}
+
+static long elan_dbfs_ioctrl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int retval = 0;
+ struct elan_tp_data *data = file->private_data;
+
+ retval = mutex_lock_interruptible(&data->dbfs_mutex);
+ if (retval)
+ return retval;
+ mutex_unlock(&data->dbfs_mutex);
+ return retval;
+}
+
+static const struct file_operations elan_debug_fops = {
+ .open = elan_dbfs_open,
+ .release = elan_dbfs_release,
+ .read = elan_dbfs_read,
+ .write = elan_dbfs_write,
+ .unlocked_ioctl = elan_dbfs_ioctrl
+};
+
+static int elan_dbfs_init(struct elan_tp_data *data)
+{
+ /* Create a global debugfs root for all elan devices */
+ /* sys/kernel/debug/elan */
+ data->dbfs_root = debugfs_create_dir("elan", NULL);
+ if (!data->dbfs_root) {
+ dev_err(&data->client->dev, "cannot create dbfs_root.\n");
+ return -ENODEV;
+ }
+ mutex_init(&data->dbfs_mutex);
+
+ debugfs_create_file(DRIVER_NAME, 0777,
+ data->dbfs_root, data, &elan_debug_fops);
+ data->dbfs_buffer.bfetch = false;
+ return 0;
+}
+
+/**********************************************************
+ * IAP firmware updater related routines *
+ **********************************************************
+*/
+
+static int elan_iap_getmode(struct elan_tp_data *data)
+{
+ u16 constant;
+ int retval = 0;
+ struct i2c_client *client = data->client;
+
+ retval = elan_i2c_read_cmd(client, ETP_I2C_IAP_CTRL_CMD, val);
+ if (retval < 0)
+ return false;
+
+ constant = le16_to_cpup((__le16 *)val);
+ dev_dbg(&client->dev, "control reg: 0x%04x.\n", constant);
+
+ if (constant & ETP_FW_IAP_MODE_ON)
+ return MAIN_MODE;
+
+ return IAP_MODE;
+}
+
+static int elan_iap_checksum(struct elan_tp_data *data)
+{
+ int retval = 0;
+ u16 checksum = -1;
+ struct i2c_client *client = data->client;
+
+ retval = elan_i2c_read_cmd(client, ETP_I2C_IAP_CHECKSUM_CMD, val);
+ if (retval < 0) {
+ dev_err(&client->dev, "Read checksum fail, %d\n", retval);
+ return -1;
+ }
+ checksum = le16_to_cpup((__le16 *)val);
+ return checksum;
+}
+
+static bool elan_iap_reset(struct elan_tp_data *data)
+{
+ int retval = 0;
+ struct i2c_client *client = data->client;
+
+ retval = elan_i2c_write_cmd(client, ETP_I2C_IAP_RESET_CMD,
+ ETP_IAP_RESET);
+ if (retval < 0) {
+ dev_err(&client->dev, "cannot reset IC, %d\n", retval);
+ return false;
+ }
+ return true;
+}
+
+static bool elan_iap_setflashkey(struct elan_tp_data *data, int mode)
+{
+ int retval = 0;
+ struct i2c_client *client = data->client;
+
+ retval = elan_i2c_write_cmd(client, ETP_I2C_IAP_CMD,
+ ETP_ENABLE_FWUPDATE);
+ if (retval < 0) {
+ dev_err(&client->dev, "cannot set flash key, %d\n", retval);
+ return false;
+ }
+
+ /* Wait for F/W IAP initialization */
+ if (mode == MAIN_MODE)
+ msleep(100);
+ else
+ msleep(30);
+
+ return true;
+}
+
+static bool elan_iap_page_write_ok(struct elan_tp_data *data)
+{
+ u16 constant;
+ int retval = 0;
+ struct i2c_client *client = data->client;
+
+ retval = elan_i2c_read_cmd(client, ETP_I2C_IAP_CTRL_CMD, val);
+ if (retval < 0)
+ return false;
+
+ constant = le16_to_cpup((__le16 *)val);
+
+ if (constant & ETP_FW_IAP_PAGE_ERR)
+ return false;
+
+ if (constant & ETP_FW_IAP_INTERFACE_ERR)
+ return false;
+
+ return true;
+}
+
+static int elan_check_fw(struct elan_tp_data *data,
+ const struct firmware *fw)
+{
+ u16 uniqueid_addr, uniqueid;
+ struct device *dev = &data->client->dev;
+
+ /* Firmware must match exact PAGE_NUM * PAGE_SIZE bytes */
+ if (fw->size != ETP_FW_SIZE) {
+ dev_err(dev, "invalid firmware size = %zu, expected %d.\n",
+ fw->size, ETP_FW_SIZE);
+ return -EBADF;
+ }
+
+ /* Check IAP Version */
+ if (!memcmp(&fw->data[ETP_IAP_VERSION_ADDR * 2],
+ &data->iap_version, 2)) {
+ dev_err(dev, "IAP F/W updating version does not match.");
+ return -EEXIST;
+ }
+
+ /* Get IAP Start Address*/
+ memcpy(&data->iap_start_addr, &fw->data[ETP_IAP_START_ADDR * 2], 2);
+ /* Get Unique ID Address */
+ memcpy(&uniqueid_addr, &fw->data[data->iap_start_addr * 2], 2);
+ /* Get Unique ID */
+ memcpy(&uniqueid, &fw->data[uniqueid_addr * 2], 2);
+
+ return 0;
+}
+
+static int elan_prepare_fw_update(struct elan_tp_data *data)
+{
+ struct i2c_client *client = data->client;
+ struct device *dev = &data->client->dev;
+
+ /* Get FW in which mode (IAP_MODE/MAIN_MODE) */
+ int mode = elan_iap_getmode(data);
+
+ if (mode == IAP_MODE) {
+ /* Reset IC */
+ if (elan_iap_reset(data) == false)
+ return -1;
+ msleep(30);
+ }
+
+ /* set flash key*/
+ if (elan_iap_setflashkey(data, mode) == false) {
+ dev_err(dev, "cannot set flash key\n");
+ return -1;
+ }
+
+ /* check is in iap mode or not*/
+ if (elan_iap_getmode(data) == MAIN_MODE) {
+ dev_err(dev, "status wrong.\n");
+ return -1;
+ }
+
+ /* set flash key again */
+ if (elan_iap_setflashkey(data, mode) == false) {
+ dev_err(dev, "cannot set flash key\n");
+ return -1;
+ }
+
+ /* read back to check we actually enabled successfully. */
+ if (elan_i2c_read_cmd(client, ETP_I2C_IAP_CMD, val) < 0) {
+ dev_err(dev, "cannot get iap register\n");
+ return -1;
+ }
+
+ if (le16_to_cpup((__le16 *)val) != ETP_ENABLE_FWUPDATE) {
+ dev_err(dev, "wrong iap password = 0x%X\n",
+ le16_to_cpup((__le16 *)val));
+ return -1;
+ }
+ return 0;
+}
+
+static int elan_write_fw_block(struct elan_tp_data *data,
+ const u8 *page, u16 checksum)
+{
+ struct device *dev = &data->client->dev;
+ int ret;
+ int repeat = 3;
+ u8 page_store[ETP_FW_PAGE_SIZE + 4];
+
+ page_store[0] = ETP_FW_IAP_REG_L;
+ page_store[1] = ETP_FW_IAP_REG_H;
+ memcpy(&page_store[2], page, ETP_FW_PAGE_SIZE);
+
+ /* recode checksum at last two bytes */
+ page_store[ETP_FW_PAGE_SIZE+2] = (u8)(checksum & 0xFF);
+ page_store[ETP_FW_PAGE_SIZE+3] = (u8)((checksum >> 8)&0xFF);
+
+ do {
+ ret = i2c_master_send(data->client, page_store,
+ ETP_FW_PAGE_SIZE + 4);
+
+ /* Wait for F/W to update one page ROM data. */
+ msleep(20);
+
+ if (ret == (ETP_FW_PAGE_SIZE + 4)) {
+ if (elan_iap_page_write_ok(data))
+ break;
+ }
+ dev_dbg(dev, "IAP retry this page!\n");
+ repeat--;
+ } while (repeat == 0);
+
+ if (repeat > 0)
+ return 0;
+ return -1;
+}
+
+static int elan_firmware(struct elan_tp_data *data)
+{
+ struct device *dev = &data->client->dev;
+ const struct firmware *fw;
+ const char *fw_name = ETP_FW_NAME;
+ int i, j, ret;
+ u16 boot_page_count;
+ u16 sw_checksum, fw_checksum;
+
+ ret = request_firmware(&fw, ETP_FW_NAME, dev);
+ if (ret) {
+ dev_err(dev, "cannot load firmware from %s, %d\n",
+ fw_name, ret);
+ goto done;
+ }
+ ret = elan_check_fw(data, fw);
+ if (ret) {
+ dev_err(dev, "Invalid Elan firmware from %s, %d\n",
+ fw_name, ret);
+ goto done;
+ }
+
+ /* set flash key and into IAP mode */
+ ret = elan_prepare_fw_update(data);
+ if (ret)
+ goto done;
+
+ sw_checksum = 0;
+ fw_checksum = 0;
+ boot_page_count = (data->iap_start_addr * 2) / ETP_FW_PAGE_SIZE;
+ for (i = boot_page_count; i < ETP_FW_PAGE_COUNT; i++) {
+ u16 checksum = 0;
+ const u8 *page = &fw->data[i * ETP_FW_PAGE_SIZE];
+
+ for (j = 0; j < ETP_FW_PAGE_SIZE; j += 2)
+ checksum += ((page[j + 1] << 8) | page[j]);
+
+ ret = elan_write_fw_block(data, page, checksum);
+ if (ret)
+ goto done;
+ sw_checksum += checksum;
+ }
+ /* Wait WDT rest and power on reset*/
+ msleep(600);
+
+ /* check checksum */
+ fw_checksum = elan_iap_checksum(data);
+ if (sw_checksum != fw_checksum) {
+ dev_err(dev, "checksum diff sw=[%04X], fw=[%04X]\n",
+ sw_checksum, fw_checksum);
+ ret = -1;
+ goto done;
+ }
+ ret = 0;
+done:
+ if (ret != 0)
+ elan_iap_reset(data);
+ release_firmware(fw);
+ return ret;
+}
+
+/**************************************************************************
+* Genernal functions
+***************************************************************************
+*/
+
+/*
+ * (value from firmware) * 10 + 790 = dpi
+ * we also have to convert dpi to dots/mm (*10/254 to avoid floating point)
+ */
+static unsigned int elan_convert_res(char val)
+{
+ int res;
+ if (val & 0x80) {
+ val = ~val + 1;
+ res = (790 - val * 10) * 10 / 254;
+ } else
+ res = (val * 10 + 790) * 10 / 254;
+ return res;
+}
+
+static int elan_get_iap_version(struct elan_tp_data *data)
+{
+ int ret;
+ elan_i2c_read_block(data->client, ETP_I2C_IAP_VERSION_CMD, val, 3);
+ ret = le16_to_cpup((__le16 *)val);
+ return ret;
+}
+
+static int elan_get_x_max(struct elan_tp_data *data)
+{
+ int ret;
+ elan_i2c_read_cmd(data->client, ETP_I2C_MAX_X_AXIS_CMD, val);
+ ret = (0x0f & val[1]) << 8 | val[0];
+ return ret;
+}
+
+static int elan_get_y_max(struct elan_tp_data *data)
+{
+ int ret;
+ elan_i2c_read_cmd(data->client, ETP_I2C_MAX_Y_AXIS_CMD, val);
+ ret = (0x0f & val[1]) << 8 | val[0];
+ return ret;
+}
+
+static int elan_get_x_tracenum(struct elan_tp_data *data)
+{
+ int ret;
+ elan_i2c_read_cmd(data->client, ETP_I2C_XY_TRACENUM_CMD, val);
+ ret = (val[0] - 1);
+ return ret;
+}
+
+static int elan_get_y_tracenum(struct elan_tp_data *data)
+{
+ int ret;
+ ret = elan_i2c_read_cmd(data->client, ETP_I2C_XY_TRACENUM_CMD, val);
+ ret = (val[1] - 1);
+ return ret;
+}
+
+static int elan_get_fw_version(struct elan_tp_data *data)
+{
+ int ret;
+ elan_i2c_read_cmd(data->client, ETP_I2C_FW_VERSION_CMD, val);
+ ret = le16_to_cpup((__le16 *)val);
+ return ret;
+}
+
+static int elan_get_sm_version(struct elan_tp_data *data)
+{
+ int ret;
+ elan_i2c_read_block(data->client, ETP_I2C_SM_VERSION_CMD, val, 1);
+ ret = val[0];
+ return ret;
+}
+
+static int elan_get_unique_id(struct elan_tp_data *data)
+{
+ int ret;
+ elan_i2c_read_cmd(data->client, ETP_I2C_UNIQUEID_CMD, val);
+ ret = le16_to_cpup((__le16 *)val);
+ return ret;
+}
+
+static int elan_get_x_resolution(struct elan_tp_data *data)
+{
+ int ret;
+ elan_i2c_read_cmd(data->client, ETP_I2C_RESOLUTION_CMD, val);
+ ret = elan_convert_res(val[0]);
+ return ret;
+}
+
+static int elan_get_y_resolution(struct elan_tp_data *data)
+{
+ int ret;
+ elan_i2c_read_cmd(data->client, ETP_I2C_RESOLUTION_CMD, val);
+ ret = elan_convert_res(val[1]);
+ return ret;
+}
+
+static int elan_get_fw_checksum(struct elan_tp_data *data)
+{
+ int ret;
+ elan_i2c_read_cmd(data->client, ETP_I2C_FW_CHECKSUM_CMD, val);
+ ret = le16_to_cpup((__le16 *)val);
+ return ret;
+}
+
+static int elan_get_max_baseline(struct elan_tp_data *data)
+{
+ int ret;
+ elan_i2c_read_cmd(data->client, ETP_I2C_MAX_BASELINE_CMD, val);
+ ret = le16_to_cpup((__le16 *)val);
+ return ret;
+}
+
+static int elan_get_min_baseline(struct elan_tp_data *data)
+{
+ int ret;
+ elan_i2c_read_cmd(data->client, ETP_I2C_MIN_BASELINE_CMD, val);
+ ret = le16_to_cpup((__le16 *)val);
+ return ret;
+}
+
+
+static int elan_enable_calibrate(struct elan_tp_data *data)
+{
+ int ret;
+ ret = elan_i2c_enable_calibrate(data->client);
+ return ret;
+}
+
+static int elan_disable_calibrate(struct elan_tp_data *data)
+{
+ int ret;
+ ret = elan_i2c_disable_calibrate(data->client);
+ return ret;
+}
+
+/********************************************************************
+ * below routines export interfaces to sysfs file system.
+ * so user can get firmware/driver/hardware information using cat command.
+ * e.g.: use below command to get firmware version
+ * cat /sys/bus/i2c/drivers/elan_i2c/1-0015/firmware_version
+ *******************************************************************
+ */
+static ssize_t elan_enable_detailinfo(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct elan_tp_data *data = dev_get_drvdata(dev);
+ data->enable_detail_info = true;
+ return sprintf(buf, "enable\n");
+}
+
+static ssize_t elan_read_fw_checksum(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ unsigned int checksum = 0;
+ struct elan_tp_data *data = dev_get_drvdata(dev);
+ if (data->enable_detail_info == true) {
+ checksum = elan_get_fw_checksum(data);
+ data->enable_detail_info = false;
+ }
+ return sprintf(buf, "0x%04x\n", checksum);
+}
+
+static ssize_t elan_read_unique_id(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct elan_tp_data *data = dev_get_drvdata(dev);
+ data->unique_id = elan_get_unique_id(data);
+ return sprintf(buf, "0x%04x\n", data->unique_id);
+}
+
+static ssize_t elan_read_driver_ver(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%s\n", ELAN_DRIVER_VERSION);
+}
+
+static ssize_t elan_read_fw_ver(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct elan_tp_data *data = dev_get_drvdata(dev);
+ data->fw_version = elan_get_fw_version(data);
+ return sprintf(buf, "0x%04x\n", data->fw_version);
+}
+
+static ssize_t elan_read_sm_ver(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct elan_tp_data *data = dev_get_drvdata(dev);
+ data->sm_version = elan_get_sm_version(data);
+ return sprintf(buf, "0x%04x\n", data->sm_version);
+}
+
+static ssize_t elan_read_iap_ver(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct elan_tp_data *data = dev_get_drvdata(dev);
+ data->iap_version = elan_get_iap_version(data);
+ return sprintf(buf, "0x%04x\n", data->iap_version);
+}
+
+
+static ssize_t elan_update_fw(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct elan_tp_data *data = dev_get_drvdata(dev);
+ int ret;
+ data->updated_fw = true;
+ ret = elan_firmware(data);
+ if (ret)
+ dev_err(dev, "firmware update failed.\n");
+ else
+ dev_info(dev, "firmware update succeeded.\n");
+ return ret ? ret : count;
+}
+
+static ssize_t elan_do_calibrate(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct elan_tp_data *data = dev_get_drvdata(dev);
+ int tries = 20;
+ int ret = 0;
+ val[0] = 0;
+
+ disable_irq(data->irq);
+ elan_enable_calibrate(data);
+ elan_i2c_write_cmd(data->client, ETP_I2C_CALIBRATE_CMD, 1);
+
+ do {
+ /* wait 250ms and check finish or not */
+ msleep(250);
+ elan_i2c_read_block(data->client,
+ ETP_I2C_CALIBRATE_CMD, val, 1);
+
+ /* calibrate finish */
+ if (val[0] == 0)
+ break;
+ } while (--tries);
+
+ elan_disable_calibrate(data);
+ enable_irq(data->irq);
+
+ if (tries == 0) {
+ dev_err(dev, "Failed to calibrate. Timeout.\n");
+ ret = -ETIMEDOUT;
+ }
+ return ret < 0 ? ret : count;
+}
+
+
+static ssize_t elan_read_baseline(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct elan_tp_data *data = dev_get_drvdata(dev);
+ int max_baseline, min_baseline;
+
+ disable_irq(data->irq);
+ elan_enable_calibrate(data);
+ msleep(250);
+ max_baseline = elan_get_max_baseline(data);
+ min_baseline = elan_get_min_baseline(data);
+ elan_disable_calibrate(data);
+ enable_irq(data->irq);
+ return sprintf(buf, "max:%d min:%d\n", max_baseline, min_baseline);
+}
+
+static ssize_t elan_do_reinitialize(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct elan_tp_data *data = dev_get_drvdata(dev);
+ int ret;
+ disable_irq(data->irq);
+ ret = elan_i2c_reinitialize(data->client);
+ enable_irq(data->irq);
+ if (ret < 0)
+ return sprintf(buf, "reinitialize fail\n");
+
+ return sprintf(buf, "reinitialize success\n");
+}
+
+static DEVICE_ATTR(unique_id, S_IRUGO, elan_read_unique_id, NULL);
+static DEVICE_ATTR(firmware_version, S_IRUGO, elan_read_fw_ver, NULL);
+static DEVICE_ATTR(sample_version, S_IRUGO, elan_read_sm_ver, NULL);
+static DEVICE_ATTR(driver_version, S_IRUGO, elan_read_driver_ver, NULL);
+static DEVICE_ATTR(iap_version, S_IRUGO, elan_read_iap_ver, NULL);
+static DEVICE_ATTR(fw_checksum, S_IRUGO, elan_read_fw_checksum, NULL);
+static DEVICE_ATTR(open_info, S_IRUGO, elan_enable_detailinfo, NULL);
+static DEVICE_ATTR(baseline, S_IRUGO, elan_read_baseline, NULL);
+static DEVICE_ATTR(reinitialize, S_IRUGO, elan_do_reinitialize, NULL);
+static DEVICE_ATTR(calibrate, S_IWUSR, NULL, elan_do_calibrate);
+static DEVICE_ATTR(update_fw, S_IWUSR, NULL, elan_update_fw);
+
+static struct attribute *elan_sysfs_entries[] = {
+ &dev_attr_unique_id.attr,
+ &dev_attr_firmware_version.attr,
+ &dev_attr_sample_version.attr,
+ &dev_attr_driver_version.attr,
+ &dev_attr_iap_version.attr,
+ &dev_attr_fw_checksum.attr,
+ &dev_attr_open_info.attr,
+ &dev_attr_baseline.attr,
+ &dev_attr_reinitialize.attr,
+ &dev_attr_calibrate.attr,
+ &dev_attr_update_fw.attr,
+ NULL,
+};
+
+static const struct attribute_group elan_sysfs_group = {
+ .attrs = elan_sysfs_entries,
+};
+
+/*****************************************************************
+* Elan i2c interface
+******************************************************************
+*/
+static int elan_i2c_read_block(struct i2c_client *client,
+ u16 reg, u8 *val, u16 len)
+{
+ struct i2c_msg msgs[2];
+ u8 buf[2];
+ int ret;
+
+ buf[0] = reg & 0xff;
+ buf[1] = (reg >> 8) & 0xff;
+
+ msgs[0].addr = client->addr;
+ msgs[0].flags = client->flags & I2C_M_TEN;
+ msgs[0].len = 2;
+ msgs[0].buf = buf;
+
+ msgs[1].addr = client->addr;
+ msgs[1].flags = client->flags & I2C_M_TEN;
+ msgs[1].flags |= I2C_M_RD;
+ msgs[1].len = len;
+ msgs[1].buf = val;
+
+ ret = i2c_transfer(client->adapter, msgs, 2);
+ return ret != 2 ? -EIO : 0;
+}
+
+static int elan_i2c_read_cmd(struct i2c_client *client, u16 reg, u8 *val)
+{
+ int retval;
+
+ retval = elan_i2c_read_block(client, reg, val, ETP_INF_LENGTH);
+ if (retval < 0) {
+ dev_err(&client->dev, "reading cmd (0x%04x) fail.\n", reg);
+ return retval;
+ }
+ return 0;
+}
+
+static int elan_i2c_write_cmd(struct i2c_client *client, u16 reg, u16 cmd)
+{
+ struct i2c_msg msg;
+ u8 buf[4];
+ int ret;
+
+ buf[0] = reg & 0xff;
+ buf[1] = (reg >> 8) & 0xff;
+ buf[2] = cmd & 0xff;
+ buf[3] = (cmd >> 8) & 0xff;
+
+ msg.addr = client->addr;
+ msg.flags = client->flags & I2C_M_TEN;
+ msg.len = 4;
+ msg.buf = buf;
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ return ret != 1 ? -EIO : 0;
+}
+
+static int elan_i2c_reset(struct i2c_client *client)
+{
+ return elan_i2c_write_cmd(client, ETP_I2C_STAND_CMD,
+ ETP_I2C_RESET);
+}
+
+static int elan_i2c_wake_up(struct i2c_client *client)
+{
+ return elan_i2c_write_cmd(client, ETP_I2C_STAND_CMD,
+ ETP_I2C_WAKE_UP);
+}
+
+static int elan_i2c_sleep(struct i2c_client *client)
+{
+ return elan_i2c_write_cmd(client, ETP_I2C_STAND_CMD,
+ ETP_I2C_SLEEP);
+}
+
+static int elan_i2c_enable_absolute_mode(struct i2c_client *client)
+{
+ return elan_i2c_write_cmd(client, ETP_I2C_SET_CMD,
+ ETP_ENABLE_ABS);
+}
+
+static int elan_i2c_enable_calibrate(struct i2c_client *client)
+{
+ return elan_i2c_write_cmd(client, ETP_I2C_SET_CMD,
+ ETP_ENABLE_ABS|ETP_ENABLE_CALIBRATE);
+}
+
+static int elan_i2c_disable_calibrate(struct i2c_client *client)
+{
+ return elan_i2c_write_cmd(client, ETP_I2C_SET_CMD,
+ ETP_ENABLE_ABS|ETP_DISABLE_CALIBRATE);
+}
+
+static int elan_i2c_get_desc(struct i2c_client *client, u8 *val)
+{
+ return elan_i2c_read_block(client, ETP_I2C_DESC_CMD, val,
+ ETP_I2C_DESC_LENGTH);
+}
+
+static int elan_i2c_get_report_desc(struct i2c_client *client, u8 *val)
+{
+ return elan_i2c_read_block(client, ETP_I2C_REPORT_DESC_CMD,
+ val, ETP_I2C_REPORT_DESC_LENGTH);
+}
+
+static int elan_i2c_initialize(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ int rc;
+
+ rc = elan_i2c_reset(client);
+ if (rc < 0) {
+ dev_err(dev, "device reset failed.\n");
+ return -1;
+ }
+
+ /* wait for get reset return flag */
+ msleep(100);
+ /* get reset return flag 0000 */
+ rc = i2c_master_recv(client, val, ETP_INF_LENGTH);
+ if (rc < 0) {
+ dev_err(dev, "get device reset return value failed.\n");
+ return -1;
+ }
+
+ rc = elan_i2c_get_desc(client, val);
+ if (rc < 0) {
+ dev_err(dev, "cannot get device descriptor.\n");
+ return -1;
+ }
+
+ rc = elan_i2c_get_report_desc(client, val);
+ if (rc < 0) {
+ dev_err(dev, "fetching report descriptor failed.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int elan_i2c_reinitialize(struct i2c_client *client)
+{
+ int ret;
+
+ ret = elan_i2c_initialize(client);
+ if (ret < 0) {
+ dev_err(&client->dev, "device initialize failed.\n");
+ goto err_i2c_reinitialize;
+ }
+
+ ret = elan_i2c_enable_absolute_mode(client);
+ if (ret < 0) {
+ dev_err(&client->dev, "cannot switch to absolute mode.\n");
+ goto err_i2c_reinitialize;
+ }
+
+ ret = elan_i2c_wake_up(client);
+ if (ret < 0)
+ dev_err(&client->dev, "device wake up failed.\n");
+err_i2c_reinitialize:
+ return ret;
+}
+
+/*****************************************************************
+* Elan isr functions
+******************************************************************
+*/
+
+static int elan_check_packet(struct elan_tp_data *data, u8 *packet)
+{
+ u8 rid;
+ rid = packet[ETP_I2C_REPORT_ID_OFFSET];
+ /* check report id */
+ if (rid != ETP_REPORT_ID) {
+ dev_err(&data->client->dev, "report id [%x] fail.\n", rid);
+ return -1;
+ }
+ return 0;
+}
+
+static void elan_report_absolute(struct elan_tp_data *data, u8 *packet)
+{
+ struct input_dev *input = data->input;
+ u8 *finger_data;
+ bool finger_on;
+ int pos_x, pos_y;
+ int pressure, mk_x, mk_y;
+ int i, area_x, area_y, major, minor, new_pressure;
+ int finger_count = 0;
+ int btn_click;
+ u8 tp_info;
+
+ finger_data = &packet[ETP_I2C_FINGER_DATA_OFFSET];
+ tp_info = packet[3];
+
+ btn_click = (tp_info & 0x01);
+ for (i = 0; i < ETP_MAX_FINGERS; i++) {
+ finger_on = (tp_info >> (3 + i)) & 0x01;
+
+ /* analyze touched finger raw data*/
+ if (finger_on) {
+ pos_x = ((finger_data[0] & 0xf0) << 4) |
+ finger_data[1];
+ pos_y = ((finger_data[0] & 0x0f) << 8) |
+ finger_data[2];
+ pos_y = data->max_y - pos_y;
+ mk_x = (finger_data[3] & 0x0f);
+ mk_y = (finger_data[3] >> 4);
+ pressure = finger_data[4];
+
+ /*
+ to avoid fat finger be as palm, so reduce the
+ width x and y per trace
+ */
+ area_x = mk_x * (data->width_x - ETP_FWIDTH_REDUCE);
+ area_y = mk_y * (data->width_y - ETP_FWIDTH_REDUCE);
+
+ major = max(area_x, area_y);
+ minor = min(area_x, area_y);
+
+ new_pressure = pressure + ETP_PRESSURE_OFFSET;
+ if (new_pressure > ETP_MAX_PRESSURE)
+ new_pressure = ETP_MAX_PRESSURE;
+
+ input_mt_slot(input, i);
+ input_mt_report_slot_state(input, MT_TOOL_FINGER,
+ true);
+ input_report_abs(input, ABS_MT_POSITION_X, pos_x);
+ input_report_abs(input, ABS_MT_POSITION_Y, pos_y);
+ input_report_abs(input, ABS_MT_PRESSURE, new_pressure);
+ input_report_abs(input, ABS_TOOL_WIDTH, mk_x);
+ input_report_abs(input, ABS_MT_TOUCH_MAJOR, major);
+ input_report_abs(input, ABS_MT_TOUCH_MINOR, minor);
+ finger_data += ETP_FINGER_DATA_LEN;
+ finger_count++;
+ } else {
+ input_mt_slot(input, i);
+ input_mt_report_slot_state(input,
+ MT_TOOL_FINGER, false);
+ }
+ }
+
+ input_report_key(input, BTN_LEFT, (btn_click == 1));
+ input_mt_report_pointer_emulation(input, true);
+ input_sync(input);
+}
+
+static irqreturn_t elan_isr(int irq, void *dev_id)
+{
+ struct elan_tp_data *data = dev_id;
+ u8 raw[ETP_MAX_REPORT_LEN];
+ int retval;
+ int report_len;
+
+ retval = mutex_lock_interruptible(&data->dbfs_mutex);
+ if (retval)
+ return IRQ_HANDLED;
+
+ if (data->updated_fw) {
+ retval = i2c_master_recv(data->client, raw,
+ ETP_INF_LENGTH);
+ if (retval == 2 && !le16_to_cpup((__le16 *)raw)) {
+ dev_info(&data->client->dev,
+ "reinitializing after F/W update...");
+ elan_i2c_reinitialize(data->client);
+ }
+ data->updated_fw = false;
+ goto elan_isr_end;
+ }
+
+ report_len = ETP_I2C_REPORT_LEN;
+ retval = i2c_master_recv(data->client, raw, report_len);
+
+ if (retval != report_len) {
+ dev_err(&data->client->dev, "wrong packet len(%d)", retval);
+ goto elan_isr_end;
+ }
+
+ if (elan_check_packet(data, raw) < 0) {
+ dev_err(&data->client->dev, "wrong packet format.");
+ goto elan_isr_end;
+ }
+ elan_report_absolute(data, raw);
+ data->dbfs_buffer.bfetch = false;
+ memcpy(data->dbfs_buffer.buffer, raw, report_len);
+
+elan_isr_end:
+ mutex_unlock(&data->dbfs_mutex);
+ return IRQ_HANDLED;
+}
+
+
+static int elan_input_dev_create(struct elan_tp_data *data)
+{
+ struct i2c_client *client = data->client;
+ struct input_dev *input;
+ unsigned int x_res, y_res;
+ int ret;
+
+ data->input = input = input_allocate_device();
+ if (!input)
+ return -ENOMEM;
+ input->name = "Elan Touchpad";
+ input->id.bustype = BUS_I2C;
+ input->dev.parent = &data->client->dev;
+
+ __set_bit(INPUT_PROP_POINTER, input->propbit);
+ __set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
+ __set_bit(EV_KEY, input->evbit);
+ __set_bit(EV_ABS, input->evbit);
+
+ __set_bit(BTN_LEFT, input->keybit);
+ __set_bit(BTN_TOUCH, input->keybit);
+ __set_bit(BTN_TOOL_FINGER, input->keybit);
+ __set_bit(BTN_TOOL_DOUBLETAP, input->keybit);
+ __set_bit(BTN_TOOL_TRIPLETAP, input->keybit);
+ __set_bit(BTN_TOOL_QUADTAP, input->keybit);
+ __set_bit(BTN_TOOL_QUINTTAP, input->keybit);
+
+ __set_bit(ABS_MT_TOUCH_MAJOR, input->absbit);
+ __set_bit(ABS_MT_TOUCH_MINOR, input->absbit);
+ __set_bit(ABS_MT_POSITION_X, input->absbit);
+ __set_bit(ABS_MT_POSITION_Y, input->absbit);
+
+ data->unique_id = elan_get_unique_id(data);
+ data->fw_version = elan_get_fw_version(data);
+ data->sm_version = elan_get_sm_version(data);
+ data->iap_version = elan_get_iap_version(data);
+ data->max_x = elan_get_x_max(data);
+ data->max_y = elan_get_y_max(data);
+ data->width_x = data->max_x / elan_get_x_tracenum(data);
+ data->width_y = data->max_y / elan_get_y_tracenum(data);
+ x_res = elan_get_x_resolution(data);
+ y_res = elan_get_y_resolution(data);
+
+ dev_info(&client->dev,
+ "Elan Touchpad Information:\n"
+ " Module unique ID: 0x%04x\n"
+ " Firmware Version: 0x%04x\n"
+ " Sample Version: 0x%04x\n"
+ " IAP Version: 0x%04x\n"
+ " Max ABS X,Y: %d,%d\n"
+ " Width X,Y: %d,%d\n"
+ " Resolution X,Y: %d,%d (dots/mm)\n",
+ data->unique_id,
+ data->fw_version,
+ data->sm_version,
+ data->iap_version,
+ data->max_x, data->max_y,
+ data->width_x, data->width_y,
+ (char)x_res, (char)y_res);
+
+ input_set_abs_params(input, ABS_X, 0, data->max_x, 0, 0);
+ input_set_abs_params(input, ABS_Y, 0, data->max_y, 0, 0);
+ input_abs_set_res(input, ABS_X, x_res);
+ input_abs_set_res(input, ABS_Y, y_res);
+ input_set_abs_params(input, ABS_PRESSURE, 0, ETP_MAX_PRESSURE, 0, 0);
+ input_set_abs_params(input, ABS_TOOL_WIDTH, 0, ETP_FINGER_WIDTH, 0, 0);
+
+ /* handle pointer emulation and unused slots in core */
+ ret = input_mt_init_slots(input, ETP_MAX_FINGERS,
+ INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED);
+ if (ret) {
+ dev_err(&client->dev, "allocate MT slots failed, %d\n", ret);
+ goto err_free_device;
+ }
+ input_set_abs_params(input, ABS_MT_POSITION_X, 0, data->max_x, 0, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_Y, 0, data->max_y, 0, 0);
+ input_abs_set_res(input, ABS_MT_POSITION_X, x_res);
+ input_abs_set_res(input, ABS_MT_POSITION_Y, y_res);
+ input_set_abs_params(input, ABS_MT_PRESSURE, 0,
+ ETP_MAX_PRESSURE, 0, 0);
+ input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0,
+ ETP_FINGER_WIDTH * max(data->width_x, data->width_y), 0, 0);
+ input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0,
+ ETP_FINGER_WIDTH * min(data->width_x, data->width_y), 0, 0);
+
+ /* Register the device in input subsystem */
+ ret = input_register_device(input);
+ if (ret) {
+ dev_err(&client->dev, "input device register failed, %d\n",
+ ret);
+ goto err_free_device;
+ }
+
+ return 0;
+
+err_free_device:
+ input_free_device(input);
+ return ret;
+}
+
+static u8 elan_check_adapter_functionality(struct i2c_client *client)
+{
+ u8 ret = ELAN_ADAPTER_FUNC_NONE;
+
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ ret |= ELAN_ADAPTER_FUNC_I2C;
+ return ret;
+}
+
+static int elan_probe(struct i2c_client *client,
+ const struct i2c_device_id *dev_id)
+{
+ struct elan_tp_data *data;
+ int ret;
+ u8 adapter_func;
+ struct device *dev = &client->dev;
+
+ adapter_func = elan_check_adapter_functionality(client);
+ if (adapter_func == ELAN_ADAPTER_FUNC_NONE) {
+ dev_err(dev, "not a supported I2C adapter\n");
+ return -EIO;
+ }
+
+ data = kzalloc(sizeof(struct elan_tp_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->client = client;
+ data->updated_fw = false;
+ data->enable_detail_info = false;
+ data->adapter_func = adapter_func;
+ data->irq = client->irq;
+
+ ret = elan_i2c_initialize(client);
+ if (ret < 0)
+ goto err_init;
+
+ ret = elan_input_dev_create(data);
+ if (ret < 0)
+ goto err_input_dev;
+
+ if (elan_dbfs_init(data)) {
+ dev_err(&client->dev, "error create elan debugfs.\n");
+ goto err_input_dev;
+ }
+ ret = request_threaded_irq(client->irq, NULL, elan_isr,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ client->name, data);
+ if (ret < 0) {
+ dev_err(&client->dev, "cannot register irq=%d\n",
+ client->irq);
+ goto err_irq;
+ }
+
+ ret = elan_i2c_enable_absolute_mode(client);
+ if (ret < 0) {
+ dev_err(&client->dev, "cannot switch to abs mode.\n");
+ goto err_switch_mode;
+ }
+
+ ret = elan_i2c_wake_up(client);
+ if (ret < 0) {
+ dev_err(&client->dev, "device wake up failed.\n");
+ goto err_switch_mode;
+ }
+
+ device_init_wakeup(&client->dev, 1);
+ ret = sysfs_create_group(&client->dev.kobj, &elan_sysfs_group);
+ if (ret < 0) {
+ dev_err(&client->dev, "cannot register dev attribute %d", ret);
+ goto err_switch_mode;
+ }
+ i2c_set_clientdata(client, data);
+ return 0;
+
+err_switch_mode:
+ free_irq(data->irq, data);
+err_irq:
+ input_free_device(data->input);
+ debugfs_remove_recursive(data->dbfs_root);
+ mutex_destroy(&data->dbfs_mutex);
+err_input_dev:
+ kfree(data);
+err_init:
+ dev_err(&client->dev, "Elan Trackpad probe fail!\n");
+ return ret;
+}
+
+static int elan_remove(struct i2c_client *client)
+{
+ struct elan_tp_data *data = i2c_get_clientdata(client);
+
+ free_irq(data->irq, data);
+ debugfs_remove_recursive(data->dbfs_root);
+ mutex_destroy(&data->dbfs_mutex);
+
+ input_unregister_device(data->input);
+ kfree(data);
+ sysfs_remove_group(&client->dev.kobj, &elan_sysfs_group);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int elan_suspend(struct device *dev)
+{
+ int ret;
+ struct elan_tp_data *data = dev_get_drvdata(dev);
+
+ disable_irq(data->irq);
+ ret = elan_i2c_sleep(data->client);
+
+ if (ret < 0) {
+ dev_err(dev, "suspend mode failed, %d\n", ret);
+ } else {
+ if (device_may_wakeup(dev))
+ data->irq_wake = (enable_irq_wake(data->irq) == 0);
+ }
+ return 0;
+}
+
+static int elan_resume(struct device *dev)
+{
+ int ret = 0;
+ struct elan_tp_data *data = dev_get_drvdata(dev);
+
+ if (device_may_wakeup(dev) && data->irq_wake)
+ disable_irq_wake(data->irq);
+
+ ret = elan_i2c_reinitialize(data->client);
+
+ if (ret < 0)
+ dev_err(dev, "resume active power failed, %d\n", ret);
+
+ enable_irq(data->irq);
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(elan_pm_ops, elan_suspend, elan_resume);
+
+static const struct i2c_device_id elan_id[] = {
+ { DRIVER_NAME, 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, elan_id);
+
+static struct i2c_driver elan_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .pm = &elan_pm_ops,
+ },
+ .probe = elan_probe,
+ .remove = elan_remove,
+ .id_table = elan_id,
+};
+
+
+static int __init elan_init(void)
+{
+ int ret;
+ ret = i2c_add_driver(&elan_driver);
+ if (ret) {
+ pr_err("elan driver register FAILED.\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+static void __exit elan_exit(void)
+{
+ i2c_del_driver(&elan_driver);
+}
+
+module_init(elan_init);
+module_exit(elan_exit);
+
+MODULE_AUTHOR("Duson Lin <dusonlin@emc.com.tw>");
+MODULE_DESCRIPTION("Elan I2C Touchpad driver");
+MODULE_LICENSE("GPL");
--
1.7.10.4
^ permalink raw reply related
* Re: [PATCHv4][ 1/4] Input: tsc2007: Add device tree support.
From: Lothar Waßmann @ 2013-10-23 7:25 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: Mark Rutland, devicetree, Sascha Hauer, Pawel Moll,
Stephen Warren, Ian Campbell, Rob Herring, Denis Carikli,
Eric Bénard, linux-input, linux-arm-kernel
In-Reply-To: <20131022223504.GA19819@core.coreip.homeip.net>
Hi,
> On Tue, Oct 22, 2013 at 11:49:47AM +0200, Lothar Waßmann wrote:
> > Hi,
> >
> > > On Mon, Oct 21, 2013 at 03:54:24PM +0200, Denis Carikli wrote:
> > > >
> > > > + if (ts->of)
> > > > + return tsc2007_get_pendown_state_dt(ts);
> > > > +
> > > > if (!ts->get_pendown_state)
> > > > return true;
> > >
> > > Instead of special casing "if (ts->of)" all over the place why don't you
> > > set up the device structure as:
> > >
> > > if (<configuring_tsc2007_form_dt>)
> > > ts->get_pendown_state = tsc2007_get_pendown_state_dt;
> > >
> > > and be done with it?
> > >
> > I also thought about that, but the existing function does not have any
> > parameters, while the DT version of get_pendown_state() requires to get
> > the GPIO passed to it somehow.
>
> You can always have tsc2007_get_pendown_state_platform() wrapping the
>
Yes, but how would you pass the GPIO number to the
get_pendown_state_dt() function? Global variables are just ugly.
> call. Or we just go and fix board code.
>
That's IMO a better option, but not easy to achieve without breaking
anything. The in-kernel platforms would be easy to fix, but there may be
external users that still use the old hook, so you can't just remove it
or change its semantics.
Lothar Waßmann
--
___________________________________________________________
Ka-Ro electronics GmbH | Pascalstraße 22 | D - 52076 Aachen
Phone: +49 2408 1402-0 | Fax: +49 2408 1402-10
Geschäftsführer: Matthias Kaussen
Handelsregistereintrag: Amtsgericht Aachen, HRB 4996
www.karo-electronics.de | info@karo-electronics.de
___________________________________________________________
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox