diff -rupN linux.orig/drivers/gpio/gpiolib-of.c linux/drivers/gpio/gpiolib-of.c --- linux.orig/drivers/gpio/gpiolib-of.c 2024-01-10 19:42:04.717152439 +0000 +++ linux/drivers/gpio/gpiolib-of.c 2024-01-10 20:18:46.559755988 +0000 @@ -25,21 +25,6 @@ #include "gpiolib.h" #include "gpiolib-of.h" -/* - * This is Linux-specific flags. By default controllers' and Linux' mapping - * match, but GPIO controllers are free to translate their own flags to - * Linux-specific in their .xlate callback. Though, 1:1 mapping is recommended. - */ -enum of_gpio_flags { - OF_GPIO_ACTIVE_LOW = 0x1, - OF_GPIO_SINGLE_ENDED = 0x2, - OF_GPIO_OPEN_DRAIN = 0x4, - OF_GPIO_TRANSITORY = 0x8, - OF_GPIO_PULL_UP = 0x10, - OF_GPIO_PULL_DOWN = 0x20, - OF_GPIO_PULL_DISABLE = 0x40, -}; - /** * of_gpio_named_count() - Count GPIOs for a device * @np: device node to count GPIOs for @@ -408,6 +393,20 @@ out: return desc; } +int of_get_named_gpio_flags(const struct device_node *np, const char *list_name, + int index, enum of_gpio_flags *flags) +{ + struct gpio_desc *desc; + + desc = of_get_named_gpiod_flags(np, list_name, index, flags); + + if (IS_ERR(desc)) + return PTR_ERR(desc); + else + return desc_to_gpio(desc); +} +EXPORT_SYMBOL_GPL(of_get_named_gpio_flags); + /** * of_get_named_gpio() - Get a GPIO number to use with GPIO API * @np: device node to get GPIO from diff -rupN linux.orig/drivers/input/Kconfig linux/drivers/input/Kconfig --- linux.orig/drivers/input/Kconfig 2024-01-10 19:42:05.049159698 +0000 +++ linux/drivers/input/Kconfig 2024-01-10 20:16:09.688768208 +0000 @@ -51,6 +51,19 @@ config INPUT_FF_MEMLESS To compile this driver as a module, choose M here: the module will be called ff-memless. +config INPUT_POLLDEV + tristate "Polled input device skeleton" + help + Say Y here if you are using a driver for an input + device that periodically polls hardware state. This + option is only useful for out-of-tree drivers since + in-tree drivers select it automatically. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called input-polldev. + config INPUT_SPARSEKMAP tristate "Sparse keymap support library" help diff -rupN linux.orig/drivers/input/Makefile linux/drivers/input/Makefile --- linux.orig/drivers/input/Makefile 2024-01-10 19:42:05.049159698 +0000 +++ linux/drivers/input/Makefile 2024-01-10 20:16:09.688768208 +0000 @@ -10,6 +10,7 @@ input-core-y := input.o input-compat.o i input-core-y += touchscreen.o obj-$(CONFIG_INPUT_FF_MEMLESS) += ff-memless.o +obj-$(CONFIG_INPUT_POLLDEV) += input-polldev.o obj-$(CONFIG_INPUT_SPARSEKMAP) += sparse-keymap.o obj-$(CONFIG_INPUT_MATRIXKMAP) += matrix-keymap.o obj-$(CONFIG_INPUT_VIVALDIFMAP) += vivaldi-fmap.o diff -rupN linux.orig/drivers/input/input-polldev.c linux/drivers/input/input-polldev.c --- linux.orig/drivers/input/input-polldev.c 1970-01-01 00:00:00.000000000 +0000 +++ linux/drivers/input/input-polldev.c 2024-01-10 20:16:09.688768208 +0000 @@ -0,0 +1,362 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Generic implementation of a polled input device + + * Copyright (c) 2007 Dmitry Torokhov + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Dmitry Torokhov "); +MODULE_DESCRIPTION("Generic implementation of a polled input device"); +MODULE_LICENSE("GPL v2"); + +static void input_polldev_queue_work(struct input_polled_dev *dev) +{ + unsigned long delay; + + delay = msecs_to_jiffies(dev->poll_interval); + if (delay >= HZ) + delay = round_jiffies_relative(delay); + + queue_delayed_work(system_freezable_wq, &dev->work, delay); +} + +static void input_polled_device_work(struct work_struct *work) +{ + struct input_polled_dev *dev = + container_of(work, struct input_polled_dev, work.work); + + dev->poll(dev); + input_polldev_queue_work(dev); +} + +static int input_open_polled_device(struct input_dev *input) +{ + struct input_polled_dev *dev = input_get_drvdata(input); + + if (dev->open) + dev->open(dev); + + /* Only start polling if polling is enabled */ + if (dev->poll_interval > 0) { + dev->poll(dev); + input_polldev_queue_work(dev); + } + + return 0; +} + +static void input_close_polled_device(struct input_dev *input) +{ + struct input_polled_dev *dev = input_get_drvdata(input); + + cancel_delayed_work_sync(&dev->work); + + if (dev->close) + dev->close(dev); +} + +/* SYSFS interface */ + +static ssize_t input_polldev_get_poll(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_polled_dev *polldev = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", polldev->poll_interval); +} + +static ssize_t input_polldev_set_poll(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct input_polled_dev *polldev = dev_get_drvdata(dev); + struct input_dev *input = polldev->input; + unsigned int interval; + int err; + + err = kstrtouint(buf, 0, &interval); + if (err) + return err; + + if (interval < polldev->poll_interval_min) + return -EINVAL; + + if (interval > polldev->poll_interval_max) + return -EINVAL; + + mutex_lock(&input->mutex); + + polldev->poll_interval = interval; + + if (input->users) { + cancel_delayed_work_sync(&polldev->work); + if (polldev->poll_interval > 0) + input_polldev_queue_work(polldev); + } + + mutex_unlock(&input->mutex); + + return count; +} + +static DEVICE_ATTR(poll, S_IRUGO | S_IWUSR, input_polldev_get_poll, + input_polldev_set_poll); + + +static ssize_t input_polldev_get_max(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_polled_dev *polldev = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", polldev->poll_interval_max); +} + +static DEVICE_ATTR(max, S_IRUGO, input_polldev_get_max, NULL); + +static ssize_t input_polldev_get_min(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct input_polled_dev *polldev = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", polldev->poll_interval_min); +} + +static DEVICE_ATTR(min, S_IRUGO, input_polldev_get_min, NULL); + +static struct attribute *sysfs_attrs[] = { + &dev_attr_poll.attr, + &dev_attr_max.attr, + &dev_attr_min.attr, + NULL +}; + +static struct attribute_group input_polldev_attribute_group = { + .attrs = sysfs_attrs +}; + +static const struct attribute_group *input_polldev_attribute_groups[] = { + &input_polldev_attribute_group, + NULL +}; + +/** + * input_allocate_polled_device - allocate memory for polled device + * + * The function allocates memory for a polled device and also + * for an input device associated with this polled device. + */ +struct input_polled_dev *input_allocate_polled_device(void) +{ + struct input_polled_dev *dev; + + dev = kzalloc(sizeof(struct input_polled_dev), GFP_KERNEL); + if (!dev) + return NULL; + + dev->input = input_allocate_device(); + if (!dev->input) { + kfree(dev); + return NULL; + } + + return dev; +} +EXPORT_SYMBOL(input_allocate_polled_device); + +struct input_polled_devres { + struct input_polled_dev *polldev; +}; + +static int devm_input_polldev_match(struct device *dev, void *res, void *data) +{ + struct input_polled_devres *devres = res; + + return devres->polldev == data; +} + +static void devm_input_polldev_release(struct device *dev, void *res) +{ + struct input_polled_devres *devres = res; + struct input_polled_dev *polldev = devres->polldev; + + dev_dbg(dev, "%s: dropping reference/freeing %s\n", + __func__, dev_name(&polldev->input->dev)); + + input_put_device(polldev->input); + kfree(polldev); +} + +static void devm_input_polldev_unregister(struct device *dev, void *res) +{ + struct input_polled_devres *devres = res; + struct input_polled_dev *polldev = devres->polldev; + + dev_dbg(dev, "%s: unregistering device %s\n", + __func__, dev_name(&polldev->input->dev)); + input_unregister_device(polldev->input); + + /* + * Note that we are still holding extra reference to the input + * device so it will stick around until devm_input_polldev_release() + * is called. + */ +} + +/** + * devm_input_allocate_polled_device - allocate managed polled device + * @dev: device owning the polled device being created + * + * Returns prepared &struct input_polled_dev or %NULL. + * + * Managed polled input devices do not need to be explicitly unregistered + * or freed as it will be done automatically when owner device unbinds + * from * its driver (or binding fails). Once such managed polled device + * is allocated, it is ready to be set up and registered in the same + * fashion as regular polled input devices (using + * input_register_polled_device() function). + * + * If you want to manually unregister and free such managed polled devices, + * it can be still done by calling input_unregister_polled_device() and + * input_free_polled_device(), although it is rarely needed. + * + * NOTE: the owner device is set up as parent of input device and users + * should not override it. + */ +struct input_polled_dev *devm_input_allocate_polled_device(struct device *dev) +{ + struct input_polled_dev *polldev; + struct input_polled_devres *devres; + + devres = devres_alloc(devm_input_polldev_release, sizeof(*devres), + GFP_KERNEL); + if (!devres) + return NULL; + + polldev = input_allocate_polled_device(); + if (!polldev) { + devres_free(devres); + return NULL; + } + + polldev->input->dev.parent = dev; + polldev->devres_managed = true; + + devres->polldev = polldev; + devres_add(dev, devres); + + return polldev; +} +EXPORT_SYMBOL(devm_input_allocate_polled_device); + +/** + * input_free_polled_device - free memory allocated for polled device + * @dev: device to free + * + * The function frees memory allocated for polling device and drops + * reference to the associated input device. + */ +void input_free_polled_device(struct input_polled_dev *dev) +{ + if (dev) { + if (dev->devres_managed) + WARN_ON(devres_destroy(dev->input->dev.parent, + devm_input_polldev_release, + devm_input_polldev_match, + dev)); + input_put_device(dev->input); + kfree(dev); + } +} +EXPORT_SYMBOL(input_free_polled_device); + +/** + * input_register_polled_device - register polled device + * @dev: device to register + * + * The function registers previously initialized polled input device + * with input layer. The device should be allocated with call to + * input_allocate_polled_device(). Callers should also set up poll() + * method and set up capabilities (id, name, phys, bits) of the + * corresponding input_dev structure. + */ +int input_register_polled_device(struct input_polled_dev *dev) +{ + struct input_polled_devres *devres = NULL; + struct input_dev *input = dev->input; + int error; + + if (dev->devres_managed) { + devres = devres_alloc(devm_input_polldev_unregister, + sizeof(*devres), GFP_KERNEL); + if (!devres) + return -ENOMEM; + + devres->polldev = dev; + } + + input_set_drvdata(input, dev); + INIT_DELAYED_WORK(&dev->work, input_polled_device_work); + + if (!dev->poll_interval) + dev->poll_interval = 500; + if (!dev->poll_interval_max) + dev->poll_interval_max = dev->poll_interval; + + input->open = input_open_polled_device; + input->close = input_close_polled_device; + + input->dev.groups = input_polldev_attribute_groups; + + error = input_register_device(input); + if (error) { + devres_free(devres); + return error; + } + + /* + * Take extra reference to the underlying input device so + * that it survives call to input_unregister_polled_device() + * and is deleted only after input_free_polled_device() + * has been invoked. This is needed to ease task of freeing + * sparse keymaps. + */ + input_get_device(input); + + if (dev->devres_managed) { + dev_dbg(input->dev.parent, "%s: registering %s with devres.\n", + __func__, dev_name(&input->dev)); + devres_add(input->dev.parent, devres); + } + + return 0; +} +EXPORT_SYMBOL(input_register_polled_device); + +/** + * input_unregister_polled_device - unregister polled device + * @dev: device to unregister + * + * The function unregisters previously registered polled input + * device from input layer. Polling is stopped and device is + * ready to be freed with call to input_free_polled_device(). + */ +void input_unregister_polled_device(struct input_polled_dev *dev) +{ + if (dev->devres_managed) + WARN_ON(devres_destroy(dev->input->dev.parent, + devm_input_polldev_unregister, + devm_input_polldev_match, + dev)); + + input_unregister_device(dev->input); +} +EXPORT_SYMBOL(input_unregister_polled_device); diff -rupN linux.orig/drivers/input/joystick/Kconfig linux/drivers/input/joystick/Kconfig --- linux.orig/drivers/input/joystick/Kconfig 2024-01-10 19:42:05.049159698 +0000 +++ linux/drivers/input/joystick/Kconfig 2024-01-10 20:16:09.688768208 +0000 @@ -400,6 +400,21 @@ config JOYSTICK_N64 Say Y here if you want enable support for the four built-in controller ports on the Nintendo 64 console. +config JOYSTICK_ODROIDGO2 + tristate "ODROIDGO2-Advance joypad driver" + help + Made for ODROIDGO2-Advance. + +config JOYSTICK_ODROIDGO2_V11 + tristate "ODROIDGO2-Advance joypad driver" + help + Made for ODROIDGO2-Advance. + +config JOYSTICK_ODROIDGO3 + tristate "ODROIDGO3 joypad driver" + help + Made for ODROIDGO3. + config JOYSTICK_SENSEHAT tristate "Raspberry Pi Sense HAT joystick" depends on INPUT && I2C diff -rupN linux.orig/drivers/input/joystick/Makefile linux/drivers/input/joystick/Makefile --- linux.orig/drivers/input/joystick/Makefile 2024-01-10 19:42:05.049159698 +0000 +++ linux/drivers/input/joystick/Makefile 2024-01-10 20:16:09.688768208 +0000 @@ -25,6 +25,9 @@ obj-$(CONFIG_JOYSTICK_JOYDUMP) += joydu obj-$(CONFIG_JOYSTICK_MAGELLAN) += magellan.o obj-$(CONFIG_JOYSTICK_MAPLE) += maplecontrol.o obj-$(CONFIG_JOYSTICK_N64) += n64joy.o +obj-$(CONFIG_JOYSTICK_ODROIDGO2) += odroidgo2-joypad.o +obj-$(CONFIG_JOYSTICK_ODROIDGO2_V11) += odroidgo2-v11-joypad.o +obj-$(CONFIG_JOYSTICK_ODROIDGO3) += odroidgo3-joypad.o obj-$(CONFIG_JOYSTICK_PSXPAD_SPI) += psxpad-spi.o obj-$(CONFIG_JOYSTICK_PXRC) += pxrc.o obj-$(CONFIG_JOYSTICK_QWIIC) += qwiic-joystick.o diff -rupN linux.orig/drivers/input/joystick/odroidgo2-joypad.c linux/drivers/input/joystick/odroidgo2-joypad.c --- linux.orig/drivers/input/joystick/odroidgo2-joypad.c 1970-01-01 00:00:00.000000000 +0000 +++ linux/drivers/input/joystick/odroidgo2-joypad.c 2024-01-10 20:16:09.688768208 +0000 @@ -0,0 +1,878 @@ +/* + * SARADC joystick & GPIO Button driver for Linux(Hardkernel ODROIDGO2-Advance) + */ +/* + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +/*----------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*----------------------------------------------------------------------------*/ +#define DRV_NAME "odroidgo2_joypad" + +/*----------------------------------------------------------------------------*/ +struct bt_adc { + /* IIO ADC Channel */ + struct iio_channel *channel; + /* report value (mV) */ + int old_value; + /* report type */ + int report_type; + /* input device init value (mV) */ + int max, min; + /* calibrated adc value */ + int cal; + /* adc scale value */ + int scale; + /* invert report */ + bool invert; +}; + +struct bt_gpio { + /* GPIO Request label */ + const char *label; + /* GPIO Number */ + int num; + /* report type */ + int report_type; + /* report linux code */ + int linux_code; + /* prev button value */ + bool old_value; + /* button press level */ + bool active_level; +}; + +struct joypad { + struct device *dev; + int poll_interval; + + /* report enable/disable */ + bool enable; + + /* report reference point */ + bool invert_absx; + bool invert_absy; + + /* report interval (ms) */ + int bt_gpio_count; + struct bt_gpio *gpios; + /* button auto repeat */ + int auto_repeat; + + /* report threshold (mV) */ + int bt_adc_fuzz, bt_adc_flat; + int bt_adc_x_range, bt_adc_y_range; + /* adc read value scale */ + int bt_adc_scale; + /* joystick deadzone control */ + int bt_adc_deadzone; + int bt_adc_count; + struct bt_adc *adcs; + + struct mutex lock; +}; + +/*----------------------------------------------------------------------------*/ +// +// set to the value in the boot.ini file. (if exist) +// +/*----------------------------------------------------------------------------*/ +static unsigned int g_button_adc_x_range = 0; +static unsigned int g_button_adc_y_range = 0; +static unsigned int g_button_adc_fuzz = 0; +static unsigned int g_button_adc_flat = 0; +static unsigned int g_button_adc_scale = 0; +static unsigned int g_button_adc_deadzone = 0; + +static int __init button_adcx_range_setup(char *str) +{ + if (!str) + return -EINVAL; + + g_button_adc_x_range = simple_strtoul(str, NULL, 10); + + return 0; +} +__setup("button-adc-x-range=", button_adcx_range_setup); + +static int __init button_adcy_range_setup(char *str) +{ + if (!str) + return -EINVAL; + + g_button_adc_y_range = simple_strtoul(str, NULL, 10); + + return 0; +} +__setup("button-adc-y-range=", button_adcy_range_setup); + +static int button_adc_fuzz(char *str) +{ + if (!str) + return -EINVAL; + g_button_adc_fuzz = simple_strtoul(str, NULL, 10); + return 0; +} +__setup("button-adc-fuzz=", button_adc_fuzz); + +static int button_adc_flat(char *str) +{ + if (!str) + return -EINVAL; + g_button_adc_flat = simple_strtoul(str, NULL, 10); + return 0; +} +__setup("button-adc-flat=", button_adc_flat); + +static int button_adc_scale(char *str) +{ + if (!str) + return -EINVAL; + g_button_adc_scale = simple_strtoul(str, NULL, 10); + return 0; +} +__setup("button-adc-scale=", button_adc_scale); + +static int button_adc_deadzone(char *str) +{ + if (!str) + return -EINVAL; + g_button_adc_deadzone = simple_strtoul(str, NULL, 10); + return 0; +} +__setup("button-adc-deadzone=", button_adc_deadzone); + +/*----------------------------------------------------------------------------*/ +/*----------------------------------------------------------------------------*/ +static int joypad_adc_read(struct bt_adc *adc) +{ + int value; + + if (iio_read_channel_processed(adc->channel, &value)) + return 0; + + value *= adc->scale; + + return (adc->invert ? (adc->max - value) : value); +} + +/*----------------------------------------------------------------------------*/ +/*----------------------------------------------------------------------------*/ +/* + * ATTRIBUTES: + * + * /sys/devices/platform/odroidgo2_joypad/poll_interval [rw] + */ +/*----------------------------------------------------------------------------*/ +static ssize_t joypad_store_poll_interval(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct joypad *joypad = platform_get_drvdata(pdev); + + mutex_lock(&joypad->lock); + joypad->poll_interval = simple_strtoul(buf, NULL, 10); + mutex_unlock(&joypad->lock); + + return count; +} + +/*----------------------------------------------------------------------------*/ +static ssize_t joypad_show_poll_interval(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct joypad *joypad = platform_get_drvdata(pdev); + + return sprintf(buf, "%d\n", joypad->poll_interval); +} + +/*----------------------------------------------------------------------------*/ +static DEVICE_ATTR(poll_interval, S_IWUSR | S_IRUGO, + joypad_show_poll_interval, + joypad_store_poll_interval); + +/*----------------------------------------------------------------------------*/ +/* + * ATTRIBUTES: + * + * /sys/devices/platform/odroidgo2_joypad/adc_fuzz [r] + */ +/*----------------------------------------------------------------------------*/ +static ssize_t joypad_show_adc_fuzz(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct joypad *joypad = platform_get_drvdata(pdev); + + return sprintf(buf, "%d\n", joypad->bt_adc_fuzz); +} + +/*----------------------------------------------------------------------------*/ +static DEVICE_ATTR(adc_fuzz, S_IWUSR | S_IRUGO, + joypad_show_adc_fuzz, + NULL); + +/*----------------------------------------------------------------------------*/ +/* + * ATTRIBUTES: + * + * /sys/devices/platform/odroidgo2_joypad/adc_flat [r] + */ +/*----------------------------------------------------------------------------*/ +static ssize_t joypad_show_adc_flat(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct joypad *joypad = platform_get_drvdata(pdev); + + return sprintf(buf, "%d\n", joypad->bt_adc_flat); +} + +/*----------------------------------------------------------------------------*/ +static DEVICE_ATTR(adc_flat, S_IWUSR | S_IRUGO, + joypad_show_adc_flat, + NULL); + +/*----------------------------------------------------------------------------*/ +/* + * ATTRIBUTES: + * + * /sys/devices/platform/odroidgo2_joypad/enable [rw] + */ +/*----------------------------------------------------------------------------*/ +static ssize_t joypad_store_enable(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct joypad *joypad = platform_get_drvdata(pdev); + + mutex_lock(&joypad->lock); + joypad->enable = simple_strtoul(buf, NULL, 10); + mutex_unlock(&joypad->lock); + + return count; +} + +/*----------------------------------------------------------------------------*/ +static ssize_t joypad_show_enable(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct joypad *joypad = platform_get_drvdata(pdev); + + return sprintf(buf, "%d\n", joypad->enable); +} + +/*----------------------------------------------------------------------------*/ +static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, + joypad_show_enable, + joypad_store_enable); + +/*----------------------------------------------------------------------------*/ +/* + * ATTRIBUTES: + * + * /sys/devices/platform/odroidgo2_joypad/adc_cal [rw] + */ +/*----------------------------------------------------------------------------*/ +static ssize_t joypad_store_adc_cal(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct joypad *joypad = platform_get_drvdata(pdev); + bool calibration; + + calibration = simple_strtoul(buf, NULL, 10); + + if (calibration) { + int nbtn; + + mutex_lock(&joypad->lock); + for (nbtn = 0; nbtn < joypad->bt_adc_count; nbtn++) { + struct bt_adc *adc = &joypad->adcs[nbtn]; + + adc->cal = joypad_adc_read(adc); + if (!adc->cal) { + dev_err(joypad->dev, "%s : saradc channels[%d]!\n", + __func__, nbtn); + continue; + } + adc->old_value = adc->cal; + } + mutex_unlock(&joypad->lock); + } + return count; +} + +/*----------------------------------------------------------------------------*/ +static ssize_t joypad_show_adc_cal(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct joypad *joypad = platform_get_drvdata(pdev); + int nbtn; + ssize_t pos; + + for (nbtn = 0, pos = 0; nbtn < joypad->bt_adc_count; nbtn++) { + struct bt_adc *adc = &joypad->adcs[nbtn]; + pos += sprintf(&buf[pos], "adc[%d]->cal = %d ", + nbtn, adc->cal); + } + pos += sprintf(&buf[pos], "\n"); + return pos; +} + +/*----------------------------------------------------------------------------*/ +static DEVICE_ATTR(adc_cal, S_IWUSR | S_IRUGO, + joypad_show_adc_cal, + joypad_store_adc_cal); + +/*----------------------------------------------------------------------------*/ +/*----------------------------------------------------------------------------*/ +static struct attribute *joypad_attrs[] = { + &dev_attr_poll_interval.attr, + &dev_attr_adc_fuzz.attr, + &dev_attr_adc_flat.attr, + &dev_attr_enable.attr, + &dev_attr_adc_cal.attr, + NULL, +}; + +static struct attribute_group joypad_attr_group = { + .attrs = joypad_attrs, +}; + +/*----------------------------------------------------------------------------*/ +/*----------------------------------------------------------------------------*/ +static void joypad_gpio_check(struct input_polled_dev *poll_dev) +{ + struct joypad *joypad = poll_dev->private; + int nbtn, value; + + for (nbtn = 0; nbtn < joypad->bt_gpio_count; nbtn++) { + struct bt_gpio *gpio = &joypad->gpios[nbtn]; + + if (gpio_get_value_cansleep(gpio->num) < 0) { + dev_err(joypad->dev, "failed to get gpio state\n"); + continue; + } + value = gpio_get_value(gpio->num); + if (value != gpio->old_value) { + input_event(poll_dev->input, + gpio->report_type, + gpio->linux_code, + (value == gpio->active_level) ? 1 : 0); + gpio->old_value = value; + } + } + input_sync(poll_dev->input); +} + +/*----------------------------------------------------------------------------*/ +static void joypad_adc_check(struct input_polled_dev *poll_dev) +{ + struct joypad *joypad = poll_dev->private; + int nbtn, value; + + for (nbtn = 0; nbtn < joypad->bt_adc_count; nbtn++) { + struct bt_adc *adc = &joypad->adcs[nbtn]; + + value = joypad_adc_read(adc); + if (!value) { + dev_err(joypad->dev, "%s : saradc channels[%d]!\n", + __func__, nbtn); + continue; + } + + /* Joystick Deadzone check */ + if (joypad->bt_adc_deadzone) { + if ((value < adc->cal + joypad->bt_adc_deadzone) && + (value > adc->cal - joypad->bt_adc_deadzone)) + value = adc->cal; + } + value = value - adc->cal; + value = value > adc->max ? adc->max : value; + value = value < adc->min ? adc->min : value; + + if (nbtn == 0) + { + // adc-x value is default inverted(h/w) + input_report_abs(poll_dev->input, + adc->report_type, value * (-1)); + } + else + { + input_report_abs(poll_dev->input, + adc->report_type, value); + } + adc->old_value = value; + } + input_sync(poll_dev->input); +} + +/*----------------------------------------------------------------------------*/ +static void joypad_poll(struct input_polled_dev *poll_dev) +{ + struct joypad *joypad = poll_dev->private; + + if (joypad->enable) { + joypad_adc_check(poll_dev); + joypad_gpio_check(poll_dev); + } + if (poll_dev->poll_interval != joypad->poll_interval) { + mutex_lock(&joypad->lock); + poll_dev->poll_interval = joypad->poll_interval; + mutex_unlock(&joypad->lock); + } +} + +/*----------------------------------------------------------------------------*/ +static void joypad_open(struct input_polled_dev *poll_dev) +{ + struct joypad *joypad = poll_dev->private; + int nbtn; + + for (nbtn = 0; nbtn < joypad->bt_gpio_count; nbtn++) { + struct bt_gpio *gpio = &joypad->gpios[nbtn]; + gpio->old_value = gpio->active_level ? 0 : 1; + } + for (nbtn = 0; nbtn < joypad->bt_adc_count; nbtn++) { + struct bt_adc *adc = &joypad->adcs[nbtn]; + + adc->old_value = joypad_adc_read(adc); + if (!adc->old_value) { + dev_err(joypad->dev, "%s : saradc channels[%d]!\n", + __func__, nbtn); + continue; + } + adc->cal = adc->old_value; + dev_info(joypad->dev, "%s : adc[%d] adc->cal = %d\n", + __func__, nbtn, adc->cal); + } + /* buttons status sync */ + joypad_adc_check(poll_dev); + joypad_gpio_check(poll_dev); + + /* button report enable */ + mutex_lock(&joypad->lock); + joypad->enable = true; + mutex_unlock(&joypad->lock); + + dev_info(joypad->dev, "%s : opened\n", __func__); +} + +/*----------------------------------------------------------------------------*/ +static void joypad_close(struct input_polled_dev *poll_dev) +{ + struct joypad *joypad = poll_dev->private; + + /* button report disable */ + mutex_lock(&joypad->lock); + joypad->enable = false; + mutex_unlock(&joypad->lock); + + dev_info(joypad->dev, "%s : closed\n", __func__); +} + +/*----------------------------------------------------------------------------*/ +static int joypad_adc_setup(struct device *dev, struct joypad *joypad) +{ + int nbtn = 0; + + joypad->adcs = devm_kzalloc(dev, joypad->bt_adc_count * + sizeof(struct bt_adc), GFP_KERNEL); + + if (!joypad->adcs) { + dev_err(dev, "%s devm_kzmalloc error!", __func__); + return -ENOMEM; + } + + for (nbtn = 0; nbtn < joypad->bt_adc_count; nbtn++) { + struct bt_adc *adc = &joypad->adcs[nbtn]; + enum iio_chan_type type; + + adc->scale = joypad->bt_adc_scale; + if (nbtn) { + adc->channel = + devm_iio_channel_get(dev, "joy_y"); + adc->report_type = ABS_Y; + if (joypad->invert_absy) + adc->invert = true; + + adc->max = (joypad->bt_adc_y_range / 2) - 1; + adc->min = -(joypad->bt_adc_y_range / 2); + } + else { + adc->channel = + devm_iio_channel_get(dev, "joy_x"); + adc->report_type = ABS_X; + if (joypad->invert_absx) + adc->invert = true; + + adc->max = (joypad->bt_adc_x_range / 2) - 1; + adc->min = -(joypad->bt_adc_x_range / 2); + } + + if (IS_ERR(adc->channel)) { + dev_err(dev, "iio channel[%d] get error\n", nbtn); + return -EINVAL; + } + if (!adc->channel->indio_dev) + return -ENXIO; + + if (iio_get_channel_type(adc->channel, &type)) + return -EINVAL; + + if (type != IIO_VOLTAGE) { + dev_err(dev, "Incompatible channel %d type %d\n", + nbtn, type); + return -EINVAL; + } + } + if (nbtn == 0) + return -EINVAL; + + return 0; +} + +/*----------------------------------------------------------------------------*/ +static int joypad_gpio_setup(struct device *dev, struct joypad *joypad) +{ + struct device_node *node, *pp; + int nbtn; + + node = dev->of_node; + if (!node) + return -ENODEV; + + joypad->gpios = devm_kzalloc(dev, joypad->bt_gpio_count * + sizeof(struct bt_gpio), GFP_KERNEL); + + if (!joypad->gpios) { + dev_err(dev, "%s devm_kzmalloc error!", __func__); + return -ENOMEM; + } + + nbtn = 0; + for_each_child_of_node(node, pp) { + enum of_gpio_flags flags; + struct bt_gpio *gpio = &joypad->gpios[nbtn++]; + int error; + + gpio->num = of_get_gpio_flags(pp, 0, &flags); + if (gpio->num < 0) { + error = gpio->num; + dev_err(dev, "Failed to get gpio flags, error: %d\n", + error); + return error; + } + + /* gpio active level(key press level) */ + gpio->active_level = (flags & OF_GPIO_ACTIVE_LOW) ? 0 : 1; + + gpio->label = of_get_property(pp, "label", NULL); + + if (gpio_is_valid(gpio->num)) { + error = devm_gpio_request_one(dev, gpio->num, + GPIOF_IN, gpio->label); + if (error < 0) { + dev_err(dev, + "Failed to request GPIO %d, error %d\n", + gpio->num, error); + return error; + } + } + if (of_property_read_u32(pp, "linux,code", &gpio->linux_code)) { + dev_err(dev, "Button without keycode: 0x%x\n", + gpio->num); + return -EINVAL; + } + if (of_property_read_u32(pp, "linux,input-type", + &gpio->report_type)) + gpio->report_type = EV_KEY; + } + if (nbtn == 0) + return -EINVAL; + + return 0; +} + +/*----------------------------------------------------------------------------*/ +static int joypad_input_setup(struct device *dev, struct joypad *joypad) +{ + struct input_polled_dev *poll_dev; + struct input_dev *input; + int nbtn, error; + + poll_dev = devm_input_allocate_polled_device(dev); + if (!poll_dev) { + dev_err(dev, "no memory for polled device\n"); + return -ENOMEM; + } + + poll_dev->private = joypad; + poll_dev->poll = joypad_poll; + poll_dev->poll_interval = joypad->poll_interval; + poll_dev->open = joypad_open; + poll_dev->close = joypad_close; + + input = poll_dev->input; + + input->name = DRV_NAME; + input->phys = DRV_NAME"/input0"; + + input->id.bustype = BUS_HOST; + input->id.vendor = 0x0001; + input->id.product = 0x0001; + input->id.version = 0x0101; + + /* IIO ADC key setup (0 mv ~ 1800 mv) * adc->scale */ + __set_bit(EV_ABS, input->evbit); + for(nbtn = 0; nbtn < joypad->bt_adc_count; nbtn++) { + struct bt_adc *adc = &joypad->adcs[nbtn]; + input_set_abs_params(input, adc->report_type, + adc->min, adc->max, + joypad->bt_adc_fuzz, + joypad->bt_adc_flat); + dev_info(dev, + "%s : SCALE = %d, ABS min = %d, max = %d," + " fuzz = %d, flat = %d, deadzone = %d\n", + __func__, adc->scale, adc->min, adc->max, + joypad->bt_adc_fuzz, joypad->bt_adc_flat, + joypad->bt_adc_deadzone); + } + + /* GPIO key setup */ + __set_bit(EV_KEY, input->evbit); + for(nbtn = 0; nbtn < joypad->bt_gpio_count; nbtn++) { + struct bt_gpio *gpio = &joypad->gpios[nbtn]; + input_set_capability(input, gpio->report_type, + gpio->linux_code); + } + + if (joypad->auto_repeat) + __set_bit(EV_REP, input->evbit); + + joypad->dev = dev; + + error = input_register_polled_device(poll_dev); + if (error) { + dev_err(dev, "unable to register polled device, err=%d\n", + error); + return error; + } + return 0; +} + +/*----------------------------------------------------------------------------*/ +static void joypad_setup_value_check(struct device *dev, struct joypad *joypad) +{ + /* + fuzz: specifies fuzz value that is used to filter noise from + the event stream. + */ + if (g_button_adc_fuzz) + joypad->bt_adc_fuzz = g_button_adc_fuzz; + else + device_property_read_u32(dev, "button-adc-fuzz", + &joypad->bt_adc_fuzz); + /* + flat: values that are within this value will be discarded by + joydev interface and reported as 0 instead. + */ + if (g_button_adc_flat) + joypad->bt_adc_flat = g_button_adc_flat; + else + device_property_read_u32(dev, "button-adc-flat", + &joypad->bt_adc_flat); + + /* Joystick report value control */ + if (g_button_adc_scale) + joypad->bt_adc_scale = g_button_adc_scale; + else + device_property_read_u32(dev, "button-adc-scale", + &joypad->bt_adc_scale); + + /* Joystick deadzone value control */ + if (g_button_adc_deadzone) + joypad->bt_adc_deadzone = g_button_adc_deadzone; + else + device_property_read_u32(dev, "button-adc-deadzone", + &joypad->bt_adc_deadzone); + + if (g_button_adc_x_range) + joypad->bt_adc_x_range = g_button_adc_x_range; + else + device_property_read_u32(dev, "button-adc-x-range", + &joypad->bt_adc_x_range); + if (g_button_adc_y_range) + joypad->bt_adc_y_range = g_button_adc_y_range; + else + device_property_read_u32(dev, "button-adc-y-range", + &joypad->bt_adc_y_range); +} + +/*----------------------------------------------------------------------------*/ +static int joypad_dt_parse(struct device *dev, struct joypad *joypad) +{ + int error = 0; + + /* initialize value check from boot.ini */ + joypad_setup_value_check(dev, joypad); + + device_property_read_u32(dev, "button-adc-count", + &joypad->bt_adc_count); + + device_property_read_u32(dev, "poll-interval", + &joypad->poll_interval); + + joypad->auto_repeat = device_property_present(dev, "autorepeat"); + + /* change the report reference point? (ADC MAX - read value) */ + joypad->invert_absx = device_property_present(dev, "invert-absx"); + joypad->invert_absy = device_property_present(dev, "invert-absy"); + dev_info(dev, "%s : invert-absx = %d, inveret-absy = %d\n", + __func__, joypad->invert_absx, joypad->invert_absy); + + joypad->bt_gpio_count = device_get_child_node_count(dev); + + if ((joypad->bt_adc_count == 0) || (joypad->bt_gpio_count == 0)) { + dev_err(dev, "adc key = %d, gpio key = %d error!", + joypad->bt_adc_count, joypad->bt_gpio_count); + return -EINVAL; + } + + error = joypad_adc_setup(dev, joypad); + if (error) + return error; + + error = joypad_gpio_setup(dev, joypad); + if (error) + return error; + + return error; +} + +/*----------------------------------------------------------------------------*/ +static int joypad_probe(struct platform_device *pdev) +{ + struct joypad *joypad; + struct device *dev = &pdev->dev; + int error; + + joypad = devm_kzalloc(dev, sizeof(struct joypad), GFP_KERNEL); + if (!joypad) { + dev_err(dev, "joypad devm_kzmalloc error!"); + return -ENOMEM; + } + + /* device tree data parse */ + error = joypad_dt_parse(dev, joypad); + if (error) { + dev_err(dev, "dt parse error!(err = %d)\n", error); + return error; + } + + mutex_init(&joypad->lock); + platform_set_drvdata(pdev, joypad); + + error = sysfs_create_group(&pdev->dev.kobj, &joypad_attr_group); + if (error) { + dev_err(dev, "create sysfs group fail, error: %d\n", + error); + return error; + } + + /* poll input device setup */ + error = joypad_input_setup(dev, joypad); + if (error) { + dev_err(dev, "input setup failed!(err = %d)\n", error); + return error; + } + dev_info(dev, "%s : probe success\n", __func__); + return 0; +} + +/*----------------------------------------------------------------------------*/ +static const struct of_device_id joypad_of_match[] = { + { .compatible = "odroidgo2-joypad", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, joypad_of_match); + +/*----------------------------------------------------------------------------*/ +static struct platform_driver joypad_driver = { + .probe = joypad_probe, + .driver = { + .name = DRV_NAME, + .of_match_table = of_match_ptr(joypad_of_match), + }, +}; + +/*----------------------------------------------------------------------------*/ +static int __init joypad_init(void) +{ + return platform_driver_register(&joypad_driver); +} + +/*----------------------------------------------------------------------------*/ +static void __exit joypad_exit(void) +{ + platform_driver_unregister(&joypad_driver); +} + +/*----------------------------------------------------------------------------*/ +late_initcall(joypad_init); +module_exit(joypad_exit); + +/*----------------------------------------------------------------------------*/ +MODULE_AUTHOR("Hardkernel Co.,LTD"); +MODULE_DESCRIPTION("Keypad driver(ADC&GPIO) for ODROIDGO-Advance"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); + +/*----------------------------------------------------------------------------*/ diff -rupN linux.orig/drivers/input/joystick/odroidgo2-v11-joypad.c linux/drivers/input/joystick/odroidgo2-v11-joypad.c --- linux.orig/drivers/input/joystick/odroidgo2-v11-joypad.c 1970-01-01 00:00:00.000000000 +0000 +++ linux/drivers/input/joystick/odroidgo2-v11-joypad.c 2024-01-10 20:16:09.688768208 +0000 @@ -0,0 +1,878 @@ +/* + * SARADC joystick & GPIO Button driver for Linux(Hardkernel ODROIDGO2-Advance) + */ +/* + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +/*----------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*----------------------------------------------------------------------------*/ +#define DRV_NAME "odroidgo2_v11_joypad" + +/*----------------------------------------------------------------------------*/ +struct bt_adc { + /* IIO ADC Channel */ + struct iio_channel *channel; + /* report value (mV) */ + int old_value; + /* report type */ + int report_type; + /* input device init value (mV) */ + int max, min; + /* calibrated adc value */ + int cal; + /* adc scale value */ + int scale; + /* invert report */ + bool invert; +}; + +struct bt_gpio { + /* GPIO Request label */ + const char *label; + /* GPIO Number */ + int num; + /* report type */ + int report_type; + /* report linux code */ + int linux_code; + /* prev button value */ + bool old_value; + /* button press level */ + bool active_level; +}; + +struct joypad { + struct device *dev; + int poll_interval; + + /* report enable/disable */ + bool enable; + + /* report reference point */ + bool invert_absx; + bool invert_absy; + + /* report interval (ms) */ + int bt_gpio_count; + struct bt_gpio *gpios; + /* button auto repeat */ + int auto_repeat; + + /* report threshold (mV) */ + int bt_adc_fuzz, bt_adc_flat; + int bt_adc_x_range, bt_adc_y_range; + /* adc read value scale */ + int bt_adc_scale; + /* joystick deadzone control */ + int bt_adc_deadzone; + int bt_adc_count; + struct bt_adc *adcs; + + struct mutex lock; +}; + +/*----------------------------------------------------------------------------*/ +// +// set to the value in the boot.ini file. (if exist) +// +/*----------------------------------------------------------------------------*/ +static unsigned int g_button_adc_x_range = 0; +static unsigned int g_button_adc_y_range = 0; +static unsigned int g_button_adc_fuzz = 0; +static unsigned int g_button_adc_flat = 0; +static unsigned int g_button_adc_scale = 0; +static unsigned int g_button_adc_deadzone = 0; + +static int __init button_adcx_range_setup(char *str) +{ + if (!str) + return -EINVAL; + + g_button_adc_x_range = simple_strtoul(str, NULL, 10); + + return 0; +} +__setup("button-adc-x-range=", button_adcx_range_setup); + +static int __init button_adcy_range_setup(char *str) +{ + if (!str) + return -EINVAL; + + g_button_adc_y_range = simple_strtoul(str, NULL, 10); + + return 0; +} +__setup("button-adc-y-range=", button_adcy_range_setup); + +static int button_adc_fuzz(char *str) +{ + if (!str) + return -EINVAL; + g_button_adc_fuzz = simple_strtoul(str, NULL, 10); + return 0; +} +__setup("button-adc-fuzz=", button_adc_fuzz); + +static int button_adc_flat(char *str) +{ + if (!str) + return -EINVAL; + g_button_adc_flat = simple_strtoul(str, NULL, 10); + return 0; +} +__setup("button-adc-flat=", button_adc_flat); + +static int button_adc_scale(char *str) +{ + if (!str) + return -EINVAL; + g_button_adc_scale = simple_strtoul(str, NULL, 10); + return 0; +} +__setup("button-adc-scale=", button_adc_scale); + +static int button_adc_deadzone(char *str) +{ + if (!str) + return -EINVAL; + g_button_adc_deadzone = simple_strtoul(str, NULL, 10); + return 0; +} +__setup("button-adc-deadzone=", button_adc_deadzone); + +/*----------------------------------------------------------------------------*/ +/*----------------------------------------------------------------------------*/ +static int joypad_adc_read(struct bt_adc *adc) +{ + int value; + + if (iio_read_channel_processed(adc->channel, &value)) + return 0; + + value *= adc->scale; + + return (adc->invert ? (adc->max - value) : value); +} + +/*----------------------------------------------------------------------------*/ +/*----------------------------------------------------------------------------*/ +/* + * ATTRIBUTES: + * + * /sys/devices/platform/odroidgo2_joypad/poll_interval [rw] + */ +/*----------------------------------------------------------------------------*/ +static ssize_t joypad_store_poll_interval(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct joypad *joypad = platform_get_drvdata(pdev); + + mutex_lock(&joypad->lock); + joypad->poll_interval = simple_strtoul(buf, NULL, 10); + mutex_unlock(&joypad->lock); + + return count; +} + +/*----------------------------------------------------------------------------*/ +static ssize_t joypad_show_poll_interval(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct joypad *joypad = platform_get_drvdata(pdev); + + return sprintf(buf, "%d\n", joypad->poll_interval); +} + +/*----------------------------------------------------------------------------*/ +static DEVICE_ATTR(poll_interval, S_IWUSR | S_IRUGO, + joypad_show_poll_interval, + joypad_store_poll_interval); + +/*----------------------------------------------------------------------------*/ +/* + * ATTRIBUTES: + * + * /sys/devices/platform/odroidgo2_joypad/adc_fuzz [r] + */ +/*----------------------------------------------------------------------------*/ +static ssize_t joypad_show_adc_fuzz(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct joypad *joypad = platform_get_drvdata(pdev); + + return sprintf(buf, "%d\n", joypad->bt_adc_fuzz); +} + +/*----------------------------------------------------------------------------*/ +static DEVICE_ATTR(adc_fuzz, S_IWUSR | S_IRUGO, + joypad_show_adc_fuzz, + NULL); + +/*----------------------------------------------------------------------------*/ +/* + * ATTRIBUTES: + * + * /sys/devices/platform/odroidgo2_joypad/adc_flat [r] + */ +/*----------------------------------------------------------------------------*/ +static ssize_t joypad_show_adc_flat(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct joypad *joypad = platform_get_drvdata(pdev); + + return sprintf(buf, "%d\n", joypad->bt_adc_flat); +} + +/*----------------------------------------------------------------------------*/ +static DEVICE_ATTR(adc_flat, S_IWUSR | S_IRUGO, + joypad_show_adc_flat, + NULL); + +/*----------------------------------------------------------------------------*/ +/* + * ATTRIBUTES: + * + * /sys/devices/platform/odroidgo2_joypad/enable [rw] + */ +/*----------------------------------------------------------------------------*/ +static ssize_t joypad_store_enable(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct joypad *joypad = platform_get_drvdata(pdev); + + mutex_lock(&joypad->lock); + joypad->enable = simple_strtoul(buf, NULL, 10); + mutex_unlock(&joypad->lock); + + return count; +} + +/*----------------------------------------------------------------------------*/ +static ssize_t joypad_show_enable(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct joypad *joypad = platform_get_drvdata(pdev); + + return sprintf(buf, "%d\n", joypad->enable); +} + +/*----------------------------------------------------------------------------*/ +static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, + joypad_show_enable, + joypad_store_enable); + +/*----------------------------------------------------------------------------*/ +/* + * ATTRIBUTES: + * + * /sys/devices/platform/odroidgo2_joypad/adc_cal [rw] + */ +/*----------------------------------------------------------------------------*/ +static ssize_t joypad_store_adc_cal(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct joypad *joypad = platform_get_drvdata(pdev); + bool calibration; + + calibration = simple_strtoul(buf, NULL, 10); + + if (calibration) { + int nbtn; + + mutex_lock(&joypad->lock); + for (nbtn = 0; nbtn < joypad->bt_adc_count; nbtn++) { + struct bt_adc *adc = &joypad->adcs[nbtn]; + + adc->cal = joypad_adc_read(adc); + if (!adc->cal) { + dev_err(joypad->dev, "%s : saradc channels[%d]!\n", + __func__, nbtn); + continue; + } + adc->old_value = adc->cal; + } + mutex_unlock(&joypad->lock); + } + return count; +} + +/*----------------------------------------------------------------------------*/ +static ssize_t joypad_show_adc_cal(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct joypad *joypad = platform_get_drvdata(pdev); + int nbtn; + ssize_t pos; + + for (nbtn = 0, pos = 0; nbtn < joypad->bt_adc_count; nbtn++) { + struct bt_adc *adc = &joypad->adcs[nbtn]; + pos += sprintf(&buf[pos], "adc[%d]->cal = %d ", + nbtn, adc->cal); + } + pos += sprintf(&buf[pos], "\n"); + return pos; +} + +/*----------------------------------------------------------------------------*/ +static DEVICE_ATTR(adc_cal, S_IWUSR | S_IRUGO, + joypad_show_adc_cal, + joypad_store_adc_cal); + +/*----------------------------------------------------------------------------*/ +/*----------------------------------------------------------------------------*/ +static struct attribute *joypad_attrs[] = { + &dev_attr_poll_interval.attr, + &dev_attr_adc_fuzz.attr, + &dev_attr_adc_flat.attr, + &dev_attr_enable.attr, + &dev_attr_adc_cal.attr, + NULL, +}; + +static struct attribute_group joypad_attr_group = { + .attrs = joypad_attrs, +}; + +/*----------------------------------------------------------------------------*/ +/*----------------------------------------------------------------------------*/ +static void joypad_gpio_check(struct input_polled_dev *poll_dev) +{ + struct joypad *joypad = poll_dev->private; + int nbtn, value; + + for (nbtn = 0; nbtn < joypad->bt_gpio_count; nbtn++) { + struct bt_gpio *gpio = &joypad->gpios[nbtn]; + + if (gpio_get_value_cansleep(gpio->num) < 0) { + dev_err(joypad->dev, "failed to get gpio state\n"); + continue; + } + value = gpio_get_value(gpio->num); + if (value != gpio->old_value) { + input_event(poll_dev->input, + gpio->report_type, + gpio->linux_code, + (value == gpio->active_level) ? 1 : 0); + gpio->old_value = value; + } + } + input_sync(poll_dev->input); +} + +/*----------------------------------------------------------------------------*/ +static void joypad_adc_check(struct input_polled_dev *poll_dev) +{ + struct joypad *joypad = poll_dev->private; + int nbtn, value; + + for (nbtn = 0; nbtn < joypad->bt_adc_count; nbtn++) { + struct bt_adc *adc = &joypad->adcs[nbtn]; + + value = joypad_adc_read(adc); + if (!value) { + dev_err(joypad->dev, "%s : saradc channels[%d]!\n", + __func__, nbtn); + continue; + } + + /* Joystick Deadzone check */ + if (joypad->bt_adc_deadzone) { + if ((value < adc->cal + joypad->bt_adc_deadzone) && + (value > adc->cal - joypad->bt_adc_deadzone)) + value = adc->cal; + } + value = value - adc->cal; + value = value > adc->max ? adc->max : value; + value = value < adc->min ? adc->min : value; + + if (nbtn == 0) + { + // adc-x value is default inverted(h/w) + input_report_abs(poll_dev->input, + adc->report_type, value * (-1)); + } + else + { + input_report_abs(poll_dev->input, + adc->report_type, value); + } + adc->old_value = value; + } + input_sync(poll_dev->input); +} + +/*----------------------------------------------------------------------------*/ +static void joypad_poll(struct input_polled_dev *poll_dev) +{ + struct joypad *joypad = poll_dev->private; + + if (joypad->enable) { + joypad_adc_check(poll_dev); + joypad_gpio_check(poll_dev); + } + if (poll_dev->poll_interval != joypad->poll_interval) { + mutex_lock(&joypad->lock); + poll_dev->poll_interval = joypad->poll_interval; + mutex_unlock(&joypad->lock); + } +} + +/*----------------------------------------------------------------------------*/ +static void joypad_open(struct input_polled_dev *poll_dev) +{ + struct joypad *joypad = poll_dev->private; + int nbtn; + + for (nbtn = 0; nbtn < joypad->bt_gpio_count; nbtn++) { + struct bt_gpio *gpio = &joypad->gpios[nbtn]; + gpio->old_value = gpio->active_level ? 0 : 1; + } + for (nbtn = 0; nbtn < joypad->bt_adc_count; nbtn++) { + struct bt_adc *adc = &joypad->adcs[nbtn]; + + adc->old_value = joypad_adc_read(adc); + if (!adc->old_value) { + dev_err(joypad->dev, "%s : saradc channels[%d]!\n", + __func__, nbtn); + continue; + } + adc->cal = adc->old_value; + dev_info(joypad->dev, "%s : adc[%d] adc->cal = %d\n", + __func__, nbtn, adc->cal); + } + /* buttons status sync */ + joypad_adc_check(poll_dev); + joypad_gpio_check(poll_dev); + + /* button report enable */ + mutex_lock(&joypad->lock); + joypad->enable = true; + mutex_unlock(&joypad->lock); + + dev_info(joypad->dev, "%s : opened\n", __func__); +} + +/*----------------------------------------------------------------------------*/ +static void joypad_close(struct input_polled_dev *poll_dev) +{ + struct joypad *joypad = poll_dev->private; + + /* button report disable */ + mutex_lock(&joypad->lock); + joypad->enable = false; + mutex_unlock(&joypad->lock); + + dev_info(joypad->dev, "%s : closed\n", __func__); +} + +/*----------------------------------------------------------------------------*/ +static int joypad_adc_setup(struct device *dev, struct joypad *joypad) +{ + int nbtn = 0; + + joypad->adcs = devm_kzalloc(dev, joypad->bt_adc_count * + sizeof(struct bt_adc), GFP_KERNEL); + + if (!joypad->adcs) { + dev_err(dev, "%s devm_kzmalloc error!", __func__); + return -ENOMEM; + } + + for (nbtn = 0; nbtn < joypad->bt_adc_count; nbtn++) { + struct bt_adc *adc = &joypad->adcs[nbtn]; + enum iio_chan_type type; + + adc->scale = joypad->bt_adc_scale; + if (nbtn) { + adc->channel = + devm_iio_channel_get(dev, "joy_y"); + adc->report_type = ABS_Y; + if (joypad->invert_absy) + adc->invert = true; + + adc->max = (joypad->bt_adc_y_range / 2) - 1; + adc->min = -(joypad->bt_adc_y_range / 2); + } + else { + adc->channel = + devm_iio_channel_get(dev, "joy_x"); + adc->report_type = ABS_X; + if (joypad->invert_absx) + adc->invert = true; + + adc->max = (joypad->bt_adc_x_range / 2) - 1; + adc->min = -(joypad->bt_adc_x_range / 2); + } + + if (IS_ERR(adc->channel)) { + dev_err(dev, "iio channel[%d] get error\n", nbtn); + return -EINVAL; + } + if (!adc->channel->indio_dev) + return -ENXIO; + + if (iio_get_channel_type(adc->channel, &type)) + return -EINVAL; + + if (type != IIO_VOLTAGE) { + dev_err(dev, "Incompatible channel %d type %d\n", + nbtn, type); + return -EINVAL; + } + } + if (nbtn == 0) + return -EINVAL; + + return 0; +} + +/*----------------------------------------------------------------------------*/ +static int joypad_gpio_setup(struct device *dev, struct joypad *joypad) +{ + struct device_node *node, *pp; + int nbtn; + + node = dev->of_node; + if (!node) + return -ENODEV; + + joypad->gpios = devm_kzalloc(dev, joypad->bt_gpio_count * + sizeof(struct bt_gpio), GFP_KERNEL); + + if (!joypad->gpios) { + dev_err(dev, "%s devm_kzmalloc error!", __func__); + return -ENOMEM; + } + + nbtn = 0; + for_each_child_of_node(node, pp) { + enum of_gpio_flags flags; + struct bt_gpio *gpio = &joypad->gpios[nbtn++]; + int error; + + gpio->num = of_get_gpio_flags(pp, 0, &flags); + if (gpio->num < 0) { + error = gpio->num; + dev_err(dev, "Failed to get gpio flags, error: %d\n", + error); + return error; + } + + /* gpio active level(key press level) */ + gpio->active_level = (flags & OF_GPIO_ACTIVE_LOW) ? 0 : 1; + + gpio->label = of_get_property(pp, "label", NULL); + + if (gpio_is_valid(gpio->num)) { + error = devm_gpio_request_one(dev, gpio->num, + GPIOF_IN, gpio->label); + if (error < 0) { + dev_err(dev, + "Failed to request GPIO %d, error %d\n", + gpio->num, error); + return error; + } + } + if (of_property_read_u32(pp, "linux,code", &gpio->linux_code)) { + dev_err(dev, "Button without keycode: 0x%x\n", + gpio->num); + return -EINVAL; + } + if (of_property_read_u32(pp, "linux,input-type", + &gpio->report_type)) + gpio->report_type = EV_KEY; + } + if (nbtn == 0) + return -EINVAL; + + return 0; +} + +/*----------------------------------------------------------------------------*/ +static int joypad_input_setup(struct device *dev, struct joypad *joypad) +{ + struct input_polled_dev *poll_dev; + struct input_dev *input; + int nbtn, error; + + poll_dev = devm_input_allocate_polled_device(dev); + if (!poll_dev) { + dev_err(dev, "no memory for polled device\n"); + return -ENOMEM; + } + + poll_dev->private = joypad; + poll_dev->poll = joypad_poll; + poll_dev->poll_interval = joypad->poll_interval; + poll_dev->open = joypad_open; + poll_dev->close = joypad_close; + + input = poll_dev->input; + + input->name = DRV_NAME; + input->phys = DRV_NAME"/input0"; + + input->id.bustype = BUS_HOST; + input->id.vendor = 0x0001; + input->id.product = 0x0002; + input->id.version = 0x0101; + + /* IIO ADC key setup (0 mv ~ 1800 mv) * adc->scale */ + __set_bit(EV_ABS, input->evbit); + for(nbtn = 0; nbtn < joypad->bt_adc_count; nbtn++) { + struct bt_adc *adc = &joypad->adcs[nbtn]; + input_set_abs_params(input, adc->report_type, + adc->min, adc->max, + joypad->bt_adc_fuzz, + joypad->bt_adc_flat); + dev_info(dev, + "%s : SCALE = %d, ABS min = %d, max = %d," + " fuzz = %d, flat = %d, deadzone = %d\n", + __func__, adc->scale, adc->min, adc->max, + joypad->bt_adc_fuzz, joypad->bt_adc_flat, + joypad->bt_adc_deadzone); + } + + /* GPIO key setup */ + __set_bit(EV_KEY, input->evbit); + for(nbtn = 0; nbtn < joypad->bt_gpio_count; nbtn++) { + struct bt_gpio *gpio = &joypad->gpios[nbtn]; + input_set_capability(input, gpio->report_type, + gpio->linux_code); + } + + if (joypad->auto_repeat) + __set_bit(EV_REP, input->evbit); + + joypad->dev = dev; + + error = input_register_polled_device(poll_dev); + if (error) { + dev_err(dev, "unable to register polled device, err=%d\n", + error); + return error; + } + return 0; +} + +/*----------------------------------------------------------------------------*/ +static void joypad_setup_value_check(struct device *dev, struct joypad *joypad) +{ + /* + fuzz: specifies fuzz value that is used to filter noise from + the event stream. + */ + if (g_button_adc_fuzz) + joypad->bt_adc_fuzz = g_button_adc_fuzz; + else + device_property_read_u32(dev, "button-adc-fuzz", + &joypad->bt_adc_fuzz); + /* + flat: values that are within this value will be discarded by + joydev interface and reported as 0 instead. + */ + if (g_button_adc_flat) + joypad->bt_adc_flat = g_button_adc_flat; + else + device_property_read_u32(dev, "button-adc-flat", + &joypad->bt_adc_flat); + + /* Joystick report value control */ + if (g_button_adc_scale) + joypad->bt_adc_scale = g_button_adc_scale; + else + device_property_read_u32(dev, "button-adc-scale", + &joypad->bt_adc_scale); + + /* Joystick deadzone value control */ + if (g_button_adc_deadzone) + joypad->bt_adc_deadzone = g_button_adc_deadzone; + else + device_property_read_u32(dev, "button-adc-deadzone", + &joypad->bt_adc_deadzone); + + if (g_button_adc_x_range) + joypad->bt_adc_x_range = g_button_adc_x_range; + else + device_property_read_u32(dev, "button-adc-x-range", + &joypad->bt_adc_x_range); + if (g_button_adc_y_range) + joypad->bt_adc_y_range = g_button_adc_y_range; + else + device_property_read_u32(dev, "button-adc-y-range", + &joypad->bt_adc_y_range); +} + +/*----------------------------------------------------------------------------*/ +static int joypad_dt_parse(struct device *dev, struct joypad *joypad) +{ + int error = 0; + + /* initialize value check from boot.ini */ + joypad_setup_value_check(dev, joypad); + + device_property_read_u32(dev, "button-adc-count", + &joypad->bt_adc_count); + + device_property_read_u32(dev, "poll-interval", + &joypad->poll_interval); + + joypad->auto_repeat = device_property_present(dev, "autorepeat"); + + /* change the report reference point? (ADC MAX - read value) */ + joypad->invert_absx = device_property_present(dev, "invert-absx"); + joypad->invert_absy = device_property_present(dev, "invert-absy"); + dev_info(dev, "%s : invert-absx = %d, inveret-absy = %d\n", + __func__, joypad->invert_absx, joypad->invert_absy); + + joypad->bt_gpio_count = device_get_child_node_count(dev); + + if ((joypad->bt_adc_count == 0) || (joypad->bt_gpio_count == 0)) { + dev_err(dev, "adc key = %d, gpio key = %d error!", + joypad->bt_adc_count, joypad->bt_gpio_count); + return -EINVAL; + } + + error = joypad_adc_setup(dev, joypad); + if (error) + return error; + + error = joypad_gpio_setup(dev, joypad); + if (error) + return error; + + return error; +} + +/*----------------------------------------------------------------------------*/ +static int joypad_probe(struct platform_device *pdev) +{ + struct joypad *joypad; + struct device *dev = &pdev->dev; + int error; + + joypad = devm_kzalloc(dev, sizeof(struct joypad), GFP_KERNEL); + if (!joypad) { + dev_err(dev, "joypad devm_kzmalloc error!"); + return -ENOMEM; + } + + /* device tree data parse */ + error = joypad_dt_parse(dev, joypad); + if (error) { + dev_err(dev, "dt parse error!(err = %d)\n", error); + return error; + } + + mutex_init(&joypad->lock); + platform_set_drvdata(pdev, joypad); + + error = sysfs_create_group(&pdev->dev.kobj, &joypad_attr_group); + if (error) { + dev_err(dev, "create sysfs group fail, error: %d\n", + error); + return error; + } + + /* poll input device setup */ + error = joypad_input_setup(dev, joypad); + if (error) { + dev_err(dev, "input setup failed!(err = %d)\n", error); + return error; + } + dev_info(dev, "%s : probe success\n", __func__); + return 0; +} + +/*----------------------------------------------------------------------------*/ +static const struct of_device_id joypad_of_match[] = { + { .compatible = "odroidgo2-v11-joypad", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, joypad_of_match); + +/*----------------------------------------------------------------------------*/ +static struct platform_driver joypad_driver = { + .probe = joypad_probe, + .driver = { + .name = DRV_NAME, + .of_match_table = of_match_ptr(joypad_of_match), + }, +}; + +/*----------------------------------------------------------------------------*/ +static int __init joypad_init(void) +{ + return platform_driver_register(&joypad_driver); +} + +/*----------------------------------------------------------------------------*/ +static void __exit joypad_exit(void) +{ + platform_driver_unregister(&joypad_driver); +} + +/*----------------------------------------------------------------------------*/ +late_initcall(joypad_init); +module_exit(joypad_exit); + +/*----------------------------------------------------------------------------*/ +MODULE_AUTHOR("Hardkernel Co.,LTD"); +MODULE_DESCRIPTION("Keypad driver(ADC&GPIO) for ODROIDGO-Advance"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); + +/*----------------------------------------------------------------------------*/ diff -rupN linux.orig/drivers/input/joystick/odroidgo3-joypad.c linux/drivers/input/joystick/odroidgo3-joypad.c --- linux.orig/drivers/input/joystick/odroidgo3-joypad.c 1970-01-01 00:00:00.000000000 +0000 +++ linux/drivers/input/joystick/odroidgo3-joypad.c 2024-01-10 20:16:09.688768208 +0000 @@ -0,0 +1,1086 @@ +/* + * SARADC joystick & GPIO Button driver for Linux(Hardkernel ODROIDGO2-Advance) + */ +/* + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +/*----------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*----------------------------------------------------------------------------*/ +#define DRV_NAME "odroidgo3_joypad" + +/*----------------------------------------------------------------------------*/ +#define ADC_MAX_VOLTAGE 1800 +#define ADC_DATA_TUNING(x, p) ((x * p) / 100) +#define ADC_TUNING_DEFAULT 180 + +/*----------------------------------------------------------------------------*/ +/* + +--------------------------------+ + | IIO Channel : ADC_IN1 | + +--------------+-----------------+-----------------+--------+---------+ + | EN(GPIO3.B5) | SEL_A(GPIO3.B3) | SEL_B(GPIO3.B0) | SELECT | EVENT | + +--------------+-----------------+-----------------+--------+---------+ + | 0 | 0 | 0 | R-Y | ABS_RY | + +--------------+-----------------+-----------------+--------+---------+ + | 0 | 0 | 1 | R-X | ABS_RX | + +--------------+-----------------+-----------------+--------+---------+ + | 0 | 1 | 0 | L-Y | ABS_Y | + +--------------+-----------------+-----------------+--------+---------+ + | 0 | 1 | 1 | L-X | ABS_X | + +--------------+-----------------+-----------------+--------+---------+ + | 1 | X | X | XXXX | + +--------------+-----------------+-----------------+--------+ +*/ +/*----------------------------------------------------------------------------*/ +struct bt_adc { + /* report value (mV) */ + int value; + /* report type */ + int report_type; + /* input device init value (mV) */ + int max, min; + /* calibrated adc value */ + int cal; + /* adc scale value */ + int scale; + /* invert report */ + bool invert; + /* amux channel */ + int amux_ch; + /* adc data tuning value([percent), p = positive, n = negative */ + int tuning_p, tuning_n; +}; + +struct analog_mux { + /* IIO ADC Channel : amux connect channel */ + struct iio_channel *iio_ch; + /* analog mux select(a,b) gpio */ + int sel_a_gpio, sel_b_gpio; + /* analog mux enable gpio */ + int en_gpio; +}; + +struct bt_gpio { + /* GPIO Request label */ + const char *label; + /* GPIO Number */ + int num; + /* report type */ + int report_type; + /* report linux code */ + int linux_code; + /* prev button value */ + bool old_value; + /* button press level */ + bool active_level; +}; + +struct joypad { + struct device *dev; + int poll_interval; + + /* report enable/disable */ + bool enable; + + /* analog mux & joystick control */ + struct analog_mux *amux; + /* analog mux max count */ + int amux_count; + /* analog button */ + struct bt_adc *adcs; + + /* report interval (ms) */ + int bt_gpio_count; + struct bt_gpio *gpios; + + /* button auto repeat */ + int auto_repeat; + + /* report threshold (mV) */ + int bt_adc_fuzz, bt_adc_flat; + /* adc read value scale */ + int bt_adc_scale; + /* joystick deadzone control */ + int bt_adc_deadzone; + + struct mutex lock; + + /* amux debug channel */ + int debug_ch; +}; + +/*----------------------------------------------------------------------------*/ +// +// set to the value in the boot.ini file. (if exist) +// +/*----------------------------------------------------------------------------*/ +static unsigned int g_button_adc_fuzz = 0; +static unsigned int g_button_adc_flat = 0; +static unsigned int g_button_adc_scale = 0; +static unsigned int g_button_adc_deadzone = 0; + +static int button_adc_fuzz(char *str) +{ + if (!str) + return -EINVAL; + g_button_adc_fuzz = simple_strtoul(str, NULL, 10); + return 0; +} +__setup("button-adc-fuzz=", button_adc_fuzz); + +static int button_adc_flat(char *str) +{ + if (!str) + return -EINVAL; + g_button_adc_flat = simple_strtoul(str, NULL, 10); + return 0; +} +__setup("button-adc-flat=", button_adc_flat); + +static int button_adc_scale(char *str) +{ + if (!str) + return -EINVAL; + g_button_adc_scale = simple_strtoul(str, NULL, 10); + return 0; +} +__setup("button-adc-scale=", button_adc_scale); + +static int button_adc_deadzone(char *str) +{ + if (!str) + return -EINVAL; + g_button_adc_deadzone = simple_strtoul(str, NULL, 10); + return 0; +} +__setup("button-adc-deadzone=", button_adc_deadzone); + +/*----------------------------------------------------------------------------*/ +/*----------------------------------------------------------------------------*/ +static int joypad_amux_select(struct analog_mux *amux, int channel) +{ + /* select mux channel */ + gpio_set_value(amux->en_gpio, 0); + + switch(channel) { + case 0: /* EVENT (ABS_RY) */ + gpio_set_value(amux->sel_a_gpio, 0); + gpio_set_value(amux->sel_b_gpio, 0); + break; + case 1: /* EVENT (ABS_RX) */ + gpio_set_value(amux->sel_a_gpio, 0); + gpio_set_value(amux->sel_b_gpio, 1); + break; + case 2: /* EVENT (ABS_Y) */ + gpio_set_value(amux->sel_a_gpio, 1); + gpio_set_value(amux->sel_b_gpio, 0); + break; + case 3: /* EVENT (ABS_X) */ + gpio_set_value(amux->sel_a_gpio, 1); + gpio_set_value(amux->sel_b_gpio, 1); + break; + default: + /* amux disanle */ + gpio_set_value(amux->en_gpio, 1); + return -1; + } + /* mux swtiching speed : 35ns(on) / 9ns(off) */ + usleep_range(1, 2); + return 0; +} + +/*----------------------------------------------------------------------------*/ +static int joypad_adc_read(struct analog_mux *amux, struct bt_adc *adc) +{ + int value; + + if (joypad_amux_select(amux, adc->amux_ch)) + return 0; + + if (iio_read_channel_processed(amux->iio_ch, &value)) + return 0; + + value *= adc->scale; + + return (adc->invert ? (adc->max - value) : value); +} + +/*----------------------------------------------------------------------------*/ +/*----------------------------------------------------------------------------*/ +/* + * ATTRIBUTES: + * + * /sys/devices/platform/odroidgo2_joypad/poll_interval [rw] + */ +/*----------------------------------------------------------------------------*/ +static ssize_t joypad_store_poll_interval(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct joypad *joypad = platform_get_drvdata(pdev); + + mutex_lock(&joypad->lock); + joypad->poll_interval = simple_strtoul(buf, NULL, 10); + mutex_unlock(&joypad->lock); + + return count; +} + +/*----------------------------------------------------------------------------*/ +static ssize_t joypad_show_poll_interval(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct joypad *joypad = platform_get_drvdata(pdev); + + return sprintf(buf, "%d\n", joypad->poll_interval); +} + +/*----------------------------------------------------------------------------*/ +static DEVICE_ATTR(poll_interval, S_IWUSR | S_IRUGO, + joypad_show_poll_interval, + joypad_store_poll_interval); + +/*----------------------------------------------------------------------------*/ +/* + * ATTRIBUTES: + * + * /sys/devices/platform/odroidgo2_joypad/adc_fuzz [r] + */ +/*----------------------------------------------------------------------------*/ +static ssize_t joypad_show_adc_fuzz(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct joypad *joypad = platform_get_drvdata(pdev); + + return sprintf(buf, "%d\n", joypad->bt_adc_fuzz); +} + +/*----------------------------------------------------------------------------*/ +static DEVICE_ATTR(adc_fuzz, S_IWUSR | S_IRUGO, + joypad_show_adc_fuzz, + NULL); + +/*----------------------------------------------------------------------------*/ +/* + * ATTRIBUTES: + * + * /sys/devices/platform/odroidgo2_joypad/adc_flat [r] + */ +/*----------------------------------------------------------------------------*/ +static ssize_t joypad_show_adc_flat(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct joypad *joypad = platform_get_drvdata(pdev); + + return sprintf(buf, "%d\n", joypad->bt_adc_flat); +} + +/*----------------------------------------------------------------------------*/ +static DEVICE_ATTR(adc_flat, S_IWUSR | S_IRUGO, + joypad_show_adc_flat, + NULL); + +/*----------------------------------------------------------------------------*/ +/* + * ATTRIBUTES: + * + * /sys/devices/platform/odroidgo2_joypad/enable [rw] + */ +/*----------------------------------------------------------------------------*/ +static ssize_t joypad_store_enable(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct joypad *joypad = platform_get_drvdata(pdev); + + mutex_lock(&joypad->lock); + joypad->enable = simple_strtoul(buf, NULL, 10); + mutex_unlock(&joypad->lock); + + return count; +} + +/*----------------------------------------------------------------------------*/ +static ssize_t joypad_show_enable(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct joypad *joypad = platform_get_drvdata(pdev); + + return sprintf(buf, "%d\n", joypad->enable); +} + +/*----------------------------------------------------------------------------*/ +static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, + joypad_show_enable, + joypad_store_enable); + +/*----------------------------------------------------------------------------*/ +/* + * ATTRIBUTES: + * + * /sys/devices/platform/odroidgo2_joypad/adc_cal [rw] + */ +/*----------------------------------------------------------------------------*/ +static ssize_t joypad_store_adc_cal(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct joypad *joypad = platform_get_drvdata(pdev); + bool calibration; + + calibration = simple_strtoul(buf, NULL, 10); + + if (calibration) { + int nbtn; + + mutex_lock(&joypad->lock); + for (nbtn = 0; nbtn < joypad->amux_count; nbtn++) { + struct bt_adc *adc = &joypad->adcs[nbtn]; + + adc->value = joypad_adc_read(joypad->amux, adc); + if (!adc->value) { + dev_err(joypad->dev, "%s : saradc channels[%d]!\n", + __func__, nbtn); + continue; + } + adc->cal = adc->value; + } + mutex_unlock(&joypad->lock); + } + return count; +} + +/*----------------------------------------------------------------------------*/ +static ssize_t joypad_show_adc_cal(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct joypad *joypad = platform_get_drvdata(pdev); + int nbtn; + ssize_t pos; + + for (nbtn = 0, pos = 0; nbtn < joypad->amux_count; nbtn++) { + struct bt_adc *adc = &joypad->adcs[nbtn]; + pos += sprintf(&buf[pos], "adc[%d]->cal = %d\n", + nbtn, adc->cal); + } + pos += sprintf(&buf[pos], "adc scale = %d\n", joypad->bt_adc_scale); + return pos; +} + +/*----------------------------------------------------------------------------*/ +static DEVICE_ATTR(adc_cal, S_IWUSR | S_IRUGO, + joypad_show_adc_cal, + joypad_store_adc_cal); + +/*----------------------------------------------------------------------------*/ +/* + * ATTRIBUTES: + * + * /sys/devices/platform/odroidgo2_joypad/amux_debug [rw] + * + * echo [debug channel] > amux_debug + * cat amux_debug : debug channel mux set & adc read + */ +/*----------------------------------------------------------------------------*/ +static ssize_t joypad_store_amux_debug(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct platform_device *pdev = to_platform_device(dev); + struct joypad *joypad = platform_get_drvdata(pdev); + + joypad->debug_ch = simple_strtoul(buf, NULL, 10); + + /* if error than default setting(debug_ch = 0) */ + if (joypad->debug_ch > joypad->amux_count) + joypad->debug_ch = 0; + + return count; +} + +/*----------------------------------------------------------------------------*/ +static ssize_t joypad_show_amux_debug(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct joypad *joypad = platform_get_drvdata(pdev); + struct analog_mux *amux = joypad->amux; + ssize_t pos; + int value; + + mutex_lock(&joypad->lock); + + /* disable poll driver */ + if (joypad->enable) + joypad->enable = false; + + if (joypad_amux_select(amux, joypad->debug_ch)) + goto err_out; + + if (iio_read_channel_processed(amux->iio_ch, &value)) + goto err_out; + + pos = sprintf(buf, "amux ch[%d], adc scale = %d, adc value = %d\n", + joypad->debug_ch, joypad->bt_adc_scale, + value * joypad->bt_adc_scale); + goto out; + +err_out: + pos = sprintf(buf, "error : amux setup & adc read!\n"); +out: + mutex_unlock(&joypad->lock); + return pos; +} + +/*----------------------------------------------------------------------------*/ +static DEVICE_ATTR(amux_debug, S_IWUSR | S_IRUGO, + joypad_show_amux_debug, + joypad_store_amux_debug); + +/*----------------------------------------------------------------------------*/ +/*----------------------------------------------------------------------------*/ +static struct attribute *joypad_attrs[] = { + &dev_attr_poll_interval.attr, + &dev_attr_adc_fuzz.attr, + &dev_attr_adc_flat.attr, + &dev_attr_enable.attr, + &dev_attr_adc_cal.attr, + &dev_attr_amux_debug.attr, + NULL, +}; + +static struct attribute_group joypad_attr_group = { + .attrs = joypad_attrs, +}; + +/*----------------------------------------------------------------------------*/ +/*----------------------------------------------------------------------------*/ +static void joypad_gpio_check(struct input_polled_dev *poll_dev) +{ + struct joypad *joypad = poll_dev->private; + int nbtn, value; + + for (nbtn = 0; nbtn < joypad->bt_gpio_count; nbtn++) { + struct bt_gpio *gpio = &joypad->gpios[nbtn]; + + if (gpio_get_value_cansleep(gpio->num) < 0) { + dev_err(joypad->dev, "failed to get gpio state\n"); + continue; + } + value = gpio_get_value(gpio->num); + if (value != gpio->old_value) { + input_event(poll_dev->input, + gpio->report_type, + gpio->linux_code, + (value == gpio->active_level) ? 1 : 0); + gpio->old_value = value; + } + } + input_sync(poll_dev->input); +} + +/*----------------------------------------------------------------------------*/ +static void joypad_adc_check(struct input_polled_dev *poll_dev) +{ + struct joypad *joypad = poll_dev->private; + int nbtn; + + for (nbtn = 0; nbtn < joypad->amux_count; nbtn++) { + struct bt_adc *adc = &joypad->adcs[nbtn]; + + adc->value = joypad_adc_read(joypad->amux, adc); + if (!adc->value) { + dev_err(joypad->dev, "%s : saradc channels[%d]!\n", + __func__, nbtn); + continue; + } + adc->value = adc->value - adc->cal; + + /* Joystick Deadzone check */ + if (joypad->bt_adc_deadzone) { + if (abs(adc->value) < joypad->bt_adc_deadzone) + adc->value = 0; + } + + /* adc data tuning */ + if (adc->tuning_n && adc->value < 0) + adc->value = ADC_DATA_TUNING(adc->value, adc->tuning_n); + if (adc->tuning_p && adc->value > 0) + adc->value = ADC_DATA_TUNING(adc->value, adc->tuning_p); + + adc->value = adc->value > adc->max ? adc->max : adc->value; + adc->value = adc->value < adc->min ? adc->min : adc->value; + + input_report_abs(poll_dev->input, + adc->report_type, + adc->invert ? adc->value * (-1) : adc->value); + } + input_sync(poll_dev->input); +} + +/*----------------------------------------------------------------------------*/ +static void joypad_poll(struct input_polled_dev *poll_dev) +{ + struct joypad *joypad = poll_dev->private; + + if (joypad->enable) { + joypad_adc_check(poll_dev); + joypad_gpio_check(poll_dev); + } + if (poll_dev->poll_interval != joypad->poll_interval) { + mutex_lock(&joypad->lock); + poll_dev->poll_interval = joypad->poll_interval; + mutex_unlock(&joypad->lock); + } +} + +/*----------------------------------------------------------------------------*/ +static void joypad_open(struct input_polled_dev *poll_dev) +{ + struct joypad *joypad = poll_dev->private; + int nbtn; + + for (nbtn = 0; nbtn < joypad->bt_gpio_count; nbtn++) { + struct bt_gpio *gpio = &joypad->gpios[nbtn]; + gpio->old_value = gpio->active_level ? 0 : 1; + } + for (nbtn = 0; nbtn < joypad->amux_count; nbtn++) { + struct bt_adc *adc = &joypad->adcs[nbtn]; + + adc->value = joypad_adc_read(joypad->amux, adc); + if (!adc->value) { + dev_err(joypad->dev, "%s : saradc channels[%d]!\n", + __func__, nbtn); + continue; + } + adc->cal = adc->value; + dev_info(joypad->dev, "%s : adc[%d] adc->cal = %d\n", + __func__, nbtn, adc->cal); + } + /* buttons status sync */ + joypad_adc_check(poll_dev); + joypad_gpio_check(poll_dev); + + /* button report enable */ + mutex_lock(&joypad->lock); + joypad->enable = true; + mutex_unlock(&joypad->lock); + + dev_info(joypad->dev, "%s : opened\n", __func__); +} + +/*----------------------------------------------------------------------------*/ +static void joypad_close(struct input_polled_dev *poll_dev) +{ + struct joypad *joypad = poll_dev->private; + + /* button report disable */ + mutex_lock(&joypad->lock); + joypad->enable = false; + mutex_unlock(&joypad->lock); + + dev_info(joypad->dev, "%s : closed\n", __func__); +} + +/*----------------------------------------------------------------------------*/ +static int joypad_amux_setup(struct device *dev, struct joypad *joypad) +{ + struct analog_mux *amux; + enum iio_chan_type type; + enum of_gpio_flags flags; + int ret; + + /* analog mux control struct init */ + joypad->amux = devm_kzalloc(dev, sizeof(struct analog_mux), + GFP_KERNEL); + if (!joypad->amux) { + dev_err(dev, "%s amux devm_kzmalloc error!", __func__); + return -ENOMEM; + } + amux = joypad->amux; + amux->iio_ch = devm_iio_channel_get(dev, "amux_adc"); + if (IS_ERR(amux->iio_ch)) { + dev_err(dev, "iio channel get error\n"); + return -EINVAL; + } + if (!amux->iio_ch->indio_dev) + return -ENXIO; + + if (iio_get_channel_type(amux->iio_ch, &type)) + return -EINVAL; + + if (type != IIO_VOLTAGE) { + dev_err(dev, "Incompatible channel type %d\n", type); + return -EINVAL; + } + + amux->sel_a_gpio = of_get_named_gpio_flags(dev->of_node, + "amux-a-gpios", 0, &flags); + if (gpio_is_valid(amux->sel_a_gpio)) { + ret = devm_gpio_request(dev, amux->sel_a_gpio, "amux-sel-a"); + if (ret < 0) { + dev_err(dev, "%s : failed to request amux-sel-a %d\n", + __func__, amux->sel_a_gpio); + goto err_out; + } + ret = gpio_direction_output(amux->sel_a_gpio, 0); + if (ret < 0) + goto err_out; + } + + amux->sel_b_gpio = of_get_named_gpio_flags(dev->of_node, + "amux-b-gpios", 0, &flags); + if (gpio_is_valid(amux->sel_b_gpio)) { + ret = devm_gpio_request(dev, amux->sel_b_gpio, "amux-sel-b"); + if (ret < 0) { + dev_err(dev, "%s : failed to request amux-sel-b %d\n", + __func__, amux->sel_b_gpio); + goto err_out; + } + ret = gpio_direction_output(amux->sel_b_gpio, 0); + if (ret < 0) + goto err_out; + } + + amux->en_gpio = of_get_named_gpio_flags(dev->of_node, + "amux-en-gpios", 0, &flags); + if (gpio_is_valid(amux->en_gpio)) { + ret = devm_gpio_request(dev, amux->en_gpio, "amux-en"); + if (ret < 0) { + dev_err(dev, "%s : failed to request amux-en %d\n", + __func__, amux->en_gpio); + goto err_out; + } + ret = gpio_direction_output(amux->en_gpio, 0); + if (ret < 0) + goto err_out; + } + return 0; +err_out: + return ret; +} + +/*----------------------------------------------------------------------------*/ +static int joypad_adc_setup(struct device *dev, struct joypad *joypad) +{ + int nbtn; + + /* adc button struct init */ + joypad->adcs = devm_kzalloc(dev, joypad->amux_count * + sizeof(struct bt_adc), GFP_KERNEL); + if (!joypad->adcs) { + dev_err(dev, "%s devm_kzmalloc error!", __func__); + return -ENOMEM; + } + + for (nbtn = 0; nbtn < joypad->amux_count; nbtn++) { + struct bt_adc *adc = &joypad->adcs[nbtn]; + + adc->scale = joypad->bt_adc_scale; + + adc->max = (ADC_MAX_VOLTAGE / 2); + adc->min = (ADC_MAX_VOLTAGE / 2) * (-1); + if (adc->scale) { + adc->max *= adc->scale; + adc->min *= adc->scale; + } + adc->amux_ch = nbtn; + adc->invert = false; + + switch (nbtn) { + case 0: + adc->report_type = ABS_RY; + if (device_property_read_u32(dev, + "abs_ry-p-tuning", + &adc->tuning_p)) + adc->tuning_p = ADC_TUNING_DEFAULT; + if (device_property_read_u32(dev, + "abs_ry-n-tuning", + &adc->tuning_n)) + adc->tuning_n = ADC_TUNING_DEFAULT; + break; + case 1: + adc->report_type = ABS_RX; + if (device_property_read_u32(dev, + "abs_rx-p-tuning", + &adc->tuning_p)) + adc->tuning_p = ADC_TUNING_DEFAULT; + if (device_property_read_u32(dev, + "abs_rx-n-tuning", + &adc->tuning_n)) + adc->tuning_n = ADC_TUNING_DEFAULT; + break; + case 2: + adc->report_type = ABS_Y; + if (device_property_read_u32(dev, + "abs_y-p-tuning", + &adc->tuning_p)) + adc->tuning_p = ADC_TUNING_DEFAULT; + if (device_property_read_u32(dev, + "abs_y-n-tuning", + &adc->tuning_n)) + adc->tuning_n = ADC_TUNING_DEFAULT; + break; + case 3: + adc->report_type = ABS_X; + if (device_property_read_u32(dev, + "abs_x-p-tuning", + &adc->tuning_p)) + adc->tuning_p = ADC_TUNING_DEFAULT; + if (device_property_read_u32(dev, + "abs_x-n-tuning", + &adc->tuning_n)) + adc->tuning_n = ADC_TUNING_DEFAULT; + break; + default : + dev_err(dev, "%s amux count(%d) error!", + __func__, nbtn); + return -EINVAL; + } + } + return 0; +} + +/*----------------------------------------------------------------------------*/ +static int joypad_gpio_setup(struct device *dev, struct joypad *joypad) +{ + struct device_node *node, *pp; + int nbtn; + + node = dev->of_node; + if (!node) + return -ENODEV; + + joypad->gpios = devm_kzalloc(dev, joypad->bt_gpio_count * + sizeof(struct bt_gpio), GFP_KERNEL); + + if (!joypad->gpios) { + dev_err(dev, "%s devm_kzmalloc error!", __func__); + return -ENOMEM; + } + + nbtn = 0; + for_each_child_of_node(node, pp) { + enum of_gpio_flags flags; + struct bt_gpio *gpio = &joypad->gpios[nbtn++]; + int error; + + gpio->num = of_get_gpio_flags(pp, 0, &flags); + if (gpio->num < 0) { + error = gpio->num; + dev_err(dev, "Failed to get gpio flags, error: %d\n", + error); + return error; + } + + /* gpio active level(key press level) */ + gpio->active_level = (flags & OF_GPIO_ACTIVE_LOW) ? 0 : 1; + + gpio->label = of_get_property(pp, "label", NULL); + + if (gpio_is_valid(gpio->num)) { + error = devm_gpio_request_one(dev, gpio->num, + GPIOF_IN, gpio->label); + if (error < 0) { + dev_err(dev, + "Failed to request GPIO %d, error %d\n", + gpio->num, error); + return error; + } + } + if (of_property_read_u32(pp, "linux,code", &gpio->linux_code)) { + dev_err(dev, "Button without keycode: 0x%x\n", + gpio->num); + return -EINVAL; + } + if (of_property_read_u32(pp, "linux,input-type", + &gpio->report_type)) + gpio->report_type = EV_KEY; + } + if (nbtn == 0) + return -EINVAL; + + return 0; +} + +/*----------------------------------------------------------------------------*/ +static int joypad_input_setup(struct device *dev, struct joypad *joypad) +{ + struct input_polled_dev *poll_dev; + struct input_dev *input; + int nbtn, error; + + poll_dev = devm_input_allocate_polled_device(dev); + if (!poll_dev) { + dev_err(dev, "no memory for polled device\n"); + return -ENOMEM; + } + + poll_dev->private = joypad; + poll_dev->poll = joypad_poll; + poll_dev->poll_interval = joypad->poll_interval; + poll_dev->open = joypad_open; + poll_dev->close = joypad_close; + + input = poll_dev->input; + + input->name = DRV_NAME; + input->phys = DRV_NAME"/input0"; + + input->id.bustype = BUS_HOST; + input->id.vendor = 0x0001; + input->id.product = 0x0001; + input->id.version = 0x0101; + + /* IIO ADC key setup (0 mv ~ 1800 mv) * adc->scale */ + __set_bit(EV_ABS, input->evbit); + for(nbtn = 0; nbtn < joypad->amux_count; nbtn++) { + struct bt_adc *adc = &joypad->adcs[nbtn]; + input_set_abs_params(input, adc->report_type, + adc->min, adc->max, + joypad->bt_adc_fuzz, + joypad->bt_adc_flat); + dev_info(dev, + "%s : SCALE = %d, ABS min = %d, max = %d," + " fuzz = %d, flat = %d, deadzone = %d\n", + __func__, adc->scale, adc->min, adc->max, + joypad->bt_adc_fuzz, joypad->bt_adc_flat, + joypad->bt_adc_deadzone); + dev_info(dev, + "%s : adc tuning_p = %d, adc_tuning_n = %d\n\n", + __func__, adc->tuning_p, adc->tuning_n); + } + + /* GPIO key setup */ + __set_bit(EV_KEY, input->evbit); + for(nbtn = 0; nbtn < joypad->bt_gpio_count; nbtn++) { + struct bt_gpio *gpio = &joypad->gpios[nbtn]; + input_set_capability(input, gpio->report_type, + gpio->linux_code); + } + + if (joypad->auto_repeat) + __set_bit(EV_REP, input->evbit); + + joypad->dev = dev; + + error = input_register_polled_device(poll_dev); + if (error) { + dev_err(dev, "unable to register polled device, err=%d\n", + error); + return error; + } + return 0; +} + +/*----------------------------------------------------------------------------*/ +static void joypad_setup_value_check(struct device *dev, struct joypad *joypad) +{ + /* + fuzz: specifies fuzz value that is used to filter noise from + the event stream. + */ + if (g_button_adc_fuzz) + joypad->bt_adc_fuzz = g_button_adc_fuzz; + else + device_property_read_u32(dev, "button-adc-fuzz", + &joypad->bt_adc_fuzz); + /* + flat: values that are within this value will be discarded by + joydev interface and reported as 0 instead. + */ + if (g_button_adc_flat) + joypad->bt_adc_flat = g_button_adc_flat; + else + device_property_read_u32(dev, "button-adc-flat", + &joypad->bt_adc_flat); + + /* Joystick report value control */ + if (g_button_adc_scale) + joypad->bt_adc_scale = g_button_adc_scale; + else + device_property_read_u32(dev, "button-adc-scale", + &joypad->bt_adc_scale); + + /* Joystick deadzone value control */ + if (g_button_adc_deadzone) + joypad->bt_adc_deadzone = g_button_adc_deadzone; + else + device_property_read_u32(dev, "button-adc-deadzone", + &joypad->bt_adc_deadzone); + +} + +/*----------------------------------------------------------------------------*/ +static int joypad_dt_parse(struct device *dev, struct joypad *joypad) +{ + int error = 0; + + /* initialize value check from boot.ini */ + joypad_setup_value_check(dev, joypad); + + device_property_read_u32(dev, "amux-count", + &joypad->amux_count); + + device_property_read_u32(dev, "poll-interval", + &joypad->poll_interval); + + joypad->auto_repeat = device_property_present(dev, "autorepeat"); + + joypad->bt_gpio_count = device_get_child_node_count(dev); + + if ((joypad->amux_count == 0) || (joypad->bt_gpio_count == 0)) { + dev_err(dev, "adc key = %d, gpio key = %d error!", + joypad->amux_count, joypad->bt_gpio_count); + return -EINVAL; + } + + error = joypad_adc_setup(dev, joypad); + if (error) + return error; + + error = joypad_amux_setup(dev, joypad); + if (error) + return error; + + error = joypad_gpio_setup(dev, joypad); + if (error) + return error; + + dev_info(dev, "%s : adc key cnt = %d, gpio key cnt = %d\n", + __func__, joypad->amux_count, joypad->bt_gpio_count); + + return error; +} + +/*----------------------------------------------------------------------------*/ +static int joypad_probe(struct platform_device *pdev) +{ + struct joypad *joypad; + struct device *dev = &pdev->dev; + int error; + + joypad = devm_kzalloc(dev, sizeof(struct joypad), GFP_KERNEL); + if (!joypad) { + dev_err(dev, "joypad devm_kzmalloc error!"); + return -ENOMEM; + } + + /* device tree data parse */ + error = joypad_dt_parse(dev, joypad); + if (error) { + dev_err(dev, "dt parse error!(err = %d)\n", error); + return error; + } + + mutex_init(&joypad->lock); + platform_set_drvdata(pdev, joypad); + + error = sysfs_create_group(&pdev->dev.kobj, &joypad_attr_group); + if (error) { + dev_err(dev, "create sysfs group fail, error: %d\n", + error); + return error; + } + + /* poll input device setup */ + error = joypad_input_setup(dev, joypad); + if (error) { + dev_err(dev, "input setup failed!(err = %d)\n", error); + return error; + } + dev_info(dev, "%s : probe success\n", __func__); + return 0; +} + +/*----------------------------------------------------------------------------*/ +static const struct of_device_id joypad_of_match[] = { + { .compatible = "odroidgo3-joypad", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, joypad_of_match); + +/*----------------------------------------------------------------------------*/ +static struct platform_driver joypad_driver = { + .probe = joypad_probe, + .driver = { + .name = DRV_NAME, + .of_match_table = of_match_ptr(joypad_of_match), + }, +}; + +/*----------------------------------------------------------------------------*/ +static int __init joypad_init(void) +{ + return platform_driver_register(&joypad_driver); +} + +/*----------------------------------------------------------------------------*/ +static void __exit joypad_exit(void) +{ + platform_driver_unregister(&joypad_driver); +} + +/*----------------------------------------------------------------------------*/ +late_initcall(joypad_init); +module_exit(joypad_exit); + +/*----------------------------------------------------------------------------*/ +MODULE_AUTHOR("Hardkernel Co.,LTD"); +MODULE_DESCRIPTION("Keypad driver(ADC&GPIO) for ODROIDGO-Advance"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" DRV_NAME); + +/*----------------------------------------------------------------------------*/ diff -rupN linux.orig/drivers/power/supply/rk817_charger.c linux/drivers/power/supply/rk817_charger.c --- linux.orig/drivers/power/supply/rk817_charger.c 2024-01-10 19:42:05.361166519 +0000 +++ linux/drivers/power/supply/rk817_charger.c 2024-01-10 20:16:09.688768208 +0000 @@ -679,7 +679,7 @@ static enum power_supply_usb_type rk817_ }; static const struct power_supply_desc rk817_bat_desc = { - .name = "rk817-battery", + .name = "battery", .type = POWER_SUPPLY_TYPE_BATTERY, .properties = rk817_bat_props, .num_properties = ARRAY_SIZE(rk817_bat_props), diff -rupN linux.orig/include/linux/input-polldev.h linux/include/linux/input-polldev.h --- linux.orig/include/linux/input-polldev.h 1970-01-01 00:00:00.000000000 +0000 +++ linux/include/linux/input-polldev.h 2024-01-10 20:16:09.688768208 +0000 @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _INPUT_POLLDEV_H +#define _INPUT_POLLDEV_H + +/* + * Copyright (c) 2007 Dmitry Torokhov + */ + +#include +#include + +/** + * struct input_polled_dev - simple polled input device + * @private: private driver data. + * @open: driver-supplied method that prepares device for polling + * (enabled the device and maybe flushes device state). + * @close: driver-supplied method that is called when device is no + * longer being polled. Used to put device into low power mode. + * @poll: driver-supplied method that polls the device and posts + * input events (mandatory). + * @poll_interval: specifies how often the poll() method should be called. + * Defaults to 500 msec unless overridden when registering the device. + * @poll_interval_max: specifies upper bound for the poll interval. + * Defaults to the initial value of @poll_interval. + * @poll_interval_min: specifies lower bound for the poll interval. + * Defaults to 0. + * @input: input device structure associated with the polled device. + * Must be properly initialized by the driver (id, name, phys, bits). + * + * Polled input device provides a skeleton for supporting simple input + * devices that do not raise interrupts but have to be periodically + * scanned or polled to detect changes in their state. + */ +struct input_polled_dev { + void *private; + + void (*open)(struct input_polled_dev *dev); + void (*close)(struct input_polled_dev *dev); + void (*poll)(struct input_polled_dev *dev); + unsigned int poll_interval; /* msec */ + unsigned int poll_interval_max; /* msec */ + unsigned int poll_interval_min; /* msec */ + + struct input_dev *input; + +/* private: */ + struct delayed_work work; + + bool devres_managed; +}; + +struct input_polled_dev *input_allocate_polled_device(void); +struct input_polled_dev *devm_input_allocate_polled_device(struct device *dev); +void input_free_polled_device(struct input_polled_dev *dev); +int input_register_polled_device(struct input_polled_dev *dev); +void input_unregister_polled_device(struct input_polled_dev *dev); + +#endif diff -rupN linux.orig/include/linux/of_gpio.h linux/include/linux/of_gpio.h --- linux.orig/include/linux/of_gpio.h 2024-01-10 19:42:05.593171591 +0000 +++ linux/include/linux/of_gpio.h 2024-01-10 20:18:46.559755988 +0000 @@ -17,8 +17,26 @@ struct device_node; +/* + * This is Linux-specific flags. By default controllers' and Linux' mapping + * match, but GPIO controllers are free to translate their own flags to + * Linux-specific in their .xlate callback. Though, 1:1 mapping is recommended. + */ +enum of_gpio_flags { + OF_GPIO_ACTIVE_LOW = 0x1, + OF_GPIO_SINGLE_ENDED = 0x2, + OF_GPIO_OPEN_DRAIN = 0x4, + OF_GPIO_TRANSITORY = 0x8, + OF_GPIO_PULL_UP = 0x10, + OF_GPIO_PULL_DOWN = 0x20, + OF_GPIO_PULL_DISABLE = 0x40, +}; + #ifdef CONFIG_OF_GPIO +extern int of_get_named_gpio_flags(const struct device_node *np, + const char *list_name, int index, enum of_gpio_flags *flags); + extern int of_get_named_gpio(const struct device_node *np, const char *list_name, int index); @@ -26,13 +44,30 @@ extern int of_get_named_gpio(const struc #include -/* Drivers may not strictly depend on the GPIO support, so let them link. */ static inline int of_get_named_gpio(const struct device_node *np, const char *propname, int index) { + return -ENOSYS; +} + +/* Drivers may not strictly depend on the GPIO support, so let them link. */ +static inline int of_get_named_gpio_flags(const struct device_node *np, + const char *list_name, int index, enum of_gpio_flags *flags) +{ + if (flags) + *flags = 0; + return -ENOSYS; } #endif /* CONFIG_OF_GPIO */ +static inline int of_get_gpio_flags(const struct device_node *np, int index, + enum of_gpio_flags *flags) +{ + return of_get_named_gpio_flags(np, "gpios", index, flags); +} + +//#endif /* CONFIG_OF_GPIO */ + #endif /* __LINUX_OF_GPIO_H */