Linux Input/HID development
 help / color / mirror / Atom feed
* [PATCH v7 1/2] drm/bridge: sil_sii8620: make remote control optional.
From: Ronald Tschalär @ 2019-04-19  8:19 UTC (permalink / raw)
  To: Dmitry Torokhov, Henrik Rydberg, Andy Shevchenko, Andrzej Hajda,
	Inki Dae, Greg Kroah-Hartman
  Cc: Lukas Wunner, Federico Lorenzi, Laurent Pinchart, linux-input,
	dri-devel, linux-kernel
In-Reply-To: <20190419081926.13567-1-ronald@innovation.ch>

commit d6abe6df706c (drm/bridge: sil_sii8620: do not have a dependency
of RC_CORE) changed the driver to select both RC_CORE and INPUT.
However, this causes problems with other drivers, in particular an input
driver that depends on MFD_INTEL_LPSS_PCI (to be added in a separate
commit):

  drivers/clk/Kconfig:9:error: recursive dependency detected!
  drivers/clk/Kconfig:9:        symbol COMMON_CLK is selected by MFD_INTEL_LPSS
  drivers/mfd/Kconfig:566:      symbol MFD_INTEL_LPSS is selected by MFD_INTEL_LPSS_PCI
  drivers/mfd/Kconfig:580:      symbol MFD_INTEL_LPSS_PCI is implied by KEYBOARD_APPLESPI
  drivers/input/keyboard/Kconfig:73:    symbol KEYBOARD_APPLESPI depends on INPUT
  drivers/input/Kconfig:8:      symbol INPUT is selected by DRM_SIL_SII8620
  drivers/gpu/drm/bridge/Kconfig:83:    symbol DRM_SIL_SII8620 depends on DRM_BRIDGE
  drivers/gpu/drm/bridge/Kconfig:1:     symbol DRM_BRIDGE is selected by DRM_PL111
  drivers/gpu/drm/pl111/Kconfig:1:      symbol DRM_PL111 depends on COMMON_CLK

According to the docs and general consensus, select should only be used
for non user-visible symbols, but both RC_CORE and INPUT are
user-visible. Furthermore almost all other references to INPUT
throughout the kernel config are depends, not selects. For this reason
the first part of this change reverts commit d6abe6df706c.

In order to address the original reason for commit d6abe6df706c, namely
that not all boards use the remote controller functionality and hence
should not need have to deal with RC_CORE, the second part of this
change now makes the remote control support in the driver optional and
contingent on RC_CORE being defined. And with this the hard dependency
on INPUT also goes away as that is only needed if RC_CORE is defined
(which in turn already depends on INPUT).

CC: Inki Dae <inki.dae@samsung.com>
CC: Andrzej Hajda <a.hajda@samsung.com>
CC: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
CC: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Signed-off-by: Ronald Tschalär <ronald@innovation.ch>
Reviewed-by: Andrzej Hajda <a.hajda@samsung.com>
---
 drivers/gpu/drm/bridge/Kconfig       |  3 +--
 drivers/gpu/drm/bridge/sil-sii8620.c | 10 +++++++---
 2 files changed, 8 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 2fee47b0d50b..9cf07105b73a 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -85,8 +85,7 @@ config DRM_SIL_SII8620
 	depends on OF
 	select DRM_KMS_HELPER
 	imply EXTCON
-	select INPUT
-	select RC_CORE
+	imply RC_CORE
 	help
 	  Silicon Image SII8620 HDMI/MHL bridge chip driver.
 
diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c
index a6e8f4591e63..cff3131aae6c 100644
--- a/drivers/gpu/drm/bridge/sil-sii8620.c
+++ b/drivers/gpu/drm/bridge/sil-sii8620.c
@@ -1763,10 +1763,8 @@ static bool sii8620_rcp_consume(struct sii8620 *ctx, u8 scancode)
 
 	scancode &= MHL_RCP_KEY_ID_MASK;
 
-	if (!ctx->rc_dev) {
-		dev_dbg(ctx->dev, "RCP input device not initialized\n");
+	if (!IS_ENABLED(CONFIG_RC_CORE) || !ctx->rc_dev)
 		return false;
-	}
 
 	if (pressed)
 		rc_keydown(ctx->rc_dev, RC_PROTO_CEC, scancode, 0);
@@ -2103,6 +2101,9 @@ static void sii8620_init_rcp_input_dev(struct sii8620 *ctx)
 	struct rc_dev *rc_dev;
 	int ret;
 
+	if (!IS_ENABLED(CONFIG_RC_CORE))
+		return;
+
 	rc_dev = rc_allocate_device(RC_DRIVER_SCANCODE);
 	if (!rc_dev) {
 		dev_err(ctx->dev, "Failed to allocate RC device\n");
@@ -2217,6 +2218,9 @@ static void sii8620_detach(struct drm_bridge *bridge)
 {
 	struct sii8620 *ctx = bridge_to_sii8620(bridge);
 
+	if (!IS_ENABLED(CONFIG_RC_CORE))
+		return;
+
 	rc_unregister_device(ctx->rc_dev);
 }
 
-- 
2.20.1

^ permalink raw reply related

* [PATCH v7 2/2] Input: add Apple SPI keyboard and trackpad driver.
From: Ronald Tschalär @ 2019-04-19  8:19 UTC (permalink / raw)
  To: Dmitry Torokhov, Henrik Rydberg, Andy Shevchenko, Andrzej Hajda,
	Inki Dae, Greg Kroah-Hartman
  Cc: Lukas Wunner, Federico Lorenzi, Laurent Pinchart, linux-input,
	dri-devel, linux-kernel
In-Reply-To: <20190419081926.13567-1-ronald@innovation.ch>

The keyboard and trackpad on recent MacBook's (since 8,1) and
MacBookPro's (13,* and 14,*) are attached to an SPI controller instead
of USB, as previously. The higher level protocol is not publicly
documented and hence has been reverse engineered. As a consequence there
are still a number of unknown fields and commands. However, the known
parts have been working well and received extensive testing and use.

In order for this driver to work, the proper SPI drivers need to be
loaded too; for MB8,1 these are spi_pxa2xx_platform and spi_pxa2xx_pci;
for all others they are spi_pxa2xx_platform and intel_lpss_pci. For this
reason enabling this driver in the config implies enabling the above
drivers.

CC: Federico Lorenzi <federico@travelground.com>
CC: Lukas Wunner <lukas@wunner.de>
CC: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Link: https://bugzilla.kernel.org/show_bug.cgi?id=99891
Link: https://bugzilla.kernel.org/show_bug.cgi?id=108331
Signed-off-by: Ronald Tschalär <ronald@innovation.ch>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
---
 drivers/input/keyboard/Kconfig          |   15 +
 drivers/input/keyboard/Makefile         |    1 +
 drivers/input/keyboard/applespi.c       | 1975 +++++++++++++++++++++++
 drivers/input/keyboard/applespi.h       |   29 +
 drivers/input/keyboard/applespi_trace.h |   94 ++
 5 files changed, 2114 insertions(+)
 create mode 100644 drivers/input/keyboard/applespi.c
 create mode 100644 drivers/input/keyboard/applespi.h
 create mode 100644 drivers/input/keyboard/applespi_trace.h

diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index a878351f1643..d0a9e7fa2508 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -70,6 +70,21 @@ config KEYBOARD_AMIGA
 config ATARI_KBD_CORE
 	bool
 
+config KEYBOARD_APPLESPI
+	tristate "Apple SPI keyboard and trackpad"
+	depends on ACPI && EFI
+	depends on SPI
+	depends on X86 || COMPILE_TEST
+	imply SPI_PXA2XX
+	imply SPI_PXA2XX_PCI
+	imply MFD_INTEL_LPSS_PCI
+	help
+	  Say Y here if you are running Linux on any Apple MacBook8,1 or later,
+	  or any MacBookPro13,* or MacBookPro14,*.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called applespi.
+
 config KEYBOARD_ATARI
 	tristate "Atari keyboard"
 	depends on ATARI
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 182e92985dbf..9283fee2505a 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_KEYBOARD_ADP5520)		+= adp5520-keys.o
 obj-$(CONFIG_KEYBOARD_ADP5588)		+= adp5588-keys.o
 obj-$(CONFIG_KEYBOARD_ADP5589)		+= adp5589-keys.o
 obj-$(CONFIG_KEYBOARD_AMIGA)		+= amikbd.o
+obj-$(CONFIG_KEYBOARD_APPLESPI)		+= applespi.o
 obj-$(CONFIG_KEYBOARD_ATARI)		+= atakbd.o
 obj-$(CONFIG_KEYBOARD_ATKBD)		+= atkbd.o
 obj-$(CONFIG_KEYBOARD_BCM)		+= bcm-keypad.o
diff --git a/drivers/input/keyboard/applespi.c b/drivers/input/keyboard/applespi.c
new file mode 100644
index 000000000000..c23d7797d108
--- /dev/null
+++ b/drivers/input/keyboard/applespi.c
@@ -0,0 +1,1975 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MacBook (Pro) SPI keyboard and touchpad driver
+ *
+ * Copyright (c) 2015-2018 Federico Lorenzi
+ * Copyright (c) 2017-2018 Ronald Tschalär
+ */
+
+/*
+ * The keyboard and touchpad controller on the MacBookAir6, MacBookPro12,
+ * MacBook8 and newer can be driven either by USB or SPI. However the USB
+ * pins are only connected on the MacBookAir6 and 7 and the MacBookPro12.
+ * All others need this driver. The interface is selected using ACPI methods:
+ *
+ * * UIEN ("USB Interface Enable"): If invoked with argument 1, disables SPI
+ *   and enables USB. If invoked with argument 0, disables USB.
+ * * UIST ("USB Interface Status"): Returns 1 if USB is enabled, 0 otherwise.
+ * * SIEN ("SPI Interface Enable"): If invoked with argument 1, disables USB
+ *   and enables SPI. If invoked with argument 0, disables SPI.
+ * * SIST ("SPI Interface Status"): Returns 1 if SPI is enabled, 0 otherwise.
+ * * ISOL: Resets the four GPIO pins used for SPI. Intended to be invoked with
+ *   argument 1, then once more with argument 0.
+ *
+ * UIEN and UIST are only provided on models where the USB pins are connected.
+ *
+ * SPI-based Protocol
+ * ------------------
+ *
+ * The device and driver exchange messages (struct message); each message is
+ * encapsulated in one or more packets (struct spi_packet). There are two types
+ * of exchanges: reads, and writes. A read is signaled by a GPE, upon which one
+ * message can be read from the device. A write exchange consists of writing a
+ * command message, immediately reading a short status packet, and then, upon
+ * receiving a GPE, reading the response message. Write exchanges cannot be
+ * interleaved, i.e. a new write exchange must not be started till the previous
+ * write exchange is complete. Whether a received message is part of a read or
+ * write exchange is indicated in the encapsulating packet's flags field.
+ *
+ * A single message may be too large to fit in a single packet (which has a
+ * fixed, 256-byte size). In that case it will be split over multiple,
+ * consecutive packets.
+ */
+
+#include <linux/acpi.h>
+#include <linux/crc16.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/efi.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/spi/spi.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+
+#include <asm/barrier.h>
+#include <asm/unaligned.h>
+
+#define CREATE_TRACE_POINTS
+#include "applespi.h"
+#include "applespi_trace.h"
+
+#define APPLESPI_PACKET_SIZE	256
+#define APPLESPI_STATUS_SIZE	4
+
+#define PACKET_TYPE_READ	0x20
+#define PACKET_TYPE_WRITE	0x40
+#define PACKET_DEV_KEYB		0x01
+#define PACKET_DEV_TPAD		0x02
+#define PACKET_DEV_INFO		0xd0
+
+#define MAX_ROLLOVER		6
+
+#define MAX_FINGERS		11
+#define MAX_FINGER_ORIENTATION	16384
+#define MAX_PKTS_PER_MSG	2
+
+#define KBD_BL_LEVEL_MIN	32U
+#define KBD_BL_LEVEL_MAX	255U
+#define KBD_BL_LEVEL_SCALE	1000000U
+#define KBD_BL_LEVEL_ADJ	\
+	((KBD_BL_LEVEL_MAX - KBD_BL_LEVEL_MIN) * KBD_BL_LEVEL_SCALE / 255U)
+
+#define EFI_BL_LEVEL_NAME	L"KeyboardBacklightLevel"
+#define EFI_BL_LEVEL_GUID	EFI_GUID(0xa076d2af, 0x9678, 0x4386, 0x8b, 0x58, 0x1f, 0xc8, 0xef, 0x04, 0x16, 0x19)
+
+#define APPLE_FLAG_FKEY		0x01
+
+#define SPI_RW_CHG_DELAY_US	100	/* from experimentation, in µs */
+
+#define SYNAPTICS_VENDOR_ID	0x06cb
+
+static unsigned int fnmode = 1;
+module_param(fnmode, uint, 0644);
+MODULE_PARM_DESC(fnmode, "Mode of Fn key on Apple keyboards (0 = disabled, [1] = fkeyslast, 2 = fkeysfirst)");
+
+static unsigned int fnremap;
+module_param(fnremap, uint, 0644);
+MODULE_PARM_DESC(fnremap, "Remap Fn key ([0] = no-remap; 1 = left-ctrl, 2 = left-shift, 3 = left-alt, 4 = left-meta, 6 = right-shift, 7 = right-alt, 8 = right-meta)");
+
+static bool iso_layout;
+module_param(iso_layout, bool, 0644);
+MODULE_PARM_DESC(iso_layout, "Enable/Disable hardcoded ISO-layout of the keyboard. ([0] = disabled, 1 = enabled)");
+
+static char touchpad_dimensions[40];
+module_param_string(touchpad_dimensions, touchpad_dimensions,
+		    sizeof(touchpad_dimensions), 0444);
+MODULE_PARM_DESC(touchpad_dimensions, "The pixel dimensions of the touchpad, as XxY+W+H .");
+
+/**
+ * struct keyboard_protocol - keyboard message.
+ * message.type = 0x0110, message.length = 0x000a
+ *
+ * @unknown1:		unknown
+ * @modifiers:		bit-set of modifier/control keys pressed
+ * @unknown2:		unknown
+ * @keys_pressed:	the (non-modifier) keys currently pressed
+ * @fn_pressed:		whether the fn key is currently pressed
+ * @crc16:		crc over the whole message struct (message header +
+ *			this struct) minus this @crc16 field
+ */
+struct keyboard_protocol {
+	__u8			unknown1;
+	__u8			modifiers;
+	__u8			unknown2;
+	__u8			keys_pressed[MAX_ROLLOVER];
+	__u8			fn_pressed;
+	__le16			crc16;
+};
+
+/**
+ * struct tp_finger - single trackpad finger structure, le16-aligned
+ *
+ * @origin:		zero when switching track finger
+ * @abs_x:		absolute x coodinate
+ * @abs_y:		absolute y coodinate
+ * @rel_x:		relative x coodinate
+ * @rel_y:		relative y coodinate
+ * @tool_major:		tool area, major axis
+ * @tool_minor:		tool area, minor axis
+ * @orientation:	16384 when point, else 15 bit angle
+ * @touch_major:	touch area, major axis
+ * @touch_minor:	touch area, minor axis
+ * @unused:		zeros
+ * @pressure:		pressure on forcetouch touchpad
+ * @multi:		one finger: varies, more fingers: constant
+ * @crc16:		on last finger: crc over the whole message struct
+ *			(i.e. message header + this struct) minus the last
+ *			@crc16 field; unknown on all other fingers.
+ */
+struct tp_finger {
+	__le16 origin;
+	__le16 abs_x;
+	__le16 abs_y;
+	__le16 rel_x;
+	__le16 rel_y;
+	__le16 tool_major;
+	__le16 tool_minor;
+	__le16 orientation;
+	__le16 touch_major;
+	__le16 touch_minor;
+	__le16 unused[2];
+	__le16 pressure;
+	__le16 multi;
+	__le16 crc16;
+};
+
+/**
+ * struct touchpad_protocol - touchpad message.
+ * message.type = 0x0210
+ *
+ * @unknown1:		unknown
+ * @clicked:		1 if a button-click was detected, 0 otherwise
+ * @unknown2:		unknown
+ * @number_of_fingers:	the number of fingers being reported in @fingers
+ * @clicked2:		same as @clicked
+ * @unknown3:		unknown
+ * @fingers:		the data for each finger
+ */
+struct touchpad_protocol {
+	__u8			unknown1[1];
+	__u8			clicked;
+	__u8			unknown2[28];
+	__u8			number_of_fingers;
+	__u8			clicked2;
+	__u8			unknown3[16];
+	struct tp_finger	fingers[0];
+};
+
+/**
+ * struct command_protocol_tp_info - get touchpad info.
+ * message.type = 0x1020, message.length = 0x0000
+ *
+ * @crc16:		crc over the whole message struct (message header +
+ *			this struct) minus this @crc16 field
+ */
+struct command_protocol_tp_info {
+	__le16			crc16;
+};
+
+/**
+ * struct touchpad_info - touchpad info response.
+ * message.type = 0x1020, message.length = 0x006e
+ *
+ * @unknown1:		unknown
+ * @model_flags:	flags (vary by model number, but significance otherwise
+ *			unknown)
+ * @model_no:		the touchpad model number
+ * @unknown2:		unknown
+ * @crc16:		crc over the whole message struct (message header +
+ *			this struct) minus this @crc16 field
+ */
+struct touchpad_info_protocol {
+	__u8			unknown1[105];
+	__u8			model_flags;
+	__u8			model_no;
+	__u8			unknown2[3];
+	__le16			crc16;
+};
+
+/**
+ * struct command_protocol_mt_init - initialize multitouch.
+ * message.type = 0x0252, message.length = 0x0002
+ *
+ * @cmd:		value: 0x0102
+ * @crc16:		crc over the whole message struct (message header +
+ *			this struct) minus this @crc16 field
+ */
+struct command_protocol_mt_init {
+	__le16			cmd;
+	__le16			crc16;
+};
+
+/**
+ * struct command_protocol_capsl - toggle caps-lock led
+ * message.type = 0x0151, message.length = 0x0002
+ *
+ * @unknown:		value: 0x01 (length?)
+ * @led:		0 off, 2 on
+ * @crc16:		crc over the whole message struct (message header +
+ *			this struct) minus this @crc16 field
+ */
+struct command_protocol_capsl {
+	__u8			unknown;
+	__u8			led;
+	__le16			crc16;
+};
+
+/**
+ * struct command_protocol_bl - set keyboard backlight brightness
+ * message.type = 0xB051, message.length = 0x0006
+ *
+ * @const1:		value: 0x01B0
+ * @level:		the brightness level to set
+ * @const2:		value: 0x0001 (backlight off), 0x01F4 (backlight on)
+ * @crc16:		crc over the whole message struct (message header +
+ *			this struct) minus this @crc16 field
+ */
+struct command_protocol_bl {
+	__le16			const1;
+	__le16			level;
+	__le16			const2;
+	__le16			crc16;
+};
+
+/**
+ * struct message - a complete spi message.
+ *
+ * Each message begins with fixed header, followed by a message-type specific
+ * payload, and ends with a 16-bit crc. Because of the varying lengths of the
+ * payload, the crc is defined at the end of each payload struct, rather than
+ * in this struct.
+ *
+ * @type:	the message type
+ * @zero:	always 0
+ * @counter:	incremented on each message, rolls over after 255; there is a
+ *		separate counter for each message type.
+ * @rsp_buf_len:response buffer length (the exact nature of this field is quite
+ *		speculative). On a request/write this is often the same as
+ *		@length, though in some cases it has been seen to be much larger
+ *		(e.g. 0x400); on a response/read this the same as on the
+ *		request; for reads that are not responses it is 0.
+ * @length:	length of the remainder of the data in the whole message
+ *		structure (after re-assembly in case of being split over
+ *		multiple spi-packets), minus the trailing crc. The total size
+ *		of the message struct is therefore @length + 10.
+ */
+struct message {
+	__le16		type;
+	__u8		zero;
+	__u8		counter;
+	__le16		rsp_buf_len;
+	__le16		length;
+	union {
+		struct keyboard_protocol	keyboard;
+		struct touchpad_protocol	touchpad;
+		struct touchpad_info_protocol	tp_info;
+		struct command_protocol_tp_info	tp_info_command;
+		struct command_protocol_mt_init	init_mt_command;
+		struct command_protocol_capsl	capsl_command;
+		struct command_protocol_bl	bl_command;
+		__u8				data[0];
+	};
+};
+
+/* type + zero + counter + rsp_buf_len + length */
+#define MSG_HEADER_SIZE	8
+
+/**
+ * struct spi_packet - a complete spi packet; always 256 bytes. This carries
+ * the (parts of the) message in the data. But note that this does not
+ * necessarily contain a complete message, as in some cases (e.g. many
+ * fingers pressed) the message is split over multiple packets (see the
+ * @offset, @remaining, and @length fields). In general the data parts in
+ * spi_packet's are concatenated until @remaining is 0, and the result is an
+ * message.
+ *
+ * @flags:	0x40 = write (to device), 0x20 = read (from device); note that
+ *		the response to a write still has 0x40.
+ * @device:	1 = keyboard, 2 = touchpad
+ * @offset:	specifies the offset of this packet's data in the complete
+ *		message; i.e. > 0 indicates this is a continuation packet (in
+ *		the second packet for a message split over multiple packets
+ *		this would then be the same as the @length in the first packet)
+ * @remaining:	number of message bytes remaining in subsequents packets (in
+ *		the first packet of a message split over two packets this would
+ *		then be the same as the @length in the second packet)
+ * @length:	length of the valid data in the @data in this packet
+ * @data:	all or part of a message
+ * @crc16:	crc over this whole structure minus this @crc16 field. This
+ *		covers just this packet, even on multi-packet messages (in
+ *		contrast to the crc in the message).
+ */
+struct spi_packet {
+	__u8			flags;
+	__u8			device;
+	__le16			offset;
+	__le16			remaining;
+	__le16			length;
+	__u8			data[246];
+	__le16			crc16;
+};
+
+struct spi_settings {
+	u64	spi_cs_delay;		/* cs-to-clk delay in us */
+	u64	reset_a2r_usec;		/* active-to-receive delay? */
+	u64	reset_rec_usec;		/* ? (cur val: 10) */
+};
+
+/* this mimics struct drm_rect */
+struct applespi_tp_info {
+	int	x_min;
+	int	y_min;
+	int	x_max;
+	int	y_max;
+};
+
+struct applespi_data {
+	struct spi_device		*spi;
+	struct spi_settings		spi_settings;
+	struct input_dev		*keyboard_input_dev;
+	struct input_dev		*touchpad_input_dev;
+
+	u8				*tx_buffer;
+	u8				*tx_status;
+	u8				*rx_buffer;
+
+	u8				*msg_buf;
+	unsigned int			saved_msg_len;
+
+	struct applespi_tp_info		tp_info;
+
+	u8				last_keys_pressed[MAX_ROLLOVER];
+	u8				last_keys_fn_pressed[MAX_ROLLOVER];
+	u8				last_fn_pressed;
+	struct input_mt_pos		pos[MAX_FINGERS];
+	int				slots[MAX_FINGERS];
+	int				gpe;
+	acpi_handle			sien;
+	acpi_handle			sist;
+
+	struct spi_transfer		dl_t;
+	struct spi_transfer		rd_t;
+	struct spi_message		rd_m;
+
+	struct spi_transfer		ww_t;
+	struct spi_transfer		wd_t;
+	struct spi_transfer		wr_t;
+	struct spi_transfer		st_t;
+	struct spi_message		wr_m;
+
+	bool				want_tp_info_cmd;
+	bool				want_mt_init_cmd;
+	bool				want_cl_led_on;
+	bool				have_cl_led_on;
+	unsigned int			want_bl_level;
+	unsigned int			have_bl_level;
+	unsigned int			cmd_msg_cntr;
+	/* lock to protect the above parameters and flags below */
+	spinlock_t			cmd_msg_lock;
+	bool				cmd_msg_queued;
+	enum applespi_evt_type		cmd_evt_type;
+
+	struct led_classdev		backlight_info;
+
+	bool				suspended;
+	bool				drain;
+	wait_queue_head_t		drain_complete;
+	bool				read_active;
+	bool				write_active;
+
+	struct work_struct		work;
+	struct touchpad_info_protocol	rcvd_tp_info;
+
+	struct dentry			*debugfs_root;
+	bool				debug_tp_dim;
+	char				tp_dim_val[40];
+	int				tp_dim_min_x;
+	int				tp_dim_max_x;
+	int				tp_dim_min_y;
+	int				tp_dim_max_y;
+};
+
+static const unsigned char applespi_scancodes[] = {
+	0, 0, 0, 0,
+	KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F, KEY_G, KEY_H, KEY_I, KEY_J,
+	KEY_K, KEY_L, KEY_M, KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T,
+	KEY_U, KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z,
+	KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0,
+	KEY_ENTER, KEY_ESC, KEY_BACKSPACE, KEY_TAB, KEY_SPACE, KEY_MINUS,
+	KEY_EQUAL, KEY_LEFTBRACE, KEY_RIGHTBRACE, KEY_BACKSLASH, 0,
+	KEY_SEMICOLON, KEY_APOSTROPHE, KEY_GRAVE, KEY_COMMA, KEY_DOT, KEY_SLASH,
+	KEY_CAPSLOCK,
+	KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9,
+	KEY_F10, KEY_F11, KEY_F12, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	KEY_RIGHT, KEY_LEFT, KEY_DOWN, KEY_UP,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_102ND,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_RO, 0, KEY_YEN, 0, 0, 0, 0, 0,
+	0, KEY_KATAKANAHIRAGANA, KEY_MUHENKAN
+};
+
+/*
+ * This must have exactly as many entries as there are bits in
+ * struct keyboard_protocol.modifiers .
+ */
+static const unsigned char applespi_controlcodes[] = {
+	KEY_LEFTCTRL,
+	KEY_LEFTSHIFT,
+	KEY_LEFTALT,
+	KEY_LEFTMETA,
+	0,
+	KEY_RIGHTSHIFT,
+	KEY_RIGHTALT,
+	KEY_RIGHTMETA
+};
+
+struct applespi_key_translation {
+	u16 from;
+	u16 to;
+	u8 flags;
+};
+
+static const struct applespi_key_translation applespi_fn_codes[] = {
+	{ KEY_BACKSPACE, KEY_DELETE },
+	{ KEY_ENTER,	KEY_INSERT },
+	{ KEY_F1,	KEY_BRIGHTNESSDOWN,	APPLE_FLAG_FKEY },
+	{ KEY_F2,	KEY_BRIGHTNESSUP,	APPLE_FLAG_FKEY },
+	{ KEY_F3,	KEY_SCALE,		APPLE_FLAG_FKEY },
+	{ KEY_F4,	KEY_DASHBOARD,		APPLE_FLAG_FKEY },
+	{ KEY_F5,	KEY_KBDILLUMDOWN,	APPLE_FLAG_FKEY },
+	{ KEY_F6,	KEY_KBDILLUMUP,		APPLE_FLAG_FKEY },
+	{ KEY_F7,	KEY_PREVIOUSSONG,	APPLE_FLAG_FKEY },
+	{ KEY_F8,	KEY_PLAYPAUSE,		APPLE_FLAG_FKEY },
+	{ KEY_F9,	KEY_NEXTSONG,		APPLE_FLAG_FKEY },
+	{ KEY_F10,	KEY_MUTE,		APPLE_FLAG_FKEY },
+	{ KEY_F11,	KEY_VOLUMEDOWN,		APPLE_FLAG_FKEY },
+	{ KEY_F12,	KEY_VOLUMEUP,		APPLE_FLAG_FKEY },
+	{ KEY_RIGHT,	KEY_END },
+	{ KEY_LEFT,	KEY_HOME },
+	{ KEY_DOWN,	KEY_PAGEDOWN },
+	{ KEY_UP,	KEY_PAGEUP },
+	{ }
+};
+
+static const struct applespi_key_translation apple_iso_keyboard[] = {
+	{ KEY_GRAVE,	KEY_102ND },
+	{ KEY_102ND,	KEY_GRAVE },
+	{ }
+};
+
+struct applespi_tp_model_info {
+	u16			model;
+	struct applespi_tp_info	tp_info;
+};
+
+static const struct applespi_tp_model_info applespi_tp_models[] = {
+	{
+		.model = 0x04,	/* MB8 MB9 MB10 */
+		.tp_info = { -5087, -182, 5579, 6089 },
+	},
+	{
+		.model = 0x05,	/* MBP13,1 MBP13,2 MBP14,1 MBP14,2 */
+		.tp_info = { -6243, -170, 6749, 7685 },
+	},
+	{
+		.model = 0x06,	/* MBP13,3 MBP14,3 */
+		.tp_info = { -7456, -163, 7976, 9283 },
+	},
+	{}
+};
+
+typedef void (*applespi_trace_fun)(enum applespi_evt_type,
+				   enum applespi_pkt_type, u8 *, size_t);
+
+static applespi_trace_fun applespi_get_trace_fun(enum applespi_evt_type type)
+{
+	switch (type) {
+	case ET_CMD_TP_INI:
+		return trace_applespi_tp_ini_cmd;
+	case ET_CMD_BL:
+		return trace_applespi_backlight_cmd;
+	case ET_CMD_CL:
+		return trace_applespi_caps_lock_cmd;
+	case ET_RD_KEYB:
+		return trace_applespi_keyboard_data;
+	case ET_RD_TPAD:
+		return trace_applespi_touchpad_data;
+	case ET_RD_UNKN:
+		return trace_applespi_unknown_data;
+	default:
+		WARN_ONCE(1, "Unknown msg type %d", type);
+		return trace_applespi_unknown_data;
+	}
+}
+
+static void applespi_setup_read_txfrs(struct applespi_data *applespi)
+{
+	struct spi_message *msg = &applespi->rd_m;
+	struct spi_transfer *dl_t = &applespi->dl_t;
+	struct spi_transfer *rd_t = &applespi->rd_t;
+
+	memset(dl_t, 0, sizeof(*dl_t));
+	memset(rd_t, 0, sizeof(*rd_t));
+
+	dl_t->delay_usecs = applespi->spi_settings.spi_cs_delay;
+
+	rd_t->rx_buf = applespi->rx_buffer;
+	rd_t->len = APPLESPI_PACKET_SIZE;
+
+	spi_message_init(msg);
+	spi_message_add_tail(dl_t, msg);
+	spi_message_add_tail(rd_t, msg);
+}
+
+static void applespi_setup_write_txfrs(struct applespi_data *applespi)
+{
+	struct spi_message *msg = &applespi->wr_m;
+	struct spi_transfer *wt_t = &applespi->ww_t;
+	struct spi_transfer *dl_t = &applespi->wd_t;
+	struct spi_transfer *wr_t = &applespi->wr_t;
+	struct spi_transfer *st_t = &applespi->st_t;
+
+	memset(wt_t, 0, sizeof(*wt_t));
+	memset(dl_t, 0, sizeof(*dl_t));
+	memset(wr_t, 0, sizeof(*wr_t));
+	memset(st_t, 0, sizeof(*st_t));
+
+	/*
+	 * All we need here is a delay at the beginning of the message before
+	 * asserting cs. But the current spi API doesn't support this, so we
+	 * end up with an extra unnecessary (but harmless) cs assertion and
+	 * deassertion.
+	 */
+	wt_t->delay_usecs = SPI_RW_CHG_DELAY_US;
+	wt_t->cs_change = 1;
+
+	dl_t->delay_usecs = applespi->spi_settings.spi_cs_delay;
+
+	wr_t->tx_buf = applespi->tx_buffer;
+	wr_t->len = APPLESPI_PACKET_SIZE;
+	wr_t->delay_usecs = SPI_RW_CHG_DELAY_US;
+
+	st_t->rx_buf = applespi->tx_status;
+	st_t->len = APPLESPI_STATUS_SIZE;
+
+	spi_message_init(msg);
+	spi_message_add_tail(wt_t, msg);
+	spi_message_add_tail(dl_t, msg);
+	spi_message_add_tail(wr_t, msg);
+	spi_message_add_tail(st_t, msg);
+}
+
+static int applespi_async(struct applespi_data *applespi,
+			  struct spi_message *message, void (*complete)(void *))
+{
+	message->complete = complete;
+	message->context = applespi;
+
+	return spi_async(applespi->spi, message);
+}
+
+static inline bool applespi_check_write_status(struct applespi_data *applespi,
+					       int sts)
+{
+	static u8 status_ok[] = { 0xac, 0x27, 0x68, 0xd5 };
+
+	if (sts < 0) {
+		dev_warn(&applespi->spi->dev, "Error writing to device: %d\n",
+			 sts);
+		return false;
+	}
+
+	if (memcmp(applespi->tx_status, status_ok, APPLESPI_STATUS_SIZE)) {
+		dev_warn(&applespi->spi->dev, "Error writing to device: %*ph\n",
+			 APPLESPI_STATUS_SIZE, applespi->tx_status);
+		return false;
+	}
+
+	return true;
+}
+
+static int applespi_get_spi_settings(struct applespi_data *applespi)
+{
+	struct acpi_device *adev = ACPI_COMPANION(&applespi->spi->dev);
+	const union acpi_object *o;
+	struct spi_settings *settings = &applespi->spi_settings;
+
+	if (!acpi_dev_get_property(adev, "spiCSDelay", ACPI_TYPE_BUFFER, &o))
+		settings->spi_cs_delay = *(u64 *)o->buffer.pointer;
+	else
+		dev_warn(&applespi->spi->dev,
+			 "Property spiCSDelay not found\n");
+
+	if (!acpi_dev_get_property(adev, "resetA2RUsec", ACPI_TYPE_BUFFER, &o))
+		settings->reset_a2r_usec = *(u64 *)o->buffer.pointer;
+	else
+		dev_warn(&applespi->spi->dev,
+			 "Property resetA2RUsec not found\n");
+
+	if (!acpi_dev_get_property(adev, "resetRecUsec", ACPI_TYPE_BUFFER, &o))
+		settings->reset_rec_usec = *(u64 *)o->buffer.pointer;
+	else
+		dev_warn(&applespi->spi->dev,
+			 "Property resetRecUsec not found\n");
+
+	dev_dbg(&applespi->spi->dev,
+		"SPI settings: spi_cs_delay=%llu reset_a2r_usec=%llu reset_rec_usec=%llu\n",
+		settings->spi_cs_delay, settings->reset_a2r_usec,
+		settings->reset_rec_usec);
+
+	return 0;
+}
+
+static int applespi_setup_spi(struct applespi_data *applespi)
+{
+	int sts;
+
+	sts = applespi_get_spi_settings(applespi);
+	if (sts)
+		return sts;
+
+	spin_lock_init(&applespi->cmd_msg_lock);
+	init_waitqueue_head(&applespi->drain_complete);
+
+	return 0;
+}
+
+static int applespi_enable_spi(struct applespi_data *applespi)
+{
+	acpi_status acpi_sts;
+	unsigned long long spi_status;
+
+	/* check if SPI is already enabled, so we can skip the delay below */
+	acpi_sts = acpi_evaluate_integer(applespi->sist, NULL, NULL,
+					 &spi_status);
+	if (ACPI_SUCCESS(acpi_sts) && spi_status)
+		return 0;
+
+	/* SIEN(1) will enable SPI communication */
+	acpi_sts = acpi_execute_simple_method(applespi->sien, NULL, 1);
+	if (ACPI_FAILURE(acpi_sts)) {
+		dev_err(&applespi->spi->dev, "SIEN failed: %s\n",
+			acpi_format_exception(acpi_sts));
+		return -ENODEV;
+	}
+
+	/*
+	 * Allow the SPI interface to come up before returning. Without this
+	 * delay, the SPI commands to enable multitouch mode may not reach
+	 * the trackpad controller, causing pointer movement to break upon
+	 * resume from sleep.
+	 */
+	msleep(50);
+
+	return 0;
+}
+
+static int applespi_send_cmd_msg(struct applespi_data *applespi);
+
+static void applespi_msg_complete(struct applespi_data *applespi,
+				  bool is_write_msg, bool is_read_compl)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&applespi->cmd_msg_lock, flags);
+
+	if (is_read_compl)
+		applespi->read_active = false;
+	if (is_write_msg)
+		applespi->write_active = false;
+
+	if (applespi->drain && !applespi->write_active)
+		wake_up_all(&applespi->drain_complete);
+
+	if (is_write_msg) {
+		applespi->cmd_msg_queued = false;
+		applespi_send_cmd_msg(applespi);
+	}
+
+	spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags);
+}
+
+static void applespi_async_write_complete(void *context)
+{
+	struct applespi_data *applespi = context;
+	enum applespi_evt_type evt_type = applespi->cmd_evt_type;
+
+	applespi_get_trace_fun(evt_type)(evt_type, PT_WRITE,
+					 applespi->tx_buffer,
+					 APPLESPI_PACKET_SIZE);
+	applespi_get_trace_fun(evt_type)(evt_type, PT_STATUS,
+					 applespi->tx_status,
+					 APPLESPI_STATUS_SIZE);
+
+	if (!applespi_check_write_status(applespi, applespi->wr_m.status)) {
+		/*
+		 * If we got an error, we presumably won't get the expected
+		 * response message either.
+		 */
+		applespi_msg_complete(applespi, true, false);
+	}
+}
+
+static int applespi_send_cmd_msg(struct applespi_data *applespi)
+{
+	u16 crc;
+	int sts;
+	struct spi_packet *packet = (struct spi_packet *)applespi->tx_buffer;
+	struct message *message = (struct message *)packet->data;
+	u16 msg_len;
+	u8 device;
+
+	/* check if draining */
+	if (applespi->drain)
+		return 0;
+
+	/* check whether send is in progress */
+	if (applespi->cmd_msg_queued)
+		return 0;
+
+	/* set up packet */
+	memset(packet, 0, APPLESPI_PACKET_SIZE);
+
+	/* are we processing init commands? */
+	if (applespi->want_tp_info_cmd) {
+		applespi->want_tp_info_cmd = false;
+		applespi->want_mt_init_cmd = true;
+		applespi->cmd_evt_type = ET_CMD_TP_INI;
+
+		/* build init command */
+		device = PACKET_DEV_INFO;
+
+		message->type = cpu_to_le16(0x1020);
+		msg_len = sizeof(message->tp_info_command);
+
+		message->zero = 0x02;
+		message->rsp_buf_len = cpu_to_le16(0x0200);
+
+	} else if (applespi->want_mt_init_cmd) {
+		applespi->want_mt_init_cmd = false;
+		applespi->cmd_evt_type = ET_CMD_TP_INI;
+
+		/* build init command */
+		device = PACKET_DEV_TPAD;
+
+		message->type = cpu_to_le16(0x0252);
+		msg_len = sizeof(message->init_mt_command);
+
+		message->init_mt_command.cmd = cpu_to_le16(0x0102);
+
+	/* do we need caps-lock command? */
+	} else if (applespi->want_cl_led_on != applespi->have_cl_led_on) {
+		applespi->have_cl_led_on = applespi->want_cl_led_on;
+		applespi->cmd_evt_type = ET_CMD_CL;
+
+		/* build led command */
+		device = PACKET_DEV_KEYB;
+
+		message->type = cpu_to_le16(0x0151);
+		msg_len = sizeof(message->capsl_command);
+
+		message->capsl_command.unknown = 0x01;
+		message->capsl_command.led = applespi->have_cl_led_on ? 2 : 0;
+
+	/* do we need backlight command? */
+	} else if (applespi->want_bl_level != applespi->have_bl_level) {
+		applespi->have_bl_level = applespi->want_bl_level;
+		applespi->cmd_evt_type = ET_CMD_BL;
+
+		/* build command buffer */
+		device = PACKET_DEV_KEYB;
+
+		message->type = cpu_to_le16(0xB051);
+		msg_len = sizeof(message->bl_command);
+
+		message->bl_command.const1 = cpu_to_le16(0x01B0);
+		message->bl_command.level =
+				cpu_to_le16(applespi->have_bl_level);
+
+		if (applespi->have_bl_level > 0)
+			message->bl_command.const2 = cpu_to_le16(0x01F4);
+		else
+			message->bl_command.const2 = cpu_to_le16(0x0001);
+
+	/* everything's up-to-date */
+	} else {
+		return 0;
+	}
+
+	/* finalize packet */
+	packet->flags = PACKET_TYPE_WRITE;
+	packet->device = device;
+	packet->length = cpu_to_le16(MSG_HEADER_SIZE + msg_len);
+
+	message->counter = applespi->cmd_msg_cntr++ % (U8_MAX + 1);
+
+	message->length = cpu_to_le16(msg_len - 2);
+	if (!message->rsp_buf_len)
+		message->rsp_buf_len = message->length;
+
+	crc = crc16(0, (u8 *)message, le16_to_cpu(packet->length) - 2);
+	put_unaligned_le16(crc, &message->data[msg_len - 2]);
+
+	crc = crc16(0, (u8 *)packet, sizeof(*packet) - 2);
+	packet->crc16 = cpu_to_le16(crc);
+
+	/* send command */
+	sts = applespi_async(applespi, &applespi->wr_m,
+			     applespi_async_write_complete);
+	if (sts) {
+		dev_warn(&applespi->spi->dev,
+			 "Error queueing async write to device: %d\n", sts);
+		return sts;
+	}
+
+	applespi->cmd_msg_queued = true;
+	applespi->write_active = true;
+
+	return 0;
+}
+
+static void applespi_init(struct applespi_data *applespi, bool is_resume)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&applespi->cmd_msg_lock, flags);
+
+	if (is_resume)
+		applespi->want_mt_init_cmd = true;
+	else
+		applespi->want_tp_info_cmd = true;
+	applespi_send_cmd_msg(applespi);
+
+	spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags);
+}
+
+static int applespi_set_capsl_led(struct applespi_data *applespi,
+				  bool capslock_on)
+{
+	unsigned long flags;
+	int sts;
+
+	spin_lock_irqsave(&applespi->cmd_msg_lock, flags);
+
+	applespi->want_cl_led_on = capslock_on;
+	sts = applespi_send_cmd_msg(applespi);
+
+	spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags);
+
+	return sts;
+}
+
+static void applespi_set_bl_level(struct led_classdev *led_cdev,
+				  enum led_brightness value)
+{
+	struct applespi_data *applespi =
+		container_of(led_cdev, struct applespi_data, backlight_info);
+	unsigned long flags;
+	int sts;
+
+	spin_lock_irqsave(&applespi->cmd_msg_lock, flags);
+
+	if (value == 0) {
+		applespi->want_bl_level = value;
+	} else {
+		/*
+		 * The backlight does not turn on till level 32, so we scale
+		 * the range here so that from a user's perspective it turns
+		 * on at 1.
+		 */
+		applespi->want_bl_level =
+			((value * KBD_BL_LEVEL_ADJ) / KBD_BL_LEVEL_SCALE +
+			 KBD_BL_LEVEL_MIN);
+	}
+
+	sts = applespi_send_cmd_msg(applespi);
+
+	spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags);
+}
+
+static int applespi_event(struct input_dev *dev, unsigned int type,
+			  unsigned int code, int value)
+{
+	struct applespi_data *applespi = input_get_drvdata(dev);
+
+	switch (type) {
+	case EV_LED:
+		applespi_set_capsl_led(applespi, !!test_bit(LED_CAPSL, dev->led));
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+/* lifted from the BCM5974 driver and renamed from raw2int */
+/* convert 16-bit little endian to signed integer */
+static inline int le16_to_int(__le16 x)
+{
+	return (signed short)le16_to_cpu(x);
+}
+
+static void applespi_debug_update_dimensions(struct applespi_data *applespi,
+					     const struct tp_finger *f)
+{
+	applespi->tp_dim_min_x = min_t(int, applespi->tp_dim_min_x, f->abs_x);
+	applespi->tp_dim_max_x = max_t(int, applespi->tp_dim_max_x, f->abs_x);
+	applespi->tp_dim_min_y = min_t(int, applespi->tp_dim_min_y, f->abs_y);
+	applespi->tp_dim_max_y = max_t(int, applespi->tp_dim_max_y, f->abs_y);
+}
+
+static int applespi_tp_dim_open(struct inode *inode, struct file *file)
+{
+	struct applespi_data *applespi = inode->i_private;
+
+	file->private_data = applespi;
+
+	snprintf(applespi->tp_dim_val, sizeof(applespi->tp_dim_val),
+		 "0x%.4x %dx%d+%u+%u\n",
+		 applespi->touchpad_input_dev->id.product,
+		 applespi->tp_dim_min_x, applespi->tp_dim_min_y,
+		 applespi->tp_dim_max_x - applespi->tp_dim_min_x,
+		 applespi->tp_dim_max_y - applespi->tp_dim_min_y);
+
+	return nonseekable_open(inode, file);
+}
+
+static ssize_t applespi_tp_dim_read(struct file *file, char __user *buf,
+				    size_t len, loff_t *off)
+{
+	struct applespi_data *applespi = file->private_data;
+
+	return simple_read_from_buffer(buf, len, off, applespi->tp_dim_val,
+				       strlen(applespi->tp_dim_val));
+}
+
+static const struct file_operations applespi_tp_dim_fops = {
+	.owner = THIS_MODULE,
+	.open = applespi_tp_dim_open,
+	.read = applespi_tp_dim_read,
+	.llseek = no_llseek,
+};
+
+static void report_finger_data(struct input_dev *input, int slot,
+			       const struct input_mt_pos *pos,
+			       const struct tp_finger *f)
+{
+	input_mt_slot(input, slot);
+	input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
+
+	input_report_abs(input, ABS_MT_TOUCH_MAJOR,
+			 le16_to_int(f->touch_major) << 1);
+	input_report_abs(input, ABS_MT_TOUCH_MINOR,
+			 le16_to_int(f->touch_minor) << 1);
+	input_report_abs(input, ABS_MT_WIDTH_MAJOR,
+			 le16_to_int(f->tool_major) << 1);
+	input_report_abs(input, ABS_MT_WIDTH_MINOR,
+			 le16_to_int(f->tool_minor) << 1);
+	input_report_abs(input, ABS_MT_ORIENTATION,
+			 MAX_FINGER_ORIENTATION - le16_to_int(f->orientation));
+	input_report_abs(input, ABS_MT_POSITION_X, pos->x);
+	input_report_abs(input, ABS_MT_POSITION_Y, pos->y);
+}
+
+static void report_tp_state(struct applespi_data *applespi,
+			    struct touchpad_protocol *t)
+{
+	const struct tp_finger *f;
+	struct input_dev *input;
+	const struct applespi_tp_info *tp_info = &applespi->tp_info;
+	int i, n;
+
+	/* touchpad_input_dev is set async in worker */
+	input = smp_load_acquire(&applespi->touchpad_input_dev);
+	if (!input)
+		return;	/* touchpad isn't initialized yet */
+
+	n = 0;
+
+	for (i = 0; i < t->number_of_fingers; i++) {
+		f = &t->fingers[i];
+		if (le16_to_int(f->touch_major) == 0)
+			continue;
+		applespi->pos[n].x = le16_to_int(f->abs_x);
+		applespi->pos[n].y = tp_info->y_min + tp_info->y_max -
+				     le16_to_int(f->abs_y);
+		n++;
+
+		if (applespi->debug_tp_dim)
+			applespi_debug_update_dimensions(applespi, f);
+	}
+
+	input_mt_assign_slots(input, applespi->slots, applespi->pos, n, 0);
+
+	for (i = 0; i < n; i++)
+		report_finger_data(input, applespi->slots[i],
+				   &applespi->pos[i], &t->fingers[i]);
+
+	input_mt_sync_frame(input);
+	input_report_key(input, BTN_LEFT, t->clicked);
+
+	input_sync(input);
+}
+
+static const struct applespi_key_translation *
+applespi_find_translation(const struct applespi_key_translation *table, u16 key)
+{
+	const struct applespi_key_translation *trans;
+
+	for (trans = table; trans->from; trans++)
+		if (trans->from == key)
+			return trans;
+
+	return NULL;
+}
+
+static unsigned int applespi_translate_fn_key(unsigned int key, int fn_pressed)
+{
+	const struct applespi_key_translation *trans;
+	int do_translate;
+
+	trans = applespi_find_translation(applespi_fn_codes, key);
+	if (trans) {
+		if (trans->flags & APPLE_FLAG_FKEY)
+			do_translate = (fnmode == 2 && fn_pressed) ||
+				       (fnmode == 1 && !fn_pressed);
+		else
+			do_translate = fn_pressed;
+
+		if (do_translate)
+			key = trans->to;
+	}
+
+	return key;
+}
+
+static unsigned int applespi_translate_iso_layout(unsigned int key)
+{
+	const struct applespi_key_translation *trans;
+
+	trans = applespi_find_translation(apple_iso_keyboard, key);
+	if (trans)
+		key = trans->to;
+
+	return key;
+}
+
+static unsigned int applespi_code_to_key(u8 code, int fn_pressed)
+{
+	unsigned int key = applespi_scancodes[code];
+
+	if (fnmode)
+		key = applespi_translate_fn_key(key, fn_pressed);
+	if (iso_layout)
+		key = applespi_translate_iso_layout(key);
+	return key;
+}
+
+static void
+applespi_remap_fn_key(struct keyboard_protocol *keyboard_protocol)
+{
+	unsigned char tmp;
+	u8 bit = BIT((fnremap - 1) & 0x07);
+
+	if (!fnremap || fnremap > ARRAY_SIZE(applespi_controlcodes) ||
+	    !applespi_controlcodes[fnremap - 1])
+		return;
+
+	tmp = keyboard_protocol->fn_pressed;
+	keyboard_protocol->fn_pressed = !!(keyboard_protocol->modifiers & bit);
+	if (tmp)
+		keyboard_protocol->modifiers |= bit;
+	else
+		keyboard_protocol->modifiers &= ~bit;
+}
+
+static void
+applespi_handle_keyboard_event(struct applespi_data *applespi,
+			       struct keyboard_protocol *keyboard_protocol)
+{
+	unsigned int key;
+	int i;
+
+	compiletime_assert(ARRAY_SIZE(applespi_controlcodes) ==
+			   sizeof_field(struct keyboard_protocol, modifiers) * 8,
+			   "applespi_controlcodes has wrong number of entries");
+
+	/* check for rollover overflow, which is signalled by all keys == 1 */
+	if (!memchr_inv(keyboard_protocol->keys_pressed, 1, MAX_ROLLOVER))
+		return;
+
+	/* remap fn key if desired */
+	applespi_remap_fn_key(keyboard_protocol);
+
+	/* check released keys */
+	for (i = 0; i < MAX_ROLLOVER; i++) {
+		if (memchr(keyboard_protocol->keys_pressed,
+			   applespi->last_keys_pressed[i], MAX_ROLLOVER))
+			continue;	/* key is still pressed */
+
+		key = applespi_code_to_key(applespi->last_keys_pressed[i],
+					   applespi->last_keys_fn_pressed[i]);
+		input_report_key(applespi->keyboard_input_dev, key, 0);
+		applespi->last_keys_fn_pressed[i] = 0;
+	}
+
+	/* check pressed keys */
+	for (i = 0; i < MAX_ROLLOVER; i++) {
+		if (keyboard_protocol->keys_pressed[i] <
+				ARRAY_SIZE(applespi_scancodes) &&
+		    keyboard_protocol->keys_pressed[i] > 0) {
+			key = applespi_code_to_key(
+					keyboard_protocol->keys_pressed[i],
+					keyboard_protocol->fn_pressed);
+			input_report_key(applespi->keyboard_input_dev, key, 1);
+			applespi->last_keys_fn_pressed[i] =
+					keyboard_protocol->fn_pressed;
+		}
+	}
+
+	/* check control keys */
+	for (i = 0; i < ARRAY_SIZE(applespi_controlcodes); i++) {
+		if (keyboard_protocol->modifiers & BIT(i))
+			input_report_key(applespi->keyboard_input_dev,
+					 applespi_controlcodes[i], 1);
+		else
+			input_report_key(applespi->keyboard_input_dev,
+					 applespi_controlcodes[i], 0);
+	}
+
+	/* check function key */
+	if (keyboard_protocol->fn_pressed && !applespi->last_fn_pressed)
+		input_report_key(applespi->keyboard_input_dev, KEY_FN, 1);
+	else if (!keyboard_protocol->fn_pressed && applespi->last_fn_pressed)
+		input_report_key(applespi->keyboard_input_dev, KEY_FN, 0);
+	applespi->last_fn_pressed = keyboard_protocol->fn_pressed;
+
+	/* done */
+	input_sync(applespi->keyboard_input_dev);
+	memcpy(&applespi->last_keys_pressed, keyboard_protocol->keys_pressed,
+	       sizeof(applespi->last_keys_pressed));
+}
+
+static const struct applespi_tp_info *applespi_find_touchpad_info(__u8 model)
+{
+	const struct applespi_tp_model_info *info;
+
+	for (info = applespi_tp_models; info->model; info++) {
+		if (info->model == model)
+			return &info->tp_info;
+	}
+
+	return NULL;
+}
+
+static int
+applespi_register_touchpad_device(struct applespi_data *applespi,
+				  struct touchpad_info_protocol *rcvd_tp_info)
+{
+	const struct applespi_tp_info *tp_info;
+	struct input_dev *touchpad_input_dev;
+	int sts;
+
+	/* set up touchpad dimensions */
+	tp_info = applespi_find_touchpad_info(rcvd_tp_info->model_no);
+	if (!tp_info) {
+		dev_warn(&applespi->spi->dev,
+			 "Unknown touchpad model %x - falling back to MB8 touchpad\n",
+			 rcvd_tp_info->model_no);
+		tp_info = &applespi_tp_models[0].tp_info;
+	}
+
+	applespi->tp_info = *tp_info;
+
+	if (touchpad_dimensions[0]) {
+		int x, y, w, h;
+
+		sts = sscanf(touchpad_dimensions, "%dx%d+%u+%u", &x, &y, &w, &h);
+		if (sts == 4) {
+			dev_info(&applespi->spi->dev,
+				 "Overriding touchpad dimensions from module param\n");
+			applespi->tp_info.x_min = x;
+			applespi->tp_info.y_min = y;
+			applespi->tp_info.x_max = x + w;
+			applespi->tp_info.y_max = y + h;
+		} else {
+			dev_warn(&applespi->spi->dev,
+				 "Invalid touchpad dimensions '%s': must be in the form XxY+W+H\n",
+				 touchpad_dimensions);
+			touchpad_dimensions[0] = '\0';
+		}
+	}
+	if (!touchpad_dimensions[0]) {
+		snprintf(touchpad_dimensions, sizeof(touchpad_dimensions),
+			 "%dx%d+%u+%u",
+			 applespi->tp_info.x_min,
+			 applespi->tp_info.y_min,
+			 applespi->tp_info.x_max - applespi->tp_info.x_min,
+			 applespi->tp_info.y_max - applespi->tp_info.y_min);
+	}
+
+	/* create touchpad input device */
+	touchpad_input_dev = devm_input_allocate_device(&applespi->spi->dev);
+	if (!touchpad_input_dev) {
+		dev_err(&applespi->spi->dev,
+			"Failed to allocate touchpad input device\n");
+		return -ENOMEM;
+	}
+
+	touchpad_input_dev->name = "Apple SPI Touchpad";
+	touchpad_input_dev->phys = "applespi/input1";
+	touchpad_input_dev->dev.parent = &applespi->spi->dev;
+	touchpad_input_dev->id.bustype = BUS_SPI;
+	touchpad_input_dev->id.vendor = SYNAPTICS_VENDOR_ID;
+	touchpad_input_dev->id.product =
+			rcvd_tp_info->model_no << 8 | rcvd_tp_info->model_flags;
+
+	/* basic properties */
+	input_set_capability(touchpad_input_dev, EV_REL, REL_X);
+	input_set_capability(touchpad_input_dev, EV_REL, REL_Y);
+
+	__set_bit(INPUT_PROP_POINTER, touchpad_input_dev->propbit);
+	__set_bit(INPUT_PROP_BUTTONPAD, touchpad_input_dev->propbit);
+
+	/* finger touch area */
+	input_set_abs_params(touchpad_input_dev, ABS_MT_TOUCH_MAJOR,
+			     0, 5000, 0, 0);
+	input_set_abs_params(touchpad_input_dev, ABS_MT_TOUCH_MINOR,
+			     0, 5000, 0, 0);
+
+	/* finger approach area */
+	input_set_abs_params(touchpad_input_dev, ABS_MT_WIDTH_MAJOR,
+			     0, 5000, 0, 0);
+	input_set_abs_params(touchpad_input_dev, ABS_MT_WIDTH_MINOR,
+			     0, 5000, 0, 0);
+
+	/* finger orientation */
+	input_set_abs_params(touchpad_input_dev, ABS_MT_ORIENTATION,
+			     -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION,
+			     0, 0);
+
+	/* finger position */
+	input_set_abs_params(touchpad_input_dev, ABS_MT_POSITION_X,
+			     applespi->tp_info.x_min, applespi->tp_info.x_max,
+			     0, 0);
+	input_set_abs_params(touchpad_input_dev, ABS_MT_POSITION_Y,
+			     applespi->tp_info.y_min, applespi->tp_info.y_max,
+			     0, 0);
+
+	/* touchpad button */
+	input_set_capability(touchpad_input_dev, EV_KEY, BTN_LEFT);
+
+	/* multitouch */
+	input_mt_init_slots(touchpad_input_dev, MAX_FINGERS,
+			    INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED |
+			    INPUT_MT_TRACK);
+
+	/* register input device */
+	sts = input_register_device(touchpad_input_dev);
+	if (sts) {
+		dev_err(&applespi->spi->dev,
+			"Unable to register touchpad input device (%d)\n", sts);
+		return sts;
+	}
+
+	/* touchpad_input_dev is read async in spi callback */
+	smp_store_release(&applespi->touchpad_input_dev, touchpad_input_dev);
+
+	return 0;
+}
+
+static void applespi_worker(struct work_struct *work)
+{
+	struct applespi_data *applespi =
+		container_of(work, struct applespi_data, work);
+
+	applespi_register_touchpad_device(applespi, &applespi->rcvd_tp_info);
+}
+
+static void applespi_handle_cmd_response(struct applespi_data *applespi,
+					 struct spi_packet *packet,
+					 struct message *message)
+{
+	if (packet->device == PACKET_DEV_INFO &&
+	    le16_to_cpu(message->type) == 0x1020) {
+		/*
+		 * We're not allowed to sleep here, but registering an input
+		 * device can sleep.
+		 */
+		applespi->rcvd_tp_info = message->tp_info;
+		schedule_work(&applespi->work);
+		return;
+	}
+
+	if (le16_to_cpu(message->length) != 0x0000) {
+		dev_warn_ratelimited(&applespi->spi->dev,
+				     "Received unexpected write response: length=%x\n",
+				     le16_to_cpu(message->length));
+		return;
+	}
+
+	if (packet->device == PACKET_DEV_TPAD &&
+	    le16_to_cpu(message->type) == 0x0252 &&
+	    le16_to_cpu(message->rsp_buf_len) == 0x0002)
+		dev_info(&applespi->spi->dev, "modeswitch done.\n");
+}
+
+static bool applespi_verify_crc(struct applespi_data *applespi, u8 *buffer,
+				size_t buflen)
+{
+	u16 crc;
+
+	crc = crc16(0, buffer, buflen);
+	if (crc) {
+		dev_warn_ratelimited(&applespi->spi->dev,
+				     "Received corrupted packet (crc mismatch)\n");
+		trace_applespi_bad_crc(ET_RD_CRC, READ, buffer, buflen);
+
+		return false;
+	}
+
+	return true;
+}
+
+static void applespi_debug_print_read_packet(struct applespi_data *applespi,
+					     struct spi_packet *packet)
+{
+	unsigned int evt_type;
+
+	if (packet->flags == PACKET_TYPE_READ &&
+	    packet->device == PACKET_DEV_KEYB)
+		evt_type = ET_RD_KEYB;
+	else if (packet->flags == PACKET_TYPE_READ &&
+		 packet->device == PACKET_DEV_TPAD)
+		evt_type = ET_RD_TPAD;
+	else if (packet->flags == PACKET_TYPE_WRITE)
+		evt_type = applespi->cmd_evt_type;
+	else
+		evt_type = ET_RD_UNKN;
+
+	applespi_get_trace_fun(evt_type)(evt_type, PT_READ, applespi->rx_buffer,
+					 APPLESPI_PACKET_SIZE);
+}
+
+static void applespi_got_data(struct applespi_data *applespi)
+{
+	struct spi_packet *packet;
+	struct message *message;
+	unsigned int msg_len;
+	unsigned int off;
+	unsigned int rem;
+	unsigned int len;
+
+	/* process packet header */
+	if (!applespi_verify_crc(applespi, applespi->rx_buffer,
+				 APPLESPI_PACKET_SIZE)) {
+		unsigned long flags;
+
+		spin_lock_irqsave(&applespi->cmd_msg_lock, flags);
+
+		if (applespi->drain) {
+			applespi->read_active = false;
+			applespi->write_active = false;
+
+			wake_up_all(&applespi->drain_complete);
+		}
+
+		spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags);
+
+		return;
+	}
+
+	packet = (struct spi_packet *)applespi->rx_buffer;
+
+	applespi_debug_print_read_packet(applespi, packet);
+
+	off = le16_to_cpu(packet->offset);
+	rem = le16_to_cpu(packet->remaining);
+	len = le16_to_cpu(packet->length);
+
+	if (len > sizeof(packet->data)) {
+		dev_warn_ratelimited(&applespi->spi->dev,
+				     "Received corrupted packet (invalid packet length %u)\n",
+				     len);
+		goto msg_complete;
+	}
+
+	/* handle multi-packet messages */
+	if (rem > 0 || off > 0) {
+		if (off != applespi->saved_msg_len) {
+			dev_warn_ratelimited(&applespi->spi->dev,
+					     "Received unexpected offset (got %u, expected %u)\n",
+					     off, applespi->saved_msg_len);
+			goto msg_complete;
+		}
+
+		if (off + rem > MAX_PKTS_PER_MSG * APPLESPI_PACKET_SIZE) {
+			dev_warn_ratelimited(&applespi->spi->dev,
+					     "Received message too large (size %u)\n",
+					     off + rem);
+			goto msg_complete;
+		}
+
+		if (off + len > MAX_PKTS_PER_MSG * APPLESPI_PACKET_SIZE) {
+			dev_warn_ratelimited(&applespi->spi->dev,
+					     "Received message too large (size %u)\n",
+					     off + len);
+			goto msg_complete;
+		}
+
+		memcpy(applespi->msg_buf + off, &packet->data, len);
+		applespi->saved_msg_len += len;
+
+		if (rem > 0)
+			return;
+
+		message = (struct message *)applespi->msg_buf;
+		msg_len = applespi->saved_msg_len;
+	} else {
+		message = (struct message *)&packet->data;
+		msg_len = len;
+	}
+
+	/* got complete message - verify */
+	if (!applespi_verify_crc(applespi, (u8 *)message, msg_len))
+		goto msg_complete;
+
+	if (le16_to_cpu(message->length) != msg_len - MSG_HEADER_SIZE - 2) {
+		dev_warn_ratelimited(&applespi->spi->dev,
+				     "Received corrupted packet (invalid message length %u - expected %u)\n",
+				     le16_to_cpu(message->length),
+				     msg_len - MSG_HEADER_SIZE - 2);
+		goto msg_complete;
+	}
+
+	/* handle message */
+	if (packet->flags == PACKET_TYPE_READ &&
+	    packet->device == PACKET_DEV_KEYB) {
+		applespi_handle_keyboard_event(applespi, &message->keyboard);
+
+	} else if (packet->flags == PACKET_TYPE_READ &&
+		   packet->device == PACKET_DEV_TPAD) {
+		struct touchpad_protocol *tp;
+		size_t tp_len;
+
+		tp = &message->touchpad;
+		tp_len = sizeof(*tp) +
+			 tp->number_of_fingers * sizeof(tp->fingers[0]);
+
+		if (le16_to_cpu(message->length) + 2 != tp_len) {
+			dev_warn_ratelimited(&applespi->spi->dev,
+					     "Received corrupted packet (invalid message length %u - num-fingers %u, tp-len %zu)\n",
+					     le16_to_cpu(message->length),
+					     tp->number_of_fingers, tp_len);
+			goto msg_complete;
+		}
+
+		if (tp->number_of_fingers > MAX_FINGERS) {
+			dev_warn_ratelimited(&applespi->spi->dev,
+					     "Number of reported fingers (%u) exceeds max (%u))\n",
+					     tp->number_of_fingers,
+					     MAX_FINGERS);
+			tp->number_of_fingers = MAX_FINGERS;
+		}
+
+		report_tp_state(applespi, tp);
+
+	} else if (packet->flags == PACKET_TYPE_WRITE) {
+		applespi_handle_cmd_response(applespi, packet, message);
+	}
+
+msg_complete:
+	applespi->saved_msg_len = 0;
+
+	applespi_msg_complete(applespi, packet->flags == PACKET_TYPE_WRITE,
+			      true);
+}
+
+static void applespi_async_read_complete(void *context)
+{
+	struct applespi_data *applespi = context;
+
+	if (applespi->rd_m.status < 0) {
+		dev_warn(&applespi->spi->dev, "Error reading from device: %d\n",
+			 applespi->rd_m.status);
+		/*
+		 * We don't actually know if this was a pure read, or a response
+		 * to a write. But this is a rare error condition that should
+		 * never occur, so clearing both flags to avoid deadlock.
+		 */
+		applespi_msg_complete(applespi, true, true);
+	} else {
+		applespi_got_data(applespi);
+	}
+
+	acpi_finish_gpe(NULL, applespi->gpe);
+}
+
+static u32 applespi_notify(acpi_handle gpe_device, u32 gpe, void *context)
+{
+	struct applespi_data *applespi = context;
+	int sts;
+	unsigned long flags;
+
+	trace_applespi_irq_received(ET_RD_IRQ, PT_READ);
+
+	spin_lock_irqsave(&applespi->cmd_msg_lock, flags);
+
+	if (!applespi->suspended) {
+		sts = applespi_async(applespi, &applespi->rd_m,
+				     applespi_async_read_complete);
+		if (sts)
+			dev_warn(&applespi->spi->dev,
+				 "Error queueing async read to device: %d\n",
+				 sts);
+		else
+			applespi->read_active = true;
+	}
+
+	spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags);
+
+	return ACPI_INTERRUPT_HANDLED;
+}
+
+static int applespi_get_saved_bl_level(struct applespi_data *applespi)
+{
+	struct efivar_entry *efivar_entry;
+	u16 efi_data = 0;
+	unsigned long efi_data_len;
+	int sts;
+
+	efivar_entry = kmalloc(sizeof(*efivar_entry), GFP_KERNEL);
+	if (!efivar_entry)
+		return -ENOMEM;
+
+	memcpy(efivar_entry->var.VariableName, EFI_BL_LEVEL_NAME,
+	       sizeof(EFI_BL_LEVEL_NAME));
+	efivar_entry->var.VendorGuid = EFI_BL_LEVEL_GUID;
+	efi_data_len = sizeof(efi_data);
+
+	sts = efivar_entry_get(efivar_entry, NULL, &efi_data_len, &efi_data);
+	if (sts && sts != -ENOENT)
+		dev_warn(&applespi->spi->dev,
+			 "Error getting backlight level from EFI vars: %d\n",
+			 sts);
+
+	kfree(efivar_entry);
+
+	return sts ? sts : efi_data;
+}
+
+static void applespi_save_bl_level(struct applespi_data *applespi,
+				   unsigned int level)
+{
+	efi_guid_t efi_guid;
+	u32 efi_attr;
+	unsigned long efi_data_len;
+	u16 efi_data;
+	int sts;
+
+	/* Save keyboard backlight level */
+	efi_guid = EFI_BL_LEVEL_GUID;
+	efi_data = (u16)level;
+	efi_data_len = sizeof(efi_data);
+	efi_attr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS |
+		   EFI_VARIABLE_RUNTIME_ACCESS;
+
+	sts = efivar_entry_set_safe(EFI_BL_LEVEL_NAME, efi_guid, efi_attr, true,
+				    efi_data_len, &efi_data);
+	if (sts)
+		dev_warn(&applespi->spi->dev,
+			 "Error saving backlight level to EFI vars: %d\n", sts);
+}
+
+static int applespi_probe(struct spi_device *spi)
+{
+	struct applespi_data *applespi;
+	acpi_handle spi_handle = ACPI_HANDLE(&spi->dev);
+	acpi_status acpi_sts;
+	int sts, i;
+	unsigned long long gpe, usb_status;
+
+	/* check if the USB interface is present and enabled already */
+	acpi_sts = acpi_evaluate_integer(spi_handle, "UIST", NULL, &usb_status);
+	if (ACPI_SUCCESS(acpi_sts) && usb_status) {
+		/* let the USB driver take over instead */
+		dev_info(&spi->dev, "USB interface already enabled\n");
+		return -ENODEV;
+	}
+
+	/* allocate driver data */
+	applespi = devm_kzalloc(&spi->dev, sizeof(*applespi), GFP_KERNEL);
+	if (!applespi)
+		return -ENOMEM;
+
+	applespi->spi = spi;
+
+	INIT_WORK(&applespi->work, applespi_worker);
+
+	/* store the driver data */
+	spi_set_drvdata(spi, applespi);
+
+	/* create our buffers */
+	applespi->tx_buffer = devm_kmalloc(&spi->dev, APPLESPI_PACKET_SIZE,
+					   GFP_KERNEL);
+	applespi->tx_status = devm_kmalloc(&spi->dev, APPLESPI_STATUS_SIZE,
+					   GFP_KERNEL);
+	applespi->rx_buffer = devm_kmalloc(&spi->dev, APPLESPI_PACKET_SIZE,
+					   GFP_KERNEL);
+	applespi->msg_buf = devm_kmalloc_array(&spi->dev, MAX_PKTS_PER_MSG,
+					       APPLESPI_PACKET_SIZE,
+					       GFP_KERNEL);
+
+	if (!applespi->tx_buffer || !applespi->tx_status ||
+	    !applespi->rx_buffer || !applespi->msg_buf)
+		return -ENOMEM;
+
+	/* set up our spi messages */
+	applespi_setup_read_txfrs(applespi);
+	applespi_setup_write_txfrs(applespi);
+
+	/* cache ACPI method handles */
+	acpi_sts = acpi_get_handle(spi_handle, "SIEN", &applespi->sien);
+	if (ACPI_FAILURE(acpi_sts)) {
+		dev_err(&applespi->spi->dev,
+			"Failed to get SIEN ACPI method handle: %s\n",
+			acpi_format_exception(acpi_sts));
+		return -ENODEV;
+	}
+
+	acpi_sts = acpi_get_handle(spi_handle, "SIST", &applespi->sist);
+	if (ACPI_FAILURE(acpi_sts)) {
+		dev_err(&applespi->spi->dev,
+			"Failed to get SIST ACPI method handle: %s\n",
+			acpi_format_exception(acpi_sts));
+		return -ENODEV;
+	}
+
+	/* switch on the SPI interface */
+	sts = applespi_setup_spi(applespi);
+	if (sts)
+		return sts;
+
+	sts = applespi_enable_spi(applespi);
+	if (sts)
+		return sts;
+
+	/* setup the keyboard input dev */
+	applespi->keyboard_input_dev = devm_input_allocate_device(&spi->dev);
+
+	if (!applespi->keyboard_input_dev)
+		return -ENOMEM;
+
+	applespi->keyboard_input_dev->name = "Apple SPI Keyboard";
+	applespi->keyboard_input_dev->phys = "applespi/input0";
+	applespi->keyboard_input_dev->dev.parent = &spi->dev;
+	applespi->keyboard_input_dev->id.bustype = BUS_SPI;
+
+	applespi->keyboard_input_dev->evbit[0] =
+			BIT_MASK(EV_KEY) | BIT_MASK(EV_LED) | BIT_MASK(EV_REP);
+	applespi->keyboard_input_dev->ledbit[0] = BIT_MASK(LED_CAPSL);
+
+	input_set_drvdata(applespi->keyboard_input_dev, applespi);
+	applespi->keyboard_input_dev->event = applespi_event;
+
+	for (i = 0; i < ARRAY_SIZE(applespi_scancodes); i++)
+		if (applespi_scancodes[i])
+			input_set_capability(applespi->keyboard_input_dev,
+					     EV_KEY, applespi_scancodes[i]);
+
+	for (i = 0; i < ARRAY_SIZE(applespi_controlcodes); i++)
+		if (applespi_controlcodes[i])
+			input_set_capability(applespi->keyboard_input_dev,
+					     EV_KEY, applespi_controlcodes[i]);
+
+	for (i = 0; i < ARRAY_SIZE(applespi_fn_codes); i++)
+		if (applespi_fn_codes[i].to)
+			input_set_capability(applespi->keyboard_input_dev,
+					     EV_KEY, applespi_fn_codes[i].to);
+
+	input_set_capability(applespi->keyboard_input_dev, EV_KEY, KEY_FN);
+
+	sts = input_register_device(applespi->keyboard_input_dev);
+	if (sts) {
+		dev_err(&applespi->spi->dev,
+			"Unable to register keyboard input device (%d)\n", sts);
+		return -ENODEV;
+	}
+
+	/*
+	 * The applespi device doesn't send interrupts normally (as is described
+	 * in its DSDT), but rather seems to use ACPI GPEs.
+	 */
+	acpi_sts = acpi_evaluate_integer(spi_handle, "_GPE", NULL, &gpe);
+	if (ACPI_FAILURE(acpi_sts)) {
+		dev_err(&applespi->spi->dev,
+			"Failed to obtain GPE for SPI slave device: %s\n",
+			acpi_format_exception(acpi_sts));
+		return -ENODEV;
+	}
+	applespi->gpe = (int)gpe;
+
+	acpi_sts = acpi_install_gpe_handler(NULL, applespi->gpe,
+					    ACPI_GPE_LEVEL_TRIGGERED,
+					    applespi_notify, applespi);
+	if (ACPI_FAILURE(acpi_sts)) {
+		dev_err(&applespi->spi->dev,
+			"Failed to install GPE handler for GPE %d: %s\n",
+			applespi->gpe, acpi_format_exception(acpi_sts));
+		return -ENODEV;
+	}
+
+	applespi->suspended = false;
+
+	acpi_sts = acpi_enable_gpe(NULL, applespi->gpe);
+	if (ACPI_FAILURE(acpi_sts)) {
+		dev_err(&applespi->spi->dev,
+			"Failed to enable GPE handler for GPE %d: %s\n",
+			applespi->gpe, acpi_format_exception(acpi_sts));
+		acpi_remove_gpe_handler(NULL, applespi->gpe, applespi_notify);
+		return -ENODEV;
+	}
+
+	/* trigger touchpad setup */
+	applespi_init(applespi, false);
+
+	/*
+	 * By default this device is not enabled for wakeup; but USB keyboards
+	 * generally are, so the expectation is that by default the keyboard
+	 * will wake the system.
+	 */
+	device_wakeup_enable(&spi->dev);
+
+	/* set up keyboard-backlight */
+	sts = applespi_get_saved_bl_level(applespi);
+	if (sts >= 0)
+		applespi_set_bl_level(&applespi->backlight_info, sts);
+
+	applespi->backlight_info.name            = "spi::kbd_backlight";
+	applespi->backlight_info.default_trigger = "kbd-backlight";
+	applespi->backlight_info.brightness_set  = applespi_set_bl_level;
+
+	sts = devm_led_classdev_register(&spi->dev, &applespi->backlight_info);
+	if (sts)
+		dev_warn(&applespi->spi->dev,
+			 "Unable to register keyboard backlight class dev (%d)\n",
+			 sts);
+
+	/* set up debugfs entries for touchpad dimensions logging */
+	applespi->debugfs_root = debugfs_create_dir("applespi", NULL);
+	if (IS_ERR(applespi->debugfs_root)) {
+		if (PTR_ERR(applespi->debugfs_root) != -ENODEV)
+			dev_warn(&applespi->spi->dev,
+				 "Error creating debugfs root entry (%ld)\n",
+				 PTR_ERR(applespi->debugfs_root));
+	} else {
+		struct dentry *ret;
+
+		ret = debugfs_create_bool("enable_tp_dim", 0600,
+					  applespi->debugfs_root,
+					  &applespi->debug_tp_dim);
+		if (IS_ERR(ret))
+			dev_dbg(&applespi->spi->dev,
+				"Error creating debugfs entry enable_tp_dim (%ld)\n",
+				PTR_ERR(ret));
+
+		ret = debugfs_create_file("tp_dim", 0400,
+					  applespi->debugfs_root, applespi,
+					  &applespi_tp_dim_fops);
+		if (IS_ERR(ret))
+			dev_dbg(&applespi->spi->dev,
+				"Error creating debugfs entry tp_dim (%ld)\n",
+				PTR_ERR(ret));
+	}
+
+	return 0;
+}
+
+static void applespi_drain_writes(struct applespi_data *applespi)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&applespi->cmd_msg_lock, flags);
+
+	applespi->drain = true;
+	wait_event_lock_irq(applespi->drain_complete, !applespi->write_active,
+			    applespi->cmd_msg_lock);
+
+	spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags);
+}
+
+static void applespi_drain_reads(struct applespi_data *applespi)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&applespi->cmd_msg_lock, flags);
+
+	wait_event_lock_irq(applespi->drain_complete, !applespi->read_active,
+			    applespi->cmd_msg_lock);
+
+	applespi->suspended = true;
+
+	spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags);
+}
+
+static int applespi_remove(struct spi_device *spi)
+{
+	struct applespi_data *applespi = spi_get_drvdata(spi);
+
+	applespi_drain_writes(applespi);
+
+	acpi_disable_gpe(NULL, applespi->gpe);
+	acpi_remove_gpe_handler(NULL, applespi->gpe, applespi_notify);
+	device_wakeup_disable(&spi->dev);
+
+	applespi_drain_reads(applespi);
+
+	debugfs_remove_recursive(applespi->debugfs_root);
+
+	return 0;
+}
+
+static void applespi_shutdown(struct spi_device *spi)
+{
+	struct applespi_data *applespi = spi_get_drvdata(spi);
+
+	applespi_save_bl_level(applespi, applespi->have_bl_level);
+}
+
+static int applespi_poweroff_late(struct device *dev)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	struct applespi_data *applespi = spi_get_drvdata(spi);
+
+	applespi_save_bl_level(applespi, applespi->have_bl_level);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int applespi_suspend(struct device *dev)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	struct applespi_data *applespi = spi_get_drvdata(spi);
+	acpi_status acpi_sts;
+	int sts;
+
+	/* turn off caps-lock - it'll stay on otherwise */
+	sts = applespi_set_capsl_led(applespi, false);
+	if (sts)
+		dev_warn(&applespi->spi->dev,
+			 "Failed to turn off caps-lock led (%d)\n", sts);
+
+	applespi_drain_writes(applespi);
+
+	/* disable the interrupt */
+	acpi_sts = acpi_disable_gpe(NULL, applespi->gpe);
+	if (ACPI_FAILURE(acpi_sts))
+		dev_err(&applespi->spi->dev,
+			"Failed to disable GPE handler for GPE %d: %s\n",
+			applespi->gpe, acpi_format_exception(acpi_sts));
+
+	applespi_drain_reads(applespi);
+
+	return 0;
+}
+
+static int applespi_resume(struct device *dev)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	struct applespi_data *applespi = spi_get_drvdata(spi);
+	acpi_status acpi_sts;
+	unsigned long flags;
+
+	/* ensure our flags and state reflect a newly resumed device */
+	spin_lock_irqsave(&applespi->cmd_msg_lock, flags);
+
+	applespi->drain = false;
+	applespi->have_cl_led_on = false;
+	applespi->have_bl_level = 0;
+	applespi->cmd_msg_queued = false;
+	applespi->read_active = false;
+	applespi->write_active = false;
+
+	applespi->suspended = false;
+
+	spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags);
+
+	/* switch on the SPI interface */
+	applespi_enable_spi(applespi);
+
+	/* re-enable the interrupt */
+	acpi_sts = acpi_enable_gpe(NULL, applespi->gpe);
+	if (ACPI_FAILURE(acpi_sts))
+		dev_err(&applespi->spi->dev,
+			"Failed to re-enable GPE handler for GPE %d: %s\n",
+			applespi->gpe, acpi_format_exception(acpi_sts));
+
+	/* switch the touchpad into multitouch mode */
+	applespi_init(applespi, true);
+
+	return 0;
+}
+#endif
+
+static const struct acpi_device_id applespi_acpi_match[] = {
+	{ "APP000D", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(acpi, applespi_acpi_match);
+
+const struct dev_pm_ops applespi_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(applespi_suspend, applespi_resume)
+	.poweroff_late	= applespi_poweroff_late,
+};
+
+static struct spi_driver applespi_driver = {
+	.driver		= {
+		.name			= "applespi",
+		.acpi_match_table	= applespi_acpi_match,
+		.pm			= &applespi_pm_ops,
+	},
+	.probe		= applespi_probe,
+	.remove		= applespi_remove,
+	.shutdown	= applespi_shutdown,
+};
+
+module_spi_driver(applespi_driver)
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MacBook(Pro) SPI Keyboard/Touchpad driver");
+MODULE_AUTHOR("Federico Lorenzi");
+MODULE_AUTHOR("Ronald Tschalär");
diff --git a/drivers/input/keyboard/applespi.h b/drivers/input/keyboard/applespi.h
new file mode 100644
index 000000000000..7f5ab10c597a
--- /dev/null
+++ b/drivers/input/keyboard/applespi.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * MacBook (Pro) SPI keyboard and touchpad driver
+ *
+ * Copyright (c) 2015-2019 Federico Lorenzi
+ * Copyright (c) 2017-2019 Ronald Tschalär
+ */
+
+#ifndef _APPLESPI_H_
+#define _APPLESPI_H_
+
+enum applespi_evt_type {
+	ET_CMD_TP_INI = BIT(0),
+	ET_CMD_BL = BIT(1),
+	ET_CMD_CL = BIT(2),
+	ET_RD_KEYB = BIT(8),
+	ET_RD_TPAD = BIT(9),
+	ET_RD_UNKN = BIT(10),
+	ET_RD_IRQ = BIT(11),
+	ET_RD_CRC = BIT(12),
+};
+
+enum applespi_pkt_type {
+	PT_READ,
+	PT_WRITE,
+	PT_STATUS,
+};
+
+#endif /* _APPLESPI_H_ */
diff --git a/drivers/input/keyboard/applespi_trace.h b/drivers/input/keyboard/applespi_trace.h
new file mode 100644
index 000000000000..5e965e1974c7
--- /dev/null
+++ b/drivers/input/keyboard/applespi_trace.h
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * MacBook (Pro) SPI keyboard and touchpad driver
+ *
+ * Copyright (c) 2015-2019 Federico Lorenzi
+ * Copyright (c) 2017-2019 Ronald Tschalär
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM applespi
+
+#if !defined(_APPLESPI_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ)
+#define _APPLESPI_TRACE_H_
+
+#include <linux/types.h>
+#include <linux/tracepoint.h>
+
+#include "applespi.h"
+
+DECLARE_EVENT_CLASS(dump_message_template,
+	TP_PROTO(enum applespi_evt_type evt_type,
+		 enum applespi_pkt_type pkt_type,
+		 u8 *buf,
+		 size_t len),
+
+	TP_ARGS(evt_type, pkt_type, buf, len),
+
+	TP_STRUCT__entry(
+		__field(enum applespi_evt_type, evt_type)
+		__field(enum applespi_pkt_type, pkt_type)
+		__field(size_t, len)
+		__dynamic_array(u8, buf, len)
+	),
+
+	TP_fast_assign(
+		__entry->evt_type = evt_type;
+		__entry->pkt_type = pkt_type;
+		__entry->len = len;
+		memcpy(__get_dynamic_array(buf), buf, len);
+	),
+
+	TP_printk("%-6s: %s",
+		  __print_symbolic(__entry->pkt_type,
+				   { PT_READ, "read" },
+				   { PT_WRITE, "write" },
+				   { PT_STATUS, "status" }
+		  ),
+		  __print_hex(__get_dynamic_array(buf), __entry->len))
+);
+
+#define DEFINE_DUMP_MESSAGE_EVENT(name)			\
+DEFINE_EVENT(dump_message_template, name,		\
+	TP_PROTO(enum applespi_evt_type evt_type,	\
+		 enum applespi_pkt_type pkt_type,	\
+		 u8 *buf,				\
+		 size_t len),				\
+	TP_ARGS(evt_type, pkt_type, buf, len)		\
+)
+
+DEFINE_DUMP_MESSAGE_EVENT(applespi_tp_ini_cmd);
+DEFINE_DUMP_MESSAGE_EVENT(applespi_backlight_cmd);
+DEFINE_DUMP_MESSAGE_EVENT(applespi_caps_lock_cmd);
+DEFINE_DUMP_MESSAGE_EVENT(applespi_keyboard_data);
+DEFINE_DUMP_MESSAGE_EVENT(applespi_touchpad_data);
+DEFINE_DUMP_MESSAGE_EVENT(applespi_unknown_data);
+DEFINE_DUMP_MESSAGE_EVENT(applespi_bad_crc);
+
+TRACE_EVENT(applespi_irq_received,
+	TP_PROTO(enum applespi_evt_type evt_type,
+		 enum applespi_pkt_type pkt_type),
+
+	TP_ARGS(evt_type, pkt_type),
+
+	TP_STRUCT__entry(
+		__field(enum applespi_evt_type, evt_type)
+		__field(enum applespi_pkt_type, pkt_type)
+	),
+
+	TP_fast_assign(
+		__entry->evt_type = evt_type;
+		__entry->pkt_type = pkt_type;
+	),
+
+	"\n"
+);
+
+#endif /* _APPLESPI_TRACE_H_ */
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH ../../drivers/input/keyboard
+#define TRACE_INCLUDE_FILE applespi_trace
+#include <trace/define_trace.h>
+
-- 
2.20.1

^ permalink raw reply related

* Re: [PATCH v2] Input: uinput: Avoid Object-Already-Free with a global lock
From: Mukesh Ojha @ 2019-04-19  8:43 UTC (permalink / raw)
  To: dmitry.torokhov@gmail.com
  Cc: linux-input, linux-kernel, Gaurav Kohli, Peter Hutterer,
	Martin Kepplinger, Paul E. McKenney
In-Reply-To: <20190419071152.x5ghvbybjhv76uxt@penguin>


On 4/19/2019 12:41 PM, dmitry.torokhov@gmail.com wrote:
> Hi Mukesh,
>
> On Fri, Apr 19, 2019 at 12:17:44PM +0530, Mukesh Ojha wrote:
>> For some reason my last mail did not get delivered,  sending it again.
>>
>>
>> On 4/18/2019 11:55 AM, Mukesh Ojha wrote:
>>>
>>> On 4/18/2019 7:13 AM, dmitry.torokhov@gmail.com wrote:
>>>> Hi Mukesh,
>>>>
>>>> On Mon, Apr 15, 2019 at 03:35:51PM +0530, Mukesh Ojha wrote:
>>>>> Hi Dmitry,
>>>>>
>>>>> Can you please have a look at this patch ? as this seems to reproducing
>>>>> quite frequently
>>>>>
>>>>> Thanks,
>>>>> Mukesh
>>>>>
>>>>> On 4/10/2019 1:29 PM, Mukesh Ojha wrote:
>>>>>> uinput_destroy_device() gets called from two places. In one place,
>>>>>> uinput_ioctl_handler() where it is protected under a lock
>>>>>> udev->mutex but there is no protection on udev device from freeing
>>>>>> inside uinput_release().
>>>> uinput_release() should be called when last file handle to the uinput
>>>> instance is being dropped, so there should be no other users and thus we
>>>> can't be racing with anyone.
>>> Lets say an example where i am creating input device quite frequently
>>>
>>> [   97.836603] input: syz0 as /devices/virtual/input/input262
>>> [   97.845589] input: syz0 as /devices/virtual/input/input261
>>> [   97.849415] input: syz0 as /devices/virtual/input/input263
>>> [   97.856479] input: syz0 as /devices/virtual/input/input264
>>> [   97.936128] input: syz0 as /devices/virtual/input/input265
>>>
>>> e.g input265
>>>
>>> while input265 gets created [1] and handlers are getting registered on
>>> that device*fput* gets called on
>>> that device as user space got to know that input265 is created and its
>>> reference is still 1(rare but possible).
> Are you saying that there are 2 threads sharing the same file
> descriptor, one issuing the registration ioctl while the other closing
> the same fd?

Dmitry,

I don't have a the exact look inside the app here, but this looks like 
the same as it is able to do
fput on the uinput device.

FYI
Syskaller app is running in userspace (which is for syscall fuzzing) on 
kernel which is enabled
with various config fault injection, FAULT_INJECTION,FAIL_SLAB, 
FAIL_PAGEALLOC, KASAN etc.

Thanks,
Mukesh

>
> Thanks.
>

^ permalink raw reply

* Re: [git pull] Input updates for v5.1-rc5
From: pr-tracker-bot @ 2019-04-19 19:10 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: Linus Torvalds, linux-kernel, linux-input
In-Reply-To: <20190419073157.epllrynbv5ww4a7r@penguin>

The pull request you sent on Fri, 19 Apr 2019 07:31:57 +0000:

> git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input.git for-linus

has been merged into torvalds/linux.git:
https://git.kernel.org/torvalds/c/240206fcab661afe9bed72e8704cef1d6e83e338

Thank you!

-- 
Deet-doot-dot, I am a bot.
https://korg.wiki.kernel.org/userdoc/prtracker

^ permalink raw reply

* Re: [PATCH v3 09/26] compat_ioctl: move drivers to compat_ptr_ioctl
From: Michael S. Tsirkin @ 2019-04-19 23:16 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Alexander Viro, linux-fsdevel, y2038, linux-kernel,
	Greg Kroah-Hartman, Jarkko Sakkinen, Jason Gunthorpe,
	Sudip Mukherjee, Peter Huewe, Jason Gunthorpe, Stefan Richter,
	Jiri Kosina, Benjamin Tissoires, Alexander Shishkin,
	Maxime Coquelin, Alexandre Torgue, Tomas Winkler,
	Artem Bityutskiy, Richard Weinberger
In-Reply-To: <20190416202013.4034148-10-arnd@arndb.de>

On Tue, Apr 16, 2019 at 10:19:47PM +0200, Arnd Bergmann wrote:
> Each of these drivers has a copy of the same trivial helper function to
> convert the pointer argument and then call the native ioctl handler.
> 
> We now have a generic implementation of that, so use it.
> 
> Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> Reviewed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
> Reviewed-by: Jason Gunthorpe <jgg@mellanox.com>
> Signed-off-by: Arnd Bergmann <arnd@arndb.de>

Acked-by: Michael S. Tsirkin <mst@redhat.com>

> ---
>  drivers/char/ppdev.c              | 12 +---------
>  drivers/char/tpm/tpm_vtpm_proxy.c | 12 +---------
>  drivers/firewire/core-cdev.c      | 12 +---------
>  drivers/hid/usbhid/hiddev.c       | 11 +--------
>  drivers/hwtracing/stm/core.c      | 12 +---------
>  drivers/misc/mei/main.c           | 22 +----------------
>  drivers/mtd/ubi/cdev.c            | 36 +++-------------------------
>  drivers/net/tap.c                 | 12 +---------
>  drivers/staging/pi433/pi433_if.c  | 12 +---------
>  drivers/usb/core/devio.c          | 16 +------------
>  drivers/vfio/vfio.c               | 39 +++----------------------------
>  drivers/vhost/net.c               | 12 +---------
>  drivers/vhost/scsi.c              | 12 +---------
>  drivers/vhost/test.c              | 12 +---------
>  drivers/vhost/vsock.c             | 12 +---------
>  fs/fat/file.c                     | 13 +----------
>  16 files changed, 20 insertions(+), 237 deletions(-)
> 
> diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c
> index 1ae77b41050a..e96c8d9623e0 100644
> --- a/drivers/char/ppdev.c
> +++ b/drivers/char/ppdev.c
> @@ -674,14 +674,6 @@ static long pp_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
>  	return ret;
>  }
>  
> -#ifdef CONFIG_COMPAT
> -static long pp_compat_ioctl(struct file *file, unsigned int cmd,
> -			    unsigned long arg)
> -{
> -	return pp_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
> -}
> -#endif
> -
>  static int pp_open(struct inode *inode, struct file *file)
>  {
>  	unsigned int minor = iminor(inode);
> @@ -790,9 +782,7 @@ static const struct file_operations pp_fops = {
>  	.write		= pp_write,
>  	.poll		= pp_poll,
>  	.unlocked_ioctl	= pp_ioctl,
> -#ifdef CONFIG_COMPAT
> -	.compat_ioctl   = pp_compat_ioctl,
> -#endif
> +	.compat_ioctl   = compat_ptr_ioctl,
>  	.open		= pp_open,
>  	.release	= pp_release,
>  };
> diff --git a/drivers/char/tpm/tpm_vtpm_proxy.c b/drivers/char/tpm/tpm_vtpm_proxy.c
> index d74f3de74ae6..fb845f0a430b 100644
> --- a/drivers/char/tpm/tpm_vtpm_proxy.c
> +++ b/drivers/char/tpm/tpm_vtpm_proxy.c
> @@ -675,20 +675,10 @@ static long vtpmx_fops_ioctl(struct file *f, unsigned int ioctl,
>  	}
>  }
>  
> -#ifdef CONFIG_COMPAT
> -static long vtpmx_fops_compat_ioctl(struct file *f, unsigned int ioctl,
> -					  unsigned long arg)
> -{
> -	return vtpmx_fops_ioctl(f, ioctl, (unsigned long)compat_ptr(arg));
> -}
> -#endif
> -
>  static const struct file_operations vtpmx_fops = {
>  	.owner = THIS_MODULE,
>  	.unlocked_ioctl = vtpmx_fops_ioctl,
> -#ifdef CONFIG_COMPAT
> -	.compat_ioctl = vtpmx_fops_compat_ioctl,
> -#endif
> +	.compat_ioctl = compat_ptr_ioctl,
>  	.llseek = noop_llseek,
>  };
>  
> diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c
> index 16a7045736a9..fb934680fdd3 100644
> --- a/drivers/firewire/core-cdev.c
> +++ b/drivers/firewire/core-cdev.c
> @@ -1659,14 +1659,6 @@ static long fw_device_op_ioctl(struct file *file,
>  	return dispatch_ioctl(file->private_data, cmd, (void __user *)arg);
>  }
>  
> -#ifdef CONFIG_COMPAT
> -static long fw_device_op_compat_ioctl(struct file *file,
> -				      unsigned int cmd, unsigned long arg)
> -{
> -	return dispatch_ioctl(file->private_data, cmd, compat_ptr(arg));
> -}
> -#endif
> -
>  static int fw_device_op_mmap(struct file *file, struct vm_area_struct *vma)
>  {
>  	struct client *client = file->private_data;
> @@ -1808,7 +1800,5 @@ const struct file_operations fw_device_ops = {
>  	.mmap		= fw_device_op_mmap,
>  	.release	= fw_device_op_release,
>  	.poll		= fw_device_op_poll,
> -#ifdef CONFIG_COMPAT
> -	.compat_ioctl	= fw_device_op_compat_ioctl,
> -#endif
> +	.compat_ioctl	= compat_ptr_ioctl,
>  };
> diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c
> index a746017fac17..ef4a1cd389d6 100644
> --- a/drivers/hid/usbhid/hiddev.c
> +++ b/drivers/hid/usbhid/hiddev.c
> @@ -855,13 +855,6 @@ static long hiddev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
>  	return r;
>  }
>  
> -#ifdef CONFIG_COMPAT
> -static long hiddev_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
> -{
> -	return hiddev_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
> -}
> -#endif
> -
>  static const struct file_operations hiddev_fops = {
>  	.owner =	THIS_MODULE,
>  	.read =		hiddev_read,
> @@ -871,9 +864,7 @@ static const struct file_operations hiddev_fops = {
>  	.release =	hiddev_release,
>  	.unlocked_ioctl =	hiddev_ioctl,
>  	.fasync =	hiddev_fasync,
> -#ifdef CONFIG_COMPAT
> -	.compat_ioctl	= hiddev_compat_ioctl,
> -#endif
> +	.compat_ioctl	= compat_ptr_ioctl,
>  	.llseek		= noop_llseek,
>  };
>  
> diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c
> index c7ba8acfd4d5..454da259f144 100644
> --- a/drivers/hwtracing/stm/core.c
> +++ b/drivers/hwtracing/stm/core.c
> @@ -840,23 +840,13 @@ stm_char_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
>  	return err;
>  }
>  
> -#ifdef CONFIG_COMPAT
> -static long
> -stm_char_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
> -{
> -	return stm_char_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
> -}
> -#else
> -#define stm_char_compat_ioctl	NULL
> -#endif
> -
>  static const struct file_operations stm_fops = {
>  	.open		= stm_char_open,
>  	.release	= stm_char_release,
>  	.write		= stm_char_write,
>  	.mmap		= stm_char_mmap,
>  	.unlocked_ioctl	= stm_char_ioctl,
> -	.compat_ioctl	= stm_char_compat_ioctl,
> +	.compat_ioctl	= compat_ptr_ioctl,
>  	.llseek		= no_llseek,
>  };
>  
> diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c
> index 87281b3695e6..cc6af92cdef0 100644
> --- a/drivers/misc/mei/main.c
> +++ b/drivers/misc/mei/main.c
> @@ -535,24 +535,6 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data)
>  	return rets;
>  }
>  
> -/**
> - * mei_compat_ioctl - the compat IOCTL function
> - *
> - * @file: pointer to file structure
> - * @cmd: ioctl command
> - * @data: pointer to mei message structure
> - *
> - * Return: 0 on success , <0 on error
> - */
> -#ifdef CONFIG_COMPAT
> -static long mei_compat_ioctl(struct file *file,
> -			unsigned int cmd, unsigned long data)
> -{
> -	return mei_ioctl(file, cmd, (unsigned long)compat_ptr(data));
> -}
> -#endif
> -
> -
>  /**
>   * mei_poll - the poll function
>   *
> @@ -855,9 +837,7 @@ static const struct file_operations mei_fops = {
>  	.owner = THIS_MODULE,
>  	.read = mei_read,
>  	.unlocked_ioctl = mei_ioctl,
> -#ifdef CONFIG_COMPAT
> -	.compat_ioctl = mei_compat_ioctl,
> -#endif
> +	.compat_ioctl = compat_ptr_ioctl,
>  	.open = mei_open,
>  	.release = mei_release,
>  	.write = mei_write,
> diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c
> index 947a8adbc799..265d34fa3efa 100644
> --- a/drivers/mtd/ubi/cdev.c
> +++ b/drivers/mtd/ubi/cdev.c
> @@ -1091,36 +1091,6 @@ static long ctrl_cdev_ioctl(struct file *file, unsigned int cmd,
>  	return err;
>  }
>  
> -#ifdef CONFIG_COMPAT
> -static long vol_cdev_compat_ioctl(struct file *file, unsigned int cmd,
> -				  unsigned long arg)
> -{
> -	unsigned long translated_arg = (unsigned long)compat_ptr(arg);
> -
> -	return vol_cdev_ioctl(file, cmd, translated_arg);
> -}
> -
> -static long ubi_cdev_compat_ioctl(struct file *file, unsigned int cmd,
> -				  unsigned long arg)
> -{
> -	unsigned long translated_arg = (unsigned long)compat_ptr(arg);
> -
> -	return ubi_cdev_ioctl(file, cmd, translated_arg);
> -}
> -
> -static long ctrl_cdev_compat_ioctl(struct file *file, unsigned int cmd,
> -				   unsigned long arg)
> -{
> -	unsigned long translated_arg = (unsigned long)compat_ptr(arg);
> -
> -	return ctrl_cdev_ioctl(file, cmd, translated_arg);
> -}
> -#else
> -#define vol_cdev_compat_ioctl  NULL
> -#define ubi_cdev_compat_ioctl  NULL
> -#define ctrl_cdev_compat_ioctl NULL
> -#endif
> -
>  /* UBI volume character device operations */
>  const struct file_operations ubi_vol_cdev_operations = {
>  	.owner          = THIS_MODULE,
> @@ -1131,7 +1101,7 @@ const struct file_operations ubi_vol_cdev_operations = {
>  	.write          = vol_cdev_write,
>  	.fsync		= vol_cdev_fsync,
>  	.unlocked_ioctl = vol_cdev_ioctl,
> -	.compat_ioctl   = vol_cdev_compat_ioctl,
> +	.compat_ioctl   = compat_ptr_ioctl,
>  };
>  
>  /* UBI character device operations */
> @@ -1139,13 +1109,13 @@ const struct file_operations ubi_cdev_operations = {
>  	.owner          = THIS_MODULE,
>  	.llseek         = no_llseek,
>  	.unlocked_ioctl = ubi_cdev_ioctl,
> -	.compat_ioctl   = ubi_cdev_compat_ioctl,
> +	.compat_ioctl   = compat_ptr_ioctl,
>  };
>  
>  /* UBI control character device operations */
>  const struct file_operations ubi_ctrl_cdev_operations = {
>  	.owner          = THIS_MODULE,
>  	.unlocked_ioctl = ctrl_cdev_ioctl,
> -	.compat_ioctl   = ctrl_cdev_compat_ioctl,
> +	.compat_ioctl   = compat_ptr_ioctl,
>  	.llseek		= no_llseek,
>  };
> diff --git a/drivers/net/tap.c b/drivers/net/tap.c
> index 2ea9b4976f4a..ebe425e65992 100644
> --- a/drivers/net/tap.c
> +++ b/drivers/net/tap.c
> @@ -1123,14 +1123,6 @@ static long tap_ioctl(struct file *file, unsigned int cmd,
>  	}
>  }
>  
> -#ifdef CONFIG_COMPAT
> -static long tap_compat_ioctl(struct file *file, unsigned int cmd,
> -			     unsigned long arg)
> -{
> -	return tap_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
> -}
> -#endif
> -
>  static const struct file_operations tap_fops = {
>  	.owner		= THIS_MODULE,
>  	.open		= tap_open,
> @@ -1140,9 +1132,7 @@ static const struct file_operations tap_fops = {
>  	.poll		= tap_poll,
>  	.llseek		= no_llseek,
>  	.unlocked_ioctl	= tap_ioctl,
> -#ifdef CONFIG_COMPAT
> -	.compat_ioctl	= tap_compat_ioctl,
> -#endif
> +	.compat_ioctl	= compat_ptr_ioctl,
>  };
>  
>  static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff *xdp)
> diff --git a/drivers/staging/pi433/pi433_if.c b/drivers/staging/pi433/pi433_if.c
> index b2314636dc89..ab7dfc7c2917 100644
> --- a/drivers/staging/pi433/pi433_if.c
> +++ b/drivers/staging/pi433/pi433_if.c
> @@ -935,16 +935,6 @@ pi433_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
>  	return retval;
>  }
>  
> -#ifdef CONFIG_COMPAT
> -static long
> -pi433_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
> -{
> -	return pi433_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));
> -}
> -#else
> -#define pi433_compat_ioctl NULL
> -#endif /* CONFIG_COMPAT */
> -
>  /*-------------------------------------------------------------------------*/
>  
>  static int pi433_open(struct inode *inode, struct file *filp)
> @@ -1101,7 +1091,7 @@ static const struct file_operations pi433_fops = {
>  	.write =	pi433_write,
>  	.read =		pi433_read,
>  	.unlocked_ioctl = pi433_ioctl,
> -	.compat_ioctl = pi433_compat_ioctl,
> +	.compat_ioctl = compat_ptr_ioctl,
>  	.open =		pi433_open,
>  	.release =	pi433_release,
>  	.llseek =	no_llseek,
> diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
> index fa783531ee88..d75052b36584 100644
> --- a/drivers/usb/core/devio.c
> +++ b/drivers/usb/core/devio.c
> @@ -2568,18 +2568,6 @@ static long usbdev_ioctl(struct file *file, unsigned int cmd,
>  	return ret;
>  }
>  
> -#ifdef CONFIG_COMPAT
> -static long usbdev_compat_ioctl(struct file *file, unsigned int cmd,
> -			unsigned long arg)
> -{
> -	int ret;
> -
> -	ret = usbdev_do_ioctl(file, cmd, compat_ptr(arg));
> -
> -	return ret;
> -}
> -#endif
> -
>  /* No kernel lock - fine */
>  static __poll_t usbdev_poll(struct file *file,
>  				struct poll_table_struct *wait)
> @@ -2603,9 +2591,7 @@ const struct file_operations usbdev_file_operations = {
>  	.read =		  usbdev_read,
>  	.poll =		  usbdev_poll,
>  	.unlocked_ioctl = usbdev_ioctl,
> -#ifdef CONFIG_COMPAT
> -	.compat_ioctl =   usbdev_compat_ioctl,
> -#endif
> +	.compat_ioctl =   compat_ptr_ioctl,
>  	.mmap =           usbdev_mmap,
>  	.open =		  usbdev_open,
>  	.release =	  usbdev_release,
> diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c
> index a3030cdf3c18..a5efe82584a5 100644
> --- a/drivers/vfio/vfio.c
> +++ b/drivers/vfio/vfio.c
> @@ -1200,15 +1200,6 @@ static long vfio_fops_unl_ioctl(struct file *filep,
>  	return ret;
>  }
>  
> -#ifdef CONFIG_COMPAT
> -static long vfio_fops_compat_ioctl(struct file *filep,
> -				   unsigned int cmd, unsigned long arg)
> -{
> -	arg = (unsigned long)compat_ptr(arg);
> -	return vfio_fops_unl_ioctl(filep, cmd, arg);
> -}
> -#endif	/* CONFIG_COMPAT */
> -
>  static int vfio_fops_open(struct inode *inode, struct file *filep)
>  {
>  	struct vfio_container *container;
> @@ -1291,9 +1282,7 @@ static const struct file_operations vfio_fops = {
>  	.read		= vfio_fops_read,
>  	.write		= vfio_fops_write,
>  	.unlocked_ioctl	= vfio_fops_unl_ioctl,
> -#ifdef CONFIG_COMPAT
> -	.compat_ioctl	= vfio_fops_compat_ioctl,
> -#endif
> +	.compat_ioctl	= compat_ptr_ioctl,
>  	.mmap		= vfio_fops_mmap,
>  };
>  
> @@ -1572,15 +1561,6 @@ static long vfio_group_fops_unl_ioctl(struct file *filep,
>  	return ret;
>  }
>  
> -#ifdef CONFIG_COMPAT
> -static long vfio_group_fops_compat_ioctl(struct file *filep,
> -					 unsigned int cmd, unsigned long arg)
> -{
> -	arg = (unsigned long)compat_ptr(arg);
> -	return vfio_group_fops_unl_ioctl(filep, cmd, arg);
> -}
> -#endif	/* CONFIG_COMPAT */
> -
>  static int vfio_group_fops_open(struct inode *inode, struct file *filep)
>  {
>  	struct vfio_group *group;
> @@ -1636,9 +1616,7 @@ static int vfio_group_fops_release(struct inode *inode, struct file *filep)
>  static const struct file_operations vfio_group_fops = {
>  	.owner		= THIS_MODULE,
>  	.unlocked_ioctl	= vfio_group_fops_unl_ioctl,
> -#ifdef CONFIG_COMPAT
> -	.compat_ioctl	= vfio_group_fops_compat_ioctl,
> -#endif
> +	.compat_ioctl	= compat_ptr_ioctl,
>  	.open		= vfio_group_fops_open,
>  	.release	= vfio_group_fops_release,
>  };
> @@ -1703,24 +1681,13 @@ static int vfio_device_fops_mmap(struct file *filep, struct vm_area_struct *vma)
>  	return device->ops->mmap(device->device_data, vma);
>  }
>  
> -#ifdef CONFIG_COMPAT
> -static long vfio_device_fops_compat_ioctl(struct file *filep,
> -					  unsigned int cmd, unsigned long arg)
> -{
> -	arg = (unsigned long)compat_ptr(arg);
> -	return vfio_device_fops_unl_ioctl(filep, cmd, arg);
> -}
> -#endif	/* CONFIG_COMPAT */
> -
>  static const struct file_operations vfio_device_fops = {
>  	.owner		= THIS_MODULE,
>  	.release	= vfio_device_fops_release,
>  	.read		= vfio_device_fops_read,
>  	.write		= vfio_device_fops_write,
>  	.unlocked_ioctl	= vfio_device_fops_unl_ioctl,
> -#ifdef CONFIG_COMPAT
> -	.compat_ioctl	= vfio_device_fops_compat_ioctl,
> -#endif
> +	.compat_ioctl	= compat_ptr_ioctl,
>  	.mmap		= vfio_device_fops_mmap,
>  };
>  
> diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
> index df51a35cf537..1642b3573230 100644
> --- a/drivers/vhost/net.c
> +++ b/drivers/vhost/net.c
> @@ -1765,14 +1765,6 @@ static long vhost_net_ioctl(struct file *f, unsigned int ioctl,
>  	}
>  }
>  
> -#ifdef CONFIG_COMPAT
> -static long vhost_net_compat_ioctl(struct file *f, unsigned int ioctl,
> -				   unsigned long arg)
> -{
> -	return vhost_net_ioctl(f, ioctl, (unsigned long)compat_ptr(arg));
> -}
> -#endif
> -
>  static ssize_t vhost_net_chr_read_iter(struct kiocb *iocb, struct iov_iter *to)
>  {
>  	struct file *file = iocb->ki_filp;
> @@ -1808,9 +1800,7 @@ static const struct file_operations vhost_net_fops = {
>  	.write_iter     = vhost_net_chr_write_iter,
>  	.poll           = vhost_net_chr_poll,
>  	.unlocked_ioctl = vhost_net_ioctl,
> -#ifdef CONFIG_COMPAT
> -	.compat_ioctl   = vhost_net_compat_ioctl,
> -#endif
> +	.compat_ioctl   = compat_ptr_ioctl,
>  	.open           = vhost_net_open,
>  	.llseek		= noop_llseek,
>  };
> diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c
> index 618fb6461017..f9b14c39d89b 100644
> --- a/drivers/vhost/scsi.c
> +++ b/drivers/vhost/scsi.c
> @@ -1721,21 +1721,11 @@ vhost_scsi_ioctl(struct file *f,
>  	}
>  }
>  
> -#ifdef CONFIG_COMPAT
> -static long vhost_scsi_compat_ioctl(struct file *f, unsigned int ioctl,
> -				unsigned long arg)
> -{
> -	return vhost_scsi_ioctl(f, ioctl, (unsigned long)compat_ptr(arg));
> -}
> -#endif
> -
>  static const struct file_operations vhost_scsi_fops = {
>  	.owner          = THIS_MODULE,
>  	.release        = vhost_scsi_release,
>  	.unlocked_ioctl = vhost_scsi_ioctl,
> -#ifdef CONFIG_COMPAT
> -	.compat_ioctl	= vhost_scsi_compat_ioctl,
> -#endif
> +	.compat_ioctl	= compat_ptr_ioctl,
>  	.open           = vhost_scsi_open,
>  	.llseek		= noop_llseek,
>  };
> diff --git a/drivers/vhost/test.c b/drivers/vhost/test.c
> index 40589850eb33..61d4d98c8f70 100644
> --- a/drivers/vhost/test.c
> +++ b/drivers/vhost/test.c
> @@ -298,21 +298,11 @@ static long vhost_test_ioctl(struct file *f, unsigned int ioctl,
>  	}
>  }
>  
> -#ifdef CONFIG_COMPAT
> -static long vhost_test_compat_ioctl(struct file *f, unsigned int ioctl,
> -				   unsigned long arg)
> -{
> -	return vhost_test_ioctl(f, ioctl, (unsigned long)compat_ptr(arg));
> -}
> -#endif
> -
>  static const struct file_operations vhost_test_fops = {
>  	.owner          = THIS_MODULE,
>  	.release        = vhost_test_release,
>  	.unlocked_ioctl = vhost_test_ioctl,
> -#ifdef CONFIG_COMPAT
> -	.compat_ioctl   = vhost_test_compat_ioctl,
> -#endif
> +	.compat_ioctl   = compat_ptr_ioctl,
>  	.open           = vhost_test_open,
>  	.llseek		= noop_llseek,
>  };
> diff --git a/drivers/vhost/vsock.c b/drivers/vhost/vsock.c
> index bb5fc0e9fbc2..9a86202678b6 100644
> --- a/drivers/vhost/vsock.c
> +++ b/drivers/vhost/vsock.c
> @@ -716,23 +716,13 @@ static long vhost_vsock_dev_ioctl(struct file *f, unsigned int ioctl,
>  	}
>  }
>  
> -#ifdef CONFIG_COMPAT
> -static long vhost_vsock_dev_compat_ioctl(struct file *f, unsigned int ioctl,
> -					 unsigned long arg)
> -{
> -	return vhost_vsock_dev_ioctl(f, ioctl, (unsigned long)compat_ptr(arg));
> -}
> -#endif
> -
>  static const struct file_operations vhost_vsock_fops = {
>  	.owner          = THIS_MODULE,
>  	.open           = vhost_vsock_dev_open,
>  	.release        = vhost_vsock_dev_release,
>  	.llseek		= noop_llseek,
>  	.unlocked_ioctl = vhost_vsock_dev_ioctl,
> -#ifdef CONFIG_COMPAT
> -	.compat_ioctl   = vhost_vsock_dev_compat_ioctl,
> -#endif
> +	.compat_ioctl   = compat_ptr_ioctl,
>  };
>  
>  static struct miscdevice vhost_vsock_misc = {
> diff --git a/fs/fat/file.c b/fs/fat/file.c
> index b3bed32946b1..f173d9261115 100644
> --- a/fs/fat/file.c
> +++ b/fs/fat/file.c
> @@ -171,15 +171,6 @@ long fat_generic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
>  	}
>  }
>  
> -#ifdef CONFIG_COMPAT
> -static long fat_generic_compat_ioctl(struct file *filp, unsigned int cmd,
> -				      unsigned long arg)
> -
> -{
> -	return fat_generic_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));
> -}
> -#endif
> -
>  static int fat_file_release(struct inode *inode, struct file *filp)
>  {
>  	if ((filp->f_mode & FMODE_WRITE) &&
> @@ -209,9 +200,7 @@ const struct file_operations fat_file_operations = {
>  	.mmap		= generic_file_mmap,
>  	.release	= fat_file_release,
>  	.unlocked_ioctl	= fat_generic_ioctl,
> -#ifdef CONFIG_COMPAT
> -	.compat_ioctl	= fat_generic_compat_ioctl,
> -#endif
> +	.compat_ioctl	= compat_ptr_ioctl,
>  	.fsync		= fat_file_fsync,
>  	.splice_read	= generic_file_splice_read,
>  	.splice_write	= iter_file_splice_write,
> -- 
> 2.20.0

^ permalink raw reply

* [PATCH] HID: intel-ish-hid: Add Comet Lake PCI device ID
From: Srinivas Pandruvada @ 2019-04-20  2:00 UTC (permalink / raw)
  To: jikos, benjamin.tissoires; +Cc: linux-input, linux-kernel, Srinivas Pandruvada

Added Comet Lake PCI device ID to the supported device list.

Signed-off-by: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
---
 drivers/hid/intel-ish-hid/ipc/hw-ish.h  | 1 +
 drivers/hid/intel-ish-hid/ipc/pci-ish.c | 1 +
 2 files changed, 2 insertions(+)

diff --git a/drivers/hid/intel-ish-hid/ipc/hw-ish.h b/drivers/hid/intel-ish-hid/ipc/hw-ish.h
index 08a8327dfd22..523c0cbd44a4 100644
--- a/drivers/hid/intel-ish-hid/ipc/hw-ish.h
+++ b/drivers/hid/intel-ish-hid/ipc/hw-ish.h
@@ -31,6 +31,7 @@
 #define CNL_H_DEVICE_ID		0xA37C
 #define ICL_MOBILE_DEVICE_ID	0x34FC
 #define SPT_H_DEVICE_ID		0xA135
+#define CML_LP_DEVICE_ID	0x02FC
 
 #define	REVISION_ID_CHT_A0	0x6
 #define	REVISION_ID_CHT_Ax_SI	0x0
diff --git a/drivers/hid/intel-ish-hid/ipc/pci-ish.c b/drivers/hid/intel-ish-hid/ipc/pci-ish.c
index a6e1ee744f4d..ac0a179daf23 100644
--- a/drivers/hid/intel-ish-hid/ipc/pci-ish.c
+++ b/drivers/hid/intel-ish-hid/ipc/pci-ish.c
@@ -40,6 +40,7 @@ static const struct pci_device_id ish_pci_tbl[] = {
 	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, CNL_H_DEVICE_ID)},
 	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, ICL_MOBILE_DEVICE_ID)},
 	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, SPT_H_DEVICE_ID)},
+	{PCI_DEVICE(PCI_VENDOR_ID_INTEL, CML_LP_DEVICE_ID)},
 	{0, }
 };
 MODULE_DEVICE_TABLE(pci, ish_pci_tbl);
-- 
2.17.2

^ permalink raw reply related

* RE: [PATCH v3 09/26] compat_ioctl: move drivers to compat_ptr_ioctl
From: Winkler, Tomas @ 2019-04-20  8:03 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: linux-usb@vger.kernel.org, kvm@vger.kernel.org,
	Michael S. Tsirkin, Alexander Shishkin, Jason Wang,
	Jarkko Sakkinen, virtualization@lists.linux-foundation.org,
	Benjamin Tissoires, linux-mtd@lists.infradead.org, Peter Huewe,
	linux1394-devel@lists.sourceforge.net,
	linux-stm32@st-md-mailman.stormreply.com,
	devel@driverdev.osuosl.org, Marek Vasut, y2038@lists.linaro.org,
	Richard Weinberger
In-Reply-To: <20190419191633-mutt-send-email-mst@kernel.org>

> 
> On Tue, Apr 16, 2019 at 10:19:47PM +0200, Arnd Bergmann wrote:
> > Each of these drivers has a copy of the same trivial helper function
> > to convert the pointer argument and then call the native ioctl handler.
> >
> > We now have a generic implementation of that, so use it.
> >
> > Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> > Reviewed-by: Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
> > Reviewed-by: Jason Gunthorpe <jgg@mellanox.com>
> > Signed-off-by: Arnd Bergmann <arnd@arndb.de>
> 
> Acked-by: Michael S. Tsirkin <mst@redhat.com>
Acked-by: Tomas Winkler <tomas.winkler@intel.com>
> 
> > ---
> >  drivers/char/ppdev.c              | 12 +---------
> >  drivers/char/tpm/tpm_vtpm_proxy.c | 12 +---------
> >  drivers/firewire/core-cdev.c      | 12 +---------
> >  drivers/hid/usbhid/hiddev.c       | 11 +--------
> >  drivers/hwtracing/stm/core.c      | 12 +---------
> >  drivers/misc/mei/main.c           | 22 +----------------
> >  drivers/mtd/ubi/cdev.c            | 36 +++-------------------------
> >  drivers/net/tap.c                 | 12 +---------
> >  drivers/staging/pi433/pi433_if.c  | 12 +---------
> >  drivers/usb/core/devio.c          | 16 +------------
> >  drivers/vfio/vfio.c               | 39 +++----------------------------
> >  drivers/vhost/net.c               | 12 +---------
> >  drivers/vhost/scsi.c              | 12 +---------
> >  drivers/vhost/test.c              | 12 +---------
> >  drivers/vhost/vsock.c             | 12 +---------
> >  fs/fat/file.c                     | 13 +----------
> >  16 files changed, 20 insertions(+), 237 deletions(-)
> >
> > diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c index
> > 1ae77b41050a..e96c8d9623e0 100644
> > --- a/drivers/char/ppdev.c
> > +++ b/drivers/char/ppdev.c
> > @@ -674,14 +674,6 @@ static long pp_ioctl(struct file *file, unsigned int
> cmd, unsigned long arg)
> >  	return ret;
> >  }
> >
> > -#ifdef CONFIG_COMPAT
> > -static long pp_compat_ioctl(struct file *file, unsigned int cmd,
> > -			    unsigned long arg)
> > -{
> > -	return pp_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
> > -}
> > -#endif
> > -
> >  static int pp_open(struct inode *inode, struct file *file)  {
> >  	unsigned int minor = iminor(inode);
> > @@ -790,9 +782,7 @@ static const struct file_operations pp_fops = {
> >  	.write		= pp_write,
> >  	.poll		= pp_poll,
> >  	.unlocked_ioctl	= pp_ioctl,
> > -#ifdef CONFIG_COMPAT
> > -	.compat_ioctl   = pp_compat_ioctl,
> > -#endif
> > +	.compat_ioctl   = compat_ptr_ioctl,
> >  	.open		= pp_open,
> >  	.release	= pp_release,
> >  };
> > diff --git a/drivers/char/tpm/tpm_vtpm_proxy.c
> > b/drivers/char/tpm/tpm_vtpm_proxy.c
> > index d74f3de74ae6..fb845f0a430b 100644
> > --- a/drivers/char/tpm/tpm_vtpm_proxy.c
> > +++ b/drivers/char/tpm/tpm_vtpm_proxy.c
> > @@ -675,20 +675,10 @@ static long vtpmx_fops_ioctl(struct file *f, unsigned
> int ioctl,
> >  	}
> >  }
> >
> > -#ifdef CONFIG_COMPAT
> > -static long vtpmx_fops_compat_ioctl(struct file *f, unsigned int ioctl,
> > -					  unsigned long arg)
> > -{
> > -	return vtpmx_fops_ioctl(f, ioctl, (unsigned long)compat_ptr(arg));
> > -}
> > -#endif
> > -
> >  static const struct file_operations vtpmx_fops = {
> >  	.owner = THIS_MODULE,
> >  	.unlocked_ioctl = vtpmx_fops_ioctl,
> > -#ifdef CONFIG_COMPAT
> > -	.compat_ioctl = vtpmx_fops_compat_ioctl,
> > -#endif
> > +	.compat_ioctl = compat_ptr_ioctl,
> >  	.llseek = noop_llseek,
> >  };
> >
> > diff --git a/drivers/firewire/core-cdev.c
> > b/drivers/firewire/core-cdev.c index 16a7045736a9..fb934680fdd3 100644
> > --- a/drivers/firewire/core-cdev.c
> > +++ b/drivers/firewire/core-cdev.c
> > @@ -1659,14 +1659,6 @@ static long fw_device_op_ioctl(struct file *file,
> >  	return dispatch_ioctl(file->private_data, cmd, (void __user *)arg);
> > }
> >
> > -#ifdef CONFIG_COMPAT
> > -static long fw_device_op_compat_ioctl(struct file *file,
> > -				      unsigned int cmd, unsigned long arg)
> > -{
> > -	return dispatch_ioctl(file->private_data, cmd, compat_ptr(arg));
> > -}
> > -#endif
> > -
> >  static int fw_device_op_mmap(struct file *file, struct vm_area_struct
> > *vma)  {
> >  	struct client *client = file->private_data; @@ -1808,7 +1800,5 @@
> > const struct file_operations fw_device_ops = {
> >  	.mmap		= fw_device_op_mmap,
> >  	.release	= fw_device_op_release,
> >  	.poll		= fw_device_op_poll,
> > -#ifdef CONFIG_COMPAT
> > -	.compat_ioctl	= fw_device_op_compat_ioctl,
> > -#endif
> > +	.compat_ioctl	= compat_ptr_ioctl,
> >  };
> > diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c
> > index a746017fac17..ef4a1cd389d6 100644
> > --- a/drivers/hid/usbhid/hiddev.c
> > +++ b/drivers/hid/usbhid/hiddev.c
> > @@ -855,13 +855,6 @@ static long hiddev_ioctl(struct file *file, unsigned int
> cmd, unsigned long arg)
> >  	return r;
> >  }
> >
> > -#ifdef CONFIG_COMPAT
> > -static long hiddev_compat_ioctl(struct file *file, unsigned int cmd,
> > unsigned long arg) -{
> > -	return hiddev_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
> > -}
> > -#endif
> > -
> >  static const struct file_operations hiddev_fops = {
> >  	.owner =	THIS_MODULE,
> >  	.read =		hiddev_read,
> > @@ -871,9 +864,7 @@ static const struct file_operations hiddev_fops = {
> >  	.release =	hiddev_release,
> >  	.unlocked_ioctl =	hiddev_ioctl,
> >  	.fasync =	hiddev_fasync,
> > -#ifdef CONFIG_COMPAT
> > -	.compat_ioctl	= hiddev_compat_ioctl,
> > -#endif
> > +	.compat_ioctl	= compat_ptr_ioctl,
> >  	.llseek		= noop_llseek,
> >  };
> >
> > diff --git a/drivers/hwtracing/stm/core.c
> > b/drivers/hwtracing/stm/core.c index c7ba8acfd4d5..454da259f144 100644
> > --- a/drivers/hwtracing/stm/core.c
> > +++ b/drivers/hwtracing/stm/core.c
> > @@ -840,23 +840,13 @@ stm_char_ioctl(struct file *file, unsigned int cmd,
> unsigned long arg)
> >  	return err;
> >  }
> >
> > -#ifdef CONFIG_COMPAT
> > -static long
> > -stm_char_compat_ioctl(struct file *file, unsigned int cmd, unsigned
> > long arg) -{
> > -	return stm_char_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
> > -}
> > -#else
> > -#define stm_char_compat_ioctl	NULL
> > -#endif
> > -
> >  static const struct file_operations stm_fops = {
> >  	.open		= stm_char_open,
> >  	.release	= stm_char_release,
> >  	.write		= stm_char_write,
> >  	.mmap		= stm_char_mmap,
> >  	.unlocked_ioctl	= stm_char_ioctl,
> > -	.compat_ioctl	= stm_char_compat_ioctl,
> > +	.compat_ioctl	= compat_ptr_ioctl,
> >  	.llseek		= no_llseek,
> >  };
> >
> > diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index
> > 87281b3695e6..cc6af92cdef0 100644
> > --- a/drivers/misc/mei/main.c
> > +++ b/drivers/misc/mei/main.c
> > @@ -535,24 +535,6 @@ static long mei_ioctl(struct file *file, unsigned int
> cmd, unsigned long data)
> >  	return rets;
> >  }
> >
> > -/**
> > - * mei_compat_ioctl - the compat IOCTL function
> > - *
> > - * @file: pointer to file structure
> > - * @cmd: ioctl command
> > - * @data: pointer to mei message structure
> > - *
> > - * Return: 0 on success , <0 on error
> > - */
> > -#ifdef CONFIG_COMPAT
> > -static long mei_compat_ioctl(struct file *file,
> > -			unsigned int cmd, unsigned long data)
> > -{
> > -	return mei_ioctl(file, cmd, (unsigned long)compat_ptr(data));
> > -}
> > -#endif
> > -
> > -
> >  /**
> >   * mei_poll - the poll function
> >   *
> > @@ -855,9 +837,7 @@ static const struct file_operations mei_fops = {
> >  	.owner = THIS_MODULE,
> >  	.read = mei_read,
> >  	.unlocked_ioctl = mei_ioctl,
> > -#ifdef CONFIG_COMPAT
> > -	.compat_ioctl = mei_compat_ioctl,
> > -#endif
> > +	.compat_ioctl = compat_ptr_ioctl,
> >  	.open = mei_open,
> >  	.release = mei_release,
> >  	.write = mei_write,
> > diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index
> > 947a8adbc799..265d34fa3efa 100644
> > --- a/drivers/mtd/ubi/cdev.c
> > +++ b/drivers/mtd/ubi/cdev.c
> > @@ -1091,36 +1091,6 @@ static long ctrl_cdev_ioctl(struct file *file,
> unsigned int cmd,
> >  	return err;
> >  }
> >
> > -#ifdef CONFIG_COMPAT
> > -static long vol_cdev_compat_ioctl(struct file *file, unsigned int cmd,
> > -				  unsigned long arg)
> > -{
> > -	unsigned long translated_arg = (unsigned long)compat_ptr(arg);
> > -
> > -	return vol_cdev_ioctl(file, cmd, translated_arg);
> > -}
> > -
> > -static long ubi_cdev_compat_ioctl(struct file *file, unsigned int cmd,
> > -				  unsigned long arg)
> > -{
> > -	unsigned long translated_arg = (unsigned long)compat_ptr(arg);
> > -
> > -	return ubi_cdev_ioctl(file, cmd, translated_arg);
> > -}
> > -
> > -static long ctrl_cdev_compat_ioctl(struct file *file, unsigned int cmd,
> > -				   unsigned long arg)
> > -{
> > -	unsigned long translated_arg = (unsigned long)compat_ptr(arg);
> > -
> > -	return ctrl_cdev_ioctl(file, cmd, translated_arg);
> > -}
> > -#else
> > -#define vol_cdev_compat_ioctl  NULL
> > -#define ubi_cdev_compat_ioctl  NULL
> > -#define ctrl_cdev_compat_ioctl NULL
> > -#endif
> > -
> >  /* UBI volume character device operations */  const struct
> > file_operations ubi_vol_cdev_operations = {
> >  	.owner          = THIS_MODULE,
> > @@ -1131,7 +1101,7 @@ const struct file_operations
> ubi_vol_cdev_operations = {
> >  	.write          = vol_cdev_write,
> >  	.fsync		= vol_cdev_fsync,
> >  	.unlocked_ioctl = vol_cdev_ioctl,
> > -	.compat_ioctl   = vol_cdev_compat_ioctl,
> > +	.compat_ioctl   = compat_ptr_ioctl,
> >  };
> >
> >  /* UBI character device operations */ @@ -1139,13 +1109,13 @@ const
> > struct file_operations ubi_cdev_operations = {
> >  	.owner          = THIS_MODULE,
> >  	.llseek         = no_llseek,
> >  	.unlocked_ioctl = ubi_cdev_ioctl,
> > -	.compat_ioctl   = ubi_cdev_compat_ioctl,
> > +	.compat_ioctl   = compat_ptr_ioctl,
> >  };
> >
> >  /* UBI control character device operations */  const struct
> > file_operations ubi_ctrl_cdev_operations = {
> >  	.owner          = THIS_MODULE,
> >  	.unlocked_ioctl = ctrl_cdev_ioctl,
> > -	.compat_ioctl   = ctrl_cdev_compat_ioctl,
> > +	.compat_ioctl   = compat_ptr_ioctl,
> >  	.llseek		= no_llseek,
> >  };
> > diff --git a/drivers/net/tap.c b/drivers/net/tap.c index
> > 2ea9b4976f4a..ebe425e65992 100644
> > --- a/drivers/net/tap.c
> > +++ b/drivers/net/tap.c
> > @@ -1123,14 +1123,6 @@ static long tap_ioctl(struct file *file, unsigned int
> cmd,
> >  	}
> >  }
> >
> > -#ifdef CONFIG_COMPAT
> > -static long tap_compat_ioctl(struct file *file, unsigned int cmd,
> > -			     unsigned long arg)
> > -{
> > -	return tap_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
> > -}
> > -#endif
> > -
> >  static const struct file_operations tap_fops = {
> >  	.owner		= THIS_MODULE,
> >  	.open		= tap_open,
> > @@ -1140,9 +1132,7 @@ static const struct file_operations tap_fops = {
> >  	.poll		= tap_poll,
> >  	.llseek		= no_llseek,
> >  	.unlocked_ioctl	= tap_ioctl,
> > -#ifdef CONFIG_COMPAT
> > -	.compat_ioctl	= tap_compat_ioctl,
> > -#endif
> > +	.compat_ioctl	= compat_ptr_ioctl,
> >  };
> >
> >  static int tap_get_user_xdp(struct tap_queue *q, struct xdp_buff
> > *xdp) diff --git a/drivers/staging/pi433/pi433_if.c
> > b/drivers/staging/pi433/pi433_if.c
> > index b2314636dc89..ab7dfc7c2917 100644
> > --- a/drivers/staging/pi433/pi433_if.c
> > +++ b/drivers/staging/pi433/pi433_if.c
> > @@ -935,16 +935,6 @@ pi433_ioctl(struct file *filp, unsigned int cmd,
> unsigned long arg)
> >  	return retval;
> >  }
> >
> > -#ifdef CONFIG_COMPAT
> > -static long
> > -pi433_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long
> > arg) -{
> > -	return pi433_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));
> > -}
> > -#else
> > -#define pi433_compat_ioctl NULL
> > -#endif /* CONFIG_COMPAT */
> > -
> >
> > /*--------------------------------------------------------------------
> > -----*/
> >
> >  static int pi433_open(struct inode *inode, struct file *filp) @@
> > -1101,7 +1091,7 @@ static const struct file_operations pi433_fops = {
> >  	.write =	pi433_write,
> >  	.read =		pi433_read,
> >  	.unlocked_ioctl = pi433_ioctl,
> > -	.compat_ioctl = pi433_compat_ioctl,
> > +	.compat_ioctl = compat_ptr_ioctl,
> >  	.open =		pi433_open,
> >  	.release =	pi433_release,
> >  	.llseek =	no_llseek,
> > diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index
> > fa783531ee88..d75052b36584 100644
> > --- a/drivers/usb/core/devio.c
> > +++ b/drivers/usb/core/devio.c
> > @@ -2568,18 +2568,6 @@ static long usbdev_ioctl(struct file *file, unsigned
> int cmd,
> >  	return ret;
> >  }
> >
> > -#ifdef CONFIG_COMPAT
> > -static long usbdev_compat_ioctl(struct file *file, unsigned int cmd,
> > -			unsigned long arg)
> > -{
> > -	int ret;
> > -
> > -	ret = usbdev_do_ioctl(file, cmd, compat_ptr(arg));
> > -
> > -	return ret;
> > -}
> > -#endif
> > -
> >  /* No kernel lock - fine */
> >  static __poll_t usbdev_poll(struct file *file,
> >  				struct poll_table_struct *wait)
> > @@ -2603,9 +2591,7 @@ const struct file_operations usbdev_file_operations
> = {
> >  	.read =		  usbdev_read,
> >  	.poll =		  usbdev_poll,
> >  	.unlocked_ioctl = usbdev_ioctl,
> > -#ifdef CONFIG_COMPAT
> > -	.compat_ioctl =   usbdev_compat_ioctl,
> > -#endif
> > +	.compat_ioctl =   compat_ptr_ioctl,
> >  	.mmap =           usbdev_mmap,
> >  	.open =		  usbdev_open,
> >  	.release =	  usbdev_release,
> > diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c index
> > a3030cdf3c18..a5efe82584a5 100644
> > --- a/drivers/vfio/vfio.c
> > +++ b/drivers/vfio/vfio.c
> > @@ -1200,15 +1200,6 @@ static long vfio_fops_unl_ioctl(struct file *filep,
> >  	return ret;
> >  }
> >
> > -#ifdef CONFIG_COMPAT
> > -static long vfio_fops_compat_ioctl(struct file *filep,
> > -				   unsigned int cmd, unsigned long arg)
> > -{
> > -	arg = (unsigned long)compat_ptr(arg);
> > -	return vfio_fops_unl_ioctl(filep, cmd, arg);
> > -}
> > -#endif	/* CONFIG_COMPAT */
> > -
> >  static int vfio_fops_open(struct inode *inode, struct file *filep)  {
> >  	struct vfio_container *container;
> > @@ -1291,9 +1282,7 @@ static const struct file_operations vfio_fops = {
> >  	.read		= vfio_fops_read,
> >  	.write		= vfio_fops_write,
> >  	.unlocked_ioctl	= vfio_fops_unl_ioctl,
> > -#ifdef CONFIG_COMPAT
> > -	.compat_ioctl	= vfio_fops_compat_ioctl,
> > -#endif
> > +	.compat_ioctl	= compat_ptr_ioctl,
> >  	.mmap		= vfio_fops_mmap,
> >  };
> >
> > @@ -1572,15 +1561,6 @@ static long vfio_group_fops_unl_ioctl(struct file
> *filep,
> >  	return ret;
> >  }
> >
> > -#ifdef CONFIG_COMPAT
> > -static long vfio_group_fops_compat_ioctl(struct file *filep,
> > -					 unsigned int cmd, unsigned long arg)
> > -{
> > -	arg = (unsigned long)compat_ptr(arg);
> > -	return vfio_group_fops_unl_ioctl(filep, cmd, arg);
> > -}
> > -#endif	/* CONFIG_COMPAT */
> > -
> >  static int vfio_group_fops_open(struct inode *inode, struct file
> > *filep)  {
> >  	struct vfio_group *group;
> > @@ -1636,9 +1616,7 @@ static int vfio_group_fops_release(struct inode
> > *inode, struct file *filep)  static const struct file_operations vfio_group_fops =
> {
> >  	.owner		= THIS_MODULE,
> >  	.unlocked_ioctl	= vfio_group_fops_unl_ioctl,
> > -#ifdef CONFIG_COMPAT
> > -	.compat_ioctl	= vfio_group_fops_compat_ioctl,
> > -#endif
> > +	.compat_ioctl	= compat_ptr_ioctl,
> >  	.open		= vfio_group_fops_open,
> >  	.release	= vfio_group_fops_release,
> >  };
> > @@ -1703,24 +1681,13 @@ static int vfio_device_fops_mmap(struct file
> *filep, struct vm_area_struct *vma)
> >  	return device->ops->mmap(device->device_data, vma);  }
> >
> > -#ifdef CONFIG_COMPAT
> > -static long vfio_device_fops_compat_ioctl(struct file *filep,
> > -					  unsigned int cmd, unsigned long arg)
> > -{
> > -	arg = (unsigned long)compat_ptr(arg);
> > -	return vfio_device_fops_unl_ioctl(filep, cmd, arg);
> > -}
> > -#endif	/* CONFIG_COMPAT */
> > -
> >  static const struct file_operations vfio_device_fops = {
> >  	.owner		= THIS_MODULE,
> >  	.release	= vfio_device_fops_release,
> >  	.read		= vfio_device_fops_read,
> >  	.write		= vfio_device_fops_write,
> >  	.unlocked_ioctl	= vfio_device_fops_unl_ioctl,
> > -#ifdef CONFIG_COMPAT
> > -	.compat_ioctl	= vfio_device_fops_compat_ioctl,
> > -#endif
> > +	.compat_ioctl	= compat_ptr_ioctl,
> >  	.mmap		= vfio_device_fops_mmap,
> >  };
> >
> > diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index
> > df51a35cf537..1642b3573230 100644
> > --- a/drivers/vhost/net.c
> > +++ b/drivers/vhost/net.c
> > @@ -1765,14 +1765,6 @@ static long vhost_net_ioctl(struct file *f, unsigned
> int ioctl,
> >  	}
> >  }
> >
> > -#ifdef CONFIG_COMPAT
> > -static long vhost_net_compat_ioctl(struct file *f, unsigned int ioctl,
> > -				   unsigned long arg)
> > -{
> > -	return vhost_net_ioctl(f, ioctl, (unsigned long)compat_ptr(arg));
> > -}
> > -#endif
> > -
> >  static ssize_t vhost_net_chr_read_iter(struct kiocb *iocb, struct
> > iov_iter *to)  {
> >  	struct file *file = iocb->ki_filp;
> > @@ -1808,9 +1800,7 @@ static const struct file_operations vhost_net_fops =
> {
> >  	.write_iter     = vhost_net_chr_write_iter,
> >  	.poll           = vhost_net_chr_poll,
> >  	.unlocked_ioctl = vhost_net_ioctl,
> > -#ifdef CONFIG_COMPAT
> > -	.compat_ioctl   = vhost_net_compat_ioctl,
> > -#endif
> > +	.compat_ioctl   = compat_ptr_ioctl,
> >  	.open           = vhost_net_open,
> >  	.llseek		= noop_llseek,
> >  };
> > diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c index
> > 618fb6461017..f9b14c39d89b 100644
> > --- a/drivers/vhost/scsi.c
> > +++ b/drivers/vhost/scsi.c
> > @@ -1721,21 +1721,11 @@ vhost_scsi_ioctl(struct file *f,
> >  	}
> >  }
> >
> > -#ifdef CONFIG_COMPAT
> > -static long vhost_scsi_compat_ioctl(struct file *f, unsigned int ioctl,
> > -				unsigned long arg)
> > -{
> > -	return vhost_scsi_ioctl(f, ioctl, (unsigned long)compat_ptr(arg));
> > -}
> > -#endif
> > -
> >  static const struct file_operations vhost_scsi_fops = {
> >  	.owner          = THIS_MODULE,
> >  	.release        = vhost_scsi_release,
> >  	.unlocked_ioctl = vhost_scsi_ioctl,
> > -#ifdef CONFIG_COMPAT
> > -	.compat_ioctl	= vhost_scsi_compat_ioctl,
> > -#endif
> > +	.compat_ioctl	= compat_ptr_ioctl,
> >  	.open           = vhost_scsi_open,
> >  	.llseek		= noop_llseek,
> >  };
> > diff --git a/drivers/vhost/test.c b/drivers/vhost/test.c index
> > 40589850eb33..61d4d98c8f70 100644
> > --- a/drivers/vhost/test.c
> > +++ b/drivers/vhost/test.c
> > @@ -298,21 +298,11 @@ static long vhost_test_ioctl(struct file *f, unsigned
> int ioctl,
> >  	}
> >  }
> >
> > -#ifdef CONFIG_COMPAT
> > -static long vhost_test_compat_ioctl(struct file *f, unsigned int ioctl,
> > -				   unsigned long arg)
> > -{
> > -	return vhost_test_ioctl(f, ioctl, (unsigned long)compat_ptr(arg));
> > -}
> > -#endif
> > -
> >  static const struct file_operations vhost_test_fops = {
> >  	.owner          = THIS_MODULE,
> >  	.release        = vhost_test_release,
> >  	.unlocked_ioctl = vhost_test_ioctl,
> > -#ifdef CONFIG_COMPAT
> > -	.compat_ioctl   = vhost_test_compat_ioctl,
> > -#endif
> > +	.compat_ioctl   = compat_ptr_ioctl,
> >  	.open           = vhost_test_open,
> >  	.llseek		= noop_llseek,
> >  };
> > diff --git a/drivers/vhost/vsock.c b/drivers/vhost/vsock.c index
> > bb5fc0e9fbc2..9a86202678b6 100644
> > --- a/drivers/vhost/vsock.c
> > +++ b/drivers/vhost/vsock.c
> > @@ -716,23 +716,13 @@ static long vhost_vsock_dev_ioctl(struct file *f,
> unsigned int ioctl,
> >  	}
> >  }
> >
> > -#ifdef CONFIG_COMPAT
> > -static long vhost_vsock_dev_compat_ioctl(struct file *f, unsigned int ioctl,
> > -					 unsigned long arg)
> > -{
> > -	return vhost_vsock_dev_ioctl(f, ioctl, (unsigned long)compat_ptr(arg));
> > -}
> > -#endif
> > -
> >  static const struct file_operations vhost_vsock_fops = {
> >  	.owner          = THIS_MODULE,
> >  	.open           = vhost_vsock_dev_open,
> >  	.release        = vhost_vsock_dev_release,
> >  	.llseek		= noop_llseek,
> >  	.unlocked_ioctl = vhost_vsock_dev_ioctl, -#ifdef CONFIG_COMPAT
> > -	.compat_ioctl   = vhost_vsock_dev_compat_ioctl,
> > -#endif
> > +	.compat_ioctl   = compat_ptr_ioctl,
> >  };
> >
> >  static struct miscdevice vhost_vsock_misc = { diff --git
> > a/fs/fat/file.c b/fs/fat/file.c index b3bed32946b1..f173d9261115
> > 100644
> > --- a/fs/fat/file.c
> > +++ b/fs/fat/file.c
> > @@ -171,15 +171,6 @@ long fat_generic_ioctl(struct file *filp, unsigned int
> cmd, unsigned long arg)
> >  	}
> >  }
> >
> > -#ifdef CONFIG_COMPAT
> > -static long fat_generic_compat_ioctl(struct file *filp, unsigned int cmd,
> > -				      unsigned long arg)
> > -
> > -{
> > -	return fat_generic_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));
> > -}
> > -#endif
> > -
> >  static int fat_file_release(struct inode *inode, struct file *filp)
> > {
> >  	if ((filp->f_mode & FMODE_WRITE) &&
> > @@ -209,9 +200,7 @@ const struct file_operations fat_file_operations = {
> >  	.mmap		= generic_file_mmap,
> >  	.release	= fat_file_release,
> >  	.unlocked_ioctl	= fat_generic_ioctl,
> > -#ifdef CONFIG_COMPAT
> > -	.compat_ioctl	= fat_generic_compat_ioctl,
> > -#endif
> > +	.compat_ioctl	= compat_ptr_ioctl,
> >  	.fsync		= fat_file_fsync,
> >  	.splice_read	= generic_file_splice_read,
> >  	.splice_write	= iter_file_splice_write,
> > --
> > 2.20.0

^ permalink raw reply

* [PATCH v4 1/3] dt-bindings: input: add GPIO controllable vibrator
From: Luca Weiss @ 2019-04-20 12:23 UTC (permalink / raw)
  Cc: Dmitry Torokhov, Rob Herring, Mark Rutland, Mauro Carvalho Chehab,
	Pascal PAILLET-LME, Coly Li, Lee Jones, Xiaotong Lu, Brian Masney,
	Rob Herring, Baolin Wang, David Brown,
	open list:ARM/QUALCOMM SUPPORT,
	open list:INPUT KEYBOARD, MOUSE, JOYSTICK , TOUCHSCREEN...,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	open list, Luca Weiss

Provide a simple driver for GPIO controllable vibrators.
It will be used by the Fairphone 2.

Signed-off-by: Luca Weiss <luca@z3ntu.xyz>
---
Changes from v3:
- Convert .txt based doc to the new yaml based format

 .../bindings/input/gpio-vibrator.yaml         | 39 +++++++++++++++++++
 1 file changed, 39 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/gpio-vibrator.yaml

diff --git a/Documentation/devicetree/bindings/input/gpio-vibrator.yaml b/Documentation/devicetree/bindings/input/gpio-vibrator.yaml
new file mode 100644
index 000000000000..bca1b6ea07a4
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/gpio-vibrator.yaml
@@ -0,0 +1,39 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/bindings/input/gpio-vibrator.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: GPIO vibrator
+
+maintainers:
+  - Luca Weiss <luca@z3ntu.xyz>
+
+description: |+
+  Registers a GPIO device as vibrator, where the on/off capability is controlled by a GPIO.
+
+properties:
+  compatible:
+    items:
+      - const: gpio-vibrator
+
+  enable-gpios:
+    maxItems: 1
+
+  vcc-supply:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description: Regulator that provides power
+
+required:
+  - compatible
+  - enable-gpios
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+
+    vibrator {
+        compatible = "gpio-vibrator";
+        enable-gpios = <&msmgpio 86 GPIO_ACTIVE_HIGH>;
+        vcc-supply = <&pm8941_l18>;
+    };
-- 
2.21.0

^ permalink raw reply related

* [PATCH v4 2/3] Input: add a driver for GPIO controllable vibrators
From: Luca Weiss @ 2019-04-20 12:23 UTC (permalink / raw)
  Cc: Dmitry Torokhov, Rob Herring, Mark Rutland, Mauro Carvalho Chehab,
	Pascal PAILLET-LME, Coly Li, Lee Jones, Xiaotong Lu, Brian Masney,
	Rob Herring, Baolin Wang, David Brown,
	open list:ARM/QUALCOMM SUPPORT,
	open list:INPUT KEYBOARD, MOUSE, JOYSTICK , TOUCHSCREEN...,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	open list, Luca Weiss
In-Reply-To: <20190420122333.23662-1-luca@z3ntu.xyz>

Provide a simple driver for GPIO controllable vibrators.
It will be used by the Fairphone 2.

Signed-off-by: Luca Weiss <luca@z3ntu.xyz>
---
Changes from v3:
- Apply suggestions from Stephen Boyd

 drivers/input/misc/Kconfig      |  12 ++
 drivers/input/misc/Makefile     |   1 +
 drivers/input/misc/gpio-vibra.c | 209 ++++++++++++++++++++++++++++++++
 3 files changed, 222 insertions(+)
 create mode 100644 drivers/input/misc/gpio-vibra.c

diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index e15ed1bb8558..6dfe9e2fe5b1 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -290,6 +290,18 @@ config INPUT_GPIO_DECODER
 	 To compile this driver as a module, choose M here: the module
 	 will be called gpio_decoder.
 
+config INPUT_GPIO_VIBRA
+	tristate "GPIO vibrator support"
+	depends on GPIOLIB || COMPILE_TEST
+	select INPUT_FF_MEMLESS
+	help
+	  Say Y here to get support for GPIO based vibrator devices.
+
+	  If unsure, say N.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called gpio-vibra.
+
 config INPUT_IXP4XX_BEEPER
 	tristate "IXP4XX Beeper support"
 	depends on ARCH_IXP4XX
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index b936c5b1d4ac..f38ebbdb05e2 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -36,6 +36,7 @@ obj-$(CONFIG_INPUT_DRV2667_HAPTICS)	+= drv2667.o
 obj-$(CONFIG_INPUT_GP2A)		+= gp2ap002a00f.o
 obj-$(CONFIG_INPUT_GPIO_BEEPER)		+= gpio-beeper.o
 obj-$(CONFIG_INPUT_GPIO_DECODER)	+= gpio_decoder.o
+obj-$(CONFIG_INPUT_GPIO_VIBRA)		+= gpio-vibra.o
 obj-$(CONFIG_INPUT_HISI_POWERKEY)	+= hisi_powerkey.o
 obj-$(CONFIG_HP_SDC_RTC)		+= hp_sdc_rtc.o
 obj-$(CONFIG_INPUT_IMS_PCU)		+= ims-pcu.o
diff --git a/drivers/input/misc/gpio-vibra.c b/drivers/input/misc/gpio-vibra.c
new file mode 100644
index 000000000000..b76c81015de9
--- /dev/null
+++ b/drivers/input/misc/gpio-vibra.c
@@ -0,0 +1,209 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ *  GPIO vibrator driver
+ *
+ *  Copyright (C) 2019 Luca Weiss <luca@z3ntu.xyz>
+ *
+ *  Based on PWM vibrator driver:
+ *  Copyright (C) 2017 Collabora Ltd.
+ *
+ *  Based on previous work from:
+ *  Copyright (C) 2012 Dmitry Torokhov <dmitry.torokhov@gmail.com>
+ *
+ *  Based on PWM beeper driver:
+ *  Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+
+struct gpio_vibrator {
+	struct input_dev *input;
+	struct gpio_desc *gpio;
+	struct regulator *vcc;
+
+	struct work_struct play_work;
+	bool running;
+	bool vcc_on;
+};
+
+static int gpio_vibrator_start(struct gpio_vibrator *vibrator)
+{
+	struct device *pdev = vibrator->input->dev.parent;
+	int err;
+
+	if (!vibrator->vcc_on) {
+		err = regulator_enable(vibrator->vcc);
+		if (err) {
+			dev_err(pdev, "failed to enable regulator: %d\n", err);
+			return err;
+		}
+		vibrator->vcc_on = true;
+	}
+
+	gpiod_set_value_cansleep(vibrator->gpio, 1);
+
+	return 0;
+}
+
+static void gpio_vibrator_stop(struct gpio_vibrator *vibrator)
+{
+	gpiod_set_value_cansleep(vibrator->gpio, 0);
+
+	if (vibrator->vcc_on) {
+		regulator_disable(vibrator->vcc);
+		vibrator->vcc_on = false;
+	}
+}
+
+static void gpio_vibrator_play_work(struct work_struct *work)
+{
+	struct gpio_vibrator *vibrator =
+		container_of(work, struct gpio_vibrator, play_work);
+
+	if (vibrator->running)
+		gpio_vibrator_start(vibrator);
+	else
+		gpio_vibrator_stop(vibrator);
+}
+
+static int gpio_vibrator_play_effect(struct input_dev *dev, void *data,
+				     struct ff_effect *effect)
+{
+	struct gpio_vibrator *vibrator = input_get_drvdata(dev);
+
+	int level = effect->u.rumble.strong_magnitude;
+
+	if (!level)
+		level = effect->u.rumble.weak_magnitude;
+
+	if (level)
+		vibrator->running = true;
+	else
+		vibrator->running = false;
+
+	schedule_work(&vibrator->play_work);
+
+	return 0;
+}
+
+static void gpio_vibrator_close(struct input_dev *input)
+{
+	struct gpio_vibrator *vibrator = input_get_drvdata(input);
+
+	cancel_work_sync(&vibrator->play_work);
+	gpio_vibrator_stop(vibrator);
+	vibrator->running = false;
+}
+
+static int gpio_vibrator_probe(struct platform_device *pdev)
+{
+	struct gpio_vibrator *vibrator;
+	int err;
+
+	vibrator = devm_kzalloc(&pdev->dev, sizeof(*vibrator), GFP_KERNEL);
+	if (!vibrator)
+		return -ENOMEM;
+
+	vibrator->input = devm_input_allocate_device(&pdev->dev);
+	if (!vibrator->input)
+		return -ENOMEM;
+
+	vibrator->vcc = devm_regulator_get(&pdev->dev, "vcc");
+	err = PTR_ERR_OR_ZERO(vibrator->vcc);
+	if (err) {
+		if (err != -EPROBE_DEFER)
+			dev_err(&pdev->dev, "Failed to request regulator: %d\n",
+				err);
+		return err;
+	}
+
+	vibrator->gpio = devm_gpiod_get(&pdev->dev, "enable", GPIOD_OUT_LOW);
+	err = PTR_ERR_OR_ZERO(vibrator->gpio);
+	if (err) {
+		if (err != -EPROBE_DEFER)
+			dev_err(&pdev->dev, "Failed to request main gpio: %d\n",
+				err);
+		return err;
+	}
+
+	INIT_WORK(&vibrator->play_work, gpio_vibrator_play_work);
+
+	vibrator->input->name = "gpio-vibrator";
+	vibrator->input->id.bustype = BUS_HOST;
+	vibrator->input->close = gpio_vibrator_close;
+
+	input_set_drvdata(vibrator->input, vibrator);
+	input_set_capability(vibrator->input, EV_FF, FF_RUMBLE);
+
+	err = input_ff_create_memless(vibrator->input, NULL,
+				      gpio_vibrator_play_effect);
+	if (err) {
+		dev_err(&pdev->dev, "Couldn't create FF dev: %d\n", err);
+		return err;
+	}
+
+	err = input_register_device(vibrator->input);
+	if (err) {
+		dev_err(&pdev->dev, "Couldn't register input dev: %d\n", err);
+		return err;
+	}
+
+	platform_set_drvdata(pdev, vibrator);
+
+	return 0;
+}
+
+static int __maybe_unused gpio_vibrator_suspend(struct device *dev)
+{
+	struct gpio_vibrator *vibrator = dev_get_drvdata(dev);
+
+	cancel_work_sync(&vibrator->play_work);
+	if (vibrator->running)
+		gpio_vibrator_stop(vibrator);
+
+	return 0;
+}
+
+static int __maybe_unused gpio_vibrator_resume(struct device *dev)
+{
+	struct gpio_vibrator *vibrator = dev_get_drvdata(dev);
+
+	if (vibrator->running)
+		gpio_vibrator_start(vibrator);
+
+	return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(gpio_vibrator_pm_ops,
+			 gpio_vibrator_suspend, gpio_vibrator_resume);
+
+#ifdef CONFIG_OF
+static const struct of_device_id gpio_vibra_dt_match_table[] = {
+	{ .compatible = "gpio-vibrator" },
+	{}
+};
+MODULE_DEVICE_TABLE(of, gpio_vibra_dt_match_table);
+#endif
+
+static struct platform_driver gpio_vibrator_driver = {
+	.probe	= gpio_vibrator_probe,
+	.driver	= {
+		.name	= "gpio-vibrator",
+		.pm	= &gpio_vibrator_pm_ops,
+		.of_match_table = of_match_ptr(gpio_vibra_dt_match_table),
+	},
+};
+module_platform_driver(gpio_vibrator_driver);
+
+MODULE_AUTHOR("Luca Weiss <luca@z3ntu.xy>");
+MODULE_DESCRIPTION("GPIO vibrator driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:gpio-vibrator");
-- 
2.21.0

^ permalink raw reply related

* [PATCH v4 3/3] ARM: dts: msm8974-FP2: Add vibration motor
From: Luca Weiss @ 2019-04-20 12:23 UTC (permalink / raw)
  Cc: Dmitry Torokhov, Rob Herring, Mark Rutland, Mauro Carvalho Chehab,
	Pascal PAILLET-LME, Coly Li, Lee Jones, Xiaotong Lu, Brian Masney,
	Rob Herring, Baolin Wang, David Brown,
	open list:ARM/QUALCOMM SUPPORT,
	open list:INPUT KEYBOARD, MOUSE, JOYSTICK , TOUCHSCREEN...,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	open list, Luca Weiss
In-Reply-To: <20190420122333.23662-1-luca@z3ntu.xyz>

Signed-off-by: Luca Weiss <luca@z3ntu.xyz>
---
 arch/arm/boot/dts/qcom-msm8974-fairphone-fp2.dts | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/arch/arm/boot/dts/qcom-msm8974-fairphone-fp2.dts b/arch/arm/boot/dts/qcom-msm8974-fairphone-fp2.dts
index 643c57f84818..bf402ae39226 100644
--- a/arch/arm/boot/dts/qcom-msm8974-fairphone-fp2.dts
+++ b/arch/arm/boot/dts/qcom-msm8974-fairphone-fp2.dts
@@ -50,6 +50,12 @@
 		};
 	};
 
+	vibrator {
+		compatible = "gpio-vibrator";
+		enable-gpios = <&msmgpio 86 GPIO_ACTIVE_HIGH>;
+		vcc-supply = <&pm8941_l18>;
+	};
+
 	smd {
 		rpm {
 			rpm_requests {
-- 
2.21.0

^ permalink raw reply related

* [PATCH 0/3] Apple iBridge support
From: Ronald Tschalär @ 2019-04-22  3:12 UTC (permalink / raw)
  To: Jiri Kosina, Benjamin Tissoires, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Lee Jones
  Cc: linux-input, linux-iio, linux-kernel

2016 and 2017 MacBook Pro's have a T1 chip that drives the Touch Bar,
ambient light sensor, webcam, and fingerprint sensor; this shows up
as an iBridge USB device in the system. These patches provide initial
support for the Touch Bar and ALS - the webcam is already handled by
existing drivers, and no information is currently known on how to access
the fingerprint sensor (other than it's apparently via one of the extra
interfaces available in the OS X USB configuration).

One thing of note here is that both the ALS and (some of) the Touch Bar
functionality are exposed via the same USB interface (and hence same
hid_device), so both drivers need to share this device. This
necessitated creating a demux hid driver in the mfd driver to which
multiple hid devices can be attached, and implied not being able to make
use of the existing hid-sensor-als driver.

Ronald Tschalär (3):
  mfd: apple-ibridge: Add Apple iBridge MFD driver.
  HID: apple-ib-tb: Add driver for the Touch Bar on MacBook Pro's.
  iio: light: apple-ib-als: Add driver for ALS on iBridge chip.

 drivers/hid/Kconfig               |   10 +
 drivers/hid/Makefile              |    1 +
 drivers/hid/apple-ib-tb.c         | 1288 +++++++++++++++++++++++++++++
 drivers/iio/light/Kconfig         |   12 +
 drivers/iio/light/Makefile        |    1 +
 drivers/iio/light/apple-ib-als.c  |  694 ++++++++++++++++
 drivers/mfd/Kconfig               |   15 +
 drivers/mfd/Makefile              |    1 +
 drivers/mfd/apple-ibridge.c       |  883 ++++++++++++++++++++
 include/linux/mfd/apple-ibridge.h |   39 +
 10 files changed, 2944 insertions(+)
 create mode 100644 drivers/hid/apple-ib-tb.c
 create mode 100644 drivers/iio/light/apple-ib-als.c
 create mode 100644 drivers/mfd/apple-ibridge.c
 create mode 100644 include/linux/mfd/apple-ibridge.h

-- 
2.20.1

^ permalink raw reply

* [PATCH 1/3] mfd: apple-ibridge: Add Apple iBridge MFD driver.
From: Ronald Tschalär @ 2019-04-22  3:12 UTC (permalink / raw)
  To: Jiri Kosina, Benjamin Tissoires, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Lee Jones
  Cc: linux-input, linux-iio, linux-kernel
In-Reply-To: <20190422031251.11968-1-ronald@innovation.ch>

The iBridge device provides access to several devices, including:
- the Touch Bar
- the iSight webcam
- the light sensor
- the fingerprint sensor

This driver provides the core support for managing the iBridge device
and the access to the underlying devices. In particular, since the
functionality for the touch bar and light sensor is exposed via USB HID
interfaces, and the same HID device is used for multiple functions, this
driver provides a multiplexing layer that allows multiple HID drivers to
be registered for a given HID device. This allows the touch bar and ALS
driver to be separated out into their own modules.

Signed-off-by: Ronald Tschalär <ronald@innovation.ch>
---
 drivers/mfd/Kconfig               |  15 +
 drivers/mfd/Makefile              |   1 +
 drivers/mfd/apple-ibridge.c       | 883 ++++++++++++++++++++++++++++++
 include/linux/mfd/apple-ibridge.h |  39 ++
 4 files changed, 938 insertions(+)
 create mode 100644 drivers/mfd/apple-ibridge.c
 create mode 100644 include/linux/mfd/apple-ibridge.h

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 76f9909cf396..d55fa77faacf 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1916,5 +1916,20 @@ config RAVE_SP_CORE
 	  Select this to get support for the Supervisory Processor
 	  device found on several devices in RAVE line of hardware.
 
+config MFD_APPLE_IBRIDGE
+	tristate "Apple iBridge chip"
+	depends on ACPI
+	depends on USB_HID
+	depends on X86 || COMPILE_TEST
+	select MFD_CORE
+	help
+	  This MFD provides the core support for the Apple iBridge chip
+	  found on recent MacBookPro's. The drivers for the Touch Bar
+	  (apple-ib-tb) and light sensor (apple-ib-als) need to be
+	  enabled separately.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called apple-ibridge.
+
 endmenu
 endif
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 12980a4ad460..c364e0e9d313 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -241,4 +241,5 @@ obj-$(CONFIG_MFD_MXS_LRADC)     += mxs-lradc.o
 obj-$(CONFIG_MFD_SC27XX_PMIC)	+= sprd-sc27xx-spi.o
 obj-$(CONFIG_RAVE_SP_CORE)	+= rave-sp.o
 obj-$(CONFIG_MFD_ROHM_BD718XX)	+= rohm-bd718x7.o
+obj-$(CONFIG_MFD_APPLE_IBRIDGE)	+= apple-ibridge.o
 
diff --git a/drivers/mfd/apple-ibridge.c b/drivers/mfd/apple-ibridge.c
new file mode 100644
index 000000000000..56d325396961
--- /dev/null
+++ b/drivers/mfd/apple-ibridge.c
@@ -0,0 +1,883 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Apple iBridge Driver
+ *
+ * Copyright (c) 2018 Ronald Tschalär
+ */
+
+/**
+ * MacBookPro models with a Touch Bar (13,[23] and 14,[23]) have an Apple
+ * iBridge chip (also known as T1 chip) which exposes the touch bar,
+ * built-in webcam (iSight), ambient light sensor, and Secure Enclave
+ * Processor (SEP) for TouchID. It shows up in the system as a USB device
+ * with 3 configurations: 'Default iBridge Interfaces', 'Default iBridge
+ * Interfaces(OS X)', and 'Default iBridge Interfaces(Recovery)'. While
+ * the second one is used by MacOS to provide the fancy touch bar
+ * functionality with custom buttons etc, this driver just uses the first.
+ *
+ * In the first (default after boot) configuration, 4 usb interfaces are
+ * exposed: 2 related to the webcam, and 2 USB HID interfaces representing
+ * the touch bar and the ambient light sensor (and possibly the SEP,
+ * though at this point in time nothing is known about that). The webcam
+ * interfaces are already handled by the uvcvideo driver; furthermore, the
+ * handling of the input reports when "keys" on the touch bar are pressed
+ * is already handled properly by the generic USB HID core. This leaves
+ * the management of the touch bar modes (e.g. switching between function
+ * and special keys when the FN key is pressed), the touch bar display
+ * (dimming and turning off), the key-remapping when the FN key is
+ * pressed, and handling of the light sensor.
+ *
+ * This driver is implemented as an MFD driver, with the touch bar and ALS
+ * functions implemented by appropriate subdrivers (mfd cells). Because
+ * both those are basically hid drivers, but the current kernel driver
+ * structure does not allow more than one driver per device, this driver
+ * implements a demuxer for hid drivers: it registers itself as a hid
+ * driver with the core, and in turn it lets the subdrivers register
+ * themselves as hid drivers with this driver; the callbacks from the core
+ * are then forwarded to the subdrivers.
+ *
+ * Lastly, this driver also takes care of the power-management for the
+ * iBridge when suspending and resuming.
+ */
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/list.h>
+#include <linux/mfd/apple-ibridge.h>
+#include <linux/mfd/core.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/rculist.h>
+#include <linux/slab.h>
+#include <linux/srcu.h>
+#include <linux/usb.h>
+
+#include "../hid/usbhid/usbhid.h"
+
+#define USB_ID_VENDOR_APPLE	0x05ac
+#define USB_ID_PRODUCT_IBRIDGE	0x8600
+
+#define APPLETB_BASIC_CONFIG	1
+
+#define	LOG_DEV(ib_dev)		(&(ib_dev)->acpi_dev->dev)
+
+struct appleib_device {
+	struct acpi_device	*acpi_dev;
+	acpi_handle		asoc_socw;
+	struct list_head	hid_drivers;
+	struct list_head	hid_devices;
+	struct mfd_cell		*subdevs;
+	struct mutex		update_lock; /* protect updates to all lists */
+	struct srcu_struct	lists_srcu;
+	bool			in_hid_probe;
+};
+
+struct appleib_hid_drv_info {
+	struct list_head	entry;
+	struct hid_driver	*driver;
+	void			*driver_data;
+};
+
+struct appleib_hid_dev_info {
+	struct list_head		entry;
+	struct list_head		drivers;
+	struct hid_device		*device;
+	const struct hid_device_id	*device_id;
+	bool				started;
+};
+
+static const struct mfd_cell appleib_subdevs[] = {
+	{ .name = PLAT_NAME_IB_TB },
+	{ .name = PLAT_NAME_IB_ALS },
+};
+
+static struct appleib_device *appleib_dev;
+
+#define	call_void_driver_func(drv_info, fn, ...)			\
+	do {								\
+		if ((drv_info)->driver->fn)				\
+			(drv_info)->driver->fn(__VA_ARGS__);		\
+	} while (0)
+
+#define	call_driver_func(drv_info, fn, ret_type, ...)			\
+	({								\
+		ret_type rc = 0;					\
+									\
+		if ((drv_info)->driver->fn)				\
+			rc = (drv_info)->driver->fn(__VA_ARGS__);	\
+									\
+		rc;							\
+	})
+
+static void appleib_remove_driver(struct appleib_device *ib_dev,
+				  struct appleib_hid_drv_info *drv_info,
+				  struct appleib_hid_dev_info *dev_info)
+{
+	list_del_rcu(&drv_info->entry);
+	synchronize_srcu(&ib_dev->lists_srcu);
+
+	call_void_driver_func(drv_info, remove, dev_info->device);
+
+	kfree(drv_info);
+}
+
+static int appleib_probe_driver(struct appleib_hid_drv_info *drv_info,
+				struct appleib_hid_dev_info *dev_info)
+{
+	struct appleib_hid_drv_info *d;
+	int rc = 0;
+
+	rc = call_driver_func(drv_info, probe, int, dev_info->device,
+			      dev_info->device_id);
+	if (rc)
+		return rc;
+
+	d = kmemdup(drv_info, sizeof(*drv_info), GFP_KERNEL);
+	if (!d) {
+		call_void_driver_func(drv_info, remove, dev_info->device);
+		return -ENOMEM;
+	}
+
+	list_add_tail_rcu(&d->entry, &dev_info->drivers);
+	return 0;
+}
+
+static void appleib_remove_driver_attachments(struct appleib_device *ib_dev,
+					struct appleib_hid_dev_info *dev_info,
+					struct hid_driver *driver)
+{
+	struct appleib_hid_drv_info *drv_info;
+	struct appleib_hid_drv_info *tmp;
+
+	list_for_each_entry_safe(drv_info, tmp, &dev_info->drivers, entry) {
+		if (!driver || drv_info->driver == driver)
+			appleib_remove_driver(ib_dev, drv_info, dev_info);
+	}
+}
+
+/*
+ * Find all devices that are attached to this driver and detach them.
+ *
+ * Note: this must be run with update_lock held.
+ */
+static void appleib_detach_devices(struct appleib_device *ib_dev,
+				   struct hid_driver *driver)
+{
+	struct appleib_hid_dev_info *dev_info;
+
+	list_for_each_entry(dev_info, &ib_dev->hid_devices, entry)
+		appleib_remove_driver_attachments(ib_dev, dev_info, driver);
+}
+
+/*
+ * Find all drivers that are attached to this device and detach them.
+ *
+ * Note: this must be run with update_lock held.
+ */
+static void appleib_detach_drivers(struct appleib_device *ib_dev,
+				   struct appleib_hid_dev_info *dev_info)
+{
+	appleib_remove_driver_attachments(ib_dev, dev_info, NULL);
+}
+
+/**
+ * Unregister a previously registered HID driver from us.
+ * @ib_dev: the appleib_device from which to unregister the driver
+ * @driver: the driver to unregister
+ */
+int appleib_unregister_hid_driver(struct appleib_device *ib_dev,
+				  struct hid_driver *driver)
+{
+	struct appleib_hid_drv_info *drv_info;
+
+	mutex_lock(&ib_dev->update_lock);
+
+	list_for_each_entry(drv_info, &ib_dev->hid_drivers, entry) {
+		if (drv_info->driver == driver) {
+			appleib_detach_devices(ib_dev, driver);
+			list_del_rcu(&drv_info->entry);
+			mutex_unlock(&ib_dev->update_lock);
+			synchronize_srcu(&ib_dev->lists_srcu);
+			kfree(drv_info);
+			dev_info(LOG_DEV(ib_dev), "unregistered driver '%s'\n",
+				 driver->name);
+			return 0;
+		}
+	}
+
+	mutex_unlock(&ib_dev->update_lock);
+
+	return -ENOENT;
+}
+EXPORT_SYMBOL_GPL(appleib_unregister_hid_driver);
+
+static int appleib_start_hid_events(struct appleib_hid_dev_info *dev_info)
+{
+	struct hid_device *hdev = dev_info->device;
+	int rc;
+
+	rc = hid_connect(hdev, HID_CONNECT_DEFAULT);
+	if (rc) {
+		hid_err(hdev, "ib: hid connect failed (%d)\n", rc);
+		return rc;
+	}
+
+	rc = hid_hw_open(hdev);
+	if (rc) {
+		hid_err(hdev, "ib: failed to open hid: %d\n", rc);
+		hid_disconnect(hdev);
+	}
+
+	if (!rc)
+		dev_info->started = true;
+
+	return rc;
+}
+
+static void appleib_stop_hid_events(struct appleib_hid_dev_info *dev_info)
+{
+	if (dev_info->started) {
+		hid_hw_close(dev_info->device);
+		hid_disconnect(dev_info->device);
+		dev_info->started = false;
+	}
+}
+
+/**
+ * Register a HID driver with us.
+ * @ib_dev: the appleib_device with which to register the driver
+ * @driver: the driver to register
+ * @data: the driver-data to associate with the driver; this is available
+ *        from appleib_get_drvdata(...).
+ */
+int appleib_register_hid_driver(struct appleib_device *ib_dev,
+				struct hid_driver *driver, void *data)
+{
+	struct appleib_hid_drv_info *drv_info;
+	struct appleib_hid_dev_info *dev_info;
+	int rc;
+
+	if (!driver->probe)
+		return -EINVAL;
+
+	drv_info = kzalloc(sizeof(*drv_info), GFP_KERNEL);
+	if (!drv_info)
+		return -ENOMEM;
+
+	drv_info->driver = driver;
+	drv_info->driver_data = data;
+
+	mutex_lock(&ib_dev->update_lock);
+
+	list_add_tail_rcu(&drv_info->entry, &ib_dev->hid_drivers);
+
+	list_for_each_entry(dev_info, &ib_dev->hid_devices, entry) {
+		appleib_stop_hid_events(dev_info);
+
+		appleib_probe_driver(drv_info, dev_info);
+
+		rc = appleib_start_hid_events(dev_info);
+		if (rc)
+			appleib_detach_drivers(ib_dev, dev_info);
+	}
+
+	mutex_unlock(&ib_dev->update_lock);
+
+	dev_info(LOG_DEV(ib_dev), "registered driver '%s'\n", driver->name);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(appleib_register_hid_driver);
+
+/**
+ * Get the driver-specific data associated with the given, previously
+ * registered HID driver (provided in the appleib_register_hid_driver()
+ * call).
+ * @ib_dev: the appleib_device with which the driver was registered
+ * @driver: the driver for which to get the data
+ */
+void *appleib_get_drvdata(struct appleib_device *ib_dev,
+			  struct hid_driver *driver)
+{
+	struct appleib_hid_drv_info *drv_info;
+	void *drv_data = NULL;
+	int idx;
+
+	idx = srcu_read_lock(&ib_dev->lists_srcu);
+
+	list_for_each_entry_rcu(drv_info, &ib_dev->hid_drivers, entry) {
+		if (drv_info->driver == driver) {
+			drv_data = drv_info->driver_data;
+			break;
+		}
+	}
+
+	srcu_read_unlock(&ib_dev->lists_srcu, idx);
+
+	return drv_data;
+}
+EXPORT_SYMBOL_GPL(appleib_get_drvdata);
+
+/*
+ * Forward a hid-driver callback to all registered sub-drivers. This is for
+ * callbacks that return a status as an int.
+ * @hdev the hid-device
+ * @forward a function that calls the callback on the given driver
+ * @args arguments for the forward function
+ */
+static int appleib_forward_int_op(struct hid_device *hdev,
+				  int (*forward)(struct appleib_hid_drv_info *,
+						 struct hid_device *, void *),
+				  void *args)
+{
+	struct appleib_device *ib_dev = hid_get_drvdata(hdev);
+	struct appleib_hid_dev_info *dev_info;
+	struct appleib_hid_drv_info *drv_info;
+	int idx;
+	int rc = 0;
+
+	idx = srcu_read_lock(&ib_dev->lists_srcu);
+
+	list_for_each_entry_rcu(dev_info, &ib_dev->hid_devices, entry) {
+		if (dev_info->device != hdev)
+			continue;
+
+		list_for_each_entry_rcu(drv_info, &dev_info->drivers, entry) {
+			rc = forward(drv_info, hdev, args);
+			if (rc)
+				break;
+		}
+
+		break;
+	}
+
+	srcu_read_unlock(&ib_dev->lists_srcu, idx);
+
+	return rc;
+}
+
+struct appleib_hid_event_args {
+	struct hid_field *field;
+	struct hid_usage *usage;
+	__s32 value;
+};
+
+static int appleib_hid_event_fwd(struct appleib_hid_drv_info *drv_info,
+				 struct hid_device *hdev, void *args)
+{
+	struct appleib_hid_event_args *evt_args = args;
+
+	return call_driver_func(drv_info, event, int, hdev, evt_args->field,
+				evt_args->usage, evt_args->value);
+}
+
+static int appleib_hid_event(struct hid_device *hdev, struct hid_field *field,
+			     struct hid_usage *usage, __s32 value)
+{
+	struct appleib_hid_event_args args = {
+		.field = field,
+		.usage = usage,
+		.value = value,
+	};
+
+	return appleib_forward_int_op(hdev, appleib_hid_event_fwd, &args);
+}
+
+static __u8 *appleib_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+				  unsigned int *rsize)
+{
+	/* Some fields have a size of 64 bits, which according to HID 1.11
+	 * Section 8.4 is not valid ("An item field cannot span more than 4
+	 * bytes in a report"). Furthermore, hid_field_extract() complains
+	 * when encountering such a field. So turn them into two 32-bit fields
+	 * instead.
+	 */
+
+	if (*rsize == 634 &&
+	    /* Usage Page 0xff12 (vendor defined) */
+	    rdesc[212] == 0x06 && rdesc[213] == 0x12 && rdesc[214] == 0xff &&
+	    /* Usage 0x51 */
+	    rdesc[416] == 0x09 && rdesc[417] == 0x51 &&
+	    /* report size 64 */
+	    rdesc[432] == 0x75 && rdesc[433] == 64 &&
+	    /* report count 1 */
+	    rdesc[434] == 0x95 && rdesc[435] == 1) {
+		rdesc[433] = 32;
+		rdesc[435] = 2;
+		hid_dbg(hdev, "Fixed up first 64-bit field\n");
+	}
+
+	if (*rsize == 634 &&
+	    /* Usage Page 0xff12 (vendor defined) */
+	    rdesc[212] == 0x06 && rdesc[213] == 0x12 && rdesc[214] == 0xff &&
+	    /* Usage 0x51 */
+	    rdesc[611] == 0x09 && rdesc[612] == 0x51 &&
+	    /* report size 64 */
+	    rdesc[627] == 0x75 && rdesc[628] == 64 &&
+	    /* report count 1 */
+	    rdesc[629] == 0x95 && rdesc[630] == 1) {
+		rdesc[628] = 32;
+		rdesc[630] = 2;
+		hid_dbg(hdev, "Fixed up second 64-bit field\n");
+	}
+
+	return rdesc;
+}
+
+static int appleib_input_configured_fwd(struct appleib_hid_drv_info *drv_info,
+					struct hid_device *hdev, void *args)
+{
+	return call_driver_func(drv_info, input_configured, int, hdev,
+				(struct hid_input *)args);
+}
+
+static int appleib_input_configured(struct hid_device *hdev,
+				    struct hid_input *hidinput)
+{
+	return appleib_forward_int_op(hdev, appleib_input_configured_fwd,
+				      hidinput);
+}
+
+#ifdef CONFIG_PM
+static int appleib_hid_suspend_fwd(struct appleib_hid_drv_info *drv_info,
+				   struct hid_device *hdev, void *args)
+{
+	return call_driver_func(drv_info, suspend, int, hdev,
+				*(pm_message_t *)args);
+}
+
+static int appleib_hid_suspend(struct hid_device *hdev, pm_message_t message)
+{
+	return appleib_forward_int_op(hdev, appleib_hid_suspend_fwd, &message);
+}
+
+static int appleib_hid_resume_fwd(struct appleib_hid_drv_info *drv_info,
+				  struct hid_device *hdev, void *args)
+{
+	return call_driver_func(drv_info, resume, int, hdev);
+}
+
+static int appleib_hid_resume(struct hid_device *hdev)
+{
+	return appleib_forward_int_op(hdev, appleib_hid_resume_fwd, NULL);
+}
+
+static int appleib_hid_reset_resume_fwd(struct appleib_hid_drv_info *drv_info,
+					struct hid_device *hdev, void *args)
+{
+	return call_driver_func(drv_info, reset_resume, int, hdev);
+}
+
+static int appleib_hid_reset_resume(struct hid_device *hdev)
+{
+	return appleib_forward_int_op(hdev, appleib_hid_reset_resume_fwd, NULL);
+}
+#endif /* CONFIG_PM */
+
+/**
+ * Find the field in the report with the given usage.
+ * @report: the report to search
+ * @field_usage: the usage of the field to search for
+ */
+struct hid_field *appleib_find_report_field(struct hid_report *report,
+					    unsigned int field_usage)
+{
+	int f, u;
+
+	for (f = 0; f < report->maxfield; f++) {
+		struct hid_field *field = report->field[f];
+
+		if (field->logical == field_usage)
+			return field;
+
+		for (u = 0; u < field->maxusage; u++) {
+			if (field->usage[u].hid == field_usage)
+				return field;
+		}
+	}
+
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(appleib_find_report_field);
+
+/**
+ * Search all the reports of the device for the field with the given usage.
+ * @hdev: the device whose reports to search
+ * @application: the usage of application collection that the field must
+ *               belong to
+ * @field_usage: the usage of the field to search for
+ */
+struct hid_field *appleib_find_hid_field(struct hid_device *hdev,
+					 unsigned int application,
+					 unsigned int field_usage)
+{
+	static const int report_types[] = { HID_INPUT_REPORT, HID_OUTPUT_REPORT,
+					    HID_FEATURE_REPORT };
+	struct hid_report *report;
+	struct hid_field *field;
+	int t;
+
+	for (t = 0; t < ARRAY_SIZE(report_types); t++) {
+		struct list_head *report_list =
+			    &hdev->report_enum[report_types[t]].report_list;
+		list_for_each_entry(report, report_list, list) {
+			if (report->application != application)
+				continue;
+
+			field = appleib_find_report_field(report, field_usage);
+			if (field)
+				return field;
+		}
+	}
+
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(appleib_find_hid_field);
+
+/**
+ * Return whether we're currently inside a hid_device_probe or not.
+ * @ib_dev: the appleib_device
+ */
+bool appleib_in_hid_probe(struct appleib_device *ib_dev)
+{
+	return ib_dev->in_hid_probe;
+}
+EXPORT_SYMBOL_GPL(appleib_in_hid_probe);
+
+static struct appleib_hid_dev_info *
+appleib_add_device(struct appleib_device *ib_dev, struct hid_device *hdev,
+		   const struct hid_device_id *id)
+{
+	struct appleib_hid_dev_info *dev_info;
+	struct appleib_hid_drv_info *drv_info;
+
+	/* allocate device-info for this device */
+	dev_info = kzalloc(sizeof(*dev_info), GFP_KERNEL);
+	if (!dev_info)
+		return NULL;
+
+	INIT_LIST_HEAD(&dev_info->drivers);
+	dev_info->device = hdev;
+	dev_info->device_id = id;
+
+	/* notify all our sub drivers */
+	mutex_lock(&ib_dev->update_lock);
+
+	ib_dev->in_hid_probe = true;
+
+	list_for_each_entry(drv_info, &ib_dev->hid_drivers, entry) {
+		appleib_probe_driver(drv_info, dev_info);
+	}
+
+	ib_dev->in_hid_probe = false;
+
+	/* remember this new device */
+	list_add_tail_rcu(&dev_info->entry, &ib_dev->hid_devices);
+
+	mutex_unlock(&ib_dev->update_lock);
+
+	return dev_info;
+}
+
+static void appleib_remove_device(struct appleib_device *ib_dev,
+				  struct appleib_hid_dev_info *dev_info)
+{
+	list_del_rcu(&dev_info->entry);
+	synchronize_srcu(&ib_dev->lists_srcu);
+
+	appleib_detach_drivers(ib_dev, dev_info);
+
+	kfree(dev_info);
+}
+
+static int appleib_hid_probe(struct hid_device *hdev,
+			     const struct hid_device_id *id)
+{
+	struct appleib_device *ib_dev;
+	struct appleib_hid_dev_info *dev_info;
+	struct usb_device *udev;
+	int rc;
+
+	/* check usb config first */
+	udev = hid_to_usb_dev(hdev);
+
+	if (udev->actconfig->desc.bConfigurationValue != APPLETB_BASIC_CONFIG) {
+		rc = usb_driver_set_configuration(udev, APPLETB_BASIC_CONFIG);
+		return rc ? rc : -ENODEV;
+	}
+
+	/* Assign the driver data */
+	ib_dev = appleib_dev;
+	hid_set_drvdata(hdev, ib_dev);
+
+	/* initialize the report info */
+	rc = hid_parse(hdev);
+	if (rc) {
+		hid_err(hdev, "ib: hid parse failed (%d)\n", rc);
+		goto error;
+	}
+
+	/* alloc bufs etc so probe's can send requests; but connect later */
+	rc = hid_hw_start(hdev, 0);
+	if (rc) {
+		hid_err(hdev, "ib: hw start failed (%d)\n", rc);
+		goto error;
+	}
+
+	/* add this hdev to our device list */
+	dev_info = appleib_add_device(ib_dev, hdev, id);
+	if (!dev_info) {
+		rc = -ENOMEM;
+		goto stop_hw;
+	}
+
+	/* start the hid */
+	rc = appleib_start_hid_events(dev_info);
+	if (rc)
+		goto remove_dev;
+
+	return 0;
+
+remove_dev:
+	mutex_lock(&ib_dev->update_lock);
+	appleib_remove_device(ib_dev, dev_info);
+	mutex_unlock(&ib_dev->update_lock);
+stop_hw:
+	hid_hw_stop(hdev);
+error:
+	return rc;
+}
+
+static void appleib_hid_remove(struct hid_device *hdev)
+{
+	struct appleib_device *ib_dev = hid_get_drvdata(hdev);
+	struct appleib_hid_dev_info *dev_info;
+
+	mutex_lock(&ib_dev->update_lock);
+
+	list_for_each_entry(dev_info, &ib_dev->hid_devices, entry) {
+		if (dev_info->device == hdev) {
+			appleib_stop_hid_events(dev_info);
+			appleib_remove_device(ib_dev, dev_info);
+			break;
+		}
+	}
+
+	mutex_unlock(&ib_dev->update_lock);
+
+	hid_hw_stop(hdev);
+}
+
+static const struct hid_device_id appleib_hid_devices[] = {
+	{ HID_USB_DEVICE(USB_ID_VENDOR_APPLE, USB_ID_PRODUCT_IBRIDGE) },
+	{ },
+};
+
+static struct hid_driver appleib_hid_driver = {
+	.name = "apple-ibridge-hid",
+	.id_table = appleib_hid_devices,
+	.probe = appleib_hid_probe,
+	.remove = appleib_hid_remove,
+	.event = appleib_hid_event,
+	.report_fixup = appleib_report_fixup,
+	.input_configured = appleib_input_configured,
+#ifdef CONFIG_PM
+	.suspend = appleib_hid_suspend,
+	.resume = appleib_hid_resume,
+	.reset_resume = appleib_hid_reset_resume,
+#endif
+};
+
+static struct appleib_device *appleib_alloc_device(struct acpi_device *acpi_dev)
+{
+	struct appleib_device *ib_dev;
+	acpi_status sts;
+	int rc;
+
+	/* allocate */
+	ib_dev = kzalloc(sizeof(*ib_dev), GFP_KERNEL);
+	if (!ib_dev)
+		return ERR_PTR(-ENOMEM);
+
+	/* init structures */
+	INIT_LIST_HEAD(&ib_dev->hid_drivers);
+	INIT_LIST_HEAD(&ib_dev->hid_devices);
+	mutex_init(&ib_dev->update_lock);
+	init_srcu_struct(&ib_dev->lists_srcu);
+
+	ib_dev->acpi_dev = acpi_dev;
+
+	/* get iBridge acpi power control method */
+	sts = acpi_get_handle(acpi_dev->handle, "SOCW", &ib_dev->asoc_socw);
+	if (ACPI_FAILURE(sts)) {
+		dev_err(LOG_DEV(ib_dev),
+			"Error getting handle for ASOC.SOCW method: %s\n",
+			acpi_format_exception(sts));
+		rc = -ENXIO;
+		goto free_mem;
+	}
+
+	/* ensure iBridge is powered on */
+	sts = acpi_execute_simple_method(ib_dev->asoc_socw, NULL, 1);
+	if (ACPI_FAILURE(sts))
+		dev_warn(LOG_DEV(ib_dev), "SOCW(1) failed: %s\n",
+			 acpi_format_exception(sts));
+
+	return ib_dev;
+
+free_mem:
+	kfree(ib_dev);
+	return ERR_PTR(rc);
+}
+
+static int appleib_probe(struct acpi_device *acpi)
+{
+	struct appleib_device *ib_dev;
+	struct appleib_platform_data *pdata;
+	int i;
+	int ret;
+
+	if (appleib_dev)
+		return -EBUSY;
+
+	ib_dev = appleib_alloc_device(acpi);
+	if (IS_ERR_OR_NULL(ib_dev))
+		return PTR_ERR(ib_dev);
+
+	ib_dev->subdevs = kmemdup(appleib_subdevs, sizeof(appleib_subdevs),
+				  GFP_KERNEL);
+	if (!ib_dev->subdevs) {
+		ret = -ENOMEM;
+		goto free_dev;
+	}
+
+	pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+	if (!pdata) {
+		ret = -ENOMEM;
+		goto free_subdevs;
+	}
+
+	pdata->ib_dev = ib_dev;
+	pdata->log_dev = LOG_DEV(ib_dev);
+	for (i = 0; i < ARRAY_SIZE(appleib_subdevs); i++) {
+		ib_dev->subdevs[i].platform_data = pdata;
+		ib_dev->subdevs[i].pdata_size = sizeof(*pdata);
+	}
+
+	ret = mfd_add_devices(&acpi->dev, PLATFORM_DEVID_NONE,
+			      ib_dev->subdevs, ARRAY_SIZE(appleib_subdevs),
+			      NULL, 0, NULL);
+	if (ret) {
+		dev_err(LOG_DEV(ib_dev), "Error adding MFD devices: %d\n", ret);
+		goto free_pdata;
+	}
+
+	acpi->driver_data = ib_dev;
+	appleib_dev = ib_dev;
+
+	ret = hid_register_driver(&appleib_hid_driver);
+	if (ret) {
+		dev_err(LOG_DEV(ib_dev), "Error registering hid driver: %d\n",
+			ret);
+		goto rem_mfd_devs;
+	}
+
+	return 0;
+
+rem_mfd_devs:
+	mfd_remove_devices(&acpi->dev);
+free_pdata:
+	kfree(pdata);
+free_subdevs:
+	kfree(ib_dev->subdevs);
+free_dev:
+	appleib_dev = NULL;
+	acpi->driver_data = NULL;
+	kfree(ib_dev);
+	return ret;
+}
+
+static int appleib_remove(struct acpi_device *acpi)
+{
+	struct appleib_device *ib_dev = acpi_driver_data(acpi);
+
+	mfd_remove_devices(&acpi->dev);
+	hid_unregister_driver(&appleib_hid_driver);
+
+	if (appleib_dev == ib_dev)
+		appleib_dev = NULL;
+
+	kfree(ib_dev->subdevs[0].platform_data);
+	kfree(ib_dev->subdevs);
+	kfree(ib_dev);
+
+	return 0;
+}
+
+static int appleib_suspend(struct device *dev)
+{
+	struct acpi_device *adev;
+	struct appleib_device *ib_dev;
+	int rc;
+
+	adev = to_acpi_device(dev);
+	ib_dev = acpi_driver_data(adev);
+
+	rc = acpi_execute_simple_method(ib_dev->asoc_socw, NULL, 0);
+	if (ACPI_FAILURE(rc))
+		dev_warn(LOG_DEV(ib_dev), "SOCW(0) failed: %s\n",
+			 acpi_format_exception(rc));
+
+	return 0;
+}
+
+static int appleib_resume(struct device *dev)
+{
+	struct acpi_device *adev;
+	struct appleib_device *ib_dev;
+	int rc;
+
+	adev = to_acpi_device(dev);
+	ib_dev = acpi_driver_data(adev);
+
+	rc = acpi_execute_simple_method(ib_dev->asoc_socw, NULL, 1);
+	if (ACPI_FAILURE(rc))
+		dev_warn(LOG_DEV(ib_dev), "SOCW(1) failed: %s\n",
+			 acpi_format_exception(rc));
+
+	return 0;
+}
+
+static const struct dev_pm_ops appleib_pm = {
+	.suspend = appleib_suspend,
+	.resume = appleib_resume,
+	.restore = appleib_resume,
+};
+
+static const struct acpi_device_id appleib_acpi_match[] = {
+	{ "APP7777", 0 },
+	{ },
+};
+
+MODULE_DEVICE_TABLE(acpi, appleib_acpi_match);
+
+static struct acpi_driver appleib_driver = {
+	.name		= "apple-ibridge",
+	.class		= "topcase", /* ? */
+	.owner		= THIS_MODULE,
+	.ids		= appleib_acpi_match,
+	.ops		= {
+		.add		= appleib_probe,
+		.remove		= appleib_remove,
+	},
+	.drv		= {
+		.pm		= &appleib_pm,
+	},
+};
+
+module_acpi_driver(appleib_driver)
+
+MODULE_AUTHOR("Ronald Tschalär");
+MODULE_DESCRIPTION("Apple iBridge driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mfd/apple-ibridge.h b/include/linux/mfd/apple-ibridge.h
new file mode 100644
index 000000000000..d321714767f7
--- /dev/null
+++ b/include/linux/mfd/apple-ibridge.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Apple iBridge Driver
+ *
+ * Copyright (c) 2018 Ronald Tschalär
+ */
+
+#ifndef __LINUX_MFD_APPLE_IBRDIGE_H
+#define __LINUX_MFD_APPLE_IBRDIGE_H
+
+#include <linux/device.h>
+#include <linux/hid.h>
+
+#define PLAT_NAME_IB_TB		"apple-ib-tb"
+#define PLAT_NAME_IB_ALS	"apple-ib-als"
+
+struct appleib_device;
+
+struct appleib_platform_data {
+	struct appleib_device *ib_dev;
+	struct device *log_dev;
+};
+
+int appleib_register_hid_driver(struct appleib_device *ib_dev,
+				struct hid_driver *driver, void *data);
+int appleib_unregister_hid_driver(struct appleib_device *ib_dev,
+				  struct hid_driver *driver);
+
+void *appleib_get_drvdata(struct appleib_device *ib_dev,
+			  struct hid_driver *driver);
+bool appleib_in_hid_probe(struct appleib_device *ib_dev);
+
+struct hid_field *appleib_find_report_field(struct hid_report *report,
+					    unsigned int field_usage);
+struct hid_field *appleib_find_hid_field(struct hid_device *hdev,
+					 unsigned int application,
+					 unsigned int field_usage);
+
+#endif
-- 
2.20.1

^ permalink raw reply related

* [PATCH 2/3] HID: apple-ib-tb: Add driver for the Touch Bar on MacBook Pro's.
From: Ronald Tschalär @ 2019-04-22  3:12 UTC (permalink / raw)
  To: Jiri Kosina, Benjamin Tissoires, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Lee Jones
  Cc: linux-input, linux-iio, linux-kernel
In-Reply-To: <20190422031251.11968-1-ronald@innovation.ch>

This driver enables basic touch bar functionality: enabling it, switching
between modes on FN key press, and dimming and turning the display
off/on when idle/active.

Signed-off-by: Ronald Tschalär <ronald@innovation.ch>
---
 drivers/hid/Kconfig       |   10 +
 drivers/hid/Makefile      |    1 +
 drivers/hid/apple-ib-tb.c | 1288 +++++++++++++++++++++++++++++++++++++
 3 files changed, 1299 insertions(+)
 create mode 100644 drivers/hid/apple-ib-tb.c

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 41e9935fc584..f0a65bb4be6e 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -135,6 +135,16 @@ config HID_APPLE
 	Say Y here if you want support for keyboards of	Apple iBooks, PowerBooks,
 	MacBooks, MacBook Pros and Apple Aluminum.
 
+config HID_APPLE_IBRIDGE_TB
+	tristate "Apple iBridge Touch Bar"
+	depends on MFD_APPLE_IBRIDGE
+	---help---
+	Say Y here if you want support for the Touch Bar on recent
+	MacBook Pros.
+
+	To compile this driver as a module, choose M here: the
+	module will be called apple-ib-tb.
+
 config HID_APPLEIR
 	tristate "Apple infrared receiver"
 	depends on (USB_HID)
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 896a51ce7ce0..dedd8049d3fb 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_HID_ACCUTOUCH)	+= hid-accutouch.o
 obj-$(CONFIG_HID_ALPS)		+= hid-alps.o
 obj-$(CONFIG_HID_ACRUX)		+= hid-axff.o
 obj-$(CONFIG_HID_APPLE)		+= hid-apple.o
+obj-$(CONFIG_HID_APPLE_IBRIDGE_TB)	+= apple-ib-tb.o
 obj-$(CONFIG_HID_APPLEIR)	+= hid-appleir.o
 obj-$(CONFIG_HID_ASUS)		+= hid-asus.o
 obj-$(CONFIG_HID_AUREAL)	+= hid-aureal.o
diff --git a/drivers/hid/apple-ib-tb.c b/drivers/hid/apple-ib-tb.c
new file mode 100644
index 000000000000..6b72ff56b17f
--- /dev/null
+++ b/drivers/hid/apple-ib-tb.c
@@ -0,0 +1,1288 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Apple Touch Bar Driver
+ *
+ * Copyright (c) 2017-2018 Ronald Tschalär
+ */
+
+/*
+ * Recent MacBookPro models (13,[23] and 14,[23]) have a touch bar, which
+ * is exposed via several USB interfaces. MacOS supports a fancy mode
+ * where arbitrary buttons can be defined; this driver currently only
+ * supports the simple mode that consists of 3 predefined layouts
+ * (escape-only, esc + special keys, and esc + function keys).
+ *
+ * The first USB HID interface supports two reports, an input report that
+ * is used to report the key presses, and an output report which can be
+ * used to set the touch bar "mode": touch bar off (in which case no touches
+ * are reported at all), escape key only, escape + 12 function keys, and
+ * escape + several special keys (including brightness, audio volume,
+ * etc). The second interface supports several, complex reports, most of
+ * which are unknown at this time, but one of which has been determined to
+ * allow for controlling of the touch bar's brightness: off (though touches
+ * are still reported), dimmed, and full brightness. This driver makes
+ * use of these two reports.
+ */
+
+#define dev_fmt(fmt) "tb: " fmt
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/input.h>
+#include <linux/jiffies.h>
+#include <linux/ktime.h>
+#include <linux/mfd/apple-ibridge.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/sysfs.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+
+#define HID_UP_APPLE		0xff120000
+#define HID_USAGE_MODE		(HID_UP_CUSTOM | 0x0004)
+#define HID_USAGE_APPLE_APP	(HID_UP_APPLE  | 0x0001)
+#define HID_USAGE_DISP		(HID_UP_APPLE  | 0x0021)
+
+#define APPLETB_MAX_TB_KEYS	13	/* ESC, F1-F12 */
+
+#define APPLETB_CMD_MODE_ESC	0
+#define APPLETB_CMD_MODE_FN	1
+#define APPLETB_CMD_MODE_SPCL	2
+#define APPLETB_CMD_MODE_OFF	3
+#define APPLETB_CMD_MODE_NONE	255
+
+#define APPLETB_CMD_DISP_ON	1
+#define APPLETB_CMD_DISP_DIM	2
+#define APPLETB_CMD_DISP_OFF	4
+#define APPLETB_CMD_DISP_NONE	255
+
+#define APPLETB_FN_MODE_FKEYS	0
+#define APPLETB_FN_MODE_NORM	1
+#define APPLETB_FN_MODE_INV	2
+#define APPLETB_FN_MODE_SPCL	3
+#define APPLETB_FN_MODE_MAX	APPLETB_FN_MODE_SPCL
+
+#define APPLETB_DEVID_KEYBOARD	1
+#define APPLETB_DEVID_TOUCHPAD	2
+
+#define APPLETB_MAX_DIM_TIME	30
+
+static int appletb_tb_def_idle_timeout = 5 * 60;
+module_param_named(idle_timeout, appletb_tb_def_idle_timeout, int, 0444);
+MODULE_PARM_DESC(idle_timeout, "Default touch bar idle timeout (in seconds); 0 disables touch bar, -1 disables timeout");
+
+static int appletb_tb_def_dim_timeout = -2;
+module_param_named(dim_timeout, appletb_tb_def_dim_timeout, int, 0444);
+MODULE_PARM_DESC(dim_timeout, "Default touch bar dim timeout (in seconds); 0 means always dimmmed, -1 disables dimming, [-2] calculates timeout based on idle-timeout");
+
+static int appletb_tb_def_fn_mode = APPLETB_FN_MODE_NORM;
+module_param_named(fnmode, appletb_tb_def_fn_mode, int, 0444);
+MODULE_PARM_DESC(fnmode, "Default Fn key mode: 0 = f-keys only, [1] = fn key switches from special to f-keys, 2 = inverse of 1, 3 = special keys only");
+
+static ssize_t idle_timeout_show(struct device *dev,
+				 struct device_attribute *attr, char *buf);
+static ssize_t idle_timeout_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t size);
+static DEVICE_ATTR_RW(idle_timeout);
+
+static ssize_t dim_timeout_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
+static ssize_t dim_timeout_store(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf, size_t size);
+static DEVICE_ATTR_RW(dim_timeout);
+
+static ssize_t fnmode_show(struct device *dev, struct device_attribute *attr,
+			   char *buf);
+static ssize_t fnmode_store(struct device *dev, struct device_attribute *attr,
+			    const char *buf, size_t size);
+static DEVICE_ATTR_RW(fnmode);
+
+static struct attribute *appletb_attrs[] = {
+	&dev_attr_idle_timeout.attr,
+	&dev_attr_dim_timeout.attr,
+	&dev_attr_fnmode.attr,
+	NULL,
+};
+
+static const struct attribute_group appletb_attr_group = {
+	.attrs = appletb_attrs,
+};
+
+struct appletb_device {
+	bool			active;
+	struct device		*log_dev;
+
+	struct appletb_report_info {
+		struct hid_device	*hdev;
+		struct usb_interface	*usb_iface;
+		unsigned int		usb_epnum;
+		unsigned int		report_id;
+		unsigned int		report_type;
+		bool			suspended;
+	}			mode_info, disp_info;
+
+	struct input_handler	inp_handler;
+	struct input_handle	kbd_handle;
+	struct input_handle	tpd_handle;
+
+	bool			last_tb_keys_pressed[APPLETB_MAX_TB_KEYS];
+	bool			last_tb_keys_translated[APPLETB_MAX_TB_KEYS];
+	bool			last_fn_pressed;
+
+	ktime_t			last_event_time;
+
+	unsigned char		cur_tb_mode;
+	unsigned char		pnd_tb_mode;
+	unsigned char		cur_tb_disp;
+	unsigned char		pnd_tb_disp;
+	bool			tb_autopm_off;
+	bool			restore_autopm;
+	struct delayed_work	tb_work;
+	/* protects most of the above */
+	spinlock_t		tb_lock;
+
+	int			dim_timeout;
+	int			idle_timeout;
+	bool			dim_to_is_calc;
+	int			fn_mode;
+};
+
+struct appletb_key_translation {
+	u16 from;
+	u16 to;
+};
+
+static const struct appletb_key_translation appletb_fn_codes[] = {
+	{ KEY_F1,  KEY_BRIGHTNESSDOWN },
+	{ KEY_F2,  KEY_BRIGHTNESSUP },
+	{ KEY_F3,  KEY_SCALE },		/* not used */
+	{ KEY_F4,  KEY_DASHBOARD },	/* not used */
+	{ KEY_F5,  KEY_KBDILLUMDOWN },
+	{ KEY_F6,  KEY_KBDILLUMUP },
+	{ KEY_F7,  KEY_PREVIOUSSONG },
+	{ KEY_F8,  KEY_PLAYPAUSE },
+	{ KEY_F9,  KEY_NEXTSONG },
+	{ KEY_F10, KEY_MUTE },
+	{ KEY_F11, KEY_VOLUMEDOWN },
+	{ KEY_F12, KEY_VOLUMEUP },
+};
+
+static struct hid_driver appletb_hid_driver;
+
+static int appletb_send_hid_report(struct appletb_report_info *rinfo,
+				   __u8 requesttype, void *data, __u16 size)
+{
+	void *buffer;
+	struct usb_device *dev = interface_to_usbdev(rinfo->usb_iface);
+	u8 ifnum = rinfo->usb_iface->cur_altsetting->desc.bInterfaceNumber;
+	int tries = 0;
+	int rc;
+
+	buffer = kmemdup(data, size, GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
+
+	do {
+		rc = usb_control_msg(dev,
+				     usb_sndctrlpipe(dev, rinfo->usb_epnum),
+				     HID_REQ_SET_REPORT, requesttype,
+				     rinfo->report_type << 8 | rinfo->report_id,
+				     ifnum, buffer, size, 2000);
+		if (rc != -EPIPE)
+			break;
+
+		usleep_range(1000 << tries, 3000 << tries);
+	} while (++tries < 5);
+
+	kfree(buffer);
+
+	return (rc > 0) ? 0 : rc;
+}
+
+static bool appletb_disable_autopm(struct appletb_report_info *rinfo)
+{
+	int rc;
+
+	rc = usb_autopm_get_interface(rinfo->usb_iface);
+	if (rc == 0)
+		return true;
+
+	hid_err(rinfo->hdev,
+		"Failed to disable auto-pm on touch bar device (%d)\n", rc);
+	return false;
+}
+
+static int appletb_set_tb_mode(struct appletb_device *tb_dev,
+			       unsigned char mode)
+{
+	int rc;
+	bool autopm_off = false;
+
+	if (!tb_dev->mode_info.usb_iface)
+		return -ENOTCONN;
+
+	autopm_off = appletb_disable_autopm(&tb_dev->mode_info);
+
+	rc = appletb_send_hid_report(&tb_dev->mode_info,
+				     USB_DIR_OUT | USB_TYPE_VENDOR |
+							USB_RECIP_DEVICE,
+				     &mode, 1);
+	if (rc < 0)
+		dev_err(tb_dev->log_dev,
+			"Failed to set touch bar mode to %u (%d)\n", mode, rc);
+
+	if (autopm_off)
+		usb_autopm_put_interface(tb_dev->mode_info.usb_iface);
+
+	return rc;
+}
+
+static int appletb_set_tb_disp(struct appletb_device *tb_dev,
+			       unsigned char disp)
+{
+	unsigned char report[] = { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+	int rc;
+
+	if (!tb_dev->disp_info.usb_iface)
+		return -ENOTCONN;
+
+	/*
+	 * Keep the USB interface powered on while the touch bar display is on
+	 * for better responsiveness.
+	 */
+	if (disp != APPLETB_CMD_DISP_OFF &&
+	    tb_dev->cur_tb_disp == APPLETB_CMD_DISP_OFF)
+		tb_dev->tb_autopm_off =
+			appletb_disable_autopm(&tb_dev->disp_info);
+
+	report[0] = tb_dev->disp_info.report_id;
+	report[2] = disp;
+
+	rc = appletb_send_hid_report(&tb_dev->disp_info,
+				     USB_DIR_OUT | USB_TYPE_CLASS |
+						USB_RECIP_INTERFACE,
+				     report, sizeof(report));
+	if (rc < 0)
+		dev_err(tb_dev->log_dev,
+			"Failed to set touch bar display to %u (%d)\n", disp,
+			rc);
+
+	if (disp == APPLETB_CMD_DISP_OFF &&
+	    tb_dev->cur_tb_disp != APPLETB_CMD_DISP_OFF) {
+		if (tb_dev->tb_autopm_off) {
+			usb_autopm_put_interface(tb_dev->disp_info.usb_iface);
+			tb_dev->tb_autopm_off = false;
+		}
+	}
+
+	return rc;
+}
+
+static bool appletb_any_tb_key_pressed(struct appletb_device *tb_dev)
+{
+	int idx;
+
+	for (idx = 0; idx < ARRAY_SIZE(tb_dev->last_tb_keys_pressed); idx++) {
+		if (tb_dev->last_tb_keys_pressed[idx])
+			return true;
+	}
+
+	return false;
+}
+
+static void appletb_schedule_tb_update(struct appletb_device *tb_dev, s64 secs)
+{
+	schedule_delayed_work(&tb_dev->tb_work, msecs_to_jiffies(secs * 1000));
+}
+
+static void appletb_set_tb_worker(struct work_struct *work)
+{
+	struct appletb_device *tb_dev =
+		container_of(work, struct appletb_device, tb_work.work);
+	s64 time_left, min_timeout, time_to_off;
+	unsigned char pending_mode;
+	unsigned char pending_disp;
+	unsigned char current_disp;
+	bool restore_autopm;
+	bool any_tb_key_pressed, need_reschedule;
+	int rc1 = 1, rc2 = 1;
+	unsigned long flags;
+
+	spin_lock_irqsave(&tb_dev->tb_lock, flags);
+
+	/* handle explicit mode-change request */
+	pending_mode = tb_dev->pnd_tb_mode;
+	pending_disp = tb_dev->pnd_tb_disp;
+	restore_autopm = tb_dev->restore_autopm;
+
+	spin_unlock_irqrestore(&tb_dev->tb_lock, flags);
+
+	if (pending_mode != APPLETB_CMD_MODE_NONE)
+		rc1 = appletb_set_tb_mode(tb_dev, pending_mode);
+	if (pending_mode != APPLETB_CMD_MODE_NONE &&
+	    pending_disp != APPLETB_CMD_DISP_NONE)
+		msleep(25);
+	if (pending_disp != APPLETB_CMD_DISP_NONE)
+		rc2 = appletb_set_tb_disp(tb_dev, pending_disp);
+
+	if (restore_autopm && tb_dev->tb_autopm_off)
+		appletb_disable_autopm(&tb_dev->disp_info);
+
+	spin_lock_irqsave(&tb_dev->tb_lock, flags);
+
+	need_reschedule = false;
+
+	if (rc1 == 0) {
+		tb_dev->cur_tb_mode = pending_mode;
+
+		if (tb_dev->pnd_tb_mode == pending_mode)
+			tb_dev->pnd_tb_mode = APPLETB_CMD_MODE_NONE;
+		else
+			need_reschedule = true;
+	}
+
+	if (rc2 == 0) {
+		tb_dev->cur_tb_disp = pending_disp;
+
+		if (tb_dev->pnd_tb_disp == pending_disp)
+			tb_dev->pnd_tb_disp = APPLETB_CMD_DISP_NONE;
+		else
+			need_reschedule = true;
+	}
+	current_disp = tb_dev->cur_tb_disp;
+
+	tb_dev->restore_autopm = false;
+
+	/* calculate time left to next timeout */
+	if (tb_dev->idle_timeout <= 0 && tb_dev->dim_timeout <= 0)
+		min_timeout = -1;
+	else if (tb_dev->dim_timeout <= 0)
+		min_timeout = tb_dev->idle_timeout;
+	else if (tb_dev->idle_timeout <= 0)
+		min_timeout = tb_dev->dim_timeout;
+	else
+		min_timeout = min(tb_dev->dim_timeout, tb_dev->idle_timeout);
+
+	if (min_timeout > 0) {
+		s64 idle_time =
+			(ktime_ms_delta(ktime_get(), tb_dev->last_event_time) +
+			 500) / 1000;
+
+		time_left = max(min_timeout - idle_time, 0LL);
+		if (tb_dev->idle_timeout <= 0)
+			time_to_off = -1;
+		else if (idle_time >= tb_dev->idle_timeout)
+			time_to_off = 0;
+		else
+			time_to_off = tb_dev->idle_timeout - idle_time;
+	}
+
+	any_tb_key_pressed = appletb_any_tb_key_pressed(tb_dev);
+
+	spin_unlock_irqrestore(&tb_dev->tb_lock, flags);
+
+	/* a new command arrived while we were busy - handle it */
+	if (need_reschedule) {
+		appletb_schedule_tb_update(tb_dev, 0);
+		return;
+	}
+
+	/* if no idle/dim timeout, we're done */
+	if (min_timeout <= 0)
+		return;
+
+	/* manage idle/dim timeout */
+	if (time_left > 0) {
+		/* we fired too soon or had a mode-change - re-schedule */
+		appletb_schedule_tb_update(tb_dev, time_left);
+	} else if (any_tb_key_pressed) {
+		/* keys are still pressed - re-schedule */
+		appletb_schedule_tb_update(tb_dev, min_timeout);
+	} else {
+		/* dim or idle timeout reached */
+		int next_disp = (time_to_off == 0) ? APPLETB_CMD_DISP_OFF :
+						     APPLETB_CMD_DISP_DIM;
+		if (next_disp != current_disp &&
+		    appletb_set_tb_disp(tb_dev, next_disp) == 0) {
+			spin_lock_irqsave(&tb_dev->tb_lock, flags);
+			tb_dev->cur_tb_disp = next_disp;
+			spin_unlock_irqrestore(&tb_dev->tb_lock, flags);
+		}
+
+		if (time_to_off > 0)
+			appletb_schedule_tb_update(tb_dev, time_to_off);
+	}
+}
+
+static u16 appletb_fn_to_special(u16 code)
+{
+	int idx;
+
+	for (idx = 0; idx < ARRAY_SIZE(appletb_fn_codes); idx++) {
+		if (appletb_fn_codes[idx].from == code)
+			return appletb_fn_codes[idx].to;
+	}
+
+	return 0;
+}
+
+static unsigned char appletb_get_cur_tb_mode(struct appletb_device *tb_dev)
+{
+	return tb_dev->pnd_tb_mode != APPLETB_CMD_MODE_NONE ?
+				tb_dev->pnd_tb_mode : tb_dev->cur_tb_mode;
+}
+
+static unsigned char appletb_get_cur_tb_disp(struct appletb_device *tb_dev)
+{
+	return tb_dev->pnd_tb_disp != APPLETB_CMD_DISP_NONE ?
+				tb_dev->pnd_tb_disp : tb_dev->cur_tb_disp;
+}
+
+static unsigned char appletb_get_fn_tb_mode(struct appletb_device *tb_dev)
+{
+	switch (tb_dev->fn_mode) {
+	case APPLETB_FN_MODE_FKEYS:
+		return APPLETB_CMD_MODE_FN;
+
+	case APPLETB_FN_MODE_SPCL:
+		return APPLETB_CMD_MODE_SPCL;
+
+	case APPLETB_FN_MODE_INV:
+		return (tb_dev->last_fn_pressed) ? APPLETB_CMD_MODE_SPCL :
+						   APPLETB_CMD_MODE_FN;
+
+	case APPLETB_FN_MODE_NORM:
+	default:
+		return (tb_dev->last_fn_pressed) ? APPLETB_CMD_MODE_FN :
+						   APPLETB_CMD_MODE_SPCL;
+	}
+}
+
+/*
+ * Switch touch bar mode and display when mode or display not the desired ones.
+ */
+static void appletb_update_touchbar_no_lock(struct appletb_device *tb_dev,
+					    bool force)
+{
+	unsigned char want_mode;
+	unsigned char want_disp;
+	bool need_update = false;
+
+	/*
+	 * Calculate the new modes:
+	 *   idle_timeout:
+	 *     -1  always on
+	 *      0  always off
+	 *     >0  turn off after idle_timeout seconds
+	 *   dim_timeout (only valid if idle_timeout != 0):
+	 *     -1  never dimmed
+	 *      0  always dimmed
+	 *     >0  dim off after dim_timeout seconds
+	 */
+	if (tb_dev->idle_timeout == 0) {
+		want_mode = APPLETB_CMD_MODE_OFF;
+		want_disp = APPLETB_CMD_DISP_OFF;
+	} else {
+		want_mode = appletb_get_fn_tb_mode(tb_dev);
+		want_disp = tb_dev->dim_timeout == 0 ? APPLETB_CMD_DISP_DIM :
+						       APPLETB_CMD_DISP_ON;
+	}
+
+	/*
+	 * See if we need to update the touch bar, taking into account that we
+	 * generally don't want to switch modes while a touch bar key is
+	 * pressed.
+	 */
+	if (appletb_get_cur_tb_mode(tb_dev) != want_mode &&
+	    !appletb_any_tb_key_pressed(tb_dev)) {
+		tb_dev->pnd_tb_mode = want_mode;
+		need_update = true;
+	}
+
+	if (appletb_get_cur_tb_disp(tb_dev) != want_disp &&
+	    (!appletb_any_tb_key_pressed(tb_dev) ||
+	     (appletb_any_tb_key_pressed(tb_dev) &&
+	      want_disp != APPLETB_CMD_DISP_OFF))) {
+		tb_dev->pnd_tb_disp = want_disp;
+		need_update = true;
+	}
+
+	if (force)
+		need_update = true;
+
+	/* schedule the update if desired */
+	dev_dbg_ratelimited(tb_dev->log_dev,
+			    "update: need_update=%d, want_mode=%d, cur-mode=%d, want_disp=%d, cur-disp=%d\n",
+			    need_update, want_mode, tb_dev->cur_tb_mode,
+			    want_disp, tb_dev->cur_tb_disp);
+	if (need_update) {
+		cancel_delayed_work(&tb_dev->tb_work);
+		appletb_schedule_tb_update(tb_dev, 0);
+	}
+}
+
+static void appletb_update_touchbar(struct appletb_device *tb_dev, bool force)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&tb_dev->tb_lock, flags);
+
+	if (tb_dev->active)
+		appletb_update_touchbar_no_lock(tb_dev, force);
+
+	spin_unlock_irqrestore(&tb_dev->tb_lock, flags);
+}
+
+static void appletb_set_idle_timeout(struct appletb_device *tb_dev, int new)
+{
+	tb_dev->idle_timeout = new;
+	if (tb_dev->dim_to_is_calc)
+		tb_dev->dim_timeout = new - min(APPLETB_MAX_DIM_TIME, new / 3);
+}
+
+static ssize_t idle_timeout_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	struct appletb_device *tb_dev =
+		appleib_get_drvdata(dev_get_drvdata(dev), &appletb_hid_driver);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", tb_dev->idle_timeout);
+}
+
+static ssize_t idle_timeout_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t size)
+{
+	struct appletb_device *tb_dev =
+		appleib_get_drvdata(dev_get_drvdata(dev), &appletb_hid_driver);
+	long new;
+	int rc;
+
+	rc = kstrtol(buf, 0, &new);
+	if (rc || new > INT_MAX || new < -1)
+		return -EINVAL;
+
+	appletb_set_idle_timeout(tb_dev, new);
+	appletb_update_touchbar(tb_dev, true);
+
+	return size;
+}
+
+static void appletb_set_dim_timeout(struct appletb_device *tb_dev, int new)
+{
+	if (new == -2) {
+		tb_dev->dim_to_is_calc = true;
+		appletb_set_idle_timeout(tb_dev, tb_dev->idle_timeout);
+	} else {
+		tb_dev->dim_to_is_calc = false;
+		tb_dev->dim_timeout = new;
+	}
+}
+
+static ssize_t dim_timeout_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct appletb_device *tb_dev =
+		appleib_get_drvdata(dev_get_drvdata(dev), &appletb_hid_driver);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n",
+			tb_dev->dim_to_is_calc ? -2 : tb_dev->dim_timeout);
+}
+
+static ssize_t dim_timeout_store(struct device *dev,
+				 struct device_attribute *attr,
+				 const char *buf, size_t size)
+{
+	struct appletb_device *tb_dev =
+		appleib_get_drvdata(dev_get_drvdata(dev), &appletb_hid_driver);
+	long new;
+	int rc;
+
+	rc = kstrtol(buf, 0, &new);
+	if (rc || new > INT_MAX || new < -2)
+		return -EINVAL;
+
+	appletb_set_dim_timeout(tb_dev, new);
+	appletb_update_touchbar(tb_dev, true);
+
+	return size;
+}
+
+static ssize_t fnmode_show(struct device *dev, struct device_attribute *attr,
+			   char *buf)
+{
+	struct appletb_device *tb_dev =
+		appleib_get_drvdata(dev_get_drvdata(dev), &appletb_hid_driver);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", tb_dev->fn_mode);
+}
+
+static ssize_t fnmode_store(struct device *dev, struct device_attribute *attr,
+			    const char *buf, size_t size)
+{
+	struct appletb_device *tb_dev =
+		appleib_get_drvdata(dev_get_drvdata(dev), &appletb_hid_driver);
+	long new;
+	int rc;
+
+	rc = kstrtol(buf, 0, &new);
+	if (rc || new > APPLETB_FN_MODE_MAX || new < 0)
+		return -EINVAL;
+
+	tb_dev->fn_mode = new;
+	appletb_update_touchbar(tb_dev, false);
+
+	return size;
+}
+
+static int appletb_tb_key_to_slot(unsigned int code)
+{
+	switch (code) {
+	case KEY_ESC:
+		return 0;
+	case KEY_F1:
+	case KEY_F2:
+	case KEY_F3:
+	case KEY_F4:
+	case KEY_F5:
+	case KEY_F6:
+	case KEY_F7:
+	case KEY_F8:
+	case KEY_F9:
+	case KEY_F10:
+		return code - KEY_F1 + 1;
+	case KEY_F11:
+	case KEY_F12:
+		return code - KEY_F11 + 11;
+	default:
+		return -1;
+	}
+}
+
+static int appletb_hid_event(struct hid_device *hdev, struct hid_field *field,
+			     struct hid_usage *usage, __s32 value)
+{
+	struct appletb_device *tb_dev =
+		appleib_get_drvdata(hid_get_drvdata(hdev), &appletb_hid_driver);
+	unsigned int new_code = 0;
+	unsigned long flags;
+	bool send_dummy = false;
+	bool send_trnsl = false;
+	int slot;
+	int rc = 0;
+
+	/* Only interested in keyboard events */
+	if ((usage->hid & HID_USAGE_PAGE) != HID_UP_KEYBOARD ||
+	    usage->type != EV_KEY)
+		return 0;
+
+	/*
+	 * Skip non-touch-bar keys.
+	 *
+	 * Either the touch bar itself or usbhid generate a slew of key-down
+	 * events for all the meta keys. None of which we're at all interested
+	 * in.
+	 */
+	slot = appletb_tb_key_to_slot(usage->code);
+	if (slot < 0)
+		return 0;
+
+	spin_lock_irqsave(&tb_dev->tb_lock, flags);
+
+	if (!tb_dev->active) {
+		spin_unlock_irqrestore(&tb_dev->tb_lock, flags);
+		return 0;
+	}
+
+	new_code = appletb_fn_to_special(usage->code);
+
+	/* remember which (untranslated) touch bar keys are pressed */
+	if (value != 2)
+		tb_dev->last_tb_keys_pressed[slot] = value;
+
+	/* remember last time keyboard or touchpad was touched */
+	tb_dev->last_event_time = ktime_get();
+
+	/* only switch touch bar mode when no touch bar keys are pressed */
+	appletb_update_touchbar_no_lock(tb_dev, false);
+
+	/*
+	 * We want to suppress touch bar keys while the touch bar is off, but
+	 * we do want to wake up the screen if it's asleep, so generate a dummy
+	 * event.
+	 */
+	if (tb_dev->cur_tb_mode == APPLETB_CMD_MODE_OFF ||
+	    tb_dev->cur_tb_disp == APPLETB_CMD_DISP_OFF) {
+		send_dummy = true;
+		rc = 1;
+	/* translate special keys */
+	} else if (new_code &&
+		   ((value > 0 &&
+		     appletb_get_cur_tb_mode(tb_dev) == APPLETB_CMD_MODE_SPCL)
+		    ||
+		    (value == 0 && tb_dev->last_tb_keys_translated[slot]))) {
+		tb_dev->last_tb_keys_translated[slot] = true;
+		send_trnsl = true;
+		rc = 1;
+	/* everything else handled normally */
+	} else {
+		tb_dev->last_tb_keys_translated[slot] = false;
+	}
+
+	spin_unlock_irqrestore(&tb_dev->tb_lock, flags);
+
+	/*
+	 * Need to send these input events outside of the lock, as otherwise
+	 * we can run into the following deadlock:
+	 *            Task 1                         Task 2
+	 *     appletb_hid_event()            input_event()
+	 *       acquire tb_lock                acquire dev->event_lock
+	 *       input_event()                  appletb_inp_event()
+	 *         acquire dev->event_lock        acquire tb_lock
+	 */
+	if (send_dummy) {
+		input_event(field->hidinput->input, EV_KEY, KEY_UNKNOWN, 1);
+		input_event(field->hidinput->input, EV_KEY, KEY_UNKNOWN, 0);
+	} else if (send_trnsl) {
+		input_event(field->hidinput->input, usage->type, new_code,
+			    value);
+	}
+
+	return rc;
+}
+
+static void appletb_inp_event(struct input_handle *handle, unsigned int type,
+			      unsigned int code, int value)
+{
+	struct appletb_device *tb_dev = handle->private;
+	unsigned long flags;
+
+	spin_lock_irqsave(&tb_dev->tb_lock, flags);
+
+	if (!tb_dev->active) {
+		spin_unlock_irqrestore(&tb_dev->tb_lock, flags);
+		return;
+	}
+
+	/* remember last state of FN key */
+	if (type == EV_KEY && code == KEY_FN && value != 2)
+		tb_dev->last_fn_pressed = value;
+
+	/* remember last time keyboard or touchpad was touched */
+	tb_dev->last_event_time = ktime_get();
+
+	/* only switch touch bar mode when no touch bar keys are pressed */
+	appletb_update_touchbar_no_lock(tb_dev, false);
+
+	spin_unlock_irqrestore(&tb_dev->tb_lock, flags);
+}
+
+/* Find and save the usb-device associated with the touch bar input device */
+static struct usb_interface *appletb_get_usb_iface(struct hid_device *hdev)
+{
+	struct device *dev = &hdev->dev;
+
+	/* find the usb-interface device */
+	if (!dev->bus || strcmp(dev->bus->name, "hid") != 0)
+		return NULL;
+
+	dev = dev->parent;
+	if (!dev || !dev->bus || strcmp(dev->bus->name, "usb") != 0)
+		return NULL;
+
+	return to_usb_interface(dev);
+}
+
+static int appletb_inp_connect(struct input_handler *handler,
+			       struct input_dev *dev,
+			       const struct input_device_id *id)
+{
+	struct appletb_device *tb_dev = handler->private;
+	struct input_handle *handle;
+	int rc;
+
+	if (id->driver_info == APPLETB_DEVID_KEYBOARD) {
+		handle = &tb_dev->kbd_handle;
+		handle->name = "tbkbd";
+	} else if (id->driver_info == APPLETB_DEVID_TOUCHPAD) {
+		handle = &tb_dev->tpd_handle;
+		handle->name = "tbtpad";
+	} else {
+		dev_err(tb_dev->log_dev, "Unknown device id (%lu)\n",
+			id->driver_info);
+		return -ENOENT;
+	}
+
+	if (handle->dev) {
+		dev_err(tb_dev->log_dev,
+			"Duplicate connect to %s input device\n", handle->name);
+		return -EEXIST;
+	}
+
+	handle->open = 0;
+	handle->dev = input_get_device(dev);
+	handle->handler = handler;
+	handle->private = tb_dev;
+
+	rc = input_register_handle(handle);
+	if (rc)
+		goto err_free_dev;
+
+	rc = input_open_device(handle);
+	if (rc)
+		goto err_unregister_handle;
+
+	dev_dbg(tb_dev->log_dev, "Connected to %s input device\n",
+		handle == &tb_dev->kbd_handle ? "keyboard" : "touchpad");
+
+	return 0;
+
+ err_unregister_handle:
+	input_unregister_handle(handle);
+ err_free_dev:
+	input_put_device(handle->dev);
+	handle->dev = NULL;
+	return rc;
+}
+
+static void appletb_inp_disconnect(struct input_handle *handle)
+{
+	struct appletb_device *tb_dev = handle->private;
+
+	input_close_device(handle);
+	input_unregister_handle(handle);
+
+	dev_dbg(tb_dev->log_dev, "Disconnected from %s input device\n",
+		handle == &tb_dev->kbd_handle ? "keyboard" : "touchpad");
+
+	input_put_device(handle->dev);
+	handle->dev = NULL;
+}
+
+static int appletb_input_configured(struct hid_device *hdev,
+				    struct hid_input *hidinput)
+{
+	int idx;
+	struct input_dev *input = hidinput->input;
+
+	/*
+	 * Clear various input capabilities that are blindly set by the hid
+	 * driver (usbkbd.c)
+	 */
+	memset(input->evbit, 0, sizeof(input->evbit));
+	memset(input->keybit, 0, sizeof(input->keybit));
+	memset(input->ledbit, 0, sizeof(input->ledbit));
+
+	/* set our actual capabilities */
+	__set_bit(EV_KEY, input->evbit);
+	__set_bit(EV_REP, input->evbit);
+	__set_bit(EV_MSC, input->evbit);  /* hid-input generates MSC_SCAN */
+
+	for (idx = 0; idx < ARRAY_SIZE(appletb_fn_codes); idx++) {
+		input_set_capability(input, EV_KEY, appletb_fn_codes[idx].from);
+		input_set_capability(input, EV_KEY, appletb_fn_codes[idx].to);
+	}
+
+	input_set_capability(input, EV_KEY, KEY_ESC);
+	input_set_capability(input, EV_KEY, KEY_UNKNOWN);
+
+	return 0;
+}
+
+static int appletb_fill_report_info(struct appletb_device *tb_dev,
+				    struct hid_device *hdev)
+{
+	struct appletb_report_info *report_info = NULL;
+	struct usb_interface *usb_iface;
+	struct hid_field *field;
+
+	field = appleib_find_hid_field(hdev, HID_GD_KEYBOARD, HID_USAGE_MODE);
+	if (field) {
+		report_info = &tb_dev->mode_info;
+	} else {
+		field = appleib_find_hid_field(hdev, HID_USAGE_APPLE_APP,
+					       HID_USAGE_DISP);
+		if (field)
+			report_info = &tb_dev->disp_info;
+	}
+
+	if (!report_info)
+		return 0;
+
+	usb_iface = appletb_get_usb_iface(hdev);
+	if (!usb_iface) {
+		dev_err(tb_dev->log_dev,
+			"Failed to find usb interface for hid device %s\n",
+			dev_name(&hdev->dev));
+		return -ENODEV;
+	}
+
+	report_info->hdev = hdev;
+
+	report_info->usb_iface = usb_get_intf(usb_iface);
+	report_info->usb_epnum = 0;
+
+	report_info->report_id = field->report->id;
+	switch (field->report->type) {
+	case HID_INPUT_REPORT:
+		report_info->report_type = 0x01; break;
+	case HID_OUTPUT_REPORT:
+		report_info->report_type = 0x02; break;
+	case HID_FEATURE_REPORT:
+		report_info->report_type = 0x03; break;
+	}
+
+	return 1;
+}
+
+static struct appletb_report_info *
+appletb_get_report_info(struct appletb_device *tb_dev, struct hid_device *hdev)
+{
+	if (hdev == tb_dev->mode_info.hdev)
+		return &tb_dev->mode_info;
+	if (hdev == tb_dev->disp_info.hdev)
+		return &tb_dev->disp_info;
+	return NULL;
+}
+
+static void appletb_mark_active(struct appletb_device *tb_dev, bool active)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&tb_dev->tb_lock, flags);
+	tb_dev->active = active;
+	spin_unlock_irqrestore(&tb_dev->tb_lock, flags);
+}
+
+static const struct input_device_id appletb_input_devices[] = {
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_BUS |
+			INPUT_DEVICE_ID_MATCH_KEYBIT,
+		.bustype = BUS_SPI,
+		.keybit = { [BIT_WORD(KEY_FN)] = BIT_MASK(KEY_FN) },
+		.driver_info = APPLETB_DEVID_KEYBOARD,
+	},			/* Builtin keyboard device */
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_BUS |
+			INPUT_DEVICE_ID_MATCH_KEYBIT,
+		.bustype = BUS_SPI,
+		.keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) },
+		.driver_info = APPLETB_DEVID_TOUCHPAD,
+	},			/* Builtin touchpad device */
+	{ },			/* Terminating zero entry */
+};
+
+static int appletb_probe(struct hid_device *hdev,
+			 const struct hid_device_id *id)
+{
+	struct appletb_device *tb_dev =
+		appleib_get_drvdata(hid_get_drvdata(hdev), &appletb_hid_driver);
+	struct appletb_report_info *report_info;
+	int rc;
+
+	/* initialize the report info */
+	rc = appletb_fill_report_info(tb_dev, hdev);
+	if (rc < 0)
+		goto error;
+
+	/* do setup if we have both interfaces */
+	if (tb_dev->mode_info.hdev && tb_dev->disp_info.hdev) {
+		/* mark active */
+		appletb_mark_active(tb_dev, true);
+
+		/* initialize the touch bar */
+		if (appletb_tb_def_fn_mode >= 0 &&
+		    appletb_tb_def_fn_mode <= APPLETB_FN_MODE_MAX)
+			tb_dev->fn_mode = appletb_tb_def_fn_mode;
+		else
+			tb_dev->fn_mode = APPLETB_FN_MODE_NORM;
+		appletb_set_idle_timeout(tb_dev, appletb_tb_def_idle_timeout);
+		appletb_set_dim_timeout(tb_dev, appletb_tb_def_dim_timeout);
+		tb_dev->last_event_time = ktime_get();
+
+		tb_dev->cur_tb_mode = APPLETB_CMD_MODE_OFF;
+		tb_dev->cur_tb_disp = APPLETB_CMD_DISP_OFF;
+
+		appletb_update_touchbar(tb_dev, false);
+
+		/* set up the input handler */
+		tb_dev->inp_handler.event = appletb_inp_event;
+		tb_dev->inp_handler.connect = appletb_inp_connect;
+		tb_dev->inp_handler.disconnect = appletb_inp_disconnect;
+		tb_dev->inp_handler.name = "appletb";
+		tb_dev->inp_handler.id_table = appletb_input_devices;
+		tb_dev->inp_handler.private = tb_dev;
+
+		rc = input_register_handler(&tb_dev->inp_handler);
+		if (rc) {
+			dev_err(tb_dev->log_dev,
+				"Unable to register keyboard handler (%d)\n",
+				rc);
+			goto mark_inactive;
+		}
+
+		/* initialize sysfs attributes */
+		rc = sysfs_create_group(&tb_dev->mode_info.hdev->dev.kobj,
+					&appletb_attr_group);
+		if (rc) {
+			dev_err(tb_dev->log_dev,
+				"Failed to create sysfs attributes (%d)\n", rc);
+			goto unreg_handler;
+		}
+
+		dev_info(tb_dev->log_dev, "Touchbar activated\n");
+	}
+
+	return 0;
+
+unreg_handler:
+	input_unregister_handler(&tb_dev->inp_handler);
+mark_inactive:
+	appletb_mark_active(tb_dev, false);
+	cancel_delayed_work_sync(&tb_dev->tb_work);
+
+	report_info = appletb_get_report_info(tb_dev, hdev);
+	if (report_info) {
+		usb_put_intf(report_info->usb_iface);
+		report_info->usb_iface = NULL;
+		report_info->hdev = NULL;
+	}
+error:
+	return rc;
+}
+
+static void appletb_remove(struct hid_device *hdev)
+{
+	struct appletb_device *tb_dev =
+		appleib_get_drvdata(hid_get_drvdata(hdev), &appletb_hid_driver);
+	struct appletb_report_info *report_info;
+
+	if ((hdev == tb_dev->mode_info.hdev && tb_dev->disp_info.hdev) ||
+	    (hdev == tb_dev->disp_info.hdev && tb_dev->mode_info.hdev)) {
+		sysfs_remove_group(&tb_dev->mode_info.hdev->dev.kobj,
+				   &appletb_attr_group);
+
+		input_unregister_handler(&tb_dev->inp_handler);
+
+		cancel_delayed_work_sync(&tb_dev->tb_work);
+		appletb_set_tb_mode(tb_dev, APPLETB_CMD_MODE_OFF);
+		appletb_set_tb_disp(tb_dev, APPLETB_CMD_DISP_ON);
+
+		if (tb_dev->tb_autopm_off)
+			usb_autopm_put_interface(tb_dev->disp_info.usb_iface);
+
+		appletb_mark_active(tb_dev, false);
+
+		dev_info(tb_dev->log_dev, "Touchbar deactivated\n");
+	}
+
+	report_info = appletb_get_report_info(tb_dev, hdev);
+	if (report_info) {
+		usb_put_intf(report_info->usb_iface);
+		report_info->usb_iface = NULL;
+		report_info->hdev = NULL;
+	}
+}
+
+#ifdef CONFIG_PM
+static int appletb_suspend(struct hid_device *hdev, pm_message_t message)
+{
+	struct appletb_device *tb_dev =
+		appleib_get_drvdata(hid_get_drvdata(hdev), &appletb_hid_driver);
+	unsigned long flags;
+	bool all_suspended = false;
+
+	if (message.event != PM_EVENT_SUSPEND &&
+	    message.event != PM_EVENT_FREEZE)
+		return 0;
+
+	/*
+	 * Wait for both interfaces to be suspended and no more async work
+	 * in progress.
+	 */
+	spin_lock_irqsave(&tb_dev->tb_lock, flags);
+
+	if (!tb_dev->mode_info.suspended && !tb_dev->disp_info.suspended) {
+		tb_dev->active = false;
+		cancel_delayed_work(&tb_dev->tb_work);
+	}
+
+	appletb_get_report_info(tb_dev, hdev)->suspended = true;
+
+	if ((!tb_dev->mode_info.hdev || tb_dev->mode_info.suspended) &&
+	    (!tb_dev->disp_info.hdev || tb_dev->disp_info.suspended))
+		all_suspended = true;
+
+	spin_unlock_irqrestore(&tb_dev->tb_lock, flags);
+
+	flush_delayed_work(&tb_dev->tb_work);
+
+	if (!all_suspended)
+		return 0;
+
+	/*
+	 * The touch bar device itself remembers the last state when suspended
+	 * in some cases, but in others (e.g. when mode != off and disp == off)
+	 * it resumes with a different state; furthermore it may be only
+	 * partially responsive in that state. By turning both mode and disp
+	 * off we ensure it is in a good state when resuming (and this happens
+	 * to be the same state after booting/resuming-from-hibernate, so less
+	 * special casing between the two).
+	 */
+	if (message.event == PM_EVENT_SUSPEND) {
+		appletb_set_tb_mode(tb_dev, APPLETB_CMD_MODE_OFF);
+		appletb_set_tb_disp(tb_dev, APPLETB_CMD_DISP_OFF);
+	}
+
+	spin_lock_irqsave(&tb_dev->tb_lock, flags);
+
+	tb_dev->cur_tb_mode = APPLETB_CMD_MODE_OFF;
+	tb_dev->cur_tb_disp = APPLETB_CMD_DISP_OFF;
+
+	spin_unlock_irqrestore(&tb_dev->tb_lock, flags);
+
+	dev_info(tb_dev->log_dev, "Touchbar suspended.\n");
+
+	return 0;
+}
+
+static int appletb_reset_resume(struct hid_device *hdev)
+{
+	struct appletb_device *tb_dev =
+		appleib_get_drvdata(hid_get_drvdata(hdev), &appletb_hid_driver);
+	unsigned long flags;
+
+	/*
+	 * Restore touch bar state. Note that autopm state is preserved, no need
+	 * explicitly restore that here.
+	 */
+	spin_lock_irqsave(&tb_dev->tb_lock, flags);
+
+	appletb_get_report_info(tb_dev, hdev)->suspended = false;
+
+	if ((tb_dev->mode_info.hdev && !tb_dev->mode_info.suspended) &&
+	    (tb_dev->disp_info.hdev && !tb_dev->disp_info.suspended)) {
+		tb_dev->active = true;
+		tb_dev->restore_autopm = true;
+		tb_dev->last_event_time = ktime_get();
+
+		appletb_update_touchbar_no_lock(tb_dev, true);
+
+		dev_info(tb_dev->log_dev, "Touchbar resumed.\n");
+	}
+
+	spin_unlock_irqrestore(&tb_dev->tb_lock, flags);
+
+	return 0;
+}
+#endif
+
+static struct appletb_device *appletb_alloc_device(struct device *log_dev)
+{
+	struct appletb_device *tb_dev;
+
+	/* allocate */
+	tb_dev = kzalloc(sizeof(*tb_dev), GFP_KERNEL);
+	if (!tb_dev)
+		return NULL;
+
+	/* initialize structures */
+	spin_lock_init(&tb_dev->tb_lock);
+	INIT_DELAYED_WORK(&tb_dev->tb_work, appletb_set_tb_worker);
+	tb_dev->log_dev = log_dev;
+
+	return tb_dev;
+}
+
+static void appletb_free_device(struct appletb_device *tb_dev)
+{
+	cancel_delayed_work_sync(&tb_dev->tb_work);
+	kfree(tb_dev);
+}
+
+static struct hid_driver appletb_hid_driver = {
+	.name = "apple-ib-touchbar",
+	.probe = appletb_probe,
+	.remove = appletb_remove,
+	.event = appletb_hid_event,
+	.input_configured = appletb_input_configured,
+#ifdef CONFIG_PM
+	.suspend = appletb_suspend,
+	.reset_resume = appletb_reset_resume,
+#endif
+};
+
+static int appletb_platform_probe(struct platform_device *pdev)
+{
+	struct appleib_platform_data *pdata = pdev->dev.platform_data;
+	struct appleib_device *ib_dev = pdata->ib_dev;
+	struct appletb_device *tb_dev;
+	int rc;
+
+	tb_dev = appletb_alloc_device(pdata->log_dev);
+	if (!tb_dev)
+		return -ENOMEM;
+
+	rc = appleib_register_hid_driver(ib_dev, &appletb_hid_driver, tb_dev);
+	if (rc) {
+		dev_err(tb_dev->log_dev, "Error registering hid driver: %d\n",
+			rc);
+		goto error;
+	}
+
+	platform_set_drvdata(pdev, tb_dev);
+
+	return 0;
+
+error:
+	appletb_free_device(tb_dev);
+	return rc;
+}
+
+static int appletb_platform_remove(struct platform_device *pdev)
+{
+	struct appleib_platform_data *pdata = pdev->dev.platform_data;
+	struct appleib_device *ib_dev = pdata->ib_dev;
+	struct appletb_device *tb_dev = platform_get_drvdata(pdev);
+	int rc;
+
+	rc = appleib_unregister_hid_driver(ib_dev, &appletb_hid_driver);
+	if (rc) {
+		dev_err(tb_dev->log_dev, "Error unregistering hid driver: %d\n",
+			rc);
+		goto error;
+	}
+
+	appletb_free_device(tb_dev);
+
+	return 0;
+
+error:
+	return rc;
+}
+
+static const struct platform_device_id appletb_platform_ids[] = {
+	{ .name = PLAT_NAME_IB_TB },
+	{ }
+};
+MODULE_DEVICE_TABLE(platform, appletb_platform_ids);
+
+static struct platform_driver appletb_platform_driver = {
+	.id_table = appletb_platform_ids,
+	.driver = {
+		.name	= "apple-ib-tb",
+	},
+	.probe = appletb_platform_probe,
+	.remove = appletb_platform_remove,
+};
+
+module_platform_driver(appletb_platform_driver);
+
+MODULE_AUTHOR("Ronald Tschalär");
+MODULE_DESCRIPTION("MacBookPro Touch Bar driver");
+MODULE_LICENSE("GPL v2");
-- 
2.20.1

^ permalink raw reply related

* [PATCH 3/3] iio: light: apple-ib-als: Add driver for ALS on iBridge chip.
From: Ronald Tschalär @ 2019-04-22  3:12 UTC (permalink / raw)
  To: Jiri Kosina, Benjamin Tissoires, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Lee Jones
  Cc: linux-input, linux-iio, linux-kernel
In-Reply-To: <20190422031251.11968-1-ronald@innovation.ch>

On 2016/2017 MacBook Pro's with a Touch Bar the ALS is attached to,
and exposed via the iBridge device. This provides the driver for that
sensor.

Signed-off-by: Ronald Tschalär <ronald@innovation.ch>
---
 drivers/iio/light/Kconfig        |  12 +
 drivers/iio/light/Makefile       |   1 +
 drivers/iio/light/apple-ib-als.c | 694 +++++++++++++++++++++++++++++++
 3 files changed, 707 insertions(+)
 create mode 100644 drivers/iio/light/apple-ib-als.c

diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
index 36f458433480..49159fab1c0e 100644
--- a/drivers/iio/light/Kconfig
+++ b/drivers/iio/light/Kconfig
@@ -64,6 +64,18 @@ config APDS9960
 	  To compile this driver as a module, choose M here: the
 	  module will be called apds9960
 
+config APPLE_IBRIDGE_ALS
+	tristate "Apple iBridge ambient light sensor"
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
+	depends on MFD_APPLE_IBRIDGE
+	help
+	  Say Y here to build the driver for the Apple iBridge ALS
+	  sensor.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called apple-ib-als.
+
 config BH1750
 	tristate "ROHM BH1750 ambient light sensor"
 	depends on I2C
diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
index 286bf3975372..144d918917f7 100644
--- a/drivers/iio/light/Makefile
+++ b/drivers/iio/light/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_ADJD_S311)		+= adjd_s311.o
 obj-$(CONFIG_AL3320A)		+= al3320a.o
 obj-$(CONFIG_APDS9300)		+= apds9300.o
 obj-$(CONFIG_APDS9960)		+= apds9960.o
+obj-$(CONFIG_APPLE_IBRIDGE_ALS)	+= apple-ib-als.o
 obj-$(CONFIG_BH1750)		+= bh1750.o
 obj-$(CONFIG_BH1780)		+= bh1780.o
 obj-$(CONFIG_CM32181)		+= cm32181.o
diff --git a/drivers/iio/light/apple-ib-als.c b/drivers/iio/light/apple-ib-als.c
new file mode 100644
index 000000000000..1718fcbe304f
--- /dev/null
+++ b/drivers/iio/light/apple-ib-als.c
@@ -0,0 +1,694 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Apple Ambient Light Sensor Driver
+ *
+ * Copyright (c) 2017-2018 Ronald Tschalär
+ */
+
+/*
+ * MacBookPro models with an iBridge chip (13,[23] and 14,[23]) have an
+ * ambient light sensor that is exposed via one of the USB interfaces on
+ * the iBridge as a standard HID light sensor. However, we cannot use the
+ * existing hid-sensor-als driver, for two reasons:
+ *
+ * 1. The hid-sensor-als driver is part of the hid-sensor-hub which in turn
+ *    is a hid driver, but you can't have more than one hid driver per hid
+ *    device, which is a problem because the touch bar also needs to
+ *    register as a driver for this hid device.
+ *
+ * 2. While the hid-sensors-als driver stores sensor readings received via
+ *    interrupt in an iio buffer, reads on the sysfs
+ *    .../iio:deviceX/in_illuminance_YYY attribute result in a get of the
+ *    feature report; however, in the case of this sensor here the
+ *    illuminance field of that report is always 0. Instead, the input
+ *    report needs to be requested.
+ */
+
+#define dev_fmt(fmt) "als: " fmt
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/hid-sensor-ids.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/mfd/apple-ibridge.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define APPLEALS_DYN_SENS		0	/* our dynamic sensitivity */
+#define APPLEALS_DEF_CHANGE_SENS	APPLEALS_DYN_SENS
+
+struct appleals_device {
+	struct appleib_device	*ib_dev;
+	struct device		*log_dev;
+	struct hid_device	*hid_dev;
+	struct hid_report	*cfg_report;
+	struct hid_field	*illum_field;
+	struct iio_dev		*iio_dev;
+	struct iio_trigger	*iio_trig;
+	int			cur_sensitivity;
+	int			cur_hysteresis;
+	bool			events_enabled;
+};
+
+static struct hid_driver appleals_hid_driver;
+
+/*
+ * This is a primitive way to get a relative sensitivity, one where we get
+ * notified when the value changes by a certain percentage rather than some
+ * absolute value. MacOS somehow manages to configure the sensor to work this
+ * way (with a 15% relative sensitivity), but I haven't been able to figure
+ * out how so far. So until we do, this provides a less-than-perfect
+ * simulation.
+ *
+ * When the brightness value is within one of the ranges, the sensitivity is
+ * set to that range's sensitivity. But in order to reduce flapping when the
+ * brightness is right on the border between two ranges, the ranges overlap
+ * somewhat (by at least one sensitivity), and sensitivity is only changed if
+ * the value leaves the current sensitivity's range.
+ *
+ * The values chosen for the map are somewhat arbitrary: a compromise of not
+ * too many ranges (and hence changing the sensitivity) but not too small or
+ * large of a percentage of the min and max values in the range (currently
+ * from 7.5% to 30%, i.e. within a factor of 2 of 15%), as well as just plain
+ * "this feels reasonable to me".
+ */
+struct appleals_sensitivity_map {
+	int	sensitivity;
+	int	illum_low;
+	int	illum_high;
+};
+
+static struct appleals_sensitivity_map appleals_sensitivity_map[] = {
+	{   1,    0,   14 },
+	{   3,   10,   40 },
+	{   9,   30,  120 },
+	{  27,   90,  360 },
+	{  81,  270, 1080 },
+	{ 243,  810, 3240 },
+	{ 729, 2430, 9720 },
+};
+
+static int appleals_compute_sensitivity(int cur_illum, int cur_sens)
+{
+	struct appleals_sensitivity_map *entry;
+	int i;
+
+	/* see if we're still in current range */
+	for (i = 0; i < ARRAY_SIZE(appleals_sensitivity_map); i++) {
+		entry = &appleals_sensitivity_map[i];
+
+		if (entry->sensitivity == cur_sens &&
+		    entry->illum_low <= cur_illum &&
+		    entry->illum_high >= cur_illum)
+			return cur_sens;
+		else if (entry->sensitivity > cur_sens)
+			break;
+	}
+
+	/* not in current range, so find new sensitivity */
+	for (i = 0; i < ARRAY_SIZE(appleals_sensitivity_map); i++) {
+		entry = &appleals_sensitivity_map[i];
+
+		if (entry->illum_low <= cur_illum &&
+		    entry->illum_high >= cur_illum)
+			return entry->sensitivity;
+	}
+
+	/* hmm, not in table, so assume we are above highest range */
+	i = ARRAY_SIZE(appleals_sensitivity_map) - 1;
+	return appleals_sensitivity_map[i].sensitivity;
+}
+
+static int appleals_get_field_value_for_usage(struct hid_field *field,
+					      unsigned int usage)
+{
+	int u;
+
+	if (!field)
+		return -1;
+
+	for (u = 0; u < field->maxusage; u++) {
+		if (field->usage[u].hid == usage)
+			return u + field->logical_minimum;
+	}
+
+	return -1;
+}
+
+static __s32 appleals_get_field_value(struct appleals_device *als_dev,
+				      struct hid_field *field)
+{
+	hid_hw_request(als_dev->hid_dev, field->report, HID_REQ_GET_REPORT);
+	hid_hw_wait(als_dev->hid_dev);
+
+	return field->value[0];
+}
+
+static void appleals_set_field_value(struct appleals_device *als_dev,
+				     struct hid_field *field, __s32 value)
+{
+	hid_set_field(field, 0, value);
+	hid_hw_request(als_dev->hid_dev, field->report, HID_REQ_SET_REPORT);
+}
+
+static int appleals_get_config(struct appleals_device *als_dev,
+			       unsigned int field_usage, __s32 *value)
+{
+	struct hid_field *field;
+
+	field = appleib_find_report_field(als_dev->cfg_report, field_usage);
+	if (!field)
+		return -EINVAL;
+
+	*value = appleals_get_field_value(als_dev, field);
+
+	return 0;
+}
+
+static int appleals_set_config(struct appleals_device *als_dev,
+			       unsigned int field_usage, __s32 value)
+{
+	struct hid_field *field;
+
+	field = appleib_find_report_field(als_dev->cfg_report, field_usage);
+	if (!field)
+		return -EINVAL;
+
+	appleals_set_field_value(als_dev, field, value);
+
+	return 0;
+}
+
+static int appleals_set_enum_config(struct appleals_device *als_dev,
+				    unsigned int field_usage,
+				    unsigned int value_usage)
+{
+	struct hid_field *field;
+	int value;
+
+	field = appleib_find_report_field(als_dev->cfg_report, field_usage);
+	if (!field)
+		return -EINVAL;
+
+	value = appleals_get_field_value_for_usage(field, value_usage);
+
+	appleals_set_field_value(als_dev, field, value);
+
+	return 0;
+}
+
+static void appleals_update_dyn_sensitivity(struct appleals_device *als_dev,
+					    __s32 value)
+{
+	int new_sens;
+	int rc;
+
+	new_sens = appleals_compute_sensitivity(value,
+						als_dev->cur_sensitivity);
+	if (new_sens != als_dev->cur_sensitivity) {
+		rc = appleals_set_config(als_dev,
+			HID_USAGE_SENSOR_LIGHT_ILLUM |
+			HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS,
+			new_sens);
+		if (!rc)
+			als_dev->cur_sensitivity = new_sens;
+	}
+}
+
+static void appleals_push_new_value(struct appleals_device *als_dev,
+				    __s32 value)
+{
+	__s32 buf[2] = { value, value };
+
+	iio_push_to_buffers(als_dev->iio_dev, buf);
+
+	if (als_dev->cur_hysteresis == APPLEALS_DYN_SENS)
+		appleals_update_dyn_sensitivity(als_dev, value);
+}
+
+static int appleals_hid_event(struct hid_device *hdev, struct hid_field *field,
+			      struct hid_usage *usage, __s32 value)
+{
+	struct appleals_device *als_dev =
+		appleib_get_drvdata(hid_get_drvdata(hdev),
+				    &appleals_hid_driver);
+	int rc = 0;
+
+	if ((usage->hid & HID_USAGE_PAGE) != HID_UP_SENSOR)
+		return 0;
+
+	if (usage->hid == HID_USAGE_SENSOR_LIGHT_ILLUM) {
+		appleals_push_new_value(als_dev, value);
+		rc = 1;
+	}
+
+	return rc;
+}
+
+static int appleals_enable_events(struct iio_trigger *trig, bool enable)
+{
+	struct appleals_device *als_dev = iio_trigger_get_drvdata(trig);
+	int value;
+
+	/* set the sensor's reporting state */
+	appleals_set_enum_config(als_dev, HID_USAGE_SENSOR_PROP_REPORT_STATE,
+		enable ? HID_USAGE_SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM :
+			 HID_USAGE_SENSOR_PROP_REPORTING_STATE_NO_EVENTS_ENUM);
+	als_dev->events_enabled = enable;
+
+	/* if the sensor was enabled, push an initial value */
+	if (enable) {
+		value = appleals_get_field_value(als_dev, als_dev->illum_field);
+		appleals_push_new_value(als_dev, value);
+	}
+
+	return 0;
+}
+
+static int appleals_read_raw(struct iio_dev *iio_dev,
+			     struct iio_chan_spec const *chan,
+			     int *val, int *val2, long mask)
+{
+	struct appleals_device *als_dev =
+				*(struct appleals_device **)iio_priv(iio_dev);
+	__s32 value;
+	int rc;
+
+	*val = 0;
+	*val2 = 0;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+	case IIO_CHAN_INFO_PROCESSED:
+		*val = appleals_get_field_value(als_dev, als_dev->illum_field);
+		return IIO_VAL_INT;
+
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		rc = appleals_get_config(als_dev,
+					 HID_USAGE_SENSOR_PROP_REPORT_INTERVAL,
+					 &value);
+		if (rc)
+			return rc;
+
+		/* interval is in ms; val is in HZ, val2 in µHZ */
+		value = 1000000000 / value;
+		*val = value / 1000000;
+		*val2 = value - (*val * 1000000);
+
+		return IIO_VAL_INT_PLUS_MICRO;
+
+	case IIO_CHAN_INFO_HYSTERESIS:
+		if (als_dev->cur_hysteresis == APPLEALS_DYN_SENS) {
+			*val = als_dev->cur_hysteresis;
+			return IIO_VAL_INT;
+		}
+
+		rc = appleals_get_config(als_dev,
+			HID_USAGE_SENSOR_LIGHT_ILLUM |
+			HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS,
+			val);
+		if (!rc) {
+			als_dev->cur_sensitivity = *val;
+			als_dev->cur_hysteresis = *val;
+		}
+		return rc ? rc : IIO_VAL_INT;
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static int appleals_write_raw(struct iio_dev *iio_dev,
+			      struct iio_chan_spec const *chan,
+			      int val, int val2, long mask)
+{
+	struct appleals_device *als_dev =
+				*(struct appleals_device **)iio_priv(iio_dev);
+	__s32 illum;
+	int rc;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		rc = appleals_set_config(als_dev,
+					 HID_USAGE_SENSOR_PROP_REPORT_INTERVAL,
+					 1000000000 / (val * 1000000 + val2));
+		break;
+
+	case IIO_CHAN_INFO_HYSTERESIS:
+		if (val == APPLEALS_DYN_SENS) {
+			if (als_dev->cur_hysteresis != APPLEALS_DYN_SENS) {
+				als_dev->cur_hysteresis = val;
+				illum = appleals_get_field_value(als_dev,
+							als_dev->illum_field);
+				appleals_update_dyn_sensitivity(als_dev, illum);
+			}
+			rc = 0;
+			break;
+		}
+
+		rc = appleals_set_config(als_dev,
+			HID_USAGE_SENSOR_LIGHT_ILLUM |
+			HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS,
+			val);
+		if (!rc) {
+			als_dev->cur_sensitivity = val;
+			als_dev->cur_hysteresis = val;
+		}
+		break;
+
+	default:
+		rc = -EINVAL;
+	}
+
+	return rc;
+}
+
+static const struct iio_chan_spec appleals_channels[] = {
+	{
+		.type = IIO_INTENSITY,
+		.modified = 1,
+		.channel2 = IIO_MOD_LIGHT_BOTH,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
+			BIT(IIO_CHAN_INFO_RAW),
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+			BIT(IIO_CHAN_INFO_HYSTERESIS),
+		.scan_type = {
+			.sign = 'u',
+			.realbits = 32,
+			.storagebits = 32,
+		},
+		.scan_index = 0,
+	},
+	{
+		.type = IIO_LIGHT,
+		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
+			BIT(IIO_CHAN_INFO_RAW),
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+			BIT(IIO_CHAN_INFO_HYSTERESIS),
+		.scan_type = {
+			.sign = 'u',
+			.realbits = 32,
+			.storagebits = 32,
+		},
+		.scan_index = 1,
+	}
+};
+
+static const struct iio_trigger_ops appleals_trigger_ops = {
+	.set_trigger_state = &appleals_enable_events,
+};
+
+static const struct iio_info appleals_info = {
+	.read_raw = &appleals_read_raw,
+	.write_raw = &appleals_write_raw,
+};
+
+static void appleals_config_sensor(struct appleals_device *als_dev,
+				   bool events_enabled, int sensitivity)
+{
+	struct hid_field *field;
+	__s32 val;
+
+	/*
+	 * We're (often) in a probe here, so need to enable input processing
+	 * in that case, but only in that case.
+	 */
+	if (appleib_in_hid_probe(als_dev->ib_dev))
+		hid_device_io_start(als_dev->hid_dev);
+
+	/* power on the sensor */
+	field = appleib_find_report_field(als_dev->cfg_report,
+					  HID_USAGE_SENSOR_PROY_POWER_STATE);
+	val = appleals_get_field_value_for_usage(field,
+			HID_USAGE_SENSOR_PROP_POWER_STATE_D0_FULL_POWER_ENUM);
+	hid_set_field(field, 0, val);
+
+	/* configure reporting of change events */
+	field = appleib_find_report_field(als_dev->cfg_report,
+					  HID_USAGE_SENSOR_PROP_REPORT_STATE);
+	val = appleals_get_field_value_for_usage(field,
+		events_enabled ?
+			HID_USAGE_SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM :
+			HID_USAGE_SENSOR_PROP_REPORTING_STATE_NO_EVENTS_ENUM);
+	hid_set_field(field, 0, val);
+
+	/* report change events asap */
+	field = appleib_find_report_field(als_dev->cfg_report,
+					 HID_USAGE_SENSOR_PROP_REPORT_INTERVAL);
+	hid_set_field(field, 0, field->logical_minimum);
+
+	/*
+	 * Set initial change sensitivity; if dynamic, enabling trigger will set
+	 * it instead.
+	 */
+	if (sensitivity != APPLEALS_DYN_SENS) {
+		field = appleib_find_report_field(als_dev->cfg_report,
+			HID_USAGE_SENSOR_LIGHT_ILLUM |
+			HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS);
+
+		hid_set_field(field, 0, sensitivity);
+	}
+
+	/* write the new config to the sensor */
+	hid_hw_request(als_dev->hid_dev, als_dev->cfg_report,
+		       HID_REQ_SET_REPORT);
+
+	if (appleib_in_hid_probe(als_dev->ib_dev))
+		hid_device_io_stop(als_dev->hid_dev);
+};
+
+static int appleals_config_iio(struct appleals_device *als_dev)
+{
+	struct iio_dev *iio_dev;
+	struct iio_trigger *iio_trig;
+	int rc;
+
+	/* create and register iio device */
+	iio_dev = iio_device_alloc(sizeof(als_dev));
+	if (!iio_dev)
+		return -ENOMEM;
+
+	*(struct appleals_device **)iio_priv(iio_dev) = als_dev;
+
+	iio_dev->channels = appleals_channels;
+	iio_dev->num_channels = ARRAY_SIZE(appleals_channels);
+	iio_dev->dev.parent = &als_dev->hid_dev->dev;
+	iio_dev->info = &appleals_info;
+	iio_dev->name = "als";
+	iio_dev->modes = INDIO_DIRECT_MODE;
+
+	rc = iio_triggered_buffer_setup(iio_dev, &iio_pollfunc_store_time, NULL,
+					NULL);
+	if (rc) {
+		dev_err(als_dev->log_dev, "failed to set up iio triggers: %d\n",
+			rc);
+		goto free_iio_dev;
+	}
+
+	iio_trig = iio_trigger_alloc("%s-dev%d", iio_dev->name, iio_dev->id);
+	if (!iio_trig) {
+		rc = -ENOMEM;
+		goto clean_trig_buf;
+	}
+
+	iio_trig->dev.parent = &als_dev->hid_dev->dev;
+	iio_trig->ops = &appleals_trigger_ops;
+	iio_trigger_set_drvdata(iio_trig, als_dev);
+
+	rc = iio_trigger_register(iio_trig);
+	if (rc) {
+		dev_err(als_dev->log_dev, "failed to register iio trigger: %d\n",
+			rc);
+		goto free_iio_trig;
+	}
+
+	als_dev->iio_trig = iio_trig;
+
+	rc = iio_device_register(iio_dev);
+	if (rc) {
+		dev_err(als_dev->log_dev, "failed to register iio device: %d\n",
+			rc);
+		goto unreg_iio_trig;
+	}
+
+	als_dev->iio_dev = iio_dev;
+
+	return 0;
+
+unreg_iio_trig:
+	iio_trigger_unregister(iio_trig);
+free_iio_trig:
+	iio_trigger_free(iio_trig);
+	als_dev->iio_trig = NULL;
+clean_trig_buf:
+	iio_triggered_buffer_cleanup(iio_dev);
+free_iio_dev:
+	iio_device_free(iio_dev);
+
+	return rc;
+}
+
+static int appleals_probe(struct hid_device *hdev,
+			  const struct hid_device_id *id)
+{
+	struct appleals_device *als_dev =
+		appleib_get_drvdata(hid_get_drvdata(hdev),
+				    &appleals_hid_driver);
+	struct hid_field *state_field;
+	struct hid_field *illum_field;
+	int rc;
+
+	/* find als fields and reports */
+	state_field = appleib_find_hid_field(hdev, HID_USAGE_SENSOR_ALS,
+					    HID_USAGE_SENSOR_PROP_REPORT_STATE);
+	illum_field = appleib_find_hid_field(hdev, HID_USAGE_SENSOR_ALS,
+					     HID_USAGE_SENSOR_LIGHT_ILLUM);
+	if (!state_field || !illum_field)
+		return -ENODEV;
+
+	if (als_dev->hid_dev) {
+		dev_warn(als_dev->log_dev,
+			 "Found duplicate ambient light sensor - ignoring\n");
+		return -EBUSY;
+	}
+
+	dev_info(als_dev->log_dev, "Found ambient light sensor\n");
+
+	/* initialize device */
+	als_dev->hid_dev = hdev;
+	als_dev->cfg_report = state_field->report;
+	als_dev->illum_field = illum_field;
+
+	als_dev->cur_hysteresis = APPLEALS_DEF_CHANGE_SENS;
+	als_dev->cur_sensitivity = APPLEALS_DEF_CHANGE_SENS;
+	appleals_config_sensor(als_dev, false, als_dev->cur_sensitivity);
+
+	rc = appleals_config_iio(als_dev);
+	if (rc)
+		return rc;
+
+	return 0;
+}
+
+static void appleals_remove(struct hid_device *hdev)
+{
+	struct appleals_device *als_dev =
+		appleib_get_drvdata(hid_get_drvdata(hdev),
+				    &appleals_hid_driver);
+
+	if (als_dev->iio_dev) {
+		iio_device_unregister(als_dev->iio_dev);
+
+		iio_trigger_unregister(als_dev->iio_trig);
+		iio_trigger_free(als_dev->iio_trig);
+		als_dev->iio_trig = NULL;
+
+		iio_triggered_buffer_cleanup(als_dev->iio_dev);
+		iio_device_free(als_dev->iio_dev);
+		als_dev->iio_dev = NULL;
+	}
+
+	als_dev->hid_dev = NULL;
+}
+
+#ifdef CONFIG_PM
+static int appleals_reset_resume(struct hid_device *hdev)
+{
+	struct appleals_device *als_dev =
+		appleib_get_drvdata(hid_get_drvdata(hdev),
+				    &appleals_hid_driver);
+
+	appleals_config_sensor(als_dev, als_dev->events_enabled,
+			       als_dev->cur_sensitivity);
+
+	return 0;
+}
+#endif
+
+static struct hid_driver appleals_hid_driver = {
+	.name = "apple-ib-als",
+	.probe = appleals_probe,
+	.remove = appleals_remove,
+	.event = appleals_hid_event,
+#ifdef CONFIG_PM
+	.reset_resume = appleals_reset_resume,
+#endif
+};
+
+static int appleals_platform_probe(struct platform_device *pdev)
+{
+	struct appleib_platform_data *pdata = pdev->dev.platform_data;
+	struct appleib_device *ib_dev = pdata->ib_dev;
+	struct appleals_device *als_dev;
+	int rc;
+
+	als_dev = kzalloc(sizeof(*als_dev), GFP_KERNEL);
+	if (!als_dev)
+		return -ENOMEM;
+
+	als_dev->ib_dev = ib_dev;
+	als_dev->log_dev = pdata->log_dev;
+
+	rc = appleib_register_hid_driver(ib_dev, &appleals_hid_driver, als_dev);
+	if (rc) {
+		dev_err(als_dev->log_dev, "Error registering hid driver: %d\n",
+			rc);
+		goto error;
+	}
+
+	platform_set_drvdata(pdev, als_dev);
+
+	return 0;
+
+error:
+	kfree(als_dev);
+	return rc;
+}
+
+static int appleals_platform_remove(struct platform_device *pdev)
+{
+	struct appleib_platform_data *pdata = pdev->dev.platform_data;
+	struct appleib_device *ib_dev = pdata->ib_dev;
+	struct appleals_device *als_dev = platform_get_drvdata(pdev);
+	int rc;
+
+	rc = appleib_unregister_hid_driver(ib_dev, &appleals_hid_driver);
+	if (rc) {
+		dev_err(als_dev->log_dev,
+			"Error unregistering hid driver: %d\n", rc);
+		goto error;
+	}
+
+	kfree(als_dev);
+
+	return 0;
+
+error:
+	return rc;
+}
+
+static const struct platform_device_id appleals_platform_ids[] = {
+	{ .name = PLAT_NAME_IB_ALS },
+	{ }
+};
+MODULE_DEVICE_TABLE(platform, appleals_platform_ids);
+
+static struct platform_driver appleals_platform_driver = {
+	.id_table = appleals_platform_ids,
+	.driver = {
+		.name	= "apple-ib-als",
+	},
+	.probe = appleals_platform_probe,
+	.remove = appleals_platform_remove,
+};
+
+module_platform_driver(appleals_platform_driver);
+
+MODULE_AUTHOR("Ronald Tschalär");
+MODULE_DESCRIPTION("Apple iBridge ALS driver");
+MODULE_LICENSE("GPL v2");
-- 
2.20.1

^ permalink raw reply related

* [PATCH] HID: i2c-hid: add iBall Aer3 to descriptor override
From: Kai-Heng Feng @ 2019-04-22  6:57 UTC (permalink / raw)
  To: jikos, benjamin.tissoires; +Cc: linux-input, linux-kernel, Kai-Heng Feng

This device uses the SIPODEV SP1064 touchpad, which does not
supply descriptors, so it has to be added to the override
list.

BugLink: https://bugs.launchpad.net/bugs/1825718
Signed-off-by: Kai-Heng Feng <kai.heng.feng@canonical.com>
---
 drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c b/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c
index fd1b6eea6d2f..75078c83be1a 100644
--- a/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c
+++ b/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c
@@ -354,6 +354,14 @@ static const struct dmi_system_id i2c_hid_dmi_desc_override_table[] = {
 		},
 		.driver_data = (void *)&sipodev_desc
 	},
+	{
+		.ident = "iBall Aer3",
+		.matches = {
+			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "iBall"),
+			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Aer3"),
+		},
+		.driver_data = (void *)&sipodev_desc
+	},
 	{ }	/* Terminate list */
 };
 
-- 
2.17.1

^ permalink raw reply related

* Re: [PATCH 3/3] iio: Add PAT9125 optical tracker sensor
From: Jonathan Cameron @ 2019-04-22  8:42 UTC (permalink / raw)
  To: Alexandre
  Cc: robh+dt, mark.rutland, knaack.h, lars, pmeerw, linux-kernel,
	linux-iio, baylibre-upstreaming, Dmitry Torokhov, linux-input
In-Reply-To: <f8ffca5b-09a8-cad7-4561-bf1263388228@baylibre.com>

On Tue, 16 Apr 2019 14:49:19 +0200
Alexandre <amergnat@baylibre.com> wrote:

> Hello Jonathan,
> 
> On 4/7/19 12:20, Jonathan Cameron wrote:
> > Hi Alexandre,
> >
> > So I have no problem with this as an IIO driver, but for devices that
> > are somewhat 'on the edge' I always like to get a clear answer to the
> > question: Why not input?
> >
> > I would also argue that, to actually be 'useful' we would typically need
> > some representation of the 'mechanicals' that are providing the motion
> > being measured.  Looking at the datasheet this includes, rotating shafts
> > (side or end on), disk edges and flat surface tracking (mouse like).
> >
> > That's easy enough to do with the iio in kernel consumer interface. These
> > are similar to when we handle analog electronic front ends.
> >
> > I you can, please describe what it is being used for in your application
> > as that may give us somewhere to start!
> >
> > + CC Dmitry and linux-input.  
> 
> I developed this driver to detect the board movement which can't be 
> detected by accelerometer (very slow motion). I admit this use case can 
> be handled by an input, and I'm agree with you, PAT9125 driver could be 
> an input. But, like you said, this chip is able to track different kind 
> of motion, and additionally have an interrupt GPIO, so using it like 
> input limit the driver potential. This chip is designed to work in 
> industrial measurement or embedded systems, and the IIO API match with 
> these environments, so it's the best way to exploit the entire potential 
> of this chip.
> 
> As I understand (from 
> https://www.kernel.org/doc/html/v4.12/input/event-codes.html#mice ), 
> mouse driver must report values when the device move. This feature 
> souldn't be mandatory for an optical tracker driver, specially for cases 
> where user prefers to use buffer or poll only when he need data.
> 
> > If 1 or 2, I would suggest that you provide absolute position to
> > Linux.  So add the value to a software counter and provide that.
> > 32 bits should be plenty of resolution for that.  
> I can't provide absolute position, only relative. Do you mean using 
> input driver to do that ? If not, how is built the position data?

Sorry, I should have been clearer on this.
I mean absolute relative to the start point.  So on startup you assume
absolute position is 0 and go from there.  What I can't work out is
if the device does internal tracking, or whether each time you read
it effectively resets it's internal counters to 0 so the next measurement
is relative to the previous one.

> 
> > Silly question for you.  What happens if you set the delta values to 0?
> > Do we get an interrupt which is effectively data ready?
> > If we do, you might want to think about a scheme where that is an option.
> > As things currently stand we have a confusing interface where changing this
> > threshold effects the buffered data output.   That should only be the
> > case if this interface is for a trigger, not an event.  
> 
> I'm not sure to understand your question. Is it possible to read delta_x 
> and delta_y = 0 in special/corner case because internal value continue 
> to be updated after toggled motion_detect pin (used for IRQ) until 
> values registers are read and then motion_detect pin is released:
> 
>   * Chip move (i.e. +2 on X axis and 0 on Y axis)
>   * Motion_detect IRQ trigger and internal reg value is updated (i.e.
>     delta_x = 2 and delta_y = 0.
>   * GPIO IRQ handled but read_value isn't executed yet (timing reason)
>   * Chip move back to it origin point (i.e. -2 on X axis and 0 on Y axis)
>   * Motion_detect IRQ still low because it hasn't been reset by read
>     value and internal reg value is updated (i.e. delta_x = 0 and
>     delta_y = 0)
>   * Read_value is executed, we get delta values = 0.
Again, I was unclear.  Is it possible to set the device to interrupt
every time it evaluates whether motion has occured? Not only when it
concludes that there has been some motion.  That would allow the interrupt
to be used as a signal that the device has taken a measurement (data
ready signal in other sensors).

> 
> > If it is actually not possible to report the two channels separately
> > then don't report them at all except via the buffered interface and
> > set the available scan masks so that both are on.  
> I found a way to keep the consistency between delta x and delta y 
> (without losing data). The first part is to reset a value only when user 
> read it (also when it's buffered). The second part is to add the new 
> value to the old value. With these two mechanism, X and Y will always be 
> consistent:
> 
>   * as possible during a move.
>   * perfectly when move is finished.
Ah. This adding old value to a new value point is what I was getting
at (I think) with 'absolute' position above.

In industrial control for example you have absolute position by using
limit switches to set your baseline.  Measurement devices are then
capable of either reporting relative position, which is the movement
since the last reading was taken, or 'absolute' position which is
referenced to some known point.  It was this form of absolute position
that I was suggesting you use.  If you use such a system without a
limit switch it is normally called unreference motion.  You can do
it but then the 0 is where ever your device was at power on.
For some systems it doesn't actually matter (conveyor belts for
instance where the positions you care about are between things
on the belt, not the position of the belt itself).

Thanks,

Jonathan

> 
> 
> Regards,
> 
> Alexandre
> 

^ permalink raw reply

* Re: [PATCH 3/3] iio: light: apple-ib-als: Add driver for ALS on iBridge chip.
From: Peter Meerwald-Stadler @ 2019-04-22  9:17 UTC (permalink / raw)
  To: Ronald Tschalär
  Cc: Jiri Kosina, Benjamin Tissoires, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Lee Jones, linux-input, linux-iio,
	linux-kernel
In-Reply-To: <20190422031251.11968-4-ronald@innovation.ch>

[-- Attachment #1: Type: text/plain, Size: 22811 bytes --]

On Sun, 21 Apr 2019, Ronald Tschalär wrote:

> On 2016/2017 MacBook Pro's with a Touch Bar the ALS is attached to,
> and exposed via the iBridge device. This provides the driver for that
> sensor.

some comments below inline
 
> Signed-off-by: Ronald Tschalär <ronald@innovation.ch>
> ---
>  drivers/iio/light/Kconfig        |  12 +
>  drivers/iio/light/Makefile       |   1 +
>  drivers/iio/light/apple-ib-als.c | 694 +++++++++++++++++++++++++++++++
>  3 files changed, 707 insertions(+)
>  create mode 100644 drivers/iio/light/apple-ib-als.c
> 
> diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
> index 36f458433480..49159fab1c0e 100644
> --- a/drivers/iio/light/Kconfig
> +++ b/drivers/iio/light/Kconfig
> @@ -64,6 +64,18 @@ config APDS9960
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called apds9960
>  
> +config APPLE_IBRIDGE_ALS
> +	tristate "Apple iBridge ambient light sensor"
> +	select IIO_BUFFER
> +	select IIO_TRIGGERED_BUFFER
> +	depends on MFD_APPLE_IBRIDGE
> +	help
> +	  Say Y here to build the driver for the Apple iBridge ALS
> +	  sensor.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called apple-ib-als.
> +
>  config BH1750
>  	tristate "ROHM BH1750 ambient light sensor"
>  	depends on I2C
> diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
> index 286bf3975372..144d918917f7 100644
> --- a/drivers/iio/light/Makefile
> +++ b/drivers/iio/light/Makefile
> @@ -9,6 +9,7 @@ obj-$(CONFIG_ADJD_S311)		+= adjd_s311.o
>  obj-$(CONFIG_AL3320A)		+= al3320a.o
>  obj-$(CONFIG_APDS9300)		+= apds9300.o
>  obj-$(CONFIG_APDS9960)		+= apds9960.o
> +obj-$(CONFIG_APPLE_IBRIDGE_ALS)	+= apple-ib-als.o
>  obj-$(CONFIG_BH1750)		+= bh1750.o
>  obj-$(CONFIG_BH1780)		+= bh1780.o
>  obj-$(CONFIG_CM32181)		+= cm32181.o
> diff --git a/drivers/iio/light/apple-ib-als.c b/drivers/iio/light/apple-ib-als.c
> new file mode 100644
> index 000000000000..1718fcbe304f
> --- /dev/null
> +++ b/drivers/iio/light/apple-ib-als.c
> @@ -0,0 +1,694 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Apple Ambient Light Sensor Driver
> + *
> + * Copyright (c) 2017-2018 Ronald Tschalär
> + */
> +
> +/*
> + * MacBookPro models with an iBridge chip (13,[23] and 14,[23]) have an
> + * ambient light sensor that is exposed via one of the USB interfaces on
> + * the iBridge as a standard HID light sensor. However, we cannot use the
> + * existing hid-sensor-als driver, for two reasons:
> + *
> + * 1. The hid-sensor-als driver is part of the hid-sensor-hub which in turn
> + *    is a hid driver, but you can't have more than one hid driver per hid
> + *    device, which is a problem because the touch bar also needs to
> + *    register as a driver for this hid device.
> + *
> + * 2. While the hid-sensors-als driver stores sensor readings received via
> + *    interrupt in an iio buffer, reads on the sysfs
> + *    .../iio:deviceX/in_illuminance_YYY attribute result in a get of the
> + *    feature report; however, in the case of this sensor here the
> + *    illuminance field of that report is always 0. Instead, the input
> + *    report needs to be requested.
> + */
> +
> +#define dev_fmt(fmt) "als: " fmt
> +
> +#include <linux/device.h>
> +#include <linux/hid.h>
> +#include <linux/hid-sensor-ids.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/mfd/apple-ibridge.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +#define APPLEALS_DYN_SENS		0	/* our dynamic sensitivity */
> +#define APPLEALS_DEF_CHANGE_SENS	APPLEALS_DYN_SENS
> +
> +struct appleals_device {
> +	struct appleib_device	*ib_dev;
> +	struct device		*log_dev;
> +	struct hid_device	*hid_dev;
> +	struct hid_report	*cfg_report;
> +	struct hid_field	*illum_field;
> +	struct iio_dev		*iio_dev;
> +	struct iio_trigger	*iio_trig;
> +	int			cur_sensitivity;
> +	int			cur_hysteresis;
> +	bool			events_enabled;
> +};
> +
> +static struct hid_driver appleals_hid_driver;
> +
> +/*
> + * This is a primitive way to get a relative sensitivity, one where we get
> + * notified when the value changes by a certain percentage rather than some
> + * absolute value. MacOS somehow manages to configure the sensor to work this
> + * way (with a 15% relative sensitivity), but I haven't been able to figure
> + * out how so far. So until we do, this provides a less-than-perfect
> + * simulation.
> + *
> + * When the brightness value is within one of the ranges, the sensitivity is
> + * set to that range's sensitivity. But in order to reduce flapping when the
> + * brightness is right on the border between two ranges, the ranges overlap
> + * somewhat (by at least one sensitivity), and sensitivity is only changed if
> + * the value leaves the current sensitivity's range.
> + *
> + * The values chosen for the map are somewhat arbitrary: a compromise of not
> + * too many ranges (and hence changing the sensitivity) but not too small or
> + * large of a percentage of the min and max values in the range (currently
> + * from 7.5% to 30%, i.e. within a factor of 2 of 15%), as well as just plain
> + * "this feels reasonable to me".
> + */
> +struct appleals_sensitivity_map {
> +	int	sensitivity;
> +	int	illum_low;
> +	int	illum_high;
> +};
> +
> +static struct appleals_sensitivity_map appleals_sensitivity_map[] = {

const?

> +	{   1,    0,   14 },
> +	{   3,   10,   40 },
> +	{   9,   30,  120 },
> +	{  27,   90,  360 },
> +	{  81,  270, 1080 },
> +	{ 243,  810, 3240 },
> +	{ 729, 2430, 9720 },
> +};
> +
> +static int appleals_compute_sensitivity(int cur_illum, int cur_sens)
> +{
> +	struct appleals_sensitivity_map *entry;
> +	int i;
> +
> +	/* see if we're still in current range */
> +	for (i = 0; i < ARRAY_SIZE(appleals_sensitivity_map); i++) {
> +		entry = &appleals_sensitivity_map[i];
> +
> +		if (entry->sensitivity == cur_sens &&
> +		    entry->illum_low <= cur_illum &&
> +		    entry->illum_high >= cur_illum)
> +			return cur_sens;
> +		else if (entry->sensitivity > cur_sens)
> +			break;
> +	}
> +
> +	/* not in current range, so find new sensitivity */
> +	for (i = 0; i < ARRAY_SIZE(appleals_sensitivity_map); i++) {
> +		entry = &appleals_sensitivity_map[i];
> +
> +		if (entry->illum_low <= cur_illum &&
> +		    entry->illum_high >= cur_illum)
> +			return entry->sensitivity;
> +	}
> +
> +	/* hmm, not in table, so assume we are above highest range */
> +	i = ARRAY_SIZE(appleals_sensitivity_map) - 1;
> +	return appleals_sensitivity_map[i].sensitivity;
> +}
> +
> +static int appleals_get_field_value_for_usage(struct hid_field *field,
> +					      unsigned int usage)
> +{
> +	int u;
> +
> +	if (!field)
> +		return -1;
> +
> +	for (u = 0; u < field->maxusage; u++) {
> +		if (field->usage[u].hid == usage)
> +			return u + field->logical_minimum;
> +	}
> +
> +	return -1;
> +}
> +
> +static __s32 appleals_get_field_value(struct appleals_device *als_dev,
> +				      struct hid_field *field)
> +{
> +	hid_hw_request(als_dev->hid_dev, field->report, HID_REQ_GET_REPORT);
> +	hid_hw_wait(als_dev->hid_dev);
> +
> +	return field->value[0];
> +}
> +
> +static void appleals_set_field_value(struct appleals_device *als_dev,
> +				     struct hid_field *field, __s32 value)
> +{
> +	hid_set_field(field, 0, value);
> +	hid_hw_request(als_dev->hid_dev, field->report, HID_REQ_SET_REPORT);
> +}
> +
> +static int appleals_get_config(struct appleals_device *als_dev,
> +			       unsigned int field_usage, __s32 *value)
> +{
> +	struct hid_field *field;
> +
> +	field = appleib_find_report_field(als_dev->cfg_report, field_usage);
> +	if (!field)
> +		return -EINVAL;
> +
> +	*value = appleals_get_field_value(als_dev, field);
> +
> +	return 0;
> +}
> +
> +static int appleals_set_config(struct appleals_device *als_dev,
> +			       unsigned int field_usage, __s32 value)
> +{
> +	struct hid_field *field;
> +
> +	field = appleib_find_report_field(als_dev->cfg_report, field_usage);
> +	if (!field)
> +		return -EINVAL;
> +
> +	appleals_set_field_value(als_dev, field, value);
> +
> +	return 0;
> +}
> +
> +static int appleals_set_enum_config(struct appleals_device *als_dev,
> +				    unsigned int field_usage,
> +				    unsigned int value_usage)
> +{
> +	struct hid_field *field;
> +	int value;
> +
> +	field = appleib_find_report_field(als_dev->cfg_report, field_usage);
> +	if (!field)
> +		return -EINVAL;
> +
> +	value = appleals_get_field_value_for_usage(field, value_usage);

can return -1, not checked

> +
> +	appleals_set_field_value(als_dev, field, value);
> +
> +	return 0;
> +}
> +
> +static void appleals_update_dyn_sensitivity(struct appleals_device *als_dev,
> +					    __s32 value)
> +{
> +	int new_sens;
> +	int rc;
> +
> +	new_sens = appleals_compute_sensitivity(value,
> +						als_dev->cur_sensitivity);
> +	if (new_sens != als_dev->cur_sensitivity) {
> +		rc = appleals_set_config(als_dev,
> +			HID_USAGE_SENSOR_LIGHT_ILLUM |
> +			HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS,
> +			new_sens);
> +		if (!rc)
> +			als_dev->cur_sensitivity = new_sens;
> +	}
> +}
> +
> +static void appleals_push_new_value(struct appleals_device *als_dev,
> +				    __s32 value)
> +{
> +	__s32 buf[2] = { value, value };
> +
> +	iio_push_to_buffers(als_dev->iio_dev, buf);
> +
> +	if (als_dev->cur_hysteresis == APPLEALS_DYN_SENS)
> +		appleals_update_dyn_sensitivity(als_dev, value);
> +}
> +
> +static int appleals_hid_event(struct hid_device *hdev, struct hid_field *field,
> +			      struct hid_usage *usage, __s32 value)
> +{
> +	struct appleals_device *als_dev =
> +		appleib_get_drvdata(hid_get_drvdata(hdev),
> +				    &appleals_hid_driver);
> +	int rc = 0;
> +
> +	if ((usage->hid & HID_USAGE_PAGE) != HID_UP_SENSOR)
> +		return 0;
> +
> +	if (usage->hid == HID_USAGE_SENSOR_LIGHT_ILLUM) {
> +		appleals_push_new_value(als_dev, value);
> +		rc = 1;
> +	}
> +
> +	return rc;
> +}
> +
> +static int appleals_enable_events(struct iio_trigger *trig, bool enable)
> +{
> +	struct appleals_device *als_dev = iio_trigger_get_drvdata(trig);
> +	int value;
> +
> +	/* set the sensor's reporting state */
> +	appleals_set_enum_config(als_dev, HID_USAGE_SENSOR_PROP_REPORT_STATE,
> +		enable ? HID_USAGE_SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM :
> +			 HID_USAGE_SENSOR_PROP_REPORTING_STATE_NO_EVENTS_ENUM);
> +	als_dev->events_enabled = enable;
> +
> +	/* if the sensor was enabled, push an initial value */
> +	if (enable) {
> +		value = appleals_get_field_value(als_dev, als_dev->illum_field);
> +		appleals_push_new_value(als_dev, value);
> +	}
> +
> +	return 0;
> +}
> +
> +static int appleals_read_raw(struct iio_dev *iio_dev,
> +			     struct iio_chan_spec const *chan,
> +			     int *val, int *val2, long mask)
> +{
> +	struct appleals_device *als_dev =
> +				*(struct appleals_device **)iio_priv(iio_dev);
> +	__s32 value;
> +	int rc;
> +
> +	*val = 0;
> +	*val2 = 0;

no need to set these here

> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +	case IIO_CHAN_INFO_PROCESSED:
> +		*val = appleals_get_field_value(als_dev, als_dev->illum_field);
> +		return IIO_VAL_INT;
> +
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		rc = appleals_get_config(als_dev,
> +					 HID_USAGE_SENSOR_PROP_REPORT_INTERVAL,
> +					 &value);
> +		if (rc)
> +			return rc;
> +
> +		/* interval is in ms; val is in HZ, val2 in µHZ */
> +		value = 1000000000 / value;
> +		*val = value / 1000000;
> +		*val2 = value - (*val * 1000000);
> +
> +		return IIO_VAL_INT_PLUS_MICRO;
> +
> +	case IIO_CHAN_INFO_HYSTERESIS:
> +		if (als_dev->cur_hysteresis == APPLEALS_DYN_SENS) {
> +			*val = als_dev->cur_hysteresis;
> +			return IIO_VAL_INT;
> +		}
> +
> +		rc = appleals_get_config(als_dev,
> +			HID_USAGE_SENSOR_LIGHT_ILLUM |
> +			HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS,
> +			val);
> +		if (!rc) {
> +			als_dev->cur_sensitivity = *val;
> +			als_dev->cur_hysteresis = *val;
> +		}
> +		return rc ? rc : IIO_VAL_INT;
> +
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int appleals_write_raw(struct iio_dev *iio_dev,
> +			      struct iio_chan_spec const *chan,
> +			      int val, int val2, long mask)
> +{
> +	struct appleals_device *als_dev =
> +				*(struct appleals_device **)iio_priv(iio_dev);
> +	__s32 illum;
> +	int rc;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		rc = appleals_set_config(als_dev,
> +					 HID_USAGE_SENSOR_PROP_REPORT_INTERVAL,
> +					 1000000000 / (val * 1000000 + val2));
> +		break;

maybe return directly instead of at the end (matter of taste);
here and in the other cases below

> +
> +	case IIO_CHAN_INFO_HYSTERESIS:
> +		if (val == APPLEALS_DYN_SENS) {
> +			if (als_dev->cur_hysteresis != APPLEALS_DYN_SENS) {
> +				als_dev->cur_hysteresis = val;
> +				illum = appleals_get_field_value(als_dev,
> +							als_dev->illum_field);
> +				appleals_update_dyn_sensitivity(als_dev, illum);
> +			}
> +			rc = 0;
> +			break;
> +		}
> +
> +		rc = appleals_set_config(als_dev,
> +			HID_USAGE_SENSOR_LIGHT_ILLUM |
> +			HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS,
> +			val);
> +		if (!rc) {
> +			als_dev->cur_sensitivity = val;
> +			als_dev->cur_hysteresis = val;
> +		}
> +		break;
> +
> +	default:
> +		rc = -EINVAL;
> +	}
> +
> +	return rc;
> +}
> +
> +static const struct iio_chan_spec appleals_channels[] = {
> +	{
> +		.type = IIO_INTENSITY,
> +		.modified = 1,
> +		.channel2 = IIO_MOD_LIGHT_BOTH,
> +		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
> +			BIT(IIO_CHAN_INFO_RAW),
> +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) |
> +			BIT(IIO_CHAN_INFO_HYSTERESIS),
> +		.scan_type = {
> +			.sign = 'u',
> +			.realbits = 32,
> +			.storagebits = 32,
> +		},
> +		.scan_index = 0,
> +	},
> +	{
> +		.type = IIO_LIGHT,
> +		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
> +			BIT(IIO_CHAN_INFO_RAW),
> +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) |
> +			BIT(IIO_CHAN_INFO_HYSTERESIS),
> +		.scan_type = {
> +			.sign = 'u',
> +			.realbits = 32,
> +			.storagebits = 32,
> +		},
> +		.scan_index = 1,
> +	}
> +};
> +
> +static const struct iio_trigger_ops appleals_trigger_ops = {
> +	.set_trigger_state = &appleals_enable_events,
> +};
> +
> +static const struct iio_info appleals_info = {
> +	.read_raw = &appleals_read_raw,
> +	.write_raw = &appleals_write_raw,
> +};
> +
> +static void appleals_config_sensor(struct appleals_device *als_dev,
> +				   bool events_enabled, int sensitivity)
> +{
> +	struct hid_field *field;
> +	__s32 val;
> +
> +	/*
> +	 * We're (often) in a probe here, so need to enable input processing
> +	 * in that case, but only in that case.
> +	 */
> +	if (appleib_in_hid_probe(als_dev->ib_dev))
> +		hid_device_io_start(als_dev->hid_dev);
> +
> +	/* power on the sensor */
> +	field = appleib_find_report_field(als_dev->cfg_report,
> +					  HID_USAGE_SENSOR_PROY_POWER_STATE);
> +	val = appleals_get_field_value_for_usage(field,
> +			HID_USAGE_SENSOR_PROP_POWER_STATE_D0_FULL_POWER_ENUM);

what if -1?

> +	hid_set_field(field, 0, val);
> +
> +	/* configure reporting of change events */
> +	field = appleib_find_report_field(als_dev->cfg_report,
> +					  HID_USAGE_SENSOR_PROP_REPORT_STATE);
> +	val = appleals_get_field_value_for_usage(field,
> +		events_enabled ?
> +			HID_USAGE_SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM :
> +			HID_USAGE_SENSOR_PROP_REPORTING_STATE_NO_EVENTS_ENUM);
> +	hid_set_field(field, 0, val);
> +
> +	/* report change events asap */
> +	field = appleib_find_report_field(als_dev->cfg_report,
> +					 HID_USAGE_SENSOR_PROP_REPORT_INTERVAL);
> +	hid_set_field(field, 0, field->logical_minimum);
> +
> +	/*
> +	 * Set initial change sensitivity; if dynamic, enabling trigger will set
> +	 * it instead.
> +	 */
> +	if (sensitivity != APPLEALS_DYN_SENS) {
> +		field = appleib_find_report_field(als_dev->cfg_report,
> +			HID_USAGE_SENSOR_LIGHT_ILLUM |
> +			HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS);
> +
> +		hid_set_field(field, 0, sensitivity);
> +	}
> +
> +	/* write the new config to the sensor */
> +	hid_hw_request(als_dev->hid_dev, als_dev->cfg_report,
> +		       HID_REQ_SET_REPORT);
> +
> +	if (appleib_in_hid_probe(als_dev->ib_dev))
> +		hid_device_io_stop(als_dev->hid_dev);
> +};

no semicolon at the end of a function please

> +
> +static int appleals_config_iio(struct appleals_device *als_dev)
> +{
> +	struct iio_dev *iio_dev;
> +	struct iio_trigger *iio_trig;
> +	int rc;
> +
> +	/* create and register iio device */
> +	iio_dev = iio_device_alloc(sizeof(als_dev));

how about using the devm_ variants?

> +	if (!iio_dev)
> +		return -ENOMEM;
> +
> +	*(struct appleals_device **)iio_priv(iio_dev) = als_dev;
> +
> +	iio_dev->channels = appleals_channels;
> +	iio_dev->num_channels = ARRAY_SIZE(appleals_channels);
> +	iio_dev->dev.parent = &als_dev->hid_dev->dev;
> +	iio_dev->info = &appleals_info;
> +	iio_dev->name = "als";
> +	iio_dev->modes = INDIO_DIRECT_MODE;
> +
> +	rc = iio_triggered_buffer_setup(iio_dev, &iio_pollfunc_store_time, NULL,
> +					NULL);
> +	if (rc) {
> +		dev_err(als_dev->log_dev, "failed to set up iio triggers: %d\n",

just one trigger?

> +			rc);
> +		goto free_iio_dev;
> +	}
> +
> +	iio_trig = iio_trigger_alloc("%s-dev%d", iio_dev->name, iio_dev->id);
> +	if (!iio_trig) {
> +		rc = -ENOMEM;
> +		goto clean_trig_buf;
> +	}
> +
> +	iio_trig->dev.parent = &als_dev->hid_dev->dev;
> +	iio_trig->ops = &appleals_trigger_ops;
> +	iio_trigger_set_drvdata(iio_trig, als_dev);
> +
> +	rc = iio_trigger_register(iio_trig);
> +	if (rc) {
> +		dev_err(als_dev->log_dev, "failed to register iio trigger: %d\n",

some messages start lowercase, some uppercase (nitpicking)

> +			rc);
> +		goto free_iio_trig;
> +	}
> +
> +	als_dev->iio_trig = iio_trig;
> +
> +	rc = iio_device_register(iio_dev);
> +	if (rc) {
> +		dev_err(als_dev->log_dev, "failed to register iio device: %d\n",
> +			rc);
> +		goto unreg_iio_trig;
> +	}
> +
> +	als_dev->iio_dev = iio_dev;
> +
> +	return 0;
> +
> +unreg_iio_trig:
> +	iio_trigger_unregister(iio_trig);
> +free_iio_trig:
> +	iio_trigger_free(iio_trig);
> +	als_dev->iio_trig = NULL;
> +clean_trig_buf:
> +	iio_triggered_buffer_cleanup(iio_dev);
> +free_iio_dev:
> +	iio_device_free(iio_dev);
> +
> +	return rc;
> +}
> +
> +static int appleals_probe(struct hid_device *hdev,
> +			  const struct hid_device_id *id)
> +{
> +	struct appleals_device *als_dev =
> +		appleib_get_drvdata(hid_get_drvdata(hdev),
> +				    &appleals_hid_driver);
> +	struct hid_field *state_field;
> +	struct hid_field *illum_field;
> +	int rc;
> +
> +	/* find als fields and reports */
> +	state_field = appleib_find_hid_field(hdev, HID_USAGE_SENSOR_ALS,
> +					    HID_USAGE_SENSOR_PROP_REPORT_STATE);
> +	illum_field = appleib_find_hid_field(hdev, HID_USAGE_SENSOR_ALS,
> +					     HID_USAGE_SENSOR_LIGHT_ILLUM);
> +	if (!state_field || !illum_field)
> +		return -ENODEV;
> +
> +	if (als_dev->hid_dev) {
> +		dev_warn(als_dev->log_dev,
> +			 "Found duplicate ambient light sensor - ignoring\n");
> +		return -EBUSY;
> +	}
> +
> +	dev_info(als_dev->log_dev, "Found ambient light sensor\n");

in general avoid logging for the OK case, it just clutters the log

> +
> +	/* initialize device */
> +	als_dev->hid_dev = hdev;
> +	als_dev->cfg_report = state_field->report;
> +	als_dev->illum_field = illum_field;
> +
> +	als_dev->cur_hysteresis = APPLEALS_DEF_CHANGE_SENS;
> +	als_dev->cur_sensitivity = APPLEALS_DEF_CHANGE_SENS;
> +	appleals_config_sensor(als_dev, false, als_dev->cur_sensitivity);
> +
> +	rc = appleals_config_iio(als_dev);
> +	if (rc)
> +		return rc;
> +
> +	return 0;
> +}
> +
> +static void appleals_remove(struct hid_device *hdev)
> +{
> +	struct appleals_device *als_dev =
> +		appleib_get_drvdata(hid_get_drvdata(hdev),
> +				    &appleals_hid_driver);
> +

could be a lot less if devm_ were used?

> +	if (als_dev->iio_dev) {
> +		iio_device_unregister(als_dev->iio_dev);
> +
> +		iio_trigger_unregister(als_dev->iio_trig);
> +		iio_trigger_free(als_dev->iio_trig);
> +		als_dev->iio_trig = NULL;
> +
> +		iio_triggered_buffer_cleanup(als_dev->iio_dev);
> +		iio_device_free(als_dev->iio_dev);
> +		als_dev->iio_dev = NULL;
> +	}
> +
> +	als_dev->hid_dev = NULL;
> +}
> +
> +#ifdef CONFIG_PM
> +static int appleals_reset_resume(struct hid_device *hdev)
> +{
> +	struct appleals_device *als_dev =
> +		appleib_get_drvdata(hid_get_drvdata(hdev),
> +				    &appleals_hid_driver);
> +
> +	appleals_config_sensor(als_dev, als_dev->events_enabled,
> +			       als_dev->cur_sensitivity);
> +
> +	return 0;
> +}
> +#endif
> +
> +static struct hid_driver appleals_hid_driver = {
> +	.name = "apple-ib-als",
> +	.probe = appleals_probe,
> +	.remove = appleals_remove,
> +	.event = appleals_hid_event,
> +#ifdef CONFIG_PM
> +	.reset_resume = appleals_reset_resume,
> +#endif
> +};
> +
> +static int appleals_platform_probe(struct platform_device *pdev)
> +{
> +	struct appleib_platform_data *pdata = pdev->dev.platform_data;
> +	struct appleib_device *ib_dev = pdata->ib_dev;
> +	struct appleals_device *als_dev;
> +	int rc;
> +
> +	als_dev = kzalloc(sizeof(*als_dev), GFP_KERNEL);
> +	if (!als_dev)
> +		return -ENOMEM;
> +
> +	als_dev->ib_dev = ib_dev;
> +	als_dev->log_dev = pdata->log_dev;
> +
> +	rc = appleib_register_hid_driver(ib_dev, &appleals_hid_driver, als_dev);
> +	if (rc) {
> +		dev_err(als_dev->log_dev, "Error registering hid driver: %d\n",
> +			rc);
> +		goto error;
> +	}
> +
> +	platform_set_drvdata(pdev, als_dev);
> +
> +	return 0;
> +
> +error:
> +	kfree(als_dev);
> +	return rc;
> +}
> +
> +static int appleals_platform_remove(struct platform_device *pdev)
> +{
> +	struct appleib_platform_data *pdata = pdev->dev.platform_data;
> +	struct appleib_device *ib_dev = pdata->ib_dev;
> +	struct appleals_device *als_dev = platform_get_drvdata(pdev);
> +	int rc;
> +
> +	rc = appleib_unregister_hid_driver(ib_dev, &appleals_hid_driver);
> +	if (rc) {
> +		dev_err(als_dev->log_dev,
> +			"Error unregistering hid driver: %d\n", rc);
> +		goto error;
> +	}
> +
> +	kfree(als_dev);
> +
> +	return 0;
> +
> +error:
> +	return rc;
> +}
> +
> +static const struct platform_device_id appleals_platform_ids[] = {
> +	{ .name = PLAT_NAME_IB_ALS },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(platform, appleals_platform_ids);
> +
> +static struct platform_driver appleals_platform_driver = {
> +	.id_table = appleals_platform_ids,
> +	.driver = {
> +		.name	= "apple-ib-als",
> +	},
> +	.probe = appleals_platform_probe,
> +	.remove = appleals_platform_remove,
> +};
> +
> +module_platform_driver(appleals_platform_driver);
> +
> +MODULE_AUTHOR("Ronald Tschalär");
> +MODULE_DESCRIPTION("Apple iBridge ALS driver");
> +MODULE_LICENSE("GPL v2");
> 

-- 

Peter Meerwald-Stadler
Mobile: +43 664 24 44 418

^ permalink raw reply

* Re: [PATCH 1/3] mfd: apple-ibridge: Add Apple iBridge MFD driver.
From: Jonathan Cameron @ 2019-04-22 11:34 UTC (permalink / raw)
  To: Ronald Tschalär
  Cc: Jiri Kosina, Benjamin Tissoires, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, Lee Jones,
	linux-input, linux-iio, linux-kernel
In-Reply-To: <20190422031251.11968-2-ronald@innovation.ch>

On Sun, 21 Apr 2019 20:12:49 -0700
Ronald Tschalär <ronald@innovation.ch> wrote:

> The iBridge device provides access to several devices, including:
> - the Touch Bar
> - the iSight webcam
> - the light sensor
> - the fingerprint sensor
> 
> This driver provides the core support for managing the iBridge device
> and the access to the underlying devices. In particular, since the
> functionality for the touch bar and light sensor is exposed via USB HID
> interfaces, and the same HID device is used for multiple functions, this
> driver provides a multiplexing layer that allows multiple HID drivers to
> be registered for a given HID device. This allows the touch bar and ALS
> driver to be separated out into their own modules.
> 
> Signed-off-by: Ronald Tschalär <ronald@innovation.ch
Hi Ronald,

I've only taken a fairly superficial look at this.  A few global
things to note though.

1. Please either use kernel-doc style for function descriptions, or
   do not.  Right now you are sort of half way there.
2. There is quite a complex nest of separate structures being allocated,
   so think about whether they can be simplified.  In particular
   use of container_of macros can allow a lot of forwards and backwards
   pointers to be dropped if you embed the various structures directly.

This obviously needs hid and mfd review though as neither is my
area of expertise!

Jonathan
>
> ---
>  drivers/mfd/Kconfig               |  15 +
>  drivers/mfd/Makefile              |   1 +
>  drivers/mfd/apple-ibridge.c       | 883 ++++++++++++++++++++++++++++++
>  include/linux/mfd/apple-ibridge.h |  39 ++
>  4 files changed, 938 insertions(+)
>  create mode 100644 drivers/mfd/apple-ibridge.c
>  create mode 100644 include/linux/mfd/apple-ibridge.h
> 
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index 76f9909cf396..d55fa77faacf 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -1916,5 +1916,20 @@ config RAVE_SP_CORE
>  	  Select this to get support for the Supervisory Processor
>  	  device found on several devices in RAVE line of hardware.
>  
> +config MFD_APPLE_IBRIDGE
> +	tristate "Apple iBridge chip"
> +	depends on ACPI
> +	depends on USB_HID
> +	depends on X86 || COMPILE_TEST
> +	select MFD_CORE
> +	help
> +	  This MFD provides the core support for the Apple iBridge chip
> +	  found on recent MacBookPro's. The drivers for the Touch Bar
> +	  (apple-ib-tb) and light sensor (apple-ib-als) need to be
> +	  enabled separately.
> +
> +	  To compile this driver as a module, choose M here: the
> +	  module will be called apple-ibridge.
> +
>  endmenu
>  endif
> diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
> index 12980a4ad460..c364e0e9d313 100644
> --- a/drivers/mfd/Makefile
> +++ b/drivers/mfd/Makefile
> @@ -241,4 +241,5 @@ obj-$(CONFIG_MFD_MXS_LRADC)     += mxs-lradc.o
>  obj-$(CONFIG_MFD_SC27XX_PMIC)	+= sprd-sc27xx-spi.o
>  obj-$(CONFIG_RAVE_SP_CORE)	+= rave-sp.o
>  obj-$(CONFIG_MFD_ROHM_BD718XX)	+= rohm-bd718x7.o
> +obj-$(CONFIG_MFD_APPLE_IBRIDGE)	+= apple-ibridge.o
>  
> diff --git a/drivers/mfd/apple-ibridge.c b/drivers/mfd/apple-ibridge.c
> new file mode 100644
> index 000000000000..56d325396961
> --- /dev/null
> +++ b/drivers/mfd/apple-ibridge.c
> @@ -0,0 +1,883 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Apple iBridge Driver
> + *
> + * Copyright (c) 2018 Ronald Tschalär
> + */
> +
> +/**
> + * MacBookPro models with a Touch Bar (13,[23] and 14,[23]) have an Apple
> + * iBridge chip (also known as T1 chip) which exposes the touch bar,
> + * built-in webcam (iSight), ambient light sensor, and Secure Enclave
> + * Processor (SEP) for TouchID. It shows up in the system as a USB device
> + * with 3 configurations: 'Default iBridge Interfaces', 'Default iBridge
> + * Interfaces(OS X)', and 'Default iBridge Interfaces(Recovery)'. While
> + * the second one is used by MacOS to provide the fancy touch bar
> + * functionality with custom buttons etc, this driver just uses the first.
> + *
> + * In the first (default after boot) configuration, 4 usb interfaces are
> + * exposed: 2 related to the webcam, and 2 USB HID interfaces representing
> + * the touch bar and the ambient light sensor (and possibly the SEP,
> + * though at this point in time nothing is known about that). The webcam
> + * interfaces are already handled by the uvcvideo driver; furthermore, the
> + * handling of the input reports when "keys" on the touch bar are pressed
> + * is already handled properly by the generic USB HID core. This leaves
> + * the management of the touch bar modes (e.g. switching between function
> + * and special keys when the FN key is pressed), the touch bar display
> + * (dimming and turning off), the key-remapping when the FN key is
> + * pressed, and handling of the light sensor.
> + *
> + * This driver is implemented as an MFD driver, with the touch bar and ALS
> + * functions implemented by appropriate subdrivers (mfd cells). Because
> + * both those are basically hid drivers, but the current kernel driver
> + * structure does not allow more than one driver per device, this driver
> + * implements a demuxer for hid drivers: it registers itself as a hid
> + * driver with the core, and in turn it lets the subdrivers register
> + * themselves as hid drivers with this driver; the callbacks from the core
> + * are then forwarded to the subdrivers.
> + *
> + * Lastly, this driver also takes care of the power-management for the
> + * iBridge when suspending and resuming.
> + */
> +
> +#include <linux/acpi.h>
> +#include <linux/device.h>
> +#include <linux/hid.h>
> +#include <linux/list.h>
> +#include <linux/mfd/apple-ibridge.h>
> +#include <linux/mfd/core.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/rculist.h>
> +#include <linux/slab.h>
> +#include <linux/srcu.h>
> +#include <linux/usb.h>
> +
> +#include "../hid/usbhid/usbhid.h"
> +
> +#define USB_ID_VENDOR_APPLE	0x05ac
> +#define USB_ID_PRODUCT_IBRIDGE	0x8600
> +
> +#define APPLETB_BASIC_CONFIG	1
> +
> +#define	LOG_DEV(ib_dev)		(&(ib_dev)->acpi_dev->dev)
> +
> +struct appleib_device {
> +	struct acpi_device	*acpi_dev;
> +	acpi_handle		asoc_socw;
> +	struct list_head	hid_drivers;
> +	struct list_head	hid_devices;
> +	struct mfd_cell		*subdevs;
> +	struct mutex		update_lock; /* protect updates to all lists */
> +	struct srcu_struct	lists_srcu;
> +	bool			in_hid_probe;
> +};
> +
> +struct appleib_hid_drv_info {
> +	struct list_head	entry;
> +	struct hid_driver	*driver;
> +	void			*driver_data;
> +};
> +
> +struct appleib_hid_dev_info {
> +	struct list_head		entry;
> +	struct list_head		drivers;
> +	struct hid_device		*device;
> +	const struct hid_device_id	*device_id;
> +	bool				started;
> +};
> +
> +static const struct mfd_cell appleib_subdevs[] = {
> +	{ .name = PLAT_NAME_IB_TB },
> +	{ .name = PLAT_NAME_IB_ALS },
> +};
> +
> +static struct appleib_device *appleib_dev;
> +
> +#define	call_void_driver_func(drv_info, fn, ...)			\

This sort of macro may seem like a good idea because it saves a few lines
of code.  However, that comes at the cost of readability, so just
put the code inline.

> +	do {								\
> +		if ((drv_info)->driver->fn)				\
> +			(drv_info)->driver->fn(__VA_ARGS__);		\
> +	} while (0)
> +
> +#define	call_driver_func(drv_info, fn, ret_type, ...)			\
> +	({								\
> +		ret_type rc = 0;					\
> +									\
> +		if ((drv_info)->driver->fn)				\
> +			rc = (drv_info)->driver->fn(__VA_ARGS__);	\
> +									\
> +		rc;							\
> +	})
> +
> +static void appleib_remove_driver(struct appleib_device *ib_dev,
> +				  struct appleib_hid_drv_info *drv_info,
> +				  struct appleib_hid_dev_info *dev_info)
> +{
> +	list_del_rcu(&drv_info->entry);
> +	synchronize_srcu(&ib_dev->lists_srcu);
> +
> +	call_void_driver_func(drv_info, remove, dev_info->device);
> +
> +	kfree(drv_info);
> +}
> +
> +static int appleib_probe_driver(struct appleib_hid_drv_info *drv_info,
> +				struct appleib_hid_dev_info *dev_info)
> +{
> +	struct appleib_hid_drv_info *d;
> +	int rc = 0;
> +
> +	rc = call_driver_func(drv_info, probe, int, dev_info->device,
> +			      dev_info->device_id);
> +	if (rc)
> +		return rc;
> +
> +	d = kmemdup(drv_info, sizeof(*drv_info), GFP_KERNEL);
> +	if (!d) {
> +		call_void_driver_func(drv_info, remove, dev_info->device);
> +		return -ENOMEM;
> +	}
> +
> +	list_add_tail_rcu(&d->entry, &dev_info->drivers);
> +	return 0;
> +}
> +
> +static void appleib_remove_driver_attachments(struct appleib_device *ib_dev,
> +					struct appleib_hid_dev_info *dev_info,
> +					struct hid_driver *driver)
> +{
> +	struct appleib_hid_drv_info *drv_info;
> +	struct appleib_hid_drv_info *tmp;
> +
> +	list_for_each_entry_safe(drv_info, tmp, &dev_info->drivers, entry) {
> +		if (!driver || drv_info->driver == driver)
> +			appleib_remove_driver(ib_dev, drv_info, dev_info);
> +	}
> +}
> +
> +/*
> + * Find all devices that are attached to this driver and detach them.
> + *
> + * Note: this must be run with update_lock held.
> + */
> +static void appleib_detach_devices(struct appleib_device *ib_dev,
> +				   struct hid_driver *driver)
> +{
> +	struct appleib_hid_dev_info *dev_info;
> +
> +	list_for_each_entry(dev_info, &ib_dev->hid_devices, entry)
> +		appleib_remove_driver_attachments(ib_dev, dev_info, driver);
> +}
> +
> +/*
> + * Find all drivers that are attached to this device and detach them.
> + *
> + * Note: this must be run with update_lock held.
> + */
> +static void appleib_detach_drivers(struct appleib_device *ib_dev,
> +				   struct appleib_hid_dev_info *dev_info)
> +{
> +	appleib_remove_driver_attachments(ib_dev, dev_info, NULL);
> +}
> +
> +/**
> + * Unregister a previously registered HID driver from us.
> + * @ib_dev: the appleib_device from which to unregister the driver
> + * @driver: the driver to unregister
> + */
> +int appleib_unregister_hid_driver(struct appleib_device *ib_dev,
> +				  struct hid_driver *driver)
> +{
> +	struct appleib_hid_drv_info *drv_info;
> +
> +	mutex_lock(&ib_dev->update_lock);
> +
> +	list_for_each_entry(drv_info, &ib_dev->hid_drivers, entry) {
> +		if (drv_info->driver == driver) {

This block does look like it perhaps should be in helper function?
Would help with readability.

> +			appleib_detach_devices(ib_dev, driver);
> +			list_del_rcu(&drv_info->entry);
> +			mutex_unlock(&ib_dev->update_lock);
> +			synchronize_srcu(&ib_dev->lists_srcu);
> +			kfree(drv_info);
> +			dev_info(LOG_DEV(ib_dev), "unregistered driver '%s'\n",
> +				 driver->name);
> +			return 0;
> +		}
> +	}
> +
> +	mutex_unlock(&ib_dev->update_lock);
> +
> +	return -ENOENT;
> +}
> +EXPORT_SYMBOL_GPL(appleib_unregister_hid_driver);
> +
> +static int appleib_start_hid_events(struct appleib_hid_dev_info *dev_info)
> +{
> +	struct hid_device *hdev = dev_info->device;
> +	int rc;
> +
> +	rc = hid_connect(hdev, HID_CONNECT_DEFAULT);
> +	if (rc) {
> +		hid_err(hdev, "ib: hid connect failed (%d)\n", rc);
> +		return rc;
> +	}
> +
> +	rc = hid_hw_open(hdev);
> +	if (rc) {
> +		hid_err(hdev, "ib: failed to open hid: %d\n", rc);
> +		hid_disconnect(hdev);
> +	}
> +
> +	if (!rc)
> +		dev_info->started = true;
> +
> +	return rc;
> +}
> +
> +static void appleib_stop_hid_events(struct appleib_hid_dev_info *dev_info)
> +{
> +	if (dev_info->started) {
> +		hid_hw_close(dev_info->device);
> +		hid_disconnect(dev_info->device);
> +		dev_info->started = false;
> +	}
> +}
> +
> +/**
> + * Register a HID driver with us.
> + * @ib_dev: the appleib_device with which to register the driver
> + * @driver: the driver to register
> + * @data: the driver-data to associate with the driver; this is available
> + *        from appleib_get_drvdata(...).
> + */
> +int appleib_register_hid_driver(struct appleib_device *ib_dev,
> +				struct hid_driver *driver, void *data)
> +{
> +	struct appleib_hid_drv_info *drv_info;
> +	struct appleib_hid_dev_info *dev_info;
> +	int rc;
> +
> +	if (!driver->probe)
> +		return -EINVAL;
> +
> +	drv_info = kzalloc(sizeof(*drv_info), GFP_KERNEL);
> +	if (!drv_info)
> +		return -ENOMEM;
> +
> +	drv_info->driver = driver;
> +	drv_info->driver_data = data;
> +
> +	mutex_lock(&ib_dev->update_lock);
> +
> +	list_add_tail_rcu(&drv_info->entry, &ib_dev->hid_drivers);
> +
> +	list_for_each_entry(dev_info, &ib_dev->hid_devices, entry) {
> +		appleib_stop_hid_events(dev_info);
> +
> +		appleib_probe_driver(drv_info, dev_info);
> +
> +		rc = appleib_start_hid_events(dev_info);
> +		if (rc)
> +			appleib_detach_drivers(ib_dev, dev_info);
> +	}
> +
> +	mutex_unlock(&ib_dev->update_lock);
> +
> +	dev_info(LOG_DEV(ib_dev), "registered driver '%s'\n", driver->name);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(appleib_register_hid_driver);
> +
> +/**
> + * Get the driver-specific data associated with the given, previously
> + * registered HID driver (provided in the appleib_register_hid_driver()
> + * call).
> + * @ib_dev: the appleib_device with which the driver was registered
> + * @driver: the driver for which to get the data
> + */
> +void *appleib_get_drvdata(struct appleib_device *ib_dev,
> +			  struct hid_driver *driver)
> +{
> +	struct appleib_hid_drv_info *drv_info;
> +	void *drv_data = NULL;
> +	int idx;
> +
> +	idx = srcu_read_lock(&ib_dev->lists_srcu);
> +
> +	list_for_each_entry_rcu(drv_info, &ib_dev->hid_drivers, entry) {
> +		if (drv_info->driver == driver) {
> +			drv_data = drv_info->driver_data;
> +			break;
> +		}
> +	}
> +
> +	srcu_read_unlock(&ib_dev->lists_srcu, idx);
> +
> +	return drv_data;
> +}
> +EXPORT_SYMBOL_GPL(appleib_get_drvdata);
> +
> +/*
> + * Forward a hid-driver callback to all registered sub-drivers. This is for
> + * callbacks that return a status as an int.
> + * @hdev the hid-device
> + * @forward a function that calls the callback on the given driver
> + * @args arguments for the forward function
> + */
> +static int appleib_forward_int_op(struct hid_device *hdev,
> +				  int (*forward)(struct appleib_hid_drv_info *,
> +						 struct hid_device *, void *),
> +				  void *args)
> +{
> +	struct appleib_device *ib_dev = hid_get_drvdata(hdev);
> +	struct appleib_hid_dev_info *dev_info;
> +	struct appleib_hid_drv_info *drv_info;
> +	int idx;
> +	int rc = 0;
> +
> +	idx = srcu_read_lock(&ib_dev->lists_srcu);
> +
> +	list_for_each_entry_rcu(dev_info, &ib_dev->hid_devices, entry) {
> +		if (dev_info->device != hdev)
> +			continue;
> +
> +		list_for_each_entry_rcu(drv_info, &dev_info->drivers, entry) {
> +			rc = forward(drv_info, hdev, args);
> +			if (rc)
> +				break;
> +		}
> +
> +		break;
> +	}
> +
> +	srcu_read_unlock(&ib_dev->lists_srcu, idx);
> +
> +	return rc;
> +}
> +
> +struct appleib_hid_event_args {
> +	struct hid_field *field;
> +	struct hid_usage *usage;
> +	__s32 value;
> +};
> +
> +static int appleib_hid_event_fwd(struct appleib_hid_drv_info *drv_info,
> +				 struct hid_device *hdev, void *args)
> +{
> +	struct appleib_hid_event_args *evt_args = args;
> +
> +	return call_driver_func(drv_info, event, int, hdev, evt_args->field,
> +				evt_args->usage, evt_args->value);
> +}
> +
> +static int appleib_hid_event(struct hid_device *hdev, struct hid_field *field,
> +			     struct hid_usage *usage, __s32 value)
> +{
> +	struct appleib_hid_event_args args = {
> +		.field = field,
> +		.usage = usage,
> +		.value = value,
> +	};
> +
> +	return appleib_forward_int_op(hdev, appleib_hid_event_fwd, &args);
> +}
> +
> +static __u8 *appleib_report_fixup(struct hid_device *hdev, __u8 *rdesc,
> +				  unsigned int *rsize)
> +{
> +	/* Some fields have a size of 64 bits, which according to HID 1.11
> +	 * Section 8.4 is not valid ("An item field cannot span more than 4
> +	 * bytes in a report"). Furthermore, hid_field_extract() complains
> +	 * when encountering such a field. So turn them into two 32-bit fields
> +	 * instead.
> +	 */
> +
> +	if (*rsize == 634 &&
> +	    /* Usage Page 0xff12 (vendor defined) */
> +	    rdesc[212] == 0x06 && rdesc[213] == 0x12 && rdesc[214] == 0xff &&
> +	    /* Usage 0x51 */
> +	    rdesc[416] == 0x09 && rdesc[417] == 0x51 &&
> +	    /* report size 64 */
> +	    rdesc[432] == 0x75 && rdesc[433] == 64 &&
> +	    /* report count 1 */
> +	    rdesc[434] == 0x95 && rdesc[435] == 1) {
> +		rdesc[433] = 32;
> +		rdesc[435] = 2;
> +		hid_dbg(hdev, "Fixed up first 64-bit field\n");
> +	}
> +
> +	if (*rsize == 634 &&
> +	    /* Usage Page 0xff12 (vendor defined) */
> +	    rdesc[212] == 0x06 && rdesc[213] == 0x12 && rdesc[214] == 0xff &&
> +	    /* Usage 0x51 */
> +	    rdesc[611] == 0x09 && rdesc[612] == 0x51 &&
> +	    /* report size 64 */
> +	    rdesc[627] == 0x75 && rdesc[628] == 64 &&
> +	    /* report count 1 */
> +	    rdesc[629] == 0x95 && rdesc[630] == 1) {
> +		rdesc[628] = 32;
> +		rdesc[630] = 2;
> +		hid_dbg(hdev, "Fixed up second 64-bit field\n");
> +	}
> +
> +	return rdesc;
> +}
> +
> +static int appleib_input_configured_fwd(struct appleib_hid_drv_info *drv_info,
> +					struct hid_device *hdev, void *args)
> +{
> +	return call_driver_func(drv_info, input_configured, int, hdev,
> +				(struct hid_input *)args);
> +}
> +
> +static int appleib_input_configured(struct hid_device *hdev,
> +				    struct hid_input *hidinput)
> +{
> +	return appleib_forward_int_op(hdev, appleib_input_configured_fwd,
> +				      hidinput);
> +}
> +
> +#ifdef CONFIG_PM
> +static int appleib_hid_suspend_fwd(struct appleib_hid_drv_info *drv_info,
> +				   struct hid_device *hdev, void *args)
> +{
> +	return call_driver_func(drv_info, suspend, int, hdev,
> +				*(pm_message_t *)args);
> +}
> +
> +static int appleib_hid_suspend(struct hid_device *hdev, pm_message_t message)
> +{
> +	return appleib_forward_int_op(hdev, appleib_hid_suspend_fwd, &message);
> +}
> +
> +static int appleib_hid_resume_fwd(struct appleib_hid_drv_info *drv_info,
> +				  struct hid_device *hdev, void *args)
> +{
> +	return call_driver_func(drv_info, resume, int, hdev);
> +}
> +
> +static int appleib_hid_resume(struct hid_device *hdev)
> +{
> +	return appleib_forward_int_op(hdev, appleib_hid_resume_fwd, NULL);
> +}
> +
> +static int appleib_hid_reset_resume_fwd(struct appleib_hid_drv_info *drv_info,
> +					struct hid_device *hdev, void *args)
> +{
> +	return call_driver_func(drv_info, reset_resume, int, hdev);
> +}
> +
> +static int appleib_hid_reset_resume(struct hid_device *hdev)
> +{
> +	return appleib_forward_int_op(hdev, appleib_hid_reset_resume_fwd, NULL);
> +}
> +#endif /* CONFIG_PM */
> +
> +/**
> + * Find the field in the report with the given usage.
> + * @report: the report to search
> + * @field_usage: the usage of the field to search for
> + */
> +struct hid_field *appleib_find_report_field(struct hid_report *report,
> +					    unsigned int field_usage)
> +{
> +	int f, u;
> +
> +	for (f = 0; f < report->maxfield; f++) {
> +		struct hid_field *field = report->field[f];
> +
> +		if (field->logical == field_usage)
> +			return field;
> +
> +		for (u = 0; u < field->maxusage; u++) {
> +			if (field->usage[u].hid == field_usage)
> +				return field;
> +		}
> +	}
> +
> +	return NULL;
> +}
> +EXPORT_SYMBOL_GPL(appleib_find_report_field);
> +
> +/**

Please use correct kernel-doc style rather than parts of it.

> + * Search all the reports of the device for the field with the given usage.
> + * @hdev: the device whose reports to search
> + * @application: the usage of application collection that the field must
> + *               belong to
> + * @field_usage: the usage of the field to search for
> + */
> +struct hid_field *appleib_find_hid_field(struct hid_device *hdev,
> +					 unsigned int application,
> +					 unsigned int field_usage)
> +{
> +	static const int report_types[] = { HID_INPUT_REPORT, HID_OUTPUT_REPORT,
> +					    HID_FEATURE_REPORT };
> +	struct hid_report *report;
> +	struct hid_field *field;
> +	int t;
> +
> +	for (t = 0; t < ARRAY_SIZE(report_types); t++) {
> +		struct list_head *report_list =
> +			    &hdev->report_enum[report_types[t]].report_list;
> +		list_for_each_entry(report, report_list, list) {
> +			if (report->application != application)
> +				continue;
> +
> +			field = appleib_find_report_field(report, field_usage);
> +			if (field)
> +				return field;
> +		}
> +	}
> +
> +	return NULL;
> +}
> +EXPORT_SYMBOL_GPL(appleib_find_hid_field);
> +
> +/**
> + * Return whether we're currently inside a hid_device_probe or not.
> + * @ib_dev: the appleib_device
> + */
> +bool appleib_in_hid_probe(struct appleib_device *ib_dev)
> +{
> +	return ib_dev->in_hid_probe;
> +}
> +EXPORT_SYMBOL_GPL(appleib_in_hid_probe);
> +
> +static struct appleib_hid_dev_info *
> +appleib_add_device(struct appleib_device *ib_dev, struct hid_device *hdev,
> +		   const struct hid_device_id *id)
> +{
> +	struct appleib_hid_dev_info *dev_info;
> +	struct appleib_hid_drv_info *drv_info;
> +
> +	/* allocate device-info for this device */
> +	dev_info = kzalloc(sizeof(*dev_info), GFP_KERNEL);
> +	if (!dev_info)
> +		return NULL;
> +
> +	INIT_LIST_HEAD(&dev_info->drivers);
> +	dev_info->device = hdev;
> +	dev_info->device_id = id;
> +
> +	/* notify all our sub drivers */
> +	mutex_lock(&ib_dev->update_lock);
> +
This is interesting. I'd like to see a comment here on what
this flag is going to do. 

> +	ib_dev->in_hid_probe = true;
> +
> +	list_for_each_entry(drv_info, &ib_dev->hid_drivers, entry) {
> +		appleib_probe_driver(drv_info, dev_info);
> +	}
> +
> +	ib_dev->in_hid_probe = false;
> +
> +	/* remember this new device */
> +	list_add_tail_rcu(&dev_info->entry, &ib_dev->hid_devices);
> +
> +	mutex_unlock(&ib_dev->update_lock);
> +
> +	return dev_info;
> +}
> +
> +static void appleib_remove_device(struct appleib_device *ib_dev,
> +				  struct appleib_hid_dev_info *dev_info)
> +{
> +	list_del_rcu(&dev_info->entry);
> +	synchronize_srcu(&ib_dev->lists_srcu);
> +
> +	appleib_detach_drivers(ib_dev, dev_info);
> +
> +	kfree(dev_info);
> +}
> +
> +static int appleib_hid_probe(struct hid_device *hdev,
> +			     const struct hid_device_id *id)
> +{
> +	struct appleib_device *ib_dev;
> +	struct appleib_hid_dev_info *dev_info;
> +	struct usb_device *udev;
> +	int rc;
> +
> +	/* check usb config first */
> +	udev = hid_to_usb_dev(hdev);
> +
> +	if (udev->actconfig->desc.bConfigurationValue != APPLETB_BASIC_CONFIG) {
> +		rc = usb_driver_set_configuration(udev, APPLETB_BASIC_CONFIG);
> +		return rc ? rc : -ENODEV;
> +	}
> +
> +	/* Assign the driver data */
> +	ib_dev = appleib_dev;
> +	hid_set_drvdata(hdev, ib_dev);
> +
> +	/* initialize the report info */
> +	rc = hid_parse(hdev);
> +	if (rc) {
> +		hid_err(hdev, "ib: hid parse failed (%d)\n", rc);
> +		goto error;
> +	}
> +
> +	/* alloc bufs etc so probe's can send requests; but connect later */
> +	rc = hid_hw_start(hdev, 0);
> +	if (rc) {
> +		hid_err(hdev, "ib: hw start failed (%d)\n", rc);
> +		goto error;
> +	}
> +
> +	/* add this hdev to our device list */
> +	dev_info = appleib_add_device(ib_dev, hdev, id);
> +	if (!dev_info) {
> +		rc = -ENOMEM;
> +		goto stop_hw;
> +	}
> +
> +	/* start the hid */
> +	rc = appleib_start_hid_events(dev_info);
> +	if (rc)
> +		goto remove_dev;
> +
> +	return 0;
> +
> +remove_dev:
> +	mutex_lock(&ib_dev->update_lock);
> +	appleib_remove_device(ib_dev, dev_info);
> +	mutex_unlock(&ib_dev->update_lock);
> +stop_hw:
> +	hid_hw_stop(hdev);
> +error:
> +	return rc;
> +}
> +
> +static void appleib_hid_remove(struct hid_device *hdev)
> +{
> +	struct appleib_device *ib_dev = hid_get_drvdata(hdev);
> +	struct appleib_hid_dev_info *dev_info;
> +
> +	mutex_lock(&ib_dev->update_lock);
> +
> +	list_for_each_entry(dev_info, &ib_dev->hid_devices, entry) {
> +		if (dev_info->device == hdev) {
> +			appleib_stop_hid_events(dev_info);
> +			appleib_remove_device(ib_dev, dev_info);
> +			break;
> +		}
> +	}
> +
> +	mutex_unlock(&ib_dev->update_lock);
> +
> +	hid_hw_stop(hdev);
> +}
> +
> +static const struct hid_device_id appleib_hid_devices[] = {
> +	{ HID_USB_DEVICE(USB_ID_VENDOR_APPLE, USB_ID_PRODUCT_IBRIDGE) },
> +	{ },
> +};
> +
> +static struct hid_driver appleib_hid_driver = {
> +	.name = "apple-ibridge-hid",
> +	.id_table = appleib_hid_devices,
> +	.probe = appleib_hid_probe,
> +	.remove = appleib_hid_remove,
> +	.event = appleib_hid_event,
> +	.report_fixup = appleib_report_fixup,
> +	.input_configured = appleib_input_configured,
> +#ifdef CONFIG_PM
> +	.suspend = appleib_hid_suspend,
> +	.resume = appleib_hid_resume,
> +	.reset_resume = appleib_hid_reset_resume,
> +#endif
> +};
> +
> +static struct appleib_device *appleib_alloc_device(struct acpi_device *acpi_dev)
> +{
> +	struct appleib_device *ib_dev;
> +	acpi_status sts;
> +	int rc;
> +
> +	/* allocate */

Drop comments that don't anything a quick glance at the code would tell you.

> +	ib_dev = kzalloc(sizeof(*ib_dev), GFP_KERNEL);
> +	if (!ib_dev)
> +		return ERR_PTR(-ENOMEM);
> +
> +	/* init structures */
> +	INIT_LIST_HEAD(&ib_dev->hid_drivers);
> +	INIT_LIST_HEAD(&ib_dev->hid_devices);
> +	mutex_init(&ib_dev->update_lock);
> +	init_srcu_struct(&ib_dev->lists_srcu);
> +
> +	ib_dev->acpi_dev = acpi_dev;
> +
> +	/* get iBridge acpi power control method */
> +	sts = acpi_get_handle(acpi_dev->handle, "SOCW", &ib_dev->asoc_socw);
> +	if (ACPI_FAILURE(sts)) {
> +		dev_err(LOG_DEV(ib_dev),
> +			"Error getting handle for ASOC.SOCW method: %s\n",
> +			acpi_format_exception(sts));
> +		rc = -ENXIO;
> +		goto free_mem;
> +	}
> +
> +	/* ensure iBridge is powered on */
> +	sts = acpi_execute_simple_method(ib_dev->asoc_socw, NULL, 1);
> +	if (ACPI_FAILURE(sts))
> +		dev_warn(LOG_DEV(ib_dev), "SOCW(1) failed: %s\n",
> +			 acpi_format_exception(sts));
> +
> +	return ib_dev;
> +
> +free_mem:
> +	kfree(ib_dev);
> +	return ERR_PTR(rc);
> +}
> +
> +static int appleib_probe(struct acpi_device *acpi)
> +{
> +	struct appleib_device *ib_dev;
> +	struct appleib_platform_data *pdata;
Platform_data has a lot of historical meaning in Linux.
Also you have things in here that are not platform related
at all, such as the dev pointer.  Hence I would rename it
as device_data or private or something like that.

> +	int i;
> +	int ret;
> +
> +	if (appleib_dev)
This singleton bothers me a bit. I'm really not sure why it
is necessary.  You can just put a pointer to this in
the pdata for the subdevs and I think that covers most of your
usecases.  It's generally a bad idea to limit things to one instance
of a device unless that actually major simplifications.
I'm not seeing them here.


> +		return -EBUSY;
> +
> +	ib_dev = appleib_alloc_device(acpi);
> +	if (IS_ERR_OR_NULL(ib_dev))
> +		return PTR_ERR(ib_dev);
> +
> +	ib_dev->subdevs = kmemdup(appleib_subdevs, sizeof(appleib_subdevs),
> +				  GFP_KERNEL);
Given this is fixed sized and always referenced via ib_dev->subdevs, just
put the array in there and memcpy into it.  That way you have one less
allocation and simpler code.

> +	if (!ib_dev->subdevs) {
> +		ret = -ENOMEM;
> +		goto free_dev;
> +	}
> +
> +	pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);

Might as well embed this in ib_dev as well.  That would let
you used container_of to avoid having to carry the ib_dev pointer
around in side pdata.

> +	if (!pdata) {
> +		ret = -ENOMEM;
> +		goto free_subdevs;
> +	}
> +
> +	pdata->ib_dev = ib_dev;
> +	pdata->log_dev = LOG_DEV(ib_dev);
> +	for (i = 0; i < ARRAY_SIZE(appleib_subdevs); i++) {
> +		ib_dev->subdevs[i].platform_data = pdata;
> +		ib_dev->subdevs[i].pdata_size = sizeof(*pdata);
> +	}
> +
> +	ret = mfd_add_devices(&acpi->dev, PLATFORM_DEVID_NONE,
> +			      ib_dev->subdevs, ARRAY_SIZE(appleib_subdevs),
> +			      NULL, 0, NULL);
> +	if (ret) {
> +		dev_err(LOG_DEV(ib_dev), "Error adding MFD devices: %d\n", ret);
> +		goto free_pdata;
> +	}
> +
> +	acpi->driver_data = ib_dev;
> +	appleib_dev = ib_dev;
> +
> +	ret = hid_register_driver(&appleib_hid_driver);
> +	if (ret) {
> +		dev_err(LOG_DEV(ib_dev), "Error registering hid driver: %d\n",
> +			ret);
> +		goto rem_mfd_devs;
> +	}
> +
> +	return 0;
> +
> +rem_mfd_devs:
> +	mfd_remove_devices(&acpi->dev);
> +free_pdata:
> +	kfree(pdata);
> +free_subdevs:
> +	kfree(ib_dev->subdevs);
> +free_dev:
> +	appleib_dev = NULL;
> +	acpi->driver_data = NULL;
Why at this point?  It's not set to anything until much later in the
probe flow.  May be worth thinking about devm_ managed allocations
to cleanup some of these allocations automatically and simplify
the error handling.

> +	kfree(ib_dev);
> +	return ret;
> +}
> +
> +static int appleib_remove(struct acpi_device *acpi)
> +{
> +	struct appleib_device *ib_dev = acpi_driver_data(acpi);
> +
> +	mfd_remove_devices(&acpi->dev);
> +	hid_unregister_driver(&appleib_hid_driver);
> +
> +	if (appleib_dev == ib_dev)
From a general reviewability point of view, it's nice to
keep the remove in the same order as the cleanup on
error in probe (and hence reverse of probe). That measn
this should be a little further down.

I'd also like to see a comment on how this condition can be
false.

> +		appleib_dev = NULL;
> +
> +	kfree(ib_dev->subdevs[0].platform_data);
> +	kfree(ib_dev->subdevs);
> +	kfree(ib_dev);

Is it worth considering devm in here to avoid the need to
clean all these up by hand?

> +
> +	return 0;
> +}
> +
> +static int appleib_suspend(struct device *dev)
> +{
> +	struct acpi_device *adev;
> +	struct appleib_device *ib_dev;
> +	int rc;
> +
> +	adev = to_acpi_device(dev);
> +	ib_dev = acpi_driver_data(adev);
Given this appears a few times, probably worth the more compact

	ib_dev = acpi_driver_data(to_acpi_device(dev));

Allowing you to drop the adev local variable that doesn't add
any info.

> +
> +	rc = acpi_execute_simple_method(ib_dev->asoc_socw, NULL, 0);
> +	if (ACPI_FAILURE(rc))
> +		dev_warn(LOG_DEV(ib_dev), "SOCW(0) failed: %s\n",

I can sort of see you might want to do the LOG_DEV for consistency
but here I'm fairly sure it's just dev which might be clearer.

> +			 acpi_format_exception(rc));
> +
> +	return 0;
> +}
> +
> +static int appleib_resume(struct device *dev)
> +{
> +	struct acpi_device *adev;
> +	struct appleib_device *ib_dev;
> +	int rc;
> +
> +	adev = to_acpi_device(dev);
> +	ib_dev = acpi_driver_data(adev);
> +
> +	rc = acpi_execute_simple_method(ib_dev->asoc_socw, NULL, 1);
> +	if (ACPI_FAILURE(rc))
> +		dev_warn(LOG_DEV(ib_dev), "SOCW(1) failed: %s\n",
> +			 acpi_format_exception(rc));
> +
> +	return 0;
> +}
> +
> +static const struct dev_pm_ops appleib_pm = {
> +	.suspend = appleib_suspend,
> +	.resume = appleib_resume,
> +	.restore = appleib_resume,
> +};
> +
> +static const struct acpi_device_id appleib_acpi_match[] = {
> +	{ "APP7777", 0 },
> +	{ },
> +};
> +
> +MODULE_DEVICE_TABLE(acpi, appleib_acpi_match);
> +
> +static struct acpi_driver appleib_driver = {
> +	.name		= "apple-ibridge",
> +	.class		= "topcase", /* ? */
> +	.owner		= THIS_MODULE,
> +	.ids		= appleib_acpi_match,
> +	.ops		= {
> +		.add		= appleib_probe,
> +		.remove		= appleib_remove,
> +	},
> +	.drv		= {
> +		.pm		= &appleib_pm,
> +	},
> +};
> +
> +module_acpi_driver(appleib_driver)
> +
> +MODULE_AUTHOR("Ronald Tschalär");
> +MODULE_DESCRIPTION("Apple iBridge driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/mfd/apple-ibridge.h b/include/linux/mfd/apple-ibridge.h
> new file mode 100644
> index 000000000000..d321714767f7
> --- /dev/null
> +++ b/include/linux/mfd/apple-ibridge.h
> @@ -0,0 +1,39 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Apple iBridge Driver
> + *
> + * Copyright (c) 2018 Ronald Tschalär
> + */
> +
> +#ifndef __LINUX_MFD_APPLE_IBRDIGE_H
> +#define __LINUX_MFD_APPLE_IBRDIGE_H
> +
> +#include <linux/device.h>
> +#include <linux/hid.h>
> +
> +#define PLAT_NAME_IB_TB		"apple-ib-tb"
> +#define PLAT_NAME_IB_ALS	"apple-ib-als"
> +
> +struct appleib_device;
> +
> +struct appleib_platform_data {
> +	struct appleib_device *ib_dev;
> +	struct device *log_dev;
> +};
> +
> +int appleib_register_hid_driver(struct appleib_device *ib_dev,
> +				struct hid_driver *driver, void *data);
> +int appleib_unregister_hid_driver(struct appleib_device *ib_dev,
> +				  struct hid_driver *driver);
> +
> +void *appleib_get_drvdata(struct appleib_device *ib_dev,
> +			  struct hid_driver *driver);
> +bool appleib_in_hid_probe(struct appleib_device *ib_dev);
> +
> +struct hid_field *appleib_find_report_field(struct hid_report *report,
> +					    unsigned int field_usage);
> +struct hid_field *appleib_find_hid_field(struct hid_device *hdev,
> +					 unsigned int application,
> +					 unsigned int field_usage);
> +
> +#endif

^ permalink raw reply

* Re: [PATCH 3/3] iio: light: apple-ib-als: Add driver for ALS on iBridge chip.
From: Jonathan Cameron @ 2019-04-22 12:01 UTC (permalink / raw)
  To: Peter Meerwald-Stadler
  Cc: Ronald Tschalär, Jiri Kosina, Benjamin Tissoires,
	Hartmut Knaack, Lars-Peter Clausen, Lee Jones, linux-input,
	linux-iio, linux-kernel
In-Reply-To: <alpine.DEB.2.21.1904221101420.964@vps.pmeerw.net>

On Mon, 22 Apr 2019 11:17:27 +0200 (CEST)
Peter Meerwald-Stadler <pmeerw@pmeerw.net> wrote:

> On Sun, 21 Apr 2019, Ronald Tschalär wrote:
> 
> > On 2016/2017 MacBook Pro's with a Touch Bar the ALS is attached to,
> > and exposed via the iBridge device. This provides the driver for that
> > sensor.  
> 
> some comments below inline
I'll 'nest' on Peter's review to avoid repetition.

A few additional comments inline.

Thanks,

Jonathan
>  
> > Signed-off-by: Ronald Tschalär <ronald@innovation.ch>
> > ---
> >  drivers/iio/light/Kconfig        |  12 +
> >  drivers/iio/light/Makefile       |   1 +
> >  drivers/iio/light/apple-ib-als.c | 694 +++++++++++++++++++++++++++++++
> >  3 files changed, 707 insertions(+)
> >  create mode 100644 drivers/iio/light/apple-ib-als.c
> > 
> > diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
> > index 36f458433480..49159fab1c0e 100644
> > --- a/drivers/iio/light/Kconfig
> > +++ b/drivers/iio/light/Kconfig
> > @@ -64,6 +64,18 @@ config APDS9960
> >  	  To compile this driver as a module, choose M here: the
> >  	  module will be called apds9960
> >  
> > +config APPLE_IBRIDGE_ALS
> > +	tristate "Apple iBridge ambient light sensor"
> > +	select IIO_BUFFER
> > +	select IIO_TRIGGERED_BUFFER
> > +	depends on MFD_APPLE_IBRIDGE
> > +	help
> > +	  Say Y here to build the driver for the Apple iBridge ALS
> > +	  sensor.
> > +
> > +	  To compile this driver as a module, choose M here: the
> > +	  module will be called apple-ib-als.
> > +
> >  config BH1750
> >  	tristate "ROHM BH1750 ambient light sensor"
> >  	depends on I2C
> > diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
> > index 286bf3975372..144d918917f7 100644
> > --- a/drivers/iio/light/Makefile
> > +++ b/drivers/iio/light/Makefile
> > @@ -9,6 +9,7 @@ obj-$(CONFIG_ADJD_S311)		+= adjd_s311.o
> >  obj-$(CONFIG_AL3320A)		+= al3320a.o
> >  obj-$(CONFIG_APDS9300)		+= apds9300.o
> >  obj-$(CONFIG_APDS9960)		+= apds9960.o
> > +obj-$(CONFIG_APPLE_IBRIDGE_ALS)	+= apple-ib-als.o
> >  obj-$(CONFIG_BH1750)		+= bh1750.o
> >  obj-$(CONFIG_BH1780)		+= bh1780.o
> >  obj-$(CONFIG_CM32181)		+= cm32181.o
> > diff --git a/drivers/iio/light/apple-ib-als.c b/drivers/iio/light/apple-ib-als.c
> > new file mode 100644
> > index 000000000000..1718fcbe304f
> > --- /dev/null
> > +++ b/drivers/iio/light/apple-ib-als.c
> > @@ -0,0 +1,694 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Apple Ambient Light Sensor Driver
> > + *
> > + * Copyright (c) 2017-2018 Ronald Tschalär
> > + */
> > +
> > +/*
> > + * MacBookPro models with an iBridge chip (13,[23] and 14,[23]) have an
> > + * ambient light sensor that is exposed via one of the USB interfaces on
> > + * the iBridge as a standard HID light sensor. However, we cannot use the
> > + * existing hid-sensor-als driver, for two reasons:
> > + *
> > + * 1. The hid-sensor-als driver is part of the hid-sensor-hub which in turn
> > + *    is a hid driver, but you can't have more than one hid driver per hid
> > + *    device, which is a problem because the touch bar also needs to
> > + *    register as a driver for this hid device.
> > + *
> > + * 2. While the hid-sensors-als driver stores sensor readings received via
> > + *    interrupt in an iio buffer, reads on the sysfs
> > + *    .../iio:deviceX/in_illuminance_YYY attribute result in a get of the
> > + *    feature report; however, in the case of this sensor here the
> > + *    illuminance field of that report is always 0. Instead, the input
> > + *    report needs to be requested.
> > + */
> > +
> > +#define dev_fmt(fmt) "als: " fmt
> > +
> > +#include <linux/device.h>
> > +#include <linux/hid.h>
> > +#include <linux/hid-sensor-ids.h>
> > +#include <linux/iio/buffer.h>
> > +#include <linux/iio/iio.h>
> > +#include <linux/iio/trigger_consumer.h>
> > +#include <linux/iio/triggered_buffer.h>
> > +#include <linux/iio/trigger.h>
> > +#include <linux/mfd/apple-ibridge.h>
> > +#include <linux/module.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/slab.h>
> > +
> > +#define APPLEALS_DYN_SENS		0	/* our dynamic sensitivity */
> > +#define APPLEALS_DEF_CHANGE_SENS	APPLEALS_DYN_SENS
> > +
> > +struct appleals_device {
> > +	struct appleib_device	*ib_dev;
> > +	struct device		*log_dev;
> > +	struct hid_device	*hid_dev;
> > +	struct hid_report	*cfg_report;
> > +	struct hid_field	*illum_field;
> > +	struct iio_dev		*iio_dev;
> > +	struct iio_trigger	*iio_trig;
> > +	int			cur_sensitivity;
> > +	int			cur_hysteresis;
> > +	bool			events_enabled;
> > +};
> > +
> > +static struct hid_driver appleals_hid_driver;
> > +
> > +/*
> > + * This is a primitive way to get a relative sensitivity, one where we get
> > + * notified when the value changes by a certain percentage rather than some
> > + * absolute value. MacOS somehow manages to configure the sensor to work this
> > + * way (with a 15% relative sensitivity), but I haven't been able to figure
> > + * out how so far. So until we do, this provides a less-than-perfect
> > + * simulation.
> > + *
> > + * When the brightness value is within one of the ranges, the sensitivity is
> > + * set to that range's sensitivity. But in order to reduce flapping when the
> > + * brightness is right on the border between two ranges, the ranges overlap
> > + * somewhat (by at least one sensitivity), and sensitivity is only changed if
> > + * the value leaves the current sensitivity's range.
> > + *
> > + * The values chosen for the map are somewhat arbitrary: a compromise of not
> > + * too many ranges (and hence changing the sensitivity) but not too small or
> > + * large of a percentage of the min and max values in the range (currently
> > + * from 7.5% to 30%, i.e. within a factor of 2 of 15%), as well as just plain
> > + * "this feels reasonable to me".
> > + */
> > +struct appleals_sensitivity_map {
> > +	int	sensitivity;
> > +	int	illum_low;
> > +	int	illum_high;
> > +};
> > +
> > +static struct appleals_sensitivity_map appleals_sensitivity_map[] = {  
> 
> const?
> 
> > +	{   1,    0,   14 },
> > +	{   3,   10,   40 },
> > +	{   9,   30,  120 },
> > +	{  27,   90,  360 },
> > +	{  81,  270, 1080 },
> > +	{ 243,  810, 3240 },
> > +	{ 729, 2430, 9720 },
> > +};
> > +
> > +static int appleals_compute_sensitivity(int cur_illum, int cur_sens)
> > +{
> > +	struct appleals_sensitivity_map *entry;
> > +	int i;
> > +
> > +	/* see if we're still in current range */
> > +	for (i = 0; i < ARRAY_SIZE(appleals_sensitivity_map); i++) {
> > +		entry = &appleals_sensitivity_map[i];
> > +
> > +		if (entry->sensitivity == cur_sens &&
> > +		    entry->illum_low <= cur_illum &&
> > +		    entry->illum_high >= cur_illum)
> > +			return cur_sens;
> > +		else if (entry->sensitivity > cur_sens)
> > +			break;
> > +	}
> > +
> > +	/* not in current range, so find new sensitivity */
> > +	for (i = 0; i < ARRAY_SIZE(appleals_sensitivity_map); i++) {
> > +		entry = &appleals_sensitivity_map[i];
> > +
> > +		if (entry->illum_low <= cur_illum &&
> > +		    entry->illum_high >= cur_illum)
> > +			return entry->sensitivity;
> > +	}
> > +
> > +	/* hmm, not in table, so assume we are above highest range */
> > +	i = ARRAY_SIZE(appleals_sensitivity_map) - 1;
> > +	return appleals_sensitivity_map[i].sensitivity;
> > +}
> > +
> > +static int appleals_get_field_value_for_usage(struct hid_field *field,
> > +					      unsigned int usage)
> > +{
> > +	int u;
> > +
> > +	if (!field)
> > +		return -1;
> > +
> > +	for (u = 0; u < field->maxusage; u++) {
> > +		if (field->usage[u].hid == usage)
> > +			return u + field->logical_minimum;
> > +	}
> > +
> > +	return -1;
> > +}
> > +
> > +static __s32 appleals_get_field_value(struct appleals_device *als_dev,
> > +				      struct hid_field *field)
> > +{
> > +	hid_hw_request(als_dev->hid_dev, field->report, HID_REQ_GET_REPORT);
> > +	hid_hw_wait(als_dev->hid_dev);
> > +
> > +	return field->value[0];
> > +}
> > +
> > +static void appleals_set_field_value(struct appleals_device *als_dev,
> > +				     struct hid_field *field, __s32 value)
> > +{
> > +	hid_set_field(field, 0, value);
> > +	hid_hw_request(als_dev->hid_dev, field->report, HID_REQ_SET_REPORT);
> > +}
> > +
> > +static int appleals_get_config(struct appleals_device *als_dev,
> > +			       unsigned int field_usage, __s32 *value)
> > +{
> > +	struct hid_field *field;
> > +
> > +	field = appleib_find_report_field(als_dev->cfg_report, field_usage);
> > +	if (!field)
> > +		return -EINVAL;
> > +
> > +	*value = appleals_get_field_value(als_dev, field);
> > +
> > +	return 0;
> > +}
> > +
> > +static int appleals_set_config(struct appleals_device *als_dev,
> > +			       unsigned int field_usage, __s32 value)
> > +{
> > +	struct hid_field *field;
> > +
> > +	field = appleib_find_report_field(als_dev->cfg_report, field_usage);
> > +	if (!field)
> > +		return -EINVAL;
> > +
> > +	appleals_set_field_value(als_dev, field, value);
> > +
> > +	return 0;
> > +}
> > +
> > +static int appleals_set_enum_config(struct appleals_device *als_dev,
> > +				    unsigned int field_usage,
> > +				    unsigned int value_usage)
> > +{
> > +	struct hid_field *field;
> > +	int value;
> > +
> > +	field = appleib_find_report_field(als_dev->cfg_report, field_usage);
> > +	if (!field)
> > +		return -EINVAL;
> > +
> > +	value = appleals_get_field_value_for_usage(field, value_usage);  
> 
> can return -1, not checked
> 
> > +
> > +	appleals_set_field_value(als_dev, field, value);
> > +
> > +	return 0;
> > +}
> > +
> > +static void appleals_update_dyn_sensitivity(struct appleals_device *als_dev,
> > +					    __s32 value)
> > +{
> > +	int new_sens;
> > +	int rc;
> > +
> > +	new_sens = appleals_compute_sensitivity(value,
> > +						als_dev->cur_sensitivity);
> > +	if (new_sens != als_dev->cur_sensitivity) {
> > +		rc = appleals_set_config(als_dev,
> > +			HID_USAGE_SENSOR_LIGHT_ILLUM |
> > +			HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS,
> > +			new_sens);
> > +		if (!rc)
> > +			als_dev->cur_sensitivity = new_sens;
> > +	}
> > +}
> > +
> > +static void appleals_push_new_value(struct appleals_device *als_dev,
> > +				    __s32 value)
> > +{
> > +	__s32 buf[2] = { value, value };
> > +
> > +	iio_push_to_buffers(als_dev->iio_dev, buf);
> > +
> > +	if (als_dev->cur_hysteresis == APPLEALS_DYN_SENS)
> > +		appleals_update_dyn_sensitivity(als_dev, value);
> > +}
> > +
> > +static int appleals_hid_event(struct hid_device *hdev, struct hid_field *field,
> > +			      struct hid_usage *usage, __s32 value)
> > +{
> > +	struct appleals_device *als_dev =
> > +		appleib_get_drvdata(hid_get_drvdata(hdev),
> > +				    &appleals_hid_driver);
> > +	int rc = 0;
> > +
> > +	if ((usage->hid & HID_USAGE_PAGE) != HID_UP_SENSOR)
> > +		return 0;
> > +
> > +	if (usage->hid == HID_USAGE_SENSOR_LIGHT_ILLUM) {
> > +		appleals_push_new_value(als_dev, value);
> > +		rc = 1;
> > +	}
> > +
> > +	return rc;
> > +}
> > +
> > +static int appleals_enable_events(struct iio_trigger *trig, bool enable)
> > +{
> > +	struct appleals_device *als_dev = iio_trigger_get_drvdata(trig);
> > +	int value;
> > +
> > +	/* set the sensor's reporting state */
> > +	appleals_set_enum_config(als_dev, HID_USAGE_SENSOR_PROP_REPORT_STATE,
> > +		enable ? HID_USAGE_SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM :
> > +			 HID_USAGE_SENSOR_PROP_REPORTING_STATE_NO_EVENTS_ENUM);
> > +	als_dev->events_enabled = enable;
> > +
> > +	/* if the sensor was enabled, push an initial value */
> > +	if (enable) {
> > +		value = appleals_get_field_value(als_dev, als_dev->illum_field);
> > +		appleals_push_new_value(als_dev, value);
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int appleals_read_raw(struct iio_dev *iio_dev,
> > +			     struct iio_chan_spec const *chan,
> > +			     int *val, int *val2, long mask)
> > +{
> > +	struct appleals_device *als_dev =
> > +				*(struct appleals_device **)iio_priv(iio_dev);
> > +	__s32 value;
> > +	int rc;
> > +
> > +	*val = 0;
> > +	*val2 = 0;  
> 
> no need to set these here
> 
> > +
> > +	switch (mask) {
> > +	case IIO_CHAN_INFO_RAW:
> > +	case IIO_CHAN_INFO_PROCESSED:
> > +		*val = appleals_get_field_value(als_dev, als_dev->illum_field);

How can one read by both processed and raw?

> > +		return IIO_VAL_INT;
> > +
> > +	case IIO_CHAN_INFO_SAMP_FREQ:
> > +		rc = appleals_get_config(als_dev,
> > +					 HID_USAGE_SENSOR_PROP_REPORT_INTERVAL,
> > +					 &value);
> > +		if (rc)
> > +			return rc;
> > +
> > +		/* interval is in ms; val is in HZ, val2 in µHZ */
> > +		value = 1000000000 / value;
> > +		*val = value / 1000000;
> > +		*val2 = value - (*val * 1000000);
> > +
> > +		return IIO_VAL_INT_PLUS_MICRO;
> > +
> > +	case IIO_CHAN_INFO_HYSTERESIS:
> > +		if (als_dev->cur_hysteresis == APPLEALS_DYN_SENS) {
> > +			*val = als_dev->cur_hysteresis;
> > +			return IIO_VAL_INT;
> > +		}
> > +
> > +		rc = appleals_get_config(als_dev,
> > +			HID_USAGE_SENSOR_LIGHT_ILLUM |
> > +			HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS,
> > +			val);
> > +		if (!rc) {
> > +			als_dev->cur_sensitivity = *val;
> > +			als_dev->cur_hysteresis = *val;
> > +		}
> > +		return rc ? rc : IIO_VAL_INT;
> > +
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +}
> > +
> > +static int appleals_write_raw(struct iio_dev *iio_dev,
> > +			      struct iio_chan_spec const *chan,
> > +			      int val, int val2, long mask)
> > +{
> > +	struct appleals_device *als_dev =
> > +				*(struct appleals_device **)iio_priv(iio_dev);
> > +	__s32 illum;
> > +	int rc;
> > +
> > +	switch (mask) {
> > +	case IIO_CHAN_INFO_SAMP_FREQ:
> > +		rc = appleals_set_config(als_dev,
> > +					 HID_USAGE_SENSOR_PROP_REPORT_INTERVAL,
> > +					 1000000000 / (val * 1000000 + val2));
> > +		break;  
> 
> maybe return directly instead of at the end (matter of taste);
> here and in the other cases below
> 
> > +
> > +	case IIO_CHAN_INFO_HYSTERESIS:
> > +		if (val == APPLEALS_DYN_SENS) {
> > +			if (als_dev->cur_hysteresis != APPLEALS_DYN_SENS) {
> > +				als_dev->cur_hysteresis = val;
> > +				illum = appleals_get_field_value(als_dev,
> > +							als_dev->illum_field);
> > +				appleals_update_dyn_sensitivity(als_dev, illum);

There is some debate in another thread on whether dynamic sensitivity can be
mapped to hysteresis or not...

> > +			}
> > +			rc = 0;
> > +			break;
> > +		}
> > +
> > +		rc = appleals_set_config(als_dev,
> > +			HID_USAGE_SENSOR_LIGHT_ILLUM |
> > +			HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS,
> > +			val);
> > +		if (!rc) {
> > +			als_dev->cur_sensitivity = val;
> > +			als_dev->cur_hysteresis = val;
> > +		}
> > +		break;
> > +
> > +	default:
> > +		rc = -EINVAL;
> > +	}
> > +
> > +	return rc;
> > +}
> > +
> > +static const struct iio_chan_spec appleals_channels[] = {
> > +	{
> > +		.type = IIO_INTENSITY,
> > +		.modified = 1,
> > +		.channel2 = IIO_MOD_LIGHT_BOTH,
> > +		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
> > +			BIT(IIO_CHAN_INFO_RAW),
Why return both processed and raw?  We don't generally allow that in IIO
(there are a few historical exceptions due to us getting it wrong the
first time).

> > +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) |
> > +			BIT(IIO_CHAN_INFO_HYSTERESIS),
> > +		.scan_type = {
> > +			.sign = 'u',
> > +			.realbits = 32,
> > +			.storagebits = 32,
> > +		},
> > +		.scan_index = 0,
> > +	},
> > +	{
> > +		.type = IIO_LIGHT,
> > +		.info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
> > +			BIT(IIO_CHAN_INFO_RAW),
> > +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) |
> > +			BIT(IIO_CHAN_INFO_HYSTERESIS),
> > +		.scan_type = {
> > +			.sign = 'u',
> > +			.realbits = 32,
> > +			.storagebits = 32,
> > +		},
> > +		.scan_index = 1,
> > +	}
> > +};
> > +
> > +static const struct iio_trigger_ops appleals_trigger_ops = {
> > +	.set_trigger_state = &appleals_enable_events,
> > +};
> > +
> > +static const struct iio_info appleals_info = {
> > +	.read_raw = &appleals_read_raw,
> > +	.write_raw = &appleals_write_raw,
> > +};
> > +
> > +static void appleals_config_sensor(struct appleals_device *als_dev,
> > +				   bool events_enabled, int sensitivity)
> > +{
> > +	struct hid_field *field;
> > +	__s32 val;
> > +
> > +	/*
> > +	 * We're (often) in a probe here, so need to enable input processing
> > +	 * in that case, but only in that case.
> > +	 */
I'd be a little happier if this wasn't termed 'in_hid_probe' but rather something
like _needs_io_start.

That way we are reflecting what needs to happen, not where we are in the code
flow that makes it true.

> > +	if (appleib_in_hid_probe(als_dev->ib_dev))
> > +		hid_device_io_start(als_dev->hid_dev);
> > +
> > +	/* power on the sensor */
> > +	field = appleib_find_report_field(als_dev->cfg_report,
> > +					  HID_USAGE_SENSOR_PROY_POWER_STATE);
> > +	val = appleals_get_field_value_for_usage(field,
> > +			HID_USAGE_SENSOR_PROP_POWER_STATE_D0_FULL_POWER_ENUM);  
> 
> what if -1?
> 
> > +	hid_set_field(field, 0, val);
> > +
> > +	/* configure reporting of change events */
> > +	field = appleib_find_report_field(als_dev->cfg_report,
> > +					  HID_USAGE_SENSOR_PROP_REPORT_STATE);
> > +	val = appleals_get_field_value_for_usage(field,
> > +		events_enabled ?
> > +			HID_USAGE_SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM :
> > +			HID_USAGE_SENSOR_PROP_REPORTING_STATE_NO_EVENTS_ENUM);
> > +	hid_set_field(field, 0, val);
> > +
> > +	/* report change events asap */
> > +	field = appleib_find_report_field(als_dev->cfg_report,
> > +					 HID_USAGE_SENSOR_PROP_REPORT_INTERVAL);
> > +	hid_set_field(field, 0, field->logical_minimum);
> > +
> > +	/*
> > +	 * Set initial change sensitivity; if dynamic, enabling trigger will set
> > +	 * it instead.
> > +	 */
> > +	if (sensitivity != APPLEALS_DYN_SENS) {
> > +		field = appleib_find_report_field(als_dev->cfg_report,
> > +			HID_USAGE_SENSOR_LIGHT_ILLUM |
> > +			HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS);
> > +
> > +		hid_set_field(field, 0, sensitivity);
> > +	}
> > +
> > +	/* write the new config to the sensor */
> > +	hid_hw_request(als_dev->hid_dev, als_dev->cfg_report,
> > +		       HID_REQ_SET_REPORT);
> > +
> > +	if (appleib_in_hid_probe(als_dev->ib_dev))
> > +		hid_device_io_stop(als_dev->hid_dev);
> > +};  
> 
> no semicolon at the end of a function please
> 
> > +
> > +static int appleals_config_iio(struct appleals_device *als_dev)
> > +{
> > +	struct iio_dev *iio_dev;
> > +	struct iio_trigger *iio_trig;
> > +	int rc;
> > +
> > +	/* create and register iio device */
A very good reason for not adding superficial comments.
This one is wrong or at least misleading.  It doesn't register
the iio device.  So get rid of all these comments that are
just saying what the code next to them is doing.  Keep the ones
that add extra information.


> > +	iio_dev = iio_device_alloc(sizeof(als_dev));  
> 
> how about using the devm_ variants?
> 
> > +	if (!iio_dev)
> > +		return -ENOMEM;
> > +
> > +	*(struct appleals_device **)iio_priv(iio_dev) = als_dev;

That is nasty.  Add a local variable to make to clear that iio_priv(iio_dev)
is actually some space for this pointer. Complex type casting is not
terribly readable.

> > +
> > +	iio_dev->channels = appleals_channels;
> > +	iio_dev->num_channels = ARRAY_SIZE(appleals_channels);
> > +	iio_dev->dev.parent = &als_dev->hid_dev->dev;
> > +	iio_dev->info = &appleals_info;
> > +	iio_dev->name = "als";
> > +	iio_dev->modes = INDIO_DIRECT_MODE;
> > +
> > +	rc = iio_triggered_buffer_setup(iio_dev, &iio_pollfunc_store_time, NULL,
> > +					NULL);
> > +	if (rc) {
> > +		dev_err(als_dev->log_dev, "failed to set up iio triggers: %d\n",  
> 
> just one trigger?
Also wrong as it's not actually setting up the trigger at all ;) It's setting
up the handling for what to do when a trigger event occurs.

> 
> > +			rc);
> > +		goto free_iio_dev;
> > +	}
> > +
> > +	iio_trig = iio_trigger_alloc("%s-dev%d", iio_dev->name, iio_dev->id);
> > +	if (!iio_trig) {
> > +		rc = -ENOMEM;
> > +		goto clean_trig_buf;
> > +	}
> > +
> > +	iio_trig->dev.parent = &als_dev->hid_dev->dev;
> > +	iio_trig->ops = &appleals_trigger_ops;
> > +	iio_trigger_set_drvdata(iio_trig, als_dev);
> > +
> > +	rc = iio_trigger_register(iio_trig);
> > +	if (rc) {
> > +		dev_err(als_dev->log_dev, "failed to register iio trigger: %d\n",  
> 
> some messages start lowercase, some uppercase (nitpicking)
> 
> > +			rc);
> > +		goto free_iio_trig;
> > +	}
> > +
> > +	als_dev->iio_trig = iio_trig;
> > +
> > +	rc = iio_device_register(iio_dev);
> > +	if (rc) {
> > +		dev_err(als_dev->log_dev, "failed to register iio device: %d\n",
> > +			rc);
> > +		goto unreg_iio_trig;
> > +	}
> > +
> > +	als_dev->iio_dev = iio_dev;

I really don't like nest of pointers going on in here.  I haven't dug
down to check if any of them can be remove, but they are definitely
ugly to deal with.

> > +
> > +	return 0;
> > +
> > +unreg_iio_trig:
> > +	iio_trigger_unregister(iio_trig);
> > +free_iio_trig:
> > +	iio_trigger_free(iio_trig);
> > +	als_dev->iio_trig = NULL;
> > +clean_trig_buf:
> > +	iio_triggered_buffer_cleanup(iio_dev);
> > +free_iio_dev:
> > +	iio_device_free(iio_dev);
> > +
> > +	return rc;
> > +}
> > +
> > +static int appleals_probe(struct hid_device *hdev,
> > +			  const struct hid_device_id *id)
> > +{
> > +	struct appleals_device *als_dev =
> > +		appleib_get_drvdata(hid_get_drvdata(hdev),
> > +				    &appleals_hid_driver);
> > +	struct hid_field *state_field;
> > +	struct hid_field *illum_field;
> > +	int rc;
> > +
> > +	/* find als fields and reports */
> > +	state_field = appleib_find_hid_field(hdev, HID_USAGE_SENSOR_ALS,
> > +					    HID_USAGE_SENSOR_PROP_REPORT_STATE);
> > +	illum_field = appleib_find_hid_field(hdev, HID_USAGE_SENSOR_ALS,
> > +					     HID_USAGE_SENSOR_LIGHT_ILLUM);
> > +	if (!state_field || !illum_field)
> > +		return -ENODEV;
> > +
> > +	if (als_dev->hid_dev) {
> > +		dev_warn(als_dev->log_dev,
> > +			 "Found duplicate ambient light sensor - ignoring\n");

I'll bite.  So how can this actually happen?  What fundamentally breaks in
your model if there really are two?  Can you fix whatever that is so
as to drop this handling?

> > +		return -EBUSY;
> > +	}
> > +
> > +	dev_info(als_dev->log_dev, "Found ambient light sensor\n");  
> 
> in general avoid logging for the OK case, it just clutters the log
> 
> > +
> > +	/* initialize device */
> > +	als_dev->hid_dev = hdev;
> > +	als_dev->cfg_report = state_field->report;
> > +	als_dev->illum_field = illum_field;
> > +
> > +	als_dev->cur_hysteresis = APPLEALS_DEF_CHANGE_SENS;
> > +	als_dev->cur_sensitivity = APPLEALS_DEF_CHANGE_SENS;
> > +	appleals_config_sensor(als_dev, false, als_dev->cur_sensitivity);
> > +
> > +	rc = appleals_config_iio(als_dev);
> > +	if (rc)
> > +		return rc;
> > +
> > +	return 0;

return appleals_config_iio(als_dev);

> > +}
> > +
> > +static void appleals_remove(struct hid_device *hdev)
> > +{
> > +	struct appleals_device *als_dev =
> > +		appleib_get_drvdata(hid_get_drvdata(hdev),
> > +				    &appleals_hid_driver);
> > +  
> 
> could be a lot less if devm_ were used?
> 
> > +	if (als_dev->iio_dev) {
> > +		iio_device_unregister(als_dev->iio_dev);
> > +
> > +		iio_trigger_unregister(als_dev->iio_trig);
> > +		iio_trigger_free(als_dev->iio_trig);
> > +		als_dev->iio_trig = NULL;

I would be decidedly worried if you actually have any paths
where setting these to NULL do anything useful. By the time
we get here we should absolutely 'know' nothing will touch
these pointers.

It is these that are blocking Peter's suggestion of using
devm to clean all of this up automatically for you.

> > +
> > +		iio_triggered_buffer_cleanup(als_dev->iio_dev);
> > +		iio_device_free(als_dev->iio_dev);
> > +		als_dev->iio_dev = NULL;
> > +	}
> > +
> > +	als_dev->hid_dev = NULL;
> > +}
> > +
> > +#ifdef CONFIG_PM
> > +static int appleals_reset_resume(struct hid_device *hdev)
> > +{
> > +	struct appleals_device *als_dev =
> > +		appleib_get_drvdata(hid_get_drvdata(hdev),
> > +				    &appleals_hid_driver);
> > +
> > +	appleals_config_sensor(als_dev, als_dev->events_enabled,
> > +			       als_dev->cur_sensitivity);
> > +
> > +	return 0;
> > +}
> > +#endif
> > +
> > +static struct hid_driver appleals_hid_driver = {
> > +	.name = "apple-ib-als",
> > +	.probe = appleals_probe,
> > +	.remove = appleals_remove,
> > +	.event = appleals_hid_event,
> > +#ifdef CONFIG_PM
> > +	.reset_resume = appleals_reset_resume,
> > +#endif
> > +};
> > +
> > +static int appleals_platform_probe(struct platform_device *pdev)
> > +{
> > +	struct appleib_platform_data *pdata = pdev->dev.platform_data;
> > +	struct appleib_device *ib_dev = pdata->ib_dev;
> > +	struct appleals_device *als_dev;
> > +	int rc;
> > +
> > +	als_dev = kzalloc(sizeof(*als_dev), GFP_KERNEL);
> > +	if (!als_dev)
> > +		return -ENOMEM;
> > +
> > +	als_dev->ib_dev = ib_dev;
> > +	als_dev->log_dev = pdata->log_dev;
> > +
> > +	rc = appleib_register_hid_driver(ib_dev, &appleals_hid_driver, als_dev);

Hmm. This is all a bit odd.  We register a platform device, to call it's driver
so that it can then register another driver?  I'll let Lee comment on that but
it does seem overly complex.

> > +	if (rc) {
> > +		dev_err(als_dev->log_dev, "Error registering hid driver: %d\n",
> > +			rc);
> > +		goto error;
> > +	}
> > +
> > +	platform_set_drvdata(pdev, als_dev);
> > +
> > +	return 0;
> > +
> > +error:
> > +	kfree(als_dev);
> > +	return rc;
> > +}
> > +
> > +static int appleals_platform_remove(struct platform_device *pdev)
> > +{
> > +	struct appleib_platform_data *pdata = pdev->dev.platform_data;
> > +	struct appleib_device *ib_dev = pdata->ib_dev;
> > +	struct appleals_device *als_dev = platform_get_drvdata(pdev);
> > +	int rc;
> > +
> > +	rc = appleib_unregister_hid_driver(ib_dev, &appleals_hid_driver);
> > +	if (rc) {
> > +		dev_err(als_dev->log_dev,
> > +			"Error unregistering hid driver: %d\n", rc);

Given you are going to have this error printing in every sub driver and
the text is totally generic, can you just move it into the core?

Same applies for similar cases above.

> > +		goto error;
> > +	}
> > +
> > +	kfree(als_dev);

Use managed allocation to avoid needing to clean this up?

> > +
> > +	return 0;
> > +
> > +error:
> > +	return rc;
> > +}
> > +
> > +static const struct platform_device_id appleals_platform_ids[] = {
> > +	{ .name = PLAT_NAME_IB_ALS },
> > +	{ }
> > +};
> > +MODULE_DEVICE_TABLE(platform, appleals_platform_ids);
> > +
> > +static struct platform_driver appleals_platform_driver = {
> > +	.id_table = appleals_platform_ids,
> > +	.driver = {
> > +		.name	= "apple-ib-als",
> > +	},
> > +	.probe = appleals_platform_probe,
> > +	.remove = appleals_platform_remove,
> > +};
> > +
> > +module_platform_driver(appleals_platform_driver);
> > +
> > +MODULE_AUTHOR("Ronald Tschalär");
> > +MODULE_DESCRIPTION("Apple iBridge ALS driver");
> > +MODULE_LICENSE("GPL v2");
> >   
> 

^ permalink raw reply

* Re: [PATCH v4 1/3] dt-bindings: input: add GPIO controllable vibrator
From: Rob Herring @ 2019-04-22 13:13 UTC (permalink / raw)
  To: Luca Weiss
  Cc: Dmitry Torokhov, Mark Rutland, Mauro Carvalho Chehab,
	Pascal PAILLET-LME, Coly Li, Lee Jones, Xiaotong Lu, Brian Masney,
	Baolin Wang, David Brown, open list:ARM/QUALCOMM SUPPORT,
	open list:INPUT (KEYBOARD, MOUSE, JOYSTICK, TOUCHSCREEN)...,
	open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
	open list
In-Reply-To: <20190420122333.23662-1-luca@z3ntu.xyz>

On Sat, Apr 20, 2019 at 7:24 AM Luca Weiss <luca@z3ntu.xyz> wrote:
>
> Provide a simple driver for GPIO controllable vibrators.
> It will be used by the Fairphone 2.
>
> Signed-off-by: Luca Weiss <luca@z3ntu.xyz>
> ---
> Changes from v3:
> - Convert .txt based doc to the new yaml based format
>
>  .../bindings/input/gpio-vibrator.yaml         | 39 +++++++++++++++++++
>  1 file changed, 39 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/input/gpio-vibrator.yaml
>
> diff --git a/Documentation/devicetree/bindings/input/gpio-vibrator.yaml b/Documentation/devicetree/bindings/input/gpio-vibrator.yaml
> new file mode 100644
> index 000000000000..bca1b6ea07a4
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/input/gpio-vibrator.yaml
> @@ -0,0 +1,39 @@
> +# SPDX-License-Identifier: GPL-2.0
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/bindings/input/gpio-vibrator.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: GPIO vibrator
> +
> +maintainers:
> +  - Luca Weiss <luca@z3ntu.xyz>
> +
> +description: |+
> +  Registers a GPIO device as vibrator, where the on/off capability is controlled by a GPIO.
> +
> +properties:
> +  compatible:

> +    items:
> +      - const: gpio-vibrator

These 2 lines can be simplified to just 'const: gpio-vibrator'.

> +
> +  enable-gpios:
> +    maxItems: 1
> +
> +  vcc-supply:
> +    $ref: /schemas/types.yaml#/definitions/phandle

This isn't necessary as we can do this for every occurrence of
'*-supply'. I'll add that to the core schema.

With those 2 changes:

Reviewed-by: Rob Herring <robh@kernel.org>

> +    description: Regulator that provides power
> +
> +required:
> +  - compatible
> +  - enable-gpios
> +
> +examples:
> +  - |
> +    #include <dt-bindings/gpio/gpio.h>
> +
> +    vibrator {
> +        compatible = "gpio-vibrator";
> +        enable-gpios = <&msmgpio 86 GPIO_ACTIVE_HIGH>;
> +        vcc-supply = <&pm8941_l18>;
> +    };
> --
> 2.21.0
>

^ permalink raw reply

* [PATCH v2 59/79] docs: hid: convert to ReST
From: Mauro Carvalho Chehab @ 2019-04-22 13:27 UTC (permalink / raw)
  To: Linux Doc Mailing List
  Cc: Mauro Carvalho Chehab, Mauro Carvalho Chehab, linux-kernel,
	Jonathan Corbet, Jiri Kosina, Jonathan Cameron,
	Srinivas Pandruvada, Benjamin Tissoires, Dmitry Torokhov,
	linux-input, linux-iio, linux-usb
In-Reply-To: <cover.1555938375.git.mchehab+samsung@kernel.org>

Rename the HID documentation files to ReST, add an
index for them and adjust in order to produce a nice html
output via the Sphinx build system.

While here, fix the sysfs example from hid-sensor.txt, that
has a lot of "?" instead of the proper UTF-8 characters that
are produced by the tree command.

At its new index.rst, let's add a :orphan: while this is not linked to
the main index.rst file, in order to avoid build warnings.

Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
---
 .../hid/{hid-alps.txt => hid-alps.rst}        |  85 ++-
 .../hid/{hid-sensor.txt => hid-sensor.rst}    | 192 +++----
 .../{hid-transport.txt => hid-transport.rst}  |  82 ++-
 Documentation/hid/{hiddev.txt => hiddev.rst}  | 130 +++--
 Documentation/hid/{hidraw.txt => hidraw.rst}  |  53 +-
 Documentation/hid/index.rst                   |  18 +
 Documentation/hid/intel-ish-hid.rst           | 485 ++++++++++++++++++
 Documentation/hid/intel-ish-hid.txt           | 454 ----------------
 Documentation/hid/{uhid.txt => uhid.rst}      |  46 +-
 Documentation/input/input.rst                 |   2 +-
 MAINTAINERS                                   |   2 +-
 11 files changed, 885 insertions(+), 664 deletions(-)
 rename Documentation/hid/{hid-alps.txt => hid-alps.rst} (64%)
 rename Documentation/hid/{hid-sensor.txt => hid-sensor.rst} (61%)
 rename Documentation/hid/{hid-transport.txt => hid-transport.rst} (93%)
 rename Documentation/hid/{hiddev.txt => hiddev.rst} (80%)
 rename Documentation/hid/{hidraw.txt => hidraw.rst} (89%)
 create mode 100644 Documentation/hid/index.rst
 create mode 100644 Documentation/hid/intel-ish-hid.rst
 delete mode 100644 Documentation/hid/intel-ish-hid.txt
 rename Documentation/hid/{uhid.txt => uhid.rst} (94%)

diff --git a/Documentation/hid/hid-alps.txt b/Documentation/hid/hid-alps.rst
similarity index 64%
rename from Documentation/hid/hid-alps.txt
rename to Documentation/hid/hid-alps.rst
index 6b02a2447c77..e2f4c4c11e3f 100644
--- a/Documentation/hid/hid-alps.txt
+++ b/Documentation/hid/hid-alps.rst
@@ -1,19 +1,26 @@
+==========================
 ALPS HID Touchpad Protocol
-----------------------
+==========================
 
 Introduction
 ------------
 Currently ALPS HID driver supports U1 Touchpad device.
 
-U1 devuce basic information.
+U1 device basic information.
+
+==========	======
 Vender ID	0x044E
 Product ID	0x120B
 Version ID	0x0121
+==========	======
 
 
 HID Descriptor
-------------
+--------------
+
+=======	====================	=====	=======================================
 Byte	Field			Value	Notes
+=======	====================	=====	=======================================
 0	wHIDDescLength		001E	Length of HID Descriptor : 30 bytes
 2	bcdVersion		0100	Compliant with Version 1.00
 4	wReportDescLength	00B2	Report Descriptor is 178 Bytes (0x00B2)
@@ -28,32 +35,42 @@ Byte	Field			Value	Notes
 22	wProductID		120B	Product ID 0x120B
 24	wVersionID		0121	Version 01.21
 26	RESERVED		0000	RESERVED
+=======	====================	=====	=======================================
 
 
 Report ID
-------------
-ReportID-1	(Input Reports)	(HIDUsage-Mouse) for TP&SP
-ReportID-2	(Input Reports)	(HIDUsage-keyboard) for TP
-ReportID-3	(Input Reports)	(Vendor Usage: Max 10 finger data) for TP
-ReportID-4	(Input Reports)	(Vendor Usage: ON bit data) for GP
-ReportID-5	(Feature Reports)	Feature Reports
-ReportID-6	(Input Reports)	(Vendor Usage: StickPointer data) for SP
-ReportID-7	(Feature Reports)	Flash update (Bootloader)
+---------
+
+==========	=================  =========================================
+ReportID-1	(Input Reports)	   (HIDUsage-Mouse) for TP&SP
+ReportID-2	(Input Reports)	   (HIDUsage-keyboard) for TP
+ReportID-3	(Input Reports)	   (Vendor Usage: Max 10 finger data) for TP
+ReportID-4	(Input Reports)	   (Vendor Usage: ON bit data) for GP
+ReportID-5	(Feature Reports)  Feature Reports
+ReportID-6	(Input Reports)	   (Vendor Usage: StickPointer data) for SP
+ReportID-7	(Feature Reports)  Flash update (Bootloader)
+==========	=================  =========================================
 
 
 Data pattern
 ------------
+
+=====	==========	=====	=================
 Case1	ReportID_1	TP/SP	Relative/Relative
 Case2	ReportID_3	TP	Absolute
 	ReportID_6	SP	Absolute
+=====	==========	=====	=================
 
 
 Command Read/Write
 ------------------
 To read/write to RAM, need to send a commands to the device.
+
 The command format is as below.
 
 DataByte(SET_REPORT)
+
+=====	======================
 Byte1	Command Byte
 Byte2	Address - Byte 0 (LSB)
 Byte3	Address - Byte 1
@@ -61,13 +78,19 @@ Byte4	Address - Byte 2
 Byte5	Address - Byte 3 (MSB)
 Byte6	Value Byte
 Byte7	Checksum
+=====	======================
 
 Command Byte is read=0xD1/write=0xD2 .
+
 Address is read/write RAM address.
+
 Value Byte is writing data when you send the write commands.
+
 When you read RAM, there is no meaning.
 
 DataByte(GET_REPORT)
+
+=====	======================
 Byte1	Response Byte
 Byte2	Address - Byte 0 (LSB)
 Byte3	Address - Byte 1
@@ -75,6 +98,7 @@ Byte4	Address - Byte 2
 Byte5	Address - Byte 3 (MSB)
 Byte6	Value Byte
 Byte7	Checksum
+=====	======================
 
 Read value is stored in Value Byte.
 
@@ -82,7 +106,11 @@ Read value is stored in Value Byte.
 Packet Format
 Touchpad data byte
 ------------------
-	b7	b6	b5	b4	b3	b2	b1	b0
+
+
+======= ======= ======= ======= ======= ======= ======= ======= =====
+-	b7	b6	b5	b4	b3	b2	b1	b0
+======= ======= ======= ======= ======= ======= ======= ======= =====
 1	0	0	SW6	SW5	SW4	SW3	SW2	SW1
 2	0	0	0	Fcv	Fn3	Fn2	Fn1	Fn0
 3	Xa0_7	Xa0_6	Xa0_5	Xa0_4	Xa0_3	Xa0_2	Xa0_1	Xa0_0
@@ -114,17 +142,25 @@ Touchpad data byte
 25	Ya4_7	Ya4_6	Ya4_5	Ya4_4	Ya4_3	Ya4_2	Ya4_1	Ya4_0
 26	Ya4_15	Ya4_14	Ya4_13	Ya4_12	Ya4_11	Ya4_10	Ya4_9	Ya4_8
 27	LFB4	Zs4_6	Zs4_5	Zs4_4	Zs4_3	Zs4_2	Zs4_1	Zs4_0
+======= ======= ======= ======= ======= ======= ======= ======= =====
 
 
-SW1-SW6:	SW ON/OFF status
-Xan_15-0(16bit):X Absolute data of the "n"th finger
-Yan_15-0(16bit):Y Absolute data of the "n"th finger
-Zsn_6-0(7bit):	Operation area of the "n"th finger
+SW1-SW6:
+	SW ON/OFF status
+Xan_15-0(16bit):
+	X Absolute data of the "n"th finger
+Yan_15-0(16bit):
+	Y Absolute data of the "n"th finger
+Zsn_6-0(7bit):
+	Operation area of the "n"th finger
 
 
 StickPointer data byte
-------------------
-	b7	b6	b5	b4	b3	b2	b1	b0
+----------------------
+
+======= ======= ======= ======= ======= ======= ======= ======= =====
+-	b7	b6	b5	b4	b3	b2	b1	b0
+======= ======= ======= ======= ======= ======= ======= ======= =====
 Byte1	1	1	1	0	1	SW3	SW2	SW1
 Byte2	X7	X6	X5	X4	X3	X2	X1	X0
 Byte3	X15	X14	X13	X12	X11	X10	X9	X8
@@ -132,8 +168,13 @@ Byte4	Y7	Y6	Y5	Y4	Y3	Y2	Y1	Y0
 Byte5	Y15	Y14	Y13	Y12	Y11	Y10	Y9	Y8
 Byte6	Z7	Z6	Z5	Z4	Z3	Z2	Z1	Z0
 Byte7	T&P	Z14	Z13	Z12	Z11	Z10	Z9	Z8
+======= ======= ======= ======= ======= ======= ======= ======= =====
 
-SW1-SW3:	SW ON/OFF status
-Xn_15-0(16bit):X Absolute data
-Yn_15-0(16bit):Y Absolute data
-Zn_14-0(15bit):Z
+SW1-SW3:
+	SW ON/OFF status
+Xn_15-0(16bit):
+	X Absolute data
+Yn_15-0(16bit):
+	Y Absolute data
+Zn_14-0(15bit):
+	Z
diff --git a/Documentation/hid/hid-sensor.txt b/Documentation/hid/hid-sensor.rst
similarity index 61%
rename from Documentation/hid/hid-sensor.txt
rename to Documentation/hid/hid-sensor.rst
index b287752a31cd..758972e34971 100644
--- a/Documentation/hid/hid-sensor.txt
+++ b/Documentation/hid/hid-sensor.rst
@@ -1,6 +1,6 @@
-
+=====================
 HID Sensors Framework
-======================
+=====================
 HID sensor framework provides necessary interfaces to implement sensor drivers,
 which are connected to a sensor hub. The sensor hub is a HID device and it provides
 a report descriptor conforming to HID 1.12 sensor usage tables.
@@ -15,22 +15,22 @@ the drivers themselves."
 This specification describes many usage IDs, which describe the type of sensor
 and also the individual data fields. Each sensor can have variable number of
 data fields. The length and order is specified in the report descriptor. For
-example a part of report descriptor can look like:
+example a part of report descriptor can look like::
 
-   INPUT(1)[INPUT]
- ..
-    Field(2)
-      Physical(0020.0073)
-      Usage(1)
-        0020.045f
-      Logical Minimum(-32767)
-      Logical Maximum(32767)
-      Report Size(8)
-      Report Count(1)
-      Report Offset(16)
-      Flags(Variable Absolute)
-..
-..
+     INPUT(1)[INPUT]
+   ..
+      Field(2)
+        Physical(0020.0073)
+        Usage(1)
+          0020.045f
+        Logical Minimum(-32767)
+        Logical Maximum(32767)
+        Report Size(8)
+        Report Count(1)
+        Report Offset(16)
+        Flags(Variable Absolute)
+  ..
+  ..
 
 The report is indicating "sensor page (0x20)" contains an accelerometer-3D (0x73).
 This accelerometer-3D has some fields. Here for example field 2 is motion intensity
@@ -40,13 +40,14 @@ data will use this format.
 
 
 Implementation
-=================
+==============
 
 This specification defines many different types of sensors with different sets of
 data fields. It is difficult to have a common input event to user space applications,
 for different sensors. For example an accelerometer can send X,Y and Z data, whereas
 an ambient light sensor can send illumination data.
 So the implementation has two parts:
+
 - Core hid driver
 - Individual sensor processing part (sensor drivers)
 
@@ -55,8 +56,11 @@ Core driver
 The core driver registers (hid-sensor-hub) registers as a HID driver. It parses
 report descriptors and identifies all the sensors present. It adds an MFD device
 with name HID-SENSOR-xxxx (where xxxx is usage id from the specification).
-For example
+
+For example:
+
 HID-SENSOR-200073 is registered for an Accelerometer 3D driver.
+
 So if any driver with this name is inserted, then the probe routine for that
 function will be called. So an accelerometer processing driver can register
 with this name and will be probed if there is an accelerometer-3D detected.
@@ -66,7 +70,8 @@ drivers to register and get events for that usage id. Also it provides parsing
 functions, which get and set each input/feature/output report.
 
 Individual sensor processing part (sensor drivers)
------------
+--------------------------------------------------
+
 The processing driver will use an interface provided by the core driver to parse
 the report and get the indexes of the fields and also can get events. This driver
 can use IIO interface to use the standard ABI defined for a type of sensor.
@@ -75,31 +80,34 @@ can use IIO interface to use the standard ABI defined for a type of sensor.
 Core driver Interface
 =====================
 
-Callback structure:
-Each processing driver can use this structure to set some callbacks.
+Callback structure::
+
+  Each processing driver can use this structure to set some callbacks.
 	int (*suspend)(..): Callback when HID suspend is received
 	int (*resume)(..): Callback when HID resume is received
 	int (*capture_sample)(..): Capture a sample for one of its data fields
 	int (*send_event)(..): One complete event is received which can have
                                multiple data fields.
 
-Registration functions:
-int sensor_hub_register_callback(struct hid_sensor_hub_device *hsdev,
+Registration functions::
+
+  int sensor_hub_register_callback(struct hid_sensor_hub_device *hsdev,
 			u32 usage_id,
 			struct hid_sensor_hub_callbacks *usage_callback):
 
 Registers callbacks for an usage id. The callback functions are not allowed
-to sleep.
+to sleep::
 
 
-int sensor_hub_remove_callback(struct hid_sensor_hub_device *hsdev,
+  int sensor_hub_remove_callback(struct hid_sensor_hub_device *hsdev,
 			u32 usage_id):
 
 Removes callbacks for an usage id.
 
 
-Parsing function:
-int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev,
+Parsing function::
+
+  int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev,
 			u8 type,
 			u32 usage_id, u32 attr_usage_id,
 			struct hid_sensor_hub_attribute_info *info);
@@ -110,26 +118,27 @@ so that fields can be set or get individually.
 These indexes avoid searching every time and getting field index to get or set.
 
 
-Set Feature report
-int sensor_hub_set_feature(struct hid_sensor_hub_device *hsdev, u32 report_id,
+Set Feature report::
+
+  int sensor_hub_set_feature(struct hid_sensor_hub_device *hsdev, u32 report_id,
 			u32 field_index, s32 value);
 
 This interface is used to set a value for a field in feature report. For example
 if there is a field report_interval, which is parsed by a call to
-sensor_hub_input_get_attribute_info before, then it can directly set that individual
-field.
+sensor_hub_input_get_attribute_info before, then it can directly set that
+individual field::
 
 
-int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id,
+  int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id,
 			u32 field_index, s32 *value);
 
 This interface is used to get a value for a field in input report. For example
 if there is a field report_interval, which is parsed by a call to
-sensor_hub_input_get_attribute_info before, then it can directly get that individual
-field value.
+sensor_hub_input_get_attribute_info before, then it can directly get that
+individual field value::
 
 
-int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev,
+  int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev,
 			u32 usage_id,
 			u32 attr_usage_id, u32 report_id);
 
@@ -143,6 +152,8 @@ registered callback function to process the sample.
 ----------
 
 HID Custom and generic Sensors
+------------------------------
+
 
 HID Sensor specification defines two special sensor usage types. Since they
 don't represent a standard sensor, it is not possible to define using Linux IIO
@@ -158,66 +169,73 @@ keyboard attached/detached or lid open/close.
 To allow application to utilize these sensors, here they are exported uses sysfs
 attribute groups, attributes and misc device interface.
 
-An example of this representation on sysfs:
-/sys/devices/pci0000:00/INT33C2:00/i2c-0/i2c-INT33D1:00/0018:8086:09FA.0001/HID-SENSOR-2000e1.6.auto$ tree -R
-.
-????????? enable_sensor
-????????? feature-0-200316
-??????? ????????? feature-0-200316-maximum
-??????? ????????? feature-0-200316-minimum
-??????? ????????? feature-0-200316-name
-??????? ????????? feature-0-200316-size
-??????? ????????? feature-0-200316-unit-expo
-??????? ????????? feature-0-200316-units
-??????? ????????? feature-0-200316-value
-????????? feature-1-200201
-??????? ????????? feature-1-200201-maximum
-??????? ????????? feature-1-200201-minimum
-??????? ????????? feature-1-200201-name
-??????? ????????? feature-1-200201-size
-??????? ????????? feature-1-200201-unit-expo
-??????? ????????? feature-1-200201-units
-??????? ????????? feature-1-200201-value
-????????? input-0-200201
-??????? ????????? input-0-200201-maximum
-??????? ????????? input-0-200201-minimum
-??????? ????????? input-0-200201-name
-??????? ????????? input-0-200201-size
-??????? ????????? input-0-200201-unit-expo
-??????? ????????? input-0-200201-units
-??????? ????????? input-0-200201-value
-????????? input-1-200202
-??????? ????????? input-1-200202-maximum
-??????? ????????? input-1-200202-minimum
-??????? ????????? input-1-200202-name
-??????? ????????? input-1-200202-size
-??????? ????????? input-1-200202-unit-expo
-??????? ????????? input-1-200202-units
-??????? ????????? input-1-200202-value
+An example of this representation on sysfs::
+
+  /sys/devices/pci0000:00/INT33C2:00/i2c-0/i2c-INT33D1:00/0018:8086:09FA.0001/HID-SENSOR-2000e1.6.auto$ tree -R
+  .
+  │   ├──  enable_sensor
+  │   │   ├── feature-0-200316
+  │   │   │   ├── feature-0-200316-maximum
+  │   │   │   ├── feature-0-200316-minimum
+  │   │   │   ├── feature-0-200316-name
+  │   │   │   ├── feature-0-200316-size
+  │   │   │   ├── feature-0-200316-unit-expo
+  │   │   │   ├── feature-0-200316-units
+  │   │   │   ├── feature-0-200316-value
+  │   │   ├── feature-1-200201
+  │   │   │   ├── feature-1-200201-maximum
+  │   │   │   ├── feature-1-200201-minimum
+  │   │   │   ├── feature-1-200201-name
+  │   │   │   ├── feature-1-200201-size
+  │   │   │   ├── feature-1-200201-unit-expo
+  │   │   │   ├── feature-1-200201-units
+  │   │   │   ├── feature-1-200201-value
+  │   │   ├── input-0-200201
+  │   │   │   ├── input-0-200201-maximum
+  │   │   │   ├── input-0-200201-minimum
+  │   │   │   ├── input-0-200201-name
+  │   │   │   ├── input-0-200201-size
+  │   │   │   ├── input-0-200201-unit-expo
+  │   │   │   ├── input-0-200201-units
+  │   │   │   ├── input-0-200201-value
+  │   │   ├── input-1-200202
+  │   │   │   ├── input-1-200202-maximum
+  │   │   │   ├── input-1-200202-minimum
+  │   │   │   ├── input-1-200202-name
+  │   │   │   ├── input-1-200202-size
+  │   │   │   ├── input-1-200202-unit-expo
+  │   │   │   ├── input-1-200202-units
+  │   │   │   ├── input-1-200202-value
 
 Here there is a custom sensors with four fields, two feature and two inputs.
 Each field is represented by a set of attributes. All fields except the "value"
 are read only. The value field is a RW field.
-Example
-/sys/bus/platform/devices/HID-SENSOR-2000e1.6.auto/feature-0-200316$ grep -r . *
-feature-0-200316-maximum:6
-feature-0-200316-minimum:0
-feature-0-200316-name:property-reporting-state
-feature-0-200316-size:1
-feature-0-200316-unit-expo:0
-feature-0-200316-units:25
-feature-0-200316-value:1
+
+Example::
+
+  /sys/bus/platform/devices/HID-SENSOR-2000e1.6.auto/feature-0-200316$ grep -r . *
+  feature-0-200316-maximum:6
+  feature-0-200316-minimum:0
+  feature-0-200316-name:property-reporting-state
+  feature-0-200316-size:1
+  feature-0-200316-unit-expo:0
+  feature-0-200316-units:25
+  feature-0-200316-value:1
 
 How to enable such sensor?
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
 By default sensor can be power gated. To enable sysfs attribute "enable" can be
-used.
-$ echo 1 > enable_sensor
+used::
+
+	$ echo 1 > enable_sensor
 
 Once enabled and powered on, sensor can report value using HID reports.
-These reports are pushed using misc device interface in a FIFO order.
-/dev$ tree | grep HID-SENSOR-2000e1.6.auto
-??????? ????????? 10:53 -> ../HID-SENSOR-2000e1.6.auto
-????????? HID-SENSOR-2000e1.6.auto
+These reports are pushed using misc device interface in a FIFO order::
+
+	/dev$ tree | grep HID-SENSOR-2000e1.6.auto
+	│   │   │   ├── 10:53 -> ../HID-SENSOR-2000e1.6.auto
+	│   ├──  HID-SENSOR-2000e1.6.auto
 
 Each reports can be of variable length preceded by a header. This header
 consist of a 32 bit usage id, 64 bit time stamp and 32 bit length field of raw
diff --git a/Documentation/hid/hid-transport.txt b/Documentation/hid/hid-transport.rst
similarity index 93%
rename from Documentation/hid/hid-transport.txt
rename to Documentation/hid/hid-transport.rst
index 3dcba9fd4a3a..6f3aaa86ce7b 100644
--- a/Documentation/hid/hid-transport.txt
+++ b/Documentation/hid/hid-transport.rst
@@ -1,5 +1,6 @@
-                          HID I/O Transport Drivers
-                         ===========================
+=========================
+HID I/O Transport Drivers
+=========================
 
 The HID subsystem is independent of the underlying transport driver. Initially,
 only USB was supported, but other specifications adopted the HID design and
@@ -16,6 +17,8 @@ transport and device setup/management. HID core is responsible of
 report-parsing, report interpretation and the user-space API. Device specifics
 and quirks are handled by all layers depending on the quirk.
 
+::
+
  +-----------+  +-----------+            +-----------+  +-----------+
  | Device #1 |  | Device #i |            | Device #j |  | Device #k |
  +-----------+  +-----------+            +-----------+  +-----------+
@@ -42,8 +45,9 @@ and quirks are handled by all layers depending on the quirk.
  +----------------+  +-----------+  +------------------+  +------------------+
 
 Example Drivers:
-  I/O: USB, I2C, Bluetooth-l2cap
-  Transport: USB-HID, I2C-HID, BT-HIDP
+
+  - I/O: USB, I2C, Bluetooth-l2cap
+  - Transport: USB-HID, I2C-HID, BT-HIDP
 
 Everything below "HID Core" is simplified in this graph as it is only of
 interest to HID device drivers. Transport drivers do not need to know the
@@ -183,7 +187,7 @@ Other ctrl-channel requests are supported by USB-HID but are not available
 -------------------
 
 Transport drivers normally use the following procedure to register a new device
-with HID core:
+with HID core::
 
 	struct hid_device *hid;
 	int ret;
@@ -215,7 +219,7 @@ Once hid_add_device() is entered, HID core might use the callbacks provided in
 "custom_ll_driver". Note that fields like "country" can be ignored by underlying
 transport-drivers if not supported.
 
-To unregister a device, use:
+To unregister a device, use::
 
 	hid_destroy_device(hid);
 
@@ -226,73 +230,110 @@ driver callbacks.
 -----------------------------
 
 The available HID callbacks are:
- - int (*start) (struct hid_device *hdev)
+
+   ::
+
+      int (*start) (struct hid_device *hdev)
+
    Called from HID device drivers once they want to use the device. Transport
    drivers can choose to setup their device in this callback. However, normally
    devices are already set up before transport drivers register them to HID core
    so this is mostly only used by USB-HID.
 
- - void (*stop) (struct hid_device *hdev)
+   ::
+
+      void (*stop) (struct hid_device *hdev)
+
    Called from HID device drivers once they are done with a device. Transport
    drivers can free any buffers and deinitialize the device. But note that
    ->start() might be called again if another HID device driver is loaded on the
    device.
+
    Transport drivers are free to ignore it and deinitialize devices after they
    destroyed them via hid_destroy_device().
 
- - int (*open) (struct hid_device *hdev)
+   ::
+
+      int (*open) (struct hid_device *hdev)
+
    Called from HID device drivers once they are interested in data reports.
    Usually, while user-space didn't open any input API/etc., device drivers are
    not interested in device data and transport drivers can put devices asleep.
    However, once ->open() is called, transport drivers must be ready for I/O.
    ->open() calls are nested for each client that opens the HID device.
 
- - void (*close) (struct hid_device *hdev)
+   ::
+
+      void (*close) (struct hid_device *hdev)
+
    Called from HID device drivers after ->open() was called but they are no
    longer interested in device reports. (Usually if user-space closed any input
    devices of the driver).
+
    Transport drivers can put devices asleep and terminate any I/O of all
    ->open() calls have been followed by a ->close() call. However, ->start() may
    be called again if the device driver is interested in input reports again.
 
- - int (*parse) (struct hid_device *hdev)
+   ::
+
+      int (*parse) (struct hid_device *hdev)
+
    Called once during device setup after ->start() has been called. Transport
    drivers must read the HID report-descriptor from the device and tell HID core
    about it via hid_parse_report().
 
- - int (*power) (struct hid_device *hdev, int level)
+   ::
+
+      int (*power) (struct hid_device *hdev, int level)
+
    Called by HID core to give PM hints to transport drivers. Usually this is
    analogical to the ->open() and ->close() hints and redundant.
 
- - void (*request) (struct hid_device *hdev, struct hid_report *report,
-                    int reqtype)
+   ::
+
+      void (*request) (struct hid_device *hdev, struct hid_report *report,
+		       int reqtype)
+
    Send an HID request on the ctrl channel. "report" contains the report that
    should be sent and "reqtype" the request type. Request-type can be
    HID_REQ_SET_REPORT or HID_REQ_GET_REPORT.
+
    This callback is optional. If not provided, HID core will assemble a raw
    report following the HID specs and send it via the ->raw_request() callback.
    The transport driver is free to implement this asynchronously.
 
- - int (*wait) (struct hid_device *hdev)
+   ::
+
+      int (*wait) (struct hid_device *hdev)
+
    Used by HID core before calling ->request() again. A transport driver can use
    it to wait for any pending requests to complete if only one request is
    allowed at a time.
 
- - int (*raw_request) (struct hid_device *hdev, unsigned char reportnum,
-                       __u8 *buf, size_t count, unsigned char rtype,
-                       int reqtype)
+   ::
+
+      int (*raw_request) (struct hid_device *hdev, unsigned char reportnum,
+                          __u8 *buf, size_t count, unsigned char rtype,
+                          int reqtype)
+
    Same as ->request() but provides the report as raw buffer. This request shall
    be synchronous. A transport driver must not use ->wait() to complete such
    requests. This request is mandatory and hid core will reject the device if
    it is missing.
 
- - int (*output_report) (struct hid_device *hdev, __u8 *buf, size_t len)
+   ::
+
+      int (*output_report) (struct hid_device *hdev, __u8 *buf, size_t len)
+
    Send raw output report via intr channel. Used by some HID device drivers
    which require high throughput for outgoing requests on the intr channel. This
    must not cause SET_REPORT calls! This must be implemented as asynchronous
    output report on the intr channel!
 
- - int (*idle) (struct hid_device *hdev, int report, int idle, int reqtype)
+   ::
+
+      int (*idle) (struct hid_device *hdev, int report, int idle, int reqtype)
+
    Perform SET/GET_IDLE request. Only used by USB-HID, do not implement!
 
 2.3) Data Path
@@ -314,4 +355,5 @@ transport driver and not passed to hid_input_report().
 Acknowledgements to SET_REPORT requests are not of interest to HID core.
 
 ----------------------------------------------------
+
 Written 2013, David Herrmann <dh.herrmann@gmail.com>
diff --git a/Documentation/hid/hiddev.txt b/Documentation/hid/hiddev.rst
similarity index 80%
rename from Documentation/hid/hiddev.txt
rename to Documentation/hid/hiddev.rst
index 638448707aa2..16c663530db2 100644
--- a/Documentation/hid/hiddev.txt
+++ b/Documentation/hid/hiddev.rst
@@ -1,6 +1,9 @@
+================================================
 Care and feeding of your Human Interface Devices
+================================================
 
-INTRODUCTION
+Introduction
+============
 
 In addition to the normal input type HID devices, USB also uses the
 human interface device protocols for things that are not really human
@@ -16,7 +19,7 @@ normalised event interface - see Documentation/input/input.rst
 * the hiddev interface, which provides fairly raw HID events
 
 The data flow for a HID event produced by a device is something like
-the following :
+the following::
 
  usb.c ---> hid-core.c  ----> hid-input.c ----> [keyboard/mouse/joystick/event]
                          |
@@ -27,27 +30,29 @@ In addition, other subsystems (apart from USB) can potentially feed
 events into the input subsystem, but these have no effect on the hid
 device interface.
 
-USING THE HID DEVICE INTERFACE
+Using the HID Device Interface
+==============================
 
 The hiddev interface is a char interface using the normal USB major,
 with the minor numbers starting at 96 and finishing at 111. Therefore,
-you need the following commands:
-mknod /dev/usb/hiddev0 c 180 96
-mknod /dev/usb/hiddev1 c 180 97
-mknod /dev/usb/hiddev2 c 180 98
-mknod /dev/usb/hiddev3 c 180 99
-mknod /dev/usb/hiddev4 c 180 100
-mknod /dev/usb/hiddev5 c 180 101
-mknod /dev/usb/hiddev6 c 180 102
-mknod /dev/usb/hiddev7 c 180 103
-mknod /dev/usb/hiddev8 c 180 104
-mknod /dev/usb/hiddev9 c 180 105
-mknod /dev/usb/hiddev10 c 180 106
-mknod /dev/usb/hiddev11 c 180 107
-mknod /dev/usb/hiddev12 c 180 108
-mknod /dev/usb/hiddev13 c 180 109
-mknod /dev/usb/hiddev14 c 180 110
-mknod /dev/usb/hiddev15 c 180 111
+you need the following commands::
+
+	mknod /dev/usb/hiddev0 c 180 96
+	mknod /dev/usb/hiddev1 c 180 97
+	mknod /dev/usb/hiddev2 c 180 98
+	mknod /dev/usb/hiddev3 c 180 99
+	mknod /dev/usb/hiddev4 c 180 100
+	mknod /dev/usb/hiddev5 c 180 101
+	mknod /dev/usb/hiddev6 c 180 102
+	mknod /dev/usb/hiddev7 c 180 103
+	mknod /dev/usb/hiddev8 c 180 104
+	mknod /dev/usb/hiddev9 c 180 105
+	mknod /dev/usb/hiddev10 c 180 106
+	mknod /dev/usb/hiddev11 c 180 107
+	mknod /dev/usb/hiddev12 c 180 108
+	mknod /dev/usb/hiddev13 c 180 109
+	mknod /dev/usb/hiddev14 c 180 110
+	mknod /dev/usb/hiddev15 c 180 111
 
 So you point your hiddev compliant user-space program at the correct
 interface for your device, and it all just works.
@@ -56,7 +61,9 @@ Assuming that you have a hiddev compliant user-space program, of
 course. If you need to write one, read on.
 
 
-THE HIDDEV API
+The HIDDEV API
+==============
+
 This description should be read in conjunction with the HID
 specification, freely available from http://www.usb.org, and
 conveniently linked of http://www.linux-usb.org.
@@ -69,12 +76,14 @@ each of which can have one or more "usages".  In the hid-core,
 each one of these usages has a single signed 32 bit value.
 
 read():
+-------
+
 This is the event interface.  When the HID device's state changes,
 it performs an interrupt transfer containing a report which contains
 the changed value.  The hid-core.c module parses the report, and
 returns to hiddev.c the individual usages that have changed within
 the report.  In its basic mode, the hiddev will make these individual
-usage changes available to the reader using a struct hiddev_event:
+usage changes available to the reader using a struct hiddev_event::
 
        struct hiddev_event {
            unsigned hid;
@@ -91,12 +100,18 @@ ioctl() described below.
 
 
 ioctl(): 
+--------
+
 This is the control interface. There are a number of controls: 
 
-HIDIOCGVERSION - int (read)
-Gets the version code out of the hiddev driver.
+HIDIOCGVERSION
+  - int (read)
+
+ Gets the version code out of the hiddev driver.
+
+HIDIOCAPPLICATION
+  - (none)
 
-HIDIOCAPPLICATION - (none)
 This ioctl call returns the HID application usage associated with the
 hid device. The third argument to ioctl() specifies which application
 index to get. This is useful when the device has more than one
@@ -106,7 +121,9 @@ returns -1. You can find out beforehand how many application
 collections the device has from the num_applications field from the
 hiddev_devinfo structure. 
 
-HIDIOCGCOLLECTIONINFO - struct hiddev_collection_info (read/write)
+HIDIOCGCOLLECTIONINFO
+  - struct hiddev_collection_info (read/write)
+
 This returns a superset of the information above, providing not only
 application collections, but all the collections the device has.  It
 also returns the level the collection lives in the hierarchy.
@@ -115,14 +132,20 @@ field set to the index that should be returned.  The ioctl fills in
 the other fields.  If the index is larger than the last collection 
 index, the ioctl returns -1 and sets errno to -EINVAL.
 
-HIDIOCGDEVINFO - struct hiddev_devinfo (read)
+HIDIOCGDEVINFO
+  - struct hiddev_devinfo (read)
+
 Gets a hiddev_devinfo structure which describes the device.
 
-HIDIOCGSTRING - struct hiddev_string_descriptor (read/write)
+HIDIOCGSTRING
+  - struct hiddev_string_descriptor (read/write)
+
 Gets a string descriptor from the device. The caller must fill in the
 "index" field to indicate which descriptor should be returned.
 
-HIDIOCINITREPORT - (none)
+HIDIOCINITREPORT
+  - (none)
+
 Instructs the kernel to retrieve all input and feature report values
 from the device. At this point, all the usage structures will contain
 current values for the device, and will maintain it as the device
@@ -130,21 +153,29 @@ changes.  Note that the use of this ioctl is unnecessary in general,
 since later kernels automatically initialize the reports from the
 device at attach time.
 
-HIDIOCGNAME - string (variable length)
+HIDIOCGNAME
+  - string (variable length)
+
 Gets the device name
 
-HIDIOCGREPORT - struct hiddev_report_info (write)
+HIDIOCGREPORT
+  - struct hiddev_report_info (write)
+
 Instructs the kernel to get a feature or input report from the device,
 in order to selectively update the usage structures (in contrast to
 INITREPORT).
 
-HIDIOCSREPORT - struct hiddev_report_info (write)
+HIDIOCSREPORT
+  - struct hiddev_report_info (write)
+
 Instructs the kernel to send a report to the device. This report can
 be filled in by the user through HIDIOCSUSAGE calls (below) to fill in
 individual usage values in the report before sending the report in full
 to the device. 
 
-HIDIOCGREPORTINFO - struct hiddev_report_info (read/write)
+HIDIOCGREPORTINFO
+  - struct hiddev_report_info (read/write)
+
 Fills in a hiddev_report_info structure for the user. The report is
 looked up by type (input, output or feature) and id, so these fields
 must be filled in by the user. The ID can be absolute -- the actual
@@ -156,19 +187,25 @@ use the relative IDs above to enumerate the valid IDs. The ioctl
 returns non-zero when there is no more next ID. The real report ID is
 filled into the returned hiddev_report_info structure. 
 
-HIDIOCGFIELDINFO - struct hiddev_field_info (read/write)
+HIDIOCGFIELDINFO
+  - struct hiddev_field_info (read/write)
+
 Returns the field information associated with a report in a
 hiddev_field_info structure. The user must fill in report_id and
 report_type in this structure, as above. The field_index should also
 be filled in, which should be a number from 0 and maxfield-1, as
 returned from a previous HIDIOCGREPORTINFO call. 
 
-HIDIOCGUCODE - struct hiddev_usage_ref (read/write)
+HIDIOCGUCODE
+  - struct hiddev_usage_ref (read/write)
+
 Returns the usage_code in a hiddev_usage_ref structure, given that
 given its report type, report id, field index, and index within the
 field have already been filled into the structure.
 
-HIDIOCGUSAGE - struct hiddev_usage_ref (read/write)
+HIDIOCGUSAGE
+  - struct hiddev_usage_ref (read/write)
+
 Returns the value of a usage in a hiddev_usage_ref structure. The
 usage to be retrieved can be specified as above, or the user can
 choose to fill in the report_type field and specify the report_id as
@@ -176,28 +213,37 @@ HID_REPORT_ID_UNKNOWN. In this case, the hiddev_usage_ref will be
 filled in with the report and field information associated with this
 usage if it is found. 
 
-HIDIOCSUSAGE - struct hiddev_usage_ref (write)
+HIDIOCSUSAGE
+  - struct hiddev_usage_ref (write)
+
 Sets the value of a usage in an output report.  The user fills in
 the hiddev_usage_ref structure as above, but additionally fills in
 the value field.
 
-HIDIOGCOLLECTIONINDEX - struct hiddev_usage_ref (write)
+HIDIOGCOLLECTIONINDEX
+  - struct hiddev_usage_ref (write)
+
 Returns the collection index associated with this usage.  This
 indicates where in the collection hierarchy this usage sits.
 
-HIDIOCGFLAG - int (read)
-HIDIOCSFLAG - int (write)
+HIDIOCGFLAG
+  - int (read)
+HIDIOCSFLAG
+  - int (write)
+
 These operations respectively inspect and replace the mode flags
 that influence the read() call above.  The flags are as follows:
 
-    HIDDEV_FLAG_UREF - read() calls will now return 
+    HIDDEV_FLAG_UREF
+      - read() calls will now return
         struct hiddev_usage_ref instead of struct hiddev_event.
         This is a larger structure, but in situations where the
         device has more than one usage in its reports with the
         same usage code, this mode serves to resolve such
         ambiguity.
 
-    HIDDEV_FLAG_REPORT - This flag can only be used in conjunction
+    HIDDEV_FLAG_REPORT
+      - This flag can only be used in conjunction
         with HIDDEV_FLAG_UREF.  With this flag set, when the device
         sends a report, a struct hiddev_usage_ref will be returned
         to read() filled in with the report_type and report_id, but 
diff --git a/Documentation/hid/hidraw.txt b/Documentation/hid/hidraw.rst
similarity index 89%
rename from Documentation/hid/hidraw.txt
rename to Documentation/hid/hidraw.rst
index c8436e354f44..4a4a0ba1f362 100644
--- a/Documentation/hid/hidraw.txt
+++ b/Documentation/hid/hidraw.rst
@@ -1,5 +1,6 @@
-      HIDRAW - Raw Access to USB and Bluetooth Human Interface Devices
-     ==================================================================
+================================================================
+HIDRAW - Raw Access to USB and Bluetooth Human Interface Devices
+================================================================
 
 The hidraw driver provides a raw interface to USB and Bluetooth Human
 Interface Devices (HIDs).  It differs from hiddev in that reports sent and
@@ -31,6 +32,7 @@ directly under /dev (eg: /dev/hidraw0).  As this location is distribution-
 and udev rule-dependent, applications should use libudev to locate hidraw
 devices attached to the system.  There is a tutorial on libudev with a
 working example at:
+
 	http://www.signal11.us/oss/udev/
 
 The HIDRAW API
@@ -51,7 +53,7 @@ byte.  For devices which do not use numbered reports, the report data
 will begin at the first byte.
 
 write()
---------
+-------
 The write() function will write a report to the device. For USB devices, if
 the device has an INTERRUPT OUT endpoint, the report will be sent on that
 endpoint. If it does not, the report will be sent over the control endpoint,
@@ -62,38 +64,52 @@ number.  If the device does not use numbered reports, the first byte should
 be set to 0. The report data itself should begin at the second byte.
 
 ioctl()
---------
+-------
 Hidraw supports the following ioctls:
 
-HIDIOCGRDESCSIZE: Get Report Descriptor Size
+HIDIOCGRDESCSIZE:
+	Get Report Descriptor Size
+
 This ioctl will get the size of the device's report descriptor.
 
-HIDIOCGRDESC: Get Report Descriptor
+HIDIOCGRDESC:
+	Get Report Descriptor
+
 This ioctl returns the device's report descriptor using a
 hidraw_report_descriptor struct.  Make sure to set the size field of the
 hidraw_report_descriptor struct to the size returned from HIDIOCGRDESCSIZE.
 
-HIDIOCGRAWINFO: Get Raw Info
+HIDIOCGRAWINFO:
+	Get Raw Info
+
 This ioctl will return a hidraw_devinfo struct containing the bus type, the
 vendor ID (VID), and product ID (PID) of the device. The bus type can be one
-of:
-	BUS_USB
-	BUS_HIL
-	BUS_BLUETOOTH
-	BUS_VIRTUAL
+of::
+
+	- BUS_USB
+	- BUS_HIL
+	- BUS_BLUETOOTH
+	- BUS_VIRTUAL
+
 which are defined in uapi/linux/input.h.
 
-HIDIOCGRAWNAME(len): Get Raw Name
+HIDIOCGRAWNAME(len):
+	Get Raw Name
+
 This ioctl returns a string containing the vendor and product strings of
 the device.  The returned string is Unicode, UTF-8 encoded.
 
-HIDIOCGRAWPHYS(len): Get Physical Address
+HIDIOCGRAWPHYS(len):
+	Get Physical Address
+
 This ioctl returns a string representing the physical address of the device.
 For USB devices, the string contains the physical path to the device (the
 USB controller, hubs, ports, etc).  For Bluetooth devices, the string
 contains the hardware (MAC) address of the device.
 
-HIDIOCSFEATURE(len): Send a Feature Report
+HIDIOCSFEATURE(len):
+	Send a Feature Report
+
 This ioctl will send a feature report to the device.  Per the HID
 specification, feature reports are always sent using the control endpoint.
 Set the first byte of the supplied buffer to the report number.  For devices
@@ -101,7 +117,9 @@ which do not use numbered reports, set the first byte to 0. The report data
 begins in the second byte. Make sure to set len accordingly, to one more
 than the length of the report (to account for the report number).
 
-HIDIOCGFEATURE(len): Get a Feature Report
+HIDIOCGFEATURE(len):
+	Get a Feature Report
+
 This ioctl will request a feature report from the device using the control
 endpoint.  The first byte of the supplied buffer should be set to the report
 number of the requested report.  For devices which do not use numbered
@@ -109,11 +127,12 @@ reports, set the first byte to 0.  The report will be returned starting at
 the first byte of the buffer (ie: the report number is not returned).
 
 Example
----------
+-------
 In samples/, find hid-example.c, which shows examples of read(), write(),
 and all the ioctls for hidraw.  The code may be used by anyone for any
 purpose, and can serve as a starting point for developing applications using
 hidraw.
 
 Document by:
+
 	Alan Ott <alan@signal11.us>, Signal 11 Software
diff --git a/Documentation/hid/index.rst b/Documentation/hid/index.rst
new file mode 100644
index 000000000000..af4324902622
--- /dev/null
+++ b/Documentation/hid/index.rst
@@ -0,0 +1,18 @@
+:orphan:
+
+=============================
+Human Interface Devices (HID)
+=============================
+
+.. toctree::
+   :maxdepth: 1
+
+   hiddev
+   hidraw
+   hid-sensor
+   hid-transport
+
+   uhid
+
+   hid-alps
+   intel-ish-hid
diff --git a/Documentation/hid/intel-ish-hid.rst b/Documentation/hid/intel-ish-hid.rst
new file mode 100644
index 000000000000..cccbf4be17d7
--- /dev/null
+++ b/Documentation/hid/intel-ish-hid.rst
@@ -0,0 +1,485 @@
+=================================
+Intel Integrated Sensor Hub (ISH)
+=================================
+
+A sensor hub enables the ability to offload sensor polling and algorithm
+processing to a dedicated low power co-processor. This allows the core
+processor to go into low power modes more often, resulting in the increased
+battery life.
+
+There are many vendors providing external sensor hubs confirming to HID
+Sensor usage tables, and used in several tablets, 2 in 1 convertible laptops
+and embedded products. Linux had this support since Linux 3.9.
+
+Intel® introduced integrated sensor hubs as a part of the SoC starting from
+Cherry Trail and now supported on multiple generations of CPU packages. There
+are many commercial devices already shipped with Integrated Sensor Hubs (ISH).
+These ISH also comply to HID sensor specification, but the  difference is the
+transport protocol used for communication. The current external sensor hubs
+mainly use HID over i2C or USB. But ISH doesn't use either i2c or USB.
+
+1. Overview
+===========
+
+Using a analogy with a usbhid implementation, the ISH follows a similar model
+for a very high speed communication::
+
+	-----------------		----------------------
+	|    USB HID	|	-->	|    ISH HID	     |
+	-----------------		----------------------
+	-----------------		----------------------
+	|  USB protocol	|	-->	|    ISH Transport   |
+	-----------------		----------------------
+	-----------------		----------------------
+	|  EHCI/XHCI	|	-->	|    ISH IPC	     |
+	-----------------		----------------------
+	      PCI				 PCI
+	-----------------		----------------------
+        |Host controller|	-->	|    ISH processor   |
+	-----------------		----------------------
+	     USB Link
+	-----------------		----------------------
+	| USB End points|	-->	|    ISH Clients     |
+	-----------------		----------------------
+
+Like USB protocol provides a method for device enumeration, link management
+and user data encapsulation, the ISH also provides similar services. But it is
+very light weight tailored to manage and communicate with ISH client
+applications implemented in the firmware.
+
+The ISH allows multiple sensor management applications executing in the
+firmware. Like USB endpoints the messaging can be to/from a client. As part of
+enumeration process, these clients are identified. These clients can be simple
+HID sensor applications, sensor calibration application or senor firmware
+update application.
+
+The implementation model is similar, like USB bus, ISH transport is also
+implemented as a bus. Each client application executing in the ISH processor
+is registered as a device on this bus. The driver, which binds each device
+(ISH HID driver) identifies the device type and registers with the hid core.
+
+2. ISH Implementation: Block Diagram
+====================================
+
+::
+
+	 ---------------------------
+	|  User Space Applications  |
+	 ---------------------------
+
+  ----------------IIO ABI----------------
+	 --------------------------
+	|  IIO Sensor Drivers	  |
+	 --------------------------
+	 --------------------------
+	|	 IIO core	  |
+	 --------------------------
+	 --------------------------
+	|   HID Sensor Hub MFD	  |
+	 --------------------------
+	 --------------------------
+	|       HID Core	  |
+	 --------------------------
+	 --------------------------
+	|   HID over ISH Client   |
+	 --------------------------
+	 --------------------------
+	|   ISH Transport (ISHTP) |
+	 --------------------------
+	 --------------------------
+	|      IPC Drivers	  |
+	 --------------------------
+  OS
+  ---------------- PCI -----------------
+  Hardware + Firmware
+	 ----------------------------
+	| ISH Hardware/Firmware(FW) |
+	 ----------------------------
+
+3. High level processing in above blocks
+========================================
+
+3.1 Hardware Interface
+----------------------
+
+The ISH is exposed as "Non-VGA unclassified PCI device" to the host. The PCI
+product and vendor IDs are changed from different generations of processors. So
+the source code which enumerate drivers needs to update from generation to
+generation.
+
+3.2 Inter Processor Communication (IPC) driver
+----------------------------------------------
+
+Location: drivers/hid/intel-ish-hid/ipc
+
+The IPC message used memory mapped I/O. The registers are defined in
+hw-ish-regs.h.
+
+3.2.1 IPC/FW message types
+^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+There are two types of messages, one for management of link and other messages
+are to and from transport layers.
+
+TX and RX of Transport messages
+...............................
+
+A set of memory mapped register offers support of multi byte messages TX and
+RX (E.g.IPC_REG_ISH2HOST_MSG, IPC_REG_HOST2ISH_MSG). The IPC layer maintains
+internal queues to sequence messages and send them in order to the FW.
+Optionally the caller can register handler to get notification of completion.
+A door bell mechanism is used in messaging to trigger processing in host and
+client firmware side. When ISH interrupt handler is called, the ISH2HOST
+doorbell register is used by host drivers to determine that the interrupt
+is for ISH.
+
+Each side has 32 32-bit message registers and a 32-bit doorbell. Doorbell
+register has the following format:
+Bits 0..6: fragment length (7 bits are used)
+Bits 10..13: encapsulated protocol
+Bits 16..19: management command (for IPC management protocol)
+Bit 31: doorbell trigger (signal H/W interrupt to the other side)
+Other bits are reserved, should be 0.
+
+3.2.2 Transport layer interface
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+To abstract HW level IPC communication, a set of callbacks are registered.
+The transport layer uses them to send and receive messages.
+Refer to  struct ishtp_hw_ops for callbacks.
+
+3.3 ISH Transport layer
+-----------------------
+
+Location: drivers/hid/intel-ish-hid/ishtp/
+
+3.3.1 A Generic Transport Layer
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The transport layer is a bi-directional protocol, which defines:
+- Set of commands to start, stop, connect, disconnect and flow control
+(ishtp/hbm.h) for details
+- A flow control mechanism to avoid buffer overflows
+
+This protocol resembles bus messages described in the following document:
+http://www.intel.com/content/dam/www/public/us/en/documents/technical-\
+specifications/dcmi-hi-1-0-spec.pdf "Chapter 7: Bus Message Layer"
+
+3.3.2 Connection and Flow Control Mechanism
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Each FW client and a protocol is identified by an UUID. In order to communicate
+to a FW client, a connection must be established using connect request and
+response bus messages. If successful, a pair (host_client_id and fw_client_id)
+will identify the connection.
+
+Once connection is established, peers send each other flow control bus messages
+independently. Every peer may send a message only if it has received a
+flow-control credit before. Once it sent a message, it may not send another one
+before receiving the next flow control credit.
+Either side can send disconnect request bus message to end communication. Also
+the link will be dropped if major FW reset occurs.
+
+3.3.3 Peer to Peer data transfer
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Peer to Peer data transfer can happen with or without using DMA. Depending on
+the sensor bandwidth requirement DMA can be enabled by using module parameter
+ishtp_use_dma under intel_ishtp.
+
+Each side (host and FW) manages its DMA transfer memory independently. When an
+ISHTP client from either host or FW side wants to send something, it decides
+whether to send over IPC or over DMA; for each transfer the decision is
+independent. The sending side sends DMA_XFER message when the message is in
+the respective host buffer (TX when host client sends, RX when FW client
+sends). The recipient of DMA message responds with DMA_XFER_ACK, indicating
+the sender that the memory region for that message may be reused.
+
+DMA initialization is started with host sending DMA_ALLOC_NOTIFY bus message
+(that includes RX buffer) and FW responds with DMA_ALLOC_NOTIFY_ACK.
+Additionally to DMA address communication, this sequence checks capabilities:
+if thw host doesn't support DMA, then it won't send DMA allocation, so FW can't
+send DMA; if FW doesn't support DMA then it won't respond with
+DMA_ALLOC_NOTIFY_ACK, in which case host will not use DMA transfers.
+Here ISH acts as busmaster DMA controller. Hence when host sends DMA_XFER,
+it's request to do host->ISH DMA transfer; when FW sends DMA_XFER, it means
+that it already did DMA and the message resides at host. Thus, DMA_XFER
+and DMA_XFER_ACK act as ownership indicators.
+
+At initial state all outgoing memory belongs to the sender (TX to host, RX to
+FW), DMA_XFER transfers ownership on the region that contains ISHTP message to
+the receiving side, DMA_XFER_ACK returns ownership to the sender. A sender
+needs not wait for previous DMA_XFER to be ack'ed, and may send another message
+as long as remaining continuous memory in its ownership is enough.
+In principle, multiple DMA_XFER and DMA_XFER_ACK messages may be sent at once
+(up to IPC MTU), thus allowing for interrupt throttling.
+Currently, ISH FW decides to send over DMA if ISHTP message is more than 3 IPC
+fragments and via IPC otherwise.
+
+3.3.4 Ring Buffers
+^^^^^^^^^^^^^^^^^^
+
+When a client initiate a connection, a ring or RX and TX buffers are allocated.
+The size of ring can be specified by the client. HID client set 16 and 32 for
+TX and RX buffers respectively. On send request from client, the data to be
+sent is copied to one of the send ring buffer and scheduled to be sent using
+bus message protocol. These buffers are required because the FW may have not
+have processed the last message and may not have enough flow control credits
+to send. Same thing holds true on receive side and flow control is required.
+
+3.3.5 Host Enumeration
+^^^^^^^^^^^^^^^^^^^^^^
+
+The host enumeration bus command allow discovery of clients present in the FW.
+There can be multiple sensor clients and clients for calibration function.
+
+To ease in implantation and allow independent driver handle each client
+this transport layer takes advantage of Linux Bus driver model. Each
+client is registered as device on the the transport bus (ishtp bus).
+
+Enumeration sequence of messages:
+
+- Host sends HOST_START_REQ_CMD, indicating that host ISHTP layer is up.
+- FW responds with HOST_START_RES_CMD
+- Host sends HOST_ENUM_REQ_CMD (enumerate FW clients)
+- FW responds with HOST_ENUM_RES_CMD that includes bitmap of available FW
+  client IDs
+- For each FW ID found in that bitmap host sends
+  HOST_CLIENT_PROPERTIES_REQ_CMD
+- FW responds with HOST_CLIENT_PROPERTIES_RES_CMD. Properties include UUID,
+  max ISHTP message size, etc.
+- Once host received properties for that last discovered client, it considers
+  ISHTP device fully functional (and allocates DMA buffers)
+
+3.4 HID over ISH Client
+-----------------------
+
+Location: drivers/hid/intel-ish-hid
+
+The ISHTP client driver is responsible for:
+
+- enumerate HID devices under FW ISH client
+- Get Report descriptor
+- Register with HID core as a LL driver
+- Process Get/Set feature request
+- Get input reports
+
+3.5 HID Sensor Hub MFD and IIO sensor drivers
+---------------------------------------------
+
+The functionality in these drivers is the same as an external sensor hub.
+Refer to
+Documentation/hid/hid-sensor.rst for HID sensor
+Documentation/ABI/testing/sysfs-bus-iio for IIO ABIs to user space
+
+3.6 End to End HID transport Sequence Diagram
+---------------------------------------------
+
+::
+
+  HID-ISH-CLN                    ISHTP                    IPC                             HW
+          |                        |                       |                               |
+          |                        |                       |-----WAKE UP------------------>|
+          |                        |                       |                               |
+          |                        |                       |-----HOST READY--------------->|
+          |                        |                       |                               |
+          |                        |                       |<----MNG_RESET_NOTIFY_ACK----- |
+          |                        |                       |                               |
+          |                        |<----ISHTP_START------ |                               |
+          |                        |                       |                               |
+          |                        |<-----------------HOST_START_RES_CMD-------------------|
+          |                        |                       |                               |
+          |                        |------------------QUERY_SUBSCRIBER-------------------->|
+          |                        |                       |                               |
+          |                        |------------------HOST_ENUM_REQ_CMD------------------->|
+          |                        |                       |                               |
+          |                        |<-----------------HOST_ENUM_RES_CMD--------------------|
+          |                        |                       |                               |
+          |                        |------------------HOST_CLIENT_PROPERTIES_REQ_CMD------>|
+          |                        |                       |                               |
+          |                        |<-----------------HOST_CLIENT_PROPERTIES_RES_CMD-------|
+          |       Create new device on in ishtp bus        |                               |
+          |                        |                       |                               |
+          |                        |------------------HOST_CLIENT_PROPERTIES_REQ_CMD------>|
+          |                        |                       |                               |
+          |                        |<-----------------HOST_CLIENT_PROPERTIES_RES_CMD-------|
+          |       Create new device on in ishtp bus        |                               |
+          |                        |                       |                               |
+          |                        |--Repeat HOST_CLIENT_PROPERTIES_REQ_CMD-till last one--|
+          |                        |                       |                               |
+       probed()
+          |----ishtp_cl_connect--->|----------------- CLIENT_CONNECT_REQ_CMD-------------->|
+          |                        |                       |                               |
+          |                        |<----------------CLIENT_CONNECT_RES_CMD----------------|
+          |                        |                       |                               |
+          |register event callback |                       |                               |
+          |                        |                       |                               |
+          |ishtp_cl_send(
+          HOSTIF_DM_ENUM_DEVICES)  |----------fill ishtp_msg_hdr struct write to HW-----  >|
+          |                        |                       |                               |
+          |                        |                       |<-----IRQ(IPC_PROTOCOL_ISHTP---|
+          |                        |                       |                               |
+          |<--ENUM_DEVICE RSP------|                       |                               |
+          |                        |                       |                               |
+  for each enumerated device
+          |ishtp_cl_send(
+          HOSTIF_GET_HID_DESCRIPTOR|----------fill ishtp_msg_hdr struct write to HW-----  >|
+          |                        |                       |                               |
+          ...Response
+          |                        |                       |                               |
+  for each enumerated device
+          |ishtp_cl_send(
+       HOSTIF_GET_REPORT_DESCRIPTOR|--------------fill ishtp_msg_hdr struct write to HW-- >|
+          |                        |                       |                               |
+          |                        |                       |                               |
+   hid_allocate_device
+          |                        |                       |                               |
+   hid_add_device                  |                       |                               |
+          |                        |                       |                               |
+
+
+3.7 ISH Debugging
+-----------------
+
+To debug ISH, event tracing mechanism is used. To enable debug logs
+echo 1 > /sys/kernel/debug/tracing/events/intel_ish/enable
+cat sys/kernel/debug/tracing/trace
+
+3.8 ISH IIO sysfs Example on Lenovo thinkpad Yoga 260
+-----------------------------------------------------
+
+::
+
+  root@otcpl-ThinkPad-Yoga-260:~# tree -l /sys/bus/iio/devices/
+  /sys/bus/iio/devices/
+  ├── iio:device0 -> ../../../devices/0044:8086:22D8.0001/HID-SENSOR-200073.9.auto/iio:device0
+  │   ├── buffer
+  │   │   ├── enable
+  │   │   ├── length
+  │   │   └── watermark
+  ...
+  │   ├── in_accel_hysteresis
+  │   ├── in_accel_offset
+  │   ├── in_accel_sampling_frequency
+  │   ├── in_accel_scale
+  │   ├── in_accel_x_raw
+  │   ├── in_accel_y_raw
+  │   ├── in_accel_z_raw
+  │   ├── name
+  │   ├── scan_elements
+  │   │   ├── in_accel_x_en
+  │   │   ├── in_accel_x_index
+  │   │   ├── in_accel_x_type
+  │   │   ├── in_accel_y_en
+  │   │   ├── in_accel_y_index
+  │   │   ├── in_accel_y_type
+  │   │   ├── in_accel_z_en
+  │   │   ├── in_accel_z_index
+  │   │   └── in_accel_z_type
+  ...
+  │   │   ├── devices
+  │   │   │   │   ├── buffer
+  │   │   │   │   │   ├── enable
+  │   │   │   │   │   ├── length
+  │   │   │   │   │   └── watermark
+  │   │   │   │   ├── dev
+  │   │   │   │   ├── in_intensity_both_raw
+  │   │   │   │   ├── in_intensity_hysteresis
+  │   │   │   │   ├── in_intensity_offset
+  │   │   │   │   ├── in_intensity_sampling_frequency
+  │   │   │   │   ├── in_intensity_scale
+  │   │   │   │   ├── name
+  │   │   │   │   ├── scan_elements
+  │   │   │   │   │   ├── in_intensity_both_en
+  │   │   │   │   │   ├── in_intensity_both_index
+  │   │   │   │   │   └── in_intensity_both_type
+  │   │   │   │   ├── trigger
+  │   │   │   │   │   └── current_trigger
+  ...
+  │   │   │   │   ├── buffer
+  │   │   │   │   │   ├── enable
+  │   │   │   │   │   ├── length
+  │   │   │   │   │   └── watermark
+  │   │   │   │   ├── dev
+  │   │   │   │   ├── in_magn_hysteresis
+  │   │   │   │   ├── in_magn_offset
+  │   │   │   │   ├── in_magn_sampling_frequency
+  │   │   │   │   ├── in_magn_scale
+  │   │   │   │   ├── in_magn_x_raw
+  │   │   │   │   ├── in_magn_y_raw
+  │   │   │   │   ├── in_magn_z_raw
+  │   │   │   │   ├── in_rot_from_north_magnetic_tilt_comp_raw
+  │   │   │   │   ├── in_rot_hysteresis
+  │   │   │   │   ├── in_rot_offset
+  │   │   │   │   ├── in_rot_sampling_frequency
+  │   │   │   │   ├── in_rot_scale
+  │   │   │   │   ├── name
+  ...
+  │   │   │   │   ├── scan_elements
+  │   │   │   │   │   ├── in_magn_x_en
+  │   │   │   │   │   ├── in_magn_x_index
+  │   │   │   │   │   ├── in_magn_x_type
+  │   │   │   │   │   ├── in_magn_y_en
+  │   │   │   │   │   ├── in_magn_y_index
+  │   │   │   │   │   ├── in_magn_y_type
+  │   │   │   │   │   ├── in_magn_z_en
+  │   │   │   │   │   ├── in_magn_z_index
+  │   │   │   │   │   ├── in_magn_z_type
+  │   │   │   │   │   ├── in_rot_from_north_magnetic_tilt_comp_en
+  │   │   │   │   │   ├── in_rot_from_north_magnetic_tilt_comp_index
+  │   │   │   │   │   └── in_rot_from_north_magnetic_tilt_comp_type
+  │   │   │   │   ├── trigger
+  │   │   │   │   │   └── current_trigger
+  ...
+  │   │   │   │   ├── buffer
+  │   │   │   │   │   ├── enable
+  │   │   │   │   │   ├── length
+  │   │   │   │   │   └── watermark
+  │   │   │   │   ├── dev
+  │   │   │   │   ├── in_anglvel_hysteresis
+  │   │   │   │   ├── in_anglvel_offset
+  │   │   │   │   ├── in_anglvel_sampling_frequency
+  │   │   │   │   ├── in_anglvel_scale
+  │   │   │   │   ├── in_anglvel_x_raw
+  │   │   │   │   ├── in_anglvel_y_raw
+  │   │   │   │   ├── in_anglvel_z_raw
+  │   │   │   │   ├── name
+  │   │   │   │   ├── scan_elements
+  │   │   │   │   │   ├── in_anglvel_x_en
+  │   │   │   │   │   ├── in_anglvel_x_index
+  │   │   │   │   │   ├── in_anglvel_x_type
+  │   │   │   │   │   ├── in_anglvel_y_en
+  │   │   │   │   │   ├── in_anglvel_y_index
+  │   │   │   │   │   ├── in_anglvel_y_type
+  │   │   │   │   │   ├── in_anglvel_z_en
+  │   │   │   │   │   ├── in_anglvel_z_index
+  │   │   │   │   │   └── in_anglvel_z_type
+  │   │   │   │   ├── trigger
+  │   │   │   │   │   └── current_trigger
+  ...
+  │   │   │   │   ├── buffer
+  │   │   │   │   │   ├── enable
+  │   │   │   │   │   ├── length
+  │   │   │   │   │   └── watermark
+  │   │   │   │   ├── dev
+  │   │   │   │   ├── in_anglvel_hysteresis
+  │   │   │   │   ├── in_anglvel_offset
+  │   │   │   │   ├── in_anglvel_sampling_frequency
+  │   │   │   │   ├── in_anglvel_scale
+  │   │   │   │   ├── in_anglvel_x_raw
+  │   │   │   │   ├── in_anglvel_y_raw
+  │   │   │   │   ├── in_anglvel_z_raw
+  │   │   │   │   ├── name
+  │   │   │   │   ├── scan_elements
+  │   │   │   │   │   ├── in_anglvel_x_en
+  │   │   │   │   │   ├── in_anglvel_x_index
+  │   │   │   │   │   ├── in_anglvel_x_type
+  │   │   │   │   │   ├── in_anglvel_y_en
+  │   │   │   │   │   ├── in_anglvel_y_index
+  │   │   │   │   │   ├── in_anglvel_y_type
+  │   │   │   │   │   ├── in_anglvel_z_en
+  │   │   │   │   │   ├── in_anglvel_z_index
+  │   │   │   │   │   └── in_anglvel_z_type
+  │   │   │   │   ├── trigger
+  │   │   │   │   │   └── current_trigger
+  ...
diff --git a/Documentation/hid/intel-ish-hid.txt b/Documentation/hid/intel-ish-hid.txt
deleted file mode 100644
index d48b21c71ddd..000000000000
--- a/Documentation/hid/intel-ish-hid.txt
+++ /dev/null
@@ -1,454 +0,0 @@
-Intel Integrated Sensor Hub (ISH)
-===============================
-
-A sensor hub enables the ability to offload sensor polling and algorithm
-processing to a dedicated low power co-processor. This allows the core
-processor to go into low power modes more often, resulting in the increased
-battery life.
-
-There are many vendors providing external sensor hubs confirming to HID
-Sensor usage tables, and used in several tablets, 2 in 1 convertible laptops
-and embedded products. Linux had this support since Linux 3.9.
-
-Intel® introduced integrated sensor hubs as a part of the SoC starting from
-Cherry Trail and now supported on multiple generations of CPU packages. There
-are many commercial devices already shipped with Integrated Sensor Hubs (ISH).
-These ISH also comply to HID sensor specification, but the  difference is the
-transport protocol used for communication. The current external sensor hubs
-mainly use HID over i2C or USB. But ISH doesn't use either i2c or USB.
-
-1. Overview
-
-Using a analogy with a usbhid implementation, the ISH follows a similar model
-for a very high speed communication:
-
-	-----------------		----------------------
-	|    USB HID	|	-->	|    ISH HID	     |
-	-----------------		----------------------
-	-----------------		----------------------
-	|  USB protocol	|	-->	|    ISH Transport   |
-	-----------------		----------------------
-	-----------------		----------------------
-	|  EHCI/XHCI	|	-->	|    ISH IPC	     |
-	-----------------		----------------------
-	      PCI				 PCI
-	-----------------		----------------------
-        |Host controller|	-->	|    ISH processor   |
-	-----------------		----------------------
-	     USB Link
-	-----------------		----------------------
-	| USB End points|	-->	|    ISH Clients     |
-	-----------------		----------------------
-
-Like USB protocol provides a method for device enumeration, link management
-and user data encapsulation, the ISH also provides similar services. But it is
-very light weight tailored to manage and communicate with ISH client
-applications implemented in the firmware.
-
-The ISH allows multiple sensor management applications executing in the
-firmware. Like USB endpoints the messaging can be to/from a client. As part of
-enumeration process, these clients are identified. These clients can be simple
-HID sensor applications, sensor calibration application or senor firmware
-update application.
-
-The implementation model is similar, like USB bus, ISH transport is also
-implemented as a bus. Each client application executing in the ISH processor
-is registered as a device on this bus. The driver, which binds each device
-(ISH HID driver) identifies the device type and registers with the hid core.
-
-2. ISH Implementation: Block Diagram
-
-	 ---------------------------
-	|  User Space Applications  |
-	 ---------------------------
-
-----------------IIO ABI----------------
-	 --------------------------
-	|  IIO Sensor Drivers	  |
-	 --------------------------
-	 --------------------------
-	|	 IIO core	  |
-	 --------------------------
-	 --------------------------
-	|   HID Sensor Hub MFD	  |
-	 --------------------------
-	 --------------------------
-	|       HID Core	  |
-	 --------------------------
-	 --------------------------
-	|   HID over ISH Client   |
-	 --------------------------
-	 --------------------------
-	|   ISH Transport (ISHTP) |
-	 --------------------------
-	 --------------------------
-	|      IPC Drivers	  |
-	 --------------------------
-OS
-----------------   PCI -----------------
-Hardware + Firmware
-	 ----------------------------
-	| ISH Hardware/Firmware(FW) |
-	 ----------------------------
-
-3. High level processing in above blocks
-
-3.1 Hardware Interface
-
-The ISH is exposed as "Non-VGA unclassified PCI device" to the host. The PCI
-product and vendor IDs are changed from different generations of processors. So
-the source code which enumerate drivers needs to update from generation to
-generation.
-
-3.2 Inter Processor Communication (IPC) driver
-Location: drivers/hid/intel-ish-hid/ipc
-
-The IPC message used memory mapped I/O. The registers are defined in
-hw-ish-regs.h.
-
-3.2.1 IPC/FW message types
-
-There are two types of messages, one for management of link and other messages
-are to and from transport layers.
-
-TX and RX of Transport messages
-
-A set of memory mapped register offers support of multi byte messages TX and
-RX (E.g.IPC_REG_ISH2HOST_MSG, IPC_REG_HOST2ISH_MSG). The IPC layer maintains
-internal queues to sequence messages and send them in order to the FW.
-Optionally the caller can register handler to get notification of completion.
-A door bell mechanism is used in messaging to trigger processing in host and
-client firmware side. When ISH interrupt handler is called, the ISH2HOST
-doorbell register is used by host drivers to determine that the interrupt
-is for ISH.
-
-Each side has 32 32-bit message registers and a 32-bit doorbell. Doorbell
-register has the following format:
-Bits 0..6: fragment length (7 bits are used)
-Bits 10..13: encapsulated protocol
-Bits 16..19: management command (for IPC management protocol)
-Bit 31: doorbell trigger (signal H/W interrupt to the other side)
-Other bits are reserved, should be 0.
-
-3.2.2 Transport layer interface
-
-To abstract HW level IPC communication, a set of callbacks are registered.
-The transport layer uses them to send and receive messages.
-Refer to  struct ishtp_hw_ops for callbacks.
-
-3.3 ISH Transport layer
-Location: drivers/hid/intel-ish-hid/ishtp/
-
-3.3.1 A Generic Transport Layer
-
-The transport layer is a bi-directional protocol, which defines:
-- Set of commands to start, stop, connect, disconnect and flow control
-(ishtp/hbm.h) for details
-- A flow control mechanism to avoid buffer overflows
-
-This protocol resembles bus messages described in the following document:
-http://www.intel.com/content/dam/www/public/us/en/documents/technical-\
-specifications/dcmi-hi-1-0-spec.pdf "Chapter 7: Bus Message Layer"
-
-3.3.2 Connection and Flow Control Mechanism
-
-Each FW client and a protocol is identified by an UUID. In order to communicate
-to a FW client, a connection must be established using connect request and
-response bus messages. If successful, a pair (host_client_id and fw_client_id)
-will identify the connection.
-
-Once connection is established, peers send each other flow control bus messages
-independently. Every peer may send a message only if it has received a
-flow-control credit before. Once it sent a message, it may not send another one
-before receiving the next flow control credit.
-Either side can send disconnect request bus message to end communication. Also
-the link will be dropped if major FW reset occurs.
-
-3.3.3 Peer to Peer data transfer
-
-Peer to Peer data transfer can happen with or without using DMA. Depending on
-the sensor bandwidth requirement DMA can be enabled by using module parameter
-ishtp_use_dma under intel_ishtp.
-
-Each side (host and FW) manages its DMA transfer memory independently. When an
-ISHTP client from either host or FW side wants to send something, it decides
-whether to send over IPC or over DMA; for each transfer the decision is
-independent. The sending side sends DMA_XFER message when the message is in
-the respective host buffer (TX when host client sends, RX when FW client
-sends). The recipient of DMA message responds with DMA_XFER_ACK, indicating
-the sender that the memory region for that message may be reused.
-
-DMA initialization is started with host sending DMA_ALLOC_NOTIFY bus message
-(that includes RX buffer) and FW responds with DMA_ALLOC_NOTIFY_ACK.
-Additionally to DMA address communication, this sequence checks capabilities:
-if thw host doesn't support DMA, then it won't send DMA allocation, so FW can't
-send DMA; if FW doesn't support DMA then it won't respond with
-DMA_ALLOC_NOTIFY_ACK, in which case host will not use DMA transfers.
-Here ISH acts as busmaster DMA controller. Hence when host sends DMA_XFER,
-it's request to do host->ISH DMA transfer; when FW sends DMA_XFER, it means
-that it already did DMA and the message resides at host. Thus, DMA_XFER
-and DMA_XFER_ACK act as ownership indicators.
-
-At initial state all outgoing memory belongs to the sender (TX to host, RX to
-FW), DMA_XFER transfers ownership on the region that contains ISHTP message to
-the receiving side, DMA_XFER_ACK returns ownership to the sender. A sender
-needs not wait for previous DMA_XFER to be ack'ed, and may send another message
-as long as remaining continuous memory in its ownership is enough.
-In principle, multiple DMA_XFER and DMA_XFER_ACK messages may be sent at once
-(up to IPC MTU), thus allowing for interrupt throttling.
-Currently, ISH FW decides to send over DMA if ISHTP message is more than 3 IPC
-fragments and via IPC otherwise.
-
-3.3.4 Ring Buffers
-
-When a client initiate a connection, a ring or RX and TX buffers are allocated.
-The size of ring can be specified by the client. HID client set 16 and 32 for
-TX and RX buffers respectively. On send request from client, the data to be
-sent is copied to one of the send ring buffer and scheduled to be sent using
-bus message protocol. These buffers are required because the FW may have not
-have processed the last message and may not have enough flow control credits
-to send. Same thing holds true on receive side and flow control is required.
-
-3.3.5 Host Enumeration
-
-The host enumeration bus command allow discovery of clients present in the FW.
-There can be multiple sensor clients and clients for calibration function.
-
-To ease in implantation and allow independent driver handle each client
-this transport layer takes advantage of Linux Bus driver model. Each
-client is registered as device on the the transport bus (ishtp bus).
-
-Enumeration sequence of messages:
-- Host sends HOST_START_REQ_CMD, indicating that host ISHTP layer is up.
-- FW responds with HOST_START_RES_CMD
-- Host sends HOST_ENUM_REQ_CMD (enumerate FW clients)
-- FW responds with HOST_ENUM_RES_CMD that includes bitmap of available FW
-client IDs
-- For each FW ID found in that bitmap host sends
-HOST_CLIENT_PROPERTIES_REQ_CMD
-- FW responds with HOST_CLIENT_PROPERTIES_RES_CMD. Properties include UUID,
-max ISHTP message size, etc.
-- Once host received properties for that last discovered client, it considers
-ISHTP device fully functional (and allocates DMA buffers)
-
-3.4 HID over ISH Client
-Location: drivers/hid/intel-ish-hid
-
-The ISHTP client driver is responsible for:
-- enumerate HID devices under FW ISH client
-- Get Report descriptor
-- Register with HID core as a LL driver
-- Process Get/Set feature request
-- Get input reports
-
-3.5 HID Sensor Hub MFD and IIO sensor drivers
-
-The functionality in these drivers is the same as an external sensor hub.
-Refer to
-Documentation/hid/hid-sensor.txt for HID sensor
-Documentation/ABI/testing/sysfs-bus-iio for IIO ABIs to user space
-
-3.6 End to End HID transport Sequence Diagram
-
-HID-ISH-CLN			ISHTP			IPC				HW
-	|			|			|				|
-	|			|			|-----WAKE UP------------------>|
-	|			|			|				|
-	|			|			|-----HOST READY--------------->|
-	|			|			|				|
-	|			|			|<----MNG_RESET_NOTIFY_ACK----- |
-	|			|			|				|
-	|			|<----ISHTP_START------ |				|
-	|			|			|				|
-	|			|<-----------------HOST_START_RES_CMD-------------------|
-	|			|			|				|
-	|			|------------------QUERY_SUBSCRIBER-------------------->|
-	|			|			|				|
-	|			|------------------HOST_ENUM_REQ_CMD------------------->|
-	|			|			|				|
-	|			|<-----------------HOST_ENUM_RES_CMD--------------------|
-	|			|			|				|
-	|			|------------------HOST_CLIENT_PROPERTIES_REQ_CMD------>|
-	|			|			|				|
-	|			|<-----------------HOST_CLIENT_PROPERTIES_RES_CMD-------|
-	|	Create new device on in ishtp bus	|				|
-	|			|			|				|
-	|			|------------------HOST_CLIENT_PROPERTIES_REQ_CMD------>|
-	|			|			|				|
-	|			|<-----------------HOST_CLIENT_PROPERTIES_RES_CMD-------|
-	|	Create new device on in ishtp bus	|				|
-	|			|			|				|
-	|			|--Repeat HOST_CLIENT_PROPERTIES_REQ_CMD-till last one--|
-	|			|			|				|
-     probed()
-	|----ishtp_cl_connect-->|----------------- CLIENT_CONNECT_REQ_CMD-------------->|
-	|			|			|				|
-	|			|<----------------CLIENT_CONNECT_RES_CMD----------------|
-	|			|			|				|
-	|register event callback|			|				|
-	|			|			|				|
-	|ishtp_cl_send(
-	HOSTIF_DM_ENUM_DEVICES) |----------fill ishtp_msg_hdr struct write to HW-----  >|
-	|			|			|				|
-	|			|			|<-----IRQ(IPC_PROTOCOL_ISHTP---|
-	|			|			|				|
-	|<--ENUM_DEVICE RSP-----|			|				|
-	|			|			|				|
-for each enumerated device
-	|ishtp_cl_send(
-	HOSTIF_GET_HID_DESCRIPTOR |----------fill ishtp_msg_hdr struct write to HW---  >|
-	|			|			|				|
-	...Response
-	|			|			|				|
-for each enumerated device
-	|ishtp_cl_send(
-	HOSTIF_GET_REPORT_DESCRIPTOR |----------fill ishtp_msg_hdr struct write to HW- >|
-	|			|			|				|
-	|			|			|				|
- hid_allocate_device
-	|			|			|				|
- hid_add_device			|			|				|
-	|			|			|				|
-
-
-3.7 ISH Debugging
-
-To debug ISH, event tracing mechanism is used. To enable debug logs
-echo 1 > /sys/kernel/debug/tracing/events/intel_ish/enable
-cat sys/kernel/debug/tracing/trace
-
-3.8 ISH IIO sysfs Example on Lenovo thinkpad Yoga 260
-
-root@otcpl-ThinkPad-Yoga-260:~# tree -l /sys/bus/iio/devices/
-/sys/bus/iio/devices/
-├── iio:device0 -> ../../../devices/0044:8086:22D8.0001/HID-SENSOR-200073.9.auto/iio:device0
-│   ├── buffer
-│   │   ├── enable
-│   │   ├── length
-│   │   └── watermark
-...
-│   ├── in_accel_hysteresis
-│   ├── in_accel_offset
-│   ├── in_accel_sampling_frequency
-│   ├── in_accel_scale
-│   ├── in_accel_x_raw
-│   ├── in_accel_y_raw
-│   ├── in_accel_z_raw
-│   ├── name
-│   ├── scan_elements
-│   │   ├── in_accel_x_en
-│   │   ├── in_accel_x_index
-│   │   ├── in_accel_x_type
-│   │   ├── in_accel_y_en
-│   │   ├── in_accel_y_index
-│   │   ├── in_accel_y_type
-│   │   ├── in_accel_z_en
-│   │   ├── in_accel_z_index
-│   │   └── in_accel_z_type
-...
-│   │   ├── devices
-│   │   │   │   ├── buffer
-│   │   │   │   │   ├── enable
-│   │   │   │   │   ├── length
-│   │   │   │   │   └── watermark
-│   │   │   │   ├── dev
-│   │   │   │   ├── in_intensity_both_raw
-│   │   │   │   ├── in_intensity_hysteresis
-│   │   │   │   ├── in_intensity_offset
-│   │   │   │   ├── in_intensity_sampling_frequency
-│   │   │   │   ├── in_intensity_scale
-│   │   │   │   ├── name
-│   │   │   │   ├── scan_elements
-│   │   │   │   │   ├── in_intensity_both_en
-│   │   │   │   │   ├── in_intensity_both_index
-│   │   │   │   │   └── in_intensity_both_type
-│   │   │   │   ├── trigger
-│   │   │   │   │   └── current_trigger
-...
-│   │   │   │   ├── buffer
-│   │   │   │   │   ├── enable
-│   │   │   │   │   ├── length
-│   │   │   │   │   └── watermark
-│   │   │   │   ├── dev
-│   │   │   │   ├── in_magn_hysteresis
-│   │   │   │   ├── in_magn_offset
-│   │   │   │   ├── in_magn_sampling_frequency
-│   │   │   │   ├── in_magn_scale
-│   │   │   │   ├── in_magn_x_raw
-│   │   │   │   ├── in_magn_y_raw
-│   │   │   │   ├── in_magn_z_raw
-│   │   │   │   ├── in_rot_from_north_magnetic_tilt_comp_raw
-│   │   │   │   ├── in_rot_hysteresis
-│   │   │   │   ├── in_rot_offset
-│   │   │   │   ├── in_rot_sampling_frequency
-│   │   │   │   ├── in_rot_scale
-│   │   │   │   ├── name
-...
-│   │   │   │   ├── scan_elements
-│   │   │   │   │   ├── in_magn_x_en
-│   │   │   │   │   ├── in_magn_x_index
-│   │   │   │   │   ├── in_magn_x_type
-│   │   │   │   │   ├── in_magn_y_en
-│   │   │   │   │   ├── in_magn_y_index
-│   │   │   │   │   ├── in_magn_y_type
-│   │   │   │   │   ├── in_magn_z_en
-│   │   │   │   │   ├── in_magn_z_index
-│   │   │   │   │   ├── in_magn_z_type
-│   │   │   │   │   ├── in_rot_from_north_magnetic_tilt_comp_en
-│   │   │   │   │   ├── in_rot_from_north_magnetic_tilt_comp_index
-│   │   │   │   │   └── in_rot_from_north_magnetic_tilt_comp_type
-│   │   │   │   ├── trigger
-│   │   │   │   │   └── current_trigger
-...
-│   │   │   │   ├── buffer
-│   │   │   │   │   ├── enable
-│   │   │   │   │   ├── length
-│   │   │   │   │   └── watermark
-│   │   │   │   ├── dev
-│   │   │   │   ├── in_anglvel_hysteresis
-│   │   │   │   ├── in_anglvel_offset
-│   │   │   │   ├── in_anglvel_sampling_frequency
-│   │   │   │   ├── in_anglvel_scale
-│   │   │   │   ├── in_anglvel_x_raw
-│   │   │   │   ├── in_anglvel_y_raw
-│   │   │   │   ├── in_anglvel_z_raw
-│   │   │   │   ├── name
-│   │   │   │   ├── scan_elements
-│   │   │   │   │   ├── in_anglvel_x_en
-│   │   │   │   │   ├── in_anglvel_x_index
-│   │   │   │   │   ├── in_anglvel_x_type
-│   │   │   │   │   ├── in_anglvel_y_en
-│   │   │   │   │   ├── in_anglvel_y_index
-│   │   │   │   │   ├── in_anglvel_y_type
-│   │   │   │   │   ├── in_anglvel_z_en
-│   │   │   │   │   ├── in_anglvel_z_index
-│   │   │   │   │   └── in_anglvel_z_type
-│   │   │   │   ├── trigger
-│   │   │   │   │   └── current_trigger
-...
-│   │   │   │   ├── buffer
-│   │   │   │   │   ├── enable
-│   │   │   │   │   ├── length
-│   │   │   │   │   └── watermark
-│   │   │   │   ├── dev
-│   │   │   │   ├── in_anglvel_hysteresis
-│   │   │   │   ├── in_anglvel_offset
-│   │   │   │   ├── in_anglvel_sampling_frequency
-│   │   │   │   ├── in_anglvel_scale
-│   │   │   │   ├── in_anglvel_x_raw
-│   │   │   │   ├── in_anglvel_y_raw
-│   │   │   │   ├── in_anglvel_z_raw
-│   │   │   │   ├── name
-│   │   │   │   ├── scan_elements
-│   │   │   │   │   ├── in_anglvel_x_en
-│   │   │   │   │   ├── in_anglvel_x_index
-│   │   │   │   │   ├── in_anglvel_x_type
-│   │   │   │   │   ├── in_anglvel_y_en
-│   │   │   │   │   ├── in_anglvel_y_index
-│   │   │   │   │   ├── in_anglvel_y_type
-│   │   │   │   │   ├── in_anglvel_z_en
-│   │   │   │   │   ├── in_anglvel_z_index
-│   │   │   │   │   └── in_anglvel_z_type
-│   │   │   │   ├── trigger
-│   │   │   │   │   └── current_trigger
-...
diff --git a/Documentation/hid/uhid.txt b/Documentation/hid/uhid.rst
similarity index 94%
rename from Documentation/hid/uhid.txt
rename to Documentation/hid/uhid.rst
index 958fff945304..b18cb96c885f 100644
--- a/Documentation/hid/uhid.txt
+++ b/Documentation/hid/uhid.rst
@@ -1,5 +1,6 @@
-      UHID - User-space I/O driver support for HID subsystem
-     ========================================================
+======================================================
+UHID - User-space I/O driver support for HID subsystem
+======================================================
 
 UHID allows user-space to implement HID transport drivers. Please see
 hid-transport.txt for an introduction into HID transport drivers. This document
@@ -22,9 +23,9 @@ If a new device is detected by your HID I/O Driver and you want to register this
 device with the HID subsystem, then you need to open /dev/uhid once for each
 device you want to register. All further communication is done by read()'ing or
 write()'ing "struct uhid_event" objects. Non-blocking operations are supported
-by setting O_NONBLOCK.
+by setting O_NONBLOCK::
 
-struct uhid_event {
+  struct uhid_event {
         __u32 type;
         union {
                 struct uhid_create2_req create2;
@@ -32,7 +33,7 @@ struct uhid_event {
                 struct uhid_input2_req input2;
                 ...
         } u;
-};
+  };
 
 The "type" field contains the ID of the event. Depending on the ID different
 payloads are sent. You must not split a single event across multiple read()'s or
@@ -86,31 +87,31 @@ the request was handled successfully. O_NONBLOCK does not affect write() as
 writes are always handled immediately in a non-blocking fashion. Future requests
 might make use of O_NONBLOCK, though.
 
-  UHID_CREATE2:
+UHID_CREATE2:
   This creates the internal HID device. No I/O is possible until you send this
   event to the kernel. The payload is of type struct uhid_create2_req and
   contains information about your device. You can start I/O now.
 
-  UHID_DESTROY:
+UHID_DESTROY:
   This destroys the internal HID device. No further I/O will be accepted. There
   may still be pending messages that you can receive with read() but no further
   UHID_INPUT events can be sent to the kernel.
   You can create a new device by sending UHID_CREATE2 again. There is no need to
   reopen the character device.
 
-  UHID_INPUT2:
+UHID_INPUT2:
   You must send UHID_CREATE2 before sending input to the kernel! This event
   contains a data-payload. This is the raw data that you read from your device
   on the interrupt channel. The kernel will parse the HID reports.
 
-  UHID_GET_REPORT_REPLY:
+UHID_GET_REPORT_REPLY:
   If you receive a UHID_GET_REPORT request you must answer with this request.
   You  must copy the "id" field from the request into the answer. Set the "err"
   field to 0 if no error occurred or to EIO if an I/O error occurred.
   If "err" is 0 then you should fill the buffer of the answer with the results
   of the GET_REPORT request and set "size" correspondingly.
 
-  UHID_SET_REPORT_REPLY:
+UHID_SET_REPORT_REPLY:
   This is the SET_REPORT equivalent of UHID_GET_REPORT_REPLY. Unlike GET_REPORT,
   SET_REPORT never returns a data buffer, therefore, it's sufficient to set the
   "id" and "err" fields correctly.
@@ -120,16 +121,18 @@ read()
 read() will return a queued output report. No reaction is required to any of
 them but you should handle them according to your needs.
 
-  UHID_START:
+UHID_START:
   This is sent when the HID device is started. Consider this as an answer to
   UHID_CREATE2. This is always the first event that is sent. Note that this
   event might not be available immediately after write(UHID_CREATE2) returns.
   Device drivers might required delayed setups.
   This event contains a payload of type uhid_start_req. The "dev_flags" field
   describes special behaviors of a device. The following flags are defined:
-      UHID_DEV_NUMBERED_FEATURE_REPORTS:
-      UHID_DEV_NUMBERED_OUTPUT_REPORTS:
-      UHID_DEV_NUMBERED_INPUT_REPORTS:
+
+      - UHID_DEV_NUMBERED_FEATURE_REPORTS
+      - UHID_DEV_NUMBERED_OUTPUT_REPORTS
+      - UHID_DEV_NUMBERED_INPUT_REPORTS
+
           Each of these flags defines whether a given report-type uses numbered
           reports. If numbered reports are used for a type, all messages from
           the kernel already have the report-number as prefix. Otherwise, no
@@ -137,33 +140,35 @@ them but you should handle them according to your needs.
           For messages sent by user-space to the kernel, you must adjust the
           prefixes according to these flags.
 
-  UHID_STOP:
+UHID_STOP:
   This is sent when the HID device is stopped. Consider this as an answer to
   UHID_DESTROY.
+
   If you didn't destroy your device via UHID_DESTROY, but the kernel sends an
   UHID_STOP event, this should usually be ignored. It means that the kernel
   reloaded/changed the device driver loaded on your HID device (or some other
   maintenance actions happened).
+
   You can usually ignored any UHID_STOP events safely.
 
-  UHID_OPEN:
+UHID_OPEN:
   This is sent when the HID device is opened. That is, the data that the HID
   device provides is read by some other process. You may ignore this event but
   it is useful for power-management. As long as you haven't received this event
   there is actually no other process that reads your data so there is no need to
   send UHID_INPUT2 events to the kernel.
 
-  UHID_CLOSE:
+UHID_CLOSE:
   This is sent when there are no more processes which read the HID data. It is
   the counterpart of UHID_OPEN and you may as well ignore this event.
 
-  UHID_OUTPUT:
+UHID_OUTPUT:
   This is sent if the HID device driver wants to send raw data to the I/O
   device on the interrupt channel. You should read the payload and forward it to
   the device. The payload is of type "struct uhid_output_req".
   This may be received even though you haven't received UHID_OPEN, yet.
 
-  UHID_GET_REPORT:
+UHID_GET_REPORT:
   This event is sent if the kernel driver wants to perform a GET_REPORT request
   on the control channeld as described in the HID specs. The report-type and
   report-number are available in the payload.
@@ -177,11 +182,12 @@ them but you should handle them according to your needs.
   timed out, the kernel will ignore the response silently. The "id" field is
   never re-used, so conflicts cannot happen.
 
-  UHID_SET_REPORT:
+UHID_SET_REPORT:
   This is the SET_REPORT equivalent of UHID_GET_REPORT. On receipt, you shall
   send a SET_REPORT request to your hid device. Once it replies, you must tell
   the kernel about it via UHID_SET_REPORT_REPLY.
   The same restrictions as for UHID_GET_REPORT apply.
 
 ----------------------------------------------------
+
 Written 2012, David Herrmann <dh.herrmann@gmail.com>
diff --git a/Documentation/input/input.rst b/Documentation/input/input.rst
index 47f86a4bf16c..0eb61e67a7b7 100644
--- a/Documentation/input/input.rst
+++ b/Documentation/input/input.rst
@@ -188,7 +188,7 @@ LCDs and many other purposes.
 
 The monitor and speaker controls should be easy to add to the hid/input
 interface, but for the UPSs and LCDs it doesn't make much sense. For this,
-the hiddev interface was designed. See Documentation/hid/hiddev.txt
+the hiddev interface was designed. See Documentation/hid/hiddev.rst
 for more information about it.
 
 The usage of the usbhid module is very simple, it takes no parameters,
diff --git a/MAINTAINERS b/MAINTAINERS
index 66bcec263dbf..4e1e598a32d9 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -16165,7 +16165,7 @@ M:	Benjamin Tissoires <benjamin.tissoires@redhat.com>
 L:	linux-usb@vger.kernel.org
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/hid/hid.git
 S:	Maintained
-F:	Documentation/hid/hiddev.txt
+F:	Documentation/hid/hiddev.rst
 F:	drivers/hid/usbhid/
 
 USB INTEL XHCI ROLE MUX DRIVER
-- 
2.20.1

^ permalink raw reply related

* Re: [RFC v2] iio: input-bridge: optionally bridge iio acceleometers to create a /dev/input interface
From: Jonathan Cameron @ 2019-04-22 14:20 UTC (permalink / raw)
  To: H. Nikolaus Schaller
  Cc: Dmitry Torokhov, Eric Piel, linux-input, letux-kernel, kernel,
	Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
	linux-kernel, linux-iio
In-Reply-To: <CD6219BE-61FF-4C38-9532-054C60A77F89@goldelico.com>

On Mon, 15 Apr 2019 23:04:15 +0200
H. Nikolaus Schaller <hns@goldelico.com> wrote:

> Hi Jonathan,
> we seem to have opposite goals and therefore quite different ideas what the right
> solution is.

Yup :)  Yikes this email has gotten complex.  Hopefully I haven't
lost track of the various points below.

> 
> I come from user-space and the input side, find that input can handle abstract "acceleration input",
> and some older chip drivers even provide that as their only interface. Therefore I want
> to provide "acceleration input" in these cases where only iio capable drivers exist by
> using the existing in-kernel-iio infrastructure. Nothing more.
> 
> You seem to come from the iio architecture and want to extend it to other APIs as
> general as possible and input is just one of several of them.

Yes, my target is to produce a subsystem that meets many (all would be nice)
requirements, including yours.  Whilst I'm happy to debate this for ever, I'm not
sure we are making any substantial progress.  As you mention below we
probably need to 'see the code' to drive the discussion forwards.

> 
> Different goals usually lead to different solution architectures.

Indeed, but in this case we have your proposal which is a subset of what
I am suggesting.  One architecture can fulfil both requirements.

I'll leave it for the other thread, but Bastien has raised the case
(that I'd forgotten) that there already userspace stacks that are
capable of happily taking in both IIO and Input devices.  The confusion
here is they will now discover 'both' without the existing userspace
knowing that they are the same device.  We need to be very careful not
to break those userspace programs.

So this is an illustration of why the simplistic case doesn't work
'now'.

> 
> > Am 14.04.2019 um 13:40 schrieb Jonathan Cameron <jic23@kernel.org>:
> > 
> > On Mon, 8 Apr 2019 15:15:56 +0200
> > H. Nikolaus Schaller <hns@goldelico.com> wrote:
> >   
> >> Hi Jonathan,
> >> 
> >> I still do not fully understand what is worrying you here.  
> >   
> >> 
> >> Do you worry about functionality, flexibility or resources or something else?  
> > 
> > Two main things:
> > 1) Lack of generality of the approach. 
> >   This is a single use trick for input devices. Why does it make sense for
> >   input devices?  
> 
> No, it is not a trick...
Bad choice of words. Sorry about that.

Any time we register a device as two types of device it is less than ideal.

If we had the true separation of IIO front end and back end then it
would be perfectly acceptable to 'only' have an input front end for
a given device.  That choice would still, in this sort of usecase,
have to be made from userspace. It's policy, not design.  If there are reasons
a particular device 'is input' then that mapping should be in DT or similar.
It's no different from knowing an ADC channel is wired to an analog
temperature sensor.  For example, you could build a joystick with
an accelerometer in the stick - then the usecase would be obvious!
Hence I would also argue that any dynamic interface should also support
a static path (DT or equivalent) for the cases where is really a
physical characteristic of the system!  Perhaps the Sony parts
fall into this category as well.

For a bit of historical background, there was a concerted effort
to produced a userspace stack for IIO for android.  
https://01.org/android-iio-sensors-hal

Unfortunately I think it died as a result of other moves in Intel on
one of their periodic shifts in direction.

> 
> Why does it make sense for input devices?

These are not just 'input' devices. They are accelerometers. One usecase
is to use them for input.  The exact same physical device is used for games
input uses and counting steps for example (actually a lot wider cases than
that, but this one is common in android devices).

Keep that in mind at all times. There are lots of usecases.
So we need a solution that does not result in problems for those
usecases.  We are not writing a subsytem targetting android use of
accelerometers. We are writing a subsystem addressing as many usecases
as we can of those devices.

Note that the original reason for IIO was to generalize a whole set of
proposed individual subsystems targeting particular sensor types. So
that is our focus - solutions that work for everyone.  This isn't
totally dissimilar from those discussions - at the time I wanted
to do a small focused ALS subsystem and got a resounding "no" from
Linus.  Generality matters a lot for the long term.

> 
> a) because there is no alternative for accelerometer input devices and there are some
> (older) accelerometer chips which only present themselves as input devices as soon
> as the driver is compiled and the chip is found.

Actually that is not accurate.  The vast majority of those older devices
that have had any attempt at mainlining are in IIO. AFAIK no accelerometer
driver has been merged to mainline input for many years. This is because,
amongst others, Dmitry has been of the view they didn't belong there for
a very long time.

> 
> b) because input events define ACCEL input devices. But no Temperature or Voltage
> input or generic ADC input.So there is no generalization of input devices beyond
> keyboard, mouse, joystick, accelerometer and some others.

That's not totally inaccurate, but the distinction in the other sensor cases
is that there is a clear 'additional' element that we can map in devicetree
which relates the IIO sensor channel to the input device.
Doesn't matter for the point of view of this discussion though.

> 
> >  There are lots of other in kernel users and potential
> >   ones in the future.  
> 
> This bridge addition does not exclude any (additional) in-kernel use.

No but it creates several problems:

1. Two ways to do the same thing. 
2. Two sets of code to maintain.
3. Confusion over what is the best way of doing it.
4. The known issues with multiple consumers (note my solution has
that problem as well!)

My job here is to maintain the code, which is why I push back on something
that makes that job harder.

When the next usecases comes along and someone says they want to map
all ADC channels to hwmon channels because that is the subystem that
they expect to measure voltages in, then I don't have a good argument
to stop them doing the same thing you have.

As a side note we have in the past had input drivers for gyroscopes and
magnetometers.  Why are accelerometers special?

I really don't see why we should treat accelerometers differently

As this discussion runs on, I am increasingly convinced that there *must*
be a userspace policy control over whether an input device is
instantiated for a given accelerometer.   Once that is the
case then I cannot see a reason to treat it any differently from
other channel types.

> 
> >  The ability to register additional IIO consumers like
> >   this is useful, lets make it useful to everyone.  
> 
> But not everyone and every iio device can and should be mappable to the "input" abstraction.
> This does not make any sense to me.

Absolutely.  It should not be.  I clearly didn't explain this well.

It should be mapped to a consumer.

One type of consumer is iio-input, another is iio-hmwon etc.

> 
> For example, does it make sense to map a temperature sensor to accelerometer input? Or an
> accelerometer to hwmon? This seems to be possible of your generalization unless I am missing something here.
> If it is, it ignores that iio sensors are already grouped by what physical property they do measure.

If people want to map crazy channels to crazy sensor outputs, why stop them?
(at this level of the interface).

This is a policy question for a userspace script.  Particular consumer drivers
could of course perform sanity checking and refuse to do anything if they
cannot sensibly use the channels.

Yes, the interface has this flexibility. Which is a good thing!  Take the example
of the gyroscope as input I used above.  If we want to add that support
in future to your driver (I have no idea if it actually makes sense)
then we can - without having to change the interface.

> 
> > 
> > 2) To much generality of the specific usecase.  I don't want to put an Input
> >   interface on accelerometers where it makes no sense.  
> 
> I think you can just ignore the input interfaces in that case, if it was created.

Bastien raised a case where this isn't true.

> 
> >  The rule of it has
> >   2-3 axis so it must make sense isn't good enough to my mind.  How
> >   does userspace know which accelerometer to use (more and more devices have
> >   multiple?)  
> 
> In our approach user-space can make it known by udev rules based on /dev/input/event*
> (not on iio but the input created for accelerometers). I think I mentioned that. This
> comes for free for any device registering as input. So it is no additional code.

Sorry, I'm lost.  What in there tells you to use 'this' interface rather than one
of the other N that were registered?  I'm not sure what information you
have available there.

> 
> >  You could do something like looking at the location info from
> >   DT / ACPI in your driver and pick the 'best' but that's policy. Should be
> >   in userspace.  Sure you can just use the right input driver, but the moment
> >   we do that, we need aware userspace, if that's the case why not make it
> >   aware from the start.
> > 
> > Believe me I've been round this one a good few times and thought about it
> > a lot.  
> 
> That is fine and why we should discuss all the different aspects we have collected.
> 
> >  I'll take a lot of convincing that this isn't a problem that
> > should be pushed into userspace.
> >   
> >> 
> >> I think having them mapped always does not need much resources (except a handful of bytes
> >> in memory and some µs during probing) unless the event device is opened and really used.
> >> Only then it starts e.g. I2C traffic to read the sensors.  
> > 
> > The bytes don't really mater.  
> 
> Ok, good to know.
> 
> > The userspace ABI additions do.  
> 
> There are only new /dev/input/event devices with well defined ABI. This approach does
> not invent anything new here, hence there are no ABI additions which we can break.

But it does - we aren't talking general ABI, but ABI on specific
devices.  Sure, Android doesn't care - though you'd be amazed how much
individual android device developers will because we just added another
pile of tests to their CI.

An industrial sensor platform absolutely does.  They have to validate
those interfaces.  They can't just ignore them because they feel like
it because who knows if some future user will use them?

For another case, see Bastien's reply to the later thread.

Instantiating interfaces has testing costs, even when the are standard
interfaces.

> 
> >   
> >> 
> >> So it is just some unused file sitting around in /dev. Or 2 or could be even 100.
> >> For devices which have no iio accelerometers configured, there will be no /dev/input
> >> file. So we are discussing the rare case of devices with more than one or two accelerometers.  
> > 
> > Well they aren't exactly rare in IIO using systems ;)  
> 
> This is another thing where our experiences differ. What specific devices are you thinking
> of? I am focussed on handhelds where the accelerometer (or two) is a way to do GUI input
> depending on device orientation in space.

Again, you are introducing this interface for everyone. Including lots of
'interesting' usecases.

I have worked with sensor platforms with accelerometers of different parts of humans,
We have people do bridge vibration measurement, flying UAVs and tracking the motion
of trucks.

Most are not huge numbers of accelerometers per node but don't rule out the
possibility.  It's normally limited by length of cables rather than anything
else so you used multiple nodes after a while each with their own set of sensors.

There are lots of plaforms out there that use multiple accelerometers in more
or less the same place to do very high dynamic range measurement (without losing
precision when things are nearly still).

Anyhow, it's not a particularly important point anyway!

> >   
> >> 
> >> Now, on every system there are many interfaces and files that are not used because it makes
> >> no sense to look at them. If I check on one of my systems, I find for example a lot of
> >> /dev/tty and only a very small portion is used and generic distros have no issue with it.
> >> 
> >> There is even /dev/iio:device0 to /dev/iio:device5 representing the raw iio devices.
> >> Not all of them are actively used, but they are simply there and can be scanned for.  
> > 
> > Agreed, in the ideal case we wouldn't have had that either, but we are
> > stuck with it.  The long term plan is to allow use of IIO backends without the
> > front end being there at all. Lots of SoC ADC users would prefer this. We are
> > stuck with the legacy intertwining fo the front end and back end of IIO so
> > this isn't as easy to do as I would like.  
> 
> Ah, ok. I think it is a similar discussion of hiding the serdev /dev/tty* if it is
> used for accessing an embedded GPS or Bluetooth chip, for example.
> 
> But is this needed? I think it is not a problem if there are multiple consumers for
> the same iio channel. Some in-kernel, some through /dev/iio:device* and maybe some
> through /dev/input (which boils down to in-kernel).

There are quite a few complexities around multiple consumers that we really haven't
solved.  Right now the cases that work are very much restricted.  I'd love
to tidy some of these up, but never enough time and all that.

It's not that relevant here, but in short a few of the issues are:
1) Interference over control settings.  - Two consumers need different filter
   settings/ sampling frequency / range.  How do we negotiate the choice and
   communicate it to the other consumers.  More complex questions such
   mediating choices of triggers.
2) One driver is doing polled reads, the other is doing interrupt driven.
   Most drivers prevent this combination because the polled reads can lead
   to unlimited delays on the interrupt driven path and hence break it.

The main driver for this separation was to present only the 'right' interface
to reduced people's validation costs etc.  People really do want to have the
option to strip back the userspace inteface.  Obviously these are the rare
people who would disable your config option, but the point of this was
that we actually would like to make even the IIO interface optional as
well but have a fair way to go before we can.

> 
> >   
> >> 
> >> So I do not see a resource problem if every accelerometer /dev/iio:device* gets
> >> some companion /dev/input/event* for being used on demand - but only if this bridge
> >> is configured at all.  
> > 
> > That argument does not apply. If we add a config option, distros will enable it.
> > So the vast majority of systems will ship with this turned on.  You cannot
> > use a config variable to control policy and expect it to be change by anyone
> > but a very very small subset of users.  So please drop the 'you can just not
> > build it argument'.  
> 
> This is not my point here. I mention this under the (now known to be wrong) assumption
> that resources do care. I just want to state that kernels built for platforms where every
> byte counts can be stripped down by disabling it. Others where resources are no concern
> simply can map them all, even if not used.

Agreed. A subset of users will just build without this.

> 
> > Userspace configuration changing is a lot easier if people actually care.
> > Sure, many distros will ship the same script to everyone.
> >   
> >>   
> >>> I think we need some deliberate userspace interaction to instantiate
> >>> one of these rather than 'always doing it'.    
> >> 
> >> My gut feeling is that this additional user-space interaction needs more resources and
> >> adds a lot of complexity, independently of how it is done.  
> > 
> > Trivial resources and actually fairly trivial complexity.  Key thing is
> > it puts the burden on the users of this functionality to configure what they
> > want.  
> 
> Hm. No. My proposal does not need configuration which accelerometers should go where.

Agreed. I was talking about my proposal here :)

> 
> I assumethat input accelerometer users do not want to configure anything, like neither
> a mouse or keyboard is to be configured to be useable (yes there are keymaps but that
> is impossible to automate).

The difference is a mouse is only really useful as a mouse and most of the time a keyboard
is a used only as a keyboard.  Here that's not true.

> 
> They just want to be able to read device orientation in a device-independent scale.
> Therefore my approach already takes the mount-matrix into account to hide sensor position
> differences.

And how does that work on the common case of a sensor in the lid of a laptop?
how do you know what angle the screen is at?  
One oddity to note here is that until very recently we deliberately didn't register
certain ACPI IDs because they confused userspace by reporting two accelerometers
without any info on which was in the lid.  Thankfully proper handling of that
is no being sorted.  It's still mostly a case of just deliberately ignoring one
of the sensors.

> 
> >   
> >> 
> >> And I think is even less flexible than "always doing it". Let me explain this claim.
> >> 
> >> For me, the kernel should present everything the hardware supports to user-space
> >> in better digestable device files or APIs (without making any assumptions about the
> >> user-space code).  
> > 
> > Agreed, we just have a different view on how this should be done. I want
> > it to be dynamic and extremely flexible, you want the easy way of just
> > putting a fixed set out all the time.
> >   
> >> 
> >> Then, every user-space that will be installed can find out what the hardware supports
> >> by looking at standard places.
> >> 
> >> E.g. it can scan for all mice and keyboards. And for all input accelerometers.  
> > 
> > Or, you an have the correct 'fairly trivial' userspace setup to scan for all
> > registered accelerometers and 'on demand' create the bindings to bring them up as
> > Input accelerometers if that is what makes sense for your platform.  
> 
> Why not scan for input accelerometers and leave it as an implementation detail that
> the kernel does serve the physical chips through the iio infrastructure?

If we could separate the IIO front end from the IIO backend I would agree that
would be another valid -userspace- policy.

> 
> IMHO some user-spaces may already be scanning all */input/event* and check for
> the device property INPUT_PROP_ACCELEROMETER.
> 
> This is a discussion mainly about proper encapsulation of lower level differences.
> 
> >   
> >> 
> >> If the kernel is hiding some chips and needs some initial user-space action before
> >> presenting them all, this requires that the user-space has some a-priori knowledge
> >> about which specific devices it should ask for.  
> > 
> > No more that it needs to know which accelerometer to use?  
> 
> >   
> >> So it does not really need to scan
> >> for them. Because it must already know. Obviously in some mapping table stored at
> >> a well known location inside the rootfs image.  
> > 
> > No. Let me give some more details of how this would work.  It's really just
> > a more flexible version of what you have.
> > 
> > A distro, or individual user decides to put the relevant script in place for the
> > following:
> > 
> > 1. Userspace detects a new accelerometer driver, via the standard methods (uevent)
> > 2. Userspace looks to see if it has the required properties. Now this includes things
> > like detecting that it is the accelerometer in the lid of a laptop - if so do not
> > register it as an input device.  If it's in the keyboard then do register it.
> > 3. Userspace script then creates the files in configfs
> > /sys/kernel/config/iio/maps/
> > (this interface needs appropriate definition)
> > Maybe...
> > /sys/kernel/config/iio/maps/iio_input/iio_device:X/accel_x, accel_y, etc
> > When done it writes to the bind file
> > /sys/kernel/config/iio/maps/iio_input/iio_device:X/bind
> > which instantiates the input driver.
> > 
> > This moves all of the policy decision into userspace, where it belongs.  If
> > we want to enable a particular accelerometer on a particular board because it
> > actually works better than the one the default policy says to use, then we can
> > do so.
> > 
> > The resulting infrastructure is much more general, because it lets us do the
> > same for any IIO consumer.  This input bridge is not a special case. It works
> > equally well for the existing hwmon bridge any would even let us do things
> > like provide the information from userspace that we have an analog accelerometer
> > wired up to an ADC on some hacker board.  
> 
> Ok, understood.
> 
> My approach triggers input uevents:
> 
> 1. kernel detects a new iio accelerometer (looks like an analog accelerometer should be
>    the DTS child of an iio adc and then iio should create an accelerometer and not a voltage
>    channel)

Yes ultimately it would be a child device that would be it's own IIO device. We
already have this for some gyroscopes.

> 2. iio-bridge registers as input event
> 3. this triggers an uevent
> 4  an udev-rule can detect the properties and map it to some "speaking" name like
>    /dev/input/main-accelerometer, /dev/input/lid-accelerometer etc. Or if the
>    accelerometer is to be ignored, it does not get a "speaking" name at all.
> 
> The required udev rules are stored in user space and are of course user-space and application
> specific. But this does not require to invent some new configfs stuff and special scripts
> in user-space. Just install some udev rule at a well established location in file-system.

I'm not sure there is any significant difference between you creating a mapping like
this an udev rule that creates the whole mapping.  Bit more to do perhaps but it's
nothing particularly special that I can see.  Sure there is new kernel support to be
done.

> 
> Yes, this does not cover arbitrary mappings. But what are arbitrary mappings good
> for? Your scheme seems to be able to map a light sensor to accelerometer input.
> Does this "full matrix of everything is possible" really make sense?

From a generic interface point of view - yes it absolutely does.

We define an interface that covers all usecases rather than a whole set of
separate ones that cover individual corner cases.  That way we don't have to
keep defining new interfaces.

The individual drivers can easily do validation of what they are provided with.

> 
> I can't decide because I have no need for it. Others may have.
> 
> But another thought: does it interfere with this input-bridge? Probably no. You can
> still add your configfs approach for general iio devices to e.g. hwmon mappings. Even
> as an alternate method of creating input devices (enabled only if my input-bridge is
> disabled).

Yes see above.  Both approaches meet your requirement (I think anyway).
I do not want to see two long term solutions to the same problem.

I'm interested in a long term sustainable solution so I want to see
the generic one.

> 
> > 
> >   
> >> 
> >> This seems to make it impossible to develop a generic distro rootfs image - without
> >> asking the user for manual configuration. And that where the kernel already knows
> >> this (which iio accelerometers do exist for a specific piece of hardware).
> >> 
> >> This is why I believe a mechanism to instantiate only on demand isn't adding but
> >> removing flexibility because it prevents copying a rootfs from one device to another.  
> > 
> > I disagree, see above.
> >   
> >>   
> >>> 
> >>> As I mentioned in V1, look at the possibility of a configfs based method
> >>> to build the map.  It's easy for userspace to work out what makes sense to
> >>> map in principle.  There may be some missing info that we also need to
> >>> look to expose.    
> >> 
> >> With a "may be missing" it is impossible to write code for it...
> >> Can you please name which information is missing on the input accelerometer
> >> API?  
> > 
> > See above. It's not the input accelerometer ABI, it's the missing ability
> > to instantiate IIO maps from user space.
> >   
> >>   
> >>> 
> >>> In general, userspace created channel maps would be very useful for
> >>> other things such as maker type boards where they can plug all sorts
> >>> of odd things into ADC channels for example.    
> >> 
> >> Ok, I understand, but this is a different problem where this iio-input-bridge is not
> >> intended to be a solution. Generic ADCs are not input devices. Like SD cards are not
> >> keyboards.
> >> 
> >> So we should not try to mix the idea of general mapping with this input-bridge for
> >> input accelerometers.  
> > Yes we should. You are proposing a solution that is a subset of the larger
> > problem set.  
> 
> Yes, of course. Because I did not see or know about the general problem set.
> And I still don't see a need for user-space controlled mapping for input-accelerometers.

We are clearly going to differ on this.  Bastien gave one example for why
this is required.  There will be others.

> 
> >  Why introduce a stop gap like this when we can do it correctly
> > and provide something useful for all those other use cases.
> > 
> > The only difference here is the uevent triggered script that creates those maps
> > for your particular usecase.  
> 
> Well, I am a friend of solving one problem after the other in smaller steps than
> immediately aiming at a very general solution, which has side-effects of inventing
> new stuff for things that would work without.

That works in a world where you can drop the previous approach as part of your
generalization.  When you are playing with kernel / userspace ABI then it
doesn't. Ideally you have to figure out the extensible general solution at the
start because you are stuck maintaining the 'small steps' for many years to
come.  I don't want to perpetually 'have' to export all 3D accelerometers as
input devices, because we didn't have the ability to chose which should be
exported at some point in the past.

> 
> > 
> >   
> >> 
> >> BTW, there is a way to define additional mapping using udev rules which symlink the
> >> /dev/input/event* paths to stable names like /dev/input/accelerometer.
> >> 
> >> This comes without additional code and is already provided by udev and the input system.
> >> 
> >> So in summary, I have not yet seen a convincing scenario where being able to dynamically
> >> map iio channels to input devices seems beneficial.  
> > 
> > That is true for the narrow case you are talking about. I don't want to see that
> > narrow case solved in a fashion that effectively breaks solving it properly.  
> 
> How does it break your approach if added later? The more I think about it they are
> not incompatible. It is just useless to apply both in parallel.

The reality is that if we put one in first that will used for ever because there
will be devices out there using it.  Therefore we have to maintain both for
ever.

> 
> > If we add this, we have to export all accelerometers for ever under all circumstances
> > to userspace, because to remove it will break existing userspace.
> > 
> > If we stand back and work out if we can do the general solution now, we avoid
> > this problem.  
> 
> We get a different problem that we break existing user-space that simply wants to see
> an /dev/input/accelerometer without doing more than an existing udev rule.

I would love to say such a userspace doesn't exist, but reality is there are
all sorts of hideous things out there.  There are cases that deal with this
as an option of course (such as Bastien's sensor-proxy)

The number of devices that are supported under mainline as input accelerometers
is pretty small.  It's not a perfect world unfortunately but having to add a
small udev script is at least not a major break if we do cause it.
> 
> >   
> >>   
> >>>   
> >>>> 
> >>>> This driver simply collects the first 3 accelerometer channels as X, Y and Z.
> >>>> If only 1 or 2 channels are available, they are used for X and Y only. Additional
> >>>> channels are ignored.
> >>>> 
> >>>> Scaling is done automatically so that 1g is represented by value 256 and
> >>>> range is assumed to be -511 .. +511 which gives a reasonable precision as an
> >>>> input device.    
> >>> 
> >>> Why do we do this, rather than letting input deal with it?  Input is used
> >>> to widely differing scales IIRC    
> >> 
> >> Well, it can't be done differently... And what I call scale here is nothing more than
> >> defining ABSMIN_ACC_VAL and ABSMAX_ACC_VAL.
> >> 
> >> We need to apply some scale since iio reports in (fractional) units of 1g, i.e. values
> >> of magnitude 1.  
> > 
> > m/s^2 not g, but doesn't matter for the point of view of this discussion.  
> 
> My fault. The driver takes care of this in the scaling formula so that "input" reports
> MAX/2 for 1g.
> 
> >   
> >> These are not adaequate for input events which use integers. So we must
> >> define some factor for iio_convert_raw_to_processed() to scale from raw value range
> >> to int value range. We could report raw values but this would be an improper abstraction
> >> from chip specific differences.  
> > 
> > Hmm. I can see we perhaps need some mapping, but is there a concept of standard scale
> > for existing input accelerometers?  How is this done to give for other input devices
> > such as touch screens?  I'd expect to see a separation between scale, and range.
> > 
> >   
> >> 
> >> BTW: the range (and therefore the factor) is reported through the evdev driver to user-space
> >> (evtest reports Min and Max as you can see in the example).
> >> 
> >> The most important thing is that this is a hardware independent definition. Every accelerometer
> >> chip will report this range. So you can easily upgrade hardware or switch accelerometers
> >> without touching user-space calibration. Like you can replace ethernet controller chips but
> >> networking works the same with all of them.  
> > 
> > Agreed, it needs to be hardware independent by the time it hits userspace, but I would
> > have thought that scaling would be done in input, rather than IIO. It's hardly
> > a problem unique to our usecase!
> > 
> > Perhaps Dmitry can give some advice on this.  
> 
> Yes, that would be helpful.
> 
> >   
> >> 
> >> 
> >> Hm. Is there an alternative to attach such private data to an struct iio_dev
> >> allocated by someone else? I have not found one yet.
> >> 
> >> Or can I add some void *input_mapping; to struct iio_dev? Depending on
> >> #if defined(CONFIG_IIO_INPUT_BRIDGE)?  
> > 
> > Yes, add a new element.  
> 
> Ok, works fine.
> 
> I already have found one case of iio accelerometer driver where it did make a problem
> not using a special element.
> 
> >>> 
> >>> iio_input_find_accel_channel(indio_dev, chan, &numchans);
> >>> iio_input_register_device(indio_dev, chan, numchans);    
> >> 
> >> Well, that looks like it needs some temporary storage of dynamic size
> >> and loop twice over channels for no functional benefit.  
> > 
> > Use fixed size. The worst that happens is we end up with it being
> > an entry larger that it needs to be.
> >   
> >> And handle the
> >> special case of numchans == 0 (the proposed code simply does not call
> >> iio_input_register_accel_channel and does not register anything).
> >> 
> >> So I'd prefer to follow the "KISS" principle and register single channels
> >> instead of a set of channels.  
> > 
> > Well we disagree on this.  A singleton approach like used here
> > is to my mind not KISS.  I would rather see what is there then
> > act as two simple steps, rather than interleave two different
> > actions with a totally different path for the first channel found.
> > If there is only one channel you just built a load of infrastructure
> > that makes no sense.  If you scan first then you can know that
> > before building anything.  
> 
> Ok, this is more a matter of taste and resource requirements can probably
> be neglected. I'll update the driver.
> 
> So in summary, I'll post a v3 that fixes some bugs of v2 (because we need
> them fixed for our production systems as well).
> 
> Then it is up to you if you want to take this approach or want to write
> a full version following your concept. Or if it is possible as I assume, we
> can have both.

Thanks. I think we need at some code for what I was proposing to discuss
much further. Unfortunately it may be a little while before I get time to
work on that.  Hopefully not too long though!

Jonathan

> 
> BR and thanks,
> Nikolaus
> 

^ permalink raw reply

* [PATCH] drivers: hid: Add a module description line
From: Joseph Salisbury @ 2019-04-22 21:31 UTC (permalink / raw)
  To: KY Srinivasan, Haiyang Zhang, Stephen Hemminger, Michael Kelley,
	sashal@kernel.org, jikos@kernel.org,
	benjamin.tissoires@redhat.com
  Cc: linux-hyperv@vger.kernel.org, linux-input@vger.kernel.org,
	linux-kernel@vger.kernel.org

Signed-off-by: Joseph Salisbury <joseph.salisbury@microsoft.com>
---
 drivers/hid/hid-hyperv.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/hid/hid-hyperv.c b/drivers/hid/hid-hyperv.c
index 704049e62d58..d3311d714d35 100644
--- a/drivers/hid/hid-hyperv.c
+++ b/drivers/hid/hid-hyperv.c
@@ -614,5 +614,7 @@ static void __exit mousevsc_exit(void)
 }
 
 MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Microsoft Hyper-V Synthetic HID Driver");
+
 module_init(mousevsc_init);
 module_exit(mousevsc_exit);
-- 
2.17.1


^ permalink raw reply related

* [PATCH] drivers: input: serio: Add a module desription
From: Joseph Salisbury @ 2019-04-22 21:38 UTC (permalink / raw)
  To: KY Srinivasan, Haiyang Zhang, Stephen Hemminger, Michael Kelley,
	sashal@kernel.org, dmitry.torokhov@gmail.com
  Cc: linux-hyperv@vger.kernel.org, linux-input@vger.kernel.org,
	linux-kernel@vger.kernel.org

Signed-off-by: Joseph Salisbury <joseph.salisbury@microsoft.com>
---
 drivers/input/serio/hyperv-keyboard.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/input/serio/hyperv-keyboard.c b/drivers/input/serio/hyperv-keyboard.c
index a8b9be3e28db..7935e52b5435 100644
--- a/drivers/input/serio/hyperv-keyboard.c
+++ b/drivers/input/serio/hyperv-keyboard.c
@@ -440,5 +440,7 @@ static void __exit hv_kbd_exit(void)
 }
 
 MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Microsoft Hyper-V Synthetic Keyboard Driver");
+
 module_init(hv_kbd_init);
 module_exit(hv_kbd_exit);
-- 
2.17.1


^ permalink raw reply related

* RE: [PATCH] drivers: hid: Add a module description line
From: Michael Kelley @ 2019-04-23  3:15 UTC (permalink / raw)
  To: Joseph Salisbury, KY Srinivasan, Haiyang Zhang, Stephen Hemminger,
	sashal@kernel.org, jikos@kernel.org,
	benjamin.tissoires@redhat.com
  Cc: linux-hyperv@vger.kernel.org, linux-input@vger.kernel.org,
	linux-kernel@vger.kernel.org
In-Reply-To: <324016691a62a13ce46c9ccd35c7e492bf609fd6.1555967348.git.joseph.salisbury@microsoft.com>

From: Joseph Salisbury <Joseph.Salisbury@microsoft.com> Sent: Monday, April 22, 2019 2:31 PM
> 
> Signed-off-by: Joseph Salisbury <joseph.salisbury@microsoft.com>
> ---
>  drivers/hid/hid-hyperv.c | 2 ++
>  1 file changed, 2 insertions(+)
> 
> diff --git a/drivers/hid/hid-hyperv.c b/drivers/hid/hid-hyperv.c
> index 704049e62d58..d3311d714d35 100644
> --- a/drivers/hid/hid-hyperv.c
> +++ b/drivers/hid/hid-hyperv.c
> @@ -614,5 +614,7 @@ static void __exit mousevsc_exit(void)
>  }
> 
>  MODULE_LICENSE("GPL");
> +MODULE_DESCRIPTION("Microsoft Hyper-V Synthetic HID Driver");
> +
>  module_init(mousevsc_init);
>  module_exit(mousevsc_exit);
> --
> 2.17.1

Even though it will likely be redundant with the commit title, there
probably needs to be a short commit message.   (And also with the
other two similar patches.)

Michael


^ 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