[PATCH v6 0/11] Output raw touch data via V4L2

STALE3718d

Revision v6 of 3 in this series.

17 messages, 4 authors, 2016-08-05 · open the first message on its own page

[PATCH v6 0/11] Output raw touch data via V4L2

From: Nick Dyer <nick@shmanahar.org>
Date: 2016-06-30 17:46:52

This is a series of patches to add output of raw touch diagnostic data via V4L2
to the Atmel maXTouch and Synaptics RMI4 drivers.

It's a rewrite of the previous implementation which output via debugfs: it now
uses a V4L2 device in a similar way to the sur40 driver.

We have a utility which can read the data and display it in a useful format:
    https://github.com/ndyer/heatmap/commits/heatmap-v4l

These patches are also available from
    https://github.com/ndyer/linux/commits/v4l-touch-2016-06-30

I will also send a patch to update v4l2-compliance.

Changes in v6:
- Remove BUF_TYPE_TOUCH_CAPTURE, as discussed with Hans V touch devices will
  use BUF_TYPE_VIDEO_CAPTURE.
- Touch devices should now register CAP_VIDEO_CAPTURE: CAP_TOUCH just says that
  this is a touch device, not a video device, but otherwise it acts the same.
- Add some code to v4l_s_fmt() to set sensible default values for fields not
  used by touch.
- Improve naming/doc of RMI4 F54 report types.
- Various minor DocBook fixes, and split to separate patch.
- Update my email address.
- Rework sur40 changes so that PIX_FMT_GREY is supported for backward
  compatibility. Florian is it possible for you to test?

Changes in v5 (Hans Verkuil review):
- Update v4l2-core:
  - Add VFL_TYPE_TOUCH, V4L2_BUF_TYPE_TOUCH_CAPTURE and V4L2_CAP_TOUCH
  - Change V4L2_INPUT_TYPE_TOUCH_SENSOR to V4L2_INPUT_TYPE_TOUCH
  - Improve DocBook documentation
  - Add FMT definitions for touch data
  - Note this will need the latest version of the heatmap util
- Synaptics RMI4 driver:
  - Remove some less important non full frame report types
  - Switch report type names to const char * array
  - Move a static array to inside context struct
- Split sur40 changes to a separate commit

Changes in v4:
- Address nits from the input side in atmel_mxt_ts patches (Dmitry Torokhov)
- Add Synaptics RMI4 F54 support patch

Changes in v3:
- Address V4L2 review comments from Hans Verkuil
- Run v4l-compliance and fix all issues - needs minor patch here:
  https://github.com/ndyer/v4l-utils/commit/cf50469773f

Changes in v2:
- Split pixfmt changes into separate commit and add DocBook
- Introduce VFL_TYPE_TOUCH_SENSOR and /dev/v4l-touch
- Remove "single node" support for now, it may be better to treat it as
  metadata later
- Explicitly set VFL_DIR_RX
- Fix Kconfig

[PATCH v6 09/11] Input: atmel_mxt_ts - add support for reference data

From: Nick Dyer <nick@shmanahar.org>
Date: 2016-06-30 17:46:54

There are different datatypes available from a maXTouch chip. Add
support to retrieve reference data as well.

