/* 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 #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; /* Initial status */ *error_state &= 0xFC00; 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; } void __unused elan_sensor_set_rst(bool state) { gpio_set_level(GPIO_FP_RST_ODL, state ? 0 : 1); }