MLK-14619 input: keyboard: rpmsg-keys: add rpmsg-keys driver
authorRobin Gong <yibin.gong@nxp.com>
Fri, 17 Mar 2017 02:14:13 +0000 (10:14 +0800)
committerNitin Garg <nitin.garg@nxp.com>
Mon, 19 Mar 2018 20:21:55 +0000 (15:21 -0500)
Add rpmsg-keys driver on i.mx7ulp-evk board since vol+/vol- keys
are connected on m4 side and have to get the status of keys by
rpmsg.

Signed-off-by: Robin Gong <yibin.gong@nxp.com>
[Irina: updated for 4.9 APIs]
Signed-off-by: Irina Tirdea <irina.tirdea@nxp.com>
Documentation/devicetree/bindings/input/rpmsg-keys.txt [new file with mode: 0644]
arch/arm/boot/dts/imx7ulp-evk.dts
arch/arm/configs/imx_v7_defconfig
drivers/input/keyboard/Kconfig
drivers/input/keyboard/Makefile
drivers/input/keyboard/rpmsg-keys.c [new file with mode: 0644]
drivers/rpmsg/imx_rpmsg.c
include/linux/imx_rpmsg.h

diff --git a/Documentation/devicetree/bindings/input/rpmsg-keys.txt b/Documentation/devicetree/bindings/input/rpmsg-keys.txt
new file mode 100644 (file)
index 0000000..d927980
--- /dev/null
@@ -0,0 +1,33 @@
+Device-Tree bindings for input/keyboard/rpmsg-keys.c keys driver over
+rpmsg. On i.mx7ULP keys are connected on M4 side, so rpmsg-keys driver
+needed to get the key status from M4 side by rpmsg.
+
+Required properties:
+       - compatible = "fsl,rpmsg-keys";
+
+Each button/key looked as the sub node:
+Required properties:
+       - label: the key name
+       - linux,code: the key value defined in
+               include/dt-bindings/input/input.h
+Optional property:
+       - rpmsg-key,wakeup: wakeup feature, the keys can wakeup from
+       suspend if the keys with this property pressed.
+
+Example nodes:
+       rpmsg_keys: rpmsg-keys {
+               compatible = "fsl,rpmsg-keys";
+
+               volume-up {
+                       label = "Volume Up";
+                       rpmsg-key,wakeup;
+                       linux,code = <KEY_VOLUMEUP>;
+               };
+
+               volume-down {
+                       label = "Volume Down";
+                       rpmsg-key,wakeup;
+                       linux,code = <KEY_VOLUMEDOWN>;
+               };
+       };
+
index 5bcde52..53b4b95 100644 (file)
@@ -10,6 +10,7 @@
 /dts-v1/;
 
 #include "imx7ulp.dtsi"
+#include <dt-bindings/input/input.h>
 
 / {
        model = "NXP i.MX7ULP EVK";
                status = "okay";
        };
 
+       rpmsg_keys: rpmsg-keys {
+               compatible = "fsl,rpmsg-keys";
+
+               volume-up {
+                       label = "Volume Up";
+                       linux,code = <KEY_VOLUMEUP>;
+               };
+
+               volume-down {
+                       label = "Volume Down";
+                       linux,code = <KEY_VOLUMEDOWN>;
+               };
+       };
+
        sound-rpmsg {
                compatible = "fsl,imx-audio-rpmsg";
                model = "rpmsg-audio";
         * --0x9FF20000~0x9FF2FFFF: pm
         * --0x9FF30000~0x9FF3FFFF: audio
         * --0x9FF40000~0x9FF4FFFF: virtual tty
+        * --0x9FF50000~0x9FF5FFFF: keys
         */
-       vdev-nums = <5>;
-       reg = <0x9FF00000 0x50000>;
+       vdev-nums = <6>;
+       reg = <0x9FF00000 0x60000>;
        status = "okay";
 };
 
index 10e6bd1..7075478 100644 (file)
@@ -155,6 +155,7 @@ CONFIG_BCMDHD_NVRAM_PATH="/lib/firmware/bcm/ZP_BCM4339/bcmdhd.ZP.OOB.cal"
 CONFIG_INPUT_EVDEV=y
 CONFIG_INPUT_EVBUG=m
 CONFIG_KEYBOARD_GPIO=y