Signed-off-by: Nick Dyer <nick@shmanahar.org>
---
 drivers/input/touchscreen/atmel_mxt_ts.c | 57 ++++++++++++++++++++++++++++----
 1 file changed, 51 insertions(+), 6 deletions(-)
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 7c4d937..f75f2ce 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -135,6 +135,7 @@ struct t9_range {
 /* MXT_DEBUG_DIAGNOSTIC_T37 */
 #define MXT_DIAGNOSTIC_PAGEUP	0x01
 #define MXT_DIAGNOSTIC_DELTAS	0x10
+#define MXT_DIAGNOSTIC_REFS	0x11
 #define MXT_DIAGNOSTIC_SIZE	128
 
 #define MXT_FAMILY_1386			160
@@ -249,6 +250,12 @@ struct mxt_dbg {
 	int input;
 };
 
+enum v4l_dbg_inputs {
+	MXT_V4L_INPUT_DELTAS,
+	MXT_V4L_INPUT_REFS,
+	MXT_V4L_INPUT_MAX,
+};
+
 static const struct v4l2_file_operations mxt_video_fops = {
 	.owner = THIS_MODULE,
 	.open = v4l2_fh_open,
@@ -2273,6 +2280,7 @@ static void mxt_buffer_queue(struct vb2_buffer *vb)
 	struct mxt_data *data = vb2_get_drv_priv(vb->vb2_queue);
 	u16 *ptr;
 	int ret;
+	u8 mode;
 
 	ptr = vb2_plane_vaddr(vb, 0);
 	if (!ptr) {
@@ -2280,7 +2288,18 @@ static void mxt_buffer_queue(struct vb2_buffer *vb)
 		goto fault;
 	}
 
-	ret = mxt_read_diagnostic_debug(data, MXT_DIAGNOSTIC_DELTAS, ptr);
+	switch (data->dbg.input) {
+	case MXT_V4L_INPUT_DELTAS:
+	default:
+		mode = MXT_DIAGNOSTIC_DELTAS;
+		break;
+
+	case MXT_V4L_INPUT_REFS:
+		mode = MXT_DIAGNOSTIC_REFS;
+		break;
+	}
+
+	ret = mxt_read_diagnostic_debug(data, mode, ptr);
 	if (ret)
 		goto fault;
 
@@ -2325,11 +2344,21 @@ static int mxt_vidioc_querycap(struct file *file, void *priv,
 static int mxt_vidioc_enum_input(struct file *file, void *priv,
 				   struct v4l2_input *i)
 {
-	if (i->index > 0)
+	if (i->index >= MXT_V4L_INPUT_MAX)
 		return -EINVAL;
 
 	i->type = V4L2_INPUT_TYPE_TOUCH;
-	strlcpy(i->name, "Mutual Capacitance Deltas", sizeof(i->name));
+
+	switch (i->index) {
+	case MXT_V4L_INPUT_REFS:
+		strlcpy(i->name, "Mutual Capacitance References",
+			sizeof(i->name));
+		break;
+	case MXT_V4L_INPUT_DELTAS:
+		strlcpy(i->name, "Mutual Capacitance Deltas", sizeof(i->name));
+		break;
+	}
+
 	return 0;
 }
 
@@ -2337,12 +2366,16 @@ static int mxt_set_input(struct mxt_data *data, unsigned int i)
 {
 	struct v4l2_pix_format *f = &data->dbg.format;
 
-	if (i > 0)
+	if (i >= MXT_V4L_INPUT_MAX)
 		return -EINVAL;
 
+	if (i == MXT_V4L_INPUT_DELTAS)
+		f->pixelformat = V4L2_TCH_FMT_DELTA_TD16;
+	else
+		f->pixelformat = V4L2_TCH_FMT_TU16;
+
 	f->width = data->xy_switch ? data->ysize : data->xsize;
 	f->height = data->xy_switch ? data->xsize : data->ysize;
-	f->pixelformat = V4L2_TCH_FMT_DELTA_TD16;
 	f->field = V4L2_FIELD_NONE;
 	f->colorspace = V4L2_COLORSPACE_RAW;
 	f->bytesperline = f->width * sizeof(u16);
@@ -2383,7 +2416,19 @@ static int mxt_vidioc_enum_fmt(struct file *file, void *priv,
 	if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
 		return -EINVAL;
 
-	fmt->pixelformat = V4L2_TCH_FMT_DELTA_TD16;
+	switch (fmt->index) {
+	case 0:
+		fmt->pixelformat = V4L2_TCH_FMT_TU16;
+		break;
+
+	case 1:
+		fmt->pixelformat = V4L2_TCH_FMT_DELTA_TD16;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
 	return 0;
 }
 
-- 
2.5.0

[PATCH v6 03/11] [media] DocBook: add support for touch devices

From: Nick Dyer <nick@shmanahar.org>
Date: 2016-06-30 17:46:55

Signed-off-by: Nick Dyer <nick@shmanahar.org>
---
 Documentation/DocBook/media/v4l/dev-touch.xml      | 51 ++++++++++++++
 Documentation/DocBook/media/v4l/media-types.xml    |  5 ++
 .../DocBook/media/v4l/pixfmt-tch-td08.xml          | 66 +++++++++++++++++
 .../DocBook/media/v4l/pixfmt-tch-td16.xml          | 82 ++++++++++++++++++++++
 .../DocBook/media/v4l/pixfmt-tch-tu08.xml          | 66 +++++++++++++++++
 .../DocBook/media/v4l/pixfmt-tch-tu16.xml          | 81 +++++++++++++++++++++
 Documentation/DocBook/media/v4l/pixfmt.xml         | 13 ++++
 Documentation/DocBook/media/v4l/v4l2.xml           |  1 +
 8 files changed, 365 insertions(+)
 create mode 100644 Documentation/DocBook/media/v4l/dev-touch.xml
 create mode 100644 Documentation/DocBook/media/v4l/pixfmt-tch-td08.xml
 create mode 100644 Documentation/DocBook/media/v4l/pixfmt-tch-td16.xml
 create mode 100644 Documentation/DocBook/media/v4l/pixfmt-tch-tu08.xml
 create mode 100644 Documentation/DocBook/media/v4l/pixfmt-tch-tu16.xml
diff --git a/Documentation/DocBook/media/v4l/dev-touch.xml b/Documentation/DocBook/media/v4l/dev-touch.xml
new file mode 100644
index 0000000..85d492a
--- /dev/null
+++ b/Documentation/DocBook/media/v4l/dev-touch.xml
@@ -0,0 +1,51 @@
+<title>Touch Devices</title>
+
+<para>Touch devices are accessed through character device special files
+  named <filename>/dev/v4l-touch0</filename> to
+  <filename>/dev/v4l-touch255</filename> with major number 81 and
+  dynamically allocated minor numbers 0 to 255.</para>
+
+<section>
+  <title>Overview</title>
+
+  <para>Sensors may be Optical, or Projected Capacitive touch (PCT).</para>
+
+  <para>Processing is required to analyse the raw data and produce input
+    events. In some systems, this may be performed on the ASIC and the raw data
+    is purely a side-channel for diagnostics or tuning. In other systems, the
+    ASIC is a simple analogue front end device which delivers touch data at
+    high rate, and any touch processing must be done on the host.</para>
+
+  <para>For capacitive touch sensing, the touchscreen is composed of an array
+    of horizontal and vertical conductors (alternatively called rows/columns,
+    X/Y lines, or tx/rx). Mutual Capacitance measured is at the nodes where the
+    conductors cross. Alternatively, Self Capacitance measures the signal from
+    each column and row independently.</para>
+
+  <para>A touch input may be determined by comparing the raw capacitance
+    measurement to a no-touch reference (or "baseline") measurement:</para>
+
+  <para>Delta = Raw - Reference</para>
+
+  <para>The reference measurement takes account of variations in the
+    capacitance across the touch sensor matrix, for example
+    manufacturing irregularities, environmental or edge effects.</para>
+</section>
+
+<section>
+  <title>Querying Capabilities</title>
+
+  <para>Devices supporting the touch interface set the
+    <constant>V4L2_CAP_VIDEO_CAPTURE</constant> flag in the
+    <structfield>capabilities</structfield> field of &v4l2-capability;
+    returned by the &VIDIOC-QUERYCAP; ioctl.</para>
+
+  <para>At least one of the read/write or streaming I/O methods must be
+    supported.</para>
+</section>
+
+<section>
+  <title>Data Format Negotiation</title>
+
+  <para>A touch device may support any I/O method.</para>
+</section>
diff --git a/Documentation/DocBook/media/v4l/media-types.xml b/Documentation/DocBook/media/v4l/media-types.xml
index 5e3f20f..fb957c7 100644
--- a/Documentation/DocBook/media/v4l/media-types.xml
+++ b/Documentation/DocBook/media/v4l/media-types.xml
@@ -202,6 +202,11 @@
 	    <entry>typically, /dev/swradio?</entry>
 	  </row>
 	  <row>
+	    <entry><constant>MEDIA_INTF_T_V4L_TOUCH</constant></entry>
+	    <entry>Device node interface for Touch device (V4L)</entry>
+	    <entry>typically, /dev/v4l-touch?</entry>
+	  </row>
+	  <row>
 	    <entry><constant>MEDIA_INTF_T_ALSA_PCM_CAPTURE</constant></entry>
 	    <entry>Device node interface for ALSA PCM Capture</entry>
 	    <entry>typically, /dev/snd/pcmC?D?c</entry>
diff --git a/Documentation/DocBook/media/v4l/pixfmt-tch-td08.xml b/Documentation/DocBook/media/v4l/pixfmt-tch-td08.xml
new file mode 100644
index 0000000..2483eb0
--- /dev/null
+++ b/Documentation/DocBook/media/v4l/pixfmt-tch-td08.xml
@@ -0,0 +1,66 @@
+<refentry id="V4L2-TCH-FMT-DELTA-TD08">
+  <refmeta>
+    <refentrytitle>V4L2_TCH_FMT_DELTA_TD08 ('TD08')</refentrytitle>
+    &manvol;
+  </refmeta>
+  <refnamediv>
+    <refname><constant>V4L2_TCH_FMT_DELTA_TD08</constant></refname>
+    <refpurpose>8-bit signed Touch Delta</refpurpose>
+  </refnamediv>
+  <refsect1>
+    <title>Description</title>
+
+    <para>This format represents delta data from a touch controller</para>
+
+    <para>Delta values may range from -128 to 127. Typically the values
+      will vary through a small range depending on whether the sensor is
+      touched or not. The full value may be seen if one of the
+      touchscreen nodes has a fault or the line is not connected.</para>
+
+    <example>
+      <title><constant>V4L2_TCH_FMT_DELTA_TD08</constant> 4 &times; 4
+        node matrix</title>
+
+      <formalpara>
+        <title>Byte Order.</title>
+        <para>Each cell is one byte.
+          <informaltable frame="none">
+            <tgroup cols="5" align="center">
+              <colspec align="left" colwidth="2*" />
+              <tbody valign="top">
+                <row>
+                  <entry>start&nbsp;+&nbsp;0:</entry>
+                  <entry>D'<subscript>00</subscript></entry>
+                  <entry>D'<subscript>01</subscript></entry>
+                  <entry>D'<subscript>02</subscript></entry>
+                  <entry>D'<subscript>03</subscript></entry>
+                </row>
+                <row>
+                  <entry>start&nbsp;+&nbsp;4:</entry>
+                  <entry>D'<subscript>10</subscript></entry>
+                  <entry>D'<subscript>11</subscript></entry>
+                  <entry>D'<subscript>12</subscript></entry>
+                  <entry>D'<subscript>13</subscript></entry>
+                </row>
+                <row>
+                  <entry>start&nbsp;+&nbsp;8:</entry>
+                  <entry>D'<subscript>20</subscript></entry>
+                  <entry>D'<subscript>21</subscript></entry>
+                  <entry>D'<subscript>22</subscript></entry>
+                  <entry>D'<subscript>23</subscript></entry>
+                </row>
+                <row>
+                  <entry>start&nbsp;+&nbsp;12:</entry>
+                  <entry>D'<subscript>30</subscript></entry>
+                  <entry>D'<subscript>31</subscript></entry>
+                  <entry>D'<subscript>32</subscript></entry>
+                  <entry>D'<subscript>33</subscript></entry>
+                </row>
+              </tbody>
+            </tgroup>
+          </informaltable>
+        </para>
+      </formalpara>
+    </example>
+  </refsect1>
+</refentry>
diff --git a/Documentation/DocBook/media/v4l/pixfmt-tch-td16.xml b/Documentation/DocBook/media/v4l/pixfmt-tch-td16.xml
new file mode 100644
index 0000000..72f6245
--- /dev/null
+++ b/Documentation/DocBook/media/v4l/pixfmt-tch-td16.xml
@@ -0,0 +1,82 @@
+<refentry id="V4L2-TCH-FMT-DELTA-TD16">
+  <refmeta>
+    <refentrytitle>V4L2_TCH_FMT_DELTA_TD16 ('TD16')</refentrytitle>
+    &manvol;
+  </refmeta>
+  <refnamediv>
+    <refname><constant>V4L2_TCH_FMT_DELTA_TD16</constant></refname>
+    <refpurpose>16-bit signed Touch Delta</refpurpose>
+  </refnamediv>
+  <refsect1>
+    <title>Description</title>
+
+    <para>This format represents delta data from a touch controller</para>
+
+    <para>Delta values may range from -32768 to 32767. Typically the values
+      will vary through a small range depending on whether the sensor is
+      touched or not. The full value may be seen if one of the
+      touchscreen nodes has a fault or the line is not connected.</para>
+
+    <example>
+      <title><constant>V4L2_TCH_FMT_DELTA_TD16</constant> 4 &times; 4
+        node matrix</title>
+
+      <formalpara>
+        <title>Byte Order.</title>
+        <para>Each cell is one byte.
+          <informaltable frame="none">
+            <tgroup cols="9" align="center">
+              <colspec align="left" colwidth="2*" />
+              <tbody valign="top">
+                <row>
+                  <entry>start&nbsp;+&nbsp;0:</entry>
+                  <entry>D'<subscript>00low</subscript></entry>
+                  <entry>D'<subscript>00high</subscript></entry>
+                  <entry>D'<subscript>01low</subscript></entry>
+                  <entry>D'<subscript>01high</subscript></entry>
+                  <entry>D'<subscript>02low</subscript></entry>
+                  <entry>D'<subscript>02high</subscript></entry>
+                  <entry>D'<subscript>03low</subscript></entry>
+                  <entry>D'<subscript>03high</subscript></entry>
+                </row>
+                <row>
+                  <entry>start&nbsp;+&nbsp;8:</entry>
+                  <entry>D'<subscript>10low</subscript></entry>
+                  <entry>D'<subscript>10high</subscript></entry>
+                  <entry>D'<subscript>11low</subscript></entry>
+                  <entry>D'<subscript>11high</subscript></entry>
+                  <entry>D'<subscript>12low</subscript></entry>
+                  <entry>D'<subscript>12high</subscript></entry>
+                  <entry>D'<subscript>13low</subscript></entry>
+                  <entry>D'<subscript>13high</subscript></entry>
+                </row>
+                <row>
+                  <entry>start&nbsp;+&nbsp;16:</entry>
+                  <entry>D'<subscript>20low</subscript></entry>
+                  <entry>D'<subscript>20high</subscript></entry>
+                  <entry>D'<subscript>21low</subscript></entry>
+                  <entry>D'<subscript>21high</subscript></entry>
+                  <entry>D'<subscript>22low</subscript></entry>
+                  <entry>D'<subscript>22high</subscript></entry>
+                  <entry>D'<subscript>23low</subscript></entry>
+                  <entry>D'<subscript>23high</subscript></entry>
+                </row>
+                <row>
+                  <entry>start&nbsp;+&nbsp;24:</entry>
+                  <entry>D'<subscript>30low</subscript></entry>
+                  <entry>D'<subscript>30high</subscript></entry>
+                  <entry>D'<subscript>31low</subscript></entry>
+                  <entry>D'<subscript>31high</subscript></entry>
+                  <entry>D'<subscript>32low</subscript></entry>
+                  <entry>D'<subscript>32high</subscript></entry>
+                  <entry>D'<subscript>33low</subscript></entry>
+                  <entry>D'<subscript>33high</subscript></entry>
+                </row>
+              </tbody>
+            </tgroup>
+          </informaltable>
+        </para>
+      </formalpara>
+    </example>
+  </refsect1>
+</refentry>
diff --git a/Documentation/DocBook/media/v4l/pixfmt-tch-tu08.xml b/Documentation/DocBook/media/v4l/pixfmt-tch-tu08.xml
new file mode 100644
index 0000000..24f76ab
--- /dev/null
+++ b/Documentation/DocBook/media/v4l/pixfmt-tch-tu08.xml
@@ -0,0 +1,66 @@
+<refentry id="V4L2-TCH-FMT-TU08">
+  <refmeta>
+    <refentrytitle>V4L2_TCH_FMT_TU08 ('TU08')</refentrytitle>
+    &manvol;
+  </refmeta>
+  <refnamediv>
+    <refname><constant>V4L2_TCH_FMT_TU08</constant></refname>
+    <refpurpose>8-bit unsigned raw touch data</refpurpose>
+  </refnamediv>
+  <refsect1>
+    <title>Description</title>
+
+    <para>This format represents unsigned 8-bit data from a touch
+      controller.</para>
+
+    <para>This may be used for output for raw and reference data. Values may
+      range from 0 to 255.</para>
+
+    <example>
+      <title><constant>V4L2_TCH_FMT_TU08</constant> 4 &times; 4
+        node matrix</title>
+
+      <formalpara>
+        <title>Byte Order.</title>
+        <para>Each cell is one byte.
+          <informaltable frame="none">
+            <tgroup cols="5" align="center">
+              <colspec align="left" colwidth="2*" />
+              <tbody valign="top">
+                <row>
+                  <entry>start&nbsp;+&nbsp;0:</entry>
+                  <entry>R'<subscript>00</subscript></entry>
+                  <entry>R'<subscript>01</subscript></entry>
+                  <entry>R'<subscript>02</subscript></entry>
+                  <entry>R'<subscript>03</subscript></entry>
+                </row>
+                <row>
+                  <entry>start&nbsp;+&nbsp;4:</entry>
+                  <entry>R'<subscript>10</subscript></entry>
+                  <entry>R'<subscript>11</subscript></entry>
+                  <entry>R'<subscript>12</subscript></entry>
+                  <entry>R'<subscript>13</subscript></entry>
+                </row>
+                <row>
+                  <entry>start&nbsp;+&nbsp;8:</entry>
+                  <entry>R'<subscript>20</subscript></entry>
+                  <entry>R'<subscript>21</subscript></entry>
+                  <entry>R'<subscript>22</subscript></entry>
+                  <entry>R'<subscript>23</subscript></entry>
+                </row>
+                <row>
+                  <entry>start&nbsp;+&nbsp;12:</entry>
+                  <entry>R'<subscript>30</subscript></entry>
+                  <entry>R'<subscript>31</subscript></entry>
+                  <entry>R'<subscript>32</subscript></entry>
+                  <entry>R'<subscript>33</subscript></entry>
+                </row>
+              </tbody>
+            </tgroup>
+          </informaltable>
+        </para>
+      </formalpara>
+
+    </example>
+  </refsect1>
+</refentry>
diff --git a/Documentation/DocBook/media/v4l/pixfmt-tch-tu16.xml b/Documentation/DocBook/media/v4l/pixfmt-tch-tu16.xml
new file mode 100644
index 0000000..2db69af
--- /dev/null
+++ b/Documentation/DocBook/media/v4l/pixfmt-tch-tu16.xml
@@ -0,0 +1,81 @@
+<refentry id="V4L2-TCH-FMT-TU16">
+  <refmeta>
+    <refentrytitle>V4L2_TCH_FMT_TU16 ('TU16')</refentrytitle>
+    &manvol;
+  </refmeta>
+  <refnamediv>
+    <refname><constant>V4L2_TCH_FMT_TU16</constant></refname>
+    <refpurpose>16-bit unsigned raw touch data</refpurpose>
+  </refnamediv>
+  <refsect1>
+    <title>Description</title>
+
+    <para>This format represents unsigned 16-bit data from a touch
+      controller.</para>
+
+    <para>This may be used for output for raw and reference data. Values may
+      range from 0 to 65535.</para>
+
+    <example>
+      <title><constant>V4L2_TCH_FMT_TU16</constant> 4 &times; 4
+        node matrix</title>
+
+      <formalpara>
+        <title>Byte Order.</title>
+        <para>Each cell is one byte.
+          <informaltable frame="none">
+            <tgroup cols="9" align="center">
+              <colspec align="left" colwidth="2*" />
+              <tbody valign="top">
+                <row>
+                  <entry>start&nbsp;+&nbsp;0:</entry>
+                  <entry>R'<subscript>00low</subscript></entry>
+                  <entry>R'<subscript>00high</subscript></entry>
+                  <entry>R'<subscript>01low</subscript></entry>
+                  <entry>R'<subscript>01high</subscript></entry>
+                  <entry>R'<subscript>02low</subscript></entry>
+                  <entry>R'<subscript>02high</subscript></entry>
+                  <entry>R'<subscript>03low</subscript></entry>
+                  <entry>R'<subscript>03high</subscript></entry>
+                </row>
+                <row>
+                  <entry>start&nbsp;+&nbsp;8:</entry>
+                  <entry>R'<subscript>10low</subscript></entry>
+                  <entry>R'<subscript>10high</subscript></entry>
+                  <entry>R'<subscript>11low</subscript></entry>
+                  <entry>R'<subscript>11high</subscript></entry>
+                  <entry>R'<subscript>12low</subscript></entry>
+                  <entry>R'<subscript>12high</subscript></entry>
+                  <entry>R'<subscript>13low</subscript></entry>
+                  <entry>R'<subscript>13high</subscript></entry>
+                </row>
+                <row>
+                  <entry>start&nbsp;+&nbsp;16:</entry>
+                  <entry>R'<subscript>20low</subscript></entry>
+                  <entry>R'<subscript>20high</subscript></entry>
+                  <entry>R'<subscript>21low</subscript></entry>
+                  <entry>R'<subscript>21high</subscript></entry>
+                  <entry>R'<subscript>22low</subscript></entry>
+                  <entry>R'<subscript>22high</subscript></entry>
+                  <entry>R'<subscript>23low</subscript></entry>
+                  <entry>R'<subscript>23high</subscript></entry>
+                </row>
+                <row>
+                  <entry>start&nbsp;+&nbsp;24:</entry>
+                  <entry>R'<subscript>30low</subscript></entry>
+                  <entry>R'<subscript>30high</subscript></entry>
+                  <entry>R'<subscript>31low</subscript></entry>
+                  <entry>R'<subscript>31high</subscript></entry>
+                  <entry>R'<subscript>32low</subscript></entry>
+                  <entry>R'<subscript>32high</subscript></entry>
+                  <entry>R'<subscript>33low</subscript></entry>
+                  <entry>R'<subscript>33high</subscript></entry>
+                </row>
+              </tbody>
+            </tgroup>
+          </informaltable>
+        </para>
+      </formalpara>
+    </example>
+  </refsect1>
+</refentry>
diff --git a/Documentation/DocBook/media/v4l/pixfmt.xml b/Documentation/DocBook/media/v4l/pixfmt.xml
index 5a08aee..509248a 100644
--- a/Documentation/DocBook/media/v4l/pixfmt.xml
+++ b/Documentation/DocBook/media/v4l/pixfmt.xml
@@ -1754,6 +1754,19 @@ interface only.</para>
 
   </section>
 
+  <section id="tch-formats">
+    <title>Touch Formats</title>
+
+    <para>These formats are used for <link linkend="touch">Touch Sensor</link>
+interface only.</para>
+
+    &sub-tch-td16;
+    &sub-tch-td08;
+    &sub-tch-tu16;
+    &sub-tch-tu08;
+
+  </section>
+
   <section id="pixfmt-reserved">
     <title>Reserved Format Identifiers</title>
 
diff --git a/Documentation/DocBook/media/v4l/v4l2.xml b/Documentation/DocBook/media/v4l/v4l2.xml
index 42e626d..b577de2 100644
--- a/Documentation/DocBook/media/v4l/v4l2.xml
+++ b/Documentation/DocBook/media/v4l/v4l2.xml
@@ -605,6 +605,7 @@ and discussions on the V4L mailing list.</revremark>
     <section id="radio"> &sub-dev-radio; </section>
     <section id="rds"> &sub-dev-rds; </section>
     <section id="sdr"> &sub-dev-sdr; </section>
+    <section id="touch"> &sub-dev-touch; </section>
     <section id="event"> &sub-dev-event; </section>
     <section id="subdev"> &sub-dev-subdev; </section>
   </chapter>
-- 
2.5.0

[PATCH v6 07/11] Input: atmel_mxt_ts - handle diagnostic data orientation

From: Nick Dyer <nick@shmanahar.org>
Date: 2016-06-30 17:46:58

Invert the diagnostic data to match the orientation of the input device.

Signed-off-by: Nick Dyer <nick@shmanahar.org>
---
 drivers/input/touchscreen/atmel_mxt_ts.c | 26 +++++++++++++++++++++-----
 1 file changed, 21 insertions(+), 5 deletions(-)
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index d09ecc3..c35fca0 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -125,6 +125,8 @@ struct t9_range {
 
 /* MXT_TOUCH_MULTI_T9 orient */
 #define MXT_T9_ORIENT_SWITCH	(1 << 0)
+#define MXT_T9_ORIENT_INVERTX	(1 << 1)
+#define MXT_T9_ORIENT_INVERTY	(1 << 2)
 
 /* MXT_SPT_COMMSCONFIG_T18 */
 #define MXT_COMMS_CTRL		0
@@ -158,6 +160,8 @@ struct t37_debug {
 #define MXT_T100_YRANGE		24
 
 #define MXT_T100_CFG_SWITCHXY	BIT(5)
+#define MXT_T100_CFG_INVERTY	BIT(6)
+#define MXT_T100_CFG_INVERTX	BIT(7)
 
 #define MXT_T100_TCHAUX_VECT	BIT(0)
 #define MXT_T100_TCHAUX_AMPL	BIT(1)
@@ -262,6 +266,8 @@ struct mxt_data {
 	unsigned int irq;
 	unsigned int max_x;
 	unsigned int max_y;
+	bool invertx;
+	bool inverty;
 	bool xy_switch;
 	u8 xsize;
 	u8 ysize;
@@ -1747,6 +1753,8 @@ static int mxt_read_t9_resolution(struct mxt_data *data)
 		return error;
 
 	data->xy_switch = orient & MXT_T9_ORIENT_SWITCH;
+	data->invertx = orient & MXT_T9_ORIENT_INVERTX;
+	data->inverty = orient & MXT_T9_ORIENT_INVERTY;
 
 	return 0;
 }
@@ -1801,6 +1809,8 @@ static int mxt_read_t100_config(struct mxt_data *data)
 		return error;
 
 	data->xy_switch = cfg & MXT_T100_CFG_SWITCHXY;
+	data->invertx = cfg & MXT_T100_CFG_INVERTX;
+	data->inverty = cfg & MXT_T100_CFG_INVERTY;
 
 	/* allocate aux bytes */
 	error =  __mxt_read_reg(client,
@@ -2145,13 +2155,19 @@ static int mxt_convert_debug_pages(struct mxt_data *data, u16 *outbuf)
 	struct mxt_dbg *dbg = &data->dbg;
 	unsigned int x = 0;
 	unsigned int y = 0;
-	unsigned int i;
+	unsigned int i, rx, ry;
 
 	for (i = 0; i < dbg->t37_nodes; i++) {
-		outbuf[i] = mxt_get_debug_value(data, x, y);
+		/* Handle orientation */
+		rx = data->xy_switch ? y : x;
+		ry = data->xy_switch ? x : y;
+		rx = data->invertx ? (data->xsize - 1 - rx) : rx;
+		ry = data->inverty ? (data->ysize - 1 - ry) : ry;
+
+		outbuf[i] = mxt_get_debug_value(data, rx, ry);
 
 		/* Next value */
-		if (++x >= data->xsize) {
+		if (++x >= (data->xy_switch ? data->ysize : data->xsize)) {
 			x = 0;
 			y++;
 		}
@@ -2306,8 +2322,8 @@ static int mxt_set_input(struct mxt_data *data, unsigned int i)
 	if (i > 0)
 		return -EINVAL;
 
-	f->width = data->xsize;
-	f->height = data->ysize;
+	f->width = data->xy_switch ? data->ysize : data->xsize;
+	f->height = data->xy_switch ? data->xsize : data->ysize;
 	f->pixelformat = V4L2_TCH_FMT_DELTA_TD16;
 	f->field = V4L2_FIELD_NONE;
 	f->colorspace = V4L2_COLORSPACE_RAW;
-- 
2.5.0

[PATCH v6 02/11] [media] v4l2-core: Add support for touch devices

From: Nick Dyer <nick@shmanahar.org>
Date: 2016-06-30 17:47:05

Some touch controllers send out touch data in a similar way to a
greyscale frame grabber.

Add new device type VFL_TYPE_TOUCH:
- This uses a new device prefix v4l-touch for these devices, to stop
  generic capture software from treating them as webcams. Otherwise,
  touch is treated similarly to video capture.
- Add V4L2_INPUT_TYPE_TOUCH
- Add MEDIA_INTF_T_V4L_TOUCH
- Add V4L2_CAP_TOUCH to indicate device is a touch device

Add formats:
- V4L2_TCH_FMT_DELTA_TD16 for signed 16-bit touch deltas
- V4L2_TCH_FMT_DELTA_TD08 for signed 16-bit touch deltas
- V4L2_TCH_FMT_TU16 for unsigned 16-bit touch data
- V4L2_TCH_FMT_TU08 for unsigned 8-bit touch data

This support will be used by:
- Atmel maXTouch (atmel_mxt_ts)
- Synaptics RMI4.
- sur40

Signed-off-by: Nick Dyer <nick@shmanahar.org>
---
 drivers/media/media-entity.c         |  2 ++
 drivers/media/v4l2-core/v4l2-dev.c   | 16 +++++++++++++---
 drivers/media/v4l2-core/v4l2-ioctl.c | 36 +++++++++++++++++++++++++++++++-----
 include/media/v4l2-dev.h             |  3 ++-
 include/uapi/linux/media.h           |  1 +
 include/uapi/linux/videodev2.h       |  9 +++++++++
 6 files changed, 58 insertions(+), 9 deletions(-)
diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c
index d8a2299..9014362 100644
--- a/drivers/media/media-entity.c
+++ b/drivers/media/media-entity.c
@@ -65,6 +65,8 @@ static inline const char *intf_type(struct media_interface *intf)
 		return "v4l-subdev";
 	case MEDIA_INTF_T_V4L_SWRADIO:
 		return "v4l-swradio";
+	case MEDIA_INTF_T_V4L_TOUCH:
+		return "v4l-touch";
 	case MEDIA_INTF_T_ALSA_PCM_CAPTURE:
 		return "alsa-pcm-capture";
 	case MEDIA_INTF_T_ALSA_PCM_PLAYBACK:
diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c
index 70b559d..21ba9b4 100644
--- a/drivers/media/v4l2-core/v4l2-dev.c
+++ b/drivers/media/v4l2-core/v4l2-dev.c
@@ -527,6 +527,7 @@ static void determine_valid_ioctls(struct video_device *vdev)
 	bool is_vbi = vdev->vfl_type == VFL_TYPE_VBI;
 	bool is_radio = vdev->vfl_type == VFL_TYPE_RADIO;
 	bool is_sdr = vdev->vfl_type == VFL_TYPE_SDR;
+	bool is_tch = vdev->vfl_type == VFL_TYPE_TOUCH;
 	bool is_rx = vdev->vfl_dir != VFL_DIR_TX;
 	bool is_tx = vdev->vfl_dir != VFL_DIR_RX;
 
@@ -573,7 +574,7 @@ static void determine_valid_ioctls(struct video_device *vdev)
 	if (ops->vidioc_enum_freq_bands || ops->vidioc_g_tuner || ops->vidioc_g_modulator)
 		set_bit(_IOC_NR(VIDIOC_ENUM_FREQ_BANDS), valid_ioctls);
 
-	if (is_vid) {
+	if (is_vid || is_tch) {
 		/* video specific ioctls */
 		if ((is_rx && (ops->vidioc_enum_fmt_vid_cap ||
 			       ops->vidioc_enum_fmt_vid_cap_mplane ||
@@ -662,7 +663,7 @@ static void determine_valid_ioctls(struct video_device *vdev)
 			set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls);
 	}
 
-	if (is_vid || is_vbi || is_sdr) {
+	if (is_vid || is_vbi || is_sdr || is_tch) {
 		/* ioctls valid for video, vbi or sdr */
 		SET_VALID_IOCTL(ops, VIDIOC_REQBUFS, vidioc_reqbufs);
 		SET_VALID_IOCTL(ops, VIDIOC_QUERYBUF, vidioc_querybuf);
@@ -675,7 +676,7 @@ static void determine_valid_ioctls(struct video_device *vdev)
 		SET_VALID_IOCTL(ops, VIDIOC_STREAMOFF, vidioc_streamoff);
 	}
 
-	if (is_vid || is_vbi) {
+	if (is_vid || is_vbi || is_tch) {
 		/* ioctls valid for video or vbi */
 		if (ops->vidioc_s_std)
 			set_bit(_IOC_NR(VIDIOC_ENUMSTD), valid_ioctls);
@@ -751,6 +752,10 @@ static int video_register_media_controller(struct video_device *vdev, int type)
 		intf_type = MEDIA_INTF_T_V4L_SWRADIO;
 		vdev->entity.function = MEDIA_ENT_F_IO_SWRADIO;
 		break;
+	case VFL_TYPE_TOUCH:
+		intf_type = MEDIA_INTF_T_V4L_TOUCH;
+		vdev->entity.function = MEDIA_ENT_F_IO_V4L;
+		break;
 	case VFL_TYPE_RADIO:
 		intf_type = MEDIA_INTF_T_V4L_RADIO;
 		/*
@@ -845,6 +850,8 @@ static int video_register_media_controller(struct video_device *vdev, int type)
  *	%VFL_TYPE_SUBDEV - A subdevice
  *
  *	%VFL_TYPE_SDR - Software Defined Radio
+ *
+ *	%VFL_TYPE_TOUCH - A touch sensor
  */
 int __video_register_device(struct video_device *vdev, int type, int nr,
 		int warn_if_nr_in_use, struct module *owner)
@@ -888,6 +895,9 @@ int __video_register_device(struct video_device *vdev, int type, int nr,
 		/* Use device name 'swradio' because 'sdr' was already taken. */
 		name_base = "swradio";
 		break;
+	case VFL_TYPE_TOUCH:
+		name_base = "v4l-touch";
+		break;
 	default:
 		printk(KERN_ERR "%s called with unknown type: %d\n",
 		       __func__, type);
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c
index 28e5be2..c3463f7 100644
--- a/drivers/media/v4l2-core/v4l2-ioctl.c
+++ b/drivers/media/v4l2-core/v4l2-ioctl.c
@@ -924,6 +924,7 @@ static int check_fmt(struct file *file, enum v4l2_buf_type type)
 	bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER;
 	bool is_vbi = vfd->vfl_type == VFL_TYPE_VBI;
 	bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR;
+	bool is_tch = vfd->vfl_type == VFL_TYPE_TOUCH;
 	bool is_rx = vfd->vfl_dir != VFL_DIR_TX;
 	bool is_tx = vfd->vfl_dir != VFL_DIR_RX;
 
@@ -932,7 +933,7 @@ static int check_fmt(struct file *file, enum v4l2_buf_type type)
 
 	switch (type) {
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-		if (is_vid && is_rx &&
+		if ((is_vid || is_tch) && is_rx &&
 		    (ops->vidioc_g_fmt_vid_cap || ops->vidioc_g_fmt_vid_cap_mplane))
 			return 0;
 		break;
@@ -1243,6 +1244,10 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt)
 	case V4L2_SDR_FMT_CS8:		descr = "Complex S8"; break;
 	case V4L2_SDR_FMT_CS14LE:	descr = "Complex S14LE"; break;
 	case V4L2_SDR_FMT_RU12LE:	descr = "Real U12LE"; break;
+	case V4L2_TCH_FMT_DELTA_TD16:	descr = "16-bit signed deltas"; break;
+	case V4L2_TCH_FMT_DELTA_TD08:	descr = "8-bit signed deltas"; break;
+	case V4L2_TCH_FMT_TU16:		descr = "16-bit unsigned touch data"; break;
+	case V4L2_TCH_FMT_TU08:		descr = "8-bit unsigned touch data"; break;
 
 	default:
 		/* Compressed formats */
@@ -1309,13 +1314,14 @@ static int v4l_enum_fmt(const struct v4l2_ioctl_ops *ops,
 	struct video_device *vfd = video_devdata(file);
 	bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER;
 	bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR;
+	bool is_tch = vfd->vfl_type == VFL_TYPE_TOUCH;
 	bool is_rx = vfd->vfl_dir != VFL_DIR_TX;
 	bool is_tx = vfd->vfl_dir != VFL_DIR_RX;
 	int ret = -EINVAL;
 
 	switch (p->type) {
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-		if (unlikely(!is_rx || !is_vid || !ops->vidioc_enum_fmt_vid_cap))
+		if (unlikely(!is_rx || (!is_vid && !is_tch) || !ops->vidioc_enum_fmt_vid_cap))
 			break;
 		ret = ops->vidioc_enum_fmt_vid_cap(file, fh, arg);
 		break;
@@ -1362,6 +1368,7 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
 	struct video_device *vfd = video_devdata(file);
 	bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER;
 	bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR;
+	bool is_tch = vfd->vfl_type == VFL_TYPE_TOUCH;
 	bool is_rx = vfd->vfl_dir != VFL_DIR_TX;
 	bool is_tx = vfd->vfl_dir != VFL_DIR_RX;
 	int ret;
@@ -1392,7 +1399,7 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
 
 	switch (p->type) {
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-		if (unlikely(!is_rx || !is_vid || !ops->vidioc_g_fmt_vid_cap))
+		if (unlikely(!is_rx || (!is_vid && !is_tch) || !ops->vidioc_g_fmt_vid_cap))
 			break;
 		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
 		ret = ops->vidioc_g_fmt_vid_cap(file, fh, arg);
@@ -1451,6 +1458,21 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
 	return -EINVAL;
 }
 
+static void v4l_pix_format_touch(struct v4l2_pix_format *p)
+{
+	/*
+	 * The v4l2_pix_format structure contains fields that make no sense for
+	 * touch. Set them to default values in this case.
+	 */
+
+	p->field = V4L2_FIELD_NONE;
+	p->colorspace = V4L2_COLORSPACE_RAW;
+	p->flags = 0;
+	p->ycbcr_enc = 0;
+	p->quantization = 0;
+	p->xfer_func = 0;
+}
+
 static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
 				struct file *file, void *fh, void *arg)
 {
@@ -1458,6 +1480,7 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
 	struct video_device *vfd = video_devdata(file);
 	bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER;
 	bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR;
+	bool is_tch = vfd->vfl_type == VFL_TYPE_TOUCH;
 	bool is_rx = vfd->vfl_dir != VFL_DIR_TX;
 	bool is_tx = vfd->vfl_dir != VFL_DIR_RX;
 	int ret;
@@ -1469,12 +1492,14 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
 
 	switch (p->type) {
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-		if (unlikely(!is_rx || !is_vid || !ops->vidioc_s_fmt_vid_cap))
+		if (unlikely(!is_rx || (!is_vid && !is_tch) || !ops->vidioc_s_fmt_vid_cap))
 			break;
 		CLEAR_AFTER_FIELD(p, fmt.pix);
 		ret = ops->vidioc_s_fmt_vid_cap(file, fh, arg);
 		/* just in case the driver zeroed it again */
 		p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC;
+		if (is_tch)
+			v4l_pix_format_touch(&p->fmt.pix);
 		return ret;
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
 		if (unlikely(!is_rx || !is_vid || !ops->vidioc_s_fmt_vid_cap_mplane))
@@ -1545,6 +1570,7 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
 	struct video_device *vfd = video_devdata(file);
 	bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER;
 	bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR;
+	bool is_tch = vfd->vfl_type == VFL_TYPE_TOUCH;
 	bool is_rx = vfd->vfl_dir != VFL_DIR_TX;
 	bool is_tx = vfd->vfl_dir != VFL_DIR_RX;
 	int ret;
@@ -1553,7 +1579,7 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
 
 	switch (p->type) {
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
-		if (unlikely(!is_rx || !is_vid || !ops->vidioc_try_fmt_vid_cap))
+		if (unlikely(!is_rx || (!is_vid && !is_tch) || !ops->vidioc_try_fmt_vid_cap))
 			break;
 		CLEAR_AFTER_FIELD(p, fmt.pix);
 		ret = ops->vidioc_try_fmt_vid_cap(file, fh, arg);
diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h
index 25a3190..a2bbf1c 100644
--- a/include/media/v4l2-dev.h
+++ b/include/media/v4l2-dev.h
@@ -25,7 +25,8 @@
 #define VFL_TYPE_RADIO		2
 #define VFL_TYPE_SUBDEV		3
 #define VFL_TYPE_SDR		4
-#define VFL_TYPE_MAX		5
+#define VFL_TYPE_TOUCH		5
+#define VFL_TYPE_MAX		6
 
 /* Is this a receiver, transmitter or mem-to-mem? */
 /* Ignored for VFL_TYPE_SUBDEV. */
diff --git a/include/uapi/linux/media.h b/include/uapi/linux/media.h
index df59ede..a962ebd 100644
--- a/include/uapi/linux/media.h
+++ b/include/uapi/linux/media.h
@@ -297,6 +297,7 @@ struct media_links_enum {
 #define MEDIA_INTF_T_V4L_RADIO  (MEDIA_INTF_T_V4L_BASE + 2)
 #define MEDIA_INTF_T_V4L_SUBDEV (MEDIA_INTF_T_V4L_BASE + 3)
 #define MEDIA_INTF_T_V4L_SWRADIO (MEDIA_INTF_T_V4L_BASE + 4)
+#define MEDIA_INTF_T_V4L_TOUCH	(MEDIA_INTF_T_V4L_BASE + 5)
 
 #define MEDIA_INTF_T_ALSA_PCM_CAPTURE   (MEDIA_INTF_T_ALSA_BASE)
 #define MEDIA_INTF_T_ALSA_PCM_PLAYBACK  (MEDIA_INTF_T_ALSA_BASE + 1)
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index 8f95191..ea64f3d 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -440,6 +440,8 @@ struct v4l2_capability {
 #define V4L2_CAP_ASYNCIO                0x02000000  /* async I/O */
 #define V4L2_CAP_STREAMING              0x04000000  /* streaming I/O ioctls */
 
+#define V4L2_CAP_TOUCH                  0x10000000  /* Is a touch device */
+
 #define V4L2_CAP_DEVICE_CAPS            0x80000000  /* sets device capabilities field */
 
 /*
@@ -633,6 +635,12 @@ struct v4l2_pix_format {
 #define V4L2_SDR_FMT_CS14LE       v4l2_fourcc('C', 'S', '1', '4') /* complex s14le */
 #define V4L2_SDR_FMT_RU12LE       v4l2_fourcc('R', 'U', '1', '2') /* real u12le */
 
+/* Touch formats - used for Touch devices */
+#define V4L2_TCH_FMT_DELTA_TD16	v4l2_fourcc('T', 'D', '1', '6') /* 16-bit signed deltas */
+#define V4L2_TCH_FMT_DELTA_TD08	v4l2_fourcc('T', 'D', '0', '8') /* 8-bit signed deltas */
+#define V4L2_TCH_FMT_TU16	v4l2_fourcc('T', 'U', '1', '6') /* 16-bit unsigned touch data */
+#define V4L2_TCH_FMT_TU08	v4l2_fourcc('T', 'U', '0', '8') /* 8-bit unsigned touch data */
+
 /* priv field value to indicates that subsequent fields are valid. */
 #define V4L2_PIX_FMT_PRIV_MAGIC		0xfeedcafe
 
@@ -1399,6 +1407,7 @@ struct v4l2_input {
 /*  Values for the 'type' field */
 #define V4L2_INPUT_TYPE_TUNER		1
 #define V4L2_INPUT_TYPE_CAMERA		2
+#define V4L2_INPUT_TYPE_TOUCH		3
 
 /* field 'status' - general */
 #define V4L2_IN_ST_NO_POWER    0x00000001  /* Attached device is off */
-- 
2.5.0

[PATCH v6 04/11] Input: atmel_mxt_ts - add support for T37 diagnostic data

From: Nick Dyer <nick@shmanahar.org>
Date: 2016-06-30 17:47:27

Atmel maXTouch devices have a T37 object which can be used to read raw
touch deltas from the device. This consists of an array of 16-bit
integers, one for each node on the touchscreen matrix.

Signed-off-by: Nick Dyer <nick@shmanahar.org>
---
 drivers/input/touchscreen/Kconfig        |   6 ++
 drivers/input/touchscreen/atmel_mxt_ts.c | 159 +++++++++++++++++++++++++++++++
 2 files changed, 165 insertions(+)
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 8ecdc38..da96ecf 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -115,6 +115,12 @@ config TOUCHSCREEN_ATMEL_MXT
 	  To compile this driver as a module, choose M here: the
 	  module will be called atmel_mxt_ts.
 
+config TOUCHSCREEN_ATMEL_MXT_T37
+	bool "Support T37 Diagnostic Data"
+	depends on TOUCHSCREEN_ATMEL_MXT
+	help
+	  Say Y here if you want support for the T37 Diagnostic Data object.
+
 config TOUCHSCREEN_AUO_PIXCIR
 	tristate "AUO in-cell touchscreen using Pixcir ICs"
 	depends on I2C
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 5af7907..0048233 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -124,6 +124,19 @@ struct t9_range {
 #define MXT_COMMS_CTRL		0
 #define MXT_COMMS_CMD		1
 
+/* MXT_DEBUG_DIAGNOSTIC_T37 */
+#define MXT_DIAGNOSTIC_PAGEUP	0x01
+#define MXT_DIAGNOSTIC_DELTAS	0x10
+#define MXT_DIAGNOSTIC_SIZE	128
+
+struct t37_debug {
+#ifdef CONFIG_TOUCHSCREEN_ATMEL_MXT_T37
+	u8 mode;
+	u8 page;
+	u8 data[MXT_DIAGNOSTIC_SIZE];
+#endif
+};
+
 /* Define for MXT_GEN_COMMAND_T6 */
 #define MXT_BOOT_VALUE		0xa5
 #define MXT_RESET_VALUE		0x01
@@ -205,6 +218,14 @@ struct mxt_object {
 	u8 num_report_ids;
 } __packed;
 
+struct mxt_dbg {
+	u16 t37_address;
+	u16 diag_cmd_address;
+	struct t37_debug *t37_buf;
+	unsigned int t37_pages;
+	unsigned int t37_nodes;
+};
+
 /* Each client has this additional data */
 struct mxt_data {
 	struct i2c_client *client;
@@ -233,6 +254,7 @@ struct mxt_data {
 	u8 num_touchids;
 	u8 multitouch;
 	struct t7_config t7_cfg;
+	struct mxt_dbg dbg;
 
 	/* Cached parameters from object table */
 	u16 T5_address;
@@ -2043,6 +2065,141 @@ recheck:
 	return 0;
 }
 
+#ifdef CONFIG_TOUCHSCREEN_ATMEL_MXT_T37
+static u16 mxt_get_debug_value(struct mxt_data *data, unsigned int x,
+			       unsigned int y)
+{
+	struct mxt_dbg *dbg = &data->dbg;
+	unsigned int ofs, page;
+
+	ofs = (y + (x * data->info.matrix_ysize)) * sizeof(u16);
+	page = ofs / MXT_DIAGNOSTIC_SIZE;
+	ofs %= MXT_DIAGNOSTIC_SIZE;
+
+	return get_unaligned_le16(&dbg->t37_buf[page].data[ofs]);
+}
+
+static int mxt_convert_debug_pages(struct mxt_data *data, u16 *outbuf)
+{
+	struct mxt_dbg *dbg = &data->dbg;
+	unsigned int x = 0;
+	unsigned int y = 0;
+	unsigned int i;
+
+	for (i = 0; i < dbg->t37_nodes; i++) {
+		outbuf[i] = mxt_get_debug_value(data, x, y);
+
+		/* Next value */
+		if (++x >= data->info.matrix_xsize) {
+			x = 0;
+			y++;
+		}
+	}
+
+	return 0;
+}
+
+static int mxt_read_diagnostic_debug(struct mxt_data *data, u8 mode,
+				     u16 *outbuf)
+{
+	struct mxt_dbg *dbg = &data->dbg;
+	int retries = 0;
+	int page;
+	int ret;
+	u8 cmd = mode;
+	struct t37_debug *p;
+	u8 cmd_poll;
+
+	for (page = 0; page < dbg->t37_pages; page++) {
+		p = dbg->t37_buf + page;
+
+		ret = mxt_write_reg(data->client, dbg->diag_cmd_address,
+				    cmd);
+		if (ret)
+			return ret;
+
+		retries = 0;
+		msleep(20);
+wait_cmd:
+		/* Read back command byte */
+		ret = __mxt_read_reg(data->client, dbg->diag_cmd_address,
+				     sizeof(cmd_poll), &cmd_poll);
+		if (ret)
+			return ret;
+
+		/* Field is cleared once the command has been processed */
+		if (cmd_poll) {
+			if (retries++ > 100)
+				return -EINVAL;
+
+			msleep(20);
+			goto wait_cmd;
+		}
+
+		/* Read T37 page */
+		ret = __mxt_read_reg(data->client, dbg->t37_address,
+				     sizeof(struct t37_debug), p);
+		if (ret)
+			return ret;
+
+		if (p->mode != mode || p->page != page) {
+			dev_err(&data->client->dev, "T37 page mismatch\n");
+			return -EINVAL;
+		}
+
+		dev_dbg(&data->client->dev, "%s page:%d retries:%d\n",
+			__func__, page, retries);
+
+		/* For remaining pages, write PAGEUP rather than mode */
+		cmd = MXT_DIAGNOSTIC_PAGEUP;
+	}
+
+	return mxt_convert_debug_pages(data, outbuf);
+}
+
+static void mxt_debug_init(struct mxt_data *data)
+{
+	struct mxt_dbg *dbg = &data->dbg;
+	struct mxt_object *object;
+
+	object = mxt_get_object(data, MXT_GEN_COMMAND_T6);
+	if (!object)
+		goto error;
+
+	dbg->diag_cmd_address = object->start_address + MXT_COMMAND_DIAGNOSTIC;
+
+	object = mxt_get_object(data, MXT_DEBUG_DIAGNOSTIC_T37);
+	if (!object)
+		goto error;
+
+	if (mxt_obj_size(object) != sizeof(struct t37_debug)) {
+		dev_warn(&data->client->dev, "Bad T37 size");
+		goto error;
+	}
+
+	dbg->t37_address = object->start_address;
+
+	/* Calculate size of data and allocate buffer */
+	dbg->t37_nodes = data->info.matrix_xsize * data->info.matrix_ysize;
+	dbg->t37_pages = DIV_ROUND_UP(dbg->t37_nodes * sizeof(u16),
+				      sizeof(dbg->t37_buf->data));
+
+	dbg->t37_buf = devm_kmalloc_array(&data->client->dev, dbg->t37_pages,
+					  sizeof(struct t37_debug), GFP_KERNEL);
+	if (!dbg->t37_buf)
+		goto error;
+
+	return;
+
+error:
+	dev_warn(&data->client->dev, "Error initializing T37\n");
+}
+#else
+static void mxt_debug_init(struct mxt_data *data)
+{
+}
+#endif
+
 static int mxt_configure_objects(struct mxt_data *data,
 				 const struct firmware *cfg)
 {
@@ -2070,6 +2227,8 @@ static int mxt_configure_objects(struct mxt_data *data,
 		dev_warn(dev, "No touch object detected\n");
 	}
 
+	mxt_debug_init(data);
+
 	dev_info(dev,
 		 "Family: %u Variant: %u Firmware V%u.%u.%02X Objects: %u\n",
 		 info->family_id, info->variant_id, info->version >> 4,
-- 
2.5.0

[PATCH v6 06/11] Input: atmel_mxt_ts - read touchscreen size

From: Nick Dyer <nick@shmanahar.org>
Date: 2016-06-30 17:48:05

The touchscreen may have a margin where not all the matrix is used. Read
the parameters from T9 and T100 and take account of the difference.

Note: this does not read the XORIGIN/YORIGIN fields so it assumes that
the touchscreen starts at (0,0)

Signed-off-by: Nick Dyer <nick@shmanahar.org>
---
 drivers/input/touchscreen/atmel_mxt_ts.c | 42 +++++++++++++++++++++++++++-----
 1 file changed, 36 insertions(+), 6 deletions(-)
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index bea95a1..d09ecc3 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -103,6 +103,8 @@ struct t7_config {
 
 /* MXT_TOUCH_MULTI_T9 field */
 #define MXT_T9_CTRL		0
+#define MXT_T9_XSIZE		3
+#define MXT_T9_YSIZE		4
 #define MXT_T9_ORIENT		9
 #define MXT_T9_RANGE		18
 
@@ -150,7 +152,9 @@ struct t37_debug {
 #define MXT_T100_CTRL		0
 #define MXT_T100_CFG1		1
 #define MXT_T100_TCHAUX		3
+#define MXT_T100_XSIZE		9
 #define MXT_T100_XRANGE		13
+#define MXT_T100_YSIZE		20
 #define MXT_T100_YRANGE		24
 
 #define MXT_T100_CFG_SWITCHXY	BIT(5)
@@ -259,6 +263,8 @@ struct mxt_data {
 	unsigned int max_x;
 	unsigned int max_y;
 	bool xy_switch;
+	u8 xsize;
+	u8 ysize;
 	bool in_bootloader;
 	u16 mem_size;
 	u8 t100_aux_ampl;
@@ -1714,6 +1720,18 @@ static int mxt_read_t9_resolution(struct mxt_data *data)
 		return -EINVAL;
 
 	error = __mxt_read_reg(client,
+			       object->start_address + MXT_T9_XSIZE,
+			       sizeof(data->xsize), &data->xsize);
+	if (error)
+		return error;
+
+	error = __mxt_read_reg(client,
+			       object->start_address + MXT_T9_YSIZE,
+			       sizeof(data->ysize), &data->ysize);
+	if (error)
+		return error;
+
+	error = __mxt_read_reg(client,
 			       object->start_address + MXT_T9_RANGE,
 			       sizeof(range), &range);
 	if (error)
@@ -1763,6 +1781,18 @@ static int mxt_read_t100_config(struct mxt_data *data)
 
 	data->max_y = get_unaligned_le16(&range_y);
 
+	error = __mxt_read_reg(client,
+			       object->start_address + MXT_T100_XSIZE,
+			       sizeof(data->xsize), &data->xsize);
+	if (error)
+		return error;
+
+	error = __mxt_read_reg(client,
+			       object->start_address + MXT_T100_YSIZE,
+			       sizeof(data->ysize), &data->ysize);
+	if (error)
+		return error;
+
 	/* read orientation config */
 	error =  __mxt_read_reg(client,
 				object->start_address + MXT_T100_CFG1,
@@ -2121,7 +2151,7 @@ static int mxt_convert_debug_pages(struct mxt_data *data, u16 *outbuf)
 		outbuf[i] = mxt_get_debug_value(data, x, y);
 
 		/* Next value */
-		if (++x >= data->info.matrix_xsize) {
+		if (++x >= data->xsize) {
 			x = 0;
 			y++;
 		}
@@ -2276,8 +2306,8 @@ static int mxt_set_input(struct mxt_data *data, unsigned int i)
 	if (i > 0)
 		return -EINVAL;
 
-	f->width = data->info.matrix_xsize;
-	f->height = data->info.matrix_ysize;
+	f->width = data->xsize;
+	f->height = data->ysize;
 	f->pixelformat = V4L2_TCH_FMT_DELTA_TD16;
 	f->field = V4L2_FIELD_NONE;
 	f->colorspace = V4L2_COLORSPACE_RAW;
@@ -2392,9 +2422,9 @@ static void mxt_debug_init(struct mxt_data *data)
 	dbg->t37_address = object->start_address;
 
 	/* Calculate size of data and allocate buffer */
-	dbg->t37_nodes = data->info.matrix_xsize * data->info.matrix_ysize;
-	dbg->t37_pages = DIV_ROUND_UP(dbg->t37_nodes * sizeof(u16),
-				      sizeof(dbg->t37_buf->data));
+	dbg->t37_nodes = data->xsize * data->ysize;
+	dbg->t37_pages = DIV_ROUND_UP(data->xsize * data->info.matrix_ysize *
+				      sizeof(u16), sizeof(dbg->t37_buf->data));
 
 	dbg->t37_buf = devm_kmalloc_array(&data->client->dev, dbg->t37_pages,
 					  sizeof(struct t37_debug), GFP_KERNEL);
-- 
2.5.0

[PATCH v6 08/11] Input: atmel_mxt_ts - add diagnostic data support for mXT1386

From: Nick Dyer <nick@shmanahar.org>
Date: 2016-06-30 17:48:33

The mXT1386 family of chips have a different architecture which splits
the diagnostic data into 3 columns.

Signed-off-by: Nick Dyer <nick@shmanahar.org>
---
 drivers/input/touchscreen/atmel_mxt_ts.c | 31 ++++++++++++++++++++++++++++---
 1 file changed, 28 insertions(+), 3 deletions(-)
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index c35fca0..7c4d937 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -137,6 +137,10 @@ struct t9_range {
 #define MXT_DIAGNOSTIC_DELTAS	0x10
 #define MXT_DIAGNOSTIC_SIZE	128
 
+#define MXT_FAMILY_1386			160
+#define MXT1386_COLUMNS			3
+#define MXT1386_PAGES_PER_COLUMN	8
+
 struct t37_debug {
 #ifdef CONFIG_TOUCHSCREEN_ATMEL_MXT_T37
 	u8 mode;
@@ -2140,13 +2144,27 @@ recheck:
 static u16 mxt_get_debug_value(struct mxt_data *data, unsigned int x,
 			       unsigned int y)
 {
+	struct mxt_info *info = &data->info;
 	struct mxt_dbg *dbg = &data->dbg;
 	unsigned int ofs, page;
+	unsigned int col = 0;
+	unsigned int col_width;
+
+	if (info->family_id == MXT_FAMILY_1386) {
+		col_width = info->matrix_ysize / MXT1386_COLUMNS;
+		col = y / col_width;
+		y = y % col_width;
+	} else {
+		col_width = info->matrix_ysize;
+	}
 
-	ofs = (y + (x * data->info.matrix_ysize)) * sizeof(u16);
+	ofs = (y + (x * col_width)) * sizeof(u16);
 	page = ofs / MXT_DIAGNOSTIC_SIZE;
 	ofs %= MXT_DIAGNOSTIC_SIZE;
 
+	if (info->family_id == MXT_FAMILY_1386)
+		page += col * MXT1386_PAGES_PER_COLUMN;
+
 	return get_unaligned_le16(&dbg->t37_buf[page].data[ofs]);
 }
 
@@ -2416,6 +2434,7 @@ static const struct video_device mxt_video_device = {
 
 static void mxt_debug_init(struct mxt_data *data)
 {
+	struct mxt_info *info = &data->info;
 	struct mxt_dbg *dbg = &data->dbg;
 	struct mxt_object *object;
 	int error;
@@ -2439,8 +2458,14 @@ static void mxt_debug_init(struct mxt_data *data)
 
 	/* Calculate size of data and allocate buffer */
 	dbg->t37_nodes = data->xsize * data->ysize;
-	dbg->t37_pages = DIV_ROUND_UP(data->xsize * data->info.matrix_ysize *
-				      sizeof(u16), sizeof(dbg->t37_buf->data));
+
+	if (info->family_id == MXT_FAMILY_1386)
+		dbg->t37_pages = MXT1386_COLUMNS * MXT1386_PAGES_PER_COLUMN;
+	else
+		dbg->t37_pages = DIV_ROUND_UP(data->xsize *
+					      data->info.matrix_ysize *
+					      sizeof(u16),
+					      sizeof(dbg->t37_buf->data));
 
 	dbg->t37_buf = devm_kmalloc_array(&data->client->dev, dbg->t37_pages,
 					  sizeof(struct t37_debug), GFP_KERNEL);
-- 
2.5.0

[PATCH v6 05/11] Input: atmel_mxt_ts - output diagnostic debug via V4L2 device

From: Nick Dyer <nick@shmanahar.org>
Date: 2016-06-30 17:48:34

Register a video device to output T37 diagnostic data.

Signed-off-by: Nick Dyer <nick@shmanahar.org>
---
 drivers/input/touchscreen/Kconfig        |   6 +-
 drivers/input/touchscreen/atmel_mxt_ts.c | 244 +++++++++++++++++++++++++++++++
 2 files changed, 248 insertions(+), 2 deletions(-)
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index da96ecf..7c1c5ec 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -117,9 +117,11 @@ config TOUCHSCREEN_ATMEL_MXT
 
 config TOUCHSCREEN_ATMEL_MXT_T37
 	bool "Support T37 Diagnostic Data"
-	depends on TOUCHSCREEN_ATMEL_MXT
+	depends on TOUCHSCREEN_ATMEL_MXT && VIDEO_V4L2
+	select VIDEOBUF2_VMALLOC
 	help
-	  Say Y here if you want support for the T37 Diagnostic Data object.
+	  Say Y here if you want support to output data from the T37
+	  Diagnostic Data object using a V4L device.
 
 config TOUCHSCREEN_AUO_PIXCIR
 	tristate "AUO in-cell touchscreen using Pixcir ICs"
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 0048233..bea95a1 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -28,6 +28,10 @@
 #include <linux/of.h>
 #include <linux/slab.h>
 #include <asm/unaligned.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/videobuf2-vmalloc.h>
 
 /* Firmware files */
 #define MXT_FW_NAME		"maxtouch.fw"
@@ -224,6 +228,23 @@ struct mxt_dbg {
 	struct t37_debug *t37_buf;
 	unsigned int t37_pages;
 	unsigned int t37_nodes;
+
+	struct v4l2_device v4l2;
+	struct v4l2_pix_format format;
+	struct video_device vdev;
+	struct vb2_queue queue;
+	struct mutex lock;
+	int input;
+};
+
+static const struct v4l2_file_operations mxt_video_fops = {
+	.owner = THIS_MODULE,
+	.open = v4l2_fh_open,
+	.release = vb2_fop_release,
+	.unlocked_ioctl = video_ioctl2,
+	.read = vb2_fop_read,
+	.mmap = vb2_fop_mmap,
+	.poll = vb2_fop_poll,
 };
 
 /* Each client has this additional data */
@@ -279,6 +300,11 @@ struct mxt_data {
 	struct completion crc_completion;
 };
 
+struct mxt_vb2_buffer {
+	struct vb2_buffer	vb;
+	struct list_head	list;
+};
+
 static size_t mxt_obj_size(const struct mxt_object *obj)
 {
 	return obj->size_minus_one + 1;
@@ -1525,6 +1551,11 @@ static void mxt_free_input_device(struct mxt_data *data)
 
 static void mxt_free_object_table(struct mxt_data *data)
 {
+#ifdef CONFIG_TOUCHSCREEN_ATMEL_MXT_T37
+	video_unregister_device(&data->dbg.vdev);
+	v4l2_device_unregister(&data->dbg.v4l2);
+#endif
+
 	kfree(data->object_table);
 	data->object_table = NULL;
 	kfree(data->msg_buf);
@@ -2157,10 +2188,191 @@ wait_cmd:
 	return mxt_convert_debug_pages(data, outbuf);
 }
 
+static int mxt_queue_setup(struct vb2_queue *q,
+		       unsigned int *nbuffers, unsigned int *nplanes,
+		       unsigned int sizes[], void *alloc_ctxs[])
+{
+	struct mxt_data *data = q->drv_priv;
+	size_t size = data->dbg.t37_nodes * sizeof(u16);
+
+	if (*nplanes)
+		return sizes[0] < size ? -EINVAL : 0;
+
+	*nplanes = 1;
+	sizes[0] = size;
+
+	return 0;
+}
+
+static void mxt_buffer_queue(struct vb2_buffer *vb)
+{
+	struct mxt_data *data = vb2_get_drv_priv(vb->vb2_queue);
+	u16 *ptr;
+	int ret;
+
+	ptr = vb2_plane_vaddr(vb, 0);
+	if (!ptr) {
+		dev_err(&data->client->dev, "Error acquiring frame ptr\n");
+		goto fault;
+	}
+
+	ret = mxt_read_diagnostic_debug(data, MXT_DIAGNOSTIC_DELTAS, ptr);
+	if (ret)
+		goto fault;
+
+	vb2_set_plane_payload(vb, 0, data->dbg.t37_nodes * sizeof(u16));
+	vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+	return;
+
+fault:
+	vb2_buffer_done(vb, VB2_BUF_STATE_ERROR);
+}
+
+/* V4L2 structures */
+static const struct vb2_ops mxt_queue_ops = {
+	.queue_setup		= mxt_queue_setup,
+	.buf_queue		= mxt_buffer_queue,
+	.wait_prepare		= vb2_ops_wait_prepare,
+	.wait_finish		= vb2_ops_wait_finish,
+};
+
+static const struct vb2_queue mxt_queue = {
+	.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+	.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ,
+	.buf_struct_size = sizeof(struct mxt_vb2_buffer),
+	.ops = &mxt_queue_ops,
+	.mem_ops = &vb2_vmalloc_memops,
+	.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC,
+	.min_buffers_needed = 1,
+};
+
+static int mxt_vidioc_querycap(struct file *file, void *priv,
+				 struct v4l2_capability *cap)
+{
+	struct mxt_data *data = video_drvdata(file);
+
+	strlcpy(cap->driver, "atmel_mxt_ts", sizeof(cap->driver));
+	strlcpy(cap->card, "atmel_mxt_ts touch", sizeof(cap->card));
+	snprintf(cap->bus_info, sizeof(cap->bus_info),
+		 "I2C:%s", dev_name(&data->client->dev));
+	return 0;
+}
+
+static int mxt_vidioc_enum_input(struct file *file, void *priv,
+				   struct v4l2_input *i)
+{
+	if (i->index > 0)
+		return -EINVAL;
+
+	i->type = V4L2_INPUT_TYPE_TOUCH;
+	strlcpy(i->name, "Mutual Capacitance Deltas", sizeof(i->name));
+	return 0;
+}
+
+static int mxt_set_input(struct mxt_data *data, unsigned int i)
+{
+	struct v4l2_pix_format *f = &data->dbg.format;
+
+	if (i > 0)
+		return -EINVAL;
+
+	f->width = data->info.matrix_xsize;
+	f->height = data->info.matrix_ysize;
+	f->pixelformat = V4L2_TCH_FMT_DELTA_TD16;
+	f->field = V4L2_FIELD_NONE;
+	f->colorspace = V4L2_COLORSPACE_RAW;
+	f->bytesperline = f->width * sizeof(u16);
+	f->sizeimage = f->width * f->height * sizeof(u16);
+
+	data->dbg.input = i;
+
+	return 0;
+}
+
+static int mxt_vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+	return mxt_set_input(video_drvdata(file), i);
+}
+
+static int mxt_vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+{
+	struct mxt_data *data = video_drvdata(file);
+
+	*i = data->dbg.input;
+
+	return 0;
+}
+
+static int mxt_vidioc_fmt(struct file *file, void *priv, struct v4l2_format *f)
+{
+	struct mxt_data *data = video_drvdata(file);
+
+	f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+	f->fmt.pix = data->dbg.format;
+
+	return 0;
+}
+
+static int mxt_vidioc_enum_fmt(struct file *file, void *priv,
+				 struct v4l2_fmtdesc *fmt)
+{
+	if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	fmt->pixelformat = V4L2_TCH_FMT_DELTA_TD16;
+	return 0;
+}
+
+static int mxt_vidioc_g_parm(struct file *file, void *fh,
+			     struct v4l2_streamparm *a)
+{
+	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	a->parm.capture.readbuffers = 1;
+	a->parm.capture.timeperframe.numerator = 1;
+	a->parm.capture.timeperframe.denominator = 10;
+	return 0;
+}
+
+static const struct v4l2_ioctl_ops mxt_video_ioctl_ops = {
+	.vidioc_querycap        = mxt_vidioc_querycap,
+
+	.vidioc_enum_fmt_vid_cap = mxt_vidioc_enum_fmt,
+	.vidioc_s_fmt_vid_cap   = mxt_vidioc_fmt,
+	.vidioc_g_fmt_vid_cap   = mxt_vidioc_fmt,
+	.vidioc_try_fmt_vid_cap	= mxt_vidioc_fmt,
+	.vidioc_g_parm		= mxt_vidioc_g_parm,
+
+	.vidioc_enum_input      = mxt_vidioc_enum_input,
+	.vidioc_g_input         = mxt_vidioc_g_input,
+	.vidioc_s_input         = mxt_vidioc_s_input,
+
+	.vidioc_reqbufs         = vb2_ioctl_reqbufs,
+	.vidioc_create_bufs     = vb2_ioctl_create_bufs,
+	.vidioc_querybuf        = vb2_ioctl_querybuf,
+	.vidioc_qbuf            = vb2_ioctl_qbuf,
+	.vidioc_dqbuf           = vb2_ioctl_dqbuf,
+	.vidioc_expbuf          = vb2_ioctl_expbuf,
+
+	.vidioc_streamon        = vb2_ioctl_streamon,
+	.vidioc_streamoff       = vb2_ioctl_streamoff,
+};
+
+static const struct video_device mxt_video_device = {
+	.name = "Atmel maxTouch",
+	.fops = &mxt_video_fops,
+	.ioctl_ops = &mxt_video_ioctl_ops,
+	.release = video_device_release_empty,
+	.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TOUCH |
+		       V4L2_CAP_READWRITE | V4L2_CAP_STREAMING,
+};
+
 static void mxt_debug_init(struct mxt_data *data)
 {
 	struct mxt_dbg *dbg = &data->dbg;
 	struct mxt_object *object;
+	int error;
 
 	object = mxt_get_object(data, MXT_GEN_COMMAND_T6);
 	if (!object)
@@ -2189,8 +2401,40 @@ static void mxt_debug_init(struct mxt_data *data)
 	if (!dbg->t37_buf)
 		goto error;
 
+	/* init channel to zero */
+	mxt_set_input(data, 0);
+
+	/* register video device */
+	snprintf(dbg->v4l2.name, sizeof(dbg->v4l2.name), "%s", "atmel_mxt_ts");
+	error = v4l2_device_register(&data->client->dev, &dbg->v4l2);
+	if (error)
+		goto error;
+
+	/* initialize the queue */
+	mutex_init(&dbg->lock);
+	dbg->queue = mxt_queue;
+	dbg->queue.drv_priv = data;
+	dbg->queue.lock = &dbg->lock;
+
+	error = vb2_queue_init(&dbg->queue);
+	if (error)
+		goto error_unreg_v4l2;
+
+	dbg->vdev = mxt_video_device;
+	dbg->vdev.v4l2_dev = &dbg->v4l2;
+	dbg->vdev.lock = &dbg->lock;
+	dbg->vdev.vfl_dir = VFL_DIR_RX;
+	dbg->vdev.queue = &dbg->queue;
+	video_set_drvdata(&dbg->vdev, data);
+
+	error = video_register_device(&dbg->vdev, VFL_TYPE_TOUCH, -1);
+	if (error)
+		goto error_unreg_v4l2;
+
 	return;
 
+error_unreg_v4l2:
+	v4l2_device_unregister(&dbg->v4l2);
 error:
 	dev_warn(&data->client->dev, "Error initializing T37\n");
 }
-- 
2.5.0

[PATCH v6 11/11] Input: sur40 - use new V4L2 touch input type

From: Nick Dyer <nick@shmanahar.org>
Date: 2016-06-30 17:49:08

Support both V4L2_TCH_FMT_TU08 and V4L2_PIX_FMT_GREY for backwards
compatibility.

Note: I have not tested these changes (I have no access to the hardware)
so not signing off.
---
 drivers/input/touchscreen/sur40.c | 121 +++++++++++++++++++++++++++-----------
 1 file changed, 88 insertions(+), 33 deletions(-)
diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c
index 880c40b..9ba68cf 100644
--- a/drivers/input/touchscreen/sur40.c
+++ b/drivers/input/touchscreen/sur40.c
@@ -139,6 +139,27 @@ struct sur40_image_header {
 #define SUR40_GET_STATE   0xc5 /*  4 bytes state (?) */
 #define SUR40_GET_SENSORS 0xb1 /*  8 bytes sensors   */
 
+static const struct v4l2_pix_format sur40_pix_format[] = {
+	{
+		.pixelformat = V4L2_TCH_FMT_TU08,
+		.width  = SENSOR_RES_X / 2,
+		.height = SENSOR_RES_Y / 2,
+		.field = V4L2_FIELD_NONE,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.bytesperline = SENSOR_RES_X / 2,
+		.sizeimage = (SENSOR_RES_X/2) * (SENSOR_RES_Y/2),
+	},
+	{
+		.pixelformat = V4L2_PIX_FMT_GREY,
+		.width  = SENSOR_RES_X / 2,
+		.height = SENSOR_RES_Y / 2,
+		.field = V4L2_FIELD_NONE,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.bytesperline = SENSOR_RES_X / 2,
+		.sizeimage = (SENSOR_RES_X/2) * (SENSOR_RES_Y/2),
+	}
+};
+
 /* master device state */
 struct sur40_state {
 
@@ -149,6 +170,7 @@ struct sur40_state {
 	struct v4l2_device v4l2;
 	struct video_device vdev;
 	struct mutex lock;
+	struct v4l2_pix_format pix_fmt;
 
 	struct vb2_queue queue;
 	struct vb2_alloc_ctx *alloc_ctx;
@@ -170,7 +192,6 @@ struct sur40_buffer {
 
 /* forward declarations */
 static const struct video_device sur40_video_device;
-static const struct v4l2_pix_format sur40_video_format;
 static const struct vb2_queue sur40_queue;
 static void sur40_process_video(struct sur40_state *sur40);
 
@@ -421,7 +442,7 @@ static void sur40_process_video(struct sur40_state *sur40)
 		goto err_poll;
 	}
 
-	if (le32_to_cpu(img->size) != sur40_video_format.sizeimage) {
+	if (le32_to_cpu(img->size) != sur40->pix_fmt.sizeimage) {
 		dev_err(sur40->dev, "image size mismatch\n");
 		goto err_poll;
 	}
@@ -432,7 +453,7 @@ static void sur40_process_video(struct sur40_state *sur40)
 
 	result = usb_sg_init(&sgr, sur40->usbdev,
 		usb_rcvbulkpipe(sur40->usbdev, VIDEO_ENDPOINT), 0,
-		sgt->sgl, sgt->nents, sur40_video_format.sizeimage, 0);
+		sgt->sgl, sgt->nents, sur40->pix_fmt.sizeimage, 0);
 	if (result < 0) {
 		dev_err(sur40->dev, "error %d in usb_sg_init\n", result);
 		goto err_poll;
@@ -593,13 +614,14 @@ static int sur40_probe(struct usb_interface *interface,
 		goto err_unreg_v4l2;
 	}
 
+	sur40->pix_fmt = sur40_pix_format[0];
 	sur40->vdev = sur40_video_device;
 	sur40->vdev.v4l2_dev = &sur40->v4l2;
 	sur40->vdev.lock = &sur40->lock;
 	sur40->vdev.queue = &sur40->queue;
 	video_set_drvdata(&sur40->vdev, sur40);
 
-	error = video_register_device(&sur40->vdev, VFL_TYPE_GRABBER, -1);
+	error = video_register_device(&sur40->vdev, VFL_TYPE_TOUCH, -1);
 	if (error) {
 		dev_err(&interface->dev,
 			"Unable to register video subdevice.");
@@ -662,10 +684,10 @@ static int sur40_queue_setup(struct vb2_queue *q,
 	alloc_ctxs[0] = sur40->alloc_ctx;
 
 	if (*nplanes)
-		return sizes[0] < sur40_video_format.sizeimage ? -EINVAL : 0;
+		return sizes[0] < sur40->pix_fmt.sizeimage ? -EINVAL : 0;
 
 	*nplanes = 1;
-	sizes[0] = sur40_video_format.sizeimage;
+	sizes[0] = sur40->pix_fmt.sizeimage;
 
 	return 0;
 }
@@ -677,7 +699,7 @@ static int sur40_queue_setup(struct vb2_queue *q,
 static int sur40_buffer_prepare(struct vb2_buffer *vb)
 {
 	struct sur40_state *sur40 = vb2_get_drv_priv(vb->vb2_queue);
-	unsigned long size = sur40_video_format.sizeimage;
+	unsigned long size = sur40->pix_fmt.sizeimage;
 
 	if (vb2_plane_size(vb, 0) < size) {
 		dev_err(&sur40->usbdev->dev, "buffer too small (%lu < %lu)\n",
@@ -751,7 +773,7 @@ static int sur40_vidioc_querycap(struct file *file, void *priv,
 	strlcpy(cap->driver, DRIVER_SHORT, sizeof(cap->driver));
 	strlcpy(cap->card, DRIVER_LONG, sizeof(cap->card));
 	usb_make_path(sur40->usbdev, cap->bus_info, sizeof(cap->bus_info));
-	cap->device_caps = V4L2_CAP_VIDEO_CAPTURE |
+	cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TOUCH |
 		V4L2_CAP_READWRITE |
 		V4L2_CAP_STREAMING;
 	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
@@ -763,7 +785,7 @@ static int sur40_vidioc_enum_input(struct file *file, void *priv,
 {
 	if (i->index != 0)
 		return -EINVAL;
-	i->type = V4L2_INPUT_TYPE_CAMERA;
+	i->type = V4L2_INPUT_TYPE_TOUCH;
 	i->std = V4L2_STD_UNKNOWN;
 	strlcpy(i->name, "In-Cell Sensor", sizeof(i->name));
 	i->capabilities = 0;
@@ -781,20 +803,57 @@ static int sur40_vidioc_g_input(struct file *file, void *priv, unsigned int *i)
 	return 0;
 }
 
-static int sur40_vidioc_fmt(struct file *file, void *priv,
+static int sur40_vidioc_try_fmt(struct file *file, void *priv,
 			    struct v4l2_format *f)
 {
-	f->fmt.pix = sur40_video_format;
+	switch (f->fmt.pix.pixelformat) {
+		case V4L2_PIX_FMT_GREY:
+			f->fmt.pix = sur40_pix_format[1];
+			break;
+
+		default:
+			f->fmt.pix = sur40_pix_format[0];
+			break;
+	}
+
+	return 0;
+}
+
+static int sur40_vidioc_s_fmt(struct file *file, void *priv,
+			    struct v4l2_format *f)
+{
+	struct sur40_state *sur40 = video_drvdata(file);
+
+	switch (f->fmt.pix.pixelformat) {
+		case V4L2_PIX_FMT_GREY:
+			sur40->pix_fmt = sur40_pix_format[1];
+			break;
+
+		default:
+			sur40->pix_fmt = sur40_pix_format[0];
+			break;
+	}
+
+	f->fmt.pix = sur40->pix_fmt;
+	return 0;
+}
+
+static int sur40_vidioc_g_fmt(struct file *file, void *priv,
+			    struct v4l2_format *f)
+{
+	struct sur40_state *sur40 = video_drvdata(file);
+
+	f->fmt.pix = sur40->pix_fmt;
 	return 0;
 }
 
 static int sur40_vidioc_enum_fmt(struct file *file, void *priv,
 				 struct v4l2_fmtdesc *f)
 {
-	if (f->index != 0)
+	if (f->index >= ARRAY_SIZE(sur40_pix_format))
 		return -EINVAL;
-	strlcpy(f->description, "8-bit greyscale", sizeof(f->description));
-	f->pixelformat = V4L2_PIX_FMT_GREY;
+
+	f->pixelformat = sur40_pix_format[f->index].pixelformat;
 	f->flags = 0;
 	return 0;
 }
@@ -802,21 +861,27 @@ static int sur40_vidioc_enum_fmt(struct file *file, void *priv,
 static int sur40_vidioc_enum_framesizes(struct file *file, void *priv,
 					struct v4l2_frmsizeenum *f)
 {
-	if ((f->index != 0) || (f->pixel_format != V4L2_PIX_FMT_GREY))
+	struct sur40_state *sur40 = video_drvdata(file);
+
+	if ((f->index != 0) || ((f->pixel_format != V4L2_TCH_FMT_TU08)
+		&& (f->pixel_format != V4L2_PIX_FMT_GREY)))
 		return -EINVAL;
 
 	f->type = V4L2_FRMSIZE_TYPE_DISCRETE;
-	f->discrete.width  = sur40_video_format.width;
-	f->discrete.height = sur40_video_format.height;
+	f->discrete.width  = sur40->pix_fmt.width;
+	f->discrete.height = sur40->pix_fmt.height;
 	return 0;
 }
 
 static int sur40_vidioc_enum_frameintervals(struct file *file, void *priv,
 					    struct v4l2_frmivalenum *f)
 {
-	if ((f->index > 1) || (f->pixel_format != V4L2_PIX_FMT_GREY)
-		|| (f->width  != sur40_video_format.width)
-		|| (f->height != sur40_video_format.height))
+	struct sur40_state *sur40 = video_drvdata(file);
+
+	if ((f->index > 1) || ((f->pixel_format != V4L2_TCH_FMT_TU08)
+	        && (f->pixel_format != V4L2_PIX_FMT_GREY))
+		|| (f->width  != sur40->pix_fmt.width)
+		|| (f->height != sur40->pix_fmt.height))
 			return -EINVAL;
 
 	f->type = V4L2_FRMIVAL_TYPE_DISCRETE;
@@ -873,9 +938,9 @@ static const struct v4l2_ioctl_ops sur40_video_ioctl_ops = {
 	.vidioc_querycap	= sur40_vidioc_querycap,
 
 	.vidioc_enum_fmt_vid_cap = sur40_vidioc_enum_fmt,
-	.vidioc_try_fmt_vid_cap	= sur40_vidioc_fmt,
-	.vidioc_s_fmt_vid_cap	= sur40_vidioc_fmt,
-	.vidioc_g_fmt_vid_cap	= sur40_vidioc_fmt,
+	.vidioc_try_fmt_vid_cap	= sur40_vidioc_try_fmt,
+	.vidioc_s_fmt_vid_cap	= sur40_vidioc_s_fmt,
+	.vidioc_g_fmt_vid_cap	= sur40_vidioc_g_fmt,
 
 	.vidioc_enum_framesizes = sur40_vidioc_enum_framesizes,
 	.vidioc_enum_frameintervals = sur40_vidioc_enum_frameintervals,
@@ -902,16 +967,6 @@ static const struct video_device sur40_video_device = {
 	.release = video_device_release_empty,
 };
 
-static const struct v4l2_pix_format sur40_video_format = {
-	.pixelformat = V4L2_PIX_FMT_GREY,
-	.width  = SENSOR_RES_X / 2,
-	.height = SENSOR_RES_Y / 2,
-	.field = V4L2_FIELD_NONE,
-	.colorspace = V4L2_COLORSPACE_SRGB,
-	.bytesperline = SENSOR_RES_X / 2,
-	.sizeimage = (SENSOR_RES_X/2) * (SENSOR_RES_Y/2),
-};
-
 /* USB-specific object needed to register this driver with the USB subsystem. */
 static struct usb_driver sur40_driver = {
 	.name = DRIVER_SHORT,
-- 
2.5.0

[PATCH v6 10/11] Input: synaptics-rmi4 - add support for F54 diagnostics

From: Nick Dyer <nick@shmanahar.org>
Date: 2016-06-30 17:49:38

Function 54 implements access to various RMI4 diagnostic features.

This patch adds support for retrieving this data. It registers a V4L2
device to output the data to user space.

Signed-off-by: Nick Dyer <nick@shmanahar.org>
---
 drivers/input/rmi4/Kconfig      |  11 +
 drivers/input/rmi4/Makefile     |   1 +
 drivers/input/rmi4/rmi_bus.c    |   3 +
 drivers/input/rmi4/rmi_driver.h |   1 +
 drivers/input/rmi4/rmi_f54.c    | 754 ++++++++++++++++++++++++++++++++++++++++
 5 files changed, 770 insertions(+)
 create mode 100644 drivers/input/rmi4/rmi_f54.c
diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig
index f73df24..f3418b6 100644
--- a/drivers/input/rmi4/Kconfig
+++ b/drivers/input/rmi4/Kconfig
@@ -61,3 +61,14 @@ config RMI4_F30
 
 	  Function 30 provides GPIO and LED support for RMI4 devices. This
 	  includes support for buttons on TouchPads and ClickPads.
+
+config RMI4_F54
+	bool "RMI4 Function 54 (Analog diagnostics)"
+	depends on RMI4_CORE
+	depends on VIDEO_V4L2
+	select VIDEOBUF2_VMALLOC
+	help
+	  Say Y here if you want to add support for RMI4 function 54
+
+	  Function 54 provides access to various diagnostic features in certain
+	  RMI4 touch sensors.
diff --git a/drivers/input/rmi4/Makefile b/drivers/input/rmi4/Makefile
index 95c00a7..0bafc85 100644
--- a/drivers/input/rmi4/Makefile
+++ b/drivers/input/rmi4/Makefile
@@ -7,6 +7,7 @@ rmi_core-$(CONFIG_RMI4_2D_SENSOR) += rmi_2d_sensor.o
 rmi_core-$(CONFIG_RMI4_F11) += rmi_f11.o
 rmi_core-$(CONFIG_RMI4_F12) += rmi_f12.o
 rmi_core-$(CONFIG_RMI4_F30) += rmi_f30.o
+rmi_core-$(CONFIG_RMI4_F54) += rmi_f54.o
 
 # Transports
 obj-$(CONFIG_RMI4_I2C) += rmi_i2c.o
diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c
index b368b05..3aedc65 100644
--- a/drivers/input/rmi4/rmi_bus.c
+++ b/drivers/input/rmi4/rmi_bus.c
@@ -315,6 +315,9 @@ static struct rmi_function_handler *fn_handlers[] = {
 #ifdef CONFIG_RMI4_F30
 	&rmi_f30_handler,
 #endif
+#ifdef CONFIG_RMI4_F54
+	&rmi_f54_handler,
+#endif
 };
 
 static void __rmi_unregister_function_handlers(int start_idx)
diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h
index 6e140fa..8dfbebe 100644
--- a/drivers/input/rmi4/rmi_driver.h
+++ b/drivers/input/rmi4/rmi_driver.h
@@ -102,4 +102,5 @@ extern struct rmi_function_handler rmi_f01_handler;
 extern struct rmi_function_handler rmi_f11_handler;
 extern struct rmi_function_handler rmi_f12_handler;
 extern struct rmi_function_handler rmi_f30_handler;
+extern struct rmi_function_handler rmi_f54_handler;
 #endif
diff --git a/drivers/input/rmi4/rmi_f54.c b/drivers/input/rmi4/rmi_f54.c
new file mode 100644
index 0000000..2361157
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f54.c
@@ -0,0 +1,754 @@
+/*
+ * Copyright (c) 2012-2015 Synaptics Incorporated
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/rmi.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/videobuf2-vmalloc.h>
+#include "rmi_driver.h"
+
+#define F54_NAME		"rmi4_f54"
+
+/* F54 data offsets */
+#define F54_REPORT_DATA_OFFSET  3
+#define F54_FIFO_OFFSET         1
+#define F54_NUM_TX_OFFSET       1
+#define F54_NUM_RX_OFFSET       0
+
+/* F54 commands */
+#define F54_GET_REPORT          1
+#define F54_FORCE_CAL           2
+
+/* Fixed sizes of reports */
+#define F54_QUERY_LEN			27
+
+/* F54 capabilities */
+#define F54_CAP_BASELINE	(1 << 2)
+#define F54_CAP_IMAGE8		(1 << 3)
+#define F54_CAP_IMAGE16		(1 << 6)
+
+/**
+ * enum rmi_f54_report_type - RMI4 F54 report types
+ *
+ * @F54_8BIT_IMAGE:	Normalized 8-Bit Image Report. The capacitance variance
+ *			from baseline for each pixel.
+ *
+ * @F54_16BIT_IMAGE:	Normalized 16-Bit Image Report. The capacitance variance
+ *			from baseline for each pixel.
+ *
+ * @F54_RAW_16BIT_IMAGE:
+ *			Raw 16-Bit Image Report. The raw capacitance for each
+ *			pixel.
+ *
+ * @F54_TRUE_BASELINE:	True Baseline Report. The baseline capacitance for each
+ *			pixel.
+ *
+ * @F54_FULL_RAW_CAP:   Full Raw Capacitance Report. The raw capacitance with
+ *			low reference set to its minimum value and high
+ *			reference set to its maximum value.
+ *
+ * @F54_FULL_RAW_CAP_RX_OFFSET_REMOVED:
+ *			Full Raw Capacitance with Receiver Offset Removed
+ *			Report. Set Low reference to its minimum value and high
+ *			references to its maximum value, then report the raw
+ *			capacitance for each pixel.
+ */
+enum rmi_f54_report_type {
+	F54_REPORT_NONE = 0,
+	F54_8BIT_IMAGE = 1,
+	F54_16BIT_IMAGE = 2,
+	F54_RAW_16BIT_IMAGE = 3,
+	F54_TRUE_BASELINE = 9,
+	F54_FULL_RAW_CAP = 19,
+	F54_FULL_RAW_CAP_RX_OFFSET_REMOVED = 20,
+	F54_MAX_REPORT_TYPE,
+};
+
+const char *rmi_f54_report_type_names[] = {
+	[F54_REPORT_NONE]		= "Unknown",
+	[F54_8BIT_IMAGE]		= "Normalized 8-Bit Image",
+	[F54_16BIT_IMAGE]		= "Normalized 16-Bit Image",
+	[F54_RAW_16BIT_IMAGE]		= "Raw 16-Bit Image",
+	[F54_TRUE_BASELINE]		= "True Baseline",
+	[F54_FULL_RAW_CAP]		= "Full Raw Capacitance",
+	[F54_FULL_RAW_CAP_RX_OFFSET_REMOVED]
+					= "Full Raw Capacitance RX Offset Removed",
+};
+
+struct rmi_f54_reports {
+	int start;
+	int size;
+};
+
+struct f54_data {
+	struct rmi_function *fn;
+
+	u8 qry[F54_QUERY_LEN];
+	u8 num_rx_electrodes;
+	u8 num_tx_electrodes;
+	u8 capabilities;
+	u16 clock_rate;
+	u8 family;
+
+	enum rmi_f54_report_type report_type;
+	u8 *report_data;
+	int report_size;
+	struct rmi_f54_reports standard_report[2];
+
+	bool is_busy;
+	struct mutex status_mutex;
+	struct mutex data_mutex;
+
+	struct workqueue_struct *workqueue;
+	struct delayed_work work;
+	unsigned long timeout;
+
+	struct completion cmd_done;
+
+	/* V4L2 support */
+	struct v4l2_device v4l2;
+	struct v4l2_pix_format format;
+	struct video_device vdev;
+	struct vb2_queue queue;
+	struct mutex lock;
+	int input;
+	enum rmi_f54_report_type inputs[F54_MAX_REPORT_TYPE];
+};
+
+/*
+ * Basic checks on report_type to ensure we write a valid type
+ * to the sensor.
+ */
+static bool is_f54_report_type_valid(struct f54_data *f54,
+				     enum rmi_f54_report_type reptype)
+{
+	switch (reptype) {
+	case F54_8BIT_IMAGE:
+		return f54->capabilities & F54_CAP_IMAGE8;
+	case F54_16BIT_IMAGE:
+	case F54_RAW_16BIT_IMAGE:
+		return f54->capabilities & F54_CAP_IMAGE16;
+	case F54_TRUE_BASELINE:
+		return f54->capabilities & F54_CAP_IMAGE16;
+	case F54_FULL_RAW_CAP:
+	case F54_FULL_RAW_CAP_RX_OFFSET_REMOVED:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static enum rmi_f54_report_type rmi_f54_get_reptype(struct f54_data *f54,
+						unsigned int i)
+{
+	if (i >= F54_MAX_REPORT_TYPE)
+		return F54_REPORT_NONE;
+
+	return f54->inputs[i];
+}
+
+static void rmi_f54_create_input_map(struct f54_data *f54)
+{
+	int i = 0;
+	enum rmi_f54_report_type reptype;
+
+	for (reptype = 1; reptype < F54_MAX_REPORT_TYPE; reptype++) {
+		if (!is_f54_report_type_valid(f54, reptype))
+			continue;
+
+		f54->inputs[i++] = reptype;
+	}
+
+	/* Remaining values are zero via kzalloc */
+}
+
+static int rmi_f54_request_report(struct rmi_function *fn, u8 report_type)
+{
+	struct f54_data *f54 = dev_get_drvdata(&fn->dev);
+	struct rmi_device *rmi_dev = fn->rmi_dev;
+	int error;
+
+	/* Write Report Type into F54_AD_Data0 */
+	if (f54->report_type != report_type) {
+		error = rmi_write(rmi_dev, f54->fn->fd.data_base_addr,
+				  report_type);
+		if (error)
+			return error;
+		f54->report_type = report_type;
+	}
+
+	/*
+	 * Small delay after disabling interrupts to avoid race condition
+	 * in firmare. This value is a bit higher than absolutely necessary.
+	 * Should be removed once issue is resolved in firmware.
+	 */
+	usleep_range(2000, 3000);
+
+	mutex_lock(&f54->data_mutex);
+
+	error = rmi_write(rmi_dev, fn->fd.command_base_addr, F54_GET_REPORT);
+	if (error < 0)
+		return error;
+
+	init_completion(&f54->cmd_done);
+
+	f54->is_busy = 1;
+	f54->timeout = jiffies + msecs_to_jiffies(100);
+
+	queue_delayed_work(f54->workqueue, &f54->work, 0);
+
+	mutex_unlock(&f54->data_mutex);
+
+	return 0;
+}
+
+static size_t rmi_f54_get_report_size(struct f54_data *f54)
+{
+	u8 rx = f54->num_rx_electrodes ? : f54->num_rx_electrodes;
+	u8 tx = f54->num_tx_electrodes ? : f54->num_tx_electrodes;
+	size_t size;
+
+	switch (rmi_f54_get_reptype(f54, f54->input)) {
+	case F54_8BIT_IMAGE:
+		size = rx * tx;
+		break;
+	case F54_16BIT_IMAGE:
+	case F54_RAW_16BIT_IMAGE:
+	case F54_TRUE_BASELINE:
+	case F54_FULL_RAW_CAP:
+	case F54_FULL_RAW_CAP_RX_OFFSET_REMOVED:
+		size = sizeof(u16) * rx * tx;
+		break;
+	default:
+		size = 0;
+	}
+
+	return size;
+}
+
+static int rmi_f54_get_pixel_fmt(enum rmi_f54_report_type reptype, u32 *pixfmt)
+{
+	int ret = 0;
+
+	switch (reptype) {
+	case F54_8BIT_IMAGE:
+		*pixfmt = V4L2_TCH_FMT_DELTA_TD08;
+		break;
+
+	case F54_16BIT_IMAGE:
+		*pixfmt = V4L2_TCH_FMT_DELTA_TD16;
+		break;
+
+	case F54_RAW_16BIT_IMAGE:
+	case F54_TRUE_BASELINE:
+	case F54_FULL_RAW_CAP:
+	case F54_FULL_RAW_CAP_RX_OFFSET_REMOVED:
+		*pixfmt = V4L2_TCH_FMT_TU16;
+		break;
+
+	case F54_REPORT_NONE:
+	case F54_MAX_REPORT_TYPE:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static const struct v4l2_file_operations rmi_f54_video_fops = {
+	.owner = THIS_MODULE,
+	.open = v4l2_fh_open,
+	.release = vb2_fop_release,
+	.unlocked_ioctl = video_ioctl2,
+	.read = vb2_fop_read,
+	.mmap = vb2_fop_mmap,
+	.poll = vb2_fop_poll,
+};
+
+static int rmi_f54_queue_setup(struct vb2_queue *q,
+			       unsigned int *nbuffers, unsigned int *nplanes,
+			       unsigned int sizes[], void *alloc_ctxs[])
+{
+	struct f54_data *f54 = q->drv_priv;
+
+	if (*nplanes)
+		return sizes[0] < rmi_f54_get_report_size(f54) ? -EINVAL : 0;
+
+	*nplanes = 1;
+	sizes[0] = rmi_f54_get_report_size(f54);
+
+	return 0;
+}
+
+static void rmi_f54_buffer_queue(struct vb2_buffer *vb)
+{
+	struct f54_data *f54 = vb2_get_drv_priv(vb->vb2_queue);
+	u16 *ptr;
+	enum vb2_buffer_state state;
+	enum rmi_f54_report_type reptype;
+	int ret;
+
+	mutex_lock(&f54->status_mutex);
+
+	reptype = rmi_f54_get_reptype(f54, f54->input);
+	if (reptype == F54_REPORT_NONE) {
+		state = VB2_BUF_STATE_ERROR;
+		goto done;
+	}
+
+	if (f54->is_busy) {
+		state = VB2_BUF_STATE_ERROR;
+		goto done;
+	}
+
+	ret = rmi_f54_request_report(f54->fn, reptype);
+	if (ret) {
+		dev_err(&f54->fn->dev, "Error requesting F54 report\n");
+		state = VB2_BUF_STATE_ERROR;
+		goto done;
+	}
+
+	/* get frame data */
+	mutex_lock(&f54->data_mutex);
+
+	while (f54->is_busy) {
+		mutex_unlock(&f54->data_mutex);
+		if (!wait_for_completion_timeout(&f54->cmd_done,
+						 msecs_to_jiffies(1000))) {
+			dev_err(&f54->fn->dev, "Timed out\n");
+			state = VB2_BUF_STATE_ERROR;
+			goto done;
+		}
+		mutex_lock(&f54->data_mutex);
+	}
+
+	ptr = vb2_plane_vaddr(vb, 0);
+	if (!ptr) {
+		dev_err(&f54->fn->dev, "Error acquiring frame ptr\n");
+		state = VB2_BUF_STATE_ERROR;
+		goto data_done;
+	}
+
+	memcpy(ptr, f54->report_data, f54->report_size);
+	vb2_set_plane_payload(vb, 0, rmi_f54_get_report_size(f54));
+	state = VB2_BUF_STATE_DONE;
+
+data_done:
+	mutex_unlock(&f54->data_mutex);
+done:
+	vb2_buffer_done(vb, state);
+	mutex_unlock(&f54->status_mutex);
+}
+
+/* V4L2 structures */
+static const struct vb2_ops rmi_f54_queue_ops = {
+	.queue_setup            = rmi_f54_queue_setup,
+	.buf_queue              = rmi_f54_buffer_queue,
+	.wait_prepare           = vb2_ops_wait_prepare,
+	.wait_finish            = vb2_ops_wait_finish,
+};
+
+static const struct vb2_queue rmi_f54_queue = {
+	.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+	.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ,
+	.buf_struct_size = sizeof(struct vb2_buffer),
+	.ops = &rmi_f54_queue_ops,
+	.mem_ops = &vb2_vmalloc_memops,
+	.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC,
+	.min_buffers_needed = 1,
+};
+
+static int rmi_f54_vidioc_querycap(struct file *file, void *priv,
+				   struct v4l2_capability *cap)
+{
+	struct f54_data *f54 = video_drvdata(file);
+
+	strlcpy(cap->driver, F54_NAME, sizeof(cap->driver));
+	strlcpy(cap->card, SYNAPTICS_INPUT_DEVICE_NAME, sizeof(cap->card));
+	strlcpy(cap->bus_info, dev_name(&f54->fn->dev), sizeof(cap->bus_info));
+
+	return 0;
+}
+
+static int rmi_f54_vidioc_enum_input(struct file *file, void *priv,
+				     struct v4l2_input *i)
+{
+	struct f54_data *f54 = video_drvdata(file);
+	enum rmi_f54_report_type reptype;
+
+	reptype = rmi_f54_get_reptype(f54, i->index);
+	if (reptype == F54_REPORT_NONE)
+		return -EINVAL;
+
+	i->type = V4L2_INPUT_TYPE_TOUCH;
+
+	strlcpy(i->name, rmi_f54_report_type_names[reptype], sizeof(i->name));
+	return 0;
+}
+
+static int rmi_f54_set_input(struct f54_data *f54, unsigned int i)
+{
+	struct v4l2_pix_format *f = &f54->format;
+	enum rmi_f54_report_type reptype;
+	int ret;
+
+	reptype = rmi_f54_get_reptype(f54, i);
+	if (reptype == F54_REPORT_NONE)
+		return -EINVAL;
+
+	ret = rmi_f54_get_pixel_fmt(reptype, &f->pixelformat);
+	if (ret)
+		return ret;
+
+	f54->input = i;
+
+	f->width = f54->num_rx_electrodes;
+	f->height = f54->num_tx_electrodes;
+	f->field = V4L2_FIELD_NONE;
+	f->colorspace = V4L2_COLORSPACE_RAW;
+	f->bytesperline = f->width * sizeof(u16);
+	f->sizeimage = f->width * f->height * sizeof(u16);
+
+	return 0;
+}
+
+static int rmi_f54_vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+	return rmi_f54_set_input(video_drvdata(file), i);
+}
+
+static int rmi_f54_vidioc_g_input(struct file *file, void *priv,
+				  unsigned int *i)
+{
+	struct f54_data *f54 = video_drvdata(file);
+
+	*i = f54->input;
+
+	return 0;
+}
+
+static int rmi_f54_vidioc_fmt(struct file *file, void *priv,
+			      struct v4l2_format *f)
+{
+	struct f54_data *f54 = video_drvdata(file);
+
+	f->fmt.pix = f54->format;
+
+	return 0;
+}
+
+static int rmi_f54_vidioc_enum_fmt(struct file *file, void *priv,
+				   struct v4l2_fmtdesc *fmt)
+{
+	if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	switch (fmt->index) {
+	case 0:
+		fmt->pixelformat = V4L2_TCH_FMT_DELTA_TD16;
+		break;
+
+	case 1:
+		fmt->pixelformat = V4L2_TCH_FMT_DELTA_TD08;
+		break;
+
+	case 2:
+		fmt->pixelformat = V4L2_TCH_FMT_TU16;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int rmi_f54_vidioc_g_parm(struct file *file, void *fh,
+				 struct v4l2_streamparm *a)
+{
+	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	a->parm.capture.readbuffers = 1;
+	a->parm.capture.timeperframe.numerator = 1;
+	a->parm.capture.timeperframe.denominator = 10;
+	return 0;
+}
+
+static const struct v4l2_ioctl_ops rmi_f54_video_ioctl_ops = {
+	.vidioc_querycap	= rmi_f54_vidioc_querycap,
+
+	.vidioc_enum_fmt_vid_cap = rmi_f54_vidioc_enum_fmt,
+	.vidioc_s_fmt_vid_cap	= rmi_f54_vidioc_fmt,
+	.vidioc_g_fmt_vid_cap	= rmi_f54_vidioc_fmt,
+	.vidioc_try_fmt_vid_cap	= rmi_f54_vidioc_fmt,
+	.vidioc_g_parm		= rmi_f54_vidioc_g_parm,
+
+	.vidioc_enum_input	= rmi_f54_vidioc_enum_input,
+	.vidioc_g_input		= rmi_f54_vidioc_g_input,
+	.vidioc_s_input		= rmi_f54_vidioc_s_input,
+
+	.vidioc_reqbufs		= vb2_ioctl_reqbufs,
+	.vidioc_create_bufs	= vb2_ioctl_create_bufs,
+	.vidioc_querybuf	= vb2_ioctl_querybuf,
+	.vidioc_qbuf		= vb2_ioctl_qbuf,
+	.vidioc_dqbuf		= vb2_ioctl_dqbuf,
+	.vidioc_expbuf		= vb2_ioctl_expbuf,
+
+	.vidioc_streamon	= vb2_ioctl_streamon,
+	.vidioc_streamoff	= vb2_ioctl_streamoff,
+};
+
+static const struct video_device rmi_f54_video_device = {
+	.name = "Synaptics RMI4",
+	.fops = &rmi_f54_video_fops,
+	.ioctl_ops = &rmi_f54_video_ioctl_ops,
+	.release = video_device_release_empty,
+	.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TOUCH |
+		       V4L2_CAP_READWRITE | V4L2_CAP_STREAMING,
+};
+
+static void rmi_f54_work(struct work_struct *work)
+{
+	struct f54_data *f54 = container_of(work, struct f54_data, work.work);
+	struct rmi_function *fn = f54->fn;
+	u8 fifo[2];
+	struct rmi_f54_reports *report;
+	int report_size;
+	u8 command;
+	u8 *data;
+	int error;
+
+	data = f54->report_data;
+	report_size = rmi_f54_get_report_size(f54);
+	if (report_size == 0) {
+		dev_err(&fn->dev, "Bad report size, report type=%d\n",
+				f54->report_type);
+		error = -EINVAL;
+		goto error;     /* retry won't help */
+	}
+	f54->standard_report[0].size = report_size;
+	report = f54->standard_report;
+
+	mutex_lock(&f54->data_mutex);
+
+	/*
+	 * Need to check if command has completed.
+	 * If not try again later.
+	 */
+	error = rmi_read(fn->rmi_dev, f54->fn->fd.command_base_addr,
+			 &command);
+	if (error) {
+		dev_err(&fn->dev, "Failed to read back command\n");
+		goto error;
+	}
+	if (command & F54_GET_REPORT) {
+		if (time_after(jiffies, f54->timeout)) {
+			dev_err(&fn->dev, "Get report command timed out\n");
+			error = -ETIMEDOUT;
+		}
+		report_size = 0;
+		goto error;
+	}
+
+	rmi_dbg(RMI_DEBUG_FN, &fn->dev, "Get report command completed, reading data\n");
+
+	report_size = 0;
+	for (; report->size; report++) {
+		fifo[0] = report->start & 0xff;
+		fifo[1] = (report->start >> 8) & 0xff;
+		error = rmi_write_block(fn->rmi_dev,
+					fn->fd.data_base_addr + F54_FIFO_OFFSET,
+					fifo, sizeof(fifo));
+		if (error) {
+			dev_err(&fn->dev, "Failed to set fifo start offset\n");
+			goto abort;
+		}
+
+		error = rmi_read_block(fn->rmi_dev, fn->fd.data_base_addr +
+				       F54_REPORT_DATA_OFFSET, data,
+				       report->size);
+		if (error) {
+			dev_err(&fn->dev, "%s: read [%d bytes] returned %d\n",
+				__func__, report->size, error);
+			goto abort;
+		}
+		data += report->size;
+		report_size += report->size;
+	}
+
+abort:
+	f54->report_size = error ? 0 : report_size;
+error:
+	if (error)
+		report_size = 0;
+
+	if (report_size == 0 && !error) {
+		queue_delayed_work(f54->workqueue, &f54->work,
+				   msecs_to_jiffies(1));
+	} else {
+		f54->is_busy = false;
+		complete(&f54->cmd_done);
+	}
+
+	mutex_unlock(&f54->data_mutex);
+}
+
+static int rmi_f54_attention(struct rmi_function *fn, unsigned long *irqbits)
+{
+	return 0;
+}
+
+static int rmi_f54_config(struct rmi_function *fn)
+{
+	struct rmi_driver *drv = fn->rmi_dev->driver;
+
+	drv->set_irq_bits(fn->rmi_dev, fn->irq_mask);
+
+	return 0;
+}
+
+static int rmi_f54_detect(struct rmi_function *fn)
+{
+	int error;
+	struct f54_data *f54;
+
+	f54 = dev_get_drvdata(&fn->dev);
+
+	error = rmi_read_block(fn->rmi_dev, fn->fd.query_base_addr,
+			       &f54->qry, sizeof(f54->qry));
+	if (error) {
+		dev_err(&fn->dev, "%s: Failed to query F54 properties\n",
+			__func__);
+		return error;
+	}
+
+	f54->num_rx_electrodes = f54->qry[0];
+	f54->num_tx_electrodes = f54->qry[1];
+	f54->capabilities = f54->qry[2];
+	f54->clock_rate = f54->qry[3] | (f54->qry[4] << 8);
+	f54->family = f54->qry[5];
+
+	rmi_dbg(RMI_DEBUG_FN, &fn->dev, "F54 num_rx_electrodes: %d\n",
+		f54->num_rx_electrodes);
+	rmi_dbg(RMI_DEBUG_FN, &fn->dev, "F54 num_tx_electrodes: %d\n",
+		f54->num_tx_electrodes);
+	rmi_dbg(RMI_DEBUG_FN, &fn->dev, "F54 capabilities: 0x%x\n",
+		f54->capabilities);
+	rmi_dbg(RMI_DEBUG_FN, &fn->dev, "F54 clock rate: 0x%x\n",
+		f54->clock_rate);
+	rmi_dbg(RMI_DEBUG_FN, &fn->dev, "F54 family: 0x%x\n",
+		f54->family);
+
+	f54->is_busy = false;
+
+	return 0;
+}
+
+static int rmi_f54_probe(struct rmi_function *fn)
+{
+	struct f54_data *f54;
+	int ret;
+	u8 rx, tx;
+
+	f54 = devm_kzalloc(&fn->dev, sizeof(struct f54_data), GFP_KERNEL);
+	if (!f54)
+		return -ENOMEM;
+
+	f54->fn = fn;
+	dev_set_drvdata(&fn->dev, f54);
+
+	ret = rmi_f54_detect(fn);
+	if (ret)
+		return ret;
+
+	mutex_init(&f54->data_mutex);
+	mutex_init(&f54->status_mutex);
+
+	rx = f54->num_rx_electrodes;
+	tx = f54->num_tx_electrodes;
+	f54->report_data = devm_kzalloc(&fn->dev,
+					sizeof(u16) * tx * rx,
+					GFP_KERNEL);
+	if (f54->report_data == NULL)
+		return -ENOMEM;
+
+	INIT_DELAYED_WORK(&f54->work, rmi_f54_work);
+
+	f54->workqueue = create_singlethread_workqueue("rmi4-poller");
+	if (!f54->workqueue)
+		return -ENOMEM;
+
+	rmi_f54_create_input_map(f54);
+
+	/* register video device */
+	strlcpy(f54->v4l2.name, F54_NAME, sizeof(f54->v4l2.name));
+	ret = v4l2_device_register(&fn->dev, &f54->v4l2);
+	if (ret) {
+		dev_err(&fn->dev, "Unable to register video dev.\n");
+		goto remove_wq;
+	}
+
+	/* initialize the queue */
+	mutex_init(&f54->lock);
+	f54->queue = rmi_f54_queue;
+	f54->queue.drv_priv = f54;
+	f54->queue.lock = &f54->lock;
+
+	ret = vb2_queue_init(&f54->queue);
+	if (ret)
+		goto remove_v4l2;
+
+	f54->vdev = rmi_f54_video_device;
+	f54->vdev.v4l2_dev = &f54->v4l2;
+	f54->vdev.lock = &f54->lock;
+	f54->vdev.vfl_dir = VFL_DIR_RX;
+	f54->vdev.queue = &f54->queue;
+	video_set_drvdata(&f54->vdev, f54);
+
+	ret = video_register_device(&f54->vdev, VFL_TYPE_TOUCH, -1);
+	if (ret) {
+		dev_err(&fn->dev, "Unable to register video subdevice.");
+		goto remove_v4l2;
+	}
+
+	return 0;
+
+remove_v4l2:
+	v4l2_device_unregister(&f54->v4l2);
+remove_wq:
+	cancel_delayed_work_sync(&f54->work);
+	flush_workqueue(f54->workqueue);
+	destroy_workqueue(f54->workqueue);
+	return ret;
+}
+
+static void rmi_f54_remove(struct rmi_function *fn)
+{
+	struct f54_data *f54 = dev_get_drvdata(&fn->dev);
+
+	video_unregister_device(&f54->vdev);
+	v4l2_device_unregister(&f54->v4l2);
+}
+
+struct rmi_function_handler rmi_f54_handler = {
+	.driver = {
+		.name = F54_NAME,
+	},
+	.func = 0x54,
+	.probe = rmi_f54_probe,
+	.config = rmi_f54_config,
+	.attention = rmi_f54_attention,
+	.remove = rmi_f54_remove,
+};
-- 
2.5.0

[PATCH v6 01/11] Input: atmel_mxt_ts - update MAINTAINERS email address

From: Nick Dyer <nick@shmanahar.org>
Date: 2016-06-30 17:51:52

I'm leaving ITDev, so change to my personal email. My understanding is
that someone at Atmel will take this on once their takeover by Microchip
has settled down.

Signed-off-by: Nick Dyer <nick@shmanahar.org>
---
 MAINTAINERS | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/MAINTAINERS b/MAINTAINERS
index 0f148d3..6affed5 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2181,9 +2181,9 @@ S:	Maintained
 F:	drivers/net/wireless/atmel/atmel*
 
 ATMEL MAXTOUCH DRIVER
-M:	Nick Dyer <nick.dyer@itdev.co.uk>
-T:	git git://github.com/atmel-maxtouch/linux.git
-S:	Supported
+M:	Nick Dyer <nick@shmanahar.org>
+T:	git git://github.com/ndyer/linux.git
+S:	Maintained
 F:	Documentation/devicetree/bindings/input/atmel,maxtouch.txt
 F:	drivers/input/touchscreen/atmel_mxt_ts.c
 F:	include/linux/platform_data/atmel_mxt_ts.h
-- 
2.5.0

[PATCH] v4l2-compliance: Changes to support touch sensors

From: Nick Dyer <nick@shmanahar.org>
Date: 2016-06-30 17:51:53

Signed-off-by: Nick Dyer <nick@shmanahar.org>
---
 utils/v4l2-compliance/v4l2-compliance.cpp        | 48 +++++++++++++++++++++++-
 utils/v4l2-compliance/v4l2-compliance.h          |  1 +
 utils/v4l2-compliance/v4l2-test-input-output.cpp |  4 +-
 3 files changed, 51 insertions(+), 2 deletions(-)
diff --git a/utils/v4l2-compliance/v4l2-compliance.cpp b/utils/v4l2-compliance/v4l2-compliance.cpp
index 48dc8b4..6bd4653 100644
--- a/utils/v4l2-compliance/v4l2-compliance.cpp
+++ b/utils/v4l2-compliance/v4l2-compliance.cpp
@@ -55,6 +55,7 @@ enum Option {
 	OptSetRadioDevice = 'r',
 	OptStreaming = 's',
 	OptSetSWRadioDevice = 'S',
+	OptSetTouchDevice = 't',
 	OptTrace = 'T',
 	OptVerbose = 'v',
 	OptSetVbiDevice = 'V',
@@ -105,6 +106,7 @@ static struct option long_options[] = {
 	{"vbi-device", required_argument, 0, OptSetVbiDevice},
 	{"sdr-device", required_argument, 0, OptSetSWRadioDevice},
 	{"expbuf-device", required_argument, 0, OptSetExpBufDevice},
+	{"touch-device", required_argument, 0, OptSetTouchDevice},
 	{"help", no_argument, 0, OptHelp},
 	{"verbose", no_argument, 0, OptVerbose},
 	{"no-warnings", no_argument, 0, OptNoWarnings},
@@ -134,6 +136,9 @@ static void usage(void)
 	printf("  -S, --sdr-device=<dev>\n");
 	printf("                     Use device <dev> as the SDR device.\n");
 	printf("                     If <dev> starts with a digit, then /dev/swradio<dev> is used.\n");
+	printf("  -t, --touch-device=<dev>\n");
+	printf("                     Use device <dev> as the touch device.\n");
+	printf("                     If <dev> starts with a digit, then /dev/v4l-touch<dev> is used.\n");
 	printf("  -e, --expbuf-device=<dev>\n");
 	printf("                     Use device <dev> to obtain DMABUF handles.\n");
 	printf("                     If <dev> starts with a digit, then /dev/video<dev> is used.\n");
@@ -206,6 +211,8 @@ std::string cap2s(unsigned cap)
 		s += "\t\tSDR Capture\n";
 	if (cap & V4L2_CAP_SDR_OUTPUT)
 		s += "\t\tSDR Output\n";
+	if (cap & V4L2_CAP_TOUCH)
+		s += "\t\tTouch Capture\n";
 	if (cap & V4L2_CAP_TUNER)
 		s += "\t\tTuner\n";
 	if (cap & V4L2_CAP_HW_FREQ_SEEK)
@@ -673,6 +680,8 @@ int main(int argc, char **argv)
 	struct node radio_node2;
 	struct node sdr_node;
 	struct node sdr_node2;
+	struct node touch_node;
+	struct node touch_node2;
 	struct node expbuf_node;
 
 	/* command args */
@@ -682,6 +691,7 @@ int main(int argc, char **argv)
 	const char *vbi_device = NULL;		/* -V device */
 	const char *radio_device = NULL;	/* -r device */
 	const char *sdr_device = NULL;		/* -S device */
+	const char *touch_device = NULL;	/* -t device */
 	const char *expbuf_device = NULL;	/* --expbuf-device device */
 	struct v4l2_capability vcap;		/* list_cap */
 	unsigned frame_count = 60;
@@ -750,6 +760,15 @@ int main(int argc, char **argv)
 				sdr_device = newdev;
 			}
 			break;
+		case OptSetTouchDevice:
+			touch_device = optarg;
+			if (touch_device[0] >= '0' && touch_device[0] <= '9' && strlen(touch_device) <= 3) {
+				static char newdev[20];
+
+				sprintf(newdev, "/dev/v4l-touch%s", touch_device);
+				touch_device = newdev;
+			}
+			break;
 		case OptSetExpBufDevice:
 			expbuf_device = optarg;
 			if (expbuf_device[0] >= '0' && expbuf_device[0] <= '9' && strlen(expbuf_device) <= 3) {
@@ -839,7 +858,8 @@ int main(int argc, char **argv)
 	if (v1 == 2 && v2 == 6)
 		kernel_version = v3;
 
-	if (!video_device && !vbi_device && !radio_device && !sdr_device)
+	if (!video_device && !vbi_device && !radio_device &&
+	    !sdr_device && !touch_device)
 		video_device = "/dev/video0";
 
 	if (video_device) {
@@ -886,6 +906,17 @@ int main(int argc, char **argv)
 		}
 	}
 
+	if (touch_device) {
+		touch_node.s_trace(options[OptTrace]);
+		touch_node.s_direct(direct);
+		fd = touch_node.open(touch_device, false);
+		if (fd < 0) {
+			fprintf(stderr, "Failed to open %s: %s\n", touch_device,
+				strerror(errno));
+			exit(1);
+		}
+	}
+
 	if (expbuf_device) {
 		expbuf_node.s_trace(options[OptTrace]);
 		expbuf_node.s_direct(true);
@@ -913,6 +944,10 @@ int main(int argc, char **argv)
 		node = sdr_node;
 		device = sdr_device;
 		node.is_sdr = true;
+	} else if (touch_node.g_fd() >= 0) {
+		node = touch_node;
+		device = touch_device;
+		node.is_touch = true;
 	}
 	node.device = device;
 
@@ -1013,6 +1048,17 @@ int main(int argc, char **argv)
 			node.node2 = &sdr_node2;
 		}
 	}
+	if (touch_device) {
+		touch_node2 = node;
+		printf("\ttest second touch open: %s\n",
+				ok(touch_node2.open(touch_device, false) >= 0 ? 0 : errno));
+		if (touch_node2.g_fd() >= 0) {
+			printf("\ttest VIDIOC_QUERYCAP: %s\n", ok(testCap(&touch_node2)));
+			printf("\ttest VIDIOC_G/S_PRIORITY: %s\n",
+					ok(testPrio(&node, &touch_node2)));
+			node.node2 = &touch_node2;
+		}
+	}
 	printf("\n");
 
 	storeState(&node);
diff --git a/utils/v4l2-compliance/v4l2-compliance.h b/utils/v4l2-compliance/v4l2-compliance.h
index 67ecbf5..60432b1 100644
--- a/utils/v4l2-compliance/v4l2-compliance.h
+++ b/utils/v4l2-compliance/v4l2-compliance.h
@@ -68,6 +68,7 @@ struct base_node {
 	bool is_radio;
 	bool is_vbi;
 	bool is_sdr;
+	bool is_touch;
 	bool is_m2m;
 	bool is_planar;
 	bool can_capture;
diff --git a/utils/v4l2-compliance/v4l2-test-input-output.cpp b/utils/v4l2-compliance/v4l2-test-input-output.cpp
index 05daf85..3b56968 100644
--- a/utils/v4l2-compliance/v4l2-test-input-output.cpp
+++ b/utils/v4l2-compliance/v4l2-test-input-output.cpp
@@ -371,7 +371,9 @@ static int checkInput(struct node *node, const struct v4l2_input &descr, unsigne
 		return fail("invalid index\n");
 	if (check_ustring(descr.name, sizeof(descr.name)))
 		return fail("invalid name\n");
-	if (descr.type != V4L2_INPUT_TYPE_TUNER && descr.type != V4L2_INPUT_TYPE_CAMERA)
+	if (descr.type != V4L2_INPUT_TYPE_TUNER &&
+      descr.type != V4L2_INPUT_TYPE_CAMERA &&
+      descr.type != V4L2_INPUT_TYPE_TOUCH)
 		return fail("invalid type\n");
 	if (descr.type == V4L2_INPUT_TYPE_CAMERA && descr.tuner)
 		return fail("invalid tuner\n");
-- 
2.5.0

Re: [PATCH v6 10/11] Input: synaptics-rmi4 - add support for F54 diagnostics

From: Nick Dyer <nick@shmanahar.org>
Date: 2016-07-01 08:51:15

On Thu, Jun 30, 2016 at 06:38:53PM +0100, Nick Dyer wrote:
Function 54 implements access to various RMI4 diagnostic features.

This patch adds support for retrieving this data. It registers a V4L2
device to output the data to user space.

Signed-off-by: Nick Dyer <nick@shmanahar.org>
---
 drivers/input/rmi4/Kconfig      |  11 +
 drivers/input/rmi4/Makefile     |   1 +
 drivers/input/rmi4/rmi_bus.c    |   3 +
 drivers/input/rmi4/rmi_driver.h |   1 +
 drivers/input/rmi4/rmi_f54.c    | 754 ++++++++++++++++++++++++++++++++++++++++
 5 files changed, 770 insertions(+)
 create mode 100644 drivers/input/rmi4/rmi_f54.c
[...]
quoted hunk
index 0000000..2361157
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f54.c
[...]
quoted hunk
+static int rmi_f54_vidioc_querycap(struct file *file, void *priv,
+				   struct v4l2_capability *cap)
+{
+	struct f54_data *f54 = video_drvdata(file);
+
+	strlcpy(cap->driver, F54_NAME, sizeof(cap->driver));
+	strlcpy(cap->card, SYNAPTICS_INPUT_DEVICE_NAME, sizeof(cap->card));
+	strlcpy(cap->bus_info, dev_name(&f54->fn->dev), sizeof(cap->bus_info));
I need to correct this to prefix the bus. RMI4 registers its own bus, so
devices appear under eg /sys/bus/rmi4/devices/rmi4-00.fn54

So I will change to:
snprintf(cap->bus_info, sizeof(cap->bus_info), "rmi4:%s", dev_name(&f54->fn->dev));

And I will need to add rmi4 to the valid prefixes in v4l2-complaince as well.
quoted hunk
+
+	return 0;
+}

Re: [PATCH v6 10/11] Input: synaptics-rmi4 - add support for F54 diagnostics

From: Andrew Duggan <hidden>
Date: 2016-07-01 19:16:42

On 06/30/2016 10:38 AM, Nick Dyer wrote:
Function 54 implements access to various RMI4 diagnostic features.

This patch adds support for retrieving this data. It registers a V4L2
device to output the data to user space.

Signed-off-by: Nick Dyer <nick@shmanahar.org>
Tested-by: Andrew Duggan <redacted>
quoted hunk
---
  drivers/input/rmi4/Kconfig      |  11 +
  drivers/input/rmi4/Makefile     |   1 +
  drivers/input/rmi4/rmi_bus.c    |   3 +
  drivers/input/rmi4/rmi_driver.h |   1 +
  drivers/input/rmi4/rmi_f54.c    | 754 ++++++++++++++++++++++++++++++++++++++++
  5 files changed, 770 insertions(+)
  create mode 100644 drivers/input/rmi4/rmi_f54.c
diff --git a/drivers/input/rmi4/Kconfig b/drivers/input/rmi4/Kconfig
index f73df24..f3418b6 100644
--- a/drivers/input/rmi4/Kconfig
+++ b/drivers/input/rmi4/Kconfig
@@ -61,3 +61,14 @@ config RMI4_F30
  
  	  Function 30 provides GPIO and LED support for RMI4 devices. This
  	  includes support for buttons on TouchPads and ClickPads.
+
+config RMI4_F54
+	bool "RMI4 Function 54 (Analog diagnostics)"
+	depends on RMI4_CORE
+	depends on VIDEO_V4L2
+	select VIDEOBUF2_VMALLOC
+	help
+	  Say Y here if you want to add support for RMI4 function 54
+
+	  Function 54 provides access to various diagnostic features in certain
+	  RMI4 touch sensors.
diff --git a/drivers/input/rmi4/Makefile b/drivers/input/rmi4/Makefile
index 95c00a7..0bafc85 100644
--- a/drivers/input/rmi4/Makefile
+++ b/drivers/input/rmi4/Makefile
@@ -7,6 +7,7 @@ rmi_core-$(CONFIG_RMI4_2D_SENSOR) += rmi_2d_sensor.o
  rmi_core-$(CONFIG_RMI4_F11) += rmi_f11.o
  rmi_core-$(CONFIG_RMI4_F12) += rmi_f12.o
  rmi_core-$(CONFIG_RMI4_F30) += rmi_f30.o
+rmi_core-$(CONFIG_RMI4_F54) += rmi_f54.o
  
  # Transports
  obj-$(CONFIG_RMI4_I2C) += rmi_i2c.o
diff --git a/drivers/input/rmi4/rmi_bus.c b/drivers/input/rmi4/rmi_bus.c
index b368b05..3aedc65 100644
--- a/drivers/input/rmi4/rmi_bus.c
+++ b/drivers/input/rmi4/rmi_bus.c
@@ -315,6 +315,9 @@ static struct rmi_function_handler *fn_handlers[] = {
  #ifdef CONFIG_RMI4_F30
  	&rmi_f30_handler,
  #endif
+#ifdef CONFIG_RMI4_F54
+	&rmi_f54_handler,
+#endif
  };
  
  static void __rmi_unregister_function_handlers(int start_idx)
diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h
index 6e140fa..8dfbebe 100644
--- a/drivers/input/rmi4/rmi_driver.h
+++ b/drivers/input/rmi4/rmi_driver.h
@@ -102,4 +102,5 @@ extern struct rmi_function_handler rmi_f01_handler;
  extern struct rmi_function_handler rmi_f11_handler;
  extern struct rmi_function_handler rmi_f12_handler;
  extern struct rmi_function_handler rmi_f30_handler;
+extern struct rmi_function_handler rmi_f54_handler;
  #endif
diff --git a/drivers/input/rmi4/rmi_f54.c b/drivers/input/rmi4/rmi_f54.c
new file mode 100644
index 0000000..2361157
--- /dev/null
+++ b/drivers/input/rmi4/rmi_f54.c
@@ -0,0 +1,754 @@
+/*
+ * Copyright (c) 2012-2015 Synaptics Incorporated
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/rmi.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/videobuf2-vmalloc.h>
+#include "rmi_driver.h"
+
+#define F54_NAME		"rmi4_f54"
+
+/* F54 data offsets */
+#define F54_REPORT_DATA_OFFSET  3
+#define F54_FIFO_OFFSET         1
+#define F54_NUM_TX_OFFSET       1
+#define F54_NUM_RX_OFFSET       0
+
+/* F54 commands */
+#define F54_GET_REPORT          1
+#define F54_FORCE_CAL           2
+
+/* Fixed sizes of reports */
+#define F54_QUERY_LEN			27
+
+/* F54 capabilities */
+#define F54_CAP_BASELINE	(1 << 2)
+#define F54_CAP_IMAGE8		(1 << 3)
+#define F54_CAP_IMAGE16		(1 << 6)
+
+/**
+ * enum rmi_f54_report_type - RMI4 F54 report types
+ *
+ * @F54_8BIT_IMAGE:	Normalized 8-Bit Image Report. The capacitance variance
+ *			from baseline for each pixel.
+ *
+ * @F54_16BIT_IMAGE:	Normalized 16-Bit Image Report. The capacitance variance
+ *			from baseline for each pixel.
+ *
+ * @F54_RAW_16BIT_IMAGE:
+ *			Raw 16-Bit Image Report. The raw capacitance for each
+ *			pixel.
+ *
+ * @F54_TRUE_BASELINE:	True Baseline Report. The baseline capacitance for each
+ *			pixel.
+ *
+ * @F54_FULL_RAW_CAP:   Full Raw Capacitance Report. The raw capacitance with
+ *			low reference set to its minimum value and high
+ *			reference set to its maximum value.
+ *
+ * @F54_FULL_RAW_CAP_RX_OFFSET_REMOVED:
+ *			Full Raw Capacitance with Receiver Offset Removed
+ *			Report. Set Low reference to its minimum value and high
+ *			references to its maximum value, then report the raw
+ *			capacitance for each pixel.
+ */
+enum rmi_f54_report_type {
+	F54_REPORT_NONE = 0,
+	F54_8BIT_IMAGE = 1,
+	F54_16BIT_IMAGE = 2,
+	F54_RAW_16BIT_IMAGE = 3,
+	F54_TRUE_BASELINE = 9,
+	F54_FULL_RAW_CAP = 19,
+	F54_FULL_RAW_CAP_RX_OFFSET_REMOVED = 20,
+	F54_MAX_REPORT_TYPE,
+};
+
+const char *rmi_f54_report_type_names[] = {
+	[F54_REPORT_NONE]		= "Unknown",
+	[F54_8BIT_IMAGE]		= "Normalized 8-Bit Image",
+	[F54_16BIT_IMAGE]		= "Normalized 16-Bit Image",
+	[F54_RAW_16BIT_IMAGE]		= "Raw 16-Bit Image",
+	[F54_TRUE_BASELINE]		= "True Baseline",
+	[F54_FULL_RAW_CAP]		= "Full Raw Capacitance",
+	[F54_FULL_RAW_CAP_RX_OFFSET_REMOVED]
+					= "Full Raw Capacitance RX Offset Removed",
+};
+
+struct rmi_f54_reports {
+	int start;
+	int size;
+};
+
+struct f54_data {
+	struct rmi_function *fn;
+
+	u8 qry[F54_QUERY_LEN];
+	u8 num_rx_electrodes;
+	u8 num_tx_electrodes;
+	u8 capabilities;
+	u16 clock_rate;
+	u8 family;
+
+	enum rmi_f54_report_type report_type;
+	u8 *report_data;
+	int report_size;
+	struct rmi_f54_reports standard_report[2];
+
+	bool is_busy;
+	struct mutex status_mutex;
+	struct mutex data_mutex;
+
+	struct workqueue_struct *workqueue;
+	struct delayed_work work;
+	unsigned long timeout;
+
+	struct completion cmd_done;
+
+	/* V4L2 support */
+	struct v4l2_device v4l2;
+	struct v4l2_pix_format format;
+	struct video_device vdev;
+	struct vb2_queue queue;
+	struct mutex lock;
+	int input;
+	enum rmi_f54_report_type inputs[F54_MAX_REPORT_TYPE];
+};
+
+/*
+ * Basic checks on report_type to ensure we write a valid type
+ * to the sensor.
+ */
+static bool is_f54_report_type_valid(struct f54_data *f54,
+				     enum rmi_f54_report_type reptype)
+{
+	switch (reptype) {
+	case F54_8BIT_IMAGE:
+		return f54->capabilities & F54_CAP_IMAGE8;
+	case F54_16BIT_IMAGE:
+	case F54_RAW_16BIT_IMAGE:
+		return f54->capabilities & F54_CAP_IMAGE16;
+	case F54_TRUE_BASELINE:
+		return f54->capabilities & F54_CAP_IMAGE16;
+	case F54_FULL_RAW_CAP:
+	case F54_FULL_RAW_CAP_RX_OFFSET_REMOVED:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static enum rmi_f54_report_type rmi_f54_get_reptype(struct f54_data *f54,
+						unsigned int i)
+{
+	if (i >= F54_MAX_REPORT_TYPE)
+		return F54_REPORT_NONE;
+
+	return f54->inputs[i];
+}
+
+static void rmi_f54_create_input_map(struct f54_data *f54)
+{
+	int i = 0;
+	enum rmi_f54_report_type reptype;
+
+	for (reptype = 1; reptype < F54_MAX_REPORT_TYPE; reptype++) {
+		if (!is_f54_report_type_valid(f54, reptype))
+			continue;
+
+		f54->inputs[i++] = reptype;
+	}
+
+	/* Remaining values are zero via kzalloc */
+}
+
+static int rmi_f54_request_report(struct rmi_function *fn, u8 report_type)
+{
+	struct f54_data *f54 = dev_get_drvdata(&fn->dev);
+	struct rmi_device *rmi_dev = fn->rmi_dev;
+	int error;
+
+	/* Write Report Type into F54_AD_Data0 */
+	if (f54->report_type != report_type) {
+		error = rmi_write(rmi_dev, f54->fn->fd.data_base_addr,
+				  report_type);
+		if (error)
+			return error;
+		f54->report_type = report_type;
+	}
+
+	/*
+	 * Small delay after disabling interrupts to avoid race condition
+	 * in firmare. This value is a bit higher than absolutely necessary.
+	 * Should be removed once issue is resolved in firmware.
+	 */
+	usleep_range(2000, 3000);
+
+	mutex_lock(&f54->data_mutex);
+
+	error = rmi_write(rmi_dev, fn->fd.command_base_addr, F54_GET_REPORT);
+	if (error < 0)
+		return error;
+
+	init_completion(&f54->cmd_done);
+
+	f54->is_busy = 1;
+	f54->timeout = jiffies + msecs_to_jiffies(100);
+
+	queue_delayed_work(f54->workqueue, &f54->work, 0);
+
+	mutex_unlock(&f54->data_mutex);
+
+	return 0;
+}
+
+static size_t rmi_f54_get_report_size(struct f54_data *f54)
+{
+	u8 rx = f54->num_rx_electrodes ? : f54->num_rx_electrodes;
+	u8 tx = f54->num_tx_electrodes ? : f54->num_tx_electrodes;
+	size_t size;
+
+	switch (rmi_f54_get_reptype(f54, f54->input)) {
+	case F54_8BIT_IMAGE:
+		size = rx * tx;
+		break;
+	case F54_16BIT_IMAGE:
+	case F54_RAW_16BIT_IMAGE:
+	case F54_TRUE_BASELINE:
+	case F54_FULL_RAW_CAP:
+	case F54_FULL_RAW_CAP_RX_OFFSET_REMOVED:
+		size = sizeof(u16) * rx * tx;
+		break;
+	default:
+		size = 0;
+	}
+
+	return size;
+}
+
+static int rmi_f54_get_pixel_fmt(enum rmi_f54_report_type reptype, u32 *pixfmt)
+{
+	int ret = 0;
+
+	switch (reptype) {
+	case F54_8BIT_IMAGE:
+		*pixfmt = V4L2_TCH_FMT_DELTA_TD08;
+		break;
+
+	case F54_16BIT_IMAGE:
+		*pixfmt = V4L2_TCH_FMT_DELTA_TD16;
+		break;
+
+	case F54_RAW_16BIT_IMAGE:
+	case F54_TRUE_BASELINE:
+	case F54_FULL_RAW_CAP:
+	case F54_FULL_RAW_CAP_RX_OFFSET_REMOVED:
+		*pixfmt = V4L2_TCH_FMT_TU16;
+		break;
+
+	case F54_REPORT_NONE:
+	case F54_MAX_REPORT_TYPE:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static const struct v4l2_file_operations rmi_f54_video_fops = {
+	.owner = THIS_MODULE,
+	.open = v4l2_fh_open,
+	.release = vb2_fop_release,
+	.unlocked_ioctl = video_ioctl2,
+	.read = vb2_fop_read,
+	.mmap = vb2_fop_mmap,
+	.poll = vb2_fop_poll,
+};
+
+static int rmi_f54_queue_setup(struct vb2_queue *q,
+			       unsigned int *nbuffers, unsigned int *nplanes,
+			       unsigned int sizes[], void *alloc_ctxs[])
+{
+	struct f54_data *f54 = q->drv_priv;
+
+	if (*nplanes)
+		return sizes[0] < rmi_f54_get_report_size(f54) ? -EINVAL : 0;
+
+	*nplanes = 1;
+	sizes[0] = rmi_f54_get_report_size(f54);
+
+	return 0;
+}
+
+static void rmi_f54_buffer_queue(struct vb2_buffer *vb)
+{
+	struct f54_data *f54 = vb2_get_drv_priv(vb->vb2_queue);
+	u16 *ptr;
+	enum vb2_buffer_state state;
+	enum rmi_f54_report_type reptype;
+	int ret;
+
+	mutex_lock(&f54->status_mutex);
+
+	reptype = rmi_f54_get_reptype(f54, f54->input);
+	if (reptype == F54_REPORT_NONE) {
+		state = VB2_BUF_STATE_ERROR;
+		goto done;
+	}
+
+	if (f54->is_busy) {
+		state = VB2_BUF_STATE_ERROR;
+		goto done;
+	}
+
+	ret = rmi_f54_request_report(f54->fn, reptype);
+	if (ret) {
+		dev_err(&f54->fn->dev, "Error requesting F54 report\n");
+		state = VB2_BUF_STATE_ERROR;
+		goto done;
+	}
+
+	/* get frame data */
+	mutex_lock(&f54->data_mutex);
+
+	while (f54->is_busy) {
+		mutex_unlock(&f54->data_mutex);
+		if (!wait_for_completion_timeout(&f54->cmd_done,
+						 msecs_to_jiffies(1000))) {
+			dev_err(&f54->fn->dev, "Timed out\n");
+			state = VB2_BUF_STATE_ERROR;
+			goto done;
+		}
+		mutex_lock(&f54->data_mutex);
+	}
+
+	ptr = vb2_plane_vaddr(vb, 0);
+	if (!ptr) {
+		dev_err(&f54->fn->dev, "Error acquiring frame ptr\n");
+		state = VB2_BUF_STATE_ERROR;
+		goto data_done;
+	}
+
+	memcpy(ptr, f54->report_data, f54->report_size);
+	vb2_set_plane_payload(vb, 0, rmi_f54_get_report_size(f54));
+	state = VB2_BUF_STATE_DONE;
+
+data_done:
+	mutex_unlock(&f54->data_mutex);
+done:
+	vb2_buffer_done(vb, state);
+	mutex_unlock(&f54->status_mutex);
+}
+
+/* V4L2 structures */
+static const struct vb2_ops rmi_f54_queue_ops = {
+	.queue_setup            = rmi_f54_queue_setup,
+	.buf_queue              = rmi_f54_buffer_queue,
+	.wait_prepare           = vb2_ops_wait_prepare,
+	.wait_finish            = vb2_ops_wait_finish,
+};
+
+static const struct vb2_queue rmi_f54_queue = {
+	.type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+	.io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF | VB2_READ,
+	.buf_struct_size = sizeof(struct vb2_buffer),
+	.ops = &rmi_f54_queue_ops,
+	.mem_ops = &vb2_vmalloc_memops,
+	.timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC,
+	.min_buffers_needed = 1,
+};
+
+static int rmi_f54_vidioc_querycap(struct file *file, void *priv,
+				   struct v4l2_capability *cap)
+{
+	struct f54_data *f54 = video_drvdata(file);
+
+	strlcpy(cap->driver, F54_NAME, sizeof(cap->driver));
+	strlcpy(cap->card, SYNAPTICS_INPUT_DEVICE_NAME, sizeof(cap->card));
+	strlcpy(cap->bus_info, dev_name(&f54->fn->dev), sizeof(cap->bus_info));
+
+	return 0;
+}
+
+static int rmi_f54_vidioc_enum_input(struct file *file, void *priv,
+				     struct v4l2_input *i)
+{
+	struct f54_data *f54 = video_drvdata(file);
+	enum rmi_f54_report_type reptype;
+
+	reptype = rmi_f54_get_reptype(f54, i->index);
+	if (reptype == F54_REPORT_NONE)
+		return -EINVAL;
+
+	i->type = V4L2_INPUT_TYPE_TOUCH;
+
+	strlcpy(i->name, rmi_f54_report_type_names[reptype], sizeof(i->name));
+	return 0;
+}
+
+static int rmi_f54_set_input(struct f54_data *f54, unsigned int i)
+{
+	struct v4l2_pix_format *f = &f54->format;
+	enum rmi_f54_report_type reptype;
+	int ret;
+
+	reptype = rmi_f54_get_reptype(f54, i);
+	if (reptype == F54_REPORT_NONE)
+		return -EINVAL;
+
+	ret = rmi_f54_get_pixel_fmt(reptype, &f->pixelformat);
+	if (ret)
+		return ret;
+
+	f54->input = i;
+
+	f->width = f54->num_rx_electrodes;
+	f->height = f54->num_tx_electrodes;
+	f->field = V4L2_FIELD_NONE;
+	f->colorspace = V4L2_COLORSPACE_RAW;
+	f->bytesperline = f->width * sizeof(u16);
+	f->sizeimage = f->width * f->height * sizeof(u16);
+
+	return 0;
+}
+
+static int rmi_f54_vidioc_s_input(struct file *file, void *priv, unsigned int i)
+{
+	return rmi_f54_set_input(video_drvdata(file), i);
+}
+
+static int rmi_f54_vidioc_g_input(struct file *file, void *priv,
+				  unsigned int *i)
+{
+	struct f54_data *f54 = video_drvdata(file);
+
+	*i = f54->input;
+
+	return 0;
+}
+
+static int rmi_f54_vidioc_fmt(struct file *file, void *priv,
+			      struct v4l2_format *f)
+{
+	struct f54_data *f54 = video_drvdata(file);
+
+	f->fmt.pix = f54->format;
+
+	return 0;
+}
+
+static int rmi_f54_vidioc_enum_fmt(struct file *file, void *priv,
+				   struct v4l2_fmtdesc *fmt)
+{
+	if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	switch (fmt->index) {
+	case 0:
+		fmt->pixelformat = V4L2_TCH_FMT_DELTA_TD16;
+		break;
+
+	case 1:
+		fmt->pixelformat = V4L2_TCH_FMT_DELTA_TD08;
+		break;
+
+	case 2:
+		fmt->pixelformat = V4L2_TCH_FMT_TU16;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int rmi_f54_vidioc_g_parm(struct file *file, void *fh,
+				 struct v4l2_streamparm *a)
+{
+	if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+		return -EINVAL;
+
+	a->parm.capture.readbuffers = 1;
+	a->parm.capture.timeperframe.numerator = 1;
+	a->parm.capture.timeperframe.denominator = 10;
+	return 0;
+}
+
+static const struct v4l2_ioctl_ops rmi_f54_video_ioctl_ops = {
+	.vidioc_querycap	= rmi_f54_vidioc_querycap,
+
+	.vidioc_enum_fmt_vid_cap = rmi_f54_vidioc_enum_fmt,
+	.vidioc_s_fmt_vid_cap	= rmi_f54_vidioc_fmt,
+	.vidioc_g_fmt_vid_cap	= rmi_f54_vidioc_fmt,
+	.vidioc_try_fmt_vid_cap	= rmi_f54_vidioc_fmt,
+	.vidioc_g_parm		= rmi_f54_vidioc_g_parm,
+
+	.vidioc_enum_input	= rmi_f54_vidioc_enum_input,
+	.vidioc_g_input		= rmi_f54_vidioc_g_input,
+	.vidioc_s_input		= rmi_f54_vidioc_s_input,
+
+	.vidioc_reqbufs		= vb2_ioctl_reqbufs,
+	.vidioc_create_bufs	= vb2_ioctl_create_bufs,
+	.vidioc_querybuf	= vb2_ioctl_querybuf,
+	.vidioc_qbuf		= vb2_ioctl_qbuf,
+	.vidioc_dqbuf		= vb2_ioctl_dqbuf,
+	.vidioc_expbuf		= vb2_ioctl_expbuf,
+
+	.vidioc_streamon	= vb2_ioctl_streamon,
+	.vidioc_streamoff	= vb2_ioctl_streamoff,
+};
+
+static const struct video_device rmi_f54_video_device = {
+	.name = "Synaptics RMI4",
+	.fops = &rmi_f54_video_fops,
+	.ioctl_ops = &rmi_f54_video_ioctl_ops,
+	.release = video_device_release_empty,
+	.device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TOUCH |
+		       V4L2_CAP_READWRITE | V4L2_CAP_STREAMING,
+};
+
+static void rmi_f54_work(struct work_struct *work)
+{
+	struct f54_data *f54 = container_of(work, struct f54_data, work.work);
+	struct rmi_function *fn = f54->fn;
+	u8 fifo[2];
+	struct rmi_f54_reports *report;
+	int report_size;
+	u8 command;
+	u8 *data;
+	int error;
+
+	data = f54->report_data;
+	report_size = rmi_f54_get_report_size(f54);
+	if (report_size == 0) {
+		dev_err(&fn->dev, "Bad report size, report type=%d\n",
+				f54->report_type);
+		error = -EINVAL;
+		goto error;     /* retry won't help */
+	}
+	f54->standard_report[0].size = report_size;
+	report = f54->standard_report;
+
+	mutex_lock(&f54->data_mutex);
+
+	/*
+	 * Need to check if command has completed.
+	 * If not try again later.
+	 */
+	error = rmi_read(fn->rmi_dev, f54->fn->fd.command_base_addr,
+			 &command);
+	if (error) {
+		dev_err(&fn->dev, "Failed to read back command\n");
+		goto error;
+	}
+	if (command & F54_GET_REPORT) {
+		if (time_after(jiffies, f54->timeout)) {
+			dev_err(&fn->dev, "Get report command timed out\n");
+			error = -ETIMEDOUT;
+		}
+		report_size = 0;
+		goto error;
+	}
+
+	rmi_dbg(RMI_DEBUG_FN, &fn->dev, "Get report command completed, reading data\n");
+
+	report_size = 0;
+	for (; report->size; report++) {
+		fifo[0] = report->start & 0xff;
+		fifo[1] = (report->start >> 8) & 0xff;
+		error = rmi_write_block(fn->rmi_dev,
+					fn->fd.data_base_addr + F54_FIFO_OFFSET,
+					fifo, sizeof(fifo));
+		if (error) {
+			dev_err(&fn->dev, "Failed to set fifo start offset\n");
+			goto abort;
+		}
+
+		error = rmi_read_block(fn->rmi_dev, fn->fd.data_base_addr +
+				       F54_REPORT_DATA_OFFSET, data,
+				       report->size);
+		if (error) {
+			dev_err(&fn->dev, "%s: read [%d bytes] returned %d\n",
+				__func__, report->size, error);
+			goto abort;
+		}
+		data += report->size;
+		report_size += report->size;
+	}
+
+abort:
+	f54->report_size = error ? 0 : report_size;
+error:
+	if (error)
+		report_size = 0;
+
+	if (report_size == 0 && !error) {
+		queue_delayed_work(f54->workqueue, &f54->work,
+				   msecs_to_jiffies(1));
+	} else {
+		f54->is_busy = false;
+		complete(&f54->cmd_done);
+	}
+
+	mutex_unlock(&f54->data_mutex);
+}
+
+static int rmi_f54_attention(struct rmi_function *fn, unsigned long *irqbits)
+{
+	return 0;
+}
+
+static int rmi_f54_config(struct rmi_function *fn)
+{
+	struct rmi_driver *drv = fn->rmi_dev->driver;
+
+	drv->set_irq_bits(fn->rmi_dev, fn->irq_mask);
+
+	return 0;
+}
+
+static int rmi_f54_detect(struct rmi_function *fn)
+{
+	int error;
+	struct f54_data *f54;
+
+	f54 = dev_get_drvdata(&fn->dev);
+
+	error = rmi_read_block(fn->rmi_dev, fn->fd.query_base_addr,
+			       &f54->qry, sizeof(f54->qry));
+	if (error) {
+		dev_err(&fn->dev, "%s: Failed to query F54 properties\n",
+			__func__);
+		return error;
+	}
+
+	f54->num_rx_electrodes = f54->qry[0];
+	f54->num_tx_electrodes = f54->qry[1];
+	f54->capabilities = f54->qry[2];
+	f54->clock_rate = f54->qry[3] | (f54->qry[4] << 8);
+	f54->family = f54->qry[5];
+
+	rmi_dbg(RMI_DEBUG_FN, &fn->dev, "F54 num_rx_electrodes: %d\n",
+		f54->num_rx_electrodes);
+	rmi_dbg(RMI_DEBUG_FN, &fn->dev, "F54 num_tx_electrodes: %d\n",
+		f54->num_tx_electrodes);
+	rmi_dbg(RMI_DEBUG_FN, &fn->dev, "F54 capabilities: 0x%x\n",
+		f54->capabilities);
+	rmi_dbg(RMI_DEBUG_FN, &fn->dev, "F54 clock rate: 0x%x\n",
+		f54->clock_rate);
+	rmi_dbg(RMI_DEBUG_FN, &fn->dev, "F54 family: 0x%x\n",
+		f54->family);
+
+	f54->is_busy = false;
+
+	return 0;
+}
+
+static int rmi_f54_probe(struct rmi_function *fn)
+{
+	struct f54_data *f54;
+	int ret;
+	u8 rx, tx;
+
+	f54 = devm_kzalloc(&fn->dev, sizeof(struct f54_data), GFP_KERNEL);
+	if (!f54)
+		return -ENOMEM;
+
+	f54->fn = fn;
+	dev_set_drvdata(&fn->dev, f54);
+
+	ret = rmi_f54_detect(fn);
+	if (ret)
+		return ret;
+
+	mutex_init(&f54->data_mutex);
+	mutex_init(&f54->status_mutex);
+
+	rx = f54->num_rx_electrodes;
+	tx = f54->num_tx_electrodes;
+	f54->report_data = devm_kzalloc(&fn->dev,
+					sizeof(u16) * tx * rx,
+					GFP_KERNEL);
+	if (f54->report_data == NULL)
+		return -ENOMEM;
+
+	INIT_DELAYED_WORK(&f54->work, rmi_f54_work);
+
+	f54->workqueue = create_singlethread_workqueue("rmi4-poller");
+	if (!f54->workqueue)
+		return -ENOMEM;
+
+	rmi_f54_create_input_map(f54);
+
+	/* register video device */
+	strlcpy(f54->v4l2.name, F54_NAME, sizeof(f54->v4l2.name));
+	ret = v4l2_device_register(&fn->dev, &f54->v4l2);
+	if (ret) {
+		dev_err(&fn->dev, "Unable to register video dev.\n");
+		goto remove_wq;
+	}
+
+	/* initialize the queue */
+	mutex_init(&f54->lock);
+	f54->queue = rmi_f54_queue;
+	f54->queue.drv_priv = f54;
+	f54->queue.lock = &f54->lock;
+
+	ret = vb2_queue_init(&f54->queue);
+	if (ret)
+		goto remove_v4l2;
+
+	f54->vdev = rmi_f54_video_device;
+	f54->vdev.v4l2_dev = &f54->v4l2;
+	f54->vdev.lock = &f54->lock;
+	f54->vdev.vfl_dir = VFL_DIR_RX;
+	f54->vdev.queue = &f54->queue;
+	video_set_drvdata(&f54->vdev, f54);
+
+	ret = video_register_device(&f54->vdev, VFL_TYPE_TOUCH, -1);
+	if (ret) {
+		dev_err(&fn->dev, "Unable to register video subdevice.");
+		goto remove_v4l2;
+	}
+
+	return 0;
+
+remove_v4l2:
+	v4l2_device_unregister(&f54->v4l2);
+remove_wq:
+	cancel_delayed_work_sync(&f54->work);
+	flush_workqueue(f54->workqueue);
+	destroy_workqueue(f54->workqueue);
+	return ret;
+}
+
+static void rmi_f54_remove(struct rmi_function *fn)
+{
+	struct f54_data *f54 = dev_get_drvdata(&fn->dev);
+
+	video_unregister_device(&f54->vdev);
+	v4l2_device_unregister(&f54->v4l2);
+}
+
+struct rmi_function_handler rmi_f54_handler = {
+	.driver = {
+		.name = F54_NAME,
+	},
+	.func = 0x54,
+	.probe = rmi_f54_probe,
+	.config = rmi_f54_config,
+	.attention = rmi_f54_attention,
+	.remove = rmi_f54_remove,
+};

Re: [PATCH v6 11/11] Input: sur40 - use new V4L2 touch input type

From: Florian Echtler <hidden>
Date: 2016-07-03 18:12:07

On 30.06.2016 19:38, Nick Dyer wrote:
Support both V4L2_TCH_FMT_TU08 and V4L2_PIX_FMT_GREY for backwards
compatibility.

Note: I have not tested these changes (I have no access to the hardware)
so not signing off.
I will try to test this ASAP. However, I'm currently ill, so it might
take a while - sorry.

Best, Florian
quoted hunk
---
 drivers/input/touchscreen/sur40.c | 121 +++++++++++++++++++++++++++-----------
 1 file changed, 88 insertions(+), 33 deletions(-)
diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c
index 880c40b..9ba68cf 100644
--- a/drivers/input/touchscreen/sur40.c
+++ b/drivers/input/touchscreen/sur40.c
@@ -139,6 +139,27 @@ struct sur40_image_header {
 #define SUR40_GET_STATE   0xc5 /*  4 bytes state (?) */
 #define SUR40_GET_SENSORS 0xb1 /*  8 bytes sensors   */
 
+static const struct v4l2_pix_format sur40_pix_format[] = {
+	{
+		.pixelformat = V4L2_TCH_FMT_TU08,
+		.width  = SENSOR_RES_X / 2,
+		.height = SENSOR_RES_Y / 2,
+		.field = V4L2_FIELD_NONE,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.bytesperline = SENSOR_RES_X / 2,
+		.sizeimage = (SENSOR_RES_X/2) * (SENSOR_RES_Y/2),
+	},
+	{
+		.pixelformat = V4L2_PIX_FMT_GREY,
+		.width  = SENSOR_RES_X / 2,
+		.height = SENSOR_RES_Y / 2,
+		.field = V4L2_FIELD_NONE,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.bytesperline = SENSOR_RES_X / 2,
+		.sizeimage = (SENSOR_RES_X/2) * (SENSOR_RES_Y/2),
+	}
+};
+
 /* master device state */
 struct sur40_state {
 
@@ -149,6 +170,7 @@ struct sur40_state {
 	struct v4l2_device v4l2;
 	struct video_device vdev;
 	struct mutex lock;
+	struct v4l2_pix_format pix_fmt;
 
 	struct vb2_queue queue;
 	struct vb2_alloc_ctx *alloc_ctx;
@@ -170,7 +192,6 @@ struct sur40_buffer {
 
 /* forward declarations */
 static const struct video_device sur40_video_device;
-static const struct v4l2_pix_format sur40_video_format;
 static const struct vb2_queue sur40_queue;
 static void sur40_process_video(struct sur40_state *sur40);
 
@@ -421,7 +442,7 @@ static void sur40_process_video(struct sur40_state *sur40)
 		goto err_poll;
 	}
 
-	if (le32_to_cpu(img->size) != sur40_video_format.sizeimage) {
+	if (le32_to_cpu(img->size) != sur40->pix_fmt.sizeimage) {
 		dev_err(sur40->dev, "image size mismatch\n");
 		goto err_poll;
 	}
@@ -432,7 +453,7 @@ static void sur40_process_video(struct sur40_state *sur40)
 
 	result = usb_sg_init(&sgr, sur40->usbdev,
 		usb_rcvbulkpipe(sur40->usbdev, VIDEO_ENDPOINT), 0,
-		sgt->sgl, sgt->nents, sur40_video_format.sizeimage, 0);
+		sgt->sgl, sgt->nents, sur40->pix_fmt.sizeimage, 0);
 	if (result < 0) {
 		dev_err(sur40->dev, "error %d in usb_sg_init\n", result);
 		goto err_poll;
@@ -593,13 +614,14 @@ static int sur40_probe(struct usb_interface *interface,
 		goto err_unreg_v4l2;
 	}
 
+	sur40->pix_fmt = sur40_pix_format[0];
 	sur40->vdev = sur40_video_device;
 	sur40->vdev.v4l2_dev = &sur40->v4l2;
 	sur40->vdev.lock = &sur40->lock;
 	sur40->vdev.queue = &sur40->queue;
 	video_set_drvdata(&sur40->vdev, sur40);
 
-	error = video_register_device(&sur40->vdev, VFL_TYPE_GRABBER, -1);
+	error = video_register_device(&sur40->vdev, VFL_TYPE_TOUCH, -1);
 	if (error) {
 		dev_err(&interface->dev,
 			"Unable to register video subdevice.");
@@ -662,10 +684,10 @@ static int sur40_queue_setup(struct vb2_queue *q,
 	alloc_ctxs[0] = sur40->alloc_ctx;
 
 	if (*nplanes)
-		return sizes[0] < sur40_video_format.sizeimage ? -EINVAL : 0;
+		return sizes[0] < sur40->pix_fmt.sizeimage ? -EINVAL : 0;
 
 	*nplanes = 1;
-	sizes[0] = sur40_video_format.sizeimage;
+	sizes[0] = sur40->pix_fmt.sizeimage;
 
 	return 0;
 }
@@ -677,7 +699,7 @@ static int sur40_queue_setup(struct vb2_queue *q,
 static int sur40_buffer_prepare(struct vb2_buffer *vb)
 {
 	struct sur40_state *sur40 = vb2_get_drv_priv(vb->vb2_queue);
-	unsigned long size = sur40_video_format.sizeimage;
+	unsigned long size = sur40->pix_fmt.sizeimage;
 
 	if (vb2_plane_size(vb, 0) < size) {
 		dev_err(&sur40->usbdev->dev, "buffer too small (%lu < %lu)\n",
@@ -751,7 +773,7 @@ static int sur40_vidioc_querycap(struct file *file, void *priv,
 	strlcpy(cap->driver, DRIVER_SHORT, sizeof(cap->driver));
 	strlcpy(cap->card, DRIVER_LONG, sizeof(cap->card));
 	usb_make_path(sur40->usbdev, cap->bus_info, sizeof(cap->bus_info));
-	cap->device_caps = V4L2_CAP_VIDEO_CAPTURE |
+	cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TOUCH |
 		V4L2_CAP_READWRITE |
 		V4L2_CAP_STREAMING;
 	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
@@ -763,7 +785,7 @@ static int sur40_vidioc_enum_input(struct file *file, void *priv,
 {
 	if (i->index != 0)
 		return -EINVAL;
-	i->type = V4L2_INPUT_TYPE_CAMERA;
+	i->type = V4L2_INPUT_TYPE_TOUCH;
 	i->std = V4L2_STD_UNKNOWN;
 	strlcpy(i->name, "In-Cell Sensor", sizeof(i->name));
 	i->capabilities = 0;
@@ -781,20 +803,57 @@ static int sur40_vidioc_g_input(struct file *file, void *priv, unsigned int *i)
 	return 0;
 }
 
-static int sur40_vidioc_fmt(struct file *file, void *priv,
+static int sur40_vidioc_try_fmt(struct file *file, void *priv,
 			    struct v4l2_format *f)
 {
-	f->fmt.pix = sur40_video_format;
+	switch (f->fmt.pix.pixelformat) {
+		case V4L2_PIX_FMT_GREY:
+			f->fmt.pix = sur40_pix_format[1];
+			break;
+
+		default:
+			f->fmt.pix = sur40_pix_format[0];
+			break;
+	}
+
+	return 0;
+}
+
+static int sur40_vidioc_s_fmt(struct file *file, void *priv,
+			    struct v4l2_format *f)
+{
+	struct sur40_state *sur40 = video_drvdata(file);
+
+	switch (f->fmt.pix.pixelformat) {
+		case V4L2_PIX_FMT_GREY:
+			sur40->pix_fmt = sur40_pix_format[1];
+			break;
+
+		default:
+			sur40->pix_fmt = sur40_pix_format[0];
+			break;
+	}
+
+	f->fmt.pix = sur40->pix_fmt;
+	return 0;
+}
+
+static int sur40_vidioc_g_fmt(struct file *file, void *priv,
+			    struct v4l2_format *f)
+{
+	struct sur40_state *sur40 = video_drvdata(file);
+
+	f->fmt.pix = sur40->pix_fmt;
 	return 0;
 }
 
 static int sur40_vidioc_enum_fmt(struct file *file, void *priv,
 				 struct v4l2_fmtdesc *f)
 {
-	if (f->index != 0)
+	if (f->index >= ARRAY_SIZE(sur40_pix_format))
 		return -EINVAL;
-	strlcpy(f->description, "8-bit greyscale", sizeof(f->description));
-	f->pixelformat = V4L2_PIX_FMT_GREY;
+
+	f->pixelformat = sur40_pix_format[f->index].pixelformat;
 	f->flags = 0;
 	return 0;
 }
@@ -802,21 +861,27 @@ static int sur40_vidioc_enum_fmt(struct file *file, void *priv,
 static int sur40_vidioc_enum_framesizes(struct file *file, void *priv,
 					struct v4l2_frmsizeenum *f)
 {
-	if ((f->index != 0) || (f->pixel_format != V4L2_PIX_FMT_GREY))
+	struct sur40_state *sur40 = video_drvdata(file);
+
+	if ((f->index != 0) || ((f->pixel_format != V4L2_TCH_FMT_TU08)
+		&& (f->pixel_format != V4L2_PIX_FMT_GREY)))
 		return -EINVAL;
 
 	f->type = V4L2_FRMSIZE_TYPE_DISCRETE;
-	f->discrete.width  = sur40_video_format.width;
-	f->discrete.height = sur40_video_format.height;
+	f->discrete.width  = sur40->pix_fmt.width;
+	f->discrete.height = sur40->pix_fmt.height;
 	return 0;
 }
 
 static int sur40_vidioc_enum_frameintervals(struct file *file, void *priv,
 					    struct v4l2_frmivalenum *f)
 {
-	if ((f->index > 1) || (f->pixel_format != V4L2_PIX_FMT_GREY)
-		|| (f->width  != sur40_video_format.width)
-		|| (f->height != sur40_video_format.height))
+	struct sur40_state *sur40 = video_drvdata(file);
+
+	if ((f->index > 1) || ((f->pixel_format != V4L2_TCH_FMT_TU08)
+	        && (f->pixel_format != V4L2_PIX_FMT_GREY))
+		|| (f->width  != sur40->pix_fmt.width)
+		|| (f->height != sur40->pix_fmt.height))
 			return -EINVAL;
 
 	f->type = V4L2_FRMIVAL_TYPE_DISCRETE;
@@ -873,9 +938,9 @@ static const struct v4l2_ioctl_ops sur40_video_ioctl_ops = {
 	.vidioc_querycap	= sur40_vidioc_querycap,
 
 	.vidioc_enum_fmt_vid_cap = sur40_vidioc_enum_fmt,
-	.vidioc_try_fmt_vid_cap	= sur40_vidioc_fmt,
-	.vidioc_s_fmt_vid_cap	= sur40_vidioc_fmt,
-	.vidioc_g_fmt_vid_cap	= sur40_vidioc_fmt,
+	.vidioc_try_fmt_vid_cap	= sur40_vidioc_try_fmt,
+	.vidioc_s_fmt_vid_cap	= sur40_vidioc_s_fmt,
+	.vidioc_g_fmt_vid_cap	= sur40_vidioc_g_fmt,
 
 	.vidioc_enum_framesizes = sur40_vidioc_enum_framesizes,
 	.vidioc_enum_frameintervals = sur40_vidioc_enum_frameintervals,
@@ -902,16 +967,6 @@ static const struct video_device sur40_video_device = {
 	.release = video_device_release_empty,
 };
 
-static const struct v4l2_pix_format sur40_video_format = {
-	.pixelformat = V4L2_PIX_FMT_GREY,
-	.width  = SENSOR_RES_X / 2,
-	.height = SENSOR_RES_Y / 2,
-	.field = V4L2_FIELD_NONE,
-	.colorspace = V4L2_COLORSPACE_SRGB,
-	.bytesperline = SENSOR_RES_X / 2,
-	.sizeimage = (SENSOR_RES_X/2) * (SENSOR_RES_Y/2),
-};
-
 /* USB-specific object needed to register this driver with the USB subsystem. */
 static struct usb_driver sur40_driver = {
 	.name = DRIVER_SHORT,

-- 
SENT FROM MY DEC VT50 TERMINAL

Re: [PATCH v6 11/11] Input: sur40 - use new V4L2 touch input type

From: Pavel Machek <hidden>
Date: 2016-08-05 13:41:43

On Thu 2016-06-30 18:38:54, Nick Dyer wrote:
Support both V4L2_TCH_FMT_TU08 and V4L2_PIX_FMT_GREY for backwards
compatibility.

Note: I have not tested these changes (I have no access to the hardware)
so not signing off.
Sign off means something else, see docs... and sign patches off :-).

									Pavel
quoted hunk
---
 drivers/input/touchscreen/sur40.c | 121 +++++++++++++++++++++++++++-----------
 1 file changed, 88 insertions(+), 33 deletions(-)
diff --git a/drivers/input/touchscreen/sur40.c b/drivers/input/touchscreen/sur40.c
index 880c40b..9ba68cf 100644
--- a/drivers/input/touchscreen/sur40.c
+++ b/drivers/input/touchscreen/sur40.c
@@ -139,6 +139,27 @@ struct sur40_image_header {
 #define SUR40_GET_STATE   0xc5 /*  4 bytes state (?) */
 #define SUR40_GET_SENSORS 0xb1 /*  8 bytes sensors   */
 
+static const struct v4l2_pix_format sur40_pix_format[] = {
+	{
+		.pixelformat = V4L2_TCH_FMT_TU08,
+		.width  = SENSOR_RES_X / 2,
+		.height = SENSOR_RES_Y / 2,
+		.field = V4L2_FIELD_NONE,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.bytesperline = SENSOR_RES_X / 2,
+		.sizeimage = (SENSOR_RES_X/2) * (SENSOR_RES_Y/2),
+	},
+	{
+		.pixelformat = V4L2_PIX_FMT_GREY,
+		.width  = SENSOR_RES_X / 2,
+		.height = SENSOR_RES_Y / 2,
+		.field = V4L2_FIELD_NONE,
+		.colorspace = V4L2_COLORSPACE_SRGB,
+		.bytesperline = SENSOR_RES_X / 2,
+		.sizeimage = (SENSOR_RES_X/2) * (SENSOR_RES_Y/2),
+	}
+};
+
 /* master device state */
 struct sur40_state {
 
@@ -149,6 +170,7 @@ struct sur40_state {
 	struct v4l2_device v4l2;
 	struct video_device vdev;
 	struct mutex lock;
+	struct v4l2_pix_format pix_fmt;
 
 	struct vb2_queue queue;
 	struct vb2_alloc_ctx *alloc_ctx;
@@ -170,7 +192,6 @@ struct sur40_buffer {
 
 /* forward declarations */
 static const struct video_device sur40_video_device;
-static const struct v4l2_pix_format sur40_video_format;
 static const struct vb2_queue sur40_queue;
 static void sur40_process_video(struct sur40_state *sur40);
 
@@ -421,7 +442,7 @@ static void sur40_process_video(struct sur40_state *sur40)
 		goto err_poll;
 	}
 
-	if (le32_to_cpu(img->size) != sur40_video_format.sizeimage) {
+	if (le32_to_cpu(img->size) != sur40->pix_fmt.sizeimage) {
 		dev_err(sur40->dev, "image size mismatch\n");
 		goto err_poll;
 	}
@@ -432,7 +453,7 @@ static void sur40_process_video(struct sur40_state *sur40)
 
 	result = usb_sg_init(&sgr, sur40->usbdev,
 		usb_rcvbulkpipe(sur40->usbdev, VIDEO_ENDPOINT), 0,
-		sgt->sgl, sgt->nents, sur40_video_format.sizeimage, 0);
+		sgt->sgl, sgt->nents, sur40->pix_fmt.sizeimage, 0);
 	if (result < 0) {
 		dev_err(sur40->dev, "error %d in usb_sg_init\n", result);
 		goto err_poll;
@@ -593,13 +614,14 @@ static int sur40_probe(struct usb_interface *interface,
 		goto err_unreg_v4l2;
 	}
 
+	sur40->pix_fmt = sur40_pix_format[0];
 	sur40->vdev = sur40_video_device;
 	sur40->vdev.v4l2_dev = &sur40->v4l2;
 	sur40->vdev.lock = &sur40->lock;
 	sur40->vdev.queue = &sur40->queue;
 	video_set_drvdata(&sur40->vdev, sur40);
 
-	error = video_register_device(&sur40->vdev, VFL_TYPE_GRABBER, -1);
+	error = video_register_device(&sur40->vdev, VFL_TYPE_TOUCH, -1);
 	if (error) {
 		dev_err(&interface->dev,
 			"Unable to register video subdevice.");
@@ -662,10 +684,10 @@ static int sur40_queue_setup(struct vb2_queue *q,
 	alloc_ctxs[0] = sur40->alloc_ctx;
 
 	if (*nplanes)
-		return sizes[0] < sur40_video_format.sizeimage ? -EINVAL : 0;
+		return sizes[0] < sur40->pix_fmt.sizeimage ? -EINVAL : 0;
 
 	*nplanes = 1;
-	sizes[0] = sur40_video_format.sizeimage;
+	sizes[0] = sur40->pix_fmt.sizeimage;
 
 	return 0;
 }
@@ -677,7 +699,7 @@ static int sur40_queue_setup(struct vb2_queue *q,
 static int sur40_buffer_prepare(struct vb2_buffer *vb)
 {
 	struct sur40_state *sur40 = vb2_get_drv_priv(vb->vb2_queue);
-	unsigned long size = sur40_video_format.sizeimage;
+	unsigned long size = sur40->pix_fmt.sizeimage;
 
 	if (vb2_plane_size(vb, 0) < size) {
 		dev_err(&sur40->usbdev->dev, "buffer too small (%lu < %lu)\n",
@@ -751,7 +773,7 @@ static int sur40_vidioc_querycap(struct file *file, void *priv,
 	strlcpy(cap->driver, DRIVER_SHORT, sizeof(cap->driver));
 	strlcpy(cap->card, DRIVER_LONG, sizeof(cap->card));
 	usb_make_path(sur40->usbdev, cap->bus_info, sizeof(cap->bus_info));
-	cap->device_caps = V4L2_CAP_VIDEO_CAPTURE |
+	cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TOUCH |
 		V4L2_CAP_READWRITE |
 		V4L2_CAP_STREAMING;
 	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
@@ -763,7 +785,7 @@ static int sur40_vidioc_enum_input(struct file *file, void *priv,
 {
 	if (i->index != 0)
 		return -EINVAL;
-	i->type = V4L2_INPUT_TYPE_CAMERA;
+	i->type = V4L2_INPUT_TYPE_TOUCH;
 	i->std = V4L2_STD_UNKNOWN;
 	strlcpy(i->name, "In-Cell Sensor", sizeof(i->name));
 	i->capabilities = 0;
@@ -781,20 +803,57 @@ static int sur40_vidioc_g_input(struct file *file, void *priv, unsigned int *i)
 	return 0;
 }
 
-static int sur40_vidioc_fmt(struct file *file, void *priv,
+static int sur40_vidioc_try_fmt(struct file *file, void *priv,
 			    struct v4l2_format *f)
 {
-	f->fmt.pix = sur40_video_format;
+	switch (f->fmt.pix.pixelformat) {
+		case V4L2_PIX_FMT_GREY:
+			f->fmt.pix = sur40_pix_format[1];
+			break;
+
+		default:
+			f->fmt.pix = sur40_pix_format[0];
+			break;
+	}
+
+	return 0;
+}
+
+static int sur40_vidioc_s_fmt(struct file *file, void *priv,
+			    struct v4l2_format *f)
+{
+	struct sur40_state *sur40 = video_drvdata(file);
+
+	switch (f->fmt.pix.pixelformat) {
+		case V4L2_PIX_FMT_GREY:
+			sur40->pix_fmt = sur40_pix_format[1];
+			break;
+
+		default:
+			sur40->pix_fmt = sur40_pix_format[0];
+			break;
+	}
+
+	f->fmt.pix = sur40->pix_fmt;
+	return 0;
+}
+
+static int sur40_vidioc_g_fmt(struct file *file, void *priv,
+			    struct v4l2_format *f)
+{
+	struct sur40_state *sur40 = video_drvdata(file);
+
+	f->fmt.pix = sur40->pix_fmt;
 	return 0;
 }
 
 static int sur40_vidioc_enum_fmt(struct file *file, void *priv,
 				 struct v4l2_fmtdesc *f)
 {
-	if (f->index != 0)
+	if (f->index >= ARRAY_SIZE(sur40_pix_format))
 		return -EINVAL;
-	strlcpy(f->description, "8-bit greyscale", sizeof(f->description));
-	f->pixelformat = V4L2_PIX_FMT_GREY;
+
+	f->pixelformat = sur40_pix_format[f->index].pixelformat;
 	f->flags = 0;
 	return 0;
 }
@@ -802,21 +861,27 @@ static int sur40_vidioc_enum_fmt(struct file *file, void *priv,
 static int sur40_vidioc_enum_framesizes(struct file *file, void *priv,
 					struct v4l2_frmsizeenum *f)
 {
-	if ((f->index != 0) || (f->pixel_format != V4L2_PIX_FMT_GREY))
+	struct sur40_state *sur40 = video_drvdata(file);
+
+	if ((f->index != 0) || ((f->pixel_format != V4L2_TCH_FMT_TU08)
+		&& (f->pixel_format != V4L2_PIX_FMT_GREY)))
 		return -EINVAL;
 
 	f->type = V4L2_FRMSIZE_TYPE_DISCRETE;
-	f->discrete.width  = sur40_video_format.width;
-	f->discrete.height = sur40_video_format.height;
+	f->discrete.width  = sur40->pix_fmt.width;
+	f->discrete.height = sur40->pix_fmt.height;
 	return 0;
 }
 
 static int sur40_vidioc_enum_frameintervals(struct file *file, void *priv,
 					    struct v4l2_frmivalenum *f)
 {
-	if ((f->index > 1) || (f->pixel_format != V4L2_PIX_FMT_GREY)
-		|| (f->width  != sur40_video_format.width)
-		|| (f->height != sur40_video_format.height))
+	struct sur40_state *sur40 = video_drvdata(file);
+
+	if ((f->index > 1) || ((f->pixel_format != V4L2_TCH_FMT_TU08)
+	        && (f->pixel_format != V4L2_PIX_FMT_GREY))
+		|| (f->width  != sur40->pix_fmt.width)
+		|| (f->height != sur40->pix_fmt.height))
 			return -EINVAL;
 
 	f->type = V4L2_FRMIVAL_TYPE_DISCRETE;
@@ -873,9 +938,9 @@ static const struct v4l2_ioctl_ops sur40_video_ioctl_ops = {
 	.vidioc_querycap	= sur40_vidioc_querycap,
 
 	.vidioc_enum_fmt_vid_cap = sur40_vidioc_enum_fmt,
-	.vidioc_try_fmt_vid_cap	= sur40_vidioc_fmt,
-	.vidioc_s_fmt_vid_cap	= sur40_vidioc_fmt,
-	.vidioc_g_fmt_vid_cap	= sur40_vidioc_fmt,
+	.vidioc_try_fmt_vid_cap	= sur40_vidioc_try_fmt,
+	.vidioc_s_fmt_vid_cap	= sur40_vidioc_s_fmt,
+	.vidioc_g_fmt_vid_cap	= sur40_vidioc_g_fmt,
 
 	.vidioc_enum_framesizes = sur40_vidioc_enum_framesizes,
 	.vidioc_enum_frameintervals = sur40_vidioc_enum_frameintervals,
@@ -902,16 +967,6 @@ static const struct video_device sur40_video_device = {
 	.release = video_device_release_empty,
 };
 
-static const struct v4l2_pix_format sur40_video_format = {
-	.pixelformat = V4L2_PIX_FMT_GREY,
-	.width  = SENSOR_RES_X / 2,
-	.height = SENSOR_RES_Y / 2,
-	.field = V4L2_FIELD_NONE,
-	.colorspace = V4L2_COLORSPACE_SRGB,
-	.bytesperline = SENSOR_RES_X / 2,
-	.sizeimage = (SENSOR_RES_X/2) * (SENSOR_RES_Y/2),
-};
-
 /* USB-specific object needed to register this driver with the USB subsystem. */
 static struct usb_driver sur40_driver = {
 	.name = DRIVER_SHORT,
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help