diff -rupN linux.orig/drivers/input/Kconfig linux/drivers/input/Kconfig --- linux.orig/drivers/input/Kconfig 2023-12-15 19:18:57.320911751 +0000 +++ linux/drivers/input/Kconfig 2023-12-15 19:20:05.987107578 +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 2023-12-15 19:18:57.320911751 +0000 +++ linux/drivers/input/Makefile 2023-12-15 19:20:05.987107578 +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 2023-12-15 19:20:05.987107578 +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 2023-12-15 19:18:57.324911879 +0000 +++ linux/drivers/input/joystick/Kconfig 2023-12-15 19:20:05.987107578 +0000 @@ -393,6 +393,12 @@ config JOYSTICK_FSIA6B To compile this driver as a module, choose M here: the module will be called fsia6b. +config JOYSTICK_SINGLEADCJOY + tristate "Single SARADC dual-joystick gamepad driver" + select INPUT_POLLDEV + help + The 1-in 4-out SARADC joypad driver. + config JOYSTICK_N64 bool "N64 controller" depends on MACH_NINTENDO64 diff -rupN linux.orig/drivers/input/joystick/Makefile linux/drivers/input/joystick/Makefile --- linux.orig/drivers/input/joystick/Makefile 2023-12-15 19:18:57.324911879 +0000 +++ linux/drivers/input/joystick/Makefile 2023-12-15 19:20:05.987107578 +0000 @@ -30,6 +30,7 @@ obj-$(CONFIG_JOYSTICK_PXRC) += pxrc.o obj-$(CONFIG_JOYSTICK_QWIIC) += qwiic-joystick.o obj-$(CONFIG_JOYSTICK_SENSEHAT) += sensehat-joystick.o obj-$(CONFIG_JOYSTICK_SIDEWINDER) += sidewinder.o +obj-$(CONFIG_JOYSTICK_SINGLEADCJOY) += singleadcjoy.o obj-$(CONFIG_JOYSTICK_SPACEBALL) += spaceball.o obj-$(CONFIG_JOYSTICK_SPACEORB) += spaceorb.o obj-$(CONFIG_JOYSTICK_STINGER) += stinger.o diff -rupN linux.orig/drivers/input/joystick/singleadcjoy.c linux/drivers/input/joystick/singleadcjoy.c --- linux.orig/drivers/input/joystick/singleadcjoy.c 1970-01-01 00:00:00.000000000 +0000 +++ linux/drivers/input/joystick/singleadcjoy.c 2023-12-15 19:36:26.739762439 +0000 @@ -0,0 +1,1416 @@ +/*----------------------------------------------------------------------------*/ + +/* + * Copyright (c) 2008-2021 Anbernic + */ + +/* + * Single SARADC dual-joystick gamepad driver + */ + +/*----------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*----------------------------------------------------------------------------*/ +#define DRV_NAME "retrogame_joypad" + +/*----------------------------------------------------------------------------*/ +#define ADC_MAX_VOLTAGE 1800 +#define ADC_DATA_TUNING(x, p) ((x * p) / 100) +#define ADC_TUNING_DEFAULT 180 +#define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x))) + +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 reference point */ + bool invert_absx; + bool invert_absy; + bool invert_absrx; + bool invert_absry; + + /* 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; + + /* pwm device for rumble*/ + struct input_dev *input; + struct pwm_device *pwm; + struct work_struct play_work; + u16 level; + u16 boost_weak; + u16 boost_strong; +}; + +static int pwm_vibrator_start(struct joypad *joypad) +{ + struct device *pdev = joypad->input->dev.parent; + struct pwm_state state; + int err; + + pwm_get_state(joypad->pwm, &state); + pwm_set_relative_duty_cycle(&state, joypad->level, 0xffff); + state.enabled = true; + + err = pwm_apply_state(joypad->pwm, &state); + if (err) { + dev_err(pdev, "failed to apply pwm state: %d", err); + return err; + } + + return 0; +} + +static void pwm_vibrator_stop(struct joypad *joypad) +{ + pwm_disable(joypad->pwm); +} + +static void pwm_vibrator_play_work(struct work_struct *work) +{ + struct joypad *joypad = container_of(work, + struct joypad, play_work); + + if (joypad->level) + pwm_vibrator_start(joypad); + else + pwm_vibrator_stop(joypad); +} + +/*----------------------------------------------------------------------------*/ +// +// 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(10, 20); + 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; + + iio_read_channel_raw(amux->iio_ch, &value); + + value *= adc->scale; + + return value; +} + +/*----------------------------------------------------------------------------*/ +/*----------------------------------------------------------------------------*/ +/* + * ATTRIBUTES: + * + * /sys/devices/platform/retrogame_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/retrogame_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/retrogame_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/retrogame_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/retrogame_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/retrogame_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); + +/*----------------------------------------------------------------------------*/ +/*----------------------------------------------------------------------------*/ +/* + * ATTRIBUTES: + * + * /sys/devices/platform/odroidgo2_joypad/rumble_period [rw] + */ +/*----------------------------------------------------------------------------*/ +static ssize_t joypad_store_period(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); + pwm_set_period(joypad->pwm, simple_strtoul(buf, NULL, 21)); + mutex_unlock(&joypad->lock); + + return count; +} + +/*----------------------------------------------------------------------------*/ +static ssize_t joypad_show_period(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", pwm_get_period(joypad->pwm)); +} + +/*----------------------------------------------------------------------------*/ +static DEVICE_ATTR(rumble_period, S_IWUSR | S_IRUGO, + joypad_show_period, + joypad_store_period); + + +/*----------------------------------------------------------------------------*/ +/*----------------------------------------------------------------------------*/ +/* + * ATTRIBUTES: + * + * /sys/devices/platform/odroidgo2_joypad/rumble_boost_strong [rw] + */ +/*----------------------------------------------------------------------------*/ +static ssize_t joypad_store_boost_strong(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->boost_strong = simple_strtoul(buf, NULL, 10); + mutex_unlock(&joypad->lock); + + return count; +} + +/*----------------------------------------------------------------------------*/ +static ssize_t joypad_show_boost_strong(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->boost_strong); +} + +/*----------------------------------------------------------------------------*/ +static DEVICE_ATTR(rumble_boost_strong, S_IWUSR | S_IRUGO, + joypad_show_boost_strong, + joypad_store_boost_strong); + +/*----------------------------------------------------------------------------*/ +/* + * ATTRIBUTES: + * + * /sys/devices/platform/odroidgo2_joypad/rumble_boost_weak [rw] + */ +/*----------------------------------------------------------------------------*/ +static ssize_t joypad_store_boost_weak(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->boost_weak = simple_strtoul(buf, NULL, 10); + mutex_unlock(&joypad->lock); + + return count; +} + +/*----------------------------------------------------------------------------*/ +static ssize_t joypad_show_boost_weak(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->boost_weak); +} + +/*----------------------------------------------------------------------------*/ +static DEVICE_ATTR(rumble_boost_weak, S_IWUSR | S_IRUGO, + joypad_show_boost_weak, + joypad_store_boost_weak); + +#define __HDMI__ + +#ifdef __HDMI__ +/*----------------------------------------------------------------------------*/ +/* + * ATTRIBUTES: + * + * /sys/devices/platform/singleadc-joypad/hdst [rw] + */ +/*----------------------------------------------------------------------------*/ +static ssize_t joypad_store_hdst(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + return count; +} + +/*----------------------------------------------------------------------------*/ +static ssize_t joypad_show_hdst(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 stcon; + ssize_t pos=0; + if(gpio_get_value(54)==1) + { + pos += sprintf(&buf[pos], "disconnected\n"); + }else{ + pos += sprintf(&buf[pos], "connected\n"); + } + return pos; +} + +/*----------------------------------------------------------------------------*/ +static DEVICE_ATTR(hdst, S_IWUSR | S_IRUGO, + joypad_show_hdst, + joypad_store_hdst); +#endif + +#ifdef __HDMI__ +/*----------------------------------------------------------------------------*/ +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, + &dev_attr_hdst.attr, + &dev_attr_rumble_period.attr, + &dev_attr_rumble_boost_strong.attr, + &dev_attr_rumble_boost_weak.attr, + NULL, +}; +#else +/*----------------------------------------------------------------------------*/ +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, + &dev_attr_rumble_period.attr, + &dev_attr_rumble_boost_strong.attr, + &dev_attr_rumble_boost_weak.attr, + NULL, +}; +#endif +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]! adc->value : %d\n",__func__, nbtn, adc->value); + 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); + + cancel_work_sync(&joypad->play_work); + pwm_vibrator_stop(joypad); + + 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: + if (joypad->invert_absry) + adc->invert = true; + 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: + if (joypad->invert_absrx) + adc->invert = true; + 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: + if (joypad->invert_absy) + adc->invert = true; + 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: + if (joypad->invert_absx) + adc->invert = true; + 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; +} + +/*----------------------------------------------------------------------------*/ +struct input_dev * joypad_input_g; + + +void rk_send_key_f_key_up(void) +{ + if (!joypad_input_g) + return; + + input_report_key(joypad_input_g, BTN_MODE, 1); + input_sync(joypad_input_g); +} +EXPORT_SYMBOL(rk_send_key_f_key_up); + + +void rk_send_key_f_key_down(void) +{ + if (!joypad_input_g) + return; + + input_report_key(joypad_input_g, BTN_MODE, 0); + input_sync(joypad_input_g); +} +EXPORT_SYMBOL(rk_send_key_f_key_down); + +static int rumble_play_effect(struct input_dev *dev, void *data, struct ff_effect *effect) +{ + struct joypad *joypad = data; + u32 boosted_level; + if (effect->type != FF_RUMBLE) + return 0; + + if (effect->u.rumble.strong_magnitude) + boosted_level = effect->u.rumble.strong_magnitude + joypad->boost_strong; + else + boosted_level = effect->u.rumble.weak_magnitude + joypad->boost_weak; + + joypad->level = (u16)CLAMP(boosted_level, 0, 0xffff); + + dev_info(joypad->dev,"joypad->level = %d", joypad->level); + schedule_work(&joypad->play_work); + return 0; +} +/*----------------------------------------------------------------------------*/ +static int joypad_rumble_setup(struct device *dev, struct joypad *joypad) +{ + int err; + struct pwm_state state; + + joypad->pwm = devm_pwm_get(dev, "enable"); + if (IS_ERR(joypad->pwm)) + { + dev_err(dev, "rumble get error\n"); + return -EINVAL; + } + + INIT_WORK(&joypad->play_work, pwm_vibrator_play_work); + + /* Sync up PWM state and ensure it is off. */ + pwm_init_state(joypad->pwm, &state); + state.enabled = false; + err = pwm_apply_state(joypad->pwm, &state); + if (err) { + dev_err(dev, "failed to apply initial PWM state: %d", + err); + return err; + } + dev_info(dev, "rumble setup success!\n"); + 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; + u32 joypad_revision = 0; + u32 joypad_product = 0; + u32 boost_weak = 0; + u32 boost_strong = 0; + 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; + joypad->input = poll_dev->input; + + device_property_read_string(dev, "joypad-name", &input->name); + input->phys = DRV_NAME"/input0"; + + device_property_read_u32(dev, "joypad-revision", &joypad_revision); + device_property_read_u32(dev, "joypad-product", &joypad_product); + input->id.bustype = BUS_HOST; + input->id.vendor = 0x484B; + input->id.product = (u16)joypad_product; + input->id.version = (u16)joypad_revision; + + /* 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); + } + + /* Rumble setip*/ + device_property_read_u32(dev, "rumble-boost-weak", &boost_weak); + device_property_read_u32(dev, "rumble-boost-strong", &boost_strong); + joypad->boost_weak = boost_weak; + joypad->boost_strong = boost_strong; + dev_info(dev, "Boost = %d, %d",boost_weak, boost_strong); + input_set_capability(input, EV_FF, FF_RUMBLE); + error = input_ff_create_memless(input, joypad, rumble_play_effect); + if (error) { + dev_err(dev, "unable to register rumble, err=%d\n", + error); + return error; + } + + + /* 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"); + + /* 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"); + joypad->invert_absrx = device_property_present(dev, "invert-absrx"); + joypad->invert_absry = device_property_present(dev, "invert-absry"); + dev_info(dev, "%s : invert-absx = %d, inveret-absy = %d, invert-absrx = %d, inveret-absry = %d\n", + __func__, joypad->invert_absx, joypad->invert_absy, joypad->invert_absrx, joypad->invert_absry); + + 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 __maybe_unused joypad_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct joypad *joypad = platform_get_drvdata(pdev); + + cancel_work_sync(&joypad->play_work); + if (joypad->level) + pwm_vibrator_stop(joypad); + + return 0; +} + +static int __maybe_unused joypad_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct joypad *joypad = platform_get_drvdata(pdev); + + if (joypad->level) + pwm_vibrator_start(joypad); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(joypad_pm_ops, + joypad_suspend, joypad_resume); +/*----------------------------------------------------------------------------*/ +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; + } + + /* rumble setup */ + error = joypad_rumble_setup(dev, joypad); + if (error) { + dev_err(dev, "rumble 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 = "singleadc-joypad", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, joypad_of_match); + +/*----------------------------------------------------------------------------*/ +static struct platform_driver joypad_driver = { + .probe = joypad_probe, + .driver = { + .name = DRV_NAME, + .pm = &joypad_pm_ops, + .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); 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 2023-12-15 19:20:05.987107578 +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