173969ff0SDaniel Mack /* 273969ff0SDaniel Mack * rotary_encoder.c 373969ff0SDaniel Mack * 473969ff0SDaniel Mack * (c) 2009 Daniel Mack <daniel@caiaq.de> 5e70bdd41SJohan Hovold * Copyright (C) 2011 Johan Hovold <jhovold@gmail.com> 673969ff0SDaniel Mack * 773969ff0SDaniel Mack * state machine code inspired by code from Tim Ruetz 873969ff0SDaniel Mack * 973969ff0SDaniel Mack * A generic driver for rotary encoders connected to GPIO lines. 10395cf969SPaul Bolle * See file:Documentation/input/rotary-encoder.txt for more information 1173969ff0SDaniel Mack * 1273969ff0SDaniel Mack * This program is free software; you can redistribute it and/or modify 1373969ff0SDaniel Mack * it under the terms of the GNU General Public License version 2 as 1473969ff0SDaniel Mack * published by the Free Software Foundation. 1573969ff0SDaniel Mack */ 1673969ff0SDaniel Mack 1773969ff0SDaniel Mack #include <linux/kernel.h> 1873969ff0SDaniel Mack #include <linux/module.h> 1973969ff0SDaniel Mack #include <linux/interrupt.h> 2073969ff0SDaniel Mack #include <linux/input.h> 2173969ff0SDaniel Mack #include <linux/device.h> 2273969ff0SDaniel Mack #include <linux/platform_device.h> 2377a8f0adSDmitry Torokhov #include <linux/gpio/consumer.h> 245a0e3ad6STejun Heo #include <linux/slab.h> 252e45e539SSachin Kamat #include <linux/of.h> 2647ec6e5aSSylvain Rochet #include <linux/pm.h> 27a9e340dcSDmitry Torokhov #include <linux/property.h> 2873969ff0SDaniel Mack 2973969ff0SDaniel Mack #define DRV_NAME "rotary-encoder" 3073969ff0SDaniel Mack 31d205a218SUwe Kleine-König enum rotary_encoder_encoding { 32d205a218SUwe Kleine-König ROTENC_GRAY, 33d205a218SUwe Kleine-König ROTENC_BINARY, 34d205a218SUwe Kleine-König }; 35d205a218SUwe Kleine-König 3673969ff0SDaniel Mack struct rotary_encoder { 3773969ff0SDaniel Mack struct input_dev *input; 38a9e340dcSDmitry Torokhov 39dee520e3STimo Teräs struct mutex access_mutex; 40bd3ce655SH Hartley Sweeten 41a9e340dcSDmitry Torokhov u32 steps; 42a9e340dcSDmitry Torokhov u32 axis; 43a9e340dcSDmitry Torokhov bool relative_axis; 44a9e340dcSDmitry Torokhov bool rollover; 45d205a218SUwe Kleine-König enum rotary_encoder_encoding encoding; 46a9e340dcSDmitry Torokhov 47bd3ce655SH Hartley Sweeten unsigned int pos; 48bd3ce655SH Hartley Sweeten 497dde4e74SUwe Kleine-König struct gpio_descs *gpios; 5077a8f0adSDmitry Torokhov 517dde4e74SUwe Kleine-König unsigned int *irq; 52bd3ce655SH Hartley Sweeten 53bd3ce655SH Hartley Sweeten bool armed; 547dde4e74SUwe Kleine-König signed char dir; /* 1 - clockwise, -1 - CCW */ 55e70bdd41SJohan Hovold 56d96caf8cSClifton Barnes unsigned int last_stable; 5773969ff0SDaniel Mack }; 5873969ff0SDaniel Mack 59d96caf8cSClifton Barnes static unsigned int rotary_encoder_get_state(struct rotary_encoder *encoder) 6073969ff0SDaniel Mack { 617dde4e74SUwe Kleine-König int i; 62d96caf8cSClifton Barnes unsigned int ret = 0; 6373969ff0SDaniel Mack 647dde4e74SUwe Kleine-König for (i = 0; i < encoder->gpios->ndescs; ++i) { 657dde4e74SUwe Kleine-König int val = gpiod_get_value_cansleep(encoder->gpios->desc[i]); 66d205a218SUwe Kleine-König 677dde4e74SUwe Kleine-König /* convert from gray encoding to normal */ 68d205a218SUwe Kleine-König if (encoder->encoding == ROTENC_GRAY && ret & 1) 697dde4e74SUwe Kleine-König val = !val; 707dde4e74SUwe Kleine-König 717dde4e74SUwe Kleine-König ret = ret << 1 | val; 727dde4e74SUwe Kleine-König } 737dde4e74SUwe Kleine-König 747dde4e74SUwe Kleine-König return ret & 3; 75521a8f5cSJohan Hovold } 7673969ff0SDaniel Mack 77521a8f5cSJohan Hovold static void rotary_encoder_report_event(struct rotary_encoder *encoder) 78521a8f5cSJohan Hovold { 79a9e340dcSDmitry Torokhov if (encoder->relative_axis) { 80521a8f5cSJohan Hovold input_report_rel(encoder->input, 817dde4e74SUwe Kleine-König encoder->axis, encoder->dir); 82bd3ce655SH Hartley Sweeten } else { 83bd3ce655SH Hartley Sweeten unsigned int pos = encoder->pos; 84bd3ce655SH Hartley Sweeten 857dde4e74SUwe Kleine-König if (encoder->dir < 0) { 8673969ff0SDaniel Mack /* turning counter-clockwise */ 87a9e340dcSDmitry Torokhov if (encoder->rollover) 88a9e340dcSDmitry Torokhov pos += encoder->steps; 89bd3ce655SH Hartley Sweeten if (pos) 90bd3ce655SH Hartley Sweeten pos--; 9173969ff0SDaniel Mack } else { 9273969ff0SDaniel Mack /* turning clockwise */ 93a9e340dcSDmitry Torokhov if (encoder->rollover || pos < encoder->steps) 94bd3ce655SH Hartley Sweeten pos++; 9573969ff0SDaniel Mack } 96521a8f5cSJohan Hovold 97a9e340dcSDmitry Torokhov if (encoder->rollover) 98a9e340dcSDmitry Torokhov pos %= encoder->steps; 9973969ff0SDaniel Mack 100521a8f5cSJohan Hovold encoder->pos = pos; 101a9e340dcSDmitry Torokhov input_report_abs(encoder->input, encoder->axis, encoder->pos); 102521a8f5cSJohan Hovold } 103521a8f5cSJohan Hovold 104521a8f5cSJohan Hovold input_sync(encoder->input); 105521a8f5cSJohan Hovold } 106521a8f5cSJohan Hovold 107521a8f5cSJohan Hovold static irqreturn_t rotary_encoder_irq(int irq, void *dev_id) 108521a8f5cSJohan Hovold { 109521a8f5cSJohan Hovold struct rotary_encoder *encoder = dev_id; 110d96caf8cSClifton Barnes unsigned int state; 111521a8f5cSJohan Hovold 112dee520e3STimo Teräs mutex_lock(&encoder->access_mutex); 113dee520e3STimo Teräs 11477a8f0adSDmitry Torokhov state = rotary_encoder_get_state(encoder); 115521a8f5cSJohan Hovold 116521a8f5cSJohan Hovold switch (state) { 117521a8f5cSJohan Hovold case 0x0: 118521a8f5cSJohan Hovold if (encoder->armed) { 119521a8f5cSJohan Hovold rotary_encoder_report_event(encoder); 120bd3ce655SH Hartley Sweeten encoder->armed = false; 121521a8f5cSJohan Hovold } 12273969ff0SDaniel Mack break; 12373969ff0SDaniel Mack 12473969ff0SDaniel Mack case 0x1: 1257dde4e74SUwe Kleine-König case 0x3: 12673969ff0SDaniel Mack if (encoder->armed) 1277dde4e74SUwe Kleine-König encoder->dir = 2 - state; 12873969ff0SDaniel Mack break; 12973969ff0SDaniel Mack 1307dde4e74SUwe Kleine-König case 0x2: 131bd3ce655SH Hartley Sweeten encoder->armed = true; 13273969ff0SDaniel Mack break; 13373969ff0SDaniel Mack } 13473969ff0SDaniel Mack 135dee520e3STimo Teräs mutex_unlock(&encoder->access_mutex); 136dee520e3STimo Teräs 13773969ff0SDaniel Mack return IRQ_HANDLED; 13873969ff0SDaniel Mack } 13973969ff0SDaniel Mack 140e70bdd41SJohan Hovold static irqreturn_t rotary_encoder_half_period_irq(int irq, void *dev_id) 141e70bdd41SJohan Hovold { 142e70bdd41SJohan Hovold struct rotary_encoder *encoder = dev_id; 1437dde4e74SUwe Kleine-König unsigned int state; 144e70bdd41SJohan Hovold 145dee520e3STimo Teräs mutex_lock(&encoder->access_mutex); 146dee520e3STimo Teräs 14777a8f0adSDmitry Torokhov state = rotary_encoder_get_state(encoder); 148e70bdd41SJohan Hovold 1497dde4e74SUwe Kleine-König if (state & 1) { 1507dde4e74SUwe Kleine-König encoder->dir = ((encoder->last_stable - state + 1) % 4) - 1; 1517dde4e74SUwe Kleine-König } else { 152e70bdd41SJohan Hovold if (state != encoder->last_stable) { 153e70bdd41SJohan Hovold rotary_encoder_report_event(encoder); 154e70bdd41SJohan Hovold encoder->last_stable = state; 155e70bdd41SJohan Hovold } 156e70bdd41SJohan Hovold } 157e70bdd41SJohan Hovold 158dee520e3STimo Teräs mutex_unlock(&encoder->access_mutex); 159dee520e3STimo Teräs 160e70bdd41SJohan Hovold return IRQ_HANDLED; 161e70bdd41SJohan Hovold } 162e70bdd41SJohan Hovold 1633a341a4cSEzequiel Garcia static irqreturn_t rotary_encoder_quarter_period_irq(int irq, void *dev_id) 1643a341a4cSEzequiel Garcia { 1653a341a4cSEzequiel Garcia struct rotary_encoder *encoder = dev_id; 1667dde4e74SUwe Kleine-König unsigned int state; 1673a341a4cSEzequiel Garcia 168dee520e3STimo Teräs mutex_lock(&encoder->access_mutex); 169dee520e3STimo Teräs 17077a8f0adSDmitry Torokhov state = rotary_encoder_get_state(encoder); 1713a341a4cSEzequiel Garcia 1727dde4e74SUwe Kleine-König if ((encoder->last_stable + 1) % 4 == state) 1737dde4e74SUwe Kleine-König encoder->dir = 1; 1747dde4e74SUwe Kleine-König else if (encoder->last_stable == (state + 1) % 4) 1757dde4e74SUwe Kleine-König encoder->dir = -1; 1767dde4e74SUwe Kleine-König else 1773a341a4cSEzequiel Garcia goto out; 1783a341a4cSEzequiel Garcia 1793a341a4cSEzequiel Garcia rotary_encoder_report_event(encoder); 1803a341a4cSEzequiel Garcia 1813a341a4cSEzequiel Garcia out: 1823a341a4cSEzequiel Garcia encoder->last_stable = state; 183dee520e3STimo Teräs mutex_unlock(&encoder->access_mutex); 184dee520e3STimo Teräs 1853a341a4cSEzequiel Garcia return IRQ_HANDLED; 1863a341a4cSEzequiel Garcia } 1873a341a4cSEzequiel Garcia 1885298cc4cSBill Pemberton static int rotary_encoder_probe(struct platform_device *pdev) 18973969ff0SDaniel Mack { 190ce919537SDmitry Torokhov struct device *dev = &pdev->dev; 19173969ff0SDaniel Mack struct rotary_encoder *encoder; 19273969ff0SDaniel Mack struct input_dev *input; 193e70bdd41SJohan Hovold irq_handler_t handler; 194a9e340dcSDmitry Torokhov u32 steps_per_period; 1957dde4e74SUwe Kleine-König unsigned int i; 19673969ff0SDaniel Mack int err; 19773969ff0SDaniel Mack 198d9202af2STimo Teräs encoder = devm_kzalloc(dev, sizeof(struct rotary_encoder), GFP_KERNEL); 199d9202af2STimo Teräs if (!encoder) 200d9202af2STimo Teräs return -ENOMEM; 201d9202af2STimo Teräs 20277a8f0adSDmitry Torokhov mutex_init(&encoder->access_mutex); 203a9e340dcSDmitry Torokhov 204a9e340dcSDmitry Torokhov device_property_read_u32(dev, "rotary-encoder,steps", &encoder->steps); 205a9e340dcSDmitry Torokhov 206a9e340dcSDmitry Torokhov err = device_property_read_u32(dev, "rotary-encoder,steps-per-period", 207a9e340dcSDmitry Torokhov &steps_per_period); 208a9e340dcSDmitry Torokhov if (err) { 209a9e340dcSDmitry Torokhov /* 210a9e340dcSDmitry Torokhov * The 'half-period' property has been deprecated, you must 211a9e340dcSDmitry Torokhov * use 'steps-per-period' and set an appropriate value, but 212a9e340dcSDmitry Torokhov * we still need to parse it to maintain compatibility. If 213a9e340dcSDmitry Torokhov * neither property is present we fall back to the one step 214a9e340dcSDmitry Torokhov * per period behavior. 215a9e340dcSDmitry Torokhov */ 216a9e340dcSDmitry Torokhov steps_per_period = device_property_read_bool(dev, 217a9e340dcSDmitry Torokhov "rotary-encoder,half-period") ? 2 : 1; 218a9e340dcSDmitry Torokhov } 219a9e340dcSDmitry Torokhov 220a9e340dcSDmitry Torokhov encoder->rollover = 221a9e340dcSDmitry Torokhov device_property_read_bool(dev, "rotary-encoder,rollover"); 222a9e340dcSDmitry Torokhov 223d205a218SUwe Kleine-König if (!device_property_present(dev, "rotary-encoder,encoding") || 224d205a218SUwe Kleine-König !device_property_match_string(dev, "rotary-encoder,encoding", 225d205a218SUwe Kleine-König "gray")) { 226d205a218SUwe Kleine-König dev_info(dev, "gray"); 227d205a218SUwe Kleine-König encoder->encoding = ROTENC_GRAY; 228d205a218SUwe Kleine-König } else if (!device_property_match_string(dev, "rotary-encoder,encoding", 229d205a218SUwe Kleine-König "binary")) { 230d205a218SUwe Kleine-König dev_info(dev, "binary"); 231d205a218SUwe Kleine-König encoder->encoding = ROTENC_BINARY; 232d205a218SUwe Kleine-König } else { 233d205a218SUwe Kleine-König dev_err(dev, "unknown encoding setting\n"); 234d205a218SUwe Kleine-König return -EINVAL; 235d205a218SUwe Kleine-König } 236d205a218SUwe Kleine-König 237a9e340dcSDmitry Torokhov device_property_read_u32(dev, "linux,axis", &encoder->axis); 238a9e340dcSDmitry Torokhov encoder->relative_axis = 239a9e340dcSDmitry Torokhov device_property_read_bool(dev, "rotary-encoder,relative-axis"); 24077a8f0adSDmitry Torokhov 2417dde4e74SUwe Kleine-König encoder->gpios = devm_gpiod_get_array(dev, NULL, GPIOD_IN); 2427dde4e74SUwe Kleine-König if (IS_ERR(encoder->gpios)) { 2437dde4e74SUwe Kleine-König dev_err(dev, "unable to get gpios\n"); 2447dde4e74SUwe Kleine-König return PTR_ERR(encoder->gpios); 24577a8f0adSDmitry Torokhov } 2467dde4e74SUwe Kleine-König if (encoder->gpios->ndescs < 2) { 2477dde4e74SUwe Kleine-König dev_err(dev, "not enough gpios found\n"); 2487dde4e74SUwe Kleine-König return -EINVAL; 24977a8f0adSDmitry Torokhov } 25077a8f0adSDmitry Torokhov 251d9202af2STimo Teräs input = devm_input_allocate_device(dev); 252d9202af2STimo Teräs if (!input) 253d9202af2STimo Teräs return -ENOMEM; 25473969ff0SDaniel Mack 25573969ff0SDaniel Mack encoder->input = input; 25673969ff0SDaniel Mack 25773969ff0SDaniel Mack input->name = pdev->name; 25873969ff0SDaniel Mack input->id.bustype = BUS_HOST; 25980c99bcdSDaniel Mack input->dev.parent = dev; 260bd3ce655SH Hartley Sweeten 261a9e340dcSDmitry Torokhov if (encoder->relative_axis) 262a9e340dcSDmitry Torokhov input_set_capability(input, EV_REL, encoder->axis); 2638631580fSDmitry Torokhov else 264a9e340dcSDmitry Torokhov input_set_abs_params(input, 265a9e340dcSDmitry Torokhov encoder->axis, 0, encoder->steps, 0, 1); 26673969ff0SDaniel Mack 2677dde4e74SUwe Kleine-König switch (steps_per_period >> (encoder->gpios->ndescs - 2)) { 2683a341a4cSEzequiel Garcia case 4: 2693a341a4cSEzequiel Garcia handler = &rotary_encoder_quarter_period_irq; 27077a8f0adSDmitry Torokhov encoder->last_stable = rotary_encoder_get_state(encoder); 2713a341a4cSEzequiel Garcia break; 2723a341a4cSEzequiel Garcia case 2: 273e70bdd41SJohan Hovold handler = &rotary_encoder_half_period_irq; 27477a8f0adSDmitry Torokhov encoder->last_stable = rotary_encoder_get_state(encoder); 2753a341a4cSEzequiel Garcia break; 2763a341a4cSEzequiel Garcia case 1: 277e70bdd41SJohan Hovold handler = &rotary_encoder_irq; 2783a341a4cSEzequiel Garcia break; 2793a341a4cSEzequiel Garcia default: 2803a341a4cSEzequiel Garcia dev_err(dev, "'%d' is not a valid steps-per-period value\n", 281a9e340dcSDmitry Torokhov steps_per_period); 282d9202af2STimo Teräs return -EINVAL; 283e70bdd41SJohan Hovold } 284e70bdd41SJohan Hovold 2857dde4e74SUwe Kleine-König encoder->irq = 286a86854d0SKees Cook devm_kcalloc(dev, 287a86854d0SKees Cook encoder->gpios->ndescs, sizeof(*encoder->irq), 2887dde4e74SUwe Kleine-König GFP_KERNEL); 2897dde4e74SUwe Kleine-König if (!encoder->irq) 2907dde4e74SUwe Kleine-König return -ENOMEM; 2917dde4e74SUwe Kleine-König 2927dde4e74SUwe Kleine-König for (i = 0; i < encoder->gpios->ndescs; ++i) { 2937dde4e74SUwe Kleine-König encoder->irq[i] = gpiod_to_irq(encoder->gpios->desc[i]); 2947dde4e74SUwe Kleine-König 2957dde4e74SUwe Kleine-König err = devm_request_threaded_irq(dev, encoder->irq[i], 2967dde4e74SUwe Kleine-König NULL, handler, 297dee520e3STimo Teräs IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | 298dee520e3STimo Teräs IRQF_ONESHOT, 29973969ff0SDaniel Mack DRV_NAME, encoder); 30073969ff0SDaniel Mack if (err) { 3017dde4e74SUwe Kleine-König dev_err(dev, "unable to request IRQ %d (gpio#%d)\n", 3027dde4e74SUwe Kleine-König encoder->irq[i], i); 303d9202af2STimo Teräs return err; 30473969ff0SDaniel Mack } 30573969ff0SDaniel Mack } 30673969ff0SDaniel Mack 30780c99bcdSDaniel Mack err = input_register_device(input); 30880c99bcdSDaniel Mack if (err) { 30980c99bcdSDaniel Mack dev_err(dev, "failed to register input device\n"); 310d9202af2STimo Teräs return err; 31180c99bcdSDaniel Mack } 31280c99bcdSDaniel Mack 313a9e340dcSDmitry Torokhov device_init_wakeup(dev, 314a9e340dcSDmitry Torokhov device_property_read_bool(dev, "wakeup-source")); 31547ec6e5aSSylvain Rochet 31673969ff0SDaniel Mack platform_set_drvdata(pdev, encoder); 31773969ff0SDaniel Mack 31873969ff0SDaniel Mack return 0; 31973969ff0SDaniel Mack } 32073969ff0SDaniel Mack 3216a6f70b3SDmitry Torokhov static int __maybe_unused rotary_encoder_suspend(struct device *dev) 32247ec6e5aSSylvain Rochet { 32347ec6e5aSSylvain Rochet struct rotary_encoder *encoder = dev_get_drvdata(dev); 3247dde4e74SUwe Kleine-König unsigned int i; 32547ec6e5aSSylvain Rochet 32647ec6e5aSSylvain Rochet if (device_may_wakeup(dev)) { 3277dde4e74SUwe Kleine-König for (i = 0; i < encoder->gpios->ndescs; ++i) 3287dde4e74SUwe Kleine-König enable_irq_wake(encoder->irq[i]); 32947ec6e5aSSylvain Rochet } 33047ec6e5aSSylvain Rochet 33147ec6e5aSSylvain Rochet return 0; 33247ec6e5aSSylvain Rochet } 33347ec6e5aSSylvain Rochet 3346a6f70b3SDmitry Torokhov static int __maybe_unused rotary_encoder_resume(struct device *dev) 33547ec6e5aSSylvain Rochet { 33647ec6e5aSSylvain Rochet struct rotary_encoder *encoder = dev_get_drvdata(dev); 3377dde4e74SUwe Kleine-König unsigned int i; 33847ec6e5aSSylvain Rochet 33947ec6e5aSSylvain Rochet if (device_may_wakeup(dev)) { 3407dde4e74SUwe Kleine-König for (i = 0; i < encoder->gpios->ndescs; ++i) 3417dde4e74SUwe Kleine-König disable_irq_wake(encoder->irq[i]); 34247ec6e5aSSylvain Rochet } 34347ec6e5aSSylvain Rochet 34447ec6e5aSSylvain Rochet return 0; 34547ec6e5aSSylvain Rochet } 34647ec6e5aSSylvain Rochet 34747ec6e5aSSylvain Rochet static SIMPLE_DEV_PM_OPS(rotary_encoder_pm_ops, 34847ec6e5aSSylvain Rochet rotary_encoder_suspend, rotary_encoder_resume); 34947ec6e5aSSylvain Rochet 350a9e340dcSDmitry Torokhov #ifdef CONFIG_OF 351a9e340dcSDmitry Torokhov static const struct of_device_id rotary_encoder_of_match[] = { 352a9e340dcSDmitry Torokhov { .compatible = "rotary-encoder", }, 353a9e340dcSDmitry Torokhov { }, 354a9e340dcSDmitry Torokhov }; 355a9e340dcSDmitry Torokhov MODULE_DEVICE_TABLE(of, rotary_encoder_of_match); 356a9e340dcSDmitry Torokhov #endif 357a9e340dcSDmitry Torokhov 35873969ff0SDaniel Mack static struct platform_driver rotary_encoder_driver = { 35973969ff0SDaniel Mack .probe = rotary_encoder_probe, 36073969ff0SDaniel Mack .driver = { 36173969ff0SDaniel Mack .name = DRV_NAME, 36247ec6e5aSSylvain Rochet .pm = &rotary_encoder_pm_ops, 36380c99bcdSDaniel Mack .of_match_table = of_match_ptr(rotary_encoder_of_match), 36473969ff0SDaniel Mack } 36573969ff0SDaniel Mack }; 366840a746bSJJ Ding module_platform_driver(rotary_encoder_driver); 36773969ff0SDaniel Mack 36873969ff0SDaniel Mack MODULE_ALIAS("platform:" DRV_NAME); 36973969ff0SDaniel Mack MODULE_DESCRIPTION("GPIO rotary encoder driver"); 370e70bdd41SJohan Hovold MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>, Johan Hovold"); 37173969ff0SDaniel Mack MODULE_LICENSE("GPL v2"); 372