Files
OpenCellular/driver/fpc1140.c
Vincent Palatin 4ce20f3f79 fpc1140: add the finger capture init sequence
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>
2017-05-23 05:57:14 -07:00

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));