208 lines
7.2 KiB
C++
208 lines
7.2 KiB
C++
// SPDX-License-Identifier: Apache-2.0
|
|
#include <drivers/st77922_touch.h>
|
|
#include <st77922_module.h>
|
|
|
|
#include <tactility/check.h>
|
|
#include <tactility/device.h>
|
|
#include <tactility/driver.h>
|
|
#include <tactility/drivers/gpio_controller.h>
|
|
#include <tactility/drivers/i2c_controller.h>
|
|
#include <tactility/drivers/pointer.h>
|
|
#include <tactility/log.h>
|
|
|
|
#include <freertos/task.h>
|
|
#include <algorithm>
|
|
#include <cstdlib>
|
|
|
|
#define TAG "ST77922 touch"
|
|
#define GET_CONFIG(device) (static_cast<const St77922TouchConfig*>((device)->config))
|
|
|
|
static constexpr uint16_t REG_MAX_TOUCHES = 0x0009;
|
|
static constexpr uint16_t REG_TOUCH_INFO = 0x0010;
|
|
static constexpr uint16_t REG_TOUCH_POINT0 = 0x0014;
|
|
static constexpr uint8_t MAX_POINTS = 10;
|
|
static constexpr TickType_t TIMEOUT = pdMS_TO_TICKS(1000);
|
|
|
|
struct TouchPoint {
|
|
uint16_t x;
|
|
uint16_t y;
|
|
};
|
|
|
|
struct St77922TouchInternal {
|
|
Device* i2c;
|
|
GpioDescriptor* reset;
|
|
uint8_t supported_points;
|
|
uint8_t point_count;
|
|
TouchPoint points[MAX_POINTS];
|
|
bool swap_xy;
|
|
bool mirror_x;
|
|
bool mirror_y;
|
|
};
|
|
|
|
static error_t read_register(Device* i2c, uint16_t reg, uint8_t* data, size_t size, TickType_t timeout) {
|
|
const uint8_t address[] = {
|
|
static_cast<uint8_t>(reg >> 8),
|
|
static_cast<uint8_t>(reg & 0xFF),
|
|
};
|
|
return i2c_controller_write_read(i2c, 0x55, address, sizeof(address), data, size, timeout);
|
|
}
|
|
|
|
static error_t start(Device* device) {
|
|
auto* parent = device_get_parent(device);
|
|
check(device_get_type(parent) == &I2C_CONTROLLER_TYPE);
|
|
const auto* config = GET_CONFIG(device);
|
|
|
|
auto* internal = static_cast<St77922TouchInternal*>(calloc(1, sizeof(St77922TouchInternal)));
|
|
if (internal == nullptr) {
|
|
return ERROR_OUT_OF_MEMORY;
|
|
}
|
|
internal->i2c = parent;
|
|
internal->supported_points = 1;
|
|
internal->swap_xy = config->swap_xy;
|
|
internal->mirror_x = config->mirror_x;
|
|
internal->mirror_y = config->mirror_y;
|
|
|
|
if (config->pin_reset.gpio_controller != nullptr) {
|
|
internal->reset = gpio_descriptor_acquire(config->pin_reset.gpio_controller,
|
|
config->pin_reset.pin, GPIO_FLAG_DIRECTION_OUTPUT | GPIO_FLAG_ACTIVE_LOW, GPIO_OWNER_GPIO);
|
|
if (internal->reset == nullptr) {
|
|
free(internal);
|
|
return ERROR_RESOURCE;
|
|
}
|
|
if (gpio_descriptor_set_level(internal->reset, true) != ERROR_NONE) {
|
|
gpio_descriptor_release(internal->reset);
|
|
free(internal);
|
|
return ERROR_RESOURCE;
|
|
}
|
|
vTaskDelay(pdMS_TO_TICKS(10));
|
|
gpio_descriptor_set_level(internal->reset, false);
|
|
vTaskDelay(pdMS_TO_TICKS(100));
|
|
}
|
|
|
|
uint8_t supported = 0;
|
|
if (read_register(parent, REG_MAX_TOUCHES, &supported, 1, TIMEOUT) == ERROR_NONE
|
|
&& supported > 0 && supported <= MAX_POINTS) {
|
|
internal->supported_points = supported;
|
|
}
|
|
LOG_I(TAG, "Controller reports %u touch points", internal->supported_points);
|
|
device_set_driver_data(device, internal);
|
|
return ERROR_NONE;
|
|
}
|
|
|
|
static error_t stop(Device* device) {
|
|
auto* internal = static_cast<St77922TouchInternal*>(device_get_driver_data(device));
|
|
if (internal->reset != nullptr) {
|
|
gpio_descriptor_release(internal->reset);
|
|
}
|
|
free(internal);
|
|
device_set_driver_data(device, nullptr);
|
|
return ERROR_NONE;
|
|
}
|
|
|
|
static error_t enter_sleep(Device*) { return ERROR_NOT_SUPPORTED; }
|
|
static error_t exit_sleep(Device*) { return ERROR_NOT_SUPPORTED; }
|
|
|
|
static error_t read_data(Device* device, TickType_t timeout) {
|
|
auto* internal = static_cast<St77922TouchInternal*>(device_get_driver_data(device));
|
|
uint8_t touch_info = 0;
|
|
if (read_register(internal->i2c, REG_TOUCH_INFO, &touch_info, 1, timeout) != ERROR_NONE) {
|
|
internal->point_count = 0;
|
|
return ERROR_RESOURCE;
|
|
}
|
|
if ((touch_info & 0x08) == 0) {
|
|
internal->point_count = 0;
|
|
return ERROR_NONE;
|
|
}
|
|
|
|
uint8_t data[7 * MAX_POINTS] = {};
|
|
const size_t read_size = 7 * internal->supported_points;
|
|
if (read_register(internal->i2c, REG_TOUCH_POINT0, data, read_size, timeout) != ERROR_NONE) {
|
|
internal->point_count = 0;
|
|
return ERROR_RESOURCE;
|
|
}
|
|
|
|
internal->point_count = 0;
|
|
for (uint8_t index = 0; index < internal->supported_points; index++) {
|
|
const uint8_t offset = index * 7;
|
|
if ((data[offset] & 0x80) == 0) {
|
|
continue;
|
|
}
|
|
internal->points[internal->point_count++] = {
|
|
.x = static_cast<uint16_t>(((data[offset] & 0x3F) << 8) | data[offset + 1]),
|
|
.y = static_cast<uint16_t>(((data[offset + 2] & 0x3F) << 8) | data[offset + 3]),
|
|
};
|
|
}
|
|
return ERROR_NONE;
|
|
}
|
|
|
|
static bool get_touched_points(Device* device, uint16_t* x, uint16_t* y, uint16_t* strength,
|
|
uint8_t* count, uint8_t maximum) {
|
|
auto* internal = static_cast<St77922TouchInternal*>(device_get_driver_data(device));
|
|
const auto* config = GET_CONFIG(device);
|
|
*count = std::min(internal->point_count, maximum);
|
|
for (uint8_t index = 0; index < *count; index++) {
|
|
uint16_t point_x = internal->points[index].x;
|
|
uint16_t point_y = internal->points[index].y;
|
|
if (internal->swap_xy) {
|
|
std::swap(point_x, point_y);
|
|
}
|
|
const uint16_t max_x = internal->swap_xy ? config->y_max : config->x_max;
|
|
const uint16_t max_y = internal->swap_xy ? config->x_max : config->y_max;
|
|
x[index] = internal->mirror_x ? max_x - point_x : point_x;
|
|
y[index] = internal->mirror_y ? max_y - point_y : point_y;
|
|
if (strength != nullptr) {
|
|
strength[index] = 0;
|
|
}
|
|
}
|
|
return *count > 0;
|
|
}
|
|
|
|
static error_t set_swap_xy(Device* device, bool value) {
|
|
static_cast<St77922TouchInternal*>(device_get_driver_data(device))->swap_xy = value;
|
|
return ERROR_NONE;
|
|
}
|
|
static error_t get_swap_xy(Device* device, bool* value) {
|
|
*value = static_cast<St77922TouchInternal*>(device_get_driver_data(device))->swap_xy;
|
|
return ERROR_NONE;
|
|
}
|
|
static error_t set_mirror_x(Device* device, bool value) {
|
|
static_cast<St77922TouchInternal*>(device_get_driver_data(device))->mirror_x = value;
|
|
return ERROR_NONE;
|
|
}
|
|
static error_t get_mirror_x(Device* device, bool* value) {
|
|
*value = static_cast<St77922TouchInternal*>(device_get_driver_data(device))->mirror_x;
|
|
return ERROR_NONE;
|
|
}
|
|
static error_t set_mirror_y(Device* device, bool value) {
|
|
static_cast<St77922TouchInternal*>(device_get_driver_data(device))->mirror_y = value;
|
|
return ERROR_NONE;
|
|
}
|
|
static error_t get_mirror_y(Device* device, bool* value) {
|
|
*value = static_cast<St77922TouchInternal*>(device_get_driver_data(device))->mirror_y;
|
|
return ERROR_NONE;
|
|
}
|
|
|
|
static const PointerApi pointer_api = {
|
|
.enter_sleep = enter_sleep,
|
|
.exit_sleep = exit_sleep,
|
|
.read_data = read_data,
|
|
.get_touched_points = get_touched_points,
|
|
.set_swap_xy = set_swap_xy,
|
|
.get_swap_xy = get_swap_xy,
|
|
.set_mirror_x = set_mirror_x,
|
|
.get_mirror_x = get_mirror_x,
|
|
.set_mirror_y = set_mirror_y,
|
|
.get_mirror_y = get_mirror_y,
|
|
};
|
|
|
|
Driver st77922_touch_driver = {
|
|
.name = "st77922-touch",
|
|
.compatible = (const char*[]) { "sitronix,st77922-touch", nullptr },
|
|
.start_device = start,
|
|
.stop_device = stop,
|
|
.api = &pointer_api,
|
|
.device_type = &POINTER_TYPE,
|
|
.owner = &st77922_module,
|
|
.internal = nullptr
|
|
};
|