Josh Boyer 2ed6732
Bugzilla: 1048314
Josh Boyer 2ed6732
Upstream-status: Queued for 3.16
Josh Boyer 2ed6732
Josh Boyer 2ed6732
From 39141443c8ea2900af627d688a255e064e2b6e19 Mon Sep 17 00:00:00 2001
Josh Boyer 2ed6732
From: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Josh Boyer 2ed6732
Date: Wed, 9 Apr 2014 11:09:21 -0400
Josh Boyer 2ed6732
Subject: [PATCH] HID: rmi: introduce RMI driver for Synaptics touchpads
Josh Boyer 2ed6732
Josh Boyer 2ed6732
This driver add support for RMI4 over USB or I2C.
Josh Boyer 2ed6732
The current state is that it uses its own RMI4 implementation, but once
Josh Boyer 2ed6732
RMI4 is merged upstream, the driver will be a transport driver for the
Josh Boyer 2ed6732
RMI4 library.
Josh Boyer 2ed6732
Josh Boyer 2ed6732
Part of this driver should be considered as temporary. Most of the RMI4
Josh Boyer 2ed6732
processing and input handling will be deleted at some point.
Josh Boyer 2ed6732
Josh Boyer 2ed6732
I based my work on Andrew's regarding its port of RMI4 over HID (see
Josh Boyer 2ed6732
https://github.com/mightybigcar/synaptics-rmi4/tree/rmihid )
Josh Boyer 2ed6732
This repo presents how the driver may looks like at the end:
Josh Boyer 2ed6732
https://github.com/mightybigcar/synaptics-rmi4/blob/rmihid/drivers/input/rmi4/rmi_hid.c
Josh Boyer 2ed6732
Josh Boyer 2ed6732
Without this temporary solution, the workaround we gave to users
Josh Boyer 2ed6732
is to disable i2c-hid, which leads to disabling the touchscreen on the
Josh Boyer 2ed6732
XPS 11 and 12 (Haswell generation).
Josh Boyer 2ed6732
Josh Boyer 2ed6732
Related bugs:
Josh Boyer 2ed6732
https://bugzilla.redhat.com/show_bug.cgi?id=1048314
Josh Boyer 2ed6732
https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1218973
Josh Boyer 2ed6732
Josh Boyer 2ed6732
Signed-off-by: Andrew Duggan <aduggan@synaptics.com>
Josh Boyer 2ed6732
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Josh Boyer 2ed6732
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Josh Boyer 2ed6732
Josh Boyer 2ed6732
- Removed obviously wrong hid_hw_stop() at the end of probe
Josh Boyer 2ed6732
Josh Boyer 2ed6732
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Josh Boyer 2ed6732
---
Josh Boyer 2ed6732
 drivers/hid/Kconfig    |   8 +
Josh Boyer 2ed6732
 drivers/hid/Makefile   |   1 +
Josh Boyer 2ed6732
 drivers/hid/hid-core.c |   2 +
Josh Boyer 2ed6732
 drivers/hid/hid-rmi.c  | 890 +++++++++++++++++++++++++++++++++++++++++++++++++
Josh Boyer 2ed6732
 include/linux/hid.h    |   2 +
Josh Boyer 2ed6732
 6 files changed, 946 insertions(+)
Josh Boyer 2ed6732
 create mode 100644 drivers/hid/hid-rmi.c
Josh Boyer 2ed6732
Josh Boyer 2ed6732
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
Josh Boyer 2ed6732
index 34e2d39..b2d733b 100644
Josh Boyer 2ed6732
--- a/drivers/hid/Kconfig
Josh Boyer 2ed6732
+++ b/drivers/hid/Kconfig
Josh Boyer 2ed6732
@@ -645,6 +645,14 @@ config HID_SUNPLUS
Josh Boyer 2ed6732
 	---help---
Josh Boyer 2ed6732
 	Support for Sunplus wireless desktop.
Josh Boyer 2ed6732
 
Josh Boyer 2ed6732
+config HID_RMI
Josh Boyer 2ed6732
+	tristate "Synaptics RMI4 device support"
Josh Boyer 2ed6732
+	depends on HID
Josh Boyer 2ed6732
+	---help---
Josh Boyer 2ed6732
+	Support for Synaptics RMI4 touchpads.
Josh Boyer 2ed6732
+	Say Y here if you have a Synaptics RMI4 touchpads over i2c-hid or usbhid
Josh Boyer 2ed6732
+	and want support for its special functionalities.
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
 config HID_GREENASIA
Josh Boyer 2ed6732
 	tristate "GreenAsia (Product ID 0x12) game controller support"
Josh Boyer 2ed6732
 	depends on HID
Josh Boyer 2ed6732
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
Josh Boyer 2ed6732
index 30e4431..b326f79 100644
Josh Boyer 2ed6732
--- a/drivers/hid/Makefile
Josh Boyer 2ed6732
+++ b/drivers/hid/Makefile
Josh Boyer 2ed6732
@@ -96,6 +96,7 @@ obj-$(CONFIG_HID_ROCCAT)	+= hid-roccat.o hid-roccat-common.o \
Josh Boyer 2ed6732
 	hid-roccat-arvo.o hid-roccat-isku.o hid-roccat-kone.o \
Josh Boyer 2ed6732
 	hid-roccat-koneplus.o hid-roccat-konepure.o hid-roccat-kovaplus.o \
Josh Boyer 2ed6732
 	hid-roccat-lua.o hid-roccat-pyra.o hid-roccat-ryos.o hid-roccat-savu.o
Josh Boyer 2ed6732
+obj-$(CONFIG_HID_RMI)		+= hid-rmi.o
Josh Boyer 2ed6732
 obj-$(CONFIG_HID_SAITEK)	+= hid-saitek.o
Josh Boyer 2ed6732
 obj-$(CONFIG_HID_SAMSUNG)	+= hid-samsung.o
Josh Boyer 2ed6732
 obj-$(CONFIG_HID_SMARTJOYPLUS)	+= hid-sjoy.o
Josh Boyer 2ed6732
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
Josh Boyer 2ed6732
index 253fe23..543dd1f 100644
Josh Boyer 2ed6732
--- a/drivers/hid/hid-core.c
Josh Boyer 2ed6732
+++ b/drivers/hid/hid-core.c
Josh Boyer 2ed6732
@@ -1836,6 +1836,8 @@ static const struct hid_device_id hid_have_special_driver[] = {
Josh Boyer 2ed6732
 	{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGP_MOUSE) },
Josh Boyer 2ed6732
 	{ HID_USB_DEVICE(USB_VENDOR_ID_STEELSERIES, USB_DEVICE_ID_STEELSERIES_SRWS1) },
Josh Boyer 2ed6732
 	{ HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) },
Josh Boyer 2ed6732
+	{ HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, HID_ANY_ID) },
Josh Boyer 2ed6732
+	{ HID_I2C_DEVICE(USB_VENDOR_ID_SYNAPTICS, HID_ANY_ID) },
Josh Boyer 2ed6732
 	{ HID_USB_DEVICE(USB_VENDOR_ID_THINGM, USB_DEVICE_ID_BLINK1) },
Josh Boyer 2ed6732
 	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300) },
Josh Boyer 2ed6732
 	{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304) },
