From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752253AbcI1IgY (ORCPT ); Wed, 28 Sep 2016 04:36:24 -0400 Received: from mx1.redhat.com ([209.132.183.28]:42700 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752105AbcI1IgB (ORCPT ); Wed, 28 Sep 2016 04:36:01 -0400 From: Benjamin Tissoires To: Dmitry Torokhov , Lyude Paul , Andrew Duggan , Christopher Heiny Cc: Peter Hutterer , linux-kernel@vger.kernel.org, linux-input@vger.kernel.org Subject: [PATCH v2 04/12] Input: synaptics-rmi4 - add support for F03 Date: Wed, 28 Sep 2016 10:35:39 +0200 Message-Id: <1475051747-25988-5-git-send-email-benjamin.tissoires@redhat.com> In-Reply-To: <1475051747-25988-1-git-send-email-benjamin.tissoires@redhat.com> References: <1475051747-25988-1-git-send-email-benjamin.tissoires@redhat.com> X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.30]); Wed, 28 Sep 2016 08:36:00 +0000 (UTC) Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Lyude Paul This adds basic functionality for PS/2 passthrough on Synaptics Touchpads using RMI4 through smbus. Reviewed-by: Andrew Duggan Signed-off-by: Lyude Paul Signed-off-by: Benjamin Tissoires --- changes in v2: - switch to use rmi_dbg instead of plain dev_dbg --- drivers/input/mouse/psmouse-base.c | 6 + drivers/input/rmi4/Kconfig | 9 ++ drivers/input/rmi4/Makefile | 1 + drivers/input/rmi4/rmi_bus.c | 3 + drivers/input/rmi4/rmi_driver.h | 1 + drivers/input/rmi4/rmi_f03.c | 227 +++++++++++++++++++++++++++++++++++++ include/uapi/linux/serio.h | 1 + 7 files changed, 248 insertions(+) create mode 100644 drivers/input/rmi4/rmi_f03.c diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c index f9b04fd..59d7c37 100644 --- a/drivers/input/mouse/psmouse-base.c +++ b/drivers/input/mouse/psmouse-base.c @@ -1666,6 +1666,12 @@ static struct serio_device_id psmouse_serio_ids[] = { .id = SERIO_ANY, .extra = SERIO_ANY, }, + { + .type = SERIO_RMI_PSTHRU, + .proto = SERIO_ANY, + .id = SERIO_ANY, + .extra = SERIO_ANY, + }, { 0 } }; diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig index f49958c..c4c0975 100644 --- a/drivers/input/rmi4/Kconfig +++ b/drivers/input/rmi4/Kconfig @@ -39,6 +39,15 @@ config RMI4_SMB To compile this driver as a module, choose M here: the module will be called rmi_smbus. +config RMI4_F03 + bool "RMI4 Function 03 (PS2 Guest)" + depends on RMI4_CORE + help + Say Y here if you want to add support for RMI4 function 03. + + Function 03 provides PS2 guest support for RMI4 devices. This + includes support for TrackPoints on TouchPads. + config RMI4_2D_SENSOR bool depends on RMI4_CORE diff --git a/drivers/input/rmi4/Makefile b/drivers/input/rmi4/Makefile index a6e2752..8271b65 100644 --- a/drivers/input/rmi4/Makefile +++ b/drivers/input/rmi4/Makefile @@ -4,6 +4,7 @@ rmi_core-y := rmi_bus.o rmi_driver.o rmi_f01.o rmi_core-$(CONFIG_RMI4_2D_SENSOR) += rmi_2d_sensor.o # Function drivers +rmi_core-$(CONFIG_RMI4_F03) += rmi_f03.o rmi_core-$(CONFIG_RMI4_F11) += rmi_f11.o rmi_core-$(CONFIG_RMI4_F12) += rmi_f12.o rmi_core-$(CONFIG_RMI4_F30) += rmi_f30.o diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c index 09c769c..fd97384 100644 --- a/drivers/input/rmi4/rmi_bus.c +++ b/drivers/input/rmi4/rmi_bus.c @@ -303,6 +303,9 @@ struct bus_type rmi_bus_type = { static struct rmi_function_handler *fn_handlers[] = { &rmi_f01_handler, +#ifdef CONFIG_RMI4_F03 + &rmi_f03_handler, +#endif #ifdef CONFIG_RMI4_F11 &rmi_f11_handler, #endif diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h index 8dfbebe..fe26aca 100644 --- a/drivers/input/rmi4/rmi_driver.h +++ b/drivers/input/rmi4/rmi_driver.h @@ -99,6 +99,7 @@ void rmi_unregister_physical_driver(void); char *rmi_f01_get_product_ID(struct rmi_function *fn); extern struct rmi_function_handler rmi_f01_handler; +extern struct rmi_function_handler rmi_f03_handler; extern struct rmi_function_handler rmi_f11_handler; extern struct rmi_function_handler rmi_f12_handler; extern struct rmi_function_handler rmi_f30_handler; diff --git a/drivers/input/rmi4/rmi_f03.c b/drivers/input/rmi4/rmi_f03.c new file mode 100644 index 0000000..a7e1b98 --- /dev/null +++ b/drivers/input/rmi4/rmi_f03.c @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2015-2016 Red Hat + * Copyright (C) 2015 Lyude Paul + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + */ + +#include +#include +#include +#include +#include "rmi_driver.h" + +#define RMI_F03_RX_DATA_OFB 0x01 +#define RMI_F03_OB_SIZE 2 + +#define RMI_F03_OB_OFFSET 2 +#define RMI_F03_OB_DATA_OFFSET 1 +#define RMI_F03_OB_FLAG_TIMEOUT (1 << 6) +#define RMI_F03_OB_FLAG_PARITY (1 << 7) + +#define RMI_F03_DEVICE_COUNT 0x07 +#define RMI_F03_BYTES_PER_DEVICE_MASK 0x70 +#define RMI_F03_BYTES_PER_DEVICE_SHIFT 4 +#define RMI_F03_QUEUE_LENGTH 0x0F + +struct f03_data { + struct rmi_function *fn; + + struct serio *serio; + + unsigned int overwrite_buttons; + + u8 device_count; + u8 rx_queue_length; +}; + +static int rmi_f03_pt_write(struct serio *id, unsigned char val) +{ + struct f03_data *f03 = id->port_data; + int rc; + + rmi_dbg(RMI_DEBUG_FN, &f03->fn->dev, + "%s: Wrote %.2hhx to PS/2 passthrough address", + __func__, val); + + rc = rmi_write(f03->fn->rmi_dev, f03->fn->fd.data_base_addr, val); + if (rc) { + dev_err(&f03->fn->dev, + "%s: Failed to write to F03 TX register.\n", __func__); + return rc; + } + + return 0; +} + +static inline int rmi_f03_initialize(struct rmi_function *fn) +{ + struct f03_data *f03; + struct device *dev = &fn->dev; + int rc; + u8 bytes_per_device; + u8 query1; + size_t query2_len; + + rc = rmi_read(fn->rmi_dev, fn->fd.query_base_addr, &query1); + if (rc) { + dev_err(dev, "Failed to read query register.\n"); + return rc; + } + + f03 = devm_kzalloc(dev, sizeof(struct f03_data), GFP_KERNEL); + if (!f03) + return -ENOMEM; + + f03->device_count = query1 & RMI_F03_DEVICE_COUNT; + bytes_per_device = (query1 & RMI_F03_BYTES_PER_DEVICE_MASK) >> + RMI_F03_BYTES_PER_DEVICE_SHIFT; + + query2_len = f03->device_count * bytes_per_device; + + /* + * The first generation of image sensors don't have a second part to + * their f03 query, as such we have to set some of these values manually + */ + if (query2_len < 1) { + f03->device_count = 1; + f03->rx_queue_length = 7; + } else { + u8 query2[query2_len]; + + rc = rmi_read_block(fn->rmi_dev, fn->fd.query_base_addr + 1, + query2, query2_len); + if (rc) { + dev_err(dev, "Failed to read second set of query registers.\n"); + return rc; + } + + f03->rx_queue_length = query2[0] & RMI_F03_QUEUE_LENGTH; + } + + f03->fn = fn; + + dev_set_drvdata(dev, f03); + + return f03->device_count; +} + +static inline int rmi_f03_register_pt(struct rmi_function *fn) +{ + struct f03_data *f03 = dev_get_drvdata(&fn->dev); + struct serio *serio = kzalloc(sizeof(struct serio), GFP_KERNEL); + + if (!serio) + return -ENOMEM; + + serio->id.type = SERIO_RMI_PSTHRU; + serio->write = rmi_f03_pt_write; + serio->port_data = f03; + + strlcpy(serio->name, "Synaptics RMI4 PS2 pass-through", + sizeof(serio->name)); + strlcpy(serio->phys, "synaptics-rmi4-pt/serio1", + sizeof(serio->phys)); + serio->dev.parent = &fn->dev; + + f03->serio = serio; + + serio_register_port(serio); + + return 0; +} + +static int rmi_f03_probe(struct rmi_function *fn) +{ + int rc; + + rc = rmi_f03_initialize(fn); + if (rc < 0) + return rc; + + rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%d devices on PS/2 passthrough", rc); + + rc = rmi_f03_register_pt(fn); + if (rc) + return rc; + + return 0; +} + +static int rmi_f03_config(struct rmi_function *fn) +{ + fn->rmi_dev->driver->set_irq_bits(fn->rmi_dev, fn->irq_mask); + + return 0; +} + +static int rmi_f03_attention(struct rmi_function *fn, unsigned long *irq_bits) +{ + struct f03_data *f03 = dev_get_drvdata(&fn->dev); + u16 data_addr = fn->fd.data_base_addr; + const u8 ob_len = f03->rx_queue_length * RMI_F03_OB_SIZE; + u8 obs[ob_len]; + u8 ob_status; + u8 ob_data; + unsigned int serio_flags; + int i; + int retval; + + /* Grab all of the data registers, and check them for data */ + retval = rmi_read_block(fn->rmi_dev, data_addr + RMI_F03_OB_OFFSET, + &obs, ob_len); + if (retval) { + dev_err(&fn->dev, "%s: Failed to read F03 output buffers.\n", + __func__); + serio_interrupt(f03->serio, 0, SERIO_TIMEOUT); + return retval; + } + + for (i = 0; i < ob_len; i += RMI_F03_OB_SIZE) { + ob_status = obs[i]; + ob_data = obs[i + RMI_F03_OB_DATA_OFFSET]; + serio_flags = 0; + + if (!(ob_status & RMI_F03_RX_DATA_OFB)) + continue; + + if (ob_status & RMI_F03_OB_FLAG_TIMEOUT) + serio_flags |= SERIO_TIMEOUT; + if (ob_status & RMI_F03_OB_FLAG_PARITY) + serio_flags |= SERIO_PARITY; + + rmi_dbg(RMI_DEBUG_FN, &fn->dev, + "%s: Received %.2hhx from PS2 guest T: %c P: %c\n", + __func__, ob_data, + serio_flags & SERIO_TIMEOUT ? 'Y' : 'N', + serio_flags & SERIO_PARITY ? 'Y' : 'N'); + + serio_interrupt(f03->serio, ob_data, serio_flags); + } + + return 0; +} + +static void rmi_f03_remove(struct rmi_function *fn) +{ + struct f03_data *f03 = dev_get_drvdata(&fn->dev); + + serio_unregister_port(f03->serio); +} + +struct rmi_function_handler rmi_f03_handler = { + .driver = { + .name = "rmi4_f03", + }, + .func = 0x03, + .probe = rmi_f03_probe, + .config = rmi_f03_config, + .attention = rmi_f03_attention, + .remove = rmi_f03_remove, +}; + +MODULE_AUTHOR("Lyude Paul "); +MODULE_DESCRIPTION("RMI F03 module"); +MODULE_LICENSE("GPL"); diff --git a/include/uapi/linux/serio.h b/include/uapi/linux/serio.h index f2447a8..7012178 100644 --- a/include/uapi/linux/serio.h +++ b/include/uapi/linux/serio.h @@ -30,6 +30,7 @@ #define SERIO_HIL_MLC 0x03 #define SERIO_PS_PSTHRU 0x05 #define SERIO_8042_XL 0x06 +#define SERIO_RMI_PSTHRU 0x07 /* * Serio protocols -- 2.7.4