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 gpiod_direction_output(line_gpios->desc[oline], 1); 81 82 if (keypad->settling_time_us) 83 fsleep(keypad->settling_time_us); 84 85 /* Read input on all other lines. */ 86 err = gpiod_get_array_value_cansleep(line_gpios->ndescs, line_gpios->desc, 87 line_gpios->info, values); 88 89 gpiod_direction_input(line_gpios->desc[oline]); 90 91 if (err) 92 return err; 93 94 for (unsigned int iline = 0; iline < keypad->nlines; iline++) { 95 if (iline == oline) 96 continue; /* Do not read active output line. */ 97 98 /* Check if GPIO is asserted. */ 99 if (test_bit(iline, values)) 100 return MATRIX_SCAN_CODE(oline, iline, 101 get_count_order(keypad->nlines)); 102 } 103 104 return 0; 105 } 106 107 static void charlieplex_keypad_poll(struct input_dev *input) 108 { 109 struct charlieplex_keypad *keypad = input_get_drvdata(input); 110 int code = 0; 111 112 for (unsigned int oline = 0; oline < keypad->nlines; oline++) { 113 code = charlieplex_keypad_scan_line(keypad, oline); 114 if (code != 0) 115 break; 116 } 117 118 if (code >= 0) 119 charlieplex_keypad_check_switch_change(input, code); 120 } 121 122 static int charlieplex_keypad_init_gpio(struct platform_device *pdev, 123 struct charlieplex_keypad *keypad) 124 { 125 char **pin_names; 126 char label[32]; 127 128 snprintf(label, sizeof(label), "%s-pin", pdev->name); 129 130 keypad->line_gpios = devm_gpiod_get_array(&pdev->dev, "line", GPIOD_IN); 131 if (IS_ERR(keypad->line_gpios)) 132 return PTR_ERR(keypad->line_gpios); 133 134 keypad->nlines = keypad->line_gpios->ndescs; 135 136 if (keypad->nlines > MATRIX_MAX_ROWS) 137 return -EINVAL; 138 139 pin_names = devm_kasprintf_strarray(&pdev->dev, label, keypad->nlines); 140 if (IS_ERR(pin_names)) 141 return PTR_ERR(pin_names); 142 143 for (unsigned int i = 0; i < keypad->line_gpios->ndescs; i++) 144 gpiod_set_consumer_name(keypad->line_gpios->desc[i], pin_names[i]); 145 146 return 0; 147 } 148 149 static int charlieplex_keypad_probe(struct platform_device *pdev) 150 { 151 struct charlieplex_keypad *keypad; 152 struct input_dev *input_dev; 153 unsigned int debounce_interval_ms = 5; 154 unsigned int poll_interval_ms; 155 int err; 156 157 keypad = devm_kzalloc(&pdev->dev, sizeof(*keypad), GFP_KERNEL); 158 if (!keypad) 159 return -ENOMEM; 160 161 input_dev = devm_input_allocate_device(&pdev->dev); 162 if (!input_dev) 163 return -ENOMEM; 164 165 keypad->input_dev = input_dev; 166 167 err = device_property_read_u32(&pdev->dev, "poll-interval", &poll_interval_ms); 168 if (err) 169 return dev_err_probe(&pdev->dev, err, 170 "failed to parse 'poll-interval' property\n"); 171 172 if (poll_interval_ms == 0) 173 return dev_err_probe(&pdev->dev, -EINVAL, "invalid 'poll-interval' value\n"); 174 175 device_property_read_u32(&pdev->dev, "debounce-delay-ms", &debounce_interval_ms); 176 device_property_read_u32(&pdev->dev, "settling-time-us", &keypad->settling_time_us); 177 178 keypad->current_code = -1; 179 keypad->debounce_code = -1; 180 keypad->debounce_threshold = DIV_ROUND_UP(debounce_interval_ms, poll_interval_ms); 181 182 err = charlieplex_keypad_init_gpio(pdev, keypad); 183 if (err) 184 return err; 185 186 input_dev->name = pdev->name; 187 input_dev->id.bustype = BUS_HOST; 188 189 err = matrix_keypad_build_keymap(NULL, NULL, keypad->nlines, 190 keypad->nlines, NULL, input_dev); 191 if (err) 192 return dev_err_probe(&pdev->dev, err, "failed to build keymap\n"); 193 194 if (device_property_read_bool(&pdev->dev, "autorepeat")) 195 __set_bit(EV_REP, input_dev->evbit); 196 197 input_set_capability(input_dev, EV_MSC, MSC_SCAN); 198 199 err = input_setup_polling(input_dev, charlieplex_keypad_poll); 200 if (err) 201 return dev_err_probe(&pdev->dev, err, "unable to set up polling\n"); 202 203 input_set_poll_interval(input_dev, poll_interval_ms); 204 205 input_set_drvdata(input_dev, keypad); 206 207 err = input_register_device(keypad->input_dev); 208 if (err) 209 return err; 210 211 return 0; 212 } 213 214 static const struct of_device_id charlieplex_keypad_dt_match[] = { 215 { .compatible = "gpio-charlieplex-keypad" }, 216 { } 217 }; 218 MODULE_DEVICE_TABLE(of, charlieplex_keypad_dt_match); 219 220 static struct platform_driver charlieplex_keypad_driver = { 221 .probe = charlieplex_keypad_probe, 222 .driver = { 223 .name = "charlieplex-keypad", 224 .of_match_table = charlieplex_keypad_dt_match, 225 }, 226 }; 227 module_platform_driver(charlieplex_keypad_driver); 228 229 MODULE_AUTHOR("Hugo Villeneuve <hvilleneuve@dimonoff.com>"); 230 MODULE_DESCRIPTION("GPIO driven charlieplex keypad driver"); 231 MODULE_LICENSE("GPL"); 232