Files
Arch-R/patches/linux/device/004-input-drivers.patch
Douglas Teles 8650d940e2 Migrate to mainline kernel 6.12.61 + board/overlay architecture
Major architecture change: move from BSP kernel 6.6 with pre-merged panel
DTBs to mainline kernel 6.12.61 LTS with separated board DTBs + panel
overlays.

- Board DTB = hardware profile (GPIOs, PMIC, joypad, audio). 16 boards.
  Auto-selected by U-Boot via SARADC ADC reading (hwrev).
- Panel overlay = display init sequence. 20 panels. Applied at boot time
  via boot.ini fdt apply.
- Two image variants: original (a_boot.ini) and clone (b_boot.ini)
- Kernel cross-compiles from x86 host (no ARM chroot needed)
- Initramfs boot splash with SVG rendering at 0.7s
- Out-of-tree joypad driver (singleadc-joypad) for clone boards
- Panel generic-dsi driver with archr,generic-dsi compatible

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 17:20:09 -03:00

6830 lines
193 KiB
Diff

diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig
index 7755e5b454d2..38355e3184fc 100644
--- a/drivers/input/joystick/Kconfig
+++ b/drivers/input/joystick/Kconfig
@@ -400,6 +400,36 @@ 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_RGB20S
+ tristate "RGB20S joypad driver"
+ help
+ Made for RGB20S.
+
+config JOYSTICK_XU10
+ tristate "XU10 joypad driver"
+ help
+ Made for XU10.
+
+config JOYSTICK_GAMEFORCE_CHI
+ tristate "gameforce joypad driver"
+ help
+ Made for Gameforce CHI.
+
config JOYSTICK_SENSEHAT
tristate "Raspberry Pi Sense HAT joystick"
depends on INPUT && I2C
diff --git a/drivers/input/joystick/Makefile b/drivers/input/joystick/Makefile
index 9976f596a920..2a0524b63ba0 100644
--- a/drivers/input/joystick/Makefile
+++ b/drivers/input/joystick/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_JOYSTICK_COBRA) += cobra.o
obj-$(CONFIG_JOYSTICK_DB9) += db9.o
obj-$(CONFIG_JOYSTICK_FSIA6B) += fsia6b.o
obj-$(CONFIG_JOYSTICK_GAMECON) += gamecon.o
+obj-$(CONFIG_JOYSTICK_GAMEFORCE_CHI) += gameforce-joypad.o
obj-$(CONFIG_JOYSTICK_GF2K) += gf2k.o
obj-$(CONFIG_JOYSTICK_GRIP) += grip.o
obj-$(CONFIG_JOYSTICK_GRIP_MP) += grip_mp.o
@@ -25,6 +26,10 @@ obj-$(CONFIG_JOYSTICK_JOYDUMP) += joydump.o
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_RGB20S) += rgb20s-joypad.o
obj-$(CONFIG_JOYSTICK_PSXPAD_SPI) += psxpad-spi.o
obj-$(CONFIG_JOYSTICK_PXRC) += pxrc.o
obj-$(CONFIG_JOYSTICK_QWIIC) += qwiic-joystick.o
@@ -40,4 +45,5 @@ obj-$(CONFIG_JOYSTICK_TWIDJOY) += twidjoy.o
obj-$(CONFIG_JOYSTICK_WARRIOR) += warrior.o
obj-$(CONFIG_JOYSTICK_WALKERA0701) += walkera0701.o
obj-$(CONFIG_JOYSTICK_XPAD) += xpad.o
+obj-$(CONFIG_JOYSTICK_XU10) += xu10-joypad.o
obj-$(CONFIG_JOYSTICK_ZHENHUA) += zhenhua.o
diff --git a/drivers/input/joystick/gameforce-joypad.c b/drivers/input/joystick/gameforce-joypad.c
new file mode 100644
index 000000000000..e2953433b491
--- /dev/null
+++ b/drivers/input/joystick/gameforce-joypad.c
@@ -0,0 +1,1330 @@
+/*
+ * SARADC joystick & GPIO Button driver for Linux(Gameforce CHI)
+ */
+/*
+ * 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 <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+/*----------------------------------------------------------------------------*/
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/input-polldev.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio_keys.h>
+#include <linux/iio/consumer.h>
+#include <linux/iio/types.h>
+#include <linux/property.h>
+#include <linux/of_gpio_legacy.h>
+
+#define RIGHT_ADC_JOYSTICK
+#define VOLUME_ADC_KEYS
+#define VIBRATORS_FF
+
+#ifdef RIGHT_ADC_JOYSTICK
+#include <linux/miscdevice.h>
+#include "../input-compat.h"
+#endif
+
+/*----------------------------------------------------------------------------*/
+#define DRV_NAME "gameforce_gamepad"
+/*----------------------------------------------------------------------------*/
+#ifdef VOLUME_ADC_KEYS
+struct of_device_id adckeys_of_match[] = {
+ { .compatible = "volume-keys", },
+ {},
+};
+#endif
+
+#ifdef VIBRATORS_FF
+struct of_device_id vibrators_of_match[] = {
+ { .compatible = "gpio,vibrators", },
+ {},
+};
+
+
+struct gpio_vib {
+ struct work_struct work;
+ int speed;
+ int index;
+ int last_index;
+ bool active;
+ int gpio0;
+ int gpio1;
+};
+#endif
+
+#ifdef VOLUME_ADC_KEYS
+struct adc_keys_button {
+ u32 voltage;
+ u32 keycode;
+};
+
+struct adc_keys_state {
+ struct iio_channel *channel;
+ u32 num_keys;
+ u32 last_key;
+ u32 keyup_voltage;
+ const struct adc_keys_button *map;
+};
+#endif
+
+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;
+#ifdef VOLUME_ADC_KEYS
+ /*adc key saradc[2]*/
+ struct adc_keys_state *st;
+#endif
+#ifdef VIBRATORS_FF
+ /* vibrator */
+ struct gpio_vib *vib;
+#endif
+ struct mutex lock;
+};
+
+#ifdef RIGHT_ADC_JOYSTICK
+struct input_dev *global_input = NULL;
+#endif
+/*----------------------------------------------------------------------------*/
+//
+// 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/DRV_NAME/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/DRV_NAME/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/DRV_NAME/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/DRV_NAME/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/DRV_NAME/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);
+ }
+ else
+ {
+ input_report_abs(poll_dev->input,
+ adc->report_type, value * (-1));
+ }
+ adc->old_value = value;
+ }
+ input_sync(poll_dev->input);
+}
+
+/*----------------------------------------------------------------------------*/
+#ifdef VOLUME_ADC_KEYS
+static int joypad_vol_check(struct input_polled_dev *poll_dev)
+{
+ struct joypad *joypad = poll_dev->private;
+ //struct adc_keys_state *st = dev->private;
+ int i, value, ret;
+ u32 diff, closest = 0xffffffff;
+ int keycode = 0;
+
+ ret = iio_read_channel_processed(joypad->st->channel, &value);
+ if (unlikely(ret < 0)) {
+ /* Forcibly release key if any was pressed */
+ value = joypad->st->keyup_voltage;
+ } else {
+ for (i = 0; i < joypad->st->num_keys; i++) {
+ diff = abs(joypad->st->map[i].voltage - value);
+ if (diff < closest) {
+ closest = diff;
+ keycode = joypad->st->map[i].keycode;
+ }
+ }
+ }
+
+ if (((157<value) && (value< 300)) || ((value > 320) && (value<1700)))
+ return -1;
+ if (abs(joypad->st->keyup_voltage - value) < closest)
+ keycode = 0;
+
+ if (joypad->st->last_key && joypad->st->last_key != keycode)
+ input_report_key(poll_dev->input, joypad->st->last_key, 0);
+
+ if (keycode)
+ input_report_key(poll_dev->input, keycode, 1);
+
+ input_sync(poll_dev->input);
+ joypad->st->last_key = keycode;
+
+ return 0;
+}
+#endif
+/*----------------------------------------------------------------------------*/
+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);
+ #ifdef VOLUME_ADC_KEYS
+ joypad_vol_check(poll_dev);
+ #endif
+ }
+ 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__);
+}
+#ifdef VIBRATORS_FF
+/**
+ * gpio_vib_set - handler to start/stop vibration
+ * @vib: pointer to vibrator structure
+ * @on: state to set
+ */
+static int gpio_vib_set(struct gpio_vib *vib, bool on)
+{
+ if (on) {
+ if (vib->last_index) {
+ gpio_direction_output(vib->gpio1, 1);
+ gpio_set_value(vib->gpio1, 1);
+ } else {
+ gpio_direction_output(vib->gpio0, 1);
+ gpio_set_value(vib->gpio0, 1);
+ }
+ }
+ else {
+ if (vib->last_index) {
+ gpio_direction_output(vib->gpio1, 0);
+ gpio_set_value(vib->gpio1, 0);
+ } else {
+ gpio_direction_output(vib->gpio0, 0);
+ gpio_set_value(vib->gpio0, 0);
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * vib_work_handler - worker to set vibration level
+ * @work: pointer to work_struct
+ */
+static void vib_work_handler(struct work_struct *work)
+{
+ struct gpio_vib *vib = container_of(work, struct gpio_vib, work);
+ //struct joypad *joypad = container_of(vib, struct joypad, vib);
+
+ /*
+ * pmic vibrator supports voltage ranges from 1.2 to 3.1V, so
+ * scale the level to fit into these ranges.
+ */
+ printk("%s speed = %d\n", __func__, vib->speed);
+ if (vib->speed) {
+ vib->active = true;
+ } else {
+ vib->active = false;
+ }
+
+ gpio_vib_set(vib, vib->active);
+}
+#endif
+
+/*----------------------------------------------------------------------------*/
+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;
+}
+
+/*----------------------------------------------------------------------------*/
+#ifdef VOLUME_ADC_KEYS
+static int joypad_vol_setup(struct device *dev, struct joypad *joypad)
+{
+ struct device_node *node, *pp;
+ struct adc_keys_button *map;
+ //struct device *adckey_dev;
+ enum iio_chan_type type;
+ int i;
+ int error;
+
+ node = NULL;
+ node = of_find_matching_node(NULL, adckeys_of_match);
+
+ if(node) {
+
+ printk("%s into ...\n", __func__);
+ //adckey_dev = container_of(node, struct device, of_node);
+ joypad->st = devm_kzalloc(dev, sizeof(*joypad->st), GFP_KERNEL);
+
+ joypad->st->channel = devm_iio_channel_get(dev, "volume-buttons");
+ if (IS_ERR(joypad->st->channel))
+ return PTR_ERR(joypad->st->channel);
+
+ if (!joypad->st->channel->indio_dev)
+ return -ENXIO;
+
+ error = iio_get_channel_type(joypad->st->channel, &type);
+ if (error < 0)
+ return error;
+
+ if (type != IIO_VOLTAGE) {
+ dev_err(dev, "Incompatible channel type %d\n", type);
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32(node, "keyup-threshold-microvolt",
+ &joypad->st->keyup_voltage)) {
+ dev_err(dev, "Invalid or missing keyup voltage\n");
+ return -EINVAL;
+ }
+ joypad->st->keyup_voltage /= 1000;
+ printk("%s : keyup_voltage = %d\n", __func__, joypad->st->keyup_voltage);
+
+ joypad->st->num_keys = of_get_child_count(node);
+ if (joypad->st->num_keys == 0) {
+ dev_err(dev, "keymap is missing\n");
+ return -EINVAL;
+ }
+ printk("%s : adc_keys = %d\n", __func__, joypad->st->num_keys);
+
+ map = devm_kmalloc_array(dev, joypad->st->num_keys, sizeof(*map), GFP_KERNEL);
+ if (!map)
+ return -ENOMEM;
+
+ i = 0;
+ for_each_child_of_node(node, pp) {
+ if (of_property_read_u32(pp, "press-threshold-microvolt", &map[i].voltage)) {
+ dev_err(dev, "Key with invalid or missing voltage\n");
+ return -EINVAL;
+ }
+ map[i].voltage /= 1000;
+ printk("%s : key[%d] voltage = %d\n", __func__, i, map[i].voltage);
+
+ if (of_property_read_u32(pp, "linux,code", &map[i].keycode)) {
+ dev_err(dev, "Key with invalid or missing linux,code\n");
+ return -EINVAL;
+ }
+ printk("%s : key[%d] keycode = %d\n", __func__, i, map[i].keycode);
+
+ i++;
+ }
+ joypad->st->map = map;
+
+ }
+ if (i == 0)
+ return -EINVAL;
+
+ printk("%s end ...\n", __func__);
+ return 0;
+}
+#endif
+/*----------------------------------------------------------------------------*/
+#ifdef VIBRATORS_FF
+static int joypad_vib_setup(struct device *dev, struct joypad *joypad)
+{
+ struct device_node *node;
+ int ret = 0;
+
+ node = NULL;
+ node = of_find_matching_node(NULL, vibrators_of_match);
+
+ if(node) {
+
+ printk("%s into ... joypad = %p\n", __func__, joypad);
+ joypad->vib = devm_kzalloc(dev, sizeof(*joypad->vib), GFP_KERNEL);
+
+ joypad->vib->gpio0 = of_get_named_gpio_flags(node, "vibrator0-gpio", 0, NULL);
+ if (!gpio_is_valid(joypad->vib->gpio0))
+ return -1;
+
+ printk("%s get vibrator0-gpio = %d \n", __func__, joypad->vib->gpio0);
+ ret = devm_gpio_request(dev, joypad->vib->gpio0, "vibrator0");
+ if (ret < 0)
+ return ret;
+
+ joypad->vib->gpio1 = of_get_named_gpio_flags(node, "vibrator1-gpio", 0, NULL);
+ if (!gpio_is_valid(joypad->vib->gpio1))
+ return -1;
+
+ printk("%s get vibrator1-gpio = %d \n", __func__, joypad->vib->gpio1);
+ ret = devm_gpio_request(dev, joypad->vib->gpio1, "vibrator1");
+ if (ret < 0)
+ return ret;
+
+ INIT_WORK(&joypad->vib->work, vib_work_handler);
+
+ }
+
+ printk("%s end ...\n", __func__);
+ return 0;
+}
+
+/**
+ * gpio_vib_play_effect - function to handle vib effects.
+ * @dev: input device pointer
+ * @data: data of effect
+ * @effect: effect to play
+ *
+ * Currently this driver supports only rumble effects.
+ */
+static int gpio_vib_play_effect(struct input_dev *dev, void *data,
+ struct ff_effect *effect)
+{
+ __u16 strong;
+ __u16 weak;
+ //struct joypad *joypad = input_get_drvdata(dev);
+ struct joypad *joypad = data;
+
+ printk("%s joypad gpio key count = %d\n", __func__, joypad->bt_gpio_count);
+ printk("%s joypad = %p, get vibrator0-gpio = %d \n", __func__, joypad, joypad->vib->gpio0);
+ strong = effect->u.rumble.strong_magnitude;
+ weak = effect->u.rumble.weak_magnitude;
+
+ printk("%s strong = 0x%x, weak = 0x%x\n", __func__, strong, weak);
+ #if 1
+ if ((strong > 1) && ((weak == 0) || (weak == 1))) {
+ joypad->vib->speed = effect->u.rumble.strong_magnitude >> 8;
+ joypad->vib->index = weak;
+ joypad->vib->last_index = joypad->vib->index;
+ }
+ else if ((strong == 0) && ((weak == 0) || (weak == 1))) {
+ joypad->vib->speed = 0;
+ }
+ else if ((weak > 0) && ((strong == 0) || (strong == 1))) {
+ joypad->vib->speed = effect->u.rumble.weak_magnitude >> 8;
+ joypad->vib->index = strong;
+ joypad->vib->last_index = joypad->vib->index;
+ }
+ else if ((weak > 0) && ((strong == 0) || (strong == 1))) {
+ joypad->vib->speed = 0;
+ }
+
+ printk("%s speed = %d, index = %d\n", __func__, joypad->vib->speed, joypad->vib->index);
+ schedule_work(&joypad->vib->work);
+ #endif
+ return 0;
+}
+#endif
+
+/*----------------------------------------------------------------------------*/
+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;
+ #ifdef RIGHT_ADC_JOYSTICK
+ global_input = poll_dev->input;
+ #endif
+ input->name = DRV_NAME;
+ input->phys = DRV_NAME"/input0";
+
+ input->id.bustype = BUS_HOST;
+ input->id.vendor = 0x0003;
+ input->id.product = 0x0003;
+ input->id.version = 0x0302;
+
+ /* 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);
+ }
+ #ifdef RIGHT_ADC_JOYSTICK
+ /* Right Analog */
+ input_set_abs_params(input, ABS_RX, -900, 899, 32, 32);
+ input_set_abs_params(input, ABS_RY, -900, 899, 32, 32);
+ #endif
+
+ /* 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);
+ }
+#ifdef VOLUME_ADC_KEYS
+ /* ADC key setup */
+ for (nbtn = 0; nbtn < joypad->st->num_keys; nbtn++)
+ __set_bit(joypad->st->map[nbtn].keycode, input->keybit);
+#endif
+ if (joypad->auto_repeat)
+ __set_bit(EV_REP, input->evbit);
+#ifdef VIBRATORS_FF
+ /* vibrator for ff */
+ input_set_capability(input, EV_FF, FF_RUMBLE);
+ error = input_ff_create_memless(input, joypad, gpio_vib_play_effect);
+ if (error)
+ return error;
+#endif
+ 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;
+#ifdef VOLUME_ADC_KEYS
+ error = joypad_vol_setup(dev, joypad);
+ printk("222 %s : error = %d\n", __func__, error);
+ if (error)
+ return error;
+#endif
+#ifdef VIBRATORS_FF
+ error = joypad_vib_setup(dev, joypad);
+ printk("333 %s : error = %d\n", __func__, error);
+ if (error)
+ return error;
+#endif
+ return error;
+}
+
+#ifdef RIGHT_ADC_JOYSTICK
+static int joypad_misc_open(struct inode *inode, struct file *file)
+{
+ printk("%s ...\n", __func__);
+
+ //file->private_data = global_input;
+ nonseekable_open(inode, file);
+
+ return 0;
+}
+
+static int joypad_misc_release(struct inode *inode, struct file *file)
+{
+ //struct input_dev *dev = file->private_data;
+
+ //input_unregister_device(dev);
+ //kfree(dev);
+
+ return 0;
+}
+
+//static ssize_t joypad_inject_events(struct input_dev *dev, const char __user *buffer, size_t count
+static ssize_t joypad_inject_events(const char __user *buffer, size_t count)
+{
+ struct input_event ev;
+ size_t bytes = 0;
+
+ if (count != 0 && count < input_event_size())
+ return -EINVAL;
+
+ while (bytes + input_event_size() <= count) {
+ /*
+ * Note that even if some events were fetched successfully
+ * we are still going to return EFAULT instead of partial
+ * count to let userspace know that it got it's buffers
+ * all wrong.
+ */
+ if (input_event_from_user(buffer + bytes, &ev))
+ return -EFAULT;
+
+ //input_event(dev, ev.type, ev.code, ev.value);
+ input_event(global_input, ev.type, ev.code, ev.value);
+ bytes += input_event_size();
+ }
+
+ return bytes;
+}
+
+static ssize_t joypad_misc_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ //struct input_dev *dev = file->private_data;
+ int retval;
+
+ if (count == 0)
+ return 0;
+
+ retval = mutex_lock_interruptible(&global_input->mutex);
+ if (retval)
+ return retval;
+
+ //retval = joypad_inject_events(dev, buffer, count);
+ retval = joypad_inject_events(buffer, count);
+
+ mutex_unlock(&global_input->mutex);
+
+ return retval;
+}
+
+static long joypad_misc_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int ret = 0;
+ return ret;
+}
+
+
+static const struct file_operations joypad_fops = {
+ .owner = THIS_MODULE,
+ .open = joypad_misc_open,
+ .release = joypad_misc_release,
+ .write = joypad_misc_write,
+ .unlocked_ioctl = joypad_misc_ioctl,
+ .llseek = no_llseek,
+
+};
+
+
+static struct miscdevice joypad_misc_dev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "joypad",
+ .fops = &joypad_fops,
+};
+#endif
+/*----------------------------------------------------------------------------*/
+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;
+ }
+#ifdef RIGHT_ADC_JOYSTICK
+ error = misc_register(&joypad_misc_dev);
+ if (error < 0)
+ printk("%s :fail to register misc device.\n", __func__);
+#endif
+ dev_info(dev, "%s : probe success\n", __func__);
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+static const struct of_device_id joypad_of_match[] = {
+ /*{ .compatible = "odroidgo2-joypad", },*/
+ { .compatible = "gameforce-gamepad", },
+ {},
+};
+
+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 Gameforce CHI");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
+
+/*----------------------------------------------------------------------------*/
diff --git a/drivers/input/joystick/odroidgo2-joypad.c b/drivers/input/joystick/odroidgo2-joypad.c
new file mode 100644
index 000000000000..1382d69eec5e
--- /dev/null
+++ b/drivers/input/joystick/odroidgo2-joypad.c
@@ -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 <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+/*----------------------------------------------------------------------------*/
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/input-polldev.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio_keys.h>
+#include <linux/iio/consumer.h>
+#include <linux/iio/types.h>
+#include <linux/property.h>
+#include <linux/of_gpio_legacy.h>
+
+/*----------------------------------------------------------------------------*/
+#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 --git a/drivers/input/joystick/odroidgo2-v11-joypad.c b/drivers/input/joystick/odroidgo2-v11-joypad.c
new file mode 100644
index 000000000000..b5bd7cb40a9b
--- /dev/null
+++ b/drivers/input/joystick/odroidgo2-v11-joypad.c
@@ -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 <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+/*----------------------------------------------------------------------------*/
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/input-polldev.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio_keys.h>
+#include <linux/iio/consumer.h>
+#include <linux/iio/types.h>
+#include <linux/property.h>
+#include <linux/of_gpio_legacy.h>
+
+/*----------------------------------------------------------------------------*/
+#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 --git a/drivers/input/joystick/odroidgo3-joypad.c b/drivers/input/joystick/odroidgo3-joypad.c
new file mode 100644
index 000000000000..34f182175bcd
--- /dev/null
+++ b/drivers/input/joystick/odroidgo3-joypad.c
@@ -0,0 +1,1112 @@
+/*
+ * 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 <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+/*----------------------------------------------------------------------------*/
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/input-polldev.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio_keys.h>
+#include <linux/iio/consumer.h>
+#include <linux/iio/types.h>
+#include <linux/property.h>
+#include <linux/of_gpio_legacy.h>
+#include <linux/delay.h>
+
+/*----------------------------------------------------------------------------*/
+#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 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;
+};
+
+/*----------------------------------------------------------------------------*/
+//
+// 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 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:
+ 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;
+}
+
+/*----------------------------------------------------------------------------*/
+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;
+
+ 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;
+
+ 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 = 0x0001;
+ 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);
+ }
+
+ /* 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? (read value * -1) */
+ 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, invert-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 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 --git a/drivers/input/joystick/rgb20s-joypad.c b/drivers/input/joystick/rgb20s-joypad.c
new file mode 100644
index 000000000000..a52981eee9fb
--- /dev/null
+++ b/drivers/input/joystick/rgb20s-joypad.c
@@ -0,0 +1,1373 @@
+/*
+ * SARADC joystick & GPIO Button driver for Linux(Powkiddy RGB20S)
+ */
+/*
+ * 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 <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+/*----------------------------------------------------------------------------*/
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/input-polldev.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio_keys.h>
+#include <linux/iio/consumer.h>
+#include <linux/iio/types.h>
+#include <linux/property.h>
+#include <linux/of_gpio_legacy.h>
+#include <linux/delay.h>
+#include <linux/pwm.h>
+
+/*----------------------------------------------------------------------------*/
+#define DRV_NAME "rgb20s_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)))
+
+/*----------------------------------------------------------------------------*/
+/*
+ +--------------------------------+
+ | 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 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_might_sleep(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(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 value;
+}
+
+/*----------------------------------------------------------------------------*/
+/*----------------------------------------------------------------------------*/
+/*
+ * ATTRIBUTES:
+ *
+ * /sys/devices/platform/rgb20s_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/rgb20s_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/rgb20s_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/rgb20s_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/rgb20s_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/rgb20s_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/rgb20s_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/rgb20s_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/rgb20s_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);
+
+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,
+};
+
+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);
+
+ 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;
+}
+
+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_might_sleep(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 = "rgb20s-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);
+
+/*----------------------------------------------------------------------------*/
+MODULE_AUTHOR("Hardkernel Co.,LTD");
+MODULE_DESCRIPTION("Keypad driver(ADC&GPIO) for Powkiddy RGB20S");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
+
+/*----------------------------------------------------------------------------*/
diff --git a/drivers/input/joystick/xu10-joypad.c b/drivers/input/joystick/xu10-joypad.c
new file mode 100644
index 000000000000..f7832a8158d4
--- /dev/null
+++ b/drivers/input/joystick/xu10-joypad.c
@@ -0,0 +1,1139 @@
+/*
+ * SARADC joystick & GPIO Button driver for Linux(Hardkernel MagicX-XU10)
+ */
+/*
+ * 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 <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+/*----------------------------------------------------------------------------*/
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/input-polldev.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/gpio_keys.h>
+#include <linux/iio/consumer.h>
+#include <linux/iio/types.h>
+#include <linux/property.h>
+#include <linux/of_gpio_legacy.h>
+#include <linux/delay.h>
+
+/*----------------------------------------------------------------------------*/
+#define DRV_NAME "xu10_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;
+ /* 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;
+ struct iio_channel *iio_ch_r;
+ /* 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;
+};
+
+/*----------------------------------------------------------------------------*/
+//
+// 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, 1);
+ gpio_set_value(amux->sel_b_gpio, 1);
+ break;
+ case 2: /* EVENT (ABS_Y) */
+ gpio_set_value(amux->sel_a_gpio, 0);
+ 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 disable */
+ 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;
+ int res;
+
+ if (joypad_amux_select(amux, adc->amux_ch))
+ return 0;
+
+ switch(adc->amux_ch) {
+ case 0:
+ case 1:
+ res = iio_read_channel_processed(amux->iio_ch_r, &value);
+ break;
+ case 2:
+ case 3:
+ default:
+ res = iio_read_channel_processed(amux->iio_ch, &value);
+ break;
+ }
+
+ if (res)
+ return 0;
+
+ value *= adc->scale;
+
+ return 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;
+ int res;
+
+ mutex_lock(&joypad->lock);
+
+ /* disable poll driver */
+ if (joypad->enable)
+ joypad->enable = false;
+
+ if (joypad_amux_select(amux, joypad->debug_ch))
+ goto err_out;
+
+ switch(joypad->debug_ch) {
+ case 0:
+ case 1:
+ res = iio_read_channel_processed(amux->iio_ch_r, &value);
+ break;
+ case 2:
+ case 3:
+ default:
+ res = iio_read_channel_processed(amux->iio_ch, &value);
+ break;
+ }
+
+ if (res)
+ 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, "joy_left");
+ amux->iio_ch_r = devm_iio_channel_get(dev, "joy_right");
+
+ if (IS_ERR(amux->iio_ch)) {
+ dev_err(dev, "iio channel get error\n");
+ return -EINVAL;
+ }
+
+ if (IS_ERR(amux->iio_ch_r)) {
+ dev_err(dev, "iio channel right get error\n");
+ return -EINVAL;
+ }
+
+ if (!amux->iio_ch->indio_dev || !amux->iio_ch_r->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;
+ }
+
+ if (iio_get_channel_type(amux->iio_ch_r, &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;
+}
+
+/*----------------------------------------------------------------------------*/
+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_product = 0;
+ u32 joypad_revision = 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;
+
+ device_property_read_string(dev, "joypad-name", &input->name);
+ input->phys = DRV_NAME"/input0";
+
+ device_property_read_u32(dev, "joypad-product", &joypad_product);
+ device_property_read_u32(dev, "joypad-revision", &joypad_revision);
+
+ input->id.bustype = BUS_HOST;
+ input->id.vendor = 0xC3B0;
+ 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);
+ }
+
+ /* 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? (read value * -1) */
+ 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, invert-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 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 = "xu10-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 MagicX-XU10");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
+
+/*----------------------------------------------------------------------------*/
diff --git a/drivers/power/supply/rk817_charger.c b/drivers/power/supply/rk817_charger.c
index 7ca91739c6cc..a2a6d8093206 100644
--- a/drivers/power/supply/rk817_charger.c
+++ b/drivers/power/supply/rk817_charger.c
@@ -679,7 +679,7 @@ static enum power_supply_usb_type rk817_usb_type[] = {
};
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),