diff options
| author | Ingo Molnar <mingo@elte.hu> | 2009-05-11 14:44:27 +0200 | 
|---|---|---|
| committer | Ingo Molnar <mingo@elte.hu> | 2009-05-11 14:44:31 +0200 | 
| commit | 41fb454ebe6024f5c1e3b3cbc0abc0da762e7b51 (patch) | |
| tree | 51c50bcb67a5039448ddfa1869d7948cab1217e9 /drivers/input/misc/rotary_encoder.c | |
| parent | 19c1a6f5764d787113fa323ffb18be7991208f82 (diff) | |
| parent | 091bf7624d1c90cec9e578a18529f615213ff847 (diff) | |
Merge commit 'v2.6.30-rc5' into core/iommu
Merge reason: core/iommu was on an .30-rc1 base,
              update it to .30-rc5 to refresh.
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'drivers/input/misc/rotary_encoder.c')
| -rw-r--r-- | drivers/input/misc/rotary_encoder.c | 221 | 
1 files changed, 221 insertions, 0 deletions
diff --git a/drivers/input/misc/rotary_encoder.c b/drivers/input/misc/rotary_encoder.c new file mode 100644 index 00000000000..5bb3ab51b8c --- /dev/null +++ b/drivers/input/misc/rotary_encoder.c @@ -0,0 +1,221 @@ +/* + * rotary_encoder.c + * + * (c) 2009 Daniel Mack <daniel@caiaq.de> + * + * state machine code inspired by code from Tim Ruetz + * + * A generic driver for rotary encoders connected to GPIO lines. + * See file:Documentation/input/rotary_encoder.txt for more information + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/input.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/gpio.h> +#include <linux/rotary_encoder.h> + +#define DRV_NAME "rotary-encoder" + +struct rotary_encoder { +	unsigned int irq_a; +	unsigned int irq_b; +	unsigned int pos; +	unsigned int armed; +	unsigned int dir; +	struct input_dev *input; +	struct rotary_encoder_platform_data *pdata; +}; + +static irqreturn_t rotary_encoder_irq(int irq, void *dev_id) +{ +	struct rotary_encoder *encoder = dev_id; +	struct rotary_encoder_platform_data *pdata = encoder->pdata; +	int a = !!gpio_get_value(pdata->gpio_a); +	int b = !!gpio_get_value(pdata->gpio_b); +	int state; + +	a ^= pdata->inverted_a; +	b ^= pdata->inverted_b; +	state = (a << 1) | b; + +	switch (state) { + +	case 0x0: +		if (!encoder->armed) +			break; + +		if (encoder->dir) { +			/* turning counter-clockwise */ +			encoder->pos += pdata->steps; +			encoder->pos--; +			encoder->pos %= pdata->steps; +		} else { +			/* turning clockwise */ +			encoder->pos++; +			encoder->pos %= pdata->steps; +		} + +		input_report_abs(encoder->input, pdata->axis, encoder->pos); +		input_sync(encoder->input); + +		encoder->armed = 0; +		break; + +	case 0x1: +	case 0x2: +		if (encoder->armed) +			encoder->dir = state - 1; +		break; + +	case 0x3: +		encoder->armed = 1; +		break; +	} + +	return IRQ_HANDLED; +} + +static int __devinit rotary_encoder_probe(struct platform_device *pdev) +{ +	struct rotary_encoder_platform_data *pdata = pdev->dev.platform_data; +	struct rotary_encoder *encoder; +	struct input_dev *input; +	int err; + +	if (!pdata || !pdata->steps) { +		dev_err(&pdev->dev, "invalid platform data\n"); +		return -ENOENT; +	} + +	encoder = kzalloc(sizeof(struct rotary_encoder), GFP_KERNEL); +	input = input_allocate_device(); +	if (!encoder || !input) { +		dev_err(&pdev->dev, "failed to allocate memory for device\n"); +		err = -ENOMEM; +		goto exit_free_mem; +	} + +	encoder->input = input; +	encoder->pdata = pdata; +	encoder->irq_a = gpio_to_irq(pdata->gpio_a); +	encoder->irq_b = gpio_to_irq(pdata->gpio_b); + +	/* create and register the input driver */ +	input->name = pdev->name; +	input->id.bustype = BUS_HOST; +	input->dev.parent = &pdev->dev; +	input->evbit[0] = BIT_MASK(EV_ABS); +	input_set_abs_params(encoder->input, +			     pdata->axis, 0, pdata->steps, 0, 1); + +	err = input_register_device(input); +	if (err) { +		dev_err(&pdev->dev, "failed to register input device\n"); +		goto exit_free_mem; +	} + +	/* request the GPIOs */ +	err = gpio_request(pdata->gpio_a, DRV_NAME); +	if (err) { +		dev_err(&pdev->dev, "unable to request GPIO %d\n", +			pdata->gpio_a); +		goto exit_unregister_input; +	} + +	err = gpio_request(pdata->gpio_b, DRV_NAME); +	if (err) { +		dev_err(&pdev->dev, "unable to request GPIO %d\n", +			pdata->gpio_b); +		goto exit_free_gpio_a; +	} + +	/* request the IRQs */ +	err = request_irq(encoder->irq_a, &rotary_encoder_irq, +			  IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_LOWEDGE, +			  DRV_NAME, encoder); +	if (err) { +		dev_err(&pdev->dev, "unable to request IRQ %d\n", +			encoder->irq_a); +		goto exit_free_gpio_b; +	} + +	err = request_irq(encoder->irq_b, &rotary_encoder_irq, +			  IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_LOWEDGE, +			  DRV_NAME, encoder); +	if (err) { +		dev_err(&pdev->dev, "unable to request IRQ %d\n", +			encoder->irq_b); +		goto exit_free_irq_a; +	} + +	platform_set_drvdata(pdev, encoder); + +	return 0; + +exit_free_irq_a: +	free_irq(encoder->irq_a, encoder); +exit_free_gpio_b: +	gpio_free(pdata->gpio_b); +exit_free_gpio_a: +	gpio_free(pdata->gpio_a); +exit_unregister_input: +	input_unregister_device(input); +	input = NULL; /* so we don't try to free it */ +exit_free_mem: +	input_free_device(input); +	kfree(encoder); +	return err; +} + +static int __devexit rotary_encoder_remove(struct platform_device *pdev) +{ +	struct rotary_encoder *encoder = platform_get_drvdata(pdev); +	struct rotary_encoder_platform_data *pdata = pdev->dev.platform_data; + +	free_irq(encoder->irq_a, encoder); +	free_irq(encoder->irq_b, encoder); +	gpio_free(pdata->gpio_a); +	gpio_free(pdata->gpio_b); +	input_unregister_device(encoder->input); +	platform_set_drvdata(pdev, NULL); +	kfree(encoder); + +	return 0; +} + +static struct platform_driver rotary_encoder_driver = { +	.probe		= rotary_encoder_probe, +	.remove		= __devexit_p(rotary_encoder_remove), +	.driver		= { +		.name	= DRV_NAME, +		.owner	= THIS_MODULE, +	} +}; + +static int __init rotary_encoder_init(void) +{ +	return platform_driver_register(&rotary_encoder_driver); +} + +static void __exit rotary_encoder_exit(void) +{ +	platform_driver_unregister(&rotary_encoder_driver); +} + +module_init(rotary_encoder_init); +module_exit(rotary_encoder_exit); + +MODULE_ALIAS("platform:" DRV_NAME); +MODULE_DESCRIPTION("GPIO rotary encoder driver"); +MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>"); +MODULE_LICENSE("GPL v2"); +  | 
