2020-03-01 15:02:50

by Artur Rojek

[permalink] [raw]
Subject: [PATCH v3 1/5] IIO: Ingenic JZ47xx: Add xlate cb to retrieve correct channel idx

Provide an of_xlate callback in order to retrieve the correct channel
specifier index from the IIO channels array.

Signed-off-by: Artur Rojek <[email protected]>
Tested-by: Paul Cercueil <[email protected]>
---

Changes:

v2-v3: no change

drivers/iio/adc/ingenic-adc.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)

diff --git a/drivers/iio/adc/ingenic-adc.c b/drivers/iio/adc/ingenic-adc.c
index 39c0a609fc94..7a24bc1dabe1 100644
--- a/drivers/iio/adc/ingenic-adc.c
+++ b/drivers/iio/adc/ingenic-adc.c
@@ -383,6 +383,21 @@ static int ingenic_adc_read_raw(struct iio_dev *iio_dev,
}
}

+static int ingenic_adc_of_xlate(struct iio_dev *iio_dev,
+ const struct of_phandle_args *iiospec)
+{
+ int i;
+
+ if (!iiospec->args_count)
+ return -EINVAL;
+
+ for (i = 0; i < iio_dev->num_channels; ++i)
+ if (iio_dev->channels[i].channel == iiospec->args[0])
+ return i;
+
+ return -EINVAL;
+}
+
static void ingenic_adc_clk_cleanup(void *data)
{
clk_unprepare(data);
@@ -392,6 +407,7 @@ static const struct iio_info ingenic_adc_info = {
.write_raw = ingenic_adc_write_raw,
.read_raw = ingenic_adc_read_raw,
.read_avail = ingenic_adc_read_avail,
+ .of_xlate = ingenic_adc_of_xlate,
};

static const struct iio_chan_spec ingenic_channels[] = {
--
2.25.1


2020-03-01 15:02:58

by Artur Rojek

[permalink] [raw]
Subject: [PATCH v3 2/5] dt-bindings: iio/adc: Add touchscreen idx for JZ47xx SoC ADC

Introduce support for touchscreen channels found in JZ47xx SoCs.

Signed-off-by: Artur Rojek <[email protected]>
Tested-by: Paul Cercueil <[email protected]>
Acked-by: Rob Herring <[email protected]>
---

Changes:

v2-v3: no change

include/dt-bindings/iio/adc/ingenic,adc.h | 2 ++
1 file changed, 2 insertions(+)

diff --git a/include/dt-bindings/iio/adc/ingenic,adc.h b/include/dt-bindings/iio/adc/ingenic,adc.h
index 42f871ab3272..95e20a8d6dc8 100644
--- a/include/dt-bindings/iio/adc/ingenic,adc.h
+++ b/include/dt-bindings/iio/adc/ingenic,adc.h
@@ -7,5 +7,7 @@
#define INGENIC_ADC_AUX 0
#define INGENIC_ADC_BATTERY 1
#define INGENIC_ADC_AUX2 2
+#define INGENIC_ADC_TOUCH_XP 3
+#define INGENIC_ADC_TOUCH_YP 4

#endif
--
2.25.1

2020-03-01 15:03:08

by Artur Rojek

[permalink] [raw]
Subject: [PATCH v3 3/5] IIO: Ingenic JZ47xx: Add touchscreen mode.

The SADC component in JZ47xx SoCs provides support for touchscreen
operations (pen position and pen down pressure) in single-ended and
differential modes.

Of the known hardware to use this controller, GCW Zero and Anbernic RG-350
utilize the touchscreen mode by having their joystick(s) attached to the
X/Y positive/negative input pins.
GCW Zero comes with a single joystick and is sufficiently handled with the
currently implemented single-ended mode. Support for boards with two
joysticks, where one is hooked up to Xn/Yn and the other to Xp/Yp channels
will need to be provided in the future.

The touchscreen component of SADC takes a significant time to stabilize
after first receiving the clock and a delay of 50ms has been empirically
proven to be a safe value before data sampling can begin.

Signed-off-by: Artur Rojek <[email protected]>
Tested-by: Paul Cercueil <[email protected]>
---

Changes:

v2: - improve description of the touchscreen mode,
- get rid of the unneeded kfifo,
- drop IIO_BUFFER_CB from Kconfig,
- remove extended names from the touchscreen channels

v3: remove unneeded `linux/iio/kfifo_buf.h` include

drivers/iio/adc/Kconfig | 1 +
drivers/iio/adc/ingenic-adc.c | 109 +++++++++++++++++++++++++++++++++-
2 files changed, 108 insertions(+), 2 deletions(-)

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 82e33082958c..d3fd4b6e2d47 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -453,6 +453,7 @@ config INA2XX_ADC
config INGENIC_ADC
tristate "Ingenic JZ47xx SoCs ADC driver"
depends on MIPS || COMPILE_TEST
+ select IIO_BUFFER
help
Say yes here to build support for the Ingenic JZ47xx SoCs ADC unit.

diff --git a/drivers/iio/adc/ingenic-adc.c b/drivers/iio/adc/ingenic-adc.c
index 7a24bc1dabe1..0dafc8d5d0d8 100644
--- a/drivers/iio/adc/ingenic-adc.c
+++ b/drivers/iio/adc/ingenic-adc.c
@@ -8,7 +8,9 @@

#include <dt-bindings/iio/adc/ingenic,adc.h>
#include <linux/clk.h>
+#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
@@ -20,6 +22,8 @@
#define JZ_ADC_REG_CFG 0x04
#define JZ_ADC_REG_CTRL 0x08
#define JZ_ADC_REG_STATUS 0x0c
+#define JZ_ADC_REG_ADSAME 0x10
+#define JZ_ADC_REG_ADWAIT 0x14
#define JZ_ADC_REG_ADTCH 0x18
#define JZ_ADC_REG_ADBDAT 0x1c
#define JZ_ADC_REG_ADSDAT 0x20
@@ -28,6 +32,9 @@
#define JZ_ADC_REG_ENABLE_PD BIT(7)
#define JZ_ADC_REG_CFG_AUX_MD (BIT(0) | BIT(1))
#define JZ_ADC_REG_CFG_BAT_MD BIT(4)
+#define JZ_ADC_REG_CFG_PULL_UP(n) ((n) << 16)
+#define JZ_ADC_REG_CFG_SAMPLE_NUM(n) ((n) << 10)
+#define JZ_ADC_REG_CFG_TOUCH_OPS_MASK (BIT(31) | GENMASK(23, 10))
#define JZ_ADC_REG_ADCLK_CLKDIV_LSB 0
#define JZ4725B_ADC_REG_ADCLK_CLKDIV10US_LSB 16
#define JZ4770_ADC_REG_ADCLK_CLKDIV10US_LSB 8
@@ -44,6 +51,14 @@
#define JZ4770_ADC_BATTERY_VREF 6600
#define JZ4770_ADC_BATTERY_VREF_BITS 12

+#define JZ_ADC_IRQ_AUX BIT(0)
+#define JZ_ADC_IRQ_BATTERY BIT(1)
+#define JZ_ADC_IRQ_TOUCH BIT(2)
+#define JZ_ADC_IRQ_PEN_DOWN BIT(3)
+#define JZ_ADC_IRQ_PEN_UP BIT(4)
+#define JZ_ADC_IRQ_PEN_DOWN_SLEEP BIT(5)
+#define JZ_ADC_IRQ_SLEEP BIT(7)
+
struct ingenic_adc;

struct ingenic_adc_soc_data {
@@ -411,6 +426,28 @@ static const struct iio_info ingenic_adc_info = {
};

static const struct iio_chan_spec ingenic_channels[] = {
+ {
+ .type = IIO_POSITIONRELATIVE,
+ .indexed = 1,
+ .channel = INGENIC_ADC_TOUCH_XP,
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 12,
+ .storagebits = 16
+ },
+ },
+ {
+ .type = IIO_POSITIONRELATIVE,
+ .indexed = 1,
+ .channel = INGENIC_ADC_TOUCH_YP,
+ .scan_index = 1,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 12,
+ .storagebits = 16
+ },
+ },
{
.extend_name = "aux",
.type = IIO_VOLTAGE,
@@ -418,6 +455,7 @@ static const struct iio_chan_spec ingenic_channels[] = {
BIT(IIO_CHAN_INFO_SCALE),
.indexed = 1,
.channel = INGENIC_ADC_AUX,
+ .scan_index = -1
},
{
.extend_name = "battery",
@@ -428,6 +466,7 @@ static const struct iio_chan_spec ingenic_channels[] = {
BIT(IIO_CHAN_INFO_SCALE),
.indexed = 1,
.channel = INGENIC_ADC_BATTERY,
+ .scan_index = -1
},
{ /* Must always be last in the array. */
.extend_name = "aux2",
@@ -436,16 +475,69 @@ static const struct iio_chan_spec ingenic_channels[] = {
BIT(IIO_CHAN_INFO_SCALE),
.indexed = 1,
.channel = INGENIC_ADC_AUX2,
+ .scan_index = -1
},
};

+static int ingenic_adc_buffer_enable(struct iio_dev *iio_dev)
+{
+ struct ingenic_adc *adc = iio_priv(iio_dev);
+
+ clk_enable(adc->clk);
+ /* It takes significant time for the touchscreen hw to stabilize. */
+ msleep(50);
+ ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_TOUCH_OPS_MASK,
+ JZ_ADC_REG_CFG_SAMPLE_NUM(4) |
+ JZ_ADC_REG_CFG_PULL_UP(4));
+ writew(80, adc->base + JZ_ADC_REG_ADWAIT);
+ writew(2, adc->base + JZ_ADC_REG_ADSAME);
+ writeb((u8)~JZ_ADC_IRQ_TOUCH, adc->base + JZ_ADC_REG_CTRL);
+ writel(0, adc->base + JZ_ADC_REG_ADTCH);
+ ingenic_adc_enable(adc, 2, true);
+
+ return 0;
+}
+
+static int ingenic_adc_buffer_disable(struct iio_dev *iio_dev)
+{
+ struct ingenic_adc *adc = iio_priv(iio_dev);
+
+ ingenic_adc_enable(adc, 2, false);
+ writeb(0xff, adc->base + JZ_ADC_REG_CTRL);
+ writeb(0xff, adc->base + JZ_ADC_REG_STATUS);
+ ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_TOUCH_OPS_MASK, 0);
+ writew(0, adc->base + JZ_ADC_REG_ADSAME);
+ writew(0, adc->base + JZ_ADC_REG_ADWAIT);
+ clk_disable(adc->clk);
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops ingenic_buffer_setup_ops = {
+ .postenable = &ingenic_adc_buffer_enable,
+ .predisable = &ingenic_adc_buffer_disable
+};
+
+static irqreturn_t ingenic_adc_irq(int irq, void *data)
+{
+ struct iio_dev *iio_dev = data;
+ struct ingenic_adc *adc = iio_priv(iio_dev);
+ u32 tdat;
+
+ tdat = readl(adc->base + JZ_ADC_REG_ADTCH);
+ iio_push_to_buffers(iio_dev, &tdat);
+ writeb(JZ_ADC_IRQ_TOUCH, adc->base + JZ_ADC_REG_STATUS);
+
+ return IRQ_HANDLED;
+}
+
static int ingenic_adc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct iio_dev *iio_dev;
struct ingenic_adc *adc;
const struct ingenic_adc_soc_data *soc_data;
- int ret;
+ int irq, ret;

soc_data = device_get_match_data(dev);
if (!soc_data)
@@ -460,6 +552,18 @@ static int ingenic_adc_probe(struct platform_device *pdev)
mutex_init(&adc->aux_lock);
adc->soc_data = soc_data;

+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(dev, "Failed to get irq: %d\n", irq);
+ return irq;
+ }
+ ret = devm_request_irq(dev, irq, ingenic_adc_irq, 0,
+ dev_name(dev), iio_dev);
+ if (ret < 0) {
+ dev_err(dev, "Failed to request irq: %d\n", ret);
+ return ret;
+ }
+
adc->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(adc->base))
return PTR_ERR(adc->base);
@@ -499,7 +603,8 @@ static int ingenic_adc_probe(struct platform_device *pdev)

iio_dev->dev.parent = dev;
iio_dev->name = "jz-adc";
- iio_dev->modes = INDIO_DIRECT_MODE;
+ iio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
+ iio_dev->setup_ops = &ingenic_buffer_setup_ops;
iio_dev->channels = ingenic_channels;
iio_dev->num_channels = ARRAY_SIZE(ingenic_channels);
/* Remove AUX2 from the list of supported channels. */
--
2.25.1

2020-03-01 15:04:10

by Artur Rojek

[permalink] [raw]
Subject: [PATCH v3 4/5] dt-bindings: input: Add docs for ADC driven joystick.

Add documentation for the adc-joystick driver, used to provide support
for joysticks connected over ADC.

Signed-off-by: Artur Rojek <[email protected]>
Tested-by: Paul Cercueil <[email protected]>
---

Changes:

v2: - Add `reg` property to axis subnode in order to enumerate the axes,
- rename `linux,abs-code` property to `linux,code`,
- drop `linux,` prefix from the remaining properties of axis subnode

v3: no change

.../bindings/input/adc-joystick.yaml | 117 ++++++++++++++++++
1 file changed, 117 insertions(+)
create mode 100644 Documentation/devicetree/bindings/input/adc-joystick.yaml

diff --git a/Documentation/devicetree/bindings/input/adc-joystick.yaml b/Documentation/devicetree/bindings/input/adc-joystick.yaml
new file mode 100644
index 000000000000..91fc87dcbddb
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/adc-joystick.yaml
@@ -0,0 +1,117 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+# Copyright 2019-2020 Artur Rojek
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/bindings/input/adc-joystick.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: ADC attached joystick
+
+maintainers:
+ - Artur Rojek <[email protected]>
+
+description: |
+ Bindings for joystick devices connected to ADC controllers supporting
+ the Industrial I/O subsystem.
+
+properties:
+ compatible:
+ const: adc-joystick
+
+ io-channels:
+ description: |
+ List of phandle and IIO specifier pairs.
+ Each pair defines one ADC channel to which a joystick axis is connected.
+ See Documentation/devicetree/bindings/iio/iio-bindings.txt for details.
+
+ '#address-cells':
+ const: 1
+
+ '#size-cells':
+ const: 0
+
+required:
+ - compatible
+ - io-channels
+ - '#address-cells'
+ - '#size-cells'
+
+additionalProperties: false
+
+patternProperties:
+ "^axis@([0-9])$":
+ type: object
+ description: |
+ Represents a joystick axis bound to the given ADC channel.
+ For each entry in the io-channels list, one axis subnode with a matching
+ reg property must be specified.
+
+ properties:
+ reg:
+ items:
+ description: Index of an io-channels list entry bound to this axis.
+
+ linux,code:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: EV_ABS specific event code generated by the axis.
+
+ abs-range:
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ items:
+ - description: minimum value
+ - description: maximum value
+ description: |
+ Minimum and maximum values produced by the axis.
+ For an ABS_X axis this will be the left-most and right-most
+ inclination of the joystick. If min > max, it is left to userspace to
+ treat the axis as inverted.
+ This property is interpreted as two signed 32 bit values.
+
+ abs-fuzz:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: |
+ Amount of noise in the input value.
+ Omitting this property indicates the axis is precise.
+
+ abs-flat:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: |
+ Axial "deadzone", or area around the center position, where the axis
+ is considered to be at rest.
+ Omitting this property indicates the axis always returns to exactly
+ the center position.
+
+ required:
+ - reg
+ - linux,code
+ - abs-range
+
+ additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/iio/adc/ingenic,adc.h>
+ #include <dt-bindings/input/input.h>
+
+ joystick: adc-joystick {
+ compatible = "adc-joystick";
+ io-channels = <&adc INGENIC_ADC_TOUCH_XP>,
+ <&adc INGENIC_ADC_TOUCH_YP>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ axis@0 {
+ reg = <0>;
+ linux,code = <ABS_X>;
+ abs-range = <3300 0>;
+ abs-fuzz = <4>;
+ abs-flat = <200>;
+ };
+ axis@1 {
+ reg = <1>;
+ linux,code = <ABS_Y>;
+ abs-range = <0 3300>;
+ abs-fuzz = <4>;
+ abs-flat = <200>;
+ };
+ };
--
2.25.1

2020-03-01 15:05:11

by Artur Rojek

[permalink] [raw]
Subject: [PATCH v3 5/5] input: joystick: Add ADC attached joystick driver.

Add a driver for joystick devices connected to ADC controllers
supporting the Industrial I/O subsystem.

Signed-off-by: Artur Rojek <[email protected]>
Tested-by: Paul Cercueil <[email protected]>
Tested-by: Heiko Stuebner <[email protected]>
Acked-by: Dmitry Torokhov <[email protected]>
---

Changes:

v2: - sanity check supported channel format on probe,
- rename adc_joystick_disable to a more sensible adc_joystick_cleanup,
- enforce correct axis order by checking the `reg` property of
child nodes

v3: no change

drivers/input/joystick/Kconfig | 10 ++
drivers/input/joystick/Makefile | 1 +
drivers/input/joystick/adc-joystick.c | 245 ++++++++++++++++++++++++++
3 files changed, 256 insertions(+)
create mode 100644 drivers/input/joystick/adc-joystick.c

diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig
index 940b744639c7..efbc20ec5099 100644
--- a/drivers/input/joystick/Kconfig
+++ b/drivers/input/joystick/Kconfig
@@ -42,6 +42,16 @@ config JOYSTICK_A3D
To compile this driver as a module, choose M here: the
module will be called a3d.

+config JOYSTICK_ADC
+ tristate "Simple joystick connected over ADC"
+ depends on IIO
+ select IIO_BUFFER_CB
+ help
+ Say Y here if you have a simple joystick connected over ADC.
+
+ To compile this driver as a module, choose M here: the
+ module will be called adc-joystick.
+
config JOYSTICK_ADI
tristate "Logitech ADI digital joysticks and gamepads"
select GAMEPORT
diff --git a/drivers/input/joystick/Makefile b/drivers/input/joystick/Makefile
index 8656023f6ef5..58232b3057d3 100644
--- a/drivers/input/joystick/Makefile
+++ b/drivers/input/joystick/Makefile
@@ -6,6 +6,7 @@
# Each configuration option enables a list of files.

obj-$(CONFIG_JOYSTICK_A3D) += a3d.o
+obj-$(CONFIG_JOYSTICK_ADC) += adc-joystick.o
obj-$(CONFIG_JOYSTICK_ADI) += adi.o
obj-$(CONFIG_JOYSTICK_AMIGA) += amijoy.o
obj-$(CONFIG_JOYSTICK_AS5011) += as5011.o
diff --git a/drivers/input/joystick/adc-joystick.c b/drivers/input/joystick/adc-joystick.c
new file mode 100644
index 000000000000..9cb9896da26e
--- /dev/null
+++ b/drivers/input/joystick/adc-joystick.c
@@ -0,0 +1,245 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Input driver for joysticks connected over ADC.
+ * Copyright (c) 2019-2020 Artur Rojek <[email protected]>
+ */
+#include <linux/ctype.h>
+#include <linux/input.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/consumer.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+
+struct adc_joystick_axis {
+ u32 code;
+ s32 range[2];
+ s32 fuzz;
+ s32 flat;
+};
+
+struct adc_joystick {
+ struct input_dev *input;
+ struct iio_cb_buffer *buffer;
+ struct adc_joystick_axis *axes;
+ struct iio_channel *chans;
+ int num_chans;
+};
+
+static int adc_joystick_handle(const void *data, void *private)
+{
+ struct adc_joystick *joy = private;
+ enum iio_endian endianness;
+ int bytes, msb, val, i;
+ bool sign;
+
+ bytes = joy->chans[0].channel->scan_type.storagebits >> 3;
+
+ for (i = 0; i < joy->num_chans; ++i) {
+ endianness = joy->chans[i].channel->scan_type.endianness;
+ msb = joy->chans[i].channel->scan_type.realbits - 1;
+ sign = (tolower(joy->chans[i].channel->scan_type.sign) == 's');
+
+ switch (bytes) {
+ case 1:
+ val = ((const u8 *)data)[i];
+ break;
+ case 2:
+ val = ((const u16 *)data)[i];
+ if (endianness == IIO_BE)
+ val = be16_to_cpu(val);
+ else if (endianness == IIO_LE)
+ val = le16_to_cpu(val);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ val >>= joy->chans[i].channel->scan_type.shift;
+ if (sign)
+ val = sign_extend32(val, msb);
+ else
+ val &= GENMASK(msb, 0);
+ input_report_abs(joy->input, joy->axes[i].code, val);
+ }
+
+ input_sync(joy->input);
+
+ return 0;
+}
+
+static int adc_joystick_open(struct input_dev *dev)
+{
+ struct adc_joystick *joy = input_get_drvdata(dev);
+ int ret;
+
+ ret = iio_channel_start_all_cb(joy->buffer);
+ if (ret)
+ dev_err(dev->dev.parent, "Unable to start callback buffer");
+
+ return ret;
+}
+
+static void adc_joystick_close(struct input_dev *dev)
+{
+ struct adc_joystick *joy = input_get_drvdata(dev);
+
+ iio_channel_stop_all_cb(joy->buffer);
+}
+
+static void adc_joystick_cleanup(void *data)
+{
+ iio_channel_release_all_cb(data);
+}
+
+static int adc_joystick_set_axes(struct device *dev, struct adc_joystick *joy)
+{
+ struct adc_joystick_axis *axes;
+ struct fwnode_handle *child;
+ int num_axes, ret, i;
+
+ num_axes = device_get_child_node_count(dev);
+ if (!num_axes) {
+ dev_err(dev, "Unable to find child nodes");
+ return -EINVAL;
+ }
+
+ if (num_axes != joy->num_chans) {
+ dev_err(dev, "Got %d child nodes for %d channels",
+ num_axes, joy->num_chans);
+ return -EINVAL;
+ }
+
+ axes = devm_kmalloc_array(dev, num_axes, sizeof(*axes), GFP_KERNEL);
+ if (!axes)
+ return -ENOMEM;
+
+ device_for_each_child_node(dev, child) {
+ ret = fwnode_property_read_u32(child, "reg", &i);
+ if (ret || i >= num_axes) {
+ dev_err(dev, "reg invalid or missing");
+ goto err;
+ }
+
+ if (fwnode_property_read_u32(child, "linux,code",
+ &axes[i].code)) {
+ dev_err(dev, "linux,code invalid or missing");
+ goto err;
+ }
+
+ if (fwnode_property_read_u32_array(child, "abs-range",
+ axes[i].range, 2)) {
+ dev_err(dev, "abs-range invalid or missing");
+ goto err;
+ }
+
+ fwnode_property_read_u32(child, "abs-fuzz",
+ &axes[i].fuzz);
+ fwnode_property_read_u32(child, "abs-flat",
+ &axes[i].flat);
+
+ input_set_abs_params(joy->input, axes[i].code,
+ axes[i].range[0], axes[i].range[1],
+ axes[i].fuzz,
+ axes[i].flat);
+ input_set_capability(joy->input, EV_ABS, axes[i].code);
+ }
+
+ joy->axes = axes;
+
+ return 0;
+
+err:
+ fwnode_handle_put(child);
+ return -EINVAL;
+}
+
+static int adc_joystick_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct adc_joystick *joy;
+ struct input_dev *input;
+ int bits, ret, i;
+
+ joy = devm_kzalloc(dev, sizeof(*joy), GFP_KERNEL);
+ if (!joy)
+ return -ENOMEM;
+
+ joy->chans = devm_iio_channel_get_all(dev);
+ if (IS_ERR(joy->chans)) {
+ ret = PTR_ERR(joy->chans);
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "Unable to get IIO channels");
+ return ret;
+ }
+
+ /* Count how many channels we got. NULL terminated. */
+ while (joy->chans[joy->num_chans].indio_dev)
+ joy->num_chans++;
+
+ bits = joy->chans[0].channel->scan_type.storagebits;
+ if (!bits || (bits >> 3) > 2) {
+ dev_err(dev, "Unsupported channel storage size");
+ return -EINVAL;
+ }
+ for (i = 1; i < joy->num_chans; ++i)
+ if (joy->chans[i].channel->scan_type.storagebits != bits) {
+ dev_err(dev, "Channels must have equal storage size");
+ return -EINVAL;
+ }
+
+ input = devm_input_allocate_device(dev);
+ if (!input) {
+ dev_err(dev, "Unable to allocate input device");
+ return -ENOMEM;
+ }
+
+ joy->input = input;
+ input->name = pdev->name;
+ input->id.bustype = BUS_HOST;
+ input->open = adc_joystick_open;
+ input->close = adc_joystick_close;
+
+ ret = adc_joystick_set_axes(dev, joy);
+ if (ret)
+ return ret;
+
+ input_set_drvdata(input, joy);
+ ret = input_register_device(input);
+ if (ret) {
+ dev_err(dev, "Unable to register input device: %d", ret);
+ return ret;
+ }
+
+ joy->buffer = iio_channel_get_all_cb(dev, adc_joystick_handle, joy);
+ if (IS_ERR(joy->buffer)) {
+ dev_err(dev, "Unable to allocate callback buffer");
+ return PTR_ERR(joy->buffer);
+ }
+
+ ret = devm_add_action_or_reset(dev, adc_joystick_cleanup, joy->buffer);
+ if (ret)
+ dev_err(dev, "Unable to add action");
+
+ return ret;
+}
+
+static const struct of_device_id adc_joystick_of_match[] = {
+ { .compatible = "adc-joystick", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, adc_joystick_of_match);
+
+static struct platform_driver adc_joystick_driver = {
+ .driver = {
+ .name = "adc-joystick",
+ .of_match_table = of_match_ptr(adc_joystick_of_match),
+ },
+ .probe = adc_joystick_probe,
+};
+module_platform_driver(adc_joystick_driver);
+
+MODULE_DESCRIPTION("Input driver for joysticks connected over ADC");
+MODULE_AUTHOR("Artur Rojek <[email protected]>");
+MODULE_LICENSE("GPL");
--
2.25.1

2020-03-04 16:44:53

by Rob Herring

[permalink] [raw]
Subject: Re: [PATCH v3 4/5] dt-bindings: input: Add docs for ADC driven joystick.

On Sun, Mar 01, 2020 at 04:09:19PM +0100, Artur Rojek wrote:
> Add documentation for the adc-joystick driver, used to provide support
> for joysticks connected over ADC.
>
> Signed-off-by: Artur Rojek <[email protected]>
> Tested-by: Paul Cercueil <[email protected]>
> ---
>
> Changes:
>
> v2: - Add `reg` property to axis subnode in order to enumerate the axes,
> - rename `linux,abs-code` property to `linux,code`,
> - drop `linux,` prefix from the remaining properties of axis subnode
>
> v3: no change
>
> .../bindings/input/adc-joystick.yaml | 117 ++++++++++++++++++
> 1 file changed, 117 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/input/adc-joystick.yaml
>
> diff --git a/Documentation/devicetree/bindings/input/adc-joystick.yaml b/Documentation/devicetree/bindings/input/adc-joystick.yaml
> new file mode 100644
> index 000000000000..91fc87dcbddb
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/input/adc-joystick.yaml
> @@ -0,0 +1,117 @@
> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> +# Copyright 2019-2020 Artur Rojek
> +%YAML 1.2
> +---
> +$id: "http://devicetree.org/schemas/bindings/input/adc-joystick.yaml#"

Drop 'bindings'.

> +$schema: "http://devicetree.org/meta-schemas/core.yaml#"
> +
> +title: ADC attached joystick
> +
> +maintainers:
> + - Artur Rojek <[email protected]>
> +
> +description: |
> + Bindings for joystick devices connected to ADC controllers supporting
> + the Industrial I/O subsystem.
> +
> +properties:
> + compatible:
> + const: adc-joystick
> +
> + io-channels:
> + description: |
> + List of phandle and IIO specifier pairs.
> + Each pair defines one ADC channel to which a joystick axis is connected.
> + See Documentation/devicetree/bindings/iio/iio-bindings.txt for details.

How many?

> +
> + '#address-cells':
> + const: 1
> +
> + '#size-cells':
> + const: 0
> +
> +required:
> + - compatible
> + - io-channels
> + - '#address-cells'
> + - '#size-cells'
> +
> +additionalProperties: false
> +
> +patternProperties:
> + "^axis@([0-9])$":

Up to 10 axis?

> + type: object
> + description: |
> + Represents a joystick axis bound to the given ADC channel.
> + For each entry in the io-channels list, one axis subnode with a matching
> + reg property must be specified.
> +
> + properties:
> + reg:
> + items:
> + description: Index of an io-channels list entry bound to this axis.

reg:
enum: [ 0, 1 ]

Or use minimum/maximum

> +
> + linux,code:
> + $ref: /schemas/types.yaml#/definitions/uint32
> + description: EV_ABS specific event code generated by the axis.
> +
> + abs-range:
> + $ref: /schemas/types.yaml#/definitions/uint32-array

Needs to be under an 'allOf'.

> + items:
> + - description: minimum value
> + - description: maximum value
> + description: |
> + Minimum and maximum values produced by the axis.
> + For an ABS_X axis this will be the left-most and right-most
> + inclination of the joystick. If min > max, it is left to userspace to
> + treat the axis as inverted.
> + This property is interpreted as two signed 32 bit values.
> +
> + abs-fuzz:
> + $ref: /schemas/types.yaml#/definitions/uint32
> + description: |
> + Amount of noise in the input value.
> + Omitting this property indicates the axis is precise.
> +
> + abs-flat:
> + $ref: /schemas/types.yaml#/definitions/uint32
> + description: |
> + Axial "deadzone", or area around the center position, where the axis
> + is considered to be at rest.
> + Omitting this property indicates the axis always returns to exactly
> + the center position.
> +
> + required:
> + - reg
> + - linux,code
> + - abs-range
> +
> + additionalProperties: false
> +
> +examples:
> + - |
> + #include <dt-bindings/iio/adc/ingenic,adc.h>
> + #include <dt-bindings/input/input.h>
> +
> + joystick: adc-joystick {
> + compatible = "adc-joystick";
> + io-channels = <&adc INGENIC_ADC_TOUCH_XP>,
> + <&adc INGENIC_ADC_TOUCH_YP>;
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + axis@0 {
> + reg = <0>;
> + linux,code = <ABS_X>;
> + abs-range = <3300 0>;
> + abs-fuzz = <4>;
> + abs-flat = <200>;
> + };
> + axis@1 {
> + reg = <1>;
> + linux,code = <ABS_Y>;
> + abs-range = <0 3300>;
> + abs-fuzz = <4>;
> + abs-flat = <200>;
> + };
> + };
> --
> 2.25.1
>

2020-03-07 12:48:28

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v3 3/5] IIO: Ingenic JZ47xx: Add touchscreen mode.

