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 de7992d55da..db92c1ebfc5 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  |