Linux Input/HID development
 help / color / mirror / Atom feed
* Re: [PATCH] Input - wacom: put a flag when the led are initialized
From: Benjamin Tissoires @ 2014-06-16 16:33 UTC (permalink / raw)
  To: Ping Cheng
  Cc: Dmitry Torokhov, Jason Gerecke, linux-input,
	linux-kernel@vger.kernel.org
In-Reply-To: <CAF8JNhKBodDC0=M0evsN4xSn7+qPvQZ52XF+mfusgT_1L3Ks8w@mail.gmail.com>

Hi Ping,

On Jun 13 2014 or thereabouts, Ping Cheng wrote:
> Hi Benjamin,
> 
> On Fri, Jun 13, 2014 at 1:29 PM, Benjamin Tissoires
> <benjamin.tissoires@redhat.com> wrote:
> > This solves a bug with the wireless receiver:
> 
> Your patch does get rid of the crash. But, it does not fix it at the
> root cause.

True, it fixes the crash, does not get rid of the root cause, but it is
still required. See later.

> 
> > - at plug, the wireless receiver does not know which Wacom device it is
> >   connected to, so it does not actually creates all the LEDs
> 
> This is the root cause - LEDs are not created for wireless devices.
> Neither here, nor later when a real device is connected.

Yep

> 
> > - when the tablet connects, wacom->wacom_wac.features.type is set to the
> >   proper device so that wacom_wac can understand the packets
> 
> LEDs are not created for any wireless devices since we don't call
> wacom_initialize_leds() when real tablets are connected.

Yep

> 
> > - when the receiver is unplugged, it detects that a LED should have been
> >   created (based on wacom->wacom_wac.features.type) and tries to remove
> >   it: crash when removing the sysfs group.
> 
> When receiver is unplugged, it remembers the last tablet that
> connected to it. If that tablet supports LEDs, wacom_destroy_leds() is
> called. But, no LEDs were initialized. That's why it crashes.

Yep

> 
> > Side effect, we can now safely call several times wacom_destroy_leds().
> 
> led_initialized will never be true if we keep wacom_initialize_leds()
> inside probe().

If you are talking about wireless devices only, yes, this value will
never be true. That's the purpose of this patch actually :)

> 
> To make initialize_leds() and desctroy_leds() work for wireless
> devices, we need to move them to wacom_wireless_work() where we know
> what type of tablet is connected/disconnected.

Unfortunately, this does not work:
- we *can* create the LEDs sysfs in the wireless work
- this will prevent the crash
- the user will think it can control the LEDs
- actually these LEDs control will do nothing because LEDs handling for
  wireless devices goes through the WL interface, and not the PEN
  interface of the WL receiver.
- we need to implement a specific led_handling for the wireless receiver
  (which will need to know which type of tablet is connected to it)
- we still need a way to say that the pen intf which is declared as the
  connected device does not has a LED sysfs.

We could add a quirk to the wacom_wac->features saying that the
connection is wireless, so there is no LED attached to the interface.

Still, there will be some work to do to properly handle the LED
configuration from the WL receiver. This work has to be done in the
kernel, but also in the user space (g-s-d) because now, the led control
will not be on the pen device, but on a plain usb device without input.

If you prefer, I can add such a quirk. But my only concern is here to
fix the kernel oops, not to add features which would require more
testing across different hardware and development on the user space
side.

> 
> > Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> 
> Thank you for your support. But, sorry
> 
> NAKed-by: Ping Cheng <pingc@wacom.com>

Please reconsider it or validate the quirk approach I mentioned.

Cheers,
Benjamin

^ permalink raw reply

* [PATCH v6] leds: USB: HID: Add support for MSI GT683R led panels
From: Janne Kanniainen @ 2014-06-16 17:23 UTC (permalink / raw)
  To: jkosina
  Cc: johan, cooloney, linux-kernel, linux-leds, linux-usb, linux-input,
	Janne Kanniainen
In-Reply-To: <20140613075411.GG10940@localhost>

This driver adds support for USB controlled led panels that exists in
MSI GT683R laptop

Signed-off-by: Janne Kanniainen <janne.kanniainen@gmail.com>
---
Changes in v2:
	- sorted headers to alphabetic order
	- using devm_kzalloc
	- using BIT(n)
	- using usb_control_msg instead of usb_submit_urb
	- removing unneeded code

Changes in v3:
	- implemented as HID device
	- some cleanups and bug fixes

Changes in v4:
	- more cleanups
	- support for selecting leds
	- suppport for selecting status

Changes in v5:
	- mode attribute documented under Documentation/ABI
	- made array for led_classdev
	- led devices uses now recommended naming scheme

Changes in v6:
	- flush_work added
	- using hid device name instead of hard coded gt683r
	- allocating name buffers with devm_kzalloc

 .../ABI/testing/sysfs-class-hid-driver-gt683r      |  14 +
 drivers/hid/Kconfig                                |  16 ++
 drivers/hid/Makefile                               |   1 +
 drivers/hid/hid-core.c                             |   1 +
 drivers/hid/hid-gt683r.c                           | 318 +++++++++++++++++++++
 drivers/hid/hid-ids.h                              |   2 +-
 drivers/hid/usbhid/hid-quirks.c                    |   2 +-
 7 files changed, 352 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
 create mode 100644 drivers/hid/hid-gt683r.c

diff --git a/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
new file mode 100644
index 0000000..6d0bd80
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
@@ -0,0 +1,14 @@
+What:		/sys/class/hidraw/<hidraw>/device/mode
+Date:		Jun 2014
+KernelVersion:	3.17
+Contact:	Janne Kanniainen <janne.kanniainen@gmail.com>
+Description:
+		Set the mode of LEDs
+
+		0 - normal
+		1 - audio
+		2 - breathing
+
+		Normal: LEDs are on
+		Audio:  LEDs brightness depends on sound level
+		Breathing: LEDs brightness varies at human breathing rate
\ No newline at end of file
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 7af9d0b..b88cabd 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -261,6 +261,22 @@ config HOLTEK_FF
 	  Say Y here if you have a Holtek On Line Grip based game controller
 	  and want to have force feedback support for it.
 
+config HID_GT683R
+	tristate "MSI GT68xR LED support"
+	depends on LEDS_CLASS && USB_HID
+	---help---
+	Say Y here if you want to enable support for the MSI GT68xR LEDS
+
+	This driver support following modes:
+	  - Normal: LEDs are on
+	  - Audio:  LEDs brightness depends on sound level
+	  - Breathing: LEDs brightness varies at human breathing rate
+
+	You can also select which leds you want to enable.
+
+	Currently the following devices are know to be supported:
+	  - MSI GT683R
+
 config HID_HUION
 	tristate "Huion tablets"
 	depends on USB_HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index fc712dd..7129311 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_HID_EMS_FF)	+= hid-emsff.o
 obj-$(CONFIG_HID_ELECOM)	+= hid-elecom.o
 obj-$(CONFIG_HID_ELO)		+= hid-elo.o
 obj-$(CONFIG_HID_EZKEY)		+= hid-ezkey.o
+obj-$(CONFIG_HID_GT683R)	+= hid-gt683r.o
 obj-$(CONFIG_HID_GYRATION)	+= hid-gyration.o
 obj-$(CONFIG_HID_HOLTEK)	+= hid-holtek-kbd.o
 obj-$(CONFIG_HID_HOLTEK)	+= hid-holtek-mouse.o
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index da52279..ec88fdb 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1827,6 +1827,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
 	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_2) },