On Sun, 1 Mar 2020 16:09:18 +0100
Artur Rojek <[email protected]> wrote:

> The SADC component in JZ47xx SoCs provides support for touchscreen
> operations (pen position and pen down pressure) in single-ended and
> differential modes.
>
> Of the known hardware to use this controller, GCW Zero and Anbernic RG-350
> utilize the touchscreen mode by having their joystick(s) attached to the
> X/Y positive/negative input pins.
> GCW Zero comes with a single joystick and is sufficiently handled with the
> currently implemented single-ended mode. Support for boards with two
> joysticks, where one is hooked up to Xn/Yn and the other to Xp/Yp channels
> will need to be provided in the future.
>
> The touchscreen component of SADC takes a significant time to stabilize
> after first receiving the clock and a delay of 50ms has been empirically
> proven to be a safe value before data sampling can begin.
>
> Signed-off-by: Artur Rojek <[email protected]>
> Tested-by: Paul Cercueil <[email protected]>

This relies on the use of an irq that wasn't previously used. Was that
always hooked up? If not we need to still work as before when it
isn't provided.

Otherwise this looks fine to me.

> ---
>
> Changes:
>
> v2: - improve description of the touchscreen mode,
> - get rid of the unneeded kfifo,
> - drop IIO_BUFFER_CB from Kconfig,
> - remove extended names from the touchscreen channels
>
> v3: remove unneeded `linux/iio/kfifo_buf.h` include
>
> drivers/iio/adc/Kconfig | 1 +
> drivers/iio/adc/ingenic-adc.c | 109 +++++++++++++++++++++++++++++++++-
> 2 files changed, 108 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 82e33082958c..d3fd4b6e2d47 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -453,6 +453,7 @@ config INA2XX_ADC
> config INGENIC_ADC
> tristate "Ingenic JZ47xx SoCs ADC driver"
> depends on MIPS || COMPILE_TEST
> + select IIO_BUFFER
> help
> Say yes here to build support for the Ingenic JZ47xx SoCs ADC unit.
>
> diff --git a/drivers/iio/adc/ingenic-adc.c b/drivers/iio/adc/ingenic-adc.c
> index 7a24bc1dabe1..0dafc8d5d0d8 100644
> --- a/drivers/iio/adc/ingenic-adc.c
> +++ b/drivers/iio/adc/ingenic-adc.c
> @@ -8,7 +8,9 @@
>
> #include <dt-bindings/iio/adc/ingenic,adc.h>
> #include <linux/clk.h>
> +#include <linux/iio/buffer.h>
> #include <linux/iio/iio.h>
> +#include <linux/interrupt.h>
> #include <linux/io.h>
> #include <linux/iopoll.h>
> #include <linux/kernel.h>
> @@ -20,6 +22,8 @@
> #define JZ_ADC_REG_CFG 0x04
> #define JZ_ADC_REG_CTRL 0x08
> #define JZ_ADC_REG_STATUS 0x0c
> +#define JZ_ADC_REG_ADSAME 0x10
> +#define JZ_ADC_REG_ADWAIT 0x14
> #define JZ_ADC_REG_ADTCH 0x18
> #define JZ_ADC_REG_ADBDAT 0x1c
> #define JZ_ADC_REG_ADSDAT 0x20
> @@ -28,6 +32,9 @@
> #define JZ_ADC_REG_ENABLE_PD BIT(7)
> #define JZ_ADC_REG_CFG_AUX_MD (BIT(0) | BIT(1))
> #define JZ_ADC_REG_CFG_BAT_MD BIT(4)
> +#define JZ_ADC_REG_CFG_PULL_UP(n) ((n) << 16)
> +#define JZ_ADC_REG_CFG_SAMPLE_NUM(n) ((n) << 10)
> +#define JZ_ADC_REG_CFG_TOUCH_OPS_MASK (BIT(31) | GENMASK(23, 10))
> #define JZ_ADC_REG_ADCLK_CLKDIV_LSB 0
> #define JZ4725B_ADC_REG_ADCLK_CLKDIV10US_LSB 16
> #define JZ4770_ADC_REG_ADCLK_CLKDIV10US_LSB 8
> @@ -44,6 +51,14 @@
> #define JZ4770_ADC_BATTERY_VREF 6600
> #define JZ4770_ADC_BATTERY_VREF_BITS 12
>
> +#define JZ_ADC_IRQ_AUX BIT(0)
> +#define JZ_ADC_IRQ_BATTERY BIT(1)
> +#define JZ_ADC_IRQ_TOUCH BIT(2)
> +#define JZ_ADC_IRQ_PEN_DOWN BIT(3)
> +#define JZ_ADC_IRQ_PEN_UP BIT(4)
> +#define JZ_ADC_IRQ_PEN_DOWN_SLEEP BIT(5)
> +#define JZ_ADC_IRQ_SLEEP BIT(7)
> +
> struct ingenic_adc;
>
> struct ingenic_adc_soc_data {
> @@ -411,6 +426,28 @@ static const struct iio_info ingenic_adc_info = {
> };
>
> static const struct iio_chan_spec ingenic_channels[] = {
> + {
> + .type = IIO_POSITIONRELATIVE,
> + .indexed = 1,
> + .channel = INGENIC_ADC_TOUCH_XP,
> + .scan_index = 0,
> + .scan_type = {
> + .sign = 'u',
> + .realbits = 12,
> + .storagebits = 16
> + },
> + },
> + {
> + .type = IIO_POSITIONRELATIVE,
> + .indexed = 1,
> + .channel = INGENIC_ADC_TOUCH_YP,
> + .scan_index = 1,
> + .scan_type = {
> + .sign = 'u',
> + .realbits = 12,
> + .storagebits = 16
> + },
> + },
> {
> .extend_name = "aux",
> .type = IIO_VOLTAGE,
> @@ -418,6 +455,7 @@ static const struct iio_chan_spec ingenic_channels[] = {
> BIT(IIO_CHAN_INFO_SCALE),
> .indexed = 1,
> .channel = INGENIC_ADC_AUX,
> + .scan_index = -1
> },
> {
> .extend_name = "battery",
> @@ -428,6 +466,7 @@ static const struct iio_chan_spec ingenic_channels[] = {
> BIT(IIO_CHAN_INFO_SCALE),
> .indexed = 1,
> .channel = INGENIC_ADC_BATTERY,
> + .scan_index = -1
> },
> { /* Must always be last in the array. */
> .extend_name = "aux2",
> @@ -436,16 +475,69 @@ static const struct iio_chan_spec ingenic_channels[] = {
> BIT(IIO_CHAN_INFO_SCALE),
> .indexed = 1,
> .channel = INGENIC_ADC_AUX2,
> + .scan_index = -1
> },
> };
>
> +static int ingenic_adc_buffer_enable(struct iio_dev *iio_dev)
> +{
> + struct ingenic_adc *adc = iio_priv(iio_dev);
> +
> + clk_enable(adc->clk);
> + /* It takes significant time for the touchscreen hw to stabilize. */
> + msleep(50);
> + ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_TOUCH_OPS_MASK,
> + JZ_ADC_REG_CFG_SAMPLE_NUM(4) |
> + JZ_ADC_REG_CFG_PULL_UP(4));
> + writew(80, adc->base + JZ_ADC_REG_ADWAIT);
> + writew(2, adc->base + JZ_ADC_REG_ADSAME);
> + writeb((u8)~JZ_ADC_IRQ_TOUCH, adc->base + JZ_ADC_REG_CTRL);
> + writel(0, adc->base + JZ_ADC_REG_ADTCH);
> + ingenic_adc_enable(adc, 2, true);
> +
> + return 0;
> +}
> +
> +static int ingenic_adc_buffer_disable(struct iio_dev *iio_dev)
> +{
> + struct ingenic_adc *adc = iio_priv(iio_dev);
> +
> + ingenic_adc_enable(adc, 2, false);
> + writeb(0xff, adc->base + JZ_ADC_REG_CTRL);
> + writeb(0xff, adc->base + JZ_ADC_REG_STATUS);
> + ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_TOUCH_OPS_MASK, 0);
> + writew(0, adc->base + JZ_ADC_REG_ADSAME);
> + writew(0, adc->base + JZ_ADC_REG_ADWAIT);
> + clk_disable(adc->clk);
> +
> + return 0;
> +}
> +
> +static const struct iio_buffer_setup_ops ingenic_buffer_setup_ops = {
> + .postenable = &ingenic_adc_buffer_enable,
> + .predisable = &ingenic_adc_buffer_disable
> +};
> +
> +static irqreturn_t ingenic_adc_irq(int irq, void *data)
> +{
> + struct iio_dev *iio_dev = data;
> + struct ingenic_adc *adc = iio_priv(iio_dev);
> + u32 tdat;
> +
> + tdat = readl(adc->base + JZ_ADC_REG_ADTCH);
> + iio_push_to_buffers(iio_dev, &tdat);
> + writeb(JZ_ADC_IRQ_TOUCH, adc->base + JZ_ADC_REG_STATUS);
> +
> + return IRQ_HANDLED;
> +}
> +
> static int ingenic_adc_probe(struct platform_device *pdev)
> {
> struct device *dev = &pdev->dev;
> struct iio_dev *iio_dev;
> struct ingenic_adc *adc;
> const struct ingenic_adc_soc_data *soc_data;
> - int ret;
> + int irq, ret;
>
> soc_data = device_get_match_data(dev);
> if (!soc_data)
> @@ -460,6 +552,18 @@ static int ingenic_adc_probe(struct platform_device *pdev)
> mutex_init(&adc->aux_lock);
> adc->soc_data = soc_data;
>
> + irq = platform_get_irq(pdev, 0);

Do we need a fallback path if there is no irq provided? We can't break existing
supported devices that don't specify one.

> + if (irq < 0) {
> + dev_err(dev, "Failed to get irq: %d\n", irq);
> + return irq;
> + }
> + ret = devm_request_irq(dev, irq, ingenic_adc_irq, 0,
> + dev_name(dev), iio_dev);
> + if (ret < 0) {
> + dev_err(dev, "Failed to request irq: %d\n", ret);
> + return ret;
> + }
> +
> adc->base = devm_platform_ioremap_resource(pdev, 0);
> if (IS_ERR(adc->base))
> return PTR_ERR(adc->base);
> @@ -499,7 +603,8 @@ static int ingenic_adc_probe(struct platform_device *pdev)
>
> iio_dev->dev.parent = dev;
> iio_dev->name = "jz-adc";
> - iio_dev->modes = INDIO_DIRECT_MODE;
> + iio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
> + iio_dev->setup_ops = &ingenic_buffer_setup_ops;
> iio_dev->channels = ingenic_channels;
> iio_dev->num_channels = ARRAY_SIZE(ingenic_channels);
> /* Remove AUX2 from the list of supported channels. */

2020-03-07 12:51:04

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v3 5/5] input: joystick: Add ADC attached joystick driver.

On Sun, 1 Mar 2020 16:09:20 +0100
Artur Rojek <[email protected]> wrote:

> Add a driver for joystick devices connected to ADC controllers
> supporting the Industrial I/O subsystem.
>
> Signed-off-by: Artur Rojek <[email protected]>
> Tested-by: Paul Cercueil <[email protected]>
> Tested-by: Heiko Stuebner <[email protected]>
> Acked-by: Dmitry Torokhov <[email protected]>
Looks fine to me. Just those bindings to tidy up and the irq stuff
in the IIO side patch.

Jonathan

> ---
>
> Changes:
>
> v2: - sanity check supported channel format on probe,
> - rename adc_joystick_disable to a more sensible adc_joystick_cleanup,
> - enforce correct axis order by checking the `reg` property of
> child nodes
>
> v3: no change
>
> drivers/input/joystick/Kconfig | 10 ++
> drivers/input/joystick/Makefile | 1 +
> drivers/input/joystick/adc-joystick.c | 245 ++++++++++++++++++++++++++
> 3 files changed, 256 insertions(+)
> create mode 100644 drivers/input/joystick/adc-joystick.c
>
> diff --git a/drivers/input/joystick/Kconfig b/drivers/input/joystick/Kconfig
> index 940b744639c7..efbc20ec5099 100644
> --- a/drivers/input/joystick/Kconfig
> +++ b/drivers/input/joystick/Kconfig
> @@ -42,6 +42,16 @@ config JOYSTICK_A3D
> To compile this driver as a module, choose M here: the
> module will be called a3d.
>
> +config JOYSTICK_ADC
> + tristate "Simple joystick connected over ADC"
> + depends on IIO
> + select IIO_BUFFER_CB
> + help
> + Say Y here if you have a simple joystick connected over ADC.
> +
> + To compile this driver as a module, choose M here: the
> + module will be called adc-joystick.
> +
> config JOYSTICK_ADI
> tristate "Logitech ADI digital joysticks and gamepads"
> select GAMEPORT
> diff --git a/drivers/input/joystick/Makefile b/drivers/input/joystick/Makefile
> index 8656023f6ef5..58232b3057d3 100644
> --- a/drivers/input/joystick/Makefile
> +++ b/drivers/input/joystick/Makefile
> @@ -6,6 +6,7 @@
> # Each configuration option enables a list of files.
>
> obj-$(CONFIG_JOYSTICK_A3D) += a3d.o
> +obj-$(CONFIG_JOYSTICK_ADC) += adc-joystick.o
> obj-$(CONFIG_JOYSTICK_ADI) += adi.o
> obj-$(CONFIG_JOYSTICK_AMIGA) += amijoy.o
> obj-$(CONFIG_JOYSTICK_AS5011) += as5011.o
> diff --git a/drivers/input/joystick/adc-joystick.c b/drivers/input/joystick/adc-joystick.c
> new file mode 100644
> index 000000000000..9cb9896da26e
> --- /dev/null
> +++ b/drivers/input/joystick/adc-joystick.c
> @@ -0,0 +1,245 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Input driver for joysticks connected over ADC.
> + * Copyright (c) 2019-2020 Artur Rojek <[email protected]>
> + */
> +#include <linux/ctype.h>
> +#include <linux/input.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/consumer.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/property.h>
> +
> +struct adc_joystick_axis {
> + u32 code;
> + s32 range[2];
> + s32 fuzz;
> + s32 flat;
> +};
> +
> +struct adc_joystick {
> + struct input_dev *input;
> + struct iio_cb_buffer *buffer;
> + struct adc_joystick_axis *axes;
> + struct iio_channel *chans;
> + int num_chans;
> +};
> +
> +static int adc_joystick_handle(const void *data, void *private)
> +{
> + struct adc_joystick *joy = private;
> + enum iio_endian endianness;
> + int bytes, msb, val, i;
> + bool sign;
> +
> + bytes = joy->chans[0].channel->scan_type.storagebits >> 3;
> +
> + for (i = 0; i < joy->num_chans; ++i) {
> + endianness = joy->chans[i].channel->scan_type.endianness;
> + msb = joy->chans[i].channel->scan_type.realbits - 1;
> + sign = (tolower(joy->chans[i].channel->scan_type.sign) == 's');
> +
> + switch (bytes) {
> + case 1:
> + val = ((const u8 *)data)[i];
> + break;
> + case 2:
> + val = ((const u16 *)data)[i];
> + if (endianness == IIO_BE)
> + val = be16_to_cpu(val);
> + else if (endianness == IIO_LE)
> + val = le16_to_cpu(val);
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + val >>= joy->chans[i].channel->scan_type.shift;
> + if (sign)
> + val = sign_extend32(val, msb);
> + else
> + val &= GENMASK(msb, 0);
> + input_report_abs(joy->input, joy->axes[i].code, val);
> + }
> +
> + input_sync(joy->input);
> +
> + return 0;
> +}
> +
> +static int adc_joystick_open(struct input_dev *dev)
> +{
> + struct adc_joystick *joy = input_get_drvdata(dev);
> + int ret;
> +
> + ret = iio_channel_start_all_cb(joy->buffer);
> + if (ret)
> + dev_err(dev->dev.parent, "Unable to start callback buffer");
> +
> + return ret;
> +}
> +
> +static void adc_joystick_close(struct input_dev *dev)
> +{
> + struct adc_joystick *joy = input_get_drvdata(dev);
> +
> + iio_channel_stop_all_cb(joy->buffer);
> +}
> +
> +static void adc_joystick_cleanup(void *data)
> +{
> + iio_channel_release_all_cb(data);
> +}
> +
> +static int adc_joystick_set_axes(struct device *dev, struct adc_joystick *joy)
> +{
> + struct adc_joystick_axis *axes;
> + struct fwnode_handle *child;
> + int num_axes, ret, i;
> +
> + num_axes = device_get_child_node_count(dev);
> + if (!num_axes) {
> + dev_err(dev, "Unable to find child nodes");
> + return -EINVAL;
> + }
> +
> + if (num_axes != joy->num_chans) {
> + dev_err(dev, "Got %d child nodes for %d channels",
> + num_axes, joy->num_chans);
> + return -EINVAL;
> + }
> +
> + axes = devm_kmalloc_array(dev, num_axes, sizeof(*axes), GFP_KERNEL);
> + if (!axes)
> + return -ENOMEM;
> +
> + device_for_each_child_node(dev, child) {
> + ret = fwnode_property_read_u32(child, "reg", &i);
> + if (ret || i >= num_axes) {
> + dev_err(dev, "reg invalid or missing");
> + goto err;
> + }
> +
> + if (fwnode_property_read_u32(child, "linux,code",
> + &axes[i].code)) {
> + dev_err(dev, "linux,code invalid or missing");
> + goto err;
> + }
> +
> + if (fwnode_property_read_u32_array(child, "abs-range",
> + axes[i].range, 2)) {
> + dev_err(dev, "abs-range invalid or missing");
> + goto err;
> + }
> +
> + fwnode_property_read_u32(child, "abs-fuzz",
> + &axes[i].fuzz);
> + fwnode_property_read_u32(child, "abs-flat",
> + &axes[i].flat);
> +
> + input_set_abs_params(joy->input, axes[i].code,
> + axes[i].range[0], axes[i].range[1],
> + axes[i].fuzz,
> + axes[i].flat);
> + input_set_capability(joy->input, EV_ABS, axes[i].code);
> + }
> +
> + joy->axes = axes;
> +
> + return 0;
> +
> +err:
> + fwnode_handle_put(child);
> + return -EINVAL;
> +}
> +
> +static int adc_joystick_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct adc_joystick *joy;
> + struct input_dev *input;
> + int bits, ret, i;
> +
> + joy = devm_kzalloc(dev, sizeof(*joy), GFP_KERNEL);
> + if (!joy)
> + return -ENOMEM;
> +
> + joy->chans = devm_iio_channel_get_all(dev);
> + if (IS_ERR(joy->chans)) {
> + ret = PTR_ERR(joy->chans);
> + if (ret != -EPROBE_DEFER)
> + dev_err(dev, "Unable to get IIO channels");
> + return ret;
> + }
> +
> + /* Count how many channels we got. NULL terminated. */
> + while (joy->chans[joy->num_chans].indio_dev)
> + joy->num_chans++;
> +
> + bits = joy->chans[0].channel->scan_type.storagebits;
> + if (!bits || (bits >> 3) > 2) {
> + dev_err(dev, "Unsupported channel storage size");
> + return -EINVAL;
> + }
> + for (i = 1; i < joy->num_chans; ++i)
> + if (joy->chans[i].channel->scan_type.storagebits != bits) {
> + dev_err(dev, "Channels must have equal storage size");
> + return -EINVAL;
> + }
> +
> + input = devm_input_allocate_device(dev);
> + if (!input) {
> + dev_err(dev, "Unable to allocate input device");
> + return -ENOMEM;
> + }
> +
> + joy->input = input;
> + input->name = pdev->name;
> + input->id.bustype = BUS_HOST;
> + input->open = adc_joystick_open;
> + input->close = adc_joystick_close;
> +
> + ret = adc_joystick_set_axes(dev, joy);
> + if (ret)
> + return ret;
> +
> + input_set_drvdata(input, joy);
> + ret = input_register_device(input);
> + if (ret) {
> + dev_err(dev, "Unable to register input device: %d", ret);
> + return ret;
> + }
> +
> + joy->buffer = iio_channel_get_all_cb(dev, adc_joystick_handle, joy);
> + if (IS_ERR(joy->buffer)) {
> + dev_err(dev, "Unable to allocate callback buffer");
> + return PTR_ERR(joy->buffer);
> + }
> +
> + ret = devm_add_action_or_reset(dev, adc_joystick_cleanup, joy->buffer);
> + if (ret)
> + dev_err(dev, "Unable to add action");
> +
> + return ret;
> +}
> +
> +static const struct of_device_id adc_joystick_of_match[] = {
> + { .compatible = "adc-joystick", },
> + { },
> +};
> +MODULE_DEVICE_TABLE(of, adc_joystick_of_match);
> +
> +static struct platform_driver adc_joystick_driver = {
> + .driver = {
> + .name = "adc-joystick",
> + .of_match_table = of_match_ptr(adc_joystick_of_match),
> + },
> + .probe = adc_joystick_probe,
> +};
> +module_platform_driver(adc_joystick_driver);
> +
> +MODULE_DESCRIPTION("Input driver for joysticks connected over ADC");
> +MODULE_AUTHOR("Artur Rojek <[email protected]>");
> +MODULE_LICENSE("GPL");

2020-03-07 13:01:47

by Paul Cercueil

[permalink] [raw]
Subject: Re: [PATCH v3 3/5] IIO: Ingenic JZ47xx: Add touchscreen mode.

Hi Jonathan,


Le sam., mars 7, 2020 at 12:47, Jonathan Cameron
<[email protected]> a ?crit :
> On Sun, 1 Mar 2020 16:09:18 +0100
> Artur Rojek <[email protected]> wrote:
>
>> The SADC component in JZ47xx SoCs provides support for touchscreen
>> operations (pen position and pen down pressure) in single-ended and
>> differential modes.
>>
>> Of the known hardware to use this controller, GCW Zero and Anbernic
>> RG-350
>> utilize the touchscreen mode by having their joystick(s) attached
>> to the
>> X/Y positive/negative input pins.
>> GCW Zero comes with a single joystick and is sufficiently handled
>> with the
>> currently implemented single-ended mode. Support for boards with two
>> joysticks, where one is hooked up to Xn/Yn and the other to Xp/Yp
>> channels
>> will need to be provided in the future.
>>
>> The touchscreen component of SADC takes a significant time to
>> stabilize
>> after first receiving the clock and a delay of 50ms has been
>> empirically
>> proven to be a safe value before data sampling can begin.
>>
>> Signed-off-by: Artur Rojek <[email protected]>
>> Tested-by: Paul Cercueil <[email protected]>
>
> This relies on the use of an irq that wasn't previously used. Was
> that
> always hooked up? If not we need to still work as before when it
> isn't provided.
>
> Otherwise this looks fine to me.

All the boards that probe the ingenic-adc driver have the interrupt
provided from devicetree, yes.

Cheers,
-Paul

>
>> ---
>>
>> Changes:
>>
>> v2: - improve description of the touchscreen mode,
>> - get rid of the unneeded kfifo,
>> - drop IIO_BUFFER_CB from Kconfig,
>> - remove extended names from the touchscreen channels
>>
>> v3: remove unneeded `linux/iio/kfifo_buf.h` include
>>
>> drivers/iio/adc/Kconfig | 1 +
>> drivers/iio/adc/ingenic-adc.c | 109
>> +++++++++++++++++++++++++++++++++-
>> 2 files changed, 108 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>> index 82e33082958c..d3fd4b6e2d47 100644
>> --- a/drivers/iio/adc/Kconfig
>> +++ b/drivers/iio/adc/Kconfig
>> @@ -453,6 +453,7 @@ config INA2XX_ADC
>> config INGENIC_ADC
>> tristate "Ingenic JZ47xx SoCs ADC driver"
>> depends on MIPS || COMPILE_TEST
>> + select IIO_BUFFER
>> help
>> Say yes here to build support for the Ingenic JZ47xx SoCs ADC
>> unit.
>>
>> diff --git a/drivers/iio/adc/ingenic-adc.c
>> b/drivers/iio/adc/ingenic-adc.c
>> index 7a24bc1dabe1..0dafc8d5d0d8 100644
>> --- a/drivers/iio/adc/ingenic-adc.c
>> +++ b/drivers/iio/adc/ingenic-adc.c
>> @@ -8,7 +8,9 @@
>>
>> #include <dt-bindings/iio/adc/ingenic,adc.h>
>> #include <linux/clk.h>
>> +#include <linux/iio/buffer.h>
>> #include <linux/iio/iio.h>
>> +#include <linux/interrupt.h>
>> #include <linux/io.h>
>> #include <linux/iopoll.h>
>> #include <linux/kernel.h>
>> @@ -20,6 +22,8 @@
>> #define JZ_ADC_REG_CFG 0x04
>> #define JZ_ADC_REG_CTRL 0x08
>> #define JZ_ADC_REG_STATUS 0x0c
>> +#define JZ_ADC_REG_ADSAME 0x10
>> +#define JZ_ADC_REG_ADWAIT 0x14
>> #define JZ_ADC_REG_ADTCH 0x18
>> #define JZ_ADC_REG_ADBDAT 0x1c
>> #define JZ_ADC_REG_ADSDAT 0x20
>> @@ -28,6 +32,9 @@
>> #define JZ_ADC_REG_ENABLE_PD BIT(7)
>> #define JZ_ADC_REG_CFG_AUX_MD (BIT(0) | BIT(1))
>> #define JZ_ADC_REG_CFG_BAT_MD BIT(4)
>> +#define JZ_ADC_REG_CFG_PULL_UP(n) ((n) << 16)
>> +#define JZ_ADC_REG_CFG_SAMPLE_NUM(n) ((n) << 10)
>> +#define JZ_ADC_REG_CFG_TOUCH_OPS_MASK (BIT(31) | GENMASK(23, 10))
>> #define JZ_ADC_REG_ADCLK_CLKDIV_LSB 0
>> #define JZ4725B_ADC_REG_ADCLK_CLKDIV10US_LSB 16
>> #define JZ4770_ADC_REG_ADCLK_CLKDIV10US_LSB 8
>> @@ -44,6 +51,14 @@
>> #define JZ4770_ADC_BATTERY_VREF 6600
>> #define JZ4770_ADC_BATTERY_VREF_BITS 12
>>
>> +#define JZ_ADC_IRQ_AUX BIT(0)
>> +#define JZ_ADC_IRQ_BATTERY BIT(1)
>> +#define JZ_ADC_IRQ_TOUCH BIT(2)
>> +#define JZ_ADC_IRQ_PEN_DOWN BIT(3)
>> +#define JZ_ADC_IRQ_PEN_UP BIT(4)
>> +#define JZ_ADC_IRQ_PEN_DOWN_SLEEP BIT(5)
>> +#define JZ_ADC_IRQ_SLEEP BIT(7)
>> +
>> struct ingenic_adc;
>>
>> struct ingenic_adc_soc_data {
>> @@ -411,6 +426,28 @@ static const struct iio_info ingenic_adc_info
>> = {
>> };
>>
>> static const struct iio_chan_spec ingenic_channels[] = {
>> + {
>> + .type = IIO_POSITIONRELATIVE,
>> + .indexed = 1,
>> + .channel = INGENIC_ADC_TOUCH_XP,
>> + .scan_index = 0,
>> + .scan_type = {
>> + .sign = 'u',
>> + .realbits = 12,
>> + .storagebits = 16
>> + },
>> + },
>> + {
>> + .type = IIO_POSITIONRELATIVE,
>> + .indexed = 1,
>> + .channel = INGENIC_ADC_TOUCH_YP,
>> + .scan_index = 1,
>> + .scan_type = {
>> + .sign = 'u',
>> + .realbits = 12,
>> + .storagebits = 16
>> + },
>> + },
>> {
>> .extend_name = "aux",
>> .type = IIO_VOLTAGE,
>> @@ -418,6 +455,7 @@ static const struct iio_chan_spec
>> ingenic_channels[] = {
>> BIT(IIO_CHAN_INFO_SCALE),
>> .indexed = 1,
>> .channel = INGENIC_ADC_AUX,
>> + .scan_index = -1
>> },
>> {
>> .extend_name = "battery",
>> @@ -428,6 +466,7 @@ static const struct iio_chan_spec
>> ingenic_channels[] = {
>> BIT(IIO_CHAN_INFO_SCALE),
>> .indexed = 1,
>> .channel = INGENIC_ADC_BATTERY,
>> + .scan_index = -1
>> },
>> { /* Must always be last in the array. */
>> .extend_name = "aux2",
>> @@ -436,16 +475,69 @@ static const struct iio_chan_spec
>> ingenic_channels[] = {
>> BIT(IIO_CHAN_INFO_SCALE),
>> .indexed = 1,
>> .channel = INGENIC_ADC_AUX2,
>> + .scan_index = -1
>> },
>> };
>>
>> +static int ingenic_adc_buffer_enable(struct iio_dev *iio_dev)
>> +{
>> + struct ingenic_adc *adc = iio_priv(iio_dev);
>> +
>> + clk_enable(adc->clk);
>> + /* It takes significant time for the touchscreen hw to stabilize.
>> */
>> + msleep(50);
>> + ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_TOUCH_OPS_MASK,
>> + JZ_ADC_REG_CFG_SAMPLE_NUM(4) |
>> + JZ_ADC_REG_CFG_PULL_UP(4));
>> + writew(80, adc->base + JZ_ADC_REG_ADWAIT);
>> + writew(2, adc->base + JZ_ADC_REG_ADSAME);
>> + writeb((u8)~JZ_ADC_IRQ_TOUCH, adc->base + JZ_ADC_REG_CTRL);
>> + writel(0, adc->base + JZ_ADC_REG_ADTCH);
>> + ingenic_adc_enable(adc, 2, true);
>> +
>> + return 0;
>> +}
>> +
>> +static int ingenic_adc_buffer_disable(struct iio_dev *iio_dev)
>> +{
>> + struct ingenic_adc *adc = iio_priv(iio_dev);
>> +
>> + ingenic_adc_enable(adc, 2, false);
>> + writeb(0xff, adc->base + JZ_ADC_REG_CTRL);
>> + writeb(0xff, adc->base + JZ_ADC_REG_STATUS);
>> + ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_TOUCH_OPS_MASK, 0);
>> + writew(0, adc->base + JZ_ADC_REG_ADSAME);
>> + writew(0, adc->base + JZ_ADC_REG_ADWAIT);
>> + clk_disable(adc->clk);
>> +
>> + return 0;
>> +}
>> +
>> +static const struct iio_buffer_setup_ops ingenic_buffer_setup_ops
>> = {
>> + .postenable = &ingenic_adc_buffer_enable,
>> + .predisable = &ingenic_adc_buffer_disable
>> +};
>> +
>> +static irqreturn_t ingenic_adc_irq(int irq, void *data)
>> +{
>> + struct iio_dev *iio_dev = data;
>> + struct ingenic_adc *adc = iio_priv(iio_dev);
>> + u32 tdat;
>> +
>> + tdat = readl(adc->base + JZ_ADC_REG_ADTCH);
>> + iio_push_to_buffers(iio_dev, &tdat);
>> + writeb(JZ_ADC_IRQ_TOUCH, adc->base + JZ_ADC_REG_STATUS);
>> +
>> + return IRQ_HANDLED;
>> +}
>> +
>> static int ingenic_adc_probe(struct platform_device *pdev)
>> {
>> struct device *dev = &pdev->dev;
>> struct iio_dev *iio_dev;
>> struct ingenic_adc *adc;
>> const struct ingenic_adc_soc_data *soc_data;
>> - int ret;
>> + int irq, ret;
>>
>> soc_data = device_get_match_data(dev);
>> if (!soc_data)
>> @@ -460,6 +552,18 @@ static int ingenic_adc_probe(struct
>> platform_device *pdev)
>> mutex_init(&adc->aux_lock);
>> adc->soc_data = soc_data;
>>
>> + irq = platform_get_irq(pdev, 0);
>
> Do we need a fallback path if there is no irq provided? We can't
> break existing
> supported devices that don't specify one.
>
>> + if (irq < 0) {
>> + dev_err(dev, "Failed to get irq: %d\n", irq);
>> + return irq;
>> + }
>> + ret = devm_request_irq(dev, irq, ingenic_adc_irq, 0,
>> + dev_name(dev), iio_dev);
>> + if (ret < 0) {
>> + dev_err(dev, "Failed to request irq: %d\n", ret);
>> + return ret;
>> + }
>> +
>> adc->base = devm_platform_ioremap_resource(pdev, 0);
>> if (IS_ERR(adc->base))
>> return PTR_ERR(adc->base);
>> @@ -499,7 +603,8 @@ static int ingenic_adc_probe(struct
>> platform_device *pdev)
>>
>> iio_dev->dev.parent = dev;
>> iio_dev->name = "jz-adc";
>> - iio_dev->modes = INDIO_DIRECT_MODE;
>> + iio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
>> + iio_dev->setup_ops = &ingenic_buffer_setup_ops;
>> iio_dev->channels = ingenic_channels;
>> iio_dev->num_channels = ARRAY_SIZE(ingenic_channels);
>> /* Remove AUX2 from the list of supported channels. */
>


2020-03-07 13:14:16

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v3 3/5] IIO: Ingenic JZ47xx: Add touchscreen mode.

On Sat, 07 Mar 2020 13:58:40 +0100
Paul Cercueil <[email protected]> wrote:

> Hi Jonathan,
>
>
> Le sam., mars 7, 2020 at 12:47, Jonathan Cameron
> <[email protected]> a écrit :
> > On Sun, 1 Mar 2020 16:09:18 +0100
> > Artur Rojek <[email protected]> wrote:
> >
> >> The SADC component in JZ47xx SoCs provides support for touchscreen
> >> operations (pen position and pen down pressure) in single-ended and
> >> differential modes.
> >>
> >> Of the known hardware to use this controller, GCW Zero and Anbernic
> >> RG-350
> >> utilize the touchscreen mode by having their joystick(s) attached
> >> to the
> >> X/Y positive/negative input pins.
> >> GCW Zero comes with a single joystick and is sufficiently handled
> >> with the
> >> currently implemented single-ended mode. Support for boards with two
> >> joysticks, where one is hooked up to Xn/Yn and the other to Xp/Yp
> >> channels
> >> will need to be provided in the future.
> >>
> >> The touchscreen component of SADC takes a significant time to
> >> stabilize
> >> after first receiving the clock and a delay of 50ms has been
> >> empirically
> >> proven to be a safe value before data sampling can begin.
> >>
> >> Signed-off-by: Artur Rojek <[email protected]>
> >> Tested-by: Paul Cercueil <[email protected]>
> >
> > This relies on the use of an irq that wasn't previously used. Was
> > that
> > always hooked up? If not we need to still work as before when it
> > isn't provided.
> >
> > Otherwise this looks fine to me.
>
> All the boards that probe the ingenic-adc driver have the interrupt
> provided from devicetree, yes.
Great :)

No problem then. Perhaps add a note to the patch description to say
that so no one else wonders in future.

Thanks,

Jonathan


>
> Cheers,
> -Paul
>
> >
> >> ---
> >>
> >> Changes:
> >>
> >> v2: - improve description of the touchscreen mode,
> >> - get rid of the unneeded kfifo,
> >> - drop IIO_BUFFER_CB from Kconfig,
> >> - remove extended names from the touchscreen channels
> >>
> >> v3: remove unneeded `linux/iio/kfifo_buf.h` include
> >>
> >> drivers/iio/adc/Kconfig | 1 +
> >> drivers/iio/adc/ingenic-adc.c | 109
> >> +++++++++++++++++++++++++++++++++-
> >> 2 files changed, 108 insertions(+), 2 deletions(-)
> >>
> >> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> >> index 82e33082958c..d3fd4b6e2d47 100644
> >> --- a/drivers/iio/adc/Kconfig
> >> +++ b/drivers/iio/adc/Kconfig
> >> @@ -453,6 +453,7 @@ config INA2XX_ADC
> >> config INGENIC_ADC
> >> tristate "Ingenic JZ47xx SoCs ADC driver"
> >> depends on MIPS || COMPILE_TEST
> >> + select IIO_BUFFER
> >> help
> >> Say yes here to build support for the Ingenic JZ47xx SoCs ADC
> >> unit.
> >>
> >> diff --git a/drivers/iio/adc/ingenic-adc.c
> >> b/drivers/iio/adc/ingenic-adc.c
> >> index 7a24bc1dabe1..0dafc8d5d0d8 100644
> >> --- a/drivers/iio/adc/ingenic-adc.c
> >> +++ b/drivers/iio/adc/ingenic-adc.c
> >> @@ -8,7 +8,9 @@
> >>
> >> #include <dt-bindings/iio/adc/ingenic,adc.h>
> >> #include <linux/clk.h>
> >> +#include <linux/iio/buffer.h>
> >> #include <linux/iio/iio.h>
> >> +#include <linux/interrupt.h>
> >> #include <linux/io.h>
> >> #include <linux/iopoll.h>
> >> #include <linux/kernel.h>
> >> @@ -20,6 +22,8 @@
> >> #define JZ_ADC_REG_CFG 0x04
> >> #define JZ_ADC_REG_CTRL 0x08
> >> #define JZ_ADC_REG_STATUS 0x0c
> >> +#define JZ_ADC_REG_ADSAME 0x10
> >> +#define JZ_ADC_REG_ADWAIT 0x14
> >> #define JZ_ADC_REG_ADTCH 0x18
> >> #define JZ_ADC_REG_ADBDAT 0x1c
> >> #define JZ_ADC_REG_ADSDAT 0x20
> >> @@ -28,6 +32,9 @@
> >> #define JZ_ADC_REG_ENABLE_PD BIT(7)
> >> #define JZ_ADC_REG_CFG_AUX_MD (BIT(0) | BIT(1))
> >> #define JZ_ADC_REG_CFG_BAT_MD BIT(4)
> >> +#define JZ_ADC_REG_CFG_PULL_UP(n) ((n) << 16)
> >> +#define JZ_ADC_REG_CFG_SAMPLE_NUM(n) ((n) << 10)
> >> +#define JZ_ADC_REG_CFG_TOUCH_OPS_MASK (BIT(31) | GENMASK(23, 10))
> >> #define JZ_ADC_REG_ADCLK_CLKDIV_LSB 0
> >> #define JZ4725B_ADC_REG_ADCLK_CLKDIV10US_LSB 16
> >> #define JZ4770_ADC_REG_ADCLK_CLKDIV10US_LSB 8
> >> @@ -44,6 +51,14 @@
> >> #define JZ4770_ADC_BATTERY_VREF 6600
> >> #define JZ4770_ADC_BATTERY_VREF_BITS 12
> >>
> >> +#define JZ_ADC_IRQ_AUX BIT(0)
> >> +#define JZ_ADC_IRQ_BATTERY BIT(1)
> >> +#define JZ_ADC_IRQ_TOUCH BIT(2)
> >> +#define JZ_ADC_IRQ_PEN_DOWN BIT(3)
> >> +#define JZ_ADC_IRQ_PEN_UP BIT(4)
> >> +#define JZ_ADC_IRQ_PEN_DOWN_SLEEP BIT(5)
> >> +#define JZ_ADC_IRQ_SLEEP BIT(7)
> >> +
> >> struct ingenic_adc;
> >>
> >> struct ingenic_adc_soc_data {
> >> @@ -411,6 +426,28 @@ static const struct iio_info ingenic_adc_info
> >> = {
> >> };
> >>
> >> static const struct iio_chan_spec ingenic_channels[] = {
> >> + {
> >> + .type = IIO_POSITIONRELATIVE,
> >> + .indexed = 1,
> >> + .channel = INGENIC_ADC_TOUCH_XP,
> >> + .scan_index = 0,
> >> + .scan_type = {
> >> + .sign = 'u',
> >> + .realbits = 12,
> >> + .storagebits = 16
> >> + },
> >> + },
> >> + {
> >> + .type = IIO_POSITIONRELATIVE,
> >> + .indexed = 1,
> >> + .channel = INGENIC_ADC_TOUCH_YP,
> >> + .scan_index = 1,
> >> + .scan_type = {
> >> + .sign = 'u',
> >> + .realbits = 12,
> >> + .storagebits = 16
> >> + },
> >> + },
> >> {
> >> .extend_name = "aux",
> >> .type = IIO_VOLTAGE,
> >> @@ -418,6 +455,7 @@ static const struct iio_chan_spec
> >> ingenic_channels[] = {
> >> BIT(IIO_CHAN_INFO_SCALE),
> >> .indexed = 1,
> >> .channel = INGENIC_ADC_AUX,
> >> + .scan_index = -1
> >> },
> >> {
> >> .extend_name = "battery",
> >> @@ -428,6 +466,7 @@ static const struct iio_chan_spec
> >> ingenic_channels[] = {
> >> BIT(IIO_CHAN_INFO_SCALE),
> >> .indexed = 1,
> >> .channel = INGENIC_ADC_BATTERY,
> >> + .scan_index = -1
> >> },
> >> { /* Must always be last in the array. */
> >> .extend_name = "aux2",
> >> @@ -436,16 +475,69 @@ static const struct iio_chan_spec
> >> ingenic_channels[] = {
> >> BIT(IIO_CHAN_INFO_SCALE),
> >> .indexed = 1,
> >> .channel = INGENIC_ADC_AUX2,
> >> + .scan_index = -1
> >> },
> >> };
> >>
> >> +static int ingenic_adc_buffer_enable(struct iio_dev *iio_dev)
> >> +{
> >> + struct ingenic_adc *adc = iio_priv(iio_dev);
> >> +
> >> + clk_enable(adc->clk);
> >> + /* It takes significant time for the touchscreen hw to stabilize.
> >> */
> >> + msleep(50);
> >> + ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_TOUCH_OPS_MASK,
> >> + JZ_ADC_REG_CFG_SAMPLE_NUM(4) |
> >> + JZ_ADC_REG_CFG_PULL_UP(4));
> >> + writew(80, adc->base + JZ_ADC_REG_ADWAIT);
> >> + writew(2, adc->base + JZ_ADC_REG_ADSAME);
> >> + writeb((u8)~JZ_ADC_IRQ_TOUCH, adc->base + JZ_ADC_REG_CTRL);
> >> + writel(0, adc->base + JZ_ADC_REG_ADTCH);
> >> + ingenic_adc_enable(adc, 2, true);
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static int ingenic_adc_buffer_disable(struct iio_dev *iio_dev)
> >> +{
> >> + struct ingenic_adc *adc = iio_priv(iio_dev);
> >> +
> >> + ingenic_adc_enable(adc, 2, false);
> >> + writeb(0xff, adc->base + JZ_ADC_REG_CTRL);
> >> + writeb(0xff, adc->base + JZ_ADC_REG_STATUS);
> >> + ingenic_adc_set_config(adc, JZ_ADC_REG_CFG_TOUCH_OPS_MASK, 0);
> >> + writew(0, adc->base + JZ_ADC_REG_ADSAME);
> >> + writew(0, adc->base + JZ_ADC_REG_ADWAIT);
> >> + clk_disable(adc->clk);
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static const struct iio_buffer_setup_ops ingenic_buffer_setup_ops
> >> = {
> >> + .postenable = &ingenic_adc_buffer_enable,
> >> + .predisable = &ingenic_adc_buffer_disable
> >> +};
> >> +
> >> +static irqreturn_t ingenic_adc_irq(int irq, void *data)
> >> +{
> >> + struct iio_dev *iio_dev = data;
> >> + struct ingenic_adc *adc = iio_priv(iio_dev);
> >> + u32 tdat;
> >> +
> >> + tdat = readl(adc->base + JZ_ADC_REG_ADTCH);
> >> + iio_push_to_buffers(iio_dev, &tdat);
> >> + writeb(JZ_ADC_IRQ_TOUCH, adc->base + JZ_ADC_REG_STATUS);
> >> +
> >> + return IRQ_HANDLED;
> >> +}
> >> +
> >> static int ingenic_adc_probe(struct platform_device *pdev)
> >> {
> >> struct device *dev = &pdev->dev;
> >> struct iio_dev *iio_dev;
> >> struct ingenic_adc *adc;
> >> const struct ingenic_adc_soc_data *soc_data;
> >> - int ret;
> >> + int irq, ret;
> >>
> >> soc_data = device_get_match_data(dev);
> >> if (!soc_data)
> >> @@ -460,6 +552,18 @@ static int ingenic_adc_probe(struct
> >> platform_device *pdev)
> >> mutex_init(&adc->aux_lock);
> >> adc->soc_data = soc_data;
> >>
> >> + irq = platform_get_irq(pdev, 0);
> >
> > Do we need a fallback path if there is no irq provided? We can't
> > break existing
> > supported devices that don't specify one.
> >
> >> + if (irq < 0) {
> >> + dev_err(dev, "Failed to get irq: %d\n", irq);
> >> + return irq;
> >> + }
> >> + ret = devm_request_irq(dev, irq, ingenic_adc_irq, 0,
> >> + dev_name(dev), iio_dev);
> >> + if (ret < 0) {
> >> + dev_err(dev, "Failed to request irq: %d\n", ret);
> >> + return ret;
> >> + }
> >> +
> >> adc->base = devm_platform_ioremap_resource(pdev, 0);
> >> if (IS_ERR(adc->base))
> >> return PTR_ERR(adc->base);
> >> @@ -499,7 +603,8 @@ static int ingenic_adc_probe(struct
> >> platform_device *pdev)
> >>
> >> iio_dev->dev.parent = dev;
> >> iio_dev->name = "jz-adc";
> >> - iio_dev->modes = INDIO_DIRECT_MODE;
> >> + iio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
> >> + iio_dev->setup_ops = &ingenic_buffer_setup_ops;
> >> iio_dev->channels = ingenic_channels;
> >> iio_dev->num_channels = ARRAY_SIZE(ingenic_channels);
> >> /* Remove AUX2 from the list of supported channels. */
> >
>
>