Added gslx680-acpi sources module.
This commit is contained in:
parent
583cffdafc
commit
d5c6d8adb0
10
gslx680-acpi/CHANGELOG
Normal file
10
gslx680-acpi/CHANGELOG
Normal file
@ -0,0 +1,10 @@
|
||||
0.2.1 (2016-04-28 onitake):
|
||||
|
||||
- Changed versioning scheme (2.0.0 becomes 0.2.0)
|
||||
- Fixed DeviceTree support
|
||||
- Added DKMS script
|
||||
- Added module parameter for specifying firmware name
|
||||
- Fixed cross compilation
|
||||
- Improved wakeup pin control (ACPI/GPIO)
|
||||
- Moved firmware and tools to https://github.com/onitake/gsl-firmware
|
||||
- Improved handling of panel dimensions
|
339
gslx680-acpi/LICENSE
Normal file
339
gslx680-acpi/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.
|
29
gslx680-acpi/Makefile
Normal file
29
gslx680-acpi/Makefile
Normal file
@ -0,0 +1,29 @@
|
||||
MODULE_NAME = gslx680_ts_acpi
|
||||
|
||||
#CROSS_COMPILE ?= arm-linux-gnueabihf-
|
||||
#ARCH ?= arm
|
||||
ARCH := $(shell uname -m | sed -e s/i.86/i386/)
|
||||
KVER := $(shell uname -r)
|
||||
KSRC := /lib/modules/$(KVER)/build
|
||||
PWD = $(shell pwd)
|
||||
MODDESTDIR := /lib/modules/$(KVER)/kernel/drivers/input/touchscreen
|
||||
|
||||
obj-m += gslx680_ts_acpi.o
|
||||
|
||||
.PHONY: all modules clean
|
||||
|
||||
all: modules
|
||||
|
||||
modules:
|
||||
make -C $(KSRC) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) M=$(PWD) modules
|
||||
|
||||
install:
|
||||
install -p -m 644 $(MODULE_NAME).ko $(MODDESTDIR)
|
||||
/sbin/depmod -a $(KVER)
|
||||
|
||||
uninstall:
|
||||
rm -f $(MODDESTDIR)/$(MODULE_NAME).ko
|
||||
/sbin/depmod -a $(KVER)
|
||||
|
||||
clean:
|
||||
make -C $(KSRC) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) M=$(PWD) clean
|
81
gslx680-acpi/README.md
Normal file
81
gslx680-acpi/README.md
Normal file
@ -0,0 +1,81 @@
|
||||
About
|
||||
-----
|
||||
|
||||
This is a generic Linux kernel driver for the Silead GSLx68y
|
||||
series of touch screen controllers.
|
||||
It is currently designed to work on ACPI platforms, but
|
||||
support for DeviceTree/OpenFirmware is also in the works.
|
||||
|
||||
The code was adapted from the platform specific driver here:
|
||||
https://github.com/jabjoe/sunxi-gslx680
|
||||
|
||||
Kernel-based finger tracking is available and can be enabled if
|
||||
the hardware doesn't support it. It works reasonably well,
|
||||
but touches close to the edges are not registered reliably,
|
||||
and dragging is very inaccurate.
|
||||
|
||||
|
||||
Firmware Instructions
|
||||
---------------------
|
||||
|
||||
The controller requires firmware to work properly. Firmware
|
||||
images extracted from vendor drivers are maintained in a separate
|
||||
repository: https://github.com/onitake/gsl-firmware
|
||||
|
||||
If your device is not mentioned yet, or the required silead_ts.fw
|
||||
is not available, please post a request in the issue tracker there,
|
||||
or consult [gsl-firmware/README.md](https://github.com/onitake/gsl-firmware/blob/master/README.md)
|
||||
for information on how to obtain the firmware yourself.
|
||||
|
||||
|
||||
Build Instructions
|
||||
------------------
|
||||
|
||||
If you don't need to cross compile, just make sure you have headers
|
||||
for your running Linux kernel installed, then type
|
||||
|
||||
make
|
||||
|
||||
This will produce gslx680_ts_acpi.ko
|
||||
|
||||
If you need to cross compile, pass appropriate KSRC, ARCH and
|
||||
CROSS_COMPILE variables to the make command. For example:
|
||||
|
||||
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KSRC=../linux-arm
|
||||
|
||||
CROSS_COMPILE is the compiler prefix (i.e. gcc will become
|
||||
arm-linux-gnueabihf-gcc with CROSS_COMPILE=arm-linux-gnueabihf-), ARCH
|
||||
is the target architecture as understood by the kernel (note: use i386
|
||||
for 32 bit Intel platforms) and KSRC is the path to the target
|
||||
kernel sources or kernel headers.
|
||||
|
||||
|
||||
Install Instructions
|
||||
--------------------
|
||||
|
||||
Install appropriate firmware for your device, as per the Firmware
|
||||
Instructions above.
|
||||
|
||||
Load and test the driver with
|
||||
|
||||
insmod ./gslx680_ts_acpi.ko
|
||||
|
||||
Running dmesg should produce some output if the device was matched
|
||||
by the driver. You should also see a message from the input
|
||||
subsystem that a new input device was added.
|
||||
|
||||
You may then observe the output from evtest. X.org touchscreen input
|
||||
should work too, but you will notice that the touch points are
|
||||
off if the panel width and height were not set accurately.
|
||||
|
||||
This can be fixed by installing xinput_calibrator and using it to
|
||||
generate a configuration file for your touchscreen. Some desktop
|
||||
environments may offer their own touchscreen calibrator,
|
||||
which you can also use.
|
||||
|
||||
xinput_calibrator, when run from an X terminal, will present a
|
||||
series of points on the screen. Touch each of them when prompted,
|
||||
then save the configuration printed to the terminal to the
|
||||
indicated location.
|
||||
|
||||
After restarting X, you should have a working touchscreen.
|
420
gslx680-acpi/acpi/gsl-dsdt.aml
Normal file
420
gslx680-acpi/acpi/gsl-dsdt.aml
Normal file
@ -0,0 +1,420 @@
|
||||
Device (TCS5)
|
||||
{
|
||||
Name (_ADR, Zero) // _ADR: Address
|
||||
Name (_HID, "MSSL1680") // _HID: Hardware ID
|
||||
Name (_CID, "PNP1680") // _CID: Compatible ID
|
||||
Name (_S0W, Zero) // _S0W: S0 Device Wake State
|
||||
Name (_DEP, Package (0x02) // _DEP: Dependencies
|
||||
{
|
||||
GPO1,
|
||||
I2C5
|
||||
})
|
||||
Method (_PS3, 0, Serialized) // _PS3: Power State 3
|
||||
{
|
||||
If (LEqual (^^^GPO0.AVBL, One))
|
||||
{
|
||||
// TCON = Touchscreen On, SHUTDOWN pin, Deepsleep on low
|
||||
Store (Zero, ^^^GPO0.TCON) /* \_SB_.GPO0.TCON */
|
||||
}
|
||||
}
|
||||
|
||||
Method (_PS0, 0, Serialized) // _PS0: Power State 0
|
||||
{
|
||||
If (LEqual (^^^GPO1.AVBL, One))
|
||||
{
|
||||
// TCD3 = Touchscreen 3.3V or 1.8V?, power gate? -> Power-on reset on low?
|
||||
Store (Zero, ^^^GPO1.TCD3) /* \_SB_.GPO1.TCD3 */
|
||||
}
|
||||
|
||||
Sleep (0x05)
|
||||
If (LEqual (^^^GPO0.AVBL, One))
|
||||
{
|
||||
// TCON = Touchscreen On, SHUTDOWN pin, Wakeup on high
|
||||
Store (One, ^^^GPO0.TCON) /* \_SB_.GPO0.TCON */
|
||||
}
|
||||
|
||||
Sleep (0x1E)
|
||||
If (LEqual (^^^GPO1.AVBL, One))
|
||||
{
|
||||
// TCD3 = Touchscreen 3.3V or 1.8V?, power gate? -> Reset complete on high?
|
||||
Store (One, ^^^GPO1.TCD3) /* \_SB_.GPO1.TCD3 */
|
||||
}
|
||||
|
||||
Sleep (0x78)
|
||||
}
|
||||
|
||||
Method (_CRS, 0, NotSerialized) // _CRS: Current Resource Settings
|
||||
{
|
||||
Name (RBUF, ResourceTemplate ()
|
||||
{
|
||||
// I2CSerialBusV2 ( SlaveAddress=_ADR , SlaveMode=_SLV , ConnectionSpeed=_SPE , AddressingMode=_MOD , ResourceSource ,
|
||||
// ResourceSourceIndex , ResourceUsage , DescriptorName , Shared=_SHR, VendorData=_VEN )
|
||||
// Interpretation: Device 0x40 (=64) on I2C controller \\_SB.I2C4, 7bit addressing, pure slave, 400kHz clock
|
||||
I2cSerialBus (0x0040, ControllerInitiated, 0x00061A80,
|
||||
AddressingMode7Bit, "\\_SB.I2C4",
|
||||
0x00, ResourceConsumer, ,
|
||||
)
|
||||
// Interrupt (ResourceUsage, EdgeLevel=_HE, ActiveLevel=_LL, Shared=_SHR, ResourceSourceIndex,
|
||||
// ResourceSource, DescriptorName) { InterruptList=_INT } => Buffer
|
||||
// Interpretation: Maps to APIC Interrupt 0x44 (=70)
|
||||
Interrupt (ResourceConsumer, Edge, ActiveHigh, Exclusive, ,, )
|
||||
{
|
||||
0x00000044,
|
||||
}
|
||||
// GpioIo ( Shared=_SHR, PinConfig=_PPI, DebounceTimeout=_DBT, DriveStrength=_DRS, IORestriction=_IOR, ResourceSource,
|
||||
// ResourceSourceIndex, ResourceUsage, DescriptorName, VendorData=_VEN ) {PinList=_PIN}
|
||||
// Interpretation: Output GPIO pin, controller \\_SB.GPO1, pin number 0x1a (=26)
|
||||
GpioIo (Shared, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly,
|
||||
"\\_SB.GPO1", 0x00, ResourceConsumer, ,
|
||||
)
|
||||
{ // Pin list
|
||||
0x001A
|
||||
}
|
||||
// Interpretation: Bidrectional GPIO pin, controller \\_SB.GPO1, pin number 0x03 (=3)
|
||||
GpioIo (Shared, PullDefault, 0x0000, 0x0000, IoRestrictionNone,
|
||||
"\\_SB.GPO2", 0x00, ResourceConsumer, ,
|
||||
)
|
||||
{ // Pin list
|
||||
0x0003
|
||||
}
|
||||
})
|
||||
Return (RBUF) /* \_SB_.I2C4.TCS5._CRS.RBUF */
|
||||
}
|
||||
|
||||
Method (_STA, 0, NotSerialized) // _STA: Status
|
||||
{
|
||||
If (LEqual (TPID, 0x04))
|
||||
{
|
||||
Return (0x0F)
|
||||
}
|
||||
|
||||
Return (Zero)
|
||||
}
|
||||
}
|
||||
|
||||
Device (GPO0)
|
||||
{
|
||||
Name (_ADR, Zero) // _ADR: Address
|
||||
Name (_HID, "INT33FC" /* Intel Baytrail GPIO Controller */) // _HID: Hardware ID
|
||||
Name (_CID, "INT33FC" /* Intel Baytrail GPIO Controller */) // _CID: Compatible ID
|
||||
Name (_DDN, "ValleyView General Purpose Input/Output (GPIO) controller") // _DDN: DOS Device Name
|
||||
Name (_UID, One) // _UID: Unique ID
|
||||
Method (_CRS, 0, NotSerialized) // _CRS: Current Resource Settings
|
||||
{
|
||||
Name (RBUF, ResourceTemplate ()
|
||||
{
|
||||
Memory32Fixed (ReadWrite,
|
||||
0xFED0C000, // Address Base
|
||||
0x00001000, // Address Length
|
||||
)
|
||||
Interrupt (ResourceConsumer, Level, ActiveLow, Shared, ,, )
|
||||
{
|
||||
0x00000031,
|
||||
}
|
||||
})
|
||||
Return (RBUF) /* \_SB_.GPO0._CRS.RBUF */
|
||||
}
|
||||
|
||||
Method (_STA, 0, NotSerialized) // _STA: Status
|
||||
{
|
||||
Return (0x0F)
|
||||
}
|
||||
|
||||
Name (AVBL, Zero)
|
||||
Method (_REG, 2, NotSerialized) // _REG: Region Availability
|
||||
{
|
||||
If (LEqual (Arg0, 0x08))
|
||||
{
|
||||
Store (Arg1, AVBL) /* \_SB_.GPO0.AVBL */
|
||||
}
|
||||
}
|
||||
|
||||
OperationRegion (GPOP, GeneralPurposeIo, Zero, 0x0C)
|
||||
Field (GPOP, ByteAcc, NoLock, Preserve)
|
||||
{
|
||||
Connection (
|
||||
GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly,
|
||||
"\\_SB.GPO0", 0x00, ResourceConsumer, ,
|
||||
)
|
||||
{ // Pin list
|
||||
0x0002
|
||||
}
|
||||
),
|
||||
CCU2, 1,
|
||||
Connection (
|
||||
GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly,
|
||||
"\\_SB.GPO0", 0x00, ResourceConsumer, ,
|
||||
)
|
||||
{ // Pin list
|
||||
0x0003
|
||||
}
|
||||
),
|
||||
CCU3, 1,
|
||||
Connection (
|
||||
GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly,
|
||||
"\\_SB.GPO0", 0x00, ResourceConsumer, ,
|
||||
)
|
||||
{ // Pin list
|
||||
0x005F
|
||||
}
|
||||
),
|
||||
TCON, 1,
|
||||
Connection (
|
||||
GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly,
|
||||
"\\_SB.GPO0", 0x00, ResourceConsumer, ,
|
||||
)
|
||||
{ // Pin list
|
||||
0x0064
|
||||
}
|
||||
),
|
||||
WWD3, 1
|
||||
}
|
||||
}
|
||||
|
||||
Device (GPO1)
|
||||
{
|
||||
Name (_ADR, Zero) // _ADR: Address
|
||||
Name (_HID, "INT33FC" /* Intel Baytrail GPIO Controller */) // _HID: Hardware ID
|
||||
Name (_CID, "INT33FC" /* Intel Baytrail GPIO Controller */) // _CID: Compatible ID
|
||||
Name (_DDN, "ValleyView GPNCORE controller") // _DDN: DOS Device Name
|
||||
Name (_UID, 0x02) // _UID: Unique ID
|
||||
Method (_CRS, 0, NotSerialized) // _CRS: Current Resource Settings
|
||||
{
|
||||
Name (RBUF, ResourceTemplate ()
|
||||
{
|
||||
Memory32Fixed (ReadWrite,
|
||||
0xFED0D000, // Address Base
|
||||
0x00001000, // Address Length
|
||||
)
|
||||
Interrupt (ResourceConsumer, Level, ActiveLow, Shared, ,, )
|
||||
{
|
||||
0x00000030,
|
||||
}
|
||||
})
|
||||
Return (RBUF) /* \_SB_.GPO1._CRS.RBUF */
|
||||
}
|
||||
|
||||
Name (AVBL, Zero)
|
||||
Method (_REG, 2, NotSerialized) // _REG: Region Availability
|
||||
{
|
||||
If (LEqual (Arg0, 0x08))
|
||||
{
|
||||
Store (Arg1, AVBL) /* \_SB_.GPO1.AVBL */
|
||||
}
|
||||
}
|
||||
|
||||
OperationRegion (GPOP, GeneralPurposeIo, Zero, 0x0C)
|
||||
Field (GPOP, ByteAcc, NoLock, Preserve)
|
||||
{
|
||||
Connection (
|
||||
GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly,
|
||||
"\\_SB.GPO1", 0x00, ResourceConsumer, ,
|
||||
)
|
||||
{ // Pin list
|
||||
0x000F
|
||||
}
|
||||
),
|
||||
BST5, 1,
|
||||
Connection (
|
||||
GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly,
|
||||
"\\_SB.GPO1", 0x00, ResourceConsumer, ,
|
||||
)
|
||||
{ // Pin list
|
||||
0x001A
|
||||
}
|
||||
),
|
||||
TCD3, 1
|
||||
}
|
||||
|
||||
Method (_STA, 0, NotSerialized) // _STA: Status
|
||||
{
|
||||
Return (0x0F)
|
||||
}
|
||||
}
|
||||
|
||||
Device (GPO2)
|
||||
{
|
||||
Name (_ADR, Zero) // _ADR: Address
|
||||
Name (_HID, "INT33FC" /* Intel Baytrail GPIO Controller */) // _HID: Hardware ID
|
||||
Name (_CID, "INT33FC" /* Intel Baytrail GPIO Controller */) // _CID: Compatible ID
|
||||
Name (_DDN, "ValleyView GPSUS controller") // _DDN: DOS Device Name
|
||||
Name (_UID, 0x03) // _UID: Unique ID
|
||||
Method (_CRS, 0, NotSerialized) // _CRS: Current Resource Settings
|
||||
{
|
||||
Name (RBUF, ResourceTemplate ()
|
||||
{
|
||||
Memory32Fixed (ReadWrite,
|
||||
0xFED0E000, // Address Base
|
||||
0x00001000, // Address Length
|
||||
)
|
||||
Interrupt (ResourceConsumer, Level, ActiveLow, Shared, ,, )
|
||||
{
|
||||
0x00000032,
|
||||
}
|
||||
})
|
||||
Return (RBUF) /* \_SB_.GPO2._CRS.RBUF */
|
||||
}
|
||||
|
||||
Method (_STA, 0, NotSerialized) // _STA: Status
|
||||
{
|
||||
Return (0x0F)
|
||||
}
|
||||
|
||||
Method (_AEI, 0, NotSerialized) // _AEI: ACPI Event Interrupts
|
||||
{
|
||||
Name (RBUF, ResourceTemplate ()
|
||||
{
|
||||
GpioInt (Edge, ActiveLow, ExclusiveAndWake, PullUp, 0x0000,
|
||||
"\\_SB.GPO2", 0x00, ResourceConsumer, ,
|
||||
)
|
||||
{ // Pin list
|
||||
0x0012
|
||||
}
|
||||
GpioInt (Edge, ActiveLow, ExclusiveAndWake, PullUp, 0x0000,
|
||||
"\\_SB.GPO2", 0x00, ResourceConsumer, ,
|
||||
)
|
||||
{ // Pin list
|
||||
0x0002
|
||||
}
|
||||
GpioInt (Edge, ActiveBoth, ExclusiveAndWake, PullDefault, 0x0000,
|
||||
"\\_SB.GPO2", 0x00, ResourceConsumer, ,
|
||||
)
|
||||
{ // Pin list
|
||||
0x0006
|
||||
}
|
||||
})
|
||||
Name (FBUF, ResourceTemplate ()
|
||||
{
|
||||
GpioInt (Edge, ActiveBoth, SharedAndWake, PullUp, 0x0000,
|
||||
"\\_SB.GPO2", 0x00, ResourceConsumer, ,
|
||||
)
|
||||
{ // Pin list
|
||||
0x0012
|
||||
}
|
||||
GpioInt (Edge, ActiveBoth, ExclusiveAndWake, PullDefault, 0x0000,
|
||||
"\\_SB.GPO2", 0x00, ResourceConsumer, ,
|
||||
)
|
||||
{ // Pin list
|
||||
0x0006
|
||||
}
|
||||
})
|
||||
Return (FBUF) /* \_SB_.GPO2._AEI.FBUF */
|
||||
}
|
||||
|
||||
Name (BMUX, Buffer (0x03)
|
||||
{
|
||||
0x00, 0x01, 0x00 /* ... */
|
||||
})
|
||||
CreateByteField (BMUX, Zero, BBBY)
|
||||
CreateByteField (BMUX, 0x02, DDDT)
|
||||
Method (_E06, 0, NotSerialized) // _Exx: Edge-Triggered GPE
|
||||
{
|
||||
If (LNotEqual (LIDA, Zero))
|
||||
{
|
||||
Store (And (PVAL, One), LIDS) /* \LIDS */
|
||||
^^PCI0.GFX0.GLID (LIDS)
|
||||
Notify (LID0, 0x80) // Status Change
|
||||
}
|
||||
}
|
||||
|
||||
Method (_E12, 0, NotSerialized) // _Exx: Edge-Triggered GPE
|
||||
{
|
||||
If (LAnd (LEqual (AVBL, One), LEqual (^^GPO1.AVBL, One)))
|
||||
{
|
||||
If (LEqual (^^I2C5.AVBL, One))
|
||||
{
|
||||
If (LEqual (USID, One))
|
||||
{
|
||||
Store (Zero, ^^GPO1.BST5) /* \_SB_.GPO1.BST5 */
|
||||
Sleep (0x05)
|
||||
Store (^^I2C5.XP30, BMUX) /* \_SB_.GPO2.BMUX */
|
||||
And (DDDT, 0x7F, DDDT) /* \_SB_.GPO2.DDDT */
|
||||
Store (BMUX, ^^I2C5.XP30) /* \_SB_.I2C5.XP30 */
|
||||
Store (One, MOTG) /* \_SB_.GPO2.MOTG */
|
||||
^^PCI0.XHC1.PWOF ()
|
||||
}
|
||||
Else
|
||||
{
|
||||
Store (^^I2C5.XP30, BMUX) /* \_SB_.GPO2.BMUX */
|
||||
Or (DDDT, 0x80, DDDT) /* \_SB_.GPO2.DDDT */
|
||||
Store (BMUX, ^^I2C5.XP30) /* \_SB_.I2C5.XP30 */
|
||||
Sleep (0x05)
|
||||
Store (One, ^^GPO1.BST5) /* \_SB_.GPO1.BST5 */
|
||||
Sleep (0x05)
|
||||
Store (Zero, MOTG) /* \_SB_.GPO2.MOTG */
|
||||
^^PCI0.XHC1.PWON ()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Name (BMBQ, Buffer (0x03)
|
||||
{
|
||||
0x00, 0x01, 0x00 /* ... */
|
||||
})
|
||||
CreateByteField (BMBQ, Zero, BBBQ)
|
||||
CreateByteField (BMBQ, 0x02, DDBQ)
|
||||
Method (BOST, 1, NotSerialized)
|
||||
{
|
||||
Store (^^I2C1.BQ01, BMBQ) /* \_SB_.GPO2.BMBQ */
|
||||
If (LEqual (Arg0, One))
|
||||
{
|
||||
And (DDBQ, 0xCF, DDBQ) /* \_SB_.GPO2.DDBQ */
|
||||
Or (DDBQ, 0x20, DDBQ) /* \_SB_.GPO2.DDBQ */
|
||||
}
|
||||
Else
|
||||
{
|
||||
And (DDBQ, 0xCF, DDBQ) /* \_SB_.GPO2.DDBQ */
|
||||
Or (DDBQ, 0x10, DDBQ) /* \_SB_.GPO2.DDBQ */
|
||||
}
|
||||
|
||||
Store (BMBQ, ^^I2C1.BQ01) /* \_SB_.I2C1.BQ01 */
|
||||
}
|
||||
|
||||
Name (BUFC, Buffer (0x03)
|
||||
{
|
||||
0x00, 0x01, 0x00 /* ... */
|
||||
})
|
||||
CreateByteField (BUFC, Zero, BYAT)
|
||||
CreateByteField (BUFC, 0x02, DATA)
|
||||
Name (AVBL, Zero)
|
||||
Method (_REG, 2, NotSerialized) // _REG: Region Availability
|
||||
{
|
||||
If (LEqual (Arg0, 0x08))
|
||||
{
|
||||
Store (Arg1, AVBL) /* \_SB_.GPO2.AVBL */
|
||||
}
|
||||
}
|
||||
|
||||
OperationRegion (GPOP, GeneralPurposeIo, Zero, 0x0C)
|
||||
Field (GPOP, ByteAcc, NoLock, Preserve)
|
||||
{
|
||||
Connection (
|
||||
GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly,
|
||||
"\\_SB.GPO2", 0x00, ResourceConsumer, ,
|
||||
)
|
||||
{ // Pin list
|
||||
0x0014
|
||||
}
|
||||
),
|
||||
WFD3, 1,
|
||||
Connection (
|
||||
GpioIo (Exclusive, PullDefault, 0x0000, 0x0000, IoRestrictionOutputOnly,
|
||||
"\\_SB.GPO2", 0x00, ResourceConsumer, ,
|
||||
)
|
||||
{ // Pin list
|
||||
0x0001
|
||||
}
|
||||
),
|
||||
MOTG, 1,
|
||||
Connection (
|
||||
GpioIo (Shared, PullDefault, 0x0000, 0x0000, IoRestrictionInputOnly,
|
||||
"\\_SB.GPO2", 0x00, ResourceConsumer, ,
|
||||
)
|
||||
{ // Pin list
|
||||
0x0012
|
||||
}
|
||||
),
|
||||
USID, 1
|
||||
}
|
||||
}
|
8
gslx680-acpi/dkms.conf
Normal file
8
gslx680-acpi/dkms.conf
Normal file
@ -0,0 +1,8 @@
|
||||
PACKAGE_NAME="gslx680-acpi"
|
||||
PACKAGE_VERSION="0.2.1"
|
||||
CLEAN="make clean"
|
||||
MAKE="make all KVER=${kernelver} KSRC=/lib/modules/${kernelver}/build"
|
||||
BUILT_MODULE_NAME="gslx680_ts_acpi"
|
||||
DEST_MODULE_LOCATION="/updates"
|
||||
AUTOINSTALL="yes"
|
||||
REMAKE_INITRD="yes"
|
775
gslx680-acpi/gslx680_ts_acpi.c
Normal file
775
gslx680-acpi/gslx680_ts_acpi.c
Normal file
@ -0,0 +1,775 @@
|
||||
/*
|
||||
* Silead GSL1680/3680 touchscreen driver
|
||||
*
|
||||
* Copyright (c) 2015 Gregor Riepl <onitake@gmail.com>
|
||||
*
|
||||
* This driver is based on gslx680_ts.c and elan_ts.c
|
||||
* Copyright (c) 2012 Shanghai Basewin
|
||||
* Guan Yuwei<guanyuwei@basewin.com>
|
||||
* Copyright (c) 2013 Joe Burmeister
|
||||
* Joe Burmeister<joe.a.burmeister@googlemail.com>
|
||||
* Copyright (C) 2014 Elan Microelectronics Corporation.
|
||||
* Scott Liu <scott.liu@emc.com.tw>
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/input.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/input/mt.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
/* Device and driver information */
|
||||
#define DEVICE_NAME "gslx680"
|
||||
#define DRIVER_VERSION "0.2.1"
|
||||
|
||||
/* Hardware API constants */
|
||||
#define GSL_DATA_REG 0x80
|
||||
#define GSL_STATUS_REG 0xe0
|
||||
#define GSL_PAGE_REG 0xf0
|
||||
#define GSL_TOUCH_STATUS_REG 0xbc
|
||||
#define GSL_PAGE_SIZE 128
|
||||
/* Why 126? */
|
||||
#define GSL_MAX_READ 126
|
||||
/* Actually 129: 1 command byte and 128 data bytes
|
||||
* this was 125 originally - using 128 to allow firmware transfers
|
||||
*/
|
||||
#define GSL_MAX_WRITE 128
|
||||
#define GSL_STATUS_FW 0x80
|
||||
#define GSL_STATUS_TOUCH 0x00
|
||||
|
||||
#define GSL_PWR_GPIO "power"
|
||||
|
||||
#define GSL_MAX_CONTACTS 10
|
||||
#define GSL_MAX_AXIS 0xfff
|
||||
#define GSL_DMAX 0
|
||||
#define GSL_JITTER 0
|
||||
#define GSL_DEADZONE 0
|
||||
|
||||
#define GSL_PACKET_SIZE ( \
|
||||
GSL_MAX_CONTACTS * sizeof(struct gsl_ts_packet_touch) + \
|
||||
sizeof(struct gsl_ts_packet_header) \
|
||||
)
|
||||
|
||||
#define GSL_FW_NAME_MAXLEN 32
|
||||
#define GSL_FW_NAME_DEFAULT "silead_ts.fw"
|
||||
#define GSL_FW_VERSION 1
|
||||
#define GSL_FW_ID_LIT(a, b, c, d) ( \
|
||||
((a) & 0xff) | \
|
||||
(((b) & 0xff) << 8) | \
|
||||
(((c) & 0xff) << 16) | \
|
||||
(((d) & 0xff) << 24) \
|
||||
)
|
||||
#define GSL_FW_MAGIC GSL_FW_ID_LIT('G', 'S', 'L', 'X')
|
||||
#define GSL_FW_MODEL_LIT(m) GSL_FW_ID_LIT( \
|
||||
(m)/1000%10+'0', \
|
||||
(m)/100%10+'0', \
|
||||
(m)/10%10+'0', \
|
||||
(m)%10+'0' \
|
||||
)
|
||||
#define GSL_FW_MODEL_1680 GSL_FW_MODEL_LIT(1680)
|
||||
|
||||
/* Module Parameters (optional) */
|
||||
static char *gsl_fw_name = NULL;
|
||||
module_param_named(fw_name, gsl_fw_name, charp, 0);
|
||||
|
||||
/* Driver state */
|
||||
enum gsl_ts_state {
|
||||
GSL_TS_INIT,
|
||||
GSL_TS_SHUTDOWN,
|
||||
GSL_TS_GREEN,
|
||||
};
|
||||
|
||||
/* Driver instance data structure */
|
||||
struct gsl_ts_data {
|
||||
struct i2c_client *client;
|
||||
struct input_dev *input;
|
||||
struct gpio_desc *gpio;
|
||||
char fw_name[GSL_FW_NAME_MAXLEN];
|
||||
|
||||
enum gsl_ts_state state;
|
||||
|
||||
bool wake_irq_enabled;
|
||||
|
||||
unsigned int x_max;
|
||||
unsigned int y_max;
|
||||
unsigned int multi_touches;
|
||||
bool x_reversed;
|
||||
bool y_reversed;
|
||||
bool xy_swapped;
|
||||
bool soft_tracking;
|
||||
int jitter;
|
||||
int deadzone;
|
||||
};
|
||||
|
||||
/* Firmware header */
|
||||
struct gsl_ts_fw_header {
|
||||
u32 magic;
|
||||
u32 model;
|
||||
u16 version;
|
||||
u16 touches;
|
||||
u16 width;
|
||||
u16 height;
|
||||
u8 swapped;
|
||||
u8 xflipped;
|
||||
u8 yflipped;
|
||||
u8 tracking;
|
||||
u32 pages;
|
||||
} __packed;
|
||||
/* Firmware data page */
|
||||
struct gsl_ts_fw_page {
|
||||
u16 address;
|
||||
u16 size;
|
||||
u8 data[128];
|
||||
} __packed;
|
||||
|
||||
/* TODO use get_unaligned_le16 instead of packed structures */
|
||||
/* Hardware touch event data header */
|
||||
struct gsl_ts_packet_header {
|
||||
u8 num_fingers;
|
||||
u8 reserved;
|
||||
u16 time_stamp; /* little-endian */
|
||||
} __packed;
|
||||
/* Hardware touch event data per finger */
|
||||
struct gsl_ts_packet_touch {
|
||||
u16 y_z; /* little endian, lower 12 bits = y, upper 4 bits = pressure (?) */
|
||||
u16 x_id; /* little endian, lower 12 bits = x, upper 4 bits = id */
|
||||
} __packed;
|
||||
|
||||
|
||||
static int gsl_ts_init(struct gsl_ts_data *ts, const struct firmware *fw)
|
||||
{
|
||||
struct gsl_ts_fw_header *header;
|
||||
u32 magic;
|
||||
u16 version;
|
||||
|
||||
dev_dbg(&ts->client->dev, "%s: initialising driver state\n", __func__);
|
||||
|
||||
ts->wake_irq_enabled = false;
|
||||
ts->state = GSL_TS_INIT;
|
||||
|
||||
if (fw->size < sizeof(struct gsl_ts_fw_header)) {
|
||||
dev_err(&ts->client->dev, "%s: invalid firmware: file too small.\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
header = (struct gsl_ts_fw_header *) fw->data;
|
||||
magic = le32_to_cpu(header->magic);
|
||||
if (magic != GSL_FW_MAGIC) {
|
||||
dev_err(&ts->client->dev, "%s: invalid firmware: invalid magic 0x%08x.\n", __func__, magic);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
version = le16_to_cpu(header->version);
|
||||
if (version != GSL_FW_VERSION) {
|
||||
dev_err(&ts->client->dev, "%s: invalid firmware: unsupported version %d.\n", __func__, version);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ts->x_max = le16_to_cpu(header->width);
|
||||
ts->y_max = le16_to_cpu(header->height);
|
||||
ts->multi_touches = le16_to_cpu(header->touches);
|
||||
if (ts->x_max == 0 || ts->y_max == 0 || ts->multi_touches == 0) {
|
||||
dev_err(&ts->client->dev, "%s: invalid firmware: panel width, height or number of touch points is zero.\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (ts->x_max > GSL_MAX_AXIS || ts->y_max > GSL_MAX_AXIS || ts->multi_touches > GSL_MAX_CONTACTS) {
|
||||
dev_err(&ts->client->dev, "%s: invalid firmware: maximum panel width, height or number of touch points exceeded.\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ts->x_reversed = header->xflipped ? 1 : 0;
|
||||
ts->y_reversed = header->yflipped ? 1 : 0;
|
||||
ts->xy_swapped = header->swapped ? 1 : 0;
|
||||
ts->soft_tracking = header->tracking ? 1 : 0;
|
||||
ts->jitter = GSL_JITTER;
|
||||
ts->deadzone = GSL_DEADZONE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gsl_ts_write(struct i2c_client *client, u8 reg, const u8 *pdata, int datalen)
|
||||
{
|
||||
u8 buf[GSL_PAGE_SIZE + 1];
|
||||
unsigned int bytelen = 0;
|
||||
|
||||
if (datalen > GSL_MAX_WRITE) {
|
||||
dev_err(&client->dev, "%s: data transfer too large (%d > %d)\n", __func__, datalen, GSL_MAX_WRITE);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
buf[0] = reg;
|
||||
bytelen++;
|
||||
|
||||
if (datalen != 0 && pdata != NULL) {
|
||||
memcpy(&buf[bytelen], pdata, datalen);
|
||||
bytelen += datalen;
|
||||
}
|
||||
|
||||
return i2c_master_send(client, buf, bytelen);
|
||||
}
|
||||
|
||||
static int gsl_ts_read(struct i2c_client *client, u8 reg, u8 *pdata, unsigned int datalen)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (datalen > GSL_MAX_READ) {
|
||||
dev_err(&client->dev, "%s: data transfer too large (%d > %d)\n", __func__, datalen, GSL_MAX_READ);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = gsl_ts_write(client, reg, NULL, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "%s: sending register location failed\n", __func__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return i2c_master_recv(client, pdata, datalen);
|
||||
}
|
||||
|
||||
static int gsl_ts_startup_chip(struct i2c_client *client)
|
||||
{
|
||||
int rc;
|
||||
u8 tmp = 0x00;
|
||||
|
||||
dev_dbg(&client->dev, "%s: starting up\n", __func__);
|
||||
rc = gsl_ts_write(client, 0xe0, &tmp, 1);
|
||||
usleep_range(10000, 20000);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int gsl_ts_reset_chip(struct i2c_client *client)
|
||||
{
|
||||
int rc;
|
||||
u8 arg[4] = { 0x00, 0x00, 0x00, 0x00 };
|
||||
|
||||
dev_dbg(&client->dev, "%s: resetting\n", __func__);
|
||||
|
||||
arg[0] = 0x88;
|
||||
rc = gsl_ts_write(client, 0xe0, arg, 1);
|
||||
if (rc < 0) {
|
||||
dev_err(&client->dev, "%s: gsl_ts_write 1 fail!\n", __func__);
|
||||
return rc;
|
||||
}
|
||||
usleep_range(10000, 20000);
|
||||
|
||||
arg[0] = 0x04;
|
||||
rc = gsl_ts_write(client, 0xe4, arg, 1);
|
||||
if (rc < 0) {
|
||||
dev_err(&client->dev, "%s: gsl_ts_write 2 fail!\n", __func__);
|
||||
return rc;
|
||||
}
|
||||
usleep_range(10000, 20000);
|
||||
|
||||
arg[0] = 0x00;
|
||||
rc = gsl_ts_write(client, 0xbc, arg, 4);
|
||||
if (rc < 0) {
|
||||
dev_err(&client->dev, "%s: gsl_ts_write 3 fail!\n", __func__);
|
||||
return rc;
|
||||
}
|
||||
usleep_range(10000, 20000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gsl_ts_write_fw(struct gsl_ts_data *ts, const struct firmware *fw)
|
||||
{
|
||||
int rc = 0;
|
||||
struct i2c_client *client = ts->client;
|
||||
const struct gsl_ts_fw_header *header;
|
||||
const struct gsl_ts_fw_page *page;
|
||||
u32 pages, address;
|
||||
u16 size;
|
||||
size_t i;
|
||||
|
||||
dev_dbg(&client->dev, "%s: sending firmware\n", __func__);
|
||||
|
||||
header = (const struct gsl_ts_fw_header *) fw->data;
|
||||
pages = le32_to_cpu(header->pages);
|
||||
if (fw->size < sizeof(struct gsl_ts_fw_header) + pages * sizeof(struct gsl_ts_fw_page)) {
|
||||
dev_err(&client->dev, "%s: firmware page data too small.\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; rc >= 0 && i < pages; i++) {
|
||||
page = (const struct gsl_ts_fw_page *) &fw->data[sizeof(struct gsl_ts_fw_header) + i * sizeof(struct gsl_ts_fw_page)];
|
||||
/* The controller expects a little endian address */
|
||||
address = cpu_to_le32(le16_to_cpu(page->address));
|
||||
size = le16_to_cpu(page->size);
|
||||
rc = gsl_ts_write(client, GSL_PAGE_REG, (u8 *) &address, sizeof(address));
|
||||
if (rc < 0) {
|
||||
dev_err(&client->dev, "%s: error setting page register. (page = 0x%x)\n", __func__, le32_to_cpu(address));
|
||||
} else {
|
||||
rc = gsl_ts_write(client, 0, page->data, size);
|
||||
if (rc < 0) {
|
||||
dev_err(&client->dev, "%s: error writing page data. (page = 0x%x)\n", __func__, le32_to_cpu(address));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gsl_ts_mt_event(struct gsl_ts_data *ts, u8 *buf)
|
||||
{
|
||||
int rc;
|
||||
struct input_dev *input = ts->input;
|
||||
struct device *dev = &ts->client->dev;
|
||||
struct gsl_ts_packet_header *header;
|
||||
struct gsl_ts_packet_touch *touch;
|
||||
u8 i;
|
||||
u16 touches, tseq, x, y, id, pressure;
|
||||
struct input_mt_pos positions[GSL_MAX_CONTACTS];
|
||||
int slots[GSL_MAX_CONTACTS];
|
||||
|
||||
header = (struct gsl_ts_packet_header *) buf;
|
||||
touches = header->num_fingers;
|
||||
tseq = le16_to_cpu(header->time_stamp);
|
||||
/* time_stamp is 0 on zero-touch events, seems to wrap around 21800 */
|
||||
dev_vdbg(dev, "%s: got touch events for %u fingers @%u\n", __func__, touches, tseq);
|
||||
|
||||
if (touches > GSL_MAX_CONTACTS) {
|
||||
touches = GSL_MAX_CONTACTS;
|
||||
}
|
||||
|
||||
for (i = 0; i < touches; i++) {
|
||||
touch = (struct gsl_ts_packet_touch *) &buf[sizeof(*header) + i * sizeof(*touch)];
|
||||
y = le16_to_cpu(touch->y_z);
|
||||
x = le16_to_cpu(touch->x_id);
|
||||
id = x >> 12;
|
||||
x &= 0xfff;
|
||||
pressure = y >> 12;
|
||||
y &= 0xfff;
|
||||
|
||||
if (ts->xy_swapped) {
|
||||
swap(x, y);
|
||||
}
|
||||
if (ts->x_reversed) {
|
||||
x = ts->x_max - x;
|
||||
}
|
||||
if (ts->y_reversed) {
|
||||
y = ts->y_max - y;
|
||||
}
|
||||
|
||||
dev_vdbg(dev, "%s: touch event %u: x=%u y=%u id=0x%x p=%u\n", __func__, i, x, y, id, pressure);
|
||||
|
||||
positions[i].x = x;
|
||||
positions[i].y = y;
|
||||
if (!ts->soft_tracking) {
|
||||
slots[i] = id;
|
||||
}
|
||||
}
|
||||
if (ts->soft_tracking) {
|
||||
/* This platform does not support finger tracking.
|
||||
* Use the input core finger tracker instead.
|
||||
*/
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)
|
||||
rc = input_mt_assign_slots(input, slots, positions, touches);
|
||||
#else
|
||||
rc = input_mt_assign_slots(input, slots, positions, touches, GSL_DMAX);
|
||||
#endif
|
||||
if (rc < 0) {
|
||||
dev_err(dev, "%s: input_mt_assign_slots returned %d\n", __func__, rc);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < touches; i++) {
|
||||
input_mt_slot(input, slots[i]);
|
||||
input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
|
||||
input_report_abs(input, ABS_MT_POSITION_X, positions[i].x);
|
||||
input_report_abs(input, ABS_MT_POSITION_Y, positions[i].y);
|
||||
}
|
||||
|
||||
input_mt_sync_frame(input);
|
||||
input_sync(input);
|
||||
}
|
||||
|
||||
static irqreturn_t gsl_ts_irq(int irq, void *arg)
|
||||
{
|
||||
int rc;
|
||||
struct gsl_ts_data *ts = (struct gsl_ts_data *) arg;
|
||||
struct i2c_client *client = ts->client;
|
||||
struct device *dev = &client->dev;
|
||||
u8 status[4] = { 0, 0, 0, 0 };
|
||||
u8 event[GSL_PACKET_SIZE];
|
||||
|
||||
dev_dbg(&client->dev, "%s: IRQ received\n", __func__);
|
||||
|
||||
if (ts->state == GSL_TS_SHUTDOWN) {
|
||||
dev_warn(&client->dev, "%s: device supended, not handling interrupt\n", __func__);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
rc = gsl_ts_read(client, GSL_STATUS_REG, status, sizeof(status));
|
||||
if (rc < 0) {
|
||||
dev_err(dev, "%s: error reading chip status\n", __func__);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if (status[0] == GSL_STATUS_FW) {
|
||||
/* TODO: Send firmware here instead of during init */
|
||||
dev_info(dev, "%s: device waiting for firmware\n", __func__);
|
||||
|
||||
} else if (status[0] == GSL_STATUS_TOUCH) {
|
||||
dev_vdbg(dev, "%s: touch event\n", __func__);
|
||||
|
||||
rc = gsl_ts_read(client, GSL_DATA_REG, event, sizeof(event));
|
||||
if (rc < 0) {
|
||||
dev_err(dev, "%s: touch data read failed\n", __func__);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
if (event[0] == 0xff) {
|
||||
dev_warn(dev, "%s: ignoring invalid touch record (0xff)\n", __func__);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
rc = gsl_ts_read(client, GSL_TOUCH_STATUS_REG, status, sizeof(status));
|
||||
if (rc < 0) {
|
||||
dev_err(dev, "%s: reading touch status register failed\n", __func__);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
if ((status[0] | status[1] | status[2] | status[3]) == 0) {
|
||||
gsl_ts_mt_event(ts, event);
|
||||
|
||||
} else {
|
||||
dev_warn(dev, "%s: device seems to be stuck, resetting\n", __func__);
|
||||
|
||||
rc = gsl_ts_reset_chip(ts->client);
|
||||
if (rc < 0) {
|
||||
dev_err(dev, "%s: reset_chip failed\n", __func__);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
rc = gsl_ts_startup_chip(ts->client);
|
||||
if (rc < 0) {
|
||||
dev_err(dev, "%s: startup_chip failed\n", __func__);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
dev_warn(&client->dev, "%s: IRQ received, unknown status 0x%02x\n", __func__, status[0]);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void gsl_ts_power(struct i2c_client *client, bool turnoff)
|
||||
{
|
||||
struct gsl_ts_data *data = i2c_get_clientdata(client);
|
||||
#ifdef CONFIG_ACPI
|
||||
int error;
|
||||
#endif
|
||||
|
||||
if (data) {
|
||||
if (data->gpio) {
|
||||
gpiod_set_value_cansleep(data->gpio, turnoff ? 0 : 1);
|
||||
#ifdef CONFIG_ACPI
|
||||
} else {
|
||||
error = acpi_bus_set_power(ACPI_HANDLE(&client->dev), turnoff ? ACPI_STATE_D3 : ACPI_STATE_D0);
|
||||
if (error) {
|
||||
dev_warn(&client->dev, "%s: error changing power state: %d\n", __func__, error);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
usleep_range(20000, 50000);
|
||||
}
|
||||
}
|
||||
|
||||
static int gsl_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
{
|
||||
struct gsl_ts_data *ts;
|
||||
const struct firmware *fw = NULL;
|
||||
unsigned long irqflags;
|
||||
int error;
|
||||
bool acpipower;
|
||||
|
||||
dev_warn(&client->dev, "%s: got a device named %s at address 0x%x, IRQ %d, flags 0x%x\n", __func__, client->name, client->addr, client->irq, client->flags);
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
||||
dev_err(&client->dev, "%s: i2c check functionality error\n", __func__);
|
||||
error = -ENXIO;
|
||||
goto release;
|
||||
}
|
||||
|
||||
if (client->irq <= 0) {
|
||||
dev_err(&client->dev, "%s: missing IRQ configuration\n", __func__);
|
||||
error = -ENODEV;
|
||||
goto release;
|
||||
}
|
||||
|
||||
ts = devm_kzalloc(&client->dev, sizeof(struct gsl_ts_data), GFP_KERNEL);
|
||||
if (!ts) {
|
||||
error = -ENOMEM;
|
||||
goto release;
|
||||
}
|
||||
|
||||
ts->client = client;
|
||||
i2c_set_clientdata(client, ts);
|
||||
|
||||
if (gsl_fw_name != NULL) {
|
||||
strncpy(ts->fw_name, gsl_fw_name, sizeof(ts->fw_name));
|
||||
} else {
|
||||
strncpy(ts->fw_name, GSL_FW_NAME_DEFAULT, sizeof(ts->fw_name));
|
||||
}
|
||||
error = request_firmware(&fw, ts->fw_name, &ts->client->dev);
|
||||
if (error < 0) {
|
||||
dev_err(&client->dev, "%s: failed to load firmware: %d\n", __func__, error);
|
||||
goto release;
|
||||
}
|
||||
|
||||
error = gsl_ts_init(ts, fw);
|
||||
if (error < 0) {
|
||||
dev_err(&client->dev, "%s: failed to initialize: %d\n", __func__, error);
|
||||
goto release;
|
||||
}
|
||||
|
||||
ts->input = devm_input_allocate_device(&client->dev);
|
||||
if (!ts->input) {
|
||||
dev_err(&client->dev, "%s: failed to allocate input device\n", __func__);
|
||||
error = -ENOMEM;
|
||||
goto release;
|
||||
}
|
||||
|
||||
ts->input->name = "Silead GSLx680 Touchscreen";
|
||||
ts->input->id.bustype = BUS_I2C;
|
||||
ts->input->phys = "input/ts";
|
||||
|
||||
input_set_capability(ts->input, EV_ABS, ABS_X);
|
||||
input_set_capability(ts->input, EV_ABS, ABS_Y);
|
||||
|
||||
input_set_abs_params(ts->input, ABS_MT_POSITION_X, 0, ts->x_max, ts->jitter, ts->deadzone);
|
||||
input_set_abs_params(ts->input, ABS_MT_POSITION_Y, 0, ts->y_max, ts->jitter, ts->deadzone);
|
||||
|
||||
input_mt_init_slots(ts->input, ts->multi_touches, INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED | INPUT_MT_TRACK);
|
||||
|
||||
input_set_drvdata(ts->input, ts);
|
||||
|
||||
error = input_register_device(ts->input);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "%s: unable to register input device: %d\n", __func__, error);
|
||||
goto release;
|
||||
}
|
||||
|
||||
/* Try to use ACPI power methods first */
|
||||
acpipower = false;
|
||||
#ifdef CONFIG_ACPI
|
||||
if (ACPI_COMPANION(&client->dev)) {
|
||||
/* Wake the device up with a power on reset */
|
||||
if (acpi_bus_set_power(ACPI_HANDLE(&client->dev), ACPI_STATE_D3)) {
|
||||
dev_warn(&client->dev, "%s: failed to wake up device through ACPI: %d, using GPIO controls instead\n", __func__, error);
|
||||
} else {
|
||||
acpipower = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
/* Not available, use GPIO settings from DSDT/DT instead */
|
||||
if (!acpipower) {
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
|
||||
ts->gpio = devm_gpiod_get_index(&client->dev, GSL_PWR_GPIO, 0);
|
||||
#else
|
||||
ts->gpio = devm_gpiod_get_index(&client->dev, GSL_PWR_GPIO, 0, GPIOD_OUT_LOW);
|
||||
#endif
|
||||
if (IS_ERR(ts->gpio)) {
|
||||
dev_err(&client->dev, "%s: error obtaining power pin GPIO resource\n", __func__);
|
||||
error = PTR_ERR(ts->gpio);
|
||||
goto release;
|
||||
}
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 17, 0)
|
||||
error = gpiod_direction_output(ts->gpio, 0);
|
||||
if (error < 0) {
|
||||
dev_err(&client->dev, "%s: error setting GPIO pin direction\n", __func__);
|
||||
goto release;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
ts->gpio = NULL;
|
||||
}
|
||||
|
||||
/* Enable power */
|
||||
gsl_ts_power(client, false);
|
||||
|
||||
/* Execute the controller startup sequence */
|
||||
error = gsl_ts_reset_chip(client);
|
||||
if (error < 0) {
|
||||
dev_err(&client->dev, "%s: chip reset failed\n", __func__);
|
||||
goto release;
|
||||
}
|
||||
error = gsl_ts_write_fw(ts, fw);
|
||||
if (error < 0) {
|
||||
dev_err(&client->dev, "%s: firmware transfer failed\n", __func__);
|
||||
goto release;
|
||||
}
|
||||
error = gsl_ts_startup_chip(client);
|
||||
if (error < 0) {
|
||||
dev_err(&client->dev, "%s: chip startup failed\n", __func__);
|
||||
goto release;
|
||||
}
|
||||
|
||||
/*
|
||||
* Systems using device tree should set up interrupt via DTS,
|
||||
* the rest will use the default falling edge interrupts.
|
||||
*/
|
||||
irqflags = client->dev.of_node ? 0 : IRQF_TRIGGER_FALLING;
|
||||
|
||||
/* Set up interrupt handler - do we still need to account for shared interrupts? */
|
||||
error = devm_request_threaded_irq(
|
||||
&client->dev,
|
||||
client->irq,
|
||||
NULL,
|
||||
gsl_ts_irq,
|
||||
irqflags | IRQF_ONESHOT,
|
||||
client->name,
|
||||
ts
|
||||
);
|
||||
if (error) {
|
||||
dev_err(&client->dev, "%s: failed to register interrupt\n", __func__);
|
||||
goto release;
|
||||
}
|
||||
|
||||
/*
|
||||
* Systems using device tree should set up wakeup via DTS,
|
||||
* the rest will configure device as wakeup source by default.
|
||||
*/
|
||||
if (!client->dev.of_node) {
|
||||
device_init_wakeup(&client->dev, true);
|
||||
}
|
||||
|
||||
ts->state = GSL_TS_GREEN;
|
||||
|
||||
release:
|
||||
if (fw) {
|
||||
release_firmware(fw);
|
||||
}
|
||||
|
||||
if (error < 0) {
|
||||
return error;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gsl_ts_remove(struct i2c_client *client) {
|
||||
/* Power the device off */
|
||||
gsl_ts_power(client, true);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused gsl_ts_suspend(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct gsl_ts_data *ts = i2c_get_clientdata(client);
|
||||
|
||||
dev_warn(&client->dev, "%s: suspending device\n", __func__);
|
||||
|
||||
disable_irq(client->irq);
|
||||
|
||||
gsl_ts_reset_chip(client);
|
||||
usleep_range(10000, 20000);
|
||||
|
||||
gsl_ts_power(client, true);
|
||||
|
||||
if (device_may_wakeup(dev)) {
|
||||
ts->wake_irq_enabled = (enable_irq_wake(client->irq) == 0);
|
||||
}
|
||||
|
||||
ts->state = GSL_TS_SHUTDOWN;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __maybe_unused gsl_ts_resume(struct device *dev)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct gsl_ts_data *ts = i2c_get_clientdata(client);
|
||||
|
||||
dev_warn(&client->dev, "%s: resuming device\n", __func__);
|
||||
|
||||
if (device_may_wakeup(dev) && ts->wake_irq_enabled) {
|
||||
disable_irq_wake(client->irq);
|
||||
}
|
||||
|
||||
gsl_ts_power(client, false);
|
||||
|
||||
gsl_ts_reset_chip(client);
|
||||
gsl_ts_startup_chip(client);
|
||||
|
||||
enable_irq(client->irq);
|
||||
|
||||
ts->state = GSL_TS_GREEN;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id gsl_ts_i2c_id[] = {
|
||||
{ DEVICE_NAME, 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, gsl_ts_i2c_id);
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static SIMPLE_DEV_PM_OPS(gsl_ts_pm_ops, gsl_ts_suspend, gsl_ts_resume);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
/* GSL3680 ACPI IDs are untested */
|
||||
static const struct acpi_device_id gsl_ts_acpi_match[] = {
|
||||
{ "MSSL1680", 0 },
|
||||
{ "MSSL3680", 0 },
|
||||
{ "PNP1680", 0 },
|
||||
{ "PNP3680", 0 },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, gsl_ts_acpi_match);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
/* These should work, but more testing is needed */
|
||||
static const struct of_device_id gsl_ts_of_match[] = {
|
||||
{ .compatible = "silead,gsl1680" },
|
||||
{ .compatible = "silead,gsl3680" },
|
||||
{ .compatible = "silead,gslx680" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, gsl_ts_of_match);
|
||||
#endif
|
||||
|
||||
static struct i2c_driver gslx680_ts_driver = {
|
||||
.probe = gsl_ts_probe,
|
||||
.remove = gsl_ts_remove,
|
||||
.id_table = gsl_ts_i2c_id,
|
||||
.driver = {
|
||||
.name = DEVICE_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
#ifdef CONFIG_PM
|
||||
.pm = &gsl_ts_pm_ops,
|
||||
#endif
|
||||
#ifdef CONFIG_ACPI
|
||||
.acpi_match_table = ACPI_PTR(gsl_ts_acpi_match),
|
||||
#endif
|
||||
#ifdef CONFIG_OF
|
||||
.of_match_table = of_match_ptr(gsl_ts_of_match),
|
||||
#endif
|
||||
},
|
||||
};
|
||||
module_i2c_driver(gslx680_ts_driver);
|
||||
|
||||
MODULE_DESCRIPTION("GSLX680 touchscreen controller driver");
|
||||
MODULE_AUTHOR("Gregor Riepl <onitake@gmail.com>");
|
||||
MODULE_PARM_DESC(fw_name, "firmware file name (default: " GSL_FW_NAME_DEFAULT ")");
|
||||
MODULE_VERSION(DRIVER_VERSION);
|
||||
MODULE_LICENSE("GPL");
|
BIN
gslx680-acpi/silead_ts.fw
Normal file
BIN
gslx680-acpi/silead_ts.fw
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user