Commit 3bdd21c6 authored by Sebastian Reichel's avatar Sebastian Reichel Committed by Dmitry Torokhov

Input: exc3000 - add EXC80H60 and EXC80H84 support

This adds support for EXC80H60 and EXCH84 controllers, which
use a different event type id and have two extra bits for the
resolution (so the maximum is 16K instead of 4K).
Signed-off-by: default avatarSebastian Reichel <sebastian.reichel@collabora.com>
Acked-by: default avatarRob Herring <robh@kernel.org>
Link: https://lore.kernel.org/r/20200805160520.456570-3-sebastian.reichel@collabora.comSigned-off-by: default avatarDmitry Torokhov <dmitry.torokhov@gmail.com>
parent 1053653f
...@@ -14,7 +14,10 @@ allOf: ...@@ -14,7 +14,10 @@ allOf:
properties: properties:
compatible: compatible:
const: eeti,exc3000 enum:
- eeti,exc3000
- eeti,exc80h60
- eeti,exc80h84
reg: reg:
const: 0x2a const: 0x2a
interrupts: interrupts:
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/sizes.h>
#include <linux/timer.h> #include <linux/timer.h>
#include <asm/unaligned.h> #include <asm/unaligned.h>
...@@ -23,11 +24,43 @@ ...@@ -23,11 +24,43 @@
#define EXC3000_SLOTS_PER_FRAME 5 #define EXC3000_SLOTS_PER_FRAME 5
#define EXC3000_LEN_FRAME 66 #define EXC3000_LEN_FRAME 66
#define EXC3000_LEN_POINT 10 #define EXC3000_LEN_POINT 10
#define EXC3000_MT_EVENT 6
#define EXC3000_MT1_EVENT 0x06
#define EXC3000_MT2_EVENT 0x18
#define EXC3000_TIMEOUT_MS 100 #define EXC3000_TIMEOUT_MS 100
static const struct i2c_device_id exc3000_id[];
struct eeti_dev_info {
const char *name;
int max_xy;
};
enum eeti_dev_id {
EETI_EXC3000,
EETI_EXC80H60,
EETI_EXC80H84,
};
static struct eeti_dev_info exc3000_info[] = {
[EETI_EXC3000] = {
.name = "EETI EXC3000 Touch Screen",
.max_xy = SZ_4K - 1,
},
[EETI_EXC80H60] = {
.name = "EETI EXC80H60 Touch Screen",
.max_xy = SZ_16K - 1,
},
[EETI_EXC80H84] = {
.name = "EETI EXC80H84 Touch Screen",
.max_xy = SZ_16K - 1,
},
};
struct exc3000_data { struct exc3000_data {
struct i2c_client *client; struct i2c_client *client;
const struct eeti_dev_info *info;
struct input_dev *input; struct input_dev *input;
struct touchscreen_properties prop; struct touchscreen_properties prop;
struct timer_list timer; struct timer_list timer;
...@@ -58,10 +91,15 @@ static void exc3000_timer(struct timer_list *t) ...@@ -58,10 +91,15 @@ static void exc3000_timer(struct timer_list *t)
input_sync(data->input); input_sync(data->input);
} }
static int exc3000_read_frame(struct i2c_client *client, u8 *buf) static int exc3000_read_frame(struct exc3000_data *data, u8 *buf)
{ {
struct i2c_client *client = data->client;
u8 expected_event = EXC3000_MT1_EVENT;
int ret; int ret;
if (data->info->max_xy == SZ_16K - 1)
expected_event = EXC3000_MT2_EVENT;
ret = i2c_master_send(client, "'", 2); ret = i2c_master_send(client, "'", 2);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -76,19 +114,21 @@ static int exc3000_read_frame(struct i2c_client *client, u8 *buf) ...@@ -76,19 +114,21 @@ static int exc3000_read_frame(struct i2c_client *client, u8 *buf)
if (ret != EXC3000_LEN_FRAME) if (ret != EXC3000_LEN_FRAME)
return -EIO; return -EIO;
if (get_unaligned_le16(buf) != EXC3000_LEN_FRAME || if (get_unaligned_le16(buf) != EXC3000_LEN_FRAME)
buf[2] != EXC3000_MT_EVENT) return -EINVAL;
if (buf[2] != expected_event)
return -EINVAL; return -EINVAL;
return 0; return 0;
} }
static int exc3000_read_data(struct i2c_client *client, static int exc3000_read_data(struct exc3000_data *data,
u8 *buf, int *n_slots) u8 *buf, int *n_slots)
{ {
int error; int error;
error = exc3000_read_frame(client, buf); error = exc3000_read_frame(data, buf);
if (error) if (error)
return error; return error;
...@@ -98,7 +138,7 @@ static int exc3000_read_data(struct i2c_client *client, ...@@ -98,7 +138,7 @@ static int exc3000_read_data(struct i2c_client *client,
if (*n_slots > EXC3000_SLOTS_PER_FRAME) { if (*n_slots > EXC3000_SLOTS_PER_FRAME) {
/* Read 2nd frame to get the rest of the contacts. */ /* Read 2nd frame to get the rest of the contacts. */
error = exc3000_read_frame(client, buf + EXC3000_LEN_FRAME); error = exc3000_read_frame(data, buf + EXC3000_LEN_FRAME);
if (error) if (error)
return error; return error;
...@@ -118,7 +158,7 @@ static irqreturn_t exc3000_interrupt(int irq, void *dev_id) ...@@ -118,7 +158,7 @@ static irqreturn_t exc3000_interrupt(int irq, void *dev_id)
int slots, total_slots; int slots, total_slots;
int error; int error;
error = exc3000_read_data(data->client, buf, &total_slots); error = exc3000_read_data(data, buf, &total_slots);
if (error) { if (error) {
/* Schedule a timer to release "stuck" contacts */ /* Schedule a timer to release "stuck" contacts */
mod_timer(&data->timer, mod_timer(&data->timer,
...@@ -149,13 +189,19 @@ static int exc3000_probe(struct i2c_client *client) ...@@ -149,13 +189,19 @@ static int exc3000_probe(struct i2c_client *client)
{ {
struct exc3000_data *data; struct exc3000_data *data;
struct input_dev *input; struct input_dev *input;
int error; int error, max_xy;
data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
if (!data) if (!data)
return -ENOMEM; return -ENOMEM;
data->client = client; data->client = client;
data->info = device_get_match_data(&client->dev);
if (!data->info) {
enum eeti_dev_id eeti_dev_id =
i2c_match_id(exc3000_id, client)->driver_data;
data->info = &exc3000_info[eeti_dev_id];
}
timer_setup(&data->timer, exc3000_timer, 0); timer_setup(&data->timer, exc3000_timer, 0);
input = devm_input_allocate_device(&client->dev); input = devm_input_allocate_device(&client->dev);
...@@ -164,11 +210,13 @@ static int exc3000_probe(struct i2c_client *client) ...@@ -164,11 +210,13 @@ static int exc3000_probe(struct i2c_client *client)
data->input = input; data->input = input;
input->name = "EETI EXC3000 Touch Screen"; input->name = data->info->name;
input->id.bustype = BUS_I2C; input->id.bustype = BUS_I2C;
input_set_abs_params(input, ABS_MT_POSITION_X, 0, 4095, 0, 0); max_xy = data->info->max_xy;
input_set_abs_params(input, ABS_MT_POSITION_Y, 0, 4095, 0, 0); input_set_abs_params(input, ABS_MT_POSITION_X, 0, max_xy, 0, 0);
input_set_abs_params(input, ABS_MT_POSITION_Y, 0, max_xy, 0, 0);
touchscreen_parse_properties(input, true, &data->prop); touchscreen_parse_properties(input, true, &data->prop);
error = input_mt_init_slots(input, EXC3000_NUM_SLOTS, error = input_mt_init_slots(input, EXC3000_NUM_SLOTS,
...@@ -190,14 +238,18 @@ static int exc3000_probe(struct i2c_client *client) ...@@ -190,14 +238,18 @@ static int exc3000_probe(struct i2c_client *client)
} }
static const struct i2c_device_id exc3000_id[] = { static const struct i2c_device_id exc3000_id[] = {
{ "exc3000", 0 }, { "exc3000", EETI_EXC3000 },
{ "exc80h60", EETI_EXC80H60 },
{ "exc80h84", EETI_EXC80H84 },
{ } { }
}; };
MODULE_DEVICE_TABLE(i2c, exc3000_id); MODULE_DEVICE_TABLE(i2c, exc3000_id);
#ifdef CONFIG_OF #ifdef CONFIG_OF
static const struct of_device_id exc3000_of_match[] = { static const struct of_device_id exc3000_of_match[] = {
{ .compatible = "eeti,exc3000" }, { .compatible = "eeti,exc3000", .data = &exc3000_info[EETI_EXC3000] },
{ .compatible = "eeti,exc80h60", .data = &exc3000_info[EETI_EXC80H60] },
{ .compatible = "eeti,exc80h84", .data = &exc3000_info[EETI_EXC80H84] },
{ } { }
}; };
MODULE_DEVICE_TABLE(of, exc3000_of_match); MODULE_DEVICE_TABLE(of, exc3000_of_match);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment