diff options
Diffstat (limited to 'drivers/input/matrix-keymap.c')
-rw-r--r-- | drivers/input/matrix-keymap.c | 175 |
1 files changed, 98 insertions, 77 deletions
diff --git a/drivers/input/matrix-keymap.c b/drivers/input/matrix-keymap.c index de7992d55da2..db92c1ebfc59 100644 --- a/drivers/input/matrix-keymap.c +++ b/drivers/input/matrix-keymap.c @@ -17,15 +17,91 @@ * */ +#include <linux/device.h> #include <linux/kernel.h> #include <linux/types.h> #include <linux/input.h> #include <linux/of.h> #include <linux/export.h> -#include <linux/gfp.h> -#include <linux/slab.h> #include <linux/input/matrix_keypad.h> +static bool matrix_keypad_map_key(struct input_dev *input_dev, + unsigned int rows, unsigned int cols, + unsigned int row_shift, unsigned int key) +{ + unsigned char *keymap = input_dev->keycode; + unsigned int row = KEY_ROW(key); + unsigned int col = KEY_COL(key); + unsigned short code = KEY_VAL(key); + + if (row >= rows || col >= cols) { + dev_err(input_dev->dev.parent, + "%s: invalid keymap entry 0x%x (row: %d, col: %d, rows: %d, cols: %d)\n", + __func__, key, row, col, rows, cols); + return false; + } + + keymap[MATRIX_SCAN_CODE(row, col, row_shift)] = code; + __set_bit(code, input_dev->keybit); + + return true; +} + +#ifdef CONFIG_OF +static int matrix_keypad_parse_of_keymap(const char *propname, + unsigned int rows, unsigned int cols, + struct input_dev *input_dev) +{ + struct device *dev = input_dev->dev.parent; + struct device_node *np = dev->of_node; + unsigned int row_shift = get_count_order(cols); + unsigned int max_keys = rows << row_shift; + unsigned int proplen, i, size; + const __be32 *prop; + + if (!np) + return -ENOENT; + + if (!propname) + propname = "linux,keymap"; + + prop = of_get_property(np, propname, &proplen); + if (!prop) { + dev_err(dev, "OF: %s property not defined in %s\n", + propname, np->full_name); + return -ENOENT; + } + + if (proplen % sizeof(u32)) { + dev_err(dev, "OF: Malformed keycode property %s in %s\n", + propname, np->full_name); + return -EINVAL; + } + + size = proplen / sizeof(u32); + if (size > max_keys) { + dev_err(dev, "OF: %s size overflow\n", propname); + return -EINVAL; + } + + for (i = 0; i < size; i++) { + unsigned int key = be32_to_cpup(prop + i); + + if (!matrix_keypad_map_key(input_dev, rows, cols, + row_shift, key)) + return -EINVAL; + } + + return 0; +} +#else +static int matrix_keypad_parse_of_keymap(const char *propname, + unsigned int rows, unsigned int cols, + struct input_dev *input_dev) +{ + return -ENOSYS; +} +#endif /** * matrix_keypad_build_keymap - convert platform keymap into matrix keymap @@ -41,6 +117,13 @@ * This function converts platform keymap (encoded with KEY() macro) into * an array of keycodes that is suitable for using in a standard matrix * keyboard driver that uses row and col as indices. + * + * If @keymap_data is not supplied and device tree support is enabled + * it will attempt load the keymap from property specified by @keymap_name + * argument (or "linux,keymap" if @keymap_name is %NULL). + * + * Callers are expected to set up input_dev->dev.parent before calling this + * function. */ int matrix_keypad_build_keymap(const struct matrix_keymap_data *keymap_data, const char *keymap_name, @@ -50,6 +133,7 @@ int matrix_keypad_build_keymap(const struct matrix_keymap_data *keymap_data, { unsigned int row_shift = get_count_order(cols); int i; + int error; input_dev->keycode = keymap; input_dev->keycodesize = sizeof(*keymap); @@ -57,86 +141,23 @@ int matrix_keypad_build_keymap(const struct matrix_keymap_data *keymap_data, __set_bit(EV_KEY, input_dev->evbit); - for (i = 0; i < keymap_data->keymap_size; i++) { - unsigned int key = keymap_data->keymap[i]; - unsigned int row = KEY_ROW(key); - unsigned int col = KEY_COL(key); - unsigned short code = KEY_VAL(key); + if (keymap_data) { + for (i = 0; i < keymap_data->keymap_size; i++) { + unsigned int key = keymap_data->keymap[i]; - if (row >= rows || col >= cols) { - dev_err(input_dev->dev.parent, - "%s: invalid keymap entry %d (row: %d, col: %d, rows: %d, cols: %d)\n", - __func__, i, row, col, rows, cols); - return -EINVAL; + if (!matrix_keypad_map_key(input_dev, rows, cols, + row_shift, key)) + return -EINVAL; } - - keymap[MATRIX_SCAN_CODE(row, col, row_shift)] = code; - __set_bit(code, input_dev->keybit); + } else { + error = matrix_keypad_parse_of_keymap(keymap_name, rows, cols, + input_dev); + if (error) + return error; } + __clear_bit(KEY_RESERVED, input_dev->keybit); return 0; } EXPORT_SYMBOL(matrix_keypad_build_keymap); - -#ifdef CONFIG_OF -struct matrix_keymap_data * -matrix_keyboard_of_fill_keymap(struct device_node *np, - const char *propname) -{ - struct matrix_keymap_data *kd; - u32 *keymap; - int proplen, i; - const __be32 *prop; - - if (!np) - return NULL; - - if (!propname) - propname = "linux,keymap"; - - prop = of_get_property(np, propname, &proplen); - if (!prop) - return NULL; - - if (proplen % sizeof(u32)) { - pr_warn("Malformed keymap property %s in %s\n", - propname, np->full_name); - return NULL; - } - - kd = kzalloc(sizeof(*kd), GFP_KERNEL); - if (!kd) - return NULL; - - kd->keymap = keymap = kzalloc(proplen, GFP_KERNEL); - if (!kd->keymap) { - kfree(kd); - return NULL; - } - - kd->keymap_size = proplen / sizeof(u32); - - for (i = 0; i < kd->keymap_size; i++) { - u32 tmp = be32_to_cpup(prop + i); - int key_code, row, col; - - row = (tmp >> 24) & 0xff; - col = (tmp >> 16) & 0xff; - key_code = tmp & 0xffff; - keymap[i] = KEY(row, col, key_code); - } - - return kd; -} -EXPORT_SYMBOL_GPL(matrix_keyboard_of_fill_keymap); - -void matrix_keyboard_of_free_keymap(const struct matrix_keymap_data *kd) -{ - if (kd) { - kfree(kd->keymap); - kfree(kd); - } -} -EXPORT_SYMBOL_GPL(matrix_keyboard_of_free_keymap); -#endif |