1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 25f565502SHans-Christian Egtvedt /* 35f565502SHans-Christian Egtvedt * Driver for simulating a mouse on GPIO lines. 45f565502SHans-Christian Egtvedt * 55f565502SHans-Christian Egtvedt * Copyright (C) 2007 Atmel Corporation 6836bd419SLinus Walleij * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org> 75f565502SHans-Christian Egtvedt */ 85f565502SHans-Christian Egtvedt 95f565502SHans-Christian Egtvedt #include <linux/module.h> 105f565502SHans-Christian Egtvedt #include <linux/platform_device.h> 113df955f8SDmitry Torokhov #include <linux/input.h> 12836bd419SLinus Walleij #include <linux/gpio/consumer.h> 13836bd419SLinus Walleij #include <linux/property.h> 14adb77b3eSLinus Walleij #include <linux/of.h> 15c5053e69SLinus Walleij 16c5053e69SLinus Walleij /** 1734cf5a1cSLinus Walleij * struct gpio_mouse 18836bd419SLinus Walleij * @scan_ms: the scan interval in milliseconds. 19c5053e69SLinus Walleij * @up: GPIO line for up value. 20c5053e69SLinus Walleij * @down: GPIO line for down value. 21c5053e69SLinus Walleij * @left: GPIO line for left value. 22c5053e69SLinus Walleij * @right: GPIO line for right value. 23c5053e69SLinus Walleij * @bleft: GPIO line for left button. 24c5053e69SLinus Walleij * @bmiddle: GPIO line for middle button. 25c5053e69SLinus Walleij * @bright: GPIO line for right button. 26c5053e69SLinus Walleij * 27c5053e69SLinus Walleij * This struct must be added to the platform_device in the board code. 28c5053e69SLinus Walleij * It is used by the gpio_mouse driver to setup GPIO lines and to 29c5053e69SLinus Walleij * calculate mouse movement. 30c5053e69SLinus Walleij */ 3134cf5a1cSLinus Walleij struct gpio_mouse { 32836bd419SLinus Walleij u32 scan_ms; 33836bd419SLinus Walleij struct gpio_desc *up; 34836bd419SLinus Walleij struct gpio_desc *down; 35836bd419SLinus Walleij struct gpio_desc *left; 36836bd419SLinus Walleij struct gpio_desc *right; 37836bd419SLinus Walleij struct gpio_desc *bleft; 38836bd419SLinus Walleij struct gpio_desc *bmiddle; 39836bd419SLinus Walleij struct gpio_desc *bright; 40c5053e69SLinus Walleij }; 415f565502SHans-Christian Egtvedt 425f565502SHans-Christian Egtvedt /* 435f565502SHans-Christian Egtvedt * Timer function which is run every scan_ms ms when the device is opened. 44*981facf9SJiang Jian * The dev input variable is set to the input_dev pointer. 455f565502SHans-Christian Egtvedt */ 463df955f8SDmitry Torokhov static void gpio_mouse_scan(struct input_dev *input) 475f565502SHans-Christian Egtvedt { 483df955f8SDmitry Torokhov struct gpio_mouse *gpio = input_get_drvdata(input); 495f565502SHans-Christian Egtvedt int x, y; 505f565502SHans-Christian Egtvedt 51836bd419SLinus Walleij if (gpio->bleft) 525f565502SHans-Christian Egtvedt input_report_key(input, BTN_LEFT, 53836bd419SLinus Walleij gpiod_get_value(gpio->bleft)); 54836bd419SLinus Walleij if (gpio->bmiddle) 555f565502SHans-Christian Egtvedt input_report_key(input, BTN_MIDDLE, 56836bd419SLinus Walleij gpiod_get_value(gpio->bmiddle)); 57836bd419SLinus Walleij if (gpio->bright) 585f565502SHans-Christian Egtvedt input_report_key(input, BTN_RIGHT, 59836bd419SLinus Walleij gpiod_get_value(gpio->bright)); 605f565502SHans-Christian Egtvedt 61836bd419SLinus Walleij x = gpiod_get_value(gpio->right) - gpiod_get_value(gpio->left); 62836bd419SLinus Walleij y = gpiod_get_value(gpio->down) - gpiod_get_value(gpio->up); 635f565502SHans-Christian Egtvedt 645f565502SHans-Christian Egtvedt input_report_rel(input, REL_X, x); 655f565502SHans-Christian Egtvedt input_report_rel(input, REL_Y, y); 665f565502SHans-Christian Egtvedt input_sync(input); 675f565502SHans-Christian Egtvedt } 685f565502SHans-Christian Egtvedt 695298cc4cSBill Pemberton static int gpio_mouse_probe(struct platform_device *pdev) 705f565502SHans-Christian Egtvedt { 71c5053e69SLinus Walleij struct device *dev = &pdev->dev; 7234cf5a1cSLinus Walleij struct gpio_mouse *gmouse; 735f565502SHans-Christian Egtvedt struct input_dev *input; 743df955f8SDmitry Torokhov int error; 755f565502SHans-Christian Egtvedt 7634cf5a1cSLinus Walleij gmouse = devm_kzalloc(dev, sizeof(*gmouse), GFP_KERNEL); 7734cf5a1cSLinus Walleij if (!gmouse) 78c5053e69SLinus Walleij return -ENOMEM; 795f565502SHans-Christian Egtvedt 80836bd419SLinus Walleij /* Assign some default scanning time */ 813df955f8SDmitry Torokhov error = device_property_read_u32(dev, "scan-interval-ms", 82836bd419SLinus Walleij &gmouse->scan_ms); 833df955f8SDmitry Torokhov if (error || gmouse->scan_ms == 0) { 84836bd419SLinus Walleij dev_warn(dev, "invalid scan time, set to 50 ms\n"); 85836bd419SLinus Walleij gmouse->scan_ms = 50; 865f565502SHans-Christian Egtvedt } 875f565502SHans-Christian Egtvedt 88836bd419SLinus Walleij gmouse->up = devm_gpiod_get(dev, "up", GPIOD_IN); 89836bd419SLinus Walleij if (IS_ERR(gmouse->up)) 90836bd419SLinus Walleij return PTR_ERR(gmouse->up); 91836bd419SLinus Walleij gmouse->down = devm_gpiod_get(dev, "down", GPIOD_IN); 92836bd419SLinus Walleij if (IS_ERR(gmouse->down)) 93836bd419SLinus Walleij return PTR_ERR(gmouse->down); 94836bd419SLinus Walleij gmouse->left = devm_gpiod_get(dev, "left", GPIOD_IN); 95836bd419SLinus Walleij if (IS_ERR(gmouse->left)) 96836bd419SLinus Walleij return PTR_ERR(gmouse->left); 97836bd419SLinus Walleij gmouse->right = devm_gpiod_get(dev, "right", GPIOD_IN); 98836bd419SLinus Walleij if (IS_ERR(gmouse->right)) 99836bd419SLinus Walleij return PTR_ERR(gmouse->right); 1005f565502SHans-Christian Egtvedt 101836bd419SLinus Walleij gmouse->bleft = devm_gpiod_get_optional(dev, "button-left", GPIOD_IN); 102836bd419SLinus Walleij if (IS_ERR(gmouse->bleft)) 103836bd419SLinus Walleij return PTR_ERR(gmouse->bleft); 104836bd419SLinus Walleij gmouse->bmiddle = devm_gpiod_get_optional(dev, "button-middle", 105836bd419SLinus Walleij GPIOD_IN); 106836bd419SLinus Walleij if (IS_ERR(gmouse->bmiddle)) 107836bd419SLinus Walleij return PTR_ERR(gmouse->bmiddle); 108836bd419SLinus Walleij gmouse->bright = devm_gpiod_get_optional(dev, "button-right", 109836bd419SLinus Walleij GPIOD_IN); 110836bd419SLinus Walleij if (IS_ERR(gmouse->bright)) 111836bd419SLinus Walleij return PTR_ERR(gmouse->bright); 1125f565502SHans-Christian Egtvedt 1133df955f8SDmitry Torokhov input = devm_input_allocate_device(dev); 1143df955f8SDmitry Torokhov if (!input) 115836bd419SLinus Walleij return -ENOMEM; 1165f565502SHans-Christian Egtvedt 1175f565502SHans-Christian Egtvedt input->name = pdev->name; 1185f565502SHans-Christian Egtvedt input->id.bustype = BUS_HOST; 1193df955f8SDmitry Torokhov 1203df955f8SDmitry Torokhov input_set_drvdata(input, gmouse); 1215f565502SHans-Christian Egtvedt 1225f565502SHans-Christian Egtvedt input_set_capability(input, EV_REL, REL_X); 1235f565502SHans-Christian Egtvedt input_set_capability(input, EV_REL, REL_Y); 124836bd419SLinus Walleij if (gmouse->bleft) 1255f565502SHans-Christian Egtvedt input_set_capability(input, EV_KEY, BTN_LEFT); 126836bd419SLinus Walleij if (gmouse->bmiddle) 1275f565502SHans-Christian Egtvedt input_set_capability(input, EV_KEY, BTN_MIDDLE); 128836bd419SLinus Walleij if (gmouse->bright) 1295f565502SHans-Christian Egtvedt input_set_capability(input, EV_KEY, BTN_RIGHT); 1305f565502SHans-Christian Egtvedt 1313df955f8SDmitry Torokhov error = input_setup_polling(input, gpio_mouse_scan); 1323df955f8SDmitry Torokhov if (error) 1333df955f8SDmitry Torokhov return error; 1343df955f8SDmitry Torokhov 1353df955f8SDmitry Torokhov input_set_poll_interval(input, gmouse->scan_ms); 1363df955f8SDmitry Torokhov 1373df955f8SDmitry Torokhov error = input_register_device(input); 1383df955f8SDmitry Torokhov if (error) { 139836bd419SLinus Walleij dev_err(dev, "could not register input device\n"); 1403df955f8SDmitry Torokhov return error; 1415f565502SHans-Christian Egtvedt } 1425f565502SHans-Christian Egtvedt 143836bd419SLinus Walleij dev_dbg(dev, "%d ms scan time, buttons: %s%s%s\n", 14434cf5a1cSLinus Walleij gmouse->scan_ms, 145836bd419SLinus Walleij gmouse->bleft ? "" : "left ", 146836bd419SLinus Walleij gmouse->bmiddle ? "" : "middle ", 147836bd419SLinus Walleij gmouse->bright ? "" : "right"); 1485f565502SHans-Christian Egtvedt 1495f565502SHans-Christian Egtvedt return 0; 1505f565502SHans-Christian Egtvedt } 1515f565502SHans-Christian Egtvedt 152adb77b3eSLinus Walleij static const struct of_device_id gpio_mouse_of_match[] = { 153adb77b3eSLinus Walleij { .compatible = "gpio-mouse", }, 154adb77b3eSLinus Walleij { }, 155adb77b3eSLinus Walleij }; 156adb77b3eSLinus Walleij MODULE_DEVICE_TABLE(of, gpio_mouse_of_match); 157adb77b3eSLinus Walleij 1580de048abSRoel Kluin static struct platform_driver gpio_mouse_device_driver = { 159eeafa5efSSaeed Bishara .probe = gpio_mouse_probe, 1605f565502SHans-Christian Egtvedt .driver = { 1615f565502SHans-Christian Egtvedt .name = "gpio_mouse", 162adb77b3eSLinus Walleij .of_match_table = gpio_mouse_of_match, 1635f565502SHans-Christian Egtvedt } 1645f565502SHans-Christian Egtvedt }; 1654fcdeac5SJJ Ding module_platform_driver(gpio_mouse_device_driver); 1665f565502SHans-Christian Egtvedt 1677c409522SHans-Christian Egtvedt MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>"); 1685f565502SHans-Christian Egtvedt MODULE_DESCRIPTION("GPIO mouse driver"); 1695f565502SHans-Christian Egtvedt MODULE_LICENSE("GPL"); 170eeafa5efSSaeed Bishara MODULE_ALIAS("platform:gpio_mouse"); /* work with hotplug and coldplug */ 171