+CONFIG_KEYBOARD_RPMSG=y
 CONFIG_KEYBOARD_PF1550_ONKEY=y
 CONFIG_KEYBOARD_IMX=y
 CONFIG_MOUSE_PS2=m
index ecf6b89..13a1295 100644 (file)
@@ -416,6 +416,15 @@ config KEYBOARD_MPR121
          To compile this driver as a module, choose M here: the
          module will be called mpr121_touchkey.
 
+config KEYBOARD_RPMSG
+       tristate "i.MX Rpmsg Keys Driver"
+       depends on (SOC_IMX7ULP)
+       depends on RPMSG
+       depends on OF
+       help
+         This is rpmsg keys driver on i.mx7ulp, because some keys located
+         in M4 side.
+
 config KEYBOARD_SNVS_PWRKEY
        tristate "IMX SNVS Power Key Driver"
        depends on (SOC_IMX6SX || SOC_IMX6UL || SOC_IMX7)
index 673199e..0a613ac 100644 (file)
@@ -50,6 +50,7 @@ obj-$(CONFIG_KEYBOARD_PXA27x)         += pxa27x_keypad.o
 obj-$(CONFIG_KEYBOARD_PXA930_ROTARY)   += pxa930_rotary.o
 obj-$(CONFIG_KEYBOARD_QT1070)           += qt1070.o
 obj-$(CONFIG_KEYBOARD_QT2160)          += qt2160.o
+obj-$(CONFIG_KEYBOARD_RPMSG)           += rpmsg-keys.o
 obj-$(CONFIG_KEYBOARD_SAMSUNG)         += samsung-keypad.o
 obj-$(CONFIG_KEYBOARD_SH_KEYSC)                += sh_keysc.o
 obj-$(CONFIG_KEYBOARD_SNVS_PWRKEY)     += snvs_pwrkey.o
