From: Luka Panio <lukapanio@gmail.com>
To: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Rob Herring <robh@kernel.org>,
Krzysztof Kozlowski <krzk+dt@kernel.org>,
Conor Dooley <conor+dt@kernel.org>,
Henrik Rydberg <rydberg@bitmath.org>,
Douglas Anderson <dianders@chromium.org>,
linux-input@vger.kernel.org, devicetree@vger.kernel.org,
dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org,
Luka Panio <lukapanio@gmail.com>
Subject: [PATCH 1/7] Input: edt-ft5x06 - split the bus independent core from the I2C driver
Date: Sat, 17 Oct 2026 16:07:49 +0200 [thread overview]
Message-ID: <20261017140827.290666-2-lukapanio@gmail.com> (raw)
In-Reply-To: <20261017140827.290666-1-lukapanio@gmail.com>
Some FocalTech controllers handled by this driver, such as the FT8722
TDDI, are connected over SPI. Prepare for an SPI transport by moving
everything that depends on the i2c_client into a small I2C probe.
The only visible change for the I2C devices is that the interrupt is now
requested with the device name (e.g. "1-0038") instead of the I2C client
name, as the core no longer has access to the latter.
Signed-off-by: Luka Panio <lukapanio@gmail.com>
---
drivers/input/touchscreen/edt-ft5x06.c | 276 +++++++++++++------------
drivers/input/touchscreen/edt-ft5x06.h | 30 +++
2 files changed, 179 insertions(+), 127 deletions(-)
create mode 100644 drivers/input/touchscreen/edt-ft5x06.h
diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c
index e69d64c3db2d..543cc62088c7 100644
--- a/drivers/input/touchscreen/edt-ft5x06.c
+++ b/drivers/input/touchscreen/edt-ft5x06.c
@@ -34,6 +34,8 @@
#include <linux/unaligned.h>
+#include "edt-ft5x06.h"
+
#define WORK_REGISTER_THRESHOLD 0x00
#define WORK_REGISTER_REPORT_RATE 0x08
#define WORK_REGISTER_GAIN 0x30
@@ -106,7 +108,9 @@ struct edt_reg_addr {
};
struct edt_ft5x06_ts_data {
- struct i2c_client *client;
+ struct device *dev;
+ int irq;
+ struct dentry *debugfs;
struct input_dev *input;
struct touchscreen_properties prop;
u16 num_x;
@@ -148,15 +152,6 @@ struct edt_ft5x06_ts_data {
unsigned int header_errors;
};
-struct edt_i2c_chip_data {
- int max_support_points;
-};
-
-static const struct regmap_config edt_ft5x06_i2c_regmap_config = {
- .reg_bits = 8,
- .val_bits = 8,
-};
-
static bool edt_ft5x06_ts_check_crc(struct edt_ft5x06_ts_data *tsdata,
u8 *buf, int buflen)
{
@@ -168,7 +163,7 @@ static bool edt_ft5x06_ts_check_crc(struct edt_ft5x06_ts_data *tsdata,
if (crc != buf[buflen - 1]) {
tsdata->crc_errors++;
- dev_err_ratelimited(&tsdata->client->dev,
+ dev_err_ratelimited(tsdata->dev,
"crc error: 0x%02x expected, got 0x%02x\n",
crc, buf[buflen - 1]);
return false;
@@ -182,7 +177,7 @@ static int edt_M06_i2c_read(void *context, const void *reg_buf, size_t reg_size,
{
struct device *dev = context;
struct i2c_client *i2c = to_i2c_client(dev);
- struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(i2c);
+ struct edt_ft5x06_ts_data *tsdata = dev_get_drvdata(dev);
struct i2c_msg xfer[2];
bool reg_read = false;
u8 addr;
@@ -258,7 +253,7 @@ static int edt_M06_i2c_write(void *context, const void *data, size_t count)
{
struct device *dev = context;
struct i2c_client *i2c = to_i2c_client(dev);
- struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(i2c);
+ struct edt_ft5x06_ts_data *tsdata = dev_get_drvdata(dev);
u8 addr, val;
u8 wbuf[4];
struct i2c_msg xfer;
@@ -298,7 +293,7 @@ static const struct regmap_config edt_M06_i2c_regmap_config = {
static irqreturn_t edt_ft5x06_ts_isr(int irq, void *dev_id)
{
struct edt_ft5x06_ts_data *tsdata = dev_id;
- struct device *dev = &tsdata->client->dev;
+ struct device *dev = tsdata->dev;
u8 rdbuf[63];
int i, type, x, y, id;
int error;
@@ -376,8 +371,7 @@ static ssize_t edt_ft5x06_setting_show(struct device *dev,
struct device_attribute *dattr,
char *buf)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
+ struct edt_ft5x06_ts_data *tsdata = dev_get_drvdata(dev);
struct edt_ft5x06_attribute *attr =
container_of(dattr, struct edt_ft5x06_attribute, dattr);
u8 *field = (u8 *)tsdata + attr->field_offset;
@@ -412,7 +406,7 @@ static ssize_t edt_ft5x06_setting_show(struct device *dev,
if (addr != NO_REGISTER) {
error = regmap_read(tsdata->regmap, addr, &val);
if (error) {
- dev_err(&tsdata->client->dev,
+ dev_err(tsdata->dev,
"Failed to fetch attribute %s, error %d\n",
dattr->attr.name, error);
return error;
@@ -422,7 +416,7 @@ static ssize_t edt_ft5x06_setting_show(struct device *dev,
}
if (val != *field) {
- dev_warn(&tsdata->client->dev,
+ dev_warn(tsdata->dev,
"%s: read (%d) and stored value (%d) differ\n",
dattr->attr.name, val, *field);
*field = val;
@@ -435,8 +429,7 @@ static ssize_t edt_ft5x06_setting_store(struct device *dev,
struct device_attribute *dattr,
const char *buf, size_t count)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
+ struct edt_ft5x06_ts_data *tsdata = dev_get_drvdata(dev);
struct edt_ft5x06_attribute *attr =
container_of(dattr, struct edt_ft5x06_attribute, dattr);
u8 *field = (u8 *)tsdata + attr->field_offset;
@@ -478,7 +471,7 @@ static ssize_t edt_ft5x06_setting_store(struct device *dev,
if (addr != NO_REGISTER) {
error = regmap_write(tsdata->regmap, addr, val);
if (error) {
- dev_err(&tsdata->client->dev,
+ dev_err(tsdata->dev,
"Failed to update attribute %s, error: %d\n",
dattr->attr.name, error);
return error;
@@ -511,8 +504,7 @@ static EDT_ATTR(report_rate, S_IWUSR | S_IRUGO, WORK_REGISTER_REPORT_RATE,
static ssize_t model_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
+ struct edt_ft5x06_ts_data *tsdata = dev_get_drvdata(dev);
return sysfs_emit(buf, "%s\n", tsdata->name);
}
@@ -522,8 +514,7 @@ static DEVICE_ATTR_RO(model);
static ssize_t fw_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
+ struct edt_ft5x06_ts_data *tsdata = dev_get_drvdata(dev);
return sysfs_emit(buf, "%s\n", tsdata->fw_version);
}
@@ -534,8 +525,7 @@ static DEVICE_ATTR_RO(fw_version);
static ssize_t header_errors_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
+ struct edt_ft5x06_ts_data *tsdata = dev_get_drvdata(dev);
return sysfs_emit(buf, "%d\n", tsdata->header_errors);
}
@@ -546,8 +536,7 @@ static DEVICE_ATTR_RO(header_errors);
static ssize_t crc_errors_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
+ struct edt_ft5x06_ts_data *tsdata = dev_get_drvdata(dev);
return sysfs_emit(buf, "%d\n", tsdata->crc_errors);
}
@@ -567,7 +556,16 @@ static struct attribute *edt_ft5x06_attrs[] = {
&dev_attr_crc_errors.attr,
NULL
};
-ATTRIBUTE_GROUPS(edt_ft5x06);
+
+static const struct attribute_group edt_ft5x06_group = {
+ .attrs = edt_ft5x06_attrs,
+};
+
+const struct attribute_group *edt_ft5x06_groups[] = {
+ &edt_ft5x06_group,
+ NULL
+};
+EXPORT_SYMBOL_NS_GPL(edt_ft5x06_groups, "EDT_FT5X06");
static void edt_ft5x06_restore_reg_parameters(struct edt_ft5x06_ts_data *tsdata)
{
@@ -590,18 +588,17 @@ static void edt_ft5x06_restore_reg_parameters(struct edt_ft5x06_ts_data *tsdata)
#ifdef CONFIG_DEBUG_FS
static int edt_ft5x06_factory_mode(struct edt_ft5x06_ts_data *tsdata)
{
- struct i2c_client *client = tsdata->client;
int retries = EDT_SWITCH_MODE_RETRIES;
unsigned int val;
int error;
if (tsdata->version != EDT_M06) {
- dev_err(&client->dev,
+ dev_err(tsdata->dev,
"No factory mode support for non-M06 devices\n");
return -EINVAL;
}
- disable_irq(client->irq);
+ disable_irq(tsdata->irq);
if (!tsdata->raw_buffer) {
tsdata->raw_bufsize = tsdata->num_x * tsdata->num_y *
@@ -616,7 +613,7 @@ static int edt_ft5x06_factory_mode(struct edt_ft5x06_ts_data *tsdata)
/* mode register is 0x3c when in the work mode */
error = regmap_write(tsdata->regmap, WORK_REGISTER_OPMODE, 0x03);
if (error) {
- dev_err(&client->dev,
+ dev_err(tsdata->dev,
"failed to switch to factory mode, error %d\n", error);
goto err_out;
}
@@ -632,7 +629,7 @@ static int edt_ft5x06_factory_mode(struct edt_ft5x06_ts_data *tsdata)
} while (--retries > 0);
if (retries == 0) {
- dev_err(&client->dev, "not in factory mode after %dms.\n",
+ dev_err(tsdata->dev, "not in factory mode after %dms.\n",
EDT_SWITCH_MODE_RETRIES * EDT_SWITCH_MODE_DELAY);
error = -EIO;
goto err_out;
@@ -644,14 +641,13 @@ static int edt_ft5x06_factory_mode(struct edt_ft5x06_ts_data *tsdata)
kfree(tsdata->raw_buffer);
tsdata->raw_buffer = NULL;
tsdata->factory_mode = false;
- enable_irq(client->irq);
+ enable_irq(tsdata->irq);
return error;
}
static int edt_ft5x06_work_mode(struct edt_ft5x06_ts_data *tsdata)
{
- struct i2c_client *client = tsdata->client;
int retries = EDT_SWITCH_MODE_RETRIES;
unsigned int val;
int error;
@@ -659,7 +655,7 @@ static int edt_ft5x06_work_mode(struct edt_ft5x06_ts_data *tsdata)
/* mode register is 0x01 when in the factory mode */
error = regmap_write(tsdata->regmap, FACTORY_REGISTER_OPMODE, 0x1);
if (error) {
- dev_err(&client->dev,
+ dev_err(tsdata->dev,
"failed to switch to work mode, error: %d\n", error);
return error;
}
@@ -675,7 +671,7 @@ static int edt_ft5x06_work_mode(struct edt_ft5x06_ts_data *tsdata)
} while (--retries > 0);
if (retries == 0) {
- dev_err(&client->dev, "not in work mode after %dms.\n",
+ dev_err(tsdata->dev, "not in work mode after %dms.\n",
EDT_SWITCH_MODE_RETRIES * EDT_SWITCH_MODE_DELAY);
tsdata->factory_mode = true;
return -EIO;
@@ -685,7 +681,7 @@ static int edt_ft5x06_work_mode(struct edt_ft5x06_ts_data *tsdata)
tsdata->raw_buffer = NULL;
edt_ft5x06_restore_reg_parameters(tsdata);
- enable_irq(client->irq);
+ enable_irq(tsdata->irq);
return 0;
}
@@ -723,7 +719,6 @@ static ssize_t edt_ft5x06_debugfs_raw_data_read(struct file *file,
loff_t *off)
{
struct edt_ft5x06_ts_data *tsdata = file->private_data;
- struct i2c_client *client = tsdata->client;
int retries = EDT_RAW_DATA_RETRIES;
unsigned int val;
int i, error;
@@ -741,7 +736,7 @@ static ssize_t edt_ft5x06_debugfs_raw_data_read(struct file *file,
error = regmap_write(tsdata->regmap, 0x08, 0x01);
if (error) {
- dev_err(&client->dev,
+ dev_err(tsdata->dev,
"failed to write 0x08 register, error %d\n", error);
return error;
}
@@ -750,7 +745,7 @@ static ssize_t edt_ft5x06_debugfs_raw_data_read(struct file *file,
usleep_range(EDT_RAW_DATA_DELAY, EDT_RAW_DATA_DELAY + 100);
error = regmap_read(tsdata->regmap, 0x08, &val);
if (error) {
- dev_err(&client->dev,
+ dev_err(tsdata->dev,
"failed to read 0x08 register, error %d\n",
error);
return error;
@@ -761,7 +756,7 @@ static ssize_t edt_ft5x06_debugfs_raw_data_read(struct file *file,
} while (--retries > 0);
if (retries == 0) {
- dev_err(&client->dev,
+ dev_err(tsdata->dev,
"timed out waiting for register to settle\n");
return -ETIMEDOUT;
}
@@ -793,7 +788,10 @@ static const struct file_operations debugfs_raw_data_fops = {
static void edt_ft5x06_ts_prepare_debugfs(struct edt_ft5x06_ts_data *tsdata)
{
- struct dentry *debug_dir = tsdata->client->debugfs;
+ struct dentry *debug_dir = tsdata->debugfs;
+
+ if (!debug_dir)
+ return;
debugfs_create_u16("num_x", S_IRUSR, debug_dir, &tsdata->num_x);
debugfs_create_u16("num_y", S_IRUSR, debug_dir, &tsdata->num_y);
@@ -829,9 +827,9 @@ static void edt_ft5x06_ts_teardown_debugfs(struct edt_ft5x06_ts_data *tsdata)
#endif /* CONFIG_DEBUGFS */
-static int edt_ft5x06_ts_identify(struct i2c_client *client,
- struct edt_ft5x06_ts_data *tsdata)
+static int edt_ft5x06_ts_identify(struct edt_ft5x06_ts_data *tsdata)
{
+ struct i2c_client *client;
u8 rdbuf[EDT_NAME_LEN];
char *p;
int error;
@@ -854,6 +852,11 @@ static int edt_ft5x06_ts_identify(struct i2c_client *client,
if (!strncasecmp(rdbuf + 1, "EP0", 3)) {
tsdata->version = EDT_M06;
+ /* M06 uses its own register protocol, only over I2C */
+ client = i2c_verify_client(tsdata->dev);
+ if (!client)
+ return -ENODEV;
+
/* remove last '$' end marker */
rdbuf[EDT_NAME_LEN - 1] = '\0';
if (rdbuf[EDT_NAME_LEN - 2] == '$')
@@ -870,7 +873,7 @@ static int edt_ft5x06_ts_identify(struct i2c_client *client,
tsdata->regmap = regmap_init_i2c(client,
&edt_M06_i2c_regmap_config);
if (IS_ERR(tsdata->regmap)) {
- dev_err(&client->dev, "regmap allocation failed\n");
+ dev_err(tsdata->dev, "regmap allocation failed\n");
return PTR_ERR(tsdata->regmap);
}
} else if (!strncasecmp(rdbuf, "EP0", 3)) {
@@ -1113,10 +1116,10 @@ static void edt_ft5x06_disable_regulators(void *arg)
regulator_disable(data->iovcc);
}
-static int edt_ft5x06_ts_probe(struct i2c_client *client)
+int edt_ft5x06_probe(struct device *dev, struct regmap *regmap, int irq,
+ const struct edt_ft5x06_chip_data *chip_data, u16 bustype,
+ struct dentry *debugfs)
{
- const struct i2c_device_id *id = i2c_client_get_device_id(client);
- const struct edt_i2c_chip_data *chip_data;
struct edt_ft5x06_ts_data *tsdata;
unsigned int val;
struct input_dev *input;
@@ -1124,57 +1127,50 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client)
int error;
u32 report_rate;
- dev_dbg(&client->dev, "probing for EDT FT5x06 I2C\n");
-
- tsdata = devm_kzalloc(&client->dev, sizeof(*tsdata), GFP_KERNEL);
+ tsdata = devm_kzalloc(dev, sizeof(*tsdata), GFP_KERNEL);
if (!tsdata) {
- dev_err(&client->dev, "failed to allocate driver data.\n");
+ regmap_exit(regmap);
return -ENOMEM;
}
- tsdata->regmap = regmap_init_i2c(client, &edt_ft5x06_i2c_regmap_config);
- if (IS_ERR(tsdata->regmap)) {
- dev_err(&client->dev, "regmap allocation failed\n");
- return PTR_ERR(tsdata->regmap);
- }
+ tsdata->dev = dev;
+ tsdata->irq = irq;
+ tsdata->debugfs = debugfs;
+ tsdata->regmap = regmap;
/*
- * We are not using devm_regmap_init_i2c() and instead install a
- * custom action because we may replace regmap with M06-specific one
- * and we need to make sure that it will not be released too early.
+ * The bus driver hands over a regmap that is not device managed
+ * because we may replace it with the M06-specific one and we need to
+ * make sure that it will not be released too early.
*/
- error = devm_add_action_or_reset(&client->dev, edt_ft5x06_exit_regmap,
- tsdata);
+ error = devm_add_action_or_reset(dev, edt_ft5x06_exit_regmap, tsdata);
if (error)
return error;
- chip_data = device_get_match_data(&client->dev);
- if (!chip_data)
- chip_data = (const struct edt_i2c_chip_data *)id->driver_data;
if (!chip_data || !chip_data->max_support_points) {
- dev_err(&client->dev, "invalid or missing chip data\n");
+ dev_err(dev, "invalid or missing chip data\n");
return -EINVAL;
}
tsdata->max_support_points = chip_data->max_support_points;
- tsdata->vcc = devm_regulator_get(&client->dev, "vcc");
+ tsdata->vcc = devm_regulator_get(dev, "vcc");
if (IS_ERR(tsdata->vcc))
- return dev_err_probe(&client->dev, PTR_ERR(tsdata->vcc),
+ return dev_err_probe(dev, PTR_ERR(tsdata->vcc),
"failed to request regulator\n");
- tsdata->iovcc = devm_regulator_get(&client->dev, "iovcc");
+ tsdata->iovcc = devm_regulator_get(dev, "iovcc");
if (IS_ERR(tsdata->iovcc)) {
error = PTR_ERR(tsdata->iovcc);
if (error != -EPROBE_DEFER)
- dev_err(&client->dev,
+ dev_err(dev,
"failed to request iovcc regulator: %d\n", error);
return error;
}
error = regulator_enable(tsdata->iovcc);
if (error < 0) {
- dev_err(&client->dev, "failed to enable iovcc: %d\n", error);
+ dev_err(dev, "failed to enable iovcc: %d\n", error);
return error;
}
@@ -1183,32 +1179,30 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client)
error = regulator_enable(tsdata->vcc);
if (error < 0) {
- dev_err(&client->dev, "failed to enable vcc: %d\n", error);
+ dev_err(dev, "failed to enable vcc: %d\n", error);
regulator_disable(tsdata->iovcc);
return error;
}
- error = devm_add_action_or_reset(&client->dev,
- edt_ft5x06_disable_regulators,
+ error = devm_add_action_or_reset(dev, edt_ft5x06_disable_regulators,
tsdata);
if (error)
return error;
- tsdata->reset_gpio = devm_gpiod_get_optional(&client->dev,
- "reset", GPIOD_OUT_HIGH);
+ tsdata->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_HIGH);
if (IS_ERR(tsdata->reset_gpio)) {
error = PTR_ERR(tsdata->reset_gpio);
- dev_err(&client->dev,
- "Failed to request GPIO reset pin, error %d\n", error);
+ dev_err(dev, "Failed to request GPIO reset pin, error %d\n",
+ error);
return error;
}
- tsdata->wake_gpio = devm_gpiod_get_optional(&client->dev,
- "wake", GPIOD_OUT_LOW);
+ tsdata->wake_gpio = devm_gpiod_get_optional(dev, "wake", GPIOD_OUT_LOW);
if (IS_ERR(tsdata->wake_gpio)) {
error = PTR_ERR(tsdata->wake_gpio);
- dev_err(&client->dev,
- "Failed to request GPIO wake pin, error %d\n", error);
+ dev_err(dev, "Failed to request GPIO wake pin, error %d\n",
+ error);
return error;
}
@@ -1237,21 +1231,20 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client)
msleep(300);
}
- input = devm_input_allocate_device(&client->dev);
+ input = devm_input_allocate_device(dev);
if (!input) {
- dev_err(&client->dev, "failed to allocate input device.\n");
+ dev_err(dev, "failed to allocate input device.\n");
return -ENOMEM;
}
mutex_init(&tsdata->mutex);
- tsdata->client = client;
tsdata->input = input;
tsdata->factory_mode = false;
- i2c_set_clientdata(client, tsdata);
+ dev_set_drvdata(dev, tsdata);
- error = edt_ft5x06_ts_identify(client, tsdata);
+ error = edt_ft5x06_ts_identify(tsdata);
if (error) {
- dev_err(&client->dev, "touchscreen probe failed\n");
+ dev_err(dev, "touchscreen probe failed\n");
return error;
}
@@ -1263,19 +1256,18 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client)
edt_ft5x06_ts_set_tdata_parameters(tsdata);
edt_ft5x06_ts_set_regs(tsdata);
- edt_ft5x06_ts_get_defaults(&client->dev, tsdata);
+ edt_ft5x06_ts_get_defaults(dev, tsdata);
edt_ft5x06_ts_get_parameters(tsdata);
if (tsdata->reg_addr.reg_report_rate != NO_REGISTER &&
- !device_property_read_u32(&client->dev,
- "report-rate-hz", &report_rate)) {
+ !device_property_read_u32(dev, "report-rate-hz", &report_rate)) {
if (tsdata->version == EDT_M06)
tsdata->report_rate = clamp_val(report_rate, 30, 140);
else
tsdata->report_rate = clamp_val(report_rate, 1, 255);
if (report_rate != tsdata->report_rate)
- dev_warn(&client->dev,
+ dev_warn(dev,
"report-rate %dHz is unsupported, use %dHz\n",
report_rate, tsdata->report_rate);
@@ -1286,12 +1278,11 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client)
tsdata->report_rate);
}
- dev_dbg(&client->dev,
- "Model \"%s\", Rev. \"%s\", %dx%d sensors\n",
+ dev_dbg(dev, "Model \"%s\", Rev. \"%s\", %dx%d sensors\n",
tsdata->name, tsdata->fw_version, tsdata->num_x, tsdata->num_y);
input->name = tsdata->name;
- input->id.bustype = BUS_I2C;
+ input->id.bustype = bustype;
input_set_abs_params(input, ABS_MT_POSITION_X,
0, tsdata->num_x * 64 - 1, 0, 0);
@@ -1303,20 +1294,19 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client)
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");
+ dev_err(dev, "Unable to init MT slots.\n");
return error;
}
- irq_flags = irq_get_trigger_type(client->irq);
+ irq_flags = irq_get_trigger_type(irq);
if (irq_flags == IRQF_TRIGGER_NONE)
irq_flags = IRQF_TRIGGER_FALLING;
irq_flags |= IRQF_ONESHOT;
- error = devm_request_threaded_irq(&client->dev, client->irq,
- NULL, edt_ft5x06_ts_isr, irq_flags,
- client->name, tsdata);
+ error = devm_request_threaded_irq(dev, irq, NULL, edt_ft5x06_ts_isr,
+ irq_flags, dev_name(dev), tsdata);
if (error) {
- dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
+ dev_err(dev, "Unable to request touchscreen IRQ.\n");
return error;
}
@@ -1326,26 +1316,27 @@ static int edt_ft5x06_ts_probe(struct i2c_client *client)
edt_ft5x06_ts_prepare_debugfs(tsdata);
- dev_dbg(&client->dev,
+ dev_dbg(dev,
"EDT FT5x06 initialized: IRQ %d, WAKE pin %d, Reset pin %d.\n",
- client->irq,
+ irq,
tsdata->wake_gpio ? desc_to_gpio(tsdata->wake_gpio) : -1,
tsdata->reset_gpio ? desc_to_gpio(tsdata->reset_gpio) : -1);
return 0;
}
+EXPORT_SYMBOL_NS_GPL(edt_ft5x06_probe, "EDT_FT5X06");
-static void edt_ft5x06_ts_remove(struct i2c_client *client)
+void edt_ft5x06_remove(struct device *dev)
{
- struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
+ struct edt_ft5x06_ts_data *tsdata = dev_get_drvdata(dev);
edt_ft5x06_ts_teardown_debugfs(tsdata);
}
+EXPORT_SYMBOL_NS_GPL(edt_ft5x06_remove, "EDT_FT5X06");
static int edt_ft5x06_ts_suspend(struct device *dev)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
+ struct edt_ft5x06_ts_data *tsdata = dev_get_drvdata(dev);
struct gpio_desc *reset_gpio = tsdata->reset_gpio;
int ret;
@@ -1370,7 +1361,7 @@ static int edt_ft5x06_ts_suspend(struct device *dev)
* settings. Disable the irq to avoid adjusting each host till the
* device is back in a full functional state.
*/
- disable_irq(tsdata->client->irq);
+ disable_irq(tsdata->irq);
gpiod_set_value_cansleep(reset_gpio, 1);
usleep_range(1000, 2000);
@@ -1387,8 +1378,7 @@ static int edt_ft5x06_ts_suspend(struct device *dev)
static int edt_ft5x06_ts_resume(struct device *dev)
{
- struct i2c_client *client = to_i2c_client(dev);
- struct edt_ft5x06_ts_data *tsdata = i2c_get_clientdata(client);
+ struct edt_ft5x06_ts_data *tsdata = dev_get_drvdata(dev);
int ret = 0;
if (device_may_wakeup(dev))
@@ -1432,7 +1422,7 @@ static int edt_ft5x06_ts_resume(struct device *dev)
msleep(300);
edt_ft5x06_restore_reg_parameters(tsdata);
- enable_irq(tsdata->client->irq);
+ enable_irq(tsdata->irq);
if (tsdata->factory_mode)
ret = edt_ft5x06_factory_mode(tsdata);
@@ -1447,38 +1437,38 @@ static int edt_ft5x06_ts_resume(struct device *dev)
return ret;
}
-static DEFINE_SIMPLE_DEV_PM_OPS(edt_ft5x06_ts_pm_ops,
- edt_ft5x06_ts_suspend, edt_ft5x06_ts_resume);
+EXPORT_NS_GPL_SIMPLE_DEV_PM_OPS(edt_ft5x06_pm_ops, edt_ft5x06_ts_suspend,
+ edt_ft5x06_ts_resume, EDT_FT5X06);
-static const struct edt_i2c_chip_data edt_ft5x06_data = {
+static const struct edt_ft5x06_chip_data edt_ft5x06_data = {
.max_support_points = 5,
};
-static const struct edt_i2c_chip_data edt_ft3518_data = {
+static const struct edt_ft5x06_chip_data edt_ft3518_data = {
.max_support_points = 10,
};
-static const struct edt_i2c_chip_data edt_ft5452_data = {
+static const struct edt_ft5x06_chip_data edt_ft5452_data = {
.max_support_points = 5,
};
-static const struct edt_i2c_chip_data edt_ft5506_data = {
+static const struct edt_ft5x06_chip_data edt_ft5506_data = {
.max_support_points = 10,
};
-static const struct edt_i2c_chip_data edt_ft6236_data = {
+static const struct edt_ft5x06_chip_data edt_ft6236_data = {
.max_support_points = 2,
};
-static const struct edt_i2c_chip_data edt_ft8201_data = {
+static const struct edt_ft5x06_chip_data edt_ft8201_data = {
.max_support_points = 10,
};
-static const struct edt_i2c_chip_data edt_ft8716_data = {
+static const struct edt_ft5x06_chip_data edt_ft8716_data = {
.max_support_points = 10,
};
-static const struct edt_i2c_chip_data edt_ft8719_data = {
+static const struct edt_ft5x06_chip_data edt_ft8719_data = {
.max_support_points = 10,
};
@@ -1515,17 +1505,49 @@ static const struct of_device_id edt_ft5x06_of_match[] = {
};
MODULE_DEVICE_TABLE(of, edt_ft5x06_of_match);
+static const struct regmap_config edt_ft5x06_i2c_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static int edt_ft5x06_i2c_probe(struct i2c_client *client)
+{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
+ const struct edt_ft5x06_chip_data *chip_data;
+ struct regmap *regmap;
+
+ dev_dbg(&client->dev, "probing for EDT FT5x06 I2C\n");
+
+ chip_data = device_get_match_data(&client->dev);
+ if (!chip_data && id)
+ chip_data = (const struct edt_ft5x06_chip_data *)id->driver_data;
+
+ regmap = regmap_init_i2c(client, &edt_ft5x06_i2c_regmap_config);
+ if (IS_ERR(regmap)) {
+ dev_err(&client->dev, "regmap allocation failed\n");
+ return PTR_ERR(regmap);
+ }
+
+ return edt_ft5x06_probe(&client->dev, regmap, client->irq, chip_data,
+ BUS_I2C, client->debugfs);
+}
+
+static void edt_ft5x06_i2c_remove(struct i2c_client *client)
+{
+ edt_ft5x06_remove(&client->dev);
+}
+
static struct i2c_driver edt_ft5x06_ts_driver = {
.driver = {
.name = "edt_ft5x06",
.dev_groups = edt_ft5x06_groups,
.of_match_table = edt_ft5x06_of_match,
- .pm = pm_sleep_ptr(&edt_ft5x06_ts_pm_ops),
+ .pm = pm_sleep_ptr(&edt_ft5x06_pm_ops),
.probe_type = PROBE_PREFER_ASYNCHRONOUS,
},
.id_table = edt_ft5x06_ts_id,
- .probe = edt_ft5x06_ts_probe,
- .remove = edt_ft5x06_ts_remove,
+ .probe = edt_ft5x06_i2c_probe,
+ .remove = edt_ft5x06_i2c_remove,
};
module_i2c_driver(edt_ft5x06_ts_driver);
diff --git a/drivers/input/touchscreen/edt-ft5x06.h b/drivers/input/touchscreen/edt-ft5x06.h
new file mode 100644
index 000000000000..deadc756cd74
--- /dev/null
+++ b/drivers/input/touchscreen/edt-ft5x06.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Bus independent core of the EDT FT5x06 touchscreen driver
+ *
+ * Copyright (C) 2026 Luka Panio, <lukapanio@gmail.com>
+ */
+#ifndef _EDT_FT5X06_H
+#define _EDT_FT5X06_H
+
+#include <linux/types.h>
+
+struct attribute_group;
+struct dentry;
+struct dev_pm_ops;
+struct device;
+struct regmap;
+
+struct edt_ft5x06_chip_data {
+ int max_support_points;
+};
+
+int edt_ft5x06_probe(struct device *dev, struct regmap *regmap, int irq,
+ const struct edt_ft5x06_chip_data *chip_data, u16 bustype,
+ struct dentry *debugfs);
+void edt_ft5x06_remove(struct device *dev);
+
+extern const struct dev_pm_ops edt_ft5x06_pm_ops;
+extern const struct attribute_group *edt_ft5x06_groups[];
+
+#endif
--
2.55.0
next prev parent reply other threads:[~2026-10-17 14:08 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-17 14:07 [PATCH 0/7] Input: edt-ft5x06 - add SPI transport and FocalTech FT8722 support Luka Panio
2026-10-17 14:07 ` Luka Panio [this message]
2026-10-17 14:07 ` [PATCH 2/7] Input: edt-ft5x06 - move the controller bring-up into a helper Luka Panio
2026-10-17 14:16 ` sashiko-bot
2026-10-17 14:07 ` [PATCH 3/7] Input: edt-ft5x06 - add an optional controller boot hook Luka Panio
2026-10-17 14:07 ` [PATCH 4/7] Input: edt-ft5x06 - allow a chip specific touch data register Luka Panio
2026-10-17 14:07 ` [PATCH 5/7] dt-bindings: input: touchscreen: edt-ft5x06: Add FocalTech FT8722 Luka Panio
2026-10-17 14:07 ` [PATCH 6/7] Input: edt-ft5x06 - add SPI transport and FT8722 support Luka Panio
2026-10-17 14:07 ` [PATCH 7/7] Input: edt-ft5x06 - support following a DRM panel Luka Panio
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261017140827.290666-2-lukapanio@gmail.com \
--to=lukapanio@gmail.com \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=dianders@chromium.org \
--cc=dmitry.torokhov@gmail.com \
--cc=dri-devel@lists.freedesktop.org \
--cc=krzk+dt@kernel.org \
--cc=linux-input@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=robh@kernel.org \
--cc=rydberg@bitmath.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.