// SPDX-License-Identifier: Apache-2.0 #include #include #include #include #include #include #include #include #include #include #include #include #define TAG "ST77922 touch" #define GET_CONFIG(device) (static_cast((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(reg >> 8), static_cast(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(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(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(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(((data[offset] & 0x3F) << 8) | data[offset + 1]), .y = static_cast(((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(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(device_get_driver_data(device))->swap_xy = value; return ERROR_NONE; } static error_t get_swap_xy(Device* device, bool* value) { *value = static_cast(device_get_driver_data(device))->swap_xy; return ERROR_NONE; } static error_t set_mirror_x(Device* device, bool value) { static_cast(device_get_driver_data(device))->mirror_x = value; return ERROR_NONE; } static error_t get_mirror_x(Device* device, bool* value) { *value = static_cast(device_get_driver_data(device))->mirror_x; return ERROR_NONE; } static error_t set_mirror_y(Device* device, bool value) { static_cast(device_get_driver_data(device))->mirror_y = value; return ERROR_NONE; } static error_t get_mirror_y(Device* device, bool* value) { *value = static_cast(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 };