Initial commit: Linux kernel 4.19 for Unplugged OS

This commit is contained in:
ziv committed 2025-10-06 13:52:42 +00:00
commit d30235b04c
80519 files changed
+35800681

No files matched your search

+309
View File
@@ -0,0 +1,309 @@
/*
* Touchscreen driver for Marvell 88PM860x
*
* Copyright (C) 2009 Marvell International Ltd.
* Haojian Zhuang <haojian.zhuang@marvell.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/mfd/88pm860x.h>
#include <linux/slab.h>
#include <linux/device.h>
#define MEAS_LEN (8)
#define ACCURATE_BIT (12)
/* touch register */
#define MEAS_EN3 (0x52)
#define MEAS_TSIX_1 (0x8D)
#define MEAS_TSIX_2 (0x8E)
#define MEAS_TSIY_1 (0x8F)
#define MEAS_TSIY_2 (0x90)
#define MEAS_TSIZ1_1 (0x91)
#define MEAS_TSIZ1_2 (0x92)
#define MEAS_TSIZ2_1 (0x93)
#define MEAS_TSIZ2_2 (0x94)
/* bit definitions of touch */
#define MEAS_PD_EN (1 << 3)
#define MEAS_TSIX_EN (1 << 4)
#define MEAS_TSIY_EN (1 << 5)
#define MEAS_TSIZ1_EN (1 << 6)
#define MEAS_TSIZ2_EN (1 << 7)
struct pm860x_touch {
struct input_dev *idev;
struct i2c_client *i2c;
struct pm860x_chip *chip;
int irq;
int res_x; /* resistor of Xplate */
};
static irqreturn_t pm860x_touch_handler(int irq, void *data)
{
struct pm860x_touch *touch = data;
struct pm860x_chip *chip = touch->chip;
unsigned char buf[MEAS_LEN];
int x, y, pen_down;
int z1, z2, rt = 0;
int ret;
ret = pm860x_bulk_read(touch->i2c, MEAS_TSIX_1, MEAS_LEN, buf);
if (ret < 0)
goto out;
pen_down = buf[1] & (1 << 6);
x = ((buf[0] & 0xFF) << 4) | (buf[1] & 0x0F);
y = ((buf[2] & 0xFF) << 4) | (buf[3] & 0x0F);
z1 = ((buf[4] & 0xFF) << 4) | (buf[5] & 0x0F);
z2 = ((buf[6] & 0xFF) << 4) | (buf[7] & 0x0F);
if (pen_down) {
if ((x != 0) && (z1 != 0) && (touch->res_x != 0)) {
rt = z2 / z1 - 1;
rt = (rt * touch->res_x * x) >> ACCURATE_BIT;
dev_dbg(chip->dev, "z1:%d, z2:%d, rt:%d\n",
z1, z2, rt);
}
input_report_abs(touch->idev, ABS_X, x);
input_report_abs(touch->idev, ABS_Y, y);
input_report_abs(touch->idev, ABS_PRESSURE, rt);
input_report_key(touch->idev, BTN_TOUCH, 1);
dev_dbg(chip->dev, "pen down at [%d, %d].\n", x, y);
} else {
input_report_abs(touch->idev, ABS_PRESSURE, 0);
input_report_key(touch->idev, BTN_TOUCH, 0);
dev_dbg(chip->dev, "pen release\n");
}
input_sync(touch->idev);
out:
return IRQ_HANDLED;
}
static int pm860x_touch_open(struct input_dev *dev)
{
struct pm860x_touch *touch = input_get_drvdata(dev);
int data, ret;
data = MEAS_PD_EN | MEAS_TSIX_EN | MEAS_TSIY_EN
| MEAS_TSIZ1_EN | MEAS_TSIZ2_EN;
ret = pm860x_set_bits(touch->i2c, MEAS_EN3, data, data);
if (ret < 0)
goto out;
return 0;
out:
return ret;
}
static void pm860x_touch_close(struct input_dev *dev)
{
struct pm860x_touch *touch = input_get_drvdata(dev);
int data;
data = MEAS_PD_EN | MEAS_TSIX_EN | MEAS_TSIY_EN
| MEAS_TSIZ1_EN | MEAS_TSIZ2_EN;
pm860x_set_bits(touch->i2c, MEAS_EN3, data, 0);
}
#ifdef CONFIG_OF
static int pm860x_touch_dt_init(struct platform_device *pdev,
struct pm860x_chip *chip,
int *res_x)
{
struct device_node *np = pdev->dev.parent->of_node;
struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
: chip->companion;
int data, n, ret;
if (!np)
return -ENODEV;
np = of_get_child_by_name(np, "touch");
if (!np) {
dev_err(&pdev->dev, "Can't find touch node\n");
return -EINVAL;
}
/* set GPADC MISC1 register */
data = 0;
if (!of_property_read_u32(np, "marvell,88pm860x-gpadc-prebias", &n))
data |= (n << 1) & PM8607_GPADC_PREBIAS_MASK;
if (!of_property_read_u32(np, "marvell,88pm860x-gpadc-slot-cycle", &n))
data |= (n << 3) & PM8607_GPADC_SLOT_CYCLE_MASK;
if (!of_property_read_u32(np, "marvell,88pm860x-gpadc-off-scale", &n))
data |= (n << 5) & PM8607_GPADC_OFF_SCALE_MASK;
if (!of_property_read_u32(np, "marvell,88pm860x-gpadc-sw-cal", &n))
data |= (n << 7) & PM8607_GPADC_SW_CAL_MASK;
if (data) {
ret = pm860x_reg_write(i2c, PM8607_GPADC_MISC1, data);
if (ret < 0)
goto err_put_node;
}
/* set tsi prebias time */
if (!of_property_read_u32(np, "marvell,88pm860x-tsi-prebias", &data)) {
ret = pm860x_reg_write(i2c, PM8607_TSI_PREBIAS, data);
if (ret < 0)
goto err_put_node;
}
/* set prebias & prechg time of pen detect */
data = 0;
if (!of_property_read_u32(np, "marvell,88pm860x-pen-prebias", &n))
data |= n & PM8607_PD_PREBIAS_MASK;
if (!of_property_read_u32(np, "marvell,88pm860x-pen-prechg", &n))
data |= n & PM8607_PD_PRECHG_MASK;
if (data) {
ret = pm860x_reg_write(i2c, PM8607_PD_PREBIAS, data);
if (ret < 0)
goto err_put_node;
}
of_property_read_u32(np, "marvell,88pm860x-resistor-X", res_x);
of_node_put(np);
return 0;
err_put_node:
of_node_put(np);
return -EINVAL;
}
#else
#define pm860x_touch_dt_init(x, y, z) (-1)
#endif
static int pm860x_touch_probe(struct platform_device *pdev)
{
struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent);
struct pm860x_touch_pdata *pdata = dev_get_platdata(&pdev->dev);
struct pm860x_touch *touch;
struct i2c_client *i2c = (chip->id == CHIP_PM8607) ? chip->client \
: chip->companion;
int irq, ret, res_x = 0, data = 0;
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "No IRQ resource!\n");
return -EINVAL;
}
if (pm860x_touch_dt_init(pdev, chip, &res_x)) {
if (pdata) {
/* set GPADC MISC1 register */
data = 0;
data |= (pdata->gpadc_prebias << 1)
& PM8607_GPADC_PREBIAS_MASK;
data |= (pdata->slot_cycle << 3)
& PM8607_GPADC_SLOT_CYCLE_MASK;
data |= (pdata->off_scale << 5)
& PM8607_GPADC_OFF_SCALE_MASK;
data |= (pdata->sw_cal << 7)
& PM8607_GPADC_SW_CAL_MASK;
if (data) {
ret = pm860x_reg_write(i2c,
PM8607_GPADC_MISC1, data);
if (ret < 0)
return -EINVAL;
}
/* set tsi prebias time */
if (pdata->tsi_prebias) {
data = pdata->tsi_prebias;
ret = pm860x_reg_write(i2c,
PM8607_TSI_PREBIAS, data);
if (ret < 0)
return -EINVAL;
}
/* set prebias & prechg time of pen detect */
data = 0;
data |= pdata->pen_prebias
& PM8607_PD_PREBIAS_MASK;
data |= (pdata->pen_prechg << 5)
& PM8607_PD_PRECHG_MASK;
if (data) {
ret = pm860x_reg_write(i2c,
PM8607_PD_PREBIAS, data);
if (ret < 0)
return -EINVAL;
}
res_x = pdata->res_x;
} else {
dev_err(&pdev->dev, "failed to get platform data\n");
return -EINVAL;
}
}
/* enable GPADC */
ret = pm860x_set_bits(i2c, PM8607_GPADC_MISC1, PM8607_GPADC_EN,
PM8607_GPADC_EN);
if (ret)
return ret;
touch = devm_kzalloc(&pdev->dev, sizeof(struct pm860x_touch),
GFP_KERNEL);
if (!touch)
return -ENOMEM;
touch->idev = devm_input_allocate_device(&pdev->dev);
if (!touch->idev) {
dev_err(&pdev->dev, "Failed to allocate input device!\n");
return -ENOMEM;
}
touch->idev->name = "88pm860x-touch";
touch->idev->phys = "88pm860x/input0";
touch->idev->id.bustype = BUS_I2C;
touch->idev->dev.parent = &pdev->dev;
touch->idev->open = pm860x_touch_open;
touch->idev->close = pm860x_touch_close;
touch->chip = chip;
touch->i2c = i2c;
touch->irq = irq;
touch->res_x = res_x;
input_set_drvdata(touch->idev, touch);
ret = devm_request_threaded_irq(&pdev->dev, touch->irq, NULL,
pm860x_touch_handler, IRQF_ONESHOT,
"touch", touch);
if (ret < 0)
return ret;
__set_bit(EV_ABS, touch->idev->evbit);
__set_bit(ABS_X, touch->idev->absbit);
__set_bit(ABS_Y, touch->idev->absbit);
__set_bit(ABS_PRESSURE, touch->idev->absbit);
__set_bit(EV_SYN, touch->idev->evbit);
__set_bit(EV_KEY, touch->idev->evbit);
__set_bit(BTN_TOUCH, touch->idev->keybit);
input_set_abs_params(touch->idev, ABS_X, 0, 1 << ACCURATE_BIT, 0, 0);
input_set_abs_params(touch->idev, ABS_Y, 0, 1 << ACCURATE_BIT, 0, 0);
input_set_abs_params(touch->idev, ABS_PRESSURE, 0, 1 << ACCURATE_BIT,
0, 0);
ret = input_register_device(touch->idev);
if (ret < 0) {
dev_err(chip->dev, "Failed to register touch!\n");
return ret;
}
return 0;
}
static struct platform_driver pm860x_touch_driver = {
.driver = {
.name = "88pm860x-touch",
},
.probe = pm860x_touch_probe,
};
module_platform_driver(pm860x_touch_driver);
MODULE_DESCRIPTION("Touchscreen driver for Marvell Semiconductor 88PM860x");
MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:88pm860x-touch");
+24
View File
@@ -0,0 +1,24 @@
# Makefile for the focaltech touchscreen drivers.
ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/mt-plat/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/
ccflags-y += -I$(srctree)/drivers/base
ccflags-y += -I$(srctree)/drivers/input/touchscreen/FT3518/
obj-y += focaltech_core.o
obj-y += focaltech_ex_fun.o
obj-y += focaltech_ex_mode.o
obj-y += focaltech_gesture.o
obj-y += focaltech_esdcheck.o
obj-y += focaltech_point_report_check.o
obj-y += focaltech_proximity.o
obj-y += focaltech_flash/
obj-y += focaltech_i2c.o
obj-y += focaltech_flash.o
obj-y += tpd_control.o
obj-y += tpd_button.o
obj-y += tpd_debug.o
obj-y += tpd_default.o
obj-y += tpd_misc.o
obj-y += tpd_setting.o
@@ -0,0 +1,159 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2016 MediaTek Inc.
*/
#ifndef __LINUX_FOCALTECH_COMMON_H__
#define __LINUX_FOCALTECH_COMMON_H__
#include <linux/types.h>
#include "focaltech_config.h"
#define FTS_DRIVER_VERSION "Focaltech V3.0 20190102"
#define BYTE_OFF_0(x) (u8)((x)&0xFF)
#define BYTE_OFF_8(x) (u8)((x >> 8) & 0xFF)
#define BYTE_OFF_16(x) (u8)((x >> 16) & 0xFF)
#define BYTE_OFF_24(x) (u8)((x >> 24) & 0xFF)
#define FLAGBIT(x) (0x00000001 << (x))
#define FLAGBITS(x, y) ((0xFFFFFFFF >> (32 - (y)-1)) & (0xFFFFFFFF << (x)))
#define FLAG_ICSERIALS_LEN 8
#define FLAG_HID_BIT 10
#define FLAG_IDC_BIT 11
#define IC_SERIALS (FTS_CHIP_TYPE & FLAGBITS(0, FLAG_ICSERIALS_LEN - 1))
#define IC_TO_SERIALS(x) ((x)&FLAGBITS(0, FLAG_ICSERIALS_LEN - 1))
#define FTS_CHIP_IDC \
((FTS_CHIP_TYPE & FLAGBIT(FLAG_IDC_BIT)) == FLAGBIT(FLAG_IDC_BIT))
#define FTS_HID_SUPPORTTED \
((FTS_CHIP_TYPE & FLAGBIT(FLAG_HID_BIT)) == FLAGBIT(FLAG_HID_BIT))
#define FTS_CHIP_TYPE_MAPPING \
{ \
{0x01, 0x58, 0x22, 0x58, 0x22, 0x00, 0x00, 0x58, 0x2C}, \
{0x02, 0x54, 0x22, 0x54, 0x22, 0x00, 0x00, 0x54, 0x2C}, \
{0x03, 0x64, 0x26, 0x64, 0x26, 0x00, 0x00, 0x79, 0x1C}, \
{0x04, 0x33, 0x67, 0x64, 0x26, 0x00, 0x00, 0x79, 0x1C}, \
{0x05, 0x87, 0x16, 0x87, 0x16, 0x87, 0xA6, 0x00, 0x00}, \
{0x06, 0x87, 0x36, 0x87, 0x36, 0x87, 0xC6, 0x00, 0x00}, \
{0x07, 0x80, 0x06, 0x80, 0x06, 0x80, 0xC6, 0x80, 0xB6}, \
{0x09, 0x86, 0x07, 0x86, 0x07, 0x86, 0xA7, 0x00, 0x00}, \
{0x0B, 0xF0, 0x06, 0xF0, 0x06, 0xF0, 0xA6, 0x00, 0x00}, \
{0x0C, 0x86, 0x13, 0x87, 0x16, 0x87, 0xA6, 0x00, 0x00}, \
{0x0D, 0x87, 0x19, 0x87, 0x19, 0x87, 0xA9, 0x87, 0xB9}, \
{0x0E, 0x87, 0x39, 0x87, 0x39, 0x87, 0xC9, 0x87, 0xD9}, \
{0x0F, 0x86, 0x15, 0x87, 0x19, 0x87, 0xA9, 0x87, 0xB9}, \
{0x10, 0x82, 0x01, 0x80, 0x06, 0x80, 0xC6, 0x80, 0xB6}, \
{0x11, 0x86, 0x22, 0x86, 0x22, 0x86, 0xA2, 0x00, 0x00}, \
{0x12, 0x72, 0x51, 0x72, 0x51, 0x72, 0xA1, 0x00, 0x00}, \
{0x13, 0x72, 0x52, 0x72, 0x51, 0x72, 0xA1, 0x00, 0x00}, \
{0x14, 0xF6, 0x13, 0x86, 0x13, 0x86, 0xA3, 0x00, 0x00}, \
{0x81, 0x54, 0x52, 0x54, 0x52, 0x00, 0x00, 0x54, 0x5C}, \
{0x82, 0x54, 0x22, 0x54, 0x22, 0x54, 0x2D, 0x54, 0x2E}, \
{0x83, 0x54, 0x56, 0x00, 0x00, 0x00, 0x00, 0x54, 0xA2}, \
{0x84, 0x62, 0x16, 0x62, 0x16, 0x62, 0x1A, 0x00, 0x00}, \
}
#define FILE_NAME_LENGTH 128
#define ENABLE 1
#define DISABLE 0
#define VALID 1
#define INVALID 0
#define FTS_CMD_START1 0x55
#define FTS_CMD_START2 0xAA
#define FTS_CMD_START_DELAY 10
#define FTS_CMD_READ_ID 0x90
#define FTS_CMD_READ_ID_LEN 4
#define FTS_CMD_READ_ID_LEN_INCELL 1
/*register address*/
#define FTS_REG_INT_CNT 0x8F
#define FTS_REG_FLOW_WORK_CNT 0x91
#define FTS_REG_WORKMODE 0x00
#define FTS_REG_WORKMODE_FACTORY_VALUE 0x40
#define FTS_REG_WORKMODE_WORK_VALUE 0x00
#define FTS_REG_ESDCHECK_DISABLE 0x8D
#define FTS_REG_CHIP_ID 0xA3
#define FTS_REG_CHIP_ID2 0x9F
#define FTS_REG_POWER_MODE 0xA5
#define FTS_REG_POWER_MODE_SLEEP_VALUE 0x03
#define FTS_REG_FW_VER 0xA6
#define FTS_REG_VENDOR_ID 0xA8
#define FTS_REG_LCD_BUSY_NUM 0xAB
#define FTS_REG_FACE_DEC_MODE_EN 0xB0
#define FTS_REG_FACTORY_MODE_DETACH_FLAG 0xB4
#define FTS_REG_FACE_DEC_MODE_STATUS 0x01
#define FTS_REG_IDE_PARA_VER_ID 0xB5
#define FTS_REG_IDE_PARA_STATUS 0xB6
#define FTS_REG_GLOVE_MODE_EN 0xC0
#define FTS_REG_COVER_MODE_EN 0xC1
#define FTS_REG_CHARGER_MODE_EN 0x8B
#define FTS_REG_GESTURE_EN 0xD0
#define FTS_REG_GESTURE_OUTPUT_ADDRESS 0xD3
#define FTS_REG_MODULE_ID 0xE3
#define FTS_REG_LIC_VER 0xE4
#define FTS_REG_ESD_SATURATE 0xED
#define FTS_SYSFS_ECHO_ON(buf) (buf[0] == '1')
#define FTS_SYSFS_ECHO_OFF(buf) (buf[0] == '0')
#define kfree_safe(pbuf) \
do { \
kfree(pbuf); \
pbuf = NULL; \
} while (0)
/*
* point report check
* default: disable
*/
#define FTS_POINT_REPORT_CHECK_EN 0
struct ft_chip_t {
u64 type;
u8 chip_idh;
u8 chip_idl;
u8 rom_idh;
u8 rom_idl;
u8 pb_idh;
u8 pb_idl;
u8 bl_idh;
u8 bl_idl;
};
struct ts_ic_info {
bool is_incell;
bool hid_supported;
struct ft_chip_t ids;
};
#if FTS_DEBUG_EN
#define FTS_DEBUG(fmt, args...) \
pr_debug("[FTS_TS]%s:" fmt "\n", __func__, ##args)
#define FTS_FUNC_ENTER() \
pr_info("[FTS_TS]%s: Enter\n", __func__)
#define FTS_FUNC_EXIT() \
pr_info("[FTS_TS]%s: Exit(%d)\n", __func__, __LINE__)
#else /* #if FTS_DEBUG_EN*/
#define FTS_DEBUG(fmt, args...)
#define FTS_FUNC_ENTER()
#define FTS_FUNC_EXIT()
#endif
#define FTS_INFO(fmt, args...) \
pr_info("[FTS_TS/I]%s:" fmt "\n", __func__, ##args)
#define FTS_ERROR(fmt, args...) \
pr_info("[FTS_TS/E]%s:" fmt "\n", __func__, ##args)
#endif /* __LINUX_FOCALTECH_COMMON_H__ */
@@ -0,0 +1,213 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2016 MediaTek Inc.
*/
#ifndef _LINUX_FOCLATECH_CONFIG_H_
#define _LINUX_FOCLATECH_CONFIG_H_
/**************************************************/
/****** G: A, I: B, S: C, U: D ******************/
/****** chip type defines, do not modify *********/
#define _FT8716 0x87160805
#define _FT8736 0x87360806
#define _FT8006M 0x80060807
#define _FT7250 0x72500807
#define _FT8607 0x86070809
#define _FT8006U 0x8006D80B
#define _FT8006S 0x8006A80B
#define _FT8613 0x8613080C
#define _FT8719 0x8719080D
#define _FT8739 0x8739080E
#define _FT8615 0x8615080F
#define _FT8201 0x82010810
#define _FT8006P 0x86220811
#define _FT7251 0x72510812
#define _FT7252 0x72520813
#define _FT8613S 0x8613C814
#define _FT5416 0x54160402
#define _FT5426 0x54260402
#define _FT5435 0x54350402
#define _FT5436 0x54360402
#define _FT5526 0x55260402
#define _FT5526I 0x5526B402
#define _FT5446 0x54460402
#define _FT5346 0x53460402
#define _FT5446I 0x5446B402
#define _FT5346I 0x5346B402
#define _FT7661 0x76610402
#define _FT7511 0x75110402
#define _FT7421 0x74210402
#define _FT7681 0x76810402
#define _FT3C47U 0x3C47D402
#define _FT3417 0x34170402
#define _FT3517 0x35170402
#define _FT3327 0x33270402
#define _FT3427 0x34270402
#define _FT7311 0x73110402
#define _FT5626 0x56260401
#define _FT5726 0x57260401
#define _FT5826B 0x5826B401
#define _FT5826S 0x5826C401
#define _FT7811 0x78110401
#define _FT3D47 0x3D470401
#define _FT3617 0x36170401
#define _FT3717 0x37170401
#define _FT3817B 0x3817B401
#define _FT3517U 0x3517D401
#define _FT6236U 0x6236D003
#define _FT6336G 0x6336A003
#define _FT6336U 0x6336D003
#define _FT6436U 0x6436D003
#define _FT3267 0x32670004
#define _FT3367 0x33670004
#define _FT3327DQQ_XXX 0x3327D482
#define _FT5446DQS_XXX 0x5446D482
#define _FT3518 0x35180481
#define _FT3558 0x35580481
#define _FT3528 0x35280481
#define _FT5536 0x55360481
#define _FT5446U 0x5446D083
#define _FT5456U 0x5456D083
#define _FT3417U 0x3417D083
#define _FT5426U 0x5426D083
#define _FT3428 0x34280083
#define _FT3437U 0x3437D083
#define _FT7302 0x73020084
#define _FT7202 0x72020084
#define _FT3308 0x33080084
/*************************************************/
/*
* choose your ic chip type of focaltech
*/
#define FTS_CHIP_TYPE _FT3518
/******************* Enables *********************/
/*********** 1 to enable, 0 to disable ***********/
/*
* show debug log info
* enable it for debug, disable it for release
*/
#define FTS_DEBUG_EN 1
/*
* Linux MultiTouch Protocol
* 1: Protocol B(default), 0: Protocol A
*/
#define FTS_MT_PROTOCOL_B_EN 1
/*
* Report Pressure in multitouch
* 1:enable(default),0:disable
*/
#define FTS_REPORT_PRESSURE_EN 1
/*
* Gesture function enable
* default: disable
*/
#define FTS_GESTURE_EN 0
/*
* ESD check & protection
* default: disable
*/
#define FTS_ESDCHECK_EN 0
/*
* Production test enable
* 1: enable, 0:disable(default)
*/
#define FTS_TEST_EN 0
/*
* Proximity sensor
* default: disable
*/
#define FTS_PSENSOR_EN 0
/*
* Nodes for tools, please keep enable
*/
#define FTS_SYSFS_NODE_EN 1
#define FTS_APK_NODE_EN 1
/*
* Customer power enable
* enable it when customer need control TP power
* default: disable
*/
#define FTS_POWER_SOURCE_CUST_EN 1
/****************************************************/
/********************** Upgrade ****************************/
/*
* auto upgrade
*/
#define FTS_AUTO_UPGRADE_EN 0
/*
* auto upgrade for lcd cfg
*/
#define FTS_AUTO_LIC_UPGRADE_EN 0
/*
* Numbers of modules support
*/
#define FTS_GET_MODULE_NUM 0
/*
* module_id: mean vendor_id generally, also maybe gpio or lcm_id...
* If means vendor_id, the FTS_MODULE_ID = PANEL_ID << 8 + VENDOR_ID
* FTS_GET_MODULE_NUM == 0/1, no check module id, you may ignore them
* FTS_GET_MODULE_NUM >= 2, compatible with FTS_MODULE2_ID
* FTS_GET_MODULE_NUM >= 3, compatible with FTS_MODULE3_ID
*/
#define FTS_MODULE_ID 0x0000
#define FTS_MODULE2_ID 0x0000
#define FTS_MODULE3_ID 0x0000
/*
* Need set the following when get firmware via firmware_request()
* For example: if module'vendor is tianma,
* #define FTS_MODULE_NAME "tianma"
* then file_name will be "focaltech_ts_fw_tianma"
* You should rename fw to "focaltech_ts_fw_tianma", and push it into
* etc/firmware or by customers
*/
#define FTS_MODULE_NAME ""
#define FTS_MODULE2_NAME ""
#define FTS_MODULE3_NAME ""
/*
* FW.i file for auto upgrade, you must replace it with your own
* define your own fw_file, the sample one to be replaced is invalid
* NOTE: if FTS_GET_MODULE_NUM > 1, it's the fw corresponding with FTS_VENDOR_ID
*/
#define FTS_UPGRADE_FW_FILE "include/firmware/fw_ft3518_j7.i"
/*
* if FTS_GET_MODULE_NUM >= 2, fw corrsponding with FTS_VENDOR_ID2
* define your own fw_file, the sample one is invalid
*/
#define FTS_UPGRADE_FW2_FILE "include/firmware/fw_ft3518_j7.i"
/*
* if FTS_GET_MODULE_NUM >= 3, fw corrsponding with FTS_VENDOR_ID3
* define your own fw_file, the sample one is invalid
*/
#define FTS_UPGRADE_FW3_FILE "include/firmware/fw_ft3518_j7.i"
/*********************************************************/
#endif /* _LINUX_FOCLATECH_CONFIG_H_ */
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,250 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2016 MediaTek Inc.
*/
#ifndef __LINUX_FOCALTECH_CORE_H__
#define __LINUX_FOCALTECH_CORE_H__
/*****************************************************************************
* Included header files
*****************************************************************************/
#include "focaltech_common.h"
#include <linux/uaccess.h>
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/firmware.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/mutex.h>
#include <linux/proc_fs.h>
#include <linux/regulator/consumer.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
#include <linux/time.h>
#include <linux/types.h>
#include <linux/version.h>
#include <linux/vmalloc.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
/*****************************************************************************
* Private constant and macro definitions using #define
*****************************************************************************/
#define FTS_MAX_POINTS_SUPPORT 10 /* constant value, can't be changed */
#define FTS_MAX_KEYS 4
#define FTS_KEY_DIM 10
#define FTS_ONE_TCH_LEN 6
#define FTS_TOUCH_DATA_LEN (FTS_MAX_POINTS_SUPPORT * FTS_ONE_TCH_LEN + 3)
#if (FTS_CHIP_TYPE == _FT8006P)
#define FTS_SPI_CLK_MAX 6000000
#else
#define FTS_SPI_CLK_MAX 10000000
#endif
#define FTS_GESTURE_POINTS_MAX 6
#define FTS_GESTURE_DATA_LEN (FTS_GESTURE_POINTS_MAX * 4 + 4)
#define FTS_MAX_ID 0x0A
#define FTS_TOUCH_X_H_POS 3
#define FTS_TOUCH_X_L_POS 4
#define FTS_TOUCH_Y_H_POS 5
#define FTS_TOUCH_Y_L_POS 6
#define FTS_TOUCH_PRE_POS 7
#define FTS_TOUCH_AREA_POS 8
#define FTS_TOUCH_POINT_NUM 2
#define FTS_TOUCH_EVENT_POS 3
#define FTS_TOUCH_ID_POS 5
#define FTS_COORDS_ARR_SIZE 4
#define FTS_X_MIN_DISPLAY_DEFAULT 0
#define FTS_Y_MIN_DISPLAY_DEFAULT 0
#define FTS_X_MAX_DISPLAY_DEFAULT 720
#define FTS_Y_MAX_DISPLAY_DEFAULT 1280
#define FTS_TOUCH_DOWN 0
#define FTS_TOUCH_UP 1
#define FTS_TOUCH_CONTACT 2
#define EVENT_DOWN(flag) \
((flag == FTS_TOUCH_DOWN) || (flag == FTS_TOUCH_CONTACT))
#define EVENT_UP(flag) (flag == FTS_TOUCH_UP)
#define EVENT_NO_DOWN(data) (!data->point_num)
#define FTX_MAX_COMPATIBLE_TYPE 4
#define FTX_MAX_COMMMAND_LENGTH 16
/*****************************************************************************
* Private enumerations, structures and unions using typedef
*****************************************************************************/
struct ftxxxx_proc {
struct proc_dir_entry *proc_entry;
u8 opmode;
u8 cmd_len;
u8 cmd[FTX_MAX_COMMMAND_LENGTH];
};
struct fts_ts_platform_data {
u32 irq_gpio;
u32 irq_gpio_flags;
u32 reset_gpio;
u32 reset_gpio_flags;
bool have_key;
u32 key_number;
u32 keys[FTS_MAX_KEYS];
u32 key_y_coords[FTS_MAX_KEYS];
u32 key_x_coords[FTS_MAX_KEYS];
u32 x_max;
u32 y_max;
u32 x_min;
u32 y_min;
u32 max_touch_number;
};
struct ts_event {
int x; /*x coordinate */
int y; /*y coordinate */
int p; /* pressure */
int flag; /* touch event flag: 0 -- down; 1-- up; 2 -- contact */
int id; /*touch ID */
int area;
};
struct fts_ts_data {
struct i2c_client *client;
struct spi_device *spi;
struct device *dev;
struct input_dev *input_dev;
struct fts_ts_platform_data *pdata;
struct ts_ic_info ic_info;
struct workqueue_struct *ts_workqueue;
struct work_struct fwupg_work;
struct delayed_work esdcheck_work;
struct delayed_work prc_work;
struct task_struct *thread_tpd;
struct ftxxxx_proc proc;
spinlock_t irq_lock;
struct mutex report_mutex;
struct mutex bus_lock;
int irq;
int log_level;
int fw_is_running; /* confirm fw is running when using spi:default 0 */
bool suspended;
bool fw_loading;
bool irq_disabled;
bool power_disabled;
bool glove_mode;
bool cover_mode;
bool charger_mode;
/* multi-touch */
struct ts_event *events;
u8 *bus_buf;
u8 *point_buf;
int pnt_buf_size;
int touchs;
int key_state;
int touch_point;
int point_num;
struct regulator *vdd;
struct pinctrl *pinctrl;
struct pinctrl_state *spi_default;
struct pinctrl_state *spi_active;
};
/*****************************************************************************
* Global variable or extern global variabls/functions
*****************************************************************************/
extern struct fts_ts_data *fts_data;
/* communication interface */
int fts_read(u8 *cmd, u32 cmdlen, u8 *data, u32 datalen);
int fts_read_reg(u8 addr, u8 *value);
int fts_write(u8 *writebuf, u32 writelen);
int fts_write_reg(u8 addr, u8 value);
void fts_hid2std(void);
int fts_bus_init(struct fts_ts_data *ts_data);
int fts_bus_exit(struct fts_ts_data *ts_data);
/* Gesture functions */
#if FTS_GESTURE_EN
int fts_gesture_init(struct fts_ts_data *ts_data);
int fts_gesture_exit(struct fts_ts_data *ts_data);
void fts_gesture_recovery(struct fts_ts_data *ts_data);
int fts_gesture_readdata(struct fts_ts_data *ts_data, u8 *data);
int fts_gesture_suspend(struct fts_ts_data *ts_data);
int fts_gesture_resume(struct fts_ts_data *ts_data);
#endif
/* Apk and functions */
#if FTS_APK_NODE_EN
int fts_create_apk_debug_channel(struct fts_ts_data *ts_data);
void fts_release_apk_debug_channel(struct fts_ts_data *ts_data);
#endif
/* ADB functions */
#if FTS_SYSFS_NODE_EN
int fts_create_sysfs(struct fts_ts_data *ts_data);
int fts_remove_sysfs(struct fts_ts_data *ts_data);
#endif
/* ESD */
#if FTS_ESDCHECK_EN
int fts_esdcheck_init(struct fts_ts_data *ts_data);
int fts_esdcheck_exit(struct fts_ts_data *ts_data);
int fts_esdcheck_switch(bool enable);
int fts_esdcheck_proc_busy(bool proc_debug);
int fts_esdcheck_set_intr(bool intr);
int fts_esdcheck_suspend(void);
int fts_esdcheck_resume(void);
#endif
/* Production test */
#if FTS_TEST_EN
int fts_test_init(struct fts_ts_data *ts_data);
int fts_test_exit(struct fts_ts_data *ts_data);
#endif
/* Point Report Check*/
#if FTS_POINT_REPORT_CHECK_EN
int fts_point_report_check_init(struct fts_ts_data *ts_data);
int fts_point_report_check_exit(struct fts_ts_data *ts_data);
void fts_prc_queue_work(struct fts_ts_data *ts_data);
#endif
/* FW upgrade */
int fts_fwupg_init(struct fts_ts_data *ts_data);
int fts_fwupg_exit(struct fts_ts_data *ts_data);
int fts_upgrade_bin(char *fw_name, bool force);
int fts_enter_test_environment(bool test_state);
/* Other */
int fts_reset_proc(int hdelayms);
int fts_wait_tp_to_valid(void);
void fts_release_all_finger(void);
void fts_tp_state_recovery(struct fts_ts_data *ts_data);
int fts_ex_mode_init(struct fts_ts_data *ts_data);
int fts_ex_mode_exit(struct fts_ts_data *ts_data);
int fts_ex_mode_recovery(struct fts_ts_data *ts_data);
void fts_irq_disable(void);
void fts_irq_enable(void);
#if FTS_PSENSOR_EN
int fts_proximity_init(void);
int fts_proximity_exit(void);
int fts_proximity_readdata(struct fts_ts_data *ts_data);
int fts_proximity_suspend(void);
int fts_proximity_resume(void);
int fts_proximity_recovery(struct fts_ts_data *ts_data);
#endif
#endif /* __LINUX_FOCALTECH_CORE_H__ */
@@ -0,0 +1,449 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2016 MediaTek Inc.
*/
#include "focaltech_core.h"
#if FTS_ESDCHECK_EN
/*****************************************************************************
* Private constant and macro definitions using #define
*****************************************************************************/
#define ESDCHECK_WAIT_TIME 1000 /* ms */
#define LCD_ESD_PATCH 0
/*****************************************************************************
* Private enumerations, structures and unions using typedef
*****************************************************************************/
struct fts_esdcheck_st {
u8 mode : 1; /* 1- need check esd 0- no esd check */
u8 suspend : 1;
u8 proc_debug : 1; /* apk or adb use */
u8 intr : 1; /* 1- Interrupt trigger */
u8 unused : 4;
u8 flow_work_hold_cnt;
u8 flow_work_cnt_last; /* Save Flow Work Cnt(reg0x91) value */
u32 hardware_reset_cnt;
u32 nack_cnt;
u32 dataerror_cnt;
};
/*****************************************************************************
* Static variables
*****************************************************************************/
static struct fts_esdcheck_st fts_esdcheck_data;
/*****************************************************************************
* Global variable or extern global variabls/functions
*****************************************************************************/
/*****************************************************************************
* Static function prototypes
*****************************************************************************/
/*****************************************************************************
* functions body
*****************************************************************************/
#if LCD_ESD_PATCH
int lcd_need_reset;
static int tp_need_recovery; /* LCD reset cause Tp reset */
int idc_esdcheck_lcderror(struct fts_ts_data *ts_data)
{
int ret = 0;
u8 val = 0;
FTS_DEBUG("[ESD]Check LCD ESD");
if ((tp_need_recovery == 1) && (lcd_need_reset == 0)) {
tp_need_recovery = 0;
/* LCD reset, need recover TP state */
fts_release_all_finger();
fts_tp_state_recovery(ts_data);
}
ret = fts_read_reg(FTS_REG_ESD_SATURATE, &val);
if (ret < 0) {
FTS_ERROR("[ESD]: Read ESD_SATURATE(0xED) failed ret=%d!", ret);
return -EIO;
}
if (val == 0xAA) {
/*
* 1. Set flag lcd_need_reset = 1;
* 2. LCD driver need reset(recovery) LCD and set lcd_need_reset
* to 0
* 3. recover TP state
*/
FTS_INFO("LCD ESD, Execute LCD reset!");
lcd_need_reset = 1;
tp_need_recovery = 1;
}
return 0;
}
#endif
static int fts_esdcheck_tp_reset(struct fts_ts_data *ts_data)
{
FTS_FUNC_ENTER();
fts_esdcheck_data.flow_work_hold_cnt = 0;
fts_esdcheck_data.hardware_reset_cnt++;
fts_reset_proc(200);
fts_release_all_finger();
fts_tp_state_recovery(ts_data);
FTS_FUNC_EXIT();
return 0;
}
/*****************************************************************************
* Name: get_chip_id
* Brief: Read Chip Id 3 times
* Input:
* Output:
* Return: 1(true) - Read Chip Id 3 times failed
* 0(false) - Read Chip Id pass
*****************************************************************************/
static bool get_chip_id(struct fts_ts_data *ts_data)
{
int ret = 0;
int i = 0;
u8 reg_value = 0;
u8 reg_addr = 0;
u8 chip_id = ts_data->ic_info.ids.chip_idh;
for (i = 0; i < 3; i++) {
reg_addr = FTS_REG_CHIP_ID;
ret = fts_read(&reg_addr, 1, &reg_value, 1);
if (ret < 0) {
FTS_ERROR("[ESD]: Read Reg 0xA3 failed ret = %d!!",
ret);
fts_esdcheck_data.nack_cnt++;
} else {
if (reg_value == chip_id)
break;
FTS_DEBUG("read chip_id:%x,retry:%d", reg_value,
i);
fts_esdcheck_data.dataerror_cnt++;
}
}
msleep(20);
}
/* if can't get correct data in 3 times, then need hardware reset */
if (i >= 3) {
FTS_ERROR(
"[ESD]: Read Chip id 3 times failed, need execute TP reset!!");
return true;
}
return false;
}
/*****************************************************************************
* Name: get_flow_cnt
* Brief: Read flow cnt(0x91)
* Input:
* Output:
* Return: 1(true) - Reg 0x91(flow cnt) abnormal: hold a value for 5 times
* 0(false) - Reg 0x91(flow cnt) normal
*****************************************************************************/
static bool get_flow_cnt(struct fts_ts_data *ts_data)
{
int ret = 0;
u8 reg_value = 0;
u8 reg_addr = 0;
reg_addr = FTS_REG_FLOW_WORK_CNT;
ret = fts_read(&reg_addr, 1, &reg_value, 1);
if (ret < 0) {
FTS_ERROR("[ESD]: Read Reg 0x91 failed ret = %d!!", ret);
fts_esdcheck_data.nack_cnt++;
} else {
if (reg_value == fts_esdcheck_data.flow_work_cnt_last)
fts_esdcheck_data.flow_work_hold_cnt++;
else
fts_esdcheck_data.flow_work_hold_cnt = 0;
fts_esdcheck_data.flow_work_cnt_last = reg_value;
}
/* if read flow work cnt 5 times and the value are all the same, then */
/* need hardware_reset */
if (fts_esdcheck_data.flow_work_hold_cnt >= 5) {
FTS_DEBUG(
"[ESD]: Flow Work Cnt(reg0x91) keep a value for 5 times, need execute TP reset!!");
return true;
}
return false;
}
static int esdcheck_algorithm(struct fts_ts_data *ts_data)
{
int ret = 0;
u8 reg_value = 0;
u8 reg_addr = 0;
bool hardware_reset = 0;
/* 1. esdcheck is interrupt, then return */
if (fts_esdcheck_data.intr == 1) {
FTS_DEBUG(
"[ESD]: In interrupt state, not check esd, return immediately!!");
return 0;
}
/* 2. check power state, if suspend, no need check esd */
if (fts_esdcheck_data.suspend == 1) {
FTS_DEBUG(
"[ESD]: In suspend, not check esd, return immediately!!");
/* because in suspend state, adb can be used, when upgrade FW,
* will active ESD check(active = 1)
* But in suspend, then will don't queue_delayed_work, when
* resume, don't check ESD again
*/
return 0;
}
/* 3. check fts_esdcheck_data.proc_debug state, if 1-proc busy, no need */
/* check esd*/
if (fts_esdcheck_data.proc_debug == 1) {
FTS_INFO(
"[ESD]: In apk or adb command mode, not check esd, return immediately!!");
return 0;
}
/* 4. In factory mode, can't check esd */
reg_addr = FTS_REG_WORKMODE;
ret = fts_read_reg(reg_addr, &reg_value);
if (ret < 0) {
fts_esdcheck_data.nack_cnt++;
} else if ((reg_value & 0x70) != FTS_REG_WORKMODE_WORK_VALUE) {
FTS_DEBUG(
"[ESD]: not in work mode, no check esd, return immediately!!");
return 0;
}
/* 5. IDC esd check lcd default:close */
#if LCD_ESD_PATCH
idc_esdcheck_lcderror(ts_data);
#endif
/* 6. Get Chip ID */
hardware_reset = get_chip_id(ts_data);
/* 7. get Flow work cnt: 0x91 If no change for 5 times, then ESD and */
/* reset */
if (!hardware_reset)
hardware_reset = get_flow_cnt(ts_data);
/* 8. If need hardware reset, then handle it here */
if (hardware_reset == 1) {
FTS_DEBUG("[ESD]: NoACK=%d, Error Data=%d, Hardware Reset=%d",
fts_esdcheck_data.nack_cnt,
fts_esdcheck_data.dataerror_cnt,
fts_esdcheck_data.hardware_reset_cnt);
fts_esdcheck_tp_reset(ts_data);
}
return 0;
}
static void esdcheck_func(struct work_struct *work)
{
u8 val = 0;
struct fts_ts_data *ts_data =
container_of(work, struct fts_ts_data, esdcheck_work.work);
if (fts_esdcheck_data.mode == ENABLE) {
if (ts_data->ic_info.is_incell) {
fts_read_reg(FTS_REG_ESDCHECK_DISABLE, &val);
if (val == 0xA5) {
fts_esdcheck_data.mode = DISABLE;
return;
}
}
esdcheck_algorithm(ts_data);
queue_delayed_work(ts_data->ts_workqueue,
&ts_data->esdcheck_work,
msecs_to_jiffies(ESDCHECK_WAIT_TIME));
}
}
int fts_esdcheck_set_intr(bool intr)
{
/* interrupt don't add debug message */
fts_esdcheck_data.intr = intr;
return 0;
}
int fts_esdcheck_get_status(void)
{
/* interrupt don't add debug message */
return fts_esdcheck_data.mode;
}
/*****************************************************************************
* Name: fts_esdcheck_proc_busy
* Brief: When APK or ADB command access TP via driver, then need set
*proc_debug,
* then will not check ESD.
* Input:
* Output:
* Return:
*****************************************************************************/
int fts_esdcheck_proc_busy(bool proc_debug)
{
fts_esdcheck_data.proc_debug = proc_debug;
return 0;
}
/*****************************************************************************
* Name: fts_esdcheck_switch
* Brief: FTS esd check function switch.
* Input: enable: 1 - Enable esd check
* 0 - Disable esd check
* Output:
* Return:
*****************************************************************************/
int fts_esdcheck_switch(bool enable)
{
struct fts_ts_data *ts_data = fts_data;
FTS_FUNC_ENTER();
if (fts_esdcheck_data.mode == ENABLE) {
if (enable) {
FTS_DEBUG("[ESD]: ESD check start!!");
fts_esdcheck_data.flow_work_hold_cnt = 0;
fts_esdcheck_data.flow_work_cnt_last = 0;
queue_delayed_work(
ts_data->ts_workqueue, &ts_data->esdcheck_work,
msecs_to_jiffies(ESDCHECK_WAIT_TIME));
} else {
FTS_DEBUG("[ESD]: ESD check stop!!");
cancel_delayed_work_sync(&ts_data->esdcheck_work);
}
} else {
FTS_DEBUG("[ESD]: ESD should disable!!");
cancel_delayed_work_sync(&ts_data->esdcheck_work);
}
FTS_FUNC_EXIT();
return 0;
}
int fts_esdcheck_suspend(void)
{
FTS_FUNC_ENTER();
fts_esdcheck_switch(DISABLE);
fts_esdcheck_data.suspend = 1;
FTS_FUNC_EXIT();
return 0;
}
int fts_esdcheck_resume(void)
{
FTS_FUNC_ENTER();
fts_esdcheck_switch(ENABLE);
fts_esdcheck_data.suspend = 0;
FTS_FUNC_EXIT();
return 0;
}
static ssize_t fts_esd_mode_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct input_dev *input_dev = fts_data->input_dev;
mutex_lock(&input_dev->mutex);
if (FTS_SYSFS_ECHO_ON(buf)) {
FTS_DEBUG("enable esdcheck");
fts_esdcheck_data.mode = ENABLE;
fts_esdcheck_switch(ENABLE);
} else if (FTS_SYSFS_ECHO_OFF(buf)) {
FTS_DEBUG("disable esdcheck");
fts_esdcheck_data.mode = DISABLE;
fts_esdcheck_switch(DISABLE);
}
mutex_unlock(&input_dev->mutex);
return count;
}
static ssize_t fts_esd_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int count;
struct input_dev *input_dev = fts_data->input_dev;
mutex_lock(&input_dev->mutex);
count = snprintf(buf, PAGE_SIZE, "Esd check: %s\n",
fts_esdcheck_get_status() ? "On" : "Off");
mutex_unlock(&input_dev->mutex);
return count;
}
/* sysfs esd node
* read example: cat fts_esd_mode ---read esd mode
* write example:echo 01 > fts_esd_mode ---make esdcheck enable
*
*/
static DEVICE_ATTR_RW(fts_esd_mode, 0644, fts_esd_mode_show,
fts_esd_mode_store);
static struct attribute *fts_esd_mode_attrs[] = {
&dev_attr_fts_esd_mode.attr, NULL,
};
static struct attribute_group fts_esd_group = {
.attrs = fts_esd_mode_attrs,
};
int fts_create_esd_sysfs(struct device *dev)
{
int ret = 0;
ret = sysfs_create_group(&dev->kobj, &fts_esd_group);
if (ret != 0) {
FTS_ERROR("%s(sysfs) create failed!", __func__);
sysfs_remove_group(&dev->kobj, &fts_esd_group);
return ret;
}
return 0;
}
int fts_esdcheck_init(struct fts_ts_data *ts_data)
{
FTS_FUNC_ENTER();
if (ts_data->ts_workqueue) {
INIT_DELAYED_WORK(&ts_data->esdcheck_work, esdcheck_func);
} else {
FTS_ERROR(
"fts workqueue is NULL, can't run esd check function");
return -EINVAL;
}
memset((u8 *)&fts_esdcheck_data, 0, sizeof(struct fts_esdcheck_st));
fts_esdcheck_data.mode = ENABLE;
fts_esdcheck_switch(ENABLE);
fts_create_esd_sysfs(ts_data->dev);
FTS_FUNC_EXIT();
return 0;
}
int fts_esdcheck_exit(struct fts_ts_data *ts_data)
{
sysfs_remove_group(&ts_data->dev->kobj, &fts_esd_group);
return 0;
}
#endif /* FTS_ESDCHECK_EN */
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,272 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2016 MediaTek Inc.
*/
#include "focaltech_core.h"
/*****************************************************************************
* 2.Private constant and macro definitions using #define
*****************************************************************************/
/*****************************************************************************
* 3.Private enumerations, structures and unions using typedef
*****************************************************************************/
enum _ex_mode {
MODE_GLOVE = 0,
MODE_COVER,
MODE_CHARGER,
};
/*****************************************************************************
* 4.Static variables
*****************************************************************************/
/*****************************************************************************
* 5.Global variable or extern global variabls/functions
*****************************************************************************/
/*****************************************************************************
* 6.Static function prototypes
*************************************************************************/
static int fts_ex_mode_switch(enum _ex_mode mode, u8 value)
{
int ret = 0;
u8 m_val = 0;
if (value)
m_val = 0x01;
else
m_val = 0x00;
switch (mode) {
case MODE_GLOVE:
ret = fts_write_reg(FTS_REG_GLOVE_MODE_EN, m_val);
if (ret < 0)
FTS_ERROR("MODE_GLOVE switch to %d fail", m_val);
break;
case MODE_COVER:
ret = fts_write_reg(FTS_REG_COVER_MODE_EN, m_val);
if (ret < 0)
FTS_ERROR("MODE_COVER switch to %d fail", m_val);
break;
case MODE_CHARGER:
ret = fts_write_reg(FTS_REG_CHARGER_MODE_EN, m_val);
if (ret < 0)
FTS_ERROR("MODE_CHARGER switch to %d fail", m_val);
break;
default:
FTS_ERROR("mode(%d) unsupport", mode);
ret = -EINVAL;
break;
}
return ret;
}
static ssize_t fts_glove_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int count = 0;
u8 val = 0;
struct fts_ts_data *ts_data = fts_data;
struct input_dev *input_dev = ts_data->input_dev;
mutex_lock(&input_dev->mutex);
fts_read_reg(FTS_REG_GLOVE_MODE_EN, &val);
count = snprintf(buf + count, PAGE_SIZE, "Glove Mode:%s\n",
ts_data->glove_mode ? "On" : "Off");
count += snprintf(buf + count, PAGE_SIZE, "Glove Reg(0xC0):%d\n", val);
mutex_unlock(&input_dev->mutex);
return count;
}
static ssize_t fts_glove_mode_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int ret = 0;
struct fts_ts_data *ts_data = fts_data;
if (FTS_SYSFS_ECHO_ON(buf)) {
if (!ts_data->glove_mode) {
FTS_DEBUG("enter glove mode");
ret = fts_ex_mode_switch(MODE_GLOVE, ENABLE);
if (ret >= 0)
ts_data->glove_mode = ENABLE;
}
} else if (FTS_SYSFS_ECHO_OFF(buf)) {
if (ts_data->glove_mode) {
FTS_DEBUG("exit glove mode");
ret = fts_ex_mode_switch(MODE_GLOVE, DISABLE);
if (ret >= 0)
ts_data->glove_mode = DISABLE;
}
}
FTS_DEBUG("glove mode:%d", ts_data->glove_mode);
return count;
}
static ssize_t fts_cover_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int count = 0;
u8 val = 0;
struct fts_ts_data *ts_data = fts_data;
struct input_dev *input_dev = ts_data->input_dev;
mutex_lock(&input_dev->mutex);
fts_read_reg(FTS_REG_COVER_MODE_EN, &val);
count = snprintf(buf + count, PAGE_SIZE, "Cover Mode:%s\n",
ts_data->cover_mode ? "On" : "Off");
count += snprintf(buf + count, PAGE_SIZE, "Cover Reg(0xC1):%d\n", val);
mutex_unlock(&input_dev->mutex);
return count;
}
static ssize_t fts_cover_mode_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int ret = 0;
struct fts_ts_data *ts_data = fts_data;
if (FTS_SYSFS_ECHO_ON(buf)) {
if (!ts_data->cover_mode) {
FTS_DEBUG("enter cover mode");
ret = fts_ex_mode_switch(MODE_COVER, ENABLE);
if (ret >= 0)
ts_data->cover_mode = ENABLE;
}
} else if (FTS_SYSFS_ECHO_OFF(buf)) {
if (ts_data->cover_mode) {
FTS_DEBUG("exit cover mode");
ret = fts_ex_mode_switch(MODE_COVER, DISABLE);
if (ret >= 0)
ts_data->cover_mode = DISABLE;
}
}
FTS_DEBUG("cover mode:%d", ts_data->cover_mode);
return count;
}
static ssize_t fts_charger_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int count = 0;
u8 val = 0;
struct fts_ts_data *ts_data = fts_data;
struct input_dev *input_dev = ts_data->input_dev;
mutex_lock(&input_dev->mutex);
fts_read_reg(FTS_REG_CHARGER_MODE_EN, &val);
count = snprintf(buf + count, PAGE_SIZE, "Charger Mode:%s\n",
ts_data->charger_mode ? "On" : "Off");
count +=
snprintf(buf + count, PAGE_SIZE, "Charger Reg(0x8B):%d\n", val);
mutex_unlock(&input_dev->mutex);
return count;
}
static ssize_t fts_charger_mode_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int ret = 0;
struct fts_ts_data *ts_data = fts_data;
if (FTS_SYSFS_ECHO_ON(buf)) {
if (!ts_data->charger_mode) {
FTS_DEBUG("enter charger mode");
ret = fts_ex_mode_switch(MODE_CHARGER, ENABLE);
if (ret >= 0)
ts_data->charger_mode = ENABLE;
}
} else if (FTS_SYSFS_ECHO_OFF(buf)) {
if (ts_data->charger_mode) {
FTS_DEBUG("exit charger mode");
ret = fts_ex_mode_switch(MODE_CHARGER, DISABLE);
if (ret >= 0)
ts_data->charger_mode = DISABLE;
}
}
FTS_DEBUG("charger mode:%d", ts_data->glove_mode);
return count;
}
/* read and write charger mode
* read example: cat fts_glove_mode ---read glove mode
* write example:echo 1 > fts_glove_mode ---write glove mode to 01
*/
static DEVICE_ATTR_RW(fts_glove_mode);
static DEVICE_ATTR_RW(fts_cover_mode);
static DEVICE_ATTR_RW(fts_charger_mode);
static struct attribute *fts_touch_mode_attrs[] = {
&dev_attr_fts_glove_mode.attr, &dev_attr_fts_cover_mode.attr,
&dev_attr_fts_charger_mode.attr, NULL,
};
static struct attribute_group fts_touch_mode_group = {
.attrs = fts_touch_mode_attrs,
};
int fts_ex_mode_recovery(struct fts_ts_data *ts_data)
{
if (ts_data->glove_mode)
fts_ex_mode_switch(MODE_GLOVE, ENABLE);
if (ts_data->cover_mode)
fts_ex_mode_switch(MODE_COVER, ENABLE);
if (ts_data->charger_mode)
fts_ex_mode_switch(MODE_CHARGER, ENABLE);
return 0;
}
int fts_ex_mode_init(struct fts_ts_data *ts_data)
{
int ret = 0;
ts_data->glove_mode = DISABLE;
ts_data->cover_mode = DISABLE;
ts_data->charger_mode = DISABLE;
ret = sysfs_create_group(&ts_data->dev->kobj, &fts_touch_mode_group);
if (ret < 0) {
FTS_ERROR("create sysfs(ex_mode) fail");
sysfs_remove_group(&ts_data->dev->kobj, &fts_touch_mode_group);
return ret;
}
FTS_DEBUG("create sysfs(ex_mode) succeedfully");
return 0;
}
int fts_ex_mode_exit(struct fts_ts_data *ts_data)
{
sysfs_remove_group(&ts_data->dev->kobj, &fts_touch_mode_group);
return 0;
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,195 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2016 MediaTek Inc.
*/
#ifndef __LINUX_FOCALTECH_FLASH_H__
#define __LINUX_FOCALTECH_FLASH_H__
/*****************************************************************************
* 1.Included header files
*****************************************************************************/
#include "focaltech_core.h"
/*****************************************************************************
* Private constant and macro definitions using #define
*****************************************************************************/
#define FTS_CMD_RESET 0x07
#define FTS_ROMBOOT_CMD_WRITE 0xAE
#define FTS_ROMBOOT_CMD_START_APP 0x08
#define FTS_DELAY_PRAMBOOT_START 10
#define FTS_ROMBOOT_CMD_ECC 0xCC
#define FTS_CMD_READ 0x03
#define FTS_CMD_READ_DELAY 1
#define FTS_CMD_READ_LEN 4
#define FTS_CMD_FLASH_TYPE 0x05
#define FTS_CMD_FLASH_MODE 0x09
#define FLASH_MODE_WRITE_FLASH_VALUE 0x0A
#define FLASH_MODE_UPGRADE_VALUE 0x0B
#define FLASH_MODE_LIC_VALUE 0x0C
#define FLASH_MODE_PARAM_VALUE 0x0D
#define FTS_CMD_ERASE_APP 0x61
#define FTS_REASE_APP_DELAY 1350
#define FTS_ERASE_SECTOR_DELAY 60
#define FTS_RETRIES_REASE 50
#define FTS_RETRIES_DELAY_REASE 200
#define FTS_CMD_FLASH_STATUS 0x6A
#define FTS_CMD_FLASH_STATUS_LEN 2
#define FTS_CMD_FLASH_STATUS_NOP 0x0000
#define FTS_CMD_FLASH_STATUS_ECC_OK 0xF055
#define FTS_CMD_FLASH_STATUS_ERASE_OK 0xF0AA
#define FTS_CMD_FLASH_STATUS_WRITE_OK 0x1000
#define FTS_CMD_ECC_INIT 0x64
#define FTS_CMD_ECC_CAL 0x65
#define FTS_CMD_ECC_CAL_LEN 6
#define FTS_RETRIES_ECC_CAL 10
#define FTS_RETRIES_DELAY_ECC_CAL 50
#define FTS_CMD_ECC_READ 0x66
#define FTS_CMD_DATA_LEN 0xB0
#define FTS_CMD_APP_DATA_LEN_INCELL 0x7A
#define FTS_CMD_DATA_LEN_LEN 4
#define FTS_CMD_WRITE 0xBF
#define FTS_RETRIES_WRITE 100
#define FTS_RETRIES_DELAY_WRITE 1
#define FTS_CMD_WRITE_LEN 6
#define FTS_DELAY_READ_ID 20
#define FTS_DELAY_UPGRADE_RESET 80
#define PRAMBOOT_MIN_SIZE 0x120
#define PRAMBOOT_MAX_SIZE (64 * 1024)
#define FTS_FLASH_PACKET_LENGTH 32 /* max=128 */
#define FTS_MAX_LEN_ECC_CALC 0xFFFE /* must be even */
#define FTS_MIN_LEN 0x120
#define FTS_MAX_LEN_FILE (128 * 1024)
#define FTS_MAX_LEN_APP (64 * 1024)
#define FTS_MAX_LEN_SECTOR (4 * 1024)
#define FTS_CONIFG_VENDORID_OFF 0x04
#define FTS_CONIFG_MODULEID_OFF 0x1E
#define FTS_CONIFG_PROJECTID_OFF 0x20
#define FTS_APPINFO_OFF 0x100
#define FTS_APPINFO_APPLEN_OFF 0x00
#define FTS_APPINFO_APPLEN2_OFF 0x12
#define FTS_REG_UPGRADE 0xFC
#define FTS_REG_UPGRADE2 0xBC
#define FTS_UPGRADE_AA 0xAA
#define FTS_UPGRADE_55 0x55
#define FTS_DELAY_UPGRADE_AA 10
#define FTS_UPGRADE_LOOP 30
#define FTS_HEADER_LEN 32
#define FTS_FW_BIN_FILEPATH "/sdcard/"
#define FTS_FW_IDE_SIG "IDE_"
#define FTS_FW_IDE_SIG_LEN 4
#define MAX_MODULE_VENDOR_NAME_LEN 16
#define FTS_ROMBOOT_CMD_ECC_NEW_LEN 7
#define FTS_ECC_FINISH_TIMEOUT 100
#define FTS_ROMBOOT_CMD_ECC_FINISH 0xCE
#define FTS_ROMBOOT_CMD_ECC_FINISH_OK_A5 0xA5
#define FTS_ROMBOOT_CMD_ECC_FINISH_OK_00 0x00
#define FTS_ROMBOOT_CMD_ECC_READ 0xCD
#define AL2_FCS_COEF ((1 << 15) + (1 << 10) + (1 << 3))
#define FTS_APP_INFO_OFFSET 0x100
enum FW_STATUS {
FTS_RUN_IN_ERROR,
FTS_RUN_IN_APP,
FTS_RUN_IN_ROM,
FTS_RUN_IN_PRAM,
FTS_RUN_IN_BOOTLOADER,
};
enum FW_FLASH_MODE {
FLASH_MODE_APP,
FLASH_MODE_LIC,
FLASH_MODE_PARAM,
FLASH_MODE_ALL,
};
enum ECC_CHECK_MODE {
ECC_CHECK_MODE_XOR,
ECC_CHECK_MODE_CRC16,
};
/*****************************************************************************
* Private enumerations, structures and unions using typedef
*****************************************************************************/
/* IC info */
struct upgrade_func {
u64 ctype[FTX_MAX_COMPATIBLE_TYPE];
u32 fwveroff;
u32 fwcfgoff;
u32 appoff;
u32 licoff;
u32 paramcfgoff;
u32 paramcfgveroff;
u32 paramcfg2off;
int pram_ecc_check_mode;
int fw_ecc_check_mode;
bool new_return_value_from_ic;
bool appoff_handle_in_ic;
bool is_reset_register_BC;
bool read_boot_id_need_reset;
bool hid_supported;
bool pramboot_supported;
u8 *pramboot;
u32 pb_length;
int (*init)(u8 *buf, u32 len);
int (*upgrade)(u8 *buf, u32 len);
int (*get_hlic_ver)(u8 *buf);
int (*lic_upgrade)(u8 *buf, u32 len);
int (*param_upgrade)(u8 *buf, u32 len);
int (*force_upgrade)(u8 *buf, u32 len);
};
struct upgrade_module {
int id;
char vendor_name[MAX_MODULE_VENDOR_NAME_LEN];
u8 *fw_file;
u32 fw_len;
};
struct fts_upgrade {
struct fts_ts_data *ts_data;
struct upgrade_module *module_info;
struct upgrade_func *func;
int module_id;
bool fw_from_request;
u8 *fw;
u32 fw_length;
u8 *lic;
u32 lic_length;
};
/*****************************************************************************
* Global variable or extern global variabls/functions
*****************************************************************************/
extern struct upgrade_func upgrade_func_ft5822;
extern struct upgrade_func upgrade_func_ft5422;
extern struct upgrade_func upgrade_func_ft6426;
extern struct upgrade_func upgrade_func_ft8716;
extern struct upgrade_func upgrade_func_ft8736;
extern struct upgrade_func upgrade_func_ft8006m;
extern struct upgrade_func upgrade_func_ft8607;
extern struct upgrade_func upgrade_func_ft8006u;
extern struct upgrade_func upgrade_func_ft8719;
extern struct upgrade_func upgrade_func_ft8739;
extern struct upgrade_func upgrade_func_ft8006p;
extern struct upgrade_func upgrade_func_ft5452;
extern struct upgrade_func upgrade_func_ft5422u;
extern struct upgrade_func upgrade_func_ft5472;
extern struct upgrade_func upgrade_func_ft6216;
extern struct upgrade_func upgrade_func_ft7251;
extern struct upgrade_func upgrade_func_ft8613s;
/*****************************************************************************
* Static function prototypes
*****************************************************************************/
int fts_fwupg_reset_in_boot(void);
int fts_fwupg_enter_into_boot(void);
int fts_fwupg_erase(u32 delay);
int fts_fwupg_ecc_cal(u32 saddr, u32 len);
int fts_flash_write_buf(u32 saddr, u8 *buf, u32 len, u32 delay);
int fts_fwupg_upgrade(struct fts_upgrade *upg);
#endif
@@ -0,0 +1 @@
obj-y += focaltech_upgrade_ft3518.o
@@ -0,0 +1,146 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2016 MediaTek Inc.
*/
#include "../focaltech_flash.h"
static int fts_ft5452_upgrade(u8 *buf, u32 len)
{
int ret = 0;
u32 start_addr = 0;
u8 cmd[4] = { 0 };
int ecc_in_host = 0;
int ecc_in_tp = 0;
int i = 0;
u8 wbuf[7] = { 0 };
u8 reg_val[4] = {0};
if (buf == NULL) {
FTS_ERROR("fw buf is null");
return -EINVAL;
}
if ((len < FTS_MIN_LEN) || (len > (60 * 1024))) {
FTS_ERROR("fw buffer len(%x) fail", len);
return -EINVAL;
}
/* enter into upgrade environment */
ret = fts_fwupg_enter_into_boot();
if (ret < 0) {
FTS_ERROR("enter into pramboot/bootloader fail,ret=%d", ret);
goto fw_reset;
}
cmd[0] = FTS_CMD_FLASH_MODE;
cmd[1] = FLASH_MODE_UPGRADE_VALUE;
ret = fts_write(cmd, 2);
if (ret < 0) {
FTS_ERROR("upgrade mode(09) cmd write fail");
goto fw_reset;
}
cmd[0] = FTS_CMD_DATA_LEN;
cmd[1] = BYTE_OFF_16(len);
cmd[2] = BYTE_OFF_8(len);
cmd[3] = BYTE_OFF_0(len);
ret = fts_write(cmd, FTS_CMD_DATA_LEN_LEN);
if (ret < 0) {
FTS_ERROR("data len cmd write fail");
goto fw_reset;
}
ret = fts_fwupg_erase(FTS_REASE_APP_DELAY);
if (ret < 0) {
FTS_ERROR("erase cmd write fail");
goto fw_reset;
}
/* write app */
start_addr = upgrade_func_ft5452.appoff;
ecc_in_host = fts_flash_write_buf(start_addr, buf, len, 1);
if (ecc_in_host < 0) {
FTS_ERROR("lcd initial code write fail");
goto fw_reset;
}
FTS_INFO("**********read out checksum**********");
/* check sum init */
wbuf[0] = FTS_CMD_ECC_INIT;
ret = fts_write(wbuf, 1);
if (ret < 0) {
FTS_ERROR("ecc init cmd write fail");
return ret;
}
/* send commond to start checksum */
wbuf[0] = FTS_CMD_ECC_CAL;
wbuf[1] = BYTE_OFF_16(start_addr);
wbuf[2] = BYTE_OFF_8(start_addr);
wbuf[3] = BYTE_OFF_0(start_addr);
wbuf[4] = BYTE_OFF_16(len);
wbuf[5] = BYTE_OFF_8(len);
wbuf[6] = BYTE_OFF_0(len);
FTS_DEBUG("ecc calc startaddr:0x%04x, len:%d", start_addr, len);
ret = fts_write(wbuf, 7);
if (ret < 0) {
FTS_ERROR("ecc calc cmd write fail");
return ret;
}
msleep(len / 256);
/* read status if check sum is finished */
for (i = 0; i < FTS_RETRIES_ECC_CAL; i++) {
wbuf[0] = FTS_CMD_FLASH_STATUS;
reg_val[0] = reg_val[1] = 0x00;
fts_read(wbuf, 1, reg_val, 2);
FTS_DEBUG("[UPGRADE]: reg_val[0]=%02x reg_val[0]=%02x!!", reg_val[0], reg_val[1]);
if ((reg_val[0] == 0xF0) && (reg_val[1] == 0x55))
break;
msleep(FTS_RETRIES_DELAY_ECC_CAL);
}
/* read out check sum */
wbuf[0] = FTS_CMD_ECC_READ;
ret = fts_read(wbuf, 1, reg_val, 1);
if (ret < 0) {
FTS_ERROR("ecc read cmd write fail");
return ret;
}
ecc_in_tp = reg_val[0];
FTS_INFO("ecc in tp:%x, host:%x", ecc_in_tp, ecc_in_host);
if (ecc_in_tp != ecc_in_host) {
FTS_ERROR("ecc check fail");
goto fw_reset;
}
FTS_INFO("upgrade success, reset to normal boot");
ret = fts_fwupg_reset_in_boot();
if (ret < 0)
FTS_ERROR("reset to normal boot fail");
msleep(200);
return 0;
fw_reset:
FTS_INFO("upgrade fail, reset to normal boot");
ret = fts_fwupg_reset_in_boot();
if (ret < 0)
FTS_ERROR("reset to normal boot fail");
return -EIO;
}
struct upgrade_func upgrade_func_ft5452 = {
.ctype = {0x81},
.fwveroff = 0x010E,
.fwcfgoff = 0x1FFB0,
.appoff = 0x0000,
.pramboot_supported = false,
.hid_supported = true,
.upgrade = fts_ft5452_upgrade,
};
@@ -0,0 +1,475 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2016 MediaTek Inc.
*/
/*****************************************************************************
*
* File Name: focaltech_gestrue.c
*
* Author: Focaltech Driver Team
*
* Created: 2016-08-08
*
* Abstract:
*
* Reference:
*
*****************************************************************************/
/*****************************************************************************
* 1.Included header files
*****************************************************************************/
#include "focaltech_core.h"
#if FTS_GESTURE_EN
/******************************************************************************
* Private constant and macro definitions using #define
*****************************************************************************/
#define KEY_GESTURE_U KEY_U
#define KEY_GESTURE_UP KEY_UP
#define KEY_GESTURE_DOWN KEY_DOWN
#define KEY_GESTURE_LEFT KEY_LEFT
#define KEY_GESTURE_RIGHT KEY_RIGHT
#define KEY_GESTURE_O KEY_O
#define KEY_GESTURE_E KEY_E
#define KEY_GESTURE_M KEY_M
#define KEY_GESTURE_L KEY_L
#define KEY_GESTURE_W KEY_W
#define KEY_GESTURE_S KEY_S
#define KEY_GESTURE_V KEY_V
#define KEY_GESTURE_C KEY_C
#define KEY_GESTURE_Z KEY_Z
#define GESTURE_LEFT 0x20
#define GESTURE_RIGHT 0x21
#define GESTURE_UP 0x22
#define GESTURE_DOWN 0x23
#define GESTURE_DOUBLECLICK 0x24
#define GESTURE_O 0x30
#define GESTURE_W 0x31
#define GESTURE_M 0x32
#define GESTURE_E 0x33
#define GESTURE_L 0x44
#define GESTURE_S 0x46
#define GESTURE_V 0x54
#define GESTURE_Z 0x41
#define GESTURE_C 0x34
/*****************************************************************************
* Private enumerations, structures and unions using typedef
*****************************************************************************/
/*
* gesture_id - mean which gesture is recognised
* point_num - points number of this gesture
* coordinate_x - All gesture point x coordinate
* coordinate_y - All gesture point y coordinate
* mode - gesture enable/disable, need enable by host
* - 1:enable gesture function(default) 0:disable
* active - gesture work flag,
* always set 1 when suspend, set 0 when resume
*/
struct fts_gesture_st {
u8 gesture_id;
u8 point_num;
u16 coordinate_x[FTS_GESTURE_POINTS_MAX];
u16 coordinate_y[FTS_GESTURE_POINTS_MAX];
u8 mode;
u8 active;
};
/*****************************************************************************
* Static variables
*****************************************************************************/
static struct fts_gesture_st fts_gesture_data;
/*****************************************************************************
* Global variable or extern global variabls/functions
*****************************************************************************/
/*****************************************************************************
* Static function prototypes
*****************************************************************************/
static ssize_t fts_gesture_mode_show(struct device *dev,
struct DEVICE_ATTR_RWibute *attr, char *buf)
{
int count = 0;
u8 val = 0;
struct input_dev *input_dev = fts_data->input_dev;
mutex_lock(&input_dev->mutex);
fts_read_reg(FTS_REG_GESTURE_EN, &val);
count = snprintf(buf, PAGE_SIZE, "Gesture Mode:%s\n",
fts_gesture_data.mode ? "On" : "Off");
count += snprintf(buf + count, PAGE_SIZE, "Reg(0xD0)=%d\n", val);
mutex_unlock(&input_dev->mutex);
return count;
}
static ssize_t fts_gesture_mode_store(struct device *dev,
struct DEVICE_ATTR_RWibute *attr, const char *buf,
size_t count)
{
struct input_dev *input_dev = fts_data->input_dev;
mutex_lock(&input_dev->mutex);
if (FTS_SYSFS_ECHO_ON(buf)) {
FTS_DEBUG("enable gesture");
fts_gesture_data.mode = ENABLE;
} else if (FTS_SYSFS_ECHO_OFF(buf)) {
FTS_DEBUG("disable gesture");
fts_gesture_data.mode = DISABLE;
}
mutex_unlock(&input_dev->mutex);
return count;
}
static ssize_t fts_gesture_buf_show(struct device *dev,
struct DEVICE_ATTR_RWibute *attr, char *buf)
{
int count = 0;
int i = 0;
struct input_dev *input_dev = fts_data->input_dev;
struct fts_gesture_st *gesture = &fts_gesture_data;
mutex_lock(&input_dev->mutex);
count = snprintf(buf, PAGE_SIZE, "Gesture ID:%d\n",
gesture->gesture_id);
count += snprintf(buf + count, PAGE_SIZE, "Gesture PointNum:%d\n",
gesture->point_num);
count += snprintf(buf + count, PAGE_SIZE, "Gesture Points Buffer:\n");
/* save point data,max:6 */
for (i = 0; i < FTS_GESTURE_POINTS_MAX; i++) {
count += snprintf(buf + count, PAGE_SIZE, "%3d(%4d,%4d) ", i,
gesture->coordinate_x[i],
gesture->coordinate_y[i]);
if ((i + 1) % 4 == 0)
count += snprintf(buf + count, PAGE_SIZE, "\n");
}
count += snprintf(buf + count, PAGE_SIZE, "\n");
mutex_unlock(&input_dev->mutex);
return count;
}
static ssize_t fts_gesture_buf_store(struct device *dev,
struct DEVICE_ATTR_RWibute *attr,
const char *buf, size_t count)
{
return -EPERM;
}
/* sysfs gesture node
* read example: cat fts_gesture_mode ---read gesture mode
* write example:echo 1 > fts_gesture_mode --- write gesture mode to 1
*
*/
static DEVICE_ATTR_RW(fts_gesture_mode, 0644, fts_gesture_mode_show,
fts_gesture_mode_store);
/*
* read example: cat fts_gesture_buf --- read gesture buf
*/
static DEVICE_ATTR_RW(fts_gesture_buf, 0644, fts_gesture_buf_show,
fts_gesture_buf_store);
static struct attribute *fts_gesture_mode_attrs[] = {
&dev_attr_fts_gesture_mode.attr, &dev_attr_fts_gesture_buf.attr, NULL,
};
static struct attribute_group fts_gesture_group = {
.attrs = fts_gesture_mode_attrs,
};
int fts_create_gesture_sysfs(struct device *dev)
{
int ret = 0;
ret = sysfs_create_group(&dev->kobj, &fts_gesture_group);
if (ret) {
FTS_ERROR("gesture sys node create fail");
sysfs_remove_group(&dev->kobj, &fts_gesture_group);
return ret;
}
return 0;
}
static void fts_gesture_report(struct input_dev *input_dev, int gesture_id)
{
int gesture;
FTS_DEBUG("gesture_id:0x%x", gesture_id);
switch (gesture_id) {
case GESTURE_LEFT:
gesture = KEY_GESTURE_LEFT;
break;
case GESTURE_RIGHT:
gesture = KEY_GESTURE_RIGHT;
break;
case GESTURE_UP:
gesture = KEY_GESTURE_UP;
break;
case GESTURE_DOWN:
gesture = KEY_GESTURE_DOWN;
break;
case GESTURE_DOUBLECLICK:
gesture = KEY_GESTURE_U;
break;
case GESTURE_O:
gesture = KEY_GESTURE_O;
break;
case GESTURE_W:
gesture = KEY_GESTURE_W;
break;
case GESTURE_M:
gesture = KEY_GESTURE_M;
break;
case GESTURE_E:
gesture = KEY_GESTURE_E;
break;
case GESTURE_L:
gesture = KEY_GESTURE_L;
break;
case GESTURE_S:
gesture = KEY_GESTURE_S;
break;
case GESTURE_V:
gesture = KEY_GESTURE_V;
break;
case GESTURE_Z:
gesture = KEY_GESTURE_Z;
break;
case GESTURE_C:
gesture = KEY_GESTURE_C;
break;
default:
gesture = -1;
break;
}
/* report event key */
if (gesture != -1) {
FTS_DEBUG("Gesture Code=%d", gesture);
input_report_key(input_dev, gesture, 1);
input_sync(input_dev);
input_report_key(input_dev, gesture, 0);
input_sync(input_dev);
}
}
/*****************************************************************************
* Name: fts_gesture_readdata
* Brief: Read information about gesture: enable flag/gesture points..., if ges-
* true enable, save gesture points' information, and report to OS.
* It will be called this function every interrupt when FTS_GESTURE_EN = 1
*
* gesture data length: 1(enable) + 1(reserve) + 2(header) + 6 * 4
* Input: ts_data - global struct data
* data - gesture data buffer if non-flash, else NULL
* Output:
* Return: 0 - read gesture data successfully, the report data is gesture data
* 1 - tp not in suspend/gesture not enable in TP FW
* -Exx - error
*****************************************************************************/
int fts_gesture_readdata(struct fts_ts_data *ts_data, u8 *data)
{
int ret = 0;
int i = 0;
int index = 0;
u8 buf[FTS_GESTURE_DATA_LEN] = {0};
struct input_dev *input_dev = ts_data->input_dev;
struct fts_gesture_st *gesture = &fts_gesture_data;
if (!ts_data->suspended || (gesture->mode == DISABLE))
return 1;
ret = fts_read_reg(FTS_REG_GESTURE_EN, &buf[0]);
if ((ret < 0) || (buf[0] != ENABLE)) {
FTS_DEBUG("gesture not enable in fw, don't process gesture");
return 1;
}
buf[2] = FTS_REG_GESTURE_OUTPUT_ADDRESS;
ret = fts_read(&buf[2], 1, &buf[2], FTS_GESTURE_DATA_LEN - 2);
if (ret < 0) {
FTS_ERROR("read gesture header data fail");
return ret;
}
/* init variable before read gesture point */
memset(gesture->coordinate_x, 0, FTS_GESTURE_POINTS_MAX * sizeof(u16));
memset(gesture->coordinate_y, 0, FTS_GESTURE_POINTS_MAX * sizeof(u16));
gesture->gesture_id = buf[2];
gesture->point_num = buf[3];
FTS_DEBUG("gesture_id=%d, point_num=%d", gesture->gesture_id,
gesture->point_num);
/* save point data,max:6 */
for (i = 0; i < FTS_GESTURE_POINTS_MAX; i++) {
index = 4 * i + 4;
gesture->coordinate_x[i] =
(u16)(((buf[0 + index] & 0x0F) << 8) + buf[1 + index]);
gesture->coordinate_y[i] =
(u16)(((buf[2 + index] & 0x0F) << 8) + buf[3 + index]);
}
/* report gesture to OS */
fts_gesture_report(input_dev, gesture->gesture_id);
return 0;
}
void fts_gesture_recovery(struct fts_ts_data *ts_data)
{
if ((fts_gesture_data.mode == ENABLE) &&
(fts_gesture_data.active == ENABLE)) {
FTS_DEBUG("gesture recovery...");
fts_write_reg(0xD1, 0xFF);
fts_write_reg(0xD2, 0xFF);
fts_write_reg(0xD5, 0xFF);
fts_write_reg(0xD6, 0xFF);
fts_write_reg(0xD7, 0xFF);
fts_write_reg(0xD8, 0xFF);
fts_write_reg(FTS_REG_GESTURE_EN, ENABLE);
}
}
int fts_gesture_suspend(struct fts_ts_data *ts_data)
{
int ret = 0;
int i = 0;
u8 state = 0xFF;
FTS_INFO("gesture suspend...");
/* gesture not enable, return immediately */
if (fts_gesture_data.mode == DISABLE) {
FTS_DEBUG("gesture is disabled");
return -EINVAL;
}
for (i = 0; i < 5; i++) {
fts_write_reg(0xD1, 0xFF);
fts_write_reg(0xD2, 0xFF);
fts_write_reg(0xD5, 0xFF);
fts_write_reg(0xD6, 0xFF);
fts_write_reg(0xD7, 0xFF);
fts_write_reg(0xD8, 0xFF);
fts_write_reg(FTS_REG_GESTURE_EN, ENABLE);
msleep(20);
fts_read_reg(FTS_REG_GESTURE_EN, &state);
if (state == ENABLE)
break;
}
if (i >= 5) {
FTS_ERROR("Enter into gesture(suspend) fail");
fts_gesture_data.active = DISABLE;
return -EIO;
}
ret = enable_irq_wake(ts_data->irq);
if (ret)
FTS_DEBUG("enable_irq_wake(irq:%d) fail", ts_data->irq);
fts_gesture_data.active = ENABLE;
FTS_INFO("Enter into gesture(suspend) successfully!");
return 0;
}
int fts_gesture_resume(struct fts_ts_data *ts_data)
{
int ret = 0;
int i = 0;
u8 state = 0xFF;
FTS_INFO("gesture resume...");
/* gesture not enable, return immediately */
if (fts_gesture_data.mode == DISABLE) {
FTS_DEBUG("gesture is disabled");
return -EINVAL;
}
if (fts_gesture_data.active == DISABLE) {
FTS_DEBUG("gesture active is disable, return immediately");
return -EINVAL;
}
fts_gesture_data.active = DISABLE;
for (i = 0; i < 5; i++) {
fts_write_reg(FTS_REG_GESTURE_EN, DISABLE);
msleep(20);
fts_read_reg(FTS_REG_GESTURE_EN, &state);
if (state == DISABLE)
break;
}
if (i >= 5) {
FTS_ERROR("exit gesture(resume) fail");
return -EIO;
}
ret = disable_irq_wake(ts_data->irq);
if (ret)
FTS_DEBUG("disable_irq_wake(irq:%d) fail", ts_data->irq);
FTS_INFO("resume from gesture successfully");
return 0;
}
int fts_gesture_init(struct fts_ts_data *ts_data)
{
struct input_dev *input_dev = ts_data->input_dev;
FTS_FUNC_ENTER();
input_set_capability(input_dev, EV_KEY, KEY_POWER);
input_set_capability(input_dev, EV_KEY, KEY_GESTURE_U);
input_set_capability(input_dev, EV_KEY, KEY_GESTURE_UP);
input_set_capability(input_dev, EV_KEY, KEY_GESTURE_DOWN);
input_set_capability(input_dev, EV_KEY, KEY_GESTURE_LEFT);
input_set_capability(input_dev, EV_KEY, KEY_GESTURE_RIGHT);
input_set_capability(input_dev, EV_KEY, KEY_GESTURE_O);
input_set_capability(input_dev, EV_KEY, KEY_GESTURE_E);
input_set_capability(input_dev, EV_KEY, KEY_GESTURE_M);
input_set_capability(input_dev, EV_KEY, KEY_GESTURE_L);
input_set_capability(input_dev, EV_KEY, KEY_GESTURE_W);
input_set_capability(input_dev, EV_KEY, KEY_GESTURE_S);
input_set_capability(input_dev, EV_KEY, KEY_GESTURE_V);
input_set_capability(input_dev, EV_KEY, KEY_GESTURE_Z);
input_set_capability(input_dev, EV_KEY, KEY_GESTURE_C);
__set_bit(KEY_GESTURE_RIGHT, input_dev->keybit);
__set_bit(KEY_GESTURE_LEFT, input_dev->keybit);
__set_bit(KEY_GESTURE_UP, input_dev->keybit);
__set_bit(KEY_GESTURE_DOWN, input_dev->keybit);
__set_bit(KEY_GESTURE_U, input_dev->keybit);
__set_bit(KEY_GESTURE_O, input_dev->keybit);
__set_bit(KEY_GESTURE_E, input_dev->keybit);
__set_bit(KEY_GESTURE_M, input_dev->keybit);
__set_bit(KEY_GESTURE_W, input_dev->keybit);
__set_bit(KEY_GESTURE_L, input_dev->keybit);
__set_bit(KEY_GESTURE_S, input_dev->keybit);
__set_bit(KEY_GESTURE_V, input_dev->keybit);
__set_bit(KEY_GESTURE_C, input_dev->keybit);
__set_bit(KEY_GESTURE_Z, input_dev->keybit);
fts_create_gesture_sysfs(ts_data->dev);
memset(&fts_gesture_data, 0, sizeof(struct fts_gesture_st));
fts_gesture_data.mode = ENABLE;
fts_gesture_data.active = DISABLE;
FTS_FUNC_EXIT();
return 0;
}
int fts_gesture_exit(struct fts_ts_data *ts_data)
{
FTS_FUNC_ENTER();
sysfs_remove_group(&ts_data->dev->kobj, &fts_gesture_group);
FTS_FUNC_EXIT();
return 0;
}
#endif
@@ -0,0 +1,287 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2016 MediaTek Inc.
*/
/************************************************************************
*
* File Name: focaltech_i2c.c
*
* Author: Focaltech Driver Team
*
* Created: 2016-08-04
*
* Abstract: i2c communication with TP
*
* Version: v1.0
*
* Revision History:
*
************************************************************************/
/*****************************************************************************
* Included header files
*****************************************************************************/
#include "focaltech_core.h"
/*****************************************************************************
* Private constant and macro definitions using #define
*****************************************************************************/
#define DMA_BUFFER_LENGTH 256
#define I2C_RETRY_NUMBER 3
/*****************************************************************************
* Private enumerations, structures and unions using typedef
*****************************************************************************/
/*****************************************************************************
* Static variables
*****************************************************************************/
#ifdef CONFIG_MTK_I2C_EXTENSION
u8 *g_dma_buff_va;
dma_addr_t g_dma_buff_pa;
#endif
/*****************************************************************************
* Global variable or extern global variabls/functions
*****************************************************************************/
/*****************************************************************************
* Static function prototypes
*****************************************************************************/
/*****************************************************************************
* functions body
*****************************************************************************/
#ifdef CONFIG_MTK_I2C_EXTENSION
int fts_read(u8 *cmd, u32 cmdlen, u8 *data, u32 datalen)
{
int ret = 0;
int i = 0;
struct i2c_client *client = NULL;
/* must have data when read */
if (!fts_data || !fts_data->client || !data || !datalen) {
FTS_ERROR("fts_data/client/data/datalen(%d) is invalid",
datalen);
return -EINVAL;
}
client = fts_data->client;
mutex_lock(&fts_data->bus_lock);
if ((writelen > 0) && (writelen <= DMA_BUFFER_LENGTH)) {
memcpy(g_dma_buff_va, cmd, cmdlen);
client->addr = ((client->addr & I2C_MASK_FLAG) | I2C_DMA_FLAG);
for (i = 0; i < I2C_RETRY_NUMBER; i++) {
ret = i2c_master_send(client,
(unsigned char *)g_dma_buff_pa,
writelen);
if (ret != writelen) {
FTS_ERROR(
"[IIC]: i2c_master_send failed, ret=%d!!",
ret);
} else
break;
}
client->addr = client->addr & I2C_MASK_FLAG & (~I2C_DMA_FLAG);
}
if ((readlen > 0) && (readlen <= DMA_BUFFER_LENGTH)) {
client->addr = ((client->addr & I2C_MASK_FLAG) | I2C_DMA_FLAG);
for (i = 0; i < I2C_RETRY_NUMBER; i++) {
ret = i2c_master_recv(client,
(unsigned char *)g_dma_buff_pa,
datalen);
if (ret != readlen)
FTS_ERROR("i2c_master_recv failed,ret=%d", ret);
else
break;
}
memcpy(data, g_dma_buff_va, readlen);
client->addr = client->addr & I2C_MASK_FLAG & (~I2C_DMA_FLAG);
} else
FTS_ERROR("i2c read len(%d) fail", readlen);
mutex_unlock(&fts_data->bus_lock);
return ret;
}
int fts_i2c_write(u8 *writebuf, int writelen)
{
int ret = 0;
int i = 0;
struct i2c_client *client = NULL;
if (!fts_data || !fts_data->client || !writebuf || !writelen) {
FTS_ERROR("fts_data/client/data/datalen(%d) is invalid",
writelen);
return -EINVAL;
}
client = fts_data->client;
mutex_lock(&fts_data->bus_lock);
if ((writelen > 0) && (writelen <= DMA_BUFFER_LENGTH)) {
memcpy(g_dma_buff_va, writebuf, writelen);
client->addr = ((client->addr & I2C_MASK_FLAG) | I2C_DMA_FLAG);
for (i = 0; i < I2C_RETRY_NUMBER; i++) {
ret = i2c_master_send(client,
(unsigned char *)g_dma_buff_pa,
writelen);
if (ret != writelen)
FTS_ERROR("i2c_master_send failed,ret=%d", ret);
else
break;
}
client->addr = client->addr & I2C_MASK_FLAG & (~I2C_DMA_FLAG);
} else
FTS_ERROR("i2c write len(%d) fail", writelen);
mutex_unlock(&fts_data->bus_lock);
return ret;
}
static void fts_i2c_msg_dma_alloct(void)
{
FTS_FUNC_ENTER();
if (g_dma_buff_va == NULL) {
fts_data->input_dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
g_dma_buff_va = (u8 *)dma_alloc_coherent(
&fts_data->input_dev->dev, DMA_BUFFER_LENGTH,
&g_dma_buff_pa, GFP_KERNEL);
if (!g_dma_buff_va)
FTS_ERROR("Allocate DMA I2C Buffer failed");
}
FTS_FUNC_EXIT();
}
static void fts_i2c_msg_dma_release(void)
{
FTS_FUNC_ENTER();
if (g_dma_buff_va) {
dma_free_coherent(NULL, DMA_BUFFER_LENGTH, g_dma_buff_va,
g_dma_buff_pa);
g_dma_buff_va = NULL;
g_dma_buff_pa = 0;
FTS_ERROR("[IIC]: Allocated DMA I2C Buffer release!!");
}
FTS_FUNC_EXIT();
}
#else
int fts_read(u8 *cmd, u32 cmdlen, u8 *data, u32 datalen)
{
int ret = 0;
int i = 0;
struct i2c_client *client = NULL;
struct i2c_msg msg_list[2];
struct i2c_msg *msg = NULL;
int msg_num = 0;
/* must have data when read */
if (!fts_data || !fts_data->client || !data || !datalen) {
FTS_ERROR("fts_data/client/data/datalen(%d) is invalid",
datalen);
return -EINVAL;
}
client = fts_data->client;
mutex_lock(&fts_data->bus_lock);
memset(&msg_list[0], 0, sizeof(struct i2c_msg));
memset(&msg_list[1], 0, sizeof(struct i2c_msg));
msg_list[0].addr = client->addr;
msg_list[0].flags = 0;
msg_list[0].len = cmdlen;
msg_list[0].buf = cmd;
msg_list[1].addr = client->addr;
msg_list[1].flags = I2C_M_RD;
msg_list[1].len = datalen;
msg_list[1].buf = data;
if (cmd && cmdlen) {
msg = &msg_list[0];
msg_num = 2;
} else {
msg = &msg_list[1];
msg_num = 1;
}
for (i = 0; i < I2C_RETRY_NUMBER; i++) {
ret = i2c_transfer(client->adapter, msg, msg_num);
if (ret < 0)
FTS_ERROR("i2c_transfer(read) fail,ret:%d", ret);
else
break;
}
mutex_unlock(&fts_data->bus_lock);
return ret;
}
int fts_write(u8 *writebuf, u32 writelen)
{
int ret = 0;
int i = 0;
struct i2c_client *client = NULL;
struct i2c_msg msgs;
if (!fts_data || !fts_data->client || !writebuf || !writelen) {
FTS_ERROR("fts_data/client/data/datalen(%d) is invalid",
writelen);
return -EINVAL;
}
client = fts_data->client;
mutex_lock(&fts_data->bus_lock);
memset(&msgs, 0, sizeof(struct i2c_msg));
msgs.addr = client->addr;
msgs.flags = 0;
msgs.len = writelen;
msgs.buf = writebuf;
for (i = 0; i < I2C_RETRY_NUMBER; i++) {
ret = i2c_transfer(client->adapter, &msgs, 1);
if (ret < 0)
FTS_ERROR("i2c_transfer(write) fail,reg:%d", ret);
else
break;
}
mutex_unlock(&fts_data->bus_lock);
return ret;
}
int fts_read_reg(u8 addr, u8 *value)
{
return fts_read(&addr, 1, value, 1);
}
int fts_write_reg(u8 addr, u8 value)
{
u8 buf[2] = {0};
buf[0] = addr;
buf[1] = value;
return fts_write(buf, sizeof(buf));
}
#endif
int fts_bus_init(struct fts_ts_data *ts_data)
{
FTS_FUNC_ENTER();
#ifdef CONFIG_MTK_I2C_EXTENSION
fts_i2c_msg_dma_alloct();
#endif
ts_data->bus_buf = NULL;
FTS_FUNC_EXIT();
return 0;
}
int fts_bus_exit(struct fts_ts_data *ts_data)
{
FTS_FUNC_ENTER();
#ifdef CONFIG_MTK_I2C_EXTENSION
fts_i2c_msg_dma_release();
#endif
FTS_FUNC_EXIT();
return 0;
}
@@ -0,0 +1,123 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2016 MediaTek Inc.
*/
/*****************************************************************************
*
* File Name: focaltech_point_report_check.c
*
* Author: Focaltech Driver Team
*
* Created: 2016-11-16
*
* Abstract: point report check function
*
* Version: v1.0
*
* Revision History:
*
*****************************************************************************/
/*****************************************************************************
* Included header files
*****************************************************************************/
#include "focaltech_core.h"
#if FTS_POINT_REPORT_CHECK_EN
/*****************************************************************************
* Private constant and macro definitions using #define
*****************************************************************************/
#define POINT_REPORT_CHECK_WAIT_TIME 200 /* unit:ms */
/*****************************************************************************
* functions body
*****************************************************************************/
/*****************************************************************************
* Name: fts_prc_func
* Brief: fts point report check work func, report whole up of points
* Input:
* Output:
* Return:
*****************************************************************************/
static void fts_prc_func(struct work_struct *work)
{
struct fts_ts_data *ts_data =
container_of(work, struct fts_ts_data, prc_work.work);
struct input_dev *input_dev = ts_data->input_dev;
#if FTS_MT_PROTOCOL_B_EN
u32 finger_count = 0;
u32 max_touches = fts_data->pdata->max_touch_number;
#endif
FTS_FUNC_ENTER();
mutex_lock(&ts_data->report_mutex);
#if FTS_MT_PROTOCOL_B_EN
for (finger_count = 0; finger_count < max_touches; finger_count++) {
input_mt_slot(input_dev, finger_count);
input_mt_report_slot_state(input_dev, MT_TOOL_FINGER, false);
}
#else
input_mt_sync(input_dev);
#endif
input_report_key(input_dev, BTN_TOUCH, 0);
input_sync(input_dev);
mutex_unlock(&ts_data->report_mutex);
FTS_FUNC_EXIT();
}
/*****************************************************************************
* Name: fts_prc_queue_work
* Brief: fts point report check queue work, call it when interrupt comes
* Input:
* Output:
* Return:
*****************************************************************************/
void fts_prc_queue_work(struct fts_ts_data *ts_data)
{
cancel_delayed_work_sync(&ts_data->prc_work);
queue_delayed_work(ts_data->ts_workqueue, &ts_data->prc_work,
msecs_to_jiffies(POINT_REPORT_CHECK_WAIT_TIME));
}
/*****************************************************************************
* Name: fts_point_report_check_init
* Brief:
* Input:
* Output:
* Return: < 0: Fail to create esd check queue
*****************************************************************************/
int fts_point_report_check_init(struct fts_ts_data *ts_data)
{
FTS_FUNC_ENTER();
if (ts_data->ts_workqueue) {
INIT_DELAYED_WORK(&ts_data->prc_work, fts_prc_func);
} else {
FTS_ERROR(
"fts workqueue is NULL, can't run point report check function");
return -EINVAL;
}
FTS_FUNC_EXIT();
return 0;
}
/*****************************************************************************
* Name: fts_point_report_check_exit
* Brief:
* Input:
* Output:
* Return:
*****************************************************************************/
int fts_point_report_check_exit(struct fts_ts_data *ts_data)
{
FTS_FUNC_ENTER();
FTS_FUNC_EXIT();
return 0;
}
#endif /* FTS_POINT_REPORT_CHECK_EN */
@@ -0,0 +1,368 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2016 MediaTek Inc.
*/
/*****************************************************************************
*
* File Name: focaltech_proximity.c
*
* Author: Focaltech Driver Team
*
* Created: 2016-09-19
*
* Abstract: close proximity function
*
* Version: v1.0
*
* Revision History:
* v1.0:
* First release based on xiaguobin's solution. By luougojin
*2016-08-19
*****************************************************************************/
/*****************************************************************************
* Included header files
*****************************************************************************/
#include "focaltech_common.h"
#include "focaltech_core.h"
#if FTS_PSENSOR_EN
#include <alsps.h>
#include <hwmsen_dev.h>
#include <hwmsensor.h>
#include <sensors_io.h>
/*****************************************************************************
* Private constant and macro definitions using #define
*****************************************************************************/
/*
* FTS_ALSPS_SUPPORT is choose structure hwmsen_object or control_path,
* data_path
* FTS_ALSPS_SUPPORT = 1, is control_path, data_path
* FTS_ALSPS_SUPPORT = 0, hwmsen_object
*/
#define FTS_ALSPS_SUPPORT 1
#define PS_FAR_AWAY 1
#define PS_NEAR 0
/*****************************************************************************
* Private enumerations, structures and unions using typedef
*****************************************************************************/
struct fts_proximity_st {
u8 mode : 1; /* 1- proximity enable 0- disable */
u8 detect : 1; /* 0-->close ; 1--> far away */
u8 unused : 4;
};
/*****************************************************************************
* Static variables
*****************************************************************************/
static struct fts_proximity_st fts_proximity_data;
/*****************************************************************************
* Global variable or extern global variabls/functions
*****************************************************************************/
/*****************************************************************************
* Static function prototypes
*****************************************************************************/
/************************************************************************
* Name: fts_enter_proximity_mode
* Brief: change proximity mode
* Input: proximity mode
* Output: no
* Return: success =0
***********************************************************************/
static int fts_enter_proximity_mode(int mode)
{
int ret = 0;
u8 buf_addr = 0;
u8 buf_value = 0;
buf_addr = FTS_REG_FACE_DEC_MODE_EN;
if (mode)
buf_value = 0x01;
else
buf_value = 0x00;
ret = fts_write_reg(buf_addr, buf_value);
if (ret < 0) {
FTS_ERROR("[PROXIMITY] Write proximity register(0xB0) fail!");
return ret;
}
fts_proximity_data.mode = buf_value ? ENABLE : DISABLE;
FTS_DEBUG("[PROXIMITY] proximity mode = %d", fts_proximity_data.mode);
return 0;
}
/*****************************************************************************
* Name: fts_proximity_recovery
* Brief: need call when reset
* Input:
* Output:
* Return:
*****************************************************************************/
int fts_proximity_recovery(struct fts_ts_data *ts_data)
{
int ret = 0;
if (fts_proximity_data.mode)
ret = fts_enter_proximity_mode(ENABLE);
return ret;
}
#if FTS_ALSPS_SUPPORT
static int ps_open_report_data(int open)
{
/* should queue work to report event if is_report_input_direct=true */
return 0;
}
static int ps_enable_nodata(int en)
{
int err = 0;
FTS_DEBUG("[PROXIMITY]SENSOR_ENABLE value = %d", en);
/* Enable proximity */
mutex_lock(&fts_data->input_dev->mutex);
err = fts_enter_proximity_mode(en);
mutex_unlock(&fts_data->input_dev->mutex);
return err;
}
static int ps_set_delay(u64 ns)
{
return 0;
}
static int ps_get_data(int *value, int *status)
{
*value = (int)fts_proximity_data.detect;
FTS_DEBUG("fts_proximity_data.detect = %d\n", *value);
*status = SENSOR_STATUS_ACCURACY_MEDIUM;
return 0;
}
int ps_local_init(void)
{
int err = 0;
struct ps_control_path ps_ctl = {0};
struct ps_data_path ps_data = {0};
ps_ctl.is_use_common_factory = false;
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 = false;
ps_ctl.is_support_batch = false;
err = ps_register_control_path(&ps_ctl);
if (err)
FTS_ERROR("register fail = %d\n", err);
ps_data.get_data = ps_get_data;
ps_data.vender_div = 100;
err = ps_register_data_path(&ps_data);
if (err)
FTS_ERROR("tregister fail = %d\n", err);
return err;
}
int ps_local_uninit(void)
{
return 0;
}
struct alsps_init_info ps_init_info = {
.name = "fts_ts", .init = ps_local_init, .uninit = ps_local_uninit,
};
#else
/*****************************************************************************
* Name: fts_ps_operate
* Brief:
* Input:
* Output:
* Return:
*****************************************************************************/
static int fts_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;
struct hwm_sensor_data *sensor_data;
FTS_DEBUG("[PROXIMITY]COMMAND = %d", command);
switch (command) {
case SENSOR_DELAY:
if ((buff_in == NULL) || (size_in < sizeof(int))) {
FTS_ERROR("[PROXIMITY]Set delay parameter error!");
err = -EINVAL;
}
break;
case SENSOR_ENABLE:
if ((buff_in == NULL) || (size_in < sizeof(int))) {
FTS_ERROR("[PROXIMITY]Enable sensor parameter error!");
err = -EINVAL;
} else {
value = *(int *)buff_in;
FTS_DEBUG("[PROXIMITY]SENSOR_ENABLE value = %d", value);
/* Enable proximity */
err = fts_enter_proximity_mode(value);
}
break;
case SENSOR_GET_DATA:
if ((buff_out == NULL) ||
(size_out < sizeof(struct hwm_sensor_data))) {
FTS_ERROR(
"[PROXIMITY]get sensor data parameter error!");
err = -EINVAL;
} else {
sensor_data = (struct hwm_sensor_data *)buff_out;
sensor_data->values[0] = (int)fts_proximity_data.detect;
FTS_DEBUG("sensor_data->values[0] = %d",
sensor_data->values[0]);
sensor_data->value_divide = 1;
sensor_data->status = SENSOR_STATUS_ACCURACY_MEDIUM;
}
break;
default:
FTS_ERROR("[PROXIMITY]ps has no operate function:%d!", command);
err = -EPERM;
break;
}
return err;
}
#endif
/*****************************************************************************
* Name: fts_proximity_readdata
* Brief:
* Input:
* Output:
* Return: 0 - need return in suspend
*****************************************************************************/
int fts_proximity_readdata(struct fts_ts_data *ts_data)
{
int ret;
int proximity_status = 1;
u8 regvalue;
#if !FTS_ALSPS_SUPPORT
struct hwm_sensor_data sensor_data;
#endif
if (fts_proximity_data.mode == DISABLE)
return -EPERM;
fts_read_reg(FTS_REG_FACE_DEC_MODE_STATUS, &regvalue);
if (regvalue == 0xC0) {
/* close. need lcd off */
proximity_status = PS_NEAR;
} else if (regvalue == 0xE0) {
/* far away */
proximity_status = PS_FAR_AWAY;
}
FTS_INFO("fts_proximity_data.detect is %d", fts_proximity_data.detect);
if (proximity_status != (int)fts_proximity_data.detect) {
FTS_DEBUG("[PROXIMITY] p-sensor state:%s",
proximity_status ? "AWAY" : "NEAR");
fts_proximity_data.detect =
proximity_status ? PS_FAR_AWAY : PS_NEAR;
#if FTS_ALSPS_SUPPORT
ret = ps_report_interrupt_data(fts_proximity_data.detect);
#else
sensor_data.values[0] = proximity_status;
sensor_data.value_divide = 1;
sensor_data.status = SENSOR_STATUS_ACCURACY_MEDIUM;
ret = hwmsen_get_interrupt_data(ID_PROXIMITY, &sensor_data);
if (ret) {
FTS_ERROR(
"[PROXIMITY] Call hwmsen_get_interrupt_data failed, ret=%d",
ret);
return ret;
}
#endif
return 0;
}
return -1;
}
/*****************************************************************************
* Name: fts_proximity_suspend
* Brief: Run when tp enter into suspend
* Input:
* Output:
* Return: 0 - need return in suspend
*****************************************************************************/
int fts_proximity_suspend(void)
{
if (fts_proximity_data.mode == ENABLE)
return 0;
else
return -1;
}
/*****************************************************************************
* Name: fts_proximity_resume
* Brief: Run when tp resume
* Input:
* Output:
* Return:
*****************************************************************************/
int fts_proximity_resume(void)
{
if (fts_proximity_data.mode == ENABLE)
return 0;
else
return -1;
}
int fts_proximity_init(void)
{
#if !FTS_ALSPS_SUPPORT
int err = 0;
struct hwmsen_object obj_ps;
#endif
FTS_FUNC_ENTER();
memset((u8 *)&fts_proximity_data, 0, sizeof(struct fts_proximity_st));
fts_proximity_data.detect = PS_FAR_AWAY; /* defalut far awway */
#if FTS_ALSPS_SUPPORT
alsps_driver_add(&ps_init_info);
#else
obj_ps.polling = 0; /* interrupt mode */
obj_ps.sensor_operate = fts_ps_operate;
err = hwmsen_attach(ID_PROXIMITY, &obj_ps);
if (err)
FTS_ERROR("[PROXIMITY]fts proximity attach fail = %d!", err);
else
FTS_INFO("[PROXIMITY]fts proximity attach ok = %d\n", err);
#endif
FTS_FUNC_EXIT();
return 0;
}
int fts_proximity_exit(void)
{
return 0;
}
#endif /* FTS_PSENSOR_EN */
@@ -0,0 +1,55 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2019 MediaTek Inc.
*/
#undef TRACE_SYSTEM
#define TRACE_SYSTEM met_touch
#if !defined(__TRACE_MET_FTRACE_TOUCH_H__) || defined(TRACE_HEADER_MULTI_READ)
#define __TRACE_MET_FTRACE_TOUCH_H__
#include <linux/tracepoint.h>
/*
* Tracepoint for MET_touch
*/
TRACE_EVENT(MET_touch,
TP_PROTO(char *touch_type, long tsec, long tusec,
char *mode, int value),
TP_ARGS(touch_type, tsec, tusec, mode, value),
TP_STRUCT__entry(
__array(char, _touch_type, 16)
__field(long, _tsec)
__field(long, _tusec)
__array(char, _mode, 16)
__field(int, _value)
),
TP_fast_assign(
memcpy(__entry->_touch_type, touch_type, 16);
__entry->_tsec = tsec;
__entry->_tusec = tusec;
memcpy(__entry->_mode, mode, 16);
__entry->_value = value;
),
TP_printk("%s,%ld.%06ld,%s,%x",
__entry->_touch_type,
__entry->_tsec,
__entry->_tusec,
__entry->_mode,
__entry->_value
)
);
#endif /* __TRACE_MET_FTRACE_TOUCH_H__ */
/* This part must be outside protection */
#undef TRACE_INCLUDE_PATH
#undef linux
#define TRACE_INCLUDE_PATH .
#define TRACE_INCLUDE_FILE met_ftrace_touch
#include <trace/define_trace.h>
+182
View File
@@ -0,0 +1,182 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2019 MediaTek Inc.
*/
#ifndef __TPD_H
#define __TPD_H
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/time.h>
#include <linux/types.h>
#include <linux/seq_file.h>
#include <linux/list.h>
#include <linux/proc_fs.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <generated/autoconf.h>
#include <linux/kobject.h>
#include <linux/regulator/consumer.h>
#include <linux/pinctrl/consumer.h>
/*debug macros */
#define TPD_DEBUG
#define TPD_DEBUG_CODE
/* #define TPD_DEBUG_TRACK */
#define TPD_DMESG(a, arg...) \
pr_info(TPD_DEVICE ":[%s:%d] " a, __func__, __LINE__, ##arg)
#if defined(TPD_DEBUG)
#undef TPD_DEBUG
#define TPD_DEBUG(a, arg...) \
pr_info(TPD_DEVICE ":[%s:%d] " a, __func__, __LINE__, ##arg)
#else
#define TPD_DEBUG(arg...)
#endif
#define SPLIT ", "
/* register, address, configurations */
#define TPD_DEVICE "mtk-tpd"
#define TPD_X 0
#define TPD_Y 1
#define TPD_Z1 2
#define TPD_Z2 3
#define TP_DELAY (2*HZ/100)
#define TP_DRV_MAX_COUNT (20)
#define TPD_WARP_CNT (4)
#define TPD_VIRTUAL_KEY_MAX (10)
/* various mode */
#define TPD_MODE_NORMAL 0
#define TPD_MODE_KEYPAD 1
#define TPD_MODE_SW 2
#define TPD_MODE_FAV_SW 3
#define TPD_MODE_FAV_HW 4
#define TPD_MODE_RAW_DATA 5
#undef TPD_RES_X
#undef TPD_RES_Y
extern unsigned long TPD_RES_X;
extern unsigned long TPD_RES_Y;
extern int tpd_load_status; /* 0: failed, 1: success */
extern int tpd_mode;
extern int tpd_mode_axis;
extern int tpd_mode_min;
extern int tpd_mode_max;
extern int tpd_mode_keypad_tolerance;
extern int tpd_em_debounce_time;
extern int tpd_em_debounce_time0;
extern int tpd_em_debounce_time1;
extern int tpd_em_asamp;
extern int tpd_em_auto_time_interval;
extern int tpd_em_sample_cnt;
extern int tpd_calmat[];
extern int tpd_def_calmat[];
extern int tpd_calmat[];
extern int tpd_def_calmat[];
extern int TPD_DO_WARP;
extern int tpd_wb_start[];
extern int tpd_wb_end[];
extern int tpd_v_magnify_x;
extern int tpd_v_magnify_y;
extern unsigned int DISP_GetScreenHeight(void);
extern unsigned int DISP_GetScreenWidth(void);
#if defined(CONFIG_MTK_S3320) || defined(CONFIG_MTK_S3320_47) || \
defined(CONFIG_MTK_S3320_50)
extern void synaptics_init_sysfs(void);
#endif /* CONFIG_MTK_S3320 */
extern void tpd_button_init(void);
struct tpd_device {
struct device *tpd_dev;
struct regulator *reg;
struct regulator *io_reg;
struct input_dev *dev;
struct input_dev *kpd;
struct timer_list timer;
struct tasklet_struct tasklet;
int btn_state;
};
struct tpd_key_dim_local {
int key_x;
int key_y;
int key_width;
int key_height;
};
struct tpd_filter_t {
int enable; /*0: disable, 1: enable*/
int pixel_density; /*XXX pixel/cm*/
int W_W[3][4];/*filter custom setting prameters*/
unsigned int VECLOCITY_THRESHOLD[3];/*filter speed custom settings*/
};
struct tpd_dts_info {
int tpd_resolution[2];
int touch_max_num;
int use_tpd_button;
int tpd_key_num;
int tpd_key_local[4];
bool tpd_use_ext_gpio;
int rst_ext_gpio_num;
struct tpd_key_dim_local tpd_key_dim_local[4];
struct tpd_filter_t touch_filter;
};
extern struct tpd_dts_info tpd_dts_data;
struct tpd_attrs {
struct device_attribute **attr;
int num;
};
struct tpd_driver_t {
char *tpd_device_name;
int (*tpd_local_init)(void);
void (*suspend)(struct device *h);
void (*resume)(struct device *h);
int tpd_have_button;
struct tpd_attrs attrs;
};
/* #ifdef TPD_HAVE_BUTTON */
void tpd_button(unsigned int x, unsigned int y, unsigned int down);
void tpd_button_init(void);
ssize_t tpd_virtual_key(char *buf);
/* #ifndef TPD_BUTTON_HEIGHT */
/* #define TPD_BUTTON_HEIGHT TPD_RES_Y */
/* #endif */
extern int tpd_driver_add(struct tpd_driver_t *tpd_drv);
extern int tpd_driver_remove(struct tpd_driver_t *tpd_drv);
void tpd_button_setting(int keycnt, void *keys, void *keys_dim);
extern int tpd_em_spl_num;
extern int tpd_em_pressure_threshold;
extern struct tpd_device *tpd;
extern void tpd_get_dts_info(void);
#define GTP_RST_PORT 0
#define GTP_INT_PORT 1
extern void tpd_gpio_as_int(int pin);
extern void tpd_gpio_output(int pin, int level);
extern const struct of_device_id touch_of_match[];
#ifdef TPD_DEBUG_CODE
#include "tpd_debug.h"
#endif
#ifdef TPD_DEBUG_TRACK
int DAL_Clean(void);
int DAL_Printf(const char *fmt, ...);
int LCD_LayerEnable(int id, BOOL enable);
#endif
#ifdef TPD_HAVE_CALIBRATION
#include "tpd_calibrate.h"
#endif
#include "tpd_default.h"
/* switch touch panel into different mode */
void _tpd_switch_single_mode(void);
void _tpd_switch_multiple_mode(void);
void _tpd_switch_sleep_mode(void);
void _tpd_switch_normal_mode(void);
#endif
@@ -0,0 +1,123 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 MediaTek Inc.
*/
#include "tpd.h"
/* #ifdef TPD_HAVE_BUTTON */
/* static int tpd_keys[TPD_KEY_COUNT] = TPD_KEYS; */
/* static int tpd_keys_dim[TPD_KEY_COUNT][4] = TPD_KEYS_DIM; */
static unsigned int tpd_keycnt;
static int tpd_keys[TPD_VIRTUAL_KEY_MAX] = { 0 };
static int tpd_keys_dim[TPD_VIRTUAL_KEY_MAX][4]; /* = {0}; */
static ssize_t mtk_virtual_keys_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
int i, j;
for (i = 0, j = 0; i < tpd_keycnt; i++)
j += snprintf(buf+j, PAGE_SIZE-j, "%s%s:%d:%d:%d:%d:%d%s", buf,
__stringify(EV_KEY), tpd_keys[i],
tpd_keys_dim[i][0], tpd_keys_dim[i][1],
tpd_keys_dim[i][2], tpd_keys_dim[i][3],
(i == tpd_keycnt - 1 ? "\n" : ":"));
return j;
}
static struct kobj_attribute mtk_virtual_keys_attr = {
.attr = {
.name = "virtualkeys.mtk-tpd",
.mode = 0644,
},
.show = &mtk_virtual_keys_show,
};
static struct attribute *mtk_properties_attrs[] = {
&mtk_virtual_keys_attr.attr,
NULL
};
static struct attribute_group mtk_properties_attr_group = {
.attrs = mtk_properties_attrs,
};
struct kobject *properties_kobj;
void tpd_button_init(void)
{
int ret = 0, i = 0;
/* if((tpd->kpd=input_allocate_device())==NULL) return -ENOMEM; */
tpd->kpd = input_allocate_device();
/* struct input_dev kpd initialization and registration */
tpd->kpd->name = TPD_DEVICE "-kpd";
set_bit(EV_KEY, tpd->kpd->evbit);
/* set_bit(EV_REL, tpd->kpd->evbit); */
/* set_bit(EV_ABS, tpd->kpd->evbit); */
for (i = 0; i < tpd_keycnt; i++)
__set_bit(tpd_keys[i], tpd->kpd->keybit);
tpd->kpd->id.bustype = BUS_HOST;
tpd->kpd->id.vendor = 0x0001;
tpd->kpd->id.product = 0x0001;
tpd->kpd->id.version = 0x0100;
if (input_register_device(tpd->kpd))
TPD_DMESG("input_register_device failed.(kpd)\n");
set_bit(EV_KEY, tpd->dev->evbit);
for (i = 0; i < tpd_keycnt; i++)
__set_bit(tpd_keys[i], tpd->dev->keybit);
properties_kobj = kobject_create_and_add("board_properties", NULL);
if (properties_kobj)
ret = sysfs_create_group(properties_kobj,
&mtk_properties_attr_group);
if (!properties_kobj || ret)
TPD_DEBUG("failed to create board_properties\n");
}
void tpd_button(unsigned int x, unsigned int y, unsigned int down)
{
int i;
bool report;
if (down) {
for (i = 0; i < tpd_keycnt; i++) {
report = x >= tpd_keys_dim[i][0] -
(tpd_keys_dim[i][2] / 2) &&
x <= tpd_keys_dim[i][0] +
(tpd_keys_dim[i][2] / 2) &&
y >= tpd_keys_dim[i][1] -
(tpd_keys_dim[i][3] / 2) &&
y <= tpd_keys_dim[i][1] +
(tpd_keys_dim[i][3] / 2) &&
!(tpd->btn_state & (1 << i));
if (report) {
input_report_key(tpd->kpd, tpd_keys[i], 1);
input_sync(tpd->kpd);
tpd->btn_state |= (1 << i);
TPD_DEBUG("press key %d (%d)\n",
i, tpd_keys[i]);
}
}
} else {
for (i = 0; i < tpd_keycnt; i++) {
if (tpd->btn_state & (1 << i)) {
input_report_key(tpd->kpd, tpd_keys[i], 0);
input_sync(tpd->kpd);
TPD_DEBUG("release key %d (%d)\n",
i, tpd_keys[i]);
}
}
tpd->btn_state = 0;
}
}
void tpd_button_setting(int keycnt, void *keys, void *keys_dim)
{
tpd_keycnt = keycnt;
memcpy(tpd_keys, keys, keycnt * 4);
memcpy(tpd_keys_dim, keys_dim, keycnt * 4 * 4);
}
/* #endif */
@@ -0,0 +1,750 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 MediaTek Inc.
*/
#include "tpd.h"
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/fb.h>
#ifdef CONFIG_MTK_MT6306_GPIO_SUPPORT
#include <mtk_6306_gpio.h>
#endif
#ifdef CONFIG_COMPAT
#include <linux/compat.h>
#endif
#if defined(CONFIG_MTK_S3320) || defined(CONFIG_MTK_S3320_50) \
|| defined(CONFIG_MTK_S3320_47) || defined(CONFIG_MTK_MIT200) \
|| defined(CONFIG_TOUCHSCREEN_SYNAPTICS_S3528) \
|| defined(CONFIG_MTK_S7020) \
|| defined(CONFIG_TOUCHSCREEN_MTK_SYNAPTICS_3320_50)
#include <linux/input/mt.h>
#endif /* CONFIG_MTK_S3320 */
/* for magnify velocity******************************************** */
#define TOUCH_IOC_MAGIC 'A'
#define TPD_GET_VELOCITY_CUSTOM_X _IO(TOUCH_IOC_MAGIC, 0)
#define TPD_GET_VELOCITY_CUSTOM_Y _IO(TOUCH_IOC_MAGIC, 1)
#define TPD_GET_FILTER_PARA _IOWR(TOUCH_IOC_MAGIC, 2, struct tpd_filter_t)
#ifdef CONFIG_COMPAT
#define COMPAT_TPD_GET_FILTER_PARA _IOWR(TOUCH_IOC_MAGIC, \
2, struct tpd_filter_t)
#endif
struct tpd_filter_t tpd_filter;
struct tpd_dts_info tpd_dts_data;
struct pinctrl *pinctrl1;
struct pinctrl_state *pins_default;
struct pinctrl_state *eint_as_int, *eint_output0,
*eint_output1, *rst_output0, *rst_output1;
const struct of_device_id touch_of_match[] = {
{ .compatible = "goodix,touch", },
{},
};
void tpd_get_dts_info(void)
{
struct device_node *node1 = NULL;
int key_dim_local[16], i;
node1 = of_find_matching_node(node1, touch_of_match);
if (node1) {
of_property_read_u32(node1,
"tpd-max-touch-num", &tpd_dts_data.touch_max_num);
of_property_read_u32(node1,
"use-tpd-button", &tpd_dts_data.use_tpd_button);
pr_debug("[tpd]use-tpd-button = %d\n",
tpd_dts_data.use_tpd_button);
if (of_property_read_u32_array(node1, "tpd-resolution",
tpd_dts_data.tpd_resolution,
ARRAY_SIZE(tpd_dts_data.tpd_resolution))) {
pr_debug("[tpd] resulution is %d %d",
tpd_dts_data.tpd_resolution[0],
tpd_dts_data.tpd_resolution[1]);
}
if (tpd_dts_data.use_tpd_button) {
of_property_read_u32(node1,
"tpd-key-num", &tpd_dts_data.tpd_key_num);
if (of_property_read_u32_array(node1, "tpd-key-local",
tpd_dts_data.tpd_key_local,
ARRAY_SIZE(tpd_dts_data.tpd_key_local)))
pr_debug("tpd-key-local: %d %d %d %d",
tpd_dts_data.tpd_key_local[0],
tpd_dts_data.tpd_key_local[1],
tpd_dts_data.tpd_key_local[2],
tpd_dts_data.tpd_key_local[3]);
if (of_property_read_u32_array(node1,
"tpd-key-dim-local",
key_dim_local, ARRAY_SIZE(key_dim_local))) {
memcpy(tpd_dts_data.tpd_key_dim_local,
key_dim_local, sizeof(key_dim_local));
for (i = 0; i < 4; i++) {
pr_debug("[tpd]key[%d].key_x = %d\n", i,
tpd_dts_data
.tpd_key_dim_local[i]
.key_x);
pr_debug("[tpd]key[%d].key_y = %d\n", i,
tpd_dts_data
.tpd_key_dim_local[i]
.key_y);
pr_debug("[tpd]key[%d].key_W = %d\n", i,
tpd_dts_data
.tpd_key_dim_local[i]
.key_width);
pr_debug("[tpd]key[%d].key_H = %d\n", i,
tpd_dts_data
.tpd_key_dim_local[i]
.key_height);
}
}
}
of_property_read_u32(node1, "tpd-filter-enable",
&tpd_dts_data.touch_filter.enable);
if (tpd_dts_data.touch_filter.enable) {
of_property_read_u32(node1,
"tpd-filter-pixel-density",
&tpd_dts_data.touch_filter.pixel_density);
if (of_property_read_u32_array(node1,
"tpd-filter-custom-prameters",
(u32 *)tpd_dts_data.touch_filter.W_W,
ARRAY_SIZE(tpd_dts_data.touch_filter.W_W)))
pr_debug("get tpd-filter-custom-parameters");
if (of_property_read_u32_array(node1,
"tpd-filter-custom-speed",
tpd_dts_data.touch_filter.VECLOCITY_THRESHOLD,
ARRAY_SIZE(tpd_dts_data
.touch_filter
.VECLOCITY_THRESHOLD)))
pr_debug("get tpd-filter-custom-speed");
}
memcpy(&tpd_filter,
&tpd_dts_data.touch_filter, sizeof(tpd_filter));
pr_debug("[tpd]tpd-filter-enable = %d, pixel_density = %d\n",
tpd_filter.enable, tpd_filter.pixel_density);
tpd_dts_data.tpd_use_ext_gpio =
of_property_read_bool(node1, "tpd-use-ext-gpio");
of_property_read_u32(node1,
"tpd-rst-ext-gpio-num",
&tpd_dts_data.rst_ext_gpio_num);
} else {
TPD_DMESG("can't find touch compatible custom node\n");
}
}
static DEFINE_MUTEX(tpd_set_gpio_mutex);
void tpd_gpio_as_int(int pin)
{
mutex_lock(&tpd_set_gpio_mutex);
TPD_DEBUG("[tpd]%s\n", __func__);
if (pin == 1)
pinctrl_select_state(pinctrl1, eint_as_int);
mutex_unlock(&tpd_set_gpio_mutex);
}
void tpd_gpio_output(int pin, int level)
{
mutex_lock(&tpd_set_gpio_mutex);
TPD_DEBUG("%s pin = %d, level = %d\n", __func__, pin, level);
if (pin == 1) {
if (level)
pinctrl_select_state(pinctrl1, eint_output1);
else
pinctrl_select_state(pinctrl1, eint_output0);
} else {
if (tpd_dts_data.tpd_use_ext_gpio) {
#ifdef CONFIG_MTK_MT6306_GPIO_SUPPORT
mt6306_set_gpio_dir(
tpd_dts_data.rst_ext_gpio_num, 1);
mt6306_set_gpio_out(
tpd_dts_data.rst_ext_gpio_num, level);
#endif
} else {
if (level)
pinctrl_select_state(pinctrl1, rst_output1);
else
pinctrl_select_state(pinctrl1, rst_output0);
}
}
mutex_unlock(&tpd_set_gpio_mutex);
}
int tpd_get_gpio_info(struct platform_device *pdev)
{
int ret;
TPD_DEBUG("[tpd %d] mt_tpd_pinctrl+++++++++++++++++\n", pdev->id);
pinctrl1 = devm_pinctrl_get(&pdev->dev);
if (IS_ERR(pinctrl1)) {
ret = PTR_ERR(pinctrl1);
dev_info(&pdev->dev, "fwq Cannot find pinctrl1!\n");
return ret;
}
pins_default = pinctrl_lookup_state(pinctrl1, "default");
if (IS_ERR(pins_default)) {
ret = PTR_ERR(pins_default);
TPD_DMESG("Cannot find pinctrl default %d!\n", ret);
}
eint_as_int = pinctrl_lookup_state(pinctrl1, "state_eint_as_int");
if (IS_ERR(eint_as_int)) {
ret = PTR_ERR(eint_as_int);
TPD_DMESG("Cannot find pinctrl state_eint_as_int!\n");
return ret;
}
eint_output0 = pinctrl_lookup_state(pinctrl1, "state_eint_output0");
if (IS_ERR(eint_output0)) {
ret = PTR_ERR(eint_output0);
TPD_DMESG("Cannot find pinctrl state_eint_output0!\n");
return ret;
}
eint_output1 = pinctrl_lookup_state(pinctrl1, "state_eint_output1");
if (IS_ERR(eint_output1)) {
ret = PTR_ERR(eint_output1);
TPD_DMESG("Cannot find pinctrl state_eint_output1!\n");
return ret;
}
if (tpd_dts_data.tpd_use_ext_gpio == false) {
rst_output0 =
pinctrl_lookup_state(pinctrl1, "state_rst_output0");
if (IS_ERR(rst_output0)) {
ret = PTR_ERR(rst_output0);
TPD_DMESG("Cannot find pinctrl state_rst_output0!\n");
return ret;
}
rst_output1 =
pinctrl_lookup_state(pinctrl1, "state_rst_output1");
if (IS_ERR(rst_output1)) {
ret = PTR_ERR(rst_output1);
TPD_DMESG("Cannot find pinctrl state_rst_output1!\n");
return ret;
}
}
TPD_DEBUG("[tpd%d] mt_tpd_pinctrl----------\n", pdev->id);
return 0;
}
static int tpd_misc_open(struct inode *inode, struct file *file)
{
return nonseekable_open(inode, file);
}
static int tpd_misc_release(struct inode *inode, struct file *file)
{
return 0;
}
#ifdef CONFIG_COMPAT
static long tpd_compat_ioctl(
struct file *file, unsigned int cmd,
unsigned long arg)
{
long ret;
void __user *arg32 = compat_ptr(arg);
if (!file->f_op || !file->f_op->unlocked_ioctl)
return -ENOTTY;
switch (cmd) {
case COMPAT_TPD_GET_FILTER_PARA:
if (arg32 == NULL) {
pr_info("invalid argument.");
return -EINVAL;
}
ret = file->f_op->unlocked_ioctl(file, TPD_GET_FILTER_PARA,
(unsigned long)arg32);
if (ret) {
pr_info("TPD_GET_FILTER_PARA unlocked_ioctl failed.");
return ret;
}
break;
default:
pr_info("tpd: unknown IOCTL: 0x%08x\n", cmd);
ret = -ENOIOCTLCMD;
break;
}
return ret;
}
#endif
static long tpd_unlocked_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
/* char strbuf[256]; */
void __user *data;
long err = 0;
if (_IOC_DIR(cmd) & _IOC_READ)
err = !access_ok(VERIFY_WRITE,
(void __user *)arg, _IOC_SIZE(cmd));
else if (_IOC_DIR(cmd) & _IOC_WRITE)
err = !access_ok(VERIFY_READ,
(void __user *)arg, _IOC_SIZE(cmd));
if (err) {
pr_info("tpd: access error: %08X, (%2d, %2d)\n",
cmd, _IOC_DIR(cmd), _IOC_SIZE(cmd));
return -EFAULT;
}
switch (cmd) {
case TPD_GET_VELOCITY_CUSTOM_X:
data = (void __user *)arg;
if (data == NULL) {
err = -EINVAL;
break;
}
if (copy_to_user(data,
&tpd_v_magnify_x, sizeof(tpd_v_magnify_x))) {
err = -EFAULT;
break;
}
break;
case TPD_GET_VELOCITY_CUSTOM_Y:
data = (void __user *)arg;
if (data == NULL) {
err = -EINVAL;
break;
}
if (copy_to_user(data,
&tpd_v_magnify_y, sizeof(tpd_v_magnify_y))) {
err = -EFAULT;
break;
}
break;
case TPD_GET_FILTER_PARA:
data = (void __user *) arg;
if (data == NULL) {
err = -EINVAL;
TPD_DMESG("GET_FILTER_PARA: data is null\n");
break;
}
if (copy_to_user(data, &tpd_filter,
sizeof(struct tpd_filter_t))) {
TPD_DMESG("GET_FILTER_PARA: copy data error\n");
err = -EFAULT;
break;
}
break;
default:
pr_info("tpd: unknown IOCTL: 0x%08x\n", cmd);
err = -ENOIOCTLCMD;
break;
}
return err;
}
static struct work_struct touch_resume_work;
static struct workqueue_struct *touch_resume_workqueue;
static const struct file_operations tpd_fops = {
/* .owner = THIS_MODULE, */
.open = tpd_misc_open,
.release = tpd_misc_release,
.unlocked_ioctl = tpd_unlocked_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = tpd_compat_ioctl,
#endif
};
/*---------------------------------------------------------------------------*/
static struct miscdevice tpd_misc_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = "touch",
.fops = &tpd_fops,
};
/* ********************************************** */
/* #endif */
/* function definitions */
static int __init tpd_device_init(void);
static void __exit tpd_device_exit(void);
static int tpd_probe(struct platform_device *pdev);
static int tpd_remove(struct platform_device *pdev);
static struct work_struct tpd_init_work;
static struct workqueue_struct *tpd_init_workqueue;
static int tpd_suspend_flag;
int tpd_register_flag;
/* global variable definitions */
struct tpd_device *tpd;
static struct tpd_driver_t tpd_driver_list[TP_DRV_MAX_COUNT]; /* = {0}; */
struct platform_device tpd_device = {
.name = TPD_DEVICE,
.id = -1,
};
const struct dev_pm_ops tpd_pm_ops = {
.suspend = NULL,
.resume = NULL,
};
static struct platform_driver tpd_driver = {
.remove = tpd_remove,
.shutdown = NULL,
.probe = tpd_probe,
.driver = {
.name = TPD_DEVICE,
.pm = &tpd_pm_ops,
.owner = THIS_MODULE,
.of_match_table = touch_of_match,
},
};
static struct tpd_driver_t *g_tpd_drv;
/* hh: use fb_notifier */
static struct notifier_block tpd_fb_notifier;
/* use fb_notifier */
static void touch_resume_workqueue_callback(struct work_struct *work)
{
TPD_DEBUG("GTP %s\n", __func__);
g_tpd_drv->resume(NULL);
tpd_suspend_flag = 0;
}
static int tpd_fb_notifier_callback(
struct notifier_block *self,
unsigned long event, void *data)
{
struct fb_event *evdata = NULL;
int blank;
int err = 0;
TPD_DEBUG("%s\n", __func__);
evdata = data;
/* If we aren't interested in this event, skip it immediately ... */
if (event != FB_EVENT_BLANK)
return 0;
blank = *(int *)evdata->data;
TPD_DMESG("fb_notify(blank=%d)\n", blank);
switch (blank) {
case FB_BLANK_UNBLANK:
TPD_DMESG("LCD ON Notify\n");
if (g_tpd_drv && tpd_suspend_flag) {
err = queue_work(touch_resume_workqueue,
&touch_resume_work);
if (!err) {
TPD_DMESG("start resume_workqueue failed\n");
return err;
}
}
break;
case FB_BLANK_POWERDOWN:
TPD_DMESG("LCD OFF Notify\n");
if (g_tpd_drv && !tpd_suspend_flag) {
err = cancel_work_sync(&touch_resume_work);
if (!err)
TPD_DMESG("cancel resume_workqueue failed\n");
g_tpd_drv->suspend(NULL);
}
tpd_suspend_flag = 1;
break;
default:
break;
}
return 0;
}
/* Add driver: if find TPD_TYPE_CAPACITIVE driver successfully, loading it */
int tpd_driver_add(struct tpd_driver_t *tpd_drv)
{
int i;
if (g_tpd_drv != NULL) {
TPD_DMESG("touch driver exist\n");
return -1;
}
/* check parameter */
if (tpd_drv == NULL)
return -1;
tpd_drv->tpd_have_button = tpd_dts_data.use_tpd_button;
/* R-touch */
if (strcmp(tpd_drv->tpd_device_name, "generic") == 0) {
tpd_driver_list[0].tpd_device_name = tpd_drv->tpd_device_name;
tpd_driver_list[0].tpd_local_init = tpd_drv->tpd_local_init;
tpd_driver_list[0].suspend = tpd_drv->suspend;
tpd_driver_list[0].resume = tpd_drv->resume;
tpd_driver_list[0].tpd_have_button = tpd_drv->tpd_have_button;
return 0;
}
for (i = 1; i < TP_DRV_MAX_COUNT; i++) {
/* add tpd driver into list */
if (tpd_driver_list[i].tpd_device_name == NULL) {
tpd_driver_list[i].tpd_device_name =
tpd_drv->tpd_device_name;
tpd_driver_list[i].tpd_local_init =
tpd_drv->tpd_local_init;
tpd_driver_list[i].suspend = tpd_drv->suspend;
tpd_driver_list[i].resume = tpd_drv->resume;
tpd_driver_list[i].tpd_have_button =
tpd_drv->tpd_have_button;
tpd_driver_list[i].attrs = tpd_drv->attrs;
break;
}
if (strcmp(tpd_driver_list[i].tpd_device_name,
tpd_drv->tpd_device_name) == 0)
return 1; /* driver exist */
}
return 0;
}
int tpd_driver_remove(struct tpd_driver_t *tpd_drv)
{
int i = 0;
/* check parameter */
if (tpd_drv == NULL)
return -1;
for (i = 0; i < TP_DRV_MAX_COUNT; i++) {
/* find it */
if (strcmp(tpd_driver_list[i].tpd_device_name,
tpd_drv->tpd_device_name) == 0) {
memset(&tpd_driver_list[i], 0,
sizeof(struct tpd_driver_t));
break;
}
}
return 0;
}
static void tpd_create_attributes(struct device *dev, struct tpd_attrs *attrs)
{
int num = attrs->num;
for (; num > 0;) {
if (device_create_file(dev, attrs->attr[--num]))
pr_info("mtk_tpd: tpd create attributes file failed\n");
}
}
/* touch panel probe */
static int tpd_probe(struct platform_device *pdev)
{
int touch_type = 1; /* 0:R-touch, 1: Cap-touch */
int i = 0;
#ifndef CONFIG_CUSTOM_LCM_X
#ifdef CONFIG_LCM_WIDTH
unsigned long tpd_res_x = 0, tpd_res_y = 0;
int ret = 0;
#endif
#endif
TPD_DMESG("enter %s, %d\n", __func__, __LINE__);
if (misc_register(&tpd_misc_device))
pr_info("mtk_tpd: tpd_misc_device register failed\n");
tpd_get_gpio_info(pdev);
tpd = kmalloc(sizeof(struct tpd_device), GFP_KERNEL);
if (tpd == NULL)
return -ENOMEM;
memset(tpd, 0, sizeof(struct tpd_device));
/* allocate input device */
tpd->dev = input_allocate_device();
if (tpd->dev == NULL) {
kfree(tpd);
return -ENOMEM;
}
/* TPD_RES_X = simple_strtoul(LCM_WIDTH, NULL, 0); */
/* TPD_RES_Y = simple_strtoul(LCM_HEIGHT, NULL, 0); */
#ifdef CONFIG_MTK_LCM_PHYSICAL_ROTATION
if (strncmp(CONFIG_MTK_LCM_PHYSICAL_ROTATION, "90", 2) == 0
|| strncmp(CONFIG_MTK_LCM_PHYSICAL_ROTATION, "270", 3) == 0) {
#ifdef CONFIG_MTK_FB
/*Fix build errors,as some projects cannot support these apis while bring up*/
TPD_RES_Y = DISP_GetScreenWidth();
TPD_RES_X = DISP_GetScreenHeight();
#endif
} else
#endif
{
#ifdef CONFIG_CUSTOM_LCM_X
#ifndef CONFIG_FPGA_EARLY_PORTING
#if defined(CONFIG_MTK_FB) && defined(CONFIG_MTK_LCM)
/*Fix build errors,as some projects cannot support these apis while bring up*/
TPD_RES_X = DISP_GetScreenWidth();
TPD_RES_Y = DISP_GetScreenHeight();
#else
/*for some projects, we do not use mtk framebuffer*/
TPD_RES_X = tpd_dts_data.tpd_resolution[0];
TPD_RES_Y = tpd_dts_data.tpd_resolution[1];
#endif
#endif
#else
#ifdef CONFIG_LCM_WIDTH
ret = kstrtoul(CONFIG_LCM_WIDTH, 0, &tpd_res_x);
if (ret < 0) {
pr_info("Touch down get lcm_x failed");
return ret;
}
TPD_RES_X = tpd_res_x;
ret = kstrtoul(CONFIG_LCM_HEIGHT, 0, &tpd_res_x);
if (ret < 0) {
pr_info("Touch down get lcm_y failed");
return ret;
}
TPD_RES_Y = tpd_res_y;
#endif
#endif
}
if (2560 == TPD_RES_X)
TPD_RES_X = 2048;
if (1600 == TPD_RES_Y)
TPD_RES_Y = 1536;
pr_debug("mtk_tpd: TPD_RES_X = %lu, TPD_RES_Y = %lu\n",
TPD_RES_X, TPD_RES_Y);
tpd_mode = TPD_MODE_NORMAL;
tpd_mode_axis = 0;
tpd_mode_min = TPD_RES_Y / 2;
tpd_mode_max = TPD_RES_Y;
tpd_mode_keypad_tolerance = TPD_RES_X * TPD_RES_X / 1600;
/* struct input_dev dev initialization and registration */
tpd->dev->name = TPD_DEVICE;
set_bit(EV_ABS, tpd->dev->evbit);
set_bit(EV_KEY, tpd->dev->evbit);
set_bit(ABS_X, tpd->dev->absbit);
set_bit(ABS_Y, tpd->dev->absbit);
set_bit(ABS_PRESSURE, tpd->dev->absbit);
#if !defined(CONFIG_MTK_S3320) && !defined(CONFIG_MTK_S3320_47)\
&& !defined(CONFIG_MTK_S3320_50) && !defined(CONFIG_MTK_MIT200) \
&& !defined(CONFIG_TOUCHSCREEN_SYNAPTICS_S3528) \
&& !defined(CONFIG_MTK_S7020) \
&& !defined(CONFIG_TOUCHSCREEN_MTK_SYNAPTICS_3320_50)
set_bit(BTN_TOUCH, tpd->dev->keybit);
#endif /* CONFIG_MTK_S3320 */
set_bit(INPUT_PROP_DIRECT, tpd->dev->propbit);
/* save dev for regulator_get() before tpd_local_init() */
tpd->tpd_dev = &pdev->dev;
for (i = 1; i < TP_DRV_MAX_COUNT; i++) {
/* add tpd driver into list */
if (tpd_driver_list[i].tpd_device_name != NULL) {
tpd_driver_list[i].tpd_local_init();
/* msleep(1); */
if (tpd_load_status == 1) {
TPD_DMESG("%s, tpd_driver_name=%s\n", __func__,
tpd_driver_list[i].tpd_device_name);
g_tpd_drv = &tpd_driver_list[i];
break;
}
}
}
if (g_tpd_drv == NULL) {
if (tpd_driver_list[0].tpd_device_name != NULL) {
g_tpd_drv = &tpd_driver_list[0];
/* touch_type:0: r-touch, 1: C-touch */
touch_type = 0;
g_tpd_drv->tpd_local_init();
TPD_DMESG("Generic touch panel driver\n");
} else {
TPD_DMESG("no touch driver is loaded!!\n");
return 0;
}
}
touch_resume_workqueue = create_singlethread_workqueue("touch_resume");
INIT_WORK(&touch_resume_work, touch_resume_workqueue_callback);
/* use fb_notifier */
tpd_fb_notifier.notifier_call = tpd_fb_notifier_callback;
if (fb_register_client(&tpd_fb_notifier))
TPD_DMESG("register fb_notifier fail!\n");
/* TPD_TYPE_CAPACITIVE handle */
if (touch_type == 1) {
set_bit(ABS_MT_TRACKING_ID, tpd->dev->absbit);
set_bit(ABS_MT_TOUCH_MAJOR, tpd->dev->absbit);
set_bit(ABS_MT_TOUCH_MINOR, tpd->dev->absbit);
set_bit(ABS_MT_POSITION_X, tpd->dev->absbit);
set_bit(ABS_MT_POSITION_Y, tpd->dev->absbit);
input_set_abs_params(tpd->dev,
ABS_MT_POSITION_X, 0, TPD_RES_X, 0, 0);
input_set_abs_params(tpd->dev,
ABS_MT_POSITION_Y, 0, TPD_RES_Y, 0, 0);
#if defined(CONFIG_MTK_S3320) || defined(CONFIG_MTK_S3320_47) \
|| defined(CONFIG_MTK_S3320_50) || defined(CONFIG_MTK_MIT200) \
|| defined(CONFIG_TOUCHSCREEN_SYNAPTICS_S3528) \
|| defined(CONFIG_MTK_S7020) \
|| defined(CONFIG_TOUCHSCREEN_MTK_SYNAPTICS_3320_50)
input_set_abs_params(tpd->dev,
ABS_MT_PRESSURE, 0, 255, 0, 0);
input_set_abs_params(tpd->dev,
ABS_MT_WIDTH_MAJOR, 0, 15, 0, 0);
input_set_abs_params(tpd->dev, ABS_MT_WIDTH_MINOR, 0, 15, 0, 0);
input_mt_init_slots(tpd->dev, 10, 0);
#else
input_set_abs_params(tpd->dev,
ABS_MT_TOUCH_MAJOR, 0, 100, 0, 0);
input_set_abs_params(tpd->dev,
ABS_MT_TOUCH_MINOR, 0, 100, 0, 0);
#endif /* CONFIG_MTK_S3320 */
TPD_DMESG("Cap touch panel driver\n");
}
input_set_abs_params(tpd->dev, ABS_X, 0, TPD_RES_X, 0, 0);
input_set_abs_params(tpd->dev, ABS_Y, 0, TPD_RES_Y, 0, 0);
input_abs_set_res(tpd->dev, ABS_X, TPD_RES_X);
input_abs_set_res(tpd->dev, ABS_Y, TPD_RES_Y);
input_set_abs_params(tpd->dev, ABS_PRESSURE, 0, 255, 0, 0);
input_set_abs_params(tpd->dev, ABS_MT_TRACKING_ID, 0, 10, 0, 0);
if (input_register_device(tpd->dev))
TPD_DMESG("input_register_device failed.(tpd)\n");
else
tpd_register_flag = 1;
if (g_tpd_drv->tpd_have_button)
tpd_button_init();
if (g_tpd_drv->attrs.num)
tpd_create_attributes(&pdev->dev, &g_tpd_drv->attrs);
return 0;
}
static int tpd_remove(struct platform_device *pdev)
{
input_unregister_device(tpd->dev);
return 0;
}
/* called when loaded into kernel */
static void tpd_init_work_callback(struct work_struct *work)
{
TPD_DEBUG("MediaTek touch panel driver init\n");
if (platform_driver_register(&tpd_driver) != 0)
TPD_DMESG("unable to register touch panel driver.\n");
}
static int __init tpd_device_init(void)
{
int res = 0;
tpd_init_workqueue = create_singlethread_workqueue("mtk-tpd");
INIT_WORK(&tpd_init_work, tpd_init_work_callback);
res = queue_work(tpd_init_workqueue, &tpd_init_work);
if (!res)
pr_info("tpd : touch device init failed res:%d\n", res);
return 0;
}
/* should never be called */
static void __exit tpd_device_exit(void)
{
TPD_DMESG("MediaTek touch panel driver exit\n");
/* input_unregister_device(tpd->dev); */
platform_driver_unregister(&tpd_driver);
}
late_initcall(tpd_device_init);
module_exit(tpd_device_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("MediaTek touch panel driver");
MODULE_AUTHOR("Kirby Wu<kirby.wu@mediatek.com>");
@@ -0,0 +1,437 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 MediaTek Inc.
*/
#include <linux/vmalloc.h>
#include <linux/uaccess.h>
#include <linux/major.h>
#include <linux/miscdevice.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include "tpd.h"
#include "met_ftrace_touch.h"
#ifdef TPD_DEBUG_CODE
int tpd_fail_count;
int tpd_trial_count;
int tpd_debug_nr;
module_param(tpd_debug_nr, int, 0644);
module_param(tpd_fail_count, int, 0644);
module_param(tpd_trial_count, int, 0644);
int tpd_debug_time;
long tpd_last_2_int_time[2] = { 0 };
long tpd_last_down_time;
int tpd_start_profiling;
int tpd_down_status;
module_param(tpd_debug_time, int, 0644);
void tpd_debug_set_time(void)
{
struct timeval t;
if (!tpd_debug_time && !tpd_em_log)
return;
do_gettimeofday(&t);
tpd_last_2_int_time[0] = tpd_last_2_int_time[1];
tpd_last_2_int_time[1] = (t.tv_sec & 0xFFF) * 1000000 + t.tv_usec;
/* */
/* Start profiling while receive touch DOWN event */
/* Stop profiling while the first frame is upadted */
/* */
if (!tpd_down_status) {
tpd_start_profiling = 1;
tpd_last_down_time = tpd_last_2_int_time[1];
}
}
#ifdef TPD_DEBUG_TRACK
int tpd_debug_track;
int tpd_debug_track_color;
int tpd_debug_touch_up;
module_param(tpd_debug_track, int, 0644);
void tpd_draw(int x, int y)
{
UINT16 *buf = (UINT16 *) dal_fb_addr;
buf = buf + (x + y * TPD_RES_X);
tpd_debug_track_color += (tpd_debug_track_color >= 31 ? 0 : 1);
*buf = (0xffe0 + tpd_debug_track_color);
}
void tpd_down_debug_track(int x, int y)
{
if (tpd_debug_touch_up == 1) {
DAL_Clean();
tpd_debug_touch_up = 0;
}
LCD_LayerEnable(5, TRUE);
tpd_draw(x - 1, y - 1);
tpd_draw(x, y - 1);
tpd_draw(x + 1, y - 1);
tpd_draw(x - 1, y);
tpd_draw(x + 1, y);
tpd_draw(x - 1, y + 1);
tpd_draw(x, y + 1);
tpd_draw(x + 1, y + 1);
}
void tpd_up_debug_track(int x, int y)
{
if (x == 0 && y == 0) {
tpd_debug_track_color = 0;
DAL_Clean();
}
tpd_debug_touch_up = 1;
}
#endif /* TPD_DEBUG_TRACK */
#define BUFFER_SIZE 128
int tpd_em_log;
module_param(tpd_em_log, int, 0664);
void tpd_enable_em_log(int enable)
{
if (enable)
tpd_em_log = 1;
else
tpd_em_log = 0;
}
EXPORT_SYMBOL(tpd_enable_em_log);
int tpd_em_log_to_fs;
module_param(tpd_em_log_to_fs, int, 0664);
int tpd_em_log_first = 1;
struct tpd_em_log_struct {
struct list_head list;
char data[BUFFER_SIZE];
};
static LIST_HEAD(tpd_em_log_list);
int tpd_log_line_buffer = 128; /* per line 128 bytes */
int tpd_log_line_cnt = 1024 * 10;
module_param(tpd_log_line_buffer, int, 0664);
module_param(tpd_log_line_cnt, int, 0664);
struct tpd_debug_log_buf {
unsigned int head;
unsigned int tail;
spinlock_t buffer_lock;
unsigned int cnt;
unsigned char *buffer;
};
struct tpd_debug_log_buf tpd_buf;
static int tpd_debug_log_open(struct inode *inode, struct file *file)
{
if (tpd_buf.buffer == NULL)
tpd_buf.buffer =
vmalloc(tpd_log_line_cnt * tpd_log_line_buffer);
if (tpd_buf.buffer == NULL) {
pr_info("tpd_log: nomem for tpd_buf->buffer\n");
return -ENOMEM;
}
spin_lock(&tpd_buf.buffer_lock);
tpd_buf.head = tpd_buf.tail = 0;
tpd_buf.cnt = 0;
spin_unlock(&tpd_buf.buffer_lock);
file->private_data = &tpd_buf;
pr_debug("[tpd_em_log]: open log file\n");
return 0;
}
static int tpd_debug_log_release(struct inode *inode, struct file *file)
{
unsigned char *tmp_buffer = NULL;
pr_debug("[tpd_em_log]: close log file\n");
spin_lock(&tpd_buf.buffer_lock);
tmp_buffer = tpd_buf.buffer;
tpd_buf.buffer = NULL;
spin_unlock(&tpd_buf.buffer_lock);
if (tmp_buffer)
vfree(tmp_buffer);
/* free(tpd_buf); */
return 0;
}
static ssize_t tpd_debug_log_write(struct file *file, const char __user *buffer,
size_t count, loff_t *ppos)
{
return 0;
}
static ssize_t tpd_debug_log_read(struct file *file, char __user *buffer,
size_t count, loff_t *ppos)
{
struct tpd_debug_log_buf *tpd_buf =
(struct tpd_debug_log_buf *)file->private_data;
unsigned int retval = 0, unit = tpd_log_line_buffer;
unsigned char *tmp_buf = NULL;
if (tpd_buf == NULL)
return -ENOMEM;
if (tpd_buf->head == tpd_buf->tail && (file->f_flags & O_NONBLOCK))
return -EAGAIN;
while ((count - retval) >= unit) {
spin_lock_irq(&tpd_buf->buffer_lock);
if (tpd_buf->head != tpd_buf->tail) {
tmp_buf = &tpd_buf->buffer[tpd_buf->tail++ * unit];
tpd_buf->tail &= tpd_log_line_cnt - 1;
} else {
spin_unlock_irq(&tpd_buf->buffer_lock);
break;
}
spin_unlock_irq(&tpd_buf->buffer_lock);
/* pr_info("%s, tmp_buf:0x%x\n", tmp_buf, tmp_buf); */
if (copy_to_user(buffer + retval, tmp_buf, unit))
return -EFAULT;
retval += unit;
}
return retval;
}
static unsigned char *tpd_log_find_buffer(void)
{
unsigned char *buffer = NULL;
unsigned int unit = tpd_log_line_buffer;
if (tpd_buf.buffer == NULL) {
pr_info("[tpd_em_log] :tpd_buf.buffer is NULL\n");
return NULL;
}
spin_lock(&tpd_buf.buffer_lock);
buffer = &tpd_buf.buffer[tpd_buf.head++ * unit];
tpd_buf.head &= tpd_log_line_cnt - 1;
spin_unlock(&tpd_buf.buffer_lock);
if (tpd_buf.head == tpd_buf.tail) {
snprintf(buffer, unit, "[tpd_em_log] overlay !!!!!\n");
return NULL;
}
memset(buffer, 0, unit);
return buffer;
}
static const struct file_operations tpd_debug_log_fops = {
.owner = THIS_MODULE,
.read = tpd_debug_log_read,
.write = tpd_debug_log_write,
.open = tpd_debug_log_open,
.release = tpd_debug_log_release,
};
static struct miscdevice tpd_debug_log_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "tpd_em_log",
.fops = &tpd_debug_log_fops,
};
#ifndef CREATE_TRACE_POINTS
#define CREATE_TRACE_POINTS
#endif
noinline void MET_touch(int raw_x, int raw_y,
int cal_x, int cal_y, int p, int down)
{
struct timeval t;
do_gettimeofday(&t);
if ((tpd_down_status == 0 && down == 1) ||
(tpd_down_status == 1 && down == 0)) {
trace_MET_touch("EV_KEY",
t.tv_sec, t.tv_usec, "BTN_TOUCH", down);
tpd_down_status = !tpd_down_status;
}
if (tpd_down_status) {
trace_MET_touch("EV_ABS", t.tv_sec, t.tv_usec, "X", raw_x);
trace_MET_touch("EV_ABS", t.tv_sec, t.tv_usec, "Y", raw_y);
}
}
void tpd_em_log_output(int raw_x, int raw_y,
int cal_x, int cal_y, int p, int down)
{
if (down == TPD_TYPE_INT_DOWN) {
pr_debug("[tpd_em_log] int trigger down\n");
} else if (down == TPD_TYPE_INT_UP) {
pr_debug("[tpd_em_log] int trigger up\n");
} else if (down == TPD_TYPE_TIMER) {
pr_debug("[tpd_em_log] timer trigger\n");
} else if (down == TPD_TYPE_RAW_DATA) {
if (tpd_em_log == TPD_TYPE_RAW_DATA) {
pr_debug("[tpd_em_log] rx=%d,ry=%d,rz1=%d"
SPLIT "rz2=%d,p=%d,r\n",
raw_x, raw_y, cal_x, cal_y, p);
}
} else if (down == TPD_TYPE_REJECT1) {
pr_debug("[tpd_em_log] the first or last point is rejected\n");
} else if (down == TPD_TYPE_REJECT2) {
pr_debug
("[tpd_em_log] pressure(%d) > NICE_PRESSURE(%d)"
SPLIT "debounce debt0:%d ms, debt1:%d ms, spl_num:%d\n",
raw_x, raw_y, cal_x, cal_y, p);
} else if (down == TPD_TYPE_FIST_LATENCY) {
pr_debug("[tpd_em_log] The first touch latency is %d ms\n",
raw_x / 1000);
} else if (down && tpd_down_status == 0) {
pr_debug("[tpd_em_log] rx=%d,ry=%d,cx=%d"
SPLIT "cy=%d,p=%d,d(+%ld ms)\n",
raw_x, raw_y, cal_x, cal_y, p,
(tpd_last_2_int_time[1] -
tpd_last_2_int_time[0]) / 1000);
} else if (down && tpd_down_status != 0) {
pr_debug("[tpd_em_log] rx=%d,ry=%d,cx=%d"
SPLIT "cy=%d,p=%d,m(+%ld ms)\n",
raw_x, raw_y, cal_x, cal_y, p,
(tpd_last_2_int_time[1] -
tpd_last_2_int_time[0]) / 1000);
} else {
pr_debug("[tpd_em_log] rx=%d,ry=%d,cx=%d"
SPLIT "cy=%d,p=%d,u(+%ld ms)\n",
raw_x, raw_y, cal_x, cal_y, p,
(tpd_last_2_int_time[1] -
tpd_last_2_int_time[0]) / 1000);
}
}
void tpd_em_log_store(int raw_x, int raw_y,
int cal_x, int cal_y, int p, int down)
{
/* static struct proc_dir_entry *entry = NULL; */
/* struct tpd_em_log_struct *tpd_em_log_struct_new; */
struct timeval t;
unsigned char *buffer = NULL;
/* unsigned int unit = tpd_log_line_buffer; */
/* pr_info("[tpd_em_log]: start register log file"); */
buffer = tpd_log_find_buffer();
if (buffer == NULL) {
pr_info("not buffer\n");
return;
}
do_gettimeofday(&t);
if (down == TPD_TYPE_INT_DOWN) {
snprintf(buffer, tpd_log_line_buffer,
"[%5lu.%06lu][tpd_em_log] int trigger down\n",
(t.tv_sec & 0xFFF), t.tv_usec);
} else if (down == TPD_TYPE_INT_UP) {
snprintf(buffer, tpd_log_line_buffer,
"[%5lu.%06lu][tpd_em_log] int trigger up\n",
(t.tv_sec & 0xFFF), t.tv_usec);
} else if (down == TPD_TYPE_TIMER) {
snprintf(buffer, tpd_log_line_buffer,
"[%5lu.%06lu][tpd_em_log] timer trigger\n",
(t.tv_sec & 0xFFF), t.tv_usec);
} else if (down == TPD_TYPE_RAW_DATA) {
if (tpd_em_log == TPD_TYPE_RAW_DATA) {
snprintf(buffer, tpd_log_line_buffer,
"[%5lu.%06lu][tpd_em_log]"
SPLIT "rx=%d,ry=%d,rz1=%d,rz2=%d,p=%d,r\n",
(t.tv_sec & 0xFFF), t.tv_usec,
raw_x, raw_y, cal_x, cal_y, p);
}
} else if (down == TPD_TYPE_REJECT1) {
snprintf(buffer, tpd_log_line_buffer,
"[%5lu.%06lu][tpd_em_log]"
SPLIT "the first or last point is rejected\n",
(t.tv_sec & 0xFFF), t.tv_usec);
} else if (down == TPD_TYPE_REJECT2) {
snprintf(buffer, tpd_log_line_buffer,
"[%5lu.%06lu][tpd_em_log]"
SPLIT "pressure(%d) > NICE_PRESSURE(%d), "
SPLIT "debounce debt0:%d, debt1:%d, spl_num:%d\n",
(t.tv_sec & 0xFFF), t.tv_usec,
raw_x, raw_y, cal_x, cal_y, p);
} else if (down == TPD_TYPE_FIST_LATENCY) {
snprintf(buffer, tpd_log_line_buffer,
"[%5lu.%06lu][tpd_em_log]"
SPLIT "The first touch latency is %d ms\n",
(t.tv_sec & 0xFFF), t.tv_usec, raw_x / 1000);
} else if (down && tpd_down_status == 0) {
snprintf(buffer, tpd_log_line_buffer,
"[%5lu.%06lu][tpd_em_log]"
SPLIT "rx=%d,ry=%d,cx=%d,cy=%d,p=%d,d(+%ld ms)\n",
(t.tv_sec & 0xFFF), t.tv_usec,
raw_x, raw_y, cal_x, cal_y, p,
(tpd_last_2_int_time[1] -
tpd_last_2_int_time[0]) / 1000);
} else if (down && tpd_down_status != 0) {
snprintf(buffer, tpd_log_line_buffer,
"[%5lu.%06lu][tpd_em_log]"
SPLIT "rx=%d,ry=%d,cx=%d,cy=%d,p=%d,d(+%ld ms)\n",
(t.tv_sec & 0xFFF), t.tv_usec,
raw_x, raw_y, cal_x, cal_y, p,
(tpd_last_2_int_time[1] -
tpd_last_2_int_time[0]) / 1000);
} else {
snprintf(buffer, tpd_log_line_buffer,
"[%5lu.%06lu][tpd_em_log]"
SPLIT "rx=%d,ry=%d,cx=%d,cy=%d,p=%d,u(+%ld ms)\n",
(t.tv_sec & 0xFFF), t.tv_usec,
raw_x, raw_y, cal_x, cal_y, p,
(tpd_last_2_int_time[1] -
tpd_last_2_int_time[0]) / 1000);
}
/* list_add_tail(&tpd_em_log_struct_new->list, &tpd_em_log_list); */
}
void tpd_em_log_release(void)
{
struct tpd_em_log_struct *p, *p_tmp;
if (!tpd_em_log_first) {
remove_proc_entry("tpd_em_log", NULL);
list_for_each_entry_safe(p, p_tmp, &tpd_em_log_list, list) {
list_del(&p->list);
kfree(p);
}
tpd_em_log_first = 1;
tpd_em_log_to_fs = 0;
}
}
static int __init tpd_log_init(void)
{
if (misc_register(&tpd_debug_log_dev) < 0) {
pr_info("[tpd_em_log] :register device failed\n");
return -1;
}
pr_info("[tpd_em_log] :register device successfully\n");
spin_lock_init(&tpd_buf.buffer_lock);
return 0;
}
int tpd_debuglog;
module_param(tpd_debuglog, int, 0664);
module_init(tpd_log_init);
#endif /* TPD_DEBUG_CODE */
@@ -0,0 +1,144 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2019 MediaTek Inc.
*/
#ifndef TPD_DEBUG_H
#define TPD_DEBUG_H
#ifdef TPD_DEBUG_CODE
#include<linux/i2c.h>
extern int tpd_debug_nr;
void tpd_debug_set_time(void);
extern int tpd_debug_time;
extern long tpd_last_2_int_time[2];
extern long tpd_last_down_time;
extern int tpd_start_profiling;
extern int tpd_down_status;
#define TPD_DEBUG_PRINT_INT \
do { \
if (tpd_debug_time) { \
pr_info("tp_int\n"); \
} \
} while (0)
#define TPD_DEBUG_PRINT_UP \
do { \
if (pending == 0 && tpd_debug_time) { \
tpd_down_status = 0; \
pr_debug("up on %ld ms (+%ld ms)\n", \
(tpd_last_2_int_time[1] - tpd_last_down_time) / 1000, \
(tpd_last_2_int_time[1] - tpd_last_2_int_time[0]) / 1000); \
} \
} while (0)
#define TPD_DEBUG_PRINT_DOWN \
do { \
if (tpd_debug_time) { \
if (tpd_down_status == 0)\
pr_debug("down on 0 ms\n"); \
else \
pr_debug("move on %ld ms (+%ld ms)\n", \
(tpd_last_2_int_time[1] - \
tpd_last_down_time) / 1000, \
(tpd_last_2_int_time[1] -\
tpd_last_2_int_time[0]) / 1000); \
tpd_down_status = 1; \
} \
} while (0)
#define TPD_DEBUG_SET_TIME do { tpd_debug_set_time(); } while (0)
extern int tpd_em_log;
extern int tpd_em_log_to_fs;
extern int tpd_type_cap;
void tpd_em_log_output(int raw_x,
int raw_y, int cal_x, int cal_y, int p, int down);
void tpd_em_log_store(int raw_x,
int raw_y, int cal_x, int cal_y, int p, int down);
void tpd_em_log_release(void);
void tpd_enable_em_log(int enable);
#ifndef CREATE_TRACE_POINTS
#define CREATE_TRACE_POINTS
#endif
noinline void MET_touch(int raw_x, int raw_y,
int cal_x, int cal_y, int p, int down);
#define TPD_TYPE_RAW_DATA 2
#define TPD_TYPE_INT_DOWN 3
#define TPD_TYPE_INT_UP 4
#define TPD_TYPE_TIMER 5
#define TPD_TYPE_REJECT1 6
#define TPD_TYPE_REJECT2 7
#define TPD_TYPE_FIST_LATENCY 8
#define TPD_EM_PRINT(raw_x, raw_y, cal_x, cal_y, p, down)\
do { \
if (tpd_em_log) {\
if (!tpd_em_log_to_fs) { \
tpd_em_log_output(raw_x, \
raw_y, cal_x, cal_y, p, down); \
} else { \
tpd_em_log_store(raw_x, \
raw_y, cal_x, cal_y, p, down); \
tpd_em_log_output(raw_x, \
raw_y, cal_x, cal_y, p, down); \
} \
} else { \
if (tpd_em_log_to_fs) { \
tpd_em_log_release(); \
} \
} \
MET_touch(raw_x, raw_y, cal_x, cal_y, p, down); \
} while (0)
#ifdef TPD_DEBUG_TRACK
extern void *dal_fb_addr;
extern int tpd_debug_track;
void tpd_up_debug_track(int x, int y);
void tpd_down_debug_track(int x, int y);
#define TPD_UP_DEBUG_TRACK(x, y) \
do { \
if (tpd_debug_track) \
tpd_up_debug_track(x, y); \
} while (0)
#define TPD_DOWN_DEBUG_TRACK(x, y) \
do { \
if (tpd_debug_track) \
tpd_down_debug_track(x, y); \
} while (0)
#endif /* TPD_DEBUG_TRACK */
#endif /* TPD_DEBUG_CODE */
/* Macros that will be embedded in code */
#ifndef TPD_DEBUG_SET_TIME
#define TPD_DEBUG_SET_TIME
#endif
#ifndef TPD_DEBUG_PRINT_UP
#define TPD_DEBUG_PRINT_UP
#endif
#ifndef TPD_DEBUG_PRINT_DOWN
#define TPD_DEBUG_PRINT_DOWN
#endif
#ifndef TPD_UP_DEBUG_TRACK
#define TPD_UP_DEBUG_TRACK(x, y)
#endif
#ifndef TPD_DOWN_DEBUG_TRACK
#define TPD_DOWN_DEBUG_TRACK(x, y)
#endif
#endif /* TPD_DEBUG_H */
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 MediaTek Inc.
*/
#include "tpd.h"
/* #ifndef TPD_CUSTOM_TREMBLE_TOLERANCE */
int tpd_trembling_tolerance(int t, int p)
{
if (t > 5 || p > 120)
return 200;
if (p > 90)
return 64;
if (p > 80)
return 36;
return 26;
}
/* #endif */
@@ -0,0 +1,10 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2019 MediaTek Inc.
*/
#ifndef TPD_FAT_TOUCH
#define TPD_FAT_TOUCH 120
#endif
int tpd_trembling_tolerance(int t, int p);
@@ -0,0 +1,48 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 MediaTek Inc.
*/
#include "tpd.h"
int tpd_calibrate_en;
module_param(tpd_calibrate_en, int, 0664);
int tpd_show_version;
module_param(tpd_show_version, int, 0664);
/* switch touch panel into single scan mode for decreasing interference */
void tpd_switch_single_mode(void)
{
#ifdef HAVE_SINGLE_MULTIPLE_SCAN_MODE
_tpd_switch_single_mode();
#endif
}
EXPORT_SYMBOL(tpd_switch_single_mode);
/* switch touch panel into multiple scan mode for better performance */
void tpd_switch_multiple_mode(void)
{
#ifdef HAVE_SINGLE_MULTIPLE_SCAN_MODE
_tpd_switch_multiple_mode();
#endif
}
EXPORT_SYMBOL(tpd_switch_multiple_mode);
/* switch touch panel into deep sleep mode */
void tpd_switch_sleep_mode(void)
{
#ifdef HAVE_SLEEP_NORMAL_MODE
_tpd_switch_sleep_mode();
#endif
}
EXPORT_SYMBOL(tpd_switch_sleep_mode);
/* switch touch panel back to normal mode */
void tpd_switch_normal_mode(void)
{
#ifdef HAVE_SLEEP_NORMAL_MODE
_tpd_switch_normal_mode();
#endif
}
EXPORT_SYMBOL(tpd_switch_normal_mode);
@@ -0,0 +1,69 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 MediaTek Inc.
*/
#include "tpd.h"
unsigned long TPD_RES_X = 480;
unsigned long TPD_RES_Y = 800;
/* #if (defined(TPD_HAVE_CALIBRATION) && !defined(TPD_CUSTOM_CALIBRATION)) */
int tpd_calmat[8] = { 0 };
int tpd_def_calmat[8] = { 0 };
int tpd_calmat_size = 8;
int tpd_def_calmat_size = 8;
module_param_array(tpd_calmat, int, &tpd_calmat_size, 0664);
module_param_array(tpd_def_calmat, int, &tpd_def_calmat_size, 0444);
/* #endif */
/* #ifdef TPD_TYPE_CAPACITIVE */
int tpd_type_cap;
int tpd_v_magnify_x = 10;
int tpd_v_magnify_y = 10;
module_param(tpd_v_magnify_x, int, 0664);
module_param(tpd_v_magnify_y, int, 0664);
module_param(tpd_type_cap, int, 0444);
int tpd_firmware_version[2] = { 0, 0 };
int tpd_firmware_version_size = 2;
module_param_array(tpd_firmware_version, int, &tpd_firmware_version_size, 0444);
int tpd_mode = TPD_MODE_NORMAL;
int tpd_mode_axis;
int tpd_mode_min = 400; /* TPD_RES_Y/2; */
int tpd_mode_max = 800; /* TPD_RES_Y; */
/* TPD_RES_X*TPD_RES_X/1600; */
int tpd_mode_keypad_tolerance = 480 * 480 / 1600;
module_param(tpd_mode, int, 0664);
module_param(tpd_mode_axis, int, 0664);
module_param(tpd_mode_min, int, 0664);
module_param(tpd_mode_max, int, 0664);
module_param(tpd_mode_keypad_tolerance, int, 0664);
int tpd_em_debounce_time0 = 1;
int tpd_em_debounce_time; /* =0 */
int tpd_em_debounce_time1 = 4;
module_param(tpd_em_debounce_time0, int, 0664);
module_param(tpd_em_debounce_time1, int, 0664);
module_param(tpd_em_debounce_time, int, 0664);
int tpd_em_spl_num = 1;
module_param(tpd_em_spl_num, int, 0664);
int tpd_em_pressure_threshold;
module_param(tpd_em_pressure_threshold, int, 0664);
int tpd_em_auto_time_interval = 10;
module_param(tpd_em_auto_time_interval, int, 0664);
int tpd_em_sample_cnt = 16;
module_param(tpd_em_sample_cnt, int, 0664);
int tpd_load_status;
module_param(tpd_load_status, int, 0664);
int tpd_em_asamp = 1;
module_param(tpd_em_asamp, int, 0664);
+11
View File
@@ -0,0 +1,11 @@
#
# Touchscreen driver configuration
#
menu "GT1151 support"
depends on INPUT_TOUCHSCREEN
config GT1151_CONFIG
string "GT1151 for Mediatek config"
help
this string is used for cfg update
endmenu
+15
View File
@@ -0,0 +1,15 @@
# Linux driver folder
ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/mt-plat/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/
obj-y += gt1x_generic.o
obj-n += gt1x_tools.o
obj-y += gt1x_tpd.o
obj-y += gt1x_update.o
obj-y += tpd_control.o
obj-y += tpd_button.o
obj-y += tpd_debug.o
obj-y += tpd_default.o
obj-y += tpd_misc.o
obj-y += tpd_setting.o
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,522 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2014 Goodix Technology.
*/
#include <linux/delay.h>
#include <linux/uaccess.h>
#include <linux/proc_fs.h>
#include <generated/utsrelease.h>
#include "gt1x_tpd_common.h"
static ssize_t gt1x_tool_read(struct file *filp, char __user *buffer,
size_t count, loff_t *ppos);
static ssize_t gt1x_tool_write(struct file *filp, const char *buffer,
size_t count, loff_t *ppos);
static s32 gt1x_tool_release(struct inode *inode, struct file *filp);
static s32 gt1x_tool_open(struct inode *inode, struct file *file);
#pragma pack(1)
struct st_cmd_head {
u8 wr; /*write read flag£¬0:R 1:W 2:PID 3:*/
u8 flag; /*0:no need flag/int 1: need flag 2:need int*/
u8 flag_addr[2]; /*flag address*/
u8 flag_val; /*flag val*/
u8 flag_relation; /*flag_val:flag 0:not equal 1:equal 2:> 3:<*/
u16 circle; /*polling cycle*/
u8 times; /*plling times*/
u8 retry; /*I2C retry times*/
u16 delay; /*delay before read or after write*/
u16 data_len; /*data length*/
u8 addr_len; /*address length*/
u8 addr[2]; /*address*/
u8 res[3]; /*reserved*/
u8 *data; /*data pointer*/
};
#pragma pack()
struct st_cmd_head cmd_head;
static DEFINE_MUTEX(rw_mutex);
s32 DATA_LENGTH;
s8 IC_TYPE[16] = "GT9XX";
#define UPDATE_FUNCTIONS
#define DATA_LENGTH_UINT 512
#define CMD_HEAD_LENGTH (sizeof(struct st_cmd_head) - sizeof(u8 *))
static char procname[20] = { 0 };
static struct proc_dir_entry *gt1x_tool_proc_entry;
static const struct file_operations gt1x_tool_fops = {
.read = gt1x_tool_read,
.write = gt1x_tool_write,
.open = gt1x_tool_open,
.release = gt1x_tool_release,
.owner = THIS_MODULE,
};
static void set_tool_node_name(char *procname)
{
int v0 = 0, v1 = 0, v2 = 0;
int ret;
ret = sscanf(UTS_RELEASE, "%d.%d.%d", &v0, &v1, &v2);
sprintf(procname, "gmnode%02d%02d%02d", v0, v1, v2);
}
int gt1x_init_tool_node(void)
{
memset(&cmd_head, 0, sizeof(cmd_head));
/*if the first operation is read, will return fail.*/
cmd_head.wr = 1;
cmd_head.data = kzalloc(DATA_LENGTH_UINT, GFP_KERNEL);
if (cmd_head.data == NULL) {
GTP_ERROR("Apply for memory failed.");
return -1;
}
GTP_INFO("Applied memory size:%d.", DATA_LENGTH_UINT);
DATA_LENGTH = DATA_LENGTH_UINT - GTP_ADDR_LENGTH;
set_tool_node_name(procname);
gt1x_tool_proc_entry =
proc_create(procname, 0664, NULL, &gt1x_tool_fops);
if (gt1x_tool_proc_entry == NULL) {
GTP_ERROR("Couldn't create proc entry!");
return -1;
}
GTP_INFO("Create proc entry success!");
return 0;
}
void gt1x_deinit_tool_node(void)
{
remove_proc_entry(procname, NULL);
kfree(cmd_head.data);
cmd_head.data = NULL;
}
static s32 tool_i2c_read(u8 *buf, u16 len)
{
u16 addr = (buf[0] << 8) + buf[1];
if (!gt1x_i2c_read(addr, &buf[2], len))
return 1;
return -1;
}
static s32 tool_i2c_write(u8 *buf, u16 len)
{
u16 addr = (buf[0] << 8) + buf[1];
if (!gt1x_i2c_write(addr, &buf[2], len - 2))
return 1;
return -1;
}
static u8 relation(u8 src, u8 dst, u8 rlt)
{
u8 ret = 0;
switch (rlt) {
case 0:
ret = (src != dst) ? true : false;
break;
case 1:
ret = (src == dst) ? true : false;
GTP_DEBUG("equal:src:0x%02x dst:0x%02x ret:%d.",
src, dst, (s32) ret);
break;
case 2:
ret = (src > dst) ? true : false;
break;
case 3:
ret = (src < dst) ? true : false;
break;
case 4:
ret = (src & dst) ? true : false;
break;
case 5:
ret = (!(src | dst)) ? true : false;
break;
default:
ret = false;
break;
}
return ret;
}
/*******************************************************
*Function:
* Comfirm function.
*Input:
* None.
*Output:
* Return write length.
********************************************************/
static u8 comfirm(void)
{
s32 i = 0;
u8 buf[32];
memcpy(buf, cmd_head.flag_addr, cmd_head.addr_len);
for (i = 0; i < cmd_head.times; i++) {
if (tool_i2c_read(buf, 1) <= 0) {
GTP_ERROR("Read flag data failed!");
return -1;
}
if (true == relation(buf[GTP_ADDR_LENGTH],
cmd_head.flag_val,
cmd_head.flag_relation)) {
GTP_DEBUG("value at flag addr:0x%02x.",
buf[GTP_ADDR_LENGTH]);
GTP_DEBUG("flag value:0x%02x.", cmd_head.flag_val);
break;
}
msleep(cmd_head.circle);
}
if (i >= cmd_head.times) {
GTP_ERROR("Didn't get the flag to continue!");
return -1;
}
return 0;
}
/*******************************************************
*Function:
* Goodix tool write function.
*Input:
* standard proc write function param.
*Output:
* Return write length.
********************************************************/
static ssize_t gt1x_tool_write(struct file *filp, const char __user *buff,
size_t len, loff_t *data)
{
u64 ret = 0;
u8 *pre_data_p;
u8 *post_data_p;
GTP_DEBUG_FUNC();
mutex_lock(&rw_mutex);
pre_data_p = cmd_head.data;
ret = copy_from_user(&cmd_head, buff, CMD_HEAD_LENGTH);
if (ret)
GTP_ERROR("copy_from_user failed.");
post_data_p = cmd_head.data;
if (pre_data_p != post_data_p) {
GTP_ERROR("pointer is overwritten! %p, %p, %p, %p, %dx\n",
pre_data_p, post_data_p,
&cmd_head, &cmd_head.data, (int)CMD_HEAD_LENGTH);
}
GTP_DEBUG("wr :0x%02x.", cmd_head.wr);
/*
* GTP_DEBUG("flag:0x%02x.", cmd_head.flag);
* GTP_DEBUG("flag addr:0x%02x%02x.", cmd_head.flag_addr[0],
* cmd_head.flag_addr[1]);
* GTP_DEBUG("flag val:0x%02x.", cmd_head.flag_val);
* GTP_DEBUG("flag rel:0x%02x.", cmd_head.flag_relation);
* GTP_DEBUG("circle :%d.", (s32)cmd_head.circle);
* GTP_DEBUG("times :%d.", (s32)cmd_head.times);
* GTP_DEBUG("retry :%d.", (s32)cmd_head.retry);
* GTP_DEBUG("delay :%d.", (s32)cmd_head.delay);
* GTP_DEBUG("data len:%d.", (s32)cmd_head.data_len);
* GTP_DEBUG("addr len:%d.", (s32)cmd_head.addr_len);
* GTP_DEBUG("addr:0x%02x%02x.", cmd_head.addr[0], cmd_head.addr[1]);
* GTP_DEBUG("len:%d.", (s32)len);
* GTP_DEBUG("buf[20]:0x%02x.", buff[CMD_HEAD_LENGTH]);
*/
GTP_DEBUG_ARRAY((u8 *) cmd_head.data, cmd_head.data_len);
if (cmd_head.data_len > DATA_LENGTH)
cmd_head.data_len = DATA_LENGTH;
if (cmd_head.wr == 1) {
u16 addr, data_len, pos;
if (cmd_head.flag == 1) {
if (comfirm()) {
GTP_ERROR("[WRITE]Comfirm fail!");
ret = -1;
goto out;
}
} else if (cmd_head.flag == 2) {
/*Need interrupt!*/
}
addr = (cmd_head.addr[0] << 8) + cmd_head.addr[1];
data_len = cmd_head.data_len;
pos = 0;
while (data_len > 0) {
len = data_len > DATA_LENGTH ? DATA_LENGTH : data_len;
ret = copy_from_user(&cmd_head.data[GTP_ADDR_LENGTH],
&buff[CMD_HEAD_LENGTH + pos],
len);
if (ret) {
GTP_ERROR("[WRITE]copy_from_user failed.");
ret = -1;
goto out;
}
cmd_head.data[0] = ((addr >> 8) & 0xFF);
cmd_head.data[1] = (addr & 0xFF);
GTP_DEBUG_ARRAY(cmd_head.data, len + GTP_ADDR_LENGTH);
if (tool_i2c_write(cmd_head.data,
len + GTP_ADDR_LENGTH) <= 0) {
GTP_ERROR("[WRITE]Write data failed!");
ret = -1;
goto out;
}
addr += len;
pos += len;
data_len -= len;
}
if (cmd_head.delay)
msleep(cmd_head.delay);
mutex_unlock(&rw_mutex);
return cmd_head.data_len + CMD_HEAD_LENGTH;
} else if (cmd_head.wr == 3) { /*gt1x unused*/
cmd_head.data_len =
cmd_head.data_len > sizeof(IC_TYPE) ?
sizeof(IC_TYPE) : cmd_head.data_len;
memcpy(IC_TYPE, cmd_head.data, cmd_head.data_len);
ret = cmd_head.data_len + CMD_HEAD_LENGTH;
goto out;
} else if (cmd_head.wr == 5) {
/*memcpy(IC_TYPE, cmd_head.data, cmd_head.data_len);*/
mutex_unlock(&rw_mutex);
return cmd_head.data_len + CMD_HEAD_LENGTH;
} else if (cmd_head.wr == 7) { /*disable irq!*/
gt1x_irq_disable();
#ifdef CONFIG_GTP_ESD_PROTECT
gt1x_esd_switch(SWITCH_OFF);
#endif
ret = CMD_HEAD_LENGTH;
goto out;
} else if (cmd_head.wr == 9) { /*enable irq!*/
gt1x_irq_enable();
#ifdef CONFIG_GTP_ESD_PROTECT
gt1x_esd_switch(SWITCH_ON);
#endif
ret = CMD_HEAD_LENGTH;
goto out;
} else if (cmd_head.wr == 17) {
ret = copy_from_user(&cmd_head.data[GTP_ADDR_LENGTH],
&buff[CMD_HEAD_LENGTH],
cmd_head.data_len);
if (ret) {
GTP_DEBUG("copy_from_user failed.");
ret = -1;
goto out;
}
if (cmd_head.data[GTP_ADDR_LENGTH]) {
GTP_DEBUG("gtp enter rawdiff.");
gt1x_rawdiff_mode = true;
} else {
gt1x_rawdiff_mode = false;
GTP_DEBUG("gtp leave rawdiff.");
}
ret = CMD_HEAD_LENGTH;
goto out;
} else if (cmd_head.wr == 11) {
gt1x_enter_update_mode();
} else if (cmd_head.wr == 13) {
gt1x_leave_update_mode();
} else if (cmd_head.wr == 15) {
memset(cmd_head.data, 0, cmd_head.data_len + 1);
ret = copy_from_user(cmd_head.data,
&buff[CMD_HEAD_LENGTH], cmd_head.data_len);
if (ret) {
GTP_DEBUG("copy_from_user failed.");
ret = -1;
goto out;
}
GTP_DEBUG("update firmware, filename: %s", cmd_head.data);
ret = gt1x_update_firmware((void *)cmd_head.data);
if (ret) {
ret = -1;
goto out;
}
}
ret = CMD_HEAD_LENGTH;
out:
mutex_unlock(&rw_mutex);
return ret;
}
static u8 devicecount;
static s32 gt1x_tool_open(struct inode *inode, struct file *file)
{
GTP_DEBUG("gt1x_tool_proc open start.");
if (devicecount > 0)
return -ERESTARTSYS;
GTP_DEBUG("gt1x_tool_proc open success!");
devicecount++;
return 0;
}
static s32 gt1x_tool_release(struct inode *inode, struct file *filp)
{
GTP_DEBUG("gt1x_tool_proc release start.");
devicecount--;
GTP_DEBUG("gt1x_tool_proc release!");
return 0;
}
/*******************************************************
* Function:
* Goodix tool read function.
*Input:
* standard proc read function param.
* Output:
* Return read length.
********************************************************/
static ssize_t gt1x_tool_read(struct file *filp, char __user *buffer,
size_t count, loff_t *ppos)
{
u64 ret;
GTP_DEBUG_FUNC();
if (*ppos) {
GTP_DEBUG("[PARAM]size: %d, *ppos: %d", (int)count, (int)*ppos);
*ppos = 0;
return 0;
}
if (cmd_head.data_len > DATA_LENGTH)
cmd_head.data_len = DATA_LENGTH;
mutex_lock(&rw_mutex);
if (cmd_head.wr % 2) {
GTP_ERROR("[READ] invaild operator fail!");
ret = -1;
goto out;
} else if (!cmd_head.wr) {
u16 addr, data_len, len, loc;
if (cmd_head.flag == 1) {
if (comfirm()) {
GTP_ERROR("[READ]Comfirm fail!");
ret = -1;
goto out;
}
} else if (cmd_head.flag == 2) {
/*Need interrupt!*/
}
addr = (cmd_head.addr[0] << 8) + cmd_head.addr[1];
data_len = cmd_head.data_len;
loc = 0;
GTP_DEBUG("[READ] ADDR:0x%04X.", addr);
GTP_DEBUG("[READ] Length: %d", data_len);
if (cmd_head.delay)
msleep(cmd_head.delay);
while (data_len > 0) {
len = data_len > DATA_LENGTH ? DATA_LENGTH : data_len;
cmd_head.data[0] = (addr >> 8) & 0xFF;
cmd_head.data[1] = (addr & 0xFF);
if (tool_i2c_read(cmd_head.data, len) <= 0) {
GTP_ERROR("[READ]Read data failed!");
ret = -1;
goto out;
}
if (copy_to_user(&buffer[loc],
&cmd_head.data[GTP_ADDR_LENGTH],
len)) {
GTP_ERROR("[READ]copy_to_user failed!");
ret = -1;
goto out;
}
data_len -= len;
addr += len;
loc += len;
GTP_DEBUG_ARRAY(&cmd_head.data[GTP_ADDR_LENGTH], len);
}
*ppos += cmd_head.data_len;
ret = cmd_head.data_len;
goto out;
} else if (cmd_head.wr == 2) {
GTP_DEBUG("Return ic type:%s len:%d.",
buffer, (s32) cmd_head.data_len);
ret = -1;
goto out;
} else if (cmd_head.wr == 4) {
u8 val[4];
val[0] = update_info.progress >> 8;
val[1] = update_info.progress & 0xff;
val[2] = update_info.max_progress >> 8;
val[3] = update_info.max_progress & 0xff;
if (copy_to_user(buffer, val, sizeof(val))) {
GTP_ERROR("[READ]copy_to_user failed!");
ret = cmd_head.data_len;
goto out;
}
*ppos += 4;
ret = 4;
goto out;
} else if (cmd_head.wr == 6) {
/*Read error code!*/
ret = -1;
goto out;
} else if (cmd_head.wr == 8) { /*Read driver version*/
s32 tmp_len = strlen(GTP_DRIVER_VERSION) + 1;
char *drv_ver = NULL;
if (count < tmp_len) {
ret = -1;
goto out;
}
drv_ver = kzalloc(tmp_len, GFP_ATOMIC);
if (drv_ver == NULL) {
GTP_ERROR("Allocate %d buffer fail\n", tmp_len);
ret = -1;
goto out;
}
strncpy(drv_ver, GTP_DRIVER_VERSION,
strlen(GTP_DRIVER_VERSION));
drv_ver[strlen(GTP_DRIVER_VERSION)] = 0;
if (copy_to_user(buffer, drv_ver, tmp_len)) {
GTP_ERROR("[READ]copy_to_user failed");
kfree(drv_ver);
ret = -1;
goto out;
}
*ppos += tmp_len;
kfree(drv_ver);
mutex_unlock(&rw_mutex);
return tmp_len;
}
*ppos += cmd_head.data_len;
ret = cmd_head.data_len;
out:
mutex_unlock(&rw_mutex);
return ret;
}
void gt1x_tool_exit(void)
{
remove_proc_entry(procname, NULL);
}
EXPORT_SYMBOL_GPL(gt1x_tool_exit);
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,416 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2014 Goodix Technology.
*/
#ifndef GT1X_TPD_COMMON_H__
#define GT1X_TPD_COMMON_H__
#include <linux/uaccess.h>
#ifdef CONFIG_MTK_BOOT
#include "mtk_boot_common.h"
#endif
#include "tpd.h"
//#include "upmu_common.h"
#include <linux/hrtimer.h>
#include <linux/string.h>
#include <linux/vmalloc.h>
#include <linux/jiffies.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/input.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <uapi/linux/sched/types.h> //for kernel-4.14
#include <linux/kthread.h>
#include <linux/bitops.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/byteorder/generic.h>
#include <linux/interrupt.h>
#include <linux/time.h>
#include <linux/input.h>
#include <linux/proc_fs.h>
#include <linux/uaccess.h>
//#include <linux/sched.h> // for kernel-4.9
#ifdef CONFIG_MTK_I2C_EXTENSION
/* if gt9l, better enable it if hardware platform supported*/
#define TPD_SUPPORT_I2C_DMA 1
#else
#define TPD_SUPPORT_I2C_DMA 0
#endif
#if defined(CONFIG_MTK_LEGACY)
#define TPD_POWER_SOURCE_CUSTOM MT6328_POWER_LDO_VGP1
#endif
#define GTP_GPIO_AS_INT(pin) tpd_gpio_as_int(pin)
#define GTP_GPIO_OUTPUT(pin, level) tpd_gpio_output(pin, level)
#define IIC_MAX_TRANSFER_SIZE 8
#define IIC_DMA_MAX_TRANSFER_SIZE 250
#define I2C_MASTER_CLOCK 300
#define TPD_MAX_RESET_COUNT 3
#define TPD_CALIBRATION_MATRIX {962, 0, 0, 0, 1600, 0, 0, 0}
#define KEY_GESTURE KEY_F24 /* customize gesture-key */
#define DEFAULT_MAX_TOUCH_NUM 10
/* define GT1151 parameter */
#define GT1151_FIRMWARE "firmware2"
#define GTP_DRIVER_SEND_CFG
#define GTP_CUSTOM_CFG
#define GTP_AUTO_UPDATE
#define GTP_REQUEST_FW_UPDATE
#define GTP_CREATE_WR_NODE
#define GTP_POWER_CTRL_SLEEP
extern int tpd_em_log;
#define CFG_GROUP_LEN(p_cfg_grp) (ARRAY_SIZE(p_cfg_grp) / sizeof(p_cfg_grp[0]))
#ifdef GTP_CUSTOM_CFG
#define GTP_INT_TRIGGER 1 /*0:Rising 1:Falling*/
#define GTP_WAKEUP_LEVEL 1
#endif
/****************************PART3:OTHER define*******************************/
#define GTP_DRIVER_VERSION "V1.0<2014/09/28>"
#define GTP_I2C_NAME "Goodix-TS"
#define GT1X_DEBUG_PROC_FILE "gt1x_debug"
#define GTP_POLL_TIME 10
#define GTP_ADDR_LENGTH 2
#define GTP_CONFIG_MIN_LENGTH 186
#define GTP_CONFIG_MAX_LENGTH 240
#define GTP_MAX_I2C_XFER_LEN 250
#define SWITCH_OFF 0
#define SWITCH_ON 1
#define GTP_REG_MATRIX_DRVNUM 0x8069
#define GTP_REG_MATRIX_SENNUM 0x806A
#define GTP_REG_RQST 0x8044
#define GTP_REG_BAK_REF 0x90EC
#define GTP_REG_MAIN_CLK 0x8020
#define GTP_REG_HAVE_KEY 0x8057
#define GTP_REG_HN_STATE 0x8800
#define GTP_REG_WAKEUP_GESTURE 0x814C
#define GTP_REG_WAKEUP_GESTURE_DETAIL 0xA2A0 /*need change */
#define GTP_BAK_REF_PATH "/data/gt1x_ref.bin"
#define GTP_MAIN_CLK_PATH "/data/gt1x_clk.bin"
/* request type */
#define GTP_RQST_CONFIG 0x01
#define GTP_RQST_BAK_REF 0x02
#define GTP_RQST_RESET 0x03
#define GTP_RQST_MAIN_CLOCK 0x04
#define GTP_RQST_HOTKNOT_CODE 0x20
#define GTP_RQST_RESPONDED 0x00
#define GTP_RQST_IDLE 0xFF
#define HN_DEVICE_PAIRED 0x80
#define HN_MASTER_DEPARTED 0x40
#define HN_SLAVE_DEPARTED 0x20
#define HN_MASTER_SEND 0x10
#define HN_SLAVE_RECEIVED 0x08
/*Register define */
#define GTP_READ_COOR_ADDR 0x814E
#define GTP_REG_CMD 0x8040
#define GTP_REG_SENSOR_ID 0x814A
#define GTP_REG_CONFIG_DATA 0x8050
#define GTP_REG_CONFIG_RESOLUTION 0x8051
#define GTP_REG_CONFIG_TRIGGER 0x8056
#define GTP_REG_CONFIG_CHECKSUM 0x813C
#define GTP_REG_CONFIG_UPDATE 0x813E
#define GTP_REG_VERSION 0x8140
#define GTP_REG_HW_INFO 0x4220
#define GTP_REG_REFRESH_RATE 0x8056
#define GTP_REG_ESD_CHECK 0x8043
#define GTP_REG_FLASH_PASSBY 0x8006
#define GTP_REG_HN_PAIRED 0x81AA
#define GTP_REG_HN_MODE 0x81A8
#define GTP_REG_MODULE_SWITCH3 0x8058
#define set_reg_bit(reg, index, val) ((reg) ^= (!(val) << (index)))
/* cmd define */
#define GTP_CMD_SLEEP 0x05
#define GTP_CMD_CHARGER_ON 0x06
#define GTP_CMD_CHARGER_OFF 0x07
#define GTP_CMD_GESTURE_WAKEUP 0x08
#define GTP_CMD_CLEAR_CFG 0x10
#define GTP_CMD_ESD 0xAA
#define GTP_CMD_HN_TRANSFER 0x22
#define GTP_CMD_HN_EXIT_SLAVE 0x28
/* define offset in the config*/
#define RESOLUTION_LOC \
(GTP_REG_CONFIG_RESOLUTION - GTP_REG_CONFIG_DATA)
#define TRIGGER_LOC \
(GTP_REG_CONFIG_TRIGGER - GTP_REG_CONFIG_DATA)
#define MODULE_SWITCH3_LOC \
(GTP_REG_MODULE_SWITCH3 - GTP_REG_CONFIG_DATA)
#define GTP_I2C_ADDRESS 0xBA
#define IS_NUM_OR_CHAR(x) \
(((x) > 'A' && (x) < 'Z') || ((x) > '0' && (x) < '9'))
/*Log define*/
#define GTP_INFO(fmt, arg...) \
pr_info("<<GT1151-INF>>[%s:%d] "fmt"\n", __func__, __LINE__, ##arg)
#define GTP_ERROR(fmt, arg...) \
pr_info("<<GT1151-ERR>>[%s:%d] "fmt"\n", __func__, __LINE__, ##arg)
#define GTP_DEBUG(fmt, arg...) \
do { \
if (tpd_em_log) \
pr_debug("<<GT1151-DBG>>[%s:%d]"fmt"\n", \
__func__, __LINE__, ##arg);\
} while (0)
#ifdef CONFIG_GTP_DEBUG_ARRAY_ON
#define GTP_DEBUG_ARRAY(array, num) \
do { \
s32 i; \
u8 *a = array; \
pr_debug("<<GTP-DBG>>"); \
for (i = 0; i < (num); i++) { \
pr_debug("%02x ", (a)[i]); \
if ((i + 1) % 10 == 0) { \
pr_debug("\n<<GTP-DBG>>");\
} \
} \
pr_debug("\n"); \
} while (0)
#else
#define GTP_DEBUG_ARRAY(array, num) do {} while (0)
#endif
#ifdef CONFIG_GTP_DEBUG_FUNC_ON
#define GTP_DEBUG_FUNC() \
pr_debug("<<GTP-FUNC>> Func:%s@Line:%d\n", __func__, __LINE__)
#else
#define GTP_DEBUG_FUNC() do {} while (0)
#endif
#define GTP_SWAP(x, y) \
do { \
typeof(x) z = x; \
x = y; \
y = z; \
} while (0)
#pragma pack(1)
struct gt1x_version_info {
u8 product_id[5];
u32 patch_id;
u32 mask_id;
u8 sensor_id;
u8 match_opt;
};
#pragma pack()
enum DOZE_T {
DOZE_DISABLED = 0,
DOZE_ENABLED = 1,
DOZE_WAKEUP = 2,
};
enum CHIP_TYPE_T {
CHIP_TYPE_GT1X = 0,
CHIP_TYPE_GT2X = 1,
CHIP_TYPE_NONE = 0xFF
};
#define _ERROR(e) ((0x01 << e) | (0x01 << (sizeof(s32) * 8 - 1)))
#define ERROR _ERROR(1) /*for common use */
/*system relevant*/
#define ERROR_IIC _ERROR(2) /*IIC communication error. */
#define ERROR_MEM _ERROR(3) /*memory error. */
/*system irrelevant*/
#define ERROR_HN_VER _ERROR(10) /*HotKnot version error. */
#define ERROR_CHECK _ERROR(11) /*Compare src and dst error. */
#define ERROR_RETRY _ERROR(12) /*Too many retries. */
#define ERROR_PATH _ERROR(13) /*Mount path error */
#define ERROR_FW _ERROR(14)
#define ERROR_FILE _ERROR(15)
#define ERROR_VALUE _ERROR(16) /*Illegal value of variables */
/* bit operation */
#define SET_BIT(data, flag) ((data) |= (flag))
#define CLR_BIT(data, flag) ((data) &= ~(flag))
#define CHK_BIT(data, flag) ((data) & (flag))
/* touch states */
#define BIT_TOUCH 0x01
#define BIT_TOUCH_KEY 0x02
#define BIT_STYLUS 0x04
#define BIT_STYLUS_KEY 0x08
#define BIT_HOVER 0x10
struct i2c_msg;
extern void tpd_on(void);
extern void tpd_off(void);
/* Export global variables and functions */
/* Export from gt1x_extents.c and gt1x_firmware.h */
#ifdef CONFIG_GTP_HOTKNOT
extern u8 hotknot_enabled;
extern u8 hotknot_transfer_mode;
extern u8 gt1x_patch_jump_fw[];
extern u8 hotknot_auth_fw[];
extern u8 hotknot_transfer_fw[];
#ifdef CONFIG_HOTKNOT_BLOCK_RW
extern s32 hotknot_paired_flag;
extern s32 hotknot_event_handler(u8 *data);
#endif
#endif /*CONFIG_GTP_HOTKNOT */
extern s32 gt1x_init_node(void);
extern bool check_flag;
#ifdef CONFIG_GTP_GESTURE_WAKEUP
extern enum DOZE_T gesture_doze_status;
extern int gesture_enabled;
extern s32 gesture_event_handler(struct input_dev *dev);
extern s32 gesture_enter_doze(void);
extern void gesture_clear_wakeup_data(void);
#endif
/* Export from gt1x_tpd.c */
extern void gt1x_touch_down(s32 x, s32 y, s32 size, s32 id);
extern void gt1x_touch_up(s32 id);
extern void gt1x_power_switch(s32 state);
extern void gt1x_irq_enable(void);
extern void gt1x_irq_disable(void);
extern int gt1x_debug_proc(u8 *buf, int count);
struct fw_update_info {
int update_type;
int status;
int progress;
int max_progress;
struct fw_info *firmware;
u32 fw_length;
/* file update */
char *fw_name;
u8 *buffer;
mm_segment_t old_fs;
struct file *fw_file;
/* header update */
u8 *fw_data;
};
/* Export form gt1x_update.c */
extern struct fw_update_info update_info;
extern u8 gt1x_default_FW[];
extern int gt1x_hold_ss51_dsp(void);
extern int gt1x_auto_update_proc(void *data);
extern int gt1x_update_firmware(char *filename);
extern void gt1x_enter_update_mode(void);
extern void gt1x_leave_update_mode(void);
extern int gt1x_hold_ss51_dsp_no_reset(void);
extern int gt1x_load_patch(
u8 *patch, u32 patch_size, int offset, int bank_size);
extern int gt1x_startup_patch(void);
extern void gt1x_auto_update_done(void);
extern int gt1x_is_tpd_halt(void);
/* Export from gt1x_tool.c */
#ifdef GTP_CREATE_WR_NODE
extern int gt1x_init_tool_node(void);
extern void gt1x_deinit_tool_node(void);
#endif
/* Export from gt1x_generic.c */
extern struct i2c_client *gt1x_i2c_client;
extern enum CHIP_TYPE_T gt1x_chip_type;
extern struct gt1x_version_info gt1x_version;
extern s32 gt1x_init_debug_node(void);
extern void gt1x_deinit_debug_node(void);
extern s32 _do_i2c_read(struct i2c_msg *msgs, u16 addr, u8 *buffer, s32 len);
extern s32 _do_i2c_write(struct i2c_msg *msg, u16 addr, u8 *buffer, s32 len);
extern s32 gt1x_i2c_write(u16 addr, u8 *buffer, s32 len);
extern s32 gt1x_i2c_read(u16 addr, u8 *buffer, s32 len);
extern s32 gt1x_i2c_test(void);
extern s32 gt1x_i2c_read_dbl_check(u16 addr, u8 *buffer, s32 len);
extern u8 gt1x_config[];
extern u32 gt1x_cfg_length;
extern u8 gt1x_int_type;
extern u8 gt1x_wakeup_level;
extern u32 gt1x_abs_x_max;
extern u32 gt1x_abs_y_max;
extern u8 gt1x_rawdiff_mode;
extern u8 gt1x_driver_num;
extern u8 gt1x_sensor_num;
extern u8 gt1x_init_failed;
extern s32 gt1x_init(void);
extern void gt1x_deinit(void);
extern s32 gt1x_read_version(struct gt1x_version_info *ver_info);
extern s32 gt1x_enter_sleep(void);
extern s32 gt1x_wakeup_sleep(void);
extern s32 gt1x_init_panel(void);
extern s32 gt1x_get_chip_type(void);
extern s32 gt1x_request_event_handler(void);
extern int gt1x_send_cmd(u8 cmd, u8 data);
extern s32 gt1x_send_cfg(u8 *config, int cfg_len);
extern void gt1x_select_addr(void);
extern s32 gt1x_reset_guitar(void);
extern void gt1x_power_reset(void);
extern void gt1x_power_reset2(void);
extern int gt1x_parse_config(char *filename, u8 *gt1x_config);
extern s32 gt1x_touch_event_handler(
u8 *data, struct input_dev *dev, struct input_dev *pen_dev);
#ifdef CONFIG_GTP_WITH_STYLUS
extern struct input_dev *pen_dev;
extern void gt1x_pen_down(s32 x, s32 y, s32 size, s32 id);
extern void gt1x_pen_up(s32 id);
#endif
#ifdef CONFIG_GTP_PROXIMITY
extern u8 gt1x_proximity_flag;
extern u8 gt1x_proximity_detect;
extern void gt1x_report_ps(u8 state);
extern void gt1x_ps_init(void);
extern int gt1x_prox_event_handler(u8 *data);
#endif
#ifdef CONFIG_GTP_ESD_PROTECT
extern void gt1x_init_esd_protect(void);
extern void gt1x_deinit_esd_protect(void);
extern s32 gt1x_init_ext_watchdog(void);
extern void gt1x_esd_switch(s32 on);
#endif
#ifdef CONFIG_GTP_CHARGER_SWITCH
extern u8 gt1x_config_charger[GTP_CONFIG_MAX_LENGTH];
extern u32 gt1x_get_charger_status(void);
extern void gt1x_charger_switch(s32 on);
extern void gt1x_charger_config(s32 dir_update);
#ifdef MT6573
#define CHR_CON0 (0xF7000000+0x2FA00)
#else
extern bool upmu_is_chr_det(void);
#endif
#endif
extern struct tpd_filter_t tpd_filter;
extern wait_queue_head_t init_waiter;
extern u8 is_resetting;
/* AF power is connected to Touch power */
#ifdef CONFIG_MTK_LENS
extern void AF_PowerDown(void);
#endif
#endif /* GT1X_TPD_COMMON_H__ */
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,55 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2019 MediaTek Inc.
*/
#undef TRACE_SYSTEM
#define TRACE_SYSTEM met_touch
#if !defined(__TRACE_MET_FTRACE_TOUCH_H__) || defined(TRACE_HEADER_MULTI_READ)
#define __TRACE_MET_FTRACE_TOUCH_H__
#include <linux/tracepoint.h>
/*
* Tracepoint for MET_touch
*/
TRACE_EVENT(MET_touch,
TP_PROTO(char *touch_type, long tsec, long tusec,
char *mode, int value),
TP_ARGS(touch_type, tsec, tusec, mode, value),
TP_STRUCT__entry(
__array(char, _touch_type, 16)
__field(long, _tsec)
__field(long, _tusec)
__array(char, _mode, 16)
__field(int, _value)
),
TP_fast_assign(
memcpy(__entry->_touch_type, touch_type, 16);
__entry->_tsec = tsec;
__entry->_tusec = tusec;
memcpy(__entry->_mode, mode, 16);
__entry->_value = value;
),
TP_printk("%s,%ld.%06ld,%s,%x",
__entry->_touch_type,
__entry->_tsec,
__entry->_tusec,
__entry->_mode,
__entry->_value
)
);
#endif /* __TRACE_MET_FTRACE_TOUCH_H__ */
/* This part must be outside protection */
#undef TRACE_INCLUDE_PATH
#undef linux
#define TRACE_INCLUDE_PATH .
#define TRACE_INCLUDE_FILE met_ftrace_touch
#include <trace/define_trace.h>
+186
View File
@@ -0,0 +1,186 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2019 MediaTek Inc.
*/
#ifndef __TPD_H
#define __TPD_H
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/time.h>
#include <linux/types.h>
#include <linux/seq_file.h>
#include <linux/list.h>
#include <linux/proc_fs.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <generated/autoconf.h>
#include <linux/kobject.h>
#include <linux/regulator/consumer.h>
/*debug macros */
#define TPD_DEBUG_OPEN
#define TPD_DEBUG_CODE
/* #define TPD_DEBUG_TRACK */
#define TPD_ERR(a, arg...) \
pr_info(TPD_DEVICE "ERR:[%s:%d] " a, __func__, __LINE__, ##arg)
#ifdef TPD_DEBUG_OPEN
#define TPD_DEBUG(a, arg...) \
pr_info(TPD_DEVICE "INFO:[%s:%d] " a, __func__, __LINE__, ##arg)
#else
#define TPD_DEBUG(arg...)
#endif
#define SPLIT ", "
/* register, address, configurations */
#define TPD_DEVICE "gt1151"
#define TPD_INPUT_DEVICE "mtk-tpd"
#define TPD_X 0
#define TPD_Y 1
#define TPD_Z1 2
#define TPD_Z2 3
#define TP_DELAY (2*HZ/100)
#define TP_DRV_MAX_COUNT (20)
#define TPD_WARP_CNT (4)
#define TPD_VIRTUAL_KEY_MAX (10)
/* various mode */
#define TPD_MODE_NORMAL 0
#define TPD_MODE_KEYPAD 1
#define TPD_MODE_SW 2
#define TPD_MODE_FAV_SW 3
#define TPD_MODE_FAV_HW 4
#define TPD_MODE_RAW_DATA 5
extern int tpd_load_status; /* 0: failed, 1: success */
extern int tpd_mode;
extern int tpd_mode_axis;
extern int tpd_mode_min;
extern int tpd_mode_max;
extern int tpd_mode_keypad_tolerance;
extern int tpd_em_debounce_time;
extern int tpd_em_debounce_time0;
extern int tpd_em_debounce_time1;
extern int tpd_em_asamp;
extern int tpd_em_auto_time_interval;
extern int tpd_em_sample_cnt;
extern int tpd_calmat[];
extern int tpd_def_calmat[];
extern int tpd_calmat[];
extern int tpd_def_calmat[];
extern int TPD_DO_WARP;
extern int tpd_wb_start[];
extern int tpd_wb_end[];
extern int tpd_v_magnify_x;
extern int tpd_v_magnify_y;
extern unsigned int DISP_GetScreenHeight(void);
extern unsigned int DISP_GetScreenWidth(void);
#if defined(CONFIG_MTK_S3320) || defined(CONFIG_MTK_S3320_47) || \
defined(CONFIG_MTK_S3320_50)
extern void synaptics_init_sysfs(void);
#endif /* CONFIG_MTK_S3320 */
extern void tpd_button_init(void);
struct tpd_device {
struct device *tpd_dev;
struct regulator *reg;
struct regulator *io_reg;
struct input_dev *dev;
struct input_dev *kpd;
struct timer_list timer;
struct tasklet_struct tasklet;
int btn_state;
};
struct tpd_key_dim_local {
int key_x;
int key_y;
int key_width;
int key_height;
};
struct tpd_filter_t {
int enable; /*0: disable, 1: enable*/
int pixel_density; /*XXX pixel/cm*/
int W_W[3][4];/*filter custom setting prameters*/
unsigned int VECLOCITY_THRESHOLD[3];/*filter speed custom settings*/
};
struct tpd_dts_info {
int tpd_resolution[2];
int touch_max_num;
int use_tpd_button;
int tpd_key_num;
int tpd_key_local[4];
bool tpd_use_ext_gpio;
int rst_ext_gpio_num;
int rst_gpio_num;
int eint_gpio_num;
int x2x;
int y2y;
const char *cfg_version;
int lcm_resolution[2];
struct tpd_key_dim_local tpd_key_dim_local[4];
struct tpd_filter_t touch_filter;
};
extern struct tpd_dts_info tpd_dts_data;
struct tpd_attrs {
struct device_attribute **attr;
int num;
};
struct tpd_driver_t {
char *tpd_device_name;
int (*tpd_local_init)(void);
void (*suspend)(struct device *h);
void (*resume)(struct device *h);
int tpd_have_button;
struct tpd_attrs attrs;
};
void tpd_button(unsigned int x, unsigned int y, unsigned int down);
void tpd_button_init(void);
ssize_t tpd_virtual_key(char *buf);
/* #ifndef TPD_BUTTON_HEIGHT */
/* #define TPD_BUTTON_HEIGHT TPD_RES_Y */
/* #endif */
extern int tpd_driver_add(struct tpd_driver_t *tpd_drv);
extern int tpd_driver_remove(struct tpd_driver_t *tpd_drv);
void tpd_button_setting(int keycnt, void *keys, void *keys_dim);
extern int tpd_em_spl_num;
extern int tpd_em_pressure_threshold;
extern struct tpd_device *tpd;
extern struct tpd_dts_info tpd_dts_data;
extern void tpd_get_dts_info(void);
#define GTP_RST_PORT 0
#define GTP_INT_PORT 1
extern void tpd_gpio_as_int(int pin);
extern void tpd_gpio_output(int pin, int level);
extern const struct of_device_id touch_of_match[];
#ifdef TPD_DEBUG_CODE
#include "tpd_debug.h"
#endif
#ifdef TPD_DEBUG_TRACK
int DAL_Clean(void);
int DAL_Printf(const char *fmt, ...);
int LCD_LayerEnable(int id, BOOL enable);
#endif
#include "tpd_default.h"
/* switch touch panel into different mode */
void _tpd_switch_single_mode(void);
void _tpd_switch_multiple_mode(void);
void _tpd_switch_sleep_mode(void);
void _tpd_switch_normal_mode(void);
extern int gt1x_driver_init(void);
extern void gt1x_driver_exit(void);
extern int tpd_log_init(void);
extern void tpd_log_exit(void);
extern void gt1x_generic_exit(void);
void gt1x_tool_exit(void);
#endif
@@ -0,0 +1,123 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 MediaTek Inc.
*/
#include "tpd.h"
/* #ifdef TPD_HAVE_BUTTON */
/* static int tpd_keys[TPD_KEY_COUNT] = TPD_KEYS; */
/* static int tpd_keys_dim[TPD_KEY_COUNT][4] = TPD_KEYS_DIM; */
static unsigned int tpd_keycnt;
static int tpd_keys[TPD_VIRTUAL_KEY_MAX] = { 0 };
static int tpd_keys_dim[TPD_VIRTUAL_KEY_MAX][4]; /* = {0}; */
static ssize_t mtk_virtual_keys_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
int i, j;
for (i = 0, j = 0; i < tpd_keycnt; i++)
j += snprintf(buf+j, PAGE_SIZE-j, "%s%s:%d:%d:%d:%d:%d%s", buf,
__stringify(EV_KEY), tpd_keys[i],
tpd_keys_dim[i][0], tpd_keys_dim[i][1],
tpd_keys_dim[i][2], tpd_keys_dim[i][3],
(i == tpd_keycnt - 1 ? "\n" : ":"));
return j;
}
static struct kobj_attribute mtk_virtual_keys_attr = {
.attr = {
.name = "virtualkeys.mtk-tpd",
.mode = 0644,
},
.show = &mtk_virtual_keys_show,
};
static struct attribute *mtk_properties_attrs[] = {
&mtk_virtual_keys_attr.attr,
NULL
};
static struct attribute_group mtk_properties_attr_group = {
.attrs = mtk_properties_attrs,
};
struct kobject *properties_kobj;
void tpd_button_init(void)
{
int ret = 0, i = 0;
/* if((tpd->kpd=input_allocate_device())==NULL) return -ENOMEM; */
tpd->kpd = input_allocate_device();
/* struct input_dev kpd initialization and registration */
tpd->kpd->name = TPD_DEVICE "-kpd";
set_bit(EV_KEY, tpd->kpd->evbit);
/* set_bit(EV_REL, tpd->kpd->evbit); */
/* set_bit(EV_ABS, tpd->kpd->evbit); */
for (i = 0; i < tpd_keycnt; i++)
__set_bit(tpd_keys[i], tpd->kpd->keybit);
tpd->kpd->id.bustype = BUS_HOST;
tpd->kpd->id.vendor = 0x0001;
tpd->kpd->id.product = 0x0001;
tpd->kpd->id.version = 0x0100;
if (input_register_device(tpd->kpd))
TPD_ERR("input_register_device failed.(kpd)\n");
set_bit(EV_KEY, tpd->dev->evbit);
for (i = 0; i < tpd_keycnt; i++)
__set_bit(tpd_keys[i], tpd->dev->keybit);
properties_kobj = kobject_create_and_add("board_properties", NULL);
if (properties_kobj)
ret = sysfs_create_group(properties_kobj,
&mtk_properties_attr_group);
if (!properties_kobj || ret)
TPD_DEBUG("failed to create board_properties\n");
}
void tpd_button(unsigned int x, unsigned int y, unsigned int down)
{
int i;
bool report;
if (down) {
for (i = 0; i < tpd_keycnt; i++) {
report = x >= tpd_keys_dim[i][0] -
(tpd_keys_dim[i][2] / 2) &&
x <= tpd_keys_dim[i][0] +
(tpd_keys_dim[i][2] / 2) &&
y >= tpd_keys_dim[i][1] -
(tpd_keys_dim[i][3] / 2) &&
y <= tpd_keys_dim[i][1] +
(tpd_keys_dim[i][3] / 2) &&
!(tpd->btn_state & (1 << i));
if (report) {
input_report_key(tpd->kpd, tpd_keys[i], 1);
input_sync(tpd->kpd);
tpd->btn_state |= (1 << i);
TPD_DEBUG("press key %d (%d)\n",
i, tpd_keys[i]);
}
}
} else {
for (i = 0; i < tpd_keycnt; i++) {
if (tpd->btn_state & (1 << i)) {
input_report_key(tpd->kpd, tpd_keys[i], 0);
input_sync(tpd->kpd);
TPD_DEBUG("release key %d (%d)\n",
i, tpd_keys[i]);
}
}
tpd->btn_state = 0;
}
}
void tpd_button_setting(int keycnt, void *keys, void *keys_dim)
{
tpd_keycnt = keycnt;
memcpy(tpd_keys, keys, keycnt * 4);
memcpy(tpd_keys_dim, keys_dim, keycnt * 4 * 4);
}
/* #endif */
@@ -0,0 +1,616 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 MediaTek Inc.
*/
#include "tpd.h"
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/fb.h>
#include <linux/of_gpio.h>
#include <linux/workqueue.h>
#ifdef CONFIG_COMPAT
#include <linux/compat.h>
#endif
/* for magnify velocity******************************************** */
#define TOUCH_IOC_MAGIC 'A'
#define TPD_GET_VELOCITY_CUSTOM_X _IO(TOUCH_IOC_MAGIC, 0)
#define TPD_GET_VELOCITY_CUSTOM_Y _IO(TOUCH_IOC_MAGIC, 1)
#define TPD_GET_FILTER_PARA _IOWR(TOUCH_IOC_MAGIC, 2, struct tpd_filter_t)
#ifdef CONFIG_COMPAT
#define COMPAT_TPD_GET_FILTER_PARA _IOWR(TOUCH_IOC_MAGIC, \
2, struct tpd_filter_t)
#endif
struct tpd_filter_t tpd_filter;
struct tpd_dts_info tpd_dts_data;
const struct of_device_id touch_of_match[] = {
{ .compatible = "goodix,touch", },
{},
};
void tpd_get_dts_info(void)
{
struct device_node *node1 = NULL;
int key_dim_local[16], i;
int convert_err = -EINVAL;
node1 = of_find_matching_node(node1, touch_of_match);
if (!node1) {
TPD_ERR("can't find touch compatible custom node\n");
} else {
of_property_read_u32(node1,
"tpd-max-touch-num", &tpd_dts_data.touch_max_num);
of_property_read_u32_array(node1, "tpd-resolution",
tpd_dts_data.tpd_resolution,
ARRAY_SIZE(tpd_dts_data.tpd_resolution));
TPD_DEBUG("[tpd] resulution is %d %d",
tpd_dts_data.tpd_resolution[0],
tpd_dts_data.tpd_resolution[1]);
#if defined(CONFIG_LCM_WIDTH) && defined(CONFIG_LCM_HEIGHT)
convert_err = kstrtou32(CONFIG_LCM_WIDTH, 10,
&tpd_dts_data.lcm_resolution[0]);
if (convert_err)
TPD_ERR("GET LCM WIDTH failed!\n");
convert_err = kstrtou32(CONFIG_LCM_HEIGHT, 10,
&tpd_dts_data.lcm_resolution[1]);
if (convert_err)
TPD_ERR("GET LCM HEIGHT failed!\n");
#else
TPD_DEBUG("Set Default lcm-resolution!");
of_property_read_u32_array(node1, "lcm-resolution",
tpd_dts_data.lcm_resolution,
ARRAY_SIZE(tpd_dts_data.lcm_resolution));
#endif
TPD_DEBUG("[lcm] resulution is %d %d",
tpd_dts_data.lcm_resolution[0],
tpd_dts_data.lcm_resolution[1]);
if (tpd_dts_data.use_tpd_button) {
of_property_read_u32(node1,
"tpd-key-num", &tpd_dts_data.tpd_key_num);
if (of_property_read_u32_array(node1, "tpd-key-local",
tpd_dts_data.tpd_key_local,
ARRAY_SIZE(tpd_dts_data.tpd_key_local)))
TPD_DEBUG("tpd-key-local: %d %d %d %d",
tpd_dts_data.tpd_key_local[0],
tpd_dts_data.tpd_key_local[1],
tpd_dts_data.tpd_key_local[2],
tpd_dts_data.tpd_key_local[3]);
if (of_property_read_u32_array(node1,
"tpd-key-dim-local",
key_dim_local, ARRAY_SIZE(key_dim_local))) {
memcpy(tpd_dts_data.tpd_key_dim_local,
key_dim_local, sizeof(key_dim_local));
for (i = 0; i < 4; i++) {
pr_info("[tpd]key[%d].key_x = %d\n",
i, tpd_dts_data
.tpd_key_dim_local[i]
.key_x);
pr_info("[tpd]key[%d].key_y = %d\n",
i, tpd_dts_data
.tpd_key_dim_local[i]
.key_y);
pr_info("[tpd]key[%d].key_W = %d\n",
i, tpd_dts_data
.tpd_key_dim_local[i]
.key_width);
pr_info("[tpd]key[%d].key_H = %d\n",
i, tpd_dts_data
.tpd_key_dim_local[i]
.key_height);
}
}
}
of_property_read_u32(node1, "tpd-filter-enable",
&tpd_dts_data.touch_filter.enable);
if (tpd_dts_data.touch_filter.enable) {
of_property_read_u32(node1,
"tpd-filter-pixel-density",
&tpd_dts_data.touch_filter.pixel_density);
if (of_property_read_u32_array(node1,
"tpd-filter-custom-prameters",
(u32 *)tpd_dts_data.touch_filter.W_W,
ARRAY_SIZE(tpd_dts_data.touch_filter.W_W)))
TPD_DEBUG("get tpd-filter-custom-parameters");
if (of_property_read_u32_array(node1,
"tpd-filter-custom-speed",
tpd_dts_data.touch_filter.VECLOCITY_THRESHOLD,
ARRAY_SIZE(tpd_dts_data
.touch_filter
.VECLOCITY_THRESHOLD)))
TPD_DEBUG("get tpd-filter-custom-speed");
}
memcpy(&tpd_filter,
&tpd_dts_data.touch_filter, sizeof(tpd_filter));
TPD_DEBUG("[tpd]tpd-filter-enable = %d, pixel_density = %d\n",
tpd_filter.enable, tpd_filter.pixel_density);
tpd_dts_data.tpd_use_ext_gpio =
of_property_read_bool(node1, "tpd-use-ext-gpio");
of_property_read_u32(node1,
"tpd-rst-ext-gpio-num",
&tpd_dts_data.rst_ext_gpio_num);
tpd_dts_data.eint_gpio_num = of_get_named_gpio(node1,
"goodix,eint-gpio", 0);
if (tpd_dts_data.eint_gpio_num < 0) {
TPD_DEBUG("tpd Invalid eint-gpio in dt: %d\n",
tpd_dts_data.eint_gpio_num);
}
tpd_dts_data.rst_gpio_num = of_get_named_gpio(node1,
"goodix,reset-gpio", 0);
if (tpd_dts_data.rst_gpio_num < 0) {
TPD_DEBUG("tpd Invalid reset-gpio in dt: %d\n",
tpd_dts_data.rst_gpio_num);
}
if (of_property_read_string(node1,
"tpd-cfg-version", &tpd_dts_data.cfg_version))
TPD_DEBUG("tpd-cfg-version: %s\n",
tpd_dts_data.cfg_version);
tpd_dts_data.x2x = of_property_read_bool(node1,
"goodix,x2x");
tpd_dts_data.y2y = of_property_read_bool(node1,
"goodix,y2y");
}
}
static int tpd_misc_open(struct inode *inode, struct file *file)
{
return nonseekable_open(inode, file);
}
static int tpd_misc_release(struct inode *inode, struct file *file)
{
return 0;
}
#ifdef CONFIG_COMPAT
static long tpd_compat_ioctl(
struct file *file, unsigned int cmd,
unsigned long arg)
{
long ret;
void __user *arg32 = compat_ptr(arg);
if (!file->f_op || !file->f_op->unlocked_ioctl)
return -ENOTTY;
switch (cmd) {
case COMPAT_TPD_GET_FILTER_PARA:
if (arg32 == NULL) {
TPD_DEBUG("invalid argument.");
return -EINVAL;
}
ret = file->f_op->unlocked_ioctl(file, TPD_GET_FILTER_PARA,
(unsigned long)arg32);
if (ret) {
TPD_DEBUG("TPD_GET_FILTER_PARA unlocked_ioctl failed.");
return ret;
}
break;
default:
TPD_DEBUG("tpd: unknown IOCTL: 0x%08x\n", cmd);
ret = -ENOIOCTLCMD;
break;
}
return ret;
}
#endif
static long tpd_unlocked_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
/* char strbuf[256]; */
void __user *data;
long err = 0;
if (_IOC_DIR(cmd) & _IOC_READ)
err = !access_ok(VERIFY_WRITE,
(void __user *)arg, _IOC_SIZE(cmd));
else if (_IOC_DIR(cmd) & _IOC_WRITE)
err = !access_ok(VERIFY_READ,
(void __user *)arg, _IOC_SIZE(cmd));
if (err) {
TPD_DEBUG("tpd: access error: %08X, (%2d, %2d)\n",
cmd, _IOC_DIR(cmd), _IOC_SIZE(cmd));
return -EFAULT;
}
switch (cmd) {
case TPD_GET_VELOCITY_CUSTOM_X:
data = (void __user *)arg;
if (data == NULL) {
err = -EINVAL;
break;
}
if (copy_to_user(data,
&tpd_v_magnify_x, sizeof(tpd_v_magnify_x))) {
err = -EFAULT;
break;
}
break;
case TPD_GET_VELOCITY_CUSTOM_Y:
data = (void __user *)arg;
if (data == NULL) {
err = -EINVAL;
break;
}
if (copy_to_user(data,
&tpd_v_magnify_y, sizeof(tpd_v_magnify_y))) {
err = -EFAULT;
break;
}
break;
case TPD_GET_FILTER_PARA:
data = (void __user *) arg;
if (data == NULL) {
err = -EINVAL;
TPD_ERR("GET_FILTER_PARA: data is null\n");
break;
}
if (copy_to_user(data, &tpd_filter,
sizeof(struct tpd_filter_t))) {
TPD_ERR("GET_FILTER_PARA: copy data error\n");
err = -EFAULT;
break;
}
break;
default:
TPD_DEBUG("tpd: unknown IOCTL: 0x%08x\n", cmd);
err = -ENOIOCTLCMD;
break;
}
return err;
}
static struct work_struct touch_resume_work;
static struct workqueue_struct *touch_resume_workqueue;
static const struct file_operations tpd_fops = {
/* .owner = THIS_MODULE, */
.open = tpd_misc_open,
.release = tpd_misc_release,
.unlocked_ioctl = tpd_unlocked_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = tpd_compat_ioctl,
#endif
};
/*---------------------------------------------------------------------------*/
static struct miscdevice tpd_misc_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = "touch",
.fops = &tpd_fops,
};
/* ********************************************** */
/* #endif */
/* function definitions */
static int __init tpd_device_init(void);
static void __exit tpd_device_exit(void);
static int tpd_probe(struct platform_device *pdev);
static int tpd_remove(struct platform_device *pdev);
static struct work_struct tpd_init_work;
static struct workqueue_struct *tpd_init_workqueue;
static int tpd_suspend_flag;
int tpd_register_flag;
/* global variable definitions */
struct tpd_device *tpd;
static struct tpd_driver_t tpd_driver_list[TP_DRV_MAX_COUNT]; /* = {0}; */
struct platform_device tpd_device = {
.name = TPD_DEVICE,
.id = -1,
};
const struct dev_pm_ops tpd_pm_ops = {
.suspend = NULL,
.resume = NULL,
};
static struct platform_driver tpd_driver = {
.remove = tpd_remove,
.shutdown = NULL,
.probe = tpd_probe,
.driver = {
.name = TPD_DEVICE,
.pm = &tpd_pm_ops,
.owner = THIS_MODULE,
.of_match_table = touch_of_match,
},
};
static struct tpd_driver_t *g_tpd_drv;
/* hh: use fb_notifier */
static struct notifier_block tpd_fb_notifier;
/* use fb_notifier */
static void touch_resume_workqueue_callback(struct work_struct *work)
{
TPD_DEBUG("GTP %s\n", __func__);
g_tpd_drv->resume(NULL);
tpd_suspend_flag = 0;
}
static int tpd_fb_notifier_callback(
struct notifier_block *self,
unsigned long event, void *data)
{
struct fb_event *evdata = NULL;
int blank;
int err = 0;
TPD_DEBUG("%s\n", __func__);
evdata = data;
/* If we aren't interested in this event, skip it immediately ... */
if (event != FB_EVENT_BLANK)
return 0;
blank = *(int *)evdata->data;
TPD_DEBUG("fb_notify(blank=%d)\n", blank);
switch (blank) {
case FB_BLANK_UNBLANK:
TPD_DEBUG("LCD ON Notify\n");
if (g_tpd_drv && tpd_suspend_flag) {
err = queue_work(touch_resume_workqueue,
&touch_resume_work);
if (!err) {
TPD_ERR("start resume_workqueue failed\n");
return err;
}
}
break;
case FB_BLANK_POWERDOWN:
TPD_DEBUG("LCD OFF Notify\n");
if (g_tpd_drv && !tpd_suspend_flag) {
err = cancel_work_sync(&touch_resume_work);
if (!err)
TPD_ERR("cancel resume_workqueue failed\n");
g_tpd_drv->suspend(NULL);
}
tpd_suspend_flag = 1;
break;
default:
break;
}
return 0;
}
/* Add driver: if find TPD_TYPE_CAPACITIVE driver successfully, loading it */
int tpd_driver_add(struct tpd_driver_t *tpd_drv)
{
int i;
if (g_tpd_drv != NULL) {
TPD_ERR("touch driver exist\n");
return -1;
}
/* check parameter */
if (tpd_drv == NULL)
return -1;
tpd_drv->tpd_have_button = tpd_dts_data.use_tpd_button;
/* R-touch */
if (strcmp(tpd_drv->tpd_device_name, "generic") == 0) {
tpd_driver_list[0].tpd_device_name = tpd_drv->tpd_device_name;
tpd_driver_list[0].tpd_local_init = tpd_drv->tpd_local_init;
tpd_driver_list[0].suspend = tpd_drv->suspend;
tpd_driver_list[0].resume = tpd_drv->resume;
tpd_driver_list[0].tpd_have_button = tpd_drv->tpd_have_button;
return 0;
}
for (i = 1; i < TP_DRV_MAX_COUNT; i++) {
/* add tpd driver into list */
if (tpd_driver_list[i].tpd_device_name == NULL) {
tpd_driver_list[i].tpd_device_name =
tpd_drv->tpd_device_name;
tpd_driver_list[i].tpd_local_init =
tpd_drv->tpd_local_init;
tpd_driver_list[i].suspend = tpd_drv->suspend;
tpd_driver_list[i].resume = tpd_drv->resume;
tpd_driver_list[i].tpd_have_button =
tpd_drv->tpd_have_button;
tpd_driver_list[i].attrs = tpd_drv->attrs;
break;
}
if (strcmp(tpd_driver_list[i].tpd_device_name,
tpd_drv->tpd_device_name) == 0)
return 1; /* driver exist */
}
return 0;
}
int tpd_driver_remove(struct tpd_driver_t *tpd_drv)
{
int i = 0;
/* check parameter */
if (tpd_drv == NULL)
return -1;
for (i = 0; i < TP_DRV_MAX_COUNT; i++) {
/* find it */
if (strcmp(tpd_driver_list[i].tpd_device_name,
tpd_drv->tpd_device_name) == 0) {
memset(&tpd_driver_list[i], 0,
sizeof(struct tpd_driver_t));
break;
}
}
return 0;
}
static void tpd_create_attributes(struct device *dev, struct tpd_attrs *attrs)
{
int num = attrs->num;
for (; num > 0;) {
if (device_create_file(dev, attrs->attr[--num]))
TPD_DEBUG("tpd create attributes file failed\n");
}
}
/* touch panel probe */
static int tpd_probe(struct platform_device *pdev)
{
int touch_type = 1; /* 0:R-touch, 1: Cap-touch */
int i = 0;
TPD_DEBUG("enter %s, %d\n", __func__, __LINE__);
tpd = kmalloc(sizeof(struct tpd_device), GFP_KERNEL);
if (tpd == NULL)
return -ENOMEM;
memset(tpd, 0, sizeof(struct tpd_device));
/* allocate input device */
tpd->dev = input_allocate_device();
if (tpd->dev == NULL) {
kfree(tpd);
return -ENOMEM;
}
tpd_mode = TPD_MODE_NORMAL;
tpd_mode_axis = 0;
tpd_mode_min = tpd_dts_data.lcm_resolution[1] / 2;
tpd_mode_max = tpd_dts_data.lcm_resolution[1];
tpd_mode_keypad_tolerance = tpd_dts_data.lcm_resolution[0] *
tpd_dts_data.lcm_resolution[0] / 1600;
/* save dev for regulator_get() before tpd_local_init() */
tpd->tpd_dev = &pdev->dev;
for (i = 1; i < TP_DRV_MAX_COUNT; i++) {
/* add tpd driver into list */
if (tpd_driver_list[i].tpd_device_name != NULL) {
tpd_driver_list[i].tpd_local_init();
/* msleep(1); */
if (tpd_load_status == 1) {
TPD_DEBUG("%s, tpd_driver_name=%s\n", __func__,
tpd_driver_list[i].tpd_device_name);
g_tpd_drv = &tpd_driver_list[i];
break;
}
}
}
if (g_tpd_drv == NULL) {
if (tpd_driver_list[0].tpd_device_name != NULL) {
g_tpd_drv = &tpd_driver_list[0];
/* touch_type:0: r-touch, 1: C-touch */
touch_type = 0;
g_tpd_drv->tpd_local_init();
TPD_DEBUG("Generic touch panel driver\n");
} else {
TPD_ERR("no touch driver is loaded!!\n");
return 0;
}
}
touch_resume_workqueue = create_singlethread_workqueue("touch_resume");
INIT_WORK(&touch_resume_work, touch_resume_workqueue_callback);
/* use fb_notifier */
tpd_fb_notifier.notifier_call = tpd_fb_notifier_callback;
if (fb_register_client(&tpd_fb_notifier))
TPD_ERR("register fb_notifier fail!\n");
/* TPD_TYPE_CAPACITIVE handle */
if (touch_type == 1) {
/* struct input_dev dev initialization and registration */
tpd->dev->name = TPD_INPUT_DEVICE;
set_bit(EV_SYN, tpd->dev->evbit);
set_bit(EV_ABS, tpd->dev->evbit);
set_bit(EV_KEY, tpd->dev->evbit);
set_bit(BTN_TOUCH, tpd->dev->keybit);
set_bit(BTN_TOOL_FINGER, tpd->dev->keybit);
set_bit(ABS_MT_TRACKING_ID, tpd->dev->absbit);
set_bit(ABS_MT_TOUCH_MAJOR, tpd->dev->absbit);
set_bit(ABS_MT_TOUCH_MINOR, tpd->dev->absbit);
set_bit(ABS_MT_POSITION_X, tpd->dev->absbit);
set_bit(ABS_MT_POSITION_Y, tpd->dev->absbit);
set_bit(INPUT_PROP_DIRECT, tpd->dev->propbit);
input_set_abs_params(tpd->dev,
ABS_MT_POSITION_X, 0,
tpd_dts_data.lcm_resolution[0], 0, 0);
input_set_abs_params(tpd->dev,
ABS_MT_POSITION_Y, 0,
tpd_dts_data.lcm_resolution[1], 0, 0);
input_set_abs_params(tpd->dev,
ABS_MT_TOUCH_MAJOR, 0, 100, 0, 0);
input_set_abs_params(tpd->dev,
ABS_MT_TOUCH_MINOR, 0, 100, 0, 0);
TPD_DEBUG("Cap touch panel driver\n");
}
if (input_register_device(tpd->dev))
TPD_ERR("input_register_device failed.(tpd)\n");
else
tpd_register_flag = 1;
if (g_tpd_drv->tpd_have_button)
tpd_button_init();
if (g_tpd_drv->attrs.num)
tpd_create_attributes(&pdev->dev, &g_tpd_drv->attrs);
if (misc_register(&tpd_misc_device))
TPD_ERR("mtk_tpd: tpd_misc_device register failed\n");
return 0;
}
static int tpd_remove(struct platform_device *pdev)
{
input_unregister_device(tpd->dev);
return 0;
}
/* called when loaded into kernel */
static void tpd_init_work_callback(struct work_struct *work)
{
TPD_DEBUG("MediaTek touch panel driver init\n");
if (platform_driver_register(&tpd_driver) != 0)
TPD_ERR("unable to register touch panel driver.\n");
}
static int __init tpd_device_init(void)
{
int res = 0;
/* load touch driver first */
gt1x_driver_init();
tpd_log_init();
tpd_init_workqueue = create_singlethread_workqueue("mtk-tpd");
INIT_WORK(&tpd_init_work, tpd_init_work_callback);
res = queue_work(tpd_init_workqueue, &tpd_init_work);
if (!res)
TPD_ERR("tpd : touch device init failed res:%d\n", res);
return 0;
}
static void __exit tpd_device_exit(void)
{
tpd_log_exit();
gt1x_generic_exit();
misc_deregister(&tpd_misc_device);
fb_unregister_client(&tpd_fb_notifier);
gt1x_driver_exit();
cancel_work_sync(&tpd_init_work);
destroy_workqueue(tpd_init_workqueue);
platform_driver_unregister(&tpd_driver);
TPD_DEBUG("Touch exit done\n");
}
late_initcall(tpd_device_init);
module_exit(tpd_device_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("MediaTek touch panel driver");
@@ -0,0 +1,440 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 MediaTek Inc.
*/
#include <linux/vmalloc.h>
#include <linux/uaccess.h>
#include <linux/major.h>
#include <linux/miscdevice.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include "tpd.h"
#include "met_ftrace_touch.h"
#ifdef TPD_DEBUG_CODE
int tpd_fail_count;
int tpd_trial_count;
int tpd_debug_nr;
module_param(tpd_debug_nr, int, 0644);
module_param(tpd_fail_count, int, 0644);
module_param(tpd_trial_count, int, 0644);
int tpd_debug_time;
long tpd_last_2_int_time[2] = { 0 };
long tpd_last_down_time;
int tpd_start_profiling;
int tpd_down_status;
module_param(tpd_debug_time, int, 0644);
void tpd_debug_set_time(void)
{
struct timeval t;
if (!tpd_debug_time && !tpd_em_log)
return;
do_gettimeofday(&t);
tpd_last_2_int_time[0] = tpd_last_2_int_time[1];
tpd_last_2_int_time[1] = (t.tv_sec & 0xFFF) * 1000000 + t.tv_usec;
/* */
/* Start profiling while receive touch DOWN event */
/* Stop profiling while the first frame is upadted */
/* */
if (!tpd_down_status) {
tpd_start_profiling = 1;
tpd_last_down_time = tpd_last_2_int_time[1];
}
}
#ifdef TPD_DEBUG_TRACK
int tpd_debug_track;
int tpd_debug_track_color;
int tpd_debug_touch_up;
module_param(tpd_debug_track, int, 0644);
void tpd_draw(int x, int y)
{
UINT16 *buf = (UINT16 *) dal_fb_addr;
buf = buf + (x + y * TPD_RES_X);
tpd_debug_track_color += (tpd_debug_track_color >= 31 ? 0 : 1);
*buf = (0xffe0 + tpd_debug_track_color);
}
void tpd_down_debug_track(int x, int y)
{
if (tpd_debug_touch_up == 1) {
DAL_Clean();
tpd_debug_touch_up = 0;
}
LCD_LayerEnable(5, TRUE);
tpd_draw(x - 1, y - 1);
tpd_draw(x, y - 1);
tpd_draw(x + 1, y - 1);
tpd_draw(x - 1, y);
tpd_draw(x + 1, y);
tpd_draw(x - 1, y + 1);
tpd_draw(x, y + 1);
tpd_draw(x + 1, y + 1);
}
void tpd_up_debug_track(int x, int y)
{
if (x == 0 && y == 0) {
tpd_debug_track_color = 0;
DAL_Clean();
}
tpd_debug_touch_up = 1;
}
#endif /* TPD_DEBUG_TRACK */
#define BUFFER_SIZE 128
int tpd_em_log;
module_param(tpd_em_log, int, 0664);
void tpd_enable_em_log(int enable)
{
if (enable)
tpd_em_log = 1;
else
tpd_em_log = 0;
}
EXPORT_SYMBOL(tpd_enable_em_log);
int tpd_em_log_to_fs;
module_param(tpd_em_log_to_fs, int, 0664);
int tpd_em_log_first = 1;
struct tpd_em_log_struct {
struct list_head list;
char data[BUFFER_SIZE];
};
static LIST_HEAD(tpd_em_log_list);
int tpd_log_line_buffer = 128; /* per line 128 bytes */
int tpd_log_line_cnt = 1024 * 10;
module_param(tpd_log_line_buffer, int, 0664);
module_param(tpd_log_line_cnt, int, 0664);
struct tpd_debug_log_buf {
unsigned int head;
unsigned int tail;
spinlock_t buffer_lock;
unsigned int cnt;
unsigned char *buffer;
};
struct tpd_debug_log_buf tpd_buf;
static int tpd_debug_log_open(struct inode *inode, struct file *file)
{
if (tpd_buf.buffer == NULL)
tpd_buf.buffer =
vmalloc(tpd_log_line_cnt * tpd_log_line_buffer);
if (tpd_buf.buffer == NULL) {
pr_info("tpd_log: nomem for tpd_buf->buffer\n");
return -ENOMEM;
}
spin_lock(&tpd_buf.buffer_lock);
tpd_buf.head = tpd_buf.tail = 0;
tpd_buf.cnt = 0;
spin_unlock(&tpd_buf.buffer_lock);
file->private_data = &tpd_buf;
pr_debug("[tpd_em_log]: open log file\n");
return 0;
}
static int tpd_debug_log_release(struct inode *inode, struct file *file)
{
unsigned char *tmp_buffer = NULL;
pr_debug("[tpd_em_log]: close log file\n");
spin_lock(&tpd_buf.buffer_lock);
tmp_buffer = tpd_buf.buffer;
tpd_buf.buffer = NULL;
spin_unlock(&tpd_buf.buffer_lock);
if (tmp_buffer)
vfree(tmp_buffer);
/* free(tpd_buf); */
return 0;
}
static ssize_t tpd_debug_log_write(struct file *file, const char __user *buffer,
size_t count, loff_t *ppos)
{
return 0;
}
static ssize_t tpd_debug_log_read(struct file *file, char __user *buffer,
size_t count, loff_t *ppos)
{
struct tpd_debug_log_buf *tpd_buf =
(struct tpd_debug_log_buf *)file->private_data;
unsigned int retval = 0, unit = tpd_log_line_buffer;
unsigned char *tmp_buf = NULL;
if (tpd_buf == NULL)
return -ENOMEM;
if (tpd_buf->head == tpd_buf->tail && (file->f_flags & O_NONBLOCK))
return -EAGAIN;
while ((count - retval) >= unit) {
spin_lock_irq(&tpd_buf->buffer_lock);
if (tpd_buf->head != tpd_buf->tail) {
tmp_buf = &tpd_buf->buffer[tpd_buf->tail++ * unit];
tpd_buf->tail &= tpd_log_line_cnt - 1;
} else {
spin_unlock_irq(&tpd_buf->buffer_lock);
break;
}
spin_unlock_irq(&tpd_buf->buffer_lock);
if (copy_to_user(buffer + retval, tmp_buf, unit))
return -EFAULT;
retval += unit;
}
return retval;
}
static unsigned char *tpd_log_find_buffer(void)
{
unsigned char *buffer = NULL;
unsigned int unit = tpd_log_line_buffer;
if (tpd_buf.buffer == NULL) {
pr_info("[tpd_em_log] :tpd_buf.buffer is NULL\n");
return NULL;
}
spin_lock(&tpd_buf.buffer_lock);
buffer = &tpd_buf.buffer[tpd_buf.head++ * unit];
tpd_buf.head &= tpd_log_line_cnt - 1;
spin_unlock(&tpd_buf.buffer_lock);
if (tpd_buf.head == tpd_buf.tail) {
snprintf(buffer, unit, "[tpd_em_log] overlay !!!!!\n");
return NULL;
}
memset(buffer, 0, unit);
return buffer;
}
static const struct file_operations tpd_debug_log_fops = {
.owner = THIS_MODULE,
.read = tpd_debug_log_read,
.write = tpd_debug_log_write,
.open = tpd_debug_log_open,
.release = tpd_debug_log_release,
};
static struct miscdevice tpd_debug_log_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "tpd_em_log",
.fops = &tpd_debug_log_fops,
};
#ifndef CREATE_TRACE_POINTS
#define CREATE_TRACE_POINTS
#endif
noinline void MET_touch(int raw_x, int raw_y,
int cal_x, int cal_y, int p, int down)
{
struct timeval t;
do_gettimeofday(&t);
if ((tpd_down_status == 0 && down == 1) ||
(tpd_down_status == 1 && down == 0)) {
trace_MET_touch("EV_KEY",
t.tv_sec, t.tv_usec, "BTN_TOUCH", down);
tpd_down_status = !tpd_down_status;
}
if (tpd_down_status) {
trace_MET_touch("EV_ABS", t.tv_sec, t.tv_usec, "X", raw_x);
trace_MET_touch("EV_ABS", t.tv_sec, t.tv_usec, "Y", raw_y);
}
}
void tpd_em_log_output(int raw_x, int raw_y,
int cal_x, int cal_y, int p, int down)
{
if (down == TPD_TYPE_INT_DOWN) {
pr_debug("[tpd_em_log] int trigger down\n");
} else if (down == TPD_TYPE_INT_UP) {
pr_debug("[tpd_em_log] int trigger up\n");
} else if (down == TPD_TYPE_TIMER) {
pr_debug("[tpd_em_log] timer trigger\n");
} else if (down == TPD_TYPE_RAW_DATA) {
if (tpd_em_log == TPD_TYPE_RAW_DATA) {
pr_debug("[tpd_em_log] rx=%d,ry=%d,rz1=%d"
SPLIT "rz2=%d,p=%d,r\n",
raw_x, raw_y, cal_x, cal_y, p);
}
} else if (down == TPD_TYPE_REJECT1) {
pr_debug("[tpd_em_log] the first or last point is rejected\n");
} else if (down == TPD_TYPE_REJECT2) {
pr_debug
("[tpd_em_log] pressure(%d) > NICE_PRESSURE(%d)"
SPLIT "debounce debt0:%d ms, debt1:%d ms, spl_num:%d\n",
raw_x, raw_y, cal_x, cal_y, p);
} else if (down == TPD_TYPE_FIST_LATENCY) {
pr_debug("[tpd_em_log] The first touch latency is %d ms\n",
raw_x / 1000);
} else if (down && tpd_down_status == 0) {
pr_debug("[tpd_em_log] rx=%d,ry=%d,cx=%d"
SPLIT "cy=%d,p=%d,d(+%ld ms)\n",
raw_x, raw_y, cal_x, cal_y, p,
(tpd_last_2_int_time[1] -
tpd_last_2_int_time[0]) / 1000);
} else if (down && tpd_down_status != 0) {
pr_debug("[tpd_em_log] rx=%d,ry=%d,cx=%d"
SPLIT "cy=%d,p=%d,m(+%ld ms)\n",
raw_x, raw_y, cal_x, cal_y, p,
(tpd_last_2_int_time[1] -
tpd_last_2_int_time[0]) / 1000);
} else {
pr_debug("[tpd_em_log] rx=%d,ry=%d,cx=%d"
SPLIT "cy=%d,p=%d,u(+%ld ms)\n",
raw_x, raw_y, cal_x, cal_y, p,
(tpd_last_2_int_time[1] -
tpd_last_2_int_time[0]) / 1000);
}
}
void tpd_em_log_store(int raw_x, int raw_y,
int cal_x, int cal_y, int p, int down)
{
/* static struct proc_dir_entry *entry = NULL; */
/* struct tpd_em_log_struct *tpd_em_log_struct_new; */
struct timeval t;
unsigned char *buffer = NULL;
/* unsigned int unit = tpd_log_line_buffer; */
buffer = tpd_log_find_buffer();
if (buffer == NULL) {
pr_info("not buffer\n");
return;
}
do_gettimeofday(&t);
if (down == TPD_TYPE_INT_DOWN) {
snprintf(buffer, tpd_log_line_buffer,
"[%5lu.%06lu][tpd_em_log] int trigger down\n",
(t.tv_sec & 0xFFF), t.tv_usec);
} else if (down == TPD_TYPE_INT_UP) {
snprintf(buffer, tpd_log_line_buffer,
"[%5lu.%06lu][tpd_em_log] int trigger up\n",
(t.tv_sec & 0xFFF), t.tv_usec);
} else if (down == TPD_TYPE_TIMER) {
snprintf(buffer, tpd_log_line_buffer,
"[%5lu.%06lu][tpd_em_log] timer trigger\n",
(t.tv_sec & 0xFFF), t.tv_usec);
} else if (down == TPD_TYPE_RAW_DATA) {
if (tpd_em_log == TPD_TYPE_RAW_DATA) {
snprintf(buffer, tpd_log_line_buffer,
"[%5lu.%06lu][tpd_em_log]"
SPLIT "rx=%d,ry=%d,rz1=%d,rz2=%d,p=%d,r\n",
(t.tv_sec & 0xFFF), t.tv_usec,
raw_x, raw_y, cal_x, cal_y, p);
}
} else if (down == TPD_TYPE_REJECT1) {
snprintf(buffer, tpd_log_line_buffer,
"[%5lu.%06lu][tpd_em_log]"
SPLIT "the first or last point is rejected\n",
(t.tv_sec & 0xFFF), t.tv_usec);
} else if (down == TPD_TYPE_REJECT2) {
snprintf(buffer, tpd_log_line_buffer,
"[%5lu.%06lu][tpd_em_log]"
SPLIT "pressure(%d) > NICE_PRESSURE(%d), "
SPLIT "debounce debt0:%d, debt1:%d, spl_num:%d\n",
(t.tv_sec & 0xFFF), t.tv_usec,
raw_x, raw_y, cal_x, cal_y, p);
} else if (down == TPD_TYPE_FIST_LATENCY) {
snprintf(buffer, tpd_log_line_buffer,
"[%5lu.%06lu][tpd_em_log]"
SPLIT "The first touch latency is %d ms\n",
(t.tv_sec & 0xFFF), t.tv_usec, raw_x / 1000);
} else if (down && tpd_down_status == 0) {
snprintf(buffer, tpd_log_line_buffer,
"[%5lu.%06lu][tpd_em_log]"
SPLIT "rx=%d,ry=%d,cx=%d,cy=%d,p=%d,d(+%ld ms)\n",
(t.tv_sec & 0xFFF), t.tv_usec,
raw_x, raw_y, cal_x, cal_y, p,
(tpd_last_2_int_time[1] -
tpd_last_2_int_time[0]) / 1000);
} else if (down && tpd_down_status != 0) {
snprintf(buffer, tpd_log_line_buffer,
"[%5lu.%06lu][tpd_em_log]"
SPLIT "rx=%d,ry=%d,cx=%d,cy=%d,p=%d,d(+%ld ms)\n",
(t.tv_sec & 0xFFF), t.tv_usec,
raw_x, raw_y, cal_x, cal_y, p,
(tpd_last_2_int_time[1] -
tpd_last_2_int_time[0]) / 1000);
} else {
snprintf(buffer, tpd_log_line_buffer,
"[%5lu.%06lu][tpd_em_log]"
SPLIT "rx=%d,ry=%d,cx=%d,cy=%d,p=%d,u(+%ld ms)\n",
(t.tv_sec & 0xFFF), t.tv_usec,
raw_x, raw_y, cal_x, cal_y, p,
(tpd_last_2_int_time[1] -
tpd_last_2_int_time[0]) / 1000);
}
/* list_add_tail(&tpd_em_log_struct_new->list, &tpd_em_log_list); */
}
void tpd_em_log_release(void)
{
struct tpd_em_log_struct *p, *p_tmp;
if (!tpd_em_log_first) {
remove_proc_entry("tpd_em_log", NULL);
list_for_each_entry_safe(p, p_tmp, &tpd_em_log_list, list) {
list_del(&p->list);
kfree(p);
}
tpd_em_log_first = 1;
tpd_em_log_to_fs = 0;
}
}
int tpd_log_init(void)
{
if (misc_register(&tpd_debug_log_dev) < 0) {
pr_info("[tpd_em_log] :register device failed\n");
return -1;
}
pr_info("[tpd_em_log] :register device successfully\n");
spin_lock_init(&tpd_buf.buffer_lock);
return 0;
}
EXPORT_SYMBOL_GPL(tpd_log_init);
void tpd_log_exit(void)
{
misc_deregister(&tpd_debug_log_dev);
}
EXPORT_SYMBOL_GPL(tpd_log_exit);
int tpd_debuglog;
module_param(tpd_debuglog, int, 0664);
#endif /* TPD_DEBUG_CODE */
@@ -0,0 +1,144 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2019 MediaTek Inc.
*/
#ifndef TPD_DEBUG_H
#define TPD_DEBUG_H
#ifdef TPD_DEBUG_CODE
#include<linux/i2c.h>
extern int tpd_debug_nr;
void tpd_debug_set_time(void);
extern int tpd_debug_time;
extern long tpd_last_2_int_time[2];
extern long tpd_last_down_time;
extern int tpd_start_profiling;
extern int tpd_down_status;
#define TPD_DEBUG_PRINT_INT \
do { \
if (tpd_debug_time) { \
pr_info("tp_int\n"); \
} \
} while (0)
#define TPD_DEBUG_PRINT_UP \
do { \
if (pending == 0 && tpd_debug_time) { \
tpd_down_status = 0; \
pr_debug("up on %ld ms (+%ld ms)\n", \
(tpd_last_2_int_time[1] - tpd_last_down_time) / 1000, \
(tpd_last_2_int_time[1] - tpd_last_2_int_time[0]) / 1000); \
} \
} while (0)
#define TPD_DEBUG_PRINT_DOWN \
do { \
if (tpd_debug_time) { \
if (tpd_down_status == 0)\
pr_debug("down on 0 ms\n"); \
else \
pr_debug("move on %ld ms (+%ld ms)\n", \
(tpd_last_2_int_time[1] - \
tpd_last_down_time) / 1000, \
(tpd_last_2_int_time[1] -\
tpd_last_2_int_time[0]) / 1000); \
tpd_down_status = 1; \
} \
} while (0)
#define TPD_DEBUG_SET_TIME do { tpd_debug_set_time(); } while (0)
extern int tpd_em_log;
extern int tpd_em_log_to_fs;
extern int tpd_type_cap;
void tpd_em_log_output(int raw_x,
int raw_y, int cal_x, int cal_y, int p, int down);
void tpd_em_log_store(int raw_x,
int raw_y, int cal_x, int cal_y, int p, int down);
void tpd_em_log_release(void);
void tpd_enable_em_log(int enable);
#ifndef CREATE_TRACE_POINTS
#define CREATE_TRACE_POINTS
#endif
noinline void MET_touch(int raw_x, int raw_y,
int cal_x, int cal_y, int p, int down);
#define TPD_TYPE_RAW_DATA 2
#define TPD_TYPE_INT_DOWN 3
#define TPD_TYPE_INT_UP 4
#define TPD_TYPE_TIMER 5
#define TPD_TYPE_REJECT1 6
#define TPD_TYPE_REJECT2 7
#define TPD_TYPE_FIST_LATENCY 8
#define TPD_EM_PRINT(raw_x, raw_y, cal_x, cal_y, p, down)\
do { \
if (tpd_em_log) {\
if (!tpd_em_log_to_fs) { \
tpd_em_log_output(raw_x, \
raw_y, cal_x, cal_y, p, down); \
} else { \
tpd_em_log_store(raw_x, \
raw_y, cal_x, cal_y, p, down); \
tpd_em_log_output(raw_x, \
raw_y, cal_x, cal_y, p, down); \
} \
} else { \
if (tpd_em_log_to_fs) { \
tpd_em_log_release(); \
} \
} \
MET_touch(raw_x, raw_y, cal_x, cal_y, p, down); \
} while (0)
#ifdef TPD_DEBUG_TRACK
extern void *dal_fb_addr;
extern int tpd_debug_track;
void tpd_up_debug_track(int x, int y);
void tpd_down_debug_track(int x, int y);
#define TPD_UP_DEBUG_TRACK(x, y) \
do { \
if (tpd_debug_track) \
tpd_up_debug_track(x, y); \
} while (0)
#define TPD_DOWN_DEBUG_TRACK(x, y) \
do { \
if (tpd_debug_track) \
tpd_down_debug_track(x, y); \
} while (0)
#endif /* TPD_DEBUG_TRACK */
#endif /* TPD_DEBUG_CODE */
/* Macros that will be embedded in code */
#ifndef TPD_DEBUG_SET_TIME
#define TPD_DEBUG_SET_TIME
#endif
#ifndef TPD_DEBUG_PRINT_UP
#define TPD_DEBUG_PRINT_UP
#endif
#ifndef TPD_DEBUG_PRINT_DOWN
#define TPD_DEBUG_PRINT_DOWN
#endif
#ifndef TPD_UP_DEBUG_TRACK
#define TPD_UP_DEBUG_TRACK(x, y)
#endif
#ifndef TPD_DOWN_DEBUG_TRACK
#define TPD_DOWN_DEBUG_TRACK(x, y)
#endif
#endif /* TPD_DEBUG_H */
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 MediaTek Inc.
*/
#include "tpd.h"
/* #ifndef TPD_CUSTOM_TREMBLE_TOLERANCE */
int tpd_trembling_tolerance(int t, int p)
{
if (t > 5 || p > 120)
return 200;
if (p > 90)
return 64;
if (p > 80)
return 36;
return 26;
}
/* #endif */
@@ -0,0 +1,10 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2019 MediaTek Inc.
*/
#ifndef TPD_FAT_TOUCH
#define TPD_FAT_TOUCH 120
#endif
int tpd_trembling_tolerance(int t, int p);
@@ -0,0 +1,48 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 MediaTek Inc.
*/
#include "tpd.h"
int tpd_calibrate_en;
module_param(tpd_calibrate_en, int, 0664);
int tpd_show_version;
module_param(tpd_show_version, int, 0664);
/* switch touch panel into single scan mode for decreasing interference */
void tpd_switch_single_mode(void)
{
#ifdef HAVE_SINGLE_MULTIPLE_SCAN_MODE
_tpd_switch_single_mode();
#endif
}
EXPORT_SYMBOL(tpd_switch_single_mode);
/* switch touch panel into multiple scan mode for better performance */
void tpd_switch_multiple_mode(void)
{
#ifdef HAVE_SINGLE_MULTIPLE_SCAN_MODE
_tpd_switch_multiple_mode();
#endif
}
EXPORT_SYMBOL(tpd_switch_multiple_mode);
/* switch touch panel into deep sleep mode */
void tpd_switch_sleep_mode(void)
{
#ifdef HAVE_SLEEP_NORMAL_MODE
_tpd_switch_sleep_mode();
#endif
}
EXPORT_SYMBOL(tpd_switch_sleep_mode);
/* switch touch panel back to normal mode */
void tpd_switch_normal_mode(void)
{
#ifdef HAVE_SLEEP_NORMAL_MODE
_tpd_switch_normal_mode();
#endif
}
EXPORT_SYMBOL(tpd_switch_normal_mode);
@@ -0,0 +1,69 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 MediaTek Inc.
*/
#include "tpd.h"
unsigned long TPD_RES_X = 480;
unsigned long TPD_RES_Y = 800;
/* #if (defined(TPD_HAVE_CALIBRATION) && !defined(TPD_CUSTOM_CALIBRATION)) */
int tpd_calmat[8] = { 0 };
int tpd_def_calmat[8] = { 0 };
int tpd_calmat_size = 8;
int tpd_def_calmat_size = 8;
module_param_array(tpd_calmat, int, &tpd_calmat_size, 0664);
module_param_array(tpd_def_calmat, int, &tpd_def_calmat_size, 0444);
/* #endif */
/* #ifdef TPD_TYPE_CAPACITIVE */
int tpd_type_cap;
int tpd_v_magnify_x = 10;
int tpd_v_magnify_y = 10;
module_param(tpd_v_magnify_x, int, 0664);
module_param(tpd_v_magnify_y, int, 0664);
module_param(tpd_type_cap, int, 0444);
int tpd_firmware_version[2] = { 0, 0 };
int tpd_firmware_version_size = 2;
module_param_array(tpd_firmware_version, int, &tpd_firmware_version_size, 0444);
int tpd_mode = TPD_MODE_NORMAL;
int tpd_mode_axis;
int tpd_mode_min = 400; /* TPD_RES_Y/2; */
int tpd_mode_max = 800; /* TPD_RES_Y; */
/* TPD_RES_X*TPD_RES_X/1600; */
int tpd_mode_keypad_tolerance = 480 * 480 / 1600;
module_param(tpd_mode, int, 0664);
module_param(tpd_mode_axis, int, 0664);
module_param(tpd_mode_min, int, 0664);
module_param(tpd_mode_max, int, 0664);
module_param(tpd_mode_keypad_tolerance, int, 0664);
int tpd_em_debounce_time0 = 1;
int tpd_em_debounce_time; /* =0 */
int tpd_em_debounce_time1 = 4;
module_param(tpd_em_debounce_time0, int, 0664);
module_param(tpd_em_debounce_time1, int, 0664);
module_param(tpd_em_debounce_time, int, 0664);
int tpd_em_spl_num = 1;
module_param(tpd_em_spl_num, int, 0664);
int tpd_em_pressure_threshold;
module_param(tpd_em_pressure_threshold, int, 0664);
int tpd_em_auto_time_interval = 10;
module_param(tpd_em_auto_time_interval, int, 0664);
int tpd_em_sample_cnt = 16;
module_param(tpd_em_sample_cnt, int, 0664);
int tpd_load_status;
module_param(tpd_load_status, int, 0664);
int tpd_em_asamp = 1;
module_param(tpd_em_asamp, int, 0664);
+12
View File
@@ -0,0 +1,12 @@
# Goodix Touchscreen Makefile
# This Makefile is only for tempory compiling use
# Use xxxproject/Makefile-release when release driver
#
# core layer
ccflags-$(CONFIG_DRM_MEDIATEK) += -I$(srctree)/drivers/gpu/drm/mediatek/
obj-y += goodix_ts_i2c.o
obj-y += goodix_ts_core.o
obj-y += goodix_cfg_bin.o
obj-y += goodix_gtx8_update.o
obj-n += goodix_ts_tui.o
@@ -0,0 +1,658 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include "goodix_cfg_bin.h"
#define CFG_TAG ""
int goodix_start_cfg_bin(struct goodix_ts_core *ts_core)
{
struct task_struct *cfg_bin_thrd;
/* create and run update thread */
cfg_bin_thrd = kthread_run(goodix_cfg_bin_proc,
ts_core, "goodix-parse_cfg_bin");
if (IS_ERR_OR_NULL(cfg_bin_thrd)) {
ts_err("Failed to create update thread:%ld",
PTR_ERR(cfg_bin_thrd));
return -EFAULT;
}
return 0;
}
int goodix_parse_cfg_bin(struct goodix_cfg_bin *cfg_bin)
{
u8 checksum;
int i, r;
u16 offset1, offset2;
if (!cfg_bin->bin_data || cfg_bin->bin_data_len == 0) {
ts_err("NO cfg_bin data, cfg_bin data length:%d",
cfg_bin->bin_data_len);
r = -EINVAL;
goto exit;
}
/* copy cfg_bin head info */
if (cfg_bin->bin_data_len < sizeof(struct goodix_cfg_bin_head)) {
ts_err("Invalid cfg_bin size:%d", cfg_bin->bin_data_len);
r = -EINVAL;
goto exit;
}
memcpy(&cfg_bin->head, cfg_bin->bin_data,
sizeof(struct goodix_cfg_bin_head));
cfg_bin->head.bin_len = le32_to_cpu(cfg_bin->head.bin_len);
/*check length*/
if (cfg_bin->bin_data_len != cfg_bin->head.bin_len) {
ts_err("cfg_bin len error,cfg_bin len:%d\n,"
CFG_TAG "len of firmware:%d\n"
, cfg_bin->head.bin_len
, cfg_bin->bin_data_len);
r = -EINVAL;
goto exit;
}
/*check cfg_bin valid*/
checksum = 0;
for (i = TS_BIN_VERSION_START_INDEX; i < cfg_bin->bin_data_len; i++)
checksum += cfg_bin->bin_data[i];
if (checksum != cfg_bin->head.checksum) {
ts_err("cfg_bin checksum ERROR, checksum in cfg_bin:0x%02x,"
CFG_TAG "checksum caculate:0x%02x"
, cfg_bin->head.checksum, checksum);
r = -EINVAL;
goto exit;
}
/*allocate memory for cfg packages*/
cfg_bin->cfg_pkgs =
kzalloc(sizeof(struct goodix_cfg_package) * cfg_bin->head.pkg_num,
GFP_KERNEL);
if (!cfg_bin->cfg_pkgs) {
ts_err("cfg_pkgs, allocate memory ERROR");
r = -ENOMEM;
goto exit;
}
/*get cfg_pkg's info*/
for (i = 0; i < cfg_bin->head.pkg_num; i++) {
/*get cfg pkg length*/
if (i == cfg_bin->head.pkg_num - 1) {
offset1 = cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN +
i * TS_CFG_OFFSET_LEN] +
(cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN +
i * TS_CFG_OFFSET_LEN + 1] << 8);
cfg_bin->cfg_pkgs[i].pkg_len = cfg_bin->bin_data_len
- offset1;
} else {
offset1 = cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN +
i * TS_CFG_OFFSET_LEN] +
(cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN +
i * TS_CFG_OFFSET_LEN + 1] << 8);
offset2 = cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN +
i * TS_CFG_OFFSET_LEN + 2] +
(cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN +
i * TS_CFG_OFFSET_LEN + 3] << 8);
if (offset2 <= offset1) {
ts_err("offset error,pkg:%d, offset1:%d,"
CFG_TAG "offset2:%d"
, i, offset1, offset2);
r = -EINVAL;
goto exit;
}
cfg_bin->cfg_pkgs[i].pkg_len = offset2 - offset1;
}
/*get cfg pkg head*/
memcpy(&cfg_bin->cfg_pkgs[i].cnst_info,
&cfg_bin->bin_data[offset1],
TS_PKG_CONST_INFO_LEN);
memcpy(&cfg_bin->cfg_pkgs[i].reg_info,
&cfg_bin->bin_data[offset1 + TS_PKG_CONST_INFO_LEN],
TS_PKG_REG_INFO_LEN);
/*compatible little edition and big edition*/
goodix_cfg_pkg_leToCpu(&cfg_bin->cfg_pkgs[i]);
/*get configuration data*/
cfg_bin->cfg_pkgs[i].cfg =
&cfg_bin->bin_data[offset1 + TS_PKG_HEAD_LEN];
}
/*debug, print pkg information*/
for (i = 0; i < cfg_bin->head.pkg_num; i++) {
ts_info("---------------------------------------------");
ts_info("------package:%d------", i + 1);
ts_info("package len:%04x",
cfg_bin->cfg_pkgs[i].cnst_info.pkg_len);
ts_info("package ic_type:%s",
cfg_bin->cfg_pkgs[i].cnst_info.ic_type);
ts_info("package cfg_type:%01x",
cfg_bin->cfg_pkgs[i].cnst_info.cfg_type);
ts_info("package sensor_id:%01x",
cfg_bin->cfg_pkgs[i].cnst_info.sensor_id);
ts_info("package hw_pid:%s",
cfg_bin->cfg_pkgs[i].cnst_info.hw_pid);
ts_info("package hw_vid:%s",
cfg_bin->cfg_pkgs[i].cnst_info.hw_vid);
ts_info("package fw_mask_version:%s",
cfg_bin->cfg_pkgs[i].cnst_info.fw_mask);
ts_info("package fw_patch_version:%s",
cfg_bin->cfg_pkgs[i].cnst_info.fw_patch);
ts_info("package x_res_offset:%02x",
cfg_bin->cfg_pkgs[i].cnst_info.x_res_offset);
ts_info("package y_res_offset:%02x",
cfg_bin->cfg_pkgs[i].cnst_info.y_res_offset);
ts_info("package trigger_offset:%02x",
cfg_bin->cfg_pkgs[i].cnst_info.trigger_offset);
ts_info("");
ts_info("send_cfg_flag reg:%02x",
cfg_bin->cfg_pkgs[i].reg_info.cfg_send_flag.addr);
ts_info("version base reg:%02x, len:%d",
cfg_bin->cfg_pkgs[i].reg_info.version_base.addr,
cfg_bin->cfg_pkgs[i].reg_info.version_base.reserved1);
ts_info("pid reg:%02x", cfg_bin->cfg_pkgs[i].reg_info.pid.addr);
ts_info("vid reg:%02x", cfg_bin->cfg_pkgs[i].reg_info.vid.addr);
ts_info("sensor_id reg:%02x",
cfg_bin->cfg_pkgs[i].reg_info.sensor_id.addr);
ts_info("fw_status reg:%02x",
cfg_bin->cfg_pkgs[i].reg_info.fw_status.addr);
ts_info("cfg_addr reg:%02x",
cfg_bin->cfg_pkgs[i].reg_info.cfg_addr.addr);
ts_info("esd reg:%02x", cfg_bin->cfg_pkgs[i].reg_info.esd.addr);
ts_info("command reg:%02x",
cfg_bin->cfg_pkgs[i].reg_info.command.addr);
ts_info("coor:%02x",
cfg_bin->cfg_pkgs[i].reg_info.coor.addr);
ts_info("gesture:%02x",
cfg_bin->cfg_pkgs[i].reg_info.gesture.addr);
ts_info("fw_request:%02x",
cfg_bin->cfg_pkgs[i].reg_info.fw_request.addr);
ts_info("proximity:%02x",
cfg_bin->cfg_pkgs[i].reg_info.proximity.addr);
ts_info("--------------------------------------------");
}
r = 0;
exit:
return r;
}
int goodix_cfg_bin_proc(void *data)
{
struct goodix_ts_core *core_data = data;
struct goodix_ts_device *ts_dev = core_data->ts_dev;
struct device *dev = ts_dev->dev;
int r;
struct goodix_cfg_bin *cfg_bin =
kzalloc(sizeof(struct goodix_cfg_bin), GFP_KERNEL);
if (!cfg_bin) {
ts_err("Failed to alloc memory for cfg_bin");
r = -ENOMEM;
goto out;
}
cfg_bin->cfg_pkgs = NULL;
/*get cfg_bin from file system*/
r = goodix_read_cfg_bin(dev, cfg_bin);
if (r < 0) {
ts_err("read cfg_bin from /etc/firmware FAILED");
goto exit;
}
/*parse cfg bin*/
r = goodix_parse_cfg_bin(cfg_bin);
if (!r) {
ts_info("parse cfg bin SUCCESS");
} else {
ts_err("parse cfg bin FAILED");
goto exit;
}
/*init i2c_set_doze_mode para*/
ts_dev->doze_mode_set_count = 0;
mutex_init(&ts_dev->doze_mode_lock);
/*get register address and configuration from cfg bin*/
r = goodix_get_reg_and_cfg(ts_dev, cfg_bin);
if (!r) {
ts_info("get reg and cfg from cfg_bin SUCCESS");
} else {
if (r != -EBUS) {
ts_err("get reg and cfg from cfg_bin FAILED,"
CFG_TAG "update fw then retry");
goodix_modules.core_data = core_data;
goodix_modules.core_exit = false;
complete_all(&goodix_modules.core_comp);
goto exit;
} else {
ts_err("get reg and cfg from cfg_bin FAILED,"
CFG_TAG "I2C com ERROR");
goto exit;
}
}
/*debug*/
ts_info("@@@@@@@@@");
ts_info("cfg_send_flag:0x%04x", ts_dev->reg.cfg_send_flag);
ts_info("pid:0x%04x", ts_dev->reg.pid);
ts_info("vid:0x%04x", ts_dev->reg.vid);
ts_info("sensor_id:0x%04x", ts_dev->reg.sensor_id);
ts_info("fw_mask:0x%04x", ts_dev->reg.fw_mask);
ts_info("fw_status:0x%04x", ts_dev->reg.fw_status);
ts_info("cfg_addr:0x%04x", ts_dev->reg.cfg_addr);
ts_info("esd:0x%04x", ts_dev->reg.esd);
ts_info("command:0x%04x", ts_dev->reg.command);
ts_info("coor:0x%04x", ts_dev->reg.coor);
ts_info("gesture:0x%04x", ts_dev->reg.gesture);
ts_info("fw_request:0x%04x", ts_dev->reg.fw_request);
ts_info("proximity:0x%04x", ts_dev->reg.proximity);
ts_info("@@@@@@@@@");
/* initialize firmware */
r = ts_dev->hw_ops->init(ts_dev);
if (-EBUS == r)
goto exit;
/* alloc/config/register input device */
r = goodix_ts_input_dev_config(core_data);
if (r < 0)
goto exit;
/* request irq line */
r = goodix_ts_irq_setup(core_data);
if (r < 0)
goto exit;
/*set flag, prevent fwupdate module parse cfg_group again*/
core_data->cfg_group_parsed = true;
/* inform the external module manager that*/
/* touch core layer is ready now */
goodix_modules.core_data = core_data;
goodix_modules.core_exit = false;
/*complete_all(&goodix_modules.core_comp);*/
#ifdef CONFIG_FB
core_data->fb_notifier.notifier_call = goodix_ts_fb_notifier_callback;
if (fb_register_client(&core_data->fb_notifier))
ts_err("Failed to register fb notifier client:%d", r);
#elif defined(CONFIG_HAS_EARLYSUSPEND)
core_data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
core_data->early_suspend.resume = goodix_ts_lateresume;
core_data->early_suspend.suspend = goodix_ts_earlysuspend;
register_early_suspend(&core_data->early_suspend);
#endif
/* esd protector */
#ifdef GT9886_ESD_ENABLE
goodix_ts_esd_init(core_data);
#endif
/* generic notifier callback */
core_data->ts_notifier.notifier_call = goodix_generic_noti_callback;
goodix_ts_register_notifier(&core_data->ts_notifier);
exit:
complete_all(&goodix_modules.core_comp);
if (cfg_bin->cfg_pkgs) {
kfree(cfg_bin->cfg_pkgs);
cfg_bin->cfg_pkgs = NULL;
}
if (cfg_bin->bin_data) {
kfree(cfg_bin->bin_data);
cfg_bin->bin_data = NULL;
}
kfree(cfg_bin);
out:
return r;
}
int goodix_get_reg_and_cfg(struct goodix_ts_device *ts_dev,
struct goodix_cfg_bin *cfg_bin)
{
int i;
u16 addr;
u8 read_len;
struct goodix_cfg_package *normal_pkg, *high_sense_pkg;
char temp_sensor_id = -1;
u8 temp_fw_mask[TS_CFG_BLOCK_FW_MASK_LEN] = {0x00};
u8 temp_pid[TS_CFG_BLOCK_PID_LEN] = {0x00};
int r = -EINVAL;
normal_pkg = NULL;
high_sense_pkg = NULL;
if (!cfg_bin->head.pkg_num || !cfg_bin->cfg_pkgs) {
ts_err("there is none cfg package, pkg_num:%d",
cfg_bin->head.pkg_num);
r = -EINVAL;
goto exit;
}
/*select suitable cfg packages*/
for (i = 0; i < cfg_bin->head.pkg_num; i++) {
/*get ic type*/
if (!strncmp(cfg_bin->cfg_pkgs[i].cnst_info.ic_type, "nanjing",
sizeof(cfg_bin->cfg_pkgs[i].cnst_info.ic_type)))
ts_dev->ic_type = IC_TYPE_NANJING;
else if (!strncmp(cfg_bin->cfg_pkgs[i].cnst_info.ic_type,
"normandy",
sizeof(cfg_bin->cfg_pkgs[i].cnst_info.ic_type)))
ts_dev->ic_type = IC_TYPE_NORMANDY;
else
ts_err("get ic type FAILED,"
CFG_TAG "unknown ic type from cfg_bin:%s",
cfg_bin->cfg_pkgs[i].cnst_info.ic_type);
ts_info("ic_type:%d", ts_dev->ic_type);
/*contrast sensor id*/
addr = cfg_bin->cfg_pkgs[i].reg_info.sensor_id.addr;
if (!addr) {
ts_info("pkg:%d, sensor_id reg is NULL", i);
continue;
}
r = goodix_i2c_read(ts_dev, addr,
&temp_sensor_id, 1);
if (r < 0) {
ts_err("read sensor id FAILED,I2C ERROR, pkg:%d,"
CFG_TAG "sensor_id reg:0x%02x", i, addr);
goto exit;
} else {
/*sensor.reserved1 is a mask, if it's not ZERO, use it*/
if (cfg_bin->cfg_pkgs[i].reg_info.sensor_id.reserved1
!= 0)
temp_sensor_id &=
cfg_bin->cfg_pkgs[i].reg_info.sensor_id.reserved1;
if (temp_sensor_id !=
cfg_bin->cfg_pkgs[i].cnst_info.sensor_id) {
ts_err("pkg:%d, sensor id contrast FAILED,"
CFG_TAG "reg:0x%02x", i, addr);
ts_err("sensor_id from i2c:%d, sensor_id of cfg"
CFG_TAG "bin:%d", temp_sensor_id,
cfg_bin->cfg_pkgs[i].cnst_info.sensor_id);
continue;
}
}
/*contrast fw_mask, if this reg is null, skip this step*/
addr = cfg_bin->cfg_pkgs[i].reg_info.fw_mask.addr;
if (!addr || !cfg_bin->cfg_pkgs[i].cnst_info.fw_mask[0]) {
ts_err("pkg:%d, fw_mask of cfg bin is NULL, Skip!!", i);
} else {
r = goodix_i2c_read(ts_dev, addr, temp_fw_mask,
sizeof(temp_fw_mask));
if (r < 0) {
ts_err("read fw_mask FAILED, I2C ERROR,pkg:"
CFG_TAG "%d, fw_mask reg:0x%02x", i, addr);
goto exit;
} else if (strncmp(temp_fw_mask,
cfg_bin->cfg_pkgs[i].cnst_info.fw_mask,
sizeof(temp_fw_mask))) {
ts_err("pkg:%d, fw_mask contrast FAILED,"
CFG_TAG "reg:0x%02x,", i, addr);
ts_err("mask from i2c:%s, mask of cfg bin:%s",
temp_fw_mask,
cfg_bin->cfg_pkgs[i].cnst_info.fw_mask);
continue;
}
}
/*contrast pid*/
addr = cfg_bin->cfg_pkgs[i].reg_info.pid.addr;
read_len = cfg_bin->cfg_pkgs[i].reg_info.pid.reserved1;
if (!addr) {
ts_err("pkg:%d, pid reg is NULL", i);
continue;
}
if (read_len <= 0 || read_len > TS_CFG_BLOCK_PID_LEN) {
ts_err("pkg:%d, hw_pid len ERROR, len:%d", i, read_len);
continue;
}
r = goodix_i2c_read(ts_dev, addr, temp_pid, read_len);
if (r < 0) {
ts_err("read pid FAILED, I2C ERROR, pkg: %d,"
CFG_TAG "pid reg:0x%02x", i, addr);
goto exit;
} else if (strncmp(temp_pid,\
cfg_bin->cfg_pkgs[i].cnst_info.hw_pid, read_len)) {
ts_err("pkg:%d, pid contrast FAILED,"
CFG_TAG "reg:0x%02x", i, addr);
ts_err("pid from i2c:%s, pid of cfg bin:%s"
, temp_pid,
cfg_bin->cfg_pkgs[i].cnst_info.hw_pid);
continue;
}
/*contrast success, cfg_type*/
if (cfg_bin->cfg_pkgs[i].cnst_info.cfg_type == TS_NORMAL_CFG) {
ts_info("find normal cfg_pkg SUCCESS");
r = 0;
normal_pkg = &cfg_bin->cfg_pkgs[i];
}
if (cfg_bin->cfg_pkgs[i].cnst_info.cfg_type ==
TS_HIGH_SENSE_CFG) {
ts_info("find high sense cfg_pkg SUCCESS");
high_sense_pkg = &cfg_bin->cfg_pkgs[i];
}
}
/*get register address from normal_pkg*/
if (!normal_pkg) {
ts_err("ERROR, none suitable normal_pkg exist in cfg_bin");
/*ts_dev->ic_type = IC_TYPE_NONE;*/
r = -EINVAL;
goto exit;
} else {
/*get ic type*/
if (!strncmp(normal_pkg->cnst_info.ic_type, "nanjing",
sizeof(normal_pkg->cnst_info.ic_type)))
ts_dev->ic_type = IC_TYPE_NANJING;
else if (!strncmp(normal_pkg->cnst_info.ic_type, "normandy",
sizeof(normal_pkg->cnst_info.ic_type)))
ts_dev->ic_type = IC_TYPE_NORMANDY;
else
ts_err("get ic type FAILED, unknown ic type from"
CFG_TAG "cfg_bin:%s"
, normal_pkg->cnst_info.ic_type);
/*get register info*/
ts_dev->reg.cfg_send_flag =
normal_pkg->reg_info.cfg_send_flag.addr;
ts_dev->reg.version_base =
normal_pkg->reg_info.version_base.addr;
ts_dev->reg.version_len =
normal_pkg->reg_info.version_base.reserved1;
ts_dev->reg.pid = normal_pkg->reg_info.pid.addr;
ts_dev->reg.pid_len = normal_pkg->reg_info.pid.reserved1;
ts_dev->reg.vid = normal_pkg->reg_info.vid.addr;
ts_dev->reg.vid_len = normal_pkg->reg_info.vid.reserved1;
ts_dev->reg.sensor_id = normal_pkg->reg_info.sensor_id.addr;
ts_dev->reg.sensor_id_mask =
normal_pkg->reg_info.sensor_id.reserved1;
ts_dev->reg.fw_mask = normal_pkg->reg_info.fw_mask.addr;
ts_dev->reg.fw_status = normal_pkg->reg_info.fw_status.addr;
ts_dev->reg.cfg_addr = normal_pkg->reg_info.cfg_addr.addr;
ts_dev->reg.esd = normal_pkg->reg_info.esd.addr;
ts_dev->reg.command = normal_pkg->reg_info.command.addr;
ts_dev->reg.coor = normal_pkg->reg_info.coor.addr;
ts_dev->reg.gesture = normal_pkg->reg_info.gesture.addr;
ts_dev->reg.fw_request = normal_pkg->reg_info.fw_request.addr;
ts_dev->reg.proximity = normal_pkg->reg_info.proximity.addr;
}
/*get configuration from pkgs*/
if (normal_pkg) {
ts_info("normal cfg is found!");
if (!ts_dev->normal_cfg) {
ts_dev->normal_cfg = devm_kzalloc(ts_dev->dev,
sizeof(*ts_dev->normal_cfg), GFP_KERNEL);
if (!ts_dev->normal_cfg) {
ts_err("Failed to alloc memory for normal cfg");
return -ENOMEM;
}
mutex_init(&ts_dev->normal_cfg->lock);
}
ts_dev->normal_cfg->length = normal_pkg->pkg_len -
TS_PKG_CONST_INFO_LEN - TS_PKG_REG_INFO_LEN;
memcpy(ts_dev->normal_cfg->data, normal_pkg->cfg
, ts_dev->normal_cfg->length);
}
if (high_sense_pkg) {
ts_info("high sense cfg is found!");
if (!ts_dev->highsense_cfg) {
ts_dev->highsense_cfg = devm_kzalloc(ts_dev->dev,
sizeof(*ts_dev->highsense_cfg), GFP_KERNEL);
if (!ts_dev->highsense_cfg) {
ts_err("Failed to alloc"
CFG_TAG " memory for high sense cfg");
return -ENOMEM;
}
mutex_init(&ts_dev->highsense_cfg->lock);
}
ts_dev->highsense_cfg->length = high_sense_pkg->pkg_len -
TS_PKG_CONST_INFO_LEN - TS_PKG_REG_INFO_LEN;
memcpy(ts_dev->highsense_cfg->data,
high_sense_pkg->cfg,
ts_dev->highsense_cfg->length);
}
exit:
return r;
}
int goodix_read_cfg_bin(struct device *dev, struct goodix_cfg_bin *cfg_bin)
{
int r;
const struct firmware *firmware = NULL;
char cfg_bin_name[32] = {0};
int i = 0;
/*get cfg_bin_name*/
r = snprintf(cfg_bin_name, sizeof(cfg_bin_name), "%s%s.bin",
TS_DEFAULT_CFG_BIN, gt9886_config_buf);
if (r >= sizeof(cfg_bin_name)) {
ts_err("get cfg_bin name FAILED!!!");
goto exit;
}
ts_info("cfg_bin_name:%s", cfg_bin_name);
for (i = 0; i < TS_RQST_FW_RETRY_TIMES; i++) {
r = request_firmware(&firmware, cfg_bin_name, dev);
if (r < 0) {
ts_err("Cfg_bin image [%s] not available,error:%d,"
CFG_TAG"try_times:%d", cfg_bin_name, r, i + 1);
msleep(3000);
} else {
ts_info("Cfg_bin image [%s] is ready, try_times:%d",
cfg_bin_name, i + 1);
break;
}
}
if (i >= TS_RQST_FW_RETRY_TIMES) {
ts_err("get cfg_bin FAILED");
goto exit;
}
if (firmware->size <= 0) {
ts_err("request_firmware, cfg_bin length ERROR,len:%zu",
firmware->size);
r = -EINVAL;
goto exit;
}
cfg_bin->bin_data_len = firmware->size;
/*allocate memory for cfg_bin->bin_data*/
cfg_bin->bin_data = kzalloc(cfg_bin->bin_data_len, GFP_KERNEL);
if (!cfg_bin->bin_data) {
ts_err("Allocate memory for cfg_bin->bin_data FAILED");
r = -ENOMEM;
goto exit;
}
memcpy(cfg_bin->bin_data, firmware->data, cfg_bin->bin_data_len);
r = 0;
exit:
if (firmware) {
release_firmware(firmware);
firmware = NULL;
}
return r;
}
int goodix_read_cfg_bin_from_dts(struct device_node *node,
struct goodix_cfg_bin *cfg_bin)
{
unsigned int len = 0;
struct property *prop = NULL;
prop = of_find_property(node, "goodix_cfg_bin", &len);
if (!prop || !prop->value || len == 0) {
ts_err("Invalid cfg type, size:%u", len);
return -EINVAL;
}
cfg_bin->bin_data_len = len;
/*allocate memory for cfg_bin->bin_data*/
cfg_bin->bin_data = kzalloc(cfg_bin->bin_data_len, GFP_KERNEL);
if (!cfg_bin->bin_data) {
ts_err("Allocate memory for cfg_bin->bin_data FAILED");
return -ENOMEM;
}
memcpy(cfg_bin->bin_data, prop->value, cfg_bin->bin_data_len);
return 0;
}
void goodix_cfg_pkg_leToCpu(struct goodix_cfg_package *pkg)
{
if (!pkg) {
ts_err("cfg package is NULL");
return;
}
/*package const_info*/
pkg->cnst_info.pkg_len = le32_to_cpu(pkg->cnst_info.pkg_len);
pkg->cnst_info.x_res_offset = le16_to_cpu(pkg->cnst_info.x_res_offset);
pkg->cnst_info.y_res_offset = le16_to_cpu(pkg->cnst_info.y_res_offset);
pkg->cnst_info.trigger_offset =
le16_to_cpu(pkg->cnst_info.trigger_offset);
/*package reg_info*/
pkg->reg_info.cfg_send_flag.addr =
le16_to_cpu(pkg->reg_info.cfg_send_flag.addr);
pkg->reg_info.pid.addr = le16_to_cpu(pkg->reg_info.pid.addr);
pkg->reg_info.vid.addr = le16_to_cpu(pkg->reg_info.vid.addr);
pkg->reg_info.sensor_id.addr =
le16_to_cpu(pkg->reg_info.sensor_id.addr);
pkg->reg_info.fw_status.addr =
le16_to_cpu(pkg->reg_info.fw_status.addr);
pkg->reg_info.cfg_addr.addr = le16_to_cpu(pkg->reg_info.cfg_addr.addr);
pkg->reg_info.esd.addr = le16_to_cpu(pkg->reg_info.esd.addr);
pkg->reg_info.command.addr = le16_to_cpu(pkg->reg_info.command.addr);
pkg->reg_info.coor.addr = le16_to_cpu(pkg->reg_info.coor.addr);
pkg->reg_info.gesture.addr = le16_to_cpu(pkg->reg_info.gesture.addr);
pkg->reg_info.fw_request.addr =
le16_to_cpu(pkg->reg_info.fw_request.addr);
pkg->reg_info.proximity.addr =
le16_to_cpu(pkg->reg_info.proximity.addr);
}
@@ -0,0 +1,124 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef _GOODIX_CFG_BIN_H_
#define _GOODIX_CFG_BIN_H_
#include "goodix_ts_core.h"
#define TS_DEFAULT_FIRMWARE "gt9886_firmware_"
#define TS_DEFAULT_CFG_BIN "gt9886_cfg_"
extern const char *gt9886_firmware_buf;
extern const char *gt9886_config_buf;
#define TS_BIN_VERSION_START_INDEX 5
#define TS_BIN_VERSION_LEN 4
#define TS_CFG_BIN_HEAD_RESERVED_LEN 6
#define TS_CFG_OFFSET_LEN 2
#define TS_IC_TYPE_NAME_MAX_LEN 15
#define TS_CFG_BIN_HEAD_LEN \
(sizeof(struct goodix_cfg_bin_head) + TS_CFG_BIN_HEAD_RESERVED_LEN)
#define TS_PKG_CONST_INFO_LEN (sizeof(struct goodix_cfg_pkg_const_info))
#define TS_PKG_REG_INFO_LEN (sizeof(struct goodix_cfg_pkg_reg_info))
#define TS_PKG_HEAD_LEN (TS_PKG_CONST_INFO_LEN + TS_PKG_REG_INFO_LEN)
/*cfg block definitin*/
#define TS_CFG_BLOCK_PID_LEN 8
#define TS_CFG_BLOCK_VID_LEN 8
#define TS_CFG_BLOCK_FW_MASK_LEN 9
#define TS_CFG_BLOCK_FW_PATCH_LEN 4
#define TS_CFG_BLOCK_RESERVED_LEN 9
#define TS_NORMAL_CFG 0x01
#define TS_HIGH_SENSE_CFG 0x03
#define TS_RQST_FW_RETRY_TIMES 5
#pragma pack(1)
struct goodix_cfg_pkg_reg {
u16 addr;
u8 reserved1;
u8 reserved2;
};
struct goodix_cfg_pkg_const_info {
u32 pkg_len;
u8 ic_type[TS_IC_TYPE_NAME_MAX_LEN];
u8 cfg_type;
u8 sensor_id;
u8 hw_pid[TS_CFG_BLOCK_PID_LEN];
u8 hw_vid[TS_CFG_BLOCK_VID_LEN];
u8 fw_mask[TS_CFG_BLOCK_FW_MASK_LEN];
u8 fw_patch[TS_CFG_BLOCK_FW_PATCH_LEN];
u16 x_res_offset;
u16 y_res_offset;
u16 trigger_offset;
};
struct goodix_cfg_pkg_reg_info {
struct goodix_cfg_pkg_reg cfg_send_flag;
struct goodix_cfg_pkg_reg version_base;
struct goodix_cfg_pkg_reg pid;
struct goodix_cfg_pkg_reg vid;
struct goodix_cfg_pkg_reg sensor_id;
struct goodix_cfg_pkg_reg fw_mask;
struct goodix_cfg_pkg_reg fw_status;
struct goodix_cfg_pkg_reg cfg_addr;
struct goodix_cfg_pkg_reg esd;
struct goodix_cfg_pkg_reg command;
struct goodix_cfg_pkg_reg coor;
struct goodix_cfg_pkg_reg gesture;
struct goodix_cfg_pkg_reg fw_request;
struct goodix_cfg_pkg_reg proximity;
u8 reserved[TS_CFG_BLOCK_RESERVED_LEN];
};
struct goodix_cfg_bin_head {
u32 bin_len;
u8 checksum;
u8 bin_version[TS_BIN_VERSION_LEN];
u8 pkg_num;
};
#pragma pack()
struct goodix_cfg_package {
struct goodix_cfg_pkg_const_info cnst_info;
struct goodix_cfg_pkg_reg_info reg_info;
const u8 *cfg;
u32 pkg_len;
};
struct goodix_cfg_bin {
unsigned char *bin_data;
unsigned int bin_data_len;
struct goodix_cfg_bin_head head;
struct goodix_cfg_package *cfg_pkgs;
};
int goodix_cfg_bin_proc(void *data);
int goodix_parse_cfg_bin(struct goodix_cfg_bin *cfg_bin);
int goodix_get_reg_and_cfg(struct goodix_ts_device *ts_dev,
struct goodix_cfg_bin *cfg_bin);
int goodix_read_cfg_bin(struct device *dev, struct goodix_cfg_bin *cfg_bin);
int goodix_read_cfg_bin_from_dts(struct device_node *node,
struct goodix_cfg_bin *cfg_bin);
void goodix_cfg_pkg_leToCpu(struct goodix_cfg_package *pkg);
int goodix_start_cfg_bin(struct goodix_ts_core *ts_core);
extern int goodix_i2c_write(struct goodix_ts_device *dev, unsigned int reg,
unsigned char *data, unsigned int len);
extern int goodix_i2c_read(struct goodix_ts_device *dev, unsigned int reg,
unsigned char *data, unsigned int len);
extern struct goodix_module goodix_modules;
#endif
@@ -0,0 +1,10 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef _GOODIX_FIRMWARE_FW_H_
#define _GOODIX_FIRMWARE_FW_H_
u8 goodix_default_fw[] = { 0x00};
#endif
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,752 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef _GOODIX_TS_CORE_H_
#define _GOODIX_TS_CORE_H_
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/firmware.h>
#include <linux/slab.h>
#include <asm/unaligned.h>
#include <linux/vmalloc.h>
#include <linux/kthread.h>
#include <linux/version.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/completion.h>
#include <linux/debugfs.h>
#include <linux/of_irq.h>
#ifdef CONFIG_OF
#include <linux/of_gpio.h>
#include <linux/regulator/consumer.h>
#endif
#ifdef CONFIG_HAS_EARLYSUSPEND
#include <linux/earlysuspend.h>
#endif
#ifdef CONFIG_FB
#include <linux/notifier.h>
#include <linux/fb.h>
#endif
/* macros definition */
/* mtk touch factory test: "mtk-tpd2" */
#define GOODIX_CORE_DRIVER_NAME "gt9886"
#define GOODIX_INPUT_DEV_NAME "mtk-tpd2"
#define GOODIX_DRIVER_VERSION "v1.2.0.1"
#define GOODIX_TS_PID_GT9886 "9886"
#define GOODIX_TS_PID_GT9885 "9885"
#define GOODIX_BUS_RETRY_TIMES 3
#define GOODIX_CHIPID_RETRY_TIMES 5
#define GOODIX_MAX_TOUCH 10
#define GOODIX_MAX_PEN 1
#define GOODIX_MAX_KEY 3
#define GOODIX_PEN_MAX_KEY 2
#define GOODIX_CFG_MAX_SIZE 1024
#define GOODIX_ESD_TICK_WRITE_DATA 0xAA
#define GOODIX_PID_MAX_LEN 8
#define GOODIX_VID_MAX_LEN 8
#define GOODIX_DEFAULT_CFG_NAME "goodix_config.cfg"
#define IC_TYPE_NONE 0
#define IC_TYPE_NORMANDY 1
#define IC_TYPE_NANJING 2
#define PINCTRL_STATE_I2C_DEFAULT "ts_i2c_mode"
#define PINCTRL_STATE_INT_ACTIVE "ts_int_active"
#define PINCTRL_STATE_RST_ACTIVE "ts_reset_active"
#define PINCTRL_STATE_INT_SUSPEND "ts_int_suspend"
#define PINCTRL_STATE_RST_SUSPEND "ts_reset_suspend"
#define GOODIX_TOUCH_EVENT 0x80
#define GOODIX_REQUEST_EVENT 0x40
#define GOODIX_GESTURE_EVENT 0x20
#define GOODIX_HOTKNOT_EVENT 0x10
/* For MTK Internal Touch Begin */
/* touch-filter Begin */
struct tpd_filter_t {
int enable; /*0: disable, 1: enable*/
int pixel_density; /*XXX pixel/cm*/
int W_W[3][4];/*filter custom setting prameters*/
unsigned int VECLOCITY_THRESHOLD[3];/*filter speed custom settings*/
};
#define TOUCH_IOC_MAGIC 'A'
#define TPD_GET_FILTER_PARA _IOWR(TOUCH_IOC_MAGIC, 2, struct tpd_filter_t)
#ifdef CONFIG_COMPAT
#define COMPAT_TPD_GET_FILTER_PARA _IOWR(TOUCH_IOC_MAGIC, \
2, struct tpd_filter_t)
#endif
/*
* struct goodix_module - external modules container
* @head: external modules list
* @initilized: whether this struct is initialized
* @mutex: mutex lock
* @count: current number of registered external module
* @wq: workqueue to do register work
* @core_exit: if goodix touch core exit, then no
* registration is allowed.
* @core_data: core_data pointer
*/
struct goodix_module {
struct list_head head;
bool initilized;
struct mutex mutex;
unsigned int count;
struct workqueue_struct *wq;
bool core_exit;
struct completion core_comp;
struct goodix_ts_core *core_data;
};
/*
* struct goodix_ts_board_data - board data
* @avdd_name: name of analoy regulator
* @reset_gpio: reset gpio number
* @irq_gpio: interrupt gpio number
* @irq_flag: irq trigger type
* @power_on_delay_us: power on delay time (us)
* @power_off_delay_us: power off delay time (us)
* @swap_axis: whether swaw x y axis
* @panel_max_id: max supported fingers
* @panel_max_x/y/w/p: resolution and size
* @panel_max_key: max supported keys
* @pannel_key_map: key map
* @fw_name: name of the firmware image
*/
struct goodix_ts_board_data {
const char *avdd_name;
unsigned int reset_gpio;
unsigned int irq_gpio;
int irq;
unsigned int irq_flags;
unsigned int power_on_delay_us;
unsigned int power_off_delay_us;
/* For MTK Internal Touch Begin */
unsigned int input_max_x;
unsigned int input_max_y;
/* For MTK Internal Touch End */
unsigned int swap_axis;
unsigned int panel_max_id; /*max touch id*/
unsigned int panel_max_x;
unsigned int panel_max_y;
unsigned int panel_max_w; /*major and minor*/
unsigned int panel_max_p; /*pressure*/
unsigned int panel_max_key;
unsigned int panel_key_map[GOODIX_MAX_KEY + GOODIX_PEN_MAX_KEY];
/*add by lishuai*/
unsigned int x2x;
unsigned int y2y;
bool pen_enable;
unsigned int tp_key_num;
/*add end*/
const char *fw_name;
const char *cfg_bin_name;
bool esd_default_on;
struct device *pinctrl_dev;
struct tpd_filter_t tpd_filter;
};
/*
* struct goodix_ts_config - chip config data
* @initialized: whether initialized
* @name: name of this config
* @lock: mutex
* @reg_base: register base of config data
* @length: bytes of the config
* @delay: delay time after sending config
* @data: config data buffer
*/
struct goodix_ts_config {
bool initialized;
char name[24];
struct mutex lock;
unsigned int reg_base;
unsigned int length;
unsigned int delay; /*ms*/
unsigned char data[GOODIX_CFG_MAX_SIZE];
};
/*
* struct goodix_ts_cmd - command package
* @initialized: whether initialized
* @cmd_reg: command register
* @length: command length in bytes
* @cmds: command data
*/
#pragma pack(4)
struct goodix_ts_cmd {
u32 initialized;
u32 cmd_reg;
u32 length;
u8 cmds[3];
};
#pragma pack()
/* interrupt event type */
enum ts_event_type {
EVENT_INVALID,
EVENT_TOUCH,
EVENT_REQUEST,
};
/* requset event type */
enum ts_request_type {
REQUEST_INVALID,
REQUEST_CONFIG,
REQUEST_BAKREF,
REQUEST_RESET,
REQUEST_MAINCLK,
};
/* notifier event */
enum ts_notify_event {
NOTIFY_FWUPDATE_START,
NOTIFY_FWUPDATE_END,
NOTIFY_SUSPEND,
NOTIFY_RESUME,
NOTIFY_ESD_OFF,
NOTIFY_ESD_ON,
};
/* coordinate package */
struct goodix_ts_coords {
int id;
unsigned int x, y, w, p;
};
/* touch event data */
struct goodix_touch_data {
/* finger */
int touch_num;
struct goodix_ts_coords coords[GOODIX_MAX_TOUCH];
/* key */
u8 key_value;
bool have_key;
/*pen*/
struct goodix_ts_coords pen_coords[GOODIX_MAX_PEN];
bool pen_down;
};
/* request event data */
struct goodix_request_data {
enum ts_request_type request_type;
};
/*
* struct goodix_ts_event - touch event struct
* @event_type: touch event type, touch data or
* request event
* @event_data: event data
*/
struct goodix_ts_event {
enum ts_event_type event_type;
union {
struct goodix_touch_data touch_data;
struct goodix_request_data request_data;
} event_data;
};
/*
* struct goodix_ts_version - firmware version
* @valid: whether these information is valid
* @pid: product id string
* @vid: firmware version code
* @cid: customer id code
* @sensor_id: sendor id
*/
struct goodix_ts_version {
bool valid;
char pid[GOODIX_PID_MAX_LEN];
char vid[GOODIX_VID_MAX_LEN];
u8 cid;
u8 sensor_id;
};
struct goodix_ts_regs {
u16 cfg_send_flag;
u16 version_base;
u8 version_len;
u16 pid;
u8 pid_len;
u16 vid;
u8 vid_len;
u16 sensor_id;
u8 sensor_id_mask;
u16 fw_mask;
u16 fw_status;
u16 cfg_addr;
u16 esd;
u16 command;
u16 coor;
u16 gesture;
u16 fw_request;
u16 proximity;
};
/*
* struct goodix_ts_device - ts device data
* @name: device name
* @version: reserved
* @bus_type: i2c or spi
* @board_data: board data obtained from dts
* @normal_cfg: normal config data
* @highsense_cfg: high sense config data
* @hw_ops: hardware operations
* @chip_version: firmware version information
* @sleep_cmd: sleep commang
* @gesture_cmd: gesture command
* @dev: device pointer,may be a i2c or spi device
* @of_node: device node
*/
struct goodix_ts_device {
char *name;
int version;
int bus_type;
u8 ic_type;
struct goodix_ts_regs reg;
int doze_mode_set_count;
struct mutex doze_mode_lock;
struct goodix_ts_board_data *board_data;
struct goodix_ts_config *normal_cfg;
struct goodix_ts_config *highsense_cfg;
const struct goodix_ts_hw_ops *hw_ops;
struct goodix_ts_version chip_version;
struct device *dev;
};
/*
* struct goodix_ts_hw_ops - hardware opeartions
* @init: hardware initialization
* @reset: hardware reset
* @read: read data from touch device
* @write: write data to touch device
* @send_cmd: send command to touch device
* @send_config: send configuration data
* @read_version: read firmware version
* @event_handler: touch event handler
* @suspend: put touch device into low power mode
* @resume: put touch device into working mode
*/
struct goodix_ts_hw_ops {
int (*init)(struct goodix_ts_device *dev);
int (*dev_confirm)(struct goodix_ts_device *ts_dev);
int (*reset)(struct goodix_ts_device *dev);
int (*read)(struct goodix_ts_device *dev, unsigned int addr,
unsigned char *data, unsigned int len);
int (*write)(struct goodix_ts_device *dev, unsigned int addr,
unsigned char *data, unsigned int len);
int (*read_trans)(struct goodix_ts_device *dev, unsigned int addr,
unsigned char *data, unsigned int len);
int (*write_trans)(struct goodix_ts_device *dev, unsigned int addr,
unsigned char *data, unsigned int len);
int (*send_cmd)(struct goodix_ts_device *dev,
struct goodix_ts_cmd *cmd);
int (*send_config)(struct goodix_ts_device *dev,
struct goodix_ts_config *config);
int (*read_config)(struct goodix_ts_device *dev,
u8 *config_data, u32 config_len);
int (*read_version)(struct goodix_ts_device *dev,
struct goodix_ts_version *version);
int (*event_handler)(struct goodix_ts_device *dev,
struct goodix_ts_event *ts_event);
int (*check_hw)(struct goodix_ts_device *dev);
int (*suspend)(struct goodix_ts_device *dev);
int (*resume)(struct goodix_ts_device *dev);
int (*read_pid)(struct goodix_ts_device *dev,
struct goodix_ts_version *version);
};
/*
* struct goodix_ts_esd - esd protector structure
* @esd_work: esd delayed work
* @esd_on: true - turn on esd protection, false - turn
* off esd protection
* @esd_mutex: protect @esd_on flag
*/
struct goodix_ts_esd {
struct delayed_work esd_work;
struct mutex esd_mutex;
struct notifier_block esd_notifier;
struct goodix_ts_core *ts_core;
bool esd_on;
};
/*
* struct godix_ts_core - core layer data struct
* @pdev: core layer platform device
* @ts_dev: hardware layer touch device
* @input_dev: input device
* @avdd: analog regulator
* @pinctrl: pinctrl handler
* @pin_sta_active: active/normal pin state
* @pin_sta_suspend: suspend/sleep pin state
* @ts_event: touch event data struct
* @power_on: power on/off flag
* @irq: irq number
* @irq_enabled: irq enabled/disabled flag
* @suspended: suspend/resume flag
* @ts_notifier: generic notifier
* @ts_esd: esd protector structure
* @fb_notifier: framebuffer notifier
* @early_suspend: early suspend
*/
struct goodix_ts_core {
struct platform_device *pdev;
struct goodix_ts_device *ts_dev;
struct input_dev *input_dev;
struct regulator *avdd;
#ifdef CONFIG_PINCTRL
struct pinctrl *pinctrl;
struct pinctrl_state *pin_i2c_mode_default;
struct pinctrl_state *pin_int_sta_active;
struct pinctrl_state *pin_int_sta_suspend;
struct pinctrl_state *pin_rst_sta_active;
struct pinctrl_state *pin_rst_sta_suspend;
#endif
struct goodix_ts_event ts_event;
int power_on;
int irq;
size_t irq_trig_cnt;
atomic_t irq_enabled;
atomic_t suspended;
bool cfg_group_parsed;
struct notifier_block ts_notifier;
struct goodix_ts_esd ts_esd;
#ifdef CONFIG_FB
struct notifier_block fb_notifier;
#elif defined(CONFIG_HAS_EARLYSUSPEND)
struct early_suspend early_suspend;
#endif
};
/* external module structures */
enum goodix_ext_priority {
EXTMOD_PRIO_RESERVED = 0,
EXTMOD_PRIO_FWUPDATE,
EXTMOD_PRIO_GESTURE,
EXTMOD_PRIO_HOTKNOT,
EXTMOD_PRIO_DBGTOOL,
EXTMOD_PRIO_DEFAULT,
};
struct goodix_ext_module;
/* external module's operations callback */
struct goodix_ext_module_funcs {
int (*init)(struct goodix_ts_core *core_data,
struct goodix_ext_module *module);
int (*exit)(struct goodix_ts_core *core_data,
struct goodix_ext_module *module);
int (*before_reset)(struct goodix_ts_core *core_data,
struct goodix_ext_module *module);
int (*after_reset)(struct goodix_ts_core *core_data,
struct goodix_ext_module *module);
int (*before_suspend)(struct goodix_ts_core *core_data,
struct goodix_ext_module *module);
int (*after_suspend)(struct goodix_ts_core *core_data,
struct goodix_ext_module *module);
int (*before_resume)(struct goodix_ts_core *core_data,
struct goodix_ext_module *module);
int (*after_resume)(struct goodix_ts_core *core_data,
struct goodix_ext_module *module);
int (*irq_event)(struct goodix_ts_core *core_data,
struct goodix_ext_module *module);
};
/*
* struct goodix_ext_module - external module struct
* @list: list used to link into modules manager
* @name: name of external module
* @priority: module priority vlaue, zero is invalid
* @funcs: operations callback
* @priv_data: private data region
* @kobj: kobject
* @work: used to queue one work to do registration
*/
struct goodix_ext_module {
struct list_head list;
char *name;
enum goodix_ext_priority priority;
const struct goodix_ext_module_funcs *funcs;
void *priv_data;
struct kobject kobj;
struct work_struct work;
};
/*
* struct goodix_ext_attribute - exteranl attribute struct
* @attr: attribute
* @show: show interface of external attribute
* @store: store interface of external attribute
*/
struct goodix_ext_attribute {
struct attribute attr;
ssize_t (*show)(struct goodix_ext_module *, char *);
ssize_t (*store)(struct goodix_ext_module *, const char *, size_t);
};
/* external attrs helper macro */
#define __EXTMOD_ATTR(_name, _mode, _show, _store) { \
.attr = {.name = __stringify(_name), .mode = _mode }, \
.show = _show, \
.store = _store, \
}
/* external attrs helper macro, used to define external attrs */
#define DEFINE_EXTMOD_ATTR(_name, _mode, _show, _store) \
static struct goodix_ext_attribute ext_attr_##_name = \
__EXTMOD_ATTR(_name, _mode, _show, _store);
/*
* get board data pointer
*/
static inline struct goodix_ts_board_data *board_data(
struct goodix_ts_core *core)
{
return core->ts_dev->board_data;
}
/*
* get touch device pointer
*/
static inline struct goodix_ts_device *ts_device(
struct goodix_ts_core *core)
{
return core->ts_dev;
}
/*
* get touch hardware operations pointer
*/
static inline const struct goodix_ts_hw_ops *ts_hw_ops(
struct goodix_ts_core *core)
{
return core->ts_dev->hw_ops;
}
/*
* checksum helper functions
* checksum can be u8/le16/be16/le32/be32 format
* NOTE: the caller should be responsible for the
* legality of @data and @size parameters, so be
* careful when call these functions.
*/
static inline u8 checksum_u8(u8 *data, u32 size)
{
u8 checksum = 0;
u32 i;
for (i = 0; i < size; i++)
checksum += data[i];
return checksum;
}
static inline u16 checksum_le16(u8 *data, u32 size)
{
u16 checksum = 0;
u32 i;
for (i = 0; i < size; i += 2)
checksum += le16_to_cpup((__le16 *)(data + i));
return checksum;
}
static inline u16 checksum_be16(u8 *data, u32 size)
{
u16 checksum = 0;
u32 i;
for (i = 0; i < size; i += 2)
checksum += be16_to_cpup((__be16 *)(data + i));
return checksum;
}
static inline u32 checksum_le32(u8 *data, u32 size)
{
u32 checksum = 0;
u32 i;
for (i = 0; i < size; i += 4)
checksum += le32_to_cpup((__le32 *)(data + i));
return checksum;
}
static inline u32 checksum_be32(u8 *data, u32 size)
{
u32 checksum = 0;
u32 i;
for (i = 0; i < size; i += 4)
checksum += be32_to_cpup((__be32 *)(data + i));
return checksum;
}
/**
* define event action
* EVT_xxx macros are used in opeartions callback
* defined in @goodix_ext_module_funcs to control
* the behaviors of event such as suspend/resume/irq_event.
* generally there are two types of behaviors:
* 1. you want the flow of this event be canceled,
* in this condition, you should return EVT_CANCEL_XXX in
* the operations callback.
* e.g. the firmware update module is updating
* the firmware, you want to cancel suspend flow,
* so you need to return EVT_CANCEL_SUSPEND in
* suspend callback function.
* 2. you want the flow of this event continue, in
* this condition, you should return EVT_HANDLED in
* the callback function.
*/
#define EVT_HANDLED 0
#define EVT_CONTINUE 0
#define EVT_CANCEL 1
#define EVT_CANCEL_IRQEVT 1
#define EVT_CANCEL_SUSPEND 1
#define EVT_CANCEL_RESUME 1
#define EVT_CANCEL_RESET 1
/*
* errno define
* Note:
* 1. bus read/write functions defined in hardware
* layer code(e.g. goodix_xxx_i2c.c) *must* return
* -EBUS if failed to transfer data on bus.
*/
#define EBUS 1000
#define ETIMEOUT 1001
#define ECHKSUM 1002
#define EMEMCMP 1003
#if 0
#define CONFIG_GOODIX_DEBUG
#endif
/* log macro */
#define ts_info(fmt, arg...)\
pr_info("[GT9886-INF][%s:%d] "fmt"\n", __func__, __LINE__, ##arg)
#define ts_err(fmt, arg...)\
pr_info("[GT9886-ERR][%s:%d] "fmt"\n", __func__, __LINE__, ##arg)
#define boot_log(fmt, arg...) g_info(fmt, ##arg)
#ifdef CONFIG_GOODIX_DEBUG
#define ts_debug(fmt, arg...)\
pr_info("[GT9886-DBG][%s:%d] "fmt"\n", __func__, __LINE__, ##arg)
#else
#define ts_debug(fmt, arg...) do {} while (0)
#endif
/**
* goodix_register_ext_module - interface for external module
* to register into touch core modules structure
*
* @module: pointer to external module to be register
* return: 0 ok, <0 failed
*/
int goodix_register_ext_module(struct goodix_ext_module *module);
/**
* goodix_unregister_ext_module - interface for external module
* to unregister external modules
*
* @module: pointer to external module
* return: 0 ok, <0 failed
*/
int goodix_unregister_ext_module(struct goodix_ext_module *module);
/**
* goodix_ts_irq_enable - Enable/Disable a irq
* @core_data: pointer to touch core data
* enable: enable or disable irq
* return: 0 ok, <0 failed
*/
int goodix_ts_irq_enable(struct goodix_ts_core *core_data, bool enable);
struct kobj_type *goodix_get_default_ktype(void);
/**
* fb_notifier_call_chain - notify clients of fb_events
* see enum ts_notify_event in goodix_ts_core.h
*/
int goodix_ts_blocking_notify(enum ts_notify_event evt, void *v);
/**
* goodix_ts_power_on - Turn on power to the touch device
* @core_data: pointer to touch core data
* return: 0 ok, <0 failed
*/
int goodix_ts_power_on(struct goodix_ts_core *core_data);
/**
* goodix_ts_power_off - Turn off power to the touch device
* @core_data: pointer to touch core data
* return: 0 ok, <0 failed
*/
int goodix_ts_power_off(struct goodix_ts_core *core_data);
int goodix_ts_hw_init(struct goodix_ts_core *core_data);
int goodix_ts_input_dev_config(struct goodix_ts_core *core_data);
int goodix_ts_irq_setup(struct goodix_ts_core *core_data);
int goodix_ts_sysfs_init(struct goodix_ts_core *core_data);
int goodix_ts_esd_init(struct goodix_ts_core *core);
int goodix_ts_register_notifier(struct notifier_block *nb);
int goodix_generic_noti_callback(struct notifier_block *self,
unsigned long action, void *data);
int goodix_ts_fb_notifier_callback(struct notifier_block *self,
unsigned long event, void *data);
int goodix_ts_irq_enable(struct goodix_ts_core *core_data,
bool enable);
extern void goodix_msg_printf(const char *fmt, ...);
extern int i2c_touch_resume(void);
extern int i2c_touch_suspend(void);
extern int goodix_start_cfg_bin(struct goodix_ts_core *ts_core);
int gt9886_touch_filter_register(void);
int goodix_ts_core_init(void);
#ifdef CONFIG_TRUSTONIC_TRUSTED_UI
extern atomic_t gt9886_tui_flag;
extern struct goodix_ts_core *resume_core_data;
#endif
#endif
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,619 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/atomic.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/ioctl.h>
#include <linux/wait.h>
#include "goodix_ts_core.h"
#define GOODIX_TOOLS_NAME "gtp_tools"
#define GOODIX_TS_IOC_MAGIC 'G'
#define NEGLECT_SIZE_MASK (~(_IOC_SIZEMASK << _IOC_SIZESHIFT))
#define GTP_IRQ_ENABLE _IO(GOODIX_TS_IOC_MAGIC, 0)
#define GTP_DEV_RESET _IO(GOODIX_TS_IOC_MAGIC, 1)
#define GTP_SEND_COMMAND (_IOW(GOODIX_TS_IOC_MAGIC, 2, u8) & NEGLECT_SIZE_MASK)
#define GTP_SEND_CONFIG (_IOW(GOODIX_TS_IOC_MAGIC, 3, u8) & NEGLECT_SIZE_MASK)
#define GTP_ASYNC_READ (_IOR(GOODIX_TS_IOC_MAGIC, 4, u8) & NEGLECT_SIZE_MASK)
#define GTP_SYNC_READ (_IOR(GOODIX_TS_IOC_MAGIC, 5, u8) & NEGLECT_SIZE_MASK)
#define GTP_ASYNC_WRITE (_IOW(GOODIX_TS_IOC_MAGIC, 6, u8) & NEGLECT_SIZE_MASK)
#define GTP_READ_CONFIG (_IOW(GOODIX_TS_IOC_MAGIC, 7, u8) & NEGLECT_SIZE_MASK)
#define GTP_ESD_ENABLE _IO(GOODIX_TS_IOC_MAGIC, 8)
#define GTP_DRV_VERSION (_IOR(GOODIX_TS_IOC_MAGIC, 9, u8) & NEGLECT_SIZE_MASK)
#define GOODIX_TS_IOC_MAXNR 10
#define IRQ_FALG (0x01 << 2)
#define I2C_MSG_HEAD_LEN 20
#define TS_REG_COORDS_BASE 0x4100
/*
* struct goodix_tools_data - goodix tools data message used in sync read
* @data: The buffer into which data is written
* @reg_addr: Slave device register start address to start read data
* @length: Number of data bytes in @data being read from slave device
* @filled: When buffer @data be filled will set this flag with 1, outhrwise 0
* @list_head:Eonnet every goodix_tools_data struct into a list
*/
struct goodix_tools_data {
u32 reg_addr;
u32 length;
u8 *data;
bool filled;
struct list_head list;
};
/*
* struct goodix_tools_dev - goodix tools device struct
* @ts_core: The core data struct of ts driver
* @ops_mode: represent device work mode
* @rawdiffcmd: Set slave device into rawdata mode
* @normalcmd: Set slave device into normal mode
* @wq: Wait queue struct use in synchronous data read
* @mutex: Protect goodix_tools_dev
*/
struct goodix_tools_dev {
struct goodix_ts_core *ts_core;
struct list_head head;
unsigned int ops_mode;
struct goodix_ts_cmd rawdiffcmd, normalcmd;
wait_queue_head_t wq;
struct mutex mutex;
atomic_t t_count;
struct goodix_ext_module module;
} *goodix_tools_dev;
/* read data from i2c asynchronous,
* success return bytes read, else return <= 0
*/
static int async_read(struct goodix_tools_dev *dev, void __user *arg)
{
u8 *databuf = NULL;
int ret = 0;
u32 reg_addr, length;
u8 i2c_msg_head[I2C_MSG_HEAD_LEN];
struct goodix_ts_device *ts_dev = dev->ts_core->ts_dev;
const struct goodix_ts_hw_ops *hw_ops = ts_dev->hw_ops;
ret = copy_from_user(&i2c_msg_head, arg, I2C_MSG_HEAD_LEN);
if (ret) {
ret = -EFAULT;
goto err_out;
}
reg_addr = i2c_msg_head[0] + (i2c_msg_head[1] << 8)
+ (i2c_msg_head[2] << 16) + (i2c_msg_head[3] << 24);
length = i2c_msg_head[4] + (i2c_msg_head[5] << 8)
+ (i2c_msg_head[6] << 16) + (i2c_msg_head[7] << 24);
if (length > GOODIX_CFG_MAX_SIZE)
return -EMSGSIZE;
databuf = kzalloc(length, GFP_KERNEL);
if (!databuf) {
ts_err("Alloc memory failed");
return -ENOMEM;
}
if (!hw_ops->read_trans(ts_dev, reg_addr, databuf, length)) {
if (copy_to_user((u8 *)arg + I2C_MSG_HEAD_LEN,
databuf, length)) {
ret = -EFAULT;
ts_err("Copy_to_user failed");
} else {
ret = length;
}
} else {
ret = -EBUSY;
ts_err("Read i2c failed");
}
err_out:
kfree(databuf);
return ret;
}
/* if success return config data length */
static int read_config_data(struct goodix_ts_device *ts_dev, void __user *arg)
{
int ret = 0;
u32 reg_addr, length;
u8 i2c_msg_head[I2C_MSG_HEAD_LEN];
u8 *tmp_buf;
ret = copy_from_user(&i2c_msg_head, arg, I2C_MSG_HEAD_LEN);
if (ret) {
ts_err("Copy data from user failed");
return -EFAULT;
}
reg_addr = i2c_msg_head[0] + (i2c_msg_head[1] << 8)
+ (i2c_msg_head[2] << 16) + (i2c_msg_head[3] << 24);
length = i2c_msg_head[4] + (i2c_msg_head[5] << 8)
+ (i2c_msg_head[6] << 16) + (i2c_msg_head[7] << 24);
ts_info("read config,reg_addr=0x%x, length=%d", reg_addr, length);
if (length > GOODIX_CFG_MAX_SIZE)
return -EMSGSIZE;
tmp_buf = kzalloc(length, GFP_KERNEL);
if (!tmp_buf) {
ts_err("failed alloc memory");
return -ENOMEM;
}
/* if reg_addr == 0, read config data with specific flow */
if (!reg_addr) {
if (ts_dev->hw_ops->read_config)
ret = ts_dev->hw_ops->read_config(ts_dev, tmp_buf, 0);
else
ret = -EINVAL;
} else {
ret = ts_dev->hw_ops->read_trans(ts_dev,
reg_addr, tmp_buf, length);
if (!ret)
ret = length;
}
if (ret <= 0)
goto err_out;
if (copy_to_user((u8 *)arg + I2C_MSG_HEAD_LEN, tmp_buf, ret)) {
ret = -EFAULT;
ts_err("Copy_to_user failed");
}
err_out:
kfree(tmp_buf);
return ret;
}
/* read data from i2c synchronous,
* success return bytes read, else return <= 0
*/
static int sync_read(struct goodix_tools_dev *dev, void __user *arg)
{
int ret = 0;
u8 i2c_msg_head[I2C_MSG_HEAD_LEN];
struct goodix_tools_data tools_data;
ret = copy_from_user(&i2c_msg_head, arg, I2C_MSG_HEAD_LEN);
if (ret) {
ts_err("Copy data from user failed");
return -EFAULT;
}
tools_data.reg_addr = i2c_msg_head[0] + (i2c_msg_head[1] << 8)
+ (i2c_msg_head[2] << 16) + (i2c_msg_head[3] << 24);
tools_data.length = i2c_msg_head[4] + (i2c_msg_head[5] << 8)
+ (i2c_msg_head[6] << 16) + (i2c_msg_head[7] << 24);
tools_data.filled = 0;
if (tools_data.length > GOODIX_CFG_MAX_SIZE)
return -EMSGSIZE;
tools_data.data = kzalloc(tools_data.length, GFP_KERNEL);
if (!tools_data.data) {
ts_err("Alloc memory failed");
return -ENOMEM;
}
mutex_lock(&dev->mutex);
list_add_tail(&tools_data.list, &dev->head);
mutex_unlock(&dev->mutex);
/* wait queue will timeout after 1 seconds */
if (!wait_event_interruptible_timeout(dev->wq,
tools_data.filled == 1, HZ * 3)) {
ret = -EAGAIN;
ts_err("Wait queue timeout");
goto err_out;
}
mutex_lock(&dev->mutex);
list_del(&tools_data.list);
mutex_unlock(&dev->mutex);
if (tools_data.filled == 1) {
if (copy_to_user((u8 *)arg + I2C_MSG_HEAD_LEN, tools_data.data,
tools_data.length)) {
ret = -EFAULT;
ts_err("Copy_to_user failed");
} else {
ret = tools_data.length;
}
} else {
ret = -EAGAIN;
ts_err("Wait queue timeout");
}
err_out:
kfree(tools_data.data);
return ret;
}
/* write data to i2c asynchronous,
* success return bytes write, else return <= 0
*/
static int async_write(struct goodix_tools_dev *dev, void __user *arg)
{
u8 *databuf;
int ret = 0;
u32 reg_addr, length;
u8 i2c_msg_head[I2C_MSG_HEAD_LEN];
struct goodix_ts_device *ts_dev = dev->ts_core->ts_dev;
const struct goodix_ts_hw_ops *hw_ops = ts_dev->hw_ops;
ret = copy_from_user(&i2c_msg_head, arg, I2C_MSG_HEAD_LEN);
if (ret) {
ts_err("Copy data from user failed");
return -EFAULT;
}
reg_addr = i2c_msg_head[0] + (i2c_msg_head[1] << 8)
+ (i2c_msg_head[2] << 16) + (i2c_msg_head[3] << 24);
length = i2c_msg_head[4] + (i2c_msg_head[5] << 8)
+ (i2c_msg_head[6] << 16) + (i2c_msg_head[7] << 24);
if (length > GOODIX_CFG_MAX_SIZE)
return -EMSGSIZE;
databuf = kzalloc(length, GFP_KERNEL);
if (!databuf) {
ts_err("Alloc memory failed");
return -ENOMEM;
}
ret = copy_from_user(databuf, (u8 *)arg + I2C_MSG_HEAD_LEN, length);
if (ret) {
ret = -EFAULT;
ts_err("Copy data from user failed");
goto err_out;
}
if (hw_ops->write_trans(ts_dev, reg_addr, databuf, length)) {
ret = -EBUSY;
ts_err("Write data to device failed");
} else {
ret = length;
}
err_out:
kfree(databuf);
return ret;
}
static int init_cfg_data(struct goodix_ts_config *cfg, void __user *arg)
{
int ret = 0;
u32 reg_addr, length;
u8 i2c_msg_head[I2C_MSG_HEAD_LEN];
cfg->initialized = 0;
mutex_init(&cfg->lock);
ret = copy_from_user(&i2c_msg_head, arg, I2C_MSG_HEAD_LEN);
if (ret) {
ts_err("Copy data from user failed");
return -EFAULT;
}
reg_addr = i2c_msg_head[0] + (i2c_msg_head[1] << 8)
+ (i2c_msg_head[2] << 16) + (i2c_msg_head[3] << 24);
length = i2c_msg_head[4] + (i2c_msg_head[5] << 8)
+ (i2c_msg_head[6] << 16) + (i2c_msg_head[7] << 24);
if (length > GOODIX_CFG_MAX_SIZE)
return -EMSGSIZE;
ret = copy_from_user(cfg->data, (u8 *)arg + I2C_MSG_HEAD_LEN, length);
if (ret) {
ret = -EFAULT;
ts_err("Copy data from user failed");
goto err_out;
}
cfg->reg_base = reg_addr;
cfg->length = length;
strlcpy(cfg->name, "tools-send-cfg", sizeof(cfg->name));
cfg->delay = 50;
cfg->initialized = true;
return 0;
err_out:
return ret;
}
/**
* goodix_tools_ioctl - ioctl implementation
*
* @filp: Pointer to file opened
* @cmd: Ioctl opertion command
* @arg: Command data
* Returns >=0 - succeed, else failed
*/
static long goodix_tools_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
int ret = 0;
struct goodix_tools_dev *dev = filp->private_data;
struct goodix_ts_device *ts_dev;
const struct goodix_ts_hw_ops *hw_ops;
struct goodix_ts_cmd temp_cmd;
struct goodix_ts_config *temp_cfg = NULL;
if (dev->ts_core == NULL) {
ts_err("Tools module not register");
return -EINVAL;
}
ts_dev = dev->ts_core->ts_dev;
hw_ops = ts_dev->hw_ops;
if (_IOC_TYPE(cmd) != GOODIX_TS_IOC_MAGIC) {
ts_err("Bad magic num:%c", _IOC_TYPE(cmd));
return -ENOTTY;
}
if (_IOC_NR(cmd) > GOODIX_TS_IOC_MAXNR) {
ts_err("Bad cmd num:%d > %d",
_IOC_NR(cmd), GOODIX_TS_IOC_MAXNR);
return -ENOTTY;
}
switch (cmd & NEGLECT_SIZE_MASK) {
case GTP_IRQ_ENABLE:
if (arg == 1) {
goodix_ts_irq_enable(dev->ts_core, true);
mutex_lock(&dev->mutex);
dev->ops_mode |= IRQ_FALG;
mutex_unlock(&dev->mutex);
ts_info("IRQ enabled");
} else if (arg == 0) {
goodix_ts_irq_enable(dev->ts_core, false);
mutex_lock(&dev->mutex);
dev->ops_mode &= ~IRQ_FALG;
mutex_unlock(&dev->mutex);
ts_info("IRQ disabled");
} else {
ts_info("Irq aready set with, arg = %ld", arg);
}
ret = 0;
break;
case GTP_ESD_ENABLE:
if (arg == 0)
goodix_ts_blocking_notify(NOTIFY_ESD_OFF, NULL);
else
goodix_ts_blocking_notify(NOTIFY_ESD_ON, NULL);
break;
case GTP_DEV_RESET:
hw_ops->reset(ts_dev);
break;
case GTP_SEND_COMMAND:
ret = copy_from_user(&temp_cmd, (void __user *)arg,
sizeof(struct goodix_ts_cmd));
if (ret) {
ret = -EINVAL;
goto err_out;
}
ret = hw_ops->send_cmd(ts_dev, &temp_cmd);
if (ret) {
ts_err("Send command failed");
ret = -EAGAIN;
}
break;
case GTP_SEND_CONFIG:
temp_cfg = kzalloc(sizeof(struct goodix_ts_config), GFP_KERNEL);
if (temp_cfg == NULL) {
ts_err("Memory allco err");
ret = -ENOMEM;
goto err_out;
}
ret = init_cfg_data(temp_cfg, (void __user *)arg);
if (!ret && hw_ops->send_config) {
ret = hw_ops->send_config(ts_dev, temp_cfg);
if (ret) {
ts_err("Failed send config");
ret = -EAGAIN;
} else {
ts_info("Send config success");
ret = 0;
}
}
break;
case GTP_READ_CONFIG:
ret = read_config_data(ts_dev, (void __user *)arg);
if (ret > 0)
ts_info("success read config:len=%d", ret);
else
ts_err("failed read config:ret=0x%x", ret);
break;
case GTP_ASYNC_READ:
ret = async_read(dev, (void __user *)arg);
if (ret < 0)
ts_err("Async data read failed");
break;
case GTP_SYNC_READ:
if (filp->f_flags & O_NONBLOCK) {
ts_err("Goodix tools now worked in sync_bus mode");
ret = -EAGAIN;
goto err_out;
}
ret = sync_read(dev, (void __user *)arg);
if (ret < 0)
ts_err("Sync data read failed");
break;
case GTP_ASYNC_WRITE:
ret = async_write(dev, (void __user *)arg);
if (ret < 0)
ts_err("Async data write failed");
break;
case GTP_DRV_VERSION:
ret = copy_to_user((u8 *)arg, GOODIX_DRIVER_VERSION,
sizeof(GOODIX_DRIVER_VERSION));
if (ret)
ts_err("failed copy driver version info to user");
break;
default:
ts_info("Invalid cmd");
ret = -ENOTTY;
break;
}
err_out:
if (!temp_cfg) {
kfree(temp_cfg);
temp_cfg = NULL;
}
return ret;
}
#ifdef CONFIG_COMPAT
static long goodix_tools_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
void __user *arg32 = compat_ptr(arg);
if (!file->f_op || !file->f_op->unlocked_ioctl)
return -ENOTTY;
return file->f_op->unlocked_ioctl(file, cmd, (unsigned long)arg32);
}
#endif
static int goodix_tools_open(struct inode *inode, struct file *filp)
{
int ret = 0;
filp->private_data = goodix_tools_dev;
ts_info("tools open");
/* Only the first time open device need to register module */
ret = goodix_register_ext_module(&goodix_tools_dev->module);
if (ret)
ts_info("failed register to core module");
return ret;
}
static int goodix_tools_release(struct inode *inode, struct file *filp)
{
int ret = 0;
/* when the last close this dev node unregister the module */
ret = goodix_unregister_ext_module(&goodix_tools_dev->module);
return ret;
}
/**
* goodix_tools_module_irq - goodix tools Irq handle
* This functions is excuted when interrupt happended
*
* @core_data: pointer to touch core data
* @module: pointer to goodix_ext_module struct
* return: EVT_CONTINUE let other module handle this irq
*/
static int goodix_tools_module_irq(struct goodix_ts_core *core_data,
struct goodix_ext_module *module)
{
struct goodix_tools_dev *dev = module->priv_data;
struct goodix_ts_device *ts_dev = dev->ts_core->ts_dev;
const struct goodix_ts_hw_ops *hw_ops = ts_dev->hw_ops;
struct goodix_tools_data *tools_data;
int r = 0;
u8 evt_sta = 0;
mutex_lock(&dev->mutex);
if (!list_empty(&dev->head)) {
r = hw_ops->read_trans(ts_dev, ts_dev->reg.coor, &evt_sta, 1);
if (r < 0 || ((evt_sta & GOODIX_TOUCH_EVENT) == 0)) {
ts_err("data not ready:0x%x, read ret =%d", evt_sta, r);
mutex_unlock(&dev->mutex);
return EVT_CONTINUE;
}
list_for_each_entry(tools_data, &dev->head, list) {
if (!hw_ops->read_trans(ts_dev, tools_data->reg_addr,
tools_data->data, tools_data->length)) {
tools_data->filled = 1;
}
}
wake_up(&dev->wq);
}
mutex_unlock(&dev->mutex);
return EVT_CONTINUE;
}
static int goodix_tools_module_init(struct goodix_ts_core *core_data,
struct goodix_ext_module *module)
{
struct goodix_tools_dev *tools_dev = module->priv_data;
if (core_data)
tools_dev->ts_core = core_data;
else
return -ENODEV;
return 0;
}
static const struct file_operations goodix_tools_fops = {
.owner = THIS_MODULE,
.open = goodix_tools_open,
.release = goodix_tools_release,
.unlocked_ioctl = goodix_tools_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = goodix_tools_compat_ioctl,
#endif
};
static struct miscdevice goodix_tools_miscdev = {
.minor = MISC_DYNAMIC_MINOR,
.name = GOODIX_TOOLS_NAME,
.fops = &goodix_tools_fops,
};
static struct goodix_ext_module_funcs goodix_tools_module_funcs = {
.irq_event = goodix_tools_module_irq,
.init = goodix_tools_module_init,
};
/**
* goodix_tools_init - init goodix tools device and register a miscdevice
*
* return: 0 success, else failed
*/
static int __init goodix_tools_init(void)
{
int ret;
goodix_tools_dev = kzalloc(sizeof(struct goodix_tools_dev), GFP_KERNEL);
if (goodix_tools_dev == NULL) {
ts_err("Memory allco err");
return -ENOMEM;
}
INIT_LIST_HEAD(&goodix_tools_dev->head);
goodix_tools_dev->ops_mode = 0;
goodix_tools_dev->ops_mode |= IRQ_FALG;
init_waitqueue_head(&goodix_tools_dev->wq);
mutex_init(&goodix_tools_dev->mutex);
atomic_set(&goodix_tools_dev->t_count, 0);
goodix_tools_dev->module.funcs = &goodix_tools_module_funcs;
goodix_tools_dev->module.name = GOODIX_TOOLS_NAME;
goodix_tools_dev->module.priv_data = goodix_tools_dev;
goodix_tools_dev->module.priority = EXTMOD_PRIO_DBGTOOL;
ret = misc_register(&goodix_tools_miscdev);
if (ret)
ts_err("Debug tools miscdev register failed");
return ret;
}
static void __exit goodix_tools_exit(void)
{
misc_deregister(&goodix_tools_miscdev);
kfree(goodix_tools_dev);
ts_info("Goodix tools miscdev exit");
}
module_init(goodix_tools_init);
module_exit(goodix_tools_exit);
MODULE_DESCRIPTION("Goodix tools Module");
MODULE_AUTHOR("Goodix, Inc.");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,37 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifdef CONFIG_TRUSTONIC_TRUSTED_UI
#include "goodix_ts_core.h"
atomic_t gt9886_tui_flag = ATOMIC_INIT(0);
EXPORT_SYMBOL(gt9886_tui_flag);
int tpd_enter_tui(void)
{
int ret = 0;
ts_info("[%s] enter TUI", __func__);
atomic_set(&gt9886_tui_flag, true);
return ret;
}
int tpd_exit_tui(void)
{
int ret = 0;
ts_info("[%s] exit TUI", __func__);
atomic_set(&gt9886_tui_flag, false);
goodix_ts_irq_enable(resume_core_data, false);
goodix_ts_irq_enable(resume_core_data, true);
return ret;
}
#endif
@@ -0,0 +1,12 @@
# Goodix Touchscreen Makefile
# This Makefile is only for tempory compiling use
# Use xxxproject/Makefile-release when release driver
#
# core layer
ccflags-$(CONFIG_DRM_MEDIATEK) += -I$(srctree)/drivers/gpu/drm/mediatek/
obj-y += goodix_ts_i2c.o
obj-y += goodix_ts_core.o
obj-y += goodix_cfg_bin.o
obj-y += goodix_gtx8_update.o
obj-n += goodix_ts_tui.o
@@ -0,0 +1,658 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include "goodix_cfg_bin.h"
#define CFG_TAG ""
int goodix_start_cfg_bin(struct goodix_ts_core *ts_core)
{
struct task_struct *cfg_bin_thrd;
/* create and run update thread */
cfg_bin_thrd = kthread_run(goodix_cfg_bin_proc,
ts_core, "goodix-parse_cfg_bin");
if (IS_ERR_OR_NULL(cfg_bin_thrd)) {
ts_err("Failed to create update thread:%ld",
PTR_ERR(cfg_bin_thrd));
return -EFAULT;
}
return 0;
}
int goodix_parse_cfg_bin(struct goodix_cfg_bin *cfg_bin)
{
u8 checksum;
int i, r;
u16 offset1, offset2;
if (!cfg_bin->bin_data || cfg_bin->bin_data_len == 0) {
ts_err("NO cfg_bin data, cfg_bin data length:%d",
cfg_bin->bin_data_len);
r = -EINVAL;
goto exit;
}
/* copy cfg_bin head info */
if (cfg_bin->bin_data_len < sizeof(struct goodix_cfg_bin_head)) {
ts_err("Invalid cfg_bin size:%d", cfg_bin->bin_data_len);
r = -EINVAL;
goto exit;
}
memcpy(&cfg_bin->head, cfg_bin->bin_data,
sizeof(struct goodix_cfg_bin_head));
cfg_bin->head.bin_len = le32_to_cpu(cfg_bin->head.bin_len);
/*check length*/
if (cfg_bin->bin_data_len != cfg_bin->head.bin_len) {
ts_err("cfg_bin len error,cfg_bin len:%d\n,"
CFG_TAG "len of firmware:%d\n"
, cfg_bin->head.bin_len
, cfg_bin->bin_data_len);
r = -EINVAL;
goto exit;
}
/*check cfg_bin valid*/
checksum = 0;
for (i = TS_BIN_VERSION_START_INDEX; i < cfg_bin->bin_data_len; i++)
checksum += cfg_bin->bin_data[i];
if (checksum != cfg_bin->head.checksum) {
ts_err("cfg_bin checksum ERROR, checksum in cfg_bin:0x%02x,"
CFG_TAG "checksum caculate:0x%02x"
, cfg_bin->head.checksum, checksum);
r = -EINVAL;
goto exit;
}
/*allocate memory for cfg packages*/
cfg_bin->cfg_pkgs =
kzalloc(sizeof(struct goodix_cfg_package) * cfg_bin->head.pkg_num,
GFP_KERNEL);
if (!cfg_bin->cfg_pkgs) {
ts_err("cfg_pkgs, allocate memory ERROR");
r = -ENOMEM;
goto exit;
}
/*get cfg_pkg's info*/
for (i = 0; i < cfg_bin->head.pkg_num; i++) {
/*get cfg pkg length*/
if (i == cfg_bin->head.pkg_num - 1) {
offset1 = cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN +
i * TS_CFG_OFFSET_LEN] +
(cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN +
i * TS_CFG_OFFSET_LEN + 1] << 8);
cfg_bin->cfg_pkgs[i].pkg_len = cfg_bin->bin_data_len
- offset1;
} else {
offset1 = cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN +
i * TS_CFG_OFFSET_LEN] +
(cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN +
i * TS_CFG_OFFSET_LEN + 1] << 8);
offset2 = cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN +
i * TS_CFG_OFFSET_LEN + 2] +
(cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN +
i * TS_CFG_OFFSET_LEN + 3] << 8);
if (offset2 <= offset1) {
ts_err("offset error,pkg:%d, offset1:%d,"
CFG_TAG "offset2:%d"
, i, offset1, offset2);
r = -EINVAL;
goto exit;
}
cfg_bin->cfg_pkgs[i].pkg_len = offset2 - offset1;
}
/*get cfg pkg head*/
memcpy(&cfg_bin->cfg_pkgs[i].cnst_info,
&cfg_bin->bin_data[offset1],
TS_PKG_CONST_INFO_LEN);
memcpy(&cfg_bin->cfg_pkgs[i].reg_info,
&cfg_bin->bin_data[offset1 + TS_PKG_CONST_INFO_LEN],
TS_PKG_REG_INFO_LEN);
/*compatible little edition and big edition*/
goodix_cfg_pkg_leToCpu(&cfg_bin->cfg_pkgs[i]);
/*get configuration data*/
cfg_bin->cfg_pkgs[i].cfg =
&cfg_bin->bin_data[offset1 + TS_PKG_HEAD_LEN];
}
/*debug, print pkg information*/
for (i = 0; i < cfg_bin->head.pkg_num; i++) {
ts_info("---------------------------------------------");
ts_info("------package:%d------", i + 1);
ts_info("package len:%04x",
cfg_bin->cfg_pkgs[i].cnst_info.pkg_len);
ts_info("package ic_type:%s",
cfg_bin->cfg_pkgs[i].cnst_info.ic_type);
ts_info("package cfg_type:%01x",
cfg_bin->cfg_pkgs[i].cnst_info.cfg_type);
ts_info("package sensor_id:%01x",
cfg_bin->cfg_pkgs[i].cnst_info.sensor_id);
ts_info("package hw_pid:%s",
cfg_bin->cfg_pkgs[i].cnst_info.hw_pid);
ts_info("package hw_vid:%s",
cfg_bin->cfg_pkgs[i].cnst_info.hw_vid);
ts_info("package fw_mask_version:%s",
cfg_bin->cfg_pkgs[i].cnst_info.fw_mask);
ts_info("package fw_patch_version:%s",
cfg_bin->cfg_pkgs[i].cnst_info.fw_patch);
ts_info("package x_res_offset:%02x",
cfg_bin->cfg_pkgs[i].cnst_info.x_res_offset);
ts_info("package y_res_offset:%02x",
cfg_bin->cfg_pkgs[i].cnst_info.y_res_offset);
ts_info("package trigger_offset:%02x",
cfg_bin->cfg_pkgs[i].cnst_info.trigger_offset);
ts_info("");
ts_info("send_cfg_flag reg:%02x",
cfg_bin->cfg_pkgs[i].reg_info.cfg_send_flag.addr);
ts_info("version base reg:%02x, len:%d",
cfg_bin->cfg_pkgs[i].reg_info.version_base.addr,
cfg_bin->cfg_pkgs[i].reg_info.version_base.reserved1);
ts_info("pid reg:%02x", cfg_bin->cfg_pkgs[i].reg_info.pid.addr);
ts_info("vid reg:%02x", cfg_bin->cfg_pkgs[i].reg_info.vid.addr);
ts_info("sensor_id reg:%02x",
cfg_bin->cfg_pkgs[i].reg_info.sensor_id.addr);
ts_info("fw_status reg:%02x",
cfg_bin->cfg_pkgs[i].reg_info.fw_status.addr);
ts_info("cfg_addr reg:%02x",
cfg_bin->cfg_pkgs[i].reg_info.cfg_addr.addr);
ts_info("esd reg:%02x", cfg_bin->cfg_pkgs[i].reg_info.esd.addr);
ts_info("command reg:%02x",
cfg_bin->cfg_pkgs[i].reg_info.command.addr);
ts_info("coor:%02x",
cfg_bin->cfg_pkgs[i].reg_info.coor.addr);
ts_info("gesture:%02x",
cfg_bin->cfg_pkgs[i].reg_info.gesture.addr);
ts_info("fw_request:%02x",
cfg_bin->cfg_pkgs[i].reg_info.fw_request.addr);
ts_info("proximity:%02x",
cfg_bin->cfg_pkgs[i].reg_info.proximity.addr);
ts_info("--------------------------------------------");
}
r = 0;
exit:
return r;
}
int goodix_cfg_bin_proc(void *data)
{
struct goodix_ts_core *core_data = data;
struct goodix_ts_device *ts_dev = core_data->ts_dev;
struct device *dev = ts_dev->dev;
int r;
struct goodix_cfg_bin *cfg_bin =
kzalloc(sizeof(struct goodix_cfg_bin), GFP_KERNEL);
if (!cfg_bin) {
ts_err("Failed to alloc memory for cfg_bin");
r = -ENOMEM;
goto out;
}
cfg_bin->cfg_pkgs = NULL;
/*get cfg_bin from file system*/
r = goodix_read_cfg_bin(dev, cfg_bin);
if (r < 0) {
ts_err("read cfg_bin from /etc/firmware FAILED");
goto exit;
}
/*parse cfg bin*/
r = goodix_parse_cfg_bin(cfg_bin);
if (!r) {
ts_info("parse cfg bin SUCCESS");
} else {
ts_err("parse cfg bin FAILED");
goto exit;
}
/*init i2c_set_doze_mode para*/
ts_dev->doze_mode_set_count = 0;
mutex_init(&ts_dev->doze_mode_lock);
/*get register address and configuration from cfg bin*/
r = goodix_get_reg_and_cfg(ts_dev, cfg_bin);
if (!r) {
ts_info("get reg and cfg from cfg_bin SUCCESS");
} else {
if (r != -EBUS) {
ts_err("get reg and cfg from cfg_bin FAILED,"
CFG_TAG "update fw then retry");
goodix_modules.core_data = core_data;
goodix_modules.core_exit = false;
complete_all(&goodix_modules.core_comp);
goto exit;
} else {
ts_err("get reg and cfg from cfg_bin FAILED,"
CFG_TAG "I2C com ERROR");
goto exit;
}
}
/*debug*/
ts_info("@@@@@@@@@");
ts_info("cfg_send_flag:0x%04x", ts_dev->reg.cfg_send_flag);
ts_info("pid:0x%04x", ts_dev->reg.pid);
ts_info("vid:0x%04x", ts_dev->reg.vid);
ts_info("sensor_id:0x%04x", ts_dev->reg.sensor_id);
ts_info("fw_mask:0x%04x", ts_dev->reg.fw_mask);
ts_info("fw_status:0x%04x", ts_dev->reg.fw_status);
ts_info("cfg_addr:0x%04x", ts_dev->reg.cfg_addr);
ts_info("esd:0x%04x", ts_dev->reg.esd);
ts_info("command:0x%04x", ts_dev->reg.command);
ts_info("coor:0x%04x", ts_dev->reg.coor);
ts_info("gesture:0x%04x", ts_dev->reg.gesture);
ts_info("fw_request:0x%04x", ts_dev->reg.fw_request);
ts_info("proximity:0x%04x", ts_dev->reg.proximity);
ts_info("@@@@@@@@@");
/* initialize firmware */
r = ts_dev->hw_ops->init(ts_dev);
if (-EBUS == r)
goto exit;
/* alloc/config/register input device */
r = goodix_ts_input_dev_config(core_data);
if (r < 0)
goto exit;
/* request irq line */
r = goodix_ts_irq_setup(core_data);
if (r < 0)
goto exit;
/*set flag, prevent fwupdate module parse cfg_group again*/
core_data->cfg_group_parsed = true;
/* inform the external module manager that*/
/* touch core layer is ready now */
goodix_modules.core_data = core_data;
goodix_modules.core_exit = false;
/*complete_all(&goodix_modules.core_comp);*/
#ifdef CONFIG_FB
core_data->fb_notifier.notifier_call = goodix_ts_fb_notifier_callback;
if (fb_register_client(&core_data->fb_notifier))
ts_err("Failed to register fb notifier client:%d", r);
#elif defined(CONFIG_HAS_EARLYSUSPEND)
core_data->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
core_data->early_suspend.resume = goodix_ts_lateresume;
core_data->early_suspend.suspend = goodix_ts_earlysuspend;
register_early_suspend(&core_data->early_suspend);
#endif
/* esd protector */
#ifdef GT9886_ESD_ENABLE
goodix_ts_esd_init(core_data);
#endif
/* generic notifier callback */
core_data->ts_notifier.notifier_call = goodix_generic_noti_callback;
goodix_ts_register_notifier(&core_data->ts_notifier);
exit:
complete_all(&goodix_modules.core_comp);
if (cfg_bin->cfg_pkgs) {
kfree(cfg_bin->cfg_pkgs);
cfg_bin->cfg_pkgs = NULL;
}
if (cfg_bin->bin_data) {
kfree(cfg_bin->bin_data);
cfg_bin->bin_data = NULL;
}
kfree(cfg_bin);
out:
return r;
}
int goodix_get_reg_and_cfg(struct goodix_ts_device *ts_dev,
struct goodix_cfg_bin *cfg_bin)
{
int i;
u16 addr;
u8 read_len;
struct goodix_cfg_package *normal_pkg, *high_sense_pkg;
char temp_sensor_id = -1;
u8 temp_fw_mask[TS_CFG_BLOCK_FW_MASK_LEN] = {0x00};
u8 temp_pid[TS_CFG_BLOCK_PID_LEN] = {0x00};
int r = -EINVAL;
normal_pkg = NULL;
high_sense_pkg = NULL;
if (!cfg_bin->head.pkg_num || !cfg_bin->cfg_pkgs) {
ts_err("there is none cfg package, pkg_num:%d",
cfg_bin->head.pkg_num);
r = -EINVAL;
goto exit;
}
/*select suitable cfg packages*/
for (i = 0; i < cfg_bin->head.pkg_num; i++) {
/*get ic type*/
if (!strncmp(cfg_bin->cfg_pkgs[i].cnst_info.ic_type, "nanjing",
sizeof(cfg_bin->cfg_pkgs[i].cnst_info.ic_type)))
ts_dev->ic_type = IC_TYPE_NANJING;
else if (!strncmp(cfg_bin->cfg_pkgs[i].cnst_info.ic_type,
"normandy",
sizeof(cfg_bin->cfg_pkgs[i].cnst_info.ic_type)))
ts_dev->ic_type = IC_TYPE_NORMANDY;
else
ts_err("get ic type FAILED,"
CFG_TAG "unknown ic type from cfg_bin:%s",
cfg_bin->cfg_pkgs[i].cnst_info.ic_type);
ts_info("ic_type:%d", ts_dev->ic_type);
/*contrast sensor id*/
addr = cfg_bin->cfg_pkgs[i].reg_info.sensor_id.addr;
if (!addr) {
ts_info("pkg:%d, sensor_id reg is NULL", i);
continue;
}
r = goodix_i2c_read(ts_dev, addr,
&temp_sensor_id, 1);
if (r < 0) {
ts_err("read sensor id FAILED,I2C ERROR, pkg:%d,"
CFG_TAG "sensor_id reg:0x%02x", i, addr);
goto exit;
} else {
/*sensor.reserved1 is a mask, if it's not ZERO, use it*/
if (cfg_bin->cfg_pkgs[i].reg_info.sensor_id.reserved1
!= 0)
temp_sensor_id &=
cfg_bin->cfg_pkgs[i].reg_info.sensor_id.reserved1;
if (temp_sensor_id !=
cfg_bin->cfg_pkgs[i].cnst_info.sensor_id) {
ts_err("pkg:%d, sensor id contrast FAILED,"
CFG_TAG "reg:0x%02x", i, addr);
ts_err("sensor_id from i2c:%d, sensor_id of cfg"
CFG_TAG "bin:%d", temp_sensor_id,
cfg_bin->cfg_pkgs[i].cnst_info.sensor_id);
continue;
}
}
/*contrast fw_mask, if this reg is null, skip this step*/
addr = cfg_bin->cfg_pkgs[i].reg_info.fw_mask.addr;
if (!addr || !cfg_bin->cfg_pkgs[i].cnst_info.fw_mask[0]) {
ts_err("pkg:%d, fw_mask of cfg bin is NULL, Skip!!", i);
} else {
r = goodix_i2c_read(ts_dev, addr, temp_fw_mask,
sizeof(temp_fw_mask));
if (r < 0) {
ts_err("read fw_mask FAILED, I2C ERROR,pkg:"
CFG_TAG "%d, fw_mask reg:0x%02x", i, addr);
goto exit;
} else if (strncmp(temp_fw_mask,
cfg_bin->cfg_pkgs[i].cnst_info.fw_mask,
sizeof(temp_fw_mask))) {
ts_err("pkg:%d, fw_mask contrast FAILED,"
CFG_TAG "reg:0x%02x,", i, addr);
ts_err("mask from i2c:%s, mask of cfg bin:%s",
temp_fw_mask,
cfg_bin->cfg_pkgs[i].cnst_info.fw_mask);
continue;
}
}
/*contrast pid*/
addr = cfg_bin->cfg_pkgs[i].reg_info.pid.addr;
read_len = cfg_bin->cfg_pkgs[i].reg_info.pid.reserved1;
if (!addr) {
ts_err("pkg:%d, pid reg is NULL", i);
continue;
}
if (read_len <= 0 || read_len > TS_CFG_BLOCK_PID_LEN) {
ts_err("pkg:%d, hw_pid len ERROR, len:%d", i, read_len);
continue;
}
r = goodix_i2c_read(ts_dev, addr, temp_pid, read_len);
if (r < 0) {
ts_err("read pid FAILED, I2C ERROR, pkg: %d,"
CFG_TAG "pid reg:0x%02x", i, addr);
goto exit;
} else if (strncmp(temp_pid,\
cfg_bin->cfg_pkgs[i].cnst_info.hw_pid, read_len)) {
ts_err("pkg:%d, pid contrast FAILED,"
CFG_TAG "reg:0x%02x", i, addr);
ts_err("pid from i2c:%s, pid of cfg bin:%s"
, temp_pid,
cfg_bin->cfg_pkgs[i].cnst_info.hw_pid);
continue;
}
/*contrast success, cfg_type*/
if (cfg_bin->cfg_pkgs[i].cnst_info.cfg_type == TS_NORMAL_CFG) {
ts_info("find normal cfg_pkg SUCCESS");
r = 0;
normal_pkg = &cfg_bin->cfg_pkgs[i];
}
if (cfg_bin->cfg_pkgs[i].cnst_info.cfg_type ==
TS_HIGH_SENSE_CFG) {
ts_info("find high sense cfg_pkg SUCCESS");
high_sense_pkg = &cfg_bin->cfg_pkgs[i];
}
}
/*get register address from normal_pkg*/
if (!normal_pkg) {
ts_err("ERROR, none suitable normal_pkg exist in cfg_bin");
/*ts_dev->ic_type = IC_TYPE_NONE;*/
r = -EINVAL;
goto exit;
} else {
/*get ic type*/
if (!strncmp(normal_pkg->cnst_info.ic_type, "nanjing",
sizeof(normal_pkg->cnst_info.ic_type)))
ts_dev->ic_type = IC_TYPE_NANJING;
else if (!strncmp(normal_pkg->cnst_info.ic_type, "normandy",
sizeof(normal_pkg->cnst_info.ic_type)))
ts_dev->ic_type = IC_TYPE_NORMANDY;
else
ts_err("get ic type FAILED, unknown ic type from"
CFG_TAG "cfg_bin:%s"
, normal_pkg->cnst_info.ic_type);
/*get register info*/
ts_dev->reg.cfg_send_flag =
normal_pkg->reg_info.cfg_send_flag.addr;
ts_dev->reg.version_base =
normal_pkg->reg_info.version_base.addr;
ts_dev->reg.version_len =
normal_pkg->reg_info.version_base.reserved1;
ts_dev->reg.pid = normal_pkg->reg_info.pid.addr;
ts_dev->reg.pid_len = normal_pkg->reg_info.pid.reserved1;
ts_dev->reg.vid = normal_pkg->reg_info.vid.addr;
ts_dev->reg.vid_len = normal_pkg->reg_info.vid.reserved1;
ts_dev->reg.sensor_id = normal_pkg->reg_info.sensor_id.addr;
ts_dev->reg.sensor_id_mask =
normal_pkg->reg_info.sensor_id.reserved1;
ts_dev->reg.fw_mask = normal_pkg->reg_info.fw_mask.addr;
ts_dev->reg.fw_status = normal_pkg->reg_info.fw_status.addr;
ts_dev->reg.cfg_addr = normal_pkg->reg_info.cfg_addr.addr;
ts_dev->reg.esd = normal_pkg->reg_info.esd.addr;
ts_dev->reg.command = normal_pkg->reg_info.command.addr;
ts_dev->reg.coor = normal_pkg->reg_info.coor.addr;
ts_dev->reg.gesture = normal_pkg->reg_info.gesture.addr;
ts_dev->reg.fw_request = normal_pkg->reg_info.fw_request.addr;
ts_dev->reg.proximity = normal_pkg->reg_info.proximity.addr;
}
/*get configuration from pkgs*/
if (normal_pkg) {
ts_info("normal cfg is found!");
if (!ts_dev->normal_cfg) {
ts_dev->normal_cfg = devm_kzalloc(ts_dev->dev,
sizeof(*ts_dev->normal_cfg), GFP_KERNEL);
if (!ts_dev->normal_cfg) {
ts_err("Failed to alloc memory for normal cfg");
return -ENOMEM;
}
mutex_init(&ts_dev->normal_cfg->lock);
}
ts_dev->normal_cfg->length = normal_pkg->pkg_len -
TS_PKG_CONST_INFO_LEN - TS_PKG_REG_INFO_LEN;
memcpy(ts_dev->normal_cfg->data, normal_pkg->cfg
, ts_dev->normal_cfg->length);
}
if (high_sense_pkg) {
ts_info("high sense cfg is found!");
if (!ts_dev->highsense_cfg) {
ts_dev->highsense_cfg = devm_kzalloc(ts_dev->dev,
sizeof(*ts_dev->highsense_cfg), GFP_KERNEL);
if (!ts_dev->highsense_cfg) {
ts_err("Failed to alloc"
CFG_TAG " memory for high sense cfg");
return -ENOMEM;
}
mutex_init(&ts_dev->highsense_cfg->lock);
}
ts_dev->highsense_cfg->length = high_sense_pkg->pkg_len -
TS_PKG_CONST_INFO_LEN - TS_PKG_REG_INFO_LEN;
memcpy(ts_dev->highsense_cfg->data,
high_sense_pkg->cfg,
ts_dev->highsense_cfg->length);
}
exit:
return r;
}
int goodix_read_cfg_bin(struct device *dev, struct goodix_cfg_bin *cfg_bin)
{
int r;
const struct firmware *firmware = NULL;
char cfg_bin_name[32] = {0};
int i = 0;
/*get cfg_bin_name*/
r = snprintf(cfg_bin_name, sizeof(cfg_bin_name), "%s%s.bin",
TS_DEFAULT_CFG_BIN, gt9886_config_buf);
if (r >= sizeof(cfg_bin_name)) {
ts_err("get cfg_bin name FAILED!!!");
goto exit;
}
ts_info("cfg_bin_name:%s", cfg_bin_name);
for (i = 0; i < TS_RQST_FW_RETRY_TIMES; i++) {
r = request_firmware(&firmware, cfg_bin_name, dev);
if (r < 0) {
ts_err("Cfg_bin image [%s] not available,error:%d,"
CFG_TAG"try_times:%d", cfg_bin_name, r, i + 1);
msleep(1000);
} else {
ts_info("Cfg_bin image [%s] is ready, try_times:%d",
cfg_bin_name, i + 1);
break;
}
}
if (i >= TS_RQST_FW_RETRY_TIMES) {
ts_err("get cfg_bin FAILED");
goto exit;
}
if (firmware->size <= 0) {
ts_err("request_firmware, cfg_bin length ERROR,len:%zu",
firmware->size);
r = -EINVAL;
goto exit;
}
cfg_bin->bin_data_len = firmware->size;
/*allocate memory for cfg_bin->bin_data*/
cfg_bin->bin_data = kzalloc(cfg_bin->bin_data_len, GFP_KERNEL);
if (!cfg_bin->bin_data) {
ts_err("Allocate memory for cfg_bin->bin_data FAILED");
r = -ENOMEM;
goto exit;
}
memcpy(cfg_bin->bin_data, firmware->data, cfg_bin->bin_data_len);
r = 0;
exit:
if (firmware) {
release_firmware(firmware);
firmware = NULL;
}
return r;
}
int goodix_read_cfg_bin_from_dts(struct device_node *node,
struct goodix_cfg_bin *cfg_bin)
{
unsigned int len = 0;
struct property *prop = NULL;
prop = of_find_property(node, "goodix_cfg_bin", &len);
if (!prop || !prop->value || len == 0) {
ts_err("Invalid cfg type, size:%u", len);
return -EINVAL;
}
cfg_bin->bin_data_len = len;
/*allocate memory for cfg_bin->bin_data*/
cfg_bin->bin_data = kzalloc(cfg_bin->bin_data_len, GFP_KERNEL);
if (!cfg_bin->bin_data) {
ts_err("Allocate memory for cfg_bin->bin_data FAILED");
return -ENOMEM;
}
memcpy(cfg_bin->bin_data, prop->value, cfg_bin->bin_data_len);
return 0;
}
void goodix_cfg_pkg_leToCpu(struct goodix_cfg_package *pkg)
{
if (!pkg) {
ts_err("cfg package is NULL");
return;
}
/*package const_info*/
pkg->cnst_info.pkg_len = le32_to_cpu(pkg->cnst_info.pkg_len);
pkg->cnst_info.x_res_offset = le16_to_cpu(pkg->cnst_info.x_res_offset);
pkg->cnst_info.y_res_offset = le16_to_cpu(pkg->cnst_info.y_res_offset);
pkg->cnst_info.trigger_offset =
le16_to_cpu(pkg->cnst_info.trigger_offset);
/*package reg_info*/
pkg->reg_info.cfg_send_flag.addr =
le16_to_cpu(pkg->reg_info.cfg_send_flag.addr);
pkg->reg_info.pid.addr = le16_to_cpu(pkg->reg_info.pid.addr);
pkg->reg_info.vid.addr = le16_to_cpu(pkg->reg_info.vid.addr);
pkg->reg_info.sensor_id.addr =
le16_to_cpu(pkg->reg_info.sensor_id.addr);
pkg->reg_info.fw_status.addr =
le16_to_cpu(pkg->reg_info.fw_status.addr);
pkg->reg_info.cfg_addr.addr = le16_to_cpu(pkg->reg_info.cfg_addr.addr);
pkg->reg_info.esd.addr = le16_to_cpu(pkg->reg_info.esd.addr);
pkg->reg_info.command.addr = le16_to_cpu(pkg->reg_info.command.addr);
pkg->reg_info.coor.addr = le16_to_cpu(pkg->reg_info.coor.addr);
pkg->reg_info.gesture.addr = le16_to_cpu(pkg->reg_info.gesture.addr);
pkg->reg_info.fw_request.addr =
le16_to_cpu(pkg->reg_info.fw_request.addr);
pkg->reg_info.proximity.addr =
le16_to_cpu(pkg->reg_info.proximity.addr);
}
@@ -0,0 +1,124 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef _GOODIX_CFG_BIN_H_
#define _GOODIX_CFG_BIN_H_
#include "goodix_ts_core.h"
#define TS_DEFAULT_FIRMWARE "gt9886_firmware_"
#define TS_DEFAULT_CFG_BIN "gt9886_cfg_"
extern const char *gt9886_firmware_buf;
extern const char *gt9886_config_buf;
#define TS_BIN_VERSION_START_INDEX 5
#define TS_BIN_VERSION_LEN 4
#define TS_CFG_BIN_HEAD_RESERVED_LEN 6
#define TS_CFG_OFFSET_LEN 2
#define TS_IC_TYPE_NAME_MAX_LEN 15
#define TS_CFG_BIN_HEAD_LEN \
(sizeof(struct goodix_cfg_bin_head) + TS_CFG_BIN_HEAD_RESERVED_LEN)
#define TS_PKG_CONST_INFO_LEN (sizeof(struct goodix_cfg_pkg_const_info))
#define TS_PKG_REG_INFO_LEN (sizeof(struct goodix_cfg_pkg_reg_info))
#define TS_PKG_HEAD_LEN (TS_PKG_CONST_INFO_LEN + TS_PKG_REG_INFO_LEN)
/*cfg block definitin*/
#define TS_CFG_BLOCK_PID_LEN 8
#define TS_CFG_BLOCK_VID_LEN 8
#define TS_CFG_BLOCK_FW_MASK_LEN 9
#define TS_CFG_BLOCK_FW_PATCH_LEN 4
#define TS_CFG_BLOCK_RESERVED_LEN 9
#define TS_NORMAL_CFG 0x01
#define TS_HIGH_SENSE_CFG 0x03
#define TS_RQST_FW_RETRY_TIMES 2
#pragma pack(1)
struct goodix_cfg_pkg_reg {
u16 addr;
u8 reserved1;
u8 reserved2;
};
struct goodix_cfg_pkg_const_info {
u32 pkg_len;
u8 ic_type[TS_IC_TYPE_NAME_MAX_LEN];
u8 cfg_type;
u8 sensor_id;
u8 hw_pid[TS_CFG_BLOCK_PID_LEN];
u8 hw_vid[TS_CFG_BLOCK_VID_LEN];
u8 fw_mask[TS_CFG_BLOCK_FW_MASK_LEN];
u8 fw_patch[TS_CFG_BLOCK_FW_PATCH_LEN];
u16 x_res_offset;
u16 y_res_offset;
u16 trigger_offset;
};
struct goodix_cfg_pkg_reg_info {
struct goodix_cfg_pkg_reg cfg_send_flag;
struct goodix_cfg_pkg_reg version_base;
struct goodix_cfg_pkg_reg pid;
struct goodix_cfg_pkg_reg vid;
struct goodix_cfg_pkg_reg sensor_id;
struct goodix_cfg_pkg_reg fw_mask;
struct goodix_cfg_pkg_reg fw_status;
struct goodix_cfg_pkg_reg cfg_addr;
struct goodix_cfg_pkg_reg esd;
struct goodix_cfg_pkg_reg command;
struct goodix_cfg_pkg_reg coor;
struct goodix_cfg_pkg_reg gesture;
struct goodix_cfg_pkg_reg fw_request;
struct goodix_cfg_pkg_reg proximity;
u8 reserved[TS_CFG_BLOCK_RESERVED_LEN];
};
struct goodix_cfg_bin_head {
u32 bin_len;
u8 checksum;
u8 bin_version[TS_BIN_VERSION_LEN];
u8 pkg_num;
};
#pragma pack()
struct goodix_cfg_package {
struct goodix_cfg_pkg_const_info cnst_info;
struct goodix_cfg_pkg_reg_info reg_info;
const u8 *cfg;
u32 pkg_len;
};
struct goodix_cfg_bin {
unsigned char *bin_data;
unsigned int bin_data_len;
struct goodix_cfg_bin_head head;
struct goodix_cfg_package *cfg_pkgs;
};
int goodix_cfg_bin_proc(void *data);
int goodix_parse_cfg_bin(struct goodix_cfg_bin *cfg_bin);
int goodix_get_reg_and_cfg(struct goodix_ts_device *ts_dev,
struct goodix_cfg_bin *cfg_bin);
int goodix_read_cfg_bin(struct device *dev, struct goodix_cfg_bin *cfg_bin);
int goodix_read_cfg_bin_from_dts(struct device_node *node,
struct goodix_cfg_bin *cfg_bin);
void goodix_cfg_pkg_leToCpu(struct goodix_cfg_package *pkg);
int goodix_start_cfg_bin(struct goodix_ts_core *ts_core);
extern int goodix_i2c_write(struct goodix_ts_device *dev, unsigned int reg,
unsigned char *data, unsigned int len);
extern int goodix_i2c_read(struct goodix_ts_device *dev, unsigned int reg,
unsigned char *data, unsigned int len);
extern struct goodix_module goodix_modules;
#endif
@@ -0,0 +1,10 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef _GOODIX_FIRMWARE_FW_H_
#define _GOODIX_FIRMWARE_FW_H_
u8 goodix_default_fw[] = { 0x00};
#endif
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,752 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifndef _GOODIX_TS_CORE_H_
#define _GOODIX_TS_CORE_H_
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/firmware.h>
#include <linux/slab.h>
#include <asm/unaligned.h>
#include <linux/vmalloc.h>
#include <linux/kthread.h>
#include <linux/version.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/completion.h>
#include <linux/debugfs.h>
#include <linux/of_irq.h>
#ifdef CONFIG_OF
#include <linux/of_gpio.h>
#include <linux/regulator/consumer.h>
#endif
#ifdef CONFIG_HAS_EARLYSUSPEND
#include <linux/earlysuspend.h>
#endif
#ifdef CONFIG_FB
#include <linux/notifier.h>
#include <linux/fb.h>
#endif
/* macros definition */
/* mtk touch factory test: "mtk-tpd2" */
#define GOODIX_CORE_DRIVER_NAME "gt9886"
#define GOODIX_INPUT_DEV_NAME "mtk-tpd2"
#define GOODIX_DRIVER_VERSION "v1.2.0.1"
#define GOODIX_TS_PID_GT9886 "9886"
#define GOODIX_TS_PID_GT9885 "9885"
#define GOODIX_BUS_RETRY_TIMES 3
#define GOODIX_CHIPID_RETRY_TIMES 5
#define GOODIX_MAX_TOUCH 10
#define GOODIX_MAX_PEN 1
#define GOODIX_MAX_KEY 3
#define GOODIX_PEN_MAX_KEY 2
#define GOODIX_CFG_MAX_SIZE 1024
#define GOODIX_ESD_TICK_WRITE_DATA 0xAA
#define GOODIX_PID_MAX_LEN 8
#define GOODIX_VID_MAX_LEN 8
#define GOODIX_DEFAULT_CFG_NAME "goodix_config.cfg"
#define IC_TYPE_NONE 0
#define IC_TYPE_NORMANDY 1
#define IC_TYPE_NANJING 2
#define PINCTRL_STATE_I2C_DEFAULT "ts_i2c_mode"
#define PINCTRL_STATE_INT_ACTIVE "ts_int_active"
#define PINCTRL_STATE_RST_ACTIVE "ts_reset_active"
#define PINCTRL_STATE_INT_SUSPEND "ts_int_suspend"
#define PINCTRL_STATE_RST_SUSPEND "ts_reset_suspend"
#define GOODIX_TOUCH_EVENT 0x80
#define GOODIX_REQUEST_EVENT 0x40
#define GOODIX_GESTURE_EVENT 0x20
#define GOODIX_HOTKNOT_EVENT 0x10
/* For MTK Internal Touch Begin */
/* touch-filter Begin */
struct tpd_filter_t {
int enable; /*0: disable, 1: enable*/
int pixel_density; /*XXX pixel/cm*/
int W_W[3][4];/*filter custom setting prameters*/
unsigned int VECLOCITY_THRESHOLD[3];/*filter speed custom settings*/
};
#define TOUCH_IOC_MAGIC 'A'
#define TPD_GET_FILTER_PARA _IOWR(TOUCH_IOC_MAGIC, 2, struct tpd_filter_t)
#ifdef CONFIG_COMPAT
#define COMPAT_TPD_GET_FILTER_PARA _IOWR(TOUCH_IOC_MAGIC, \
2, struct tpd_filter_t)
#endif
/*
* struct goodix_module - external modules container
* @head: external modules list
* @initilized: whether this struct is initialized
* @mutex: mutex lock
* @count: current number of registered external module
* @wq: workqueue to do register work
* @core_exit: if goodix touch core exit, then no
* registration is allowed.
* @core_data: core_data pointer
*/
struct goodix_module {
struct list_head head;
bool initilized;
struct mutex mutex;
unsigned int count;
struct workqueue_struct *wq;
bool core_exit;
struct completion core_comp;
struct goodix_ts_core *core_data;
};
/*
* struct goodix_ts_board_data - board data
* @avdd_name: name of analoy regulator
* @reset_gpio: reset gpio number
* @irq_gpio: interrupt gpio number
* @irq_flag: irq trigger type
* @power_on_delay_us: power on delay time (us)
* @power_off_delay_us: power off delay time (us)
* @swap_axis: whether swaw x y axis
* @panel_max_id: max supported fingers
* @panel_max_x/y/w/p: resolution and size
* @panel_max_key: max supported keys
* @pannel_key_map: key map
* @fw_name: name of the firmware image
*/
struct goodix_ts_board_data {
const char *avdd_name;
unsigned int reset_gpio;
unsigned int irq_gpio;
int irq;
unsigned int irq_flags;
unsigned int power_on_delay_us;
unsigned int power_off_delay_us;
/* For MTK Internal Touch Begin */
unsigned int input_max_x;
unsigned int input_max_y;
/* For MTK Internal Touch End */
unsigned int swap_axis;
unsigned int panel_max_id; /*max touch id*/
unsigned int panel_max_x;
unsigned int panel_max_y;
unsigned int panel_max_w; /*major and minor*/
unsigned int panel_max_p; /*pressure*/
unsigned int panel_max_key;
unsigned int panel_key_map[GOODIX_MAX_KEY + GOODIX_PEN_MAX_KEY];
/*add by lishuai*/
unsigned int x2x;
unsigned int y2y;
bool pen_enable;
unsigned int tp_key_num;
/*add end*/
const char *fw_name;
const char *cfg_bin_name;
bool esd_default_on;
struct device *pinctrl_dev;
struct tpd_filter_t tpd_filter;
};
/*
* struct goodix_ts_config - chip config data
* @initialized: whether initialized
* @name: name of this config
* @lock: mutex
* @reg_base: register base of config data
* @length: bytes of the config
* @delay: delay time after sending config
* @data: config data buffer
*/
struct goodix_ts_config {
bool initialized;
char name[24];
struct mutex lock;
unsigned int reg_base;
unsigned int length;
unsigned int delay; /*ms*/
unsigned char data[GOODIX_CFG_MAX_SIZE];
};
/*
* struct goodix_ts_cmd - command package
* @initialized: whether initialized
* @cmd_reg: command register
* @length: command length in bytes
* @cmds: command data
*/
#pragma pack(4)
struct goodix_ts_cmd {
u32 initialized;
u32 cmd_reg;
u32 length;
u8 cmds[3];
};
#pragma pack()
/* interrupt event type */
enum ts_event_type {
EVENT_INVALID,
EVENT_TOUCH,
EVENT_REQUEST,
};
/* requset event type */
enum ts_request_type {
REQUEST_INVALID,
REQUEST_CONFIG,
REQUEST_BAKREF,
REQUEST_RESET,
REQUEST_MAINCLK,
};
/* notifier event */
enum ts_notify_event {
NOTIFY_FWUPDATE_START,
NOTIFY_FWUPDATE_END,
NOTIFY_SUSPEND,
NOTIFY_RESUME,
NOTIFY_ESD_OFF,
NOTIFY_ESD_ON,
};
/* coordinate package */
struct goodix_ts_coords {
int id;
unsigned int x, y, w, p;
};
/* touch event data */
struct goodix_touch_data {
/* finger */
int touch_num;
struct goodix_ts_coords coords[GOODIX_MAX_TOUCH];
/* key */
u8 key_value;
bool have_key;
/*pen*/
struct goodix_ts_coords pen_coords[GOODIX_MAX_PEN];
bool pen_down;
};
/* request event data */
struct goodix_request_data {
enum ts_request_type request_type;
};
/*
* struct goodix_ts_event - touch event struct
* @event_type: touch event type, touch data or
* request event
* @event_data: event data
*/
struct goodix_ts_event {
enum ts_event_type event_type;
union {
struct goodix_touch_data touch_data;
struct goodix_request_data request_data;
} event_data;
};
/*
* struct goodix_ts_version - firmware version
* @valid: whether these information is valid
* @pid: product id string
* @vid: firmware version code
* @cid: customer id code
* @sensor_id: sendor id
*/
struct goodix_ts_version {
bool valid;
char pid[GOODIX_PID_MAX_LEN];
char vid[GOODIX_VID_MAX_LEN];
u8 cid;
u8 sensor_id;
};
struct goodix_ts_regs {
u16 cfg_send_flag;
u16 version_base;
u8 version_len;
u16 pid;
u8 pid_len;
u16 vid;
u8 vid_len;
u16 sensor_id;
u8 sensor_id_mask;
u16 fw_mask;
u16 fw_status;
u16 cfg_addr;
u16 esd;
u16 command;
u16 coor;
u16 gesture;
u16 fw_request;
u16 proximity;
};
/*
* struct goodix_ts_device - ts device data
* @name: device name
* @version: reserved
* @bus_type: i2c or spi
* @board_data: board data obtained from dts
* @normal_cfg: normal config data
* @highsense_cfg: high sense config data
* @hw_ops: hardware operations
* @chip_version: firmware version information
* @sleep_cmd: sleep commang
* @gesture_cmd: gesture command
* @dev: device pointer,may be a i2c or spi device
* @of_node: device node
*/
struct goodix_ts_device {
char *name;
int version;
int bus_type;
u8 ic_type;
struct goodix_ts_regs reg;
int doze_mode_set_count;
struct mutex doze_mode_lock;
struct goodix_ts_board_data *board_data;
struct goodix_ts_config *normal_cfg;
struct goodix_ts_config *highsense_cfg;
const struct goodix_ts_hw_ops *hw_ops;
struct goodix_ts_version chip_version;
struct device *dev;
};
/*
* struct goodix_ts_hw_ops - hardware opeartions
* @init: hardware initialization
* @reset: hardware reset
* @read: read data from touch device
* @write: write data to touch device
* @send_cmd: send command to touch device
* @send_config: send configuration data
* @read_version: read firmware version
* @event_handler: touch event handler
* @suspend: put touch device into low power mode
* @resume: put touch device into working mode
*/
struct goodix_ts_hw_ops {
int (*init)(struct goodix_ts_device *dev);
int (*dev_confirm)(struct goodix_ts_device *ts_dev);
int (*reset)(struct goodix_ts_device *dev);
int (*read)(struct goodix_ts_device *dev, unsigned int addr,
unsigned char *data, unsigned int len);
int (*write)(struct goodix_ts_device *dev, unsigned int addr,
unsigned char *data, unsigned int len);
int (*read_trans)(struct goodix_ts_device *dev, unsigned int addr,
unsigned char *data, unsigned int len);
int (*write_trans)(struct goodix_ts_device *dev, unsigned int addr,
unsigned char *data, unsigned int len);
int (*send_cmd)(struct goodix_ts_device *dev,
struct goodix_ts_cmd *cmd);
int (*send_config)(struct goodix_ts_device *dev,
struct goodix_ts_config *config);
int (*read_config)(struct goodix_ts_device *dev,
u8 *config_data, u32 config_len);
int (*read_version)(struct goodix_ts_device *dev,
struct goodix_ts_version *version);
int (*event_handler)(struct goodix_ts_device *dev,
struct goodix_ts_event *ts_event);
int (*check_hw)(struct goodix_ts_device *dev);
int (*suspend)(struct goodix_ts_device *dev);
int (*resume)(struct goodix_ts_device *dev);
int (*read_pid)(struct goodix_ts_device *dev,
struct goodix_ts_version *version);
};
/*
* struct goodix_ts_esd - esd protector structure
* @esd_work: esd delayed work
* @esd_on: true - turn on esd protection, false - turn
* off esd protection
* @esd_mutex: protect @esd_on flag
*/
struct goodix_ts_esd {
struct delayed_work esd_work;
struct mutex esd_mutex;
struct notifier_block esd_notifier;
struct goodix_ts_core *ts_core;
bool esd_on;
};
/*
* struct godix_ts_core - core layer data struct
* @pdev: core layer platform device
* @ts_dev: hardware layer touch device
* @input_dev: input device
* @avdd: analog regulator
* @pinctrl: pinctrl handler
* @pin_sta_active: active/normal pin state
* @pin_sta_suspend: suspend/sleep pin state
* @ts_event: touch event data struct
* @power_on: power on/off flag
* @irq: irq number
* @irq_enabled: irq enabled/disabled flag
* @suspended: suspend/resume flag
* @ts_notifier: generic notifier
* @ts_esd: esd protector structure
* @fb_notifier: framebuffer notifier
* @early_suspend: early suspend
*/
struct goodix_ts_core {
struct platform_device *pdev;
struct goodix_ts_device *ts_dev;
struct input_dev *input_dev;
struct regulator *avdd;
#ifdef CONFIG_PINCTRL
struct pinctrl *pinctrl;
struct pinctrl_state *pin_i2c_mode_default;
struct pinctrl_state *pin_int_sta_active;
struct pinctrl_state *pin_int_sta_suspend;
struct pinctrl_state *pin_rst_sta_active;
struct pinctrl_state *pin_rst_sta_suspend;
#endif
struct goodix_ts_event ts_event;
int power_on;
int irq;
size_t irq_trig_cnt;
atomic_t irq_enabled;
atomic_t suspended;
bool cfg_group_parsed;
struct notifier_block ts_notifier;
struct goodix_ts_esd ts_esd;
#ifdef CONFIG_FB
struct notifier_block fb_notifier;
#elif defined(CONFIG_HAS_EARLYSUSPEND)
struct early_suspend early_suspend;
#endif
};
/* external module structures */
enum goodix_ext_priority {
EXTMOD_PRIO_RESERVED = 0,
EXTMOD_PRIO_FWUPDATE,
EXTMOD_PRIO_GESTURE,
EXTMOD_PRIO_HOTKNOT,
EXTMOD_PRIO_DBGTOOL,
EXTMOD_PRIO_DEFAULT,
};
struct goodix_ext_module;
/* external module's operations callback */
struct goodix_ext_module_funcs {
int (*init)(struct goodix_ts_core *core_data,
struct goodix_ext_module *module);
int (*exit)(struct goodix_ts_core *core_data,
struct goodix_ext_module *module);
int (*before_reset)(struct goodix_ts_core *core_data,
struct goodix_ext_module *module);
int (*after_reset)(struct goodix_ts_core *core_data,
struct goodix_ext_module *module);
int (*before_suspend)(struct goodix_ts_core *core_data,
struct goodix_ext_module *module);
int (*after_suspend)(struct goodix_ts_core *core_data,
struct goodix_ext_module *module);
int (*before_resume)(struct goodix_ts_core *core_data,
struct goodix_ext_module *module);
int (*after_resume)(struct goodix_ts_core *core_data,
struct goodix_ext_module *module);
int (*irq_event)(struct goodix_ts_core *core_data,
struct goodix_ext_module *module);
};
/*
* struct goodix_ext_module - external module struct
* @list: list used to link into modules manager
* @name: name of external module
* @priority: module priority vlaue, zero is invalid
* @funcs: operations callback
* @priv_data: private data region
* @kobj: kobject
* @work: used to queue one work to do registration
*/
struct goodix_ext_module {
struct list_head list;
char *name;
enum goodix_ext_priority priority;
const struct goodix_ext_module_funcs *funcs;
void *priv_data;
struct kobject kobj;
struct work_struct work;
};
/*
* struct goodix_ext_attribute - exteranl attribute struct
* @attr: attribute
* @show: show interface of external attribute
* @store: store interface of external attribute
*/
struct goodix_ext_attribute {
struct attribute attr;
ssize_t (*show)(struct goodix_ext_module *, char *);
ssize_t (*store)(struct goodix_ext_module *, const char *, size_t);
};
/* external attrs helper macro */
#define __EXTMOD_ATTR(_name, _mode, _show, _store) { \
.attr = {.name = __stringify(_name), .mode = _mode }, \
.show = _show, \
.store = _store, \
}
/* external attrs helper macro, used to define external attrs */
#define DEFINE_EXTMOD_ATTR(_name, _mode, _show, _store) \
static struct goodix_ext_attribute ext_attr_##_name = \
__EXTMOD_ATTR(_name, _mode, _show, _store);
/*
* get board data pointer
*/
static inline struct goodix_ts_board_data *board_data(
struct goodix_ts_core *core)
{
return core->ts_dev->board_data;
}
/*
* get touch device pointer
*/
static inline struct goodix_ts_device *ts_device(
struct goodix_ts_core *core)
{
return core->ts_dev;
}
/*
* get touch hardware operations pointer
*/
static inline const struct goodix_ts_hw_ops *ts_hw_ops(
struct goodix_ts_core *core)
{
return core->ts_dev->hw_ops;
}
/*
* checksum helper functions
* checksum can be u8/le16/be16/le32/be32 format
* NOTE: the caller should be responsible for the
* legality of @data and @size parameters, so be
* careful when call these functions.
*/
static inline u8 checksum_u8(u8 *data, u32 size)
{
u8 checksum = 0;
u32 i;
for (i = 0; i < size; i++)
checksum += data[i];
return checksum;
}
static inline u16 checksum_le16(u8 *data, u32 size)
{
u16 checksum = 0;
u32 i;
for (i = 0; i < size; i += 2)
checksum += le16_to_cpup((__le16 *)(data + i));
return checksum;
}
static inline u16 checksum_be16(u8 *data, u32 size)
{
u16 checksum = 0;
u32 i;
for (i = 0; i < size; i += 2)
checksum += be16_to_cpup((__be16 *)(data + i));
return checksum;
}
static inline u32 checksum_le32(u8 *data, u32 size)
{
u32 checksum = 0;
u32 i;
for (i = 0; i < size; i += 4)
checksum += le32_to_cpup((__le32 *)(data + i));
return checksum;
}
static inline u32 checksum_be32(u8 *data, u32 size)
{
u32 checksum = 0;
u32 i;
for (i = 0; i < size; i += 4)
checksum += be32_to_cpup((__be32 *)(data + i));
return checksum;
}
/**
* define event action
* EVT_xxx macros are used in opeartions callback
* defined in @goodix_ext_module_funcs to control
* the behaviors of event such as suspend/resume/irq_event.
* generally there are two types of behaviors:
* 1. you want the flow of this event be canceled,
* in this condition, you should return EVT_CANCEL_XXX in
* the operations callback.
* e.g. the firmware update module is updating
* the firmware, you want to cancel suspend flow,
* so you need to return EVT_CANCEL_SUSPEND in
* suspend callback function.
* 2. you want the flow of this event continue, in
* this condition, you should return EVT_HANDLED in
* the callback function.
*/
#define EVT_HANDLED 0
#define EVT_CONTINUE 0
#define EVT_CANCEL 1
#define EVT_CANCEL_IRQEVT 1
#define EVT_CANCEL_SUSPEND 1
#define EVT_CANCEL_RESUME 1
#define EVT_CANCEL_RESET 1
/*
* errno define
* Note:
* 1. bus read/write functions defined in hardware
* layer code(e.g. goodix_xxx_i2c.c) *must* return
* -EBUS if failed to transfer data on bus.
*/
#define EBUS 1000
#define ETIMEOUT 1001
#define ECHKSUM 1002
#define EMEMCMP 1003
#if 0
#define CONFIG_GOODIX_DEBUG
#endif
/* log macro */
#define ts_info(fmt, arg...)\
pr_info("[GT9886-INF][%s:%d] "fmt"\n", __func__, __LINE__, ##arg)
#define ts_err(fmt, arg...)\
pr_info("[GT9886-ERR][%s:%d] "fmt"\n", __func__, __LINE__, ##arg)
#define boot_log(fmt, arg...) g_info(fmt, ##arg)
#ifdef CONFIG_GOODIX_DEBUG
#define ts_debug(fmt, arg...)\
pr_info("[GT9886-DBG][%s:%d] "fmt"\n", __func__, __LINE__, ##arg)
#else
#define ts_debug(fmt, arg...) do {} while (0)
#endif
/**
* goodix_register_ext_module - interface for external module
* to register into touch core modules structure
*
* @module: pointer to external module to be register
* return: 0 ok, <0 failed
*/
int goodix_register_ext_module(struct goodix_ext_module *module);
/**
* goodix_unregister_ext_module - interface for external module
* to unregister external modules
*
* @module: pointer to external module
* return: 0 ok, <0 failed
*/
int goodix_unregister_ext_module(struct goodix_ext_module *module);
/**
* goodix_ts_irq_enable - Enable/Disable a irq
* @core_data: pointer to touch core data
* enable: enable or disable irq
* return: 0 ok, <0 failed
*/
int goodix_ts_irq_enable(struct goodix_ts_core *core_data, bool enable);
struct kobj_type *goodix_get_default_ktype(void);
/**
* fb_notifier_call_chain - notify clients of fb_events
* see enum ts_notify_event in goodix_ts_core.h
*/
int goodix_ts_blocking_notify(enum ts_notify_event evt, void *v);
/**
* goodix_ts_power_on - Turn on power to the touch device
* @core_data: pointer to touch core data
* return: 0 ok, <0 failed
*/
int goodix_ts_power_on(struct goodix_ts_core *core_data);
/**
* goodix_ts_power_off - Turn off power to the touch device
* @core_data: pointer to touch core data
* return: 0 ok, <0 failed
*/
int goodix_ts_power_off(struct goodix_ts_core *core_data);
int goodix_ts_hw_init(struct goodix_ts_core *core_data);
int goodix_ts_input_dev_config(struct goodix_ts_core *core_data);
int goodix_ts_irq_setup(struct goodix_ts_core *core_data);
int goodix_ts_sysfs_init(struct goodix_ts_core *core_data);
int goodix_ts_esd_init(struct goodix_ts_core *core);
int goodix_ts_register_notifier(struct notifier_block *nb);
int goodix_generic_noti_callback(struct notifier_block *self,
unsigned long action, void *data);
int goodix_ts_fb_notifier_callback(struct notifier_block *self,
unsigned long event, void *data);
int goodix_ts_irq_enable(struct goodix_ts_core *core_data,
bool enable);
extern void goodix_msg_printf(const char *fmt, ...);
extern int i2c_touch_resume(void);
extern int i2c_touch_suspend(void);
extern int goodix_start_cfg_bin(struct goodix_ts_core *ts_core);
int gt9886_touch_filter_register(void);
int goodix_ts_core_init(void);
#ifdef CONFIG_TRUSTONIC_TRUSTED_UI
extern atomic_t gt9886_tui_flag;
extern struct goodix_ts_core *resume_core_data;
#endif
#endif
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,619 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/atomic.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/ioctl.h>
#include <linux/wait.h>
#include "goodix_ts_core.h"
#define GOODIX_TOOLS_NAME "gtp_tools"
#define GOODIX_TS_IOC_MAGIC 'G'
#define NEGLECT_SIZE_MASK (~(_IOC_SIZEMASK << _IOC_SIZESHIFT))
#define GTP_IRQ_ENABLE _IO(GOODIX_TS_IOC_MAGIC, 0)
#define GTP_DEV_RESET _IO(GOODIX_TS_IOC_MAGIC, 1)
#define GTP_SEND_COMMAND (_IOW(GOODIX_TS_IOC_MAGIC, 2, u8) & NEGLECT_SIZE_MASK)
#define GTP_SEND_CONFIG (_IOW(GOODIX_TS_IOC_MAGIC, 3, u8) & NEGLECT_SIZE_MASK)
#define GTP_ASYNC_READ (_IOR(GOODIX_TS_IOC_MAGIC, 4, u8) & NEGLECT_SIZE_MASK)
#define GTP_SYNC_READ (_IOR(GOODIX_TS_IOC_MAGIC, 5, u8) & NEGLECT_SIZE_MASK)
#define GTP_ASYNC_WRITE (_IOW(GOODIX_TS_IOC_MAGIC, 6, u8) & NEGLECT_SIZE_MASK)
#define GTP_READ_CONFIG (_IOW(GOODIX_TS_IOC_MAGIC, 7, u8) & NEGLECT_SIZE_MASK)
#define GTP_ESD_ENABLE _IO(GOODIX_TS_IOC_MAGIC, 8)
#define GTP_DRV_VERSION (_IOR(GOODIX_TS_IOC_MAGIC, 9, u8) & NEGLECT_SIZE_MASK)
#define GOODIX_TS_IOC_MAXNR 10
#define IRQ_FALG (0x01 << 2)
#define I2C_MSG_HEAD_LEN 20
#define TS_REG_COORDS_BASE 0x4100
/*
* struct goodix_tools_data - goodix tools data message used in sync read
* @data: The buffer into which data is written
* @reg_addr: Slave device register start address to start read data
* @length: Number of data bytes in @data being read from slave device
* @filled: When buffer @data be filled will set this flag with 1, outhrwise 0
* @list_head:Eonnet every goodix_tools_data struct into a list
*/
struct goodix_tools_data {
u32 reg_addr;
u32 length;
u8 *data;
bool filled;
struct list_head list;
};
/*
* struct goodix_tools_dev - goodix tools device struct
* @ts_core: The core data struct of ts driver
* @ops_mode: represent device work mode
* @rawdiffcmd: Set slave device into rawdata mode
* @normalcmd: Set slave device into normal mode
* @wq: Wait queue struct use in synchronous data read
* @mutex: Protect goodix_tools_dev
*/
struct goodix_tools_dev {
struct goodix_ts_core *ts_core;
struct list_head head;
unsigned int ops_mode;
struct goodix_ts_cmd rawdiffcmd, normalcmd;
wait_queue_head_t wq;
struct mutex mutex;
atomic_t t_count;
struct goodix_ext_module module;
} *goodix_tools_dev;
/* read data from i2c asynchronous,
* success return bytes read, else return <= 0
*/
static int async_read(struct goodix_tools_dev *dev, void __user *arg)
{
u8 *databuf = NULL;
int ret = 0;
u32 reg_addr, length;
u8 i2c_msg_head[I2C_MSG_HEAD_LEN];
struct goodix_ts_device *ts_dev = dev->ts_core->ts_dev;
const struct goodix_ts_hw_ops *hw_ops = ts_dev->hw_ops;
ret = copy_from_user(&i2c_msg_head, arg, I2C_MSG_HEAD_LEN);
if (ret) {
ret = -EFAULT;
goto err_out;
}
reg_addr = i2c_msg_head[0] + (i2c_msg_head[1] << 8)
+ (i2c_msg_head[2] << 16) + (i2c_msg_head[3] << 24);
length = i2c_msg_head[4] + (i2c_msg_head[5] << 8)
+ (i2c_msg_head[6] << 16) + (i2c_msg_head[7] << 24);
if (length > GOODIX_CFG_MAX_SIZE)
return -EMSGSIZE;
databuf = kzalloc(length, GFP_KERNEL);
if (!databuf) {
ts_err("Alloc memory failed");
return -ENOMEM;
}
if (!hw_ops->read_trans(ts_dev, reg_addr, databuf, length)) {
if (copy_to_user((u8 *)arg + I2C_MSG_HEAD_LEN,
databuf, length)) {
ret = -EFAULT;
ts_err("Copy_to_user failed");
} else {
ret = length;
}
} else {
ret = -EBUSY;
ts_err("Read i2c failed");
}
err_out:
kfree(databuf);
return ret;
}
/* if success return config data length */
static int read_config_data(struct goodix_ts_device *ts_dev, void __user *arg)
{
int ret = 0;
u32 reg_addr, length;
u8 i2c_msg_head[I2C_MSG_HEAD_LEN];
u8 *tmp_buf;
ret = copy_from_user(&i2c_msg_head, arg, I2C_MSG_HEAD_LEN);
if (ret) {
ts_err("Copy data from user failed");
return -EFAULT;
}
reg_addr = i2c_msg_head[0] + (i2c_msg_head[1] << 8)
+ (i2c_msg_head[2] << 16) + (i2c_msg_head[3] << 24);
length = i2c_msg_head[4] + (i2c_msg_head[5] << 8)
+ (i2c_msg_head[6] << 16) + (i2c_msg_head[7] << 24);
ts_info("read config,reg_addr=0x%x, length=%d", reg_addr, length);
if (length > GOODIX_CFG_MAX_SIZE)
return -EMSGSIZE;
tmp_buf = kzalloc(length, GFP_KERNEL);
if (!tmp_buf) {
ts_err("failed alloc memory");
return -ENOMEM;
}
/* if reg_addr == 0, read config data with specific flow */
if (!reg_addr) {
if (ts_dev->hw_ops->read_config)
ret = ts_dev->hw_ops->read_config(ts_dev, tmp_buf, 0);
else
ret = -EINVAL;
} else {
ret = ts_dev->hw_ops->read_trans(ts_dev,
reg_addr, tmp_buf, length);
if (!ret)
ret = length;
}
if (ret <= 0)
goto err_out;
if (copy_to_user((u8 *)arg + I2C_MSG_HEAD_LEN, tmp_buf, ret)) {
ret = -EFAULT;
ts_err("Copy_to_user failed");
}
err_out:
kfree(tmp_buf);
return ret;
}
/* read data from i2c synchronous,
* success return bytes read, else return <= 0
*/
static int sync_read(struct goodix_tools_dev *dev, void __user *arg)
{
int ret = 0;
u8 i2c_msg_head[I2C_MSG_HEAD_LEN];
struct goodix_tools_data tools_data;
ret = copy_from_user(&i2c_msg_head, arg, I2C_MSG_HEAD_LEN);
if (ret) {
ts_err("Copy data from user failed");
return -EFAULT;
}
tools_data.reg_addr = i2c_msg_head[0] + (i2c_msg_head[1] << 8)
+ (i2c_msg_head[2] << 16) + (i2c_msg_head[3] << 24);
tools_data.length = i2c_msg_head[4] + (i2c_msg_head[5] << 8)
+ (i2c_msg_head[6] << 16) + (i2c_msg_head[7] << 24);
tools_data.filled = 0;
if (tools_data.length > GOODIX_CFG_MAX_SIZE)
return -EMSGSIZE;
tools_data.data = kzalloc(tools_data.length, GFP_KERNEL);
if (!tools_data.data) {
ts_err("Alloc memory failed");
return -ENOMEM;
}
mutex_lock(&dev->mutex);
list_add_tail(&tools_data.list, &dev->head);
mutex_unlock(&dev->mutex);
/* wait queue will timeout after 1 seconds */
if (!wait_event_interruptible_timeout(dev->wq,
tools_data.filled == 1, HZ * 3)) {
ret = -EAGAIN;
ts_err("Wait queue timeout");
goto err_out;
}
mutex_lock(&dev->mutex);
list_del(&tools_data.list);
mutex_unlock(&dev->mutex);
if (tools_data.filled == 1) {
if (copy_to_user((u8 *)arg + I2C_MSG_HEAD_LEN, tools_data.data,
tools_data.length)) {
ret = -EFAULT;
ts_err("Copy_to_user failed");
} else {
ret = tools_data.length;
}
} else {
ret = -EAGAIN;
ts_err("Wait queue timeout");
}
err_out:
kfree(tools_data.data);
return ret;
}
/* write data to i2c asynchronous,
* success return bytes write, else return <= 0
*/
static int async_write(struct goodix_tools_dev *dev, void __user *arg)
{
u8 *databuf;
int ret = 0;
u32 reg_addr, length;
u8 i2c_msg_head[I2C_MSG_HEAD_LEN];
struct goodix_ts_device *ts_dev = dev->ts_core->ts_dev;
const struct goodix_ts_hw_ops *hw_ops = ts_dev->hw_ops;
ret = copy_from_user(&i2c_msg_head, arg, I2C_MSG_HEAD_LEN);
if (ret) {
ts_err("Copy data from user failed");
return -EFAULT;
}
reg_addr = i2c_msg_head[0] + (i2c_msg_head[1] << 8)
+ (i2c_msg_head[2] << 16) + (i2c_msg_head[3] << 24);
length = i2c_msg_head[4] + (i2c_msg_head[5] << 8)
+ (i2c_msg_head[6] << 16) + (i2c_msg_head[7] << 24);
if (length > GOODIX_CFG_MAX_SIZE)
return -EMSGSIZE;
databuf = kzalloc(length, GFP_KERNEL);
if (!databuf) {
ts_err("Alloc memory failed");
return -ENOMEM;
}
ret = copy_from_user(databuf, (u8 *)arg + I2C_MSG_HEAD_LEN, length);
if (ret) {
ret = -EFAULT;
ts_err("Copy data from user failed");
goto err_out;
}
if (hw_ops->write_trans(ts_dev, reg_addr, databuf, length)) {
ret = -EBUSY;
ts_err("Write data to device failed");
} else {
ret = length;
}
err_out:
kfree(databuf);
return ret;
}
static int init_cfg_data(struct goodix_ts_config *cfg, void __user *arg)
{
int ret = 0;
u32 reg_addr, length;
u8 i2c_msg_head[I2C_MSG_HEAD_LEN];
cfg->initialized = 0;
mutex_init(&cfg->lock);
ret = copy_from_user(&i2c_msg_head, arg, I2C_MSG_HEAD_LEN);
if (ret) {
ts_err("Copy data from user failed");
return -EFAULT;
}
reg_addr = i2c_msg_head[0] + (i2c_msg_head[1] << 8)
+ (i2c_msg_head[2] << 16) + (i2c_msg_head[3] << 24);
length = i2c_msg_head[4] + (i2c_msg_head[5] << 8)
+ (i2c_msg_head[6] << 16) + (i2c_msg_head[7] << 24);
if (length > GOODIX_CFG_MAX_SIZE)
return -EMSGSIZE;
ret = copy_from_user(cfg->data, (u8 *)arg + I2C_MSG_HEAD_LEN, length);
if (ret) {
ret = -EFAULT;
ts_err("Copy data from user failed");
goto err_out;
}
cfg->reg_base = reg_addr;
cfg->length = length;
strlcpy(cfg->name, "tools-send-cfg", sizeof(cfg->name));
cfg->delay = 50;
cfg->initialized = true;
return 0;
err_out:
return ret;
}
/**
* goodix_tools_ioctl - ioctl implementation
*
* @filp: Pointer to file opened
* @cmd: Ioctl opertion command
* @arg: Command data
* Returns >=0 - succeed, else failed
*/
static long goodix_tools_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
int ret = 0;
struct goodix_tools_dev *dev = filp->private_data;
struct goodix_ts_device *ts_dev;
const struct goodix_ts_hw_ops *hw_ops;
struct goodix_ts_cmd temp_cmd;
struct goodix_ts_config *temp_cfg = NULL;
if (dev->ts_core == NULL) {
ts_err("Tools module not register");
return -EINVAL;
}
ts_dev = dev->ts_core->ts_dev;
hw_ops = ts_dev->hw_ops;
if (_IOC_TYPE(cmd) != GOODIX_TS_IOC_MAGIC) {
ts_err("Bad magic num:%c", _IOC_TYPE(cmd));
return -ENOTTY;
}
if (_IOC_NR(cmd) > GOODIX_TS_IOC_MAXNR) {
ts_err("Bad cmd num:%d > %d",
_IOC_NR(cmd), GOODIX_TS_IOC_MAXNR);
return -ENOTTY;
}
switch (cmd & NEGLECT_SIZE_MASK) {
case GTP_IRQ_ENABLE:
if (arg == 1) {
goodix_ts_irq_enable(dev->ts_core, true);
mutex_lock(&dev->mutex);
dev->ops_mode |= IRQ_FALG;
mutex_unlock(&dev->mutex);
ts_info("IRQ enabled");
} else if (arg == 0) {
goodix_ts_irq_enable(dev->ts_core, false);
mutex_lock(&dev->mutex);
dev->ops_mode &= ~IRQ_FALG;
mutex_unlock(&dev->mutex);
ts_info("IRQ disabled");
} else {
ts_info("Irq aready set with, arg = %ld", arg);
}
ret = 0;
break;
case GTP_ESD_ENABLE:
if (arg == 0)
goodix_ts_blocking_notify(NOTIFY_ESD_OFF, NULL);
else
goodix_ts_blocking_notify(NOTIFY_ESD_ON, NULL);
break;
case GTP_DEV_RESET:
hw_ops->reset(ts_dev);
break;
case GTP_SEND_COMMAND:
ret = copy_from_user(&temp_cmd, (void __user *)arg,
sizeof(struct goodix_ts_cmd));
if (ret) {
ret = -EINVAL;
goto err_out;
}
ret = hw_ops->send_cmd(ts_dev, &temp_cmd);
if (ret) {
ts_err("Send command failed");
ret = -EAGAIN;
}
break;
case GTP_SEND_CONFIG:
temp_cfg = kzalloc(sizeof(struct goodix_ts_config), GFP_KERNEL);
if (temp_cfg == NULL) {
ts_err("Memory allco err");
ret = -ENOMEM;
goto err_out;
}
ret = init_cfg_data(temp_cfg, (void __user *)arg);
if (!ret && hw_ops->send_config) {
ret = hw_ops->send_config(ts_dev, temp_cfg);
if (ret) {
ts_err("Failed send config");
ret = -EAGAIN;
} else {
ts_info("Send config success");
ret = 0;
}
}
break;
case GTP_READ_CONFIG:
ret = read_config_data(ts_dev, (void __user *)arg);
if (ret > 0)
ts_info("success read config:len=%d", ret);
else
ts_err("failed read config:ret=0x%x", ret);
break;
case GTP_ASYNC_READ:
ret = async_read(dev, (void __user *)arg);
if (ret < 0)
ts_err("Async data read failed");
break;
case GTP_SYNC_READ:
if (filp->f_flags & O_NONBLOCK) {
ts_err("Goodix tools now worked in sync_bus mode");
ret = -EAGAIN;
goto err_out;
}
ret = sync_read(dev, (void __user *)arg);
if (ret < 0)
ts_err("Sync data read failed");
break;
case GTP_ASYNC_WRITE:
ret = async_write(dev, (void __user *)arg);
if (ret < 0)
ts_err("Async data write failed");
break;
case GTP_DRV_VERSION:
ret = copy_to_user((u8 *)arg, GOODIX_DRIVER_VERSION,
sizeof(GOODIX_DRIVER_VERSION));
if (ret)
ts_err("failed copy driver version info to user");
break;
default:
ts_info("Invalid cmd");
ret = -ENOTTY;
break;
}
err_out:
if (!temp_cfg) {
kfree(temp_cfg);
temp_cfg = NULL;
}
return ret;
}
#ifdef CONFIG_COMPAT
static long goodix_tools_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
void __user *arg32 = compat_ptr(arg);
if (!file->f_op || !file->f_op->unlocked_ioctl)
return -ENOTTY;
return file->f_op->unlocked_ioctl(file, cmd, (unsigned long)arg32);
}
#endif
static int goodix_tools_open(struct inode *inode, struct file *filp)
{
int ret = 0;
filp->private_data = goodix_tools_dev;
ts_info("tools open");
/* Only the first time open device need to register module */
ret = goodix_register_ext_module(&goodix_tools_dev->module);
if (ret)
ts_info("failed register to core module");
return ret;
}
static int goodix_tools_release(struct inode *inode, struct file *filp)
{
int ret = 0;
/* when the last close this dev node unregister the module */
ret = goodix_unregister_ext_module(&goodix_tools_dev->module);
return ret;
}
/**
* goodix_tools_module_irq - goodix tools Irq handle
* This functions is excuted when interrupt happended
*
* @core_data: pointer to touch core data
* @module: pointer to goodix_ext_module struct
* return: EVT_CONTINUE let other module handle this irq
*/
static int goodix_tools_module_irq(struct goodix_ts_core *core_data,
struct goodix_ext_module *module)
{
struct goodix_tools_dev *dev = module->priv_data;
struct goodix_ts_device *ts_dev = dev->ts_core->ts_dev;
const struct goodix_ts_hw_ops *hw_ops = ts_dev->hw_ops;
struct goodix_tools_data *tools_data;
int r = 0;
u8 evt_sta = 0;
mutex_lock(&dev->mutex);
if (!list_empty(&dev->head)) {
r = hw_ops->read_trans(ts_dev, ts_dev->reg.coor, &evt_sta, 1);
if (r < 0 || ((evt_sta & GOODIX_TOUCH_EVENT) == 0)) {
ts_err("data not ready:0x%x, read ret =%d", evt_sta, r);
mutex_unlock(&dev->mutex);
return EVT_CONTINUE;
}
list_for_each_entry(tools_data, &dev->head, list) {
if (!hw_ops->read_trans(ts_dev, tools_data->reg_addr,
tools_data->data, tools_data->length)) {
tools_data->filled = 1;
}
}
wake_up(&dev->wq);
}
mutex_unlock(&dev->mutex);
return EVT_CONTINUE;
}
static int goodix_tools_module_init(struct goodix_ts_core *core_data,
struct goodix_ext_module *module)
{
struct goodix_tools_dev *tools_dev = module->priv_data;
if (core_data)
tools_dev->ts_core = core_data;
else
return -ENODEV;
return 0;
}
static const struct file_operations goodix_tools_fops = {
.owner = THIS_MODULE,
.open = goodix_tools_open,
.release = goodix_tools_release,
.unlocked_ioctl = goodix_tools_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = goodix_tools_compat_ioctl,
#endif
};
static struct miscdevice goodix_tools_miscdev = {
.minor = MISC_DYNAMIC_MINOR,
.name = GOODIX_TOOLS_NAME,
.fops = &goodix_tools_fops,
};
static struct goodix_ext_module_funcs goodix_tools_module_funcs = {
.irq_event = goodix_tools_module_irq,
.init = goodix_tools_module_init,
};
/**
* goodix_tools_init - init goodix tools device and register a miscdevice
*
* return: 0 success, else failed
*/
static int __init goodix_tools_init(void)
{
int ret;
goodix_tools_dev = kzalloc(sizeof(struct goodix_tools_dev), GFP_KERNEL);
if (goodix_tools_dev == NULL) {
ts_err("Memory allco err");
return -ENOMEM;
}
INIT_LIST_HEAD(&goodix_tools_dev->head);
goodix_tools_dev->ops_mode = 0;
goodix_tools_dev->ops_mode |= IRQ_FALG;
init_waitqueue_head(&goodix_tools_dev->wq);
mutex_init(&goodix_tools_dev->mutex);
atomic_set(&goodix_tools_dev->t_count, 0);
goodix_tools_dev->module.funcs = &goodix_tools_module_funcs;
goodix_tools_dev->module.name = GOODIX_TOOLS_NAME;
goodix_tools_dev->module.priv_data = goodix_tools_dev;
goodix_tools_dev->module.priority = EXTMOD_PRIO_DBGTOOL;
ret = misc_register(&goodix_tools_miscdev);
if (ret)
ts_err("Debug tools miscdev register failed");
return ret;
}
static void __exit goodix_tools_exit(void)
{
misc_deregister(&goodix_tools_miscdev);
kfree(goodix_tools_dev);
ts_info("Goodix tools miscdev exit");
}
module_init(goodix_tools_init);
module_exit(goodix_tools_exit);
MODULE_DESCRIPTION("Goodix tools Module");
MODULE_AUTHOR("Goodix, Inc.");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,37 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#ifdef CONFIG_TRUSTONIC_TRUSTED_UI
#include "goodix_ts_core.h"
atomic_t gt9886_tui_flag = ATOMIC_INIT(0);
EXPORT_SYMBOL(gt9886_tui_flag);
int tpd_enter_tui(void)
{
int ret = 0;
ts_info("[%s] enter TUI", __func__);
atomic_set(&gt9886_tui_flag, true);
return ret;
}
int tpd_exit_tui(void)
{
int ret = 0;
ts_info("[%s] exit TUI", __func__);
atomic_set(&gt9886_tui_flag, false);
goodix_ts_irq_enable(resume_core_data, false);
goodix_ts_irq_enable(resume_core_data, true);
return ret;
}
#endif
@@ -0,0 +1,9 @@
# Goodix Touchscreen Makefile
# This Makefile is only for tempory compiling use
# Use xxxproject/Makefile-release when release driver
#
obj-y += goodix_ts_spi.o
obj-y += goodix_ts_core.o
obj-y += goodix_cfg_bin.o
obj-y += goodix_gtx8_update.o
obj-n += goodix_ts_tools.o
@@ -0,0 +1,585 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2016 MediaTek Inc.
*/
#include "goodix_cfg_bin.h"
int gt9896s_parse_cfg_bin(struct gt9896s_cfg_bin *cfg_bin)
{
u8 checksum;
int i, r;
u16 offset1, offset2;
if (!cfg_bin->bin_data || cfg_bin->bin_data_len == 0) {
ts_err("NO cfg_bin data, cfg_bin data length:%d",
cfg_bin->bin_data_len);
r = -EINVAL;
goto exit;
}
/* copy cfg_bin head info */
if (cfg_bin->bin_data_len < sizeof(struct gt9896s_cfg_bin_head)) {
ts_err("Invalid cfg_bin size:%d", cfg_bin->bin_data_len);
r = -EINVAL;
goto exit;
}
memcpy(&cfg_bin->head, cfg_bin->bin_data,
sizeof(struct gt9896s_cfg_bin_head));
cfg_bin->head.bin_len = le32_to_cpu(cfg_bin->head.bin_len);
/*check length*/
if (cfg_bin->bin_data_len != cfg_bin->head.bin_len) {
ts_err("cfg_bin len check failed,%d != %d",
cfg_bin->head.bin_len, cfg_bin->bin_data_len);
r = -EINVAL;
goto exit;
}
/*check cfg_bin valid*/
checksum = 0;
for (i = TS_BIN_VERSION_START_INDEX; i < cfg_bin->bin_data_len; i++) {
checksum += cfg_bin->bin_data[i];
}
if (checksum != cfg_bin->head.checksum) {
ts_err("cfg_bin checksum check filed 0x%02x != 0x%02x",
cfg_bin->head.checksum, checksum);
r = -EINVAL;
goto exit;
}
/*allocate memory for cfg packages*/
cfg_bin->cfg_pkgs = kzalloc(sizeof(struct gt9896s_cfg_package) *
cfg_bin->head.pkg_num, GFP_KERNEL);
if (!cfg_bin->cfg_pkgs) {
ts_err("cfg_pkgs, allocate memory ERROR");
r = -ENOMEM;
goto exit;
}
/*get cfg_pkg's info*/
for (i = 0; i < cfg_bin->head.pkg_num; i++) {
/*get cfg pkg length*/
if (i == cfg_bin->head.pkg_num - 1) {
offset1 = cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN + i * TS_CFG_OFFSET_LEN] +
(cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN + i * TS_CFG_OFFSET_LEN + 1] << 8);
cfg_bin->cfg_pkgs[i].pkg_len = cfg_bin->bin_data_len - offset1;
} else {
offset1 = cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN + i * TS_CFG_OFFSET_LEN] +
(cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN + i * TS_CFG_OFFSET_LEN + 1] << 8);
offset2 = cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN + i * TS_CFG_OFFSET_LEN + 2] +
(cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN + i * TS_CFG_OFFSET_LEN + 3] << 8);
if (offset2 <= offset1) {
ts_err("offset error,pkg:%d, offset1:%d, offset2:%d", i, offset1, offset2);
r = -EINVAL;
goto exit;
}
cfg_bin->cfg_pkgs[i].pkg_len = offset2 - offset1;
}
/*get cfg pkg head*/
memcpy(&cfg_bin->cfg_pkgs[i].cnst_info,
&cfg_bin->bin_data[offset1], TS_PKG_CONST_INFO_LEN);
memcpy(&cfg_bin->cfg_pkgs[i].reg_info,
&cfg_bin->bin_data[offset1 + TS_PKG_CONST_INFO_LEN],
TS_PKG_REG_INFO_LEN);
/*compatible little edition and big edition*/
gt9896s_cfg_pkg_leToCpu(&cfg_bin->cfg_pkgs[i]);
/*get configuration data*/
cfg_bin->cfg_pkgs[i].cfg = &cfg_bin->bin_data[offset1 + TS_PKG_HEAD_LEN];
}
/*debug, print pkg information*/
ts_info("Driver bin info: ver %s, len %d, pkgs %d", cfg_bin->head.bin_version,
cfg_bin->head.bin_len, cfg_bin->head.pkg_num);
for (i = 0; i < cfg_bin->head.pkg_num; i++) {
ts_debug("---------------------------------------------");
ts_debug("------package:%d------", i + 1);
ts_debug("package len:%04x", cfg_bin->cfg_pkgs[i].cnst_info.pkg_len);
ts_debug("package ic_type:%s", cfg_bin->cfg_pkgs[i].cnst_info.ic_type);
ts_debug("package cfg_type:%01x", cfg_bin->cfg_pkgs[i].cnst_info.cfg_type);
ts_debug("package sensor_id:%01x", cfg_bin->cfg_pkgs[i].cnst_info.sensor_id);
ts_debug("package hw_pid:%s", cfg_bin->cfg_pkgs[i].cnst_info.hw_pid);
ts_debug("package hw_vid:%s", cfg_bin->cfg_pkgs[i].cnst_info.hw_vid);
ts_debug("package fw_mask_version:%s", cfg_bin->cfg_pkgs[i].cnst_info.fw_mask);
ts_debug("package fw_patch_version:%s", cfg_bin->cfg_pkgs[i].cnst_info.fw_patch);
ts_debug("package x_res_offset:%02x", cfg_bin->cfg_pkgs[i].cnst_info.x_res_offset);
ts_debug("package y_res_offset:%02x", cfg_bin->cfg_pkgs[i].cnst_info.y_res_offset);
ts_debug("package trigger_offset:%02x", cfg_bin->cfg_pkgs[i].cnst_info.trigger_offset);
ts_debug("reg info");
ts_debug("send_cfg_flag reg:%02x", cfg_bin->cfg_pkgs[i].reg_info.cfg_send_flag.addr);
ts_debug("version base reg:%02x, len:%d",
cfg_bin->cfg_pkgs[i].reg_info.version_base.addr,
cfg_bin->cfg_pkgs[i].reg_info.version_base.reserved1);
ts_debug("pid reg:%02x:%d", cfg_bin->cfg_pkgs[i].reg_info.pid.addr,
cfg_bin->cfg_pkgs[i].reg_info.pid.reserved1);
ts_debug("vid reg:%02x:%d", cfg_bin->cfg_pkgs[i].reg_info.vid.addr,
cfg_bin->cfg_pkgs[i].reg_info.vid.reserved1);
ts_debug("sensor_id reg:%02x,mask:%x", cfg_bin->cfg_pkgs[i].reg_info.sensor_id.addr,
cfg_bin->cfg_pkgs[i].reg_info.sensor_id.addr);
ts_debug("fw_status reg:%02x", cfg_bin->cfg_pkgs[i].reg_info.fw_status.addr);
ts_debug("cfg_addr reg:%02x", cfg_bin->cfg_pkgs[i].reg_info.cfg_addr.addr);
ts_debug("esd reg:%02x", cfg_bin->cfg_pkgs[i].reg_info.esd.addr);
ts_debug("command reg:%02x", cfg_bin->cfg_pkgs[i].reg_info.command.addr);
ts_debug("coor:%02x", cfg_bin->cfg_pkgs[i].reg_info.coor.addr);
ts_debug("gesture:%02x", cfg_bin->cfg_pkgs[i].reg_info.gesture.addr);
ts_debug("fw_request:%02x", cfg_bin->cfg_pkgs[i].reg_info.fw_request.addr);
ts_debug("proximity:%02x", cfg_bin->cfg_pkgs[i].reg_info.proximity.addr);
ts_debug("--------------------------------------------");
}
r = 0;
exit:
return r;
}
static int gt9896s_cfg_bin_proc(struct gt9896s_ts_core *core_data)
{
int r;
struct gt9896s_ts_device *ts_dev = core_data->ts_dev;
struct gt9896s_cfg_bin *cfg_bin = kzalloc(sizeof(struct gt9896s_cfg_bin),
GFP_KERNEL);
if (!cfg_bin) {
return -ENOMEM;
}
if (!ts_dev) {
ts_err("ts device can't be null");
goto out;
}
/* when start init config bin with error state */
ts_dev->cfg_bin_state = CFG_BIN_STATE_ERROR;
/*get cfg_bin from file system*/
r = gt9896s_read_cfg_bin(ts_dev, cfg_bin);
if (r < 0) {
ts_err("Failed get valid config bin data");
goto exit;
}
/*parse cfg bin*/
r = gt9896s_parse_cfg_bin(cfg_bin);
if (!r) {
ts_info("parse cfg bin SUCCESS");
} else {
ts_err("parse cfg bin FAILED");
goto exit;
}
/*get register address and configuration from cfg bin*/
r = gt9896s_get_reg_and_cfg(ts_dev, cfg_bin);
if (!r) {
ts_info("success get reg and cfg info from cfg bin");
} else {
ts_err("failed get cfg and reg info, update fw then retry");
}
/*debug*/
ts_info("cfg_send_flag:0x%04x", ts_dev->reg.cfg_send_flag);
ts_info("pid:0x%04x", ts_dev->reg.pid);
ts_info("vid:0x%04x", ts_dev->reg.vid);
ts_info("sensor_id:0x%04x", ts_dev->reg.sensor_id);
ts_info("fw_mask:0x%04x", ts_dev->reg.fw_mask);
ts_info("fw_status:0x%04x", ts_dev->reg.fw_status);
ts_info("cfg_addr:0x%04x", ts_dev->reg.cfg_addr);
ts_info("esd:0x%04x", ts_dev->reg.esd);
ts_info("command:0x%04x", ts_dev->reg.command);
ts_info("coor:0x%04x", ts_dev->reg.coor);
ts_info("gesture:0x%04x", ts_dev->reg.gesture);
ts_info("fw_request:0x%04x", ts_dev->reg.fw_request);
ts_info("proximity:0x%04x", ts_dev->reg.proximity);
exit:
kfree(cfg_bin->cfg_pkgs);
kfree(cfg_bin->bin_data);
kfree(cfg_bin);
if (r)
gt9896s_ts_blocking_notify(NOTIFY_CFG_BIN_FAILED, &r);
else
gt9896s_ts_blocking_notify(NOTIFY_CFG_BIN_SUCCESS, &r);
ts_info("cfg bin state %d, ret %d", ts_dev->cfg_bin_state, r);
return r;
out:
kfree(cfg_bin);
return -EINVAL;
}
static int gt9896s_extract_cfg_pkg(struct gt9896s_ts_device *ts_dev,
struct gt9896s_cfg_package *cfg_pkg)
{
struct gt9896s_ts_config *ts_cfg;
if (cfg_pkg->cnst_info.cfg_type == TS_NORMAL_CFG) {
ts_cfg = &(ts_dev->normal_cfg);
} else if (cfg_pkg->cnst_info.cfg_type == TS_HIGH_SENSE_CFG) {
ts_cfg = &(ts_dev->highsense_cfg);
} else {
ts_err("unknown cfg type %d", cfg_pkg->cnst_info.cfg_type);
return -EINVAL;
}
ts_cfg->length = cfg_pkg->pkg_len -
TS_PKG_CONST_INFO_LEN - TS_PKG_REG_INFO_LEN;
if (ts_cfg->length > sizeof(ts_cfg->data)) {
ts_err("illegal cfg length %d", ts_cfg->length);
return -EINVAL;
}
if (ts_cfg->length) {
ts_info("get config type %d, len %d",
cfg_pkg->cnst_info.cfg_type, ts_cfg->length);
memcpy(ts_cfg->data, cfg_pkg->cfg, ts_cfg->length);
ts_cfg->initialized = TS_CFG_STABLE;
mutex_init(&ts_cfg->lock);
} else {
ts_info("no config data");
}
/*get register info*/
ts_dev->reg.cfg_send_flag = cfg_pkg->reg_info.cfg_send_flag.addr;
ts_dev->reg.version_base = cfg_pkg->reg_info.version_base.addr;
ts_dev->reg.version_len = cfg_pkg->reg_info.version_base.reserved1;
ts_dev->reg.pid = cfg_pkg->reg_info.pid.addr;
ts_dev->reg.pid_len = cfg_pkg->reg_info.pid.reserved1;
ts_dev->reg.vid = cfg_pkg->reg_info.vid.addr;
ts_dev->reg.vid_len = cfg_pkg->reg_info.vid.reserved1;
ts_dev->reg.sensor_id = cfg_pkg->reg_info.sensor_id.addr;
ts_dev->reg.sensor_id_mask = cfg_pkg->reg_info.sensor_id.reserved1;
ts_dev->reg.fw_mask = cfg_pkg->reg_info.fw_mask.addr;
ts_dev->reg.fw_status = cfg_pkg->reg_info.fw_status.addr;
ts_dev->reg.cfg_addr = cfg_pkg->reg_info.cfg_addr.addr;
ts_dev->reg.esd = cfg_pkg->reg_info.esd.addr;
ts_dev->reg.command = cfg_pkg->reg_info.command.addr;
ts_dev->reg.coor = cfg_pkg->reg_info.coor.addr;
ts_dev->reg.gesture = cfg_pkg->reg_info.gesture.addr;
ts_dev->reg.fw_request = cfg_pkg->reg_info.fw_request.addr;
ts_dev->reg.proximity = cfg_pkg->reg_info.proximity.addr;
return 0;
}
int gt9896s_get_reg_and_cfg(struct gt9896s_ts_device *ts_dev,
struct gt9896s_cfg_bin *cfg_bin)
{
int i;
u16 addr;
u8 read_len;
char temp_sensor_id = -1;
u8 temp_fw_mask[TS_CFG_BLOCK_FW_MASK_LEN] = {0x00};
u8 temp_pid[TS_CFG_BLOCK_PID_LEN] = {0x00};
int r = -EINVAL;
if (!cfg_bin->head.pkg_num || !cfg_bin->cfg_pkgs) {
ts_err("there is none cfg package, pkg_num:%d",
cfg_bin->head.pkg_num);
return -EINVAL;
}
memset(&ts_dev->normal_cfg, 0, sizeof(ts_dev->normal_cfg));
memset(&ts_dev->highsense_cfg, 0, sizeof(ts_dev->highsense_cfg));
/* find suitable cfg packages */
for (i = 0; i < cfg_bin->head.pkg_num; i++) {
/*get ic type*/
if (!strncmp(cfg_bin->cfg_pkgs[i].cnst_info.ic_type,
"normandy", strlen("normandy"))) {
ts_dev->ic_type = IC_TYPE_NORMANDY;
} else if (!strncmp(cfg_bin->cfg_pkgs[i].cnst_info.ic_type,
"yellowstone_s", strlen("yellowstone_s"))) {
ts_dev->ic_type = IC_TYPE_YELLOWSTONE_SPI;
} else if (!strncmp(cfg_bin->cfg_pkgs[i].cnst_info.ic_type,
"yellowstone", strlen("yellowstone"))) {
ts_dev->ic_type = IC_TYPE_YELLOWSTONE;
} else {
ts_err("unknow ic type of cfg_bin:%s",
cfg_bin->cfg_pkgs[i].cnst_info.ic_type);
continue;
}
ts_info("ic_type:%d", ts_dev->ic_type);
/* contrast sensor id */
addr = cfg_bin->cfg_pkgs[i].reg_info.sensor_id.addr;
if (!addr) {
ts_info("pkg:%d, sensor_id reg is NULL", i);
continue;
}
r = ts_dev->hw_ops->read(ts_dev, addr, &temp_sensor_id, 1);
if (r < 0) {
ts_err("failed get sensor of pkg:%d, reg:0x%02x", i, addr);
goto get_default_pkg;
}
ts_info("sensor id is %d", temp_sensor_id);
/*sensor.reserved1 is a mask, if it's not ZERO, use it*/
if (cfg_bin->cfg_pkgs[i].reg_info.sensor_id.reserved1 != 0)
temp_sensor_id &= cfg_bin->cfg_pkgs[i].reg_info.sensor_id.reserved1;
if (temp_sensor_id != cfg_bin->cfg_pkgs[i].cnst_info.sensor_id) {
ts_err("pkg:%d, sensor id contrast FAILED, reg:0x%02x",
i, addr);
ts_err("sensor_id from i2c:%d, sensor_id of cfg bin:%d",
temp_sensor_id,
cfg_bin->cfg_pkgs[i].cnst_info.sensor_id);
continue;
}
/*contrast fw_mask, if this reg is null, skip this step*/
addr = cfg_bin->cfg_pkgs[i].reg_info.fw_mask.addr;
if (addr && cfg_bin->cfg_pkgs[i].cnst_info.fw_mask[0]) {
r = ts_dev->hw_ops->read(ts_dev, addr, temp_fw_mask,
sizeof(temp_fw_mask));
if (r < 0) {
ts_err("failed read fw_mask pkg:%d, reg:0x%02x",
i, addr);
goto get_default_pkg;
}
if (strncmp(temp_fw_mask, cfg_bin->cfg_pkgs[i].cnst_info.fw_mask,
sizeof(temp_fw_mask))) {
ts_err("pkg:%d, fw_mask contrast FAILED, reg:0x%02x,", i, addr);
ts_err("mask from i2c:%s, mask of cfg bin:%s",
temp_fw_mask,
cfg_bin->cfg_pkgs[i].cnst_info.fw_mask);
continue;
}
}
/*contrast pid*/
addr = cfg_bin->cfg_pkgs[i].reg_info.pid.addr;
read_len = cfg_bin->cfg_pkgs[i].reg_info.pid.reserved1;
if (!addr) {
ts_err("pkg:%d, pid reg is NULL", i);
continue;
}
if (read_len <= 0 || read_len > TS_CFG_BLOCK_PID_LEN) {
ts_err("pkg:%d, hw_pid length ERROR, len:%d",
i, read_len);
continue;
}
r = ts_dev->hw_ops->read(ts_dev, addr, temp_pid, read_len);
if (r < 0) {
ts_err("failed read pid pkg:%d, pid reg:0x%02x", i, addr);
goto get_default_pkg;
}
if (strncmp(temp_pid, cfg_bin->cfg_pkgs[i].cnst_info.hw_pid, read_len)) {
ts_err("pkg:%d, pid contrast FAILED, reg:0x%02x", i, addr);
ts_err("pid from spi:%s, pid of cfg bin:%s",
temp_pid, cfg_bin->cfg_pkgs[i].cnst_info.hw_pid);
continue;
}
ts_info("try get package info: ic type %s, cfg type %d",
cfg_bin->cfg_pkgs[i].cnst_info.ic_type,
cfg_bin->cfg_pkgs[i].cnst_info.cfg_type);
/* currently only support normal and high_sense config */
if (cfg_bin->cfg_pkgs[i].cnst_info.cfg_type == TS_NORMAL_CFG ||
cfg_bin->cfg_pkgs[i].cnst_info.cfg_type == TS_HIGH_SENSE_CFG) {
r = gt9896s_extract_cfg_pkg(ts_dev, &cfg_bin->cfg_pkgs[i]);
if (!r) {
ts_dev->cfg_bin_state = CFG_BIN_STATE_INITIALIZED;
ts_info("success parse cfg bin");
} else {
ts_err("failed parse cfg bin");
break;
}
}
}
get_default_pkg:
if (ts_dev->cfg_bin_state != CFG_BIN_STATE_INITIALIZED) {
ts_err("no valid normal cfg, use cfg_pkg 0 as default");
/* Foo code for recover dead IC.
* force set package 0 config type to normal config, this will
* config will use to recover IC.
*/
cfg_bin->cfg_pkgs[0].cnst_info.cfg_type = TS_NORMAL_CFG;
if (gt9896s_extract_cfg_pkg(ts_dev, &cfg_bin->cfg_pkgs[0])) {
ts_err("failed get valid config for IC recover");
ts_dev->cfg_bin_state = CFG_BIN_STATE_ERROR;
} else {
ts_dev->cfg_bin_state = CFG_BIN_STATE_TEMP;
ts_info("get temp config data");
}
r = -EINVAL;
}
return r;
}
int gt9896s_read_cfg_bin(struct gt9896s_ts_device *ts_dev, struct gt9896s_cfg_bin *cfg_bin)
{
const struct firmware *firmware = NULL;
struct gt9896s_ts_board_data *ts_bdata = &ts_dev->board_data;
char cfg_bin_name[64] = {0x00};
int i = 0, r;
/*get cfg_bin_name*/
if (ts_bdata->lcm_max_x == 1080 && ts_bdata->lcm_max_y == 2280) {
r = snprintf(cfg_bin_name, sizeof(cfg_bin_name), "%s%s_1080x2280.bin",
TS_DEFAULT_CFG_BIN, gt9896s_config_buf);
} else if (ts_bdata->lcm_max_x == 1080 && ts_bdata->lcm_max_y == 2300) {
r = snprintf(cfg_bin_name, sizeof(cfg_bin_name), "%s%s_1080x2300.bin",
TS_DEFAULT_CFG_BIN, gt9896s_config_buf);
} else {
r = snprintf(cfg_bin_name, sizeof(cfg_bin_name), "%s%s.bin",
TS_DEFAULT_CFG_BIN, gt9896s_config_buf);
}
if (r >= sizeof(cfg_bin_name)) {
ts_err("get cfg_bin name FAILED!!!");
goto exit;
}
ts_info("cfg_bin_name:%s", cfg_bin_name);
for (i = 0; i < TS_RQST_FW_RETRY_TIMES; i++) {
r = request_firmware(&firmware, cfg_bin_name, ts_dev->dev);
if (r < 0) {
ts_err("failed get cfg bin[%s] error:%d, try_times:%d",
cfg_bin_name, r, i + 1);
msleep(3000);
} else {
ts_info("Cfg_bin image [%s] is ready, try_times:%d",
cfg_bin_name, i + 1);
break;
}
}
if (i >= TS_RQST_FW_RETRY_TIMES) {
ts_err("get cfg_bin FAILED");
goto exit;
}
if (firmware->size <= 0) {
ts_err("request_firmware, cfg_bin length ERROR,len:%zu",
firmware->size);
r = -EINVAL;
goto exit;
}
cfg_bin->bin_data_len = firmware->size;
/*allocate memory for cfg_bin->bin_data*/
cfg_bin->bin_data = kzalloc(cfg_bin->bin_data_len, GFP_KERNEL);
if (!cfg_bin->bin_data) {
r = -ENOMEM;
goto exit;
}
memcpy(cfg_bin->bin_data, firmware->data, cfg_bin->bin_data_len);
r = 0;
exit:
if (firmware) {
release_firmware(firmware);
firmware = NULL;
}
return r;
}
void gt9896s_cfg_pkg_leToCpu(struct gt9896s_cfg_package *pkg)
{
if (!pkg) {
ts_err("cfg package is NULL");
return;
}
/*package const_info*/
pkg->cnst_info.pkg_len = le32_to_cpu(pkg->cnst_info.pkg_len);
pkg->cnst_info.x_res_offset = le16_to_cpu(pkg->cnst_info.x_res_offset);
pkg->cnst_info.y_res_offset = le16_to_cpu(pkg->cnst_info.y_res_offset);
pkg->cnst_info.trigger_offset = le16_to_cpu(pkg->cnst_info.trigger_offset);
/*package reg_info*/
pkg->reg_info.cfg_send_flag.addr = le16_to_cpu(pkg->reg_info.cfg_send_flag.addr);
pkg->reg_info.pid.addr = le16_to_cpu(pkg->reg_info.pid.addr);
pkg->reg_info.vid.addr = le16_to_cpu(pkg->reg_info.vid.addr);
pkg->reg_info.sensor_id.addr = le16_to_cpu(pkg->reg_info.sensor_id.addr);
pkg->reg_info.fw_status.addr = le16_to_cpu(pkg->reg_info.fw_status.addr);
pkg->reg_info.cfg_addr.addr = le16_to_cpu(pkg->reg_info.cfg_addr.addr);
pkg->reg_info.esd.addr = le16_to_cpu(pkg->reg_info.esd.addr);
pkg->reg_info.command.addr = le16_to_cpu(pkg->reg_info.command.addr);
pkg->reg_info.coor.addr = le16_to_cpu(pkg->reg_info.coor.addr);
pkg->reg_info.gesture.addr = le16_to_cpu(pkg->reg_info.gesture.addr);
pkg->reg_info.fw_request.addr = le16_to_cpu(pkg->reg_info.fw_request.addr);
pkg->reg_info.proximity.addr = le16_to_cpu(pkg->reg_info.proximity.addr);
}
static int gt9896s_later_init_thread(void *data)
{
int ret;
struct gt9896s_ts_core *ts_core = data;
struct gt9896s_ts_device *ts_dev;
if (!data) {
ts_err("ts core data can't be null");
return -EINVAL;
}
ts_dev = ts_core->ts_dev;
if (!ts_dev) {
ts_err("ts dev data can't be null");
return -EINVAL;
}
ret = gt9896s_cfg_bin_proc(ts_core);
if (ret)
ts_err("parse cfg bin encounter error, %d", ret);
else
ts_info("success get cfg bin");
if (ts_dev->cfg_bin_state == CFG_BIN_STATE_ERROR) {
ts_err("parse cfg bin encounter fatal err");
goto release_core;
}
if (ts_dev->cfg_bin_state == CFG_BIN_STATE_TEMP) {
ts_err("failed get valid config data, retry after fwupdate");
ret = gt9896s_do_fw_update(UPDATE_MODE_BLOCK|UPDATE_MODE_FORCE|
UPDATE_MODE_FLASH_CFG|
UPDATE_MODE_SRC_REQUEST);
if (ret) {
ts_err("fw update failed, %d", ret);
goto release_core;
}
ts_info("fw update success retry parse cfg bin");
ret = gt9896s_cfg_bin_proc(ts_core);
if (ret) {
ts_err("failed parse cfg bin after fw update");
goto release_core;
}
} else {
ts_info("success parse config bin");
ret = gt9896s_do_fw_update(UPDATE_MODE_BLOCK|
UPDATE_MODE_FLASH_CFG|
UPDATE_MODE_SRC_REQUEST);
if (ret) {
ts_err("fw update failed, %d[ignore]", ret);
ret = 0;
}
}
ret = gt9896s_ts_stage2_init(ts_core);
if (!ret) {
ts_info("stage2 init success");
return ret;
}
ts_err("stage2 init failed, %d", ret);
release_core:
gt9896s_ts_core_release(ts_core);
return ret;
}
int gt9896s_start_later_init(struct gt9896s_ts_core *ts_core)
{
struct task_struct *init_thrd;
/* create and run update thread */
init_thrd = kthread_run(gt9896s_later_init_thread,
ts_core, "gt9896s_init_thread");
if (IS_ERR_OR_NULL(init_thrd)) {
ts_err("Failed to create update thread:%ld",
PTR_ERR(init_thrd));
return -EFAULT;
}
return 0;
}
@@ -0,0 +1,119 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2016 MediaTek Inc.
*/
#ifndef _GOODIX_CFG_BIN_H_
#define _GOODIX_CFG_BIN_H_
#include "goodix_ts_core.h"
#define TS_DEFAULT_FIRMWARE "gt9896s_firmware_"
#define TS_DEFAULT_CFG_BIN "gt9896s_cfg_"
extern const char *gt9896s_firmware_buf;
extern const char *gt9896s_config_buf;
#define TS_BIN_VERSION_START_INDEX 5
#define TS_BIN_VERSION_LEN 4
#define TS_CFG_BIN_HEAD_RESERVED_LEN 6
#define TS_CFG_OFFSET_LEN 2
#define TS_IC_TYPE_NAME_MAX_LEN 15
#define TS_CFG_BIN_HEAD_LEN (sizeof(struct gt9896s_cfg_bin_head) + TS_CFG_BIN_HEAD_RESERVED_LEN)
#define TS_PKG_CONST_INFO_LEN (sizeof(struct gt9896s_cfg_pkg_const_info))
#define TS_PKG_REG_INFO_LEN (sizeof(struct gt9896s_cfg_pkg_reg_info))
#define TS_PKG_HEAD_LEN (TS_PKG_CONST_INFO_LEN + TS_PKG_REG_INFO_LEN)
/*cfg block definitin*/
#define TS_CFG_BLOCK_PID_LEN 8
#define TS_CFG_BLOCK_VID_LEN 8
#define TS_CFG_BLOCK_FW_MASK_LEN 9
#define TS_CFG_BLOCK_FW_PATCH_LEN 4
#define TS_CFG_BLOCK_RESERVED_LEN 9
#define TS_NORMAL_CFG 0x01
#define TS_HIGH_SENSE_CFG 0x03
#define TS_RQST_FW_RETRY_TIMES 5
#pragma pack(1)
struct gt9896s_cfg_pkg_reg {
u16 addr;
u8 reserved1;
u8 reserved2;
};
struct gt9896s_cfg_pkg_const_info {
u32 pkg_len;
u8 ic_type[TS_IC_TYPE_NAME_MAX_LEN];
u8 cfg_type;
u8 sensor_id;
u8 hw_pid[TS_CFG_BLOCK_PID_LEN];
u8 hw_vid[TS_CFG_BLOCK_VID_LEN];
u8 fw_mask[TS_CFG_BLOCK_FW_MASK_LEN];
u8 fw_patch[TS_CFG_BLOCK_FW_PATCH_LEN];
u16 x_res_offset;
u16 y_res_offset;
u16 trigger_offset;
};
struct gt9896s_cfg_pkg_reg_info {
struct gt9896s_cfg_pkg_reg cfg_send_flag;
struct gt9896s_cfg_pkg_reg version_base;
struct gt9896s_cfg_pkg_reg pid;
struct gt9896s_cfg_pkg_reg vid;
struct gt9896s_cfg_pkg_reg sensor_id;
struct gt9896s_cfg_pkg_reg fw_mask;
struct gt9896s_cfg_pkg_reg fw_status;
struct gt9896s_cfg_pkg_reg cfg_addr;
struct gt9896s_cfg_pkg_reg esd;
struct gt9896s_cfg_pkg_reg command;
struct gt9896s_cfg_pkg_reg coor;
struct gt9896s_cfg_pkg_reg gesture;
struct gt9896s_cfg_pkg_reg fw_request;
struct gt9896s_cfg_pkg_reg proximity;
u8 reserved[TS_CFG_BLOCK_RESERVED_LEN];
};
struct gt9896s_cfg_bin_head {
u32 bin_len;
u8 checksum;
u8 bin_version[TS_BIN_VERSION_LEN];
u8 pkg_num;
};
#pragma pack()
struct gt9896s_cfg_package {
struct gt9896s_cfg_pkg_const_info cnst_info;
struct gt9896s_cfg_pkg_reg_info reg_info;
const u8 *cfg;
u32 pkg_len;
};
struct gt9896s_cfg_bin {
unsigned char *bin_data;
unsigned int bin_data_len;
struct gt9896s_cfg_bin_head head;
struct gt9896s_cfg_package *cfg_pkgs;
};
extern struct gt9896s_module gt9896s_modules;
int gt9896s_ts_stage2_init(struct gt9896s_ts_core *core_data);
int gt9896s_ts_core_release(struct gt9896s_ts_core *core_data);
int gt9896s_parse_cfg_bin(struct gt9896s_cfg_bin *cfg_bin);
int gt9896s_get_reg_and_cfg(struct gt9896s_ts_device *ts_dev,
struct gt9896s_cfg_bin *cfg_bin);
// int gt9896s_read_cfg_bin(struct device *dev,
// struct gt9896s_cfg_bin *cfg_bin);
int gt9896s_read_cfg_bin(struct gt9896s_ts_device *ts_dev,
struct gt9896s_cfg_bin *cfg_bin);
void gt9896s_cfg_pkg_leToCpu(struct gt9896s_cfg_package *pkg);
#endif
@@ -0,0 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2016 MediaTek Inc.
*/
#ifndef _GOODIX_FIRMWARE_FW_H_
#define _GOODIX_FIRMWARE_FW_H_
u8 gt9896s_default_fw[] = { 0x00};
#endif
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,764 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2016 MediaTek Inc.
*/
#ifndef _GOODIX_TS_CORE_H_
#define _GOODIX_TS_CORE_H_
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/firmware.h>
#include <linux/slab.h>
#include <asm/unaligned.h>
#include <linux/vmalloc.h>
#include <linux/kthread.h>
#include <linux/version.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/completion.h>
#include <linux/debugfs.h>
#include <linux/of_irq.h>
#ifdef CONFIG_OF
#include <linux/of_gpio.h>
#include <linux/regulator/consumer.h>
#endif
#ifdef CONFIG_HAS_EARLYSUSPEND
#include <linux/earlysuspend.h>
#endif
#ifdef CONFIG_FB
#include <linux/notifier.h>
#include <linux/fb.h>
#endif
#define GOODIX_FLASH_CONFIG_WITH_ISP 1
/* macros definition */
#define GOODIX_CORE_DRIVER_NAME "mtk-tpd"
#define GOODIX_PEN_DRIVER_NAME "goodix_ts,pen"
#define GOODIX_DRIVER_VERSION "v1.4.4.0"
#define GOODIX_BUS_RETRY_TIMES 3
#define GOODIX_MAX_TOUCH 10
#define GOODIX_CFG_MAX_SIZE 3072
#define GOODIX_ESD_TICK_WRITE_DATA 0xAA
#define GOODIX_PID_MAX_LEN 8
#define GOODIX_VID_MAX_LEN 8
#define GOODIX_DEFAULT_CFG_NAME "goodix_config.cfg"
#define IC_TYPE_NONE 0
#define IC_TYPE_NORMANDY 1
#define IC_TYPE_NANJING 2
#define IC_TYPE_YELLOWSTONE 3
#define IC_TYPE_YELLOWSTONE_SPI 4
#define PINCTRL_STATE_SPI_DEFAULT "gt9896s_spi_mode"
#define PINCTRL_STATE_INT_ACTIVE "gt9896s_int_active"
#define PINCTRL_STATE_RST_ACTIVE "gt9896s_reset_active"
#define PINCTRL_STATE_INT_SUSPEND "gt9896s_int_suspend"
#define PINCTRL_STATE_RST_SUSPEND "gt9896s_reset_suspend"
#define GOODIX_TOUCH_EVENT 0x80
#define GOODIX_REQUEST_EVENT 0x40
#define GOODIX_GESTURE_EVENT 0x20
#define GOODIX_HOTKNOT_EVENT 0x10
#define GOODIX_PEN_MAX_PRESSURE 4096
#define GOODIX_MAX_TP_KEY 4
#define GOODIX_MAX_PEN_KEY 2
/*
* struct goodix_module - external modules container
* @head: external modules list
* @initilized: whether this struct is initilized
* @mutex: mutex lock
* @count: current number of registered external module
* @wq: workqueue to do register work
* @core_data: core_data pointer
*/
struct gt9896s_module {
struct list_head head;
bool initilized;
struct mutex mutex;
unsigned int count;
struct workqueue_struct *wq;
struct completion core_comp;
struct gt9896s_ts_core *core_data;
};
/*
* struct gt9896s_ts_board_data - board data
* @avdd_name: name of analoy regulator
* @reset_gpio: reset gpio number
* @irq_gpio: interrupt gpio number
* @irq_flag: irq trigger type
* @power_on_delay_us: power on delay time (us)
* @power_off_delay_us: power off delay time (us)
* @swap_axis: whether swaw x y axis
* @panel_max_x/y/w/p: resolution and size
* @panel_max_key: max supported keys
* @pannel_key_map: key map
* @fw_name: name of the firmware image
*/
struct gt9896s_ts_board_data {
char avdd_name[24];
unsigned int reset_gpio;
unsigned int irq_gpio;
int irq;
unsigned int irq_flags;
unsigned int power_on_delay_us;
unsigned int power_off_delay_us;
/* For MTK Internal Touch Begin */
unsigned int input_max_x;
unsigned int input_max_y;
/* For MTK Internal Touch End */
unsigned int swap_axis;
unsigned int lcm_max_x;
unsigned int lcm_max_y;
unsigned int panel_max_x;
unsigned int panel_max_y;
unsigned int panel_max_w; /*major and minor*/
unsigned int panel_max_p; /*pressure*/
unsigned int panel_max_key;
unsigned int panel_key_map[GOODIX_MAX_TP_KEY];
unsigned int x2x;
unsigned int y2y;
bool pen_enable;
unsigned int tp_key_num;
/*add end*/
const char *fw_name;
const char *cfg_bin_name;
bool esd_default_on;
};
enum gt9896s_fw_update_mode {
UPDATE_MODE_DEFAULT = 0,
UPDATE_MODE_FORCE = (1<<0), /* force update mode */
UPDATE_MODE_BLOCK = (1<<1), /* update in block mode */
UPDATE_MODE_FLASH_CFG = (1<<2), /* reflash config */
UPDATE_MODE_SRC_SYSFS = (1<<4), /* firmware file from sysfs */
UPDATE_MODE_SRC_HEAD = (1<<5), /* firmware file from head file */
UPDATE_MODE_SRC_REQUEST = (1<<6), /* request firmware */
UPDATE_MODE_SRC_ARGS = (1<<7), /* firmware data from function args */
};
enum gt9896s_cfg_init_state {
TS_CFG_UNINITALIZED,
TS_CFG_STABLE,
TS_CFG_TEMP,
};
/*
* struct gt9896s_ts_config - chip config data
* @initialized: cfg init state, 0 uninit,
* 1 init with stable config, 2 init with temp config.
* @name: name of this config
* @lock: mutex
* @reg_base: register base of config data
* @length: bytes of the config
* @delay: delay time after sending config
* @data: config data buffer
*/
struct gt9896s_ts_config {
int initialized;
char name[24];
struct mutex lock;
unsigned int reg_base;
unsigned int length;
unsigned int delay; /*ms*/
unsigned char data[GOODIX_CFG_MAX_SIZE];
};
/*
* struct gt9896s_ts_cmd - command package
* @initialized: whether initialized
* @cmd_reg: command register
* @length: command length in bytes
* @cmds: command data
*/
#pragma pack(4)
struct gt9896s_ts_cmd {
u32 initialized;
u32 cmd_reg;
u32 length;
u8 cmds[8];
};
#pragma pack()
/* interrupt event type */
enum ts_event_type {
EVENT_INVALID = 0,
EVENT_TOUCH = (1 << 0), /* finger touch event */
EVENT_PEN = (1 << 1), /* pen event */
EVENT_REQUEST = (1 << 2),
};
/* requset event type */
//enum ts_request_type {
// REQUEST_INVALID,
// REQUEST_CONFIG,
// REQUEST_BAKREF,
// REQUEST_RESET,
// REQUEST_MAINCLK,
//};
/* notifier event */
enum ts_notify_event {
NOTIFY_FWUPDATE_START,
NOTIFY_FWUPDATE_FAILED,
NOTIFY_FWUPDATE_SUCCESS,
NOTIFY_SUSPEND,
NOTIFY_RESUME,
NOTIFY_ESD_OFF,
NOTIFY_ESD_ON,
NOTIFY_CFG_BIN_FAILED,
NOTIFY_CFG_BIN_SUCCESS,
};
enum touch_point_status {
TS_NONE,
TS_RELEASE,
TS_TOUCH,
};
/* coordinate package */
struct gt9896s_ts_coords {
int status; /* NONE, RELEASE, TOUCH */
unsigned int x, y, w, p;
};
struct gt9896s_pen_coords {
int status; /* NONE, RELEASE, TOUCH */
int tool_type; /* BTN_TOOL_RUBBER BTN_TOOL_PEN */
unsigned int x, y, p;
signed char tilt_x;
signed char tilt_y;
};
struct gt9896s_ts_key {
int status;
int code;
};
/* touch event data */
struct gt9896s_touch_data {
int touch_num;
struct gt9896s_ts_coords coords[GOODIX_MAX_TOUCH];
struct gt9896s_ts_key keys[GOODIX_MAX_TP_KEY];
};
struct gt9896s_pen_data {
struct gt9896s_pen_coords coords;
struct gt9896s_ts_key keys[GOODIX_MAX_PEN_KEY];
};
/*
* struct gt9896s_ts_event - touch event struct
* @event_type: touch event type, touch data or
* request event
* @event_data: event data
*/
struct gt9896s_ts_event {
enum ts_event_type event_type;
struct gt9896s_touch_data touch_data;
struct gt9896s_pen_data pen_data;
};
/*
* struct gt9896s_ts_version - firmware version
* @valid: whether these infomation is valid
* @pid: product id string
* @vid: firmware version code
* @cid: customer id code
* @sensor_id: sendor id
*/
struct gt9896s_ts_version {
bool valid;
char pid[GOODIX_PID_MAX_LEN];
char vid[GOODIX_VID_MAX_LEN];
u8 cid;
u8 sensor_id;
};
struct gt9896s_ts_regs {
u16 cfg_send_flag;
u16 version_base;
u8 version_len;
u16 pid;
u8 pid_len;
u16 vid;
u8 vid_len;
u16 sensor_id;
u8 sensor_id_mask;
u16 fw_mask;
u16 fw_status;
u16 cfg_addr;
u16 esd;
u16 command;
u16 coor;
u16 gesture;
u16 fw_request;
u16 proximity;
};
enum gt9896s_cfg_bin_state {
CFG_BIN_STATE_UNINIT, /* config bin uninit */
CFG_BIN_STATE_ERROR, /* config bin encounter fatal error */
CFG_BIN_STATE_INITIALIZED, /* config bin init success */
CFG_BIN_STATE_TEMP, /* config bin need reparse */
};
/*
* struct gt9896s_ts_device - ts device data
* @name: device name
* @version: reserved
* @bus_type: i2c or spi
* @ic_type: normandy or yellowstone
* @cfg_bin_state: see enum gt9896s_cfg_bin_state
* @fw_uptodate: set to 1 after do fw update
* @board_data: board data obtained from dts
* @normal_cfg: normal config data
* @highsense_cfg: high sense config data
* @hw_ops: hardware operations
* @chip_version: firmware version infomation
* @sleep_cmd: sleep commang
* @gesture_cmd: gesture command
* @dev: device pointer,may be a i2c or spi device
* @of_node: device node
*/
struct gt9896s_ts_device {
char *name;
int version;
int bus_type;
int ic_type;
int cfg_bin_state;
struct gt9896s_ts_regs reg;
struct gt9896s_ts_board_data board_data;
struct gt9896s_ts_config normal_cfg;
struct gt9896s_ts_config highsense_cfg;
const struct gt9896s_ts_hw_ops *hw_ops;
struct gt9896s_ts_version chip_version;
struct device *dev;
struct spi_device *spi_dev;
char *tx_buff;
char *rx_buff;
};
/*
* struct gt9896s_ts_hw_ops - hardware opeartions
* @dev_prepare: ic prepare
* @reset: hardware reset
* @read: read data from touch device
* @write: write data to touch device
* @send_cmd: send command to touch device
* @send_config: send configuration data
* @read_version: read firmware version
* @event_handler: touch event handler
* @suspend: put touch device into low power mode
* @resume: put touch device into working mode
*/
struct gt9896s_ts_hw_ops {
int (*init)(struct gt9896s_ts_device *dev);
int (*dev_prepare)(struct gt9896s_ts_device *ts_dev);
int (*reset)(struct gt9896s_ts_device *dev);
int (*read)(struct gt9896s_ts_device *dev, unsigned int addr,
unsigned char *data, unsigned int len);
int (*write)(struct gt9896s_ts_device *dev, unsigned int addr,
unsigned char *data, unsigned int len);
int (*read_trans)(struct gt9896s_ts_device *dev, unsigned int addr,
unsigned char *data, unsigned int len);
int (*write_trans)(struct gt9896s_ts_device *dev, unsigned int addr,
unsigned char *data, unsigned int len);
int (*send_cmd)(struct gt9896s_ts_device *dev, struct gt9896s_ts_cmd *cmd);
int (*send_config)(struct gt9896s_ts_device *dev,
struct gt9896s_ts_config *config);
int (*read_config)(struct gt9896s_ts_device *dev, u8 *config_data);
int (*read_version)(struct gt9896s_ts_device *dev,
struct gt9896s_ts_version *version);
int (*event_handler)(struct gt9896s_ts_device *dev,
struct gt9896s_ts_event *ts_event);
int (*check_hw)(struct gt9896s_ts_device *dev);
int (*suspend)(struct gt9896s_ts_device *dev);
int (*resume)(struct gt9896s_ts_device *dev);
};
/*
* struct gt9896s_ts_esd - esd protector structure
* @esd_work: esd delayed work
* @esd_on: 1 - turn on esd protection, 0 - turn
* off esd protection
*/
struct gt9896s_ts_esd {
struct delayed_work esd_work;
struct notifier_block esd_notifier;
struct gt9896s_ts_core *ts_core;
atomic_t esd_on;
};
/*
* struct godix_ts_core - core layer data struct
* @initialized: indicate core state, 1 ok, 0 bad
* @pdev: core layer platform device
* @ts_dev: hardware layer touch device
* @input_dev: input device
* @avdd: analog regulator
* @pinctrl: pinctrl handler
* @pin_sta_active: active/normal pin state
* @pin_sta_suspend: suspend/sleep pin state
* @ts_event: touch event data struct
* @power_on: power on/off flag
* @irq: irq number
* @irq_enabled: irq enabled/disabled flag
* @suspended: suspend/resume flag
* @ts_notifier: generic notifier
* @ts_esd: esd protector structure
* @fb_notifier: framebuffer notifier
* @early_suspend: early suspend
*/
struct gt9896s_ts_core {
int initialized;
struct platform_device *pdev;
struct gt9896s_ts_device *ts_dev;
struct input_dev *input_dev;
struct input_dev *pen_dev;
struct regulator *avdd;
#ifdef CONFIG_PINCTRL
struct pinctrl *pinctrl;
struct pinctrl_state *pin_spi_mode_default;
struct pinctrl_state *pin_int_sta_active;
struct pinctrl_state *pin_int_sta_suspend;
struct pinctrl_state *pin_rst_sta_active;
struct pinctrl_state *pin_rst_sta_suspend;
#endif
struct gt9896s_ts_event ts_event;
int power_on;
int irq;
size_t irq_trig_cnt;
atomic_t irq_enabled;
atomic_t suspended;
struct notifier_block ts_notifier;
struct gt9896s_ts_esd ts_esd;
#ifdef CONFIG_FB
struct notifier_block fb_notifier;
#elif defined(CONFIG_HAS_EARLYSUSPEND)
struct early_suspend early_suspend;
#endif
};
/* external module structures */
enum gt9896s_ext_priority {
EXTMOD_PRIO_RESERVED = 0,
EXTMOD_PRIO_FWUPDATE,
EXTMOD_PRIO_GESTURE,
EXTMOD_PRIO_HOTKNOT,
EXTMOD_PRIO_DBGTOOL,
EXTMOD_PRIO_DEFAULT,
};
struct gt9896s_ext_module;
/* external module's operations callback */
struct gt9896s_ext_module_funcs {
int (*init)(struct gt9896s_ts_core *core_data,
struct gt9896s_ext_module *module);
int (*exit)(struct gt9896s_ts_core *core_data,
struct gt9896s_ext_module *module);
int (*before_reset)(struct gt9896s_ts_core *core_data,
struct gt9896s_ext_module *module);
int (*after_reset)(struct gt9896s_ts_core *core_data,
struct gt9896s_ext_module *module);
int (*before_suspend)(struct gt9896s_ts_core *core_data,
struct gt9896s_ext_module *module);
int (*after_suspend)(struct gt9896s_ts_core *core_data,
struct gt9896s_ext_module *module);
int (*before_resume)(struct gt9896s_ts_core *core_data,
struct gt9896s_ext_module *module);
int (*after_resume)(struct gt9896s_ts_core *core_data,
struct gt9896s_ext_module *module);
int (*irq_event)(struct gt9896s_ts_core *core_data,
struct gt9896s_ext_module *module);
};
/*
* struct gt9896s_ext_module - external module struct
* @list: list used to link into modules manager
* @name: name of external module
* @priority: module priority vlaue, zero is invalid
* @funcs: operations callback
* @priv_data: private data region
* @kobj: kobject
* @work: used to queue one work to do registration
*/
struct gt9896s_ext_module {
struct list_head list;
char *name;
enum gt9896s_ext_priority priority;
const struct gt9896s_ext_module_funcs *funcs;
void *priv_data;
struct kobject kobj;
struct work_struct work;
};
/*
* struct gt9896s_ext_attribute - exteranl attribute struct
* @attr: attribute
* @show: show interface of external attribute
* @store: store interface of external attribute
*/
struct gt9896s_ext_attribute {
struct attribute attr;
ssize_t (*show)(struct gt9896s_ext_module *, char *);
ssize_t (*store)(struct gt9896s_ext_module *, const char *, size_t);
};
/* external attrs helper macro */
#define __EXTMOD_ATTR(_name, _mode, _show, _store) { \
.attr = {.name = __stringify(_name), .mode = _mode }, \
.show = _show, \
.store = _store, \
}
/* external attrs helper macro, used to define external attrs */
#define DEFINE_EXTMOD_ATTR(_name, _mode, _show, _store) \
static struct gt9896s_ext_attribute ext_attr_##_name = \
__EXTMOD_ATTR(_name, _mode, _show, _store);
/*
* get board data pointer
*/
static inline struct gt9896s_ts_board_data *board_data(
struct gt9896s_ts_core *core)
{
if (!core || !core->ts_dev)
return NULL;
return &(core->ts_dev->board_data);
}
/*
* get touch device pointer
*/
static inline struct gt9896s_ts_device *ts_device(
struct gt9896s_ts_core *core)
{
if (!core)
return NULL;
return core->ts_dev;
}
/*
* get touch hardware operations pointer
*/
static inline const struct gt9896s_ts_hw_ops *ts_hw_ops(
struct gt9896s_ts_core *core)
{
if (!core || !core->ts_dev)
return NULL;
return core->ts_dev->hw_ops;
}
/*
* checksum helper functions
* checksum can be u8/le16/be16/le32/be32 format
* NOTE: the caller shoule be responsible for the
* legality of @data and @size parameters, so be
* careful when call these functions.
*/
static inline u8 checksum_u8(u8 *data, u32 size)
{
u8 checksum = 0;
u32 i;
for (i = 0; i < size; i++)
checksum += data[i];
return checksum;
}
/* cal u8 data checksum for yellowston */
static inline u16 checksum_u8_ys(u8 *data, u32 size)
{
u16 checksum = 0;
u32 i;
for (i = 0; i < size - 2; i++)
checksum += data[i];
return checksum - (data[size - 2] << 8 | data[size - 1]);
}
static inline u16 checksum_le16(u8 *data, u32 size)
{
u16 checksum = 0;
u32 i;
for (i = 0; i < size; i += 2)
checksum += le16_to_cpup((__le16 *)(data + i));
return checksum;
}
static inline u16 checksum_be16(u8 *data, u32 size)
{
u16 checksum = 0;
u32 i;
for (i = 0; i < size; i += 2)
checksum += be16_to_cpup((__be16 *)(data + i));
return checksum;
}
static inline u32 checksum_le32(u8 *data, u32 size)
{
u32 checksum = 0;
u32 i;
for (i = 0; i < size; i += 4)
checksum += le32_to_cpup((__le32 *)(data + i));
return checksum;
}
static inline u32 checksum_be32(u8 *data, u32 size)
{
u32 checksum = 0;
u32 i;
for (i = 0; i < size; i += 4)
checksum += be32_to_cpup((__be32 *)(data + i));
return checksum;
}
/*
* define event action
* EVT_xxx macros are used in opeartions callback
* defined in @gt9896s_ext_module_funcs to control
* the behaviors of event such as suspend/resume/irq_event.
* generally there are two types of behaviors:
* 1. you want the flow of this event be canceled,
* in this condition, you should return EVT_CANCEL_XXX in
* the operations callback.
* e.g. the firmware update module is updating
* the firmware, you want to cancel suspend flow,
* so you need to return EVT_CANCEL_SUSPEND in
* suspend callback function.
* 2. you want the flow of this event continue, in
* this condition, you should return EVT_HANDLED in
* the callback function.
* */
#define EVT_HANDLED 0
#define EVT_CONTINUE 0
#define EVT_CANCEL 1
#define EVT_CANCEL_IRQEVT 1
#define EVT_CANCEL_SUSPEND 1
#define EVT_CANCEL_RESUME 1
#define EVT_CANCEL_RESET 1
/*
* errno define
* Note:
* 1. bus read/write functions defined in hardware
* layer code(e.g. gt9896s_xxx_i2c.c) *must* return
* -EBUS if failed to transfer data on bus.
*/
#define EBUS 1000
#define ETIMEOUT 1001
#define ECHKSUM 1002
#define EMEMCMP 1003
//#define CONFIG_GOODIX_DEBUG
/* log macro */
#define ts_info(fmt, arg...) pr_info("[GT9896s-INF][%s:%d] "fmt"\n", __func__, __LINE__, ##arg)
#define ts_err(fmt, arg...) pr_info("[GT9896s-ERR][%s:%d] "fmt"\n", __func__, __LINE__, ##arg)
#define boot_log(fmt, arg...) g_info(fmt, ##arg)
#ifdef CONFIG_GOODIX_DEBUG
#define ts_debug(fmt, arg...) pr_info("[GT9896s-DBG][%s:%d] "fmt"\n", __func__, __LINE__, ##arg)
#else
#define ts_debug(fmt, arg...) do {} while (0)
#endif
/**
* gt9896s_register_ext_module - interface for external module
* to register into touch core modules structure
*
* @module: pointer to external module to be register
* return: 0 ok, <0 failed
*/
int gt9896s_register_ext_module(struct gt9896s_ext_module *module);
/**
* gt9896s_unregister_ext_module - interface for external module
* to unregister external modules
*
* @module: pointer to external module
* return: 0 ok, <0 failed
*/
int gt9896s_unregister_ext_module(struct gt9896s_ext_module *module);
/**
* gt9896s_ts_irq_enable - Enable/Disable a irq
* @core_data: pointer to touch core data
* enable: enable or disable irq
* return: 0 ok, <0 failed
*/
int gt9896s_ts_irq_enable(struct gt9896s_ts_core *core_data, bool enable);
struct kobj_type *gt9896s_get_default_ktype(void);
/**
* fb_notifier_call_chain - notify clients of fb_events
* see enum ts_notify_event in gt9896s_ts_core.h
*/
int gt9896s_ts_blocking_notify(enum ts_notify_event evt, void *v);
/**
* * gt9896s_ts_power_on - Turn on power to the touch device
* * @core_data: pointer to touch core data
* * return: 0 ok, <0 failed
* */
int gt9896s_ts_power_on(struct gt9896s_ts_core *core_data);
/**
* gt9896s_ts_power_off - Turn off power to the touch device
* @core_data: pointer to touch core data
* return: 0 ok, <0 failed
*/
int gt9896s_ts_power_off(struct gt9896s_ts_core *core_data);
int gt9896s_ts_irq_setup(struct gt9896s_ts_core *core_data);
int gt9896s_ts_esd_init(struct gt9896s_ts_core *core);
int gt9896s_ts_register_notifier(struct notifier_block *nb);
int gt9896s_ts_fb_notifier_callback(struct notifier_block *self,
unsigned long event, void *data);
void gt9896s_msg_printf(const char *fmt, ...);
int gt9896s_do_fw_update(int mode);
int gt9896s_ts_remove(struct platform_device *pdev);
int gt9896s_start_later_init(struct gt9896s_ts_core *ts_core);
void gt9896s_ts_dev_release(void);
int gt9896s_ts_core_init(void);
#endif
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,605 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2016 MediaTek Inc.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/atomic.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/ioctl.h>
#include <linux/wait.h>
#include "goodix_ts_core.h"
#define GOODIX_TOOLS_NAME "gtp_tools"
#define GOODIX_TS_IOC_MAGIC 'G'
#define NEGLECT_SIZE_MASK (~(_IOC_SIZEMASK << _IOC_SIZESHIFT))
#define GTP_IRQ_ENABLE _IO(GOODIX_TS_IOC_MAGIC, 0)
#define GTP_DEV_RESET _IO(GOODIX_TS_IOC_MAGIC, 1)
#define GTP_SEND_COMMAND (_IOW(GOODIX_TS_IOC_MAGIC, 2, u8) & NEGLECT_SIZE_MASK)
#define GTP_SEND_CONFIG (_IOW(GOODIX_TS_IOC_MAGIC, 3, u8) & NEGLECT_SIZE_MASK)
#define GTP_ASYNC_READ (_IOR(GOODIX_TS_IOC_MAGIC, 4, u8) & NEGLECT_SIZE_MASK)
#define GTP_SYNC_READ (_IOR(GOODIX_TS_IOC_MAGIC, 5, u8) & NEGLECT_SIZE_MASK)
#define GTP_ASYNC_WRITE (_IOW(GOODIX_TS_IOC_MAGIC, 6, u8) & NEGLECT_SIZE_MASK)
#define GTP_READ_CONFIG (_IOW(GOODIX_TS_IOC_MAGIC, 7, u8) & NEGLECT_SIZE_MASK)
#define GTP_ESD_ENABLE _IO(GOODIX_TS_IOC_MAGIC, 8)
#define GTP_DRV_VERSION (_IOR(GOODIX_TS_IOC_MAGIC, 9, u8) & NEGLECT_SIZE_MASK)
#define GOODIX_TS_IOC_MAXNR 10
#define IRQ_FALG (0x01 << 2)
#define I2C_MSG_HEAD_LEN 20
#define TS_REG_COORDS_BASE 0x4100
/*
* struct gt9896s_tools_data - gt9896s tools data message used in sync read
* @data: The buffer into which data is written
* @reg_addr: Slave device register start address to start read data
* @length: Number of data bytes in @data being read from slave device
* @filled: When buffer @data be filled will set this flag with 1, outhrwise 0
* @list_head:Eonnet every gt9896s_tools_data struct into a list
*/
struct gt9896s_tools_data {
u32 reg_addr;
u32 length;
u8 *data;
bool filled;
struct list_head list;
};
/*
* struct gt9896s_tools_dev - gt9896s tools device struct
* @ts_core: The core data struct of ts driver
* @ops_mode: represent device work mode
* @rawdiffcmd: Set slave device into rawdata mode
* @normalcmd: Set slave device into normal mode
* @wq: Wait queue struct use in synchronous data read
* @mutex: Protect gt9896s_tools_dev
*/
struct gt9896s_tools_dev {
struct gt9896s_ts_core *ts_core;
struct list_head head;
unsigned int ops_mode;
struct gt9896s_ts_cmd rawdiffcmd, normalcmd;
wait_queue_head_t wq;
struct mutex mutex;
atomic_t t_count;
struct gt9896s_ext_module module;
} *gt9896s_tools_dev;
/* read data from i2c asynchronous,
* success return bytes read, else return <= 0
*/
static int async_read(struct gt9896s_tools_dev *dev, void __user *arg)
{
u8 *databuf = NULL;
int ret = 0;
u32 reg_addr, length;
u8 i2c_msg_head[I2C_MSG_HEAD_LEN];
struct gt9896s_ts_device *ts_dev = dev->ts_core->ts_dev;
const struct gt9896s_ts_hw_ops *hw_ops = ts_dev->hw_ops;
ret = copy_from_user(&i2c_msg_head, arg, I2C_MSG_HEAD_LEN);
if (ret) {
ret = -EFAULT;
goto err_out;
}
reg_addr = i2c_msg_head[0] + (i2c_msg_head[1] << 8)
+ (i2c_msg_head[2] << 16) + (i2c_msg_head[3] << 24);
length = i2c_msg_head[4] + (i2c_msg_head[5] << 8)
+ (i2c_msg_head[6] << 16) + (i2c_msg_head[7] << 24);
databuf = kzalloc(length, GFP_KERNEL);
if (!databuf) {
ts_err("Alloc memory failed");
return -ENOMEM;
}
if (!hw_ops->read_trans(ts_dev, reg_addr, databuf, length)) {
if (copy_to_user((u8 *)arg + I2C_MSG_HEAD_LEN, databuf, length)) {
ret = -EFAULT;
ts_err("Copy_to_user failed");
} else {
ret = length;
}
} else {
ret = -EBUSY;
ts_err("Read i2c failed");
}
err_out:
kfree(databuf);
return ret;
}
/* if success return config data length */
static int read_config_data(struct gt9896s_ts_device *ts_dev, void __user *arg)
{
int ret = 0;
u32 reg_addr, length;
u8 i2c_msg_head[I2C_MSG_HEAD_LEN];
u8 *tmp_buf;
ret = copy_from_user(&i2c_msg_head, arg, I2C_MSG_HEAD_LEN);
if (ret) {
ts_err("Copy data from user failed");
return -EFAULT;
}
reg_addr = i2c_msg_head[0] + (i2c_msg_head[1] << 8)
+ (i2c_msg_head[2] << 16) + (i2c_msg_head[3] << 24);
length = i2c_msg_head[4] + (i2c_msg_head[5] << 8)
+ (i2c_msg_head[6] << 16) + (i2c_msg_head[7] << 24);
ts_info("read config,reg_addr=0x%x, length=%d", reg_addr, length);
tmp_buf = kzalloc(length, GFP_KERNEL);
if (!tmp_buf) {
ts_err("failed alloc memory");
return -ENOMEM;
}
/* if reg_addr == 0, read config data with specific flow */
if (!reg_addr) {
if (ts_dev->hw_ops->read_config)
ret = ts_dev->hw_ops->read_config(ts_dev, tmp_buf);
else
ret = -EINVAL;
} else {
ret = ts_dev->hw_ops->read_trans(ts_dev, reg_addr, tmp_buf, length);
if (!ret)
ret = length;
}
if (ret <= 0)
goto err_out;
if (copy_to_user((u8 *)arg + I2C_MSG_HEAD_LEN, tmp_buf, ret)) {
ret = -EFAULT;
ts_err("Copy_to_user failed");
}
err_out:
kfree(tmp_buf);
return ret;
}
/* read data from i2c synchronous,
* success return bytes read, else return <= 0
*/
static int sync_read(struct gt9896s_tools_dev *dev, void __user *arg)
{
int ret = 0;
u8 i2c_msg_head[I2C_MSG_HEAD_LEN];
struct gt9896s_tools_data tools_data;
ret = copy_from_user(&i2c_msg_head, arg, I2C_MSG_HEAD_LEN);
if (ret) {
ts_err("Copy data from user failed");
return -EFAULT;
}
tools_data.reg_addr = i2c_msg_head[0] + (i2c_msg_head[1] << 8)
+ (i2c_msg_head[2] << 16) + (i2c_msg_head[3] << 24);
tools_data.length = i2c_msg_head[4] + (i2c_msg_head[5] << 8)
+ (i2c_msg_head[6] << 16) + (i2c_msg_head[7] << 24);
tools_data.filled = 0;
tools_data.data = kzalloc(tools_data.length, GFP_KERNEL);
if (!tools_data.data) {
ts_err("Alloc memory failed");
return -ENOMEM;
}
mutex_lock(&dev->mutex);
list_add_tail(&tools_data.list, &dev->head);
mutex_unlock(&dev->mutex);
/* wait queue will timeout after 1 seconds */
wait_event_interruptible_timeout(dev->wq, tools_data.filled == 1, HZ * 3);
mutex_lock(&dev->mutex);
list_del(&tools_data.list);
mutex_unlock(&dev->mutex);
if (tools_data.filled == 1) {
if (copy_to_user((u8 *)arg + I2C_MSG_HEAD_LEN, tools_data.data,
tools_data.length)) {
ret = -EFAULT;
ts_err("Copy_to_user failed");
} else {
ret = tools_data.length;
}
} else {
ret = -EAGAIN;
ts_err("Wait queue timeout");
}
kfree(tools_data.data);
return ret;
}
/* write data to i2c asynchronous,
* success return bytes write, else return <= 0
*/
static int async_write(struct gt9896s_tools_dev *dev, void __user *arg)
{
u8 *databuf;
int ret = 0;
u32 reg_addr, length;
u8 i2c_msg_head[I2C_MSG_HEAD_LEN];
struct gt9896s_ts_device *ts_dev = dev->ts_core->ts_dev;
const struct gt9896s_ts_hw_ops *hw_ops = ts_dev->hw_ops;
ret = copy_from_user(&i2c_msg_head, arg, I2C_MSG_HEAD_LEN);
if (ret) {
ts_err("Copy data from user failed");
return -EFAULT;
}
reg_addr = i2c_msg_head[0] + (i2c_msg_head[1] << 8)
+ (i2c_msg_head[2] << 16) + (i2c_msg_head[3] << 24);
length = i2c_msg_head[4] + (i2c_msg_head[5] << 8)
+ (i2c_msg_head[6] << 16) + (i2c_msg_head[7] << 24);
databuf = kzalloc(length, GFP_KERNEL);
if (!databuf) {
ts_err("Alloc memory failed");
return -ENOMEM;
}
ret = copy_from_user(databuf, (u8 *)arg + I2C_MSG_HEAD_LEN, length);
if (ret) {
ret = -EFAULT;
ts_err("Copy data from user failed");
goto err_out;
}
if (hw_ops->write_trans(ts_dev, reg_addr, databuf, length)) {
ret = -EBUSY;
ts_err("Write data to device failed");
} else {
ret = length;
}
err_out:
kfree(databuf);
return ret;
}
static int init_cfg_data(struct gt9896s_ts_config *cfg, void __user *arg)
{
int ret = 0;
u32 reg_addr, length;
u8 i2c_msg_head[I2C_MSG_HEAD_LEN];
cfg->initialized = 0;
mutex_init(&cfg->lock);
ret = copy_from_user(&i2c_msg_head, arg, I2C_MSG_HEAD_LEN);
if (ret) {
ts_err("Copy data from user failed");
return -EFAULT;
}
reg_addr = i2c_msg_head[0] + (i2c_msg_head[1] << 8)
+ (i2c_msg_head[2] << 16) + (i2c_msg_head[3] << 24);
length = i2c_msg_head[4] + (i2c_msg_head[5] << 8)
+ (i2c_msg_head[6] << 16) + (i2c_msg_head[7] << 24);
ret = copy_from_user(cfg->data, (u8 *)arg + I2C_MSG_HEAD_LEN, length);
if (ret) {
ret = -EFAULT;
ts_err("Copy data from user failed");
goto err_out;
}
cfg->reg_base = reg_addr;
cfg->length = length;
strlcpy(cfg->name, "tools-send-cfg", sizeof(cfg->name));
cfg->delay = 50;
cfg->initialized = true;
return 0;
err_out:
return ret;
}
/**
* gt9896s_tools_ioctl - ioctl implementation
*
* @filp: Pointer to file opened
* @cmd: Ioctl opertion command
* @arg: Command data
* Returns >=0 - succeed, else failed
*/
static long gt9896s_tools_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
int ret = 0;
struct gt9896s_tools_dev *dev = filp->private_data;
struct gt9896s_ts_device *ts_dev;
const struct gt9896s_ts_hw_ops *hw_ops;
struct gt9896s_ts_cmd temp_cmd;
struct gt9896s_ts_config *temp_cfg = NULL;
if (dev->ts_core == NULL) {
ts_err("Tools module not register");
return -EINVAL;
}
ts_dev = dev->ts_core->ts_dev;
hw_ops = ts_dev->hw_ops;
if (_IOC_TYPE(cmd) != GOODIX_TS_IOC_MAGIC) {
ts_err("Bad magic num:%c", _IOC_TYPE(cmd));
return -ENOTTY;
}
if (_IOC_NR(cmd) > GOODIX_TS_IOC_MAXNR) {
ts_err("Bad cmd num:%d > %d",
_IOC_NR(cmd), GOODIX_TS_IOC_MAXNR);
return -ENOTTY;
}
switch (cmd & NEGLECT_SIZE_MASK) {
case GTP_IRQ_ENABLE:
if (arg == 1) {
gt9896s_ts_irq_enable(dev->ts_core, true);
mutex_lock(&dev->mutex);
dev->ops_mode |= IRQ_FALG;
mutex_unlock(&dev->mutex);
ts_info("IRQ enabled");
} else if (arg == 0) {
gt9896s_ts_irq_enable(dev->ts_core, false);
mutex_lock(&dev->mutex);
dev->ops_mode &= ~IRQ_FALG;
mutex_unlock(&dev->mutex);
ts_info("IRQ disabled");
} else {
ts_info("Irq aready set with, arg = %ld", arg);
}
ret = 0;
break;
case GTP_ESD_ENABLE:
if (arg == 0)
gt9896s_ts_blocking_notify(NOTIFY_ESD_OFF, NULL);
else
gt9896s_ts_blocking_notify(NOTIFY_ESD_ON, NULL);
break;
case GTP_DEV_RESET:
hw_ops->reset(ts_dev);
break;
case GTP_SEND_COMMAND:
ret = copy_from_user(&temp_cmd, (void __user *)arg,
sizeof(struct gt9896s_ts_cmd));
if (ret) {
ret = -EINVAL;
goto err_out;
}
ret = hw_ops->send_cmd(ts_dev, &temp_cmd);
if (ret) {
ts_err("Send command failed");
ret = -EAGAIN;
}
break;
case GTP_SEND_CONFIG:
temp_cfg =
kzalloc(sizeof(struct gt9896s_ts_config), GFP_KERNEL);
if (temp_cfg == NULL) {
ts_err("Memory allco err");
ret = -ENOMEM;
goto err_out;
}
ret = init_cfg_data(temp_cfg, (void __user *)arg);
if (!ret && hw_ops->send_config) {
ret = hw_ops->send_config(ts_dev, temp_cfg);
if (ret) {
ts_err("Failed send config");
ret = -EAGAIN;
} else {
ts_info("Send config success");
ret = 0;
}
}
break;
case GTP_READ_CONFIG:
ret = read_config_data(ts_dev, (void __user *)arg);
if (ret > 0)
ts_info("success read config:len=%d", ret);
else
ts_err("failed read config:ret=0x%x", ret);
break;
case GTP_ASYNC_READ:
ret = async_read(dev, (void __user *)arg);
if (ret < 0)
ts_err("Async data read failed");
break;
case GTP_SYNC_READ:
if (filp->f_flags & O_NONBLOCK) {
ts_err("Goodix tools now worked in sync_bus mode");
ret = -EAGAIN;
goto err_out;
}
ret = sync_read(dev, (void __user *)arg);
if (ret < 0)
ts_err("Sync data read failed");
break;
case GTP_ASYNC_WRITE:
ret = async_write(dev, (void __user *)arg);
if (ret < 0)
ts_err("Async data write failed");
break;
case GTP_DRV_VERSION:
ret = copy_to_user((u8 *)arg, GOODIX_DRIVER_VERSION,
sizeof(GOODIX_DRIVER_VERSION));
if (ret)
ts_err("failed copy driver version info to user");
break;
default:
ts_info("Invalid cmd");
ret = -ENOTTY;
break;
}
err_out:
if (!temp_cfg) {
kfree(temp_cfg);
temp_cfg = NULL;
}
return ret;
}
#ifdef CONFIG_COMPAT
static long gt9896s_tools_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
void __user *arg32 = compat_ptr(arg);
if (!file->f_op || !file->f_op->unlocked_ioctl)
return -ENOTTY;
return file->f_op->unlocked_ioctl(file, cmd, (unsigned long)arg32);
}
#endif
static int gt9896s_tools_open(struct inode *inode, struct file *filp)
{
int ret = 0;
filp->private_data = gt9896s_tools_dev;
ts_info("tools open");
/* Only the first time open device need to register module */
ret = gt9896s_register_ext_module(&gt9896s_tools_dev->module);
if (ret) {
ts_info("failed register to core module");
}
return ret;
}
static int gt9896s_tools_release(struct inode *inode, struct file *filp)
{
int ret = 0;
/* when the last close this dev node unregister the module */
ret = gt9896s_unregister_ext_module(&gt9896s_tools_dev->module);
return ret;
}
/**
* gt9896s_tools_module_irq - gt9896s tools Irq handle
* This functions is excuted when interrupt happended
*
* @core_data: pointer to touch core data
* @module: pointer to gt9896s_ext_module struct
* return: EVT_CONTINUE let other module handle this irq
*/
static int gt9896s_tools_module_irq(struct gt9896s_ts_core *core_data,
struct gt9896s_ext_module *module)
{
struct gt9896s_tools_dev *dev = module->priv_data;
struct gt9896s_ts_device *ts_dev = dev->ts_core->ts_dev;
const struct gt9896s_ts_hw_ops *hw_ops = ts_dev->hw_ops;
struct gt9896s_tools_data *tools_data, *next;
int r = 0;
u8 evt_sta = 0;
mutex_lock(&dev->mutex);
if (!list_empty(&dev->head)) {
r = hw_ops->read_trans(ts_dev, ts_dev->reg.coor/* TS_REG_COORDS_BASE*/, &evt_sta, 1);
if (r < 0 || ((evt_sta & GOODIX_TOUCH_EVENT) == 0)) {
ts_err("data not ready:0x%x, read ret =%d", evt_sta, r);
mutex_unlock(&dev->mutex);
return EVT_CONTINUE;
}
list_for_each_entry_safe(tools_data, next, &dev->head, list) {
if (!hw_ops->read_trans(ts_dev, tools_data->reg_addr,
tools_data->data, tools_data->length)) {
tools_data->filled = 1;
}
}
wake_up(&dev->wq);
}
mutex_unlock(&dev->mutex);
return EVT_CONTINUE;
}
static int gt9896s_tools_module_init(struct gt9896s_ts_core *core_data,
struct gt9896s_ext_module *module)
{
struct gt9896s_tools_dev *tools_dev = module->priv_data;
if (core_data)
tools_dev->ts_core = core_data;
else
return -ENODEV;
return 0;
}
static const struct file_operations gt9896s_tools_fops = {
.owner = THIS_MODULE,
.open = gt9896s_tools_open,
.release = gt9896s_tools_release,
.unlocked_ioctl = gt9896s_tools_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = gt9896s_tools_compat_ioctl,
#endif
};
static struct miscdevice gt9896s_tools_miscdev = {
.minor = MISC_DYNAMIC_MINOR,
.name = GOODIX_TOOLS_NAME,
.fops = &gt9896s_tools_fops,
};
static struct gt9896s_ext_module_funcs gt9896s_tools_module_funcs = {
.irq_event = gt9896s_tools_module_irq,
.init = gt9896s_tools_module_init,
};
/**
* gt9896s_tools_init - init gt9896s tools device and register a miscdevice
*
* return: 0 success, else failed
*/
static int __init gt9896s_tools_init(void)
{
int ret;
gt9896s_tools_dev =
kzalloc(sizeof(struct gt9896s_tools_dev), GFP_KERNEL);
if (gt9896s_tools_dev == NULL) {
ts_err("Memory allco err");
return -ENOMEM;
}
INIT_LIST_HEAD(&gt9896s_tools_dev->head);
gt9896s_tools_dev->ops_mode = 0;
gt9896s_tools_dev->ops_mode |= IRQ_FALG;
init_waitqueue_head(&gt9896s_tools_dev->wq);
mutex_init(&gt9896s_tools_dev->mutex);
atomic_set(&gt9896s_tools_dev->t_count, 0);
gt9896s_tools_dev->module.funcs = &gt9896s_tools_module_funcs;
gt9896s_tools_dev->module.name = GOODIX_TOOLS_NAME;
gt9896s_tools_dev->module.priv_data = gt9896s_tools_dev;
gt9896s_tools_dev->module.priority = EXTMOD_PRIO_DBGTOOL;
ret = misc_register(&gt9896s_tools_miscdev);
if (ret)
ts_err("Debug tools miscdev register failed");
return ret;
}
static void __exit gt9896s_tools_exit(void)
{
misc_deregister(&gt9896s_tools_miscdev);
kfree(gt9896s_tools_dev);
ts_info("Goodix tools miscdev exit");
}
module_init(gt9896s_tools_init);
module_exit(gt9896s_tools_exit);
MODULE_DESCRIPTION("Goodix tools Module");
MODULE_AUTHOR("Goodix, Inc.");
MODULE_LICENSE("GPL v2");
@@ -0,0 +1,49 @@
BUILD_INFAE := spi
BUILD_PLATFORM := mtk
BUILD_MODULE := n
ccflags-y += -Wall
ifeq ($(BUILD_PLATFORM),mtk)
platform=ilitek_v3_mtk
ccflags-y += -I$(srctree)/drivers/spi/mediatek/mt6797/
ccflags-y += -I$(srctree)/drivers/input/touchscreen/ILITEK_V3/
ccflags-y += -I$(srctree)/drivers/input/touchscreen/ILITEK_V3/firmware/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/mt-plat/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/
endif
ifeq ($(BUILD_INFAE),i2c)
interface=ilitek_v3_i2c
fwupdate=ilitek_v3_flash
endif
ifeq ($(BUILD_INFAE),spi)
interface=ilitek_v3_spi
fwupdate=ilitek_v3_hostdl
endif
ifeq ($(BUILD_MODULE),n)
obj-y += ilitek_v3.o \
$(interface).o \
$(platform).o \
ilitek_v3_ic.o \
ilitek_v3_touch.o \
$(fwupdate).o \
ilitek_v3_node.o \
tpd_control.o \
tpd_button.o \
tpd_debug.o \
tpd_default.o \
tpd_misc.o \
tpd_setting.o
else
obj-m += ilitek.o
ilitek-y := ilitek_v3.o \
$(interface).o \
$(platform).o \
ilitek_v3_ic.o \
ilitek_v3_touch.o \
$(fwupdate).o \
ilitek_v3_node.o
endif
@@ -0,0 +1,74 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2019 MediaTek Inc.
*/
#ifndef __ILITEK_V3_FW_H
#define __ILITEK_V3_FW_H
/* define names and paths for the variety of tp modules */
#define DEF_INI_NAME_PATH "/sdcard/mp.ini"
#define DEF_FW_FILP_PATH "/sdcard/ILITEK_FW"
#define DEF_INI_REQUEST_PATH "mp.ini"
#define DEF_FW_REQUEST_PATH "ILITEK_FW"
static unsigned char CTPM_FW_DEF[] = {
#include "9882N_MTK_90Hz_V0x00.0x00_AP_0x01_MP_0x01_SPI_SLAVE20200818.ili"
};
#define CSOT_INI_NAME_PATH "/sdcard/mp_csot.ini"
#define CSOT_FW_FILP_PATH "/sdcard/ILITEK_FW_CSOT"
#define CSOT_INI_REQUEST_PATH "mp_csot.ini"
#define CSOT_FW_REQUEST_PATH "ILITEK_FW_CSOT"
static unsigned char CTPM_FW_CSOT[] = {
0xFF,
};
#define AUO_INI_NAME_PATH "/sdcard/mp_auo.ini"
#define AUO_FW_FILP_PATH "/sdcard/ILITEK_FW_AUO"
#define AUO_INI_REQUEST_PATH "mp_auo.ini"
#define AUO_FW_REQUEST_PATH "ILITEK_FW_AUO"
static unsigned char CTPM_FW_AUO[] = {
0xFF,
};
#define BOE_INI_NAME_PATH "/sdcard/mp_boe.ini"
#define BOE_FW_FILP_PATH "/sdcard/ILITEK_FW_BOE"
#define BOE_INI_REQUEST_PATH "mp_boe.ini"
#define BOE_FW_REQUEST_PATH "ILITEK_FW_BOE"
static unsigned char CTPM_FW_BOE[] = {
0xFF,
};
#define INX_INI_NAME_PATH "/sdcard/mp_inx.ini"
#define INX_FW_FILP_PATH "/sdcard/ILITEK_FW_INX"
#define INX_INI_REQUEST_PATH "mp_inx.ini"
#define INX_FW_REQUEST_PATH "ILITEK_FW_INX"
static unsigned char CTPM_FW_INX[] = {
0xFF,
};
#define DJ_INI_NAME_PATH "/sdcard/mp_dj.ini"
#define DJ_FW_FILP_PATH "/sdcard/ILITEK_FW_DJ"
#define DJ_INI_REQUEST_PATH "mp_dj.ini"
#define DJ_FW_REQUEST_PATH "ILITEK_FW_DJ"
static unsigned char CTPM_FW_DJ[] = {
0xFF,
};
#define TXD_INI_NAME_PATH "/sdcard/mp_txd.ini"
#define TXD_FW_FILP_PATH "/sdcard/ILITEK_FW_TXD"
#define TXD_INI_REQUEST_PATH "mp_txd.ini"
#define TXD_FW_REQUEST_PATH "ILITEK_FW_TXD"
static unsigned char CTPM_FW_TXD[] = {
0xFF,
};
#define TM_INI_NAME_PATH "/sdcard/mp_tm.ini"
#define TM_FW_FILP_PATH "/sdcard/ILITEK_FW_TM"
#define TM_INI_REQUEST_PATH "mp_tm.ini"
#define TM_FW_REQUEST_PATH "ILITEK_FW_TM"
static unsigned char CTPM_FW_TM[] = {
0xFF,
};
#endif
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,899 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2019 MediaTek Inc.
*/
#include "ilitek_v3.h"
#define UPDATE_PASS 0
#define UPDATE_FAIL -1
#define TIMEOUT_SECTOR 500
#define TIMEOUT_PAGE 3500
#define TIMEOUT_PROGRAM 10
static struct touch_fw_data {
u8 block_number;
u32 start_addr;
u32 end_addr;
u32 new_fw_cb;
int delay_after_upgrade;
bool isCRC;
bool isboot;
bool is80k;
int hex_tag;
} tfd;
static struct flash_block_info {
char *name;
u32 start;
u32 end;
u32 len;
u32 mem_start;
u32 fix_mem_start;
u8 mode;
} fbi[FW_BLOCK_INFO_NUM];
static u8 *pfw;
static u8 *CTPM_FW;
static u32 HexToDec(char *phex, s32 len)
{
u32 ret = 0, temp = 0, i;
s32 shift = (len - 1) * 4;
for (i = 0; i < len; shift -= 4, i++) {
if ((phex[i] >= '0') && (phex[i] <= '9'))
temp = phex[i] - '0';
else if ((phex[i] >= 'a') && (phex[i] <= 'f'))
temp = (phex[i] - 'a') + 10;
else if ((phex[i] >= 'A') && (phex[i] <= 'F'))
temp = (phex[i] - 'A') + 10;
ret |= (temp << shift);
}
return ret;
}
static int CalculateCRC32(u32 start_addr, u32 len, u8 *pfw)
{
int i = 0, j = 0;
int crc_poly = 0x04C11DB7;
int tmp_crc = 0xFFFFFFFF;
for (i = start_addr; i < start_addr + len; i++) {
tmp_crc ^= (pfw[i] << 24);
for (j = 0; j < 8; j++) {
if ((tmp_crc & 0x80000000) != 0)
tmp_crc = tmp_crc << 1 ^ crc_poly;
else
tmp_crc = tmp_crc << 1;
}
}
return tmp_crc;
}
static int calc_hw_dma_crc(u32 start_addr, u32 block_size)
{
int count = 50;
u32 busy = 0;
if (ilits->chip->dma_reset) {
ILI_DBG("operate dma reset in reg after tp reset\n");
if (ili_ice_mode_write(0x40040, 0x00800000, 4) < 0)
ILI_ERR("Failed to open DMA reset\n");
if (ili_ice_mode_write(0x40040, 0x00000000, 4) < 0)
ILI_ERR("Failed to close DMA reset\n");
}
/* dma1 src1 address */
if (ili_ice_mode_write(0x072104, start_addr, 4) < 0)
ILI_ERR("Write dma1 src1 address failed\n");
/* dma1 src1 format */
if (ili_ice_mode_write(0x072108, 0x80000001, 4) < 0)
ILI_ERR("Write dma1 src1 format failed\n");
/* dma1 dest address */
if (ili_ice_mode_write(0x072114, 0x0002725C, 4) < 0)
ILI_ERR("Write dma1 src1 format failed\n");
/* dma1 dest format */
if (ili_ice_mode_write(0x072118, 0x80000000, 4) < 0)
ILI_ERR("Write dma1 dest format failed\n");
/* Block size*/
if (ili_ice_mode_write(0x07211C, block_size, 4) < 0)
ILI_ERR("Write block size (%d) failed\n", block_size);
/* crc off */
if (ili_ice_mode_write(0x041016, 0x00, 1) < 0)
ILI_INFO("Write crc of failed\n");
/* dma crc */
if (ili_ice_mode_write(0x041017, 0x03, 1) < 0)
ILI_ERR("Write dma 1 crc failed\n");
/* crc on */
if (ili_ice_mode_write(0x041016, 0x01, 1) < 0)
ILI_ERR("Write crc on failed\n");
/* Dma1 stop */
if (ili_ice_mode_write(0x072100, 0x02000000, 4) < 0)
ILI_ERR("Write dma1 stop failed\n");
/* clr int */
if (ili_ice_mode_write(0x048006, 0x2, 1) < 0)
ILI_ERR("Write clr int failed\n");
/* Dma1 start */
if (ili_ice_mode_write(0x072100, 0x01000000, 4) < 0)
ILI_ERR("Write dma1 start failed\n");
/* Polling BIT0 */
while (count > 0) {
mdelay(1);
if (ili_ice_mode_read(0x048006, &busy, sizeof(u8)) < 0)
ILI_ERR("Read busy error\n");
ILI_DBG("busy = %x\n", busy);
if ((busy & 0x02) == 2)
break;
count--;
}
if (count <= 0) {
ILI_ERR("BIT0 is busy\n");
return -1;
}
if (ili_ice_mode_read(0x04101C, &busy, sizeof(u32)) < 0) {
ILI_ERR("Read dma crc error\n");
return -1;
}
return busy;
}
static int ilitek_tddi_fw_iram_read(u8 *buf, u32 start, int len)
{
int limit = SPI_RX_BUF_SIZE;
int addr = 0, loop = 0, tmp_len = len, cnt = 0;
u8 cmd[4] = {0};
if (!buf) {
ILI_ERR("buf is null\n");
return -ENOMEM;
}
if (len % limit)
loop = (len / limit) + 1;
else
loop = len / limit;
for (cnt = 0, addr = start; cnt < loop; cnt++, addr += limit) {
if (tmp_len > limit)
tmp_len = limit;
cmd[0] = 0x25;
cmd[3] = (char)((addr & 0x00FF0000) >> 16);
cmd[2] = (char)((addr & 0x0000FF00) >> 8);
cmd[1] = (char)((addr & 0x000000FF));
if (ilits->wrapper(cmd, 4, NULL, 0, OFF, OFF) < 0) {
ILI_ERR("Failed to write iram data\n");
return -ENODEV;
}
if (ilits->wrapper(NULL, 0, buf + cnt * limit, tmp_len, OFF, OFF) < 0) {
ILI_ERR("Failed to Read iram data\n");
return -ENODEV;
}
tmp_len = len - cnt * limit;
ilits->fw_update_stat = ((len - tmp_len) * 100) / len;
ILI_INFO("Reading iram data .... %d%c", ilits->fw_update_stat, '%');
}
return 0;
}
int ili_fw_dump_iram_data(u32 start, u32 end, bool save, bool mcu)
{
struct file *f = NULL;
mm_segment_t old_fs;
loff_t pos = 0;
int i, ret = 0;
int len, tmp = debug_en;
bool ice = atomic_read(&ilits->ice_stat);
if (!ice) {
ret = ili_ice_mode_ctrl(ENABLE, mcu);
if (ret < 0) {
ILI_ERR("Enable ice mode failed\n");
return ret;
}
}
len = end - start + 1;
if (len > MAX_HEX_FILE_SIZE) {
ILI_ERR("len is larger than buffer, abort\n");
ret = -EINVAL;
goto out;
}
for (i = 0; i < MAX_HEX_FILE_SIZE; i++)
ilits->update_buf[i] = 0xFF;
ret = ilitek_tddi_fw_iram_read(ilits->update_buf, start, len);
if (ret < 0) {
ILI_ERR("Read IRAM data failed\n");
goto out;
}
if (save) {
f = filp_open(DUMP_IRAM_PATH, O_WRONLY | O_CREAT | O_TRUNC, 644);
if (ERR_ALLOC_MEM(f)) {
ILI_ERR("Failed to open the file at %ld.\n", PTR_ERR(f));
ret = -ENOMEM;
goto out;
}
old_fs = get_fs();
set_fs(get_ds());
set_fs(KERNEL_DS);
pos = 0;
vfs_write(f, ilits->update_buf, len, &pos);
set_fs(old_fs);
filp_close(f, NULL);
ILI_INFO("Save iram data to %s\n", DUMP_IRAM_PATH);
} else {
debug_en = DEBUG_ALL;
ili_dump_data(ilits->update_buf, 8, len, 0, "IRAM");
debug_en = tmp;
}
out:
if (!ice) {
if (ili_ice_mode_ctrl(DISABLE, mcu) < 0)
ILI_ERR("Enable ice mode failed after code reset\n");
}
ILI_INFO("Dump IRAM %s\n", (ret < 0) ? "FAIL" : "SUCCESS");
return ret;
}
static int ilitek_tddi_fw_iram_program(u32 start, u8 *w_buf, u32 w_len, u32 split_len)
{
int i = 0, j = 0, addr = 0;
u32 end = start + w_len;
bool fix_4_alignment = false;
if (split_len % 4 > 0)
ILI_ERR("Since split_len must be four-aligned, it must be a multiple of four");
if (split_len != 0) {
for (addr = start, i = 0; addr < end; addr += split_len, i += split_len) {
if ((addr + split_len) > end) {
split_len = end - addr;
if (split_len % 4 != 0)
fix_4_alignment = true;
}
ilits->update_buf[0] = SPI_WRITE;
ilits->update_buf[1] = 0x25;
ilits->update_buf[2] = (char)((addr & 0x000000FF));
ilits->update_buf[3] = (char)((addr & 0x0000FF00) >> 8);
ilits->update_buf[4] = (char)((addr & 0x00FF0000) >> 16);
for (j = 0; j < split_len; j++)
ilits->update_buf[5 + j] = w_buf[i + j];
if (fix_4_alignment) {
ILI_INFO("org split_len = 0x%X\n", split_len);
ILI_INFO("idev->update_buf[5 + 0x%X] = 0x%X\n", split_len - 4, ilits->update_buf[5 + split_len - 4]);
ILI_INFO("idev->update_buf[5 + 0x%X] = 0x%X\n", split_len - 3, ilits->update_buf[5 + split_len - 3]);
ILI_INFO("idev->update_buf[5 + 0x%X] = 0x%X\n", split_len - 2, ilits->update_buf[5 + split_len - 2]);
ILI_INFO("idev->update_buf[5 + 0x%X] = 0x%X\n", split_len - 1, ilits->update_buf[5 + split_len - 1]);
for (j = 0; j < (4 - (split_len % 4)); j++) {
ilits->update_buf[5 + j + split_len] = 0xFF;
ILI_INFO("idev->update_buf[5 + 0x%X] = 0x%X\n", j + split_len, ilits->update_buf[5 + j + split_len]);
}
ILI_INFO("split_len %% 4 = %d\n", split_len % 4);
split_len = split_len + (4 - (split_len % 4));
ILI_INFO("fix split_len = 0x%X\n", split_len);
}
if (ilits->spi_write_then_read(ilits->spi, ilits->update_buf, split_len + 5, NULL, 0)) {
ILI_ERR("Failed to write data via SPI in host download (%x)\n", split_len + 5);
return -EIO;
}
ilits->fw_update_stat = (i * 100) / w_len;
}
} else {
for (i = 0; i < MAX_HEX_FILE_SIZE; i++)
ilits->update_buf[i] = 0xFF;
ilits->update_buf[0] = SPI_WRITE;
ilits->update_buf[1] = 0x25;
ilits->update_buf[2] = (char)((start & 0x000000FF));
ilits->update_buf[3] = (char)((start & 0x0000FF00) >> 8);
ilits->update_buf[4] = (char)((start & 0x00FF0000) >> 16);
memcpy(&ilits->update_buf[5], w_buf, w_len);
if (w_len % 4 != 0) {
ILI_INFO("org w_len = %d\n", w_len);
w_len = w_len + (4 - (w_len % 4));
ILI_INFO("w_len = %d w_len %% 4 = %d\n", w_len, w_len % 4);
}
/* It must be supported by platforms that have the ability to transfer all data at once. */
if (ilits->spi_write_then_read(ilits->spi, ilits->update_buf, w_len + 5, NULL, 0) < 0) {
ILI_ERR("Failed to write data via SPI in host download (%x)\n", w_len + 5);
return -EIO;
}
}
return 0;
}
static int ilitek_tddi_fw_iram_upgrade(u8 *pfw, bool mcu)
{
int i, ret = UPDATE_PASS;
u32 mode, crc, dma, iram_crc = 0;
u8 *fw_ptr = NULL, crc_temp[4], crc_len = 4;
bool iram_crc_err = false;
if (!ilits->ddi_rest_done) {
if (ilits->actual_tp_mode != P5_X_FW_GESTURE_MODE)
ili_reset_ctrl(ilits->reset);
ret = ili_ice_mode_ctrl(ENABLE, mcu);
if (ret < 0)
return -EFW_ICE_MODE;
} else {
/* Restore it if the wq of load_fw_ddi has been called. */
ilits->ddi_rest_done = false;
}
/* Point to pfw with different addresses for getting its block data. */
fw_ptr = pfw;
if (ilits->actual_tp_mode == P5_X_FW_TEST_MODE) {
mode = MP;
} else if (ilits->actual_tp_mode == P5_X_FW_GESTURE_MODE) {
mode = GESTURE;
crc_len = 0;
} else {
mode = AP;
}
/* Program data to iram acorrding to each block */
for (i = 0; i < FW_BLOCK_INFO_NUM; i++) {
if (fbi[i].mode == mode && fbi[i].len != 0) {
ILI_DBG("Download %s code from hex 0x%x to IRAM 0x%x, len = 0x%x\n",
fbi[i].name, fbi[i].start, fbi[i].mem_start, fbi[i].len);
#if SPI_DMA_TRANSFER_SPLIT
if (ilitek_tddi_fw_iram_program(fbi[i].mem_start, (fw_ptr + fbi[i].start), fbi[i].len, SPI_UPGRADE_LEN) < 0)
ILI_ERR("IRAM program failed\n");
#else
if (ilitek_tddi_fw_iram_program(fbi[i].mem_start, (fw_ptr + fbi[i].start), fbi[i].len, 0) < 0)
ILI_ERR("IRAM program failed\n");
#endif
crc = CalculateCRC32(fbi[i].start, fbi[i].len - crc_len, fw_ptr);
dma = calc_hw_dma_crc(fbi[i].mem_start, fbi[i].len - crc_len);
if (mode != GESTURE) {
ilitek_tddi_fw_iram_read(crc_temp, (fbi[i].mem_start + fbi[i].len - crc_len), sizeof(crc_temp));
iram_crc = crc_temp[0] << 24 | crc_temp[1] << 16 | crc_temp[2] << 8 | crc_temp[3];
if (iram_crc != dma)
iram_crc_err = true;
}
ILI_INFO("%s CRC is %s hex(%x) : dma(%x) : iram(%x), calculation len is 0x%x\n",
fbi[i].name, ((crc != dma) || (iram_crc_err)) ? "Invalid !" : "Correct !", crc, dma, iram_crc, fbi[i].len - crc_len);
if ((crc != dma) || iram_crc_err) {
ILI_ERR("CRC Error! print iram data with first 16 bytes\n");
ili_fw_dump_iram_data(0x0, 0xF, false, OFF);
return -EFW_CRC;
}
}
}
if (ilits->actual_tp_mode != P5_X_FW_GESTURE_MODE) {
if (ili_reset_ctrl(TP_IC_CODE_RST) < 0) {
ILI_ERR("TP Code reset failed during iram programming\n");
ret = -EFW_REST;
return ret;
}
}
if (ili_ice_mode_ctrl(DISABLE, mcu) < 0) {
ILI_ERR("Disable ice mode failed after code reset\n");
ret = -EFW_ICE_MODE;
}
/* Waiting for fw ready sending first cmd */
if (!ilits->info_from_hex || (ilits->chip->core_ver < CORE_VER_1410))
mdelay(100);
return ret;
}
static int ilitek_fw_calc_file_crc(u8 *pfw)
{
int i;
u32 ex_addr, data_crc, file_crc;
for (i = 0; i < ARRAY_SIZE(fbi); i++) {
if (fbi[i].end == 0)
continue;
ex_addr = fbi[i].end;
data_crc = CalculateCRC32(fbi[i].start, fbi[i].len - 4, pfw);
file_crc = pfw[ex_addr - 3] << 24 | pfw[ex_addr - 2] << 16 | pfw[ex_addr - 1] << 8 | pfw[ex_addr];
ILI_DBG("data crc = %x, file crc = %x\n", data_crc, file_crc);
if (data_crc != file_crc) {
ILI_ERR("Content of fw file is broken. (%d, %x, %x)\n",
i, data_crc, file_crc);
return -1;
}
}
ILI_INFO("Content of fw file is correct\n");
return 0;
}
static int ilitek_tddi_fw_update_block_info(u8 *pfw)
{
u32 ges_area_section = 0, ges_info_addr = 0, ges_fw_start = 0, ges_fw_end = 0;
u32 ap_end = 0, ap_len = 0;
u32 fw_info_addr = 0, fw_mp_ver_addr = 0;
if (tfd.hex_tag != BLOCK_TAG_AF) {
ILI_ERR("HEX TAG is invalid (0x%X)\n", tfd.hex_tag);
return -EINVAL;
}
fbi[AP].mem_start = (fbi[AP].fix_mem_start != INT_MAX) ? fbi[AP].fix_mem_start : 0;
fbi[DATA].mem_start = (fbi[DATA].fix_mem_start != INT_MAX) ? fbi[DATA].fix_mem_start : DLM_START_ADDRESS;
fbi[TUNING].mem_start = (fbi[TUNING].fix_mem_start != INT_MAX) ? fbi[TUNING].fix_mem_start : fbi[DATA].mem_start + fbi[DATA].len;
fbi[MP].mem_start = (fbi[MP].fix_mem_start != INT_MAX) ? fbi[MP].fix_mem_start : 0;
fbi[GESTURE].mem_start = (fbi[GESTURE].fix_mem_start != INT_MAX) ? fbi[GESTURE].fix_mem_start : 0;
fbi[TAG].mem_start = (fbi[TAG].fix_mem_start != INT_MAX) ? fbi[TAG].fix_mem_start : 0;
fbi[PARA_BACKUP].mem_start = (fbi[PARA_BACKUP].fix_mem_start != INT_MAX) ? fbi[PARA_BACKUP].fix_mem_start : 0;
fbi[DDI].mem_start = (fbi[DDI].fix_mem_start != INT_MAX) ? fbi[DDI].fix_mem_start : 0;
/* Parsing gesture info form AP code */
ges_info_addr = (fbi[AP].end + 1 - 60);
ges_area_section = (pfw[ges_info_addr + 3] << 24) + (pfw[ges_info_addr + 2] << 16) + (pfw[ges_info_addr + 1] << 8) + pfw[ges_info_addr];
fbi[GESTURE].mem_start = (pfw[ges_info_addr + 7] << 24) + (pfw[ges_info_addr + 6] << 16) + (pfw[ges_info_addr + 5] << 8) + pfw[ges_info_addr + 4];
ap_end = (pfw[ges_info_addr + 11] << 24) + (pfw[ges_info_addr + 10] << 16) + (pfw[ges_info_addr + 9] << 8) + pfw[ges_info_addr + 8];
if (ap_end != fbi[GESTURE].mem_start)
ap_len = ap_end - fbi[GESTURE].mem_start + 1;
ges_fw_start = (pfw[ges_info_addr + 15] << 24) + (pfw[ges_info_addr + 14] << 16) + (pfw[ges_info_addr + 13] << 8) + pfw[ges_info_addr + 12];
ges_fw_end = (pfw[ges_info_addr + 19] << 24) + (pfw[ges_info_addr + 18] << 16) + (pfw[ges_info_addr + 17] << 8) + pfw[ges_info_addr + 16];
if (ges_fw_end != ges_fw_start)
fbi[GESTURE].len = ges_fw_end - ges_fw_start;
/* update gesture address */
fbi[GESTURE].start = ges_fw_start;
ILI_INFO("==== Gesture loader info ====\n");
ILI_INFO("gesture move to ap addr => start = 0x%x, ap_end = 0x%x, ap_len = 0x%x\n", fbi[GESTURE].mem_start, ap_end, ap_len);
ILI_INFO("gesture hex addr => start = 0x%x, gesture_end = 0x%x, gesture_len = 0x%x\n", ges_fw_start, ges_fw_end, fbi[GESTURE].len);
ILI_INFO("=============================\n");
fbi[AP].name = "AP";
fbi[DATA].name = "DATA";
fbi[TUNING].name = "TUNING";
fbi[MP].name = "MP";
fbi[GESTURE].name = "GESTURE";
fbi[TAG].name = "TAG";
fbi[PARA_BACKUP].name = "PARA_BACKUP";
fbi[DDI].name = "DDI";
/* upgrade mode define */
fbi[DATA].mode = fbi[AP].mode = fbi[TUNING].mode = AP;
fbi[MP].mode = MP;
fbi[GESTURE].mode = GESTURE;
if (fbi[AP].end > (64*K))
tfd.is80k = true;
/* Copy fw info */
fw_info_addr = fbi[AP].end - INFO_HEX_ST_ADDR;
ILI_INFO("Parsing hex info start addr = 0x%x\n", fw_info_addr);
ipio_memcpy(ilits->fw_info, (pfw + fw_info_addr), sizeof(ilits->fw_info), sizeof(ilits->fw_info));
/* copy fw mp ver */
fw_mp_ver_addr = fbi[MP].end - INFO_MP_HEX_ADDR;
ILI_INFO("Parsing hex mp ver addr = 0x%x\n", fw_mp_ver_addr);
ipio_memcpy(ilits->fw_mp_ver, pfw + fw_mp_ver_addr, sizeof(ilits->fw_mp_ver), sizeof(ilits->fw_mp_ver));
/* copy fw core ver */
ilits->chip->core_ver = (ilits->fw_info[68] << 24) | (ilits->fw_info[69] << 16) |
(ilits->fw_info[70] << 8) | ilits->fw_info[71];
ILI_INFO("New FW Core version = %x\n", ilits->chip->core_ver);
/* Get hex fw vers */
tfd.new_fw_cb = (ilits->fw_info[48] << 24) | (ilits->fw_info[49] << 16) |
(ilits->fw_info[50] << 8) | ilits->fw_info[51];
/* Get hex report info block*/
ipio_memcpy(&ilits->rib, ilits->fw_info, sizeof(ilits->rib), sizeof(ilits->rib));
ILI_INFO("report_info_block : nReportByPixel = %d, nIsHostDownload = %d, nIsSPIICE = %d, nIsSPISLAVE = %d\n",
ilits->rib.nReportByPixel, ilits->rib.nIsHostDownload, ilits->rib.nIsSPIICE, ilits->rib.nIsSPISLAVE);
ILI_INFO("report_info_block : nIsI2C = %d, nReserved00 = %d, nReserved01 = %x, nReserved02 = %x, nReserved03 = %x\n",
ilits->rib.nIsI2C, ilits->rib.nReserved00, ilits->rib.nReserved01, ilits->rib.nReserved02, ilits->rib.nReserved03);
/* Calculate update address */
ILI_INFO("New FW ver = 0x%x\n", tfd.new_fw_cb);
ILI_INFO("star_addr = 0x%06X, end_addr = 0x%06X, Block Num = %d\n", tfd.start_addr, tfd.end_addr, tfd.block_number);
return 0;
}
static int ilitek_tddi_fw_ili_convert(u8 *pfw)
{
int i, size, blk_num = 0, blk_map = 0, num;
int b0_addr = 0, b0_num = 0;
if (ERR_ALLOC_MEM(ilits->md_fw_ili))
return -ENOMEM;
CTPM_FW = ilits->md_fw_ili;
size = ilits->md_fw_ili_size;
if (size < ILI_FILE_HEADER || size > MAX_HEX_FILE_SIZE) {
ILI_ERR("size of ILI file is invalid\n");
return -EINVAL;
}
/* Check if it's old version of ILI format. */
if (CTPM_FW[22] == 0xFF && CTPM_FW[23] == 0xFF &&
CTPM_FW[24] == 0xFF && CTPM_FW[25] == 0xFF) {
ILI_ERR("Invaild ILI format, abort!\n");
return -EINVAL;
}
blk_num = CTPM_FW[131];
blk_map = (CTPM_FW[129] << 8) | CTPM_FW[130];
ILI_INFO("Parsing ILI file, block num = %d, block mapping = %x\n", blk_num, blk_map);
if (blk_num > (FW_BLOCK_INFO_NUM - 1) || !blk_num || !blk_map) {
ILI_ERR("Number of block or block mapping is invalid, abort!\n");
return -EINVAL;
}
memset(fbi, 0x0, sizeof(fbi));
tfd.start_addr = 0;
tfd.end_addr = 0;
tfd.hex_tag = BLOCK_TAG_AF;
/* Parsing block info */
for (i = 0; i < FW_BLOCK_INFO_NUM; i++) {
/* B0 tag */
b0_addr = (CTPM_FW[4 + i * 4] << 16) | (CTPM_FW[5 + i * 4] << 8) | (CTPM_FW[6 + i * 4]);
b0_num = CTPM_FW[7 + i * 4];
if ((b0_num != 0) && (b0_addr != 0x000000))
fbi[b0_num].fix_mem_start = b0_addr;
/* AF tag */
num = i + 1;
if (((blk_map >> i) & 0x01) == 0x01) {
fbi[num].start = (CTPM_FW[132 + i * 6] << 16) | (CTPM_FW[133 + i * 6] << 8) | CTPM_FW[134 + i * 6];
fbi[num].end = (CTPM_FW[135 + i * 6] << 16) | (CTPM_FW[136 + i * 6] << 8) | CTPM_FW[137 + i * 6];
if (fbi[num].fix_mem_start == 0)
fbi[num].fix_mem_start = INT_MAX;
fbi[num].len = fbi[num].end - fbi[num].start + 1;
ILI_DBG("Block[%d]: start_addr = %x, end = %x, fix_mem_start = 0x%x\n", num, fbi[num].start,
fbi[num].end, fbi[num].fix_mem_start);
if (num == GESTURE)
ilits->gesture_load_code = true;
}
}
memcpy(pfw, CTPM_FW + ILI_FILE_HEADER, size - ILI_FILE_HEADER);
if (ilitek_fw_calc_file_crc(pfw) < 0)
return -1;
tfd.block_number = blk_num;
tfd.end_addr = size - ILI_FILE_HEADER;
return 0;
}
static int ilitek_tddi_fw_hex_convert(u8 *phex, int size, u8 *pfw)
{
int block = 0;
u32 i = 0, j = 0, k = 0, num = 0;
u32 len = 0, addr = 0, type = 0;
u32 start_addr = 0x0, end_addr = 0x0, ex_addr = 0;
u32 offset;
memset(fbi, 0x0, sizeof(fbi));
/* Parsing HEX file */
for (; i < size;) {
len = HexToDec(&phex[i + 1], 2);
addr = HexToDec(&phex[i + 3], 4);
type = HexToDec(&phex[i + 7], 2);
if (type == 0x04) {
ex_addr = HexToDec(&phex[i + 9], 4);
} else if (type == 0x02) {
ex_addr = HexToDec(&phex[i + 9], 4);
ex_addr = ex_addr >> 12;
} else if (type == BLOCK_TAG_AF) {
/* insert block info extracted from hex */
tfd.hex_tag = type;
if (tfd.hex_tag == BLOCK_TAG_AF)
num = HexToDec(&phex[i + 9 + 6 + 6], 2);
else
num = 0xFF;
if (num > (FW_BLOCK_INFO_NUM - 1)) {
ILI_ERR("ERROR! block num is larger than its define (%d, %d)\n",
num, FW_BLOCK_INFO_NUM - 1);
return -EINVAL;
}
fbi[num].start = HexToDec(&phex[i + 9], 6);
fbi[num].end = HexToDec(&phex[i + 9 + 6], 6);
fbi[num].fix_mem_start = INT_MAX;
fbi[num].len = fbi[num].end - fbi[num].start + 1;
ILI_DBG("Block[%d]: start_addr = %x, end = %x", num, fbi[num].start, fbi[num].end);
if (num == GESTURE)
ilits->gesture_load_code = true;
block++;
} else if (type == BLOCK_TAG_B0 && tfd.hex_tag == BLOCK_TAG_AF) {
num = HexToDec(&phex[i + 9 + 6], 2);
if (num > (FW_BLOCK_INFO_NUM - 1)) {
ILI_ERR("ERROR! block num is larger than its define (%d, %d)\n",
num, FW_BLOCK_INFO_NUM - 1);
return -EINVAL;
}
fbi[num].fix_mem_start = HexToDec(&phex[i + 9], 6);
ILI_DBG("Tag 0xB0: change Block[%d] to addr = 0x%x\n", num, fbi[num].fix_mem_start);
}
addr = addr + (ex_addr << 16);
if (phex[i + 1 + 2 + 4 + 2 + (len * 2) + 2] == 0x0D)
offset = 2;
else
offset = 1;
if (addr > MAX_HEX_FILE_SIZE) {
ILI_ERR("Invalid hex format %d\n", addr);
return -1;
}
if (type == 0x00) {
end_addr = addr + len;
if (addr < start_addr)
start_addr = addr;
/* fill data */
for (j = 0, k = 0; j < (len * 2); j += 2, k++)
pfw[addr + k] = HexToDec(&phex[i + 9 + j], 2);
}
i += 1 + 2 + 4 + 2 + (len * 2) + 2 + offset;
}
if (ilitek_fw_calc_file_crc(pfw) < 0)
return -1;
tfd.start_addr = start_addr;
tfd.end_addr = end_addr;
tfd.block_number = block;
return 0;
}
static int ilitek_tdd_fw_hex_open(u8 op, u8 *pfw)
{
int ret = 0, fsize = 0;
const struct firmware *fw = NULL;
struct file *f = NULL;
mm_segment_t old_fs;
loff_t pos = 0;
ILI_INFO("Open file method = %s, path = %s\n",
op ? "FILP_OPEN" : "REQUEST_FIRMWARE",
op ? ilits->md_fw_filp_path : ilits->md_fw_rq_path);
switch (op) {
case REQUEST_FIRMWARE:
if (request_firmware(&fw, ilits->md_fw_rq_path, ilits->dev) < 0) {
ILI_ERR("Request firmware failed, try again\n");
if (request_firmware(&fw, ilits->md_fw_rq_path, ilits->dev) < 0) {
ILI_ERR("Request firmware failed after retry\n");
ret = -1;
goto out;
}
}
fsize = fw->size;
ILI_INFO("fsize = %d\n", fsize);
if (fsize <= 0) {
ILI_ERR("The size of file is invaild\n");
release_firmware(fw);
ret = -1;
goto out;
}
ilits->tp_fw.size = 0;
ilits->tp_fw.data = vmalloc(fsize);
if (ERR_ALLOC_MEM(ilits->tp_fw.data)) {
ILI_ERR("Failed to allocate tp_fw by vmalloc, try again\n");
ilits->tp_fw.data = vmalloc(fsize);
if (ERR_ALLOC_MEM(ilits->tp_fw.data)) {
ILI_ERR("Failed to allocate tp_fw after retry\n");
release_firmware(fw);
ret = -ENOMEM;
goto out;
}
}
/* Copy fw data got from request_firmware to global */
ipio_memcpy((u8 *)ilits->tp_fw.data, fw->data, fsize * sizeof(*fw->data), fsize);
ilits->tp_fw.size = fsize;
release_firmware(fw);
break;
case FILP_OPEN:
f = filp_open(ilits->md_fw_filp_path, O_RDONLY, 0644);
if (ERR_ALLOC_MEM(f)) {
ILI_ERR("Failed to open the file, %ld\n", PTR_ERR(f));
ret = -1;
goto out;
}
fsize = f->f_inode->i_size;
ILI_INFO("fsize = %d\n", fsize);
if (fsize <= 0) {
ILI_ERR("The size of file is invaild\n");
filp_close(f, NULL);
ret = -1;
goto out;
}
ilits->tp_fw.size = 0;
ilits->tp_fw.data = vmalloc(fsize);
if (ERR_ALLOC_MEM(ilits->tp_fw.data)) {
ILI_ERR("Failed to allocate tp_fw by vmalloc, try again\n");
ilits->tp_fw.data = vmalloc(fsize);
if (ERR_ALLOC_MEM(ilits->tp_fw.data)) {
ILI_ERR("Failed to allocate tp_fw after retry\n");
filp_close(f, NULL);
ret = -ENOMEM;
goto out;
}
}
/* ready to map user's memory to obtain data by reading files */
old_fs = get_fs();
set_fs(get_ds());
set_fs(KERNEL_DS);
pos = 0;
vfs_read(f, (u8 *)ilits->tp_fw.data, fsize, &pos);
set_fs(old_fs);
filp_close(f, NULL);
ilits->tp_fw.size = fsize;
break;
default:
ILI_ERR("Unknown open file method, %d\n", op);
break;
}
if (ERR_ALLOC_MEM(ilits->tp_fw.data) || ilits->tp_fw.size <= 0) {
ILI_ERR("fw data/size is invaild\n");
ret = -1;
goto out;
}
/* Convert hex and copy data from tp_fw.data to pfw */
if (ilitek_tddi_fw_hex_convert((u8 *)ilits->tp_fw.data, ilits->tp_fw.size, pfw) < 0) {
ILI_ERR("Convert hex file failed\n");
ret = -1;
}
out:
ipio_vfree((void **)&(ilits->tp_fw.data));
return ret;
}
int ili_fw_upgrade(int op)
{
int i, ret = 0, retry = 3;
if (!ilits->boot || ilits->force_fw_update || ERR_ALLOC_MEM(pfw)) {
ilits->gesture_load_code = false;
if (ERR_ALLOC_MEM(pfw)) {
ipio_vfree((void **)&pfw);
pfw = vmalloc(MAX_HEX_FILE_SIZE * sizeof(u8));
if (ERR_ALLOC_MEM(pfw)) {
ILI_ERR("Failed to allocate pfw memory, %ld\n", PTR_ERR(pfw));
ipio_vfree((void **)&pfw);
ret = -ENOMEM;
goto out;
}
}
for (i = 0; i < MAX_HEX_FILE_SIZE; i++)
pfw[i] = 0xFF;
if (ilitek_tdd_fw_hex_open(op, pfw) < 0) {
ILI_ERR("Open hex file fail, try upgrade from ILI file\n");
/*
* Users might not be aware of a broken hex file when recovering
* fw from ILI file. We should force them to check
* hex files if they attempt to update via device node.
*/
if (ilits->node_update) {
ILI_ERR("Ignore update from ILI file\n");
ipio_vfree((void **)&pfw);
return -EFW_CONVERT_FILE;
}
if (ilitek_tddi_fw_ili_convert(pfw) < 0) {
ILI_ERR("Convert ILI file error\n");
ret = -EFW_CONVERT_FILE;
goto out;
}
}
if (ilitek_tddi_fw_update_block_info(pfw) < 0) {
ret = -EFW_CONVERT_FILE;
goto out;
}
if (ilits->chip->core_ver >= CORE_VER_1470 && ilits->rib.nIsHostDownload == 0) {
ILI_ERR("hex file interface no match error\n");
return -EFW_INTERFACE;
}
}
do {
ret = ilitek_tddi_fw_iram_upgrade(pfw, OFF);
if (ret == UPDATE_PASS)
break;
ILI_ERR("Upgrade failed, do retry!\n");
} while (--retry > 0);
if (ret != UPDATE_PASS) {
ILI_ERR("Failed to upgrade fw %d times, erasing iram\n", retry);
if (ili_reset_ctrl(ilits->reset) < 0)
ILI_ERR("TP reset failed while erasing data\n");
ilits->xch_num = 0;
ilits->ych_num = 0;
return ret;
}
out:
ili_ic_get_core_ver();
ili_ic_get_protocl_ver();
ili_ic_get_fw_ver();
ili_ic_get_tp_info();
ili_ic_get_panel_info();
ili_ic_func_ctrl_reset();
return ret;
}
void ili_fw_read_flash_info(bool mcu)
{
return;
}
void ili_flash_clear_dma(void)
{
return;
}
void ili_flash_dma_write(u32 start, u32 end, u32 len)
{
return;
}
int ili_fw_dump_flash_data(u32 start, u32 end, bool user, bool mcu)
{
return 0;
}
@@ -0,0 +1,454 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2019 MediaTek Inc.
*/
#include "ilitek_v3.h"
struct touch_bus_info {
struct i2c_driver bus_driver;
struct ilitek_hwif_info *hwif;
};
struct ilitek_ts_data *ilits;
#define RW_SYNC 0
#define R_ONLY 1
#define W_ONLY 2
#if I2C_DMA_TRANSFER
static unsigned char *ilitek_dma_va;
static dma_addr_t ilitek_dma_pa;
#define DMA_VA_BUFFER 4096
static int dma_i2c_alloc(struct i2c_client *client)
{
if (client != NULL) {
client->dev.coherent_dma_mask = DMA_BIT_MASK(32);
ilitek_dma_va = (u8 *)dmam_alloc_coherent(client->dev, DMA_VA_BUFFER, &ilitek_dma_pa, GFP_KERNEL);
if (ERR_ALLOC_MEM(ilitek_dma_va)) {
ILI_ERR("Allocate DMA I2C Buffer failed\n");
return -ENOMEM;
}
memset(ilitek_dma_va, 0, DMA_VA_BUFFER);
client->ext_flag |= I2C_DMA_FLAG;
return 0;
}
ILI_ERR("client is NULL, allocated dma i2c failed\n");
return -ENODEV;
}
#endif
static int core_i2c_write(void *buf, int len)
{
int ret = 0;
u8 *txbuf = (u8 *)buf;
u8 check_sum = 0;
u8 *mpbuf = NULL;
struct i2c_msg msgs[] = {
{
.addr = ilits->i2c->addr,
.flags = 0, /* write flag. */
.len = len,
.buf = txbuf,
},
};
#if I2C_DMA_TRANSFER
if (len > 8) {
msgs[0].addr = (ilits->client->addr & I2C_MASK_FLAG);
msgs[0].ext_flag = (ilits->client->ext_flag | I2C_ENEXT_FLAG | I2C_DMA_FLAG);
memcpy(ilitek_dma_va, txbuf, len);
msgs[0].buf = (u8 *)ilitek_dma_pa;
}
#endif
/*
* NOTE: If TP driver is doing MP test and commanding 0xF1 to FW, we add a checksum
* to the last index and plus 1 with size.
*/
if (atomic_read(&ilits->mp_stat) && txbuf[0] == P5_X_SET_CDC_INIT) {
check_sum = ili_calc_packet_checksum(txbuf, len);
mpbuf = kcalloc(len + 1, sizeof(u8), GFP_KERNEL);
if (ERR_ALLOC_MEM(mpbuf)) {
ILI_ERR("Failed to allocate mpbuf mem\n");
return -ENOMEM;
}
ipio_memcpy(mpbuf, txbuf, len, msgs[0].len);
mpbuf[len] = check_sum;
msgs[0].buf = mpbuf;
ili_dump_data(mpbuf, 8, len+1, 0, "mp cdc cmd");
msgs[0].len = len + 1;
}
if (i2c_transfer(ilits->i2c->adapter, msgs, 1) != 1)
ret = -1;
ipio_kfree((void **)&mpbuf);
return ret;
}
static int core_i2c_read(void *buf, int len)
{
int ret;
u8 *rxbuf = (u8 *)buf;
struct i2c_msg msgs[] = {
{
.addr = ilits->i2c->addr,
.flags = I2C_M_RD, /* read flag. */
.len = len,
.buf = rxbuf,
},
};
#if I2C_DMA_TRANSFER
if (len > 8) {
msgs[0].addr = (ilits->client->addr & I2C_MASK_FLAG);
msgs[0].ext_flag = (ilits->client->ext_flag | I2C_ENEXT_FLAG | I2C_DMA_FLAG);
msgs[0].buf = (u8 *)ilitek_dma_pa;
} else {
msgs[0].buf = rxbuf;
}
#endif
ret = i2c_transfer(ilits->i2c->adapter, msgs, 1);
#if I2C_DMA_TRANSFER
if (len > 8)
memcpy(rxbuf, ilitek_dma_va, len);
#endif
/*
* If i2c_transfer is ok (must return 1 because only sends one msg),
* return #bytes transferred, else error code.
*/
return (ret == 1) ? len : ret;
}
static int ilitek_i2c_write(void *buf, int len)
{
int ret = 0;
if (len == 0) {
ILI_ERR("i2c write len is invalid\n");
return -EINVAL;
}
ret = core_i2c_write(buf, len);
if (ret < 0) {
if (atomic_read(&ilits->tp_reset) == START) {
ret = 0;
goto out;
}
ILI_ERR("i2c write error, ret = %d\n", ret);
}
out:
return ret;
}
static int ilitek_i2c_read(void *buf, int len)
{
int ret = 0;
if (len == 0) {
ILI_ERR("i2c read len is invalid\n");
return -EINVAL;
}
ret = core_i2c_read(buf, len);
if (ret < 0) {
if (atomic_read(&ilits->tp_reset) == START) {
ret = 0;
goto out;
}
ILI_ERR("i2c read error, ret = %d\n", ret);
}
out:
return ret;
}
static int ili_i2c_pll_clk_wakeup(void)
{
int ret = 0;
u8 wakeup = 0xA3;
#if (PLL_CLK_WAKEUP_TP_RESUME == ENABLE)
if (ilits->pll_clk_wakeup == true) {
#else
if ((ilits->pll_clk_wakeup == true) && (ilits->tp_suspend == true)) {
#endif
ret = ilitek_i2c_write(&wakeup, sizeof(wakeup));
if (ret < 0)
ILI_ERR("i2c write dummy cmd error\n");
}
return ret;
}
static int ili_i2c_wrapper(u8 *txbuf, u32 wlen, u8 *rxbuf, u32 rlen, bool spi_irq, bool i2c_irq)
{
int ret = 0, operate = -1;
if (wlen > 0) {
if (ERR_ALLOC_MEM(txbuf)) {
ILI_ERR("txbuf is null\n");
return -ENOMEM;
}
}
if (rlen > 0) {
if (ERR_ALLOC_MEM(rxbuf)) {
ILI_ERR("rxbuf is null\n");
return -ENOMEM;
}
}
if (wlen > 0 && rlen > 0)
operate = RW_SYNC;
else if (wlen > 0 && !rlen)
operate = W_ONLY;
else
operate = R_ONLY;
if (ilits->int_pulse)
ilits->detect_int_stat = ili_ic_check_int_pulse;
else
ilits->detect_int_stat = ili_ic_check_int_level;
if (i2c_irq)
atomic_set(&ilits->cmd_int_check, ENABLE);
switch (operate) {
case RW_SYNC:
ret = ilitek_i2c_write(txbuf, wlen);
if (ret < 0) {
ILI_ERR("i2c-wrapper write error\n");
break;
}
if (i2c_irq) {
if (ilits->detect_int_stat(false) < 0) {
ILI_ERR("ERROR! Check INT timeout\n");
ret = -ETIME;
break;
}
} else {
msleep(1);
}
ret = ilitek_i2c_read(rxbuf, rlen);
if (ret < 0) {
ILI_ERR("i2c-wrapper read error\n");
break;
}
break;
case W_ONLY:
ret = ili_i2c_pll_clk_wakeup();
if (ret < 0) {
ILI_ERR("i2c-wrapper dummy cmd write error\n");
}
ret = ilitek_i2c_write(txbuf, wlen);
if (ret < 0) {
ILI_ERR("i2c-wrapper write error\n");
break;
}
break;
case R_ONLY:
if (i2c_irq) {
if (ilits->detect_int_stat(false) < 0) {
ILI_ERR("ERROR! Check INT timeout\n");
ret = -ETIME;
break;
}
}
ret = ilitek_i2c_read(rxbuf, rlen);
if (ret < 0) {
ILI_ERR("i2c-wrapper read error\n");
break;
}
break;
default:
ILI_ERR("Unknown ts-i2c operation\n");
ret = -EINVAL;
break;
}
if (i2c_irq)
atomic_set(&ilits->cmd_int_check, DISABLE);
return ret;
}
int ili_core_spi_setup(int num)
{
ILI_ERR("Not support this interface\n");
return 0;
}
static int ilitek_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
{
struct touch_bus_info *info =
container_of(to_i2c_driver(i2c->dev.driver),
struct touch_bus_info, bus_driver);
ILI_INFO("ilitek i2c probe\n");
if (!i2c) {
ILI_ERR("i2c client is NULL\n");
return -ENODEV;
}
if (i2c->addr != TDDI_I2C_ADDR) {
i2c->addr = TDDI_I2C_ADDR;
ILI_INFO("i2c addr doesn't be set up, use default : 0x%x\n", i2c->addr);
}
if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) {
ILI_ERR("i2c functions are not supported!\n");
return -ENODEV;
}
ilits = devm_kzalloc(&i2c->dev, sizeof(struct ilitek_ts_data), GFP_KERNEL);
if (ERR_ALLOC_MEM(ilits)) {
ILI_ERR("Failed to allocate ts memory, %ld\n", PTR_ERR(ilits));
return -ENOMEM;
}
/* Used for receiving touch data only, do not mix up with others. */
ilits->tr_buf = kzalloc(TR_BUF_SIZE, GFP_ATOMIC);
if (ERR_ALLOC_MEM(ilits->tr_buf)) {
ILI_ERR("failed to allocate touch report buffer\n");
return -ENOMEM;
}
ilits->gcoord = kzalloc(sizeof(struct gesture_coordinate), GFP_KERNEL);
if (ERR_ALLOC_MEM(ilits->gcoord)) {
ILI_ERR("Failed to allocate gresture coordinate buffer\n");
return -ENOMEM;
}
ilits->i2c = i2c;
ilits->spi = NULL;
ilits->dev = &i2c->dev;
ilits->hwif = info->hwif;
ilits->phys = "I2C";
ilits->wrapper = ili_i2c_wrapper;
ilits->detect_int_stat = ili_ic_check_int_pulse;
ilits->int_pulse = true;
ilits->mp_retry = false;
#if I2C_DMA_TRANSFER
if (dma_i2c_alloc(ilits->i2c) < 0)
ILI_ERR("Failed to alllocate DMA mem %ld\n", PTR_ERR(ilits->i2c));
#endif
ilits->actual_tp_mode = P5_X_FW_AP_MODE;
ilits->tp_data_mode = AP_MODE;
if (TDDI_RST_BIND)
ilits->reset = TP_IC_WHOLE_RST;
else
ilits->reset = TP_HW_RST_ONLY;
ilits->rst_edge_delay = 100;
ilits->fw_open = FILP_OPEN;
ilits->fw_upgrade_mode = UPGRADE_FLASH;
ilits->mp_move_code = ili_move_mp_code_flash;
ilits->gesture_move_code = ili_move_gesture_code_flash;
ilits->esd_recover = ili_wq_esd_i2c_check;
ilits->ges_recover = ili_touch_esd_gesture_flash;
ilits->gesture_mode = DATA_FORMAT_GESTURE_INFO;
ilits->gesture_demo_ctrl = DISABLE;
ilits->wtd_ctrl = OFF;
ilits->report = ENABLE;
ilits->netlink = DISABLE;
ilits->dnp = DISABLE;
ilits->irq_tirgger_type = IRQF_TRIGGER_FALLING;
ilits->info_from_hex = DISABLE;
ilits->wait_int_timeout = AP_INT_TIMEOUT;
#if ENABLE_GESTURE
ilits->gesture = DISABLE;
ilits->ges_sym.double_tap = DOUBLE_TAP;
ilits->ges_sym.alphabet_line_2_top = ALPHABET_LINE_2_TOP;
ilits->ges_sym.alphabet_line_2_bottom = ALPHABET_LINE_2_BOTTOM;
ilits->ges_sym.alphabet_line_2_left = ALPHABET_LINE_2_LEFT;
ilits->ges_sym.alphabet_line_2_right = ALPHABET_LINE_2_RIGHT;
ilits->ges_sym.alphabet_m = ALPHABET_M;
ilits->ges_sym.alphabet_w = ALPHABET_W;
ilits->ges_sym.alphabet_c = ALPHABET_C;
ilits->ges_sym.alphabet_E = ALPHABET_E;
ilits->ges_sym.alphabet_V = ALPHABET_V;
ilits->ges_sym.alphabet_O = ALPHABET_O;
ilits->ges_sym.alphabet_S = ALPHABET_S;
ilits->ges_sym.alphabet_Z = ALPHABET_Z;
ilits->ges_sym.alphabet_V_down = ALPHABET_V_DOWN;
ilits->ges_sym.alphabet_V_left = ALPHABET_V_LEFT;
ilits->ges_sym.alphabet_V_right = ALPHABET_V_RIGHT;
ilits->ges_sym.alphabet_two_line_2_bottom = ALPHABET_TWO_LINE_2_BOTTOM;
ilits->ges_sym.alphabet_F = ALPHABET_F;
ilits->ges_sym.alphabet_AT = ALPHABET_AT;
#endif
return info->hwif->plat_probe();
}
static int ilitek_i2c_remove(struct i2c_client *i2c)
{
ILI_INFO();
return 0;
}
static const struct i2c_device_id tp_i2c_id[] = {
{TDDI_DEV_ID, 0},
{},
};
int ili_interface_dev_init(struct ilitek_hwif_info *hwif)
{
struct touch_bus_info *info;
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info) {
ILI_ERR("faied to allocate i2c_driver\n");
return -ENOMEM;
}
if (hwif->bus_type != BUS_I2C) {
ILI_ERR("Not I2C dev\n");
ipio_kfree((void **)&info);
return -EINVAL;
}
hwif->info = info;
info->bus_driver.driver.name = hwif->name;
info->bus_driver.driver.owner = hwif->owner;
info->bus_driver.driver.of_match_table = hwif->of_match_table;
info->bus_driver.driver.pm = hwif->pm;
info->bus_driver.probe = ilitek_i2c_probe;
info->bus_driver.remove = ilitek_i2c_remove;
info->bus_driver.id_table = tp_i2c_id;
info->hwif = hwif;
return i2c_add_driver(&info->bus_driver);
}
void ili_interface_dev_exit(struct ilitek_ts_data *ts)
{
struct touch_bus_info *info = (struct touch_bus_info *)ilits->hwif->info;
ILI_INFO("remove i2c dev\n");
i2c_del_driver(&info->bus_driver);
ipio_kfree((void **)&info);
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,450 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2019 MediaTek Inc.
*/
#include "ilitek_v3.h"
#define DTS_INT_GPIO "touch,irq-gpio"
#define DTS_RESET_GPIO "touch,reset-gpio"
#define DTS_OF_NAME "ilitek"
#define MTK_RST_GPIO GTP_RST_PORT
#define MTK_INT_GPIO GTP_INT_PORT
extern struct tpd_device *tpd;
void ili_tp_reset(void)
{
ILI_INFO("edge delay = %d\n", ilits->rst_edge_delay);
/* Need accurate power sequence, do not change it to msleep */
tpd_gpio_output(ilits->tp_rst, 1);
mdelay(1);
tpd_gpio_output(ilits->tp_rst, 0);
mdelay(5);
tpd_gpio_output(ilits->tp_rst, 1);
mdelay(ilits->rst_edge_delay);
}
void ili_input_register(void)
{
int i;
ilits->input = tpd->dev;
if (tpd_dts_data.use_tpd_button) {
for (i = 0; i < tpd_dts_data.tpd_key_num; i++)
input_set_capability(ilits->input, EV_KEY, tpd_dts_data.tpd_key_local[i]);
}
/* set the supported event type for input device */
set_bit(EV_ABS, ilits->input->evbit);
set_bit(EV_SYN, ilits->input->evbit);
set_bit(EV_KEY, ilits->input->evbit);
set_bit(BTN_TOUCH, ilits->input->keybit);
set_bit(BTN_TOOL_FINGER, ilits->input->keybit);
set_bit(INPUT_PROP_DIRECT, ilits->input->propbit);
if (MT_PRESSURE)
input_set_abs_params(ilits->input, ABS_MT_PRESSURE, 0, 255, 0, 0);
if (MT_B_TYPE) {
#if KERNEL_VERSION(3, 7, 0) <= LINUX_VERSION_CODE
input_mt_init_slots(ilits->input, MAX_TOUCH_NUM, INPUT_MT_DIRECT);
#else
input_mt_init_slots(ilits->input, MAX_TOUCH_NUM);
#endif /* LINUX_VERSION_CODE */
} else {
input_set_abs_params(ilits->input, ABS_MT_TRACKING_ID, 0, MAX_TOUCH_NUM, 0, 0);
}
/* Gesture keys register */
input_set_capability(ilits->input, EV_KEY, KEY_POWER);
input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_UP);
input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_DOWN);
input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_LEFT);
input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_RIGHT);
input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_O);
input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_E);
input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_M);
input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_W);
input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_S);
input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_V);
input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_Z);
input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_C);
input_set_capability(ilits->input, EV_KEY, KEY_GESTURE_F);
__set_bit(KEY_GESTURE_POWER, ilits->input->keybit);
__set_bit(KEY_GESTURE_UP, ilits->input->keybit);
__set_bit(KEY_GESTURE_DOWN, ilits->input->keybit);
__set_bit(KEY_GESTURE_LEFT, ilits->input->keybit);
__set_bit(KEY_GESTURE_RIGHT, ilits->input->keybit);
__set_bit(KEY_GESTURE_O, ilits->input->keybit);
__set_bit(KEY_GESTURE_E, ilits->input->keybit);
__set_bit(KEY_GESTURE_M, ilits->input->keybit);
__set_bit(KEY_GESTURE_W, ilits->input->keybit);
__set_bit(KEY_GESTURE_S, ilits->input->keybit);
__set_bit(KEY_GESTURE_V, ilits->input->keybit);
__set_bit(KEY_GESTURE_Z, ilits->input->keybit);
__set_bit(KEY_GESTURE_C, ilits->input->keybit);
__set_bit(KEY_GESTURE_F, ilits->input->keybit);
}
#if REGULATOR_POWER
void ili_plat_regulator_power_on(bool status)
{
ILI_INFO("%s\n", status ? "POWER ON" : "POWER OFF");
if (status) {
if (ilits->vdd) {
if (regulator_enable(ilits->vdd) < 0)
ILI_ERR("regulator_enable VDD fail\n");
}
if (ilits->vcc) {
if (regulator_enable(ilits->vcc) < 0)
ILI_ERR("regulator_enable VCC fail\n");
}
} else {
if (ilits->vdd) {
if (regulator_disable(ilits->vdd) < 0)
ILI_ERR("regulator_enable VDD fail\n");
}
if (ilits->vcc) {
if (regulator_disable(ilits->vcc) < 0)
ILI_ERR("regulator_enable VCC fail\n");
}
}
atomic_set(&ilits->ice_stat, DISABLE);
mdelay(5);
}
static void ilitek_plat_regulator_power_init(void)
{
const char *vdd_name = "vdd";
const char *vcc_name = "vcc";
ilits->vdd = regulator_get(tpd->tpd_dev, vdd_name);
if (ERR_ALLOC_MEM(ilits->vdd)) {
ILI_ERR("regulator_get VDD fail\n");
ilits->vdd = NULL;
}
tpd->reg = ilits->vdd;
if (regulator_set_voltage(ilits->vdd, VDD_VOLTAGE, VDD_VOLTAGE) < 0)
ILI_ERR("Failed to set VDD %d\n", VDD_VOLTAGE);
ilits->vcc = regulator_get(ilits->dev, vcc_name);
if (ERR_ALLOC_MEM(ilits->vcc)) {
ILI_ERR("regulator_get VCC fail.\n");
ilits->vcc = NULL;
}
if (regulator_set_voltage(ilits->vcc, VCC_VOLTAGE, VCC_VOLTAGE) < 0)
ILI_ERR("Failed to set VCC %d\n", VCC_VOLTAGE);
ili_plat_regulator_power_on(true);
}
#endif
static int ilitek_plat_gpio_register(void)
{
int ret = 0;
ilits->tp_int = MTK_INT_GPIO;
ilits->tp_rst = MTK_RST_GPIO;
ILI_INFO("TP INT: %d\n", ilits->tp_int);
ILI_INFO("TP RESET: %d\n", ilits->tp_rst);
if (!gpio_is_valid(ilits->tp_int)) {
ILI_ERR("Invalid INT gpio: %d\n", ilits->tp_int);
return -EBADR;
}
if (!gpio_is_valid(ilits->tp_rst)) {
ILI_ERR("Invalid RESET gpio: %d\n", ilits->tp_rst);
return -EBADR;
}
gpio_direction_input(ilits->tp_int);
return ret;
}
void ili_irq_disable(void)
{
unsigned long flag;
spin_lock_irqsave(&ilits->irq_spin, flag);
if (atomic_read(&ilits->irq_stat) == DISABLE)
goto out;
if (!ilits->irq_num) {
ILI_ERR("gpio_to_irq (%d) is incorrect\n", ilits->irq_num);
goto out;
}
disable_irq_nosync(ilits->irq_num);
atomic_set(&ilits->irq_stat, DISABLE);
ILI_DBG("Disable irq success\n");
out:
spin_unlock_irqrestore(&ilits->irq_spin, flag);
}
void ili_irq_enable(void)
{
unsigned long flag;
spin_lock_irqsave(&ilits->irq_spin, flag);
if (atomic_read(&ilits->irq_stat) == ENABLE)
goto out;
if (!ilits->irq_num) {
ILI_ERR("gpio_to_irq (%d) is incorrect\n", ilits->irq_num);
goto out;
}
enable_irq(ilits->irq_num);
atomic_set(&ilits->irq_stat, ENABLE);
ILI_DBG("Enable irq success\n");
out:
spin_unlock_irqrestore(&ilits->irq_spin, flag);
}
static irqreturn_t ilitek_plat_isr_top_half(int irq, void *dev_id)
{
if (irq != ilits->irq_num) {
ILI_ERR("Incorrect irq number (%d)\n", irq);
return IRQ_NONE;
}
if (atomic_read(&ilits->cmd_int_check) == ENABLE) {
atomic_set(&ilits->cmd_int_check, DISABLE);
ILI_DBG("CMD INT detected, ignore\n");
wake_up(&(ilits->inq));
return IRQ_HANDLED;
}
if (ilits->prox_near) {
ILI_INFO("Proximity event, ignore interrupt!\n");
return IRQ_HANDLED;
}
ILI_DBG("report: %d, rst: %d, fw: %d, switch: %d, mp: %d, sleep: %d, esd: %d\n",
ilits->report,
atomic_read(&ilits->tp_reset),
atomic_read(&ilits->fw_stat),
atomic_read(&ilits->tp_sw_mode),
atomic_read(&ilits->mp_stat),
atomic_read(&ilits->tp_sleep),
atomic_read(&ilits->esd_stat));
if (!ilits->report || atomic_read(&ilits->tp_reset) ||
atomic_read(&ilits->fw_stat) || atomic_read(&ilits->tp_sw_mode) ||
atomic_read(&ilits->mp_stat) || atomic_read(&ilits->tp_sleep) ||
atomic_read(&ilits->esd_stat)) {
ILI_DBG("ignore interrupt !\n");
return IRQ_HANDLED;
}
return IRQ_WAKE_THREAD;
}
static irqreturn_t ilitek_plat_isr_bottom_half(int irq, void *dev_id)
{
if (mutex_is_locked(&ilits->touch_mutex)) {
ILI_DBG("touch is locked, ignore\n");
return IRQ_HANDLED;
}
mutex_lock(&ilits->touch_mutex);
ili_report_handler();
mutex_unlock(&ilits->touch_mutex);
return IRQ_HANDLED;
}
void ili_irq_unregister(void)
{
devm_free_irq(ilits->dev, ilits->irq_num, NULL);
}
int ili_irq_register(int type)
{
int ret = 0;
static bool get_irq_pin;
struct device_node *node;
atomic_set(&ilits->irq_stat, DISABLE);
if (get_irq_pin == false) {
node = of_find_matching_node(NULL, touch_of_match);
if (node)
ilits->irq_num = irq_of_parse_and_map(node, 0);
ILI_INFO("ilits->irq_num = %d\n", ilits->irq_num);
get_irq_pin = true;
}
ret = devm_request_threaded_irq(ilits->dev, ilits->irq_num,
ilitek_plat_isr_top_half,
ilitek_plat_isr_bottom_half,
type | IRQF_ONESHOT, "ilitek", NULL);
if (type == IRQF_TRIGGER_FALLING)
ILI_INFO("IRQ TYPE = IRQF_TRIGGER_FALLING\n");
if (type == IRQF_TRIGGER_RISING)
ILI_INFO("IRQ TYPE = IRQF_TRIGGER_RISING\n");
if (ret != 0)
ILI_ERR("Failed to register irq handler, irq = %d, ret = %d\n", ilits->irq_num, ret);
atomic_set(&ilits->irq_stat, ENABLE);
return ret;
}
static void tpd_resume(struct device *h)
{
if (ili_sleep_handler(TP_RESUME) < 0)
ILI_ERR("TP resume failed\n");
}
static void tpd_suspend(struct device *h)
{
if (ili_sleep_handler(TP_DEEP_SLEEP) < 0)
ILI_ERR("TP suspend failed\n");
}
static int ilitek_tp_pm_suspend(struct device *dev)
{
ILI_INFO("CALL BACK TP PM SUSPEND");
ilits->pm_suspend = true;
reinit_completion(&ilits->pm_completion);
return 0;
}
static int ilitek_tp_pm_resume(struct device *dev)
{
ILI_INFO("CALL BACK TP PM RESUME");
ilits->pm_suspend = false;
complete(&ilits->pm_completion);
return 0;
}
static int ilitek_plat_probe(void)
{
ILI_INFO("platform probe\n");
#if REGULATOR_POWER
ilitek_plat_regulator_power_init();
#endif
if (ilitek_plat_gpio_register() < 0)
ILI_ERR("Register gpio failed\n");
ili_update_tp_module_info();
ili_irq_register(ilits->irq_tirgger_type);
if (ili_tddi_init() < 0) {
ILI_ERR("ILITEK Driver probe failed\n");
ili_irq_unregister();
return -ENODEV;
}
tpd_load_status = 1;
ilits->pm_suspend = false;
init_completion(&ilits->pm_completion);
ILI_INFO("ILITEK Driver loaded successfully!");
return 0;
}
static int ilitek_plat_remove(void)
{
ILI_INFO("remove plat dev\n");
ili_dev_remove();
return 0;
}
static const struct dev_pm_ops tp_pm_ops = {
.suspend = ilitek_tp_pm_suspend,
.resume = ilitek_tp_pm_resume,
};
static const struct of_device_id tp_match_table[] = {
{.compatible = DTS_OF_NAME},
{},
};
#ifdef ROI
struct ts_device_ops ilitek_ops = {
.chip_roi_rawdata = ili_knuckle_roi_rawdata,
.chip_roi_switch = ili_knuckle_roi_switch,
};
#endif
static struct ilitek_hwif_info hwif = {
.bus_type = TDDI_INTERFACE,
.plat_type = TP_PLAT_MTK,
.owner = THIS_MODULE,
.name = TDDI_DEV_ID,
.of_match_table = of_match_ptr(tp_match_table),
.plat_probe = ilitek_plat_probe,
.plat_remove = ilitek_plat_remove,
.pm = &tp_pm_ops,
};
static int tpd_local_init(void)
{
ILI_INFO("TPD init device driver\n");
if (ili_dev_init(&hwif) < 0) {
ILI_ERR("Failed to register i2c/spi bus driver\n");
return -ENODEV;
}
if (tpd_load_status == 0) {
ILI_ERR("Add error touch panel driver\n");
return -1;
}
if (tpd_dts_data.use_tpd_button) {
tpd_button_setting(tpd_dts_data.tpd_key_num, tpd_dts_data.tpd_key_local,
tpd_dts_data.tpd_key_dim_local);
}
tpd_type_cap = 1;
return 0;
}
static struct tpd_driver_t tpd_device_driver = {
.tpd_device_name = TDDI_DEV_ID,
.tpd_local_init = tpd_local_init,
.suspend = tpd_suspend,
.resume = tpd_resume,
};
static int __init ilitek_plat_dev_init(void)
{
int ret = 0;
ILI_INFO("ILITEK TP driver init for MTK\n");
tpd_get_dts_info();
ret = tpd_driver_add(&tpd_device_driver);
if (ret < 0) {
ILI_ERR("ILITEK add TP driver failed\n");
tpd_driver_remove(&tpd_device_driver);
return -ENODEV;
}
return 0;
}
static void __exit ilitek_plat_dev_exit(void)
{
ILI_INFO("ilitek driver has been removed\n");
tpd_driver_remove(&tpd_device_driver);
}
module_init(ilitek_plat_dev_init);
module_exit(ilitek_plat_dev_exit);
MODULE_AUTHOR("ILITEK");
MODULE_LICENSE("GPL");
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,611 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2019 MediaTek Inc.
*/
#include "ilitek_v3.h"
struct touch_bus_info {
struct spi_driver bus_driver;
struct ilitek_hwif_info *hwif;
};
struct ilitek_ts_data *ilits;
#if SPI_DMA_TRANSFER_SPLIT
#define DMA_TRANSFER_MAX_CHUNK 4 // number of chunks to be transferred.
#define DMA_TRANSFER_MAX_LEN 4096 // length of a chunk.
int ili_spi_write_then_read_split(struct spi_device *spi,
const void *txbuf, unsigned n_tx,
void *rxbuf, unsigned n_rx)
{
int status = -1, duplex_len = 0;
int xfercnt = 0, xferlen = 0, xferloop = 0;
int offset = 0;
u8 cmd = 0;
struct spi_message message;
struct spi_transfer xfer[DMA_TRANSFER_MAX_CHUNK];
if (n_rx > SPI_RX_BUF_SIZE) {
ILI_ERR("Rx length is greater than spi local buf, abort\n");
status = -ENOMEM;
goto out;
}
spi_message_init(&message);
memset(xfer, 0, sizeof(xfer));
memset(ilits->spi_tx, 0x0, SPI_TX_BUF_SIZE);
memset(ilits->spi_rx, 0x0, SPI_RX_BUF_SIZE);
if ((n_tx > 0) && (n_rx > 0))
cmd = SPI_READ;
else
cmd = SPI_WRITE;
switch (cmd) {
case SPI_WRITE:
if (n_tx % DMA_TRANSFER_MAX_LEN)
xferloop = (n_tx / DMA_TRANSFER_MAX_LEN) + 1;
else
xferloop = n_tx / DMA_TRANSFER_MAX_LEN;
if (xferloop > DMA_TRANSFER_MAX_CHUNK) {
ILI_ERR("xferloop = %d > %d\n", xferloop, DMA_TRANSFER_MAX_CHUNK);
status = -EINVAL;
break;
}
xferlen = n_tx;
memcpy(ilits->spi_tx, (u8 *)txbuf, xferlen);
for (xfercnt = 0; xfercnt < xferloop; xfercnt++) {
if (xferlen > DMA_TRANSFER_MAX_LEN)
xferlen = DMA_TRANSFER_MAX_LEN;
xfer[xfercnt].len = xferlen;
xfer[xfercnt].tx_buf = ilits->spi_tx + xfercnt * DMA_TRANSFER_MAX_LEN;
spi_message_add_tail(&xfer[xfercnt], &message);
xferlen = n_tx - (xfercnt+1) * DMA_TRANSFER_MAX_LEN;
}
status = spi_sync(spi, &message);
break;
case SPI_READ:
if (n_tx > DMA_TRANSFER_MAX_LEN) {
ILI_ERR("Tx length must be lower than dma length (%d).\n", DMA_TRANSFER_MAX_LEN);
status = -EINVAL;
break;
}
if (!atomic_read(&ilits->ice_stat))
offset = 2;
memcpy(ilits->spi_tx, txbuf, n_tx);
duplex_len = n_tx + n_rx + offset;
if (duplex_len % DMA_TRANSFER_MAX_LEN)
xferloop = (duplex_len / DMA_TRANSFER_MAX_LEN) + 1;
else
xferloop = duplex_len / DMA_TRANSFER_MAX_LEN;
if (xferloop > DMA_TRANSFER_MAX_CHUNK) {
ILI_ERR("xferloop = %d > %d\n", xferloop, DMA_TRANSFER_MAX_CHUNK);
status = -EINVAL;
break;
}
xferlen = duplex_len;
for (xfercnt = 0; xfercnt < xferloop; xfercnt++) {
if (xferlen > DMA_TRANSFER_MAX_LEN)
xferlen = DMA_TRANSFER_MAX_LEN;
xfer[xfercnt].len = xferlen;
xfer[xfercnt].tx_buf = ilits->spi_tx;
xfer[xfercnt].rx_buf = ilits->spi_rx + xfercnt * DMA_TRANSFER_MAX_LEN;
spi_message_add_tail(&xfer[xfercnt], &message);
xferlen = duplex_len - (xfercnt + 1) * DMA_TRANSFER_MAX_LEN;
}
status = spi_sync(spi, &message);
if (status == 0) {
if (ilits->spi_rx[1] != SPI_ACK && !atomic_read(&ilits->ice_stat)) {
status = DO_SPI_RECOVER;
ILI_ERR("Do spi recovery: rxbuf[1] = 0x%x, ice = %d\n", ilits->spi_rx[1], atomic_read(&ilits->ice_stat));
break;
}
memcpy((u8 *)rxbuf, ilits->spi_rx + offset + 1, n_rx);
} else {
ILI_ERR("spi read fail, status = %d\n", status);
}
break;
default:
ILI_INFO("Unknown command 0x%x\n", cmd);
break;
}
out:
return status;
}
#else
int ili_spi_write_then_read_direct(struct spi_device *spi,
const void *txbuf, unsigned n_tx,
void *rxbuf, unsigned n_rx)
{
int status = -1, duplex_len = 0;
int offset = 0;
u8 cmd;
struct spi_message message;
struct spi_transfer xfer;
if (n_rx > SPI_RX_BUF_SIZE) {
ILI_ERR("Rx length is greater than spi local buf, abort\n");
status = -ENOMEM;
goto out;
}
spi_message_init(&message);
memset(&xfer, 0, sizeof(xfer));
if ((n_tx > 0) && (n_rx > 0))
cmd = SPI_READ;
else
cmd = SPI_WRITE;
switch (cmd) {
case SPI_WRITE:
xfer.len = n_tx;
xfer.tx_buf = txbuf;
spi_message_add_tail(&xfer, &message);
status = spi_sync(spi, &message);
break;
case SPI_READ:
if (!atomic_read(&ilits->ice_stat))
offset = 2;
duplex_len = n_tx + n_rx + offset;
if ((duplex_len > SPI_TX_BUF_SIZE) ||
(duplex_len > SPI_RX_BUF_SIZE)) {
ILI_ERR("duplex_len is over than dma buf, abort\n");
status = -ENOMEM;
break;
}
memset(ilits->spi_tx, 0x0, SPI_TX_BUF_SIZE);
memset(ilits->spi_rx, 0x0, SPI_RX_BUF_SIZE);
xfer.len = duplex_len;
memcpy(ilits->spi_tx, txbuf, n_tx);
xfer.tx_buf = ilits->spi_tx;
xfer.rx_buf = ilits->spi_rx;
spi_message_add_tail(&xfer, &message);
status = spi_sync(spi, &message);
if (status == 0) {
if (ilits->spi_rx[1] != SPI_ACK && !atomic_read(&ilits->ice_stat)) {
status = DO_SPI_RECOVER;
ILI_ERR("Do spi recovery: rxbuf[1] = 0x%x, ice = %d\n", ilits->spi_rx[1], atomic_read(&ilits->ice_stat));
break;
}
memcpy((u8 *)rxbuf, ilits->spi_rx + offset + 1, n_rx);
} else {
ILI_ERR("spi read fail, status = %d\n", status);
}
break;
default:
ILI_INFO("Unknown command 0x%x\n", cmd);
break;
}
out:
return status;
}
#endif
static int ili_spi_mp_pre_cmd(u8 cdc)
{
u8 pre[5] = {0};
if (!atomic_read(&ilits->mp_stat) || cdc != P5_X_SET_CDC_INIT ||
ilits->chip->core_ver >= CORE_VER_1430)
return 0;
ILI_DBG("mp test with pre commands\n");
pre[0] = SPI_WRITE;
pre[1] = 0x0;// dummy byte
pre[2] = 0x2;// Write len byte
pre[3] = P5_X_READ_DATA_CTRL;
pre[4] = P5_X_GET_CDC_DATA;
if (ilits->spi_write_then_read(ilits->spi, pre, 5, NULL, 0) < 0) {
ILI_ERR("Failed to write pre commands\n");
return -1;
}
pre[0] = SPI_WRITE;
pre[1] = 0x0;// dummy byte
pre[2] = 0x1;// Write len byte
pre[3] = P5_X_GET_CDC_DATA;
if (ilits->spi_write_then_read(ilits->spi, pre, 4, NULL, 0) < 0) {
ILI_ERR("Failed to write pre commands\n");
return -1;
}
return 0;
}
static int ili_spi_pll_clk_wakeup(void)
{
int index = 0;
u8 wdata[32] = {0};
u8 wakeup[9] = {0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3};
u32 wlen = sizeof(wakeup);
wdata[0] = SPI_WRITE;
wdata[1] = wlen >> 8;
wdata[2] = wlen & 0xff;
index = 3;
wlen += index;
ipio_memcpy(&wdata[index], wakeup, wlen, wlen);
ILI_INFO("Write dummy to wake up spi pll clk\n");
if (ilits->spi_write_then_read(ilits->spi, wdata, wlen, NULL, 0) < 0) {
ILI_INFO("spi slave write error\n");
return -1;
}
return 0;
}
static int ili_spi_wrapper(u8 *txbuf, u32 wlen, u8 *rxbuf, u32 rlen, bool spi_irq, bool i2c_irq)
{
int ret = 0;
int mode = 0, index = 0;
u8 wdata[128] = {0};
u8 checksum = 0;
bool ice = atomic_read(&ilits->ice_stat);
if (wlen > 0) {
if (!txbuf) {
ILI_ERR("txbuf is null\n");
return -ENOMEM;
}
/* 3 bytes data consist of length and header */
if ((wlen + 3) > sizeof(wdata)) {
ILI_ERR("WARNING! wlen(%d) > wdata(%d), using wdata length to transfer\n", wlen, (int)sizeof(wdata));
wlen = sizeof(wdata);
}
}
if (rlen > 0) {
if (!rxbuf) {
ILI_ERR("rxbuf is null\n");
return -ENOMEM;
}
}
if (rlen > 0 && !wlen)
mode = SPI_READ;
else
mode = SPI_WRITE;
if (ilits->int_pulse)
ilits->detect_int_stat = ili_ic_check_int_pulse;
else
ilits->detect_int_stat = ili_ic_check_int_level;
if (spi_irq)
atomic_set(&ilits->cmd_int_check, ENABLE);
switch (mode) {
case SPI_WRITE:
#if (PLL_CLK_WAKEUP_TP_RESUME == ENABLE)
if (ilits->pll_clk_wakeup == true) {
#else
if ((ilits->pll_clk_wakeup == true) && (ilits->tp_suspend == true)) {
#endif
ret = ili_spi_pll_clk_wakeup();
if (ret < 0) {
ILI_ERR("Wakeup pll clk error\n");
break;
}
}
if (ice) {
wdata[0] = SPI_WRITE;
index = 1;
} else {
wdata[0] = SPI_WRITE;
wdata[1] = wlen >> 8;
wdata[2] = wlen & 0xff;
index = 3;
}
wlen += index;
ipio_memcpy(&wdata[index], txbuf, wlen, wlen);
/*
* NOTE: If TP driver is doing MP test and commanding 0xF1 to FW, we add a checksum
* to the last index and plus 1 with size.
*/
if (atomic_read(&ilits->mp_stat) && wdata[index] == P5_X_SET_CDC_INIT) {
checksum = ili_calc_packet_checksum(&wdata[index], wlen - index);
wdata[wlen] = checksum;
wlen++;
wdata[1] = (wlen - index) >> 8;
wdata[2] = (wlen - index) & 0xff;
ili_dump_data(wdata, 8, wlen, 0, "mp cdc cmd with checksum");
}
ret = ilits->spi_write_then_read(ilits->spi, wdata, wlen, txbuf, 0);
if (ret < 0) {
ILI_INFO("spi-wrapper write error\n");
break;
}
/* Won't break if it needs to read data following with writing. */
if (!rlen)
break;
case SPI_READ:
if (!ice && spi_irq) {
/* Check INT triggered by FW when sending cmds. */
if (ilits->detect_int_stat(false) < 0) {
ILI_ERR("ERROR! Check INT timeout\n");
ret = -ETIME;
break;
}
}
ret = ili_spi_mp_pre_cmd(wdata[3]);
if (ret < 0)
ILI_ERR("spi-wrapper mp pre cmd error\n");
wdata[0] = SPI_READ;
ret = ilits->spi_write_then_read(ilits->spi, wdata, 1, rxbuf, rlen);
if (ret < 0)
ILI_ERR("spi-wrapper read error\n");
break;
default:
ILI_ERR("Unknown spi mode (%d)\n", mode);
ret = -EINVAL;
break;
}
if (spi_irq)
atomic_set(&ilits->cmd_int_check, DISABLE);
return ret;
}
int ili_core_spi_setup(int num)
{
u32 freq[] = {
TP_SPI_CLK_1M,
TP_SPI_CLK_2M,
TP_SPI_CLK_3M,
TP_SPI_CLK_4M,
TP_SPI_CLK_5M,
TP_SPI_CLK_6M,
TP_SPI_CLK_7M,
TP_SPI_CLK_8M,
TP_SPI_CLK_9M,
TP_SPI_CLK_10M,
TP_SPI_CLK_11M,
TP_SPI_CLK_12M,
TP_SPI_CLK_13M,
TP_SPI_CLK_14M,
TP_SPI_CLK_15M
};
if (num > sizeof(freq)) {
ILI_ERR("Invaild clk freq, set default clk freq\n");
num = 7;
}
ILI_INFO("spi clock = %d\n", freq[num]);
ilits->spi->mode = SPI_MODE_0;
ilits->spi->bits_per_word = 8;
ilits->spi->max_speed_hz = freq[num];
if (spi_setup(ilits->spi) < 0) {
ILI_ERR("Failed to setup spi device\n");
return -ENODEV;
}
ILI_INFO("name = %s, bus_num = %d,cs = %d, mode = %d, speed = %d\n",
ilits->spi->modalias,
ilits->spi->master->bus_num,
ilits->spi->chip_select,
ilits->spi->mode,
ilits->spi->max_speed_hz);
return 0;
}
static int ilitek_spi_probe(struct spi_device *spi)
{
struct touch_bus_info *info =
container_of(to_spi_driver(spi->dev.driver),
struct touch_bus_info, bus_driver);
ILI_INFO("ilitek spi probe\n");
tpd_gpio_mode_set();
if (!spi) {
ILI_ERR("spi device is NULL\n");
return -ENODEV;
}
ilits = devm_kzalloc(&spi->dev, sizeof(struct ilitek_ts_data), GFP_KERNEL);
if (ERR_ALLOC_MEM(ilits)) {
ILI_ERR("Failed to allocate ts memory, %ld\n", PTR_ERR(ilits));
return -ENOMEM;
}
if (spi->master->flags & SPI_MASTER_HALF_DUPLEX) {
ILI_ERR("Full duplex not supported by master\n");
return -EIO;
}
ilits->update_buf = kzalloc(MAX_HEX_FILE_SIZE, GFP_KERNEL | GFP_DMA);
if (ERR_ALLOC_MEM(ilits->update_buf)) {
ILI_ERR("fw kzalloc error\n");
return -ENOMEM;
}
/* Used for receiving touch data only, do not mix up with others. */
ilits->tr_buf = kzalloc(TR_BUF_SIZE, GFP_ATOMIC);
if (ERR_ALLOC_MEM(ilits->tr_buf)) {
ILI_ERR("failed to allocate touch report buffer\n");
return -ENOMEM;
}
ilits->spi_tx = kzalloc(SPI_TX_BUF_SIZE, GFP_KERNEL | GFP_DMA);
if (ERR_ALLOC_MEM(ilits->spi_tx)) {
ILI_ERR("Failed to allocate spi tx buffer\n");
return -ENOMEM;
}
ilits->spi_rx = kzalloc(SPI_RX_BUF_SIZE, GFP_KERNEL | GFP_DMA);
if (ERR_ALLOC_MEM(ilits->spi_rx)) {
ILI_ERR("Failed to allocate spi rx buffer\n");
return -ENOMEM;
}
ilits->gcoord = kzalloc(sizeof(struct gesture_coordinate), GFP_KERNEL);
if (ERR_ALLOC_MEM(ilits->gcoord)) {
ILI_ERR("Failed to allocate gresture coordinate buffer\n");
return -ENOMEM;
}
ilits->i2c = NULL;
ilits->spi = spi;
ilits->dev = &spi->dev;
ilits->hwif = info->hwif;
ilits->phys = "SPI";
ilits->wrapper = ili_spi_wrapper;
ilits->detect_int_stat = ili_ic_check_int_pulse;
ilits->int_pulse = true;
ilits->mp_retry = false;
#if SPI_DMA_TRANSFER_SPLIT
ilits->spi_write_then_read = ili_spi_write_then_read_split;
#else
ilits->spi_write_then_read = ili_spi_write_then_read_direct;
#endif
ilits->actual_tp_mode = P5_X_FW_AP_MODE;
ilits->tp_data_format = DATA_FORMAT_DEMO;
ilits->tp_data_len = P5_X_DEMO_MODE_PACKET_LEN;
ilits->tp_data_mode = AP_MODE;
if (TDDI_RST_BIND)
ilits->reset = TP_IC_WHOLE_RST;
else
ilits->reset = TP_HW_RST_ONLY;
ilits->rst_edge_delay = 10;
ilits->fw_open = FILP_OPEN;
ilits->fw_upgrade_mode = UPGRADE_IRAM;
ilits->mp_move_code = ili_move_mp_code_iram;
ilits->gesture_move_code = ili_move_gesture_code_iram;
ilits->esd_recover = ili_wq_esd_spi_check;
ilits->ges_recover = ili_touch_esd_gesture_iram;
ilits->gesture_mode = DATA_FORMAT_GESTURE_INFO;
ilits->gesture_demo_ctrl = DISABLE;
ilits->wtd_ctrl = OFF;
ilits->report = ENABLE;
ilits->netlink = DISABLE;
ilits->dnp = DISABLE;
ilits->irq_tirgger_type = IRQF_TRIGGER_FALLING;
ilits->info_from_hex = ENABLE;
ilits->wait_int_timeout = AP_INT_TIMEOUT;
#if ENABLE_GESTURE
ilits->gesture = DISABLE;
ilits->ges_sym.double_tap = DOUBLE_TAP;
ilits->ges_sym.alphabet_line_2_top = ALPHABET_LINE_2_TOP;
ilits->ges_sym.alphabet_line_2_bottom = ALPHABET_LINE_2_BOTTOM;
ilits->ges_sym.alphabet_line_2_left = ALPHABET_LINE_2_LEFT;
ilits->ges_sym.alphabet_line_2_right = ALPHABET_LINE_2_RIGHT;
ilits->ges_sym.alphabet_m = ALPHABET_M;
ilits->ges_sym.alphabet_w = ALPHABET_W;
ilits->ges_sym.alphabet_c = ALPHABET_C;
ilits->ges_sym.alphabet_E = ALPHABET_E;
ilits->ges_sym.alphabet_V = ALPHABET_V;
ilits->ges_sym.alphabet_O = ALPHABET_O;
ilits->ges_sym.alphabet_S = ALPHABET_S;
ilits->ges_sym.alphabet_Z = ALPHABET_Z;
ilits->ges_sym.alphabet_V_down = ALPHABET_V_DOWN;
ilits->ges_sym.alphabet_V_left = ALPHABET_V_LEFT;
ilits->ges_sym.alphabet_V_right = ALPHABET_V_RIGHT;
ilits->ges_sym.alphabet_two_line_2_bottom = ALPHABET_TWO_LINE_2_BOTTOM;
ilits->ges_sym.alphabet_F = ALPHABET_F;
ilits->ges_sym.alphabet_AT = ALPHABET_AT;
#endif
if (ili_core_spi_setup(SPI_CLK) < 0)
return -EINVAL;
return info->hwif->plat_probe();
}
static int ilitek_spi_remove(struct spi_device *spi)
{
ILI_INFO();
return 0;
}
static struct spi_device_id tp_spi_id[] = {
{TDDI_DEV_ID, 0},
{},
};
int ili_interface_dev_init(struct ilitek_hwif_info *hwif)
{
struct touch_bus_info *info;
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info) {
ILI_ERR("faied to allocate spi_driver\n");
return -ENOMEM;
}
if (hwif->bus_type != BUS_SPI) {
ILI_ERR("Not SPI dev\n");
ipio_kfree((void **)&info);
return -EINVAL;
}
hwif->info = info;
info->bus_driver.driver.name = hwif->name;
info->bus_driver.driver.owner = hwif->owner;
info->bus_driver.driver.of_match_table = hwif->of_match_table;
info->bus_driver.driver.pm = hwif->pm;
info->bus_driver.probe = ilitek_spi_probe;
info->bus_driver.remove = ilitek_spi_remove;
info->bus_driver.id_table = tp_spi_id;
info->hwif = hwif;
return spi_register_driver(&info->bus_driver);
}
void ili_interface_dev_exit(struct ilitek_ts_data *ts)
{
struct touch_bus_info *info = (struct touch_bus_info *)ilits->hwif->info;
ILI_INFO("remove spi dev\n");
kfree(ilits->update_buf);
kfree(ilits->spi_tx);
kfree(ilits->spi_rx);
spi_unregister_driver(&info->bus_driver);
ipio_kfree((void **)&info);
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,55 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2019 MediaTek Inc.
*/
#undef TRACE_SYSTEM
#define TRACE_SYSTEM met_touch
#if !defined(__TRACE_MET_FTRACE_TOUCH_H__) || defined(TRACE_HEADER_MULTI_READ)
#define __TRACE_MET_FTRACE_TOUCH_H__
#include <linux/tracepoint.h>
/*
* Tracepoint for MET_touch
*/
TRACE_EVENT(MET_touch,
TP_PROTO(char *touch_type, long tsec, long tusec,
char *mode, int value),
TP_ARGS(touch_type, tsec, tusec, mode, value),
TP_STRUCT__entry(
__array(char, _touch_type, 16)
__field(long, _tsec)
__field(long, _tusec)
__array(char, _mode, 16)
__field(int, _value)
),
TP_fast_assign(
memcpy(__entry->_touch_type, touch_type, 16);
__entry->_tsec = tsec;
__entry->_tusec = tusec;
memcpy(__entry->_mode, mode, 16);
__entry->_value = value;
),
TP_printk("%s,%ld.%06ld,%s,%x",
__entry->_touch_type,
__entry->_tsec,
__entry->_tusec,
__entry->_mode,
__entry->_value
)
);
#endif /* __TRACE_MET_FTRACE_TOUCH_H__ */
/* This part must be outside protection */
#undef TRACE_INCLUDE_PATH
#undef linux
#define TRACE_INCLUDE_PATH .
#define TRACE_INCLUDE_FILE met_ftrace_touch
#include <trace/define_trace.h>
+184
View File
@@ -0,0 +1,184 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2019 MediaTek Inc.
*/
#ifndef __TPD_H
#define __TPD_H
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/time.h>
#include <linux/types.h>
#include <linux/seq_file.h>
#include <linux/list.h>
#include <linux/proc_fs.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <generated/autoconf.h>
#include <linux/kobject.h>
#include <linux/regulator/consumer.h>
#include <linux/pinctrl/consumer.h>
/*debug macros */
#define TPD_DEBUG
#define TPD_DEBUG_CODE
/* #define TPD_DEBUG_TRACK */
#define TPD_DMESG(a, arg...) \
pr_info(TPD_DEVICE ":[%s:%d] " a, __func__, __LINE__, ##arg)
#if defined(TPD_DEBUG)
#undef TPD_DEBUG
#define TPD_DEBUG(a, arg...) \
pr_info(TPD_DEVICE ":[%s:%d] " a, __func__, __LINE__, ##arg)
#else
#define TPD_DEBUG(arg...)
#endif
#define SPLIT ", "
/* register, address, configurations */
#define TPD_DEVICE "mtk-tpd"
#define TPD_X 0
#define TPD_Y 1
#define TPD_Z1 2
#define TPD_Z2 3
#define TP_DELAY (2*HZ/100)
#define TP_DRV_MAX_COUNT (20)
#define TPD_WARP_CNT (4)
#define TPD_VIRTUAL_KEY_MAX (10)
/* various mode */
#define TPD_MODE_NORMAL 0
#define TPD_MODE_KEYPAD 1
#define TPD_MODE_SW 2
#define TPD_MODE_FAV_SW 3
#define TPD_MODE_FAV_HW 4
#define TPD_MODE_RAW_DATA 5
#undef TPD_RES_X
#undef TPD_RES_Y
extern unsigned long TPD_RES_X;
extern unsigned long TPD_RES_Y;
extern int tpd_load_status; /* 0: failed, 1: success */
extern int tpd_mode;
extern int tpd_mode_axis;
extern int tpd_mode_min;
extern int tpd_mode_max;
extern int tpd_mode_keypad_tolerance;
extern int tpd_em_debounce_time;
extern int tpd_em_debounce_time0;
extern int tpd_em_debounce_time1;
extern int tpd_em_asamp;
extern int tpd_em_auto_time_interval;
extern int tpd_em_sample_cnt;
extern int tpd_calmat[];
extern int tpd_def_calmat[];
extern int tpd_calmat[];
extern int tpd_def_calmat[];
extern int TPD_DO_WARP;
extern int tpd_wb_start[];
extern int tpd_wb_end[];
extern int tpd_v_magnify_x;
extern int tpd_v_magnify_y;
extern unsigned int DISP_GetScreenHeight(void);
extern unsigned int DISP_GetScreenWidth(void);
#if defined(CONFIG_MTK_S3320) || defined(CONFIG_MTK_S3320_47) || \
defined(CONFIG_MTK_S3320_50)
extern void synaptics_init_sysfs(void);
#endif /* CONFIG_MTK_S3320 */
extern void tpd_button_init(void);
struct tpd_device {
struct device *tpd_dev;
struct regulator *reg;
struct regulator *io_reg;
struct input_dev *dev;
struct input_dev *kpd;
struct timer_list timer;
struct tasklet_struct tasklet;
int btn_state;
};
struct tpd_key_dim_local {
int key_x;
int key_y;
int key_width;
int key_height;
};
struct tpd_filter_t {
int enable; /*0: disable, 1: enable*/
int pixel_density; /*XXX pixel/cm*/
int W_W[3][4];/*filter custom setting prameters*/
unsigned int VECLOCITY_THRESHOLD[3];/*filter speed custom settings*/
};
struct tpd_dts_info {
int tpd_resolution[2];
int touch_max_num;
int use_tpd_button;
int tpd_key_num;
int tpd_key_local[4];
bool tpd_use_ext_gpio;
int rst_ext_gpio_num;
struct tpd_key_dim_local tpd_key_dim_local[4];
struct tpd_filter_t touch_filter;
};
extern struct tpd_dts_info tpd_dts_data;
struct tpd_attrs {
struct device_attribute **attr;
int num;
};
struct tpd_driver_t {
char *tpd_device_name;
int (*tpd_local_init)(void);
void (*suspend)(struct device *h);
void (*resume)(struct device *h);
int tpd_have_button;
struct tpd_attrs attrs;
};
/* #ifdef TPD_HAVE_BUTTON */
void tpd_button(unsigned int x, unsigned int y, unsigned int down);
void tpd_button_init(void);
ssize_t tpd_virtual_key(char *buf);
/* #ifndef TPD_BUTTON_HEIGHT */
/* #define TPD_BUTTON_HEIGHT TPD_RES_Y */
/* #endif */
extern int tpd_driver_add(struct tpd_driver_t *tpd_drv);
extern int tpd_driver_remove(struct tpd_driver_t *tpd_drv);
void tpd_button_setting(int keycnt, void *keys, void *keys_dim);
extern int tpd_em_spl_num;
extern int tpd_em_pressure_threshold;
extern struct tpd_device *tpd;
extern void tpd_get_dts_info(void);
#define GTP_RST_PORT 174
#define GTP_INT_PORT 0
extern void tpd_gpio_as_int(int pin);
extern void tpd_gpio_output(int pin, int level);
extern void tpd_gpio_mode_set(void);
extern const struct of_device_id touch_of_match[];
#ifdef TPD_DEBUG_CODE
#include "tpd_debug.h"
#endif
#ifdef TPD_DEBUG_TRACK
int DAL_Clean(void);
int DAL_Printf(const char *fmt, ...);
int LCD_LayerEnable(int id, BOOL enable);
#endif
#ifdef TPD_HAVE_CALIBRATION
#include "tpd_calibrate.h"
#endif
#include "tpd_default.h"
/* switch touch panel into different mode */
void _tpd_switch_single_mode(void);
void _tpd_switch_multiple_mode(void);
void _tpd_switch_sleep_mode(void);
void _tpd_switch_normal_mode(void);
#endif
@@ -0,0 +1,123 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 MediaTek Inc.
*/
#include "tpd.h"
/* #ifdef TPD_HAVE_BUTTON */
/* static int tpd_keys[TPD_KEY_COUNT] = TPD_KEYS; */
/* static int tpd_keys_dim[TPD_KEY_COUNT][4] = TPD_KEYS_DIM; */
static unsigned int tpd_keycnt;
static int tpd_keys[TPD_VIRTUAL_KEY_MAX] = { 0 };
static int tpd_keys_dim[TPD_VIRTUAL_KEY_MAX][4]; /* = {0}; */
static ssize_t mtk_virtual_keys_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
int i, j;
for (i = 0, j = 0; i < tpd_keycnt; i++)
j += snprintf(buf+j, PAGE_SIZE-j, "%s%s:%d:%d:%d:%d:%d%s", buf,
__stringify(EV_KEY), tpd_keys[i],
tpd_keys_dim[i][0], tpd_keys_dim[i][1],
tpd_keys_dim[i][2], tpd_keys_dim[i][3],
(i == tpd_keycnt - 1 ? "\n" : ":"));
return j;
}
static struct kobj_attribute mtk_virtual_keys_attr = {
.attr = {
.name = "virtualkeys.mtk-tpd",
.mode = 0644,
},
.show = &mtk_virtual_keys_show,
};
static struct attribute *mtk_properties_attrs[] = {
&mtk_virtual_keys_attr.attr,
NULL
};
static struct attribute_group mtk_properties_attr_group = {
.attrs = mtk_properties_attrs,
};
struct kobject *properties_kobj;
void tpd_button_init(void)
{
int ret = 0, i = 0;
/* if((tpd->kpd=input_allocate_device())==NULL) return -ENOMEM; */
tpd->kpd = input_allocate_device();
/* struct input_dev kpd initialization and registration */
tpd->kpd->name = TPD_DEVICE "-kpd";
set_bit(EV_KEY, tpd->kpd->evbit);
/* set_bit(EV_REL, tpd->kpd->evbit); */
/* set_bit(EV_ABS, tpd->kpd->evbit); */
for (i = 0; i < tpd_keycnt; i++)
__set_bit(tpd_keys[i], tpd->kpd->keybit);
tpd->kpd->id.bustype = BUS_HOST;
tpd->kpd->id.vendor = 0x0001;
tpd->kpd->id.product = 0x0001;
tpd->kpd->id.version = 0x0100;
if (input_register_device(tpd->kpd))
TPD_DMESG("input_register_device failed.(kpd)\n");
set_bit(EV_KEY, tpd->dev->evbit);
for (i = 0; i < tpd_keycnt; i++)
__set_bit(tpd_keys[i], tpd->dev->keybit);
properties_kobj = kobject_create_and_add("board_properties", NULL);
if (properties_kobj)
ret = sysfs_create_group(properties_kobj,
&mtk_properties_attr_group);
if (!properties_kobj || ret)
TPD_DEBUG("failed to create board_properties\n");
}
void tpd_button(unsigned int x, unsigned int y, unsigned int down)
{
int i;
bool report;
if (down) {
for (i = 0; i < tpd_keycnt; i++) {
report = x >= tpd_keys_dim[i][0] -
(tpd_keys_dim[i][2] / 2) &&
x <= tpd_keys_dim[i][0] +
(tpd_keys_dim[i][2] / 2) &&
y >= tpd_keys_dim[i][1] -
(tpd_keys_dim[i][3] / 2) &&
y <= tpd_keys_dim[i][1] +
(tpd_keys_dim[i][3] / 2) &&
!(tpd->btn_state & (1 << i));
if (report) {
input_report_key(tpd->kpd, tpd_keys[i], 1);
input_sync(tpd->kpd);
tpd->btn_state |= (1 << i);
TPD_DEBUG("press key %d (%d)\n",
i, tpd_keys[i]);
}
}
} else {
for (i = 0; i < tpd_keycnt; i++) {
if (tpd->btn_state & (1 << i)) {
input_report_key(tpd->kpd, tpd_keys[i], 0);
input_sync(tpd->kpd);
TPD_DEBUG("release key %d (%d)\n",
i, tpd_keys[i]);
}
}
tpd->btn_state = 0;
}
}
void tpd_button_setting(int keycnt, void *keys, void *keys_dim)
{
tpd_keycnt = keycnt;
memcpy(tpd_keys, keys, keycnt * 4);
memcpy(tpd_keys_dim, keys_dim, keycnt * 4 * 4);
}
/* #endif */
@@ -0,0 +1,765 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 MediaTek Inc.
*/
#include "tpd.h"
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/miscdevice.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/fb.h>
#ifdef CONFIG_MTK_MT6306_GPIO_SUPPORT
#include <mtk_6306_gpio.h>
#endif
#ifdef CONFIG_COMPAT
#include <linux/compat.h>
#endif
#if defined(CONFIG_MTK_S3320) || defined(CONFIG_MTK_S3320_50) \
|| defined(CONFIG_MTK_S3320_47) || defined(CONFIG_MTK_MIT200) \
|| defined(CONFIG_TOUCHSCREEN_SYNAPTICS_S3528) \
|| defined(CONFIG_MTK_S7020) \
|| defined(CONFIG_TOUCHSCREEN_MTK_SYNAPTICS_3320_50)
#include <linux/input/mt.h>
#endif /* CONFIG_MTK_S3320 */
/* for magnify velocity******************************************** */
#define TOUCH_IOC_MAGIC 'A'
#define TPD_GET_VELOCITY_CUSTOM_X _IO(TOUCH_IOC_MAGIC, 0)
#define TPD_GET_VELOCITY_CUSTOM_Y _IO(TOUCH_IOC_MAGIC, 1)
#define TPD_GET_FILTER_PARA _IOWR(TOUCH_IOC_MAGIC, 2, struct tpd_filter_t)
#ifdef CONFIG_COMPAT
#define COMPAT_TPD_GET_FILTER_PARA _IOWR(TOUCH_IOC_MAGIC, \
2, struct tpd_filter_t)
#endif
struct tpd_filter_t tpd_filter;
struct tpd_dts_info tpd_dts_data;
struct pinctrl *pinctrl1;
struct pinctrl_state *pins_default;
struct pinctrl_state *eint_as_int, *eint_output0,
*eint_output1, *rst_output0, *rst_output1,
*pins_spi_mode;
const struct of_device_id touch_of_match[] = {
{ .compatible = "goodix,touch", },
{},
};
void tpd_get_dts_info(void)
{
struct device_node *node1 = NULL;
int key_dim_local[16], i;
node1 = of_find_matching_node(node1, touch_of_match);
if (node1) {
of_property_read_u32(node1,
"tpd-max-touch-num", &tpd_dts_data.touch_max_num);
of_property_read_u32(node1,
"use-tpd-button", &tpd_dts_data.use_tpd_button);
pr_debug("[tpd]use-tpd-button = %d\n",
tpd_dts_data.use_tpd_button);
if (of_property_read_u32_array(node1, "tpd-resolution",
tpd_dts_data.tpd_resolution,
ARRAY_SIZE(tpd_dts_data.tpd_resolution))) {
pr_debug("[tpd] resulution is %d %d",
tpd_dts_data.tpd_resolution[0],
tpd_dts_data.tpd_resolution[1]);
}
if (tpd_dts_data.use_tpd_button) {
of_property_read_u32(node1,
"tpd-key-num", &tpd_dts_data.tpd_key_num);
if (of_property_read_u32_array(node1, "tpd-key-local",
tpd_dts_data.tpd_key_local,
ARRAY_SIZE(tpd_dts_data.tpd_key_local)))
pr_debug("tpd-key-local: %d %d %d %d",
tpd_dts_data.tpd_key_local[0],
tpd_dts_data.tpd_key_local[1],
tpd_dts_data.tpd_key_local[2],
tpd_dts_data.tpd_key_local[3]);
if (of_property_read_u32_array(node1,
"tpd-key-dim-local",
key_dim_local, ARRAY_SIZE(key_dim_local))) {
memcpy(tpd_dts_data.tpd_key_dim_local,
key_dim_local, sizeof(key_dim_local));
for (i = 0; i < 4; i++) {
pr_debug("[tpd]key[%d].key_x = %d\n", i,
tpd_dts_data
.tpd_key_dim_local[i]
.key_x);
pr_debug("[tpd]key[%d].key_y = %d\n", i,
tpd_dts_data
.tpd_key_dim_local[i]
.key_y);
pr_debug("[tpd]key[%d].key_W = %d\n", i,
tpd_dts_data
.tpd_key_dim_local[i]
.key_width);
pr_debug("[tpd]key[%d].key_H = %d\n", i,
tpd_dts_data
.tpd_key_dim_local[i]
.key_height);
}
}
}
of_property_read_u32(node1, "tpd-filter-enable",
&tpd_dts_data.touch_filter.enable);
if (tpd_dts_data.touch_filter.enable) {
of_property_read_u32(node1,
"tpd-filter-pixel-density",
&tpd_dts_data.touch_filter.pixel_density);
if (of_property_read_u32_array(node1,
"tpd-filter-custom-prameters",
(u32 *)tpd_dts_data.touch_filter.W_W,
ARRAY_SIZE(tpd_dts_data.touch_filter.W_W)))
pr_debug("get tpd-filter-custom-parameters");
if (of_property_read_u32_array(node1,
"tpd-filter-custom-speed",
tpd_dts_data.touch_filter.VECLOCITY_THRESHOLD,
ARRAY_SIZE(tpd_dts_data
.touch_filter
.VECLOCITY_THRESHOLD)))
pr_debug("get tpd-filter-custom-speed");
}
memcpy(&tpd_filter,
&tpd_dts_data.touch_filter, sizeof(tpd_filter));
pr_debug("[tpd]tpd-filter-enable = %d, pixel_density = %d\n",
tpd_filter.enable, tpd_filter.pixel_density);
tpd_dts_data.tpd_use_ext_gpio =
of_property_read_bool(node1, "tpd-use-ext-gpio");
of_property_read_u32(node1,
"tpd-rst-ext-gpio-num",
&tpd_dts_data.rst_ext_gpio_num);
} else {
TPD_DMESG("can't find touch compatible custom node\n");
}
}
static DEFINE_MUTEX(tpd_set_gpio_mutex);
void tpd_gpio_as_int(int pin)
{
mutex_lock(&tpd_set_gpio_mutex);
TPD_DEBUG("[tpd]%s\n", __func__);
if (pin == 1)
pinctrl_select_state(pinctrl1, eint_as_int);
mutex_unlock(&tpd_set_gpio_mutex);
}
void tpd_gpio_mode_set(void)
{
mutex_lock(&tpd_set_gpio_mutex);
TPD_DEBUG("[tpd]%s set gpio mode!\n", __func__);
pinctrl_select_state(pinctrl1, pins_spi_mode);
mutex_unlock(&tpd_set_gpio_mutex);
}
void tpd_gpio_output(int pin, int level)
{
mutex_lock(&tpd_set_gpio_mutex);
TPD_DEBUG("%s pin = %d, level = %d\n", __func__, pin, level);
if (pin == 1) {
if (level)
pinctrl_select_state(pinctrl1, eint_output1);
else
pinctrl_select_state(pinctrl1, eint_output0);
} else {
if (tpd_dts_data.tpd_use_ext_gpio) {
#ifdef CONFIG_MTK_MT6306_GPIO_SUPPORT
mt6306_set_gpio_dir(
tpd_dts_data.rst_ext_gpio_num, 1);
mt6306_set_gpio_out(
tpd_dts_data.rst_ext_gpio_num, level);
#endif
} else {
if (level)
pinctrl_select_state(pinctrl1, rst_output1);
else
pinctrl_select_state(pinctrl1, rst_output0);
}
}
mutex_unlock(&tpd_set_gpio_mutex);
}
int tpd_get_gpio_info(struct platform_device *pdev)
{
int ret;
TPD_DEBUG("[tpd %d] mt_tpd_pinctrl+++++++++++++++++\n", pdev->id);
pinctrl1 = devm_pinctrl_get(&pdev->dev);
if (IS_ERR(pinctrl1)) {
ret = PTR_ERR(pinctrl1);
dev_info(&pdev->dev, "fwq Cannot find pinctrl1!\n");
return ret;
}
pins_default = pinctrl_lookup_state(pinctrl1, "default");
if (IS_ERR(pins_default)) {
ret = PTR_ERR(pins_default);
TPD_DMESG("Cannot find pinctrl default %d!\n", ret);
}
eint_as_int = pinctrl_lookup_state(pinctrl1, "state_eint_as_int");
if (IS_ERR(eint_as_int)) {
ret = PTR_ERR(eint_as_int);
TPD_DMESG("Cannot find pinctrl state_eint_as_int!\n");
return ret;
}
eint_output0 = pinctrl_lookup_state(pinctrl1, "state_eint_output0");
if (IS_ERR(eint_output0)) {
ret = PTR_ERR(eint_output0);
TPD_DMESG("Cannot find pinctrl state_eint_output0!\n");
return ret;
}
eint_output1 = pinctrl_lookup_state(pinctrl1, "state_eint_output1");
if (IS_ERR(eint_output1)) {
ret = PTR_ERR(eint_output1);
TPD_DMESG("Cannot find pinctrl state_eint_output1!\n");
return ret;
}
pins_spi_mode = pinctrl_lookup_state(pinctrl1, "state_spi_mode");
if (IS_ERR(pins_spi_mode)) {
ret = PTR_ERR(pins_spi_mode);
TPD_DMESG("Cannot find pinctrl state_spi_mode!\n");
return ret;
}
if (tpd_dts_data.tpd_use_ext_gpio == false) {
rst_output0 =
pinctrl_lookup_state(pinctrl1, "state_rst_output0");
if (IS_ERR(rst_output0)) {
ret = PTR_ERR(rst_output0);
TPD_DMESG("Cannot find pinctrl state_rst_output0!\n");
return ret;
}
rst_output1 =
pinctrl_lookup_state(pinctrl1, "state_rst_output1");
if (IS_ERR(rst_output1)) {
ret = PTR_ERR(rst_output1);
TPD_DMESG("Cannot find pinctrl state_rst_output1!\n");
return ret;
}
}
TPD_DEBUG("[tpd%d] mt_tpd_pinctrl----------\n", pdev->id);
return 0;
}
static int tpd_misc_open(struct inode *inode, struct file *file)
{
return nonseekable_open(inode, file);
}
static int tpd_misc_release(struct inode *inode, struct file *file)
{
return 0;
}
#ifdef CONFIG_COMPAT
static long tpd_compat_ioctl(
struct file *file, unsigned int cmd,
unsigned long arg)
{
long ret;
void __user *arg32 = compat_ptr(arg);
if (!file->f_op || !file->f_op->unlocked_ioctl)
return -ENOTTY;
switch (cmd) {
case COMPAT_TPD_GET_FILTER_PARA:
if (arg32 == NULL) {
pr_info("invalid argument.");
return -EINVAL;
}
ret = file->f_op->unlocked_ioctl(file, TPD_GET_FILTER_PARA,
(unsigned long)arg32);
if (ret) {
pr_info("TPD_GET_FILTER_PARA unlocked_ioctl failed.");
return ret;
}
break;
default:
pr_info("tpd: unknown IOCTL: 0x%08x\n", cmd);
ret = -ENOIOCTLCMD;
break;
}
return ret;
}
#endif
static long tpd_unlocked_ioctl(struct file *file,
unsigned int cmd, unsigned long arg)
{
/* char strbuf[256]; */
void __user *data;
long err = 0;
if (_IOC_DIR(cmd) & _IOC_READ)
err = !access_ok(VERIFY_WRITE,
(void __user *)arg, _IOC_SIZE(cmd));
else if (_IOC_DIR(cmd) & _IOC_WRITE)
err = !access_ok(VERIFY_READ,
(void __user *)arg, _IOC_SIZE(cmd));
if (err) {
pr_info("tpd: access error: %08X, (%2d, %2d)\n",
cmd, _IOC_DIR(cmd), _IOC_SIZE(cmd));
return -EFAULT;
}
switch (cmd) {
case TPD_GET_VELOCITY_CUSTOM_X:
data = (void __user *)arg;
if (data == NULL) {
err = -EINVAL;
break;
}
if (copy_to_user(data,
&tpd_v_magnify_x, sizeof(tpd_v_magnify_x))) {
err = -EFAULT;
break;
}
break;
case TPD_GET_VELOCITY_CUSTOM_Y:
data = (void __user *)arg;
if (data == NULL) {
err = -EINVAL;
break;
}
if (copy_to_user(data,
&tpd_v_magnify_y, sizeof(tpd_v_magnify_y))) {
err = -EFAULT;
break;
}
break;
case TPD_GET_FILTER_PARA:
data = (void __user *) arg;
if (data == NULL) {
err = -EINVAL;
TPD_DMESG("GET_FILTER_PARA: data is null\n");
break;
}
if (copy_to_user(data, &tpd_filter,
sizeof(struct tpd_filter_t))) {
TPD_DMESG("GET_FILTER_PARA: copy data error\n");
err = -EFAULT;
break;
}
break;
default:
pr_info("tpd: unknown IOCTL: 0x%08x\n", cmd);
err = -ENOIOCTLCMD;
break;
}
return err;
}
static struct work_struct touch_resume_work;
static struct workqueue_struct *touch_resume_workqueue;
static const struct file_operations tpd_fops = {
/* .owner = THIS_MODULE, */
.open = tpd_misc_open,
.release = tpd_misc_release,
.unlocked_ioctl = tpd_unlocked_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = tpd_compat_ioctl,
#endif
};
/*---------------------------------------------------------------------------*/
static struct miscdevice tpd_misc_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = "touch",
.fops = &tpd_fops,
};
/* ********************************************** */
/* #endif */
/* function definitions */
static int __init tpd_device_init(void);
static void __exit tpd_device_exit(void);
static int tpd_probe(struct platform_device *pdev);
static int tpd_remove(struct platform_device *pdev);
static struct work_struct tpd_init_work;
static struct workqueue_struct *tpd_init_workqueue;
static int tpd_suspend_flag;
int tpd_register_flag;
/* global variable definitions */
struct tpd_device *tpd;
static struct tpd_driver_t tpd_driver_list[TP_DRV_MAX_COUNT]; /* = {0}; */
struct platform_device tpd_device = {
.name = TPD_DEVICE,
.id = -1,
};
const struct dev_pm_ops tpd_pm_ops = {
.suspend = NULL,
.resume = NULL,
};
static struct platform_driver tpd_driver = {
.remove = tpd_remove,
.shutdown = NULL,
.probe = tpd_probe,
.driver = {
.name = TPD_DEVICE,
.pm = &tpd_pm_ops,
.owner = THIS_MODULE,
.of_match_table = touch_of_match,
},
};
static struct tpd_driver_t *g_tpd_drv;
/* hh: use fb_notifier */
static struct notifier_block tpd_fb_notifier;
/* use fb_notifier */
static void touch_resume_workqueue_callback(struct work_struct *work)
{
TPD_DEBUG("GTP %s\n", __func__);
g_tpd_drv->resume(NULL);
tpd_suspend_flag = 0;
}
static int tpd_fb_notifier_callback(
struct notifier_block *self,
unsigned long event, void *data)
{
struct fb_event *evdata = NULL;
int blank;
int err = 0;
TPD_DEBUG("%s\n", __func__);
evdata = data;
/* If we aren't interested in this event, skip it immediately ... */
if (event != FB_EVENT_BLANK)
return 0;
blank = *(int *)evdata->data;
TPD_DMESG("fb_notify(blank=%d)\n", blank);
switch (blank) {
case FB_BLANK_UNBLANK:
TPD_DMESG("LCD ON Notify\n");
if (g_tpd_drv && tpd_suspend_flag) {
err = queue_work(touch_resume_workqueue,
&touch_resume_work);
if (!err) {
TPD_DMESG("start resume_workqueue failed\n");
return err;
}
}
break;
case FB_BLANK_POWERDOWN:
TPD_DMESG("LCD OFF Notify\n");
if (g_tpd_drv && !tpd_suspend_flag) {
err = cancel_work_sync(&touch_resume_work);
if (!err)
TPD_DMESG("cancel resume_workqueue failed\n");
g_tpd_drv->suspend(NULL);
}
tpd_suspend_flag = 1;
break;
default:
break;
}
return 0;
}
/* Add driver: if find TPD_TYPE_CAPACITIVE driver successfully, loading it */
int tpd_driver_add(struct tpd_driver_t *tpd_drv)
{
int i;
if (g_tpd_drv != NULL) {
TPD_DMESG("touch driver exist\n");
return -1;
}
/* check parameter */
if (tpd_drv == NULL)
return -1;
tpd_drv->tpd_have_button = tpd_dts_data.use_tpd_button;
/* R-touch */
if (strcmp(tpd_drv->tpd_device_name, "generic") == 0) {
tpd_driver_list[0].tpd_device_name = tpd_drv->tpd_device_name;
tpd_driver_list[0].tpd_local_init = tpd_drv->tpd_local_init;
tpd_driver_list[0].suspend = tpd_drv->suspend;
tpd_driver_list[0].resume = tpd_drv->resume;
tpd_driver_list[0].tpd_have_button = tpd_drv->tpd_have_button;
return 0;
}
for (i = 1; i < TP_DRV_MAX_COUNT; i++) {
/* add tpd driver into list */
if (tpd_driver_list[i].tpd_device_name == NULL) {
tpd_driver_list[i].tpd_device_name =
tpd_drv->tpd_device_name;
tpd_driver_list[i].tpd_local_init =
tpd_drv->tpd_local_init;
tpd_driver_list[i].suspend = tpd_drv->suspend;
tpd_driver_list[i].resume = tpd_drv->resume;
tpd_driver_list[i].tpd_have_button =
tpd_drv->tpd_have_button;
tpd_driver_list[i].attrs = tpd_drv->attrs;
break;
}
if (strcmp(tpd_driver_list[i].tpd_device_name,
tpd_drv->tpd_device_name) == 0)
return 1; /* driver exist */
}
return 0;
}
int tpd_driver_remove(struct tpd_driver_t *tpd_drv)
{
int i = 0;
/* check parameter */
if (tpd_drv == NULL)
return -1;
for (i = 0; i < TP_DRV_MAX_COUNT; i++) {
/* find it */
if (strcmp(tpd_driver_list[i].tpd_device_name,
tpd_drv->tpd_device_name) == 0) {
memset(&tpd_driver_list[i], 0,
sizeof(struct tpd_driver_t));
break;
}
}
return 0;
}
static void tpd_create_attributes(struct device *dev, struct tpd_attrs *attrs)
{
int num = attrs->num;
for (; num > 0;) {
if (device_create_file(dev, attrs->attr[--num]))
pr_info("mtk_tpd: tpd create attributes file failed\n");
}
}
/* touch panel probe */
static int tpd_probe(struct platform_device *pdev)
{
int touch_type = 1; /* 0:R-touch, 1: Cap-touch */
int i = 0;
#ifndef CONFIG_CUSTOM_LCM_X
#ifdef CONFIG_LCM_WIDTH
unsigned long tpd_res_x = 0, tpd_res_y = 0;
int ret = 0;
#endif
#endif
TPD_DMESG("enter %s, %d\n", __func__, __LINE__);
if (misc_register(&tpd_misc_device))
pr_info("mtk_tpd: tpd_misc_device register failed\n");
tpd_get_gpio_info(pdev);
tpd = kmalloc(sizeof(struct tpd_device), GFP_KERNEL);
if (tpd == NULL)
return -ENOMEM;
memset(tpd, 0, sizeof(struct tpd_device));
/* allocate input device */
tpd->dev = input_allocate_device();
if (tpd->dev == NULL) {
kfree(tpd);
return -ENOMEM;
}
/* TPD_RES_X = simple_strtoul(LCM_WIDTH, NULL, 0); */
/* TPD_RES_Y = simple_strtoul(LCM_HEIGHT, NULL, 0); */
#ifdef CONFIG_MTK_LCM_PHYSICAL_ROTATION
if (strncmp(CONFIG_MTK_LCM_PHYSICAL_ROTATION, "90", 2) == 0
|| strncmp(CONFIG_MTK_LCM_PHYSICAL_ROTATION, "270", 3) == 0) {
#ifdef CONFIG_MTK_FB
/*Fix build errors,as some projects cannot support these apis while bring up*/
TPD_RES_Y = DISP_GetScreenWidth();
TPD_RES_X = DISP_GetScreenHeight();
#endif
} else
#endif
{
#ifdef CONFIG_CUSTOM_LCM_X
#ifndef CONFIG_FPGA_EARLY_PORTING
#if defined(CONFIG_MTK_FB) && defined(CONFIG_MTK_LCM)
/*Fix build errors,as some projects cannot support these apis while bring up*/
TPD_RES_X = DISP_GetScreenWidth();
TPD_RES_Y = DISP_GetScreenHeight();
#else
/*for some projects, we do not use mtk framebuffer*/
TPD_RES_X = tpd_dts_data.tpd_resolution[0];
TPD_RES_Y = tpd_dts_data.tpd_resolution[1];
#endif
#endif
#else
#ifdef CONFIG_LCM_WIDTH
ret = kstrtoul(CONFIG_LCM_WIDTH, 0, &tpd_res_x);
if (ret < 0) {
pr_info("Touch down get lcm_x failed");
return ret;
}
TPD_RES_X = tpd_res_x;
ret = kstrtoul(CONFIG_LCM_HEIGHT, 0, &tpd_res_x);
if (ret < 0) {
pr_info("Touch down get lcm_y failed");
return ret;
}
TPD_RES_Y = tpd_res_y;
#endif
#endif
}
if (2560 == TPD_RES_X)
TPD_RES_X = 2048;
if (1600 == TPD_RES_Y)
TPD_RES_Y = 1536;
pr_debug("mtk_tpd: TPD_RES_X = %lu, TPD_RES_Y = %lu\n",
TPD_RES_X, TPD_RES_Y);
tpd_mode = TPD_MODE_NORMAL;
tpd_mode_axis = 0;
tpd_mode_min = TPD_RES_Y / 2;
tpd_mode_max = TPD_RES_Y;
tpd_mode_keypad_tolerance = TPD_RES_X * TPD_RES_X / 1600;
/* struct input_dev dev initialization and registration */
tpd->dev->name = TPD_DEVICE;
set_bit(EV_ABS, tpd->dev->evbit);
set_bit(EV_KEY, tpd->dev->evbit);
set_bit(ABS_X, tpd->dev->absbit);
set_bit(ABS_Y, tpd->dev->absbit);
set_bit(ABS_PRESSURE, tpd->dev->absbit);
#if !defined(CONFIG_MTK_S3320) && !defined(CONFIG_MTK_S3320_47)\
&& !defined(CONFIG_MTK_S3320_50) && !defined(CONFIG_MTK_MIT200) \
&& !defined(CONFIG_TOUCHSCREEN_SYNAPTICS_S3528) \
&& !defined(CONFIG_MTK_S7020) \
&& !defined(CONFIG_TOUCHSCREEN_MTK_SYNAPTICS_3320_50)
set_bit(BTN_TOUCH, tpd->dev->keybit);
#endif /* CONFIG_MTK_S3320 */
set_bit(INPUT_PROP_DIRECT, tpd->dev->propbit);
/* save dev for regulator_get() before tpd_local_init() */
tpd->tpd_dev = &pdev->dev;
for (i = 1; i < TP_DRV_MAX_COUNT; i++) {
/* add tpd driver into list */
if (tpd_driver_list[i].tpd_device_name != NULL) {
tpd_driver_list[i].tpd_local_init();
/* msleep(1); */
if (tpd_load_status == 1) {
TPD_DMESG("%s, tpd_driver_name=%s\n", __func__,
tpd_driver_list[i].tpd_device_name);
g_tpd_drv = &tpd_driver_list[i];
break;
}
}
}
if (g_tpd_drv == NULL) {
if (tpd_driver_list[0].tpd_device_name != NULL) {
g_tpd_drv = &tpd_driver_list[0];
/* touch_type:0: r-touch, 1: C-touch */
touch_type = 0;
g_tpd_drv->tpd_local_init();
TPD_DMESG("Generic touch panel driver\n");
} else {
TPD_DMESG("no touch driver is loaded!!\n");
return 0;
}
}
touch_resume_workqueue = create_singlethread_workqueue("touch_resume");
INIT_WORK(&touch_resume_work, touch_resume_workqueue_callback);
/* use fb_notifier */
tpd_fb_notifier.notifier_call = tpd_fb_notifier_callback;
if (fb_register_client(&tpd_fb_notifier))
TPD_DMESG("register fb_notifier fail!\n");
/* TPD_TYPE_CAPACITIVE handle */
if (touch_type == 1) {
set_bit(ABS_MT_TRACKING_ID, tpd->dev->absbit);
set_bit(ABS_MT_TOUCH_MAJOR, tpd->dev->absbit);
set_bit(ABS_MT_TOUCH_MINOR, tpd->dev->absbit);
set_bit(ABS_MT_POSITION_X, tpd->dev->absbit);
set_bit(ABS_MT_POSITION_Y, tpd->dev->absbit);
input_set_abs_params(tpd->dev,
ABS_MT_POSITION_X, 0, TPD_RES_X, 0, 0);
input_set_abs_params(tpd->dev,
ABS_MT_POSITION_Y, 0, TPD_RES_Y, 0, 0);
#if defined(CONFIG_MTK_S3320) || defined(CONFIG_MTK_S3320_47) \
|| defined(CONFIG_MTK_S3320_50) || defined(CONFIG_MTK_MIT200) \
|| defined(CONFIG_TOUCHSCREEN_SYNAPTICS_S3528) \
|| defined(CONFIG_MTK_S7020) \
|| defined(CONFIG_TOUCHSCREEN_MTK_SYNAPTICS_3320_50)
input_set_abs_params(tpd->dev,
ABS_MT_PRESSURE, 0, 255, 0, 0);
input_set_abs_params(tpd->dev,
ABS_MT_WIDTH_MAJOR, 0, 15, 0, 0);
input_set_abs_params(tpd->dev, ABS_MT_WIDTH_MINOR, 0, 15, 0, 0);
input_mt_init_slots(tpd->dev, 10, 0);
#else
input_set_abs_params(tpd->dev,
ABS_MT_TOUCH_MAJOR, 0, 100, 0, 0);
input_set_abs_params(tpd->dev,
ABS_MT_TOUCH_MINOR, 0, 100, 0, 0);
#endif /* CONFIG_MTK_S3320 */
TPD_DMESG("Cap touch panel driver\n");
}
input_set_abs_params(tpd->dev, ABS_X, 0, TPD_RES_X, 0, 0);
input_set_abs_params(tpd->dev, ABS_Y, 0, TPD_RES_Y, 0, 0);
input_abs_set_res(tpd->dev, ABS_X, TPD_RES_X);
input_abs_set_res(tpd->dev, ABS_Y, TPD_RES_Y);
input_set_abs_params(tpd->dev, ABS_PRESSURE, 0, 255, 0, 0);
input_set_abs_params(tpd->dev, ABS_MT_TRACKING_ID, 0, 10, 0, 0);
if (input_register_device(tpd->dev))
TPD_DMESG("input_register_device failed.(tpd)\n");
else
tpd_register_flag = 1;
if (g_tpd_drv->tpd_have_button)
tpd_button_init();
if (g_tpd_drv->attrs.num)
tpd_create_attributes(&pdev->dev, &g_tpd_drv->attrs);
return 0;
}
static int tpd_remove(struct platform_device *pdev)
{
input_unregister_device(tpd->dev);
return 0;
}
/* called when loaded into kernel */
static void tpd_init_work_callback(struct work_struct *work)
{
TPD_DEBUG("MediaTek touch panel driver init\n");
if (platform_driver_register(&tpd_driver) != 0)
TPD_DMESG("unable to register touch panel driver.\n");
}
static int __init tpd_device_init(void)
{
int res = 0;
tpd_init_workqueue = create_singlethread_workqueue("mtk-tpd");
INIT_WORK(&tpd_init_work, tpd_init_work_callback);
res = queue_work(tpd_init_workqueue, &tpd_init_work);
if (!res)
pr_info("tpd : touch device init failed res:%d\n", res);
return 0;
}
/* should never be called */
static void __exit tpd_device_exit(void)
{
TPD_DMESG("MediaTek touch panel driver exit\n");
/* input_unregister_device(tpd->dev); */
platform_driver_unregister(&tpd_driver);
}
late_initcall(tpd_device_init);
module_exit(tpd_device_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("MediaTek touch panel driver");
MODULE_AUTHOR("Kirby Wu<kirby.wu@mediatek.com>");
@@ -0,0 +1,437 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 MediaTek Inc.
*/
#include <linux/vmalloc.h>
#include <linux/uaccess.h>
#include <linux/major.h>
#include <linux/miscdevice.h>
#include <linux/i2c.h>
#include <linux/delay.h>
#include "tpd.h"
#include "met_ftrace_touch.h"
#ifdef TPD_DEBUG_CODE
int tpd_fail_count;
int tpd_trial_count;
int tpd_debug_nr;
module_param(tpd_debug_nr, int, 0644);
module_param(tpd_fail_count, int, 0644);
module_param(tpd_trial_count, int, 0644);
int tpd_debug_time;
long tpd_last_2_int_time[2] = { 0 };
long tpd_last_down_time;
int tpd_start_profiling;
int tpd_down_status;
module_param(tpd_debug_time, int, 0644);
void tpd_debug_set_time(void)
{
struct timeval t;
if (!tpd_debug_time && !tpd_em_log)
return;
do_gettimeofday(&t);
tpd_last_2_int_time[0] = tpd_last_2_int_time[1];
tpd_last_2_int_time[1] = (t.tv_sec & 0xFFF) * 1000000 + t.tv_usec;
/* */
/* Start profiling while receive touch DOWN event */
/* Stop profiling while the first frame is upadted */
/* */
if (!tpd_down_status) {
tpd_start_profiling = 1;
tpd_last_down_time = tpd_last_2_int_time[1];
}
}
#ifdef TPD_DEBUG_TRACK
int tpd_debug_track;
int tpd_debug_track_color;
int tpd_debug_touch_up;
module_param(tpd_debug_track, int, 0644);
void tpd_draw(int x, int y)
{
UINT16 *buf = (UINT16 *) dal_fb_addr;
buf = buf + (x + y * TPD_RES_X);
tpd_debug_track_color += (tpd_debug_track_color >= 31 ? 0 : 1);
*buf = (0xffe0 + tpd_debug_track_color);
}
void tpd_down_debug_track(int x, int y)
{
if (tpd_debug_touch_up == 1) {
DAL_Clean();
tpd_debug_touch_up = 0;
}
LCD_LayerEnable(5, TRUE);
tpd_draw(x - 1, y - 1);
tpd_draw(x, y - 1);
tpd_draw(x + 1, y - 1);
tpd_draw(x - 1, y);
tpd_draw(x + 1, y);
tpd_draw(x - 1, y + 1);
tpd_draw(x, y + 1);
tpd_draw(x + 1, y + 1);
}
void tpd_up_debug_track(int x, int y)
{
if (x == 0 && y == 0) {
tpd_debug_track_color = 0;
DAL_Clean();
}
tpd_debug_touch_up = 1;
}
#endif /* TPD_DEBUG_TRACK */
#define BUFFER_SIZE 128
int tpd_em_log;
module_param(tpd_em_log, int, 0664);
void tpd_enable_em_log(int enable)
{
if (enable)
tpd_em_log = 1;
else
tpd_em_log = 0;
}
EXPORT_SYMBOL(tpd_enable_em_log);
int tpd_em_log_to_fs;
module_param(tpd_em_log_to_fs, int, 0664);
int tpd_em_log_first = 1;
struct tpd_em_log_struct {
struct list_head list;
char data[BUFFER_SIZE];
};
static LIST_HEAD(tpd_em_log_list);
int tpd_log_line_buffer = 128; /* per line 128 bytes */
int tpd_log_line_cnt = 1024 * 10;
module_param(tpd_log_line_buffer, int, 0664);
module_param(tpd_log_line_cnt, int, 0664);
struct tpd_debug_log_buf {
unsigned int head;
unsigned int tail;
spinlock_t buffer_lock;
unsigned int cnt;
unsigned char *buffer;
};
struct tpd_debug_log_buf tpd_buf;
static int tpd_debug_log_open(struct inode *inode, struct file *file)
{
if (tpd_buf.buffer == NULL)
tpd_buf.buffer =
vmalloc(tpd_log_line_cnt * tpd_log_line_buffer);
if (tpd_buf.buffer == NULL) {
pr_info("tpd_log: nomem for tpd_buf->buffer\n");
return -ENOMEM;
}
spin_lock(&tpd_buf.buffer_lock);
tpd_buf.head = tpd_buf.tail = 0;
tpd_buf.cnt = 0;
spin_unlock(&tpd_buf.buffer_lock);
file->private_data = &tpd_buf;
pr_debug("[tpd_em_log]: open log file\n");
return 0;
}
static int tpd_debug_log_release(struct inode *inode, struct file *file)
{
unsigned char *tmp_buffer = NULL;
pr_debug("[tpd_em_log]: close log file\n");
spin_lock(&tpd_buf.buffer_lock);
tmp_buffer = tpd_buf.buffer;
tpd_buf.buffer = NULL;
spin_unlock(&tpd_buf.buffer_lock);
if (tmp_buffer)
vfree(tmp_buffer);
/* free(tpd_buf); */
return 0;
}
static ssize_t tpd_debug_log_write(struct file *file, const char __user *buffer,
size_t count, loff_t *ppos)
{
return 0;
}
static ssize_t tpd_debug_log_read(struct file *file, char __user *buffer,
size_t count, loff_t *ppos)
{
struct tpd_debug_log_buf *tpd_buf =
(struct tpd_debug_log_buf *)file->private_data;
unsigned int retval = 0, unit = tpd_log_line_buffer;
unsigned char *tmp_buf = NULL;
if (tpd_buf == NULL)
return -ENOMEM;
if (tpd_buf->head == tpd_buf->tail && (file->f_flags & O_NONBLOCK))
return -EAGAIN;
while ((count - retval) >= unit) {
spin_lock_irq(&tpd_buf->buffer_lock);
if (tpd_buf->head != tpd_buf->tail) {
tmp_buf = &tpd_buf->buffer[tpd_buf->tail++ * unit];
tpd_buf->tail &= tpd_log_line_cnt - 1;
} else {
spin_unlock_irq(&tpd_buf->buffer_lock);
break;
}
spin_unlock_irq(&tpd_buf->buffer_lock);
/* pr_info("%s, tmp_buf:0x%x\n", tmp_buf, tmp_buf); */
if (copy_to_user(buffer + retval, tmp_buf, unit))
return -EFAULT;
retval += unit;
}
return retval;
}
static unsigned char *tpd_log_find_buffer(void)
{
unsigned char *buffer = NULL;
unsigned int unit = tpd_log_line_buffer;
if (tpd_buf.buffer == NULL) {
pr_info("[tpd_em_log] :tpd_buf.buffer is NULL\n");
return NULL;
}
spin_lock(&tpd_buf.buffer_lock);
buffer = &tpd_buf.buffer[tpd_buf.head++ * unit];
tpd_buf.head &= tpd_log_line_cnt - 1;
spin_unlock(&tpd_buf.buffer_lock);
if (tpd_buf.head == tpd_buf.tail) {
snprintf(buffer, unit, "[tpd_em_log] overlay !!!!!\n");
return NULL;
}
memset(buffer, 0, unit);
return buffer;
}
static const struct file_operations tpd_debug_log_fops = {
.owner = THIS_MODULE,
.read = tpd_debug_log_read,
.write = tpd_debug_log_write,
.open = tpd_debug_log_open,
.release = tpd_debug_log_release,
};
static struct miscdevice tpd_debug_log_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = "tpd_em_log",
.fops = &tpd_debug_log_fops,
};
#ifndef CREATE_TRACE_POINTS
#define CREATE_TRACE_POINTS
#endif
noinline void MET_touch(int raw_x, int raw_y,
int cal_x, int cal_y, int p, int down)
{
struct timeval t;
do_gettimeofday(&t);
if ((tpd_down_status == 0 && down == 1) ||
(tpd_down_status == 1 && down == 0)) {
trace_MET_touch("EV_KEY",
t.tv_sec, t.tv_usec, "BTN_TOUCH", down);
tpd_down_status = !tpd_down_status;
}
if (tpd_down_status) {
trace_MET_touch("EV_ABS", t.tv_sec, t.tv_usec, "X", raw_x);
trace_MET_touch("EV_ABS", t.tv_sec, t.tv_usec, "Y", raw_y);
}
}
void tpd_em_log_output(int raw_x, int raw_y,
int cal_x, int cal_y, int p, int down)
{
if (down == TPD_TYPE_INT_DOWN) {
pr_debug("[tpd_em_log] int trigger down\n");
} else if (down == TPD_TYPE_INT_UP) {
pr_debug("[tpd_em_log] int trigger up\n");
} else if (down == TPD_TYPE_TIMER) {
pr_debug("[tpd_em_log] timer trigger\n");
} else if (down == TPD_TYPE_RAW_DATA) {
if (tpd_em_log == TPD_TYPE_RAW_DATA) {
pr_debug("[tpd_em_log] rx=%d,ry=%d,rz1=%d"
SPLIT "rz2=%d,p=%d,r\n",
raw_x, raw_y, cal_x, cal_y, p);
}
} else if (down == TPD_TYPE_REJECT1) {
pr_debug("[tpd_em_log] the first or last point is rejected\n");
} else if (down == TPD_TYPE_REJECT2) {
pr_debug
("[tpd_em_log] pressure(%d) > NICE_PRESSURE(%d)"
SPLIT "debounce debt0:%d ms, debt1:%d ms, spl_num:%d\n",
raw_x, raw_y, cal_x, cal_y, p);
} else if (down == TPD_TYPE_FIST_LATENCY) {
pr_debug("[tpd_em_log] The first touch latency is %d ms\n",
raw_x / 1000);
} else if (down && tpd_down_status == 0) {
pr_debug("[tpd_em_log] rx=%d,ry=%d,cx=%d"
SPLIT "cy=%d,p=%d,d(+%ld ms)\n",
raw_x, raw_y, cal_x, cal_y, p,
(tpd_last_2_int_time[1] -
tpd_last_2_int_time[0]) / 1000);
} else if (down && tpd_down_status != 0) {
pr_debug("[tpd_em_log] rx=%d,ry=%d,cx=%d"
SPLIT "cy=%d,p=%d,m(+%ld ms)\n",
raw_x, raw_y, cal_x, cal_y, p,
(tpd_last_2_int_time[1] -
tpd_last_2_int_time[0]) / 1000);
} else {
pr_debug("[tpd_em_log] rx=%d,ry=%d,cx=%d"
SPLIT "cy=%d,p=%d,u(+%ld ms)\n",
raw_x, raw_y, cal_x, cal_y, p,
(tpd_last_2_int_time[1] -
tpd_last_2_int_time[0]) / 1000);
}
}
void tpd_em_log_store(int raw_x, int raw_y,
int cal_x, int cal_y, int p, int down)
{
/* static struct proc_dir_entry *entry = NULL; */
/* struct tpd_em_log_struct *tpd_em_log_struct_new; */
struct timeval t;
unsigned char *buffer = NULL;
/* unsigned int unit = tpd_log_line_buffer; */
/* pr_info("[tpd_em_log]: start register log file"); */
buffer = tpd_log_find_buffer();
if (buffer == NULL) {
pr_info("not buffer\n");
return;
}
do_gettimeofday(&t);
if (down == TPD_TYPE_INT_DOWN) {
snprintf(buffer, tpd_log_line_buffer,
"[%5lu.%06lu][tpd_em_log] int trigger down\n",
(t.tv_sec & 0xFFF), t.tv_usec);
} else if (down == TPD_TYPE_INT_UP) {
snprintf(buffer, tpd_log_line_buffer,
"[%5lu.%06lu][tpd_em_log] int trigger up\n",
(t.tv_sec & 0xFFF), t.tv_usec);
} else if (down == TPD_TYPE_TIMER) {
snprintf(buffer, tpd_log_line_buffer,
"[%5lu.%06lu][tpd_em_log] timer trigger\n",
(t.tv_sec & 0xFFF), t.tv_usec);
} else if (down == TPD_TYPE_RAW_DATA) {
if (tpd_em_log == TPD_TYPE_RAW_DATA) {
snprintf(buffer, tpd_log_line_buffer,
"[%5lu.%06lu][tpd_em_log]"
SPLIT "rx=%d,ry=%d,rz1=%d,rz2=%d,p=%d,r\n",
(t.tv_sec & 0xFFF), t.tv_usec,
raw_x, raw_y, cal_x, cal_y, p);
}
} else if (down == TPD_TYPE_REJECT1) {
snprintf(buffer, tpd_log_line_buffer,
"[%5lu.%06lu][tpd_em_log]"
SPLIT "the first or last point is rejected\n",
(t.tv_sec & 0xFFF), t.tv_usec);
} else if (down == TPD_TYPE_REJECT2) {
snprintf(buffer, tpd_log_line_buffer,
"[%5lu.%06lu][tpd_em_log]"
SPLIT "pressure(%d) > NICE_PRESSURE(%d), "
SPLIT "debounce debt0:%d, debt1:%d, spl_num:%d\n",
(t.tv_sec & 0xFFF), t.tv_usec,
raw_x, raw_y, cal_x, cal_y, p);
} else if (down == TPD_TYPE_FIST_LATENCY) {
snprintf(buffer, tpd_log_line_buffer,
"[%5lu.%06lu][tpd_em_log]"
SPLIT "The first touch latency is %d ms\n",
(t.tv_sec & 0xFFF), t.tv_usec, raw_x / 1000);
} else if (down && tpd_down_status == 0) {
snprintf(buffer, tpd_log_line_buffer,
"[%5lu.%06lu][tpd_em_log]"
SPLIT "rx=%d,ry=%d,cx=%d,cy=%d,p=%d,d(+%ld ms)\n",
(t.tv_sec & 0xFFF), t.tv_usec,
raw_x, raw_y, cal_x, cal_y, p,
(tpd_last_2_int_time[1] -
tpd_last_2_int_time[0]) / 1000);
} else if (down && tpd_down_status != 0) {
snprintf(buffer, tpd_log_line_buffer,
"[%5lu.%06lu][tpd_em_log]"
SPLIT "rx=%d,ry=%d,cx=%d,cy=%d,p=%d,d(+%ld ms)\n",
(t.tv_sec & 0xFFF), t.tv_usec,
raw_x, raw_y, cal_x, cal_y, p,
(tpd_last_2_int_time[1] -
tpd_last_2_int_time[0]) / 1000);
} else {
snprintf(buffer, tpd_log_line_buffer,
"[%5lu.%06lu][tpd_em_log]"
SPLIT "rx=%d,ry=%d,cx=%d,cy=%d,p=%d,u(+%ld ms)\n",
(t.tv_sec & 0xFFF), t.tv_usec,
raw_x, raw_y, cal_x, cal_y, p,
(tpd_last_2_int_time[1] -
tpd_last_2_int_time[0]) / 1000);
}
/* list_add_tail(&tpd_em_log_struct_new->list, &tpd_em_log_list); */
}
void tpd_em_log_release(void)
{
struct tpd_em_log_struct *p, *p_tmp;
if (!tpd_em_log_first) {
remove_proc_entry("tpd_em_log", NULL);
list_for_each_entry_safe(p, p_tmp, &tpd_em_log_list, list) {
list_del(&p->list);
kfree(p);
}
tpd_em_log_first = 1;
tpd_em_log_to_fs = 0;
}
}
static int __init tpd_log_init(void)
{
if (misc_register(&tpd_debug_log_dev) < 0) {
pr_info("[tpd_em_log] :register device failed\n");
return -1;
}
pr_info("[tpd_em_log] :register device successfully\n");
spin_lock_init(&tpd_buf.buffer_lock);
return 0;
}
int tpd_debuglog;
module_param(tpd_debuglog, int, 0664);
module_init(tpd_log_init);
#endif /* TPD_DEBUG_CODE */
@@ -0,0 +1,144 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2019 MediaTek Inc.
*/
#ifndef TPD_DEBUG_H
#define TPD_DEBUG_H
#ifdef TPD_DEBUG_CODE
#include<linux/i2c.h>
extern int tpd_debug_nr;
void tpd_debug_set_time(void);
extern int tpd_debug_time;
extern long tpd_last_2_int_time[2];
extern long tpd_last_down_time;
extern int tpd_start_profiling;
extern int tpd_down_status;
#define TPD_DEBUG_PRINT_INT \
do { \
if (tpd_debug_time) { \
pr_info("tp_int\n"); \
} \
} while (0)
#define TPD_DEBUG_PRINT_UP \
do { \
if (pending == 0 && tpd_debug_time) { \
tpd_down_status = 0; \
pr_debug("up on %ld ms (+%ld ms)\n", \
(tpd_last_2_int_time[1] - tpd_last_down_time) / 1000, \
(tpd_last_2_int_time[1] - tpd_last_2_int_time[0]) / 1000); \
} \
} while (0)
#define TPD_DEBUG_PRINT_DOWN \
do { \
if (tpd_debug_time) { \
if (tpd_down_status == 0)\
pr_debug("down on 0 ms\n"); \
else \
pr_debug("move on %ld ms (+%ld ms)\n", \
(tpd_last_2_int_time[1] - \
tpd_last_down_time) / 1000, \
(tpd_last_2_int_time[1] -\
tpd_last_2_int_time[0]) / 1000); \
tpd_down_status = 1; \
} \
} while (0)
#define TPD_DEBUG_SET_TIME do { tpd_debug_set_time(); } while (0)
extern int tpd_em_log;
extern int tpd_em_log_to_fs;
extern int tpd_type_cap;
void tpd_em_log_output(int raw_x,
int raw_y, int cal_x, int cal_y, int p, int down);
void tpd_em_log_store(int raw_x,
int raw_y, int cal_x, int cal_y, int p, int down);
void tpd_em_log_release(void);
void tpd_enable_em_log(int enable);
#ifndef CREATE_TRACE_POINTS
#define CREATE_TRACE_POINTS
#endif
noinline void MET_touch(int raw_x, int raw_y,
int cal_x, int cal_y, int p, int down);
#define TPD_TYPE_RAW_DATA 2
#define TPD_TYPE_INT_DOWN 3
#define TPD_TYPE_INT_UP 4
#define TPD_TYPE_TIMER 5
#define TPD_TYPE_REJECT1 6
#define TPD_TYPE_REJECT2 7
#define TPD_TYPE_FIST_LATENCY 8
#define TPD_EM_PRINT(raw_x, raw_y, cal_x, cal_y, p, down)\
do { \
if (tpd_em_log) {\
if (!tpd_em_log_to_fs) { \
tpd_em_log_output(raw_x, \
raw_y, cal_x, cal_y, p, down); \
} else { \
tpd_em_log_store(raw_x, \
raw_y, cal_x, cal_y, p, down); \
tpd_em_log_output(raw_x, \
raw_y, cal_x, cal_y, p, down); \
} \
} else { \
if (tpd_em_log_to_fs) { \
tpd_em_log_release(); \
} \
} \
MET_touch(raw_x, raw_y, cal_x, cal_y, p, down); \
} while (0)
#ifdef TPD_DEBUG_TRACK
extern void *dal_fb_addr;
extern int tpd_debug_track;
void tpd_up_debug_track(int x, int y);
void tpd_down_debug_track(int x, int y);
#define TPD_UP_DEBUG_TRACK(x, y) \
do { \
if (tpd_debug_track) \
tpd_up_debug_track(x, y); \
} while (0)
#define TPD_DOWN_DEBUG_TRACK(x, y) \
do { \
if (tpd_debug_track) \
tpd_down_debug_track(x, y); \
} while (0)
#endif /* TPD_DEBUG_TRACK */
#endif /* TPD_DEBUG_CODE */
/* Macros that will be embedded in code */
#ifndef TPD_DEBUG_SET_TIME
#define TPD_DEBUG_SET_TIME
#endif
#ifndef TPD_DEBUG_PRINT_UP
#define TPD_DEBUG_PRINT_UP
#endif
#ifndef TPD_DEBUG_PRINT_DOWN
#define TPD_DEBUG_PRINT_DOWN
#endif
#ifndef TPD_UP_DEBUG_TRACK
#define TPD_UP_DEBUG_TRACK(x, y)
#endif
#ifndef TPD_DOWN_DEBUG_TRACK
#define TPD_DOWN_DEBUG_TRACK(x, y)
#endif
#endif /* TPD_DEBUG_H */
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 MediaTek Inc.
*/
#include "tpd.h"
/* #ifndef TPD_CUSTOM_TREMBLE_TOLERANCE */
int tpd_trembling_tolerance(int t, int p)
{
if (t > 5 || p > 120)
return 200;
if (p > 90)
return 64;
if (p > 80)
return 36;
return 26;
}
/* #endif */
@@ -0,0 +1,10 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2019 MediaTek Inc.
*/
#ifndef TPD_FAT_TOUCH
#define TPD_FAT_TOUCH 120
#endif
int tpd_trembling_tolerance(int t, int p);
@@ -0,0 +1,48 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 MediaTek Inc.
*/
#include "tpd.h"
int tpd_calibrate_en;
module_param(tpd_calibrate_en, int, 0664);
int tpd_show_version;
module_param(tpd_show_version, int, 0664);
/* switch touch panel into single scan mode for decreasing interference */
void tpd_switch_single_mode(void)
{
#ifdef HAVE_SINGLE_MULTIPLE_SCAN_MODE
_tpd_switch_single_mode();
#endif
}
EXPORT_SYMBOL(tpd_switch_single_mode);
/* switch touch panel into multiple scan mode for better performance */
void tpd_switch_multiple_mode(void)
{
#ifdef HAVE_SINGLE_MULTIPLE_SCAN_MODE
_tpd_switch_multiple_mode();
#endif
}
EXPORT_SYMBOL(tpd_switch_multiple_mode);
/* switch touch panel into deep sleep mode */
void tpd_switch_sleep_mode(void)
{
#ifdef HAVE_SLEEP_NORMAL_MODE
_tpd_switch_sleep_mode();
#endif
}
EXPORT_SYMBOL(tpd_switch_sleep_mode);
/* switch touch panel back to normal mode */
void tpd_switch_normal_mode(void)
{
#ifdef HAVE_SLEEP_NORMAL_MODE
_tpd_switch_normal_mode();
#endif
}
EXPORT_SYMBOL(tpd_switch_normal_mode);
@@ -0,0 +1,69 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019 MediaTek Inc.
*/
#include "tpd.h"
unsigned long TPD_RES_X = 480;
unsigned long TPD_RES_Y = 800;
/* #if (defined(TPD_HAVE_CALIBRATION) && !defined(TPD_CUSTOM_CALIBRATION)) */
int tpd_calmat[8] = { 0 };
int tpd_def_calmat[8] = { 0 };
int tpd_calmat_size = 8;
int tpd_def_calmat_size = 8;
module_param_array(tpd_calmat, int, &tpd_calmat_size, 0664);
module_param_array(tpd_def_calmat, int, &tpd_def_calmat_size, 0444);
/* #endif */
/* #ifdef TPD_TYPE_CAPACITIVE */
int tpd_type_cap;
int tpd_v_magnify_x = 10;
int tpd_v_magnify_y = 10;
module_param(tpd_v_magnify_x, int, 0664);
module_param(tpd_v_magnify_y, int, 0664);
module_param(tpd_type_cap, int, 0444);
int tpd_firmware_version[2] = { 0, 0 };
int tpd_firmware_version_size = 2;
module_param_array(tpd_firmware_version, int, &tpd_firmware_version_size, 0444);
int tpd_mode = TPD_MODE_NORMAL;
int tpd_mode_axis;
int tpd_mode_min = 400; /* TPD_RES_Y/2; */
int tpd_mode_max = 800; /* TPD_RES_Y; */
/* TPD_RES_X*TPD_RES_X/1600; */
int tpd_mode_keypad_tolerance = 480 * 480 / 1600;
module_param(tpd_mode, int, 0664);
module_param(tpd_mode_axis, int, 0664);
module_param(tpd_mode_min, int, 0664);
module_param(tpd_mode_max, int, 0664);
module_param(tpd_mode_keypad_tolerance, int, 0664);
int tpd_em_debounce_time0 = 1;
int tpd_em_debounce_time; /* =0 */
int tpd_em_debounce_time1 = 4;
module_param(tpd_em_debounce_time0, int, 0664);
module_param(tpd_em_debounce_time1, int, 0664);
module_param(tpd_em_debounce_time, int, 0664);
int tpd_em_spl_num = 1;
module_param(tpd_em_spl_num, int, 0664);
int tpd_em_pressure_threshold;
module_param(tpd_em_pressure_threshold, int, 0664);
int tpd_em_auto_time_interval = 10;
module_param(tpd_em_auto_time_interval, int, 0664);
int tpd_em_sample_cnt = 16;
module_param(tpd_em_sample_cnt, int, 0664);
int tpd_load_status;
module_param(tpd_load_status, int, 0664);
int tpd_em_asamp = 1;
module_param(tpd_em_asamp, int, 0664);
@@ -0,0 +1,58 @@
BUILD_INFAE := spi
BUILD_PLATFORM := mtk
BUILD_MODULE := n
ccflags-y += -Wall
ifeq ($(BUILD_PLATFORM),mtk)
platform=ilitek_plat_mtk
ccflags-y += -I$(srctree)/drivers/spi/mediatek/mt6797/
ccflags-y += -I$(srctree)/drivers/input/touchscreen/ILITEK_V2/
ccflags-y += -I$(srctree)/drivers/input/touchscreen/ILITEK_V2/firmware/
ccflags-y += -I$(srctree)/drivers/input/touchscreen/mediatek/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/mt-plat/
ccflags-y += -I$(srctree)/drivers/misc/mediatek/include/mt-plat/$(MTK_PLATFORM)/include/
endif
ifeq ($(BUILD_PLATFORM),qcom)
platform=ilitek_plat_qcom
ccflags-y += -I$(srctree)/drivers/input/touchscreen/ILITEK_V2/
ccflags-y += -I$(srctree)/drivers/input/touchscreen/ILITEK_V2/firmware/
endif
ifeq ($(BUILD_INFAE),i2c)
interface=ilitek_i2c
fwupdate=ilitek_flash
endif
ifeq ($(BUILD_INFAE),spi)
interface=ilitek_spi
fwupdate=ilitek_hostdl
endif
ifeq ($(BUILD_MODULE),n)
obj-y += ilitek_main.o \
$(interface).o \
$(platform).o \
ilitek_ic.o \
ilitek_touch.o \
$(fwupdate).o \
ilitek_node.o \
tpd_control.o \
tpd_button.o \
tpd_debug.o \
tpd_default.o \
tpd_misc.o \
tpd_setting.o
else
obj-m += ilitek.o
ilitek-y := ilitek_main.o \
$(interface).o \
$(platform).o \
ilitek_ic.o \
ilitek_touch.o \
ilitek_mp.o \
$(fwupdate).o \
ilitek_node.o
endif
@@ -0,0 +1,91 @@
/*
* ILITEK Touch IC driver
*
* Copyright (C) 2011 ILI Technology Corporation.
*
* Author: Dicky Chiang <dicky_chiang@ilitek.com>
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef __ILITEK_FW_H
#define __ILITEK_FW_H
/* define names and paths for the variety of tp modules */
#define DEF_INI_NAME_PATH "/sdcard/mp.ini"
#define DEF_FW_FILP_PATH "/sdcard/ILITEK_FW"
#define DEF_INI_REQUEST_PATH "mp.ini"
#define DEF_FW_REQUEST_PATH "ILITEK_FW"
static unsigned char CTPM_FW_DEF[] = {
#include "9881H_MTK_90Hz_V0x00.0x00_AP_0x01_MP_0x01_SPI_SLAVE_20200818.ili"
};
#define CSOT_INI_NAME_PATH "/sdcard/mp_csot.ini"
#define CSOT_FW_FILP_PATH "/sdcard/ILITEK_FW_CSOT"
#define CSOT_INI_REQUEST_PATH "mp_csot.ini"
#define CSOT_FW_REQUEST_PATH "ILITEK_FW_CSOT"
static unsigned char CTPM_FW_CSOT[] = {
0xFF,
};
#define AUO_INI_NAME_PATH "/sdcard/mp_auo.ini"
#define AUO_FW_FILP_PATH "/sdcard/ILITEK_FW_AUO"
#define AUO_INI_REQUEST_PATH "mp_auo.ini"
#define AUO_FW_REQUEST_PATH "ILITEK_FW_AUO"
static unsigned char CTPM_FW_AUO[] = {
0xFF,
};
#define BOE_INI_NAME_PATH "/sdcard/mp_boe.ini"
#define BOE_FW_FILP_PATH "/sdcard/ILITEK_FW_BOE"
#define BOE_INI_REQUEST_PATH "mp_boe.ini"
#define BOE_FW_REQUEST_PATH "ILITEK_FW_BOE"
static unsigned char CTPM_FW_BOE[] = {
0xFF,
};
#define INX_INI_NAME_PATH "/sdcard/mp_inx.ini"
#define INX_FW_FILP_PATH "/sdcard/ILITEK_FW_INX"
#define INX_INI_REQUEST_PATH "mp_inx.ini"
#define INX_FW_REQUEST_PATH "ILITEK_FW_INX"
static unsigned char CTPM_FW_INX[] = {
0xFF,
};
#define DJ_INI_NAME_PATH "/sdcard/mp_dj.ini"
#define DJ_FW_FILP_PATH "/sdcard/ILITEK_FW_DJ"
#define DJ_INI_REQUEST_PATH "mp_dj.ini"
#define DJ_FW_REQUEST_PATH "ILITEK_FW_DJ"
static unsigned char CTPM_FW_DJ[] = {
0xFF,
};
#define TXD_INI_NAME_PATH "/sdcard/mp_txd.ini"
#define TXD_FW_FILP_PATH "/sdcard/ILITEK_FW_TXD"
#define TXD_INI_REQUEST_PATH "mp_txd.ini"
#define TXD_FW_REQUEST_PATH "ILITEK_FW_TXD"
static unsigned char CTPM_FW_TXD[] = {
0xFF,
};
#define TM_INI_NAME_PATH "/sdcard/mp_tm.ini"
#define TM_FW_FILP_PATH "/sdcard/ILITEK_FW_TM"
#define TM_INI_REQUEST_PATH "mp_tm.ini"
#define TM_FW_REQUEST_PATH "ILITEK_FW_TM"
static unsigned char CTPM_FW_TM[] = {
0xFF,
};
#endif
File diff suppressed because it is too large. Load diff
Loaded 100 of 80519 files, more files were not shown because too many files have changed in this diff. Show more