drivers: kscan: generalize and improve FT5336 driver
The FT5336 driver has been improved and generalized so that multiple Focaltech touch controllers can be supported using the same single driver. According to specifications the following list of controller variants should be supported: FT5x06, FT5606, FT5x16, FT6x06, Ft6x36, FT5x06i, FT5336, FT3316, FT5436i, FT5336i and FT5x46. Tested using ER-TFTM028-4 display module (FT6X06). Signed-off-by: Gerard Marull-Paretas <gerard@teslabs.com>
This commit is contained in:
parent
1672e51d51
commit
ca9dce0d7d
@ -1,17 +1,27 @@
|
||||
# Copyright (c) 2020 NXP
|
||||
# Copyright (c) 2020 Teslabs Engineering S.L.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
menuconfig KSCAN_FT5336
|
||||
bool "FT5336 capacitive touch panel driver"
|
||||
bool "FT5XX6/FT6XX6 capacitive touch panel driver"
|
||||
depends on I2C && HAS_DTS_I2C
|
||||
help
|
||||
Enable driver for the FT5336 capacitive touch panel controller.
|
||||
Enable driver for multiple Focaltech capacitive touch panel
|
||||
controllers. This driver should support FT5x06, FT5606, FT5x16,
|
||||
FT6x06, Ft6x36, FT5x06i, FT5336, FT3316, FT5436i, FT5336i and FT5x46.
|
||||
|
||||
if KSCAN_FT5336
|
||||
|
||||
config KSCAN_FT5336_PERIOD
|
||||
int "Sample period (ms)"
|
||||
int "Sample period"
|
||||
depends on !KSCAN_FT5336_INTERRUPT
|
||||
default 10
|
||||
depends on KSCAN_FT5336
|
||||
help
|
||||
Sample period in milliseconds when in polling mode.
|
||||
|
||||
config KSCAN_FT5336_INTERRUPT
|
||||
bool "Enable interrupt"
|
||||
depends on KSCAN_FT5336
|
||||
help
|
||||
Enable interrupt support (requires GPIO).
|
||||
|
||||
endif # KSCAN_FT5336
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Copyright (c) 2020 NXP
|
||||
* Copyright (c) 2020 Mark Olsson <mark@markolsson.se>
|
||||
* Copyright (c) 2020 Teslabs Engineering S.L.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -10,106 +11,138 @@
|
||||
#include <drivers/kscan.h>
|
||||
#include <drivers/i2c.h>
|
||||
#include <drivers/gpio.h>
|
||||
#include <logging/log.h>
|
||||
|
||||
#include <logging/log.h>
|
||||
LOG_MODULE_REGISTER(ft5336, CONFIG_KSCAN_LOG_LEVEL);
|
||||
|
||||
#define FT5406_DATA_SIZE 0x20
|
||||
/* FT5336 used registers */
|
||||
#define REG_TD_STATUS 0x02U
|
||||
#define REG_P1_XH 0x03U
|
||||
|
||||
enum ft5336_event {
|
||||
FT5336_EVENT_DOWN = 0,
|
||||
FT5336_EVENT_UP = 1,
|
||||
FT5336_EVENT_CONTACT = 2,
|
||||
FT5336_EVENT_RESERVED = 3,
|
||||
/* REG_TD_STATUS: Touch points. */
|
||||
#define TOUCH_POINTS_POS 0U
|
||||
#define TOUCH_POINTS_MSK 0x0FU
|
||||
|
||||
/* REG_Pn_XH: Events. */
|
||||
#define EVENT_POS 6U
|
||||
#define EVENT_MSK 0x03U
|
||||
|
||||
#define EVENT_PRESS_DOWN 0x00U
|
||||
#define EVENT_LIFT_UP 0x01U
|
||||
#define EVENT_CONTACT 0x02U
|
||||
#define EVENT_NONE 0x03U
|
||||
|
||||
/* REG_Pn_XH: Position */
|
||||
#define POSITION_H_MSK 0x0FU
|
||||
|
||||
/** GPIO DT information. */
|
||||
struct gpio_dt_info {
|
||||
/** Port. */
|
||||
const char *port;
|
||||
/** Pin. */
|
||||
gpio_pin_t pin;
|
||||
/** Flags. */
|
||||
gpio_dt_flags_t flags;
|
||||
};
|
||||
|
||||
/** FT5336 configuration (DT). */
|
||||
struct ft5336_config {
|
||||
/** I2C controller device name. */
|
||||
char *i2c_name;
|
||||
/** I2C chip address. */
|
||||
uint8_t i2c_address;
|
||||
#ifdef CONFIG_KSCAN_FT5336_INTERRUPT
|
||||
char *int_gpio_controller;
|
||||
uint8_t int_gpio_pin;
|
||||
uint8_t int_gpio_flags;
|
||||
char *label;
|
||||
/** Interrupt GPIO information. */
|
||||
struct gpio_dt_info int_gpio;
|
||||
#endif
|
||||
};
|
||||
|
||||
/** FT5336 data. */
|
||||
struct ft5336_data {
|
||||
/** Device pointer. */
|
||||
struct device *dev;
|
||||
/** I2C controller device. */
|
||||
struct device *i2c;
|
||||
/** KSCAN Callback. */
|
||||
kscan_callback_t callback;
|
||||
/** Work queue (for deferred read). */
|
||||
struct k_work work;
|
||||
#ifdef CONFIG_KSCAN_FT5336_INTERRUPT
|
||||
/** Interrupt GPIO controller. */
|
||||
struct device *int_gpio;
|
||||
/** Interrupt GPIO callback. */
|
||||
struct gpio_callback int_gpio_cb;
|
||||
struct device *int_gpio_dev;
|
||||
#else
|
||||
/** Timer (polling mode). */
|
||||
struct k_timer timer;
|
||||
#endif
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
static int ft5336_read(struct device *dev)
|
||||
static int ft5336_process(struct device *dev)
|
||||
{
|
||||
const struct ft5336_config *config = dev->config;
|
||||
struct ft5336_data *data = dev->data;
|
||||
uint8_t buffer[FT5406_DATA_SIZE];
|
||||
|
||||
int r;
|
||||
uint8_t points;
|
||||
uint8_t coords[4U];
|
||||
uint8_t event;
|
||||
uint16_t row, column;
|
||||
uint16_t row, col;
|
||||
bool pressed;
|
||||
|
||||
if (i2c_burst_read(data->i2c, config->i2c_address, 1, buffer,
|
||||
sizeof(buffer))) {
|
||||
LOG_ERR("Could not read point");
|
||||
return -EIO;
|
||||
/* obtain number of touch points (NOTE: multi-touch ignored) */
|
||||
r = i2c_reg_read_byte(data->i2c, config->i2c_address, REG_TD_STATUS,
|
||||
&points);
|
||||
if (r < 0) {
|
||||
return r;
|
||||
}
|
||||
|
||||
event = buffer[2] >> 6;
|
||||
pressed = (event == FT5336_EVENT_DOWN) ||
|
||||
(event == FT5336_EVENT_CONTACT);
|
||||
points = (points >> TOUCH_POINTS_POS) & TOUCH_POINTS_MSK;
|
||||
if (points != 0U && points != 1U) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
row = ((buffer[2] & 0x0f) << 8) | buffer[3];
|
||||
column = ((buffer[4] & 0x0f) << 8) | buffer[5];
|
||||
/* obtain first point X, Y coordinates and event from:
|
||||
* REG_P1_XH, REG_P1_XL, REG_P1_YH, REG_P1_YL.
|
||||
*/
|
||||
r = i2c_burst_read(data->i2c, config->i2c_address, REG_P1_XH, coords,
|
||||
sizeof(coords));
|
||||
if (r < 0) {
|
||||
return r;
|
||||
}
|
||||
|
||||
LOG_DBG("row: %d, col: %d, pressed: %d", row, column, pressed);
|
||||
event = (coords[0] >> EVENT_POS) & EVENT_MSK;
|
||||
row = ((coords[0] & POSITION_H_MSK) << 8U) | coords[1];
|
||||
col = ((coords[2] & POSITION_H_MSK) << 8U) | coords[3];
|
||||
pressed = (event == EVENT_PRESS_DOWN) || (event == EVENT_CONTACT);
|
||||
|
||||
data->callback(dev, row, column, pressed);
|
||||
LOG_DBG("event: %d, row: %d, col: %d", event, row, col);
|
||||
|
||||
data->callback(dev, row, col, pressed);
|
||||
|
||||
#ifdef CONFIG_KSCAN_FT5336_INTERRUPT
|
||||
gpio_pin_interrupt_configure(data->int_gpio_dev, config->int_gpio_pin,
|
||||
GPIO_INT_EDGE_TO_ACTIVE);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_KSCAN_FT5336_INTERRUPT
|
||||
static void ft5336_timer_handler(struct k_timer *timer)
|
||||
{
|
||||
struct ft5336_data *data =
|
||||
CONTAINER_OF(timer, struct ft5336_data, timer);
|
||||
|
||||
k_work_submit(&data->work);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void ft5336_work_handler(struct k_work *work)
|
||||
{
|
||||
struct ft5336_data *data =
|
||||
CONTAINER_OF(work, struct ft5336_data, work);
|
||||
struct ft5336_data *data = CONTAINER_OF(work, struct ft5336_data, work);
|
||||
|
||||
ft5336_read(data->dev);
|
||||
ft5336_process(data->dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KSCAN_FT5336_INTERRUPT
|
||||
static void ft5336_isr_handler(struct device *dev, struct gpio_callback *cb,
|
||||
uint32_t pins)
|
||||
static void ft5336_isr_handler(struct device *dev, struct gpio_callback *cb, uint32_t pins)
|
||||
{
|
||||
const struct ft5336_config *config = dev->config;
|
||||
struct ft5336_data *drv_data =
|
||||
CONTAINER_OF(cb, struct ft5336_data, int_gpio_cb);
|
||||
struct ft5336_data *data = CONTAINER_OF(cb, struct ft5336_data, int_gpio_cb);
|
||||
|
||||
gpio_pin_interrupt_configure(dev, config->int_gpio_pin,
|
||||
GPIO_INT_DISABLE);
|
||||
k_work_submit(&data->work);
|
||||
}
|
||||
#else
|
||||
static void ft5336_timer_handler(struct k_timer *timer)
|
||||
{
|
||||
struct ft5336_data *data = CONTAINER_OF(timer, struct ft5336_data, timer);
|
||||
|
||||
k_work_submit(&drv_data->work);
|
||||
k_work_submit(&data->work);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -118,6 +151,7 @@ static int ft5336_configure(struct device *dev, kscan_callback_t callback)
|
||||
struct ft5336_data *data = dev->data;
|
||||
|
||||
if (!callback) {
|
||||
LOG_ERR("Invalid callback (NULL)");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@ -131,7 +165,7 @@ static int ft5336_enable_callback(struct device *dev)
|
||||
struct ft5336_data *data = dev->data;
|
||||
|
||||
#ifdef CONFIG_KSCAN_FT5336_INTERRUPT
|
||||
gpio_add_callback(data->int_gpio_dev, &data->int_gpio_cb);
|
||||
gpio_add_callback(data->int_gpio, &data->int_gpio_cb);
|
||||
#else
|
||||
k_timer_start(&data->timer, K_MSEC(CONFIG_KSCAN_FT5336_PERIOD),
|
||||
K_MSEC(CONFIG_KSCAN_FT5336_PERIOD));
|
||||
@ -145,7 +179,7 @@ static int ft5336_disable_callback(struct device *dev)
|
||||
struct ft5336_data *data = dev->data;
|
||||
|
||||
#ifdef CONFIG_KSCAN_FT5336_INTERRUPT
|
||||
gpio_remove_callback(data->int_gpio_dev, &data->int_gpio_cb);
|
||||
gpio_remove_callback(data->int_gpio, &data->int_gpio_cb);
|
||||
#else
|
||||
k_timer_stop(&data->timer);
|
||||
#endif
|
||||
@ -159,75 +193,81 @@ static int ft5336_init(struct device *dev)
|
||||
struct ft5336_data *data = dev->data;
|
||||
|
||||
data->i2c = device_get_binding(config->i2c_name);
|
||||
if (data->i2c == NULL) {
|
||||
LOG_ERR("Could not find I2C device");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (i2c_reg_write_byte(data->i2c, config->i2c_address, 0, 0)) {
|
||||
LOG_ERR("Could not enable");
|
||||
return -EINVAL;
|
||||
if (!data->i2c) {
|
||||
LOG_ERR("Could not find I2C controller");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
data->dev = dev;
|
||||
|
||||
k_work_init(&data->work, ft5336_work_handler);
|
||||
#ifndef CONFIG_KSCAN_FT5336_INTERRUPT
|
||||
k_timer_init(&data->timer, ft5336_timer_handler, NULL);
|
||||
#else
|
||||
int ret;
|
||||
|
||||
data->int_gpio_dev = device_get_binding(config->int_gpio_controller);
|
||||
if (data->int_gpio_dev == NULL) {
|
||||
LOG_ERR("Error: unknown device %s\n",
|
||||
config->int_gpio_controller);
|
||||
return -EINVAL;
|
||||
#ifdef CONFIG_KSCAN_FT5336_INTERRUPT
|
||||
int r;
|
||||
|
||||
data->int_gpio = device_get_binding(config->int_gpio.port);
|
||||
if (!data->int_gpio) {
|
||||
LOG_ERR("Could not find interrupt GPIO controller");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = gpio_pin_configure(data->int_gpio_dev, config->int_gpio_pin,
|
||||
config->int_gpio_flags | GPIO_INPUT);
|
||||
if (ret != 0) {
|
||||
LOG_ERR("Error %d: failed to configure pin %d '%s'\n",
|
||||
ret, config->int_gpio_pin, config->label);
|
||||
return -EINVAL;
|
||||
r = gpio_pin_configure(data->int_gpio, config->int_gpio.pin,
|
||||
config->int_gpio.flags | GPIO_INPUT);
|
||||
if (r < 0) {
|
||||
LOG_ERR("Could not configure interrupt GPIO pin");
|
||||
return r;
|
||||
}
|
||||
|
||||
ret = gpio_pin_interrupt_configure(data->int_gpio_dev,
|
||||
config->int_gpio_pin, GPIO_INT_EDGE_TO_ACTIVE);
|
||||
if (ret != 0) {
|
||||
LOG_ERR("Error %d: failed to configure pin interrupt %d '%s'\n",
|
||||
ret, config->int_gpio_pin, config->label);
|
||||
return -EINVAL;
|
||||
r = gpio_pin_interrupt_configure(data->int_gpio, config->int_gpio.pin,
|
||||
GPIO_INT_EDGE_TO_ACTIVE);
|
||||
if (r < 0) {
|
||||
LOG_ERR("Could not configure interrupt GPIO interrupt.");
|
||||
return r;
|
||||
}
|
||||
|
||||
gpio_init_callback(&data->int_gpio_cb, ft5336_isr_handler,
|
||||
BIT(config->int_gpio_pin));
|
||||
BIT(config->int_gpio.pin));
|
||||
#else
|
||||
k_timer_init(&data->timer, ft5336_timer_handler, NULL);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const struct kscan_driver_api ft5336_driver_api = {
|
||||
.config = ft5336_configure,
|
||||
.enable_callback = ft5336_enable_callback,
|
||||
.disable_callback = ft5336_disable_callback,
|
||||
};
|
||||
|
||||
static const struct ft5336_config ft5336_config = {
|
||||
.i2c_name = DT_INST_BUS_LABEL(0),
|
||||
.i2c_address = DT_INST_REG_ADDR(0),
|
||||
#define DT_INST_GPIO(index, gpio_pha) \
|
||||
{ \
|
||||
DT_INST_GPIO_LABEL(index, gpio_pha), \
|
||||
DT_INST_GPIO_PIN(index, gpio_pha), \
|
||||
DT_INST_GPIO_FLAGS(index, gpio_pha), \
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KSCAN_FT5336_INTERRUPT
|
||||
.int_gpio_controller = DT_INST_GPIO_LABEL(0, int_gpios),
|
||||
.int_gpio_pin = DT_INST_GPIO_PIN(0, int_gpios),
|
||||
.int_gpio_flags = DT_INST_GPIO_FLAGS(0, int_gpios),
|
||||
.label = DT_INST_GPIO_LABEL(0, int_gpios),
|
||||
#define FT5336_DEFINE_CONFIG(index) \
|
||||
static const struct ft5336_config ft5336_config_##index = { \
|
||||
.i2c_name = DT_INST_BUS_LABEL(index), \
|
||||
.i2c_address = DT_INST_REG_ADDR(index), \
|
||||
.int_gpio = DT_INST_GPIO(index, int_gpios) \
|
||||
}
|
||||
#else
|
||||
#define FT5336_DEFINE_CONFIG(index) \
|
||||
static const struct ft5336_config ft5336_config_##index = { \
|
||||
.i2c_name = DT_INST_BUS_LABEL(index), \
|
||||
.i2c_address = DT_INST_REG_ADDR(index) \
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct ft5336_data ft5336_data;
|
||||
#define FT5336_INIT(index) \
|
||||
FT5336_DEFINE_CONFIG(index); \
|
||||
static struct ft5336_data ft5336_data_##index; \
|
||||
DEVICE_AND_API_INIT(ft5336_##index, DT_INST_LABEL(index), ft5336_init, \
|
||||
&ft5336_data_##index, &ft5336_config_##index, \
|
||||
POST_KERNEL, CONFIG_KSCAN_INIT_PRIORITY, \
|
||||
&ft5336_driver_api);
|
||||
|
||||
DEVICE_AND_API_INIT(ft5336, DT_INST_LABEL(0), ft5336_init,
|
||||
&ft5336_data, &ft5336_config,
|
||||
POST_KERNEL, CONFIG_KSCAN_INIT_PRIORITY,
|
||||
&ft5336_driver_api);
|
||||
DT_INST_FOREACH_STATUS_OKAY(FT5336_INIT)
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# Copyright (c) 2020 NXP
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: FT5336 capacitive touch panel
|
||||
description: FT5XX6/FT6XX6 capacitive touch panels
|
||||
|
||||
compatible: "focaltech,ft5336"
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user