xref: /linux/drivers/input/matrix-keymap.c (revision 3ea5eb68b9d624935108b5e696859304edfac202)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Helpers for matrix keyboard bindings
4  *
5  * Copyright (C) 2012 Google, Inc
6  *
7  * Author:
8  *	Olof Johansson <olof@lixom.net>
9  */
10 
11 #include <linux/device.h>
12 #include <linux/export.h>
13 #include <linux/gfp.h>
14 #include <linux/input.h>
15 #include <linux/input/matrix_keypad.h>
16 #include <linux/kernel.h>
17 #include <linux/module.h>
18 #include <linux/property.h>
19 #include <linux/slab.h>
20 #include <linux/types.h>
21 
22 static bool matrix_keypad_map_key(struct input_dev *input_dev,
23 				  unsigned int rows, unsigned int cols,
24 				  unsigned int row_shift, unsigned int key)
25 {
26 	unsigned short *keymap = input_dev->keycode;
27 	unsigned int row = KEY_ROW(key);
28 	unsigned int col = KEY_COL(key);
29 	unsigned short code = KEY_VAL(key);
30 
31 	if (row >= rows || col >= cols) {
32 		dev_err(input_dev->dev.parent,
33 			"%s: invalid keymap entry 0x%x (row: %d, col: %d, rows: %d, cols: %d)\n",
34 			__func__, key, row, col, rows, cols);
35 		return false;
36 	}
37 
38 	keymap[MATRIX_SCAN_CODE(row, col, row_shift)] = code;
39 	__set_bit(code, input_dev->keybit);
40 
41 	return true;
42 }
43 
44 /**
45  * matrix_keypad_parse_properties() - Read properties of matrix keypad
46  *
47  * @dev: Device containing properties
48  * @rows: Returns number of matrix rows
49  * @cols: Returns number of matrix columns
50  * @return 0 if OK, <0 on error
51  */
52 int matrix_keypad_parse_properties(struct device *dev,
53 				   unsigned int *rows, unsigned int *cols)
54 {
55 	*rows = *cols = 0;
56 
57 	device_property_read_u32(dev, "keypad,num-rows", rows);
58 	device_property_read_u32(dev, "keypad,num-columns", cols);
59 
60 	if (!*rows || !*cols) {
61 		dev_err(dev, "number of keypad rows/columns not specified\n");
62 		return -EINVAL;
63 	}
64 
65 	return 0;
66 }
67 EXPORT_SYMBOL_GPL(matrix_keypad_parse_properties);
68 
69 static int matrix_keypad_parse_keymap(const char *propname,
70 				      unsigned int rows, unsigned int cols,
71 				      struct input_dev *input_dev)
72 {
73 	struct device *dev = input_dev->dev.parent;
74 	unsigned int row_shift = get_count_order(cols);
75 	unsigned int max_keys = rows << row_shift;
76 	int i;
77 	int size;
78 	int error;
79 
80 	if (!propname)
81 		propname = "linux,keymap";
82 
83 	size = device_property_count_u32(dev, propname);
84 	if (size <= 0) {
85 		dev_err(dev, "missing or malformed property %s: %d\n",
86 			propname, size);
87 		return size < 0 ? size : -EINVAL;
88 	}
89 
90 	if (size > max_keys) {
91 		dev_err(dev, "%s size overflow (%d vs max %u)\n",
92 			propname, size, max_keys);
93 		return -EINVAL;
94 	}
95 
96 	u32 *keys __free(kfree) = kmalloc_array(size, sizeof(*keys), GFP_KERNEL);
97 	if (!keys)
98 		return -ENOMEM;
99 
100 	error = device_property_read_u32_array(dev, propname, keys, size);
101 	if (error) {
102 		dev_err(dev, "failed to read %s property: %d\n",
103 			propname, error);
104 		return error;
105 	}
106 
107 	for (i = 0; i < size; i++) {
108 		if (!matrix_keypad_map_key(input_dev, rows, cols,
109 					   row_shift, keys[i]))
110 			return -EINVAL;
111 	}
112 
113 	return 0;
114 }
115 
116 /**
117  * matrix_keypad_build_keymap - convert platform keymap into matrix keymap
118  * @keymap_data: keymap supplied by the platform code
119  * @keymap_name: name of device tree property containing keymap (if device
120  *	tree support is enabled).
121  * @rows: number of rows in target keymap array
122  * @cols: number of cols in target keymap array
123  * @keymap: expanded version of keymap that is suitable for use by
124  * matrix keyboard driver
125  * @input_dev: input devices for which we are setting up the keymap
126  *
127  * This function converts platform keymap (encoded with KEY() macro) into
128  * an array of keycodes that is suitable for using in a standard matrix
129  * keyboard driver that uses row and col as indices.
130  *
131  * If @keymap_data is not supplied and device tree support is enabled
132  * it will attempt load the keymap from property specified by @keymap_name
133  * argument (or "linux,keymap" if @keymap_name is %NULL).
134  *
135  * If @keymap is %NULL the function will automatically allocate managed
136  * block of memory to store the keymap. This memory will be associated with
137  * the parent device and automatically freed when device unbinds from the
138  * driver.
139  *
140  * Callers are expected to set up input_dev->dev.parent before calling this
141  * function.
142  */
143 int matrix_keypad_build_keymap(const struct matrix_keymap_data *keymap_data,
144 			       const char *keymap_name,
145 			       unsigned int rows, unsigned int cols,
146 			       unsigned short *keymap,
147 			       struct input_dev *input_dev)
148 {
149 	unsigned int row_shift = get_count_order(cols);
150 	size_t max_keys = rows << row_shift;
151 	int i;
152 	int error;
153 
154 	if (WARN_ON(!input_dev->dev.parent))
155 		return -EINVAL;
156 
157 	if (!keymap) {
158 		keymap = devm_kcalloc(input_dev->dev.parent,
159 				      max_keys, sizeof(*keymap),
160 				      GFP_KERNEL);
161 		if (!keymap) {
162 			dev_err(input_dev->dev.parent,
163 				"Unable to allocate memory for keymap");
164 			return -ENOMEM;
165 		}
166 	}
167 
168 	input_dev->keycode = keymap;
169 	input_dev->keycodesize = sizeof(*keymap);
170 	input_dev->keycodemax = max_keys;
171 
172 	__set_bit(EV_KEY, input_dev->evbit);
173 
174 	if (keymap_data) {
175 		for (i = 0; i < keymap_data->keymap_size; i++) {
176 			unsigned int key = keymap_data->keymap[i];
177 
178 			if (!matrix_keypad_map_key(input_dev, rows, cols,
179 						   row_shift, key))
180 				return -EINVAL;
181 		}
182 	} else {
183 		error = matrix_keypad_parse_keymap(keymap_name, rows, cols,
184 						   input_dev);
185 		if (error)
186 			return error;
187 	}
188 
189 	__clear_bit(KEY_RESERVED, input_dev->keybit);
190 
191 	return 0;
192 }
193 EXPORT_SYMBOL(matrix_keypad_build_keymap);
194 
195 MODULE_DESCRIPTION("Helpers for matrix keyboard bindings");
196 MODULE_LICENSE("GPL");
197