diff -rupN linux.orig/drivers/input/joystick/Kconfig linux/drivers/input/joystick/Kconfig --- linux.orig/drivers/input/joystick/Kconfig 2023-09-12 12:02:58.173629790 +0000 +++ linux/drivers/input/joystick/Kconfig 2023-09-12 12:03:27.490291628 +0000 @@ -344,6 +344,12 @@ config JOYSTICK_MAPLE To compile this as a module choose M here: the module will be called maplecontrol. +config JOYSTICK_ODROID_GOU + tristate "ODROID-Go-Ultra joypad driver" + depends on INPUT_POLLDEV + help + Made for ODROID-GO-Ultra. + config JOYSTICK_PSXPAD_SPI tristate "PlayStation 1/2 joypads via SPI interface" depends on SPI diff -rupN linux.orig/drivers/input/joystick/Makefile linux/drivers/input/joystick/Makefile --- linux.orig/drivers/input/joystick/Makefile 2023-09-12 12:02:58.173629790 +0000 +++ linux/drivers/input/joystick/Makefile 2023-09-12 12:03:27.490291628 +0000 @@ -25,6 +25,7 @@ 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_ODROID_GOU) += odroid-gou-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/amlogic-saradc.h linux/drivers/input/joystick/amlogic-saradc.h --- linux.orig/drivers/input/joystick/amlogic-saradc.h 1970-01-01 00:00:00.000000000 +0000 +++ linux/drivers/input/joystick/amlogic-saradc.h 2023-09-12 12:03:27.490291628 +0000 @@ -0,0 +1,32 @@ +/* + * include/dt-bindings/iio/adc/amlogic-saradc.h + * + * Copyright (C) 2017 Amlogic, Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef _DT_BINDINGS_IIO_ADC_AMLOGIC_H +#define _DT_BINDINGS_IIO_ADC_AMLOGIC_H + +#define SARADC_CH0 0 +#define SARADC_CH1 1 +#define SARADC_CH2 2 +#define SARADC_CH3 3 +#define SARADC_CH4 4 +#define SARADC_CH5 5 +#define SARADC_CH6 6 +#define SARADC_CH7 7 + +#define SARADC_CH_NUM 8 + +#endif diff -rupN linux.orig/drivers/input/joystick/odroid-gou-joypad.c linux/drivers/input/joystick/odroid-gou-joypad.c --- linux.orig/drivers/input/joystick/odroid-gou-joypad.c 1970-01-01 00:00:00.000000000 +0000 +++ linux/drivers/input/joystick/odroid-gou-joypad.c 2023-09-12 12:03:27.490291628 +0000 @@ -0,0 +1,960 @@ +/*----------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "amlogic-saradc.h" +#include + +/*----------------------------------------------------------------------------*/ +#define DRV_NAME "odroidgo_joypad" + +/*----------------------------------------------------------------------------*/ + + +/*----------------------------------------------------------------------------*/ +#define ADC_MAX_VOLTAGE 1800 +#define ADC_DATA_TUNING(x, p) ((x * p) / 100) +#define ADC_TUNING_DEFAULT 180 + +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; + /* adc channel */ + int channel; + /* adc data tuning value([percent), p = positive, n = negative */ + int tuning_p, tuning_n; +}; + +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; + struct input_polled_dev *poll_dev; + int poll_interval; + + /* report enable/disable */ + bool enable; + + /* analog mux & joystick control */ + struct iio_channel *adc_ch[SARADC_CH_NUM]; + + /* adc input channel count */ + int chan_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; + + /* adc 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_adc_read(struct joypad *joypad, struct bt_adc *adc) +{ + int value; + + if (iio_read_channel_processed(joypad->adc_ch[adc->channel], &value) < 0) + return 0; + + value *= adc->scale; + + return (adc->invert ? (adc->max - value) : value); +} + +/*----------------------------------------------------------------------------*/ +/*----------------------------------------------------------------------------*/ +/* + * ATTRIBUTES: + * + * /sys/devices/platform/odroidgo_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/odroidgo_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/odroidgo_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/oodroidgo_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/odroidgo_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->chan_count; nbtn++) { + struct bt_adc *adc = &joypad->adcs[nbtn]; + + adc->value = joypad_adc_read(joypad, 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->chan_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); + +/*----------------------------------------------------------------------------*/ +/*----------------------------------------------------------------------------*/ +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; + int mag; + + /* Assumes an even number of axes and that joystick axis pairs are sequential */ + /* e.g. left stick Y immediately follows left stick X */ + for (nbtn = 0; nbtn < joypad->chan_count; nbtn += 2) { + struct bt_adc *adcx = &joypad->adcs[nbtn]; + struct bt_adc *adcy = &joypad->adcs[nbtn + 1]; + + /* Read first joystick axis */ + adcx->value = joypad_adc_read(joypad, adcx); + if (!adcx->value) { + dev_err(joypad->dev, "%s : saradc channels[%d]!\n", + __func__, nbtn); + continue; + } + adcx->value = adcx->value - adcx->cal; + + /* Read second joystick axis */ + adcy->value = joypad_adc_read(joypad, adcy); + if (!adcy->value) { + dev_err(joypad->dev, "%s : saradc channels[%d]!\n", + __func__, nbtn + 1); + continue; + } + adcy->value = adcy->value - adcy->cal; + + /* Scaled Radial Deadzone */ + /* https://web.archive.org/web/20190129113357/http://www.third-helix.com/2013/04/12/doing-thumbstick-dead-zones-right.html */ + mag = int_sqrt((adcx->value * adcx->value) + (adcy->value * adcy->value)); + if (joypad->bt_adc_deadzone) { + if (mag <= joypad->bt_adc_deadzone) { + adcx->value = 0; + adcy->value = 0; + } + else { + /* Assumes adcx->max == -adcx->min == adcy->max == -adcy->min */ + /* Order of operations is critical to avoid integer overflow */ + adcx->value = (((adcx->max * adcx->value) / mag) * (mag - joypad->bt_adc_deadzone)) / (adcx->max - joypad->bt_adc_deadzone); + adcy->value = (((adcy->max * adcy->value) / mag) * (mag - joypad->bt_adc_deadzone)) / (adcy->max - joypad->bt_adc_deadzone); + } + } + + /* adc data tuning */ + if (adcx->tuning_n && adcx->value < 0) + adcx->value = ADC_DATA_TUNING(adcx->value, adcx->tuning_n); + if (adcx->tuning_p && adcx->value > 0) + adcx->value = ADC_DATA_TUNING(adcx->value, adcx->tuning_p); + if (adcy->tuning_n && adcy->value < 0) + adcy->value = ADC_DATA_TUNING(adcy->value, adcy->tuning_n); + if (adcy->tuning_p && adcy->value > 0) + adcy->value = ADC_DATA_TUNING(adcy->value, adcy->tuning_p); + + /* Clamp to [min, max] */ + adcx->value = adcx->value > adcx->max ? adcx->max : adcx->value; + adcx->value = adcx->value < adcx->min ? adcx->min : adcx->value; + adcy->value = adcy->value > adcy->max ? adcy->max : adcy->value; + adcy->value = adcy->value < adcy->min ? adcy->min : adcy->value; + + input_report_abs(poll_dev->input, + adcx->report_type, + adcx->invert ? adcx->value * (-1) : adcx->value); + input_report_abs(poll_dev->input, + adcy->report_type, + adcy->invert ? adcy->value * (-1) : adcy->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->chan_count; nbtn++) { + struct bt_adc *adc = &joypad->adcs[nbtn]; + + adc->value = joypad_adc_read(joypad, 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_iochannel_setup(struct device *dev, struct joypad *joypad) +{ + enum iio_chan_type type; + unsigned char cnt; + const char *uname; + int ret; + + for (cnt = 0; cnt < joypad->chan_count; cnt++) { + + ret = of_property_read_string_index(dev->of_node, + "io-channel-names", cnt, &uname); + if (ret < 0) { + dev_err(dev, "invalid channel name index[%d]\n", cnt); + return -EINVAL; + } + + joypad->adc_ch[cnt] = devm_iio_channel_get(dev, + uname); + if (IS_ERR(joypad->adc_ch[cnt])) { + dev_err(dev, "iio channel get error\n"); + return -EINVAL; + } + if (!joypad->adc_ch[cnt]->indio_dev) + return -ENXIO; + + if (iio_get_channel_type(joypad->adc_ch[cnt], &type)) + return -EINVAL; + + if (type != IIO_VOLTAGE) { + dev_err(dev, "Incompatible channel type %d\n", type); + return -EINVAL; + } + } + 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->chan_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->chan_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->channel = 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 io channel 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 0 + error = gpiod_set_pull(gpio_to_desc(gpio->num), GPIOD_PULL_UP); + if (error < 0) { + dev_err(dev, + "Failed to set pull-up GPIO %d, error %d\n", + gpio->num, error); + return error; + } + #endif + } + 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; + u32 joypad_vendor = 0; + u32 joypad_revision = 0; + u32 joypad_product = 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; + + input->name = DRV_NAME; + + device_property_read_string(dev, "joypad-name", &input->name); + input->phys = DRV_NAME"/input0"; + + device_property_read_u32(dev, "joypad-vendor", &joypad_vendor); + 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.bustype = BUS_USB; + input->id.vendor = (u16)joypad_vendor; + 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); + + // Set mapped ones on dt + for(nbtn = 0; nbtn < joypad->chan_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; + } + joypad->dev = dev; + joypad->poll_dev = poll_dev; + + 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); + + joypad->chan_count = of_property_count_strings(dev->of_node, + "io-channel-names"); + + 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->chan_count == 0) || (joypad->bt_gpio_count == 0)) { + dev_err(dev, "adc key = %d, gpio key = %d error!", + joypad->chan_count, joypad->bt_gpio_count); + return -EINVAL; + } + + error = joypad_adc_setup(dev, joypad); + if (error) + return error; + + error = joypad_iochannel_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->chan_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 void joypad_shutdown(struct platform_device *pdev) +{ + struct joypad *joypad = platform_get_drvdata(pdev); + input_unregister_polled_device(joypad->poll_dev); +} +/*----------------------------------------------------------------------------*/ +static const struct of_device_id joypad_of_match[] = { + { .compatible = "odroidgou-joypad", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, joypad_of_match); + +/*----------------------------------------------------------------------------*/ +static struct platform_driver joypad_driver = { + .probe = joypad_probe, + .shutdown = joypad_shutdown, + .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); + +/*----------------------------------------------------------------------------*/