Merge "touch: goodix: fix coding warnings and porting touch fixes"

This commit is contained in:
qctecmdr 2022-05-06 16:15:59 -07:00 committed by Gerrit - the friendly Code Review server
commit 6ef0449e73
17 changed files with 11537 additions and 0 deletions

View File

@ -1343,4 +1343,6 @@ source "drivers/input/touchscreen/synaptics_dsx/Kconfig"
source "drivers/input/touchscreen/nt36xxx/Kconfig"
source "drivers/input/touchscreen/goodix_berlin_driver/Kconfig"
endif

View File

@ -117,3 +117,4 @@ obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_DSX) += synaptics_dsx/
obj-$(CONFIG_TOUCHSCREEN_ZINITIX) += zinitix.o
obj-$(CONFIG_TOUCHSCREEN_NT36XXX_SPI) += nt36xxx/
obj-$(CONFIG_TOUCHSCREEN_NT36XXX_I2C) += nt36xxx/
obj-$(CONFIG_TOUCHSCREEN_GOODIX_BRL) += goodix_berlin_driver/

View File

@ -0,0 +1,20 @@
#
# Goodix touchscreen driver configuration
#
menuconfig TOUCHSCREEN_GOODIX_BRL
tristate "Goodix berlin touchscreen"
help
Say Y here if you have a Goodix berlin series touch controller
to your system.
If build module, say M.
If unsure, say N.
if TOUCHSCREEN_GOODIX_BRL
config TOUCHSCREEN_GOODIX_BRL_SPI
bool "support SPI bus connection"
help
Say Y here if the touchscreen is connected via SPI bus.
endif

View File

@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

View File

@ -0,0 +1,12 @@
obj-$(CONFIG_TOUCHSCREEN_GOODIX_BRL) += goodix_core.o
goodix_core-y := \
goodix_brl_i2c.o \
goodix_brl_spi.o \
goodix_ts_core.o \
goodix_brl_hw.o \
goodix_cfg_bin.o \
goodix_ts_utils.o \
goodix_brl_fwupdate.o \
goodix_ts_gesture.o \
goodix_ts_inspect.o \
goodix_ts_tools.o

View File

