unplugged-vendor/kernel-4.19/drivers/misc/mediatek/fingerprint/focaltech_ree/chip-ft9348.c

675 lines
19 KiB
C

/**
* ${ANDROID_BUILD_TOP}/vendor/focaltech/src/chips/ft9348.c
*
* Copyright (C) 2014-2017 FocalTech Systems Co., Ltd. All Rights Reserved.
*
**/
#include <linux/delay.h>
#include "ff_log.h"
#include "ff_err.h"
#include "ff_spi.h"
#include "ff_chip.h"
#undef LOG_TAG
#define LOG_TAG "focaltech:ft9348"
/*
* Protocol commands.
*/
#define FT9348_CMD_DMA_ENTER 0x06
#define FT9348_CMD_SFR_WRITE 0x09
#define FT9348_CMD_SFR_READ 0x08
#define FT9348_CMD_SRAM_WRITE 0x05
#define FT9348_CMD_SRAM_READ 0x04
#define FT9348_CMD_INFO_WRITE 0x10
#define FT9348_CMD_INFO_READ 0x11
#define FT9348_CMD_BOOT_WRITE 0x11
/*
* App info offset in XRAM.
*/
#define FT9348_INFO_OFFSET (0x0500 * 2)
/*
* SPI exchange buffer offset in XRAM.
*/
#define FT9348_XBUF_OFFSET (0x05c0 * 2)
/*
* Boot Info data structure.
* Note: Big-endian.
*/
typedef struct __attribute__((__packed__)) {
uint8_t e_WorkState;
uint8_t e_RstType;
uint16_t e_ChipId;
uint8_t e_VendorId;
uint8_t e_FactoryId;
uint8_t e_PromVersion;
uint8_t e_AppValid;
uint8_t e_UpgradeFlg;
uint8_t e_FlashType;
uint8_t e_FwType;
uint8_t e_CheckAppValid;
uint8_t e_Dummy0;
uint8_t e_Dummy1;
uint8_t e_Reserved[147];
uint8_t e_WO_SoftReset;
uint8_t e_WO_StartApp;
uint8_t e_Dummy3;
uint8_t e_WO_EnterUpgrade;
uint8_t e_WO_VerifyApp;
} ft9348_boot_info_t;
/*
* App Info data structure.
* Note: Big-endian.
*/
typedef struct __attribute__((__packed__)) {
uint8_t e_WorkState; // 0x00
uint8_t e_ImageBitsWide;
uint8_t e_LockAgc;
uint8_t e_Agc1;
uint8_t e_Agc2;
uint8_t e_Agc3;
uint8_t e_Agc4;
uint16_t e_DAC1;
uint16_t e_DAC2;
uint16_t e_DAC_Offset;
uint32_t e_SM_Threshold;
uint8_t e_NormalScanTimes;
uint8_t e_SM_ScanTimems;
uint8_t e_SM_TrigerBlkNum;
uint8_t e_SensorX; // 0x14
uint8_t e_SensorY;
uint16_t e_ChipId; // 0x16
uint8_t e_VendorId;
uint8_t e_FactoryId;
uint8_t e_FirmwareVersion; // 0x1a
uint8_t e_HighVoltageFlag;
uint8_t e_MpIntSetLowTime;
uint8_t e_FingerOnSensorFlag; // 0x1d
uint8_t e_AutoPowerAdjust; // 0x1e
uint8_t e_PlatformFeatureEn; // 0x1f
uint16_t e_McuFreeFlag; // 0x20
uint8_t e_AutoPowerTimeWdsD;
uint8_t e_AutoPowerTimeWdsU;
uint8_t e_FastSfrAddr;
uint8_t e_FastSfrValue;
uint8_t e_FastSfrRWFlag;
uint8_t e_OtpAddrR;
uint8_t e_OtpNumR;
uint8_t e_OtpReadEn;
uint8_t e_OtpBuff[10];
uint8_t e_InternalIoVcc;
uint8_t e_InternalIoVccLock;
uint8_t e_SMThresholdUpdateEn;
uint8_t e_AutoSetTeeFuntion;
uint8_t e_SMThresholdVirtTouch;
uint8_t e_SMThresholdVirtTouchDef;
uint16_t e_GestureStatus; // 0x3a
uint8_t e_AgcVersion; // 0x3c
uint8_t e_AutoPowerFastEn;
uint8_t e_FactoryIdleStopMode;
uint8_t e_ImageFingerFuntEn;
uint8_t e_TeeWorkMode; // 0x40
uint8_t e_SMScanRate;
uint8_t e_SmartSmStable; // 0x42
uint8_t e_GestureSupport;
uint8_t e_ImageEccEn;
uint16_t e_ImageEcc;
uint8_t e_SmSmartAreaBlkNum;
uint8_t e_SmSmartAreaContinueCnt;
uint8_t e_SmSmartAreaActionCnt;
uint8_t e_SmTriggerNum[2];
uint8_t e_SmTotalValue[8];
uint8_t e_FastEnterAPA; // 0x54
uint8_t e_Reserved[11];
uint16_t e_AppInfoAddr; // 0x60
uint16_t e_BadPixelThrUpper;
uint16_t e_BadPixelThrLower;
uint8_t e_BadPixelNum;
uint8_t e_RepairBadPixelEn;
uint8_t e_PollingScanEn;
uint8_t e_SpecialAldoSetEn;
uint8_t e_BadPixelAddrX[6];
uint8_t e_BadPixelAddrY[6];
uint8_t e_Dummy0;
uint8_t e_Dummy1;
uint8_t e_Dummy0_H;
uint8_t e_Dummy0_L;
uint8_t e_Dummy1_H;
uint8_t e_Dummy1_L;
} ft9348_app_info_t;
/*
* The singleton instance of 'ft9348_context_t'.
*/
typedef struct {
ff_device_t device;
ft9348_boot_info_t boot_info;
ft9348_app_info_t app_info;
ff_device_mode_t work_mode;
} ft9348_context_t;
static ft9348_context_t ft9348_context = {
.device.info.chip_id = 0x95a8,
.work_mode = FF_DEVICE_MODE_IDLE,
}, *g_context = &ft9348_context;
extern uint16_t fw93xx_chipid_get(void);
int ft9348_write_sfr(uint8_t addr, uint8_t data)
{
ff_sfr_buf_t tx_buf;
int err = FF_SUCCESS;
FF_LOGV("'%s' enter.", __func__);
tx_buf.cmd[0] = (uint8_t)( FT9348_CMD_SFR_WRITE);
tx_buf.cmd[1] = (uint8_t)(~FT9348_CMD_SFR_WRITE);
tx_buf.addr = addr;
tx_buf.tx_byte = data;
FF_CHECK_ERR(ff_spi_write_buf(&tx_buf, 4));
FF_LOGV("'%s' leave.", __func__);
return err;
}
int ft9348_read_sfr(uint8_t addr, uint8_t *data)
{
ff_sfr_buf_t tx_buf;
int err = FF_SUCCESS;
FF_LOGV("'%s' enter.", __func__);
tx_buf.cmd[0] = (uint8_t)( FT9348_CMD_SFR_READ);
tx_buf.cmd[1] = (uint8_t)(~FT9348_CMD_SFR_READ);
tx_buf.addr = addr;
FF_CHECK_ERR(ff_spi_write_then_read_buf(&tx_buf, 4, data, 1));
FF_LOGV("'%s' leave.", __func__);
return err;
}
int ft9348_write_sram(uint16_t addr, const void *data, uint16_t length)
{
int err = FF_SUCCESS, remain = length, tx_len;
int dlen, stride = MAX_XFER_BUF_SIZE - sizeof(ff_sram_buf_t);
bool b_in_pram = (addr == 0x0000);
uint16_t offset = addr;
static uint8_t tx_buffer[MAX_XFER_BUF_SIZE] = {0, };
ff_sram_buf_t *tx_buf = TYPE_OF(ff_sram_buf_t, tx_buffer);
uint8_t *p_data = TYPE_OF(uint8_t, data);
FF_LOGV("'%s' enter.", __func__);
/* Sign the package. */
tx_buf->cmd[0] = (uint8_t)( FT9348_CMD_SRAM_WRITE);
tx_buf->cmd[1] = (uint8_t)(~FT9348_CMD_SRAM_WRITE);
/* Write it repeatedly. */
while (remain > 0) {
/* The last package? */
if (remain < stride) {
stride = remain;
}
/* HW specific protocol. */
addr = b_in_pram ? (offset / 2) : 0x8000 | (offset / 2);
dlen = stride / 2 - 1;
if (dlen < 0) {
dlen = 0;
}
/* Packing. */
tx_buf->addr = u16_swap_endian(addr);
tx_buf->dlen = u16_swap_endian(dlen);
tx_len = sizeof(ff_sram_buf_t) + stride;
memcpy(tx_buf->data, p_data, stride);
/* Low-level transfer. */
FF_CHECK_ERR(ff_spi_write_buf(tx_buf, tx_len));
/* Next package. */
offset += stride;
p_data += stride;
remain -= stride;
}
FF_LOGV("'%s' leave.", __func__);
return err;
}
int ft9348_read_sram(uint16_t addr, void *data, uint16_t length)
{
int err = FF_SUCCESS, remain = length, tx_len;
int dlen, stride = MAX_XFER_BUF_SIZE - sizeof(ff_sram_buf_t);
bool b_in_pram = (addr == 0x0000);
uint16_t offset = addr;
ff_sram_buf_t tx_buffer, *tx_buf = &tx_buffer;
uint8_t *p_data = TYPE_OF(uint8_t, data);
FF_LOGV("'%s' enter.", __func__);
/* Sign the package. */
tx_buf->cmd[0] = (uint8_t)( FT9348_CMD_SRAM_READ);
tx_buf->cmd[1] = (uint8_t)(~FT9348_CMD_SRAM_READ);
/* Read it repeatedly. */
while (remain > 0) {
/* The last package? */
if (remain < stride) {
stride = remain;
}
/* HW specific protocol. */
addr = b_in_pram ? (offset / 2) : 0x8000 | (offset / 2);
dlen = stride / 2 - 1;
if (dlen < 0) {
dlen = 0;
}
/* Packing. */
tx_buf->addr = u16_swap_endian(addr);
tx_buf->dlen = u16_swap_endian(dlen);
tx_len = sizeof(ff_sram_buf_t);
/* Low-level transfer. */
FF_CHECK_ERR(ff_spi_write_then_read_buf(tx_buf, tx_len, p_data, stride));
/* Next package. */
offset += stride;
p_data += stride;
remain -= stride;
}
FF_LOGV("'%s' leave.", __func__);
return err;
}
int ft9348_write_inf(uint8_t addr, uint8_t data)
{
ff_info_buf_t info;
int err = FF_SUCCESS;
FF_LOGV("'%s' enter.", __func__);
/* 2-1: Write the info parameters. */
info.cmd[0] = (uint8_t)( FT9348_CMD_INFO_WRITE);
info.cmd[1] = (uint8_t)(~FT9348_CMD_INFO_WRITE);
info.addr = addr;
info.dlen = data;
err = ft9348_write_sram(FT9348_XBUF_OFFSET, &info, sizeof(ff_info_buf_t));
FF_CHECK_ERR(err);
/* 2-2: Triger the operation. */
FF_CHECK_ERR(ft9348_write_sfr(0xa4, 0x01));
FF_LOGV("'%s' leave.", __func__);
return err;
}
static int ft9348_write_info(uint8_t addr, uint8_t data, bool in_boot)
{
ff_info_buf_t info;
int err = FF_SUCCESS;
FF_LOGV("'%s' enter.", __func__);
/* 2-1: Write the info parameters. */
if (in_boot) {
info.cmd[0] = (uint8_t)( FT9348_CMD_BOOT_WRITE);
info.cmd[1] = (uint8_t)(~FT9348_CMD_BOOT_WRITE);
} else {
info.cmd[0] = (uint8_t)( FT9348_CMD_INFO_WRITE);
info.cmd[1] = (uint8_t)(~FT9348_CMD_INFO_WRITE);
}
info.addr = in_boot ? (addr | 0x80) : addr;
info.dlen = data;
err = ft9348_write_sram(FT9348_XBUF_OFFSET, &info, sizeof(ff_info_buf_t));
FF_CHECK_ERR(err);
/* 2-2: Triger the operation. */
FF_CHECK_ERR(ft9348_write_sfr(0xa4, 0x01));
FF_LOGV("'%s' leave.", __func__);
return err;
}
static int ft9348_read_info_indirectly(uint8_t offset, void *data, uint8_t dlen)
{
ff_info_buf_t info;
int err = FF_SUCCESS;
FF_LOGV("'%s' enter.", __func__);
/* 3-1: Write the info parameters. */
info.cmd[0] = (uint8_t)( FT9348_CMD_INFO_READ);
info.cmd[1] = (uint8_t)(~FT9348_CMD_INFO_READ);
info.addr = offset;
info.dlen = dlen;
err = ft9348_write_sram(FT9348_XBUF_OFFSET, &info, sizeof(ff_info_buf_t));
FF_CHECK_ERR(err);
/* 3-2: Triger the operation. */
FF_CHECK_ERR(ft9348_write_sfr(0xa4, 0x01));
/* 3-3: Read out the result. */
FF_CHECK_ERR(ft9348_read_sram(FT9348_XBUF_OFFSET, data, dlen));
FF_LOGV("'%s' leave.", __func__);
return err;
}
/* Note: APP space only. */
static int ft9348_read_info_directly(uint8_t offset, void *data, uint8_t dlen)
{
uint16_t addr = FT9348_INFO_OFFSET + offset;
int err = FF_SUCCESS;
FF_LOGV("'%s' enter.", __func__);
// FIXME: Reading from an odd offset.
if (addr % 2) {
data = ((uint8_t *)data) - 1;
dlen += 1;
}
FF_CHECK_ERR(ft9348_read_sram(addr, data, dlen));
FF_LOGV("'%s' leave.", __func__);
return err;
}
/*
* Note: Only 1 byte can be written.
*/
#define ft9348_write_boot_info(member) \
do { \
uint8_t offset = (uint8_t)((void *)&member - (void *)&g_context->boot_info); \
FF_CHECK_ERR(ft9348_write_info(offset, member, true)); \
} while (0)
/*
* Note: Only 2 bytes can be read.
*/
#define ft9348_read_boot_info(member) \
do { \
uint8_t offset = (uint8_t)((void *)&member - (void *)&g_context->boot_info); \
uint8_t length = (uint8_t)sizeof(member); \
FF_CHECK_ERR(ft9348_read_info_indirectly(offset, &member, length)); \
} while (0)
#define ft9348_write_app_info(member) \
do { \
uint8_t offset = (uint8_t)((void *)&member - (void *)&g_context->app_info); \
FF_CHECK_ERR(ft9348_write_info(offset, member, false)); \
} while (0)
#define ft9348_read_app_info_indirectly(member) \
do { \
uint8_t offset = (uint8_t)((void *)&member - (void *)&g_context->app_info); \
uint8_t length = (uint8_t)sizeof(member); \
FF_CHECK_ERR(ft9348_read_info_indirectly(offset, &member, length)); \
} while (0)
#define ft9348_read_app_info_directly(member) \
do { \
uint8_t offset = (uint8_t)((void *)&member - (void *)&g_context->app_info); \
uint8_t length = (uint8_t)sizeof(member); \
FF_CHECK_ERR(ft9348_read_info_directly(offset, &member, length)); \
} while (0)
/*---8<-------------------------------------------------------------------------*/
int ft9348_query_event_status(void)
{
int err = FF_SUCCESS;
FF_LOGV("'%s' enter.", __func__);
// TODO:
UNUSED_VAR(err);
FF_LOGV("'%s' leave.", __func__);
return err;
}
int ft9348_query_device_status(void)
{
ff_device_status_t stat = FF_DEVICE_STAT_IDLE;
FF_LOGV("'%s' enter.", __func__);
ft9348_read_app_info_directly(g_context->app_info.e_McuFreeFlag);
if (g_context->app_info.e_McuFreeFlag != 0x5aa5) {
stat = FF_DEVICE_STAT_BUSY;
}
FF_LOGV("'%s' leave. ", __func__);
return stat;
}
int ft9348_query_finger_status(void)
{
int err = FF_SUCCESS;
FF_LOGV("'%s' enter.", __func__);
// TODO:
UNUSED_VAR(err);
FF_LOGV("'%s' leave.", __func__);
return err;
}
int ft9348_query_gesture_status(void)
{
FF_LOGV("'%s' enter.", __func__);
ft9348_read_app_info_directly(g_context->app_info.e_GestureStatus);
FF_LOGD("fw gesture_code = 0x%x.", g_context->app_info.e_GestureStatus);
FF_LOGV("'%s' leave.", __func__);
return (int)g_context->app_info.e_GestureStatus;
}
int ft9348_check_alive(void)
{
int err = FF_SUCCESS;
FF_LOGV("'%s' enter.", __func__);
ft9348_read_app_info_directly(g_context->app_info.e_AutoPowerAdjust);
if (g_context->app_info.e_AutoPowerAdjust != 0x01) {
return FF_ERR_DEAD_DEVICE;
}
FF_LOGV("'%s' leave.", __func__);
return err;
}
static int ft9348_init_sleep_mode(void)
{
ff_device_status_t stat = FF_DEVICE_STAT_BUSY;
int err = FF_SUCCESS, tries = 50;
FF_LOGV("'%s' enter.", __func__);
/* TODO: Algorithm has wrote these two info registers already. */
g_context->app_info.e_PlatformFeatureEn = 0x01; // 0x1f
ft9348_write_app_info(g_context->app_info.e_PlatformFeatureEn);
g_context->app_info.e_TeeWorkMode = 0x01; // 0x40
ft9348_write_app_info(g_context->app_info.e_TeeWorkMode);
g_context->app_info.e_AutoPowerAdjust = 0x01; // 0x1e
ft9348_write_app_info(g_context->app_info.e_AutoPowerAdjust);
/* Wait for the device to be IDLE state. */
do {
mdelay(3);
stat = ft9348_query_device_status();
} while (stat != FF_DEVICE_STAT_IDLE && --tries);
FF_LOGV("tries remainder = %d", tries);
/* INT pin shall pull down after reading the 0x1d info. */
ft9348_read_app_info_indirectly(g_context->app_info.e_FingerOnSensorFlag);
/* Verify that the device has been in the sensor mode. */
stat = ft9348_query_device_status();
if (stat != FF_DEVICE_STAT_BUSY) {
FF_LOGE("failed to init sensor mode.");
err = FF_ERR_INTERNAL;
}
FF_LOGV("'%s' leave.", __func__);
return err;
}
int ft9348_config_power_mode(ff_power_mode_t mode)
{
int err = FF_SUCCESS, tries = 10;
FF_LOGV("'%s' enter.", __func__);
switch (mode) {
case FF_POWER_MODE_WAKEUP: {
ff_device_status_t stat = ft9348_query_device_status();
if (stat != FF_DEVICE_STAT_IDLE) {
/* Try to wakeup the device. */
do {
ff_sfr_buf_t tx_buf;
tx_buf.cmd[0] = 0x70;
FF_CHECK_ERR(ff_spi_write_buf(&tx_buf, 1));
mdelay(5);
FF_CHECK_ERR(ff_spi_write_buf(&tx_buf, 1));
mdelay(20);
stat = ft9348_query_device_status();
} while (stat != FF_DEVICE_STAT_IDLE && --tries);
FF_LOGV("tries remainder = %d", tries);
if (stat == FF_DEVICE_STAT_BUSY) {
FF_LOGE("can't wake up the device.");
FF_CHECK_ERR(FF_ERR_BUSY);
}
}
break;
}
case FF_POWER_MODE_INIT_SLEEP: {
FF_FAILURE_RETRY(ft9348_init_sleep_mode(), 3);
break;
}
case FF_POWER_MODE_AUTO_SLEEP: {
g_context->app_info.e_WorkState = 0x00;
ft9348_write_app_info(g_context->app_info.e_WorkState);
g_context->app_info.e_FastEnterAPA = 0x01;
ft9348_write_app_info(g_context->app_info.e_FastEnterAPA);
break;
}
case FF_POWER_MODE_DEEP_SLEEP:
FF_LOGI("switch to 'FF_POWER_MODE_DEEP_SLEEP' mode.");
err = ft9348_config_power_mode(FF_POWER_MODE_WAKEUP);
g_context->app_info.e_WorkState = 0x73;
ft9348_write_app_info(g_context->app_info.e_WorkState);
break;
case FF_POWER_MODE_LOST_POWER:
// TODO:
break;
default:
FF_LOGW("unknown power mode.");
break;
}
FF_LOGV("'%s' leave.", __func__);
return err;
}
int ft9348_config_device_mode(ff_device_mode_t mode)
{
const char *hint = "switch to '%s' mode.";
int err = FF_SUCCESS;
FF_LOGV("'%s' enter.", __func__);
switch (mode) {
#if 0
case FF_DEVICE_MODE_IDLE: {
err = ft9348_config_power_mode(FF_POWER_MODE_WAKEUP);
/* Enter deep sleep mode instead of idle mode. */
err = ft9348_config_power_mode(FF_POWER_MODE_DEEP_SLEEP);
break;
}
#endif
case FF_DEVICE_MODE_SCAN_IMAGE: {
FF_LOGI(hint, "FF_DEVICE_MODE_SCAN_IMAGE");
// FIXME: There is no this mode for ft9348.
break;
}
case FF_DEVICE_MODE_GESTURE: {
// TODO:
break;
}
case FF_DEVICE_MODE_WAIT_TOUCH:
case FF_DEVICE_MODE_WAIT_LEAVE:
case FF_DEVICE_MODE_WAIT_IMAGE:
case FF_DEVICE_MODE_IDLE:
default:
FF_LOGI(hint, "FF_DEVICE_MODE_SENSOR");
err = ft9348_config_power_mode(FF_POWER_MODE_WAKEUP);
err = ft9348_config_power_mode(FF_POWER_MODE_AUTO_SLEEP);
break;
}
g_context->work_mode = mode;
FF_LOGV("'%s' leave.", __func__);
return err;
}
/*---8<-----------------------------------------------------------------------*/
/* See plat-xxxx.c for platform dependent implementation. */
extern int ff_ctl_reset_device(void);
int ft9348_hw_reset(void)
{
ff_sfr_buf_t tx_buf;
int err = FF_SUCCESS;
FF_LOGV("'%s' enter.", __func__);
/* 3-1: HW reset. */
err = ff_ctl_reset_device();
/* 3-2: Wait PROM ready. */
mdelay(20);
/* 3-3: Enter SPI DMA mode. */
tx_buf.cmd[0] = (uint8_t)( FT9348_CMD_DMA_ENTER);
tx_buf.cmd[1] = (uint8_t)(~FT9348_CMD_DMA_ENTER);
tx_buf.addr = 0x00;
err = ff_spi_write_buf(&tx_buf, 3);
if (err) {
FF_LOGE("failed to enter DMA mode.");
FF_CHECK_ERR(err);
}
FF_LOGV("'%s' leave.", __func__);
return err;
}
/*---8<-----------------------------------------------------------------------*/
int ff_chip_init(void)
{
uint16_t device_id = 0x0000;
int err = FF_SUCCESS;
FF_LOGV("'%s' enter.", __func__);
/* Try another way: Reset to PROM space and read boot info. */
FF_LOGD("try to read the chip id in bootloader...");
FF_CHECK_ERR(ft9348_hw_reset());
mdelay(26);
#if NEED_READ_CHIPID
device_id = fw93xx_chipid_get();
FF_LOGD("chip id is = : 0x%04x ", device_id);
return device_id;
#endif
/* Sensor dimensions. */
ft9348_read_app_info_directly(g_context->app_info.e_SensorX);
ft9348_read_app_info_directly(g_context->app_info.e_SensorY);
FF_LOGI("sensor resolution: %d x %d.", g_context->app_info.e_SensorX, g_context->app_info.e_SensorY);
/* Enter SensorMode. */
FF_CHECK_ERR(ft9348_config_power_mode(FF_POWER_MODE_INIT_SLEEP));
FF_CHECK_ERR(ft9348_config_power_mode(FF_POWER_MODE_AUTO_SLEEP));
FF_LOGV("'%s' leave.", __func__);
return err;
}