pax_global_header00006660000000000000000000000064134347313040014514gustar00rootroot0000000000000052 comment=63c132a9f3c65839a36dce40140cebee8a3ac0ed evdi-1.6.0+dfsg/000077500000000000000000000000001343473130400133665ustar00rootroot00000000000000evdi-1.6.0+dfsg/cleanup_script.sh000077500000000000000000000001421343473130400167350ustar00rootroot00000000000000#!/bin/sh rm -r ci rm -r docs rm mkdocs.yml rm .travis.yml rm .gitignore rm README.md rm Makefile evdi-1.6.0+dfsg/library/000077500000000000000000000000001343473130400150325ustar00rootroot00000000000000evdi-1.6.0+dfsg/library/LICENSE000066400000000000000000000636311343473130400160500ustar00rootroot00000000000000 GNU LESSER GENERAL PUBLIC LICENSE Version 2.1, February 1999 Copyright (C) 1991, 1999 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. [This is the first released version of the Lesser GPL. It also counts as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This license, the Lesser General Public License, applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. You can use it too, but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case, based on the explanations below. When we speak of free software, we are referring to freedom of use, 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 and use pieces of it in new free programs; and that you are informed that you can do these things. To protect your rights, we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. For example, if you distribute copies of the library, whether gratis or for a fee, you must give the recipients all the rights that we gave you. You must make sure that they, too, receive or can get the source code. If you link other code with the library, you must provide complete object files to the recipients, so that they can relink them with the library after making changes to the library and recompiling it. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library, and (2) we offer you this license, which gives you legal permission to copy, distribute and/or modify the library. To protect each distributor, we want to make it very clear that there is no warranty for the free library. Also, if the library is modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. Therefore, we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. Most GNU software, including some libraries, is covered by the ordinary GNU General Public License. This license, the GNU Lesser General Public License, applies to certain designated libraries, and is quite different from the ordinary General Public License. We use this license for certain libraries in order to permit linking those libraries into non-free programs. When a program is linked with a library, whether statically or using a shared library, the combination of the two is legally speaking a combined work, a derivative of the original library. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. The Lesser General Public License permits more lax criteria for linking other code with the library. We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. It also provides other free software developers Less of an advantage over competing non-free programs. These disadvantages are the reason we use the ordinary General Public License for many libraries. However, the Lesser license provides advantages in certain special circumstances. For example, on rare occasions, there may be a special need to encourage the widest possible use of a certain library, so that it becomes a de-facto standard. To achieve this, non-free programs must be allowed to use the library. A more frequent case is that a free library does the same job as widely used non-free libraries. In this case, there is little to gain by limiting the free library to free software only, so we use the Lesser General Public License. In other cases, permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. For example, permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system, as well as its variant, the GNU/Linux operating system. Although the Lesser General Public License is Less protective of the users' freedom, it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. The precise terms and conditions for copying, distribution and modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). Each licensee is addressed as "you". A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. The "Library", below, refers to any such software library or work which has been distributed under these terms. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say, a work containing the Library or a portion of it, either verbatim or with modifications and/or translated straightforwardly into another language. (Hereinafter, translation is included without limitation in the term "modification".) "Source code" for a work means the preferred form of the work for making modifications to it. For a library, 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 library. Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running a program using the Library is not restricted, and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. 1. You may copy and distribute verbatim copies of the Library's complete 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 distribute a copy of this License along with the Library. 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 Library or any portion of it, thus forming a work based on the Library, 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) The modified work must itself be a software library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. c) You must cause the whole of the work to be licensed at no charge to all third parties under the terms of this License. d) If a facility in the modified Library refers to a function or a table of data to be supplied by an application program that uses the facility, other than as an argument passed when the facility is invoked, then you must make a good faith effort to ensure that, in the event an application does not supply such function or table, the facility still operates, and performs whatever part of its purpose remains meaningful. (For example, a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. Therefore, Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it, the square root function must still compute square roots.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Library, 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 Library, 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 Library. In addition, mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. To do this, you must alter all the notices that refer to this License, so that they refer to the ordinary GNU General Public License, version 2, instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. 4. You may copy and distribute the Library (or a portion or derivative of it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you 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. If distribution of 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 satisfies the requirement to distribute the source code, even though third parties are not compelled to copy the source along with the object code. 5. A program that contains no derivative of any portion of the Library, but is designed to work with the Library by being compiled or linked with it, is called a "work that uses the Library". Such a work, in isolation, is not a derivative work of the Library, and therefore falls outside the scope of this License. However, linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library), rather than a "work that uses the library". The executable is therefore covered by this License. Section 6 states terms for distribution of such executables. When a "work that uses the Library" uses material from a header file that is part of the Library, the object code for the work may be a derivative work of the Library even though the source code is not. Whether this is true is especially significant if the work can be linked without the Library, or if the work is itself a library. The threshold for this to be true is not precisely defined by law. If such an object file uses only numerical parameters, data structure layouts and accessors, and small macros and small inline functions (ten lines or less in length), then the use of the object file is unrestricted, regardless of whether it is legally a derivative work. (Executables containing this object code plus portions of the Library will still fall under Section 6.) Otherwise, if the work is a derivative of the Library, you may distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work under terms of your choice, provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. You must supply a copy of this License. If the work during execution displays copyright notices, you must include the copyright notice for the Library among them, as well as a reference directing the user to the copy of this License. Also, you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above); and, if the work is an executable linked with the Library, with the complete machine-readable "work that uses the Library", as object code and/or source code, so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. (It is understood that the user who changes the contents of definitions files in the Library will not necessarily be able to recompile the application to use the modified definitions.) b) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system, rather than copying library functions into the executable, and (2) will operate properly with a modified version of the library, if the user installs one, as long as the modified version is interface-compatible with the version that the work was made with. c) Accompany the work with a written offer, valid for at least three years, to give the same user the materials specified in Subsection 6a, above, for a charge no more than the cost of performing this distribution. d) If distribution of the work is made by offering access to copy from a designated place, offer equivalent access to copy the above specified materials from the same place. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. For an executable, the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. However, as a special exception, the materials to be 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. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined library, provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted, and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities. This must be distributed under the terms of the Sections above. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 8. You may not copy, modify, sublicense, link with, or distribute the Library except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense, link with, or distribute the Library 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. 9. 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 Library or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Library (or any work based on the Library), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Library or works based on it. 10. Each time you redistribute the Library (or any work based on the Library), the recipient automatically receives a license from the original licensor to copy, distribute, link with or modify the Library 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 with this License. 11. 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 Library at all. For example, if a patent license would not permit royalty-free redistribution of the Library 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 Library. 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. 12. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Library 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. 13. The Free Software Foundation may publish revised and/or new versions of the Lesser 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 Library 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 Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, 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 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "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 LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. 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 LIBRARY 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 LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), 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 Libraries If you develop a new library, and you want it to be of the greatest possible use to the public, we recommend making it free software that everyone can redistribute and change. You can do so by permitting redistribution under these terms (or, alternatively, under the terms of the ordinary General Public License). To apply these terms, attach the following notices to the library. 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 library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; 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. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the library, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. , 1 April 1990 Ty Coon, President of Vice That's all there is to it! evdi-1.6.0+dfsg/library/Makefile000066400000000000000000000004631343473130400164750ustar00rootroot00000000000000# # Copyright (c) 2015 - 2016 DisplayLink (UK) Ltd. # DEPS = evdi_ioctl.h CFLAGS := -I../module -std=gnu99 -fPIC $(CFLAGS) default: libevdi.so clean: rm -rf *.o libevdi.so %.o: %.c $(DEPS) $(CC) -c -o $@ $< $(CFLAGS) libevdi.so: evdi_lib.o $(CC) $^ -o $@ -lc -lgcc -shared -Wl,-soname,libevdi.so.0 evdi-1.6.0+dfsg/library/evdi_lib.c000066400000000000000000000370251343473130400167620ustar00rootroot00000000000000// SPDX-License-Identifier: LGPL-2.1-only // Copyright (c) 2015 - 2017 DisplayLink (UK) Ltd. #include #include #include #ifndef __user # define __user #endif #include "evdi_drm.h" #include "evdi_lib.h" #include #include #include #include #include #include #include #include #include #include #include #include #include // ********************* Private part ************************** #define MAX_DIRTS 16 #define EVDI_MODULE_COMPATIBILITY_VERSION_MAJOR 1 #define EVDI_MODULE_COMPATIBILITY_VERSION_MINOR 6 #define EVDI_MODULE_COMPATIBILITY_VERSION_PATCHLEVEL 0 #define evdi_log(...) do { \ if (g_evdi_logging.function) { \ g_evdi_logging.function(g_evdi_logging.user_data, \ __VA_ARGS__); \ } else { \ printf("[libevdi] " __VA_ARGS__); \ printf("\n"); \ } \ } while (0) struct evdi_logging g_evdi_logging = { .function = NULL, .user_data = NULL }; struct evdi_frame_buffer_node { struct evdi_buffer frame_buffer; struct evdi_frame_buffer_node *next; }; struct evdi_device_context { int fd; int bufferToUpdate; struct evdi_frame_buffer_node *frameBuffersListHead; int device_index; }; static int do_ioctl(int fd, unsigned int request, void *data, const char *msg) { const int err = ioctl(fd, request, data); if (err < 0) evdi_log("Ioctl %s error: %s", msg, strerror(errno)); return err; } static struct evdi_frame_buffer_node *findBuffer(evdi_handle context, int id) { struct evdi_frame_buffer_node *node = NULL; assert(context); for (node = context->frameBuffersListHead; node != NULL; node = (struct evdi_frame_buffer_node *)node->next) { if (node->frame_buffer.id == id) return node; } return NULL; } static void addFrameBuffer(evdi_handle context, struct evdi_buffer const *frame_buffer) { struct evdi_frame_buffer_node **node = NULL; for (node = &context->frameBuffersListHead; ; node = (struct evdi_frame_buffer_node **)&(*node)->next) { if (*node) continue; *node = calloc(1, sizeof(struct evdi_frame_buffer_node)); assert(node); memcpy(*node, frame_buffer, sizeof(struct evdi_buffer)); return; } } /* * @brief Removes all frame buffers matching the given id * @param id of frame buffer to remove, NULL matches every buffer, thus all * will be removed * @return number of buffers removed * @todo Return value doesn't seem to be used anywhere */ static int removeFrameBuffer(evdi_handle context, int const *id) { struct evdi_frame_buffer_node *current = NULL; struct evdi_frame_buffer_node *next = NULL; struct evdi_frame_buffer_node **prev = NULL; int removedCount = 0; current = context->frameBuffersListHead; prev = &context->frameBuffersListHead; while (current) { next = current->next; if (!id || current->frame_buffer.id == *id) { free(current); ++removedCount; *prev = next; } else { prev = ¤t->next; } current = next; } return removedCount; } static int is_evdi_compatible(int fd) { struct drm_version ver = { 0 }; evdi_log("LibEvdi version (%d.%d.%d)", LIBEVDI_VERSION_MAJOR, LIBEVDI_VERSION_MINOR, LIBEVDI_VERSION_PATCHLEVEL); if (do_ioctl(fd, DRM_IOCTL_VERSION, &ver, "version") != 0) return 0; evdi_log("Evdi version (%d.%d.%d)", ver.version_major, ver.version_minor, ver.version_patchlevel); if (ver.version_major == EVDI_MODULE_COMPATIBILITY_VERSION_MAJOR && ver.version_minor == EVDI_MODULE_COMPATIBILITY_VERSION_MINOR) return 1; evdi_log("Doesn't match LibEvdi compatibility one (%d.%d.%d)", EVDI_MODULE_COMPATIBILITY_VERSION_MAJOR, EVDI_MODULE_COMPATIBILITY_VERSION_MINOR, EVDI_MODULE_COMPATIBILITY_VERSION_PATCHLEVEL); #ifdef NDEBUG return 0; #else return 1; #endif } static int is_evdi(int fd) { char name[64] = { 0 }, date[64] = { 0 }, desc[64] = { 0 }; struct drm_version ver = { .name_len = sizeof(name), .name = name, .date_len = sizeof(date), .date = date, .desc_len = sizeof(desc), .desc = desc, }; if (do_ioctl(fd, DRM_IOCTL_VERSION, &ver, "version") == 0 && strcmp(name, "evdi") == 0) { return 1; } return 0; } static int path_exists(const char *path) { struct stat buf; return stat(path, &buf) == 0; } static int device_exists(int device) { char dev[PATH_MAX] = ""; snprintf(dev, PATH_MAX, "/dev/dri/card%d", device); return path_exists(dev); } static int does_path_links_to(const char *link, const char *substr) { char real_path[PATH_MAX]; ssize_t r; r = readlink(link, real_path, sizeof(real_path)); if (r < 0) return 0; real_path[r] = '\0'; return (strstr(real_path, substr) != NULL); } static int process_opened_device(const char *pid, const char *device_file_path) { char maps_path[PATH_MAX]; FILE *maps = NULL; char line[BUFSIZ]; int result = 0; snprintf(maps_path, PATH_MAX, "/proc/%s/maps", pid); maps = fopen(maps_path, "r"); if (maps == NULL) return 0; while (fgets(line, BUFSIZ, maps)) { if (strstr(line, device_file_path)) { result = 1; break; } } fclose(maps); return result; } static int process_opened_files(const char *pid, const char *device_file_path) { char fd_path[PATH_MAX]; DIR *fd_dir; struct dirent *fd_entry; int result = 0; snprintf(fd_path, PATH_MAX, "/proc/%s/fd", pid); fd_dir = opendir(fd_path); if (fd_dir == NULL) return 0; while ((fd_entry = readdir(fd_dir)) != NULL) { char *d_name = fd_entry->d_name; char path[PATH_MAX]; snprintf(path, PATH_MAX, "/proc/%s/fd/%s", pid, d_name); if (does_path_links_to(path, device_file_path)) { result = 1; break; } } closedir(fd_dir); return result; } static int device_has_master(const char *device_file_path) { pid_t myself = getpid(); DIR *proc_dir; struct dirent *proc_entry; int result = 0; proc_dir = opendir("/proc"); if (proc_dir == NULL) return 0; while ((proc_entry = readdir(proc_dir)) != NULL) { char *d_name = proc_entry->d_name; if (d_name[0] < '0' || d_name[0] > '9' || myself == atoi(d_name)) { continue; } if (process_opened_files(d_name, device_file_path)) { result = 1; break; } if (process_opened_device(d_name, device_file_path)) { result = 1; break; } } closedir(proc_dir); return result; } static void wait_for_master(const char *device_path) { const unsigned int TOTAL_WAIT_US = 5000000L; const unsigned int SLEEP_INTERVAL_US = 100000L; unsigned int cnt = TOTAL_WAIT_US / SLEEP_INTERVAL_US; int has_master = 0; while ((has_master = device_has_master(device_path)) == 0 && cnt--) usleep(SLEEP_INTERVAL_US); if (!has_master) evdi_log("Wait for master timed out"); } static int wait_for_device(const char *device_path) { const unsigned int TOTAL_WAIT_US = 5000000L; const unsigned int SLEEP_INTERVAL_US = 100000L; unsigned int cnt = TOTAL_WAIT_US / SLEEP_INTERVAL_US; int fd = 0; while ((fd = open(device_path, O_RDWR)) < 0 && cnt--) usleep(SLEEP_INTERVAL_US); if (fd < 0) evdi_log("Failed to open a device: %s", strerror(errno)); return fd; } static int open_device(int device) { char dev[PATH_MAX] = ""; int fd = 0; snprintf(dev, PATH_MAX, "/dev/dri/card%d", device); #ifndef CHROMEOS wait_for_master(dev); #endif fd = wait_for_device(dev); if (fd >= 0) { const int err = ioctl(fd, DRM_IOCTL_DROP_MASTER, NULL); if (err == 0) evdi_log("Dropped master on %s", dev); } return fd; } // ********************* Public part ************************** evdi_handle evdi_open(int device) { int fd; evdi_handle h = EVDI_INVALID_HANDLE; fd = open_device(device); if (fd > 0) { if (is_evdi(fd) && is_evdi_compatible(fd)) { h = calloc(1, sizeof(struct evdi_device_context)); if (h) { h->fd = fd; h->device_index = device; } } if (h == EVDI_INVALID_HANDLE) close(fd); } return h; } enum evdi_device_status evdi_check_device(int device) { struct dirent *fd_entry; DIR *fd_dir; enum evdi_device_status status = UNRECOGNIZED; char path[PATH_MAX]; if (!device_exists(device)) return NOT_PRESENT; fd_dir = opendir("/sys/devices/platform"); if (fd_dir == NULL) { evdi_log("Failed to list platform devices"); return NOT_PRESENT; } while ((fd_entry = readdir(fd_dir)) != NULL) { if (strncmp(fd_entry->d_name, "evdi", 4) != 0) continue; snprintf(path, PATH_MAX, "/sys/devices/platform/%s/drm/card%d", fd_entry->d_name, device); if (path_exists(path)) { status = AVAILABLE; break; } } closedir(fd_dir); return status; } int evdi_add_device(void) { FILE *add_devices = fopen("/sys/devices/evdi/add", "w"); int written = 0; if (add_devices != NULL) { static const char devices_to_add[] = "1"; const size_t elem_bytes = 1; written = fwrite(devices_to_add, elem_bytes, sizeof(devices_to_add), add_devices); fclose(add_devices); } return written; } void evdi_close(evdi_handle handle) { if (handle != EVDI_INVALID_HANDLE) { close(handle->fd); free(handle); } } void evdi_connect(evdi_handle handle, const unsigned char *edid, const unsigned int edid_length, const uint32_t sku_area_limit) { struct drm_evdi_connect cmd = { .connected = 1, .dev_index = handle->device_index, .edid = edid, .edid_length = edid_length, .sku_area_limit = sku_area_limit, }; do_ioctl(handle->fd, DRM_IOCTL_EVDI_CONNECT, &cmd, "connect"); } void evdi_disconnect(evdi_handle handle) { struct drm_evdi_connect cmd = { 0, 0, 0, 0, 0 }; do_ioctl(handle->fd, DRM_IOCTL_EVDI_CONNECT, &cmd, "disconnect"); } void evdi_grab_pixels(evdi_handle handle, struct evdi_rect *rects, int *num_rects) { struct drm_clip_rect kernelDirts[MAX_DIRTS] = { { 0, 0, 0, 0 } }; struct evdi_frame_buffer_node *destinationNode = NULL; struct evdi_buffer *destinationBuffer = NULL; destinationNode = findBuffer(handle, handle->bufferToUpdate); if (!destinationNode) { evdi_log("Buffer %d not found. Not grabbing.", handle->bufferToUpdate); *num_rects = 0; return; } destinationBuffer = &destinationNode->frame_buffer; struct drm_evdi_grabpix grab = { EVDI_GRABPIX_MODE_DIRTY, destinationBuffer->width, destinationBuffer->height, destinationBuffer->stride, destinationBuffer->buffer, MAX_DIRTS, kernelDirts }; if (do_ioctl( handle->fd, DRM_IOCTL_EVDI_GRABPIX, &grab, "grabpix") == 0) { /* * Buffer was filled by ioctl * now we only have to fill the dirty rects */ int r = 0; for (; r < grab.num_rects; ++r) { rects[r].x1 = kernelDirts[r].x1; rects[r].y1 = kernelDirts[r].y1; rects[r].x2 = kernelDirts[r].x2; rects[r].y2 = kernelDirts[r].y2; } *num_rects = grab.num_rects; } else { int id = destinationBuffer->id; evdi_log("Grabbing pixels for buffer %d failed.", id); evdi_log("Ignore if caused by change of mode."); *num_rects = 0; } } void evdi_register_buffer(evdi_handle handle, struct evdi_buffer buffer) { assert(handle); assert(!findBuffer(handle, buffer.id)); addFrameBuffer(handle, &buffer); } void evdi_unregister_buffer(evdi_handle handle, int bufferId) { struct evdi_buffer *bufferToRemove = NULL; assert(handle); bufferToRemove = &findBuffer(handle, bufferId)->frame_buffer; assert(bufferToRemove); removeFrameBuffer(handle, &bufferId); } bool evdi_request_update(evdi_handle handle, int bufferId) { assert(handle); handle->bufferToUpdate = bufferId; { struct drm_evdi_request_update cmd; const int requestResult = do_ioctl( handle->fd, DRM_IOCTL_EVDI_REQUEST_UPDATE, &cmd, "request_update"); const bool grabImmediately = requestResult == 1; return grabImmediately; } } static struct evdi_mode to_evdi_mode(struct drm_evdi_event_mode_changed *event) { struct evdi_mode mode; mode.width = event->hdisplay; mode.height = event->vdisplay; mode.refresh_rate = event->vrefresh; mode.bits_per_pixel = event->bits_per_pixel; mode.pixel_format = event->pixel_format; return mode; } static uint64_t evdi_get_dumb_offset(evdi_handle ehandle, uint32_t handle) { struct drm_mode_map_dumb map_dumb = { 0 }; map_dumb.handle = handle; do_ioctl(ehandle->fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb, "DRM_MODE_MAP_DUMB"); return map_dumb.offset; } static struct evdi_cursor_set to_evdi_cursor_set( evdi_handle handle, struct drm_evdi_event_cursor_set *event) { struct evdi_cursor_set cursor_set; cursor_set.hot_x = event->hot_x; cursor_set.hot_y = event->hot_y; cursor_set.width = event->width; cursor_set.height = event->height; cursor_set.enabled = event->enabled; cursor_set.buffer_length = event->buffer_length; cursor_set.buffer = NULL; cursor_set.pixel_format = event->pixel_format; cursor_set.stride = event->stride; if (event->enabled) { size_t size = event->buffer_length; uint64_t offset = evdi_get_dumb_offset(handle, event->buffer_handle); void *ptr = mmap(0, size, PROT_READ, MAP_SHARED, handle->fd, offset); if (ptr != MAP_FAILED) { cursor_set.buffer = malloc(size); memcpy(cursor_set.buffer, ptr, size); munmap(ptr, size); } } return cursor_set; } static struct evdi_cursor_move to_evdi_cursor_move( struct drm_evdi_event_cursor_move *event) { struct evdi_cursor_move cursor_move; cursor_move.x = event->x; cursor_move.y = event->y; return cursor_move; } static void evdi_handle_event(evdi_handle handle, struct evdi_event_context *evtctx, struct drm_event *e) { switch (e->type) { case DRM_EVDI_EVENT_UPDATE_READY: if (evtctx->update_ready_handler) evtctx->update_ready_handler(handle->bufferToUpdate, evtctx->user_data); break; case DRM_EVDI_EVENT_DPMS: if (evtctx->dpms_handler) { struct drm_evdi_event_dpms *event = (struct drm_evdi_event_dpms *) e; evtctx->dpms_handler(event->mode, evtctx->user_data); } break; case DRM_EVDI_EVENT_MODE_CHANGED: if (evtctx->mode_changed_handler) { struct drm_evdi_event_mode_changed *event = (struct drm_evdi_event_mode_changed *) e; evtctx->mode_changed_handler(to_evdi_mode(event), evtctx->user_data); } break; case DRM_EVDI_EVENT_CRTC_STATE: if (evtctx->crtc_state_handler) { struct drm_evdi_event_crtc_state *event = (struct drm_evdi_event_crtc_state *) e; evtctx->crtc_state_handler(event->state, evtctx->user_data); } break; case DRM_EVDI_EVENT_CURSOR_SET: if (evtctx->cursor_set_handler) { struct drm_evdi_event_cursor_set *event = (struct drm_evdi_event_cursor_set *) e; evtctx->cursor_set_handler(to_evdi_cursor_set(handle, event), evtctx->user_data); } break; case DRM_EVDI_EVENT_CURSOR_MOVE: if (evtctx->cursor_move_handler) { struct drm_evdi_event_cursor_move *event = (struct drm_evdi_event_cursor_move *) e; evtctx->cursor_move_handler(to_evdi_cursor_move(event), evtctx->user_data); } break; default: evdi_log("Warning: Unhandled event"); } } void evdi_handle_events(evdi_handle handle, struct evdi_event_context *evtctx) { char buffer[1024]; int i = 0; int bytesRead = read(handle->fd, buffer, sizeof(buffer)); if (!evtctx) { evdi_log("Error: Event context is null!"); return; } while (i < bytesRead) { struct drm_event *e = (struct drm_event *) &buffer[i]; evdi_handle_event(handle, evtctx, e); i += e->length; } } evdi_selectable evdi_get_event_ready(evdi_handle handle) { return handle->fd; } void evdi_get_lib_version(struct evdi_lib_version *version) { if (version != NULL) { version->version_major = LIBEVDI_VERSION_MAJOR; version->version_minor = LIBEVDI_VERSION_MINOR; version->version_patchlevel = LIBEVDI_VERSION_PATCHLEVEL; } } void evdi_set_logging(struct evdi_logging evdi_logging) { g_evdi_logging = evdi_logging; } evdi-1.6.0+dfsg/library/evdi_lib.h000066400000000000000000000051511343473130400167620ustar00rootroot00000000000000/* SPDX-License-Identifier: LGPL-2.1-only * Copyright (c) 2015 - 2017 DisplayLink (UK) Ltd. */ #ifndef EVDI_LIB_H #define EVDI_LIB_H #include #ifdef __cplusplus extern "C" { #endif #define LIBEVDI_VERSION_MAJOR 1 #define LIBEVDI_VERSION_MINOR 6 #define LIBEVDI_VERSION_PATCHLEVEL 0 struct evdi_lib_version { int version_major; int version_minor; int version_patchlevel; }; struct evdi_device_context; typedef struct evdi_device_context *evdi_handle; typedef int evdi_selectable; enum evdi_device_status { AVAILABLE, UNRECOGNIZED, NOT_PRESENT }; struct evdi_rect { int x1, y1, x2, y2; }; struct evdi_mode { int width; int height; int refresh_rate; int bits_per_pixel; unsigned int pixel_format; }; struct evdi_buffer { int id; void *buffer; int width; int height; int stride; struct evdi_rect *rects; int rect_count; }; struct evdi_cursor_set { int32_t hot_x; int32_t hot_y; uint32_t width; uint32_t height; uint8_t enabled; uint32_t buffer_length; uint32_t *buffer; uint32_t pixel_format; uint32_t stride; }; struct evdi_cursor_move { int32_t x; int32_t y; }; struct evdi_event_context { void (*dpms_handler)(int dpms_mode, void *user_data); void (*mode_changed_handler)(struct evdi_mode mode, void *user_data); void (*update_ready_handler)(int buffer_to_be_updated, void *user_data); void (*crtc_state_handler)(int state, void *user_data); void (*cursor_set_handler)(struct evdi_cursor_set cursor_set, void *user_data); void (*cursor_move_handler)(struct evdi_cursor_move cursor_move, void *user_data); void *user_data; }; struct evdi_logging { void (*function)(void *user_data, const char *fmt, ...); void *user_data; }; #define EVDI_INVALID_HANDLE NULL enum evdi_device_status evdi_check_device(int device); evdi_handle evdi_open(int device); int evdi_add_device(void); void evdi_close(evdi_handle handle); void evdi_connect(evdi_handle handle, const unsigned char *edid, const unsigned int edid_length, const uint32_t sku_area_limit); void evdi_disconnect(evdi_handle handle); void evdi_grab_pixels(evdi_handle handle, struct evdi_rect *rects, int *num_rects); void evdi_register_buffer(evdi_handle handle, struct evdi_buffer buffer); void evdi_unregister_buffer(evdi_handle handle, int bufferId); bool evdi_request_update(evdi_handle handle, int bufferId); void evdi_handle_events(evdi_handle handle, struct evdi_event_context *evtctx); evdi_selectable evdi_get_event_ready(evdi_handle handle); void evdi_get_lib_version(struct evdi_lib_version *version); void evdi_set_logging(struct evdi_logging evdi_logging); #ifdef __cplusplus } #endif #endif evdi-1.6.0+dfsg/module/000077500000000000000000000000001343473130400146535ustar00rootroot00000000000000evdi-1.6.0+dfsg/module/Kconfig000066400000000000000000000011331343473130400161540ustar00rootroot00000000000000# # Copyright (c) 2015 - 2016 DisplayLink (UK) Ltd. # # This file is subject to the terms and conditions of the GNU General Public # License v2. See the file COPYING in the main directory of this archive for # more details. # config DRM_EVDI tristate "Extensible Virtual Display Interface" depends on DRM select DRM_KMS_HELPER help This is a KMS interface driver allowing user-space programs to register a virtual display (that imitates physical monitor) and retrieve contents (as a frame buffer) that system renders on it. Say M/Y to add support for these devices via DRM/KMS interfaces. evdi-1.6.0+dfsg/module/LICENSE000066400000000000000000000432541343473130400156700ustar00rootroot00000000000000 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. evdi-1.6.0+dfsg/module/Makefile000066400000000000000000000032071343473130400163150ustar00rootroot00000000000000# # Copyright (c) 2015 - 2019 DisplayLink (UK) Ltd. # # This file is subject to the terms and conditions of the GNU General Public # License v2. See the file COPYING in the main directory of this archive for # more details. # ifneq ($(DKMS_BUILD),) # DKMS KERN_DIR := /lib/modules/$(KERNELRELEASE)/build ccflags-y := -Iinclude/drm evdi-y := evdi_drv.o evdi_modeset.o evdi_connector.o evdi_encoder.o evdi_main.o evdi_fb.o evdi_gem.o evdi_painter.o evdi_params.o evdi_cursor.o evdi_debug.o evdi-$(CONFIG_COMPAT) += evdi_ioc32.o obj-m := evdi.o KBUILD_VERBOSE ?= 1 all: $(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE) SUBDIRS=$(CURDIR) SRCROOT=$(CURDIR) CONFIG_MODULE_SIG= -C $(KERN_DIR) modules clean: @echo $(KERN_DIR) $(MAKE) KBUILD_VERBOSE=$(KBUILD_VERBOSE) SUBDIRS=$(CURDIR) SRCROOT=$(CURDIR) -C $(KERN_DIR) clean else # Not DKMS ifneq ($(KERNELRELEASE),) # inside kbuild # Note: this can be removed once it is in kernel tree and Kconfig is properly used CONFIG_DRM_EVDI := m LINUXINCLUDE := $(subst -I,-isystem,$(LINUXINCLUDE)) ccflags-y := -isystem include/drm $(CFLAGS) evdi-y := evdi_drv.o evdi_modeset.o evdi_connector.o evdi_encoder.o evdi_main.o evdi_fb.o evdi_gem.o evdi_painter.o evdi_params.o evdi_cursor.o evdi_debug.o evdi-$(CONFIG_COMPAT) += evdi_ioc32.o obj-$(CONFIG_DRM_EVDI) := evdi.o else # kbuild against specified or current kernel ifeq ($(KVER),) KVER := $(shell uname -r) endif ifeq ($(KDIR),) KDIR := /lib/modules/$(KVER)/build endif default: module module: $(MAKE) -C $(KDIR) M=$$PWD clean: rm -rf *.o *.ko .tmp* .*.*.cmd Module.symvers evdi.mod.c modules.order endif # ifneq ($(KERNELRELEASE),) endif # ifneq ($(DKMS_BUILD),) evdi-1.6.0+dfsg/module/dkms.conf000066400000000000000000000006401343473130400164600ustar00rootroot00000000000000## @file # Linux DKMS config script for the EVDI kernel modules # # # Copyright (c) 2015 DisplayLink (UK) Ltd. # PACKAGE_NAME="evdi" PACKAGE_VERSION=1.0.0 AUTOINSTALL=yes MAKE[0]="make all INCLUDEDIR=/lib/modules/$kernelver/build/include KVERSION=$kernelver DKMS_BUILD=1" DEST_MODULE_LOCATION[0]="/kernel/drivers/gpu/drm/evdi" BUILT_MODULE_NAME[0]="evdi" CLEAN="make clean KERNELRELEASE=$kernelver DKMS_BUILD=1" evdi-1.6.0+dfsg/module/evdi_connector.c000066400000000000000000000104551343473130400200250ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2012 Red Hat * Copyright (c) 2015 - 2017 DisplayLink (UK) Ltd. * * Based on parts on udlfb.c: * Copyright (C) 2009 its respective authors * * This file is subject to the terms and conditions of the GNU General Public * License v2. See the file COPYING in the main directory of this archive for * more details. */ #include #include #include #include #include #include #include "evdi_drv.h" /* * dummy connector to just get EDID, * all EVDI appear to have a DVI-D */ static int evdi_get_modes(struct drm_connector *connector) { struct evdi_device *evdi = connector->dev->dev_private; struct edid *edid = NULL; int ret = 0; edid = (struct edid *)evdi_painter_get_edid_copy(evdi); if (!edid) { #if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE drm_connector_update_edid_property(connector, NULL); #else drm_mode_connector_update_edid_property(connector, NULL); #endif return 0; } #if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE ret = drm_connector_update_edid_property(connector, edid); #else ret = drm_mode_connector_update_edid_property(connector, edid); #endif if (!ret) drm_add_edid_modes(connector, edid); else EVDI_ERROR("Failed to set edid modes! error: %d", ret); kfree(edid); return ret; } static enum drm_mode_status evdi_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { struct evdi_device *evdi = connector->dev->dev_private; uint32_t mode_area = mode->hdisplay * mode->vdisplay; if (evdi->sku_area_limit == 0) return MODE_OK; if (mode_area > evdi->sku_area_limit) { EVDI_WARN("(dev=%d) Mode %dx%d@%d rejected\n", evdi->dev_index, mode->hdisplay, mode->vdisplay, drm_mode_vrefresh(mode)); return MODE_BAD; } return MODE_OK; } static enum drm_connector_status evdi_detect(struct drm_connector *connector, __always_unused bool force) { struct evdi_device *evdi = connector->dev->dev_private; EVDI_CHECKPT(); if (evdi_painter_is_connected(evdi)) { EVDI_DEBUG("(dev=%d) poll connector state: connected\n", evdi->dev_index); return connector_status_connected; } EVDI_DEBUG("(dev=%d) poll connector state: disconnected\n", evdi->dev_index); return connector_status_disconnected; } static void evdi_connector_destroy(struct drm_connector *connector) { drm_connector_unregister(connector); drm_connector_cleanup(connector); kfree(connector); } static struct drm_encoder *evdi_best_encoder(struct drm_connector *connector) { return drm_encoder_find(connector->dev, #if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE NULL, #endif connector->encoder_ids[0]); } static struct drm_connector_helper_funcs evdi_connector_helper_funcs = { .get_modes = evdi_get_modes, .mode_valid = evdi_mode_valid, .best_encoder = evdi_best_encoder, }; static const struct drm_connector_funcs evdi_connector_funcs = { #if KERNEL_VERSION(4, 14, 0) > LINUX_VERSION_CODE .dpms = drm_atomic_helper_connector_dpms, #endif .detect = evdi_detect, .fill_modes = drm_helper_probe_single_connector_modes, .destroy = evdi_connector_destroy, #if KERNEL_VERSION(4, 14, 0) > LINUX_VERSION_CODE .set_property = drm_atomic_helper_connector_set_property, #endif .reset = drm_atomic_helper_connector_reset, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, .atomic_destroy_state = drm_atomic_helper_connector_destroy_state }; int evdi_connector_init(struct drm_device *dev, struct drm_encoder *encoder) { struct drm_connector *connector; connector = kzalloc(sizeof(struct drm_connector), GFP_KERNEL); if (!connector) return -ENOMEM; /* TODO: Initialize connector with actual connector type */ drm_connector_init(dev, connector, &evdi_connector_funcs, DRM_MODE_CONNECTOR_DVII); drm_connector_helper_add(connector, &evdi_connector_helper_funcs); connector->polled = DRM_CONNECTOR_POLL_HPD; drm_connector_register(connector); #if KERNEL_VERSION(4, 19, 0) <= LINUX_VERSION_CODE drm_connector_attach_encoder(connector, encoder); #else drm_mode_connector_attach_encoder(connector, encoder); #endif #if KERNEL_VERSION(4, 9, 0) > LINUX_VERSION_CODE drm_object_attach_property(&connector->base, dev->mode_config.dirty_info_property, 1); #endif return 0; } evdi-1.6.0+dfsg/module/evdi_cursor.c000066400000000000000000000152431343473130400173500ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-only /* * evdi_cursor.c * * Copyright (c) 2016 The Chromium OS Authors * Copyright (c) 2016 - 2017 DisplayLink (UK) Ltd. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be 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, see . */ #include #include #include #include #include "evdi_cursor.h" #include "evdi_drv.h" /* * EVDI drm cursor private structure. */ struct evdi_cursor { bool enabled; int32_t x; int32_t y; uint32_t width; uint32_t height; int32_t hot_x; int32_t hot_y; uint32_t pixel_format; uint32_t stride; struct evdi_gem_object *obj; struct mutex lock; }; static void evdi_cursor_set_gem(struct evdi_cursor *cursor, struct evdi_gem_object *obj) { #if KERNEL_VERSION(4, 12, 0) <= LINUX_VERSION_CODE if (obj) drm_gem_object_get(&obj->base); if (cursor->obj) drm_gem_object_put_unlocked(&cursor->obj->base); #else if (obj) drm_gem_object_reference(&obj->base); if (cursor->obj) drm_gem_object_unreference_unlocked(&cursor->obj->base); #endif cursor->obj = obj; } struct evdi_gem_object *evdi_cursor_gem(struct evdi_cursor *cursor) { return cursor->obj; } int evdi_cursor_init(struct evdi_cursor **cursor) { if (WARN_ON(*cursor)) return -EINVAL; *cursor = kzalloc(sizeof(struct evdi_cursor), GFP_KERNEL); if (*cursor) { mutex_init(&(*cursor)->lock); return 0; } else { return -ENOMEM; } } void evdi_cursor_lock(struct evdi_cursor *cursor) { mutex_lock(&cursor->lock); } void evdi_cursor_unlock(struct evdi_cursor *cursor) { mutex_unlock(&cursor->lock); } void evdi_cursor_free(struct evdi_cursor *cursor) { if (WARN_ON(!cursor)) return; evdi_cursor_set_gem(cursor, NULL); kfree(cursor); } bool evdi_cursor_enabled(struct evdi_cursor *cursor) { return cursor->enabled; } void evdi_cursor_enable(struct evdi_cursor *cursor, bool enable) { evdi_cursor_lock(cursor); cursor->enabled = enable; if (!enable) evdi_cursor_set_gem(cursor, NULL); evdi_cursor_unlock(cursor); } void evdi_cursor_set(struct evdi_cursor *cursor, struct evdi_gem_object *obj, uint32_t width, uint32_t height, int32_t hot_x, int32_t hot_y, uint32_t pixel_format, uint32_t stride) { int err = 0; evdi_cursor_lock(cursor); if (obj && !obj->vmapping) err = evdi_gem_vmap(obj); if (err != 0) { EVDI_ERROR("Failed to map cursor.\n"); obj = NULL; } cursor->enabled = obj != NULL; cursor->width = width; cursor->height = height; cursor->hot_x = hot_x; cursor->hot_y = hot_y; cursor->pixel_format = pixel_format; cursor->stride = stride; evdi_cursor_set_gem(cursor, obj); evdi_cursor_unlock(cursor); } void evdi_cursor_move(struct evdi_cursor *cursor, int32_t x, int32_t y) { evdi_cursor_lock(cursor); cursor->x = x; cursor->y = y; evdi_cursor_unlock(cursor); } static inline uint32_t blend_component(uint32_t pixel, uint32_t blend, uint32_t alpha) { uint32_t pre_blend = (pixel * (255 - alpha) + blend * alpha); return (pre_blend + ((pre_blend + 1) << 8)) >> 16; } static inline uint32_t blend_alpha(const uint32_t pixel_val32, uint32_t blend_val32) { uint32_t alpha = (blend_val32 >> 24); return blend_component(pixel_val32 & 0xff, blend_val32 & 0xff, alpha) | blend_component((pixel_val32 & 0xff00) >> 8, (blend_val32 & 0xff00) >> 8, alpha) << 8 | blend_component((pixel_val32 & 0xff0000) >> 16, (blend_val32 & 0xff0000) >> 16, alpha) << 16; } static int evdi_cursor_compose_pixel(char __user *buffer, int const cursor_value, int const fb_value, int cmd_offset) { int const composed_value = blend_alpha(fb_value, cursor_value); return copy_to_user(buffer + cmd_offset, &composed_value, 4); } int evdi_cursor_compose_and_copy(struct evdi_cursor *cursor, struct evdi_framebuffer *efb, char __user *buffer, int buf_byte_stride) { int x, y; struct drm_framebuffer *fb = &efb->base; const int h_cursor_w = cursor->width >> 1; const int h_cursor_h = cursor->height >> 1; uint32_t *cursor_buffer = NULL; uint32_t bytespp = 0; if (!cursor->enabled) return 0; if (!cursor->obj) return -EINVAL; if (!cursor->obj->vmapping) return -EINVAL; bytespp = evdi_fb_get_bpp(cursor->pixel_format); bytespp = DIV_ROUND_UP(bytespp, 8); if (bytespp != 4) { EVDI_ERROR("Unsupported cursor format bpp=%u\n", bytespp); return -EINVAL; } if (cursor->width * cursor->height * bytespp > cursor->obj->base.size){ EVDI_ERROR("Wrong cursor size\n"); return -EINVAL; } cursor_buffer = (uint32_t *)cursor->obj->vmapping; for (y = -h_cursor_h; y < h_cursor_h; ++y) { for (x = -h_cursor_w; x < h_cursor_w; ++x) { uint32_t curs_val; int *fbsrc; int fb_value; int cmd_offset; int cursor_pix; int const mouse_pix_x = cursor->x + x + h_cursor_w; int const mouse_pix_y = cursor->y + y + h_cursor_h; bool const is_pix_sane = mouse_pix_x >= 0 && mouse_pix_y >= 0 && mouse_pix_x < fb->width && mouse_pix_y < fb->height; if (!is_pix_sane) continue; cursor_pix = h_cursor_w+x + (h_cursor_h+y)*cursor->width; curs_val = le32_to_cpu(cursor_buffer[cursor_pix]); fbsrc = (int *)efb->obj->vmapping; fb_value = *(fbsrc + ((fb->pitches[0]>>2) * mouse_pix_y + mouse_pix_x)); cmd_offset = (buf_byte_stride * mouse_pix_y) + (mouse_pix_x * bytespp); if (evdi_cursor_compose_pixel(buffer, curs_val, fb_value, cmd_offset)) { EVDI_ERROR("Failed to compose cursor pixel\n"); return -EFAULT; } } } return 0; } void evdi_cursor_position(struct evdi_cursor *cursor, int32_t *x, int32_t *y) { *x = cursor->x; *y = cursor->y; } void evdi_cursor_hotpoint(struct evdi_cursor *cursor, int32_t *hot_x, int32_t *hot_y) { *hot_x = cursor->hot_x; *hot_y = cursor->hot_y; } void evdi_cursor_size(struct evdi_cursor *cursor, uint32_t *width, uint32_t *height) { *width = cursor->width; *height = cursor->height; } void evdi_cursor_format(struct evdi_cursor *cursor, uint32_t *format) { *format = cursor->pixel_format; } void evdi_cursor_stride(struct evdi_cursor *cursor, uint32_t *stride) { *stride = cursor->stride; } evdi-1.6.0+dfsg/module/evdi_cursor.h000066400000000000000000000043161343473130400173540ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only * evdi_cursor.h * * Copyright (c) 2016 The Chromium OS Authors * Copyright (c) 2016 - 2017 DisplayLink (UK) Ltd. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be 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, see . */ #ifndef _EVDI_CURSOR_H_ #define _EVDI_CURSOR_H_ #include #include #include struct evdi_cursor; struct evdi_framebuffer; struct evdi_gem_object; int evdi_cursor_init(struct evdi_cursor **cursor); void evdi_cursor_free(struct evdi_cursor *cursor); void evdi_cursor_lock(struct evdi_cursor *cursor); void evdi_cursor_unlock(struct evdi_cursor *cursor); bool evdi_cursor_enabled(struct evdi_cursor *cursor); void evdi_cursor_enable(struct evdi_cursor *cursor, bool enabled); void evdi_cursor_set(struct evdi_cursor *cursor, struct evdi_gem_object *obj, uint32_t width, uint32_t height, int32_t hot_x, int32_t hot_y, uint32_t pixel_format, uint32_t stride); void evdi_cursor_move(struct evdi_cursor *cursor, int32_t x, int32_t y); void evdi_cursor_position(struct evdi_cursor *cursor, int32_t *x, int32_t *y); void evdi_cursor_hotpoint(struct evdi_cursor *cursor, int32_t *hot_x, int32_t *hot_y); void evdi_cursor_size(struct evdi_cursor *cursor, uint32_t *width, uint32_t *height); void evdi_cursor_format(struct evdi_cursor *cursor, uint32_t *format); void evdi_cursor_stride(struct evdi_cursor *cursor, uint32_t *stride); struct evdi_gem_object *evdi_cursor_gem(struct evdi_cursor *cursor); int evdi_cursor_compose_and_copy(struct evdi_cursor *cursor, struct evdi_framebuffer *efb, char __user *buffer, int buf_byte_stride); #endif evdi-1.6.0+dfsg/module/evdi_debug.c000066400000000000000000000014641343473130400171210ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2019 DisplayLink (UK) Ltd. * * This file is subject to the terms and conditions of the GNU General Public * License v2. See the file COPYING in the main directory of this archive for * more details. */ #include #include "evdi_debug.h" void evdi_log_process(void) { int task_pid = (int)task_pid_nr(current); char task_comm[TASK_COMM_LEN] = { 0 }; get_task_comm(task_comm, current); if (current->group_leader) { char process_comm[TASK_COMM_LEN] = { 0 }; get_task_comm(process_comm, current->group_leader); EVDI_INFO("Task %d (%s) of process %d (%s)\n", task_pid, task_comm, (int)task_pid_nr(current->group_leader), process_comm); } else { EVDI_INFO("Task %d (%s)\n", task_pid, task_comm); } } evdi-1.6.0+dfsg/module/evdi_debug.h000066400000000000000000000034311343473130400171220ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only * Copyright (c) 2015 - 2016 DisplayLink (UK) Ltd. * * This file is subject to the terms and conditions of the GNU General Public * License v2. See the file COPYING in the main directory of this archive for * more details. */ #ifndef EVDI_DEBUG_H #define EVDI_DEBUG_H #include "evdi_params.h" #define EVDI_LOGLEVEL_ALWAYS 0 #define EVDI_LOGLEVEL_FATAL 1 #define EVDI_LOGLEVEL_ERROR 2 #define EVDI_LOGLEVEL_WARN 3 #define EVDI_LOGLEVEL_INFO 4 #define EVDI_LOGLEVEL_DEBUG 5 #define EVDI_LOGLEVEL_VERBOSE 6 #define EVDI_PRINTK(KERN_LEVEL, LEVEL, FORMAT_STR, ...) do { \ if (evdi_loglevel >= LEVEL) {\ printk(KERN_LEVEL "evdi: " FORMAT_STR, ##__VA_ARGS__); \ } \ } while (0) #define EVDI_FATAL(FORMAT_STR, ...) \ EVDI_PRINTK(KERN_CRIT, EVDI_LOGLEVEL_FATAL,\ "[F] %s:%d " FORMAT_STR, __func__, __LINE__, ##__VA_ARGS__) #define EVDI_ERROR(FORMAT_STR, ...) \ EVDI_PRINTK(KERN_ERR, EVDI_LOGLEVEL_ERROR,\ "[E] %s:%d " FORMAT_STR, __func__, __LINE__, ##__VA_ARGS__) #define EVDI_WARN(FORMAT_STR, ...) \ EVDI_PRINTK(KERN_WARNING, EVDI_LOGLEVEL_WARN,\ "[W] %s:%d " FORMAT_STR, __func__, __LINE__, ##__VA_ARGS__) #define EVDI_INFO(FORMAT_STR, ...) \ EVDI_PRINTK(KERN_DEFAULT, EVDI_LOGLEVEL_INFO,\ "[I] " FORMAT_STR, ##__VA_ARGS__) #define EVDI_DEBUG(FORMAT_STR, ...) \ EVDI_PRINTK(KERN_DEFAULT, EVDI_LOGLEVEL_DEBUG,\ "[D] %s:%d " FORMAT_STR, __func__, __LINE__, ##__VA_ARGS__) #define EVDI_VERBOSE(FORMAT_STR, ...) \ EVDI_PRINTK(KERN_DEFAULT, EVDI_LOGLEVEL_VERBOSE,\ "[V] %s:%d " FORMAT_STR, __func__, __LINE__, ##__VA_ARGS__) #define EVDI_CHECKPT() EVDI_VERBOSE("\n") #define EVDI_ENTER() EVDI_VERBOSE("enter\n") #define EVDI_EXIT() EVDI_VERBOSE("exit\n") void evdi_log_process(void); #endif /* EVDI_DEBUG_H */ evdi-1.6.0+dfsg/module/evdi_drm.h000066400000000000000000000047031343473130400166210ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only * Copyright (c) 2015 - 2017 DisplayLink (UK) Ltd. * * This file is subject to the terms and conditions of the GNU General Public * License v2. See the file COPYING in the main directory of this archive for * more details. */ #ifndef __UAPI_EVDI_DRM_H__ #define __UAPI_EVDI_DRM_H__ /* Output events sent from driver to evdi lib */ #define DRM_EVDI_EVENT_UPDATE_READY 0x80000000 #define DRM_EVDI_EVENT_DPMS 0x80000001 #define DRM_EVDI_EVENT_MODE_CHANGED 0x80000002 #define DRM_EVDI_EVENT_CRTC_STATE 0x80000003 #define DRM_EVDI_EVENT_CURSOR_SET 0x80000004 #define DRM_EVDI_EVENT_CURSOR_MOVE 0x80000005 struct drm_evdi_event_update_ready { struct drm_event base; }; struct drm_evdi_event_dpms { struct drm_event base; int32_t mode; }; struct drm_evdi_event_mode_changed { struct drm_event base; int32_t hdisplay; int32_t vdisplay; int32_t vrefresh; int32_t bits_per_pixel; uint32_t pixel_format; }; struct drm_evdi_event_crtc_state { struct drm_event base; int32_t state; }; struct drm_evdi_connect { int32_t connected; int32_t dev_index; const unsigned char * __user edid; uint32_t edid_length; uint32_t sku_area_limit; }; struct drm_evdi_request_update { int32_t reserved; }; enum drm_evdi_grabpix_mode { EVDI_GRABPIX_MODE_RECTS = 0, EVDI_GRABPIX_MODE_DIRTY = 1, }; struct drm_evdi_grabpix { enum drm_evdi_grabpix_mode mode; int32_t buf_width; int32_t buf_height; int32_t buf_byte_stride; unsigned char __user *buffer; int32_t num_rects; struct drm_clip_rect __user *rects; }; struct drm_evdi_event_cursor_set { struct drm_event base; int32_t hot_x; int32_t hot_y; uint32_t width; uint32_t height; uint8_t enabled; uint32_t buffer_handle; uint32_t buffer_length; uint32_t pixel_format; uint32_t stride; }; struct drm_evdi_event_cursor_move { struct drm_event base; int32_t x; int32_t y; }; /* Input ioctls from evdi lib to driver */ #define DRM_EVDI_CONNECT 0x00 #define DRM_EVDI_REQUEST_UPDATE 0x01 #define DRM_EVDI_GRABPIX 0x02 /* LAST_IOCTL 0x5F -- 96 driver specific ioctls to use */ #define DRM_IOCTL_EVDI_CONNECT DRM_IOWR(DRM_COMMAND_BASE + \ DRM_EVDI_CONNECT, struct drm_evdi_connect) #define DRM_IOCTL_EVDI_REQUEST_UPDATE DRM_IOWR(DRM_COMMAND_BASE + \ DRM_EVDI_REQUEST_UPDATE, struct drm_evdi_request_update) #define DRM_IOCTL_EVDI_GRABPIX DRM_IOWR(DRM_COMMAND_BASE + \ DRM_EVDI_GRABPIX, struct drm_evdi_grabpix) #endif /* __EVDI_UAPI_DRM_H__ */ evdi-1.6.0+dfsg/module/evdi_drv.c000066400000000000000000000207751343473130400166340ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2012 Red Hat * Copyright (c) 2015 - 2018 DisplayLink (UK) Ltd. * * This file is subject to the terms and conditions of the GNU General Public * License v2. See the file COPYING in the main directory of this archive for * more details. */ #include #include #include #include #include "evdi_drv.h" #include "evdi_drm.h" #include "evdi_params.h" #include "evdi_debug.h" MODULE_AUTHOR("DisplayLink (UK) Ltd."); MODULE_DESCRIPTION("Extensible Virtual Display Interface"); MODULE_LICENSE("GPL"); #define EVDI_DEVICE_COUNT_MAX 16 static struct evdi_context { struct device *root_dev; unsigned int dev_count; struct platform_device *devices[EVDI_DEVICE_COUNT_MAX]; } evdi_context; static struct drm_driver driver; struct drm_ioctl_desc evdi_painter_ioctls[] = { DRM_IOCTL_DEF_DRV(EVDI_CONNECT, evdi_painter_connect_ioctl, DRM_UNLOCKED), DRM_IOCTL_DEF_DRV(EVDI_REQUEST_UPDATE, evdi_painter_request_update_ioctl, DRM_UNLOCKED), DRM_IOCTL_DEF_DRV(EVDI_GRABPIX, evdi_painter_grabpix_ioctl, DRM_UNLOCKED), }; static const struct vm_operations_struct evdi_gem_vm_ops = { .fault = evdi_gem_fault, .open = drm_gem_vm_open, .close = drm_gem_vm_close, }; static const struct file_operations evdi_driver_fops = { .owner = THIS_MODULE, .open = drm_open, .mmap = evdi_drm_gem_mmap, .poll = drm_poll, .read = drm_read, .unlocked_ioctl = drm_ioctl, .release = drm_release, #ifdef CONFIG_COMPAT .compat_ioctl = evdi_compat_ioctl, #endif .llseek = noop_llseek, }; static int evdi_enable_vblank(__always_unused struct drm_device *dev, #if KERNEL_VERSION(4, 4, 0) > LINUX_VERSION_CODE __always_unused int pipe) #else __always_unused unsigned int pipe) #endif { return 1; } static void evdi_disable_vblank(__always_unused struct drm_device *dev, #if KERNEL_VERSION(4, 4, 0) > LINUX_VERSION_CODE __always_unused int pipe) #else __always_unused unsigned int pipe) #endif { } static struct drm_driver driver = { .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME | DRIVER_ATOMIC, #if KERNEL_VERSION(4, 12, 0) > LINUX_VERSION_CODE .load = evdi_driver_load, #endif .unload = evdi_driver_unload, .preclose = evdi_driver_preclose, .postclose = evdi_driver_postclose, /* gem hooks */ .gem_free_object = evdi_gem_free_object, .gem_vm_ops = &evdi_gem_vm_ops, .dumb_create = evdi_dumb_create, .dumb_map_offset = evdi_gem_mmap, .dumb_destroy = drm_gem_dumb_destroy, .ioctls = evdi_painter_ioctls, .num_ioctls = ARRAY_SIZE(evdi_painter_ioctls), .fops = &evdi_driver_fops, .prime_fd_to_handle = drm_gem_prime_fd_to_handle, .gem_prime_import = evdi_gem_prime_import, .prime_handle_to_fd = drm_gem_prime_handle_to_fd, .gem_prime_export = evdi_gem_prime_export, #if KERNEL_VERSION(4, 4, 0) > LINUX_VERSION_CODE .get_vblank_counter = drm_vblank_count, #elif KERNEL_VERSION(4, 12, 0) > LINUX_VERSION_CODE .get_vblank_counter = drm_vblank_no_hw_counter, #endif .enable_vblank = evdi_enable_vblank, .disable_vblank = evdi_disable_vblank, .name = DRIVER_NAME, .desc = DRIVER_DESC, .date = DRIVER_DATE, .major = DRIVER_MAJOR, .minor = DRIVER_MINOR, .patchlevel = DRIVER_PATCHLEVEL, }; static void evdi_add_device(void) { struct platform_device_info pdevinfo = { .parent = NULL, .name = "evdi", .id = evdi_context.dev_count, .res = NULL, .num_res = 0, .data = NULL, .size_data = 0, .dma_mask = DMA_BIT_MASK(32), }; evdi_context.devices[evdi_context.dev_count] = platform_device_register_full(&pdevinfo); if (dma_set_mask(&evdi_context.devices[evdi_context.dev_count]->dev, DMA_BIT_MASK(64))) { EVDI_DEBUG("Unable to change dma mask to 64 bit. "); EVDI_DEBUG("Sticking with 32 bit\n"); } evdi_context.dev_count++; } static int evdi_add_devices(unsigned int val) { if (val == 0) { EVDI_WARN("Adding 0 devices has no effect\n"); return 0; } if (val > EVDI_DEVICE_COUNT_MAX - evdi_context.dev_count) { EVDI_ERROR("Evdi device add failed. Too many devices.\n"); return -EINVAL; } EVDI_DEBUG("Increasing device count to %u\n", evdi_context.dev_count + val); while (val--) evdi_add_device(); return 0; } #if KERNEL_VERSION(4, 12, 0) <= LINUX_VERSION_CODE static int evdi_platform_probe(struct platform_device *pdev) { struct drm_device *dev; int ret; EVDI_CHECKPT(); dev = drm_dev_alloc(&driver, &pdev->dev); if (IS_ERR(dev)) return PTR_ERR(dev); ret = evdi_driver_setup(dev); if (ret) goto err_free; ret = drm_dev_register(dev, 0); if (ret) goto err_free; return 0; err_free: #if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE drm_dev_put(dev); #else drm_dev_unref(dev); #endif return ret; } #else static int evdi_platform_probe(struct platform_device *pdev) { EVDI_CHECKPT(); return drm_platform_init(&driver, pdev); } #endif static int evdi_platform_remove(struct platform_device *pdev) { struct drm_device *drm_dev = (struct drm_device *)platform_get_drvdata(pdev); EVDI_CHECKPT(); #if KERNEL_VERSION(4, 14, 0) > LINUX_VERSION_CODE drm_unplug_dev(drm_dev); #else drm_dev_unplug(drm_dev); #endif return 0; } static void evdi_remove_all(void) { int i; EVDI_DEBUG("removing all evdi devices\n"); for (i = 0; i < evdi_context.dev_count; ++i) { if (evdi_context.devices[i]) { EVDI_DEBUG("removing evdi %d\n", i); platform_device_unregister(evdi_context.devices[i]); evdi_context.devices[i] = NULL; } } evdi_context.dev_count = 0; } static struct platform_driver evdi_platform_driver = { .probe = evdi_platform_probe, .remove = evdi_platform_remove, .driver = { .name = "evdi", .mod_name = KBUILD_MODNAME, .owner = THIS_MODULE, } }; static ssize_t version_show(__always_unused struct device *dev, __always_unused struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%u.%u.%u\n", DRIVER_MAJOR, DRIVER_MINOR, DRIVER_PATCHLEVEL); } static ssize_t count_show(__always_unused struct device *dev, __always_unused struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%u\n", evdi_context.dev_count); } static ssize_t add_store(__always_unused struct device *dev, __always_unused struct device_attribute *attr, const char *buf, size_t count) { unsigned int val; int ret; if (kstrtouint(buf, 10, &val)) { EVDI_ERROR("Invalid device count \"%s\"\n", buf); return -EINVAL; } ret = evdi_add_devices(val); if (ret) return ret; return count; } static ssize_t remove_all_store(__always_unused struct device *dev, __always_unused struct device_attribute *attr, __always_unused const char *buf, size_t count) { evdi_remove_all(); return count; } static ssize_t loglevel_show(__always_unused struct device *dev, __always_unused struct device_attribute *attr, char *buf) { return snprintf(buf, PAGE_SIZE, "%u\n", evdi_loglevel); } static ssize_t loglevel_store(__always_unused struct device *dev, __always_unused struct device_attribute *attr, const char *buf, size_t count) { unsigned int val; if (kstrtouint(buf, 10, &val)) { EVDI_ERROR("Unable to parse %u\n", val); return -EINVAL; } if (val > EVDI_LOGLEVEL_VERBOSE) { EVDI_ERROR("Invalid loglevel %u\n", val); return -EINVAL; } EVDI_INFO("Setting loglevel to %u\n", val); evdi_loglevel = val; return count; } static struct device_attribute evdi_device_attributes[] = { __ATTR_RO(count), __ATTR_RO(version), __ATTR_RW(loglevel), __ATTR_WO(add), __ATTR_WO(remove_all) }; static int __init evdi_init(void) { int i, ret; EVDI_INFO("Initialising logging on level %u\n", evdi_loglevel); EVDI_INFO("Atomic driver:%s", (driver.driver_features & DRIVER_ATOMIC) ? "yes" : "no"); evdi_context.root_dev = root_device_register("evdi"); if (!PTR_RET(evdi_context.root_dev)) for (i = 0; i < ARRAY_SIZE(evdi_device_attributes); i++) { device_create_file(evdi_context.root_dev, &evdi_device_attributes[i]); } ret = platform_driver_register(&evdi_platform_driver); if (ret) return ret; if (evdi_initial_device_count) return evdi_add_devices(evdi_initial_device_count); return 0; } static void __exit evdi_exit(void) { int i; EVDI_CHECKPT(); evdi_remove_all(); platform_driver_unregister(&evdi_platform_driver); if (!PTR_RET(evdi_context.root_dev)) { for (i = 0; i < ARRAY_SIZE(evdi_device_attributes); i++) { device_remove_file(evdi_context.root_dev, &evdi_device_attributes[i]); } root_device_unregister(evdi_context.root_dev); } } module_init(evdi_init); module_exit(evdi_exit); evdi-1.6.0+dfsg/module/evdi_drv.h000066400000000000000000000131621343473130400166310ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only * Copyright (C) 2012 Red Hat * Copyright (c) 2015 - 2018 DisplayLink (UK) Ltd. * * Based on parts on udlfb.c: * Copyright (C) 2009 its respective authors * * This file is subject to the terms and conditions of the GNU General Public * License v2. See the file COPYING in the main directory of this archive for * more details. */ #ifndef EVDI_DRV_H #define EVDI_DRV_H #include #include #include #include #include #include #include #include #include "evdi_debug.h" #define DRIVER_NAME "evdi" #define DRIVER_DESC "Extensible Virtual Display Interface" #define DRIVER_DATE "20180913" #define DRIVER_MAJOR 1 #define DRIVER_MINOR 6 #define DRIVER_PATCHLEVEL 0 struct evdi_fbdev; struct evdi_painter; struct evdi_device { struct device *dev; struct drm_device *ddev; struct evdi_cursor *cursor; uint32_t sku_area_limit; struct evdi_fbdev *fbdev; struct evdi_painter *painter; int dev_index; }; struct evdi_gem_object { struct drm_gem_object base; struct page **pages; void *vmapping; struct sg_table *sg; struct reservation_object *resv; struct reservation_object _resv; }; #define to_evdi_bo(x) container_of(x, struct evdi_gem_object, base) struct evdi_framebuffer { struct drm_framebuffer base; struct evdi_gem_object *obj; bool active; }; #define to_evdi_fb(x) container_of(x, struct evdi_framebuffer, base) /* modeset */ void evdi_modeset_init(struct drm_device *dev); void evdi_modeset_cleanup(struct drm_device *dev); int evdi_connector_init(struct drm_device *dev, struct drm_encoder *encoder); struct drm_encoder *evdi_encoder_init(struct drm_device *dev); int evdi_driver_load(struct drm_device *dev, unsigned long flags); #if KERNEL_VERSION(4, 11, 0) > LINUX_VERSION_CODE int evdi_driver_unload(struct drm_device *dev); #else void evdi_driver_unload(struct drm_device *dev); #endif void evdi_driver_preclose(struct drm_device *dev, struct drm_file *file_priv); void evdi_driver_postclose(struct drm_device *dev, struct drm_file *file_priv); #ifdef CONFIG_COMPAT long evdi_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg); #endif #ifdef CONFIG_FB int evdi_fbdev_init(struct drm_device *dev); void evdi_fbdev_cleanup(struct drm_device *dev); void evdi_fbdev_unplug(struct drm_device *dev); #endif /* CONFIG_FB */ struct drm_framebuffer *evdi_fb_user_fb_create( struct drm_device *dev, struct drm_file *file, #if KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE struct drm_mode_fb_cmd2 *mode_cmd); #else const struct drm_mode_fb_cmd2 *mode_cmd); #endif int evdi_dumb_create(struct drm_file *file_priv, struct drm_device *dev, struct drm_mode_create_dumb *args); int evdi_gem_mmap(struct drm_file *file_priv, struct drm_device *dev, uint32_t handle, uint64_t *offset); void evdi_gem_free_object(struct drm_gem_object *gem_obj); struct evdi_gem_object *evdi_gem_alloc_object(struct drm_device *dev, size_t size); uint32_t evdi_gem_object_handle_lookup(struct drm_file *filp, struct drm_gem_object *obj); struct drm_gem_object *evdi_gem_prime_import(struct drm_device *dev, struct dma_buf *dma_buf); struct dma_buf *evdi_gem_prime_export(struct drm_device *dev, struct drm_gem_object *obj, int flags); int evdi_gem_vmap(struct evdi_gem_object *obj); void evdi_gem_vunmap(struct evdi_gem_object *obj); int evdi_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma); #if KERNEL_VERSION(4, 11, 0) > LINUX_VERSION_CODE int evdi_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf); #else int evdi_gem_fault(struct vm_fault *vmf); #endif bool evdi_painter_is_connected(struct evdi_device *evdi); void evdi_painter_close(struct evdi_device *evdi, struct drm_file *file); u8 *evdi_painter_get_edid_copy(struct evdi_device *evdi); int evdi_painter_get_num_dirts(struct evdi_device *evdi); void evdi_painter_mark_dirty(struct evdi_device *evdi, const struct drm_clip_rect *rect); void evdi_painter_send_update_ready_if_needed(struct evdi_device *evdi); void evdi_painter_dpms_notify(struct evdi_device *evdi, int mode); void evdi_painter_mode_changed_notify(struct evdi_device *evdi, struct drm_display_mode *mode); void evdi_painter_crtc_state_notify(struct evdi_device *evdi, int state); unsigned int evdi_painter_poll(struct file *filp, struct poll_table_struct *wait); int evdi_painter_status_ioctl(struct drm_device *drm_dev, void *data, struct drm_file *file); int evdi_painter_connect_ioctl(struct drm_device *drm_dev, void *data, struct drm_file *file); int evdi_painter_grabpix_ioctl(struct drm_device *drm_dev, void *data, struct drm_file *file); int evdi_painter_request_update_ioctl(struct drm_device *drm_dev, void *data, struct drm_file *file); int evdi_painter_init(struct evdi_device *evdi); void evdi_painter_cleanup(struct evdi_device *evdi); void evdi_painter_set_scanout_buffer(struct evdi_device *evdi, struct evdi_framebuffer *buffer); struct drm_clip_rect evdi_framebuffer_sanitize_rect( const struct evdi_framebuffer *fb, const struct drm_clip_rect *rect); int evdi_driver_setup(struct drm_device *dev); void evdi_painter_send_cursor_set(struct evdi_painter *painter, struct evdi_cursor *cursor); void evdi_painter_send_cursor_move(struct evdi_painter *painter, struct evdi_cursor *cursor); bool evdi_painter_needs_full_modeset(struct evdi_device *evdi); struct drm_clip_rect evdi_painter_framebuffer_size( struct evdi_painter *painter); int evdi_fb_get_bpp(uint32_t format); #endif evdi-1.6.0+dfsg/module/evdi_encoder.c000066400000000000000000000033271343473130400174520ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2012 Red Hat * Copyright (c) 2015 - 2016 DisplayLink (UK) Ltd. * * Based on parts on udlfb.c: * Copyright (C) 2009 its respective authors * * This file is subject to the terms and conditions of the GNU General Public * License v2. See the file COPYING in the main directory of this archive for * more details. */ #include #include #include #include #include "evdi_drv.h" /* dummy encoder */ static void evdi_enc_destroy(struct drm_encoder *encoder) { drm_encoder_cleanup(encoder); kfree(encoder); } static void evdi_encoder_enable(__always_unused struct drm_encoder *encoder) { } static void evdi_encoder_disable(__always_unused struct drm_encoder *encoder) { } static const struct drm_encoder_helper_funcs evdi_enc_helper_funcs = { .enable = evdi_encoder_enable, .disable = evdi_encoder_disable }; static const struct drm_encoder_funcs evdi_enc_funcs = { .destroy = evdi_enc_destroy, }; struct drm_encoder *evdi_encoder_init(struct drm_device *dev) { struct drm_encoder *encoder; int ret = 0; encoder = kzalloc(sizeof(struct drm_encoder), GFP_KERNEL); if (!encoder) goto err; #if KERNEL_VERSION(4, 5, 0) <= LINUX_VERSION_CODE ret = drm_encoder_init(dev, encoder, &evdi_enc_funcs, DRM_MODE_ENCODER_TMDS, dev_name(dev->dev)); #else ret = drm_encoder_init(dev, encoder, &evdi_enc_funcs, DRM_MODE_ENCODER_TMDS); #endif if (ret) { EVDI_ERROR("Failed to initialize encoder: %d\n", ret); goto err_encoder; } drm_encoder_helper_add(encoder, &evdi_enc_helper_funcs); encoder->possible_crtcs = 1; return encoder; err_encoder: kfree(encoder); err: return NULL; } evdi-1.6.0+dfsg/module/evdi_fb.c000066400000000000000000000350501343473130400164200ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2012 Red Hat * Copyright (c) 2015 - 2016 DisplayLink (UK) Ltd. * * Based on parts on udlfb.c: * Copyright (C) 2009 its respective authors * * This file is subject to the terms and conditions of the GNU General Public * License v2. See the file COPYING in the main directory of this archive for * more details. */ #include #ifdef CONFIG_FB #include #endif /* CONFIG_FB */ #include #include #include #include #include #include #include #include "evdi_drv.h" #if KERNEL_VERSION(4, 12, 0) > LINUX_VERSION_CODE static inline void drm_gem_object_put(struct drm_gem_object *obj) { drm_gem_object_unreference(obj); } static inline void drm_gem_object_put_unlocked(struct drm_gem_object *obj) { drm_gem_object_unreference_unlocked(obj); } #endif struct evdi_fbdev { struct drm_fb_helper helper; struct evdi_framebuffer efb; struct list_head fbdev_list; struct fb_ops fb_ops; int fb_count; }; struct drm_clip_rect evdi_framebuffer_sanitize_rect( const struct evdi_framebuffer *fb, const struct drm_clip_rect *dirty_rect) { struct drm_clip_rect rect = *dirty_rect; if (rect.x1 > rect.x2) { unsigned short tmp = rect.x2; EVDI_WARN("Wrong clip rect: x1 > x2\n"); rect.x2 = rect.x1; rect.x1 = tmp; } if (rect.y1 > rect.y2) { unsigned short tmp = rect.y2; EVDI_WARN("Wrong clip rect: y1 > y2\n"); rect.y2 = rect.y1; rect.y1 = tmp; } if (rect.x1 > fb->base.width) { EVDI_WARN("Wrong clip rect: x1 > fb.width\n"); rect.x1 = fb->base.width; } if (rect.y1 > fb->base.height) { EVDI_WARN("Wrong clip rect: y1 > fb.height\n"); rect.y1 = fb->base.height; } if (rect.x2 > fb->base.width) { EVDI_WARN("Wrong clip rect: x2 > fb.width\n"); rect.x2 = fb->base.width; } if (rect.y2 > fb->base.height) { EVDI_WARN("Wrong clip rect: y2 > fb.height\n"); rect.y2 = fb->base.height; } return rect; } static int evdi_handle_damage(struct evdi_framebuffer *fb, int x, int y, int width, int height) { const struct drm_clip_rect dirty_rect = { x, y, x + width, y + height }; const struct drm_clip_rect rect = evdi_framebuffer_sanitize_rect(fb, &dirty_rect); struct drm_device *dev = fb->base.dev; struct evdi_device *evdi = dev->dev_private; EVDI_CHECKPT(); if (!fb->active) return 0; evdi_painter_set_scanout_buffer(evdi, fb); evdi_painter_mark_dirty(evdi, &rect); return 0; } #ifdef CONFIG_FB static int evdi_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) { unsigned long start = vma->vm_start; unsigned long size = vma->vm_end - vma->vm_start; unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; unsigned long page, pos; if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) return -EINVAL; if (offset > info->fix.smem_len || size > info->fix.smem_len - offset) return -EINVAL; pos = (unsigned long)info->fix.smem_start + offset; pr_notice("mmap() framebuffer addr:%lu size:%lu\n", pos, size); while (size > 0) { page = vmalloc_to_pfn((void *)pos); if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) return -EAGAIN; start += PAGE_SIZE; pos += PAGE_SIZE; if (size > PAGE_SIZE) size -= PAGE_SIZE; else size = 0; } return 0; } static void evdi_fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) { struct evdi_fbdev *efbdev = info->par; EVDI_CHECKPT(); sys_fillrect(info, rect); evdi_handle_damage(&efbdev->efb, rect->dx, rect->dy, rect->width, rect->height); } static void evdi_fb_copyarea(struct fb_info *info, const struct fb_copyarea *region) { struct evdi_fbdev *efbdev = info->par; EVDI_CHECKPT(); sys_copyarea(info, region); evdi_handle_damage(&efbdev->efb, region->dx, region->dy, region->width, region->height); } static void evdi_fb_imageblit(struct fb_info *info, const struct fb_image *image) { struct evdi_fbdev *efbdev = info->par; EVDI_CHECKPT(); sys_imageblit(info, image); evdi_handle_damage(&efbdev->efb, image->dx, image->dy, image->width, image->height); } /* * It's common for several clients to have framebuffer open simultaneously. * e.g. both fbcon and X. Makes things interesting. * Assumes caller is holding info->lock (for open and release at least) */ static int evdi_fb_open(struct fb_info *info, int user) { struct evdi_fbdev *efbdev = info->par; efbdev->fb_count++; pr_notice("open /dev/fb%d user=%d fb_info=%p count=%d\n", info->node, user, info, efbdev->fb_count); return 0; } /* * Assumes caller is holding info->lock mutex (for open and release at least) */ static int evdi_fb_release(struct fb_info *info, int user) { struct evdi_fbdev *efbdev = info->par; efbdev->fb_count--; pr_warn("released /dev/fb%d user=%d count=%d\n", info->node, user, efbdev->fb_count); return 0; } static struct fb_ops evdifb_ops = { .owner = THIS_MODULE, .fb_check_var = drm_fb_helper_check_var, .fb_set_par = drm_fb_helper_set_par, .fb_fillrect = evdi_fb_fillrect, .fb_copyarea = evdi_fb_copyarea, .fb_imageblit = evdi_fb_imageblit, .fb_pan_display = drm_fb_helper_pan_display, .fb_blank = drm_fb_helper_blank, .fb_setcmap = drm_fb_helper_setcmap, .fb_debug_enter = drm_fb_helper_debug_enter, .fb_debug_leave = drm_fb_helper_debug_leave, .fb_mmap = evdi_fb_mmap, .fb_open = evdi_fb_open, .fb_release = evdi_fb_release, }; #endif /* CONFIG_FB */ /* * Function taken from * https://lists.freedesktop.org/archives/dri-devel/2018-September/188716.html */ static int evdi_user_framebuffer_dirty( struct drm_framebuffer *fb, __maybe_unused struct drm_file *file_priv, __always_unused unsigned int flags, __always_unused unsigned int color, __always_unused struct drm_clip_rect *clips, __always_unused unsigned int num_clips) { struct evdi_framebuffer *efb = to_evdi_fb(fb); struct drm_device *dev = efb->base.dev; struct evdi_device *evdi = dev->dev_private; struct drm_modeset_acquire_ctx ctx; struct drm_atomic_state *state; struct drm_plane *plane; int ret = 0; int i; EVDI_CHECKPT(); drm_modeset_acquire_init(&ctx, #if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE /* * When called from ioctl, we are interruptable, * but not when called internally (ie. defio worker) */ file_priv ? DRM_MODESET_ACQUIRE_INTERRUPTIBLE : #endif 0); state = drm_atomic_state_alloc(fb->dev); if (!state) { ret = -ENOMEM; goto out; } state->acquire_ctx = &ctx; for (i = 0; i < num_clips; ++i) evdi_painter_mark_dirty(evdi, &clips[i]); #if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE retry: #endif #if KERNEL_VERSION(4, 3, 0) <= LINUX_VERSION_CODE drm_for_each_plane(plane, fb->dev) { #else list_for_each_entry(plane, &fb->dev->mode_config.plane_list, head) { #endif struct drm_plane_state *plane_state; if (plane->state->fb != fb) continue; /* * Even if it says 'get state' this function will create and * initialize state if it does not exists. We use this property * to force create state. */ plane_state = drm_atomic_get_plane_state(state, plane); if (IS_ERR(plane_state)) { ret = PTR_ERR(plane_state); goto out; } } ret = drm_atomic_commit(state); out: if (ret == -EDEADLK) { drm_atomic_state_clear(state); #if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE ret = drm_modeset_backoff(&ctx); if (!ret) goto retry; #else drm_modeset_backoff(&ctx); #endif } #if KERNEL_VERSION(4, 10, 0) <= LINUX_VERSION_CODE drm_atomic_state_put(state); #else drm_atomic_state_free(state); #endif drm_modeset_drop_locks(&ctx); drm_modeset_acquire_fini(&ctx); return ret; } static int evdi_user_framebuffer_create_handle(struct drm_framebuffer *fb, struct drm_file *file_priv, unsigned int *handle) { struct evdi_framebuffer *efb = to_evdi_fb(fb); return drm_gem_handle_create(file_priv, &efb->obj->base, handle); } static void evdi_user_framebuffer_destroy(struct drm_framebuffer *fb) { struct evdi_framebuffer *efb = to_evdi_fb(fb); EVDI_CHECKPT(); if (efb->obj) drm_gem_object_put_unlocked(&efb->obj->base); drm_framebuffer_cleanup(fb); kfree(efb); } static const struct drm_framebuffer_funcs evdifb_funcs = { .create_handle = evdi_user_framebuffer_create_handle, .destroy = evdi_user_framebuffer_destroy, .dirty = evdi_user_framebuffer_dirty, }; static int evdi_framebuffer_init(struct drm_device *dev, struct evdi_framebuffer *efb, #if KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE struct drm_mode_fb_cmd2 *mode_cmd, #else const struct drm_mode_fb_cmd2 *mode_cmd, #endif struct evdi_gem_object *obj) { efb->obj = obj; #if KERNEL_VERSION(4, 11, 0) > LINUX_VERSION_CODE drm_helper_mode_fill_fb_struct(&efb->base, mode_cmd); #else drm_helper_mode_fill_fb_struct(dev, &efb->base, mode_cmd); #endif return drm_framebuffer_init(dev, &efb->base, &evdifb_funcs); } #ifdef CONFIG_FB static int evdifb_create(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) { struct evdi_fbdev *efbdev = (struct evdi_fbdev *)helper; struct drm_device *dev = efbdev->helper.dev; struct fb_info *info; struct device *device = dev->dev; struct drm_framebuffer *fb; struct drm_mode_fb_cmd2 mode_cmd; struct evdi_gem_object *obj; uint32_t size; int ret = 0; if (sizes->surface_bpp == 24) { sizes->surface_bpp = 32; } else if (sizes->surface_bpp != 32) { EVDI_ERROR("Not supported pixel format (bpp=%d)\n", sizes->surface_bpp); return -EINVAL; } mode_cmd.width = sizes->surface_width; mode_cmd.height = sizes->surface_height; mode_cmd.pitches[0] = mode_cmd.width * ((sizes->surface_bpp + 7) / 8); mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, sizes->surface_depth); size = mode_cmd.pitches[0] * mode_cmd.height; size = ALIGN(size, PAGE_SIZE); obj = evdi_gem_alloc_object(dev, size); if (!obj) goto out; ret = evdi_gem_vmap(obj); if (ret) { DRM_ERROR("failed to vmap fb\n"); goto out_gfree; } info = framebuffer_alloc(0, device); if (!info) { ret = -ENOMEM; goto out_gfree; } info->par = efbdev; ret = evdi_framebuffer_init(dev, &efbdev->efb, &mode_cmd, obj); if (ret) goto out_gfree; fb = &efbdev->efb.base; efbdev->helper.fb = fb; efbdev->helper.fbdev = info; strcpy(info->fix.id, "evdidrmfb"); info->screen_base = efbdev->efb.obj->vmapping; info->fix.smem_len = size; info->fix.smem_start = (unsigned long)efbdev->efb.obj->vmapping; #if KERNEL_VERSION(4, 20, 0) > LINUX_VERSION_CODE info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT; #else info->flags = FBINFO_DEFAULT; #endif efbdev->fb_ops = evdifb_ops; info->fbops = &efbdev->fb_ops; #if KERNEL_VERSION(4, 11, 0) > LINUX_VERSION_CODE drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); #else drm_fb_helper_fill_fix(info, fb->pitches[0], fb->format->depth); #endif drm_fb_helper_fill_var(info, &efbdev->helper, sizes->fb_width, sizes->fb_height); ret = fb_alloc_cmap(&info->cmap, 256, 0); if (ret) { ret = -ENOMEM; goto out_gfree; } DRM_DEBUG_KMS("allocated %dx%d vmal %p\n", fb->width, fb->height, efbdev->efb.obj->vmapping); return ret; out_gfree: drm_gem_object_put_unlocked(&efbdev->efb.obj->base); out: return ret; } static struct drm_fb_helper_funcs evdi_fb_helper_funcs = { .fb_probe = evdifb_create, }; static void evdi_fbdev_destroy(__always_unused struct drm_device *dev, struct evdi_fbdev *efbdev) { struct fb_info *info; if (efbdev->helper.fbdev) { info = efbdev->helper.fbdev; unregister_framebuffer(info); if (info->cmap.len) fb_dealloc_cmap(&info->cmap); framebuffer_release(info); } drm_fb_helper_fini(&efbdev->helper); drm_framebuffer_unregister_private(&efbdev->efb.base); drm_framebuffer_cleanup(&efbdev->efb.base); drm_gem_object_put_unlocked(&efbdev->efb.obj->base); } int evdi_fbdev_init(struct drm_device *dev) { struct evdi_device *evdi; struct evdi_fbdev *efbdev; int ret; evdi = dev->dev_private; efbdev = kzalloc(sizeof(struct evdi_fbdev), GFP_KERNEL); if (!efbdev) return -ENOMEM; evdi->fbdev = efbdev; drm_fb_helper_prepare(dev, &efbdev->helper, &evdi_fb_helper_funcs); #if KERNEL_VERSION(4, 11, 0) > LINUX_VERSION_CODE ret = drm_fb_helper_init(dev, &efbdev->helper, 1, 1); #else ret = drm_fb_helper_init(dev, &efbdev->helper, 1); #endif if (ret) { kfree(efbdev); return ret; } drm_fb_helper_single_add_all_connectors(&efbdev->helper); ret = drm_fb_helper_initial_config(&efbdev->helper, 32); if (ret) { drm_fb_helper_fini(&efbdev->helper); kfree(efbdev); } return ret; } void evdi_fbdev_cleanup(struct drm_device *dev) { struct evdi_device *evdi = dev->dev_private; if (!evdi->fbdev) return; evdi_fbdev_destroy(dev, evdi->fbdev); kfree(evdi->fbdev); evdi->fbdev = NULL; } void evdi_fbdev_unplug(struct drm_device *dev) { struct evdi_device *evdi = dev->dev_private; struct evdi_fbdev *efbdev; if (!evdi->fbdev) return; efbdev = evdi->fbdev; if (efbdev->helper.fbdev) { struct fb_info *info; info = efbdev->helper.fbdev; unlink_framebuffer(info); } } #endif /* CONFIG_FB */ int evdi_fb_get_bpp(uint32_t format) { #if KERNEL_VERSION(4, 10, 0) <= LINUX_VERSION_CODE const struct drm_format_info *info = drm_format_info(format); if (!info) return 0; return info->cpp[0] * 8; #else unsigned int depth; int bpp; drm_fb_get_bpp_depth(format, &depth, &bpp); return bpp; #endif } struct drm_framebuffer *evdi_fb_user_fb_create( struct drm_device *dev, struct drm_file *file, #if KERNEL_VERSION(4, 5, 0) > LINUX_VERSION_CODE struct drm_mode_fb_cmd2 *mode_cmd) #else const struct drm_mode_fb_cmd2 *mode_cmd) #endif { struct drm_gem_object *obj; struct evdi_framebuffer *efb; int ret; uint32_t size; int bpp = evdi_fb_get_bpp(mode_cmd->pixel_format); if (bpp != 32) { EVDI_ERROR("Unsupported bpp (%d)\n", bpp); return ERR_PTR(-EINVAL); } #if KERNEL_VERSION(4, 7, 0) > LINUX_VERSION_CODE obj = drm_gem_object_lookup(dev, file, mode_cmd->handles[0]); #else obj = drm_gem_object_lookup(file, mode_cmd->handles[0]); #endif if (obj == NULL) return ERR_PTR(-ENOENT); size = mode_cmd->pitches[0] * mode_cmd->height; size = ALIGN(size, PAGE_SIZE); if (size > obj->size) { DRM_ERROR("object size not sufficient for fb %d %zu %d %d\n", size, obj->size, mode_cmd->pitches[0], mode_cmd->height); goto err_no_mem; } efb = kzalloc(sizeof(*efb), GFP_KERNEL); if (efb == NULL) goto err_no_mem; ret = evdi_framebuffer_init(dev, efb, mode_cmd, to_evdi_bo(obj)); if (ret) goto err_inval; return &efb->base; err_no_mem: drm_gem_object_put(obj); return ERR_PTR(-ENOMEM); err_inval: kfree(efb); drm_gem_object_put(obj); return ERR_PTR(-EINVAL); } evdi-1.6.0+dfsg/module/evdi_gem.c000066400000000000000000000315361343473130400166060ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2012 Red Hat * Copyright (c) 2015 - 2016 DisplayLink (UK) Ltd. * * This file is subject to the terms and conditions of the GNU General Public * License v2. See the file COPYING in the main directory of this archive for * more details. */ #include #include "evdi_drv.h" #include #include #include #if KERNEL_VERSION(4, 11, 0) <= LINUX_VERSION_CODE #include #endif #if KERNEL_VERSION(4, 12, 0) > LINUX_VERSION_CODE static inline void drm_gem_object_put(struct drm_gem_object *obj) { drm_gem_object_unreference(obj); } static inline void drm_gem_object_put_unlocked(struct drm_gem_object *obj) { drm_gem_object_unreference_unlocked(obj); } #endif uint32_t evdi_gem_object_handle_lookup(struct drm_file *filp, struct drm_gem_object *obj) { uint32_t it_handle = 0; struct drm_gem_object *it_obj = NULL; spin_lock(&filp->table_lock); idr_for_each_entry(&filp->object_idr, it_obj, it_handle) { if (it_obj == obj) break; } spin_unlock(&filp->table_lock); if (!it_obj) it_handle = 0; return it_handle; } struct evdi_gem_object *evdi_gem_alloc_object(struct drm_device *dev, size_t size) { struct evdi_gem_object *obj; obj = kzalloc(sizeof(*obj), GFP_KERNEL); if (obj == NULL) return NULL; if (drm_gem_object_init(dev, &obj->base, size) != 0) { kfree(obj); return NULL; } reservation_object_init(&obj->_resv); obj->resv = &obj->_resv; return obj; } static int evdi_gem_create(struct drm_file *file, struct drm_device *dev, uint64_t size, uint32_t *handle_p) { struct evdi_gem_object *obj; int ret; u32 handle; size = roundup(size, PAGE_SIZE); obj = evdi_gem_alloc_object(dev, size); if (obj == NULL) return -ENOMEM; ret = drm_gem_handle_create(file, &obj->base, &handle); if (ret) { drm_gem_object_release(&obj->base); kfree(obj); return ret; } drm_gem_object_put_unlocked(&obj->base); *handle_p = handle; return 0; } int evdi_dumb_create(struct drm_file *file, struct drm_device *dev, struct drm_mode_create_dumb *args) { args->pitch = args->width * DIV_ROUND_UP(args->bpp, 8); args->size = args->pitch * args->height; return evdi_gem_create(file, dev, args->size, &args->handle); } int evdi_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) { int ret; ret = drm_gem_mmap(filp, vma); if (ret) return ret; vma->vm_flags &= ~VM_PFNMAP; vma->vm_flags |= VM_MIXEDMAP; return ret; } #if KERNEL_VERSION(4, 11, 0) <= LINUX_VERSION_CODE int evdi_gem_fault(struct vm_fault *vmf) { struct vm_area_struct *vma = vmf->vma; #else int evdi_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { #endif struct evdi_gem_object *obj = to_evdi_bo(vma->vm_private_data); struct page *page; unsigned int page_offset; int ret = 0; #if KERNEL_VERSION(4, 10, 0) <= LINUX_VERSION_CODE page_offset = (vmf->address - vma->vm_start) >> PAGE_SHIFT; #else page_offset = ((unsigned long)vmf->virtual_address - vma->vm_start) >> PAGE_SHIFT; #endif if (!obj->pages) return VM_FAULT_SIGBUS; page = obj->pages[page_offset]; #if KERNEL_VERSION(4, 10, 0) <= LINUX_VERSION_CODE ret = vm_insert_page(vma, vmf->address, page); #else ret = vm_insert_page(vma, (unsigned long)vmf->virtual_address, page); #endif switch (ret) { case -EAGAIN: case 0: case -ERESTARTSYS: return VM_FAULT_NOPAGE; case -ENOMEM: return VM_FAULT_OOM; default: return VM_FAULT_SIGBUS; } return VM_FAULT_SIGBUS; } static int evdi_gem_get_pages(struct evdi_gem_object *obj, __always_unused gfp_t gfpmask) { struct page **pages; if (obj->pages) return 0; pages = drm_gem_get_pages(&obj->base); if (IS_ERR(pages)) return PTR_ERR(pages); obj->pages = pages; #if defined(CONFIG_X86) drm_clflush_pages(obj->pages, obj->base.size / PAGE_SIZE); #endif return 0; } static void evdi_gem_put_pages(struct evdi_gem_object *obj) { if (obj->base.import_attach) { #if KERNEL_VERSION(4, 13, 0) > LINUX_VERSION_CODE drm_free_large(obj->pages); #else kvfree(obj->pages); #endif obj->pages = NULL; return; } drm_gem_put_pages(&obj->base, obj->pages, false, false); obj->pages = NULL; } int evdi_gem_vmap(struct evdi_gem_object *obj) { int page_count = obj->base.size / PAGE_SIZE; int ret; if (obj->base.import_attach) { obj->vmapping = dma_buf_vmap(obj->base.import_attach->dmabuf); if (!obj->vmapping) return -ENOMEM; return 0; } ret = evdi_gem_get_pages(obj, GFP_KERNEL); if (ret) return ret; obj->vmapping = vmap(obj->pages, page_count, 0, PAGE_KERNEL); if (!obj->vmapping) return -ENOMEM; return 0; } void evdi_gem_vunmap(struct evdi_gem_object *obj) { if (obj->base.import_attach) { dma_buf_vunmap(obj->base.import_attach->dmabuf, obj->vmapping); obj->vmapping = NULL; return; } if (obj->vmapping) { vunmap(obj->vmapping); obj->vmapping = NULL; } evdi_gem_put_pages(obj); } void evdi_gem_free_object(struct drm_gem_object *gem_obj) { struct evdi_gem_object *obj = to_evdi_bo(gem_obj); if (obj->vmapping) evdi_gem_vunmap(obj); if (gem_obj->import_attach) { drm_prime_gem_destroy(gem_obj, obj->sg); put_device(gem_obj->dev->dev); } if (obj->pages) evdi_gem_put_pages(obj); if (gem_obj->dev->vma_offset_manager) drm_gem_free_mmap_offset(gem_obj); reservation_object_fini(&obj->_resv); obj->resv = NULL; } /* * the dumb interface doesn't work with the GEM straight MMAP * interface, it expects to do MMAP on the drm fd, like normal */ int evdi_gem_mmap(struct drm_file *file, struct drm_device *dev, uint32_t handle, uint64_t *offset) { struct evdi_gem_object *gobj; struct drm_gem_object *obj; int ret = 0; mutex_lock(&dev->struct_mutex); #if KERNEL_VERSION(4, 7, 0) > LINUX_VERSION_CODE obj = drm_gem_object_lookup(dev, file, handle); #else obj = drm_gem_object_lookup(file, handle); #endif if (obj == NULL) { ret = -ENOENT; goto unlock; } gobj = to_evdi_bo(obj); ret = evdi_gem_get_pages(gobj, GFP_KERNEL); if (ret) goto out; ret = drm_gem_create_mmap_offset(obj); if (ret) goto out; *offset = drm_vma_node_offset_addr(&gobj->base.vma_node); out: drm_gem_object_put(&gobj->base); unlock: mutex_unlock(&dev->struct_mutex); return ret; } static int evdi_prime_create(struct drm_device *dev, size_t size, struct sg_table *sg, struct evdi_gem_object **obj_p) { struct evdi_gem_object *obj; int npages; npages = size / PAGE_SIZE; *obj_p = NULL; obj = evdi_gem_alloc_object(dev, npages * PAGE_SIZE); if (!obj) return -ENOMEM; obj->sg = sg; #if KERNEL_VERSION(4, 13, 0) > LINUX_VERSION_CODE obj->pages = drm_malloc_ab(npages, sizeof(struct page *)); #else obj->pages = kvmalloc_array(npages, sizeof(struct page *), GFP_KERNEL); #endif if (obj->pages == NULL) { DRM_ERROR("obj pages is NULL %d\n", npages); return -ENOMEM; } drm_prime_sg_to_page_addr_arrays(sg, obj->pages, NULL, npages); *obj_p = obj; return 0; } struct evdi_drm_dmabuf_attachment { struct sg_table sgt; enum dma_data_direction dir; bool is_mapped; }; static int evdi_attach_dma_buf(__always_unused struct dma_buf *dmabuf, #if KERNEL_VERSION(4, 19, 0) > LINUX_VERSION_CODE __always_unused struct device *dev, #endif struct dma_buf_attachment *attach) { struct evdi_drm_dmabuf_attachment *evdi_attach; evdi_attach = kzalloc(sizeof(*evdi_attach), GFP_KERNEL); if (!evdi_attach) return -ENOMEM; evdi_attach->dir = DMA_NONE; attach->priv = evdi_attach; return 0; } static void evdi_detach_dma_buf(__always_unused struct dma_buf *dmabuf, struct dma_buf_attachment *attach) { struct evdi_drm_dmabuf_attachment *evdi_attach = attach->priv; struct sg_table *sgt; if (!evdi_attach) return; sgt = &evdi_attach->sgt; if (evdi_attach->dir != DMA_NONE) dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents, evdi_attach->dir); sg_free_table(sgt); kfree(evdi_attach); attach->priv = NULL; } static struct sg_table *evdi_map_dma_buf(struct dma_buf_attachment *attach, enum dma_data_direction dir) { struct evdi_drm_dmabuf_attachment *evdi_attach = attach->priv; struct evdi_gem_object *obj = to_evdi_bo(attach->dmabuf->priv); struct drm_device *dev = obj->base.dev; struct scatterlist *rd, *wr; struct sg_table *sgt = NULL; unsigned int i; int page_count; int nents, ret; DRM_DEBUG_PRIME("[DEV:%s] size:%zd dir=%d\n", dev_name(attach->dev), attach->dmabuf->size, dir); /* just return current sgt if already requested. */ if (evdi_attach->dir == dir && evdi_attach->is_mapped) return &evdi_attach->sgt; if (!obj->pages) { ret = evdi_gem_get_pages(obj, GFP_KERNEL); if (ret) { DRM_ERROR("failed to map pages.\n"); return ERR_PTR(ret); } } page_count = obj->base.size / PAGE_SIZE; obj->sg = drm_prime_pages_to_sg(obj->pages, page_count); if (IS_ERR(obj->sg)) { DRM_ERROR("failed to allocate sgt.\n"); return ERR_CAST(obj->sg); } sgt = &evdi_attach->sgt; ret = sg_alloc_table(sgt, obj->sg->orig_nents, GFP_KERNEL); if (ret) { DRM_ERROR("failed to alloc sgt.\n"); return ERR_PTR(-ENOMEM); } mutex_lock(&dev->struct_mutex); rd = obj->sg->sgl; wr = sgt->sgl; for (i = 0; i < sgt->orig_nents; ++i) { sg_set_page(wr, sg_page(rd), rd->length, rd->offset); rd = sg_next(rd); wr = sg_next(wr); } if (dir != DMA_NONE) { nents = dma_map_sg(attach->dev, sgt->sgl, sgt->orig_nents, dir); if (!nents) { DRM_ERROR("failed to map sgl with iommu.\n"); sg_free_table(sgt); sgt = ERR_PTR(-EIO); goto err_unlock; } } evdi_attach->is_mapped = true; evdi_attach->dir = dir; attach->priv = evdi_attach; err_unlock: mutex_unlock(&dev->struct_mutex); return sgt; } static void evdi_unmap_dma_buf( __always_unused struct dma_buf_attachment *attach, __always_unused struct sg_table *sgt, __always_unused enum dma_data_direction dir) { } static void *evdi_dmabuf_kmap(__always_unused struct dma_buf *dma_buf, __always_unused unsigned long page_num) { return NULL; } static void evdi_dmabuf_kunmap( __always_unused struct dma_buf *dma_buf, __always_unused unsigned long page_num, __always_unused void *addr) { } #if KERNEL_VERSION(4, 19, 0) > LINUX_VERSION_CODE static void *evdi_dmabuf_kmap_atomic(__always_unused struct dma_buf *dma_buf, __always_unused unsigned long page_num) { return NULL; } static void evdi_dmabuf_kunmap_atomic( __always_unused struct dma_buf *dma_buf, __always_unused unsigned long page_num, __always_unused void *addr) { } #endif static int evdi_dmabuf_mmap(__always_unused struct dma_buf *dma_buf, __always_unused struct vm_area_struct *vma) { return -EINVAL; } static struct dma_buf_ops evdi_dmabuf_ops = { .attach = evdi_attach_dma_buf, .detach = evdi_detach_dma_buf, .map_dma_buf = evdi_map_dma_buf, .unmap_dma_buf = evdi_unmap_dma_buf, #if KERNEL_VERSION(4, 12, 0) > LINUX_VERSION_CODE .kmap = evdi_dmabuf_kmap, .kmap_atomic = evdi_dmabuf_kmap_atomic, .kunmap = evdi_dmabuf_kunmap, .kunmap_atomic = evdi_dmabuf_kunmap_atomic, #elif KERNEL_VERSION(4, 19, 0) > LINUX_VERSION_CODE .map = evdi_dmabuf_kmap, .map_atomic = evdi_dmabuf_kmap_atomic, .unmap = evdi_dmabuf_kunmap, .unmap_atomic = evdi_dmabuf_kunmap_atomic, #else .map = evdi_dmabuf_kmap, .unmap = evdi_dmabuf_kunmap, #endif .mmap = evdi_dmabuf_mmap, .release = drm_gem_dmabuf_release, }; struct drm_gem_object *evdi_gem_prime_import(struct drm_device *dev, struct dma_buf *dma_buf) { struct dma_buf_attachment *attach; struct sg_table *sg; struct evdi_gem_object *uobj; int ret; /* check if our object */ if (dma_buf->ops == &evdi_dmabuf_ops) { uobj = to_evdi_bo(dma_buf->priv); if (uobj->base.dev == dev) { #if KERNEL_VERSION(4, 12, 0) <= LINUX_VERSION_CODE drm_gem_object_get(&uobj->base); #else drm_gem_object_reference(&uobj->base); #endif return &uobj->base; } } /* need to attach */ get_device(dev->dev); attach = dma_buf_attach(dma_buf, dev->dev); if (IS_ERR(attach)) { put_device(dev->dev); return ERR_CAST(attach); } get_dma_buf(dma_buf); sg = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); if (IS_ERR(sg)) { ret = PTR_ERR(sg); goto fail_detach; } ret = evdi_prime_create(dev, dma_buf->size, sg, &uobj); if (ret) goto fail_unmap; uobj->base.import_attach = attach; uobj->resv = attach->dmabuf->resv; return &uobj->base; fail_unmap: dma_buf_unmap_attachment(attach, sg, DMA_BIDIRECTIONAL); fail_detach: dma_buf_detach(dma_buf, attach); dma_buf_put(dma_buf); put_device(dev->dev); return ERR_PTR(ret); } struct dma_buf *evdi_gem_prime_export(__maybe_unused struct drm_device *dev, struct drm_gem_object *obj, int flags) { struct evdi_gem_object *evdi_obj = to_evdi_bo(obj); struct dma_buf_export_info exp_info = { .exp_name = "evdi", .ops = &evdi_dmabuf_ops, .size = obj->size, .flags = flags, .resv = evdi_obj->resv, .priv = obj }; #if KERNEL_VERSION(4, 9, 0) <= LINUX_VERSION_CODE return drm_gem_dmabuf_export(dev, &exp_info); #else return dma_buf_export(&exp_info); #endif } evdi-1.6.0+dfsg/module/evdi_ioc32.c000066400000000000000000000077551343473130400167630ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-only /* * evdi_ioc32.c * * Copyright (c) 2016 The Chromium OS Authors * Copyright (c) 2017 DisplayLink (UK) Ltd. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be 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, see . */ #include #include #include #include "evdi_drm.h" #include "evdi_drv.h" struct drm_evdi_connect32 { int32_t connected; int32_t dev_index; uint32_t edid_ptr32; uint32_t edid_length; uint32_t sku_area_limit; }; struct drm_evdi_grabpix32 { uint32_t mode; int32_t buf_width; int32_t buf_height; int32_t buf_byte_stride; uint32_t buffer_ptr32; int32_t num_rects; uint32_t rects_ptr32; }; static int compat_evdi_connect(struct file *file, unsigned int __always_unused cmd, unsigned long arg) { struct drm_evdi_connect32 req32; struct drm_evdi_connect __user *request; if (copy_from_user(&req32, (void __user *)arg, sizeof(req32))) return -EFAULT; request = compat_alloc_user_space(sizeof(*request)); #if KERNEL_VERSION(5, 0, 0) > LINUX_VERSION_CODE if (!access_ok(VERIFY_WRITE, request, sizeof(*request)) #else if (!access_ok(request, sizeof(*request)) #endif || __put_user(req32.connected, &request->connected) || __put_user(req32.dev_index, &request->dev_index) || __put_user((void __user *)(unsigned long)req32.edid_ptr32, &request->edid) || __put_user(req32.edid_length, &request->edid_length) || __put_user(req32.sku_area_limit, &request->sku_area_limit)) return -EFAULT; return drm_ioctl(file, DRM_IOCTL_EVDI_CONNECT, (unsigned long)request); } static int compat_evdi_grabpix(struct file *file, unsigned int __always_unused cmd, unsigned long arg) { struct drm_evdi_grabpix32 req32; struct drm_evdi_grabpix __user *request; if (copy_from_user(&req32, (void __user *)arg, sizeof(req32))) return -EFAULT; request = compat_alloc_user_space(sizeof(*request)); #if KERNEL_VERSION(5, 0, 0) > LINUX_VERSION_CODE if (!access_ok(VERIFY_WRITE, request, sizeof(*request)) #else if (!access_ok(request, sizeof(*request)) #endif || __put_user(req32.mode, &request->mode) || __put_user(req32.buf_width, &request->buf_width) || __put_user(req32.buf_height, &request->buf_height) || __put_user(req32.buf_byte_stride, &request->buf_byte_stride) || __put_user((void __user *)(unsigned long)req32.buffer_ptr32, &request->buffer) || __put_user(req32.num_rects, &request->num_rects) || __put_user((void __user *)(unsigned long)req32.rects_ptr32, &request->rects)) return -EFAULT; return drm_ioctl(file, DRM_IOCTL_EVDI_GRABPIX, (unsigned long)request); } static drm_ioctl_compat_t *evdi_compat_ioctls[] = { [DRM_EVDI_CONNECT] = compat_evdi_connect, [DRM_EVDI_GRABPIX] = compat_evdi_grabpix, }; /** * Called whenever a 32-bit process running under a 64-bit kernel * performs an ioctl on /dev/dri/card. * * \param filp file pointer. * \param cmd command. * \param arg user argument. * \return zero on success or negative number on failure. */ long evdi_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { unsigned int nr = DRM_IOCTL_NR(cmd); drm_ioctl_compat_t *fn = NULL; int ret; if (nr < DRM_COMMAND_BASE || nr >= DRM_COMMAND_END) return drm_compat_ioctl(filp, cmd, arg); if (nr < DRM_COMMAND_BASE + ARRAY_SIZE(evdi_compat_ioctls)) fn = evdi_compat_ioctls[nr - DRM_COMMAND_BASE]; if (fn != NULL) ret = (*fn) (filp, cmd, arg); else ret = drm_ioctl(filp, cmd, arg); return ret; } evdi-1.6.0+dfsg/module/evdi_main.c000066400000000000000000000057331343473130400167620ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2012 Red Hat * Copyright (c) 2015 - 2019 DisplayLink (UK) Ltd. * * Based on parts on udlfb.c: * Copyright (C) 2009 its respective authors * * This file is subject to the terms and conditions of the GNU General Public * License v2. See the file COPYING in the main directory of this archive for * more details. */ #include #include #include "evdi_drv.h" #include "evdi_cursor.h" int evdi_driver_setup(struct drm_device *dev) { struct platform_device *platdev = NULL; struct evdi_device *evdi; int ret; EVDI_CHECKPT(); evdi = kzalloc(sizeof(struct evdi_device), GFP_KERNEL); if (!evdi) return -ENOMEM; evdi->ddev = dev; dev->dev_private = evdi; ret = evdi_cursor_init(&evdi->cursor); if (ret) goto err; EVDI_CHECKPT(); evdi_modeset_init(dev); #ifdef CONFIG_FB ret = evdi_fbdev_init(dev); if (ret) goto err; #endif /* CONFIG_FB */ ret = drm_vblank_init(dev, 1); if (ret) goto err_fb; ret = evdi_painter_init(evdi); if (ret) goto err_fb; drm_kms_helper_poll_init(dev); platdev = to_platform_device(dev->dev); platform_set_drvdata(platdev, dev); return 0; err_fb: #ifdef CONFIG_FB evdi_fbdev_cleanup(dev); #endif /* CONFIG_FB */ err: kfree(evdi); EVDI_ERROR("%d\n", ret); if (evdi->cursor) evdi_cursor_free(evdi->cursor); return ret; } #if KERNEL_VERSION(4, 12, 0) > LINUX_VERSION_CODE int evdi_driver_load(struct drm_device *dev, __always_unused unsigned long flags) { return evdi_driver_setup(dev); } #endif #if KERNEL_VERSION(4, 11, 0) > LINUX_VERSION_CODE int evdi_driver_unload(struct drm_device *dev) #else void evdi_driver_unload(struct drm_device *dev) #endif { struct evdi_device *evdi = dev->dev_private; EVDI_CHECKPT(); #if KERNEL_VERSION(4, 14, 0) > LINUX_VERSION_CODE drm_vblank_cleanup(dev); #endif drm_kms_helper_poll_fini(dev); #if KERNEL_VERSION(4, 8, 0) <= LINUX_VERSION_CODE #elif KERNEL_VERSION(4, 7, 0) <= LINUX_VERSION_CODE drm_connector_unregister_all(dev); #else drm_connector_unplug_all(dev); #endif #ifdef CONFIG_FB evdi_fbdev_unplug(dev); #endif /* CONFIG_FB */ if (evdi->cursor) evdi_cursor_free(evdi->cursor); evdi_painter_cleanup(evdi); #ifdef CONFIG_FB evdi_fbdev_cleanup(dev); #endif /* CONFIG_FB */ evdi_modeset_cleanup(dev); kfree(evdi); #if KERNEL_VERSION(4, 11, 0) > LINUX_VERSION_CODE return 0; #endif } static void evdi_driver_close(struct drm_device *drm_dev, struct drm_file *file) { struct evdi_device *evdi = drm_dev->dev_private; EVDI_CHECKPT(); if (evdi) evdi_painter_close(evdi, file); } void evdi_driver_preclose(struct drm_device *drm_dev, struct drm_file *file) { evdi_driver_close(drm_dev, file); } void evdi_driver_postclose(struct drm_device *drm_dev, struct drm_file *file) { struct evdi_device *evdi = drm_dev->dev_private; EVDI_DEBUG("(dev=%d) Process tries to close us, postclose\n", evdi ? evdi->dev_index : -1); evdi_log_process(); evdi_driver_close(drm_dev, file); } evdi-1.6.0+dfsg/module/evdi_modeset.c000066400000000000000000000300271343473130400174700ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2012 Red Hat * Copyright (c) 2015 - 2018 DisplayLink (UK) Ltd. * * Based on parts on udlfb.c: * Copyright (C) 2009 its respective authors * * This file is subject to the terms and conditions of the GNU General Public * License v2. See the file COPYING in the main directory of this archive for * more details. */ #include #include #include #include #include #include #include #include "evdi_drm.h" #include "evdi_drv.h" #include "evdi_cursor.h" #include "evdi_params.h" static void evdi_crtc_dpms(__always_unused struct drm_crtc *crtc, __always_unused int mode) { EVDI_CHECKPT(); } static void evdi_crtc_disable(__always_unused struct drm_crtc *crtc) { EVDI_CHECKPT(); } static void evdi_crtc_destroy(struct drm_crtc *crtc) { EVDI_CHECKPT(); drm_crtc_cleanup(crtc); kfree(crtc); } static void evdi_crtc_commit(__always_unused struct drm_crtc *crtc) { EVDI_CHECKPT(); } static void evdi_crtc_set_nofb(__always_unused struct drm_crtc *crtc) { } static void evdi_crtc_atomic_flush( struct drm_crtc *crtc #if KERNEL_VERSION(4, 3, 0) <= LINUX_VERSION_CODE , __always_unused struct drm_crtc_state *old_state #endif ) { struct drm_crtc_state *state = crtc->state; struct evdi_device *evdi = crtc->dev->dev_private; unsigned long flags; if (state->event) { spin_lock_irqsave(&crtc->dev->event_lock, flags); drm_crtc_send_vblank_event(crtc, state->event); state->event = NULL; spin_unlock_irqrestore(&crtc->dev->event_lock, flags); } if (state->mode_changed && state->active) evdi_painter_mode_changed_notify(evdi, &state->adjusted_mode); if (state->active_changed) evdi_painter_dpms_notify(evdi, state->active ? DRM_MODE_DPMS_ON : DRM_MODE_DPMS_OFF); evdi_painter_send_update_ready_if_needed(evdi); } static void evdi_mark_full_screen_dirty(struct evdi_device *evdi) { const struct drm_clip_rect rect = evdi_painter_framebuffer_size(evdi->painter); evdi_painter_mark_dirty(evdi, &rect); evdi_painter_send_update_ready_if_needed(evdi); } static int evdi_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file, uint32_t handle, uint32_t width, uint32_t height, int32_t hot_x, int32_t hot_y) { struct drm_device *dev = crtc->dev; struct evdi_device *evdi = dev->dev_private; struct drm_gem_object *obj = NULL; struct evdi_gem_object *eobj = NULL; /* * evdi_crtc_cursor_set is callback function using * deprecated cursor entry point. * There is no info about underlaying pixel format. * Hence we are assuming that it is in ARGB 32bpp format. * This format it the only one supported in cursor composition * function. * This format is also enforced during framebuffer creation. * * Proper format will be available when driver start support * universal planes for cursor. */ uint32_t format = DRM_FORMAT_ARGB8888; uint32_t stride = 4 * width; EVDI_CHECKPT(); if (handle) { mutex_lock(&dev->struct_mutex); #if KERNEL_VERSION(4, 7, 0) > LINUX_VERSION_CODE obj = drm_gem_object_lookup(crtc->dev, file, handle); #else obj = drm_gem_object_lookup(file, handle); #endif if (obj) eobj = to_evdi_bo(obj); else EVDI_ERROR("Failed to lookup gem object.\n"); mutex_unlock(&dev->struct_mutex); } evdi_cursor_set(evdi->cursor, eobj, width, height, hot_x, hot_y, format, stride); #if KERNEL_VERSION(4, 12, 0) <= LINUX_VERSION_CODE drm_gem_object_put_unlocked(obj); #else drm_gem_object_unreference_unlocked(obj); #endif /* * For now we don't care whether the application wanted the mouse set, * or not. */ if (evdi_enable_cursor_blending) evdi_mark_full_screen_dirty(evdi); else evdi_painter_send_cursor_set(evdi->painter, evdi->cursor); return 0; } static int evdi_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) { struct drm_device *dev = crtc->dev; struct evdi_device *evdi = dev->dev_private; EVDI_CHECKPT(); evdi_cursor_move(evdi->cursor, x, y); if (evdi_enable_cursor_blending) evdi_mark_full_screen_dirty(evdi); else evdi_painter_send_cursor_move(evdi->painter, evdi->cursor); return 0; } static struct drm_crtc_helper_funcs evdi_helper_funcs = { .mode_set_nofb = evdi_crtc_set_nofb, .atomic_flush = evdi_crtc_atomic_flush, .dpms = evdi_crtc_dpms, .commit = evdi_crtc_commit, .disable = evdi_crtc_disable }; static const struct drm_crtc_funcs evdi_crtc_funcs = { .reset = drm_atomic_helper_crtc_reset, .destroy = evdi_crtc_destroy, .set_config = drm_atomic_helper_set_config, .page_flip = drm_atomic_helper_page_flip, #if KERNEL_VERSION(4, 14, 0) > LINUX_VERSION_CODE .set_property = drm_atomic_helper_crtc_set_property, #endif .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state, .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state, .cursor_set2 = evdi_crtc_cursor_set, .cursor_move = evdi_crtc_cursor_move }; static void evdi_plane_atomic_update(struct drm_plane *plane, struct drm_plane_state *old_state) { struct drm_plane_state *state; struct evdi_device *evdi; if (!plane || !plane->state) { EVDI_WARN("Plane state is null\n"); return; } if (!plane->dev || !plane->dev->dev_private) { EVDI_WARN("Plane device is null\n"); return; } state = plane->state; evdi = plane->dev->dev_private; if (!old_state->crtc && state->crtc) evdi_painter_dpms_notify(evdi, DRM_MODE_DPMS_ON); else if (old_state->crtc && !state->crtc) evdi_painter_dpms_notify(evdi, DRM_MODE_DPMS_OFF); if (state->fb) { struct drm_framebuffer *fb = state->fb; struct evdi_framebuffer *efb = to_evdi_fb(fb); const struct drm_clip_rect fullscreen_rect = { 0, 0, fb->width, fb->height }; if (state->fb != old_state->fb || evdi_painter_needs_full_modeset(evdi)) { evdi_painter_set_scanout_buffer(evdi, efb); evdi_painter_mark_dirty(evdi, &fullscreen_rect); } else if (evdi_painter_get_num_dirts(evdi) == 0) { evdi_painter_mark_dirty(evdi, &fullscreen_rect); } } } static void evdi_cursor_atomic_get_rect(struct drm_clip_rect *rect, struct drm_plane_state *state) { rect->x1 = (state->crtc_x < 0) ? 0 : state->crtc_x; rect->y1 = (state->crtc_y < 0) ? 0 : state->crtc_y; rect->x2 = state->crtc_x + state->crtc_w; rect->y2 = state->crtc_y + state->crtc_h; } static void evdi_cursor_atomic_update(struct drm_plane *plane, struct drm_plane_state *old_state) { if (plane && plane->state && plane->dev && plane->dev->dev_private) { struct drm_plane_state *state = plane->state; struct evdi_device *evdi = plane->dev->dev_private; struct drm_framebuffer *fb = state->fb; struct evdi_framebuffer *efb = to_evdi_fb(fb); struct drm_clip_rect old_rect; struct drm_clip_rect rect; bool cursor_changed = false; bool cursor_position_changed = false; int32_t cursor_position_x = 0; int32_t cursor_position_y = 0; mutex_lock(&plane->dev->struct_mutex); evdi_cursor_position(evdi->cursor, &cursor_position_x, &cursor_position_y); evdi_cursor_move(evdi->cursor, state->crtc_x, state->crtc_y); cursor_position_changed = cursor_position_x != state->crtc_x || cursor_position_y != state->crtc_y; if (fb != old_state->fb) { if (fb != NULL) { uint32_t stride = 4 * fb->width; evdi_cursor_set(evdi->cursor, efb->obj, fb->width, fb->height, 0, 0, #if KERNEL_VERSION(4, 11, 0) > LINUX_VERSION_CODE fb->pixel_format, #else fb->format->format, #endif stride); } evdi_cursor_enable(evdi->cursor, fb != NULL); cursor_changed = true; } mutex_unlock(&plane->dev->struct_mutex); if (evdi_enable_cursor_blending) { evdi_cursor_atomic_get_rect(&old_rect, old_state); evdi_cursor_atomic_get_rect(&rect, state); evdi_painter_mark_dirty(evdi, &old_rect); evdi_painter_mark_dirty(evdi, &rect); return; } if (cursor_changed) evdi_painter_send_cursor_set(evdi->painter, evdi->cursor); if (cursor_position_changed) evdi_painter_send_cursor_move(evdi->painter, evdi->cursor); } } static const struct drm_plane_helper_funcs evdi_plane_helper_funcs = { .atomic_update = evdi_plane_atomic_update }; static const struct drm_plane_helper_funcs evdi_cursor_helper_funcs = { .atomic_update = evdi_cursor_atomic_update }; static const struct drm_plane_funcs evdi_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, .destroy = drm_plane_cleanup, .reset = drm_atomic_helper_plane_reset, .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state, .atomic_destroy_state = drm_atomic_helper_plane_destroy_state, }; static const uint32_t formats[] = { DRM_FORMAT_XRGB8888, DRM_FORMAT_ARGB8888, DRM_FORMAT_XBGR8888, DRM_FORMAT_ABGR8888, }; static struct drm_plane *evdi_create_plane( struct drm_device *dev, enum drm_plane_type type, const struct drm_plane_helper_funcs *helper_funcs) { struct drm_plane *plane; int ret; plane = kzalloc(sizeof(*plane), GFP_KERNEL); if (plane == NULL) { EVDI_ERROR("Failed to allocate primary plane\n"); return NULL; } plane->format_default = true; ret = drm_universal_plane_init(dev, plane, 0xFF, &evdi_plane_funcs, formats, ARRAY_SIZE(formats), #if KERNEL_VERSION(4, 14, 0) <= LINUX_VERSION_CODE NULL, #endif type #if KERNEL_VERSION(4, 5, 0) <= LINUX_VERSION_CODE , NULL #endif ); if (ret) { EVDI_ERROR("Failed to initialize primary plane\n"); kfree(plane); return NULL; } drm_plane_helper_add(plane, helper_funcs); return plane; } static int evdi_crtc_init(struct drm_device *dev) { struct drm_crtc *crtc = NULL; struct drm_plane *primary_plane = NULL; struct drm_plane *cursor_plane = NULL; int status = 0; EVDI_CHECKPT(); crtc = kzalloc(sizeof(struct drm_crtc), GFP_KERNEL); if (crtc == NULL) return -ENOMEM; primary_plane = evdi_create_plane(dev, DRM_PLANE_TYPE_PRIMARY, &evdi_plane_helper_funcs); status = drm_crtc_init_with_planes(dev, crtc, primary_plane, cursor_plane, &evdi_crtc_funcs #if KERNEL_VERSION(4, 5, 0) <= LINUX_VERSION_CODE , NULL #endif ); EVDI_DEBUG("drm_crtc_init: %d p%p\n", status, primary_plane); drm_crtc_helper_add(crtc, &evdi_helper_funcs); return 0; } static int evdi_atomic_check(struct drm_device *dev, struct drm_atomic_state *state) { struct drm_crtc *crtc; struct drm_crtc_state *crtc_state; int i; struct evdi_device *evdi = dev->dev_private; if (evdi_painter_needs_full_modeset(evdi)) { #if KERNEL_VERSION(4, 15, 0) <= LINUX_VERSION_CODE for_each_new_crtc_in_state(state, crtc, crtc_state, i) { #else for_each_crtc_in_state(state, crtc, crtc_state, i) { #endif crtc_state->active_changed = true; crtc_state->mode_changed = true; } } return drm_atomic_helper_check(dev, state); } static const struct drm_mode_config_funcs evdi_mode_funcs = { .fb_create = evdi_fb_user_fb_create, .output_poll_changed = NULL, .atomic_commit = drm_atomic_helper_commit, .atomic_check = evdi_atomic_check }; void evdi_modeset_init(struct drm_device *dev) { struct drm_encoder *encoder; EVDI_CHECKPT(); drm_mode_config_init(dev); dev->mode_config.min_width = 64; dev->mode_config.min_height = 64; dev->mode_config.max_width = 3840; dev->mode_config.max_height = 2160; dev->mode_config.prefer_shadow = 0; dev->mode_config.preferred_depth = 24; dev->mode_config.funcs = &evdi_mode_funcs; #if KERNEL_VERSION(4, 9, 0) > LINUX_VERSION_CODE drm_mode_create_dirty_info_property(dev); #endif #if KERNEL_VERSION(4, 8, 0) <= LINUX_VERSION_CODE #elif KERNEL_VERSION(4, 5, 0) <= LINUX_VERSION_CODE drm_dev_set_unique(dev, dev_name(dev->dev)); #else drm_dev_set_unique(dev, "%s", dev_name(dev->dev)); #endif evdi_crtc_init(dev); encoder = evdi_encoder_init(dev); evdi_connector_init(dev, encoder); drm_mode_config_reset(dev); } void evdi_modeset_cleanup(struct drm_device *dev) { EVDI_CHECKPT(); drm_mode_config_cleanup(dev); } evdi-1.6.0+dfsg/module/evdi_painter.c000066400000000000000000000560321343473130400174760ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2013 - 2019 DisplayLink (UK) Ltd. * * This file is subject to the terms and conditions of the GNU General Public * License v2. See the file COPYING in the main directory of this archive for * more details. */ #include "linux/thread_info.h" #include "linux/mm.h" #include #include #include "evdi_drm.h" #include "evdi_drv.h" #include "evdi_cursor.h" #include "evdi_params.h" #include #include #if KERNEL_VERSION(4, 12, 0) > LINUX_VERSION_CODE static inline void drm_framebuffer_put(struct drm_framebuffer *fb) { drm_framebuffer_unreference(fb); } static inline void drm_framebuffer_get(struct drm_framebuffer *fb) { drm_framebuffer_reference(fb); } #endif struct evdi_event_cursor_set_pending { struct drm_pending_event base; struct drm_evdi_event_cursor_set cursor_set; }; struct evdi_event_cursor_move_pending { struct drm_pending_event base; struct drm_evdi_event_cursor_move cursor_move; }; struct evdi_event_update_ready_pending { struct drm_pending_event base; struct drm_evdi_event_update_ready update_ready; }; struct evdi_event_dpms_pending { struct drm_pending_event base; struct drm_evdi_event_dpms dpms; }; struct evdi_event_mode_changed_pending { struct drm_pending_event base; struct drm_evdi_event_mode_changed mode_changed; }; struct evdi_event_crtc_state_pending { struct drm_pending_event base; struct drm_evdi_event_crtc_state crtc_state; }; #define MAX_DIRTS 16 #define EDID_EXT_BLOCK_SIZE 128 #define MAX_EDID_SIZE (255 * EDID_EXT_BLOCK_SIZE + sizeof(struct edid)) struct evdi_painter { bool is_connected; struct edid *edid; unsigned int edid_length; struct mutex lock; struct drm_clip_rect dirty_rects[MAX_DIRTS]; int num_dirts; struct evdi_framebuffer *scanout_fb; struct drm_file *drm_filp; bool was_update_requested; bool needs_full_modeset; }; static void expand_rect(struct drm_clip_rect *a, const struct drm_clip_rect *b) { a->x1 = min(a->x1, b->x1); a->y1 = min(a->y1, b->y1); a->x2 = max(a->x2, b->x2); a->y2 = max(a->y2, b->y2); } static int rect_area(const struct drm_clip_rect *r) { return (r->x2 - r->x1) * (r->y2 - r->y1); } static void merge_dirty_rects(struct drm_clip_rect *rects, int *count) { int a, b; for (a = 0; a < *count - 1; ++a) { for (b = a + 1; b < *count;) { /* collapse to bounding rect if it is fewer pixels */ const int area_a = rect_area(&rects[a]); const int area_b = rect_area(&rects[b]); struct drm_clip_rect bounding_rect = rects[a]; expand_rect(&bounding_rect, &rects[b]); if (rect_area(&bounding_rect) <= area_a + area_b) { rects[a] = bounding_rect; rects[b] = rects[*count - 1]; /* repass */ b = a + 1; --*count; } else { ++b; } } } } static void collapse_dirty_rects(struct drm_clip_rect *rects, int *count) { int i; EVDI_CHECKPT(); EVDI_WARN("Not enough space for clip rects! Rects will be collapsed"); for (i = 1; i < *count; ++i) expand_rect(&rects[0], &rects[i]); *count = 1; } static int copy_primary_pixels(struct evdi_framebuffer *efb, char __user *buffer, int buf_byte_stride, int num_rects, struct drm_clip_rect *rects, int const max_x, int const max_y) { struct drm_framebuffer *fb = &efb->base; struct drm_clip_rect *r; EVDI_CHECKPT(); for (r = rects; r != rects + num_rects; ++r) { const int byte_offset = r->x1 * 4; const int byte_span = (r->x2 - r->x1) * 4; const int src_offset = fb->pitches[0] * r->y1 + byte_offset; const char *src = (char *)efb->obj->vmapping + src_offset; const int dst_offset = buf_byte_stride * r->y1 + byte_offset; char __user *dst = buffer + dst_offset; int y = r->y2 - r->y1; /* rect size may correspond to previous resolution */ if (max_x < r->x2 || max_y < r->y2) { EVDI_WARN("Rect size beyond expected dimensions\n"); return -EFAULT; } EVDI_VERBOSE("copy rect %d,%d-%d,%d\n", r->x1, r->y1, r->x2, r->y2); for (; y > 0; --y) { if (copy_to_user(dst, src, byte_span)) return -EFAULT; src += fb->pitches[0]; dst += buf_byte_stride; } } return 0; } static void copy_cursor_pixels(struct evdi_framebuffer *efb, char __user *buffer, int buf_byte_stride, struct evdi_cursor *cursor) { if (evdi_enable_cursor_blending) { evdi_cursor_lock(cursor); if (evdi_cursor_compose_and_copy(cursor, efb, buffer, buf_byte_stride)) EVDI_ERROR("Failed to blend cursor\n"); evdi_cursor_unlock(cursor); } } #define painter_lock(painter) \ do { \ EVDI_VERBOSE("Painter lock\n"); \ mutex_lock(&painter->lock); \ } while (0) #define painter_unlock(painter) \ do { \ EVDI_VERBOSE("Painter unlock\n"); \ mutex_unlock(&painter->lock); \ } while (0) bool evdi_painter_is_connected(struct evdi_device *evdi) { if (evdi && evdi->painter) return evdi->painter->is_connected; return false; } u8 *evdi_painter_get_edid_copy(struct evdi_device *evdi) { u8 *block = NULL; EVDI_CHECKPT(); painter_lock(evdi->painter); if (evdi_painter_is_connected(evdi) && evdi->painter->edid && evdi->painter->edid_length) { block = kmalloc(evdi->painter->edid_length, GFP_KERNEL); if (block) { memcpy(block, evdi->painter->edid, evdi->painter->edid_length); EVDI_DEBUG("(dev=%d) EDID valid\n", evdi->dev_index); } } painter_unlock(evdi->painter); return block; } static void evdi_painter_send_event(struct drm_file *drm_filp, struct list_head *event_link) { list_add_tail(event_link, &drm_filp->event_list); wake_up_interruptible(&drm_filp->event_wait); } static void evdi_painter_send_update_ready(struct evdi_painter *painter) { struct evdi_event_update_ready_pending *event; if (painter->drm_filp) { event = kzalloc(sizeof(*event), GFP_KERNEL); event->update_ready.base.type = DRM_EVDI_EVENT_UPDATE_READY; event->update_ready.base.length = sizeof(event->update_ready); event->base.event = &event->update_ready.base; event->base.file_priv = painter->drm_filp; #if KERNEL_VERSION(4, 8, 0) > LINUX_VERSION_CODE event->base.destroy = (void (*)(struct drm_pending_event *))kfree; #endif evdi_painter_send_event(painter->drm_filp, &event->base.link); } else { EVDI_WARN("Painter is not connected!"); } } static uint32_t evdi_painter_get_gem_handle(struct evdi_painter *painter, struct evdi_gem_object *obj) { uint32_t handle = 0; if (!obj) return 0; handle = evdi_gem_object_handle_lookup(painter->drm_filp, &obj->base); if (handle) return handle; if (drm_gem_handle_create(painter->drm_filp, &obj->base, &handle)) { EVDI_ERROR("Failed to create gem handle for %p\n", painter->drm_filp); } return handle; } void evdi_painter_send_cursor_set(struct evdi_painter *painter, struct evdi_cursor *cursor) { struct evdi_event_cursor_set_pending *event; struct evdi_gem_object *eobj = NULL; if (painter->drm_filp) { event = kzalloc(sizeof(*event), GFP_KERNEL); event->cursor_set.base.type = DRM_EVDI_EVENT_CURSOR_SET; event->cursor_set.base.length = sizeof(event->cursor_set); evdi_cursor_lock(cursor); event->cursor_set.enabled = evdi_cursor_enabled(cursor); evdi_cursor_hotpoint(cursor, &event->cursor_set.hot_x, &event->cursor_set.hot_y); evdi_cursor_size(cursor, &event->cursor_set.width, &event->cursor_set.height); evdi_cursor_format(cursor, &event->cursor_set.pixel_format); evdi_cursor_stride(cursor, &event->cursor_set.stride); eobj = evdi_cursor_gem(cursor); event->cursor_set.buffer_handle = evdi_painter_get_gem_handle(painter, eobj); if (eobj) event->cursor_set.buffer_length = eobj->base.size; if (!event->cursor_set.buffer_handle) { event->cursor_set.enabled = false; event->cursor_set.buffer_length = 0; } evdi_cursor_unlock(cursor); event->base.event = &event->cursor_set.base; event->base.file_priv = painter->drm_filp; #if KERNEL_VERSION(4, 8, 0) > LINUX_VERSION_CODE event->base.destroy = (void (*)(struct drm_pending_event *))kfree; #endif evdi_painter_send_event(painter->drm_filp, &event->base.link); } else { EVDI_WARN("Painter is not connected!"); } } void evdi_painter_send_cursor_move(struct evdi_painter *painter, struct evdi_cursor *cursor) { struct evdi_event_cursor_move_pending *event; if (painter->drm_filp) { event = kzalloc(sizeof(*event), GFP_KERNEL); event->cursor_move.base.type = DRM_EVDI_EVENT_CURSOR_MOVE; event->cursor_move.base.length = sizeof(event->cursor_move); evdi_cursor_lock(cursor); evdi_cursor_position( cursor, &event->cursor_move.x, &event->cursor_move.y); evdi_cursor_unlock(cursor); event->base.event = &event->cursor_move.base; event->base.file_priv = painter->drm_filp; #if KERNEL_VERSION(4, 8, 0) > LINUX_VERSION_CODE event->base.destroy = (void (*)(struct drm_pending_event *))kfree; #endif evdi_painter_send_event(painter->drm_filp, &event->base.link); } else { EVDI_WARN("Painter is not connected!"); } } static void evdi_painter_send_dpms(struct evdi_painter *painter, int mode) { struct evdi_event_dpms_pending *event; if (painter->drm_filp) { event = kzalloc(sizeof(*event), GFP_KERNEL); event->dpms.base.type = DRM_EVDI_EVENT_DPMS; event->dpms.base.length = sizeof(event->dpms); event->dpms.mode = mode; event->base.event = &event->dpms.base; event->base.file_priv = painter->drm_filp; #if KERNEL_VERSION(4, 8, 0) > LINUX_VERSION_CODE event->base.destroy = (void (*)(struct drm_pending_event *))kfree; #endif evdi_painter_send_event(painter->drm_filp, &event->base.link); } else { EVDI_WARN("Painter is not connected!"); } } static void evdi_painter_send_crtc_state(struct evdi_painter *painter, int state) { struct evdi_event_crtc_state_pending *event; if (painter->drm_filp) { event = kzalloc(sizeof(*event), GFP_KERNEL); event->crtc_state.base.type = DRM_EVDI_EVENT_CRTC_STATE; event->crtc_state.base.length = sizeof(event->crtc_state); event->crtc_state.state = state; event->base.event = &event->crtc_state.base; event->base.file_priv = painter->drm_filp; #if KERNEL_VERSION(4, 8, 0) > LINUX_VERSION_CODE event->base.destroy = (void (*)(struct drm_pending_event *))kfree; #endif evdi_painter_send_event(painter->drm_filp, &event->base.link); } else { EVDI_WARN("Painter is not connected!"); } } static void evdi_painter_send_mode_changed( struct evdi_painter *painter, struct drm_display_mode *current_mode, int32_t bits_per_pixel, uint32_t pixel_format) { struct evdi_event_mode_changed_pending *event; if (painter->drm_filp) { event = kzalloc(sizeof(*event), GFP_KERNEL); event->mode_changed.base.type = DRM_EVDI_EVENT_MODE_CHANGED; event->mode_changed.base.length = sizeof(event->mode_changed); event->mode_changed.hdisplay = current_mode->hdisplay; event->mode_changed.vdisplay = current_mode->vdisplay; event->mode_changed.vrefresh = drm_mode_vrefresh(current_mode); event->mode_changed.bits_per_pixel = bits_per_pixel; event->mode_changed.pixel_format = pixel_format; event->base.event = &event->mode_changed.base; event->base.file_priv = painter->drm_filp; #if KERNEL_VERSION(4, 8, 0) > LINUX_VERSION_CODE event->base.destroy = (void (*)(struct drm_pending_event *))kfree; #endif evdi_painter_send_event(painter->drm_filp, &event->base.link); } else { EVDI_WARN("Painter is not connected!"); } } int evdi_painter_get_num_dirts(struct evdi_device *evdi) { struct evdi_painter *painter = evdi->painter; int num_dirts; if (painter == NULL) { EVDI_WARN("Painter is not connected!"); return 0; } painter_lock(painter); num_dirts = painter->num_dirts; painter_unlock(painter); return num_dirts; } struct drm_clip_rect evdi_painter_framebuffer_size( struct evdi_painter *painter) { struct drm_clip_rect rect = {0, 0, 0, 0}; struct evdi_framebuffer *efb = NULL; if (painter == NULL) { EVDI_WARN("Painter is not connected!"); return rect; } painter_lock(painter); efb = painter->scanout_fb; if (!efb) { EVDI_DEBUG("Scanout buffer not set."); goto unlock; } rect.x1 = 0; rect.y1 = 0; rect.x2 = efb->base.width; rect.y2 = efb->base.height; unlock: painter_unlock(painter); return rect; } void evdi_painter_mark_dirty(struct evdi_device *evdi, const struct drm_clip_rect *dirty_rect) { struct drm_clip_rect rect; struct evdi_framebuffer *efb = NULL; struct evdi_painter *painter = evdi->painter; if (painter == NULL) { EVDI_WARN("Painter is not connected!"); return; } painter_lock(evdi->painter); efb = evdi->painter->scanout_fb; if (!efb) { EVDI_DEBUG("(dev=%d) Skip clip rect. Scanout buffer not set.\n", evdi->dev_index); goto unlock; } rect = evdi_framebuffer_sanitize_rect(efb, dirty_rect); EVDI_VERBOSE("(dev=%d) %d,%d-%d,%d\n", evdi->dev_index, rect.x1, rect.y1, rect.x2, rect.y2); if (painter->num_dirts == MAX_DIRTS) merge_dirty_rects(&painter->dirty_rects[0], &painter->num_dirts); if (painter->num_dirts == MAX_DIRTS) collapse_dirty_rects(&painter->dirty_rects[0], &painter->num_dirts); memcpy(&painter->dirty_rects[painter->num_dirts], &rect, sizeof(rect)); painter->num_dirts++; unlock: painter_unlock(evdi->painter); } void evdi_painter_send_update_ready_if_needed(struct evdi_device *evdi) { struct evdi_painter *painter = evdi->painter; if (painter) { painter_lock(painter); if (painter->was_update_requested) { evdi_painter_send_update_ready(painter); painter->was_update_requested = false; } painter_unlock(painter); } else { EVDI_WARN("Painter does not exist!"); } } void evdi_painter_dpms_notify(struct evdi_device *evdi, int mode) { struct evdi_painter *painter = evdi->painter; if (painter) { EVDI_DEBUG("(dev=%d) Notifying dpms mode: %d\n", evdi->dev_index, mode); evdi_painter_send_dpms(painter, mode); } else { EVDI_WARN("Painter does not exist!"); } } void evdi_painter_crtc_state_notify(struct evdi_device *evdi, int state) { struct evdi_painter *painter = evdi->painter; if (painter) { EVDI_DEBUG("(dev=%d) Notifying crtc state: %d\n", evdi->dev_index, state); evdi_painter_send_crtc_state(painter, state); } else { EVDI_WARN("Painter does not exist!"); } } static void evdi_log_pixel_format(uint32_t pixel_format) { #if KERNEL_VERSION(4, 10, 0) <= LINUX_VERSION_CODE struct drm_format_name_buf format_name; drm_get_format_name(pixel_format, &format_name); EVDI_DEBUG("pixel format %s\n", format_name.str); #elif KERNEL_VERSION(4, 9, 0) <= LINUX_VERSION_CODE char *format_name = drm_get_format_name(pixel_format); EVDI_DEBUG("pixel format %s\n", format_name); kfree(format_name); #else const char *format_name = drm_get_format_name(pixel_format); EVDI_DEBUG("pixel format %s\n", format_name); #endif } void evdi_painter_mode_changed_notify(struct evdi_device *evdi, struct drm_display_mode *new_mode) { struct evdi_painter *painter = evdi->painter; struct drm_framebuffer *fb = &painter->scanout_fb->base; int bits_per_pixel; uint32_t pixel_format; if (fb == NULL) return; #if KERNEL_VERSION(4, 11, 0) > LINUX_VERSION_CODE bits_per_pixel = fb->bits_per_pixel; pixel_format = fb->pixel_format; #else bits_per_pixel = fb->format->cpp[0] * 8; pixel_format = fb->format->format; #endif EVDI_DEBUG("(dev=%d) Notifying mode changed: %dx%d@%d; bpp %d; ", evdi->dev_index, new_mode->hdisplay, new_mode->vdisplay, drm_mode_vrefresh(new_mode), bits_per_pixel); evdi_log_pixel_format(pixel_format); evdi_painter_send_mode_changed(painter, new_mode, bits_per_pixel, pixel_format); if (painter) painter->needs_full_modeset = false; } static int evdi_painter_connect(struct evdi_device *evdi, void const __user *edid_data, unsigned int edid_length, uint32_t sku_area_limit, struct drm_file *file, int dev_index) { struct evdi_painter *painter = evdi->painter; struct edid *new_edid = NULL; int expected_edid_size = 0; EVDI_DEBUG("(dev=%d) Process is trying to connect\n", evdi->dev_index); evdi_log_process(); if (edid_length < sizeof(struct edid)) { EVDI_ERROR("Edid length too small\n"); return -EINVAL; } if (edid_length > MAX_EDID_SIZE) { EVDI_ERROR("Edid length too large\n"); return -EINVAL; } new_edid = kzalloc(edid_length, GFP_KERNEL); if (!new_edid) return -ENOMEM; if (copy_from_user(new_edid, edid_data, edid_length)) { EVDI_ERROR("(dev=%d) Failed to read edid\n", dev_index); kfree(new_edid); return -EFAULT; } expected_edid_size = sizeof(struct edid) + new_edid->extensions * EDID_EXT_BLOCK_SIZE; if (expected_edid_size != edid_length) { EVDI_ERROR("Wrong edid size. Expected %d but is %d\n", expected_edid_size, edid_length); kfree(new_edid); return -EINVAL; } if (painter->drm_filp) EVDI_WARN("(dev=%d) Double connect - replacing %p with %p\n", dev_index, painter->drm_filp, file); painter_lock(painter); evdi->dev_index = dev_index; evdi->sku_area_limit = sku_area_limit; painter->drm_filp = file; kfree(painter->edid); painter->edid_length = edid_length; painter->edid = new_edid; painter->is_connected = true; painter->needs_full_modeset = true; painter_unlock(painter); EVDI_DEBUG("(dev=%d) Connected with %p\n", evdi->dev_index, painter->drm_filp); drm_helper_hpd_irq_event(evdi->ddev); return 0; } static int evdi_painter_disconnect(struct evdi_device *evdi, struct drm_file *file) { struct evdi_painter *painter = evdi->painter; EVDI_CHECKPT(); painter_lock(painter); if (file != painter->drm_filp) { painter_unlock(painter); return -EFAULT; } if (painter->scanout_fb) { drm_framebuffer_put(&painter->scanout_fb->base); painter->scanout_fb = NULL; } painter->is_connected = false; EVDI_DEBUG("(dev=%d) Disconnected from %p\n", evdi->dev_index, painter->drm_filp); evdi_cursor_enable(evdi->cursor, false); painter->drm_filp = NULL; evdi->dev_index = -1; painter->was_update_requested = false; painter_unlock(painter); drm_helper_hpd_irq_event(evdi->ddev); return 0; } void evdi_painter_close(struct evdi_device *evdi, struct drm_file *file) { EVDI_CHECKPT(); if (evdi->painter) evdi_painter_disconnect(evdi, file); else EVDI_WARN("Painter does not exist!"); } int evdi_painter_connect_ioctl(struct drm_device *drm_dev, void *data, struct drm_file *file) { struct evdi_device *evdi = drm_dev->dev_private; struct evdi_painter *painter = evdi->painter; struct drm_evdi_connect *cmd = data; int ret; EVDI_CHECKPT(); if (painter) { if (cmd->connected) ret = evdi_painter_connect(evdi, cmd->edid, cmd->edid_length, cmd->sku_area_limit, file, cmd->dev_index); else ret = evdi_painter_disconnect(evdi, file); if (ret) { EVDI_WARN("(dev=%d)(pid=%d) disconnect failed\n", evdi->dev_index, (int)task_pid_nr(current)); } return ret; } EVDI_WARN("Painter does not exist!"); return -ENODEV; } int evdi_painter_grabpix_ioctl(struct drm_device *drm_dev, void *data, __always_unused struct drm_file *file) { struct evdi_device *evdi = drm_dev->dev_private; struct evdi_painter *painter = evdi->painter; struct drm_evdi_grabpix *cmd = data; struct evdi_framebuffer *efb = NULL; struct drm_clip_rect dirty_rects[MAX_DIRTS]; int err; EVDI_CHECKPT(); if (cmd->mode != EVDI_GRABPIX_MODE_DIRTY) { EVDI_ERROR("Unknown command mode\n"); return -EINVAL; } if (cmd->num_rects < 1) { EVDI_ERROR("No space for clip rects\n"); return -EINVAL; } if (!painter) return -ENODEV; painter_lock(painter); if (painter->was_update_requested) { EVDI_WARN("(dev=%d) Update ready not sent,", evdi->dev_index); EVDI_WARN(" but pixels are grabbed.\n"); } if (painter->num_dirts < 0) { err = -EAGAIN; goto err_painter; } merge_dirty_rects(&painter->dirty_rects[0], &painter->num_dirts); if (painter->num_dirts > cmd->num_rects) collapse_dirty_rects(&painter->dirty_rects[0], &painter->num_dirts); cmd->num_rects = painter->num_dirts; memcpy(dirty_rects, painter->dirty_rects, painter->num_dirts * sizeof(painter->dirty_rects[0])); efb = painter->scanout_fb; if (!efb) { EVDI_ERROR("Scanout buffer not set\n"); err = -EAGAIN; goto err_painter; } painter->num_dirts = 0; drm_framebuffer_get(&efb->base); painter_unlock(painter); if (!efb->obj->vmapping) { if (evdi_gem_vmap(efb->obj) == -ENOMEM) { EVDI_ERROR("Failed to map scanout buffer\n"); err = -EFAULT; goto err_fb; } if (!efb->obj->vmapping) { EVDI_ERROR("Inexistent vmapping\n"); err = -EFAULT; goto err_fb; } } if (cmd->buf_width != efb->base.width || cmd->buf_height != efb->base.height) { EVDI_ERROR("Invalid buffer dimension\n"); err = -EINVAL; goto err_fb; } if (copy_to_user(cmd->rects, dirty_rects, cmd->num_rects * sizeof(cmd->rects[0]))) { err = -EFAULT; goto err_fb; } err = copy_primary_pixels(efb, cmd->buffer, cmd->buf_byte_stride, cmd->num_rects, dirty_rects, cmd->buf_width, cmd->buf_height); if (err == 0) copy_cursor_pixels(efb, cmd->buffer, cmd->buf_byte_stride, evdi->cursor); err_fb: drm_framebuffer_put(&efb->base); return err; err_painter: painter_unlock(painter); return err; } int evdi_painter_request_update_ioctl(struct drm_device *drm_dev, __always_unused void *data, __always_unused struct drm_file *file) { struct evdi_device *evdi = drm_dev->dev_private; struct evdi_painter *painter = evdi->painter; int result = 0; if (painter) { painter_lock(painter); if (painter->was_update_requested) { EVDI_WARN ("(dev=%d) Update was already requested - ignoring\n", evdi->dev_index); } else { if (painter->num_dirts > 0) result = 1; else painter->was_update_requested = true; } painter_unlock(painter); return result; } else { return -ENODEV; } } int evdi_painter_init(struct evdi_device *dev) { EVDI_CHECKPT(); dev->painter = kzalloc(sizeof(*dev->painter), GFP_KERNEL); if (dev->painter) { mutex_init(&dev->painter->lock); dev->painter->edid = NULL; dev->painter->edid_length = 0; dev->painter->needs_full_modeset = true; return 0; } return -ENOMEM; } void evdi_painter_cleanup(struct evdi_device *evdi) { struct evdi_painter *painter = evdi->painter; EVDI_CHECKPT(); if (painter) { painter_lock(painter); kfree(painter->edid); painter->edid_length = 0; painter->edid = 0; painter_unlock(painter); } else { EVDI_WARN("Painter does not exist\n"); } } void evdi_painter_set_scanout_buffer(struct evdi_device *evdi, struct evdi_framebuffer *newfb) { struct evdi_painter *painter = evdi->painter; struct evdi_framebuffer *oldfb = NULL; if (newfb) drm_framebuffer_get(&newfb->base); painter_lock(painter); oldfb = painter->scanout_fb; painter->scanout_fb = newfb; painter_unlock(painter); if (oldfb) drm_framebuffer_put(&oldfb->base); } bool evdi_painter_needs_full_modeset(struct evdi_device *evdi) { struct evdi_painter *painter = evdi->painter; if (painter) return painter->needs_full_modeset; return false; } evdi-1.6.0+dfsg/module/evdi_params.c000066400000000000000000000020771343473130400173170ustar00rootroot00000000000000// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2015 - 2018 DisplayLink (UK) Ltd. * * This file is subject to the terms and conditions of the GNU General Public * License v2. See the file COPYING in the main directory of this archive for * more details. */ #include #include #include "evdi_params.h" #include "evdi_debug.h" unsigned int evdi_loglevel __read_mostly = EVDI_LOGLEVEL_DEBUG; unsigned short int evdi_initial_device_count __read_mostly; bool evdi_enable_cursor_blending __read_mostly = true; module_param_named(initial_loglevel, evdi_loglevel, int, 0400); MODULE_PARM_DESC(initial_loglevel, "Initial log level"); module_param_named(initial_device_count, evdi_initial_device_count, ushort, 0644); MODULE_PARM_DESC(initial_device_count, "Initial DRM device count (default: 0)"); module_param_named(enable_cursor_blending, evdi_enable_cursor_blending, bool, 0644); MODULE_PARM_DESC(enable_cursor_blending, "Enables cursor compositing on user supplied framebuffer via EVDI_GRABPIX ioctl (default: true)"); evdi-1.6.0+dfsg/module/evdi_params.h000066400000000000000000000007331343473130400173210ustar00rootroot00000000000000/* SPDX-License-Identifier: GPL-2.0-only * Copyright (c) 2015 - 2018 DisplayLink (UK) Ltd. * * This file is subject to the terms and conditions of the GNU General Public * License v2. See the file COPYING in the main directory of this archive for * more details. */ #ifndef EVDI_PARAMS_H #define EVDI_PARAMS_H extern unsigned int evdi_loglevel; extern unsigned short int evdi_initial_device_count; extern bool evdi_enable_cursor_blending; #endif /* EVDI_PARAMS_H */