@ -0,0 +1,192 @@
# **Berlin Driver Porting Guide**
## **Introduce**
Berlin series driver is currently support BerlinA(GT9897), BerlinB(GT9966, GT7986). And support I2C or SPI connection.
## **Driver source file prepare**
1. Move driver source code to $KERNEL_SRC/drivers/input/touchscree/
2. Change $KERNEL_SRC/drivers/input/touchscree/Makefile
Add this line to the Makefile
```
obj-y += goodix_berlin_driver/
```
3. Change $KERNEL_SRC/drivers/input/touchscree/Kconfg
Add this line to the Kconfig
```
source "drivers/input/touchscreen/goodix_berlin_driver/Kconfig"
```
## **Add device declaration in the board devicetree**
Please add Goodix touch device declaration info in the board devicetree, you can refer the Appendix goodix-ts-i2c-dtsi or goodix-ts-spi-dtsi to see how to set deivce properties.
## **Build driver**
When build kernel you will see the following promt to let you confirm how to build the driver. This driver support built-in kernel or build as modules.
**Setting up in the menu:**
1. In the $KERNEL_SRC directory, exec `make menuconfig`, then select
`Device Drivers ---> Input device support ---> Touchscreens --->`
2. Find `Goodix berlin touchscreen` menu, you can select `<*>`(build in kernel) or `<M>`(build a module).
3. Enter `Goodix berlin touchscreen`, you can see `support SPI bus connection` item. If
you are on SPI connection, select `<*>`, or on I2C connection.
**Setting up in the defconfig file:**
1. Add the following in you defconfig file.
```
CONFIG_TOUCHSCREEN_GOODIX_BRL=y
```
or
```
CONFIG_TOUCHSCREEN_GOODIX_BRL=m
```
2. If you are on SPI connection, add the following.
```
CONFIG_TOUCHSCREEN_GOODIX_BRL_SPI=y
```
## **Appendix**
**goodix-ts-i2c-dtsi**
```dts
devicetree binding for Goodix i2c touchdriver
Required properties:
- compatible: device & driver matching.
* for berlin series touch device, souch as "goodix,gt9897", "goodix,gt9966"
- reg: i2c client address, value can be 0x14 or 0x5d. please refer to datasheet.
- goodix,reset-gpio: reset gpio.
- goodix,irq-gpio: interrupt gpio.
- goodix,irq-flags: irq trigger type config, value should be:
1 - rising edge,
2 - falling edge,
4 - high level,
5 - low level.
- goodix,panel-max-x: max resolution of x direction.
- goodix,panel-max-y: max resolution of y direction.
- goodix,panel-max-w: panel max width value.
- goodix,panel-max-p: pen device max pressure value.
Optional properties:
- goodix,avdd-name: set name of regulator.
- avdd-supply: power supply for the touch device.
example of regulator:
goodix,avdd-name = "avdd";
avdd-supply = <&pm8916_l15>;
- iovdd-supply: power supply for digital io circuit
example of regulator:
goodix,iovdd-name = "iovdd";
iovdd-supply = <&pm8916_l16>;
- goodix,pen-enable: set this property if you want support stylus.
goodix,pen-enable;
- goodix,firmware-name: set firmware file name, if not configured, use the default name.
- goodix,config-name: set config file name, if not configured, use the default name.
Example 1:
goodix-berlin@5d {
compatible = "goodix,gt9897";
reg = <0x5d>;
goodix,reset-gpio = <&msm_gpio 12 0x0>;
goodix,irq-gpio = <&msm_gpio 13 0x2800>;
goodix,irq-flags = <2>; /* 1:trigger rising, 2:trigger falling;*/
goodix,panel-max-x = <720>;
goodix,panel-max-y = <1280>;
goodix,panel-max-w = <255>;
};
Example 2:
goodix-berlin@5d {
compatible = "goodix,gt9966";
goodix,avdd-name = "avdd";
avdd-supply = <&pm8916_l15>;
goodix,iovdd-name = "iovdd";
iovdd-supply = <&pm8916_l16>;
reg = <0x5d>;
goodix,reset-gpio = <&msm_gpio 12 0x0>;
goodix,irq-gpio = <&msm_gpio 13 0x2800>;
goodix,irq-flags = <2>; /* 1:trigger rising, 2:trigger falling;*/
goodix,panel-max-x = <720>;
goodix,panel-max-y = <1280>;
goodix,panel-max-w = <255>;
goodix,panel-max-p = <4096>; /* max pressure that pen device supported */
goodix,pen-enable; /* support active stylus device*/
goodix,firmware-name = "goodix_firmware.bin";
goodix,config-name = "goodix_cfg_group.bin";
};
```
**goodix-ts-spi-dtsi**
```dts
devicetree binding for Goodix spi touchdriver
Required properties:
- compatible: device & driver matching.
* for berlin series touch device, souch as "goodix,gt9897T"
- spi-max-frequency: set spi transfer speed.
- reg: depend on CS gpio.
- goodix,reset-gpio: reset gpio.
- goodix,irq-gpio: interrupt gpio.
- goodix,irq-flags: irq trigger type config, value should be:
1 - rising edge,
2 - falling edge,
4 - high level,
5 - low level.
- goodix,panel-max-x: max resolution of x direction.
- goodix,panel-max-y: max resolution of y direction.
- goodix,panel-max-w: panel max width value.
- goodix,panel-max-p: pen device max pressure value.
Optional properties:
- goodix,avdd-name: set name of regulator.
- avdd-supply: power supply for the touch device.
example of regulator:
goodix,avdd-name = "avdd";
avdd-supply = <&pm8916_l15>;
- iovdd-supply: power supply for digital io circuit
example of regulator:
goodix,iovdd-name = "iovdd";
iovdd-supply = <&pm8916_l16>;
- goodix,pen-enable: set this property if you want support stylus.
goodix,pen-enable;
- goodix,firmware-name: set firmware file name, if not configured, use the default name.
- goodix,config-name: set config file name, if not configured, use the default name.
Example 1:
goodix-berlin@0 {
compatible = "goodix,gt9897";
reg = <0>;
spi-max-frequency = <1000000>;
goodix,reset-gpio = <&msm_gpio 12 0x0>;
goodix,irq-gpio = <&msm_gpio 13 0x2800>;
goodix,irq-flags = <2>; /* 1:trigger rising, 2:trigger falling;*/
goodix,panel-max-x = <720>;
goodix,panel-max-y = <1280>;
goodix,panel-max-w = <255>;
};
Example 2:
goodix-berlin@0 {
compatible = "goodix,gt9966S";
reg = <0>;
spi-max-frequency = <1000000>;
goodix,avdd-name = "avdd";
avdd-supply = <&pm8916_l15>;
goodix,iovdd-name = "iovdd";
iovdd-supply = <&pm8916_l16>;
goodix,reset-gpio = <&msm_gpio 12 0x0>;
goodix,irq-gpio = <&msm_gpio 13 0x2800>;
goodix,irq-flags = <2>; /* 1:trigger rising, 2:trigger falling;*/
goodix,panel-max-x = <720>;
goodix,panel-max-y = <1280>;
goodix,panel-max-w = <255>;
goodix,panel-max-p = <4096>; /* max pressure that pen device supported */
goodix,pen-enable; /* support active stylus device*/
goodix,firmware-name = "goodix_firmware.bin";
goodix,config-name = "goodix_cfg_group.bin";
};
```

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,261 @@
/*
* Goodix Touchscreen Driver
* Copyright (C) 2020 - 2021 Goodix, Inc.
*
* 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/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include "goodix_ts_core.h"
#define TS_DRIVER_NAME "gtx8_i2c"
#define I2C_MAX_TRANSFER_SIZE 256
#define GOODIX_BUS_RETRY_TIMES 2
#define GOODIX_REG_ADDR_SIZE 4
static struct platform_device *goodix_pdev;
struct goodix_bus_interface goodix_i2c_bus;
static int goodix_i2c_read(struct device *dev, unsigned int reg,
unsigned char *data, unsigned int len)
{
struct i2c_client *client = to_i2c_client(dev);
unsigned int transfer_length = 0;
unsigned int pos = 0, address = reg;
unsigned char get_buf[128], addr_buf[GOODIX_REG_ADDR_SIZE];
int retry, r = 0;
struct i2c_msg msgs[] = {
{
.addr = client->addr,
.flags = !I2C_M_RD,
.buf = &addr_buf[0],
.len = GOODIX_REG_ADDR_SIZE,
}, {
.addr = client->addr,
.flags = I2C_M_RD,
}
};
if (likely(len < sizeof(get_buf))) {
/* code optimize, use stack memory */
msgs[1].buf = &get_buf[0];
} else {
msgs[1].buf = kzalloc(len, GFP_KERNEL);
if (msgs[1].buf == NULL)
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 >> 24) & 0xFF;
msgs[0].buf[1] = (address >> 16) & 0xFF;
msgs[0].buf[2] = (address >> 8) & 0xFF;
msgs[0].buf[3] = address & 0xFF;
msgs[1].len = transfer_length;
for (retry = 0; retry < GOODIX_BUS_RETRY_TIMES; retry++) {
if (likely(i2c_transfer(client->adapter, msgs, 2) == 2)) {
memcpy(&data[pos], msgs[1].buf, transfer_length);
pos += transfer_length;
address += transfer_length;
break;
}
ts_info("I2c read retry[%d]:0x%x", retry + 1, reg);
usleep_range(2000, 2100);
}
if (unlikely(retry == GOODIX_BUS_RETRY_TIMES)) {
ts_err("I2c read failed,dev:%02x,reg:%04x,size:%u",
client->addr, reg, len);
r = -EAGAIN;
goto read_exit;
}
}
read_exit:
if (unlikely(len >= sizeof(get_buf)))
kfree(msgs[1].buf);
return r;
}
static int goodix_i2c_write(struct device *dev, unsigned int reg,
unsigned char *data, unsigned int len)
{
struct i2c_client *client = to_i2c_client(dev);
unsigned int pos = 0, transfer_length = 0;
unsigned int address = reg;
unsigned char put_buf[128];
int retry, r = 0;
struct i2c_msg msg = {
.addr = client->addr,
.flags = !I2C_M_RD,
};
if (likely(len + GOODIX_REG_ADDR_SIZE < sizeof(put_buf))) {
/* code optimize,use stack memory*/
msg.buf = &put_buf[0];
} else {
msg.buf = kmalloc(len + GOODIX_REG_ADDR_SIZE, GFP_KERNEL);
if (msg.buf == NULL)
return -ENOMEM;
}
while (pos != len) {
if (unlikely(len - pos > I2C_MAX_TRANSFER_SIZE -
GOODIX_REG_ADDR_SIZE))
transfer_length = I2C_MAX_TRANSFER_SIZE -
GOODIX_REG_ADDR_SIZE;
else
transfer_length = len - pos;
msg.buf[0] = (address >> 24) & 0xFF;
msg.buf[1] = (address >> 16) & 0xFF;
msg.buf[2] = (address >> 8) & 0xFF;
msg.buf[3] = address & 0xFF;
msg.len = transfer_length + GOODIX_REG_ADDR_SIZE;
memcpy(&msg.buf[GOODIX_REG_ADDR_SIZE],
&data[pos], transfer_length);
for (retry = 0; retry < GOODIX_BUS_RETRY_TIMES; retry++) {
if (likely(i2c_transfer(client->adapter,
&msg, 1) == 1)) {
pos += transfer_length;
address += transfer_length;
break;
}
ts_debug("I2c write retry[%d]", retry + 1);
msleep(20);
}
if (unlikely(retry == GOODIX_BUS_RETRY_TIMES)) {
ts_err("I2c write failed,dev:%02x,reg:%04x,size:%u",
client->addr, reg, len);
r = -EAGAIN;
goto write_exit;
}
}
write_exit:
if (likely(len + GOODIX_REG_ADDR_SIZE >= sizeof(put_buf)))
kfree(msg.buf);
return r;
}
static void goodix_pdev_release(struct device *dev)
{
ts_info("goodix pdev released");
kfree(goodix_pdev);
}
static int goodix_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *dev_id)
{
int ret = 0;
ts_info("goodix i2c probe in");
ret = i2c_check_functionality(client->adapter, I2C_FUNC_I2C);
if (!ret)
return -EIO;
/* get ic type */
ret = goodix_get_ic_type(client->dev.of_node);
if (ret < 0)
return ret;
goodix_i2c_bus.ic_type = ret;
goodix_i2c_bus.bus_type = GOODIX_BUS_TYPE_I2C;
goodix_i2c_bus.dev = &client->dev;
goodix_i2c_bus.read = goodix_i2c_read;
goodix_i2c_bus.write = goodix_i2c_write;
/* ts core device */
goodix_pdev = kzalloc(sizeof(struct platform_device), GFP_KERNEL);
if (!goodix_pdev)
return -ENOMEM;
goodix_pdev->name = GOODIX_CORE_DRIVER_NAME;
goodix_pdev->id = 0;
goodix_pdev->num_resources = 0;
/*
* you can find this platform dev in
* /sys/devices/platform/goodix_ts.0
* goodix_pdev->dev.parent = &client->dev;
*/
goodix_pdev->dev.platform_data = &goodix_i2c_bus;
goodix_pdev->dev.release = goodix_pdev_release;
/* register platform device, then the goodix_ts_core
* module will probe the touch device.
*/
ret = platform_device_register(goodix_pdev);
if (ret) {
ts_err("failed register goodix platform device, %d", ret);
goto err_pdev;
}
ts_info("i2c probe out");
return ret;
err_pdev:
kfree(goodix_pdev);
goodix_pdev = NULL;
ts_info("i2c probe out, %d", ret);
return ret;
}
static int goodix_i2c_remove(struct i2c_client *client)
{
platform_device_unregister(goodix_pdev);
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id i2c_matchs[] = {
{.compatible = "goodix,gt9897",},
{.compatible = "goodix,gt9966",},
{.compatible = "goodix,gt9916",},
{},
};
MODULE_DEVICE_TABLE(of, i2c_matchs);
#endif
static const struct i2c_device_id i2c_id_table[] = {
{TS_DRIVER_NAME, 0},
{},
};
MODULE_DEVICE_TABLE(i2c, i2c_id_table);
static struct i2c_driver goodix_i2c_driver = {
.driver = {
.name = TS_DRIVER_NAME,
//.owner = THIS_MODULE,
.of_match_table = of_match_ptr(i2c_matchs),
},
.probe = goodix_i2c_probe,
.remove = goodix_i2c_remove,
.id_table = i2c_id_table,
};
int goodix_i2c_bus_init(void)
{
ts_info("Goodix i2c driver init");
return i2c_add_driver(&goodix_i2c_driver);
}
void goodix_i2c_bus_exit(void)
{
ts_info("Goodix i2c driver exit");
i2c_del_driver(&goodix_i2c_driver);
}

View File

@ -0,0 +1,298 @@
/*
* Goodix Touchscreen Driver
* Copyright (C) 2020 - 2021 Goodix, Inc.
*
* 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/kernel.h>
#include <linux/module.h>
#include <linux/spi/spi.h>
#include "goodix_ts_core.h"
#define TS_DRIVER_NAME "gtx8_spi"
#define SPI_TRANS_PREFIX_LEN 1
#define REGISTER_WIDTH 4
#define SPI_READ_DUMMY_LEN 4
#define SPI_READ_PREFIX_LEN \
(SPI_TRANS_PREFIX_LEN + REGISTER_WIDTH + SPI_READ_DUMMY_LEN)
#define SPI_WRITE_PREFIX_LEN (SPI_TRANS_PREFIX_LEN + REGISTER_WIDTH)
#define SPI_WRITE_FLAG 0xF0
#define SPI_READ_FLAG 0xF1
static struct platform_device *goodix_pdev;
struct goodix_bus_interface goodix_spi_bus;
/**
* goodix_spi_read_bra- read device register through spi bus
* @dev: pointer to device data
* @addr: register address
* @data: read buffer
* @len: bytes to read
* return: 0 - read ok, < 0 - spi transter error
*/
static int goodix_spi_read_bra(struct device *dev, unsigned int addr,
unsigned char *data, unsigned int len)
{
struct spi_device *spi = to_spi_device(dev);
u8 *rx_buf = NULL;
u8 *tx_buf = NULL;
struct spi_transfer xfers;
struct spi_message spi_msg;
int ret = 0;
rx_buf = kzalloc(SPI_READ_PREFIX_LEN + len, GFP_KERNEL);
tx_buf = kzalloc(SPI_READ_PREFIX_LEN + len, GFP_KERNEL);
if (!rx_buf || !tx_buf) {
ts_err("alloc tx/rx_buf failed, size:%d",
SPI_READ_PREFIX_LEN + len);
return -ENOMEM;
}
spi_message_init(&spi_msg);
memset(&xfers, 0, sizeof(xfers));
/*spi_read tx_buf format: 0xF1 + addr(4bytes) + data*/
tx_buf[0] = SPI_READ_FLAG;
tx_buf[1] = (addr >> 24) & 0xFF;
tx_buf[2] = (addr >> 16) & 0xFF;
tx_buf[3] = (addr >> 8) & 0xFF;
tx_buf[4] = addr & 0xFF;
tx_buf[5] = 0xFF;
tx_buf[6] = 0xFF;
tx_buf[7] = 0xFF;
tx_buf[8] = 0xFF;
xfers.tx_buf = tx_buf;
xfers.rx_buf = rx_buf;
xfers.len = SPI_READ_PREFIX_LEN + len;
xfers.cs_change = 0;
spi_message_add_tail(&xfers, &spi_msg);
ret = spi_sync(spi, &spi_msg);
if (ret < 0) {
ts_err("spi transfer error:%d", ret);
goto exit;
}
memcpy(data, &rx_buf[SPI_READ_PREFIX_LEN], len);
exit:
kfree(rx_buf);
kfree(tx_buf);
return ret;
}
static int goodix_spi_read(struct device *dev, unsigned int addr,
unsigned char *data, unsigned int len)
{
struct spi_device *spi = to_spi_device(dev);
u8 *rx_buf = NULL;
u8 *tx_buf = NULL;
struct spi_transfer xfers;
struct spi_message spi_msg;
int ret = 0;
rx_buf = kzalloc(SPI_READ_PREFIX_LEN - 1 + len, GFP_KERNEL);
tx_buf = kzalloc(SPI_READ_PREFIX_LEN - 1 + len, GFP_KERNEL);
if (!rx_buf || !tx_buf) {
ts_err("alloc tx/rx_buf failed, size:%d",
SPI_READ_PREFIX_LEN - 1 + len);
return -ENOMEM;
}
spi_message_init(&spi_msg);
memset(&xfers, 0, sizeof(xfers));
/*spi_read tx_buf format: 0xF1 + addr(4bytes) + data*/
tx_buf[0] = SPI_READ_FLAG;
tx_buf[1] = (addr >> 24) & 0xFF;
tx_buf[2] = (addr >> 16) & 0xFF;
tx_buf[3] = (addr >> 8) & 0xFF;
tx_buf[4] = addr & 0xFF;
tx_buf[5] = 0xFF;
tx_buf[6] = 0xFF;
tx_buf[7] = 0xFF;
xfers.tx_buf = tx_buf;
xfers.rx_buf = rx_buf;
xfers.len = SPI_READ_PREFIX_LEN - 1 + len;
xfers.cs_change = 0;
spi_message_add_tail(&xfers, &spi_msg);
ret = spi_sync(spi, &spi_msg);
if (ret < 0) {
ts_err("spi transfer error:%d", ret);
goto exit;
}
memcpy(data, &rx_buf[SPI_READ_PREFIX_LEN - 1], len);
exit:
kfree(rx_buf);
kfree(tx_buf);
return ret;
}
/**
* goodix_spi_write- write device register through spi bus
* @dev: pointer to device data
* @addr: register address
* @data: write buffer
* @len: bytes to write
* return: 0 - write ok; < 0 - spi transter error.
*/
static int goodix_spi_write(struct device *dev, unsigned int addr,
unsigned char *data, unsigned int len)
{
struct spi_device *spi = to_spi_device(dev);
u8 *tx_buf = NULL;
struct spi_transfer xfers;
struct spi_message spi_msg;
int ret = 0;
tx_buf = kzalloc(SPI_WRITE_PREFIX_LEN + len, GFP_KERNEL);
if (!tx_buf)
return -ENOMEM;
spi_message_init(&spi_msg);
memset(&xfers, 0, sizeof(xfers));
tx_buf[0] = SPI_WRITE_FLAG;
tx_buf[1] = (addr >> 24) & 0xFF;
tx_buf[2] = (addr >> 16) & 0xFF;
tx_buf[3] = (addr >> 8) & 0xFF;
tx_buf[4] = addr & 0xFF;
memcpy(&tx_buf[SPI_WRITE_PREFIX_LEN], data, len);
xfers.tx_buf = tx_buf;
xfers.len = SPI_WRITE_PREFIX_LEN + len;
xfers.cs_change = 0;
spi_message_add_tail(&xfers, &spi_msg);
ret = spi_sync(spi, &spi_msg);
if (ret < 0)
ts_err("spi transfer error:%d", ret);
kfree(tx_buf);
return ret;
}
static void goodix_pdev_release(struct device *dev)
{
ts_info("goodix pdev released");
kfree(goodix_pdev);
}
static int goodix_spi_probe(struct spi_device *spi)
{
int ret = 0;
ts_info("goodix spi probe in");
/* init spi_device */
spi->mode = SPI_MODE_0;
spi->bits_per_word = 8;
ret = spi_setup(spi);
if (ret) {
ts_err("failed set spi mode, %d", ret);
return ret;
}
/* get ic type */
ret = goodix_get_ic_type(spi->dev.of_node);
if (ret < 0)
return ret;
goodix_spi_bus.ic_type = ret;
goodix_spi_bus.bus_type = GOODIX_BUS_TYPE_SPI;
goodix_spi_bus.dev = &spi->dev;
if (goodix_spi_bus.ic_type == IC_TYPE_BERLIN_A)
goodix_spi_bus.read = goodix_spi_read_bra;
else
goodix_spi_bus.read = goodix_spi_read;
goodix_spi_bus.write = goodix_spi_write;
/* ts core device */
goodix_pdev = kzalloc(sizeof(struct platform_device), GFP_KERNEL);
if (!goodix_pdev)
return -ENOMEM;
goodix_pdev->name = GOODIX_CORE_DRIVER_NAME;
goodix_pdev->id = 0;
goodix_pdev->num_resources = 0;
/*
* you can find this platform dev in
* /sys/devices/platfrom/goodix_ts.0
* goodix_pdev->dev.parent = &client->dev;
*/
goodix_pdev->dev.platform_data = &goodix_spi_bus;
goodix_pdev->dev.release = goodix_pdev_release;
/*
* register platform device, then the goodix_ts_core
* module will probe the touch deivce.
*/
ret = platform_device_register(goodix_pdev);
if (ret) {
ts_err("failed register goodix platform device, %d", ret);
goto err_pdev;
}
ts_info("spi probe out");
return 0;
err_pdev:
kfree(goodix_pdev);
goodix_pdev = NULL;
ts_info("spi probe out, %d", ret);
return ret;
}
static int goodix_spi_remove(struct spi_device *spi)
{
platform_device_unregister(goodix_pdev);
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id spi_matchs[] = {
{.compatible = "goodix,gt9897S",},
{.compatible = "goodix,gt9897T",},
{.compatible = "goodix,gt9966S",},
{.compatible = "goodix,gt9916S",},
{},
};
#endif
static const struct spi_device_id spi_id_table[] = {
{TS_DRIVER_NAME, 0},
{},
};
static struct spi_driver goodix_spi_driver = {
.driver = {
.name = TS_DRIVER_NAME,
//.owner = THIS_MODULE,
.of_match_table = spi_matchs,
},
.id_table = spi_id_table,
.probe = goodix_spi_probe,
.remove = goodix_spi_remove,
};
int goodix_spi_bus_init(void)
{
ts_info("Goodix spi driver init");
return spi_register_driver(&goodix_spi_driver);
}
void goodix_spi_bus_exit(void)
{
ts_info("Goodix spi driver exit");
spi_unregister_driver(&goodix_spi_driver);
}

View File

@ -0,0 +1,343 @@
/*
* Goodix Touchscreen Driver
* Copyright (C) 2020 - 2021 Goodix, Inc.
*
* 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 "goodix_ts_core.h"
#define TS_BIN_VERSION_START_INDEX 5
#define TS_BIN_VERSION_LEN 4
#define TS_CFG_BIN_HEAD_RESERVED_LEN 6
#define TS_CFG_OFFSET_LEN 2
#define TS_IC_TYPE_NAME_MAX_LEN 15
#define TS_CFG_BIN_HEAD_LEN \
(sizeof(struct goodix_cfg_bin_head) + \
TS_CFG_BIN_HEAD_RESERVED_LEN)
#define TS_PKG_CONST_INFO_LEN \
(sizeof(struct goodix_cfg_pkg_const_info))
#define TS_PKG_REG_INFO_LEN \
(sizeof(struct goodix_cfg_pkg_reg_info))
#define TS_PKG_HEAD_LEN \
(TS_PKG_CONST_INFO_LEN + TS_PKG_REG_INFO_LEN)
/*cfg block definitin*/
#define TS_CFG_BLOCK_PID_LEN 8
#define TS_CFG_BLOCK_VID_LEN 8
#define TS_CFG_BLOCK_FW_MASK_LEN 9
#define TS_CFG_BLOCK_FW_PATCH_LEN 4
#define TS_CFG_BLOCK_RESERVED_LEN 9
#define TS_NORMAL_CFG 0x01
#define TS_HIGH_SENSE_CFG 0x03
#define TS_RQST_FW_RETRY_TIMES 2
#pragma pack(1)
struct goodix_cfg_pkg_reg {
u16 addr;
u8 reserved1;
u8 reserved2;
};
struct goodix_cfg_pkg_const_info {
u32 pkg_len;
u8 ic_type[TS_IC_TYPE_NAME_MAX_LEN];
u8 cfg_type;
u8 sensor_id;
u8 hw_pid[TS_CFG_BLOCK_PID_LEN];
u8 hw_vid[TS_CFG_BLOCK_VID_LEN];
u8 fw_mask[TS_CFG_BLOCK_FW_MASK_LEN];
u8 fw_patch[TS_CFG_BLOCK_FW_PATCH_LEN];
u16 x_res_offset;
u16 y_res_offset;
u16 trigger_offset;
};
struct goodix_cfg_pkg_reg_info {
struct goodix_cfg_pkg_reg cfg_send_flag;
struct goodix_cfg_pkg_reg version_base;
struct goodix_cfg_pkg_reg pid;
struct goodix_cfg_pkg_reg vid;
struct goodix_cfg_pkg_reg sensor_id;
struct goodix_cfg_pkg_reg fw_mask;
struct goodix_cfg_pkg_reg fw_status;
struct goodix_cfg_pkg_reg cfg_addr;
struct goodix_cfg_pkg_reg esd;
struct goodix_cfg_pkg_reg command;
struct goodix_cfg_pkg_reg coor;
struct goodix_cfg_pkg_reg gesture;
struct goodix_cfg_pkg_reg fw_request;
struct goodix_cfg_pkg_reg proximity;
u8 reserved[TS_CFG_BLOCK_RESERVED_LEN];
};
struct goodix_cfg_bin_head {
u32 bin_len;
u8 checksum;
u8 bin_version[TS_BIN_VERSION_LEN];
u8 pkg_num;
};
#pragma pack()
struct goodix_cfg_package {
struct goodix_cfg_pkg_const_info cnst_info;
struct goodix_cfg_pkg_reg_info reg_info;
const u8 *cfg;
u32 pkg_len;
};
struct goodix_cfg_bin {
unsigned char *bin_data;
unsigned int bin_data_len;
struct goodix_cfg_bin_head head;
struct goodix_cfg_package *cfg_pkgs;
};
static int goodix_read_cfg_bin(struct device *dev, const char *cfg_name,
struct goodix_cfg_bin *cfg_bin)
{
const struct firmware *firmware = NULL;
int ret;
int retry = GOODIX_RETRY_3;
ts_info("cfg_bin_name:%s", cfg_name);
while (retry--) {
ret = request_firmware(&firmware, cfg_name, dev);
if (!ret)
break;
ts_info("get cfg bin retry:[%d]", GOODIX_RETRY_3 - retry);
msleep(200);
}
if (retry < 0) {
ts_err("failed get cfg bin[%s] error:%d", cfg_name, ret);
return ret;
}
if (firmware->size <= 0) {
ts_err("request_firmware, cfg_bin length ERROR,len:%zu",
firmware->size);
ret = -EINVAL;
goto exit;
}
cfg_bin->bin_data_len = firmware->size;
/* allocate memory for cfg_bin->bin_data */
cfg_bin->bin_data = kzalloc(cfg_bin->bin_data_len, GFP_KERNEL);
if (!cfg_bin->bin_data) {
ret = -ENOMEM;
goto exit;
}
memcpy(cfg_bin->bin_data, firmware->data, cfg_bin->bin_data_len);
exit:
release_firmware(firmware);
return ret;
}
static int goodix_parse_cfg_bin(struct goodix_cfg_bin *cfg_bin)
{
u16 offset1, offset2;
u8 checksum;
int i;
/* copy cfg_bin head info */
if (cfg_bin->bin_data_len < sizeof(struct goodix_cfg_bin_head)) {
ts_err("Invalid cfg_bin size:%d", cfg_bin->bin_data_len);
return -EINVAL;
}
memcpy(&cfg_bin->head, cfg_bin->bin_data,
sizeof(struct goodix_cfg_bin_head));
cfg_bin->head.bin_len = le32_to_cpu(cfg_bin->head.bin_len);
/*check length*/
if (cfg_bin->bin_data_len != cfg_bin->head.bin_len) {
ts_err("cfg_bin len check failed,%d != %d",
cfg_bin->head.bin_len, cfg_bin->bin_data_len);
return -EINVAL;
}
/*check cfg_bin valid*/
checksum = 0;
for (i = TS_BIN_VERSION_START_INDEX; i < cfg_bin->bin_data_len; i++)
checksum += cfg_bin->bin_data[i];
if (checksum != cfg_bin->head.checksum) {
ts_err("cfg_bin checksum check filed 0x%02x != 0x%02x",
cfg_bin->head.checksum, checksum);
return -EINVAL;
}
/*allocate memory for cfg packages*/
cfg_bin->cfg_pkgs = kzalloc(sizeof(struct goodix_cfg_package) *
cfg_bin->head.pkg_num, GFP_KERNEL);
if (!cfg_bin->cfg_pkgs)
return -ENOMEM;
/*get cfg_pkg's info*/
for (i = 0; i < cfg_bin->head.pkg_num; i++) {
/*get cfg pkg length*/
if (i == cfg_bin->head.pkg_num - 1) {
offset1 = cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN +
i * TS_CFG_OFFSET_LEN] +
(cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN +
i * TS_CFG_OFFSET_LEN + 1] << 8);
cfg_bin->cfg_pkgs[i].pkg_len =
cfg_bin->bin_data_len - offset1;
} else {
offset1 = cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN +
i * TS_CFG_OFFSET_LEN] +
(cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN +
i * TS_CFG_OFFSET_LEN + 1] << 8);
offset2 = cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN +
i * TS_CFG_OFFSET_LEN + 2] +
(cfg_bin->bin_data[TS_CFG_BIN_HEAD_LEN +
i * TS_CFG_OFFSET_LEN + 3] << 8);
if (offset2 <= offset1) {
ts_err("offset error,pkg:%d, offset1:%d, offset2:%d",
i, offset1, offset2);
goto exit;
}
cfg_bin->cfg_pkgs[i].pkg_len = offset2 - offset1;
}
/*get cfg pkg head*/
memcpy(&cfg_bin->cfg_pkgs[i].cnst_info,
&cfg_bin->bin_data[offset1], TS_PKG_CONST_INFO_LEN);
memcpy(&cfg_bin->cfg_pkgs[i].reg_info,
&cfg_bin->bin_data[offset1 + TS_PKG_CONST_INFO_LEN],
TS_PKG_REG_INFO_LEN);
/*get configuration data*/
cfg_bin->cfg_pkgs[i].cfg =
&cfg_bin->bin_data[offset1 + TS_PKG_HEAD_LEN];
}
/*debug, print pkg information*/
ts_info("Driver bin info: ver %s, len %d, pkgs %d",
cfg_bin->head.bin_version,
cfg_bin->head.bin_len,
cfg_bin->head.pkg_num);
return 0;
exit:
kfree(cfg_bin->cfg_pkgs);
return -EINVAL;
}
static int goodix_get_reg_and_cfg(struct goodix_ts_core *cd, u8 sensor_id,
struct goodix_cfg_bin *cfg_bin)
{
int i;
u8 cfg_type;
u32 cfg_len;
struct goodix_cfg_package *cfg_pkg;
if (!cfg_bin->head.pkg_num || !cfg_bin->cfg_pkgs) {
ts_err("there is none cfg package, pkg_num:%d",
cfg_bin->head.pkg_num);
return -EINVAL;
}
/* find cfg packages with same sensor_id */
for (i = 0; i < cfg_bin->head.pkg_num; i++) {
cfg_pkg = &cfg_bin->cfg_pkgs[i];
if (sensor_id != cfg_pkg->cnst_info.sensor_id) {
ts_info("pkg:%d, sensor id contrast FAILED, bin %d != %d",
i, cfg_pkg->cnst_info.sensor_id, sensor_id);
continue;
}
cfg_type = cfg_pkg->cnst_info.cfg_type;
if (cfg_type >= GOODIX_MAX_CONFIG_GROUP) {
ts_err("usupported config type %d",
cfg_pkg->cnst_info.cfg_type);
goto err_out;
}
cfg_len = cfg_pkg->pkg_len - TS_PKG_CONST_INFO_LEN -
TS_PKG_REG_INFO_LEN;
if (cfg_len > GOODIX_CFG_MAX_SIZE) {
ts_err("config len exceed limit %d > %d",
cfg_len, GOODIX_CFG_MAX_SIZE);
goto err_out;
}
if (cd->ic_configs[cfg_type]) {
ts_err("found same type config twice for sensor id %d, skiped",
sensor_id);
continue;
}
cd->ic_configs[cfg_type] =
kzalloc(sizeof(struct goodix_ic_config),
GFP_KERNEL);
if (!cd->ic_configs[cfg_type])
goto err_out;
cd->ic_configs[cfg_type]->len = cfg_len;
memcpy(cd->ic_configs[cfg_type]->data, cfg_pkg->cfg, cfg_len);
ts_info("get config type %d, len %d, for sensor id %d",
cfg_type, cfg_len, sensor_id);
}
return 0;
err_out:
/* parse config enter error, release memory alloced */
for (i = 0; i < GOODIX_MAX_CONFIG_GROUP; i++) {
kfree(cd->ic_configs[i]);
cd->ic_configs[i] = NULL;
}
return -EINVAL;
}
static int goodix_get_config_data(struct goodix_ts_core *cd, u8 sensor_id)
{
struct goodix_cfg_bin cfg_bin = {0};
char *cfg_name = cd->board_data.cfg_bin_name;
int ret;
/*get cfg_bin from file system*/
ret = goodix_read_cfg_bin(&cd->pdev->dev, cfg_name, &cfg_bin);
if (ret) {
ts_err("failed get valid config bin data");
return ret;
}
/*parse cfg bin*/
ret = goodix_parse_cfg_bin(&cfg_bin);
if (ret) {
ts_err("failed parse cfg bin");
goto err_out;
}
/*get register address and configuration from cfg bin*/
ret = goodix_get_reg_and_cfg(cd, sensor_id, &cfg_bin);
if (!ret)
ts_info("success get reg and cfg info from cfg bin");
else
ts_err("failed get cfg and reg info, update fw then retry");
kfree(cfg_bin.cfg_pkgs);
err_out:
kfree(cfg_bin.bin_data);
return ret;
}
int goodix_get_config_proc(struct goodix_ts_core *cd)
{
return goodix_get_config_data(cd, cd->fw_version.sensor_id);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,692 @@
/*
* Goodix Touchscreen Driver
* Copyright (C) 2020 - 2021 Goodix, Inc.
*
* 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.
*
*/
#ifndef _GOODIX_TS_CORE_H_
#define _GOODIX_TS_CORE_H_
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/firmware.h>
#include <linux/slab.h>
#include <asm/unaligned.h>
#include <linux/vmalloc.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/completion.h>
#include <linux/of_irq.h>
#if IS_ENABLED(CONFIG_OF)
#include <linux/of_gpio.h>
#include <linux/regulator/consumer.h>
#endif
#if IS_ENABLED(CONFIG_FB)
#include <linux/notifier.h>
#include <linux/fb.h>
#endif
#define GOODIX_CORE_DRIVER_NAME "goodix_ts"
#define GOODIX_PEN_DRIVER_NAME "goodix_ts,pen"
#define GOODIX_DRIVER_VERSION "v1.2.4"
#define GOODIX_MAX_TOUCH 10
#define GOODIX_PEN_MAX_PRESSURE 4096
#define GOODIX_MAX_PEN_KEY 2
#define GOODIX_PEN_MAX_TILT 90
#define GOODIX_CFG_MAX_SIZE 4096
#define GOODIX_FW_MAX_SIEZE (300 * 1024)
#define GOODIX_MAX_STR_LABLE_LEN 32
#define GOODIX_MAX_FRAMEDATA_LEN 1700
#define GOODIX_GESTURE_DATA_LEN 16
#define GOODIX_NORMAL_RESET_DELAY_MS 100
#define GOODIX_HOLD_CPU_RESET_DELAY_MS 5
#define GOODIX_RETRY_3 3
#define GOODIX_RETRY_5 5
#define GOODIX_RETRY_10 10
#define TS_DEFAULT_FIRMWARE "goodix_firmware.bin"
#define TS_DEFAULT_CFG_BIN "goodix_cfg_group.bin"
enum GOODIX_GESTURE_TYP {
GESTURE_SINGLE_TAP = (1 << 0),
GESTURE_DOUBLE_TAP = (1 << 1),
GESTURE_FOD_PRESS = (1 << 2)
};
enum CORD_PROB_STA {
CORE_MODULE_UNPROBED = 0,
CORE_MODULE_PROB_SUCCESS = 1,
CORE_MODULE_PROB_FAILED = -1,
CORE_MODULE_REMOVED = -2,
};
enum GOODIX_ERR_CODE {
GOODIX_EBUS = (1<<0),
GOODIX_ECHECKSUM = (1<<1),
GOODIX_EVERSION = (1<<2),
GOODIX_ETIMEOUT = (1<<3),
GOODIX_EMEMCMP = (1<<4),
GOODIX_EOTHER = (1<<7)
};
enum IC_TYPE_ID {
IC_TYPE_NONE,
IC_TYPE_NORMANDY,
IC_TYPE_NANJING,
IC_TYPE_YELLOWSTONE,
IC_TYPE_BERLIN_A,
IC_TYPE_BERLIN_B,
IC_TYPE_BERLIN_D
};
enum GOODIX_IC_CONFIG_TYPE {
CONFIG_TYPE_TEST = 0,
CONFIG_TYPE_NORMAL = 1,
CONFIG_TYPE_HIGHSENSE = 2,
CONFIG_TYPE_CHARGER = 3,
CONFIG_TYPE_CHARGER_HS = 4,
CONFIG_TYPE_HOLSTER = 5,
CONFIG_TYPE_HOSTER_CH = 6,
CONFIG_TYPE_OTHER = 7,
/* keep this at the last */
GOODIX_MAX_CONFIG_GROUP = 8,
};
enum CHECKSUM_MODE {
CHECKSUM_MODE_U8_LE,
CHECKSUM_MODE_U16_LE,
};
#define MAX_SCAN_FREQ_NUM 8
#define MAX_SCAN_RATE_NUM 8
#define MAX_FREQ_NUM_STYLUS 8
#define MAX_STYLUS_SCAN_FREQ_NUM 6
#pragma pack(1)
struct frame_head {
uint8_t sync;
uint16_t frame_index;
uint16_t cur_frame_len;
uint16_t next_frame_len;
uint32_t data_en; /* 0- 7 for pack_en; 8 - 31 for type en */
uint8_t touch_pack_index;
uint8_t stylus_pack_index;
uint8_t res;
uint16_t checksum;
};
struct goodix_fw_version {
u8 rom_pid[6]; /* rom PID */
u8 rom_vid[3]; /* Mask VID */
u8 rom_vid_reserved;
u8 patch_pid[8]; /* Patch PID */
u8 patch_vid[4]; /* Patch VID */
u8 patch_vid_reserved;
u8 sensor_id;
u8 reserved[2];
u16 checksum;
};
struct goodix_ic_info_version {
u8 info_customer_id;
u8 info_version_id;
u8 ic_die_id;
u8 ic_version_id;
u32 config_id;
u8 config_version;
u8 frame_data_customer_id;
u8 frame_data_version_id;
u8 touch_data_customer_id;
u8 touch_data_version_id;
u8 reserved[3];
};
struct goodix_ic_info_feature { /* feature info*/
u16 freqhop_feature;
u16 calibration_feature;
u16 gesture_feature;
u16 side_touch_feature;
u16 stylus_feature;
};
struct goodix_ic_info_param { /* param */
u8 drv_num;
u8 sen_num;
u8 button_num;
u8 force_num;
u8 active_scan_rate_num;
u16 active_scan_rate[MAX_SCAN_RATE_NUM];
u8 mutual_freq_num;
u16 mutual_freq[MAX_SCAN_FREQ_NUM];
u8 self_tx_freq_num;
u16 self_tx_freq[MAX_SCAN_FREQ_NUM];
u8 self_rx_freq_num;
u16 self_rx_freq[MAX_SCAN_FREQ_NUM];
u8 stylus_freq_num;
u16 stylus_freq[MAX_FREQ_NUM_STYLUS];
};
struct goodix_ic_info_misc { /* other data */
u32 cmd_addr;
u16 cmd_max_len;
u32 cmd_reply_addr;
u16 cmd_reply_len;
u32 fw_state_addr;
u16 fw_state_len;
u32 fw_buffer_addr;
u16 fw_buffer_max_len;
u32 frame_data_addr;
u16 frame_data_head_len;
u16 fw_attr_len;
u16 fw_log_len;
u8 pack_max_num;
u8 pack_compress_version;
u16 stylus_struct_len;
u16 mutual_struct_len;
u16 self_struct_len;
u16 noise_struct_len;
u32 touch_data_addr;
u16 touch_data_head_len;
u16 point_struct_len;
u16 reserved1;
u16 reserved2;
u32 mutual_rawdata_addr;
u32 mutual_diffdata_addr;
u32 mutual_refdata_addr;
u32 self_rawdata_addr;
u32 self_diffdata_addr;
u32 self_refdata_addr;
u32 iq_rawdata_addr;
u32 iq_refdata_addr;
u32 im_rawdata_addr;
u16 im_readata_len;
u32 noise_rawdata_addr;
u16 noise_rawdata_len;
u32 stylus_rawdata_addr;
u16 stylus_rawdata_len;
u32 noise_data_addr;
u32 esd_addr;
};
struct goodix_ic_info {
u16 length;
struct goodix_ic_info_version version;
struct goodix_ic_info_feature feature;
struct goodix_ic_info_param parm;
struct goodix_ic_info_misc misc;
};
#pragma pack()
/*
* struct ts_rawdata_info
*
*/
#define TS_RAWDATA_BUFF_MAX 7000
#define TS_RAWDATA_RESULT_MAX 100
struct ts_rawdata_info {
int used_size; //fill in rawdata size
s16 buff[TS_RAWDATA_BUFF_MAX];
char result[TS_RAWDATA_RESULT_MAX];
};
/*
* struct goodix_module - external modules container
* @head: external modules list
* @initilized: whether this struct is initilized
* @mutex: mutex lock
* @wq: workqueue to do register work
* @core_data: core_data pointer
*/
struct goodix_module {
struct list_head head;
bool initilized;
struct mutex mutex;
struct workqueue_struct *wq;
struct goodix_ts_core *core_data;
};
/*
* struct goodix_ts_board_data - board data
* @avdd_name: name of analoy regulator
* @iovdd_name: name of analoy regulator
* @reset_gpio: reset gpio number
* @irq_gpio: interrupt gpio number
* @irq_flag: irq trigger type
* @swap_axis: whether swaw x y axis
* @panel_max_x/y/w/p: resolution and size
* @invert_xy: invert x and y for inversely mounted IC
* @pannel_key_map: key map
* @fw_name: name of the firmware image
*/
struct goodix_ts_board_data {
char avdd_name[GOODIX_MAX_STR_LABLE_LEN];
char iovdd_name[GOODIX_MAX_STR_LABLE_LEN];
int reset_gpio;
int irq_gpio;
int avdd_gpio;
int iovdd_gpio;
unsigned int irq_flags;
unsigned int swap_axis;
unsigned int panel_max_x;
unsigned int panel_max_y;
unsigned int panel_max_w; /*major and minor*/
unsigned int panel_max_p; /*pressure*/
bool invert_xy;
bool pen_enable;
char fw_name[GOODIX_MAX_STR_LABLE_LEN];
char cfg_bin_name[GOODIX_MAX_STR_LABLE_LEN];
};
enum goodix_fw_update_mode {
UPDATE_MODE_DEFAULT = 0,
UPDATE_MODE_FORCE = (1 << 0), /* force update mode */
UPDATE_MODE_BLOCK = (1 << 1), /* update in block mode */
UPDATE_MODE_FLASH_CFG = (1 << 2), /* reflash config */
UPDATE_MODE_SRC_SYSFS = (1 << 4), /* firmware file from sysfs */
UPDATE_MODE_SRC_HEAD = (1 << 5), /* firmware file from head file */
UPDATE_MODE_SRC_REQUEST = (1 << 6), /* request firmware */
UPDATE_MODE_SRC_ARGS = (1 << 7), /* firmware data from function args */
};
#define MAX_CMD_DATA_LEN 10
#define MAX_CMD_BUF_LEN 16
#pragma pack(1)
struct goodix_ts_cmd {
union {
struct {
u8 state;
u8 ack;
u8 len;
u8 cmd;
u8 data[MAX_CMD_DATA_LEN];
};
u8 buf[MAX_CMD_BUF_LEN];
};
};
#pragma pack()
/* interrupt event type */
enum ts_event_type {
EVENT_INVALID = 0,
EVENT_TOUCH = (1 << 0), /* finger touch event */
EVENT_PEN = (1 << 1), /* pen event */
EVENT_REQUEST = (1 << 2),
EVENT_GESTURE = (1 << 3),
};
enum ts_request_type {
REQUEST_TYPE_CONFIG = 1,
REQUEST_TYPE_RESET = 3,
};
/* notifier event */
enum ts_notify_event {
NOTIFY_FWUPDATE_START,
NOTIFY_FWUPDATE_FAILED,
NOTIFY_FWUPDATE_SUCCESS,
NOTIFY_SUSPEND,
NOTIFY_RESUME,
NOTIFY_ESD_OFF,
NOTIFY_ESD_ON,
NOTIFY_CFG_BIN_FAILED,
NOTIFY_CFG_BIN_SUCCESS,
};
enum touch_point_status {
TS_NONE,
TS_RELEASE,
TS_TOUCH,
};
/* coordinate package */
struct goodix_ts_coords {
int status; /* NONE, RELEASE, TOUCH */
unsigned int x, y, w, p;
};
struct goodix_pen_coords {
int status; /* NONE, RELEASE, TOUCH */
int tool_type; /* BTN_TOOL_RUBBER BTN_TOOL_PEN */
unsigned int x, y, p;
signed char tilt_x;
signed char tilt_y;
};
/* touch event data */
struct goodix_touch_data {
int touch_num;
struct goodix_ts_coords coords[GOODIX_MAX_TOUCH];
};
struct goodix_ts_key {
int status;
int code;
};
struct goodix_pen_data {
struct goodix_pen_coords coords;
struct goodix_ts_key keys[GOODIX_MAX_PEN_KEY];
};
/*
* struct goodix_ts_event - touch event struct
* @event_type: touch event type, touch data or
* request event
* @event_data: event data
*/
struct goodix_ts_event {
enum ts_event_type event_type;
u8 request_code; /* represent the request type */
u8 gesture_type;
u8 gesture_data[GOODIX_GESTURE_DATA_LEN];
struct goodix_touch_data touch_data;
struct goodix_pen_data pen_data;
};
enum goodix_ic_bus_type {
GOODIX_BUS_TYPE_I2C,
GOODIX_BUS_TYPE_SPI,
GOODIX_BUS_TYPE_I3C,
};
struct goodix_bus_interface {
int bus_type;
int ic_type;
struct device *dev;
int (*read)(struct device *dev, unsigned int addr,
unsigned char *data, unsigned int len);
int (*write)(struct device *dev, unsigned int addr,
unsigned char *data, unsigned int len);
};
struct goodix_ts_hw_ops {
int (*power_on)(struct goodix_ts_core *cd, bool on);
int (*resume)(struct goodix_ts_core *cd);
int (*suspend)(struct goodix_ts_core *cd);
int (*gesture)(struct goodix_ts_core *cd, int gesture_type);
int (*reset)(struct goodix_ts_core *cd, int delay_ms);
int (*irq_enable)(struct goodix_ts_core *cd, bool enable);
int (*read)(struct goodix_ts_core *cd, unsigned int addr,
unsigned char *data, unsigned int len);
int (*write)(struct goodix_ts_core *cd, unsigned int addr,
unsigned char *data, unsigned int len);
int (*send_cmd)(struct goodix_ts_core *cd,
struct goodix_ts_cmd *cmd);
int (*send_config)(struct goodix_ts_core *cd,
u8 *config, int len);
int (*read_config)(struct goodix_ts_core *cd,
u8 *config_data, int size);
int (*read_version)(struct goodix_ts_core *cd,
struct goodix_fw_version *version);
int (*get_ic_info)(struct goodix_ts_core *cd,
struct goodix_ic_info *ic_info);
int (*esd_check)(struct goodix_ts_core *cd);
int (*event_handler)(struct goodix_ts_core *cd,
struct goodix_ts_event *ts_event);
int (*after_event_handler)(struct goodix_ts_core *cd);
int (*get_capacitance_data)(struct goodix_ts_core *cd,
struct ts_rawdata_info *info);
};
/*
* struct goodix_ts_esd - esd protector structure
* @esd_work: esd delayed work
* @esd_on: 1 - turn on esd protection, 0 - turn
* off esd protection
*/
struct goodix_ts_esd {
bool irq_status;
atomic_t esd_on;
struct delayed_work esd_work;
struct notifier_block esd_notifier;
struct goodix_ts_core *ts_core;
};
enum goodix_core_init_stage {
CORE_UNINIT,
CORE_INIT_FAIL,
CORE_INIT_STAGE1,
CORE_INIT_STAGE2
};
struct goodix_ic_config {
int len;
u8 data[GOODIX_CFG_MAX_SIZE];
};
struct goodix_ts_core {
int init_stage;
struct platform_device *pdev;
struct goodix_fw_version fw_version;
struct goodix_ic_info ic_info;
struct goodix_bus_interface *bus;
struct goodix_ts_board_data board_data;
struct goodix_ts_hw_ops *hw_ops;
struct input_dev *input_dev;
struct input_dev *pen_dev;
/* TODO counld we remove this from core data? */
struct goodix_ts_event ts_event;
/* every pointer of this array represent a kind of config */
struct goodix_ic_config *ic_configs[GOODIX_MAX_CONFIG_GROUP];
struct regulator *avdd;
struct regulator *iovdd;
unsigned char gesture_type;
int power_on;
int irq;
size_t irq_trig_cnt;
atomic_t irq_enabled;
atomic_t suspended;
/* when this flag is true, driver should not clean the sync flag */
bool tools_ctrl_sync;
struct notifier_block ts_notifier;
struct goodix_ts_esd ts_esd;
#if defined(CONFIG_DRM)
struct notifier_block fb_notifier;
void *notifier_cookie;
const char *touch_environment;
#elif defined(CONFIG_FB)
struct notifier_block fb_notifier;
#endif
#ifdef CONFIG_GOODIX_TRUSTED_TOUCH
struct trusted_touch_vm_info *vm_info;
struct mutex fts_clk_io_ctrl_mutex;
struct completion trusted_touch_powerdown;
struct clk *core_clk;
struct clk *iface_clk;
atomic_t trusted_touch_initialized;
atomic_t trusted_touch_enabled;
atomic_t trusted_touch_transition;
atomic_t trusted_touch_event;
atomic_t trusted_touch_abort_status;
atomic_t delayed_vm_probe_pending;
atomic_t trusted_touch_mode;
#endif
};
/* external module structures */
enum goodix_ext_priority {
EXTMOD_PRIO_RESERVED = 0,
EXTMOD_PRIO_FWUPDATE,
EXTMOD_PRIO_GESTURE,
EXTMOD_PRIO_HOTKNOT,
EXTMOD_PRIO_DBGTOOL,
EXTMOD_PRIO_DEFAULT,
};
#define EVT_HANDLED 0
#define EVT_CONTINUE 0
#define EVT_CANCEL 1
#define EVT_CANCEL_IRQEVT 1
#define EVT_CANCEL_SUSPEND 1
#define EVT_CANCEL_RESUME 1
#define EVT_CANCEL_RESET 1
struct goodix_ext_module;
/* external module's operations callback */
struct goodix_ext_module_funcs {
int (*init)(struct goodix_ts_core *core_data,
struct goodix_ext_module *module);
int (*exit)(struct goodix_ts_core *core_data,
struct goodix_ext_module *module);
int (*before_reset)(struct goodix_ts_core *core_data,
struct goodix_ext_module *module);
int (*after_reset)(struct goodix_ts_core *core_data,
struct goodix_ext_module *module);
int (*before_suspend)(struct goodix_ts_core *core_data,
struct goodix_ext_module *module);
int (*after_suspend)(struct goodix_ts_core *core_data,
struct goodix_ext_module *module);
int (*before_resume)(struct goodix_ts_core *core_data,
struct goodix_ext_module *module);
int (*after_resume)(struct goodix_ts_core *core_data,
struct goodix_ext_module *module);
int (*irq_event)(struct goodix_ts_core *core_data,
struct goodix_ext_module *module);
};
/*
* struct goodix_ext_module - external module struct
* @list: list used to link into modules manager
* @name: name of external module
* @priority: module priority vlaue, zero is invalid
* @funcs: operations callback
* @priv_data: private data region
* @kobj: kobject
* @work: used to queue one work to do registration
*/
struct goodix_ext_module {
struct list_head list;
char *name;
enum goodix_ext_priority priority;
const struct goodix_ext_module_funcs *funcs;
void *priv_data;
struct kobject kobj;
struct work_struct work;
};
/*
* struct goodix_ext_attribute - exteranl attribute struct
* @attr: attribute
* @show: show interface of external attribute
* @store: store interface of external attribute
*/
struct goodix_ext_attribute {
struct attribute attr;
ssize_t (*show)(struct goodix_ext_module *module, char *buf);
ssize_t (*store)(struct goodix_ext_module *module,
const char *buf, size_t len);
};
/* external attrs helper macro */
#define __EXTMOD_ATTR(_name, _mode, _show, _store) { \
.attr = {.name = __stringify(_name), .mode = _mode }, \
.show = _show, \
.store = _store, \
}
/* external attrs helper macro, used to define external attrs */
#define DEFINE_EXTMOD_ATTR(_name, _mode, _show, _store) \
static struct goodix_ext_attribute ext_attr_##_name = \
__EXTMOD_ATTR(_name, _mode, _show, _store)
/* log macro */
extern bool debug_log_flag;
#define ts_info(fmt, arg...) \
pr_info("[GTP-INF][%s:%d] "fmt"\n", __func__, __LINE__, ##arg)
#define ts_err(fmt, arg...) \
pr_err("[GTP-ERR][%s:%d] "fmt"\n", __func__, __LINE__, ##arg)
#define ts_debug(fmt, arg...) \
{if (debug_log_flag) \
pr_info("[GTP-DBG][%s:%d] "fmt"\n", __func__, __LINE__, ##arg);}
/*
* get board data pointer
*/
static inline struct goodix_ts_board_data *board_data(
struct goodix_ts_core *core)
{
if (!core)
return NULL;
return &(core->board_data);
}
/**
* goodix_register_ext_module - interface for external module
* to register into touch core modules structure
*
* @module: pointer to external module to be register
* return: 0 ok, <0 failed
*/
int goodix_register_ext_module(struct goodix_ext_module *module);
/* register module no wait */
int goodix_register_ext_module_no_wait(struct goodix_ext_module *module);
/**
* goodix_unregister_ext_module - interface for external module
* to unregister external modules
*
* @module: pointer to external module
* return: 0 ok, <0 failed
*/
int goodix_unregister_ext_module(struct goodix_ext_module *module);
/* remove all registered ext module
* return 0 on success, otherwise return < 0
*/
int goodix_ts_blocking_notify(enum ts_notify_event evt, void *v);
struct kobj_type *goodix_get_default_ktype(void);
struct kobject *goodix_get_default_kobj(void);
struct goodix_ts_hw_ops *goodix_get_hw_ops(void);
int goodix_get_config_proc(struct goodix_ts_core *cd);
int goodix_spi_bus_init(void);
void goodix_spi_bus_exit(void);
int goodix_i2c_bus_init(void);
void goodix_i2c_bus_exit(void);
u32 goodix_append_checksum(u8 *data, int len, int mode);
int checksum_cmp(const u8 *data, int size, int mode);
int is_risk_data(const u8 *data, int size);
u32 goodix_get_file_config_id(u8 *ic_config);
void goodix_rotate_abcd2cbad(int tx, int rx, s16 *data);
int goodix_fw_update_init(struct goodix_ts_core *core_data);
void goodix_fw_update_uninit(void);
int goodix_do_fw_update(struct goodix_ic_config *ic_config, int mode);
int goodix_get_ic_type(struct device_node *node);
int gesture_module_init(void);
void gesture_module_exit(void);
int inspect_module_init(void);
void inspect_module_exit(void);
int goodix_tools_init(void);
void goodix_tools_exit(void);
/* goodix FB test */
/*
void goodix_fb_ext_ctrl(int suspend);
*/
#endif

View File

@ -0,0 +1,446 @@
/*
* Goodix Gesture Module
*
* Copyright (C) 2019 - 2020 Goodix, Inc.
*
* 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/spinlock.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/input.h>
#include <linux/platform_device.h>
#include <linux/version.h>
#include <linux/delay.h>
#include <linux/atomic.h>
#include <linux/input/mt.h>
#include "goodix_ts_core.h"
#define GOODIX_GESTURE_DOUBLE_TAP 0xCC
#define GOODIX_GESTURE_SINGLE_TAP 0x4C
#define GOODIX_GESTURE_FOD_DOWN 0x46
#define GOODIX_GESTURE_FOD_UP 0x55
/*
* struct gesture_module - gesture module data
* @registered: module register state
* @sysfs_node_created: sysfs node state
* @gesture_type: valid gesture type, each bit represent one gesture type
* @gesture_data: store latest gesture code get from irq event
* @gesture_ts_cmd: gesture command data
*/
struct gesture_module {
atomic_t registered;
struct goodix_ts_core *ts_core;
struct goodix_ext_module module;
};
static struct gesture_module *gsx_gesture; /*allocated in gesture init module*/
static bool module_initialized;
static ssize_t gsx_double_type_show(struct goodix_ext_module *module,
char *buf)
{
struct gesture_module *gsx = module->priv_data;
unsigned char type = gsx->ts_core->gesture_type;
if (!gsx)
return -EIO;
if (atomic_read(&gsx->registered) == 0) {
ts_err("gesture module is not registered");
return 0;
}
return scnprintf(buf, PAGE_SIZE, "%s\n",
(type & GESTURE_DOUBLE_TAP) ? "enable" : "disable");
}
static ssize_t gsx_double_type_store(struct goodix_ext_module *module,
const char *buf, size_t count)
{
struct gesture_module *gsx = module->priv_data;
if (!gsx)
return -EIO;
if (atomic_read(&gsx->registered) == 0) {
ts_err("gesture module is not registered");
return 0;
}
if (buf[0] == '1') {
ts_info("enable double tap");
gsx->ts_core->gesture_type |= GESTURE_DOUBLE_TAP;
} else if (buf[0] == '0') {
ts_info("disable double tap");
gsx->ts_core->gesture_type &= ~GESTURE_DOUBLE_TAP;
} else
ts_err("invalid cmd[%d]", buf[0]);
return count;
}
static ssize_t gsx_single_type_show(struct goodix_ext_module *module,
char *buf)
{
struct gesture_module *gsx = module->priv_data;
unsigned char type = gsx->ts_core->gesture_type;
if (!gsx)
return -EIO;
if (atomic_read(&gsx->registered) == 0) {
ts_err("gesture module is not registered");
return 0;
}
return scnprintf(buf, PAGE_SIZE, "%s\n",
(type & GESTURE_SINGLE_TAP) ? "enable" : "disable");
}
static ssize_t gsx_single_type_store(struct goodix_ext_module *module,
const char *buf, size_t count)
{
struct gesture_module *gsx = module->priv_data;
if (!gsx)
return -EIO;
if (atomic_read(&gsx->registered) == 0) {
ts_err("gesture module is not registered");
return 0;
}
if (buf[0] == '1') {
ts_info("enable single tap");
gsx->ts_core->gesture_type |= GESTURE_SINGLE_TAP;
} else if (buf[0] == '0') {
ts_info("disable single tap");
gsx->ts_core->gesture_type &= ~GESTURE_SINGLE_TAP;
} else
ts_err("invalid cmd[%d]", buf[0]);
return count;
}
static ssize_t gsx_fod_type_show(struct goodix_ext_module *module,
char *buf)
{
struct gesture_module *gsx = module->priv_data;
unsigned char type = gsx->ts_core->gesture_type;
if (!gsx)
return -EIO;
if (atomic_read(&gsx->registered) == 0) {
ts_err("gesture module is not registered");
return 0;
}
return scnprintf(buf, PAGE_SIZE, "%s\n",
(type & GESTURE_FOD_PRESS) ? "enable" : "disable");
}
static ssize_t gsx_fod_type_store(struct goodix_ext_module *module,
const char *buf, size_t count)
{
struct gesture_module *gsx = module->priv_data;
if (!gsx)
return -EIO;
if (atomic_read(&gsx->registered) == 0) {
ts_err("gesture module is not registered");
return 0;
}
if (buf[0] == '1') {
ts_info("enable fod");
gsx->ts_core->gesture_type |= GESTURE_FOD_PRESS;
} else if (buf[0] == '0') {
ts_info("disable fod");
gsx->ts_core->gesture_type &= ~GESTURE_FOD_PRESS;
} else
ts_err("invalid cmd[%d]", buf[0]);
return count;
}
const struct goodix_ext_attribute gesture_attrs[] = {
__EXTMOD_ATTR(double_en, 0664,
gsx_double_type_show, gsx_double_type_store),
__EXTMOD_ATTR(single_en, 0664,
gsx_single_type_show, gsx_single_type_store),
__EXTMOD_ATTR(fod_en, 0664,
gsx_fod_type_show, gsx_fod_type_store),
};
static int gsx_gesture_init(struct goodix_ts_core *cd,
struct goodix_ext_module *module)
{
struct gesture_module *gsx = module->priv_data;
if (!cd || !cd->hw_ops->gesture) {
ts_err("gesture unsupported");
return -EINVAL;
}
gsx->ts_core = cd;
gsx->ts_core->gesture_type = 0;
atomic_set(&gsx->registered, 1);
return 0;
}
static int gsx_gesture_exit(struct goodix_ts_core *cd,
struct goodix_ext_module *module)
{
struct gesture_module *gsx = module->priv_data;
if (!cd || !cd->hw_ops->gesture) {
ts_err("gesture unsupported");
return -EINVAL;
}
atomic_set(&gsx->registered, 0);
return 0;
}
/**
* gsx_gesture_ist - Gesture Irq handle
* This functions is excuted when interrupt happended and
* ic in doze mode.
*
* @cd: pointer to touch core data
* @module: pointer to goodix_ext_module struct
* return: 0 goon execute, EVT_CANCEL_IRQEVT stop execute
*/
static int gsx_gesture_ist(struct goodix_ts_core *cd,
struct goodix_ext_module *module)
{
struct goodix_ts_hw_ops *hw_ops = cd->hw_ops;
struct goodix_ts_event gs_event = {0};
int fodx, fody, overlay_area;
int ret;
if (atomic_read(&cd->suspended) == 0 || cd->gesture_type == 0)
return EVT_CONTINUE;
ret = hw_ops->event_handler(cd, &gs_event);
if (ret) {
ts_err("failed get gesture data");
goto re_send_ges_cmd;
}
if (!(gs_event.event_type & EVENT_GESTURE)) {
ts_err("invalid event type: 0x%x",
cd->ts_event.event_type);
goto re_send_ges_cmd;
}
switch (gs_event.gesture_type) {
case GOODIX_GESTURE_SINGLE_TAP:
if (cd->gesture_type & GESTURE_SINGLE_TAP) {
ts_info("get SINGLE-TAP gesture");
input_report_key(cd->input_dev, KEY_WAKEUP, 1);
// input_report_key(cd->input_dev, KEY_GOTO, 1);
input_sync(cd->input_dev);
input_report_key(cd->input_dev, KEY_WAKEUP, 0);
// input_report_key(cd->input_dev, KEY_GOTO, 0);
input_sync(cd->input_dev);
} else {
ts_debug("not enable SINGLE-TAP");
}
break;
case GOODIX_GESTURE_DOUBLE_TAP:
if (cd->gesture_type & GESTURE_DOUBLE_TAP) {
ts_info("get DOUBLE-TAP gesture");
input_report_key(cd->input_dev, KEY_WAKEUP, 1);
input_sync(cd->input_dev);
input_report_key(cd->input_dev, KEY_WAKEUP, 0);
input_sync(cd->input_dev);
} else {
ts_debug("not enable DOUBLE-TAP");
}
break;
case GOODIX_GESTURE_FOD_DOWN:
if (cd->gesture_type & GESTURE_FOD_PRESS) {
ts_info("get FOD-DOWN gesture");
fodx = le16_to_cpup((__le16 *)gs_event.gesture_data);
fody = le16_to_cpup((__le16 *)(gs_event.gesture_data + 2));
overlay_area = gs_event.gesture_data[4];
ts_debug("fodx:%d fody:%d overlay_area:%d", fodx, fody, overlay_area);
input_report_key(cd->input_dev, BTN_TOUCH, 1);
input_mt_slot(cd->input_dev, 0);
input_mt_report_slot_state(cd->input_dev, MT_TOOL_FINGER, 1);
input_report_abs(cd->input_dev, ABS_MT_POSITION_X, fodx);
input_report_abs(cd->input_dev, ABS_MT_POSITION_Y, fody);
input_report_abs(cd->input_dev, ABS_MT_WIDTH_MAJOR, overlay_area);
input_sync(cd->input_dev);
} else {
ts_debug("not enable FOD-DOWN");
}
break;
case GOODIX_GESTURE_FOD_UP:
if (cd->gesture_type & GESTURE_FOD_PRESS) {
ts_info("get FOD-UP gesture");
fodx = le16_to_cpup((__le16 *)gs_event.gesture_data);
fody = le16_to_cpup((__le16 *)(gs_event.gesture_data + 2));
overlay_area = gs_event.gesture_data[4];
input_report_key(cd->input_dev, BTN_TOUCH, 0);
input_mt_slot(cd->input_dev, 0);
input_mt_report_slot_state(cd->input_dev,
MT_TOOL_FINGER, 0);
input_sync(cd->input_dev);
} else {
ts_debug("not enable FOD-UP");
}
break;
default:
ts_err("not support gesture type[%02X]", gs_event.gesture_type);
break;
}
re_send_ges_cmd:
if (hw_ops->gesture(cd, 0))
ts_info("warning: failed re_send gesture cmd");
return EVT_CANCEL_IRQEVT;
}
/**
* gsx_gesture_before_suspend - execute gesture suspend routine
* This functions is excuted to set ic into doze mode
*
* @cd: pointer to touch core data
* @module: pointer to goodix_ext_module struct
* return: 0 goon execute, EVT_IRQCANCLED stop execute
*/
static int gsx_gesture_before_suspend(struct goodix_ts_core *cd,
struct goodix_ext_module *module)
{
int ret;
const struct goodix_ts_hw_ops *hw_ops = cd->hw_ops;
if (cd->gesture_type == 0)
return EVT_CONTINUE;
ret = hw_ops->gesture(cd, 0);
if (ret)
ts_err("failed enter gesture mode");
else
ts_info("enter gesture mode, type[0x%02X]", cd->gesture_type);
hw_ops->irq_enable(cd, true);
enable_irq_wake(cd->irq);
return EVT_CANCEL_SUSPEND;
}
static int gsx_gesture_before_resume(struct goodix_ts_core *cd,
struct goodix_ext_module *module)
{
const struct goodix_ts_hw_ops *hw_ops = cd->hw_ops;
if (cd->gesture_type == 0)
return EVT_CONTINUE;
disable_irq_wake(cd->irq);
hw_ops->reset(cd, GOODIX_NORMAL_RESET_DELAY_MS);
return EVT_CANCEL_RESUME;
}
static struct goodix_ext_module_funcs gsx_gesture_funcs = {
.irq_event = gsx_gesture_ist,
.init = gsx_gesture_init,
.exit = gsx_gesture_exit,
.before_suspend = gsx_gesture_before_suspend,
.before_resume = gsx_gesture_before_resume,
};
int gesture_module_init(void)
{
int ret;
int i;
struct kobject *def_kobj = goodix_get_default_kobj();
struct kobj_type *def_kobj_type = goodix_get_default_ktype();
gsx_gesture = kzalloc(sizeof(struct gesture_module), GFP_KERNEL);
if (!gsx_gesture)
return -ENOMEM;
gsx_gesture->module.funcs = &gsx_gesture_funcs;
gsx_gesture->module.priority = EXTMOD_PRIO_GESTURE;
gsx_gesture->module.name = "Goodix_gsx_gesture";
gsx_gesture->module.priv_data = gsx_gesture;
atomic_set(&gsx_gesture->registered, 0);
/* gesture sysfs init */
ret = kobject_init_and_add(&gsx_gesture->module.kobj,
def_kobj_type, def_kobj, "gesture");
if (ret) {
ts_err("failed create gesture sysfs node!");
goto err_out;
}
for (i = 0; i < ARRAY_SIZE(gesture_attrs) && !ret; i++)
ret = sysfs_create_file(&gsx_gesture->module.kobj,
&gesture_attrs[i].attr);
if (ret) {
ts_err("failed create gst sysfs files");
while (--i >= 0)
sysfs_remove_file(&gsx_gesture->module.kobj,
&gesture_attrs[i].attr);
kobject_put(&gsx_gesture->module.kobj);
goto err_out;
}
module_initialized = true;
goodix_register_ext_module_no_wait(&gsx_gesture->module);
ts_info("gesture module init success");
return 0;
err_out:
ts_err("gesture module init failed!");
kfree(gsx_gesture);
return ret;
}
void gesture_module_exit(void)
{
int i;
ts_info("gesture module exit");
if (!module_initialized)
return;
goodix_unregister_ext_module(&gsx_gesture->module);
/* deinit sysfs */
for (i = 0; i < ARRAY_SIZE(gesture_attrs); i++)
sysfs_remove_file(&gsx_gesture->module.kobj,
&gesture_attrs[i].attr);
kobject_put(&gsx_gesture->module.kobj);
kfree(gsx_gesture);
module_initialized = false;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,503 @@
/*
* Goodix Touchscreen Driver
* Copyright (C) 2020 - 2021 Goodix, Inc.
*
* 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/module.h>
#include <linux/kernel.h>
#include <linux/atomic.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/ioctl.h>
#include <linux/wait.h>
#include "goodix_ts_core.h"
#define GOODIX_TOOLS_NAME "gtp_tools"
#define GOODIX_TOOLS_VER_MAJOR 1
#define GOODIX_TOOLS_VER_MINOR 0
static const u16 goodix_tools_ver = ((GOODIX_TOOLS_VER_MAJOR << 8) +
(GOODIX_TOOLS_VER_MINOR));
#define GOODIX_TS_IOC_MAGIC 'G'
#define NEGLECT_SIZE_MASK (~(_IOC_SIZEMASK << _IOC_SIZESHIFT))
#define GTP_IRQ_ENABLE _IO(GOODIX_TS_IOC_MAGIC, 0)
#define GTP_DEV_RESET _IO(GOODIX_TS_IOC_MAGIC, 1)
#define GTP_SEND_COMMAND (_IOW(GOODIX_TS_IOC_MAGIC, 2, u8) & NEGLECT_SIZE_MASK)
#define GTP_SEND_CONFIG (_IOW(GOODIX_TS_IOC_MAGIC, 3, u8) & NEGLECT_SIZE_MASK)
#define GTP_ASYNC_READ (_IOR(GOODIX_TS_IOC_MAGIC, 4, u8) & NEGLECT_SIZE_MASK)
#define GTP_SYNC_READ (_IOR(GOODIX_TS_IOC_MAGIC, 5, u8) & NEGLECT_SIZE_MASK)
#define GTP_ASYNC_WRITE (_IOW(GOODIX_TS_IOC_MAGIC, 6, u8) & NEGLECT_SIZE_MASK)
#define GTP_READ_CONFIG (_IOW(GOODIX_TS_IOC_MAGIC, 7, u8) & NEGLECT_SIZE_MASK)
#define GTP_ESD_ENABLE _IO(GOODIX_TS_IOC_MAGIC, 8)
#define GTP_TOOLS_VER (_IOR(GOODIX_TS_IOC_MAGIC, 9, u8) & NEGLECT_SIZE_MASK)
#define GTP_TOOLS_CTRL_SYNC (_IOW(GOODIX_TS_IOC_MAGIC, 10, u8) & NEGLECT_SIZE_MASK)
#define MAX_BUF_LENGTH (16*1024)
#define IRQ_FALG (0x01 << 2)
#define I2C_MSG_HEAD_LEN 20
/*
* struct goodix_tools_dev - goodix tools device struct
* @ts_core: The core data struct of ts driver
* @ops_mode: represent device work mode
* @rawdiffcmd: Set slave device into rawdata mode
* @normalcmd: Set slave device into normal mode
* @wq: Wait queue struct use in synchronous data read
* @mutex: Protect goodix_tools_dev
* @in_use: device in use
*/
struct goodix_tools_dev {
struct goodix_ts_core *ts_core;
struct list_head head;
unsigned int ops_mode;
struct goodix_ts_cmd rawdiffcmd, normalcmd;
wait_queue_head_t wq;
struct mutex mutex;
atomic_t in_use;
struct goodix_ext_module module;
} *goodix_tools_dev;
/* read data asynchronous,
* success return data length, otherwise return < 0
*/
static int async_read(struct goodix_tools_dev *dev, void __user *arg)
{
u8 *databuf = NULL;
int ret = 0;
u32 reg_addr, length;
u8 i2c_msg_head[I2C_MSG_HEAD_LEN];
const struct goodix_ts_hw_ops *hw_ops = dev->ts_core->hw_ops;
ret = copy_from_user(&i2c_msg_head, arg, I2C_MSG_HEAD_LEN);
if (ret)
return -EFAULT;
reg_addr = i2c_msg_head[0] + (i2c_msg_head[1] << 8)
+ (i2c_msg_head[2] << 16) + (i2c_msg_head[3] << 24);
length = i2c_msg_head[4] + (i2c_msg_head[5] << 8)
+ (i2c_msg_head[6] << 16) + (i2c_msg_head[7] << 24);
if (length > MAX_BUF_LENGTH) {
ts_err("buffer too long:%d > %d", length, MAX_BUF_LENGTH);
return -EINVAL;
}
databuf = kzalloc(length, GFP_KERNEL);
if (!databuf) {
ts_err("Alloc memory failed");
return -ENOMEM;
}
if (hw_ops->read(dev->ts_core, reg_addr, databuf, length)) {
ret = -EBUSY;
ts_err("Read i2c failed");
goto err_out;
}
ret = copy_to_user((u8 *)arg + I2C_MSG_HEAD_LEN, databuf, length);
if (ret) {
ret = -EFAULT;
ts_err("Copy_to_user failed");
goto err_out;
}
ret = length;
err_out:
kfree(databuf);
return ret;
}
/* if success return config data length */
static int read_config_data(struct goodix_ts_core *ts_core, void __user *arg)
{
int ret = 0;
u32 reg_addr, length;
u8 i2c_msg_head[I2C_MSG_HEAD_LEN];
u8 *tmp_buf;
ret = copy_from_user(&i2c_msg_head, arg, I2C_MSG_HEAD_LEN);
if (ret) {
ts_err("Copy data from user failed");
return -EFAULT;
}
reg_addr = i2c_msg_head[0] + (i2c_msg_head[1] << 8)
+ (i2c_msg_head[2] << 16) + (i2c_msg_head[3] << 24);
length = i2c_msg_head[4] + (i2c_msg_head[5] << 8)
+ (i2c_msg_head[6] << 16) + (i2c_msg_head[7] << 24);
ts_info("read config,reg_addr=0x%x, length=%d", reg_addr, length);
if (length > MAX_BUF_LENGTH) {
ts_err("buffer too long:%d > %d", length, MAX_BUF_LENGTH);
return -EINVAL;
}
tmp_buf = kzalloc(length, GFP_KERNEL);
if (!tmp_buf) {
ts_err("failed alloc memory");
return -ENOMEM;
}
/* if reg_addr == 0, read config data with specific flow */
if (!reg_addr) {
if (ts_core->hw_ops->read_config)
ret = ts_core->hw_ops->read_config(ts_core, tmp_buf, length);
else
ret = -EINVAL;
} else {
ret = ts_core->hw_ops->read(ts_core, reg_addr, tmp_buf, length);
if (!ret)
ret = length;
}
if (ret <= 0)
goto err_out;
if (copy_to_user((u8 *)arg + I2C_MSG_HEAD_LEN, tmp_buf, ret)) {
ret = -EFAULT;
ts_err("Copy_to_user failed");
}
err_out:
kfree(tmp_buf);
return ret;
}
/* write data to i2c asynchronous,
* success return bytes write, else return <= 0
*/
static int async_write(struct goodix_tools_dev *dev, void __user *arg)
{
u8 *databuf;
int ret = 0;
u32 reg_addr, length;
u8 i2c_msg_head[I2C_MSG_HEAD_LEN];
struct goodix_ts_core *ts_core = dev->ts_core;
const struct goodix_ts_hw_ops *hw_ops = ts_core->hw_ops;
ret = copy_from_user(&i2c_msg_head, arg, I2C_MSG_HEAD_LEN);
if (ret) {
ts_err("Copy data from user failed");
return -EFAULT;
}
reg_addr = i2c_msg_head[0] + (i2c_msg_head[1] << 8)
+ (i2c_msg_head[2] << 16) + (i2c_msg_head[3] << 24);
length = i2c_msg_head[4] + (i2c_msg_head[5] << 8)
+ (i2c_msg_head[6] << 16) + (i2c_msg_head[7] << 24);
if (length > MAX_BUF_LENGTH) {
ts_err("buffer too long:%d > %d", length, MAX_BUF_LENGTH);
return -EINVAL;
}
databuf = kzalloc(length, GFP_KERNEL);
if (!databuf) {
ts_err("Alloc memory failed");
return -ENOMEM;
}
ret = copy_from_user(databuf, (u8 *)arg + I2C_MSG_HEAD_LEN, length);
if (ret) {
ret = -EFAULT;
ts_err("Copy data from user failed");
goto err_out;
}
if (hw_ops->write(ts_core, reg_addr, databuf, length)) {
ret = -EBUSY;
ts_err("Write data to device failed");
} else {
ret = length;
}
err_out:
kfree(databuf);
return ret;
}
static int init_cfg_data(struct goodix_ic_config *cfg, void __user *arg)
{
int ret = 0;
u32 length;
u8 i2c_msg_head[I2C_MSG_HEAD_LEN] = {0};
ret = copy_from_user(&i2c_msg_head, arg, I2C_MSG_HEAD_LEN);
if (ret) {
ts_err("Copy data from user failed");
return -EFAULT;
}
length = i2c_msg_head[4] + (i2c_msg_head[5] << 8)
+ (i2c_msg_head[6] << 16) + (i2c_msg_head[7] << 24);
if (length > GOODIX_CFG_MAX_SIZE) {
ts_err("buffer too long:%d > %d", length, MAX_BUF_LENGTH);
return -EINVAL;
}
ret = copy_from_user(cfg->data, (u8 *)arg + I2C_MSG_HEAD_LEN, length);
if (ret) {
ts_err("Copy data from user failed");
return -EFAULT;
}
cfg->len = length;
return 0;
}
/**
* goodix_tools_ioctl - ioctl implementation
*
* @filp: Pointer to file opened
* @cmd: Ioctl opertion command
* @arg: Command data
* Returns >=0 - succeed, else failed
*/
static long goodix_tools_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
int ret = 0;
struct goodix_tools_dev *dev = filp->private_data;
struct goodix_ts_core *ts_core;
const struct goodix_ts_hw_ops *hw_ops;
struct goodix_ic_config *temp_cfg = NULL;
if (!dev->ts_core) {
ts_err("Tools module not register");
return -EINVAL;
}
ts_core = dev->ts_core;
hw_ops = ts_core->hw_ops;
if (_IOC_TYPE(cmd) != GOODIX_TS_IOC_MAGIC) {
ts_err("Bad magic num:%c", _IOC_TYPE(cmd));
return -ENOTTY;
}
switch (cmd & NEGLECT_SIZE_MASK) {
case GTP_IRQ_ENABLE:
if (arg == 1) {
hw_ops->irq_enable(ts_core, true);
mutex_lock(&dev->mutex);
dev->ops_mode |= IRQ_FALG;
mutex_unlock(&dev->mutex);
ts_info("IRQ enabled");
} else if (arg == 0) {
hw_ops->irq_enable(ts_core, false);
mutex_lock(&dev->mutex);
dev->ops_mode &= ~IRQ_FALG;
mutex_unlock(&dev->mutex);
ts_info("IRQ disabled");
} else {
ts_info("Irq aready set with, arg = %ld", arg);
}
ret = 0;
break;
case GTP_ESD_ENABLE:
if (arg == 0)
goodix_ts_blocking_notify(NOTIFY_ESD_OFF, NULL);
else
goodix_ts_blocking_notify(NOTIFY_ESD_ON, NULL);
break;
case GTP_DEV_RESET:
hw_ops->reset(ts_core, GOODIX_NORMAL_RESET_DELAY_MS);
break;
case GTP_SEND_COMMAND:
/* deprecated command */
ts_err("the GTP_SEND_COMMAND function has been removed");
ret = -EINVAL;
break;
case GTP_SEND_CONFIG:
temp_cfg = kzalloc(sizeof(struct goodix_ic_config), GFP_KERNEL);
if (!temp_cfg) {
ts_err("Memory allco err");
ret = -ENOMEM;
goto err_out;
}
ret = init_cfg_data(temp_cfg, (void __user *)arg);
if (!ret && hw_ops->send_config) {
ret = hw_ops->send_config(ts_core, temp_cfg->data, temp_cfg->len);
if (ret) {
ts_err("Failed send config");
ret = -EAGAIN;
} else {
ts_info("Send config success");
ret = 0;
}
}
kfree(temp_cfg);
temp_cfg = NULL;
break;
case GTP_READ_CONFIG:
ret = read_config_data(ts_core, (void __user *)arg);
if (ret > 0)
ts_info("success read config:len=%d", ret);
else
ts_err("failed read config:ret=0x%x", ret);
break;
case GTP_ASYNC_READ:
ret = async_read(dev, (void __user *)arg);
if (ret < 0)
ts_err("Async data read failed");
break;
case GTP_SYNC_READ:
ts_info("unsupport sync read");
break;
case GTP_ASYNC_WRITE:
ret = async_write(dev, (void __user *)arg);
if (ret < 0)
ts_err("Async data write failed");
break;
case GTP_TOOLS_VER:
ret = copy_to_user((u8 *)arg, &goodix_tools_ver, sizeof(u16));
if (ret)
ts_err("failed copy driver version info to user");
break;
case GTP_TOOLS_CTRL_SYNC:
ts_core->tools_ctrl_sync = !!arg;
ts_info("set tools ctrl sync %d", ts_core->tools_ctrl_sync);
break;
default:
ts_info("Invalid cmd");
ret = -ENOTTY;
break;
}
err_out:
return ret;
}
#ifdef CONFIG_COMPAT
static long goodix_tools_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
void __user *arg32 = compat_ptr(arg);
if (!file->f_op || !file->f_op->unlocked_ioctl)
return -ENOTTY;
return file->f_op->unlocked_ioctl(file, cmd, (unsigned long)arg32);
}
#endif
static int goodix_tools_open(struct inode *inode, struct file *filp)
{
int ret = 0;
ts_info("try open tool");
/* Only the first time open device need to register module */
ret = goodix_register_ext_module_no_wait(&goodix_tools_dev->module);
if (ret) {
ts_info("failed register to core module");
return -EFAULT;
}
ts_info("success open tools");
goodix_ts_blocking_notify(NOTIFY_ESD_OFF, NULL);
filp->private_data = goodix_tools_dev;
atomic_set(&goodix_tools_dev->in_use, 1);
return 0;
}
static int goodix_tools_release(struct inode *inode, struct file *filp)
{
int ret = 0;
/* when the last close this dev node unregister the module */
goodix_tools_dev->ts_core->tools_ctrl_sync = false;
atomic_set(&goodix_tools_dev->in_use, 0);
goodix_ts_blocking_notify(NOTIFY_ESD_ON, NULL);
ret = goodix_unregister_ext_module(&goodix_tools_dev->module);
return ret;
}
static int goodix_tools_module_init(struct goodix_ts_core *core_data,
struct goodix_ext_module *module)
{
struct goodix_tools_dev *tools_dev = module->priv_data;
if (core_data)
tools_dev->ts_core = core_data;
else
return -ENODEV;
return 0;
}
static int goodix_tools_module_exit(struct goodix_ts_core *core_data,
struct goodix_ext_module *module)
{
struct goodix_tools_dev *tools_dev = module->priv_data;
ts_debug("tools module unregister");
if (atomic_read(&tools_dev->in_use)) {
ts_err("tools module busy, please close it then retry");
return -EBUSY;
}
return 0;
}
static const struct file_operations goodix_tools_fops = {
.owner = THIS_MODULE,
.open = goodix_tools_open,
.release = goodix_tools_release,
.unlocked_ioctl = goodix_tools_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = goodix_tools_compat_ioctl,
#endif
};
static struct miscdevice goodix_tools_miscdev = {
.minor = MISC_DYNAMIC_MINOR,
.name = GOODIX_TOOLS_NAME,
.fops = &goodix_tools_fops,
};
static struct goodix_ext_module_funcs goodix_tools_module_funcs = {
.init = goodix_tools_module_init,
.exit = goodix_tools_module_exit,
};
/**
* goodix_tools_init - init goodix tools device and register a miscdevice
*
* return: 0 success, else failed
*/
int goodix_tools_init(void)
{
int ret;
goodix_tools_dev = kzalloc(sizeof(struct goodix_tools_dev), GFP_KERNEL);
if (goodix_tools_dev == NULL) {
ts_err("Memory allco err");
return -ENOMEM;
}
INIT_LIST_HEAD(&goodix_tools_dev->head);
goodix_tools_dev->ops_mode = 0;
goodix_tools_dev->ops_mode |= IRQ_FALG;
init_waitqueue_head(&goodix_tools_dev->wq);
mutex_init(&goodix_tools_dev->mutex);
atomic_set(&goodix_tools_dev->in_use, 0);
goodix_tools_dev->module.funcs = &goodix_tools_module_funcs;
goodix_tools_dev->module.name = GOODIX_TOOLS_NAME;
goodix_tools_dev->module.priv_data = goodix_tools_dev;
goodix_tools_dev->module.priority = EXTMOD_PRIO_DBGTOOL;
ret = misc_register(&goodix_tools_miscdev);
if (ret)
ts_err("Debug tools miscdev register failed");
else
ts_info("Debug tools miscdev register success");
return ret;
}
void goodix_tools_exit(void)
{
misc_deregister(&goodix_tools_miscdev);
kfree(goodix_tools_dev);
ts_info("Debug tools miscdev exit");
}

View File

@ -0,0 +1,179 @@
/*
* Goodix Touchscreen Driver
* Copyright (C) 2020 - 2021 Goodix, Inc.
*
* 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 "goodix_ts_core.h"
bool debug_log_flag = false;
/*****************************************************************************
* goodix_append_checksum
* @summary
* Calcualte data checksum with the specified mode.
*
* @param data
* data need to be calculate
* @param len
* data length
* @param mode
* calculate for u8 or u16 checksum
* @return
* return the data checksum value.
*
*****************************************************************************/
u32 goodix_append_checksum(u8 *data, int len, int mode)
{
u32 checksum = 0;
int i;
checksum = 0;
if (mode == CHECKSUM_MODE_U8_LE) {
for (i = 0; i < len; i++)
checksum += data[i];
} else {
for (i = 0; i < len; i+=2)
checksum += (data[i] + (data[i+1] << 8));
}
if (mode == CHECKSUM_MODE_U8_LE) {
data[len] = checksum & 0xff;
data[len + 1] = (checksum >> 8) & 0xff;
return 0xFFFF & checksum;
}
data[len] = checksum & 0xff;
data[len + 1] = (checksum >> 8) & 0xff;
data[len + 2] = (checksum >> 16) & 0xff;
data[len + 3] = (checksum >> 24) & 0xff;
return checksum;
}
/* checksum_cmp: check data valid or not
* @data: data need to be check
* @size: data length need to be check(include the checksum bytes)
* @mode: compare with U8 or U16 mode
* */
int checksum_cmp(const u8 *data, int size, int mode)
{
u32 cal_checksum = 0;
u32 r_checksum = 0;
u32 i;
if (mode == CHECKSUM_MODE_U8_LE) {
if (size < 2)
return 1;
for (i = 0; i < size - 2; i++)
cal_checksum += data[i];
r_checksum = data[size - 2] + (data[size - 1] << 8);
return (cal_checksum & 0xFFFF) == r_checksum ? 0 : 1;
}
if (size < 4)
return 1;
for (i = 0; i < size - 4; i += 2)
cal_checksum += data[i] + (data[i + 1] << 8);
r_checksum = data[size - 4] + (data[size - 3] << 8) +
(data[size - 2] << 16) + (data[size - 1] << 24);
return cal_checksum == r_checksum ? 0 : 1;
}
/* return 1 if all data is zero or ff
* else return 0
*/
int is_risk_data(const u8 *data, int size)
{
int i;
int zero_count = 0;
int ff_count = 0;
for (i = 0; i < size; i++) {
if (data[i] == 0)
zero_count++;
else if (data[i] == 0xff)
ff_count++;
}
if (zero_count == size || ff_count == size) {
ts_info("warning data is all %s\n",
zero_count == size ? "zero" : "0xff");
return 1;
}
return 0;
}
/* get config id form config file */
#define CONFIG_ID_OFFSET 30
u32 goodix_get_file_config_id(u8 *ic_config)
{
if (!ic_config)
return 0;
return le32_to_cpup((__le32 *)&ic_config[CONFIG_ID_OFFSET]);
}
/* matrix transpose */
void goodix_rotate_abcd2cbad(int tx, int rx, s16 *data)
{
s16 *temp_buf = NULL;
int size = tx * rx;
int i;
int j;
int col;
temp_buf = kcalloc(size, sizeof(s16), GFP_KERNEL);
if (!temp_buf) {
ts_err("malloc failed");
return;
}
for (i = 0, j = 0, col = 0; i < size; i++) {
temp_buf[i] = data[j++ * rx + col];
if (j == tx) {
j = 0;
col++;
}
}
memcpy(data, temp_buf, size * sizeof(s16));
kfree(temp_buf);
}
/* get ic type */
int goodix_get_ic_type(struct device_node *node)
{
const char *name_tmp;
int ret;
ret = of_property_read_string(node, "compatible", &name_tmp);
if (ret < 0) {
ts_err("get compatible failed");
return ret;
}
if (strstr(name_tmp, "9897")) {
ts_info("ic type is BerlinA");
ret = IC_TYPE_BERLIN_A;
} else if (strstr(name_tmp, "9966") || strstr(name_tmp, "7986")) {
ts_info("ic type is BerlinB");
ret = IC_TYPE_BERLIN_B;
} else if (strstr(name_tmp, "9916")) {
ts_info("ic type is BerlinD");
ret = IC_TYPE_BERLIN_D;
} else {
ts_info("can't find valid ic_type");
ret = -EINVAL;
}
return ret;
}