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

Input: Add Synaptics NavPoint (PXA27x SSP/SPI) driver

This driver adds support for the Synaptics NavPoint touchpad connected
to a PXA27x SSP port in SPI slave mode. The device emulates a mouse;
a tap or tap-and-a-half drag gesture emulates the left mouse button.
For example, use the xf86-input-evdev driver for an X pointing device.

Signed-off-by: Paul Parsons <lost.distance@yahoo.com>
Tested-by: Philipp Zabel <philipp.zabel@gmail.com>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>

authored by

Paul Parsons and committed by
Dmitry Torokhov
ae99ea56 6ee3dbf9

+394
+12
drivers/input/mouse/Kconfig
··· 339 339 To compile this driver as a module, choose M here: the 340 340 module will be called synaptics_usb. 341 341 342 + config MOUSE_NAVPOINT_PXA27x 343 + tristate "Synaptics NavPoint (PXA27x SSP/SPI)" 344 + depends on PXA27x && PXA_SSP 345 + help 346 + This driver adds support for the Synaptics NavPoint touchpad connected 347 + to a PXA27x SSP port in SPI slave mode. The device emulates a mouse; 348 + a tap or tap-and-a-half drag gesture emulates the left mouse button. 349 + For example, use the xf86-input-evdev driver for an X pointing device. 350 + 351 + To compile this driver as a module, choose M here: the 352 + module will be called navpoint. 353 + 342 354 endif
+1
drivers/input/mouse/Makefile
··· 12 12 obj-$(CONFIG_MOUSE_INPORT) += inport.o 13 13 obj-$(CONFIG_MOUSE_LOGIBM) += logibm.o 14 14 obj-$(CONFIG_MOUSE_MAPLE) += maplemouse.o 15 + obj-$(CONFIG_MOUSE_NAVPOINT_PXA27x) += navpoint.o 15 16 obj-$(CONFIG_MOUSE_PC110PAD) += pc110pad.o 16 17 obj-$(CONFIG_MOUSE_PS2) += psmouse.o 17 18 obj-$(CONFIG_MOUSE_PXA930_TRKBALL) += pxa930_trkball.o
+369
drivers/input/mouse/navpoint.c
··· 1 + /* 2 + * Synaptics NavPoint (PXA27x SSP/SPI) driver. 3 + * 4 + * Copyright (C) 2012 Paul Parsons <lost.distance@yahoo.com> 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License version 2 as 8 + * published by the Free Software Foundation. 9 + */ 10 + 11 + #include <linux/kernel.h> 12 + #include <linux/init.h> 13 + #include <linux/module.h> 14 + #include <linux/platform_device.h> 15 + #include <linux/clk.h> 16 + #include <linux/delay.h> 17 + #include <linux/gpio.h> 18 + #include <linux/input.h> 19 + #include <linux/input/navpoint.h> 20 + #include <linux/interrupt.h> 21 + #include <linux/mutex.h> 22 + #include <linux/pxa2xx_ssp.h> 23 + #include <linux/slab.h> 24 + 25 + /* 26 + * Synaptics Modular Embedded Protocol: Module Packet Format. 27 + * Module header byte 2:0 = Length (# bytes that follow) 28 + * Module header byte 4:3 = Control 29 + * Module header byte 7:5 = Module Address 30 + */ 31 + #define HEADER_LENGTH(byte) ((byte) & 0x07) 32 + #define HEADER_CONTROL(byte) (((byte) >> 3) & 0x03) 33 + #define HEADER_ADDRESS(byte) ((byte) >> 5) 34 + 35 + struct navpoint { 36 + struct ssp_device *ssp; 37 + struct input_dev *input; 38 + struct device *dev; 39 + int gpio; 40 + int index; 41 + u8 data[1 + HEADER_LENGTH(0xff)]; 42 + }; 43 + 44 + /* 45 + * Initialization values for SSCR0_x, SSCR1_x, SSSR_x. 46 + */ 47 + static const u32 sscr0 = 0 48 + | SSCR0_TUM /* TIM = 1; No TUR interrupts */ 49 + | SSCR0_RIM /* RIM = 1; No ROR interrupts */ 50 + | SSCR0_SSE /* SSE = 1; SSP enabled */ 51 + | SSCR0_Motorola /* FRF = 0; Motorola SPI */ 52 + | SSCR0_DataSize(16) /* DSS = 15; Data size = 16-bit */ 53 + ; 54 + static const u32 sscr1 = 0 55 + | SSCR1_SCFR /* SCFR = 1; SSPSCLK only during transfers */ 56 + | SSCR1_SCLKDIR /* SCLKDIR = 1; Slave mode */ 57 + | SSCR1_SFRMDIR /* SFRMDIR = 1; Slave mode */ 58 + | SSCR1_RWOT /* RWOT = 1; Receive without transmit mode */ 59 + | SSCR1_RxTresh(1) /* RFT = 0; Receive FIFO threshold = 1 */ 60 + | SSCR1_SPH /* SPH = 1; SSPSCLK inactive 0.5 + 1 cycles */ 61 + | SSCR1_RIE /* RIE = 1; Receive FIFO interrupt enabled */ 62 + ; 63 + static const u32 sssr = 0 64 + | SSSR_BCE /* BCE = 1; Clear BCE */ 65 + | SSSR_TUR /* TUR = 1; Clear TUR */ 66 + | SSSR_EOC /* EOC = 1; Clear EOC */ 67 + | SSSR_TINT /* TINT = 1; Clear TINT */ 68 + | SSSR_PINT /* PINT = 1; Clear PINT */ 69 + | SSSR_ROR /* ROR = 1; Clear ROR */ 70 + ; 71 + 72 + /* 73 + * MEP Query $22: Touchpad Coordinate Range Query is not supported by 74 + * the NavPoint module, so sampled values provide the default limits. 75 + */ 76 + #define NAVPOINT_X_MIN 1278 77 + #define NAVPOINT_X_MAX 5340 78 + #define NAVPOINT_Y_MIN 1572 79 + #define NAVPOINT_Y_MAX 4396 80 + #define NAVPOINT_PRESSURE_MIN 0 81 + #define NAVPOINT_PRESSURE_MAX 255 82 + 83 + static void navpoint_packet(struct navpoint *navpoint) 84 + { 85 + int finger; 86 + int gesture; 87 + int x, y, z; 88 + 89 + switch (navpoint->data[0]) { 90 + case 0xff: /* Garbage (packet?) between reset and Hello packet */ 91 + case 0x00: /* Module 0, NULL packet */ 92 + break; 93 + 94 + case 0x0e: /* Module 0, Absolute packet */ 95 + finger = (navpoint->data[1] & 0x01); 96 + gesture = (navpoint->data[1] & 0x02); 97 + x = ((navpoint->data[2] & 0x1f) << 8) | navpoint->data[3]; 98 + y = ((navpoint->data[4] & 0x1f) << 8) | navpoint->data[5]; 99 + z = navpoint->data[6]; 100 + input_report_key(navpoint->input, BTN_TOUCH, finger); 101 + input_report_abs(navpoint->input, ABS_X, x); 102 + input_report_abs(navpoint->input, ABS_Y, y); 103 + input_report_abs(navpoint->input, ABS_PRESSURE, z); 104 + input_report_key(navpoint->input, BTN_TOOL_FINGER, finger); 105 + input_report_key(navpoint->input, BTN_LEFT, gesture); 106 + input_sync(navpoint->input); 107 + break; 108 + 109 + case 0x19: /* Module 0, Hello packet */ 110 + if ((navpoint->data[1] & 0xf0) == 0x10) 111 + break; 112 + /* FALLTHROUGH */ 113 + default: 114 + dev_warn(navpoint->dev, 115 + "spurious packet: data=0x%02x,0x%02x,...\n", 116 + navpoint->data[0], navpoint->data[1]); 117 + break; 118 + } 119 + } 120 + 121 + static irqreturn_t navpoint_irq(int irq, void *dev_id) 122 + { 123 + struct navpoint *navpoint = dev_id; 124 + struct ssp_device *ssp = navpoint->ssp; 125 + irqreturn_t ret = IRQ_NONE; 126 + u32 status; 127 + 128 + status = pxa_ssp_read_reg(ssp, SSSR); 129 + if (status & sssr) { 130 + dev_warn(navpoint->dev, 131 + "unexpected interrupt: status=0x%08x\n", status); 132 + pxa_ssp_write_reg(ssp, SSSR, (status & sssr)); 133 + ret = IRQ_HANDLED; 134 + } 135 + 136 + while (status & SSSR_RNE) { 137 + u32 data; 138 + 139 + data = pxa_ssp_read_reg(ssp, SSDR); 140 + navpoint->data[navpoint->index + 0] = (data >> 8); 141 + navpoint->data[navpoint->index + 1] = data; 142 + navpoint->index += 2; 143 + if (HEADER_LENGTH(navpoint->data[0]) < navpoint->index) { 144 + navpoint_packet(navpoint); 145 + navpoint->index = 0; 146 + } 147 + status = pxa_ssp_read_reg(ssp, SSSR); 148 + ret = IRQ_HANDLED; 149 + } 150 + 151 + return ret; 152 + } 153 + 154 + static void navpoint_up(struct navpoint *navpoint) 155 + { 156 + struct ssp_device *ssp = navpoint->ssp; 157 + int timeout; 158 + 159 + clk_prepare_enable(ssp->clk); 160 + 161 + pxa_ssp_write_reg(ssp, SSCR1, sscr1); 162 + pxa_ssp_write_reg(ssp, SSSR, sssr); 163 + pxa_ssp_write_reg(ssp, SSTO, 0); 164 + pxa_ssp_write_reg(ssp, SSCR0, sscr0); /* SSCR0_SSE written last */ 165 + 166 + /* Wait until SSP port is ready for slave clock operations */ 167 + for (timeout = 100; timeout != 0; --timeout) { 168 + if (!(pxa_ssp_read_reg(ssp, SSSR) & SSSR_CSS)) 169 + break; 170 + msleep(1); 171 + } 172 + 173 + if (timeout == 0) 174 + dev_err(navpoint->dev, 175 + "timeout waiting for SSSR[CSS] to clear\n"); 176 + 177 + if (gpio_is_valid(navpoint->gpio)) 178 + gpio_set_value(navpoint->gpio, 1); 179 + } 180 + 181 + static void navpoint_down(struct navpoint *navpoint) 182 + { 183 + struct ssp_device *ssp = navpoint->ssp; 184 + 185 + if (gpio_is_valid(navpoint->gpio)) 186 + gpio_set_value(navpoint->gpio, 0); 187 + 188 + pxa_ssp_write_reg(ssp, SSCR0, 0); 189 + 190 + clk_disable_unprepare(ssp->clk); 191 + } 192 + 193 + static int navpoint_open(struct input_dev *input) 194 + { 195 + struct navpoint *navpoint = input_get_drvdata(input); 196 + 197 + navpoint_up(navpoint); 198 + 199 + return 0; 200 + } 201 + 202 + static void navpoint_close(struct input_dev *input) 203 + { 204 + struct navpoint *navpoint = input_get_drvdata(input); 205 + 206 + navpoint_down(navpoint); 207 + } 208 + 209 + static int __devinit navpoint_probe(struct platform_device *pdev) 210 + { 211 + const struct navpoint_platform_data *pdata = 212 + dev_get_platdata(&pdev->dev); 213 + struct ssp_device *ssp; 214 + struct input_dev *input; 215 + struct navpoint *navpoint; 216 + int error; 217 + 218 + if (!pdata) { 219 + dev_err(&pdev->dev, "no platform data\n"); 220 + return -EINVAL; 221 + } 222 + 223 + if (gpio_is_valid(pdata->gpio)) { 224 + error = gpio_request_one(pdata->gpio, GPIOF_OUT_INIT_LOW, 225 + "SYNAPTICS_ON"); 226 + if (error) 227 + return error; 228 + } 229 + 230 + ssp = pxa_ssp_request(pdata->port, pdev->name); 231 + if (!ssp) { 232 + error = -ENODEV; 233 + goto err_free_gpio; 234 + } 235 + 236 + /* HaRET does not disable devices before jumping into Linux */ 237 + if (pxa_ssp_read_reg(ssp, SSCR0) & SSCR0_SSE) { 238 + pxa_ssp_write_reg(ssp, SSCR0, 0); 239 + dev_warn(&pdev->dev, "ssp%d already enabled\n", pdata->port); 240 + } 241 + 242 + navpoint = kzalloc(sizeof(*navpoint), GFP_KERNEL); 243 + input = input_allocate_device(); 244 + if (!navpoint || !input) { 245 + error = -ENOMEM; 246 + goto err_free_mem; 247 + } 248 + 249 + navpoint->ssp = ssp; 250 + navpoint->input = input; 251 + navpoint->dev = &pdev->dev; 252 + navpoint->gpio = pdata->gpio; 253 + 254 + input->name = pdev->name; 255 + input->dev.parent = &pdev->dev; 256 + 257 + __set_bit(EV_KEY, input->evbit); 258 + __set_bit(EV_ABS, input->evbit); 259 + __set_bit(BTN_LEFT, input->keybit); 260 + __set_bit(BTN_TOUCH, input->keybit); 261 + __set_bit(BTN_TOOL_FINGER, input->keybit); 262 + 263 + input_set_abs_params(input, ABS_X, 264 + NAVPOINT_X_MIN, NAVPOINT_X_MAX, 0, 0); 265 + input_set_abs_params(input, ABS_Y, 266 + NAVPOINT_Y_MIN, NAVPOINT_Y_MAX, 0, 0); 267 + input_set_abs_params(input, ABS_PRESSURE, 268 + NAVPOINT_PRESSURE_MIN, NAVPOINT_PRESSURE_MAX, 269 + 0, 0); 270 + 271 + input->open = navpoint_open; 272 + input->close = navpoint_close; 273 + 274 + input_set_drvdata(input, navpoint); 275 + 276 + error = request_irq(ssp->irq, navpoint_irq, 0, pdev->name, navpoint); 277 + if (error) 278 + goto err_free_mem; 279 + 280 + error = input_register_device(input); 281 + if (error) 282 + goto err_free_irq; 283 + 284 + platform_set_drvdata(pdev, navpoint); 285 + dev_dbg(&pdev->dev, "ssp%d, irq %d\n", pdata->port, ssp->irq); 286 + 287 + return 0; 288 + 289 + err_free_irq: 290 + free_irq(ssp->irq, &pdev->dev); 291 + err_free_mem: 292 + input_free_device(input); 293 + kfree(navpoint); 294 + pxa_ssp_free(ssp); 295 + err_free_gpio: 296 + if (gpio_is_valid(pdata->gpio)) 297 + gpio_free(pdata->gpio); 298 + 299 + return error; 300 + } 301 + 302 + static int __devexit navpoint_remove(struct platform_device *pdev) 303 + { 304 + const struct navpoint_platform_data *pdata = 305 + dev_get_platdata(&pdev->dev); 306 + struct navpoint *navpoint = platform_get_drvdata(pdev); 307 + struct ssp_device *ssp = navpoint->ssp; 308 + 309 + free_irq(ssp->irq, navpoint); 310 + 311 + input_unregister_device(navpoint->input); 312 + kfree(navpoint); 313 + 314 + pxa_ssp_free(ssp); 315 + 316 + if (gpio_is_valid(pdata->gpio)) 317 + gpio_free(pdata->gpio); 318 + 319 + return 0; 320 + } 321 + 322 + #ifdef CONFIG_PM_SLEEP 323 + static int navpoint_suspend(struct device *dev) 324 + { 325 + struct platform_device *pdev = to_platform_device(dev); 326 + struct navpoint *navpoint = platform_get_drvdata(pdev); 327 + struct input_dev *input = navpoint->input; 328 + 329 + mutex_lock(&input->mutex); 330 + if (input->users) 331 + navpoint_down(navpoint); 332 + mutex_unlock(&input->mutex); 333 + 334 + return 0; 335 + } 336 + 337 + static int navpoint_resume(struct device *dev) 338 + { 339 + struct platform_device *pdev = to_platform_device(dev); 340 + struct navpoint *navpoint = platform_get_drvdata(pdev); 341 + struct input_dev *input = navpoint->input; 342 + 343 + mutex_lock(&input->mutex); 344 + if (input->users) 345 + navpoint_up(navpoint); 346 + mutex_unlock(&input->mutex); 347 + 348 + return 0; 349 + } 350 + #endif 351 + 352 + static SIMPLE_DEV_PM_OPS(navpoint_pm_ops, navpoint_suspend, navpoint_resume); 353 + 354 + static struct platform_driver navpoint_driver = { 355 + .probe = navpoint_probe, 356 + .remove = __devexit_p(navpoint_remove), 357 + .driver = { 358 + .name = "navpoint", 359 + .owner = THIS_MODULE, 360 + .pm = &navpoint_pm_ops, 361 + }, 362 + }; 363 + 364 + module_platform_driver(navpoint_driver); 365 + 366 + MODULE_AUTHOR("Paul Parsons <lost.distance@yahoo.com>"); 367 + MODULE_DESCRIPTION("Synaptics NavPoint (PXA27x SSP/SPI) driver"); 368 + MODULE_LICENSE("GPL"); 369 + MODULE_ALIAS("platform:navpoint");
+12
include/linux/input/navpoint.h
··· 1 + /* 2 + * Copyright (C) 2012 Paul Parsons <lost.distance@yahoo.com> 3 + * 4 + * This program is free software; you can redistribute it and/or modify 5 + * it under the terms of the GNU General Public License version 2 as 6 + * published by the Free Software Foundation. 7 + */ 8 + 9 + struct navpoint_platform_data { 10 + int port; /* PXA SSP port for pxa_ssp_request() */ 11 + int gpio; /* GPIO for power on/off */ 12 + };