Path: blob/master/drivers/input/misc/rotary_encoder.c
15109 views
/*1* rotary_encoder.c2*3* (c) 2009 Daniel Mack <[email protected]>4* Copyright (C) 2011 Johan Hovold <[email protected]>5*6* state machine code inspired by code from Tim Ruetz7*8* A generic driver for rotary encoders connected to GPIO lines.9* See file:Documentation/input/rotary_encoder.txt for more information10*11* This program is free software; you can redistribute it and/or modify12* it under the terms of the GNU General Public License version 2 as13* published by the Free Software Foundation.14*/1516#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#include <linux/slab.h>2627#define DRV_NAME "rotary-encoder"2829struct rotary_encoder {30struct input_dev *input;31struct rotary_encoder_platform_data *pdata;3233unsigned int axis;34unsigned int pos;3536unsigned int irq_a;37unsigned int irq_b;3839bool armed;40unsigned char dir; /* 0 - clockwise, 1 - CCW */4142char last_stable;43};4445static int rotary_encoder_get_state(struct rotary_encoder_platform_data *pdata)46{47int a = !!gpio_get_value(pdata->gpio_a);48int b = !!gpio_get_value(pdata->gpio_b);4950a ^= pdata->inverted_a;51b ^= pdata->inverted_b;5253return ((a << 1) | b);54}5556static void rotary_encoder_report_event(struct rotary_encoder *encoder)57{58struct rotary_encoder_platform_data *pdata = encoder->pdata;5960if (pdata->relative_axis) {61input_report_rel(encoder->input,62pdata->axis, encoder->dir ? -1 : 1);63} else {64unsigned int pos = encoder->pos;6566if (encoder->dir) {67/* turning counter-clockwise */68if (pdata->rollover)69pos += pdata->steps;70if (pos)71pos--;72} else {73/* turning clockwise */74if (pdata->rollover || pos < pdata->steps)75pos++;76}7778if (pdata->rollover)79pos %= pdata->steps;8081encoder->pos = pos;82input_report_abs(encoder->input, pdata->axis, encoder->pos);83}8485input_sync(encoder->input);86}8788static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)89{90struct rotary_encoder *encoder = dev_id;91int state;9293state = rotary_encoder_get_state(encoder->pdata);9495switch (state) {96case 0x0:97if (encoder->armed) {98rotary_encoder_report_event(encoder);99encoder->armed = false;100}101break;102103case 0x1:104case 0x2:105if (encoder->armed)106encoder->dir = state - 1;107break;108109case 0x3:110encoder->armed = true;111break;112}113114return IRQ_HANDLED;115}116117static irqreturn_t rotary_encoder_half_period_irq(int irq, void *dev_id)118{119struct rotary_encoder *encoder = dev_id;120int state;121122state = rotary_encoder_get_state(encoder->pdata);123124switch (state) {125case 0x00:126case 0x03:127if (state != encoder->last_stable) {128rotary_encoder_report_event(encoder);129encoder->last_stable = state;130}131break;132133case 0x01:134case 0x02:135encoder->dir = (encoder->last_stable + state) & 0x01;136break;137}138139return IRQ_HANDLED;140}141142static int __devinit rotary_encoder_probe(struct platform_device *pdev)143{144struct rotary_encoder_platform_data *pdata = pdev->dev.platform_data;145struct rotary_encoder *encoder;146struct input_dev *input;147irq_handler_t handler;148int err;149150if (!pdata) {151dev_err(&pdev->dev, "missing platform data\n");152return -ENOENT;153}154155encoder = kzalloc(sizeof(struct rotary_encoder), GFP_KERNEL);156input = input_allocate_device();157if (!encoder || !input) {158dev_err(&pdev->dev, "failed to allocate memory for device\n");159err = -ENOMEM;160goto exit_free_mem;161}162163encoder->input = input;164encoder->pdata = pdata;165encoder->irq_a = gpio_to_irq(pdata->gpio_a);166encoder->irq_b = gpio_to_irq(pdata->gpio_b);167168/* create and register the input driver */169input->name = pdev->name;170input->id.bustype = BUS_HOST;171input->dev.parent = &pdev->dev;172173if (pdata->relative_axis) {174input->evbit[0] = BIT_MASK(EV_REL);175input->relbit[0] = BIT_MASK(pdata->axis);176} else {177input->evbit[0] = BIT_MASK(EV_ABS);178input_set_abs_params(encoder->input,179pdata->axis, 0, pdata->steps, 0, 1);180}181182err = input_register_device(input);183if (err) {184dev_err(&pdev->dev, "failed to register input device\n");185goto exit_free_mem;186}187188/* request the GPIOs */189err = gpio_request(pdata->gpio_a, DRV_NAME);190if (err) {191dev_err(&pdev->dev, "unable to request GPIO %d\n",192pdata->gpio_a);193goto exit_unregister_input;194}195196err = gpio_direction_input(pdata->gpio_a);197if (err) {198dev_err(&pdev->dev, "unable to set GPIO %d for input\n",199pdata->gpio_a);200goto exit_unregister_input;201}202203err = gpio_request(pdata->gpio_b, DRV_NAME);204if (err) {205dev_err(&pdev->dev, "unable to request GPIO %d\n",206pdata->gpio_b);207goto exit_free_gpio_a;208}209210err = gpio_direction_input(pdata->gpio_b);211if (err) {212dev_err(&pdev->dev, "unable to set GPIO %d for input\n",213pdata->gpio_b);214goto exit_free_gpio_a;215}216217/* request the IRQs */218if (pdata->half_period) {219handler = &rotary_encoder_half_period_irq;220encoder->last_stable = rotary_encoder_get_state(pdata);221} else {222handler = &rotary_encoder_irq;223}224225err = request_irq(encoder->irq_a, handler,226IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,227DRV_NAME, encoder);228if (err) {229dev_err(&pdev->dev, "unable to request IRQ %d\n",230encoder->irq_a);231goto exit_free_gpio_b;232}233234err = request_irq(encoder->irq_b, handler,235IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,236DRV_NAME, encoder);237if (err) {238dev_err(&pdev->dev, "unable to request IRQ %d\n",239encoder->irq_b);240goto exit_free_irq_a;241}242243platform_set_drvdata(pdev, encoder);244245return 0;246247exit_free_irq_a:248free_irq(encoder->irq_a, encoder);249exit_free_gpio_b:250gpio_free(pdata->gpio_b);251exit_free_gpio_a:252gpio_free(pdata->gpio_a);253exit_unregister_input:254input_unregister_device(input);255input = NULL; /* so we don't try to free it */256exit_free_mem:257input_free_device(input);258kfree(encoder);259return err;260}261262static int __devexit rotary_encoder_remove(struct platform_device *pdev)263{264struct rotary_encoder *encoder = platform_get_drvdata(pdev);265struct rotary_encoder_platform_data *pdata = pdev->dev.platform_data;266267free_irq(encoder->irq_a, encoder);268free_irq(encoder->irq_b, encoder);269gpio_free(pdata->gpio_a);270gpio_free(pdata->gpio_b);271input_unregister_device(encoder->input);272platform_set_drvdata(pdev, NULL);273kfree(encoder);274275return 0;276}277278static struct platform_driver rotary_encoder_driver = {279.probe = rotary_encoder_probe,280.remove = __devexit_p(rotary_encoder_remove),281.driver = {282.name = DRV_NAME,283.owner = THIS_MODULE,284}285};286287static int __init rotary_encoder_init(void)288{289return platform_driver_register(&rotary_encoder_driver);290}291292static void __exit rotary_encoder_exit(void)293{294platform_driver_unregister(&rotary_encoder_driver);295}296297module_init(rotary_encoder_init);298module_exit(rotary_encoder_exit);299300MODULE_ALIAS("platform:" DRV_NAME);301MODULE_DESCRIPTION("GPIO rotary encoder driver");302MODULE_AUTHOR("Daniel Mack <[email protected]>, Johan Hovold");303MODULE_LICENSE("GPL v2");304305306