Skip to content

Commit

Permalink
input: touchscreen: add dfr0550 support
Browse files Browse the repository at this point in the history
Add support for the touch controller found on a DFROBOT DFR0550/DFR0776
touchscreen display.

These touchscreen displays are intended to be compatible with the official
Raspberry Pi 7in display which has an FTx06 touch controller directly
attached to the 15pin connector to the host processor. However these
displays have an FTx06 touch controller that connected to an I2C master
on a STM32F103 micro controller which polls the FTx06 and emulates a
virtual I2C device connected to the 15pin connector to the host processor.

The emulated FTx06 implements a subset of the FTx06 register set but
must be read with individual transactions between reading the number
of points and the point data itself.

Additionally there is no IRQ made available so this is a polling
driver.

Signed-off-by: Tim Harvey <tharvey@gateworks.com>
  • Loading branch information
Gateworks committed Jul 19, 2023
1 parent b2acd92 commit 5bf0ffc
Show file tree
Hide file tree
Showing 4 changed files with 220 additions and 0 deletions.
5 changes: 5 additions & 0 deletions MAINTAINERS
Original file line number Diff line number Diff line change
Expand Up @@ -6029,6 +6029,11 @@ F: include/net/devlink.h
F: include/uapi/linux/devlink.h
F: net/core/devlink.c

DFR0550 TOUCHSCREEN DRIVER
M: Tim Harvey <harvey.tim@gateworks.com>
S: Supported
F: drivers/input/touchscreen/dfr0550.c

DH ELECTRONICS IMX6 DHCOM BOARD SUPPORT
M: Christoph Niedermaier <cniedermaier@dh-electronics.com>
L: kernel@dh-electronics.com
Expand Down
12 changes: 12 additions & 0 deletions drivers/input/touchscreen/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,18 @@ config TOUCHSCREEN_DA9052
To compile this driver as a module, choose M here: the
module will be called da9052_tsi.

config TOUCHSCREEN_DFR0550
tristate "Touchscreen support for DFROBOT DFR0550"
depends on I2C
help
Say Y here to enable the support for the touchscreen found
on DFROBOT DFR0550 and DFR0678 displays.

If unsure, say N.

To compile this driver as a module, choose M here: the
module will be called dfr0550.

config TOUCHSCREEN_DYNAPRO
tristate "Dynapro serial touchscreen"
select SERIO
Expand Down
1 change: 1 addition & 0 deletions drivers/input/touchscreen/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ obj-$(CONFIG_TOUCHSCREEN_CYTTSP4_I2C) += cyttsp4_i2c.o cyttsp_i2c_common.o
obj-$(CONFIG_TOUCHSCREEN_CYTTSP4_SPI) += cyttsp4_spi.o
obj-$(CONFIG_TOUCHSCREEN_DA9034) += da9034-ts.o
obj-$(CONFIG_TOUCHSCREEN_DA9052) += da9052_tsi.o
obj-$(CONFIG_TOUCHSCREEN_DFR0550) += dfr0550.o
obj-$(CONFIG_TOUCHSCREEN_DYNAPRO) += dynapro.o
obj-$(CONFIG_TOUCHSCREEN_EDT_FT5X06) += edt-ft5x06.o
obj-$(CONFIG_TOUCHSCREEN_HAMPSHIRE) += hampshire.o
Expand Down
202 changes: 202 additions & 0 deletions drivers/input/touchscreen/dfr0550.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Driver for the touch controller found on a DFROBOT DFR0550/DFR0776
* touchscreen display.
*
* These touchscreen displays are intended to be compatible with the official
* Raspberry Pi 7in display which has an FTx06 touch controller directly
* attached to the 15pin connector to the host processor. However these
* displays have an FTx06 touch controller that connected to an I2C master
* on a STM32F103 micro controller which polls the FTx06 and emulates a
* virtual I2C device connected to the 15pin connector to the host processor.
* The emulated FTx06 implements a subset of the FTx06 register set but
* must be read with individual transactions between reading the number
* of points and the point data itself.
*
* Additionally there is no IRQ made available so this is a polling driver.
*/

#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/input/touchscreen.h>
#include <linux/kernel.h>
#include <linux/module.h>

#define TOUCH_EVENT_DOWN 0x00
#define TOUCH_EVENT_UP 0x01
#define TOUCH_EVENT_ON 0x02
#define TOUCH_EVENT_RESERVED 0x03

struct dfr0550_ts_data {
struct i2c_client *client;
struct input_dev *input;
struct touchscreen_properties prop;
int max_support_points;
unsigned int known_ids;
u16 num_x;
u16 num_y;
};

