15f565502SHans-Christian Egtvedt /* 25f565502SHans-Christian Egtvedt * Driver for simulating a mouse on GPIO lines. 35f565502SHans-Christian Egtvedt * 45f565502SHans-Christian Egtvedt * Copyright (C) 2007 Atmel Corporation 5836bd419SLinus Walleij * Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org> 65f565502SHans-Christian Egtvedt * 75f565502SHans-Christian Egtvedt * This program is free software; you can redistribute it and/or modify 85f565502SHans-Christian Egtvedt * it under the terms of the GNU General Public License version 2 as 95f565502SHans-Christian Egtvedt * published by the Free Software Foundation. 105f565502SHans-Christian Egtvedt */ 115f565502SHans-Christian Egtvedt 125f565502SHans-Christian Egtvedt #include <linux/module.h> 135f565502SHans-Christian Egtvedt #include <linux/platform_device.h> 145f565502SHans-Christian Egtvedt #include <linux/input-polldev.h> 15836bd419SLinus Walleij #include <linux/gpio/consumer.h> 16836bd419SLinus Walleij #include <linux/property.h> 17*adb77b3eSLinus Walleij #include <linux/of.h> 18c5053e69SLinus Walleij 19c5053e69SLinus Walleij /** 2034cf5a1cSLinus Walleij * struct gpio_mouse 21836bd419SLinus Walleij * @scan_ms: the scan interval in milliseconds. 22c5053e69SLinus Walleij * @up: GPIO line for up value. 23c5053e69SLinus Walleij * @down: GPIO line for down value. 24c5053e69SLinus Walleij * @left: GPIO line for left value. 25c5053e69SLinus Walleij * @right: GPIO line for right value. 26c5053e69SLinus Walleij * @bleft: GPIO line for left button. 27c5053e69SLinus Walleij * @bmiddle: GPIO line for middle button. 28c5053e69SLinus Walleij * @bright: GPIO line for right button. 29c5053e69SLinus Walleij * 30c5053e69SLinus Walleij * This struct must be added to the platform_device in the board code. 31c5053e69SLinus Walleij * It is used by the gpio_mouse driver to setup GPIO lines and to 32c5053e69SLinus Walleij * calculate mouse movement. 33c5053e69SLinus Walleij */ 3434cf5a1cSLinus Walleij struct gpio_mouse { 35836bd419SLinus Walleij u32 scan_ms; 36836bd419SLinus Walleij struct gpio_desc *up; 37836bd419SLinus Walleij struct gpio_desc *down; 38836bd419SLinus Walleij struct gpio_desc *left; 39836bd419SLinus Walleij struct gpio_desc *right; 40836bd419SLinus Walleij struct gpio_desc *bleft; 41836bd419SLinus Walleij struct gpio_desc *bmiddle; 42836bd419SLinus Walleij struct gpio_desc *bright; 43c5053e69SLinus Walleij }; 445f565502SHans-Christian Egtvedt 455f565502SHans-Christian Egtvedt /* 465f565502SHans-Christian Egtvedt * Timer function which is run every scan_ms ms when the device is opened. 473a070ad1SUwe Kleine-Koenig * The dev input variable is set to the the input_dev pointer. 485f565502SHans-Christian Egtvedt */ 495f565502SHans-Christian Egtvedt static void gpio_mouse_scan(struct input_polled_dev *dev) 505f565502SHans-Christian Egtvedt { 5134cf5a1cSLinus Walleij struct gpio_mouse *gpio = dev->private; 525f565502SHans-Christian Egtvedt struct input_dev *input = dev->input; 535f565502SHans-Christian Egtvedt int x, y; 545f565502SHans-Christian Egtvedt 55836bd419SLinus Walleij if (gpio->bleft) 565f565502SHans-Christian Egtvedt input_report_key(input, BTN_LEFT, 57836bd419SLinus Walleij gpiod_get_value(gpio->bleft)); 58836bd419SLinus Walleij if (gpio->bmiddle) 595f565502SHans-Christian Egtvedt input_report_key(input, BTN_MIDDLE, 60836bd419SLinus Walleij gpiod_get_value(gpio->bmiddle)); 61836bd419SLinus Walleij if (gpio->bright) 625f565502SHans-Christian Egtvedt input_report_key(input, BTN_RIGHT, 63836bd419SLinus Walleij gpiod_get_value(gpio->bright)); 645f565502SHans-Christian Egtvedt 65836bd419SLinus Walleij x = gpiod_get_value(gpio->right) - gpiod_get_value(gpio->left); 66836bd419SLinus Walleij y = gpiod_get_value(gpio->down) - gpiod_get_value(gpio->up); 675f565502SHans-Christian Egtvedt 685f565502SHans-Christian Egtvedt input_report_rel(input, REL_X, x); 695f565502SHans-Christian Egtvedt input_report_rel(input, REL_Y, y); 705f565502SHans-Christian Egtvedt input_sync(input); 715f565502SHans-Christian Egtvedt } 725f565502SHans-Christian Egtvedt 735298cc4cSBill Pemberton static int gpio_mouse_probe(struct platform_device *pdev) 745f565502SHans-Christian Egtvedt { 75c5053e69SLinus Walleij struct device *dev = &pdev->dev; 7634cf5a1cSLinus Walleij struct gpio_mouse *gmouse; 775f565502SHans-Christian Egtvedt struct input_polled_dev *input_poll; 785f565502SHans-Christian Egtvedt struct input_dev *input; 79836bd419SLinus Walleij int ret; 805f565502SHans-Christian Egtvedt 8134cf5a1cSLinus Walleij gmouse = devm_kzalloc(dev, sizeof(*gmouse), GFP_KERNEL); 8234cf5a1cSLinus Walleij if (!gmouse) 83c5053e69SLinus Walleij return -ENOMEM; 845f565502SHans-Christian Egtvedt 85836bd419SLinus Walleij /* Assign some default scanning time */ 86836bd419SLinus Walleij ret = device_property_read_u32(dev, "scan-interval-ms", 87836bd419SLinus Walleij &gmouse->scan_ms); 88836bd419SLinus Walleij if (ret || gmouse->scan_ms == 0) { 89836bd419SLinus Walleij dev_warn(dev, "invalid scan time, set to 50 ms\n"); 90836bd419SLinus Walleij gmouse->scan_ms = 50; 915f565502SHans-Christian Egtvedt } 925f565502SHans-Christian Egtvedt 93836bd419SLinus Walleij gmouse->up = devm_gpiod_get(dev, "up", GPIOD_IN); 94836bd419SLinus Walleij if (IS_ERR(gmouse->up)) 95836bd419SLinus Walleij return PTR_ERR(gmouse->up); 96836bd419SLinus Walleij gmouse->down = devm_gpiod_get(dev, "down", GPIOD_IN); 97836bd419SLinus Walleij if (IS_ERR(gmouse->down)) 98836bd419SLinus Walleij return PTR_ERR(gmouse->down); 99836bd419SLinus Walleij gmouse->left = devm_gpiod_get(dev, "left", GPIOD_IN); 100836bd419SLinus Walleij if (IS_ERR(gmouse->left)) 101836bd419SLinus Walleij return PTR_ERR(gmouse->left); 102836bd419SLinus Walleij gmouse->right = devm_gpiod_get(dev, "right", GPIOD_IN); 103836bd419SLinus Walleij if (IS_ERR(gmouse->right)) 104836bd419SLinus Walleij return PTR_ERR(gmouse->right); 1055f565502SHans-Christian Egtvedt 106836bd419SLinus Walleij gmouse->bleft = devm_gpiod_get_optional(dev, "button-left", GPIOD_IN); 107836bd419SLinus Walleij if (IS_ERR(gmouse->bleft)) 108836bd419SLinus Walleij return PTR_ERR(gmouse->bleft); 109836bd419SLinus Walleij gmouse->bmiddle = devm_gpiod_get_optional(dev, "button-middle", 110836bd419SLinus Walleij GPIOD_IN); 111836bd419SLinus Walleij if (IS_ERR(gmouse->bmiddle)) 112836bd419SLinus Walleij return PTR_ERR(gmouse->bmiddle); 113836bd419SLinus Walleij gmouse->bright = devm_gpiod_get_optional(dev, "button-right", 114836bd419SLinus Walleij GPIOD_IN); 115836bd419SLinus Walleij if (IS_ERR(gmouse->bright)) 116836bd419SLinus Walleij return PTR_ERR(gmouse->bright); 1175f565502SHans-Christian Egtvedt 118836bd419SLinus Walleij input_poll = devm_input_allocate_polled_device(dev); 1195f565502SHans-Christian Egtvedt if (!input_poll) { 120836bd419SLinus Walleij dev_err(dev, "not enough memory for input device\n"); 121836bd419SLinus Walleij return -ENOMEM; 1225f565502SHans-Christian Egtvedt } 1235f565502SHans-Christian Egtvedt 1245f565502SHans-Christian Egtvedt platform_set_drvdata(pdev, input_poll); 1255f565502SHans-Christian Egtvedt 1265f565502SHans-Christian Egtvedt /* set input-polldev handlers */ 12734cf5a1cSLinus Walleij input_poll->private = gmouse; 1285f565502SHans-Christian Egtvedt input_poll->poll = gpio_mouse_scan; 12934cf5a1cSLinus Walleij input_poll->poll_interval = gmouse->scan_ms; 1305f565502SHans-Christian Egtvedt 1315f565502SHans-Christian Egtvedt input = input_poll->input; 1325f565502SHans-Christian Egtvedt input->name = pdev->name; 1335f565502SHans-Christian Egtvedt input->id.bustype = BUS_HOST; 1345f565502SHans-Christian Egtvedt input->dev.parent = &pdev->dev; 1355f565502SHans-Christian Egtvedt 1365f565502SHans-Christian Egtvedt input_set_capability(input, EV_REL, REL_X); 1375f565502SHans-Christian Egtvedt input_set_capability(input, EV_REL, REL_Y); 138836bd419SLinus Walleij if (gmouse->bleft) 1395f565502SHans-Christian Egtvedt input_set_capability(input, EV_KEY, BTN_LEFT); 140836bd419SLinus Walleij if (gmouse->bmiddle) 1415f565502SHans-Christian Egtvedt input_set_capability(input, EV_KEY, BTN_MIDDLE); 142836bd419SLinus Walleij if (gmouse->bright) 1435f565502SHans-Christian Egtvedt input_set_capability(input, EV_KEY, BTN_RIGHT); 1445f565502SHans-Christian Egtvedt 145836bd419SLinus Walleij ret = input_register_polled_device(input_poll); 146836bd419SLinus Walleij if (ret) { 147836bd419SLinus Walleij dev_err(dev, "could not register input device\n"); 148836bd419SLinus Walleij return ret; 1495f565502SHans-Christian Egtvedt } 1505f565502SHans-Christian Egtvedt 151836bd419SLinus Walleij dev_dbg(dev, "%d ms scan time, buttons: %s%s%s\n", 15234cf5a1cSLinus Walleij gmouse->scan_ms, 153836bd419SLinus Walleij gmouse->bleft ? "" : "left ", 154836bd419SLinus Walleij gmouse->bmiddle ? "" : "middle ", 155836bd419SLinus Walleij gmouse->bright ? "" : "right"); 1565f565502SHans-Christian Egtvedt 1575f565502SHans-Christian Egtvedt return 0; 1585f565502SHans-Christian Egtvedt } 1595f565502SHans-Christian Egtvedt 160*adb77b3eSLinus Walleij static const struct of_device_id gpio_mouse_of_match[] = { 161*adb77b3eSLinus Walleij { .compatible = "gpio-mouse", }, 162*adb77b3eSLinus Walleij { }, 163*adb77b3eSLinus Walleij }; 164*adb77b3eSLinus Walleij MODULE_DEVICE_TABLE(of, gpio_mouse_of_match); 165*adb77b3eSLinus Walleij 1660de048abSRoel Kluin static struct platform_driver gpio_mouse_device_driver = { 167eeafa5efSSaeed Bishara .probe = gpio_mouse_probe, 1685f565502SHans-Christian Egtvedt .driver = { 1695f565502SHans-Christian Egtvedt .name = "gpio_mouse", 170*adb77b3eSLinus Walleij .of_match_table = gpio_mouse_of_match, 1715f565502SHans-Christian Egtvedt } 1725f565502SHans-Christian Egtvedt }; 1734fcdeac5SJJ Ding module_platform_driver(gpio_mouse_device_driver); 1745f565502SHans-Christian Egtvedt 1757c409522SHans-Christian Egtvedt MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>"); 1765f565502SHans-Christian Egtvedt MODULE_DESCRIPTION("GPIO mouse driver"); 1775f565502SHans-Christian Egtvedt MODULE_LICENSE("GPL"); 178eeafa5efSSaeed Bishara MODULE_ALIAS("platform:gpio_mouse"); /* work with hotplug and coldplug */ 179