mirror of
https://github.com/armbian/build
synced 2025-09-24 19:47:06 +07:00
5811 lines
158 KiB
Diff
5811 lines
158 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Patrick Yavitz <pyavitz@armbian.com>
|
|
Date: Fri, 21 Jun 2024 11:54:06 -0400
|
|
Subject: add spacemit patch set
|
|
|
|
source: https://gitee.com/bianbu-linux/linux-6.1
|
|
|
|
Signed-off-by: Patrick Yavitz <pyavitz@armbian.com>
|
|
---
|
|
drivers/input/misc/Kconfig | 9 +
|
|
drivers/input/misc/Makefile | 1 +
|
|
drivers/input/misc/spacemit-pwrkey.c | 144 +
|
|
drivers/input/touchscreen/Kconfig | 10 +
|
|
drivers/input/touchscreen/Makefile | 2 +
|
|
drivers/input/touchscreen/goodix_tool.c | 537 ++
|
|
drivers/input/touchscreen/gt9xx.c | 2541 ++++++++++
|
|
drivers/input/touchscreen/gt9xx.h | 380 ++
|
|
drivers/input/touchscreen/gt9xx_update.c | 2092 ++++++++
|
|
9 files changed, 5716 insertions(+)
|
|
|
|
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
|
|
index 111111111111..222222222222 100644
|
|
--- a/drivers/input/misc/Kconfig
|
|
+++ b/drivers/input/misc/Kconfig
|
|
@@ -890,6 +890,15 @@ config INPUT_HISI_POWERKEY
|
|
To compile this driver as a module, choose M here: the
|
|
module will be called hisi_powerkey.
|
|
|
|
+config INPUT_SPACEMIT_POWERKEY
|
|
+ tristate "Spacemit PMIC pwrkey support"
|
|
+ depends on MFD_SPACEMIT_PMIC
|
|
+ help
|
|
+ Say Y to enable support for PMIC PWRKEY.
|
|
+
|
|
+ To compile this driver as a module, choose M here: the
|
|
+ module will be called spacemit-pwrkey.
|
|
+
|
|
config INPUT_RAVE_SP_PWRBUTTON
|
|
tristate "RAVE SP Power button Driver"
|
|
depends on RAVE_SP_CORE
|
|
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
|
|
index 111111111111..222222222222 100644
|
|
--- a/drivers/input/misc/Makefile
|
|
+++ b/drivers/input/misc/Makefile
|
|
@@ -89,3 +89,4 @@ obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o
|
|
obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o
|
|
obj-$(CONFIG_INPUT_YEALINK) += yealink.o
|
|
obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR) += ideapad_slidebar.o
|
|
+obj-$(CONFIG_INPUT_SPACEMIT_POWERKEY) += spacemit-pwrkey.o
|
|
diff --git a/drivers/input/misc/spacemit-pwrkey.c b/drivers/input/misc/spacemit-pwrkey.c
|
|
new file mode 100644
|
|
index 000000000000..111111111111
|
|
--- /dev/null
|
|
+++ b/drivers/input/misc/spacemit-pwrkey.c
|
|
@@ -0,0 +1,144 @@
|
|
+// SPDX-License-Identifier: GPL-2.0-only
|
|
+
|
|
+#include <linux/of.h>
|
|
+#include <linux/errno.h>
|
|
+#include <linux/init.h>
|
|
+#include <linux/input.h>
|
|
+#include <linux/interrupt.h>
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/pm_wakeirq.h>
|
|
+#include <linux/mfd/spacemit/spacemit_pmic.h>
|
|
+
|
|
+static irqreturn_t pwrkey_fall_irq(int irq, void *_pwr)
|
|
+{
|
|
+ struct input_dev *pwr = _pwr;
|
|
+
|
|
+ input_report_key(pwr, KEY_POWER, 1);
|
|
+ input_sync(pwr);
|
|
+
|
|
+ return IRQ_HANDLED;
|
|
+}
|
|
+
|
|
+static irqreturn_t pwrkey_rise_irq(int irq, void *_pwr)
|
|
+{
|
|
+ struct input_dev *pwr = _pwr;
|
|
+
|
|
+ input_report_key(pwr, KEY_POWER, 0);
|
|
+ input_sync(pwr);
|
|
+
|
|
+ return IRQ_HANDLED;
|
|
+}
|
|
+
|
|
+static irqreturn_t pwrkey_skey_irq(int irq, void *_pwr)
|
|
+{
|
|
+ /* do nothing by now */
|
|
+ return IRQ_HANDLED;
|
|
+}
|
|
+
|
|
+static irqreturn_t pwrkey_lkey_irq(int irq, void *_pwr)
|
|
+{
|
|
+ /* do nothing by now */
|
|
+ return IRQ_HANDLED;
|
|
+}
|
|
+
|
|
+static int spacemit_pwrkey_probe(struct platform_device *pdev)
|
|
+{
|
|
+ int err;
|
|
+ struct input_dev *pwr;
|
|
+ int rise_irq, fall_irq, s_key_irq, l_key_irq;
|
|
+
|
|
+ pwr = devm_input_allocate_device(&pdev->dev);
|
|
+ if (!pwr) {
|
|
+ dev_err(&pdev->dev, "Can't allocate power button\n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ pwr->name = "spacemit pwrkey";
|
|
+ pwr->phys = "spacemit_pwrkey/input0";
|
|
+ pwr->id.bustype = BUS_HOST;
|
|
+ input_set_capability(pwr, EV_KEY, KEY_POWER);
|
|
+
|
|
+ rise_irq = platform_get_irq(pdev, 0);
|
|
+ if (rise_irq < 0)
|
|
+ return rise_irq;
|
|
+
|
|
+ fall_irq = platform_get_irq(pdev, 1);
|
|
+ if (fall_irq < 0)
|
|
+ return fall_irq;
|
|
+
|
|
+ s_key_irq = platform_get_irq(pdev, 2);
|
|
+ if (s_key_irq < 0)
|
|
+ return s_key_irq;
|
|
+
|
|
+ l_key_irq = platform_get_irq(pdev, 3);
|
|
+ if (l_key_irq < 0)
|
|
+ return l_key_irq;
|
|
+
|
|
+ err = devm_request_any_context_irq(&pwr->dev, rise_irq,
|
|
+ pwrkey_rise_irq,
|
|
+ IRQF_TRIGGER_NONE | IRQF_ONESHOT,
|
|
+ "spacemit_pwrkey_rise", pwr);
|
|
+ if (err < 0) {
|
|
+ dev_err(&pdev->dev, "Can't register rise irq: %d\n", err);
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ err = devm_request_any_context_irq(&pwr->dev, fall_irq,
|
|
+ pwrkey_fall_irq,
|
|
+ IRQF_TRIGGER_NONE | IRQF_ONESHOT,
|
|
+ "spacemit_pwrkey_fall", pwr);
|
|
+ if (err < 0) {
|
|
+ dev_err(&pdev->dev, "Can't register fall irq: %d\n", err);
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ err = devm_request_any_context_irq(&pwr->dev, s_key_irq,
|
|
+ pwrkey_skey_irq,
|
|
+ IRQF_TRIGGER_NONE | IRQF_ONESHOT,
|
|
+ "spacemit_pwrkey_skey", pwr);
|
|
+ if (err < 0) {
|
|
+ dev_err(&pdev->dev, "Can't register skey irq: %d\n", err);
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ err = devm_request_any_context_irq(&pwr->dev, l_key_irq,
|
|
+ pwrkey_lkey_irq,
|
|
+ IRQF_TRIGGER_NONE | IRQF_ONESHOT,
|
|
+ "spacemit_pwrkey_lkey", pwr);
|
|
+ if (err < 0) {
|
|
+ dev_err(&pdev->dev, "Can't register lkey irq: %d\n", err);
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ err = input_register_device(pwr);
|
|
+ if (err) {
|
|
+ dev_err(&pdev->dev, "Can't register power button: %d\n", err);
|
|
+ return err;
|
|
+ }
|
|
+
|
|
+ platform_set_drvdata(pdev, pwr);
|
|
+ dev_pm_set_wake_irq(&pdev->dev, fall_irq);
|
|
+ device_init_wakeup(&pdev->dev, true);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct of_device_id spacemit_pwr_key_id_table[] = {
|
|
+ { .compatible = "pmic,pwrkey,spm8821", },
|
|
+ { }
|
|
+};
|
|
+MODULE_DEVICE_TABLE(of, spacemit_pwr_key_id_table);
|
|
+
|
|
+static struct platform_driver spacemit_pwrkey_driver = {
|
|
+ .probe = spacemit_pwrkey_probe,
|
|
+ .driver = {
|
|
+ .name = "spacemit-pmic-pwrkey",
|
|
+ .of_match_table = of_match_ptr(spacemit_pwr_key_id_table),
|
|
+ },
|
|
+};
|
|
+module_platform_driver(spacemit_pwrkey_driver);
|
|
+
|
|
+MODULE_DESCRIPTION("SPACEMIT Power Key driver");
|
|
+MODULE_LICENSE("GPL v2");
|
|
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
|
|
index 111111111111..222222222222 100644
|
|
--- a/drivers/input/touchscreen/Kconfig
|
|
+++ b/drivers/input/touchscreen/Kconfig
|
|
@@ -24,6 +24,16 @@ config TOUCHSCREEN_88PM860X
|
|
To compile this driver as a module, choose M here: the
|
|
module will be called 88pm860x-ts.
|
|
|
|
+config TOUCHSCREEN_GT9XX
|
|
+ tristate "Goodix touchpanel GT9xx series"
|
|
+ depends on I2C
|
|
+ help
|
|
+ Say Y here if you have a Goodix GT9xx touchscreen
|
|
+ controller.
|
|
+
|
|
+ If build module, say M.
|
|
+ If unsure, say N.
|
|
+
|
|
config TOUCHSCREEN_ADS7846
|
|
tristate "ADS7846/TSC2046/AD7873 and AD(S)7843 based touchscreens"
|
|
depends on SPI_MASTER
|
|
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
|
|
index 111111111111..222222222222 100644
|
|
--- a/drivers/input/touchscreen/Makefile
|
|
+++ b/drivers/input/touchscreen/Makefile
|
|
@@ -8,6 +8,8 @@
|
|
wm97xx-ts-y := wm97xx-core.o
|
|
goodix_ts-y := goodix.o goodix_fwupload.o
|
|
|
|
+obj-$(CONFIG_TOUCHSCREEN_GT9XX) += gt9xx_core.o
|
|
+gt9xx_core-y := gt9xx.o gt9xx_update.o goodix_tool.o
|
|
obj-$(CONFIG_TOUCHSCREEN_88PM860X) += 88pm860x-ts.o
|
|
obj-$(CONFIG_TOUCHSCREEN_AD7877) += ad7877.o
|
|
obj-$(CONFIG_TOUCHSCREEN_AD7879) += ad7879.o
|
|
diff --git a/drivers/input/touchscreen/goodix_tool.c b/drivers/input/touchscreen/goodix_tool.c
|
|
new file mode 100644
|
|
index 000000000000..111111111111
|
|
--- /dev/null
|
|
+++ b/drivers/input/touchscreen/goodix_tool.c
|
|
@@ -0,0 +1,537 @@
|
|
+/*
|
|
+ * Goodix GT9xx touchscreen driver
|
|
+ *
|
|
+ * Copyright (C) 2016 - 2017 Goodix. Ltd.
|
|
+ *
|
|
+ * 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 a reference
|
|
+ * to you, when you are integrating the GOODiX's CTP IC into your system,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
+ * General Public License for more details.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include "gt9xx.h"
|
|
+#include <linux/version.h>
|
|
+
|
|
+#define DATA_LENGTH_UINT 512
|
|
+#define CMD_HEAD_LENGTH (sizeof(struct st_cmd_head) - sizeof(u8 *))
|
|
+static char procname[20] = {0};
|
|
+
|
|
+#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 struct i2c_client *gt_client;
|
|
+static struct proc_dir_entry *goodix_proc_entry;
|
|
+
|
|
+static ssize_t goodix_tool_read(struct file *, char __user *, size_t, loff_t *);
|
|
+static ssize_t goodix_tool_write(struct file *, const char __user *, size_t, loff_t *);
|
|
+
|
|
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0))
|
|
+static const struct proc_ops gtp_proc_ops = {
|
|
+ .proc_read = goodix_tool_read,
|
|
+ .proc_write = goodix_tool_write,
|
|
+};
|
|
+#else
|
|
+static const struct file_operations gtp_proc_ops = {
|
|
+ .read = goodix_tool_read,
|
|
+ .write = goodix_tool_write,
|
|
+};
|
|
+#endif
|
|
+
|
|
+
|
|
+/* static s32 goodix_tool_write(struct file *filp,
|
|
+ * const char __user *buff, unsigned long len, void *data);
|
|
+ */
|
|
+/*static s32 goodix_tool_read( char *page, char
|
|
+ **start, off_t off, int count, int *eof, void *data );
|
|
+ */
|
|
+static s32 (*tool_i2c_read)(u8 *, u16);
|
|
+static s32 (*tool_i2c_write)(u8 *, u16);
|
|
+
|
|
+static s32 DATA_LENGTH = (s32)0;
|
|
+static s8 IC_TYPE[16] = "GT9XX";
|
|
+
|
|
+static void tool_set_proc_name(char *procname)
|
|
+{
|
|
+ snprintf(procname, 20, "gmnode"); /* modify for moto */
|
|
+}
|
|
+
|
|
+static s32 tool_i2c_read_no_extra(u8 *buf, u16 len)
|
|
+{
|
|
+ s32 ret = -1;
|
|
+ s32 i = 0;
|
|
+ struct goodix_ts_data *ts = i2c_get_clientdata(i2c_connect_client);
|
|
+
|
|
+ for (i = 0; i < cmd_head.retry; i++) {
|
|
+ ret = gtp_i2c_read(ts->client, buf, len + GTP_ADDR_LENGTH);
|
|
+ if (ret > 0)
|
|
+ break;
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static s32 tool_i2c_write_no_extra(u8 *buf, u16 len)
|
|
+{
|
|
+ s32 ret = -1;
|
|
+ s32 i = 0;
|
|
+ struct goodix_ts_data *ts = i2c_get_clientdata(i2c_connect_client);
|
|
+
|
|
+ for (i = 0; i < cmd_head.retry; i++) {
|
|
+ ret = gtp_i2c_write(ts->client, buf, len);
|
|
+ if (ret > 0)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static s32 tool_i2c_read_with_extra(u8 *buf, u16 len)
|
|
+{
|
|
+ s32 ret = -1;
|
|
+ u8 pre[2] = {0x0f, 0xff};
|
|
+ u8 end[2] = {0x80, 0x00};
|
|
+
|
|
+ tool_i2c_write_no_extra(pre, 2);
|
|
+ ret = tool_i2c_read_no_extra(buf, len);
|
|
+ tool_i2c_write_no_extra(end, 2);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static s32 tool_i2c_write_with_extra(u8 *buf, u16 len)
|
|
+{
|
|
+ s32 ret = -1;
|
|
+ u8 pre[2] = {0x0f, 0xff};
|
|
+ u8 end[2] = {0x80, 0x00};
|
|
+
|
|
+ tool_i2c_write_no_extra(pre, 2);
|
|
+ ret = tool_i2c_write_no_extra(buf, len);
|
|
+ tool_i2c_write_no_extra(end, 2);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void register_i2c_func(void)
|
|
+{
|
|
+ /* if (!strncmp(IC_TYPE, "GT818", 5)
|
|
+ * || !strncmp(IC_TYPE, "GT816", 5)
|
|
+ * || !strncmp(IC_TYPE, "GT811", 5)
|
|
+ * || !strncmp(IC_TYPE, "GT818F", 6)
|
|
+ * || !strncmp(IC_TYPE, "GT827", 5)
|
|
+ * || !strncmp(IC_TYPE,"GT828", 5)
|
|
+ * || !strncmp(IC_TYPE, "GT813", 5))
|
|
+ */
|
|
+ if (strncmp(IC_TYPE, "GT8110", 6) &&
|
|
+ strncmp(IC_TYPE, "GT8105", 6) &&
|
|
+ strncmp(IC_TYPE, "GT801", 5) &&
|
|
+ strncmp(IC_TYPE, "GT800", 5) &&
|
|
+ strncmp(IC_TYPE, "GT801PLUS", 9) &&
|
|
+ strncmp(IC_TYPE, "GT811", 5) &&
|
|
+ strncmp(IC_TYPE, "GTxxx", 5) &&
|
|
+ strncmp(IC_TYPE, "GT9XX", 5)) {
|
|
+ tool_i2c_read = tool_i2c_read_with_extra;
|
|
+ tool_i2c_write = tool_i2c_write_with_extra;
|
|
+ dev_dbg(>_client->dev, "I2C function: with pre and end cmd!");
|
|
+ } else {
|
|
+ tool_i2c_read = tool_i2c_read_no_extra;
|
|
+ tool_i2c_write = tool_i2c_write_no_extra;
|
|
+ dev_info(>_client->dev, "I2C function: without pre and end cmd!");
|
|
+ }
|
|
+}
|
|
+
|
|
+static void unregister_i2c_func(void)
|
|
+{
|
|
+ tool_i2c_read = NULL;
|
|
+ tool_i2c_write = NULL;
|
|
+ dev_info(>_client->dev, "I2C function: unregister i2c transfer function!");
|
|
+}
|
|
+
|
|
+s32 init_wr_node(struct i2c_client *client)
|
|
+{
|
|
+ s32 i;
|
|
+
|
|
+ gt_client = client;
|
|
+ memset(&cmd_head, 0, sizeof(cmd_head));
|
|
+ cmd_head.data = NULL;
|
|
+
|
|
+ i = 6;
|
|
+ while ((!cmd_head.data) && i) {
|
|
+ cmd_head.data = kzalloc(i * DATA_LENGTH_UINT, GFP_KERNEL);
|
|
+ if (cmd_head.data)
|
|
+ break;
|
|
+ i--;
|
|
+ }
|
|
+ if (i) {
|
|
+ DATA_LENGTH = i * DATA_LENGTH_UINT - GTP_ADDR_LENGTH;
|
|
+ dev_info(>_client->dev,
|
|
+ "Alloc memory size:%d.", DATA_LENGTH);
|
|
+ } else {
|
|
+ dev_err(>_client->dev, "Apply for memory failed.");
|
|
+ return FAIL;
|
|
+ }
|
|
+
|
|
+ cmd_head.addr_len = 2;
|
|
+ cmd_head.retry = 5;
|
|
+
|
|
+ register_i2c_func();
|
|
+
|
|
+ tool_set_proc_name(procname);
|
|
+ goodix_proc_entry = proc_create(procname, 0666, NULL, >p_proc_ops);
|
|
+ if (!goodix_proc_entry) {
|
|
+ dev_err(>_client->dev, "Couldn't create proc entry!");
|
|
+ return FAIL;
|
|
+ }
|
|
+
|
|
+ dev_info(>_client->dev, "Create proc entry success!");
|
|
+ return SUCCESS;
|
|
+}
|
|
+
|
|
+void uninit_wr_node(void)
|
|
+{
|
|
+ kfree(cmd_head.data);
|
|
+ cmd_head.data = NULL;
|
|
+ unregister_i2c_func();
|
|
+ remove_proc_entry(procname, NULL);
|
|
+}
|
|
+
|
|
+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;
|
|
+ dev_dbg(>_client->dev,
|
|
+ "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) {
|
|
+ dev_err(>_client->dev, "Read flag data failed!");
|
|
+ return FAIL;
|
|
+ }
|
|
+ if (true == relation(buf[GTP_ADDR_LENGTH],
|
|
+ cmd_head.flag_val, cmd_head.flag_relation)) {
|
|
+ dev_dbg(>_client->dev, "value at flag addr:0x%02x.",
|
|
+ buf[GTP_ADDR_LENGTH]);
|
|
+ dev_dbg(>_client->dev, "flag value:0x%02x.",
|
|
+ cmd_head.flag_val);
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ msleep(cmd_head.circle);
|
|
+ }
|
|
+
|
|
+ if (i >= cmd_head.times) {
|
|
+ dev_err(>_client->dev, "Can't get the continue flag!");
|
|
+ return FAIL;
|
|
+ }
|
|
+
|
|
+ return SUCCESS;
|
|
+}
|
|
+
|
|
+ssize_t goodix_tool_write(struct file *filp, const char __user *buff,
|
|
+ size_t len, loff_t *off)
|
|
+{
|
|
+ s32 ret = 0;
|
|
+ struct goodix_ts_data *ts = i2c_get_clientdata(gt_client);
|
|
+
|
|
+ ret = copy_from_user(&cmd_head, buff, CMD_HEAD_LENGTH);
|
|
+ if (ret) {
|
|
+ dev_err(>_client->dev, "copy_from_user failed.");
|
|
+ return -EPERM;
|
|
+ }
|
|
+
|
|
+ dev_dbg(>_client->dev, "[Operation]wr: %02X", cmd_head.wr);
|
|
+ dev_dbg(>_client->dev,
|
|
+ "[Flag]flag: %02X,addr: %02X%02X,value: %02X,relation: %02X",
|
|
+ cmd_head.flag, cmd_head.flag_addr[0],
|
|
+ cmd_head.flag_addr[1], cmd_head.flag_val,
|
|
+ cmd_head.flag_relation);
|
|
+ dev_dbg(>_client->dev,
|
|
+ "[Retry]circle: %d,times: %d,retry: %d, delay: %d",
|
|
+ (s32)cmd_head.circle,
|
|
+ (s32)cmd_head.times, (s32)cmd_head.retry,
|
|
+ (s32)cmd_head.delay);
|
|
+
|
|
+ if (1 == cmd_head.wr) {
|
|
+ if (cmd_head.data_len > DATA_LENGTH) {
|
|
+ dev_err(>_client->dev,
|
|
+ "Tool write failed data too long");
|
|
+ return -EPERM;
|
|
+ }
|
|
+ ret = copy_from_user(&cmd_head.data[GTP_ADDR_LENGTH],
|
|
+ &buff[CMD_HEAD_LENGTH],
|
|
+ cmd_head.data_len);
|
|
+ if (ret) {
|
|
+ dev_err(>_client->dev, "copy_from_user failed.");
|
|
+ return -EPERM;
|
|
+ }
|
|
+ memcpy(&cmd_head.data[GTP_ADDR_LENGTH - cmd_head.addr_len],
|
|
+ cmd_head.addr, cmd_head.addr_len);
|
|
+
|
|
+ GTP_DEBUG_ARRAY(cmd_head.data, cmd_head.data_len +
|
|
+ cmd_head.addr_len);
|
|
+
|
|
+ if (1 == cmd_head.flag) {
|
|
+ if (FAIL == comfirm()) {
|
|
+ dev_err(>_client->dev,
|
|
+ "[WRITE]Comfirm fail!");
|
|
+ return -EPERM;
|
|
+ }
|
|
+ } else if (2 == cmd_head.flag) {
|
|
+ /*Need interrupt!*/
|
|
+ }
|
|
+ if (tool_i2c_write(&cmd_head.data[GTP_ADDR_LENGTH -
|
|
+ cmd_head.addr_len], cmd_head.data_len +
|
|
+ cmd_head.addr_len) <= 0) {
|
|
+ dev_err(>_client->dev, "[WRITE]Write data failed!");
|
|
+ return -EPERM;
|
|
+ }
|
|
+
|
|
+ GTP_DEBUG_ARRAY(&cmd_head.data[GTP_ADDR_LENGTH -
|
|
+ cmd_head.addr_len],
|
|
+ cmd_head.data_len + cmd_head.addr_len);
|
|
+ if (cmd_head.delay)
|
|
+ msleep(cmd_head.delay);
|
|
+ } else if (3 == cmd_head.wr) {
|
|
+ if (cmd_head.data_len > DATA_LENGTH) {
|
|
+ dev_err(>_client->dev,
|
|
+ "Tool write failed data too long");
|
|
+ return -EPERM;
|
|
+ }
|
|
+ ret = copy_from_user(&cmd_head.data[0], &buff[CMD_HEAD_LENGTH],
|
|
+ cmd_head.data_len);
|
|
+ if (ret) {
|
|
+ dev_err(>_client->dev, "copy_from_user failed.");
|
|
+ return -EPERM;
|
|
+ }
|
|
+ memcpy(IC_TYPE, cmd_head.data, cmd_head.data_len);
|
|
+
|
|
+ register_i2c_func();
|
|
+ } else if (5 == cmd_head.wr) {
|
|
+ /*memcpy(IC_TYPE, cmd_head.data, cmd_head.data_len);*/
|
|
+ } else if (7 == cmd_head.wr) {/*disable irq!*/
|
|
+ gtp_work_control_enable(i2c_get_clientdata(gt_client), false);
|
|
+
|
|
+ if (ts->pdata->esd_protect)
|
|
+ gtp_esd_off(ts);
|
|
+ } else if (9 == cmd_head.wr) {/*enable irq!*/
|
|
+ gtp_work_control_enable(i2c_get_clientdata(gt_client), true);
|
|
+
|
|
+ if (ts->pdata->esd_protect)
|
|
+ gtp_esd_on(ts);
|
|
+ } else if (17 == cmd_head.wr) {
|
|
+ if (cmd_head.data_len > DATA_LENGTH) {
|
|
+ dev_err(>_client->dev,
|
|
+ "Tool write failed data too long");
|
|
+ return -EPERM;
|
|
+ }
|
|
+ ret = copy_from_user(&cmd_head.data[GTP_ADDR_LENGTH],
|
|
+ &buff[CMD_HEAD_LENGTH],
|
|
+ cmd_head.data_len);
|
|
+ if (ret) {
|
|
+ dev_dbg(>_client->dev, "copy_from_user failed.");
|
|
+ return -EPERM;
|
|
+ }
|
|
+ if (cmd_head.data[GTP_ADDR_LENGTH]) {
|
|
+ dev_info(>_client->dev, "gtp enter rawdiff.");
|
|
+ set_bit(RAW_DATA_MODE, &ts->flags);
|
|
+ } else {
|
|
+ clear_bit(RAW_DATA_MODE, &ts->flags);
|
|
+ dev_info(>_client->dev, "gtp leave rawdiff.");
|
|
+ }
|
|
+ } else if (19 == cmd_head.wr) {
|
|
+ /* add new command: reset guitar */
|
|
+ gtp_reset_guitar(gt_client, 20);
|
|
+ }
|
|
+
|
|
+ else if (11 == cmd_head.wr) {/*Enter update mode!*/
|
|
+ if (FAIL == gup_enter_update_mode(gt_client))
|
|
+ return -EPERM;
|
|
+ } else if (13 == cmd_head.wr) {/*Leave update mode!*/
|
|
+ gup_leave_update_mode(gt_client);
|
|
+ } else if (15 == cmd_head.wr) {/*Update firmware!*/
|
|
+ show_len = 0;
|
|
+ total_len = 0;
|
|
+ if (cmd_head.data_len > DATA_LENGTH) {
|
|
+ dev_err(>_client->dev,
|
|
+ "Tool write failed data too long");
|
|
+ return -EPERM;
|
|
+ }
|
|
+ memset(cmd_head.data, 0, DATA_LENGTH);
|
|
+ ret = copy_from_user(cmd_head.data,
|
|
+ &buff[CMD_HEAD_LENGTH],
|
|
+ cmd_head.data_len);
|
|
+ if (ret) {
|
|
+ dev_dbg(>_client->dev, "copy_from_user failed.");
|
|
+ return -EPERM;
|
|
+ }
|
|
+
|
|
+ if (FAIL == gup_update_proc((void *)cmd_head.data))
|
|
+ return -EPERM;
|
|
+ }
|
|
+
|
|
+
|
|
+ return len;
|
|
+}
|
|
+
|
|
+/*******************************************************
|
|
+ * Function:
|
|
+ * Goodix tool read function.
|
|
+ * Input:
|
|
+ * standard proc read function param.
|
|
+ * Output:
|
|
+ * Return read length.
|
|
+ ********************************************************/
|
|
+ssize_t goodix_tool_read(struct file *file, char __user *page,
|
|
+ size_t size, loff_t *ppos)
|
|
+{
|
|
+ s32 ret = 0;
|
|
+
|
|
+ if (*ppos) {
|
|
+ /* ADB call again
|
|
+ * dev_dbg(>_client->dev, "[HEAD]wr: %d", cmd_head.wr);
|
|
+ * dev_dbg(>_client->dev,
|
|
+ * "[PARAM]size: %d, *ppos: %d", size, (int)*ppos);
|
|
+ * dev_dbg(>_client->dev,
|
|
+ * "[TOOL_READ]ADB call again, return it.");
|
|
+ */
|
|
+ *ppos = 0;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (cmd_head.wr % 2) {
|
|
+ return -EPERM;
|
|
+ } else if (!cmd_head.wr) {
|
|
+ u16 len, data_len, loc, addr;
|
|
+
|
|
+ if (1 == cmd_head.flag) {
|
|
+ if (FAIL == comfirm()) {
|
|
+ dev_err(>_client->dev, "[READ]Comfirm fail!");
|
|
+ return -EPERM;
|
|
+ }
|
|
+ } else if (2 == cmd_head.flag) {
|
|
+ /*Need interrupt!*/
|
|
+ }
|
|
+
|
|
+ if (cmd_head.delay)
|
|
+ msleep(cmd_head.delay);
|
|
+
|
|
+ data_len = cmd_head.data_len;
|
|
+ addr = (cmd_head.addr[0] << 8) + cmd_head.addr[1];
|
|
+ loc = 0;
|
|
+
|
|
+ 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) {
|
|
+ dev_err(>_client->dev, "[READ]Read data failed!");
|
|
+ return -EPERM;
|
|
+ }
|
|
+ ret = simple_read_from_buffer(&page[loc], size, ppos,
|
|
+ &cmd_head.data[GTP_ADDR_LENGTH], len);
|
|
+ if (ret < 0)
|
|
+ return ret;
|
|
+ loc += len;
|
|
+ addr += len;
|
|
+ data_len -= len;
|
|
+ }
|
|
+ return cmd_head.data_len;
|
|
+ } else if (2 == cmd_head.wr) {
|
|
+ ret = simple_read_from_buffer(page, size, ppos,
|
|
+ IC_TYPE, sizeof(IC_TYPE));
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ else if (4 == cmd_head.wr) {
|
|
+ u8 progress_buf[4];
|
|
+
|
|
+ progress_buf[0] = show_len >> 8;
|
|
+ progress_buf[1] = show_len & 0xff;
|
|
+ progress_buf[2] = total_len >> 8;
|
|
+ progress_buf[3] = total_len & 0xff;
|
|
+
|
|
+ ret = simple_read_from_buffer(page, size, ppos,
|
|
+ progress_buf, 4);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ else if (6 == cmd_head.wr) {
|
|
+ /*Read error code!*/
|
|
+ } else if (8 == cmd_head.wr) { /*Read driver version*/
|
|
+ ret = simple_read_from_buffer(page, size, ppos,
|
|
+ GTP_DRIVER_VERSION,
|
|
+ strlen(GTP_DRIVER_VERSION));
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ return -EPERM;
|
|
+}
|
|
diff --git a/drivers/input/touchscreen/gt9xx.c b/drivers/input/touchscreen/gt9xx.c
|
|
new file mode 100644
|
|
index 000000000000..111111111111
|
|
--- /dev/null
|
|
+++ b/drivers/input/touchscreen/gt9xx.c
|
|
@@ -0,0 +1,2541 @@
|
|
+/*
|
|
+ * Goodix GT9xx touchscreen driver
|
|
+ *
|
|
+ * Copyright (C) 2016 - 2017 Goodix. Ltd.
|
|
+ *
|
|
+ * 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 a reference
|
|
+ * to you, when you are integrating the GOODiX's CTP IC into your system,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
+ * General Public License for more details.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include <linux/irq.h>
|
|
+#include <linux/version.h>
|
|
+#include <linux/platform_device.h>
|
|
+#include <linux/pinctrl/consumer.h>
|
|
+#include <linux/input/mt.h>
|
|
+#include "gt9xx.h"
|
|
+
|
|
+#define GOODIX_COORDS_ARR_SIZE 4
|
|
+#define PROP_NAME_SIZE 24
|
|
+#define I2C_MAX_TRANSFER_SIZE 255
|
|
+#define GTP_PEN_BUTTON1 BTN_STYLUS
|
|
+#define GTP_PEN_BUTTON2 BTN_STYLUS2
|
|
+#define PULL_UP BIT(14) | BIT(15) | BIT(12) | BIT(6)
|
|
+
|
|
+static const char *goodix_ts_name = "goodix-ts";
|
|
+static const char *goodix_input_phys = "input/ts";
|
|
+struct i2c_client *i2c_connect_client;
|
|
+static struct proc_dir_entry *gtp_config_proc;
|
|
+
|
|
+enum doze {
|
|
+ DOZE_DISABLED = 0,
|
|
+ DOZE_ENABLED = 1,
|
|
+ DOZE_WAKEUP = 2,
|
|
+};
|
|
+
|
|
+static enum doze doze_status = DOZE_DISABLED;
|
|
+
|
|
+static int gtp_i2c_test(struct i2c_client *client);
|
|
+static int gtp_enter_doze(struct goodix_ts_data *ts);
|
|
+
|
|
+static int gtp_unregister_powermanager(struct goodix_ts_data *ts);
|
|
+static int gtp_register_powermanager(struct goodix_ts_data *ts);
|
|
+
|
|
+static int gtp_esd_init(struct goodix_ts_data *ts);
|
|
+static void gtp_esd_check_func(struct work_struct *);
|
|
+static int gtp_init_ext_watchdog(struct i2c_client *client);
|
|
+
|
|
+/*
|
|
+ * return: 2 - ok, < 0 - i2c transfer error
|
|
+ */
|
|
+int gtp_i2c_read(struct i2c_client *client, u8 *buf, int len)
|
|
+{
|
|
+ unsigned int transfer_length = 0;
|
|
+ unsigned int pos = 0, address = (buf[0] << 8) + buf[1];
|
|
+ unsigned char get_buf[64], addr_buf[2];
|
|
+ int retry, r = 2;
|
|
+ struct i2c_msg msgs[] = {
|
|
+ {
|
|
+ .addr = client->addr,
|
|
+ .flags = !I2C_M_RD,
|
|
+ .buf = &addr_buf[0],
|
|
+ .len = GTP_ADDR_LENGTH,
|
|
+ }, {
|
|
+ .addr = client->addr,
|
|
+ .flags = I2C_M_RD,
|
|
+ }
|
|
+ };
|
|
+
|
|
+ len -= GTP_ADDR_LENGTH;
|
|
+ if (likely(len < sizeof(get_buf))) {
|
|
+ /* code optimize, use stack memory */
|
|
+ msgs[1].buf = &get_buf[0];
|
|
+ } else {
|
|
+ msgs[1].buf = kzalloc(len > I2C_MAX_TRANSFER_SIZE
|
|
+ ? I2C_MAX_TRANSFER_SIZE : len, GFP_KERNEL);
|
|
+ if (!msgs[1].buf)
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ while (pos != len) {
|
|
+ if (unlikely(len - pos > I2C_MAX_TRANSFER_SIZE))
|
|
+ transfer_length = I2C_MAX_TRANSFER_SIZE;
|
|
+ else
|
|
+ transfer_length = len - pos;
|
|
+ msgs[0].buf[0] = (address >> 8) & 0xFF;
|
|
+ msgs[0].buf[1] = address & 0xFF;
|
|
+ msgs[1].len = transfer_length;
|
|
+ for (retry = 0; retry < RETRY_MAX_TIMES; retry++) {
|
|
+ if (likely(i2c_transfer(client->adapter, msgs, 2) == 2)) {
|
|
+ memcpy(&buf[2 + pos], msgs[1].buf, transfer_length);
|
|
+ pos += transfer_length;
|
|
+ address += transfer_length;
|
|
+ break;
|
|
+ }
|
|
+ dev_dbg(&client->dev, "I2c read retry[%d]:0x%x\n",
|
|
+ retry + 1, address);
|
|
+ usleep_range(2000, 2100);
|
|
+ }
|
|
+ if (unlikely(retry == RETRY_MAX_TIMES)) {
|
|
+ dev_err(&client->dev,
|
|
+ "I2c read failed,dev:%02x,reg:%04x,size:%u\n",
|
|
+ client->addr, address, len);
|
|
+ r = -EAGAIN;
|
|
+ goto read_exit;
|
|
+ }
|
|
+ }
|
|
+read_exit:
|
|
+ if (len >= sizeof(get_buf))
|
|
+ kfree(msgs[1].buf);
|
|
+ return r;
|
|
+}
|
|
+
|
|
+/*******************************************************
|
|
+ * Function:
|
|
+ * Write data to the i2c slave device.
|
|
+ * Input:
|
|
+ * client: i2c device.
|
|
+ * buf[0~1]: write start address.
|
|
+ * buf[2~len-1]: data buffer
|
|
+ * len: GTP_ADDR_LENGTH + write bytes count
|
|
+ * Output:
|
|
+ * numbers of i2c_msgs to transfer:
|
|
+ * 1: succeed, otherwise: failed
|
|
+ *********************************************************/
|
|
+int gtp_i2c_write(struct i2c_client *client, u8 *buf, int len)
|
|
+
|
|
+{
|
|
+ unsigned int pos = 0, transfer_length = 0;
|
|
+ unsigned int address = (buf[0] << 8) + buf[1];
|
|
+ unsigned char put_buf[64];
|
|
+ int retry, r = 1;
|
|
+ struct i2c_msg msg = {
|
|
+ .addr = client->addr,
|
|
+ .flags = !I2C_M_RD,
|
|
+ };
|
|
+
|
|
+ if (likely(len < sizeof(put_buf))) {
|
|
+ /* code optimize,use stack memory*/
|
|
+ msg.buf = &put_buf[0];
|
|
+ } else {
|
|
+ msg.buf = kmalloc(len > I2C_MAX_TRANSFER_SIZE
|
|
+ ? I2C_MAX_TRANSFER_SIZE : len, GFP_KERNEL);
|
|
+ if (!msg.buf)
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ len -= GTP_ADDR_LENGTH;
|
|
+ while (pos != len) {
|
|
+ if (unlikely(len - pos > I2C_MAX_TRANSFER_SIZE - GTP_ADDR_LENGTH))
|
|
+ transfer_length = I2C_MAX_TRANSFER_SIZE - GTP_ADDR_LENGTH;
|
|
+ else
|
|
+ transfer_length = len - pos;
|
|
+ msg.buf[0] = (unsigned char)((address >> 8) & 0xFF);
|
|
+ msg.buf[1] = (unsigned char)(address & 0xFF);
|
|
+ msg.len = transfer_length + 2;
|
|
+ memcpy(&msg.buf[2], &buf[2 + pos], transfer_length);
|
|
+ for (retry = 0; retry < RETRY_MAX_TIMES; retry++) {
|
|
+ if (likely(i2c_transfer(client->adapter, &msg, 1) == 1)) {
|
|
+ pos += transfer_length;
|
|
+ address += transfer_length;
|
|
+ break;
|
|
+ }
|
|
+ dev_dbg(&client->dev, "I2C write retry[%d]\n", retry + 1);
|
|
+ usleep_range(2000, 2100);
|
|
+ }
|
|
+ if (unlikely(retry == RETRY_MAX_TIMES)) {
|
|
+ dev_err(&client->dev,
|
|
+ "I2c write failed,dev:%02x,reg:%04x,size:%u\n",
|
|
+ client->addr, address, len);
|
|
+ r = -EAGAIN;
|
|
+ goto write_exit;
|
|
+ }
|
|
+ }
|
|
+write_exit:
|
|
+ if (len + GTP_ADDR_LENGTH >= sizeof(put_buf))
|
|
+ kfree(msg.buf);
|
|
+ return r;
|
|
+}
|
|
+
|
|
+/*******************************************************
|
|
+ * Function:
|
|
+ * i2c read twice, compare the results
|
|
+ * Input:
|
|
+ * client: i2c device
|
|
+ * addr: operate address
|
|
+ * rxbuf: read data to store, if compare successful
|
|
+ * len: bytes to read
|
|
+ * Output:
|
|
+ * FAIL: read failed
|
|
+ * SUCCESS: read successful
|
|
+ *********************************************************/
|
|
+s32 gtp_i2c_read_dbl_check(struct i2c_client *client,
|
|
+ u16 addr, u8 *rxbuf, int len)
|
|
+{
|
|
+ u8 buf[16] = {0};
|
|
+ u8 confirm_buf[16] = {0};
|
|
+ u8 retry = 0;
|
|
+
|
|
+ if (len + 2 > sizeof(buf)) {
|
|
+ dev_warn(&client->dev,
|
|
+ "%s, only support length less then %zu\n",
|
|
+ __func__, sizeof(buf) - 2);
|
|
+ return FAIL;
|
|
+ }
|
|
+ while (retry++ < 3) {
|
|
+ memset(buf, 0xAA, 16);
|
|
+ buf[0] = (u8)(addr >> 8);
|
|
+ buf[1] = (u8)(addr & 0xFF);
|
|
+ gtp_i2c_read(client, buf, len + 2);
|
|
+
|
|
+ memset(confirm_buf, 0xAB, 16);
|
|
+ confirm_buf[0] = (u8)(addr >> 8);
|
|
+ confirm_buf[1] = (u8)(addr & 0xFF);
|
|
+ gtp_i2c_read(client, confirm_buf, len + 2);
|
|
+
|
|
+ if (!memcmp(buf, confirm_buf, len + 2)) {
|
|
+ memcpy(rxbuf, confirm_buf + 2, len);
|
|
+ return SUCCESS;
|
|
+ }
|
|
+ }
|
|
+ dev_err(&client->dev,
|
|
+ "I2C read 0x%04X, %d bytes, double check failed!\n",
|
|
+ addr, len);
|
|
+
|
|
+ return FAIL;
|
|
+}
|
|
+
|
|
+/*******************************************************
|
|
+ * Function:
|
|
+ * Send config.
|
|
+ * Input:
|
|
+ * client: i2c device.
|
|
+ * Output:
|
|
+ * result of i2c write operation.
|
|
+ * 1: succeed, otherwise
|
|
+ * 0: Not executed
|
|
+ * < 0: failed
|
|
+ *********************************************************/
|
|
+s32 gtp_send_cfg(struct i2c_client *client)
|
|
+{
|
|
+ s32 ret, i;
|
|
+ u8 check_sum;
|
|
+ s32 retry = 0;
|
|
+ struct goodix_ts_data *ts = i2c_get_clientdata(client);
|
|
+ struct goodix_config_data *cfg = &ts->pdata->config;
|
|
+
|
|
+ if (!cfg->length || !ts->pdata->driver_send_cfg) {
|
|
+ dev_info(&ts->client->dev,
|
|
+ "No config data or error occurred in panel_init\n");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ check_sum = 0;
|
|
+ for (i = GTP_ADDR_LENGTH; i < cfg->length; i++)
|
|
+ check_sum += cfg->data[i];
|
|
+ cfg->data[cfg->length] = (~check_sum) + 1;
|
|
+
|
|
+ dev_info(&ts->client->dev, "Driver send config\n");
|
|
+ for (retry = 0; retry < RETRY_MAX_TIMES; retry++) {
|
|
+ ret = gtp_i2c_write(client, cfg->data,
|
|
+ GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH);
|
|
+ if (ret > 0)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/*******************************************************
|
|
+ * Function:
|
|
+ * Control enable or disable of work thread.
|
|
+ * Input:
|
|
+ * ts: goodix i2c_client private data
|
|
+ * enable: enable var.
|
|
+ *********************************************************/
|
|
+void gtp_work_control_enable(struct goodix_ts_data *ts, bool enable)
|
|
+{
|
|
+ if (enable) {
|
|
+ set_bit(WORK_THREAD_ENABLED, &ts->flags);
|
|
+ dev_dbg(&ts->client->dev, "Input report thread enabled!\n");
|
|
+ } else {
|
|
+ clear_bit(WORK_THREAD_ENABLED, &ts->flags);
|
|
+ dev_dbg(&ts->client->dev, "Input report thread disabled!\n");
|
|
+ }
|
|
+}
|
|
+
|
|
+static int gtp_gesture_handler(struct goodix_ts_data *ts)
|
|
+{
|
|
+ u8 doze_buf[3] = {GTP_REG_DOZE_BUF >> 8, GTP_REG_DOZE_BUF & 0xFF};
|
|
+ int ret;
|
|
+
|
|
+ ret = gtp_i2c_read(ts->client, doze_buf, 3);
|
|
+ if (ret < 0) {
|
|
+ dev_err(&ts->client->dev, "Failed read doze buf");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ dev_dbg(&ts->client->dev, "0x814B = 0x%02X", doze_buf[2]);
|
|
+ if ((doze_buf[2] == 'a') || (doze_buf[2] == 'b') ||
|
|
+ (doze_buf[2] == 'c') || (doze_buf[2] == 'd') ||
|
|
+ (doze_buf[2] == 'e') || (doze_buf[2] == 'g') ||
|
|
+ (doze_buf[2] == 'h') || (doze_buf[2] == 'm') ||
|
|
+ (doze_buf[2] == 'o') || (doze_buf[2] == 'q') ||
|
|
+ (doze_buf[2] == 's') || (doze_buf[2] == 'v') ||
|
|
+ (doze_buf[2] == 'w') || (doze_buf[2] == 'y') ||
|
|
+ (doze_buf[2] == 'z') || (doze_buf[2] == 0x5E) ||
|
|
+ (doze_buf[2] == 0xAA) || (doze_buf[2] == 0xAB) ||
|
|
+ (doze_buf[2] == 0xBA) || (doze_buf[2] == 0xBB) ||
|
|
+ (doze_buf[2] == 0xCC)) {
|
|
+ doze_status = DOZE_WAKEUP;
|
|
+ input_report_key(ts->input_dev, KEY_POWER, 1);
|
|
+ input_sync(ts->input_dev);
|
|
+ input_report_key(ts->input_dev, KEY_POWER, 0);
|
|
+ input_sync(ts->input_dev);
|
|
+ /* clear 0x814B */
|
|
+ doze_buf[2] = 0x00;
|
|
+ gtp_i2c_write(ts->client, doze_buf, 3);
|
|
+ } else {
|
|
+ /* clear 0x814B */
|
|
+ doze_buf[2] = 0x00;
|
|
+ gtp_i2c_write(ts->client, doze_buf, 3);
|
|
+ gtp_enter_doze(ts);
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * return touch state register value
|
|
+ * pen event id fixed with 9 and set tool type TOOL_PEN
|
|
+ *
|
|
+ */
|
|
+static u8 gtp_get_points(struct goodix_ts_data *ts,
|
|
+ struct goodix_point_t *points,
|
|
+ u8 *key_value)
|
|
+{
|
|
+ int ret;
|
|
+ int i;
|
|
+ u8 *coor_data = NULL;
|
|
+ u8 finger_state = 0;
|
|
+ u8 have_key = 0;
|
|
+ u8 touch_num = 0;
|
|
+ u8 end_cmd[3] = { GTP_READ_COOR_ADDR >> 8,
|
|
+ GTP_READ_COOR_ADDR & 0xFF, 0 };
|
|
+ u8 point_data[2 + 1 + 8 * GTP_MAX_TOUCH_ID + 1] = {
|
|
+ GTP_READ_COOR_ADDR >> 8, GTP_READ_COOR_ADDR & 0xFF };
|
|
+
|
|
+ ret = gtp_i2c_read(ts->client, point_data, 12);
|
|
+ if (ret < 0) {
|
|
+ dev_err(&ts->client->dev,
|
|
+ "I2C transfer error. errno:%d\n ", ret);
|
|
+ return 0;
|
|
+ }
|
|
+ finger_state = point_data[GTP_ADDR_LENGTH];
|
|
+ if (finger_state == 0x00)
|
|
+ return 0;
|
|
+
|
|
+ have_key = finger_state & 0x10;
|
|
+ touch_num = finger_state & 0x0f;
|
|
+ if ((finger_state & MASK_BIT_8) == 0 ||
|
|
+ touch_num > ts->pdata->max_touch_id) {
|
|
+ dev_err(&ts->client->dev,
|
|
+ "Invalid touch state: 0x%x", finger_state);
|
|
+ finger_state = 0;
|
|
+ goto exit_get_point;
|
|
+ }
|
|
+
|
|
+ if (touch_num > 1) {
|
|
+ u8 buf[8 * GTP_MAX_TOUCH_ID] = {
|
|
+ (GTP_READ_COOR_ADDR + 10) >> 8,
|
|
+ (GTP_READ_COOR_ADDR + 10) & 0xff };
|
|
+
|
|
+ ret = gtp_i2c_read(ts->client, buf, 2 + 8 * (touch_num - 1));
|
|
+ if (ret < 0) {
|
|
+ dev_err(&ts->client->dev, "I2C error. %d\n", ret);
|
|
+ finger_state = 0;
|
|
+ goto exit_get_point;
|
|
+ }
|
|
+ memcpy(&point_data[12], &buf[2], 8 * (touch_num - 1));
|
|
+ }
|
|
+
|
|
+ /* panel have touch key */
|
|
+ /* 0x20_UPKEY 0X10_DOWNKEY 0X40_ALLKEYDOWN */
|
|
+ if (have_key)
|
|
+ *key_value = point_data[3 + 8 * touch_num];
|
|
+ else
|
|
+ *key_value = 0;
|
|
+
|
|
+ memset(points, 0, sizeof(*points) * GTP_MAX_TOUCH_ID);
|
|
+ for (i = 0; i < touch_num; i++) {
|
|
+ coor_data = &point_data[i * 8 + 3];
|
|
+ points[i].id = coor_data[0];
|
|
+ points[i].x = coor_data[1] | (coor_data[2] << 8);
|
|
+ points[i].y = coor_data[3] | (coor_data[4] << 8);
|
|
+ points[i].w = coor_data[5] | (coor_data[6] << 8);
|
|
+ /* if pen hover points[].p must set to zero */
|
|
+ points[i].p = coor_data[5] | (coor_data[6] << 8);
|
|
+
|
|
+ if (ts->pdata->swap_x2y)
|
|
+ GTP_SWAP(points[i].x, points[i].y);
|
|
+
|
|
+ dev_dbg(&ts->client->dev, "[%d][%d %d %d]\n",
|
|
+ points[i].id, points[i].x, points[i].y, points[i].p);
|
|
+
|
|
+ /* pen device coordinate */
|
|
+ if (points[i].id & 0x80) {
|
|
+ points[i].tool_type = GTP_TOOL_PEN;
|
|
+ points[i].id = 10;
|
|
+ if (ts->pdata->pen_suppress_finger) {
|
|
+ points[0] = points[i];
|
|
+ memset(++points, 0, sizeof(*points) * (GTP_MAX_TOUCH_ID - 1));
|
|
+ finger_state &= 0xf0;
|
|
+ finger_state |= 0x01;
|
|
+ break;
|
|
+ }
|
|
+ } else {
|
|
+ points[i].tool_type = GTP_TOOL_FINGER;
|
|
+ }
|
|
+ }
|
|
+
|
|
+exit_get_point:
|
|
+ if (!test_bit(RAW_DATA_MODE, &ts->flags)) {
|
|
+ ret = gtp_i2c_write(ts->client, end_cmd, 3);
|
|
+ if (ret < 0)
|
|
+ dev_info(&ts->client->dev, "I2C write end_cmd error!");
|
|
+ }
|
|
+ return finger_state;
|
|
+}
|
|
+
|
|
+static void gtp_type_a_report(struct goodix_ts_data *ts, u8 touch_num,
|
|
+ struct goodix_point_t *points)
|
|
+{
|
|
+ int i;
|
|
+ u16 cur_touch = 0;
|
|
+ static u16 pre_touch;
|
|
+ static u8 pre_pen_id;
|
|
+
|
|
+ if (touch_num)
|
|
+ input_report_key(ts->input_dev, BTN_TOUCH, 1);
|
|
+
|
|
+ for (i = 0; i < ts->pdata->max_touch_id; i++) {
|
|
+ if (touch_num && i == points->id) {
|
|
+ input_report_abs(ts->input_dev, ABS_MT_TRACKING_ID, points->id);
|
|
+
|
|
+ if (points->tool_type == GTP_TOOL_PEN) {
|
|
+ input_report_key(ts->input_dev, BTN_TOOL_PEN, true);
|
|
+ pre_pen_id = points->id;
|
|
+ } else {
|
|
+ input_report_key(ts->input_dev, BTN_TOOL_FINGER, true);
|
|
+ }
|
|
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_X,
|
|
+ points->x);
|
|
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_Y,
|
|
+ points->y);
|
|
+ input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR,
|
|
+ points->w);
|
|
+ input_report_abs(ts->input_dev, ABS_MT_PRESSURE,
|
|
+ points->p);
|
|
+ input_mt_sync(ts->input_dev);
|
|
+
|
|
+ cur_touch |= 0x01 << points->id;
|
|
+ points++;
|
|
+ } else if (pre_touch & 0x01 << i) {
|
|
+ if (pre_pen_id == i) {
|
|
+ input_report_key(ts->input_dev, BTN_TOOL_PEN, false);
|
|
+ /* valid id will < 10, so id to 0xff to indicate a invalid state */
|
|
+ pre_pen_id = 0xff;
|
|
+ } else {
|
|
+ input_report_key(ts->input_dev, BTN_TOOL_FINGER, false);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ pre_touch = cur_touch;
|
|
+ if (!pre_touch) {
|
|
+ input_mt_sync(ts->input_dev);
|
|
+ input_report_key(ts->input_dev, BTN_TOUCH, 0);
|
|
+ }
|
|
+ input_sync(ts->input_dev);
|
|
+}
|
|
+
|
|
+static void gtp_mt_slot_report(struct goodix_ts_data *ts, u8 touch_num,
|
|
+ struct goodix_point_t *points)
|
|
+{
|
|
+ int i;
|
|
+ u16 cur_touch = 0;
|
|
+ static u16 pre_touch;
|
|
+ static u8 pre_pen_id;
|
|
+
|
|
+ for (i = 0; i < ts->pdata->max_touch_id; i++) {
|
|
+ if (touch_num && i == points->id) {
|
|
+ input_mt_slot(ts->input_dev, points->id);
|
|
+
|
|
+ if (points->tool_type == GTP_TOOL_PEN) {
|
|
+ input_mt_report_slot_state(ts->input_dev,
|
|
+ MT_TOOL_PEN, true);
|
|
+ pre_pen_id = points->id;
|
|
+ } else {
|
|
+ input_mt_report_slot_state(ts->input_dev,
|
|
+ MT_TOOL_FINGER, true);
|
|
+ }
|
|
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_X,
|
|
+ points->x);
|
|
+ input_report_abs(ts->input_dev, ABS_MT_POSITION_Y,
|
|
+ points->y);
|
|
+ input_report_abs(ts->input_dev, ABS_MT_TOUCH_MAJOR,
|
|
+ points->w);
|
|
+ input_report_abs(ts->input_dev, ABS_MT_PRESSURE,
|
|
+ points->p);
|
|
+
|
|
+ cur_touch |= 0x01 << points->id;
|
|
+ points++;
|
|
+ } else if (pre_touch & 0x01 << i) {
|
|
+ input_mt_slot(ts->input_dev, i);
|
|
+ if (pre_pen_id == i) {
|
|
+ input_mt_report_slot_state(ts->input_dev,
|
|
+ MT_TOOL_PEN, false);
|
|
+ /* valid id will < 10, so set id to 0xff to
|
|
+ * indicate a invalid state
|
|
+ */
|
|
+ pre_pen_id = 0xff;
|
|
+ } else {
|
|
+ input_mt_report_slot_state(ts->input_dev,
|
|
+ MT_TOOL_FINGER, false);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ pre_touch = cur_touch;
|
|
+ /* report BTN_TOUCH event */
|
|
+ input_mt_sync_frame(ts->input_dev);
|
|
+ input_sync(ts->input_dev);
|
|
+}
|
|
+
|
|
+/*******************************************************
|
|
+ * Function:
|
|
+ * Goodix touchscreen sensor report function
|
|
+ * Input:
|
|
+ * ts: goodix tp private data
|
|
+ * Output:
|
|
+ * None.
|
|
+ *********************************************************/
|
|
+static void gtp_work_func(struct goodix_ts_data *ts)
|
|
+{
|
|
+ u8 point_state = 0;
|
|
+ u8 key_value = 0;
|
|
+ s32 i = 0;
|
|
+ s32 ret = -1;
|
|
+ static u8 pre_key;
|
|
+ struct goodix_point_t points[GTP_MAX_TOUCH_ID];
|
|
+
|
|
+ if (test_bit(PANEL_RESETTING, &ts->flags))
|
|
+ return;
|
|
+ if (!test_bit(WORK_THREAD_ENABLED, &ts->flags))
|
|
+ return;
|
|
+
|
|
+ /* gesture event */
|
|
+ if (ts->pdata->slide_wakeup && test_bit(DOZE_MODE, &ts->flags)) {
|
|
+ ret = gtp_gesture_handler(ts);
|
|
+ if (ret)
|
|
+ dev_err(&ts->client->dev,
|
|
+ "Failed handler gesture event %d\n", ret);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ point_state = gtp_get_points(ts, points, &key_value);
|
|
+ if (!point_state) {
|
|
+ dev_dbg(&ts->client->dev, "Invalid finger points\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* touch key event */
|
|
+ if (key_value & 0xf0 || pre_key & 0xf0) {
|
|
+ /* pen button */
|
|
+ switch (key_value) {
|
|
+ case 0x40:
|
|
+ input_report_key(ts->input_dev, GTP_PEN_BUTTON1, 1);
|
|
+ input_report_key(ts->input_dev, GTP_PEN_BUTTON2, 1);
|
|
+ break;
|
|
+ case 0x10:
|
|
+ input_report_key(ts->input_dev, GTP_PEN_BUTTON1, 1);
|
|
+ input_report_key(ts->input_dev, GTP_PEN_BUTTON2, 0);
|
|
+ dev_dbg(&ts->client->dev, "pen button1 down\n");
|
|
+ break;
|
|
+ case 0x20:
|
|
+ input_report_key(ts->input_dev, GTP_PEN_BUTTON1, 0);
|
|
+ input_report_key(ts->input_dev, GTP_PEN_BUTTON2, 1);
|
|
+ break;
|
|
+ default:
|
|
+ input_report_key(ts->input_dev, GTP_PEN_BUTTON1, 0);
|
|
+ input_report_key(ts->input_dev, GTP_PEN_BUTTON2, 0);
|
|
+ dev_dbg(&ts->client->dev, "button1 up\n");
|
|
+ break;
|
|
+ }
|
|
+ input_sync(ts->input_dev);
|
|
+ pre_key = key_value;
|
|
+ } else if (key_value & 0x0f || pre_key & 0x0f) {
|
|
+ /* panel key */
|
|
+ for (i = 0; i < ts->pdata->key_nums; i++) {
|
|
+ if ((pre_key | key_value) & (0x01 << i))
|
|
+ input_report_key(ts->input_dev,
|
|
+ ts->pdata->key_map[i],
|
|
+ key_value & (0x01 << i));
|
|
+ }
|
|
+ input_sync(ts->input_dev);
|
|
+ pre_key = key_value;
|
|
+ }
|
|
+
|
|
+ if (!ts->pdata->type_a_report)
|
|
+ gtp_mt_slot_report(ts, point_state & 0x0f, points);
|
|
+ else
|
|
+ gtp_type_a_report(ts, point_state & 0x0f, points);
|
|
+}
|
|
+
|
|
+/*******************************************************
|
|
+ * Function:
|
|
+ * Timer interrupt service routine for polling mode.
|
|
+ * Input:
|
|
+ * timer: timer struct pointer
|
|
+ * Output:
|
|
+ * Timer work mode.
|
|
+ * HRTIMER_NORESTART:
|
|
+ * no restart mode
|
|
+ *********************************************************/
|
|
+static enum hrtimer_restart gtp_timer_handler(struct hrtimer *timer)
|
|
+{
|
|
+ struct goodix_ts_data *ts =
|
|
+ container_of(timer, struct goodix_ts_data, timer);
|
|
+
|
|
+ gtp_work_func(ts);
|
|
+ hrtimer_start(&ts->timer, ktime_set(0, (GTP_POLL_TIME + 6) * 1000000),
|
|
+ HRTIMER_MODE_REL);
|
|
+
|
|
+ return HRTIMER_NORESTART;
|
|
+}
|
|
+
|
|
+static irqreturn_t gtp_irq_handler(int irq, void *dev_id)
|
|
+{
|
|
+ struct goodix_ts_data *ts = dev_id;
|
|
+
|
|
+ gtp_work_func(ts);
|
|
+ return IRQ_HANDLED;
|
|
+}
|
|
+
|
|
+void gtp_int_output(struct goodix_ts_data *ts, int level)
|
|
+{
|
|
+ if (!ts->pdata->int_sync)
|
|
+ return;
|
|
+
|
|
+ if (level == 0) {
|
|
+ if (ts->pinctrl.pinctrl)
|
|
+ pinctrl_select_state(ts->pinctrl.pinctrl,
|
|
+ ts->pinctrl.int_out_low);
|
|
+ else if (gpio_is_valid(ts->pdata->irq_gpio))
|
|
+ gpio_direction_output(ts->pdata->irq_gpio, 0);
|
|
+ else
|
|
+ dev_err(&ts->client->dev,
|
|
+ "Failed set int pin output low\n");
|
|
+ } else {
|
|
+ if (ts->pinctrl.pinctrl)
|
|
+ pinctrl_select_state(ts->pinctrl.pinctrl,
|
|
+ ts->pinctrl.int_out_high);
|
|
+ else if (gpio_is_valid(ts->pdata->irq_gpio))
|
|
+ gpio_direction_output(ts->pdata->irq_gpio, 1);
|
|
+ else
|
|
+ dev_err(&ts->client->dev,
|
|
+ "Failed set int pin output high\n");
|
|
+ }
|
|
+}
|
|
+
|
|
+void gtp_int_sync(struct goodix_ts_data *ts, s32 ms)
|
|
+{
|
|
+ if (!ts->pdata->int_sync)
|
|
+ return;
|
|
+
|
|
+ if (ts->pinctrl.pinctrl) {
|
|
+ gtp_int_output(ts, 0);
|
|
+ msleep(ms);
|
|
+ pinctrl_select_state(ts->pinctrl.pinctrl,
|
|
+ ts->pinctrl.int_input);
|
|
+ } else if (gpio_is_valid(ts->pdata->irq_gpio)) {
|
|
+ gpio_direction_output(ts->pdata->irq_gpio, 0);
|
|
+ msleep(ms);
|
|
+ gpio_direction_input(ts->pdata->irq_gpio);
|
|
+ } else {
|
|
+ dev_err(&ts->client->dev, "Failed sync int pin\n");
|
|
+ }
|
|
+}
|
|
+
|
|
+/*******************************************************
|
|
+ * Function:
|
|
+ * Reset chip. Control the reset pin and int-pin(if
|
|
+ * defined),
|
|
+ * Input:
|
|
+ * client: i2c device.
|
|
+ * ms: reset time in millisecond
|
|
+ * Output:
|
|
+ * None.
|
|
+ *******************************************************/
|
|
+void gtp_reset_guitar(struct i2c_client *client, s32 ms)
|
|
+{
|
|
+ struct goodix_ts_data *ts = i2c_get_clientdata(client);
|
|
+
|
|
+ dev_info(&client->dev, "Guitar reset");
|
|
+ set_bit(PANEL_RESETTING, &ts->flags);
|
|
+ if (!gpio_is_valid(ts->pdata->rst_gpio)) {
|
|
+ dev_warn(&client->dev, "reset failed no valid reset gpio");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ gpio_direction_output(ts->pdata->rst_gpio, 0);
|
|
+ usleep_range(ms * 1000, ms * 1000 + 100); /* T2: > 10ms */
|
|
+
|
|
+ gtp_int_output(ts, client->addr == 0x14);
|
|
+
|
|
+ usleep_range(2000, 3000); /* T3: > 100us (2ms)*/
|
|
+ gpio_direction_output(ts->pdata->rst_gpio, 1);
|
|
+
|
|
+ usleep_range(6000, 7000); /* T4: > 5ms */
|
|
+ gpio_direction_input(ts->pdata->rst_gpio);
|
|
+
|
|
+ gtp_int_sync(ts, 50);
|
|
+ if (ts->pdata->esd_protect)
|
|
+ gtp_init_ext_watchdog(client);
|
|
+
|
|
+ clear_bit(PANEL_RESETTING, &ts->flags);
|
|
+}
|
|
+
|
|
+/*******************************************************
|
|
+ * Function:
|
|
+ * Enter doze mode for sliding wakeup.
|
|
+ * Input:
|
|
+ * ts: goodix tp private data
|
|
+ * Output:
|
|
+ * 1: succeed, otherwise failed
|
|
+ *******************************************************/
|
|
+static int gtp_enter_doze(struct goodix_ts_data *ts)
|
|
+{
|
|
+ int ret = -1;
|
|
+ int retry = 0;
|
|
+ u8 i2c_control_buf[3] = { (u8)(GTP_REG_COMMAND >> 8),
|
|
+ (u8)GTP_REG_COMMAND, 8 };
|
|
+
|
|
+ /* resend doze command
|
|
+ * if (test_and_set_bit(DOZE_MODE, &ts->flags)) {
|
|
+ * dev_info(&ts->client->dev, "Already in doze mode\n");
|
|
+ * return SUCCESS;
|
|
+ * }
|
|
+ */
|
|
+ set_bit(DOZE_MODE, &ts->flags);
|
|
+ dev_dbg(&ts->client->dev, "Entering gesture mode.");
|
|
+ while (retry++ < 5) {
|
|
+ i2c_control_buf[0] = (u8)(GTP_REG_COMMAND_CHECK >> 8);
|
|
+ i2c_control_buf[1] = (u8)GTP_REG_COMMAND_CHECK;
|
|
+ ret = gtp_i2c_write(ts->client, i2c_control_buf, 3);
|
|
+ if (ret < 0) {
|
|
+ dev_dbg(&ts->client->dev,
|
|
+ "failed to set doze flag into 0x8046, %d\n",
|
|
+ retry);
|
|
+ continue;
|
|
+ }
|
|
+ i2c_control_buf[0] = (u8)(GTP_REG_COMMAND >> 8);
|
|
+ i2c_control_buf[1] = (u8)GTP_REG_COMMAND;
|
|
+ ret = gtp_i2c_write(ts->client, i2c_control_buf, 3);
|
|
+ if (ret > 0) {
|
|
+ dev_dbg(&ts->client->dev, "Gesture mode enabled\n");
|
|
+ return ret;
|
|
+ }
|
|
+ usleep_range(10000, 11000);
|
|
+ }
|
|
+
|
|
+ dev_err(&ts->client->dev, "Failed enter doze mode\n");
|
|
+ clear_bit(DOZE_MODE, &ts->flags);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static s8 gtp_enter_sleep(struct goodix_ts_data *ts)
|
|
+{
|
|
+ s8 ret = -1;
|
|
+ s8 retry = 0;
|
|
+ u8 i2c_control_buf[3] = { (u8)(GTP_REG_COMMAND >> 8),
|
|
+ (u8)GTP_REG_COMMAND, 5 };
|
|
+
|
|
+ gtp_int_output(ts, 0);
|
|
+ usleep_range(5000, 6000);
|
|
+
|
|
+ while (retry++ < 5) {
|
|
+ ret = gtp_i2c_write(ts->client, i2c_control_buf, 3);
|
|
+ if (ret > 0) {
|
|
+ dev_info(&ts->client->dev, "Enter sleep mode\n");
|
|
+
|
|
+ return ret;
|
|
+ }
|
|
+ usleep_range(10000, 11000);
|
|
+ }
|
|
+ dev_err(&ts->client->dev, "Failed send sleep cmd\n");
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int gtp_wakeup_sleep(struct goodix_ts_data *ts)
|
|
+{
|
|
+ u8 retry = 0;
|
|
+ int ret = -1;
|
|
+
|
|
+ while (retry++ < 10) {
|
|
+ gtp_int_output(ts, 1);
|
|
+ usleep_range(5000, 6000);
|
|
+
|
|
+ ret = gtp_i2c_test(ts->client);
|
|
+ if (!ret) {
|
|
+ dev_dbg(&ts->client->dev, "Success wakeup sleep\n");
|
|
+
|
|
+ gtp_int_sync(ts, 25);
|
|
+ if (ts->pdata->esd_protect)
|
|
+ gtp_init_ext_watchdog(ts->client);
|
|
+
|
|
+ return ret;
|
|
+ }
|
|
+ gtp_reset_guitar(ts->client, 20);
|
|
+ }
|
|
+
|
|
+ dev_err(&ts->client->dev, "Failed wakeup from sleep mode\n");
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+static int gtp_find_valid_cfg_data(struct goodix_ts_data *ts)
|
|
+{
|
|
+ int ret = -1;
|
|
+ u8 sensor_id = 0;
|
|
+ struct goodix_config_data *cfg = &ts->pdata->config;
|
|
+
|
|
+ /* if defined CONFIG_OF, parse config data from dtsi
|
|
+ * else parse config data form header file.
|
|
+ */
|
|
+ cfg->length = 0;
|
|
+
|
|
+#ifndef CONFIG_OF
|
|
+ u8 cfg_info_group0[] = CTP_CFG_GROUP0;
|
|
+ u8 cfg_info_group1[] = CTP_CFG_GROUP1;
|
|
+ u8 cfg_info_group2[] = CTP_CFG_GROUP2;
|
|
+ u8 cfg_info_group3[] = CTP_CFG_GROUP3;
|
|
+ u8 cfg_info_group4[] = CTP_CFG_GROUP4;
|
|
+ u8 cfg_info_group5[] = CTP_CFG_GROUP5;
|
|
+
|
|
+ u8 *send_cfg_buf[] = { cfg_info_group0, cfg_info_group1,
|
|
+ cfg_info_group2, cfg_info_group3,
|
|
+ cfg_info_group4, cfg_info_group5 };
|
|
+ u8 cfg_info_len[] = { CFG_GROUP_LEN(cfg_info_group0),
|
|
+ CFG_GROUP_LEN(cfg_info_group1),
|
|
+ CFG_GROUP_LEN(cfg_info_group2),
|
|
+ CFG_GROUP_LEN(cfg_info_group3),
|
|
+ CFG_GROUP_LEN(cfg_info_group4),
|
|
+ CFG_GROUP_LEN(cfg_info_group5)};
|
|
+
|
|
+ dev_dbg(&ts->client->dev,
|
|
+ "Config Groups\' Lengths: %d, %d, %d, %d, %d, %d",
|
|
+ cfg_info_len[0], cfg_info_len[1], cfg_info_len[2],
|
|
+ cfg_info_len[3], cfg_info_len[4], cfg_info_len[5]);
|
|
+#endif
|
|
+
|
|
+ /* read sensor id */
|
|
+ ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_SENSOR_ID,
|
|
+ &sensor_id, 1);
|
|
+ if (SUCCESS != ret || sensor_id >= 0x06) {
|
|
+ dev_err(&ts->client->dev,
|
|
+ "Failed get valid sensor_id(0x%02X), No Config Sent\n",
|
|
+ sensor_id);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ dev_dbg(&ts->client->dev, "Sensor_ID: %d", sensor_id);
|
|
+ /* parse config data */
|
|
+#ifdef CONFIG_OF
|
|
+ dev_dbg(&ts->client->dev, "Get config data from device tree\n");
|
|
+ ret = gtp_parse_dt_cfg(&ts->client->dev,
|
|
+ &cfg->data[GTP_ADDR_LENGTH],
|
|
+ &cfg->length, sensor_id);
|
|
+ if (ret < 0) {
|
|
+ dev_err(&ts->client->dev,
|
|
+ "Failed to parse config data form device tree\n");
|
|
+ cfg->length = 0;
|
|
+ return -EPERM;
|
|
+ }
|
|
+#else
|
|
+ dev_dbg(&ts->client->dev, "Get config data from header file\n");
|
|
+ if ((!cfg_info_len[1]) && (!cfg_info_len[2]) &&
|
|
+ (!cfg_info_len[3]) && (!cfg_info_len[4]) &&
|
|
+ (!cfg_info_len[5])) {
|
|
+ sensor_id = 0;
|
|
+ }
|
|
+ cfg->length = cfg_info_len[sensor_id];
|
|
+ memset(&cfg->data[GTP_ADDR_LENGTH], 0, GTP_CONFIG_MAX_LENGTH);
|
|
+ memcpy(&cfg->data[GTP_ADDR_LENGTH], send_cfg_buf[sensor_id],
|
|
+ cfg->length);
|
|
+#endif
|
|
+
|
|
+ if (cfg->length < GTP_CONFIG_MIN_LENGTH) {
|
|
+ dev_err(&ts->client->dev,
|
|
+ "Failed get valid config data with sensor id %d\n",
|
|
+ sensor_id);
|
|
+ cfg->length = 0;
|
|
+ return -EPERM;
|
|
+ }
|
|
+
|
|
+ dev_info(&ts->client->dev, "Config group%d used,length: %d\n",
|
|
+ sensor_id, cfg->length);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*******************************************************
|
|
+ * Function:
|
|
+ * Get valid config data from dts or .h file.
|
|
+ * Read firmware version info and judge firmware
|
|
+ * working state
|
|
+ * Input:
|
|
+ * ts: goodix private data
|
|
+ * Output:
|
|
+ * Executive outcomes.
|
|
+ * 0: succeed, otherwise: failed
|
|
+ *******************************************************/
|
|
+static s32 gtp_init_panel(struct goodix_ts_data *ts)
|
|
+{
|
|
+ s32 ret = -1;
|
|
+ u8 opr_buf[16] = {0};
|
|
+ u8 drv_cfg_version = 0;
|
|
+ u8 flash_cfg_version = 0;
|
|
+ struct goodix_config_data *cfg = &ts->pdata->config;
|
|
+
|
|
+ if (!ts->pdata->driver_send_cfg) {
|
|
+ dev_info(&ts->client->dev, "Driver set not send config\n");
|
|
+ cfg->length = GTP_CONFIG_MAX_LENGTH;
|
|
+ ret = gtp_i2c_read(ts->client,
|
|
+ cfg->data, cfg->length +
|
|
+ GTP_ADDR_LENGTH);
|
|
+ if (ret < 0)
|
|
+ dev_err(&ts->client->dev, "Read origin Config Failed\n");
|
|
+
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ gtp_find_valid_cfg_data(ts);
|
|
+
|
|
+ /* check firmware */
|
|
+ ret = gtp_i2c_read_dbl_check(ts->client, 0x41E4, opr_buf, 1);
|
|
+ if (SUCCESS == ret) {
|
|
+ if (opr_buf[0] != 0xBE) {
|
|
+ set_bit(FW_ERROR, &ts->flags);
|
|
+ dev_err(&ts->client->dev,
|
|
+ "Firmware error, no config sent!\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_CONFIG_DATA,
|
|
+ &opr_buf[0], 1);
|
|
+ if (ret == SUCCESS) {
|
|
+ dev_dbg(&ts->client->dev,
|
|
+ "Config Version: %d; IC Config Version: %d\n",
|
|
+ cfg->data[GTP_ADDR_LENGTH], opr_buf[0]);
|
|
+ flash_cfg_version = opr_buf[0];
|
|
+ drv_cfg_version = cfg->data[GTP_ADDR_LENGTH];
|
|
+
|
|
+ if (flash_cfg_version < 90 &&
|
|
+ flash_cfg_version > drv_cfg_version)
|
|
+ cfg->data[GTP_ADDR_LENGTH] = 0x00;
|
|
+ } else {
|
|
+ dev_err(&ts->client->dev,
|
|
+ "Failed to get ic config version!No config sent\n");
|
|
+ return -EPERM;
|
|
+ }
|
|
+
|
|
+ ret = gtp_send_cfg(ts->client);
|
|
+ if (ret < 0)
|
|
+ dev_err(&ts->client->dev, "Send config error\n");
|
|
+ else
|
|
+ usleep_range(10000, 11000); /* 10 ms */
|
|
+
|
|
+ /* restore config version */
|
|
+ cfg->data[GTP_ADDR_LENGTH] = drv_cfg_version;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static ssize_t gtp_config_read_proc(struct file *file, char __user *page,
|
|
+ size_t size, loff_t *ppos)
|
|
+{
|
|
+ int i, ret;
|
|
+ char *ptr;
|
|
+ size_t data_len = 0;
|
|
+ char temp_data[GTP_CONFIG_MAX_LENGTH + 2] = {
|
|
+ (u8)(GTP_REG_CONFIG_DATA >> 8),
|
|
+ (u8)GTP_REG_CONFIG_DATA };
|
|
+ struct goodix_ts_data *ts = i2c_get_clientdata(i2c_connect_client);
|
|
+ struct goodix_config_data *cfg = &ts->pdata->config;
|
|
+
|
|
+ ptr = kzalloc(4096, GFP_KERNEL);
|
|
+ if (!ptr) {
|
|
+ dev_err(&ts->client->dev, "Failed alloc memory for config\n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ data_len += snprintf(ptr + data_len, 4096 - data_len,
|
|
+ "====init value====\n");
|
|
+ for (i = 0 ; i < GTP_CONFIG_MAX_LENGTH ; i++) {
|
|
+ data_len += snprintf(ptr + data_len, 4096 - data_len,
|
|
+ "0x%02X ", cfg->data[i + 2]);
|
|
+
|
|
+ if (i % 8 == 7)
|
|
+ data_len += snprintf(ptr + data_len,
|
|
+ 4096 - data_len, "\n");
|
|
+ }
|
|
+ data_len += snprintf(ptr + data_len, 4096 - data_len, "\n");
|
|
+
|
|
+ data_len += snprintf(ptr + data_len, 4096 - data_len,
|
|
+ "====real value====\n");
|
|
+ ret = gtp_i2c_read(i2c_connect_client, temp_data,
|
|
+ GTP_CONFIG_MAX_LENGTH + 2);
|
|
+ if (ret < 0) {
|
|
+ data_len += snprintf(ptr + data_len, 4096 - data_len,
|
|
+ "Failed read real config data\n");
|
|
+ } else {
|
|
+ for (i = 0; i < GTP_CONFIG_MAX_LENGTH; i++) {
|
|
+ data_len += snprintf(ptr + data_len, 4096 - data_len,
|
|
+ "0x%02X ", temp_data[i + 2]);
|
|
+
|
|
+ if (i % 8 == 7)
|
|
+ data_len += snprintf(ptr + data_len,
|
|
+ 4096 - data_len, "\n");
|
|
+ }
|
|
+ }
|
|
+
|
|
+ data_len = simple_read_from_buffer(page, size, ppos, ptr, data_len);
|
|
+ kfree(ptr);
|
|
+ ptr = NULL;
|
|
+ return data_len;
|
|
+}
|
|
+
|
|
+int gtp_ascii_to_array(const u8 *src_buf, int src_len, u8 *dst_buf)
|
|
+{
|
|
+ int i, ret;
|
|
+ int cfg_len = 0;
|
|
+ long val;
|
|
+ char temp_buf[5];
|
|
+
|
|
+ for (i = 0; i < src_len;) {
|
|
+ if (src_buf[i] == ' ' || src_buf[i] == '\r' ||
|
|
+ src_buf[i] == '\n') {
|
|
+ i++;
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ temp_buf[0] = src_buf[i];
|
|
+ temp_buf[1] = src_buf[i + 1];
|
|
+ temp_buf[2] = src_buf[i + 2];
|
|
+ temp_buf[3] = src_buf[i + 3];
|
|
+ temp_buf[4] = '\0';
|
|
+ if (!kstrtol(temp_buf, 16, &val)) {
|
|
+ if (cfg_len < GTP_CONFIG_MAX_LENGTH) {
|
|
+ dst_buf[cfg_len++] = val & 0xFF;
|
|
+ i += 5;
|
|
+ } else {
|
|
+ ret = -2;
|
|
+ goto convert_failed;
|
|
+ }
|
|
+ } else {
|
|
+ ret = -3;
|
|
+ goto convert_failed;
|
|
+ }
|
|
+ }
|
|
+ return cfg_len;
|
|
+
|
|
+convert_failed:
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static ssize_t gtp_config_write_proc(struct file *filp,
|
|
+ const char __user *buffer,
|
|
+ size_t count, loff_t *off)
|
|
+{
|
|
+ u8 *temp_buf;
|
|
+ u8 *file_config;
|
|
+ int file_cfg_len;
|
|
+ s32 ret = 0;
|
|
+ struct goodix_ts_data *ts = i2c_get_clientdata(i2c_connect_client);
|
|
+
|
|
+ dev_dbg(&ts->client->dev, "write count %zu\n", count);
|
|
+
|
|
+ if (count > PAGE_SIZE) {
|
|
+ dev_err(&ts->client->dev, "config to long %zu\n", count);
|
|
+ return -EFAULT;
|
|
+ }
|
|
+
|
|
+ temp_buf = kzalloc(count, GFP_KERNEL);
|
|
+ if (!temp_buf) {
|
|
+ dev_err(&ts->client->dev, "failed alloc temp memory");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ file_config = kzalloc(GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH,
|
|
+ GFP_KERNEL);
|
|
+ if (!file_config) {
|
|
+ dev_err(&ts->client->dev, "failed alloc config memory");
|
|
+ kfree(temp_buf);
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+ file_config[0] = GTP_REG_CONFIG_DATA >> 8;
|
|
+ file_config[1] = GTP_REG_CONFIG_DATA & 0xff;
|
|
+
|
|
+ if (copy_from_user(temp_buf, buffer, count)) {
|
|
+ dev_err(&ts->client->dev, "Failed copy from user\n");
|
|
+ ret = -EFAULT;
|
|
+ goto send_cfg_err;
|
|
+ }
|
|
+
|
|
+ file_cfg_len = gtp_ascii_to_array(temp_buf, (int)count,
|
|
+ &file_config[GTP_ADDR_LENGTH]);
|
|
+ if (file_cfg_len < 0) {
|
|
+ dev_err(&ts->client->dev, "failed covert ascii to hex");
|
|
+ ret = -EFAULT;
|
|
+ goto send_cfg_err;
|
|
+ }
|
|
+
|
|
+ GTP_DEBUG_ARRAY(file_config + GTP_ADDR_LENGTH, file_cfg_len);
|
|
+
|
|
+ ret = gtp_i2c_write(ts->client, file_config, file_cfg_len + 2);
|
|
+ if (ret > 0) {
|
|
+ dev_info(&ts->client->dev, "Send config SUCCESS.");
|
|
+ ret = count;
|
|
+ } else {
|
|
+ dev_err(&ts->client->dev, "Send config i2c error.");
|
|
+ ret = -EFAULT;
|
|
+ }
|
|
+
|
|
+send_cfg_err:
|
|
+ kfree(temp_buf);
|
|
+ kfree(file_config);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 6, 0))
|
|
+static const struct proc_ops config_proc_ops = {
|
|
+ .proc_read = gtp_config_read_proc,
|
|
+ .proc_write = gtp_config_write_proc,
|
|
+};
|
|
+#else
|
|
+static const struct file_operations config_proc_ops = {
|
|
+ .read = gtp_config_read_proc,
|
|
+ .write = gtp_config_write_proc,
|
|
+};
|
|
+#endif
|
|
+
|
|
+static ssize_t gtp_workmode_show(struct device *dev,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ size_t data_len = 0;
|
|
+ struct goodix_ts_data *data = dev_get_drvdata(dev);
|
|
+
|
|
+ if (test_bit(DOZE_MODE, &data->flags))
|
|
+ data_len = scnprintf(buf, PAGE_SIZE, "%s\n",
|
|
+ "doze_mode");
|
|
+ else if (test_bit(SLEEP_MODE, &data->flags))
|
|
+ data_len = scnprintf(buf, PAGE_SIZE, "%s\n",
|
|
+ "sleep_mode");
|
|
+ else
|
|
+ data_len = scnprintf(buf, PAGE_SIZE, "%s\n",
|
|
+ "normal_mode");
|
|
+
|
|
+ return data_len;
|
|
+}
|
|
+static DEVICE_ATTR(workmode, 0444, gtp_workmode_show, NULL);
|
|
+
|
|
+#define FW_NAME_MAX_LEN 80
|
|
+static ssize_t gtp_dofwupdate_store(struct device *dev,
|
|
+ struct device_attribute *attr,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ struct goodix_ts_data *ts = dev_get_drvdata(dev);
|
|
+ char update_file_name[FW_NAME_MAX_LEN];
|
|
+ int retval;
|
|
+
|
|
+ if (count > FW_NAME_MAX_LEN) {
|
|
+ dev_info(&ts->client->dev, "FW filename is too long\n");
|
|
+ retval = -EINVAL;
|
|
+ goto exit;
|
|
+ }
|
|
+
|
|
+ strlcpy(update_file_name, buf, count);
|
|
+
|
|
+ ts->force_update = true;
|
|
+ retval = gup_update_proc(update_file_name);
|
|
+ if (retval == FAIL)
|
|
+ dev_err(&ts->client->dev, "Fail to update GTP firmware.\n");
|
|
+ else
|
|
+ dev_info(&ts->client->dev, "Update success\n");
|
|
+
|
|
+ return count;
|
|
+
|
|
+exit:
|
|
+ return retval;
|
|
+}
|
|
+static DEVICE_ATTR(dofwupdate, 0664, NULL, gtp_dofwupdate_store);
|
|
+
|
|
+static ssize_t gtp_productinfo_show(struct device *dev,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct goodix_ts_data *data = dev_get_drvdata(dev);
|
|
+ struct goodix_fw_info *fw_info = &data->fw_info;
|
|
+
|
|
+ return scnprintf(buf, PAGE_SIZE, "GT%s_%x_%d\n",
|
|
+ fw_info->pid, fw_info->version, fw_info->sensor_id);
|
|
+}
|
|
+static DEVICE_ATTR(productinfo, 0444, gtp_productinfo_show, NULL);
|
|
+
|
|
+static ssize_t gtp_drv_irq_store(struct device *dev,
|
|
+ struct device_attribute *attr,
|
|
+ const char *buf, size_t count)
|
|
+{
|
|
+ unsigned long value = 0;
|
|
+ int err = 0;
|
|
+ struct goodix_ts_data *data = dev_get_drvdata(dev);
|
|
+
|
|
+ err = kstrtoul(buf, 10, &value);
|
|
+ if (err < 0) {
|
|
+ dev_err(dev, "Failed to convert value\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ switch (value) {
|
|
+ case 0:
|
|
+ /* Disable irq */
|
|
+ gtp_work_control_enable(data, false);
|
|
+ break;
|
|
+ case 1:
|
|
+ /* Enable irq */
|
|
+ gtp_work_control_enable(data, true);
|
|
+ break;
|
|
+ default:
|
|
+ dev_err(dev, "Invalid value\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ return count;
|
|
+}
|
|
+
|
|
+static ssize_t gtp_drv_irq_show(struct device *dev,
|
|
+ struct device_attribute *attr, char *buf)
|
|
+{
|
|
+ struct goodix_ts_data *data = dev_get_drvdata(dev);
|
|
+
|
|
+ return scnprintf(buf, PAGE_SIZE, "%s\n",
|
|
+ test_bit(WORK_THREAD_ENABLED, &data->flags)
|
|
+ ? "enabled" : "disabled");
|
|
+}
|
|
+static DEVICE_ATTR(drv_irq, 0664, gtp_drv_irq_show, gtp_drv_irq_store);
|
|
+
|
|
+static ssize_t gtp_reset_store(struct device *dev,
|
|
+ struct device_attribute *attr, const char *buf, size_t count)
|
|
+{
|
|
+ struct goodix_ts_data *data = dev_get_drvdata(dev);
|
|
+
|
|
+ if ('1' != buf[0]) {
|
|
+ dev_err(dev, "Invalid argument for reset\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ gtp_reset_guitar(data->client, 20);
|
|
+
|
|
+ return count;
|
|
+}
|
|
+static DEVICE_ATTR(reset, 0220, NULL, gtp_reset_store);
|
|
+
|
|
+static struct attribute *gtp_attrs[] = {
|
|
+ &dev_attr_workmode.attr,
|
|
+ &dev_attr_productinfo.attr,
|
|
+ &dev_attr_dofwupdate.attr,
|
|
+ &dev_attr_drv_irq.attr,
|
|
+ &dev_attr_reset.attr,
|
|
+ NULL
|
|
+};
|
|
+
|
|
+static const struct attribute_group gtp_attr_group = {
|
|
+ .attrs = gtp_attrs,
|
|
+};
|
|
+
|
|
+static int gtp_create_file(struct goodix_ts_data *ts)
|
|
+{
|
|
+ int ret;
|
|
+ struct i2c_client *client = ts->client;
|
|
+
|
|
+ /* Create proc file system */
|
|
+ gtp_config_proc = NULL;
|
|
+ gtp_config_proc = proc_create(GT91XX_CONFIG_PROC_FILE, 0664,
|
|
+ NULL, &config_proc_ops);
|
|
+ if (!gtp_config_proc)
|
|
+ dev_err(&client->dev, "create_proc_entry %s failed\n",
|
|
+ GT91XX_CONFIG_PROC_FILE);
|
|
+ else
|
|
+ dev_info(&client->dev, "create proc entry %s success\n",
|
|
+ GT91XX_CONFIG_PROC_FILE);
|
|
+
|
|
+ ret = sysfs_create_group(&client->dev.kobj, >p_attr_group);
|
|
+ if (ret) {
|
|
+ dev_err(&client->dev, "Failure create sysfs group %d\n", ret);
|
|
+ /*TODO: debug change */
|
|
+ goto exit_free_config_proc;
|
|
+ }
|
|
+ return 0;
|
|
+
|
|
+exit_free_config_proc:
|
|
+ remove_proc_entry(GT91XX_CONFIG_PROC_FILE, gtp_config_proc);
|
|
+ return -ENODEV;
|
|
+}
|
|
+
|
|
+s32 gtp_get_fw_info(struct i2c_client *client, struct goodix_fw_info *fw_info)
|
|
+{
|
|
+ s32 ret = -1;
|
|
+ u8 buf[8] = {GTP_REG_VERSION >> 8, GTP_REG_VERSION & 0xff};
|
|
+
|
|
+ ret = gtp_i2c_read(client, buf, sizeof(buf));
|
|
+ if (ret < 0) {
|
|
+ dev_err(&client->dev, "Failed read fw_info\n");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ /* product id */
|
|
+ memset(fw_info, 0, sizeof(*fw_info));
|
|
+
|
|
+ if (buf[5] == 0x00) {
|
|
+ memcpy(fw_info->pid, buf + GTP_ADDR_LENGTH, 3);
|
|
+ dev_info(&client->dev, "IC Version: %c%c%c_%02X%02X\n",
|
|
+ buf[2], buf[3], buf[4], buf[7], buf[6]);
|
|
+ } else {
|
|
+ memcpy(fw_info->pid, buf + GTP_ADDR_LENGTH, 4);
|
|
+ dev_info(&client->dev, "IC Version: %c%c%c%c_%02X%02X\n",
|
|
+ buf[2], buf[3], buf[4], buf[5], buf[7], buf[6]);
|
|
+ }
|
|
+
|
|
+ /* current firmware version */
|
|
+ fw_info->version = (buf[7] << 8) | buf[6];
|
|
+
|
|
+ /* read sensor id */
|
|
+ fw_info->sensor_id = 0xff;
|
|
+ ret = gtp_i2c_read_dbl_check(client, GTP_REG_SENSOR_ID,
|
|
+ &fw_info->sensor_id, 1);
|
|
+ if (SUCCESS != ret || fw_info->sensor_id >= 0x06) {
|
|
+ dev_err(&client->dev,
|
|
+ "Failed get valid sensor_id(0x%02X), No Config Sent\n",
|
|
+ fw_info->sensor_id);
|
|
+
|
|
+ fw_info->sensor_id = 0xff;
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int gtp_i2c_test(struct i2c_client *client)
|
|
+{
|
|
+ u8 test[3] = {GTP_REG_CONFIG_DATA >> 8, GTP_REG_CONFIG_DATA & 0xff};
|
|
+ u8 retry = 0;
|
|
+ int ret = -1;
|
|
+
|
|
+ while (retry++ < 3) {
|
|
+ ret = gtp_i2c_read(client, test, 3);
|
|
+ if (ret == 2)
|
|
+ return 0;
|
|
+
|
|
+ dev_err(&client->dev, "GTP i2c test failed time %d\n", retry);
|
|
+ usleep_range(10000, 11000); /* 10 ms */
|
|
+ }
|
|
+
|
|
+ return -EAGAIN;
|
|
+}
|
|
+
|
|
+static int gtp_pinctrl_init(struct goodix_ts_data *ts)
|
|
+{
|
|
+ struct goodix_pinctrl *pinctrl = &ts->pinctrl;
|
|
+
|
|
+ pinctrl->pinctrl = devm_pinctrl_get(&ts->client->dev);
|
|
+ if (IS_ERR_OR_NULL(pinctrl->pinctrl)) {
|
|
+ dev_info(&ts->client->dev, "No pinctrl found\n");
|
|
+ pinctrl->pinctrl = NULL;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ pinctrl->default_sta = pinctrl_lookup_state(pinctrl->pinctrl,
|
|
+ "default");
|
|
+ if (IS_ERR_OR_NULL(pinctrl->default_sta)) {
|
|
+ dev_info(&ts->client->dev,
|
|
+ "Failed get pinctrl state:default state\n");
|
|
+ goto exit_pinctrl_init;
|
|
+ }
|
|
+
|
|
+ pinctrl->int_out_high = pinctrl_lookup_state(pinctrl->pinctrl,
|
|
+ "int-output-high");
|
|
+ if (IS_ERR_OR_NULL(pinctrl->int_out_high)) {
|
|
+ dev_info(&ts->client->dev,
|
|
+ "Failed get pinctrl state:output_high\n");
|
|
+ goto exit_pinctrl_init;
|
|
+ }
|
|
+
|
|
+ pinctrl->int_out_low = pinctrl_lookup_state(pinctrl->pinctrl,
|
|
+ "int-output-low");
|
|
+ if (IS_ERR_OR_NULL(pinctrl->int_out_low)) {
|
|
+ dev_info(&ts->client->dev,
|
|
+ "Failed get pinctrl state:output_low\n");
|
|
+ goto exit_pinctrl_init;
|
|
+ }
|
|
+
|
|
+ pinctrl->int_input = pinctrl_lookup_state(pinctrl->pinctrl,
|
|
+ "int-input");
|
|
+ if (IS_ERR_OR_NULL(pinctrl->int_input)) {
|
|
+ dev_info(&ts->client->dev,
|
|
+ "Failed get pinctrl state:int-input\n");
|
|
+ goto exit_pinctrl_init;
|
|
+ }
|
|
+ dev_info(&ts->client->dev, "Success init pinctrl\n");
|
|
+ return 0;
|
|
+exit_pinctrl_init:
|
|
+ devm_pinctrl_put(pinctrl->pinctrl);
|
|
+ pinctrl->pinctrl = NULL;
|
|
+ pinctrl->int_out_high = NULL;
|
|
+ pinctrl->int_out_low = NULL;
|
|
+ pinctrl->int_input = NULL;
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+static void gtp_pinctrl_deinit(struct goodix_ts_data *ts)
|
|
+{
|
|
+ if (ts->pinctrl.pinctrl)
|
|
+ devm_pinctrl_put(ts->pinctrl.pinctrl);
|
|
+}
|
|
+
|
|
+static int gtp_request_io_port(struct goodix_ts_data *ts)
|
|
+{
|
|
+ int ret = 0;
|
|
+
|
|
+ if (gpio_is_valid(ts->pdata->irq_gpio)) {
|
|
+ ret = gpio_request(ts->pdata->irq_gpio, "goodix_ts_int");
|
|
+ if (ret < 0) {
|
|
+ dev_err(&ts->client->dev,
|
|
+ "Failed to request GPIO:%d, ERRNO:%d\n",
|
|
+ (s32)ts->pdata->irq_gpio, ret);
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+ gpio_direction_input(ts->pdata->irq_gpio);
|
|
+ dev_info(&ts->client->dev, "Success request irq-gpio\n");
|
|
+ }
|
|
+
|
|
+ if (gpio_is_valid(ts->pdata->rst_gpio)) {
|
|
+ ret = gpio_request(ts->pdata->rst_gpio, "goodix_ts_rst");
|
|
+ if (ret < 0) {
|
|
+ dev_err(&ts->client->dev,
|
|
+ "Failed to request GPIO:%d, ERRNO:%d\n",
|
|
+ (s32)ts->pdata->rst_gpio, ret);
|
|
+
|
|
+ if (gpio_is_valid(ts->pdata->irq_gpio))
|
|
+ gpio_free(ts->pdata->irq_gpio);
|
|
+
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+ gpio_direction_input(ts->pdata->rst_gpio);
|
|
+ dev_info(&ts->client->dev, "Success request rst-gpio\n");
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*******************************************************
|
|
+ * Function:
|
|
+ * Request interrupt if define irq pin, else use hrtimer
|
|
+ * as interrupt source
|
|
+ * Input:
|
|
+ * ts: private data.
|
|
+ * Output:
|
|
+ * Executive outcomes.
|
|
+ * 0: succeed, -1: failed.
|
|
+ *******************************************************/
|
|
+static int gtp_request_irq(struct goodix_ts_data *ts)
|
|
+{
|
|
+ int ret = -1;
|
|
+
|
|
+ /* use irq */
|
|
+ if (gpio_is_valid(ts->pdata->irq_gpio) || ts->client->irq > 0) {
|
|
+ if (gpio_is_valid(ts->pdata->irq_gpio))
|
|
+ ts->client->irq = gpio_to_irq(ts->pdata->irq_gpio);
|
|
+
|
|
+ dev_info(&ts->client->dev, "INT num %d, trigger type:%d\n",
|
|
+ ts->client->irq, ts->pdata->irq_flags);
|
|
+ ret = request_threaded_irq(ts->client->irq, NULL,
|
|
+ gtp_irq_handler,
|
|
+ ts->pdata->irq_flags | IRQF_ONESHOT,
|
|
+ ts->client->name,
|
|
+ ts);
|
|
+ if (ret < 0) {
|
|
+ dev_err(&ts->client->dev,
|
|
+ "Failed to request irq %d\n", ts->client->irq);
|
|
+ return ret;
|
|
+ }
|
|
+ } else { /* use hrtimer */
|
|
+ dev_info(&ts->client->dev, "No hardware irq, use hrtimer\n");
|
|
+ hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
|
|
+ ts->timer.function = gtp_timer_handler;
|
|
+ hrtimer_start(&ts->timer,
|
|
+ ktime_set(0, (GTP_POLL_TIME + 6) * 1000000),
|
|
+ HRTIMER_MODE_REL);
|
|
+ set_bit(HRTIMER_USED, &ts->flags);
|
|
+ ret = 0;
|
|
+ }
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static s8 gtp_request_input_dev(struct goodix_ts_data *ts)
|
|
+{
|
|
+ s8 ret = -1;
|
|
+ u8 index = 0;
|
|
+
|
|
+ ts->input_dev = input_allocate_device();
|
|
+ if (!ts->input_dev) {
|
|
+ dev_err(&ts->client->dev, "Failed to allocate input device\n");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ ts->input_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY)
|
|
+ | BIT_MASK(EV_ABS);
|
|
+ if (!ts->pdata->type_a_report) {
|
|
+ input_mt_init_slots(ts->input_dev, 16, INPUT_MT_DIRECT);
|
|
+ dev_info(&ts->client->dev, "Use slot report protocol\n");
|
|
+ } else {
|
|
+ __set_bit(INPUT_PROP_DIRECT, ts->input_dev->propbit);
|
|
+ __set_bit(BTN_TOUCH, ts->input_dev->keybit);
|
|
+ dev_info(&ts->client->dev, "Use type A report protocol\n");
|
|
+ }
|
|
+
|
|
+ input_set_capability(ts->input_dev, EV_KEY, GTP_PEN_BUTTON1);
|
|
+ input_set_capability(ts->input_dev, EV_KEY, GTP_PEN_BUTTON2);
|
|
+
|
|
+ /* touch key register */
|
|
+ for (index = 0; index < ts->pdata->key_nums; index++)
|
|
+ input_set_capability(ts->input_dev, EV_KEY,
|
|
+ ts->pdata->key_map[index]);
|
|
+
|
|
+ if (ts->pdata->slide_wakeup)
|
|
+ input_set_capability(ts->input_dev, EV_KEY, KEY_POWER);
|
|
+
|
|
+ if (ts->pdata->swap_x2y)
|
|
+ GTP_SWAP(ts->pdata->abs_size_x, ts->pdata->abs_size_y);
|
|
+
|
|
+ input_set_abs_params(ts->input_dev, ABS_MT_POSITION_X, 0,
|
|
+ ts->pdata->abs_size_x, 0, 0);
|
|
+ input_set_abs_params(ts->input_dev, ABS_MT_POSITION_Y, 0,
|
|
+ ts->pdata->abs_size_y, 0, 0);
|
|
+ input_set_abs_params(ts->input_dev, ABS_MT_TOUCH_MAJOR, 0,
|
|
+ ts->pdata->max_touch_width, 0, 0);
|
|
+ input_set_abs_params(ts->input_dev, ABS_MT_PRESSURE, 0,
|
|
+ ts->pdata->max_touch_pressure, 0, 0);
|
|
+ input_set_abs_params(ts->input_dev, ABS_MT_TRACKING_ID, 0,
|
|
+ ts->pdata->max_touch_id, 0, 0);
|
|
+ if (!ts->pdata->type_a_report) {
|
|
+ input_set_abs_params(ts->input_dev, ABS_MT_TOOL_TYPE,
|
|
+ 0, MT_TOOL_MAX, 0, 0);
|
|
+ } else {
|
|
+ __set_bit(BTN_TOOL_PEN, ts->input_dev->keybit);
|
|
+ __set_bit(BTN_TOOL_FINGER, ts->input_dev->keybit);
|
|
+ }
|
|
+
|
|
+ ts->input_dev->name = goodix_ts_name;
|
|
+ ts->input_dev->phys = goodix_input_phys;
|
|
+ ts->input_dev->id.bustype = BUS_I2C;
|
|
+ ts->input_dev->id.vendor = 0xDEAD;
|
|
+ ts->input_dev->id.product = 0xBEEF;
|
|
+ ts->input_dev->id.version = 10427;
|
|
+
|
|
+ ret = input_register_device(ts->input_dev);
|
|
+ if (ret) {
|
|
+ dev_err(&ts->client->dev, "Register %s input device failed\n",
|
|
+ ts->input_dev->name);
|
|
+ input_free_device(ts->input_dev);
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Devices Tree support
|
|
+ */
|
|
+#ifdef CONFIG_OF
|
|
+static void gtp_parse_dt_coords(struct device *dev,
|
|
+ struct goodix_ts_platform_data *pdata)
|
|
+{
|
|
+ struct device_node *np = dev->of_node;
|
|
+ int ret;
|
|
+
|
|
+ ret = of_property_read_u32(np, "touchscreen-max-id",
|
|
+ &pdata->max_touch_id);
|
|
+ if (ret || pdata->max_touch_id > GTP_MAX_TOUCH_ID) {
|
|
+ dev_info(dev, "Unset touchscreen-max-id, use default\n");
|
|
+ pdata->max_touch_id = GTP_MAX_TOUCH_ID;
|
|
+ }
|
|
+
|
|
+ ret = of_property_read_u32(np, "touchscreen-size-x",
|
|
+ &pdata->abs_size_x);
|
|
+ if (ret) {
|
|
+ dev_info(dev, "Unset touchscreen-size-x, use default\n");
|
|
+ pdata->abs_size_x = GTP_DEFAULT_MAX_X;
|
|
+ }
|
|
+
|
|
+ ret = of_property_read_u32(np, "touchscreen-size-y",
|
|
+ &pdata->abs_size_y);
|
|
+ if (ret) {
|
|
+ dev_info(dev, "Unset touchscreen-size-y, use default\n");
|
|
+ pdata->abs_size_y = GTP_DEFAULT_MAX_Y;
|
|
+ }
|
|
+
|
|
+ ret = of_property_read_u32(np, "touchscreen-max-w",
|
|
+ &pdata->max_touch_width);
|
|
+ if (ret) {
|
|
+ dev_info(dev, "Unset touchscreen-max-w, use default\n");
|
|
+ pdata->max_touch_width = GTP_DEFAULT_MAX_WIDTH;
|
|
+ }
|
|
+
|
|
+ ret = of_property_read_u32(np, "touchscreen-max-p",
|
|
+ &pdata->max_touch_pressure);
|
|
+ if (ret) {
|
|
+ dev_info(dev, "Unset touchscreen-max-p, use default\n");
|
|
+ pdata->max_touch_pressure = GTP_DEFAULT_MAX_PRESSURE;
|
|
+ }
|
|
+ dev_info(dev, "touch input parameters is [id x y w p]<%d %d %d %d %d>\n",
|
|
+ pdata->max_touch_id, pdata->abs_size_x, pdata->abs_size_y,
|
|
+ pdata->max_touch_width, pdata->max_touch_pressure);
|
|
+}
|
|
+
|
|
+static int gtp_parse_dt(struct device *dev,
|
|
+ struct goodix_ts_platform_data *pdata)
|
|
+{
|
|
+ int ret;
|
|
+ u32 key_nums;
|
|
+ struct property *prop;
|
|
+ u32 key_map[MAX_KEY_NUMS];
|
|
+ struct device_node *np = dev->of_node;
|
|
+
|
|
+ gtp_parse_dt_coords(dev, pdata);
|
|
+
|
|
+ ret = of_property_read_u32(np, "irq-flags",
|
|
+ &pdata->irq_flags);
|
|
+ if (ret) {
|
|
+ dev_info(dev,
|
|
+ "Failed get int-trigger-type from dts,set default\n");
|
|
+ pdata->irq_flags = GTP_DEFAULT_INT_TRIGGER;
|
|
+ }
|
|
+ of_property_read_u32(np, "goodix,int-sync", &pdata->int_sync);
|
|
+ if (pdata->int_sync)
|
|
+ dev_info(dev, "int-sync enabled\n");
|
|
+
|
|
+ of_property_read_u32(np, "goodix,driver-send-cfg",
|
|
+ &pdata->driver_send_cfg);
|
|
+ if (pdata->driver_send_cfg)
|
|
+ dev_info(dev, "driver-send-cfg enabled\n");
|
|
+
|
|
+ of_property_read_u32(np, "goodix,swap-x2y", &pdata->swap_x2y);
|
|
+ if (pdata->swap_x2y)
|
|
+ dev_info(dev, "swap-x2y enabled\n");
|
|
+
|
|
+ of_property_read_u32(np, "goodix,slide-wakeup", &pdata->slide_wakeup);
|
|
+ if (pdata->slide_wakeup)
|
|
+ dev_info(dev, "slide-wakeup enabled\n");
|
|
+
|
|
+ of_property_read_u32(np, "goodix,auto-update", &pdata->auto_update);
|
|
+ if (pdata->auto_update)
|
|
+ dev_info(dev, "auto-update enabled\n");
|
|
+
|
|
+ of_property_read_u32(np, "goodix,auto-update-cfg",
|
|
+ &pdata->auto_update_cfg);
|
|
+ if (pdata->auto_update_cfg)
|
|
+ dev_info(dev, "auto-update-cfg enabled\n");
|
|
+
|
|
+ of_property_read_u32(np, "goodix,esd-protect", &pdata->esd_protect);
|
|
+ if (pdata->esd_protect)
|
|
+ dev_info(dev, "esd-protect enabled\n");
|
|
+
|
|
+ of_property_read_u32(np, "goodix,type-a-report",
|
|
+ &pdata->type_a_report);
|
|
+ if (pdata->type_a_report)
|
|
+ dev_info(dev, "type-a-report enabled\n");
|
|
+
|
|
+ of_property_read_u32(np, "goodix,resume-in-workqueue",
|
|
+ &pdata->resume_in_workqueue);
|
|
+ if (pdata->resume_in_workqueue)
|
|
+ dev_info(dev, "resume-in-workqueue enabled\n");
|
|
+
|
|
+ of_property_read_u32(np, "goodix,power-off-sleep",
|
|
+ &pdata->power_off_sleep);
|
|
+ if (pdata->power_off_sleep)
|
|
+ dev_info(dev, "power-off-sleep enabled\n");
|
|
+
|
|
+ of_property_read_u32(np, "goodix,pen-suppress-finger",
|
|
+ &pdata->pen_suppress_finger);
|
|
+ if (pdata->pen_suppress_finger)
|
|
+ dev_info(dev, "pen-suppress-finger enabled\n");
|
|
+
|
|
+ prop = of_find_property(np, "touchscreen-key-map", NULL);
|
|
+ if (prop) {
|
|
+ key_nums = prop->length / sizeof(key_map[0]);
|
|
+ key_nums = key_nums > MAX_KEY_NUMS ? MAX_KEY_NUMS : key_nums;
|
|
+
|
|
+ dev_dbg(dev, "key nums %d\n", key_nums);
|
|
+ ret = of_property_read_u32_array(np,
|
|
+ "touchscreen-key-map", key_map,
|
|
+ key_nums);
|
|
+ if (ret) {
|
|
+ dev_err(dev, "Unable to read key codes\n");
|
|
+ pdata->key_nums = 0;
|
|
+ memset(pdata->key_map, 0,
|
|
+ MAX_KEY_NUMS * sizeof(pdata->key_map[0]));
|
|
+ }
|
|
+ pdata->key_nums = key_nums;
|
|
+ memcpy(pdata->key_map, key_map,
|
|
+ key_nums * sizeof(pdata->key_map[0]));
|
|
+ dev_info(dev, "key-map is [%x %x %x %x]\n",
|
|
+ pdata->key_map[0], pdata->key_map[1],
|
|
+ pdata->key_map[2], pdata->key_map[3]);
|
|
+ }
|
|
+
|
|
+ pdata->irq_gpio = of_get_named_gpio(np, "irq-gpios", 0);
|
|
+ if (!gpio_is_valid(pdata->irq_gpio))
|
|
+ dev_err(dev, "No valid irq gpio");
|
|
+
|
|
+ pdata->rst_gpio = of_get_named_gpio(np, "reset-gpios", 0);
|
|
+ if (!gpio_is_valid(pdata->rst_gpio))
|
|
+ dev_err(dev, "No valid rst gpio");
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*******************************************************
|
|
+ * Function:
|
|
+ * parse config data from devices tree.
|
|
+ * Input:
|
|
+ * dev: device that this driver attached.
|
|
+ * cfg: pointer of the config array.
|
|
+ * cfg_len: pointer of the config length.
|
|
+ * sid: sensor id.
|
|
+ * Output:
|
|
+ * Executive outcomes.
|
|
+ * 0-succeed, -1-faileds.
|
|
+ *******************************************************/
|
|
+int gtp_parse_dt_cfg(struct device *dev, u8 *cfg, int *cfg_len, u8 sid)
|
|
+{
|
|
+ struct device_node *np = dev->of_node;
|
|
+ struct property *prop;
|
|
+ char cfg_name[18];
|
|
+ int ret;
|
|
+
|
|
+ snprintf(cfg_name, sizeof(cfg_name), "goodix,cfg-group%d", sid);
|
|
+ prop = of_find_property(np, cfg_name, cfg_len);
|
|
+ if (!prop || !prop->value || *cfg_len == 0 ||
|
|
+ *cfg_len > GTP_CONFIG_MAX_LENGTH) {
|
|
+ *cfg_len = 0;
|
|
+ ret = -EPERM;/* failed */
|
|
+ } else {
|
|
+ memcpy(cfg, prop->value, *cfg_len);
|
|
+ ret = 0;
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+#endif
|
|
+
|
|
+static int gtp_power_on(struct goodix_ts_data *ts)
|
|
+{
|
|
+ int ret = 0;
|
|
+
|
|
+ if (ts->vdd_ana) {
|
|
+ ret = regulator_enable(ts->vdd_ana);
|
|
+ if (ret) {
|
|
+ dev_err(&ts->client->dev,
|
|
+ "Regulator vdd enable failed ret=%d\n",
|
|
+ ret);
|
|
+ goto err_enable_vdd_ana;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (ts->vcc_i2c) {
|
|
+ ret = regulator_enable(ts->vcc_i2c);
|
|
+ if (ret) {
|
|
+ dev_err(&ts->client->dev,
|
|
+ "Regulator vcc_i2c enable failed ret=%d\n",
|
|
+ ret);
|
|
+ goto err_enable_vcc_i2c;
|
|
+ }
|
|
+ }
|
|
+ clear_bit(POWER_OFF_MODE, &ts->flags);
|
|
+ return 0;
|
|
+
|
|
+err_enable_vcc_i2c:
|
|
+ if (ts->vdd_ana)
|
|
+ regulator_disable(ts->vdd_ana);
|
|
+err_enable_vdd_ana:
|
|
+ set_bit(POWER_OFF_MODE, &ts->flags);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int gtp_power_off(struct goodix_ts_data *ts)
|
|
+{
|
|
+ int ret = 0;
|
|
+
|
|
+ if (ts->vcc_i2c) {
|
|
+ set_bit(POWER_OFF_MODE, &ts->flags);
|
|
+ ret = regulator_disable(ts->vcc_i2c);
|
|
+ if (ret) {
|
|
+ dev_err(&ts->client->dev,
|
|
+ "Regulator vcc_i2c disable failed ret=%d\n",
|
|
+ ret);
|
|
+ goto err_disable_vcc_i2c;
|
|
+ }
|
|
+ dev_info(&ts->client->dev,
|
|
+ "Regulator vcc_i2c disabled\n");
|
|
+ }
|
|
+
|
|
+ if (ts->vdd_ana) {
|
|
+ set_bit(POWER_OFF_MODE, &ts->flags);
|
|
+ ret = regulator_disable(ts->vdd_ana);
|
|
+ if (ret) {
|
|
+ dev_err(&ts->client->dev,
|
|
+ "Regulator vdd disable failed ret=%d\n",
|
|
+ ret);
|
|
+ goto err_disable_vdd_ana;
|
|
+ }
|
|
+ dev_info(&ts->client->dev,
|
|
+ "Regulator vdd_ana disabled\n");
|
|
+ }
|
|
+ return ret;
|
|
+
|
|
+err_disable_vdd_ana:
|
|
+ if (ts->vcc_i2c)
|
|
+ ret = regulator_enable(ts->vcc_i2c);
|
|
+err_disable_vcc_i2c:
|
|
+ clear_bit(POWER_OFF_MODE, &ts->flags);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int gtp_power_init(struct goodix_ts_data *ts)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ ts->vdd_ana = regulator_get(&ts->client->dev, "vdd_ana");
|
|
+ if (IS_ERR(ts->vdd_ana)) {
|
|
+ ts->vdd_ana = NULL;
|
|
+ ret = PTR_ERR(ts->vdd_ana);
|
|
+ dev_info(&ts->client->dev,
|
|
+ "Regulator get failed vdd ret=%d\n", ret);
|
|
+ }
|
|
+
|
|
+ ts->vcc_i2c = regulator_get(&ts->client->dev, "vcc_i2c");
|
|
+ if (IS_ERR(ts->vcc_i2c)) {
|
|
+ ts->vcc_i2c = NULL;
|
|
+ ret = PTR_ERR(ts->vcc_i2c);
|
|
+ dev_info(&ts->client->dev,
|
|
+ "Regulator get failed vcc_i2c ret=%d\n", ret);
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int gtp_power_deinit(struct goodix_ts_data *ts)
|
|
+{
|
|
+ if (ts->vdd_ana)
|
|
+ regulator_put(ts->vdd_ana);
|
|
+ if (ts->vcc_i2c)
|
|
+ regulator_put(ts->vcc_i2c);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static void gtp_shutdown(struct i2c_client *client)
|
|
+{
|
|
+ struct goodix_ts_data *data = i2c_get_clientdata(client);
|
|
+
|
|
+ if (!data->init_done)
|
|
+ return;
|
|
+
|
|
+ gtp_work_control_enable(data, false);
|
|
+ gtp_power_off(data);
|
|
+
|
|
+ return;
|
|
+}
|
|
+
|
|
+static int gtp_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
|
+{
|
|
+ int ret = -1;
|
|
+ struct goodix_ts_data *ts;
|
|
+ struct goodix_ts_platform_data *pdata;
|
|
+
|
|
+ /* do NOT remove these logs */
|
|
+ dev_info(&client->dev, "GTP Driver Version: %s\n", GTP_DRIVER_VERSION);
|
|
+ dev_info(&client->dev, "GTP I2C Address: 0x%02x\n", client->addr);
|
|
+
|
|
+ i2c_connect_client = client;
|
|
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
|
+ dev_err(&client->dev, "Failed check I2C functionality");
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
+ ts = devm_kzalloc(&client->dev, sizeof(*ts), GFP_KERNEL);
|
|
+ if (!ts) {
|
|
+ dev_err(&client->dev, "Failed alloc ts memory");
|
|
+ return -ENOMEM;
|
|
+ }
|
|
+
|
|
+ pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
|
|
+ if (!pdata) {
|
|
+ dev_err(&client->dev, "Failed alloc pdata memory\n");
|
|
+ devm_kfree(&client->dev, ts);
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ ts->init_done = false;
|
|
+
|
|
+#ifdef CONFIG_OF
|
|
+ if (client->dev.of_node) {
|
|
+ ret = gtp_parse_dt(&client->dev, pdata);
|
|
+ if (ret) {
|
|
+ dev_err(&client->dev, "Failed parse dts\n");
|
|
+ goto exit_free_client_data;
|
|
+ }
|
|
+ }
|
|
+#else
|
|
+ /* set parameters at here if you platform doesn't DTS */
|
|
+ pdata->rst_gpio = GTP_RST_PORT;
|
|
+ pdata->irq_gpio = GTP_INT_PORT;
|
|
+ pdata->slide_wakeup = false;
|
|
+ pdata->auto_update = true;
|
|
+ pdata->auto_update_cfg = false;
|
|
+ pdata->type_a_report = false;
|
|
+ pdata->esd_protect = false;
|
|
+ pdata->max_touch_id = GTP_MAX_TOUCH_ID;
|
|
+ pdata->abs_size_x = GTP_DEFAULT_MAX_X;
|
|
+ pdata->abs_size_y = GTP_DEFAULT_MAX_Y;
|
|
+ pdata->max_touch_width = GTP_DEFAULT_MAX_WIDTH;
|
|
+ pdata->max_touch_pressure = GTP_DEFAULT_MAX_PRESSURE;
|
|
+#endif
|
|
+
|
|
+ ts->client = client;
|
|
+ ts->pdata = pdata;
|
|
+
|
|
+ i2c_set_clientdata(client, ts);
|
|
+
|
|
+ ret = gtp_power_init(ts);
|
|
+ if (ret) {
|
|
+ dev_err(&client->dev, "Failed get regulator\n");
|
|
+ ret = -EINVAL;
|
|
+ goto exit_free_client_data;
|
|
+ }
|
|
+
|
|
+ ret = gtp_power_on(ts);
|
|
+ if (ret) {
|
|
+ dev_err(&client->dev, "Failed power on device\n");
|
|
+ ret = -EINVAL;
|
|
+ goto exit_deinit_power;
|
|
+ }
|
|
+
|
|
+ ret = gtp_pinctrl_init(ts);
|
|
+ if (ret < 0) {
|
|
+ /* if define pinctrl must define the following state
|
|
+ * to let int-pin work normally: default, int_output_high,
|
|
+ * int_output_low, int_input
|
|
+ */
|
|
+ dev_err(&client->dev, "Failed get wanted pinctrl state\n");
|
|
+ goto exit_deinit_power;
|
|
+ }
|
|
+
|
|
+ ret = gtp_request_io_port(ts);
|
|
+ if (ret < 0) {
|
|
+ dev_err(&client->dev, "Failed request IO port\n");
|
|
+ goto exit_power_off;
|
|
+ }
|
|
+
|
|
+ gtp_reset_guitar(ts->client, 20);
|
|
+
|
|
+ ret = gtp_i2c_test(client);
|
|
+ if (ret) {
|
|
+ dev_err(&client->dev, "Failed communicate with IC use I2C\n");
|
|
+ goto exit_free_io_port;
|
|
+ }
|
|
+
|
|
+ dev_info(&client->dev, "I2C Addr is %x\n", client->addr);
|
|
+
|
|
+ ret = gtp_get_fw_info(client, &ts->fw_info);
|
|
+ if (ret < 0) {
|
|
+ dev_err(&client->dev, "Failed read FW version\n");
|
|
+ goto exit_free_io_port;
|
|
+ }
|
|
+
|
|
+ pdata->config.data[0] = GTP_REG_CONFIG_DATA >> 8;
|
|
+ pdata->config.data[1] = GTP_REG_CONFIG_DATA & 0xff;
|
|
+ ret = gtp_init_panel(ts);
|
|
+ if (ret < 0)
|
|
+ dev_info(&client->dev, "Panel un-initialize\n");
|
|
+
|
|
+ ret = gtp_request_input_dev(ts);
|
|
+ if (ret < 0) {
|
|
+ dev_err(&client->dev, "Failed request input device\n");
|
|
+ goto exit_free_io_port;
|
|
+ }
|
|
+
|
|
+ mutex_init(&ts->lock);
|
|
+
|
|
+ ret = gtp_request_irq(ts);
|
|
+ if (ret < 0) {
|
|
+ dev_err(&client->dev, "Failed create work thread");
|
|
+ goto exit_unreg_input_dev;
|
|
+ }
|
|
+ gtp_work_control_enable(ts, false);
|
|
+ if (ts->pdata->slide_wakeup) {
|
|
+ dev_info(&client->dev, "slide wakeup enabled\n");
|
|
+ ret = enable_irq_wake(client->irq);
|
|
+ if (ret < 0)
|
|
+ dev_err(&client->dev, "Failed set irq wake\n");
|
|
+ }
|
|
+
|
|
+ gtp_register_powermanager(ts);
|
|
+
|
|
+ ret = gtp_create_file(ts);
|
|
+ if (ret) {
|
|
+ dev_info(&client->dev, "Failed create attributes file");
|
|
+ goto exit_powermanager;
|
|
+ }
|
|
+
|
|
+ gtp_esd_init(ts);
|
|
+ gtp_esd_on(ts);
|
|
+ /* probe init finished */
|
|
+ ts->init_done = true;
|
|
+ gtp_work_control_enable(ts, true);
|
|
+
|
|
+ init_wr_node(client);/*TODO judge return value */
|
|
+
|
|
+ if (ts->pdata->auto_update) {
|
|
+ ret = gup_init_update_proc(ts);
|
|
+ if (ret < 0)
|
|
+ dev_err(&client->dev, "Failed create update thread\n");
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+
|
|
+exit_powermanager:
|
|
+ gtp_unregister_powermanager(ts);
|
|
+exit_unreg_input_dev:
|
|
+ input_unregister_device(ts->input_dev);
|
|
+exit_free_io_port:
|
|
+ if (gpio_is_valid(ts->pdata->rst_gpio))
|
|
+ gpio_free(ts->pdata->rst_gpio);
|
|
+ if (gpio_is_valid(ts->pdata->irq_gpio))
|
|
+ gpio_free(ts->pdata->irq_gpio);
|
|
+exit_power_off:
|
|
+ gtp_power_off(ts);
|
|
+ gtp_pinctrl_deinit(ts);
|
|
+exit_deinit_power:
|
|
+ gtp_power_deinit(ts);
|
|
+exit_free_client_data:
|
|
+ devm_kfree(&client->dev, pdata);
|
|
+ devm_kfree(&client->dev, ts);
|
|
+ i2c_set_clientdata(client, NULL);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void gtp_drv_remove(struct i2c_client *client)
|
|
+{
|
|
+ struct goodix_ts_data *ts = i2c_get_clientdata(client);
|
|
+
|
|
+ gtp_work_control_enable(ts, false);
|
|
+ gtp_unregister_powermanager(ts);
|
|
+
|
|
+ remove_proc_entry(GT91XX_CONFIG_PROC_FILE, gtp_config_proc);
|
|
+
|
|
+ sysfs_remove_group(&client->dev.kobj, >p_attr_group);
|
|
+
|
|
+ uninit_wr_node();
|
|
+
|
|
+ if (ts->pdata->esd_protect)
|
|
+ gtp_esd_off(ts);
|
|
+
|
|
+ /* TODO: how to judge a irq numbers validity */
|
|
+ if (ts->client->irq)
|
|
+ free_irq(client->irq, ts);
|
|
+ else
|
|
+ hrtimer_cancel(&ts->timer);
|
|
+
|
|
+ if (gpio_is_valid(ts->pdata->rst_gpio))
|
|
+ gpio_free(ts->pdata->rst_gpio);
|
|
+
|
|
+ if (gpio_is_valid(ts->pdata->irq_gpio))
|
|
+ gpio_free(ts->pdata->irq_gpio);
|
|
+
|
|
+ gtp_power_off(ts);
|
|
+ gtp_power_deinit(ts);
|
|
+ gtp_pinctrl_deinit(ts);
|
|
+ dev_info(&client->dev, "goodix ts driver removed");
|
|
+ i2c_set_clientdata(client, NULL);
|
|
+ input_unregister_device(ts->input_dev);
|
|
+ mutex_destroy(&ts->lock);
|
|
+
|
|
+ devm_kfree(&client->dev, ts->pdata);
|
|
+ devm_kfree(&client->dev, ts);
|
|
+
|
|
+ //return 0;
|
|
+}
|
|
+
|
|
+static void gtp_suspend(struct goodix_ts_data *ts)
|
|
+{
|
|
+ int ret = -1;
|
|
+
|
|
+ if (test_bit(FW_UPDATE_RUNNING, &ts->flags)) {
|
|
+ dev_warn(&ts->client->dev,
|
|
+ "Fw upgrade in progress, can't go to suspend\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (test_and_set_bit(SLEEP_MODE, &ts->flags)) {
|
|
+ dev_info(&ts->client->dev, "Already in suspend state\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ dev_dbg(&ts->client->dev, "Try enter suspend mode\n");
|
|
+
|
|
+ gtp_esd_off(ts);
|
|
+ gtp_work_control_enable(ts, false);
|
|
+ if (ts->pdata->slide_wakeup) {
|
|
+ ret = gtp_enter_doze(ts);
|
|
+ gtp_work_control_enable(ts, true);
|
|
+ } else if (ts->pdata->power_off_sleep) {
|
|
+ /*TODO: power off routine */
|
|
+ gtp_power_off(ts);
|
|
+ ret = SUCCESS;
|
|
+ } else {
|
|
+ ret = gtp_enter_sleep(ts);
|
|
+ }
|
|
+
|
|
+ if (ret < 0)
|
|
+ dev_err(&ts->client->dev, "Failed enter suspend\n");
|
|
+
|
|
+ /* to avoid waking up while not sleeping */
|
|
+ /* delay 48 + 10ms to ensure reliability */
|
|
+ msleep(GTP_58_DLY_MS);
|
|
+}
|
|
+
|
|
+static int gtp_gesture_wakeup(struct goodix_ts_data *ts)
|
|
+{
|
|
+ int ret;
|
|
+ int retry = 10;
|
|
+
|
|
+ do {
|
|
+ gtp_reset_guitar(ts->client, 10);
|
|
+ ret = gtp_i2c_test(ts->client);
|
|
+ if (!ret)
|
|
+ break;
|
|
+ } while (--retry);
|
|
+
|
|
+ if (!retry)
|
|
+ ret = -EIO;
|
|
+
|
|
+ clear_bit(DOZE_MODE, &ts->flags);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void gtp_resume(struct goodix_ts_data *ts)
|
|
+{
|
|
+ int ret = 0;
|
|
+
|
|
+ if (test_bit(FW_UPDATE_RUNNING, &ts->flags)) {
|
|
+ dev_info(&ts->client->dev,
|
|
+ "Fw upgrade in progress, can't do resume\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (!test_bit(SLEEP_MODE, &ts->flags)) {
|
|
+ dev_dbg(&ts->client->dev, "Already in awake state\n");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ dev_info(&ts->client->dev, "Try resume from sleep mode\n");
|
|
+
|
|
+ gtp_work_control_enable(ts, false);
|
|
+
|
|
+ if (ts->pdata->slide_wakeup && test_bit(DOZE_MODE, &ts->flags)) {
|
|
+ ret = gtp_gesture_wakeup(ts);
|
|
+ if (ret)
|
|
+ dev_warn(&ts->client->dev, "Failed wake up from gesture mode\n");
|
|
+ } else if (ts->pdata->power_off_sleep) {
|
|
+ ret = gtp_power_on(ts);
|
|
+ if (ret) {
|
|
+ dev_warn(&ts->client->dev, "Failed wake up from gesture mode\n");
|
|
+ } else {
|
|
+ gtp_reset_guitar(ts->client, 20);
|
|
+ ret = gtp_i2c_test(ts->client);
|
|
+ if (ret)
|
|
+ dev_warn(&ts->client->dev,
|
|
+ "I2C communicate failed after power on\n");
|
|
+ }
|
|
+ } else {
|
|
+ ret = gtp_wakeup_sleep(ts);
|
|
+ if (ret)
|
|
+ dev_warn(&ts->client->dev,
|
|
+ "Failed wakeup from sleep mode\n");
|
|
+ }
|
|
+
|
|
+ if (ret)
|
|
+ dev_warn(&ts->client->dev, "Later resume failed\n");
|
|
+ else
|
|
+ gtp_esd_on(ts);
|
|
+
|
|
+ clear_bit(SLEEP_MODE, &ts->flags);
|
|
+ gtp_work_control_enable(ts, true);
|
|
+}
|
|
+
|
|
+#if defined(CONFIG_FB)
|
|
+static void fb_notify_resume_work(struct work_struct *work)
|
|
+{
|
|
+ struct goodix_ts_data *ts =
|
|
+ container_of(work, struct goodix_ts_data, fb_notify_work);
|
|
+ dev_info(&ts->client->dev, "try resume in workqueue\n");
|
|
+ gtp_resume(ts);
|
|
+}
|
|
+
|
|
+/* frame buffer notifier block control the suspend/resume procedure */
|
|
+static int gtp_fb_notifier_callback(struct notifier_block *noti,
|
|
+ unsigned long event, void *data)
|
|
+{
|
|
+ struct fb_event *ev_data = data;
|
|
+ struct goodix_ts_data *ts = container_of(noti,
|
|
+ struct goodix_ts_data, notifier);
|
|
+ int *blank;
|
|
+
|
|
+ if (ev_data && ev_data->data && event == FB_EVENT_BLANK && ts) {
|
|
+ blank = ev_data->data;
|
|
+ if (*blank == FB_BLANK_UNBLANK ||
|
|
+ *blank == FB_BLANK_NORMAL) {
|
|
+ dev_dbg(&ts->client->dev, "ts_resume");
|
|
+ if (ts->pdata->resume_in_workqueue)
|
|
+ schedule_work(&ts->fb_notify_work);
|
|
+ else
|
|
+ gtp_resume(ts);
|
|
+ } else if (*blank == FB_BLANK_POWERDOWN) {
|
|
+ dev_dbg(&ts->client->dev, "ts_suspend");
|
|
+ if (ts->pdata->resume_in_workqueue)
|
|
+ flush_work(&ts->fb_notify_work);
|
|
+ gtp_suspend(ts);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#elif defined(CONFIG_PM)
|
|
+static int gtp_pm_suspend(struct device *dev)
|
|
+{
|
|
+ struct i2c_client *client = to_i2c_client(dev);
|
|
+ struct goodix_ts_data *ts = i2c_get_clientdata(client);
|
|
+
|
|
+ if (ts) {
|
|
+ dev_dbg(&ts->client->dev, "Suspend by i2c pm.");
|
|
+ gtp_suspend(ts);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int gtp_pm_resume(struct device *dev)
|
|
+{
|
|
+ struct i2c_client *client = to_i2c_client(dev);
|
|
+ struct goodix_ts_data *ts = i2c_get_clientdata(client);
|
|
+
|
|
+ if (ts) {
|
|
+ dev_dbg(&ts->client->dev, "Resume by i2c pm.");
|
|
+ gtp_resume(ts);
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static const struct dev_pm_ops gtp_pm_ops = {
|
|
+ .suspend = gtp_pm_suspend,
|
|
+ .resume = gtp_pm_resume,
|
|
+};
|
|
+
|
|
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
|
|
+static void gtp_early_suspend(struct early_suspend *h)
|
|
+{
|
|
+ struct goodix_ts_data *ts = container_of(h,
|
|
+ struct goodix_ts_data, early_suspend);
|
|
+
|
|
+ if (ts) {
|
|
+ dev_dbg(&ts->client->dev, "Suspend by earlysuspend module.");
|
|
+ gtp_suspend(ts);
|
|
+ }
|
|
+}
|
|
+
|
|
+static void gtp_late_resume(struct early_suspend *h)
|
|
+{
|
|
+ struct goodix_ts_data *ts = container_of(h,
|
|
+ struct goodix_ts_data, early_suspend);
|
|
+
|
|
+ if (ts) {
|
|
+ dev_dbg(&ts->client->dev, "Resume by earlysuspend module.");
|
|
+ gtp_resume(ts);
|
|
+ }
|
|
+}
|
|
+#endif
|
|
+
|
|
+static int gtp_register_powermanager(struct goodix_ts_data *ts)
|
|
+{
|
|
+ int ret;
|
|
+#if defined(CONFIG_FB)
|
|
+ INIT_WORK(&ts->fb_notify_work, fb_notify_resume_work);
|
|
+ ts->notifier.notifier_call = gtp_fb_notifier_callback;
|
|
+ ret = fb_register_client(&ts->notifier);
|
|
+ if (ret)
|
|
+ dev_err(&ts->client->dev,
|
|
+ "Unable to register fb_notifier: %d\n", ret);
|
|
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
|
|
+ ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
|
|
+ ts->early_suspend.suspend = goodix_ts_early_suspend;
|
|
+ ts->early_suspend.resume = goodix_ts_late_resume;
|
|
+ register_early_suspend(&ts->early_suspend);
|
|
+#endif
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static int gtp_unregister_powermanager(struct goodix_ts_data *ts)
|
|
+{
|
|
+#if defined(CONFIG_FB)
|
|
+ fb_unregister_client(&ts->notifier);
|
|
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
|
|
+ unregister_early_suspend(&ts->early_suspend);
|
|
+#endif
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/*******************************************************
|
|
+ * Function:
|
|
+ * Initialize external watchdog for esd protect
|
|
+ * Input:
|
|
+ * client: i2c device.
|
|
+ * Output:
|
|
+ * result of i2c write operation.
|
|
+ * 0: succeed, otherwise: failed
|
|
+ ********************************************************/
|
|
+static int gtp_init_ext_watchdog(struct i2c_client *client)
|
|
+{
|
|
+ int ret;
|
|
+ u8 opr_buffer[3] = { (u8)(GTP_REG_ESD_CHECK >> 8),
|
|
+ (u8)GTP_REG_ESD_CHECK,
|
|
+ (u8)GTP_ESD_CHECK_VALUE };
|
|
+
|
|
+ dev_dbg(&client->dev, "[Esd]Init external watchdog\n");
|
|
+ ret = gtp_i2c_write(client, opr_buffer, 3);
|
|
+ if (ret == 1)
|
|
+ return 0;
|
|
+
|
|
+ dev_err(&client->dev, "Failed init ext watchdog\n");
|
|
+ return -EINVAL;
|
|
+}
|
|
+
|
|
+static void gtp_esd_check_func(struct work_struct *work)
|
|
+{
|
|
+ s32 i;
|
|
+ s32 ret = -1;
|
|
+ u8 esd_buf[5] = { (u8)(GTP_REG_COMMAND >> 8), (u8)GTP_REG_COMMAND };
|
|
+ struct delayed_work *dwork = to_delayed_work(work);
|
|
+ struct goodix_ts_esd *ts_esd = container_of(dwork, struct goodix_ts_esd,
|
|
+ delayed_work);
|
|
+ struct goodix_ts_data *ts = container_of(ts_esd, struct goodix_ts_data,
|
|
+ ts_esd);
|
|
+
|
|
+ if (test_bit(SLEEP_MODE, &ts->flags) ||
|
|
+ test_bit(FW_UPDATE_RUNNING, &ts->flags)) {
|
|
+ dev_dbg(&ts->client->dev,
|
|
+ "Esd cancled by power_suspend or fw_update!");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (ts_esd->esd_on == false)
|
|
+ return;
|
|
+
|
|
+ for (i = 0; i < 3; i++) {
|
|
+ ret = gtp_i2c_read(ts->client, esd_buf, 4);
|
|
+ if (ret < 0)
|
|
+ continue;
|
|
+
|
|
+ dev_dbg(&ts->client->dev,
|
|
+ "[Esd]0x8040 = 0x%02X, 0x8041 = 0x%02X",
|
|
+ esd_buf[2], esd_buf[3]);
|
|
+ if (esd_buf[2] == (u8)GTP_ESD_CHECK_VALUE ||
|
|
+ esd_buf[3] != (u8)GTP_ESD_CHECK_VALUE) {
|
|
+ gtp_i2c_read(ts->client, esd_buf, 4);
|
|
+ if (ret < 0)
|
|
+ continue;
|
|
+
|
|
+ if (esd_buf[2] == (u8)GTP_ESD_CHECK_VALUE ||
|
|
+ esd_buf[3] != (u8)GTP_ESD_CHECK_VALUE) {
|
|
+ i = 3;
|
|
+ break;
|
|
+ }
|
|
+ } else {
|
|
+ /* IC works normally, Write 0x8040 0xAA, feed the dog */
|
|
+ esd_buf[2] = (u8)GTP_ESD_CHECK_VALUE;
|
|
+ gtp_i2c_write(ts->client, esd_buf, 3);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if (i >= 3) {
|
|
+ dev_err(&ts->client->dev, "IC working abnormally! Reset IC\n");
|
|
+ esd_buf[0] = 0x42;
|
|
+ esd_buf[1] = 0x26;
|
|
+ esd_buf[2] = 0x01;
|
|
+ esd_buf[3] = 0x01;
|
|
+ esd_buf[4] = 0x01;
|
|
+ gtp_i2c_write(ts->client, esd_buf, 5);
|
|
+ /* TODO: Is power off really need? */
|
|
+ msleep(GTP_50_DLY_MS);
|
|
+ gtp_power_off(ts);
|
|
+ msleep(GTP_20_DLY_MS);
|
|
+ gtp_power_on(ts);
|
|
+ msleep(GTP_20_DLY_MS);
|
|
+
|
|
+ gtp_reset_guitar(ts->client, 50);
|
|
+ msleep(GTP_50_DLY_MS);
|
|
+ gtp_send_cfg(ts->client);
|
|
+ }
|
|
+
|
|
+ if (ts_esd->esd_on == true && !test_bit(SLEEP_MODE, &ts->flags)) {
|
|
+ schedule_delayed_work(&ts_esd->delayed_work, 2 * HZ);
|
|
+ dev_dbg(&ts->client->dev, "ESD work rescheduled\n");
|
|
+ }
|
|
+}
|
|
+
|
|
+static int gtp_esd_init(struct goodix_ts_data *ts)
|
|
+{
|
|
+ struct goodix_ts_esd *ts_esd = &ts->ts_esd;
|
|
+
|
|
+ INIT_DELAYED_WORK(&ts_esd->delayed_work, gtp_esd_check_func);
|
|
+ mutex_init(&ts_esd->mutex);
|
|
+ ts_esd->esd_on = false;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+void gtp_esd_on(struct goodix_ts_data *ts)
|
|
+{
|
|
+ struct goodix_ts_esd *ts_esd = &ts->ts_esd;
|
|
+
|
|
+ if (!ts->pdata->esd_protect)
|
|
+ return;
|
|
+ mutex_lock(&ts_esd->mutex);
|
|
+ if (ts_esd->esd_on == false) {
|
|
+ ts_esd->esd_on = true;
|
|
+ schedule_delayed_work(&ts_esd->delayed_work, 2 * HZ);
|
|
+ dev_info(&ts->client->dev, "ESD on");
|
|
+ }
|
|
+ mutex_unlock(&ts_esd->mutex);
|
|
+}
|
|
+
|
|
+void gtp_esd_off(struct goodix_ts_data *ts)
|
|
+{
|
|
+ struct goodix_ts_esd *ts_esd = &ts->ts_esd;
|
|
+
|
|
+ if (!ts->pdata->esd_protect)
|
|
+ return;
|
|
+ mutex_lock(&ts_esd->mutex);
|
|
+ if (ts_esd->esd_on == true) {
|
|
+ ts_esd->esd_on = false;
|
|
+ cancel_delayed_work_sync(&ts_esd->delayed_work);
|
|
+ dev_info(&ts->client->dev, "ESD off");
|
|
+ }
|
|
+ mutex_unlock(&ts_esd->mutex);
|
|
+}
|
|
+
|
|
+#ifdef CONFIG_OF
|
|
+static const struct of_device_id gtp_match_table[] = {
|
|
+ {.compatible = "goodix,gt9xx",},
|
|
+ { },
|
|
+};
|
|
+#endif
|
|
+
|
|
+static const struct i2c_device_id gtp_device_id[] = {
|
|
+ { GTP_I2C_NAME, 0 },
|
|
+ { }
|
|
+};
|
|
+
|
|
+static struct i2c_driver goodix_ts_driver = {
|
|
+ .probe = gtp_probe,
|
|
+ .remove = gtp_drv_remove,
|
|
+ .id_table = gtp_device_id,
|
|
+ .shutdown = gtp_shutdown,
|
|
+ .driver = {
|
|
+ .name = GTP_I2C_NAME,
|
|
+ .owner = THIS_MODULE,
|
|
+#ifdef CONFIG_OF
|
|
+ .of_match_table = gtp_match_table,
|
|
+#endif
|
|
+#if !defined(CONFIG_FB) && defined(CONFIG_PM)
|
|
+ .pm = >p_pm_ops,
|
|
+#endif
|
|
+ },
|
|
+};
|
|
+
|
|
+static int __init gtp_init(void)
|
|
+{
|
|
+ s32 ret;
|
|
+
|
|
+ pr_info("Gt9xx driver installing..\n");
|
|
+ ret = i2c_add_driver(&goodix_ts_driver);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void __exit gtp_exit(void)
|
|
+{
|
|
+ pr_info("Gt9xx driver exited\n");
|
|
+ i2c_del_driver(&goodix_ts_driver);
|
|
+}
|
|
+
|
|
+module_init(gtp_init);
|
|
+module_exit(gtp_exit);
|
|
+
|
|
+MODULE_DESCRIPTION("GT9 serials touch controller Driver");
|
|
+MODULE_LICENSE("GPL v2");
|
|
+
|
|
diff --git a/drivers/input/touchscreen/gt9xx.h b/drivers/input/touchscreen/gt9xx.h
|
|
new file mode 100644
|
|
index 000000000000..111111111111
|
|
--- /dev/null
|
|
+++ b/drivers/input/touchscreen/gt9xx.h
|
|
@@ -0,0 +1,380 @@
|
|
+/*
|
|
+ * Goodix GT9xx touchscreen driver
|
|
+ *
|
|
+ * Copyright (C) 2016 - 2017 Goodix. Ltd.
|
|
+ *
|
|
+ * 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 a reference
|
|
+ * to you, when you are integrating the GOODiX's CTP IC into your system,
|
|
+ * 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.
|
|
+ *
|
|
+ * Version: 2.8.0.2
|
|
+ * Release Date: 2017/12/14
|
|
+ */
|
|
+
|
|
+#ifndef _GOODIX_GT9XX_H_
|
|
+#define _GOODIX_GT9XX_H_
|
|
+
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/hrtimer.h>
|
|
+#include <linux/i2c.h>
|
|
+#include <linux/input.h>
|
|
+#include <linux/module.h>
|
|
+#include <linux/delay.h>
|
|
+#include <linux/i2c.h>
|
|
+#include <linux/proc_fs.h>
|
|
+#include <linux/string.h>
|
|
+#include <linux/uaccess.h>
|
|
+#include <linux/vmalloc.h>
|
|
+#include <linux/interrupt.h>
|
|
+#include <linux/io.h>
|
|
+#include <linux/gpio.h>
|
|
+#include <linux/regulator/consumer.h>
|
|
+#include <linux/major.h>
|
|
+#include <linux/kdev_t.h>
|
|
+#ifdef CONFIG_OF
|
|
+#include <linux/of_gpio.h>
|
|
+#endif
|
|
+#ifdef CONFIG_FB
|
|
+#include <linux/notifier.h>
|
|
+#include <linux/fb.h>
|
|
+#endif
|
|
+#ifdef CONFIG_HAS_EARLYSUSPEND
|
|
+#include <linux/earlysuspend.h>
|
|
+#endif
|
|
+#include <linux/usb.h>
|
|
+#include <linux/power_supply.h>
|
|
+
|
|
+#define GTP_TOOL_PEN 1
|
|
+#define GTP_TOOL_FINGER 2
|
|
+
|
|
+#define MAX_KEY_NUMS 4
|
|
+#define GTP_CONFIG_MAX_LENGTH 240
|
|
+#define GTP_ADDR_LENGTH 2
|
|
+
|
|
+/***************************PART1:ON/OFF define*******************************/
|
|
+#define GTP_DEBUG_ON 1
|
|
+#define GTP_DEBUG_ARRAY_ON 0
|
|
+#define GTP_DEBUG_FUNC_ON 0
|
|
+
|
|
+struct goodix_point_t {
|
|
+ int id;
|
|
+ int x;
|
|
+ int y;
|
|
+ int w;
|
|
+ int p;
|
|
+ int tool_type;
|
|
+};
|
|
+
|
|
+struct goodix_config_data {
|
|
+ int length;
|
|
+ u8 data[GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH];
|
|
+};
|
|
+
|
|
+struct goodix_ts_platform_data {
|
|
+ int irq_gpio;
|
|
+ int rst_gpio;
|
|
+ u32 irq_flags;
|
|
+ u32 abs_size_x;
|
|
+ u32 abs_size_y;
|
|
+ u32 max_touch_id;
|
|
+ u32 max_touch_width;
|
|
+ u32 max_touch_pressure;
|
|
+ u32 key_map[MAX_KEY_NUMS];
|
|
+ u32 key_nums;
|
|
+ u32 int_sync;
|
|
+ u32 driver_send_cfg;
|
|
+ u32 swap_x2y;
|
|
+ u32 slide_wakeup;
|
|
+ u32 auto_update;
|
|
+ u32 auto_update_cfg;
|
|
+ u32 esd_protect;
|
|
+ u32 type_a_report;
|
|
+ u32 power_off_sleep;
|
|
+ u32 resume_in_workqueue;
|
|
+ u32 pen_suppress_finger;
|
|
+ struct goodix_config_data config;
|
|
+};
|
|
+
|
|
+struct goodix_ts_esd {
|
|
+ struct delayed_work delayed_work;
|
|
+ struct mutex mutex;
|
|
+ bool esd_on;
|
|
+};
|
|
+
|
|
+enum {
|
|
+ WORK_THREAD_ENABLED = 0,
|
|
+ HRTIMER_USED,
|
|
+ FW_ERROR,
|
|
+
|
|
+ DOZE_MODE,
|
|
+ SLEEP_MODE,
|
|
+ POWER_OFF_MODE,
|
|
+ RAW_DATA_MODE,
|
|
+
|
|
+ FW_UPDATE_RUNNING,
|
|
+ PANEL_RESETTING
|
|
+};
|
|
+
|
|
+struct goodix_pinctrl {
|
|
+ struct pinctrl *pinctrl;
|
|
+ struct pinctrl_state *default_sta;
|
|
+ struct pinctrl_state *int_out_high;
|
|
+ struct pinctrl_state *int_out_low;
|
|
+ struct pinctrl_state *int_input;
|
|
+};
|
|
+
|
|
+struct goodix_fw_info {
|
|
+ u8 pid[6];
|
|
+ u16 version;
|
|
+ u8 sensor_id;
|
|
+};
|
|
+
|
|
+struct goodix_ts_data {
|
|
+ unsigned long flags; /* This member record the device status */
|
|
+
|
|
+ struct goodix_ts_esd ts_esd;
|
|
+ struct i2c_client *client;
|
|
+ struct input_dev *input_dev;
|
|
+ struct input_dev *pen_dev;
|
|
+ struct goodix_ts_platform_data *pdata;
|
|
+ /* use pinctrl control int-pin output low or high */
|
|
+ struct goodix_pinctrl pinctrl;
|
|
+ struct hrtimer timer;
|
|
+ struct mutex lock;
|
|
+ struct notifier_block ps_notif;
|
|
+ struct regulator *vdd_ana;
|
|
+ struct regulator *vcc_i2c;
|
|
+#if defined(CONFIG_FB)
|
|
+ struct notifier_block notifier;
|
|
+ struct work_struct fb_notify_work;
|
|
+#elif defined(CONFIG_HAS_EARLYSUSPEND)
|
|
+ struct early_suspend early_suspend;
|
|
+#endif
|
|
+ struct goodix_fw_info fw_info;
|
|
+ bool force_update;
|
|
+ bool init_done;
|
|
+};
|
|
+
|
|
+/************************* PART2:TODO define *******************************/
|
|
+/* STEP_1(REQUIRED): Define Configuration Information Group(s)
|
|
+ Sensor_ID Map:
|
|
+ sensor_opt1 sensor_opt2 Sensor_ID
|
|
+ GND GND 0
|
|
+ VDDIO GND 1
|
|
+ NC GND 2
|
|
+ GND NC/300K 3
|
|
+ VDDIO NC/300K 4
|
|
+ NC NC/300K 5
|
|
+*/
|
|
+/* TODO: define your own default or for Sensor_ID == 0 config here.
|
|
+ The predefined one is just a sample config,
|
|
+ which is not suitable for your tp in most cases. */
|
|
+#define CTP_CFG_GROUP0 {\
|
|
+ 0x41, 0xD0, 0x02, 0x00, 0x05, 0x0A, 0x34, \
|
|
+ 0x00, 0x01, 0x08, 0x28, 0x05, 0x50, 0x32, \
|
|
+ 0x03, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, \
|
|
+ 0x00, 0x00, 0x17, 0x19, 0x1E, 0x14, 0x8C, \
|
|
+ 0x2D, 0x0E, 0x3C, 0x3E, 0x82, 0x0A, 0x82, \
|
|
+ 0x0A, 0x00, 0x99, 0x33, 0x1D, 0x00, 0x00, \
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
|
|
+ 0x00, 0x2B, 0x19, 0x64, 0x94, 0xC0, 0x02, \
|
|
+ 0x08, 0x00, 0x00, 0x04, 0xF2, 0x1C, 0x00, \
|
|
+ 0xB9, 0x26, 0x00, 0x93, 0x32, 0x00, 0x77, \
|
|
+ 0x42, 0x00, 0x62, 0x57, 0x00, 0x62, 0x00, \
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
|
|
+ 0x00, 0x00, 0x00, 0xFF, 0x65, 0x00, 0x00, \
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
|
|
+ 0x19, 0x46, 0x00, 0x00, 0x00, 0x00, 0x32, \
|
|
+ 0x1C, 0x1A, 0x18, 0x16, 0x14, 0x12, 0x10, \
|
|
+ 0x0E, 0x0C, 0x0A, 0x08, 0x06, 0x04, 0x02, \
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
|
|
+ 0x00, 0x00, 0x00, 0x02, 0x04, 0x06, 0x08, \
|
|
+ 0x0A, 0x0C, 0x0F, 0x10, 0x12, 0x13, 0x14, \
|
|
+ 0x18, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, \
|
|
+ 0x22, 0x24, 0x26, 0x28, 0x29, 0x2A, 0xFF, \
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
|
|
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
|
|
+ 0x00, 0x00, 0xB8, 0x01\
|
|
+}
|
|
+
|
|
+/* TODO: define your config for Sensor_ID == 1 here, if needed */
|
|
+#define CTP_CFG_GROUP1 {\
|
|
+}
|
|
+
|
|
+/* TODO: define your config for Sensor_ID == 2 here, if needed */
|
|
+#define CTP_CFG_GROUP2 {\
|
|
+}
|
|
+
|
|
+/* TODO: define your config for Sensor_ID == 3 here, if needed */
|
|
+#define CTP_CFG_GROUP3 {\
|
|
+}
|
|
+/* TODO: define your config for Sensor_ID == 4 here, if needed */
|
|
+#define CTP_CFG_GROUP4 {\
|
|
+ 0x53,0xD0,0x02,0x00,0x05,0x05,0xF5,0xD5,0x21,0x48,0x2D,0x0F,\
|
|
+ 0x5A,0x41,0x0E,0x05,0x00,0x00,0x32,0x32,0x20,0x00,0x05,0x14,\
|
|
+ 0x14,0x1A,0x14,0x8B,0x2B,0x0C,0xB5,0xB7,0xEB,0x04,0xFF,0xFE,\
|
|
+ 0x00,0x22,0x33,0x10,0x3C,0x80,0x00,0x00,0x00,0x1E,0x12,0x41,\
|
|
+ 0x23,0x12,0x5A,0xAA,0xBE,0x4A,0x55,0x04,0x00,0x14,0x19,0x04,\
|
|
+ 0x80,0xAB,0x00,0x7F,0xAF,0x64,0x7E,0xB3,0x00,0x7E,0xB7,0x00,\
|
|
+ 0x7B,0xBB,0x3C,0x7B,0x08,0x30,0x00,0x00,0xF8,0x70,0x50,0xFF,\
|
|
+ 0xFF,0x17,0x00,0x46,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1E,\
|
|
+ 0x08,0x46,0x80,0x08,0x0A,0x00,0xA0,0x00,0x3C,0x28,0x19,0x19,\
|
|
+ 0x80,0x11,0x00,0x00,0x18,0x16,0x14,0x12,0x10,0x0E,0x0C,0x0A,\
|
|
+ 0x08,0x06,0x04,0x02,0xFF,0xFF,0x28,0x00,0x32,0x20,0x00,0x06,\
|
|
+ 0x00,0x00,0x0A,0x06,0x10,0x08,0x0A,0x22,0xEB,0x04,0x26,0x24,\
|
|
+ 0x22,0x21,0x20,0x1F,0x1E,0x1D,0x1C,0x18,0x16,0x12,0x10,0x0F,\
|
|
+ 0x0C,0x0A,0x08,0x06,0x04,0x02,0x00,0x13,0xFF,0xFF,0xFF,0xFF,\
|
|
+ 0x00,0x00,0x00,0x02,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,\
|
|
+ 0x00,0x00,0x00,0x00,0x28,0x0B,0x0B,0x00,0x00,0x00,0x00,0x00,\
|
|
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
|
|
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,\
|
|
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE6,0x10,0xEF,0x01\
|
|
+}
|
|
+
|
|
+/* TODO: define your config for Sensor_ID == 5 here, if needed */
|
|
+#define CTP_CFG_GROUP5 {\
|
|
+}
|
|
+
|
|
+/* STEP_2(REQUIRED): Customize your I/O ports & I/O operations */
|
|
+#define GTP_RST_PORT 64 /* EXYNOS4_GPX2(0) */
|
|
+#define GTP_INT_PORT 65 /* EXYNOS4_GPX2(1) */
|
|
+
|
|
+#define GTP_GPIO_AS_INPUT(pin) (gpio_direction_input(pin))
|
|
+#define GTP_GPIO_AS_INT(pin) (GTP_GPIO_AS_INPUT(pin))
|
|
+#define GTP_GPIO_GET_VALUE(pin) gpio_get_value(pin)
|
|
+#define GTP_GPIO_OUTPUT(pin, level) gpio_direction_output(pin, level)
|
|
+#define GTP_GPIO_REQUEST(pin, label) gpio_request(pin, label)
|
|
+#define GTP_GPIO_FREE(pin) gpio_free(pin)
|
|
+
|
|
+/* STEP_3(optional): Specify your special config info if needed */
|
|
+#define GTP_DEFAULT_MAX_X 720 /* default coordinate max values */
|
|
+#define GTP_DEFAULT_MAX_Y 1080
|
|
+#define GTP_DEFAULT_MAX_WIDTH 1024
|
|
+#define GTP_DEFAULT_MAX_PRESSURE 1024
|
|
+#define GTP_DEFAULT_INT_TRIGGER 1 /* 1 rising, 2 falling */
|
|
+#define GTP_MAX_TOUCH_ID 16
|
|
+
|
|
+/* STEP_4(optional): If keys are available and reported as keys,
|
|
+config your key info here */
|
|
+#define GTP_KEY_TAB {KEY_MENU, KEY_HOME, KEY_BACK, KEY_HOMEPAGE, \
|
|
+ KEY_F1, KEY_F2, KEY_F3}
|
|
+
|
|
+/**************************PART3:OTHER define*******************************/
|
|
+#define GTP_DRIVER_VERSION "V2.8.2<2022/09/20>"
|
|
+#define GTP_I2C_NAME "goodix-ts"
|
|
+#define GT91XX_CONFIG_PROC_FILE "gt9xx_config"
|
|
+#define GTP_POLL_TIME 10
|
|
+#define GTP_CONFIG_MIN_LENGTH 186
|
|
+#define GTP_ESD_CHECK_VALUE 0xAA
|
|
+#define RETRY_MAX_TIMES 5
|
|
+#define PEN_TRACK_ID 9
|
|
+#define MASK_BIT_8 0x80
|
|
+#define FAIL 0
|
|
+#define SUCCESS 1
|
|
+
|
|
+/* Registers define */
|
|
+#define GTP_REG_COMMAND 0x8040
|
|
+#define GTP_REG_ESD_CHECK 0x8041
|
|
+#define GTP_REG_COMMAND_CHECK 0x8046
|
|
+#define GTP_REG_CONFIG_DATA 0x8047
|
|
+#define GTP_REG_VERSION 0x8140
|
|
+#define GTP_REG_SENSOR_ID 0x814A
|
|
+#define GTP_REG_DOZE_BUF 0x814B
|
|
+#define GTP_READ_COOR_ADDR 0x814E
|
|
+
|
|
+/* Sleep time define */
|
|
+#define GTP_1_DLY_MS 1
|
|
+#define GTP_2_DLY_MS 2
|
|
+#define GTP_10_DLY_MS 10
|
|
+#define GTP_20_DLY_MS 20
|
|
+#define GTP_50_DLY_MS 50
|
|
+#define GTP_58_DLY_MS 58
|
|
+#define GTP_100_DLY_MS 100
|
|
+#define GTP_500_DLY_MS 500
|
|
+#define GTP_1000_DLY_MS 1000
|
|
+#define GTP_3000_DLY_MS 3000
|
|
+
|
|
+#define RESOLUTION_LOC 3
|
|
+#define TRIGGER_LOC 8
|
|
+
|
|
+#define CFG_GROUP_LEN(p_cfg_grp) (sizeof(p_cfg_grp) / sizeof(p_cfg_grp[0]))
|
|
+/* Log define */
|
|
+#define GTP_DEBUG(fmt, arg...) \
|
|
+do { \
|
|
+ if (GTP_DEBUG_ON) {\
|
|
+ pr_info("<<-GTP-DEBUG->> [%d]"fmt"\n", __LINE__, ##arg);\
|
|
+ } \
|
|
+} while (0)
|
|
+#define GTP_DEBUG_ARRAY(array, num) \
|
|
+do { \
|
|
+ s32 i;\
|
|
+ u8 *a = array;\
|
|
+ if (GTP_DEBUG_ARRAY_ON) {\
|
|
+ pr_warn("<<-GTP-DEBUG-ARRAY->>\n");\
|
|
+ for (i = 0; i < (num); i++) {\
|
|
+ pr_warn("%02x ", (a)[i]);\
|
|
+ if ((i + 1) % 10 == 0) {\
|
|
+ pr_warn("\n");\
|
|
+ } \
|
|
+ } \
|
|
+ pr_warn("\n");\
|
|
+ } \
|
|
+} while (0)
|
|
+#define GTP_DEBUG_FUNC() \
|
|
+do {\
|
|
+ if (GTP_DEBUG_FUNC_ON) {\
|
|
+ pr_warn("<<-GTP-FUNC->> Func:%s@Line:%d\n", \
|
|
+ __func__, __LINE__);\
|
|
+ } \
|
|
+} while (0)
|
|
+#define GTP_SWAP(x, y) \
|
|
+do {\
|
|
+ typeof(x) z = x;\
|
|
+ x = y;\
|
|
+ y = z;\
|
|
+} while (0)
|
|
+
|
|
+/******************************End of Part III********************************/
|
|
+#ifdef CONFIG_OF
|
|
+extern int gtp_parse_dt_cfg(struct device *dev, u8 *cfg, int *cfg_len, u8 sid);
|
|
+#endif
|
|
+
|
|
+extern void gtp_reset_guitar(struct i2c_client *client, s32 ms);
|
|
+extern void gtp_int_sync(struct goodix_ts_data *ts, s32 ms);
|
|
+extern void gtp_esd_on(struct goodix_ts_data *ts);
|
|
+extern void gtp_esd_off(struct goodix_ts_data *ts);
|
|
+extern void gtp_work_control_enable(struct goodix_ts_data *ts, bool enable);
|
|
+
|
|
+extern u16 show_len;
|
|
+extern u16 total_len;
|
|
+extern u8 gup_init_update_proc(struct goodix_ts_data *);
|
|
+extern s32 gup_update_proc(void *dir);
|
|
+extern s32 gup_enter_update_mode(struct i2c_client *client);
|
|
+extern void gup_leave_update_mode(struct i2c_client *client);
|
|
+
|
|
+extern s32 init_wr_node(struct i2c_client *);
|
|
+extern void uninit_wr_node(void);
|
|
+
|
|
+/*********** For gt9xx_update Start *********/
|
|
+extern struct i2c_client *i2c_connect_client;
|
|
+extern void gtp_reset_guitar(struct i2c_client *client, s32 ms);
|
|
+extern void gtp_int_output(struct goodix_ts_data *ts, int level);
|
|
+extern s32 gtp_send_cfg(struct i2c_client *client);
|
|
+extern s32 gtp_get_fw_info(struct i2c_client *, struct goodix_fw_info *fw_info);
|
|
+extern s32 gtp_i2c_read_dbl_check(struct i2c_client *, u16, u8 *, int);
|
|
+extern int gtp_i2c_read(struct i2c_client *, u8 *, int);
|
|
+extern int gtp_i2c_write(struct i2c_client *, u8 *, int);
|
|
+extern s32 gtp_fw_startup(struct i2c_client *client);
|
|
+extern int gtp_ascii_to_array(const u8 *src_buf, int src_len, u8 *dst_buf);
|
|
+/*********** For gt9xx_update End *********/
|
|
+
|
|
+#endif /* _GOODIX_GT9XX_H_ */
|
|
diff --git a/drivers/input/touchscreen/gt9xx_update.c b/drivers/input/touchscreen/gt9xx_update.c
|
|
new file mode 100644
|
|
index 000000000000..111111111111
|
|
--- /dev/null
|
|
+++ b/drivers/input/touchscreen/gt9xx_update.c
|
|
@@ -0,0 +1,2092 @@
|
|
+/*
|
|
+ * Goodix GT9xx touchscreen driver
|
|
+ *
|
|
+ * Copyright (C) 2016 - 2017 Goodix. Ltd.
|
|
+ *
|
|
+ * 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 a reference
|
|
+ * to you, when you are integrating the GOODiX's CTP IC into your system,
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
+ * General Public License for more details.
|
|
+ *
|
|
+ */
|
|
+
|
|
+#include <linux/kthread.h>
|
|
+#include "gt9xx.h"
|
|
+
|
|
+#include <linux/namei.h>
|
|
+#include <linux/mount.h>
|
|
+#include <linux/firmware.h>
|
|
+#include <linux/ctype.h>
|
|
+
|
|
+#define GUP_REG_HW_INFO 0x4220
|
|
+#define GUP_REG_FW_MSG 0x41E4
|
|
+#define GUP_REG_PID_VID 0x8140
|
|
+
|
|
+#define FIRMWARE_NAME_LEN_MAX 256
|
|
+#define GOODIX_FIRMWARE_FILE_NAME "goodix_firmware.bin"
|
|
+#define GOODIX_CONFIG_FILE_NAME "goodix_config.cfg"
|
|
+
|
|
+#define FW_HEAD_LENGTH 14
|
|
+#define FW_SECTION_LENGTH 0x2000 /* 8K */
|
|
+#define FW_DSP_ISP_LENGTH 0x1000 /* 4K */
|
|
+#define FW_DSP_LENGTH 0x1000 /* 4K */
|
|
+#define FW_BOOT_LENGTH 0x800 /* 2K */
|
|
+#define FW_SS51_LENGTH (4 * FW_SECTION_LENGTH) /* 32K */
|
|
+#define FW_BOOT_ISP_LENGTH 0x800 /* 2k */
|
|
+#define FW_GLINK_LENGTH 0x3000 /* 12k */
|
|
+#define FW_GWAKE_LENGTH (4 * FW_SECTION_LENGTH) /* 32k */
|
|
+
|
|
+#define PACK_SIZE 256
|
|
+#define MAX_FRAME_CHECK_TIME 5
|
|
+
|
|
+
|
|
+#define _bRW_MISCTL__SRAM_BANK 0x4048
|
|
+#define _bRW_MISCTL__MEM_CD_EN 0x4049
|
|
+#define _bRW_MISCTL__CACHE_EN 0x404B
|
|
+#define _bRW_MISCTL__TMR0_EN 0x40B0
|
|
+#define _rRW_MISCTL__SWRST_B0_ 0x4180
|
|
+#define _bWO_MISCTL__CPU_SWRST_PULSE 0x4184
|
|
+#define _rRW_MISCTL__BOOTCTL_B0_ 0x4190
|
|
+#define _rRW_MISCTL__BOOT_OPT_B0_ 0x4218
|
|
+#define _rRW_MISCTL__BOOT_CTL_ 0x5094
|
|
+
|
|
+#pragma pack(1)
|
|
+struct st_fw_head {
|
|
+ u8 hw_info[4]; /* hardware info */
|
|
+ u8 pid[8]; /* product id */
|
|
+ u16 vid; /* version id */
|
|
+};
|
|
+#pragma pack()
|
|
+
|
|
+struct st_update_msg {
|
|
+ u8 fw_damaged;
|
|
+ u8 fw_flag;
|
|
+ const u8 *fw_data;
|
|
+ struct file *cfg_file;
|
|
+ struct st_fw_head ic_fw_msg;
|
|
+ u32 fw_total_len;
|
|
+ u32 fw_burned_len;
|
|
+ const struct firmware *fw;
|
|
+} update_msg;
|
|
+
|
|
+struct st_update_msg update_msg;
|
|
+
|
|
+u16 show_len;
|
|
+u16 total_len;
|
|
+
|
|
+static u8 gup_burn_fw_gwake_section(struct i2c_client *client,
|
|
+ u8 *fw_section, u16 start_addr,
|
|
+ u32 len, u8 bank_cmd);
|
|
+
|
|
+static s32 gup_init_panel(struct goodix_ts_data *ts)
|
|
+{
|
|
+ s32 ret = 0;
|
|
+ u8 opr_buf[16];
|
|
+ u8 sensor_id = 0;
|
|
+ u8 drv_cfg_version;
|
|
+ u8 flash_cfg_version;
|
|
+ struct goodix_config_data *cfg = &ts->pdata->config;
|
|
+
|
|
+ if (cfg->length < GTP_CONFIG_MIN_LENGTH) {
|
|
+ dev_err(&ts->client->dev,
|
|
+ "No valid config with sensor_ID(%d) ",
|
|
+ sensor_id);
|
|
+
|
|
+ return -EPERM;
|
|
+ }
|
|
+
|
|
+ ret = gtp_i2c_read_dbl_check(ts->client, GTP_REG_CONFIG_DATA,
|
|
+ &opr_buf[0], 1);
|
|
+ if (ret == SUCCESS) {
|
|
+ dev_dbg(&ts->client->dev,
|
|
+ "CFG_GROUP%d Config Version: %d, IC Config Version: %d",
|
|
+ sensor_id, cfg->data[GTP_ADDR_LENGTH], opr_buf[0]);
|
|
+
|
|
+ flash_cfg_version = opr_buf[0];
|
|
+ drv_cfg_version = cfg->data[GTP_ADDR_LENGTH];
|
|
+
|
|
+ if (flash_cfg_version < 90 &&
|
|
+ flash_cfg_version > drv_cfg_version)
|
|
+ cfg->data[GTP_ADDR_LENGTH] = 0x00;
|
|
+ } else {
|
|
+ dev_err(&ts->client->dev,
|
|
+ "Failed to get ic config version!No config sent!");
|
|
+ return -EPERM;
|
|
+ }
|
|
+
|
|
+ ret = gtp_send_cfg(ts->client);
|
|
+ if (ret < 0)
|
|
+ dev_err(&ts->client->dev, "Send config error.");
|
|
+ else
|
|
+ usleep_range(10000, 11000);
|
|
+
|
|
+ /* restore config vrsion */
|
|
+ cfg->data[GTP_ADDR_LENGTH] = drv_cfg_version;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+
|
|
+static u8 gup_get_ic_msg(struct i2c_client *client, u16 addr, u8 *msg, s32 len)
|
|
+{
|
|
+ s32 i = 0;
|
|
+
|
|
+ msg[0] = (addr >> 8) & 0xff;
|
|
+ msg[1] = addr & 0xff;
|
|
+
|
|
+ for (i = 0; i < 5; i++) {
|
|
+ if (gtp_i2c_read(client, msg, GTP_ADDR_LENGTH + len) > 0)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (i >= 5) {
|
|
+ dev_err(&client->dev,
|
|
+ "Read data from 0x%02x%02x failed!",
|
|
+ msg[0], msg[1]);
|
|
+ return FAIL;
|
|
+ }
|
|
+
|
|
+ return SUCCESS;
|
|
+}
|
|
+
|
|
+static u8 gup_set_ic_msg(struct i2c_client *client, u16 addr, u8 val)
|
|
+{
|
|
+ s32 i = 0;
|
|
+ u8 msg[3];
|
|
+
|
|
+ msg[0] = (addr >> 8) & 0xff;
|
|
+ msg[1] = addr & 0xff;
|
|
+ msg[2] = val;
|
|
+
|
|
+ for (i = 0; i < 5; i++) {
|
|
+ if (gtp_i2c_write(client, msg, GTP_ADDR_LENGTH + 1) > 0)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (i >= 5) {
|
|
+ dev_err(&client->dev,
|
|
+ "Set data to 0x%02x%02x failed!", msg[0], msg[1]);
|
|
+ return FAIL;
|
|
+ }
|
|
+
|
|
+ return SUCCESS;
|
|
+}
|
|
+
|
|
+static u8 gup_get_ic_fw_msg(struct i2c_client *client)
|
|
+{
|
|
+ s32 ret = -1;
|
|
+ u8 retry = 0;
|
|
+ u8 buf[16];
|
|
+ u8 i;
|
|
+
|
|
+ /* step1:get hardware info */
|
|
+ ret = gtp_i2c_read_dbl_check(client, GUP_REG_HW_INFO,
|
|
+ &buf[GTP_ADDR_LENGTH], 4);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&client->dev, "[get_ic_fw_msg]get hw_info failed,exit");
|
|
+ return FAIL;
|
|
+ }
|
|
+
|
|
+ /* buf[2~5]: 00 06 90 00
|
|
+ * hw_info: 00 90 06 00
|
|
+ */
|
|
+ for (i = 0; i < 4; i++) {
|
|
+ update_msg.ic_fw_msg.hw_info[i] = buf[GTP_ADDR_LENGTH + 3 - i];
|
|
+ }
|
|
+
|
|
+ dev_dbg(&client->dev,
|
|
+ "IC Hardware info:%02x%02x%02x%02x",
|
|
+ update_msg.ic_fw_msg.hw_info[0],
|
|
+ update_msg.ic_fw_msg.hw_info[1],
|
|
+ update_msg.ic_fw_msg.hw_info[2],
|
|
+ update_msg.ic_fw_msg.hw_info[3]);
|
|
+ /* step2:get firmware message */
|
|
+ for (retry = 0; retry < 2; retry++) {
|
|
+ ret = gup_get_ic_msg(client, GUP_REG_FW_MSG, buf, 1);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&client->dev, "Read firmware message fail.");
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ update_msg.fw_damaged = buf[GTP_ADDR_LENGTH];
|
|
+ if ((0xBE != update_msg.fw_damaged) && (!retry)) {
|
|
+ dev_info(&client->dev, "The check sum in ic is error.");
|
|
+ dev_info(&client->dev, "The IC will be updated by force.");
|
|
+ continue;
|
|
+ }
|
|
+ break;
|
|
+ }
|
|
+ dev_dbg(&client->dev,
|
|
+ "IC force update flag:0x%x", update_msg.fw_damaged);
|
|
+
|
|
+ /* step3:get pid & vid */
|
|
+ ret = gtp_i2c_read_dbl_check(client, GUP_REG_PID_VID,
|
|
+ &buf[GTP_ADDR_LENGTH], 6);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&client->dev, "[get_ic_fw_msg]get pid & vid failed,exit");
|
|
+ return FAIL;
|
|
+ }
|
|
+
|
|
+ memset(update_msg.ic_fw_msg.pid, 0, sizeof(update_msg.ic_fw_msg.pid));
|
|
+ memcpy(update_msg.ic_fw_msg.pid, &buf[GTP_ADDR_LENGTH], 4);
|
|
+ dev_dbg(&client->dev, "IC Product id:%s", update_msg.ic_fw_msg.pid);
|
|
+
|
|
+ /* GT9XX PID MAPPING */
|
|
+ /*|-----FLASH-----RAM-----|
|
|
+ *|------918------918-----|
|
|
+ *|------968------968-----|
|
|
+ *|------913------913-----|
|
|
+ *|------913P-----913P----|
|
|
+ *|------927------927-----|
|
|
+ *|------927P-----927P----|
|
|
+ *|------9110-----9110----|
|
|
+ *|------9110P----9111----|*/
|
|
+ if (update_msg.ic_fw_msg.pid[0] != 0) {
|
|
+ if (!memcmp(update_msg.ic_fw_msg.pid, "9111", 4)) {
|
|
+ dev_dbg(&client->dev, "IC Mapping Product id:%s",
|
|
+ update_msg.ic_fw_msg.pid);
|
|
+ memcpy(update_msg.ic_fw_msg.pid, "9110P", 5);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ update_msg.ic_fw_msg.vid = buf[GTP_ADDR_LENGTH + 4] +
|
|
+ (buf[GTP_ADDR_LENGTH + 5] << 8);
|
|
+ dev_dbg(&client->dev, "IC version id:%04x", update_msg.ic_fw_msg.vid);
|
|
+
|
|
+ return SUCCESS;
|
|
+}
|
|
+
|
|
+s32 gup_enter_update_mode(struct i2c_client *client)
|
|
+{
|
|
+ s32 ret = -1;
|
|
+ s32 retry = 0;
|
|
+ u8 rd_buf[3];
|
|
+
|
|
+ struct goodix_ts_data *ts = i2c_get_clientdata(client);
|
|
+
|
|
+ /* step1:RST output low last at least 2ms */
|
|
+ if (!gpio_is_valid(ts->pdata->rst_gpio)) {
|
|
+ dev_err(&ts->client->dev, "update failed, no rst pin\n");
|
|
+ return FAIL;
|
|
+ }
|
|
+ gpio_direction_output(ts->pdata->rst_gpio, 0);
|
|
+ usleep_range(2000, 3000);
|
|
+
|
|
+ /* step2:select I2C slave addr,INT:0--0xBA;1--0x28. */
|
|
+ gtp_int_output(ts, client->addr == 0x14);
|
|
+ usleep_range(2000, 3000);
|
|
+
|
|
+ /* step3:RST output high reset guitar */
|
|
+ gpio_direction_output(ts->pdata->rst_gpio, 1);
|
|
+
|
|
+ /* 20121211 modify start */
|
|
+ usleep_range(5000, 6000);
|
|
+ while (retry++ < 200) {
|
|
+ /* step4:Hold ss51 & dsp */
|
|
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C);
|
|
+ if (ret <= 0) {
|
|
+ dev_dbg(&client->dev,
|
|
+ "Hold ss51 & dsp I2C error,retry:%d",
|
|
+ retry);
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ /* step5:Confirm hold */
|
|
+ ret = gup_get_ic_msg(client, _rRW_MISCTL__SWRST_B0_, rd_buf, 1);
|
|
+ if (ret <= 0) {
|
|
+ dev_dbg(&client->dev,
|
|
+ "Hold ss51 & dsp I2C error,retry:%d",
|
|
+ retry);
|
|
+ continue;
|
|
+ }
|
|
+ if (0x0C == rd_buf[GTP_ADDR_LENGTH]) {
|
|
+ dev_dbg(&client->dev, "Hold ss51 & dsp confirm SUCCESS");
|
|
+ break;
|
|
+ }
|
|
+ dev_dbg(&client->dev,
|
|
+ "Hold ss51 & dsp confirm 0x4180 failed,value:%d",
|
|
+ rd_buf[GTP_ADDR_LENGTH]);
|
|
+ }
|
|
+ if (retry >= 200) {
|
|
+ dev_err(&client->dev, "Enter update Hold ss51 failed.");
|
|
+ return FAIL;
|
|
+ }
|
|
+
|
|
+ /* step6:DSP_CK and DSP_ALU_CK PowerOn */
|
|
+ ret = gup_set_ic_msg(client, 0x4010, 0x00);
|
|
+
|
|
+ /* 20121211 modify end */
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+void gup_leave_update_mode(struct i2c_client *client)
|
|
+{
|
|
+ struct goodix_ts_data *ts = i2c_get_clientdata(client);
|
|
+
|
|
+ if (ts->pdata->int_sync && ts->pinctrl.pinctrl)
|
|
+ pinctrl_select_state(ts->pinctrl.pinctrl,
|
|
+ ts->pinctrl.int_input);
|
|
+ else if (ts->pdata->int_sync && gpio_is_valid(ts->pdata->irq_gpio))
|
|
+ gpio_direction_input(ts->pdata->irq_gpio);
|
|
+ dev_dbg(&client->dev, "[leave_update_mode]reset chip.");
|
|
+ gtp_reset_guitar(i2c_connect_client, 20);
|
|
+}
|
|
+
|
|
+static u8 gup_enter_update_judge(struct i2c_client *client,
|
|
+ struct st_fw_head *fw_head)
|
|
+{
|
|
+ u16 u16_tmp;
|
|
+ s32 i = 0;
|
|
+ u32 fw_len = 0;
|
|
+ s32 pid_cmp_len = 0;
|
|
+
|
|
+ u16_tmp = fw_head->vid;
|
|
+ fw_head->vid = (u16)(u16_tmp>>8) + (u16)(u16_tmp<<8);
|
|
+
|
|
+ dev_info(&client->dev, "FILE HARDWARE INFO:%*ph\n", 4,
|
|
+ &fw_head->hw_info[0]);
|
|
+ dev_info(&client->dev, "FILE PID:%s\n", fw_head->pid);
|
|
+ dev_info(&client->dev, "FILE VID:%04x\n", fw_head->vid);
|
|
+
|
|
+ dev_info(&client->dev, "IC HARDWARE INFO:%*ph\n", 4,
|
|
+ &update_msg.ic_fw_msg.hw_info[0]);
|
|
+ dev_info(&client->dev, "IC PID:%s\n", update_msg.ic_fw_msg.pid);
|
|
+ dev_info(&client->dev, "IC VID:%04x\n", update_msg.ic_fw_msg.vid);
|
|
+
|
|
+ if (!memcmp(fw_head->pid, "9158", 4) &&
|
|
+ !memcmp(update_msg.ic_fw_msg.pid, "915S", 4)) {
|
|
+ dev_info(&client->dev, "Update GT915S to GT9158 directly!");
|
|
+ return SUCCESS;
|
|
+ }
|
|
+ /* First two conditions */
|
|
+ if (!memcmp(fw_head->hw_info, update_msg.ic_fw_msg.hw_info,
|
|
+ sizeof(update_msg.ic_fw_msg.hw_info))) {
|
|
+ fw_len = 42 * 1024;
|
|
+ } else {
|
|
+ fw_len = fw_head->hw_info[3];
|
|
+ fw_len += (((u32)fw_head->hw_info[2]) << 8);
|
|
+ fw_len += (((u32)fw_head->hw_info[1]) << 16);
|
|
+ fw_len += (((u32)fw_head->hw_info[0]) << 24);
|
|
+ }
|
|
+ if (update_msg.fw_total_len != fw_len) {
|
|
+ dev_err(&client->dev,
|
|
+ "Inconsistent firmware size, Update aborted!");
|
|
+ dev_err(&client->dev,
|
|
+ " Default size: %d(%dK), actual size: %d(%dK)",
|
|
+ fw_len, fw_len/1024, update_msg.fw_total_len,
|
|
+ update_msg.fw_total_len/1024);
|
|
+ return FAIL;
|
|
+ }
|
|
+ dev_info(&client->dev, "Firmware length:%d(%dK)",
|
|
+ update_msg.fw_total_len,
|
|
+ update_msg.fw_total_len/1024);
|
|
+
|
|
+ if (update_msg.fw_damaged != 0xBE) {
|
|
+ dev_info(&client->dev, "FW chksum error,need enter update.");
|
|
+ return SUCCESS;
|
|
+ }
|
|
+
|
|
+ /* 20130523 start */
|
|
+ if (strlen(update_msg.ic_fw_msg.pid) < 3) {
|
|
+ dev_info(&client->dev, "Illegal IC pid, need enter update");
|
|
+ return SUCCESS;
|
|
+ }
|
|
+
|
|
+ /* check pid legality */
|
|
+ for (i = 0; i < 3; i++) {
|
|
+ if (!isdigit(update_msg.ic_fw_msg.pid[i])) {
|
|
+ dev_info(&client->dev,
|
|
+ "Illegal IC pid, need enter update");
|
|
+ return SUCCESS;
|
|
+ }
|
|
+ }
|
|
+ /* 20130523 end */
|
|
+
|
|
+ pid_cmp_len = strlen(fw_head->pid);
|
|
+ if (pid_cmp_len < strlen(update_msg.ic_fw_msg.pid))
|
|
+ pid_cmp_len = strlen(update_msg.ic_fw_msg.pid);
|
|
+
|
|
+ if ((!memcmp(fw_head->pid, update_msg.ic_fw_msg.pid, pid_cmp_len)) ||
|
|
+ (!memcmp(update_msg.ic_fw_msg.pid, "91XX", 4)) ||
|
|
+ (!memcmp(fw_head->pid, "91XX", 4))) {
|
|
+ if (!memcmp(fw_head->pid, "91XX", 4))
|
|
+ dev_dbg(&client->dev,
|
|
+ "Force none same pid update mode.");
|
|
+ else
|
|
+ dev_dbg(&client->dev, "Get the same pid.");
|
|
+
|
|
+ /* The third condition */
|
|
+ if (fw_head->vid != update_msg.ic_fw_msg.vid) {
|
|
+ dev_info(&client->dev, "Need enter update.");
|
|
+ return SUCCESS;
|
|
+ }
|
|
+ dev_err(&client->dev, "File VID == Ic VID, update aborted!");
|
|
+ } else {
|
|
+ dev_err(&client->dev, "File PID != Ic PID, update aborted!");
|
|
+ }
|
|
+
|
|
+ return FAIL;
|
|
+}
|
|
+
|
|
+static int gup_update_config(struct i2c_client *client)
|
|
+{
|
|
+ s32 ret = 0;
|
|
+ s32 i = 0;
|
|
+ s32 file_cfg_len = 0;
|
|
+ u8 *file_config;
|
|
+ const struct firmware *fw_cfg;
|
|
+
|
|
+ struct goodix_ts_data *ts = i2c_get_clientdata(client);
|
|
+
|
|
+ ret = request_firmware(&fw_cfg, GOODIX_CONFIG_FILE_NAME,
|
|
+ &client->dev);
|
|
+ if (ret < 0) {
|
|
+ dev_err(&client->dev,
|
|
+ "Cannot get config file - %s (%d)\n",
|
|
+ GOODIX_CONFIG_FILE_NAME, ret);
|
|
+ return -EFAULT;
|
|
+ }
|
|
+ if (!fw_cfg || !fw_cfg->data || fw_cfg->size > PAGE_SIZE) {
|
|
+ dev_err(&client->dev, "config file illegal");
|
|
+ ret = -EFAULT;
|
|
+ goto cfg_fw_err;
|
|
+ }
|
|
+
|
|
+ dev_dbg(&client->dev, "config firmware file len:%zu", fw_cfg->size);
|
|
+
|
|
+ file_config = kzalloc(GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH,
|
|
+ GFP_KERNEL);
|
|
+ if (!file_config) {
|
|
+ dev_err(&ts->client->dev, "failed alloc memory");
|
|
+ ret = -ENOMEM;
|
|
+ goto cfg_fw_err;
|
|
+ }
|
|
+ file_config[0] = GTP_REG_CONFIG_DATA >> 8;
|
|
+ file_config[1] = GTP_REG_CONFIG_DATA & 0xff;
|
|
+ file_cfg_len = gtp_ascii_to_array(fw_cfg->data, fw_cfg->size,
|
|
+ &file_config[GTP_ADDR_LENGTH]);
|
|
+ if (file_cfg_len < 0) {
|
|
+ dev_err(&client->dev, "failed covert ascii to hex");
|
|
+ ret = -EFAULT;
|
|
+ goto update_cfg_file_failed;
|
|
+ }
|
|
+
|
|
+ GTP_DEBUG_ARRAY(file_config + GTP_ADDR_LENGTH, file_cfg_len);
|
|
+
|
|
+ i = 0;
|
|
+ while (i++ < 5) {
|
|
+ ret = gtp_i2c_write(client, file_config, file_cfg_len + 2);
|
|
+ if (ret > 0) {
|
|
+ dev_info(&client->dev, "Send config SUCCESS.");
|
|
+ msleep(500);
|
|
+ break;
|
|
+ }
|
|
+ dev_err(&ts->client->dev, "Send config i2c error.");
|
|
+ }
|
|
+
|
|
+update_cfg_file_failed:
|
|
+ kfree(file_config);
|
|
+cfg_fw_err:
|
|
+ release_firmware(fw_cfg);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static u8 gup_check_firmware_name(struct i2c_client *client,
|
|
+ u8 **path_p)
|
|
+{
|
|
+ u8 len;
|
|
+ u8 *fname;
|
|
+
|
|
+ if (!(*path_p)) {
|
|
+ *path_p = GOODIX_FIRMWARE_FILE_NAME;
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ len = strnlen(*path_p, FIRMWARE_NAME_LEN_MAX);
|
|
+ if (len >= FIRMWARE_NAME_LEN_MAX) {
|
|
+ dev_err(&client->dev, "firmware name too long!");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ fname = strrchr(*path_p, '/');
|
|
+ if (fname) {
|
|
+ fname = fname + 1;
|
|
+ *path_p = fname;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static u8 gup_get_update_file(struct i2c_client *client,
|
|
+ struct st_fw_head *fw_head, u8 *path)
|
|
+{
|
|
+ s32 ret = 0;
|
|
+ s32 i = 0;
|
|
+ s32 fw_checksum = 0;
|
|
+ struct goodix_ts_data *ts = i2c_get_clientdata(client);
|
|
+
|
|
+ if (ts->pdata->auto_update_cfg) {
|
|
+ ret = gup_update_config(client);
|
|
+ if (ret <= 0)
|
|
+ dev_err(&client->dev, "Update config failed.");
|
|
+ }
|
|
+
|
|
+ ret = gup_check_firmware_name(client, &path);
|
|
+ if (ret < 0)
|
|
+ return FAIL;
|
|
+
|
|
+ ret = request_firmware(&update_msg.fw, path, &client->dev);
|
|
+ if (ret < 0) {
|
|
+ dev_err(&client->dev, "Failed get firmware:%d\n", ret);
|
|
+ return FAIL;
|
|
+ }
|
|
+
|
|
+ dev_info(&client->dev, "FW File: %s size=%zu",
|
|
+ path, update_msg.fw->size);
|
|
+ update_msg.fw_data = update_msg.fw->data;
|
|
+ update_msg.fw_total_len = update_msg.fw->size;
|
|
+
|
|
+ if (update_msg.fw_total_len <
|
|
+ FW_HEAD_LENGTH + FW_SECTION_LENGTH * 4 + FW_DSP_ISP_LENGTH +
|
|
+ FW_DSP_LENGTH + FW_BOOT_LENGTH) {
|
|
+ dev_err(&client->dev,
|
|
+ "INVALID bin file(size: %d), update aborted.",
|
|
+ update_msg.fw_total_len);
|
|
+ goto invalied_fw;
|
|
+ }
|
|
+
|
|
+ update_msg.fw_total_len -= FW_HEAD_LENGTH;
|
|
+
|
|
+ dev_dbg(&client->dev, "Bin firmware actual size: %d(%dK)",
|
|
+ update_msg.fw_total_len, update_msg.fw_total_len/1024);
|
|
+
|
|
+ memcpy(fw_head, update_msg.fw_data, FW_HEAD_LENGTH);
|
|
+
|
|
+ /* check firmware legality */
|
|
+ fw_checksum = 0;
|
|
+ for (i = 0; i < update_msg.fw_total_len; i += 2) {
|
|
+ u16 temp;
|
|
+
|
|
+ temp = (update_msg.fw_data[FW_HEAD_LENGTH + i] << 8) +
|
|
+ update_msg.fw_data[FW_HEAD_LENGTH + i + 1];
|
|
+ fw_checksum += temp;
|
|
+ }
|
|
+
|
|
+ dev_dbg(&client->dev, "firmware checksum:%x", fw_checksum&0xFFFF);
|
|
+ if (fw_checksum & 0xFFFF) {
|
|
+ dev_err(&client->dev, "Illegal firmware file.");
|
|
+ goto invalied_fw;
|
|
+ }
|
|
+
|
|
+ return SUCCESS;
|
|
+
|
|
+invalied_fw:
|
|
+ update_msg.fw_data = NULL;
|
|
+ update_msg.fw_total_len = 0;
|
|
+ release_firmware(update_msg.fw);
|
|
+ return FAIL;
|
|
+}
|
|
+
|
|
+static u8 gup_burn_proc(struct i2c_client *client, u8 *burn_buf,
|
|
+ u16 start_addr, u16 total_length)
|
|
+{
|
|
+ s32 ret = 0;
|
|
+ u16 burn_addr = start_addr;
|
|
+ u16 frame_length = 0;
|
|
+ u16 burn_length = 0;
|
|
+ u8 wr_buf[PACK_SIZE + GTP_ADDR_LENGTH];
|
|
+ u8 rd_buf[PACK_SIZE + GTP_ADDR_LENGTH];
|
|
+ u8 retry = 0;
|
|
+
|
|
+ dev_dbg(&client->dev, "Begin burn %dk data to addr 0x%x",
|
|
+ total_length / 1024, start_addr);
|
|
+ while (burn_length < total_length) {
|
|
+ dev_dbg(&client->dev,
|
|
+ "B/T:%04d/%04d", burn_length, total_length);
|
|
+ frame_length = ((total_length - burn_length)
|
|
+ > PACK_SIZE) ? PACK_SIZE : (total_length - burn_length);
|
|
+ wr_buf[0] = (u8)(burn_addr>>8);
|
|
+ rd_buf[0] = wr_buf[0];
|
|
+ wr_buf[1] = (u8)burn_addr;
|
|
+ rd_buf[1] = wr_buf[1];
|
|
+ memcpy(&wr_buf[GTP_ADDR_LENGTH],
|
|
+ &burn_buf[burn_length], frame_length);
|
|
+
|
|
+ for (retry = 0; retry < MAX_FRAME_CHECK_TIME; retry++) {
|
|
+ ret = gtp_i2c_write(client,
|
|
+ wr_buf, GTP_ADDR_LENGTH + frame_length);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev,
|
|
+ "Write frame data i2c error.");
|
|
+ continue;
|
|
+ }
|
|
+ ret = gtp_i2c_read(client, rd_buf,
|
|
+ GTP_ADDR_LENGTH + frame_length);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev,
|
|
+ "Read back frame data i2c error.");
|
|
+ continue;
|
|
+ }
|
|
+ if (memcmp(&wr_buf[GTP_ADDR_LENGTH],
|
|
+ &rd_buf[GTP_ADDR_LENGTH], frame_length)) {
|
|
+ dev_err(&client->dev,
|
|
+ "Check frame data fail,not equal.");
|
|
+ dev_dbg(&client->dev, "write array:");
|
|
+ GTP_DEBUG_ARRAY(&wr_buf[GTP_ADDR_LENGTH],
|
|
+ frame_length);
|
|
+ dev_dbg(&client->dev, "read array:");
|
|
+ GTP_DEBUG_ARRAY(&rd_buf[GTP_ADDR_LENGTH],
|
|
+ frame_length);
|
|
+ continue;
|
|
+ } else {
|
|
+ /* dev_dbg(&client->dev,
|
|
+ * "Check frame data success.");
|
|
+ */
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if (retry >= MAX_FRAME_CHECK_TIME) {
|
|
+ dev_err(&client->dev,
|
|
+ "Burn frame data time out,exit.");
|
|
+ return FAIL;
|
|
+ }
|
|
+ burn_length += frame_length;
|
|
+ burn_addr += frame_length;
|
|
+ }
|
|
+
|
|
+ return SUCCESS;
|
|
+}
|
|
+
|
|
+static u8 gup_load_section_file(u8 *buf, u32 offset, u16 length, u8 set_or_end)
|
|
+{
|
|
+ if (!update_msg.fw_data ||
|
|
+ update_msg.fw_total_len < offset + length) {
|
|
+ dev_err(&i2c_connect_client->dev,
|
|
+ "cannot load section data. fw_len=%d read end=%d\n",
|
|
+ update_msg.fw_total_len,
|
|
+ FW_HEAD_LENGTH + offset + length);
|
|
+ return FAIL;
|
|
+ }
|
|
+
|
|
+ if (SEEK_SET == set_or_end) {
|
|
+ memcpy(buf, &update_msg.fw_data[FW_HEAD_LENGTH + offset],
|
|
+ length);
|
|
+ } else {
|
|
+ /* seek end */
|
|
+ memcpy(buf, &update_msg.fw_data[update_msg.fw_total_len +
|
|
+ FW_HEAD_LENGTH - offset], length);
|
|
+ }
|
|
+
|
|
+ return SUCCESS;
|
|
+}
|
|
+
|
|
+static u8 gup_recall_check(struct i2c_client *client, u8 *chk_src,
|
|
+ u16 start_rd_addr, u16 chk_length)
|
|
+{
|
|
+ u8 rd_buf[PACK_SIZE + GTP_ADDR_LENGTH];
|
|
+ s32 ret = 0;
|
|
+ u16 recall_addr = start_rd_addr;
|
|
+ u16 recall_length = 0;
|
|
+ u16 frame_length = 0;
|
|
+
|
|
+ while (recall_length < chk_length) {
|
|
+ frame_length = ((chk_length - recall_length)
|
|
+ > PACK_SIZE) ? PACK_SIZE :
|
|
+ (chk_length - recall_length);
|
|
+ ret = gup_get_ic_msg(client, recall_addr, rd_buf, frame_length);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev, "recall i2c error,exit");
|
|
+ return FAIL;
|
|
+ }
|
|
+
|
|
+ if (memcmp(&rd_buf[GTP_ADDR_LENGTH],
|
|
+ &chk_src[recall_length], frame_length)) {
|
|
+ dev_err(&client->dev, "Recall frame data fail,not equal.");
|
|
+ dev_dbg(&client->dev, "chk_src array:");
|
|
+ GTP_DEBUG_ARRAY(&chk_src[recall_length], frame_length);
|
|
+ dev_dbg(&client->dev, "recall array:");
|
|
+ GTP_DEBUG_ARRAY(&rd_buf[GTP_ADDR_LENGTH], frame_length);
|
|
+ return FAIL;
|
|
+ }
|
|
+
|
|
+ recall_length += frame_length;
|
|
+ recall_addr += frame_length;
|
|
+ }
|
|
+ dev_dbg(&client->dev,
|
|
+ "Recall check %dk firmware success.",
|
|
+ (chk_length/1024));
|
|
+
|
|
+ return SUCCESS;
|
|
+}
|
|
+
|
|
+static u8 gup_burn_fw_section(struct i2c_client *client, u8 *fw_section,
|
|
+ u16 start_addr, u8 bank_cmd)
|
|
+{
|
|
+ s32 ret = 0;
|
|
+ u8 rd_buf[5];
|
|
+
|
|
+ /* step1:hold ss51 & dsp */
|
|
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev, "[burn_fw_section]hold ss51 & dsp fail.");
|
|
+ return FAIL;
|
|
+ }
|
|
+
|
|
+ /* step2:set scramble */
|
|
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev, "[burn_fw_section]set scramble fail.");
|
|
+ return FAIL;
|
|
+ }
|
|
+
|
|
+ /* step3:select bank */
|
|
+ ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK,
|
|
+ (bank_cmd >> 4)&0x0F);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_section]select bank %d fail.",
|
|
+ (bank_cmd >> 4)&0x0F);
|
|
+ return FAIL;
|
|
+ }
|
|
+
|
|
+ /* step4:enable accessing code */
|
|
+ ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x01);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_section]enable accessing code fail.");
|
|
+ return FAIL;
|
|
+ }
|
|
+
|
|
+ /* step5:burn 8k fw section */
|
|
+ ret = gup_burn_proc(client, fw_section, start_addr, FW_SECTION_LENGTH);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_section]burn fw_section fail.");
|
|
+ return FAIL;
|
|
+ }
|
|
+
|
|
+ /* step6:hold ss51 & release dsp */
|
|
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x04);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_section]hold ss51 & release dsp fail.");
|
|
+ return FAIL;
|
|
+ }
|
|
+ /* must delay */
|
|
+ usleep_range(1000, 2000);
|
|
+
|
|
+ /* step7:send burn cmd to move data to flash from sram */
|
|
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, bank_cmd&0x0f);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev, "[burn_fw_section]send burn cmd fail.");
|
|
+ return FAIL;
|
|
+ }
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_section]Wait for the burn is complete......");
|
|
+ do {
|
|
+ ret = gup_get_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, rd_buf, 1);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_section]Get burn state fail");
|
|
+ return FAIL;
|
|
+ }
|
|
+ usleep_range(10000, 11000);
|
|
+ /* dev_dbg(&client->dev, "[burn_fw_section]Get burn state:%d.",
|
|
+ * rd_buf[GTP_ADDR_LENGTH]);
|
|
+ */
|
|
+ } while (rd_buf[GTP_ADDR_LENGTH]);
|
|
+
|
|
+ /* step8:select bank */
|
|
+ ret = gup_set_ic_msg(client,
|
|
+ _bRW_MISCTL__SRAM_BANK, (bank_cmd >> 4) & 0x0F);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_section]select bank %d fail.",
|
|
+ (bank_cmd >> 4)&0x0F);
|
|
+ return FAIL;
|
|
+ }
|
|
+
|
|
+ /* step9:enable accessing code */
|
|
+ ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x01);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_section]enable accessing code fail.");
|
|
+ return FAIL;
|
|
+ }
|
|
+
|
|
+ /* step10:recall 8k fw section */
|
|
+ ret = gup_recall_check(client,
|
|
+ fw_section, start_addr, FW_SECTION_LENGTH);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_section]recall check %dk firmware fail.",
|
|
+ FW_SECTION_LENGTH / 1024);
|
|
+ return FAIL;
|
|
+ }
|
|
+
|
|
+ /* step11:disable accessing code */
|
|
+ ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x00);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_section]disable accessing code fail.");
|
|
+ return FAIL;
|
|
+ }
|
|
+
|
|
+ return SUCCESS;
|
|
+}
|
|
+
|
|
+static u8 gup_burn_dsp_isp(struct i2c_client *client)
|
|
+{
|
|
+ s32 ret = 0;
|
|
+ u8 *fw_dsp_isp = NULL;
|
|
+ u8 retry = 0;
|
|
+
|
|
+ dev_info(&client->dev, "[burn_dsp_isp]Begin burn dsp isp---->>");
|
|
+
|
|
+ /* step1:alloc memory */
|
|
+ dev_dbg(&client->dev, "[burn_dsp_isp]step1:alloc memory");
|
|
+ while (retry++ < 5) {
|
|
+ fw_dsp_isp = kzalloc(FW_DSP_ISP_LENGTH, GFP_KERNEL);
|
|
+ if (fw_dsp_isp == NULL) {
|
|
+ continue;
|
|
+ } else {
|
|
+ dev_info(&client->dev,
|
|
+ "[burn_dsp_isp]Alloc %dk byte memory success.",
|
|
+ FW_DSP_ISP_LENGTH / 1024);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if (retry >= 5) {
|
|
+ dev_err(&client->dev, "[burn_dsp_isp]Alloc memory fail,exit.");
|
|
+ return FAIL;
|
|
+ }
|
|
+
|
|
+ /* step2:load dsp isp file data */
|
|
+ dev_dbg(&client->dev, "[burn_dsp_isp]step2:load dsp isp file data");
|
|
+ ret = gup_load_section_file(fw_dsp_isp, FW_DSP_ISP_LENGTH,
|
|
+ FW_DSP_ISP_LENGTH, SEEK_END);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_dsp_isp]load firmware dsp_isp fail.");
|
|
+ goto exit_burn_dsp_isp;
|
|
+ }
|
|
+
|
|
+ /* step3:disable wdt,clear cache enable */
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_dsp_isp]step3:disable wdt,clear cache enable");
|
|
+ ret = gup_set_ic_msg(client, _bRW_MISCTL__TMR0_EN, 0x00);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev, "[burn_dsp_isp]disable wdt fail.");
|
|
+ ret = FAIL;
|
|
+ goto exit_burn_dsp_isp;
|
|
+ }
|
|
+ ret = gup_set_ic_msg(client, _bRW_MISCTL__CACHE_EN, 0x00);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_dsp_isp]clear cache enable fail.");
|
|
+ ret = FAIL;
|
|
+ goto exit_burn_dsp_isp;
|
|
+ }
|
|
+
|
|
+ /* step4:hold ss51 & dsp */
|
|
+ dev_dbg(&client->dev, "[burn_dsp_isp]step4:hold ss51 & dsp");
|
|
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev, "[burn_dsp_isp]hold ss51 & dsp fail.");
|
|
+ ret = FAIL;
|
|
+ goto exit_burn_dsp_isp;
|
|
+ }
|
|
+
|
|
+ /* step5:set boot from sram */
|
|
+ dev_dbg(&client->dev, "[burn_dsp_isp]step5:set boot from sram");
|
|
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOTCTL_B0_, 0x02);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev, "[burn_dsp_isp]set boot from sram fail");
|
|
+ ret = FAIL;
|
|
+ goto exit_burn_dsp_isp;
|
|
+ }
|
|
+
|
|
+ /* step6:software reboot */
|
|
+ dev_dbg(&client->dev, "[burn_dsp_isp]step6:software reboot");
|
|
+ ret = gup_set_ic_msg(client, _bWO_MISCTL__CPU_SWRST_PULSE, 0x01);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev, "[burn_dsp_isp]software reboot fail.");
|
|
+ ret = FAIL;
|
|
+ goto exit_burn_dsp_isp;
|
|
+ }
|
|
+
|
|
+ /* step7:select bank2 */
|
|
+ dev_dbg(&client->dev, "[burn_dsp_isp]step7:select bank2");
|
|
+ ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, 0x02);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev, "[burn_dsp_isp]select bank2 fail");
|
|
+ ret = FAIL;
|
|
+ goto exit_burn_dsp_isp;
|
|
+ }
|
|
+
|
|
+ /* step8:enable accessing code */
|
|
+ dev_dbg(&client->dev, "[burn_dsp_isp]step8:enable accessing code");
|
|
+ ret = gup_set_ic_msg(client, _bRW_MISCTL__MEM_CD_EN, 0x01);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_dsp_isp]enable accessing code fail.");
|
|
+ ret = FAIL;
|
|
+ goto exit_burn_dsp_isp;
|
|
+ }
|
|
+
|
|
+ /* step9:burn 4k dsp_isp */
|
|
+ dev_dbg(&client->dev, "[burn_dsp_isp]step9:burn 4k dsp_isp");
|
|
+ ret = gup_burn_proc(client, fw_dsp_isp, 0xC000, FW_DSP_ISP_LENGTH);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&client->dev, "[burn_dsp_isp]burn dsp_isp fail.");
|
|
+ goto exit_burn_dsp_isp;
|
|
+ }
|
|
+
|
|
+ /* step10:set scramble */
|
|
+ dev_dbg(&client->dev, "[burn_dsp_isp]step10:set scramble");
|
|
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev, "[burn_dsp_isp]set scramble fail.");
|
|
+ ret = FAIL;
|
|
+ goto exit_burn_dsp_isp;
|
|
+ }
|
|
+ update_msg.fw_burned_len += FW_DSP_ISP_LENGTH;
|
|
+ dev_dbg(&client->dev, "[burn_dsp_isp]Burned length:%d",
|
|
+ update_msg.fw_burned_len);
|
|
+ ret = SUCCESS;
|
|
+
|
|
+exit_burn_dsp_isp:
|
|
+ kfree(fw_dsp_isp);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static u8 gup_burn_fw_ss51(struct i2c_client *client)
|
|
+{
|
|
+ u8 *fw_ss51 = NULL;
|
|
+ u8 retry = 0;
|
|
+ s32 ret = 0;
|
|
+
|
|
+ dev_info(&client->dev, "[burn_fw_ss51]Begin burn ss51 firmware---->>");
|
|
+
|
|
+ /* step1:alloc memory */
|
|
+ dev_dbg(&client->dev, "[burn_fw_ss51]step1:alloc memory");
|
|
+ while (retry++ < 5) {
|
|
+ fw_ss51 = kzalloc(FW_SECTION_LENGTH, GFP_KERNEL);
|
|
+ if (fw_ss51 == NULL) {
|
|
+ continue;
|
|
+ } else {
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_ss51]Alloc %dk byte memory success.",
|
|
+ (FW_SECTION_LENGTH / 1024));
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if (retry >= 5) {
|
|
+ dev_err(&client->dev, "[burn_fw_ss51]Alloc memory fail,exit.");
|
|
+ return FAIL;
|
|
+ }
|
|
+
|
|
+ dev_info(&client->dev, "[burn_fw_ss51]Reset first 8K of ss51 to 0xFF.");
|
|
+ dev_dbg(&client->dev, "[burn_fw_ss51]step2: reset bank0 0xC000~0xD000");
|
|
+ memset(fw_ss51, 0xFF, FW_SECTION_LENGTH);
|
|
+
|
|
+ /* step3:clear control flag */
|
|
+ dev_dbg(&client->dev, "[burn_fw_ss51]step3:clear control flag");
|
|
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x00);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev, "[burn_fw_ss51]clear control flag fail.");
|
|
+ ret = FAIL;
|
|
+ goto exit_burn_fw_ss51;
|
|
+ }
|
|
+
|
|
+ /* step4:burn ss51 firmware section 1 */
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_ss51]step4:burn ss51 firmware section 1");
|
|
+ ret = gup_burn_fw_section(client, fw_ss51, 0xC000, 0x01);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_ss51]burn ss51 firmware section 1 fail.");
|
|
+ goto exit_burn_fw_ss51;
|
|
+ }
|
|
+
|
|
+ /* step5:load ss51 firmware section 2 file data */
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_ss51]step5:load ss51 firmware section 2 file data");
|
|
+ ret = gup_load_section_file(fw_ss51, FW_SECTION_LENGTH,
|
|
+ FW_SECTION_LENGTH, SEEK_SET);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_ss51]load ss51 firmware section 2 fail.");
|
|
+ goto exit_burn_fw_ss51;
|
|
+ }
|
|
+
|
|
+ /* step6:burn ss51 firmware section 2 */
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_ss51]step6:burn ss51 firmware section 2");
|
|
+ ret = gup_burn_fw_section(client, fw_ss51, 0xE000, 0x02);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_ss51]burn ss51 firmware section 2 fail.");
|
|
+ goto exit_burn_fw_ss51;
|
|
+ }
|
|
+
|
|
+ /* step7:load ss51 firmware section 3 file data */
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_ss51]step7:load ss51 firmware section 3 file data");
|
|
+ ret = gup_load_section_file(fw_ss51, 2 * FW_SECTION_LENGTH,
|
|
+ FW_SECTION_LENGTH, SEEK_SET);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_ss51]load ss51 firmware section 3 fail.");
|
|
+ goto exit_burn_fw_ss51;
|
|
+ }
|
|
+
|
|
+ /* step8:burn ss51 firmware section 3 */
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_ss51]step8:burn ss51 firmware section 3");
|
|
+ ret = gup_burn_fw_section(client, fw_ss51, 0xC000, 0x13);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_ss51]burn ss51 firmware section 3 fail.");
|
|
+ goto exit_burn_fw_ss51;
|
|
+ }
|
|
+
|
|
+ /* step9:load ss51 firmware section 4 file data */
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_ss51]step9:load ss51 firmware section 4 file data");
|
|
+ ret = gup_load_section_file(fw_ss51, 3 * FW_SECTION_LENGTH,
|
|
+ FW_SECTION_LENGTH, SEEK_SET);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_ss51]load ss51 firmware section 4 fail.");
|
|
+ goto exit_burn_fw_ss51;
|
|
+ }
|
|
+
|
|
+ /* step10:burn ss51 firmware section 4 */
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_ss51]step10:burn ss51 firmware section 4");
|
|
+ ret = gup_burn_fw_section(client, fw_ss51, 0xE000, 0x14);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_ss51]burn ss51 firmware section 4 fail.");
|
|
+ goto exit_burn_fw_ss51;
|
|
+ }
|
|
+
|
|
+ update_msg.fw_burned_len += (FW_SECTION_LENGTH*4);
|
|
+ dev_dbg(&client->dev, "[burn_fw_ss51]Burned length:%d",
|
|
+ update_msg.fw_burned_len);
|
|
+ ret = SUCCESS;
|
|
+
|
|
+exit_burn_fw_ss51:
|
|
+ kfree(fw_ss51);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static u8 gup_burn_fw_dsp(struct i2c_client *client)
|
|
+{
|
|
+ s32 ret = 0;
|
|
+ u8 *fw_dsp = NULL;
|
|
+ u8 retry = 0;
|
|
+ u8 rd_buf[5];
|
|
+
|
|
+ dev_info(&client->dev, "[burn_fw_dsp]Begin burn dsp firmware---->>");
|
|
+ /* step1:alloc memory */
|
|
+ dev_dbg(&client->dev, "[burn_fw_dsp]step1:alloc memory");
|
|
+ while (retry++ < 5) {
|
|
+ fw_dsp = kzalloc(FW_DSP_LENGTH, GFP_KERNEL);
|
|
+ if (fw_dsp == NULL) {
|
|
+ continue;
|
|
+ } else {
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_dsp]Alloc %dk byte memory success.",
|
|
+ FW_SECTION_LENGTH / 1024);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if (retry >= 5) {
|
|
+ dev_err(&client->dev, "[burn_fw_dsp]Alloc memory fail,exit.");
|
|
+ return FAIL;
|
|
+ }
|
|
+
|
|
+ /* step2:load firmware dsp */
|
|
+ dev_dbg(&client->dev, "[burn_fw_dsp]step2:load firmware dsp");
|
|
+ ret = gup_load_section_file(fw_dsp, 4 * FW_SECTION_LENGTH,
|
|
+ FW_DSP_LENGTH, SEEK_SET);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&client->dev, "[burn_fw_dsp]load firmware dsp fail.");
|
|
+ goto exit_burn_fw_dsp;
|
|
+ }
|
|
+
|
|
+ /* step3:select bank3 */
|
|
+ dev_dbg(&client->dev, "[burn_fw_dsp]step3:select bank3");
|
|
+ ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, 0x03);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev, "[burn_fw_dsp]select bank3 fail.");
|
|
+ ret = FAIL;
|
|
+ goto exit_burn_fw_dsp;
|
|
+ }
|
|
+
|
|
+ /* step4:hold ss51 & dsp */
|
|
+ dev_dbg(&client->dev, "[burn_fw_dsp]step4:hold ss51 & dsp");
|
|
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev, "[burn_fw_dsp]hold ss51 & dsp fail.");
|
|
+ ret = FAIL;
|
|
+ goto exit_burn_fw_dsp;
|
|
+ }
|
|
+
|
|
+ /* step5:set scramble */
|
|
+ dev_dbg(&client->dev, "[burn_fw_dsp]step5:set scramble");
|
|
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev, "[burn_fw_dsp]set scramble fail.");
|
|
+ ret = FAIL;
|
|
+ goto exit_burn_fw_dsp;
|
|
+ }
|
|
+
|
|
+ /* step6:release ss51 & dsp */
|
|
+ dev_dbg(&client->dev, "[burn_fw_dsp]step6:release ss51 & dsp");
|
|
+ ret = gup_set_ic_msg(
|
|
+ client, _rRW_MISCTL__SWRST_B0_, 0x04);/* 20121211 */
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev, "[burn_fw_dsp]release ss51 & dsp fail.");
|
|
+ ret = FAIL;
|
|
+ goto exit_burn_fw_dsp;
|
|
+ }
|
|
+ /* must delay */
|
|
+ usleep_range(1000, 1100);
|
|
+
|
|
+ /* step7:burn 4k dsp firmware */
|
|
+ dev_dbg(&client->dev, "[burn_fw_dsp]step7:burn 4k dsp firmware");
|
|
+ ret = gup_burn_proc(client, fw_dsp, 0x9000, FW_DSP_LENGTH);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&client->dev, "[burn_fw_dsp]burn fw_section fail.");
|
|
+ goto exit_burn_fw_dsp;
|
|
+ }
|
|
+
|
|
+ /* step8:send burn cmd to move data to flash from sram */
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_dsp]step8:send burn cmd to move data to flash from sram");
|
|
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x05);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev, "[burn_fw_dsp]send burn cmd fail.");
|
|
+ goto exit_burn_fw_dsp;
|
|
+ }
|
|
+ dev_dbg(&client->dev, "[burn_fw_dsp]Wait for the burn is complete......");
|
|
+ do {
|
|
+ ret = gup_get_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, rd_buf, 1);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev, "[burn_fw_dsp]Get burn state fail");
|
|
+ goto exit_burn_fw_dsp;
|
|
+ }
|
|
+ usleep_range(10000, 11000);
|
|
+ /* dev_dbg(&client->dev, "[burn_fw_dsp]Get burn state:%d.",
|
|
+ rd_buf[GTP_ADDR_LENGTH]); */
|
|
+ } while (rd_buf[GTP_ADDR_LENGTH]);
|
|
+
|
|
+ /* step9:recall check 4k dsp firmware */
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_dsp]step9:recall check 4k dsp firmware");
|
|
+ ret = gup_recall_check(client, fw_dsp, 0x9000, FW_DSP_LENGTH);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_dsp]recall check 4k dsp firmware fail.");
|
|
+ goto exit_burn_fw_dsp;
|
|
+ }
|
|
+
|
|
+ update_msg.fw_burned_len += FW_DSP_LENGTH;
|
|
+ dev_dbg(&client->dev, "[burn_fw_dsp]Burned length:%d",
|
|
+ update_msg.fw_burned_len);
|
|
+ ret = SUCCESS;
|
|
+
|
|
+exit_burn_fw_dsp:
|
|
+ kfree(fw_dsp);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static u8 gup_burn_fw_boot(struct i2c_client *client)
|
|
+{
|
|
+ s32 ret = 0;
|
|
+ u8 *fw_boot = NULL;
|
|
+ u8 retry = 0;
|
|
+ u8 rd_buf[5];
|
|
+
|
|
+ dev_info(&client->dev,
|
|
+ "[burn_fw_boot]Begin burn bootloader firmware---->>");
|
|
+
|
|
+ /* step1:Alloc memory */
|
|
+ dev_dbg(&client->dev, "[burn_fw_boot]step1:Alloc memory");
|
|
+ while (retry++ < 5) {
|
|
+ fw_boot = kzalloc(FW_BOOT_LENGTH, GFP_KERNEL);
|
|
+ if (fw_boot == NULL) {
|
|
+ continue;
|
|
+ } else {
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_boot]Alloc %dk byte memory success.",
|
|
+ FW_BOOT_LENGTH / 1024);
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if (retry >= 5) {
|
|
+ dev_err(&client->dev, "[burn_fw_boot]Alloc memory fail,exit.");
|
|
+ return FAIL;
|
|
+ }
|
|
+
|
|
+ /* step2:load firmware bootloader */
|
|
+ dev_dbg(&client->dev, "[burn_fw_boot]step2:load firmware bootloader");
|
|
+ ret = gup_load_section_file(fw_boot,
|
|
+ 4 * FW_SECTION_LENGTH + FW_DSP_LENGTH,
|
|
+ FW_BOOT_LENGTH, SEEK_SET);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_boot]load firmware bootcode fail.");
|
|
+ goto exit_burn_fw_boot;
|
|
+ }
|
|
+
|
|
+ /* step3:hold ss51 & dsp */
|
|
+ dev_dbg(&client->dev, "[burn_fw_boot]step3:hold ss51 & dsp");
|
|
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev, "[burn_fw_boot]hold ss51 & dsp fail.");
|
|
+ ret = FAIL;
|
|
+ goto exit_burn_fw_boot;
|
|
+ }
|
|
+
|
|
+ /* step4:set scramble */
|
|
+ dev_dbg(&client->dev, "[burn_fw_boot]step4:set scramble");
|
|
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev, "[burn_fw_boot]set scramble fail.");
|
|
+ ret = FAIL;
|
|
+ goto exit_burn_fw_boot;
|
|
+ }
|
|
+
|
|
+ /* step5:hold ss51 & release dsp */
|
|
+ dev_dbg(&client->dev, "[burn_fw_boot]step5:hold ss51 & release dsp");
|
|
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x04);
|
|
+ /* 20121211 */
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev, "[burn_fw_boot]release ss51 & dsp fail");
|
|
+ ret = FAIL;
|
|
+ goto exit_burn_fw_boot;
|
|
+ }
|
|
+ /* must delay */
|
|
+ usleep_range(1000, 1100);
|
|
+
|
|
+ /* step6:select bank3 */
|
|
+ dev_dbg(&client->dev, "[burn_fw_boot]step6:select bank3");
|
|
+ ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, 0x03);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev, "[burn_fw_boot]select bank3 fail.");
|
|
+ ret = FAIL;
|
|
+ goto exit_burn_fw_boot;
|
|
+ }
|
|
+
|
|
+ /* step6:burn 2k bootloader firmware */
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_boot]step6:burn 2k bootloader firmware");
|
|
+ ret = gup_burn_proc(client, fw_boot, 0x9000, FW_BOOT_LENGTH);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&client->dev, "[burn_fw_boot]burn fw_boot fail.");
|
|
+ goto exit_burn_fw_boot;
|
|
+ }
|
|
+
|
|
+ /* step7:send burn cmd to move data to flash from sram */
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_boot]step7:send burn cmd to move data to flash from sram");
|
|
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x06);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev, "[burn_fw_boot]send burn cmd fail.");
|
|
+ goto exit_burn_fw_boot;
|
|
+ }
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_boot]Wait for the burn is complete......");
|
|
+ do {
|
|
+ ret = gup_get_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, rd_buf, 1);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_boot]Get burn state fail");
|
|
+ goto exit_burn_fw_boot;
|
|
+ }
|
|
+ usleep_range(10000, 11000);
|
|
+ /* dev_dbg(&client->dev, "[burn_fw_boot]Get burn state:%d.",
|
|
+ * rd_buf[GTP_ADDR_LENGTH]);
|
|
+ */
|
|
+ } while (rd_buf[GTP_ADDR_LENGTH]);
|
|
+
|
|
+ /* step8:recall check 2k bootloader firmware */
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_boot]step8:recall check 2k bootloader firmware");
|
|
+ ret = gup_recall_check(client, fw_boot, 0x9000, FW_BOOT_LENGTH);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_boot]recall check 2k bootcode firmware fail");
|
|
+ goto exit_burn_fw_boot;
|
|
+ }
|
|
+
|
|
+ update_msg.fw_burned_len += FW_BOOT_LENGTH;
|
|
+ dev_dbg(&client->dev, "[burn_fw_boot]Burned length:%d",
|
|
+ update_msg.fw_burned_len);
|
|
+ ret = SUCCESS;
|
|
+
|
|
+exit_burn_fw_boot:
|
|
+ kfree(fw_boot);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+static u8 gup_burn_fw_boot_isp(struct i2c_client *client)
|
|
+{
|
|
+ s32 ret = 0;
|
|
+ u8 *fw_boot_isp = NULL;
|
|
+ u8 retry = 0;
|
|
+ u8 rd_buf[5];
|
|
+
|
|
+ if (update_msg.fw_burned_len >= update_msg.fw_total_len) {
|
|
+ dev_dbg(&client->dev, "No need to upgrade the boot_isp code!");
|
|
+ return SUCCESS;
|
|
+ }
|
|
+ dev_info(&client->dev,
|
|
+ "[burn_fw_boot_isp]Begin burn boot_isp firmware---->>");
|
|
+
|
|
+ /* step1:Alloc memory */
|
|
+ dev_dbg(&client->dev, "[burn_fw_boot_isp]step1:Alloc memory");
|
|
+ while (retry++ < 5) {
|
|
+ fw_boot_isp = kzalloc(FW_BOOT_ISP_LENGTH, GFP_KERNEL);
|
|
+ if (fw_boot_isp == NULL) {
|
|
+ continue;
|
|
+ } else {
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_boot_isp]Alloc %dk byte memory success.",
|
|
+ (FW_BOOT_ISP_LENGTH/1024));
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if (retry >= 5) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_boot_isp]Alloc memory fail,exit.");
|
|
+ return FAIL;
|
|
+ }
|
|
+
|
|
+ /* step2:load firmware bootloader */
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_boot_isp]step2:load firmware bootloader isp");
|
|
+ /* ret = gup_load_section_file(fw_boot_isp,
|
|
+ * (4*FW_SECTION_LENGTH+FW_DSP_LENGTH +
|
|
+ * FW_BOOT_LENGTH+FW_DSP_ISP_LENGTH), FW_BOOT_ISP_LENGTH, SEEK_SET);
|
|
+ */
|
|
+ ret = gup_load_section_file(fw_boot_isp, (update_msg.fw_burned_len - FW_DSP_ISP_LENGTH),
|
|
+ FW_BOOT_ISP_LENGTH, SEEK_SET);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_boot_isp]load firmware boot_isp fail.");
|
|
+ goto exit_burn_fw_boot_isp;
|
|
+ }
|
|
+
|
|
+ /* step3:hold ss51 & dsp */
|
|
+ dev_dbg(&client->dev, "[burn_fw_boot_isp]step3:hold ss51 & dsp");
|
|
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev, "[burn_fw_boot_isp]hold ss51 & dsp fail");
|
|
+ ret = FAIL;
|
|
+ goto exit_burn_fw_boot_isp;
|
|
+ }
|
|
+
|
|
+ /* step4:set scramble */
|
|
+ dev_dbg(&client->dev, "[burn_fw_boot_isp]step4:set scramble");
|
|
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev, "[burn_fw_boot_isp]set scramble fail.");
|
|
+ ret = FAIL;
|
|
+ goto exit_burn_fw_boot_isp;
|
|
+ }
|
|
+
|
|
+
|
|
+ /* step5:hold ss51 & release dsp */
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_boot_isp]step5:hold ss51 & release dsp");
|
|
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x04);
|
|
+ /* 20121211 */
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_boot_isp]release ss51 & dsp fail.");
|
|
+ ret = FAIL;
|
|
+ goto exit_burn_fw_boot_isp;
|
|
+ }
|
|
+ /* must delay */
|
|
+ usleep_range(1000, 2000);
|
|
+
|
|
+ /* step6:select bank3 */
|
|
+ dev_dbg(&client->dev, "[burn_fw_boot_isp]step6:select bank3");
|
|
+ ret = gup_set_ic_msg(client, _bRW_MISCTL__SRAM_BANK, 0x03);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev, "[burn_fw_boot_isp]select bank3 fail.");
|
|
+ ret = FAIL;
|
|
+ goto exit_burn_fw_boot_isp;
|
|
+ }
|
|
+
|
|
+ /* step7:burn 2k bootload_isp firmware */
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_boot_isp]step7:burn 2k bootloader firmware");
|
|
+ ret = gup_burn_proc(client, fw_boot_isp, 0x9000, FW_BOOT_ISP_LENGTH);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_boot_isp]burn fw_section fail.");
|
|
+ goto exit_burn_fw_boot_isp;
|
|
+ }
|
|
+
|
|
+ /* step7:send burn cmd to move data to flash from sram */
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_boot_isp]step8:send burn cmd to move data to flash from sram");
|
|
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x07);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev, "[burn_fw_boot_isp]send burn cmd fail.");
|
|
+ goto exit_burn_fw_boot_isp;
|
|
+ }
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_boot_isp]Wait for the burn is complete......");
|
|
+ do {
|
|
+ ret = gup_get_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, rd_buf, 1);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_boot_isp]Get burn state fail");
|
|
+ goto exit_burn_fw_boot_isp;
|
|
+ }
|
|
+ usleep_range(10000, 11000);
|
|
+ /* dev_dbg(&client->dev, "[burn_fw_boot_isp]Get
|
|
+ * burn state:%d.", rd_buf[GTP_ADDR_LENGTH]);
|
|
+ */
|
|
+ } while (rd_buf[GTP_ADDR_LENGTH]);
|
|
+
|
|
+ /* step8:recall check 2k bootload_isp firmware */
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_boot_isp]step9:recall check 2k bootloader firmware");
|
|
+ ret = gup_recall_check(client, fw_boot_isp, 0x9000, FW_BOOT_ISP_LENGTH);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_boot_isp]recall check 2k bootcode_isp firmware fail.");
|
|
+ goto exit_burn_fw_boot_isp;
|
|
+ }
|
|
+
|
|
+ update_msg.fw_burned_len += FW_BOOT_ISP_LENGTH;
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_boot_isp]Burned length:%d", update_msg.fw_burned_len);
|
|
+ ret = SUCCESS;
|
|
+
|
|
+exit_burn_fw_boot_isp:
|
|
+ kfree(fw_boot_isp);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static u8 gup_burn_fw_link(struct i2c_client *client)
|
|
+{
|
|
+ u8 *fw_link = NULL;
|
|
+ u8 retry = 0;
|
|
+ s32 ret = 0;
|
|
+ u32 offset;
|
|
+
|
|
+ if (update_msg.fw_burned_len >= update_msg.fw_total_len) {
|
|
+ dev_dbg(&client->dev, "No need to upgrade the link code!");
|
|
+ return SUCCESS;
|
|
+ }
|
|
+ dev_info(&client->dev, "[burn_fw_link]Begin burn link firmware---->>");
|
|
+
|
|
+ /* step1:Alloc memory */
|
|
+ dev_dbg(&client->dev, "[burn_fw_link]step1:Alloc memory");
|
|
+ while (retry++ < 5) {
|
|
+ fw_link = kzalloc(FW_SECTION_LENGTH, GFP_KERNEL);
|
|
+ if (fw_link == NULL) {
|
|
+ continue;
|
|
+ } else {
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_link]Alloc %dk byte memory success.",
|
|
+ (FW_SECTION_LENGTH/1024));
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if (retry >= 5) {
|
|
+ dev_err(&client->dev, "[burn_fw_link]Alloc memory fail,exit.");
|
|
+ return FAIL;
|
|
+ }
|
|
+
|
|
+ /* step2:load firmware link section 1 */
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_link]step2:load firmware link section 1");
|
|
+ offset = update_msg.fw_burned_len - FW_DSP_ISP_LENGTH;
|
|
+ ret = gup_load_section_file(fw_link, offset, FW_SECTION_LENGTH, SEEK_SET);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_link]load firmware link section 1 fail.");
|
|
+ goto exit_burn_fw_link;
|
|
+ }
|
|
+
|
|
+ /* step3:burn link firmware section 1 */
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_link]step3:burn link firmware section 1");
|
|
+ ret = gup_burn_fw_gwake_section(
|
|
+ client, fw_link, 0x9000, FW_SECTION_LENGTH, 0x38);
|
|
+
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_link]burn link firmware section 1 fail.");
|
|
+ goto exit_burn_fw_link;
|
|
+ }
|
|
+
|
|
+ /* step4:load link firmware section 2 file data */
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_link]step4:load link firmware section 2 file data");
|
|
+ offset += FW_SECTION_LENGTH;
|
|
+ ret = gup_load_section_file(fw_link, offset,
|
|
+ FW_GLINK_LENGTH - FW_SECTION_LENGTH, SEEK_SET);
|
|
+
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_link]load link firmware section 2 fail.");
|
|
+ goto exit_burn_fw_link;
|
|
+ }
|
|
+
|
|
+ /* step5:burn link firmware section 2 */
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_link]step4:burn link firmware section 2");
|
|
+ ret = gup_burn_fw_gwake_section(client,
|
|
+ fw_link, 0x9000, FW_GLINK_LENGTH - FW_SECTION_LENGTH, 0x39);
|
|
+
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_link]burn link firmware section 2 fail.");
|
|
+ goto exit_burn_fw_link;
|
|
+ }
|
|
+
|
|
+ update_msg.fw_burned_len += FW_GLINK_LENGTH;
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_link]Burned length:%d", update_msg.fw_burned_len);
|
|
+ ret = SUCCESS;
|
|
+
|
|
+exit_burn_fw_link:
|
|
+ kfree(fw_link);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static u8 gup_burn_fw_gwake_section(struct i2c_client *client,
|
|
+ u8 *fw_section, u16 start_addr, u32 len, u8 bank_cmd)
|
|
+{
|
|
+ s32 ret = 0;
|
|
+ u8 rd_buf[5];
|
|
+
|
|
+ /* step1:hold ss51 & dsp */
|
|
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x0C);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_app_section]hold ss51 & dsp fail.");
|
|
+ return FAIL;
|
|
+ }
|
|
+
|
|
+ /* step2:set scramble */
|
|
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_OPT_B0_, 0x00);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_app_section]set scramble fail.");
|
|
+ return FAIL;
|
|
+ }
|
|
+
|
|
+ /* step3:hold ss51 & release dsp */
|
|
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x04);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_app_section]hold ss51 & release dsp fail.");
|
|
+ return FAIL;
|
|
+ }
|
|
+ /* must delay */
|
|
+ usleep_range(1000, 2000);
|
|
+
|
|
+ /* step4:select bank */
|
|
+ ret = gup_set_ic_msg(
|
|
+ client, _bRW_MISCTL__SRAM_BANK, (bank_cmd >> 4)&0x0F);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_section]select bank %d fail.",
|
|
+ (bank_cmd >> 4)&0x0F);
|
|
+ return FAIL;
|
|
+ }
|
|
+
|
|
+ /* step5:burn fw section */
|
|
+ ret = gup_burn_proc(client, fw_section, start_addr, len);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_app_section]burn fw_section fail.");
|
|
+ return FAIL;
|
|
+ }
|
|
+
|
|
+ /* step6:send burn cmd to move data to flash from sram */
|
|
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, bank_cmd&0x0F);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_app_section]send burn cmd fail.");
|
|
+ return FAIL;
|
|
+ }
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_section]Wait for the burn is complete......");
|
|
+ do {
|
|
+ ret = gup_get_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, rd_buf, 1);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_app_section]Get burn state fail");
|
|
+ return FAIL;
|
|
+ }
|
|
+ usleep_range(10000, 11000);
|
|
+ /* dev_dbg(&client->dev, "[burn_fw_app_section]Get burn state:%d."
|
|
+ * rd_buf[GTP_ADDR_LENGTH]);
|
|
+ */
|
|
+ } while (rd_buf[GTP_ADDR_LENGTH]);
|
|
+
|
|
+ /* step7:recall fw section */
|
|
+ ret = gup_recall_check(client, fw_section, start_addr, len);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_app_section]recall check %dk firmware fail.",
|
|
+ len/1024);
|
|
+ return FAIL;
|
|
+ }
|
|
+
|
|
+ return SUCCESS;
|
|
+}
|
|
+
|
|
+static u8 gup_burn_fw_gwake(struct i2c_client *client)
|
|
+{
|
|
+ u8 *fw_gwake = NULL;
|
|
+ u8 retry = 0;
|
|
+ s32 ret = 0;
|
|
+ u16 start_index = 4*FW_SECTION_LENGTH +
|
|
+ FW_DSP_LENGTH + FW_BOOT_LENGTH +
|
|
+ FW_BOOT_ISP_LENGTH + FW_GLINK_LENGTH;/* 32 + 4 + 2 + 4 = 42K */
|
|
+ /* u16 start_index; */
|
|
+
|
|
+ if (start_index >= update_msg.fw_total_len) {
|
|
+ dev_dbg(&client->dev, "No need to upgrade the gwake code!");
|
|
+ return SUCCESS;
|
|
+ }
|
|
+ /* start_index = update_msg.fw_burned_len - FW_DSP_ISP_LENGTH; */
|
|
+ dev_info(&client->dev,
|
|
+ "[burn_fw_gwake]Begin burn gwake firmware---->>");
|
|
+
|
|
+ /* step1:alloc memory */
|
|
+ dev_dbg(&client->dev, "[burn_fw_gwake]step1:alloc memory");
|
|
+ while (retry++ < 5) {
|
|
+ fw_gwake =
|
|
+ kzalloc(FW_SECTION_LENGTH, GFP_KERNEL);
|
|
+ if (fw_gwake == NULL) {
|
|
+ continue;
|
|
+ } else {
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_gwake]Alloc %dk byte memory success.",
|
|
+ (FW_SECTION_LENGTH/1024));
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if (retry >= 5) {
|
|
+ dev_err(&client->dev, "[burn_fw_gwake]Alloc memory fail,exit.");
|
|
+ return FAIL;
|
|
+ }
|
|
+
|
|
+ /* clear control flag */
|
|
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x00);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_finish]clear control flag fail.");
|
|
+ goto exit_burn_fw_gwake;
|
|
+ }
|
|
+
|
|
+ /* step2:load app_code firmware section 1 file data */
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_gwake]step2:load app_code firmware section 1 file data");
|
|
+ ret = gup_load_section_file(fw_gwake, start_index,
|
|
+ FW_SECTION_LENGTH, SEEK_SET);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_gwake]load app_code firmware section 1 fail.");
|
|
+ goto exit_burn_fw_gwake;
|
|
+ }
|
|
+
|
|
+ /* step3:burn app_code firmware section 1 */
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_gwake]step3:burn app_code firmware section 1");
|
|
+ ret = gup_burn_fw_gwake_section(client,
|
|
+ fw_gwake, 0x9000, FW_SECTION_LENGTH, 0x3A);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_gwake]burn app_code firmware section 1 fail.");
|
|
+ goto exit_burn_fw_gwake;
|
|
+ }
|
|
+
|
|
+ /* step5:load app_code firmware section 2 file data */
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_gwake]step5:load app_code firmware section 2 file data");
|
|
+ ret = gup_load_section_file(fw_gwake, start_index+FW_SECTION_LENGTH,
|
|
+ FW_SECTION_LENGTH, SEEK_SET);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_gwake]load app_code firmware section 2 fail.");
|
|
+ goto exit_burn_fw_gwake;
|
|
+ }
|
|
+
|
|
+ /* step6:burn app_code firmware section 2 */
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_gwake]step6:burn app_code firmware section 2");
|
|
+ ret = gup_burn_fw_gwake_section(client,
|
|
+ fw_gwake, 0x9000, FW_SECTION_LENGTH, 0x3B);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_gwake]burn app_code firmware section 2 fail.");
|
|
+ goto exit_burn_fw_gwake;
|
|
+ }
|
|
+
|
|
+ /* step7:load app_code firmware section 3 file data */
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_gwake]step7:load app_code firmware section 3 file data");
|
|
+ ret = gup_load_section_file(fw_gwake,
|
|
+ start_index + 2 * FW_SECTION_LENGTH,
|
|
+ FW_SECTION_LENGTH, SEEK_SET);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_gwake]load app_code firmware section 3 fail.");
|
|
+ goto exit_burn_fw_gwake;
|
|
+ }
|
|
+
|
|
+ /* step8:burn app_code firmware section 3 */
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_gwake]step8:burn app_code firmware section 3");
|
|
+ ret = gup_burn_fw_gwake_section(
|
|
+ client, fw_gwake, 0x9000, FW_SECTION_LENGTH, 0x3C);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_gwake]burn app_code firmware section 3 fail.");
|
|
+ goto exit_burn_fw_gwake;
|
|
+ }
|
|
+
|
|
+ /* step9:load app_code firmware section 4 file data */
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_gwake]step9:load app_code firmware section 4 file data");
|
|
+ ret = gup_load_section_file(fw_gwake,
|
|
+ start_index + 3 * FW_SECTION_LENGTH,
|
|
+ FW_SECTION_LENGTH, SEEK_SET);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_gwake]load app_code firmware section 4 fail.");
|
|
+ goto exit_burn_fw_gwake;
|
|
+ }
|
|
+
|
|
+ /* step10:burn app_code firmware section 4 */
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_gwake]step10:burn app_code firmware section 4");
|
|
+ ret = gup_burn_fw_gwake_section(
|
|
+ client, fw_gwake, 0x9000, FW_SECTION_LENGTH, 0x3D);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_gwake]burn app_code firmware section 4 fail.");
|
|
+ goto exit_burn_fw_gwake;
|
|
+ }
|
|
+
|
|
+ /* update_msg.fw_burned_len += FW_GWAKE_LENGTH; */
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_gwake]Burned length:%d", update_msg.fw_burned_len);
|
|
+ ret = SUCCESS;
|
|
+
|
|
+exit_burn_fw_gwake:
|
|
+ kfree(fw_gwake);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static u8 gup_burn_fw_finish(struct i2c_client *client)
|
|
+{
|
|
+ u8 *fw_ss51 = NULL;
|
|
+ u8 retry = 0;
|
|
+ s32 ret = 0;
|
|
+
|
|
+ dev_info(&client->dev,
|
|
+ "[burn_fw_finish]burn first 8K of ss51 and finish update.");
|
|
+ /* step1:alloc memory */
|
|
+ dev_dbg(&client->dev, "[burn_fw_finish]step1:alloc memory");
|
|
+ while (retry++ < 5) {
|
|
+ fw_ss51 = kzalloc(FW_SECTION_LENGTH, GFP_KERNEL);
|
|
+ if (fw_ss51 == NULL) {
|
|
+ continue;
|
|
+ } else {
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_finish]Alloc %dk byte memory success.",
|
|
+ (FW_SECTION_LENGTH/1024));
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ if (retry >= 5) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_finish]Alloc memory fail,exit.");
|
|
+ return FAIL;
|
|
+ }
|
|
+
|
|
+ dev_dbg(&client->dev, "[burn_fw_finish]step2: burn ss51 first 8K.");
|
|
+ ret = gup_load_section_file(fw_ss51, 0, FW_SECTION_LENGTH, SEEK_SET);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_finish]load ss51 firmware section 1 fail.");
|
|
+ goto exit_burn_fw_finish;
|
|
+ }
|
|
+
|
|
+ dev_dbg(&client->dev, "[burn_fw_finish]step3:clear control flag");
|
|
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x00);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_finish]clear control flag fail.");
|
|
+ goto exit_burn_fw_finish;
|
|
+ }
|
|
+
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_finish]step4:burn ss51 firmware section 1");
|
|
+ ret = gup_burn_fw_section(client, fw_ss51, 0xC000, 0x01);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_finish]burn ss51 firmware section 1 fail.");
|
|
+ goto exit_burn_fw_finish;
|
|
+ }
|
|
+
|
|
+ /* step11:enable download DSP code */
|
|
+ dev_dbg(&client->dev,
|
|
+ "[burn_fw_finish]step5:enable download DSP code ");
|
|
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__BOOT_CTL_, 0x99);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_finish]enable download DSP code fail.");
|
|
+ goto exit_burn_fw_finish;
|
|
+ }
|
|
+
|
|
+ /* step12:release ss51 & hold dsp */
|
|
+ dev_dbg(&client->dev, "[burn_fw_finish]step6:release ss51 & hold dsp");
|
|
+ ret = gup_set_ic_msg(client, _rRW_MISCTL__SWRST_B0_, 0x08);
|
|
+ if (ret <= 0) {
|
|
+ dev_err(&client->dev,
|
|
+ "[burn_fw_finish]release ss51 & hold dsp fail.");
|
|
+ goto exit_burn_fw_finish;
|
|
+ }
|
|
+
|
|
+ if (fw_ss51 != NULL)
|
|
+ kfree(fw_ss51);
|
|
+ return SUCCESS;
|
|
+
|
|
+exit_burn_fw_finish:
|
|
+ if (fw_ss51 != NULL)
|
|
+ kfree(fw_ss51);
|
|
+
|
|
+ return FAIL;
|
|
+}
|
|
+
|
|
+/* return 0 can update, else no update condition */
|
|
+static int gup_update_condition_check(struct goodix_ts_data *ts)
|
|
+{
|
|
+ if (test_bit(SLEEP_MODE, &ts->flags)) {
|
|
+ dev_info(&ts->client->dev, "Update abort, tp in sleep mode\n");
|
|
+ return -EINVAL;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+s32 gup_update_proc(void *dir)
|
|
+{
|
|
+ s32 ret = 0;
|
|
+ s32 update_ret = FAIL;
|
|
+ u8 retry = 0;
|
|
+ struct st_fw_head fw_head;
|
|
+ struct goodix_ts_data *ts = NULL;
|
|
+
|
|
+ ts = i2c_get_clientdata(i2c_connect_client);
|
|
+
|
|
+ dev_dbg(&ts->client->dev, "[update_proc]Begin update ......\n");
|
|
+
|
|
+ show_len = 1;
|
|
+ total_len = 100;
|
|
+
|
|
+ ret = gup_update_condition_check(ts);
|
|
+ if (ret) {
|
|
+ dev_warn(&ts->client->dev, "Update start failed\n");
|
|
+ return FAIL;
|
|
+ }
|
|
+
|
|
+ if (test_and_set_bit(FW_UPDATE_RUNNING, &ts->flags)) {
|
|
+ dev_warn(&ts->client->dev, "FW update may already running\n");
|
|
+ return FAIL;
|
|
+ }
|
|
+
|
|
+ ret = gup_get_update_file(i2c_connect_client, &fw_head, (u8 *)dir);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&ts->client->dev,
|
|
+ "Failed get valied firmware data\n");
|
|
+ clear_bit(FW_UPDATE_RUNNING, &ts->flags);
|
|
+ return FAIL;
|
|
+ }
|
|
+
|
|
+ gtp_work_control_enable(ts, false);
|
|
+ gtp_esd_off(ts);
|
|
+
|
|
+ ret = gup_get_ic_fw_msg(i2c_connect_client);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&ts->client->dev, "[update_proc]get ic message fail.");
|
|
+ goto file_fail;
|
|
+ }
|
|
+
|
|
+ if (ts->force_update || dir) {
|
|
+ dev_dbg(&ts->client->dev, "Enter force update.");
|
|
+ } else {
|
|
+ ret = gup_enter_update_judge(i2c_connect_client, &fw_head);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&ts->client->dev,
|
|
+ "[update_proc]Doesn't meet update condition\n");
|
|
+ goto file_fail;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ ret = gup_enter_update_mode(ts->client);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&ts->client->dev,
|
|
+ "[update_proc]enter update mode fail.");
|
|
+ goto update_fail;
|
|
+ }
|
|
+
|
|
+ while (retry++ < 5) {
|
|
+ show_len = 10;
|
|
+ total_len = 100;
|
|
+ update_msg.fw_burned_len = 0;
|
|
+ ret = gup_burn_dsp_isp(i2c_connect_client);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&ts->client->dev,
|
|
+ "[update_proc]burn dsp isp fail.");
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ show_len = 20;
|
|
+ ret = gup_burn_fw_gwake(i2c_connect_client);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&ts->client->dev,
|
|
+ "[update_proc]burn app_code firmware fail.");
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ show_len = 30;
|
|
+ ret = gup_burn_fw_ss51(i2c_connect_client);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&ts->client->dev,
|
|
+ "[update_proc]burn ss51 firmware fail.");
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ show_len = 40;
|
|
+ ret = gup_burn_fw_dsp(i2c_connect_client);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&ts->client->dev,
|
|
+ "[update_proc]burn dsp firmware fail.");
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ show_len = 50;
|
|
+ ret = gup_burn_fw_boot(i2c_connect_client);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&ts->client->dev,
|
|
+ "[update_proc]burn bootloader firmware fail.");
|
|
+ continue;
|
|
+ }
|
|
+ show_len = 60;
|
|
+
|
|
+ ret = gup_burn_fw_boot_isp(i2c_connect_client);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&ts->client->dev,
|
|
+ "[update_proc]burn boot_isp firmware fail.");
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ show_len = 70;
|
|
+ ret = gup_burn_fw_link(i2c_connect_client);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&ts->client->dev,
|
|
+ "[update_proc]burn link firmware fail.");
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ show_len = 80;
|
|
+ ret = gup_burn_fw_finish(i2c_connect_client);
|
|
+ if (FAIL == ret) {
|
|
+ dev_err(&ts->client->dev,
|
|
+ "[update_proc]burn finish fail.");
|
|
+ continue;
|
|
+ }
|
|
+ show_len = 90;
|
|
+ dev_info(&ts->client->dev, "[update_proc]UPDATE SUCCESS.");
|
|
+ retry = 0;
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (retry >= 5) {
|
|
+ dev_err(&ts->client->dev,
|
|
+ "[update_proc]retry timeout,UPDATE FAIL.");
|
|
+ update_ret = FAIL;
|
|
+ } else {
|
|
+ update_ret = SUCCESS;
|
|
+ }
|
|
+
|
|
+update_fail:
|
|
+ dev_dbg(&ts->client->dev, "[update_proc]leave update mode.");
|
|
+ gup_leave_update_mode(i2c_connect_client);
|
|
+
|
|
+ msleep(GTP_100_DLY_MS);
|
|
+
|
|
+ if (SUCCESS == update_ret) {
|
|
+ dev_info(&ts->client->dev,
|
|
+ "firmware error auto update, resent config!\n");
|
|
+ gup_init_panel(ts);
|
|
+ }
|
|
+ gtp_get_fw_info(ts->client, &ts->fw_info);
|
|
+
|
|
+file_fail:
|
|
+
|
|
+ update_msg.fw_data = NULL;
|
|
+ update_msg.fw_total_len = 0;
|
|
+ release_firmware(update_msg.fw);
|
|
+
|
|
+ clear_bit(FW_UPDATE_RUNNING, &ts->flags);
|
|
+ gtp_work_control_enable(ts, true);
|
|
+ gtp_esd_on(ts);
|
|
+ total_len = 100;
|
|
+ ts->force_update = false;
|
|
+ if (SUCCESS == update_ret) {
|
|
+ show_len = 100;
|
|
+ clear_bit(FW_ERROR, &ts->flags);
|
|
+ return SUCCESS;
|
|
+ } else {
|
|
+ show_len = 200;
|
|
+ return FAIL;
|
|
+ }
|
|
+}
|
|
+
|
|
+u8 gup_init_update_proc(struct goodix_ts_data *ts)
|
|
+{
|
|
+ struct task_struct *thread = NULL;
|
|
+
|
|
+ dev_info(&ts->client->dev, "Ready to run update thread.");
|
|
+
|
|
+ thread = kthread_run(gup_update_proc,
|
|
+ (void *)NULL, "guitar_update");
|
|
+
|
|
+ if (IS_ERR(thread)) {
|
|
+ dev_err(&ts->client->dev,
|
|
+ "Failed to create update thread.\n");
|
|
+ return -EPERM;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
--
|
|
Armbian
|
|
|