diff --git a/drivers/input/keyboard/rpmsg-keys.c b/drivers/input/keyboard/rpmsg-keys.c
new file mode 100644 (file)
index 0000000..f6012e8
--- /dev/null
@@ -0,0 +1,310 @@
+/*
+ * Copyright 2017 NXP
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/imx_rpmsg.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_qos.h>
+#include <linux/rpmsg.h>
+#include <linux/uaccess.h>
+#include <linux/virtio.h>
+
+#define RPMSG_TIMEOUT 1000
+
+enum key_cmd_type {
+       KEY_RPMSG_SETUP,
+       KEY_RPMSG_REPLY,
+       KEY_RPMSG_NOTIFY,
+};
+
+enum keys_type {
+       KEY_PRESS = 1,
+       KEY_RELEASE,
+       KEY_BOTH,
+};
+
+struct key_rpmsg_data {
+       struct imx_rpmsg_head header;
+       u8 key_index;
+       union {
+               u8 event;
+               u8 retcode;
+       };
+       u8 wakeup;
+} __attribute__((packed));
+
+struct rpmsg_keys_button {
+       unsigned int code;
+       enum keys_type type;
+       int wakeup;
+       struct input_dev *input;
+};
+
+struct rpmsg_keys_drvdata {
+       struct input_dev *input;
+       struct rpmsg_device *rpdev;
+       struct device *dev;
+       struct key_rpmsg_data *msg;
+       bool ack;
+       struct pm_qos_request pm_qos_req;
+       struct delayed_work keysetup_work;
+       struct completion cmd_complete;
+       int nbuttons;
+       struct rpmsg_keys_button buttons[0];
+};
+
+static struct rpmsg_keys_drvdata *keys_rpmsg;
+
+static int key_send_message(struct key_rpmsg_data *msg,
+                       struct rpmsg_keys_drvdata *info, bool ack)
+{
+       int err;
+
+       if (!info->rpdev) {
+               dev_dbg(info->dev,
+                       "rpmsg channel not ready, m4 image ready?\n");
+               return -EINVAL;
+       }
+
+       pm_qos_add_request(&info->pm_qos_req,
+                       PM_QOS_CPU_DMA_LATENCY, 0);
+
+       if (ack) {
+               info->ack = true;
+               reinit_completion(&info->cmd_complete);
+       }
+
+       err = rpmsg_send(info->rpdev->ept, (void *)msg,
+                           sizeof(struct key_rpmsg_data));
+       if (err) {
+               dev_err(&info->rpdev->dev, "rpmsg_send failed: %d\n", err);
+               goto err_out;
+       }
+
+       if (ack) {
+               err = wait_for_completion_timeout(&info->cmd_complete,
+                                       msecs_to_jiffies(RPMSG_TIMEOUT));
+               if (!err) {
+                       dev_err(&info->rpdev->dev, "rpmsg_send timeout!\n");
+                       err = -ETIMEDOUT;
+                       goto err_out;
+               }
+
+               if (info->msg->retcode != 0) {
+                       dev_err(&info->rpdev->dev, "rpmsg not ack %d!\n",
+                               info->msg->retcode);
+                       err = -EINVAL;
+                       goto err_out;
+               }
+
+               err = 0;
+       }
+
+err_out:
+       info->ack = true;
+       pm_qos_remove_request(&info->pm_qos_req);
+
+       return err;
+}
+
+static int keys_rpmsg_cb(struct rpmsg_device *rpdev,
+       void *data, int len, void *priv, u32 src)
+{
+       struct key_rpmsg_data *msg = (struct key_rpmsg_data *)data;
+
+       if (msg->header.type == KEY_RPMSG_REPLY) {
+               keys_rpmsg->msg = msg;
+               complete(&keys_rpmsg->cmd_complete);
+               return 0;
+       } else if (msg->header.type == KEY_RPMSG_NOTIFY) {
+               keys_rpmsg->msg = msg;
+               keys_rpmsg->ack = false;
+       } else
+               dev_err(&keys_rpmsg->rpdev->dev, "wrong command type!\n");
+
+       input_event(keys_rpmsg->input, EV_KEY, msg->key_index, msg->event);
+       input_sync(keys_rpmsg->input);
+
+       return 0;
+}
+
+static void keys_init_handler(struct work_struct *work)
+{
+       struct key_rpmsg_data msg;
+       int i;
+
+       /* setup keys */
+       for (i = 0; i < keys_rpmsg->nbuttons; i++) {
+               struct rpmsg_keys_button *button = &keys_rpmsg->buttons[i];
+
+               msg.header.cate = IMX_RPMSG_KEY;
+               msg.header.major = IMX_RMPSG_MAJOR;
+               msg.header.minor = IMX_RMPSG_MINOR;
+               msg.header.type = KEY_RPMSG_SETUP;
+               msg.header.cmd = 0;
+               msg.key_index = button->code;
+               msg.wakeup = button->wakeup;
+               msg.event = button->type;
+               if (key_send_message(&msg, keys_rpmsg, true))
+                       dev_err(&keys_rpmsg->rpdev->dev,
+                               "key %d setup failed!\n", button->code);
+       }
+}
+
+static int keys_rpmsg_probe(struct rpmsg_device *rpdev)
+{
+       keys_rpmsg->rpdev = rpdev;
+
+       dev_info(&rpdev->dev, "new channel: 0x%x -> 0x%x!\n",
+                       rpdev->src, rpdev->dst);
+
+       init_completion(&keys_rpmsg->cmd_complete);
+
+       INIT_DELAYED_WORK(&keys_rpmsg->keysetup_work,
+                               keys_init_handler);
+       schedule_delayed_work(&keys_rpmsg->keysetup_work,
+                       msecs_to_jiffies(100));
+
+       return 0;
+}
+
+static struct rpmsg_device_id keys_rpmsg_id_table[] = {
+       { .name = "rpmsg-keypad-channel" },
+       { },
+};
+
+static struct rpmsg_driver keys_rpmsg_driver = {
+       .drv.name       = "key_rpmsg",
+       .drv.owner      = THIS_MODULE,
+       .id_table       = keys_rpmsg_id_table,
+       .probe          = keys_rpmsg_probe,
+       .callback       = keys_rpmsg_cb,
+};
+
+static struct rpmsg_keys_drvdata *
+rpmsg_keys_get_devtree_pdata(struct device *dev)
+{
+       struct device_node *node, *pp;
+       struct rpmsg_keys_drvdata *ddata;
+       struct rpmsg_keys_button *button;
+       int nbuttons;
+       int i;
+
+       node = dev->of_node;
+       if (!node)
+               return ERR_PTR(-ENODEV);
+
+       nbuttons = of_get_child_count(node);
+       if (nbuttons == 0)
+               return ERR_PTR(-ENODEV);
+
+       ddata = devm_kzalloc(dev,
+                            sizeof(*ddata) + nbuttons *
+                            sizeof(struct rpmsg_keys_button),
+                            GFP_KERNEL);
+       if (!ddata)
+               return ERR_PTR(-ENOMEM);
+
+       ddata->nbuttons = nbuttons;
+
+       i = 0;
+       for_each_child_of_node(node, pp) {
+               button = &ddata->buttons[i++];
+
+               if (of_property_read_u32(pp, "linux,code", &button->code)) {
+                       dev_err(dev, "Button without keycode: 0x%x\n",
+                               button->code);
+                       return ERR_PTR(-EINVAL);
+               }
+
+               button->wakeup = !!of_get_property(pp, "rpmsg-key,wakeup",
+                                                       NULL);
+               button->type = KEY_BOTH;
+       }
+
+       return ddata;
+}
+
+static int rpmsg_keys_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct rpmsg_keys_drvdata *ddata;
+       int i, error;
+       struct input_dev *input;
+
+       ddata = rpmsg_keys_get_devtree_pdata(dev);
+       if (IS_ERR(ddata))
+               return PTR_ERR(ddata);
+
+       input = devm_input_allocate_device(dev);
+       if (!input) {
+               dev_err(dev, "failed to allocate input device\n");
+               return -ENOMEM;
+       }
+
+       ddata->input = input;
+
+       keys_rpmsg = ddata;
+       platform_set_drvdata(pdev, ddata);
+
+       input->name = pdev->name;
+       input->phys = "rpmsg-keys/input0";
+       input->dev.parent = &pdev->dev;
+
+       input->id.bustype = BUS_HOST;
+
+       for (i = 0; i < ddata->nbuttons; i++) {
+               struct rpmsg_keys_button *button = &ddata->buttons[i];
+
+               input_set_capability(input, EV_KEY, button->code);
+       }
+
+       error = input_register_device(input);
+       if (error) {
+               dev_err(dev, "Unable to register input device, error: %d\n",
+                       error);
+               goto err_out;
+       }
+
+       return register_rpmsg_driver(&keys_rpmsg_driver);
+err_out:
+       return error;
+}
+
+static const struct of_device_id rpmsg_keys_of_match[] = {
+       { .compatible = "fsl,rpmsg-keys", },
+       { },
+};
+
+MODULE_DEVICE_TABLE(of, rpmsg_keys_of_match);
+
+static struct platform_driver rpmsg_keys_device_driver = {
+       .probe          = rpmsg_keys_probe,
+       .driver         =  {
+               .name   = "rpmsg-keys",
+               .of_match_table = of_match_ptr(rpmsg_keys_of_match)
+       }
+};
+
+module_platform_driver(rpmsg_keys_device_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Robin Gong <yibin.gong@nxp.com>");
+MODULE_DESCRIPTION("Keyboard driver based on rpmsg");
index c171a2f..d70c87e 100644 (file)
@@ -52,7 +52,7 @@ struct imx_rpmsg_vproc {
        char *rproc_name;
        struct mutex lock;
        int vdev_nums;
-#define MAX_VDEV_NUMS  5
+#define MAX_VDEV_NUMS  6
        struct imx_virdev ivdev[MAX_VDEV_NUMS];
 };
 
index 1299774..894ebaf 100644 (file)
@@ -26,6 +26,7 @@
 #define IMX_RMPSG_LIFECYCLE    1
 #define IMX_RPMSG_PMIC         2
 #define IMX_RPMSG_AUDIO                3
+#define IMX_RPMSG_KEY          4
 /* rpmsg version */
 #define IMX_RMPSG_MAJOR                1
 #define IMX_RMPSG_MINOR                0