Hello-
This is an updated series of patches to add diagnostic data support to the
Atmel maXTouch driver.
There's an existing implementation in the open-source mxt-app tool, however
there are performance advantages to moving this code into the driver. The
algorithm for retrieving the data has been fairly consistent across a range of
chips, with the exception of the mXT1386 series (see patch). It would be good
if we could agree a single debugfs interface which could be supported by all
touchscreen chips that have this kind of feature, so I've attempted to
keep that part of this vendor neutral.
Changes since v1:
* adding a way to retrieve a single node at high performance
* adding some locking
* switching to using seq_file.
* numerous minor refactorings
This patch sequence is also available from:
https://github.com/ndyer/linux/commits/diagnostic-debug
A utility to display this data has now been released, and you can find it at:
https://github.com/ndyer/heatmap
I've recorded a couple of videos of the utility in action on a Pixel 2:
https://youtu.be/M0VD2gZt8Zk and https://youtu.be/nwDLB4zikzU
Any feedback appreciated.
Best regards
Nick Dyer
Both T100 and T9 handle range and orientation in a similar fashion.
Reduce duplication between the two implementations.
Signed-off-by: Nick Dyer <[email protected]>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 68 ++++++++++++--------------------
1 file changed, 26 insertions(+), 42 deletions(-)
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index c562205..4a1d218 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -113,8 +113,8 @@ struct t7_config {
#define MXT_T9_DETECT (1 << 7)
struct t9_range {
- u16 x;
- u16 y;
+ __le16 x;
+ __le16 y;
} __packed;
/* MXT_TOUCH_MULTI_T9 orient */
@@ -216,6 +216,7 @@ struct mxt_data {
unsigned int irq;
unsigned int max_x;
unsigned int max_y;
+ bool xy_switch;
bool in_bootloader;
u16 mem_size;
u8 t100_aux_ampl;
@@ -1665,8 +1666,8 @@ static int mxt_read_t9_resolution(struct mxt_data *data)
if (error)
return error;
- le16_to_cpus(&range.x);
- le16_to_cpus(&range.y);
+ data->max_x = get_unaligned_le16(&range.x);
+ data->max_y = get_unaligned_le16(&range.y);
error = __mxt_read_reg(client,
object->start_address + MXT_T9_ORIENT,
@@ -1674,23 +1675,7 @@ static int mxt_read_t9_resolution(struct mxt_data *data)
if (error)
return error;
- /* Handle default values */
- if (range.x == 0)
- range.x = 1023;
-
- if (range.y == 0)
- range.y = 1023;
-
- if (orient & MXT_T9_ORIENT_SWITCH) {
- data->max_x = range.y;
- data->max_y = range.x;
- } else {
- data->max_x = range.x;
- data->max_y = range.y;
- }
-
- dev_dbg(&client->dev,
- "Touchscreen size X%uY%u\n", data->max_x, data->max_y);
+ data->xy_switch = orient & MXT_T9_ORIENT_SWITCH;
return 0;
}
@@ -1708,13 +1693,14 @@ static int mxt_read_t100_config(struct mxt_data *data)
if (!object)
return -EINVAL;
+ /* read touchscreen dimensions */
error = __mxt_read_reg(client,
object->start_address + MXT_T100_XRANGE,
sizeof(range_x), &range_x);
if (error)
return error;
- le16_to_cpus(&range_x);
+ data->max_x = get_unaligned_le16(&range_x);
error = __mxt_read_reg(client,
object->start_address + MXT_T100_YRANGE,
@@ -1722,36 +1708,24 @@ static int mxt_read_t100_config(struct mxt_data *data)
if (error)
return error;
- le16_to_cpus(&range_y);
+ data->max_y = get_unaligned_le16(&range_y);
+ /* read orientation config */
error = __mxt_read_reg(client,
object->start_address + MXT_T100_CFG1,
1, &cfg);
if (error)
return error;
+ data->xy_switch = cfg & MXT_T100_CFG_SWITCHXY;
+
+ /* allocate aux bytes */
error = __mxt_read_reg(client,
object->start_address + MXT_T100_TCHAUX,
1, &tchaux);
if (error)
return error;
- /* Handle default values */
- if (range_x == 0)
- range_x = 1023;
-
- if (range_y == 0)
- range_y = 1023;
-
- if (cfg & MXT_T100_CFG_SWITCHXY) {
- data->max_x = range_y;
- data->max_y = range_x;
- } else {
- data->max_x = range_x;
- data->max_y = range_y;
- }
-
- /* allocate aux bytes */
aux = 6;
if (tchaux & MXT_T100_TCHAUX_VECT)
@@ -1767,9 +1741,6 @@ static int mxt_read_t100_config(struct mxt_data *data)
"T100 aux mappings vect:%u ampl:%u area:%u\n",
data->t100_aux_vect, data->t100_aux_ampl, data->t100_aux_area);
- dev_info(&client->dev,
- "T100 Touchscreen size X%uY%u\n", data->max_x, data->max_y);
-
return 0;
}
@@ -1828,6 +1799,19 @@ static int mxt_initialize_input_device(struct mxt_data *data)
return -EINVAL;
}
+ /* Handle default values and orientation switch */
+ if (data->max_x == 0)
+ data->max_x = 1023;
+
+ if (data->max_y == 0)
+ data->max_y = 1023;
+
+ if (data->xy_switch)
+ swap(data->max_x, data->max_y);
+
+ dev_info(dev, "Touchscreen size X%uY%u\n", data->max_x, data->max_y);
+
+ /* Register input device */
input_dev = input_allocate_device();
if (!input_dev) {
dev_err(dev, "Failed to allocate memory\n");
--
2.5.0
Retrieve refs data from the T37 diagnostic data object and expose it via
a binary attribute in debugfs.
Signed-off-by: Nick Dyer <[email protected]>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 208 +++++++++++++++++++++++++++++++
1 file changed, 208 insertions(+)
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 4a1d218..96000f4 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -28,6 +28,7 @@
#include <linux/of.h>
#include <linux/slab.h>
#include <asm/unaligned.h>
+#include <linux/debugfs.h>
/* Firmware files */
#define MXT_FW_NAME "maxtouch.fw"
@@ -124,6 +125,17 @@ 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 {
+ u8 mode;
+ u8 page;
+ u8 data[MXT_DIAGNOSTIC_SIZE];
+};
+
/* Define for MXT_GEN_COMMAND_T6 */
#define MXT_BOOT_VALUE 0xa5
#define MXT_RESET_VALUE 0x01
@@ -205,6 +217,18 @@ struct mxt_object {
u8 num_report_ids;
} __packed;
+#ifdef CONFIG_DEBUG_FS
+struct mxt_dbg {
+ u16 t37_address;
+ u16 diag_cmd_address;
+ struct t37_debug *t37_buf;
+ unsigned int t37_pages;
+ unsigned int t37_nodes;
+
+ struct dentry *debugfs_dir;
+};
+#endif
+
/* Each client has this additional data */
struct mxt_data {
struct i2c_client *client;
@@ -233,6 +257,9 @@ struct mxt_data {
u8 num_touchids;
u8 multitouch;
struct t7_config t7_cfg;
+#ifdef CONFIG_DEBUG_FS
+ struct mxt_dbg dbg;
+#endif
/* Cached parameters from object table */
u16 T5_address;
@@ -2043,6 +2070,184 @@ recheck:
return 0;
}
+#ifdef CONFIG_DEBUG_FS
+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 void mxt_convert_debug_pages(struct seq_file *s, struct mxt_data *data)
+{
+ struct mxt_dbg *dbg = &data->dbg;
+ unsigned int x = 0;
+ unsigned int y = 0;
+ unsigned int i;
+ u16 val;
+
+ for (i = 0; i < dbg->t37_nodes; i++) {
+ val = mxt_get_debug_value(data, x, y);
+ seq_write(s, &val, sizeof(u16));
+
+ /* Next value */
+ if (++x >= data->info.matrix_xsize) {
+ x = 0;
+ y++;
+ }
+ }
+}
+
+static int mxt_read_diagnostic_debug(struct seq_file *s, void *d)
+{
+ struct mxt_data *data = dev_get_drvdata(s->private);
+ struct mxt_dbg *dbg = &data->dbg;
+ int retries = 0;
+ int page;
+ int ret;
+ u8 mode = MXT_DIAGNOSTIC_DELTAS;
+ u8 cmd = mode;
+ struct t37_debug *p;
+
+ 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;
+
+ /* Poll until command is actioned */
+ msleep(20);
+wait_cmd:
+ /* Read first two bytes only */
+ ret = __mxt_read_reg(data->client, dbg->t37_address,
+ 2, p);
+ if (ret)
+ return ret;
+
+ if ((p->mode != mode) || (p->page != page)) {
+ if (retries++ > 100)
+ return -EINVAL;
+
+ msleep(20);
+ goto wait_cmd;
+ }
+
+ /* Read entire T37 page */
+ ret = __mxt_read_reg(data->client, dbg->t37_address,
+ sizeof(struct t37_debug), p);
+ if (ret)
+ return ret;
+
+ 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;
+ }
+
+ mxt_convert_debug_pages(s, data);
+
+ return 0;
+}
+
+static int mxt_debugfs_data_open(struct inode *inode, struct file *f)
+{
+ struct mxt_data *data = inode->i_private;
+ size_t size = data->dbg.t37_nodes * sizeof(u16);
+
+ return single_open_size(f, mxt_read_diagnostic_debug, data, size);
+}
+
+static const struct file_operations mxt_debugfs_data_ops = {
+ .owner = THIS_MODULE,
+ .open = mxt_debugfs_data_open,
+ .release = single_release,
+ .read = seq_read,
+ .llseek = seq_lseek
+};
+
+static void mxt_debugfs_remove(struct mxt_data *data)
+{
+ debugfs_remove_recursive(data->dbg.debugfs_dir);
+}
+
+static void mxt_debugfs_init(struct mxt_data *data)
+{
+ struct mxt_dbg *dbg = &data->dbg;
+ struct mxt_object *object;
+ char dirname[50];
+ struct dentry *dent;
+
+ object = mxt_get_object(data, MXT_GEN_COMMAND_T6);
+ if (!object)
+ return;
+
+ dbg->diag_cmd_address = object->start_address + MXT_COMMAND_DIAGNOSTIC;
+
+ object = mxt_get_object(data, MXT_DEBUG_DIAGNOSTIC_T37);
+ if (!object)
+ return;
+
+ if (mxt_obj_size(object) != sizeof(struct t37_debug)) {
+ dev_warn(&data->client->dev, "Bad T37 size");
+ return;
+ }
+
+ dbg->t37_address = object->start_address;
+
+ snprintf(dirname, sizeof(dirname), "heatmap-%s-%s",
+ dev_driver_string(&data->client->dev),
+ dev_name(&data->client->dev));
+
+ dbg->debugfs_dir = debugfs_create_dir(dirname, NULL);
+ if (!dbg->debugfs_dir) {
+ dev_err(&data->client->dev, "Error creating debugfs dir\n");
+ return;
+ }
+
+ /* Calculate size of data and allocate buffer */
+ dbg->t37_nodes = data->info.matrix_xsize * data->info.matrix_ysize;
+ dbg->t37_pages = dbg->t37_nodes * sizeof(u16)
+ / sizeof(dbg->t37_buf->data) + 1;
+
+ dbg->t37_buf = devm_kzalloc(&data->client->dev,
+ sizeof(struct t37_debug) * dbg->t37_pages,
+ GFP_KERNEL);
+ if (!dbg->t37_buf)
+ goto error;
+
+ dent = debugfs_create_file("deltas", S_IRUGO,
+ dbg->debugfs_dir, data,
+ &mxt_debugfs_data_ops);
+ if (!dent)
+ goto error;
+
+ return;
+
+error:
+ dev_err(&data->client->dev, "Error creating debugfs entry\n");
+ mxt_debugfs_remove(data);
+}
+#else
+static inline void mxt_debugfs_remove(struct mxt_data *data)
+{
+}
+
+static inline void mxt_debugfs_init(struct mxt_data *data)
+{
+}
+#endif /* CONFIG_DEBUG_FS */
+
static int mxt_configure_objects(struct mxt_data *data,
const struct firmware *cfg)
{
@@ -2070,6 +2275,8 @@ static int mxt_configure_objects(struct mxt_data *data,
dev_warn(dev, "No touch object detected\n");
}
+ mxt_debugfs_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,
@@ -2617,6 +2824,7 @@ static int mxt_remove(struct i2c_client *client)
{
struct mxt_data *data = i2c_get_clientdata(client);
+ mxt_debugfs_remove(data);
sysfs_remove_group(&client->dev.kobj, &mxt_attr_group);
free_irq(data->irq, data);
mxt_free_input_device(data);
--
2.5.0
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.
Signed-off-by: Nick Dyer <[email protected]>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 41 ++++++++++++++++++++++++++++----
1 file changed, 36 insertions(+), 5 deletions(-)
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 96000f4..a177019 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -100,6 +100,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
@@ -145,7 +147,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)
@@ -241,6 +245,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;
@@ -1688,6 +1694,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)
@@ -1737,6 +1755,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,
@@ -2077,7 +2107,7 @@ static u16 mxt_get_debug_value(struct mxt_data *data, unsigned int x,
struct mxt_dbg *dbg = &data->dbg;
unsigned int ofs, page;
- ofs = (y + (x * (data->info.matrix_ysize))) * sizeof(u16);
+ ofs = (y + (x * data->info.matrix_ysize)) * sizeof(u16);
page = ofs / MXT_DIAGNOSTIC_SIZE;
ofs %= MXT_DIAGNOSTIC_SIZE;
@@ -2097,7 +2127,7 @@ static void mxt_convert_debug_pages(struct seq_file *s, struct mxt_data *data)
seq_write(s, &val, sizeof(u16));
/* Next value */
- if (++x >= data->info.matrix_xsize) {
+ if (++x >= data->xsize) {
x = 0;
y++;
}
@@ -2216,9 +2246,10 @@ static void mxt_debugfs_init(struct mxt_data *data)
}
/* Calculate size of data and allocate buffer */
- dbg->t37_nodes = data->info.matrix_xsize * data->info.matrix_ysize;
- dbg->t37_pages = dbg->t37_nodes * sizeof(u16)
- / sizeof(dbg->t37_buf->data) + 1;
+ dbg->t37_nodes = data->xsize * data->ysize;
+ dbg->t37_pages = ((data->xsize * data->info.matrix_ysize)
+ * sizeof(u16) / sizeof(dbg->t37_buf->data)) + 1;
+
dbg->t37_buf = devm_kzalloc(&data->client->dev,
sizeof(struct t37_debug) * dbg->t37_pages,
--
2.5.0
Invert the diagnostic data to match the orientation of the input device.
Signed-off-by: Nick Dyer <[email protected]>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 30 +++++++++++++++++++++++-------
1 file changed, 23 insertions(+), 7 deletions(-)
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index a177019..48bf9ec 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -122,6 +122,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
@@ -153,6 +155,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)
@@ -244,7 +248,9 @@ struct mxt_data {
unsigned int irq;
unsigned int max_x;
unsigned int max_y;
- bool xy_switch;
+ bool invertx;
+ bool inverty;
+ bool xyswitch;
u8 xsize;
u8 ysize;
bool in_bootloader;
@@ -1720,7 +1726,9 @@ static int mxt_read_t9_resolution(struct mxt_data *data)
if (error)
return error;
- data->xy_switch = orient & MXT_T9_ORIENT_SWITCH;
+ data->xyswitch = orient & MXT_T9_ORIENT_SWITCH;
+ data->invertx = orient & MXT_T9_ORIENT_INVERTX;
+ data->inverty = orient & MXT_T9_ORIENT_INVERTY;
return 0;
}
@@ -1774,7 +1782,9 @@ static int mxt_read_t100_config(struct mxt_data *data)
if (error)
return error;
- data->xy_switch = cfg & MXT_T100_CFG_SWITCHXY;
+ data->xyswitch = 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,
@@ -1863,7 +1873,7 @@ static int mxt_initialize_input_device(struct mxt_data *data)
if (data->max_y == 0)
data->max_y = 1023;
- if (data->xy_switch)
+ if (data->xyswitch)
swap(data->max_x, data->max_y);
dev_info(dev, "Touchscreen size X%uY%u\n", data->max_x, data->max_y);
@@ -2119,15 +2129,21 @@ static void mxt_convert_debug_pages(struct seq_file *s, struct mxt_data *data)
struct mxt_dbg *dbg = &data->dbg;
unsigned int x = 0;
unsigned int y = 0;
- unsigned int i;
+ unsigned int i, rx, ry;
u16 val;
for (i = 0; i < dbg->t37_nodes; i++) {
- val = mxt_get_debug_value(data, x, y);
+ /* Handle orientation */
+ rx = data->xyswitch ? y : x;
+ ry = data->xyswitch ? x : y;
+ rx = data->invertx ? (data->xsize - 1 - rx) : rx;
+ ry = data->inverty ? (data->ysize - 1 - ry) : ry;
+
+ val = mxt_get_debug_value(data, rx, ry);
seq_write(s, &val, sizeof(u16));
/* Next value */
- if (++x >= data->xsize) {
+ if (++x >= (data->xyswitch ? data->ysize : data->xsize)) {
x = 0;
y++;
}
--
2.5.0
The mXT1386 family of chips have a different architecture which splits
the diagnostic data into 3 columns.
Signed-off-by: Nick Dyer <[email protected]>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 30 +++++++++++++++++++++++++++---
1 file changed, 27 insertions(+), 3 deletions(-)
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 48bf9ec..a2f11836 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -134,6 +134,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 {
u8 mode;
u8 page;
@@ -2114,13 +2118,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]);
}
@@ -2229,6 +2247,7 @@ static void mxt_debugfs_remove(struct mxt_data *data)
static void mxt_debugfs_init(struct mxt_data *data)
{
+ struct mxt_info *info = &data->info;
struct mxt_dbg *dbg = &data->dbg;
struct mxt_object *object;
char dirname[50];
@@ -2263,8 +2282,13 @@ static void mxt_debugfs_init(struct mxt_data *data)
/* Calculate size of data and allocate buffer */
dbg->t37_nodes = data->xsize * data->ysize;
- dbg->t37_pages = ((data->xsize * data->info.matrix_ysize)
- * sizeof(u16) / sizeof(dbg->t37_buf->data)) + 1;
+
+ if (info->family_id == MXT_FAMILY_1386)
+ dbg->t37_pages = MXT1386_COLUMNS * MXT1386_PAGES_PER_COLUMN;
+ else
+ dbg->t37_pages = ((data->xsize * info->matrix_ysize)
+ * sizeof(u16) / sizeof(dbg->t37_buf->data))
+ + 1;
dbg->t37_buf = devm_kzalloc(&data->client->dev,
--
2.5.0
There are different datatypes available from a maXTouch chip. Add
support to retrieve reference data as well.
Signed-off-by: Nick Dyer <[email protected]>
---
drivers/input/touchscreen/atmel_mxt_ts.c | 75 +++++++++++++++++++++++++-------
1 file changed, 59 insertions(+), 16 deletions(-)
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index a2f11836..bccd7bc 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -132,6 +132,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
@@ -211,6 +212,8 @@ enum t100_type {
#define MXT_PIXELS_PER_MM 20
+struct mxt_data;
+
struct mxt_info {
u8 family_id;
u8 variant_id;
@@ -230,6 +233,27 @@ struct mxt_object {
} __packed;
#ifdef CONFIG_DEBUG_FS
+struct mxt_debug_datatype {
+ u8 mode;
+ char *name;
+};
+
+struct mxt_debug_entry {
+ struct mxt_data *data;
+ const struct mxt_debug_datatype *datatype;
+};
+
+static const struct mxt_debug_datatype mxt_dbg_datatypes[] = {
+ {
+ .mode = MXT_DIAGNOSTIC_REFS,
+ .name = "refs",
+ },
+ {
+ .mode = MXT_DIAGNOSTIC_DELTAS,
+ .name = "deltas",
+ },
+};
+
struct mxt_dbg {
u16 t37_address;
u16 diag_cmd_address;
@@ -238,6 +262,8 @@ struct mxt_dbg {
unsigned int t37_nodes;
struct dentry *debugfs_dir;
+ struct mxt_debug_entry entries[ARRAY_SIZE(mxt_dbg_datatypes)];
+ struct mutex dbg_mutex;
};
#endif
@@ -2142,7 +2168,7 @@ static u16 mxt_get_debug_value(struct mxt_data *data, unsigned int x,
return get_unaligned_le16(&dbg->t37_buf[page].data[ofs]);
}
-static void mxt_convert_debug_pages(struct seq_file *s, struct mxt_data *data)
+static int mxt_convert_debug_pages(struct seq_file *s, struct mxt_data *data)
{
struct mxt_dbg *dbg = &data->dbg;
unsigned int x = 0;
@@ -2166,26 +2192,31 @@ static void mxt_convert_debug_pages(struct seq_file *s, struct mxt_data *data)
y++;
}
}
+
+ return 0;
}
static int mxt_read_diagnostic_debug(struct seq_file *s, void *d)
{
- struct mxt_data *data = dev_get_drvdata(s->private);
+ struct mxt_debug_entry *e = s->private;
+ struct mxt_data *data = e->data;
struct mxt_dbg *dbg = &data->dbg;
int retries = 0;
int page;
int ret;
- u8 mode = MXT_DIAGNOSTIC_DELTAS;
+ u8 mode = e->datatype->mode;
u8 cmd = mode;
struct t37_debug *p;
+ mutex_lock(&dbg->dbg_mutex);
+
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;
+ goto release;
retries = 0;
@@ -2196,7 +2227,7 @@ wait_cmd:
ret = __mxt_read_reg(data->client, dbg->t37_address,
2, p);
if (ret)
- return ret;
+ goto release;
if ((p->mode != mode) || (p->page != page)) {
if (retries++ > 100)
@@ -2210,7 +2241,7 @@ wait_cmd:
ret = __mxt_read_reg(data->client, dbg->t37_address,
sizeof(struct t37_debug), p);
if (ret)
- return ret;
+ goto release;
dev_dbg(&data->client->dev, "%s page:%d retries:%d\n",
__func__, page, retries);
@@ -2219,17 +2250,19 @@ wait_cmd:
cmd = MXT_DIAGNOSTIC_PAGEUP;
}
- mxt_convert_debug_pages(s, data);
+ ret = mxt_convert_debug_pages(s, data);
- return 0;
+release:
+ mutex_unlock(&dbg->dbg_mutex);
+ return ret;
}
static int mxt_debugfs_data_open(struct inode *inode, struct file *f)
{
- struct mxt_data *data = inode->i_private;
- size_t size = data->dbg.t37_nodes * sizeof(u16);
+ struct mxt_debug_entry *e = inode->i_private;
+ size_t size = e->data->dbg.t37_nodes * sizeof(u16);
- return single_open_size(f, mxt_read_diagnostic_debug, data, size);
+ return single_open_size(f, mxt_read_diagnostic_debug, e, size);
}
static const struct file_operations mxt_debugfs_data_ops = {
@@ -2252,6 +2285,8 @@ static void mxt_debugfs_init(struct mxt_data *data)
struct mxt_object *object;
char dirname[50];
struct dentry *dent;
+ struct mxt_debug_entry *e;
+ int i;
object = mxt_get_object(data, MXT_GEN_COMMAND_T6);
if (!object)
@@ -2297,11 +2332,19 @@ static void mxt_debugfs_init(struct mxt_data *data)
if (!dbg->t37_buf)
goto error;
- dent = debugfs_create_file("deltas", S_IRUGO,
- dbg->debugfs_dir, data,
- &mxt_debugfs_data_ops);
- if (!dent)
- goto error;
+ for (i = 0; i < ARRAY_SIZE(mxt_dbg_datatypes); i++) {
+ e = &dbg->entries[i];
+ e->data = data;
+ e->datatype = mxt_dbg_datatypes + i;
+
+ dent = debugfs_create_file(mxt_dbg_datatypes[i].name, S_IRUGO,
+ dbg->debugfs_dir, e,
+ &mxt_debugfs_data_ops);
+ if (!dent)
+ goto error;
+ }
+
+ mutex_init(&dbg->dbg_mutex);
return;
--
2.5.0
Add information to debugfs to allow a generic utility to retrieve
screen parameters and info.
Signed-off-by: Nick Dyer <[email protected]>
---
Documentation/ABI/testing/debugfs-heatmap | 60 +++++++++++++++++++++++++++++++
drivers/input/touchscreen/atmel_mxt_ts.c | 48 +++++++++++++++++++++++--
2 files changed, 105 insertions(+), 3 deletions(-)
create mode 100644 Documentation/ABI/testing/debugfs-heatmap
diff --git a/Documentation/ABI/testing/debugfs-heatmap b/Documentation/ABI/testing/debugfs-heatmap
new file mode 100644
index 0000000..9246340
--- /dev/null
+++ b/Documentation/ABI/testing/debugfs-heatmap
@@ -0,0 +1,60 @@
+What: /sys/kernel/debug/heatmap-dev_driver_string-dev_name/
+Date:
+KernelVersion:
+Contact:
+Description:
+ A directory will be created under heatmap for each device which
+ provides heatmap data.
+
+What: /sys/kernel/debug/heatmap-dev_driver_string-dev_name/datatype/
+Date:
+KernelVersion:
+Contact:
+Description:
+ The device can have multiple heatmap data types. A directory is created
+ for each one.
+
+What: /sys/kernel/debug/heatmap-xxx/datatype/format
+Date:
+KernelVersion:
+Contact:
+Description:
+ Specifies the type of each data value, one of:
+ uint8
+ uint16
+ uint32
+ int8
+ int16
+ int32
+
+What: /sys/kernel/debug/heatmap-xxx/datatype/width
+Date:
+KernelVersion:
+Contact:
+Description:
+ The width of the data.
+
+What: /sys/kernel/debug/heatmap-xxx/datatype/height
+Date:
+KernelVersion:
+Contact:
+Description:
+ The height of the data.
+
+What: /sys/kernel/debug/heatmap-xxx/datatype/name
+Date:
+KernelVersion:
+Contact:
+Description:
+ Display name for the data.
+
+What: /sys/kernel/debug/heatmap-xxx/datatype/data
+Date:
+KernelVersion:
+Contact:
+Description:
+ Binary attribute for the data.
+
+ The orientation of the data should correspond to the co-ordinates
+ reported to the input layer. Starting at the top left hand corner, rows
+ then columns. The endianness of data values will be as per host cpu.
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index bccd7bc..3f12915 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -236,21 +236,31 @@ struct mxt_object {
struct mxt_debug_datatype {
u8 mode;
char *name;
+ char *desc;
+ char *format;
};
struct mxt_debug_entry {
struct mxt_data *data;
const struct mxt_debug_datatype *datatype;
+ u16 width;
+ u16 height;
+ struct debugfs_blob_wrapper format_wrapper;
+ struct debugfs_blob_wrapper desc_wrapper;
};
static const struct mxt_debug_datatype mxt_dbg_datatypes[] = {
{
.mode = MXT_DIAGNOSTIC_REFS,
.name = "refs",
+ .desc = "Mutual Capacitance References",
+ .format = "uint16",
},
{
.mode = MXT_DIAGNOSTIC_DELTAS,
.name = "deltas",
+ .desc = "Mutual Capacitance Deltas",
+ .format = "int16",
},
};
@@ -2286,6 +2296,7 @@ static void mxt_debugfs_init(struct mxt_data *data)
char dirname[50];
struct dentry *dent;
struct mxt_debug_entry *e;
+ struct dentry *dir;
int i;
object = mxt_get_object(data, MXT_GEN_COMMAND_T6);
@@ -2337,9 +2348,40 @@ static void mxt_debugfs_init(struct mxt_data *data)
e->data = data;
e->datatype = mxt_dbg_datatypes + i;
- dent = debugfs_create_file(mxt_dbg_datatypes[i].name, S_IRUGO,
- dbg->debugfs_dir, e,
- &mxt_debugfs_data_ops);
+ dir = debugfs_create_dir(mxt_dbg_datatypes[i].name,
+ dbg->debugfs_dir);
+ if (!dir)
+ goto error;
+
+ e->width = data->xyswitch ? data->ysize : data->xsize;
+ e->height = data->xyswitch ? data->xsize : data->ysize;
+
+ e->format_wrapper.data = (void *)e->datatype->format;
+ e->format_wrapper.size = strlen(e->datatype->format);
+ dent = debugfs_create_blob("format", S_IRUGO,
+ dir, &e->format_wrapper);
+ if (!dent)
+ goto error;
+
+ e->desc_wrapper.data = (void *)e->datatype->desc;
+ e->desc_wrapper.size = strlen(e->datatype->desc);
+ dent = debugfs_create_blob("name", S_IRUGO,
+ dir, &e->desc_wrapper);
+ if (!dent)
+ goto error;
+
+ dent = debugfs_create_u16("width", S_IRUGO,
+ dir, &e->width);
+ if (!dent)
+ goto error;
+
+ dent = debugfs_create_u16("height", S_IRUGO,
+ dir, &e->height);
+ if (!dent)
+ goto error;
+
+ dent = debugfs_create_file("data", S_IRUGO,
+ dir, e, &mxt_debugfs_data_ops);
if (!dent)
goto error;
}
--
2.5.0
Add files in debugfs directory with info about the chip and input device.
Signed-off-by: Nick Dyer <[email protected]>
---
Documentation/ABI/testing/debugfs-heatmap | 14 ++++++++++++++
drivers/input/touchscreen/atmel_mxt_ts.c | 22 ++++++++++++++++++++++
2 files changed, 36 insertions(+)
diff --git a/Documentation/ABI/testing/debugfs-heatmap b/Documentation/ABI/testing/debugfs-heatmap
index 9246340..95668a5 100644
--- a/Documentation/ABI/testing/debugfs-heatmap
+++ b/Documentation/ABI/testing/debugfs-heatmap
@@ -6,6 +6,13 @@ Description:
A directory will be created under heatmap for each device which
provides heatmap data.
+What: /sys/kernel/debug/heatmap-dev_driver_string-dev_name/info
+Date:
+KernelVersion:
+Contact:
+Description:
+ Info relating to the device, eg hardware/firmware version
+
What: /sys/kernel/debug/heatmap-dev_driver_string-dev_name/datatype/
Date:
KernelVersion:
@@ -48,6 +55,13 @@ Contact:
Description:
Display name for the data.
+What: /sys/kernel/debug/heatmap-xxx/datatype/input_name
+Date:
+KernelVersion:
+Contact:
+Description:
+ The name of the corresponding input device, if relevant.
+
What: /sys/kernel/debug/heatmap-xxx/datatype/data
Date:
KernelVersion:
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 3f12915..05bacc6 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -274,6 +274,9 @@ struct mxt_dbg {
struct dentry *debugfs_dir;
struct mxt_debug_entry entries[ARRAY_SIZE(mxt_dbg_datatypes)];
struct mutex dbg_mutex;
+ struct debugfs_blob_wrapper input_name_wrapper;
+ char info[50];
+ struct debugfs_blob_wrapper info_wrapper;
};
#endif
@@ -2326,6 +2329,18 @@ static void mxt_debugfs_init(struct mxt_data *data)
return;
}
+ dbg->info_wrapper.data = dbg->info;
+ dbg->info_wrapper.size = sizeof(dbg->info);
+ scnprintf(dbg->info, sizeof(dbg->info),
+ "Family: %u Variant: %u Firmware V%u.%u.%02X",
+ info->family_id, info->variant_id, info->version >> 4,
+ info->version & 0xf, info->build);
+
+ dent = debugfs_create_blob("info", S_IRUGO, dbg->debugfs_dir,
+ &dbg->info_wrapper);
+ if (!dent)
+ goto error;
+
/* Calculate size of data and allocate buffer */
dbg->t37_nodes = data->xsize * data->ysize;
@@ -2370,6 +2385,13 @@ static void mxt_debugfs_init(struct mxt_data *data)
if (!dent)
goto error;
+ dbg->input_name_wrapper.data = (void *)data->input_dev->name;
+ dbg->input_name_wrapper.size = strlen(data->input_dev->name);
+ dent = debugfs_create_blob("input_name", S_IRUGO,
+ dir, &dbg->input_name_wrapper);
+ if (!dent)
+ goto error;
+
dent = debugfs_create_u16("width", S_IRUGO,
dir, &e->width);
if (!dent)
--
2.5.0
Add support for retrieving a single node of data at high rate.
---
drivers/input/touchscreen/atmel_mxt_ts.c | 60 +++++++++++++++++++++++++++++---
1 file changed, 55 insertions(+), 5 deletions(-)
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 05bacc6..fd3aaae 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -238,6 +238,7 @@ struct mxt_debug_datatype {
char *name;
char *desc;
char *format;
+ bool single_node;
};
struct mxt_debug_entry {
@@ -262,6 +263,20 @@ static const struct mxt_debug_datatype mxt_dbg_datatypes[] = {
.desc = "Mutual Capacitance Deltas",
.format = "int16",
},
+ {
+ .mode = MXT_DIAGNOSTIC_REFS,
+ .name = "single_node_refs",
+ .desc = "Single Node References",
+ .format = "uint16",
+ .single_node = true,
+ },
+ {
+ .mode = MXT_DIAGNOSTIC_DELTAS,
+ .name = "single_node_deltas",
+ .format = "int16",
+ .desc = "Single Node Deltas",
+ .single_node = true,
+ },
};
struct mxt_dbg {
@@ -270,6 +285,7 @@ struct mxt_dbg {
struct t37_debug *t37_buf;
unsigned int t37_pages;
unsigned int t37_nodes;
+ unsigned int single_node_ofs;
struct dentry *debugfs_dir;
struct mxt_debug_entry entries[ARRAY_SIZE(mxt_dbg_datatypes)];
@@ -2216,14 +2232,16 @@ static int mxt_read_diagnostic_debug(struct seq_file *s, void *d)
struct mxt_dbg *dbg = &data->dbg;
int retries = 0;
int page;
+ int pages = e->datatype->single_node ? 1 : dbg->t37_pages;
int ret;
u8 mode = e->datatype->mode;
u8 cmd = mode;
struct t37_debug *p;
+ u16 val;
mutex_lock(&dbg->dbg_mutex);
- for (page = 0; page < dbg->t37_pages; page++) {
+ for (page = 0; page < pages; page++) {
p = dbg->t37_buf + page;
ret = mxt_write_reg(data->client, dbg->diag_cmd_address,
@@ -2263,7 +2281,14 @@ wait_cmd:
cmd = MXT_DIAGNOSTIC_PAGEUP;
}
- ret = mxt_convert_debug_pages(s, data);
+ if (e->datatype->single_node) {
+ val = get_unaligned_le16(&dbg->t37_buf[0]
+ .data[dbg->single_node_ofs]);
+ seq_write(s, &val, sizeof(u16));
+ ret = 0;
+ } else {
+ ret = mxt_convert_debug_pages(s, data);
+ }
release:
mutex_unlock(&dbg->dbg_mutex);
@@ -2273,7 +2298,12 @@ release:
static int mxt_debugfs_data_open(struct inode *inode, struct file *f)
{
struct mxt_debug_entry *e = inode->i_private;
- size_t size = e->data->dbg.t37_nodes * sizeof(u16);
+ size_t size;
+
+ if (e->datatype->single_node)
+ size = sizeof(u16);
+ else
+ size = e->data->dbg.t37_nodes * sizeof(u16);
return single_open_size(f, mxt_read_diagnostic_debug, e, size);
}
@@ -2291,6 +2321,19 @@ static void mxt_debugfs_remove(struct mxt_data *data)
debugfs_remove_recursive(data->dbg.debugfs_dir);
}
+static void mxt_debugfs_calc_single_node_ofs(struct mxt_data *data)
+{
+ struct mxt_info *info = &data->info;
+ int ofs = data->ysize / 2;
+
+ while ((ofs + info->matrix_ysize) <= (MXT_DIAGNOSTIC_SIZE/sizeof(u16)))
+ ofs += info->matrix_ysize;
+
+ dev_dbg(&data->client->dev, "Single node ofs: %d\n", ofs);
+
+ data->dbg.single_node_ofs = ofs;
+}
+
static void mxt_debugfs_init(struct mxt_data *data)
{
struct mxt_info *info = &data->info;
@@ -2358,6 +2401,8 @@ static void mxt_debugfs_init(struct mxt_data *data)
if (!dbg->t37_buf)
goto error;
+ mxt_debugfs_calc_single_node_ofs(data);
+
for (i = 0; i < ARRAY_SIZE(mxt_dbg_datatypes); i++) {
e = &dbg->entries[i];
e->data = data;
@@ -2368,8 +2413,13 @@ static void mxt_debugfs_init(struct mxt_data *data)
if (!dir)
goto error;
- e->width = data->xyswitch ? data->ysize : data->xsize;
- e->height = data->xyswitch ? data->xsize : data->ysize;
+ if (e->datatype->single_node) {
+ e->width = 1;
+ e->height = 1;
+ } else {
+ e->width = data->xyswitch ? data->ysize : data->xsize;
+ e->height = data->xyswitch ? data->xsize : data->ysize;
+ }
e->format_wrapper.data = (void *)e->datatype->format;
e->format_wrapper.size = strlen(e->datatype->format);
--
2.5.0