diff options
author | Chiehchun Yu <chiehchun.yu@elan.corp-partner.google.com> | 2020-01-16 14:44:50 +0800 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2021-01-12 20:47:52 +0000 |
commit | 3775fdbf7e863603eb65ec52348a45d7139db628 (patch) | |
tree | 9b9bc902067543a51db1d7772e348a2a15f9e437 /driver/fingerprint/elan/elan_sensor_pal.c | |
parent | 4167ccc6fc40b865d5eb9f6e13b7a95f9aba0f19 (diff) | |
download | chrome-ec-3775fdbf7e863603eb65ec52348a45d7139db628.tar.gz |
elan_private: Implement elan sensor driver
This patch provides ELAN FP APIs that used to control ELAN FP sensor
and matching algorithm.
BRANCH=None
BUG=None
TEST=We build on nami_fp, and testing Elan sensor with libelan_515.a
and libelan_80.a successfully.
Change-Id: I7a58025106f8ed570860b758323bf2047cde0731
Signed-off-by: Chiehchun Yu <chiehchun.yu@elan.corp-partner.google.com>
Signed-off-by: herman lin <herman.lin@emc.com.tw>
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2002994
Reviewed-by: Yicheng Li <yichengli@chromium.org>
Tested-by: Yicheng Li <yichengli@chromium.org>
Commit-Queue: Yicheng Li <yichengli@chromium.org>
Diffstat (limited to 'driver/fingerprint/elan/elan_sensor_pal.c')
-rw-r--r-- | driver/fingerprint/elan/elan_sensor_pal.c | 246 |
1 files changed, 246 insertions, 0 deletions
diff --git a/driver/fingerprint/elan/elan_sensor_pal.c b/driver/fingerprint/elan/elan_sensor_pal.c new file mode 100644 index 0000000000..0c8827d684 --- /dev/null +++ b/driver/fingerprint/elan/elan_sensor_pal.c @@ -0,0 +1,246 @@ +/* Copyright 2021 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. + */ +/* ELAN Platform Abstraction Layer callbacks */ + +#include <stddef.h> +#include "common.h" +#include "console.h" +#include "endian.h" +#include "fpsensor.h" +#include "gpio.h" +#include "link_defs.h" +#include "spi.h" +#include "system.h" +#include "timer.h" +#include "util.h" +#include "shared_mem.h" +#include "math_util.h" +#include "cryptoc/util.h" + +#include "elan_setting.h" +#include "elan_sensor.h" +#include "elan_sensor_pal.h" + +static uint8_t tx_buf[CONFIG_SPI_TX_BUF_SIZE] __uncached; +static uint8_t rx_buf[CONFIG_SPI_RX_BUF_SIZE] __uncached; + +int elan_write_cmd(uint8_t fp_cmd) +{ + int rc = 0; + + memset(tx_buf, 0, CONFIG_SPI_TX_BUF_SIZE); + memset(rx_buf, 0, CONFIG_SPI_RX_BUF_SIZE); + + tx_buf[0] = fp_cmd; + rc = spi_transaction(&spi_devices[0], tx_buf, 2, rx_buf, + SPI_READBACK_ALL); + return rc; +} + +int elan_read_cmd(uint8_t fp_cmd, uint8_t *regdata) +{ + int ret = 0; + + memset(tx_buf, 0, CONFIG_SPI_TX_BUF_SIZE); + memset(rx_buf, 0, CONFIG_SPI_RX_BUF_SIZE); + + tx_buf[0] = fp_cmd; /* one byte data read */ + ret = spi_transaction(&spi_devices[0], tx_buf, 2, rx_buf, + SPI_READBACK_ALL); + *regdata = rx_buf[1]; + + return ret; +} + +int elan_spi_transaction(uint8_t *tx, int tx_len, uint8_t *rx, int rx_len) +{ + int ret = 0; + + memset(tx_buf, 0, CONFIG_SPI_TX_BUF_SIZE); + memset(rx_buf, 0, CONFIG_SPI_RX_BUF_SIZE); + + memcpy(tx_buf, tx, tx_len); + ret = spi_transaction(&spi_devices[0], tx_buf, tx_len, rx_buf, rx_len); + memcpy(rx, rx_buf, rx_len); + + return ret; +} + +int elan_write_register(uint8_t regaddr, uint8_t regdata) +{ + int ret = 0; + + memset(tx_buf, 0, CONFIG_SPI_TX_BUF_SIZE); + memset(rx_buf, 0, CONFIG_SPI_RX_BUF_SIZE); + + tx_buf[0] = WRITE_REG_HEAD + regaddr; /* one byte data write */ + tx_buf[1] = regdata; + ret = spi_transaction(&spi_devices[0], tx_buf, 2, rx_buf, + SPI_READBACK_ALL); + return ret; +} + +int elan_write_page(uint8_t page) +{ + int ret = 0; + + memset(tx_buf, 0, CONFIG_SPI_TX_BUF_SIZE); + memset(rx_buf, 0, CONFIG_SPI_RX_BUF_SIZE); + + tx_buf[0] = PAGE_SEL; + tx_buf[1] = page; + ret = spi_transaction(&spi_devices[0], tx_buf, 2, rx_buf, + SPI_READBACK_ALL); + + return ret; +} + +int elan_write_reg_vector(const uint8_t *reg_table, int length) +{ + int ret = 0; + int i = 0; + uint8_t write_regaddr; + uint8_t write_regdata; + + for (i = 0; i < length; i = i + 2) { + write_regaddr = reg_table[i]; + write_regdata = reg_table[i + 1]; + ret = elan_write_register(write_regaddr, write_regdata); + if (ret < 0) + break; + } + return ret; +} + +int raw_capture(uint16_t *short_raw) +{ + int ret = 0, i = 0, image_index = 0, index = 0; + int cnt_timer = 0; + int dma_loop = 0, dma_len = 0; + uint8_t regdata[4] = { 0 }; + char *img_buf; + + memset(short_raw, 0, sizeof(uint16_t) * IMAGE_TOTAL_PIXEL); + + ret = shared_mem_acquire(sizeof(uint8_t) * IMG_BUF_SIZE, &img_buf); + if (ret) { + LOGE_SA("%s Can't get shared mem\n", __func__); + return ret; + } + memset(img_buf, 0, sizeof(uint8_t) * IMG_BUF_SIZE); + + /* Write start scans command to fp sensor */ + if (elan_write_cmd(START_SCAN) < 0) { + ret = ELAN_ERROR_SPI; + LOGE_SA("%s SPISendCommand( SSP2, START_SCAN ) fail ret = %d", + __func__, ret); + goto exit; + } + + /* Polling scan status */ + cnt_timer = 0; + while (1) { + usleep(1000); + cnt_timer++; + regdata[0] = SENSOR_STATUS; + elan_spi_transaction(regdata, 2, regdata, 2); + if (regdata[0] & 0x04) + break; + + if (cnt_timer > POLLING_SCAN_TIMER) { + ret = ELAN_ERROR_SCAN; + LOGE_SA("%s regdata = 0x%x, fail ret = %d", __func__, + regdata[0], ret); + goto exit; + } + } + + /* Read the image from fp sensor */ + dma_loop = 4; + dma_len = IMG_BUF_SIZE / dma_loop; + + for (i = 0; i < dma_loop; i++) { + memset(tx_buf, 0, CONFIG_SPI_TX_BUF_SIZE); + memset(rx_buf, 0, CONFIG_SPI_RX_BUF_SIZE); + tx_buf[0] = START_READ_IMAGE; + ret = spi_transaction(&spi_devices[0], tx_buf, 2, rx_buf, + dma_len); + memcpy(&img_buf[dma_len * i], rx_buf, dma_len); + } + + /* Remove dummy byte */ + for (image_index = 1; image_index < IMAGE_WIDTH; image_index++) + memcpy(&img_buf[RAW_PIXEL_SIZE * image_index], + &img_buf[RAW_DATA_SIZE * image_index], RAW_PIXEL_SIZE); + + for (index = 0; index < IMAGE_TOTAL_PIXEL; index++) + short_raw[index] = + (img_buf[index * 2] << 8) + img_buf[index * 2 + 1]; + +exit: + if (img_buf != NULL) { + always_memset(img_buf, 0, sizeof(uint8_t) * IMG_BUF_SIZE); + shared_mem_release(img_buf); + } + + if (ret != 0) + LOGE_SA("%s error = %d", __func__, ret); + + return ret; +} + +int elan_execute_calibration(void) +{ + int retry_time = 0; + int ret = 0; + + while (retry_time < REK_TIMES) { + elan_write_cmd(SRST); + elan_write_cmd(FUSE_LOAD); + register_initialization(); + elan_sensing_mode(); + + ret = calibration(); + if (ret == 0) + break; + + retry_time++; + } + + return ret; +} + +int elan_fp_maintenance(uint16_t *error_state) +{ + int rv; + fp_sensor_info_t sensor_info; + timestamp_t start = get_time(); + + if (error_state == NULL) + return EC_ERROR_INVAL; + + sensor_info.num_defective_pixels = 0; + sensor_info.sensor_error_code = 0; + rv = fp_sensor_maintenance(&sensor_info); + LOGE_SA("Maintenance took %d ms", time_since32(start) / MSEC); + + if (rv != 0) { + /* + * Failure can occur if any of the fingerprint detection zones + * are covered (i.e., finger is on sensor). + */ + LOGE_SA("Failed to run maintenance: %d", rv); + return EC_ERROR_HW_INTERNAL; + } + if (sensor_info.num_defective_pixels >= FP_ERROR_DEAD_PIXELS_UNKNOWN) + *error_state = FP_ERROR_DEAD_PIXELS_UNKNOWN; + else + *error_state |= + FP_ERROR_DEAD_PIXELS(sensor_info.num_defective_pixels); + LOGE_SA("num_defective_pixels: %d", sensor_info.num_defective_pixels); + LOGE_SA("sensor_error_code: %d", sensor_info.sensor_error_code); + + return EC_SUCCESS; +} |