1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * GPIO driven charlieplex keypad driver 4 * 5 * Copyright (c) 2026 Hugo Villeneuve <hvilleneuve@dimonoff.com> 6 * 7 * Based on matrix_keyboard.c 8 */ 9 10 #include <linux/bitops.h> 11 #include <linux/delay.h> 12 #include <linux/dev_printk.h> 13 #include <linux/device/devres.h> 14 #include <linux/err.h> 15 #include <linux/gpio/consumer.h> 16 #include <linux/input.h> 17 #include <linux/input/matrix_keypad.h> 18 #include <linux/math.h> 19 #include <linux/module.h> 20 #include <linux/mod_devicetable.h> 21 #include <linux/platform_device.h> 22 #include <linux/property.h> 23 #include <linux/string_helpers.h> 24 #include <linux/types.h> 25 26 struct charlieplex_keypad { 27 struct input_dev *input_dev; 28 struct gpio_descs *line_gpios; 29 unsigned int nlines; 30 unsigned int settling_time_us; 31 unsigned int debounce_threshold; 32 unsigned int debounce_count; 33 int debounce_code; 34 int current_code; 35 }; 36 37 static void charlieplex_keypad_report_key(struct input_dev *input) 38 { 39 struct charlieplex_keypad *keypad = input_get_drvdata(input); 40 const unsigned short *keycodes = input->keycode; 41 42 if (keypad->current_code > 0) { 43 input_event(input, EV_MSC, MSC_SCAN, keypad->current_code); 44 input_report_key(input, keycodes[keypad->current_code], 0); 45 input_sync(input); 46 } 47 48 if (keypad->debounce_code) { 49 input_event(input, EV_MSC, MSC_SCAN, keypad->debounce_code); 50 input_report_key(input, keycodes[keypad->debounce_code], 1); 51 input_sync(input); 52 } 53 54 keypad->current_code = keypad->debounce_code; 55 } 56 57 static void charlieplex_keypad_check_switch_change(struct input_dev *input, 58 unsigned int code) 59 { 60 struct charlieplex_keypad *keypad = input_get_drvdata(input); 61 62 if (code != keypad->debounce_code) { 63 keypad->debounce_count = 0; 64 keypad->debounce_code = code; 65 } 66 67 if (keypad->debounce_code != keypad->current_code) { 68 if (keypad->debounce_count++ >= keypad->debounce_threshold) 69 charlieplex_keypad_report_key(input); 70 } 71 } 72 73 static int charlieplex_keypad_scan_line(struct charlieplex_keypad *keypad, 74 unsigned int oline) 75 { 76 struct gpio_descs *line_gpios = keypad->line_gpios; 77 DECLARE_BITMAP(values, MATRIX_MAX_ROWS); 78 int err; 79 80 /* Activate only one line as output at a time. */ 81 gpiod_direction_output(line_gpios->desc[oline], 1); 82 83 if (keypad->settling_time_us) 84 fsleep(keypad->settling_time_us); 85 86 /* Read input on all other lines. */ 87 err = gpiod_get_array_value_cansleep(line_gpios->ndescs, line_gpios->desc, 88 line_gpios->info, values); 89 90 gpiod_direction_input(line_gpios->desc[oline]); 91 92 if (err) 93 return err; 94 95 for (unsigned int iline = 0; iline < keypad->nlines; iline++) { 96 if (iline == oline) 97 continue; /* Do not read active output line. */ 98 99 /* Check if GPIO is asserted. */ 100 if (test_bit(iline, values)) 101 return MATRIX_SCAN_CODE(oline, iline, 102 get_count_order(keypad->nlines)); 103 } 104 105 return 0; 106 } 107 108 static void charlieplex_keypad_poll(struct input_dev *input) 109 { 110 struct charlieplex_keypad *keypad = input_get_drvdata(input); 111 int code = 0; 112 113 for (unsigned int oline = 0; oline < keypad->nlines; oline++) { 114 code = charlieplex_keypad_scan_line(keypad, oline); 115 if (code != 0) 116 break; 117 } 118 119 if (code >= 0) 120 charlieplex_keypad_check_switch_change(input, code); 121 } 122 123 static int charlieplex_keypad_init_gpio(struct platform_device *pdev, 124 struct charlieplex_keypad *keypad) 125 { 126 char **pin_names; 127 char label[32]; 128 129 snprintf(label, sizeof(label), "%s-pin", pdev->name); 130 131 keypad->line_gpios = devm_gpiod_get_array(&pdev->dev, "line", GPIOD_IN); 132 if (IS_ERR(keypad->line_gpios)) 133 return PTR_ERR(keypad->line_gpios); 134 135 keypad->nlines = keypad->line_gpios->ndescs; 136 137 if (keypad->nlines > MATRIX_MAX_ROWS) 138 return -EINVAL; 139 140 pin_names = devm_kasprintf_strarray(&pdev->dev, label, keypad->nlines); 141 if (IS_ERR(pin_names)) 142 return PTR_ERR(pin_names); 143 144 for (unsigned int i = 0; i < keypad->line_gpios->ndescs; i++) 145 gpiod_set_consumer_name(keypad->line_gpios->desc[i], pin_names[i]); 146 147 return 0; 148 } 149 150 static int charlieplex_keypad_probe(struct platform_device *pdev) 151 { 152 struct charlieplex_keypad *keypad; 153 struct input_dev *input_dev; 154 unsigned int debounce_interval_ms = 5; 155 unsigned int poll_interval_ms; 156 int err; 157 158 keypad = devm_kzalloc(&pdev->dev, sizeof(*keypad), GFP_KERNEL); 159 if (!keypad) 160 return -ENOMEM; 161 162 input_dev = devm_input_allocate_device(&pdev->dev); 163 if (!input_dev) 164 return -ENOMEM; 165 166 keypad->input_dev = input_dev; 167 168 err = device_property_read_u32(&pdev->dev, "poll-interval", &poll_interval_ms); 169 if (err) 170 return dev_err_probe(&pdev->dev, err, 171 "failed to parse 'poll-interval' property\n"); 172 173 if (poll_interval_ms == 0) 174 return dev_err_probe(&pdev->dev, -EINVAL, "invalid 'poll-interval' value\n"); 175 176 device_property_read_u32(&pdev->dev, "debounce-delay-ms", &debounce_interval_ms); 177 device_property_read_u32(&pdev->dev, "settling-time-us", &keypad->settling_time_us); 178 179 keypad->current_code = -1; 180 keypad->debounce_code = -1; 181 keypad->debounce_threshold = DIV_ROUND_UP(debounce_interval_ms, poll_interval_ms); 182 183 err = charlieplex_keypad_init_gpio(pdev, keypad); 184 if (err) 185 return err; 186 187 input_dev->name = pdev->name; 188 input_dev->id.bustype = BUS_HOST; 189 190 err = matrix_keypad_build_keymap(NULL, NULL, keypad->nlines, 191 keypad->nlines, NULL, input_dev); 192 if (err) 193 return dev_err_probe(&pdev->dev, err, "failed to build keymap\n"); 194 195 if (device_property_read_bool(&pdev->dev, "autorepeat")) 196 __set_bit(EV_REP, input_dev->evbit); 197 198 input_set_capability(input_dev, EV_MSC, MSC_SCAN); 199 200 err = input_setup_polling(input_dev, charlieplex_keypad_poll); 201 if (err) 202 return dev_err_probe(&pdev->dev, err, "unable to set up polling\n"); 203 204 input_set_poll_interval(input_dev, poll_interval_ms); 205 206 input_set_drvdata(input_dev, keypad); 207 208 err = input_register_device(keypad->input_dev); 209 if (err) 210 return err; 211 212 return 0; 213 } 214 215 static const struct of_device_id charlieplex_keypad_dt_match[] = { 216 { .compatible = "gpio-charlieplex-keypad" }, 217 { } 218 }; 219 MODULE_DEVICE_TABLE(of, charlieplex_keypad_dt_match); 220 221 static struct platform_driver charlieplex_keypad_driver = { 222 .probe = charlieplex_keypad_probe, 223 .driver = { 224 .name = "charlieplex-keypad", 225 .of_match_table = charlieplex_keypad_dt_match, 226 }, 227 }; 228 module_platform_driver(charlieplex_keypad_driver); 229 230 MODULE_AUTHOR("Hugo Villeneuve <hvilleneuve@dimonoff.com>"); 231 MODULE_DESCRIPTION("GPIO driven charlieplex keypad driver"); 232 MODULE_LICENSE("GPL"); 233