drivers: kscan: add support for cst816s touch panel driver

Add touch panel driver support for pinetime

Signed-off-by: Qingsong Gou <gouqs@hotmail.com>
This commit is contained in:
Qingsong Gou 2022-01-17 21:13:25 +08:00 committed by Maureen Helm
commit b0eb3207a9
9 changed files with 391 additions and 1 deletions

View file

@ -21,4 +21,7 @@ config ST7789V
endif # DISPLAY endif # DISPLAY
config I2C
default y if KSCAN
endif # BOARD_PINETIME_DEVKIT0 endif # BOARD_PINETIME_DEVKIT0

View file

@ -32,6 +32,7 @@
led2 = &blled2; /* backlight high */ led2 = &blled2; /* backlight high */
led3 = &statusled; /* status led, may be not populated */ led3 = &statusled; /* status led, may be not populated */
sw0 = &key_in; /* key in */ sw0 = &key_in; /* key in */
kscan0 = &cst816s;
}; };
leds { leds {
@ -108,7 +109,7 @@
/* Hynitron CST816S Capacitive Touch Controller (400KHz) */ /* Hynitron CST816S Capacitive Touch Controller (400KHz) */
cst816s: cst816s@15 { cst816s: cst816s@15 {
compatible = "hynitron-cst816s"; compatible = "hynitron,cst816s";
reg = <0x15>; reg = <0x15>;
label = "CST816S"; label = "CST816S";
irq-gpios = <&gpio0 28 GPIO_ACTIVE_LOW>; irq-gpios = <&gpio0 28 GPIO_ACTIVE_LOW>;

View file

@ -8,3 +8,5 @@ toolchain:
- xtools - xtools
ram: 64 ram: 64
flash: 512 flash: 512
supported:
- kscan:touch

View file

@ -7,5 +7,6 @@ zephyr_library_sources_ifdef(CONFIG_KSCAN_ITE_IT8XXX2 kscan_ite_it8xxx2.c)
zephyr_library_sources_ifdef(CONFIG_KSCAN_XEC kscan_mchp_xec.c) zephyr_library_sources_ifdef(CONFIG_KSCAN_XEC kscan_mchp_xec.c)
zephyr_library_sources_ifdef(CONFIG_KSCAN_SDL kscan_sdl.c) zephyr_library_sources_ifdef(CONFIG_KSCAN_SDL kscan_sdl.c)
zephyr_library_sources_ifdef(CONFIG_KSCAN_HT16K33 kscan_ht16k33.c) zephyr_library_sources_ifdef(CONFIG_KSCAN_HT16K33 kscan_ht16k33.c)
zephyr_library_sources_ifdef(CONFIG_KSCAN_CST816S kscan_cst816s.c)
zephyr_library_sources_ifdef(CONFIG_USERSPACE kscan_handlers.c) zephyr_library_sources_ifdef(CONFIG_USERSPACE kscan_handlers.c)

View file

@ -15,6 +15,7 @@ source "drivers/kscan/Kconfig.it8xxx2"
source "drivers/kscan/Kconfig.xec" source "drivers/kscan/Kconfig.xec"
source "drivers/kscan/Kconfig.sdl" source "drivers/kscan/Kconfig.sdl"
source "drivers/kscan/Kconfig.ht16k33" source "drivers/kscan/Kconfig.ht16k33"
source "drivers/kscan/Kconfig.cst816s"
module = KSCAN module = KSCAN
module-str = kscan module-str = kscan

View file

@ -0,0 +1,29 @@
# Copyright (c) 2020 Qingsong Gou <gouqs@hotmail.com>
# SPDX-License-Identifier: Apache-2.0
DT_COMPAT_HYNITRON_CST816S := hynitron,cst816s
menuconfig KSCAN_CST816S
bool "CST816S capacitive touch panel driver"
default $(dt_compat_enabled,$(DT_COMPAT_HYNITRON_CST816S))
depends on I2C
help
Enable driver for hynitron cst816s touch panel.
if KSCAN_CST816S
config KSCAN_CST816S_PERIOD
int "Sample period"
depends on !KSCAN_CST816S_INTERRUPT
default 20
help
Sample period in milliseconds when in polling mode.
config KSCAN_CST816S_INTERRUPT
bool "Enable interrupt support"
default y
depends on GPIO
help
Enable interrupt support (requires GPIO).
endif # KSCAN_CST816S

View file

@ -0,0 +1,327 @@
/*
* Copyright (c) 2021 Qingsong Gou <gouqs@hotmail.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT hynitron_cst816s
#include <sys/byteorder.h>
#include <drivers/kscan.h>
#include <drivers/i2c.h>
#include <drivers/gpio.h>
#include <logging/log.h>
LOG_MODULE_REGISTER(cst816s, CONFIG_KSCAN_LOG_LEVEL);
#define CST816S_CHIP_ID 0xB4
#define CST816S_REG_DATA 0x00
#define CST816S_REG_GESTURE_ID 0x01
#define CST816S_REG_FINGER_NUM 0x02
#define CST816S_REG_XPOS_H 0x03
#define CST816S_REG_XPOS_L 0x04
#define CST816S_REG_YPOS_H 0x05
#define CST816S_REG_YPOS_L 0x06
#define CST816S_REG_BPC0H 0xB0
#define CST816S_REG_BPC0L 0xB1
#define CST816S_REG_BPC1H 0xB2
#define CST816S_REG_BPC1L 0xB3
#define CST816S_REG_POWER_MODE 0xA5
#define CST816S_REG_CHIP_ID 0xA7
#define CST816S_REG_PROJ_ID 0xA8
#define CST816S_REG_FW_VERSION 0xA9
#define CST816S_REG_MOTION_MASK 0xEC
#define CST816S_REG_IRQ_PULSE_WIDTH 0xED
#define CST816S_REG_NOR_SCAN_PER 0xEE
#define CST816S_REG_MOTION_S1_ANGLE 0xEF
#define CST816S_REG_LP_SCAN_RAW1H 0xF0
#define CST816S_REG_LP_SCAN_RAW1L 0xF1
#define CST816S_REG_LP_SCAN_RAW2H 0xF2
#define CST816S_REG_LP_SCAN_RAW2L 0xF3
#define CST816S_REG_LP_AUTO_WAKEUP_TIME 0xF4
#define CST816S_REG_LP_SCAN_TH 0xF5
#define CST816S_REG_LP_SCAN_WIN 0xF6
#define CST816S_REG_LP_SCAN_FREQ 0xF7
#define CST816S_REG_LP_SCAN_I_DAC 0xF8
#define CST816S_REG_AUTOSLEEP_TIME 0xF9
#define CST816S_REG_IRQ_CTL 0xFA
#define CST816S_REG_DEBOUNCE_TIME 0xFB
#define CST816S_REG_LONG_PRESS_TIME 0xFC
#define CST816S_REG_IOCTL 0xFD
#define CST816S_REG_DIS_AUTO_SLEEP 0xFE
#define CST816S_MOTION_EN_CON_LR BIT(2)
#define CST816S_MOTION_EN_CON_UR BIT(1)
#define CST816S_MOTION_EN_DCLICK BIT(0)
#define CST816S_IRQ_EN_TEST BIT(7)
#define CST816S_IRQ_EN_TOUCH BIT(6)
#define CST816S_IRQ_EN_CHANGE BIT(5)
#define CST816S_IRQ_EN_MOTION BIT(4)
#define CST816S_IRQ_ONCE_WLP BIT(0)
#define CST816S_IOCTL_SOFT_RTS BIT(2)
#define CST816S_IOCTL_IIC_OD BIT(1)
#define CST816S_IOCTL_EN_1V8 BIT(0)
#define CST816S_POWER_MODE_SLEEP (0x03)
#define CST816S_POWER_MODE_EXPERIMENTAL (0x05)
#define CST816S_EVENT_BITS_POS (0x06)
#define CST816S_RESET_DELAY (5) /* in ms */
#define CST816S_WAIT_DELAY (50) /* in ms */
#define EVENT_PRESS_DOWN 0x00U
#define EVENT_LIFT_UP 0x01U
#define EVENT_CONTACT 0x02U
#define EVENT_NONE 0x03U
/** cst816s configuration (DT). */
struct cst816s_config {
struct i2c_dt_spec i2c;
const struct gpio_dt_spec rst_gpio;
#ifdef CONFIG_KSCAN_CST816S_INTERRUPT
const struct gpio_dt_spec int_gpio;
#endif
};
/** cst816s data. */
struct cst816s_data {
/** Device pointer. */
const struct device *dev;
/** KSCAN Callback. */
kscan_callback_t callback;
/** Work queue (for deferred read). */
struct k_work work;
#ifdef CONFIG_KSCAN_CST816S_INTERRUPT
/** Interrupt GPIO callback. */
struct gpio_callback int_gpio_cb;
#else
/** Timer (polling mode). */
struct k_timer timer;
#endif
};
static int cst816s_process(const struct device *dev)
{
const struct cst816s_config *cfg = dev->config;
struct cst816s_data *data = dev->data;
int r;
uint8_t event;
uint16_t row, col;
bool pressed;
uint16_t x;
uint16_t y;
r = i2c_burst_read_dt(&cfg->i2c, CST816S_REG_XPOS_H, (uint8_t *)&x, sizeof(x));
if (r < 0) {
LOG_ERR("Could not read x data");
return r;
}
r = i2c_burst_read_dt(&cfg->i2c, CST816S_REG_YPOS_H, (uint8_t *)&y, sizeof(y));
if (r < 0) {
LOG_ERR("Could not read y data");
return r;
}
col = sys_be16_to_cpu(x) & 0x0fff;
row = sys_be16_to_cpu(y) & 0x0fff;
event = (x & 0xff) >> CST816S_EVENT_BITS_POS;
pressed = (event == EVENT_PRESS_DOWN) || (event == EVENT_CONTACT);
LOG_DBG("event: %d, row: %d, col: %d", event, row, col);
if (data->callback) {
data->callback(dev, row, col, pressed);
}
return r;
}
static void cst816s_work_handler(struct k_work *work)
{
struct cst816s_data *data = CONTAINER_OF(work, struct cst816s_data, work);
cst816s_process(data->dev);
}
#ifdef CONFIG_KSCAN_CST816S_INTERRUPT
static void cst816s_isr_handler(const struct device *dev,
struct gpio_callback *cb, uint32_t pins)
{
struct cst816s_data *data = CONTAINER_OF(cb, struct cst816s_data, int_gpio_cb);
k_work_submit(&data->work);
}
#else
static void cst816s_timer_handler(struct k_timer *timer)
{
struct cst816s_data *data = CONTAINER_OF(timer, struct cst816s_data, timer);
k_work_submit(&data->work);
}
#endif
static int cst816s_configure(const struct device *dev,
kscan_callback_t callback)
{
struct cst816s_data *data = dev->data;
if (!callback) {
LOG_ERR("Invalid callback (NULL)");
return -EINVAL;
}
data->callback = callback;
return 0;
}
static int cst816s_enable_callback(const struct device *dev)
{
struct cst816s_data *data = dev->data;
#ifdef CONFIG_KSCAN_CST816S_INTERRUPT
const struct cst816s_config *config = dev->config;
gpio_add_callback(config->int_gpio.port, &data->int_gpio_cb);
#else
k_timer_start(&data->timer, K_MSEC(CONFIG_KSCAN_CST816S_PERIOD),
K_MSEC(CONFIG_KSCAN_CST816S_PERIOD));
#endif
return 0;
}
static int cst816s_disable_callback(const struct device *dev)
{
struct cst816s_data *data = dev->data;
#ifdef CONFIG_KSCAN_CST816S_INTERRUPT
const struct cst816s_config *config = dev->config;
gpio_remove_callback(config->int_gpio.port, &data->int_gpio_cb);
#else
k_timer_stop(&data->timer);
#endif
return 0;
}
static void cst816s_chip_reset(const struct device *dev)
{
const struct cst816s_config *config = dev->config;
int ret;
if (device_is_ready(config->rst_gpio.port)) {
ret = gpio_pin_configure_dt(&config->rst_gpio,
GPIO_OUTPUT_INACTIVE);
if (ret < 0) {
LOG_ERR("Could not configure reset GPIO pin");
return;
}
gpio_pin_set_dt(&config->rst_gpio, 1);
k_msleep(CST816S_RESET_DELAY);
gpio_pin_set_dt(&config->rst_gpio, 0);
k_msleep(CST816S_WAIT_DELAY);
}
}
static int cst816s_chip_init(const struct device *dev)
{
const struct cst816s_config *cfg = dev->config;
int ret = 0;
uint8_t chip_id = 0;
cst816s_chip_reset(dev);
if (!device_is_ready(cfg->i2c.bus)) {
LOG_ERR("I2C bus %s not ready", cfg->i2c.bus->name);
return -ENODEV;
}
ret = i2c_reg_read_byte_dt(&cfg->i2c, CST816S_REG_CHIP_ID, &chip_id);
if (ret < 0) {
LOG_ERR("failed reading chip id");
return ret;
}
if (chip_id != CST816S_CHIP_ID) {
LOG_ERR("CST816S wrong chip id: returned 0x%x", chip_id);
return -ENODEV;
}
ret = i2c_reg_update_byte_dt(&cfg->i2c,
CST816S_REG_IRQ_CTL,
CST816S_IRQ_EN_TOUCH | CST816S_IRQ_EN_CHANGE,
CST816S_IRQ_EN_TOUCH | CST816S_IRQ_EN_CHANGE);
if (ret < 0) {
LOG_ERR("Could not enable irq");
return ret;
}
return ret;
}
static int cst816s_init(const struct device *dev)
{
struct cst816s_data *data = dev->data;
data->dev = dev;
k_work_init(&data->work, cst816s_work_handler);
#ifdef CONFIG_KSCAN_CST816S_INTERRUPT
const struct cst816s_config *config = dev->config;
int ret;
if (!device_is_ready(config->int_gpio.port)) {
LOG_ERR("GPIO port %s not ready", config->int_gpio.port->name);
return -ENODEV;
}
ret = gpio_pin_configure_dt(&config->int_gpio, GPIO_INPUT);
if (ret < 0) {
LOG_ERR("Could not configure interrupt GPIO pin");
return ret;
}
ret = gpio_pin_interrupt_configure_dt(&config->int_gpio,
GPIO_INT_EDGE_TO_ACTIVE);
if (ret < 0) {
LOG_ERR("Could not configure interrupt GPIO interrupt.");
return ret;
}
gpio_init_callback(&data->int_gpio_cb, cst816s_isr_handler,
BIT(config->int_gpio.pin));
#else
k_timer_init(&data->timer, cst816s_timer_handler, NULL);
#endif
return cst816s_chip_init(dev);
}
static const struct kscan_driver_api cst816s_driver_api = {
.config = cst816s_configure,
.enable_callback = cst816s_enable_callback,
.disable_callback = cst816s_disable_callback,
};
#define CST816S_DEFINE(index) \
static const struct cst816s_config cst816s_config_##index = { \
.i2c = I2C_DT_SPEC_INST_GET(index), \
COND_CODE_1(CONFIG_KSCAN_CST816S_INTERRUPT, \
(.int_gpio = GPIO_DT_SPEC_INST_GET(index, irq_gpios),),\
()) \
.rst_gpio = GPIO_DT_SPEC_INST_GET_OR(index, rst_gpios, {}), \
}; \
static struct cst816s_data cst816s_data_##index; \
DEVICE_DT_INST_DEFINE(index, cst816s_init, NULL, \
&cst816s_data_##index, &cst816s_config_##index, \
POST_KERNEL, CONFIG_KSCAN_INIT_PRIORITY, \
&cst816s_driver_api);
DT_INST_FOREACH_STATUS_OKAY(CST816S_DEFINE)

View file

@ -0,0 +1,25 @@
# Copyright (c) 2021 Qingsong Gou <gouqs@hotmail.com>
# SPDX-License-Identifier: Apache-2.0
description: Hynitron CST816S touchscreen sensor
compatible: "hynitron,cst816s"
include: i2c-device.yaml
properties:
irq-gpios:
type: phandle-array
required: false
description: |
The irq signal defaults to active low as produced by the
sensor. The property value should ensure the flags properly
describe the signal that is presented to the driver.
rst-gpios:
type: phandle-array
required: false
description: |
The reset signal defaults to active low to the
sensor. The property value should ensure the flags properly
describe the signal that is presented to the driver.

View file

@ -258,6 +258,7 @@ hugsun Shenzhen Hugsun Technology Co. Ltd.
hwacom HwaCom Systems Inc. hwacom HwaCom Systems Inc.
hycon Hycon Technology Corp. hycon Hycon Technology Corp.
hydis Hydis Technologies hydis Hydis Technologies
hynitron Hynitron
hyundai Hyundai Technology hyundai Hyundai Technology
i2se I2SE GmbH i2se I2SE GmbH
ibm International Business Machines (IBM) ibm International Business Machines (IBM)