Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Input: synaptics-rmi4 - add support for F03

This adds basic functionality for PS/2 passthrough on Synaptics
Touchpads using RMI4 through smbus.

Reviewed-by: Andrew Duggan <aduggan@synaptics.com>
Signed-off-by: Lyude Paul <thatslyude@gmail.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>

authored by

Lyude Paul and committed by
Dmitry Torokhov
c5e8848f 0a135b88

+246
+9
drivers/input/rmi4/Kconfig
··· 39 39 To compile this driver as a module, choose M here: the module will be 40 40 called rmi_smbus. 41 41 42 + config RMI4_F03 43 + bool "RMI4 Function 03 (PS2 Guest)" 44 + depends on RMI4_CORE && SERIO 45 + help 46 + Say Y here if you want to add support for RMI4 function 03. 47 + 48 + Function 03 provides PS2 guest support for RMI4 devices. This 49 + includes support for TrackPoints on TouchPads. 50 + 42 51 config RMI4_2D_SENSOR 43 52 bool 44 53 depends on RMI4_CORE
+1
drivers/input/rmi4/Makefile
··· 4 4 rmi_core-$(CONFIG_RMI4_2D_SENSOR) += rmi_2d_sensor.o 5 5 6 6 # Function drivers 7 + rmi_core-$(CONFIG_RMI4_F03) += rmi_f03.o 7 8 rmi_core-$(CONFIG_RMI4_F11) += rmi_f11.o 8 9 rmi_core-$(CONFIG_RMI4_F12) += rmi_f12.o 9 10 rmi_core-$(CONFIG_RMI4_F30) += rmi_f30.o
+3
drivers/input/rmi4/rmi_bus.c
··· 306 306 307 307 static struct rmi_function_handler *fn_handlers[] = { 308 308 &rmi_f01_handler, 309 + #ifdef CONFIG_RMI4_F03 310 + &rmi_f03_handler, 311 + #endif 309 312 #ifdef CONFIG_RMI4_F11 310 313 &rmi_f11_handler, 311 314 #endif
+1
drivers/input/rmi4/rmi_driver.h
··· 121 121 #endif /* CONFIG_RMI_F34 */ 122 122 123 123 extern struct rmi_function_handler rmi_f01_handler; 124 + extern struct rmi_function_handler rmi_f03_handler; 124 125 extern struct rmi_function_handler rmi_f11_handler; 125 126 extern struct rmi_function_handler rmi_f12_handler; 126 127 extern struct rmi_function_handler rmi_f30_handler;
+232
drivers/input/rmi4/rmi_f03.c
··· 1 + /* 2 + * Copyright (C) 2015-2016 Red Hat 3 + * Copyright (C) 2015 Lyude Paul <thatslyude@gmail.com> 4 + * 5 + * This program is free software; you can redistribute it and/or modify it 6 + * under the terms of the GNU General Public License version 2 as published by 7 + * the Free Software Foundation. 8 + */ 9 + 10 + #include <linux/kernel.h> 11 + #include <linux/slab.h> 12 + #include <linux/serio.h> 13 + #include <linux/notifier.h> 14 + #include "rmi_driver.h" 15 + 16 + #define RMI_F03_RX_DATA_OFB 0x01 17 + #define RMI_F03_OB_SIZE 2 18 + 19 + #define RMI_F03_OB_OFFSET 2 20 + #define RMI_F03_OB_DATA_OFFSET 1 21 + #define RMI_F03_OB_FLAG_TIMEOUT BIT(6) 22 + #define RMI_F03_OB_FLAG_PARITY BIT(7) 23 + 24 + #define RMI_F03_DEVICE_COUNT 0x07 25 + #define RMI_F03_BYTES_PER_DEVICE 0x07 26 + #define RMI_F03_BYTES_PER_DEVICE_SHIFT 4 27 + #define RMI_F03_QUEUE_LENGTH 0x0F 28 + 29 + struct f03_data { 30 + struct rmi_function *fn; 31 + 32 + struct serio *serio; 33 + 34 + u8 device_count; 35 + u8 rx_queue_length; 36 + }; 37 + 38 + static int rmi_f03_pt_write(struct serio *id, unsigned char val) 39 + { 40 + struct f03_data *f03 = id->port_data; 41 + int error; 42 + 43 + rmi_dbg(RMI_DEBUG_FN, &f03->fn->dev, 44 + "%s: Wrote %.2hhx to PS/2 passthrough address", 45 + __func__, val); 46 + 47 + error = rmi_write(f03->fn->rmi_dev, f03->fn->fd.data_base_addr, val); 48 + if (error) { 49 + dev_err(&f03->fn->dev, 50 + "%s: Failed to write to F03 TX register (%d).\n", 51 + __func__, error); 52 + return error; 53 + } 54 + 55 + return 0; 56 + } 57 + 58 + static int rmi_f03_initialize(struct f03_data *f03) 59 + { 60 + struct rmi_function *fn = f03->fn; 61 + struct device *dev = &fn->dev; 62 + int error; 63 + u8 bytes_per_device; 64 + u8 query1; 65 + u8 query2[RMI_F03_DEVICE_COUNT * RMI_F03_BYTES_PER_DEVICE]; 66 + size_t query2_len; 67 + 68 + error = rmi_read(fn->rmi_dev, fn->fd.query_base_addr, &query1); 69 + if (error) { 70 + dev_err(dev, "Failed to read query register (%d).\n", error); 71 + return error; 72 + } 73 + 74 + f03->device_count = query1 & RMI_F03_DEVICE_COUNT; 75 + bytes_per_device = (query1 >> RMI_F03_BYTES_PER_DEVICE_SHIFT) & 76 + RMI_F03_BYTES_PER_DEVICE; 77 + 78 + query2_len = f03->device_count * bytes_per_device; 79 + 80 + /* 81 + * The first generation of image sensors don't have a second part to 82 + * their f03 query, as such we have to set some of these values manually 83 + */ 84 + if (query2_len < 1) { 85 + f03->device_count = 1; 86 + f03->rx_queue_length = 7; 87 + } else { 88 + error = rmi_read_block(fn->rmi_dev, fn->fd.query_base_addr + 1, 89 + query2, query2_len); 90 + if (error) { 91 + dev_err(dev, 92 + "Failed to read second set of query registers (%d).\n", 93 + error); 94 + return error; 95 + } 96 + 97 + f03->rx_queue_length = query2[0] & RMI_F03_QUEUE_LENGTH; 98 + } 99 + 100 + return 0; 101 + } 102 + 103 + static int rmi_f03_register_pt(struct f03_data *f03) 104 + { 105 + struct serio *serio; 106 + 107 + serio = kzalloc(sizeof(struct serio), GFP_KERNEL); 108 + if (!serio) 109 + return -ENOMEM; 110 + 111 + serio->id.type = SERIO_8042; 112 + serio->write = rmi_f03_pt_write; 113 + serio->port_data = f03; 114 + 115 + strlcpy(serio->name, "Synaptics RMI4 PS/2 pass-through", 116 + sizeof(serio->name)); 117 + strlcpy(serio->phys, "synaptics-rmi4-pt/serio1", 118 + sizeof(serio->phys)); 119 + serio->dev.parent = &f03->fn->dev; 120 + 121 + f03->serio = serio; 122 + 123 + serio_register_port(serio); 124 + 125 + return 0; 126 + } 127 + 128 + static int rmi_f03_probe(struct rmi_function *fn) 129 + { 130 + struct device *dev = &fn->dev; 131 + struct f03_data *f03; 132 + int error; 133 + 134 + f03 = devm_kzalloc(dev, sizeof(struct f03_data), GFP_KERNEL); 135 + if (!f03) 136 + return -ENOMEM; 137 + 138 + f03->fn = fn; 139 + 140 + error = rmi_f03_initialize(f03); 141 + if (error < 0) 142 + return error; 143 + 144 + if (f03->device_count != 1) 145 + dev_warn(dev, "found %d devices on PS/2 passthrough", 146 + f03->device_count); 147 + 148 + dev_set_drvdata(dev, f03); 149 + 150 + error = rmi_f03_register_pt(f03); 151 + if (error) 152 + return error; 153 + 154 + return 0; 155 + } 156 + 157 + static int rmi_f03_config(struct rmi_function *fn) 158 + { 159 + fn->rmi_dev->driver->set_irq_bits(fn->rmi_dev, fn->irq_mask); 160 + 161 + return 0; 162 + } 163 + 164 + static int rmi_f03_attention(struct rmi_function *fn, unsigned long *irq_bits) 165 + { 166 + struct f03_data *f03 = dev_get_drvdata(&fn->dev); 167 + u16 data_addr = fn->fd.data_base_addr; 168 + const u8 ob_len = f03->rx_queue_length * RMI_F03_OB_SIZE; 169 + u8 obs[RMI_F03_QUEUE_LENGTH * RMI_F03_OB_SIZE]; 170 + u8 ob_status; 171 + u8 ob_data; 172 + unsigned int serio_flags; 173 + int i; 174 + int error; 175 + 176 + /* Grab all of the data registers, and check them for data */ 177 + error = rmi_read_block(fn->rmi_dev, data_addr + RMI_F03_OB_OFFSET, 178 + &obs, ob_len); 179 + if (error) { 180 + dev_err(&fn->dev, 181 + "%s: Failed to read F03 output buffers: %d\n", 182 + __func__, error); 183 + serio_interrupt(f03->serio, 0, SERIO_TIMEOUT); 184 + return error; 185 + } 186 + 187 + for (i = 0; i < ob_len; i += RMI_F03_OB_SIZE) { 188 + ob_status = obs[i]; 189 + ob_data = obs[i + RMI_F03_OB_DATA_OFFSET]; 190 + serio_flags = 0; 191 + 192 + if (!(ob_status & RMI_F03_RX_DATA_OFB)) 193 + continue; 194 + 195 + if (ob_status & RMI_F03_OB_FLAG_TIMEOUT) 196 + serio_flags |= SERIO_TIMEOUT; 197 + if (ob_status & RMI_F03_OB_FLAG_PARITY) 198 + serio_flags |= SERIO_PARITY; 199 + 200 + rmi_dbg(RMI_DEBUG_FN, &fn->dev, 201 + "%s: Received %.2hhx from PS2 guest T: %c P: %c\n", 202 + __func__, ob_data, 203 + serio_flags & SERIO_TIMEOUT ? 'Y' : 'N', 204 + serio_flags & SERIO_PARITY ? 'Y' : 'N'); 205 + 206 + serio_interrupt(f03->serio, ob_data, serio_flags); 207 + } 208 + 209 + return 0; 210 + } 211 + 212 + static void rmi_f03_remove(struct rmi_function *fn) 213 + { 214 + struct f03_data *f03 = dev_get_drvdata(&fn->dev); 215 + 216 + serio_unregister_port(f03->serio); 217 + } 218 + 219 + struct rmi_function_handler rmi_f03_handler = { 220 + .driver = { 221 + .name = "rmi4_f03", 222 + }, 223 + .func = 0x03, 224 + .probe = rmi_f03_probe, 225 + .config = rmi_f03_config, 226 + .attention = rmi_f03_attention, 227 + .remove = rmi_f03_remove, 228 + }; 229 + 230 + MODULE_AUTHOR("Lyude Paul <thatslyude@gmail.com>"); 231 + MODULE_DESCRIPTION("RMI F03 module"); 232 + MODULE_LICENSE("GPL");