Josh Boyer 2ed6732
diff --git a/drivers/hid/hid-rmi.c b/drivers/hid/hid-rmi.c
Josh Boyer 2ed6732
new file mode 100644
Josh Boyer 2ed6732
index 0000000..a4f04d4
Josh Boyer 2ed6732
--- /dev/null
Josh Boyer 2ed6732
+++ b/drivers/hid/hid-rmi.c
Josh Boyer 2ed6732
@@ -0,0 +1,890 @@
Josh Boyer 2ed6732
+/*
Josh Boyer 2ed6732
+ *  Copyright (c) 2013 Andrew Duggan <aduggan@synaptics.com>
Josh Boyer 2ed6732
+ *  Copyright (c) 2013 Synaptics Incorporated
Josh Boyer 2ed6732
+ *  Copyright (c) 2014 Benjamin Tissoires <benjamin.tissoires@gmail.com>
Josh Boyer 2ed6732
+ *  Copyright (c) 2014 Red Hat, Inc
Josh Boyer 2ed6732
+ *
Josh Boyer 2ed6732
+ * This program is free software; you can redistribute it and/or modify it
Josh Boyer 2ed6732
+ * under the terms of the GNU General Public License as published by the Free
Josh Boyer 2ed6732
+ * Software Foundation; either version 2 of the License, or (at your option)
Josh Boyer 2ed6732
+ * any later version.
Josh Boyer 2ed6732
+ */
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+#include <linux/kernel.h>
Josh Boyer 2ed6732
+#include <linux/hid.h>
Josh Boyer 2ed6732
+#include <linux/input.h>
Josh Boyer 2ed6732
+#include <linux/input/mt.h>
Josh Boyer 2ed6732
+#include <linux/module.h>
Josh Boyer 2ed6732
+#include <linux/pm.h>
Josh Boyer 2ed6732
+#include <linux/slab.h>
Josh Boyer 2ed6732
+#include <linux/wait.h>
Josh Boyer 2ed6732
+#include <linux/sched.h>
Josh Boyer 2ed6732
+#include "hid-ids.h"
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+/* removed backported compat.h include */
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+#define RMI_MOUSE_REPORT_ID		0x01 /* Mouse emulation Report */
Josh Boyer 2ed6732
+#define RMI_WRITE_REPORT_ID		0x09 /* Output Report */
Josh Boyer 2ed6732
+#define RMI_READ_ADDR_REPORT_ID		0x0a /* Output Report */
Josh Boyer 2ed6732
+#define RMI_READ_DATA_REPORT_ID		0x0b /* Input Report */
Josh Boyer 2ed6732
+#define RMI_ATTN_REPORT_ID		0x0c /* Input Report */
Josh Boyer 2ed6732
+#define RMI_SET_RMI_MODE_REPORT_ID	0x0f /* Feature Report */
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+/* flags */
Josh Boyer 2ed6732
+#define RMI_READ_REQUEST_PENDING	BIT(0)
Josh Boyer 2ed6732
+#define RMI_READ_DATA_PENDING		BIT(1)
Josh Boyer 2ed6732
+#define RMI_STARTED			BIT(2)
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+enum rmi_mode_type {
Josh Boyer 2ed6732
+	RMI_MODE_OFF			= 0,
Josh Boyer 2ed6732
+	RMI_MODE_ATTN_REPORTS		= 1,
Josh Boyer 2ed6732
+	RMI_MODE_NO_PACKED_ATTN_REPORTS	= 2,
Josh Boyer 2ed6732
+};
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+struct rmi_function {
Josh Boyer 2ed6732
+	unsigned page;			/* page of the function */
Josh Boyer 2ed6732
+	u16 query_base_addr;		/* base address for queries */
Josh Boyer 2ed6732
+	u16 command_base_addr;		/* base address for commands */
Josh Boyer 2ed6732
+	u16 control_base_addr;		/* base address for controls */
Josh Boyer 2ed6732
+	u16 data_base_addr;		/* base address for datas */
Josh Boyer 2ed6732
+	unsigned int interrupt_base;	/* cross-function interrupt number
Josh Boyer 2ed6732
+					 * (uniq in the device)*/
Josh Boyer 2ed6732
+	unsigned int interrupt_count;	/* number of interrupts */
Josh Boyer 2ed6732
+	unsigned int report_size;	/* size of a report */
Josh Boyer 2ed6732
+	unsigned long irq_mask;		/* mask of the interrupts
Josh Boyer 2ed6732
+					 * (to be applied against ATTN IRQ) */
Josh Boyer 2ed6732
+};
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+/**
Josh Boyer 2ed6732
+ * struct rmi_data - stores information for hid communication
Josh Boyer 2ed6732
+ *
Josh Boyer 2ed6732
+ * @page_mutex: Locks current page to avoid changing pages in unexpected ways.
Josh Boyer 2ed6732
+ * @page: Keeps track of the current virtual page
Josh Boyer 2ed6732
+ *
Josh Boyer 2ed6732
+ * @wait: Used for waiting for read data
Josh Boyer 2ed6732
+ *
Josh Boyer 2ed6732
+ * @writeReport: output buffer when writing RMI registers
Josh Boyer 2ed6732
+ * @readReport: input buffer when reading RMI registers
Josh Boyer 2ed6732
+ *
Josh Boyer 2ed6732
+ * @input_report_size: size of an input report (advertised by HID)
Josh Boyer 2ed6732
+ * @output_report_size: size of an output report (advertised by HID)
Josh Boyer 2ed6732
+ *
Josh Boyer 2ed6732
+ * @flags: flags for the current device (started, reading, etc...)
Josh Boyer 2ed6732
+ *
Josh Boyer 2ed6732
+ * @f11: placeholder of internal RMI function F11 description
Josh Boyer 2ed6732
+ * @f30: placeholder of internal RMI function F30 description
Josh Boyer 2ed6732
+ *
Josh Boyer 2ed6732
+ * @max_fingers: maximum finger count reported by the device
Josh Boyer 2ed6732
+ * @max_x: maximum x value reported by the device
Josh Boyer 2ed6732
+ * @max_y: maximum y value reported by the device
Josh Boyer 2ed6732
+ *
Josh Boyer 2ed6732
+ * @gpio_led_count: count of GPIOs + LEDs reported by F30
Josh Boyer 2ed6732
+ * @button_count: actual physical buttons count
Josh Boyer 2ed6732
+ * @button_mask: button mask used to decode GPIO ATTN reports
Josh Boyer 2ed6732
+ * @button_state_mask: pull state of the buttons
Josh Boyer 2ed6732
+ *
Josh Boyer 2ed6732
+ * @input: pointer to the kernel input device
Josh Boyer 2ed6732
+ *
Josh Boyer 2ed6732
+ * @reset_work: worker which will be called in case of a mouse report
Josh Boyer 2ed6732
+ * @hdev: pointer to the struct hid_device
Josh Boyer 2ed6732
+ */
Josh Boyer 2ed6732
+struct rmi_data {
Josh Boyer 2ed6732
+	struct mutex page_mutex;
Josh Boyer 2ed6732
+	int page;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	wait_queue_head_t wait;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	u8 *writeReport;
Josh Boyer 2ed6732
+	u8 *readReport;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	int input_report_size;
Josh Boyer 2ed6732
+	int output_report_size;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	unsigned long flags;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	struct rmi_function f11;
Josh Boyer 2ed6732
+	struct rmi_function f30;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	unsigned int max_fingers;
Josh Boyer 2ed6732
+	unsigned int max_x;
Josh Boyer 2ed6732
+	unsigned int max_y;
Josh Boyer 2ed6732
+	unsigned int x_size_mm;
Josh Boyer 2ed6732
+	unsigned int y_size_mm;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	unsigned int gpio_led_count;
Josh Boyer 2ed6732
+	unsigned int button_count;
Josh Boyer 2ed6732
+	unsigned long button_mask;
Josh Boyer 2ed6732
+	unsigned long button_state_mask;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	struct input_dev *input;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	struct work_struct reset_work;
Josh Boyer 2ed6732
+	struct hid_device *hdev;
Josh Boyer 2ed6732
+};
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+#define RMI_PAGE(addr) (((addr) >> 8) & 0xff)
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+static int rmi_write_report(struct hid_device *hdev, u8 *report, int len);
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+/**
Josh Boyer 2ed6732
+ * rmi_set_page - Set RMI page
Josh Boyer 2ed6732
+ * @hdev: The pointer to the hid_device struct
Josh Boyer 2ed6732
+ * @page: The new page address.
Josh Boyer 2ed6732
+ *
Josh Boyer 2ed6732
+ * RMI devices have 16-bit addressing, but some of the physical
Josh Boyer 2ed6732
+ * implementations (like SMBus) only have 8-bit addressing. So RMI implements
Josh Boyer 2ed6732
+ * a page address at 0xff of every page so we can reliable page addresses
Josh Boyer 2ed6732
+ * every 256 registers.
Josh Boyer 2ed6732
+ *
Josh Boyer 2ed6732
+ * The page_mutex lock must be held when this function is entered.
Josh Boyer 2ed6732
+ *
Josh Boyer 2ed6732
+ * Returns zero on success, non-zero on failure.
Josh Boyer 2ed6732
+ */
Josh Boyer 2ed6732
+static int rmi_set_page(struct hid_device *hdev, u8 page)
Josh Boyer 2ed6732
+{
Josh Boyer 2ed6732
+	struct rmi_data *data = hid_get_drvdata(hdev);
Josh Boyer 2ed6732
+	int retval;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	data->writeReport[0] = RMI_WRITE_REPORT_ID;
Josh Boyer 2ed6732
+	data->writeReport[1] = 1;
Josh Boyer 2ed6732
+	data->writeReport[2] = 0xFF;
Josh Boyer 2ed6732
+	data->writeReport[4] = page;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	retval = rmi_write_report(hdev, data->writeReport,
Josh Boyer 2ed6732
+			data->output_report_size);
Josh Boyer 2ed6732
+	if (retval != data->output_report_size) {
Josh Boyer 2ed6732
+		dev_err(&hdev->dev,
Josh Boyer 2ed6732
+			"%s: set page failed: %d.", __func__, retval);
Josh Boyer 2ed6732
+		return retval;
Josh Boyer 2ed6732
+	}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	data->page = page;
Josh Boyer 2ed6732
+	return 0;
Josh Boyer 2ed6732
+}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+static int rmi_set_mode(struct hid_device *hdev, u8 mode)
Josh Boyer 2ed6732
+{
Josh Boyer 2ed6732
+	int ret;
Josh Boyer 2ed6732
+	u8 txbuf[2] = {RMI_SET_RMI_MODE_REPORT_ID, mode};
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	ret = hid_hw_raw_request(hdev, RMI_SET_RMI_MODE_REPORT_ID, txbuf,
Josh Boyer 2ed6732
+			sizeof(txbuf), HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
Josh Boyer 2ed6732
+	if (ret < 0) {
Josh Boyer 2ed6732
+		dev_err(&hdev->dev, "unable to set rmi mode to %d (%d)\n", mode,
Josh Boyer 2ed6732
+			ret);
Josh Boyer 2ed6732
+		return ret;
Josh Boyer 2ed6732
+	}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	return 0;
Josh Boyer 2ed6732
+}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+static int rmi_write_report(struct hid_device *hdev, u8 *report, int len)
Josh Boyer 2ed6732
+{
Josh Boyer 2ed6732
+	int ret;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	ret = hid_hw_output_report(hdev, (void *)report, len);
Josh Boyer 2ed6732
+	if (ret < 0) {
Josh Boyer 2ed6732
+		dev_err(&hdev->dev, "failed to write hid report (%d)\n", ret);
Josh Boyer 2ed6732
+		return ret;
Josh Boyer 2ed6732
+	}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	return ret;
Josh Boyer 2ed6732
+}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+static int rmi_read_block(struct hid_device *hdev, u16 addr, void *buf,
Josh Boyer 2ed6732
+		const int len)
Josh Boyer 2ed6732
+{
Josh Boyer 2ed6732
+	struct rmi_data *data = hid_get_drvdata(hdev);
Josh Boyer 2ed6732
+	int ret;
Josh Boyer 2ed6732
+	int bytes_read;
Josh Boyer 2ed6732
+	int bytes_needed;
Josh Boyer 2ed6732
+	int retries;
Josh Boyer 2ed6732
+	int read_input_count;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	mutex_lock(&data->page_mutex);
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	if (RMI_PAGE(addr) != data->page) {
Josh Boyer 2ed6732
+		ret = rmi_set_page(hdev, RMI_PAGE(addr));
Josh Boyer 2ed6732
+		if (ret < 0)
Josh Boyer 2ed6732
+			goto exit;
Josh Boyer 2ed6732
+	}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	for (retries = 5; retries > 0; retries--) {
Josh Boyer 2ed6732
+		data->writeReport[0] = RMI_READ_ADDR_REPORT_ID;
Josh Boyer 2ed6732
+		data->writeReport[1] = 0; /* old 1 byte read count */
Josh Boyer 2ed6732
+		data->writeReport[2] = addr & 0xFF;
Josh Boyer 2ed6732
+		data->writeReport[3] = (addr >> 8) & 0xFF;
Josh Boyer 2ed6732
+		data->writeReport[4] = len  & 0xFF;
Josh Boyer 2ed6732
+		data->writeReport[5] = (len >> 8) & 0xFF;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+		set_bit(RMI_READ_REQUEST_PENDING, &data->flags);
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+		ret = rmi_write_report(hdev, data->writeReport,
Josh Boyer 2ed6732
+						data->output_report_size);
Josh Boyer 2ed6732
+		if (ret != data->output_report_size) {
Josh Boyer 2ed6732
+			clear_bit(RMI_READ_REQUEST_PENDING, &data->flags);
Josh Boyer 2ed6732
+			dev_err(&hdev->dev,
Josh Boyer 2ed6732
+				"failed to write request output report (%d)\n",
Josh Boyer 2ed6732
+				ret);
Josh Boyer 2ed6732
+			goto exit;
Josh Boyer 2ed6732
+		}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+		bytes_read = 0;
Josh Boyer 2ed6732
+		bytes_needed = len;
Josh Boyer 2ed6732
+		while (bytes_read < len) {
Josh Boyer 2ed6732
+			if (!wait_event_timeout(data->wait,
Josh Boyer 2ed6732
+				test_bit(RMI_READ_DATA_PENDING, &data->flags),
Josh Boyer 2ed6732
+					msecs_to_jiffies(1000))) {
Josh Boyer 2ed6732
+				hid_warn(hdev, "%s: timeout elapsed\n",
Josh Boyer 2ed6732
+					 __func__);
Josh Boyer 2ed6732
+				ret = -EAGAIN;
Josh Boyer 2ed6732
+				break;
Josh Boyer 2ed6732
+			}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+			read_input_count = data->readReport[1];
Josh Boyer 2ed6732
+			memcpy(buf + bytes_read, &data->readReport[2],
Josh Boyer 2ed6732
+				read_input_count < bytes_needed ?
Josh Boyer 2ed6732
+					read_input_count : bytes_needed);
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+			bytes_read += read_input_count;
Josh Boyer 2ed6732
+			bytes_needed -= read_input_count;
Josh Boyer 2ed6732
+			clear_bit(RMI_READ_DATA_PENDING, &data->flags);
Josh Boyer 2ed6732
+		}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+		if (ret >= 0) {
Josh Boyer 2ed6732
+			ret = 0;
Josh Boyer 2ed6732
+			break;
Josh Boyer 2ed6732
+		}
Josh Boyer 2ed6732
+	}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+exit:
Josh Boyer 2ed6732
+	clear_bit(RMI_READ_REQUEST_PENDING, &data->flags);
Josh Boyer 2ed6732
+	mutex_unlock(&data->page_mutex);
Josh Boyer 2ed6732
+	return ret;
Josh Boyer 2ed6732
+}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+static inline int rmi_read(struct hid_device *hdev, u16 addr, void *buf)
Josh Boyer 2ed6732
+{
Josh Boyer 2ed6732
+	return rmi_read_block(hdev, addr, buf, 1);
Josh Boyer 2ed6732
+}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+static void rmi_f11_process_touch(struct rmi_data *hdata, int slot,
Josh Boyer 2ed6732
+		u8 finger_state, u8 *touch_data)
Josh Boyer 2ed6732
+{
Josh Boyer 2ed6732
+	int x, y, wx, wy;
Josh Boyer 2ed6732
+	int wide, major, minor;
Josh Boyer 2ed6732
+	int z;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	input_mt_slot(hdata->input, slot);
Josh Boyer 2ed6732
+	input_mt_report_slot_state(hdata->input, MT_TOOL_FINGER,
Josh Boyer 2ed6732
+			finger_state == 0x01);
Josh Boyer 2ed6732
+	if (finger_state == 0x01) {
Josh Boyer 2ed6732
+		x = (touch_data[0] << 4) | (touch_data[2] & 0x07);
Josh Boyer 2ed6732
+		y = (touch_data[1] << 4) | (touch_data[2] >> 4);
Josh Boyer 2ed6732
+		wx = touch_data[3] & 0x07;
Josh Boyer 2ed6732
+		wy = touch_data[3] >> 4;
Josh Boyer 2ed6732
+		wide = (wx > wy);
Josh Boyer 2ed6732
+		major = max(wx, wy);
Josh Boyer 2ed6732
+		minor = min(wx, wy);
Josh Boyer 2ed6732
+		z = touch_data[4];
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+		/* y is inverted */
Josh Boyer 2ed6732
+		y = hdata->max_y - y;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+		input_event(hdata->input, EV_ABS, ABS_MT_POSITION_X, x);
Josh Boyer 2ed6732
+		input_event(hdata->input, EV_ABS, ABS_MT_POSITION_Y, y);
Josh Boyer 2ed6732
+		input_event(hdata->input, EV_ABS, ABS_MT_ORIENTATION, wide);
Josh Boyer 2ed6732
+		input_event(hdata->input, EV_ABS, ABS_MT_PRESSURE, z);
Josh Boyer 2ed6732
+		input_event(hdata->input, EV_ABS, ABS_MT_TOUCH_MAJOR, major);
Josh Boyer 2ed6732
+		input_event(hdata->input, EV_ABS, ABS_MT_TOUCH_MINOR, minor);
Josh Boyer 2ed6732
+	}
Josh Boyer 2ed6732
+}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+static void rmi_reset_work(struct work_struct *work)
Josh Boyer 2ed6732
+{
Josh Boyer 2ed6732
+	struct rmi_data *hdata = container_of(work, struct rmi_data,
Josh Boyer 2ed6732
+						reset_work);
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	/* switch the device to RMI if we receive a generic mouse report */
Josh Boyer 2ed6732
+	rmi_set_mode(hdata->hdev, RMI_MODE_ATTN_REPORTS);
Josh Boyer 2ed6732
+}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+static inline int rmi_schedule_reset(struct hid_device *hdev)
Josh Boyer 2ed6732
+{
Josh Boyer 2ed6732
+	struct rmi_data *hdata = hid_get_drvdata(hdev);
Josh Boyer 2ed6732
+	return schedule_work(&hdata->reset_work);
Josh Boyer 2ed6732
+}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+static int rmi_f11_input_event(struct hid_device *hdev, u8 irq, u8 *data,
Josh Boyer 2ed6732
+		int size)
Josh Boyer 2ed6732
+{
Josh Boyer 2ed6732
+	struct rmi_data *hdata = hid_get_drvdata(hdev);
Josh Boyer 2ed6732
+	int offset;
Josh Boyer 2ed6732
+	int i;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	if (size < hdata->f11.report_size)
Josh Boyer 2ed6732
+		return 0;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	if (!(irq & hdata->f11.irq_mask))
Josh Boyer 2ed6732
+		return 0;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	offset = (hdata->max_fingers >> 2) + 1;
Josh Boyer 2ed6732
+	for (i = 0; i < hdata->max_fingers; i++) {
Josh Boyer 2ed6732
+		int fs_byte_position = i >> 2;
Josh Boyer 2ed6732
+		int fs_bit_position = (i & 0x3) << 1;
Josh Boyer 2ed6732
+		int finger_state = (data[fs_byte_position] >> fs_bit_position) &
Josh Boyer 2ed6732
+					0x03;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+		rmi_f11_process_touch(hdata, i, finger_state,
Josh Boyer 2ed6732
+				&data[offset + 5 * i]);
Josh Boyer 2ed6732
+	}
Josh Boyer 2ed6732
+	input_mt_sync_frame(hdata->input);
Josh Boyer 2ed6732
+	input_sync(hdata->input);
Josh Boyer 2ed6732
+	return hdata->f11.report_size;
Josh Boyer 2ed6732
+}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+static int rmi_f30_input_event(struct hid_device *hdev, u8 irq, u8 *data,
Josh Boyer 2ed6732
+		int size)
Josh Boyer 2ed6732
+{
Josh Boyer 2ed6732
+	struct rmi_data *hdata = hid_get_drvdata(hdev);
Josh Boyer 2ed6732
+	int i;
Josh Boyer 2ed6732
+	int button = 0;
Josh Boyer 2ed6732
+	bool value;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	if (!(irq & hdata->f30.irq_mask))
Josh Boyer 2ed6732
+		return 0;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	for (i = 0; i < hdata->gpio_led_count; i++) {
Josh Boyer 2ed6732
+		if (test_bit(i, &hdata->button_mask)) {
Josh Boyer 2ed6732
+			value = (data[i / 8] >> (i & 0x07)) & BIT(0);
Josh Boyer 2ed6732
+			if (test_bit(i, &hdata->button_state_mask))
Josh Boyer 2ed6732
+				value = !value;
Josh Boyer 2ed6732
+			input_event(hdata->input, EV_KEY, BTN_LEFT + button++,
Josh Boyer 2ed6732
+					value);
Josh Boyer 2ed6732
+		}
Josh Boyer 2ed6732
+	}
Josh Boyer 2ed6732
+	return hdata->f30.report_size;
Josh Boyer 2ed6732
+}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+static int rmi_input_event(struct hid_device *hdev, u8 *data, int size)
Josh Boyer 2ed6732
+{
Josh Boyer 2ed6732
+	struct rmi_data *hdata = hid_get_drvdata(hdev);
Josh Boyer 2ed6732
+	unsigned long irq_mask = 0;
Josh Boyer 2ed6732
+	unsigned index = 2;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	if (!(test_bit(RMI_STARTED, &hdata->flags)))
Josh Boyer 2ed6732
+		return 0;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	irq_mask |= hdata->f11.irq_mask;
Josh Boyer 2ed6732
+	irq_mask |= hdata->f30.irq_mask;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	if (data[1] & ~irq_mask)
Josh Boyer 2ed6732
+		hid_warn(hdev, "unknown intr source:%02lx %s:%d\n",
Josh Boyer 2ed6732
+			data[1] & ~irq_mask, __FILE__, __LINE__);
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	if (hdata->f11.interrupt_base < hdata->f30.interrupt_base) {
Josh Boyer 2ed6732
+		index += rmi_f11_input_event(hdev, data[1], &data[index],
Josh Boyer 2ed6732
+				size - index);
Josh Boyer 2ed6732
+		index += rmi_f30_input_event(hdev, data[1], &data[index],
Josh Boyer 2ed6732
+				size - index);
Josh Boyer 2ed6732
+	} else {
Josh Boyer 2ed6732
+		index += rmi_f30_input_event(hdev, data[1], &data[index],
Josh Boyer 2ed6732
+				size - index);
Josh Boyer 2ed6732
+		index += rmi_f11_input_event(hdev, data[1], &data[index],
Josh Boyer 2ed6732
+				size - index);
Josh Boyer 2ed6732
+	}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	return 1;
Josh Boyer 2ed6732
+}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+static int rmi_read_data_event(struct hid_device *hdev, u8 *data, int size)
Josh Boyer 2ed6732
+{
Josh Boyer 2ed6732
+	struct rmi_data *hdata = hid_get_drvdata(hdev);
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	if (!test_bit(RMI_READ_REQUEST_PENDING, &hdata->flags)) {
Josh Boyer 2ed6732
+		hid_err(hdev, "no read request pending\n");
Josh Boyer 2ed6732
+		return 0;
Josh Boyer 2ed6732
+	}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	memcpy(hdata->readReport, data, size < hdata->input_report_size ?
Josh Boyer 2ed6732
+			size : hdata->input_report_size);
Josh Boyer 2ed6732
+	set_bit(RMI_READ_DATA_PENDING, &hdata->flags);
Josh Boyer 2ed6732
+	wake_up(&hdata->wait);
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	return 1;
Josh Boyer 2ed6732
+}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+static int rmi_raw_event(struct hid_device *hdev,
Josh Boyer 2ed6732
+		struct hid_report *report, u8 *data, int size)
Josh Boyer 2ed6732
+{
Josh Boyer 2ed6732
+	switch (data[0]) {
Josh Boyer 2ed6732
+	case RMI_READ_DATA_REPORT_ID:
Josh Boyer 2ed6732
+		return rmi_read_data_event(hdev, data, size);
Josh Boyer 2ed6732
+	case RMI_ATTN_REPORT_ID:
Josh Boyer 2ed6732
+		return rmi_input_event(hdev, data, size);
Josh Boyer 2ed6732
+	case RMI_MOUSE_REPORT_ID:
Josh Boyer 2ed6732
+		rmi_schedule_reset(hdev);
Josh Boyer 2ed6732
+		break;
Josh Boyer 2ed6732
+	}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	return 0;
Josh Boyer 2ed6732
+}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+static int rmi_post_reset(struct hid_device *hdev)
Josh Boyer 2ed6732
+{
Josh Boyer 2ed6732
+	return rmi_set_mode(hdev, RMI_MODE_ATTN_REPORTS);
Josh Boyer 2ed6732
+}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+static int rmi_post_resume(struct hid_device *hdev)
Josh Boyer 2ed6732
+{
Josh Boyer 2ed6732
+	return rmi_set_mode(hdev, RMI_MODE_ATTN_REPORTS);
Josh Boyer 2ed6732
+}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+#define RMI4_MAX_PAGE 0xff
Josh Boyer 2ed6732
+#define RMI4_PAGE_SIZE 0x0100
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+#define PDT_START_SCAN_LOCATION 0x00e9
Josh Boyer 2ed6732
+#define PDT_END_SCAN_LOCATION	0x0005
Josh Boyer 2ed6732
+#define RMI4_END_OF_PDT(id) ((id) == 0x00 || (id) == 0xff)
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+struct pdt_entry {
Josh Boyer 2ed6732
+	u8 query_base_addr:8;
Josh Boyer 2ed6732
+	u8 command_base_addr:8;
Josh Boyer 2ed6732
+	u8 control_base_addr:8;
Josh Boyer 2ed6732
+	u8 data_base_addr:8;
Josh Boyer 2ed6732
+	u8 interrupt_source_count:3;
Josh Boyer 2ed6732
+	u8 bits3and4:2;
Josh Boyer 2ed6732
+	u8 function_version:2;
Josh Boyer 2ed6732
+	u8 bit7:1;
Josh Boyer 2ed6732
+	u8 function_number:8;
Josh Boyer 2ed6732
+} __attribute__((__packed__));
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+static inline unsigned long rmi_gen_mask(unsigned irq_base, unsigned irq_count)
Josh Boyer 2ed6732
+{
Josh Boyer 2ed6732
+	return GENMASK(irq_count + irq_base - 1, irq_base);
Josh Boyer 2ed6732
+}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+static void rmi_register_function(struct rmi_data *data,
Josh Boyer 2ed6732
+	struct pdt_entry *pdt_entry, int page, unsigned interrupt_count)
Josh Boyer 2ed6732
+{
Josh Boyer 2ed6732
+	struct rmi_function *f = NULL;
Josh Boyer 2ed6732
+	u16 page_base = page << 8;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	switch (pdt_entry->function_number) {
Josh Boyer 2ed6732
+	case 0x11:
Josh Boyer 2ed6732
+		f = &data->f11;
Josh Boyer 2ed6732
+		break;
Josh Boyer 2ed6732
+	case 0x30:
Josh Boyer 2ed6732
+		f = &data->f30;
Josh Boyer 2ed6732
+		break;
Josh Boyer 2ed6732
+	}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	if (f) {
Josh Boyer 2ed6732
+		f->page = page;
Josh Boyer 2ed6732
+		f->query_base_addr = page_base | pdt_entry->query_base_addr;
Josh Boyer 2ed6732
+		f->command_base_addr = page_base | pdt_entry->command_base_addr;
Josh Boyer 2ed6732
+		f->control_base_addr = page_base | pdt_entry->control_base_addr;
Josh Boyer 2ed6732
+		f->data_base_addr = page_base | pdt_entry->data_base_addr;
Josh Boyer 2ed6732
+		f->interrupt_base = interrupt_count;
Josh Boyer 2ed6732
+		f->interrupt_count = pdt_entry->interrupt_source_count;
Josh Boyer 2ed6732
+		f->irq_mask = rmi_gen_mask(f->interrupt_base,
Josh Boyer 2ed6732
+						f->interrupt_count);
Josh Boyer 2ed6732
+	}
Josh Boyer 2ed6732
+}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+static int rmi_scan_pdt(struct hid_device *hdev)
Josh Boyer 2ed6732
+{
Josh Boyer 2ed6732
+	struct rmi_data *data = hid_get_drvdata(hdev);
Josh Boyer 2ed6732
+	struct pdt_entry entry;
Josh Boyer 2ed6732
+	int page;
Josh Boyer 2ed6732
+	bool page_has_function;
Josh Boyer 2ed6732
+	int i;
Josh Boyer 2ed6732
+	int retval;
Josh Boyer 2ed6732
+	int interrupt = 0;
Josh Boyer 2ed6732
+	u16 page_start, pdt_start , pdt_end;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	hid_info(hdev, "Scanning PDT...\n");
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	for (page = 0; (page <= RMI4_MAX_PAGE); page++) {
Josh Boyer 2ed6732
+		page_start = RMI4_PAGE_SIZE * page;
Josh Boyer 2ed6732
+		pdt_start = page_start + PDT_START_SCAN_LOCATION;
Josh Boyer 2ed6732
+		pdt_end = page_start + PDT_END_SCAN_LOCATION;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+		page_has_function = false;
Josh Boyer 2ed6732
+		for (i = pdt_start; i >= pdt_end; i -= sizeof(entry)) {
Josh Boyer 2ed6732
+			retval = rmi_read_block(hdev, i, &entry, sizeof(entry));
Josh Boyer 2ed6732
+			if (retval) {
Josh Boyer 2ed6732
+				hid_err(hdev,
Josh Boyer 2ed6732
+					"Read of PDT entry at %#06x failed.\n",
Josh Boyer 2ed6732
+					i);
Josh Boyer 2ed6732
+				goto error_exit;
Josh Boyer 2ed6732
+			}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+			if (RMI4_END_OF_PDT(entry.function_number))
Josh Boyer 2ed6732
+				break;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+			page_has_function = true;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+			hid_info(hdev, "Found F%02X on page %#04x\n",
Josh Boyer 2ed6732
+					entry.function_number, page);
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+			rmi_register_function(data, &entry, page, interrupt);
Josh Boyer 2ed6732
+			interrupt += entry.interrupt_source_count;
Josh Boyer 2ed6732
+		}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+		if (!page_has_function)
Josh Boyer 2ed6732
+			break;
Josh Boyer 2ed6732
+	}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	hid_info(hdev, "%s: Done with PDT scan.\n", __func__);
Josh Boyer 2ed6732
+	retval = 0;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+error_exit:
Josh Boyer 2ed6732
+	return retval;
Josh Boyer 2ed6732
+}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+static int rmi_populate_f11(struct hid_device *hdev)
Josh Boyer 2ed6732
+{
Josh Boyer 2ed6732
+	struct rmi_data *data = hid_get_drvdata(hdev);
Josh Boyer 2ed6732
+	u8 buf[20];
Josh Boyer 2ed6732
+	int ret;
Josh Boyer 2ed6732
+	bool has_query12;
Josh Boyer 2ed6732
+	bool has_physical_props;
Josh Boyer 2ed6732
+	unsigned x_size, y_size;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	if (!data->f11.query_base_addr) {
Josh Boyer 2ed6732
+		hid_err(hdev, "No 2D sensor found, giving up.\n");
Josh Boyer 2ed6732
+		return -ENODEV;
Josh Boyer 2ed6732
+	}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	/* query 0 contains some useful information */
Josh Boyer 2ed6732
+	ret = rmi_read(hdev, data->f11.query_base_addr, buf);
Josh Boyer 2ed6732
+	if (ret) {
Josh Boyer 2ed6732
+		hid_err(hdev, "can not get query 0: %d.\n", ret);
Josh Boyer 2ed6732
+		return ret;
Josh Boyer 2ed6732
+	}
Josh Boyer 2ed6732
+	has_query12 = !!(buf[0] & BIT(5));
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	/* query 1 to get the max number of fingers */
Josh Boyer 2ed6732
+	ret = rmi_read(hdev, data->f11.query_base_addr + 1, buf);
Josh Boyer 2ed6732
+	if (ret) {
Josh Boyer 2ed6732
+		hid_err(hdev, "can not get NumberOfFingers: %d.\n", ret);
Josh Boyer 2ed6732
+		return ret;
Josh Boyer 2ed6732
+	}
Josh Boyer 2ed6732
+	data->max_fingers = (buf[0] & 0x07) + 1;
Josh Boyer 2ed6732
+	if (data->max_fingers > 5)
Josh Boyer 2ed6732
+		data->max_fingers = 10;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	data->f11.report_size = data->max_fingers * 5 +
Josh Boyer 2ed6732
+				DIV_ROUND_UP(data->max_fingers, 4);
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	if (!(buf[0] & BIT(4))) {
Josh Boyer 2ed6732
+		hid_err(hdev, "No absolute events, giving up.\n");
Josh Boyer 2ed6732
+		return -ENODEV;
Josh Boyer 2ed6732
+	}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	/*
Josh Boyer 2ed6732
+	 * query 12 to know if the physical properties are reported
Josh Boyer 2ed6732
+	 * (query 12 is at offset 10 for HID devices)
Josh Boyer 2ed6732
+	 */
Josh Boyer 2ed6732
+	if (has_query12) {
Josh Boyer 2ed6732
+		ret = rmi_read(hdev, data->f11.query_base_addr + 10, buf);
Josh Boyer 2ed6732
+		if (ret) {
Josh Boyer 2ed6732
+			hid_err(hdev, "can not get query 12: %d.\n", ret);
Josh Boyer 2ed6732
+			return ret;
Josh Boyer 2ed6732
+		}
Josh Boyer 2ed6732
+		has_physical_props = !!(buf[0] & BIT(5));
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+		if (has_physical_props) {
Josh Boyer 2ed6732
+			ret = rmi_read_block(hdev,
Josh Boyer 2ed6732
+					data->f11.query_base_addr + 11, buf, 4);
Josh Boyer 2ed6732
+			if (ret) {
Josh Boyer 2ed6732
+				hid_err(hdev, "can not read query 15-18: %d.\n",
Josh Boyer 2ed6732
+					ret);
Josh Boyer 2ed6732
+				return ret;
Josh Boyer 2ed6732
+			}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+			x_size = buf[0] | (buf[1] << 8);
Josh Boyer 2ed6732
+			y_size = buf[2] | (buf[3] << 8);
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+			data->x_size_mm = DIV_ROUND_CLOSEST(x_size, 10);
Josh Boyer 2ed6732
+			data->y_size_mm = DIV_ROUND_CLOSEST(y_size, 10);
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+			hid_info(hdev, "%s: size in mm: %d x %d\n",
Josh Boyer 2ed6732
+				 __func__, data->x_size_mm, data->y_size_mm);
Josh Boyer 2ed6732
+		}
Josh Boyer 2ed6732
+	}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	/* retrieve the ctrl registers */
Josh Boyer 2ed6732
+	ret = rmi_read_block(hdev, data->f11.control_base_addr, buf, 20);
Josh Boyer 2ed6732
+	if (ret) {
Josh Boyer 2ed6732
+		hid_err(hdev, "can not read ctrl block of size 20: %d.\n", ret);
Josh Boyer 2ed6732
+		return ret;
Josh Boyer 2ed6732
+	}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	data->max_x = buf[6] | (buf[7] << 8);
Josh Boyer 2ed6732
+	data->max_y = buf[8] | (buf[9] << 8);
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	return 0;
Josh Boyer 2ed6732
+}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+static int rmi_populate_f30(struct hid_device *hdev)
Josh Boyer 2ed6732
+{
Josh Boyer 2ed6732
+	struct rmi_data *data = hid_get_drvdata(hdev);
Josh Boyer 2ed6732
+	u8 buf[20];
Josh Boyer 2ed6732
+	int ret;
Josh Boyer 2ed6732
+	bool has_gpio, has_led;
Josh Boyer 2ed6732
+	unsigned bytes_per_ctrl;
Josh Boyer 2ed6732
+	u8 ctrl2_addr;
Josh Boyer 2ed6732
+	int ctrl2_3_length;
Josh Boyer 2ed6732
+	int i;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	/* function F30 is for physical buttons */
Josh Boyer 2ed6732
+	if (!data->f30.query_base_addr) {
Josh Boyer 2ed6732
+		hid_err(hdev, "No GPIO/LEDs found, giving up.\n");
Josh Boyer 2ed6732
+		return -ENODEV;
Josh Boyer 2ed6732
+	}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	ret = rmi_read_block(hdev, data->f30.query_base_addr, buf, 2);
Josh Boyer 2ed6732
+	if (ret) {
Josh Boyer 2ed6732
+		hid_err(hdev, "can not get F30 query registers: %d.\n", ret);
Josh Boyer 2ed6732
+		return ret;
Josh Boyer 2ed6732
+	}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	has_gpio = !!(buf[0] & BIT(3));
Josh Boyer 2ed6732
+	has_led = !!(buf[0] & BIT(2));
Josh Boyer 2ed6732
+	data->gpio_led_count = buf[1] & 0x1f;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	/* retrieve ctrl 2 & 3 registers */
Josh Boyer 2ed6732
+	bytes_per_ctrl = (data->gpio_led_count + 7) / 8;
Josh Boyer 2ed6732
+	/* Ctrl0 is present only if both has_gpio and has_led are set*/
Josh Boyer 2ed6732
+	ctrl2_addr = (has_gpio && has_led) ? bytes_per_ctrl : 0;
Josh Boyer 2ed6732
+	/* Ctrl1 is always be present */
Josh Boyer 2ed6732
+	ctrl2_addr += bytes_per_ctrl;
Josh Boyer 2ed6732
+	ctrl2_3_length = 2 * bytes_per_ctrl;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	data->f30.report_size = bytes_per_ctrl;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	ret = rmi_read_block(hdev, data->f30.control_base_addr + ctrl2_addr,
Josh Boyer 2ed6732
+				buf, ctrl2_3_length);
Josh Boyer 2ed6732
+	if (ret) {
Josh Boyer 2ed6732
+		hid_err(hdev, "can not read ctrl 2&3 block of size %d: %d.\n",
Josh Boyer 2ed6732
+			ctrl2_3_length, ret);
Josh Boyer 2ed6732
+		return ret;
Josh Boyer 2ed6732
+	}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	for (i = 0; i < data->gpio_led_count; i++) {
Josh Boyer 2ed6732
+		int byte_position = i >> 3;
Josh Boyer 2ed6732
+		int bit_position = i & 0x07;
Josh Boyer 2ed6732
+		u8 dir_byte = buf[byte_position];
Josh Boyer 2ed6732
+		u8 data_byte = buf[byte_position + bytes_per_ctrl];
Josh Boyer 2ed6732
+		bool dir = (dir_byte >> bit_position) & BIT(0);
Josh Boyer 2ed6732
+		bool dat = (data_byte >> bit_position) & BIT(0);
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+		if (dir == 0) {
Josh Boyer 2ed6732
+			/* input mode */
Josh Boyer 2ed6732
+			if (dat) {
Josh Boyer 2ed6732
+				/* actual buttons have pull up resistor */
Josh Boyer 2ed6732
+				data->button_count++;
Josh Boyer 2ed6732
+				set_bit(i, &data->button_mask);
Josh Boyer 2ed6732
+				set_bit(i, &data->button_state_mask);
Josh Boyer 2ed6732
+			}
Josh Boyer 2ed6732
+		}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	return 0;
Josh Boyer 2ed6732
+}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+static int rmi_populate(struct hid_device *hdev)
Josh Boyer 2ed6732
+{
Josh Boyer 2ed6732
+	int ret;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	ret = rmi_scan_pdt(hdev);
Josh Boyer 2ed6732
+	if (ret) {
Josh Boyer 2ed6732
+		hid_err(hdev, "PDT scan failed with code %d.\n", ret);
Josh Boyer 2ed6732
+		return ret;
Josh Boyer 2ed6732
+	}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	ret = rmi_populate_f11(hdev);
Josh Boyer 2ed6732
+	if (ret) {
Josh Boyer 2ed6732
+		hid_err(hdev, "Error while initializing F11 (%d).\n", ret);
Josh Boyer 2ed6732
+		return ret;
Josh Boyer 2ed6732
+	}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	ret = rmi_populate_f30(hdev);
Josh Boyer 2ed6732
+	if (ret)
Josh Boyer 2ed6732
+		hid_warn(hdev, "Error while initializing F30 (%d).\n", ret);
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	return 0;
Josh Boyer 2ed6732
+}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+static void rmi_input_configured(struct hid_device *hdev, struct hid_input *hi)
Josh Boyer 2ed6732
+{
Josh Boyer 2ed6732
+	struct rmi_data *data = hid_get_drvdata(hdev);
Josh Boyer 2ed6732
+	struct input_dev *input = hi->input;
Josh Boyer 2ed6732
+	int ret;
Josh Boyer 2ed6732
+	int res_x, res_y, i;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	data->input = input;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	hid_dbg(hdev, "Opening low level driver\n");
Josh Boyer 2ed6732
+	ret = hid_hw_open(hdev);
Josh Boyer 2ed6732
+	if (ret)
Josh Boyer 2ed6732
+		return;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	/* Allow incoming hid reports */
Josh Boyer 2ed6732
+	hid_device_io_start(hdev);
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	ret = rmi_set_mode(hdev, RMI_MODE_ATTN_REPORTS);
Josh Boyer 2ed6732
+	if (ret < 0) {
Josh Boyer 2ed6732
+		dev_err(&hdev->dev, "failed to set rmi mode\n");
Josh Boyer 2ed6732
+		goto exit;
Josh Boyer 2ed6732
+	}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	ret = rmi_set_page(hdev, 0);
Josh Boyer 2ed6732
+	if (ret < 0) {
Josh Boyer 2ed6732
+		dev_err(&hdev->dev, "failed to set page select to 0.\n");
Josh Boyer 2ed6732
+		goto exit;
Josh Boyer 2ed6732
+	}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	ret = rmi_populate(hdev);
Josh Boyer 2ed6732
+	if (ret)
Josh Boyer 2ed6732
+		goto exit;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	__set_bit(EV_ABS, input->evbit);
Josh Boyer 2ed6732
+	input_set_abs_params(input, ABS_MT_POSITION_X, 1, data->max_x, 0, 0);
Josh Boyer 2ed6732
+	input_set_abs_params(input, ABS_MT_POSITION_Y, 1, data->max_y, 0, 0);
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	if (data->x_size_mm && data->x_size_mm) {
Josh Boyer 2ed6732
+		res_x = (data->max_x - 1) / data->x_size_mm;
Josh Boyer 2ed6732
+		res_y = (data->max_y - 1) / data->x_size_mm;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+		input_abs_set_res(input, ABS_MT_POSITION_X, res_x);
Josh Boyer 2ed6732
+		input_abs_set_res(input, ABS_MT_POSITION_Y, res_y);
Josh Boyer 2ed6732
+	}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	input_set_abs_params(input, ABS_MT_ORIENTATION, 0, 1, 0, 0);
Josh Boyer 2ed6732
+	input_set_abs_params(input, ABS_MT_PRESSURE, 0, 0xff, 0, 0);
Josh Boyer 2ed6732
+	input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 0x0f, 0, 0);
Josh Boyer 2ed6732
+	input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 0x0f, 0, 0);
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	input_mt_init_slots(input, data->max_fingers, INPUT_MT_POINTER);
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	if (data->button_count) {
Josh Boyer 2ed6732
+		__set_bit(EV_KEY, input->evbit);
Josh Boyer 2ed6732
+		for (i = 0; i < data->button_count; i++)
Josh Boyer 2ed6732
+			__set_bit(BTN_LEFT + i, input->keybit);
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+		if (data->button_count == 1)
Josh Boyer 2ed6732
+			__set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
Josh Boyer 2ed6732
+	}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	set_bit(RMI_STARTED, &data->flags);
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+exit:
Josh Boyer 2ed6732
+	hid_device_io_stop(hdev);
Josh Boyer 2ed6732
+	hid_hw_close(hdev);
Josh Boyer 2ed6732
+}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+static int rmi_input_mapping(struct hid_device *hdev,
Josh Boyer 2ed6732
+		struct hid_input *hi, struct hid_field *field,
Josh Boyer 2ed6732
+		struct hid_usage *usage, unsigned long **bit, int *max)
Josh Boyer 2ed6732
+{
Josh Boyer 2ed6732
+	/* we want to make HID ignore the advertised HID collection */
Josh Boyer 2ed6732
+	return -1;
Josh Boyer 2ed6732
+}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+static int rmi_probe(struct hid_device *hdev, const struct hid_device_id *id)
Josh Boyer 2ed6732
+{
Josh Boyer 2ed6732
+	struct rmi_data *data = NULL;
Josh Boyer 2ed6732
+	int ret;
Josh Boyer 2ed6732
+	size_t alloc_size;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	data = devm_kzalloc(&hdev->dev, sizeof(struct rmi_data), GFP_KERNEL);
Josh Boyer 2ed6732
+	if (!data)
Josh Boyer 2ed6732
+		return -ENOMEM;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	INIT_WORK(&data->reset_work, rmi_reset_work);
Josh Boyer 2ed6732
+	data->hdev = hdev;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	hid_set_drvdata(hdev, data);
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	hdev->quirks |= HID_QUIRK_NO_INIT_REPORTS;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	ret = hid_parse(hdev);
Josh Boyer 2ed6732
+	if (ret) {
Josh Boyer 2ed6732
+		hid_err(hdev, "parse failed\n");
Josh Boyer 2ed6732
+		return ret;
Josh Boyer 2ed6732
+	}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	data->input_report_size = (hdev->report_enum[HID_INPUT_REPORT]
Josh Boyer 2ed6732
+		.report_id_hash[RMI_ATTN_REPORT_ID]->size >> 3)
Josh Boyer 2ed6732
+		+ 1 /* report id */;
Josh Boyer 2ed6732
+	data->output_report_size = (hdev->report_enum[HID_OUTPUT_REPORT]
Josh Boyer 2ed6732
+		.report_id_hash[RMI_WRITE_REPORT_ID]->size >> 3)
Josh Boyer 2ed6732
+		+ 1 /* report id */;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	alloc_size = data->output_report_size + data->input_report_size;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	data->writeReport = devm_kzalloc(&hdev->dev, alloc_size, GFP_KERNEL);
Josh Boyer 2ed6732
+	if (!data->writeReport) {
Josh Boyer 2ed6732
+		ret = -ENOMEM;
Josh Boyer 2ed6732
+		return ret;
Josh Boyer 2ed6732
+	}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	data->readReport = data->writeReport + data->output_report_size;
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	init_waitqueue_head(&data->wait);
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	mutex_init(&data->page_mutex);
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
Josh Boyer 2ed6732
+	if (ret) {
Josh Boyer 2ed6732
+		hid_err(hdev, "hw start failed\n");
Josh Boyer 2ed6732
+		return ret;
Josh Boyer 2ed6732
+	}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	if (!test_bit(RMI_STARTED, &data->flags)) {
Josh Boyer 2ed6732
+		hid_hw_stop(hdev);
Josh Boyer 2ed6732
+		return -EIO;
Josh Boyer 2ed6732
+	}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	return 0;
Josh Boyer 2ed6732
+}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+static void rmi_remove(struct hid_device *hdev)
Josh Boyer 2ed6732
+{
Josh Boyer 2ed6732
+	struct rmi_data *hdata = hid_get_drvdata(hdev);
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	clear_bit(RMI_STARTED, &hdata->flags);
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+	hid_hw_stop(hdev);
Josh Boyer 2ed6732
+}
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+static const struct hid_device_id rmi_id[] = {
Josh Boyer 2ed6732
+	{ HID_I2C_DEVICE(USB_VENDOR_ID_SYNAPTICS, HID_ANY_ID) },
Josh Boyer 2ed6732
+	{ HID_USB_DEVICE(USB_VENDOR_ID_SYNAPTICS, HID_ANY_ID) },
Josh Boyer 2ed6732
+	{ }
Josh Boyer 2ed6732
+};
Josh Boyer 2ed6732
+MODULE_DEVICE_TABLE(hid, rmi_id);
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+static struct hid_driver rmi_driver = {
Josh Boyer 2ed6732
+	.name = "hid-rmi",
Josh Boyer 2ed6732
+	.id_table		= rmi_id,
Josh Boyer 2ed6732
+	.probe			= rmi_probe,
Josh Boyer 2ed6732
+	.remove			= rmi_remove,
Josh Boyer 2ed6732
+	.raw_event		= rmi_raw_event,
Josh Boyer 2ed6732
+	.input_mapping		= rmi_input_mapping,
Josh Boyer 2ed6732
+	.input_configured	= rmi_input_configured,
Josh Boyer 2ed6732
+#ifdef CONFIG_PM
Josh Boyer 2ed6732
+	.resume			= rmi_post_resume,
Josh Boyer 2ed6732
+	.reset_resume		= rmi_post_reset,
Josh Boyer 2ed6732
+#endif
Josh Boyer 2ed6732
+};
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+module_hid_driver(rmi_driver);
Josh Boyer 2ed6732
+
Josh Boyer 2ed6732
+MODULE_AUTHOR("Andrew Duggan <aduggan@synaptics.com>");
Josh Boyer 2ed6732
+MODULE_DESCRIPTION("RMI HID driver");
Josh Boyer 2ed6732
+MODULE_LICENSE("GPL");
Josh Boyer 2ed6732
diff --git a/include/linux/hid.h b/include/linux/hid.h
Josh Boyer 2ed6732
index 31b9d29..1b5f1e9 100644
Josh Boyer 2ed6732
--- a/include/linux/hid.h
Josh Boyer 2ed6732
+++ b/include/linux/hid.h
Josh Boyer 2ed6732
@@ -571,6 +571,8 @@ struct hid_descriptor {
Josh Boyer 2ed6732
 	.bus = BUS_USB, .vendor = (ven), .product = (prod)
Josh Boyer 2ed6732
 #define HID_BLUETOOTH_DEVICE(ven, prod)					\
Josh Boyer 2ed6732
 	.bus = BUS_BLUETOOTH, .vendor = (ven), .product = (prod)
Josh Boyer 2ed6732
+#define HID_I2C_DEVICE(ven, prod)				\
Josh Boyer 2ed6732
+	.bus = BUS_I2C, .vendor = (ven), .product = (prod)
Josh Boyer 2ed6732
 
Josh Boyer 2ed6732
 #define HID_REPORT_ID(rep) \
Josh Boyer 2ed6732
 	.report_type = (rep)
Josh Boyer 2ed6732
-- 
Josh Boyer 2ed6732
1.8.3.1
Josh Boyer 2ed6732