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 
3173969ff0SDaniel Mack struct rotary_encoder {
3273969ff0SDaniel Mack 	struct input_dev *input;
33a9e340dcSDmitry Torokhov 
34dee520e3STimo Teräs 	struct mutex access_mutex;
35bd3ce655SH Hartley Sweeten 
36a9e340dcSDmitry Torokhov 	u32 steps;
37a9e340dcSDmitry Torokhov 	u32 axis;
38a9e340dcSDmitry Torokhov 	bool relative_axis;
39a9e340dcSDmitry Torokhov 	bool rollover;
40a9e340dcSDmitry Torokhov 
41bd3ce655SH Hartley Sweeten 	unsigned int pos;
42bd3ce655SH Hartley Sweeten 
4377a8f0adSDmitry Torokhov 	struct gpio_desc *gpio_a;
4477a8f0adSDmitry Torokhov 	struct gpio_desc *gpio_b;
4577a8f0adSDmitry Torokhov 
46bd3ce655SH Hartley Sweeten 	unsigned int irq_a;
47bd3ce655SH Hartley Sweeten 	unsigned int irq_b;
48bd3ce655SH Hartley Sweeten 
49bd3ce655SH Hartley Sweeten 	bool armed;
50bd3ce655SH Hartley Sweeten 	unsigned char dir;	/* 0 - clockwise, 1 - CCW */
51e70bdd41SJohan Hovold 
52e70bdd41SJohan Hovold 	char last_stable;
5373969ff0SDaniel Mack };
5473969ff0SDaniel Mack 
5577a8f0adSDmitry Torokhov static int rotary_encoder_get_state(struct rotary_encoder *encoder)
5673969ff0SDaniel Mack {
5777a8f0adSDmitry Torokhov 	int a = !!gpiod_get_value_cansleep(encoder->gpio_a);
5877a8f0adSDmitry Torokhov 	int b = !!gpiod_get_value_cansleep(encoder->gpio_b);
5973969ff0SDaniel Mack 
60521a8f5cSJohan Hovold 	return ((a << 1) | b);
61521a8f5cSJohan Hovold }
6273969ff0SDaniel Mack 
63521a8f5cSJohan Hovold static void rotary_encoder_report_event(struct rotary_encoder *encoder)
64521a8f5cSJohan Hovold {
65a9e340dcSDmitry Torokhov 	if (encoder->relative_axis) {
66521a8f5cSJohan Hovold 		input_report_rel(encoder->input,
67a9e340dcSDmitry Torokhov 				 encoder->axis, encoder->dir ? -1 : 1);
68bd3ce655SH Hartley Sweeten 	} else {
69bd3ce655SH Hartley Sweeten 		unsigned int pos = encoder->pos;
70bd3ce655SH Hartley Sweeten 
7173969ff0SDaniel Mack 		if (encoder->dir) {
7273969ff0SDaniel Mack 			/* turning counter-clockwise */
73a9e340dcSDmitry Torokhov 			if (encoder->rollover)
74a9e340dcSDmitry Torokhov 				pos += encoder->steps;
75bd3ce655SH Hartley Sweeten 			if (pos)
76bd3ce655SH Hartley Sweeten 				pos--;
7773969ff0SDaniel Mack 		} else {
7873969ff0SDaniel Mack 			/* turning clockwise */
79a9e340dcSDmitry Torokhov 			if (encoder->rollover || pos < encoder->steps)
80bd3ce655SH Hartley Sweeten 				pos++;
8173969ff0SDaniel Mack 		}
82521a8f5cSJohan Hovold 
83a9e340dcSDmitry Torokhov 		if (encoder->rollover)
84a9e340dcSDmitry Torokhov 			pos %= encoder->steps;
8573969ff0SDaniel Mack 
86521a8f5cSJohan Hovold 		encoder->pos = pos;
87a9e340dcSDmitry Torokhov 		input_report_abs(encoder->input, encoder->axis, encoder->pos);
88521a8f5cSJohan Hovold 	}
89521a8f5cSJohan Hovold 
90521a8f5cSJohan Hovold 	input_sync(encoder->input);
91521a8f5cSJohan Hovold }
92521a8f5cSJohan Hovold 
93521a8f5cSJohan Hovold static irqreturn_t rotary_encoder_irq(int irq, void *dev_id)
94521a8f5cSJohan Hovold {
95521a8f5cSJohan Hovold 	struct rotary_encoder *encoder = dev_id;
96521a8f5cSJohan Hovold 	int state;
97521a8f5cSJohan Hovold 
98dee520e3STimo Teräs 	mutex_lock(&encoder->access_mutex);
99dee520e3STimo Teräs 
10077a8f0adSDmitry Torokhov 	state = rotary_encoder_get_state(encoder);
101521a8f5cSJohan Hovold 
102521a8f5cSJohan Hovold 	switch (state) {
103521a8f5cSJohan Hovold 	case 0x0:
104521a8f5cSJohan Hovold 		if (encoder->armed) {
105521a8f5cSJohan Hovold 			rotary_encoder_report_event(encoder);
106bd3ce655SH Hartley Sweeten 			encoder->armed = false;
107521a8f5cSJohan Hovold 		}
10873969ff0SDaniel Mack 		break;
10973969ff0SDaniel Mack 
11073969ff0SDaniel Mack 	case 0x1:
11173969ff0SDaniel Mack 	case 0x2:
11273969ff0SDaniel Mack 		if (encoder->armed)
11373969ff0SDaniel Mack 			encoder->dir = state - 1;
11473969ff0SDaniel Mack 		break;
11573969ff0SDaniel Mack 
11673969ff0SDaniel Mack 	case 0x3:
117bd3ce655SH Hartley Sweeten 		encoder->armed = true;
11873969ff0SDaniel Mack 		break;
11973969ff0SDaniel Mack 	}
12073969ff0SDaniel Mack 
121dee520e3STimo Teräs 	mutex_unlock(&encoder->access_mutex);
122dee520e3STimo Teräs 
12373969ff0SDaniel Mack 	return IRQ_HANDLED;
12473969ff0SDaniel Mack }
12573969ff0SDaniel Mack 
126e70bdd41SJohan Hovold static irqreturn_t rotary_encoder_half_period_irq(int irq, void *dev_id)
127e70bdd41SJohan Hovold {
128e70bdd41SJohan Hovold 	struct rotary_encoder *encoder = dev_id;
129e70bdd41SJohan Hovold 	int state;
130e70bdd41SJohan Hovold 
131dee520e3STimo Teräs 	mutex_lock(&encoder->access_mutex);
132dee520e3STimo Teräs 
13377a8f0adSDmitry Torokhov 	state = rotary_encoder_get_state(encoder);
134e70bdd41SJohan Hovold 
135e70bdd41SJohan Hovold 	switch (state) {
136e70bdd41SJohan Hovold 	case 0x00:
137e70bdd41SJohan Hovold 	case 0x03:
138e70bdd41SJohan Hovold 		if (state != encoder->last_stable) {
139e70bdd41SJohan Hovold 			rotary_encoder_report_event(encoder);
140e70bdd41SJohan Hovold 			encoder->last_stable = state;
141e70bdd41SJohan Hovold 		}
142e70bdd41SJohan Hovold 		break;
143e70bdd41SJohan Hovold 
144e70bdd41SJohan Hovold 	case 0x01:
145e70bdd41SJohan Hovold 	case 0x02:
146e70bdd41SJohan Hovold 		encoder->dir = (encoder->last_stable + state) & 0x01;
147e70bdd41SJohan Hovold 		break;
148e70bdd41SJohan Hovold 	}
149e70bdd41SJohan Hovold 
150dee520e3STimo Teräs 	mutex_unlock(&encoder->access_mutex);
151dee520e3STimo Teräs 
152e70bdd41SJohan Hovold 	return IRQ_HANDLED;
153e70bdd41SJohan Hovold }
154e70bdd41SJohan Hovold 
1553a341a4cSEzequiel Garcia static irqreturn_t rotary_encoder_quarter_period_irq(int irq, void *dev_id)
1563a341a4cSEzequiel Garcia {
1573a341a4cSEzequiel Garcia 	struct rotary_encoder *encoder = dev_id;
1583a341a4cSEzequiel Garcia 	unsigned char sum;
1593a341a4cSEzequiel Garcia 	int state;
1603a341a4cSEzequiel Garcia 
161dee520e3STimo Teräs 	mutex_lock(&encoder->access_mutex);
162dee520e3STimo Teräs 
16377a8f0adSDmitry Torokhov 	state = rotary_encoder_get_state(encoder);
1643a341a4cSEzequiel Garcia 
1653a341a4cSEzequiel Garcia 	/*
1663a341a4cSEzequiel Garcia 	 * We encode the previous and the current state using a byte.
1673a341a4cSEzequiel Garcia 	 * The previous state in the MSB nibble, the current state in the LSB
1683a341a4cSEzequiel Garcia 	 * nibble. Then use a table to decide the direction of the turn.
1693a341a4cSEzequiel Garcia 	 */
1703a341a4cSEzequiel Garcia 	sum = (encoder->last_stable << 4) + state;
1713a341a4cSEzequiel Garcia 	switch (sum) {
1723a341a4cSEzequiel Garcia 	case 0x31:
1733a341a4cSEzequiel Garcia 	case 0x10:
1743a341a4cSEzequiel Garcia 	case 0x02:
1753a341a4cSEzequiel Garcia 	case 0x23:
1763a341a4cSEzequiel Garcia 		encoder->dir = 0; /* clockwise */
1773a341a4cSEzequiel Garcia 		break;
1783a341a4cSEzequiel Garcia 
1793a341a4cSEzequiel Garcia 	case 0x13:
1803a341a4cSEzequiel Garcia 	case 0x01:
1813a341a4cSEzequiel Garcia 	case 0x20:
1823a341a4cSEzequiel Garcia 	case 0x32:
1833a341a4cSEzequiel Garcia 		encoder->dir = 1; /* counter-clockwise */
1843a341a4cSEzequiel Garcia 		break;
1853a341a4cSEzequiel Garcia 
1863a341a4cSEzequiel Garcia 	default:
1873a341a4cSEzequiel Garcia 		/*
1883a341a4cSEzequiel Garcia 		 * Ignore all other values. This covers the case when the
1893a341a4cSEzequiel Garcia 		 * state didn't change (a spurious interrupt) and the
1903a341a4cSEzequiel Garcia 		 * cases where the state changed by two steps, making it
1913a341a4cSEzequiel Garcia 		 * impossible to tell the direction.
1923a341a4cSEzequiel Garcia 		 *
1933a341a4cSEzequiel Garcia 		 * In either case, don't report any event and save the
1943a341a4cSEzequiel Garcia 		 * state for later.
1953a341a4cSEzequiel Garcia 		 */
1963a341a4cSEzequiel Garcia 		goto out;
1973a341a4cSEzequiel Garcia 	}
1983a341a4cSEzequiel Garcia 
1993a341a4cSEzequiel Garcia 	rotary_encoder_report_event(encoder);
2003a341a4cSEzequiel Garcia 
2013a341a4cSEzequiel Garcia out:
2023a341a4cSEzequiel Garcia 	encoder->last_stable = state;
203dee520e3STimo Teräs 	mutex_unlock(&encoder->access_mutex);
204dee520e3STimo Teräs 
2053a341a4cSEzequiel Garcia 	return IRQ_HANDLED;
2063a341a4cSEzequiel Garcia }
2073a341a4cSEzequiel Garcia 
2085298cc4cSBill Pemberton static int rotary_encoder_probe(struct platform_device *pdev)
20973969ff0SDaniel Mack {
210ce919537SDmitry Torokhov 	struct device *dev = &pdev->dev;
21173969ff0SDaniel Mack 	struct rotary_encoder *encoder;
21273969ff0SDaniel Mack 	struct input_dev *input;
213e70bdd41SJohan Hovold 	irq_handler_t handler;
214a9e340dcSDmitry Torokhov 	u32 steps_per_period;
21573969ff0SDaniel Mack 	int err;
21673969ff0SDaniel Mack 
217d9202af2STimo Teräs 	encoder = devm_kzalloc(dev, sizeof(struct rotary_encoder), GFP_KERNEL);
218d9202af2STimo Teräs 	if (!encoder)
219d9202af2STimo Teräs 		return -ENOMEM;
220d9202af2STimo Teräs 
22177a8f0adSDmitry Torokhov 	mutex_init(&encoder->access_mutex);
222a9e340dcSDmitry Torokhov 
223a9e340dcSDmitry Torokhov 	device_property_read_u32(dev, "rotary-encoder,steps", &encoder->steps);
224a9e340dcSDmitry Torokhov 
225a9e340dcSDmitry Torokhov 	err = device_property_read_u32(dev, "rotary-encoder,steps-per-period",
226a9e340dcSDmitry Torokhov 				       &steps_per_period);
227a9e340dcSDmitry Torokhov 	if (err) {
228a9e340dcSDmitry Torokhov 		/*
229a9e340dcSDmitry Torokhov 		 * The 'half-period' property has been deprecated, you must
230a9e340dcSDmitry Torokhov 		 * use 'steps-per-period' and set an appropriate value, but
231a9e340dcSDmitry Torokhov 		 * we still need to parse it to maintain compatibility. If
232a9e340dcSDmitry Torokhov 		 * neither property is present we fall back to the one step
233a9e340dcSDmitry Torokhov 		 * per period behavior.
234a9e340dcSDmitry Torokhov 		 */
235a9e340dcSDmitry Torokhov 		steps_per_period = device_property_read_bool(dev,
236a9e340dcSDmitry Torokhov 					"rotary-encoder,half-period") ? 2 : 1;
237a9e340dcSDmitry Torokhov 	}
238a9e340dcSDmitry Torokhov 
239a9e340dcSDmitry Torokhov 	encoder->rollover =
240a9e340dcSDmitry Torokhov 		device_property_read_bool(dev, "rotary-encoder,rollover");
241a9e340dcSDmitry Torokhov 
242a9e340dcSDmitry Torokhov 	device_property_read_u32(dev, "linux,axis", &encoder->axis);
243a9e340dcSDmitry Torokhov 	encoder->relative_axis =
244a9e340dcSDmitry Torokhov 		device_property_read_bool(dev, "rotary-encoder,relative-axis");
24577a8f0adSDmitry Torokhov 
24677a8f0adSDmitry Torokhov 	encoder->gpio_a = devm_gpiod_get_index(dev, NULL, 0, GPIOD_IN);
24777a8f0adSDmitry Torokhov 	if (IS_ERR(encoder->gpio_a)) {
24877a8f0adSDmitry Torokhov 		err = PTR_ERR(encoder->gpio_a);
24977a8f0adSDmitry Torokhov 		dev_err(dev, "unable to get GPIO at index 0: %d\n", err);
25077a8f0adSDmitry Torokhov 		return err;
25177a8f0adSDmitry Torokhov 	}
25277a8f0adSDmitry Torokhov 
25377a8f0adSDmitry Torokhov 	encoder->irq_a = gpiod_to_irq(encoder->gpio_a);
25477a8f0adSDmitry Torokhov 
25577a8f0adSDmitry Torokhov 	encoder->gpio_b = devm_gpiod_get_index(dev, NULL, 1, GPIOD_IN);
25677a8f0adSDmitry Torokhov 	if (IS_ERR(encoder->gpio_b)) {
25777a8f0adSDmitry Torokhov 		err = PTR_ERR(encoder->gpio_b);
25877a8f0adSDmitry Torokhov 		dev_err(dev, "unable to get GPIO at index 1: %d\n", err);
25977a8f0adSDmitry Torokhov 		return err;
26077a8f0adSDmitry Torokhov 	}
26177a8f0adSDmitry Torokhov 
26277a8f0adSDmitry Torokhov 	encoder->irq_b = gpiod_to_irq(encoder->gpio_b);
26377a8f0adSDmitry Torokhov 
264d9202af2STimo Teräs 	input = devm_input_allocate_device(dev);
265d9202af2STimo Teräs 	if (!input)
266d9202af2STimo Teräs 		return -ENOMEM;
26773969ff0SDaniel Mack 
26873969ff0SDaniel Mack 	encoder->input = input;
26973969ff0SDaniel Mack 
27073969ff0SDaniel Mack 	input->name = pdev->name;
27173969ff0SDaniel Mack 	input->id.bustype = BUS_HOST;
27280c99bcdSDaniel Mack 	input->dev.parent = dev;
273bd3ce655SH Hartley Sweeten 
274a9e340dcSDmitry Torokhov 	if (encoder->relative_axis)
275a9e340dcSDmitry Torokhov 		input_set_capability(input, EV_REL, encoder->axis);
2768631580fSDmitry Torokhov 	else
277a9e340dcSDmitry Torokhov 		input_set_abs_params(input,
278a9e340dcSDmitry Torokhov 				     encoder->axis, 0, encoder->steps, 0, 1);
27973969ff0SDaniel Mack 
280a9e340dcSDmitry Torokhov 	switch (steps_per_period) {
2813a341a4cSEzequiel Garcia 	case 4:
2823a341a4cSEzequiel Garcia 		handler = &rotary_encoder_quarter_period_irq;
28377a8f0adSDmitry Torokhov 		encoder->last_stable = rotary_encoder_get_state(encoder);
2843a341a4cSEzequiel Garcia 		break;
2853a341a4cSEzequiel Garcia 	case 2:
286e70bdd41SJohan Hovold 		handler = &rotary_encoder_half_period_irq;
28777a8f0adSDmitry Torokhov 		encoder->last_stable = rotary_encoder_get_state(encoder);
2883a341a4cSEzequiel Garcia 		break;
2893a341a4cSEzequiel Garcia 	case 1:
290e70bdd41SJohan Hovold 		handler = &rotary_encoder_irq;
2913a341a4cSEzequiel Garcia 		break;
2923a341a4cSEzequiel Garcia 	default:
2933a341a4cSEzequiel Garcia 		dev_err(dev, "'%d' is not a valid steps-per-period value\n",
294a9e340dcSDmitry Torokhov 			steps_per_period);
295d9202af2STimo Teräs 		return -EINVAL;
296e70bdd41SJohan Hovold 	}
297e70bdd41SJohan Hovold 
298dee520e3STimo Teräs 	err = devm_request_threaded_irq(dev, encoder->irq_a, NULL, handler,
299dee520e3STimo Teräs 				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
300dee520e3STimo Teräs 				IRQF_ONESHOT,
30173969ff0SDaniel Mack 				DRV_NAME, encoder);
30273969ff0SDaniel Mack 	if (err) {
303429a34d7SDaniel Mack 		dev_err(dev, "unable to request IRQ %d\n", encoder->irq_a);
304d9202af2STimo Teräs 		return err;
30573969ff0SDaniel Mack 	}
30673969ff0SDaniel Mack 
307dee520e3STimo Teräs 	err = devm_request_threaded_irq(dev, encoder->irq_b, NULL, handler,
308dee520e3STimo Teräs 				IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
309dee520e3STimo Teräs 				IRQF_ONESHOT,
31073969ff0SDaniel Mack 				DRV_NAME, encoder);
31173969ff0SDaniel Mack 	if (err) {
312429a34d7SDaniel Mack 		dev_err(dev, "unable to request IRQ %d\n", encoder->irq_b);
313d9202af2STimo Teräs 		return err;
31473969ff0SDaniel Mack 	}
31573969ff0SDaniel Mack 
31680c99bcdSDaniel Mack 	err = input_register_device(input);
31780c99bcdSDaniel Mack 	if (err) {
31880c99bcdSDaniel Mack 		dev_err(dev, "failed to register input device\n");
319d9202af2STimo Teräs 		return err;
32080c99bcdSDaniel Mack 	}
32180c99bcdSDaniel Mack 
322a9e340dcSDmitry Torokhov 	device_init_wakeup(dev,
323a9e340dcSDmitry Torokhov 			   device_property_read_bool(dev, "wakeup-source"));
32447ec6e5aSSylvain Rochet 
32573969ff0SDaniel Mack 	platform_set_drvdata(pdev, encoder);
32673969ff0SDaniel Mack 
32773969ff0SDaniel Mack 	return 0;
32873969ff0SDaniel Mack }
32973969ff0SDaniel Mack 
3306a6f70b3SDmitry Torokhov static int __maybe_unused rotary_encoder_suspend(struct device *dev)
33147ec6e5aSSylvain Rochet {
33247ec6e5aSSylvain Rochet 	struct rotary_encoder *encoder = dev_get_drvdata(dev);
33347ec6e5aSSylvain Rochet 
33447ec6e5aSSylvain Rochet 	if (device_may_wakeup(dev)) {
33547ec6e5aSSylvain Rochet 		enable_irq_wake(encoder->irq_a);
33647ec6e5aSSylvain Rochet 		enable_irq_wake(encoder->irq_b);
33747ec6e5aSSylvain Rochet 	}
33847ec6e5aSSylvain Rochet 
33947ec6e5aSSylvain Rochet 	return 0;
34047ec6e5aSSylvain Rochet }
34147ec6e5aSSylvain Rochet 
3426a6f70b3SDmitry Torokhov static int __maybe_unused rotary_encoder_resume(struct device *dev)
34347ec6e5aSSylvain Rochet {
34447ec6e5aSSylvain Rochet 	struct rotary_encoder *encoder = dev_get_drvdata(dev);
34547ec6e5aSSylvain Rochet 
34647ec6e5aSSylvain Rochet 	if (device_may_wakeup(dev)) {
34747ec6e5aSSylvain Rochet 		disable_irq_wake(encoder->irq_a);
34847ec6e5aSSylvain Rochet 		disable_irq_wake(encoder->irq_b);
34947ec6e5aSSylvain Rochet 	}
35047ec6e5aSSylvain Rochet 
35147ec6e5aSSylvain Rochet 	return 0;
35247ec6e5aSSylvain Rochet }
35347ec6e5aSSylvain Rochet 
35447ec6e5aSSylvain Rochet static SIMPLE_DEV_PM_OPS(rotary_encoder_pm_ops,
35547ec6e5aSSylvain Rochet 			 rotary_encoder_suspend, rotary_encoder_resume);
35647ec6e5aSSylvain Rochet 
357a9e340dcSDmitry Torokhov #ifdef CONFIG_OF
358a9e340dcSDmitry Torokhov static const struct of_device_id rotary_encoder_of_match[] = {
359a9e340dcSDmitry Torokhov 	{ .compatible = "rotary-encoder", },
360a9e340dcSDmitry Torokhov 	{ },
361a9e340dcSDmitry Torokhov };
362a9e340dcSDmitry Torokhov MODULE_DEVICE_TABLE(of, rotary_encoder_of_match);
363a9e340dcSDmitry Torokhov #endif
364a9e340dcSDmitry Torokhov 
36573969ff0SDaniel Mack static struct platform_driver rotary_encoder_driver = {
36673969ff0SDaniel Mack 	.probe		= rotary_encoder_probe,
36773969ff0SDaniel Mack 	.driver		= {
36873969ff0SDaniel Mack 		.name	= DRV_NAME,
36947ec6e5aSSylvain Rochet 		.pm	= &rotary_encoder_pm_ops,
37080c99bcdSDaniel Mack 		.of_match_table = of_match_ptr(rotary_encoder_of_match),
37173969ff0SDaniel Mack 	}
37273969ff0SDaniel Mack };
373840a746bSJJ Ding module_platform_driver(rotary_encoder_driver);
37473969ff0SDaniel Mack 
37573969ff0SDaniel Mack MODULE_ALIAS("platform:" DRV_NAME);
37673969ff0SDaniel Mack MODULE_DESCRIPTION("GPIO rotary encoder driver");
377e70bdd41SJohan Hovold MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>, Johan Hovold");
37873969ff0SDaniel Mack MODULE_LICENSE("GPL v2");
379