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