diff options
Diffstat (limited to 'drivers/input/keyboard/cros_ec_keyb.c')
| -rw-r--r-- | drivers/input/keyboard/cros_ec_keyb.c | 192 |
1 files changed, 123 insertions, 69 deletions
diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c index fc02c540636e..1c6b0461dc35 100644 --- a/drivers/input/keyboard/cros_ec_keyb.c +++ b/drivers/input/keyboard/cros_ec_keyb.c @@ -12,9 +12,11 @@ // expensive. #include <linux/module.h> +#include <linux/acpi.h> #include <linux/bitops.h> #include <linux/i2c.h> #include <linux/input.h> +#include <linux/input/vivaldi-fmap.h> #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/notifier.h> @@ -25,9 +27,7 @@ #include <linux/platform_data/cros_ec_commands.h> #include <linux/platform_data/cros_ec_proto.h> -#include <asm/unaligned.h> - -#define MAX_NUM_TOP_ROW_KEYS 15 +#include <linux/unaligned.h> /** * struct cros_ec_keyb - Structure representing EC keyboard device @@ -35,7 +35,6 @@ * @rows: Number of rows in the keypad * @cols: Number of columns in the keypad * @row_shift: log2 or number of rows, rounded up - * @keymap_data: Matrix keymap data used to convert to keyscan values * @ghost_filter: true to enable the matrix key-ghosting filter * @valid_keys: bitmap of existing keys for each matrix column * @old_kb_state: bitmap of keys pressed last scan @@ -44,15 +43,12 @@ * @idev: The input device for the matrix keys. * @bs_idev: The input device for non-matrix buttons and switches (or NULL). * @notifier: interrupt event notifier for transport devices - * @function_row_physmap: An array of the encoded rows/columns for the top - * row function keys, in an order from left to right - * @num_function_row_keys: The number of top row keys in a custom keyboard + * @vdata: vivaldi function row data */ struct cros_ec_keyb { unsigned int rows; unsigned int cols; int row_shift; - const struct matrix_keymap_data *keymap_data; bool ghost_filter; uint8_t *valid_keys; uint8_t *old_kb_state; @@ -64,8 +60,7 @@ struct cros_ec_keyb { struct input_dev *bs_idev; struct notifier_block notifier; - u16 function_row_physmap[MAX_NUM_TOP_ROW_KEYS]; - size_t num_function_row_keys; + struct vivaldi_data vdata; }; /** @@ -103,6 +98,21 @@ static const struct cros_ec_bs_map cros_ec_keyb_bs[] = { .code = KEY_VOLUMEDOWN, .bit = EC_MKBP_VOL_DOWN, }, + { + .ev_type = EV_KEY, + .code = KEY_BRIGHTNESSUP, + .bit = EC_MKBP_BRI_UP, + }, + { + .ev_type = EV_KEY, + .code = KEY_BRIGHTNESSDOWN, + .bit = EC_MKBP_BRI_DOWN, + }, + { + .ev_type = EV_KEY, + .code = KEY_SCREENLOCK, + .bit = EC_MKBP_SCREEN_LOCK, + }, /* Switches */ { @@ -251,6 +261,12 @@ static int cros_ec_keyb_work(struct notifier_block *nb, case EC_MKBP_EVENT_KEY_MATRIX: pm_wakeup_event(ckdev->dev, 0); + if (!ckdev->idev) { + dev_warn_once(ckdev->dev, + "Unexpected key matrix event\n"); + return NOTIFY_OK; + } + if (ckdev->ec->event_size != ckdev->cols) { dev_err(ckdev->dev, "Discarded incomplete key matrix event.\n"); @@ -418,7 +434,7 @@ static int cros_ec_keyb_query_switches(struct cros_ec_keyb *ckdev) * * Returns 0 if no error or -error upon error. */ -static __maybe_unused int cros_ec_keyb_resume(struct device *dev) +static int cros_ec_keyb_resume(struct device *dev) { struct cros_ec_keyb *ckdev = dev_get_drvdata(dev); @@ -439,10 +455,13 @@ static __maybe_unused int cros_ec_keyb_resume(struct device *dev) * but the ckdev->bs_idev will remain NULL when this function exits. * * @ckdev: The keyboard device + * @expect_buttons_switches: Indicates that EC must report button and/or + * switch events * * Returns 0 if no error or -error upon error. */ -static int cros_ec_keyb_register_bs(struct cros_ec_keyb *ckdev) +static int cros_ec_keyb_register_bs(struct cros_ec_keyb *ckdev, + bool expect_buttons_switches) { struct cros_ec_device *ec_dev = ckdev->ec; struct device *dev = ckdev->dev; @@ -469,7 +488,7 @@ static int cros_ec_keyb_register_bs(struct cros_ec_keyb *ckdev) switches = get_unaligned_le32(&event_data.switches); if (!buttons && !switches) - return 0; + return expect_buttons_switches ? -EINVAL : 0; /* * We call the non-matrix buttons/switches 'input1', if present. @@ -519,8 +538,52 @@ static int cros_ec_keyb_register_bs(struct cros_ec_keyb *ckdev) return 0; } +static void cros_ec_keyb_parse_vivaldi_physmap(struct cros_ec_keyb *ckdev) +{ + u32 *physmap = ckdev->vdata.function_row_physmap; + unsigned int row, col, scancode; + int n_physmap; + int error; + int i; + + n_physmap = device_property_count_u32(ckdev->dev, + "function-row-physmap"); + if (n_physmap <= 0) + return; + + if (n_physmap >= VIVALDI_MAX_FUNCTION_ROW_KEYS) { + dev_warn(ckdev->dev, + "only up to %d top row keys is supported (%d specified)\n", + VIVALDI_MAX_FUNCTION_ROW_KEYS, n_physmap); + n_physmap = VIVALDI_MAX_FUNCTION_ROW_KEYS; + } + + error = device_property_read_u32_array(ckdev->dev, + "function-row-physmap", + physmap, n_physmap); + if (error) { + dev_warn(ckdev->dev, + "failed to parse function-row-physmap property: %d\n", + error); + return; + } + + /* + * Convert (in place) from row/column encoding to matrix "scancode" + * used by the driver. + */ + for (i = 0; i < n_physmap; i++) { + row = KEY_ROW(physmap[i]); + col = KEY_COL(physmap[i]); + scancode = MATRIX_SCAN_CODE(row, col, ckdev->row_shift); + physmap[i] = scancode; + } + + ckdev->vdata.num_function_row_keys = n_physmap; +} + /** - * cros_ec_keyb_register_bs - Register matrix keys + * cros_ec_keyb_register_matrix - Register matrix keys * * Handles all the bits of the keyboard driver related to matrix keys. * @@ -535,11 +598,6 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev) struct input_dev *idev; const char *phys; int err; - struct property *prop; - const __be32 *p; - u16 *physmap; - u32 key_pos; - int row, col; err = matrix_keypad_parse_properties(dev, &ckdev->rows, &ckdev->cols); if (err) @@ -574,7 +632,7 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev) idev->id.product = 0; idev->dev.parent = dev; - ckdev->ghost_filter = of_property_read_bool(dev->of_node, + ckdev->ghost_filter = device_property_read_bool(dev, "google,needs-ghost-filter"); err = matrix_keypad_build_keymap(NULL, NULL, ckdev->rows, ckdev->cols, @@ -590,21 +648,7 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev) input_set_drvdata(idev, ckdev); ckdev->idev = idev; cros_ec_keyb_compute_valid_keys(ckdev); - - physmap = ckdev->function_row_physmap; - of_property_for_each_u32(dev->of_node, "function-row-physmap", - prop, p, key_pos) { - if (ckdev->num_function_row_keys == MAX_NUM_TOP_ROW_KEYS) { - dev_warn(dev, "Only support up to %d top row keys\n", - MAX_NUM_TOP_ROW_KEYS); - break; - } - row = KEY_ROW(key_pos); - col = KEY_COL(key_pos); - *physmap = MATRIX_SCAN_CODE(row, col, ckdev->row_shift); - physmap++; - ckdev->num_function_row_keys++; - } + cros_ec_keyb_parse_vivaldi_physmap(ckdev); err = input_register_device(ckdev->idev); if (err) { @@ -619,18 +663,10 @@ static ssize_t function_row_physmap_show(struct device *dev, struct device_attribute *attr, char *buf) { - ssize_t size = 0; - int i; - struct cros_ec_keyb *ckdev = dev_get_drvdata(dev); - u16 *physmap = ckdev->function_row_physmap; + const struct cros_ec_keyb *ckdev = dev_get_drvdata(dev); + const struct vivaldi_data *data = &ckdev->vdata; - for (i = 0; i < ckdev->num_function_row_keys; i++) - size += scnprintf(buf + size, PAGE_SIZE - size, - "%s%02X", size ? " " : "", physmap[i]); - if (size) - size += scnprintf(buf + size, PAGE_SIZE - size, "\n"); - - return size; + return vivaldi_function_row_physmap_show(data, buf); } static DEVICE_ATTR_RO(function_row_physmap); @@ -648,27 +684,39 @@ static umode_t cros_ec_keyb_attr_is_visible(struct kobject *kobj, struct cros_ec_keyb *ckdev = dev_get_drvdata(dev); if (attr == &dev_attr_function_row_physmap.attr && - !ckdev->num_function_row_keys) + !ckdev->vdata.num_function_row_keys) return 0; return attr->mode; } -static const struct attribute_group cros_ec_keyb_attr_group = { +static const struct attribute_group cros_ec_keyb_group = { .is_visible = cros_ec_keyb_attr_is_visible, .attrs = cros_ec_keyb_attrs, }; - +__ATTRIBUTE_GROUPS(cros_ec_keyb); static int cros_ec_keyb_probe(struct platform_device *pdev) { - struct cros_ec_device *ec = dev_get_drvdata(pdev->dev.parent); + struct cros_ec_device *ec; struct device *dev = &pdev->dev; struct cros_ec_keyb *ckdev; + bool buttons_switches_only = device_get_match_data(dev); int err; - if (!dev->of_node) - return -ENODEV; + /* + * If the parent ec device has not been probed yet, defer the probe of + * this keyboard/button driver until later. + */ + ec = dev_get_drvdata(pdev->dev.parent); + if (!ec) + return -EPROBE_DEFER; + /* + * Even if the cros_ec_device pointer is available, still need to check + * if the device is fully registered before using it. + */ + if (!cros_ec_device_registered(ec)) + return -EPROBE_DEFER; ckdev = devm_kzalloc(dev, sizeof(*ckdev), GFP_KERNEL); if (!ckdev) @@ -678,24 +726,21 @@ static int cros_ec_keyb_probe(struct platform_device *pdev) ckdev->dev = dev; dev_set_drvdata(dev, ckdev); - err = cros_ec_keyb_register_matrix(ckdev); - if (err) { - dev_err(dev, "cannot register matrix inputs: %d\n", err); - return err; + if (!buttons_switches_only) { + err = cros_ec_keyb_register_matrix(ckdev); + if (err) { + dev_err(dev, "cannot register matrix inputs: %d\n", + err); + return err; + } } - err = cros_ec_keyb_register_bs(ckdev); + err = cros_ec_keyb_register_bs(ckdev, buttons_switches_only); if (err) { dev_err(dev, "cannot register non-matrix inputs: %d\n", err); return err; } - err = devm_device_add_group(dev, &cros_ec_keyb_attr_group); - if (err) { - dev_err(dev, "failed to create attributes. err=%d\n", err); - return err; - } - ckdev->notifier.notifier_call = cros_ec_keyb_work; err = blocking_notifier_chain_register(&ckdev->ec->event_notifier, &ckdev->notifier); @@ -708,33 +753,42 @@ static int cros_ec_keyb_probe(struct platform_device *pdev) return 0; } -static int cros_ec_keyb_remove(struct platform_device *pdev) +static void cros_ec_keyb_remove(struct platform_device *pdev) { struct cros_ec_keyb *ckdev = dev_get_drvdata(&pdev->dev); blocking_notifier_chain_unregister(&ckdev->ec->event_notifier, &ckdev->notifier); - - return 0; } +#ifdef CONFIG_ACPI +static const struct acpi_device_id cros_ec_keyb_acpi_match[] = { + { "GOOG0007", true }, + { } +}; +MODULE_DEVICE_TABLE(acpi, cros_ec_keyb_acpi_match); +#endif + #ifdef CONFIG_OF static const struct of_device_id cros_ec_keyb_of_match[] = { { .compatible = "google,cros-ec-keyb" }, - {}, + { .compatible = "google,cros-ec-keyb-switches", .data = (void *)true }, + {} }; MODULE_DEVICE_TABLE(of, cros_ec_keyb_of_match); #endif -static SIMPLE_DEV_PM_OPS(cros_ec_keyb_pm_ops, NULL, cros_ec_keyb_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(cros_ec_keyb_pm_ops, NULL, cros_ec_keyb_resume); static struct platform_driver cros_ec_keyb_driver = { .probe = cros_ec_keyb_probe, .remove = cros_ec_keyb_remove, .driver = { .name = "cros-ec-keyb", + .dev_groups = cros_ec_keyb_groups, .of_match_table = of_match_ptr(cros_ec_keyb_of_match), - .pm = &cros_ec_keyb_pm_ops, + .acpi_match_table = ACPI_PTR(cros_ec_keyb_acpi_match), + .pm = pm_sleep_ptr(&cros_ec_keyb_pm_ops), }, }; |
