blob: 860e5fcab86945ff00c4e7546a356c5b26c7bf6c [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "gt6853.h"
#include <endian.h>
#include <lib/ddk/debug.h>
#include <lib/ddk/platform-defs.h>
#include <lib/zx/clock.h>
#include <lib/zx/profile.h>
#include <threads.h>
#include <zircon/threads.h>
#include <ddktl/fidl.h>
#include <fbl/auto_lock.h>
#include "src/ui/input/drivers/gt6853/gt6853-bind.h"
namespace {
constexpr int64_t kMaxContactX = 600;
constexpr int64_t kMaxContactY = 1024;
constexpr size_t kContactSize = 8;
constexpr uint8_t kTouchEvent = 1 << 7;
constexpr uint8_t kCpuCtrlHoldSs51 = 0x24;
constexpr zx::duration kResetSetupTime = zx::msec(2);
constexpr int kFirmwareTries = 200;
constexpr size_t kMaxSubsysCount = 28;
constexpr size_t kI2cMaxTransferSize = 256;
constexpr uint8_t kCpuRunFromFlash[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
constexpr uint8_t kCpuRunFromRam[8] = {0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55};
} // namespace
namespace touch {
enum class Gt6853Device::HostCommand : uint8_t {
kConfigStart = 0x80,
kConfigEnd = 0x83,
};
enum class Gt6853Device::DeviceCommand : uint8_t {
kReadyForConfig = 0x82,
kDeviceIdle = 0xff,
};
void Gt6853InputReport::ToFidlInputReport(
fidl::WireTableBuilder<fuchsia_input_report::wire::InputReport>& input_report,
fidl::AnyArena& allocator) {
fidl::VectorView<fuchsia_input_report::wire::ContactInputReport> input_contacts(allocator,
num_contacts);
for (size_t i = 0; i < num_contacts; i++) {
auto contact = fuchsia_input_report::wire::ContactInputReport::Builder(allocator);
contact.contact_id(contacts[i].contact_id);
contact.position_x(contacts[i].position_x);
contact.position_y(contacts[i].position_y);
input_contacts[i] = contact.Build();
}
auto touch_report = fuchsia_input_report::wire::TouchInputReport::Builder(allocator);
touch_report.contacts(input_contacts);
input_report.event_time(event_time.get());
input_report.touch(touch_report.Build());
}
zx_status_t Gt6853Device::Create(void* ctx, zx_device_t* parent) {
ddk::I2cChannel i2c(parent, "i2c");
if (!i2c.is_valid()) {
zxlogf(ERROR, "Failed to get I2C fragment");
return ZX_ERR_NO_RESOURCES;
}
ddk::GpioProtocolClient interrupt_gpio(parent, "gpio-int");
if (!interrupt_gpio.is_valid()) {
zxlogf(ERROR, "Failed to get interrupt GPIO fragment");
return ZX_ERR_NO_RESOURCES;
}
ddk::GpioProtocolClient reset_gpio(parent, "gpio-reset");
if (!reset_gpio.is_valid()) {
zxlogf(ERROR, "Failed to get reset GPIO fragment");
return ZX_ERR_NO_RESOURCES;
}
std::unique_ptr<Gt6853Device> device =
std::make_unique<Gt6853Device>(parent, std::move(i2c), interrupt_gpio, reset_gpio);
if (!device) {
return ZX_ERR_NO_MEMORY;
}
zx_status_t status = device->Init();
if (status != ZX_OK) {
return status;
}
if ((status = device->DdkAdd(ddk::DeviceAddArgs("gt6853").set_inspect_vmo(
device->inspector_.DuplicateVmo()))) != ZX_OK) {
zxlogf(ERROR, "DdkAdd failed: %d", status);
return status;
}
__UNUSED auto _ = device.release();
return ZX_OK;
}
void Gt6853Device::DdkUnbind(ddk::UnbindTxn txn) {
Shutdown();
txn.Reply();
}
void Gt6853Device::GetInputReportsReader(GetInputReportsReaderRequestView request,
GetInputReportsReaderCompleter::Sync& completer) {
zx_status_t status =
input_report_readers_.CreateReader(loop_.dispatcher(), std::move(request->reader));
if (status == ZX_OK) {
sync_completion_signal(&next_reader_wait_); // Only for tests.
}
}
void Gt6853Device::GetDescriptor(GetDescriptorRequestView request,
GetDescriptorCompleter::Sync& completer) {
constexpr size_t kDescriptorBufferSize = 512;
constexpr fuchsia_input_report::wire::Axis kAxisX = {
.range = {.min = 0, .max = kMaxContactX},
.unit = {.type = fuchsia_input_report::wire::UnitType::kNone, .exponent = 0},
};
constexpr fuchsia_input_report::wire::Axis kAxisY = {
.range = {.min = 0, .max = kMaxContactY},
.unit = {.type = fuchsia_input_report::wire::UnitType::kNone, .exponent = 0},
};
fidl::Arena<kDescriptorBufferSize> allocator;
fuchsia_input_report::wire::DeviceInfo device_info;
device_info.vendor_id = static_cast<uint32_t>(fuchsia_input_report::wire::VendorId::kGoogle);
device_info.product_id = static_cast<uint32_t>(
fuchsia_input_report::wire::VendorGoogleProductId::kFocaltechTouchscreen);
fidl::VectorView<fuchsia_input_report::wire::ContactInputDescriptor> touch_input_contacts(
allocator, kMaxContacts);
for (uint32_t i = 0; i < kMaxContacts; i++) {
touch_input_contacts[i] = fuchsia_input_report::wire::ContactInputDescriptor::Builder(allocator)
.position_x(kAxisX)
.position_y(kAxisY)
.Build();
}
auto touch_input_descriptor =
fuchsia_input_report::wire::TouchInputDescriptor::Builder(allocator);
touch_input_descriptor.contacts(touch_input_contacts);
touch_input_descriptor.max_contacts(kMaxContacts);
touch_input_descriptor.touch_type(fuchsia_input_report::wire::TouchType::kTouchscreen);
auto touch_descriptor = fuchsia_input_report::wire::TouchDescriptor::Builder(allocator);
touch_descriptor.input(touch_input_descriptor.Build());
auto descriptor = fuchsia_input_report::wire::DeviceDescriptor::Builder(allocator);
descriptor.device_info(device_info);
descriptor.touch(touch_descriptor.Build());
completer.Reply(descriptor.Build());
}
void Gt6853Device::SendOutputReport(SendOutputReportRequestView request,
SendOutputReportCompleter::Sync& completer) {
completer.ReplyError(ZX_ERR_NOT_SUPPORTED);
}
void Gt6853Device::GetFeatureReport(GetFeatureReportRequestView request,
GetFeatureReportCompleter::Sync& completer) {
completer.ReplyError(ZX_ERR_NOT_SUPPORTED);
}
void Gt6853Device::SetFeatureReport(SetFeatureReportRequestView request,
SetFeatureReportCompleter::Sync& completer) {
completer.ReplyError(ZX_ERR_NOT_SUPPORTED);
}
void Gt6853Device::GetInputReport(GetInputReportRequestView request,
GetInputReportCompleter::Sync& completer) {
completer.ReplyError(ZX_ERR_NOT_SUPPORTED);
}
void Gt6853Device::WaitForNextReader() {
sync_completion_wait(&next_reader_wait_, ZX_TIME_INFINITE);
sync_completion_reset(&next_reader_wait_);
}
Gt6853Contact Gt6853Device::ParseContact(const uint8_t* const contact_buffer) {
Gt6853Contact ret = {};
ret.contact_id = contact_buffer[0] & 0b1111;
ret.position_x = contact_buffer[1] | (contact_buffer[2] << 8);
ret.position_y = contact_buffer[3] | (contact_buffer[4] << 8);
return ret;
}
zx_status_t Gt6853Device::Init() {
root_ = inspector_.GetRoot().CreateChild("gt6853");
firmware_status_ = root_.CreateString("firmware_status", "initialization failed");
config_status_ = root_.CreateString("config_status", "initialization failed");
// These names must match the strings in //src/diagnostics/config/sampler/input.json.
metrics_root_ = inspector_.GetRoot().CreateChild("hid-input-report-touch");
average_latency_usecs_ = metrics_root_.CreateUint("average_latency_usecs", 0);
max_latency_usecs_ = metrics_root_.CreateUint("max_latency_usecs", 0);
zx_status_t status = interrupt_gpio_.ConfigIn(GPIO_NO_PULL);
if (status != ZX_OK) {
zxlogf(ERROR, "ConfigIn failed: %d", status);
return status;
}
if ((status = interrupt_gpio_.GetInterrupt(ZX_INTERRUPT_MODE_EDGE_LOW, &interrupt_)) != ZX_OK) {
zxlogf(ERROR, "GetInterrupt failed: %d", status);
return status;
}
zx::status<fuchsia_mem::wire::Range> config = GetConfigFileVmo();
if (config.is_error()) {
return config.status_value();
}
if (config->vmo.is_valid()) {
if ((status = UpdateFirmwareIfNeeded()) != ZX_OK) {
firmware_status_.Set("failed");
return status;
}
if ((status = DownloadConfigIfNeeded(*config)) != ZX_OK) {
config_status_.Set("failed");
return status;
}
} else {
zxlogf(INFO, "No device metadata, assuming mexec and preserving controller state");
firmware_status_.Set("skipped");
}
status = thrd_create_with_name(
&thread_, [](void* arg) -> int { return reinterpret_cast<Gt6853Device*>(arg)->Thread(); },
this, "gt6853-thread");
if (status != thrd_success) {
zxlogf(ERROR, "Failed to create thread: %d", status);
return thrd_status_to_zx_status(status);
}
// Copied from //src/ui/input/drivers/focaltech/ft_device.cc
// Set profile for device thread.
// TODO(fxbug.dev/40858): Migrate to the role-based API when available, instead of hard
// coding parameters.
{
const zx::duration capacity = zx::usec(200);
const zx::duration deadline = zx::msec(1);
const zx::duration period = deadline;
zx::profile profile;
status = device_get_deadline_profile(zxdev(), capacity.get(), deadline.get(), period.get(),
"gt6853-thread", profile.reset_and_get_address());
if (status != ZX_OK) {
zxlogf(WARNING, "Failed to get deadline profile: %d", status);
} else {
status = zx_object_set_profile(thrd_get_zx_handle(thread_), profile.get(), 0);
if (status != ZX_OK) {
zxlogf(WARNING, "Failed to apply deadline profile to device thread: %d", status);
}
}
}
if ((status = loop_.StartThread("gt6853-reader-thread")) != ZX_OK) {
zxlogf(ERROR, "Failed to start loop: %d", status);
Shutdown();
return status;
}
return ZX_OK;
}
zx_status_t Gt6853Device::DownloadConfigIfNeeded(const fuchsia_mem::wire::Range& config_file) {
zx::status<uint8_t> sensor_id = ReadReg8(Register::kSensorIdReg);
if (sensor_id.is_error()) {
zxlogf(ERROR, "Failed to read sensor ID register: %d", sensor_id.error_value());
return sensor_id.error_value();
}
zxlogf(INFO, "Sensor ID 0x%02x", sensor_id.value());
sensor_id_ = root_.CreateInt("sensor_id", sensor_id.value());
fzl::VmoMapper mapped_config;
zx_status_t status = mapped_config.Map(config_file.vmo, 0, config_file.size, ZX_VM_PERM_READ);
if (status != ZX_OK) {
zxlogf(ERROR, "Failed to map config VMO: %d", status);
return status;
}
zx::status<uint64_t> config_offset = GetConfigOffset(mapped_config, sensor_id.value() & 0xf);
if (config_offset.is_error()) {
return config_offset.error_value();
}
uint32_t config_size = 0;
if (config_file.size < config_offset.value() + sizeof(config_size)) {
zxlogf(ERROR, "Config VMO size is %zu, must be at least %lu", config_file.size,
config_offset.value() + sizeof(config_size));
return ZX_ERR_IO_INVALID;
}
const uint8_t* const config_data = reinterpret_cast<uint8_t*>(mapped_config.start());
memcpy(&config_size, config_data + config_offset.value(), sizeof(config_size));
config_size = le32toh(config_size);
// The offset of the config data in each config table entry.
constexpr uint32_t kConfigDataOffset = 121;
if (config_size < kConfigDataOffset) {
zxlogf(ERROR, "Config size is %u, must be at least %u", config_size, kConfigDataOffset);
return ZX_ERR_IO_INVALID;
}
zxlogf(INFO, "Found %u-byte config at offset %lu", config_size, config_offset.value());
cpp20::span<const uint8_t> config{config_data + config_offset.value() + kConfigDataOffset,
config_size - kConfigDataOffset};
return SendConfig(config);
}
zx::status<uint64_t> Gt6853Device::GetConfigOffset(const fzl::VmoMapper& mapped_config,
const uint8_t sensor_id) {
constexpr size_t kConfigTableHeaderSize = 16;
if (mapped_config.size() < kConfigTableHeaderSize) {
zxlogf(ERROR, "Config VMO size is %zu, must be at least %zu", mapped_config.size(),
kConfigTableHeaderSize);
return zx::error(ZX_ERR_IO_INVALID);
}
uint32_t config_size = 0;
memcpy(&config_size, mapped_config.start(), sizeof(config_size));
config_size = le32toh(config_size);
if (config_size != mapped_config.size()) {
zxlogf(ERROR, "Config size (%u) doesn't match VMO size (%zu)", config_size,
mapped_config.size());
return zx::error(ZX_ERR_IO_INVALID);
}
const uint8_t* const config_data = reinterpret_cast<uint8_t*>(mapped_config.start());
uint8_t expected_checksum = 0;
for (uint64_t i = sizeof(config_size) + 1; i < mapped_config.size(); i++) {
expected_checksum += config_data[i];
}
if (config_data[sizeof(config_size)] != expected_checksum) {
zxlogf(ERROR, "Config checksum doesn't match calculated value");
return zx::error(ZX_ERR_IO_DATA_INTEGRITY);
}
// The offset of the config entry count in the table header.
constexpr uint64_t kConfigEntryCountOffset = 9;
const uint8_t config_count = config_data[kConfigEntryCountOffset];
if (mapped_config.size() < (kConfigTableHeaderSize + (config_count * sizeof(uint16_t)))) {
zxlogf(ERROR, "Config VMO size is %zu, must be at least %zu", mapped_config.size(),
kConfigTableHeaderSize + (config_count * sizeof(uint16_t)));
return zx::error(ZX_ERR_IO_INVALID);
}
for (int i = 0; i < config_count; i++) {
const uint64_t config_offset_offset = kConfigTableHeaderSize + (i * sizeof(uint16_t));
uint16_t config_offset = 0;
memcpy(&config_offset, config_data + config_offset_offset, sizeof(config_offset));
config_offset = le16toh(config_offset);
// The offset of the sensor ID in each config table entry.
constexpr uint64_t kConfigSensorIdOffset = 20;
if (mapped_config.size() < config_offset + kConfigSensorIdOffset) {
zxlogf(ERROR, "Config offset %u is too big", config_offset);
return zx::error(ZX_ERR_IO_INVALID);
}
const uint8_t config_sensor_id = config_data[config_offset + kConfigSensorIdOffset];
if (config_sensor_id == sensor_id) {
return zx::ok(config_offset);
}
}
zxlogf(ERROR, "Failed to find config for sensor ID 0x%02x", sensor_id);
return zx::error(ZX_ERR_NOT_FOUND);
}
zx_status_t Gt6853Device::PollCommandRegister(const DeviceCommand command) {
constexpr int kCommandTimeoutMs = 100; // An arbitrary timeout that seems to work.
for (int i = 0; i < kCommandTimeoutMs; i++) {
auto status = ReadReg8(Register::kCommandReg);
if (status.is_error()) {
zxlogf(ERROR, "Failed to read command register");
return status.error_value();
}
if (status.value() == static_cast<uint8_t>(command)) {
return ZX_OK;
}
zx::nanosleep(zx::deadline_after(zx::msec(1)));
}
zxlogf(ERROR, "Timed out waiting for command register 0x%02x", static_cast<uint8_t>(command));
return ZX_ERR_TIMED_OUT;
}
zx_status_t Gt6853Device::SendCommand(const HostCommand command) {
const uint8_t checksum = 0xff - static_cast<uint8_t>(command) + 1;
uint8_t buffer[] = {
static_cast<uint16_t>(Register::kCommandReg) >> 8,
static_cast<uint16_t>(Register::kCommandReg) & 0xff,
static_cast<uint8_t>(command),
0x00,
checksum,
};
zx_status_t status = i2c_.WriteSync(buffer, sizeof(buffer));
if (status != ZX_OK) {
zxlogf(ERROR, "Failed to send command 0x%02x: %d", static_cast<uint8_t>(command), status);
return status;
}
return ZX_OK;
}
zx_status_t Gt6853Device::SendConfig(cpp20::span<const uint8_t> config) {
zx_status_t status = PollCommandRegister(DeviceCommand::kDeviceIdle);
if (status != ZX_OK) {
zxlogf(ERROR, "Device not idle before config download");
return status;
}
if ((status = SendCommand(HostCommand::kConfigStart)) != ZX_OK) {
zxlogf(ERROR, "Failed to start config download");
return status;
}
if ((status = PollCommandRegister(DeviceCommand::kReadyForConfig)) != ZX_OK) {
return status;
}
constexpr size_t kMaxConfigPacketSize = 128;
size_t size = config.size();
size_t offset = 0;
while (size > 0) {
const size_t tx_size = std::min(size, kMaxConfigPacketSize);
uint8_t buffer[sizeof(Register::kConfigDataReg) + kMaxConfigPacketSize];
buffer[0] = static_cast<uint16_t>(Register::kConfigDataReg) >> 8;
buffer[1] = static_cast<uint16_t>(Register::kConfigDataReg) & 0xff;
memcpy(buffer + 2, config.data() + offset, tx_size);
if ((status = i2c_.WriteSync(buffer, tx_size + sizeof(Register::kConfigDataReg))) != ZX_OK) {
zxlogf(ERROR, "Failed to write %zu config bytes: %d", tx_size, status);
return status;
}
size -= tx_size;
offset += tx_size;
}
if ((status = SendCommand(HostCommand::kConfigEnd)) != ZX_OK) {
zxlogf(ERROR, "Failed to stop config download");
return status;
}
if ((status = PollCommandRegister(DeviceCommand::kDeviceIdle)) != ZX_OK) {
zxlogf(ERROR, "Device not idle after config download");
return status;
}
config_status_.Set("download succeeded");
return ZX_OK;
}
zx_status_t Gt6853Device::UpdateFirmwareIfNeeded() {
zx::vmo fw_vmo;
size_t fw_vmo_size = 0;
zx_status_t status =
load_firmware(parent(), GT6853_FIRMWARE_PATH, fw_vmo.reset_and_get_address(), &fw_vmo_size);
if (status != ZX_OK) {
zxlogf(WARNING, "Failed to load firmware binary, skipping firmware update");
firmware_status_.Set("skipped, no firmware found");
return ZX_OK;
}
fzl::VmoMapper mapped_fw;
if ((status = mapped_fw.Map(fw_vmo, 0, fw_vmo_size, ZX_VM_PERM_READ)) != ZX_OK) {
zxlogf(ERROR, "Failed to map firmware VMO: %d", status);
return status;
}
FirmwareSubsysInfo subsys_entries[kMaxSubsysCount];
zx::status<size_t> entry_count = ParseFirmwareInfo(mapped_fw, subsys_entries);
if (entry_count.is_error()) {
return entry_count.error_value();
}
cpp20::span<const FirmwareSubsysInfo> subsys_span(subsys_entries, entry_count.value());
if ((status = PrepareFirmwareUpdate(subsys_span)) != ZX_OK) {
return status;
}
for (const FirmwareSubsysInfo& subsys_info : subsys_span.subspan(1)) {
if ((status = FlashSubsystem(subsys_info)) != ZX_OK) {
return status;
}
}
return FinishFirmwareUpdate();
}
zx::status<size_t> Gt6853Device::ParseFirmwareInfo(const fzl::VmoMapper& mapped_fw,
FirmwareSubsysInfo* out_subsys_entries) {
constexpr size_t kFirmwareHeaderSize = 32;
constexpr size_t kSubsysCountOffset = 27;
constexpr size_t kSubsysEntrySize = 8;
constexpr size_t kSubsysDataOffset = kFirmwareHeaderSize + (kMaxSubsysCount * kSubsysEntrySize);
if (mapped_fw.size() < kSubsysDataOffset) {
zxlogf(ERROR, "Firmware VMO size is %zu, must be at least %zu", mapped_fw.size(),
kSubsysDataOffset);
return zx::error(ZX_ERR_IO_INVALID);
}
const uint8_t* fw_data = static_cast<const uint8_t*>(mapped_fw.start());
uint32_t fw_size;
memcpy(&fw_size, fw_data, sizeof(fw_size));
fw_size = be32toh(fw_size);
uint16_t checksum;
if (fw_size + sizeof(fw_size) + sizeof(checksum) != mapped_fw.size()) {
zxlogf(ERROR, "Firmware header indicates size %zu, but VMO size is %zu",
fw_size + sizeof(fw_size) + sizeof(checksum), mapped_fw.size());
return zx::error(ZX_ERR_IO_INVALID);
}
memcpy(&checksum, fw_data + sizeof(fw_size), sizeof(checksum));
checksum = be16toh(checksum);
uint16_t expected_checksum = 0;
for (size_t i = sizeof(fw_size) + sizeof(checksum); i < mapped_fw.size(); i++) {
expected_checksum += fw_data[i];
}
if (checksum != expected_checksum) {
zxlogf(ERROR, "Firmware checksum doesn't match calculated value");
return zx::error(ZX_ERR_IO_DATA_INTEGRITY);
}
const uint8_t subsys_count = fw_data[kSubsysCountOffset];
if (subsys_count > kMaxSubsysCount) {
zxlogf(ERROR, "Firmware subsys count is %u, only %zu are allowed", subsys_count,
kMaxSubsysCount);
return zx::error(ZX_ERR_IO_INVALID);
}
size_t subsys_data_offset = kSubsysDataOffset;
for (uint32_t i = 0; i < subsys_count; i++) {
const uint8_t* subsys_header = fw_data + kFirmwareHeaderSize + (i * kSubsysEntrySize);
FirmwareSubsysInfo& entry = out_subsys_entries[i];
entry.type = *subsys_header++;
memcpy(&entry.size, subsys_header, sizeof(entry.size));
entry.size = be32toh(entry.size);
subsys_header += sizeof(entry.size);
memcpy(&entry.flash_addr, subsys_header, sizeof(entry.flash_addr));
entry.flash_addr = be16toh(entry.flash_addr);
entry.data = fw_data + subsys_data_offset;
subsys_data_offset += entry.size;
if (subsys_data_offset > mapped_fw.size()) {
zxlogf(ERROR, "Subsys offset %zu exceeds firmware size", subsys_data_offset);
return zx::error(ZX_ERR_IO_INVALID);
}
}
return zx::ok(subsys_count);
}
zx_status_t Gt6853Device::PrepareFirmwareUpdate(
cpp20::span<const FirmwareSubsysInfo> subsys_entries) {
constexpr zx::duration kResetHoldTime = zx::msec(10);
constexpr int kHoldSs51Tries = 20;
constexpr zx::duration kHoldSs51TryInterval = zx::msec(20);
if (subsys_entries.empty()) {
zxlogf(ERROR, "Expected at least one firmware subsys entry");
return ZX_ERR_IO_INVALID;
}
zx::status<> status = Write(Register::kCpuRunFrom, kCpuRunFromFlash, sizeof(kCpuRunFromFlash));
if (status.is_error()) {
return status.error_value();
}
reset_gpio_.ConfigOut(0);
zx::nanosleep(zx::deadline_after(kResetSetupTime));
reset_gpio_.Write(1);
zx::nanosleep(zx::deadline_after(kResetHoldTime));
for (int i = 0; i < kHoldSs51Tries; i++) {
status = WriteAndCheck(Register::kCpuCtrl, &kCpuCtrlHoldSs51, sizeof(kCpuCtrlHoldSs51));
if (status.is_error()) {
zx::nanosleep(zx::deadline_after(kHoldSs51TryInterval));
} else {
break;
}
}
if (status.is_error()) {
zxlogf(ERROR, "Timed out waiting for CPU control register");
return ZX_ERR_TIMED_OUT;
}
const uint8_t dsp_mcu_power = 0;
status = WriteAndCheck(Register::kDspMcuPower, &dsp_mcu_power, sizeof(dsp_mcu_power));
if (status.is_error()) {
zxlogf(ERROR, "Failed to enable DSP/MCU power: %d", status.error_value());
return status.error_value();
}
// Disable the watchdog timer.
constexpr uint8_t kWatchdogDisableKey1 = 0x95;
constexpr uint8_t kWatchdogDisableKey2 = 0x27;
if ((status = WriteReg8(Register::kCache, 0)).is_error()) {
return status.error_value();
}
if ((status = WriteReg8(Register::kEsdKey, kWatchdogDisableKey1)).is_error()) {
return status.error_value();
}
if ((status = WriteReg8(Register::kWtdTimer, 0)).is_error()) {
return status.error_value();
}
if ((status = WriteReg8(Register::kEsdKey, kWatchdogDisableKey2)).is_error()) {
return status.error_value();
}
if ((status = WriteReg8(Register::kScramble, 0)).is_error()) {
return status.error_value();
}
return LoadIsp(subsys_entries[0]);
}
zx_status_t Gt6853Device::LoadIsp(const FirmwareSubsysInfo& isp_info) {
zx::status status = WriteReg8(Register::kBankSelect, 0);
if (status.is_error()) {
return status.error_value();
}
if ((status = WriteReg8(Register::kAccessPatch0, 1)).is_error()) {
return status.error_value();
}
if ((status = WriteAndCheck(Register::kIspAddr, isp_info.data, isp_info.size)).is_error()) {
zxlogf(ERROR, "Failed to write ISP data: %d", status.error_value());
return status.error_value();
}
const uint8_t disable_patch0_access = 0;
if ((status = WriteAndCheck(Register::kAccessPatch0, &disable_patch0_access, 1)).is_error()) {
zxlogf(ERROR, "Failed to write disable patch0 access: %d", status.error_value());
return status.error_value();
}
const uint8_t isp_run_flag[2] = {0, 0};
if ((status = Write(Register::kIspRunFlag, isp_run_flag, sizeof(isp_run_flag))).is_error()) {
return status.error_value();
}
if ((status = Write(Register::kCpuRunFrom, kCpuRunFromRam, sizeof(kCpuRunFromRam))).is_error()) {
return status.error_value();
}
if ((status = WriteReg8(Register::kCpuCtrl, 0)).is_error()) {
return status.error_value();
}
constexpr uint8_t kIspRunFlagWorking1 = 0xaa;
constexpr uint8_t kIspRunFlagWorking2 = 0xbb;
constexpr zx::duration kIspRunFlagTryInterval = zx::msec(10);
for (int i = 0; i < kFirmwareTries; i++) {
zx::nanosleep(zx::deadline_after(kIspRunFlagTryInterval));
uint8_t isp_run_check[2];
if (Read(Register::kIspRunFlag, isp_run_check, sizeof(isp_run_check)).is_ok()) {
if (isp_run_check[0] == kIspRunFlagWorking1 && isp_run_check[1] == kIspRunFlagWorking2) {
return ZX_OK;
}
}
}
zxlogf(ERROR, "Timed out waiting for ISP to be ready");
return ZX_ERR_TIMED_OUT;
}
zx_status_t Gt6853Device::FlashSubsystem(const FirmwareSubsysInfo& subsys_info) {
constexpr uint32_t kIspMaxTransferSize = 1024 * 4;
constexpr size_t kPacketHeaderAndChecksumSize = sizeof(uint16_t) * 3;
constexpr int kFirmwarePacketTries = 3;
// Packet format (total size n, all fields big-endian):
// 0x00: data length
// 0x02: flash address
// 0x04: data
// ...: data
// n-2: checksum of data length, flash address, and data fields
uint32_t remaining = subsys_info.size;
uint32_t offset = 0;
while (remaining > 0) {
const auto transfer_size = static_cast<uint16_t>(std::min(remaining, kIspMaxTransferSize));
uint8_t packet_buffer[kIspMaxTransferSize + kPacketHeaderAndChecksumSize];
const uint16_t transfer_size_be = htobe16(transfer_size);
memcpy(packet_buffer, &transfer_size_be, 2);
const uint16_t flash_addr_be = htobe16(subsys_info.flash_addr + (offset >> 8));
memcpy(packet_buffer + 2, &flash_addr_be, 2);
memcpy(packet_buffer + 4, subsys_info.data + offset, transfer_size);
const uint16_t checksum_be = htobe16(Checksum16(packet_buffer, transfer_size + 4));
memcpy(packet_buffer + transfer_size + 4, &checksum_be, 2);
zx_status_t status;
for (int i = 0; i < kFirmwarePacketTries; i++) {
status = SendFirmwarePacket(subsys_info.type, packet_buffer,
transfer_size + kPacketHeaderAndChecksumSize);
if (status == ZX_OK) {
break;
}
}
if (status != ZX_OK) {
zxlogf(ERROR, "Exhausted retries for sending subsys %u packet", subsys_info.type);
return status;
}
offset += transfer_size;
remaining -= transfer_size;
}
return ZX_OK;
}
uint16_t Gt6853Device::Checksum16(const uint8_t* data, const size_t size) {
ZX_ASSERT(size % 2 == 0);
uint16_t checksum = 0;
for (size_t i = 0; i < size; i += 2) {
uint16_t entry;
memcpy(&entry, data + i, 2);
checksum += be16toh(entry);
}
return ~checksum + 1;
}
zx_status_t Gt6853Device::SendFirmwarePacket(const uint8_t type, const uint8_t* packet,
const size_t size) {
constexpr uint8_t kFlashStatusWriting = 0xaa;
constexpr uint8_t kFlashStatusSuccess = 0xbb;
constexpr uint8_t kFlashStatusError = 0xcc;
constexpr uint8_t kFlashStatusCheckError = 0xdd;
constexpr zx::duration kFlashWritingWait = zx::msec(55);
zx::status<> status = WriteAndCheck(Register::kIspBuffer, packet, size);
if (status.is_error()) {
zxlogf(ERROR, "Failed to send firmware packet: %d", status.error_value());
return status.error_value();
}
const uint8_t flash_flag[2] = {0, 0};
if ((status = WriteAndCheck(Register::kFlashFlag, flash_flag, sizeof(flash_flag))).is_error()) {
zxlogf(ERROR, "Failed to set flash flag: %d", status.error_value());
return status.error_value();
}
const uint8_t subsys_type[2] = {type, type};
status = WriteAndCheck(Register::kSubsysType, subsys_type, sizeof(subsys_type));
if (status.is_error()) {
zxlogf(ERROR, "Failed to set subsys type to %u: %d", type, status.error_value());
return status.error_value();
}
for (int i = 0; i < kFirmwareTries; i++) {
uint8_t flash_status[2];
if ((status = Read(Register::kFlashFlag, flash_status, sizeof(flash_status))).is_error()) {
return status.error_value();
}
if (flash_status[0] == kFlashStatusWriting && flash_status[1] == kFlashStatusWriting) {
zx::nanosleep(zx::deadline_after(kFlashWritingWait));
continue;
}
if (flash_status[0] == kFlashStatusSuccess && flash_status[1] == kFlashStatusSuccess) {
if ((status = Read(Register::kFlashFlag, flash_status, sizeof(flash_status))).is_error()) {
return status.error_value();
}
if (flash_status[0] == kFlashStatusSuccess && flash_status[1] == kFlashStatusSuccess) {
return ZX_OK;
}
}
if (flash_status[0] == kFlashStatusError && flash_status[1] == kFlashStatusError) {
zxlogf(ERROR, "Failed to flash subsys %u", type);
return ZX_ERR_IO;
}
if (flash_status[0] == kFlashStatusCheckError) {
zxlogf(ERROR, "Flash checksum error for subsys %u", type);
return ZX_ERR_IO_DATA_INTEGRITY;
}
zx::nanosleep(zx::deadline_after(zx::msec(1)));
}
zxlogf(ERROR, "Timed out waiting for subsys %u flash to complete", type);
return ZX_ERR_TIMED_OUT;
}
zx_status_t Gt6853Device::FinishFirmwareUpdate() {
constexpr zx::duration kResetHoldTime = zx::msec(80);
zx::status<> status = WriteReg8(Register::kCpuCtrl, kCpuCtrlHoldSs51);
if (status.is_error()) {
return status.error_value();
}
status = Write(Register::kCpuRunFrom, kCpuRunFromFlash, sizeof(kCpuRunFromFlash));
if (status.is_error()) {
return status.error_value();
}
if ((status = WriteReg8(Register::kCpuCtrl, 0)).is_error()) {
return status.error_value();
}
reset_gpio_.Write(0);
zx::nanosleep(zx::deadline_after(kResetSetupTime));
reset_gpio_.Write(1);
zx::nanosleep(zx::deadline_after(kResetHoldTime));
zxlogf(INFO, "Updated firmware, reset IC");
firmware_status_.Set("update succeeded");
return ZX_OK;
}
zx::status<uint8_t> Gt6853Device::ReadReg8(const Register reg) {
const uint16_t address = htobe16(static_cast<uint16_t>(reg));
uint8_t value = 0;
zx_status_t status = i2c_.WriteReadSync(reinterpret_cast<const uint8_t*>(&address),
sizeof(address), &value, sizeof(value));
if (status != ZX_OK) {
zxlogf(ERROR, "Failed to read from 0x%02x: %d", address, status);
return zx::error_status(status);
}
return zx::ok(value);
}
zx::status<> Gt6853Device::Read(const Register reg, uint8_t* buffer, const size_t size) {
uint16_t address = static_cast<uint16_t>(reg);
size_t remaining = size;
while (remaining > 0) {
const size_t transfer_size = std::min(remaining, kI2cMaxTransferSize);
const uint16_t be_address = htobe16(address);
zx_status_t status = i2c_.WriteReadSync(reinterpret_cast<const uint8_t*>(&be_address),
sizeof(be_address), buffer, transfer_size);
if (status != ZX_OK) {
zxlogf(ERROR, "Failed to read %zu bytes from 0x%02x: %d", transfer_size, address, status);
return zx::error_status(status);
}
address += transfer_size;
buffer += transfer_size;
remaining -= transfer_size;
}
return zx::ok();
}
zx::status<> Gt6853Device::WriteReg8(const Register reg, const uint8_t value) {
const uint16_t address = static_cast<uint16_t>(reg);
const uint8_t buffer[] = {
static_cast<uint8_t>(address >> 8),
static_cast<uint8_t>(address & 0xff),
value,
};
zx_status_t status = i2c_.WriteSync(buffer, sizeof(buffer));
if (status != ZX_OK) {
zxlogf(ERROR, "Failed to write 0x%02x to 0x%02x: %d", value, address, status);
return zx::error_status(status);
}
return zx::ok();
}
zx::status<> Gt6853Device::Write(const Register reg, const uint8_t* buffer, const size_t size) {
uint16_t address = static_cast<uint16_t>(reg);
size_t remaining = size;
while (remaining > 0) {
const size_t transfer_size = std::min(remaining, kI2cMaxTransferSize - sizeof(address));
const uint16_t be_address = htobe16(address);
uint8_t write_buffer[kI2cMaxTransferSize];
memcpy(write_buffer, &be_address, sizeof(be_address));
memcpy(write_buffer + sizeof(be_address), buffer, transfer_size);
zx_status_t status = i2c_.WriteSync(write_buffer, sizeof(be_address) + transfer_size);
if (status != ZX_OK) {
zxlogf(ERROR, "Failed to write %zu bytes to 0x%02x: %d", transfer_size, address, status);
return zx::error_status(status);
}
address += transfer_size;
buffer += transfer_size;
remaining -= transfer_size;
}
return zx::ok();
}
zx::status<> Gt6853Device::WriteAndCheck(Register reg, const uint8_t* buffer, size_t size) {
zx::status<> write_status = Write(reg, buffer, size);
if (write_status.is_error()) {
return write_status;
}
uint16_t address = static_cast<uint16_t>(reg);
size_t remaining = size;
while (remaining > 0) {
const size_t transfer_size = std::min(remaining, kI2cMaxTransferSize);
const uint16_t be_address = htobe16(address);
uint8_t read_buffer[kI2cMaxTransferSize];
zx_status_t status = i2c_.WriteReadSync(reinterpret_cast<const uint8_t*>(&be_address),
sizeof(be_address), read_buffer, transfer_size);
if (status != ZX_OK) {
zxlogf(ERROR, "Failed to read %zu bytes from 0x%02x: %d", transfer_size, address, status);
return zx::error_status(status);
}
if (memcmp(read_buffer, buffer, transfer_size) != 0) {
return zx::error_status(ZX_ERR_IO_DATA_INTEGRITY);
}
address += transfer_size;
buffer += transfer_size;
remaining -= transfer_size;
}
return zx::ok();
}
int Gt6853Device::Thread() {
zx::time timestamp;
while (interrupt_.wait(&timestamp) == ZX_OK) {
zx::status<uint8_t> status = ReadReg8(Register::kEventStatusReg);
if (status.is_error()) {
zxlogf(ERROR, "Failed to read event status register");
return thrd_error;
}
if (status.value() != kTouchEvent) {
continue;
}
if ((status = ReadReg8(Register::kContactsReg)).is_error()) {
zxlogf(ERROR, "Failed to read contact count register");
return thrd_error;
}
const uint8_t contacts = status.value() & 0b1111;
if (contacts > kMaxContacts) {
zxlogf(ERROR, "Touch event with too many contacts: %u", contacts);
return thrd_error;
}
uint8_t contacts_buffer[kContactSize * kMaxContacts] = {};
if (Read(Register::kContactsStartReg, contacts_buffer, contacts * kContactSize).is_error()) {
zxlogf(ERROR, "Failed to read contacts");
return thrd_error;
}
// Clear the status register so that interrupts stop being generated.
if (WriteReg8(Register::kEventStatusReg, 0).is_error()) {
zxlogf(ERROR, "Failed to reset event status register");
return thrd_error;
}
Gt6853InputReport report = {
.event_time = timestamp,
.contacts = {},
.num_contacts = contacts,
};
for (uint8_t i = 0; i < contacts; i++) {
report.contacts[i] = ParseContact(&contacts_buffer[i * kContactSize]);
}
input_report_readers_.SendReportToAllReaders(report);
const zx::duration latency = zx::clock::get_monotonic() - timestamp;
total_latency_ += latency;
report_count_++;
average_latency_usecs_.Set(total_latency_.to_usecs() / report_count_);
if (latency > max_latency_) {
max_latency_ = latency;
max_latency_usecs_.Set(max_latency_.to_usecs());
}
}
return thrd_success;
}
void Gt6853Device::Shutdown() {
interrupt_.destroy();
thrd_join(thread_, nullptr);
}
static zx_driver_ops_t gt6853_driver_ops = []() -> zx_driver_ops_t {
zx_driver_ops_t ops = {};
ops.version = DRIVER_OPS_VERSION;
ops.bind = Gt6853Device::Create;
return ops;
}();
} // namespace touch
ZIRCON_DRIVER(Gt6853Device, touch::gt6853_driver_ops, "zircon", "0.1");