Merge "touch: goodix: fix coding warnings and porting touch fixes"
This commit is contained in:
commit
6ef0449e73
@ -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
|
||||
|
@ -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/
|
||||
|
20
drivers/input/touchscreen/goodix_berlin_driver/Kconfig
Normal file
20
drivers/input/touchscreen/goodix_berlin_driver/Kconfig
Normal 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
|
339
drivers/input/touchscreen/goodix_berlin_driver/LICENSE
Normal file
339
drivers/input/touchscreen/goodix_berlin_driver/LICENSE
Normal 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.
|
12
drivers/input/touchscreen/goodix_berlin_driver/Makefile
Normal file
12
drivers/input/touchscreen/goodix_berlin_driver/Makefile
Normal 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
|
@ -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";
|
||||
};
|
||||
```
|
||||
|
1358
drivers/input/touchscreen/goodix_berlin_driver/goodix_brl_fwupdate.c
Normal file
1358
drivers/input/touchscreen/goodix_berlin_driver/goodix_brl_fwupdate.c
Normal file
File diff suppressed because it is too large
Load Diff
1460
drivers/input/touchscreen/goodix_berlin_driver/goodix_brl_hw.c
Normal file
1460
drivers/input/touchscreen/goodix_berlin_driver/goodix_brl_hw.c
Normal file
File diff suppressed because it is too large
Load Diff
261
drivers/input/touchscreen/goodix_berlin_driver/goodix_brl_i2c.c
Normal file
261
drivers/input/touchscreen/goodix_berlin_driver/goodix_brl_i2c.c
Normal 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);
|
||||
}
|
298
drivers/input/touchscreen/goodix_berlin_driver/goodix_brl_spi.c
Normal file
298
drivers/input/touchscreen/goodix_berlin_driver/goodix_brl_spi.c
Normal 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);
|
||||
}
|
343
drivers/input/touchscreen/goodix_berlin_driver/goodix_cfg_bin.c
Normal file
343
drivers/input/touchscreen/goodix_berlin_driver/goodix_cfg_bin.c
Normal 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);
|
||||
}
|
||||
|
||||
|
2459
drivers/input/touchscreen/goodix_berlin_driver/goodix_ts_core.c
Normal file
2459
drivers/input/touchscreen/goodix_berlin_driver/goodix_ts_core.c
Normal file
File diff suppressed because it is too large
Load Diff
692
drivers/input/touchscreen/goodix_berlin_driver/goodix_ts_core.h
Normal file
692
drivers/input/touchscreen/goodix_berlin_driver/goodix_ts_core.h
Normal 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
|
@ -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;
|
||||
}
|
2972
drivers/input/touchscreen/goodix_berlin_driver/goodix_ts_inspect.c
Normal file
2972
drivers/input/touchscreen/goodix_berlin_driver/goodix_ts_inspect.c
Normal file
File diff suppressed because it is too large
Load Diff
503
drivers/input/touchscreen/goodix_berlin_driver/goodix_ts_tools.c
Normal file
503
drivers/input/touchscreen/goodix_berlin_driver/goodix_ts_tools.c
Normal 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");
|
||||
}
|
179
drivers/input/touchscreen/goodix_berlin_driver/goodix_ts_utils.c
Normal file
179
drivers/input/touchscreen/goodix_berlin_driver/goodix_ts_utils.c
Normal 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user