/** * ${ANDROID_BUILD_TOP}/vendor/focaltech/src/chips/ft9348.c * * Copyright (C) 2014-2017 FocalTech Systems Co., Ltd. All Rights Reserved. * **/ #include #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; }