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 5*836bd419SLinus 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> 15*836bd419SLinus Walleij #include <linux/gpio/consumer.h> 16*836bd419SLinus Walleij #include <linux/property.h> 17c5053e69SLinus Walleij 18c5053e69SLinus Walleij /** 1934cf5a1cSLinus Walleij * struct gpio_mouse 20*836bd419SLinus Walleij * @scan_ms: the scan interval in milliseconds. 21c5053e69SLinus Walleij * @up: GPIO line for up value. 22c5053e69SLinus Walleij * @down: GPIO line for down value. 23c5053e69SLinus Walleij * @left: GPIO line for left value. 24c5053e69SLinus Walleij * @right: GPIO line for right value. 25c5053e69SLinus Walleij * @bleft: GPIO line for left button. 26c5053e69SLinus Walleij * @bmiddle: GPIO line for middle button. 27c5053e69SLinus Walleij * @bright: GPIO line for right button. 28c5053e69SLinus Walleij * 29c5053e69SLinus Walleij * This struct must be added to the platform_device in the board code. 30c5053e69SLinus Walleij * It is used by the gpio_mouse driver to setup GPIO lines and to 31c5053e69SLinus Walleij * calculate mouse movement. 32c5053e69SLinus Walleij */ 3334cf5a1cSLinus Walleij struct gpio_mouse { 34*836bd419SLinus Walleij u32 scan_ms; 35*836bd419SLinus Walleij struct gpio_desc *up; 36*836bd419SLinus Walleij struct gpio_desc *down; 37*836bd419SLinus Walleij struct gpio_desc *left; 38*836bd419SLinus Walleij struct gpio_desc *right; 39*836bd419SLinus Walleij struct gpio_desc *bleft; 40*836bd419SLinus Walleij struct gpio_desc *bmiddle; 41*836bd419SLinus Walleij struct gpio_desc *bright; 42c5053e69SLinus Walleij }; 435f565502SHans-Christian Egtvedt 445f565502SHans-Christian Egtvedt /* 455f565502SHans-Christian Egtvedt * Timer function which is run every scan_ms ms when the device is opened. 463a070ad1SUwe Kleine-Koenig * The dev input variable is set to the the input_dev pointer. 475f565502SHans-Christian Egtvedt */ 485f565502SHans-Christian Egtvedt static void gpio_mouse_scan(struct input_polled_dev *dev) 495f565502SHans-Christian Egtvedt { 5034cf5a1cSLinus Walleij struct gpio_mouse *gpio = dev->private; 515f565502SHans-Christian Egtvedt struct input_dev *input = dev->input; 525f565502SHans-Christian Egtvedt int x, y; 535f565502SHans-Christian Egtvedt 54*836bd419SLinus Walleij if (gpio->bleft) 555f565502SHans-Christian Egtvedt input_report_key(input, BTN_LEFT, 56*836bd419SLinus Walleij gpiod_get_value(gpio->bleft)); 57*836bd419SLinus Walleij if (gpio->bmiddle) 585f565502SHans-Christian Egtvedt input_report_key(input, BTN_MIDDLE, 59*836bd419SLinus Walleij gpiod_get_value(gpio->bmiddle)); 60*836bd419SLinus Walleij if (gpio->bright) 615f565502SHans-Christian Egtvedt input_report_key(input, BTN_RIGHT, 62*836bd419SLinus Walleij gpiod_get_value(gpio->bright)); 635f565502SHans-Christian Egtvedt 64*836bd419SLinus Walleij x = gpiod_get_value(gpio->right) - gpiod_get_value(gpio->left); 65*836bd419SLinus Walleij y = gpiod_get_value(gpio->down) - gpiod_get_value(gpio->up); 665f565502SHans-Christian Egtvedt 675f565502SHans-Christian Egtvedt input_report_rel(input, REL_X, x); 685f565502SHans-Christian Egtvedt input_report_rel(input, REL_Y, y); 695f565502SHans-Christian Egtvedt input_sync(input); 705f565502SHans-Christian Egtvedt } 715f565502SHans-Christian Egtvedt 725298cc4cSBill Pemberton static int gpio_mouse_probe(struct platform_device *pdev) 735f565502SHans-Christian Egtvedt { 74c5053e69SLinus Walleij struct device *dev = &pdev->dev; 7534cf5a1cSLinus Walleij struct gpio_mouse *gmouse; 765f565502SHans-Christian Egtvedt struct input_polled_dev *input_poll; 775f565502SHans-Christian Egtvedt struct input_dev *input; 78*836bd419SLinus Walleij int ret; 795f565502SHans-Christian Egtvedt 8034cf5a1cSLinus Walleij gmouse = devm_kzalloc(dev, sizeof(*gmouse), GFP_KERNEL); 8134cf5a1cSLinus Walleij if (!gmouse) 82c5053e69SLinus Walleij return -ENOMEM; 835f565502SHans-Christian Egtvedt 84*836bd419SLinus Walleij /* Assign some default scanning time */ 85*836bd419SLinus Walleij ret = device_property_read_u32(dev, "scan-interval-ms", 86*836bd419SLinus Walleij &gmouse->scan_ms); 87*836bd419SLinus Walleij if (ret || gmouse->scan_ms == 0) { 88*836bd419SLinus Walleij dev_warn(dev, "invalid scan time, set to 50 ms\n"); 89*836bd419SLinus Walleij gmouse->scan_ms = 50; 905f565502SHans-Christian Egtvedt } 915f565502SHans-Christian Egtvedt 92*836bd419SLinus Walleij gmouse->up = devm_gpiod_get(dev, "up", GPIOD_IN); 93*836bd419SLinus Walleij if (IS_ERR(gmouse->up)) 94*836bd419SLinus Walleij return PTR_ERR(gmouse->up); 95*836bd419SLinus Walleij gmouse->down = devm_gpiod_get(dev, "down", GPIOD_IN); 96*836bd419SLinus Walleij if (IS_ERR(gmouse->down)) 97*836bd419SLinus Walleij return PTR_ERR(gmouse->down); 98*836bd419SLinus Walleij gmouse->left = devm_gpiod_get(dev, "left", GPIOD_IN); 99*836bd419SLinus Walleij if (IS_ERR(gmouse->left)) 100*836bd419SLinus Walleij return PTR_ERR(gmouse->left); 101*836bd419SLinus Walleij gmouse->right = devm_gpiod_get(dev, "right", GPIOD_IN); 102*836bd419SLinus Walleij if (IS_ERR(gmouse->right)) 103*836bd419SLinus Walleij return PTR_ERR(gmouse->right); 1045f565502SHans-Christian Egtvedt 105*836bd419SLinus Walleij gmouse->bleft = devm_gpiod_get_optional(dev, "button-left", GPIOD_IN); 106*836bd419SLinus Walleij if (IS_ERR(gmouse->bleft)) 107*836bd419SLinus Walleij return PTR_ERR(gmouse->bleft); 108*836bd419SLinus Walleij gmouse->bmiddle = devm_gpiod_get_optional(dev, "button-middle", 109*836bd419SLinus Walleij GPIOD_IN); 110*836bd419SLinus Walleij if (IS_ERR(gmouse->bmiddle)) 111*836bd419SLinus Walleij return PTR_ERR(gmouse->bmiddle); 112*836bd419SLinus Walleij gmouse->bright = devm_gpiod_get_optional(dev, "button-right", 113*836bd419SLinus Walleij GPIOD_IN); 114*836bd419SLinus Walleij if (IS_ERR(gmouse->bright)) 115*836bd419SLinus Walleij return PTR_ERR(gmouse->bright); 1165f565502SHans-Christian Egtvedt 117*836bd419SLinus Walleij input_poll = devm_input_allocate_polled_device(dev); 1185f565502SHans-Christian Egtvedt if (!input_poll) { 119*836bd419SLinus Walleij dev_err(dev, "not enough memory for input device\n"); 120*836bd419SLinus Walleij return -ENOMEM; 1215f565502SHans-Christian Egtvedt } 1225f565502SHans-Christian Egtvedt 1235f565502SHans-Christian Egtvedt platform_set_drvdata(pdev, input_poll); 1245f565502SHans-Christian Egtvedt 1255f565502SHans-Christian Egtvedt /* set input-polldev handlers */ 12634cf5a1cSLinus Walleij input_poll->private = gmouse; 1275f565502SHans-Christian Egtvedt input_poll->poll = gpio_mouse_scan; 12834cf5a1cSLinus Walleij input_poll->poll_interval = gmouse->scan_ms; 1295f565502SHans-Christian Egtvedt 1305f565502SHans-Christian Egtvedt input = input_poll->input; 1315f565502SHans-Christian Egtvedt input->name = pdev->name; 1325f565502SHans-Christian Egtvedt input->id.bustype = BUS_HOST; 1335f565502SHans-Christian Egtvedt input->dev.parent = &pdev->dev; 1345f565502SHans-Christian Egtvedt 1355f565502SHans-Christian Egtvedt input_set_capability(input, EV_REL, REL_X); 1365f565502SHans-Christian Egtvedt input_set_capability(input, EV_REL, REL_Y); 137*836bd419SLinus Walleij if (gmouse->bleft) 1385f565502SHans-Christian Egtvedt input_set_capability(input, EV_KEY, BTN_LEFT); 139*836bd419SLinus Walleij if (gmouse->bmiddle) 1405f565502SHans-Christian Egtvedt input_set_capability(input, EV_KEY, BTN_MIDDLE); 141*836bd419SLinus Walleij if (gmouse->bright) 1425f565502SHans-Christian Egtvedt input_set_capability(input, EV_KEY, BTN_RIGHT); 1435f565502SHans-Christian Egtvedt 144*836bd419SLinus Walleij ret = input_register_polled_device(input_poll); 145*836bd419SLinus Walleij if (ret) { 146*836bd419SLinus Walleij dev_err(dev, "could not register input device\n"); 147*836bd419SLinus Walleij return ret; 1485f565502SHans-Christian Egtvedt } 1495f565502SHans-Christian Egtvedt 150*836bd419SLinus Walleij dev_dbg(dev, "%d ms scan time, buttons: %s%s%s\n", 15134cf5a1cSLinus Walleij gmouse->scan_ms, 152*836bd419SLinus Walleij gmouse->bleft ? "" : "left ", 153*836bd419SLinus Walleij gmouse->bmiddle ? "" : "middle ", 154*836bd419SLinus Walleij gmouse->bright ? "" : "right"); 1555f565502SHans-Christian Egtvedt 1565f565502SHans-Christian Egtvedt return 0; 1575f565502SHans-Christian Egtvedt } 1585f565502SHans-Christian Egtvedt 1590de048abSRoel Kluin static struct platform_driver gpio_mouse_device_driver = { 160eeafa5efSSaeed Bishara .probe = gpio_mouse_probe, 1615f565502SHans-Christian Egtvedt .driver = { 1625f565502SHans-Christian Egtvedt .name = "gpio_mouse", 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