static int dfr0550_i2c_read(struct i2c_client *client, u8 reg, u8 *buf, int len)
{
struct i2c_msg msgs[2];
int ret;

msgs[0].flags = 0;
msgs[0].addr = client->addr;
msgs[0].len = 1;
msgs[0].buf = &reg;

msgs[1].flags = I2C_M_RD;
msgs[1].addr = client->addr;
msgs[1].len = len;
msgs[1].buf = buf;

ret = i2c_transfer(client->adapter, msgs, 2);
return reg < 0 ? ret : (ret != ARRAY_SIZE(msgs) ? -EIO : 0);
}

static void dfr0550_ts_poll(struct input_dev *input)
{
struct dfr0550_ts_data *ts = input_get_drvdata(input);
unsigned int active_ids = 0;
int i, type, x, y, id, points;
long released_ids;
u8 buf[4];
int error;

error = dfr0550_i2c_read(ts->client, 0x02, buf, 1);
if (error)
goto error;
/* official rpi 7in display with ft5x06 shows 0xff until touched */
if (buf[0] == 0xff)
return;
points = min(buf[0] & 0xf, ts->max_support_points);

for (i = 0; i < points; i++) {
error = dfr0550_i2c_read(ts->client, 3+6*i, buf, 4);
if (error)
goto error;

type = (buf[0] >> 6) & 0x3;
id = (buf[2] >> 4) & 0xf;
x = ((buf[0] & 0xf) << 8) + buf[1];
y = ((buf[2] & 0xf) << 8) + buf[3];

active_ids |= BIT(id);
if (type == TOUCH_EVENT_UP ||
type == TOUCH_EVENT_ON) {
input_mt_slot(input, id);
input_mt_report_slot_state(input, MT_TOOL_FINGER, 1);
touchscreen_report_pos(input, &ts->prop, x, y, true);
}
}

released_ids = ts->known_ids & ~active_ids;
for_each_set_bit(i, &released_ids, ts->max_support_points) {
input_mt_slot(input, i);
input_mt_report_slot_inactive(input);
}
ts->known_ids = active_ids;

input_mt_sync_frame(input);
input_mt_report_pointer_emulation(input, true);
input_sync(input);

return;

error:
dev_err_ratelimited(&ts->client->dev,
"Unable to fetch data, error: %d\n", error);
}

static int dfr0550_ts_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct dfr0550_ts_data *tsdata;
struct input_dev *input;
u32 poll_interval = 0;
int error;

tsdata = devm_kzalloc(&client->dev, sizeof(*tsdata), GFP_KERNEL);
if (!tsdata)
return -ENOMEM;

input = devm_input_allocate_device(&client->dev);
if (!input) {
dev_err(&client->dev, "failed to allocate input device.\n");
return -ENOMEM;
}

tsdata->client = client;
tsdata->input = input;

input->name = "dfr0550-ts";
input->id.bustype = BUS_I2C;
input->dev.parent = &client->dev;

input_set_abs_params(input, ABS_MT_POSITION_X,
0, tsdata->num_x * 64 - 1, 0, 0);
input_set_abs_params(input, ABS_MT_POSITION_Y,
0, tsdata->num_y * 64 - 1, 0, 0);

touchscreen_parse_properties(input, true, &tsdata->prop);

tsdata->max_support_points = 5;
error = input_mt_init_slots(input, tsdata->max_support_points,
INPUT_MT_DIRECT);
if (error) {
dev_err(&client->dev, "Unable to init MT slots.\n");
return error;
}

i2c_set_clientdata(client, tsdata);

device_property_read_u32(&client->dev, "poll-interval",
&poll_interval);
error = input_setup_polling(input, dfr0550_ts_poll);
if (error) {
dev_err(&client->dev,
"Unable to set up polling mode: %d\n", error);
return error;
}
input_set_drvdata(input, tsdata);
input_set_poll_interval(input, poll_interval);

error = input_register_device(input);
if (error)
return error;

dev_info(&client->dev, "Polling device at %dms\n", poll_interval);

return 0;
}

static const struct i2c_device_id dfr0550_ts_id[] = {
{ .name = "dfr,dfr0550" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, dfr0550_ts_id);

static const struct of_device_id dfr0550_of_match[] = {
{ .compatible = "dfr,dfr0550", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, dfr0550_of_match);

static struct i2c_driver dfr0550_ts_driver = {
.driver = {
.name = "dfr0550",
.of_match_table = dfr0550_of_match,
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
.id_table = dfr0550_ts_id,
.probe = dfr0550_ts_probe,
};

module_i2c_driver(dfr0550_ts_driver);

MODULE_AUTHOR("Tim Harvey <tharvey@gateworks.com>");
MODULE_DESCRIPTION("DFROBOT DFR0550 I2C Touchscreen Driver");
MODULE_LICENSE("GPL v2");

0 comments on commit 5bf0ffc

Please sign in to comment.