From 3cbcb9e39b8eb4157aaa038ebbc6b0d1d656d347 Mon Sep 17 00:00:00 2001 From: hyperion70 Date: Sat, 2 Feb 2019 22:21:36 +0300 Subject: [PATCH] Upd als_ps | add gx556 --- arch/arm64/configs/N1_defconfig | 8 +- drivers/input/fingerprint/Kconfig | 22 +- drivers/input/fingerprint/gx556/Kconfig | 7 + drivers/input/fingerprint/gx556/Makefile | 13 + drivers/input/fingerprint/gx556/gf_spi.c | 1671 +++ drivers/input/fingerprint/gx556/gf_spi.h | 179 + drivers/input/fingerprint/gx556/platform.c | 325 + .../misc/mediatek/alsps/stk3x1x-new/stk3x1x.c | 8982 +++++++++++------ .../misc/mediatek/alsps/stk3x1x-new/stk3x1x.h | 36 +- kernel/Kconfig.locks | 2 +- 10 files changed, 8142 insertions(+), 3103 deletions(-) create mode 100755 drivers/input/fingerprint/gx556/Kconfig create mode 100755 drivers/input/fingerprint/gx556/Makefile create mode 100755 drivers/input/fingerprint/gx556/gf_spi.c create mode 100755 drivers/input/fingerprint/gx556/gf_spi.h create mode 100755 drivers/input/fingerprint/gx556/platform.c diff --git a/arch/arm64/configs/N1_defconfig b/arch/arm64/configs/N1_defconfig index b675f520b..ecf4189e2 100755 --- a/arch/arm64/configs/N1_defconfig +++ b/arch/arm64/configs/N1_defconfig @@ -52,6 +52,7 @@ CONFIG_XFRM_SUB_POLICY=y CONFIG_XFRM_MIGRATE=y CONFIG_NET_KEY=y CONFIG_IKCONFIG_PROC=y +CONFIG_IKCONFIG=y CONFIG_INET=y CONFIG_IP_MULTICAST=y CONFIG_IP_ADVANCED_ROUTER=y @@ -562,9 +563,9 @@ CONFIG_MTK_MEMCFG=y CONFIG_CUSTOM_KERNEL_IMGSENSOR="imx258_mipi_raw imx258_mipi_mono s5k3p3sx_mipi_raw" CONFIG_MTK_FINGERPRINT_SUPPORT=y -CONFIG_GOODIX_FINGERPRINT=y -#CONFIG_GX556_FINGERPRINT=y -#CONFIG_MTK_HALL_SUPPORT=y +#CONFIG_GOODIX_FINGERPRINT=y +CONFIG_GX556_FINGERPRINT=y +CONFIG_MTK_HALL_SUPPORT=y # CONFIG_TOUCHSCREEN_MTK_GT1151=y CONFIG_TOUCHSCREEN_MTK_SYNAPTICS=y @@ -579,4 +580,5 @@ CONFIG_DEBUG_KMEMLEAK=y CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=3200 CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y +CONFIG_MTK_RCU_MONITOR=y # [hyperion70 add end] diff --git a/drivers/input/fingerprint/Kconfig b/drivers/input/fingerprint/Kconfig index 2e19df025..24a7ab608 100644 --- a/drivers/input/fingerprint/Kconfig +++ b/drivers/input/fingerprint/Kconfig @@ -13,31 +13,17 @@ config MTK_FINGERPRINT_SUPPORT fingerprint driver will support fingerprint function if MTK_FINGERPRINT_SUPPORT -config MTK_FINGERPRINT_SELECT - string "sensor type" - default "GX556" +config GX556_FINGERPRINT + tristate "goodix gx556 Fingerprint" + default n -if MTK_FINGERPRINT_SELECT = "FPC1145" -config FPC_FINGERPRINT - bool "FPC Fingerprint" - default y - ---help--- - FPC fingerprint FPC's 102x/104x device. -endif -if MTK_FINGERPRINT_SELECT = "GX556" || MTK_FINGERPRINT_SELECT = "GF5216" config GOODIX_FINGERPRINT bool "Goodix Fingerprint" - default y + default n ---help--- Goodix Fingerprint chip GF316M/GX556/GF3118M/GF518M/GF5118M/GF516M/GF816M/GF3208/GF3206/GF3266/GF3288/GF5206/GF5216/GF5208 TEE driver -config GOODIX_SENSOR_TYPE - string "SENSOR_TYPE" - default "GF318M" - ---help--- - Must input sensor type, or default is GF316M GF318M GF3118M GX556 GF5118M GF516M GF816M GF3208 GF5216 -endif endif endmenu diff --git a/drivers/input/fingerprint/gx556/Kconfig b/drivers/input/fingerprint/gx556/Kconfig new file mode 100755 index 000000000..651512aa9 --- /dev/null +++ b/drivers/input/fingerprint/gx556/Kconfig @@ -0,0 +1,7 @@ +config FINGERPRINT_GT556 + bool "gf5216(goodix fingerprint sensor) for dewav" + default n + help + It support different type sensor + in this platform. If this option + is set, it will support fingerprint gf3208 silead sensor . diff --git a/drivers/input/fingerprint/gx556/Makefile b/drivers/input/fingerprint/gx556/Makefile new file mode 100755 index 000000000..dbde40cdf --- /dev/null +++ b/drivers/input/fingerprint/gx556/Makefile @@ -0,0 +1,13 @@ +#include $(srctree)/drivers/misc/mediatek/Makefile.custom +# Makefile for the fingerprint device. +ccflags-y += -I$(srctree)/drivers/spi/mediatek/$(MTK_PLATFORM) +ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/mt-plat +ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include +subdir-ccflags-y += -Werror +subdir-ccflags-y += -I$(srctree)/drivers/misc/mediatek/include +subdir-ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include +subdir-ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/mt-plat +subdir-ccflags-y += -I$(srctree)/drivers/spi/mediatek/$(MTK_PLATFORM) +subdir-ccflags-y += -I$(srctree)/drivers/misc/mediatek/base/power/mt6735/ + +obj-y += gf_spi.o platform.o diff --git a/drivers/input/fingerprint/gx556/gf_spi.c b/drivers/input/fingerprint/gx556/gf_spi.c new file mode 100755 index 000000000..48a30f0cd --- /dev/null +++ b/drivers/input/fingerprint/gx556/gf_spi.c @@ -0,0 +1,1671 @@ + /*Simple synchronous userspace interface to SPI devices + * + * Copyright (C) 2006 SWAPP + * Andrea Paterniani + * Copyright (C) 2007 David Brownell (simplification, cleanup) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "gf_spi.h" +//#include "../fp_drv/fp_drv.h" +#include //xiaowen + +#define GF_SPIDEV_NAME "goodix,fingerprint" +/*device name after register in charater*/ +#define GF_DEV_NAME "goodix_fp" +#define GF_INPUT_NAME "qwerty" /*"goodix_fp" */ +#define SPI_DEV_NAME "spidev" + +#define CHRD_DRIVER_NAME "goodix_fp_spi" +#define CLASS_NAME "goodix_fp" +#define SPIDEV_MAJOR 225 /* assigned */ +#define N_SPI_MINORS 32 /* ... up to 256 */ + +/*GF input keys*/ +#define GF_KEY_POWER KEY_POWER +#define GF_KEY_HOME KEY_HOME +#define GF_KEY_MENU KEY_MENU +#define GF_KEY_BACK KEY_BACK +#define GF_UP_KEY KEY_UP +#define GF_DOWN_KEY KEY_DOWN +#define GF_LEFT_KEY KEY_LEFT +#define GF_RIGHT_KEY KEY_RIGHT +#define GF_KEY_FORCE KEY_F9 +#define GF_APP_SWITCH KEY_F19 + +#define CRC_16_POLYNOMIALS 0x8005 +/**************************debug******************************/ +/*Global variables*/ +/*static MODE g_mode = GF_IMAGE_MODE;*/ +static DECLARE_BITMAP(minors, N_SPI_MINORS); +static LIST_HEAD(device_list); +static DEFINE_MUTEX(device_list_lock); +static DECLARE_WAIT_QUEUE_HEAD(gf_poll_wq); + +#define GF_DATA_ADDR (0x5202) + +//value for GF_STATUS(0x0837) register +#define GF_INT_ERR_FW (0xE0) +#define GF_INT_ERR_CFG (0xE1) +#define GF_INT_ERR_ESD (0xE3) +#define GF_INT_KEY (0xB0) +#define GF_INT_KEY_LEFT (0xB1) +#define GF_INT_KEY_RIGHT (0xB2) +#define GF_INT_KEY_LIGHT (0xB5) +#define GF_INT_KEY_WEIGHT (0xB6) +#define GF_INT_IMAGE (0xC0) +#define GF_INT_GSC (0xC1) +#define GF_INT_HBD (0xA0) +#define GF_INT_INVALID (0x00) + +#define GF_ROW_SIZE (2 + SENSOR_COL*2 +2) +#define GF_ROW_DATA_SIZE (SENSOR_COL*2) +#define GF_READ_BUFF_SIZE1 (8 * GF_ROW_SIZE) +#define GF_READ_BUFF_SIZE2 (7 * GF_ROW_SIZE) +#define GF_SPI_DATA_LEN (SENSOR_ROW * GF_ROW_SIZE) //valid data len + +#define BUF_SIZE (GF_SPI_DATA_LEN + 2048 ) + +static int gf_probe(struct spi_device *spi); +static int gf_remove(struct spi_device *spi); +//static int gf_suspend(struct spi_device *spi, pm_message_t mesg); +//static int gf_resume(struct spi_device *spi); +static int gf_spi_transfer(struct gf_dev *gf_dev, unsigned long arg); +static ssize_t gf_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos); +int gf_spi_write_word(struct gf_dev* gf_dev, u16 addr, u16 value); +static int ftm_gfx_irq_state = 0; +static int ftm_gfx_irq_key = 0; + +static struct gf_dev gf; +static struct class *gf_class; + +static struct mt_chip_conf spi_conf_mt65xx = { + .setuptime = 15, + .holdtime = 15, + .high_time = 21, + .low_time = 21, + .cs_idletime = 20, + .ulthgh_thrsh = 0, + .cpol = 0, + .cpha = 0, + .rx_mlsb = 1, + .tx_mlsb = 1, + .tx_endian = 0, + .rx_endian = 0, + .com_mod = FIFO_TRANSFER, + .pause = 0, + .finish_intr = 1, + .deassert = 0, + .ulthigh = 0, + .tckdly = 0, +}; + +/* +static const struct dev_pm_ops gx_pm = { + .suspend = gf_suspend_test, + .resume = gf_resume_test +}; +*/ +static struct of_device_id gx_match_table[] = { + { .compatible = "mediatek,fingerprint", }, + {}, +}; + +static struct spi_driver gf_driver = { + .driver = { + .name = GF_DEV_NAME, + .owner = THIS_MODULE, + .bus = &spi_bus_type, + .of_match_table = gx_match_table, + }, + .probe = gf_probe, + .remove = gf_remove, +// .suspend = gf_suspend, +// .resume = gf_resume, +}; + +static struct spi_board_info spi_board_devs[] __initdata = { + [0] = { + .modalias = GF_DEV_NAME, + .bus_num = 0, + .chip_select=0, + .mode = SPI_MODE_0, + .controller_data = &spi_conf_mt65xx, + }, +}; + +/* IRQ operations */ +static void gf_enable_irq(struct gf_dev *gf_dev) +{ + FUNC_ENTRY(); + +#if 0 + enable_irq(gf_dev->spi->irq); + gf_dev->irq_enabled = 1; + +#else + if (gf_dev->irq_enabled) { + gf_print("IRQ has been enabled.\n"); + } else { + enable_irq(gf_dev->irq); + gf_dev->irq_enabled = 1; + } +#endif + FUNC_EXIT(); +} + +static void gf_disable_irq(struct gf_dev *gf_dev) +{ + FUNC_ENTRY(); +#if 0 + gf_dev->irq_enabled = 0; + disable_irq(gf_dev->spi->irq); +#else + if (gf_dev->irq_enabled) { + gf_dev->irq_enabled = 0; + disable_irq(gf_dev->irq); + } else { + gf_print("IRQ has been disabled.\n"); + } +#endif + FUNC_EXIT(); +} + +/* spi clock reference */ +static long spi_clk_max_rate(struct clk *clk, unsigned long rate) +{ + long lowest_available, nearest_low, step_size, cur; + long step_direction = -1; + long guess = rate; + int max_steps = 10; + + cur = clk_round_rate(clk, rate); + if (cur == rate) + return rate; + + /* if we got here then: cur > rate */ + lowest_available = clk_round_rate(clk, 0); + if (lowest_available > rate) + return -EINVAL; + + step_size = (rate - lowest_available) >> 1; + nearest_low = lowest_available; + + while (max_steps-- && step_size) { + guess += step_size * step_direction; + cur = clk_round_rate(clk, guess); + + if ((cur < rate) && (cur > nearest_low)) + nearest_low = cur; + /* + * if we stepped too far, then start stepping in the other + * direction with half the step size + */ + if (((cur > rate) && (step_direction > 0)) + || ((cur < rate) && (step_direction < 0))) { + step_direction = -step_direction; + step_size >>= 1; + } + } + return nearest_low; +} + +static void spi_clock_set(struct gf_dev *gf_dev, int speed) +{ + long rate; + int rc; + + rate = spi_clk_max_rate(gf_dev->core_clk, speed); + if (rate < 0) { + pr_info("%s: no match found for requested clock frequency:%d", + __func__, speed); + return; + } + + rc = clk_set_rate(gf_dev->core_clk, rate); +} + +static void gf_spi_clk_open(struct gf_dev *gf_dev) +{ + /*open core clk*/ + + if ((!gf_dev->core_clk) || (!gf_dev->iface_clk)) + return; + + clk_prepare(gf_dev->core_clk); + + /*open iface clk*/ + clk_prepare(gf_dev->iface_clk); + gf_dev->clk_enabled = 1; + +} + +static void gf_spi_clk_close(struct gf_dev *gf_dev) +{ + if ((!gf_dev->core_clk) || (!gf_dev->iface_clk)) + return; + + /*close iface clk*/ + clk_unprepare(gf_dev->iface_clk); + + /*open core clk*/ + clk_unprepare(gf_dev->core_clk); + gf_dev->clk_enabled = 0; +} + +void gf_spi_setup(struct gf_dev *gf_dev, int max_speed_hz) +{ + pr_info("####### %s %d \n", __func__, __LINE__); + gf_dev->spi->mode = SPI_MODE_0; //CPOL=CPHA=0 + gf_dev->spi->max_speed_hz = max_speed_hz; + gf_dev->spi->bits_per_word = 8; + gf_dev->spi->controller_data = (void*)&spi_conf_mt65xx; + spi_setup(gf_dev->spi); +} + +static void gf_spi_set_mode(struct spi_device *spi, SPI_SPEED speed, int flag) +{ + struct mt_chip_conf *mcc = &spi_conf_mt65xx; + if(flag == 0) { + mcc->com_mod = FIFO_TRANSFER; + } else { + mcc->com_mod = DMA_TRANSFER; + } + switch(speed) + { + case SPEED_500KHZ: + mcc->high_time = 120; + mcc->low_time = 120; + break; + case SPEED_1MHZ: + mcc->high_time = 60; + mcc->low_time = 60; + break; + case SPEED_2MHZ: + mcc->high_time = 30; + mcc->low_time = 30; + break; + case SPEED_3MHZ: + mcc->high_time = 20; + mcc->low_time = 20; + break; + case SPEED_4MHZ: + mcc->high_time = 15; + mcc->low_time = 15; + break; + + case SPEED_6MHZ: + mcc->high_time = 10; + mcc->low_time = 10; + break; + case SPEED_8MHZ: + mcc->high_time = 8; + mcc->low_time = 8; + break; + case SPEED_KEEP: + case SPEED_UNSUPPORTED: + break; + } + if(spi_setup(spi) < 0){ + //gf_error("gf:Failed to set spi."); + } +} + +static SPI_SPEED trans_spi_speed(unsigned int speed) +{ + SPI_SPEED spi_speed = SPEED_1MHZ; + + if (speed >= 4000000) + { + spi_speed = SPEED_4MHZ; + } + + //printk("%s , speed_in=%l, speed_out=%l\n", __func__, speed, spi_speed); + return spi_speed; +} + +static long gf_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + struct gf_dev *gf_dev = &gf; + struct gf_key gf_key = { 0 }; + unsigned int speed = 0; + int retval = 0; + u32 tmp = 0;//xiaowen + + FUNC_ENTRY(); + if (_IOC_TYPE(cmd) != GF_IOC_MAGIC) + return -ENODEV; + + if (_IOC_DIR(cmd) & _IOC_READ) + retval = + !access_ok(VERIFY_WRITE, (void __user *)arg, + _IOC_SIZE(cmd)); + if ((retval == 0) && (_IOC_DIR(cmd) & _IOC_WRITE)) + retval = + !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd)); + if (retval) + return -EFAULT; + + switch (cmd) { + case GF_IOC_DISABLE_IRQ: + gf_print("%s , GF_IOC_DISABLE_IRQ",__func__); + gf_disable_irq(gf_dev); + break; + case GF_IOC_ENABLE_IRQ: + gf_print("%s , GF_IOC_ENABLE_IRQ",__func__); + gf_enable_irq(gf_dev); + break; + case GF_IOC_SETSPEED: + gf_print("%s , GF_IOC_SETSPEED",__func__); + retval = __get_user(speed, (u32 __user *) arg); + if (retval == 0) { + if (speed > 12 * 1000 * 1000) { + pr_info("Set speed:%d is larger than 12Mbps.\n",speed); + } else { + pr_info("Set speed:%d bps.\n",speed); + gf_spi_set_mode(gf_dev->spi, trans_spi_speed(speed), 0); + } + } else { + pr_info("Failed to get speed from user. retval = %d\n",retval); + } + break; + case GF_IOC_RESET: + gf_print("%s , GF_IOC_RESET",__func__); + gf_hw_reset(gf_dev, 70); + break; + case GF_IOC_COOLBOOT: + gf_print("%s , GF_IOC_COOLBOOT",__func__); + gf_power_on(gf_dev); + mdelay(5); + gf_power_on(gf_dev); + break; + case GF_IOC_SENDKEY: + ftm_gfx_irq_state = 0; + if (copy_from_user + (&gf_key, (struct gf_key *)arg, sizeof(struct gf_key))) { + pr_info("Failed to copy data from user space,line=%d.\n", __LINE__); + retval = -EFAULT; + break; + } + gf_print(KERN_ERR"%s: gf_key.key = %d, gf_key.value = %d.\n",__func__,gf_key.key, gf_key.value); + if(gf_key.value == 1) { + ftm_gfx_irq_state = 0x1; //down. + } else if(gf_key.value == 0) { + ftm_gfx_irq_state = 0x2; //up. + } + printk(KERN_ERR"%s: ftm_gfx_irq_state = %d.\n",__func__, ftm_gfx_irq_state); + ftm_gfx_irq_key = 1; + wake_up(&gf_poll_wq); + + input_report_key(gf_dev->input, gf_key.key, gf_key.value); + input_sync(gf_dev->input); + break; + case GF_IOC_CLK_READY: + gf_print("%s , GF_IOC_CLK_READY",__func__); + gf_spi_clk_open(gf_dev); + break; + case GF_IOC_CLK_UNREADY: + gf_print("%s , GF_IOC_CLK_UNREADY",__func__); + gf_spi_clk_close(gf_dev); + break; + case GF_IOC_PM_FBCABCK: + gf_print("%s , GF_IOC_PM_FBCABCK",__func__); + __put_user(gf_dev->fb_black, (u8 __user *) arg); + break; + case GF_IOC_SPI_TRANSFER: + gf_print("%s , GF_IOC_SPI_TRANSFER",__func__); + gf_spi_transfer(gf_dev, arg); + break; + case GF_IOC_SET_MODE: + { + //u16 status = 0; + //u16 mode = 0; + //u16 key = 0; + u16 next_mode = 0; + + printk(KERN_ERR"[FTM]:set mode\n"); + + retval = __get_user(tmp, (u32 __user*)arg); + next_mode = (u16)tmp; + printk(KERN_ERR"[FTM]:next mode:[%d]\n", next_mode); + + if(next_mode >= GFX1XM_IMAGE_MODE && next_mode <= GFX1XM_FF_MODE){ + gf_spi_write_word(gf_dev, 0x0834, next_mode); + } + + //gf_spi_read_word(gf_dev, 0x0836, &status); + //gf_spi_read_word(gf_dev, 0x083B, &key); + } + break; + /* + case GF_IOC_FTM: + { + int tmp1 = 0; + printk(KERN_ERR"%s: ftm_gfx_irq_state = %d.\n",__func__, + ftm_gfx_irq_state); + retval = __get_user(tmp1, (u32 __user*)arg); + tmp = ftm_gfx_irq_state; + if(tmp1 == 0) { + ftm_gfx_irq_state = 0; + } + retval = __put_user(tmp, (__u32 __user *)arg); + } + break; + */ + default: + gf_print("Unsupport cmd:0x%x\n", cmd); + break; + } + //FUNC_EXIT(); + return retval; +} + +#ifdef CONFIG_COMPAT +static long +gf_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +{ + return gf_ioctl(filp, cmd, (unsigned long)compat_ptr(arg)); +} +#endif /*CONFIG_COMPAT*/ + +static irqreturn_t gf_irq(int irq, void *handle) +{ + struct gf_dev *gf_dev = &gf; + FUNC_ENTRY(); +#if 0 + u8 data = 0; + int i; + char version[20]= {0}; + + // gf_spi_read_byte(gf_dev, 0x0837, &data); + // gf_print(" -----------interrupt event callback!!---------- %d\n", data); + + + for(i = 0; i < 1; i++) { + gf_spi_read_bytes(gf_dev,0x0800, 6, gf_dev->gBuffer); + memcpy(version, gf_dev->gBuffer + GF_RDATA_OFFSET, 6); + gf_print("interrupt: chip===>version = %x,%x,%x,%x,%x,%x \n", + version[0],version[1],version[2],version[3],version[4],version[5]); + } +#endif + +#ifdef GF_FASYNC + if (gf_dev->async){ + kill_fasync(&gf_dev->async, SIGIO, POLL_IN); + } +#endif + //FUNC_EXIT(); + + return IRQ_HANDLED; +} + +/********************************************************** + *Message format: + * write cmd | ADDR_H |ADDR_L | data stream | + * 1B | 1B | 1B | length | + * + * read buffer length should be 1 + 1 + 1 + data_length + ***********************************************************/ +static int gf_spi_write_bytes(struct gf_dev *gf_dev, + u16 addr, u32 data_len, u8 *tx_buf) +{ + struct spi_message msg; + struct spi_transfer *xfer; + u32 package_num = (data_len + 2*GF_WDATA_OFFSET)>>MTK_SPI_ALIGN_MASK_NUM; + u32 reminder = (data_len + 2*GF_WDATA_OFFSET) & MTK_SPI_ALIGN_MASK; + u8 *reminder_buf = NULL; + u8 twice = 0; + int ret = 0; + + FUNC_ENTRY(); + + /*set spi mode.*/ + if((data_len + GF_WDATA_OFFSET) > 32) { + gf_spi_set_mode(gf_dev->spi, SPEED_KEEP, 1); //FIFO + } else { + gf_spi_set_mode(gf_dev->spi, SPEED_KEEP, 0); //FIFO + } + if((package_num > 0) && (reminder != 0)) { + twice = 1; + /*copy the reminder data to temporarity buffer.*/ + reminder_buf = kzalloc(reminder + GF_WDATA_OFFSET, GFP_KERNEL); + if(reminder_buf == NULL ) { + gf_print("gf:No memory for exter data."); + return -ENOMEM; + } + memcpy(reminder_buf + GF_WDATA_OFFSET, tx_buf + 2*GF_WDATA_OFFSET+data_len - reminder, reminder); + gf_print("gf:w-reminder:0x%x-0x%x,0x%x", reminder_buf[GF_WDATA_OFFSET],reminder_buf[GF_WDATA_OFFSET+1], + reminder_buf[GF_WDATA_OFFSET + 2]); + xfer = kzalloc(sizeof(*xfer)*2, GFP_KERNEL); + } else { + twice = 0; + xfer = kzalloc(sizeof(*xfer), GFP_KERNEL); + } + if(xfer == NULL){ + gf_print("gf:No memory for command."); + if(reminder_buf != NULL) + kfree(reminder_buf); + return -ENOMEM; + } + + gf_print("gf:write twice = %d. data_len = %d, package_num = %d, reminder = %d\n", (int)twice, (int)data_len, (int)package_num, (int)reminder); + /*if the length is not align with 1024. Need 2 transfer at least.*/ + spi_message_init(&msg); + tx_buf[0] = GF_W; + tx_buf[1] = (u8)((addr >> 8)&0xFF); + tx_buf[2] = (u8)(addr & 0xFF); + xfer[0].tx_buf = tx_buf; + //xfer[0].delay_usecs = 5; + if(twice == 1) { + xfer[0].len = package_num << MTK_SPI_ALIGN_MASK_NUM; + spi_message_add_tail(&xfer[0], &msg); + addr += (data_len - reminder + GF_WDATA_OFFSET); + reminder_buf[0] = GF_W; + reminder_buf[1] = (u8)((addr >> 8)&0xFF); + reminder_buf[2] = (u8)(addr & 0xFF); + xfer[1].tx_buf = reminder_buf; + xfer[1].len = reminder + 2*GF_WDATA_OFFSET; + //xfer[1].delay_usecs = 5; + spi_message_add_tail(&xfer[1], &msg); + } else { + xfer[0].len = data_len + GF_WDATA_OFFSET; + spi_message_add_tail(&xfer[0], &msg); + } + ret = spi_sync(gf_dev->spi, &msg); + if(ret == 0) { + if(twice == 1) + ret = msg.actual_length - 2*GF_WDATA_OFFSET; + else + ret = msg.actual_length - GF_WDATA_OFFSET; + } else { + gf_print("gf:write async failed. ret = %d", ret); + } + + if(xfer != NULL) { + kfree(xfer); + xfer = NULL; + } + if(reminder_buf != NULL) { + kfree(reminder_buf); + reminder_buf = NULL; + } + + return ret; +} + +/************************************************************* + *First message: + * write cmd | ADDR_H |ADDR_L | + * 1B | 1B | 1B | + *Second message: + * read cmd | data stream | + * 1B | length | + * + * read buffer length should be 1 + 1 + 1 + 1 + data_length + **************************************************************/ +int gf_spi_read_bytes(struct gf_dev *gf_dev, + u16 addr, u32 data_len, u8 *rx_buf) +{ + struct spi_message msg; + struct spi_transfer *xfer; + u32 package_num = (data_len + 1 + 1)>>MTK_SPI_ALIGN_MASK_NUM; + u32 reminder = (data_len + 1 + 1) & MTK_SPI_ALIGN_MASK; + u8 *one_more_buff = NULL; + u8 twice = 0; + int ret = 0; + + FUNC_ENTRY(); + + one_more_buff = kzalloc(15000, GFP_KERNEL); + if(one_more_buff == NULL ) { + gf_print("No memory for one_more_buff."); + return -ENOMEM; + } + xfer = kzalloc(sizeof(*xfer)*2, GFP_KERNEL); + if((package_num > 0) && (reminder != 0)) { + twice = 1; + printk("stone package_num is %d reminder is %d\n",package_num,reminder); + } else { + twice = 0; + } + if( xfer == NULL){ + gf_print("No memory for command."); + if(one_more_buff != NULL) + kfree(one_more_buff); + return -ENOMEM; + } + /*set spi mode.*/ + if((data_len + GF_RDATA_OFFSET) > 32) { + gf_spi_set_mode(gf_dev->spi, SPEED_KEEP, 1); //DMA + } else { + gf_spi_set_mode(gf_dev->spi, SPEED_KEEP, 0); //FIFO + } + spi_message_init(&msg); + /*send GF command to device.*/ + rx_buf[0] = GF_W; + rx_buf[1] = (u8)((addr >> 8)&0xFF); + rx_buf[2] = (u8)(addr & 0xFF); + xfer[0].tx_buf = rx_buf; + xfer[0].len = 3; + spi_message_add_tail(&xfer[0], &msg); + spi_sync(gf_dev->spi, &msg); + spi_message_init(&msg); + + /*if wanted to read data from GF. + *Should write Read command to device + *before read any data from device. + */ + //memset(rx_buf, 0xff, data_len); + one_more_buff[0] = GF_R; + xfer[1].tx_buf = &one_more_buff[0]; + xfer[1].rx_buf = &one_more_buff[0]; + //read 1 additional package to ensure no even data read + if(twice == 1) + xfer[1].len = ((package_num+1) << MTK_SPI_ALIGN_MASK_NUM); + else + xfer[1].len = data_len + 1; + spi_message_add_tail(&xfer[1], &msg); + ret = spi_sync(gf_dev->spi, &msg); + if(ret == 0) { + memcpy(rx_buf + GF_RDATA_OFFSET,one_more_buff+1,data_len); + ret = data_len; + }else { + gf_print("gf: read failed. ret = %d", ret); + } + + kfree(xfer); + if(xfer != NULL) + xfer = NULL; + if(one_more_buff != NULL) { + kfree(one_more_buff); + one_more_buff = NULL; + } + //gf_debug(SPI_DEBUG,"gf:read twice = %d, data_len = %d, package_num = %d, reminder = %d\n",(int)twice, (int)data_len, (int)package_num, (int)reminder); + gf_print("gf:data_len = %d, msg.actual_length = %d, ret = %d\n", (int)data_len, (int)msg.actual_length, ret); + return ret; +} +/* +static int gf_spi_read_byte(struct gf_dev *gf_dev, u16 addr, u8 *value) +{ + int status = 0; + mutex_lock(&gf_dev->buf_lock); + + status = gf_spi_read_bytes(gf_dev, addr, 1, gf_dev->gBuffer); + *value = gf_dev->gBuffer[GF_WDATA_OFFSET]; + mutex_unlock(&gf_dev->buf_lock); + return status; +} +static int gf_spi_write_byte(struct gf_dev *gf_dev, u16 addr, u8 value) +{ + int status = 0; + mutex_lock(&gf_dev->buf_lock); + gf_dev->gBuffer[GF_WDATA_OFFSET] = value; + status = gf_spi_write_bytes(gf_dev, addr, 1, gf_dev->gBuffer); + mutex_unlock(&gf_dev->buf_lock); + return status; +} +*/ +int gf_spi_read_word(struct gf_dev *gf_dev, u16 addr, u16 *value) +{ + int status = 0; + u8 *buf = NULL; + mutex_lock(&gf_dev->buf_lock); + status = gf_spi_read_bytes(gf_dev, addr, 2, gf_dev->gBuffer); + buf = gf_dev->gBuffer + GF_RDATA_OFFSET; + *value = (u16)buf[0]<<8 | buf[1]; + mutex_unlock(&gf_dev->buf_lock); + return status; +} + +int gf_spi_write_word(struct gf_dev* gf_dev, u16 addr, u16 value) +{ + int status = 0; + mutex_lock(&gf_dev->buf_lock); + gf_dev->gBuffer[GF_WDATA_OFFSET] = 0x00; + gf_dev->gBuffer[GF_WDATA_OFFSET+1] = 0x01; + gf_dev->gBuffer[GF_WDATA_OFFSET+2] = (u8)(value>>8); + gf_dev->gBuffer[GF_WDATA_OFFSET+3] = (u8)(value & 0x00ff); + status = gf_spi_write_bytes(gf_dev, addr, 4, gf_dev->gBuffer); + mutex_unlock(&gf_dev->buf_lock); + + return status; +} + +static int gf_spi_transfer(struct gf_dev *gf_dev, unsigned long arg) +{ + struct gf_spi_transfer ioc = {0}; + FUNC_ENTRY(); + /*copy command data from user to kernel.*/ + if(copy_from_user(&ioc, (struct gf_spi_transfer*)arg, sizeof(struct gf_spi_transfer))){ + gf_print("Failed to copy command from user to kernel.line=%d\n", __LINE__); + return -EFAULT; + } + if(ioc.len == 0) { + gf_print("The request length is 0.\n"); + return -EMSGSIZE; + } + + mutex_lock(&gf_dev->buf_lock); + if(ioc.cmd == GF_R) { + /*if want to read data from hardware.*/ + //pr_info("Read data from 0x%x, len = 0x%x buf = 0x%llx\n", ioc.addr, ioc.len, ioc.buf); + gf_spi_read_bytes(gf_dev, ioc.addr, ioc.len, gf_dev->gBuffer); + if(copy_to_user((void __user*) ioc.buf, (void *)(gf_dev->gBuffer + GF_RDATA_OFFSET), ioc.len)) + { + gf_print("Failed to copy data from kernel to user.line=%d\n",__LINE__); + mutex_unlock(&gf_dev->buf_lock); + return -EFAULT; + } + } else if (ioc.cmd == GF_W) { + /*if want to read data from hardware.*/ + //pr_info("Write data from 0x%x, len = 0x%x\n", ioc.addr, ioc.len); + + if(copy_from_user(gf_dev->gBuffer + GF_WDATA_OFFSET, (void *)ioc.buf, ioc.len)) +{ + gf_print("Failed to copy data from user to kernel.line=%d\n", __LINE__); + mutex_unlock(&gf_dev->buf_lock); + return -EFAULT; + } + + gf_spi_write_bytes(gf_dev, ioc.addr, ioc.len, gf_dev->gBuffer); + } else { + gf_print("Error command for gf.\n"); + } + mutex_unlock(&gf_dev->buf_lock); + FUNC_EXIT(); + return 0; +} + +static int gf_open(struct inode *inode, struct file *filp) +{ + struct gf_dev *gf_dev; + int status = -ENXIO; + + FUNC_ENTRY(); + mutex_lock(&device_list_lock); + + list_for_each_entry(gf_dev, &device_list, device_entry) { + if (gf_dev->devt == inode->i_rdev) { + gf_print("Found\n"); + status = 0; + break; + } + } + + if (status == 0) { + if (status == 0) { + gf_dev->users++; + filp->private_data = gf_dev; + nonseekable_open(inode, filp); + gf_print("Succeed to open device. irq = %d, users:%d\n", + gf_dev->spi->irq, gf_dev->users); + if (gf_dev->users == 1){ + //gf_enable_irq(gf_dev); + } + } + } else { + gf_print("No device for minor %d\n", iminor(inode)); + } + //gf_power_on(gf_dev); + mutex_unlock(&device_list_lock); + FUNC_EXIT(); + return status; +} + +#ifdef GF_FASYNC +static int gf_fasync(int fd, struct file *filp, int mode) +{ + struct gf_dev *gf_dev = filp->private_data; + int ret; + + FUNC_ENTRY(); + ret = fasync_helper(fd, filp, mode, &gf_dev->async); + FUNC_EXIT(); + gf_print("ret = %d\n", ret); + return ret; +} +#endif +/* +//xiaowen, add start +static unsigned int gf_fp_poll(struct file *file, poll_table *wait) +{ + unsigned int mask = 0; + + poll_wait(file, &gf_poll_wq, wait); + + if(ftm_gfx_irq_key != 0){ + mask = POLLIN | POLLRDNORM; + } + + ftm_gfx_irq_key = 0; + return mask; +} +//xiaowen, add end +*/ +static int gf_release(struct inode *inode, struct file *filp) +{ + struct gf_dev *gf_dev; + int status = 0; + + FUNC_ENTRY(); + mutex_lock(&device_list_lock); + gf_dev = filp->private_data; + filp->private_data = NULL; + + /*last close?? */ + gf_dev->users--; + if (!gf_dev->users) { + + gf_print("disble_irq. irq = %d\n", gf_dev->spi->irq); + gf_disable_irq(gf_dev); + } + mutex_unlock(&device_list_lock); + FUNC_EXIT(); + return status; +} + +static const struct file_operations gf_fops = { + .owner = THIS_MODULE, + /* REVISIT switch to aio primitives, so that userspace + * gets more complete API coverage. It'll simplify things + * too, except for the locking. + */ + .unlocked_ioctl = gf_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = gf_compat_ioctl, +#endif /*CONFIG_COMPAT*/ + .open = gf_open, + .release = gf_release, + .read = gf_read, +#ifdef GF_FASYNC + .fasync = gf_fasync, +#endif + //.poll = gf_fp_poll, +}; + +/* The main reason to have this class is to make mdev/udev create the + * /dev/spidevB.C character device nodes exposing our userspace API. + * It also simplifies memory management. + */ + +#ifdef USE_SPI_BUS +/* +static int gfspi_ioctl_clk_init(struct spi_device *spi, struct gf_dev *data) +{ + pr_debug("%s: enter\n", __func__); + + data->clk_enabled = 0; + data->core_clk = clk_get(&spi->dev, "core_clk"); + if (IS_ERR_OR_NULL(data->core_clk)) { + pr_err("%s: fail to get core_clk\n", __func__); + return -1; + } + data->iface_clk = clk_get(&spi->dev, "iface_clk"); + if (IS_ERR_OR_NULL(data->iface_clk)) { + pr_err("%s: fail to get iface_clk\n", __func__); + clk_put(data->core_clk); + data->core_clk = NULL; + return -2; + } + return 0; +} + +static int gfspi_ioctl_clk_enable(struct gf_dev *data) +{ + int err; + + pr_debug("%s: enter\n", __func__); + + if (data->clk_enabled) + return 0; + + err = clk_prepare_enable(data->core_clk); + if (err) { + pr_err("%s: fail to enable core_clk\n", __func__); + return -1; + } + + err = clk_prepare_enable(data->iface_clk); + if (err) { + pr_err("%s: fail to enable iface_clk\n", __func__); + clk_disable_unprepare(data->core_clk); + return -2; + } + + data->clk_enabled = 1; + + return 0; +} +*/ +static int gfspi_ioctl_clk_disable(struct gf_dev *data) +{ + pr_debug("%s: enter\n", __func__); + + if (!data->clk_enabled) + return 0; + + clk_disable_unprepare(data->core_clk); + clk_disable_unprepare(data->iface_clk); + data->clk_enabled = 0; + + return 0; +} + +static int gfspi_ioctl_clk_uninit(struct gf_dev *data) +{ + pr_debug("%s: enter\n", __func__); + + if (data->clk_enabled) + gfspi_ioctl_clk_disable(data); + + if (!IS_ERR_OR_NULL(data->core_clk)) { + clk_put(data->core_clk); + data->core_clk = NULL; + } + + if (!IS_ERR_OR_NULL(data->iface_clk)) { + clk_put(data->iface_clk); + data->iface_clk = NULL; + } + + return 0; +} +#endif + +static int goodix_fb_state_chg_callback(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct gf_dev *gf_dev; + struct fb_event *evdata = data; + unsigned int blank; + + if (val != FB_EARLY_EVENT_BLANK) + return 0; + pr_info("[info] %s go to the goodix_fb_state_chg_callback value = %d\n", + __func__, (int)val); + gf_dev = container_of(nb, struct gf_dev, notifier); + if (evdata && evdata->data && val == FB_EARLY_EVENT_BLANK && gf_dev) { + blank = *(int *)(evdata->data); + switch (blank) { + case FB_BLANK_POWERDOWN: + if (gf_dev->device_available == 1) { + gf_dev->fb_black = 1; +#ifdef GF_FASYNC + if (gf_dev->async) { + kill_fasync(&gf_dev->async, SIGIO, + POLL_IN); + } +#endif + /*device unavailable */ + gf_dev->device_available = 0; + } + break; + case FB_BLANK_UNBLANK: + if (gf_dev->device_available == 0) { + gf_dev->fb_black = 0; + /*device available */ + gf_dev->device_available = 1; + } + break; + default: + pr_info("%s defalut\n", __func__); + break; + } + } + return NOTIFY_OK; +} + +static struct notifier_block goodix_noti_block = { + .notifier_call = goodix_fb_state_chg_callback, +}; + +static void gf_reg_key_kernel(struct gf_dev *gf_dev) +{ + __set_bit(EV_KEY, gf_dev->input->evbit); + __set_bit(GF_KEY_POWER, gf_dev->input->keybit); + __set_bit(GF_KEY_HOME, gf_dev->input->keybit); + __set_bit(GF_KEY_MENU, gf_dev->input->keybit); + __set_bit(GF_KEY_BACK, gf_dev->input->keybit); + __set_bit(GF_UP_KEY, gf_dev->input->keybit); + __set_bit(GF_RIGHT_KEY, gf_dev->input->keybit); + __set_bit(GF_LEFT_KEY, gf_dev->input->keybit); + __set_bit(GF_DOWN_KEY, gf_dev->input->keybit); + __set_bit(GF_KEY_FORCE, gf_dev->input->keybit); + __set_bit(GF_APP_SWITCH, gf_dev->input->keybit); + gf_dev->input->name = GF_INPUT_NAME; + if (input_register_device(gf_dev->input)) + gf_print("Failed to register GF as input device.\n"); +} + +int gf_read_chip_id(struct gf_dev *pdev) +{ + unsigned short val = 0; + FUNC_ENTRY(); + + gf_spi_read_word(pdev, 0x0142, &val); + gf_print("goodix-chip id: 0x%x \n", val); + if((val >> 4) == (0x12a1 >> 4)) { + return 0; + } + printk(KERN_ERR"3goodix-chip id: 0x%x \n", val); + return -1; +} + +static int gf_probe(struct spi_device *spi) +{ + struct gf_dev *gf_dev = &gf; + int status = -EINVAL; + unsigned long minor; + int ret; + + FUNC_ENTRY(); + + INIT_LIST_HEAD(&gf_dev->device_entry); + gf_dev->spi = spi; + gf_dev->irq_gpio = -EINVAL; + gf_dev->reset_gpio = -EINVAL; + gf_dev->pwr_gpio = -EINVAL; + gf_dev->device_available = 0; + gf_dev->fb_black = 0; + + mutex_init(&gf_dev->buf_lock); + spin_lock_init(&gf_dev->spi_lock); + + gf_dev->gBuffer = (unsigned char*)__get_free_pages(GFP_KERNEL, get_order(BUF_SIZE)); + if(gf_dev->gBuffer == NULL) { + return -ENOMEM; + } + + if (gf_parse_dts(gf_dev)) { + gf_print("gf_parse_dts err! \n"); + goto error; + } + + if (gf_power_init(gf_dev)) { + gf_print("gf_power_init err! \n"); + goto error; + } + + if (gf_power_on(gf_dev)) { + gf_print("gf_power_on err! \n"); + goto error; + } + if (gf_read_chip_id(gf_dev)) { + gf_print("gf_read_chip_id err! \n"); + goto error; + } + printk(KERN_ERR"gf_read_chip_id = %d", gf_read_chip_id(gf_dev)); + + BUILD_BUG_ON(N_SPI_MINORS > 256); + status = register_chrdev(SPIDEV_MAJOR, CHRD_DRIVER_NAME, &gf_fops); //register char devices. + if (status < 0) { + gf_print("Failed to register char device!\n"); + goto error; + } + gf_class = class_create(THIS_MODULE, CLASS_NAME); + if (IS_ERR(gf_class)) { + unregister_chrdev(SPIDEV_MAJOR, gf_driver.driver.name); + gf_print("Failed to create class.\n"); + goto error; + } + + mutex_lock(&device_list_lock); + minor = find_first_zero_bit(minors, N_SPI_MINORS); + if (minor < N_SPI_MINORS) { + struct device *dev; + gf_dev->devt = MKDEV(SPIDEV_MAJOR, minor); + dev = device_create(gf_class, &gf_dev->spi->dev, gf_dev->devt, + gf_dev, GF_DEV_NAME); + status = IS_ERR(dev) ? PTR_ERR(dev) : 0; + } else { + gf_print("no minor number available!\n"); + status = -ENODEV; + } + + if (status == 0) { + set_bit(minor, minors); + list_add(&gf_dev->device_entry, &device_list); + } else { + gf_dev->devt = 0; + } + mutex_unlock(&device_list_lock); + + if (status == 0) { + spi_set_drvdata(spi, gf_dev); + /*input device subsystem */ + gf_dev->input = input_allocate_device(); + if (gf_dev->input == NULL) { + gf_print("Faile to allocate input device.\n"); + status = -ENOMEM; + } + + gf_dev->notifier = goodix_noti_block; + fb_register_client(&gf_dev->notifier); + gf_reg_key_kernel(gf_dev); + + gf_hw_reset(gf_dev, 60); + gf_spi_setup(gf_dev, 8*1000*1000); + gf_spi_set_mode(gf_dev->spi, SPEED_1MHZ, 0); + + gf_gpio_as_int(gf_dev); + if(gf_irq_num(gf_dev)) { + gf_print("gf_irq_num err! \n"); + goto error; + } + + ret = request_threaded_irq(gf_dev->spi->irq, NULL, gf_irq, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, "gf", gf_dev); + if (!ret) { + enable_irq_wake(gf_dev->spi->irq); + gf_dev->irq_enabled = 1; + gf_disable_irq(gf_dev); + } + else { + gf_print("request_threaded_irq ret: %d\n", ret); + goto error; + } + } + else { + goto error; + } + + gf_dev->device_available = 1; + gf_print("------------- succeed to gf_probe! --------------\n"); + + //full_fp_chip_name(GF_DEV_NAME); + return status; + +error: + + gf_cleanup(gf_dev); + gf_dev->device_available = 0; + if (gf_dev->devt != 0) { + gf_print("Err: status = %d\n", status); + mutex_lock(&device_list_lock); + list_del(&gf_dev->device_entry); + device_destroy(gf_class, gf_dev->devt); + clear_bit(MINOR(gf_dev->devt), minors); + mutex_unlock(&device_list_lock); +/* +gfspi_probe_clk_enable_failed: + gfspi_ioctl_clk_uninit(gf_dev); +gfspi_probe_clk_init_failed: + if (gf_dev->input != NULL) + input_unregister_device(gf_dev->input); +*/ + } + + if(gf_dev->gBuffer != NULL) { + free_pages((unsigned long)gf_dev->gBuffer, get_order(BUF_SIZE)); + } + + + gf_print("~~~~ fail to gf_probe! ~~~~\n"); + + return status; +} + +static int gf_remove(struct spi_device *spi) +{ + struct gf_dev *gf_dev = &gf; + FUNC_ENTRY(); + + + /* make sure ops on existing fds can abort cleanly */ + if (gf_dev->spi->irq) + free_irq(gf_dev->spi->irq, gf_dev); + + if (gf_dev->input != NULL) + input_unregister_device(gf_dev->input); + input_free_device(gf_dev->input); + + /* prevent new opens */ + mutex_lock(&device_list_lock); + list_del(&gf_dev->device_entry); + device_destroy(gf_class, gf_dev->devt); + clear_bit(MINOR(gf_dev->devt), minors); + + gf_cleanup(gf_dev); + gf_dev->device_available = 0; + fb_unregister_client(&gf_dev->notifier); + gfspi_ioctl_clk_uninit(gf_dev); + + if (gf_dev->users == 0) { + if(gf_dev->gBuffer) + free_pages((unsigned long)gf_dev->gBuffer, get_order(BUF_SIZE)); + } + else{ + gf_print("Not free_pages.\n"); + } + + mutex_unlock(&device_list_lock); + + FUNC_EXIT(); + return 0; +} + +void stop_sampling(struct gf_dev *gf_dev) +{ + unsigned short val = 0; + + gf_spi_read_word(gf_dev, 0x0840, &val); + val &= 0xFF00; + val |= 0x0001; + gf_spi_write_word(gf_dev, 0x0840, val); + +} +/* +int start_sampling(struct gf_dev *gf_dev) +{ + unsigned short val = 0; + + gf_spi_read_word(gf_dev, 0x0840, &val); + val &= 0xFF00; + gf_spi_write_word(gf_dev, 0x0840, val); +} +*/ +int gf_read_pre_image(struct gf_dev *gf_dev) +{ + int cnt = 0; + unsigned char status = 0; + unsigned short r0836 = 0; + int ret = -1; + + while (cnt < 100) + { + gf_spi_read_word(gf_dev, 0x0836, &r0836); + status = (unsigned char)(r0836 >> 8); + gf_print("status=0x%02x", status); + + if (status == GF_INT_IMAGE) + { + ret = 0; + break; + } + else if (status != GF_INT_INVALID) + { + gf_spi_write_word(gf_dev, 0x0836, 0); + //break; + } + + mdelay(3); + cnt++; + } + return ret; +} + +int get_frame_data(struct gf_dev *gf_dev, unsigned char *pFrame, int len) +{ + int timeout = 0; + unsigned short val = 0; + unsigned short fifo_len = 0; + unsigned short fifo_len_tmp = 0; + unsigned short fifo_len_next = GF_READ_BUFF_SIZE1; + unsigned short total_len = 0; + const unsigned int memsize = 3 * 1024 + GF_RDATA_OFFSET; + //unsigned char buf[3 * 1024 + GF_RDATA_OFFSET] = {0}; + unsigned char *buf = NULL; + + int ret = -1; + + if ((pFrame == NULL) || (len < GF_SPI_DATA_LEN)) { + gf_print("[%s]input error, len=%d", __func__, len); + return -1; + } + + buf = (unsigned char*)__get_free_pages(GFP_KERNEL, get_order(memsize)); + if (buf == NULL) { + gf_print("[%s]get_free_pages error", __func__); + return -1; + } + + //1. check to see if FIFO is empty or not + //[0x5204]&0x8000 == 0, data is valid + timeout = 100; //100 * 1ms + do{ + gf_spi_read_word(gf_dev, 0x5204, &val); + if (!(val & (1 << 15))) + { + break; + } + mdelay(1); + timeout --; + }while(timeout > 0); + + if (timeout <= 0) + { + + free_pages((unsigned long)buf, get_order(len)); + gf_print("wait_data time out"); + return -1; + } + + //2. notify_fw_reading_begin + gf_spi_write_word(gf_dev, 0x0836, 0x0002); + gf_print("fifo_reading_begin"); + + //3. read data + timeout = 100; + do + { + gf_spi_read_word(gf_dev, 0x5204, &val); + //gf_print("0x5204=0x%x", val); + if (val & (1 << 15)) + { + gf_print("fifo is empty, exit"); + break; + } + + val &= 0x07ff; + fifo_len_tmp = val * 2; + + if (fifo_len_tmp >= fifo_len_next) + { + fifo_len = fifo_len_tmp; + fifo_len_next = + (fifo_len_next == GF_READ_BUFF_SIZE1) ? + GF_READ_BUFF_SIZE2 : GF_READ_BUFF_SIZE1; + if (total_len + fifo_len <= GF_SPI_DATA_LEN) + { + +// gf_secspi_read_fifo(gf_secdev, GF_DATA_ADDR, fifo_len - 4, +// buf + total_len); +// gf_secspi_read_fifo(gf_secdev, GF_DATA_ADDR, 4, +// buf + total_len + fifo_len - 4); + spi_clock_set(gf_dev, 12*1000*1000); + gf_spi_setup(gf_dev, 12*1000*1000); + + gf_spi_read_bytes(gf_dev, GF_DATA_ADDR, fifo_len - 4, buf); + memcpy(pFrame + total_len, buf + GF_RDATA_OFFSET, fifo_len - 4); + total_len += fifo_len - 4; + + gf_spi_read_bytes(gf_dev, GF_DATA_ADDR, 4, buf); + + spi_clock_set(gf_dev, 1*1000*1000); + gf_spi_setup(gf_dev, 1*1000*1000); + + memcpy(pFrame + total_len , buf + GF_RDATA_OFFSET, 4); + total_len += 4; + + //gf_print("fifo_read len=%d, total=%d", fifo_len, total_len); + if (total_len == GF_SPI_DATA_LEN) + { + ret = 0; + //ret = restructure_raw_data(buf, image->buf, total_len); + break; + } + } + else + { + gf_print("error, total_len + fifo_len > GF_SPI_DATA_LEN"); + } + } + else + { + //gf_print("error, fifo_len_tmp = %d", fifo_len_tmp); + //break; + } + + timeout--; + udelay(300); + } while ((timeout > 0) && (total_len < GF_SPI_DATA_LEN)); + // end of ADC FIFO reading block + + //4. notify_fw_reading_end + gf_spi_write_word(gf_dev, 0x0836, 0); + gf_print("fifo_reading_end"); + + + free_pages((unsigned long)buf, get_order(memsize)); + return ret; +} + +static void endian_exchange(unsigned char* buf, int len) +{ + int i = 0; + unsigned char buf_tmp = 0; + + for(i = 0; i < len /2; i++) + { + buf_tmp = buf[2 * i + 1]; + buf[2 * i + 1] = buf[2 * i]; + buf[2 * i] = buf_tmp; + } +} + +unsigned short gf_calc_crc(unsigned char* pchMsg, unsigned short wDataLen) +{ + unsigned char i = 0, chChar = 0; + unsigned short wCRC = 0xFFFF; + + while(wDataLen--) + { + chChar = *pchMsg; + pchMsg++; + wCRC ^= (((unsigned short) chChar) << 8); + + for(i = 0; i < 8; i++) { + if (wCRC & 0x8000) + { + wCRC = (wCRC << 1) ^ CRC_16_POLYNOMIALS; + } + else + { + wCRC <<= 1; + } + } + } + return wCRC; +} + +static int restructure_raw_data(unsigned char *pFrame, + unsigned char* pFpdata, int total_len) +{ + int i = 0, j = 0; + unsigned short id = 0; + unsigned short calc_crc = 0; + unsigned char crc_buf[GF_ROW_DATA_SIZE] = + { 0 }; + gx_row_t row = + { 0 }; + + if (total_len != SENSOR_ROW * GF_ROW_SIZE) + { + gf_print("error total_len=%d", total_len); + return -1; + } + + for (i = 0; i < SENSOR_ROW; i++) + { + memcpy(&row, pFrame + i * GF_ROW_SIZE, GF_ROW_SIZE); + memcpy(crc_buf, row.raw, sizeof(crc_buf)); + /* Change byte order for calculating crc */ + //endian_exchange(crc_buf, GF_ROW_SIZE); + endian_exchange((unsigned char*)&row, GF_ROW_SIZE); + + calc_crc = gf_calc_crc(crc_buf, sizeof(crc_buf)); + if (calc_crc == row.crc) + { + id = row.id; + row.id = 4 * (id / 4) + (4 - id % 4) - 1; + //gf_print("%d - > %d", id, row.id); + for (j = 0; j < SENSOR_COL; j++) + { + row.raw[j] = row.raw[j] / 16; + } + memcpy(pFpdata + row.id * GF_ROW_DATA_SIZE, + (unsigned char*) row.raw, GF_ROW_DATA_SIZE); + } + else + { + gf_print( "Row %d crc error, calc_crc=%x, read_crc=%x", + row.id, calc_crc, row.crc); + return -1; + } + } + + gf_print("crc success, read frame ok"); + return 0; + +} + +int gf_read_image(struct gf_dev *gf_dev, unsigned char* pImage, int len) +{ + //unsigned char buf[GF_SPI_DATA_LEN] = {0}; + unsigned char *buf = NULL; + gf_print("%s\n", __func__); + + buf = (unsigned char*)__get_free_pages(GFP_KERNEL, get_order(GF_SPI_DATA_LEN)); + if (buf == NULL) { + gf_print("[%s]get_free_pages error", __func__); + return -1; + } + + //input parameter validity check + if ((pImage == NULL) || (len < SENSOR_ROW * SENSOR_COL * 2)) + { + free_pages((unsigned long)buf, get_order(GF_SPI_DATA_LEN)); + gf_print("Input buffer is NULL in %s\n", __func__); + return -1; + } + + if (gf_read_pre_image(gf_dev) != 0) + { + free_pages((unsigned long)buf, get_order(GF_SPI_DATA_LEN)); + gf_print("gf_read_pre_image, timeout"); + return -1; + } + + stop_sampling(gf_dev); + + if (get_frame_data(gf_dev, buf, GF_SPI_DATA_LEN) != 0) + { + free_pages((unsigned long)buf, get_order(GF_SPI_DATA_LEN)); + gf_print("gf_read_image, error"); + return -1; + } + + return restructure_raw_data(buf, pImage, GF_SPI_DATA_LEN); + +} + +/*-------------------------------------------------------------------------*/ +/* Read-only message with current device setup */ +static ssize_t gf_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) +{ + struct gf_dev *gf_dev = filp->private_data; + ssize_t status = 0; + int ret = -1; + const unsigned int len = SENSOR_ROW * SENSOR_COL * 2; + unsigned char *bufImage = NULL; + //long int t1, t2; + FUNC_ENTRY(); + + //gf_print("[%s]buf=%p, count=%d",__func__, buf, count); + + if ((count > len)||(count == 0)) { + gf_print("Max size for write buffer is %d. wanted length is %d\n", len, (unsigned int)count); + FUNC_EXIT(); + return -EMSGSIZE; + } + + bufImage = (unsigned char*)__get_free_pages(GFP_KERNEL, get_order(len)); + if (bufImage == NULL){ + gf_print("get_free_pages error"); + } + + gf_dev = filp->private_data; + + //t1 = ktime_to_us(ktime_get()); + + mutex_lock(&gf_dev->buf_lock); + ret = gf_read_image(gf_dev, bufImage, len); + if(ret == 0) { + ret = copy_to_user(buf, bufImage, len); + if (ret == 0){ + status = len; + } + else{ + pr_err("copy_to_user error."); + status = -EFAULT; + } + } else { + pr_err("Failed to read data from FIFO.\n"); + status = -EFAULT; + } + // t2 = ktime_to_us(ktime_get()); + //gf_print("read time use: %ld\n", t2-t1); + mutex_unlock(&gf_dev->buf_lock); + + free_pages((unsigned long)bufImage, get_order(len)); + return status; +} + +static int __init gf_init(void) +{ + int status; + FUNC_ENTRY(); + + spi_register_board_info(spi_board_devs,ARRAY_SIZE(spi_board_devs)); + status = spi_register_driver(&gf_driver); + if (status < 0) { + gf_print("Failed to register SPI driver."); + } + return status; +} + +static void __exit gf_exit(void) +{ + FUNC_ENTRY(); + spi_unregister_driver(&gf_driver); +} + +module_init(gf_init); +module_exit(gf_exit); + +MODULE_AUTHOR("Jiangtao Yi, "); +MODULE_DESCRIPTION("User mode SPI device interface"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:gf-spi"); diff --git a/drivers/input/fingerprint/gx556/gf_spi.h b/drivers/input/fingerprint/gx556/gf_spi.h new file mode 100755 index 000000000..4d994325b --- /dev/null +++ b/drivers/input/fingerprint/gx556/gf_spi.h @@ -0,0 +1,179 @@ +#ifndef __GF_SPI_H +#define __GF_SPI_H + +#include +#include +#include + + +#include "../../../../spi/mediatek/mt6757/mtk_spi.h" + +/**********************************************************/ +/**********************GF ops****************************/ +#define SENSOR_ROW (60) +#define SENSOR_COL (128) +#define GF_W 0xF0 +#define GF_R 0xF1 +#define GF_WDATA_OFFSET (0x3) +#define GF_RDATA_OFFSET (0x5) + +#define GF_IOC_MAGIC 'G' +#define GF_IOC_DISABLE_IRQ _IO(GF_IOC_MAGIC, 0) +#define GF_IOC_ENABLE_IRQ _IO(GF_IOC_MAGIC, 1) +#define GF_IOC_SETSPEED _IOW(GF_IOC_MAGIC, 2, unsigned int) +#define GF_IOC_RESET _IO(GF_IOC_MAGIC, 3) +#define GF_IOC_COOLBOOT _IO(GF_IOC_MAGIC, 4) +#define GF_IOC_SENDKEY _IOW(GF_IOC_MAGIC, 5, struct gf_key) +#define GF_IOC_CLK_READY _IO(GF_IOC_MAGIC, 6) +#define GF_IOC_CLK_UNREADY _IO(GF_IOC_MAGIC, 7) +#define GF_IOC_PM_FBCABCK _IO(GF_IOC_MAGIC, 8) +#define GF_IOC_SPI_TRANSFER _IOWR(GF_IOC_MAGIC, 9, struct gf_spi_transfer) +#define GF_IOC_FTM _IOW(GF_IOC_MAGIC, 101, int) +#define GF_IOC_SET_MODE _IOW(GF_IOC_MAGIC, 102, int) +#define GF_IOC_MAXNR 10 +#define GF_IOC_FTM _IOW(GF_IOC_MAGIC, 101, int) +#define GF_IOC_SET_MODE _IOW(GF_IOC_MAGIC, 102, int) + +#define MTK_SPI_ALIGN_MASK_NUM 10 +#define MTK_SPI_ALIGN_MASK ((0x1 << MTK_SPI_ALIGN_MASK_NUM) - 1) + + +#define GFX1XM_IMAGE_MODE (0x00) +#define GFX1XM_KEY_MODE (0x01) +#define GFX1XM_SLEEP_MODE (0x02) +#define GFX1XM_FF_MODE (0x03) +#define GFX1XM_DEBUG_MODE (0x56) + +/**************************debug******************************/ +#define DEFAULT_DEBUG (0) +#define SUSPEND_DEBUG (1) +#define SPI_DEBUG (2) +#define TIME_DEBUG (3) +#define FLOW_DEBUG (4) + +#define GF_DEBUG +/*#undef GF_DEBUG*/ + +#ifdef GF_DEBUG +#define gf_print(fmt, args...) do { \ + printk(KERN_ERR"gf5216_spi:" fmt, ##args);\ + } while (0) + +//#define gf_print(args...) printk(KERN_ERR "gf_spi:" ##args) +#define FUNC_ENTRY() printk(KERN_ERR "gf5216_spi:%s, entry\n", __func__) +#define FUNC_EXIT() printk(KERN_ERR "gf5216_spi:%s, exit\n", __func__) +#else +#define gf_print(fmt, args...) +#define FUNC_ENTRY() +#define FUNC_EXIT() +#endif + +typedef enum { + GF_IMAGE_MODE = 0, + GF_KEY_MODE, + GF_SLEEP_MODE, + GF_FF_MODE, + GF_DEBUG_MODE = 0x56 +}MODE; + +struct gf_key { + unsigned int key; + int value; +}; + +struct gf_spi_transfer { + unsigned char cmd; + unsigned char reserve; + unsigned short addr; + unsigned int len; + unsigned long buf; +}; + +typedef struct +{ + unsigned short id; + unsigned short raw[SENSOR_COL]; + unsigned short crc; +} gx_row_t; + +typedef enum { + SPEED_500KHZ=0, + SPEED_1MHZ, + SPEED_2MHZ, + SPEED_3MHZ, + SPEED_4MHZ, + SPEED_6MHZ, + SPEED_8MHZ, + SPEED_KEEP, + SPEED_UNSUPPORTED +}SPI_SPEED; + +#define GF_VDD_MIN_UV 2800000 +#define GF_VDD_MAX_UV 2800000 +#define GF_VIO_MIN_UV 1800000 +#define GF_VIO_MAX_UV 1800000 + +#define USE_SPI_BUS 1 +#define GF_FASYNC 1//If support fasync mechanism. +struct gf_dev { + dev_t devt; + spinlock_t spi_lock; + struct list_head device_entry; +#if defined(USE_SPI_BUS) + struct spi_device *spi; +#elif defined(USE_PLATFORM_BUS) + struct platform_device *spi; +#endif + struct clk *core_clk; + struct clk *iface_clk; + + struct input_dev *input; + /* buffer is NULL unless this device is open (users > 0) */ + unsigned users; + signed irq_gpio; + signed reset_gpio; + signed pwr_gpio; + int irq; + int irq_enabled; + int clk_enabled; +#ifdef GF_FASYNC + struct fasync_struct *async; +#endif + struct notifier_block notifier; + char device_available; + char fb_black; + unsigned char *gBuffer; + struct mutex buf_lock; + + unsigned int isPowerOn; + struct regulator *avdd; + struct pinctrl *pinctrl1; + struct pinctrl_state *pins_default; + struct pinctrl_state *eint_as_int, *eint_in_low, *eint_in_high, *eint_in_float, + *fp_rst_low, *fp_rst_high, + *miso_pull_up,*miso_pull_disable,*fp_enable_high,*fp_enable_low,*fp_enable1v8_high,*fp_enable1v8_low; + +}; + +extern unsigned int irq_of_parse_and_map(struct device_node *node, int index); +void gf_cleanup(struct gf_dev *gf_dev); +int gf_parse_dts(struct gf_dev* pdev); +int gf_power_init(struct gf_dev* pdev); +int gf_power_on(struct gf_dev *pdev); +int gf_power_off(struct gf_dev *pdev); +int gf_irq_num(struct gf_dev *pdev); +int gf_hw_reset(struct gf_dev *pdev, unsigned int delay_ms); +void gf_gpio_as_int(struct gf_dev *pdev); + +void gf_power_output(struct gf_dev *pdev, int level); +void gf_miso_pullup(struct gf_dev *pdev, int enable); +void gf_reset_output(struct gf_dev *pdev, int level); +void gf_irq_pulldown(struct gf_dev *pdev, int enable); + +int gf_power_on(struct gf_dev *gf_dev); +int gf_power_off(struct gf_dev *gf_dev); + +int gf_hw_reset(struct gf_dev *gf_dev, unsigned int delay_ms); +int gf_irq_num(struct gf_dev *gf_dev); + +#endif /*__GF_SPI_H*/ diff --git a/drivers/input/fingerprint/gx556/platform.c b/drivers/input/fingerprint/gx556/platform.c new file mode 100755 index 000000000..34734ed86 --- /dev/null +++ b/drivers/input/fingerprint/gx556/platform.c @@ -0,0 +1,325 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "gf_spi.h" + +#include +#include + + +/**********************************************************************************/ +static DEFINE_MUTEX(gf_set_gpio_mutex); + +/**********************************************************************************/ + +int gf_parse_dts(struct gf_dev* pdev) +{ + int ret = -1; + + FUNC_ENTRY(); + + if(pdev->spi->dev.of_node == NULL) { + pdev->spi->dev.of_node = of_find_compatible_node(NULL, NULL, "mediatek,goodix"); + } + + if(pdev->spi->dev.of_node) { + pdev->pinctrl1 = devm_pinctrl_get(&pdev->spi->dev); + if (IS_ERR(pdev->pinctrl1)) { + ret = PTR_ERR(pdev->pinctrl1); + gf_print(" Cannot find fp pinctrl1!\n"); + return ret; + } + + pdev->fp_rst_high = pinctrl_lookup_state(pdev->pinctrl1, "fp_rst_high"); + if (IS_ERR(pdev->fp_rst_high)) { + ret = PTR_ERR(pdev->fp_rst_high); + dev_err(&pdev->spi->dev, " Cannot find fp pinctrl fp_rst_high!\n"); + return ret; + } + pdev->fp_rst_low = pinctrl_lookup_state(pdev->pinctrl1, "fp_rst_low"); + if (IS_ERR(pdev->fp_rst_low)) { + ret = PTR_ERR(pdev->fp_rst_low); + dev_err(&pdev->spi->dev, " Cannot find fp pinctrl fp_rst_low!\n"); + return ret; + } + pdev->eint_as_int = pinctrl_lookup_state(pdev->pinctrl1, "eint_as_int"); + if (IS_ERR(pdev->eint_as_int)) { + ret = PTR_ERR(pdev->eint_as_int); + dev_err(&pdev->spi->dev, " Cannot find fp pinctrl eint_as_int!\n"); + return ret; + } + pdev->eint_in_low = pinctrl_lookup_state(pdev->pinctrl1, "eint_in_low"); + if (IS_ERR(pdev->eint_in_low)) { + ret = PTR_ERR(pdev->eint_in_low); + dev_err(&pdev->spi->dev, " Cannot find fp pinctrl eint_output_low!\n"); + return ret; + } + pdev->eint_in_high= pinctrl_lookup_state(pdev->pinctrl1, "eint_in_high"); + if (IS_ERR(pdev->eint_in_high)) { + ret = PTR_ERR(pdev->eint_in_high); + dev_err(&pdev->spi->dev, " Cannot find fp pinctrl eint_in_high!\n"); + return ret; + } + + pdev->eint_in_float = pinctrl_lookup_state(pdev->pinctrl1, "eint_in_float"); + if (IS_ERR(pdev->eint_in_float)) { + ret = PTR_ERR(pdev->eint_in_float); + dev_err(&pdev->spi->dev, " Cannot find fp pinctrl eint_output_high!\n"); + return ret; + } + pdev->miso_pull_up = pinctrl_lookup_state(pdev->pinctrl1, "miso_pull_up"); + if (IS_ERR(pdev->miso_pull_up)) { + ret = PTR_ERR(pdev->miso_pull_up); + dev_err(&pdev->spi->dev, " Cannot find fp pinctrl miso_pull_up!\n"); + return ret; + } + pdev->miso_pull_disable = pinctrl_lookup_state(pdev->pinctrl1, "miso_pull_disable"); + if (IS_ERR(pdev->miso_pull_disable)) { + ret = PTR_ERR(pdev->miso_pull_disable); + dev_err(&pdev->spi->dev, " Cannot find fp pinctrl miso_pull_disable!\n"); + return ret; + } + pdev->fp_enable_high = pinctrl_lookup_state(pdev->pinctrl1, "fp_enable_high"); + if (IS_ERR(pdev->fp_enable_high)) { + ret = PTR_ERR(pdev->fp_enable_high); + dev_err(&pdev->spi->dev, " Cannot find fp pinctrl fp_enable_high!\n"); + return ret; + } + pdev->fp_enable_low = pinctrl_lookup_state(pdev->pinctrl1, "fp_enable_low"); + if (IS_ERR(pdev->fp_enable_low)) { + ret = PTR_ERR(pdev->fp_enable_low); + dev_err(&pdev->spi->dev, " Cannot find fp pinctrl fp_enable_low!\n"); + return ret; + } + pdev->fp_enable1v8_high = pinctrl_lookup_state(pdev->pinctrl1, "fp_enable1v8_high"); + if (IS_ERR(pdev->fp_enable1v8_high)) { + ret = PTR_ERR(pdev->fp_enable1v8_high); + dev_err(&pdev->spi->dev, " Cannot find fp pinctrl fp_enable1v8_high!\n"); + return ret; + } + pdev->fp_enable1v8_low = pinctrl_lookup_state(pdev->pinctrl1, "fp_enable1v8_low"); + if (IS_ERR(pdev->fp_enable1v8_low)) { + ret = PTR_ERR(pdev->fp_enable1v8_low); + dev_err(&pdev->spi->dev, " Cannot find fp pinctrl fp_enable1v8_low!\n"); + return ret; + } + + + } + else { + gf_print(" pdev->spi->dev.of_node is null!\n"); + return ret; + } + return 0; +} + +static int gf_power_ctl(struct gf_dev* pdev, int on) +{ + //int rc = 0; + + FUNC_ENTRY(); + if((on && !pdev->isPowerOn) || (!on && pdev->isPowerOn)) { + if(on) + { + gf_reset_output(pdev,0); + gf_irq_pulldown(pdev,0); + + gf_power_output(pdev,1); + /*rc = regulator_enable(pdev->avdd); + if (rc) { + gf_print("regulator_enable() failed!\n"); + return rc; + } + */ + udelay(400); + + gf_reset_output(pdev,1); + udelay(100); + + gf_irq_pulldown(pdev,1); + msleep(20); + + gf_irq_pulldown(pdev,0); + msleep(10); + + gf_irq_pulldown(pdev,-1); + msleep(1); + gf_reset_output(pdev,1); + msleep(60); + pdev->isPowerOn = 1; + } + else + { + gf_irq_pulldown(pdev,1); + gf_reset_output(pdev,0); + msleep(10); + gf_power_output(pdev,0); + /*rc = regulator_disable(pdev->avdd); + if (rc) { + gf_print("regulator_disable() failed!\n"); + return rc; + } + */ + msleep(50); + pdev->isPowerOn = 0; + } + } + else { + gf_print("Ignore %d to %d\n", on, pdev->isPowerOn); + } + return 0; +} + +void gf_cleanup(struct gf_dev * pdev) { } + +int gf_power_init(struct gf_dev* pdev) +{ + //int ret = 0; + + FUNC_ENTRY(); + /* pdev->avdd = regulator_get(&pdev->spi->dev, "vfp"); + if(IS_ERR(pdev->avdd)) { + ret = PTR_ERR(pdev->avdd); + gf_print("Regulator get failed vdd ret=%d\n", ret); + return ret; + } + + ret = regulator_set_voltage(pdev->avdd, GF_VDD_MIN_UV, GF_VDD_MAX_UV); + if (ret) { + gf_print("regulator_set_voltage(%d) failed!\n", ret); + goto reg_vdd_put; + } + */ + return 0; +} + +int gf_power_on(struct gf_dev *pdev) +{ + int rc; + + FUNC_ENTRY(); + + rc = gf_power_ctl(pdev, 1); + msleep(10); + return rc; +} + +int gf_power_off(struct gf_dev *pdev) +{ + FUNC_ENTRY(); + + return gf_power_ctl(pdev, 0); +} + +int gf_hw_reset(struct gf_dev *pdev, unsigned int delay_ms) +{ + gf_miso_pullup(pdev, 1); + gf_reset_output(pdev, 0); + msleep(5); + gf_reset_output(pdev, 1); + if(delay_ms){ + msleep(delay_ms); + } + + gf_miso_pullup(pdev, 0); + return 0; +} + +int gf_irq_num(struct gf_dev *pdev) +{ + //int ret; + u32 ints[2] = {0, 0}; + struct device_node *node = pdev->spi->dev.of_node; + + FUNC_ENTRY(); + + if(node) { + of_property_read_u32_array(node, "debounce", ints, ARRAY_SIZE(ints)); + gpio_request(ints[0], "fingerprint-irq"); + gpio_set_debounce(ints[0], ints[1]); + gf_print("gpio_set_debounce ints[0] = %d, ints[1] = %d!!\n", ints[0], ints[1]); + + pdev->spi->irq = irq_of_parse_and_map(node, 0); + + gf_print(" %s: irq num = %d\n", __func__, pdev->spi->irq); + if(! pdev->spi->irq) { + gf_print("%s: gf_irq_num fail!!\n", __func__); + return -1; + } + } + else { + gf_print("%s: null node!!\n",__func__); + return -1; + } + return 0; +} + +void gf_gpio_as_int(struct gf_dev *pdev) +{ + mutex_lock(&gf_set_gpio_mutex); + pinctrl_select_state(pdev->pinctrl1, pdev->eint_as_int); + mutex_unlock(&gf_set_gpio_mutex); +} + +void gf_reset_output(struct gf_dev *pdev, int level) +{ + mutex_lock(&gf_set_gpio_mutex); + //gf_print("%s: level = %d\n", __func__,level); + + if (level) + pinctrl_select_state(pdev->pinctrl1, pdev->fp_rst_high); + else + pinctrl_select_state(pdev->pinctrl1, pdev->fp_rst_low); + mutex_unlock(&gf_set_gpio_mutex); +} +void gf_power_output(struct gf_dev *pdev, int level) +{ + mutex_lock(&gf_set_gpio_mutex); + //gf_print("%s: level = %d\n", __func__,level); + + if (level) + { + pinctrl_select_state(pdev->pinctrl1, pdev->fp_enable_high); + pinctrl_select_state(pdev->pinctrl1, pdev->fp_enable1v8_high); + } + else + { + pinctrl_select_state(pdev->pinctrl1, pdev->fp_enable_low); + pinctrl_select_state(pdev->pinctrl1, pdev->fp_enable1v8_low); + } + mutex_unlock(&gf_set_gpio_mutex); +} + + void gf_irq_pulldown(struct gf_dev *pdev, int enable) +{ + mutex_lock(&gf_set_gpio_mutex); + //gf_print("%s: enable = %d\n", __func__,enable); + + if (enable == 1) { + pinctrl_select_state(pdev->pinctrl1, pdev->eint_in_low); + } + else if(enable == 0) { + pinctrl_select_state(pdev->pinctrl1, pdev->eint_in_high); + } + else if(enable == -1) { + pinctrl_select_state(pdev->pinctrl1, pdev->eint_in_float); + } + mutex_unlock(&gf_set_gpio_mutex); +} + +void gf_miso_pullup(struct gf_dev *pdev, int enable) +{ + mutex_lock(&gf_set_gpio_mutex); + //gf_print("%s: enable = %d\n", __func__,enable); + + if (enable) + pinctrl_select_state(pdev->pinctrl1, pdev->miso_pull_up); + else + pinctrl_select_state(pdev->pinctrl1, pdev->miso_pull_disable); + mutex_unlock(&gf_set_gpio_mutex); +} diff --git a/drivers/misc/mediatek/alsps/stk3x1x-new/stk3x1x.c b/drivers/misc/mediatek/alsps/stk3x1x-new/stk3x1x.c index e1b8a912f..9d3c1c94a 100755 --- a/drivers/misc/mediatek/alsps/stk3x1x-new/stk3x1x.c +++ b/drivers/misc/mediatek/alsps/stk3x1x-new/stk3x1x.c @@ -1,3060 +1,5922 @@ -/* -* Copyright(C)2014 MediaTek Inc. -* Modification based on code covered by the below mentioned copyright -* and/or permission notice(S). -*/ - -/* - * Author: MingHsien Hsieh - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include "stk3x1x.h" - -#ifdef CONFIG_CUSTOM_KERNEL_SENSORHUB -#include -#endif - -/* #define STK_PS_POLLING_LOG */ -/* #define STK_FIR */ -/* #define STK_IRS */ -/* #include */ - -#define DRIVER_VERSION "3.1.2.1nk" - -/*------------------------- define-------------------------------*/ -#define POWER_NONE_MACRO MT65XX_POWER_NONE - -/****************************************************************************** - * configuration -*******************************************************************************/ -#define PSCTRL_VAL 0x71 /* ps_persistance=4, ps_gain=64X, PS_IT=0.391ms */ -#define ALSCTRL_VAL 0x38 /* als_persistance=1, als_gain=64X, ALS_IT=50ms */ -#define LEDCTRL_VAL 0xFF /* 100mA IRDR, 64/64 LED duty */ -#define WAIT_VAL 0x7 /* 50 ms */ - -/*----------------------------------------------------------------------------*/ -#define stk3x1x_DEV_NAME "stk3x1x" -/*----------------------------------------------------------------------------*/ -#define APS_TAG "[ALS/PS] " -#define APS_FUN(f) pr_debug(APS_TAG"%s\n", __func__) -#define APS_ERR(fmt, args...) pr_err(APS_TAG"%s %d : "fmt, __func__, __LINE__, ##args) -#define APS_LOG(fmt, args...) pr_debug(APS_TAG fmt, ##args) -#define APS_DBG(fmt, args...) pr_debug(fmt, ##args) -/****************************************************************************** - * extern functions -*******************************************************************************/ -/*----------------------------------------------------------------------------*/ -#define STK2213_PID 0x23 -#define STK2213I_PID 0x22 -#define STK3010_PID 0x33 -#define STK3210_STK3310_PID 0x13 -#define STK3211_STK3311_PID 0x12 - -#define STK_IRC_MAX_ALS_CODE 20000 -#define STK_IRC_MIN_ALS_CODE 25 -#define STK_IRC_MIN_IR_CODE 50 -#define STK_IRC_ALS_DENOMI 2 -#define STK_IRC_ALS_NUMERA 5 -#define STK_IRC_ALS_CORREC 748 - -/*----------------------------------------------------------------------------*/ -static struct i2c_client *stk3x1x_i2c_client; -struct alsps_hw alsps_cust; -static struct alsps_hw *hw = &alsps_cust; - -/*----------------------------------------------------------------------------*/ -static const struct i2c_device_id stk3x1x_i2c_id[] = { {stk3x1x_DEV_NAME, 0}, {} }; - -/*----------------------------------------------------------------------------*/ -static int stk3x1x_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id); -static int stk3x1x_i2c_remove(struct i2c_client *client); -/*----------------------------------------------------------------------------*/ -//static int stk3x1x_i2c_suspend(struct i2c_client *client, pm_message_t msg); -//static int stk3x1x_i2c_resume(struct i2c_client *client); -static struct stk3x1x_priv *g_stk3x1x_ptr; -static unsigned long long int_top_time; -#define C_I2C_FIFO_SIZE 8 - -static DEFINE_MUTEX(STK3X1X_i2c_mutex); -static int stk3x1x_init_flag = -1; /* 0<==>OK -1 <==> fail */ -static int stk3x1x_local_init(void); -static int stk3x1x_local_uninit(void); -static struct alsps_init_info stk3x1x_init_info = { - .name = "stk3x1x", - .init = stk3x1x_local_init, - .uninit = stk3x1x_local_uninit, -}; - - -/*----------------------------------------------------------------------------*/ -typedef enum { - STK_TRC_ALS_DATA = 0x0001, - STK_TRC_PS_DATA = 0x0002, - STK_TRC_EINT = 0x0004, - STK_TRC_IOCTL = 0x0008, - STK_TRC_I2C = 0x0010, - STK_TRC_CVT_ALS = 0x0020, - STK_TRC_CVT_PS = 0x0040, - STK_TRC_DEBUG = 0x8000, -} STK_TRC; -/*----------------------------------------------------------------------------*/ -typedef enum { - STK_BIT_ALS = 1, - STK_BIT_PS = 2, -} STK_BIT; -/*----------------------------------------------------------------------------*/ -struct stk3x1x_i2c_addr { -/*define a series of i2c slave address*/ - u8 state; /* enable/disable state */ - u8 psctrl; /* PS control */ - u8 alsctrl; /* ALS control */ - u8 ledctrl; /* LED control */ - u8 intmode; /* INT mode */ - u8 wait; /* wait time */ - u8 thdh1_ps; /* PS INT threshold high 1 */ - u8 thdh2_ps; /* PS INT threshold high 2 */ - u8 thdl1_ps; /* PS INT threshold low 1 */ - u8 thdl2_ps; /* PS INT threshold low 2 */ - u8 thdh1_als; /* ALS INT threshold high 1 */ - u8 thdh2_als; /* ALS INT threshold high 2 */ - u8 thdl1_als; /* ALS INT threshold low 1 */ - u8 thdl2_als; /* ALS INT threshold low 2 */ - u8 flag; /* int flag */ - u8 data1_ps; /* ps data1 */ - u8 data2_ps; /* ps data2 */ - u8 data1_als; /* als data1 */ - u8 data2_als; /* als data2 */ - u8 data1_offset; /* offset data1 */ - u8 data2_offset; /* offset data2 */ - u8 data1_ir; /* ir data1 */ - u8 data2_ir; /* ir data2 */ - u8 soft_reset; /* software reset */ -}; -/*----------------------------------------------------------------------------*/ -#ifdef STK_FIR -struct data_filter { - s16 raw[8]; - int sum; - int num; - int idx; -}; -#endif - -struct stk3x1x_priv { - struct alsps_hw *hw; - struct i2c_client *client; - struct delayed_work eint_work; - - /*i2c address group */ - struct stk3x1x_i2c_addr addr; - - struct device_node *irq_node; - int irq; - - /*misc */ - atomic_t trace; - atomic_t i2c_retry; - atomic_t als_suspend; - atomic_t als_debounce; /*debounce time after enabling als */ - atomic_t als_deb_on; /*indicates if the debounce is on */ - atomic_t als_deb_end; /*the jiffies representing the end of debounce */ - atomic_t ps_mask; /*mask ps: always return far away */ - atomic_t ps_debounce; /*debounce time after enabling ps */ - atomic_t ps_deb_on; /*indicates if the debounce is on */ - atomic_t ps_deb_end; /*the jiffies representing the end of debounce */ - atomic_t ps_suspend; - - - /*data */ - u16 als; - u16 ps; - u8 _align; - u16 als_level_num; - u16 als_value_num; - u32 als_level[C_CUST_ALS_LEVEL - 1]; - u32 als_value[C_CUST_ALS_LEVEL]; - int ps_cali; - - atomic_t state_val; - atomic_t psctrl_val; - atomic_t alsctrl_val; - u8 wait_val; - u8 ledctrl_val; - u8 int_val; - - atomic_t ps_high_thd_val; /*the cmd value can't be read, stored in ram */ - atomic_t ps_low_thd_val; /*the cmd value can't be read, stored in ram */ - ulong enable; /*enable mask */ - ulong pending_intr; /*pending interrupt */ - atomic_t recv_reg; - /*early suspend */ - bool first_boot; -#ifdef STK_FIR - struct data_filter fir; -#endif - uint16_t ir_code; - uint16_t als_correct_factor; -}; -/*----------------------------------------------------------------------------*/ -#ifdef CONFIG_OF -static const struct of_device_id alsps_of_match[] = { - {.compatible = "mediatek,alsps"}, - {}, -}; -#endif - -static struct i2c_driver stk3x1x_i2c_driver = { - .probe = stk3x1x_i2c_probe, - .remove = stk3x1x_i2c_remove, - .id_table = stk3x1x_i2c_id, - .driver = { - .name = stk3x1x_DEV_NAME, -#ifdef CONFIG_OF - .of_match_table = alsps_of_match, -#endif - }, -}; - -#if defined(CONFIG_CUSTOM_KERNEL_SENSORHUB) -struct stk_raw_data { - u8 raw_bytes[PACKAGE_SIZE]; - u16 ps_raw; - u16 ps_state; - u16 ps_int_state; - u16 als_ch0_raw; - u16 als_ch1_raw; -}; -static struct stk_raw_data gRawData; -#endif - -static struct stk3x1x_priv *stk3x1x_obj; -static int stk3x1x_get_ps_value(struct stk3x1x_priv *obj, u16 ps); -static int stk3x1x_get_ps_value_only(struct stk3x1x_priv *obj, u16 ps); -static int stk3x1x_get_als_value(struct stk3x1x_priv *obj, u16 als); -static int stk3x1x_read_als(struct i2c_client *client, u16 *data); -static int stk3x1x_read_ps(struct i2c_client *client, u16 *data); -/*static int stk3x1x_set_als_int_thd(struct i2c_client *client, u16 als_data_reg);*/ -static int32_t stk3x1x_get_ir_value(struct stk3x1x_priv *obj); -struct wake_lock ps_lock; - -/*----------------------------------------------------------------------------*/ -int stk3x1x_get_addr(struct alsps_hw *hw, struct stk3x1x_i2c_addr *addr) -{ - if (!hw || !addr) - return -EFAULT; - - addr->state = STK_STATE_REG; - addr->psctrl = STK_PSCTRL_REG; - addr->alsctrl = STK_ALSCTRL_REG; - addr->ledctrl = STK_LEDCTRL_REG; - addr->intmode = STK_INT_REG; - addr->wait = STK_WAIT_REG; - addr->thdh1_ps = STK_THDH1_PS_REG; - addr->thdh2_ps = STK_THDH2_PS_REG; - addr->thdl1_ps = STK_THDL1_PS_REG; - addr->thdl2_ps = STK_THDL2_PS_REG; - addr->thdh1_als = STK_THDH1_ALS_REG; - addr->thdh2_als = STK_THDH2_ALS_REG; - addr->thdl1_als = STK_THDL1_ALS_REG; - addr->thdl2_als = STK_THDL2_ALS_REG; - addr->flag = STK_FLAG_REG; - addr->data1_ps = STK_DATA1_PS_REG; - addr->data2_ps = STK_DATA2_PS_REG; - addr->data1_als = STK_DATA1_ALS_REG; - addr->data2_als = STK_DATA2_ALS_REG; - addr->data1_offset = STK_DATA1_OFFSET_REG; - addr->data2_offset = STK_DATA2_OFFSET_REG; - addr->data1_ir = STK_DATA1_IR_REG; - addr->data2_ir = STK_DATA2_IR_REG; - addr->soft_reset = STK_SW_RESET_REG; - - return 0; -} - -/*----------------------------------------------------------------------------*/ -static int stk3x1x_i2c_read_block(struct i2c_client *client, u8 addr, u8 *data, u8 len) -{ - int err; - u8 beg = addr; - struct i2c_msg msgs[2] = { - { - .addr = client->addr, - .flags = 0, - .len = 1, - .buf = &beg, }, - { - .addr = client->addr, - .flags = I2C_M_RD, - .len = len, - .buf = data, } - }; - - mutex_lock(&STK3X1X_i2c_mutex); - if (!client) { - mutex_unlock(&STK3X1X_i2c_mutex); - return -EINVAL; - } else if (len > C_I2C_FIFO_SIZE) { - mutex_unlock(&STK3X1X_i2c_mutex); - APS_LOG(" length %d exceeds %d\n", len, C_I2C_FIFO_SIZE); - return -EINVAL; - } - - err = i2c_transfer(client->adapter, msgs, sizeof(msgs) / sizeof(msgs[0])); - mutex_unlock(&STK3X1X_i2c_mutex); - if (err != 2) { - APS_LOG("i2c_transfer error: (%d %p %d) %d\n", addr, data, len, err); - err = -EIO; - } else { - err = 0; /*no error */ - } - return err; -} - -static int stk3x1x_i2c_write_block(struct i2c_client *client, u8 addr, u8 *data, u8 len) -{ - int err, idx, num; - char buf[C_I2C_FIFO_SIZE]; - - err = 0; - mutex_lock(&STK3X1X_i2c_mutex); - if (!client) { - mutex_unlock(&STK3X1X_i2c_mutex); - return -EINVAL; - } else if (len >= C_I2C_FIFO_SIZE) { - APS_ERR(" length %d exceeds %d\n", len, C_I2C_FIFO_SIZE); - mutex_unlock(&STK3X1X_i2c_mutex); - return -EINVAL; - } - - num = 0; - buf[num++] = addr; - for (idx = 0; idx < len; idx++) { - buf[num++] = data[idx]; - } - - err = i2c_master_send(client, buf, num); - if (err < 0) { - APS_ERR("send command error!!\n"); - mutex_unlock(&STK3X1X_i2c_mutex); - return -EFAULT; - } - mutex_unlock(&STK3X1X_i2c_mutex); - return err; -} - -/*----------------------------------------------------------------------------*/ -/*static int stk3x1x_get_timing(void) -{ - return 200; - - u32 base = I2C2_BASE; - return (__raw_readw(mt6516_I2C_HS) << 16) | (__raw_readw(mt6516_I2C_TIMING)); - -} -*/ -/*----------------------------------------------------------------------------*/ -static int stk3x1x_master_recv(struct i2c_client *client, u16 addr, u8 *buf, int count) -{ - struct stk3x1x_priv *obj = i2c_get_clientdata(client); - int ret = 0, retry = 0; - int trc = atomic_read(&obj->trace); - int max_try = atomic_read(&obj->i2c_retry); - - while (retry++ < max_try) { - ret = stk3x1x_i2c_read_block(client, addr, buf, count); - if (ret == 0) - break; - udelay(100); - } - - if (unlikely(trc)) { - if ((retry != 1) && (trc & STK_TRC_DEBUG)) { - APS_LOG("(recv) %d/%d\n", retry - 1, max_try); - - } - } - - /* If everything went ok (i.e. 1 msg transmitted), return #bytes - transmitted, else error code. */ - return (ret == 0) ? count : ret; -} - -/*----------------------------------------------------------------------------*/ -static int stk3x1x_master_send(struct i2c_client *client, u16 addr, u8 *buf, int count) -{ - int ret = 0, retry = 0; - struct stk3x1x_priv *obj = i2c_get_clientdata(client); - int trc = atomic_read(&obj->trace); - int max_try = atomic_read(&obj->i2c_retry); - - - while (retry++ < max_try) { - ret = stk3x1x_i2c_write_block(client, addr, buf, count); - if (ret == 0) - break; - udelay(100); - } - - if (unlikely(trc)) { - if ((retry != 1) && (trc & STK_TRC_DEBUG)) { - APS_LOG("(send) %d/%d\n", retry - 1, max_try); - } - } - /* If everything went ok (i.e. 1 msg transmitted), return #bytes - transmitted, else error code. */ - return (ret == 0) ? count : ret; -} - -/*----------------------------------------------------------------------------*/ -static int stk3x1x_write_led(struct i2c_client *client, u8 data) -{ - struct stk3x1x_priv *obj = i2c_get_clientdata(client); - int ret = 0; - - ret = stk3x1x_master_send(client, obj->addr.ledctrl, &data, 1); - if (ret < 0) { - APS_ERR("write led = %d\n", ret); - return -EFAULT; - } - - return 0; -} - -/*----------------------------------------------------------------------------*/ -int stk3x1x_read_als(struct i2c_client *client, u16 *data) -{ - struct stk3x1x_priv *obj = i2c_get_clientdata(client); - int ret = 0; - u8 buf[2]; - int32_t als_comperator; - u16 als_data; -#ifdef STK_FIR - int idx; -#endif - if (NULL == client) { - return -EINVAL; - } - ret = stk3x1x_master_recv(client, obj->addr.data1_als, buf, 0x02); - if (ret < 0) { - APS_DBG("error: %d\n", ret); - return -EFAULT; - } else { - als_data = (buf[0] << 8) | (buf[1]); -#ifdef STK_FIR - if (obj->fir.num < 8) { - obj->fir.raw[obj->fir.num] = als_data; - obj->fir.sum += als_data; - obj->fir.num++; - obj->fir.idx++; - } else { - idx = obj->fir.idx % 8; - obj->fir.sum -= obj->fir.raw[idx]; - obj->fir.raw[idx] = als_data; - obj->fir.sum += als_data; - obj->fir.idx++; - als_data = obj->fir.sum / 8; - } -#endif - } - - if (obj->ir_code) { - obj->als_correct_factor = 1000; - if (als_data < STK_IRC_MAX_ALS_CODE && als_data > STK_IRC_MIN_ALS_CODE && - obj->ir_code > STK_IRC_MIN_IR_CODE) { - als_comperator = als_data * STK_IRC_ALS_NUMERA / STK_IRC_ALS_DENOMI; - if (obj->ir_code > als_comperator) - obj->als_correct_factor = STK_IRC_ALS_CORREC; - } - APS_LOG("%s: als=%d, ir=%d, als_correct_factor=%d", __func__, als_data, - obj->ir_code, obj->als_correct_factor); - obj->ir_code = 0; - } - *data = als_data * obj->als_correct_factor / 1000; - - if (atomic_read(&obj->trace) & STK_TRC_ALS_DATA) { - APS_DBG("ALS: 0x%04X\n", (u32) (*data)); - } - - return 0; -} - -/*----------------------------------------------------------------------------*/ -int stk3x1x_write_als(struct i2c_client *client, u8 data) -{ - struct stk3x1x_priv *obj = i2c_get_clientdata(client); - int ret = 0; - - ret = stk3x1x_master_send(client, obj->addr.alsctrl, &data, 1); - if (ret < 0) { - APS_ERR("write als = %d\n", ret); - return -EFAULT; - } - - return 0; -} - -/*----------------------------------------------------------------------------*/ -int stk3x1x_read_flag(struct i2c_client *client, u8 *data) -{ - struct stk3x1x_priv *obj = i2c_get_clientdata(client); - int ret = 0; - u8 buf; - - if (NULL == client) { - return -EINVAL; - } - ret = stk3x1x_master_recv(client, obj->addr.flag, &buf, 0x01); - if (ret < 0) { - APS_DBG("error: %d\n", ret); - return -EFAULT; - } else { - *data = buf; - } - - if (atomic_read(&obj->trace) & STK_TRC_ALS_DATA) { - APS_DBG("PS NF flag: 0x%04X\n", (u32) (*data)); - } - - return 0; -} - -/*----------------------------------------------------------------------------*/ -int stk3x1x_read_id(struct i2c_client *client) -{ - int ret = 0; - u8 buf[2]; - - if (NULL == client) { - return -EINVAL; - } - ret = stk3x1x_master_recv(client, STK_PDT_ID_REG, buf, 0x02); - if (ret < 0) { - APS_DBG("error: %d\n", ret); - return -EFAULT; - } - APS_LOG("%s: PID=0x%d, VID=0x%x\n", __func__, buf[0], buf[1]); - - if (buf[1] == 0xC0) - APS_LOG("%s: RID=0xC0!!!!!!!!!!!!!\n", __func__); - - switch (buf[0]) { - case STK2213_PID: - case STK2213I_PID: - case STK3010_PID: - case STK3210_STK3310_PID: - case STK3211_STK3311_PID: - return 0; - case 0x0: - APS_ERR("PID=0x0, please make sure the chip is stk3x1x!\n"); - return -2; - default: - APS_ERR("%s: invalid PID(%#x)\n", __func__, buf[0]); - return -1; - } - return 0; -} - -/*----------------------------------------------------------------------------*/ -int stk3x1x_read_ps(struct i2c_client *client, u16 *data) -{ - struct stk3x1x_priv *obj = i2c_get_clientdata(client); - int ret = 0; - u8 buf[2]; - - if (NULL == client) { - APS_ERR("i2c client is NULL\n"); - return -EINVAL; - } - ret = stk3x1x_master_recv(client, obj->addr.data1_ps, buf, 0x02); - if (ret < 0) { - APS_DBG("error: %d\n", ret); - return -EFAULT; - } else { - if (((buf[0] << 8) | (buf[1])) < obj->ps_cali) - *data = 0; - else - *data = ((buf[0] << 8) | (buf[1])) - obj->ps_cali; - } - - if (atomic_read(&obj->trace) & STK_TRC_ALS_DATA) { - APS_DBG("PS: 0x%04X\n", (u32) (*data)); - } - - return 0; -} - -/*----------------------------------------------------------------------------*/ -int stk3x1x_write_ps(struct i2c_client *client, u8 data) -{ - struct stk3x1x_priv *obj = i2c_get_clientdata(client); - int ret = 0; - - ret = stk3x1x_master_send(client, obj->addr.psctrl, &data, 1); - if (ret < 0) { - APS_ERR("write ps = %d\n", ret); - return -EFAULT; - } - return 0; -} - -/*----------------------------------------------------------------------------*/ -int stk3x1x_write_wait(struct i2c_client *client, u8 data) -{ - struct stk3x1x_priv *obj = i2c_get_clientdata(client); - int ret = 0; - - ret = stk3x1x_master_send(client, obj->addr.wait, &data, 1); - if (ret < 0) { - APS_ERR("write wait = %d\n", ret); - return -EFAULT; - } - return 0; -} - -/*----------------------------------------------------------------------------*/ -int stk3x1x_write_int(struct i2c_client *client, u8 data) -{ - struct stk3x1x_priv *obj = i2c_get_clientdata(client); - int ret = 0; - - ret = stk3x1x_master_send(client, obj->addr.intmode, &data, 1); - if (ret < 0) { - APS_ERR("write intmode = %d\n", ret); - return -EFAULT; - } - return 0; -} - -/*----------------------------------------------------------------------------*/ -int stk3x1x_write_state(struct i2c_client *client, u8 data) -{ - struct stk3x1x_priv *obj = i2c_get_clientdata(client); - int ret = 0; - - ret = stk3x1x_master_send(client, obj->addr.state, &data, 1); - if (ret < 0) { - APS_ERR("write state = %d\n", ret); - return -EFAULT; - } - return 0; -} - -/*----------------------------------------------------------------------------*/ -int stk3x1x_write_flag(struct i2c_client *client, u8 data) -{ - struct stk3x1x_priv *obj = i2c_get_clientdata(client); - int ret = 0; - - ret = stk3x1x_master_send(client, obj->addr.flag, &data, 1); - if (ret < 0) { - APS_ERR("write ps = %d\n", ret); - return -EFAULT; - } - return 0; -} - -/*----------------------------------------------------------------------------*/ -static int stk3x1x_write_sw_reset(struct i2c_client *client) -{ - struct stk3x1x_priv *obj = i2c_get_clientdata(client); - u8 buf = 0, r_buf = 0; - int ret = 0; - - buf = 0x7F; - ret = stk3x1x_master_send(client, obj->addr.wait, (char *)&buf, sizeof(buf)); - if (ret < 0) { - APS_ERR("i2c write test error = %d\n", ret); - return -EFAULT; - } - - ret = stk3x1x_master_recv(client, obj->addr.wait, &r_buf, 1); - if (ret < 0) { - APS_ERR("i2c read test error = %d\n", ret); - return -EFAULT; - } - - if (buf != r_buf) { - APS_ERR - ("i2c r/w test error, read-back value is not the same, write=0x%x, read=0x%x\n", - buf, r_buf); - return -EIO; - } - - buf = 0; - ret = stk3x1x_master_send(client, obj->addr.soft_reset, (char *)&buf, sizeof(buf)); - if (ret < 0) { - APS_ERR("write software reset error = %d\n", ret); - return -EFAULT; - } - mdelay(1); - return 0; -} - -/*----------------------------------------------------------------------------*/ -static int stk3x1x_write_ps_high_thd(struct i2c_client *client, u16 thd) -{ - struct stk3x1x_priv *obj = i2c_get_clientdata(client); - u8 buf[2]; - int ret = 0; - - buf[0] = (u8) ((0xFF00 & thd) >> 8); - buf[1] = (u8) (0x00FF & thd); - ret = stk3x1x_master_send(client, obj->addr.thdh1_ps, &buf[0], 1); - if (ret < 0) { - APS_ERR("WARNING: %d\n", ret); - return -EFAULT; - } - - ret = stk3x1x_master_send(client, obj->addr.thdh2_ps, &(buf[1]), 1); - if (ret < 0) { - APS_ERR("WARNING: %d\n", ret); - return -EFAULT; - } - - return 0; -} - -/*----------------------------------------------------------------------------*/ -static int stk3x1x_write_ps_low_thd(struct i2c_client *client, u16 thd) -{ - struct stk3x1x_priv *obj = i2c_get_clientdata(client); - u8 buf[2]; - int ret = 0; - - buf[0] = (u8) ((0xFF00 & thd) >> 8); - buf[1] = (u8) (0x00FF & thd); - ret = stk3x1x_master_send(client, obj->addr.thdl1_ps, &buf[0], 1); - if (ret < 0) { - APS_ERR("WARNING: %s: %d\n", __func__, ret); - return -EFAULT; - } - - ret = stk3x1x_master_send(client, obj->addr.thdl2_ps, &(buf[1]), 1); - if (ret < 0) { - APS_ERR("WARNING: %s: %d\n", __func__, ret); - return -EFAULT; - } - - return 0; -} - -/*----------------------------------------------------------------------------*/ -static int stk3x1x_write_als_high_thd(struct i2c_client *client, u16 thd) -{ - struct stk3x1x_priv *obj = i2c_get_clientdata(client); - u8 buf[2]; - int ret = 0; - - buf[0] = (u8) ((0xFF00 & thd) >> 8); - buf[1] = (u8) (0x00FF & thd); - ret = stk3x1x_master_send(client, obj->addr.thdh1_als, &buf[0], 1); - if (ret < 0) { - APS_ERR("WARNING: %s: %d\n", __func__, ret); - return -EFAULT; - } - - ret = stk3x1x_master_send(client, obj->addr.thdh2_als, &(buf[1]), 1); - if (ret < 0) { - APS_ERR("WARNING: %s: %d\n", __func__, ret); - return -EFAULT; - } - - return 0; -} - -/*----------------------------------------------------------------------------*/ -static int stk3x1x_write_als_low_thd(struct i2c_client *client, u16 thd) -{ - struct stk3x1x_priv *obj = i2c_get_clientdata(client); - u8 buf[2]; - int ret = 0; - - buf[0] = (u8) ((0xFF00 & thd) >> 8); - buf[1] = (u8) (0x00FF & thd); - ret = stk3x1x_master_send(client, obj->addr.thdl1_als, &buf[0], 1); - if (ret < 0) { - APS_ERR("WARNING: %s: %d\n", __func__, ret); - return -EFAULT; - } - - ret = stk3x1x_master_send(client, obj->addr.thdl2_als, &(buf[1]), 1); - if (ret < 0) { - APS_ERR("WARNING: %s: %d\n", __func__, ret); - return -EFAULT; - } - - return 0; -} - -/*----------------------------------------------------------------------------*/ -#if 0 -int stk3x1x_write_foffset(struct i2c_client *client, u16 ofset) -{ - struct stk3x1x_priv *obj = i2c_get_clientdata(client); - u8 buf[2]; - int ret = 0; - - buf[0] = (u8) ((0xFF00 & ofset) >> 8); - buf[1] = (u8) (0x00FF & ofset); - ret = stk3x1x_master_send(client, obj->addr.data1_offset, &buf[0], 1); - if (ret < 0) { - APS_ERR("WARNING: %s: %d\n", __func__, ret); - return -EFAULT; - } - - ret = stk3x1x_master_send(client, obj->addr.data2_offset, &(buf[1]), 1); - if (ret < 0) { - APS_ERR("WARNING: %s: %d\n", __func__, ret); - return -EFAULT; - } - - return 0; -} - -/*----------------------------------------------------------------------------*/ - -int stk3x1x_write_aoffset(struct i2c_client *client, u16 ofset) -{ - struct stk3x1x_priv *obj = i2c_get_clientdata(client); - u8 buf[2]; - int ret = 0; - u8 s_buf = 0, re_en; - - ret = stk3x1x_master_recv(client, obj->addr.state, &s_buf, 1); - if (ret < 0) { - APS_ERR("i2c read state error = %d\n", ret); - return -EFAULT; - } - re_en = (s_buf & STK_STATE_EN_AK_MASK) ? 1 : 0; - if (re_en) { - s_buf &= (~STK_STATE_EN_AK_MASK); - ret = stk3x1x_master_send(client, obj->addr.state, &s_buf, 1); - if (ret < 0) { - APS_ERR("write state = %d\n", ret); - return -EFAULT; - } - msleep(3); - } - - buf[0] = (u8) ((0xFF00 & ofset) >> 8); - buf[1] = (u8) (0x00FF & ofset); - ret = stk3x1x_master_send(client, 0x0E, &buf[0], 1); - if (ret < 0) { - APS_ERR("WARNING: %s: %d\n", __func__, ret); - return -EFAULT; - } - - ret = stk3x1x_master_send(client, 0x0F, &(buf[1]), 1); - if (ret < 0) { - APS_ERR("WARNING: %s: %d\n", __func__, ret); - return -EFAULT; - } - if (!re_en) - return 0; - s_buf |= STK_STATE_EN_AK_MASK; - ret = stk3x1x_master_send(client, obj->addr.state, &s_buf, 1); - if (ret < 0) { - APS_ERR("write state = %d\n", ret); - return -EFAULT; - } - return 0; -} -#endif -/*----------------------------------------------------------------------------*/ -static void stk3x1x_power(struct alsps_hw *hw, unsigned int on) -{ -/* - static unsigned int power_on; - - if (hw->power_id != POWER_NONE_MACRO) { - if (power_on == on) { - APS_LOG("ignore power control: %d\n", on); - } else if (on) { - if (!hwPowerOn(hw->power_id, hw->power_vol, "stk3x1x")) { - APS_ERR("power on fails!!\n"); - } - } else { - if (!hwPowerDown(hw->power_id, "stk3x1x")) { - APS_ERR("power off fail!!\n"); - } - } - } - power_on = on; -*/ -} - -/*----------------------------------------------------------------------------*/ -static int stk3x1x_enable_als(struct i2c_client *client, int enable) -{ - struct stk3x1x_priv *obj = i2c_get_clientdata(client); - int err, cur = 0; - int old = atomic_read(&obj->state_val); - int trc = atomic_read(&obj->trace); - - APS_LOG("%s: enable=%d\n", __func__, enable); - cur = old & (~(STK_STATE_EN_ALS_MASK | STK_STATE_EN_WAIT_MASK)); - if (enable) - cur |= STK_STATE_EN_ALS_MASK; - else if (old & STK_STATE_EN_PS_MASK) - cur |= STK_STATE_EN_WAIT_MASK; - - if (trc & STK_TRC_DEBUG) - APS_LOG("%s: %08X, %08X, %d\n", __func__, cur, old, enable); - - if (0 == (cur ^ old)) - return 0; -#ifdef STK_IRS - if (enable && !(old & STK_STATE_EN_PS_MASK)) { - err = stk3x1x_get_ir_value(obj); - if (err > 0) - obj->ir_code = err; - } -#endif - - if (enable && obj->hw->polling_mode_als == 0) { - stk3x1x_write_als_high_thd(client, 0x0); - stk3x1x_write_als_low_thd(client, 0xFFFF); - } - err = stk3x1x_write_state(client, cur); - if (err < 0) - return err; - else - atomic_set(&obj->state_val, cur); - - if (enable) { - if (obj->hw->polling_mode_als) { - atomic_set(&obj->als_deb_on, 1); - atomic_set(&obj->als_deb_end, - jiffies + atomic_read(&obj->als_debounce) * HZ / 1000); - } else { - schedule_delayed_work(&obj->eint_work, 220 * HZ / 1000); - } - } - - if (trc & STK_TRC_DEBUG) - APS_LOG("enable als (%d)\n", enable); - - return err; -} - -/*----------------------------------------------------------------------------*/ -static int stk3x1x_enable_ps(struct i2c_client *client, int enable) -{ - struct stk3x1x_priv *obj = i2c_get_clientdata(client); - int err, cur = 0, old = atomic_read(&obj->state_val); - int trc = atomic_read(&obj->trace); - int int_flag = 0; - - cur = old; - err = stk3x1x_write_ps_high_thd(client, atomic_read(&obj->ps_high_thd_val)); - if (err) { - APS_ERR("write high thd error: %d\n", err); - return err; - } - - err = stk3x1x_write_ps_low_thd(client, atomic_read(&obj->ps_low_thd_val)); - if (err) { - APS_ERR("write low thd error: %d\n", err); - return err; - } - - APS_LOG("%s: enable=%d\n", __func__, enable); - cur &= (~(0x45)); - if (enable) { - cur |= (STK_STATE_EN_PS_MASK); - if (!(old & STK_STATE_EN_ALS_MASK)) - cur |= STK_STATE_EN_WAIT_MASK; - if (1 == obj->hw->polling_mode_ps) - wake_lock(&ps_lock); - } else { - if (1 == obj->hw->polling_mode_ps) - wake_unlock(&ps_lock); - } - - if (trc & STK_TRC_DEBUG) { - APS_LOG("%s: %08X, %08X, %d\n", __func__, cur, old, enable); - } - - if (0 == (cur ^ old)) { - return 0; - } - - err = stk3x1x_write_state(client, cur); - if (err < 0) - return err; - else - atomic_set(&obj->state_val, cur); - - if (enable) { - if (obj->hw->polling_mode_ps) { - atomic_set(&obj->ps_deb_on, 1); - atomic_set(&obj->ps_deb_end, - jiffies + atomic_read(&obj->ps_debounce) * HZ / 1000); - } else { - msleep(4); - err = stk3x1x_read_ps(obj->client, &obj->ps); - if (err) { - APS_ERR("stk3x1x read ps data: %d\n", err); - return err; - } - - err = stk3x1x_get_ps_value_only(obj, obj->ps); - if (err < 0) { - APS_ERR("stk3x1x get ps value: %d\n", err); - return err; - } else if (stk3x1x_obj->hw->polling_mode_ps == 0) { - APS_LOG("%s:ps raw 0x%x -> value 0x%x\n", __func__, obj->ps,int_flag); - if (ps_report_interrupt_data(int_flag)) - APS_ERR("call ps_report_interrupt_data fail\n"); - } - } - } - - if (trc & STK_TRC_DEBUG) - APS_LOG("enable ps (%d)\n", enable); - - return err; -} - -/*----------------------------------------------------------------------------*/ - -static int stk3x1x_check_intr(struct i2c_client *client, u8 *status) -{ - struct stk3x1x_priv *obj = i2c_get_clientdata(client); - int err; - - err = stk3x1x_read_flag(client, status); - if (err < 0) { - APS_ERR("WARNING: read flag reg error: %d\n", err); - return -EFAULT; - } - APS_LOG("%s: read status reg: 0x%x\n", __func__, *status); - - if (*status & STK_FLG_ALSINT_MASK) - set_bit(STK_BIT_ALS, &obj->pending_intr); - else - clear_bit(STK_BIT_ALS, &obj->pending_intr); - - if (*status & STK_FLG_PSINT_MASK) - set_bit(STK_BIT_PS, &obj->pending_intr); - else - clear_bit(STK_BIT_PS, &obj->pending_intr); - - if (atomic_read(&obj->trace) & STK_TRC_DEBUG) - APS_LOG("check intr: 0x%02X => 0x%08lX\n", *status, obj->pending_intr); - - return 0; -} - - -static int stk3x1x_clear_intr(struct i2c_client *client, u8 status, u8 disable_flag) -{ - int err = 0; - - status = - status | (STK_FLG_ALSINT_MASK | STK_FLG_PSINT_MASK | STK_FLG_OUI_MASK | - STK_FLG_IR_RDY_MASK); - status &= (~disable_flag); - APS_LOG(" set flag reg: 0x%x\n", status); - err = stk3x1x_write_flag(client, status); - if (err) - APS_ERR("stk3x1x_write_flag failed, err=%d\n", err); - return err; -} - -/*----------------------------------------------------------------------------*/ -/*static int stk3x1x_set_als_int_thd(struct i2c_client *client, u16 als_data_reg) -{ - s32 als_thd_h, als_thd_l; - - als_thd_h = als_data_reg + STK_ALS_CODE_CHANGE_THD; - als_thd_l = als_data_reg - STK_ALS_CODE_CHANGE_THD; - if (als_thd_h >= (1 << 16)) - als_thd_h = (1 << 16) - 1; - if (als_thd_l < 0) - als_thd_l = 0; - APS_LOG("stk3x1x_set_als_int_thd:als_thd_h:%d,als_thd_l:%d\n", als_thd_h, als_thd_l); - - stk3x1x_write_als_high_thd(client, als_thd_h); - stk3x1x_write_als_low_thd(client, als_thd_l); - - return 0; -} -*/ -#ifdef CONFIG_CUSTOM_KERNEL_SENSORHUB -static int alsps_irq_handler(void *data, uint len) -{ - struct stk3x1x_priv *obj = stk3x1x_obj; - SCP_SENSOR_HUB_DATA_P rsp = (SCP_SENSOR_HUB_DATA_P) data; - - if (!obj) - return -1; - - APS_ERR("len = %d, type = %d, sction = %d, event = %d, data = %d\n", len, - rsp->rsp.sensorType, rsp->rsp.action, rsp->rsp.errCode, rsp->notify_rsp.data[1]); - - switch (rsp->rsp.action) { - case SENSOR_HUB_NOTIFY: - switch (rsp->notify_rsp.event) { - case SCP_INIT_DONE: - schedule_work(&obj->init_done_work); - break; - case SCP_NOTIFY: - if (STK3X1X_NOTIFY_PROXIMITY_CHANGE == rsp->notify_rsp.data[0]) { - gRawData.ps_state = rsp->notify_rsp.data[1]; - schedule_work(&obj->eint_work); - } else if (STK3X1X_NOTIFY_PROXIMITY_NOT_CHANGE == rsp->notify_rsp.data[0]) { - gRawData.ps_state = rsp->notify_rsp.data[1]; - schedule_work(&obj->data_work); - } else { - APS_ERR("Unknown notify\n"); - } - break; - default: - APS_ERR("Error sensor hub notify\n"); - break; - } - break; - default: - APS_ERR("Error sensor hub action\n"); - break; - } - - return 0; -} -#else -static irqreturn_t stk3x1x_eint_func(int irq, void *dev_id) -{ - struct stk3x1x_priv *obj = g_stk3x1x_ptr; - - int_top_time = sched_clock(); - - if (!obj) - return IRQ_HANDLED; - - disable_irq_nosync(stk3x1x_obj->irq); - if (obj->hw->polling_mode_ps == 0 || obj->hw->polling_mode_als == 0) - schedule_delayed_work(&obj->eint_work, 0); - if (atomic_read(&obj->trace) & STK_TRC_EINT) { - APS_LOG("eint: als/ps intrs\n"); - } - - return IRQ_HANDLED; -} -#endif - -/*----------------------------------------------------------------------------*/ -static void stk3x1x_eint_work(struct work_struct *work) -{ -#ifdef CONFIG_CUSTOM_KERNEL_SENSORHUB - int res = 0; - - res = ps_report_interrupt_data(gRawData.ps_state); - if (res != 0) - APS_ERR("stk3x1x_eint_work err: %d\n", res); - return res; -#else - struct stk3x1x_priv *obj = g_stk3x1x_ptr; - int err; - int int_flag = 0; - u8 flag_reg, disable_flag = 0; - - APS_LOG("stk3x1x int top half time = %lld\n", int_top_time); - - err = stk3x1x_check_intr(obj->client, &flag_reg); - if (err) { - APS_ERR("stk3x1x_check_intr fail: %d\n", err); - goto err_i2c_rw; - } - - APS_LOG("obj->pending_intr =%lx\n", obj->pending_intr); - - if (((1 << STK_BIT_PS) & obj->pending_intr) && (obj->hw->polling_mode_ps == 0)) { - APS_LOG("stk ps change\n"); - disable_flag |= STK_FLG_PSINT_MASK; - - err = stk3x1x_read_ps(obj->client, &obj->ps); - if (err) { - APS_ERR("stk3x1x read ps data: %d\n", err); - goto err_i2c_rw; - } - - int_flag = (flag_reg & STK_FLG_NF_MASK) ? 1 : 0; - /* let up layer to know */ - if (ps_report_interrupt_data(int_flag)) { - APS_ERR("call ps_report_interrupt_data fail\n"); - } - } - - err = stk3x1x_clear_intr(obj->client, flag_reg, disable_flag); - if (err) { - APS_ERR("fail: %d\n", err); - goto err_i2c_rw; - } - - mdelay(1); - enable_irq(stk3x1x_obj->irq); - return; - -err_i2c_rw: - msleep(30); - enable_irq(stk3x1x_obj->irq); - return; -#endif - -} - -/*----------------------------------------------------------------------------*/ -static int stk3x1x_setup_eint(struct i2c_client *client) -{ -#ifdef CONFIG_CUSTOM_KERNEL_SENSORHUB - int err = 0; - - err = SCP_sensorHub_rsp_registration(ID_PROXIMITY, alsps_irq_handler); -#else - struct stk3x1x_priv *obj = i2c_get_clientdata(client); - int ret; - u32 ints[2] = { 0, 0 }; - struct pinctrl *pinctrl; - struct pinctrl_state *pins_default; - struct pinctrl_state *pins_cfg; - struct platform_device *alsps_pdev = get_alsps_platformdev(); - - APS_LOG("stk3x1x_setup_eint\n"); - - g_stk3x1x_ptr = obj; - - /*configure to GPIO function, external interrupt */ - pinctrl = devm_pinctrl_get(&alsps_pdev->dev); - if (IS_ERR(pinctrl)) { - ret = PTR_ERR(pinctrl); - APS_ERR("Cannot find alsps pinctrl!\n"); - } - pins_default = pinctrl_lookup_state(pinctrl, "pin_default"); - if (IS_ERR(pins_default)) { - ret = PTR_ERR(pins_default); - APS_ERR("Cannot find alsps pinctrl default!\n"); - } - - pins_cfg = pinctrl_lookup_state(pinctrl, "pin_cfg"); - if (IS_ERR(pins_cfg)) { - ret = PTR_ERR(pins_cfg); - APS_ERR("Cannot find alsps pinctrl pin_cfg!\n"); - - } - pinctrl_select_state(pinctrl, pins_cfg); - - if (stk3x1x_obj->irq_node) { - of_property_read_u32_array(stk3x1x_obj->irq_node, "debounce", ints, - ARRAY_SIZE(ints)); - gpio_request(ints[0], "p-sensor"); - gpio_set_debounce(ints[0], ints[1]); - APS_LOG("ints[0] = %d, ints[1] = %d!!\n", ints[0], ints[1]); - - stk3x1x_obj->irq = irq_of_parse_and_map(stk3x1x_obj->irq_node, 0); - APS_LOG("stk3x1x_obj->irq = %d\n", stk3x1x_obj->irq); - if (!stk3x1x_obj->irq) { - APS_ERR("irq_of_parse_and_map fail!!\n"); - return -EINVAL; - } - if (request_irq - (stk3x1x_obj->irq, stk3x1x_eint_func, IRQF_TRIGGER_NONE, "ALS-eint", NULL)) { - APS_ERR("IRQ LINE NOT AVAILABLE!!\n"); - return -EINVAL; - } - /*enable_irq(stk3x1x_obj->irq);*/ - } else { - APS_ERR("null irq node!!\n"); - return -EINVAL; - } - - /*enable_irq(stk3x1x_obj->irq);*/ -#endif - - return 0; -} - -/*----------------------------------------------------------------------------*/ -static int stk3x1x_init_client(struct i2c_client *client) -{ - struct stk3x1x_priv *obj = i2c_get_clientdata(client); - int err; - int ps_ctrl; - /* u8 int_status; */ - - err = stk3x1x_write_sw_reset(client); - if (err) { - APS_ERR("software reset error, err=%d", err); - return err; - } -/* - if((err = stk3x1x_read_id(client))) - { - APS_ERR("stk3x1x_read_id error, err=%d", err); - return err; - } -*/ - if (obj->hw->polling_mode_ps == 0 || obj->hw->polling_mode_als == 0) { - /*disable_irq(stk3x1x_obj->irq);*/ - err = stk3x1x_setup_eint(client); - if (err) { - APS_ERR("setup eint error: %d\n", err); - return err; - } - } - - err = stk3x1x_write_state(client, atomic_read(&obj->state_val)); - if (err) { - APS_ERR("write stete error: %d\n", err); - return err; - } - - /* - if((err = stk3x1x_check_intr(client, &int_status))) - { - APS_ERR("check intr error: %d\n", err); - // return err; - } - - if((err = stk3x1x_clear_intr(client, int_status, STK_FLG_PSINT_MASK | STK_FLG_ALSINT_MASK))) - { - APS_ERR("clear intr error: %d\n", err); - return err; - } - */ - ps_ctrl = atomic_read(&obj->psctrl_val); - if (obj->hw->polling_mode_ps == 1) - ps_ctrl &= 0x3F; - - err = stk3x1x_write_ps(client, ps_ctrl); - if (err) { - APS_ERR("write ps error: %d\n", err); - return err; - } - - err = stk3x1x_write_als(client, atomic_read(&obj->alsctrl_val)); - if (err) { - APS_ERR("write als error: %d\n", err); - return err; - } - - err = stk3x1x_write_led(client, obj->ledctrl_val); - if (err) { - APS_ERR("write led error: %d\n", err); - return err; - } - - err = stk3x1x_write_wait(client, obj->wait_val); - if (err) { - APS_ERR("write wait error: %d\n", err); - return err; - } - err = stk3x1x_write_int(client, obj->int_val); - if (err) { - APS_ERR("write int mode error: %d\n", err); - return err; - } -#ifdef STK_FIR - memset(&obj->fir, 0x00, sizeof(obj->fir)); -#endif - return 0; -} - -/****************************************************************************** - * Sysfs attributes -*******************************************************************************/ -static ssize_t stk3x1x_show_config(struct device_driver *ddri, char *buf) -{ - ssize_t res; - - if (!stk3x1x_obj) { - APS_ERR("stk3x1x_obj is null!!\n"); - return 0; - } - - res = scnprintf(buf, PAGE_SIZE, "(%d %d %d %d %d %d)\n", - atomic_read(&stk3x1x_obj->i2c_retry), - atomic_read(&stk3x1x_obj->als_debounce), atomic_read(&stk3x1x_obj->ps_mask), - atomic_read(&stk3x1x_obj->ps_high_thd_val), - atomic_read(&stk3x1x_obj->ps_low_thd_val), - atomic_read(&stk3x1x_obj->ps_debounce)); - return res; -} - -/*----------------------------------------------------------------------------*/ -static ssize_t stk3x1x_store_config(struct device_driver *ddri, const char *buf, size_t count) -{ - int retry, als_deb, ps_deb, mask, hthres, lthres, err; - struct i2c_client *client; - - client = stk3x1x_i2c_client; - if (!stk3x1x_obj) { - APS_ERR("stk3x1x_obj is null!!\n"); - return 0; - } - - if (6 == - sscanf(buf, "%d %d %d %d %d %d", &retry, &als_deb, &mask, &hthres, <hres, &ps_deb)) { - atomic_set(&stk3x1x_obj->i2c_retry, retry); - atomic_set(&stk3x1x_obj->als_debounce, als_deb); - atomic_set(&stk3x1x_obj->ps_mask, mask); - atomic_set(&stk3x1x_obj->ps_high_thd_val, hthres); - atomic_set(&stk3x1x_obj->ps_low_thd_val, lthres); - atomic_set(&stk3x1x_obj->ps_debounce, ps_deb); - - err = stk3x1x_write_ps_high_thd(client,atomic_read(&stk3x1x_obj->ps_high_thd_val)); - if (err) { - APS_ERR("write high thd error: %d\n", err); - return err; - } - - err = stk3x1x_write_ps_low_thd(client, atomic_read(&stk3x1x_obj->ps_low_thd_val)); - if (err) { - APS_ERR("write low thd error: %d\n", err); - return err; - } - } else { - APS_ERR("invalid content: '%s', length = %zu\n", buf, count); - } - return count; -} - -/*----------------------------------------------------------------------------*/ -static ssize_t stk3x1x_show_trace(struct device_driver *ddri, char *buf) -{ - ssize_t res; - - if (!stk3x1x_obj) { - APS_ERR("stk3x1x_obj is null!!\n"); - return 0; - } - - res = scnprintf(buf, PAGE_SIZE, "0x%04X\n", atomic_read(&stk3x1x_obj->trace)); - return res; -} - -/*----------------------------------------------------------------------------*/ -static ssize_t stk3x1x_store_trace(struct device_driver *ddri, const char *buf, size_t count) -{ - int trace; - - if (!stk3x1x_obj) { - APS_ERR("stk3x1x_obj is null!!\n"); - return 0; - } - - if (1 == sscanf(buf, "0x%x", &trace)) { - atomic_set(&stk3x1x_obj->trace, trace); - } else { - APS_ERR("invalid content: '%s', length = %d\n", buf, (int)count); - } - return count; -} - -/*----------------------------------------------------------------------------*/ -static ssize_t stk3x1x_show_ir(struct device_driver *ddri, char *buf) -{ - int32_t reading; - - if (!stk3x1x_obj) { - APS_ERR("stk3x1x_obj is null!!\n"); - return 0; - } - reading = stk3x1x_get_ir_value(stk3x1x_obj); - if (reading < 0) - return scnprintf(buf, PAGE_SIZE, "ERROR: %d\n", reading); - - stk3x1x_obj->ir_code = reading; - return scnprintf(buf, PAGE_SIZE, "0x%04X\n", stk3x1x_obj->ir_code); -} - -/*----------------------------------------------------------------------------*/ -static ssize_t stk3x1x_show_als(struct device_driver *ddri, char *buf) -{ - int res; - - if (!stk3x1x_obj) { - APS_ERR("stk3x1x_obj is null!!\n"); - return 0; - } - if ((res = stk3x1x_read_als(stk3x1x_obj->client, &stk3x1x_obj->als))) { - return scnprintf(buf, PAGE_SIZE, "ERROR: %d\n", res); - } else { - return scnprintf(buf, PAGE_SIZE, "0x%04X\n", stk3x1x_obj->als); - } -} - -/*----------------------------------------------------------------------------*/ -static ssize_t stk3x1x_show_ps(struct device_driver *ddri, char *buf) -{ - int res; - - if (!stk3x1x_obj) { - APS_ERR("stk3x1x_obj is null!!\n"); - return 0; - } - - if ((res = stk3x1x_read_ps(stk3x1x_obj->client, &stk3x1x_obj->ps))) { - return scnprintf(buf, PAGE_SIZE, "ERROR: %d\n", res); - } else { - return scnprintf(buf, PAGE_SIZE, "0x%04X\n", stk3x1x_obj->ps); - } -} - -/*----------------------------------------------------------------------------*/ -static ssize_t stk3x1x_show_reg(struct device_driver *ddri, char *buf) -{ - u8 int_status; - - if (!stk3x1x_obj) { - APS_ERR("stk3x1x_obj is null!!\n"); - return 0; - } - - /*read */ - stk3x1x_check_intr(stk3x1x_obj->client, &int_status); - /* stk3x1x_clear_intr(stk3x1x_obj->client, int_status, 0x0); */ - stk3x1x_read_ps(stk3x1x_obj->client, &stk3x1x_obj->ps); - stk3x1x_read_als(stk3x1x_obj->client, &stk3x1x_obj->als); - /*write */ - stk3x1x_write_als(stk3x1x_obj->client, atomic_read(&stk3x1x_obj->alsctrl_val)); - stk3x1x_write_ps(stk3x1x_obj->client, atomic_read(&stk3x1x_obj->psctrl_val)); - stk3x1x_write_ps_high_thd(stk3x1x_obj->client, atomic_read(&stk3x1x_obj->ps_high_thd_val)); - stk3x1x_write_ps_low_thd(stk3x1x_obj->client, atomic_read(&stk3x1x_obj->ps_low_thd_val)); - return 0; -} - -/*----------------------------------------------------------------------------*/ -static ssize_t stk3x1x_show_send(struct device_driver *ddri, char *buf) -{ - return 0; -} - -/*----------------------------------------------------------------------------*/ -static ssize_t stk3x1x_store_send(struct device_driver *ddri, const char *buf, size_t count) -{ - int addr, cmd; - u8 dat; - int ret = 0; - - if (!stk3x1x_obj) { - APS_ERR("stk3x1x_obj is null!!\n"); - return 0; - } else if (2 != sscanf(buf, "%x %x", &addr, &cmd)) { - APS_ERR("invalid format: '%s'\n", buf); - return 0; - } - - dat = (u8) cmd; - ret = stk3x1x_master_send(stk3x1x_obj->client, (u16) addr, &dat, sizeof(dat)); - APS_LOG("send(%02X, %02X) = %d\n", addr, cmd, ret); - - return count; -} - -/*----------------------------------------------------------------------------*/ -static ssize_t stk3x1x_show_recv(struct device_driver *ddri, char *buf) -{ - if (!stk3x1x_obj) { - APS_ERR("stk3x1x_obj is null!!\n"); - return 0; - } - return scnprintf(buf, PAGE_SIZE, "0x%04X\n", atomic_read(&stk3x1x_obj->recv_reg)); -} - -/*----------------------------------------------------------------------------*/ -static ssize_t stk3x1x_store_recv(struct device_driver *ddri, const char *buf, size_t count) -{ - int addr; - u8 dat; - int ret = 0; - - if (!stk3x1x_obj) { - APS_ERR("stk3x1x_obj is null!!\n"); - return 0; - } else if (1 != sscanf(buf, "%x", &addr)) { - APS_ERR("invalid format: '%s'\n", buf); - return 0; - } - ret = stk3x1x_master_recv(stk3x1x_obj->client, (u16) addr, (char *)&dat, sizeof(dat)); - APS_LOG("recv(%02X) = %d, 0x%02X\n", addr, ret, dat); - atomic_set(&stk3x1x_obj->recv_reg, dat); - return count; -} - -/*----------------------------------------------------------------------------*/ -static ssize_t stk3x1x_show_allreg(struct device_driver *ddri, char *buf) -{ - int ret = 0; - u8 rbuf[27]; - int cnt; - - memset(rbuf, 0, sizeof(rbuf)); - if (!stk3x1x_obj) { - APS_ERR("stk3x1x_obj is null!!\n"); - return 0; - } - ret = stk3x1x_master_recv(stk3x1x_obj->client, 0, &rbuf[0], 7); - if (ret < 0) { - APS_DBG("error: %d\n", ret); - return -EFAULT; - } - ret = stk3x1x_master_recv(stk3x1x_obj->client, 7, &rbuf[7], 7); - if (ret < 0) { - APS_DBG("error: %d\n", ret); - return -EFAULT; - } - ret = stk3x1x_master_recv(stk3x1x_obj->client, 14, &rbuf[14], 7); - if (ret < 0) { - APS_DBG("error: %d\n", ret); - return -EFAULT; - } - ret = stk3x1x_master_recv(stk3x1x_obj->client, 21, &rbuf[21], 4); - if (ret < 0) { - APS_DBG("error: %d\n", ret); - return -EFAULT; - } - ret = stk3x1x_master_recv(stk3x1x_obj->client, STK_PDT_ID_REG, &rbuf[25], 2); - if (ret < 0) { - APS_DBG("error: %d\n", ret); - return -EFAULT; - } - - for (cnt = 0; cnt < 25; cnt++) { - APS_LOG("reg[0x%x]=0x%x\n", cnt, rbuf[cnt]); - } - APS_LOG("reg[0x3E]=0x%x\n", rbuf[cnt]); - APS_LOG("reg[0x3F]=0x%x\n", rbuf[cnt++]); - return scnprintf(buf, PAGE_SIZE, - "[0]%2X [1]%2X [2]%2X [3]%2X [4]%2X [5]%2X [6/7 HTHD]%2X,%2X [8/9 LTHD]%2X, %2X [A]%2X [B]%2X [C]%2X [D]%2X [E/F Aoff]%2X,%2X,[10]%2X [11/12 PS]%2X,%2X [13]%2X [14]%2X [15/16 Foff]%2X,%2X [17]%2X [18]%2X [3E]%2X [3F]%2X\n", - rbuf[0], rbuf[1], rbuf[2], rbuf[3], rbuf[4], rbuf[5], rbuf[6], rbuf[7], - rbuf[8], rbuf[9], rbuf[10], rbuf[11], rbuf[12], rbuf[13], rbuf[14], - rbuf[15], rbuf[16], rbuf[17], rbuf[18], rbuf[19], rbuf[20], rbuf[21], - rbuf[22], rbuf[23], rbuf[24], rbuf[25], rbuf[26]); -} - -/*----------------------------------------------------------------------------*/ -static ssize_t stk3x1x_show_status(struct device_driver *ddri, char *buf) -{ - ssize_t len = 0; - u8 rbuf[25]; - int ret = 0; - - if (!stk3x1x_obj) { - APS_ERR("stk3x1x_obj is null!!\n"); - return 0; - } - - if (stk3x1x_obj->hw) { - len += - scnprintf(buf + len, PAGE_SIZE - len, - "CUST: %d, (%d %d) (%02X) (%02X %02X %02X) (%02X %02X %02X %02X)\n", - stk3x1x_obj->hw->i2c_num, stk3x1x_obj->hw->power_id, - stk3x1x_obj->hw->power_vol, stk3x1x_obj->addr.flag, - stk3x1x_obj->addr.alsctrl, stk3x1x_obj->addr.data1_als, - stk3x1x_obj->addr.data2_als, stk3x1x_obj->addr.psctrl, - stk3x1x_obj->addr.data1_ps, stk3x1x_obj->addr.data2_ps, - stk3x1x_obj->addr.thdh1_ps); - } else { - len += scnprintf(buf + len, PAGE_SIZE - len, "CUST: NULL\n"); - } - - len += - scnprintf(buf + len, PAGE_SIZE - len, - "REGS: %02X %02X %02X %02X %02X %02X %02X %02X %02lX %02lX\n", - atomic_read(&stk3x1x_obj->state_val), atomic_read(&stk3x1x_obj->psctrl_val), - atomic_read(&stk3x1x_obj->alsctrl_val), stk3x1x_obj->ledctrl_val, - stk3x1x_obj->int_val, stk3x1x_obj->wait_val, - atomic_read(&stk3x1x_obj->ps_high_thd_val), - atomic_read(&stk3x1x_obj->ps_low_thd_val), stk3x1x_obj->enable, - stk3x1x_obj->pending_intr); - - len += - scnprintf(buf + len, PAGE_SIZE - len, "MISC: %d %d\n", - atomic_read(&stk3x1x_obj->als_suspend), - atomic_read(&stk3x1x_obj->ps_suspend)); - len += scnprintf(buf + len, PAGE_SIZE - len, "VER.: %s\n", DRIVER_VERSION); - - memset(rbuf, 0, sizeof(rbuf)); - ret = stk3x1x_master_recv(stk3x1x_obj->client, 0, &rbuf[0], 7); - if (ret < 0) { - APS_DBG("error: %d\n", ret); - return -EFAULT; - } - ret = stk3x1x_master_recv(stk3x1x_obj->client, 7, &rbuf[7], 7); - if (ret < 0) { - APS_DBG("error: %d\n", ret); - return -EFAULT; - } - ret = stk3x1x_master_recv(stk3x1x_obj->client, 14, &rbuf[14], 7); - if (ret < 0) { - APS_DBG("error: %d\n", ret); - return -EFAULT; - } - /* - ret = stk3x1x_master_recv(stk3x1x_obj->client, 21, &rbuf[21], 4); - if(ret < 0) - { - APS_DBG("error: %d\n", ret); - return -EFAULT; - } - */ - len += - scnprintf(buf + len, PAGE_SIZE - len, - "[PS=%2X] [ALS=%2X] [WAIT=%4Xms] [EN_ASO=%2X] [EN_AK=%2X] [NEAR/FAR=%2X] [FLAG_OUI=%2X] [FLAG_PSINT=%2X] [FLAG_ALSINT=%2X]\n", - rbuf[0] & 0x01, (rbuf[0] & 0x02) >> 1, ((rbuf[0] & 0x04) >> 2) * rbuf[5] * 6, - (rbuf[0] & 0x20) >> 5, (rbuf[0] & 0x40) >> 6, rbuf[16] & 0x01, - (rbuf[16] & 0x04) >> 2, (rbuf[16] & 0x10) >> 4, (rbuf[16] & 0x20) >> 5); - - return len; -} - -/*----------------------------------------------------------------------------*/ -#define IS_SPACE(CH) (((CH) == ' ') || ((CH) == '\n')) -/*----------------------------------------------------------------------------*/ -static int read_int_from_buf(struct stk3x1x_priv *obj, const char *buf, size_t count, - u32 data[], int len) -{ - int idx = 0; - char *cur = (char *)buf, *end = (char *)(buf + count); - - while (idx < len) { - while ((cur < end) && IS_SPACE(*cur)) { - cur++; - } - - if (1 != sscanf(cur, "%d", &data[idx])) { - break; - } - - idx++; - while ((cur < end) && !IS_SPACE(*cur)) { - cur++; - } - } - return idx; -} - -/*----------------------------------------------------------------------------*/ -static ssize_t stk3x1x_show_alslv(struct device_driver *ddri, char *buf) -{ - ssize_t len = 0; - int idx; - - if (!stk3x1x_obj) { - APS_ERR("stk3x1x_obj is null!!\n"); - return 0; - } - - for (idx = 0; idx < stk3x1x_obj->als_level_num; idx++) { - len += - scnprintf(buf + len, PAGE_SIZE - len, "%d ", stk3x1x_obj->hw->als_level[idx]); - } - len += scnprintf(buf + len, PAGE_SIZE - len, "\n"); - return len; -} - -/*----------------------------------------------------------------------------*/ -static ssize_t stk3x1x_store_alslv(struct device_driver *ddri, const char *buf, size_t count) -{ - if (!stk3x1x_obj) { - APS_ERR("stk3x1x_obj is null!!\n"); - return 0; - } else if (!strcmp(buf, "def")) { - memcpy(stk3x1x_obj->als_level, stk3x1x_obj->hw->als_level, - sizeof(stk3x1x_obj->als_level)); - } else if (stk3x1x_obj->als_level_num != - read_int_from_buf(stk3x1x_obj, buf, count, stk3x1x_obj->hw->als_level, - stk3x1x_obj->als_level_num)) { - APS_ERR("invalid format: '%s'\n", buf); - } - return count; -} - -/*----------------------------------------------------------------------------*/ -static ssize_t stk3x1x_show_alsval(struct device_driver *ddri, char *buf) -{ - ssize_t len = 0; - int idx; - - if (!stk3x1x_obj) { - APS_ERR("stk3x1x_obj is null!!\n"); - return 0; - } - - for (idx = 0; idx < stk3x1x_obj->als_value_num; idx++) { - len += - scnprintf(buf + len, PAGE_SIZE - len, "%d ", stk3x1x_obj->hw->als_value[idx]); - } - len += scnprintf(buf + len, PAGE_SIZE - len, "\n"); - return len; -} - -/*----------------------------------------------------------------------------*/ -static ssize_t stk3x1x_store_alsval(struct device_driver *ddri, const char *buf, size_t count) -{ - if (!stk3x1x_obj) { - APS_ERR("stk3x1x_obj is null!!\n"); - return 0; - } else if (!strcmp(buf, "def")) { - memcpy(stk3x1x_obj->als_value, stk3x1x_obj->hw->als_value, - sizeof(stk3x1x_obj->als_value)); - } else if (stk3x1x_obj->als_value_num != - read_int_from_buf(stk3x1x_obj, buf, count, stk3x1x_obj->hw->als_value, - stk3x1x_obj->als_value_num)) { - APS_ERR("invalid format: '%s'\n", buf); - } - return count; -} - - -/*----------------------------------------------------------------------------*/ -static DRIVER_ATTR(als, S_IWUSR | S_IRUGO, stk3x1x_show_als, NULL); -static DRIVER_ATTR(ps, S_IWUSR | S_IRUGO, stk3x1x_show_ps, NULL); -static DRIVER_ATTR(ir, S_IWUSR | S_IRUGO, stk3x1x_show_ir, NULL); -static DRIVER_ATTR(config, S_IWUSR | S_IRUGO, stk3x1x_show_config, stk3x1x_store_config); -static DRIVER_ATTR(alslv, S_IWUSR | S_IRUGO, stk3x1x_show_alslv, stk3x1x_store_alslv); -static DRIVER_ATTR(alsval, S_IWUSR | S_IRUGO, stk3x1x_show_alsval, stk3x1x_store_alsval); -static DRIVER_ATTR(trace, S_IWUSR | S_IRUGO, stk3x1x_show_trace, stk3x1x_store_trace); -static DRIVER_ATTR(status, S_IWUSR | S_IRUGO, stk3x1x_show_status, NULL); -static DRIVER_ATTR(send, S_IWUSR | S_IRUGO, stk3x1x_show_send, stk3x1x_store_send); -static DRIVER_ATTR(recv, S_IWUSR | S_IRUGO, stk3x1x_show_recv, stk3x1x_store_recv); -static DRIVER_ATTR(reg, S_IWUSR | S_IRUGO, stk3x1x_show_reg, NULL); -static DRIVER_ATTR(allreg, S_IWUSR | S_IRUGO, stk3x1x_show_allreg, NULL); -/*----------------------------------------------------------------------------*/ -static struct driver_attribute *stk3x1x_attr_list[] = { - &driver_attr_als, - &driver_attr_ps, - &driver_attr_ir, - &driver_attr_trace, /*trace log */ - &driver_attr_config, - &driver_attr_alslv, - &driver_attr_alsval, - &driver_attr_status, - &driver_attr_send, - &driver_attr_recv, - &driver_attr_allreg, -/* &driver_attr_i2c, */ - &driver_attr_reg, -}; - -/*----------------------------------------------------------------------------*/ -static int stk3x1x_create_attr(struct device_driver *driver) -{ - int idx, err = 0; - int num = (int)(sizeof(stk3x1x_attr_list) / sizeof(stk3x1x_attr_list[0])); - - if (driver == NULL) { - return -EINVAL; - } - - for (idx = 0; idx < num; idx++) { - err = driver_create_file(driver, stk3x1x_attr_list[idx]); - if (err) { - APS_ERR("create attr fail(%s) = %d\n", stk3x1x_attr_list[idx]->attr.name,err); - break; - } - } - return err; -} - -/*----------------------------------------------------------------------------*/ -static int stk3x1x_delete_attr(struct device_driver *driver) -{ - int idx, err = 0; - int num = (int)(sizeof(stk3x1x_attr_list) / sizeof(stk3x1x_attr_list[0])); - - if (!driver) - return -EINVAL; - - for (idx = 0; idx < num; idx++) { - driver_remove_file(driver, stk3x1x_attr_list[idx]); - } - - return err; -} - -/****************************************************************************** - * Function Configuration -******************************************************************************/ -static int stk3x1x_get_als_value(struct stk3x1x_priv *obj, u16 als) -{ - int idx; - int invalid = 0; - - for (idx = 0; idx < obj->als_level_num; idx++) { - if (als < obj->hw->als_level[idx]) { - break; - } - } - - if (idx >= obj->als_value_num) { - APS_ERR("exceed range\n"); - idx = obj->als_value_num - 1; - } - - if (1 == atomic_read(&obj->als_deb_on)) { - unsigned long endt = atomic_read(&obj->als_deb_end); - - if (time_after(jiffies, endt)) { - atomic_set(&obj->als_deb_on, 0); - } - - if (1 == atomic_read(&obj->als_deb_on)) { - invalid = 1; - } - } - - if (!invalid) { -#if defined(CONFIG_MTK_AAL_SUPPORT) - int level_high = obj->hw->als_level[idx]; - int level_low = (idx > 0) ? obj->hw->als_level[idx - 1] : 0; - int level_diff = level_high - level_low; - int value_high = obj->hw->als_value[idx]; - int value_low = (idx > 0) ? obj->hw->als_value[idx - 1] : 0; - int value_diff = value_high - value_low; - int value = 0; - - if ((level_low >= level_high) || (value_low >= value_high)) - value = value_low; - else - value = - (level_diff * value_low + (als - level_low) * value_diff + - ((level_diff + 1) >> 1)) / level_diff; - APS_DBG("ALS: %d [%d, %d] => %d [%d, %d]\n", als, level_low, level_high, value, - value_low, value_high); - return value; -#endif - - if (atomic_read(&obj->trace) & STK_TRC_CVT_ALS) { - APS_DBG("ALS: %05d => %05d\n", als, obj->hw->als_value[idx]); - } - - return obj->hw->als_value[idx]; - } else { - if (atomic_read(&obj->trace) & STK_TRC_CVT_ALS) { - APS_DBG("ALS: %05d => %05d (-1)\n", als, obj->hw->als_value[idx]); - } - return -1; - } -} - -/*----------------------------------------------------------------------------*/ -static int stk3x1x_get_ps_value_only(struct stk3x1x_priv *obj, u16 ps) -{ - int mask = atomic_read(&obj->ps_mask); - int invalid = 0, val; - int err; - u8 flag; - - err = stk3x1x_read_flag(obj->client, &flag); - if (err) - return err; - val = (flag & STK_FLG_NF_MASK) ? 1 : 0; - - if (atomic_read(&obj->ps_suspend)) { - invalid = 1; - } else if (1 == atomic_read(&obj->ps_deb_on)) { - unsigned long endt = atomic_read(&obj->ps_deb_end); - - if (time_after(jiffies, endt)) { - atomic_set(&obj->ps_deb_on, 0); - } - - if (1 == atomic_read(&obj->ps_deb_on)) { - invalid = 1; - } - } - - if (!invalid) { - if (unlikely(atomic_read(&obj->trace) & STK_TRC_CVT_PS)) { - if (mask) { - APS_DBG("PS: %05d => %05d [M]\n", ps, val); - } else { - APS_DBG("PS: %05d => %05d\n", ps, val); - } - } - return val; - - } else { - APS_ERR(" ps value is invalid, PS: %05d => %05d\n", ps, val); - if (unlikely(atomic_read(&obj->trace) & STK_TRC_CVT_PS)) { - APS_DBG("PS: %05d => %05d (-1)\n", ps, val); - } - return -1; - } -} - -/*----------------------------------------------------------------------------*/ -static int stk3x1x_get_ps_value(struct stk3x1x_priv *obj, u16 ps) -{ - int mask = atomic_read(&obj->ps_mask); - int invalid = 0, val; - int err; - u8 flag; - - err = stk3x1x_read_flag(obj->client, &flag); - if (err) - return err; - - val = (flag & STK_FLG_NF_MASK) ? 1 : 0; - err = stk3x1x_clear_intr(obj->client, flag, STK_FLG_OUI_MASK); - if (err) { - APS_ERR("fail: %d\n", err); - return err; - } - - if (atomic_read(&obj->ps_suspend)) { - invalid = 1; - } else if (1 == atomic_read(&obj->ps_deb_on)) { - unsigned long endt = atomic_read(&obj->ps_deb_end); - - if (time_after(jiffies, endt)) { - atomic_set(&obj->ps_deb_on, 0); - } - - if (1 == atomic_read(&obj->ps_deb_on)) { - invalid = 1; - } - } - - - if (!invalid) { - if (unlikely(atomic_read(&obj->trace) & STK_TRC_CVT_PS)) { - if (mask) { - APS_DBG("PS: %05d => %05d [M]\n", ps, val); - } else { - APS_DBG("PS: %05d => %05d\n", ps, val); - } - } - return val; - - } else { - APS_ERR(" ps value is invalid, PS: %05d => %05d\n", ps, val); - if (unlikely(atomic_read(&obj->trace) & STK_TRC_CVT_PS)) { - APS_DBG("PS: %05d => %05d (-1)\n", ps, val); - } - return -1; - } -} - -/*----------------------------------------------------------------------------*/ - -static int32_t stk3x1x_set_irs_it_slp(struct stk3x1x_priv *obj, uint16_t *slp_time) -{ - uint8_t irs_alsctrl; - int32_t ret; - - irs_alsctrl = (atomic_read(&obj->alsctrl_val) & 0x0F) - 2; - switch (irs_alsctrl) { - case 6: - *slp_time = 12; - break; - case 7: - *slp_time = 24; - break; - case 8: - *slp_time = 48; - break; - case 9: - *slp_time = 96; - break; - default: - APS_ERR("%s: unknown ALS IT=0x%x\n", __func__, irs_alsctrl); - ret = -EINVAL; - return ret; - } - irs_alsctrl |= (atomic_read(&obj->alsctrl_val) & 0xF0); - ret = i2c_smbus_write_byte_data(obj->client, STK_ALSCTRL_REG, irs_alsctrl); - if (ret < 0) { - APS_ERR("%s: write i2c error\n", __func__); - return ret; - } - return 0; -} - -static int32_t stk3x1x_get_ir_value(struct stk3x1x_priv *obj) -{ - int32_t word_data, ret; - uint8_t w_reg, retry = 0; - uint16_t irs_slp_time = 100; - bool re_enable_ps = false; - u8 flag; - u8 buf[2]; - - re_enable_ps = (atomic_read(&obj->state_val) & STK_STATE_EN_PS_MASK) ? true : false; - if (re_enable_ps) { - stk3x1x_enable_ps(obj->client, 0); - } - - ret = stk3x1x_set_irs_it_slp(obj, &irs_slp_time); - if (ret < 0) - goto irs_err_i2c_rw; - - w_reg = atomic_read(&obj->state_val) | STK_STATE_EN_IRS_MASK; - ret = i2c_smbus_write_byte_data(obj->client, STK_STATE_REG, w_reg); - if (ret < 0) { - APS_ERR("%s: write i2c error\n", __func__); - goto irs_err_i2c_rw; - } - msleep(irs_slp_time); - - do { - msleep(3); - ret = stk3x1x_read_flag(obj->client, &flag); - if (ret < 0) { - APS_ERR("WARNING: read flag reg error: %d\n", ret); - goto irs_err_i2c_rw; - } - retry++; - } while (retry < 10 && ((flag & STK_FLG_IR_RDY_MASK) == 0)); - - if (retry == 10) { - APS_ERR("%s: ir data is not ready for 300ms\n", __func__); - ret = -EINVAL; - goto irs_err_i2c_rw; - } - - ret = stk3x1x_clear_intr(obj->client, flag, STK_FLG_IR_RDY_MASK); - if (ret < 0) { - APS_ERR("%s: write i2c error\n", __func__); - goto irs_err_i2c_rw; - } - - ret = stk3x1x_master_recv(obj->client, STK_DATA1_IR_REG, buf, 2); - if (ret < 0) { - APS_ERR("%s fail, ret=0x%x", __func__, ret); - goto irs_err_i2c_rw; - } - word_data = (buf[0] << 8) | buf[1]; - - ret = - i2c_smbus_write_byte_data(obj->client, STK_ALSCTRL_REG, atomic_read(&obj->alsctrl_val)); - if (ret < 0) { - APS_ERR("%s: write i2c error\n", __func__); - goto irs_err_i2c_rw; - } - if (re_enable_ps) - stk3x1x_enable_ps(obj->client, 1); - return word_data; - -irs_err_i2c_rw: - if (re_enable_ps) - stk3x1x_enable_ps(obj->client, 1); - return ret; -} - -/****************************************************************************** - * Function Configuration -******************************************************************************/ -static int stk3x1x_open(struct inode *inode, struct file *file) -{ - file->private_data = stk3x1x_i2c_client; - - if (!file->private_data) { - APS_ERR("null pointer!!\n"); - return -EINVAL; - } - - return nonseekable_open(inode, file); -} - -/*----------------------------------------------------------------------------*/ -static int stk3x1x_release(struct inode *inode, struct file *file) -{ - file->private_data = NULL; - return 0; -} - -/*----------------------------------------------------------------------------*/ -static long stk3x1x_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ - int err = 0; - void __user *ptr = (void __user *)arg; - int dat; - uint32_t enable; - int ps_result; - int ps_cali; - int threshold[2]; - struct i2c_client *client = (struct i2c_client *)file->private_data; - struct stk3x1x_priv *obj = i2c_get_clientdata(client); - - - switch (cmd) { - case ALSPS_SET_PS_MODE: - if (copy_from_user(&enable, ptr, sizeof(enable))) { - err = -EFAULT; - goto err_out; - } - if (enable) { - err = stk3x1x_enable_ps(obj->client, 1); - if (err) { - APS_ERR("enable ps fail: %d\n", err); - goto err_out; - } - - set_bit(STK_BIT_PS, &obj->enable); - } else { - err = stk3x1x_enable_ps(obj->client, 0); - if (err) { - APS_ERR("disable ps fail: %d\n", err); - goto err_out; - } - clear_bit(STK_BIT_PS, &obj->enable); - } - break; - - case ALSPS_GET_PS_MODE: - enable = test_bit(STK_BIT_PS, &obj->enable) ? (1) : (0); - if (copy_to_user(ptr, &enable, sizeof(enable))) { - err = -EFAULT; - goto err_out; - } - break; - - case ALSPS_GET_PS_DATA: - err = stk3x1x_read_ps(obj->client, &obj->ps); - if (err) - goto err_out; - - dat = stk3x1x_get_ps_value(obj, obj->ps); - if (dat < 0) { - err = dat; - goto err_out; - } -#ifdef STK_PS_POLLING_LOG - APS_LOG("%s:ps raw 0x%x -> value 0x%x\n", __func__, obj->ps, dat); -#endif - if (copy_to_user(ptr, &dat, sizeof(dat))) { - err = -EFAULT; - goto err_out; - } - break; - - case ALSPS_GET_PS_RAW_DATA: - err = stk3x1x_read_ps(obj->client, &obj->ps); - if (err) - goto err_out; - - dat = obj->ps; - if (copy_to_user(ptr, &dat, sizeof(dat))) { - err = -EFAULT; - goto err_out; - } - break; - - case ALSPS_SET_ALS_MODE: - if (copy_from_user(&enable, ptr, sizeof(enable))) { - err = -EFAULT; - goto err_out; - } - if (enable) { - err = stk3x1x_enable_als(obj->client, 1); - if (err) { - APS_ERR("enable als fail: %d\n", err); - goto err_out; - } - set_bit(STK_BIT_ALS, &obj->enable); - } else { - err = stk3x1x_enable_als(obj->client, 0); - if (err) { - APS_ERR("disable als fail: %d\n", err); - goto err_out; - } - clear_bit(STK_BIT_ALS, &obj->enable); - } - break; - - case ALSPS_GET_ALS_MODE: - enable = test_bit(STK_BIT_ALS, &obj->enable) ? (1) : (0); - if (copy_to_user(ptr, &enable, sizeof(enable))) { - err = -EFAULT; - goto err_out; - } - break; - - case ALSPS_GET_ALS_DATA: - err = stk3x1x_read_als(obj->client, &obj->als); - if (err) - goto err_out; - - dat = stk3x1x_get_als_value(obj, obj->als); - if (copy_to_user(ptr, &dat, sizeof(dat))) { - err = -EFAULT; - goto err_out; - } - break; - - case ALSPS_GET_ALS_RAW_DATA: - err = stk3x1x_read_als(obj->client, &obj->als); - if (err) - goto err_out; - - dat = obj->als; - if (copy_to_user(ptr, &dat, sizeof(dat))) { - err = -EFAULT; - goto err_out; - } - break; - /*----------------------------------for factory mode test---------------------------------------*/ - case ALSPS_GET_PS_TEST_RESULT: - err = stk3x1x_read_ps(obj->client, &obj->ps); - if (err) - goto err_out; - - if (obj->ps > atomic_read(&obj->ps_high_thd_val)) - ps_result = 0; - else - ps_result = 1; - - if (copy_to_user(ptr, &ps_result, sizeof(ps_result))) { - err = -EFAULT; - goto err_out; - } - break; - - case ALSPS_IOCTL_CLR_CALI: - if (copy_from_user(&dat, ptr, sizeof(dat))) { - err = -EFAULT; - goto err_out; - } - if (dat == 0) - obj->ps_cali = 0; - break; - - case ALSPS_IOCTL_GET_CALI: - ps_cali = obj->ps_cali; - if (copy_to_user(ptr, &ps_cali, sizeof(ps_cali))) { - err = -EFAULT; - goto err_out; - } - break; - - case ALSPS_IOCTL_SET_CALI: - if (copy_from_user(&ps_cali, ptr, sizeof(ps_cali))) { - err = -EFAULT; - goto err_out; - } - - obj->ps_cali = ps_cali; - break; - - case ALSPS_SET_PS_THRESHOLD: - if (copy_from_user(threshold, ptr, sizeof(threshold))) { - err = -EFAULT; - goto err_out; - } - APS_ERR("%s set threshold high: 0x%x, low: 0x%x\n", __func__, threshold[0], - threshold[1]); - atomic_set(&obj->ps_high_thd_val, (threshold[0] + obj->ps_cali)); - atomic_set(&obj->ps_low_thd_val, (threshold[1] + obj->ps_cali)); /* need to confirm */ - - err = stk3x1x_write_ps_high_thd(obj->client, atomic_read(&obj->ps_high_thd_val)); - if (err) { - APS_ERR("write high thd error: %d\n", err); - goto err_out; - } - err = stk3x1x_write_ps_low_thd(obj->client, atomic_read(&obj->ps_low_thd_val)); - if (err) { - APS_ERR("write low thd error: %d\n", err); - goto err_out; - } - - break; - - case ALSPS_GET_PS_THRESHOLD_HIGH: - threshold[0] = atomic_read(&obj->ps_high_thd_val) - obj->ps_cali; - APS_ERR("%s get threshold high: 0x%x\n", __func__, threshold[0]); - err = copy_to_user(ptr, &threshold[0], sizeof(threshold[0])); - if (err) { - err = -EFAULT; - goto err_out; - } - break; - - case ALSPS_GET_PS_THRESHOLD_LOW: - threshold[0] = atomic_read(&obj->ps_low_thd_val) - obj->ps_cali; - APS_ERR("%s get threshold low: 0x%x\n", __func__, threshold[0]); - err = copy_to_user(ptr, &threshold[0], sizeof(threshold[0])); - if (err) { - err = -EFAULT; - goto err_out; - } - break; - - default: - APS_ERR("%s not supported = 0x%04x", __func__, cmd); - err = -ENOIOCTLCMD; - break; - } - -err_out: - return err; -} - -/*----------------------------------------------------------------------------*/ -static const struct file_operations stk3x1x_fops = { - .open = stk3x1x_open, - .release = stk3x1x_release, - .unlocked_ioctl = stk3x1x_unlocked_ioctl, -}; - -/*----------------------------------------------------------------------------*/ -static struct miscdevice stk3x1x_device = { - .minor = MISC_DYNAMIC_MINOR, - .name = "als_ps", - .fops = &stk3x1x_fops, -}; - -/*----------------------------------------------------------------------------*/ -/*----------------------------------------------------------------------------*/ -/* -static void stk3x1x_early_suspend(struct early_suspend *h) -{ - int err; - struct stk3x1x_priv *obj = container_of(h, struct stk3x1x_priv, early_drv); - int old = atomic_read(&obj->state_val); - - APS_FUN(); - - if (!obj) { - APS_ERR("null pointer!!\n"); - return; - } - - if (old & STK_STATE_EN_ALS_MASK) { - atomic_set(&obj->als_suspend, 1); - if ((err = stk3x1x_enable_als(obj->client, 0))) { - APS_ERR("disable als fail: %d\n", err); - } - } -} - -static void stk3x1x_late_resume(struct early_suspend *h) -{ - int err; - hwm_sensor_data sensor_data; - struct stk3x1x_priv *obj = container_of(h, struct stk3x1x_priv, early_drv); - - memset(&sensor_data, 0, sizeof(sensor_data)); - APS_FUN(); - - if (!obj) { - APS_ERR("null pointer!!\n"); - return; - } - if (atomic_read(&obj->als_suspend)) { - atomic_set(&obj->als_suspend, 0); - if (test_bit(STK_BIT_ALS, &obj->enable)) { - if ((err = stk3x1x_enable_als(obj->client, 1))) { - APS_ERR("enable als fail: %d\n", err); - - } - } - } -} -*/ - -/* -int stk3x1x_ps_operate(void *self, uint32_t command, void *buff_in, int size_in, - void *buff_out, int size_out, int *actualout) -{ - int err = 0; - int value; - hwm_sensor_data *sensor_data; - struct stk3x1x_priv *obj = (struct stk3x1x_priv *)self; - - switch (command) { - case SENSOR_DELAY: - if ((buff_in == NULL) || (size_in < sizeof(int))) { - APS_ERR("Set delay parameter error!\n"); - err = -EINVAL; - } - break; - - case SENSOR_ENABLE: - if ((buff_in == NULL) || (size_in < sizeof(int))) { - APS_ERR("Enable sensor parameter error!\n"); - err = -EINVAL; - } else { - value = *(int *)buff_in; - if (value) { - if ((err = stk3x1x_enable_ps(obj->client, 1))) { - APS_ERR("enable ps fail: %d\n", err); - return -1; - } - set_bit(STK_BIT_PS, &obj->enable); - } else { - if ((err = stk3x1x_enable_ps(obj->client, 0))) { - APS_ERR("disable ps fail: %d\n", err); - return -1; - } - clear_bit(STK_BIT_PS, &obj->enable); - } - } - break; - - case SENSOR_GET_DATA: - if ((buff_out == NULL) || (size_out < sizeof(hwm_sensor_data))) { - APS_ERR("get sensor data parameter error!\n"); - err = -EINVAL; - } else { - sensor_data = (hwm_sensor_data *) buff_out; - - if ((err = stk3x1x_read_ps(obj->client, &obj->ps))) { - err = -1; - } else { - value = stk3x1x_get_ps_value(obj, obj->ps); - if (value < 0) { - err = -1; - } else { - sensor_data->values[0] = value; - sensor_data->value_divide = 1; - sensor_data->status = SENSOR_STATUS_ACCURACY_MEDIUM; -#ifdef STK_PS_POLLING_LOG - APS_LOG("%s:ps raw 0x%x -> value 0x%x\n", __func__, - obj->ps, sensor_data->values[0]); -#endif - } - } - } - break; - default: - APS_ERR("proximity sensor operate function no this parameter %d!\n", command); - err = -1; - break; - } - - return err; -} -*/ -/* -int stk3x1x_als_operate(void *self, uint32_t command, void *buff_in, int size_in, - void *buff_out, int size_out, int *actualout) -{ - int err = 0; - int value; - hwm_sensor_data *sensor_data; - struct stk3x1x_priv *obj = (struct stk3x1x_priv *)self; - u8 flag; - - switch (command) { - case SENSOR_DELAY: - if ((buff_in == NULL) || (size_in < sizeof(int))) { - APS_ERR("Set delay parameter error!\n"); - err = -EINVAL; - } - break; - - case SENSOR_ENABLE: - if ((buff_in == NULL) || (size_in < sizeof(int))) { - APS_ERR("Enable sensor parameter error!\n"); - err = -EINVAL; - } else { - value = *(int *)buff_in; - if (value) { - if ((err = stk3x1x_enable_als(obj->client, 1))) { - APS_ERR("enable als fail: %d\n", err); - return -1; - } - set_bit(STK_BIT_ALS, &obj->enable); - } else { - if ((err = stk3x1x_enable_als(obj->client, 0))) { - APS_ERR("disable als fail: %d\n", err); - return -1; - } - clear_bit(STK_BIT_ALS, &obj->enable); - } - - } - break; - - case SENSOR_GET_DATA: - if ((buff_out == NULL) || (size_out < sizeof(hwm_sensor_data))) { - APS_ERR("get sensor data parameter error!\n"); - err = -EINVAL; - } else { - err = stk3x1x_read_flag(obj->client, &flag); - if (err) - return err; - - if (!(flag & STK_FLG_ALSDR_MASK)) - return -1; - - sensor_data = (hwm_sensor_data *) buff_out; - if ((err = stk3x1x_read_als(obj->client, &obj->als))) { - err = -1; - } else { - sensor_data->values[0] = stk3x1x_get_als_value(obj, obj->als); - sensor_data->value_divide = 1; - sensor_data->status = SENSOR_STATUS_ACCURACY_MEDIUM; - } - } - break; - default: - APS_ERR("light sensor operate function no this parameter %d!\n", command); - err = -1; - break; - } - - return err; -} -*/ -static int als_open_report_data(int open) -{ - /* should queuq work to report event if is_report_input_direct=true */ - return 0; -} - -/* if use this typ of enable , Gsensor only enabled but not report inputEvent to HAL */ -#ifdef CONFIG_CUSTOM_KERNEL_SENSORHUB -static int als_enable_nodata(int en) -{ - int res = 0; - SCP_SENSOR_HUB_DATA req; - int len; - - APS_LOG("stk3x1x_obj als enable value = %d\n", en); - - req.activate_req.sensorType = ID_LIGHT; - req.activate_req.action = SENSOR_HUB_ACTIVATE; - req.activate_req.enable = en; - len = sizeof(req.activate_req); - res = SCP_sensorHub_req_send(&req, &len, 1); - if (res) { - APS_ERR("als_enable_nodata is failed!!\n"); - return -1; - } - return 0; -} -#else -static int als_enable_nodata(int en) -{ - int res = 0; - - APS_LOG("stk3x1x_obj als enable value = %d\n", en); - if (!stk3x1x_obj) { - APS_ERR("stk3x1x_obj is null!!\n"); - return -1; - } - res = stk3x1x_enable_als(stk3x1x_obj->client, en); - if (res) { - APS_ERR("als_enable_nodata is failed!!\n"); - return -1; - } - return 0; -} -#endif -static int als_set_delay(u64 ns) -{ - return 0; -} - -#ifdef CONFIG_CUSTOM_KERNEL_SENSORHUB -static int als_get_data(int *value, int *status) -{ - int err = 0; - SCP_SENSOR_HUB_DATA req; - int len; - - req.get_data_req.sensorType = ID_LIGHT; - req.get_data_req.action = SENSOR_HUB_GET_DATA; - len = sizeof(req.get_data_req); - err = SCP_sensorHub_req_send(&req, &len, 1); - if (err) { - APS_ERR("SCP_sensorHub_req_send fail!\n"); - } else { - *value = req.get_data_rsp.int16_Data[0]; - *status = SENSOR_STATUS_ACCURACY_MEDIUM; - } - - if (atomic_read(&stk3x1x_obj->trace) & CMC_TRC_PS_DATA) - APS_LOG("value = %d\n", *value); - - return err; -} - -#else -static int als_get_data(int *value, int *status) -{ - int err = 0; - struct stk3x1x_priv *obj = NULL; - - if (!stk3x1x_obj) { - APS_ERR("stk3x1x_obj is null!!\n"); - return -1; - } - obj = stk3x1x_obj; - err = stk3x1x_read_als(obj->client, &obj->als); - if (err) { - err = -1; - } else { - *value = stk3x1x_get_als_value(obj, obj->als); - *status = SENSOR_STATUS_ACCURACY_MEDIUM; - } - - return err; -} -#endif - -static int ps_open_report_data(int open) -{ - /* should queuq work to report event if is_report_input_direct=true */ - return 0; -} - -/* if use this typ of enable , sensor only enabled but not report inputEvent to HAL */ -static int ps_enable_nodata(int en) -{ - int res = 0; -#ifdef CONFIG_CUSTOM_KERNEL_SENSORHUB - SCP_SENSOR_HUB_DATA req; - int len; -#endif - - APS_LOG("stk3x1x_obj als enable value = %d\n", en); - -#ifdef CONFIG_CUSTOM_KERNEL_SENSORHUB - req.activate_req.sensorType = ID_PROXIMITY; - req.activate_req.action = SENSOR_HUB_ACTIVATE; - req.activate_req.enable = en; - len = sizeof(req.activate_req); - res = SCP_sensorHub_req_send(&req, &len, 1); -#else - if (!stk3x1x_obj) { - APS_ERR("stk3x1x_obj is null!!\n"); - return -1; - } - res = stk3x1x_enable_ps(stk3x1x_obj->client, en); -#endif - - if (res) { - APS_ERR("als_enable_nodata is failed!!\n"); - return -1; - } - return 0; - -} - -static int ps_set_delay(u64 ns) -{ - return 0; -} - -static int ps_get_data(int *value, int *status) -{ - int err = 0; -#ifdef CONFIG_CUSTOM_KERNEL_SENSORHUB - SCP_SENSOR_HUB_DATA req; - int len; -#endif - -#ifdef CONFIG_CUSTOM_KERNEL_SENSORHUB - req.get_data_req.sensorType = ID_PROXIMITY; - req.get_data_req.action = SENSOR_HUB_GET_DATA; - len = sizeof(req.get_data_req); - err = SCP_sensorHub_req_send(&req, &len, 1); - if (err) { - APS_ERR("SCP_sensorHub_req_send fail!\n"); - } else { - *value = req.get_data_rsp.int16_Data[0]; - *status = SENSOR_STATUS_ACCURACY_MEDIUM; - } - - if (atomic_read(&stk3x1x_obj->trace) & CMC_TRC_PS_DATA) - APS_LOG("value = %d\n", *value); -#else - if (!stk3x1x_obj) { - APS_ERR("stk3x1x_obj is null!!\n"); - return -1; - } - - err = stk3x1x_read_ps(stk3x1x_obj->client, &stk3x1x_obj->ps); - if (err) { - err = -1; - } else { - *value = stk3x1x_get_ps_value(stk3x1x_obj, stk3x1x_obj->ps); - *status = SENSOR_STATUS_ACCURACY_MEDIUM; - } -#endif - - return 0; -} - - -/*----------------------------------------------------------------------------*/ -static int stk3x1x_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) -{ - int err = 0; - struct stk3x1x_priv *obj; - struct als_control_path als_ctl = { 0 }; - struct als_data_path als_data = { 0 }; - struct ps_control_path ps_ctl = { 0 }; - struct ps_data_path ps_data = { 0 }; - - APS_LOG("%s: driver version: %s\n", __func__, DRIVER_VERSION); - if (!(obj = kzalloc(sizeof(*obj), GFP_KERNEL))) { - err = -ENOMEM; - goto exit; - } - - stk3x1x_obj = obj; - obj->hw = hw; - stk3x1x_get_addr(obj->hw, &obj->addr); - - INIT_DELAYED_WORK(&obj->eint_work, stk3x1x_eint_work); - obj->client = client; - i2c_set_clientdata(client, obj); - atomic_set(&obj->als_debounce, 200); - atomic_set(&obj->als_deb_on, 0); - atomic_set(&obj->als_deb_end, 0); - atomic_set(&obj->ps_debounce, 100); - atomic_set(&obj->ps_deb_on, 0); - atomic_set(&obj->ps_deb_end, 0); - atomic_set(&obj->ps_mask, 0); - atomic_set(&obj->trace, 0x00); - atomic_set(&obj->als_suspend, 0); - - atomic_set(&obj->state_val, 0x0); - atomic_set(&obj->psctrl_val, PSCTRL_VAL); - atomic_set(&obj->alsctrl_val, ALSCTRL_VAL); - obj->ledctrl_val = LEDCTRL_VAL; - obj->wait_val = WAIT_VAL; - obj->int_val = 0; - obj->first_boot = true; - obj->als_correct_factor = 1000; - obj->ps_cali = 0; - - atomic_set(&obj->ps_high_thd_val, obj->hw->ps_threshold_high); - atomic_set(&obj->ps_low_thd_val, obj->hw->ps_threshold_low); - - atomic_set(&obj->recv_reg, 0); - obj->irq_node = of_find_compatible_node(NULL, NULL, "mediatek, als-eint"); - - if (obj->hw->polling_mode_ps == 0) - APS_LOG("%s: enable PS interrupt\n", __func__); - obj->int_val |= STK_INT_PS_MODE1; - - if (obj->hw->polling_mode_als == 0) { - obj->int_val |= STK_INT_ALS; - APS_LOG("%s: enable ALS interrupt\n", __func__); - } - - APS_LOG - ("%s: state=0x%x, psctrl=0x%x, alsctrl=0x%x, ledctrl=0x%x, wait=0x%x, int=0x%x\n", - __func__, atomic_read(&obj->state_val), atomic_read(&obj->psctrl_val), - atomic_read(&obj->alsctrl_val), obj->ledctrl_val, obj->wait_val, obj->int_val); - - obj->enable = 0; - obj->pending_intr = 0; - obj->als_level_num = sizeof(obj->hw->als_level) / sizeof(obj->hw->als_level[0]); - obj->als_value_num = sizeof(obj->hw->als_value) / sizeof(obj->hw->als_value[0]); - BUG_ON(sizeof(obj->als_level) != sizeof(obj->hw->als_level)); - memcpy(obj->als_level, obj->hw->als_level, sizeof(obj->als_level)); - BUG_ON(sizeof(obj->als_value) != sizeof(obj->hw->als_value)); - memcpy(obj->als_value, obj->hw->als_value, sizeof(obj->als_value)); - atomic_set(&obj->i2c_retry, 3); - if (atomic_read(&obj->state_val) & STK_STATE_EN_ALS_MASK) { - set_bit(STK_BIT_ALS, &obj->enable); - } - - if (atomic_read(&obj->state_val) & STK_STATE_EN_PS_MASK) { - set_bit(STK_BIT_PS, &obj->enable); - } - - stk3x1x_i2c_client = client; - - err = stk3x1x_init_client(client); - if (err) - goto exit_init_failed; - - err = misc_register(&stk3x1x_device); - if (err) { - APS_ERR("stk3x1x_device register failed\n"); - goto exit_misc_device_register_failed; - } - als_ctl.is_use_common_factory = false; - ps_ctl.is_use_common_factory = false; - - err = stk3x1x_create_attr(&(stk3x1x_init_info.platform_diver_addr->driver)); - if (err) { - APS_ERR("create attribute err = %d\n", err); - goto exit_create_attr_failed; - } - - - - als_ctl.open_report_data = als_open_report_data; - als_ctl.enable_nodata = als_enable_nodata; - als_ctl.set_delay = als_set_delay; - als_ctl.is_report_input_direct = false; -#ifdef CONFIG_CUSTOM_KERNEL_SENSORHUB - als_ctl.is_support_batch = obj->hw->is_batch_supported_als; -#else - als_ctl.is_support_batch = false; -#endif - - err = als_register_control_path(&als_ctl); - if (err) { - APS_ERR("register fail = %d\n", err); - goto exit_sensor_obj_attach_fail; - } - - als_data.get_data = als_get_data; - als_data.vender_div = 100; - err = als_register_data_path(&als_data); - if (err) { - APS_ERR("tregister fail = %d\n", err); - goto exit_sensor_obj_attach_fail; - } - - - ps_ctl.open_report_data = ps_open_report_data; - ps_ctl.enable_nodata = ps_enable_nodata; - ps_ctl.set_delay = ps_set_delay; - ps_ctl.is_report_input_direct = true; -#ifdef CONFIG_CUSTOM_KERNEL_SENSORHUB - ps_ctl.is_support_batch = obj->hw->is_batch_supported_ps; -#else - ps_ctl.is_support_batch = false; -#endif - - err = ps_register_control_path(&ps_ctl); - if (err) { - APS_ERR("register fail = %d\n", err); - goto exit_sensor_obj_attach_fail; - } - - ps_data.get_data = ps_get_data; - ps_data.vender_div = 100; - err = ps_register_data_path(&ps_data); - if (err) { - APS_ERR("tregister fail = %d\n", err); - goto exit_sensor_obj_attach_fail; - } - - err = batch_register_support_info(ID_LIGHT, als_ctl.is_support_batch, 100, 0); - if (err) - APS_ERR("register light batch support err = %d\n", err); - - err = batch_register_support_info(ID_PROXIMITY, ps_ctl.is_support_batch, 100, 0); - if (err) - APS_ERR("register proximity batch support err = %d\n", err); - - stk3x1x_init_flag = 0; - APS_LOG("%s: OK\n", __func__); - return 0; - -exit_sensor_obj_attach_fail: -exit_create_attr_failed: - misc_deregister(&stk3x1x_device); -exit_misc_device_register_failed: -exit_init_failed: - kfree(obj); - obj = NULL; -exit: - stk3x1x_i2c_client = NULL; - stk3x1x_init_flag = -1; - APS_ERR("%s: err = %d\n", __func__, err); - return err; -} - -static int stk3x1x_i2c_remove(struct i2c_client *client) -{ - int err; - - err = stk3x1x_delete_attr(&(stk3x1x_init_info.platform_diver_addr->driver)); - if (err) - APS_ERR("stk3x1x_delete_attr fail: %d\n", err); - - //err = misc_deregister(&stk3x1x_device); - if (err) - APS_ERR("misc_deregister fail: %d\n", err); - - stk3x1x_i2c_client = NULL; - i2c_unregister_device(client); - kfree(i2c_get_clientdata(client)); - - return 0; -} - -/*----------------------------------------------------------------------------*/ -static int stk3x1x_local_init(void) -{ - stk3x1x_power(hw, 1); - if (i2c_add_driver(&stk3x1x_i2c_driver)) { - APS_ERR("add driver error\n"); - return -1; - } - if (-1 == stk3x1x_init_flag) - return -1; - - return 0; -} - -static int stk3x1x_local_uninit(void) -{ - APS_FUN(); - stk3x1x_power(hw, 0); - i2c_del_driver(&stk3x1x_i2c_driver); - return 0; -} - - -/*----------------------------------------------------------------------------*/ -static int __init stk3x1x_init(void) -{ - const char *name = "mediatek,stk3x1x"; - - APS_FUN(); - - hw = get_alsps_dts_func(name, hw); - if (!hw) - APS_ERR("get dts info fail\n"); - alsps_driver_add(&stk3x1x_init_info); - return 0; -} - -static void __exit stk3x1x_exit(void) -{ - APS_FUN(); -} - -/*----------------------------------------------------------------------------*/ -module_init(stk3x1x_init); -module_exit(stk3x1x_exit); -/*----------------------------------------------------------------------------*/ -MODULE_AUTHOR("MingHsien Hsieh"); -MODULE_DESCRIPTION("SensorTek stk3x1x proximity and light sensor driver"); -MODULE_LICENSE("GPL"); +/* Copyright Statement: + * + * This software/firmware and related documentation ("MediaTek Software") are + * protected under relevant copyright laws. The information contained herein + * is confidential and proprietary to MediaTek Inc. and/or its licensors. + * Without the prior written permission of MediaTek inc. and/or its licensors, + * any reproduction, modification, use or disclosure of MediaTek Software, + * and information contained herein, in whole or in part, shall be strictly prohibited. + */ +/* MediaTek Inc. (C) 2010. All rights reserved. + * + * BY OPENING THIS FILE, RECEIVER HEREBY UNEQUIVOCALLY ACKNOWLEDGES AND AGREES + * THAT THE SOFTWARE/FIRMWARE AND ITS DOCUMENTATIONS ("MEDIATEK SOFTWARE") + * RECEIVED FROM MEDIATEK AND/OR ITS REPRESENTATIVES ARE PROVIDED TO RECEIVER ON + * AN "AS-IS" BASIS ONLY. MEDIATEK EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NONINFRINGEMENT. + * NEITHER DOES MEDIATEK PROVIDE ANY WARRANTY WHATSOEVER WITH RESPECT TO THE + * SOFTWARE OF ANY THIRD PARTY WHICH MAY BE USED BY, INCORPORATED IN, OR + * SUPPLIED WITH THE MEDIATEK SOFTWARE, AND RECEIVER AGREES TO LOOK ONLY TO SUCH + * THIRD PARTY FOR ANY WARRANTY CLAIM RELATING THERETO. RECEIVER EXPRESSLY ACKNOWLEDGES + * THAT IT IS RECEIVER'S SOLE RESPONSIBILITY TO OBTAIN FROM ANY THIRD PARTY ALL PROPER LICENSES + * CONTAINED IN MEDIATEK SOFTWARE. MEDIATEK SHALL ALSO NOT BE RESPONSIBLE FOR ANY MEDIATEK + * SOFTWARE RELEASES MADE TO RECEIVER'S SPECIFICATION OR TO CONFORM TO A PARTICULAR + * STANDARD OR OPEN FORUM. RECEIVER'S SOLE AND EXCLUSIVE REMEDY AND MEDIATEK'S ENTIRE AND + * CUMULATIVE LIABILITY WITH RESPECT TO THE MEDIATEK SOFTWARE RELEASED HEREUNDER WILL BE, + * AT MEDIATEK'S OPTION, TO REVISE OR REPLACE THE MEDIATEK SOFTWARE AT ISSUE, + * OR REFUND ANY SOFTWARE LICENSE FEES OR SERVICE CHARGE PAID BY RECEIVER TO + * MEDIATEK FOR SUCH MEDIATEK SOFTWARE AT ISSUE. + * + * The following software/firmware and/or related documentation ("MediaTek Software") + * have been modified by MediaTek Inc. All revisions are subject to any receiver's + * applicable license agreements with MediaTek Inc. + */ + +/* drivers/hwmon/mt6516/amit/stk3x1x.c - stk3x1x ALS/PS driver + * + * Author: MingHsien Hsieh + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "cust_alsps.h" +#include "alsps.h" +#include "stk3x1x.h" + +#define DRIVER_VERSION "3.9.1" +#define STK_PS_POLLING_LOG +//#define STK_PS_DEBUG +#define STK_TUNE0 +// #define STK_TUNE1 +#define CALI_EVERY_TIME +#define STK_ALS_FIR +// #define STK_IRS +//#define STK_CHK_REG +// #define STK_GES +#define MTK_AUTO_DETECT_ALSPS + +#define STK3X1X_I2C_ADDR 0x48 + +//extern struct alsps_hw* get_cust_alsps_hw(void); +extern struct platform_device *get_alsps_platformdev(void); + +/****************************************************************************** + * configuration +*******************************************************************************/ + +/*----------------------------------------------------------------------------*/ +#define stk3x1x_DEV_NAME "stk3x1x" +/*----------------------------------------------------------------------------*/ +#define APS_TAG "[ALS/PS] " +/* +#define APS_FUN(f) printk(KERN_ERR APS_TAG"%s\n", __FUNCTION__) +#define APS_ERR(fmt, args...) printk(KERN_ERR APS_TAG"%s %d : "fmt, __FUNCTION__, __LINE__, ##args) +#define APS_LOG(fmt, args...) printk(KERN_ERR APS_TAG fmt, ##args) +#define APS_DBG(fmt, args...) printk(KERN_ERR fmt, ##args) +*/ +#define APS_FUN(f) pr_debug(APS_TAG"%s\n", __func__) +#define APS_ERR(fmt, args...) pr_err(APS_TAG"%s %d : "fmt, __func__, __LINE__, ##args) +#define APS_LOG(fmt, args...) pr_err(APS_TAG fmt, ##args) +#define APS_DBG(fmt, args...) pr_err(APS_TAG fmt, ##args) +/****************************************************************************** + * extern functions +*******************************************************************************/ +#ifdef STK_TUNE0 +/* [yanlin start] modified calibration */ + #define STK_MAX_MIN_DIFF 200 +// #define STK_LT_N_CT 100 + #define STK_LT_N_CT 22 +// #define STK_HT_N_CT 150 + #define STK_HT_N_CT 40 + #define PS_THDH_LIMITED 1700 + #define PS_THDL_LIMITED 1600 + #define PS_MAX_THD_RANGE 1500 +/* [yanlin end] modified calibration */ +#endif /* #ifdef STK_TUNE0 */ + +#ifdef STK_TUNE1 + #define STK_FIN_THD (2000) + #define STK_ALS_NEAR (40) + #define STK_ALS_FAR (50) + #define STK_PS_HIGHER_THDH (STK_FIN_THD - 400) + #define STK_PS_HIGHER_THDL (STK_FIN_THD - 500) + + #define STK_PS_CT_LIMIT STK_FIN_THD + #define PS_NOISE (35) + #define STK_CT_UPDATE_DIFF (40) + + #define STK_PS_VAL_BUF (20) + #define STK_INT_NEAR_SAMPLE_NO (10) + #define STK_TUNE1_INVALID_PS_LIMIT (5) +#endif // #ifdef STK_TUNE1 + +#define STK_IRC_MAX_ALS_CODE 20000 +#define STK_IRC_MIN_ALS_CODE 25 +#define STK_IRC_MIN_IR_CODE 50 +#define STK_IRC_ALS_DENOMI 2 +#define STK_IRC_ALS_NUMERA 5 +#define STK_IRC_ALS_CORREC 748 + +#define STK_IRS_IT_REDUCE 2 +#define STK_ALS_READ_IRS_IT_REDUCE 5 +#define STK_ALS_THRESHOLD 30 + +#define STK3310SA_PID 0x17 +#define STK3311SA_PID 0x1E +#define STK3311WV_PID 0x1D +#define STK3311X_PID 0x12 +#define STK33119_PID 0x11 + +/*----------------------------------------------------------------------------*/ +static struct i2c_client *stk3x1x_i2c_client = NULL; +static int distance_flag = 1; /* hw default away after enable. */ + +// [wanglei start] +// Add for als adjust +#if defined(CONFIG_TPLINK_PRODUCT_TP904) +static int winfac = 155; +#else +static int winfac = 100; +#endif +// [wanglei end] +/*----------------------------------------------------------------------------*/ +static const struct i2c_device_id stk3x1x_i2c_id[] = {{stk3x1x_DEV_NAME,0},{}}; +//static struct regulator *stk3x1x_regulator; +/*----------------------------------------------------------------------------*/ +static int stk3x1x_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id); +static int stk3x1x_i2c_remove(struct i2c_client *client); +static int stk3x1x_i2c_detect(struct i2c_client *client, struct i2c_board_info *info); +/*----------------------------------------------------------------------------*/ +//static int stk3x1x_i2c_suspend(struct i2c_client *client, pm_message_t msg); +//static int stk3x1x_i2c_resume(struct i2c_client *client); +//static struct stk3x1x_priv *g_stk3x1x_ptr = NULL; + +#ifndef C_I2C_FIFO_SIZE + #define C_I2C_FIFO_SIZE 8 +#endif +static DEFINE_MUTEX(STK3X1X_i2c_mutex); +static int stk3x1x_init_flag = -1; // 0<==>OK -1 <==> fail +static int stk3x1x_calibration_flag = 0; +static int stk3x1x_local_init(void); +static int stk3x1x_local_uninit(void); +// static struct sensor_init_info stk3x1x_init_info = { +static struct alsps_init_info stk3x1x_init_info = { + .name = "stk3x1x", + .init = stk3x1x_local_init, + .uninit = stk3x1x_local_uninit, +}; + +static struct alsps_hw alsps_cust; +static struct alsps_hw *hw = &alsps_cust; +static struct platform_device *alspsPltFmDev; +/* For alsp driver get cust info */ +struct alsps_hw *get_cust_alsps(void) +{ + return &alsps_cust; +} +/*----------------------------------------------------------------------------*/ +typedef enum { + STK_TRC_ALS_DATA= 0x0001, + STK_TRC_PS_DATA = 0x0002, + STK_TRC_EINT = 0x0004, + STK_TRC_IOCTL = 0x0008, + STK_TRC_I2C = 0x0010, + STK_TRC_CVT_ALS = 0x0020, + STK_TRC_CVT_PS = 0x0040, + STK_TRC_DEBUG = 0x8000, +} STK_TRC; +/*----------------------------------------------------------------------------*/ +typedef enum { + STK_BIT_ALS = 1, + STK_BIT_PS = 2, +} STK_BIT; +/*----------------------------------------------------------------------------*/ +struct stk3x1x_i2c_addr { +/*define a series of i2c slave address*/ + u8 state; /* enable/disable state */ + u8 psctrl; /* PS control */ + u8 alsctrl; /* ALS control */ + u8 ledctrl; /* LED control */ + u8 intmode; /* INT mode */ + u8 wait; /* wait time */ + u8 thdh1_ps; /* PS INT threshold high 1 */ + u8 thdh2_ps; /* PS INT threshold high 2 */ + u8 thdl1_ps; /* PS INT threshold low 1 */ + u8 thdl2_ps; /* PS INT threshold low 2 */ + u8 thdh1_als; /* ALS INT threshold high 1 */ + u8 thdh2_als; /* ALS INT threshold high 2 */ + u8 thdl1_als; /* ALS INT threshold low 1 */ + u8 thdl2_als; /* ALS INT threshold low 2 */ + u8 flag; /* int flag */ + u8 data1_ps; /* ps data1 */ + u8 data2_ps; /* ps data2 */ + u8 data1_als; /* als data1 */ + u8 data2_als; /* als data2 */ + u8 data1_offset; /* offset data1 */ + u8 data2_offset; /* offset data2 */ + u8 data1_ir; /* ir data1 */ + u8 data2_ir; /* ir data2 */ + u8 soft_reset; /* software reset */ +}; +/*----------------------------------------------------------------------------*/ +#ifdef STK_ALS_FIR + #define STK_FIR_LEN 8 + #define MAX_FIR_LEN 32 +struct data_filter { + u16 raw[MAX_FIR_LEN]; + int sum; + int num; + int idx; +}; +#endif + +#ifdef STK_GES +union stk_ges_operation{ + uint8_t ops[4]; + struct { + uint8_t rw_len_retry; + uint8_t reg; + uint8_t reg_value_retry_crit; + uint8_t sleep_10ns; + }action; +}; + +union stk_ges_operation stk_ges_op[10] = +{ + {.ops={0xc1, 0x24, 0, 0}}, + {.ops={0, 0, 0, 0}}, + {.ops={0, 0, 0, 0}}, + {.ops={0, 0, 0, 0}}, + {.ops={0, 0, 0, 0}}, + {.ops={0, 0, 0, 0}}, + {.ops={0, 0, 0, 0}}, + {.ops={0, 0, 0, 0}}, + {.ops={0, 0, 0, 0}}, + {.ops={0, 0, 0, 0}} +}; +#endif + +#ifdef STK_TUNE1 +enum ps_int_state +{ + STK_PSINT_NORM = 0, + STK_PSINT_FIN_DET, + STK_PSINT_CT_DET +}; +#endif + +struct stk3x1x_priv { + struct alsps_hw *hw; + struct i2c_client *client; + struct delayed_work eint_work; + + /*i2c address group*/ + struct stk3x1x_i2c_addr addr; + + /*misc*/ + atomic_t trace; + atomic_t i2c_retry; + atomic_t als_suspend; + atomic_t als_debounce; /*debounce time after enabling als*/ + atomic_t als_deb_on; /*indicates if the debounce is on*/ + atomic_t als_deb_end; /*the jiffies representing the end of debounce*/ + atomic_t ps_mask; /*mask ps: always return far away*/ + atomic_t ps_debounce; /*debounce time after enabling ps*/ + atomic_t ps_deb_on; /*indicates if the debounce is on*/ + atomic_t ps_deb_end; /*the jiffies representing the end of debounce*/ + atomic_t ps_suspend; + atomic_t init_done; + struct device_node *irq_node; + int irq; + + /*data*/ + u16 als; + u16 ps; + u8 _align; + u16 als_level_num; + u16 als_value_num; + u32 als_level[C_CUST_ALS_LEVEL-1]; + u32 als_value[C_CUST_ALS_LEVEL]; + + atomic_t state_val; + atomic_t psctrl_val; + atomic_t alsctrl_val; + u8 wait_val; + u8 ledctrl_val; + u8 int_val; + + atomic_t ps_high_thd_val; /*the cmd value can't be read, stored in ram*/ + atomic_t ps_low_thd_val; /*the cmd value can't be read, stored in ram*/ + ulong enable; /*enable mask*/ + ulong pending_intr; /*pending interrupt*/ + atomic_t recv_reg; + /*early suspend*/ +#if defined(CONFIG_HAS_EARLYSUSPEND) + struct early_suspend early_drv; +#endif + bool first_boot; + struct hrtimer ps_tune0_timer; + struct workqueue_struct *stk_ps_tune0_wq; + struct work_struct stk_ps_tune0_work; +#ifdef STK_TUNE0 + uint16_t psa; + uint16_t psi; + uint16_t psi_set; + uint16_t stk_max_min_diff; + uint16_t stk_lt_n_ct; + uint16_t stk_ht_n_ct; + uint16_t crosstalk; +#ifdef CALI_EVERY_TIME + uint16_t ps_high_thd_boot; + uint16_t ps_low_thd_boot; +#endif + uint16_t boot_cali; + + ktime_t ps_tune0_delay; + bool tune_zero_init_proc; + uint32_t ps_stat_data[3]; + int data_count; +#endif +#ifdef STK_ALS_FIR + struct data_filter fir; + atomic_t firlength; +#endif + uint16_t ir_code; + uint16_t als_correct_factor; + u16 als_last; +#ifdef STK_GES + struct input_dev *ges_input_dev; + int ges_enabled; + bool re_enable_ges; + int re_enable_ges2; + atomic_t gesture2; +#endif + bool re_enable_ps; + bool re_enable_als; + u16 ps_cali; + + uint8_t pid; + uint8_t p_wv_r_bd_with_co; + uint32_t als_code_last; + uint16_t als_data_index; + uint8_t ps_distance_last; +#ifdef STK_TUNE1 + enum ps_int_state stk_int_handle_state; + int invalid_ps_cnt; +#endif + uint8_t p_1x_r_bd_with_co; + uint8_t p_19_r_bc; +}; +/*----------------------------------------------------------------------------*/ +#ifdef CONFIG_OF +static const struct of_device_id alsps_of_match[] = { + {.compatible = "mediatek,alsps"}, + {}, +}; +#endif + +static struct i2c_driver stk3x1x_i2c_driver = { + .probe = stk3x1x_i2c_probe, + .remove = stk3x1x_i2c_remove, + .detect = stk3x1x_i2c_detect, +// .suspend = stk3x1x_i2c_suspend, +// .resume = stk3x1x_i2c_resume, + .id_table = stk3x1x_i2c_id, + .driver = { + .name = stk3x1x_DEV_NAME, +#ifdef CONFIG_OF + .of_match_table = alsps_of_match, +#endif + }, +}; + +static struct stk3x1x_priv *stk3x1x_obj = NULL; +static int stk3x1x_get_ps_value(struct stk3x1x_priv *obj, u16 ps); +static int stk3x1x_get_ps_value_only(struct stk3x1x_priv *obj, u16 ps); +static int stk3x1x_get_als_value(struct stk3x1x_priv *obj, u16 als); +static int stk3x1x_read_als(struct i2c_client *client, u16 *data); +static int stk3x1x_read_ps(struct i2c_client *client, u16 *data); +//static int stk3x1x_set_als_int_thd(struct i2c_client *client, u16 als_data_reg); +static int32_t stk3x1x_get_ir_value(struct stk3x1x_priv *obj, int32_t als_it_reduce); +#ifdef STK_TUNE0 +static int stk_ps_tune_zero_func_fae(struct stk3x1x_priv *obj); +#endif +#ifdef STK_CHK_REG +static int stk3x1x_validate_n_handle(struct i2c_client *client); +#endif +static int stk3x1x_init_client(struct i2c_client *client); +static struct wake_lock ps_lock; +#ifdef STK_GES +static uint32_t stk3x1x_get_ges_value(struct stk3x1x_priv *obj, unsigned int *ges0, unsigned int *ges1, unsigned int *ges2); +static int32_t stk3x1x_enable_ges(struct i2c_client *client, int enable, int mode); +#endif +static DEFINE_MUTEX(run_cali_mutex); +/*----------------------------------------------------------------------------*/ +int stk3x1x_get_addr(struct alsps_hw *hw, struct stk3x1x_i2c_addr *addr) +{ + if(!hw || !addr) + { + return -EFAULT; + } + addr->state = STK_STATE_REG; + addr->psctrl = STK_PSCTRL_REG; + addr->alsctrl = STK_ALSCTRL_REG; + addr->ledctrl = STK_LEDCTRL_REG; + addr->intmode = STK_INT_REG; + addr->wait = STK_WAIT_REG; + addr->thdh1_ps = STK_THDH1_PS_REG; + addr->thdh2_ps = STK_THDH2_PS_REG; + addr->thdl1_ps = STK_THDL1_PS_REG; + addr->thdl2_ps = STK_THDL2_PS_REG; + addr->thdh1_als = STK_THDH1_ALS_REG; + addr->thdh2_als = STK_THDH2_ALS_REG; + addr->thdl1_als = STK_THDL1_ALS_REG ; + addr->thdl2_als = STK_THDL2_ALS_REG; + addr->flag = STK_FLAG_REG; + addr->data1_ps = STK_DATA1_PS_REG; + addr->data2_ps = STK_DATA2_PS_REG; + addr->data1_als = STK_DATA1_ALS_REG; + addr->data2_als = STK_DATA2_ALS_REG; + addr->data1_offset = STK_DATA1_OFFSET_REG; + addr->data2_offset = STK_DATA2_OFFSET_REG; + addr->data1_ir = STK_DATA1_IR_REG; + addr->data2_ir = STK_DATA2_IR_REG; + addr->soft_reset = STK_SW_RESET_REG; + + return 0; +} +/*----------------------------------------------------------------------------*/ +int stk3x1x_hwmsen_read_block(struct i2c_client *client, u8 addr, u8 *data, u8 len) +{ + int err; + u8 beg = addr; + struct i2c_msg msgs[2] = + { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf= &beg + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = len, + .buf = data, + } + }; + + mutex_lock(&STK3X1X_i2c_mutex); + if (!client) + { + mutex_unlock(&STK3X1X_i2c_mutex); + return -EINVAL; + } + else if (len > C_I2C_FIFO_SIZE) + { + mutex_unlock(&STK3X1X_i2c_mutex); + APS_LOG(" length %d exceeds %d\n", len, C_I2C_FIFO_SIZE); + return -EINVAL; + } + + err = i2c_transfer(client->adapter, msgs, sizeof(msgs)/sizeof(msgs[0])); + mutex_unlock(&STK3X1X_i2c_mutex); + if (err != 2) + { + APS_LOG("i2c_transfer error: (%d %p %d) %d\n", addr, data, len, err); + err = -EIO; + } + else + { + err = 0;/*no error*/ + } + return err; +} +/*----------------------------------------------------------------------------*/ +int stk3x1x_get_timing(void) +{ + return 200; +} + +/*----------------------------------------------------------------------------*/ +int stk3x1x_master_recv(struct i2c_client *client, u16 addr, u8 *buf ,int count) +{ + struct stk3x1x_priv *obj = i2c_get_clientdata(client); + int ret = 0, retry = 0; + int trc = atomic_read(&obj->trace); + int max_try = atomic_read(&obj->i2c_retry); + + while(retry++ < max_try) + { + ret = stk3x1x_hwmsen_read_block(client, addr, buf, count); + if(ret == 0) + break; + udelay(100); + } + + if(unlikely(trc)) + { + if((retry != 1) && (trc & STK_TRC_DEBUG)) + { + APS_LOG("(recv) %d/%d\n", retry-1, max_try); + + } + } + + /* If everything went ok (i.e. 1 msg transmitted), return #bytes + transmitted, else error code. */ + return (ret == 0) ? count : ret; +} +/*----------------------------------------------------------------------------*/ +int stk3x1x_master_send(struct i2c_client *client, u16 addr, u8 *buf ,int count) +{ + int ret = 0, retry = 0; + struct stk3x1x_priv *obj = i2c_get_clientdata(client); + int trc = atomic_read(&obj->trace); + int max_try = atomic_read(&obj->i2c_retry); + + + while(retry++ < max_try) + { + ret = hwmsen_write_block(client, addr, buf, count); + if (ret == 0) + break; + udelay(100); + } + + if(unlikely(trc)) + { + if((retry != 1) && (trc & STK_TRC_DEBUG)) + { + APS_LOG("(send) %d/%d\n", retry-1, max_try); + } + } + /* If everything went ok (i.e. 1 msg transmitted), return #bytes + transmitted, else error code. */ + return (ret == 0) ? count : ret; +} +/*----------------------------------------------------------------------------*/ +static int stk3x1x_otp_read_byte_data(struct i2c_client *client, unsigned char command) +{ + int ret; + int value; + u8 data; + + data = 0x2; + ret = stk3x1x_master_send(client, 0x0, &data, 1); + if (ret < 0) + { + APS_ERR("write 0x0 = %d\n", ret); + return -EFAULT; + } + + data = command; + ret = stk3x1x_master_send(client, 0x90, &data, 1); + if (ret < 0) + { + APS_ERR("write 0x90 = %d\n", ret); + return -EFAULT; + } + + data = 0x82; + ret = stk3x1x_master_send(client, 0x92, &data, 1); + if (ret < 0) + { + APS_ERR("write 0x0 = %d\n", ret); + return -EFAULT; + } + usleep_range(2000, 4000); + + ret = stk3x1x_master_recv(client, 0x91, &data, 1); + if(ret < 0) + { + APS_DBG("error: %d\n", ret); + return -EFAULT; + } + value = data; + APS_LOG("%s: 0x%x=0x%x\n", __func__, command, value); + + data = 0x0; + ret = stk3x1x_master_send(client, 0x0, &data, 1); + if (ret < 0) + { + APS_ERR("write 0x0 = %d\n", ret); + return -EFAULT; + } + + return value; +} +/*----------------------------------------------------------------------------*/ +int stk3x1x_write_led(struct i2c_client *client, u8 data) +{ + struct stk3x1x_priv *obj = i2c_get_clientdata(client); + int ret = 0; + + ret = stk3x1x_master_send(client, obj->addr.ledctrl, &data, 1); + if(ret < 0) + { + APS_ERR("write led = %d\n", ret); + return -EFAULT; + } + + return 0; +} +/*----------------------------------------------------------------------------*/ +void stk_als_ir_get_corr(struct stk3x1x_priv *obj, u32 als_data) +{ + int32_t als_comperator; + + if(obj->ir_code) + { + obj->als_correct_factor = 1000; + if(als_data < STK_IRC_MAX_ALS_CODE && als_data > STK_IRC_MIN_ALS_CODE && + obj->ir_code > STK_IRC_MIN_IR_CODE) + { + als_comperator = als_data * STK_IRC_ALS_NUMERA / STK_IRC_ALS_DENOMI; + if(obj->ir_code > als_comperator) + obj->als_correct_factor = STK_IRC_ALS_CORREC; + } + APS_LOG("%s: als=%d, ir=%d, als_correct_factor=%d", __func__, als_data, obj->ir_code, obj->als_correct_factor); + obj->ir_code = 0; + } + return; +} + +/*----------------------------------------------------------------------------*/ +#ifdef STK_IRS +int stk_als_ir_skip_als(struct stk3x1x_priv *obj) +{ + int ret; + u8 buf[2]; + + if(obj->als_data_index < 60000) + obj->als_data_index++; + else + obj->als_data_index = 0; + + if( obj->als_data_index % 10 == 1) + { + ret = stk3x1x_master_recv(obj->client, obj->addr.data1_als, buf, 0x02); + if(ret < 0) + { + APS_DBG("error: %d\n", ret); + return -EFAULT; + } + return 1; + } + + return 0; +} +/*----------------------------------------------------------------------------*/ +static int stk_als_ir_run(struct stk3x1x_priv *obj) +{ + u32 ir_data; + + if( obj->als_data_index % 10 == 0) + { + if(obj->ps_distance_last != 0 && obj->ir_code == 0) + { + ir_data = stk3x1x_get_ir_value(obj, STK_IRS_IT_REDUCE); + if(ir_data > 0) + obj->ir_code = ir_data; + } + return ir_data; + } + return 0; +} +#endif /* #ifdef STK_IRS */ +/*----------------------------------------------------------------------------*/ + +#define STK_ALS_THRESHOLD 30 +int stk3x1x_read_als(struct i2c_client *client, u16 *data) +{ + struct stk3x1x_priv *obj = i2c_get_clientdata(client); + int ret = 0; + u8 buf[2]; + u32 als_data; + int32_t ir_data; +#ifdef STK_IRS + const int ir_enlarge = 1 << (STK_ALS_READ_IRS_IT_REDUCE - STK_IRS_IT_REDUCE); +#endif +#ifdef STK_ALS_FIR + int idx; + int firlen = atomic_read(&obj->firlength); +#endif + if(NULL == client) + { + return -EINVAL; + } + +#ifdef STK_IRS + ret = stk_als_ir_skip_als(obj); + if(ret == 1) + return 0; +#endif + + ret = stk3x1x_master_recv(client, obj->addr.data1_als, buf, 0x02); + if(ret < 0) + { + APS_DBG("error: %d\n", ret); + return -EFAULT; + } + als_data = (buf[0] << 8) | (buf[1]); + + if(obj->p_1x_r_bd_with_co == 0x07 || obj->p_19_r_bc == 0x03) + { + als_data = als_data * 16 / 10; + if(als_data > 65535) + als_data = 65535; + } + + if(obj->p_wv_r_bd_with_co & 0b010) + { + if(als_data < STK_ALS_THRESHOLD && obj->als_code_last > 10000) + { + ir_data = stk3x1x_get_ir_value(obj, STK_ALS_READ_IRS_IT_REDUCE); +#ifdef STK_IRS + if(ir_data > 0) + obj->ir_code = ir_data * ir_enlarge; +#endif + // APS_LOG("%s: als_data=%d, als_code_last=%d,ir_data=%d\n", + // __func__, als_data, obj->als_code_last, ir_data); + if(ir_data > (STK_ALS_THRESHOLD * 3)) + { + als_data = obj->als_code_last; + } + } +#ifdef STK_IRS + else + { + obj->ir_code = 0; + } +#endif + } + obj->als_code_last = als_data; + +#ifdef STK_ALS_FIR + if(obj->fir.num < firlen) + { + obj->fir.raw[obj->fir.num] = als_data; + obj->fir.sum += als_data; + obj->fir.num++; + obj->fir.idx++; + } + else + { + idx = obj->fir.idx % firlen; + obj->fir.sum -= obj->fir.raw[idx]; + obj->fir.raw[idx] = als_data; + obj->fir.sum += als_data; + obj->fir.idx++; + als_data = (obj->fir.sum / firlen); + } +#endif + +#ifdef STK_IRS + stk_als_ir_get_corr(obj, als_data); + als_data = als_data * obj->als_correct_factor / 1000; +#endif + + // [wanglei start] + // Add for als adjust + *data = (u16) (als_data * winfac / 100); + // [wanglei end] + +#ifdef STK_IRS + stk_als_ir_run(obj); +#endif + + if(atomic_read(&obj->trace) & STK_TRC_ALS_DATA) + { + APS_DBG("ALS: 0x%04X\n", (u32)(*data)); + } + + return 0; +} +/*----------------------------------------------------------------------------*/ +int stk3x1x_write_als(struct i2c_client *client, u8 data) +{ + struct stk3x1x_priv *obj = i2c_get_clientdata(client); + int ret = 0; + + ret = stk3x1x_master_send(client, obj->addr.alsctrl, &data, 1); + if(ret < 0) + { + APS_ERR("write als = %d\n", ret); + return -EFAULT; + } + + return 0; +} +/*----------------------------------------------------------------------------*/ +int stk3x1x_read_state(struct i2c_client *client, u8 *data) +{ + //struct stk3x1x_priv *obj = i2c_get_clientdata(client); + int ret = 0; + u8 buf; + + if(NULL == client) + { + return -EINVAL; + } + ret = stk3x1x_master_recv(client, STK_STATE_REG, &buf, 0x01); + if(ret < 0) + { + APS_DBG("error: %d\n", ret); + return -EFAULT; + } + else + { + *data = buf; + } + + return 0; +} +/*----------------------------------------------------------------------------*/ +int stk3x1x_read_flag(struct i2c_client *client, u8 *data) +{ + struct stk3x1x_priv *obj = i2c_get_clientdata(client); + int ret = 0; + u8 buf; + + if(NULL == client) + { + return -EINVAL; + } + ret = stk3x1x_master_recv(client, obj->addr.flag, &buf, 0x01); + if(ret < 0) + { + APS_DBG("error: %d\n", ret); + return -EFAULT; + } + else + { + *data = buf; + } + + if(atomic_read(&obj->trace) & STK_TRC_ALS_DATA) + { + APS_DBG("PS NF flag: 0x%04X\n", (u32)(*data)); + } + + return 0; +} +/*----------------------------------------------------------------------------*/ +int stk3x1x_read_id(struct i2c_client *client) +{ + struct stk3x1x_priv *obj = i2c_get_clientdata(client); + int ret = 0; + u8 buf[2]; + u8 pid_msb; + int otp25 = 0; + + if(NULL == client) + { + return -EINVAL; + } + obj->p_wv_r_bd_with_co = 0; + obj->p_1x_r_bd_with_co = 0; + obj->p_19_r_bc = 0; + + ret = stk3x1x_master_recv(client, STK_PDT_ID_REG, buf, 0x02); + if(ret < 0) + { + APS_DBG("error: %d\n", ret); + return -EFAULT; + } + obj->pid = buf[0]; + APS_LOG("%s: PID=0x%x, VID=0x%x\n", __func__, buf[0], buf[1]); + + if(obj->pid == STK3310SA_PID || obj->pid == STK3311SA_PID) + obj->ledctrl_val &= 0x3F; + + if(buf[0] == STK3311WV_PID) + obj->p_wv_r_bd_with_co |= 0b100; + else if(buf[0] == STK3311X_PID) + obj->p_1x_r_bd_with_co |= 0b100; + else if(buf[0] == STK33119_PID) + obj->p_19_r_bc |= 0b10; + + if(buf[1] == 0xC3) + { + obj->p_wv_r_bd_with_co |= 0b010; + obj->p_1x_r_bd_with_co |= 0b010; + } + else if(buf[1] == 0xC2) + { + obj->p_19_r_bc |= 0b01; + } + + ret = stk3x1x_otp_read_byte_data(client, 0x25); + if(ret < 0) + return ret; + otp25 = ret; + if(otp25 & 0x80) + obj->p_wv_r_bd_with_co |= 0b001; + APS_LOG("%s: p_wv_r_bd_with_co = 0x%x\n", __func__, obj->p_wv_r_bd_with_co); + + if(otp25 & 0x40) + obj->p_1x_r_bd_with_co |= 0b001; + APS_LOG("%s: p_1x_r_bd_with_co = 0x%x\n", __func__, obj->p_1x_r_bd_with_co); + APS_LOG("%s: p_19_r_bc = 0x%x\n", __func__, obj->p_19_r_bc); + + if(buf[0] == 0) + { + APS_ERR( "PID=0x0, please make sure the chip is stk3x1x!\n"); + return -2; + } + + pid_msb = buf[0] & 0xF0; + switch(pid_msb) + { + case 0x10: + case 0x20: + case 0x30: + return 0; + default: + APS_ERR( "invalid PID(%#x)\n", buf[0]); + return -1; + } + return 0; +} +/*----------------------------------------------------------------------------*/ +int stk3x1x_read_ps(struct i2c_client *client, u16 *data) +{ + struct stk3x1x_priv *obj = i2c_get_clientdata(client); + int ret = 0; + u8 buf[2]; + + if(NULL == client) + { + APS_ERR("i2c client is NULL\n"); + return -EINVAL; + } + ret = stk3x1x_master_recv(client, obj->addr.data1_ps, buf, 0x02); + if(ret < 0) + { + APS_DBG("error: %d\n", ret); + return -EFAULT; + } + else + { + *data = (buf[0] << 8) | (buf[1]); + } + + if(atomic_read(&obj->trace) & STK_TRC_ALS_DATA) + { + APS_DBG("PS: 0x%04X\n", (u32)(*data)); + } + + return 0; +} +/*----------------------------------------------------------------------------*/ +int stk3x1x_write_ps(struct i2c_client *client, u8 data) +{ + struct stk3x1x_priv *obj = i2c_get_clientdata(client); + int ret = 0; + + ret = stk3x1x_master_send(client, obj->addr.psctrl, &data, 1); + if (ret < 0) + { + APS_ERR("write ps = %d\n", ret); + return -EFAULT; + } + return 0; +} + +/*----------------------------------------------------------------------------*/ +int stk3x1x_write_wait(struct i2c_client *client, u8 data) +{ + struct stk3x1x_priv *obj = i2c_get_clientdata(client); + int ret = 0; + + ret = stk3x1x_master_send(client, obj->addr.wait, &data, 1); + if (ret < 0) + { + APS_ERR("write wait = %d\n", ret); + return -EFAULT; + } + return 0; +} + +/*----------------------------------------------------------------------------*/ +int stk3x1x_write_int(struct i2c_client *client, u8 data) +{ + struct stk3x1x_priv *obj = i2c_get_clientdata(client); + int ret = 0; + + ret = stk3x1x_master_send(client, obj->addr.intmode, &data, 1); + if (ret < 0) + { + APS_ERR("write intmode = %d\n", ret); + return -EFAULT; + } + return 0; +} +/*----------------------------------------------------------------------------*/ +int stk3x1x_write_state(struct i2c_client *client, u8 data) +{ + struct stk3x1x_priv *obj = i2c_get_clientdata(client); + int ret = 0; + + ret = stk3x1x_master_send(client, obj->addr.state, &data, 1); + if (ret < 0) + { + APS_ERR("write state = %d\n", ret); + return -EFAULT; + } + return 0; +} +/*----------------------------------------------------------------------------*/ +int stk3x1x_write_flag(struct i2c_client *client, u8 data) +{ + struct stk3x1x_priv *obj = i2c_get_clientdata(client); + int ret = 0; + + ret = stk3x1x_master_send(client, obj->addr.flag, &data, 1); + if (ret < 0) + { + APS_ERR("write ps = %d\n", ret); + return -EFAULT; + } + return 0; +} +/*----------------------------------------------------------------------------*/ +int stk3x1x_write_sw_reset(struct i2c_client *client) +{ + struct stk3x1x_priv *obj = i2c_get_clientdata(client); + u8 buf = 0, r_buf = 0; + int ret = 0; + + buf = 0x7F; + ret = stk3x1x_master_send(client, obj->addr.wait, (char*)&buf, sizeof(buf)); + if (ret < 0) + { + APS_ERR("i2c write test error = %d\n", ret); + return -EFAULT; + } + + ret = stk3x1x_master_recv(client, obj->addr.wait, &r_buf, 1); + if (ret < 0) + { + APS_ERR("i2c read test error = %d\n", ret); + return -EFAULT; + } + + if(buf != r_buf) + { + APS_ERR("i2c r/w test error, read-back value is not the same, write=0x%x, read=0x%x\n", buf, r_buf); + return -EIO; + } + + buf = 0; + ret = stk3x1x_master_send(client, obj->addr.soft_reset, (char*)&buf, sizeof(buf)); + if (ret < 0) + { + APS_ERR("write software reset error = %d\n", ret); + return -EFAULT; + } + msleep(13); + return 0; +} + +/*----------------------------------------------------------------------------*/ +int stk3x1x_write_ps_high_thd(struct i2c_client *client, u16 thd) +{ + struct stk3x1x_priv *obj = i2c_get_clientdata(client); + u8 buf[2]; + int ret = 0; + + buf[0] = (u8) ((0xFF00 & thd) >> 8); + buf[1] = (u8) (0x00FF & thd); + + ret = stk3x1x_master_send(client, obj->addr.thdh1_ps, &buf[0], 1); + if (ret < 0) + { + APS_ERR("WARNING: %d\n", ret); + return -EFAULT; + } + + ret = stk3x1x_master_send(client, obj->addr.thdh2_ps, &(buf[1]), 1); + if (ret < 0) + { + APS_ERR("WARNING: %d\n", ret); + return -EFAULT; + } + + return 0; +} +/*----------------------------------------------------------------------------*/ +int stk3x1x_write_ps_low_thd(struct i2c_client *client, u16 thd) +{ + struct stk3x1x_priv *obj = i2c_get_clientdata(client); + u8 buf[2]; + int ret = 0; + + buf[0] = (u8) ((0xFF00 & thd) >> 8); + buf[1] = (u8) (0x00FF & thd); + ret = stk3x1x_master_send(client, obj->addr.thdl1_ps, &buf[0], 1); + if (ret < 0) + { + APS_ERR("WARNING: %s: %d\n", __func__, ret); + return -EFAULT; + } + + ret = stk3x1x_master_send(client, obj->addr.thdl2_ps, &(buf[1]), 1); + if (ret < 0) + { + APS_ERR("WARNING: %s: %d\n", __func__, ret); + return -EFAULT; + } + + return 0; +} +/*----------------------------------------------------------------------------*/ +int stk3x1x_write_als_high_thd(struct i2c_client *client, u16 thd) +{ + struct stk3x1x_priv *obj = i2c_get_clientdata(client); + u8 buf[2]; + int ret = 0; + + buf[0] = (u8) ((0xFF00 & thd) >> 8); + buf[1] = (u8) (0x00FF & thd); + ret = stk3x1x_master_send(client, obj->addr.thdh1_als, &buf[0], 1); + if (ret < 0) + { + APS_ERR("WARNING: %s: %d\n", __func__, ret); + return -EFAULT; + } + + ret = stk3x1x_master_send(client, obj->addr.thdh2_als, &(buf[1]), 1); + if (ret < 0) + { + APS_ERR("WARNING: %s: %d\n", __func__, ret); + return -EFAULT; + } + + return 0; +} +/*----------------------------------------------------------------------------*/ +int stk3x1x_write_als_low_thd(struct i2c_client *client, u16 thd) +{ + struct stk3x1x_priv *obj = i2c_get_clientdata(client); + u8 buf[2]; + int ret = 0; + + buf[0] = (u8) ((0xFF00 & thd) >> 8); + buf[1] = (u8) (0x00FF & thd); + ret = stk3x1x_master_send(client, obj->addr.thdl1_als, &buf[0], 1); + if (ret < 0) + { + APS_ERR("WARNING: %s: %d\n", __func__, ret); + return -EFAULT; + } + + ret = stk3x1x_master_send(client, obj->addr.thdl2_als, &(buf[1]), 1); + if (ret < 0) + { + APS_ERR("WARNING: %s: %d\n", __func__, ret); + return -EFAULT; + } + + return 0; +} +/*----------------------------------------------------------------------------*/ +#ifdef STK_PS_DEBUG +static int show_allreg(void) +{ + int ret = 0; + u8 rbuf[0x22]; + int cnt; + + memset(rbuf, 0, sizeof(rbuf)); + if(!stk3x1x_obj) + { + APS_ERR("stk3x1x_obj is null!!\n"); + return 0; + } + ret = stk3x1x_master_recv(stk3x1x_obj->client, 0, &rbuf[0], 7); + if(ret < 0) + { + APS_DBG("error: %d\n", ret); + return -EFAULT; + } + ret = stk3x1x_master_recv(stk3x1x_obj->client, 7, &rbuf[7], 7); + if(ret < 0) + { + APS_DBG("error: %d\n", ret); + return -EFAULT; + } + ret = stk3x1x_master_recv(stk3x1x_obj->client, 14, &rbuf[14], 7); + if(ret < 0) + { + APS_DBG("error: %d\n", ret); + return -EFAULT; + } + ret = stk3x1x_master_recv(stk3x1x_obj->client, 21, &rbuf[21], 7); + if(ret < 0) + { + APS_DBG("error: %d\n", ret); + return -EFAULT; + } + ret = stk3x1x_master_recv(stk3x1x_obj->client, 28, &rbuf[28], 4); + if(ret < 0) + { + APS_DBG("error: %d\n", ret); + return -EFAULT; + } + + ret = stk3x1x_master_recv(stk3x1x_obj->client, STK_PDT_ID_REG, &rbuf[32], 2); + if(ret < 0) + { + APS_DBG("error: %d\n", ret); + return -EFAULT; + } + + for(cnt=0;cnt<0x20;cnt++) + { + APS_LOG("reg[0x%x]=0x%x\n", cnt, rbuf[cnt]); + } + APS_LOG("reg[0x3E]=0x%x\n", rbuf[cnt]); + APS_LOG("reg[0x3F]=0x%x\n", rbuf[cnt++]); + + return 0; +} +#endif +/*----------------------------------------------------------------------------*/ +#if 0 +int stk3x1x_write_foffset(struct i2c_client *client, u16 ofset) +{ + struct stk3x1x_priv *obj = i2c_get_clientdata(client); + u8 buf[2]; + int ret = 0; + + buf[0] = (u8) ((0xFF00 & ofset) >> 8); + buf[1] = (u8) (0x00FF & ofset); + ret = stk3x1x_master_send(client, obj->addr.data1_offset, &buf[0], 1); + if (ret < 0) + { + APS_ERR("WARNING: %s: %d\n", __func__, ret); + return -EFAULT; + } + + ret = stk3x1x_master_send(client, obj->addr.data2_offset, &(buf[1]), 1); + if (ret < 0) + { + APS_ERR("WARNING: %s: %d\n", __func__, ret); + return -EFAULT; + } + + return 0; +} + +/*----------------------------------------------------------------------------*/ + +int stk3x1x_write_aoffset(struct i2c_client *client, u16 ofset) +{ + struct stk3x1x_priv *obj = i2c_get_clientdata(client); + u8 buf[2]; + int ret = 0; + u8 s_buf = 0, re_en; + ret = stk3x1x_master_recv(client, obj->addr.state, &s_buf, 1); + if (ret < 0) + { + APS_ERR("i2c read state error = %d\n", ret); + return -EFAULT; + } + re_en = (s_buf & STK_STATE_EN_AK_MASK) ? 1: 0; + if(re_en) + { + s_buf &= (~STK_STATE_EN_AK_MASK); + ret = stk3x1x_master_send(client, obj->addr.state, &s_buf, 1); + if (ret < 0) + { + APS_ERR("write state = %d\n", ret); + return -EFAULT; + } + msleep(3); + } + + buf[0] = (u8) ((0xFF00 & ofset) >> 8); + buf[1] = (u8) (0x00FF & ofset); + ret = stk3x1x_master_send(client, 0x0E, &buf[0], 1); + if (ret < 0) + { + APS_ERR("WARNING: %s: %d\n", __func__, ret); + return -EFAULT; + } + + ret = stk3x1x_master_send(client, 0x0F, &(buf[1]), 1); + if (ret < 0) + { + APS_ERR("WARNING: %s: %d\n", __func__, ret); + return -EFAULT; + } + if(!re_en) + return 0; + s_buf |= STK_STATE_EN_AK_MASK; + ret = stk3x1x_master_send(client, obj->addr.state, &s_buf, 1); + if (ret < 0) + { + APS_ERR("write state = %d\n", ret); + return -EFAULT; + } + return 0; +} +#endif +/*----------------------------------------------------------------------------*/ +static void stk3x1x_power(struct alsps_hw *hw, unsigned int on) +{ +#if 0 + int ret; + if(on == 1){ + ret = regulator_enable(stk3x1x_regulator); /*enable regulator*/ + if (ret) + printk("regulator_enable() failed!\n"); + }else{ + ret = regulator_disable(stk3x1x_regulator); /*enable regulator*/ + if (ret) + printk("regulator_disable() failed!\n"); + } +#endif +} +/*----------------------------------------------------------------------------*/ +static void stk_ps_report(struct stk3x1x_priv *obj, int nf) +{ +/* + hwm_sensor_data sensor_data; + + sensor_data.values[0] = nf; + sensor_data.value_divide = 1; + sensor_data.status = SENSOR_STATUS_ACCURACY_MEDIUM; + if((nf = hwmsen_get_interrupt_data(ID_PROXIMITY, &sensor_data))) + APS_ERR("call hwmsen_get_interrupt_data fail = %d\n", nf); +*/ + if(ps_report_interrupt_data(nf)) + APS_ERR("call ps_report_interrupt_data fail\n"); + + APS_LOG("%s:ps raw 0x%x -> value 0x%x \n",__FUNCTION__, obj->ps, nf); + obj->ps_distance_last = nf; +} +/*----------------------------------------------------------------------------*/ +static int stk3x1x_enable_als(struct i2c_client *client, int enable) +{ + struct stk3x1x_priv *obj = i2c_get_clientdata(client); + int err, cur = 0, old = atomic_read(&obj->state_val); + int trc = atomic_read(&obj->trace); + + APS_LOG("%s: enable=%d\n", __func__, enable); + +#ifdef STK_GES + if(obj->ges_enabled) + { + APS_LOG( "%s: since ges is enabled, ALS is disabled\n", __func__); + obj->re_enable_als = enable ? true : false; + return 0; + } +#endif + cur = old & (~(STK_STATE_EN_ALS_MASK | STK_STATE_EN_WAIT_MASK)); + if(enable) + { + cur |= STK_STATE_EN_ALS_MASK; + } + else if (old & STK_STATE_EN_PS_MASK) + { + cur |= STK_STATE_EN_WAIT_MASK; + } + if(trc & STK_TRC_DEBUG) + { + APS_LOG("%s: %08X, %08X, %d\n", __func__, cur, old, enable); + } + + if(0 == (cur ^ old)) + { + return 0; + } + +#ifdef STK_IRS + if(enable && !(old & STK_STATE_EN_PS_MASK)) + { + err = stk3x1x_get_ir_value(obj, STK_IRS_IT_REDUCE); + if(err > 0) + obj->ir_code = err; + } +#endif + + if(enable && obj->hw->polling_mode_als == 0) + { + stk3x1x_write_als_high_thd(client, 0x0); + stk3x1x_write_als_low_thd(client, 0xFFFF); + } + err = stk3x1x_write_state(client, cur); + if(err < 0) + return err; + else + atomic_set(&obj->state_val, cur); + + if(enable) + { + + obj->als_last = 0; + if(obj->hw->polling_mode_als) + { + atomic_set(&obj->als_deb_on, 1); + atomic_set(&obj->als_deb_end, jiffies+atomic_read(&obj->als_debounce)*HZ/1000); + } + else + { + //set_bit(STK_BIT_ALS, &obj->pending_intr); + schedule_delayed_work(&obj->eint_work,220*HZ/1000); + } + } +#ifdef STK_IRS + obj->als_data_index = 0; +#endif + if(trc & STK_TRC_DEBUG) + { + APS_LOG("enable als (%d)\n", enable); + } + + return err; +} +/*----------------------------------------------------------------------------*/ +static int stk3x1x_enable_ps_set_thd(struct stk3x1x_priv *obj) +{ + int err; + +#ifdef STK_TUNE1 + #ifdef CALI_EVERY_TIME + if( obj->crosstalk > 0 ) + { + atomic_set(&obj->ps_high_thd_val, obj->ps_high_thd_boot); + atomic_set(&obj->ps_low_thd_val, obj->ps_low_thd_boot); + } + else if( obj->ps <= STK_FIN_THD ) + { + atomic_set(&obj->ps_high_thd_val, obj->ps + obj->stk_ht_n_ct); + atomic_set(&obj->ps_low_thd_val, obj->ps + obj->stk_lt_n_ct); + } + else if(obj->boot_cali == 1 && obj->ps_low_thd_boot < STK_PS_HIGHER_THDH ) + { + atomic_set(&obj->ps_high_thd_val, obj->ps_high_thd_boot); + atomic_set(&obj->ps_low_thd_val, obj->ps_low_thd_boot); + } + else + #endif + { + atomic_set(&obj->ps_high_thd_val, STK_PS_HIGHER_THDH); + atomic_set(&obj->ps_low_thd_val, STK_PS_HIGHER_THDL); + } + + if( atomic_read(&obj->ps_high_thd_val) > STK_PS_HIGHER_THDH) + { + atomic_set(&obj->ps_high_thd_val, STK_PS_HIGHER_THDH); + atomic_set(&obj->ps_low_thd_val, STK_PS_HIGHER_THDL); + } + + if ((err = stk3x1x_write_ps_high_thd(obj->client, atomic_read(&obj->ps_high_thd_val)))) + { + APS_ERR("write high thd error: %d\n", err); + return err; + } + if ((err = stk3x1x_write_ps_low_thd(obj->client, 0))) + { + APS_ERR("write low thd error: %d\n", err); + return err; + } + APS_LOG("%s:in reg, HT=%d, LT=0\n", __func__, atomic_read(&obj->ps_high_thd_val)); +#elif defined(STK_TUNE0) + + #ifdef CALI_EVERY_TIME + if( obj->crosstalk > 0) + { + atomic_set(&obj->ps_high_thd_val, obj->ps_high_thd_boot); + atomic_set(&obj->ps_low_thd_val, obj->ps_low_thd_boot); + } + /* [yanlin start] modified calibration */ + else if (obj->ps <= PS_MAX_THD_RANGE) + { + atomic_set(&obj->ps_high_thd_val, obj->ps + obj->stk_ht_n_ct); + atomic_set(&obj->ps_low_thd_val, obj->ps + obj->stk_lt_n_ct); + } + else if(obj->boot_cali == 1 && obj->ps_low_thd_boot < 1000 ) + { + atomic_set(&obj->ps_high_thd_val, obj->ps_high_thd_boot); + atomic_set(&obj->ps_low_thd_val, obj->ps_low_thd_boot); + } + else + { + atomic_set(&obj->ps_high_thd_val, PS_THDH_LIMITED); + atomic_set(&obj->ps_low_thd_val, PS_THDL_LIMITED); + } + /* [yanlin end] */ + /* + else + { + atomic_set(&obj->ps_high_thd_val, 0xFFFF); + atomic_set(&obj->ps_low_thd_val, 0); + } + */ + #endif + if ((err = stk3x1x_write_ps_high_thd(obj->client, atomic_read(&obj->ps_high_thd_val)))) + { + APS_ERR("write high thd error: %d\n", err); + return err; + } + if ((err = stk3x1x_write_ps_low_thd(obj->client, atomic_read(&obj->ps_low_thd_val)))) + { + APS_ERR("write low thd error: %d\n", err); + return err; + } + APS_LOG("%s:in reg, HT=%d, LT=%d\n", __func__, atomic_read(&obj->ps_high_thd_val), atomic_read(&obj->ps_low_thd_val)); +#else + if ((err = stk3x1x_write_ps_high_thd(obj->client, atomic_read(&obj->ps_high_thd_val)))) + { + APS_ERR("write high thd error: %d\n", err); + return err; + } + if ((err = stk3x1x_write_ps_low_thd(obj->client, atomic_read(&obj->ps_low_thd_val)))) + { + APS_ERR("write low thd error: %d\n", err); + return err; + } + APS_LOG("%s:in reg, HT=%d, LT=%d\n", __func__, atomic_read(&obj->ps_high_thd_val), atomic_read(&obj->ps_low_thd_val)); +#endif + + APS_LOG("%s:in driver, HT=%d, LT=%d\n", __func__, atomic_read(&obj->ps_high_thd_val), atomic_read(&obj->ps_low_thd_val)); + return 0; +} +/*----------------------------------------------------------------------------*/ +static int stk3x1x_enable_ps(struct i2c_client *client, int enable, int validate_reg) +{ + struct stk3x1x_priv *obj = i2c_get_clientdata(client); + int err, cur = 0, old = atomic_read(&obj->state_val); + int trc = atomic_read(&obj->trace); + +#ifdef STK_PS_DEBUG + if(enable) + { + APS_LOG("%s: before enable ps, HT=%d, LT=%d\n", __func__, atomic_read(&obj->ps_high_thd_val), atomic_read(&obj->ps_low_thd_val)); + APS_LOG("%s: before enable ps, show all reg\n", __FUNCTION__); + show_allreg(); //for debug + } +#endif + + printk("%s: enable=%d\n", __FUNCTION__, enable); + + cur = old; + cur &= (~(0x45)); + if(enable) + { + cur |= (STK_STATE_EN_PS_MASK); + if(!(old & STK_STATE_EN_ALS_MASK)) + cur |= STK_STATE_EN_WAIT_MASK; + } + + if(0 == (cur ^ old)) + { + printk("%s: repeat enable=%d, reg=0x%x\n", __FUNCTION__, enable, cur); + return 0; + } + +#ifdef STK_GES + + if(obj->ges_enabled) + { + if(enable) + { + APS_LOG( "%s: force disable Ges, mode = %d\n", __func__, obj->ges_enabled); + obj->re_enable_ges2 = obj->ges_enabled; + stk3x1x_enable_ges(obj->client, 0, obj->ges_enabled); + old = atomic_read(&obj->state_val); + } + else + { + APS_LOG( "%s: ps is disabled\n", __func__); + return 0; + } + // APS_LOG( "%s: since ges is enabled, PS is disabled\n", __func__); + // obj->re_enable_ps = enable ? true : false; + // return 0; + + } +#endif + +#ifdef STK_CHK_REG + if(validate_reg) + { + err = stk3x1x_validate_n_handle(obj->client); + if(err < 0) + { + APS_ERR("stk3x1x_validate_n_handle fail: %d\n", err); + } + } +#endif +#ifdef STK_TUNE0 + if (!(obj->psi_set) && !enable) + { + hrtimer_cancel(&obj->ps_tune0_timer); + cancel_work_sync(&obj->stk_ps_tune0_work); + } +#endif + + if(obj->first_boot == true) + { + obj->first_boot = false; + } + + if(enable) + { + /* [yanlin start] modified calibration */ + //stk3x1x_enable_ps_set_thd(obj); + /* [yanlin end] */ + if(1 == obj->hw->polling_mode_ps) + wake_lock(&ps_lock); + } + else + { + if(1 == obj->hw->polling_mode_ps) + wake_unlock(&ps_lock); + } + + /* [yanlin start] modified calibration */ + if(enable) + { + if ((err = stk3x1x_write_ps_high_thd(obj->client, 0xffff))) + { + APS_ERR("write high thd error: %d\n", err); + return err; + } + if ((err = stk3x1x_write_ps_low_thd(obj->client, 0x0))) + { + APS_ERR("write low thd error: %d\n", err); + return err; + } + } + /* [yanlin end] */ + + if(trc & STK_TRC_DEBUG) + { + APS_LOG("%s: %08X, %08X, %d\n", __func__, cur, old, enable); + } + + err = stk3x1x_write_state(client, cur); + if(err < 0) + return err; + atomic_set(&obj->state_val, cur); + + if(enable) + { +#ifdef STK_TUNE0 + #ifndef CALI_EVERY_TIME + if (!(obj->psi_set)) + hrtimer_start(&obj->ps_tune0_timer, obj->ps_tune0_delay, HRTIMER_MODE_REL); + #else + obj->psi_set = 0; + obj->psa = 0; + obj->psi = 0xFFFF; + hrtimer_start(&obj->ps_tune0_timer, obj->ps_tune0_delay, HRTIMER_MODE_REL); + #endif +#endif +#ifdef STK_TUNE1 + obj->stk_int_handle_state = STK_PSINT_NORM; +#endif + if(obj->hw->polling_mode_ps) + { + atomic_set(&obj->ps_deb_on, 1); + atomic_set(&obj->ps_deb_end, jiffies+atomic_read(&obj->ps_debounce)*HZ/1000); + } + else + { +#ifdef STK_CHK_REG + if(!validate_reg) + { + stk_ps_report(obj, 1); + } + else +#endif + { + msleep(5); + if((err = stk3x1x_read_ps(obj->client, &obj->ps))) + { + APS_ERR("stk3x1x read ps data: %d\n", err); + return err; + } + /* [yanlin start] modified calibration */ + stk3x1x_enable_ps_set_thd(obj); + /* [yanlin end] */ +#ifdef STK_TUNE1 + // err = (obj->ps >= atomic_read(&obj->ps_high_thd_val))?0:1; + err = 1; +#else + err = stk3x1x_get_ps_value_only(obj, obj->ps); +#endif + if(err < 0) + { + APS_ERR("stk3x1x get ps value: %d\n", err); + return err; + } + else if(stk3x1x_obj->hw->polling_mode_ps == 0) + { + distance_flag = err; + APS_LOG("%s:ps raw 0x%x -> value 0x%x \n",__FUNCTION__, obj->ps, + distance_flag); + /* [yanlin start] modified calibration */ + if (obj->ps < atomic_read(&obj->ps_low_thd_val)) + stk_ps_report(obj, 1); + else if (obj->ps > atomic_read(&obj->ps_high_thd_val)) + stk_ps_report(obj, 0); + /* [yanlin end] */ + } + + #ifdef STK_PS_DEBUG + APS_LOG("%s: after stk_ps_report, show all reg\n", __FUNCTION__); + show_allreg(); //for debug + #endif + } + } + } +#ifdef STK_GES + else + { + if(obj->re_enable_ges2) + { + cur = obj->re_enable_ges2; + obj->re_enable_ges2 = 0; + APS_LOG( "%s: re-enable Ges, mode = %d\n", __func__, cur); + stk3x1x_enable_ges(obj->client, 1, cur); + } + } +#endif + + if(trc & STK_TRC_DEBUG) + { + APS_LOG("enable ps (%d)\n", enable); + } + + return 0; +} +/*----------------------------------------------------------------------------*/ +#ifdef STK_GES +static int32_t stk3x1x_enable_ges(struct i2c_client *client, int enable, int mode) +{ + struct stk3x1x_priv *obj = i2c_get_clientdata(client); + u8 reg; + int err; + int org_state_reg = atomic_read(&obj->state_val); + int org_mode = 0; + + APS_LOG("%s: enable=%d\n", __FUNCTION__, enable); + + + if(enable == obj->ges_enabled) + return 0; + + if(enable) + { + if(org_state_reg & STK_STATE_EN_PS_MASK) + { + // APS_LOG( "%s: force disable PS\n", __func__); + // stk3x1x_enable_ps(obj->client, 0, 1); + // obj->re_enable_ps = true; + APS_LOG( "%s: since PS is enabled, Ges is disabled, mode = %d\n", __func__, mode); + obj->re_enable_ges2 = mode; + return 0; + } + if(org_state_reg & STK_STATE_EN_ALS_MASK) + { + APS_LOG( "%s: force disable ALS\n", __func__); + stk3x1x_enable_als(obj->client, 0); + obj->re_enable_als = true; + } + + if((err = stk3x1x_write_wait(client, 0))) + { + APS_ERR("write wait error: %d\n", err); + return err; + } + + if((err = stk3x1x_write_int(obj->client, 0))) + { + APS_ERR("write int mode error: %d\n", err); + return err; + } + + reg = STK_STATE_EN_WAIT_MASK | STK_STATE_EN_PS_MASK; + err = stk3x1x_write_state(client, reg); + if(err < 0) + return err; + atomic_set(&obj->state_val, reg); + obj->ges_enabled = mode; + } + else + { + org_mode = obj->ges_enabled; + if(org_mode == 2) + { + if(obj->hw->polling_mode_ps == 1) + { + hrtimer_cancel(&obj->ps_tune0_timer); + cancel_work_sync(&obj->stk_ps_tune0_work); + } + else + { +#if defined(CONFIG_OF) + disable_irq(obj->irq); +#elif ((defined MT6573) || (defined MT6575) || (defined MT6577) || (defined MT6589) || (defined MT6572)) + mt65xx_eint_mask(CUST_EINT_ALS_NUM); +#else + mt_eint_mask(CUST_EINT_ALS_NUM); +#endif + } + } + + err = stk3x1x_write_state(client, 0); + if(err < 0) + return err; + atomic_set(&obj->state_val, 0); + if((err = stk3x1x_write_wait(client, obj->wait_val))) + { + APS_ERR("write wait error: %d\n", err); + return err; + } + + if((err = stk3x1x_write_int(obj->client, obj->int_val))) + { + APS_ERR("write int mode error: %d\n", err); + return err; + } + obj->ges_enabled = 0; + /* + if(obj->re_enable_ps) + { + APS_LOG( "%s: re-enable PS\n", __func__); + stk3x1x_enable_ps(obj->client, 1, 1); + obj->re_enable_ps = false; + } + */ + if(obj->re_enable_als) + { + APS_LOG( "%s: re-enable ALS\n", __func__); + stk3x1x_enable_als(obj->client, 1); + obj->re_enable_als = false; + } + } + + return 0; +} +#endif /* #ifdef STK_GES */ + +/*----------------------------------------------------------------------------*/ +static int stk3x1x_ps_calibration(struct i2c_client *client) +{ + struct stk3x1x_priv *obj = i2c_get_clientdata(client); + u8 reg; + int err = 0; + int org_state_reg = atomic_read(&obj->state_val); + bool re_en_ps = false, re_en_als = false; + int counter = 0; + uint32_t ps_sum = 0; + const int PS_CALI_COUNT = 5; + +#ifdef STK_GES + bool re_en_ges = false; + + if(obj->ges_enabled) + { + stk3x1x_enable_ges(obj->client, 0, 1); + re_en_ges = true; + } +#endif + if(org_state_reg & STK_STATE_EN_PS_MASK) + { + APS_LOG( "%s: force disable PS\n", __func__); + stk3x1x_enable_ps(obj->client, 0, 1); + re_en_ps = true; + } + + if(org_state_reg & STK_STATE_EN_ALS_MASK) + { + APS_LOG( "%s: force disable ALS\n", __func__); + stk3x1x_enable_als(obj->client, 0); + re_en_als = true; + } +#if defined(CONFIG_OF) + disable_irq(obj->irq); +#elif ((defined MT6573) || (defined MT6575) || (defined MT6577) || (defined MT6589) || (defined MT6572)) + mt65xx_eint_mask(CUST_EINT_ALS_NUM); +#else + mt_eint_mask(CUST_EINT_ALS_NUM); +#endif + + reg = STK_STATE_EN_WAIT_MASK | STK_STATE_EN_PS_MASK; + err = stk3x1x_write_state(client, reg); + if(err < 0) + goto err_out; + + while(counter < PS_CALI_COUNT) + { + msleep(60); + if((err = stk3x1x_read_ps(obj->client, &obj->ps))) + goto err_out; +//#ifdef STK_PS_POLLING_LOG + APS_LOG( "%s: ps=%d\n", __func__, obj->ps); +//#endif + ps_sum += obj->ps; + counter++; + } + + obj->ps_cali = (ps_sum / PS_CALI_COUNT); +#ifdef STK_TUNE0 + obj->crosstalk = (ps_sum / PS_CALI_COUNT); + + #ifdef STK_TUNE1 + if (obj->crosstalk == 0 || obj->crosstalk > STK_PS_HIGHER_THDL) + #else + if (obj->crosstalk == 0) + #endif + { + #ifdef STK_TUNE1 + obj->crosstalk = STK_PS_HIGHER_THDL; + #else + obj->crosstalk = obj->hw->ps_threshold_low; + #endif + stk3x1x_calibration_flag = 0; + }else { + stk3x1x_calibration_flag = 1; + } +#endif +#ifdef CALI_EVERY_TIME + obj->ps_high_thd_boot = obj->crosstalk + obj->stk_ht_n_ct; + obj->ps_low_thd_boot = obj->crosstalk + obj->stk_lt_n_ct; + printk( "%s: crosstalk=%d, high thd=%d, low thd=%d\n", __func__, obj->crosstalk, obj->ps_high_thd_boot, obj->ps_low_thd_boot); +#endif + +err_out: + /* [yanlin start] fix irq invalid when use calibration in Engineer mode */ +/* +#if defined(CONFIG_OF) + disable_irq(obj->irq); +#elif ((defined MT6573) || (defined MT6575) || (defined MT6577) || (defined MT6589) || (defined MT6572)) + mt65xx_eint_mask(CUST_EINT_ALS_NUM); +#else + mt_eint_mask(CUST_EINT_ALS_NUM); +#endif +*/ +#if defined(CONFIG_OF) + enable_irq(obj->irq); +#endif + /* [yanlin end] fix irq invalid when use calibration in Engineer mode */ + + if(re_en_als) + { + APS_LOG( "%s: re-enable ALS\n", __func__); + stk3x1x_enable_als(obj->client, 1); + } + + if(re_en_ps) + { + APS_LOG( "%s: re-enable PS\n", __func__); + stk3x1x_enable_ps(obj->client, 1, 1); + } +#ifdef STK_GES + if(re_en_ges) + stk3x1x_enable_ges(obj->client, 1, 1); +#endif + return err; +} + +/*----------------------------------------------------------------------------*/ +static int stk3x1x_check_intr(struct i2c_client *client, u8 *status) +{ + struct stk3x1x_priv *obj = i2c_get_clientdata(client); + int err; + + //if (mt_get_gpio_in(GPIO_ALS_EINT_PIN) == 1) /*skip if no interrupt*/ + // return 0; + + err = stk3x1x_read_flag(client, status); + if (err < 0) + { + APS_ERR("WARNING: read flag reg error: %d\n", err); + return -EFAULT; + } + APS_LOG("%s: read status reg: 0x%x\n", __func__, *status); + + if(*status & STK_FLG_ALSINT_MASK) + { + set_bit(STK_BIT_ALS, &obj->pending_intr); + } + else + { + clear_bit(STK_BIT_ALS, &obj->pending_intr); + } + + if(*status & STK_FLG_PSINT_MASK) + { + set_bit(STK_BIT_PS, &obj->pending_intr); + } + else + { + clear_bit(STK_BIT_PS, &obj->pending_intr); + } + + if(atomic_read(&obj->trace) & STK_TRC_DEBUG) + { + APS_LOG("check intr: 0x%02X => 0x%08lX\n", *status, obj->pending_intr); + } + + return 0; +} + + +static int stk3x1x_clear_intr(struct i2c_client *client, u8 status, u8 disable_flag) +{ + int err = 0; + + status = status | (STK_FLG_ALSINT_MASK | STK_FLG_PSINT_MASK | STK_FLG_OUI_MASK | STK_FLG_IR_RDY_MASK); + status &= (~disable_flag); + //APS_LOG(" set flag reg: 0x%x\n", status); + if((err = stk3x1x_write_flag(client, status))) + APS_ERR("stk3x1x_write_flag failed, err=%d\n", err); + return err; +} + +/*----------------------------------------------------------------------------*/ +#ifdef STK_CHK_REG +static int stk3x1x_chk_reg_valid(struct stk3x1x_priv *obj) +{ + int ret = 0; + u8 buf[9]; + + if(NULL == obj) + { + return -EINVAL; + } + memset(buf, 0, sizeof(buf)); + + ret = stk3x1x_master_recv(stk3x1x_obj->client, 1, &buf[0], 7); + if(ret < 0) + { + APS_DBG("error: %d\n", ret); + return -EFAULT; + } + ret = stk3x1x_master_recv(stk3x1x_obj->client, 8, &buf[7], 2); + if(ret < 0) + { + APS_DBG("error: %d\n", ret); + return -EFAULT; + } + + if(buf[0] != atomic_read(&obj->psctrl_val)) + { + APS_ERR("%s: invalid reg 0x01=0x%2x\n", __func__, buf[0]); + return 0xFF; + } +#ifdef STK_IRS + if(buf[1] != atomic_read(&obj->alsctrl_val) && buf[1] != (atomic_read(&obj->alsctrl_val) - STK_IRS_IT_REDUCE) + && buf[1] != (atomic_read(&obj->alsctrl_val) - STK_ALS_READ_IRS_IT_REDUCE)) +#else + if(buf[1] != atomic_read(&obj->alsctrl_val) && buf[1] != (atomic_read(&obj->alsctrl_val) - STK_ALS_READ_IRS_IT_REDUCE)) +#endif + { + APS_ERR("%s: invalid reg 0x02=0x%2x\n", __func__, buf[1]); + return 0xFF; + } + if(buf[2] != obj->ledctrl_val) + { + APS_ERR("%s: invalid reg 0x03=0x%2x\n", __func__, buf[2]); + return 0xFF; + } + if(buf[3] != obj->int_val) + { + APS_ERR("%s: invalid reg 0x04=0x%2x\n", __func__, buf[3]); + return 0xFF; + } + if(buf[4] != obj->wait_val) + { + APS_ERR("%s: invalid reg 0x05=0x%2x\n", __func__, buf[4]); + return 0xFF; + } + if(buf[5] != (atomic_read(&obj->ps_high_thd_val) & 0xFF00) >> 8) + { + APS_ERR("%s: invalid reg 0x06=0x%2x\n", __func__, buf[5]); + return 0xFF; + } + if(buf[6] != (atomic_read(&obj->ps_high_thd_val) & 0x00FF)) + { + APS_ERR("%s: invalid reg 0x07=0x%2x\n", __func__, buf[6]); + return 0xFF; + } + if(buf[7] != (atomic_read(&obj->ps_low_thd_val) & 0xFF00) >> 8) + { + APS_ERR("%s: invalid reg 0x08=0x%2x\n", __func__, buf[7]); + return 0xFF; + } + if(buf[8] != (atomic_read(&obj->ps_low_thd_val) & 0x00FF)) + { + APS_ERR("%s: invalid reg 0x09=0x%2x\n", __func__, buf[8]); + return 0xFF; + } + + return 0; +} + +static int stk3x1x_validate_n_handle(struct i2c_client *client) +{ + struct stk3x1x_priv *obj = i2c_get_clientdata(client); + int err; + + err = stk3x1x_chk_reg_valid(obj); + if(err < 0) + { + APS_ERR("stk3x1x_chk_reg_valid fail: %d\n", err); + return err; + } + + if(err == 0xFF) + { + APS_ERR("%s: Re-init chip\n", __func__); + stk3x1x_init_client(obj->client); + //obj->psa = 0; + //obj->psi = 0xFFFF; + if((err = stk3x1x_write_ps_high_thd(client, atomic_read(&obj->ps_high_thd_val)))) + { + APS_ERR("write high thd error: %d\n", err); + return err; + } + + if((err = stk3x1x_write_ps_low_thd(client, atomic_read(&obj->ps_low_thd_val)))) + { + APS_ERR("write low thd error: %d\n", err); + return err; + } + + return 0xFF; + } + return 0; +} +#endif /* #ifdef STK_CHK_REG */ +/*----------------------------------------------------------------------------*/ +#if 0 +static int stk3x1x_set_als_int_thd(struct i2c_client *client, u16 als_data_reg) +{ + s32 als_thd_h, als_thd_l; + + als_thd_h = als_data_reg + STK_ALS_CODE_CHANGE_THD; + als_thd_l = als_data_reg - STK_ALS_CODE_CHANGE_THD; + if (als_thd_h >= (1<<16)) + als_thd_h = (1<<16) -1; + if (als_thd_l <0) + als_thd_l = 0; + APS_LOG("stk3x1x_set_als_int_thd:als_thd_h:%d,als_thd_l:%d\n", als_thd_h, als_thd_l); + + stk3x1x_write_als_high_thd(client, als_thd_h); + stk3x1x_write_als_low_thd(client, als_thd_l); + + return 0; +} +#endif +/*----------------------------------------------------------------------------*/ +static int stk3x1x_ps_val(void) +{ + int mode; + int32_t word_data, lii; + u8 buf[4]; + int ret; + + ret = stk3x1x_master_recv(stk3x1x_obj->client, 0x20, buf, 4); + if(ret < 0) + { + APS_ERR("%s fail, err=0x%x", __FUNCTION__, ret); + return ret; + } + word_data = (buf[0] << 8) | buf[1]; + word_data += (buf[2] << 8) | buf[3]; + + mode = atomic_read(&stk3x1x_obj->psctrl_val) & 0x3F; + if(mode == 0x30) + { + lii = 100; + } + else if (mode == 0x31) + { + lii = 200; + } + else if (mode == 0x32) + { + lii = 400; + } + else if (mode == 0x33) + { + lii = 800; + } + else + { + APS_ERR("%s: unsupported PS_IT(0x%x)\n", __FUNCTION__, mode); + return -1; + } + + if(word_data > lii) + { + APS_LOG( "%s: word_data=%d, lii=%d\n", __FUNCTION__, word_data, lii); + return 0xFFFF; + } + return 0; +} +/*----------------------------------------------------------------------------*/ +#ifdef STK_GES +static int32_t stk_ges_poll_func(struct stk3x1x_priv *obj) +{ + return 0; +} +#endif +/*----------------------------------------------------------------------------*/ +#ifdef STK_TUNE0 +static int stk_ps_tune_zero_final(struct stk3x1x_priv *obj) +{ + int err; + + obj->tune_zero_init_proc = false; + if((err = stk3x1x_write_int(obj->client, obj->int_val))) + { + APS_ERR("write int mode error: %d\n", err); + return err; + } + + if((err = stk3x1x_write_state(obj->client, atomic_read(&obj->state_val)))) + { + APS_ERR("write stete error: %d\n", err); + return err; + } + + if(obj->data_count == -1) + { + APS_LOG("%s: exceed limit\n", __func__); + hrtimer_cancel(&obj->ps_tune0_timer); + return 0; + } + + obj->psa = obj->ps_stat_data[0]; + obj->psi = obj->ps_stat_data[2]; + +#ifndef CALI_EVERY_TIME + atomic_set(&obj->ps_high_thd_val, obj->ps_stat_data[1] + obj->stk_ht_n_ct); + atomic_set(&obj->ps_low_thd_val, obj->ps_stat_data[1] + obj->stk_lt_n_ct); +#else + obj->ps_high_thd_boot = obj->ps_stat_data[1] + obj->stk_ht_n_ct; + obj->ps_low_thd_boot = obj->ps_stat_data[1] + obj->stk_lt_n_ct; + atomic_set(&obj->ps_high_thd_val, obj->ps_high_thd_boot); + atomic_set(&obj->ps_low_thd_val, obj->ps_low_thd_boot); +#endif +// obj->crosstalk = obj->ps_stat_data[1]; + + if((err = stk3x1x_write_ps_high_thd(obj->client, atomic_read(&obj->ps_high_thd_val)))) + { + APS_ERR("write high thd error: %d\n", err); + return err; + } + if((err = stk3x1x_write_ps_low_thd(obj->client, atomic_read(&obj->ps_low_thd_val)))) + { + APS_ERR("write low thd error: %d\n", err); + return err; + } + obj->boot_cali = 1; + + APS_LOG("%s: set HT=%d,LT=%d\n", __func__, atomic_read(&obj->ps_high_thd_val), atomic_read(&obj->ps_low_thd_val)); + hrtimer_cancel(&obj->ps_tune0_timer); + return 0; +} +/*----------------------------------------------------------------------------*/ +static int32_t stk_tune_zero_get_ps_data(struct stk3x1x_priv *obj) +{ + int err; + + err = stk3x1x_ps_val(); + if(err == 0xFFFF) + { + obj->data_count = -1; + stk_ps_tune_zero_final(obj); + return 0; + } + + if((err = stk3x1x_read_ps(obj->client, &obj->ps))) + { + APS_ERR("stk3x1x read ps data: %d\n", err); + return err; + } + APS_LOG("%s: ps #%d=%d\n", __func__, obj->data_count, obj->ps); + + obj->ps_stat_data[1] += obj->ps; + if(obj->ps > obj->ps_stat_data[0]) + obj->ps_stat_data[0] = obj->ps; + if(obj->ps < obj->ps_stat_data[2]) + obj->ps_stat_data[2] = obj->ps; + obj->data_count++; + + if(obj->data_count == 5) + { + obj->ps_stat_data[1] /= obj->data_count; + stk_ps_tune_zero_final(obj); + } + + return 0; +} +/*----------------------------------------------------------------------------*/ +static int stk_ps_tune_zero_init(struct stk3x1x_priv *obj) +{ + u8 w_state_reg; + int err; + + obj->psa = 0; + obj->psi = 0xFFFF; + obj->psi_set = 0; + obj->tune_zero_init_proc = true; + obj->ps_stat_data[0] = 0; + obj->ps_stat_data[2] = 9999; + obj->ps_stat_data[1] = 0; + obj->data_count = 0; + obj->boot_cali = 0; + +#ifdef CALI_EVERY_TIME + obj->ps_high_thd_boot = obj->hw->ps_threshold_high; + obj->ps_low_thd_boot = obj->hw->ps_threshold_low; + if(obj->ps_high_thd_boot <= 0) + { + obj->ps_high_thd_boot = obj->stk_ht_n_ct*3; + obj->ps_low_thd_boot = obj->stk_lt_n_ct*3; + } +#endif + + if((err = stk3x1x_write_int(obj->client, 0))) + { + APS_ERR("write int mode error: %d\n", err); + return err; + } + + w_state_reg = (STK_STATE_EN_PS_MASK | STK_STATE_EN_WAIT_MASK); + if((err = stk3x1x_write_state(obj->client, w_state_reg))) + { + APS_ERR("write stete error: %d\n", err); + return err; + } + hrtimer_start(&obj->ps_tune0_timer, obj->ps_tune0_delay, HRTIMER_MODE_REL); + return 0; +} +/*----------------------------------------------------------------------------*/ +static int stk_ps_tune_zero_func_fae(struct stk3x1x_priv *obj) +{ + int32_t word_data; + u8 flag; + bool ps_enabled = false; + u8 buf[2]; + int ret, diff; + + ps_enabled = (atomic_read(&obj->state_val) & STK_STATE_EN_PS_MASK) ? true : false; + +#ifndef CALI_EVERY_TIME + if(obj->psi_set || !(ps_enabled)) +#else + if(!(ps_enabled)) +#endif + { + return 0; + } + + ret = stk3x1x_read_flag(obj->client, &flag); + if(ret < 0) + { + APS_ERR( "%s: get flag failed, err=0x%x\n", __func__, ret); + return ret; + } + if(!(flag&STK_FLG_PSDR_MASK)) + { + return 0; + } + + ret = stk3x1x_ps_val(); + if(ret == 0) + { + ret = stk3x1x_master_recv(obj->client, 0x11, buf, 2); + if(ret < 0) + { + APS_ERR( "%s fail, err=0x%x", __func__, ret); + return ret; + } + word_data = (buf[0] << 8) | buf[1]; + //APS_LOG("%s: word_data=%d\n", __func__, word_data); + + if(word_data == 0) + { + //APS_ERR( "%s: incorrect word data (0)\n", __func__); + return 0xFFFF; + } + + if(word_data > obj->psa) + { + obj->psa = word_data; + APS_LOG("%s: update psa: psa=%d,psi=%d\n", __func__, obj->psa, obj->psi); + } + if(word_data < obj->psi) + { + obj->psi = word_data; + APS_LOG("%s: update psi: psa=%d,psi=%d\n", __func__, obj->psa, obj->psi); + } + } + + diff = obj->psa - obj->psi; + if(diff > obj->stk_max_min_diff) + { + obj->psi_set = obj->psi; + + /* [yanlin start] modified calibration */ + if (obj->ps - obj->psi >= obj->stk_max_min_diff) + { + APS_LOG("--- %s: update threshold: ps=%d,psi=%d ---\n", __func__, obj->ps, obj->psi); + if (obj->ps_high_thd_boot <= obj->psi + obj->stk_ht_n_ct) + { + atomic_set(&obj->ps_high_thd_val, obj->ps_high_thd_boot); + atomic_set(&obj->ps_low_thd_val, obj->ps_low_thd_boot); + } + else + { + atomic_set(&obj->ps_high_thd_val, obj->psi + obj->stk_ht_n_ct); + atomic_set(&obj->ps_low_thd_val, obj->psi + obj->stk_lt_n_ct); + } + } + else if (obj->psa - obj->ps >= obj->stk_max_min_diff) + { + APS_LOG("--- %s: update threshold: ps=%d,psa=%d ---\n", __func__, obj->ps, obj->psa); + atomic_set(&obj->ps_high_thd_val, obj->psi + obj->stk_ht_n_ct); + atomic_set(&obj->ps_low_thd_val, obj->psi + obj->stk_lt_n_ct); + } + +#ifndef STK_TUNE1 + //atomic_set(&obj->ps_high_thd_val, obj->psi + obj->stk_ht_n_ct); + //atomic_set(&obj->ps_low_thd_val, obj->psi + obj->stk_lt_n_ct); + /* [yanlin end] */ +#ifdef CALI_EVERY_TIME + if( (atomic_read(&obj->ps_high_thd_val) > (obj->ps_high_thd_boot + 500)) && (obj->boot_cali == 1) ) + { + atomic_set(&obj->ps_high_thd_val, obj->ps_high_thd_boot); + atomic_set(&obj->ps_low_thd_val, obj->ps_low_thd_boot); + APS_LOG("%s: change THD to boot HT=%d, LT=%d\n", __func__, atomic_read(&obj->ps_high_thd_val), atomic_read(&obj->ps_low_thd_val)); + } + else + { + APS_LOG("%s: boot HT=%d, LT=%d, boot_cali=%d\n", __func__, obj->ps_high_thd_boot, obj->ps_low_thd_boot, obj->boot_cali); + } +#endif + + if((ret = stk3x1x_write_ps_high_thd(obj->client, atomic_read(&obj->ps_high_thd_val)))) + { + APS_ERR("write high thd error: %d\n", ret); + return ret; + } + if((ret = stk3x1x_write_ps_low_thd(obj->client, atomic_read(&obj->ps_low_thd_val)))) + { + APS_ERR("write low thd error: %d\n", ret); + return ret; + } +#ifdef STK_DEBUG_PRINTF + APS_LOG("%s: FAE tune0 psa-psi(%d) > DIFF found\n", __func__, diff); +#endif + APS_LOG("%s: set HT=%d, LT=%d\n", __func__, atomic_read(&obj->ps_high_thd_val), atomic_read(&obj->ps_low_thd_val)); +#endif + hrtimer_cancel(&obj->ps_tune0_timer); + } + + return 0; +} +#endif /*#ifdef STK_TUNE0 */ + +static void stk_ps_tune0_work_func(struct work_struct *work) +{ + struct stk3x1x_priv *obj = container_of(work, struct stk3x1x_priv, stk_ps_tune0_work); +#ifdef STK_GES + if(obj->ges_enabled) + stk_ges_poll_func(obj); +#endif +#ifdef STK_TUNE0 + if(obj->tune_zero_init_proc) + stk_tune_zero_get_ps_data(obj); + else + stk_ps_tune_zero_func_fae(obj); +#endif + return; +} + +/*----------------------------------------------------------------------------*/ +#ifdef STK_TUNE0 +static enum hrtimer_restart stk_ps_tune0_timer_func(struct hrtimer *timer) +{ + struct stk3x1x_priv *obj = container_of(timer, struct stk3x1x_priv, ps_tune0_timer); + queue_work(obj->stk_ps_tune0_wq, &obj->stk_ps_tune0_work); + hrtimer_forward_now(&obj->ps_tune0_timer, obj->ps_tune0_delay); + return HRTIMER_RESTART; +} +#endif +/*----------------------------------------------------------------------------*/ + +#ifdef STK_TUNE1 + +struct get_ct_struct +{ + int ps_val[STK_PS_VAL_BUF]; + unsigned int ps_val_buffer_cnt; + int psi; + int psa; +}; + +int stk_tune1_check_stable(int val[], int val_len, int criteria, int *imax, int *imin) +{ + int aa; + int max, min; + + max = INT_MIN; + min = INT_MAX; + + for(aa=0;aa max) + max = val[aa]; + if(val[aa] < min) + min = val[aa]; + } + + if(imax != NULL) + *imax = max; + if(imin != NULL) + *imin = min; + + if((max - min) < criteria) + return 1; + return 0; +} + +static struct get_ct_struct ps_get_ct; + +static void stk_tune1_reset_para(void) +{ + int cnt; + + ps_get_ct.psi = 0xFFFF; + ps_get_ct.psa = 0; + ps_get_ct.ps_val_buffer_cnt = 0; + + for(cnt=0;cnt= STK_PS_VAL_BUF) + { + ps_get_ct.ps_val_buffer_cnt = 0; + } + + if(ps < ps_get_ct.psi) + ps_get_ct.psi = ps; + if(ps > ps_get_ct.psa) + ps_get_ct.psa = ps; + + // if(ps_get_ct.psa - ps_get_ct.psi > STK_CT_UPDATE_DIFF) + // { + if(stk_tune1_check_stable(ps_get_ct.ps_val, STK_PS_VAL_BUF, PS_NOISE, &ps_max, &ps_min) == 1) + { + stk_tune1_reset_para(); + return ps_min; + } + // } + return -1; +} + +static int stk_tune1_ct_det(struct stk3x1x_priv *obj, uint32_t ps) +{ + int ct = 0; + int err; + + err = stk3x1x_ps_val(); + if(err == 0xFFFF) + { + if(obj->invalid_ps_cnt++ > STK_TUNE1_INVALID_PS_LIMIT) + { + printk("%s: invalid_ps_cnt > limit\n", __func__); + return 3; + } + return 0; + } + + ct = stk_tune1_get_ct(ps); + if(ct >= 0) + { + if(ct < STK_PS_CT_LIMIT) + { + atomic_set(&obj->ps_high_thd_val, ct + obj->stk_ht_n_ct); + atomic_set(&obj->ps_low_thd_val, ct + obj->stk_lt_n_ct); + stk3x1x_write_ps_high_thd(obj->client, atomic_read(&obj->ps_high_thd_val)); + if(obj->hw->polling_mode_ps) + { + stk3x1x_write_ps_low_thd(obj->client, atomic_read(&obj->ps_low_thd_val)); + } + else + { + stk3x1x_write_ps_low_thd(obj->client, 0); + } + // update_thd += STK_THD_F_PS0_CT_OK; + printk("%s: HT=%d,LT=%d\n", __func__, atomic_read(&obj->ps_high_thd_val), atomic_read(&obj->ps_low_thd_val)); + return 1; + } + else + { + // update_thd = STK_THD_F_PS0_CT_NG; + printk("%s: ct>STK_PS_CT_LIMIT,keep HT/LT\n", __func__); + return 2; + } + } + return 0; +} + +static int stk_tune1_fin_det(struct stk3x1x_priv *obj, uint32_t ps, int nf_flag) +{ + static uint8_t ps_far_re_cali = 0; + + if(nf_flag == 0) + { + if(ps >= STK_FIN_THD) + { + ps_far_re_cali = 1; + atomic_set(&obj->ps_high_thd_val, STK_PS_HIGHER_THDH); + atomic_set(&obj->ps_low_thd_val, STK_PS_HIGHER_THDL); + if(obj->hw->polling_mode_ps) + { + stk3x1x_write_ps_high_thd(obj->client, STK_PS_HIGHER_THDH); + stk3x1x_write_ps_low_thd(obj->client, STK_PS_HIGHER_THDL); + } + printk("%s: fixed HT=%d,LT=%d\n", __func__, STK_PS_HIGHER_THDH, STK_PS_HIGHER_THDL); + return 1; + } + } + else + { + if(ps_far_re_cali) + { + ps_far_re_cali = 0; + if(ps < STK_FIN_THD) + { + atomic_set(&obj->ps_high_thd_val, ps + obj->stk_ht_n_ct * 2); + atomic_set(&obj->ps_low_thd_val, ps + obj->stk_lt_n_ct * 2); + if(obj->hw->polling_mode_ps) + { + stk3x1x_write_ps_high_thd(obj->client, atomic_read(&obj->ps_high_thd_val)); + stk3x1x_write_ps_low_thd(obj->client, atomic_read(&obj->ps_low_thd_val)); + } + printk("%s: keep HT=%d,LT=%d\n", __func__, atomic_read(&obj->ps_high_thd_val), atomic_read(&obj->ps_low_thd_val)); + } + else + { + printk("%s: keep HT=%d,LT=%d\n", __func__, atomic_read(&obj->ps_high_thd_val), atomic_read(&obj->ps_low_thd_val)); + } + return 0x10; + } + } + return 0; +} + +static int stk_tune1_ps_int_handle(struct stk3x1x_priv *obj) +{ + int ret; + int32_t nf_state; + u16 als_reading = 0; + + int ps_thd_h = atomic_read(&obj->ps_high_thd_val); + int ps_thd_l = atomic_read(&obj->ps_low_thd_val); + + nf_state = (obj->ps >= ps_thd_h) ? 0 : (obj->ps<= ps_thd_l ? 1 : obj->ps_distance_last); + printk("%s:handle_state=%d,ps=%d,als=%d,nf_flag=%d,ps_distance_last=%d\n", + __func__, obj->stk_int_handle_state, obj->ps, als_reading, + nf_state, obj->ps_distance_last); + + switch(obj->stk_int_handle_state) + { + case STK_PSINT_NORM: + stk_ps_report(obj, nf_state); + printk("%s:(N) ps input event=%d, ps code=%d\n",__func__, nf_state, obj->ps); + if(nf_state) + { + stk3x1x_write_ps_high_thd(obj->client, atomic_read(&obj->ps_high_thd_val)); + stk3x1x_write_ps_low_thd(obj->client, 0); + obj->stk_int_handle_state = STK_PSINT_NORM; + printk("%s:(N) HT=%d,LT=0\n", __func__, ps_thd_h); + } + else + { + stk3x1x_write_ps_high_thd(obj->client, STK_FIN_THD); + stk3x1x_write_ps_low_thd(obj->client, atomic_read(&obj->ps_low_thd_val)); + obj->stk_int_handle_state = STK_PSINT_FIN_DET; + printk("%s:(N) HT=%d,LT=%d\n", __func__, STK_FIN_THD, ps_thd_l); + } + break; + case STK_PSINT_FIN_DET: + if(nf_state) + { + stk_ps_report(obj, nf_state); + printk("%s:(F) ps input event=%d, ps=%d\n",__func__, nf_state, obj->ps); + stk3x1x_write_ps_high_thd(obj->client, atomic_read(&obj->ps_high_thd_val)); + stk3x1x_write_ps_low_thd(obj->client, 0); + obj->stk_int_handle_state = STK_PSINT_NORM; + printk("%s:(F) HT=%d,LT=0\n", __func__, ps_thd_h); + } + break; + case STK_PSINT_CT_DET: + ret = stk_tune1_ct_det(obj, obj->ps); + if(ret > 0) + { + stk3x1x_write_ps_high_thd(obj->client, atomic_read(&obj->ps_high_thd_val)); + stk3x1x_write_ps_low_thd(obj->client, 0); + obj->stk_int_handle_state = STK_PSINT_NORM; + printk("%s:(C) HT=%d,LT=0\n", __func__, ps_thd_h); + } + + if(!nf_state) + { + stk_ps_report(obj, nf_state); + printk("%s:(C) ps input event=%d, ps=%d\n",__func__, nf_state, obj->ps); + stk3x1x_write_ps_high_thd(obj->client, STK_FIN_THD); + stk3x1x_write_ps_low_thd(obj->client, atomic_read(&obj->ps_low_thd_val)); + obj->stk_int_handle_state = STK_PSINT_FIN_DET; + printk("%s:(C) HT=%d,LT=%d\n", __func__, STK_FIN_THD, ps_thd_l); + } + break; + default: + printk(KERN_ERR "%s: invalid stk_int_handle_state %d\n", __func__, obj->stk_int_handle_state); + } + + ret = stk_tune1_fin_det(obj, obj->ps, nf_state); + if(ret == 1) + { + if(obj->stk_int_handle_state == STK_PSINT_FIN_DET) + { + obj->stk_int_handle_state = STK_PSINT_NORM; + stk3x1x_write_ps_high_thd(obj->client, 0xFFFF); + stk3x1x_write_ps_low_thd(obj->client, atomic_read(&obj->ps_low_thd_val)); + printk("%s: HT=0xffff,LT=%d\n", __func__, atomic_read(&obj->ps_low_thd_val)); + } + } + else if(ret == 0x10 && nf_state == 1) + { + stk3x1x_write_ps_high_thd(obj->client, 0); + stk3x1x_write_ps_low_thd(obj->client, 0xffff); + obj->stk_int_handle_state = STK_PSINT_CT_DET; + obj->invalid_ps_cnt = 0; + printk("%s: HT=0,LT=0xffff\n", __func__); + + stk_tune1_reset_para(); + printk("%s: reset ps_get_ct\n", __func__); + } + return 0; +} + +#endif // #ifdef STK_TUNE1 + + +/*----------------------------------------------------------------------------*/ +void stk3x1x_eint_func(void) +{ + struct stk3x1x_priv *obj = stk3x1x_obj; + APS_LOG(" interrupt fuc\n"); + if(!obj) + { + return; + } + //schedule_work(&obj->eint_work); + if(obj->hw->polling_mode_ps == 0 || obj->hw->polling_mode_als == 0) + schedule_delayed_work(&obj->eint_work,0); + if(atomic_read(&obj->trace) & STK_TRC_EINT) + { + APS_LOG("eint: als/ps intrs\n"); + } +} +/*----------------------------------------------------------------------------*/ +static void stk3x1x_eint_work(struct work_struct *work) +{ + struct stk3x1x_priv *obj = stk3x1x_obj; + int err; + u8 flag_reg, disable_flag = 0; + + APS_LOG(" eint work\n"); + + if((err = stk3x1x_check_intr(obj->client, &flag_reg))) + { + APS_ERR("stk3x1x_check_intr fail: %d\n", err); + goto err_i2c_rw; + } + + APS_LOG(" &obj->pending_intr =%lx\n",obj->pending_intr); + + if(((1<pending_intr) && (obj->hw->polling_mode_ps == 0)) + { + APS_LOG("stk ps change\n"); + disable_flag |= STK_FLG_PSINT_MASK; + + if((err = stk3x1x_read_ps(obj->client, &obj->ps))) + { + APS_ERR("stk3x1x read ps data: %d\n", err); + goto err_i2c_rw; + } + + distance_flag = (flag_reg & STK_FLG_NF_MASK)? 1 : 0; + APS_LOG("%s:ps raw 0x%x -> value 0x%x \n",__FUNCTION__, obj->ps,distance_flag); + + /* [yanlin start] check if eint was error */ + if ((!(atomic_read(&obj->state_val) & STK_STATE_EN_PS_MASK)) + || ((obj->ps < atomic_read(&obj->ps_low_thd_val)) && (distance_flag == 0)) + || ((obj->ps > atomic_read(&obj->ps_high_thd_val)) && (distance_flag == 1))) + { + APS_ERR("--- stk ps eint error: state=0x%x ---\n", atomic_read(&obj->state_val)); + if((err = stk3x1x_clear_intr(obj->client, flag_reg, disable_flag))) + { + APS_ERR("fail: %d\n", err); + } + goto err_i2c_rw; + } + /* [yanlin end] */ + + //let up layer to know + stk_ps_report(obj, distance_flag); + +#ifdef STK_PS_DEBUG + APS_LOG("%s: ps interrupt, show all reg\n", __FUNCTION__); + show_allreg(); //for debug +#endif + } + if(disable_flag) + { + if((err = stk3x1x_clear_intr(obj->client, flag_reg, disable_flag))) + { + APS_ERR("fail: %d\n", err); + goto err_i2c_rw; + } + } + msleep(1); + +#if defined(CONFIG_OF) + enable_irq(obj->irq); +#endif + + return; + +err_i2c_rw: + msleep(30); +#if defined(CONFIG_OF) + enable_irq(obj->irq); +#endif + return; +} + + +#if defined(CONFIG_OF) +static irqreturn_t stk3x1x_eint_handler(int irq, void *desc) +{ + printk("----------------stk3x1x_eint_handler-----------------------\n"); + disable_irq_nosync(stk3x1x_obj->irq); + stk3x1x_eint_func(); + + return IRQ_HANDLED; +} +#endif + + +/*----------------------------------------------------------------------------*/ +int stk3x1x_setup_eint(struct i2c_client *client) +{ + int ret; + struct pinctrl *pinctrl; + struct pinctrl_state *pins_default; + struct pinctrl_state *pins_cfg; + + u32 ints[2] = {0, 0}; + + alspsPltFmDev = get_alsps_platformdev(); +/* gpio setting */ + pinctrl = devm_pinctrl_get(&alspsPltFmDev->dev); + if (IS_ERR(pinctrl)) { + ret = PTR_ERR(pinctrl); + APS_ERR("Cannot find alsps pinctrl!\n"); + } + pins_default = pinctrl_lookup_state(pinctrl, "pin_default"); + if (IS_ERR(pins_default)) { + ret = PTR_ERR(pins_default); + APS_ERR("Cannot find alsps pinctrl default!\n"); + } + + pins_cfg = pinctrl_lookup_state(pinctrl, "pin_cfg"); + if (IS_ERR(pins_cfg)) { + ret = PTR_ERR(pins_cfg); + APS_ERR("Cannot find alsps pinctrl pin_cfg!\n"); + } +/* eint request */ + if (stk3x1x_obj->irq_node) { + //node = of_find_matching_node(node, touch_of_match); + + of_property_read_u32_array(stk3x1x_obj->irq_node, "debounce", ints, ARRAY_SIZE(ints)); + gpio_request(ints[0], "p-sensor"); + gpio_set_debounce(ints[0], ints[1]); + pinctrl_select_state(pinctrl, pins_cfg); + APS_LOG("ints[0] = %d, ints[1] = %d!!\n", ints[0], ints[1]); + + stk3x1x_obj->irq = irq_of_parse_and_map(stk3x1x_obj->irq_node, 0); + APS_LOG("stk3x1x_obj->irq = %d\n", stk3x1x_obj->irq); + if (!stk3x1x_obj->irq) { + APS_ERR("irq_of_parse_and_map fail!!\n"); + return -EINVAL; + } + if (request_irq(stk3x1x_obj->irq, stk3x1x_eint_handler, IRQ_TYPE_LEVEL_LOW, "ALS-eint", NULL)) { + APS_ERR("IRQ LINE NOT AVAILABLE!!\n"); + return -EINVAL; + } + //enable_irq(stk3x1x_obj->irq); /* [yanlin] fix irq invalid when use calibration in Engineer mode */ + } else { + APS_ERR("null irq node!!\n"); + return -EINVAL; + } + + return 0; +} +/*----------------------------------------------------------------------------*/ +static int stk3x1x_init_client(struct i2c_client *client) +{ + struct stk3x1x_priv *obj = i2c_get_clientdata(client); + int err; + int ps_ctrl; + //u8 int_status; + + if((err = stk3x1x_write_sw_reset(client))) + { + APS_ERR("software reset error, err=%d", err); + return err; + } + if((err = stk3x1x_read_id(client))) + { + APS_ERR("stk3x1x_read_id error, err=%d", err); + return err; + } + if(obj->first_boot == true) + { + if(obj->hw->polling_mode_ps == 0 || obj->hw->polling_mode_als == 0) + { + if((err = stk3x1x_setup_eint(client))) + { + APS_ERR("setup eint error: %d\n", err); + return err; + } + } + } + if((err = stk3x1x_write_state(client, atomic_read(&obj->state_val)))) + { + APS_ERR("write stete error: %d\n", err); + return err; + } + +/* + if((err = stk3x1x_check_intr(client, &int_status))) + { + APS_ERR("check intr error: %d\n", err); + return err; + } + + if((err = stk3x1x_clear_intr(client, int_status, STK_FLG_PSINT_MASK | STK_FLG_ALSINT_MASK))) + { + APS_ERR("clear intr error: %d\n", err); + return err; + } +*/ + ps_ctrl = atomic_read(&obj->psctrl_val); + if(obj->hw->polling_mode_ps == 1) + ps_ctrl &= 0x3F; + + if((err = stk3x1x_write_ps(client, ps_ctrl))) + { + APS_ERR("write ps error: %d\n", err); + return err; + } + + if((err = stk3x1x_write_als(client, atomic_read(&obj->alsctrl_val)))) + { + APS_ERR("write als error: %d\n", err); + return err; + } + + if((err = stk3x1x_write_led(client, obj->ledctrl_val))) + { + APS_ERR("write led error: %d\n", err); + return err; + } + + if((err = stk3x1x_write_wait(client, obj->wait_val))) + { + APS_ERR("write wait error: %d\n", err); + return err; + } +#ifndef STK_TUNE0 + if((err = stk3x1x_write_ps_high_thd(client, atomic_read(&obj->ps_high_thd_val)))) + { + APS_ERR("write high thd error: %d\n", err); + return err; + } + + if((err = stk3x1x_write_ps_low_thd(client, atomic_read(&obj->ps_low_thd_val)))) + { + APS_ERR("write low thd error: %d\n", err); + return err; + } +#endif +#ifdef STK_TUNE1 + if(obj->hw->polling_mode_ps == 0) + obj->int_val = 0x7; +#endif + if((err = stk3x1x_write_int(client, obj->int_val))) + { + APS_ERR("write int mode error: %d\n", err); + return err; + } + + /* + u8 data; + data = 0x60; + err = stk3x1x_master_send(client, 0x87, &data, 1); + if (err < 0) + { + APS_ERR("write 0x87 = %d\n", err); + return -EFAULT; + } + */ +#ifdef STK_ALS_FIR + memset(&obj->fir, 0x00, sizeof(obj->fir)); +#endif +#ifdef STK_TUNE0 + if(obj->first_boot == true) + stk_ps_tune_zero_init(obj); +#endif +#ifdef STK_GES + obj->re_enable_ges = false; + obj->re_enable_ges2 = 0; + atomic_set(&obj->gesture2, 0); +#endif + obj->re_enable_ps = false; + obj->re_enable_als = false; + obj->als_code_last = 100; +#ifdef STK_TUNE1 + if(obj->hw->polling_mode_als == 1 && obj->hw->polling_mode_ps == 0) + obj->hw->polling_mode_als = 0; +#endif + obj->ps_distance_last = -1; + obj->als_code_last = 100; +#ifdef STK_TUNE1 + obj->stk_int_handle_state = STK_PSINT_NORM; +#endif + return 0; +} + +/****************************************************************************** + * Sysfs attributes +*******************************************************************************/ +static ssize_t stk3x1x_show_config(struct device_driver *ddri, char *buf) +{ + ssize_t res; + + if(!stk3x1x_obj) + { + APS_ERR("stk3x1x_obj is null!!\n"); + return 0; + } + + res = scnprintf(buf, PAGE_SIZE, "(%d %d %d %d %d %d)\n", + atomic_read(&stk3x1x_obj->i2c_retry), atomic_read(&stk3x1x_obj->als_debounce), + atomic_read(&stk3x1x_obj->ps_mask), atomic_read(&stk3x1x_obj->ps_high_thd_val),atomic_read(&stk3x1x_obj->ps_low_thd_val), atomic_read(&stk3x1x_obj->ps_debounce)); + return res; +} +/*----------------------------------------------------------------------------*/ +static ssize_t stk3x1x_store_config(struct device_driver *ddri, const char *buf, size_t count) +{ + int retry, als_deb, ps_deb, mask, hthres, lthres, err; + struct i2c_client *client; + client = stk3x1x_i2c_client; + if(!stk3x1x_obj) + { + APS_ERR("stk3x1x_obj is null!!\n"); + return 0; + } + + if(6 == sscanf(buf, "%d %d %d %d %d %d", &retry, &als_deb, &mask, &hthres, <hres, &ps_deb)) + { + atomic_set(&stk3x1x_obj->i2c_retry, retry); + atomic_set(&stk3x1x_obj->als_debounce, als_deb); + atomic_set(&stk3x1x_obj->ps_mask, mask); + atomic_set(&stk3x1x_obj->ps_high_thd_val, hthres); + atomic_set(&stk3x1x_obj->ps_low_thd_val, lthres); + atomic_set(&stk3x1x_obj->ps_debounce, ps_deb); + + if((err = stk3x1x_write_ps_high_thd(client, atomic_read(&stk3x1x_obj->ps_high_thd_val)))) + { + APS_ERR("write high thd error: %d\n", err); + return err; + } + + if((err = stk3x1x_write_ps_low_thd(client, atomic_read(&stk3x1x_obj->ps_low_thd_val)))) + { + APS_ERR("write low thd error: %d\n", err); + return err; + } + } + else + { + APS_ERR("invalid content: '%s'\n", buf); + } + return count; +} +/*----------------------------------------------------------------------------*/ +static ssize_t stk3x1x_show_trace(struct device_driver *ddri, char *buf) +{ + ssize_t res; + if(!stk3x1x_obj) + { + APS_ERR("stk3x1x_obj is null!!\n"); + return 0; + } + + res = scnprintf(buf, PAGE_SIZE, "0x%04X\n", atomic_read(&stk3x1x_obj->trace)); + return res; +} +/*----------------------------------------------------------------------------*/ +static ssize_t stk3x1x_store_trace(struct device_driver *ddri, const char *buf, size_t count) +{ + int trace; + if(!stk3x1x_obj) + { + APS_ERR("stk3x1x_obj is null!!\n"); + return 0; + } + + if(1 == sscanf(buf, "0x%x", &trace)) + { + atomic_set(&stk3x1x_obj->trace, trace); + } + else + { + APS_ERR("invalid content: '%s', length = %d\n", buf, (int)count); + } + return count; +} + +/*----------------------------------------------------------------------------*/ +static ssize_t stk3x1x_show_ir(struct device_driver *ddri, char *buf) +{ + int32_t reading; + + if(!stk3x1x_obj) + { + APS_ERR("stk3x1x_obj is null!!\n"); + return 0; + } + reading = stk3x1x_get_ir_value(stk3x1x_obj, STK_IRS_IT_REDUCE); + if(reading < 0) + return scnprintf(buf, PAGE_SIZE, "ERROR: %d\n", reading); + + stk3x1x_obj->ir_code = reading; + return scnprintf(buf, PAGE_SIZE, "0x%04X\n", stk3x1x_obj->ir_code); +} + +/*----------------------------------------------------------------------------*/ +static ssize_t stk3x1x_show_als(struct device_driver *ddri, char *buf) +{ + int res; + + if(!stk3x1x_obj) + { + APS_ERR("stk3x1x_obj is null!!\n"); + return 0; + } + if(stk3x1x_obj->hw->polling_mode_als == 0) + { + if((res = stk3x1x_read_als(stk3x1x_obj->client, &stk3x1x_obj->als))) + return scnprintf(buf, PAGE_SIZE, "ERROR: %d\n", res); + else + return scnprintf(buf, PAGE_SIZE, "0x%04X\n", stk3x1x_obj->als); + } + return scnprintf(buf, PAGE_SIZE, "0x%04X\n", stk3x1x_obj->als_code_last); +} +/*----------------------------------------------------------------------------*/ +static ssize_t stk3x1x_show_ps(struct device_driver *ddri, char *buf) +{ + int res; + if(!stk3x1x_obj) + { + APS_ERR("stk3x1x_obj is null!!\n"); + return 0; + } + + if((res = stk3x1x_read_ps(stk3x1x_obj->client, &stk3x1x_obj->ps))) + { + return scnprintf(buf, PAGE_SIZE, "ERROR: %d\n", res); + } + else + { + /* [liguanxiong] ps data output format same as ltr579 */ + return scnprintf(buf, PAGE_SIZE, "0x%04X(%d)\n", stk3x1x_obj->ps, stk3x1x_obj->ps); + } +} +/*----------------------------------------------------------------------------*/ +static ssize_t stk3x1x_show_reg(struct device_driver *ddri, char *buf) +{ + u8 int_status; + if(!stk3x1x_obj) + { + APS_ERR("stk3x1x_obj is null!!\n"); + return 0; + } + + /*read*/ + stk3x1x_check_intr(stk3x1x_obj->client, &int_status); + //stk3x1x_clear_intr(stk3x1x_obj->client, int_status, 0x0); + stk3x1x_read_ps(stk3x1x_obj->client, &stk3x1x_obj->ps); + stk3x1x_read_als(stk3x1x_obj->client, &stk3x1x_obj->als); + /*write*/ + stk3x1x_write_als(stk3x1x_obj->client, atomic_read(&stk3x1x_obj->alsctrl_val)); + stk3x1x_write_ps(stk3x1x_obj->client, atomic_read(&stk3x1x_obj->psctrl_val)); + stk3x1x_write_ps_high_thd(stk3x1x_obj->client, atomic_read(&stk3x1x_obj->ps_high_thd_val)); + stk3x1x_write_ps_low_thd(stk3x1x_obj->client, atomic_read(&stk3x1x_obj->ps_low_thd_val)); + return 0; +} +/*----------------------------------------------------------------------------*/ +static ssize_t stk3x1x_show_send(struct device_driver *ddri, char *buf) +{ + return 0; +} +/*----------------------------------------------------------------------------*/ +static ssize_t stk3x1x_store_send(struct device_driver *ddri, const char *buf, size_t count) +{ + int addr, cmd; + u8 dat; + + if(!stk3x1x_obj) + { + APS_ERR("stk3x1x_obj is null!!\n"); + return 0; + } + else if(2 != sscanf(buf, "%x %x", &addr, &cmd)) + { + APS_ERR("invalid format: '%s'\n", buf); + return 0; + } + + dat = (u8)cmd; + APS_LOG("send(%02X, %02X) = %d\n", addr, cmd, + stk3x1x_master_send(stk3x1x_obj->client, (u16)addr, &dat, sizeof(dat))); + + return count; +} +/*----------------------------------------------------------------------------*/ +static ssize_t stk3x1x_show_recv(struct device_driver *ddri, char *buf) +{ + if(!stk3x1x_obj) + { + APS_ERR("stk3x1x_obj is null!!\n"); + return 0; + } + return scnprintf(buf, PAGE_SIZE, "0x%04X\n", atomic_read(&stk3x1x_obj->recv_reg)); +} +/*----------------------------------------------------------------------------*/ +static ssize_t stk3x1x_store_recv(struct device_driver *ddri, const char *buf, size_t count) +{ + int addr; + u8 dat; + if(!stk3x1x_obj) + { + APS_ERR("stk3x1x_obj is null!!\n"); + return 0; + } + else if(1 != sscanf(buf, "%x", &addr)) + { + APS_ERR("invalid format: '%s'\n", buf); + return 0; + } + + APS_LOG("recv(%02X) = %d, 0x%02X\n", addr, + stk3x1x_master_recv(stk3x1x_obj->client, (u16)addr, (char*)&dat, sizeof(dat)), dat); + atomic_set(&stk3x1x_obj->recv_reg, dat); + return count; +} +/*----------------------------------------------------------------------------*/ + +static ssize_t stk3x1x_show_allreg(struct device_driver *ddri, char *buf) +{ + int ret = 0; + u8 rbuf[0x22]; + int cnt; + int len = 0; + + memset(rbuf, 0, sizeof(rbuf)); + if(!stk3x1x_obj) + { + APS_ERR("stk3x1x_obj is null!!\n"); + return 0; + } + ret = stk3x1x_master_recv(stk3x1x_obj->client, 0, &rbuf[0], 7); + if(ret < 0) + { + APS_DBG("error: %d\n", ret); + return -EFAULT; + } + ret = stk3x1x_master_recv(stk3x1x_obj->client, 7, &rbuf[7], 7); + if(ret < 0) + { + APS_DBG("error: %d\n", ret); + return -EFAULT; + } + ret = stk3x1x_master_recv(stk3x1x_obj->client, 14, &rbuf[14], 7); + if(ret < 0) + { + APS_DBG("error: %d\n", ret); + return -EFAULT; + } + ret = stk3x1x_master_recv(stk3x1x_obj->client, 21, &rbuf[21], 7); + if(ret < 0) + { + APS_DBG("error: %d\n", ret); + return -EFAULT; + } + ret = stk3x1x_master_recv(stk3x1x_obj->client, 28, &rbuf[28], 4); + if(ret < 0) + { + APS_DBG("error: %d\n", ret); + return -EFAULT; + } + + ret = stk3x1x_master_recv(stk3x1x_obj->client, STK_PDT_ID_REG, &rbuf[32], 2); + if(ret < 0) + { + APS_DBG("error: %d\n", ret); + return -EFAULT; + } + + for(cnt=0;cnt<0x20;cnt++) + { + APS_LOG("reg[0x%x]=0x%x\n", cnt, rbuf[cnt]); + len += scnprintf(buf+len, PAGE_SIZE-len, "[%2X]%2X,", cnt, rbuf[cnt]); + } + APS_LOG("reg[0x3E]=0x%x\n", rbuf[cnt]); + APS_LOG("reg[0x3F]=0x%x\n", rbuf[cnt++]); + len += scnprintf(buf+len, PAGE_SIZE-len, "[0x3E]%2X,[0x3F]%2X\n", rbuf[cnt-1], rbuf[cnt]); + return len; + /* + return scnprintf(buf, PAGE_SIZE, "[0]%2X [1]%2X [2]%2X [3]%2X [4]%2X [5]%2X [6/7 HTHD]%2X,%2X [8/9 LTHD]%2X, %2X [A]%2X [B]%2X [C]%2X [D]%2X [E/F Aoff]%2X,%2X,[10]%2X [11/12 PS]%2X,%2X [13]%2X [14]%2X [15/16 Foff]%2X,%2X [17]%2X [18]%2X [3E]%2X [3F]%2X\n", + rbuf[0], rbuf[1], rbuf[2], rbuf[3], rbuf[4], rbuf[5], rbuf[6], rbuf[7], rbuf[8], + rbuf[9], rbuf[10], rbuf[11], rbuf[12], rbuf[13], rbuf[14], rbuf[15], rbuf[16], rbuf[17], + rbuf[18], rbuf[19], rbuf[20], rbuf[21], rbuf[22], rbuf[23], rbuf[24], rbuf[25], rbuf[26]); + */ +} +/*----------------------------------------------------------------------------*/ +static ssize_t stk3x1x_show_status(struct device_driver *ddri, char *buf) +{ + ssize_t len = 0; + u8 rbuf[25]; + int ret = 0; + + if(!stk3x1x_obj) + { + APS_ERR("stk3x1x_obj is null!!\n"); + return 0; + } + + if(stk3x1x_obj->hw) + { + len += scnprintf(buf+len, PAGE_SIZE-len, "CUST: %d, (%d %d) (%02X) (%02X %02X %02X) (%02X %02X %02X %02X)\n", + stk3x1x_obj->hw->i2c_num, stk3x1x_obj->hw->power_id, stk3x1x_obj->hw->power_vol, stk3x1x_obj->addr.flag, + stk3x1x_obj->addr.alsctrl, stk3x1x_obj->addr.data1_als, stk3x1x_obj->addr.data2_als, stk3x1x_obj->addr.psctrl, + stk3x1x_obj->addr.data1_ps, stk3x1x_obj->addr.data2_ps, stk3x1x_obj->addr.thdh1_ps); + } + else + { + len += scnprintf(buf+len, PAGE_SIZE-len, "CUST: NULL\n"); + } + + len += scnprintf(buf+len, PAGE_SIZE-len, "REGS: %02X %02X %02X %02X %02X %02X %02X %02X %02lX %02lX\n", + atomic_read(&stk3x1x_obj->state_val), atomic_read(&stk3x1x_obj->psctrl_val), atomic_read(&stk3x1x_obj->alsctrl_val), + stk3x1x_obj->ledctrl_val, stk3x1x_obj->int_val, stk3x1x_obj->wait_val, + atomic_read(&stk3x1x_obj->ps_high_thd_val), atomic_read(&stk3x1x_obj->ps_low_thd_val),stk3x1x_obj->enable, stk3x1x_obj->pending_intr); +#ifdef MT6516 + len += scnprintf(buf+len, PAGE_SIZE-len, "EINT: %d (%d %d %d %d)\n", mt_get_gpio_in(GPIO_ALS_EINT_PIN), + CUST_EINT_ALS_NUM, CUST_EINT_ALS_POLARITY, CUST_EINT_ALS_DEBOUNCE_EN, CUST_EINT_ALS_DEBOUNCE_CN); + + len += scnprintf(buf+len, PAGE_SIZE-len, "GPIO: %d (%d %d %d %d)\n", GPIO_ALS_EINT_PIN, + mt_get_gpio_dir(GPIO_ALS_EINT_PIN), mt_get_gpio_mode(GPIO_ALS_EINT_PIN), + mt_get_gpio_pull_enable(GPIO_ALS_EINT_PIN), mt_get_gpio_pull_select(GPIO_ALS_EINT_PIN)); +#endif + + len += scnprintf(buf+len, PAGE_SIZE-len, "MISC: %d %d\n", atomic_read(&stk3x1x_obj->als_suspend), atomic_read(&stk3x1x_obj->ps_suspend)); + len += scnprintf(buf+len, PAGE_SIZE-len, "VER.: %s\n", DRIVER_VERSION); + + memset(rbuf, 0, sizeof(rbuf)); + ret = stk3x1x_master_recv(stk3x1x_obj->client, 0, &rbuf[0], 7); + if(ret < 0) + { + APS_DBG("error: %d\n", ret); + return -EFAULT; + } + ret = stk3x1x_master_recv(stk3x1x_obj->client, 7, &rbuf[7], 7); + if(ret < 0) + { + APS_DBG("error: %d\n", ret); + return -EFAULT; + } + ret = stk3x1x_master_recv(stk3x1x_obj->client, 14, &rbuf[14], 7); + if(ret < 0) + { + APS_DBG("error: %d\n", ret); + return -EFAULT; + } + /* + ret = stk3x1x_master_recv(stk3x1x_obj->client, 21, &rbuf[21], 4); + if(ret < 0) + { + APS_DBG("error: %d\n", ret); + return -EFAULT; + } + */ + len += scnprintf(buf+len, PAGE_SIZE-len, "[PS=%2X] [ALS=%2X] [WAIT=%4Xms] [EN_ASO=%2X] [EN_AK=%2X] [NEAR/FAR=%2X] [FLAG_OUI=%2X] [FLAG_PSINT=%2X] [FLAG_ALSINT=%2X]\n", + rbuf[0]&0x01,(rbuf[0]&0x02)>>1,((rbuf[0]&0x04)>>2)*rbuf[5]*6,(rbuf[0]&0x20)>>5, + (rbuf[0]&0x40)>>6,rbuf[16]&0x01,(rbuf[16]&0x04)>>2,(rbuf[16]&0x10)>>4,(rbuf[16]&0x20)>>5); + + return len; +} +/*----------------------------------------------------------------------------*/ +#define IS_SPACE(CH) (((CH) == ' ') || ((CH) == '\n')) +/*----------------------------------------------------------------------------*/ +static int read_int_from_buf(struct stk3x1x_priv *obj, const char* buf, size_t count, + u32 data[], int len) +{ + int idx = 0; + char *cur = (char*)buf, *end = (char*)(buf+count); + + while(idx < len) + { + while((cur < end) && IS_SPACE(*cur)) + { + cur++; + } + + if(1 != sscanf(cur, "%d", &data[idx])) + { + break; + } + + idx++; + while((cur < end) && !IS_SPACE(*cur)) + { + cur++; + } + } + return idx; +} +/*----------------------------------------------------------------------------*/ +static ssize_t stk3x1x_show_alslv(struct device_driver *ddri, char *buf) +{ + ssize_t len = 0; + int idx; + if(!stk3x1x_obj) + { + APS_ERR("stk3x1x_obj is null!!\n"); + return 0; + } + + for(idx = 0; idx < stk3x1x_obj->als_level_num; idx++) + { + len += scnprintf(buf+len, PAGE_SIZE-len, "%d ", stk3x1x_obj->hw->als_level[idx]); + } + len += scnprintf(buf+len, PAGE_SIZE-len, "\n"); + return len; +} +/*----------------------------------------------------------------------------*/ +static ssize_t stk3x1x_store_alslv(struct device_driver *ddri, const char *buf, size_t count) +{ + if(!stk3x1x_obj) + { + APS_ERR("stk3x1x_obj is null!!\n"); + return 0; + } + else if(!strcmp(buf, "def")) + { + memcpy(stk3x1x_obj->als_level, stk3x1x_obj->hw->als_level, sizeof(stk3x1x_obj->als_level)); + } + else if(stk3x1x_obj->als_level_num != read_int_from_buf(stk3x1x_obj, buf, count, + stk3x1x_obj->hw->als_level, stk3x1x_obj->als_level_num)) + { + APS_ERR("invalid format: '%s'\n", buf); + } + return count; +} +/*----------------------------------------------------------------------------*/ +static ssize_t stk3x1x_show_alsval(struct device_driver *ddri, char *buf) +{ + ssize_t len = 0; + int idx; + if(!stk3x1x_obj) + { + APS_ERR("stk3x1x_obj is null!!\n"); + return 0; + } + + for(idx = 0; idx < stk3x1x_obj->als_value_num; idx++) + { + len += scnprintf(buf+len, PAGE_SIZE-len, "%d ", stk3x1x_obj->hw->als_value[idx]); + } + len += scnprintf(buf+len, PAGE_SIZE-len, "\n"); + return len; +} +/*----------------------------------------------------------------------------*/ +static ssize_t stk3x1x_store_alsval(struct device_driver *ddri, const char *buf, size_t count) +{ + if(!stk3x1x_obj) + { + APS_ERR("stk3x1x_obj is null!!\n"); + return 0; + } + else if(!strcmp(buf, "def")) + { + memcpy(stk3x1x_obj->als_value, stk3x1x_obj->hw->als_value, sizeof(stk3x1x_obj->als_value)); + } + else if(stk3x1x_obj->als_value_num != read_int_from_buf(stk3x1x_obj, buf, count, + stk3x1x_obj->hw->als_value, stk3x1x_obj->als_value_num)) + { + APS_ERR("invalid format: '%s'\n", buf); + } + return count; +} + +#ifdef STK_TUNE0 +static ssize_t stk3x1x_show_cali(struct device_driver *ddri, char *buf) +{ + int32_t word_data; + u8 r_buf[2]; + int ret; + + if(!stk3x1x_obj) + { + APS_ERR("stk3x1x_obj is null!!\n"); + return 0; + } + + ret = stk3x1x_master_recv(stk3x1x_obj->client, 0x20, r_buf, 2); + if(ret < 0) + { + APS_ERR("%s fail, err=0x%x", __FUNCTION__, ret); + return ret; + } + word_data = (r_buf[0] << 8) | r_buf[1]; + + ret = stk3x1x_master_recv(stk3x1x_obj->client, 0x22, r_buf, 2); + if(ret < 0) + { + APS_ERR("%s fail, err=0x%x", __FUNCTION__, ret); + return ret; + } + word_data += (r_buf[0] << 8) | r_buf[1]; + + APS_LOG("%s: psi_set=%d, psa=%d,psi=%d, word_data=%d\n", __FUNCTION__, + stk3x1x_obj->psi_set, stk3x1x_obj->psa, stk3x1x_obj->psi, word_data); +#ifdef CALI_EVERY_TIME + APS_LOG("%s: boot HT=%d, LT=%d\n", __func__, stk3x1x_obj->ps_high_thd_boot, stk3x1x_obj->ps_low_thd_boot); +#endif + return scnprintf(buf, PAGE_SIZE, "%5d\n", stk3x1x_obj->psi_set); + //return 0; +} + +static ssize_t stk3x1x_ps_maxdiff_store(struct device_driver *ddri, const char *buf, size_t count) +{ + unsigned long value = 0; + int ret; + + if((ret = kstrtoul(buf, 10, &value)) < 0) + { + printk(KERN_ERR "%s:kstrtoul failed, ret=0x%x\n", __func__, ret); + return ret; + } + stk3x1x_obj->stk_max_min_diff = (int) value; + return count; +} + + +static ssize_t stk3x1x_ps_maxdiff_show(struct device_driver *ddri, char *buf) +{ + if(!stk3x1x_obj) + { + APS_ERR("stk3x1x_obj is null!!\n"); + return 0; + } + + return scnprintf(buf, PAGE_SIZE, "%d\n", stk3x1x_obj->stk_max_min_diff); +} + +static ssize_t stk3x1x_ps_ltnct_store(struct device_driver *ddri, const char *buf, size_t count) +{ + unsigned long value = 0; + int ret; + + if((ret = kstrtoul(buf, 10, &value)) < 0) + { + printk(KERN_ERR "%s:kstrtoul failed, ret=0x%x\n", __func__, ret); + return ret; + } + stk3x1x_obj->stk_lt_n_ct = (int) value; + return count; +} + +static ssize_t stk3x1x_ps_ltnct_show(struct device_driver *ddri, char *buf) +{ + if(!stk3x1x_obj) + { + APS_ERR("stk3x1x_obj is null!!\n"); + return 0; + } + return scnprintf(buf, PAGE_SIZE, "%d\n", stk3x1x_obj->stk_lt_n_ct); +} + +static ssize_t stk3x1x_ps_htnct_store(struct device_driver *ddri, const char *buf, size_t count) +{ + unsigned long value = 0; + int ret; + + if((ret = kstrtoul(buf, 10, &value)) < 0) + { + printk(KERN_ERR "%s:kstrtoul failed, ret=0x%x\n", __func__, ret); + return ret; + } + stk3x1x_obj->stk_ht_n_ct = (int) value; + return count; +} + +static ssize_t stk3x1x_ps_htnct_show(struct device_driver *ddri, char *buf) +{ + if(!stk3x1x_obj) + { + APS_ERR("stk3x1x_obj is null!!\n"); + return 0; + } + return scnprintf(buf, PAGE_SIZE, "%d\n", stk3x1x_obj->stk_ht_n_ct); +} + +#endif // #ifdef STK_TUNE0 + +#ifdef STK_ALS_FIR +/*----------------------------------------------------------------------------*/ +static ssize_t stk3x1x_show_firlen(struct device_driver *ddri, char *buf) +{ + int len = atomic_read(&stk3x1x_obj->firlength); + + if(!stk3x1x_obj) + { + APS_ERR("stk3x1x_obj is null!!\n"); + return 0; + } + + APS_LOG("%s: len = %2d, idx = %2d\n", __func__, len, stk3x1x_obj->fir.idx); + APS_LOG("%s: sum = %5d, ave = %5d\n", __func__, stk3x1x_obj->fir.sum, stk3x1x_obj->fir.sum/len); + + return scnprintf(buf, PAGE_SIZE, "%d\n", len); +} + +/*----------------------------------------------------------------------------*/ +static ssize_t stk3x1x_store_firlen(struct device_driver *ddri, const char *buf, size_t count) +{ + int value; + + if(!stk3x1x_obj) + { + APS_ERR("stk3x1x_obj is null!!\n"); + return 0; + } + else if(1 != sscanf(buf, "%d", &value)) + { + APS_ERR("invalid format: '%s'\n", buf); + return 0; + } + + if(value > MAX_FIR_LEN) + { + APS_ERR("%s: firlen exceed maximum filter length\n", __func__); + } + else if (value < 1) + { + atomic_set(&stk3x1x_obj->firlength, 1); + memset(&stk3x1x_obj->fir, 0x00, sizeof(stk3x1x_obj->fir)); + } + else + { + atomic_set(&stk3x1x_obj->firlength, value); + memset(&stk3x1x_obj->fir, 0x00, sizeof(stk3x1x_obj->fir)); + } + + return count; +} +#endif /* #ifdef STK_ALS_FIR */ + +/*---Offset At-------------------------------------------------------------------------*/ +static ssize_t stk3x1x_show_ps_offset(struct device_driver *ddri, char *buf) +{ + if(!stk3x1x_obj) + { + APS_ERR("stk3x1x_obj is null!!\n"); + return 0; + } +#ifdef STK_TUNE0 + return snprintf(buf, PAGE_SIZE, "%d\n", stk3x1x_obj->crosstalk); +#else + return 0; +#endif +} +/*----------------------------------------------------------------------------*/ +static ssize_t stk3x1x_set_ps_offset(struct device_driver *ddri, const char *buf, size_t count) +{ + int ret; + ret = stk3x1x_ps_calibration(stk3x1x_obj->client); + return count; +} + +/*----------------------------------------------------------------------------*/ +static ssize_t stk3x1x_show_pthreshold_calibration(struct device_driver *ddri, char *buf) +{ + // struct stk3x1x_priv *data = i2c_get_clientdata(stk3x1x_obj->client); +#ifdef CALI_EVERY_TIME + return snprintf(buf,PAGE_SIZE, "Low threshold = %d , High threshold = %d\n", stk3x1x_obj->ps_low_thd_boot, stk3x1x_obj->ps_high_thd_boot); +#else + return 0; +#endif +} + +static ssize_t stk3x1x_store_ps_offset(struct device_driver *ddri, const char *buf, size_t count) +{ +#ifdef STK_TUNE0 + struct stk3x1x_priv *obj = stk3x1x_obj; + int ps_cali_val = 0; + //int threshold[2] = {0}; + sscanf(buf,"%d",&ps_cali_val); + obj->crosstalk = ps_cali_val; + #ifdef STK_TUNE1 + if (obj->crosstalk == 0 || obj->crosstalk > STK_PS_HIGHER_THDL) + #else + if (obj->crosstalk == 0) + #endif + { + #ifdef STK_TUNE1 + obj->crosstalk = STK_PS_HIGHER_THDL; + #else + obj->crosstalk = obj->hw->ps_threshold_low; + #endif + stk3x1x_calibration_flag = 0; + }else { + stk3x1x_calibration_flag = 1; + } + mutex_lock(&run_cali_mutex); + #ifdef CALI_EVERY_TIME + obj->ps_high_thd_boot = obj->crosstalk + obj->stk_ht_n_ct; + obj->ps_low_thd_boot = obj->crosstalk + obj->stk_lt_n_ct; + printk( "%s: crosstalk=%d, high thd=%d, low thd=%d\n", __func__, obj->crosstalk, obj->ps_high_thd_boot, obj->ps_low_thd_boot); + #endif + + mutex_unlock(&run_cali_mutex); +#endif + return count; +} + +/* [yanlin add] */ +//#define DRIVER_VERSION "1.0.0" +static ssize_t stk3x1x_show_hw_info(struct device_driver *ddri, char *buf) +{ + return sprintf(buf,"type:\t%s\nvendor:\t%s\ndriver_version:\t%s\n", + "STK3321-35A", "SensorTek", DRIVER_VERSION); +} + +/*----------------------------------------------------------------------------*/ +/* [yanlin start] for hw info in /sys/kernel */ +static ssize_t show_alsps_info(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf,"type:\t%s\nvendor:\t%s\ndriver_version:\t%s\n", + "STK3321-35A", "SensorTek", DRIVER_VERSION); +} +static DEVICE_ATTR(alsps_info, S_IWUSR | S_IRUGO, show_alsps_info, NULL); +/* [yanlin end] */ + +// [wanglei start] +// Add for als adjust +static ssize_t stk3x1x_show_winfac(struct device_driver *ddri, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", winfac); +} +static ssize_t stk3x1x_store_winfac(struct device_driver *ddri, const char *buf, size_t count) +{ + int value = 0; + int ret = 0; + ret = kstrtoint(buf, 10, &value); + if (ret == 0) { + winfac = value; + APS_DBG("set als winfac = %d\n", winfac); + } + return count; +} +static DRIVER_ATTR(winfac, S_IWUSR | S_IRUGO, stk3x1x_show_winfac, stk3x1x_store_winfac); +// [wanglei end] + +/*----------------------------------------------------------------------------*/ +static DRIVER_ATTR(als, S_IWUSR | S_IRUGO, stk3x1x_show_als, NULL); +static DRIVER_ATTR(ps, S_IWUSR | S_IRUGO, stk3x1x_show_ps, NULL); +static DRIVER_ATTR(ir, S_IWUSR | S_IRUGO, stk3x1x_show_ir, NULL); +static DRIVER_ATTR(config, S_IWUSR | S_IRUGO, stk3x1x_show_config,stk3x1x_store_config); +static DRIVER_ATTR(alslv, S_IWUSR | S_IRUGO, stk3x1x_show_alslv, stk3x1x_store_alslv); +static DRIVER_ATTR(alsval, S_IWUSR | S_IRUGO, stk3x1x_show_alsval,stk3x1x_store_alsval); +static DRIVER_ATTR(trace, S_IWUSR | S_IRUGO, stk3x1x_show_trace, stk3x1x_store_trace); +static DRIVER_ATTR(status, S_IWUSR | S_IRUGO, stk3x1x_show_status, NULL); +static DRIVER_ATTR(send, S_IWUSR | S_IRUGO, stk3x1x_show_send, stk3x1x_store_send); +static DRIVER_ATTR(recv, S_IWUSR | S_IRUGO, stk3x1x_show_recv, stk3x1x_store_recv); +static DRIVER_ATTR(reg, S_IWUSR | S_IRUGO, stk3x1x_show_reg, NULL); +static DRIVER_ATTR(allreg, S_IWUSR | S_IRUGO, stk3x1x_show_allreg, NULL); +static DRIVER_ATTR(pscalibration, S_IWUSR | S_IRUGO, stk3x1x_show_ps_offset, stk3x1x_set_ps_offset); +static DRIVER_ATTR(pthredcalibration, S_IWUSR | S_IRUGO, stk3x1x_show_pthreshold_calibration, NULL); +#ifdef STK_TUNE0 +static DRIVER_ATTR(cali, S_IWUSR | S_IRUGO, stk3x1x_show_cali, NULL); +static DRIVER_ATTR(maxdiff, S_IWUSR | S_IRUGO, stk3x1x_ps_maxdiff_show, stk3x1x_ps_maxdiff_store); +static DRIVER_ATTR(ltnct, S_IWUSR | S_IRUGO, stk3x1x_ps_ltnct_show, stk3x1x_ps_ltnct_store); +static DRIVER_ATTR(htnct, S_IWUSR | S_IRUGO, stk3x1x_ps_htnct_show, stk3x1x_ps_htnct_store); +#endif +#ifdef STK_ALS_FIR +static DRIVER_ATTR(firlen, S_IWUSR | S_IRUGO, stk3x1x_show_firlen, stk3x1x_store_firlen); +#endif +static DRIVER_ATTR(ps_offset, S_IWUSR | S_IRUGO,NULL, stk3x1x_store_ps_offset); + +/* [yanlin add] */ +static DRIVER_ATTR(hw_info, S_IWUSR | S_IRUGO, stk3x1x_show_hw_info, NULL); +/*----------------------------------------------------------------------------*/ +static struct driver_attribute *stk3x1x_attr_list[] = { + &driver_attr_als, + &driver_attr_ps, + &driver_attr_ir, + &driver_attr_trace, /*trace log*/ + &driver_attr_config, + &driver_attr_alslv, + &driver_attr_alsval, + &driver_attr_status, + &driver_attr_send, + &driver_attr_recv, + &driver_attr_allreg, + &driver_attr_reg, + &driver_attr_pscalibration, + &driver_attr_pthredcalibration, +#ifdef STK_TUNE0 + &driver_attr_cali, + &driver_attr_maxdiff, + &driver_attr_ltnct, + &driver_attr_htnct, +#endif +#ifdef STK_ALS_FIR + &driver_attr_firlen, +#endif + &driver_attr_ps_offset, + &driver_attr_hw_info, + &driver_attr_winfac, // [wanglei] Add for als adjust +}; + +/*----------------------------------------------------------------------------*/ +static int stk3x1x_create_attr(struct device_driver *driver) +{ + int idx, err = 0; + int num = (int)(sizeof(stk3x1x_attr_list)/sizeof(stk3x1x_attr_list[0])); + if (driver == NULL) + { + return -EINVAL; + } + + for(idx = 0; idx < num; idx++) + { + if((err = driver_create_file(driver, stk3x1x_attr_list[idx]))) + { + APS_ERR("driver_create_file (%s) = %d\n", stk3x1x_attr_list[idx]->attr.name, err); + break; + } + } + + /* [yanlin add] for hw info in /sys/kernel */ + err = sysfs_create_file(kernel_kobj, &dev_attr_alsps_info.attr); + + return err; +} +/*----------------------------------------------------------------------------*/ +static int stk3x1x_delete_attr(struct device_driver *driver) +{ + int idx ,err = 0; + int num = (int)(sizeof(stk3x1x_attr_list)/sizeof(stk3x1x_attr_list[0])); + + if (!driver) + return -EINVAL; + + for (idx = 0; idx < num; idx++) + { + driver_remove_file(driver, stk3x1x_attr_list[idx]); + } + + /* [yanlin add] for hw info in /sys/kernel */ + sysfs_remove_file(kernel_kobj, &dev_attr_alsps_info.attr); + + return err; +} + + +#ifdef STK_GES +static ssize_t stk_ges_poll_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + int len = 0, ii = 0, jj = 0; + + while(stk_ges_op[ii].ops[0] != 0) + { + len += scnprintf(buf + len, PAGE_SIZE - len, "%x ", ii); + for(jj=0;jj<4;jj++) + len += scnprintf(buf + len, PAGE_SIZE - len, "%x ", stk_ges_op[ii].ops[jj]); + len += scnprintf(buf + len, PAGE_SIZE - len, "\n"); + ii++; + } + return len; +} + +static ssize_t stk_ges_poll_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) +{ + int32_t ret, i = 0, index = 0; + char *token; + unsigned long value = 0; + + while(buf != '\0') + { + token = strsep((char **)&buf, " "); + if((ret = kstrtoul(token, 16, &value)) < 0) + { + printk(KERN_ERR "%s:kstrtoul failed, ret=0x%x\n", __func__, ret); + return ret; + } + + if(i == 0) + { + if(value >= 10) + { + memset(stk_ges_op, 0, sizeof(stk_ges_op)); + break; + } + else + index = value; + } + else + { + stk_ges_op[index].ops[i-1] = value; + } + i++; + if(i == 5) + break; + } + if(i != 5) + { + printk(KERN_ERR "%s: invalid length(%d)\n", __func__, i); + memset(&(stk_ges_op[index]), 0, sizeof(union stk_ges_operation)); + } + return size; +} + +static ssize_t stk_ges_code_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct stk3x1x_priv *obj = dev_get_drvdata(dev); + int ret; + unsigned int gest0 = 0, gest1 = 0, gest2 = 0; + + if(!obj->ges_enabled) + return 0; + + ret = stk3x1x_get_ges_value(obj, &gest0, &gest1, &gest2); + if(ret < 0) + return ret; + else if(ret == 0xFFFF) + atomic_set(&obj->gesture2, 0); + + return scnprintf(buf, PAGE_SIZE, "%5d,%5d,%5d\n", gest0, gest1, atomic_read(&obj->gesture2)); +} + +static ssize_t stk_ges_code_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) +{ + struct stk3x1x_priv *obj = dev_get_drvdata(dev); + uint8_t ges; + unsigned long value = 0; + int ret; + + ret = kstrtoul(buf, 16, &value); + if(ret < 0) + { + APS_ERR( "%s:kstrtoul failed, ret=%d\n", __func__, ret); + return ret; + } + + if(obj->ges_enabled) + { + switch(value) + { + case 3: + //APS_LOG( "%s: ges input event, not detected\n",__func__); + case 0: + return size; + case 1: + ges = KEY_PAGEUP; + atomic_set(&obj->gesture2, 0); + APS_LOG( "%s: ges input event >>>\n",__func__); + break; + case 2: + ges = KEY_PAGEDOWN; + atomic_set(&obj->gesture2, 0); + APS_LOG( "%s: ges input event <<<\n",__func__); + break; + case 32: + ges = KEY_VOLUMEDOWN; + APS_LOG( "%s: ges input event near\n",__func__); + break; + case 48: + ges = KEY_VOLUMEUP; + APS_LOG( "%s: ges input event far\n",__func__); + break; + default: + APS_ERR( "%s, invalid value %d\n", __func__, (int)value); + return -EINVAL; + } + + input_report_key(obj->ges_input_dev, ges, 1); + input_report_key(obj->ges_input_dev, ges, 0); + input_sync(obj->ges_input_dev); + } + return size; +} + +static ssize_t stk_ges_enable_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) +{ + struct stk3x1x_priv *obj = dev_get_drvdata(dev); + unsigned long value = 0; + int ret; + + ret = kstrtoul(buf, 16, &value); + if(ret < 0) + { + APS_ERR( "%s:kstrtoul failed, ret=%d\n", __func__, ret); + return ret; + } + APS_LOG( "%s: Enable GES : %d\n", __func__, (int)value); + + switch(value) + { + case 0: + if(obj->ges_enabled == 1) + stk3x1x_enable_ges(obj->client, 0, 1); + else + stk3x1x_enable_ges(obj->client, 0, 2); + break; + case 1: + stk3x1x_enable_ges(obj->client, 1, 1); + break; + case 2: + stk3x1x_enable_ges(obj->client, 1, 2); + break; + default: + APS_ERR( "%s, invalid value %d\n", __func__, *buf); + return -EINVAL; + } + + return size; +} + +static ssize_t stk_ges_enable_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct stk3x1x_priv *obj = dev_get_drvdata(dev); + + return scnprintf(buf, PAGE_SIZE, "%d\n", obj->ges_enabled); +} + +static ssize_t stk_recv_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + if(!stk3x1x_obj) + { + APS_ERR("stk3x1x_obj is null!!\n"); + return 0; + } + return scnprintf(buf, PAGE_SIZE, "0x%04X\n", atomic_read(&stk3x1x_obj->recv_reg)); +} + +static ssize_t stk_recv_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) +{ + int addr; + u8 dat; + if(!stk3x1x_obj) + { + APS_ERR("stk3x1x_obj is null!!\n"); + return 0; + } + else if(1 != sscanf(buf, "%x", &addr)) + { + APS_ERR("invalid format: '%s'\n", buf); + return 0; + } + stk3x1x_master_recv(stk3x1x_obj->client, (u16)addr, (char*)&dat, sizeof(dat)); + //APS_LOG("recv(%02X) = %d, 0x%02X\n", addr, + //stk3x1x_master_recv(stk3x1x_obj->client, (u16)addr, (char*)&dat, sizeof(dat)), dat); + atomic_set(&stk3x1x_obj->recv_reg, dat); + return size; +} + +static ssize_t stk_send_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return 0; +} + +static ssize_t stk_send_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) +{ + int addr, cmd; + u8 dat; + + if(!stk3x1x_obj) + { + APS_ERR("stk3x1x_obj is null!!\n"); + return 0; + } + else if(2 != sscanf(buf, "%x %x", &addr, &cmd)) + { + APS_ERR("invalid format: '%s'\n", buf); + return 0; + } + + dat = (u8)cmd; + APS_LOG("send(%02X, %02X) = %d\n", addr, cmd, + stk3x1x_master_send(stk3x1x_obj->client, (u16)addr, &dat, sizeof(dat))); + + return size; +} + +static struct device_attribute ges_enable_attribute = __ATTR(enable,0664,stk_ges_enable_show,stk_ges_enable_store); +static struct device_attribute ges_code_attribute = __ATTR(code, 0664, stk_ges_code_show, stk_ges_code_store); +static struct device_attribute ges_poll_attribute = __ATTR(poll, 0664, stk_ges_poll_show, stk_ges_poll_store); +static struct device_attribute ges_recv_attribute = __ATTR(recv,0664,stk_recv_show,stk_recv_store); +static struct device_attribute ges_send_attribute = __ATTR(send,0664,stk_send_show, stk_send_store); + +static struct attribute *stk_ges_attrs [] = +{ + &ges_enable_attribute.attr, + &ges_code_attribute.attr, + &ges_poll_attribute.attr, + &ges_recv_attribute.attr, + &ges_send_attribute.attr, + NULL +}; + +static struct attribute_group stk_ges_attribute_group = +{ + .name = "driver", + .attrs = stk_ges_attrs, +}; +#endif /* #ifdef STK_GES */ +/****************************************************************************** + * Function Configuration +******************************************************************************/ +#ifdef STK_GES +static uint32_t stk3x1x_get_ges_value(struct stk3x1x_priv *obj, unsigned int *ges0, unsigned int *ges1, unsigned int *ges2) +{ + u8 buf[4]; + int err, retry = 10; + u8 flag; + + do { + err = stk3x1x_read_flag(obj->client, &flag); + if(err < 0) + return err; + if(flag & STK_FLG_PSDR_MASK) + break; + //APS_LOG( "%s: ps isnot ready\n", __func__); + retry--; + usleep_range(350, 1000); + } while(retry > 0); + + err = stk3x1x_master_recv(obj->client, obj->addr.data1_ps, buf, 0x02); + if(err < 0) + { + APS_DBG("error: %d\n", err); + return -EFAULT; + } + + err = stk3x1x_master_recv(obj->client, 0x24, buf, 0x04); + if(err < 0) + { + APS_DBG("error: %d\n", err); + return -EFAULT; + } + *ges0 = (buf[0]<<8) | buf[1]; + *ges1 = (buf[2]<<8) | buf[3]; + //APS_LOG( "%s: ges=%d,%d\n",__func__, *ges0, *ges1); + return 0; +} +#endif + + +static int stk3x1x_get_als_value(struct stk3x1x_priv *obj, u16 als) +{ + int idx; + int invalid = 0; + for(idx = 0; idx < obj->als_level_num; idx++) + { + if(als < obj->hw->als_level[idx]) + { + break; + } + } + + if(idx >= obj->als_value_num) + { + APS_ERR("exceed range\n"); + idx = obj->als_value_num - 1; + } + + if(1 == atomic_read(&obj->als_deb_on)) + { + unsigned long endt = atomic_read(&obj->als_deb_end); + if(time_after(jiffies, endt)) + { + atomic_set(&obj->als_deb_on, 0); + } + + if(1 == atomic_read(&obj->als_deb_on)) + { + invalid = 1; + } + } + + if(!invalid) + { + if (atomic_read(&obj->trace) & STK_TRC_CVT_ALS) + { + APS_DBG("ALS: %05d => %05d\n", als, obj->hw->als_value[idx]); + } + + return obj->hw->als_value[idx]; + } + else + { + if(atomic_read(&obj->trace) & STK_TRC_CVT_ALS) + { + APS_DBG("ALS: %05d => %05d (-1)\n", als, obj->hw->als_value[idx]); + } + return -1; + } +} + +/*----------------------------------------------------------------------------*/ +static int stk3x1x_get_ps_value_only(struct stk3x1x_priv *obj, u16 ps) +{ + int mask = atomic_read(&obj->ps_mask); + int invalid = 0, val; + int err; + u8 flag; + + err = stk3x1x_read_flag(obj->client, &flag); + if(err) + return err; + val = (flag & STK_FLG_NF_MASK)? 1 : 0; + + if(atomic_read(&obj->ps_suspend)) + { + invalid = 1; + } + else if(1 == atomic_read(&obj->ps_deb_on)) + { + unsigned long endt = atomic_read(&obj->ps_deb_end); + if(time_after(jiffies, endt)) + { + atomic_set(&obj->ps_deb_on, 0); + } + + if (1 == atomic_read(&obj->ps_deb_on)) + { + invalid = 1; + } + } + + if(!invalid) + { + if(unlikely(atomic_read(&obj->trace) & STK_TRC_CVT_PS)) + { + if(mask) + { + APS_DBG("PS: %05d => %05d [M] \n", ps, val); + } + else + { + APS_DBG("PS: %05d => %05d\n", ps, val); + } + } + return val; + + } + else + { + APS_ERR(" ps value is invalid, PS: %05d => %05d\n", ps, val); + if(unlikely(atomic_read(&obj->trace) & STK_TRC_CVT_PS)) + { + APS_DBG("PS: %05d => %05d (-1)\n", ps, val); + } + return -1; + } +} +/*----------------------------------------------------------------------------*/ +static int stk3x1x_get_ps_value(struct stk3x1x_priv *obj, u16 ps) +{ + int mask = atomic_read(&obj->ps_mask); + int invalid = 0, val; + int err; + u8 flag; + + err = stk3x1x_read_flag(obj->client, &flag); + if(err) + return err; + + val = (flag & STK_FLG_NF_MASK)? 1 : 0; +#ifdef STK_TUNE1 + stk_tune1_fin_det(obj, ps, val); + printk("%s: als=%d,ps=%d,nf_flag=%d\n", __func__, obj->als_code_last, ps,val); + if(val == 1) + stk_tune1_ct_det(obj, ps); +#endif + APS_LOG("%s: read_flag = 0x%x, val = %d\n", __FUNCTION__, flag, val); + /* + if((err = stk3x1x_clear_intr(obj->client, flag, STK_FLG_OUI_MASK))) + { + APS_ERR("fail: %d\n", err); + return err; + } +*/ + if(atomic_read(&obj->ps_suspend)) + { + invalid = 1; + } + else if(1 == atomic_read(&obj->ps_deb_on)) + { + unsigned long endt = atomic_read(&obj->ps_deb_end); + if(time_after(jiffies, endt)) + { + atomic_set(&obj->ps_deb_on, 0); + } + + if (1 == atomic_read(&obj->ps_deb_on)) + { + invalid = 1; + } + } + + + if(!invalid) + { + if(unlikely(atomic_read(&obj->trace) & STK_TRC_CVT_PS)) + { + if(mask) + { + APS_DBG("PS: %05d => %05d [M] \n", ps, val); + } + else + { + APS_DBG("PS: %05d => %05d\n", ps, val); + } + } + return val; + + } + else + { + APS_ERR(" ps value is invalid, PS: %05d => %05d\n", ps, val); + if(unlikely(atomic_read(&obj->trace) & STK_TRC_CVT_PS)) + { + APS_DBG("PS: %05d => %05d (-1)\n", ps, val); + } + return -1; + } +} + +/*----------------------------------------------------------------------------*/ + +static int32_t stk3x1x_set_irs_it_slp(struct stk3x1x_priv *obj, uint16_t *slp_time, int32_t ials_it_reduce) +{ + uint8_t irs_alsctrl; + int32_t ret; + + irs_alsctrl = (atomic_read(&obj->alsctrl_val) & 0x0F) - ials_it_reduce; + switch(irs_alsctrl) + { + case 2: + *slp_time = 1; + break; + case 3: + *slp_time = 2; + break; + case 4: + *slp_time = 3; + break; + case 5: + *slp_time = 6; + break; + case 6: + *slp_time = 12; + break; + case 7: + *slp_time = 24; + break; + case 8: + *slp_time = 48; + break; + case 9: + *slp_time = 96; + break; + case 10: + *slp_time = 192; + break; + default: + APS_ERR( "%s: unknown ALS IT=0x%x\n", __func__, irs_alsctrl); + ret = -EINVAL; + return ret; + } + irs_alsctrl |= (atomic_read(&obj->alsctrl_val) & 0xF0); + ret = i2c_smbus_write_byte_data(obj->client, STK_ALSCTRL_REG, irs_alsctrl); + if (ret < 0) + { + APS_ERR( "%s: write i2c error\n", __func__); + return ret; + } + return 0; +} + +static int32_t stk3x1x_get_ir_value(struct stk3x1x_priv *obj, int32_t als_it_reduce) +{ + int32_t word_data, ret; + uint8_t w_reg, retry = 0; + uint16_t irs_slp_time = 100; + // bool re_enable_ps = false; + u8 flag; + u8 buf[2]; + +/* re_enable_ps = (atomic_read(&obj->state_val) & STK_STATE_EN_PS_MASK) ? true : false; + if(re_enable_ps) + { +#ifdef STK_TUNE0 + if (!(obj->psi_set)) + { + hrtimer_cancel(&obj->ps_tune0_timer); + cancel_work_sync(&obj->stk_ps_tune0_work); + } +#endif + stk3x1x_enable_ps(obj->client, 0, 1); + } +*/ + ret = stk3x1x_set_irs_it_slp(obj, &irs_slp_time, als_it_reduce); + if(ret < 0) + goto irs_err_i2c_rw; + + w_reg = atomic_read(&obj->state_val) | STK_STATE_EN_IRS_MASK; + ret = i2c_smbus_write_byte_data(obj->client, STK_STATE_REG, w_reg); + if (ret < 0) + { + APS_ERR( "%s: write i2c error\n", __func__); + goto irs_err_i2c_rw; + } + msleep(irs_slp_time); + + do + { + msleep(3); + ret = stk3x1x_read_flag(obj->client, &flag); + if (ret < 0) + { + APS_ERR("WARNING: read flag reg error: %d\n", ret); + goto irs_err_i2c_rw; + } + retry++; + }while(retry < 10 && ((flag&STK_FLG_IR_RDY_MASK) == 0)); + + if(retry == 10) + { + APS_ERR( "%s: ir data is not ready for a long time\n", __func__); + ret = -EINVAL; + goto irs_err_i2c_rw; + } + + ret = stk3x1x_clear_intr(obj->client, flag, STK_FLG_IR_RDY_MASK); + if (ret < 0) + { + APS_ERR( "%s: write i2c error\n", __func__); + goto irs_err_i2c_rw; + } + + ret = stk3x1x_master_recv(obj->client, STK_DATA1_IR_REG, buf, 2); + if(ret < 0) + { + APS_ERR( "%s fail, ret=0x%x", __func__, ret); + goto irs_err_i2c_rw; + } + word_data = (buf[0] << 8) | buf[1]; + + ret = i2c_smbus_write_byte_data(obj->client, STK_ALSCTRL_REG, atomic_read(&obj->alsctrl_val)); + if (ret < 0) + { + APS_ERR( "%s: write i2c error\n", __func__); + goto irs_err_i2c_rw; + } + //if(re_enable_ps) + //stk3x1x_enable_ps(obj->client, 1, 0); + return word_data; + +irs_err_i2c_rw: + //if(re_enable_ps) + // stk3x1x_enable_ps(obj->client, 1, 0); + return ret; +} + +/****************************************************************************** + * Function Configuration +******************************************************************************/ +static int stk3x1x_open(struct inode *inode, struct file *file) +{ + file->private_data = stk3x1x_i2c_client; + + if (!file->private_data) + { + APS_ERR("null pointer!!\n"); + return -EINVAL; + } + + return nonseekable_open(inode, file); +} +/*----------------------------------------------------------------------------*/ +static int stk3x1x_release(struct inode *inode, struct file *file) +{ + file->private_data = NULL; + return 0; +} +/*----------------------------------------------------------------------------*/ +static long stk3x1x_unlocked_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct i2c_client *client = (struct i2c_client*)file->private_data; + struct stk3x1x_priv *obj = i2c_get_clientdata(client); + long err = 0; + void __user *ptr = (void __user*) arg; + int dat; + uint32_t enable; + int ps_result, ps_cali_result; + int threshold[2]; + + switch (cmd) + { + case ALSPS_SET_PS_MODE: + if(copy_from_user(&enable, ptr, sizeof(enable))) + { + err = -EFAULT; + goto err_out; + } + if(enable) + { + if((err = stk3x1x_enable_ps(obj->client, 1, 1))) + { + APS_ERR("enable ps fail: %ld\n", err); + goto err_out; + } + + set_bit(STK_BIT_PS, &obj->enable); + } + else + { + if((err = stk3x1x_enable_ps(obj->client, 0, 1))) + { + APS_ERR("disable ps fail: %ld\n", err); + + goto err_out; + } + + clear_bit(STK_BIT_PS, &obj->enable); + } + break; + + case ALSPS_GET_PS_MODE: + enable = test_bit(STK_BIT_PS, &obj->enable) ? (1) : (0); + if(copy_to_user(ptr, &enable, sizeof(enable))) + { + err = -EFAULT; + goto err_out; + } + break; + + case ALSPS_GET_PS_DATA: + if((err = stk3x1x_read_ps(obj->client, &obj->ps))) + { + goto err_out; + } + + dat = stk3x1x_get_ps_value(obj, obj->ps); + if(dat < 0) + { + err = dat; + goto err_out; + } +#ifdef STK_PS_POLLING_LOG + APS_LOG("%s:ps raw 0x%x -> value 0x%x \n",__FUNCTION__, obj->ps, dat); +#endif + if(copy_to_user(ptr, &dat, sizeof(dat))) + { + err = -EFAULT; + goto err_out; + } + break; + + case ALSPS_GET_PS_RAW_DATA: + if((err = stk3x1x_read_ps(obj->client, &obj->ps))) + { + goto err_out; + } + + dat = obj->ps; + if(copy_to_user(ptr, &dat, sizeof(dat))) + { + err = -EFAULT; + goto err_out; + } + break; + + case ALSPS_SET_ALS_MODE: + if(copy_from_user(&enable, ptr, sizeof(enable))) + { + err = -EFAULT; + goto err_out; + } + if(enable) + { + if((err = stk3x1x_enable_als(obj->client, 1))) + { + APS_ERR("enable als fail: %ld\n", err); + + goto err_out; + } + set_bit(STK_BIT_ALS, &obj->enable); + } + else + { + if((err = stk3x1x_enable_als(obj->client, 0))) + { + APS_ERR("disable als fail: %ld\n", err); + + goto err_out; + } + clear_bit(STK_BIT_ALS, &obj->enable); + } + break; + + case ALSPS_GET_ALS_MODE: + enable = test_bit(STK_BIT_ALS, &obj->enable) ? (1) : (0); + if(copy_to_user(ptr, &enable, sizeof(enable))) + { + err = -EFAULT; + goto err_out; + } + break; + + case ALSPS_GET_ALS_DATA: + if((err = stk3x1x_read_als(obj->client, &obj->als))) + { + goto err_out; + } + + dat = stk3x1x_get_als_value(obj, obj->als); + if(copy_to_user(ptr, &dat, sizeof(dat))) + { + err = -EFAULT; + goto err_out; + } + break; + + case ALSPS_GET_ALS_RAW_DATA: + if((err = stk3x1x_read_als(obj->client, &obj->als))) + { + goto err_out; + } + + dat = obj->als; + if(copy_to_user(ptr, &dat, sizeof(dat))) + { + err = -EFAULT; + goto err_out; + } + break; + + case ALSPS_GET_PS_THRESHOLD_HIGH: + dat = atomic_read(&obj->ps_high_thd_val); + APS_LOG("%s:ps_high_thd_val:%d\n",__func__,dat); + if(copy_to_user(ptr, &dat, sizeof(dat))) + { + err = -EFAULT; + goto err_out; + } + break; + case ALSPS_GET_PS_THRESHOLD_LOW: + dat = atomic_read(&obj->ps_low_thd_val); + APS_LOG("%s:ps_low_thd_val:%d\n",__func__,dat); + if(copy_to_user(ptr, &dat, sizeof(dat))) + { + err = -EFAULT; + goto err_out; + } + break; + + case ALSPS_GET_PS_TEST_RESULT: + if((err = stk3x1x_read_ps(obj->client, &obj->ps))) + { + goto err_out; + } + + if(obj->ps > atomic_read(&obj->ps_high_thd_val)) + ps_result = 0; + else + ps_result = 1; + APS_LOG("ALSPS_GET_PS_TEST_RESULT : ps_result = %d\n",ps_result); + if(copy_to_user(ptr, &ps_result, sizeof(ps_result))) + { + err = -EFAULT; + goto err_out; + } + break; + + case ALSPS_IOCTL_CLR_CALI: + if(copy_from_user(&dat, ptr, sizeof(dat))) + { + err = -EFAULT; + goto err_out; + } + obj->ps_cali = 0; + atomic_set(&obj->ps_high_thd_val, obj->hw->ps_threshold_high); + atomic_set(&obj->ps_low_thd_val, obj->hw->ps_threshold_low); //obj->hw->ps_low_thd_val + APS_LOG("ALSPS_IOCTL_CLR_CALI : obj->ps_cali:%d high:%d low:%d\n", obj->ps_cali, + atomic_read(&obj->ps_high_thd_val), + atomic_read(&obj->ps_low_thd_val)); + break; + + case ALSPS_IOCTL_GET_CALI: + stk3x1x_ps_calibration(obj->client); + ps_cali_result = obj->ps_cali; + APS_LOG("ALSPS_IOCTL_GET_CALI : ps_cali = %d\n",obj->ps_cali); + if(copy_to_user(ptr, &ps_cali_result, sizeof(ps_cali_result))) + { + err = -EFAULT; + goto err_out; + } + break; + + case ALSPS_IOCTL_SET_CALI: + //1. libhwm.so calc value store in ps_cali; + //2. nvram_daemon update ps_cali in driver + if(copy_from_user(&ps_cali_result, ptr, sizeof(ps_cali_result))) + { + err = -EFAULT; + goto err_out; + } + obj->ps_cali = ps_cali_result; +#ifdef STK_TUNE0 + atomic_set(&obj->ps_high_thd_val, obj->ps_cali + obj->stk_ht_n_ct); + atomic_set(&obj->ps_low_thd_val, obj->ps_cali + obj->stk_lt_n_ct); +#else + atomic_set(&obj->ps_high_thd_val, obj->ps_cali + 200); + atomic_set(&obj->ps_low_thd_val, obj->ps_cali + 150); +#endif + if((err = stk3x1x_write_ps_high_thd(obj->client, atomic_read(&obj->ps_high_thd_val)))) + goto err_out; + if((err = stk3x1x_write_ps_low_thd(obj->client, atomic_read(&obj->ps_low_thd_val)))) + goto err_out; + APS_LOG("ALSPS_IOCTL_SET_CALI : ps_cali_result = %d\n",ps_cali_result); + APS_LOG("ALSPS_IOCTL_SET_CALI : obj->ps_cali:%d high:%d low:%d\n", obj->ps_cali, + atomic_read(&obj->ps_high_thd_val), + atomic_read(&obj->ps_low_thd_val)); + break; + + case ALSPS_SET_PS_THRESHOLD: + if(copy_from_user(threshold, ptr, sizeof(threshold))) + { + err = -EFAULT; + goto err_out; + } + APS_ERR("%s set threshold high: 0x%x, low: 0x%x\n", __func__, threshold[0],threshold[1]); + atomic_set(&obj->ps_high_thd_val, (threshold[0]+obj->ps_cali)); + atomic_set(&obj->ps_low_thd_val, (threshold[1]+obj->ps_cali));//need to confirm + + if((err = stk3x1x_write_ps_high_thd(obj->client, atomic_read(&obj->ps_high_thd_val)))) + { + APS_ERR("write high thd error: %ld\n", err); + goto err_out; + } + + if((err = stk3x1x_write_ps_low_thd(obj->client, atomic_read(&obj->ps_low_thd_val)))) + { + APS_ERR("write low thd error: %ld\n", err); + goto err_out; + } + + break; + + default: + APS_ERR("%s not supported = 0x%04x", __FUNCTION__, cmd); + err = -ENOIOCTLCMD; + break; + } + + err_out: + return err; +} +/*----------------------------------------------------------------------------*/ +static struct file_operations stk3x1x_fops = { + .open = stk3x1x_open, + .release = stk3x1x_release, + .unlocked_ioctl = stk3x1x_unlocked_ioctl, +}; +/*----------------------------------------------------------------------------*/ +static struct miscdevice stk3x1x_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "als_ps", + .fops = &stk3x1x_fops, +}; +/*----------------------------------------------------------------------------*/ +/* +static int stk3x1x_i2c_suspend(struct i2c_client *client, pm_message_t msg) +{ + APS_FUN(); + + if(msg.event == PM_EVENT_SUSPEND) + { + if(!obj) + { + APS_ERR("null pointer!!\n"); + return -EINVAL; + } + + atomic_set(&obj->als_suspend, 1); + if((err = stk3x1x_enable_als(client, 0))) + { + APS_ERR("disable als: %d\n", err); + return err; + } + + atomic_set(&obj->ps_suspend, 1); + if((err = stk3x1x_enable_ps(client, 0, 1))) + { + APS_ERR("disable ps: %d\n", err); + return err; + } + + stk3x1x_power(obj->hw, 0); + } + + return 0; +} +*/ +/*----------------------------------------------------------------------------*/ +/* +static int stk3x1x_i2c_resume(struct i2c_client *client) +{ + APS_FUN(); + + if(!obj) + { + APS_ERR("null pointer!!\n"); + return -EINVAL; + } + + stk3x1x_power(obj->hw, 1); + if((err = stk3x1x_init_client(client))) + { + APS_ERR("initialize client fail!!\n"); + return err; + } + atomic_set(&obj->als_suspend, 0); + if(test_bit(STK_BIT_ALS, &obj->enable)) + { + if((err = stk3x1x_enable_als(client, 1))) + { + APS_ERR("enable als fail: %d\n", err); + } + } + atomic_set(&obj->ps_suspend, 0); + if(test_bit(STK_BIT_PS, &obj->enable)) + { + if((err = stk3x1x_enable_ps(client, 1, 1))) + { + APS_ERR("enable ps fail: %d\n", err); + } + } + + return 0; +} +*/ +#if defined(CONFIG_HAS_EARLYSUSPEND) +/*----------------------------------------------------------------------------*/ +static void stk3x1x_early_suspend(struct early_suspend *h) +{ /*early_suspend is only applied for ALS*/ + int err; + struct stk3x1x_priv *obj = container_of(h, struct stk3x1x_priv, early_drv); + int old = atomic_read(&obj->state_val); + APS_FUN(); + + if(!obj) + { + APS_ERR("null pointer!!\n"); + return; + } +#ifdef STK_CHK_REG + err = stk3x1x_validate_n_handle(obj->client); + if(err < 0) + { + APS_ERR("stk3x1x_validate_n_handle fail: %d\n", err); + } + else if (err == 0xFF) + { + if(old & STK_STATE_EN_PS_MASK) + stk3x1x_enable_ps(obj->client, 1, 0); + } +#endif /* #ifdef STK_CHK_REG */ + +#ifdef STK_GES + if(obj->ges_enabled == 1) + { + obj->re_enable_ges = obj->ges_enabled; + stk3x1x_enable_ges(obj->client, 0, 1); + } + else if(obj->ges_enabled == 2) + { + obj->re_enable_ges = obj->ges_enabled; + stk3x1x_enable_ges(obj->client, 0, 2); + } +#endif + if(old & STK_STATE_EN_ALS_MASK) + { + atomic_set(&obj->als_suspend, 1); + if((err = stk3x1x_enable_als(obj->client, 0))) + { + APS_ERR("disable als fail: %d\n", err); + } + APS_LOG( "%s: Enable ALS : 0\n", __func__); + } +} +/*----------------------------------------------------------------------------*/ +static void stk3x1x_late_resume(struct early_suspend *h) +{ /*early_suspend is only applied for ALS*/ + int err; + struct stk3x1x_priv *obj = container_of(h, struct stk3x1x_priv, early_drv); + + APS_FUN(); + + if(!obj) + { + APS_ERR("null pointer!!\n"); + return; + } +#ifdef STK_CHK_REG + err = stk3x1x_validate_n_handle(obj->client); + if(err < 0) + { + APS_ERR("stk3x1x_validate_n_handle fail: %d\n", err); + } + else if (err == 0xFF) + { + if(atomic_read(&obj->state_val) & STK_STATE_EN_PS_MASK) + stk3x1x_enable_ps(obj->client, 1, 0); + } +#endif /* #ifdef STK_CHK_REG */ + +#ifdef STK_GES + if(obj->re_enable_ges == 1) + { + stk3x1x_enable_ges(obj->client, 1, 1); + obj->re_enable_ges = 0; + } + else if(obj->re_enable_ges == 2) + { + stk3x1x_enable_ges(obj->client, 1, 2); + obj->re_enable_ges = 0; + } +#endif +} +#endif +#if 0 +int stk3x1x_ps_operate(void* self, uint32_t command, void* buff_in, int size_in, + void* buff_out, int size_out, int* actualout) +{ + int err = 0; + int value; + hwm_sensor_data* sensor_data; + struct stk3x1x_priv *obj = (struct stk3x1x_priv *)self; + + //APS_FUN(f); + switch (command) + { + case SENSOR_DELAY: + if((buff_in == NULL) || (size_in < sizeof(int))) + { + APS_ERR("Set delay parameter error!\n"); + err = -EINVAL; + } + // Do nothing + break; + + case SENSOR_ENABLE: + if((buff_in == NULL) || (size_in < sizeof(int))) + { + APS_ERR("Enable sensor parameter error!\n"); + err = -EINVAL; + } + else + { + value = *(int *)buff_in; + if(value) + { + if((err = stk3x1x_enable_ps(obj->client, 1, 1))) + { + APS_ERR("enable ps fail: %d\n", err); + return -1; + } + set_bit(STK_BIT_PS, &obj->enable); + } + else + { + if((err = stk3x1x_enable_ps(obj->client, 0, 1))) + { + APS_ERR("disable ps fail: %d\n", err); + return -1; + } + clear_bit(STK_BIT_PS, &obj->enable); + } + } + break; + + case SENSOR_GET_DATA: + if((buff_out == NULL) || (size_out< sizeof(hwm_sensor_data))) + { + APS_ERR("get sensor data parameter error!\n"); + err = -EINVAL; + } + else + { + sensor_data = (hwm_sensor_data *)buff_out; + + if((err = stk3x1x_read_ps(obj->client, &obj->ps))) + { + err = -1; + } + else + { + value = stk3x1x_get_ps_value(obj, obj->ps); + if(value < 0) + { + err = -1; + } + else + { + sensor_data->values[0] = value; + sensor_data->value_divide = 1; + sensor_data->status = SENSOR_STATUS_ACCURACY_MEDIUM; +#ifdef STK_PS_POLLING_LOG + APS_LOG("%s:ps raw 0x%x -> value 0x%x \n",__FUNCTION__, obj->ps, sensor_data->values[0]); +#endif + } + } + } + break; + default: + APS_ERR("proximity sensor operate function no this parameter %d!\n", command); + err = -1; + break; + } + + return err; +} + +int stk3x1x_als_operate(void* self, uint32_t command, void* buff_in, int size_in, + void* buff_out, int size_out, int* actualout) +{ + int err = 0; + int value; + hwm_sensor_data* sensor_data; + struct stk3x1x_priv *obj = (struct stk3x1x_priv *)self; + u8 flag; + + //APS_FUN(f); + switch (command) + { + case SENSOR_DELAY: + if((buff_in == NULL) || (size_in < sizeof(int))) + { + APS_ERR("Set delay parameter error!\n"); + err = -EINVAL; + } + // Do nothing + break; + + case SENSOR_ENABLE: + if((buff_in == NULL) || (size_in < sizeof(int))) + { + APS_ERR("Enable sensor parameter error!\n"); + err = -EINVAL; + } + else + { + value = *(int *)buff_in; + if(value) + { + if((err = stk3x1x_enable_als(obj->client, 1))) + { + APS_ERR("enable als fail: %d\n", err); + return -1; + } + set_bit(STK_BIT_ALS, &obj->enable); + } + else + { + if((err = stk3x1x_enable_als(obj->client, 0))) + { + APS_ERR("disable als fail: %d\n", err); + return -1; + } + clear_bit(STK_BIT_ALS, &obj->enable); + } + + } + break; + + case SENSOR_GET_DATA: + if((buff_out == NULL) || (size_out< sizeof(hwm_sensor_data))) + { + APS_ERR("get sensor data parameter error!\n"); + err = -EINVAL; + } + else + { + sensor_data = (hwm_sensor_data *)buff_out; +#ifdef STK_GES + if(obj->ges_enabled) + { + sensor_data->values[0] = stk3x1x_get_als_value(obj, obj->als_last); + sensor_data->value_divide = 1; + sensor_data->status = SENSOR_STATUS_ACCURACY_MEDIUM; + break; + } +#endif + err = stk3x1x_read_flag(obj->client, &flag); + if(err) + return err; + + if(!(flag & STK_FLG_ALSDR_MASK)) + return -1; + + if((err = stk3x1x_read_als(obj->client, &obj->als))) + { + err = -1; + } + else + { + if(obj->als < 3) + { + obj->als_last = obj->als; + sensor_data->values[0] = stk3x1x_get_als_value(obj, 0); + } + else if(abs(obj->als - obj->als_last) >= STK_ALS_CODE_CHANGE_THD) + { + obj->als_last = obj->als; + sensor_data->values[0] = stk3x1x_get_als_value(obj, obj->als); + } + else + { + sensor_data->values[0] = stk3x1x_get_als_value(obj, obj->als_last); + } + sensor_data->value_divide = 1; + sensor_data->status = SENSOR_STATUS_ACCURACY_MEDIUM; + } + } + break; + default: + APS_ERR("light sensor operate function no this parameter %d!\n", command); + err = -1; + break; + } + + return err; +} +#endif + +/*----------------------------------------------------------------------------*/ +static int stk3x1x_i2c_detect(struct i2c_client *client, struct i2c_board_info *info) +{ + strcpy(info->type, stk3x1x_DEV_NAME); + return 0; +} + +/*----------------------------------------------------------------------------*/ +#ifdef STK_GES +static int stk3x1x_set_input_device(struct stk3x1x_priv *obj) +{ + int err; + + obj->ges_input_dev = input_allocate_device(); + if (obj->ges_input_dev==NULL) + { + APS_ERR( "%s: could not allocate ps device\n", __func__); + err = -ENOMEM; + goto err_ges_input_allocate; + } + obj->ges_input_dev->name = "stk_ges"; + obj->ges_input_dev->evbit[0] = BIT_MASK(EV_KEY); + set_bit(KEY_PAGEUP, obj->ges_input_dev->keybit); + set_bit(KEY_PAGEDOWN, obj->ges_input_dev->keybit); + set_bit(KEY_VOLUMEUP, obj->ges_input_dev->keybit); + set_bit(KEY_VOLUMEDOWN, obj->ges_input_dev->keybit); + /* + set_bit(KEY_LEFT, obj->ges_input_dev->keybit); + set_bit(KEY_RIGHT, obj->ges_input_dev->keybit); + set_bit(KEY_UP, obj->ges_input_dev->keybit); + set_bit(KEY_DOWN, obj->ges_input_dev->keybit); + */ + err = input_register_device(obj->ges_input_dev); + if (err<0) + { + APS_ERR( "%s: can not register ps input device\n", __func__); + return err; + } + + err = sysfs_create_group(&obj->ges_input_dev->dev.kobj, &stk_ges_attribute_group); + if (err < 0) + { + APS_ERR( "%s:could not create sysfs group for ps\n", __func__); + return err; + } + input_set_drvdata(obj->ges_input_dev, obj); + return 0; + +err_ges_input_allocate: + input_free_device(obj->ges_input_dev); + return err; +} +#endif + +static int als_open_report_data(int open) +{ + //should queuq work to report event if is_report_input_direct=true + return 0; +} + +// if use this typ of enable , Gsensor only enabled but not report inputEvent to HAL + +static int als_enable_nodata(int en) +{ + int res = 0; +#ifdef CUSTOM_KERNEL_SENSORHUB + SCP_SENSOR_HUB_DATA req; + int len; +#endif //#ifdef CUSTOM_KERNEL_SENSORHUB + + APS_LOG("stk3x1x_obj als enable value = %d\n", en); + +#ifdef CUSTOM_KERNEL_SENSORHUB + req.activate_req.sensorType = ID_LIGHT; + req.activate_req.action = SENSOR_HUB_ACTIVATE; + req.activate_req.enable = en; + len = sizeof(req.activate_req); + res = SCP_sensorHub_req_send(&req, &len, 1); +#else //#ifdef CUSTOM_KERNEL_SENSORHUB + if(!stk3x1x_obj) + { + APS_ERR("stk3x1x_obj is null!!\n"); + return -1; + } + res = stk3x1x_enable_als(stk3x1x_obj->client, en); +#endif //#ifdef CUSTOM_KERNEL_SENSORHUB + if(res){ + APS_ERR("als_enable_nodata is failed!!\n"); + return -1; + } + return 0; +} + +static int als_set_delay(u64 ns) +{ + return 0; +} + +static int als_get_data(int* value, int* status) +{ + int err = 0; +#ifdef CUSTOM_KERNEL_SENSORHUB + SCP_SENSOR_HUB_DATA req; + int len; +#else + struct stk3x1x_priv *obj = NULL; +#endif //#ifdef CUSTOM_KERNEL_SENSORHUB +#ifdef CUSTOM_KERNEL_SENSORHUB + req.get_data_req.sensorType = ID_LIGHT; + req.get_data_req.action = SENSOR_HUB_GET_DATA; + len = sizeof(req.get_data_req); + err = SCP_sensorHub_req_send(&req, &len, 1); + if (err) + { + APS_ERR("SCP_sensorHub_req_send fail!\n"); + } + else + { + *value = req.get_data_rsp.int16_Data[0]; + *status = SENSOR_STATUS_ACCURACY_MEDIUM; + } + + if(atomic_read(&stk3x1x_obj->trace) & CMC_TRC_PS_DATA) + { + APS_LOG("value = %d\n", *value); + //show data + } +#else //#ifdef CUSTOM_KERNEL_SENSORHUB + if(!stk3x1x_obj) + { + APS_ERR("stk3x1x_obj is null!!\n"); + return -1; + } + obj = stk3x1x_obj; + if((err = stk3x1x_read_als(obj->client, &obj->als))) + { + err = -1; + } + else + { + if(obj->als < 3) + { + obj->als_last = obj->als; + *value = stk3x1x_get_als_value(obj, 0); + } + else if(abs(obj->als - obj->als_last) >= STK_ALS_CODE_CHANGE_THD) + { + obj->als_last = obj->als; + *value = stk3x1x_get_als_value(obj, obj->als); + } + else + { + *value = stk3x1x_get_als_value(obj, obj->als_last); + } + + /* [yanlin add] */ + *value = obj->als; + *status = SENSOR_STATUS_ACCURACY_MEDIUM; + } +#endif //#ifdef CUSTOM_KERNEL_SENSORHUB + + return err; +} + +static int ps_open_report_data(int open) +{ + //should queuq work to report event if is_report_input_direct=true + return 0; +} + +// if use this typ of enable , Gsensor only enabled but not report inputEvent to HAL + +static int ps_enable_nodata(int en) +{ + int res = 0; + //struct stk3x1x_priv *obj = i2c_get_clientdata(client); +#ifdef CUSTOM_KERNEL_SENSORHUB + SCP_SENSOR_HUB_DATA req; + int len; +#endif //#ifdef CUSTOM_KERNEL_SENSORHUB + + APS_LOG("stk3x1x_obj ps enable value = %d\n", en); + +#ifdef CUSTOM_KERNEL_SENSORHUB + req.activate_req.sensorType = ID_PROXIMITY; + req.activate_req.action = SENSOR_HUB_ACTIVATE; + req.activate_req.enable = en; + len = sizeof(req.activate_req); + res = SCP_sensorHub_req_send(&req, &len, 1); +#else //#ifdef CUSTOM_KERNEL_SENSORHUB + if(!stk3x1x_obj) + { + APS_ERR("stk3x1x_obj is null!!\n"); + return -1; + } + res = stk3x1x_enable_ps(stk3x1x_obj->client, en,1); + if(res){ + APS_ERR("als_enable_nodata is failed!!\n"); + return -1; + } +#endif //#ifdef CUSTOM_KERNEL_SENSORHUB + + return 0; + +} + +static int ps_set_delay(u64 ns) +{ + return 0; +} + +static int ps_get_data(int* value, int* status) +{ + int err = 0; +#ifdef CUSTOM_KERNEL_SENSORHUB + SCP_SENSOR_HUB_DATA req; + int len; +#endif //#ifdef CUSTOM_KERNEL_SENSORHUB + +#ifdef CUSTOM_KERNEL_SENSORHUB + req.get_data_req.sensorType = ID_PROXIMITY; + req.get_data_req.action = SENSOR_HUB_GET_DATA; + len = sizeof(req.get_data_req); + err = SCP_sensorHub_req_send(&req, &len, 1); + if (err) + { + APS_ERR("SCP_sensorHub_req_send fail!\n"); + } + else + { + *value = req.get_data_rsp.int16_Data[0]; + *status = SENSOR_STATUS_ACCURACY_MEDIUM; + } + + if(atomic_read(&stk3x1x_obj->trace) & CMC_TRC_PS_DATA) + { + APS_LOG("value = %d\n", *value); + //show data + } +#else //#ifdef CUSTOM_KERNEL_SENSORHUB + if(!stk3x1x_obj) + { + APS_ERR("stk3x1x_obj is null!!\n"); + return -1; + } + + if((err = stk3x1x_read_ps(stk3x1x_obj->client, &stk3x1x_obj->ps))) + { + err = -1;; + } + else + { + *value = stk3x1x_get_ps_value(stk3x1x_obj, stk3x1x_obj->ps); + *status = SENSOR_STATUS_ACCURACY_MEDIUM; + } +#endif //#ifdef CUSTOM_KERNEL_SENSORHUB + + return 0; +} + + +/*----------------------------------------------------------------------------*/ +static int stk3x1x_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct stk3x1x_priv *obj; + //struct hwmsen_object obj_ps, obj_als; + int err = 0; + struct als_control_path als_ctl={0}; + struct als_data_path als_data={0}; + struct ps_control_path ps_ctl={0}; + struct ps_data_path ps_data={0}; + + APS_LOG("%s: driver version: %s\n", __FUNCTION__, DRIVER_VERSION); + +// client->timing = 400; + client->addr = STK3X1X_I2C_ADDR; + if(!(obj = kzalloc(sizeof(*obj), GFP_KERNEL))) + { + err = -ENOMEM; + goto exit; + } + memset(obj, 0, sizeof(*obj)); + stk3x1x_obj = obj; +// obj->hw = get_cust_alsps_hw(); + obj->hw = hw; + stk3x1x_get_addr(obj->hw, &obj->addr); + + INIT_DELAYED_WORK(&obj->eint_work, stk3x1x_eint_work); + printk("client->addr======%x\n",client->addr); + client->addr = 0x48; + printk("client->addr======%x\n",client->addr); + obj->client = client; + i2c_set_clientdata(client, obj); + atomic_set(&obj->als_debounce, 200); + atomic_set(&obj->als_deb_on, 0); + atomic_set(&obj->als_deb_end, 0); + atomic_set(&obj->ps_debounce, 10); + atomic_set(&obj->ps_deb_on, 0); + atomic_set(&obj->ps_deb_end, 0); + atomic_set(&obj->ps_mask, 0); + atomic_set(&obj->trace, 0x00); + atomic_set(&obj->als_suspend, 0); + atomic_set(&obj->init_done, 0); + //obj->irq_node = of_find_matching_node(obj->irq_node, alsps_of_match); + obj->irq_node = of_find_compatible_node(NULL, NULL, "mediatek, als-eint"); + + atomic_set(&obj->state_val, 0); + atomic_set(&obj->psctrl_val, 0x31); + atomic_set(&obj->alsctrl_val, 0x39); + obj->ledctrl_val = 0xFF; + obj->wait_val = 0xF; + obj->int_val = 0; + obj->first_boot = true; + obj->als_correct_factor = 1000; + atomic_set(&obj->ps_high_thd_val, obj->hw->ps_threshold_high);// obj->hw->ps_high_thd_val + atomic_set(&obj->ps_low_thd_val, obj->hw->ps_threshold_low); + atomic_set(&obj->recv_reg, 0); +#ifdef STK_ALS_FIR + atomic_set(&obj->firlength, STK_FIR_LEN); +#endif + +#ifdef STK_TUNE0 + obj->crosstalk = 0; + obj->stk_max_min_diff = STK_MAX_MIN_DIFF; + obj->stk_lt_n_ct = STK_LT_N_CT; + obj->stk_ht_n_ct = STK_HT_N_CT; + #ifdef CALI_EVERY_TIME + obj->ps_high_thd_boot = obj->hw->ps_threshold_high; + obj->ps_low_thd_boot = obj->hw->ps_threshold_low; + if(obj->ps_high_thd_boot <= 0) + { + obj->ps_high_thd_boot = obj->stk_ht_n_ct*3; + obj->ps_low_thd_boot = obj->stk_lt_n_ct*3; + } + #endif +#endif + +#ifdef STK_TUNE1 + obj->hw->polling_mode_ps = 0; + obj->hw->polling_mode_als = 1; + APS_LOG("%s: force PS = interrupt and ALS = polling in tune1 mode\n", __FUNCTION__); +#endif + + if(obj->hw->polling_mode_ps == 0) + { + APS_LOG("%s: enable PS interrupt\n", __FUNCTION__); + } + obj->int_val |= STK_INT_PS_MODE1; + + if(obj->hw->polling_mode_als == 0) + { + obj->int_val |= STK_INT_ALS; + APS_LOG("%s: enable ALS interrupt\n", __FUNCTION__); + } + + APS_LOG("%s: state_val=0x%x, psctrl_val=0x%x, alsctrl_val=0x%x, ledctrl_val=0x%x, wait_val=0x%x, int_val=0x%x\n", + __FUNCTION__, atomic_read(&obj->state_val), atomic_read(&obj->psctrl_val), atomic_read(&obj->alsctrl_val), + obj->ledctrl_val, obj->wait_val, obj->int_val); + + APS_LOG("stk3x1x_i2c_probe() OK!\n"); + obj->enable = 0; + obj->pending_intr = 0; + obj->als_level_num = sizeof(obj->hw->als_level)/sizeof(obj->hw->als_level[0]); + obj->als_value_num = sizeof(obj->hw->als_value)/sizeof(obj->hw->als_value[0]); + BUG_ON(sizeof(obj->als_level) != sizeof(obj->hw->als_level)); + memcpy(obj->als_level, obj->hw->als_level, sizeof(obj->als_level)); + BUG_ON(sizeof(obj->als_value) != sizeof(obj->hw->als_value)); + memcpy(obj->als_value, obj->hw->als_value, sizeof(obj->als_value)); + atomic_set(&obj->i2c_retry, 3); + if(atomic_read(&obj->state_val) & STK_STATE_EN_ALS_MASK) + { + set_bit(STK_BIT_ALS, &obj->enable); + } + + if(atomic_read(&obj->state_val) & STK_STATE_EN_PS_MASK) + { + set_bit(STK_BIT_PS, &obj->enable); + } + + stk3x1x_i2c_client = client; + + obj->stk_ps_tune0_wq = create_singlethread_workqueue("stk_ps_tune0_wq"); + INIT_WORK(&obj->stk_ps_tune0_work, stk_ps_tune0_work_func); + hrtimer_init(&obj->ps_tune0_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); +#ifdef STK_TUNE0 + obj->ps_tune0_delay = ns_to_ktime(60 * NSEC_PER_MSEC); + obj->ps_tune0_timer.function = stk_ps_tune0_timer_func; +#endif + if((err = stk3x1x_init_client(client))) + { + goto exit_init_failed; + } + + if((err = misc_register(&stk3x1x_device))) + { + APS_ERR("stk3x1x_device register failed\n"); + goto exit_misc_device_register_failed; + } + + if((err = stk3x1x_create_attr(&(stk3x1x_init_info.platform_diver_addr->driver)))) + //if((err = stk3x1x_create_attr(&stk3x1x_alsps_driver.driver))) + { + APS_ERR("create attribute err = %d\n", err); + goto exit_create_attr_failed; + } + + /* + obj_ps.self = stk3x1x_obj; + if(1 == obj->hw->polling_mode_ps) + { + obj_ps.polling = 1; + wake_lock_init(&ps_lock,WAKE_LOCK_SUSPEND,"ps wakelock"); + } + else + { + obj_ps.polling = 0;//PS interrupt mode + } + obj_ps.sensor_operate = stk3x1x_ps_operate; + if((err = hwmsen_attach(ID_PROXIMITY, &obj_ps))) + { + APS_ERR("attach fail = %d\n", err); + goto exit_create_attr_failed; + } + + obj_als.self = stk3x1x_obj; + if(1 == obj->hw->polling_mode_als) + { + obj_als.polling = 1; + } + else + { + obj_als.polling = 0;//ALS interrupt mode + } + obj_als.sensor_operate = stk3x1x_als_operate; + if((err = hwmsen_attach(ID_LIGHT, &obj_als))) + { + APS_ERR("attach fail = %d\n", err); + goto exit_create_attr_failed; + }*/ + als_ctl.open_report_data= als_open_report_data; + als_ctl.enable_nodata = als_enable_nodata; + als_ctl.set_delay = als_set_delay; + als_ctl.is_report_input_direct = false; + als_ctl.is_use_common_factory =false; + if(1 == obj->hw->polling_mode_als) + { + als_ctl.is_polling_mode = true; + } + else + { + als_ctl.is_polling_mode = false; + } +#ifdef CUSTOM_KERNEL_SENSORHUB + als_ctl.is_support_batch = obj->hw->is_batch_supported_als; +#else + als_ctl.is_support_batch = false; +#endif + + err = als_register_control_path(&als_ctl); + if(err) + { + APS_ERR("als_control register fail = %d\n", err); + goto exit_sensor_obj_attach_fail; + } + + als_data.get_data = als_get_data; + als_data.vender_div = 100; + err = als_register_data_path(&als_data); + if(err) + { + APS_ERR("als_data register fail = %d\n", err); + goto exit_sensor_obj_attach_fail; + } + + ps_ctl.open_report_data= ps_open_report_data; + ps_ctl.enable_nodata = ps_enable_nodata; + ps_ctl.set_delay = ps_set_delay; + ps_ctl.ps_calibration = NULL; + //int (*ps_calibration)(int type, int value); + ps_ctl.ps_calibration = NULL; + // int (*ps_threshold_setting)(int type, int value[2]); +// ps_ctl.is_report_input_direct = false; + ps_ctl.is_use_common_factory = false; + if(1 == obj->hw->polling_mode_ps) + { + ps_ctl.is_polling_mode = true; + ps_ctl.is_report_input_direct = false; + wake_lock_init(&ps_lock,WAKE_LOCK_SUSPEND,"ps wakelock"); + } + else + { + ps_ctl.is_polling_mode = false; + ps_ctl.is_report_input_direct = true; + } +#ifdef CUSTOM_KERNEL_SENSORHUB + ps_ctl.is_support_batch = obj->hw->is_batch_supported_ps; +#else + ps_ctl.is_support_batch = false; +#endif + + err = ps_register_control_path(&ps_ctl); + if(err) + { + APS_ERR("ps_control register fail = %d\n", err); + goto exit_sensor_obj_attach_fail; + } + + ps_data.get_data = ps_get_data; + ps_data.vender_div = 100; + err = ps_register_data_path(&ps_data); + if(err) + { + APS_ERR("ps_data register fail = %d\n", err); + goto exit_sensor_obj_attach_fail; + } + + err = batch_register_support_info(ID_LIGHT,als_ctl.is_support_batch, 100, 0); + if(err) + { + APS_ERR("register light batch support err = %d\n", err); + } + + err = batch_register_support_info(ID_PROXIMITY,ps_ctl.is_support_batch, 100, 0); + if(err) + { + APS_ERR("register proximity batch support err = %d\n", err); + } + +#ifdef STK_GES + err = stk3x1x_set_input_device(obj); + if(err < 0) + goto exit_set_input_failed; +#endif + +#if defined(CONFIG_HAS_EARLYSUSPEND) + obj->early_drv.level = EARLY_SUSPEND_LEVEL_DISABLE_FB - 1, + obj->early_drv.suspend = stk3x1x_early_suspend, + obj->early_drv.resume = stk3x1x_late_resume, + register_early_suspend(&obj->early_drv); +#endif + + stk3x1x_init_flag = 0; + APS_LOG("%s: OK\n", __FUNCTION__); + + return 0; + +#ifdef STK_GES +exit_set_input_failed: + input_unregister_device(obj->ges_input_dev); +#endif +exit_sensor_obj_attach_fail: +exit_create_attr_failed: + misc_deregister(&stk3x1x_device); +exit_misc_device_register_failed: +exit_init_failed: + //i2c_detach_client(client); +// exit_kfree: +exit: + stk3x1x_i2c_client = NULL; + #ifdef MT6516 + MT6516_EINTIRQMask(CUST_EINT_ALS_NUM); /*mask interrupt if fail*/ + #endif + stk3x1x_init_flag = -1; + APS_ERR("%s: err = %d\n", __FUNCTION__, err); + return err; +} +/*----------------------------------------------------------------------------*/ +static int stk3x1x_i2c_remove(struct i2c_client *client) +{ + int err; + struct stk3x1x_priv *obj = i2c_get_clientdata(client); + +#ifdef STK_TUNE0 + destroy_workqueue(obj->stk_ps_tune0_wq); +#endif +#ifdef STK_GES + sysfs_remove_group(&obj->ges_input_dev->dev.kobj, &stk_ges_attribute_group); + input_unregister_device(obj->ges_input_dev); +#endif + //if((err = stk3x1x_delete_attr(&stk3x1x_i2c_driver.driver))) + if((err = stk3x1x_delete_attr(&(stk3x1x_init_info.platform_diver_addr->driver)))) + { + APS_ERR("stk3x1x_delete_attr fail: %d\n", err); + } + +/* if((err = misc_deregister(&stk3x1x_device))) + { + APS_ERR("misc_deregister fail: %d\n", err); + } +*/ + stk3x1x_i2c_client = NULL; + i2c_unregister_device(client); + kfree(i2c_get_clientdata(client)); + + return 0; +} + +static int stk3x1x_local_uninit(void) +{ + APS_FUN(); + stk3x1x_power(hw, 0); + i2c_del_driver(&stk3x1x_i2c_driver); + stk3x1x_i2c_client = NULL; + return 0; +} + +/*----------------------------------------------------------------------------*/ + +static int stk3x1x_local_init(void) +{ + struct stk3x1x_i2c_addr addr; + + APS_FUN(); + stk3x1x_power(hw, 1); + stk3x1x_get_addr(hw, &addr); + if(i2c_add_driver(&stk3x1x_i2c_driver)) + { + APS_ERR("add driver error\n"); + return -1; + } + + if(-1 == stk3x1x_init_flag) + { + APS_ERR("stk3x1x_local_init fail with stk3x1x_init_flag=%d\n", stk3x1x_init_flag); + return -1; + } + return 0; +} + +/*----------------------------------------------------------------------------*/ +static int __init stk3x1x_init(void) +{ + const char *name = "mediatek,stk3x1x"; // "mediatek,arg_stk3x1x"; + + APS_FUN(); + printk("stk3x1x_init In"); + + hw = get_alsps_dts_func(name, hw); + if (!hw) + { + APS_ERR("get dts info fail\n"); + //hw = get_cust_alsps_hw(); + } + alsps_driver_add(&stk3x1x_init_info);// hwmsen_alsps_add(&stk3x1x_init_info); + +#if 0 + stk3x1x_regulator = regulator_get(&alspsPltFmDev->dev,"vstk3x1x"); + if(!stk3x1x_regulator) + printk("stk3x1x_regulator is NULL\n"); + ret = regulator_set_voltage(stk3x1x_regulator, 2800000, 2800000); /*set 2.8v*/ + if (ret){ + printk("regulator_set_voltage() failed!\n"); + } + msleep(3); +#endif + printk("stk3x1x_init Out"); + + return 0; +} +/*----------------------------------------------------------------------------*/ +static void __exit stk3x1x_exit(void) +{ + APS_FUN(); +} +/*----------------------------------------------------------------------------*/ +module_init(stk3x1x_init); +module_exit(stk3x1x_exit); +/*----------------------------------------------------------------------------*/ +MODULE_AUTHOR("MingHsien Hsieh"); +MODULE_DESCRIPTION("SensorTek stk3x1x proximity and light sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/mediatek/alsps/stk3x1x-new/stk3x1x.h b/drivers/misc/mediatek/alsps/stk3x1x-new/stk3x1x.h index 9dcc7843c..0abf6b82a 100755 --- a/drivers/misc/mediatek/alsps/stk3x1x-new/stk3x1x.h +++ b/drivers/misc/mediatek/alsps/stk3x1x-new/stk3x1x.h @@ -46,6 +46,9 @@ #define STK_RSRVD_REG 0x3F #define STK_SW_RESET_REG 0x80 +#define STK_GSCTRL_REG 0x1A +#define STK_FLAG2_REG 0x1C + /* Define state reg */ #define STK_STATE_EN_IRS_SHIFT 7 #define STK_STATE_EN_AK_SHIFT 6 @@ -120,29 +123,20 @@ #define STK_FLG_IR_RDY_MASK 0x02 #define STK_FLG_NF_MASK 0x01 +/* Define flag2 reg */ +#define STK_FLG2_INT_GS_SHIFT 6 +#define STK_FLG2_GS10_SHIFT 5 +#define STK_FLG2_GS01_SHIFT 4 + +#define STK_FLG2_INT_GS_MASK 0x40 +#define STK_FLG2_GS10_MASK 0x20 +#define STK_FLG2_GS01_MASK 0x10 + /* misc define */ #define ALS_MIN_DELAY 100 #define PS_MIN_DELAY 10 -#define STK_ALS_CODE_CHANGE_THD 5 - -#if defined(CONFIG_CUSTOM_KERNEL_SENSORHUB) -/*----------------------------------------------------------------------------*/ -enum STK3X1X_NOTIFY_TYPE { - STK3X1X_NOTIFY_PROXIMITY_CHANGE = 1, - STK3X1X_NOTIFY_ALS_RAW_DATA, - STK3X1X_NOTIFY_PS_RAW_DATA, - STK3X1X_NOTIFY_PROXIMITY_NOT_CHANGE -}; -/*----------------------------------------------------------------------------*/ -enum STK3X1X_CUST_ACTION { - STK3X1X_CUST_ACTION_SET_CUST = 1, - STK3X1X_CUST_ACTION_CLR_CALI, - STK3X1X_CUST_ACTION_SET_CALI, - STK3X1X_CUST_ACTION_SET_PS_THRESHODL, - STK3X1X_CUST_ACTION_SET_EINT_INFO, - STK3X1X_CUST_ACTION_GET_ALS_RAW_DATA, - STK3X1X_CUST_ACTION_GET_PS_RAW_DATA, -}; -#endif +#define STK_ALS_CODE_CHANGE_THD 10 + +extern struct platform_device *get_alsps_platformdev(void); #endif diff --git a/kernel/Kconfig.locks b/kernel/Kconfig.locks index ebdb00432..3cc631447 100644 --- a/kernel/Kconfig.locks +++ b/kernel/Kconfig.locks @@ -224,7 +224,7 @@ config ARCH_SUPPORTS_ATOMIC_RMW bool config MUTEX_SPIN_ON_OWNER - def_bool y + def_bool n depends on SMP && !DEBUG_MUTEXES && ARCH_SUPPORTS_ATOMIC_RMW config RWSEM_SPIN_ON_OWNER