mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-12-30 18:41:11 +00:00
Allow to configure the sensor to detect autonomously finger touch event similar to what is done in the suspend() routine of the kernel fpc1020 driver. Signed-off-by: Vincent Palatin <vpalatin@chromium.org> BRANCH=none BUG=b:35648259 TEST=make BOARD=eve_fp Change-Id: I8b78bd6bdeecd8658850383417c950d9025fdf40 Reviewed-on: https://chromium-review.googlesource.com/491072 Commit-Ready: Vincent Palatin <vpalatin@chromium.org> Tested-by: Vincent Palatin <vpalatin@chromium.org> Reviewed-by: Todd Broch <tbroch@chromium.org>
343 lines
7.9 KiB
C
343 lines
7.9 KiB
C
/* Copyright 2017 The Chromium OS 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 "atomic.h"
|
|
#include "common.h"
|
|
#include "console.h"
|
|
#include "ec_commands.h"
|
|
#include "endian.h"
|
|
#include "gpio.h"
|
|
#include "host_command.h"
|
|
#include "mkbp_event.h"
|
|
#include "spi.h"
|
|
#include "system.h"
|
|
#include "task.h"
|
|
#include "timer.h"
|
|
#include "util.h"
|
|
#ifdef HAVE_PRIVATE
|
|
#include "fpc1145_private.h"
|
|
|
|
uint8_t fp_buffer[FPC_IMAGE_SIZE];
|
|
#endif
|
|
|
|
#define CPRINTF(format, args...) cprintf(CC_FP, format, ## args)
|
|
#define CPRINTS(format, args...) cprints(CC_FP, format, ## args)
|
|
|
|
/* Sensor IC commands */
|
|
enum fpc_cmd {
|
|
FPC_CMD_STATUS = 0x14,
|
|
FPC_CMD_INT_STS = 0x18,
|
|
FPC_CMD_INT_CLR = 0x1C,
|
|
FPC_CMD_FINGER_QUERY = 0x20,
|
|
FPC_CMD_SLEEP = 0x28,
|
|
FPC_CMD_DEEPSLEEP = 0x2B,
|
|
FPC_CMD_SOFT_RESET = 0xF8,
|
|
FPC_CMD_HW_ID = 0xFC,
|
|
};
|
|
|
|
#define FPC_IDLE_MASK 0x1E
|
|
|
|
#define FPC_INT_FINGER_DOWN (1 << 0)
|
|
|
|
#ifndef SPI_FPC_DEVICE
|
|
#define SPI_FPC_DEVICE (&spi_devices[0])
|
|
#endif
|
|
|
|
static uint32_t fp_events;
|
|
static uint32_t sensor_mode;
|
|
|
|
/* Opaque sensor configuration registers settings */
|
|
static union {
|
|
struct ec_params_fp_sensor_config sensor_config;
|
|
uint8_t data[0x220];
|
|
} u;
|
|
|
|
static int fpc_send_cmd(const uint8_t cmd)
|
|
{
|
|
return spi_transaction(SPI_FPC_DEVICE, &cmd, 1, NULL, 0);
|
|
}
|
|
|
|
static int fpc_check_hwid(void)
|
|
{
|
|
const uint8_t cmd = FPC_CMD_HW_ID;
|
|
uint16_t id;
|
|
int rc;
|
|
|
|
rc = spi_transaction(SPI_FPC_DEVICE, &cmd, 1, (void *)&id, sizeof(id));
|
|
if (rc) {
|
|
CPRINTS("FPC ID read failed %d", rc);
|
|
return rc;
|
|
}
|
|
id = be16toh(id);
|
|
if ((id >> 4) != 0x140) {
|
|
CPRINTS("FPC unknown silicon 0x%04x", id);
|
|
return EC_ERROR_INVAL;
|
|
}
|
|
CPRINTS("FPC1140 id 0x%04x", id);
|
|
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
static uint8_t fpc_read_clear_int(void)
|
|
{
|
|
const uint8_t cmd = FPC_CMD_INT_CLR;
|
|
uint8_t val = 0xff;
|
|
|
|
if (spi_transaction(SPI_FPC_DEVICE, &cmd, 1, &val, 1))
|
|
return 0xff;
|
|
return val;
|
|
}
|
|
|
|
static uint8_t fpc_read_int(void)
|
|
{
|
|
const uint8_t cmd = FPC_CMD_INT_STS;
|
|
uint8_t val = 0xff;
|
|
|
|
if (spi_transaction(SPI_FPC_DEVICE, &cmd, 1, &val, 1))
|
|
return 0xff;
|
|
return val;
|
|
}
|
|
|
|
static uint8_t fpc_read_status(void)
|
|
{
|
|
const uint8_t cmd = FPC_CMD_STATUS;
|
|
uint8_t val[2];
|
|
|
|
if (spi_transaction(SPI_FPC_DEVICE, &cmd, 1, val, 2))
|
|
return 0xff;
|
|
return val[1];
|
|
}
|
|
|
|
static int fpc_wait_for_idle(void)
|
|
{
|
|
uint8_t sts;
|
|
int retries = 100;
|
|
|
|
do {
|
|
fpc_read_clear_int();
|
|
sts = fpc_read_status();
|
|
} while (sts != FPC_IDLE_MASK && retries--);
|
|
|
|
return sts != FPC_IDLE_MASK;
|
|
}
|
|
|
|
/* Reset and initialize the sensor IC */
|
|
static int fpc_init(void)
|
|
{
|
|
#ifdef HAVE_PRIVATE
|
|
memcpy(&u.sensor_config, &fpc1145_config, fpc1145_config_size);
|
|
#endif
|
|
/* configure the SPI controller (also ensure that CS_N is high) */
|
|
gpio_config_module(MODULE_SPI_MASTER, 1);
|
|
spi_enable(CONFIG_SPI_FP_PORT, 1);
|
|
|
|
/* Ensure we pulse reset low to initiate the startup */
|
|
gpio_set_level(GPIO_FP_RST_ODL, 0);
|
|
usleep(100);
|
|
gpio_set_level(GPIO_FP_RST_ODL, 1);
|
|
/* the IRQ line should be set high by the sensor */
|
|
usleep(10000);
|
|
if (!gpio_get_level(GPIO_FPS_INT)) {
|
|
CPRINTS("FPC not ready");
|
|
return EC_ERROR_TIMEOUT;
|
|
}
|
|
|
|
/* Check the Hardware ID */
|
|
if (fpc_check_hwid())
|
|
return EC_ERROR_INVAL;
|
|
|
|
/* clear the pending 'ready' IRQ before enabling interrupts */
|
|
fpc_read_clear_int();
|
|
gpio_enable_interrupt(GPIO_FPS_INT);
|
|
|
|
fpc_send_cmd(FPC_CMD_DEEPSLEEP);
|
|
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
static void fp_configure_sensor(void)
|
|
{
|
|
int i, index;
|
|
|
|
for (i = 0, index = 0; i < u.sensor_config.count; i++) {
|
|
uint8_t *data = u.sensor_config.data + index;
|
|
int len = u.sensor_config.len[i];
|
|
int rc = spi_transaction(&spi_devices[0], data, len, NULL, 0);
|
|
|
|
if (rc)
|
|
CPRINTS("Config %d failed with %d for 0x%02x",
|
|
i, rc, data[0]);
|
|
index += len;
|
|
}
|
|
}
|
|
|
|
static void fp_prepare_capture(void)
|
|
{
|
|
/* wake it from deep-sleep by doing a soft-reset */
|
|
fpc_send_cmd(FPC_CMD_SOFT_RESET);
|
|
fpc_wait_for_idle();
|
|
fp_configure_sensor();
|
|
/* sleep until the finger down is detected */
|
|
fpc_send_cmd(FPC_CMD_SLEEP);
|
|
}
|
|
|
|
/* Interrupt line from the fingerprint sensor */
|
|
void fps_event(enum gpio_signal signal)
|
|
{
|
|
task_wake(TASK_ID_FPC1140);
|
|
}
|
|
|
|
void fp_task(void)
|
|
{
|
|
fpc_init();
|
|
|
|
while (1) {
|
|
uint8_t evt;
|
|
|
|
task_wait_event(-1);
|
|
evt = fpc_read_int();
|
|
atomic_or(&fp_events, evt);
|
|
CPRINTS("FPS event %02x", evt);
|
|
|
|
if (evt & FPC_INT_FINGER_DOWN)
|
|
CPRINTS("Finger!");
|
|
|
|
if (evt)
|
|
mkbp_send_event(EC_MKBP_EVENT_FINGERPRINT);
|
|
}
|
|
}
|
|
|
|
static int fp_get_next_event(uint8_t *out)
|
|
{
|
|
uint32_t event_out = atomic_read_clear(&fp_events);
|
|
|
|
memcpy(out, &event_out, sizeof(event_out));
|
|
|
|
return sizeof(event_out);
|
|
}
|
|
DECLARE_EVENT_SOURCE(EC_MKBP_EVENT_FINGERPRINT, fp_get_next_event);
|
|
|
|
static int fp_command_passthru(struct host_cmd_handler_args *args)
|
|
{
|
|
const struct ec_params_fp_passthru *params = args->params;
|
|
void *out = args->response;
|
|
int rc;
|
|
int ret = EC_RES_SUCCESS;
|
|
|
|
if (system_is_locked())
|
|
return EC_RES_ACCESS_DENIED;
|
|
|
|
if (params->len > args->params_size +
|
|
offsetof(struct ec_params_fp_passthru, data) ||
|
|
params->len > args->response_max)
|
|
return EC_RES_INVALID_PARAM;
|
|
|
|
rc = spi_transaction_async(&spi_devices[0], params->data,
|
|
params->len, out, SPI_READBACK_ALL);
|
|
if (params->flags & EC_FP_FLAG_NOT_COMPLETE)
|
|
rc |= spi_transaction_wait(&spi_devices[0]);
|
|
else
|
|
rc |= spi_transaction_flush(&spi_devices[0]);
|
|
|
|
if (rc == EC_ERROR_TIMEOUT)
|
|
ret = EC_RES_TIMEOUT;
|
|
else if (rc)
|
|
ret = EC_RES_ERROR;
|
|
|
|
args->response_size = params->len;
|
|
return ret;
|
|
}
|
|
DECLARE_HOST_COMMAND(EC_CMD_FP_PASSTHRU, fp_command_passthru, EC_VER_MASK(0));
|
|
|
|
static int fp_command_sensor_config(struct host_cmd_handler_args *args)
|
|
{
|
|
const struct ec_params_fp_sensor_config *p = args->params;
|
|
int i, index;
|
|
unsigned limit = args->params_size
|
|
- offsetof(struct ec_params_fp_sensor_config, data);
|
|
|
|
/* Validate the content size */
|
|
if (p->count > EC_FP_SENSOR_CONFIG_MAX_REGS ||
|
|
args->params_size > sizeof(u))
|
|
return EC_RES_INVALID_PARAM;
|
|
for (i = 0, index = 0; i < p->count; i++) {
|
|
if (index + p->len[i] > limit)
|
|
return EC_RES_INVALID_PARAM;
|
|
index += p->len[i];
|
|
}
|
|
|
|
memcpy(&u.sensor_config, p, args->params_size);
|
|
|
|
return EC_RES_SUCCESS;
|
|
}
|
|
DECLARE_HOST_COMMAND(EC_CMD_FP_SENSOR_CONFIG, fp_command_sensor_config,
|
|
EC_VER_MASK(0));
|
|
|
|
static int fp_command_mode(struct host_cmd_handler_args *args)
|
|
{
|
|
const struct ec_params_fp_mode *p = args->params;
|
|
struct ec_response_fp_mode *r = args->response;
|
|
|
|
if (!(p->mode & FP_MODE_DONT_CHANGE)) {
|
|
sensor_mode = p->mode;
|
|
if (p->mode & FP_MODE_DEEPSLEEP) {
|
|
fpc_send_cmd(FPC_CMD_DEEPSLEEP);
|
|
} else {
|
|
if (p->mode & FP_MODE_FINGER_DOWN)
|
|
fp_prepare_capture();
|
|
if (p->mode & FP_MODE_FINGER_UP)
|
|
/* TBD */;
|
|
}
|
|
}
|
|
|
|
r->mode = sensor_mode;
|
|
args->response_size = sizeof(*r);
|
|
return EC_RES_SUCCESS;
|
|
}
|
|
DECLARE_HOST_COMMAND(EC_CMD_FP_MODE, fp_command_mode, EC_VER_MASK(0));
|
|
|
|
static int fp_command_info(struct host_cmd_handler_args *args)
|
|
{
|
|
struct ec_response_fp_info *r = args->response;
|
|
int rc;
|
|
const uint8_t cmd = FPC_CMD_HW_ID;
|
|
uint16_t id;
|
|
int ret = EC_RES_SUCCESS;
|
|
|
|
#ifdef HAVE_PRIVATE
|
|
memcpy(r, &fpc1145_info, sizeof(*r));
|
|
#else
|
|
return EC_RES_UNAVAILABLE;
|
|
#endif
|
|
rc = spi_transaction(SPI_FPC_DEVICE, &cmd, 1, (void *)&id, sizeof(id));
|
|
if (rc)
|
|
return EC_RES_ERROR;
|
|
r->model_id = be16toh(id);
|
|
|
|
args->response_size = sizeof(*r);
|
|
return ret;
|
|
}
|
|
DECLARE_HOST_COMMAND(EC_CMD_FP_INFO, fp_command_info, EC_VER_MASK(0));
|
|
|
|
static int fp_command_frame(struct host_cmd_handler_args *args)
|
|
{
|
|
#ifdef HAVE_PRIVATE
|
|
const struct ec_params_fp_frame *params = args->params;
|
|
void *out = args->response;
|
|
|
|
if (params->offset + params->size > sizeof(fp_buffer) ||
|
|
params->size > args->response_max)
|
|
return EC_RES_INVALID_PARAM;
|
|
|
|
memcpy(out, fp_buffer + params->offset, params->size);
|
|
|
|
args->response_size = params->size;
|
|
return EC_RES_SUCCESS;
|
|
#else
|
|
return EC_RES_UNAVAILABLE;
|
|
#endif
|
|
}
|
|
DECLARE_HOST_COMMAND(EC_CMD_FP_FRAME, fp_command_frame, EC_VER_MASK(0));
|