diff --git a/drivers/hid/hid-gt683r.c b/drivers/hid/hid-gt683r.c
new file mode 100644
index 0000000..132cc54
--- /dev/null
+++ b/drivers/hid/hid-gt683r.c
@@ -0,0 +1,318 @@
+/*
+ * MSI GT683R led driver
+ *
+ * Copyright (c) 2014 Janne Kanniainen <janne.kanniainen@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+#define GT683R_BUFFER_SIZE			8
+
+/*
+ * GT683R_LED_OFF: all LEDs are off
+ * GT683R_LED_AUDIO: LEDs brightness depends
+ * on sound level
+ * GT683R_LED_BREATHING: LEDs brightness varies
+ * at human breathing rate
+ * GT683R_LED_NORMAL: LEDs are on
+ */
+enum gt683r_led_mode {
+	GT683R_LED_OFF = 0,
+	GT683R_LED_AUDIO = 2,
+	GT683R_LED_BREATHING = 3,
+	GT683R_LED_NORMAL = 5
+};
+
+enum gt683r_panels {
+	GT683R_LED_BACK,
+	GT683R_LED_SIDE,
+	GT683R_LED_FRONT,
+	GT683R_LED_COUNT,
+};
+
+static const char * const gt683r_panel_names[] = {
+	"back",
+	"side",
+	"front",
+};
+
+struct gt683r_led {
+	struct hid_device *hdev;
+	struct led_classdev led_devs[GT683R_LED_COUNT];
+	struct mutex lock;
+	struct work_struct work;
+	enum led_brightness brightnesses[GT683R_LED_COUNT];
+	enum gt683r_led_mode mode;
+};
+
+static const struct hid_device_id gt683r_led_id[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
+	{ }
+};
+
+static void gt683r_brightness_set(struct led_classdev *led_cdev,
+				enum led_brightness brightness)
+{
+	int i;
+	struct device *dev = led_cdev->dev->parent;
+	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+	struct gt683r_led *led = hid_get_drvdata(hdev);
+
+	for (i = 0; i < GT683R_LED_COUNT; i++) {
+		if (&led->led_devs[i] == led_cdev)
+			break;
+	}
+
+	if (i < GT683R_LED_COUNT) {
+		led->brightnesses[i] = brightness;
+		schedule_work(&led->work);
+	}
+}
+
+static ssize_t mode_show(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	u8 sysfs_mode;
+	struct hid_device *hdev =
+		container_of(dev, struct hid_device, dev);
+	struct gt683r_led *led = hid_get_drvdata(hdev);
+
+	if (led->mode == GT683R_LED_NORMAL)
+		sysfs_mode = 0;
+	else if (led->mode == GT683R_LED_AUDIO)
+		sysfs_mode = 1;
+	else
+		sysfs_mode = 2;
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n", sysfs_mode);
+}
+
+static ssize_t mode_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	u8 sysfs_mode;
+	struct hid_device *hdev =
+		container_of(dev, struct hid_device, dev);
+	struct gt683r_led *led = hid_get_drvdata(hdev);
+
+	mutex_lock(&led->lock);
+
+	if (kstrtou8(buf, 10, &sysfs_mode) || sysfs_mode > 2) {
+		count = -EINVAL;
+		goto fail;
+	}
+
+	if (sysfs_mode == 0)
+		led->mode = GT683R_LED_NORMAL;
+	else if (sysfs_mode == 1)
+		led->mode = GT683R_LED_AUDIO;
+	else
+		led->mode = GT683R_LED_BREATHING;
+
+fail:
+	mutex_unlock(&led->lock);
+
+	if (count != -EINVAL)
+		schedule_work(&led->work);
+
+	return count;
+}
+
+static int gt683r_led_snd_msg(struct gt683r_led *led, u8 *msg)
+{
+	int ret;
+
+	ret = hid_hw_raw_request(led->hdev, msg[0], msg, GT683R_BUFFER_SIZE,
+				HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
+	if (ret != GT683R_BUFFER_SIZE) {
+		hid_err(led->hdev,
+			"failed to send set report request: %i\n", ret);
+		if (ret < 0)
+			return ret;
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int gt683r_leds_set(struct gt683r_led *led, u8 leds)
+{
+	int ret;
+	u8 *buffer;
+
+	buffer = kzalloc(GT683R_BUFFER_SIZE, GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
+
+	buffer[0] = 0x01;
+	buffer[1] = 0x02;
+	buffer[2] = 0x30;
+	buffer[3] = leds;
+	ret = gt683r_led_snd_msg(led, buffer);
+
+	kfree(buffer);
+	return ret;
+}
+
+static int gt683r_mode_set(struct gt683r_led *led, u8 mode)
+{
+	int ret;
+	u8 *buffer;
+
+	buffer = kzalloc(GT683R_BUFFER_SIZE, GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
+
+	buffer[0] = 0x01;
+	buffer[1] = 0x02;
+	buffer[2] = 0x20;
+	buffer[3] = mode;
+	buffer[4] = 0x01;
+	ret = gt683r_led_snd_msg(led, buffer);
+
+	kfree(buffer);
+	return ret;
+}
+
+static void gt683r_led_work(struct work_struct *work)
+{
+	u8 leds = 0, mode;
+	struct gt683r_led *led =
+		container_of(work, struct gt683r_led, work);
+
+	mutex_lock(&led->lock);
+
+	if (led->brightnesses[GT683R_LED_BACK])
+		leds |= BIT(GT683R_LED_BACK);
+
+	if (led->brightnesses[GT683R_LED_SIDE])
+		leds |= BIT(GT683R_LED_SIDE);
+
+	if (led->brightnesses[GT683R_LED_FRONT])
+		leds |= BIT(GT683R_LED_FRONT);
+
+	if (gt683r_leds_set(led, leds))
+		goto fail;
+
+	if (leds)
+		mode = led->mode;
+	else
+		mode = GT683R_LED_OFF;
+
+	gt683r_mode_set(led, mode);
+fail:
+	mutex_unlock(&led->lock);
+}
+
+static DEVICE_ATTR_RW(mode);
+
+static int gt683r_led_probe(struct hid_device *hdev,
+			const struct hid_device_id *id)
+{
+	int ret, i, name_sz;
+	char *name;
+	struct gt683r_led *led;
+
+	led = devm_kzalloc(&hdev->dev, sizeof(struct gt683r_led), GFP_KERNEL);
+	if (!led)
+		return -ENOMEM;
+
+	led->mode = GT683R_LED_NORMAL;
+	led->hdev = hdev;
+	hid_set_drvdata(hdev, led);
+
+	ret = hid_parse(hdev);
+	if (ret) {
+		hid_err(hdev, "hid parsing failed\n");
+		goto fail;
+	}
+
+	ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
+	if (ret) {
+		hid_err(hdev, "hw start failed\n");
+		goto fail;
+	}
+
+	for (i = 0; i < GT683R_LED_COUNT; i++) {
+		name_sz = strlen(dev_name(&hdev->dev)) +
+			strlen(gt683r_panel_names[i]) + 3;
+
+		name = devm_kzalloc(&hdev->dev, sizeof(char) * name_sz,
+				GFP_KERNEL);
+
+		snprintf(name, name_sz, "%s::%s",
+			dev_name(&hdev->dev), gt683r_panel_names[i]);
+		led->led_devs[i].name = name;
+		led->led_devs[i].max_brightness = 1;
+		led->led_devs[i].brightness_set = gt683r_brightness_set;
+		ret = led_classdev_register(&hdev->dev, &led->led_devs[i]);
+		if (ret) {
+			hid_err(hdev, "could not register led device\n");
+			goto fail2;
+		}
+	}
+
+	ret = device_create_file(&led->hdev->dev,
+				&dev_attr_mode);
+	if (ret) {
+		hid_err(hdev, "could not make mode attribute file\n");
+		goto fail2;
+	}
+
+	mutex_init(&led->lock);
+	INIT_WORK(&led->work, gt683r_led_work);
+
+	return 0;
+
+fail2:
+	for (; i > 0; i--)
+		led_classdev_unregister(&led->led_devs[i]);
+	hid_hw_stop(hdev);
+fail:
+	return ret;
+}
+
+static void gt683r_led_remove(struct hid_device *hdev)
+{
+	int i;
+	struct gt683r_led *led = hid_get_drvdata(hdev);
+
+	for (i = 0; i < GT683R_LED_COUNT; i++)
+		led_classdev_unregister(&led->led_devs[i]);
+	device_remove_file(&hdev->dev,
+			&dev_attr_mode);
+	flush_work(&led->work);
+	hid_hw_stop(hdev);
+}
+
+static struct hid_driver gt683r_led_driver = {
+	.probe = gt683r_led_probe,
+	.remove = gt683r_led_remove,
+	.name = "gt683r_led",
+	.id_table = gt683r_led_id,
+};
+
+module_hid_driver(gt683r_led_driver);
+
+MODULE_AUTHOR("Janne Kanniainen");
+MODULE_DESCRIPTION("MSI GT683R led driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 34bb220..3692d37 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -641,7 +641,7 @@
 #define USB_DEVICE_ID_GENIUS_KB29E	0x3004
 
 #define USB_VENDOR_ID_MSI		0x1770
-#define USB_DEVICE_ID_MSI_GX680R_LED_PANEL	0xff00
+#define USB_DEVICE_ID_MSI_GT683R_LED_PANEL 0xff00
 
 #define USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR 0x0400
 #define USB_DEVICE_ID_N_S_HARMONY	0xc359
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index 8e4ddb3..c640e1d 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -73,7 +73,7 @@ static const struct hid_blacklist {
 	{ USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS },
 	{ USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET },
 	{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET },
-	{ USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GX680R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
+	{ USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
 	{ USB_VENDOR_ID_NEXIO, USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750, HID_QUIRK_NO_INIT_REPORTS },
 	{ USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS },
 	{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NO_INIT_REPORTS },
-- 
1.9.2

^ permalink raw reply related

* [PATCH 0/2] touchscreen: sun4i-ts: fix reported temperature on A10
From: Hans de Goede @ 2014-06-16 18:24 UTC (permalink / raw)
  To: Dmitry Torokhov, Maxime Ripard
  Cc: Tong Zhang, linux-input-u79uwXL29TY76Z2rM5mHXA, LM Sensors,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw

Hi Dmitry and Maxime,

I've learned today that the temperature reported on the A10 / sun4i SoC family
is aprox twice too high, since the A10 rtp controller temperature register
seems to have double the resolution (one bit more) then that of the A13
(sun5i) and later.

Dmitry, can you please add the sun4i-ts.c patch to your tree? Ideally
as a bug-fix for 3.16 (where this driver was first added).

Maxime, can you please add the dts patch to your tree ?

Thanks & Regards,

Hans

^ permalink raw reply

* [PATCH 1/2] touchscreen: sun4i-ts: A10 (sun4i) has double the temperature precision
From: Hans de Goede @ 2014-06-16 18:24 UTC (permalink / raw)
  To: Dmitry Torokhov, Maxime Ripard
  Cc: Tong Zhang, linux-input-u79uwXL29TY76Z2rM5mHXA, LM Sensors,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Hans de Goede
In-Reply-To: <1402943069-19420-1-git-send-email-hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>

Testing has revealed that the temperature in the rtp controller of the A10
(sun4i) SoC has a resolution of 50 milli degrees / step, where as the
A13 (sun5i) and later models have 100 milli degrees / step.

Add a new sun5i-a13-ts compatible to differentiate the newer models and
set the resolution based on the compatible string.

This fixes the temperature reported on the A10 being twice as high as expected.

Reported-by: Tong Zhang <lovewilliam-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
 Documentation/devicetree/bindings/input/touchscreen/sun4i.txt | 2 +-
 drivers/input/touchscreen/sun4i-ts.c                          | 8 +++++++-
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/Documentation/devicetree/bindings/input/touchscreen/sun4i.txt b/Documentation/devicetree/bindings/input/touchscreen/sun4i.txt
index aef5779..5106709 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/sun4i.txt
+++ b/Documentation/devicetree/bindings/input/touchscreen/sun4i.txt
@@ -2,7 +2,7 @@ sun4i resistive touchscreen controller
 --------------------------------------
 
 Required properties:
- - compatible: "allwinner,sun4i-a10-ts"
+ - compatible: "allwinner,sun4i-a10-ts" or "allwinner,sun5i-a13-ts"
  - reg: mmio address range of the chip
  - interrupts: interrupt to which the chip is connected
 
diff --git a/drivers/input/touchscreen/sun4i-ts.c b/drivers/input/touchscreen/sun4i-ts.c
index 2ba8260..5661be0 100644
--- a/drivers/input/touchscreen/sun4i-ts.c
+++ b/drivers/input/touchscreen/sun4i-ts.c
@@ -111,6 +111,7 @@ struct sun4i_ts_data {
 	unsigned int irq;
 	bool ignore_fifo_data;
 	int temp_data;
+	int temp_step;
 };
 
 static void sun4i_ts_irq_handle_input(struct sun4i_ts_data *ts, u32 reg_val)
@@ -189,7 +190,7 @@ static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
 	if (ts->temp_data == -1)
 		return -EAGAIN;
 
-	return sprintf(buf, "%d\n", (ts->temp_data - 1447) * 100);
+	return sprintf(buf, "%d\n", (ts->temp_data - 1447) * ts->temp_step);
 }
 
 static ssize_t show_temp_label(struct device *dev,
@@ -224,6 +225,10 @@ static int sun4i_ts_probe(struct platform_device *pdev)
 	ts->dev = dev;
 	ts->ignore_fifo_data = true;
 	ts->temp_data = -1;
+	if (of_device_is_compatible(np, "allwinner,sun4i-a10-ts"))
+		ts->temp_step = 50;
+	else
+		ts->temp_step = 100;
 
 	ts_attached = of_property_read_bool(np, "allwinner,ts-attached");
 	if (ts_attached) {
@@ -318,6 +323,7 @@ static int sun4i_ts_remove(struct platform_device *pdev)
 
 static const struct of_device_id sun4i_ts_of_match[] = {
 	{ .compatible = "allwinner,sun4i-a10-ts", },
+	{ .compatible = "allwinner,sun5i-a13-ts", },
 	{ /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, sun4i_ts_of_match);
-- 
2.0.0

^ permalink raw reply related

* [PATCH 2/2] ARM: dts: sunxi: Adjust touchscreen compatible for sun5i and later
From: Hans de Goede @ 2014-06-16 18:24 UTC (permalink / raw)
  To: Dmitry Torokhov, Maxime Ripard
  Cc: Tong Zhang, linux-input-u79uwXL29TY76Z2rM5mHXA, LM Sensors,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, devicetree,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw, Hans de Goede
In-Reply-To: <1402943069-19420-1-git-send-email-hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>

The touchscreen controller in the A13 and later has a different temperature
resulution compated to the one in the original A10, change the compatible
for the A13 and later so that the kernel will use the correct resolution.

Reported-by: Tong Zhang <lovewilliam-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
 arch/arm/boot/dts/sun5i-a10s.dtsi | 2 +-
 arch/arm/boot/dts/sun5i-a13.dtsi  | 2 +-
 arch/arm/boot/dts/sun7i-a20.dtsi  | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/arch/arm/boot/dts/sun5i-a10s.dtsi b/arch/arm/boot/dts/sun5i-a10s.dtsi
index 6835b88..25549c1 100644
--- a/arch/arm/boot/dts/sun5i-a10s.dtsi
+++ b/arch/arm/boot/dts/sun5i-a10s.dtsi
@@ -519,7 +519,7 @@
 		};
 
 		rtp: rtp@01c25000 {
-			compatible = "allwinner,sun4i-a10-ts";
+			compatible = "allwinner,sun5i-a13-ts";
 			reg = <0x01c25000 0x100>;
 			interrupts = <29>;
 		};
diff --git a/arch/arm/boot/dts/sun5i-a13.dtsi b/arch/arm/boot/dts/sun5i-a13.dtsi
index 206b6f8..be28b3b 100644
--- a/arch/arm/boot/dts/sun5i-a13.dtsi
+++ b/arch/arm/boot/dts/sun5i-a13.dtsi
@@ -467,7 +467,7 @@
 		};
 
 		rtp: rtp@01c25000 {
-			compatible = "allwinner,sun4i-a10-ts";
+			compatible = "allwinner,sun5i-a13-ts";
 			reg = <0x01c25000 0x100>;
 			interrupts = <29>;
 		};
diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi
index 8029a75..87c7d33 100644
--- a/arch/arm/boot/dts/sun7i-a20.dtsi
+++ b/arch/arm/boot/dts/sun7i-a20.dtsi
@@ -823,7 +823,7 @@
 		};
 
 		rtp: rtp@01c25000 {
-			compatible = "allwinner,sun4i-a10-ts";
+			compatible = "allwinner,sun5i-a13-ts";
 			reg = <0x01c25000 0x100>;
 			interrupts = <0 29 4>;
 		};
-- 
2.0.0

^ permalink raw reply related

* Re: [PATCH v3 1/2] Rename hid-lenovo-tpkbd to hid-lenovo, so we can add other keyboards
From: Antonio Ospite @ 2014-06-16 20:40 UTC (permalink / raw)
  To: Jamie Lentin; +Cc: Jiri Kosina, Alexander Clouter, linux-input, linux-kernel
In-Reply-To: <1402864790-7478-2-git-send-email-jm@lentin.co.uk>

On Sun, 15 Jun 2014 21:39:49 +0100
Jamie Lentin <jm@lentin.co.uk> wrote:

Almost there :)

> Signed-off-by: Jamie Lentin <jm@lentin.co.uk>
> ---
>  drivers/hid/Kconfig                              |  14 +-
>  drivers/hid/Makefile                             |   2 +-
>  drivers/hid/hid-core.c                           |   2 +-
>  drivers/hid/{hid-lenovo-tpkbd.c => hid-lenovo.c} | 233 +++++++++++++----------
>  4 files changed, 142 insertions(+), 109 deletions(-)
>  rename drivers/hid/{hid-lenovo-tpkbd.c => hid-lenovo.c} (59%)
> 
> diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
> index f722001..dd07d59 100644
> --- a/drivers/hid/Kconfig
> +++ b/drivers/hid/Kconfig
> @@ -322,18 +322,18 @@ config HID_LCPOWER
>  	---help---
>  	Support for LC-Power RC1000MCE RF remote control.
>  
> -config HID_LENOVO_TPKBD
> -	tristate "Lenovo ThinkPad USB Keyboard with TrackPoint"
> +config HID_LENOVO
> +	tristate "Lenovo / Thinkpad devices"
>  	depends on HID
>  	select NEW_LEDS
>  	select LEDS_CLASS
>  	---help---
> -	Support for the Lenovo ThinkPad USB Keyboard with TrackPoint.
> +	Support for Lenovo devices that are not fully compliant with HID standard.
>

Try to wrap text in Kconfig at 80 chars.

> -	Say Y here if you have a Lenovo ThinkPad USB Keyboard with TrackPoint
> -	and would like to use device-specific features like changing the
> -	sensitivity of the trackpoint, using the microphone mute button or
> -	controlling the mute and microphone mute LEDs.
> +	Say Y if you want support for the non-compliant features of the Lenovo
> +	Thinkpad standalone keyboards, e.g:

Maybe don't mention keyboards just yet on the line above since the
driver is now supposed to support other devices too.

> +	- ThinkPad USB Keyboard with TrackPoint (supports extra LEDs and trackpoint
> +	  configuration)
>

Wrap at 80 chars here too.

>  config HID_LOGITECH
>  	tristate "Logitech devices" if EXPERT
> diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
> index 30e4431..384f981 100644
> --- a/drivers/hid/Makefile
> +++ b/drivers/hid/Makefile
> @@ -58,7 +58,7 @@ obj-$(CONFIG_HID_KENSINGTON)	+= hid-kensington.o
>  obj-$(CONFIG_HID_KEYTOUCH)	+= hid-keytouch.o
>  obj-$(CONFIG_HID_KYE)		+= hid-kye.o
>  obj-$(CONFIG_HID_LCPOWER)       += hid-lcpower.o
> -obj-$(CONFIG_HID_LENOVO_TPKBD)	+= hid-lenovo-tpkbd.o
> +obj-$(CONFIG_HID_LENOVO)	+= hid-lenovo.o
>  obj-$(CONFIG_HID_LOGITECH)	+= hid-logitech.o
>  obj-$(CONFIG_HID_LOGITECH_DJ)	+= hid-logitech-dj.o
>  obj-$(CONFIG_HID_MAGICMOUSE)    += hid-magicmouse.o
> diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
> index 8a5384c..e8ce932 100644
> --- a/drivers/hid/hid-core.c
> +++ b/drivers/hid/hid-core.c
> @@ -1736,7 +1736,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
>  	{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_EASYPEN_M610X) },
>  	{ HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD) },
>  	{ HID_USB_DEVICE(USB_VENDOR_ID_LCPOWER, USB_DEVICE_ID_LCPOWER_LC1000 ) },
> -#if IS_ENABLED(CONFIG_HID_LENOVO_TPKBD)
> +#if IS_ENABLED(CONFIG_HID_LENOVO)
>  	{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) },
>  #endif
>  	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) },
> diff --git a/drivers/hid/hid-lenovo-tpkbd.c b/drivers/hid/hid-lenovo.c
> similarity index 59%
> rename from drivers/hid/hid-lenovo-tpkbd.c
> rename to drivers/hid/hid-lenovo.c
> index 2d25b6c..aabf084 100644
> --- a/drivers/hid/hid-lenovo-tpkbd.c
> +++ b/drivers/hid/hid-lenovo.c
> @@ -1,5 +1,6 @@
>  /*
> - *  HID driver for Lenovo ThinkPad USB Keyboard with TrackPoint
> + *  HID driver for Lenovo:-

The dash before the colon is not needed (really not a big deal I
mentioned that again just because there are other fixes too).

> + *  - ThinkPad USB Keyboard with TrackPoint (tpkbd)
>   *

This hunk could go into a preparatory patch separated from the rename
one, see below.

>   *  Copyright (c) 2012 Bernhard Seibold
>   */
> @@ -20,8 +21,7 @@
>  
>  #include "hid-ids.h"
>  
> -/* This is only used for the trackpoint part of the driver, hence _tp */
> -struct tpkbd_data_pointer {
> +struct lenovo_drvdata_tpkbd {
>  	int led_state;
>  	struct led_classdev led_mute;
>  	struct led_classdev led_micmute;
> @@ -35,7 +35,7 @@ struct tpkbd_data_pointer {
>  
>  #define map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c))
>  
> -static int tpkbd_input_mapping(struct hid_device *hdev,
> +static int lenovo_input_mapping_tpkbd(struct hid_device *hdev,

I'd name this just lenovo_input_mapping() for now.

>  		struct hid_input *hi, struct hid_field *field,
>  		struct hid_usage *usage, unsigned long **bit, int *max)
>  {
> @@ -48,12 +48,26 @@ static int tpkbd_input_mapping(struct hid_device *hdev,
>  	return 0;
>  }
>  
> +static int lenovo_input_mapping(struct hid_device *hdev,

And add this separation in a preparatory patch, more or less equal to
your previous patch 1, which would become a following patch on top of
the rename one.

> +		struct hid_input *hi, struct hid_field *field,
> +		struct hid_usage *usage, unsigned long **bit, int *max)
> +{
> +	switch (hdev->product) {
> +	case USB_DEVICE_ID_LENOVO_TPKBD:
> +		return lenovo_input_mapping_tpkbd(hdev, hi, field,
> +							usage, bit, max);
> +		break;

break after return is not needed.

I also like explicit default cases, but I guess it's just personal
preference.

> +	}
> +
> +	return 0;
> +}
> +
>  #undef map_key_clear
>  
> -static int tpkbd_features_set(struct hid_device *hdev)
> +static int lenovo_features_set_tpkbd(struct hid_device *hdev)
>  {
>  	struct hid_report *report;
> -	struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
> +	struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
>  
>  	report = hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[4];
>  
> @@ -69,23 +83,23 @@ static int tpkbd_features_set(struct hid_device *hdev)
>  	return 0;
>  }
>  
> -static ssize_t pointer_press_to_select_show(struct device *dev,
> +static ssize_t attr_press_to_select_show_tpkbd(struct device *dev,
>  		struct device_attribute *attr,

I have no definitive opinion on these renames, I guess they are OK;
maybe having these functions end in _store and _show is slightly more
readable, but this is not a big deal.

>  		char *buf)
>  {
>  	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
> -	struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
> +	struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
>  
>  	return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->press_to_select);
>  }
>  
> -static ssize_t pointer_press_to_select_store(struct device *dev,
> +static ssize_t attr_press_to_select_store_tpkbd(struct device *dev,
>  		struct device_attribute *attr,
>  		const char *buf,
>  		size_t count)
>  {
>  	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
> -	struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
> +	struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
>  	int value;
>  
>  	if (kstrtoint(buf, 10, &value))
> @@ -94,28 +108,28 @@ static ssize_t pointer_press_to_select_store(struct device *dev,
>  		return -EINVAL;
>  
>  	data_pointer->press_to_select = value;
> -	tpkbd_features_set(hdev);
> +	lenovo_features_set_tpkbd(hdev);
>  
>  	return count;
>  }
>  
> -static ssize_t pointer_dragging_show(struct device *dev,
> +static ssize_t attr_dragging_show_tpkbd(struct device *dev,
>  		struct device_attribute *attr,
>  		char *buf)
>  {
>  	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
> -	struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
> +	struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
>  
>  	return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->dragging);
>  }
>  
> -static ssize_t pointer_dragging_store(struct device *dev,
> +static ssize_t attr_dragging_store_tpkbd(struct device *dev,
>  		struct device_attribute *attr,
>  		const char *buf,
>  		size_t count)
>  {
>  	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
> -	struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
> +	struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
>  	int value;
>  
>  	if (kstrtoint(buf, 10, &value))
> @@ -124,28 +138,28 @@ static ssize_t pointer_dragging_store(struct device *dev,
>  		return -EINVAL;
>  
>  	data_pointer->dragging = value;
> -	tpkbd_features_set(hdev);
> +	lenovo_features_set_tpkbd(hdev);
>  
>  	return count;
>  }
>  
> -static ssize_t pointer_release_to_select_show(struct device *dev,
> +static ssize_t attr_release_to_select_show_tpkbd(struct device *dev,
>  		struct device_attribute *attr,
>  		char *buf)
>  {
>  	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
> -	struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
> +	struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
>  
>  	return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->release_to_select);
>  }
>  
> -static ssize_t pointer_release_to_select_store(struct device *dev,
> +static ssize_t attr_release_to_select_store_tpkbd(struct device *dev,
>  		struct device_attribute *attr,
>  		const char *buf,
>  		size_t count)
>  {
>  	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
> -	struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
> +	struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
>  	int value;
>  
>  	if (kstrtoint(buf, 10, &value))
> @@ -154,28 +168,28 @@ static ssize_t pointer_release_to_select_store(struct device *dev,
>  		return -EINVAL;
>  
>  	data_pointer->release_to_select = value;
> -	tpkbd_features_set(hdev);
> +	lenovo_features_set_tpkbd(hdev);
>  
>  	return count;
>  }
>  
> -static ssize_t pointer_select_right_show(struct device *dev,
> +static ssize_t attr_select_right_show_tpkbd(struct device *dev,
>  		struct device_attribute *attr,
>  		char *buf)
>  {
>  	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
> -	struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
> +	struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
>  
>  	return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->select_right);
>  }
>  
> -static ssize_t pointer_select_right_store(struct device *dev,
> +static ssize_t attr_select_right_store_tpkbd(struct device *dev,
>  		struct device_attribute *attr,
>  		const char *buf,
>  		size_t count)
>  {
>  	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
> -	struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
> +	struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
>  	int value;
>  
>  	if (kstrtoint(buf, 10, &value))
> @@ -184,119 +198,119 @@ static ssize_t pointer_select_right_store(struct device *dev,
>  		return -EINVAL;
>  
>  	data_pointer->select_right = value;
> -	tpkbd_features_set(hdev);
> +	lenovo_features_set_tpkbd(hdev);
>  
>  	return count;
>  }
>  
> -static ssize_t pointer_sensitivity_show(struct device *dev,
> +static ssize_t attr_sensitivity_show_tpkbd(struct device *dev,
>  		struct device_attribute *attr,
>  		char *buf)
>  {
>  	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
> -	struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
> +	struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
>  
>  	return snprintf(buf, PAGE_SIZE, "%u\n",
>  		data_pointer->sensitivity);
>  }
>  
> -static ssize_t pointer_sensitivity_store(struct device *dev,
> +static ssize_t attr_sensitivity_store_tpkbd(struct device *dev,
>  		struct device_attribute *attr,
>  		const char *buf,
>  		size_t count)
>  {
>  	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
> -	struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
> +	struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
>  	int value;
>  
>  	if (kstrtoint(buf, 10, &value) || value < 1 || value > 255)
>  		return -EINVAL;
>  
>  	data_pointer->sensitivity = value;
> -	tpkbd_features_set(hdev);
> +	lenovo_features_set_tpkbd(hdev);
>  
>  	return count;
>  }
>  
> -static ssize_t pointer_press_speed_show(struct device *dev,
> +static ssize_t attr_press_speed_show_tpkbd(struct device *dev,
>  		struct device_attribute *attr,
>  		char *buf)
>  {
>  	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
> -	struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
> +	struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
>  
>  	return snprintf(buf, PAGE_SIZE, "%u\n",
>  		data_pointer->press_speed);
>  }
>  
> -static ssize_t pointer_press_speed_store(struct device *dev,
> +static ssize_t attr_press_speed_store_tpkbd(struct device *dev,
>  		struct device_attribute *attr,
>  		const char *buf,
>  		size_t count)
>  {
>  	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
> -	struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
> +	struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
>  	int value;
>  
>  	if (kstrtoint(buf, 10, &value) || value < 1 || value > 255)
>  		return -EINVAL;
>  
>  	data_pointer->press_speed = value;
> -	tpkbd_features_set(hdev);
> +	lenovo_features_set_tpkbd(hdev);
>  
>  	return count;
>  }
>  
> -static struct device_attribute dev_attr_pointer_press_to_select =
> +static struct device_attribute dev_attr_press_to_select_tpkbd =
>  	__ATTR(press_to_select, S_IWUSR | S_IRUGO,
> -			pointer_press_to_select_show,
> -			pointer_press_to_select_store);
> +			attr_press_to_select_show_tpkbd,
> +			attr_press_to_select_store_tpkbd);
>  
> -static struct device_attribute dev_attr_pointer_dragging =
> +static struct device_attribute dev_attr_dragging_tpkbd =
>  	__ATTR(dragging, S_IWUSR | S_IRUGO,
> -			pointer_dragging_show,
> -			pointer_dragging_store);
> +			attr_dragging_show_tpkbd,
> +			attr_dragging_store_tpkbd);
>  
> -static struct device_attribute dev_attr_pointer_release_to_select =
> +static struct device_attribute dev_attr_release_to_select_tpkbd =
>  	__ATTR(release_to_select, S_IWUSR | S_IRUGO,
> -			pointer_release_to_select_show,
> -			pointer_release_to_select_store);
> +			attr_release_to_select_show_tpkbd,
> +			attr_release_to_select_store_tpkbd);
>  
> -static struct device_attribute dev_attr_pointer_select_right =
> +static struct device_attribute dev_attr_select_right_tpkbd =
>  	__ATTR(select_right, S_IWUSR | S_IRUGO,
> -			pointer_select_right_show,
> -			pointer_select_right_store);
> +			attr_select_right_show_tpkbd,
> +			attr_select_right_store_tpkbd);
>  
> -static struct device_attribute dev_attr_pointer_sensitivity =
> +static struct device_attribute dev_attr_sensitivity_tpkbd =
>  	__ATTR(sensitivity, S_IWUSR | S_IRUGO,
> -			pointer_sensitivity_show,
> -			pointer_sensitivity_store);
> +			attr_sensitivity_show_tpkbd,
> +			attr_sensitivity_store_tpkbd);
>  
> -static struct device_attribute dev_attr_pointer_press_speed =
> +static struct device_attribute dev_attr_press_speed_tpkbd =
>  	__ATTR(press_speed, S_IWUSR | S_IRUGO,
> -			pointer_press_speed_show,
> -			pointer_press_speed_store);
> -
> -static struct attribute *tpkbd_attributes_pointer[] = {
> -	&dev_attr_pointer_press_to_select.attr,
> -	&dev_attr_pointer_dragging.attr,
> -	&dev_attr_pointer_release_to_select.attr,
> -	&dev_attr_pointer_select_right.attr,
> -	&dev_attr_pointer_sensitivity.attr,
> -	&dev_attr_pointer_press_speed.attr,
> +			attr_press_speed_show_tpkbd,
> +			attr_press_speed_store_tpkbd);
> +
> +static struct attribute *lenovo_attributes_tpkbd[] = {
> +	&dev_attr_press_to_select_tpkbd.attr,
> +	&dev_attr_dragging_tpkbd.attr,
> +	&dev_attr_release_to_select_tpkbd.attr,
> +	&dev_attr_select_right_tpkbd.attr,
> +	&dev_attr_sensitivity_tpkbd.attr,
> +	&dev_attr_press_speed_tpkbd.attr,
>  	NULL
>  };
>  
> -static const struct attribute_group tpkbd_attr_group_pointer = {
> -	.attrs = tpkbd_attributes_pointer,
> +static const struct attribute_group lenovo_attr_group_tpkbd = {
> +	.attrs = lenovo_attributes_tpkbd,
>  };
>  
> -static enum led_brightness tpkbd_led_brightness_get(
> +static enum led_brightness lenovo_led_brightness_get_tpkbd(
>  			struct led_classdev *led_cdev)
>  {
>  	struct device *dev = led_cdev->dev->parent;
>  	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
> -	struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
> +	struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
>  	int led_nr = 0;
>  
>  	if (led_cdev == &data_pointer->led_micmute)
> @@ -307,12 +321,12 @@ static enum led_brightness tpkbd_led_brightness_get(
>  				: LED_OFF;
>  }
>  
> -static void tpkbd_led_brightness_set(struct led_classdev *led_cdev,
> +static void lenovo_led_brightness_set_tpkbd(struct led_classdev *led_cdev,
>  			enum led_brightness value)
>  {
>  	struct device *dev = led_cdev->dev->parent;
>  	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
> -	struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
> +	struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
>  	struct hid_report *report;
>  	int led_nr = 0;
>  
> @@ -330,13 +344,24 @@ static void tpkbd_led_brightness_set(struct led_classdev *led_cdev,
>  	hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
>  }
>  
> -static int tpkbd_probe_tp(struct hid_device *hdev)
> +static int lenovo_probe_tpkbd(struct hid_device *hdev,
> +			const struct hid_device_id *id)

Just curios, why did the function change the signature too here?
The "id" argument is not used in either of the "internal" probe
routines.

>  {
>  	struct device *dev = &hdev->dev;
> -	struct tpkbd_data_pointer *data_pointer;
> +	struct lenovo_drvdata_tpkbd *data_pointer;
>  	size_t name_sz = strlen(dev_name(dev)) + 16;
>  	char *name_mute, *name_micmute;
> -	int i;
> +	int i, ret;
> +
> +	/*
> +	 * If this is the pointer half of the keyboard, input_mapping should
> +	 * have set drvdata to 1. Otherwise, it's the keyboard which needs
> +	 * nothing special doing to it.
> +	 */
> +	if (!hid_get_drvdata(hdev))
> +		return 0;
> +
> +	hid_set_drvdata(hdev, NULL);
>

This last part would also go in the preparatory patch.

>  	/* Validate required reports. */
>  	for (i = 0; i < 4; i++) {
> @@ -346,13 +371,12 @@ static int tpkbd_probe_tp(struct hid_device *hdev)
>  	if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 3, 0, 2))
>  		return -ENODEV;
>  
> -	if (sysfs_create_group(&hdev->dev.kobj,
> -				&tpkbd_attr_group_pointer)) {
> +	ret = sysfs_create_group(&hdev->dev.kobj, &lenovo_attr_group_tpkbd);
> +	if (ret)
>  		hid_warn(hdev, "Could not create sysfs group\n");
> -	}

This is a cleanup of existing code, this ideally would go alone in a
dedicated "cleanup" patch.

>  
>  	data_pointer = devm_kzalloc(&hdev->dev,
> -				    sizeof(struct tpkbd_data_pointer),
> +				    sizeof(struct lenovo_drvdata_tpkbd),
>  				    GFP_KERNEL);
>  	if (data_pointer == NULL) {
>  		hid_err(hdev, "Could not allocate memory for driver data\n");
> @@ -375,23 +399,25 @@ static int tpkbd_probe_tp(struct hid_device *hdev)
>  	hid_set_drvdata(hdev, data_pointer);
>  
>  	data_pointer->led_mute.name = name_mute;
> -	data_pointer->led_mute.brightness_get = tpkbd_led_brightness_get;
> -	data_pointer->led_mute.brightness_set = tpkbd_led_brightness_set;
> +	data_pointer->led_mute.brightness_get = lenovo_led_brightness_get_tpkbd;
> +	data_pointer->led_mute.brightness_set = lenovo_led_brightness_set_tpkbd;
>  	data_pointer->led_mute.dev = dev;
>  	led_classdev_register(dev, &data_pointer->led_mute);
>  
>  	data_pointer->led_micmute.name = name_micmute;
> -	data_pointer->led_micmute.brightness_get = tpkbd_led_brightness_get;
> -	data_pointer->led_micmute.brightness_set = tpkbd_led_brightness_set;
> +	data_pointer->led_micmute.brightness_get =
> +		lenovo_led_brightness_get_tpkbd;
> +	data_pointer->led_micmute.brightness_set =
> +		lenovo_led_brightness_set_tpkbd;
>  	data_pointer->led_micmute.dev = dev;
>  	led_classdev_register(dev, &data_pointer->led_micmute);
>  
> -	tpkbd_features_set(hdev);
> +	lenovo_features_set_tpkbd(hdev);
>  
>  	return 0;
>  }
>  
> -static int tpkbd_probe(struct hid_device *hdev,
> +static int lenovo_probe(struct hid_device *hdev,
>  		const struct hid_device_id *id)
>  {
>  	int ret;
> @@ -408,12 +434,13 @@ static int tpkbd_probe(struct hid_device *hdev,
>  		goto err;
>  	}
>  
> -	if (hid_get_drvdata(hdev)) {
> -		hid_set_drvdata(hdev, NULL);
> -		ret = tpkbd_probe_tp(hdev);
> -		if (ret)
> -			goto err_hid;
> +	switch (hdev->product) {
> +	case USB_DEVICE_ID_LENOVO_TPKBD:
> +		ret = lenovo_probe_tpkbd(hdev, id);
> +		break;
>  	}
> +	if (ret)
> +		goto err_hid;
>

This hunk would also go in the preparatory patch.

>  	return 0;
>  err_hid:
> @@ -422,12 +449,15 @@ err:
>  	return ret;
>  }
>  
> -static void tpkbd_remove_tp(struct hid_device *hdev)
> +static void lenovo_remove_tpkbd(struct hid_device *hdev)
>  {
> -	struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
> +	struct lenovo_drvdata_tpkbd *data_pointer = hid_get_drvdata(hdev);
>  
>  	sysfs_remove_group(&hdev->dev.kobj,
> -			&tpkbd_attr_group_pointer);
> +			&lenovo_attr_group_tpkbd);
> +
> +	if (data_pointer == NULL)
> +		return;
>

And this too.

>  	led_classdev_unregister(&data_pointer->led_micmute);
>  	led_classdev_unregister(&data_pointer->led_mute);
> @@ -435,28 +465,31 @@ static void tpkbd_remove_tp(struct hid_device *hdev)
>  	hid_set_drvdata(hdev, NULL);
>  }
>  
> -static void tpkbd_remove(struct hid_device *hdev)
> +static void lenovo_remove(struct hid_device *hdev)
>  {
> -	if (hid_get_drvdata(hdev))
> -		tpkbd_remove_tp(hdev);
> +	switch (hdev->product) {
> +	case USB_DEVICE_ID_LENOVO_TPKBD:
> +		lenovo_remove_tpkbd(hdev);
> +		break;
> +	}
>

And this.

>  	hid_hw_stop(hdev);
>  }
>  
> -static const struct hid_device_id tpkbd_devices[] = {
> +static const struct hid_device_id lenovo_devices[] = {
>  	{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) },
>  	{ }
>  };
>  
> -MODULE_DEVICE_TABLE(hid, tpkbd_devices);
> +MODULE_DEVICE_TABLE(hid, lenovo_devices);
>  
> -static struct hid_driver tpkbd_driver = {
> +static struct hid_driver lenovo_driver = {
>  	.name = "lenovo_tpkbd",

The name could now become just "lenovo".

> -	.id_table = tpkbd_devices,
> -	.input_mapping = tpkbd_input_mapping,
> -	.probe = tpkbd_probe,
> -	.remove = tpkbd_remove,
> +	.id_table = lenovo_devices,
> +	.input_mapping = lenovo_input_mapping,
> +	.probe = lenovo_probe,
> +	.remove = lenovo_remove,
>  };
> -module_hid_driver(tpkbd_driver);
> +module_hid_driver(lenovo_driver);
>  
>  MODULE_LICENSE("GPL");
> -- 
> 2.0.0.rc2
> 
> 


-- 
Antonio Ospite
http://ao2.it

A: Because it messes up the order in which people normally read text.
   See http://en.wikipedia.org/wiki/Posting_style
Q: Why is top-posting such a bad thing?

^ permalink raw reply

* Re: [PATCH v3 2/2] Add support for Compact (Bluetooth|USB) keyboard with Trackpoint
From: Antonio Ospite @ 2014-06-16 21:03 UTC (permalink / raw)
  To: Jamie Lentin; +Cc: Jiri Kosina, Alexander Clouter, linux-input, linux-kernel
In-Reply-To: <1402864790-7478-3-git-send-email-jm@lentin.co.uk>

On Sun, 15 Jun 2014 21:39:50 +0100
Jamie Lentin <jm@lentin.co.uk> wrote:

This one does not compile on 3.15, see below.

Maybe you can take the chance to split the series in 4 patches:
	1. rename only
	2. cleanup of already existing code
	3. preparatory changes to support multiple devices (the original
	   1/2)
	4. support for the Compact keyboard (the original 2/2).

but two patches are fine too as long as the important issues are sorted
out.

> Signed-off-by: Jamie Lentin <jm@lentin.co.uk>
> ---
>  drivers/hid/Kconfig      |   2 +
>  drivers/hid/hid-core.c   |   2 +
>  drivers/hid/hid-ids.h    |   2 +
>  drivers/hid/hid-lenovo.c | 202 +++++++++++++++++++++++++++++++++++++++++++++++
>  include/linux/hid.h      |   1 +
>  5 files changed, 209 insertions(+)
> 
> diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
> index dd07d59..48b4777 100644
> --- a/drivers/hid/Kconfig
> +++ b/drivers/hid/Kconfig
> @@ -334,6 +334,8 @@ config HID_LENOVO
>  	Thinkpad standalone keyboards, e.g:
>  	- ThinkPad USB Keyboard with TrackPoint (supports extra LEDs and trackpoint
>  	  configuration)
> +	- ThinkPad Compact Bluetooth Keyboard with TrackPoint (supports Fn keys)
> +	- ThinkPad Compact USB Keyboard with TrackPoint (supports Fn keys)
>  
>  config HID_LOGITECH
>  	tristate "Logitech devices" if EXPERT
> diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
> index e8ce932..bce37c3 100644
> --- a/drivers/hid/hid-core.c
> +++ b/drivers/hid/hid-core.c
> @@ -1738,6 +1738,8 @@ static const struct hid_device_id hid_have_special_driver[] = {
>  	{ HID_USB_DEVICE(USB_VENDOR_ID_LCPOWER, USB_DEVICE_ID_LCPOWER_LC1000 ) },
>  #if IS_ENABLED(CONFIG_HID_LENOVO)
>  	{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) },
> +	{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CUSBKBD) },
> +	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CBTKBD) },
>  #endif
>  	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) },
>  	{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) },
> diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
> index 6e12cd0..1763a07 100644
> --- a/drivers/hid/hid-ids.h
> +++ b/drivers/hid/hid-ids.h
> @@ -551,6 +551,8 @@
>  
>  #define USB_VENDOR_ID_LENOVO		0x17ef
>  #define USB_DEVICE_ID_LENOVO_TPKBD	0x6009
> +#define USB_DEVICE_ID_LENOVO_CUSBKBD	0x6047
> +#define USB_DEVICE_ID_LENOVO_CBTKBD		0x6048

One TAB is enough here to align the second entry.

>  
>  #define USB_VENDOR_ID_LG		0x1fd2
>  #define USB_DEVICE_ID_LG_MULTITOUCH	0x0064
> diff --git a/drivers/hid/hid-lenovo.c b/drivers/hid/hid-lenovo.c
> index aabf084..0a19f84 100644
> --- a/drivers/hid/hid-lenovo.c
> +++ b/drivers/hid/hid-lenovo.c
> @@ -1,8 +1,11 @@
>  /*
>   *  HID driver for Lenovo:-
>   *  - ThinkPad USB Keyboard with TrackPoint (tpkbd)
> + *  - ThinkPad Compact Bluetooth Keyboard with TrackPoint (cptkbd)
> + *  - ThinkPad Compact USB Keyboard with TrackPoint (cptkbd)
>   *
>   *  Copyright (c) 2012 Bernhard Seibold
> + *  Copyright (c) 2014 Jamie Lentin <jm@lentin.co.uk>
>   */
>  
>  /*
> @@ -33,6 +36,10 @@ struct lenovo_drvdata_tpkbd {
>  	int press_speed;
>  };
>  
> +struct lenovo_drvdata_cptkbd {
> +	unsigned int fn_lock;

bool? 

> +};
> +
>  #define map_key_clear(c) hid_map_usage_clear(hi, usage, bit, max, EV_KEY, (c))
>  
>  static int lenovo_input_mapping_tpkbd(struct hid_device *hdev,
> @@ -48,6 +55,49 @@ static int lenovo_input_mapping_tpkbd(struct hid_device *hdev,
>  	return 0;
>  }
>  
> +static int lenovo_input_mapping_cptkbd(struct hid_device *hdev,
> +		struct hid_input *hi, struct hid_field *field,
> +		struct hid_usage *usage, unsigned long **bit, int *max)
> +{
> +	/* HID_UP_LNVENDOR = USB, HID_UP_MSVENDOR = BT */
> +	if ((usage->hid & HID_USAGE_PAGE) == HID_UP_MSVENDOR ||
> +	    (usage->hid & HID_USAGE_PAGE) == HID_UP_LNVENDOR) {
> +		set_bit(EV_REP, hi->input->evbit);
> +		switch (usage->hid & HID_USAGE) {
> +		case 0x00f1: /* Fn-F4: Mic mute */
> +			map_key_clear(KEY_MICMUTE);
> +			return 1;
> +		case 0x00f2: /* Fn-F5: Brightness down */
> +			map_key_clear(KEY_BRIGHTNESSDOWN);
> +			return 1;
> +		case 0x00f3: /* Fn-F6: Brightness up */
> +			map_key_clear(KEY_BRIGHTNESSUP);
> +			return 1;
> +		case 0x00f4: /* Fn-F7: External display (projector) */
> +			map_key_clear(KEY_SWITCHVIDEOMODE);
> +			return 1;
> +		case 0x00f5: /* Fn-F8: Wireless */
> +			map_key_clear(KEY_WLAN);
> +			return 1;
> +		case 0x00f6: /* Fn-F9: Control panel */
> +			map_key_clear(KEY_CONFIG);
> +			return 1;
> +		case 0x00f8: /* Fn-F11: View open applications (3 boxes) */
> +			map_key_clear(KEY_FN_F11);
> +			return 1;
> +		case 0x00fa: /* Fn-Esc: Fn-lock toggle */
> +			map_key_clear(KEY_FN_ESC);
> +			return 1;
> +		case 0x00fb: /* Fn-F12: Open My computer (6 boxes) USB-only */
> +			/* NB: This mapping is invented in raw_event below */
> +			map_key_clear(KEY_FILE);
> +			return 1;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
>  static int lenovo_input_mapping(struct hid_device *hdev,
>  		struct hid_input *hi, struct hid_field *field,
>  		struct hid_usage *usage, unsigned long **bit, int *max)
> @@ -57,6 +107,11 @@ static int lenovo_input_mapping(struct hid_device *hdev,
>  		return lenovo_input_mapping_tpkbd(hdev, hi, field,
>  							usage, bit, max);
>  		break;
> +	case USB_DEVICE_ID_LENOVO_CUSBKBD:
> +	case USB_DEVICE_ID_LENOVO_CBTKBD:
> +		return lenovo_input_mapping_cptkbd(hdev, hi, field,
> +							usage, bit, max);
> +		break;

break after return not needed.

>  	}
>  
>  	return 0;
> @@ -64,6 +119,96 @@ static int lenovo_input_mapping(struct hid_device *hdev,
>  
>  #undef map_key_clear
>  
> +/* Send a config command to the keyboard */
> +static int lenovo_send_cmd_cptkbd(struct hid_device *hdev,
> +			unsigned char byte2, unsigned char byte3)
> +{
> +	int ret;
> +	unsigned char buf[] = {0x18, byte2, byte3};
> +	unsigned char report_type = HID_OUTPUT_REPORT;
> +
> +	/* The USB keyboard accepts commands via SET_FEATURE */
> +	if (hdev->product == USB_DEVICE_ID_LENOVO_CUSBKBD) {
> +		buf[0] = 0x13;
> +		report_type = HID_FEATURE_REPORT;
> +	}
> +
> +	ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), report_type);

This makes the driver fail to compile on 3.15.

The hid_output_raw_report() handler is not present anymore since
3.15-rc1, see commit 6fd182028c43baf1c7d017d52b0134ecadbdc447 in
current linus tree, I think you need to use something like:

	ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(buf),
	 			 report_type, HID_REQ_SET_REPORT);

but I cannot test that.

Please test the driver at least on 3.15, or even better on 3.16rc if you
can.

> +	return ret < 0 ? ret : 0; /* BT returns 0, USB returns sizeof(buf) */
> +}

The callers never propagate the error code, so maybe this function can
be declared to return a bool directly, and:
	return (ret >= 0);

just an idea.

> +
> +static void lenovo_features_set_cptkbd(struct hid_device *hdev)
> +{
> +	struct lenovo_drvdata_cptkbd *tpcsc = hid_get_drvdata(hdev);
> +
> +	if (lenovo_send_cmd_cptkbd(hdev, 0x05, tpcsc->fn_lock ? 0x01 : 0x00))
> +		hid_err(hdev, "Fn-lock setting failed\n");
> +}
> +
> +static ssize_t attr_fn_lock_show_cptkbd(struct device *dev,
> +		struct device_attribute *attr,
> +		char *buf)
> +{
> +	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
> +	struct lenovo_drvdata_cptkbd *tpcsc = hid_get_drvdata(hdev);
> +
> +	return snprintf(buf, PAGE_SIZE, "%u\n", tpcsc->fn_lock);
> +}
> +
> +static ssize_t attr_fn_lock_store_cptkbd(struct device *dev,
> +		struct device_attribute *attr,
> +		const char *buf,
> +		size_t count)
> +{
> +	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
> +	struct lenovo_drvdata_cptkbd *tpcsc = hid_get_drvdata(hdev);
> +	int value;
> +
> +	if (kstrtoint(buf, 10, &value))
> +		return -EINVAL;
> +	if (value < 0 || value > 1)
> +		return -EINVAL;
> +
> +	tpcsc->fn_lock = value;

if tpcsc->fn_lock is bool you can use "!!value" here to convert value
to bool.

> +	lenovo_features_set_cptkbd(hdev);
> +
> +	return count;
> +}
> +
> +static struct device_attribute dev_attr_fn_lock_cptkbd =
> +	__ATTR(fn_lock, S_IWUSR | S_IRUGO,
> +			attr_fn_lock_show_cptkbd,
> +			attr_fn_lock_store_cptkbd);
> +
> +static struct attribute *lenovo_attributes_cptkbd[] = {
> +	&dev_attr_fn_lock_cptkbd.attr,
> +	NULL
> +};
> +
> +static const struct attribute_group lenovo_attr_group_cptkbd = {
> +	.attrs = lenovo_attributes_cptkbd,
> +};
> +
> +static int lenovo_raw_event(struct hid_device *hdev,
> +			struct hid_report *report, u8 *data, int size)
> +{
> +	/*
> +	 * Compact USB keyboard's Fn-F12 report holds down many other keys, and
> +	 * it's own key is outside the usage page range. Remove extra
           ^^^^
its?

> +	 * keypresses and remap to inside usage page.
> +	 */
> +	if (unlikely(hdev->product == USB_DEVICE_ID_LENOVO_CUSBKBD
> +			&& size == 3
> +			&& data[0] == 0x15
> +			&& data[1] == 0x94
> +			&& data[2] == 0x01)) {
> +		data[1] = 0x0;
> +		data[2] = 0x4;
> +	}
> +
> +	return 0;
> +}
> +
>  static int lenovo_features_set_tpkbd(struct hid_device *hdev)
>  {
>  	struct hid_report *report;
> @@ -417,6 +562,46 @@ static int lenovo_probe_tpkbd(struct hid_device *hdev,
>  	return 0;
>  }
>  
> +static int lenovo_probe_cptkbd(struct hid_device *hdev,
> +			const struct hid_device_id *id)

The id argument is not needed for the "internal" probe function.

> +{
> +	int ret;
> +	struct lenovo_drvdata_cptkbd *tpcsc;
> +
> +	tpcsc = devm_kzalloc(&hdev->dev, sizeof(*tpcsc), GFP_KERNEL);
> +	if (tpcsc == NULL) {
> +		hid_err(hdev, "can't alloc keyboard descriptor\n");
> +		return -ENOMEM;
> +	}
> +	hid_set_drvdata(hdev, tpcsc);
> +
> +	/* All the custom action happens on the mouse device for USB */
> +	if (hdev->product == USB_DEVICE_ID_LENOVO_CUSBKBD
> +			&& hdev->type != HID_TYPE_USBMOUSE) {
> +		pr_debug("Ignoring keyboard half of device\n");

hid_dbg(hdev, ...); can also be used

> +		return 0;
> +	}
> +
> +	/*
> +	 * Tell the keyboard a driver understands it, and turn F7, F9, F11 into
> +	 * regular keys
> +	 */
> +	ret = lenovo_send_cmd_cptkbd(hdev, 0x01, 0x03);
> +	if (ret)
> +		hid_warn(hdev, "Failed to switch F7/9/11 into regular keys\n");
> +
> +	/* Turn Fn-Lock on by default */
> +	tpcsc->fn_lock = 1;
> +	lenovo_features_set_cptkbd(hdev);
> +
> +	if (sysfs_create_group(&hdev->dev.kobj,
> +				&lenovo_attr_group_cptkbd)) {

	ret = sysfs_create_group(...);
	if (ret)
		hid_warn(...)

no curly braces needed.

> +		hid_warn(hdev, "Could not create sysfs group\n");
> +	}
> +
> +	return 0;
> +}
> +
>  static int lenovo_probe(struct hid_device *hdev,
>  		const struct hid_device_id *id)
>  {
> @@ -438,6 +623,10 @@ static int lenovo_probe(struct hid_device *hdev,
>  	case USB_DEVICE_ID_LENOVO_TPKBD:
>  		ret = lenovo_probe_tpkbd(hdev, id);
>  		break;
> +	case USB_DEVICE_ID_LENOVO_CUSBKBD:
> +	case USB_DEVICE_ID_LENOVO_CBTKBD:
> +		ret = lenovo_probe_cptkbd(hdev, id);
> +		break;
>  	}
>  	if (ret)
>  		goto err_hid;
> @@ -465,12 +654,22 @@ static void lenovo_remove_tpkbd(struct hid_device *hdev)
>  	hid_set_drvdata(hdev, NULL);
>  }
>  
> +static void lenovo_remove_cptkbd(struct hid_device *hdev)
> +{
> +	sysfs_remove_group(&hdev->dev.kobj,
> +			&lenovo_attr_group_cptkbd);
> +}
> +
>  static void lenovo_remove(struct hid_device *hdev)
>  {
>  	switch (hdev->product) {
>  	case USB_DEVICE_ID_LENOVO_TPKBD:
>  		lenovo_remove_tpkbd(hdev);
>  		break;
> +	case USB_DEVICE_ID_LENOVO_CUSBKBD:
> +	case USB_DEVICE_ID_LENOVO_CBTKBD:
> +		lenovo_remove_cptkbd(hdev);
> +		break;
>  	}
>  
>  	hid_hw_stop(hdev);
> @@ -478,6 +677,8 @@ static void lenovo_remove(struct hid_device *hdev)
>  
>  static const struct hid_device_id lenovo_devices[] = {
>  	{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_TPKBD) },
> +	{ HID_USB_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CUSBKBD) },
> +	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_CBTKBD) },
>  	{ }
>  };
>  
> @@ -489,6 +690,7 @@ static struct hid_driver lenovo_driver = {
>  	.input_mapping = lenovo_input_mapping,
>  	.probe = lenovo_probe,
>  	.remove = lenovo_remove,
> +	.raw_event = lenovo_raw_event,
>  };
>  module_hid_driver(lenovo_driver);
>  
> diff --git a/include/linux/hid.h b/include/linux/hid.h
> index 31b9d29..ed23d6a 100644
> --- a/include/linux/hid.h
> +++ b/include/linux/hid.h
> @@ -167,6 +167,7 @@ struct hid_item {
>  #define HID_UP_MSVENDOR		0xff000000
>  #define HID_UP_CUSTOM		0x00ff0000
>  #define HID_UP_LOGIVENDOR	0xffbc0000
> +#define HID_UP_LNVENDOR		0xffa00000
>  #define HID_UP_SENSOR		0x00200000
>  
>  #define HID_USAGE		0x0000ffff
> -- 
> 2.0.0.rc2
> 
> 


-- 
Antonio Ospite
http://ao2.it

A: Because it messes up the order in which people normally read text.
   See http://en.wikipedia.org/wiki/Posting_style
Q: Why is top-posting such a bad thing?

^ permalink raw reply

* Re: [PATCH 1/2] touchscreen: sun4i-ts: A10 (sun4i) has double the temperature precision
From: Dmitry Torokhov @ 2014-06-16 21:30 UTC (permalink / raw)
  To: Hans de Goede
  Cc: Maxime Ripard, Tong Zhang, linux-input-u79uwXL29TY76Z2rM5mHXA,
	LM Sensors, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree, linux-sunxi-/JYPxA39Uh5TLH3MbocFFw
In-Reply-To: <1402943069-19420-2-git-send-email-hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>

Hi Hans,

On Mon, Jun 16, 2014 at 08:24:28PM +0200, Hans de Goede wrote:
> Testing has revealed that the temperature in the rtp controller of the A10
> (sun4i) SoC has a resolution of 50 milli degrees / step, where as the
> A13 (sun5i) and later models have 100 milli degrees / step.
> 
> Add a new sun5i-a13-ts compatible to differentiate the newer models and
> set the resolution based on the compatible string.
> 
> This fixes the temperature reported on the A10 being twice as high as expected.

Should we maybe add explicit temperature steps property so that we won;t
need to patch again if they decided to change resolution again?

Thanks.

-- 
Dmitry

^ permalink raw reply

* [PATCH 0/10] Batch of cleanup patches for cros_ec
From: Doug Anderson @ 2014-06-16 21:39 UTC (permalink / raw)
  To: Lee Jones
  Cc: Andrew Bresticker, swarren, olof, Sonny Rao, linux-samsung-soc,
	Javier Martinez Canillas, Bill Richardson, sjg, Wolfram Sang,
	broonie, Doug Anderson, vpalatin, sameo, dmitry.torokhov,
	linux-kernel, geert, linux-i2c, linux-input

This is a batch of cleanup patches picked from the ChromeOS 3.8 kernel
tree and applied to ToT.  Most of these patches were authored by Bill
Richardson (CCed).  Where appropriate I've squashed patches together,
though I have erred on the side of keeping patches logically distinct
rather than squashing into one big cleanup patch.

There is very little functionality added by this series, but this gets
us closer to how things look in the ChromeOS tree so we can add more
patches atop it.  In general I took the oldest patches from our tree
and stopped picking when I got to a reasonable patch size (10
patches).  There are about 5 more cleanup patches still in the
ChromeOS tree, then some more major functionality patches.

Note that I didn't take the "cros_ec_dev" userspace inteface, the
"LPC" implementation, the "vboot context" implementation, and patches
relating to exynos5250-spring when picking patches.  These bits are
very separate (and big!) and can be added and debated separately after
we've got cleanup in.  Whenever patches touched those pieces of the
code I ignored that part of the patch.  In general I did take cleanup
code that was intended to make it easier to later add these bits.

I have tested basic functionality of these patches on exynos5250-snow
and exynos5420-peach-pit.


Bill Richardson (9):
  mfd: cros_ec: Fix the comment on cros_ec_remove()
  mfd: cros_ec: IRQs for cros_ec should be optional
  mfd: cros_ec: Allow static din/dout buffers with cros_ec_register()
  mfd: cros_ec: Tweak struct cros_ec_device for clarity
  mfd: cros_ec: Use struct cros_ec_command to communicate with the EC
  mfd: cros_ec: cleanup: remove unused fields from struct cros_ec_device
  mfd: cros_ec: cleanup: Remove EC wrapper functions
  mfd: cros_ec: Check result code from EC messages
  mfd: cros_ec: ec_dev->cmd_xfer() returns number of bytes received from
    EC

Simon Glass (1):
  mdf: cros_ec: Detect in-progress commands

 drivers/i2c/busses/i2c-cros-ec-tunnel.c |  17 ++++--
 drivers/input/keyboard/cros_ec_keyb.c   |  14 ++++-
 drivers/mfd/cros_ec.c                   |  76 +++++++-----------------
 drivers/mfd/cros_ec_i2c.c               |  44 ++++++++------
 drivers/mfd/cros_ec_spi.c               |  43 ++++++++------
 include/linux/mfd/cros_ec.h             | 100 +++++++++++++++-----------------
 6 files changed, 144 insertions(+), 150 deletions(-)

-- 
2.0.0.526.g5318336

^ permalink raw reply

* [PATCH 08/10] mfd: cros_ec: cleanup: Remove EC wrapper functions
From: Doug Anderson @ 2014-06-16 21:39 UTC (permalink / raw)
  To: Lee Jones
  Cc: Andrew Bresticker, swarren, olof, Sonny Rao, linux-samsung-soc,
	Javier Martinez Canillas, Bill Richardson, sjg, Wolfram Sang,
	broonie, Doug Anderson, dmitry.torokhov, sameo, vpalatin, geert,
	linux-i2c, linux-kernel, linux-input
In-Reply-To: <1402954800-28215-1-git-send-email-dianders@chromium.org>

From: Bill Richardson <wfrichar@chromium.org>

Remove the three wrapper functions that talk to the EC without passing all
the desired arguments and just use the underlying communication function
that passes everything in a struct intead.

This is internal code refactoring only. Nothing should change.

Signed-off-by: Bill Richardson <wfrichar@chromium.org>
Signed-off-by: Doug Anderson <dianders@chromium.org>
---
 drivers/i2c/busses/i2c-cros-ec-tunnel.c | 15 +++++++++++----
 drivers/input/keyboard/cros_ec_keyb.c   | 14 ++++++++++++--
 drivers/mfd/cros_ec.c                   | 32 --------------------------------
 include/linux/mfd/cros_ec.h             | 19 ++++++-------------
 4 files changed, 29 insertions(+), 51 deletions(-)

diff --git a/drivers/i2c/busses/i2c-cros-ec-tunnel.c b/drivers/i2c/busses/i2c-cros-ec-tunnel.c
index 8e7a714..dd07818 100644
--- a/drivers/i2c/busses/i2c-cros-ec-tunnel.c
+++ b/drivers/i2c/busses/i2c-cros-ec-tunnel.c
@@ -183,6 +183,7 @@ static int ec_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg i2c_msgs[],
 	u8 *request = NULL;
 	u8 *response = NULL;
 	int result;
+	struct cros_ec_command msg;
 
 	request_len = ec_i2c_count_message(i2c_msgs, num);
 	if (request_len < 0) {
@@ -218,9 +219,15 @@ static int ec_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg i2c_msgs[],
 	}
 
 	ec_i2c_construct_message(request, i2c_msgs, num, bus_num);
-	result = bus->ec->command_sendrecv(bus->ec, EC_CMD_I2C_PASSTHRU,
-					   request, request_len,
-					   response, response_len);
+
+	msg.version = 0;
+	msg.command = EC_CMD_I2C_PASSTHRU;
+	msg.outdata = request;
+	msg.outsize = request_len;
+	msg.indata = response;
+	msg.insize = response_len;
+
+	result = bus->ec->cmd_xfer(bus->ec, &msg);
 	if (result)
 		goto exit;
 
@@ -258,7 +265,7 @@ static int ec_i2c_probe(struct platform_device *pdev)
 	u32 remote_bus;
 	int err;
 
-	if (!ec->command_sendrecv) {
+	if (!ec->cmd_xfer) {
 		dev_err(dev, "Missing sendrecv\n");
 		return -EINVAL;
 	}
diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c
index 4083796..dc37b6b 100644
--- a/drivers/input/keyboard/cros_ec_keyb.c
+++ b/drivers/input/keyboard/cros_ec_keyb.c
@@ -191,8 +191,18 @@ static void cros_ec_keyb_close(struct input_dev *dev)
 
 static int cros_ec_keyb_get_state(struct cros_ec_keyb *ckdev, uint8_t *kb_state)
 {
-	return ckdev->ec->command_recv(ckdev->ec, EC_CMD_MKBP_STATE,
-					  kb_state, ckdev->cols);
+	int ret;
+	struct cros_ec_command msg = {
+		.version = 0,
+		.command = EC_CMD_MKBP_STATE,
+		.outdata = NULL,
+		.outsize = 0,
+		.indata = kb_state,
+		.insize = ckdev->cols,
+	};
+
+	ret = ckdev->ec->cmd_xfer(ckdev->ec, &msg);
+	return ret;
 }
 
 static int cros_ec_keyb_work(struct notifier_block *nb,
diff --git a/drivers/mfd/cros_ec.c b/drivers/mfd/cros_ec.c
index d242714..6dd91e9 100644
--- a/drivers/mfd/cros_ec.c
+++ b/drivers/mfd/cros_ec.c
@@ -44,34 +44,6 @@ int cros_ec_prepare_tx(struct cros_ec_device *ec_dev,
 }
 EXPORT_SYMBOL(cros_ec_prepare_tx);
 
-static int cros_ec_command_sendrecv(struct cros_ec_device *ec_dev,
-		uint16_t cmd, void *out_buf, int out_len,
-		void *in_buf, int in_len)
-{
-	struct cros_ec_command msg;
-
-	msg.version = cmd >> 8;
-	msg.command = cmd & 0xff;
-	msg.outdata = out_buf;
-	msg.outsize = out_len;
-	msg.indata = in_buf;
-	msg.insize = in_len;
-
-	return ec_dev->cmd_xfer(ec_dev, &msg);
-}
-
-static int cros_ec_command_recv(struct cros_ec_device *ec_dev,
-		uint16_t cmd, void *buf, int buf_len)
-{
-	return cros_ec_command_sendrecv(ec_dev, cmd, NULL, 0, buf, buf_len);
-}
-
-static int cros_ec_command_send(struct cros_ec_device *ec_dev,
-		uint16_t cmd, void *buf, int buf_len)
-{
-	return cros_ec_command_sendrecv(ec_dev, cmd, buf, buf_len, NULL, 0);
-}
-
 static irqreturn_t ec_irq_thread(int irq, void *data)
 {
 	struct cros_ec_device *ec_dev = data;
@@ -104,10 +76,6 @@ int cros_ec_register(struct cros_ec_device *ec_dev)
 
 	BLOCKING_INIT_NOTIFIER_HEAD(&ec_dev->event_notifier);
 
-	ec_dev->command_send = cros_ec_command_send;
-	ec_dev->command_recv = cros_ec_command_recv;
-	ec_dev->command_sendrecv = cros_ec_command_sendrecv;
-
 	if (ec_dev->din_size) {
 		ec_dev->din = devm_kzalloc(dev, ec_dev->din_size, GFP_KERNEL);
 		if (!ec_dev->din)
diff --git a/include/linux/mfd/cros_ec.h b/include/linux/mfd/cros_ec.h
index 2b0c598..60c0880 100644
--- a/include/linux/mfd/cros_ec.h
+++ b/include/linux/mfd/cros_ec.h
@@ -63,9 +63,10 @@ struct cros_ec_command {
  * @was_wake_device: true if this device was set to wake the system from
  * sleep at the last suspend
  * @event_notifier: interrupt event notifier for transport devices
- * @command_send: send a command
- * @command_recv: receive a response
- * @command_sendrecv: send a command and receive a response
+ * @cmd_xfer: send command to EC and get response
+ *     Returns 0 if the communication succeeded, but that doesn't mean the EC
+ *     was happy with the command it got. Caller should check msg.result for
+ *     the EC's result code.
  *
  * @priv: Private data
  * @irq: Interrupt to use
@@ -83,7 +84,6 @@ struct cros_ec_command {
  * @parent: pointer to parent device (e.g. i2c or spi device)
  * @wake_enabled: true if this device can wake the system from sleep
  * @lock: one transaction at a time
- * @cmd_xfer: low-level channel to the EC
  */
 struct cros_ec_device {
 
@@ -94,13 +94,8 @@ struct cros_ec_device {
 	bool was_wake_device;
 	struct class *cros_class;
 	struct blocking_notifier_head event_notifier;
-	int (*command_send)(struct cros_ec_device *ec,
-			    uint16_t cmd, void *out_buf, int out_len);
-	int (*command_recv)(struct cros_ec_device *ec,
-			    uint16_t cmd, void *in_buf, int in_len);
-	int (*command_sendrecv)(struct cros_ec_device *ec,
-				uint16_t cmd, void *out_buf, int out_len,
-				void *in_buf, int in_len);
+	int (*cmd_xfer)(struct cros_ec_device *ec,
+			struct cros_ec_command *msg);
 
 	/* These are used to implement the platform-specific interface */
 	void *priv;
@@ -112,8 +107,6 @@ struct cros_ec_device {
 	struct device *parent;
 	bool wake_enabled;
 	struct mutex lock;
-	int (*cmd_xfer)(struct cros_ec_device *ec,
-			struct cros_ec_command *msg);
 };
 
 /**
-- 
2.0.0.526.g5318336


^ permalink raw reply related

* [PATCH v4 0/2] Input: Support in the elantech driver of the trackpoint present on for instance Lenovo L530
From: Ulrik De Bie @ 2014-06-16 21:53 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-input, Hans de Goede, David Herrmann, ulrik.debie-os
In-Reply-To: <CANq1E4S5xVuWO3THy2rB5O_8fsLeYCWAres2Xk5Pf1j1_Po=og@mail.gmail.com>

Patch 1 adds support for trackpoint on elantech driver for v3 models.
Patch 2 adds a psmouse_reset when the elantech probes fails. Patch 2 depends 
on Patch 1.

Changes since v3:
* Patch1: added (correct) error after input_allocate_device failure in elantech_init()
* Patch2: added more explanation to the why 

Changes since v2:
* psmouse_reset change is now moved to a separate patch
* comments/white spaces/newlines  cleanup
* Unexpected trackpoint message warning now only printed once
* removed some unnecessary casts
* Deleted etd->trackpoint_present and use instead etd->tp_dev to indicate the
  presence of a trackpoint
* Propagate the error when elantech_init fails

Changes since v1:
* New patch now with reference to 3.14rc1
* Added etd->trackpoint_present to indicate presence of trackpoint (based
  on MSB of etd->capabilities[0])
* trackpoint will only be registered now when MSB of etd->capabilities[0] is
  set; got confirmation that this is the indicator of trackpoint
* Added input_unregister_device/input_free_device in elantech_disconnect()
* Fixed a bug in cleaning up when elantech_init fails
* Rename commit to be more specific (now also applicable to future elantech
  v3 models with trackpoint)
* input device name 'TPPS/2 IBM TrackPoint' changed to
  'Elantech PS/2 TrackPoint', this patch is not ibm/lenovo specific!
* dev2 renamed to tp_dev to indicate that this is the trackpoint device
* etd->phys renamed to etd->tp_phys
* Added Lenovo 530 and Fujitsu H730 to the laptop list because those are now
  also known.
* Added psmouse_reset at the end of elantech_init when it fails
* Added warning when trackpoint packets are received with no trackpoint detected

The patches are also available from:
https://github.com/ulrikdb/linux/commit/53d8424bc2143d34c2be76064892079fe1917a9e
https://github.com/ulrikdb/linux/commit/cf12fcc6cdf51a7aad48d056750478aecd9d7eca


Ulrik De Bie (2):
  elantech: Add support for trackpoint found on some v3 models
  elantech: Call psmouse_reset when elantech probe fails

 drivers/input/mouse/elantech.c | 123 +++++++++++++++++++++++++++++++++++++++--
 drivers/input/mouse/elantech.h |   3 +
 2 files changed, 122 insertions(+), 4 deletions(-)

-- 
2.0.0


^ permalink raw reply

* [PATCH v4 1/2] elantech: Add support for trackpoint found on some v3 models
From: Ulrik De Bie @ 2014-06-16 21:53 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-input, Hans de Goede, David Herrmann, ulrik.debie-os
In-Reply-To: <1402955589-23709-1-git-send-email-ulrik.debie-os@e2big.org>

Some elantech v3 touchpad equipped laptops also have a trackpoint, before
this commit, these give sync errors. With this patch, the trackpoint is
provided as another input device: 'Elantech PS/2 TrackPoint'

The patch will also output messages that do not follow the expected pattern.
In the mean time I've seen 2 unknown packets occasionally:
0x04 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00
0x00 , 0x00 , 0x00 , 0x02 , 0x00 , 0x00
I don't know what those are for, but they can be safely ignored.

Currently all packets that are not known to v3 touchpad and where
packet[3] (the fourth byte) lowest nibble is 6 are now recognized as
PACKET_TRACKPOINT and processed by the new elantech_report_trackpoint.

This has been verified to work on a laptop Lenovo L530 where the
touchpad/trackpoint combined identify themselves as:
psmouse serio1: elantech: assuming hardware version 3 (with firmware version 0x350f02)
psmouse serio1: elantech: Synaptics capabilities query result 0xb9, 0x15, 0x0c.

Reviewed-by: David Herrmann <dh.herrmann@gmail.com>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Ulrik De Bie <ulrik.debie-os@e2big.org>
---
 drivers/input/mouse/elantech.c | 122 +++++++++++++++++++++++++++++++++++++++--
 drivers/input/mouse/elantech.h |   3 +
 2 files changed, 121 insertions(+), 4 deletions(-)

diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
index ee2a04d..5dd620a 100644
--- a/drivers/input/mouse/elantech.c
+++ b/drivers/input/mouse/elantech.c
@@ -403,6 +403,71 @@ static void elantech_report_absolute_v2(struct psmouse *psmouse)
 	input_sync(dev);
 }
 
+static void elantech_report_trackpoint(struct psmouse *psmouse,
+				       int packet_type)
+{
+	/*
+	 * byte 0:  0   0 ~sx ~sy   0   M   R   L
+	 * byte 1: sx   0   0   0   0   0   0   0
+	 * byte 2: sy   0   0   0   0   0   0   0
+	 * byte 3:  0   0  sy  sx   0   1   1   0
+	 * byte 4: x7  x6  x5  x4  x3  x2  x1  x0
+	 * byte 5: y7  y6  y5  y4  y3  y2  y1  y0
+	 *
+	 * x and y are written in two's complement spread
+	 * over 9 bits with sx/sy the relative top bit and
+	 * x7..x0 and y7..y0 the lower bits.
+	 * The sign of y is opposite to what the input driver
+	 * expects for a relative movement
+	 */
+
+	struct elantech_data *etd = psmouse->private;
+	struct input_dev *tp_dev = etd->tp_dev;
+	unsigned char *packet = psmouse->packet;
+	int x, y;
+	u32 t;
+
+	if (!tp_dev) {
+		static bool __section(.data.unlikely) __warned;
+
+		if (!__warned) {
+			__warned = true;
+			psmouse_err(psmouse, "Unexpected trackpoint message\n");
+			if (etd->debug == 1)
+				elantech_packet_dump(psmouse);
+		}
+
+		return;
+	}
+
+	input_report_key(tp_dev, BTN_LEFT, packet[0] & 0x01);
+	input_report_key(tp_dev, BTN_RIGHT, packet[0] & 0x02);
+	input_report_key(tp_dev, BTN_MIDDLE, packet[0] & 0x04);
+
+	x = ((packet[1] & 0x80) ? 0U : 0xFFFFFF00U) | packet[4];
+	y = -(int)(((packet[2] & 0x80) ? 0U : 0xFFFFFF00U) | packet[5]);
+
+	input_report_rel(tp_dev, REL_X, x);
+	input_report_rel(tp_dev, REL_Y, y);
+
+	t = (((u32)packet[0] & 0xF8) << 24) | ((u32)packet[1] << 16)
+		| (u32)packet[2] << 8 | (u32)packet[3];
+	switch (t) {
+	case 0x00808036U:
+	case 0x10008026U:
+	case 0x20800016U:
+	case 0x30000006U:
+		break;
+	default:
+		/* Dump unexpected packet sequences if debug=1 (default) */
+		if (etd->debug == 1)
+			elantech_packet_dump(psmouse);
+		break;
+	}
+
+	input_sync(tp_dev);
+}
+
 /*
  * Interpret complete data packets and report absolute mode input events for
  * hardware version 3. (12 byte packets for two fingers)
@@ -715,6 +780,8 @@ static int elantech_packet_check_v3(struct psmouse *psmouse)
 
 		if ((packet[0] & 0x0c) == 0x0c && (packet[3] & 0xce) == 0x0c)
 			return PACKET_V3_TAIL;
+		if ((packet[3] & 0x0f) == 0x06)
+			return PACKET_TRACKPOINT;
 	}
 
 	return PACKET_UNKNOWN;
@@ -798,7 +865,10 @@ static psmouse_ret_t elantech_process_byte(struct psmouse *psmouse)
 		if (packet_type == PACKET_UNKNOWN)
 			return PSMOUSE_BAD_DATA;
 
-		elantech_report_absolute_v3(psmouse, packet_type);
+		if (packet_type == PACKET_TRACKPOINT)
+			elantech_report_trackpoint(psmouse, packet_type);
+		else
+			elantech_report_absolute_v3(psmouse, packet_type);
 		break;
 
 	case 4:
@@ -1018,8 +1088,10 @@ static int elantech_get_resolution_v4(struct psmouse *psmouse,
  * Asus UX31               0x361f00        20, 15, 0e      clickpad
  * Asus UX32VD             0x361f02        00, 15, 0e      clickpad
  * Avatar AVIU-145A2       0x361f00        ?               clickpad
+ * Fujitsu H730            0x570f00        c0, 14, 0c      3 hw buttons (**)
  * Gigabyte U2442          0x450f01        58, 17, 0c      2 hw buttons
  * Lenovo L430             0x350f02        b9, 15, 0c      2 hw buttons (*)
+ * Lenovo L530             0x350f02        b9, 15, 0c      2 hw buttons (*)
  * Samsung NF210           0x150b00        78, 14, 0a      2 hw buttons
  * Samsung NP770Z5E        0x575f01        10, 15, 0f      clickpad
  * Samsung NP700Z5B        0x361f06        21, 15, 0f      clickpad
@@ -1029,6 +1101,8 @@ static int elantech_get_resolution_v4(struct psmouse *psmouse,
  * Samsung RF710           0x450f00        ?               2 hw buttons
  * System76 Pangolin       0x250f01        ?               2 hw buttons
  * (*) + 3 trackpoint buttons
+ * (**) + 0 trackpoint buttons
+ * Note: Lenovo L430 and Lenovo L430 have the same fw_version/caps
  */
 static void elantech_set_buttonpad_prop(struct psmouse *psmouse)
 {
@@ -1324,6 +1398,10 @@ int elantech_detect(struct psmouse *psmouse, bool set_properties)
  */
 static void elantech_disconnect(struct psmouse *psmouse)
 {
+	struct elantech_data *etd = psmouse->private;
+
+	if (etd->tp_dev)
+		input_unregister_device(etd->tp_dev);
 	sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj,
 			   &elantech_attr_group);
 	kfree(psmouse->private);
@@ -1438,8 +1516,10 @@ static int elantech_set_properties(struct elantech_data *etd)
 int elantech_init(struct psmouse *psmouse)
 {
 	struct elantech_data *etd;
-	int i, error;
+	int i;
+	int error = -EINVAL;
 	unsigned char param[3];
+	struct input_dev *tp_dev;
 
 	psmouse->private = etd = kzalloc(sizeof(struct elantech_data), GFP_KERNEL);
 	if (!etd)
@@ -1498,14 +1578,48 @@ int elantech_init(struct psmouse *psmouse)
 		goto init_fail;
 	}
 
+	/* The MSB indicates the presence of the trackpoint */
+	if ((etd->capabilities[0] & 0x80) == 0x80) {
+		tp_dev = input_allocate_device();
+
+		if (!tp_dev) {
+			error = -ENOMEM;
+			goto init_fail_tp_alloc;
+		}
+
+		etd->tp_dev = tp_dev;
+		snprintf(etd->tp_phys, sizeof(etd->tp_phys), "%s/input1",
+			psmouse->ps2dev.serio->phys);
+		tp_dev->phys = etd->tp_phys;
+		tp_dev->name = "Elantech PS/2 TrackPoint";
+		tp_dev->id.bustype = BUS_I8042;
+		tp_dev->id.vendor  = 0x0002;
+		tp_dev->id.product = PSMOUSE_ELANTECH;
+		tp_dev->id.version = 0x0000;
+		tp_dev->dev.parent = &psmouse->ps2dev.serio->dev;
+		tp_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+		tp_dev->relbit[BIT_WORD(REL_X)] =
+			BIT_MASK(REL_X) | BIT_MASK(REL_Y);
+		tp_dev->keybit[BIT_WORD(BTN_LEFT)] =
+			BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_MIDDLE) |
+			BIT_MASK(BTN_RIGHT);
+		error = input_register_device(etd->tp_dev);
+		if (error < 0)
+			goto init_fail_tp_reg;
+	}
+
 	psmouse->protocol_handler = elantech_process_byte;
 	psmouse->disconnect = elantech_disconnect;
 	psmouse->reconnect = elantech_reconnect;
 	psmouse->pktsize = etd->hw_version > 1 ? 6 : 4;
 
 	return 0;
-
+ init_fail_tp_reg:
+	input_free_device(tp_dev);
+ init_fail_tp_alloc:
+	sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj,
+			   &elantech_attr_group);
  init_fail:
 	kfree(etd);
-	return -1;
+	return error;
 }
diff --git a/drivers/input/mouse/elantech.h b/drivers/input/mouse/elantech.h
index 9e0e2a1..e410336 100644
--- a/drivers/input/mouse/elantech.h
+++ b/drivers/input/mouse/elantech.h
@@ -94,6 +94,7 @@
 #define PACKET_V4_HEAD			0x05
 #define PACKET_V4_MOTION		0x06
 #define PACKET_V4_STATUS		0x07
+#define PACKET_TRACKPOINT		0x08
 
 /*
  * track up to 5 fingers for v4 hardware
@@ -114,6 +115,8 @@ struct finger_pos {
 };
 
 struct elantech_data {
+	struct input_dev *tp_dev;	/* Relative device for trackpoint */
+	char	tp_phys[32];
 	unsigned char reg_07;
 	unsigned char reg_10;
 	unsigned char reg_11;
-- 
2.0.0


^ permalink raw reply related

* [PATCH v4 2/2] elantech: Call psmouse_reset when elantech probe fails
From: Ulrik De Bie @ 2014-06-16 21:53 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: linux-input, Hans de Goede, David Herrmann, ulrik.debie-os
In-Reply-To: <1402955589-23709-1-git-send-email-ulrik.debie-os@e2big.org>

elantech_init() calls elantech_set_absolute_mode which sets the driver in
an absolute mode. When after this the elantech_init fails, it is best
to turn the ps/2 mouse emulation mode back on by calling psmouse_reset()
so that it can work as a regular mouse.

Signed-off-by: Ulrik De Bie <ulrik.debie-os@e2big.org>
---
 drivers/input/mouse/elantech.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
index 5dd620a..771291c 100644
--- a/drivers/input/mouse/elantech.c
+++ b/drivers/input/mouse/elantech.c
@@ -1620,6 +1620,7 @@ int elantech_init(struct psmouse *psmouse)
 	sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj,
 			   &elantech_attr_group);
  init_fail:
+	psmouse_reset(psmouse);
 	kfree(etd);
 	return error;
 }
-- 
2.0.0


^ permalink raw reply related

* [PATCH v6] leds: USB: HID: Add support for MSI GT683R led panels
From: Janne Kanniainen @ 2014-06-16 22:01 UTC (permalink / raw)
  To: jkosina
  Cc: johan, cooloney, linux-kernel, linux-leds, linux-usb, linux-input,
	Janne Kanniainen
In-Reply-To: <1402939415-32056-1-git-send-email-janne.kanniainen@gmail.com>

This driver adds support for USB controlled led panels that exists in
MSI GT683R laptop

Signed-off-by: Janne Kanniainen <janne.kanniainen@gmail.com>
---
Changes in v2:
	- sorted headers to alphabetic order
	- using devm_kzalloc
	- using BIT(n)
	- using usb_control_msg instead of usb_submit_urb
	- removing unneeded code

Changes in v3:
	- implemented as HID device
	- some cleanups and bug fixes

Changes in v4:
	- more cleanups
	- support for selecting leds
	- suppport for selecting status

Changes in v5:
	- mode attribute documented under Documentation/ABI
	- made array for led_classdev
	- led devices uses now recommended naming scheme

Changes in v6:
	- flush_work added
	- using hid device name instead of hard coded gt683r
	- allocating name buffers with devm_kzalloc

There was a bug with "for", so I fixed it.

 .../ABI/testing/sysfs-class-hid-driver-gt683r      |  14 +
 drivers/hid/Kconfig                                |  16 ++
 drivers/hid/Makefile                               |   1 +
 drivers/hid/hid-core.c                             |   1 +
 drivers/hid/hid-gt683r.c                           | 318 +++++++++++++++++++++
 drivers/hid/hid-ids.h                              |   2 +-
 drivers/hid/usbhid/hid-quirks.c                    |   2 +-
 7 files changed, 352 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
 create mode 100644 drivers/hid/hid-gt683r.c

diff --git a/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
new file mode 100644
index 0000000..6d0bd80
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-hid-driver-gt683r
@@ -0,0 +1,14 @@
+What:		/sys/class/hidraw/<hidraw>/device/mode
+Date:		Jun 2014
+KernelVersion:	3.17
+Contact:	Janne Kanniainen <janne.kanniainen@gmail.com>
+Description:
+		Set the mode of LEDs
+
+		0 - normal
+		1 - audio
+		2 - breathing
+
+		Normal: LEDs are on
+		Audio:  LEDs brightness depends on sound level
+		Breathing: LEDs brightness varies at human breathing rate
\ No newline at end of file
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 7af9d0b..b88cabd 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -261,6 +261,22 @@ config HOLTEK_FF
 	  Say Y here if you have a Holtek On Line Grip based game controller
 	  and want to have force feedback support for it.
 
+config HID_GT683R
+	tristate "MSI GT68xR LED support"
+	depends on LEDS_CLASS && USB_HID
+	---help---
+	Say Y here if you want to enable support for the MSI GT68xR LEDS
+
+	This driver support following modes:
+	  - Normal: LEDs are on
+	  - Audio:  LEDs brightness depends on sound level
+	  - Breathing: LEDs brightness varies at human breathing rate
+
+	You can also select which leds you want to enable.
+
+	Currently the following devices are know to be supported:
+	  - MSI GT683R
+
 config HID_HUION
 	tristate "Huion tablets"
 	depends on USB_HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index fc712dd..7129311 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_HID_EMS_FF)	+= hid-emsff.o
 obj-$(CONFIG_HID_ELECOM)	+= hid-elecom.o
 obj-$(CONFIG_HID_ELO)		+= hid-elo.o
 obj-$(CONFIG_HID_EZKEY)		+= hid-ezkey.o
+obj-$(CONFIG_HID_GT683R)	+= hid-gt683r.o
 obj-$(CONFIG_HID_GYRATION)	+= hid-gyration.o
 obj-$(CONFIG_HID_HOLTEK)	+= hid-holtek-kbd.o
 obj-$(CONFIG_HID_HOLTEK)	+= hid-holtek-mouse.o
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index da52279..ec88fdb 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1827,6 +1827,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
 	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_WIRELESS_OPTICAL_DESKTOP_3_0) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_MICROSOFT, USB_DEVICE_ID_MS_OFFICE_KB) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_MONTEREY, USB_DEVICE_ID_GENIUS_KB29E) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_1) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_NTRIG, USB_DEVICE_ID_NTRIG_TOUCH_SCREEN_2) },
diff --git a/drivers/hid/hid-gt683r.c b/drivers/hid/hid-gt683r.c
new file mode 100644
index 0000000..1b16250
--- /dev/null
+++ b/drivers/hid/hid-gt683r.c
@@ -0,0 +1,318 @@
+/*
+ * MSI GT683R led driver
+ *
+ * Copyright (c) 2014 Janne Kanniainen <janne.kanniainen@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+#define GT683R_BUFFER_SIZE			8
+
+/*
+ * GT683R_LED_OFF: all LEDs are off
+ * GT683R_LED_AUDIO: LEDs brightness depends
+ * on sound level
+ * GT683R_LED_BREATHING: LEDs brightness varies
+ * at human breathing rate
+ * GT683R_LED_NORMAL: LEDs are on
+ */
+enum gt683r_led_mode {
+	GT683R_LED_OFF = 0,
+	GT683R_LED_AUDIO = 2,
+	GT683R_LED_BREATHING = 3,
+	GT683R_LED_NORMAL = 5
+};
+
+enum gt683r_panels {
+	GT683R_LED_BACK,
+	GT683R_LED_SIDE,
+	GT683R_LED_FRONT,
+	GT683R_LED_COUNT,
+};
+
+static const char * const gt683r_panel_names[] = {
+	"back",
+	"side",
+	"front",
+};
+
+struct gt683r_led {
+	struct hid_device *hdev;
+	struct led_classdev led_devs[GT683R_LED_COUNT];
+	struct mutex lock;
+	struct work_struct work;
+	enum led_brightness brightnesses[GT683R_LED_COUNT];
+	enum gt683r_led_mode mode;
+};
+
+static const struct hid_device_id gt683r_led_id[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL) },
+	{ }
+};
+
+static void gt683r_brightness_set(struct led_classdev *led_cdev,
+				enum led_brightness brightness)
+{
+	int i;
+	struct device *dev = led_cdev->dev->parent;
+	struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+	struct gt683r_led *led = hid_get_drvdata(hdev);
+
+	for (i = 0; i < GT683R_LED_COUNT; i++) {
+		if (&led->led_devs[i] == led_cdev)
+			break;
+	}
+
+	if (i < GT683R_LED_COUNT) {
+		led->brightnesses[i] = brightness;
+		schedule_work(&led->work);
+	}
+}
+
+static ssize_t mode_show(struct device *dev,
+				struct device_attribute *attr,
+				char *buf)
+{
+	u8 sysfs_mode;
+	struct hid_device *hdev =
+		container_of(dev, struct hid_device, dev);
+	struct gt683r_led *led = hid_get_drvdata(hdev);
+
+	if (led->mode == GT683R_LED_NORMAL)
+		sysfs_mode = 0;
+	else if (led->mode == GT683R_LED_AUDIO)
+		sysfs_mode = 1;
+	else
+		sysfs_mode = 2;
+
+	return scnprintf(buf, PAGE_SIZE, "%u\n", sysfs_mode);
+}
+
+static ssize_t mode_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	u8 sysfs_mode;
+	struct hid_device *hdev =
+		container_of(dev, struct hid_device, dev);
+	struct gt683r_led *led = hid_get_drvdata(hdev);
+
+	mutex_lock(&led->lock);
+
+	if (kstrtou8(buf, 10, &sysfs_mode) || sysfs_mode > 2) {
+		count = -EINVAL;
+		goto fail;
+	}
+
+	if (sysfs_mode == 0)
+		led->mode = GT683R_LED_NORMAL;
+	else if (sysfs_mode == 1)
+		led->mode = GT683R_LED_AUDIO;
+	else
+		led->mode = GT683R_LED_BREATHING;
+
+fail:
+	mutex_unlock(&led->lock);
+
+	if (count != -EINVAL)
+		schedule_work(&led->work);
+
+	return count;
+}
+
+static int gt683r_led_snd_msg(struct gt683r_led *led, u8 *msg)
+{
+	int ret;
+
+	ret = hid_hw_raw_request(led->hdev, msg[0], msg, GT683R_BUFFER_SIZE,
+				HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
+	if (ret != GT683R_BUFFER_SIZE) {
+		hid_err(led->hdev,
+			"failed to send set report request: %i\n", ret);
+		if (ret < 0)
+			return ret;
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int gt683r_leds_set(struct gt683r_led *led, u8 leds)
+{
+	int ret;
+	u8 *buffer;
+
+	buffer = kzalloc(GT683R_BUFFER_SIZE, GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
+
+	buffer[0] = 0x01;
+	buffer[1] = 0x02;
+	buffer[2] = 0x30;
+	buffer[3] = leds;
+	ret = gt683r_led_snd_msg(led, buffer);
+
+	kfree(buffer);
+	return ret;
+}
+
+static int gt683r_mode_set(struct gt683r_led *led, u8 mode)
+{
+	int ret;
+	u8 *buffer;
+
+	buffer = kzalloc(GT683R_BUFFER_SIZE, GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
+
+	buffer[0] = 0x01;
+	buffer[1] = 0x02;
+	buffer[2] = 0x20;
+	buffer[3] = mode;
+	buffer[4] = 0x01;
+	ret = gt683r_led_snd_msg(led, buffer);
+
+	kfree(buffer);
+	return ret;
+}
+
+static void gt683r_led_work(struct work_struct *work)
+{
+	u8 leds = 0, mode;
+	struct gt683r_led *led =
+		container_of(work, struct gt683r_led, work);
+
+	mutex_lock(&led->lock);
+
+	if (led->brightnesses[GT683R_LED_BACK])
+		leds |= BIT(GT683R_LED_BACK);
+
+	if (led->brightnesses[GT683R_LED_SIDE])
+		leds |= BIT(GT683R_LED_SIDE);
+
+	if (led->brightnesses[GT683R_LED_FRONT])
+		leds |= BIT(GT683R_LED_FRONT);
+
+	if (gt683r_leds_set(led, leds))
+		goto fail;
+
+	if (leds)
+		mode = led->mode;
+	else
+		mode = GT683R_LED_OFF;
+
+	gt683r_mode_set(led, mode);
+fail:
+	mutex_unlock(&led->lock);
+}
+
+static DEVICE_ATTR_RW(mode);
+
+static int gt683r_led_probe(struct hid_device *hdev,
+			const struct hid_device_id *id)
+{
+	int ret, i, name_sz;
+	char *name;
+	struct gt683r_led *led;
+
+	led = devm_kzalloc(&hdev->dev, sizeof(struct gt683r_led), GFP_KERNEL);
+	if (!led)
+		return -ENOMEM;
+
+	led->mode = GT683R_LED_NORMAL;
+	led->hdev = hdev;
+	hid_set_drvdata(hdev, led);
+
+	ret = hid_parse(hdev);
+	if (ret) {
+		hid_err(hdev, "hid parsing failed\n");
+		goto fail;
+	}
+
+	ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
+	if (ret) {
+		hid_err(hdev, "hw start failed\n");
+		goto fail;
+	}
+
+	for (i = 0; i < GT683R_LED_COUNT; i++) {
+		name_sz = strlen(dev_name(&hdev->dev)) +
+			strlen(gt683r_panel_names[i]) + 3;
+
+		name = devm_kzalloc(&hdev->dev, sizeof(char) * name_sz,
+				GFP_KERNEL);
+
+		snprintf(name, name_sz, "%s::%s",
+			dev_name(&hdev->dev), gt683r_panel_names[i]);
+		led->led_devs[i].name = name;
+		led->led_devs[i].max_brightness = 1;
+		led->led_devs[i].brightness_set = gt683r_brightness_set;
+		ret = led_classdev_register(&hdev->dev, &led->led_devs[i]);
+		if (ret) {
+			hid_err(hdev, "could not register led device\n");
+			goto fail2;
+		}
+	}
+
+	ret = device_create_file(&led->hdev->dev,
+				&dev_attr_mode);
+	if (ret) {
+		hid_err(hdev, "could not make mode attribute file\n");
+		goto fail2;
+	}
+
+	mutex_init(&led->lock);
+	INIT_WORK(&led->work, gt683r_led_work);
+
+	return 0;
+
+fail2:
+	for (; i >= 0; i--)
+		led_classdev_unregister(&led->led_devs[i]);
+	hid_hw_stop(hdev);
+fail:
+	return ret;
+}
+
+static void gt683r_led_remove(struct hid_device *hdev)
+{
+	int i;
+	struct gt683r_led *led = hid_get_drvdata(hdev);
+
+	for (i = 0; i < GT683R_LED_COUNT; i++)
+		led_classdev_unregister(&led->led_devs[i]);
+	device_remove_file(&hdev->dev,
+			&dev_attr_mode);
+	flush_work(&led->work);
+	hid_hw_stop(hdev);
+}
+
+static struct hid_driver gt683r_led_driver = {
+	.probe = gt683r_led_probe,
+	.remove = gt683r_led_remove,
+	.name = "gt683r_led",
+	.id_table = gt683r_led_id,
+};
+
+module_hid_driver(gt683r_led_driver);
+
+MODULE_AUTHOR("Janne Kanniainen");
+MODULE_DESCRIPTION("MSI GT683R led driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 34bb220..3692d37 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -641,7 +641,7 @@
 #define USB_DEVICE_ID_GENIUS_KB29E	0x3004
 
 #define USB_VENDOR_ID_MSI		0x1770
-#define USB_DEVICE_ID_MSI_GX680R_LED_PANEL	0xff00
+#define USB_DEVICE_ID_MSI_GT683R_LED_PANEL 0xff00
 
 #define USB_VENDOR_ID_NATIONAL_SEMICONDUCTOR 0x0400
 #define USB_DEVICE_ID_N_S_HARMONY	0xc359
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index 8e4ddb3..c640e1d 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -73,7 +73,7 @@ static const struct hid_blacklist {
 	{ USB_VENDOR_ID_FORMOSA, USB_DEVICE_ID_FORMOSA_IR_RECEIVER, HID_QUIRK_NO_INIT_REPORTS },
 	{ USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET },
 	{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET },
-	{ USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GX680R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
+	{ USB_VENDOR_ID_MSI, USB_DEVICE_ID_MSI_GT683R_LED_PANEL, HID_QUIRK_NO_INIT_REPORTS },
 	{ USB_VENDOR_ID_NEXIO, USB_DEVICE_ID_NEXIO_MULTITOUCH_PTI0750, HID_QUIRK_NO_INIT_REPORTS },
 	{ USB_VENDOR_ID_NOVATEK, USB_DEVICE_ID_NOVATEK_MOUSE, HID_QUIRK_NO_INIT_REPORTS },
 	{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NO_INIT_REPORTS },
-- 
1.9.2


^ permalink raw reply related

* Re: [PATCH v4 2/2] elantech: Call psmouse_reset when elantech probe fails
From: Hans de Goede @ 2014-06-17  6:08 UTC (permalink / raw)
  To: Ulrik De Bie, Dmitry Torokhov; +Cc: linux-input, David Herrmann
In-Reply-To: <1402955589-23709-3-git-send-email-ulrik.debie-os@e2big.org>

Hi,

On 06/16/2014 11:53 PM, Ulrik De Bie wrote:
> elantech_init() calls elantech_set_absolute_mode which sets the driver in
> an absolute mode. When after this the elantech_init fails, it is best
> to turn the ps/2 mouse emulation mode back on by calling psmouse_reset()
> so that it can work as a regular mouse.
> 
> Signed-off-by: Ulrik De Bie <ulrik.debie-os@e2big.org>

Looks good, this patch is:

Reviewed-by: Hans de Goede <hdegoede@redhat.com>

Regards,

Hans

> ---
>  drivers/input/mouse/elantech.c | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
> index 5dd620a..771291c 100644
> --- a/drivers/input/mouse/elantech.c
> +++ b/drivers/input/mouse/elantech.c
> @@ -1620,6 +1620,7 @@ int elantech_init(struct psmouse *psmouse)
>  	sysfs_remove_group(&psmouse->ps2dev.serio->dev.kobj,
>  			   &elantech_attr_group);
>   init_fail:
> +	psmouse_reset(psmouse);
>  	kfree(etd);
>  	return error;
>  }
> 

^ permalink raw reply

* Re: [PATCH 1/2] touchscreen: sun4i-ts: A10 (sun4i) has double the temperature precision
From: Hans de Goede @ 2014-06-17  6:31 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Maxime Ripard, Tong Zhang, linux-input-u79uwXL29TY76Z2rM5mHXA,
	LM Sensors, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree, linux-sunxi-/JYPxA39Uh5TLH3MbocFFw
In-Reply-To: <20140616213048.GA422-WlK9ik9hQGAhIp7JRqBPierSzoNAToWh@public.gmane.org>

Hi,

On 06/16/2014 11:30 PM, Dmitry Torokhov wrote:
> Hi Hans,
> 
> On Mon, Jun 16, 2014 at 08:24:28PM +0200, Hans de Goede wrote:
>> Testing has revealed that the temperature in the rtp controller of the A10
>> (sun4i) SoC has a resolution of 50 milli degrees / step, where as the
>> A13 (sun5i) and later models have 100 milli degrees / step.
>>
>> Add a new sun5i-a13-ts compatible to differentiate the newer models and
>> set the resolution based on the compatible string.
>>
>> This fixes the temperature reported on the A10 being twice as high as expected.
> 
> Should we maybe add explicit temperature steps property so that we won;t
> need to patch again if they decided to change resolution again?

I can understand where your coming from, but in general the rule for
devicetree bindings is to not add properties for things which are purely
dependent on the SoC generation. Those must be deferred from the compatible
string.

Regards,

Hans

^ permalink raw reply

* Re: [PATCH v6 3/7] mfd: AXP20x: Add bindings documentation
From: Boris BREZILLON @ 2014-06-17  7:24 UTC (permalink / raw)
  To: Carlo Caione, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
	maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	hdegoede-H+wXaHxf7aLQT0dZR+AlfA, emilio-0Z03zUJReD5OxF6Tv1QG9Q,
	wens-jdAy2FN1RRM, sameo-VuQAYsv1563Yd54FQh9/CA,
	dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w,
	linux-input-u79uwXL29TY76Z2rM5mHXA,
	linux-doc-u79uwXL29TY76Z2rM5mHXA,
	lgirdwood-Re5JQEeQqe8AvxtiuMwx3w, broonie-DgEjT+Ai2ygdnm+yROfE0A,
	lee.jones-QSEj5FYQhm4dnm+yROfE0A
In-Reply-To: <1400528868-5971-4-git-send-email-carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>

Hello,

On 19/05/2014 21:47, Carlo Caione wrote:
> Bindings documentation for the AXP20x driver. In this file also
> sub-nodes are documented.

I think this patch has been forgotten (I didn't find it in linus' tree
or linux-next tree).

Best Regards,

Boris

>
> Signed-off-by: Carlo Caione <carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>
> Acked-by: Maxime Ripard <maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> ---
>  Documentation/devicetree/bindings/mfd/axp20x.txt | 93 ++++++++++++++++++++++++
>  1 file changed, 93 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/mfd/axp20x.txt
>
> diff --git a/Documentation/devicetree/bindings/mfd/axp20x.txt b/Documentation/devicetree/bindings/mfd/axp20x.txt
> new file mode 100644
> index 0000000..cc9e01b
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mfd/axp20x.txt
> @@ -0,0 +1,93 @@
> +AXP202/AXP209 device tree bindings
> +
> +The axp20x family current members :-
> +axp202 (X-Powers)
> +axp209 (X-Powers)
> +
> +Required properties:
> +- compatible: "x-powers,axp202" or "x-powers,axp209"
> +- reg: The I2C slave address for the AXP chip
> +- interrupt-parent: The parent interrupt controller
> +- interrupts: Interrupt specifiers for interrupt sources
> +- interrupt-controller: axp20x has its own internal IRQs
> +- #interrupt-cells: Should be set to 1
> +- acin-supply: The input supply for LDO1
> +- vin2-supply: The input supply for DCDC2
> +- vin3-supply: The input supply for DCDC3
> +- ldo24in-supply: The input supply for LDO2, LDO4
> +- ldo3in-supply: The input supply for LDO3
> +- ldo5in-supply: The input supply for LDO5
> +
> +- regulators: A node that houses a sub-node for each regulator. The regulators are
> +	      bound using their name as listed here: dcdc2, dcdc3, ldo1, ldo2,
> +	      ldo3, ldo4, ldo5.  The bindings details of individual regulator
> +	      device can be found in:
> +	      Documentation/devicetree/bindings/regulator/regulator.txt with
> +	      the exception of x-powers,dcdc-freq
> +- x-powers,dcdc-freq: defines the work frequency of DC-DC in KHz
> +		      (range: 750-1875). Default: 1.5MHz
> +
> +Optional properties for DCDCs:
> +- x-powers,dcdc-workmode: 1 for PWM mode, 0 for AUTO mode
> +			  Default: AUTO mode
> +
> +Example:
> +
> +axp209: pmic@34 {
> +	compatible = "x-powers,axp209";
> +	reg = <0x34>;
> +	interrupt-parent = <&nmi_intc>;
> +	interrupts = <0 8>;
> +
> +	interrupt-controller;
> +	#interrupt-cells = <1>;
> +
> +	acin-supply = <&axp_ipsout_reg>;
> +	vin2-supply = <&axp_ipsout_reg>;
> +	vin3-supply = <&axp_ipsout_reg>;
> +	ldo24in-supply = <&axp_ipsout_reg>;
> +	ldo3in-supply = <&axp_ipsout_reg>;
> +	ldo5in-supply = <&axp_ipsout_reg>;
> +
> +	regulators {
> +		x-powers,dcdc-freq = <1500>;
> +
> +		axp_vcore_reg: dcdc2 {
> +			regulator-min-microvolt = <700000>;
> +			regulator-max-microvolt = <2275000>;
> +			regulator-always-on;
> +		};
> +
> +		axp_ddr_reg: dcdc3 {
> +			regulator-min-microvolt = <700000>;
> +			regulator-max-microvolt = <3500000>;
> +			regulator-always-on;
> +		};
> +
> +		axp_rtc_reg: ldo1 {
> +			regulator-always-on;
> +		};
> +
> +		axp_analog_reg: ldo2 {
> +			regulator-min-microvolt = <1800000>;
> +			regulator-max-microvolt = <3300000>;
> +			regulator-always-on;
> +		};
> +
> +		axp_pll_reg: ldo3 {
> +			regulator-min-microvolt = <700000>;
> +			regulator-max-microvolt = <3500000>;
> +		};
> +
> +		axp_hdmi_reg: ldo4 {
> +			regulator-min-microvolt = <1250000>;
> +			regulator-max-microvolt = <3300000>;
> +		};
> +
> +		axp_mic_reg: ldo5 {
> +			regulator-min-microvolt = <1800000>;
> +			regulator-max-microvolt = <3300000>;
> +		};
> +	};
> +};
> +

-- 
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply

* [PATCH v7 1/5] Input: pixcir_i2c_ts: Use Type-B Multi-Touch protocol
From: Roger Quadros @ 2014-06-17  9:31 UTC (permalink / raw)
  To: dmitry.torokhov, tony
  Cc: rydberg, balbi, dmurphy, mugunthanvnm, nsekhar, linux-input,
	linux-kernel, linux-omap, Roger Quadros
In-Reply-To: <1402997507-9736-1-git-send-email-rogerq@ti.com>

Switch to using the Type-B Multi-Touch protocol.

Reviewed-by: Henrik Rydberg <rydberg@euromail.se>
Signed-off-by: Roger Quadros <rogerq@ti.com>
---
 drivers/input/touchscreen/pixcir_i2c_ts.c | 125 ++++++++++++++++++++++--------
 1 file changed, 94 insertions(+), 31 deletions(-)

diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c
index 19c6c0f..0b01681 100644
--- a/drivers/input/touchscreen/pixcir_i2c_ts.c
+++ b/drivers/input/touchscreen/pixcir_i2c_ts.c
@@ -23,9 +23,12 @@
 #include <linux/slab.h>
 #include <linux/i2c.h>
 #include <linux/input.h>
+#include <linux/input/mt.h>
 #include <linux/input/pixcir_ts.h>
 #include <linux/gpio.h>
 
+#define PIXCIR_MAX_SLOTS       2
+
 struct pixcir_i2c_ts_data {
 	struct i2c_client *client;
 	struct input_dev *input;
@@ -33,12 +36,25 @@ struct pixcir_i2c_ts_data {
 	bool running;
 };
 
-static void pixcir_ts_poscheck(struct pixcir_i2c_ts_data *data)
+struct pixcir_touch {
+	int x;
+	int y;
+};
+
+struct pixcir_report_data {
+	int num_touches;
+	struct pixcir_touch touches[PIXCIR_MAX_SLOTS];
+};
+
+static void pixcir_ts_parse(struct pixcir_i2c_ts_data *tsdata,
+			    struct pixcir_report_data *report)
 {
-	struct pixcir_i2c_ts_data *tsdata = data;
 	u8 rdbuf[10], wrbuf[1] = { 0 };
+	u8 *bufptr;
 	u8 touch;
-	int ret;
+	int ret, i;
+
+	memset(report, 0, sizeof(struct pixcir_report_data));
 
 	ret = i2c_master_send(tsdata->client, wrbuf, sizeof(wrbuf));
 	if (ret != sizeof(wrbuf)) {
@@ -56,45 +72,85 @@ static void pixcir_ts_poscheck(struct pixcir_i2c_ts_data *data)
 		return;
 	}
 
-	touch = rdbuf[0];
-	if (touch) {
-		u16 posx1 = (rdbuf[3] << 8) | rdbuf[2];
-		u16 posy1 = (rdbuf[5] << 8) | rdbuf[4];
-		u16 posx2 = (rdbuf[7] << 8) | rdbuf[6];
-		u16 posy2 = (rdbuf[9] << 8) | rdbuf[8];
-
-		input_report_key(tsdata->input, BTN_TOUCH, 1);
-		input_report_abs(tsdata->input, ABS_X, posx1);
-		input_report_abs(tsdata->input, ABS_Y, posy1);
-
-		input_report_abs(tsdata->input, ABS_MT_POSITION_X, posx1);
-		input_report_abs(tsdata->input, ABS_MT_POSITION_Y, posy1);
-		input_mt_sync(tsdata->input);
-
-		if (touch == 2) {
-			input_report_abs(tsdata->input,
-					 ABS_MT_POSITION_X, posx2);
-			input_report_abs(tsdata->input,
-					 ABS_MT_POSITION_Y, posy2);
-			input_mt_sync(tsdata->input);
-		}
-	} else {
-		input_report_key(tsdata->input, BTN_TOUCH, 0);
+	touch = rdbuf[0] & 0x7;
+	if (touch > PIXCIR_MAX_SLOTS)
+		touch = PIXCIR_MAX_SLOTS;
+
+	report->num_touches = touch;
+	bufptr = &rdbuf[2];
+
+	for (i = 0; i < touch; i++) {
+		report->touches[i].x = (bufptr[1] << 8) | bufptr[0];
+		report->touches[i].y = (bufptr[3] << 8) | bufptr[2];
+
+		bufptr = bufptr + 4;
 	}
+}
+
+static void pixcir_ts_report(struct pixcir_i2c_ts_data *ts,
+			     struct pixcir_report_data *report)
+{
+	struct input_mt_pos pos[PIXCIR_MAX_SLOTS];
+	int slots[PIXCIR_MAX_SLOTS];
+	struct pixcir_touch *touch;
+	int n, i, slot;
+	struct device *dev = &ts->client->dev;
+
+	n = report->num_touches;
+	if (n > PIXCIR_MAX_SLOTS)
+		n = PIXCIR_MAX_SLOTS;
 
-	input_sync(tsdata->input);
+	for (i = 0; i < n; i++) {
+		touch = &report->touches[i];
+		pos[i].x = touch->x;
+		pos[i].y = touch->y;
+	}
+
+	input_mt_assign_slots(ts->input, slots, pos, n);
+
+	for (i = 0; i < n; i++) {
+		touch = &report->touches[i];
+		slot = slots[i];
+
+		input_mt_slot(ts->input, slot);
+		input_mt_report_slot_state(ts->input,
+					   MT_TOOL_FINGER, true);
+
+		input_event(ts->input, EV_ABS, ABS_MT_POSITION_X, touch->x);
+		input_event(ts->input, EV_ABS, ABS_MT_POSITION_Y, touch->y);
+
+		dev_dbg(dev, "%d: slot %d, x %d, y %d\n",
+			i, slot, touch->x, touch->y);
+	}
+
+	input_mt_sync_frame(ts->input);
+	input_sync(ts->input);
 }
 
 static irqreturn_t pixcir_ts_isr(int irq, void *dev_id)
 {
 	struct pixcir_i2c_ts_data *tsdata = dev_id;
 	const struct pixcir_ts_platform_data *pdata = tsdata->chip;
+	struct pixcir_report_data report;
 
 	while (tsdata->running) {
-		pixcir_ts_poscheck(tsdata);
-
-		if (gpio_get_value(pdata->gpio_attb))
+		/* parse packet */
+		pixcir_ts_parse(tsdata, &report);
+
+		/* report it */
+		pixcir_ts_report(tsdata, &report);
+
+		if (gpio_get_value(pdata->gpio_attb)) {
+			if (report.num_touches) {
+				/*
+				 * Last report with no finger up?
+				 * Do it now then.
+				 */
+				input_mt_sync_frame(tsdata->input);
+				input_sync(tsdata->input);
+			}
 			break;
+		}
 
 		msleep(20);
 	}
@@ -371,6 +427,13 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client,
 	input_set_abs_params(input, ABS_MT_POSITION_X, 0, pdata->x_max, 0, 0);
 	input_set_abs_params(input, ABS_MT_POSITION_Y, 0, pdata->y_max, 0, 0);
 
+	error = input_mt_init_slots(input, PIXCIR_MAX_SLOTS,
+				    INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
+	if (error) {
+		dev_err(dev, "Error initializing Multi-Touch slots\n");
+		return error;
+	}
+
 	input_set_drvdata(input, tsdata);
 
 	error = devm_gpio_request_one(dev, pdata->gpio_attb,
-- 
1.8.3.2


^ permalink raw reply related

* [PATCH v7 2/5] Input: pixcir_i2c_ts: support upto 5 fingers and hardware provided tracking IDs
From: Roger Quadros @ 2014-06-17  9:31 UTC (permalink / raw)
  To: dmitry.torokhov, tony
  Cc: rydberg, balbi, dmurphy, mugunthanvnm, nsekhar, linux-input,
	linux-kernel, linux-omap, Roger Quadros
In-Reply-To: <1402997507-9736-1-git-send-email-rogerq@ti.com>

Some variants of the Pixcir touch controller support upto 5
simultaneous fingers and hardware tracking IDs. Prepare the driver
for that.

Signed-off-by: Roger Quadros <rogerq@ti.com>
---
 drivers/input/touchscreen/pixcir_i2c_ts.c | 74 ++++++++++++++++++++++++-------
 include/linux/input/pixcir_ts.h           | 12 +++++
 2 files changed, 69 insertions(+), 17 deletions(-)

diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c
index 0b01681..083af4f 100644
--- a/drivers/input/touchscreen/pixcir_i2c_ts.c
+++ b/drivers/input/touchscreen/pixcir_i2c_ts.c
@@ -27,18 +27,20 @@
 #include <linux/input/pixcir_ts.h>
 #include <linux/gpio.h>
 
-#define PIXCIR_MAX_SLOTS       2
+#define PIXCIR_MAX_SLOTS       5 /* Max fingers supported by driver */
 
 struct pixcir_i2c_ts_data {
 	struct i2c_client *client;
 	struct input_dev *input;
-	const struct pixcir_ts_platform_data *chip;
+	const struct pixcir_ts_platform_data *pdata;
 	bool running;
+	int max_fingers;	/* Max fingers supported in this instance */
 };
 
 struct pixcir_touch {
 	int x;
 	int y;
+	int id;
 };
 
 struct pixcir_report_data {
@@ -49,13 +51,21 @@ struct pixcir_report_data {
 static void pixcir_ts_parse(struct pixcir_i2c_ts_data *tsdata,
 			    struct pixcir_report_data *report)
 {
-	u8 rdbuf[10], wrbuf[1] = { 0 };
+	u8 rdbuf[2 + PIXCIR_MAX_SLOTS * 5];
+	u8 wrbuf[1] = { 0 };
 	u8 *bufptr;
 	u8 touch;
 	int ret, i;
+	int readsize;
+	const struct pixcir_i2c_chip_data *chip = &tsdata->pdata->chip;
 
 	memset(report, 0, sizeof(struct pixcir_report_data));
 
+	i = chip->has_hw_ids ? 1 : 0;
+	readsize = 2 + tsdata->max_fingers * (4 + i);
+	if (readsize > sizeof(rdbuf))
+		readsize = sizeof(rdbuf);
+
 	ret = i2c_master_send(tsdata->client, wrbuf, sizeof(wrbuf));
 	if (ret != sizeof(wrbuf)) {
 		dev_err(&tsdata->client->dev,
@@ -64,7 +74,7 @@ static void pixcir_ts_parse(struct pixcir_i2c_ts_data *tsdata,
 		return;
 	}
 
-	ret = i2c_master_recv(tsdata->client, rdbuf, sizeof(rdbuf));
+	ret = i2c_master_recv(tsdata->client, rdbuf, readsize);
 	if (ret != sizeof(rdbuf)) {
 		dev_err(&tsdata->client->dev,
 			"%s: i2c_master_recv failed(), ret=%d\n",
@@ -73,8 +83,8 @@ static void pixcir_ts_parse(struct pixcir_i2c_ts_data *tsdata,
 	}
 
 	touch = rdbuf[0] & 0x7;
-	if (touch > PIXCIR_MAX_SLOTS)
-		touch = PIXCIR_MAX_SLOTS;
+	if (touch > tsdata->max_fingers)
+		touch = tsdata->max_fingers;
 
 	report->num_touches = touch;
 	bufptr = &rdbuf[2];
@@ -83,7 +93,12 @@ static void pixcir_ts_parse(struct pixcir_i2c_ts_data *tsdata,
 		report->touches[i].x = (bufptr[1] << 8) | bufptr[0];
 		report->touches[i].y = (bufptr[3] << 8) | bufptr[2];
 
-		bufptr = bufptr + 4;
+		if (chip->has_hw_ids) {
+			report->touches[i].id = bufptr[4];
+			bufptr = bufptr + 5;
+		} else {
+			bufptr = bufptr + 4;
+		}
 	}
 }
 
@@ -95,22 +110,35 @@ static void pixcir_ts_report(struct pixcir_i2c_ts_data *ts,
 	struct pixcir_touch *touch;
 	int n, i, slot;
 	struct device *dev = &ts->client->dev;
+	const struct pixcir_i2c_chip_data *chip = &ts->pdata->chip;
 
 	n = report->num_touches;
 	if (n > PIXCIR_MAX_SLOTS)
 		n = PIXCIR_MAX_SLOTS;
 
-	for (i = 0; i < n; i++) {
-		touch = &report->touches[i];
-		pos[i].x = touch->x;
-		pos[i].y = touch->y;
-	}
+	if (!chip->has_hw_ids) {
+		for (i = 0; i < n; i++) {
+			touch = &report->touches[i];
+			pos[i].x = touch->x;
+			pos[i].y = touch->y;
+		}
 
-	input_mt_assign_slots(ts->input, slots, pos, n);
+		input_mt_assign_slots(ts->input, slots, pos, n);
+	}
 
 	for (i = 0; i < n; i++) {
 		touch = &report->touches[i];
-		slot = slots[i];
+
+		if (chip->has_hw_ids) {
+			slot = input_mt_get_slot_by_key(ts->input, touch->id);
+			if (slot < 0) {
+				dev_dbg(dev, "no free slot for id 0x%x\n",
+					touch->id);
+				continue;
+			}
+		} else {
+			slot = slots[i];
+		}
 
 		input_mt_slot(ts->input, slot);
 		input_mt_report_slot_state(ts->input,
@@ -130,7 +158,7 @@ static void pixcir_ts_report(struct pixcir_i2c_ts_data *ts,
 static irqreturn_t pixcir_ts_isr(int irq, void *dev_id)
 {
 	struct pixcir_i2c_ts_data *tsdata = dev_id;
-	const struct pixcir_ts_platform_data *pdata = tsdata->chip;
+	const struct pixcir_ts_platform_data *pdata = tsdata->pdata;
 	struct pixcir_report_data report;
 
 	while (tsdata->running) {
@@ -399,6 +427,11 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client,
 		return -EINVAL;
 	}
 
+	if (pdata->chip.max_fingers <= 0) {
+		dev_err(dev, "Invalid max_fingers in pdata\n");
+		return -EINVAL;
+	}
+
 	tsdata = devm_kzalloc(dev, sizeof(*tsdata), GFP_KERNEL);
 	if (!tsdata)
 		return -ENOMEM;
@@ -411,7 +444,7 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client,
 
 	tsdata->client = client;
 	tsdata->input = input;
-	tsdata->chip = pdata;
+	tsdata->pdata = pdata;
 
 	input->name = client->name;
 	input->id.bustype = BUS_I2C;
@@ -427,7 +460,14 @@ static int pixcir_i2c_ts_probe(struct i2c_client *client,
 	input_set_abs_params(input, ABS_MT_POSITION_X, 0, pdata->x_max, 0, 0);
 	input_set_abs_params(input, ABS_MT_POSITION_Y, 0, pdata->y_max, 0, 0);
 
-	error = input_mt_init_slots(input, PIXCIR_MAX_SLOTS,
+	tsdata->max_fingers = tsdata->pdata->chip.max_fingers;
+	if (tsdata->max_fingers > PIXCIR_MAX_SLOTS) {
+		tsdata->max_fingers = PIXCIR_MAX_SLOTS;
+		dev_info(dev, "Limiting maximum fingers to %d\n",
+			 tsdata->max_fingers);
+	}
+
+	error = input_mt_init_slots(input, tsdata->max_fingers,
 				    INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
 	if (error) {
 		dev_err(dev, "Error initializing Multi-Touch slots\n");
diff --git a/include/linux/input/pixcir_ts.h b/include/linux/input/pixcir_ts.h
index 160cf35..7bae83b 100644
--- a/include/linux/input/pixcir_ts.h
+++ b/include/linux/input/pixcir_ts.h
@@ -43,10 +43,22 @@ enum pixcir_int_mode {
 #define PIXCIR_INT_ENABLE	(1UL << 3)
 #define PIXCIR_INT_POL_HIGH	(1UL << 2)
 
+/**
+ * struct pixcir_irc_chip_data - chip related data
+ * @max_fingers:	Max number of fingers reported simultaneously by h/w
+ * @has_hw_ids:		Hardware supports finger tracking IDs
+ *
+ */
+struct pixcir_i2c_chip_data {
+	u8 max_fingers;
+	bool has_hw_ids;
+};
+
 struct pixcir_ts_platform_data {
 	int x_max;
 	int y_max;
 	int gpio_attb;		/* GPIO connected to ATTB line */
+	struct pixcir_i2c_chip_data chip;
 };
 
 #endif
-- 
1.8.3.2

^ permalink raw reply related

* [PATCH v7 3/5] Input: pixcir_i2c_ts: Add device tree support
From: Roger Quadros @ 2014-06-17  9:31 UTC (permalink / raw)
  To: dmitry.torokhov, tony
  Cc: rydberg, balbi, dmurphy, mugunthanvnm, nsekhar, linux-input,
	linux-kernel, linux-omap, Roger Quadros
In-Reply-To: <1402997507-9736-1-git-send-email-rogerq@ti.com>

Provide device tree support and binding information.
Also provide support for a new chip "pixcir_tangoc".

Signed-off-by: Roger Quadros <rogerq@ti.com>
---
 .../bindings/input/touchscreen/pixcir_i2c_ts.txt   | 26 ++++++++
 .../devicetree/bindings/vendor-prefixes.txt        |  1 +
 drivers/input/touchscreen/pixcir_i2c_ts.c          | 78 ++++++++++++++++++++++
 3 files changed, 105 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/touchscreen/pixcir_i2c_ts.txt

diff --git a/Documentation/devicetree/bindings/input/touchscreen/pixcir_i2c_ts.txt b/Documentation/devicetree/bindings/input/touchscreen/pixcir_i2c_ts.txt
new file mode 100644
index 0000000..6e55109
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/touchscreen/pixcir_i2c_ts.txt
@@ -0,0 +1,26 @@
+* Pixcir I2C touchscreen controllers
+
+Required properties:
+- compatible: must be "pixcir,pixcir_ts" or "pixcir,pixcir_tangoc"
+- reg: I2C address of the chip
+- interrupts: interrupt to which the chip is connected
+- attb-gpio: GPIO connected to the ATTB line of the chip
+- touchscreen-size-x: horizontal resolution of touchscreen (in pixels)
+- touchscreen-size-y: vertical resolution of touchscreen (in pixels)
+
+Example:
+
+	i2c@00000000 {
+		/* ... */
+
+		pixcir_ts@5c {
+			compatible = "pixcir,pixcir_ts";
+			reg = <0x5c>;
+			interrupts = <2 0>;
+			attb-gpio = <&gpf 2 0 2>;
+			touchscreen-size-x = <800>;
+			touchscreen-size-y = <600>;
+		};
+
+		/* ... */
+	};
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 4d7f375..364f18f 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -98,6 +98,7 @@ panasonic	Panasonic Corporation
 phytec	PHYTEC Messtechnik GmbH
 picochip	Picochip Ltd
 plathome	Plat'Home Co., Ltd.
+pixcir  PIXCIR MICROELECTRONICS Co., Ltd
 powervr	PowerVR (deprecated, use img)
 qca	Qualcomm Atheros, Inc.
 qcom	Qualcomm Technologies, Inc
diff --git a/drivers/input/touchscreen/pixcir_i2c_ts.c b/drivers/input/touchscreen/pixcir_i2c_ts.c
index 083af4f..ed2fe96 100644
--- a/drivers/input/touchscreen/pixcir_i2c_ts.c
+++ b/drivers/input/touchscreen/pixcir_i2c_ts.c
@@ -26,6 +26,9 @@
 #include <linux/input/mt.h>
 #include <linux/input/pixcir_ts.h>
 #include <linux/gpio.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_device.h>
 
 #define PIXCIR_MAX_SLOTS       5 /* Max fingers supported by driver */
 
@@ -407,16 +410,70 @@ unlock:
 static SIMPLE_DEV_PM_OPS(pixcir_dev_pm_ops,
 			 pixcir_i2c_ts_suspend, pixcir_i2c_ts_resume);
 
+#ifdef CONFIG_OF
+static const struct of_device_id pixcir_of_match[];
+
+static struct pixcir_ts_platform_data *pixcir_parse_dt(struct device *dev)
+{
+	struct pixcir_ts_platform_data *pdata;
+	struct device_node *np = dev->of_node;
+
+	const struct of_device_id *match;
+
+	match = of_match_device(of_match_ptr(pixcir_of_match), dev);
+	if (!match)
+		return ERR_PTR(-EINVAL);
+
+	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+	if (!pdata)
+		return ERR_PTR(-ENOMEM);
+
+	pdata->chip = *(const struct pixcir_i2c_chip_data *)match->data;
+
+	pdata->gpio_attb = of_get_named_gpio(np, "attb-gpio", 0);
+	/* gpio_attb validity is checked in probe */
+
+	if (of_property_read_u32(np, "touchscreen-size-x", &pdata->x_max)) {
+		dev_err(dev, "Failed to get touchscreen-size-x property\n");
+		return ERR_PTR(-EINVAL);
+	}
+	pdata->x_max -= 1;
+
+	if (of_property_read_u32(np, "touchscreen-size-y", &pdata->y_max)) {
+		dev_err(dev, "Failed to get touchscreen-size-y property\n");
+		return ERR_PTR(-EINVAL);
+	}
+	pdata->y_max -= 1;
+
+	dev_dbg(dev, "%s: x %d, y %d, gpio %d\n", __func__,
+		pdata->x_max + 1, pdata->y_max + 1, pdata->gpio_attb);
+
+	return pdata;
+}
+#else
+static struct pixcir_ts_platform_data *pixcir_parse_dt(struct device *dev)
+{
+	return ERR_PTR(-EINVAL);
+}
+#endif
+
 static int pixcir_i2c_ts_probe(struct i2c_client *client,
 					 const struct i2c_device_id *id)
 {
 	const struct pixcir_ts_platform_data *pdata =
 			dev_get_platdata(&client->dev);
 	struct device *dev = &client->dev;
+	struct device_node *np = dev->of_node;
 	struct pixcir_i2c_ts_data *tsdata;
 	struct input_dev *input;
 	int error;
 
+	if (np && !pdata) {
+		pdata = pixcir_parse_dt(dev);
+		if (IS_ERR(pdata))
+			return PTR_ERR(pdata);
+	}
+
 	if (!pdata) {
 		dev_err(&client->dev, "platform data not defined\n");
 		return -EINVAL;
@@ -522,15 +579,36 @@ static int pixcir_i2c_ts_remove(struct i2c_client *client)
 
 static const struct i2c_device_id pixcir_i2c_ts_id[] = {
 	{ "pixcir_ts", 0 },
+	{ "pixcir_tangoc", 0 },
 	{ }
 };
 MODULE_DEVICE_TABLE(i2c, pixcir_i2c_ts_id);
 
+#ifdef CONFIG_OF
+static const struct pixcir_i2c_chip_data pixcir_ts_data = {
+	.max_fingers = 2,
+	/* no hw id support */
+};
+
+static const struct pixcir_i2c_chip_data pixcir_tangoc_data = {
+	.max_fingers = 5,
+	.has_hw_ids = true,
+};
+
+static const struct of_device_id pixcir_of_match[] = {
+	{ .compatible = "pixcir,pixcir_ts", .data = &pixcir_ts_data },
+	{ .compatible = "pixcir,pixcir_tangoc", .data = &pixcir_tangoc_data },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, pixcir_of_match);
+#endif
+
 static struct i2c_driver pixcir_i2c_ts_driver = {
 	.driver = {
 		.owner	= THIS_MODULE,
 		.name	= "pixcir_ts",
 		.pm	= &pixcir_dev_pm_ops,
+		.of_match_table = of_match_ptr(pixcir_of_match),
 	},
 	.probe		= pixcir_i2c_ts_probe,
 	.remove		= pixcir_i2c_ts_remove,
-- 
1.8.3.2


^ permalink raw reply related

* [PATCH v7 4/5] ARM: dts: am43x-epos-evm: Update binding for touchscreen size
From: Roger Quadros @ 2014-06-17  9:31 UTC (permalink / raw)
  To: dmitry.torokhov, tony
  Cc: rydberg, balbi, dmurphy, mugunthanvnm, nsekhar, linux-input,
	linux-kernel, linux-omap, Roger Quadros, Benoit Cousson
In-Reply-To: <1402997507-9736-1-git-send-email-rogerq@ti.com>

Update the bindings for touchscreen size.

CC: Benoit Cousson <bcousson@baylibre.com>
CC: Tony Lindgren <tony@atomide.com>
CC: Mugunthan V N <mugunthanvnm@ti.com>
Signed-off-by: Roger Quadros <rogerq@ti.com>
---
 arch/arm/boot/dts/am43x-epos-evm.dts | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/arm/boot/dts/am43x-epos-evm.dts b/arch/arm/boot/dts/am43x-epos-evm.dts
index 19f1f7e..f3fe017 100644
--- a/arch/arm/boot/dts/am43x-epos-evm.dts
+++ b/arch/arm/boot/dts/am43x-epos-evm.dts
@@ -340,8 +340,8 @@
 
 		attb-gpio = <&gpio1 17 GPIO_ACTIVE_HIGH>;
 
-		x-size = <1024>;
-		y-size = <600>;
+		touchscreen-size-x = <1024>;
+		touchscreen-size-y = <600>;
 	};
 };
 
-- 
1.8.3.2

^ permalink raw reply related

* [PATCH v7 5/5] ARM: dts: am437x-gp-evm: Update binding for touchscreen size
From: Roger Quadros @ 2014-06-17  9:31 UTC (permalink / raw)
  To: dmitry.torokhov, tony
  Cc: rydberg, balbi, dmurphy, mugunthanvnm, nsekhar, linux-input,
	linux-kernel, linux-omap, Roger Quadros, Benoit Cousson
In-Reply-To: <1402997507-9736-1-git-send-email-rogerq@ti.com>

Update the bindings for touchscreen size.

CC: Benoit Cousson <bcousson@baylibre.com>
CC: Tony Lindgren <tony@atomide.com>
CC: Mugunthan V N <mugunthanvnm@ti.com>
Signed-off-by: Roger Quadros <rogerq@ti.com>
---
 arch/arm/boot/dts/am437x-gp-evm.dts | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/arch/arm/boot/dts/am437x-gp-evm.dts b/arch/arm/boot/dts/am437x-gp-evm.dts
index 003766c..2674132 100644
--- a/arch/arm/boot/dts/am437x-gp-evm.dts
+++ b/arch/arm/boot/dts/am437x-gp-evm.dts
@@ -277,8 +277,8 @@
 
 		attb-gpio = <&gpio3 22 GPIO_ACTIVE_HIGH>;
 
-		x-size = <1024>;
-		y-size = <600>;
+		touchscreen-size-x = <1024>;
+		touchscreen-size-y = <600>;
 	};
 };
 
-- 
1.8.3.2

^ permalink raw reply related

* [PATCH v7 0/5] Input: pixcir_i2c_ts: Add Type-B Multi-touch and DT support
From: Roger Quadros @ 2014-06-17  9:31 UTC (permalink / raw)
  To: dmitry.torokhov, tony
  Cc: rydberg, balbi, dmurphy, mugunthanvnm, nsekhar, linux-input,
	linux-kernel, linux-omap, Roger Quadros

Hi Dmitry,

These are the pending patches that didn't go through in the 3.16 merge window.
Please queue them for -next. Thanks.

The series does the following
- convert to Type-B multi touch protocol
- support upto 5 fingers with hardware supplied tracking IDs
- device tree support

Tony,

The last 2 patches fix the touch screen size property in the device tree since
we now use the unified touch screen properties.

cheers,
-roger

Changelog:

v7:
- Rebased on 3.16-rc1.

v6:
- Use unified touchscreen bindings.
- Update pointer math i.e. bufptr += 4 instead of &bufptr[4];

v5:
- Changed ts->exiting flag to ts->running in patch 2.
- Don't call pixcir_stop() from .suspend() if wakeup is required.

v4:
- Imporved pixcir_stop() as per Dmitry's suggestion.
- Removed unnecessary input_unregister_device() from .remove().

v3:
- Rebased to 3.15-rc3
- Fixed suspend while touchscreen in use
- Fixed module removal while touchscreen in use

v2:
- Addressed review comments and re-arranged patch order

v1:
- http://article.gmane.org/gmane.linux.kernel/1616417

--

Roger Quadros (5):
  Input: pixcir_i2c_ts: Use Type-B Multi-Touch protocol
  Input: pixcir_i2c_ts: support upto 5 fingers and hardware provided
    tracking IDs
  Input: pixcir_i2c_ts: Add device tree support
  ARM: dts: am43x-epos-evm: Update binding for touchscreen size
  ARM: dts: am437x-gp-evm: Update binding for touchscreen size

 .../bindings/input/touchscreen/pixcir_i2c_ts.txt   |  26 +++
 .../devicetree/bindings/vendor-prefixes.txt        |   1 +
 arch/arm/boot/dts/am437x-gp-evm.dts                |   4 +-
 arch/arm/boot/dts/am43x-epos-evm.dts               |   4 +-
 drivers/input/touchscreen/pixcir_i2c_ts.c          | 251 ++++++++++++++++++---
 include/linux/input/pixcir_ts.h                    |  12 +
 6 files changed, 259 insertions(+), 39 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/input/touchscreen/pixcir_i2c_ts.txt

-- 
1.8.3.2


^ permalink raw reply

* Re: [PATCH v7 4/5] ARM: dts: am43x-epos-evm: Update binding for touchscreen size
From: Tony Lindgren @ 2014-06-17  9:50 UTC (permalink / raw)
  To: Roger Quadros
  Cc: dmitry.torokhov, rydberg, balbi, dmurphy, mugunthanvnm, nsekhar,
	linux-input, linux-kernel, linux-omap, Benoit Cousson
In-Reply-To: <1402997507-9736-5-git-send-email-rogerq@ti.com>

* Roger Quadros <rogerq@ti.com> [140617 02:33]:
> Update the bindings for touchscreen size.
> 
> CC: Benoit Cousson <bcousson@baylibre.com>
> CC: Tony Lindgren <tony@atomide.com>
> CC: Mugunthan V N <mugunthanvnm@ti.com>
> Signed-off-by: Roger Quadros <rogerq@ti.com>

This should be fine to queue along with the driver changes:

Acked-by: Tony Lindgren <tony@atomide.com>

> ---
>  arch/arm/boot/dts/am43x-epos-evm.dts | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/arch/arm/boot/dts/am43x-epos-evm.dts b/arch/arm/boot/dts/am43x-epos-evm.dts
> index 19f1f7e..f3fe017 100644
> --- a/arch/arm/boot/dts/am43x-epos-evm.dts
> +++ b/arch/arm/boot/dts/am43x-epos-evm.dts
> @@ -340,8 +340,8 @@
>  
>  		attb-gpio = <&gpio1 17 GPIO_ACTIVE_HIGH>;
>  
> -		x-size = <1024>;
> -		y-size = <600>;
> +		touchscreen-size-x = <1024>;
> +		touchscreen-size-y = <600>;
>  	};
>  };
>  
> -- 
> 1.8.3.2
> 

^ permalink raw reply

* Re: [PATCH v7 5/5] ARM: dts: am437x-gp-evm: Update binding for touchscreen size
From: Tony Lindgren @ 2014-06-17  9:50 UTC (permalink / raw)
  To: Roger Quadros
  Cc: dmitry.torokhov, rydberg, balbi, dmurphy, mugunthanvnm, nsekhar,
	linux-input, linux-kernel, linux-omap, Benoit Cousson
In-Reply-To: <1402997507-9736-6-git-send-email-rogerq@ti.com>

* Roger Quadros <rogerq@ti.com> [140617 02:33]:
> Update the bindings for touchscreen size.
> 
> CC: Benoit Cousson <bcousson@baylibre.com>
> CC: Tony Lindgren <tony@atomide.com>
> CC: Mugunthan V N <mugunthanvnm@ti.com>
> Signed-off-by: Roger Quadros <rogerq@ti.com>

This too:

Acked-by: Tony Lindgren <tony@atomide.com>

> ---
>  arch/arm/boot/dts/am437x-gp-evm.dts | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
> 
> diff --git a/arch/arm/boot/dts/am437x-gp-evm.dts b/arch/arm/boot/dts/am437x-gp-evm.dts
> index 003766c..2674132 100644
> --- a/arch/arm/boot/dts/am437x-gp-evm.dts
> +++ b/arch/arm/boot/dts/am437x-gp-evm.dts
> @@ -277,8 +277,8 @@
>  
>  		attb-gpio = <&gpio3 22 GPIO_ACTIVE_HIGH>;
>  
> -		x-size = <1024>;
> -		y-size = <600>;
> +		touchscreen-size-x = <1024>;
> +		touchscreen-size-y = <600>;
>  	};
>  };
>  
> -- 
> 1.8.3.2
> 

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox