diff --git a/gslx680-acpi/CHANGELOG b/gslx680-acpi/CHANGELOG new file mode 100644 index 0000000..ff3e72a --- /dev/null +++ b/gslx680-acpi/CHANGELOG @@ -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 diff --git a/gslx680-acpi/LICENSE b/gslx680-acpi/LICENSE new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/gslx680-acpi/LICENSE @@ -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. + + + Copyright (C) + + 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. + + , 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. diff --git a/gslx680-acpi/Makefile b/gslx680-acpi/Makefile new file mode 100644 index 0000000..e5b17fd --- /dev/null +++ b/gslx680-acpi/Makefile @@ -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 diff --git a/gslx680-acpi/README.md b/gslx680-acpi/README.md new file mode 100644 index 0000000..359436a --- /dev/null +++ b/gslx680-acpi/README.md @@ -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. diff --git a/gslx680-acpi/acpi/gsl-dsdt.aml b/gslx680-acpi/acpi/gsl-dsdt.aml new file mode 100644 index 0000000..661f541 --- /dev/null +++ b/gslx680-acpi/acpi/gsl-dsdt.aml @@ -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 + } +} diff --git a/gslx680-acpi/dkms.conf b/gslx680-acpi/dkms.conf new file mode 100644 index 0000000..411e815 --- /dev/null +++ b/gslx680-acpi/dkms.conf @@ -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" \ No newline at end of file diff --git a/gslx680-acpi/gslx680_ts_acpi.c b/gslx680-acpi/gslx680_ts_acpi.c new file mode 100644 index 0000000..748cedb --- /dev/null +++ b/gslx680-acpi/gslx680_ts_acpi.c @@ -0,0 +1,775 @@ +/* + * Silead GSL1680/3680 touchscreen driver + * + * Copyright (c) 2015 Gregor Riepl + * + * This driver is based on gslx680_ts.c and elan_ts.c + * Copyright (c) 2012 Shanghai Basewin + * Guan Yuwei + * Copyright (c) 2013 Joe Burmeister + * Joe Burmeister + * Copyright (C) 2014 Elan Microelectronics Corporation. + * Scott Liu + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* 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 "); +MODULE_PARM_DESC(fw_name, "firmware file name (default: " GSL_FW_NAME_DEFAULT ")"); +MODULE_VERSION(DRIVER_VERSION); +MODULE_LICENSE("GPL"); diff --git a/gslx680-acpi/silead_ts.fw b/gslx680-acpi/silead_ts.fw new file mode 100644 index 0000000..c106cd4 Binary files /dev/null and b/gslx680-acpi/silead_ts.fw differ