-
Notifications
You must be signed in to change notification settings - Fork 8k
drivers: input: add ft6146 driver #96330
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
ck-telecom
wants to merge
1
commit into
zephyrproject-rtos:main
Choose a base branch
from
ck-telecom:ft6146
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
# Copyright (c) 2025 Qingsong Gou <[email protected]> | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
menuconfig INPUT_FT6146 | ||
bool "FT6146 touch controller" | ||
default y | ||
depends on DT_HAS_FOCALTECH_FT6146_ENABLED | ||
select I2C | ||
select INPUT_TOUCH | ||
help | ||
Enable driver for FT6146 capacitive touch controller. | ||
|
||
if INPUT_FT6146 | ||
|
||
config INPUT_FT6416_PERIOD | ||
int "Sample period" | ||
depends on !INPUT_FT6146_INTERRUPT | ||
default 10 | ||
help | ||
Sample period in milliseconds when in polling mode. | ||
|
||
config INPUT_FT6146_INTERRUPT | ||
bool "FT6146 interrupt support" | ||
default y if $(dt_compat_any_has_prop,$(DT_COMPAT_FOCALTECH_FT56146),int-gpios) | ||
help | ||
Enable interrupt support for the FT6146 touch controller. | ||
|
||
endif # INPUT_FT6146 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,282 @@ | ||
/* | ||
* Copyright (c) 2025 Qingsong Gou <[email protected]> | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
#define DT_DRV_COMPAT focaltech_ft6146 | ||
|
||
#include <zephyr/kernel.h> | ||
#include <zephyr/drivers/i2c.h> | ||
#include <zephyr/drivers/gpio.h> | ||
#include <zephyr/input/input.h> | ||
#include <zephyr/input/input_touch.h> | ||
#include <zephyr/logging/log.h> | ||
|
||
LOG_MODULE_REGISTER(ft6146, CONFIG_INPUT_LOG_LEVEL); | ||
|
||
/* FT6146 register definitions */ | ||
#define FT6146_REG_DEVICE_MODE 0x00 | ||
#define FT6146_REG_GEST_ID 0x01 | ||
#define FT6146_REG_TD_STATUS 0x02 | ||
#define FT6146_REG_P1_XH 0x03 | ||
#define FT6146_REG_P1_XL 0x04 | ||
#define FT6146_REG_P1_YH 0x05 | ||
#define FT6146_REG_P1_YL 0x06 | ||
#define FT6146_REG_P1_WEIGHT 0x07 | ||
#define FT6146_REG_P1_MISC 0x08 | ||
#define FT6146_REG_P2_XH 0x09 | ||
#define FT6146_REG_P2_XL 0x0A | ||
#define FT6146_REG_P2_YH 0x0B | ||
#define FT6146_REG_P2_YL 0x0C | ||
#define FT6146_REG_P2_WEIGHT 0x0D | ||
#define FT6146_REG_P2_MISC 0x0E | ||
#define FT6146_REG_THRESHOLD 0x80 | ||
#define FT6146_REG_FILTER_COE 0x85 | ||
#define FT6146_REG_CTRL 0x86 | ||
#define FT6146_REG_TIMEENTERMONITOR 0x87 | ||
#define FT6146_REG_PERIODACTIVE 0x88 | ||
#define FT6146_REG_PERIODMONITOR 0x89 | ||
#define FT6146_REG_RADIAN_VALUE 0x91 | ||
#define FT6146_REG_OFFSET_LEFT_RIGHT 0x92 | ||
#define FT6146_REG_OFFSET_UP_DOWN 0x93 | ||
#define FT6146_REG_DIST_LEFT_RIGHT 0x94 | ||
#define FT6146_REG_DIST_UP_DOWN 0x95 | ||
#define FT6146_REG_ZOOM_DIS_SQR 0x96 | ||
#define FT6146_REG_RADIAN_THRESHOLD 0x97 | ||
#define FT6146_REG_SMALL_OBJECT_THRESHOLD 0x98 | ||
|
||
/* Device mode values */ | ||
#define FT6146_DEVICE_MODE_NORMAL 0x00 | ||
#define FT6146_DEVICE_MODE_TEST 0x04 | ||
#define FT6146_DEVICE_MODE_SYSTEM 0x01 | ||
|
||
/* Gesture IDs */ | ||
#define FT6146_GESTURE_NO_GESTURE 0x00 | ||
#define FT6146_GESTURE_MOVE_UP 0x10 | ||
#define FT6146_GESTURE_MOVE_RIGHT 0x14 | ||
#define FT6146_GESTURE_MOVE_DOWN 0x18 | ||
#define FT6146_GESTURE_MOVE_LEFT 0x1C | ||
#define FT6146_GESTURE_ZOOM_IN 0x48 | ||
#define FT6146_GESTURE_ZOOM_OUT 0x49 | ||
|
||
/* Reset timing */ | ||
#define FT6146_RESET_DELAY_MS 10 | ||
#define FT6146_POST_RESET_DELAY_MS 100 | ||
|
||
#define MAX_POINT_NUM 2 | ||
|
||
#define FT6146_READ_ID_H (0xA3) | ||
#define FT6146_READ_ID_L (0x9F) | ||
|
||
#define FT6146_CHIP_ID (0x5533) | ||
|
||
#define CTP_DOWN (0U) | ||
#define CTP_UP (1U) | ||
#define CTP_MOVE (2U) | ||
#define CTP_RESERVE (3U) | ||
|
||
struct ft6146_data { | ||
const struct device *dev; | ||
ck-telecom marked this conversation as resolved.
Show resolved
Hide resolved
|
||
struct gpio_callback int_cb; | ||
struct k_work work; | ||
struct k_timer poll_timer; | ||
}; | ||
|
||
struct ft6146_config { | ||
struct input_touchscreen_common_config common; | ||
struct i2c_dt_spec i2c; | ||
struct gpio_dt_spec int_gpio; | ||
struct gpio_dt_spec reset_gpio; | ||
}; | ||
|
||
static void ft6146_process_touch(const struct device *dev, uint8_t status) | ||
{ | ||
const struct ft6146_config *config = dev->config; | ||
uint8_t point_data[14]; | ||
uint8_t touch_num; | ||
uint8_t event_flag; | ||
uint16_t x, y; | ||
int id; | ||
int ret; | ||
|
||
/* Read touch data */ | ||
ret = i2c_burst_read_dt(&config->i2c, FT6146_REG_GEST_ID, point_data, sizeof(point_data)); | ||
if (ret < 0) { | ||
LOG_ERR("Failed to read touch data: %d", ret); | ||
return; | ||
} | ||
|
||
touch_num = point_data[1] & 0x0f; | ||
LOG_DBG("touch_num: %d", touch_num); | ||
|
||
if (touch_num > MAX_POINT_NUM) { | ||
return; | ||
} | ||
|
||
for (int i = 0; i < touch_num; i++) { | ||
event_flag = (point_data[2 + i * 6] >> 6) & 0x03; | ||
id = point_data[4 + i * 6] >> 4; | ||
x = ((point_data[2 + i * 6] & 0x0F) << 8) + point_data[3 + i * 6]; | ||
y = ((point_data[4 + i * 6] & 0x0F) << 8) + point_data[5 + i * 6]; | ||
|
||
LOG_DBG("event_flag:%d, id:%d, x:%d, y:%d", event_flag, id, x, y); | ||
if ((event_flag == 0U) || (event_flag == 2U)) { | ||
input_touchscreen_report_pos(dev, x, y, K_FOREVER); | ||
input_report_key(dev, INPUT_BTN_TOUCH, 1, true, K_FOREVER); | ||
} else if (event_flag == 1U) { | ||
input_report_key(dev, INPUT_BTN_TOUCH, 0, true, K_FOREVER); | ||
} | ||
} | ||
} | ||
|
||
static void ft6146_work_handler(struct k_work *work) | ||
{ | ||
struct ft6146_data *data = CONTAINER_OF(work, struct ft6146_data, work); | ||
const struct device *dev = data->dev; | ||
|
||
ft6146_process_touch(dev, 0); | ||
} | ||
|
||
#ifndef CONFIG_INPUT_FT6146_INTERRUPT | ||
static void ft6146_poll_timer_handler(struct k_timer *timer) | ||
{ | ||
struct ft6146_data *data = CONTAINER_OF(timer, struct ft6146_data, poll_timer); | ||
|
||
k_work_submit(&data->work); | ||
} | ||
#else | ||
static void ft6146_isr_handler(const struct device *dev, struct gpio_callback *cb, uint32_t pins) | ||
{ | ||
struct ft6146_data *data = CONTAINER_OF(cb, struct ft6146_data, int_cb); | ||
|
||
k_work_submit(&data->work); | ||
} | ||
#endif | ||
|
||
static int ft6146_reset(const struct device *dev) | ||
{ | ||
const struct ft6146_config *config = dev->config; | ||
int ret; | ||
|
||
if (!config->reset_gpio.port) { | ||
return 0; | ||
} | ||
|
||
if (!device_is_ready(config->reset_gpio.port)) { | ||
LOG_ERR("Reset GPIO not ready"); | ||
return -ENODEV; | ||
} | ||
|
||
ret = gpio_pin_configure_dt(&config->reset_gpio, GPIO_OUTPUT_INACTIVE); | ||
if (ret < 0) { | ||
LOG_ERR("Failed to configure reset GPIO: %d", ret); | ||
return ret; | ||
} | ||
|
||
/* Assert reset */ | ||
ret = gpio_pin_set_dt(&config->reset_gpio, 1); | ||
if (ret < 0) { | ||
LOG_ERR("Failed to assert reset: %d", ret); | ||
return ret; | ||
} | ||
|
||
k_msleep(FT6146_RESET_DELAY_MS); | ||
|
||
/* De-assert reset */ | ||
ret = gpio_pin_set_dt(&config->reset_gpio, 0); | ||
if (ret < 0) { | ||
LOG_ERR("Failed to de-assert reset: %d", ret); | ||
return ret; | ||
} | ||
|
||
k_msleep(FT6146_POST_RESET_DELAY_MS); | ||
|
||
return ret; | ||
} | ||
|
||
static int ft6146_init(const struct device *dev) | ||
{ | ||
const struct ft6146_config *config = dev->config; | ||
struct ft6146_data *data = dev->data; | ||
int ret; | ||
uint8_t tmp; | ||
uint16_t chip_id; | ||
|
||
if (!i2c_is_ready_dt(&config->i2c)) { | ||
LOG_ERR("I2C bus not ready"); | ||
return -ENODEV; | ||
} | ||
|
||
data->dev = dev; | ||
|
||
/* Perform reset sequence */ | ||
ret = ft6146_reset(dev); | ||
if (ret < 0) { | ||
return ret; | ||
} | ||
|
||
ret = i2c_reg_read_byte_dt(&config->i2c, FT6146_READ_ID_H, &tmp); | ||
if (ret < 0) { | ||
LOG_ERR("Failed to read ID_H: %d", ret); | ||
return ret; | ||
} | ||
chip_id |= (tmp << 8); | ||
|
||
ret = i2c_reg_read_byte_dt(&config->i2c, FT6146_READ_ID_L, &tmp); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it possible to have this in one operation? |
||
if (ret < 0) { | ||
LOG_ERR("Failed to read ID_L: %d", ret); | ||
return ret; | ||
} | ||
chip_id |= tmp; | ||
|
||
if (chip_id != FT6146_CHIP_ID) { | ||
LOG_ERR("unknown chip id: 0x%2x", chip_id); | ||
return -ENODEV; | ||
} | ||
|
||
#ifdef CONFIG_INPUT_FT6146_INTERRUPT | ||
ret = gpio_pin_configure_dt(&config->int_gpio, GPIO_INPUT); | ||
if (ret < 0) { | ||
LOG_ERR("Failed to configure interrupt GPIO: %d", ret); | ||
return ret; | ||
} | ||
|
||
ret = gpio_pin_interrupt_configure_dt(&config->int_gpio, GPIO_INT_EDGE_TO_ACTIVE); | ||
if (ret < 0) { | ||
LOG_ERR("Failed to configure interrupt: %d", ret); | ||
return ret; | ||
} | ||
|
||
gpio_init_callback(&data->int_cb, ft6146_isr_handler, BIT(config->int_gpio.pin)); | ||
ret = gpio_add_callback(config->int_gpio.port, &data->int_cb); | ||
if (ret < 0) { | ||
LOG_ERR("Failed to add callback: %d", ret); | ||
return ret; | ||
} | ||
#else | ||
/* Initialize polling timer */ | ||
k_timer_init(&data->poll_timer, ft6146_poll_timer_handler, NULL); | ||
k_timer_start(&data->poll_timer, K_MSEC(CONFIG_INPUT_FT6416_PERIOD), | ||
K_MSEC(CONFIG_INPUT_FT6416_PERIOD)); | ||
#endif | ||
|
||
/* Initialize work queue */ | ||
k_work_init(&data->work, ft6146_work_handler); | ||
|
||
return ret; | ||
} | ||
|
||
#define FT6146_INIT(n) \ | ||
static struct ft6146_data ft6146_data_##n; \ | ||
\ | ||
static const struct ft6146_config ft6146_config_##n = { \ | ||
.common = INPUT_TOUCH_DT_INST_COMMON_CONFIG_INIT(n), \ | ||
.i2c = I2C_DT_SPEC_INST_GET(n), \ | ||
.reset_gpio = GPIO_DT_SPEC_INST_GET_OR(n, reset_gpios, {0}), \ | ||
COND_CODE_1(CONFIG_INPUT_FT6146_INTERRUPT, \ | ||
(.int_gpio = GPIO_DT_SPEC_INST_GET(n, int_gpios),), ()) }; \ | ||
\ | ||
DEVICE_DT_INST_DEFINE(n, ft6146_init, NULL, &ft6146_data_##n, &ft6146_config_##n, \ | ||
POST_KERNEL, CONFIG_INPUT_INIT_PRIORITY, NULL); | ||
|
||
DT_INST_FOREACH_STATUS_OKAY(FT6146_INIT) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
# Copyright (c) 2025 Qingsong Gou <[email protected]> | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
description: FocalTech FT6146 capacitive touch controller | ||
|
||
compatible: "focaltech,ft6146" | ||
|
||
include: [i2c-device.yaml, touchscreen-common.yaml] | ||
|
||
properties: | ||
int-gpios: | ||
type: phandle-array | ||
description: Interrupt GPIO specification, active low | ||
|
||
reset-gpios: | ||
type: phandle-array | ||
description: Reset GPIO specification, active low |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.