1 /* 2 * rotary_encoder.c 3 * 4 * (c) 2009 Daniel Mack <daniel@caiaq.de> 5 * 6 * state machine code inspired by code from Tim Ruetz 7 * 8 * A generic driver for rotary encoders connected to GPIO lines. 9 * See file:Documentation/input/rotary_encoder.txt for more information 10 * 11 * This program is free software; you can redistribute it and/or modify 12 * it under the terms of the GNU General Public License version 2 as 13 * published by the Free Software Foundation. 14 */ 15 16 #include <linux/kernel.h> 17 #include <linux/module.h> 18 #include <linux/init.h> 19 #include <linux/interrupt.h> 20 #include <linux/input.h> 21 #include <linux/device.h> 22 #include <linux/platform_device.h> 23 #include <linux/gpio.h> 24 #include <linux/rotary_encoder.h> 25 26 #define DRV_NAME "rotary-encoder" 27 28 struct rotary_encoder { 29 struct input_dev *input; 30 struct rotary_encoder_platform_data *pdata; 31 32 unsigned int axis; 33 unsigned int pos; 34 35 unsigned int irq_a; 36 unsigned int irq_b; 37 38 bool armed; 39 unsigned char dir; /* 0 - clockwise, 1 - CCW */ 40 }; 41 42 static irqreturn_t rotary_encoder_irq(int irq, void *dev_id) 43 { 44 struct rotary_encoder *encoder = dev_id; 45 struct rotary_encoder_platform_data *pdata = encoder->pdata; 46 int a = !!gpio_get_value(pdata->gpio_a); 47 int b = !!gpio_get_value(pdata->gpio_b); 48 int state; 49 50 a ^= pdata->inverted_a; 51 b ^= pdata->inverted_b; 52 state = (a << 1) | b; 53 54 switch (state) { 55 56 case 0x0: 57 if (!encoder->armed) 58 break; 59 60 if (pdata->relative_axis) { 61 input_report_rel(encoder->input, pdata->axis, 62 encoder->dir ? -1 : 1); 63 } else { 64 unsigned int pos = encoder->pos; 65 66 if (encoder->dir) { 67 /* turning counter-clockwise */ 68 if (pdata->rollover) 69 pos += pdata->steps; 70 if (pos) 71 pos--; 72 } else { 73 /* turning clockwise */ 74 if (pdata->rollover || pos < pdata->steps) 75 pos++; 76 } 77 if (pdata->rollover) 78 pos %= pdata->steps; 79 encoder->pos = pos; 80 input_report_abs(encoder->input, pdata->axis, 81 encoder->pos); 82 } 83 input_sync(encoder->input); 84 85 encoder->armed = false; 86 break; 87 88 case 0x1: 89 case 0x2: 90 if (encoder->armed) 91 encoder->dir = state - 1; 92 break; 93 94 case 0x3: 95 encoder->armed = true; 96 break; 97 } 98 99 return IRQ_HANDLED; 100 } 101 102 static int __devinit rotary_encoder_probe(struct platform_device *pdev) 103 { 104 struct rotary_encoder_platform_data *pdata = pdev->dev.platform_data; 105 struct rotary_encoder *encoder; 106 struct input_dev *input; 107 int err; 108 109 if (!pdata) { 110 dev_err(&pdev->dev, "missing platform data\n"); 111 return -ENOENT; 112 } 113 114 encoder = kzalloc(sizeof(struct rotary_encoder), GFP_KERNEL); 115 input = input_allocate_device(); 116 if (!encoder || !input) { 117 dev_err(&pdev->dev, "failed to allocate memory for device\n"); 118 err = -ENOMEM; 119 goto exit_free_mem; 120 } 121 122 encoder->input = input; 123 encoder->pdata = pdata; 124 encoder->irq_a = gpio_to_irq(pdata->gpio_a); 125 encoder->irq_b = gpio_to_irq(pdata->gpio_b); 126 127 /* create and register the input driver */ 128 input->name = pdev->name; 129 input->id.bustype = BUS_HOST; 130 input->dev.parent = &pdev->dev; 131 132 if (pdata->relative_axis) { 133 input->evbit[0] = BIT_MASK(EV_REL); 134 input->relbit[0] = BIT_MASK(pdata->axis); 135 } else { 136 input->evbit[0] = BIT_MASK(EV_ABS); 137 input_set_abs_params(encoder->input, 138 pdata->axis, 0, pdata->steps, 0, 1); 139 } 140 141 err = input_register_device(input); 142 if (err) { 143 dev_err(&pdev->dev, "failed to register input device\n"); 144 goto exit_free_mem; 145 } 146 147 /* request the GPIOs */ 148 err = gpio_request(pdata->gpio_a, DRV_NAME); 149 if (err) { 150 dev_err(&pdev->dev, "unable to request GPIO %d\n", 151 pdata->gpio_a); 152 goto exit_unregister_input; 153 } 154 155 err = gpio_request(pdata->gpio_b, DRV_NAME); 156 if (err) { 157 dev_err(&pdev->dev, "unable to request GPIO %d\n", 158 pdata->gpio_b); 159 goto exit_free_gpio_a; 160 } 161 162 /* request the IRQs */ 163 err = request_irq(encoder->irq_a, &rotary_encoder_irq, 164 IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_LOWEDGE, 165 DRV_NAME, encoder); 166 if (err) { 167 dev_err(&pdev->dev, "unable to request IRQ %d\n", 168 encoder->irq_a); 169 goto exit_free_gpio_b; 170 } 171 172 err = request_irq(encoder->irq_b, &rotary_encoder_irq, 173 IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_LOWEDGE, 174 DRV_NAME, encoder); 175 if (err) { 176 dev_err(&pdev->dev, "unable to request IRQ %d\n", 177 encoder->irq_b); 178 goto exit_free_irq_a; 179 } 180 181 platform_set_drvdata(pdev, encoder); 182 183 return 0; 184 185 exit_free_irq_a: 186 free_irq(encoder->irq_a, encoder); 187 exit_free_gpio_b: 188 gpio_free(pdata->gpio_b); 189 exit_free_gpio_a: 190 gpio_free(pdata->gpio_a); 191 exit_unregister_input: 192 input_unregister_device(input); 193 input = NULL; /* so we don't try to free it */ 194 exit_free_mem: 195 input_free_device(input); 196 kfree(encoder); 197 return err; 198 } 199 200 static int __devexit rotary_encoder_remove(struct platform_device *pdev) 201 { 202 struct rotary_encoder *encoder = platform_get_drvdata(pdev); 203 struct rotary_encoder_platform_data *pdata = pdev->dev.platform_data; 204 205 free_irq(encoder->irq_a, encoder); 206 free_irq(encoder->irq_b, encoder); 207 gpio_free(pdata->gpio_a); 208 gpio_free(pdata->gpio_b); 209 input_unregister_device(encoder->input); 210 platform_set_drvdata(pdev, NULL); 211 kfree(encoder); 212 213 return 0; 214 } 215 216 static struct platform_driver rotary_encoder_driver = { 217 .probe = rotary_encoder_probe, 218 .remove = __devexit_p(rotary_encoder_remove), 219 .driver = { 220 .name = DRV_NAME, 221 .owner = THIS_MODULE, 222 } 223 }; 224 225 static int __init rotary_encoder_init(void) 226 { 227 return platform_driver_register(&rotary_encoder_driver); 228 } 229 230 static void __exit rotary_encoder_exit(void) 231 { 232 platform_driver_unregister(&rotary_encoder_driver); 233 } 234 235 module_init(rotary_encoder_init); 236 module_exit(rotary_encoder_exit); 237 238 MODULE_ALIAS("platform:" DRV_NAME); 239 MODULE_DESCRIPTION("GPIO rotary encoder driver"); 240 MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>"); 241 MODULE_LICENSE("GPL v2"); 242 243