18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * rotary_encoder.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * (c) 2009 Daniel Mack <daniel@caiaq.de> 68c2ecf20Sopenharmony_ci * Copyright (C) 2011 Johan Hovold <jhovold@gmail.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * state machine code inspired by code from Tim Ruetz 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * A generic driver for rotary encoders connected to GPIO lines. 118c2ecf20Sopenharmony_ci * See file:Documentation/input/devices/rotary-encoder.rst for more information 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/kernel.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 178c2ecf20Sopenharmony_ci#include <linux/input.h> 188c2ecf20Sopenharmony_ci#include <linux/device.h> 198c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 208c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 218c2ecf20Sopenharmony_ci#include <linux/slab.h> 228c2ecf20Sopenharmony_ci#include <linux/of.h> 238c2ecf20Sopenharmony_ci#include <linux/pm.h> 248c2ecf20Sopenharmony_ci#include <linux/property.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define DRV_NAME "rotary-encoder" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cienum rotary_encoder_encoding { 298c2ecf20Sopenharmony_ci ROTENC_GRAY, 308c2ecf20Sopenharmony_ci ROTENC_BINARY, 318c2ecf20Sopenharmony_ci}; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistruct rotary_encoder { 348c2ecf20Sopenharmony_ci struct input_dev *input; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci struct mutex access_mutex; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci u32 steps; 398c2ecf20Sopenharmony_ci u32 axis; 408c2ecf20Sopenharmony_ci bool relative_axis; 418c2ecf20Sopenharmony_ci bool rollover; 428c2ecf20Sopenharmony_ci enum rotary_encoder_encoding encoding; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci unsigned int pos; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci struct gpio_descs *gpios; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci unsigned int *irq; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci bool armed; 518c2ecf20Sopenharmony_ci signed char dir; /* 1 - clockwise, -1 - CCW */ 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci unsigned int last_stable; 548c2ecf20Sopenharmony_ci}; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic unsigned int rotary_encoder_get_state(struct rotary_encoder *encoder) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci int i; 598c2ecf20Sopenharmony_ci unsigned int ret = 0; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci for (i = 0; i < encoder->gpios->ndescs; ++i) { 628c2ecf20Sopenharmony_ci int val = gpiod_get_value_cansleep(encoder->gpios->desc[i]); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci /* convert from gray encoding to normal */ 658c2ecf20Sopenharmony_ci if (encoder->encoding == ROTENC_GRAY && ret & 1) 668c2ecf20Sopenharmony_ci val = !val; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci ret = ret << 1 | val; 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci return ret & 3; 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic void rotary_encoder_report_event(struct rotary_encoder *encoder) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci if (encoder->relative_axis) { 778c2ecf20Sopenharmony_ci input_report_rel(encoder->input, 788c2ecf20Sopenharmony_ci encoder->axis, encoder->dir); 798c2ecf20Sopenharmony_ci } else { 808c2ecf20Sopenharmony_ci unsigned int pos = encoder->pos; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci if (encoder->dir < 0) { 838c2ecf20Sopenharmony_ci /* turning counter-clockwise */ 848c2ecf20Sopenharmony_ci if (encoder->rollover) 858c2ecf20Sopenharmony_ci pos += encoder->steps; 868c2ecf20Sopenharmony_ci if (pos) 878c2ecf20Sopenharmony_ci pos--; 888c2ecf20Sopenharmony_ci } else { 898c2ecf20Sopenharmony_ci /* turning clockwise */ 908c2ecf20Sopenharmony_ci if (encoder->rollover || pos < encoder->steps) 918c2ecf20Sopenharmony_ci pos++; 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci if (encoder->rollover) 958c2ecf20Sopenharmony_ci pos %= encoder->steps; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci encoder->pos = pos; 988c2ecf20Sopenharmony_ci input_report_abs(encoder->input, encoder->axis, encoder->pos); 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci input_sync(encoder->input); 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic irqreturn_t rotary_encoder_irq(int irq, void *dev_id) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci struct rotary_encoder *encoder = dev_id; 1078c2ecf20Sopenharmony_ci unsigned int state; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci mutex_lock(&encoder->access_mutex); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci state = rotary_encoder_get_state(encoder); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci switch (state) { 1148c2ecf20Sopenharmony_ci case 0x0: 1158c2ecf20Sopenharmony_ci if (encoder->armed) { 1168c2ecf20Sopenharmony_ci rotary_encoder_report_event(encoder); 1178c2ecf20Sopenharmony_ci encoder->armed = false; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci break; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci case 0x1: 1228c2ecf20Sopenharmony_ci case 0x3: 1238c2ecf20Sopenharmony_ci if (encoder->armed) 1248c2ecf20Sopenharmony_ci encoder->dir = 2 - state; 1258c2ecf20Sopenharmony_ci break; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci case 0x2: 1288c2ecf20Sopenharmony_ci encoder->armed = true; 1298c2ecf20Sopenharmony_ci break; 1308c2ecf20Sopenharmony_ci } 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci mutex_unlock(&encoder->access_mutex); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic irqreturn_t rotary_encoder_half_period_irq(int irq, void *dev_id) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci struct rotary_encoder *encoder = dev_id; 1408c2ecf20Sopenharmony_ci unsigned int state; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci mutex_lock(&encoder->access_mutex); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci state = rotary_encoder_get_state(encoder); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci if (state & 1) { 1478c2ecf20Sopenharmony_ci encoder->dir = ((encoder->last_stable - state + 1) % 4) - 1; 1488c2ecf20Sopenharmony_ci } else { 1498c2ecf20Sopenharmony_ci if (state != encoder->last_stable) { 1508c2ecf20Sopenharmony_ci rotary_encoder_report_event(encoder); 1518c2ecf20Sopenharmony_ci encoder->last_stable = state; 1528c2ecf20Sopenharmony_ci } 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci mutex_unlock(&encoder->access_mutex); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic irqreturn_t rotary_encoder_quarter_period_irq(int irq, void *dev_id) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct rotary_encoder *encoder = dev_id; 1638c2ecf20Sopenharmony_ci unsigned int state; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci mutex_lock(&encoder->access_mutex); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci state = rotary_encoder_get_state(encoder); 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci if ((encoder->last_stable + 1) % 4 == state) 1708c2ecf20Sopenharmony_ci encoder->dir = 1; 1718c2ecf20Sopenharmony_ci else if (encoder->last_stable == (state + 1) % 4) 1728c2ecf20Sopenharmony_ci encoder->dir = -1; 1738c2ecf20Sopenharmony_ci else 1748c2ecf20Sopenharmony_ci goto out; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci rotary_encoder_report_event(encoder); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ciout: 1798c2ecf20Sopenharmony_ci encoder->last_stable = state; 1808c2ecf20Sopenharmony_ci mutex_unlock(&encoder->access_mutex); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic int rotary_encoder_probe(struct platform_device *pdev) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 1888c2ecf20Sopenharmony_ci struct rotary_encoder *encoder; 1898c2ecf20Sopenharmony_ci struct input_dev *input; 1908c2ecf20Sopenharmony_ci irq_handler_t handler; 1918c2ecf20Sopenharmony_ci u32 steps_per_period; 1928c2ecf20Sopenharmony_ci unsigned int i; 1938c2ecf20Sopenharmony_ci int err; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci encoder = devm_kzalloc(dev, sizeof(struct rotary_encoder), GFP_KERNEL); 1968c2ecf20Sopenharmony_ci if (!encoder) 1978c2ecf20Sopenharmony_ci return -ENOMEM; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci mutex_init(&encoder->access_mutex); 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci device_property_read_u32(dev, "rotary-encoder,steps", &encoder->steps); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci err = device_property_read_u32(dev, "rotary-encoder,steps-per-period", 2048c2ecf20Sopenharmony_ci &steps_per_period); 2058c2ecf20Sopenharmony_ci if (err) { 2068c2ecf20Sopenharmony_ci /* 2078c2ecf20Sopenharmony_ci * The 'half-period' property has been deprecated, you must 2088c2ecf20Sopenharmony_ci * use 'steps-per-period' and set an appropriate value, but 2098c2ecf20Sopenharmony_ci * we still need to parse it to maintain compatibility. If 2108c2ecf20Sopenharmony_ci * neither property is present we fall back to the one step 2118c2ecf20Sopenharmony_ci * per period behavior. 2128c2ecf20Sopenharmony_ci */ 2138c2ecf20Sopenharmony_ci steps_per_period = device_property_read_bool(dev, 2148c2ecf20Sopenharmony_ci "rotary-encoder,half-period") ? 2 : 1; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci encoder->rollover = 2188c2ecf20Sopenharmony_ci device_property_read_bool(dev, "rotary-encoder,rollover"); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci if (!device_property_present(dev, "rotary-encoder,encoding") || 2218c2ecf20Sopenharmony_ci !device_property_match_string(dev, "rotary-encoder,encoding", 2228c2ecf20Sopenharmony_ci "gray")) { 2238c2ecf20Sopenharmony_ci dev_info(dev, "gray"); 2248c2ecf20Sopenharmony_ci encoder->encoding = ROTENC_GRAY; 2258c2ecf20Sopenharmony_ci } else if (!device_property_match_string(dev, "rotary-encoder,encoding", 2268c2ecf20Sopenharmony_ci "binary")) { 2278c2ecf20Sopenharmony_ci dev_info(dev, "binary"); 2288c2ecf20Sopenharmony_ci encoder->encoding = ROTENC_BINARY; 2298c2ecf20Sopenharmony_ci } else { 2308c2ecf20Sopenharmony_ci dev_err(dev, "unknown encoding setting\n"); 2318c2ecf20Sopenharmony_ci return -EINVAL; 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci device_property_read_u32(dev, "linux,axis", &encoder->axis); 2358c2ecf20Sopenharmony_ci encoder->relative_axis = 2368c2ecf20Sopenharmony_ci device_property_read_bool(dev, "rotary-encoder,relative-axis"); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci encoder->gpios = devm_gpiod_get_array(dev, NULL, GPIOD_IN); 2398c2ecf20Sopenharmony_ci if (IS_ERR(encoder->gpios)) { 2408c2ecf20Sopenharmony_ci err = PTR_ERR(encoder->gpios); 2418c2ecf20Sopenharmony_ci if (err != -EPROBE_DEFER) 2428c2ecf20Sopenharmony_ci dev_err(dev, "unable to get gpios: %d\n", err); 2438c2ecf20Sopenharmony_ci return err; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci if (encoder->gpios->ndescs < 2) { 2468c2ecf20Sopenharmony_ci dev_err(dev, "not enough gpios found\n"); 2478c2ecf20Sopenharmony_ci return -EINVAL; 2488c2ecf20Sopenharmony_ci } 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci input = devm_input_allocate_device(dev); 2518c2ecf20Sopenharmony_ci if (!input) 2528c2ecf20Sopenharmony_ci return -ENOMEM; 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci encoder->input = input; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci input->name = pdev->name; 2578c2ecf20Sopenharmony_ci input->id.bustype = BUS_HOST; 2588c2ecf20Sopenharmony_ci input->dev.parent = dev; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci if (encoder->relative_axis) 2618c2ecf20Sopenharmony_ci input_set_capability(input, EV_REL, encoder->axis); 2628c2ecf20Sopenharmony_ci else 2638c2ecf20Sopenharmony_ci input_set_abs_params(input, 2648c2ecf20Sopenharmony_ci encoder->axis, 0, encoder->steps, 0, 1); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci switch (steps_per_period >> (encoder->gpios->ndescs - 2)) { 2678c2ecf20Sopenharmony_ci case 4: 2688c2ecf20Sopenharmony_ci handler = &rotary_encoder_quarter_period_irq; 2698c2ecf20Sopenharmony_ci encoder->last_stable = rotary_encoder_get_state(encoder); 2708c2ecf20Sopenharmony_ci break; 2718c2ecf20Sopenharmony_ci case 2: 2728c2ecf20Sopenharmony_ci handler = &rotary_encoder_half_period_irq; 2738c2ecf20Sopenharmony_ci encoder->last_stable = rotary_encoder_get_state(encoder); 2748c2ecf20Sopenharmony_ci break; 2758c2ecf20Sopenharmony_ci case 1: 2768c2ecf20Sopenharmony_ci handler = &rotary_encoder_irq; 2778c2ecf20Sopenharmony_ci break; 2788c2ecf20Sopenharmony_ci default: 2798c2ecf20Sopenharmony_ci dev_err(dev, "'%d' is not a valid steps-per-period value\n", 2808c2ecf20Sopenharmony_ci steps_per_period); 2818c2ecf20Sopenharmony_ci return -EINVAL; 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci encoder->irq = 2858c2ecf20Sopenharmony_ci devm_kcalloc(dev, 2868c2ecf20Sopenharmony_ci encoder->gpios->ndescs, sizeof(*encoder->irq), 2878c2ecf20Sopenharmony_ci GFP_KERNEL); 2888c2ecf20Sopenharmony_ci if (!encoder->irq) 2898c2ecf20Sopenharmony_ci return -ENOMEM; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci for (i = 0; i < encoder->gpios->ndescs; ++i) { 2928c2ecf20Sopenharmony_ci encoder->irq[i] = gpiod_to_irq(encoder->gpios->desc[i]); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci err = devm_request_threaded_irq(dev, encoder->irq[i], 2958c2ecf20Sopenharmony_ci NULL, handler, 2968c2ecf20Sopenharmony_ci IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | 2978c2ecf20Sopenharmony_ci IRQF_ONESHOT, 2988c2ecf20Sopenharmony_ci DRV_NAME, encoder); 2998c2ecf20Sopenharmony_ci if (err) { 3008c2ecf20Sopenharmony_ci dev_err(dev, "unable to request IRQ %d (gpio#%d)\n", 3018c2ecf20Sopenharmony_ci encoder->irq[i], i); 3028c2ecf20Sopenharmony_ci return err; 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci err = input_register_device(input); 3078c2ecf20Sopenharmony_ci if (err) { 3088c2ecf20Sopenharmony_ci dev_err(dev, "failed to register input device\n"); 3098c2ecf20Sopenharmony_ci return err; 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci device_init_wakeup(dev, 3138c2ecf20Sopenharmony_ci device_property_read_bool(dev, "wakeup-source")); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, encoder); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci return 0; 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic int __maybe_unused rotary_encoder_suspend(struct device *dev) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci struct rotary_encoder *encoder = dev_get_drvdata(dev); 3238c2ecf20Sopenharmony_ci unsigned int i; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if (device_may_wakeup(dev)) { 3268c2ecf20Sopenharmony_ci for (i = 0; i < encoder->gpios->ndescs; ++i) 3278c2ecf20Sopenharmony_ci enable_irq_wake(encoder->irq[i]); 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci return 0; 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_cistatic int __maybe_unused rotary_encoder_resume(struct device *dev) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci struct rotary_encoder *encoder = dev_get_drvdata(dev); 3368c2ecf20Sopenharmony_ci unsigned int i; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci if (device_may_wakeup(dev)) { 3398c2ecf20Sopenharmony_ci for (i = 0; i < encoder->gpios->ndescs; ++i) 3408c2ecf20Sopenharmony_ci disable_irq_wake(encoder->irq[i]); 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci return 0; 3448c2ecf20Sopenharmony_ci} 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(rotary_encoder_pm_ops, 3478c2ecf20Sopenharmony_ci rotary_encoder_suspend, rotary_encoder_resume); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 3508c2ecf20Sopenharmony_cistatic const struct of_device_id rotary_encoder_of_match[] = { 3518c2ecf20Sopenharmony_ci { .compatible = "rotary-encoder", }, 3528c2ecf20Sopenharmony_ci { }, 3538c2ecf20Sopenharmony_ci}; 3548c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, rotary_encoder_of_match); 3558c2ecf20Sopenharmony_ci#endif 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_cistatic struct platform_driver rotary_encoder_driver = { 3588c2ecf20Sopenharmony_ci .probe = rotary_encoder_probe, 3598c2ecf20Sopenharmony_ci .driver = { 3608c2ecf20Sopenharmony_ci .name = DRV_NAME, 3618c2ecf20Sopenharmony_ci .pm = &rotary_encoder_pm_ops, 3628c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(rotary_encoder_of_match), 3638c2ecf20Sopenharmony_ci } 3648c2ecf20Sopenharmony_ci}; 3658c2ecf20Sopenharmony_cimodule_platform_driver(rotary_encoder_driver); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:" DRV_NAME); 3688c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("GPIO rotary encoder driver"); 3698c2ecf20Sopenharmony_ciMODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>, Johan Hovold"); 3708c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 371