* [PATCH 1/2] DT: i2c: Add more devices handled by the rtc-rs5c372 driver
From: Geert Uytterhoeven @ 2014-11-14 16:03 UTC (permalink / raw)
To: Wolfram Sang
Cc: Dmitry Torokhov, Alessandro Zummo,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-input-u79uwXL29TY76Z2rM5mHXA,
rtc-linux-/JYPxA39Uh5TLH3MbocFFw,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-i2c-u79uwXL29TY76Z2rM5mHXA, Geert Uytterhoeven
This allows checkpatch to validate more DTSes.
Signed-off-by: Geert Uytterhoeven <geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ@public.gmane.org>
---
Documentation/devicetree/bindings/i2c/trivial-devices.txt | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/Documentation/devicetree/bindings/i2c/trivial-devices.txt b/Documentation/devicetree/bindings/i2c/trivial-devices.txt
index fbde415078e6845c..7a2bbea61b70b942 100644
--- a/Documentation/devicetree/bindings/i2c/trivial-devices.txt
+++ b/Documentation/devicetree/bindings/i2c/trivial-devices.txt
@@ -74,7 +74,12 @@ ovti,ov5642 OV5642: Color CMOS QSXGA (5-megapixel) Image Sensor with OmniBSI an
pericom,pt7c4338 Real-time Clock Module
plx,pex8648 48-Lane, 12-Port PCI Express Gen 2 (5.0 GT/s) Switch
ramtron,24c64 i2c serial eeprom (24cxx)
+ricoh,r2025sd I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
+ricoh,r2221tl I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
ricoh,rs5c372a I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
+ricoh,rs5c372b I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
+ricoh,rv5c386 I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
+ricoh,rv5c387a I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
samsung,24ad0xd1 S524AD0XF1 (128K/256K-bit Serial EEPROM for Low Power)
sii,s35390a 2-wire CMOS real-time clock
st-micro,24c256 i2c serial eeprom (24cxx)
--
1.9.1
^ permalink raw reply related
* Re: [PATCH 01/15] mfd: add new driver for Sharp LoCoMo
From: Dmitry Eremin-Solenikov @ 2014-11-14 15:30 UTC (permalink / raw)
To: Mark Brown
Cc: Linus Walleij, linux-arm-kernel@lists.infradead.org,
linux-gpio@vger.kernel.org, Linux Input,
linux-leds@vger.kernel.org, linux-spi@vger.kernel.org,
linux-fbdev@vger.kernel.org, alsa-devel@alsa-project.org,
Andrea Adami, Russell King, Daniel Mack, Haojian Zhuang,
Robert Jarzmik, Alexandre Courbot, Dmitry Torokhov, Bryan Wu,
Richard Purdie, Samuel Ortiz, Lee Jones
In-Reply-To: <20141114151000.GC3815@sirena.org.uk>
2014-11-14 18:10 GMT+03:00 Mark Brown <broonie@kernel.org>:
> On Fri, Nov 14, 2014 at 04:47:00PM +0400, Dmitry Eremin-Solenikov wrote:
>
>> I'm actually looking at the regulator interface. Since this DAC serves mostly
>> like a (semi-)constant voltage interface, would it be rather logical to use
>> regulator subsystem to drive it?
>
> Possibly... it's mostly a function of what the consumers of the
> functionality look like - if that code looks weird calling into the
> regulator API then it's bad, if that code looks OK it's fine. I haven't
> looked at the hardware at all so don't have strong feelings either way
> so long as the result looks tasteful.
The DAC is used in backlight driver (control brightness) and in ASoC
machine driver (master joined volume).
--
With best wishes
Dmitry
^ permalink raw reply
* Re: [PATCH] HID: usbhid: get/put around clearing needs_remote_wakeup
From: Alan Stern @ 2014-11-14 15:17 UTC (permalink / raw)
To: Benson Leung
Cc: johan-DgEjT+Ai2ygdnm+yROfE0A, Jiri Kosina,
linux-usb-u79uwXL29TY76Z2rM5mHXA,
linux-input-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Sameer Nanda
In-Reply-To: <CANLzEksHgo_tQiwJoUtXd0iV7tDXq0ieNrAXgtXwPtxxPBWrgw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On Thu, 13 Nov 2014, Benson Leung wrote:
> Hi Alan,
>
> On Thu, Nov 13, 2014 at 2:11 PM, Alan Stern <stern-nwvwT67g6+6dFdvTe/nMLpVzexx5G7lz@public.gmane.org> wrote:
> > Wait a minute -- in your previous email you said this approach didn't
> > work. So does it work or doesn't it?
>
> Sorry for the confusion. The approach *does* work.
>
> That was actually my original idea to fix the problem, but I saw other
> places in the kernel where it was done with a get/put.
The reason for the get/put is to force a call to autosuspend_check().
But in this case, if killing the interrupt URB causes
autosuspend_check() to run then the get/put isn't needed.
On the other hand, I don't see why killing the interrupt URB would
cause autosuspend_check() to run. Can you explain that?
Alan Stern
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH 01/15] mfd: add new driver for Sharp LoCoMo
From: Mark Brown @ 2014-11-14 15:10 UTC (permalink / raw)
To: Dmitry Eremin-Solenikov
Cc: Linus Walleij, linux-arm-kernel@lists.infradead.org,
linux-gpio@vger.kernel.org, Linux Input,
linux-leds@vger.kernel.org, linux-spi@vger.kernel.org,
linux-fbdev@vger.kernel.org, alsa-devel@alsa-project.org,
Andrea Adami, Russell King, Daniel Mack, Haojian Zhuang,
Robert Jarzmik, Alexandre Courbot, Dmitry Torokhov, Bryan Wu,
Richard Purdie, Samuel Ortiz, Lee Jones
In-Reply-To: <CALT56yOtjNbYOWM4ba5yP8pz7bNLY5BWpkkpfmv5QZU-7Y5u_g@mail.gmail.com>
[-- Attachment #1: Type: text/plain, Size: 583 bytes --]
On Fri, Nov 14, 2014 at 04:47:00PM +0400, Dmitry Eremin-Solenikov wrote:
> I'm actually looking at the regulator interface. Since this DAC serves mostly
> like a (semi-)constant voltage interface, would it be rather logical to use
> regulator subsystem to drive it?
Possibly... it's mostly a function of what the consumers of the
functionality look like - if that code looks weird calling into the
regulator API then it's bad, if that code looks OK it's fine. I haven't
looked at the hardware at all so don't have strong feelings either way
so long as the result looks tasteful.
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 473 bytes --]
^ permalink raw reply
* Re: [PATCH 02/15] GPIO: port LoCoMo gpio support from old driver
From: Dmitry Eremin-Solenikov @ 2014-11-14 12:48 UTC (permalink / raw)
To: Linus Walleij
Cc: Mark Brown, linux-arm-kernel@lists.infradead.org,
linux-gpio@vger.kernel.org, Linux Input,
linux-leds@vger.kernel.org, linux-spi@vger.kernel.org,
linux-fbdev@vger.kernel.org, alsa-devel@alsa-project.org,
Andrea Adami, Russell King, Daniel Mack, Haojian Zhuang,
Robert Jarzmik, Alexandre Courbot, Dmitry Torokhov, Bryan Wu,
Richard Purdie, Samuel Ortiz, Lee Jones, Jingoo
In-Reply-To: <CACRpkdZiari6CtWq1kBCvX3cwwatJiHAuWUb40WSUEfjZjk+Lg@mail.gmail.com>
2014-11-14 13:11 GMT+03:00 Linus Walleij <linus.walleij@linaro.org>:
> On Tue, Nov 11, 2014 at 2:16 PM, Dmitry Eremin-Solenikov
> <dbaryshkov@gmail.com> wrote:
>
>> Just to better understand your suggestions: do you want me to convert
>> to regmap only gpio driver or do you suggest to convert all LoCoMo drivers?
>
> Um... I was just thinking about this one usecase.
I ended up converting all drivers. It allowed me to clean up several points
in the driver.
>
> It's no big deal, the other review comments are more important.
Fixed most of the comments. Last remaining issue is factoring out m62332
interface.
>> That is doable, of course, but the amount of changes is overwhelming.
>> Also I'm concerned about the performance impact from going through
>> regmap layers.
>
> Is it on a critical path? The current locking isn't any less invasive
> AFAICT.
>
> Yours,
> Linus Walleij
--
With best wishes
Dmitry
^ permalink raw reply
* Re: [PATCH 01/15] mfd: add new driver for Sharp LoCoMo
From: Dmitry Eremin-Solenikov @ 2014-11-14 12:47 UTC (permalink / raw)
To: Mark Brown
Cc: Linus Walleij, linux-arm-kernel@lists.infradead.org,
linux-gpio@vger.kernel.org, Linux Input,
linux-leds@vger.kernel.org, linux-spi@vger.kernel.org,
linux-fbdev@vger.kernel.org, alsa-devel@alsa-project.org,
Andrea Adami, Russell King, Daniel Mack, Haojian Zhuang,
Robert Jarzmik, Alexandre Courbot, Dmitry Torokhov, Bryan Wu,
Richard Purdie, Samuel Ortiz, Lee Jones
In-Reply-To: <20141105202405.GJ8509@sirena.org.uk>
Hello,
2014-11-05 23:24 GMT+03:00 Mark Brown <broonie@kernel.org>:
> On Thu, Nov 06, 2014 at 12:02:49AM +0400, Dmitry Eremin-Solenikov wrote:
>> 2014-11-03 16:41 GMT+03:00 Linus Walleij <linus.walleij@linaro.org>:
>
>> > The point is still the same: no unrelated code in drivers/mfd,
>> > then either use IIO DAC as a middle layer or sink the DAC handling
>> > into respective subdriver, i.e. push it into the backlight or
>> > volume directly then.
>
>> The problem is that the DAC is equally used by backlight and by sound
>> device (WIP).
>> What about true i2c device driver sitting in drivers/misc and exporting a regmap
>> of 2 8-bit registers?
>
> If it can just export registers that sounds like a MFD. If it needs to
> export functionality then like Linus says the IIO subsystem abstracts
> DACs.
I took a look at IIO subsystem. Thanks for the pointer. Indeed max517 driver
can drive m62332 DAC with minimal modifications. However write support
of the consumer interface is non-existing yet (which would be required
to support DACs in unified manner).
I'm actually looking at the regulator interface. Since this DAC serves mostly
like a (semi-)constant voltage interface, would it be rather logical to use
regulator subsystem to drive it?
--
With best wishes
Dmitry
^ permalink raw reply
* Re: [PATCH v3 4/4] input: alps: Fix trackstick detection
From: Pali Rohár @ 2014-11-14 11:22 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: Hans de Goede, Yunkang Tang, Tommy Will, linux-input,
linux-kernel
In-Reply-To: <201411091230.05337@pali>
[-- Attachment #1: Type: Text/Plain, Size: 2445 bytes --]
On Sunday 09 November 2014 12:30:03 Pali Rohár wrote:
> On Sunday 09 November 2014 09:05:04 Dmitry Torokhov wrote:
> > Hi Pali,
> >
> > On Sun, Nov 02, 2014 at 12:25:10AM +0100, Pali Rohár wrote:
> > > int alps_detect(struct psmouse *psmouse, bool
> > > set_properties) {
> > >
> > > - struct alps_data dummy;
> > > + unsigned char e6[4];
> > >
> > > - if (alps_identify(psmouse, &dummy) < 0)
> > > - return -1;
> > > + /*
> > > + * Try "E6 report".
> > > + * ALPS should return 0,0,10 or 0,0,100 if no buttons
> > > are pressed. + * The bits 0-2 of the first byte will be
> > > 1s if some buttons are + * pressed.
> > > + */
> > > + if (alps_rpt_cmd(psmouse, PSMOUSE_CMD_SETRES,
> > > + PSMOUSE_CMD_SETSCALE11, e6))
> > > + return -EIO;
> > > +
> > > + if ((e6[0] & 0xf8) != 0 || e6[1] != 0 || (e6[2] != 10
&&
> > > e6[2] != 100)) + return -EINVAL;
> > >
> > > if (set_properties) {
> > >
> > > + /*
> > > + * NOTE: To detect model and trackstick presence we
>
> need
>
> > > to do + * full device reset. To speed up
>
> detection
>
> > > and prevent + * calling duplicate
initialization
> > > sequence (both in + * alps_detect() and
> > > alps_init()) we set model/protocol + * version
and
> > > correct name in alps_init() (which will + * do
>
> full
>
> > > device reset). For now set name to DualPoint. + */
> > >
> > > psmouse->vendor = "ALPS";
> > >
> > > - psmouse->name = dummy.flags & ALPS_DUALPOINT ?
> > > - "DualPoint TouchPad" : "GlidePoint";
> > > - psmouse->model = dummy.proto_version << 8;
> > > + psmouse->name = "DualPoint TouchPad";
> > >
> > > }
> > >
> > > +
> > >
> > > return 0;
> >
> > We can't do this; going by e6 only will give us false
> > positives and alps_detect is supposed to be authoritative.
> >
> > Thanks.
>
> psmouse-base.c is calling alps_detect() and alps_init()
> consecutively so it does not matter if code is in _detect or
> _init function. Just need to make sure that correct order will
> be used. So this patch could not bring any problems.
>
> If psmouse-base.c detect device false-positive as ALPS (with
> alps_detect()) then it immediately calls alps_init() which
> fails and psmouse-base.c will try to use another protocol.
Dmitry: PING
Also see my comment for PATCH v3 3/4.
--
Pali Rohár
pali.rohar@gmail.com
[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 198 bytes --]
^ permalink raw reply
* Re: [PATCH 02/15] GPIO: port LoCoMo gpio support from old driver
From: Linus Walleij @ 2014-11-14 10:11 UTC (permalink / raw)
To: Dmitry Eremin-Solenikov
Cc: Mark Brown,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
linux-gpio-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Linux Input,
linux-leds-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-spi-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
linux-fbdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw@public.gmane.org, Andrea Adami,
Russell King, Daniel Mack, Haojian Zhuang, Robert Jarzmik,
Alexandre Courbot, Dmitry Torokhov, Bryan Wu, Richard Purdie,
Samuel Ortiz, Lee Jones, Jingoo
In-Reply-To: <CALT56yPr42FV66USngocw=eWPt81d5R2MJxmzBnv02HOMmXAkA-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
On Tue, Nov 11, 2014 at 2:16 PM, Dmitry Eremin-Solenikov
<dbaryshkov-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> Just to better understand your suggestions: do you want me to convert
> to regmap only gpio driver or do you suggest to convert all LoCoMo drivers?
Um... I was just thinking about this one usecase.
It's no big deal, the other review comments are more important.
> That is doable, of course, but the amount of changes is overwhelming.
> Also I'm concerned about the performance impact from going through
> regmap layers.
Is it on a critical path? The current locking isn't any less invasive
AFAICT.
Yours,
Linus Walleij
--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH] HID: usbhid: get/put around clearing needs_remote_wakeup
From: Oliver Neukum @ 2014-11-14 9:08 UTC (permalink / raw)
To: Benson Leung; +Cc: johan, jkosina, linux-usb, linux-input, linux-kernel, snanda
In-Reply-To: <1415909806-23848-1-git-send-email-bleung@chromium.org>
On Thu, 2014-11-13 at 12:16 -0800, Benson Leung wrote:
> In usbhid_open, usb_autopm_get_interface is called
> before setting the needs_remote_wakeup flag, and
> usb_autopm_put_interface is called after hid_start_in.
>
> However, when the device is closed in usbhid_close, the same
> protection isn't there when clearing needs_remote_wakeup. This will
> add that to usbhid_close as well as usbhid_stop.
Interesting, but this has the side effect of waking devices
that are asleep just to remove the flag.
Regards
Oliver
--
Oliver Neukum <oneukum@suse.de>
^ permalink raw reply
* [PATCH] driver: input :touchscreen : add Raydium I2C touch driver
From: jeffrey.lin @ 2014-11-14 7:19 UTC (permalink / raw)
To: dmitry.torokhov, rydberg, olof, dianders, benzh, davidriley,
bleung, joe.konno
Cc: jeffrey.lin, roger.yang, KP.li, linux-kernel, linux-input
From: "jeffrey.lin" <jeffrey.lin@rad-ic.com>
this patch is porting Raydium I2C touch driver. Developer can enable
Raydium touch driver by modifying define "CONFIG_TOUCHSCREEN_RM31100"
in config/base.config
Change-Id: Idae54cc4bca17f321a1d0895a8b59680bf9af859
Signed-off-by: jeffrey.lin@rad-ic.com
chromeos: config: add Raydium touch default config
Add to default table and set as not to use
Signed-off-by: jeffrey.lin@rad-ic.com
---
chromeos/config/base.config | 1 +
drivers/input/touchscreen/RM31100.c | 1037 +++++++++++++++++++++++++++++++++++
include/linux/input/RM31100.h | 60 ++
3 files changed, 1098 insertions(+)
create mode 100644 drivers/input/touchscreen/RM31100.c
create mode 100644 include/linux/input/RM31100.h
diff --git a/chromeos/config/base.config b/chromeos/config/base.config
index 59fa689..45b1023 100644
--- a/chromeos/config/base.config
+++ b/chromeos/config/base.config
@@ -1727,6 +1727,7 @@ CONFIG_TOUCHSCREEN_ATMEL_MXT=y
# CONFIG_TOUCHSCREEN_TSC2007 is not set
# CONFIG_TOUCHSCREEN_TSC_SERIO is not set
CONFIG_TOUCHSCREEN_USB_COMPOSITE=m
+# CONFIG_TOUCHSCREEN_RM_TS is not set
# CONFIG_TOUCHSCREEN_WACOM_I2C is not set
# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set
# CONFIG_TPS6105X is not set
diff --git a/drivers/input/touchscreen/RM31100.c b/drivers/input/touchscreen/RM31100.c
new file mode 100644
index 0000000..58eb850
--- /dev/null
+++ b/drivers/input/touchscreen/RM31100.c
@@ -0,0 +1,1037 @@
+/* Source for:
+ * Raydium RM31100 Prototype touchscreen driver.
+ * drivers/input/touchscreen/RM31100.c
+ *
+ * Copyright (C) 2012,
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Raydium reserves the right to make changes without further notice
+ * to the materials described herein. Raydium does not assume any
+ * liability arising out of the application described herein.
+ *
+ * Contact Raydium Semiconductor Corporation at www.rad-ic.com
+ *
+ * History:
+ * (C) 2012 Raydium - Update for GPL distribution
+ * (C) 2009 Enea - Original prototype
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/input/RM31100.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+/*#include <plat/gpio-cfg.h>*/
+#include <linux/miscdevice.h>
+/*#include <asm/uaccess.h> copy_to_user() */
+#include <linux/uaccess.h>
+
+
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+#include <linux/earlysuspend.h>
+
+/* Early-suspend level */
+#define RM31100_TS_SUSPEND_LEVEL 1
+#endif
+
+#define RM31100 0x0
+#define RM3110x 0x1
+
+#define INVALID_DATA 0xff
+
+#define TOUCHSCREEN_TIMEOUT (msecs_to_jiffies(10))
+#define INITIAL_DELAY (msecs_to_jiffies(25000))
+
+#define EVAL_REPORT_RATE 1
+#define ENABLE_DEBUG_MSG 0
+
+#if ENABLE_DEBUG_MSG
+#define DEBUG_PRINT printk
+#else
+#define DEBUG_PRINT(arg...)
+#endif
+
+#define I2C_CLIENT_ADDR 0x39
+#define I2C_DMA_CLIENT_ADDR 0x5A
+#undef CONFIG_PM
+struct RM31100_ts_data {
+ u8 x_index;
+ u8 y_index;
+ u8 z_index;
+ u8 id_index;
+ u8 touch_index;
+ u8 data_reg;
+ u8 status_reg;
+ u8 data_size;
+ u8 touch_bytes;
+ u8 update_data;
+ u8 touch_meta_data;
+ u8 finger_size;
+};
+
+static struct RM31100_ts_data devices[] = {
+ [0] = {
+ .x_index = 2,
+ .y_index = 4,
+ .z_index = 6,
+ .id_index = 1,
+ .data_reg = 0x1,
+ .status_reg = 0,
+ .update_data = 0x0,/*0x4*/
+ .touch_bytes = 6,
+ .touch_meta_data = 1,
+ .finger_size = 70,
+ },
+};
+
+struct RM31100_ts {
+ struct i2c_client *client;
+ struct input_dev *input;
+ struct delayed_work work;
+ struct workqueue_struct *wq;
+ struct RM3110x_ts_platform_data *pdata;
+ struct RM31100_ts_data *dd;
+ u8 *touch_data;
+ u8 device_id;
+ u8 prev_touches;
+ bool is_suspended;
+ bool int_pending;
+ struct mutex sus_lock;
+ struct mutex access_lock;
+ u32 pen_irq;
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+ struct early_suspend early_suspend;
+#endif
+};
+
+struct RM31100_ts *pts;
+
+static inline u16 join_bytes(u8 a, u8 b)
+{
+ u16 ab = 0;
+ ab = ab | a;
+ ab = ab << 8 | b;
+ return ab;
+}
+
+static s32 RM31100_ts_write_reg_u8(struct i2c_client *client, u8 reg, u8 val)
+{
+ s32 data;
+
+ data = i2c_smbus_write_byte_data(client, reg, val);
+ if (data < 0)
+ dev_err(&client->dev, "error %d in writing reg 0x%x\n",
+ data, reg);
+
+ return data;
+}
+
+static s32 RM31100_ts_read_reg_u8(struct i2c_client *client, u8 reg)
+{
+ s32 data;
+
+ data = i2c_smbus_read_byte_data(client, reg);
+ if (data < 0)
+ dev_err(&client->dev, "error %d in reading reg 0x%x\n",
+ data, reg);
+
+ return data;
+}
+
+static int RM31100_ts_read(struct i2c_client *client, u8 reg, u8 *buf, int num)
+{
+ struct i2c_msg xfer_msg[2];
+
+ xfer_msg[0].addr = client->addr;
+ xfer_msg[0].len = 1;
+ xfer_msg[0].flags = 0;
+ xfer_msg[0].buf = ®
+
+ xfer_msg[1].addr = client->addr;
+ xfer_msg[1].len = num;
+ xfer_msg[1].flags = I2C_M_RD;
+ xfer_msg[1].buf = buf;
+
+ return i2c_transfer(client->adapter, xfer_msg, 2);
+}
+
+static int RM31100_ts_write(struct i2c_client *client, u8 *buf, int num)
+{
+ struct i2c_msg xfer_msg[1];
+
+ xfer_msg[0].addr = client->addr;
+ xfer_msg[0].len = num;
+ xfer_msg[0].flags = 0;
+ xfer_msg[0].buf = buf;
+
+ return i2c_transfer(client->adapter, xfer_msg, 1);
+}
+
+static int dev_open(struct inode *inode, struct file *filp)
+{
+ /*printk("***%s***", __func__);*/
+ mutex_lock(&pts->access_lock);
+ return 0;
+}
+
+static int dev_release(struct inode *inode, struct file *filp)
+{
+ /*printk("***%s***", __func__);*/
+ mutex_unlock(&pts->access_lock);
+ return 0;
+}
+
+static ssize_t
+dev_read(struct file *filp, char __user *buf, size_t count, loff_t *pos)
+{
+ u8 *kbuf;
+ struct i2c_msg xfer_msg;
+ /*static char out[] = "1234567890";*/
+ /*static int idx;*//*= 0; remove by checkpatch*/
+ int i;
+
+#if 0
+ DEBUG_PRINT("===%s===", __func__);
+ copy_to_user(buf, &out[idx], count);
+ idx = (idx+3);
+ if (idx >= sizeof(out)) {
+ idx = 0;
+ return 0;
+ }
+ return 3;
+#else
+ kbuf = kmalloc(count, GFP_KERNEL);
+ if (kbuf == NULL)
+ return -ENOMEM;
+
+ /*xfer_msg.addr = pts->client->addr;*/
+ xfer_msg.addr = I2C_CLIENT_ADDR;
+ xfer_msg.len = count;
+ xfer_msg.flags = I2C_M_RD;
+ xfer_msg.buf = kbuf;
+
+ i2c_transfer(pts->client->adapter, &xfer_msg, 1);
+
+ DEBUG_PRINT("dev_read(): count=%zu, data = ", count);
+ for (i = 0; i < count; i++)
+ DEBUG_PRINT("%hhx ", kbuf[i]);
+
+ DEBUG_PRINT("\n");
+
+ if (copy_to_user(buf, kbuf, count) == 0)
+ return count;
+ else
+ return -EFAULT;
+#endif
+}
+
+static ssize_t
+dev_write(struct file *filp, const char __user *buf, size_t count, loff_t *pos)
+{
+ u8 *kbuf;
+ ssize_t status = 0;
+ int i;
+
+ kbuf = kmalloc(count, GFP_KERNEL);
+ if (kbuf == NULL) {
+ DEBUG_PRINT("kmalloc() fail\n");
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(kbuf, buf, count) == 0) {
+ DEBUG_PRINT("dev_write(): count=%zu, data = ", count);
+ for (i = 0; i < count; i++)
+ DEBUG_PRINT("%hhx ", kbuf[i]);
+
+ DEBUG_PRINT("\n");
+ pts->client->addr = I2C_CLIENT_ADDR;
+ if (RM31100_ts_write(pts->client, kbuf, count) < 0)
+ status = -EFAULT;
+ else
+ status = count;
+ } else {
+ DEBUG_PRINT("copy_from_user() fail\n");
+ status = -EFAULT;
+ }
+
+ kfree(kbuf);
+ return status;
+}
+
+static struct file_operations dev_fops = {
+ .owner = THIS_MODULE,
+ .open = dev_open,
+ .release = dev_release,
+ .read = dev_read,
+ .write = dev_write,
+ /*.unlocked_ioctl = dev_ioctl,*/
+};
+
+static struct miscdevice raydium_ts_miscdev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "raydium_ts",
+ .fops = &dev_fops,
+};
+
+
+
+ssize_t show(struct device_driver *drv, char *buff)
+{
+#if 0
+ unsigned char tmp[100];
+
+ /*i2c_master_recv(pts->client, tmp, 1);*/
+ if (RM31100_ts_read(pts->client, 50, tmp, 61) < 0)
+ DEBUG_PRINT("RM31100_ts_read fail\n");
+
+ return snprintf(buf, PAGE_SIZE, "%hhu\n", tmp[0]);
+#else
+ struct i2c_msg xfer_msg;
+ int num = 10;
+ char buf[100];
+ /*int i;*/
+
+ xfer_msg.addr = pts->client->addr;
+ xfer_msg.len = num;
+ xfer_msg.flags = I2C_M_RD;
+ xfer_msg.buf = buf;
+ pts->client->addr = I2C_CLIENT_ADDR;
+ i2c_transfer(pts->client->adapter, &xfer_msg, 1);
+ /*
+ printk("\n");
+ for (i = 0; i < num; i++)
+ {
+ printk("%hhu ", buf[i]);
+ }
+ printk("\n");
+ */
+ return 0;
+#endif
+}
+
+ssize_t store(struct device_driver *drv, const char *buf, size_t count)
+{
+ /*unsigned char pkt[] = { 0xF2, 5, 1, 1 };*/
+ unsigned char pkt[] = { 0xF1, 5, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
+#if 0
+ unsigned char val, ret;
+
+ ret = sscanf(buf, "%hhx", &val);
+/*
+ if (RM31100_ts_write(pts->client, &val, 1) < 0)
+ printk("RM31100_ts_write fail\n");
+*/
+
+ return count;
+#else
+ pts->client->addr = I2C_CLIENT_ADDR;
+ RM31100_ts_write(pts->client, pkt, sizeof(pkt));
+ /*RM31100_ts_write_reg_u8(pts->client, 12, 17);*/
+#endif
+ /*printk("***%s***", __func__);*/
+ return sizeof(pkt);
+}
+
+DRIVER_ATTR(myAttr, 0x777, show, store);
+
+static void report_data(struct RM31100_ts *ts, u16 x, u16 y, u8 pressure, u8 id)
+{
+ if (ts->pdata->swap_xy)
+ swap(x, y);
+
+ /* handle inverting coordinates */
+ if (ts->pdata->invert_x)
+ x = ts->pdata->res_x - x;
+ if (ts->pdata->invert_y)
+ y = ts->pdata->res_y - y;
+
+ input_report_abs(ts->input, ABS_MT_TRACKING_ID, id);
+ input_report_abs(ts->input, ABS_MT_POSITION_X, x);
+ input_report_abs(ts->input, ABS_MT_POSITION_Y, y);
+ input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, pressure);
+ input_report_abs(ts->input, ABS_MT_WIDTH_MAJOR, ts->dd->finger_size);
+ input_mt_sync(ts->input);
+
+ DEBUG_PRINT("%s(): id =%2hhd, x =%4hd, y =%4hd, pressure = %hhd\n",
+ __func__, id, x, y, pressure);
+}
+
+static void process_RM31100_data(struct RM31100_ts *ts)
+{
+ u8 id, pressure, touches, i;
+ u16 x, y;
+
+ touches = ts->touch_data[ts->dd->touch_index];
+
+ if (touches > 0) {
+ for (i = 0; i < touches; i++) {
+ id = ts->touch_data[i * ts->dd->touch_bytes +
+ ts->dd->id_index];
+ pressure = ts->touch_data[i * ts->dd->touch_bytes +
+ ts->dd->z_index];
+ x = join_bytes(ts->touch_data[i * ts->dd->touch_bytes +
+ ts->dd->x_index + 1],
+ ts->touch_data[i * ts->dd->touch_bytes +
+ ts->dd->x_index]);
+ y = join_bytes(ts->touch_data[i * ts->dd->touch_bytes +
+ ts->dd->y_index + 1],
+ ts->touch_data[i * ts->dd->touch_bytes +
+ ts->dd->y_index]);
+ report_data(ts, x, y, pressure, id);
+ }
+ } else
+ input_mt_sync(ts->input);
+
+#if 0
+ for (i = 0; i < ts->prev_touches - (char)touches; i++) {
+ input_report_abs(ts->input, ABS_MT_TOUCH_MAJOR, 0);
+ input_report_abs(ts->input, ABS_MT_WIDTH_MAJOR, 0);
+ input_mt_sync(ts->input);
+ }
+#endif
+
+ ts->prev_touches = touches;
+ input_report_key(ts->input, BTN_TOUCH, 1);
+ input_sync(ts->input);
+}
+
+static void RM31100_ts_xy_worker(struct work_struct *work)
+{
+ int rc;
+ u8 DMAAddress[4];
+ struct RM31100_ts *ts;
+#if EVAL_REPORT_RATE
+ static struct timeval tv_start;
+ struct timeval tv_now;
+ static int cnt = -1;
+ int us;
+#endif /* EVAL_REPORT_RATE*/
+
+
+ ts = container_of(work, struct RM31100_ts,
+ work.work);
+ DEBUG_PRINT("****RM31100_ts_xy_worker******\n");
+ mutex_lock(&ts->sus_lock);
+ if (ts->is_suspended == true) {
+ dev_dbg(&ts->client->dev, "TS is supended\n");
+ ts->int_pending = true;
+ mutex_unlock(&ts->sus_lock);
+ return;
+ }
+ mutex_unlock(&ts->sus_lock);
+
+ mutex_lock(&ts->access_lock);
+ /* read data from DATA_REG */
+ /*RM31100 DMA Mode*/
+#if 1 /*T010 OR w001+T012*/
+ DMAAddress[0] = 0x0F;
+ DMAAddress[1] = 0x00;
+ DMAAddress[2] = 0x20;
+ DMAAddress[3] = 0x81;/* Turn on DMA Mode*/
+ ts->client->addr = I2C_DMA_CLIENT_ADDR;
+ rc = RM31100_ts_write(ts->client, DMAAddress, 0x04);
+ if (rc < 0) {
+ dev_err(&ts->client->dev, "write failed\n");
+ goto schedule;
+ }
+ ts->client->addr = I2C_CLIENT_ADDR;
+ rc = RM31100_ts_read(ts->client, ts->dd->data_reg, ts->touch_data,
+ ts->dd->data_size);
+#endif
+
+ if (rc < 0) {
+ dev_err(&ts->client->dev, "read failed\n");
+ goto schedule;
+ }
+
+ if (ts->touch_data[ts->dd->touch_index] == INVALID_DATA)
+ goto schedule;
+
+ /* write to STATUS_REG to release lock */
+ rc = RM31100_ts_write_reg_u8(ts->client,
+ ts->dd->status_reg, ts->dd->update_data);
+ if (rc < 0) {
+ dev_err(&ts->client->dev, "write failed, try once more\n");
+
+ rc = RM31100_ts_write_reg_u8(ts->client,
+ ts->dd->status_reg, ts->dd->update_data);
+ if (rc < 0)
+ dev_err(&ts->client->dev, "write failed, exiting\n");
+ }
+
+ process_RM31100_data(ts);
+
+#if EVAL_REPORT_RATE
+ cnt++;
+
+ if (cnt == 0)/* first time this function is executed.*/
+ do_gettimeofday(&tv_start);
+ else if (cnt == 100) {
+ do_gettimeofday(&tv_now);
+ us = 1000000 * (tv_now.tv_sec - tv_start.tv_sec)
+ + tv_now.tv_usec - tv_start.tv_usec;
+ tv_start.tv_sec = tv_now.tv_sec;
+ tv_start.tv_usec = tv_now.tv_usec;
+/*printk("Report Rate = %d(100 frames / %d us\n",100000000 / us, us);*/
+ cnt = 0;
+ }
+#endif /* EVAL_REPORT_RATE*/
+schedule:
+
+ mutex_unlock(&ts->access_lock);
+ DEBUG_PRINT("****Leave RM31100_ts_xy_worker******\n");
+ enable_irq(ts->pen_irq);
+}
+
+static irqreturn_t RM31100_ts_irq(int irq, void *dev_id)
+{
+ struct RM31100_ts *ts = dev_id;
+
+ disable_irq_nosync(irq);
+
+ queue_delayed_work(ts->wq, &ts->work, 0);
+
+ return IRQ_HANDLED;
+}
+
+static int RM31100_ts_init_ts(struct i2c_client *client, struct RM31100_ts *ts)
+{
+ struct input_dev *input_device;
+ int rc = 0;
+ /*printk("RM31100_ts_init_ts!!\n");*/
+ ts->dd = &devices[ts->device_id];
+
+ if (!ts->pdata->nfingers) {
+ dev_err(&client->dev, "Touches information not specified\n");
+ return -EINVAL;
+ }
+
+ if (ts->device_id == RM3110x) {
+ if (ts->pdata->nfingers > 2) {
+ dev_err(&client->dev, "Touches >=1 & <= 2\n");
+ return -EINVAL;
+ }
+ ts->dd->data_size = ts->dd->touch_bytes;
+ ts->dd->touch_index = 0x0;
+ } else if (ts->device_id == RM31100) {
+ if (ts->pdata->nfingers > 10) {
+ dev_err(&client->dev, "Touches >=1 & <= 10\n");
+ return -EINVAL;
+ }
+ ts->dd->data_size = ts->pdata->nfingers * ts->dd->touch_bytes +
+ ts->dd->touch_meta_data;
+ ts->dd->touch_index = 0x0;
+ }
+#if 1 /* w001 */
+ else {
+ ts->dd->data_size = ts->pdata->nfingers * ts->dd->touch_bytes +
+ ts->dd->touch_meta_data;
+ ts->dd->touch_index = 0x0;
+ }
+#endif
+ ts->touch_data = kzalloc(ts->dd->data_size, GFP_KERNEL);
+ if (!ts->touch_data) {
+ pr_err("%s: Unable to allocate memory\n", __func__);
+ return -ENOMEM;
+ }
+
+ ts->prev_touches = 0;
+
+ input_device = input_allocate_device();
+ if (!input_device) {
+ rc = -ENOMEM;
+ goto error_alloc_dev;
+ }
+
+ ts->input = input_device;
+ input_device->name = ts->pdata->ts_name;
+ input_device->id.bustype = BUS_I2C;
+ input_device->dev.parent = &client->dev;
+ input_set_drvdata(input_device, ts);
+
+ __set_bit(EV_ABS, input_device->evbit);
+ __set_bit(INPUT_PROP_DIRECT, input_device->propbit);
+ /*__set_bit(EV_SYN, input_device->evbit);*/
+ /*__set_bit(BTN_TOUCH, input_device->keybit);*/
+
+
+ if (ts->device_id == RM31100) {
+ /* set up virtual key */
+ __set_bit(EV_KEY, input_device->evbit);
+ /* set dummy key to make driver work with virtual keys */
+ input_set_capability(input_device, EV_KEY, KEY_PROG1);
+ }
+
+ input_set_abs_params(input_device, ABS_MT_POSITION_X,
+ ts->pdata->dis_min_x, ts->pdata->dis_max_x, 0, 0);
+ input_set_abs_params(input_device, ABS_MT_POSITION_Y,
+ ts->pdata->dis_min_y, ts->pdata->dis_max_y, 0, 0);
+#if 0
+ input_set_abs_params(input_device, ABS_MT_TOUCH_MAJOR,
+ ts->pdata->min_touch, ts->pdata->max_touch, 0, 0);
+ input_set_abs_params(input_device, ABS_MT_WIDTH_MAJOR,
+ ts->pdata->min_width, ts->pdata->max_width, 0, 0);
+#endif
+ input_set_abs_params(input_device, ABS_MT_TRACKING_ID,
+ ts->pdata->min_tid, ts->pdata->max_tid, 0, 0);
+
+ ts->wq = create_singlethread_workqueue("kworkqueue_ts");
+ if (!ts->wq) {
+ dev_err(&client->dev, "Could not create workqueue\n");
+ goto error_wq_create;
+ }
+
+ INIT_DELAYED_WORK(&ts->work, RM31100_ts_xy_worker);
+
+ rc = input_register_device(input_device);
+ if (rc)
+ goto error_unreg_device;
+
+ /*printk("RM31100_init_OKK\n"); */
+ return 0;
+
+error_unreg_device:
+ destroy_workqueue(ts->wq);
+error_wq_create:
+ input_free_device(input_device);
+error_alloc_dev:
+ kfree(ts->touch_data);
+ return rc;
+}
+
+#ifdef CONFIG_PM
+static int RM31100_ts_suspend(struct device *dev)
+{
+ struct RM31100_ts *ts = dev_get_drvdata(dev);
+ int rc = 0;
+ /*printk("****RM31100_ts_suspend******\n"); */
+ if (device_may_wakeup(dev)) {
+ /* mark suspend flag */
+ mutex_lock(&ts->sus_lock);
+ ts->is_suspended = true;
+ mutex_unlock(&ts->sus_lock);
+
+ enable_irq_wake(ts->pen_irq);
+ } else {
+ disable_irq_nosync(ts->pen_irq);
+
+ rc = cancel_delayed_work_sync(&ts->work);
+
+ if (rc) {
+ /* missed the worker, write to STATUS_REG to
+ acknowledge interrupt */
+ rc = RM31100_ts_write_reg_u8(ts->client,
+ ts->dd->status_reg, ts->dd->update_data);
+ if (rc < 0) {
+ dev_err(&ts->client->dev,
+ "write failed, try once more\n");
+
+ rc = RM31100_ts_write_reg_u8(ts->client,
+ ts->dd->status_reg,
+ ts->dd->update_data);
+ if (rc < 0)
+ dev_err(&ts->client->dev,
+ "write failed, exiting\n");
+ }
+
+ enable_irq(ts->pen_irq);
+ }
+
+ gpio_free(ts->pdata->irq_gpio);
+
+ if (ts->pdata->power_on) {
+ rc = ts->pdata->power_on(0);
+ if (rc) {
+ dev_err(dev, "unable to goto suspend\n");
+ return rc;
+ }
+ }
+ }
+ /*printk("****Leave RM31100_ts_suspend******\n"); */
+ return 0;
+}
+
+static int RM31100_ts_resume(struct device *dev)
+{
+ struct RM31100_ts *ts = dev_get_drvdata(dev);
+
+ int rc = 0;
+ /*printk("**** RM31100_ts_resume******\n"); */
+ if (device_may_wakeup(dev)) {
+ disable_irq_wake(ts->pen_irq);
+
+ mutex_lock(&ts->sus_lock);
+ ts->is_suspended = false;
+
+ if (ts->int_pending == true) {
+ ts->int_pending = false;
+
+ /* start a delayed work */
+ queue_delayed_work(ts->wq, &ts->work, 0);
+ }
+ mutex_unlock(&ts->sus_lock);
+
+ } else {
+ if (ts->pdata->power_on) {
+ rc = ts->pdata->power_on(1);
+ if (rc) {
+ dev_err(dev, "unable to resume\n");
+ return rc;
+ }
+ }
+
+ /* configure touchscreen interrupt gpio */
+ rc = gpio_request(ts->pdata->irq_gpio, "RM31100_irq_gpio");
+ if (rc) {
+ pr_err("%s: unable to request gpio %d\n",
+ __func__, ts->pdata->irq_gpio);
+ goto err_power_off;
+ }
+ if (ts->pdata->irq_cfg) {
+ s3c_gpio_cfgpin(ts->pdata->irq_gpio,
+ ts->pdata->irq_cfg);
+ s3c_gpio_setpull(ts->pdata->irq_gpio,
+ S3C_GPIO_PULL_NONE);
+ }
+
+ rc = gpio_direction_input(ts->pdata->irq_gpio);
+ if (rc) {
+ pr_err("%s: unable to set direction for gpio %d\n",
+ __func__, ts->pdata->irq_gpio);
+ goto err_gpio_free;
+ }
+
+ enable_irq(ts->pen_irq);
+
+ /* Clear the status register of the TS controller */
+ rc = RM31100_ts_write_reg_u8(ts->client,
+ ts->dd->status_reg, ts->dd->update_data);
+ if (rc < 0) {
+ dev_err(&ts->client->dev,
+ "write failed, try once more\n");
+
+ rc = RM31100_ts_write_reg_u8(ts->client,
+ ts->dd->status_reg,
+ ts->dd->update_data);
+ if (rc < 0)
+ dev_err(&ts->client->dev,
+ "write failed, exiting\n");
+ }
+ }
+ /*printk("**** Leave RM31100_ts_resume******\n");*/
+ return 0;
+err_gpio_free:
+ gpio_free(ts->pdata->irq_gpio);
+err_power_off:
+ if (ts->pdata->power_on)
+ rc = ts->pdata->power_on(0);
+ return rc;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void RM31100_ts_early_suspend(struct early_suspend *h)
+{
+ struct RM31100_ts *ts =
+ container_of(h, struct RM31100_ts, early_suspend);
+
+ RM31100_ts_suspend(&ts->client->dev);
+}
+
+static void RM31100_ts_late_resume(struct early_suspend *h)
+{
+ struct RM31100_ts *ts = container_of(h,
+ struct RM31100_ts, early_suspend);
+
+ RM31100_ts_resume(&ts->client->dev);
+}
+#endif
+
+static struct dev_pm_ops RM31100_ts_pm_ops = {
+#ifndef CONFIG_HAS_EARLYSUSPEND
+ .suspend = RM31100_ts_suspend,
+ .resume = RM31100_ts_resume,
+#endif
+};
+#endif
+
+/*static int __devinit RM31100_ts_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)*/
+static int __init RM31100_ts_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct RM31100_ts *ts;
+ struct RM3110x_ts_platform_data *pdata = client->dev.platform_data;
+ int rc, temp_reg;
+
+/*printk("RM31100_ts_probe\n"); */
+ if (!pdata) {
+ dev_err(&client->dev, "platform data is required!\n");
+ return -EINVAL;
+ }
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_WORD_DATA)) {
+ dev_err(&client->dev, "I2C functionality not supported\n");
+ return -EIO;
+ }
+
+ ts = kzalloc(sizeof(*ts), GFP_KERNEL);
+ if (!ts)
+ return -ENOMEM;
+ pts = ts;
+
+ /* Enable runtime PM ops, start in ACTIVE mode */
+ rc = pm_runtime_set_active(&client->dev);
+ if (rc < 0)
+ dev_dbg(&client->dev, "unable to set runtime pm state\n");
+ pm_runtime_enable(&client->dev);
+
+ ts->client = client;
+ ts->pdata = pdata;
+ i2c_set_clientdata(client, ts);
+ ts->device_id = id->driver_data;
+
+ if (ts->pdata->dev_setup) {
+ rc = ts->pdata->dev_setup(1);
+ if (rc < 0) {
+ dev_err(&client->dev, "dev setup failed\n");
+ goto error_touch_data_alloc;
+ }
+ }
+
+ /* power on the device */
+ if (ts->pdata->power_on) {
+ rc = ts->pdata->power_on(1);
+ if (rc) {
+ pr_err("%s: Unable to power on the device\n", __func__);
+ goto error_dev_setup;
+ }
+ }
+
+ /* read one byte to make sure i2c device exists */
+ if (id->driver_data == RM3110x)
+ temp_reg = 0x01;
+ else if (id->driver_data == RM31100)
+ temp_reg = 0x00;
+ else
+ temp_reg = 0x05;
+
+/*printk("RM31100 read one byte to make sure i2c device exists\n");*/
+
+ rc = RM31100_ts_read_reg_u8(client, temp_reg);
+ if (rc < 0) {
+ dev_err(&client->dev, "i2c sanity check failed\n");
+ goto error_power_on;
+ }
+
+ ts->is_suspended = false;
+ ts->int_pending = false;
+ mutex_init(&ts->sus_lock);
+ mutex_init(&ts->access_lock);
+
+ rc = RM31100_ts_init_ts(client, ts);
+ if (rc < 0) {
+ dev_err(&client->dev, "RM31100 init failed\n");
+ goto error_mutex_destroy;
+ }
+
+ if (ts->pdata->resout_gpio < 0)
+ goto config_irq_gpio;
+
+ /* configure touchscreen reset out gpio */
+ rc = gpio_request(ts->pdata->resout_gpio, "RM31100_resout_gpio");
+ if (rc) {
+ pr_err("%s: unable to request gpio %d\n",
+ __func__, ts->pdata->resout_gpio);
+ goto error_uninit_ts;
+ }
+
+ rc = gpio_direction_output(ts->pdata->resout_gpio, 0);
+ if (rc) {
+ pr_err("%s: unable to set direction for gpio %d\n",
+ __func__, ts->pdata->resout_gpio);
+ goto error_resout_gpio_dir;
+ }
+ /* reset gpio stabilization time */
+ msleep(20);
+
+config_irq_gpio:
+ /* configure touchscreen interrupt gpio */
+ rc = gpio_request(ts->pdata->irq_gpio, "RM31100_irq_gpio");
+ if (rc) {
+ pr_err("%s: unable to request gpio %d\n",
+ __func__, ts->pdata->irq_gpio);
+ goto error_irq_gpio_req;
+ }
+
+ rc = gpio_direction_input(ts->pdata->irq_gpio);
+ if (rc) {
+ pr_err("%s: unable to set direction for gpio %d\n",
+ __func__, ts->pdata->irq_gpio);
+ goto error_irq_gpio_dir;
+ }
+
+ ts->pen_irq = gpio_to_irq(ts->pdata->irq_gpio);
+ rc = request_irq(ts->pen_irq, RM31100_ts_irq,
+ IRQF_TRIGGER_FALLING,
+ ts->client->dev.driver->name, ts);
+ if (rc) {
+ dev_err(&ts->client->dev, "could not request irq\n");
+ goto error_req_irq_fail;
+ }
+
+ device_init_wakeup(&client->dev, ts->pdata->wakeup);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN +
+ RM31100_TS_SUSPEND_LEVEL;
+ ts->early_suspend.suspend = RM31100_ts_early_suspend;
+ ts->early_suspend.resume = RM31100_ts_late_resume;
+ register_early_suspend(&ts->early_suspend);
+#endif
+
+ rc = misc_register(&raydium_ts_miscdev);
+ if (rc) {
+ dev_err(&ts->client->dev,
+ "Raydium TS: cannot register miscdev:%d\n", rc);
+ goto error_reg_misc_dev;
+ }
+
+
+ return 0;
+error_reg_misc_dev:
+error_req_irq_fail:
+error_irq_gpio_dir:
+ gpio_free(ts->pdata->irq_gpio);
+error_irq_gpio_req:
+error_resout_gpio_dir:
+ if (ts->pdata->resout_gpio >= 0)
+ gpio_free(ts->pdata->resout_gpio);
+error_uninit_ts:
+ destroy_workqueue(ts->wq);
+ input_unregister_device(ts->input);
+ kfree(ts->touch_data);
+error_mutex_destroy:
+ mutex_destroy(&ts->sus_lock);
+ mutex_destroy(&ts->access_lock);
+error_power_on:
+/* if (ts->pdata->power_on)
+ ts->pdata->power_on(0);*/
+error_dev_setup:
+ if (ts->pdata->dev_setup)
+ ts->pdata->dev_setup(0);
+error_touch_data_alloc:
+ pm_runtime_set_suspended(&client->dev);
+ pm_runtime_disable(&client->dev);
+ kfree(ts);
+ return rc;
+}
+
+/*static int __devexit RM31100_ts_remove(struct i2c_client *client)*/
+static int __exit RM31100_ts_remove(struct i2c_client *client)
+{
+ struct RM31100_ts *ts = i2c_get_clientdata(client);
+
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+ unregister_early_suspend(&ts->early_suspend);
+#endif
+ pm_runtime_set_suspended(&client->dev);
+ pm_runtime_disable(&client->dev);
+
+ device_init_wakeup(&client->dev, 0);
+
+ cancel_delayed_work_sync(&ts->work);
+
+ free_irq(ts->pen_irq, ts);
+
+ gpio_free(ts->pdata->irq_gpio);
+
+ if (ts->pdata->resout_gpio >= 0)
+ gpio_free(ts->pdata->resout_gpio);
+
+ destroy_workqueue(ts->wq);
+
+ input_unregister_device(ts->input);
+
+ mutex_destroy(&ts->sus_lock);
+ mutex_destroy(&ts->access_lock);
+
+ if (ts->pdata->power_on)
+ ts->pdata->power_on(0);
+
+ if (ts->pdata->dev_setup)
+ ts->pdata->dev_setup(0);
+
+ kfree(ts->touch_data);
+ kfree(ts);
+
+ return 0;
+}
+
+static const struct i2c_device_id RM31100_ts_id[] = {
+ {"RM31100", RM31100},
+ {"RM3110x", RM3110x},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, RM31100_ts_id);
+
+
+static struct i2c_driver RM31100_ts_driver = {
+ .driver = {
+ .name = "RM31100_ts",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = &RM31100_ts_pm_ops,
+#endif
+ },
+ .probe = RM31100_ts_probe,
+ /*.remove = __devexit_p(RM31100_ts_remove),*/
+ .remove = __exit_p(RM31100_ts_remove),
+ .id_table = RM31100_ts_id,
+};
+
+static int __init RM31100_ts_init(void)
+{
+ int rc;
+ int rc2;
+ /*printk("Raydium I2C Driver Initial!!!!!!!\n");*/
+ rc = i2c_add_driver(&RM31100_ts_driver);
+
+ rc2 = driver_create_file(&RM31100_ts_driver.driver,
+ &driver_attr_myAttr);
+
+ return rc;
+}
+/* Making this as late init to avoid power fluctuations
+ * during LCD initialization.
+ */
+late_initcall(RM31100_ts_init);
+
+static void __exit RM31100_ts_exit(void)
+{
+ return i2c_del_driver(&RM31100_ts_driver);
+}
+module_exit(RM31100_ts_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("RM31100-RM3110x touchscreen controller driver");
+MODULE_AUTHOR("Raydium");
+MODULE_ALIAS("platform:RM31100_ts");
diff --git a/include/linux/input/RM31100.h b/include/linux/input/RM31100.h
new file mode 100644
index 0000000..714a14b
--- /dev/null
+++ b/include/linux/input/RM31100.h
@@ -0,0 +1,60 @@
+/* Header file for:
+ * Raydium RM31100 Prototype touchscreen driver.
+ *
+ * Copyright (C) 2012, Raydium Semiconductor, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2, and only version 2, as published by the
+ * Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Raydium reserves the right to make changes without further notice
+ * to the materials described herein. Raydium does not assume any
+ * liability arising out of the application described herein.
+ *
+ * Contact Raydium Semiconductor at www.rad-ic.com
+ *
+ * History:
+ * (C) 2012 Raydium - Update for GPL distribution
+ * (C) 2009 Enea - Original prototype
+ *
+ */
+#ifndef __RM3110xTS_H__
+#define __RM3110xTS_H__
+
+
+/* RM3110x platform data
+ */
+struct RM3110x_ts_platform_data {
+ int (*power_on)(int on);
+ int (*dev_setup)(bool on);
+ const char *ts_name;
+ u32 dis_min_x; /* display resoltion */
+ u32 dis_max_x;
+ u32 dis_min_y;
+ u32 dis_max_y;
+ u32 min_touch; /* no.of touches supported */
+ u32 max_touch;
+ u32 min_tid; /* track id */
+ u32 max_tid;
+ u32 min_width;/* size of the finger */
+ u32 max_width;
+ u32 res_x; /* TS resolution */
+ u32 res_y;
+ u32 swap_xy;
+ u32 flags;
+ u16 invert_x;
+ u16 invert_y;
+ u8 nfingers;
+ u32 irq_gpio;
+ int resout_gpio;
+ bool wakeup;
+ u32 irq_cfg;
+};
+
+#endif
--
2.1.2
^ permalink raw reply related
* [PATCH v10 01/18] input: cyapa: add device resource management infrastructure support
From: Dudley Du @ 2014-11-14 6:03 UTC (permalink / raw)
To: dmitry.torokhov, rydberg; +Cc: Dudley Du, bleung, linux-input, linux-kernel
In-Reply-To: <1415945004-19868-1-git-send-email-dudl@cypress.com>
This patch modified the code to fix the patch check warning issue with latest
checkpatch.sh tool, and also changed the return variable name from "ret" to
"error" when there is only one error path to follow code style.
Signed-off-by: Dudley Du <dudl@cypress.com>
---
drivers/input/mouse/cyapa.c | 151 ++++++++++++++++++++++----------------------
1 file changed, 75 insertions(+), 76 deletions(-)
diff --git a/drivers/input/mouse/cyapa.c b/drivers/input/mouse/cyapa.c
index b409c3d..c35f398 100644
--- a/drivers/input/mouse/cyapa.c
+++ b/drivers/input/mouse/cyapa.c
@@ -409,11 +409,11 @@ static ssize_t cyapa_read_block(struct cyapa *cyapa, u8 cmd_idx, u8 *values)
cmd = cyapa_smbus_cmds[cmd_idx].cmd;
len = cyapa_smbus_cmds[cmd_idx].len;
return cyapa_smbus_read_block(cyapa, cmd, len, values);
- } else {
- cmd = cyapa_i2c_cmds[cmd_idx].cmd;
- len = cyapa_i2c_cmds[cmd_idx].len;
- return cyapa_i2c_reg_read_block(cyapa, cmd, len, values);
}
+
+ cmd = cyapa_i2c_cmds[cmd_idx].cmd;
+ len = cyapa_i2c_cmds[cmd_idx].len;
+ return cyapa_i2c_reg_read_block(cyapa, cmd, len, values);
}
/*
@@ -422,8 +422,8 @@ static ssize_t cyapa_read_block(struct cyapa *cyapa, u8 cmd_idx, u8 *values)
*/
static int cyapa_get_state(struct cyapa *cyapa)
{
- int ret;
u8 status[BL_STATUS_SIZE];
+ int error;
cyapa->state = CYAPA_STATE_NO_DEVICE;
@@ -433,7 +433,7 @@ static int cyapa_get_state(struct cyapa *cyapa)
* If the device is in operation mode, this will be the DATA regs.
*
*/
- ret = cyapa_i2c_reg_read_block(cyapa, BL_HEAD_OFFSET, BL_STATUS_SIZE,
+ error = cyapa_i2c_reg_read_block(cyapa, BL_HEAD_OFFSET, BL_STATUS_SIZE,
status);
/*
@@ -441,10 +441,10 @@ static int cyapa_get_state(struct cyapa *cyapa)
* -ETIMEDOUT. In this case, try again using the smbus equivalent
* command. This should return a BL_HEAD indicating CYAPA_STATE_OP.
*/
- if (cyapa->smbus && (ret == -ETIMEDOUT || ret == -ENXIO))
- ret = cyapa_read_block(cyapa, CYAPA_CMD_BL_STATUS, status);
+ if (cyapa->smbus && (error == -ETIMEDOUT || error == -ENXIO))
+ error = cyapa_read_block(cyapa, CYAPA_CMD_BL_STATUS, status);
- if (ret != BL_STATUS_SIZE)
+ if (error != BL_STATUS_SIZE)
goto error;
if ((status[REG_OP_STATUS] & OP_STATUS_SRC) == OP_STATUS_SRC) {
@@ -454,7 +454,7 @@ static int cyapa_get_state(struct cyapa *cyapa)
cyapa->state = CYAPA_STATE_OP;
break;
default:
- ret = -EAGAIN;
+ error = -EAGAIN;
goto error;
}
} else {
@@ -468,7 +468,7 @@ static int cyapa_get_state(struct cyapa *cyapa)
return 0;
error:
- return (ret < 0) ? ret : -EAGAIN;
+ return (error < 0) ? error : -EAGAIN;
}
/*
@@ -487,31 +487,31 @@ error:
*/
static int cyapa_poll_state(struct cyapa *cyapa, unsigned int timeout)
{
- int ret;
+ int error;
int tries = timeout / 100;
- ret = cyapa_get_state(cyapa);
- while ((ret || cyapa->state >= CYAPA_STATE_BL_BUSY) && tries--) {
+ error = cyapa_get_state(cyapa);
+ while ((error || cyapa->state >= CYAPA_STATE_BL_BUSY) && tries--) {
msleep(100);
- ret = cyapa_get_state(cyapa);
+ error = cyapa_get_state(cyapa);
}
- return (ret == -EAGAIN || ret == -ETIMEDOUT) ? -ETIMEDOUT : ret;
+ return (error == -EAGAIN || error == -ETIMEDOUT) ? -ETIMEDOUT : error;
}
static int cyapa_bl_deactivate(struct cyapa *cyapa)
{
- int ret;
+ int error;
- ret = cyapa_i2c_reg_write_block(cyapa, 0, sizeof(bl_deactivate),
+ error = cyapa_i2c_reg_write_block(cyapa, 0, sizeof(bl_deactivate),
bl_deactivate);
- if (ret < 0)
- return ret;
+ if (error)
+ return error;
/* wait for bootloader to switch to idle state; should take < 100ms */
msleep(100);
- ret = cyapa_poll_state(cyapa, 500);
- if (ret < 0)
- return ret;
+ error = cyapa_poll_state(cyapa, 500);
+ if (error)
+ return error;
if (cyapa->state != CYAPA_STATE_BL_IDLE)
return -EAGAIN;
return 0;
@@ -532,11 +532,11 @@ static int cyapa_bl_deactivate(struct cyapa *cyapa)
*/
static int cyapa_bl_exit(struct cyapa *cyapa)
{
- int ret;
+ int error;
- ret = cyapa_i2c_reg_write_block(cyapa, 0, sizeof(bl_exit), bl_exit);
- if (ret < 0)
- return ret;
+ error = cyapa_i2c_reg_write_block(cyapa, 0, sizeof(bl_exit), bl_exit);
+ if (error)
+ return error;
/*
* Wait for bootloader to exit, and operation mode to start.
@@ -548,9 +548,9 @@ static int cyapa_bl_exit(struct cyapa *cyapa)
* updated to new firmware, it must first calibrate its sensors, which
* can take up to an additional 2 seconds.
*/
- ret = cyapa_poll_state(cyapa, 2000);
- if (ret < 0)
- return ret;
+ error = cyapa_poll_state(cyapa, 2000);
+ if (error < 0)
+ return error;
if (cyapa->state != CYAPA_STATE_OP)
return -EAGAIN;
@@ -637,28 +637,28 @@ static int cyapa_check_is_operational(struct cyapa *cyapa)
{
struct device *dev = &cyapa->client->dev;
static const char unique_str[] = "CYTRA";
- int ret;
+ int error;
- ret = cyapa_poll_state(cyapa, 2000);
- if (ret < 0)
- return ret;
+ error = cyapa_poll_state(cyapa, 2000);
+ if (error)
+ return error;
switch (cyapa->state) {
case CYAPA_STATE_BL_ACTIVE:
- ret = cyapa_bl_deactivate(cyapa);
- if (ret)
- return ret;
+ error = cyapa_bl_deactivate(cyapa);
+ if (error)
+ return error;
/* Fallthrough state */
case CYAPA_STATE_BL_IDLE:
- ret = cyapa_bl_exit(cyapa);
- if (ret)
- return ret;
+ error = cyapa_bl_exit(cyapa);
+ if (error)
+ return error;
/* Fallthrough state */
case CYAPA_STATE_OP:
- ret = cyapa_get_query_data(cyapa);
- if (ret < 0)
- return ret;
+ error = cyapa_get_query_data(cyapa);
+ if (error)
+ return error;
/* only support firmware protocol gen3 */
if (cyapa->gen != CYAPA_GEN3) {
@@ -756,8 +756,8 @@ static u8 cyapa_check_adapter_functionality(struct i2c_client *client)
static int cyapa_create_input_dev(struct cyapa *cyapa)
{
struct device *dev = &cyapa->client->dev;
- int ret;
struct input_dev *input;
+ int error;
if (!cyapa->physical_size_x || !cyapa->physical_size_y)
return -EINVAL;
@@ -802,17 +802,18 @@ static int cyapa_create_input_dev(struct cyapa *cyapa)
__set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
/* handle pointer emulation and unused slots in core */
- ret = input_mt_init_slots(input, CYAPA_MAX_MT_SLOTS,
+ error = input_mt_init_slots(input, CYAPA_MAX_MT_SLOTS,
INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED);
- if (ret) {
- dev_err(dev, "allocate memory for MT slots failed, %d\n", ret);
+ if (error) {
+ dev_err(dev, "allocate memory for MT slots failed, %d\n",
+ error);
goto err_free_device;
}
/* Register the device in input subsystem */
- ret = input_register_device(input);
- if (ret) {
- dev_err(dev, "input device register failed, %d\n", ret);
+ error = input_register_device(input);
+ if (error) {
+ dev_err(dev, "input device register failed, %d\n", error);
goto err_free_device;
}
return 0;
@@ -820,16 +821,16 @@ static int cyapa_create_input_dev(struct cyapa *cyapa)
err_free_device:
input_free_device(input);
cyapa->input = NULL;
- return ret;
+ return error;
}
static int cyapa_probe(struct i2c_client *client,
const struct i2c_device_id *dev_id)
{
- int ret;
u8 adapter_func;
struct cyapa *cyapa;
struct device *dev = &client->dev;
+ int error;
adapter_func = cyapa_check_adapter_functionality(client);
if (adapter_func == CYAPA_ADAPTER_FUNC_NONE) {
@@ -838,10 +839,8 @@ static int cyapa_probe(struct i2c_client *client,
}
cyapa = kzalloc(sizeof(struct cyapa), GFP_KERNEL);
- if (!cyapa) {
- dev_err(dev, "allocate memory for cyapa failed\n");
+ if (!cyapa)
return -ENOMEM;
- }
cyapa->gen = CYAPA_GEN3;
cyapa->client = client;
@@ -853,33 +852,33 @@ static int cyapa_probe(struct i2c_client *client,
if (adapter_func == CYAPA_ADAPTER_FUNC_SMBUS)
cyapa->smbus = true;
cyapa->state = CYAPA_STATE_NO_DEVICE;
- ret = cyapa_check_is_operational(cyapa);
- if (ret) {
- dev_err(dev, "device not operational, %d\n", ret);
+ error = cyapa_check_is_operational(cyapa);
+ if (error) {
+ dev_err(dev, "device not operational, %d\n", error);
goto err_mem_free;
}
- ret = cyapa_create_input_dev(cyapa);
- if (ret) {
- dev_err(dev, "create input_dev instance failed, %d\n", ret);
+ error = cyapa_create_input_dev(cyapa);
+ if (error) {
+ dev_err(dev, "create input_dev instance failed, %d\n", error);
goto err_mem_free;
}
- ret = cyapa_set_power_mode(cyapa, PWR_MODE_FULL_ACTIVE);
- if (ret) {
- dev_err(dev, "set active power failed, %d\n", ret);
+ error = cyapa_set_power_mode(cyapa, PWR_MODE_FULL_ACTIVE);
+ if (error) {
+ dev_err(dev, "set active power failed, %d\n", error);
goto err_unregister_device;
}
cyapa->irq = client->irq;
- ret = request_threaded_irq(cyapa->irq,
+ error = request_threaded_irq(cyapa->irq,
NULL,
cyapa_irq,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"cyapa",
cyapa);
- if (ret) {
- dev_err(dev, "IRQ request failed: %d\n, ", ret);
+ if (error) {
+ dev_err(dev, "IRQ request failed: %d\n, ", error);
goto err_unregister_device;
}
@@ -890,7 +889,7 @@ err_unregister_device:
err_mem_free:
kfree(cyapa);
- return ret;
+ return error;
}
static int cyapa_remove(struct i2c_client *client)
@@ -908,7 +907,7 @@ static int cyapa_remove(struct i2c_client *client)
#ifdef CONFIG_PM_SLEEP
static int cyapa_suspend(struct device *dev)
{
- int ret;
+ int error;
u8 power_mode;
struct cyapa *cyapa = dev_get_drvdata(dev);
@@ -920,9 +919,9 @@ static int cyapa_suspend(struct device *dev)
*/
power_mode = device_may_wakeup(dev) ? PWR_MODE_IDLE
: PWR_MODE_OFF;
- ret = cyapa_set_power_mode(cyapa, power_mode);
- if (ret < 0)
- dev_err(dev, "set power mode failed, %d\n", ret);
+ error = cyapa_set_power_mode(cyapa, power_mode);
+ if (error)
+ dev_err(dev, "set power mode failed, %d\n", error);
if (device_may_wakeup(dev))
cyapa->irq_wake = (enable_irq_wake(cyapa->irq) == 0);
@@ -931,15 +930,15 @@ static int cyapa_suspend(struct device *dev)
static int cyapa_resume(struct device *dev)
{
- int ret;
+ int error;
struct cyapa *cyapa = dev_get_drvdata(dev);
if (device_may_wakeup(dev) && cyapa->irq_wake)
disable_irq_wake(cyapa->irq);
- ret = cyapa_set_power_mode(cyapa, PWR_MODE_FULL_ACTIVE);
- if (ret)
- dev_warn(dev, "resume active power failed, %d\n", ret);
+ error = cyapa_set_power_mode(cyapa, PWR_MODE_FULL_ACTIVE);
+ if (error)
+ dev_warn(dev, "resume active power failed, %d\n", error);
enable_irq(cyapa->irq);
return 0;
--
1.9.1
^ permalink raw reply related
* [PATCH v10 00/18] input: cyapa: instruction of cyapa patches
From: Dudley Du @ 2014-11-14 6:03 UTC (permalink / raw)
To: dmitry.torokhov, rydberg
Cc: Dudley Du, bleung, dso, linux-input, linux-kernel
V10 patches have below main updates compared with v9 patches:
1) Modify code to following kernel code style.
e.g.: correct to use error as return name when there is only error path,
and fix the checkpatch.sh wanting in the driver.
2) Remove cyapa_remove method and use input open and close interface to
following device resouse management infrastructure.
3) Modify cyapa_detect method to return tristate issue to make the return value
much more consistent and clear.
4) Use platform supplied functions as possible instead of driver
specific rewritten version.
V9 patches have below updates compared with v8 patches:
1) Removed all async thread stuff from the driver.
2) Split driver into 18 patches for each function change one patch.
V8 patches have below updates compared with v7 patches:
1) [PATCH v8 01/13] - Remove the async thread for device detect in
probe routine, now the device detect process is completely done within
the device probe routine.
2) [PATCH v8 01/13] - Split the irq cmd hander function to separated
function cyapa_default_irq_cmd_handler() and set it to interface
cyapa_default_ops.irq_cmd_handler.
3) [PATCH v8 06/13] - Add cyapa->gen check in cyapa_gen3_irq_cmd_handler()
to avoid miss-enter when device protocol is still in detecting.
V7 patches have below updates compared with v6 patches:
1) [PATCH v7 01/13] - Split the irq cmd hander function to separated
function cyapa_default_irq_cmd_handler() and set it to interface
cyapa_default_ops.irq_cmd_handler.
2) [PATCH v7 06/13] - Add cyapa->gen check in cyapa_gen3_irq_cmd_handler()
to avoid miss-enter when device protocol is still in detecting.
V6 patches have below updates compared with v5 patches:
1) Remove patch 14 of the lid filtering from the cyapa driver.
V5 patches have below updates compared with v4 patches:
1) Uses get_device()/put_device() instead of kobject_get()/kobject_put();
2) Fix memories freed before debugfs entries issue;
3) Make cyapa_debugs_root valid in driver module level
in module_init()/moudle_exit() ;
4) Fix i2c_transfer() may return partial transfer issues.
5) Add cyapa->removed flag to avoid detecting thread may still running
when driver module is removed.
6) Fix the meanings of some comments and return error code not clear issue.
This patch set is aimed to re-design the cyapa driver to support
old gen3 trackpad devices and new gen5 trackpad devices in one
cyapa driver, it's for easily productions support based on
customers' requirements. And add sysfs functions and interfaces
supported that required by users and customers.
Since the earlier gen3 and the latest gen5 trackpad devices using
two different chipsets, and have different protocols and interfaces,
so if supported these two type trackpad devices in two different drivers,
then it will be difficult to manage productions and later firmware updates.
e.g.: It will cause customer don't know which one trackpad device firmware
image to use and update when it has been used and integrated
in same one productions, so here we support these two trackpad
devices in same on driver.
The new design cyapa driver contains:
cyapa.c - the core of the re-design, supply interfaces and
functions to system and read trackpad devices.
cyapa.h - header file including macros and data structure definitions.
cyapa_gen3.c - functions support for gen3 trackpad devices,
cyapa_gen5.c - functions support for gen5 trackpad devices.
Beside this introduction patch, it has 18 patches listed as below.
For these patches each one is patched based on previous one.
patch 1/18: add device resource management infrastructure supported.
patch 2/18: re-design cyapa driver with core functions and interface
to support multi-type trackpad devices.
patch 3/18: add gen3 trackpad device basic functions supported into the
re-design cyapa driver.
patch 4/18: add gen5 trackpad device basic functions supported into the
re-design cyapa driver.
patch 5/18: add power management interfaces supported for the deivce.
patch 6/18: add runtime power management interfaces supported for the device.
patch 7/18: add sysfs interfaces supported in the cyapa driver.
Including read firmware version, get production ID, read baseline,
re-calibrate trackpad baselines and do trackpad firmware update.
patch 8/18: add gen3 trackpad device's firmware update function supported.
patch 9/18: add gen3 trackpad device's read baseline function supported.
patch 10/18: add gen3 trackpad device's force re-calibrate function supported.
patch 11/18: add gen5 trackpad device's firmware update function supported.
patch 12/18: add gen5 trackpad device's read baseline function supported.
patch 13/18: add gen5 trackpad device's force re-calibrate function.
patch 14/18: add read firmware image debugfs interface supported
in the cyapa driver.
patch 15/18: add gen3 trackpad device's read firmware image function supported.
patch 16/18: add gen5 trackpad device's read firmware image function supported.
patch 17/18: add read trackpad device's sensors' raw data debugfs interface
supported in the cyapa driver.
patch 18/18: add gen5 trackpad device's read read raw function supported.
^ permalink raw reply
* [PATCH v10 18/18] input: cyapa: add gen5 trackpad device read raw data function support
From: Dudley Du @ 2014-11-14 6:03 UTC (permalink / raw)
To: dmitry.torokhov, rydberg; +Cc: Dudley Du, bleung, linux-input, linux-kernel
In-Reply-To: <1415945004-19868-1-git-send-email-dudl@cypress.com>
Add read raw data function supported for gen5 trackpad device,
it can be used through debugfs raw_data interface.
TEST=test on Chromebooks.
Signed-off-by: Dudley Du <dudl@cypress.com>
---
drivers/input/mouse/cyapa_gen5.c | 138 +++++++++++++++++++++++++++++++++++++++
1 file changed, 138 insertions(+)
diff --git a/drivers/input/mouse/cyapa_gen5.c b/drivers/input/mouse/cyapa_gen5.c
index aee3e57..52c915b 100644
--- a/drivers/input/mouse/cyapa_gen5.c
+++ b/drivers/input/mouse/cyapa_gen5.c
@@ -2363,6 +2363,143 @@ resume_scanning:
return size;
}
+static int cyapa_gen5_read_electrodies_rx_tx(struct cyapa *cyapa)
+{
+ u8 cmd[7] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, 0x71 };
+ u8 resp_data[7];
+ int resp_len;
+ int error;
+
+ resp_len = sizeof(resp_data);
+ error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+ cmd, 7,
+ resp_data, &resp_len,
+ 150, cyapa_gen5_sort_tsg_pip_app_resp_data, true);
+ if (error || !VALID_CMD_RESP_HEADER(resp_data, 0x71) ||
+ !resp_data[5] || !resp_data[6])
+ return -EINVAL;
+
+ cyapa->electrodes_rx = resp_data[6];
+
+ return 0;
+}
+
+static int cyapa_gen5_read_raw_data(struct cyapa *cyapa)
+{
+ int raw_cap_mutual_max, raw_cap_mutual_min, raw_cap_mutual_ave;
+ int raw_cap_self_max, raw_cap_self_min, raw_cap_self_ave;
+ int offset;
+ int data_size, max, min, ave;
+ ktime_t time_mono;
+ int error, resume_error;
+
+ offset = 0;
+ if (!cyapa->tp_raw_data) {
+ if (cyapa->state != CYAPA_STATE_GEN5_APP ||
+ !cyapa->electrodes_x || !cyapa->electrodes_y)
+ return -EINVAL;
+
+ cyapa->tp_raw_data_size = sizeof(s32) * (cyapa->electrodes_x *
+ cyapa->electrodes_y + cyapa->electrodes_x +
+ cyapa->electrodes_y) + GEN5_RAW_DATA_HEAD_SIZE;
+ /*
+ * This buffer will be hold after used until the driver is
+ * unloaded, the purpose of it is to improve the performace
+ * to avoid frequently allocate and release the buffer.
+ */
+ cyapa->tp_raw_data = devm_kzalloc(&cyapa->client->dev,
+ cyapa->tp_raw_data_size, GFP_KERNEL);
+ if (!cyapa->tp_raw_data)
+ return -ENOMEM;
+ }
+
+ /*
+ * 1. Suspend Scanning.
+ *
+ * After suspend scanning, the raw data will not be updated,
+ * so the time of the raw data is before scanning suspended.
+ */
+ time_mono = ktime_get();
+ error = cyapa_gen5_suspend_scanning(cyapa);
+ if (error)
+ return error;
+
+ /* 2. Get the correct electrodes_rx number. */
+ if (cyapa->electrodes_rx == 0) {
+ error = cyapa_gen5_read_electrodies_rx_tx(cyapa);
+ if (error || cyapa->electrodes_rx == 0) {
+ cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
+
+ /*
+ * Since, old firmware doesn't support the command to
+ * read the electrodies' Rx and Tx values, so using
+ * the read global idac interface to get the Rx number,
+ * this value is useful to analyze and
+ * display the raw data map in userspace.
+ */
+ data_size = 0;
+ error = cyapa_gen5_read_idac_data(cyapa,
+ GEN5_CMD_RETRIEVE_DATA_STRUCTURE,
+ GEN5_RETRIEVE_MUTUAL_PWC_DATA,
+ &data_size, &max, &min, &ave);
+ if (error || cyapa->electrodes_rx == 0)
+ goto resume_scanning;
+ }
+ }
+
+ /* 3. Execuate panel scan. It must be executed before read data. */
+ error = cyapa_gen5_execute_panel_scan(cyapa);
+ if (error)
+ goto resume_scanning;
+
+ /* 4. Retrieve panel scan, mutual cap raw data. */
+ offset = GEN5_RAW_DATA_HEAD_SIZE;
+ error = cyapa_gen5_read_panel_scan_raw_data(cyapa,
+ GEN5_CMD_RETRIEVE_PANEL_SCAN,
+ GEN5_PANEL_SCAN_MUTUAL_DIFFCOUNT,
+ cyapa->electrodes_x * cyapa->electrodes_y,
+ &raw_cap_mutual_max, &raw_cap_mutual_min,
+ &raw_cap_mutual_ave,
+ cyapa->tp_raw_data + offset);
+ if (error)
+ goto resume_scanning;
+
+ offset += sizeof(s32) * cyapa->electrodes_x * cyapa->electrodes_y;
+
+ /* 5. Retrieve panel scan, self cap raw data. */
+ error = cyapa_gen5_read_panel_scan_raw_data(cyapa,
+ GEN5_CMD_RETRIEVE_PANEL_SCAN,
+ GEN5_PANEL_SCAN_SELF_DIFFCOUNT,
+ cyapa->electrodes_x + cyapa->electrodes_y,
+ &raw_cap_self_max, &raw_cap_self_min,
+ &raw_cap_self_ave,
+ cyapa->tp_raw_data + offset);
+ if (error)
+ goto resume_scanning;
+
+ offset += sizeof(s32) * (cyapa->electrodes_x + cyapa->electrodes_y);
+
+resume_scanning:
+ /* 6. Resume Scanning*/
+ resume_error = cyapa_gen5_resume_scanning(cyapa);
+ if (resume_error || error)
+ return resume_error ? resume_error : error;
+
+ *((struct timeval *)&cyapa->tp_raw_data[0]) =
+ ktime_to_timeval(time_mono);
+ cyapa->tp_raw_data[16] = (u8)cyapa->electrodes_x;
+ cyapa->tp_raw_data[17] = (u8)cyapa->electrodes_y;
+ cyapa->tp_raw_data[18] = (u8)cyapa->x_origin;
+ cyapa->tp_raw_data[19] = (u8)cyapa->y_origin;
+ cyapa->tp_raw_data[20] = (u8)sizeof(s32);
+ cyapa->tp_raw_data[21] = (u8)sizeof(s32);
+ cyapa->tp_raw_data[22] = (u8)cyapa->electrodes_rx;
+ cyapa->tp_raw_data[23] = 0; /* Reserved. */
+
+ cyapa->tp_raw_data_size = offset;
+ return 0;
+}
+
static bool cyapa_gen5_sort_system_info_data(struct cyapa *cyapa,
u8 *buf, int len)
{
@@ -2776,6 +2913,7 @@ const struct cyapa_dev_ops cyapa_gen5_ops = {
.calibrate_store = cyapa_gen5_do_calibrate,
.read_fw = cyapa_gen5_read_fw,
+ .read_raw_data = cyapa_gen5_read_raw_data,
.initialize = cyapa_gen5_initialize,
--
1.9.1
^ permalink raw reply related
* [PATCH v10 17/18] input: cyapa: add read sensors raw data debugfs interface support
From: Dudley Du @ 2014-11-14 6:03 UTC (permalink / raw)
To: dmitry.torokhov, rydberg; +Cc: Dudley Du, bleung, linux-input, linux-kernel
In-Reply-To: <1415945004-19868-1-git-send-email-dudl@cypress.com>
Add read sensors' raw data from trackpad device interface supported in cyapa
driver through debugfs raw_data interface.
Through this interface, user can read difference count map of each sensors
directly from trackpad device (some customers want). And it's useful to help
users to find out the root cause when there is performance gap happened.
TEST=test on Chromebooks.
Signed-off-by: Dudley Du <dudl@cypress.com>
---
drivers/input/mouse/cyapa.c | 90 +++++++++++++++++++++++++++++++++++++++++++++
drivers/input/mouse/cyapa.h | 4 ++
2 files changed, 94 insertions(+)
diff --git a/drivers/input/mouse/cyapa.c b/drivers/input/mouse/cyapa.c
index 5c07ac0..8523f40 100644
--- a/drivers/input/mouse/cyapa.c
+++ b/drivers/input/mouse/cyapa.c
@@ -34,6 +34,7 @@
#define CYAPA_ADAPTER_FUNC_BOTH 3
#define CYAPA_DEBUGFS_READ_FW "read_fw"
+#define CYAPA_DEBUGFS_RAW_DATA "raw_data"
#define CYAPA_FW_NAME "cyapa.bin"
const char unique_str[] = "CYTRA";
@@ -580,6 +581,91 @@ static const struct file_operations cyapa_read_fw_fops = {
.read = cyapa_debugfs_read_fw
};
+static int cyapa_debugfs_raw_data_open(struct inode *inode, struct file *file)
+{
+ struct cyapa *cyapa = inode->i_private;
+ int error;
+
+ if (!cyapa)
+ return -ENODEV;
+
+ /* Start to be supported after Gen5 trackpad devices. */
+ if (cyapa->gen < CYAPA_GEN5)
+ return -ENOTSUPP;
+
+ error = mutex_lock_interruptible(&cyapa->debugfs_mutex);
+ if (error)
+ return error;
+
+ if (!get_device(&cyapa->client->dev)) {
+ error = -ENODEV;
+ goto out;
+ }
+
+ file->private_data = cyapa;
+out:
+ mutex_unlock(&cyapa->debugfs_mutex);
+ return error;
+}
+
+static int cyapa_debugfs_raw_data_release(struct inode *inode,
+ struct file *file)
+{
+ struct cyapa *cyapa = file->private_data;
+ int error;
+
+ if (!cyapa)
+ return 0;
+
+ error = mutex_lock_interruptible(&cyapa->debugfs_mutex);
+ if (error)
+ return error;
+ file->private_data = NULL;
+ put_device(&cyapa->client->dev);
+ mutex_unlock(&cyapa->debugfs_mutex);
+
+ return 0;
+}
+
+/* Always return the sensors' latest raw data from trackpad device. */
+static ssize_t cyapa_debugfs_read_raw_data(struct file *file,
+ char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ int error;
+ struct cyapa *cyapa = file->private_data;
+
+ error = mutex_lock_interruptible(&cyapa->state_sync_lock);
+ if (error)
+ return error;
+
+ if (cyapa->ops->read_raw_data)
+ error = cyapa->ops->read_raw_data(cyapa);
+ else
+ error = -EPERM;
+ mutex_unlock(&cyapa->state_sync_lock);
+ if (error)
+ return error;
+
+ if (*ppos >= cyapa->tp_raw_data_size)
+ return 0;
+
+ if (count + *ppos > cyapa->tp_raw_data_size)
+ count = cyapa->tp_raw_data_size - *ppos;
+
+ if (copy_to_user(buffer, &cyapa->tp_raw_data[*ppos], count))
+ return -EFAULT;
+
+ *ppos += count;
+ return count;
+}
+
+static const struct file_operations cyapa_read_raw_data_fops = {
+ .open = cyapa_debugfs_raw_data_open,
+ .release = cyapa_debugfs_raw_data_release,
+ .read = cyapa_debugfs_read_raw_data
+};
+
static int cyapa_debugfs_init(struct cyapa *cyapa)
{
struct device *dev = &cyapa->client->dev;
@@ -598,6 +684,10 @@ static int cyapa_debugfs_init(struct cyapa *cyapa)
debugfs_create_file(CYAPA_DEBUGFS_READ_FW, S_IRUSR, cyapa->dentry_dev,
cyapa, &cyapa_read_fw_fops);
+ if (cyapa->gen >= CYAPA_GEN5)
+ debugfs_create_file(CYAPA_DEBUGFS_RAW_DATA, S_IRUSR,
+ cyapa->dentry_dev, cyapa, &cyapa_read_raw_data_fops);
+
return 0;
}
diff --git a/drivers/input/mouse/cyapa.h b/drivers/input/mouse/cyapa.h
index 6a18be3..4f22b38 100644
--- a/drivers/input/mouse/cyapa.h
+++ b/drivers/input/mouse/cyapa.h
@@ -187,6 +187,7 @@ struct cyapa_dev_ops {
struct device_attribute *, const char *, size_t);
int (*read_fw)(struct cyapa *);
+ int (*read_raw_data)(struct cyapa *);
int (*initialize)(struct cyapa *cyapa);
@@ -307,6 +308,9 @@ struct cyapa {
struct cyapa_tsg_bin_image_head fw_img_head;
u8 *fw_image;
size_t fw_image_size;
+ /* Buffer to store sensors' raw data */
+ u8 *tp_raw_data;
+ size_t tp_raw_data_size;
const struct cyapa_dev_ops *ops;
--
1.9.1
^ permalink raw reply related
* [PATCH v10 16/18] input: cyapa: add gen5 trackpad device read firmware image function support
From: Dudley Du @ 2014-11-14 6:03 UTC (permalink / raw)
To: dmitry.torokhov, rydberg; +Cc: Dudley Du, bleung, linux-input, linux-kernel
In-Reply-To: <1415945004-19868-1-git-send-email-dudl@cypress.com>
Add read firmware image function supported for gen5 trackpad device,
it can be used through debugfs read_fw interface.
TEST=test on Chromebooks.
Signed-off-by: Dudley Du <dudl@cypress.com>
---
drivers/input/mouse/cyapa.h | 1 +
drivers/input/mouse/cyapa_gen5.c | 155 +++++++++++++++++++++++++++++++++++++++
2 files changed, 156 insertions(+)
diff --git a/drivers/input/mouse/cyapa.h b/drivers/input/mouse/cyapa.h
index 3d139bc..6a18be3 100644
--- a/drivers/input/mouse/cyapa.h
+++ b/drivers/input/mouse/cyapa.h
@@ -304,6 +304,7 @@ struct cyapa {
/* Buffer to store firmware read using debugfs */
struct mutex debugfs_mutex;
+ struct cyapa_tsg_bin_image_head fw_img_head;
u8 *fw_image;
size_t fw_image_size;
diff --git a/drivers/input/mouse/cyapa_gen5.c b/drivers/input/mouse/cyapa_gen5.c
index 51bdf06..aee3e57 100644
--- a/drivers/input/mouse/cyapa_gen5.c
+++ b/drivers/input/mouse/cyapa_gen5.c
@@ -1210,6 +1210,154 @@ static int cyapa_gen5_write_fw_block(struct cyapa *cyapa,
return 0;
}
+static int cyapa_gen5_read_fw_bytes(struct cyapa *cyapa, u16 row_num, u8 *data)
+{
+ u8 cmd[16];
+ size_t cmd_len;
+ u8 resp_data[CYAPA_TSG_FW_ROW_SIZE / 2 + GEN5_MIN_BL_RESP_LENGTH];
+ int resp_len;
+ u16 offset;
+ u16 cmd_crc;
+ struct cyapa_tsg_bin_image_data_record *fw_img_record;
+ int error;
+
+ fw_img_record = (struct cyapa_tsg_bin_image_data_record *)data;
+
+ cmd[0] = 0x04; /* Register address */
+ cmd[1] = 0x00;
+ cmd[2] = 0x0e;
+ cmd[3] = 0x00;
+ cmd[4] = 0x40; /* Report id 40h */
+ cmd[5] = 0x00;
+ cmd[6] = GEN5_SOP_KEY;
+ cmd[7] = 0x3d; /* Read application image command code */
+ cmd[8] = 0x03;
+ cmd[9] = 0x00;
+ offset = row_num * CYAPA_TSG_FW_ROW_SIZE -
+ CYAPA_TSG_START_OF_APPLICATION;
+ put_unaligned_le16(offset, &cmd[10]);
+ cmd[12] = CYAPA_TSG_IMG_READ_SIZE;
+ cmd_crc = crc_itu_t(0xffff, &cmd[6], 7);
+ put_unaligned_le16(cmd_crc, &cmd[13]); /* CRC[15:0] */
+ cmd[15] = GEN5_EOP_KEY; /* EOP = 17h */
+ cmd_len = 16;
+
+ resp_len = CYAPA_TSG_IMG_READ_SIZE + GEN5_MIN_BL_RESP_LENGTH;
+ error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+ cmd, cmd_len,
+ resp_data, &resp_len,
+ 50, cyapa_gen5_sort_tsg_pip_bl_resp_data, true);
+ if (resp_len != (CYAPA_TSG_IMG_READ_SIZE + GEN5_MIN_BL_RESP_LENGTH) ||
+ error || resp_data[2] != GEN5_BL_RESP_REPORT_ID ||
+ !GEN5_CMD_COMPLETE_SUCCESS(resp_data[5]))
+ return error ? error : -EAGAIN;
+
+ /* Copy first 64 bytes in the row. */
+ memcpy(&fw_img_record->record_data[0], &resp_data[8],
+ CYAPA_TSG_IMG_READ_SIZE);
+
+ if (row_num == CYAPA_TSG_IMG_APP_INTEGRITY_ROW_NUM) {
+ /* Last row's rest 64 bytes are bootloader metadata,
+ * it's not allowed to be read out, will respond with error. */
+ memset(&fw_img_record->record_data[CYAPA_TSG_IMG_READ_SIZE],
+ 0, CYAPA_TSG_IMG_READ_SIZE);
+ goto skip_last_row;
+ }
+
+ /* Read next 64 bytes in the row. */
+ offset = offset + CYAPA_TSG_IMG_READ_SIZE;
+ put_unaligned_le16(offset, &cmd[10]);
+ cmd_crc = crc_itu_t(0xffff, &cmd[6], 7);
+ put_unaligned_le16(cmd_crc, &cmd[13]); /* CRC[15:0] */
+ error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+ cmd, cmd_len,
+ resp_data, &resp_len,
+ 500, cyapa_gen5_sort_tsg_pip_bl_resp_data, true);
+ if (resp_len != (CYAPA_TSG_IMG_READ_SIZE + GEN5_MIN_BL_RESP_LENGTH) ||
+ error || resp_data[2] != GEN5_BL_RESP_REPORT_ID ||
+ !GEN5_CMD_COMPLETE_SUCCESS(resp_data[5]))
+ return error ? error : -EAGAIN;
+
+ /* Copy last 64 bytes in the row. */
+ memcpy(&fw_img_record->record_data[CYAPA_TSG_IMG_READ_SIZE],
+ &resp_data[8], CYAPA_TSG_IMG_READ_SIZE);
+
+skip_last_row:
+ fw_img_record->flash_array_id = 0;
+ put_unaligned_be16(row_num, &fw_img_record->row_number);
+ put_unaligned_be16(CYAPA_TSG_FW_ROW_SIZE, &fw_img_record->record_len);
+
+ return 0;
+}
+
+static int cyapa_gen5_read_fw(struct cyapa *cyapa)
+{
+ int fw_img_head_size;
+ int fw_img_record_size;
+ int fw_img_size;
+ int row_index;
+ int array_index;
+ u32 img_start;
+ u16 img_len;
+ u16 img_start_row;
+ u16 img_end_row;
+ struct cyapa_tsg_bin_image_data_record app_integrity;
+ u8 *record_data;
+ int error;
+
+ error = cyapa_gen5_bl_enter(cyapa);
+ if (error)
+ return error;
+
+ cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
+
+ fw_img_head_size = sizeof(struct cyapa_tsg_bin_image_head);
+ fw_img_record_size = sizeof(struct cyapa_tsg_bin_image_data_record);
+
+ /* Read app integrity block data. */
+ row_index = CYAPA_TSG_IMG_APP_INTEGRITY_ROW_NUM;
+ error = cyapa_gen5_read_fw_bytes(cyapa,
+ row_index, (u8 *)&app_integrity);
+ if (error)
+ return error;
+ img_start = get_unaligned_le32(&app_integrity.record_data[16]);
+ img_len = get_unaligned_le16(&app_integrity.record_data[20]);
+ if ((img_start + img_len) % CYAPA_TSG_FW_ROW_SIZE)
+ return -EINVAL;
+ img_start_row = img_start / CYAPA_TSG_FW_ROW_SIZE;
+ img_end_row = (img_start + img_len) / CYAPA_TSG_FW_ROW_SIZE - 1;
+
+ /* Allocate memory for image. */
+ fw_img_size = fw_img_head_size +
+ (img_end_row - img_start_row + 2) * fw_img_record_size;
+ cyapa->fw_image = cyapa->fw_image ? cyapa->fw_image :
+ devm_kzalloc(&cyapa->client->dev, fw_img_size, GFP_KERNEL);
+ if (!cyapa->fw_image)
+ return -ENOMEM;
+
+ /* Set image head data. */
+ memcpy(cyapa->fw_image, &cyapa->fw_img_head, fw_img_head_size);
+
+ /* Read image blocks. */
+ for (row_index = img_start_row, array_index = 0;
+ row_index <= img_end_row;
+ row_index++, array_index++) {
+ record_data = &cyapa->fw_image[fw_img_head_size +
+ array_index * fw_img_record_size];
+ error = cyapa_gen5_read_fw_bytes(cyapa, row_index, record_data);
+ if (error)
+ return error;
+ }
+
+ /* Append last app integrity block data. */
+ record_data = &cyapa->fw_image[fw_img_head_size +
+ array_index * fw_img_record_size];
+ memcpy(record_data, &app_integrity, fw_img_record_size);
+
+ cyapa->fw_image_size = fw_img_size;
+ return 0;
+}
+
static int cyapa_gen5_do_fw_update(struct cyapa *cyapa,
const struct firmware *fw)
{
@@ -2288,6 +2436,11 @@ static int cyapa_gen5_get_query_data(struct cyapa *cyapa)
if (error || resp_len < sizeof(resp_data))
return error ? error : -EIO;
+ cyapa->fw_img_head.head_size =
+ sizeof(struct cyapa_tsg_bin_image_head) - 1;
+ memcpy(&cyapa->fw_img_head.ttda_driver_major_version,
+ &resp_data[5], cyapa->fw_img_head.head_size);
+
product_family = get_unaligned_le16(&resp_data[7]);
if ((product_family & GEN5_PRODUCT_FAMILY_MASK) !=
GEN5_PRODUCT_FAMILY_TRACKPAD)
@@ -2622,6 +2775,8 @@ const struct cyapa_dev_ops cyapa_gen5_ops = {
.show_baseline = cyapa_gen5_show_baseline,
.calibrate_store = cyapa_gen5_do_calibrate,
+ .read_fw = cyapa_gen5_read_fw,
+
.initialize = cyapa_gen5_initialize,
.state_parse = cyapa_gen5_state_parse,
--
1.9.1
^ permalink raw reply related
* [PATCH v10 15/18] input: cyapa: add gen3 trackpad device read firmware image function support
From: Dudley Du @ 2014-11-14 6:03 UTC (permalink / raw)
To: dmitry.torokhov, rydberg; +Cc: Dudley Du, bleung, linux-input, linux-kernel
In-Reply-To: <1415945004-19868-1-git-send-email-dudl@cypress.com>
Add read firmware image function supported for gen3 trackpad device,
it can be used through debugfs read_fw interface.
TEST=test on Chromebooks.
Signed-off-by: Dudley Du <dudl@cypress.com>
---
drivers/input/mouse/cyapa_gen3.c | 67 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 67 insertions(+)
diff --git a/drivers/input/mouse/cyapa_gen3.c b/drivers/input/mouse/cyapa_gen3.c
index 281209f..98513eb 100644
--- a/drivers/input/mouse/cyapa_gen3.c
+++ b/drivers/input/mouse/cyapa_gen3.c
@@ -708,6 +708,36 @@ static int cyapa_gen3_write_fw_block(struct cyapa *cyapa,
return ret;
}
+/*
+ * A firmware block read command reads 16 bytes of data from flash starting
+ * from a given address. The 12-byte block read command has the format:
+ * <0xff> <CMD> <Key> <Addr>
+ *
+ * <0xff> - every command starts with 0xff
+ * <CMD> - the read command value is 0x3c
+ * <Key> - read commands include an 8-byte key: { 00 01 02 03 04 05 06 07 }
+ * <Addr> - Memory address (16-bit, big-endian)
+ *
+ * The command is followed by an i2c block read to read the 16 bytes of data.
+ */
+static int cyapa_gen3_read_fw_bytes(struct cyapa *cyapa, u16 addr, u8 *data)
+{
+ u8 cmd[] = { 0xff, 0x3c, 0x00, 0x01, 0x02, 0x03, 0x04,
+ 0x05, 0x06, 0x07, addr >> 8, addr };
+ int ret, error;
+
+ error = cyapa_gen3_write_buffer(cyapa, cmd, sizeof(cmd));
+ if (error)
+ return error;
+
+ /* Read data buffer starting from offset 16 */
+ ret = cyapa_i2c_reg_read_block(cyapa, 16, CYAPA_FW_READ_SIZE, data);
+ if (ret != CYAPA_FW_READ_SIZE)
+ return (ret < 0) ? ret : -EIO;
+
+ return 0;
+}
+
static int cyapa_gen3_write_blocks(struct cyapa *cyapa,
size_t start_block, size_t block_count,
const u8 *image_data)
@@ -754,6 +784,41 @@ static int cyapa_gen3_do_fw_update(struct cyapa *cyapa,
return 0;
}
+/*
+ * Read the entire firmware image into ->fw_image.
+ * If the ->fw_image has already been allocated, then this function
+ * doesn't do anything and just returns 0.
+ * If an error occurs while reading the image, ->fw_image is freed, and
+ * the error is returned.
+ *
+ * The firmware is a fixed size (CYAPA_FW_SIZE), and is read out in
+ * fixed length (CYAPA_FW_READ_SIZE) chunks.
+ */
+static int cyapa_gen3_read_fw(struct cyapa *cyapa)
+{
+ int error;
+ int addr;
+
+ error = cyapa_gen3_bl_enter(cyapa);
+ if (error)
+ return error;
+
+ cyapa->fw_image = cyapa->fw_image ? cyapa->fw_image :
+ devm_kzalloc(&cyapa->client->dev, CYAPA_FW_SIZE, GFP_KERNEL);
+ if (!cyapa->fw_image)
+ return -ENOMEM;
+
+ for (addr = 0; addr < CYAPA_FW_SIZE; addr += CYAPA_FW_READ_SIZE) {
+ error = cyapa_gen3_read_fw_bytes(cyapa,
+ CYAPA_FW_HDR_START + addr, &cyapa->fw_image[addr]);
+ if (error)
+ return error;
+ }
+
+ cyapa->fw_image_size = CYAPA_FW_SIZE;
+ return 0;
+}
+
static ssize_t cyapa_gen3_do_calibrate(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
@@ -1195,6 +1260,8 @@ const struct cyapa_dev_ops cyapa_gen3_ops = {
.show_baseline = cyapa_gen3_show_baseline,
.calibrate_store = cyapa_gen3_do_calibrate,
+ .read_fw = cyapa_gen3_read_fw,
+
.state_parse = cyapa_gen3_state_parse,
.operational_check = cyapa_gen3_do_operational_check,
--
1.9.1
^ permalink raw reply related
* [PATCH v10 14/18] input: cyapa: add read firmware image debugfs interface support
From: Dudley Du @ 2014-11-14 6:03 UTC (permalink / raw)
To: dmitry.torokhov, rydberg; +Cc: Dudley Du, bleung, linux-input, linux-kernel
In-Reply-To: <1415945004-19868-1-git-send-email-dudl@cypress.com>
Add read firmware image from trackpad device interface supported in cyapa
driver through debugfs read_fw interface.
Through this interface user can read out, check and backup the firmware image
of the trackpad device before any firmware update, or can use the backed image
to do firmware image recovery.
TEST=test on Chromebooks.
Signed-off-by: Dudley Du <dudl@cypress.com>
---
drivers/input/mouse/cyapa.c | 176 +++++++++++++++++++++++++++++++++++++++++++-
drivers/input/mouse/cyapa.h | 10 +++
2 files changed, 185 insertions(+), 1 deletion(-)
diff --git a/drivers/input/mouse/cyapa.c b/drivers/input/mouse/cyapa.c
index 80a21ed..5c07ac0 100644
--- a/drivers/input/mouse/cyapa.c
+++ b/drivers/input/mouse/cyapa.c
@@ -14,6 +14,7 @@
* more details.
*/
+#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/input.h>
@@ -32,10 +33,14 @@
#define CYAPA_ADAPTER_FUNC_SMBUS 2
#define CYAPA_ADAPTER_FUNC_BOTH 3
+#define CYAPA_DEBUGFS_READ_FW "read_fw"
#define CYAPA_FW_NAME "cyapa.bin"
const char unique_str[] = "CYTRA";
+/* Global root node of the cyapa debugfs directory. */
+static struct dentry *cyapa_debugfs_root;
+
/* Returns 0 on success, else negative errno on failure. */
ssize_t cyapa_i2c_read(struct cyapa *cyapa, u8 reg, size_t len,
u8 *values)
@@ -476,6 +481,135 @@ int cyapa_detect(struct cyapa *cyapa)
}
/*
+ **************************************************************
+ * debugfs interface
+ **************************************************************
+*/
+static int cyapa_debugfs_open(struct inode *inode, struct file *file)
+{
+ struct cyapa *cyapa = inode->i_private;
+ int error;
+
+ if (!cyapa)
+ return -ENODEV;
+
+ error = mutex_lock_interruptible(&cyapa->debugfs_mutex);
+ if (error)
+ return error;
+
+ if (!get_device(&cyapa->client->dev)) {
+ error = -ENODEV;
+ goto out;
+ }
+
+ file->private_data = cyapa;
+
+ if (cyapa->fw_image && cyapa->fw_image_size) {
+ error = 0;
+ goto out;
+ }
+
+ error = mutex_lock_interruptible(&cyapa->state_sync_lock);
+ if (error)
+ goto out;
+ /*
+ * If firmware hasn't been read yet, read it all in one pass.
+ * Subsequent opens will reuse the data in this same buffer.
+ */
+ if (cyapa->ops->read_fw) {
+ error = cyapa->ops->read_fw(cyapa);
+
+ /*
+ * Redetect trackpad device states because read_fw will
+ * reset trackpad device into bootloader mode.
+ */
+ cyapa_detect(cyapa);
+ } else {
+ error = -EPERM;
+ }
+
+ mutex_unlock(&cyapa->state_sync_lock);
+out:
+ mutex_unlock(&cyapa->debugfs_mutex);
+ return error;
+}
+
+static int cyapa_debugfs_release(struct inode *inode, struct file *file)
+{
+ struct cyapa *cyapa = file->private_data;
+ int error;
+
+ if (!cyapa)
+ return 0;
+
+ error = mutex_lock_interruptible(&cyapa->debugfs_mutex);
+ if (error)
+ return error;
+ file->private_data = NULL;
+ put_device(&cyapa->client->dev);
+ mutex_unlock(&cyapa->debugfs_mutex);
+
+ return 0;
+}
+
+/* Return some bytes from the buffered firmware image, starting from *ppos */
+static ssize_t cyapa_debugfs_read_fw(struct file *file, char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct cyapa *cyapa = file->private_data;
+
+ if (!cyapa->fw_image)
+ return -EINVAL;
+
+ if (*ppos >= cyapa->fw_image_size)
+ return 0;
+
+ if (count + *ppos > cyapa->fw_image_size)
+ count = cyapa->fw_image_size - *ppos;
+
+ if (copy_to_user(buffer, &cyapa->fw_image[*ppos], count))
+ return -EFAULT;
+
+ *ppos += count;
+ return count;
+}
+
+static const struct file_operations cyapa_read_fw_fops = {
+ .open = cyapa_debugfs_open,
+ .release = cyapa_debugfs_release,
+ .read = cyapa_debugfs_read_fw
+};
+
+static int cyapa_debugfs_init(struct cyapa *cyapa)
+{
+ struct device *dev = &cyapa->client->dev;
+
+ if (!cyapa_debugfs_root)
+ return -ENODEV;
+
+ cyapa->dentry_dev = debugfs_create_dir(kobject_name(&dev->kobj),
+ cyapa_debugfs_root);
+
+ if (!cyapa->dentry_dev)
+ return -ENODEV;
+
+ mutex_init(&cyapa->debugfs_mutex);
+
+ debugfs_create_file(CYAPA_DEBUGFS_READ_FW, S_IRUSR, cyapa->dentry_dev,
+ cyapa, &cyapa_read_fw_fops);
+
+ return 0;
+}
+
+static void cyapa_remove_debugfs(void *data)
+{
+ struct cyapa *cyapa = data;
+
+ debugfs_remove_recursive(cyapa->dentry_dev);
+ mutex_destroy(&cyapa->debugfs_mutex);
+}
+
+/*
* Sysfs Interface.
*/
@@ -1040,6 +1174,20 @@ static int cyapa_probe(struct i2c_client *client,
return error;
}
+ error = cyapa_debugfs_init(cyapa);
+ if (error) {
+ dev_err(dev, "failed to create debugfs entries: %d\n", error);
+ return error;
+ }
+
+ error = devm_add_action(dev, cyapa_remove_debugfs, cyapa);
+ if (error) {
+ cyapa_remove_debugfs(cyapa);
+ dev_err(dev, "failed to add debugfs cleanup action :%d\n",
+ error);
+ return error;
+ }
+
#ifdef CONFIG_PM_SLEEP
if (device_can_wakeup(dev)) {
error = sysfs_merge_group(&client->dev.kobj,
@@ -1218,7 +1366,33 @@ static struct i2c_driver cyapa_driver = {
.id_table = cyapa_id_table,
};
-module_i2c_driver(cyapa_driver);
+static int __init cyapa_init(void)
+{
+ int error;
+
+ /* Create a global debugfs root for all cyapa devices */
+ cyapa_debugfs_root = debugfs_create_dir("cyapa", NULL);
+ if (cyapa_debugfs_root == ERR_PTR(-ENODEV))
+ cyapa_debugfs_root = NULL;
+
+ error = i2c_add_driver(&cyapa_driver);
+ if (error) {
+ pr_err("cyapa driver register FAILED.\n");
+ return error;
+ }
+
+ return 0;
+}
+
+static void __exit cyapa_exit(void)
+{
+ debugfs_remove_recursive(cyapa_debugfs_root);
+
+ i2c_del_driver(&cyapa_driver);
+}
+
+module_init(cyapa_init);
+module_exit(cyapa_exit);
MODULE_DESCRIPTION("Cypress APA I2C Trackpad Driver");
MODULE_AUTHOR("Dudley Du <dudl@cypress.com>");
diff --git a/drivers/input/mouse/cyapa.h b/drivers/input/mouse/cyapa.h
index 1420b7e..3d139bc 100644
--- a/drivers/input/mouse/cyapa.h
+++ b/drivers/input/mouse/cyapa.h
@@ -186,6 +186,8 @@ struct cyapa_dev_ops {
ssize_t (*calibrate_store)(struct device *,
struct device_attribute *, const char *, size_t);
+ int (*read_fw)(struct cyapa *);
+
int (*initialize)(struct cyapa *cyapa);
int (*state_parse)(struct cyapa *cyapa, u8 *reg_status, int len);
@@ -297,6 +299,14 @@ struct cyapa {
*/
struct mutex state_sync_lock;
+ /* Per-instance debugfs root */
+ struct dentry *dentry_dev;
+
+ /* Buffer to store firmware read using debugfs */
+ struct mutex debugfs_mutex;
+ u8 *fw_image;
+ size_t fw_image_size;
+
const struct cyapa_dev_ops *ops;
union cyapa_cmd_states cmd_states;
--
1.9.1
^ permalink raw reply related
* [PATCH v10 13/18] input: cyapa: add gen5 trackpad device force re-calibrate function support
From: Dudley Du @ 2014-11-14 6:03 UTC (permalink / raw)
To: dmitry.torokhov, rydberg; +Cc: Dudley Du, bleung, linux-input, linux-kernel
In-Reply-To: <1415945004-19868-1-git-send-email-dudl@cypress.com>
Add force re-calibrate function supported for gen5 trackpad device,
it can be used through sysfs calibrate interface.
TEST=test on Chromebooks.
Signed-off-by: Dudley Du <dudl@cypress.com>
---
drivers/input/mouse/cyapa_gen5.c | 65 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 65 insertions(+)
diff --git a/drivers/input/mouse/cyapa_gen5.c b/drivers/input/mouse/cyapa_gen5.c
index b401b2a..51bdf06 100644
--- a/drivers/input/mouse/cyapa_gen5.c
+++ b/drivers/input/mouse/cyapa_gen5.c
@@ -1580,6 +1580,70 @@ static int cyapa_gen5_suspend_scanning(struct cyapa *cyapa)
return 0;
}
+static int cyapa_gen5_calibrate_pwcs(struct cyapa *cyapa,
+ u8 calibrate_sensing_mode_type)
+{
+ u8 cmd[8];
+ u8 resp_data[6];
+ int resp_len;
+ int error;
+
+ /* Try to dump all bufferred data before doing command. */
+ cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
+
+ cmd[0] = 0x04;
+ cmd[1] = 0x00;
+ cmd[2] = 0x06;
+ cmd[3] = 0x00;
+ cmd[4] = GEN5_APP_CMD_REPORT_ID;
+ cmd[5] = 0x00;
+ cmd[6] = GEN5_CMD_CALIBRATE;
+ cmd[7] = calibrate_sensing_mode_type;
+ resp_len = sizeof(resp_data);
+ error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+ cmd, sizeof(cmd),
+ resp_data, &resp_len,
+ 5000, cyapa_gen5_sort_tsg_pip_app_resp_data, true);
+ if (error || !VALID_CMD_RESP_HEADER(resp_data, GEN5_CMD_CALIBRATE) ||
+ !GEN5_CMD_COMPLETE_SUCCESS(resp_data[5]))
+ return error < 0 ? error : -EAGAIN;
+
+ return 0;
+}
+
+static ssize_t cyapa_gen5_do_calibrate(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct cyapa *cyapa = dev_get_drvdata(dev);
+ int error, calibrate_error;
+
+ /* 1. Suspend Scanning*/
+ error = cyapa_gen5_suspend_scanning(cyapa);
+ if (error)
+ return error;
+
+ /* 2. Do mutual capacitance fine calibrate. */
+ calibrate_error = cyapa_gen5_calibrate_pwcs(cyapa,
+ CYAPA_SENSING_MODE_MUTUAL_CAP_FINE);
+ if (calibrate_error)
+ goto resume_scanning;
+
+ /* 3. Do self capacitance calibrate. */
+ calibrate_error = cyapa_gen5_calibrate_pwcs(cyapa,
+ CYAPA_SENSING_MODE_SELF_CAP);
+ if (calibrate_error)
+ goto resume_scanning;
+
+resume_scanning:
+ /* 4. Resume Scanning*/
+ error = cyapa_gen5_resume_scanning(cyapa);
+ if (error || calibrate_error)
+ return error ? error : calibrate_error;
+
+ return count;
+}
+
static s32 two_complement_to_s32(s32 value, int num_bits)
{
if (value >> (num_bits - 1))
@@ -2556,6 +2620,7 @@ const struct cyapa_dev_ops cyapa_gen5_ops = {
.update_fw = cyapa_gen5_do_fw_update,
.show_baseline = cyapa_gen5_show_baseline,
+ .calibrate_store = cyapa_gen5_do_calibrate,
.initialize = cyapa_gen5_initialize,
--
1.9.1
^ permalink raw reply related
* [PATCH v10 12/18] input: cyapa: add gen5 trackpad device read baseline function support
From: Dudley Du @ 2014-11-14 6:03 UTC (permalink / raw)
To: dmitry.torokhov, rydberg; +Cc: Dudley Du, bleung, linux-input, linux-kernel
In-Reply-To: <1415945004-19868-1-git-send-email-dudl@cypress.com>
Add read baseline function supported for gen5 trackpad device,
it can be used through sysfs baseline interface.
TEST=test on Chromebooks.
Signed-off-by: Dudley Du <dudl@cypress.com>
---
drivers/input/mouse/cyapa.h | 2 +
drivers/input/mouse/cyapa_gen5.c | 621 +++++++++++++++++++++++++++++++++++++++
2 files changed, 623 insertions(+)
diff --git a/drivers/input/mouse/cyapa.h b/drivers/input/mouse/cyapa.h
index 2dc29c1..1420b7e 100644
--- a/drivers/input/mouse/cyapa.h
+++ b/drivers/input/mouse/cyapa.h
@@ -285,6 +285,8 @@ struct cyapa {
u8 y_origin; /* Y Axis Origin: 0 = top; 1 = bottom. */
int electrodes_x; /* Number of electrodes on the X Axis*/
int electrodes_y; /* Number of electrodes on the Y Axis*/
+ int electrodes_rx; /* Number of Rx electrodes */
+ int algined_electrodes_rx; /* 4 aligned */
int max_z;
/*
diff --git a/drivers/input/mouse/cyapa_gen5.c b/drivers/input/mouse/cyapa_gen5.c
index f8f5b15..b401b2a 100644
--- a/drivers/input/mouse/cyapa_gen5.c
+++ b/drivers/input/mouse/cyapa_gen5.c
@@ -1532,6 +1532,625 @@ static int cyapa_gen5_set_power_mode(struct cyapa *cyapa,
return 0;
}
+static int cyapa_gen5_resume_scanning(struct cyapa *cyapa)
+{
+ u8 cmd[7] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, 0x04 };
+ u8 resp_data[6];
+ int resp_len;
+ int error;
+
+ /* Try to dump all bufferred data before doing command. */
+ cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
+
+ resp_len = 6;
+ error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+ cmd, 7,
+ resp_data, &resp_len,
+ 500, cyapa_gen5_sort_tsg_pip_app_resp_data, true);
+ if (error || !VALID_CMD_RESP_HEADER(resp_data, 0x04))
+ return -EINVAL;
+
+ /* Try to dump all bufferred data when resuming scanning. */
+ cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
+
+ return 0;
+}
+
+static int cyapa_gen5_suspend_scanning(struct cyapa *cyapa)
+{
+ u8 cmd[7] = { 0x04, 0x00, 0x05, 0x00, 0x2f, 0x00, 0x03 };
+ u8 resp_data[6];
+ int resp_len;
+ int error;
+
+ /* Try to dump all bufferred data before doing command. */
+ cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
+
+ resp_len = 6;
+ error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+ cmd, 7,
+ resp_data, &resp_len,
+ 500, cyapa_gen5_sort_tsg_pip_app_resp_data, true);
+ if (error || !VALID_CMD_RESP_HEADER(resp_data, 0x03))
+ return -EINVAL;
+
+ /* Try to dump all bufferred data when suspending scanning. */
+ cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
+
+ return 0;
+}
+
+static s32 two_complement_to_s32(s32 value, int num_bits)
+{
+ if (value >> (num_bits - 1))
+ value |= -1 << num_bits;
+ return value;
+}
+
+static s32 cyapa_parse_structure_data(u8 data_format, u8 *buf, int buf_len)
+{
+ int data_size;
+ bool big_endian;
+ bool unsigned_type;
+ s32 value;
+
+ data_size = (data_format & 0x07);
+ big_endian = ((data_format & 0x10) == 0x00);
+ unsigned_type = ((data_format & 0x20) == 0x00);
+
+ if (buf_len < data_size)
+ return 0;
+
+ switch (data_size) {
+ case 1:
+ value = buf[0];
+ break;
+ case 2:
+ if (big_endian)
+ value = get_unaligned_be16(buf);
+ else
+ value = get_unaligned_le16(buf);
+ break;
+ case 4:
+ if (big_endian)
+ value = get_unaligned_be32(buf);
+ else
+ value = get_unaligned_le32(buf);
+ break;
+ default:
+ /* Should not happen, just as default case here. */
+ value = 0;
+ break;
+ }
+
+ if (!unsigned_type)
+ value = two_complement_to_s32(value, data_size * 8);
+
+ return value;
+}
+
+
+/*
+ * Read all the global mutual or self idac data or mutual or self local PWC
+ * data based on the @idac_data_type.
+ * If the input value of @data_size is 0, then means read global mutual or
+ * self idac data. For read global mutual idac data, @idac_max, @idac_min and
+ * @idac_ave are in order used to return the max value of global mutual idac
+ * data, the min value of global mutual idac and the average value of the
+ * global mutual idac data. For read global self idac data, @idac_max is used
+ * to return the global self cap idac data in Rx direction, @idac_min is used
+ * to return the global self cap idac data in Tx direction. @idac_ave is not
+ * used.
+ * If the input value of @data_size is not 0, than means read the mutual or
+ * self local PWC data. The @idac_max, @idac_min and @idac_ave are used to
+ * return the max, min and average value of the mutual or self local PWC data.
+ * Note, in order to raed mutual local PWC data, must read invoke this function
+ * to read the mutual global idac data firstly to set the correct Rx number
+ * value, otherwise, the read mutual idac and PWC data may not correct.
+ */
+static int cyapa_gen5_read_idac_data(struct cyapa *cyapa,
+ u8 cmd_code, u8 idac_data_type, int *data_size,
+ int *idac_max, int *idac_min, int *idac_ave)
+{
+ int i;
+ u8 cmd[12];
+ u8 resp_data[256];
+ int resp_len;
+ int read_len;
+ int value;
+ u16 offset;
+ int read_elements;
+ bool read_global_idac;
+ int sum, count, max_element_cnt;
+ int tmp_max, tmp_min, tmp_ave, tmp_sum, tmp_count;
+ int electrodes_rx, electrodes_tx;
+ int error;
+
+ if (cmd_code != GEN5_CMD_RETRIEVE_DATA_STRUCTURE ||
+ (idac_data_type != GEN5_RETRIEVE_MUTUAL_PWC_DATA &&
+ idac_data_type != GEN5_RETRIEVE_SELF_CAP_PWC_DATA) ||
+ !data_size || !idac_max || !idac_min || !idac_ave)
+ return -EINVAL;
+
+ *idac_max = INT_MIN;
+ *idac_min = INT_MAX;
+ sum = count = tmp_count = 0;
+ electrodes_rx = electrodes_tx = 0;
+ if (*data_size == 0) {
+ /*
+ * Read global idac values firstly.
+ * Currently, no idac data exceed 4 bytes.
+ */
+ read_global_idac = true;
+ offset = 0;
+ *data_size = 4;
+ tmp_max = INT_MIN;
+ tmp_min = INT_MAX;
+ tmp_ave = tmp_sum = tmp_count = 0;
+
+ if (idac_data_type == GEN5_RETRIEVE_MUTUAL_PWC_DATA) {
+ if (cyapa->algined_electrodes_rx == 0) {
+ if (cyapa->electrodes_rx != 0) {
+ electrodes_rx = cyapa->electrodes_rx;
+ electrodes_tx = (cyapa->electrodes_x ==
+ electrodes_rx) ?
+ cyapa->electrodes_y :
+ cyapa->electrodes_x;
+ } else {
+ electrodes_tx = min(cyapa->electrodes_x,
+ cyapa->electrodes_y);
+ electrodes_rx = max(cyapa->electrodes_x,
+ cyapa->electrodes_y);
+ }
+ cyapa->algined_electrodes_rx =
+ (electrodes_rx + 3) & ~3u;
+ }
+ max_element_cnt =
+ (cyapa->algined_electrodes_rx + 7) & ~7u;
+ } else {
+ max_element_cnt = 2;
+ }
+ } else {
+ read_global_idac = false;
+ if (*data_size > 4)
+ *data_size = 4;
+ /* Calculate the start offset in bytes of local PWC data. */
+ if (idac_data_type == GEN5_RETRIEVE_MUTUAL_PWC_DATA) {
+ offset = cyapa->algined_electrodes_rx * (*data_size);
+ electrodes_tx =
+ (cyapa->electrodes_rx == cyapa->electrodes_x) ?
+ cyapa->electrodes_y : cyapa->electrodes_x;
+ max_element_cnt = ((cyapa->algined_electrodes_rx + 7) &
+ ~7u) * electrodes_tx;
+ } else if (idac_data_type == GEN5_RETRIEVE_SELF_CAP_PWC_DATA) {
+ offset = 2;
+ max_element_cnt = cyapa->electrodes_x +
+ cyapa->electrodes_y;
+ max_element_cnt = (max_element_cnt + 3) & ~3u;
+ }
+ }
+
+ do {
+ read_elements = (256 - 10) / (*data_size);
+ read_elements = min(read_elements, max_element_cnt - count);
+ read_len = read_elements * (*data_size);
+
+ cmd[0] = 0x04;
+ cmd[1] = 0x00;
+ cmd[2] = 0x0a;
+ cmd[3] = 0x00;
+ cmd[4] = GEN5_APP_CMD_REPORT_ID;
+ cmd[5] = 0x00;
+ cmd[6] = cmd_code;
+ put_unaligned_le16(offset, &cmd[7]); /* Read Offset[15:0] */
+ put_unaligned_le16(read_len, &cmd[9]); /* Read Length[15:0] */
+ cmd[11] = idac_data_type;
+ resp_len = 10 + read_len;
+ error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+ cmd, 12,
+ resp_data, &resp_len,
+ 500, cyapa_gen5_sort_tsg_pip_app_resp_data,
+ true);
+ if (error || resp_len < 10 ||
+ !VALID_CMD_RESP_HEADER(resp_data, cmd_code) ||
+ !GEN5_CMD_COMPLETE_SUCCESS(resp_data[5]) ||
+ resp_data[6] != idac_data_type)
+ return (error < 0) ? error : -EAGAIN;
+ read_len = get_unaligned_le16(&resp_data[7]);
+ if (read_len == 0)
+ break;
+
+ *data_size = (resp_data[9] & GEN5_PWC_DATA_ELEMENT_SIZE_MASK);
+ if (read_len < *data_size)
+ return -EINVAL;
+
+ if (read_global_idac &&
+ idac_data_type == GEN5_RETRIEVE_SELF_CAP_PWC_DATA) {
+ /* Rx's self global idac data. */
+ *idac_max = cyapa_parse_structure_data(
+ resp_data[9], &resp_data[10],
+ *data_size);
+ /* Tx's self global idac data. */
+ *idac_min = cyapa_parse_structure_data(
+ resp_data[9],
+ &resp_data[10 + *data_size],
+ *data_size);
+ break;
+ }
+
+ /* Read mutual global idac or local mutual/self PWC data. */
+ offset += read_len;
+ for (i = 10; i < (read_len + 10); i += *data_size) {
+ value = cyapa_parse_structure_data(resp_data[9],
+ &resp_data[i], *data_size);
+ *idac_min = min(value, *idac_min);
+ *idac_max = max(value, *idac_max);
+
+ if (idac_data_type == GEN5_RETRIEVE_MUTUAL_PWC_DATA &&
+ tmp_count < cyapa->algined_electrodes_rx &&
+ read_global_idac) {
+ /*
+ * The value gap betwen global and local mutual
+ * idac data must bigger than 50%.
+ * Normally, global value bigger than 50,
+ * local values less than 10.
+ */
+ if (!tmp_ave || value > tmp_ave / 2) {
+ tmp_min = min(value, tmp_min);
+ tmp_max = max(value, tmp_max);
+ tmp_sum += value;
+ tmp_count++;
+
+ tmp_ave = tmp_sum / tmp_count;
+ }
+ }
+
+ sum += value;
+ count++;
+
+ if (count >= max_element_cnt)
+ goto out;
+ }
+ } while (true);
+
+out:
+ *idac_ave = count ? (sum / count) : 0;
+
+ if (read_global_idac &&
+ idac_data_type == GEN5_RETRIEVE_MUTUAL_PWC_DATA) {
+ if (tmp_count == 0)
+ return 0;
+
+ if (tmp_count == cyapa->algined_electrodes_rx) {
+ cyapa->electrodes_rx = cyapa->electrodes_rx ?
+ cyapa->electrodes_rx : electrodes_rx;
+ } else if (tmp_count == electrodes_rx) {
+ cyapa->electrodes_rx = cyapa->electrodes_rx ?
+ cyapa->electrodes_rx : electrodes_rx;
+ cyapa->algined_electrodes_rx = electrodes_rx;
+ } else {
+ cyapa->electrodes_rx = cyapa->electrodes_rx ?
+ cyapa->electrodes_rx : electrodes_tx;
+ cyapa->algined_electrodes_rx = tmp_count;
+ }
+
+ *idac_min = tmp_min;
+ *idac_max = tmp_max;
+ *idac_ave = tmp_ave;
+ }
+
+ return 0;
+}
+
+static int cyapa_gen5_read_mutual_idac_data(struct cyapa *cyapa,
+ int *gidac_mutual_max, int *gidac_mutual_min, int *gidac_mutual_ave,
+ int *lidac_mutual_max, int *lidac_mutual_min, int *lidac_mutual_ave)
+{
+ int error;
+ int data_size;
+
+ *gidac_mutual_max = *gidac_mutual_min = *gidac_mutual_ave = 0;
+ *lidac_mutual_max = *lidac_mutual_min = *lidac_mutual_ave = 0;
+
+ data_size = 0;
+ error = cyapa_gen5_read_idac_data(cyapa,
+ GEN5_CMD_RETRIEVE_DATA_STRUCTURE,
+ GEN5_RETRIEVE_MUTUAL_PWC_DATA,
+ &data_size,
+ gidac_mutual_max, gidac_mutual_min, gidac_mutual_ave);
+ if (error)
+ return error;
+
+ error = cyapa_gen5_read_idac_data(cyapa,
+ GEN5_CMD_RETRIEVE_DATA_STRUCTURE,
+ GEN5_RETRIEVE_MUTUAL_PWC_DATA,
+ &data_size,
+ lidac_mutual_max, lidac_mutual_min, lidac_mutual_ave);
+ return error;
+}
+
+static int cyapa_gen5_read_self_idac_data(struct cyapa *cyapa,
+ int *gidac_self_rx, int *gidac_self_tx,
+ int *lidac_self_max, int *lidac_self_min, int *lidac_self_ave)
+{
+ int error;
+ int data_size;
+
+ *gidac_self_rx = *gidac_self_tx = 0;
+ *lidac_self_max = *lidac_self_min = *lidac_self_ave = 0;
+
+ data_size = 0;
+ error = cyapa_gen5_read_idac_data(cyapa,
+ GEN5_CMD_RETRIEVE_DATA_STRUCTURE,
+ GEN5_RETRIEVE_SELF_CAP_PWC_DATA,
+ &data_size,
+ lidac_self_max, lidac_self_min, lidac_self_ave);
+ if (error)
+ return error;
+ *gidac_self_rx = *lidac_self_max;
+ *gidac_self_tx = *lidac_self_min;
+
+ error = cyapa_gen5_read_idac_data(cyapa,
+ GEN5_CMD_RETRIEVE_DATA_STRUCTURE,
+ GEN5_RETRIEVE_SELF_CAP_PWC_DATA,
+ &data_size,
+ lidac_self_max, lidac_self_min, lidac_self_ave);
+ return error;
+}
+
+static ssize_t cyapa_gen5_execute_panel_scan(struct cyapa *cyapa)
+{
+ int error;
+ u8 cmd[7];
+ u8 resp_data[6];
+ int resp_len;
+
+ cmd[0] = 0x04;
+ cmd[1] = 0x00;
+ cmd[2] = 0x05;
+ cmd[3] = 0x00;
+ cmd[4] = GEN5_APP_CMD_REPORT_ID;
+ cmd[5] = 0x00;
+ cmd[6] = GEN5_CMD_EXECUTE_PANEL_SCAN; /* Command code */
+ resp_len = 6;
+ error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+ cmd, 7,
+ resp_data, &resp_len,
+ 500, cyapa_gen5_sort_tsg_pip_app_resp_data, true);
+ if (error || resp_len != 6 ||
+ !VALID_CMD_RESP_HEADER(resp_data,
+ GEN5_CMD_EXECUTE_PANEL_SCAN) ||
+ !GEN5_CMD_COMPLETE_SUCCESS(resp_data[5]))
+ return error ? error : -EAGAIN;
+
+ return 0;
+}
+
+static int cyapa_gen5_read_panel_scan_raw_data(struct cyapa *cyapa,
+ u8 cmd_code, u8 raw_data_type, int raw_data_max_num,
+ int *raw_data_max, int *raw_data_min, int *raw_data_ave,
+ u8 *buffer)
+{
+ int i;
+ u8 cmd[12];
+ u8 resp_data[256]; /* Max bytes can transfer one time. */
+ int resp_len;
+ int read_elements;
+ int read_len;
+ u16 offset;
+ s32 value;
+ int sum, count;
+ int data_size;
+ s32 *intp;
+ int error;
+
+ if (cmd_code != GEN5_CMD_RETRIEVE_PANEL_SCAN ||
+ (raw_data_type > GEN5_PANEL_SCAN_SELF_DIFFCOUNT) ||
+ !raw_data_max || !raw_data_min || !raw_data_ave)
+ return -EINVAL;
+
+ intp = (s32 *)buffer;
+ *raw_data_max = INT_MIN;
+ *raw_data_min = INT_MAX;
+ sum = count = 0;
+ offset = 0;
+ read_elements = (256 - 10) / 4; /* Currently, max element size is 4. */
+ read_len = read_elements * 4;
+ do {
+ cmd[0] = 0x04;
+ cmd[1] = 0x00;
+ cmd[2] = 0x0a;
+ cmd[3] = 0x00;
+ cmd[4] = GEN5_APP_CMD_REPORT_ID;
+ cmd[5] = 0x00;
+ cmd[6] = cmd_code; /* Command code */
+ put_unaligned_le16(offset, &cmd[7]);
+ put_unaligned_le16(read_elements, &cmd[9]);
+ cmd[11] = raw_data_type;
+ resp_len = 10 + read_len;
+
+ error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+ cmd, 12,
+ resp_data, &resp_len,
+ 500, cyapa_gen5_sort_tsg_pip_app_resp_data, true);
+ if (error || resp_len < 10 ||
+ !VALID_CMD_RESP_HEADER(resp_data, cmd_code) ||
+ !GEN5_CMD_COMPLETE_SUCCESS(resp_data[5]) ||
+ resp_data[6] != raw_data_type)
+ return error ? error : -EAGAIN;
+
+ read_elements = get_unaligned_le16(&resp_data[7]);
+ if (read_elements == 0)
+ break;
+
+ data_size = (resp_data[9] & GEN5_PWC_DATA_ELEMENT_SIZE_MASK);
+ offset += read_elements;
+ if (read_elements) {
+ for (i = 10;
+ i < (read_elements * data_size + 10);
+ i += data_size) {
+ value = cyapa_parse_structure_data(resp_data[9],
+ &resp_data[i], data_size);
+ *raw_data_min = min(value, *raw_data_min);
+ *raw_data_max = max(value, *raw_data_max);
+
+ if (intp)
+ put_unaligned_le32(value, &intp[count]);
+
+ sum += value;
+ count++;
+
+ }
+ }
+
+ if (count >= raw_data_max_num)
+ break;
+
+ read_elements = (sizeof(resp_data) - 10) / data_size;
+ read_len = read_elements * data_size;
+ } while (true);
+
+ *raw_data_ave = count ? (sum / count) : 0;
+
+ return 0;
+}
+
+static ssize_t cyapa_gen5_show_baseline(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cyapa *cyapa = dev_get_drvdata(dev);
+ int gidac_mutual_max, gidac_mutual_min, gidac_mutual_ave;
+ int lidac_mutual_max, lidac_mutual_min, lidac_mutual_ave;
+ int gidac_self_rx, gidac_self_tx;
+ int lidac_self_max, lidac_self_min, lidac_self_ave;
+ int raw_cap_mutual_max, raw_cap_mutual_min, raw_cap_mutual_ave;
+ int raw_cap_self_max, raw_cap_self_min, raw_cap_self_ave;
+ int mutual_diffdata_max, mutual_diffdata_min, mutual_diffdata_ave;
+ int self_diffdata_max, self_diffdata_min, self_diffdata_ave;
+ int mutual_baseline_max, mutual_baseline_min, mutual_baseline_ave;
+ int self_baseline_max, self_baseline_min, self_baseline_ave;
+ int error, resume_error;
+ int size;
+
+ if (cyapa->state != CYAPA_STATE_GEN5_APP)
+ return -EBUSY;
+
+ /* 1. Suspend Scanning*/
+ error = cyapa_gen5_suspend_scanning(cyapa);
+ if (error)
+ return error;
+
+ /* 2. Read global and local mutual IDAC data. */
+ gidac_self_rx = gidac_self_tx = 0;
+ error = cyapa_gen5_read_mutual_idac_data(cyapa,
+ &gidac_mutual_max, &gidac_mutual_min,
+ &gidac_mutual_ave, &lidac_mutual_max,
+ &lidac_mutual_min, &lidac_mutual_ave);
+ if (error)
+ goto resume_scanning;
+
+ /* 3. Read global and local self IDAC data. */
+ error = cyapa_gen5_read_self_idac_data(cyapa,
+ &gidac_self_rx, &gidac_self_tx,
+ &lidac_self_max, &lidac_self_min,
+ &lidac_self_ave);
+ if (error)
+ goto resume_scanning;
+
+ /* 4. Execuate panel scan. It must be executed before read data. */
+ error = cyapa_gen5_execute_panel_scan(cyapa);
+ if (error)
+ goto resume_scanning;
+
+ /* 5. Retrieve panel scan, mutual cap raw data. */
+ error = cyapa_gen5_read_panel_scan_raw_data(cyapa,
+ GEN5_CMD_RETRIEVE_PANEL_SCAN,
+ GEN5_PANEL_SCAN_MUTUAL_RAW_DATA,
+ cyapa->electrodes_x * cyapa->electrodes_y,
+ &raw_cap_mutual_max, &raw_cap_mutual_min,
+ &raw_cap_mutual_ave,
+ NULL);
+ if (error)
+ goto resume_scanning;
+
+ /* 6. Retrieve panel scan, self cap raw data. */
+ error = cyapa_gen5_read_panel_scan_raw_data(cyapa,
+ GEN5_CMD_RETRIEVE_PANEL_SCAN,
+ GEN5_PANEL_SCAN_SELF_RAW_DATA,
+ cyapa->electrodes_x + cyapa->electrodes_y,
+ &raw_cap_self_max, &raw_cap_self_min,
+ &raw_cap_self_ave,
+ NULL);
+ if (error)
+ goto resume_scanning;
+
+ /* 7. Retrieve panel scan, mutual cap diffcount raw data. */
+ error = cyapa_gen5_read_panel_scan_raw_data(cyapa,
+ GEN5_CMD_RETRIEVE_PANEL_SCAN,
+ GEN5_PANEL_SCAN_MUTUAL_DIFFCOUNT,
+ cyapa->electrodes_x * cyapa->electrodes_y,
+ &mutual_diffdata_max, &mutual_diffdata_min,
+ &mutual_diffdata_ave,
+ NULL);
+ if (error)
+ goto resume_scanning;
+
+ /* 8. Retrieve panel scan, self cap diffcount raw data. */
+ error = cyapa_gen5_read_panel_scan_raw_data(cyapa,
+ GEN5_CMD_RETRIEVE_PANEL_SCAN,
+ GEN5_PANEL_SCAN_SELF_DIFFCOUNT,
+ cyapa->electrodes_x + cyapa->electrodes_y,
+ &self_diffdata_max, &self_diffdata_min,
+ &self_diffdata_ave,
+ NULL);
+ if (error)
+ goto resume_scanning;
+
+ /* 9. Retrieve panel scan, mutual cap baseline raw data. */
+ error = cyapa_gen5_read_panel_scan_raw_data(cyapa,
+ GEN5_CMD_RETRIEVE_PANEL_SCAN,
+ GEN5_PANEL_SCAN_MUTUAL_BASELINE,
+ cyapa->electrodes_x * cyapa->electrodes_y,
+ &mutual_baseline_max, &mutual_baseline_min,
+ &mutual_baseline_ave,
+ NULL);
+ if (error)
+ goto resume_scanning;
+
+ /* 10. Retrieve panel scan, self cap baseline raw data. */
+ error = cyapa_gen5_read_panel_scan_raw_data(cyapa,
+ GEN5_CMD_RETRIEVE_PANEL_SCAN,
+ GEN5_PANEL_SCAN_SELF_BASELINE,
+ cyapa->electrodes_x + cyapa->electrodes_y,
+ &self_baseline_max, &self_baseline_min,
+ &self_baseline_ave,
+ NULL);
+ if (error)
+ goto resume_scanning;
+
+resume_scanning:
+ /* 11. Resume Scanning*/
+ resume_error = cyapa_gen5_resume_scanning(cyapa);
+ if (resume_error || error)
+ return resume_error ? resume_error : error;
+
+ /* 12. Output data strings */
+ size = scnprintf(buf, PAGE_SIZE, "%d %d %d %d %d %d %d %d %d %d %d ",
+ gidac_mutual_min, gidac_mutual_max, gidac_mutual_ave,
+ lidac_mutual_min, lidac_mutual_max, lidac_mutual_ave,
+ gidac_self_rx, gidac_self_tx,
+ lidac_self_min, lidac_self_max, lidac_self_ave);
+ size += scnprintf(buf + size, PAGE_SIZE - size,
+ "%d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n",
+ raw_cap_mutual_min, raw_cap_mutual_max, raw_cap_mutual_ave,
+ raw_cap_self_min, raw_cap_self_max, raw_cap_self_ave,
+ mutual_diffdata_min, mutual_diffdata_max, mutual_diffdata_ave,
+ self_diffdata_min, self_diffdata_max, self_diffdata_ave,
+ mutual_baseline_min, mutual_baseline_max, mutual_baseline_ave,
+ self_baseline_min, self_baseline_max, self_baseline_ave);
+ return size;
+}
+
static bool cyapa_gen5_sort_system_info_data(struct cyapa *cyapa,
u8 *buf, int len)
{
@@ -1936,6 +2555,8 @@ const struct cyapa_dev_ops cyapa_gen5_ops = {
.bl_initiate = cyapa_gen5_bl_initiate,
.update_fw = cyapa_gen5_do_fw_update,
+ .show_baseline = cyapa_gen5_show_baseline,
+
.initialize = cyapa_gen5_initialize,
.state_parse = cyapa_gen5_state_parse,
--
1.9.1
^ permalink raw reply related
* [PATCH v10 11/18] input: cyapa: add gen5 trackpad device firmware update function support
From: Dudley Du @ 2014-11-14 6:03 UTC (permalink / raw)
To: dmitry.torokhov, rydberg; +Cc: Dudley Du, bleung, linux-input, linux-kernel
In-Reply-To: <1415945004-19868-1-git-send-email-dudl@cypress.com>
Add firmware image update function supported for gen5 trackpad device,
it can be used through sysfs update_fw interface.
TEST=test on Chromebooks.
Signed-off-by: Dudley Du <dudl@cypress.com>
---
drivers/input/mouse/Kconfig | 2 +-
drivers/input/mouse/cyapa_gen5.c | 290 +++++++++++++++++++++++++++++++++++++++
2 files changed, 291 insertions(+), 1 deletion(-)
diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
index 366fc7a..005d69b 100644
--- a/drivers/input/mouse/Kconfig
+++ b/drivers/input/mouse/Kconfig
@@ -205,7 +205,7 @@ config MOUSE_BCM5974
config MOUSE_CYAPA
tristate "Cypress APA I2C Trackpad support"
- depends on I2C
+ depends on I2C && CRC_ITU_T
help
This driver adds support for Cypress All Points Addressable (APA)
I2C Trackpads, including the ones used in 2012 Samsung Chromebooks.
diff --git a/drivers/input/mouse/cyapa_gen5.c b/drivers/input/mouse/cyapa_gen5.c
index ee93e63..f8f5b15 100644
--- a/drivers/input/mouse/cyapa_gen5.c
+++ b/drivers/input/mouse/cyapa_gen5.c
@@ -18,6 +18,7 @@
#include <linux/completion.h>
#include <linux/slab.h>
#include <linux/unaligned/access_ok.h>
+#include <linux/crc-itu-t.h>
#include "cyapa.h"
@@ -911,6 +912,86 @@ static int cyapa_gen5_state_parse(struct cyapa *cyapa, u8 *reg_data, int len)
return -EAGAIN;
}
+static int cyapa_gen5_bl_initiate(struct cyapa *cyapa,
+ const struct firmware *fw)
+{
+ u16 length = 0;
+ u16 data_len = 0;
+ u16 meta_data_crc = 0;
+ u16 cmd_crc = 0;
+ u8 bl_gen5_activate[18 + CYAPA_TSG_FLASH_MAP_BLOCK_SIZE + 3];
+ int bl_gen5_activate_size = 0;
+ u8 resp_data[11];
+ int resp_len;
+ struct cyapa_tsg_bin_image *image;
+ int records_num;
+ u8 *data;
+ int error;
+
+ /* Try to dump all bufferred report data before send any command. */
+ cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
+
+ bl_gen5_activate_size = sizeof(bl_gen5_activate);
+ memset(bl_gen5_activate, 0, bl_gen5_activate_size);
+
+ /* Output Report Register Address[15:0] = 0004h */
+ bl_gen5_activate[0] = 0x04;
+ bl_gen5_activate[1] = 0x00;
+
+ /* Total command length[15:0] */
+ length = bl_gen5_activate_size - 2;
+ put_unaligned_le16(length, &bl_gen5_activate[2]);
+ bl_gen5_activate[4] = 0x40; /* Report ID = 40h */
+ bl_gen5_activate[5] = 0x00; /* RSVD = 00h */
+
+ bl_gen5_activate[6] = GEN5_SOP_KEY; /* SOP = 01h */
+ bl_gen5_activate[7] = 0x48; /* Command Code = 48h */
+
+ /* 8 Key bytes and block size */
+ data_len = CYAPA_TSG_BL_KEY_SIZE + CYAPA_TSG_FLASH_MAP_BLOCK_SIZE;
+ /* Data Length[15:0] */
+ put_unaligned_le16(data_len, &bl_gen5_activate[8]);
+ bl_gen5_activate[10] = 0xa5; /* Key Byte 0 */
+ bl_gen5_activate[11] = 0x01;
+ bl_gen5_activate[12] = 0x02; /* . */
+ bl_gen5_activate[13] = 0x03; /* . */
+ bl_gen5_activate[14] = 0xff; /* . */
+ bl_gen5_activate[15] = 0xfe;
+ bl_gen5_activate[16] = 0xfd;
+ bl_gen5_activate[17] = 0x5a; /* Key Byte 7 */
+
+ /* Copy 60 bytes Meta Data Row Parameters */
+ image = (struct cyapa_tsg_bin_image *)fw->data;
+ records_num = (fw->size - sizeof(struct cyapa_tsg_bin_image_head)) /
+ sizeof(struct cyapa_tsg_bin_image_data_record);
+ /* APP_INTEGRITY row is always the last row block */
+ data = image->records[records_num - 1].record_data;
+ memcpy(&bl_gen5_activate[18], data, CYAPA_TSG_FLASH_MAP_METADATA_SIZE);
+
+ meta_data_crc = crc_itu_t(0xffff, &bl_gen5_activate[18],
+ CYAPA_TSG_FLASH_MAP_METADATA_SIZE);
+ /* Meta Data CRC[15:0] */
+ put_unaligned_le16(meta_data_crc,
+ &bl_gen5_activate[18 + CYAPA_TSG_FLASH_MAP_METADATA_SIZE]);
+
+ cmd_crc = crc_itu_t(0xffff, &bl_gen5_activate[6], 4 + data_len);
+ put_unaligned_le16(cmd_crc,
+ &bl_gen5_activate[bl_gen5_activate_size - 3]); /* CRC[15:0] */
+ bl_gen5_activate[bl_gen5_activate_size - 1] = GEN5_EOP_KEY;
+
+ resp_len = sizeof(resp_data);
+ error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+ bl_gen5_activate, sizeof(bl_gen5_activate),
+ resp_data, &resp_len, 12000,
+ cyapa_gen5_sort_tsg_pip_bl_resp_data, true);
+ if (error || resp_len != GEN5_BL_INITIATE_RESP_LEN ||
+ resp_data[2] != GEN5_BL_RESP_REPORT_ID ||
+ !GEN5_CMD_COMPLETE_SUCCESS(resp_data[5]))
+ return error ? error : -EAGAIN;
+
+ return 0;
+}
+
bool cyapa_gen5_sort_bl_exit_data(struct cyapa *cyapa, u8 *buf, int len)
{
if (buf == NULL || len < GEN5_RESP_LENGTH_SIZE)
@@ -959,6 +1040,210 @@ static int cyapa_gen5_bl_exit(struct cyapa *cyapa)
return -ENODEV;
}
+static int cyapa_gen5_bl_enter(struct cyapa *cyapa)
+{
+ int error;
+ u8 cmd[] = { 0x04, 0x00, 0x05, 0x00, 0x2F, 0x00, 0x01 };
+ u8 resp_data[2];
+ int resp_len;
+
+ error = cyapa_poll_state(cyapa, 500);
+ if (error < 0)
+ return error;
+ if (cyapa->gen != CYAPA_GEN5)
+ return -EINVAL;
+
+ /* Already in Gen5 BL. Skipping exit. */
+ if (cyapa->state == CYAPA_STATE_GEN5_BL)
+ return 0;
+
+ if (cyapa->state != CYAPA_STATE_GEN5_APP)
+ return -EAGAIN;
+
+ /* Try to dump all bufferred report data before send any command. */
+ cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
+
+ /*
+ * Send bootloader enter command to trackpad device,
+ * after enter bootloader, the response data is two bytes of 0x00 0x00.
+ */
+ resp_len = sizeof(resp_data);
+ memset(resp_data, 0, resp_len);
+ error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+ cmd, sizeof(cmd),
+ resp_data, &resp_len,
+ 5000, cyapa_gen5_sort_application_launch_data,
+ true);
+ if (error || resp_data[0] != 0x00 || resp_data[1] != 0x00)
+ return error < 0 ? error : -EAGAIN;
+
+ cyapa->state = CYAPA_STATE_GEN5_BL;
+ return 0;
+}
+
+static int cyapa_gen5_check_fw(struct cyapa *cyapa, const struct firmware *fw)
+{
+ int i;
+ struct cyapa_tsg_bin_image *image;
+ int flash_records_count;
+ u16 expected_app_crc;
+ u16 expected_app_integrity_crc;
+ u16 app_crc = 0;
+ u16 app_integrity_crc = 0;
+ u16 row_num;
+ u8 *data;
+ u32 app_start;
+ u16 app_len;
+ u32 img_start;
+ u16 img_len;
+ int record_index;
+ struct device *dev = &cyapa->client->dev;
+
+ image = (struct cyapa_tsg_bin_image *)fw->data;
+ flash_records_count = (fw->size -
+ sizeof(struct cyapa_tsg_bin_image_head)) /
+ sizeof(struct cyapa_tsg_bin_image_data_record);
+
+ /* APP_INTEGRITY row is always the last row block,
+ * and the row id must be 0x01ff */
+ row_num = get_unaligned_be16(
+ &image->records[flash_records_count - 1].row_number);
+ if (&image->records[flash_records_count - 1].flash_array_id != 0x00 &&
+ row_num != 0x01ff) {
+ dev_err(dev, "%s: invalid app_integrity data.\n", __func__);
+ return -EINVAL;
+ }
+ data = image->records[flash_records_count - 1].record_data;
+ app_start = get_unaligned_le32(&data[4]);
+ app_len = get_unaligned_le16(&data[8]);
+ expected_app_crc = get_unaligned_le16(&data[10]);
+ img_start = get_unaligned_le32(&data[16]);
+ img_len = get_unaligned_le16(&data[20]);
+ expected_app_integrity_crc = get_unaligned_le16(&data[60]);
+
+ if ((app_start + app_len + img_start + img_len) %
+ CYAPA_TSG_FW_ROW_SIZE) {
+ dev_err(dev, "%s: invalid image alignment.\n", __func__);
+ return -EINVAL;
+ }
+
+ /* Verify app_integrity crc */
+ app_integrity_crc = crc_itu_t(0xffff, data,
+ CYAPA_TSG_APP_INTEGRITY_SIZE);
+ if (app_integrity_crc != expected_app_integrity_crc) {
+ dev_err(dev, "%s: invalid app_integrity crc.\n", __func__);
+ return -EINVAL;
+ }
+
+ /*
+ * Verify application image CRC
+ */
+ record_index = app_start / CYAPA_TSG_FW_ROW_SIZE -
+ CYAPA_TSG_IMG_START_ROW_NUM;
+ data = (u8 *)&image->records[record_index].record_data;
+ app_crc = crc_itu_t(0xffff, data, CYAPA_TSG_FW_ROW_SIZE);
+ for (i = 1; i < (app_len / CYAPA_TSG_FW_ROW_SIZE); i++) {
+ data = (u8 *)&image->records[++record_index].record_data;
+ app_crc = crc_itu_t(app_crc, data, CYAPA_TSG_FW_ROW_SIZE);
+ }
+
+ if (app_crc != expected_app_crc) {
+ dev_err(dev, "%s: invalid firmware app crc check.\n", __func__);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cyapa_gen5_write_fw_block(struct cyapa *cyapa,
+ struct cyapa_tsg_bin_image_data_record *flash_record)
+{
+ u8 flash_array_id;
+ u16 flash_row_id;
+ u16 record_len;
+ u8 *record_data;
+ u8 cmd[144]; /* 13 + 128+ 3 */
+ u16 cmd_len;
+ u16 data_len;
+ u16 crc;
+ u8 resp_data[11];
+ int resp_len;
+ int error;
+
+ flash_array_id = flash_record->flash_array_id;
+ flash_row_id = get_unaligned_be16(&flash_record->row_number);
+ record_len = get_unaligned_be16(&flash_record->record_len);
+ record_data = flash_record->record_data;
+
+ cmd_len = sizeof(cmd) - 2; /* Not include 2 bytes regisetr address. */
+ memset(cmd, 0, cmd_len + 2);
+ cmd[0] = 0x04; /* Register address */
+ cmd[1] = 0x00;
+
+ put_unaligned_le16(cmd_len, &cmd[2]);
+ cmd[4] = 0x40; /* Report id 40h */
+ cmd[5] = 0x00;
+
+ cmd[6] = GEN5_SOP_KEY; /* SOP = 01h */
+ cmd[7] = 0x39; /* Command code = 39h */
+ /* 1 (Flash Array ID) + 2 (Flash Row ID) + 128 (flash data) */
+ data_len = 3 + record_len;
+ put_unaligned_le16(data_len, &cmd[8]);
+ cmd[10] = flash_array_id; /* Flash Array ID = 00h */
+ put_unaligned_le16(flash_row_id, &cmd[11]);
+
+ memcpy(&cmd[13], record_data, record_len);
+ crc = crc_itu_t(0xffff, &cmd[6], 4 + data_len);
+ put_unaligned_le16(crc, &cmd[2 + cmd_len - 3]);
+ cmd[2 + cmd_len - 1] = GEN5_EOP_KEY;
+
+ resp_len = sizeof(resp_data);
+ error = cyapa_i2c_pip_cmd_irq_sync(cyapa,
+ cmd, sizeof(cmd),
+ resp_data, &resp_len,
+ 500, cyapa_gen5_sort_tsg_pip_bl_resp_data, true);
+ if (error || resp_len != GEN5_BL_BLOCK_WRITE_RESP_LEN ||
+ resp_data[2] != GEN5_BL_RESP_REPORT_ID ||
+ !GEN5_CMD_COMPLETE_SUCCESS(resp_data[5]))
+ return error < 0 ? error : -EAGAIN;
+
+ return 0;
+}
+
+static int cyapa_gen5_do_fw_update(struct cyapa *cyapa,
+ const struct firmware *fw)
+{
+ struct device *dev = &cyapa->client->dev;
+ struct cyapa_tsg_bin_image *image =
+ (struct cyapa_tsg_bin_image *)fw->data;
+ struct cyapa_tsg_bin_image_data_record *flash_record;
+ int flash_records_count;
+ int i;
+ int error;
+
+ /* Try to dump all bufferred data if exists before send commands. */
+ cyapa_empty_pip_output_data(cyapa, NULL, NULL, NULL);
+
+ flash_records_count =
+ (fw->size - sizeof(struct cyapa_tsg_bin_image_head)) /
+ sizeof(struct cyapa_tsg_bin_image_data_record);
+ /*
+ * The last flash row 0x01ff has been written through bl_initiate
+ * command, so DO NOT write flash 0x01ff to trackpad device.
+ */
+ for (i = 0; i < (flash_records_count - 1); i++) {
+ flash_record = &image->records[i];
+ error = cyapa_gen5_write_fw_block(cyapa, flash_record);
+ if (error) {
+ dev_err(dev, "%s: Gen5 FW update aborted: %d\n",
+ __func__, error);
+ return error;
+ }
+ }
+
+ return 0;
+}
+
static int cyapa_gen5_change_power_state(struct cyapa *cyapa, u8 power_state)
{
u8 cmd[8] = { 0x04, 0x00, 0x06, 0x00, 0x2f, 0x00, 0x08, 0x01 };
@@ -1646,6 +1931,11 @@ static int cyapa_gen5_irq_handler(struct cyapa *cyapa)
}
const struct cyapa_dev_ops cyapa_gen5_ops = {
+ .check_fw = cyapa_gen5_check_fw,
+ .bl_enter = cyapa_gen5_bl_enter,
+ .bl_initiate = cyapa_gen5_bl_initiate,
+ .update_fw = cyapa_gen5_do_fw_update,
+
.initialize = cyapa_gen5_initialize,
.state_parse = cyapa_gen5_state_parse,
--
1.9.1
^ permalink raw reply related
* [PATCH v10 10/18] input: cyapa: add gen3 trackpad device force re-calibrate function support
From: Dudley Du @ 2014-11-14 6:03 UTC (permalink / raw)
To: dmitry.torokhov, rydberg; +Cc: Dudley Du, bleung, linux-input, linux-kernel
In-Reply-To: <1415945004-19868-1-git-send-email-dudl@cypress.com>
Add force re-calibrate function supported for gen3 trackpad device,
it can be used through sysfs calibrate interface.
TEST=test on Chromebooks.
Signed-off-by: Dudley Du <dudl@cypress.com>
---
drivers/input/mouse/cyapa_gen3.c | 58 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 58 insertions(+)
diff --git a/drivers/input/mouse/cyapa_gen3.c b/drivers/input/mouse/cyapa_gen3.c
index bc2be93..281209f 100644
--- a/drivers/input/mouse/cyapa_gen3.c
+++ b/drivers/input/mouse/cyapa_gen3.c
@@ -754,6 +754,63 @@ static int cyapa_gen3_do_fw_update(struct cyapa *cyapa,
return 0;
}
+static ssize_t cyapa_gen3_do_calibrate(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct cyapa *cyapa = dev_get_drvdata(dev);
+ int tries = 20; /* max recalibration timeout 2s. */
+ int ret;
+
+ ret = cyapa_read_byte(cyapa, CYAPA_CMD_DEV_STATUS);
+ if (ret < 0) {
+ dev_err(dev, "Error reading dev status: %d\n", ret);
+ goto out;
+ }
+ if ((ret & CYAPA_DEV_NORMAL) != CYAPA_DEV_NORMAL) {
+ dev_warn(dev, "Trackpad device is busy, device state: 0x%02x\n",
+ ret);
+ ret = -EAGAIN;
+ goto out;
+ }
+
+ ret = cyapa_write_byte(cyapa, CYAPA_CMD_SOFT_RESET,
+ OP_RECALIBRATION_MASK);
+ if (ret < 0) {
+ dev_err(dev, "Failed to send calibrate command: %d\n",
+ ret);
+ goto out;
+ }
+
+ do {
+ /*
+ * For this recalibration, the max time will not exceed 2s.
+ * The average time is approximately 500 - 700 ms, and we
+ * will check the status every 100 - 200ms.
+ */
+ usleep_range(100000, 200000);
+
+ ret = cyapa_read_byte(cyapa, CYAPA_CMD_DEV_STATUS);
+ if (ret < 0) {
+ dev_err(dev, "Error reading dev status: %d\n",
+ ret);
+ goto out;
+ }
+ if ((ret & CYAPA_DEV_NORMAL) == CYAPA_DEV_NORMAL)
+ break;
+ } while (--tries);
+
+ if (tries == 0) {
+ dev_err(dev, "Failed to calibrate. Timeout.\n");
+ ret = -ETIMEDOUT;
+ goto out;
+ }
+ dev_dbg(dev, "Calibration successful.\n");
+
+out:
+ return ret < 0 ? ret : count;
+}
+
static ssize_t cyapa_gen3_show_baseline(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -1136,6 +1193,7 @@ const struct cyapa_dev_ops cyapa_gen3_ops = {
.bl_deactivate = cyapa_gen3_bl_deactivate,
.show_baseline = cyapa_gen3_show_baseline,
+ .calibrate_store = cyapa_gen3_do_calibrate,
.state_parse = cyapa_gen3_state_parse,
.operational_check = cyapa_gen3_do_operational_check,
--
1.9.1
^ permalink raw reply related
* [PATCH v10 09/18] input: cyapa: add gen3 trackpad device read baseline function support
From: Dudley Du @ 2014-11-14 6:03 UTC (permalink / raw)
To: dmitry.torokhov, rydberg; +Cc: Dudley Du, bleung, linux-input, linux-kernel
In-Reply-To: <1415945004-19868-1-git-send-email-dudl@cypress.com>
Add read baseline function supported for gen3 trackpad device,
it can be used through sysfs baseline interface.
TEST=test on Chromebooks.
Signed-off-by: Dudley Du <dudl@cypress.com>
---
drivers/input/mouse/cyapa_gen3.c | 71 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 71 insertions(+)
diff --git a/drivers/input/mouse/cyapa_gen3.c b/drivers/input/mouse/cyapa_gen3.c
index fc1b781..bc2be93 100644
--- a/drivers/input/mouse/cyapa_gen3.c
+++ b/drivers/input/mouse/cyapa_gen3.c
@@ -754,6 +754,75 @@ static int cyapa_gen3_do_fw_update(struct cyapa *cyapa,
return 0;
}
+static ssize_t cyapa_gen3_show_baseline(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cyapa *cyapa = dev_get_drvdata(dev);
+ int max_baseline, min_baseline;
+ int tries = 3;
+ int ret;
+
+ ret = cyapa_read_byte(cyapa, CYAPA_CMD_DEV_STATUS);
+ if (ret < 0) {
+ dev_err(dev, "Error reading dev status. err = %d\n", ret);
+ goto out;
+ }
+ if ((ret & CYAPA_DEV_NORMAL) != CYAPA_DEV_NORMAL) {
+ dev_warn(dev, "Trackpad device is busy. device state = 0x%x\n",
+ ret);
+ ret = -EAGAIN;
+ goto out;
+ }
+
+ ret = cyapa_write_byte(cyapa, CYAPA_CMD_SOFT_RESET,
+ OP_REPORT_BASELINE_MASK);
+ if (ret < 0) {
+ dev_err(dev, "Failed to send report baseline command. %d\n",
+ ret);
+ goto out;
+ }
+
+ do {
+ usleep_range(10000, 20000);
+
+ ret = cyapa_read_byte(cyapa, CYAPA_CMD_DEV_STATUS);
+ if (ret < 0) {
+ dev_err(dev, "Error reading dev status. err = %d\n",
+ ret);
+ goto out;
+ }
+ if ((ret & CYAPA_DEV_NORMAL) == CYAPA_DEV_NORMAL)
+ break;
+ } while (--tries);
+
+ if (tries == 0) {
+ dev_err(dev, "Device timed out going to Normal state.\n");
+ ret = -ETIMEDOUT;
+ goto out;
+ }
+
+ ret = cyapa_read_byte(cyapa, CYAPA_CMD_MAX_BASELINE);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read max baseline. err = %d\n", ret);
+ goto out;
+ }
+ max_baseline = ret;
+
+ ret = cyapa_read_byte(cyapa, CYAPA_CMD_MIN_BASELINE);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read min baseline. err = %d\n", ret);
+ goto out;
+ }
+ min_baseline = ret;
+
+ dev_dbg(dev, "Baseline report successful. Max: %d Min: %d\n",
+ max_baseline, min_baseline);
+ ret = scnprintf(buf, PAGE_SIZE, "%d %d\n", max_baseline, min_baseline);
+
+out:
+ return ret;
+}
+
/*
* cyapa_get_wait_time_for_pwr_cmd
*
@@ -1066,6 +1135,8 @@ const struct cyapa_dev_ops cyapa_gen3_ops = {
.update_fw = cyapa_gen3_do_fw_update,
.bl_deactivate = cyapa_gen3_bl_deactivate,
+ .show_baseline = cyapa_gen3_show_baseline,
+
.state_parse = cyapa_gen3_state_parse,
.operational_check = cyapa_gen3_do_operational_check,
--
1.9.1
^ permalink raw reply related
* [PATCH v10 08/18] input: cyapa: add gen3 trackpad device firmware update function support
From: Dudley Du @ 2014-11-14 6:03 UTC (permalink / raw)
To: dmitry.torokhov, rydberg; +Cc: Dudley Du, bleung, linux-input, linux-kernel
In-Reply-To: <1415945004-19868-1-git-send-email-dudl@cypress.com>
Add firmware image update function supported for gen3 trackpad device,
it can be used through sysfs update_fw interface.
TEST=test on Chromebooks.
Signed-off-by: Dudley Du <dudl@cypress.com>
---
drivers/input/mouse/cyapa_gen3.c | 284 +++++++++++++++++++++++++++++++++++++++
1 file changed, 284 insertions(+)
diff --git a/drivers/input/mouse/cyapa_gen3.c b/drivers/input/mouse/cyapa_gen3.c
index 58c23e1..fc1b781 100644
--- a/drivers/input/mouse/cyapa_gen3.c
+++ b/drivers/input/mouse/cyapa_gen3.c
@@ -416,6 +416,72 @@ static int cyapa_gen3_state_parse(struct cyapa *cyapa, u8 *reg_data, int len)
return -EAGAIN;
}
+/*
+ * Enter bootloader by soft resetting the device.
+ *
+ * If device is already in the bootloader, the function just returns.
+ * Otherwise, reset the device; after reset, device enters bootloader idle
+ * state immediately.
+ *
+ * Also, if device was unregister device from input core. Device will
+ * re-register after it is detected following resumption of operational mode.
+ *
+ * Returns:
+ * 0 on success
+ * -EAGAIN device was reset, but is not now in bootloader idle state
+ * < 0 if the device never responds within the timeout
+ */
+static int cyapa_gen3_bl_enter(struct cyapa *cyapa)
+{
+ int error;
+
+ error = cyapa_poll_state(cyapa, 500);
+ if (error)
+ return error;
+ if (cyapa->state == CYAPA_STATE_BL_IDLE) {
+ /* Already in BL_IDLE. Skipping reset. */
+ return 0;
+ }
+
+ if (cyapa->state != CYAPA_STATE_OP)
+ return -EAGAIN;
+
+ cyapa->state = CYAPA_STATE_NO_DEVICE;
+ error = cyapa_write_byte(cyapa, CYAPA_CMD_SOFT_RESET, 0x01);
+ if (error)
+ return -EIO;
+
+ usleep_range(25000, 50000);
+ error = cyapa_poll_state(cyapa, 500);
+ if (error)
+ return error;
+ if ((cyapa->state != CYAPA_STATE_BL_IDLE) ||
+ (cyapa->status[REG_BL_STATUS] & BL_STATUS_WATCHDOG))
+ return -EAGAIN;
+
+ return 0;
+}
+
+static int cyapa_gen3_bl_activate(struct cyapa *cyapa)
+{
+ int error;
+
+ error = cyapa_i2c_reg_write_block(cyapa, 0, sizeof(bl_activate),
+ bl_activate);
+ if (error)
+ return error;
+
+ /* Wait for bootloader to activate; takes between 2 and 12 seconds */
+ msleep(2000);
+ error = cyapa_poll_state(cyapa, 11000);
+ if (error)
+ return error;
+ if (cyapa->state != CYAPA_STATE_BL_ACTIVE)
+ return -EAGAIN;
+
+ return 0;
+}
+
static int cyapa_gen3_bl_deactivate(struct cyapa *cyapa)
{
int error;
@@ -476,6 +542,218 @@ static int cyapa_gen3_bl_exit(struct cyapa *cyapa)
return 0;
}
+/* Used in gen3 bootloader commands. */
+static u16 cyapa_gen3_csum(const u8 *buf, size_t count)
+{
+ int i;
+ u16 csum = 0;
+
+ for (i = 0; i < count; i++)
+ csum += buf[i];
+
+ return csum;
+}
+
+/*
+ * Verify the integrity of a CYAPA firmware image file.
+ *
+ * The firmware image file is 30848 bytes, composed of 482 64-byte blocks.
+ *
+ * The first 2 blocks are the firmware header.
+ * The next 480 blocks are the firmware image.
+ *
+ * The first two bytes of the header hold the header checksum, computed by
+ * summing the other 126 bytes of the header.
+ * The last two bytes of the header hold the firmware image checksum, computed
+ * by summing the 30720 bytes of the image modulo 0xffff.
+ *
+ * Both checksums are stored little-endian.
+ */
+static int cyapa_gen3_check_fw(struct cyapa *cyapa, const struct firmware *fw)
+{
+ struct device *dev = &cyapa->client->dev;
+ u16 csum;
+ u16 csum_expected;
+
+ /* Firmware must match exact 30848 bytes = 482 64-byte blocks. */
+ if (fw->size != CYAPA_FW_SIZE) {
+ dev_err(dev, "invalid firmware size = %zu, expected %u.\n",
+ fw->size, CYAPA_FW_SIZE);
+ return -EINVAL;
+ }
+
+ /* Verify header block */
+ csum_expected = (fw->data[0] << 8) | fw->data[1];
+ csum = cyapa_gen3_csum(&fw->data[2], CYAPA_FW_HDR_SIZE - 2);
+ if (csum != csum_expected) {
+ dev_err(dev, "%s %04x, expected: %04x\n",
+ "invalid firmware header checksum = ",
+ csum, csum_expected);
+ return -EINVAL;
+ }
+
+ /* Verify firmware image */
+ csum_expected = (fw->data[CYAPA_FW_HDR_SIZE - 2] << 8) |
+ fw->data[CYAPA_FW_HDR_SIZE - 1];
+ csum = cyapa_gen3_csum(&fw->data[CYAPA_FW_HDR_SIZE],
+ CYAPA_FW_DATA_SIZE);
+ if (csum != csum_expected) {
+ dev_err(dev, "%s %04x, expected: %04x\n",
+ "invalid firmware header checksum = ",
+ csum, csum_expected);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * Write a |len| byte long buffer |buf| to the device, by chopping it up into a
+ * sequence of smaller |CYAPA_CMD_LEN|-length write commands.
+ *
+ * The data bytes for a write command are prepended with the 1-byte offset
+ * of the data relative to the start of |buf|.
+ */
+static int cyapa_gen3_write_buffer(struct cyapa *cyapa,
+ const u8 *buf, size_t len)
+{
+ int error;
+ size_t i;
+ unsigned char cmd[CYAPA_CMD_LEN + 1];
+ size_t cmd_len;
+
+ for (i = 0; i < len; i += CYAPA_CMD_LEN) {
+ const u8 *payload = &buf[i];
+
+ cmd_len = (len - i >= CYAPA_CMD_LEN) ? CYAPA_CMD_LEN : len - i;
+ cmd[0] = i;
+ memcpy(&cmd[1], payload, cmd_len);
+
+ error = cyapa_i2c_reg_write_block(cyapa, 0, cmd_len + 1, cmd);
+ if (error)
+ return error;
+ }
+ return 0;
+}
+
+/*
+ * A firmware block write command writes 64 bytes of data to a single flash
+ * page in the device. The 78-byte block write command has the format:
+ * <0xff> <CMD> <Key> <Start> <Data> <Data-Checksum> <CMD Checksum>
+ *
+ * <0xff> - every command starts with 0xff
+ * <CMD> - the write command value is 0x39
+ * <Key> - write commands include an 8-byte key: { 00 01 02 03 04 05 06 07 }
+ * <Block> - Memory Block number (address / 64) (16-bit, big-endian)
+ * <Data> - 64 bytes of firmware image data
+ * <Data Checksum> - sum of 64 <Data> bytes, modulo 0xff
+ * <CMD Checksum> - sum of 77 bytes, from 0xff to <Data Checksum>
+ *
+ * Each write command is split into 5 i2c write transactions of up to 16 bytes.
+ * Each transaction starts with an i2c register offset: (00, 10, 20, 30, 40).
+ */
+static int cyapa_gen3_write_fw_block(struct cyapa *cyapa,
+ u16 block, const u8 *data)
+{
+ int ret;
+ u8 cmd[78];
+ u8 status[BL_STATUS_SIZE];
+ /* Programming for one block can take about 100ms. */
+ int tries = 11;
+ u8 bl_status, bl_error;
+
+ /* Set write command and security key bytes. */
+ cmd[0] = 0xff;
+ cmd[1] = 0x39;
+ cmd[2] = 0x00;
+ cmd[3] = 0x01;
+ cmd[4] = 0x02;
+ cmd[5] = 0x03;
+ cmd[6] = 0x04;
+ cmd[7] = 0x05;
+ cmd[8] = 0x06;
+ cmd[9] = 0x07;
+ cmd[10] = block >> 8;
+ cmd[11] = block;
+ memcpy(&cmd[12], data, CYAPA_FW_BLOCK_SIZE);
+ cmd[76] = cyapa_gen3_csum(data, CYAPA_FW_BLOCK_SIZE);
+ cmd[77] = cyapa_gen3_csum(cmd, sizeof(cmd) - 1);
+
+ ret = cyapa_gen3_write_buffer(cyapa, cmd, sizeof(cmd));
+ if (ret)
+ return ret;
+
+ /* Wait for write to finish */
+ do {
+ usleep_range(10000, 20000);
+
+ /* Check block write command result status. */
+ ret = cyapa_i2c_reg_read_block(cyapa, BL_HEAD_OFFSET,
+ BL_STATUS_SIZE, status);
+ if (ret != BL_STATUS_SIZE)
+ return (ret < 0) ? ret : -EIO;
+ } while ((status[REG_BL_STATUS] & BL_STATUS_BUSY) && --tries);
+
+ /* Ignore WATCHDOG bit and reserved bits. */
+ bl_status = status[REG_BL_STATUS] & ~BL_STATUS_REV_MASK;
+ bl_error = status[REG_BL_ERROR] & ~BL_ERROR_RESERVED;
+
+ if (bl_status & BL_STATUS_BUSY)
+ ret = -ETIMEDOUT;
+ else if (bl_status != BL_STATUS_RUNNING ||
+ bl_error != BL_ERROR_BOOTLOADING)
+ ret = -EIO;
+ else
+ ret = 0;
+
+ return ret;
+}
+
+static int cyapa_gen3_write_blocks(struct cyapa *cyapa,
+ size_t start_block, size_t block_count,
+ const u8 *image_data)
+{
+ int error;
+ int i;
+
+ for (i = 0; i < block_count; i++) {
+ size_t block = start_block + i;
+ size_t addr = i * CYAPA_FW_BLOCK_SIZE;
+ const u8 *data = &image_data[addr];
+
+ error = cyapa_gen3_write_fw_block(cyapa, block, data);
+ if (error)
+ return error;
+ }
+ return 0;
+}
+
+static int cyapa_gen3_do_fw_update(struct cyapa *cyapa,
+ const struct firmware *fw)
+{
+ struct device *dev = &cyapa->client->dev;
+ int error;
+
+ /* First write data, starting at byte 128 of fw->data */
+ error = cyapa_gen3_write_blocks(cyapa,
+ CYAPA_FW_DATA_BLOCK_START, CYAPA_FW_DATA_BLOCK_COUNT,
+ &fw->data[CYAPA_FW_HDR_BLOCK_COUNT * CYAPA_FW_BLOCK_SIZE]);
+ if (error) {
+ dev_err(dev, "FW update aborted, write image: %d\n", error);
+ return error;
+ }
+
+ /* Then write checksum */
+ error = cyapa_gen3_write_blocks(cyapa,
+ CYAPA_FW_HDR_BLOCK_START, CYAPA_FW_HDR_BLOCK_COUNT,
+ &fw->data[0]);
+ if (error) {
+ dev_err(dev, "FW update aborted, write checksum: %d\n", error);
+ return error;
+ }
+
+ return 0;
+}
+
/*
* cyapa_get_wait_time_for_pwr_cmd
*
@@ -782,6 +1060,12 @@ static int cyapa_gen3_irq_handler(struct cyapa *cyapa)
}
const struct cyapa_dev_ops cyapa_gen3_ops = {
+ .check_fw = cyapa_gen3_check_fw,
+ .bl_enter = cyapa_gen3_bl_enter,
+ .bl_activate = cyapa_gen3_bl_activate,
+ .update_fw = cyapa_gen3_do_fw_update,
+ .bl_deactivate = cyapa_gen3_bl_deactivate,
+
.state_parse = cyapa_gen3_state_parse,
.operational_check = cyapa_gen3_do_operational_check,
--
1.9.1
^ permalink raw reply related
* [PATCH v10 07/18] input: cyapa: add sysfs interfaces supported in the cyapa driver
From: Dudley Du @ 2014-11-14 6:03 UTC (permalink / raw)
To: dmitry.torokhov, rydberg; +Cc: Dudley Du, bleung, linux-input, linux-kernel
In-Reply-To: <1415945004-19868-1-git-send-email-dudl@cypress.com>
Add device's basic control and features supported in cyapa driver through
sysfs file system interfaces. These interfaces are commonly used in
pre- and after production, for trackpad device state checking, managing
and firmware image updating.
These interfaces including mode, firmware_version and product_id interfaces
for reading firmware version and trackpad device product id values,
and including update_fw interface to command firmware image update
process. Also including baseline and calibrate interfaces for
reading and checking trackpad device's sensors states.
TEST=test on Chromebooks.
Signed-off-by: Dudley Du <dudl@cypress.com>
---
drivers/input/mouse/cyapa.c | 267 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 267 insertions(+)
diff --git a/drivers/input/mouse/cyapa.c b/drivers/input/mouse/cyapa.c
index 51c0d86..80a21ed 100644
--- a/drivers/input/mouse/cyapa.c
+++ b/drivers/input/mouse/cyapa.c
@@ -32,6 +32,8 @@
#define CYAPA_ADAPTER_FUNC_SMBUS 2
#define CYAPA_ADAPTER_FUNC_BOTH 3
+#define CYAPA_FW_NAME "cyapa.bin"
+
const char unique_str[] = "CYTRA";
/* Returns 0 on success, else negative errno on failure. */
@@ -702,6 +704,258 @@ static int cyapa_start_runtime(struct cyapa *cyapa)
}
#endif /* CONFIG_PM_RUNTIME */
+static ssize_t cyapa_show_fm_ver(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int error;
+ struct cyapa *cyapa = dev_get_drvdata(dev);
+
+ error = mutex_lock_interruptible(&cyapa->state_sync_lock);
+ if (error)
+ return error;
+ error = scnprintf(buf, PAGE_SIZE, "%d.%d\n", cyapa->fw_maj_ver,
+ cyapa->fw_min_ver);
+ mutex_unlock(&cyapa->state_sync_lock);
+ return error;
+}
+
+static ssize_t cyapa_show_product_id(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cyapa *cyapa = dev_get_drvdata(dev);
+ int size;
+ int error;
+
+ error = mutex_lock_interruptible(&cyapa->state_sync_lock);
+ if (error)
+ return error;
+ size = scnprintf(buf, PAGE_SIZE, "%s\n", cyapa->product_id);
+ mutex_unlock(&cyapa->state_sync_lock);
+ return size;
+}
+
+static int cyapa_firmware(struct cyapa *cyapa, const char *fw_name)
+{
+ struct device *dev = &cyapa->client->dev;
+ const struct firmware *fw;
+ int error;
+
+ error = request_firmware(&fw, fw_name, dev);
+ if (error) {
+ dev_err(dev, "Could not load firmware from %s: %d\n",
+ fw_name, error);
+ return error;
+ }
+
+ if (cyapa->ops->check_fw) {
+ error = cyapa->ops->check_fw(cyapa, fw);
+ if (error) {
+ dev_err(dev, "Invalid CYAPA firmware image: %s\n",
+ fw_name);
+ goto done;
+ }
+ } else {
+ dev_err(dev, "No valid device ops->check_fw handler set.\n");
+ error = -ENODEV;
+ goto done;
+ }
+
+ /*
+ * Resume the potentially suspended device because doing FW
+ * update on a device not in the FULL mode has a chance to
+ * fail.
+ */
+ pm_runtime_get_sync(dev);
+
+ if (cyapa->ops->bl_enter) {
+ error = cyapa->ops->bl_enter(cyapa);
+ if (error)
+ goto err_detect;
+ }
+
+ if (cyapa->ops->bl_activate) {
+ error = cyapa->ops->bl_activate(cyapa);
+ if (error)
+ goto err_detect;
+ }
+
+ if (cyapa->ops->bl_initiate) {
+ error = cyapa->ops->bl_initiate(cyapa, fw);
+ if (error)
+ goto err_detect;
+ }
+
+ if (cyapa->ops->update_fw) {
+ error = cyapa->ops->update_fw(cyapa, fw);
+ if (error)
+ goto err_detect;
+ }
+
+ if (cyapa->ops->bl_verify_app_integrity) {
+ error = cyapa->ops->bl_verify_app_integrity(cyapa);
+ if (error)
+ goto err_detect;
+ }
+
+err_detect:
+ pm_runtime_put_noidle(dev);
+
+done:
+ release_firmware(fw);
+ return error;
+}
+
+static ssize_t cyapa_update_fw_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct cyapa *cyapa = dev_get_drvdata(dev);
+ char fw_name[64];
+ int error;
+
+ if (count > 64) {
+ dev_err(dev, "File name too long\n");
+ return -EINVAL;
+ }
+
+ memcpy(fw_name, buf, count);
+ if (fw_name[count - 1] == '\n')
+ fw_name[count - 1] = '\0';
+ else
+ fw_name[count] = '\0';
+
+ error = mutex_lock_interruptible(&cyapa->state_sync_lock);
+ if (error)
+ return error;
+
+ if (cyapa->operational)
+ cyapa->operational = false;
+
+ error = cyapa_firmware(cyapa, fw_name);
+ if (error)
+ dev_err(dev, "firmware update failed: %d\n", error);
+ else
+ dev_dbg(dev, "firmware update successfully done.\n");
+
+ /*
+ * Redetect trackpad device states because firmware update process
+ * will reset trackpad device into bootloader mode.
+ */
+ cyapa_detect(cyapa);
+
+ mutex_unlock(&cyapa->state_sync_lock);
+
+ return error ? error : count;
+}
+
+static ssize_t cyapa_calibrate_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct cyapa *cyapa = dev_get_drvdata(dev);
+ int error;
+
+ error = mutex_lock_interruptible(&cyapa->state_sync_lock);
+ if (error)
+ return error;
+
+ if (!cyapa->ops->calibrate_store) {
+ dev_err(dev, "Calibrate operation not supported.\n");
+ error = -ENOTSUPP;
+ } else {
+ error = cyapa->ops->calibrate_store(dev, attr, buf, count);
+ }
+
+ mutex_unlock(&cyapa->state_sync_lock);
+ return error < 0 ? error : count;
+}
+
+static ssize_t cyapa_show_baseline(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cyapa *cyapa = dev_get_drvdata(dev);
+ ssize_t error;
+
+ error = mutex_lock_interruptible(&cyapa->state_sync_lock);
+ if (error)
+ return error;
+
+ if (!cyapa->ops->show_baseline) {
+ dev_err(dev, "Calibrate operation not supported.\n");
+ error = -ENOTSUPP;
+ } else {
+ error = cyapa->ops->show_baseline(dev, attr, buf);
+ }
+
+ mutex_unlock(&cyapa->state_sync_lock);
+ return error;
+}
+
+static char *cyapa_state_to_string(struct cyapa *cyapa)
+{
+ switch (cyapa->state) {
+ case CYAPA_STATE_BL_BUSY:
+ return "bootloader busy";
+ case CYAPA_STATE_BL_IDLE:
+ return "bootloader idle";
+ case CYAPA_STATE_BL_ACTIVE:
+ return "bootloader active";
+ case CYAPA_STATE_GEN5_BL:
+ return "bootloader";
+ case CYAPA_STATE_OP:
+ case CYAPA_STATE_GEN5_APP:
+ return "operational";
+ default:
+ return "invalid mode";
+ }
+}
+
+static ssize_t cyapa_show_mode(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct cyapa *cyapa = dev_get_drvdata(dev);
+ int size;
+ int error;
+
+ error = mutex_lock_interruptible(&cyapa->state_sync_lock);
+ if (error)
+ return error;
+
+ size = scnprintf(buf, PAGE_SIZE, "gen%d %s\n",
+ cyapa->gen, cyapa_state_to_string(cyapa));
+
+ mutex_unlock(&cyapa->state_sync_lock);
+ return size;
+}
+
+static DEVICE_ATTR(firmware_version, S_IRUGO, cyapa_show_fm_ver, NULL);
+static DEVICE_ATTR(product_id, S_IRUGO, cyapa_show_product_id, NULL);
+static DEVICE_ATTR(update_fw, S_IWUSR, NULL, cyapa_update_fw_store);
+static DEVICE_ATTR(baseline, S_IRUGO, cyapa_show_baseline, NULL);
+static DEVICE_ATTR(calibrate, S_IWUSR, NULL, cyapa_calibrate_store);
+static DEVICE_ATTR(mode, S_IRUGO, cyapa_show_mode, NULL);
+
+static struct attribute *cyapa_sysfs_entries[] = {
+ &dev_attr_firmware_version.attr,
+ &dev_attr_product_id.attr,
+ &dev_attr_update_fw.attr,
+ &dev_attr_baseline.attr,
+ &dev_attr_calibrate.attr,
+ &dev_attr_mode.attr,
+ NULL,
+};
+
+static const struct attribute_group cyapa_sysfs_group = {
+ .attrs = cyapa_sysfs_entries,
+};
+
+static void cyapa_remove_sysfs_group(void *data)
+{
+ struct cyapa *cyapa = data;
+
+ sysfs_remove_group(&cyapa->client->dev.kobj, &cyapa_sysfs_group);
+}
+
/*
* Returns:
* 0 Driver and device initialization successfully done.
@@ -773,6 +1027,19 @@ static int cyapa_probe(struct i2c_client *client,
return error;
}
+ error = sysfs_create_group(&client->dev.kobj, &cyapa_sysfs_group);
+ if (error) {
+ dev_err(dev, "failed to create sysfs entries: %d\n", error);
+ return error;
+ }
+
+ error = devm_add_action(dev, cyapa_remove_sysfs_group, cyapa);
+ if (error) {
+ cyapa_remove_sysfs_group(cyapa);
+ dev_err(dev, "failed to add sysfs cleanup action: %d\n", error);
+ return error;
+ }
+
#ifdef CONFIG_PM_SLEEP
if (device_can_wakeup(dev)) {
error = sysfs_merge_group(&client->dev.kobj,
--
1.9.1
^ permalink raw reply related
* [PATCH v10 06/18] input: cyapa: add runtime power management interfaces supported for the device
From: Dudley Du @ 2014-11-14 6:03 UTC (permalink / raw)
To: dmitry.torokhov, rydberg; +Cc: Dudley Du, bleung, linux-input, linux-kernel
In-Reply-To: <1415945004-19868-1-git-send-email-dudl@cypress.com>
Add runtime_suspend_scanrate_ms power management interfaces in device's
power group, so users or applications can control the runtime power
management strategy of trackpad device as their requirements.
TEST=test on Chromebooks.
Signed-off-by: Dudley Du <dudl@cypress.com>
---
drivers/input/mouse/cyapa.c | 167 ++++++++++++++++++++++++++++++++++++++++++++
drivers/input/mouse/cyapa.h | 4 ++
2 files changed, 171 insertions(+)
diff --git a/drivers/input/mouse/cyapa.c b/drivers/input/mouse/cyapa.c
index bdb7b0f..51c0d86 100644
--- a/drivers/input/mouse/cyapa.c
+++ b/drivers/input/mouse/cyapa.c
@@ -23,6 +23,7 @@
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
+#include <linux/pm_runtime.h>
#include "cyapa.h"
@@ -148,6 +149,7 @@ static void cyapa_close(struct input_dev *input)
struct cyapa *cyapa = input_get_drvdata(input);
disable_irq(cyapa->client->irq);
+ pm_runtime_disable(&cyapa->client->dev);
if (mutex_lock_interruptible(&cyapa->state_sync_lock))
return;
@@ -301,6 +303,7 @@ static irqreturn_t cyapa_irq(int irq, void *dev_id)
struct device *dev = &cyapa->client->dev;
bool cont;
+ pm_runtime_get_sync(dev);
if (device_may_wakeup(dev))
pm_wakeup_event(dev, 0);
@@ -325,6 +328,8 @@ static irqreturn_t cyapa_irq(int irq, void *dev_id)
}
out:
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_sync_autosuspend(dev);
return IRQ_HANDLED;
}
@@ -587,6 +592,116 @@ static void cyapa_remove_power_wakeup_group(void *data)
}
#endif /* CONFIG_PM_SLEEP */
+#ifdef CONFIG_PM_RUNTIME
+static ssize_t cyapa_show_rt_suspend_scanrate(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct cyapa *cyapa = dev_get_drvdata(dev);
+ u8 pwr_cmd;
+ u16 sleep_time;
+ int error;
+
+ error = mutex_lock_interruptible(&cyapa->state_sync_lock);
+ if (error)
+ return error;
+ pwr_cmd = cyapa->runtime_suspend_power_mode;
+ sleep_time = cyapa->runtime_suspend_sleep_time;
+ mutex_unlock(&cyapa->state_sync_lock);
+
+ if (cyapa->gen == CYAPA_GEN3)
+ return scnprintf(buf, PAGE_SIZE, "%u\n",
+ cyapa_pwr_cmd_to_sleep_time(pwr_cmd));
+ return scnprintf(buf, PAGE_SIZE, "%u\n", sleep_time);
+}
+
+static ssize_t cyapa_update_rt_suspend_scanrate(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct cyapa *cyapa = dev_get_drvdata(dev);
+ u16 time;
+ int error;
+
+ if (buf == NULL || count == 0 || kstrtou16(buf, 10, &time)) {
+ dev_err(dev, "invalid runtime suspend scanrate ms parameter\n");
+ return -EINVAL;
+ }
+
+ /*
+ * When the suspend scanrate is changed, pm_runtime_get to resume
+ * a potentially suspended device, update to the new pwr_cmd
+ * and then pm_runtime_put to suspend into the new power mode.
+ */
+ pm_runtime_get_sync(dev);
+ error = mutex_lock_interruptible(&cyapa->state_sync_lock);
+ if (error)
+ return error;
+ cyapa->runtime_suspend_sleep_time = max_t(u16, time, 1000);
+ cyapa->runtime_suspend_power_mode =
+ cyapa_sleep_time_to_pwr_cmd(cyapa->runtime_suspend_sleep_time);
+ mutex_unlock(&cyapa->state_sync_lock);
+ pm_runtime_put_sync_autosuspend(dev);
+
+ return count;
+}
+
+static DEVICE_ATTR(runtime_suspend_scanrate_ms, S_IRUGO|S_IWUSR,
+ cyapa_show_rt_suspend_scanrate,
+ cyapa_update_rt_suspend_scanrate);
+
+static struct attribute *cyapa_power_runtime_entries[] = {
+ &dev_attr_runtime_suspend_scanrate_ms.attr,
+ NULL,
+};
+
+static const struct attribute_group cyapa_power_runtime_group = {
+ .name = power_group_name,
+ .attrs = cyapa_power_runtime_entries,
+};
+
+static void cyapa_remove_power_runtime_group(void *data)
+{
+ struct cyapa *cyapa = data;
+
+ sysfs_unmerge_group(&cyapa->client->dev.kobj,
+ &cyapa_power_runtime_group);
+}
+
+static int cyapa_start_runtime(struct cyapa *cyapa)
+{
+ struct device *dev = &cyapa->client->dev;
+ int error;
+
+ cyapa->runtime_suspend_power_mode = PWR_MODE_IDLE;
+ cyapa->runtime_suspend_sleep_time =
+ cyapa_pwr_cmd_to_sleep_time(cyapa->runtime_suspend_power_mode);
+
+ error = sysfs_merge_group(&dev->kobj, &cyapa_power_runtime_group);
+ if (error) {
+ dev_err(dev,
+ "failed to create power runtime group: %d\n", error);
+ return error;
+ }
+
+ error = devm_add_action(dev, cyapa_remove_power_runtime_group, cyapa);
+ if (error) {
+ cyapa_remove_power_runtime_group(cyapa);
+ dev_err(dev,
+ "failed to add power runtime cleanup action: %d\n",
+ error);
+ return error;
+ }
+
+ pm_runtime_set_active(dev);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_autosuspend_delay(dev, AUTOSUSPEND_DELAY);
+ pm_runtime_enable(dev);
+
+ return 0;
+}
+#endif /* CONFIG_PM_RUNTIME */
+
/*
* Returns:
* 0 Driver and device initialization successfully done.
@@ -679,6 +794,14 @@ static int cyapa_probe(struct i2c_client *client,
}
#endif /* CONFIG_PM_SLEEP */
+#ifdef CONFIG_PM_RUNTIME
+ error = cyapa_start_runtime(cyapa);
+ if (error) {
+ dev_err(dev, "failed to start pm_runtime: %d\n", error);
+ return error;
+ }
+#endif /* CONFIG_PM_RUNTIME */
+
error = devm_request_threaded_irq(dev,
client->irq,
NULL,
@@ -713,6 +836,7 @@ static int __maybe_unused cyapa_suspend(struct device *dev)
if (error)
return error;
+ pm_runtime_disable(dev);
/*
* Disable IRQ to avoid the command response interrupt cause system
* suspending process interrupted and failed.
@@ -735,6 +859,8 @@ static int __maybe_unused cyapa_suspend(struct device *dev)
error);
}
+ pm_runtime_set_suspended(dev);
+
if (device_may_wakeup(dev))
cyapa->irq_wake = (enable_irq_wake(cyapa->client->irq) == 0);
@@ -754,17 +880,58 @@ static int __maybe_unused cyapa_resume(struct device *dev)
cyapa->irq_wake = false;
}
+ /* Runtime set active to reflect active state. */
+ pm_runtime_set_active(dev);
+
/* Re-detect device state and reset it to active power mode. */
cyapa_detect(cyapa);
enable_irq(cyapa->client->irq);
+ pm_runtime_enable(dev);
+
mutex_unlock(&cyapa->state_sync_lock);
return 0;
}
+#ifdef CONFIG_PM_RUNTIME
+static int cyapa_runtime_suspend(struct device *dev)
+{
+ struct cyapa *cyapa = dev_get_drvdata(dev);
+ int error;
+
+ if (cyapa->operational && cyapa->ops->set_power_mode) {
+ /* Set trackpad device to idle mode. */
+ error = cyapa->ops->set_power_mode(cyapa,
+ cyapa->runtime_suspend_power_mode,
+ cyapa->runtime_suspend_sleep_time);
+ if (error)
+ dev_err(dev, "runtime suspend failed: %d\n", error);
+ }
+
+ return 0;
+}
+
+static int cyapa_runtime_resume(struct device *dev)
+{
+ struct cyapa *cyapa = dev_get_drvdata(dev);
+ int error;
+
+ if (cyapa->operational && cyapa->ops->set_power_mode) {
+ /* Resume to full active mode. */
+ error = cyapa->ops->set_power_mode(cyapa,
+ PWR_MODE_FULL_ACTIVE, 0);
+ if (error)
+ dev_err(dev, "runtime resume failed: %d\n", error);
+ }
+
+ return 0;
+}
+#endif /* CONFIG_PM_RUNTIME */
+
static const struct dev_pm_ops cyapa_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(cyapa_suspend, cyapa_resume)
+ SET_RUNTIME_PM_OPS(cyapa_runtime_suspend, cyapa_runtime_resume, NULL)
};
static const struct i2c_device_id cyapa_id_table[] = {
diff --git a/drivers/input/mouse/cyapa.h b/drivers/input/mouse/cyapa.h
index c17284e..2dc29c1 100644
--- a/drivers/input/mouse/cyapa.h
+++ b/drivers/input/mouse/cyapa.h
@@ -262,6 +262,10 @@ struct cyapa {
/* power mode settings */
u8 suspend_power_mode;
u16 suspend_sleep_time;
+#ifdef CONFIG_PM_RUNTIME
+ u8 runtime_suspend_power_mode;
+ u16 runtime_suspend_sleep_time;
+#endif /* CONFIG_PM_RUNTIME */
u8 dev_pwr_mode;
u16 dev_sleep_time;
--
1.9.1
^ permalink raw reply related
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