mirror of
https://github.com/archr-linux/Arch-R.git
synced 2026-03-31 14:41:55 -07:00
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>
6830 lines
193 KiB
Diff
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),
|