Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * nvec_kbd: keyboard driver for a NVIDIA compliant embedded controller
0004  *
0005  * Copyright (C) 2011 The AC100 Kernel Team <ac100@lists.launchpad.net>
0006  *
0007  * Authors:  Pierre-Hugues Husson <phhusson@free.fr>
0008  *           Marc Dietrich <marvin24@gmx.de>
0009  */
0010 
0011 #include <linux/module.h>
0012 #include <linux/slab.h>
0013 #include <linux/input.h>
0014 #include <linux/delay.h>
0015 #include <linux/platform_device.h>
0016 
0017 #include "nvec-keytable.h"
0018 #include "nvec.h"
0019 
0020 enum kbd_subcmds {
0021     CNFG_WAKE = 3,
0022     CNFG_WAKE_KEY_REPORTING,
0023     SET_LEDS = 0xed,
0024     ENABLE_KBD = 0xf4,
0025     DISABLE_KBD,
0026 };
0027 
0028 static unsigned char keycodes[ARRAY_SIZE(code_tab_102us)
0029                   + ARRAY_SIZE(extcode_tab_us102)];
0030 
0031 struct nvec_keys {
0032     struct input_dev *input;
0033     struct notifier_block notifier;
0034     struct nvec_chip *nvec;
0035     bool caps_lock;
0036 };
0037 
0038 static struct nvec_keys keys_dev;
0039 
0040 static void nvec_kbd_toggle_led(void)
0041 {
0042     char buf[] = { NVEC_KBD, SET_LEDS, 0 };
0043 
0044     keys_dev.caps_lock = !keys_dev.caps_lock;
0045 
0046     if (keys_dev.caps_lock)
0047         /* should be BIT(0) only, firmware bug? */
0048         buf[2] = BIT(0) | BIT(1) | BIT(2);
0049 
0050     nvec_write_async(keys_dev.nvec, buf, sizeof(buf));
0051 }
0052 
0053 static int nvec_keys_notifier(struct notifier_block *nb,
0054                   unsigned long event_type, void *data)
0055 {
0056     int code, state;
0057     unsigned char *msg = data;
0058 
0059     if (event_type == NVEC_KB_EVT) {
0060         int _size = (msg[0] & (3 << 5)) >> 5;
0061 
0062 /* power on/off button */
0063         if (_size == NVEC_VAR_SIZE)
0064             return NOTIFY_STOP;
0065 
0066         if (_size == NVEC_3BYTES)
0067             msg++;
0068 
0069         code = msg[1] & 0x7f;
0070         state = msg[1] & 0x80;
0071 
0072         if (code_tabs[_size][code] == KEY_CAPSLOCK && state)
0073             nvec_kbd_toggle_led();
0074 
0075         input_report_key(keys_dev.input, code_tabs[_size][code],
0076                  !state);
0077         input_sync(keys_dev.input);
0078 
0079         return NOTIFY_STOP;
0080     }
0081 
0082     return NOTIFY_DONE;
0083 }
0084 
0085 static int nvec_kbd_event(struct input_dev *dev, unsigned int type,
0086               unsigned int code, int value)
0087 {
0088     struct nvec_chip *nvec = keys_dev.nvec;
0089     char buf[] = { NVEC_KBD, SET_LEDS, 0 };
0090 
0091     if (type == EV_REP)
0092         return 0;
0093 
0094     if (type != EV_LED)
0095         return -1;
0096 
0097     if (code != LED_CAPSL)
0098         return -1;
0099 
0100     buf[2] = !!value;
0101     nvec_write_async(nvec, buf, sizeof(buf));
0102 
0103     return 0;
0104 }
0105 
0106 static int nvec_kbd_probe(struct platform_device *pdev)
0107 {
0108     struct nvec_chip *nvec = dev_get_drvdata(pdev->dev.parent);
0109     int i, j, err;
0110     struct input_dev *idev;
0111     char    clear_leds[] = { NVEC_KBD, SET_LEDS, 0 },
0112         enable_kbd[] = { NVEC_KBD, ENABLE_KBD },
0113         cnfg_wake[] = { NVEC_KBD, CNFG_WAKE, true, true },
0114         cnfg_wake_key_reporting[] = { NVEC_KBD, CNFG_WAKE_KEY_REPORTING,
0115                         true };
0116 
0117     j = 0;
0118 
0119     for (i = 0; i < ARRAY_SIZE(code_tab_102us); ++i)
0120         keycodes[j++] = code_tab_102us[i];
0121 
0122     for (i = 0; i < ARRAY_SIZE(extcode_tab_us102); ++i)
0123         keycodes[j++] = extcode_tab_us102[i];
0124 
0125     idev = devm_input_allocate_device(&pdev->dev);
0126     if (!idev)
0127         return -ENOMEM;
0128     idev->name = "nvec keyboard";
0129     idev->phys = "nvec";
0130     idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP) | BIT_MASK(EV_LED);
0131     idev->ledbit[0] = BIT_MASK(LED_CAPSL);
0132     idev->event = nvec_kbd_event;
0133     idev->keycode = keycodes;
0134     idev->keycodesize = sizeof(unsigned char);
0135     idev->keycodemax = ARRAY_SIZE(keycodes);
0136 
0137     for (i = 0; i < ARRAY_SIZE(keycodes); ++i)
0138         set_bit(keycodes[i], idev->keybit);
0139 
0140     clear_bit(0, idev->keybit);
0141     err = input_register_device(idev);
0142     if (err)
0143         return err;
0144 
0145     keys_dev.input = idev;
0146     keys_dev.notifier.notifier_call = nvec_keys_notifier;
0147     keys_dev.nvec = nvec;
0148     nvec_register_notifier(nvec, &keys_dev.notifier, 0);
0149 
0150     /* Enable keyboard */
0151     nvec_write_async(nvec, enable_kbd, 2);
0152 
0153     /* configures wake on special keys */
0154     nvec_write_async(nvec, cnfg_wake, 4);
0155     /* enable wake key reporting */
0156     nvec_write_async(nvec, cnfg_wake_key_reporting, 3);
0157 
0158     /* Disable caps lock LED */
0159     nvec_write_async(nvec, clear_leds, sizeof(clear_leds));
0160 
0161     return 0;
0162 }
0163 
0164 static int nvec_kbd_remove(struct platform_device *pdev)
0165 {
0166     struct nvec_chip *nvec = dev_get_drvdata(pdev->dev.parent);
0167     char disable_kbd[] = { NVEC_KBD, DISABLE_KBD },
0168          uncnfg_wake_key_reporting[] = { NVEC_KBD, CNFG_WAKE_KEY_REPORTING,
0169                         false };
0170     nvec_write_async(nvec, uncnfg_wake_key_reporting, 3);
0171     nvec_write_async(nvec, disable_kbd, 2);
0172     nvec_unregister_notifier(nvec, &keys_dev.notifier);
0173 
0174     return 0;
0175 }
0176 
0177 static struct platform_driver nvec_kbd_driver = {
0178     .probe  = nvec_kbd_probe,
0179     .remove = nvec_kbd_remove,
0180     .driver = {
0181         .name = "nvec-kbd",
0182     },
0183 };
0184 
0185 module_platform_driver(nvec_kbd_driver);
0186 
0187 MODULE_AUTHOR("Marc Dietrich <marvin24@gmx.de>");
0188 MODULE_DESCRIPTION("NVEC keyboard driver");
0189 MODULE_ALIAS("platform:nvec-kbd");
0190 MODULE_LICENSE("GPL");